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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minimal-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Bun-first 的本地 Agent 内核,支持工具、插件、工作流、上下文压缩、长期记忆与 OpenAI 兼容接口;Tauri 桌面宿主和 Node.js npm 发布可复用同一内核",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Bill Wang <leiwang0359@gmail.com>",
|
|
@@ -131,9 +131,12 @@
|
|
|
131
131
|
"prepublishOnly": "bun run clean && bun run build"
|
|
132
132
|
},
|
|
133
133
|
"dependencies": {
|
|
134
|
+
"ccxt": "^4.5.58",
|
|
135
|
+
"diff": "^8.0.2",
|
|
134
136
|
"fast-glob": "^3.3.2",
|
|
135
137
|
"ink": "^5.0.1",
|
|
136
138
|
"marked": "^18.0.6",
|
|
139
|
+
"pdfjs-dist": "^6.2.108",
|
|
137
140
|
"react": "^18.3.1",
|
|
138
141
|
"turndown": "^7.2.4",
|
|
139
142
|
"vscode-jsonrpc": "^8.2.1",
|
|
@@ -148,6 +151,7 @@
|
|
|
148
151
|
"typescript": "^5.5.0"
|
|
149
152
|
},
|
|
150
153
|
"optionalDependencies": {
|
|
154
|
+
"@napi-rs/canvas": "^0.1.100",
|
|
151
155
|
"playwright-core": "^1.40.0",
|
|
152
156
|
"pngjs": "^7.0.0",
|
|
153
157
|
"pptxgenjs": "^4.0.1"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 缓存这次 import,所以 cold start 后 dynamic import 只跑一次。
|
|
13
13
|
* ============================================================
|
|
14
14
|
*/
|
|
15
|
-
import { ALL_TOOLS,
|
|
15
|
+
import { ALL_TOOLS, buildTransientPromptBundle, createAgentRuntimeContext, derivePromptCacheContext, filterToolsForRuntime, getWorkingDir, } from '../../src/plugin-sdk.js';
|
|
16
16
|
import { runWorkflowFromCommand, runWorkflowsList } from './src/index.js';
|
|
17
17
|
const api = {
|
|
18
18
|
async *runCommand(commandName, args, ctx) {
|
|
@@ -43,21 +43,22 @@ const api = {
|
|
|
43
43
|
? ALL_TOOLS.filter((tool) => hostToolNames.has(tool.name))
|
|
44
44
|
: ALL_TOOLS;
|
|
45
45
|
const workflowTools = filterToolsForRuntime(hostTools, workflowRuntime);
|
|
46
|
-
const transientBundle = ctx.transientBundle ??
|
|
47
|
-
|
|
48
|
-
tools: workflowTools,
|
|
49
|
-
profile: 'main',
|
|
50
|
-
includeProjectInstructions: true,
|
|
51
|
-
includePlugins: true,
|
|
52
|
-
runtime: {
|
|
53
|
-
surface: 'workflow',
|
|
46
|
+
const transientBundle = ctx.transientBundle ??
|
|
47
|
+
(await buildTransientPromptBundle({
|
|
54
48
|
cwd,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
tools: workflowTools,
|
|
50
|
+
profile: 'main',
|
|
51
|
+
includeProjectInstructions: true,
|
|
52
|
+
includePlugins: true,
|
|
53
|
+
runtime: {
|
|
54
|
+
surface: 'workflow',
|
|
55
|
+
cwd,
|
|
56
|
+
workspaceId: workflowRuntime.workspaceId,
|
|
57
|
+
interactiveAsk: workflowRuntime.interactiveAsk,
|
|
58
|
+
memoryAccess: workflowRuntime.memoryAccess,
|
|
59
|
+
currentUserInput: args,
|
|
60
|
+
},
|
|
61
|
+
}).catch(() => undefined));
|
|
61
62
|
yield* runWorkflowFromCommand(args, {
|
|
62
63
|
provider: ctx.provider,
|
|
63
64
|
history: ctx.history,
|
|
@@ -66,6 +67,17 @@ const api = {
|
|
|
66
67
|
cwd,
|
|
67
68
|
tools: workflowTools,
|
|
68
69
|
runtimeContext: workflowRuntime,
|
|
70
|
+
executeTool: ctx.executeTool,
|
|
71
|
+
toolContext: {
|
|
72
|
+
history: ctx.history,
|
|
73
|
+
sessionState: ctx.sessionState,
|
|
74
|
+
provider: ctx.provider,
|
|
75
|
+
toolGate: ctx.host?.toolGate,
|
|
76
|
+
promptCache: ctx.promptCache,
|
|
77
|
+
workspaceId: workflowRuntime.workspaceId,
|
|
78
|
+
sessionId: workflowRuntime.sessionId,
|
|
79
|
+
memoryAccess: workflowRuntime.memoryAccess,
|
|
80
|
+
},
|
|
69
81
|
// 宿主授权上下文原样带上;引擎按各步 `uses:` 切片后才转给工具,不整份下发。
|
|
70
82
|
host: ctx.host,
|
|
71
83
|
transientContext: ctx.transientContext,
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
import type { PluginApi } from '../../src/plugin-sdk.js';
|
|
17
17
|
import {
|
|
18
18
|
ALL_TOOLS,
|
|
19
|
-
|
|
20
|
-
getWorkingDir,
|
|
19
|
+
buildTransientPromptBundle,
|
|
21
20
|
createAgentRuntimeContext,
|
|
21
|
+
derivePromptCacheContext,
|
|
22
22
|
filterToolsForRuntime,
|
|
23
|
-
|
|
23
|
+
getWorkingDir,
|
|
24
24
|
} from '../../src/plugin-sdk.js';
|
|
25
25
|
import { runWorkflowFromCommand, runWorkflowsList } from './src/index.js';
|
|
26
26
|
|
|
@@ -43,9 +43,10 @@ const api: PluginApi = {
|
|
|
43
43
|
cwd,
|
|
44
44
|
surface: 'workflow',
|
|
45
45
|
owner: ctx.runtimeContext?.owner ?? 'untrusted',
|
|
46
|
-
memoryAccess:
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
memoryAccess:
|
|
47
|
+
ctx.runtimeContext?.memoryAccess === 'denied'
|
|
48
|
+
? 'denied'
|
|
49
|
+
: 'read-only',
|
|
49
50
|
});
|
|
50
51
|
const hostToolNames = ctx.allowedTools
|
|
51
52
|
? new Set(ctx.allowedTools)
|
|
@@ -54,21 +55,23 @@ const api: PluginApi = {
|
|
|
54
55
|
? ALL_TOOLS.filter((tool) => hostToolNames.has(tool.name))
|
|
55
56
|
: ALL_TOOLS;
|
|
56
57
|
const workflowTools = filterToolsForRuntime(hostTools, workflowRuntime);
|
|
57
|
-
const transientBundle =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
profile: 'main',
|
|
61
|
-
includeProjectInstructions: true,
|
|
62
|
-
includePlugins: true,
|
|
63
|
-
runtime: {
|
|
64
|
-
surface: 'workflow',
|
|
58
|
+
const transientBundle =
|
|
59
|
+
ctx.transientBundle ??
|
|
60
|
+
(await buildTransientPromptBundle({
|
|
65
61
|
cwd,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
tools: workflowTools,
|
|
63
|
+
profile: 'main',
|
|
64
|
+
includeProjectInstructions: true,
|
|
65
|
+
includePlugins: true,
|
|
66
|
+
runtime: {
|
|
67
|
+
surface: 'workflow',
|
|
68
|
+
cwd,
|
|
69
|
+
workspaceId: workflowRuntime.workspaceId,
|
|
70
|
+
interactiveAsk: workflowRuntime.interactiveAsk,
|
|
71
|
+
memoryAccess: workflowRuntime.memoryAccess,
|
|
72
|
+
currentUserInput: args,
|
|
73
|
+
},
|
|
74
|
+
}).catch(() => undefined));
|
|
72
75
|
yield* runWorkflowFromCommand(args, {
|
|
73
76
|
provider: ctx.provider,
|
|
74
77
|
history: ctx.history,
|
|
@@ -77,6 +80,17 @@ const api: PluginApi = {
|
|
|
77
80
|
cwd,
|
|
78
81
|
tools: workflowTools,
|
|
79
82
|
runtimeContext: workflowRuntime,
|
|
83
|
+
executeTool: ctx.executeTool,
|
|
84
|
+
toolContext: {
|
|
85
|
+
history: ctx.history,
|
|
86
|
+
sessionState: ctx.sessionState,
|
|
87
|
+
provider: ctx.provider,
|
|
88
|
+
toolGate: ctx.host?.toolGate,
|
|
89
|
+
promptCache: ctx.promptCache,
|
|
90
|
+
workspaceId: workflowRuntime.workspaceId,
|
|
91
|
+
sessionId: workflowRuntime.sessionId,
|
|
92
|
+
memoryAccess: workflowRuntime.memoryAccess,
|
|
93
|
+
},
|
|
80
94
|
// 宿主授权上下文原样带上;引擎按各步 `uses:` 切片后才转给工具,不整份下发。
|
|
81
95
|
host: ctx.host,
|
|
82
96
|
transientContext: ctx.transientContext,
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
* ③ 检测逻辑增加内容长度启发式(reason > 80 字符视为误报)
|
|
20
20
|
* ============================================================
|
|
21
21
|
*/
|
|
22
|
-
import {
|
|
22
|
+
import { createAgentRuntimeContext, runQuery, stripThinkBlocks, } from '../../../src/plugin-sdk.js';
|
|
23
|
+
import { effectiveWorkflowCwd } from './toolRuntime.js';
|
|
23
24
|
import { normalizeCapture, } from './types.js';
|
|
24
25
|
/**
|
|
25
26
|
* agent 显式放弃的唯一标记;recover 与 agent 节点共用。
|
|
@@ -59,7 +60,9 @@ export function effectiveGoalAnchor(ctx) {
|
|
|
59
60
|
if (supps.length === 0)
|
|
60
61
|
return base;
|
|
61
62
|
const lines = supps.map((s) => `> 📌 用户补充要求:${s}`).join('\n');
|
|
62
|
-
return base
|
|
63
|
+
return base
|
|
64
|
+
? `${base}\n${lines}`
|
|
65
|
+
: `${lines}\n> ⚠️ 以上补充要求与原始目标同等约束所有操作。`;
|
|
63
66
|
}
|
|
64
67
|
/** 把 llm_error 细节拼进可重试失败的抛错文案(无细节时退化为原样文案)。 */
|
|
65
68
|
export function describeLlmFailure(stepId, detail) {
|
|
@@ -96,14 +99,10 @@ function createStepHistory(step, ctx) {
|
|
|
96
99
|
export async function* runStepQuery(prompt, step, ctx, opts) {
|
|
97
100
|
const activeHistory = createStepHistory(step, ctx);
|
|
98
101
|
const availableTools = ctx.tools ?? [];
|
|
99
|
-
const hostToolNames = availableTools.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
? step.allowed_tools
|
|
104
|
-
: step.allowed_tools === undefined
|
|
105
|
-
? [...hostToolNames]
|
|
106
|
-
: step.allowed_tools.filter((name) => hostToolNames.has(name));
|
|
102
|
+
const hostToolNames = new Set(availableTools.map((tool) => tool.name));
|
|
103
|
+
const effectiveStepTools = step.allowed_tools === undefined
|
|
104
|
+
? [...hostToolNames]
|
|
105
|
+
: step.allowed_tools.filter((name) => hostToolNames.has(name));
|
|
107
106
|
// 记录 runQuery 开始前的 history 长度,用于之后提取本步的 assistant 输出
|
|
108
107
|
const historyLenBefore = activeHistory.length;
|
|
109
108
|
const runtimeContext = createAgentRuntimeContext({
|
|
@@ -112,7 +111,7 @@ export async function* runStepQuery(prompt, step, ctx, opts) {
|
|
|
112
111
|
profile: 'main',
|
|
113
112
|
interactiveAsk: false,
|
|
114
113
|
}),
|
|
115
|
-
cwd: ctx
|
|
114
|
+
cwd: effectiveWorkflowCwd(ctx),
|
|
116
115
|
owner: ctx.runtimeContext?.owner ?? 'untrusted',
|
|
117
116
|
memoryAccess: ctx.runtimeContext?.memoryAccess ?? 'denied',
|
|
118
117
|
});
|
|
@@ -283,7 +282,9 @@ function isLikelyFalsePositive(reason) {
|
|
|
283
282
|
if (reason.length > 80)
|
|
284
283
|
return true;
|
|
285
284
|
// 如果 reason 看起来像 markdown 正文(标题/章节),也是误报
|
|
286
|
-
if (/^#{1,6}\s/m.test(reason) ||
|
|
285
|
+
if (/^#{1,6}\s/m.test(reason) ||
|
|
286
|
+
/^[-*+]\s/m.test(reason) ||
|
|
287
|
+
/^\d+\.\s/m.test(reason)) {
|
|
287
288
|
return true;
|
|
288
289
|
}
|
|
289
290
|
return false;
|
|
@@ -25,10 +25,11 @@ import {
|
|
|
25
25
|
type ImageInputSpec,
|
|
26
26
|
type LoopEvent,
|
|
27
27
|
type Message,
|
|
28
|
-
runQuery,
|
|
29
28
|
createAgentRuntimeContext,
|
|
29
|
+
runQuery,
|
|
30
30
|
stripThinkBlocks,
|
|
31
31
|
} from '../../../src/plugin-sdk.js';
|
|
32
|
+
import { effectiveWorkflowCwd } from './toolRuntime.js';
|
|
32
33
|
import {
|
|
33
34
|
type RunContext,
|
|
34
35
|
type StepDef,
|
|
@@ -81,7 +82,9 @@ export function effectiveGoalAnchor(ctx: RunContext): string {
|
|
|
81
82
|
const supps = (ctx.goalSupplements ?? []).filter((s) => s.trim());
|
|
82
83
|
if (supps.length === 0) return base;
|
|
83
84
|
const lines = supps.map((s) => `> 📌 用户补充要求:${s}`).join('\n');
|
|
84
|
-
return base
|
|
85
|
+
return base
|
|
86
|
+
? `${base}\n${lines}`
|
|
87
|
+
: `${lines}\n> ⚠️ 以上补充要求与原始目标同等约束所有操作。`;
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
/** runStepQuery 的归纳结果:本步产出文本 + 终结性错误旗标(语义判断留给各执行器)。 */
|
|
@@ -164,12 +167,9 @@ export async function* runStepQuery(
|
|
|
164
167
|
): AsyncGenerator<LoopEvent, StepQueryOutcome, void> {
|
|
165
168
|
const activeHistory = createStepHistory(step, ctx);
|
|
166
169
|
const availableTools = ctx.tools ?? [];
|
|
167
|
-
const hostToolNames = availableTools.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const effectiveStepTools = hostToolNames === null
|
|
171
|
-
? step.allowed_tools
|
|
172
|
-
: step.allowed_tools === undefined
|
|
170
|
+
const hostToolNames = new Set(availableTools.map((tool) => tool.name));
|
|
171
|
+
const effectiveStepTools =
|
|
172
|
+
step.allowed_tools === undefined
|
|
173
173
|
? [...hostToolNames]
|
|
174
174
|
: step.allowed_tools.filter((name) => hostToolNames.has(name));
|
|
175
175
|
// 记录 runQuery 开始前的 history 长度,用于之后提取本步的 assistant 输出
|
|
@@ -180,7 +180,7 @@ export async function* runStepQuery(
|
|
|
180
180
|
profile: 'main' as const,
|
|
181
181
|
interactiveAsk: false,
|
|
182
182
|
}),
|
|
183
|
-
cwd: ctx
|
|
183
|
+
cwd: effectiveWorkflowCwd(ctx),
|
|
184
184
|
owner: ctx.runtimeContext?.owner ?? 'untrusted',
|
|
185
185
|
memoryAccess: ctx.runtimeContext?.memoryAccess ?? 'denied',
|
|
186
186
|
});
|
|
@@ -200,7 +200,8 @@ export async function* runStepQuery(
|
|
|
200
200
|
signal: ctx.signal,
|
|
201
201
|
maxTurns: opts.maxTurns,
|
|
202
202
|
allowedTools: effectiveStepTools,
|
|
203
|
-
requiredTools:
|
|
203
|
+
requiredTools:
|
|
204
|
+
step.allowed_tools === undefined ? undefined : effectiveStepTools,
|
|
204
205
|
chat: ctx.chat,
|
|
205
206
|
executeTool: ctx.executeTool,
|
|
206
207
|
sessionState: ctx.sessionState,
|
|
@@ -296,7 +297,10 @@ export async function* runStepQuery(
|
|
|
296
297
|
export const MAX_TURNS_CUTOFF_MARKER = '达到最大轮数';
|
|
297
298
|
|
|
298
299
|
/** maxTurns 截断的可重试失败文案(重试 = 在共享 history 上续作,再给一份轮数预算)。 */
|
|
299
|
-
export function describeMaxTurnsCutoff(
|
|
300
|
+
export function describeMaxTurnsCutoff(
|
|
301
|
+
stepId: string,
|
|
302
|
+
maxTurns: number,
|
|
303
|
+
): string {
|
|
300
304
|
return `step ${stepId}: ${MAX_TURNS_CUTOFF_MARKER} ${maxTurns} 被截断(可续跑)`;
|
|
301
305
|
}
|
|
302
306
|
|
|
@@ -351,7 +355,11 @@ export function parseGiveup(text: string): string | null {
|
|
|
351
355
|
function isLikelyFalsePositive(reason: string): boolean {
|
|
352
356
|
if (reason.length > 80) return true;
|
|
353
357
|
// 如果 reason 看起来像 markdown 正文(标题/章节),也是误报
|
|
354
|
-
if (
|
|
358
|
+
if (
|
|
359
|
+
/^#{1,6}\s/m.test(reason) ||
|
|
360
|
+
/^[-*+]\s/m.test(reason) ||
|
|
361
|
+
/^\d+\.\s/m.test(reason)
|
|
362
|
+
) {
|
|
355
363
|
return true;
|
|
356
364
|
}
|
|
357
365
|
return false;
|
|
@@ -405,7 +413,8 @@ export function interpretMiniReActResult(
|
|
|
405
413
|
ok: true,
|
|
406
414
|
value: {
|
|
407
415
|
raw,
|
|
408
|
-
preview:
|
|
416
|
+
preview:
|
|
417
|
+
cleanText.length > 200 ? `${cleanText.slice(0, 200)}…` : cleanText,
|
|
409
418
|
},
|
|
410
419
|
};
|
|
411
420
|
}
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
import { existsSync, readFileSync } from 'node:fs';
|
|
41
41
|
import { join, isAbsolute } from 'node:path';
|
|
42
42
|
import { getWorkingDir } from '../../../src/plugin-sdk.js';
|
|
43
|
+
import { DANGEROUS_KEYS, isAbsWithinCwd } from './types.js';
|
|
43
44
|
const PUNCT2 = new Set(['==', '!=', '>=', '<=', '&&', '||']);
|
|
44
45
|
const PUNCT1 = new Set(['(', ')', '[', ']', ',', '.', '!', '>', '<', '+', '?', ':']);
|
|
45
46
|
function tokenize(src) {
|
|
@@ -143,12 +144,25 @@ function isPunct(c, v) {
|
|
|
143
144
|
const t = peek(c);
|
|
144
145
|
return !!t && t.kind === 'punct' && t.value === v;
|
|
145
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* 读磁盘的内置函数(fileExists / readJson)的锚点。
|
|
149
|
+
* 必须和写入侧同源:进了 session worktree 后产物落在 worktree 里,
|
|
150
|
+
* 这里若仍锚原 workspace,done_when 会在空目录里找文件而恒判 false。
|
|
151
|
+
*/
|
|
152
|
+
function cwdOf(vars) {
|
|
153
|
+
return vars.resolveCwd?.() ?? getWorkingDir();
|
|
154
|
+
}
|
|
146
155
|
const BUILTINS = {
|
|
147
|
-
fileExists(args) {
|
|
156
|
+
fileExists(args, cwd) {
|
|
148
157
|
const p = String(args[0] ?? '');
|
|
149
158
|
if (!p)
|
|
150
159
|
return false;
|
|
151
|
-
const abs = isAbsolute(p) ? p : join(
|
|
160
|
+
const abs = isAbsolute(p) ? p : join(cwd, p);
|
|
161
|
+
// 安全守卫:越出 cwd 的路径视为"不存在"(fail-closed,与 readJson 同策略)。
|
|
162
|
+
// 不可信 YAML(模板库/他人分享)可能带 fileExists('/etc/passwd') 做信息刺探,
|
|
163
|
+
// 这里与 cleanupDeclaredArtifacts 保持一致:只允许操作 cwd 下的文件。
|
|
164
|
+
if (!isAbsWithinCwd(cwd, abs))
|
|
165
|
+
return false;
|
|
152
166
|
return existsSync(abs);
|
|
153
167
|
},
|
|
154
168
|
length(args) {
|
|
@@ -247,16 +261,19 @@ const BUILTINS = {
|
|
|
247
261
|
return 'null';
|
|
248
262
|
}
|
|
249
263
|
},
|
|
250
|
-
// 读磁盘 JSON 文件并取值:path
|
|
251
|
-
//
|
|
264
|
+
// 读磁盘 JSON 文件并取值:path 相对当前有效 cwd(绝对路径先经 cwd 安全校验,
|
|
265
|
+
// 越界直接 null,与 fileExists 同 fail-closed 策略);给了 key 则返回顶层
|
|
266
|
+
// obj[key],否则返回整值。缺文件 / 坏 JSON / 键不存在 / 越界 → null
|
|
252
267
|
//(与 match/extract 同「不抛」哲学)。动机:让确定性脚本产出的 manifest
|
|
253
268
|
//(如 source_assets.json)直接驱动工作流 for-each / branch —— 填补「磁盘
|
|
254
269
|
// 结构化数据 → 控制流」的空白(与 fileExists 平级的基础能力)。
|
|
255
|
-
readJson(args) {
|
|
270
|
+
readJson(args, cwd) {
|
|
256
271
|
const p = String(args[0] ?? '');
|
|
257
272
|
if (!p)
|
|
258
273
|
return null;
|
|
259
|
-
const abs = isAbsolute(p) ? p : join(
|
|
274
|
+
const abs = isAbsolute(p) ? p : join(cwd, p);
|
|
275
|
+
if (!isAbsWithinCwd(cwd, abs))
|
|
276
|
+
return null; // 越界 → null(不抛,fail-closed)
|
|
260
277
|
try {
|
|
261
278
|
// 剥前导 BOM:某些工具/重定向写出的 JSON 带 BOM,JSON.parse 会因此抛错。
|
|
262
279
|
let raw = readFileSync(abs, 'utf8');
|
|
@@ -439,7 +456,7 @@ function parsePrimary(c, vars) {
|
|
|
439
456
|
const fn = BUILTINS[t.value];
|
|
440
457
|
if (!fn)
|
|
441
458
|
throw new Error(`未知函数 "${t.value}"。白名单:${Object.keys(BUILTINS).join(', ')}`);
|
|
442
|
-
return fn(argv);
|
|
459
|
+
return fn(argv, cwdOf(vars));
|
|
443
460
|
}
|
|
444
461
|
// 字面常量
|
|
445
462
|
if (t.value === 'true')
|
|
@@ -608,8 +625,12 @@ export function interpolateDeep(value, vars, opts) {
|
|
|
608
625
|
if (value && typeof value === 'object') {
|
|
609
626
|
const out = {};
|
|
610
627
|
for (const [k, v] of Object.entries(value)) {
|
|
611
|
-
// 注意:key 也插值(loop 里 `hook_${idx}` 这种),但只对字符串 key
|
|
628
|
+
// 注意:key 也插值(loop 里 `hook_${idx}` 这种),但只对字符串 key 有意义;
|
|
629
|
+
// 插值后的 key 若命中危险键(__proto__/constructor/prototype)→ 直接跳过不写,
|
|
630
|
+
// 防止经动态 key 注入原型链属性(与 VarStack 防线保持一致)。
|
|
612
631
|
const newKey = interpolate(k, vars, opts);
|
|
632
|
+
if (DANGEROUS_KEYS.has(newKey))
|
|
633
|
+
continue;
|
|
613
634
|
out[newKey] = interpolateDeep(v, vars, opts);
|
|
614
635
|
}
|
|
615
636
|
return out;
|
|
@@ -42,7 +42,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
42
42
|
import { join, isAbsolute } from 'node:path';
|
|
43
43
|
|
|
44
44
|
import { getWorkingDir } from '../../../src/plugin-sdk.js';
|
|
45
|
-
import type
|
|
45
|
+
import { DANGEROUS_KEYS, isAbsWithinCwd, type VarStack } from './types.js';
|
|
46
46
|
|
|
47
47
|
// ---------------- Tokenizer ----------------
|
|
48
48
|
|
|
@@ -160,11 +160,24 @@ function isPunct(c: Cursor, v: string): boolean {
|
|
|
160
160
|
return !!t && t.kind === 'punct' && t.value === v;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
/**
|
|
164
|
+
* 读磁盘的内置函数(fileExists / readJson)的锚点。
|
|
165
|
+
* 必须和写入侧同源:进了 session worktree 后产物落在 worktree 里,
|
|
166
|
+
* 这里若仍锚原 workspace,done_when 会在空目录里找文件而恒判 false。
|
|
167
|
+
*/
|
|
168
|
+
function cwdOf(vars: VarStack): string {
|
|
169
|
+
return vars.resolveCwd?.() ?? getWorkingDir();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const BUILTINS: Record<string, (args: unknown[], cwd: string) => unknown> = {
|
|
173
|
+
fileExists(args, cwd) {
|
|
165
174
|
const p = String(args[0] ?? '');
|
|
166
175
|
if (!p) return false;
|
|
167
|
-
const abs = isAbsolute(p) ? p : join(
|
|
176
|
+
const abs = isAbsolute(p) ? p : join(cwd, p);
|
|
177
|
+
// 安全守卫:越出 cwd 的路径视为"不存在"(fail-closed,与 readJson 同策略)。
|
|
178
|
+
// 不可信 YAML(模板库/他人分享)可能带 fileExists('/etc/passwd') 做信息刺探,
|
|
179
|
+
// 这里与 cleanupDeclaredArtifacts 保持一致:只允许操作 cwd 下的文件。
|
|
180
|
+
if (!isAbsWithinCwd(cwd, abs)) return false;
|
|
168
181
|
return existsSync(abs);
|
|
169
182
|
},
|
|
170
183
|
length(args) {
|
|
@@ -254,15 +267,17 @@ const BUILTINS: Record<string, (args: unknown[]) => unknown> = {
|
|
|
254
267
|
return 'null';
|
|
255
268
|
}
|
|
256
269
|
},
|
|
257
|
-
// 读磁盘 JSON 文件并取值:path
|
|
258
|
-
//
|
|
270
|
+
// 读磁盘 JSON 文件并取值:path 相对当前有效 cwd(绝对路径先经 cwd 安全校验,
|
|
271
|
+
// 越界直接 null,与 fileExists 同 fail-closed 策略);给了 key 则返回顶层
|
|
272
|
+
// obj[key],否则返回整值。缺文件 / 坏 JSON / 键不存在 / 越界 → null
|
|
259
273
|
//(与 match/extract 同「不抛」哲学)。动机:让确定性脚本产出的 manifest
|
|
260
274
|
//(如 source_assets.json)直接驱动工作流 for-each / branch —— 填补「磁盘
|
|
261
275
|
// 结构化数据 → 控制流」的空白(与 fileExists 平级的基础能力)。
|
|
262
|
-
readJson(args) {
|
|
276
|
+
readJson(args, cwd) {
|
|
263
277
|
const p = String(args[0] ?? '');
|
|
264
278
|
if (!p) return null;
|
|
265
|
-
const abs = isAbsolute(p) ? p : join(
|
|
279
|
+
const abs = isAbsolute(p) ? p : join(cwd, p);
|
|
280
|
+
if (!isAbsWithinCwd(cwd, abs)) return null; // 越界 → null(不抛,fail-closed)
|
|
266
281
|
try {
|
|
267
282
|
// 剥前导 BOM:某些工具/重定向写出的 JSON 带 BOM,JSON.parse 会因此抛错。
|
|
268
283
|
let raw = readFileSync(abs, 'utf8');
|
|
@@ -436,7 +451,7 @@ function parsePrimary(c: Cursor, vars: VarStack): unknown {
|
|
|
436
451
|
eat(c);
|
|
437
452
|
const fn = BUILTINS[t.value];
|
|
438
453
|
if (!fn) throw new Error(`未知函数 "${t.value}"。白名单:${Object.keys(BUILTINS).join(', ')}`);
|
|
439
|
-
return fn(argv);
|
|
454
|
+
return fn(argv, cwdOf(vars));
|
|
440
455
|
}
|
|
441
456
|
// 字面常量
|
|
442
457
|
if (t.value === 'true') return true;
|
|
@@ -612,8 +627,11 @@ export function interpolateDeep<T>(
|
|
|
612
627
|
if (value && typeof value === 'object') {
|
|
613
628
|
const out: Record<string, unknown> = {};
|
|
614
629
|
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
615
|
-
// 注意:key 也插值(loop 里 `hook_${idx}` 这种),但只对字符串 key
|
|
630
|
+
// 注意:key 也插值(loop 里 `hook_${idx}` 这种),但只对字符串 key 有意义;
|
|
631
|
+
// 插值后的 key 若命中危险键(__proto__/constructor/prototype)→ 直接跳过不写,
|
|
632
|
+
// 防止经动态 key 注入原型链属性(与 VarStack 防线保持一致)。
|
|
616
633
|
const newKey = interpolate(k, vars, opts);
|
|
634
|
+
if (DANGEROUS_KEYS.has(newKey)) continue;
|
|
617
635
|
out[newKey] = interpolateDeep(v, vars, opts);
|
|
618
636
|
}
|
|
619
637
|
return out as unknown as T;
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* 不污染默认 T-A-O-R 路径。
|
|
11
11
|
* ============================================================
|
|
12
12
|
*/
|
|
13
|
+
import { buildBaseSystemPrompt, } from '../../../src/plugin-sdk.js';
|
|
13
14
|
import { findWorkflowByName, listWorkflows } from './loader.js';
|
|
14
15
|
import { runWorkflow } from './runner.js';
|
|
15
|
-
import { buildBaseSystemPrompt, } from '../../../src/plugin-sdk.js';
|
|
16
16
|
/**
|
|
17
17
|
* 把 "/workflow <name> [pos1 pos2 ...] [--input k=v ...]" 拆成 name + 显式 inputs + 位置参数。
|
|
18
18
|
* 简单解析器(不依赖第三方):
|
|
@@ -53,7 +53,20 @@ export function parseWorkflowArgs(rawArgs) {
|
|
|
53
53
|
}
|
|
54
54
|
return { name, inputs, positional };
|
|
55
55
|
}
|
|
56
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* shell 风格 token 化:空白切分 token;引号(' 或 ")在 token **任意位置**(不只开头)
|
|
58
|
+
* 出现即进入引号模式——引号内的空白不切分、引号字符本身被剥离。
|
|
59
|
+
*
|
|
60
|
+
* 修复:`--input topic="带空格的值"`(引号在 `=` 后,token 开头是 `t`)曾被按空白
|
|
61
|
+
* 硬切 → 值截断 + 引号残留(实测 inputs.topic 带 `"` 尾,positional 里散落被拆的字)。
|
|
62
|
+
* 现在 token 内任意位置的引号都正确分组。
|
|
63
|
+
*
|
|
64
|
+
* 边界语义(被 parseWorkflowArgs.test.ts 冻结):
|
|
65
|
+
* - 未闭合引号:剩余部分并入当前 token(不报错)
|
|
66
|
+
* - 双引号内的单引号是字面量(不触发嵌套)
|
|
67
|
+
* - 反斜杠不转义(`a\"b` 中 `"` 仍开启引号模式,行为变化但测试用 toBeDefined 容错)
|
|
68
|
+
* - 连续空白合并、`""` 产生空 token
|
|
69
|
+
*/
|
|
57
70
|
function tokenizeArgs(s) {
|
|
58
71
|
const out = [];
|
|
59
72
|
let i = 0;
|
|
@@ -62,24 +75,27 @@ function tokenizeArgs(s) {
|
|
|
62
75
|
i++;
|
|
63
76
|
if (i >= s.length)
|
|
64
77
|
break;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
i
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
let acc = '';
|
|
79
|
+
while (i < s.length) {
|
|
80
|
+
const c = s[i];
|
|
81
|
+
if (c === '"' || c === "'") {
|
|
82
|
+
// 引号模式:收集到闭合引号,引号内空白保留
|
|
83
|
+
const q = c;
|
|
84
|
+
i++;
|
|
85
|
+
while (i < s.length && s[i] !== q)
|
|
86
|
+
acc += s[i++];
|
|
87
|
+
if (i < s.length)
|
|
88
|
+
i++; // 跳过闭合引号
|
|
71
89
|
}
|
|
72
|
-
if (
|
|
90
|
+
else if (/\s/.test(c)) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
acc += c;
|
|
73
95
|
i++;
|
|
74
|
-
out.push(acc);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
let acc = '';
|
|
78
|
-
while (i < s.length && !/\s/.test(s[i])) {
|
|
79
|
-
acc += s[i++];
|
|
80
96
|
}
|
|
81
|
-
out.push(acc);
|
|
82
97
|
}
|
|
98
|
+
out.push(acc);
|
|
83
99
|
}
|
|
84
100
|
return out;
|
|
85
101
|
}
|
|
@@ -103,7 +119,7 @@ function coerceInputs(raw, defs) {
|
|
|
103
119
|
continue;
|
|
104
120
|
}
|
|
105
121
|
if (def.type === 'number') {
|
|
106
|
-
const n = parseFloat(rawVal);
|
|
122
|
+
const n = Number.parseFloat(rawVal);
|
|
107
123
|
if (Number.isNaN(n))
|
|
108
124
|
throw new Error(`input ${def.name} 必须是数字,收到 "${rawVal}"`);
|
|
109
125
|
out[def.name] = n;
|
|
@@ -167,12 +183,17 @@ export async function* runWorkflowFromCommand(rawArgs, opts) {
|
|
|
167
183
|
// - 默认:严格按"第 i 个位置参数 ↔ def.inputs[i]"对应。
|
|
168
184
|
// --input k=v 显式优先:已显式的 input 不被位置参数覆盖(对应位置参数被丢弃,会 warn)。
|
|
169
185
|
const overriddenByKV = [];
|
|
186
|
+
const greedyNotes = [];
|
|
170
187
|
const greedyFirst = def.inputs?.[0]?.greedy === true;
|
|
171
188
|
if (greedyFirst && def.inputs && positional.length > 0) {
|
|
172
189
|
const greedyName = def.inputs[0].name;
|
|
173
190
|
if (greedyName in rawInputs) {
|
|
174
|
-
//
|
|
175
|
-
|
|
191
|
+
// 首参已由 --input 提供时,位置参数仍按贪婪语义【拼回】首参末尾--
|
|
192
|
+
// 含空格的值未加引号时会被 shell 拆开(--input topic=A B C 只有 A 进 KV,
|
|
193
|
+
// B C 散落成位置参数),静默丢弃曾把整段中文调研主题截成半句、
|
|
194
|
+
// 连锁触发任务仲裁误判 new-task 清空全部产物。
|
|
195
|
+
rawInputs[greedyName] = [rawInputs[greedyName], ...positional].join(' ');
|
|
196
|
+
greedyNotes.push(`${greedyName}:--input 值未加引号被拆成 ${positional.length} 段位置参数,已自动拼回(下次请写 --input "${greedyName}=完整值")`);
|
|
176
197
|
}
|
|
177
198
|
else {
|
|
178
199
|
// 贪婪吸收:整句进首参(空格 join 还原用户原话,不逐位拆映射)
|
|
@@ -202,6 +223,13 @@ export async function* runWorkflowFromCommand(rawArgs, opts) {
|
|
|
202
223
|
message: `⚠ 忽略了 ${dropped.length} 个多余位置参数: ${dropped.map((d) => JSON.stringify(d)).join(' ')}(workflow "${name}" 只声明了 ${def.inputs.length} 个 input)`,
|
|
203
224
|
};
|
|
204
225
|
}
|
|
226
|
+
// warn:贪婪首参的 --input 值被 shell 拆开后自动拼回
|
|
227
|
+
if (greedyNotes.length > 0) {
|
|
228
|
+
yield {
|
|
229
|
+
type: 'warning',
|
|
230
|
+
message: `⚠ ${greedyNotes.join(';')}`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
205
233
|
// warn:位置参数被 --input 覆盖
|
|
206
234
|
if (overriddenByKV.length > 0) {
|
|
207
235
|
yield {
|
|
@@ -229,6 +257,8 @@ export async function* runWorkflowFromCommand(rawArgs, opts) {
|
|
|
229
257
|
transientContext: opts.transientContext,
|
|
230
258
|
transientBundle: opts.transientBundle,
|
|
231
259
|
promptCache: opts.promptCache,
|
|
260
|
+
executeTool: opts.executeTool,
|
|
261
|
+
toolContext: opts.toolContext,
|
|
232
262
|
};
|
|
233
263
|
// 透传 runWorkflow 的事件,并观察是否出现终结事件(error / interrupted = 失败或中断,
|
|
234
264
|
// 与 gateway broker 的 TERMINAL 判定同一套逻辑)。成功(没见到终结事件)则在末尾补一条
|
|
@@ -265,6 +295,6 @@ export async function* runWorkflowsList() {
|
|
|
265
295
|
lines.push(' /workflow <name> <val> 位置参数(按 inputs 声明顺序,第一个 input 直接写值)');
|
|
266
296
|
lines.push(' /workflow <name>.yaml <val> .yaml 后缀可省');
|
|
267
297
|
lines.push(' /workflow <name> --input k=v 显式 KV(多 input 或想跳过中间 input 时用)');
|
|
268
|
-
yield { type: 'text', delta: lines.join('\n')
|
|
298
|
+
yield { type: 'text', delta: `${lines.join('\n')}\n` };
|
|
269
299
|
yield { type: 'turn_done' };
|
|
270
300
|
}
|