minimal-agent 0.11.0 → 0.13.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 (181) hide show
  1. package/package.json +5 -1
  2. package/plugins/workflow-runner/plugin.js +27 -15
  3. package/plugins/workflow-runner/plugin.ts +34 -20
  4. package/plugins/workflow-runner/src/agentCommon.js +13 -12
  5. package/plugins/workflow-runner/src/agentCommon.ts +22 -13
  6. package/plugins/workflow-runner/src/expressions.js +29 -8
  7. package/plugins/workflow-runner/src/expressions.ts +28 -10
  8. package/plugins/workflow-runner/src/index.js +50 -20
  9. package/plugins/workflow-runner/src/index.ts +75 -31
  10. package/plugins/workflow-runner/src/multimodal.js +2 -1
  11. package/plugins/workflow-runner/src/multimodal.ts +2 -1
  12. package/plugins/workflow-runner/src/recovery.js +9 -2
  13. package/plugins/workflow-runner/src/recovery.ts +9 -2
  14. package/plugins/workflow-runner/src/runner.js +31 -5
  15. package/plugins/workflow-runner/src/runner.ts +33 -5
  16. package/plugins/workflow-runner/src/stepExecutors/assert.js +2 -1
  17. package/plugins/workflow-runner/src/stepExecutors/assert.ts +3 -1
  18. package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
  19. package/plugins/workflow-runner/src/stepExecutors/skill.ts +12 -7
  20. package/plugins/workflow-runner/src/stepExecutors/tool.js +91 -61
  21. package/plugins/workflow-runner/src/stepExecutors/tool.ts +111 -71
  22. package/plugins/workflow-runner/src/stepExecutors/verify.js +6 -3
  23. package/plugins/workflow-runner/src/stepExecutors/verify.ts +7 -3
  24. package/plugins/workflow-runner/src/taskIdentity.js +197 -21
  25. package/plugins/workflow-runner/src/taskIdentity.ts +201 -20
  26. package/plugins/workflow-runner/src/toolRuntime.js +48 -0
  27. package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
  28. package/plugins/workflow-runner/src/types.js +113 -7
  29. package/plugins/workflow-runner/src/types.ts +137 -14
  30. package/plugins/workflow-runner/src/workflowState.js +455 -54
  31. package/plugins/workflow-runner/src/workflowState.ts +508 -63
  32. package/plugins/workflow-runner/test/cli.e2e.test.ts +20 -0
  33. package/plugins/workflow-runner/test/context-files.test.ts +47 -16
  34. package/plugins/workflow-runner/test/e2eNewLayout.test.ts +314 -0
  35. package/plugins/workflow-runner/test/engineRobustness.test.ts +9 -1
  36. package/plugins/workflow-runner/test/expressions.test.ts +42 -0
  37. package/plugins/workflow-runner/test/hostCapability.test.ts +9 -3
  38. package/plugins/workflow-runner/test/illustrate.load.test.ts +2 -2
  39. package/plugins/workflow-runner/test/multimodal.test.ts +61 -10
  40. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +19 -0
  41. package/plugins/workflow-runner/test/ppt.load.test.ts +15 -15
  42. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +3 -2
  43. package/plugins/workflow-runner/test/research.load.test.ts +328 -0
  44. package/plugins/workflow-runner/test/runnerPreflight.test.ts +68 -11
  45. package/plugins/workflow-runner/test/stateLifecycle.test.ts +60 -12
  46. package/plugins/workflow-runner/test/taskArbitration.test.ts +19 -3
  47. package/plugins/workflow-runner/test/taskLifecycle.test.ts +61 -7
  48. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
  49. package/plugins/workflow-runner/test/webppt.load.test.ts +4 -4
  50. package/plugins/workflow-runner/test/word.load.test.ts +9 -9
  51. package/skills/skill-creator/scripts/run_eval.py +3 -2
  52. package/src/cli/print.js +36 -4
  53. package/src/cli/print.ts +42 -4
  54. package/src/cli/streamJson.js +19 -4
  55. package/src/cli/streamJson.ts +17 -4
  56. package/src/config/configDefaults.js +25 -0
  57. package/src/config/configDefaults.ts +33 -0
  58. package/src/config/configFile.js +2 -21
  59. package/src/config/configFile.ts +11 -24
  60. package/src/config/configManagement.js +1 -1
  61. package/src/config/configManagement.ts +2 -2
  62. package/src/config/configService.js +24 -0
  63. package/src/config/configService.ts +23 -0
  64. package/src/context/compact.js +4 -0
  65. package/src/context/compact.ts +4 -0
  66. package/src/context/contextBundle.js +38 -13
  67. package/src/context/contextBundle.ts +47 -13
  68. package/src/harness/stallGuard.js +19 -4
  69. package/src/harness/stallGuard.ts +30 -7
  70. package/src/infra/verification.js +25 -18
  71. package/src/infra/verification.ts +50 -18
  72. package/src/loop.js +95 -11
  73. package/src/loop.ts +125 -16
  74. package/src/main.js +8 -1
  75. package/src/main.tsx +7 -1
  76. package/src/memory/constants.js +10 -0
  77. package/src/memory/constants.ts +13 -0
  78. package/src/memory/episodicMemory.js +274 -2
  79. package/src/memory/episodicMemory.ts +306 -1
  80. package/src/memory/fileStore.js +24 -4
  81. package/src/memory/fileStore.ts +22 -2
  82. package/src/memory/memoryService.js +79 -12
  83. package/src/memory/memoryService.ts +108 -9
  84. package/src/memory/repository.js +415 -85
  85. package/src/memory/repository.ts +437 -86
  86. package/src/memory/turnJournal.js +95 -0
  87. package/src/memory/turnJournal.ts +120 -0
  88. package/src/memory/types.ts +16 -0
  89. package/src/plugins/pluginApi.ts +30 -6
  90. package/src/plugins/pluginRunner.js +23 -8
  91. package/src/plugins/pluginRunner.ts +32 -13
  92. package/src/prompts/policyRegistry.js +55 -1
  93. package/src/prompts/policyRegistry.ts +55 -1
  94. package/src/prompts/promptAssembly.js +39 -7
  95. package/src/prompts/promptAssembly.ts +145 -59
  96. package/src/session/runAgentTurn.js +43 -21
  97. package/src/session/runAgentTurn.ts +68 -26
  98. package/src/session/runTurn.js +11 -46
  99. package/src/session/runTurn.ts +12 -46
  100. package/src/session/runtimeStore.js +149 -0
  101. package/src/session/runtimeStore.ts +197 -0
  102. package/src/session/sessionState.js +5 -0
  103. package/src/session/sessionState.ts +14 -0
  104. package/src/tools/advisor/advisor.js +76 -9
  105. package/src/tools/advisor/advisor.ts +79 -9
  106. package/src/tools/agent/agent.js +143 -14
  107. package/src/tools/agent/agent.ts +149 -14
  108. package/src/tools/agent/agentTaskService.js +60 -0
  109. package/src/tools/agent/agentTaskService.ts +113 -0
  110. package/src/tools/agent/agentTypes.js +5 -0
  111. package/src/tools/agent/agentTypes.ts +5 -0
  112. package/src/tools/bash/bash.js +80 -15
  113. package/src/tools/bash/bash.ts +83 -15
  114. package/src/tools/capabilities.js +5 -0
  115. package/src/tools/capabilities.ts +11 -1
  116. package/src/tools/edit/edit.js +7 -4
  117. package/src/tools/edit/edit.ts +7 -4
  118. package/src/tools/edit/multi-edit.js +4 -2
  119. package/src/tools/edit/multi-edit.ts +4 -2
  120. package/src/tools/glob/glob.js +156 -40
  121. package/src/tools/glob/glob.ts +185 -46
  122. package/src/tools/index.js +35 -17
  123. package/src/tools/index.ts +81 -20
  124. package/src/tools/lsp/lsp.js +165 -22
  125. package/src/tools/lsp/lsp.ts +206 -36
  126. package/src/tools/powershell/powershell.js +134 -27
  127. package/src/tools/powershell/powershell.ts +171 -30
  128. package/src/tools/presentresult/presentresult.js +10 -0
  129. package/src/tools/presentresult/presentresult.ts +10 -0
  130. package/src/tools/read/read.js +249 -29
  131. package/src/tools/read/read.ts +396 -59
  132. package/src/tools/shared/diffSummary.js +44 -0
  133. package/src/tools/shared/diffSummary.ts +63 -0
  134. package/src/tools/shared/fileActivity.js +2 -1
  135. package/src/tools/shared/fileActivity.ts +3 -0
  136. package/src/tools/shared/permanentDeleteGuard.js +57 -0
  137. package/src/tools/shared/permanentDeleteGuard.ts +71 -0
  138. package/src/tools/shared/toolOutputStore.js +97 -0
  139. package/src/tools/shared/toolOutputStore.ts +110 -0
  140. package/src/tools/skill/skill.js +86 -0
  141. package/src/tools/skill/skill.ts +94 -0
  142. package/src/tools/snip/snip.js +67 -0
  143. package/src/tools/snip/snip.ts +80 -0
  144. package/src/tools/tasks/backgroundTaskService.js +79 -28
  145. package/src/tools/tasks/backgroundTaskService.ts +165 -49
  146. package/src/tools/tasks/taskManagement.js +163 -0
  147. package/src/tools/tasks/taskManagement.ts +180 -0
  148. package/src/tools/tasks/taskOutput.js +64 -14
  149. package/src/tools/tasks/taskOutput.ts +102 -23
  150. package/src/tools/tasks/taskStop.js +48 -8
  151. package/src/tools/tasks/taskStop.ts +69 -9
  152. package/src/tools/tasks/taskStore.js +46 -0
  153. package/src/tools/tasks/taskStore.ts +77 -0
  154. package/src/tools/terminal/terminalCapture.js +167 -0
  155. package/src/tools/terminal/terminalCapture.ts +179 -0
  156. package/src/tools/trash/trash.js +233 -0
  157. package/src/tools/trash/trash.ts +283 -0
  158. package/src/tools/webbrowser/browserControl.js +5 -1
  159. package/src/tools/webbrowser/browserControl.ts +7 -1
  160. package/src/tools/webbrowser/webbrowser.js +8 -4
  161. package/src/tools/webbrowser/webbrowser.ts +8 -4
  162. package/src/tools/worktree/sessionWorktree.js +128 -0
  163. package/src/tools/worktree/sessionWorktree.ts +158 -0
  164. package/src/tools/write/write.js +6 -2
  165. package/src/tools/write/write.ts +6 -2
  166. package/src/types.ts +22 -4
  167. package/src/ui/hooks/useChat.js +31 -11
  168. package/src/ui/hooks/useChat.ts +31 -13
  169. package/src/web/protocol.ts +78 -3
  170. package/workflows/adaptive-research.yaml +5 -5
  171. package/workflows/book-review-short.yaml +5 -5
  172. package/workflows/e2e-write-greet.yaml +2 -2
  173. package/workflows/illustrate.yaml +12 -12
  174. package/workflows/plan.yaml +18 -18
  175. package/workflows/ppt.yaml +201 -201
  176. package/workflows/ralph-loop.yaml +3 -1
  177. package/workflows/research.md +449 -0
  178. package/workflows/research.yaml +873 -0
  179. package/workflows/webppt.yaml +47 -47
  180. package/workflows/word.yaml +80 -80
  181. package/workflows/youtube-shorts.yaml +25 -25
