c0de-agent 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/core/config.js +4 -1
  2. package/dist/core/loop/compaction.d.ts +31 -0
  3. package/dist/core/loop/compaction.js +137 -0
  4. package/dist/core/loop/persist.d.ts +11 -0
  5. package/dist/core/loop/persist.js +107 -0
  6. package/dist/core/loop/segment.d.ts +8 -0
  7. package/dist/core/loop/segment.js +72 -0
  8. package/dist/core/loop/stream-collect.d.ts +47 -0
  9. package/dist/core/loop/stream-collect.js +183 -0
  10. package/dist/core/loop/subagent.d.ts +12 -0
  11. package/dist/core/loop/subagent.js +171 -0
  12. package/dist/core/loop/todo.d.ts +5 -0
  13. package/dist/core/loop/todo.js +26 -0
  14. package/dist/core/loop.d.ts +2 -20
  15. package/dist/core/loop.js +12 -658
  16. package/dist/core/prompt-registry.d.ts +1 -1
  17. package/dist/core/prompt-registry.js +10 -0
  18. package/dist/core/slash.js +1 -1
  19. package/dist/core/todo-tags.d.ts +46 -0
  20. package/dist/core/todo-tags.js +192 -0
  21. package/dist/core/workflows/builtins.js +4 -4
  22. package/dist/core/workflows/discovery.js +4 -1
  23. package/dist/llm/registry.d.ts +30 -3
  24. package/dist/llm/registry.js +25 -7
  25. package/dist/llm/retry.d.ts +4 -2
  26. package/dist/llm/retry.js +7 -6
  27. package/dist/llm/schema/errors.d.ts +13 -3
  28. package/dist/llm/schema/errors.js +35 -3
  29. package/dist/llm/transport.js +5 -1
  30. package/dist/project/resolve.js +2 -2
  31. package/dist/server/routes/chat.js +8 -2
  32. package/dist/server/routes/todo.js +5 -2
  33. package/dist/server/server.d.ts +4 -2
  34. package/dist/server/server.js +20 -12
  35. package/dist/session/squash.js +31 -24
  36. package/dist/shared/types/agent.d.ts +11 -0
  37. package/dist/tools/builtin/todo.d.ts +7 -0
  38. package/dist/tools/builtin/todo.js +23 -16
  39. package/package.json +2 -1
