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
|
@@ -20,14 +20,15 @@ from scripts.utils import parse_skill_md
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def find_project_root() -> Path:
|
|
23
|
-
"""Find the project root by walking up from cwd looking for .
|
|
23
|
+
"""Find the project root by walking up from cwd looking for .kakadeai/
|
|
24
|
+
(this repo's project-config dir) or .claude/ (upstream convention).
|
|
24
25
|
|
|
25
26
|
Mimics how Claude Code discovers its project root, so the command file
|
|
26
27
|
we create ends up where claude -p will look for it.
|
|
27
28
|
"""
|
|
28
29
|
current = Path.cwd()
|
|
29
30
|
for parent in [current, *current.parents]:
|
|
30
|
-
if (parent / ".claude").is_dir():
|
|
31
|
+
if (parent / ".kakadeai").is_dir() or (parent / ".claude").is_dir():
|
|
31
32
|
return parent
|
|
32
33
|
return current
|
|
33
34
|
|
package/src/cli/print.js
CHANGED
|
@@ -27,7 +27,9 @@ import { updateSessionMetadata } from '../context/sessionRegistry.js';
|
|
|
27
27
|
import { workspaceIndexService } from '../context/workspaceIndex.js';
|
|
28
28
|
import { normalizeImageDetail } from '../multimodal.js';
|
|
29
29
|
import { runWithPlugins } from '../plugins/pluginRunner.js';
|
|
30
|
-
import { buildTransientPromptBundle } from '../prompts/system.js';
|
|
30
|
+
import { buildBaseSystemPrompt, buildTransientPromptBundle } from '../prompts/system.js';
|
|
31
|
+
import { getOrCreateSessionState, sessionStateKey } from '../session/sessionState.js';
|
|
32
|
+
import { hydrateSessionWorktree } from '../tools/worktree/sessionWorktree.js';
|
|
31
33
|
import { ALL_TOOLS } from '../tools/index.js';
|
|
32
34
|
import { createPromptCacheContext } from '../llm/promptCache.js';
|
|
33
35
|
import { createAgentRuntimeContext, filterToolsForRuntime, memoryContextFromRuntime, } from '../runtime/agentRuntimeContext.js';
|
|
@@ -106,8 +108,16 @@ export function extractImageOptions(args) {
|
|
|
106
108
|
* stop_reason = end_turn | max_turns | stalled | error | interrupted | config_error
|
|
107
109
|
* num_turns = assistant_message 事件计数
|
|
108
110
|
* error = 失败文案(成功时 null)
|
|
111
|
+
* warnings = 本轮所有非致命警告(与 stderr 同源;宿主要结构化的那一份)
|
|
109
112
|
* session_file(可选)= 当前对话落盘路径;拿得到才填,拿不到省略该键
|
|
110
113
|
*
|
|
114
|
+
* completionStatus = 本层从 stop_reason + deliverables 重算:跑完了没、产物验没验
|
|
115
|
+
* delivery_status(可选)= 内核 turn_done 的交付判定,**回答的是另一个问题**:
|
|
116
|
+
* completed / delivery_incomplete / delivery_unverified / claims_unverified。
|
|
117
|
+
* 两者并列而非替代 —— `ok:true` 配 `delivery_status:'claims_unverified'` 是
|
|
118
|
+
* 完全合法的一行:Agent 确实正常收尾了,但它宣称的「测试已通过」没有证据支撑。
|
|
119
|
+
* 判断「任务是否真做成」的宿主应当读它,而不是只看 ok。
|
|
120
|
+
*
|
|
111
121
|
* 导出供单测断言。
|
|
112
122
|
*/
|
|
113
123
|
export function emitJsonResult(result, sessionFile) {
|
|
@@ -134,6 +144,7 @@ export function emitJsonResult(result, sessionFile) {
|
|
|
134
144
|
num_turns: result.numTurns,
|
|
135
145
|
error: result.error,
|
|
136
146
|
completionStatus,
|
|
147
|
+
...(result.deliveryStatus ? { delivery_status: result.deliveryStatus } : {}),
|
|
137
148
|
deliverables,
|
|
138
149
|
verification: allVerified ? 'passed' : deliverables.length > 0 ? 'not_run' : 'none',
|
|
139
150
|
warnings,
|
|
@@ -232,10 +243,16 @@ export async function runPrintMode(provider, args, initialHistory, options) {
|
|
|
232
243
|
void finalizeOnce().finally(() => process.exit(130));
|
|
233
244
|
});
|
|
234
245
|
try {
|
|
235
|
-
const
|
|
236
|
-
const workspace = await workspaceIndexService.findByPath(
|
|
246
|
+
const workspaceRoot = options.workspace?.cwd ?? process.cwd();
|
|
247
|
+
const workspace = await workspaceIndexService.findByPath(workspaceRoot).catch(() => null);
|
|
237
248
|
const sessionId = options.workspace?.sessionId ?? 'default';
|
|
238
|
-
const
|
|
249
|
+
const sessionState = getOrCreateSessionState(sessionStateKey({
|
|
250
|
+
surface: 'cli', workspaceId: workspace?.id,
|
|
251
|
+
cwd: workspace?.id ? undefined : workspaceRoot, sessionId,
|
|
252
|
+
}));
|
|
253
|
+
await hydrateSessionWorktree({ sessionState, workspaceId: workspace?.id, sessionId, cwd: workspaceRoot });
|
|
254
|
+
const cwd = sessionState.activeWorktree?.path ?? workspaceRoot;
|
|
255
|
+
const conversationKey = `workspace:${workspace?.id ?? workspaceRoot}:session:${sessionId}`;
|
|
239
256
|
const computerRun = options.computerUseMode && options.computerUseMode !== 'disabled'
|
|
240
257
|
? createCliComputerRun({
|
|
241
258
|
mode: options.computerUseMode,
|
|
@@ -259,6 +276,11 @@ export async function runPrintMode(provider, args, initialHistory, options) {
|
|
|
259
276
|
computer: computerRun,
|
|
260
277
|
});
|
|
261
278
|
const turnTools = filterToolsForRuntime(ALL_TOOLS, runtimeContext);
|
|
279
|
+
const stablePrompt = await buildBaseSystemPrompt(cwd, turnTools);
|
|
280
|
+
if (history[0]?.role === 'system')
|
|
281
|
+
history[0] = { ...history[0], content: stablePrompt };
|
|
282
|
+
else
|
|
283
|
+
history.unshift({ role: 'system', content: stablePrompt });
|
|
262
284
|
const transientBundle = await buildTransientPromptBundle({
|
|
263
285
|
cwd,
|
|
264
286
|
tools: turnTools,
|
|
@@ -266,6 +288,7 @@ export async function runPrintMode(provider, args, initialHistory, options) {
|
|
|
266
288
|
surface: 'cli',
|
|
267
289
|
cwd,
|
|
268
290
|
workspaceId: workspace?.id,
|
|
291
|
+
sessionId,
|
|
269
292
|
interactiveAsk: false,
|
|
270
293
|
memoryAccess: runtimeContext.memoryAccess,
|
|
271
294
|
currentUserInput: prompt,
|
|
@@ -282,6 +305,7 @@ export async function runPrintMode(provider, args, initialHistory, options) {
|
|
|
282
305
|
imageMaxLongSidePixel: imageOptions.imageMaxLongSidePixel,
|
|
283
306
|
allowedTools: turnTools.map((tool) => tool.name),
|
|
284
307
|
runtimeContext,
|
|
308
|
+
sessionState,
|
|
285
309
|
...(transientBundle ? { transientBundle } : {}),
|
|
286
310
|
promptCache: createPromptCacheContext(`workspace:${workspace?.id ?? cwd}:session:${sessionId}`),
|
|
287
311
|
memoryContext: memoryContextFromRuntime(runtimeContext),
|
|
@@ -384,11 +408,19 @@ export function handleEvent(event, result, verbose, outputFormat = 'text') {
|
|
|
384
408
|
// 通用非致命警告(如 workflow 多余参数、pause 被自动跳过等)始终走 stderr,
|
|
385
409
|
// 不受 verbose 控制 —— 这是用户必须看到的信号,避免静默丢弃语义被忽略。
|
|
386
410
|
process.stderr.write(`${event.message}\n`);
|
|
411
|
+
// 同时收进结局契约:stderr 是给人看的,spawn 它的宿主要的是结构化的一条。
|
|
412
|
+
// (此前 PrintResult.warnings 声明了、读了、也输出了,就是没有任何地方写它 ——
|
|
413
|
+
// json 那行的 warnings 恒为空数组。)
|
|
414
|
+
(result.warnings ??= []).push(event.message);
|
|
387
415
|
return {};
|
|
388
416
|
case 'turn_done':
|
|
389
417
|
// text 模式:非 verbose 时这里一次性把累积答案打到 stdout(旧行为,逐字节不变)。
|
|
390
418
|
// json 模式:只记 stopReason='end_turn',stdout 保持静默。
|
|
391
419
|
result.stopReason = 'end_turn';
|
|
420
|
+
// 内核的交付判定必须过进程边界:模型谎称「已验证」时,若不记这一笔,
|
|
421
|
+
// 宿主收到的 JSON 与一切正常逐字节相同,唯一线索只剩 stderr 上一行中文。
|
|
422
|
+
if (event.completionStatus)
|
|
423
|
+
result.deliveryStatus = event.completionStatus;
|
|
392
424
|
if (!isJson && !verbose)
|
|
393
425
|
console.log(result.buffer);
|
|
394
426
|
return {};
|
package/src/cli/print.ts
CHANGED
|
@@ -29,7 +29,9 @@ import { workspaceIndexService } from '../context/workspaceIndex.js';
|
|
|
29
29
|
import { normalizeImageDetail } from '../multimodal.js';
|
|
30
30
|
import type { DeliverableManifest, FileActivityMetadata, HarnessDiagnostics, ImageInputSpec, LoopEvent, Message, Provider } from '../types.js';
|
|
31
31
|
import { runWithPlugins } from '../plugins/pluginRunner.js';
|
|
32
|
-
import { buildTransientPromptBundle } from '../prompts/system.js';
|
|
32
|
+
import { buildBaseSystemPrompt, buildTransientPromptBundle } from '../prompts/system.js';
|
|
33
|
+
import { getOrCreateSessionState, sessionStateKey } from '../session/sessionState.js';
|
|
34
|
+
import { hydrateSessionWorktree } from '../tools/worktree/sessionWorktree.js';
|
|
33
35
|
import { ALL_TOOLS } from '../tools/index.js';
|
|
34
36
|
import { createPromptCacheContext } from '../llm/promptCache.js';
|
|
35
37
|
import {
|
|
@@ -96,6 +98,15 @@ export interface PrintResult {
|
|
|
96
98
|
deliverables?: DeliverableManifest['files'];
|
|
97
99
|
warnings?: string[];
|
|
98
100
|
harnessDiagnostics?: HarnessDiagnostics;
|
|
101
|
+
/**
|
|
102
|
+
* 内核 `turn_done` 给出的交付判定(loop.ts 的四态词表)。
|
|
103
|
+
*
|
|
104
|
+
* 与下面 json 里那个从 stopReason+deliverables 重算的 `completionStatus` **不是**
|
|
105
|
+
* 一回事:那个只回答「跑完了没、产物验没验」,这个回答「模型宣称的事到底做没做」。
|
|
106
|
+
* 尤其 `claims_unverified` —— 模型说「测试已全部通过」但本轮证据台账里没有一条
|
|
107
|
+
* 成功的验证记录 —— 是重不重要全看它能不能过进程边界。
|
|
108
|
+
*/
|
|
109
|
+
deliveryStatus?: string;
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
/** handleEvent 返回的结构 */
|
|
@@ -189,8 +200,16 @@ export function extractImageOptions(args: string[]): {
|
|
|
189
200
|
* stop_reason = end_turn | max_turns | stalled | error | interrupted | config_error
|
|
190
201
|
* num_turns = assistant_message 事件计数
|
|
191
202
|
* error = 失败文案(成功时 null)
|
|
203
|
+
* warnings = 本轮所有非致命警告(与 stderr 同源;宿主要结构化的那一份)
|
|
192
204
|
* session_file(可选)= 当前对话落盘路径;拿得到才填,拿不到省略该键
|
|
193
205
|
*
|
|
206
|
+
* completionStatus = 本层从 stop_reason + deliverables 重算:跑完了没、产物验没验
|
|
207
|
+
* delivery_status(可选)= 内核 turn_done 的交付判定,**回答的是另一个问题**:
|
|
208
|
+
* completed / delivery_incomplete / delivery_unverified / claims_unverified。
|
|
209
|
+
* 两者并列而非替代 —— `ok:true` 配 `delivery_status:'claims_unverified'` 是
|
|
210
|
+
* 完全合法的一行:Agent 确实正常收尾了,但它宣称的「测试已通过」没有证据支撑。
|
|
211
|
+
* 判断「任务是否真做成」的宿主应当读它,而不是只看 ok。
|
|
212
|
+
*
|
|
194
213
|
* 导出供单测断言。
|
|
195
214
|
*/
|
|
196
215
|
export function emitJsonResult(result: PrintResult, sessionFile?: string): void {
|
|
@@ -218,6 +237,7 @@ export function emitJsonResult(result: PrintResult, sessionFile?: string): void
|
|
|
218
237
|
num_turns: result.numTurns,
|
|
219
238
|
error: result.error,
|
|
220
239
|
completionStatus,
|
|
240
|
+
...(result.deliveryStatus ? { delivery_status: result.deliveryStatus } : {}),
|
|
221
241
|
deliverables,
|
|
222
242
|
verification: allVerified ? 'passed' : deliverables.length > 0 ? 'not_run' : 'none',
|
|
223
243
|
warnings,
|
|
@@ -325,10 +345,16 @@ export async function runPrintMode(
|
|
|
325
345
|
});
|
|
326
346
|
|
|
327
347
|
try {
|
|
328
|
-
const
|
|
329
|
-
const workspace = await workspaceIndexService.findByPath(
|
|
348
|
+
const workspaceRoot = options.workspace?.cwd ?? process.cwd();
|
|
349
|
+
const workspace = await workspaceIndexService.findByPath(workspaceRoot).catch(() => null);
|
|
330
350
|
const sessionId = options.workspace?.sessionId ?? 'default';
|
|
331
|
-
const
|
|
351
|
+
const sessionState = getOrCreateSessionState(sessionStateKey({
|
|
352
|
+
surface: 'cli', workspaceId: workspace?.id,
|
|
353
|
+
cwd: workspace?.id ? undefined : workspaceRoot, sessionId,
|
|
354
|
+
}));
|
|
355
|
+
await hydrateSessionWorktree({ sessionState, workspaceId: workspace?.id, sessionId, cwd: workspaceRoot });
|
|
356
|
+
const cwd = sessionState.activeWorktree?.path ?? workspaceRoot;
|
|
357
|
+
const conversationKey = `workspace:${workspace?.id ?? workspaceRoot}:session:${sessionId}`;
|
|
332
358
|
const computerRun = options.computerUseMode && options.computerUseMode !== 'disabled'
|
|
333
359
|
? createCliComputerRun({
|
|
334
360
|
mode: options.computerUseMode,
|
|
@@ -352,6 +378,9 @@ export async function runPrintMode(
|
|
|
352
378
|
computer: computerRun,
|
|
353
379
|
});
|
|
354
380
|
const turnTools = filterToolsForRuntime(ALL_TOOLS, runtimeContext);
|
|
381
|
+
const stablePrompt = await buildBaseSystemPrompt(cwd, turnTools);
|
|
382
|
+
if (history[0]?.role === 'system') history[0] = { ...history[0], content: stablePrompt };
|
|
383
|
+
else history.unshift({ role: 'system', content: stablePrompt });
|
|
355
384
|
const transientBundle = await buildTransientPromptBundle({
|
|
356
385
|
cwd,
|
|
357
386
|
tools: turnTools,
|
|
@@ -359,6 +388,7 @@ export async function runPrintMode(
|
|
|
359
388
|
surface: 'cli',
|
|
360
389
|
cwd,
|
|
361
390
|
workspaceId: workspace?.id,
|
|
391
|
+
sessionId,
|
|
362
392
|
interactiveAsk: false,
|
|
363
393
|
memoryAccess: runtimeContext.memoryAccess,
|
|
364
394
|
currentUserInput: prompt,
|
|
@@ -375,6 +405,7 @@ export async function runPrintMode(
|
|
|
375
405
|
imageMaxLongSidePixel: imageOptions.imageMaxLongSidePixel,
|
|
376
406
|
allowedTools: turnTools.map((tool) => tool.name),
|
|
377
407
|
runtimeContext,
|
|
408
|
+
sessionState,
|
|
378
409
|
...(transientBundle ? { transientBundle } : {}),
|
|
379
410
|
promptCache: createPromptCacheContext(
|
|
380
411
|
`workspace:${workspace?.id ?? cwd}:session:${sessionId}`,
|
|
@@ -496,12 +527,19 @@ export function handleEvent(
|
|
|
496
527
|
// 通用非致命警告(如 workflow 多余参数、pause 被自动跳过等)始终走 stderr,
|
|
497
528
|
// 不受 verbose 控制 —— 这是用户必须看到的信号,避免静默丢弃语义被忽略。
|
|
498
529
|
process.stderr.write(`${event.message}\n`);
|
|
530
|
+
// 同时收进结局契约:stderr 是给人看的,spawn 它的宿主要的是结构化的一条。
|
|
531
|
+
// (此前 PrintResult.warnings 声明了、读了、也输出了,就是没有任何地方写它 ——
|
|
532
|
+
// json 那行的 warnings 恒为空数组。)
|
|
533
|
+
(result.warnings ??= []).push(event.message);
|
|
499
534
|
return {};
|
|
500
535
|
|
|
501
536
|
case 'turn_done':
|
|
502
537
|
// text 模式:非 verbose 时这里一次性把累积答案打到 stdout(旧行为,逐字节不变)。
|
|
503
538
|
// json 模式:只记 stopReason='end_turn',stdout 保持静默。
|
|
504
539
|
result.stopReason = 'end_turn';
|
|
540
|
+
// 内核的交付判定必须过进程边界:模型谎称「已验证」时,若不记这一笔,
|
|
541
|
+
// 宿主收到的 JSON 与一切正常逐字节相同,唯一线索只剩 stderr 上一行中文。
|
|
542
|
+
if (event.completionStatus) result.deliveryStatus = event.completionStatus;
|
|
505
543
|
if (!isJson && !verbose) console.log(result.buffer);
|
|
506
544
|
return {};
|
|
507
545
|
|
package/src/cli/streamJson.js
CHANGED
|
@@ -28,7 +28,9 @@ import { saveContext } from '../context/persistContext.js';
|
|
|
28
28
|
import { setAskHandler } from '../askBridge.js';
|
|
29
29
|
import { normalizeImageDetail } from '../multimodal.js';
|
|
30
30
|
import { runWithPlugins } from '../plugins/pluginRunner.js';
|
|
31
|
-
import { buildTransientPromptBundle } from '../prompts/system.js';
|
|
31
|
+
import { buildBaseSystemPrompt, buildTransientPromptBundle } from '../prompts/system.js';
|
|
32
|
+
import { getOrCreateSessionState, sessionStateKey } from '../session/sessionState.js';
|
|
33
|
+
import { hydrateSessionWorktree } from '../tools/worktree/sessionWorktree.js';
|
|
32
34
|
import { ALL_TOOLS } from '../tools/index.js';
|
|
33
35
|
import { createPromptCacheContext } from '../llm/promptCache.js';
|
|
34
36
|
import { createAgentRuntimeContext, filterToolsForRuntime, memoryContextFromRuntime, } from '../runtime/agentRuntimeContext.js';
|
|
@@ -267,10 +269,16 @@ export async function runStreamJsonMode(provider, args, initialHistory, options)
|
|
|
267
269
|
void finalize().finally(() => process.exit(130));
|
|
268
270
|
});
|
|
269
271
|
try {
|
|
270
|
-
const
|
|
271
|
-
const workspace = await workspaceIndexService.findByPath(
|
|
272
|
+
const workspaceRoot = options.workspace?.cwd ?? getWorkingDir();
|
|
273
|
+
const workspace = await workspaceIndexService.findByPath(workspaceRoot).catch(() => null);
|
|
272
274
|
const sessionId = options.workspace?.sessionId ?? 'default';
|
|
273
|
-
const
|
|
275
|
+
const sessionState = getOrCreateSessionState(sessionStateKey({
|
|
276
|
+
surface: 'headless', workspaceId: workspace?.id,
|
|
277
|
+
cwd: workspace?.id ? undefined : workspaceRoot, sessionId,
|
|
278
|
+
}));
|
|
279
|
+
await hydrateSessionWorktree({ sessionState, workspaceId: workspace?.id, sessionId, cwd: workspaceRoot });
|
|
280
|
+
const cwd = sessionState.activeWorktree?.path ?? workspaceRoot;
|
|
281
|
+
const conversationKey = `workspace:${workspace?.id ?? workspaceRoot}:session:${sessionId}`;
|
|
274
282
|
const computerRun = options.computerUseMode && options.computerUseMode !== 'disabled'
|
|
275
283
|
? createCliComputerRun({
|
|
276
284
|
mode: options.computerUseMode,
|
|
@@ -294,6 +302,11 @@ export async function runStreamJsonMode(provider, args, initialHistory, options)
|
|
|
294
302
|
computer: computerRun,
|
|
295
303
|
});
|
|
296
304
|
const turnTools = filterToolsForRuntime(ALL_TOOLS, runtimeContext);
|
|
305
|
+
const stablePrompt = await buildBaseSystemPrompt(cwd, turnTools);
|
|
306
|
+
if (history[0]?.role === 'system')
|
|
307
|
+
history[0] = { ...history[0], content: stablePrompt };
|
|
308
|
+
else
|
|
309
|
+
history.unshift({ role: 'system', content: stablePrompt });
|
|
297
310
|
const transientBundle = await buildTransientPromptBundle({
|
|
298
311
|
cwd,
|
|
299
312
|
tools: turnTools,
|
|
@@ -301,6 +314,7 @@ export async function runStreamJsonMode(provider, args, initialHistory, options)
|
|
|
301
314
|
surface: 'headless',
|
|
302
315
|
cwd,
|
|
303
316
|
workspaceId: workspace?.id,
|
|
317
|
+
sessionId,
|
|
304
318
|
interactiveAsk: control,
|
|
305
319
|
memoryAccess: runtimeContext.memoryAccess,
|
|
306
320
|
currentUserInput: prompt,
|
|
@@ -316,6 +330,7 @@ export async function runStreamJsonMode(provider, args, initialHistory, options)
|
|
|
316
330
|
imageDetail: cliImageOptions.imageDetail,
|
|
317
331
|
imageMaxLongSidePixel: cliImageOptions.imageMaxLongSidePixel,
|
|
318
332
|
runtimeContext,
|
|
333
|
+
sessionState,
|
|
319
334
|
allowedTools: turnTools.map((tool) => tool.name),
|
|
320
335
|
...(transientBundle ? { transientBundle } : {}),
|
|
321
336
|
promptCache: createPromptCacheContext(`workspace:${workspace?.id ?? cwd}:session:${sessionId}`),
|
package/src/cli/streamJson.ts
CHANGED
|
@@ -33,7 +33,9 @@ export type { AskAnswer } from '../askBridge.js';
|
|
|
33
33
|
import { normalizeImageDetail } from '../multimodal.js';
|
|
34
34
|
import type { ImageInputSpec, Message, Provider } from '../types.js';
|
|
35
35
|
import { runWithPlugins } from '../plugins/pluginRunner.js';
|
|
36
|
-
import { buildTransientPromptBundle } from '../prompts/system.js';
|
|
36
|
+
import { buildBaseSystemPrompt, buildTransientPromptBundle } from '../prompts/system.js';
|
|
37
|
+
import { getOrCreateSessionState, sessionStateKey } from '../session/sessionState.js';
|
|
38
|
+
import { hydrateSessionWorktree } from '../tools/worktree/sessionWorktree.js';
|
|
37
39
|
import { ALL_TOOLS } from '../tools/index.js';
|
|
38
40
|
import { createPromptCacheContext } from '../llm/promptCache.js';
|
|
39
41
|
import {
|
|
@@ -344,10 +346,16 @@ export async function runStreamJsonMode(
|
|
|
344
346
|
});
|
|
345
347
|
|
|
346
348
|
try {
|
|
347
|
-
const
|
|
348
|
-
const workspace = await workspaceIndexService.findByPath(
|
|
349
|
+
const workspaceRoot = options.workspace?.cwd ?? getWorkingDir();
|
|
350
|
+
const workspace = await workspaceIndexService.findByPath(workspaceRoot).catch(() => null);
|
|
349
351
|
const sessionId = options.workspace?.sessionId ?? 'default';
|
|
350
|
-
const
|
|
352
|
+
const sessionState = getOrCreateSessionState(sessionStateKey({
|
|
353
|
+
surface: 'headless', workspaceId: workspace?.id,
|
|
354
|
+
cwd: workspace?.id ? undefined : workspaceRoot, sessionId,
|
|
355
|
+
}));
|
|
356
|
+
await hydrateSessionWorktree({ sessionState, workspaceId: workspace?.id, sessionId, cwd: workspaceRoot });
|
|
357
|
+
const cwd = sessionState.activeWorktree?.path ?? workspaceRoot;
|
|
358
|
+
const conversationKey = `workspace:${workspace?.id ?? workspaceRoot}:session:${sessionId}`;
|
|
351
359
|
const computerRun = options.computerUseMode && options.computerUseMode !== 'disabled'
|
|
352
360
|
? createCliComputerRun({
|
|
353
361
|
mode: options.computerUseMode,
|
|
@@ -371,6 +379,9 @@ export async function runStreamJsonMode(
|
|
|
371
379
|
computer: computerRun,
|
|
372
380
|
});
|
|
373
381
|
const turnTools = filterToolsForRuntime(ALL_TOOLS, runtimeContext);
|
|
382
|
+
const stablePrompt = await buildBaseSystemPrompt(cwd, turnTools);
|
|
383
|
+
if (history[0]?.role === 'system') history[0] = { ...history[0], content: stablePrompt };
|
|
384
|
+
else history.unshift({ role: 'system', content: stablePrompt });
|
|
374
385
|
const transientBundle = await buildTransientPromptBundle({
|
|
375
386
|
cwd,
|
|
376
387
|
tools: turnTools,
|
|
@@ -378,6 +389,7 @@ export async function runStreamJsonMode(
|
|
|
378
389
|
surface: 'headless',
|
|
379
390
|
cwd,
|
|
380
391
|
workspaceId: workspace?.id,
|
|
392
|
+
sessionId,
|
|
381
393
|
interactiveAsk: control,
|
|
382
394
|
memoryAccess: runtimeContext.memoryAccess,
|
|
383
395
|
currentUserInput: prompt,
|
|
@@ -393,6 +405,7 @@ export async function runStreamJsonMode(
|
|
|
393
405
|
imageDetail: cliImageOptions.imageDetail,
|
|
394
406
|
imageMaxLongSidePixel: cliImageOptions.imageMaxLongSidePixel,
|
|
395
407
|
runtimeContext,
|
|
408
|
+
sessionState,
|
|
396
409
|
allowedTools: turnTools.map((tool) => tool.name),
|
|
397
410
|
...(transientBundle ? { transientBundle } : {}),
|
|
398
411
|
promptCache: createPromptCacheContext(
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浏览器与 Node/Bun 都可安全加载的纯配置默认值。
|
|
3
|
+
* 不得在此模块引入 node:*、文件系统或运行时路径逻辑。
|
|
4
|
+
*/
|
|
5
|
+
export const DEFAULT_SUBAGENTS_CONFIG = {
|
|
6
|
+
enabled: true,
|
|
7
|
+
maxCallsPerTurn: 6,
|
|
8
|
+
maxConcurrent: 3,
|
|
9
|
+
defaultWriteIsolation: 'none',
|
|
10
|
+
timeoutMs: 1_800_000,
|
|
11
|
+
exploreMaxTurns: 50,
|
|
12
|
+
generalMaxTurns: 100,
|
|
13
|
+
};
|
|
14
|
+
export const DEFAULT_BROWSER_CONFIG = {
|
|
15
|
+
enabled: true,
|
|
16
|
+
profileId: 'default',
|
|
17
|
+
developerMode: false,
|
|
18
|
+
allowedCdpOrigins: [],
|
|
19
|
+
};
|
|
20
|
+
export const DEFAULT_COMPUTER_USE_CONFIG = {
|
|
21
|
+
enabled: true,
|
|
22
|
+
imagePolicy: 'single-adaptive',
|
|
23
|
+
maxImagePixels: 1_600_000,
|
|
24
|
+
observationFrames: 1,
|
|
25
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BrowserConfig,
|
|
3
|
+
ComputerUseConfig,
|
|
4
|
+
SubagentsConfig,
|
|
5
|
+
} from './configFile.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 浏览器与 Node/Bun 都可安全加载的纯配置默认值。
|
|
9
|
+
* 不得在此模块引入 node:*、文件系统或运行时路径逻辑。
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_SUBAGENTS_CONFIG: SubagentsConfig = {
|
|
12
|
+
enabled: true,
|
|
13
|
+
maxCallsPerTurn: 6,
|
|
14
|
+
maxConcurrent: 3,
|
|
15
|
+
defaultWriteIsolation: 'none',
|
|
16
|
+
timeoutMs: 1_800_000,
|
|
17
|
+
exploreMaxTurns: 50,
|
|
18
|
+
generalMaxTurns: 100,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_BROWSER_CONFIG: BrowserConfig = {
|
|
22
|
+
enabled: true,
|
|
23
|
+
profileId: 'default',
|
|
24
|
+
developerMode: false,
|
|
25
|
+
allowedCdpOrigins: [],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_COMPUTER_USE_CONFIG: ComputerUseConfig = {
|
|
29
|
+
enabled: true,
|
|
30
|
+
imagePolicy: 'single-adaptive',
|
|
31
|
+
maxImagePixels: 1_600_000,
|
|
32
|
+
observationFrames: 1,
|
|
33
|
+
};
|
package/src/config/configFile.js
CHANGED
|
@@ -5,27 +5,8 @@ import { dirname, join } from 'node:path';
|
|
|
5
5
|
import { execSiblingDir } from '../utils/greenRoot.js';
|
|
6
6
|
import { normalizePromptCacheMode } from '../llm/promptCache.js';
|
|
7
7
|
import { CONFIG_LIMITS, normalizeLimited } from './numericLimits.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
maxCallsPerTurn: 6,
|
|
11
|
-
maxConcurrent: 3,
|
|
12
|
-
defaultWriteIsolation: 'none',
|
|
13
|
-
timeoutMs: 1_800_000,
|
|
14
|
-
exploreMaxTurns: 50,
|
|
15
|
-
generalMaxTurns: 100,
|
|
16
|
-
};
|
|
17
|
-
export const DEFAULT_BROWSER_CONFIG = {
|
|
18
|
-
enabled: true,
|
|
19
|
-
profileId: 'default',
|
|
20
|
-
developerMode: false,
|
|
21
|
-
allowedCdpOrigins: [],
|
|
22
|
-
};
|
|
23
|
-
export const DEFAULT_COMPUTER_USE_CONFIG = {
|
|
24
|
-
enabled: true,
|
|
25
|
-
imagePolicy: 'single-adaptive',
|
|
26
|
-
maxImagePixels: 1_600_000,
|
|
27
|
-
observationFrames: 1,
|
|
28
|
-
};
|
|
8
|
+
import { DEFAULT_BROWSER_CONFIG, DEFAULT_COMPUTER_USE_CONFIG, DEFAULT_SUBAGENTS_CONFIG, } from './configDefaults.js';
|
|
9
|
+
export { DEFAULT_BROWSER_CONFIG, DEFAULT_COMPUTER_USE_CONFIG, DEFAULT_SUBAGENTS_CONFIG, } from './configDefaults.js';
|
|
29
10
|
const DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
30
11
|
const LOCK_STALE_MS = 120_000;
|
|
31
12
|
const LOCK_WAIT_MS = 15_000;
|
package/src/config/configFile.ts
CHANGED
|
@@ -7,6 +7,17 @@ import { execSiblingDir } from '../utils/greenRoot.js';
|
|
|
7
7
|
import type { PromptCacheConfig } from '../types.js';
|
|
8
8
|
import { normalizePromptCacheMode } from '../llm/promptCache.js';
|
|
9
9
|
import { CONFIG_LIMITS, normalizeLimited } from './numericLimits.js';
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_BROWSER_CONFIG,
|
|
12
|
+
DEFAULT_COMPUTER_USE_CONFIG,
|
|
13
|
+
DEFAULT_SUBAGENTS_CONFIG,
|
|
14
|
+
} from './configDefaults.js';
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
DEFAULT_BROWSER_CONFIG,
|
|
18
|
+
DEFAULT_COMPUTER_USE_CONFIG,
|
|
19
|
+
DEFAULT_SUBAGENTS_CONFIG,
|
|
20
|
+
} from './configDefaults.js';
|
|
10
21
|
|
|
11
22
|
export type ImageProtocol = 'auto' | 'chat' | 'images';
|
|
12
23
|
export type ApiFormat = 'openai' | 'anthropic';
|
|
@@ -95,30 +106,6 @@ export interface MinimalAgentConfig {
|
|
|
95
106
|
savedAt: number;
|
|
96
107
|
}
|
|
97
108
|
|
|
98
|
-
export const DEFAULT_SUBAGENTS_CONFIG: SubagentsConfig = {
|
|
99
|
-
enabled: true,
|
|
100
|
-
maxCallsPerTurn: 6,
|
|
101
|
-
maxConcurrent: 3,
|
|
102
|
-
defaultWriteIsolation: 'none',
|
|
103
|
-
timeoutMs: 1_800_000,
|
|
104
|
-
exploreMaxTurns: 50,
|
|
105
|
-
generalMaxTurns: 100,
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export const DEFAULT_BROWSER_CONFIG: BrowserConfig = {
|
|
109
|
-
enabled: true,
|
|
110
|
-
profileId: 'default',
|
|
111
|
-
developerMode: false,
|
|
112
|
-
allowedCdpOrigins: [],
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
export const DEFAULT_COMPUTER_USE_CONFIG: ComputerUseConfig = {
|
|
116
|
-
enabled: true,
|
|
117
|
-
imagePolicy: 'single-adaptive',
|
|
118
|
-
maxImagePixels: 1_600_000,
|
|
119
|
-
observationFrames: 1,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
109
|
/**
|
|
123
110
|
* 加模型是提交式弹窗、顾问改字段都是有界枚举/数字——不会出现「输入中途非法值」,
|
|
124
111
|
* 所以 v3 时代的 provider/advisor 草稿桶随 schema 一起删除,只剩 tools(imageGeneration/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_BROWSER_CONFIG, DEFAULT_COMPUTER_USE_CONFIG, } from './
|
|
1
|
+
import { DEFAULT_BROWSER_CONFIG, DEFAULT_COMPUTER_USE_CONFIG, } from './configDefaults.js';
|
|
2
2
|
/**
|
|
3
3
|
* CLI 和 Desktop 共用的脱敏配置视图。这里不返回任何凭据,也不读取 Runtime
|
|
4
4
|
* 状态;它只是把 V4 权威配置稳定地投影成可展示、可比较的管理模型。
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_BROWSER_CONFIG,
|
|
3
3
|
DEFAULT_COMPUTER_USE_CONFIG,
|
|
4
|
-
|
|
5
|
-
} from './configFile.js';
|
|
4
|
+
} from './configDefaults.js';
|
|
5
|
+
import type { ModelConfig } from './configFile.js';
|
|
6
6
|
import type { ConfigSnapshot } from './configService.js';
|
|
7
7
|
|
|
8
8
|
export type ManagementStatus = 'unconfigured' | 'configured' | 'incomplete';
|
|
@@ -373,6 +373,30 @@ export class ConfigService {
|
|
|
373
373
|
return null;
|
|
374
374
|
return { ...advisor, provider: resolveProvider(entry), supportsVision: entry.supportsVision !== false };
|
|
375
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* 同步版 advisor 可用性探测——仅用于 resolveAvailability 这类工具列表探测场景。
|
|
378
|
+
* 读当前内存快照(可能略滞后于磁盘最新值),但调用频率高、对实时性要求不高,
|
|
379
|
+
* 下一轮 LLM 请求前 configService.reload() 会刷新。
|
|
380
|
+
*
|
|
381
|
+
* 为什么不用 advisor() 的 fresh reload:
|
|
382
|
+
* resolveAvailability 是在主循环构造工具集时同步调用的,不能 await;
|
|
383
|
+
* 而且每次工具集构造都读盘的话开销太大(主循环每轮都可能构造一次)。
|
|
384
|
+
*/
|
|
385
|
+
advisorAvailabilitySync() {
|
|
386
|
+
const config = this.snapshot.config;
|
|
387
|
+
const advisor = config?.advisor;
|
|
388
|
+
if (!advisor?.modelId)
|
|
389
|
+
return { enabled: false, mode: 'auto', modelId: null, configured: false };
|
|
390
|
+
const entry = config?.models?.find((m) => m.id === advisor.modelId);
|
|
391
|
+
if (!entry?.apiKey)
|
|
392
|
+
return { enabled: false, mode: 'auto', modelId: null, configured: false };
|
|
393
|
+
return {
|
|
394
|
+
enabled: advisor.enabled !== false,
|
|
395
|
+
mode: advisor.mode === 'manual' ? 'manual' : 'auto',
|
|
396
|
+
modelId: advisor.modelId,
|
|
397
|
+
configured: true,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
376
400
|
async subagents() {
|
|
377
401
|
return (await this.reload()).config?.subagents ?? DEFAULT_SUBAGENTS_CONFIG;
|
|
378
402
|
}
|
|
@@ -431,6 +431,29 @@ export class ConfigService {
|
|
|
431
431
|
return { ...advisor, provider: resolveProvider(entry), supportsVision: entry.supportsVision !== false };
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
/**
|
|
435
|
+
* 同步版 advisor 可用性探测——仅用于 resolveAvailability 这类工具列表探测场景。
|
|
436
|
+
* 读当前内存快照(可能略滞后于磁盘最新值),但调用频率高、对实时性要求不高,
|
|
437
|
+
* 下一轮 LLM 请求前 configService.reload() 会刷新。
|
|
438
|
+
*
|
|
439
|
+
* 为什么不用 advisor() 的 fresh reload:
|
|
440
|
+
* resolveAvailability 是在主循环构造工具集时同步调用的,不能 await;
|
|
441
|
+
* 而且每次工具集构造都读盘的话开销太大(主循环每轮都可能构造一次)。
|
|
442
|
+
*/
|
|
443
|
+
advisorAvailabilitySync(): { enabled: boolean; mode: 'auto' | 'manual'; modelId: string | null; configured: boolean } {
|
|
444
|
+
const config = this.snapshot.config;
|
|
445
|
+
const advisor = config?.advisor;
|
|
446
|
+
if (!advisor?.modelId) return { enabled: false, mode: 'auto', modelId: null, configured: false };
|
|
447
|
+
const entry = config?.models?.find((m) => m.id === advisor.modelId);
|
|
448
|
+
if (!entry?.apiKey) return { enabled: false, mode: 'auto', modelId: null, configured: false };
|
|
449
|
+
return {
|
|
450
|
+
enabled: advisor.enabled !== false,
|
|
451
|
+
mode: advisor.mode === 'manual' ? 'manual' : 'auto',
|
|
452
|
+
modelId: advisor.modelId,
|
|
453
|
+
configured: true,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
434
457
|
async subagents(): Promise<SubagentsConfig> {
|
|
435
458
|
return (await this.reload()).config?.subagents ?? DEFAULT_SUBAGENTS_CONFIG;
|
|
436
459
|
}
|
package/src/context/compact.js
CHANGED
|
@@ -379,6 +379,10 @@ async function runCompaction(messages, provider, signal, chatFn = chat, promptCa
|
|
|
379
379
|
...(systemMsg ? [systemMsg] : []),
|
|
380
380
|
{
|
|
381
381
|
role: 'user',
|
|
382
|
+
// ⚠️ 这里**故意不带** provider 原始报错:本条消息会进 history,既落盘也会在
|
|
383
|
+
// 后续轮次重新发给模型,错误体里可能夹带 URL / 请求体 / 凭据。
|
|
384
|
+
// 排障线索走 `compact_failed` 事件(只到 stderr / UI,不进 history)。
|
|
385
|
+
// `compact.test.ts::reports snip fallback without exposing the raw LLM error` 锁定此约定。
|
|
382
386
|
content: '(LLM 摘要压缩失败,已使用规则式截断继续会话)'
|
|
383
387
|
+ (failureLedger
|
|
384
388
|
? `\n\n<user_messages>\n以下是被截掉的那部分对话里**用户的原话**(程序逐字抽取)。摘要不存在,这里就是唯一残留的历史要求。\n${failureLedger}\n</user_messages>`
|
package/src/context/compact.ts
CHANGED
|
@@ -491,6 +491,10 @@ async function runCompaction(
|
|
|
491
491
|
...(systemMsg ? [systemMsg] : []),
|
|
492
492
|
{
|
|
493
493
|
role: 'user',
|
|
494
|
+
// ⚠️ 这里**故意不带** provider 原始报错:本条消息会进 history,既落盘也会在
|
|
495
|
+
// 后续轮次重新发给模型,错误体里可能夹带 URL / 请求体 / 凭据。
|
|
496
|
+
// 排障线索走 `compact_failed` 事件(只到 stderr / UI,不进 history)。
|
|
497
|
+
// `compact.test.ts::reports snip fallback without exposing the raw LLM error` 锁定此约定。
|
|
494
498
|
content: '(LLM 摘要压缩失败,已使用规则式截断继续会话)'
|
|
495
499
|
+ (failureLedger
|
|
496
500
|
? `\n\n<user_messages>\n以下是被截掉的那部分对话里**用户的原话**(程序逐字抽取)。摘要不存在,这里就是唯一残留的历史要求。\n${failureLedger}\n</user_messages>`
|