@@ -27,6 +27,7 @@ import { getSkillList, formatSkillHint } from './skillList.js';
27
27
  import { getPluginMetaList, formatPluginHint } from './pluginList.js';
28
28
  import { memoryService } from '../memory/memoryService.js';
29
29
  import { countTextTokens } from '../context/tokenCounter.js';
30
+ import { effectiveTaskStatus, listManagedTasks, } from '../tools/tasks/taskStore.js';
30
31
  import { assembleContextBundle, contextFragment, DEFAULT_TRANSIENT_BUDGET_TOKENS, } from '../context/contextBundle.js';
31
32
  import { assertPromptPolicyRegistry, capabilitiesForTools, enabledPolicyIds, renderCapabilityPolicyLayer, renderStablePolicyLayer, } from './policyRegistry.js';
32
33
  const stablePromptCache = new Map();
@@ -90,9 +91,9 @@ ${rules}`;
90
91
  }
91
92
  async function buildMemoryTransient(runtime, tools) {
92
93
  const names = new Set(tools.map((tool) => tool.name));
93
- const memoryAllowed = runtime.memoryAccess !== undefined && runtime.memoryAccess !== 'denied'
94
+ const memoryAllowed = (runtime.memoryAccess !== undefined && runtime.memoryAccess !== 'denied') ||
94
95
  // Compatibility is explicit-only: omitted ownerLocalSession never grants access.
95
- || (runtime.memoryAccess === undefined && runtime.ownerLocalSession === true);
96
+ (runtime.memoryAccess === undefined && runtime.ownerLocalSession === true);
96
97
  if (!memoryAllowed || !names.has('RecallMemory'))
97
98
  return null;
98
99
  try {
@@ -204,8 +205,12 @@ export async function buildTransientBundle(runtime, profile, tools, options = {}
204
205
  runtime.supervisedBrowser
205
206
  ? '宿主提供可监督浏览器:调用 WebBrowser 时用户可看到过程并在验证或登录环节接管。'
206
207
  : '',
207
- runtime.interactiveAsk ? '' : '当前宿主不能交互提问;不要等待用户选择,缺少关键输入时说明阻塞。',
208
- ].filter(Boolean).join('\n');
208
+ runtime.interactiveAsk
209
+ ? ''
210
+ : '当前宿主不能交互提问;不要等待用户选择,缺少关键输入时说明阻塞。',
211
+ ]
212
+ .filter(Boolean)
213
+ .join('\n');
209
214
  const fragments = [
210
215
  contextFragment({
211
216
  id: 'runtime.host-state',
@@ -216,7 +221,7 @@ export async function buildTransientBundle(runtime, profile, tools, options = {}
216
221
  content: runtimeLines,
217
222
  }),
218
223
  ];
219
- const [memory, advisor, skillHint, pluginHint, projectInstructions] = await Promise.all([
224
+ const [memory, advisor, skillHint, pluginHint, projectInstructions, activeTasks,] = await Promise.all([
220
225
  buildMemoryTransient(runtime, tools),
221
226
  buildAdvisorTransient(profile, tools),
222
227
  getSkillList(runtime.cwd, new Set(tools.map((tool) => tool.name)))
@@ -224,13 +229,28 @@ export async function buildTransientBundle(runtime, profile, tools, options = {}
224
229
  .catch(() => ''),
225
230
  options.includePlugins === false
226
231
  ? Promise.resolve('')
227
- : getPluginMetaList(runtime.cwd).then(formatPluginHint).catch(() => ''),
232
+ : getPluginMetaList(runtime.cwd)
233
+ .then(formatPluginHint)
234
+ .catch(() => ''),
228
235
  options.includeProjectInstructions === false
229
236
  ? Promise.resolve('')
230
237
  : loadProjectPromptInstructions(runtime.cwd).catch(() => ''),
238
+ runtime.sessionId &&
239
+ tools.some((tool) => ['TaskCreate', 'TaskGet', 'TaskUpdate', 'TaskList'].includes(tool.name))
240
+ ? listManagedTasks({
241
+ workspaceId: runtime.workspaceId,
242
+ sessionId: runtime.sessionId,
243
+ cwd: runtime.cwd,
244
+ }).catch(() => [])
245
+ : Promise.resolve([]),
231
246
  ]);
232
247
  if (memory) {
233
- const priorities = { persona: 82, preferences: 78, relevant: 70, health: 10 };
248
+ const priorities = {
249
+ persona: 82,
250
+ preferences: 78,
251
+ relevant: 70,
252
+ health: 10,
253
+ };
234
254
  for (const section of memory.sections) {
235
255
  fragments.push(contextFragment({
236
256
  id: `memory.${section.id}`,
@@ -276,6 +296,18 @@ export async function buildTransientBundle(runtime, profile, tools, options = {}
276
296
  required: true,
277
297
  content: `# 项目指令\n以下内容来自当前工作目录的 minimal-agent.md。行为约束和仓库约定必须遵守;架构事实若与当前代码冲突,以实际代码为准。不要擅自重写该文件。\n\n${projectInstructions}`,