@@ -0,0 +1,171 @@
1
+ import { appendMessage } from '../../session/message.js';
2
+ import { createSession } from '../../session/session.js';
3
+ import { generateId } from '../../shared/index.js';
4
+ import { createAgent, runAgent } from '../agent.js';
5
+ import { applyPatchToParent, captureBaseline, captureDeltaPatch, createWorktree, removeWorktree, } from '../worktree.js';
6
+ /** 运行一个按类型派发的子 agent(spec: multi-agent-design §4.5)。
7
+ *
8
+ * Host 端实现:查 agentRegistry 获取 AgentDefinition → 创建隔离子 session(agentType 记录)
9
+ * → 构建子 agent(专属 prompt + 受限工具集 + yield)→ 运行到 yield 或完成 → 返回结果。
10
+ * 发射 subagent_start/subagent_end 事件供父 agent 转发(spec §4.5 step 7)。
11
+ * abort 链接父→子。maxRecursion 控制子 agent 能否再递归派生 task(spec §4.5 step 4)。
12
+ * def.isolated 时在 git worktree 中运行,结束后把 delta 自动 apply 回父仓库(spec §4.6)。
13
+ * request.background 时 fork 异步运行,立即返回 running(spec §4.7)。 */
14
+ export async function runSubAgent(deps, parent, request) {
15
+ // 1. 查 agent 类型
16
+ if (!deps.agentRegistry) {
17
+ return { _tag: 'error', error: 'task tool unavailable: no agent registry is wired' };
18
+ }
19
+ const def = deps.agentRegistry.get(request.agentType);
20
+ if (!def) {
21
+ return {
22
+ _tag: 'error',
23
+ error: `Unknown agent type: ${request.agentType} is not a valid agent type`,
24
+ };
25
+ }
26
+ const title = request.description?.trim() ||
27
+ `Sub-agent (${request.agentType}): ${request.prompt.slice(0, 60)}`;
28
+ const childId = generateId();
29
+ const yielded = [];
30
+ // 发射 subagent_start 事件(spec §4.5 step 7)
31
+ deps._subagentEventSink?.({
32
+ _tag: 'subagent_start',
33
+ childId,
34
+ agentType: request.agentType,
35
+ description: request.description ?? '',
36
+ background: request.background ?? false,
37
+ });
38
+ // 2. 创建子 session(记录 agentType)
39
+ let childSession;
40
+ try {
41
+ childSession = await createSession(deps.db, title, parent.session.projectId ?? undefined, request.agentType);
42
+ }
43
+ catch (e) {
44
+ return { _tag: 'error', error: e instanceof Error ? e.message : String(e) };
45
+ }
46
+ // 3. worktree 隔离(isolated agent):失败回退共享 cwd
47
+ let worktreePath;
48
+ let baseline;
49
+ if (def.isolated) {
50
+ try {
51
+ baseline = await captureBaseline(deps.cwd);
52
+ worktreePath = await createWorktree(deps.cwd, `subagent-${childSession.id}`);
53
+ }
54
+ catch (e) {
55
+ console.warn(`[subagent] worktree creation failed, falling back to shared cwd: ${e instanceof Error ? e.message : e}`);
56
+ }
57
+ }
58
+ const childCwd = worktreePath ?? deps.cwd;
59
+ // 实际运行子 agent 的内部函数(sync 与 background 共用)
60
+ const runBody = async () => {
61
+ // 4. 构建子 agent 配置:工具集隔离 + 模型覆盖 + 递归限制 + yield
62
+ const parentDepth = deps._subagentDepth ?? 0;
63
+ const childDepth = parentDepth + 1;
64
+ const declaredTools = def.tools ?? parent.config.tools;
65
+ const maxRec = def.maxRecursion ?? 0;
66
+ const baseTools = childDepth > maxRec ? declaredTools.filter((t) => t !== 'task') : declaredTools;
67
+ const childTools = Array.from(new Set([...baseTools, 'yield']));
68
+ const childConfig = {
69
+ ...parent.config,
70
+ systemPrompt: def.systemPrompt,
71
+ // 子 agent 走整段 systemPrompt 替换,清除父的 role override 避免干扰
72
+ agentRolePrompt: undefined,
73
+ tools: childTools,
74
+ ...(def.model ? { model: def.model } : {}),
75
+ ...(request.model ? { model: request.model } : {}),
76
+ };
77
+ // 子 agent 的 deps:覆盖 cwd(worktree)+ 注入 yield 收集器 + 递归深度
78
+ const childDeps = {
79
+ ...deps,
80
+ cwd: childCwd,
81
+ _subagentYieldCollector: (data) => {
82
+ yielded.push(data);
83
+ },
84
+ _subagentDepth: childDepth,
85
+ };
86
+ const childState = await createAgent(childSession, childConfig, childDeps);
87
+ // abort 链接:父 abort 则子 abort
88
+ if (parent.abortController.signal.aborted) {
89
+ childState.abortController.abort();
90
+ }
91
+ else {
92
+ parent.abortController.signal.addEventListener('abort', () => childState.abortController.abort(), { once: true });
93
+ }
94
+ // 运行子 agent loop
95
+ const childPrompt = request.context
96
+ ? `CONTEXT\n${request.context}\n\nASSIGNMENT\n${request.prompt}`
97
+ : request.prompt;
98
+ const text = [];
99
+ let errMsg = null;
100
+ try {
101
+ for await (const ev of runAgent(childState, [{ _tag: 'text', text: childPrompt }], childDeps)) {
102
+ if (ev._tag === 'text_delta') {
103
+ text.push(ev.text);
104
+ }
105
+ else if (ev._tag === 'error') {
106
+ const e = ev.error;
107
+ errMsg = e._tag === 'unexpected' || e._tag === 'provider' ? e.message : e._tag;
108
+ }
109
+ }
110
+ }
111
+ catch (e) {
112
+ errMsg = e instanceof Error ? e.message : String(e);
113
+ }
114
+ // 5. worktree 回传:仅成功时把 delta apply 回父仓库(spec §4.6);无论成败都清理 worktree
115
+ if (baseline && worktreePath) {
116
+ if (errMsg === null) {
117
+ try {
118
+ const patch = await captureDeltaPatch(worktreePath, baseline);
119
+ await applyPatchToParent(deps.cwd, patch, `agent(isolated): ${title}`);
120
+ }
121
+ catch (e) {
122
+ console.warn(`[subagent] worktree apply failed: ${e instanceof Error ? e.message : e}`);
123
+ }
124
+ }
125
+ removeWorktree(deps.cwd, worktreePath);
126
+ }
127
+ const success = errMsg === null;
128
+ // 发射 subagent_end 事件(spec §4.5 step 7)
129
+ deps._subagentEventSink?.({
130
+ _tag: 'subagent_end',
131
+ childId,
132
+ agentType: request.agentType,
133
+ success,
134
+ ...(success ? { output: text.join('') } : {}),
135
+ });
136
+ if (errMsg !== null) {
137
+ return { _tag: 'error', error: errMsg, sessionId: childSession.id };
138
+ }
139
+ const data = yielded.length > 0 ? (yielded.length === 1 ? yielded[0] : yielded) : undefined;
140
+ return {
141
+ _tag: 'success',
142
+ output: text.join(''),
143
+ sessionId: childSession.id,
144
+ ...(data !== undefined ? { data } : {}),
145
+ };
146
+ };
147
+ // 6. background 模式:fork 异步运行,立即返回 running;完成时向父 session 注入合成通知
148
+ if (request.background) {
149
+ const jobId = childSession.id;
150
+ void runBody()
151
+ .then((result) => {
152
+ const success = result._tag === 'success';
153
+ const output = success ? result.output : result.error;
154
+ const tag = success ? 'task_result' : 'task_error';
155
+ const synthetic = `<task id="${childSession.id}" state="${success ? 'completed' : 'failed'}">\n<${tag}>\n${output}\n</${tag}>\n</task>`;
156
+ void appendMessage(deps.db, parent.session.id, {
157
+ role: 'user',
158
+ content: [{ _tag: 'text', text: synthetic }],
159
+ }).catch((e) => {
160
+ // 通知消息持久化失败:任务已算完但父 session 收不到完成通知——记录避免静默丢失。
161
+ console.warn('[subagent] background 通知消息持久化失败:', e instanceof Error ? e.message : String(e));
162
+ });
163
+ })
164
+ .catch((e) => {
165
+ // background 子 agent 执行或合成失败:父 session 永远收不到结果,记录避免静默丢失。
166
+ console.warn('[subagent] background 子 agent 执行失败:', e instanceof Error ? e.message : String(e));
167
+ });
168
+ return { _tag: 'running', jobId, sessionId: childSession.id };
169
+ }
170
+ return runBody();
171
+ }
@@ -0,0 +1,5 @@
1
+ import type { AgentEvent, AgentState } from '../../shared/types/agent.js';
2
+ /** Process <todo:*> tags embedded in assistant text.
3
+ * Parses tags, applies them to state.todoPhases via applyTodoTags,
4
+ * yields todo_update event on success, injects steering on error/view. */
5
+ export declare function processTodoTags(state: AgentState, text: string): AsyncGenerator<AgentEvent>;
@@ -0,0 +1,26 @@
1
+ import { formatSummary } from '../../tools/builtin/todo.js';
2
+ import { injectSteering } from '../steering.js';
3
+ import { applyTodoTags } from '../todo-tags.js';
4
+ /** Process <todo:*> tags embedded in assistant text.
5
+ * Parses tags, applies them to state.todoPhases via applyTodoTags,
6
+ * yields todo_update event on success, injects steering on error/view. */
7
+ export async function* processTodoTags(state, text) {
8
+ if (text.length === 0)
9
+ return;
10
+ const result = applyTodoTags(state.todoPhases, text);
11
+ if (result.errors.length > 0) {
12
+ // Inject error feedback into steering queue for next turn
13
+ injectSteering(state, `<todo-tag-errors>\n${result.errors.join('\n')}\n</todo-tag-errors>`);
14
+ }
15
+ if (result.hasView) {
16
+ // View request: inject current state (with seq) into steering
17
+ const summary = formatSummary(result.phases, [], true);
18
+ injectSteering(state, `<todo-state>\n${summary}\n</todo-state>`);
19
+ }
20
+ // Emit event + update state if anything happened
21
+ const tagsFound = result.errors.length > 0 || result.hasView || result.phases !== state.todoPhases;
22
+ if (tagsFound) {
23
+ state.todoPhases = result.phases;
24
+ yield { _tag: 'todo_update', phases: result.phases };
25
+ }
26
+ }
@@ -1,7 +1,8 @@
1
1
  import { chatStream as llmChatStream } from '../llm/provider.js';
