Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,4 @@ Thumbs.db
coverage/

# Claude Code
CLAUDE.md
.claude/
102 changes: 102 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Open Alice

File-driven AI trading agent. All state (sessions, config, logs) stored as files — no database.

## Quick Start

```bash
pnpm install
pnpm dev # Dev mode (tsx watch, port 3002)
pnpm build # Production build (backend + UI)
pnpm test # Vitest
```

## Project Structure

```
src/
├── main.ts # Composition root
├── core/
│ ├── agent-center.ts # Top-level AI orchestration, owns GenerateRouter
│ ├── ai-provider-manager.ts # GenerateRouter + StreamableResult + AskOptions
│ ├── tool-center.ts # Centralized tool registry (Vercel + MCP export)
│ ├── session.ts # JSONL session store
│ ├── compaction.ts # Auto-summarize long context windows
│ ├── config.ts # Zod-validated config loader
│ ├── ai-config.ts # Runtime AI provider selection
│ ├── event-log.ts # Append-only JSONL event log
│ ├── connector-center.ts # ConnectorCenter — push delivery + last-interacted tracking
│ ├── async-channel.ts # AsyncChannel for streaming provider events to SSE
│ ├── model-factory.ts # Model instance factory for Vercel AI SDK
│ ├── media.ts # MediaAttachment extraction
│ ├── media-store.ts # Media file persistence
│ └── types.ts # Plugin, EngineContext interfaces
├── ai-providers/
│ ├── claude-code/ # Claude Code CLI subprocess
│ ├── vercel-ai-sdk/ # Vercel AI SDK ToolLoopAgent
│ └── agent-sdk/ # Agent SDK (@anthropic-ai/claude-agent-sdk)
├── extension/
│ ├── analysis-kit/ # Indicators, market data tools, sandbox
│ ├── equity/ # Equity fundamentals
│ ├── market/ # Unified symbol search
│ ├── news/ # OpenBB news tools
│ ├── news-collector/ # RSS collector + archive search
│ ├── trading/ # Unified multi-account trading, guard pipeline, git-like commits
│ ├── thinking-kit/ # Reasoning and calculation tools
│ ├── brain/ # Cognitive state (memory, emotion)
│ └── browser/ # Browser automation bridge (OpenClaw)
├── openbb/ # In-process data SDK (equity, crypto, currency, commodity, economy, news)
├── connectors/
│ ├── web/ # Web UI (Hono, SSE streaming, sub-channels)
│ ├── telegram/ # Telegram bot (grammY)
│ └── mcp-ask/ # MCP Ask connector
├── plugins/
│ └── mcp.ts # MCP protocol server
├── task/
│ ├── cron/ # Cron scheduling
│ └── heartbeat/ # Periodic heartbeat
├── skills/ # Agent skill definitions
└── openclaw/ # ⚠️ Frozen — DO NOT MODIFY
```

## Key Architecture

### AgentCenter → GenerateRouter → GenerateProvider

Two layers (Engine was removed):

1. **AgentCenter** (`core/agent-center.ts`) — top-level orchestration. Manages sessions, compaction, and routes calls through GenerateRouter. Exposes `ask()` (stateless) and `askWithSession()` (with history).

2. **GenerateRouter** (`core/ai-provider-manager.ts`) — reads `ai-provider.json` on each call, resolves to active provider. Three backends:
- Claude Code CLI (`inputKind: 'text'`)
- Vercel AI SDK (`inputKind: 'messages'`)
- Agent SDK (`inputKind: 'text'`)

**AIProvider interface**: `ask(prompt)` for one-shot, `generate(input, opts)` for streaming `ProviderEvent` (tool_use / tool_result / text / done). Optional `compact()` for provider-native compaction.

**StreamableResult**: dual interface — `PromiseLike` (await for result) + `AsyncIterable` (for-await for streaming). Multiple consumers each get independent cursors.

Per-request provider and model overrides via `AskOptions.provider` and `AskOptions.vercelAiSdk` / `AskOptions.agentSdk`.

### ConnectorCenter

`connector-center.ts` manages push channels (Web, Telegram, MCP Ask). Tracks last-interacted channel for delivery routing.

### ToolCenter

