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.
- package/package.json +5 -1
- package/plugins/workflow-runner/plugin.js +27 -15
- package/plugins/workflow-runner/plugin.ts +34 -20
- package/plugins/workflow-runner/src/agentCommon.js +13 -12
- package/plugins/workflow-runner/src/agentCommon.ts +22 -13
- package/plugins/workflow-runner/src/expressions.js +29 -8
- package/plugins/workflow-runner/src/expressions.ts +28 -10
- package/plugins/workflow-runner/src/index.js +50 -20
- package/plugins/workflow-runner/src/index.ts +75 -31
- package/plugins/workflow-runner/src/multimodal.js +2 -1
- package/plugins/workflow-runner/src/multimodal.ts +2 -1
- package/plugins/workflow-runner/src/recovery.js +9 -2
- package/plugins/workflow-runner/src/recovery.ts +9 -2
- package/plugins/workflow-runner/src/runner.js +31 -5
- package/plugins/workflow-runner/src/runner.ts +33 -5
- package/plugins/workflow-runner/src/stepExecutors/assert.js +2 -1
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +3 -1
- package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +12 -7
- package/plugins/workflow-runner/src/stepExecutors/tool.js +91 -61
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +111 -71
- package/plugins/workflow-runner/src/stepExecutors/verify.js +6 -3
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +7 -3
- package/plugins/workflow-runner/src/taskIdentity.js +197 -21
- package/plugins/workflow-runner/src/taskIdentity.ts +201 -20
- package/plugins/workflow-runner/src/toolRuntime.js +48 -0
- package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
- package/plugins/workflow-runner/src/types.js +113 -7
- package/plugins/workflow-runner/src/types.ts +137 -14
- package/plugins/workflow-runner/src/workflowState.js +455 -54
- package/plugins/workflow-runner/src/workflowState.ts +508 -63
- package/plugins/workflow-runner/test/cli.e2e.test.ts +20 -0
- package/plugins/workflow-runner/test/context-files.test.ts +47 -16
- package/plugins/workflow-runner/test/e2eNewLayout.test.ts +314 -0
- package/plugins/workflow-runner/test/engineRobustness.test.ts +9 -1
- package/plugins/workflow-runner/test/expressions.test.ts +42 -0
- package/plugins/workflow-runner/test/hostCapability.test.ts +9 -3
- package/plugins/workflow-runner/test/illustrate.load.test.ts +2 -2
- package/plugins/workflow-runner/test/multimodal.test.ts +61 -10
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +19 -0
- package/plugins/workflow-runner/test/ppt.load.test.ts +15 -15
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +3 -2
- package/plugins/workflow-runner/test/research.load.test.ts +328 -0
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +68 -11
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +60 -12
- package/plugins/workflow-runner/test/taskArbitration.test.ts +19 -3
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +61 -7
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
- package/plugins/workflow-runner/test/webppt.load.test.ts +4 -4
- package/plugins/workflow-runner/test/word.load.test.ts +9 -9
- package/skills/skill-creator/scripts/run_eval.py +3 -2
- package/src/cli/print.js +36 -4
- package/src/cli/print.ts +42 -4
- package/src/cli/streamJson.js +19 -4
- package/src/cli/streamJson.ts +17 -4
- package/src/config/configDefaults.js +25 -0
- package/src/config/configDefaults.ts +33 -0
- package/src/config/configFile.js +2 -21
- package/src/config/configFile.ts +11 -24
- package/src/config/configManagement.js +1 -1
- package/src/config/configManagement.ts +2 -2
- package/src/config/configService.js +24 -0
- package/src/config/configService.ts +23 -0
- package/src/context/compact.js +4 -0
- package/src/context/compact.ts +4 -0
- package/src/context/contextBundle.js +38 -13
- package/src/context/contextBundle.ts +47 -13
- package/src/harness/stallGuard.js +19 -4
- package/src/harness/stallGuard.ts +30 -7
- package/src/infra/verification.js +25 -18
- package/src/infra/verification.ts +50 -18
- package/src/loop.js +95 -11
- package/src/loop.ts +125 -16
- package/src/main.js +8 -1
- package/src/main.tsx +7 -1
- package/src/memory/constants.js +10 -0
- package/src/memory/constants.ts +13 -0
- package/src/memory/episodicMemory.js +274 -2
- package/src/memory/episodicMemory.ts +306 -1
- package/src/memory/fileStore.js +24 -4
- package/src/memory/fileStore.ts +22 -2
- package/src/memory/memoryService.js +79 -12
- package/src/memory/memoryService.ts +108 -9
- package/src/memory/repository.js +415 -85
- package/src/memory/repository.ts +437 -86
- package/src/memory/turnJournal.js +95 -0
- package/src/memory/turnJournal.ts +120 -0
- package/src/memory/types.ts +16 -0
- package/src/plugins/pluginApi.ts +30 -6
- package/src/plugins/pluginRunner.js +23 -8
- package/src/plugins/pluginRunner.ts +32 -13
- package/src/prompts/policyRegistry.js +55 -1
- package/src/prompts/policyRegistry.ts +55 -1
- package/src/prompts/promptAssembly.js +39 -7
- package/src/prompts/promptAssembly.ts +145 -59
- package/src/session/runAgentTurn.js +43 -21
- package/src/session/runAgentTurn.ts +68 -26
- package/src/session/runTurn.js +11 -46
- package/src/session/runTurn.ts +12 -46
- package/src/session/runtimeStore.js +149 -0
- package/src/session/runtimeStore.ts +197 -0
- package/src/session/sessionState.js +5 -0
- package/src/session/sessionState.ts +14 -0
- package/src/tools/advisor/advisor.js +76 -9
- package/src/tools/advisor/advisor.ts +79 -9
- package/src/tools/agent/agent.js +143 -14
- package/src/tools/agent/agent.ts +149 -14
- package/src/tools/agent/agentTaskService.js +60 -0
- package/src/tools/agent/agentTaskService.ts +113 -0
- package/src/tools/agent/agentTypes.js +5 -0
- package/src/tools/agent/agentTypes.ts +5 -0
- package/src/tools/bash/bash.js +80 -15
- package/src/tools/bash/bash.ts +83 -15
- package/src/tools/capabilities.js +5 -0
- package/src/tools/capabilities.ts +11 -1
- package/src/tools/edit/edit.js +7 -4
- package/src/tools/edit/edit.ts +7 -4
- package/src/tools/edit/multi-edit.js +4 -2
- package/src/tools/edit/multi-edit.ts +4 -2
- package/src/tools/glob/glob.js +156 -40
- package/src/tools/glob/glob.ts +185 -46
- package/src/tools/index.js +35 -17
- package/src/tools/index.ts +81 -20
- package/src/tools/lsp/lsp.js +165 -22
- package/src/tools/lsp/lsp.ts +206 -36
- package/src/tools/powershell/powershell.js +134 -27
- package/src/tools/powershell/powershell.ts +171 -30
- package/src/tools/presentresult/presentresult.js +10 -0
- package/src/tools/presentresult/presentresult.ts +10 -0
- package/src/tools/read/read.js +249 -29
- package/src/tools/read/read.ts +396 -59
- package/src/tools/shared/diffSummary.js +44 -0
- package/src/tools/shared/diffSummary.ts +63 -0
- package/src/tools/shared/fileActivity.js +2 -1
- package/src/tools/shared/fileActivity.ts +3 -0
- package/src/tools/shared/permanentDeleteGuard.js +57 -0
- package/src/tools/shared/permanentDeleteGuard.ts +71 -0
- package/src/tools/shared/toolOutputStore.js +97 -0
- package/src/tools/shared/toolOutputStore.ts +110 -0
- package/src/tools/skill/skill.js +86 -0
- package/src/tools/skill/skill.ts +94 -0
- package/src/tools/snip/snip.js +67 -0
- package/src/tools/snip/snip.ts +80 -0
- package/src/tools/tasks/backgroundTaskService.js +79 -28
- package/src/tools/tasks/backgroundTaskService.ts +165 -49
- package/src/tools/tasks/taskManagement.js +163 -0
- package/src/tools/tasks/taskManagement.ts +180 -0
- package/src/tools/tasks/taskOutput.js +64 -14
- package/src/tools/tasks/taskOutput.ts +102 -23
- package/src/tools/tasks/taskStop.js +48 -8
- package/src/tools/tasks/taskStop.ts +69 -9
- package/src/tools/tasks/taskStore.js +46 -0
- package/src/tools/tasks/taskStore.ts +77 -0
- package/src/tools/terminal/terminalCapture.js +167 -0
- package/src/tools/terminal/terminalCapture.ts +179 -0
- package/src/tools/trash/trash.js +233 -0
- package/src/tools/trash/trash.ts +283 -0
- package/src/tools/webbrowser/browserControl.js +5 -1
- package/src/tools/webbrowser/browserControl.ts +7 -1
- package/src/tools/webbrowser/webbrowser.js +8 -4
- package/src/tools/webbrowser/webbrowser.ts +8 -4
- package/src/tools/worktree/sessionWorktree.js +128 -0
- package/src/tools/worktree/sessionWorktree.ts +158 -0
- package/src/tools/write/write.js +6 -2
- package/src/tools/write/write.ts +6 -2
- package/src/types.ts +22 -4
- package/src/ui/hooks/useChat.js +31 -11
- package/src/ui/hooks/useChat.ts +31 -13
- package/src/web/protocol.ts +78 -3
- package/workflows/adaptive-research.yaml +5 -5
- package/workflows/book-review-short.yaml +5 -5
- package/workflows/e2e-write-greet.yaml +2 -2
- package/workflows/illustrate.yaml +12 -12
- package/workflows/plan.yaml +18 -18
- package/workflows/ppt.yaml +201 -201
- package/workflows/ralph-loop.yaml +3 -1
- package/workflows/research.md +449 -0
- package/workflows/research.yaml +873 -0
- package/workflows/webppt.yaml +47 -47
- package/workflows/word.yaml +80 -80
- 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
|
-
|
|
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
|
-
|
|
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)
|
|
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 = {
|
|
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 =
|
|
132
|
-
|
|
133
|
-
|
|
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(
|
|
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<
|
|
174
|
+
): Promise<Awaited<
|
|
175
|
+
ReturnType<typeof memoryService.buildContextBundle>
|
|
176
|
+
> | null> {
|
|
166
177
|
const names = new Set(tools.map((tool) => tool.name));
|
|
167
|
-
const memoryAllowed =
|
|
178
|
+
const memoryAllowed =
|
|
179
|
+
(runtime.memoryAccess !== undefined && runtime.memoryAccess !== 'denied') ||
|
|
168
180
|
// Compatibility is explicit-only: omitted ownerLocalSession never grants access.
|
|
169
|
-
|
|
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(
|
|
184
|
-
|
|
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 = (
|
|
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<
|
|
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
|
-
|
|
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 [
|
|
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)
|
|
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 = {
|
|
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(
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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)
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
21
|
+
sessionModelId =
|
|
22
|
+
(await configService.getSnapshot()).config?.baseModelId ?? undefined;
|
|
21
23
|
if (sessionModelId) {
|
|
22
|
-
await updateSessionMetadata(opts.cwd, opts.sessionId, {
|
|
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
|
|
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
|
-
|
|
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:
|
|
63
|
+
cwd: effectiveCwd,
|
|
44
64
|
workspaceId: workspace?.id,
|
|
45
65
|
sessionId: opts.sessionId,
|
|
46
|
-
owner: opts.memoryAccess && opts.memoryAccess !== 'denied'
|
|
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(
|
|
73
|
+
const systemPrompt = await buildBaseSystemPrompt(effectiveCwd, turnTools);
|
|
52
74
|
const transientBundle = await buildTransientPromptBundle({
|
|
53
|
-
cwd:
|
|
75
|
+
cwd: effectiveCwd,
|
|
54
76
|
tools: turnTools,
|
|
55
77
|
runtime: {
|
|
56
78
|
surface: 'headless',
|
|
57
|
-
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, {
|
|
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:
|
|
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 &&
|
|
126
|
+
return Boolean(ev &&
|
|
127
|
+
typeof ev === 'object' &&
|
|
128
|
+
typeof ev.type === 'string');
|
|
107
129
|
}
|