278
298
  }));
299
+ const unfinished = activeTasks.filter((task) => task.status !== 'completed');
300
+ if (unfinished.length)
301
+ fragments.push(contextFragment({
302
+ id: 'tasks.active',
303
+ kind: 'task',
304
+ authority: 'constraint',
305
+ priority: 85,
306
+ content: `# Active tasks\n${unfinished
307
+ .slice(0, 20)
308
+ .map((task) => `- ${task.id} [${effectiveTaskStatus(task, activeTasks)}] ${task.title}`)
309
+ .join('\n')}`,
310
+ }));
279
311
  return assembleContextBundle(fragments, runtime.transientBudgetTokens ?? DEFAULT_TRANSIENT_BUDGET_TOKENS);
280
312
  }
281
313
  /**
@@ -29,6 +29,10 @@ import { getSkillList, formatSkillHint } from './skillList.js';
29
29
  import { getPluginMetaList, formatPluginHint } from './pluginList.js';
30
30
  import { memoryService } from '../memory/memoryService.js';
31
31
  import { countTextTokens } from '../context/tokenCounter.js';
32
+ import {
33
+ effectiveTaskStatus,
34
+ listManagedTasks,
35
+ } from '../tools/tasks/taskStore.js';
32
36
  import {
33
37
  assembleContextBundle,
34
38
  contextFragment,
@@ -77,6 +81,7 @@ export interface PromptRuntimeContext {
77
81
  surface: PromptSurface;
78
82
  cwd: string;
79
83
  workspaceId?: string;
84
+ sessionId?: string;
80
85
  interactiveAsk: boolean;
81
86
  supervisedBrowser: boolean;
82
87
  /** Trusted host decision. Defaults to denied; never inferred from profile. */