Centralized registry. Extensions register tools, exports in Vercel and MCP formats. Decoupled from AgentCenter.

## Conventions

- ESM only (`.js` extensions in imports), path alias `@/*` → `./src/*`
- Strict TypeScript, ES2023 target
- Zod for config, TypeBox for tool parameter schemas
- `decimal.js` for financial math
- Pino logger → `logs/engine.log`

## Git Workflow

- `origin` = `TraderAlice/OpenAlice` (production)
- `dev` branch for all development, `master` only via PR
- **Never** force push master, **never** push `archive/dev` (contains old API keys)
- CLAUDE.md is **committed to the repo and publicly visible** — never put API keys, personal paths, or sensitive information in it
Binary file added docs/images/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@hono/node-server": "^1.19.11",
"@modelcontextprotocol/sdk": "^1.26.0",
"@sinclair/typebox": "0.34.48",
"@traderalice/opentypebb": "workspace:*",
"ai": "^6.0.86",
"ajv": "^8.18.0",
"ccxt": "^4.5.38",
Expand All @@ -49,7 +50,6 @@
"grammy": "^1.40.0",
"hono": "^4.12.7",
"json5": "^2.2.3",
"@traderalice/opentypebb": "workspace:*",
"pino": "^10.3.1",
"playwright-core": "1.58.2",
"sharp": "^0.34.5",
Expand All @@ -63,6 +63,7 @@
"@types/express": "^5.0.6",
"@types/node": "^25.2.3",
"@types/ws": "^8.18.1",
"@vitest/coverage-v8": "^4.0.18",
"tsup": "^8.5.1",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
Expand Down
93 changes: 93 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 3 additions & 6 deletions src/ai-providers/agent-sdk/agent-sdk-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@

import { resolve } from 'node:path'
import type { Tool } from 'ai'
import type { ProviderResult, ProviderEvent, GenerateProvider, GenerateInput, GenerateOpts } from '../types.js'
import type { ProviderResult, ProviderEvent, AIProvider, GenerateInput, GenerateOpts } from '../types.js'
import type { AgentSdkConfig, AgentSdkOverride } from './query.js'
import { readAgentConfig } from '../../core/config.js'
import { extractMediaFromToolResultContent } from '../../core/media.js'
import { createChannel } from '../../core/async-channel.js'
import { askAgentSdk } from './query.js'
import { buildAgentSdkMcpServer } from './tool-bridge.js'

export class AgentSdkProvider implements GenerateProvider {
export class AgentSdkProvider implements AIProvider {
readonly inputKind = 'text' as const
readonly providerTag = 'agent-sdk' as const

Expand Down Expand Up @@ -66,7 +65,6 @@ export class AgentSdkProvider implements GenerateProvider {
const mcpServer = await this.buildMcpServer(opts?.disabledTools)

const channel = createChannel<ProviderEvent>()
const media: import('../../core/types.js').MediaAttachment[] = []

const resultPromise = askAgentSdk(
input.prompt,
Expand All @@ -76,7 +74,6 @@ export class AgentSdkProvider implements GenerateProvider {
channel.push({ type: 'tool_use', id, name, input: toolInput })
},
onToolResult: ({ toolUseId, content }) => {
media.push(...extractMediaFromToolResultContent(content))
channel.push({ type: 'tool_result', tool_use_id: toolUseId, content })
},
onText: (text) => {
Expand All @@ -92,7 +89,7 @@ export class AgentSdkProvider implements GenerateProvider {

const result = await resultPromise
const prefix = result.ok ? '' : '[error] '
yield { type: 'done', result: { text: prefix + result.text, media } }
yield { type: 'done', result: { text: prefix + result.text, media: [] } }
}

}
2 changes: 1 addition & 1 deletion src/ai-providers/agent-sdk/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { McpSdkServerConfigWithInstance } from '@anthropic-ai/claude-agent-
import { pino } from 'pino'
import type { ContentBlock } from '../../core/session.js'
import { readAIProviderConfig } from '../../core/config.js'
import { logToolCall } from '../log-tool-call.js'
import { logToolCall } from '../utils.js'

const logger = pino({
transport: { target: 'pino/file', options: { destination: 'logs/agent-sdk.log', mkdir: true } },
Expand Down
Loading
Loading