diff --git a/.gitignore b/.gitignore index 22655b2..b986d75 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ packages/client/dist/ *.tsbuildinfo bundle.* docs/* +!docs/call-code-architecture.html +!docs/call-code-architecture.png diff --git a/README.md b/README.md index a0ec2dc..38de30d 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,15 @@ OpenAI-compatible LLM 模型层 └── 循环执行,直到任务完成 ``` -运行时的核心流程: +整体架构(图)如下: + +

+ call-code 架构图 +

+ +交互式版本见 [docs/call-code-architecture.html](docs/call-code-architecture.html),可明暗主题切换、搜索、关系高亮与节点聚焦,直接在浏览器打开。 + +运行时核心流程: 1. CLI 接收自然语言任务,交给 agent 构建上下文并调用 LLM。 2. 模型返回 `tool_call` 或 `final`,由 protocol 解析为结构化 action。 diff --git a/docs/call-code-architecture.html b/docs/call-code-architecture.html new file mode 100644 index 0000000..879a330 --- /dev/null +++ b/docs/call-code-architecture.html @@ -0,0 +1,13686 @@ + + + + + + + call-code 架构 Diagram + + + + + + + + + + + +
+ +
+
+
+

call-code 架构

+
+
+ + + + + + +
+ + call-code 架构 + A architecture diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + agent-core + + + + + + + + + + + + + + + + CLI 交互层 · source/app.tsx · Architecture component + + + + CLI 交互层 + source/app.tsx + + + + 核心运行器 · agent · runLoop · Architecture component + + + + 核心运行器 + agent · runLoop + + + + LLM 服务 · OpenAI 兼容接口 · Architecture component + + + + LLM 服务 + OpenAI 兼容接口 + + + + 上下文构建 · token 预算 · 摘要 · agent-core + + + + 上下文构建 + token 预算 · 摘要 + + + + 协议解析 · tool_call / final · agent-core + + + + 协议解析 + tool_call / final + + + + 模式权限 · PLAN / BUILD · agent-core + + + + 模式权限 + PLAN / BUILD + + + + 内置工具集 · read/write/bash/git_diff/ocr · agent-core + + + + 内置工具集 + read/write/bash/git_diff/ocr + + + + 记忆 · 短期 / 长期 · agent-core + + + + 记忆 + 短期 / 长期 + + + + 会话存储 · node:sqlite · Architecture component + + + + 会话存储 + node:sqlite + + + + 会话展示端 · React + Vite · GitHub Pages · Architecture component + + + + 会话展示端 + React + Vite · GitHub Pages + + + + + + 任务输入 + + + + chat.completions + + + + 流式响应 + + + + + + 记忆读写 + + + + 会话持久化 + + + + 导出 data.json + + + + + + + Legend + + + Frontend + + + + Backend + + + + Database + + + + External + + + +

+ + + + + + + + +
+ + +
+
+
+
+

功能特性

+
+
    +
  • • Ink 终端界面 · 双模式 PLAN/BUILD
  • +
  • • 本地工具集 · 结构化 action 协议
  • +
  • • 短期 / 长期记忆 · token 预算裁剪
  • +
  • • node:sqlite 会话持久化 + GitHub Pages 展示
  • +
+
+ +
+
+
+

运行流程

+
+
    +
  • • CLI 接任务 → 核心构建上下文
  • +
  • • LLM 返回 tool_call / final
  • +
  • • policy 校验权限后工具执行
  • +
  • • 结果回写 observation 循环直至完成
  • +
+
+
+ +
+ + + + diff --git a/docs/call-code-architecture.png b/docs/call-code-architecture.png new file mode 100644 index 0000000..e7de0e2 Binary files /dev/null and b/docs/call-code-architecture.png differ diff --git a/packages/agent-core/src/harness/compaction/branch-summarization.ts b/packages/agent-core/src/harness/compaction/branch-summarization.ts index 6022093..74eaefb 100644 --- a/packages/agent-core/src/harness/compaction/branch-summarization.ts +++ b/packages/agent-core/src/harness/compaction/branch-summarization.ts @@ -1,5 +1,5 @@ -import type { ContextMessage } from '../session/context/context-types'; -import type { EntryLike, SessionStoreLike } from '../session/sessionInfo/session-repository'; +import type { ContextMessage } from '../context/context-types'; +import type { EntryLike, SessionStoreLike } from '../session/store-types'; import type { SummarizeFn } from './compaction'; import { computeFileLists, @@ -279,4 +279,3 @@ export const branchSummaryEntryToMessage = (entry: EntryLike): ContextMessage | } return { role: 'user', content: `[分支摘要]\n${payload.summary}` }; }; - diff --git a/packages/agent-core/src/harness/compaction/compaction.ts b/packages/agent-core/src/harness/compaction/compaction.ts index bc53dbf..54f71d2 100644 --- a/packages/agent-core/src/harness/compaction/compaction.ts +++ b/packages/agent-core/src/harness/compaction/compaction.ts @@ -1,5 +1,5 @@ -import type { ContextMessage } from '../session/context/context-types'; -import type { EntryLike, SessionStoreLike } from '../session/sessionInfo/session-repository'; +import type { ContextMessage } from '../context/context-types'; +import type { EntryLike, SessionStoreLike } from '../session/store-types'; import { computeFileLists, createFileOps, @@ -392,4 +392,3 @@ export const compactionEntryToMessage = (entry: EntryLike): ContextMessage | nul } return createSummaryMessage(payload.summary); }; - diff --git a/packages/agent-core/src/harness/compaction/utils.ts b/packages/agent-core/src/harness/compaction/utils.ts index dc3577d..64b9948 100644 --- a/packages/agent-core/src/harness/compaction/utils.ts +++ b/packages/agent-core/src/harness/compaction/utils.ts @@ -1,5 +1,5 @@ -import type { ContextMessage } from '../session/context/context-types'; -import type { EntryLike } from '../session/sessionInfo/session-repository'; +import type { ContextMessage } from '../context/context-types'; +import type { EntryLike } from '../session/store-types'; /** 摘要阶段累积的文件读写信息,后续追加到摘要里让模型保留文件上下文。 */ export interface FileOperations { diff --git a/packages/agent-core/src/harness/session/context/builder.ts b/packages/agent-core/src/harness/context/builder.ts similarity index 100% rename from packages/agent-core/src/harness/session/context/builder.ts rename to packages/agent-core/src/harness/context/builder.ts diff --git a/packages/agent-core/src/harness/session/context/context-builder.ts b/packages/agent-core/src/harness/context/context-builder.ts similarity index 100% rename from packages/agent-core/src/harness/session/context/context-builder.ts rename to packages/agent-core/src/harness/context/context-builder.ts diff --git a/packages/agent-core/src/harness/session/context/context-summarizer.ts b/packages/agent-core/src/harness/context/context-summarizer.ts similarity index 100% rename from packages/agent-core/src/harness/session/context/context-summarizer.ts rename to packages/agent-core/src/harness/context/context-summarizer.ts diff --git a/packages/agent-core/src/harness/session/context/context-types.ts b/packages/agent-core/src/harness/context/context-types.ts similarity index 100% rename from packages/agent-core/src/harness/session/context/context-types.ts rename to packages/agent-core/src/harness/context/context-types.ts diff --git a/packages/agent-core/src/harness/session/context/runtime-context.ts b/packages/agent-core/src/harness/context/runtime-context.ts similarity index 100% rename from packages/agent-core/src/harness/session/context/runtime-context.ts rename to packages/agent-core/src/harness/context/runtime-context.ts diff --git a/packages/agent-core/src/harness/session/context/task-context.ts b/packages/agent-core/src/harness/context/task-context.ts similarity index 100% rename from packages/agent-core/src/harness/session/context/task-context.ts rename to packages/agent-core/src/harness/context/task-context.ts diff --git a/packages/agent-core/src/harness/core/agent.ts b/packages/agent-core/src/harness/core/agent.ts index eb6b0a9..04fef07 100644 --- a/packages/agent-core/src/harness/core/agent.ts +++ b/packages/agent-core/src/harness/core/agent.ts @@ -1,4 +1,4 @@ -import { runLoop } from '@agent-core/harness/core/loop'; +import { runLoop } from '@agent-core/harness/runtime/run-loop'; import type { StreamHandlers } from '@agent-core/harness/core/llm'; import { createTaskState, type AgentMode } from '@agent-core/harness/core/state'; diff --git a/packages/agent-core/src/harness/session/memory/memory-retriever.ts b/packages/agent-core/src/harness/memory/memory-retriever.ts similarity index 90% rename from packages/agent-core/src/harness/session/memory/memory-retriever.ts rename to packages/agent-core/src/harness/memory/memory-retriever.ts index ddeb3d1..528764b 100644 --- a/packages/agent-core/src/harness/session/memory/memory-retriever.ts +++ b/packages/agent-core/src/harness/memory/memory-retriever.ts @@ -1,4 +1,4 @@ -import { memoryStore } from '@agent-core/harness/session/memory/memory-store'; +import { memoryStore } from './memory-store'; export interface RetrievedMemory { longFacts: string[]; diff --git a/packages/agent-core/src/harness/session/memory/memory-schema.ts b/packages/agent-core/src/harness/memory/memory-schema.ts similarity index 100% rename from packages/agent-core/src/harness/session/memory/memory-schema.ts rename to packages/agent-core/src/harness/memory/memory-schema.ts diff --git a/packages/agent-core/src/harness/session/memory/memory-store.ts b/packages/agent-core/src/harness/memory/memory-store.ts similarity index 94% rename from packages/agent-core/src/harness/session/memory/memory-store.ts rename to packages/agent-core/src/harness/memory/memory-store.ts index 73acfdb..c1dfe49 100644 --- a/packages/agent-core/src/harness/session/memory/memory-store.ts +++ b/packages/agent-core/src/harness/memory/memory-store.ts @@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto'; import type { LongMemoryItem, MemorySnapshot, -} from '@agent-core/harness/session/memory/memory-schema'; +} from './memory-schema'; export class MemoryStore { private readonly longMemory: LongMemoryItem[] = []; diff --git a/packages/agent-core/src/harness/session/memory/memory-writer.ts b/packages/agent-core/src/harness/memory/memory-writer.ts similarity index 81% rename from packages/agent-core/src/harness/session/memory/memory-writer.ts rename to packages/agent-core/src/harness/memory/memory-writer.ts index 5a53bd7..afebcfe 100644 --- a/packages/agent-core/src/harness/session/memory/memory-writer.ts +++ b/packages/agent-core/src/harness/memory/memory-writer.ts @@ -1,6 +1,6 @@ import type { TaskState } from '@core/state'; -import type { ContextMessage } from '@agent-core/harness/session/context/context-types'; -import { memoryStore } from '@agent-core/harness/session/memory/memory-store'; +import type { ContextMessage } from '../context/context-types'; +import { memoryStore } from './memory-store'; const countStableMentions = (messages: ContextMessage[], text: string): number => messages.reduce((count, item) => { diff --git a/packages/agent-core/src/harness/runtime/index.ts b/packages/agent-core/src/harness/runtime/index.ts new file mode 100644 index 0000000..8c269df --- /dev/null +++ b/packages/agent-core/src/harness/runtime/index.ts @@ -0,0 +1,3 @@ +export * from './run-loop'; +export * from './session-runtime'; +export * from './tool-runtime'; diff --git a/packages/agent-core/src/harness/core/loop.ts b/packages/agent-core/src/harness/runtime/run-loop.ts similarity index 67% rename from packages/agent-core/src/harness/core/loop.ts rename to packages/agent-core/src/harness/runtime/run-loop.ts index a824b60..8396c5e 100644 --- a/packages/agent-core/src/harness/core/loop.ts +++ b/packages/agent-core/src/harness/runtime/run-loop.ts @@ -1,33 +1,27 @@ -import { callLLM, streamLLM, type StreamHandlers } from './llm'; -import { ContextBuilder } from '../session/context/context-builder'; -import { buildRuntimeContext } from '../session/context/runtime-context'; -import type { ContextMessage } from '../session/context/context-types'; +import { callLLM, streamLLM, type StreamHandlers } from '../core/llm'; +import { ContextBuilder } from '../context/context-builder'; +import { buildRuntimeContext } from '../context/runtime-context'; +import type { ContextMessage } from '../context/context-types'; import { systemPrompt } from '../prompt/system'; import { toolPrompt } from '../prompt/tool'; import { getModePrompt } from '../prompt/modes'; -import type { TaskState } from './state'; +import type { TaskState } from '../core/state'; import { extractFinalText, parseAgentResponse, shouldContinueLoop, } from '../protocol/parser'; import { isToolCallAction } from '../protocol/action'; -import { executeToolCall } from '../session/tools/executor'; -import { promoteStableFact } from '../session/memory/memory-writer'; -import { retrieveMemoryForTask } from '../session/memory/memory-retriever'; -import { - appendTaskEntry, - appendTaskRecord, - ensureTaskSession, - getSharedSessionStoreOrNull, - readTaskHistory, - type SessionStoreLike, -} from '../session/sessionInfo/session-repository'; +import { promoteStableFact } from '../memory/memory-writer'; +import { retrieveMemoryForTask } from '../memory/memory-retriever'; +import { getSharedSessionStoreOrNull } from '../session/store-registry'; +import type { SessionStoreLike } from '../session/store-types'; +import { createSessionRuntime } from './session-runtime'; +import { runToolCall } from './tool-runtime'; import { DEFAULT_COMPACTION_SETTINGS, compact, createSummaryMessage, - persistCompactionEntry, prepareMessagesToCompact, shouldCompact, type CompactionSettings, @@ -48,12 +42,12 @@ export interface RunLoopOptions { summarize?: SummarizeFn; } +/** 编排 Agent 主循环,会话持久化与工具执行分别委托给 session-runtime 与 tool-runtime。 */ export const runLoop = async ( task: TaskState, handlers: StreamHandlers = {}, options: RunLoopOptions = {}, ): Promise => { - const history: ContextMessage[] = []; const store = options.persist === true ? (options.sessionStore ?? getSharedSessionStoreOrNull()) : null; @@ -67,11 +61,9 @@ export const runLoop = async ( : null; const summarize: SummarizeFn = options.summarize ?? (async (messages) => callLLM(messages)); - if (store) { - ensureTaskSession(task, store); - appendTaskEntry(task, { role: 'user', content: task.input, tags: ['task-input'] }, store); - history.push(...readTaskHistory(task, { limit: 100 }, store)); - } + // 会话恢复、条目与记录追加、压缩持久化统一交给 session-runtime。 + const session = createSessionRuntime(task, store); + const history: ContextMessage[] = [...session.history]; const maybeCompact = async (historyToCompact: ContextMessage[]): Promise => { if (!compactionSettings) { @@ -96,9 +88,7 @@ export const runLoop = async ( historyToCompact.length = 0; historyToCompact.push(createSummaryMessage(result.summary), ...result.retainedTail); - if (store) { - persistCompactionEntry(task.id, result, store); - } + session.persistCompaction(result); handlers.onTrace?.(`上下文已压缩到摘要,保留最近 ${result.retainedTail.length} 条消息`); }; @@ -137,39 +127,13 @@ export const runLoop = async ( if (!res) { return '无法获取 LLM 回复'; } - history.push({ - role: 'assistant', - content: res, - }); - if (store) { - appendTaskEntry(task, { role: 'assistant', content: res, tags: ['model-response'] }, store); - } + history.push({ role: 'assistant', content: res }); + session.appendAssistant(res); const parsed = parseAgentResponse(res); if (parsed && isToolCallAction(parsed)) { - if (store) { - appendTaskRecord(task, { - type: 'tool_call', - opKind: parsed.tool, - payload: parsed, - }, store); - } - const execution = await executeToolCall(task.mode, parsed); - history.push({ - role: 'user', - content: execution.content, - }); - if (store) { - appendTaskEntry(task, { role: 'tool', content: execution.content, tool: parsed.tool, tags: ['tool-result', parsed.tool] }, store); - appendTaskRecord(task, { - type: 'tool_result', - opKind: parsed.tool, - payload: { - tool: parsed.tool, - content: execution.content, - }, - }, store); - } + const execution = await runToolCall(task, parsed, session); + history.push({ role: 'user', content: execution.content }); handlers.onTrace?.(execution.trace); continue; } @@ -186,4 +150,4 @@ export const runLoop = async ( } return '已超出最大循环次数'; -}; +}; \ No newline at end of file diff --git a/packages/agent-core/src/harness/runtime/session-runtime.ts b/packages/agent-core/src/harness/runtime/session-runtime.ts new file mode 100644 index 0000000..baff470 --- /dev/null +++ b/packages/agent-core/src/harness/runtime/session-runtime.ts @@ -0,0 +1,73 @@ +import type { ContextMessage } from '../context/context-types'; +import { persistCompactionEntry, type CompactResult } from '../compaction/compaction'; +import type { TaskState } from '../core/state'; +import type { ToolCallAction } from '../protocol/action'; +import type { SessionStoreLike } from '../session/store-types'; +import { readTaskHistory } from '../session/history'; +import { + appendTaskEntry, + appendTaskRecord, + ensureTaskSession, +} from '../session/task-session'; + +export interface SessionRuntime { + history: ContextMessage[]; + appendAssistant(content: string): void; + recordToolCall(parsed: ToolCallAction): void; + appendToolResult(toolName: string, content: string): void; + persistCompaction(result: CompactResult): void; +} + +const noop = (): void => undefined; + +/** 收敛 runLoop 的会话恢复与持久化细节,store 为空时提供无副作用实现。 */ +export const createSessionRuntime = ( + task: TaskState, + store: SessionStoreLike | null, +): SessionRuntime => { + if (!store) { + return { + history: [], + appendAssistant: noop, + recordToolCall: noop, + appendToolResult: noop, + persistCompaction: noop, + }; + } + + ensureTaskSession(task, store); + appendTaskEntry(task, { role: 'user', content: task.input, tags: ['task-input'] }, store); + + return { + history: readTaskHistory(task, { limit: 100 }, store), + appendAssistant(content) { + appendTaskEntry( + task, + { role: 'assistant', content, tags: ['model-response'] }, + store, + ); + }, + recordToolCall(parsed) { + appendTaskRecord(task, { + type: 'tool_call', + opKind: parsed.tool, + payload: parsed, + }, store); + }, + appendToolResult(toolName, content) { + appendTaskEntry( + task, + { role: 'tool', content, tool: toolName, tags: ['tool-result', toolName] }, + store, + ); + appendTaskRecord(task, { + type: 'tool_result', + opKind: toolName, + payload: { tool: toolName, content }, + }, store); + }, + persistCompaction(result) { + persistCompactionEntry(task.id, result, store); + }, + }; +}; diff --git a/packages/agent-core/src/harness/runtime/tool-runtime.ts b/packages/agent-core/src/harness/runtime/tool-runtime.ts new file mode 100644 index 0000000..393c95e --- /dev/null +++ b/packages/agent-core/src/harness/runtime/tool-runtime.ts @@ -0,0 +1,16 @@ +import type { TaskState } from '../core/state'; +import type { ToolCallAction } from '../protocol/action'; +import { executeToolCall, type ToolExecutionResult } from '../tools/executor'; +import type { SessionRuntime } from './session-runtime'; + +/** 执行工具并复用会话运行时记录调用与结果。 */ +export const runToolCall = async ( + task: TaskState, + parsed: ToolCallAction, + session: SessionRuntime, +): Promise => { + session.recordToolCall(parsed); + const execution = await executeToolCall(task.mode, parsed); + session.appendToolResult(parsed.tool, execution.content); + return execution; +}; diff --git a/packages/agent-core/src/harness/session/activity-query.ts b/packages/agent-core/src/harness/session/activity-query.ts new file mode 100644 index 0000000..b63522e --- /dev/null +++ b/packages/agent-core/src/harness/session/activity-query.ts @@ -0,0 +1,107 @@ +import type { EntryLike, SessionStoreLike } from './store-types'; +import { getSharedSessionStore } from './store-registry'; + +/** + * 活动面板需要的历史条目。 + */ +export interface RecentHistoryItem { + id: string; + sessionId: string; + role: 'user' | 'assistant'; + content: string; + timestamp: string; +} + +/** + * 活动面板用于扫描相关页面的最近条目。 + */ +export interface RecentFeedItem { + id: string; + sessionId: string; + role: string; + content: string; + timestamp: string; +} + +const entryToRecentHistoryItem = (entry: EntryLike): RecentHistoryItem | null => { + if (entry.type !== 'user' && entry.type !== 'assistant') { + return null; + } + const payload = entry.payload as { content?: unknown } | null; + if (!payload || typeof payload.content !== 'string') { + return null; + } + return { + id: entry.id, + sessionId: entry.sessionId, + role: entry.type, + content: payload.content, + timestamp: entry.timestamp, + }; +}; + +const entryToRecentFeedItem = (entry: EntryLike): RecentFeedItem | null => { + const payload = entry.payload as { content?: unknown } | null; + if (!payload || typeof payload.content !== 'string') { + return null; + } + return { + id: entry.id, + sessionId: entry.sessionId, + role: entry.type, + content: payload.content, + timestamp: entry.timestamp, + }; +}; + +/** + * 读取最近会话中的 user/assistant 条目,按时间倒序。 + */ +export const loadRecentHistory = ( + limit = 8, + store: SessionStoreLike = getSharedSessionStore(), +): RecentHistoryItem[] => { + const sessions = store.listSessions({ limit: 20 }); + const result: RecentHistoryItem[] = []; + + for (const session of sessions) { + const entries = store.getEntries(session.id, { limit: 1000 }); + for (let index = entries.length - 1; index >= 0; index--) { + const item = entryToRecentHistoryItem(entries[index]); + if (item) { + result.push(item); + if (result.length >= limit) { + return result; + } + } + } + } + + return result; +}; + +/** + * 读取最近条目,包含 tool 输出,用于扫描相关页面。 + */ +export const loadRecentFeed = ( + limit = 200, + store: SessionStoreLike = getSharedSessionStore(), +): RecentFeedItem[] => { + const sessions = store.listSessions({ limit: 20 }); + const result: RecentFeedItem[] = []; + + for (const session of sessions) { + const entries = store.getEntries(session.id, { limit: 1000 }); + for (let index = entries.length - 1; index >= 0; index--) { + const item = entryToRecentFeedItem(entries[index]); + if (item) { + result.push(item); + if (result.length >= limit) { + return result; + } + } + } + } + + return result; +}; diff --git a/packages/agent-core/src/harness/session/history.ts b/packages/agent-core/src/harness/session/history.ts new file mode 100644 index 0000000..faf7cab --- /dev/null +++ b/packages/agent-core/src/harness/session/history.ts @@ -0,0 +1,84 @@ +import type { ContextMessage } from '../context/context-types'; +import type { EntryLike, SessionStoreLike } from './store-types'; +import { getSharedSessionStore } from './store-registry'; + +const entryToContextMessage = (entry: EntryLike): ContextMessage | null => { + const payload = entry.payload as { content?: unknown; role?: unknown } | null; + if (!payload || typeof payload.content !== 'string') { + return null; + } + + // user 消息由 context-builder 单独放入,避免重复;tool 结果沿用 runLoop 的 user 角色。 + if (entry.type === 'assistant') { + return { role: 'assistant', content: payload.content }; + } + if (entry.type === 'tool') { + return { role: 'user', content: payload.content }; + } + if (entry.type === 'compaction' || entry.type === 'branch_summary') { + const summary = (payload as { summary?: unknown }).summary; + if (typeof summary !== 'string') { + return null; + } + const prefix = entry.type === 'compaction' ? '[历史摘要]' : '[分支摘要]'; + return { role: 'user', content: `${prefix}\n${summary}` }; + } + return null; +}; + +const isContextMessage = (value: unknown): value is ContextMessage => { + if (!value || typeof value !== 'object') { + return false; + } + const message = value as { role?: unknown; content?: unknown }; + return ( + (message.role === 'user' || message.role === 'assistant') && + typeof message.content === 'string' + ); +}; + +/** + * 读取任务会话历史,供 runLoop 恢复上下文使用。 + * 只返回 assistant/tool 条目,user 指令由 buildRuntimeContext 单独拼接。 + */ +export const readTaskHistory = ( + task: { id: string }, + options: { limit?: number } = {}, + store: SessionStoreLike = getSharedSessionStore(), +): ContextMessage[] => { + // getEntries 按 seq 升序返回,这里取最近 limit 条,避免只拿到最早的历史。 + const limit = options.limit ?? 1000; + const entries = store.getEntries(task.id, { limit: Math.max(limit, 1000) }); + + let lastCompactionIndex = -1; + for (let index = entries.length - 1; index >= 0; index--) { + if (entries[index].type === 'compaction') { + lastCompactionIndex = index; + break; + } + } + + if (lastCompactionIndex >= 0) { + const compaction = entries[lastCompactionIndex]; + const payload = compaction.payload as { retainedTail?: unknown } | null; + const retainedTail = Array.isArray(payload?.retainedTail) + ? payload.retainedTail.filter(isContextMessage) + : []; + const history: ContextMessage[] = [ + ...(entryToContextMessage(compaction) ? [entryToContextMessage(compaction)!] : []), + ...retainedTail, + ...entries + .slice(lastCompactionIndex + 1) + .map(entryToContextMessage) + .filter((item): item is ContextMessage => item !== null), + ]; + return history.slice(-limit); + } + + return entries + .slice(-limit) + .map(entryToContextMessage) + .filter((item): item is ContextMessage => item !== null); +}; + +export { entryToContextMessage }; diff --git a/packages/agent-core/src/harness/session/index.ts b/packages/agent-core/src/harness/session/index.ts index 42dd4ee..05dce44 100644 --- a/packages/agent-core/src/harness/session/index.ts +++ b/packages/agent-core/src/harness/session/index.ts @@ -1,9 +1,5 @@ -export * from './context/builder'; -export * from './memory/memory-retriever'; -export * from './memory/memory-schema'; -export * from './memory/memory-store'; -export * from './memory/memory-writer'; -export * from './policy/guard'; -export * from './policy/modes'; -export * from './sessionInfo/session-repository'; -export * from './tools'; +export * from './activity-query'; +export * from './history'; +export * from './store-registry'; +export * from './store-types'; +export * from './task-session'; diff --git a/packages/agent-core/src/harness/session/sessionInfo/session-repository.ts b/packages/agent-core/src/harness/session/sessionInfo/session-repository.ts deleted file mode 100644 index a4904ba..0000000 --- a/packages/agent-core/src/harness/session/sessionInfo/session-repository.ts +++ /dev/null @@ -1,420 +0,0 @@ -import type { TaskState } from '@agent-core/harness/core/state'; -import type { ContextMessage } from '../context/context-types'; - -/** 默认泳道名称,与 session-sqlite store 保持一致 */ -export const DEFAULT_LANE = 'default'; - -/** - * SessionStoreLike 是 agent-core 依赖的最小会话存储接口。 - * 具体实现由 session-sqlite 的 SessionStore 提供,避免 agent-core 反向依赖存储包。 - */ -export interface SessionStoreLike { - close?(): void; - getSession(id: string): SessionLike | null; - createSession(input: { - id?: string; - cwd: string; - parentSessionId?: string | null; - metadata?: Record; - }): SessionLike; - replaceSession(input: { - id?: string; - cwd: string; - parentSessionId?: string | null; - metadata?: Record; - }): SessionLike; - appendEntry( - sessionId: string, - input: { - id?: string; - parentId?: string | null; - type: string; - payload: unknown; - branchId?: string; - lane?: string; - }, - ): EntryLike; - appendRecord( - sessionId: string, - input: { - lane: string; - runId?: string | null; - type: string; - opKind?: string | null; - payload: unknown; - }, - ): RecordLike; - getStats(sessionId: string): SessionStatsLike; - updateStats( - sessionId: string, - stats: Omit, - ): SessionStatsLike; - getEntries( - sessionId: string, - options: { limit?: number }, - ): EntryLike[]; - listSessions(options: { limit?: number }): SessionLike[]; -} - -/** 会话基本信息的最小形状 */ -export interface SessionLike { - id: string; - createdAt: string; - cwd: string; - parentSessionId: string | null; - metadata: Record; -} - -/** 条目信息的最小形状 */ -export interface EntryLike { - sessionId: string; - seq: number; - id: string; - parentId: string | null; - type: string; - timestamp: string; - payload: unknown; -} - -/** 记录信息的最小形状 */ -export interface RecordLike { - sessionId: string; - seq: number; - id: string; - lane: string; - runId: string | null; - type: string; - opKind: string | null; - timestamp: string; - payload: unknown; -} - -/** 会话统计的最小形状 */ -export interface SessionStatsLike { - sessionId: string; - messageCount: number; - cachedTokens: number; - uncachedTokens: number; - totalTokens: number; - costTotal: number; -} - -/** - * 会话条目写入参数。 - */ -export interface TaskEntryInput { - /** 条目唯一 ID,不传则由 SessionStore 生成 */ - id?: string; - /** 父条目 ID,不传时默认挂在当前泳道叶子下 */ - parentId?: string | null; - /** 条目角色,会同时作为 entries.type 保存 */ - role: 'user' | 'assistant' | 'tool' | 'system'; - /** 条目正文 */ - content: string; - /** 工具名,tool 类条目使用 */ - tool?: string | null; - /** 标签,便于后续检索和活动面板展示 */ - tags?: string[]; - /** 分支 ID */ - branchId?: string; - /** 泳道 */ - lane?: string; -} - -/** - * 运行记录写入参数,对应 schema 中的 records 表。 - */ -export interface TaskRecordInput { - lane?: string; - runId?: string | null; - type: string; - opKind?: string | null; - payload: unknown; -} - -/** - * 活动面板需要的历史条目。 - */ -export interface RecentHistoryItem { - id: string; - sessionId: string; - role: 'user' | 'assistant'; - content: string; - timestamp: string; -} - -/** - * 活动面板用于扫描相关页面的最近条目。 - */ -export interface RecentFeedItem { - id: string; - sessionId: string; - role: string; - content: string; - timestamp: string; -} - -let sharedStore: SessionStoreLike | null = null; - -/** 获取进程内共享的 SessionStoreLike。 */ -export const getSharedSessionStore = (): SessionStoreLike => { - if (!sharedStore) { - throw new Error('会话存储未初始化,请先调用 setSharedSessionStore'); - } - return sharedStore; -}; - -/** 获取当前共享存储,未初始化时返回 null。 */ -export const getSharedSessionStoreOrNull = (): SessionStoreLike | null => sharedStore; - -/** 替换共享 SessionStore,测试传入 :memory: 或注入自定义实例时使用。 */ -export const setSharedSessionStore = (store: SessionStoreLike | null): void => { - sharedStore = store; -}; - -/** 关闭并清空共享 SessionStore。 */ -export const closeSharedSessionStore = (): void => { - sharedStore?.close?.(); - sharedStore = null; -}; - -const taskMetadata = (task: TaskState) => ({ - mode: task.mode, - objective: task.objective, - constraints: task.constraints, - createdAt: task.createdAt, -}); - -/** - * 为任务创建对应会话。已存在时更新元数据,避免覆盖已有历史条目。 - */ -export const ensureTaskSession = ( - task: TaskState, - store: SessionStoreLike = getSharedSessionStore(), -): SessionLike => { - const cwd = task.workspace ?? process.cwd(); - const metadata = taskMetadata(task); - - if (store.getSession(task.id)) { - return store.replaceSession({ - id: task.id, - cwd, - metadata, - }); - } - - return store.createSession({ - id: task.id, - cwd, - metadata, - }); -}; - -/** - * 追加一条 agent 对话条目,并同步递增会话消息数统计。 - */ -export const appendTaskEntry = ( - task: TaskState, - input: TaskEntryInput, - store: SessionStoreLike = getSharedSessionStore(), -): EntryLike => { - const entryInput = { - id: input.id, - parentId: input.parentId, - type: input.role, - payload: { - role: input.role, - content: input.content, - tool: input.tool ?? null, - tags: input.tags ?? [], - }, - branchId: input.branchId, - lane: input.lane ?? DEFAULT_LANE, - }; - - const entry = store.appendEntry(task.id, entryInput); - const stats = store.getStats(task.id); - store.updateStats(task.id, { - ...stats, - messageCount: stats.messageCount + 1, - }); - return entry; -}; - -/** - * 追加一条运行记录,对应 schema 中的 records 表。 - */ -export const appendTaskRecord = ( - task: TaskState, - input: TaskRecordInput, - store: SessionStoreLike = getSharedSessionStore(), -): RecordLike => - store.appendRecord(task.id, { - lane: input.lane ?? 'run', - runId: input.runId ?? task.id, - type: input.type, - opKind: input.opKind ?? null, - payload: input.payload, - }); - -const entryToContextMessage = (entry: EntryLike): ContextMessage | null => { - const payload = entry.payload as { content?: unknown; role?: unknown } | null; - if (!payload || typeof payload.content !== 'string') { - return null; - } - - // user 消息由 context-builder 单独放入,避免重复;tool 结果沿用 runLoop 的 user 角色。 - if (entry.type === 'assistant') { - return { role: 'assistant', content: payload.content }; - } - if (entry.type === 'tool') { - return { role: 'user', content: payload.content }; - } - if (entry.type === 'compaction' || entry.type === 'branch_summary') { - const summary = (payload as { summary?: unknown }).summary; - if (typeof summary !== 'string') { - return null; - } - const prefix = entry.type === 'compaction' ? '[历史摘要]' : '[分支摘要]'; - return { role: 'user', content: `${prefix}\n${summary}` }; - } - return null; -}; - -const isContextMessage = (value: unknown): value is ContextMessage => { - if (!value || typeof value !== 'object') { - return false; - } - const message = value as { role?: unknown; content?: unknown }; - return ( - (message.role === 'user' || message.role === 'assistant') && - typeof message.content === 'string' - ); -}; - -/** - * 读取任务会话历史,供 runLoop 恢复上下文使用。 - * 只返回 assistant/tool 条目,user 指令由 buildRuntimeContext 单独拼接。 - */ -export const readTaskHistory = ( - task: TaskState, - options: { limit?: number } = {}, - store: SessionStoreLike = getSharedSessionStore(), -): ContextMessage[] => { - // getEntries 按 seq 升序返回,这里取最近 limit 条,避免只拿到最早的历史。 - const limit = options.limit ?? 1000; - const entries = store.getEntries(task.id, { limit: Math.max(limit, 1000) }); - - let lastCompactionIndex = -1; - for (let index = entries.length - 1; index >= 0; index--) { - if (entries[index].type === 'compaction') { - lastCompactionIndex = index; - break; - } - } - - if (lastCompactionIndex >= 0) { - const compaction = entries[lastCompactionIndex]; - const payload = compaction.payload as { retainedTail?: unknown } | null; - const retainedTail = Array.isArray(payload?.retainedTail) - ? payload.retainedTail.filter(isContextMessage) - : []; - const history: ContextMessage[] = [ - ...(entryToContextMessage(compaction) ? [entryToContextMessage(compaction)!] : []), - ...retainedTail, - ...entries - .slice(lastCompactionIndex + 1) - .map(entryToContextMessage) - .filter((item): item is ContextMessage => item !== null), - ]; - return history.slice(-limit); - } - - return entries - .slice(-limit) - .map(entryToContextMessage) - .filter((item): item is ContextMessage => item !== null); -}; - -const entryToRecentHistoryItem = (entry: EntryLike): RecentHistoryItem | null => { - if (entry.type !== 'user' && entry.type !== 'assistant') { - return null; - } - const payload = entry.payload as { content?: unknown } | null; - if (!payload || typeof payload.content !== 'string') { - return null; - } - return { - id: entry.id, - sessionId: entry.sessionId, - role: entry.type, - content: payload.content, - timestamp: entry.timestamp, - }; -}; - -const entryToRecentFeedItem = (entry: EntryLike): RecentFeedItem | null => { - const payload = entry.payload as { content?: unknown } | null; - if (!payload || typeof payload.content !== 'string') { - return null; - } - return { - id: entry.id, - sessionId: entry.sessionId, - role: entry.type, - content: payload.content, - timestamp: entry.timestamp, - }; -}; - -/** - * 读取最近会话中的 user/assistant 条目,按时间倒序。 - */ -export const loadRecentHistory = ( - limit = 8, - store: SessionStoreLike = getSharedSessionStore(), -): RecentHistoryItem[] => { - const sessions = store.listSessions({ limit: 20 }); - const result: RecentHistoryItem[] = []; - - for (const session of sessions) { - const entries = store.getEntries(session.id, { limit: 1000 }); - for (let index = entries.length - 1; index >= 0; index--) { - const item = entryToRecentHistoryItem(entries[index]); - if (item) { - result.push(item); - if (result.length >= limit) { - return result; - } - } - } - } - - return result; -}; - -/** - * 读取最近条目,包含 tool 输出,用于扫描相关页面。 - */ -export const loadRecentFeed = ( - limit = 200, - store: SessionStoreLike = getSharedSessionStore(), -): RecentFeedItem[] => { - const sessions = store.listSessions({ limit: 20 }); - const result: RecentFeedItem[] = []; - - for (const session of sessions) { - const entries = store.getEntries(session.id, { limit: 1000 }); - for (let index = entries.length - 1; index >= 0; index--) { - const item = entryToRecentFeedItem(entries[index]); - if (item) { - result.push(item); - if (result.length >= limit) { - return result; - } - } - } - } - - return result; -}; diff --git a/packages/agent-core/src/harness/session/store-registry.ts b/packages/agent-core/src/harness/session/store-registry.ts new file mode 100644 index 0000000..8562c5a --- /dev/null +++ b/packages/agent-core/src/harness/session/store-registry.ts @@ -0,0 +1,25 @@ +import type { SessionStoreLike } from './store-types'; + +let sharedStore: SessionStoreLike | null = null; + +/** 获取进程内共享的 SessionStoreLike。 */ +export const getSharedSessionStore = (): SessionStoreLike => { + if (!sharedStore) { + throw new Error('会话存储未初始化,请先调用 setSharedSessionStore'); + } + return sharedStore; +}; + +/** 获取当前共享存储,未初始化时返回 null。 */ +export const getSharedSessionStoreOrNull = (): SessionStoreLike | null => sharedStore; + +/** 替换共享 SessionStore,测试传入 :memory: 或注入自定义实例时使用。 */ +export const setSharedSessionStore = (store: SessionStoreLike | null): void => { + sharedStore = store; +}; + +/** 关闭并清空共享 SessionStore。 */ +export const closeSharedSessionStore = (): void => { + sharedStore?.close?.(); + sharedStore = null; +}; diff --git a/packages/agent-core/src/harness/session/store-types.ts b/packages/agent-core/src/harness/session/store-types.ts new file mode 100644 index 0000000..a3a419d --- /dev/null +++ b/packages/agent-core/src/harness/session/store-types.ts @@ -0,0 +1,94 @@ +/** + * SessionStoreLike 是 agent-core 依赖的最小会话存储接口。 + * 具体实现由 session-sqlite 的 SessionStore 提供,避免 agent-core 反向依赖存储包。 + */ +export interface SessionStoreLike { + close?(): void; + getSession(id: string): SessionLike | null; + createSession(input: { + id?: string; + cwd: string; + parentSessionId?: string | null; + metadata?: Record; + }): SessionLike; + replaceSession(input: { + id?: string; + cwd: string; + parentSessionId?: string | null; + metadata?: Record; + }): SessionLike; + appendEntry( + sessionId: string, + input: { + id?: string; + parentId?: string | null; + type: string; + payload: unknown; + branchId?: string; + lane?: string; + }, + ): EntryLike; + appendRecord( + sessionId: string, + input: { + lane: string; + runId?: string | null; + type: string; + opKind?: string | null; + payload: unknown; + }, + ): RecordLike; + getStats(sessionId: string): SessionStatsLike; + updateStats( + sessionId: string, + stats: Omit, + ): SessionStatsLike; + getEntries( + sessionId: string, + options: { limit?: number }, + ): EntryLike[]; + listSessions(options: { limit?: number }): SessionLike[]; +} + +/** 会话基本信息的最小形状 */ +export interface SessionLike { + id: string; + createdAt: string; + cwd: string; + parentSessionId: string | null; + metadata: Record; +} + +/** 条目信息的最小形状 */ +export interface EntryLike { + sessionId: string; + seq: number; + id: string; + parentId: string | null; + type: string; + timestamp: string; + payload: unknown; +} + +/** 记录信息的最小形状 */ +export interface RecordLike { + sessionId: string; + seq: number; + id: string; + lane: string; + runId: string | null; + type: string; + opKind: string | null; + timestamp: string; + payload: unknown; +} + +/** 会话统计的最小形状 */ +export interface SessionStatsLike { + sessionId: string; + messageCount: number; + cachedTokens: number; + uncachedTokens: number; + totalTokens: number; + costTotal: number; +} diff --git a/packages/agent-core/src/harness/session/task-session.ts b/packages/agent-core/src/harness/session/task-session.ts new file mode 100644 index 0000000..a3efcbb --- /dev/null +++ b/packages/agent-core/src/harness/session/task-session.ts @@ -0,0 +1,123 @@ +import type { TaskState } from '../core/state'; +import type { + EntryLike, + RecordLike, + SessionLike, + SessionStoreLike, +} from './store-types'; +import { getSharedSessionStore } from './store-registry'; + +/** 默认泳道名称,与 session-sqlite store 保持一致 */ +export const DEFAULT_LANE = 'default'; + +/** + * 会话条目写入参数。 + */ +export interface TaskEntryInput { + /** 条目唯一 ID,不传则由 SessionStore 生成 */ + id?: string; + /** 父条目 ID,不传时默认挂在当前泳道叶子下 */ + parentId?: string | null; + /** 条目角色,会同时作为 entries.type 保存 */ + role: 'user' | 'assistant' | 'tool' | 'system'; + /** 条目正文 */ + content: string; + /** 工具名,tool 类条目使用 */ + tool?: string | null; + /** 标签,便于后续检索和活动面板展示 */ + tags?: string[]; + /** 分支 ID */ + branchId?: string; + /** 泳道 */ + lane?: string; +} + +/** + * 运行记录写入参数,对应 schema 中的 records 表。 + */ +export interface TaskRecordInput { + lane?: string; + runId?: string | null; + type: string; + opKind?: string | null; + payload: unknown; +} + +const taskMetadata = (task: TaskState) => ({ + mode: task.mode, + objective: task.objective, + constraints: task.constraints, + createdAt: task.createdAt, +}); + +/** + * 为任务创建对应会话。已存在时更新元数据,避免覆盖已有历史条目。 + */ +export const ensureTaskSession = ( + task: TaskState, + store: SessionStoreLike = getSharedSessionStore(), +): SessionLike => { + const cwd = task.workspace ?? process.cwd(); + const metadata = taskMetadata(task); + + if (store.getSession(task.id)) { + return store.replaceSession({ + id: task.id, + cwd, + metadata, + }); + } + + return store.createSession({ + id: task.id, + cwd, + metadata, + }); +}; + +/** + * 追加一条 agent 对话条目,并同步递增会话消息数统计。 + */ +export const appendTaskEntry = ( + task: TaskState, + input: TaskEntryInput, + store: SessionStoreLike = getSharedSessionStore(), +): EntryLike => { + const entryInput = { + id: input.id, + parentId: input.parentId, + type: input.role, + payload: { + role: input.role, + content: input.content, + tool: input.tool ?? null, + tags: input.tags ?? [], + }, + branchId: input.branchId, + lane: input.lane ?? DEFAULT_LANE, + }; + + const entry = store.appendEntry(task.id, entryInput); + const stats = store.getStats(task.id); + store.updateStats(task.id, { + ...stats, + messageCount: stats.messageCount + 1, + }); + return entry; +}; + +/** + * 追加一条运行记录,对应 schema 中的 records 表。 + */ +export const appendTaskRecord = ( + task: TaskState, + input: TaskRecordInput, + store: SessionStoreLike = getSharedSessionStore(), +): RecordLike => + store.appendRecord(task.id, { + lane: input.lane ?? 'run', + runId: input.runId ?? task.id, + type: input.type, + opKind: input.opKind ?? null, + payload: input.payload, + }); diff --git a/packages/agent-core/src/harness/session/tools/bash.ts b/packages/agent-core/src/harness/tools/bash.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/bash.ts rename to packages/agent-core/src/harness/tools/bash.ts diff --git a/packages/agent-core/src/harness/session/tools/executor.ts b/packages/agent-core/src/harness/tools/executor.ts similarity index 96% rename from packages/agent-core/src/harness/session/tools/executor.ts rename to packages/agent-core/src/harness/tools/executor.ts index a039c83..7be3fff 100644 --- a/packages/agent-core/src/harness/session/tools/executor.ts +++ b/packages/agent-core/src/harness/tools/executor.ts @@ -1,5 +1,5 @@ import { tools } from './'; -import { enforceToolPermission } from '../policy/guard'; +import { enforceToolPermission } from './policy/guard'; import type { AgentMode } from '@agent-core/harness/core/state'; import type { ToolCallAction } from '@agent-core/harness/protocol/action'; import { createToolResultObservation } from '@agent-core/harness/protocol/observation'; diff --git a/packages/agent-core/src/harness/session/tools/getEnvironment.ts b/packages/agent-core/src/harness/tools/getEnvironment.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/getEnvironment.ts rename to packages/agent-core/src/harness/tools/getEnvironment.ts diff --git a/packages/agent-core/src/harness/session/tools/gitDiff.ts b/packages/agent-core/src/harness/tools/gitDiff.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/gitDiff.ts rename to packages/agent-core/src/harness/tools/gitDiff.ts diff --git a/packages/agent-core/src/harness/session/tools/index.ts b/packages/agent-core/src/harness/tools/index.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/index.ts rename to packages/agent-core/src/harness/tools/index.ts diff --git a/packages/agent-core/src/harness/session/tools/ocr.ts b/packages/agent-core/src/harness/tools/ocr.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/ocr.ts rename to packages/agent-core/src/harness/tools/ocr.ts diff --git a/packages/agent-core/src/harness/session/tools/pathUtils.ts b/packages/agent-core/src/harness/tools/pathUtils.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/pathUtils.ts rename to packages/agent-core/src/harness/tools/pathUtils.ts diff --git a/packages/agent-core/src/harness/session/policy/guard.ts b/packages/agent-core/src/harness/tools/policy/guard.ts similarity index 100% rename from packages/agent-core/src/harness/session/policy/guard.ts rename to packages/agent-core/src/harness/tools/policy/guard.ts diff --git a/packages/agent-core/src/harness/session/policy/modes.ts b/packages/agent-core/src/harness/tools/policy/modes.ts similarity index 100% rename from packages/agent-core/src/harness/session/policy/modes.ts rename to packages/agent-core/src/harness/tools/policy/modes.ts diff --git a/packages/agent-core/src/harness/session/tools/readFile.ts b/packages/agent-core/src/harness/tools/readFile.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/readFile.ts rename to packages/agent-core/src/harness/tools/readFile.ts diff --git a/packages/agent-core/src/harness/session/tools/search.ts b/packages/agent-core/src/harness/tools/search.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/search.ts rename to packages/agent-core/src/harness/tools/search.ts diff --git a/packages/agent-core/src/harness/session/tools/writeFile.ts b/packages/agent-core/src/harness/tools/writeFile.ts similarity index 100% rename from packages/agent-core/src/harness/session/tools/writeFile.ts rename to packages/agent-core/src/harness/tools/writeFile.ts diff --git a/packages/agent-core/src/web/export.ts b/packages/agent-core/src/web/export.ts index ecae32a..f8a7c56 100644 --- a/packages/agent-core/src/web/export.ts +++ b/packages/agent-core/src/web/export.ts @@ -4,7 +4,7 @@ import type { EntryLike, SessionStoreLike, SessionStatsLike, -} from '../harness/session/sessionInfo/session-repository'; +} from '../harness/session/store-types'; /** GitHub Pages 客户端读取的会话快照格式。 */ export interface WebEntry { diff --git a/source/app.tsx b/source/app.tsx index 47cc2e1..bdbd887 100644 --- a/source/app.tsx +++ b/source/app.tsx @@ -7,17 +7,17 @@ import { llmModel } from '@agent-core/harness/core/llm'; import TextInput from 'ink-text-input'; import Spinner from 'ink-spinner'; import { get_encoding } from 'tiktoken'; -import { memoryStore } from '@agent-core/harness/session/memory/memory-store'; +import { memoryStore } from '@agent-core/harness/memory/memory-store'; import { getSharedSessionStore, loadRecentFeed, loadRecentHistory, setSharedSessionStore, type RecentHistoryItem, -} from '@agent-core/harness/session/sessionInfo/session-repository'; +} from '@agent-core/harness/session'; import { writeWebExport } from '@web/export'; import { SessionStore } from '../packages/session-sqlite/src/index'; -import { resolveUserPath } from '@agent-core/harness/session/tools/pathUtils'; +import { resolveUserPath } from '@agent-core/harness/tools/pathUtils'; const encoding = get_encoding('cl100k_base'); diff --git a/tests/agent-session-runloop.spec.ts b/tests/agent-session-runloop.spec.ts index e17009c..2a4ee6c 100644 --- a/tests/agent-session-runloop.spec.ts +++ b/tests/agent-session-runloop.spec.ts @@ -4,14 +4,14 @@ vi.mock('@core/llm', () => ({ streamLLM: vi.fn(), })); -import { runLoop } from '@core/loop'; +import { runLoop } from '@agent-core/harness/runtime/run-loop'; import { streamLLM } from '@core/llm'; import { createTaskState } from '@core/state'; import { SessionStore } from '../packages/session-sqlite/src/index'; import { closeSharedSessionStore, setSharedSessionStore, -} from '@agent-core/harness/session/sessionInfo/session-repository'; +} from '@agent-core/harness/session'; describe('runLoop SQLite 持久化', () => { let store: SessionStore; diff --git a/tests/agent.spec.ts b/tests/agent.spec.ts index 8133123..0246300 100644 --- a/tests/agent.spec.ts +++ b/tests/agent.spec.ts @@ -1,12 +1,12 @@ import { vi, describe, it, expect } from 'vitest'; // Mock runLoop to avoid calling the real loop implementation -vi.mock('@core/loop', () => ({ +vi.mock('@agent-core/harness/runtime/run-loop', () => ({ runLoop: vi.fn(async () => 'handled:task'), })); -import { agent } from '@core/agent'; -import { runLoop } from '@core/loop'; +import { agent } from '@agent-core/harness/core/agent'; +import { runLoop } from '@agent-core/harness/runtime/run-loop'; describe('agent', () => { it('calls runLoop with input and returns the final response', async () => { diff --git a/tests/compaction.spec.ts b/tests/compaction.spec.ts index d001992..04c3280 100644 --- a/tests/compaction.spec.ts +++ b/tests/compaction.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { SessionStore } from '../packages/session-sqlite/src/index'; -import type { ContextMessage } from '@agent-core/harness/session/context/context-types'; +import type { ContextMessage } from '@agent-core/harness/context/context-types'; import { compact, createSummaryMessage, diff --git a/tests/runLoop.spec.ts b/tests/runLoop.spec.ts index 3da20af..838092e 100644 --- a/tests/runLoop.spec.ts +++ b/tests/runLoop.spec.ts @@ -5,7 +5,7 @@ vi.mock('@core/llm', () => ({ streamLLM: vi.fn(), })); -import { runLoop } from '@core/loop'; +import { runLoop } from '@agent-core/harness/runtime/run-loop'; import { streamLLM } from '@core/llm'; import { createTaskState } from '@core/state'; diff --git a/tests/session-repository.spec.ts b/tests/session-repository.spec.ts index 44cb2fd..e3f9f3b 100644 --- a/tests/session-repository.spec.ts +++ b/tests/session-repository.spec.ts @@ -10,7 +10,7 @@ import { loadRecentHistory, readTaskHistory, setSharedSessionStore, -} from '@agent-core/harness/session/sessionInfo/session-repository'; +} from '@agent-core/harness/session'; describe('SessionRepository', () => { let store: SessionStore; diff --git a/tsconfig.json b/tsconfig.json index f26785d..4581f75 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,9 +19,9 @@ "@utils/*": ["./packages/agent-core/src/harness/utils/*"], "@web/*": ["./packages/agent-core/src/web/*"], "@prompt/*": ["./packages/agent-core/src/harness/prompt/*"], - "@tools": ["./packages/agent-core/src/harness/session/tools/index.ts"], - "@tools/*": ["./packages/agent-core/src/harness/session/tools/*"], - "@policy/*": ["./packages/agent-core/src/harness/session/policy/*"], + "@tools": ["./packages/agent-core/src/harness/tools/index.ts"], + "@tools/*": ["./packages/agent-core/src/harness/tools/*"], + "@policy/*": ["./packages/agent-core/src/harness/tools/policy/*"], "@protocol/*": ["./packages/agent-core/src/harness/protocol/*"] } }, diff --git a/vitest.config.ts b/vitest.config.ts index a80ab5e..c88459f 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -19,9 +19,9 @@ export default defineConfig({ { find: '@utils', replacement: path.resolve(__dirname, './packages/agent-core/src/harness/utils') }, { find: '@web', replacement: path.resolve(__dirname, './packages/agent-core/src/web') }, { find: '@prompt', replacement: path.resolve(__dirname, './packages/agent-core/src/harness/prompt') }, - { find: /^@tools$/, replacement: path.resolve(__dirname, './packages/agent-core/src/harness/session/tools/index.ts') }, - { find: /^@tools\//, replacement: path.resolve(__dirname, './packages/agent-core/src/harness/session/tools') + '/' }, - { find: '@policy', replacement: path.resolve(__dirname, './packages/agent-core/src/harness/session/policy') }, + { find: /^@tools$/, replacement: path.resolve(__dirname, './packages/agent-core/src/harness/tools/index.ts') }, + { find: /^@tools\//, replacement: path.resolve(__dirname, './packages/agent-core/src/harness/tools') + '/' }, + { find: '@policy', replacement: path.resolve(__dirname, './packages/agent-core/src/harness/tools/policy') }, { find: '@protocol', replacement: path.resolve(__dirname, './packages/agent-core/src/harness/protocol') }, ], },