@@ -128,18 +133,22 @@ async function buildAdvisorStatusLine(): Promise<string> {
128
133
  const advisor = await configService.advisor();
129
134
  if (!advisor) return 'Advisor 状态:未配置。';
130
135
  const mode = advisor.mode === 'auto' ? '自动' : '仅手动';
131
- const vision = advisor.supportsVision && advisor.shareSensitive
132
- ? '允许图片'
133
- : advisor.supportsVision
134
- ? '支持图片但敏感共享关闭'
135
- : '不支持图片';
136
+ const vision =
137
+ advisor.supportsVision && advisor.shareSensitive
138
+ ? '允许图片'
139
+ : advisor.supportsVision
140
+ ? '支持图片但敏感共享关闭'
141
+ : '不支持图片';
136
142
  return `Advisor 状态:${advisor.enabled ? '已启用' : '已关闭'},${advisor.provider.name}/${advisor.provider.model},${mode},${vision},超时 ${Math.round(advisor.timeoutMs / 1000)} 秒。`;
137
143
  } catch {
138
144
  return 'Advisor 状态:暂时无法读取。';
139
145
  }
140
146
  }
141
147
 
142
- async function buildToolLayer(tools: Tool[], context: StablePromptContext): Promise<string> {
148
+ async function buildToolLayer(
149
+ tools: Tool[],
150
+ context: StablePromptContext,
151
+ ): Promise<string> {
143
152
  const catalog = tools.map((tool) => tool.name).join(', ');
144
153
  const rules = renderCapabilityPolicyLayer(context);
145
154
 
@@ -162,11 +171,14 @@ export interface AssemblePromptOptions {
162
171
  async function buildMemoryTransient(
163
172
  runtime: PromptRuntimeContext,
164
173
  tools: Tool[],
165
- ): Promise<Awaited<ReturnType<typeof memoryService.buildContextBundle>> | null> {
174
+ ): Promise<Awaited<
175
+ ReturnType<typeof memoryService.buildContextBundle>
176
+ > | null> {
166
177
  const names = new Set(tools.map((tool) => tool.name));
167
- const memoryAllowed = runtime.memoryAccess !== undefined && runtime.memoryAccess !== 'denied'
178
+ const memoryAllowed =
179
+ (runtime.memoryAccess !== undefined && runtime.memoryAccess !== 'denied') ||
168
180
  // Compatibility is explicit-only: omitted ownerLocalSession never grants access.
169
- || (runtime.memoryAccess === undefined && runtime.ownerLocalSession === true);
181
+ (runtime.memoryAccess === undefined && runtime.ownerLocalSession === true);
170
182
  if (!memoryAllowed || !names.has('RecallMemory')) return null;
171
183
  try {
172
184
  // One repository snapshot feeds persona/preferences/relevance/diagnostics.
@@ -180,8 +192,12 @@ async function buildMemoryTransient(
180
192
  }
181
193
  }
182
194
 
183
- async function buildAdvisorTransient(profile: PromptProfile, tools: Tool[]): Promise<string> {
184
- if (profile !== 'main' || !tools.some((tool) => tool.name === 'Advisor')) return '';
195
+ async function buildAdvisorTransient(
196
+ profile: PromptProfile,
197
+ tools: Tool[],
198
+ ): Promise<string> {
199
+ if (profile !== 'main' || !tools.some((tool) => tool.name === 'Advisor'))
200
+ return '';
185
201
  return buildAdvisorStatusLine();
186
202
  }
187
203
 
@@ -280,7 +296,9 @@ export async function assembleStablePrompt(
280
296
  ].join('|');
281
297
  const cached = stablePromptCache.get(key);
282
298
  if (cached) return cached;
283
- const base = (await buildStableBase(options.cwd, options.tools, profile, runtime)).base;
299
+ const base = (
300
+ await buildStableBase(options.cwd, options.tools, profile, runtime)
301
+ ).base;
284
302
  stablePromptCache.set(key, base);
285
303
  if (stablePromptCache.size > 64) {
286
304
  stablePromptCache.delete(stablePromptCache.keys().next().value!);
@@ -310,15 +328,22 @@ export async function buildTransientBundle(
310
328
  runtime: PromptRuntimeContext,
311
329
  profile: PromptProfile,
312
330
  tools: Tool[],
313
- options: Pick<AssemblePromptOptions, 'includeProjectInstructions' | 'includePlugins'> = {},
331
+ options: Pick<
332
+ AssemblePromptOptions,
333
+ 'includeProjectInstructions' | 'includePlugins'
334
+ > = {},
314
335
  ): Promise<ContextBundle> {
315
336
  const runtimeLines = [
316
337
  `【运行时状态】本地日期:${localDate()}。时效敏感信息必须在线核实并使用明确日期或年份。`,
317
338
  runtime.supervisedBrowser
318
339
  ? '宿主提供可监督浏览器:调用 WebBrowser 时用户可看到过程并在验证或登录环节接管。'
319
340
  : '',
320
- runtime.interactiveAsk ? '' : '当前宿主不能交互提问;不要等待用户选择,缺少关键输入时说明阻塞。',
321
- ].filter(Boolean).join('\n');
341
+ runtime.interactiveAsk
342
+ ? ''
343
+ : '当前宿主不能交互提问;不要等待用户选择,缺少关键输入时说明阻塞。',
344
+ ]
345
+ .filter(Boolean)
346
+ .join('\n');
322
347
  const fragments: ContextFragment[] = [
323
348
  contextFragment({
324
349
  id: 'runtime.host-state',
@@ -329,7 +354,14 @@ export async function buildTransientBundle(
329
354
  content: runtimeLines,
330
355
  }),
331
356
  ];
332
- const [memory, advisor, skillHint, pluginHint, projectInstructions] = await Promise.all([
357
+ const [
358
+ memory,
359
+ advisor,
360
+ skillHint,
361
+ pluginHint,
362
+ projectInstructions,
363
+ activeTasks,
364
+ ] = await Promise.all([
333
365
  buildMemoryTransient(runtime, tools),
334
366
  buildAdvisorTransient(profile, tools),
335
367
  getSkillList(runtime.cwd, new Set(tools.map((tool) => tool.name)))
@@ -337,54 +369,102 @@ export async function buildTransientBundle(
337
369
  .catch(() => ''),
338
370
  options.includePlugins === false
339
371
  ? Promise.resolve('')
340
- : getPluginMetaList(runtime.cwd).then(formatPluginHint).catch(() => ''),
372
+ : getPluginMetaList(runtime.cwd)
373
+ .then(formatPluginHint)
374
+ .catch(() => ''),
341
375
  options.includeProjectInstructions === false
342
376
  ? Promise.resolve('')
343
377
  : loadProjectPromptInstructions(runtime.cwd).catch(() => ''),
378
+ runtime.sessionId &&
379
+ tools.some((tool) =>
380
+ ['TaskCreate', 'TaskGet', 'TaskUpdate', 'TaskList'].includes(tool.name),
381
+ )
382
+ ? listManagedTasks({
383
+ workspaceId: runtime.workspaceId,
384
+ sessionId: runtime.sessionId,
385
+ cwd: runtime.cwd,
386
+ }).catch(() => [])
387
+ : Promise.resolve([]),
344
388
  ]);
345
389
  if (memory) {
346
- const priorities = { persona: 82, preferences: 78, relevant: 70, health: 10 } as const;
390
+ const priorities = {
391
+ persona: 82,
392
+ preferences: 78,
393
+ relevant: 70,
394
+ health: 10,
395
+ } as const;
347
396
  for (const section of memory.sections) {
348
- fragments.push(contextFragment({
349
- id: `memory.${section.id}`,
350
- kind: section.id === 'persona' ? 'persona' : 'memory',
351
- authority: section.id === 'health' ? 'host' : 'user-evidence',
352
- priority: priorities[section.id],
353
- estimatedTokens: section.estimatedTokens,
354
- content: section.content,
355
- }));
397
+ fragments.push(
398
+ contextFragment({
399
+ id: `memory.${section.id}`,
400
+ kind: section.id === 'persona' ? 'persona' : 'memory',
401
+ authority: section.id === 'health' ? 'host' : 'user-evidence',
402
+ priority: priorities[section.id],
403
+ estimatedTokens: section.estimatedTokens,
404
+ content: section.content,
405
+ }),
406
+ );
356
407
  }
357
408
  }
358
- if (advisor) fragments.push(contextFragment({
359
- id: 'diagnostic.advisor',
360
- kind: 'diagnostic',
361
- authority: 'host',
362
- priority: 20,
363
- content: advisor,
364
- }));
365
- if (skillHint) fragments.push(contextFragment({
366
- id: 'skills.catalog',
367
- kind: 'skill',
368
- authority: 'constraint',
369
- priority: 45,
370
- clippable: true,
371
- content: skillHint,
372
- }));
373
- if (pluginHint) fragments.push(contextFragment({
374
- id: 'plugins.catalog',
375
- kind: 'plugin',
376
- authority: 'untrusted-data',
377
- priority: 50,
378
- content: pluginHint,
379
- }));
380
- if (projectInstructions) fragments.push(contextFragment({
381
- id: 'project.instructions',
382
- kind: 'project',
383
- authority: 'constraint',
384
- priority: 90,
385
- required: true,
386
- content: `# 项目指令\n以下内容来自当前工作目录的 minimal-agent.md。行为约束和仓库约定必须遵守;架构事实若与当前代码冲突,以实际代码为准。不要擅自重写该文件。\n\n${projectInstructions}`,
387
- }));
409
+ if (advisor)
410
+ fragments.push(
411
+ contextFragment({
412
+ id: 'diagnostic.advisor',
413
+ kind: 'diagnostic',
414
+ authority: 'host',
415
+ priority: 20,
416
+ content: advisor,
417
+ }),
418
+ );
419
+ if (skillHint)
420
+ fragments.push(
421
+ contextFragment({
422
+ id: 'skills.catalog',
423
+ kind: 'skill',
424
+ authority: 'constraint',
425
+ priority: 45,
426
+ clippable: true,
427
+ content: skillHint,
428
+ }),
429
+ );
430
+ if (pluginHint)
431
+ fragments.push(
432
+ contextFragment({
433
+ id: 'plugins.catalog',
434
+ kind: 'plugin',
435
+ authority: 'untrusted-data',
436
+ priority: 50,
437
+ content: pluginHint,
438
+ }),
439
+ );
440
+ if (projectInstructions)
441
+ fragments.push(
442
+ contextFragment({
443
+ id: 'project.instructions',
444
+ kind: 'project',
445
+ authority: 'constraint',
446
+ priority: 90,
447
+ required: true,
448
+ content: `# 项目指令\n以下内容来自当前工作目录的 minimal-agent.md。行为约束和仓库约定必须遵守;架构事实若与当前代码冲突,以实际代码为准。不要擅自重写该文件。\n\n${projectInstructions}`,
449
+ }),
450
+ );
451
+ const unfinished = activeTasks.filter((task) => task.status !== 'completed');
452
+ if (unfinished.length)
453
+ fragments.push(
454
+ contextFragment({
455
+ id: 'tasks.active',
456
+ kind: 'task',
457
+ authority: 'constraint',
458
+ priority: 85,
459
+ content: `# Active tasks\n${unfinished
460
+ .slice(0, 20)
461
+ .map(
462
+ (task) =>
463
+ `- ${task.id} [${effectiveTaskStatus(task, activeTasks)}] ${task.title}`,
464
+ )
465
+ .join('\n')}`,
466
+ }),
467
+ );
388
468
  return assembleContextBundle(
389
469
  fragments,
390
470
  runtime.transientBudgetTokens ?? DEFAULT_TRANSIENT_BUDGET_TOKENS,
@@ -397,12 +477,16 @@ export async function buildTransientBundle(
397
477
  * 直接调 assemblePromptEnvelope 再取 .transient 也能拿到同样结果,但会白算一遍 base
398
478
  * (27 个 SKILL.md + 项目指令共约 40 次文件读)。每回合都白算一遍不划算,故单独开口。
399
479
  */
400
- export async function buildTransientContext(options: AssemblePromptOptions): Promise<string> {
480
+ export async function buildTransientContext(
481
+ options: AssemblePromptOptions,
482
+ ): Promise<string> {
401
483
  return (await buildTransientPromptBundle(options)).text;
402
484
  }
403
485
 
404
486
  /** Structured counterpart used by the Harness for request-time budget reduction. */
405
- export async function buildTransientPromptBundle(options: AssemblePromptOptions): Promise<ContextBundle> {
487
+ export async function buildTransientPromptBundle(
488
+ options: AssemblePromptOptions,
489
+ ): Promise<ContextBundle> {
406
490
  const profile = options.profile ?? 'main';
407
491
  const runtime: PromptRuntimeContext = {
408
492
  surface: profile === 'main' ? 'cli' : 'subagent',
@@ -418,7 +502,9 @@ export async function buildTransientPromptBundle(options: AssemblePromptOptions)
418
502
  });
419
503
  }
420
504
 
421
- export async function assembleBasePrompt(options: AssemblePromptOptions): Promise<string> {
505
+ export async function assembleBasePrompt(
506
+ options: AssemblePromptOptions,
507
+ ): Promise<string> {
422
508
  const envelope = await assemblePromptEnvelope(options);
423
509
  return [envelope.base, envelope.transient].filter(Boolean).join('\n\n');
424
510
  }
@@ -1,10 +1,11 @@
1
- import { buildBaseSystemPrompt, buildTransientPromptBundle } from '../prompts/system.js';
1
+ import { buildBaseSystemPrompt, buildTransientPromptBundle, } from '../prompts/system.js';
2
2
  import { ALL_TOOLS } from '../tools/index.js';
3
3
  import { sessionFileFor } from '../context/sessionPath.js';
4
- import { getOrCreateSessionState, sessionStateKey } from './sessionState.js';
4
+ import { getOrCreateSessionState, sessionStateKey, } from './sessionState.js';
5
+ import { hydrateSessionWorktree } from '../tools/worktree/sessionWorktree.js';
5
6
  import { runTurn } from './runTurn.js';
6
7
  import { withWorkingDir } from '../bootstrap/workingDir.js';
7
- import { listSessionSummaries, updateSessionMetadata } from '../context/sessionRegistry.js';
8
+ import { listSessionSummaries, updateSessionMetadata, } from '../context/sessionRegistry.js';
8
9
  import { workspaceIndexService } from '../context/workspaceIndex.js';
9
10
  import { configService } from '../config/configService.js';
10
11
  import { createPromptCacheContext } from '../llm/promptCache.js';
@@ -17,9 +18,12 @@ deps = {}) {
17
18
  const session = (await listSessionSummaries(opts.cwd)).find((item) => item.id === opts.sessionId);
18
19
  let sessionModelId = session?.modelId;
19
20
  if (!opts.model && !sessionModelId) {
20
- sessionModelId = (await configService.getSnapshot()).config?.baseModelId ?? undefined;
21
+ sessionModelId =
22
+ (await configService.getSnapshot()).config?.baseModelId ?? undefined;
21
23
  if (sessionModelId) {
22
- await updateSessionMetadata(opts.cwd, opts.sessionId, { modelId: sessionModelId }).catch(() => { });
24
+ await updateSessionMetadata(opts.cwd, opts.sessionId, {
25
+ modelId: sessionModelId,
26
+ }).catch(() => { });
23
27
  }
24
28
  }
25
29
  const sessionProvider = !opts.model && sessionModelId
@@ -27,41 +31,62 @@ deps = {}) {
27
31
  : null;
28
32
  const provider = opts.model
29
33
  ? { ...opts.provider, model: opts.model }
30
- : sessionProvider ?? opts.provider;
31
- const workspace = await workspaceIndexService.register(opts.cwd).catch(() => undefined);
34
+ : (sessionProvider ?? opts.provider);
35
+ const workspace = await workspaceIndexService
36
+ .register(opts.cwd)
37
+ .catch(() => undefined);
32
38
  const sessionFile = sessionFileFor(opts.cwd, opts.sessionId);
33
39
  const events = [];
34
40
  let result = '';
35
41
  let error;
36
42
  let numTurns = 0;
37
- await withWorkingDir(opts.cwd, async () => {
43
+ const turnSessionState = opts.sessionState ??
44
+ getOrCreateSessionState(sessionStateKey({
45
+ surface: 'headless',
46
+ workspaceId: workspace?.id,
47
+ cwd: workspace?.id ? undefined : opts.cwd,
48
+ sessionId: opts.sessionId,
49
+ }));
50
+ await hydrateSessionWorktree({
51
+ sessionState: turnSessionState,
52
+ workspaceId: workspace?.id,
53
+ sessionId: opts.sessionId,
54
+ cwd: opts.cwd,
55
+ });
56
+ const effectiveCwd = turnSessionState.activeWorktree?.path ?? opts.cwd;
57
+ await withWorkingDir(effectiveCwd, async () => {
38
58
  // 稳定层进 history[0](runTurn 每轮重写它,必须逐字节可复现,见 runTurn.ts 的缓存不变量);
39
59
  // 易变层每轮单独计算,仅附加到当前用户消息的模型副本。
40
60
  const runtimeContext = createAgentRuntimeContext({
41
61
  surface: 'headless',
42
62
  profile: 'main',
43
- cwd: opts.cwd,
63
+ cwd: effectiveCwd,
44
64
  workspaceId: workspace?.id,
45
65
  sessionId: opts.sessionId,
46
- owner: opts.memoryAccess && opts.memoryAccess !== 'denied' ? 'local-owner' : 'untrusted',
66
+ owner: opts.memoryAccess && opts.memoryAccess !== 'denied'
67
+ ? 'local-owner'
68
+ : 'untrusted',
47
69
  memoryAccess: opts.memoryAccess ?? 'denied',
48
70
  interactiveAsk: false,
49
71
  });
50
72
  const turnTools = filterToolsForRuntime(ALL_TOOLS, runtimeContext);
51
- const systemPrompt = await buildBaseSystemPrompt(opts.cwd, turnTools);
73
+ const systemPrompt = await buildBaseSystemPrompt(effectiveCwd, turnTools);
52
74
  const transientBundle = await buildTransientPromptBundle({
53
- cwd: opts.cwd,
75
+ cwd: effectiveCwd,
54
76
  tools: turnTools,
55
77
  runtime: {
56
78
  surface: 'headless',
57
- cwd: opts.cwd,
79
+ cwd: effectiveCwd,
58
80
  workspaceId: workspace?.id,
81
+ sessionId: opts.sessionId,
59
82
  interactiveAsk: false,
60
83
  memoryAccess: runtimeContext.memoryAccess,
61
84
  currentUserInput: opts.input,
62
85
  },
63
86
  }).catch(() => undefined);
64
- await updateSessionMetadata(opts.cwd, opts.sessionId, { lastMessagePreview: opts.input }).catch(() => { });
87
+ await updateSessionMetadata(opts.cwd, opts.sessionId, {
88
+ lastMessagePreview: opts.input,
89
+ }).catch(() => { });
65
90
  for await (const ev of run({
66
91
  userInput: opts.input,
67
92
  provider,
@@ -69,12 +94,7 @@ deps = {}) {
69
94
  systemPrompt,
70
95
  ...(transientBundle ? { transientBundle } : {}),
71
96
  promptCache: createPromptCacheContext(`workspace:${workspace?.id ?? opts.cwd}:session:${opts.sessionId}`),
72
- sessionState: opts.sessionState ?? getOrCreateSessionState(sessionStateKey({
73
- surface: runtimeContext.surface,
74
- workspaceId: runtimeContext.workspaceId,
75
- cwd: opts.cwd,
76
- sessionId: opts.sessionId,
77
- })),
97
+ sessionState: turnSessionState,
78
98
  signal: opts.signal,
79
99
  maxTurns: opts.maxTurns,
80
100
  runtimeContext,
@@ -103,5 +123,7 @@ deps = {}) {
103
123
  };
104
124
  }
105
125
  function isEventLike(ev) {
106
- return Boolean(ev && typeof ev === 'object' && typeof ev.type === 'string');
126
+ return Boolean(ev &&
127
+ typeof ev === 'object' &&
128
+ typeof ev.type === 'string');
107
129
  }