minimal-agent 0.11.0 → 0.12.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 +4 -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/index.js +5 -3
- package/plugins/workflow-runner/src/index.ts +31 -13
- package/plugins/workflow-runner/src/multimodal.js +2 -1
- package/plugins/workflow-runner/src/multimodal.ts +2 -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 +68 -57
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +88 -66
- package/plugins/workflow-runner/src/toolRuntime.js +48 -0
- package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
- package/plugins/workflow-runner/src/types.ts +26 -7
- package/plugins/workflow-runner/test/context-files.test.ts +47 -16
- package/plugins/workflow-runner/test/hostCapability.test.ts +9 -3
- package/plugins/workflow-runner/test/multimodal.test.ts +61 -10
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
- package/src/cli/print.js +19 -4
- package/src/cli/print.ts +17 -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/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/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 +6 -2
- package/src/plugins/pluginRunner.ts +12 -7
- package/src/prompts/policyRegistry.js +27 -0
- package/src/prompts/policyRegistry.ts +27 -0
- 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/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 +31 -11
- package/src/tools/bash/bash.ts +29 -10
- 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 +116 -26
- package/src/tools/powershell/powershell.ts +155 -30
- package/src/tools/presentresult/presentresult.js +10 -0
- package/src/tools/presentresult/presentresult.ts +10 -0
- package/src/tools/read/read.js +244 -29
- package/src/tools/read/read.ts +391 -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 +26 -0
- package/src/tools/shared/permanentDeleteGuard.ts +34 -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/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 +12 -0
- package/src/ui/hooks/useChat.js +31 -11
- package/src/ui/hooks/useChat.ts +31 -13
- package/src/web/protocol.ts +78 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minimal-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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,11 @@
|
|
|
131
131
|
"prepublishOnly": "bun run clean && bun run build"
|
|
132
132
|
},
|
|
133
133
|
"dependencies": {
|
|
134
|
+
"diff": "^8.0.2",
|
|
134
135
|
"fast-glob": "^3.3.2",
|
|
135
136
|
"ink": "^5.0.1",
|
|
136
137
|
"marked": "^18.0.6",
|
|
138
|
+
"pdfjs-dist": "4.10.38",
|
|
137
139
|
"react": "^18.3.1",
|
|
138
140
|
"turndown": "^7.2.4",
|
|
139
141
|
"vscode-jsonrpc": "^8.2.1",
|
|
@@ -148,6 +150,7 @@
|
|
|
148
150
|
"typescript": "^5.5.0"
|
|
149
151
|
},
|
|
150
152
|
"optionalDependencies": {
|
|
153
|
+
"@napi-rs/canvas": "^0.1.100",
|
|
151
154
|
"playwright-core": "^1.40.0",
|
|
152
155
|
"pngjs": "^7.0.0",
|
|
153
156
|
"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
|
}
|
|
@@ -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
|
* 简单解析器(不依赖第三方):
|
|
@@ -103,7 +103,7 @@ function coerceInputs(raw, defs) {
|
|
|
103
103
|
continue;
|
|
104
104
|
}
|
|
105
105
|
if (def.type === 'number') {
|
|
106
|
-
const n = parseFloat(rawVal);
|
|
106
|
+
const n = Number.parseFloat(rawVal);
|
|
107
107
|
if (Number.isNaN(n))
|
|
108
108
|
throw new Error(`input ${def.name} 必须是数字,收到 "${rawVal}"`);
|
|
109
109
|
out[def.name] = n;
|
|
@@ -229,6 +229,8 @@ export async function* runWorkflowFromCommand(rawArgs, opts) {
|
|
|
229
229
|
transientContext: opts.transientContext,
|
|
230
230
|
transientBundle: opts.transientBundle,
|
|
231
231
|
promptCache: opts.promptCache,
|
|
232
|
+
executeTool: opts.executeTool,
|
|
233
|
+
toolContext: opts.toolContext,
|
|
232
234
|
};
|
|
233
235
|
// 透传 runWorkflow 的事件,并观察是否出现终结事件(error / interrupted = 失败或中断,
|
|
234
236
|
// 与 gateway broker 的 TERMINAL 判定同一套逻辑)。成功(没见到终结事件)则在末尾补一条
|
|
@@ -265,6 +267,6 @@ export async function* runWorkflowsList() {
|
|
|
265
267
|
lines.push(' /workflow <name> <val> 位置参数(按 inputs 声明顺序,第一个 input 直接写值)');
|
|
266
268
|
lines.push(' /workflow <name>.yaml <val> .yaml 后缀可省');
|
|
267
269
|
lines.push(' /workflow <name> --input k=v 显式 KV(多 input 或想跳过中间 input 时用)');
|
|
268
|
-
yield { type: 'text', delta: lines.join('\n')
|
|
270
|
+
yield { type: 'text', delta: `${lines.join('\n')}\n` };
|
|
269
271
|
yield { type: 'turn_done' };
|
|
270
272
|
}
|
|
@@ -11,16 +11,17 @@
|
|
|
11
11
|
* ============================================================
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { findWorkflowByName, listWorkflows } from './loader.js';
|
|
15
|
-
import { runWorkflow } from './runner.js';
|
|
16
|
-
import type { InputDef, RunContext, WorkflowEvent } from './types.js';
|
|
17
14
|
import {
|
|
18
|
-
buildBaseSystemPrompt,
|
|
19
15
|
type LoopEvent,
|
|
20
16
|
type Message,
|
|
17
|
+
type PluginContext,
|
|
21
18
|
type Provider,
|
|
22
19
|
type Tool,
|
|
20
|
+
buildBaseSystemPrompt,
|
|
23
21
|
} from '../../../src/plugin-sdk.js';
|
|
22
|
+
import { findWorkflowByName, listWorkflows } from './loader.js';
|
|
23
|
+
import { runWorkflow } from './runner.js';
|
|
24
|
+
import type { InputDef, RunContext, WorkflowEvent } from './types.js';
|
|
24
25
|
|
|
25
26
|
export interface RunWorkflowFromCommandOptions {
|
|
26
27
|
provider: Provider;
|
|
@@ -31,11 +32,13 @@ export interface RunWorkflowFromCommandOptions {
|
|
|
31
32
|
sessionState?: import('../../../src/plugin-sdk.js').SessionState;
|
|
32
33
|
/** 当前工作目录(默认 process.cwd()) */
|
|
33
34
|
cwd?: string;
|
|
34
|
-
/**
|
|
35
|
+
/** 宿主过滤后的可用工具列表;缺省为空,绝不在 runner 内恢复全量工具。 */
|
|
35
36
|
tools?: Tool[];
|
|
36
37
|
transientContext?: string;
|
|
37
38
|
transientBundle?: import('../../../src/plugin-sdk.js').ContextBundle;
|
|
38
39
|
promptCache?: import('../../../src/plugin-sdk.js').PromptCacheContext;
|
|
40
|
+
executeTool?: PluginContext['executeTool'];
|
|
41
|
+
toolContext?: RunContext['toolContext'];
|
|
39
42
|
/**
|
|
40
43
|
* 宿主授权上下文(浏览器 / 电脑操控)。
|
|
41
44
|
*
|
|
@@ -138,8 +141,9 @@ function coerceInputs(
|
|
|
138
141
|
continue;
|
|
139
142
|
}
|
|
140
143
|
if (def.type === 'number') {
|
|
141
|
-
const n = parseFloat(rawVal);
|
|
142
|
-
if (Number.isNaN(n))
|
|
144
|
+
const n = Number.parseFloat(rawVal);
|
|
145
|
+
if (Number.isNaN(n))
|
|
146
|
+
throw new Error(`input ${def.name} 必须是数字,收到 "${rawVal}"`);
|
|
143
147
|
out[def.name] = n;
|
|
144
148
|
} else if (def.type === 'enum') {
|
|
145
149
|
if (!def.values?.includes(rawVal)) {
|
|
@@ -167,7 +171,9 @@ async function refreshWorkflowSystemPrompt(
|
|
|
167
171
|
// 只刷稳定层:workflow 一跑就是几十步,system 消息若含日期/记忆等易变内容,
|
|
168
172
|
// 每步都会重写 history[0]、把 provider 前缀缓存打散(见 prompts/system.ts)。
|
|
169
173
|
const content = await buildBaseSystemPrompt(cwd, tools);
|
|
170
|
-
const systemIndex = history.findIndex(
|
|
174
|
+
const systemIndex = history.findIndex(
|
|
175
|
+
(m) => m.role === 'system' && !m._ephemeral,
|
|
176
|
+
);
|
|
171
177
|
const fresh: Message = { role: 'system', content };
|
|
172
178
|
|
|
173
179
|
if (systemIndex === 0) {
|
|
@@ -278,6 +284,8 @@ export async function* runWorkflowFromCommand(
|
|
|
278
284
|
transientContext: opts.transientContext,
|
|
279
285
|
transientBundle: opts.transientBundle,
|
|
280
286
|
promptCache: opts.promptCache,
|
|
287
|
+
executeTool: opts.executeTool,
|
|
288
|
+
toolContext: opts.toolContext,
|
|
281
289
|
};
|
|
282
290
|
|
|
283
291
|
// 透传 runWorkflow 的事件,并观察是否出现终结事件(error / interrupted = 失败或中断,
|
|
@@ -295,7 +303,11 @@ export async function* runWorkflowFromCommand(
|
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
/** /workflows —— 列出可用 workflow。yield 一条 text 事件让 UI 显示。 */
|
|
298
|
-
export async function* runWorkflowsList(): AsyncGenerator<
|
|
306
|
+
export async function* runWorkflowsList(): AsyncGenerator<
|
|
307
|
+
LoopEvent,
|
|
308
|
+
void,
|
|
309
|
+
void
|
|
310
|
+
> {
|
|
299
311
|
const list = await listWorkflows();
|
|
300
312
|
if (list.length === 0) {
|
|
301
313
|
yield {
|
|
@@ -308,13 +320,19 @@ export async function* runWorkflowsList(): AsyncGenerator<LoopEvent, void, void>
|
|
|
308
320
|
}
|
|
309
321
|
const lines = ['可用工作流:'];
|
|
310
322
|
for (const w of list) {
|
|
311
|
-
lines.push(
|
|
323
|
+
lines.push(
|
|
324
|
+
` /workflow ${w.name} — ${w.description.split('\n')[0].trim()}`,
|
|
325
|
+
);
|
|
312
326
|
}
|
|
313
327
|
lines.push('');
|
|
314
328
|
lines.push('运行示例:');
|
|
315
|
-
lines.push(
|
|
329
|
+
lines.push(
|
|
330
|
+
' /workflow <name> <val> 位置参数(按 inputs 声明顺序,第一个 input 直接写值)',
|
|
331
|
+
);
|
|
316
332
|
lines.push(' /workflow <name>.yaml <val> .yaml 后缀可省');
|
|
317
|
-
lines.push(
|
|
318
|
-
|
|
333
|
+
lines.push(
|
|
334
|
+
' /workflow <name> --input k=v 显式 KV(多 input 或想跳过中间 input 时用)',
|
|
335
|
+
);
|
|
336
|
+
yield { type: 'text', delta: `${lines.join('\n')}\n` };
|
|
319
337
|
yield { type: 'turn_done' };
|
|
320
338
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isAbsolute, resolve } from 'node:path';
|
|
2
2
|
import { interpolate } from './expressions.js';
|
|
3
|
+
import { effectiveWorkflowCwd } from './toolRuntime.js';
|
|
3
4
|
export function resolveWorkflowMultimodalOptions(step, vars, ctx, onMissing) {
|
|
4
5
|
const imageDetail = step.image_detail;
|
|
5
6
|
const imageMaxLongSidePixel = step.image_max_long_side_pixel;
|
|
@@ -33,5 +34,5 @@ function resolveWorkflowImageSource(source, ctx) {
|
|
|
33
34
|
isAbsolute(clean)) {
|
|
34
35
|
return clean;
|
|
35
36
|
}
|
|
36
|
-
return resolve(ctx
|
|
37
|
+
return resolve(effectiveWorkflowCwd(ctx), clean);
|
|
37
38
|
}
|
|
@@ -2,6 +2,7 @@ import { isAbsolute, resolve } from 'node:path';
|
|
|
2
2
|
|
|
3
3
|
import type { ImageDetail, ImageInputSpec } from '../../../src/plugin-sdk.js';
|
|
4
4
|
import { interpolate } from './expressions.js';
|
|
5
|
+
import { effectiveWorkflowCwd } from './toolRuntime.js';
|
|
5
6
|
import type { RunContext, StepDef, VarStack } from './types.js';
|
|
6
7
|
|
|
7
8
|
export interface WorkflowMultimodalOptions {
|
|
@@ -59,5 +60,5 @@ function resolveWorkflowImageSource(source: string, ctx: RunContext): string {
|
|
|
59
60
|
) {
|
|
60
61
|
return clean;
|
|
61
62
|
}
|
|
62
|
-
return resolve(ctx
|
|
63
|
+
return resolve(effectiveWorkflowCwd(ctx), clean);
|
|
63
64
|
}
|
|
@@ -15,11 +15,12 @@
|
|
|
15
15
|
* capture:{ text: var_name } / { result: var_name }
|
|
16
16
|
* ============================================================
|
|
17
17
|
*/
|
|
18
|
-
import {
|
|
18
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
19
19
|
import { join } from 'node:path';
|
|
20
|
-
import { getResourceSearchPaths } from '../../../../src/plugin-sdk.js';
|
|
20
|
+
import { getResourceSearchPaths, } from '../../../../src/plugin-sdk.js';
|
|
21
21
|
import { describeLlmFailure, describeMaxTurnsCutoff, effectiveGoalAnchor, runStepQuery, } from '../agentCommon.js';
|
|
22
22
|
import { interpolate } from '../expressions.js';
|
|
23
|
+
import { effectiveWorkflowCwd } from '../toolRuntime.js';
|
|
23
24
|
function stripFrontmatter(content) {
|
|
24
25
|
return content.replace(/^---\n[\s\S]*?\n---\n?/, '');
|
|
25
26
|
}
|
|
@@ -53,9 +54,7 @@ async function findSkillFile(skillName, cwd) {
|
|
|
53
54
|
if (parseFrontmatterName(md) === skillName)
|
|
54
55
|
return skillFile;
|
|
55
56
|
}
|
|
56
|
-
catch {
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
57
|
+
catch { }
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
return null;
|
|
@@ -64,7 +63,7 @@ export async function* execSkillStep(step, vars, ctx) {
|
|
|
64
63
|
if (!step.skill)
|
|
65
64
|
throw new Error(`step ${step.id}: 缺少 skill 字段`);
|
|
66
65
|
const skillName = interpolate(step.skill, vars);
|
|
67
|
-
const skillFile = await findSkillFile(skillName, ctx
|
|
66
|
+
const skillFile = await findSkillFile(skillName, effectiveWorkflowCwd(ctx));
|
|
68
67
|
if (!skillFile) {
|
|
69
68
|
throw new Error(`step ${step.id}: 未找到 skill "${skillName}"(cwd/skills/ 与 packageRoot/skills/ 双源均未命中)`);
|
|
70
69
|
}
|
|
@@ -16,10 +16,13 @@
|
|
|
16
16
|
* ============================================================
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
type LoopEvent,
|
|
24
|
+
getResourceSearchPaths,
|
|
25
|
+
} from '../../../../src/plugin-sdk.js';
|
|
23
26
|
import {
|
|
24
27
|
describeLlmFailure,
|
|
25
28
|
describeMaxTurnsCutoff,
|
|
@@ -27,6 +30,7 @@ import {
|
|
|
27
30
|
runStepQuery,
|
|
28
31
|
} from '../agentCommon.js';
|
|
29
32
|
import { interpolate } from '../expressions.js';
|
|
33
|
+
import { effectiveWorkflowCwd } from '../toolRuntime.js';
|
|
30
34
|
import type { RunContext, StepDef, StepResult, VarStack } from '../types.js';
|
|
31
35
|
|
|
32
36
|
function stripFrontmatter(content: string): string {
|
|
@@ -41,7 +45,10 @@ function parseFrontmatterName(content: string): string | null {
|
|
|
41
45
|
return nm[1].trim().replace(/^["']|["']$/g, '');
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
async function findSkillFile(
|
|
48
|
+
async function findSkillFile(
|
|
49
|
+
skillName: string,
|
|
50
|
+
cwd: string,
|
|
51
|
+
): Promise<string | null> {
|
|
45
52
|
const roots = getResourceSearchPaths('skills', import.meta.url, cwd);
|
|
46
53
|
|
|
47
54
|
for (const root of roots) {
|
|
@@ -58,9 +65,7 @@ async function findSkillFile(skillName: string, cwd: string): Promise<string | n
|
|
|
58
65
|
try {
|
|
59
66
|
const md = await readFile(skillFile, 'utf8');
|
|
60
67
|
if (parseFrontmatterName(md) === skillName) return skillFile;
|
|
61
|
-
} catch {
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
68
|
+
} catch {}
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
return null;
|
|
@@ -74,7 +79,7 @@ export async function* execSkillStep(
|
|
|
74
79
|
if (!step.skill) throw new Error(`step ${step.id}: 缺少 skill 字段`);
|
|
75
80
|
const skillName = interpolate(step.skill, vars);
|
|
76
81
|
|
|
77
|
-
const skillFile = await findSkillFile(skillName, ctx
|
|
82
|
+
const skillFile = await findSkillFile(skillName, effectiveWorkflowCwd(ctx));
|
|
78
83
|
if (!skillFile) {
|
|
79
84
|
throw new Error(
|
|
80
85
|
`step ${step.id}: 未找到 skill "${skillName}"(cwd/skills/ 与 packageRoot/skills/ 双源均未命中)`,
|