2
2
  import type { AgentEvent, AgentState } from '../shared/types/agent.js';
3
- import type { SubAgentRequest, SubAgentResult } from '../shared/types/tool.js';
4
3
  import type { AgentDependencies } from './types.js';
4
+ export { compactContext } from './loop/compaction.js';
5
+ export { runSubAgent } from './loop/subagent.js';
5
6
  type LoopDeps = AgentDependencies & {
6
7
  chatStream?: typeof llmChatStream;
7
8
  /** 子 agent 运行时注入:yield 工具的结果收集器(透传到 ToolContext.collectYield)。 */
@@ -13,23 +14,4 @@ type LoopDeps = AgentDependencies & {
13
14
  readonly _subagentDepth?: number;
14
15
  };
15
16
  export type { LoopDeps };
16
- /** 运行一个按类型派发的子 agent(spec: multi-agent-design §4.5)。
17
- *
18
- * Host 端实现:查 agentRegistry 获取 AgentDefinition → 创建隔离子 session(agentType 记录)
19
- * → 构建子 agent(专属 prompt + 受限工具集 + yield)→ 运行到 yield 或完成 → 返回结果。
20
- * 发射 subagent_start/subagent_end 事件供父 agent 转发(spec §4.5 step 7)。
21
- * abort 链接父→子。maxRecursion 控制子 agent 能否再递归派生 task(spec §4.5 step 4)。
22
- * def.isolated 时在 git worktree 中运行,结束后把 delta 自动 apply 回父仓库(spec §4.6)。
23
- * request.background 时 fork 异步运行,立即返回 running(spec §4.7)。 */
24
- export declare function runSubAgent(deps: LoopDeps, parent: AgentState, request: SubAgentRequest): Promise<SubAgentResult>;
25
- /**
26
- * 执行会话压缩并刷新 token 预算。
27
- *
28
- * 自动压缩(agentLoop 阈值触发)与手动 /compact 共用此逻辑:复用
29
- * createSummarizer + runCompaction,压缩改写消息历史后标记下一段 trigger='compaction'。
30
- *
31
- * 成功时 yield 一条 text_delta 通知:手动 /compact 透传给用户;自动压缩由调用方
32
- * 静默消费。失败时抛错,由调用方决定是否记录(自动压缩非致命,仅 console.warn)。
33
- */
34
- export declare function compactContext(state: AgentState, deps: LoopDeps): AsyncGenerator<AgentEvent>;
35
17
  export declare function agentLoop(state: AgentState, deps: LoopDeps): AsyncGenerator<AgentEvent>;