@meiyukichan/dsh-sandore 0.1.0-rc.6
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/LICENSE +201 -0
- package/cordis.patch.yml +39 -0
- package/dsh.plugin.json +16 -0
- package/lib/client/CommandEditor.d.ts +19 -0
- package/lib/client/DagEditor.d.ts +22 -0
- package/lib/client/DefinitionDetail.d.ts +13 -0
- package/lib/client/DefinitionList.d.ts +19 -0
- package/lib/client/DiscussionInput.d.ts +63 -0
- package/lib/client/DragHandle.d.ts +16 -0
- package/lib/client/EditorPane.d.ts +13 -0
- package/lib/client/FlowEditor.d.ts +31 -0
- package/lib/client/InstanceList.d.ts +24 -0
- package/lib/client/NewInstanceDialog.d.ts +19 -0
- package/lib/client/PreviewPanel.d.ts +13 -0
- package/lib/client/RunView.d.ts +79 -0
- package/lib/client/StepChatPanel.d.ts +34 -0
- package/lib/client/TreeNav.d.ts +27 -0
- package/lib/client/UnifiedTimeline.d.ts +11 -0
- package/lib/client/WorkflowButton.d.ts +11 -0
- package/lib/client/WorkflowList.d.ts +18 -0
- package/lib/client/WorkflowOverlay.d.ts +6 -0
- package/lib/client/chat/AssistantMarkdown.d.ts +22 -0
- package/lib/client/chat/CompactionItem.d.ts +24 -0
- package/lib/client/chat/ContextBody.d.ts +136 -0
- package/lib/client/chat/ContextInjectionRow.d.ts +25 -0
- package/lib/client/chat/GenericCommandCard.d.ts +10 -0
- package/lib/client/chat/MessageIconActions.d.ts +41 -0
- package/lib/client/chat/MessageItem.d.ts +65 -0
- package/lib/client/chat/ReasoningRow.d.ts +13 -0
- package/lib/client/chat/TurnTailNodeView.d.ts +29 -0
- package/lib/client/chat/UserMessageRow.d.ts +18 -0
- package/lib/client/chat/attachment/ImageLightbox.d.ts +26 -0
- package/lib/client/chat/attachment/MessageImage.d.ts +56 -0
- package/lib/client/chat/attachment/image-attachment.d.ts +41 -0
- package/lib/client/chat/attachment/image-labels.d.ts +21 -0
- package/lib/client/chat/message-chrome.d.ts +51 -0
- package/lib/client/chat/reference/ReferenceIcon.d.ts +20 -0
- package/lib/client/chat/turn-assistant.d.ts +12 -0
- package/lib/client/chat/turn-metrics.d.ts +42 -0
- package/lib/client/chat/use-calendar-day.d.ts +5 -0
- package/lib/client/chat/use-throttled-visual-update.d.ts +7 -0
- package/lib/client/chat-flow.d.ts +110 -0
- package/lib/client/composer-selects.d.ts +17 -0
- package/lib/client/dag-editor-model.d.ts +141 -0
- package/lib/client/describe-error.d.ts +9 -0
- package/lib/client/index.d.ts +6 -0
- package/lib/client/locales.d.ts +777 -0
- package/lib/client/model-faces.d.ts +156 -0
- package/lib/client/open-discussion.d.ts +24 -0
- package/lib/client/panel-model.d.ts +34 -0
- package/lib/client/remote.d.ts +34 -0
- package/lib/client/run-view-model.d.ts +304 -0
- package/lib/client/sandore-save.d.ts +41 -0
- package/lib/client/sandore-sse.d.ts +15 -0
- package/lib/client/step-chat-retry.d.ts +44 -0
- package/lib/client/tool/GenericToolCard.d.ts +28 -0
- package/lib/client/tool/ToolRow.d.ts +95 -0
- package/lib/client/tool/diff-card-model.d.ts +63 -0
- package/lib/client/tool/read-card-model.d.ts +67 -0
- package/lib/client/tool/search-card-model.d.ts +94 -0
- package/lib/client/tool/terminal-card-model.d.ts +86 -0
- package/lib/client/tool/tool-call-model.d.ts +74 -0
- package/lib/client/tool/web-card-model.d.ts +44 -0
- package/lib/client/use-focus-refresh.d.ts +1 -0
- package/lib/client/workflow-face.d.ts +68 -0
- package/lib/client/workflow-store.d.ts +80 -0
- package/lib/client/workspace-path.d.ts +15 -0
- package/lib/client.js +38389 -0
- package/lib/index.js +8854 -0
- package/lib/microflow/branch-evaluator.d.ts +290 -0
- package/lib/microflow/concurrency-limiter.d.ts +39 -0
- package/lib/microflow/errors.d.ts +39 -0
- package/lib/microflow/file-utils.d.ts +210 -0
- package/lib/microflow/finalize-manager.d.ts +414 -0
- package/lib/microflow/fork-session.d.ts +151 -0
- package/lib/microflow/index.d.ts +17 -0
- package/lib/microflow/internal.d.ts +56 -0
- package/lib/microflow/loop-manager.d.ts +127 -0
- package/lib/microflow/rules-resolver.d.ts +28 -0
- package/lib/microflow/stage-executor.d.ts +362 -0
- package/lib/microflow/step-navigator.d.ts +108 -0
- package/lib/microflow/step-runner.d.ts +190 -0
- package/lib/microflow/subagents.d.ts +380 -0
- package/lib/tool/index.d.ts +65 -0
- package/lib/typert.host.d.ts +16 -0
- package/lib/typert.host.js +25 -0
- package/lib/typert.remote-client.d.ts +114 -0
- package/lib/typert.remote-client.js +399 -0
- package/lib/workflow/ask-tool.d.ts +78 -0
- package/lib/workflow/dag-navigator.d.ts +136 -0
- package/lib/workflow/domain.d.ts +304 -0
- package/lib/workflow/engine-surface.d.ts +70 -0
- package/lib/workflow/errors.d.ts +13 -0
- package/lib/workflow/host-agent.d.ts +53 -0
- package/lib/workflow/index.d.ts +23 -0
- package/lib/workflow/interaction-manager.d.ts +170 -0
- package/lib/workflow/lifecycle-manager.d.ts +111 -0
- package/lib/workflow/persistence.d.ts +35 -0
- package/lib/workflow/plugin.d.ts +27 -0
- package/lib/workflow/reactive-state.d.ts +27 -0
- package/lib/workflow/recovery-manager.d.ts +158 -0
- package/lib/workflow/remote.d.ts +139 -0
- package/lib/workflow/schema.d.ts +1917 -0
- package/lib/workflow/seam.d.ts +254 -0
- package/lib/workflow/shell.d.ts +127 -0
- package/lib/workflow/sse.d.ts +16 -0
- package/lib/workflow/typert/typert.host.d.ts +16 -0
- package/lib/workflow/typert/typert.remote-client.d.ts +114 -0
- package/lib/workflow/types.d.ts +187 -0
- package/lib/workflow/wire.d.ts +256 -0
- package/lib/workflow/workflow-engine.d.ts +373 -0
- package/lib/workflow/yaml-error.d.ts +22 -0
- package/lib/workflow/yaml-parser.d.ts +23 -0
- package/package.json +140 -0
- package/packages/client/CommandEditor.tsx +73 -0
- package/packages/client/DagEditor.module.css +711 -0
- package/packages/client/DagEditor.tsx +1203 -0
- package/packages/client/DefinitionDetail.module.css +110 -0
- package/packages/client/DefinitionDetail.tsx +126 -0
- package/packages/client/DefinitionList.tsx +173 -0
- package/packages/client/DiscussionInput.tsx +763 -0
- package/packages/client/DragHandle.tsx +92 -0
- package/packages/client/EditorPane.tsx +70 -0
- package/packages/client/FlowEditor.tsx +227 -0
- package/packages/client/InstanceList.tsx +250 -0
- package/packages/client/NewInstanceDialog.module.css +244 -0
- package/packages/client/NewInstanceDialog.tsx +478 -0
- package/packages/client/PreviewPanel.tsx +55 -0
- package/packages/client/RunView.module.css +246 -0
- package/packages/client/RunView.tsx +314 -0
- package/packages/client/StepChatPanel.tsx +486 -0
- package/packages/client/TreeNav.tsx +294 -0
- package/packages/client/UnifiedTimeline.tsx +484 -0
- package/packages/client/WorkflowButton.module.css +72 -0
- package/packages/client/WorkflowButton.tsx +50 -0
- package/packages/client/WorkflowList.tsx +158 -0
- package/packages/client/WorkflowOverlay.module.css +390 -0
- package/packages/client/WorkflowOverlay.tsx +136 -0
- package/packages/client/WorkflowPanel.module.css +247 -0
- package/packages/client/chat/AssistantMarkdown.module.css +38 -0
- package/packages/client/chat/AssistantMarkdown.tsx +120 -0
- package/packages/client/chat/ChatView.module.css +198 -0
- package/packages/client/chat/CompactionItem.tsx +85 -0
- package/packages/client/chat/ContextBody.module.css +162 -0
- package/packages/client/chat/ContextBody.tsx +598 -0
- package/packages/client/chat/ContextInjectionRow.module.css +63 -0
- package/packages/client/chat/ContextInjectionRow.tsx +78 -0
- package/packages/client/chat/GenericCommandCard.module.css +87 -0
- package/packages/client/chat/GenericCommandCard.tsx +76 -0
- package/packages/client/chat/MessageIconActions.module.css +84 -0
- package/packages/client/chat/MessageIconActions.tsx +147 -0
- package/packages/client/chat/MessageItem.module.css +293 -0
- package/packages/client/chat/MessageItem.tsx +384 -0
- package/packages/client/chat/ReasoningRow.module.css +82 -0
- package/packages/client/chat/ReasoningRow.tsx +70 -0
- package/packages/client/chat/TurnTailNodeView.module.css +10 -0
- package/packages/client/chat/TurnTailNodeView.tsx +63 -0
- package/packages/client/chat/UserMessageRow.module.css +75 -0
- package/packages/client/chat/UserMessageRow.tsx +92 -0
- package/packages/client/chat/accessibility.module.css +9 -0
- package/packages/client/chat/attachment/ImageLightbox.module.css +45 -0
- package/packages/client/chat/attachment/ImageLightbox.tsx +70 -0
- package/packages/client/chat/attachment/MessageImage.module.css +70 -0
- package/packages/client/chat/attachment/MessageImage.tsx +136 -0
- package/packages/client/chat/attachment/image-attachment.ts +98 -0
- package/packages/client/chat/attachment/image-labels.ts +34 -0
- package/packages/client/chat/message-chrome.ts +96 -0
- package/packages/client/chat/reference/ReferenceIcon.tsx +40 -0
- package/packages/client/chat/turn-assistant.ts +15 -0
- package/packages/client/chat/turn-metrics.ts +102 -0
- package/packages/client/chat/use-calendar-day.ts +27 -0
- package/packages/client/chat/use-throttled-visual-update.ts +44 -0
- package/packages/client/chat-flow.spec.ts +357 -0
- package/packages/client/chat-flow.ts +278 -0
- package/packages/client/composer-selects.module.css +376 -0
- package/packages/client/composer-selects.tsx +531 -0
- package/packages/client/dag-editor-model.spec.ts +173 -0
- package/packages/client/dag-editor-model.ts +359 -0
- package/packages/client/describe-error.spec.ts +18 -0
- package/packages/client/describe-error.ts +12 -0
- package/packages/client/index.ts +144 -0
- package/packages/client/input.module.css +854 -0
- package/packages/client/is-conditional-check.spec.ts +34 -0
- package/packages/client/locales.ts +789 -0
- package/packages/client/model-faces.ts +225 -0
- package/packages/client/open-discussion.spec.ts +39 -0
- package/packages/client/open-discussion.ts +32 -0
- package/packages/client/panel-model.spec.ts +152 -0
- package/packages/client/panel-model.ts +72 -0
- package/packages/client/preview.module.css +95 -0
- package/packages/client/react-dom.d.ts +11 -0
- package/packages/client/reactflow-theme.css +91 -0
- package/packages/client/remote.spec.ts +41 -0
- package/packages/client/remote.ts +46 -0
- package/packages/client/run-view-model.spec.ts +641 -0
- package/packages/client/run-view-model.ts +636 -0
- package/packages/client/sandore-save.spec.ts +146 -0
- package/packages/client/sandore-save.ts +97 -0
- package/packages/client/sandore-sse.ts +59 -0
- package/packages/client/step-chat-retry.spec.ts +70 -0
- package/packages/client/step-chat-retry.ts +65 -0
- package/packages/client/timeline.module.css +503 -0
- package/packages/client/tool/GenericToolCard.tsx +99 -0
- package/packages/client/tool/ToolRow.module.css +308 -0
- package/packages/client/tool/ToolRow.tsx +328 -0
- package/packages/client/tool/diff-card-model.ts +103 -0
- package/packages/client/tool/read-card-model.ts +86 -0
- package/packages/client/tool/search-card-model.ts +166 -0
- package/packages/client/tool/terminal-card-model.ts +225 -0
- package/packages/client/tool/tool-call-model.ts +267 -0
- package/packages/client/tool/web-card-model.ts +80 -0
- package/packages/client/tree-nav.module.css +282 -0
- package/packages/client/use-focus-refresh.ts +23 -0
- package/packages/client/workflow-face.ts +72 -0
- package/packages/client/workflow-store.spec.ts +49 -0
- package/packages/client/workflow-store.ts +161 -0
- package/packages/client/workspace-path.ts +27 -0
- package/packages/css-modules.d.ts +6 -0
- package/packages/microflow/branch-evaluator.spec.ts +745 -0
- package/packages/microflow/branch-evaluator.ts +1037 -0
- package/packages/microflow/concurrency-limiter.ts +70 -0
- package/packages/microflow/errors.ts +64 -0
- package/packages/microflow/file-utils.spec.ts +604 -0
- package/packages/microflow/file-utils.ts +521 -0
- package/packages/microflow/finalize-manager.spec.ts +1977 -0
- package/packages/microflow/finalize-manager.ts +1464 -0
- package/packages/microflow/fork-session.spec.ts +184 -0
- package/packages/microflow/fork-session.ts +316 -0
- package/packages/microflow/index.ts +17 -0
- package/packages/microflow/internal.ts +166 -0
- package/packages/microflow/loop-manager.spec.ts +294 -0
- package/packages/microflow/loop-manager.ts +255 -0
- package/packages/microflow/rules-resolver.spec.ts +147 -0
- package/packages/microflow/rules-resolver.ts +58 -0
- package/packages/microflow/stage-executor.spec.ts +1577 -0
- package/packages/microflow/stage-executor.ts +1478 -0
- package/packages/microflow/step-navigator.spec.ts +301 -0
- package/packages/microflow/step-navigator.ts +300 -0
- package/packages/microflow/step-runner.spec.ts +1132 -0
- package/packages/microflow/step-runner.ts +752 -0
- package/packages/microflow/subagents.spec.ts +215 -0
- package/packages/microflow/subagents.ts +422 -0
- package/packages/tests/fixtures/test-workflow.yaml +128 -0
- package/packages/tests/mocks/agents.ts +206 -0
- package/packages/tests/mocks/llm.ts +46 -0
- package/packages/tests/mocks/session-title.ts +31 -0
- package/packages/tests/mocks/sessions.ts +131 -0
- package/packages/tests/mocks/shell.ts +60 -0
- package/packages/tests/mocks/storage-domain.ts +101 -0
- package/packages/tests/mocks/subagents.ts +197 -0
- package/packages/tests/mocks/subprocess.ts +59 -0
- package/packages/tests/mocks/user-questions.ts +33 -0
- package/packages/tests/mocks/workspace.ts +52 -0
- package/packages/tool/index.spec.ts +287 -0
- package/packages/tool/index.ts +348 -0
- package/packages/workflow/ask-tool.ts +173 -0
- package/packages/workflow/dag-navigator.spec.ts +236 -0
- package/packages/workflow/dag-navigator.ts +425 -0
- package/packages/workflow/domain.ts +26 -0
- package/packages/workflow/engine-surface.ts +71 -0
- package/packages/workflow/errors.ts +13 -0
- package/packages/workflow/host-agent.spec.ts +92 -0
- package/packages/workflow/host-agent.ts +62 -0
- package/packages/workflow/index.ts +25 -0
- package/packages/workflow/interaction-manager.spec.ts +390 -0
- package/packages/workflow/interaction-manager.ts +397 -0
- package/packages/workflow/lifecycle-manager.spec.ts +683 -0
- package/packages/workflow/lifecycle-manager.ts +288 -0
- package/packages/workflow/persistence.spec.ts +109 -0
- package/packages/workflow/persistence.ts +64 -0
- package/packages/workflow/plugin.ts +39 -0
- package/packages/workflow/reactive-state.spec.ts +69 -0
- package/packages/workflow/reactive-state.ts +52 -0
- package/packages/workflow/recovery-manager.spec.ts +606 -0
- package/packages/workflow/recovery-manager.ts +471 -0
- package/packages/workflow/remote.spec.ts +1045 -0
- package/packages/workflow/remote.ts +479 -0
- package/packages/workflow/schema.spec.ts +303 -0
- package/packages/workflow/schema.ts +562 -0
- package/packages/workflow/seam.ts +293 -0
- package/packages/workflow/shell.ts +142 -0
- package/packages/workflow/sse.ts +110 -0
- package/packages/workflow/typert/typert.host.d.ts +16 -0
- package/packages/workflow/typert/typert.host.js +25 -0
- package/packages/workflow/typert/typert.remote-client.d.ts +114 -0
- package/packages/workflow/typert/typert.remote-client.js +399 -0
- package/packages/workflow/types.ts +258 -0
- package/packages/workflow/wire.ts +267 -0
- package/packages/workflow/workflow-engine-e2e.spec.ts +262 -0
- package/packages/workflow/workflow-engine-real-e2e.spec.ts +922 -0
- package/packages/workflow/workflow-engine.spec.ts +4389 -0
- package/packages/workflow/workflow-engine.ts +2133 -0
- package/packages/workflow/yaml-error.ts +29 -0
- package/packages/workflow/yaml-parser.spec.ts +757 -0
- package/packages/workflow/yaml-parser.ts +287 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 单 Step 执行器(shell / prompt / agent / skill / mcp)。
|
|
3
|
+
*
|
|
4
|
+
* shell Step 走 `ctx.shell.resolve(request)` → `ctx.shell.run(spec)`(简单命令,
|
|
5
|
+
* 无 stdin/env 注入需求);存在 stdin(`inputFrom` 解析到内容)或非空透传变量
|
|
6
|
+
* 注入需求时改走 `ctx.subprocess.spawn()`(`stdin: { data }` 写 JSON、`env` 合并)。
|
|
7
|
+
* prompt/agent/skill/mcp 四类 AI 会话统一走可续接子代理(设计决策 D3:
|
|
8
|
+
* `ctx.subagents.startContinuable` → `ctx.agents.get(childId)` → settle
|
|
9
|
+
* (`awaitSubagentSettlement` 对齐 DSH `subagent/end`)→ final output)——prompt
|
|
10
|
+
* 与 agent/skill/mcp 行为对齐(真实子代理会话:可暂停/恢复/重试/会话可点;提问经
|
|
11
|
+
* workflow 层注入的 child-scope `ask_user_question` 工具,见 workflow/ask-tool.ts)。
|
|
12
|
+
* agent 定义 `agentParamsTemplate` 时先 `fillParams` 临时会话填充
|
|
13
|
+
* 「### 当前Step参数」子节。所有提示词/命令构建前先 `replacePassThroughVars`
|
|
14
|
+
* 替换 `${xxx}` 透传变量(含循环上下文变量)。
|
|
15
|
+
* @module dsh-sandore/microflow/src/step-runner
|
|
16
|
+
*/
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
19
|
+
|
|
20
|
+
import { SandoreError } from './errors.ts';
|
|
21
|
+
import { idMatches, replacePassThroughVars } from './file-utils.ts';
|
|
22
|
+
import {
|
|
23
|
+
buildShellEnv,
|
|
24
|
+
describeError,
|
|
25
|
+
escapeXml,
|
|
26
|
+
escapeXmlAttr,
|
|
27
|
+
resolvePassThroughVars,
|
|
28
|
+
resolveProviderModel as resolveModelRoute,
|
|
29
|
+
textSummary,
|
|
30
|
+
} from './internal.ts';
|
|
31
|
+
import { finalAssistantOutput, collectSessionMessages } from './subagents.ts';
|
|
32
|
+
import type { Agent, SubagentSettlement } from './subagents.ts';
|
|
33
|
+
import type { CommandConfig, SessionMessage, WorkflowStep } from '../workflow/schema';
|
|
34
|
+
import type { ShellExecRequest, ShellRunResult } from '../workflow/shell';
|
|
35
|
+
|
|
36
|
+
type ShellCommand = Extract<CommandConfig, { type: 'shell' }>;
|
|
37
|
+
type PromptCommand = Extract<CommandConfig, { type: 'prompt' }>;
|
|
38
|
+
type AgentCommand = Extract<CommandConfig, { type: 'agent' }>;
|
|
39
|
+
type SkillCommand = Extract<CommandConfig, { type: 'skill' }>;
|
|
40
|
+
type McpCommand = Extract<CommandConfig, { type: 'mcp' }>;
|
|
41
|
+
|
|
42
|
+
/** 走可续接子代理的命令类型(agent/skill/mcp/prompt;仅 shell 除外)。 */
|
|
43
|
+
type SubagentCommand = AgentCommand | SkillCommand | McpCommand;
|
|
44
|
+
|
|
45
|
+
/** 可续接子代理命令的 type 判别值(`collectSubagentOutput`/`resumeSubagent` 的 commandType 取值域)。 */
|
|
46
|
+
type SubagentCommandType = 'agent' | 'skill' | 'mcp' | 'prompt';
|
|
47
|
+
|
|
48
|
+
/** prompt/agent/skill/mcp 四种命令的 value(provider/model 三层回退的取值域)。 */
|
|
49
|
+
type ModelRoutedValue =
|
|
50
|
+
| PromptCommand['value']
|
|
51
|
+
| AgentCommand['value']
|
|
52
|
+
| SkillCommand['value']
|
|
53
|
+
| McpCommand['value'];
|
|
54
|
+
|
|
55
|
+
/** StepRunner 的引擎级配置(provider/model 三层回退的 L2 来源 + 名称校验数据源)。 */
|
|
56
|
+
export interface StepRunnerConfig {
|
|
57
|
+
readonly defaultTimeoutMs: number;
|
|
58
|
+
readonly defaultProvider?: string;
|
|
59
|
+
readonly defaultModel?: string;
|
|
60
|
+
/**
|
|
61
|
+
* 可续接子代理的 DSH subagent provider 注册名(`startContinuable` 的 `provider`
|
|
62
|
+
* 参数),与模型路由解耦:子代理的模型路由经 `request.agentOptions`
|
|
63
|
+
* (`{ provider, model }`)承载(DSH `continuation.ts` 以
|
|
64
|
+
* `request.agentOptions?.provider ?? parent.options.provider` 解析)。DSH 默认
|
|
65
|
+
* bundle 已注册 `fork`(`subagent-fork-in-process`,支持 continuable:
|
|
66
|
+
* prepareContinuable + startContinuable + followup + interrupt + cold resume),
|
|
67
|
+
* 缺省即 `'fork'`,运行 agent/prompt/skill/mcp Step **无需任何额外 subagent
|
|
68
|
+
* provider 配置**。如需换用其他已注册 provider(如 `'spawn'`)可在此覆盖。
|
|
69
|
+
* **注意:模型 provider id(如 `xiaomi-token-plan-cn`)绝不能填到这里**——DSH 只
|
|
70
|
+
* 认注册的 subagent provider 名,否则运行时报
|
|
71
|
+
* `no subagent provider registered for "<model provider id>"`。
|
|
72
|
+
*/
|
|
73
|
+
readonly subagentProvider?: string;
|
|
74
|
+
/** 工作流级 skills 名称数组(skill 类型名称校验数据源)。 */
|
|
75
|
+
readonly skills?: readonly string[];
|
|
76
|
+
/** 工作流级 mcps 名称数组(mcp 类型名称校验数据源)。 */
|
|
77
|
+
readonly mcps?: readonly string[];
|
|
78
|
+
/** 工作流级 agents 名称数组(agent 类型名称校验数据源)。 */
|
|
79
|
+
readonly agents?: readonly string[];
|
|
80
|
+
/** 宿主主代理(子代理 parent);缺省时使用最小桩对象。 */
|
|
81
|
+
readonly hostAgent?: Agent;
|
|
82
|
+
/**
|
|
83
|
+
* 子代理真实 childId 就绪回调(由 workflow 层注入):`startContinuable` 返回后、
|
|
84
|
+
* settle 等待前调用,把真实子代理 SessionId 上抛给引擎(供暂停反查 childId)。
|
|
85
|
+
* 返回 Promise 时 runner 会 await(引擎借此把真实 childId 落盘后再继续)。
|
|
86
|
+
*/
|
|
87
|
+
readonly onChildStarted?: (stepId: string, childId: string) => void | Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* 子代理 settle 终态等待回调(由 workflow 层注入,对齐 DSH `subagent/end`)。
|
|
90
|
+
*
|
|
91
|
+
* 传入子代理 SessionId 与取消信号,resolve 为该子代理的 settle 终态结算信号
|
|
92
|
+
* ({@link SubagentSettlement}:stopReason + 可选 output)。DSH continuable 子代理
|
|
93
|
+
* 「完成 = settle」:interrupt → settle(`aborted`);自然完成 → settle(`completed`);
|
|
94
|
+
* 模型/传输失败 → `error`。引擎实现经 `sandore/subagent-settled` 事件路由
|
|
95
|
+
* (DSH 适配层把 `subagent/end` 转译为该事件)。
|
|
96
|
+
*
|
|
97
|
+
* 缺省(未注入)时回退 `whenIdle()` + `finalAssistantOutput()`(兼容无注入场景,
|
|
98
|
+
* 如最小桩/不测 settle 的单测——此回退路径无法区分 abort,不适用于暂停语义)。
|
|
99
|
+
*/
|
|
100
|
+
readonly awaitSubagentSettlement?: (childId: string, signal: AbortSignal) => Promise<SubagentSettlement>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 前置 Step 的输出摘要(prompt 结构化提示词的参考信息)。 */
|
|
104
|
+
export interface PreviousStepOutput {
|
|
105
|
+
readonly name: string;
|
|
106
|
+
readonly id: string;
|
|
107
|
+
readonly returnPrompt: string;
|
|
108
|
+
readonly files: string[];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** 单 Step 执行请求。 */
|
|
112
|
+
export interface StepRunRequest {
|
|
113
|
+
readonly step: WorkflowStep;
|
|
114
|
+
/** 执行段唯一 id(executed.id,纯 UUID);区别于 `step.stepId`(定义 id)——提示词 title 展示用执行段 id。 */
|
|
115
|
+
readonly executedId: string;
|
|
116
|
+
readonly workdir: string;
|
|
117
|
+
readonly originalInput: string;
|
|
118
|
+
readonly previousOutputs: readonly PreviousStepOutput[];
|
|
119
|
+
/** 所属 Stage id(子代理 ask 转接回调的定位字段)。 */
|
|
120
|
+
readonly stageId: string;
|
|
121
|
+
readonly signal?: AbortSignal;
|
|
122
|
+
/** 当前透传变量(键为变量名,值为已解析字符串)。 */
|
|
123
|
+
readonly passThroughVars?: Readonly<Record<string, string>>;
|
|
124
|
+
/** 当前循环迭代序号(1-based;不在循环中缺省视为 0)。 */
|
|
125
|
+
readonly loopTurn?: number;
|
|
126
|
+
/** 活跃循环 `maxIterations`(不在循环中缺省视为 0)。 */
|
|
127
|
+
readonly loopMaxIterations?: number;
|
|
128
|
+
/**
|
|
129
|
+
* 当前 Step 解析后的任务规则(缺陷 7 修复:3 级合并 step ∪ stage → workflow.rules
|
|
130
|
+
* 查内容;由 {@link resolveStepRules} 在 {@link StageExecutor.processCommandNode}
|
|
131
|
+
* 计算)。非空时注入提示词 `## 任务规则` 节,prompt/agent/skill/mcp 四路 AI 提示词
|
|
132
|
+
* 同时生效;shell Step 无 AI 提示词不适用。缺省(无规则)时不注入该节。
|
|
133
|
+
*/
|
|
134
|
+
readonly resolvedRules?: Readonly<Record<string, string>>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 单 Step 执行结果附带的会话元数据(prompt/agent/skill/mcp 返回真实 childId)。 */
|
|
138
|
+
export interface StepRunSessionMeta {
|
|
139
|
+
readonly childId?: string;
|
|
140
|
+
readonly synthetic: boolean;
|
|
141
|
+
readonly command: CommandConfig['type'];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** 单 Step 执行结果(成功时的输出块)。 */
|
|
145
|
+
export interface StepRunResult {
|
|
146
|
+
readonly output: ContentBlock[];
|
|
147
|
+
readonly sessionMeta?: StepRunSessionMeta;
|
|
148
|
+
/** 子代理会话消息行(§3.8A:真实子代理采集,随结果带出写 `executed.sessionMessages`)。 */
|
|
149
|
+
readonly sessionMessages?: SessionMessage[];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** 结构化提示词主头部(v6:行动引导 + 职责三点;无角色声明、无行为要求、无输出格式要求)。 */
|
|
153
|
+
const PROMPT_MAIN_HEADER = [
|
|
154
|
+
'你正在执行工作流中的一个 Step。必须先明确以下三点,再开始执行:',
|
|
155
|
+
'- `## 用户指令` —— 用户提供的当前任务指令,是本次 Step 要达成的**唯一验收目标**。',
|
|
156
|
+
'- `## 任务规则` —— 用户配置的约束,执行时**不得违反**。',
|
|
157
|
+
'- `## 参考信息` —— 执行上下文,按需查阅。',
|
|
158
|
+
].join('\n');
|
|
159
|
+
|
|
160
|
+
/** fillParams 临时会话的任务指令。 */
|
|
161
|
+
const FILL_PARAMS_TASK =
|
|
162
|
+
'你的唯一任务是根据这个参数模板,然后根据(## 输入信息)中的信息,\n或者你可以进行一定程度的探索后,将参数模板的内容按照模板格式填充后返回这个结果!';
|
|
163
|
+
|
|
164
|
+
/** fillParams 临时会话的红线规则(只读,禁止文件改动与过度探索)。 */
|
|
165
|
+
const FILL_PARAMS_RED_LINES = [
|
|
166
|
+
'禁止编辑或者创建或者删除任何文件',
|
|
167
|
+
'禁止过度探索,你的任务是根据已有信息,做足够探索后尽快返回这个参数模板结果',
|
|
168
|
+
'不要过度思考,你的职责不是设计或者开发',
|
|
169
|
+
] as const;
|
|
170
|
+
|
|
171
|
+
/** 构造缺省 hostAgent 时的最小 parent 桩(每次返回新对象,避免跨 run 共享可变 `session.events`)。 */
|
|
172
|
+
function createHostAgentStub(): Agent {
|
|
173
|
+
return {
|
|
174
|
+
id: 'sandore-host',
|
|
175
|
+
options: {},
|
|
176
|
+
session: { events: [] },
|
|
177
|
+
inbox: { hasPending: false },
|
|
178
|
+
status: 'idle',
|
|
179
|
+
ctx: undefined,
|
|
180
|
+
followup: () => {},
|
|
181
|
+
whenIdle: async () => {},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** 多行文本每行前加 N 空格缩进(XML 块内内容对齐)。 */
|
|
186
|
+
function indentLines(text: string, spaces: number): string[] {
|
|
187
|
+
const prefix = ' '.repeat(spaces);
|
|
188
|
+
return text.split('\n').map(line => `${prefix}${line}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 渲染「前置 Steps 输出和产物」XML 块(v6:`<Reference title="前置Step: {{name}} (id: {{id}}) 的输出和产物">`,
|
|
193
|
+
* 内容为 `<Output>` + `<Artifacts>`,全部转义;无前置输出返回空)。
|
|
194
|
+
*/
|
|
195
|
+
function renderPreviousOutputs(previousOutputs: readonly PreviousStepOutput[]): string[] {
|
|
196
|
+
if (previousOutputs.length === 0) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
const sections: string[] = [];
|
|
200
|
+
for (const previous of previousOutputs) {
|
|
201
|
+
const name = escapeXmlAttr(previous.name);
|
|
202
|
+
const id = escapeXmlAttr(previous.id);
|
|
203
|
+
sections.push(` <Reference title="前置Step: ${name} (id: ${id}) 的输出和产物">`);
|
|
204
|
+
sections.push(' <Output>');
|
|
205
|
+
sections.push(...indentLines(escapeXml(previous.returnPrompt), 6));
|
|
206
|
+
sections.push(' </Output>');
|
|
207
|
+
sections.push(' <Artifacts>');
|
|
208
|
+
for (const file of previous.files) {
|
|
209
|
+
sections.push(` <Artifact>${escapeXml(file)}</Artifact>`);
|
|
210
|
+
}
|
|
211
|
+
sections.push(' </Artifacts>');
|
|
212
|
+
sections.push(' </Reference>');
|
|
213
|
+
sections.push('');
|
|
214
|
+
}
|
|
215
|
+
return sections;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** 命中名称数组时加前缀,未命中回退纯提示词。 */
|
|
219
|
+
function prefixIfAllowed(resolved: string, prefix: string, name: string, allowed: readonly string[] | undefined): string {
|
|
220
|
+
if (allowed !== undefined && allowed.includes(name)) {
|
|
221
|
+
return `${prefix}${resolved}`;
|
|
222
|
+
}
|
|
223
|
+
return resolved;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** 按命令类型构建「## 你的任务」内容(透传变量替换 + 名称校验前缀)。 */
|
|
227
|
+
function buildTaskText(
|
|
228
|
+
command: SubagentCommand | PromptCommand,
|
|
229
|
+
vars: Readonly<Record<string, string>>,
|
|
230
|
+
skills: readonly string[] | undefined,
|
|
231
|
+
mcps: readonly string[] | undefined,
|
|
232
|
+
agents: readonly string[] | undefined,
|
|
233
|
+
): string {
|
|
234
|
+
const name = command.value.promptText;
|
|
235
|
+
const resolved = replacePassThroughVars(name, vars);
|
|
236
|
+
switch (command.type) {
|
|
237
|
+
case 'agent':
|
|
238
|
+
return prefixIfAllowed(resolved, '使用 Agent:', name, agents);
|
|
239
|
+
case 'skill':
|
|
240
|
+
return prefixIfAllowed(resolved, '使用 skill:', name, skills);
|
|
241
|
+
case 'mcp':
|
|
242
|
+
return prefixIfAllowed(resolved, '使用 MCP:', name, mcps);
|
|
243
|
+
default:
|
|
244
|
+
return resolved;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 构建结构化提示词(v6:头部三点 + `## 用户指令`(`<UserInstruction>` XML 隔离 + 转义)
|
|
250
|
+
* + 可选 `## 任务规则` + `## 参考信息`(`<ReferenceInfo>`:当前 Step 身份块(含可选
|
|
251
|
+
* `<Params>`)+ 整体目标块 + 前置输出和产物块)。
|
|
252
|
+
*
|
|
253
|
+
* 双通道 ask 重构后不再注入「向用户提问」引导节(`[[SANDORE_ASK]]` 标记块机制已删):
|
|
254
|
+
* 子代理提问走 child-scope `ask_user_question` 工具(registerContinuableSetup 注入,
|
|
255
|
+
* 见 workflow/ask-tool.ts),prompt/agent/skill/mcp 四路 AI 提示词共用本函数。
|
|
256
|
+
*
|
|
257
|
+
* `## 任务规则` 节置于参考信息**之前**(与头部三点顺序一致:用户指令 → 任务规则 →
|
|
258
|
+
* 参考信息);仅当 `resolvedRules` 非空时渲染,逐键输出 `- **{key}**: {content}`。
|
|
259
|
+
*
|
|
260
|
+
* 所有 XML 内容统一转义(`escapeXml`,含 `<UserInstruction>`);title 属性值用
|
|
261
|
+
* `escapeXmlAttr`。`<UserInstruction>` 内容为 `buildTaskText` 输出(agent/skill/mcp
|
|
262
|
+
* 名称命中定义数组时带「使用 Agent:/使用 skill:/使用 MCP:」前缀,防纯名称歧义)。
|
|
263
|
+
*/
|
|
264
|
+
function buildPromptText(
|
|
265
|
+
taskText: string,
|
|
266
|
+
stepName: string,
|
|
267
|
+
executedId: string,
|
|
268
|
+
originalInput: string,
|
|
269
|
+
previousOutputs: readonly PreviousStepOutput[],
|
|
270
|
+
paramsText: string | undefined,
|
|
271
|
+
resolvedRules?: Readonly<Record<string, string>>,
|
|
272
|
+
): string {
|
|
273
|
+
const sections: string[] = [];
|
|
274
|
+
sections.push(PROMPT_MAIN_HEADER);
|
|
275
|
+
sections.push('');
|
|
276
|
+
sections.push('## 用户指令');
|
|
277
|
+
sections.push('<UserInstruction>');
|
|
278
|
+
sections.push(escapeXml(taskText));
|
|
279
|
+
sections.push('</UserInstruction>');
|
|
280
|
+
// `## 任务规则`(可选)——置于参考信息之前,与头部三点顺序一致。
|
|
281
|
+
if (resolvedRules !== undefined) {
|
|
282
|
+
const ruleKeys = Object.keys(resolvedRules);
|
|
283
|
+
if (ruleKeys.length > 0) {
|
|
284
|
+
sections.push('');
|
|
285
|
+
sections.push('## 任务规则');
|
|
286
|
+
for (const key of ruleKeys) {
|
|
287
|
+
sections.push(`- **${key}**: ${resolvedRules[key]}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
sections.push('');
|
|
292
|
+
sections.push('## 参考信息');
|
|
293
|
+
sections.push('<ReferenceInfo>');
|
|
294
|
+
// 当前 Step 身份块(含可选 <Params> 子标签;无参数时为空块锚点)。
|
|
295
|
+
// id 展示执行段 id(executed.id,本次执行唯一),非定义 stepId。
|
|
296
|
+
const stepNameEscaped = escapeXmlAttr(stepName);
|
|
297
|
+
const executedIdEscaped = escapeXmlAttr(executedId);
|
|
298
|
+
if (paramsText === undefined) {
|
|
299
|
+
sections.push(` <Reference title="你正在执行的Step: ${stepNameEscaped} (id: ${executedIdEscaped})"></Reference>`);
|
|
300
|
+
} else {
|
|
301
|
+
sections.push(` <Reference title="你正在执行的Step: ${stepNameEscaped} (id: ${executedIdEscaped})">`);
|
|
302
|
+
sections.push(' <Params>');
|
|
303
|
+
sections.push(...indentLines(escapeXml(paramsText), 6));
|
|
304
|
+
sections.push(' </Params>');
|
|
305
|
+
sections.push(' </Reference>');
|
|
306
|
+
}
|
|
307
|
+
sections.push('');
|
|
308
|
+
// 用户原始需求(整个工作流的整体目标)。
|
|
309
|
+
sections.push(' <Reference title="用户原始需求(整个工作流的整体目标)">');
|
|
310
|
+
sections.push(...indentLines(escapeXml(originalInput), 4));
|
|
311
|
+
sections.push(' </Reference>');
|
|
312
|
+
// 前置 Steps 输出和产物(可选,每个前置一块)。
|
|
313
|
+
sections.push(...renderPreviousOutputs(previousOutputs));
|
|
314
|
+
sections.push('</ReferenceInfo>');
|
|
315
|
+
return sections.join('\n');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** 构建 fillParams 临时会话提示词(输入信息 + 参数模板 + 任务指令 + 红线规则)。 */
|
|
319
|
+
function buildFillParamsPrompt(
|
|
320
|
+
originalInput: string,
|
|
321
|
+
previousOutputs: readonly PreviousStepOutput[],
|
|
322
|
+
template: string,
|
|
323
|
+
): string {
|
|
324
|
+
const sections: string[] = [];
|
|
325
|
+
sections.push('## 输入信息');
|
|
326
|
+
sections.push('### 用户原始需求');
|
|
327
|
+
sections.push(originalInput);
|
|
328
|
+
sections.push(...renderPreviousOutputs(previousOutputs));
|
|
329
|
+
sections.push('');
|
|
330
|
+
sections.push('## 参数模板');
|
|
331
|
+
sections.push(template);
|
|
332
|
+
sections.push('');
|
|
333
|
+
sections.push('## 你的任务');
|
|
334
|
+
sections.push(FILL_PARAMS_TASK);
|
|
335
|
+
sections.push('');
|
|
336
|
+
sections.push('## 红线规则');
|
|
337
|
+
for (const rule of FILL_PARAMS_RED_LINES) {
|
|
338
|
+
sections.push(`- ${rule}`);
|
|
339
|
+
}
|
|
340
|
+
return sections.join('\n');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** 从 `inputFrom` 的 `path` 取 Step id(简化格式:path 直接为 stepId,单段)。 */
|
|
344
|
+
function stepIdFromPath(path: string): string {
|
|
345
|
+
return path;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 按 `step.inputFrom` 过滤前置输出(v6 修复:prompt 提示词只注入**声明的依赖**,
|
|
350
|
+
* 对齐 `resolveShellStdin` 的匹配语义——此前全量注入导致「配置只依赖 sce1 却显示
|
|
351
|
+
* 所有前置 step 输出」)。
|
|
352
|
+
*
|
|
353
|
+
* - `inputFrom` 未配置/为空 → 返回全量(向后兼容:未声明依赖的工作流仍可引用全部前置)。
|
|
354
|
+
* - 配置后按 `path`(stepId 匹配 `idMatches`)/ `regex`(returnPrompt 匹配)筛选;
|
|
355
|
+
* 循环/重试多段同 stepId 时**按 id 去重、只保留最近一段**(数组序 = 完成顺序)。
|
|
356
|
+
*/
|
|
357
|
+
function filterPreviousOutputs(
|
|
358
|
+
step: WorkflowStep,
|
|
359
|
+
previousOutputs: readonly PreviousStepOutput[],
|
|
360
|
+
): readonly PreviousStepOutput[] {
|
|
361
|
+
const inputFrom = step.inputFrom;
|
|
362
|
+
if (inputFrom === undefined || inputFrom.length === 0) {
|
|
363
|
+
return previousOutputs;
|
|
364
|
+
}
|
|
365
|
+
const matched: PreviousStepOutput[] = [];
|
|
366
|
+
for (const item of inputFrom) {
|
|
367
|
+
if (item.regex !== undefined) {
|
|
368
|
+
let regex: RegExp;
|
|
369
|
+
try {
|
|
370
|
+
regex = new RegExp(item.regex);
|
|
371
|
+
} catch {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
for (const previous of previousOutputs) {
|
|
375
|
+
if (regex.test(previous.returnPrompt)) {
|
|
376
|
+
matched.push(previous);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} else if (item.path !== undefined) {
|
|
380
|
+
const stepId = stepIdFromPath(item.path);
|
|
381
|
+
for (const previous of previousOutputs) {
|
|
382
|
+
if (idMatches(previous.id, stepId)) {
|
|
383
|
+
matched.push(previous);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// 按 id 去重、保留最近一段(数组序 = 完成顺序,最后一段为最新)。
|
|
389
|
+
const seen = new Set<string>();
|
|
390
|
+
const result: PreviousStepOutput[] = [];
|
|
391
|
+
for (let index = matched.length - 1; index >= 0; index -= 1) {
|
|
392
|
+
const previous = matched[index]!;
|
|
393
|
+
if (!seen.has(previous.id)) {
|
|
394
|
+
seen.add(previous.id);
|
|
395
|
+
result.unshift(previous);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** 解析 shell Step 的 stdin:`inputFrom` 命中前置输出时返回 JSON 序列化,否则 undefined。 */
|
|
402
|
+
function resolveShellStdin(step: WorkflowStep, previousOutputs: readonly PreviousStepOutput[]): string | undefined {
|
|
403
|
+
const inputFrom = step.inputFrom;
|
|
404
|
+
if (inputFrom === undefined || inputFrom.length === 0) {
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
const matched: PreviousStepOutput[] = [];
|
|
408
|
+
for (const item of inputFrom) {
|
|
409
|
+
if (item.regex !== undefined) {
|
|
410
|
+
let regex: RegExp;
|
|
411
|
+
try {
|
|
412
|
+
regex = new RegExp(item.regex);
|
|
413
|
+
} catch {
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
for (const previous of previousOutputs) {
|
|
417
|
+
if (regex.test(previous.returnPrompt)) {
|
|
418
|
+
matched.push(previous);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
} else if (item.path !== undefined) {
|
|
422
|
+
const stepId = stepIdFromPath(item.path);
|
|
423
|
+
for (const previous of previousOutputs) {
|
|
424
|
+
if (idMatches(previous.id, stepId)) {
|
|
425
|
+
matched.push(previous);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (matched.length === 0) {
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
return JSON.stringify(matched);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* 单 Step 执行器。
|
|
438
|
+
*
|
|
439
|
+
* 构造时绑定 `ctx` 与引擎级配置;`run()` 按 `step.command.type` 分发到
|
|
440
|
+
* shell 与 prompt/agent/skill/mcp 两条路径(决策 D3:除 shell 外全部经可续接子代理)。
|
|
441
|
+
* 成功返回 `{ output: ContentBlock[] }`,失败抛 `SandoreError`
|
|
442
|
+
* (超时 → `STEP_TIMEOUT`,其余 → `STEP_EXECUTION`)。
|
|
443
|
+
*/
|
|
444
|
+
export class StepRunner {
|
|
445
|
+
/**
|
|
446
|
+
* 可续接子代理的 DSH subagent provider 注册名(见
|
|
447
|
+
* {@link StepRunnerConfig.subagentProvider});缺省 `'fork'`(DSH base bundle 已注册)。
|
|
448
|
+
*/
|
|
449
|
+
private readonly subagentProvider: string;
|
|
450
|
+
|
|
451
|
+
constructor(
|
|
452
|
+
private readonly ctx: Context,
|
|
453
|
+
private readonly config: StepRunnerConfig,
|
|
454
|
+
) {
|
|
455
|
+
// 方案 2:复用 DSH 默认 fork subagent provider(支持 continuable),provider 参数
|
|
456
|
+
// 与模型路由解耦——模型路由经 request.agentOptions 传给子代理。缺省 'fork' 即满足
|
|
457
|
+
// agent/prompt/skill/mcp Step,无需任何额外 subagent provider 配置。
|
|
458
|
+
this.subagentProvider = config.subagentProvider ?? 'fork';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** 执行单个 Step;失败抛 `SandoreError`。 */
|
|
462
|
+
async run(request: StepRunRequest): Promise<StepRunResult> {
|
|
463
|
+
const command = request.step.command;
|
|
464
|
+
if (command === undefined) {
|
|
465
|
+
throw new SandoreError(`step '${request.step.id}' has no command`, 'STEP_EXECUTION');
|
|
466
|
+
}
|
|
467
|
+
switch (command.type) {
|
|
468
|
+
case 'shell':
|
|
469
|
+
return await this.runShell(command, request);
|
|
470
|
+
case 'prompt':
|
|
471
|
+
case 'agent':
|
|
472
|
+
case 'skill':
|
|
473
|
+
case 'mcp':
|
|
474
|
+
// 设计决策 D3:除 shell 外,prompt/agent/skill/mcp 全部经可续接子代理
|
|
475
|
+
// (`startContinuable`)运行——prompt 与 agent/skill/mcp 行为对齐。
|
|
476
|
+
return await this.runSubagent(command, request);
|
|
477
|
+
default:
|
|
478
|
+
// 穷尽 5 类型后 `command` 已收窄为 never;此处仅防御运行期注入的未知命令类型。
|
|
479
|
+
throw new SandoreError(
|
|
480
|
+
`unsupported step command type '${(command as CommandConfig).type}'`,
|
|
481
|
+
'STEP_EXECUTION',
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private async runShell(command: ShellCommand, request: StepRunRequest): Promise<StepRunResult> {
|
|
487
|
+
const resolvedCommand = replacePassThroughVars(
|
|
488
|
+
command.value.command,
|
|
489
|
+
resolvePassThroughVars(request.passThroughVars, request.loopTurn, request.loopMaxIterations),
|
|
490
|
+
);
|
|
491
|
+
const stdin = resolveShellStdin(request.step, request.previousOutputs);
|
|
492
|
+
const env = buildShellEnv(request.passThroughVars, request.loopTurn, request.loopMaxIterations);
|
|
493
|
+
// 统一经 `ctx.shell`(平台 shell 解释器,如 PowerShell/bash)执行:env/stdin
|
|
494
|
+
// 由 ShellExecRequest 携带。不要在这里改用 `ctx.subprocess.spawn` 直接执行
|
|
495
|
+
// 命令——subprocess 不做 shell 解释,PowerShell cmdlet(New-Item 等)不是可
|
|
496
|
+
// 执行文件,带透传变量/inputFrom 的 shell 步骤会失败。
|
|
497
|
+
return await this.runShellSimple(resolvedCommand, request, stdin, env);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
private async runShellSimple(
|
|
501
|
+
command: string,
|
|
502
|
+
request: StepRunRequest,
|
|
503
|
+
stdin: string | undefined = undefined,
|
|
504
|
+
env: Record<string, string> | undefined = undefined,
|
|
505
|
+
): Promise<StepRunResult> {
|
|
506
|
+
const shellRequest: ShellExecRequest = {
|
|
507
|
+
command,
|
|
508
|
+
timeoutMs: this.stepTimeoutMs(request.step),
|
|
509
|
+
...(request.workdir === undefined ? {} : { workdir: request.workdir }),
|
|
510
|
+
...(request.signal === undefined ? {} : { signal: request.signal }),
|
|
511
|
+
...(stdin === undefined ? {} : { stdin }),
|
|
512
|
+
...(env === undefined ? {} : { env }),
|
|
513
|
+
};
|
|
514
|
+
const spec = this.ctx.shell.resolve(shellRequest);
|
|
515
|
+
const result = await this.ctx.shell.run(spec);
|
|
516
|
+
return this.toShellResult(result);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private toShellResult(result: ShellRunResult): StepRunResult {
|
|
520
|
+
if (result.exitCode === 0 && !result.timedOut && !result.aborted) {
|
|
521
|
+
return { output: [{ type: 'text', text: result.stdout.text }] };
|
|
522
|
+
}
|
|
523
|
+
const stderr = result.stderr.text.trim();
|
|
524
|
+
const stderrSummary = stderr === '' ? '(no stderr)' : stderr;
|
|
525
|
+
if (result.timedOut) {
|
|
526
|
+
throw new SandoreError(`shell command timed out: ${stderrSummary}`, 'STEP_TIMEOUT');
|
|
527
|
+
}
|
|
528
|
+
const reason = result.aborted
|
|
529
|
+
? 'aborted'
|
|
530
|
+
: `exited with code ${result.exitCode === null ? 'signal' : String(result.exitCode)}`;
|
|
531
|
+
throw new SandoreError(`shell command ${reason}: ${stderrSummary}`, 'STEP_EXECUTION');
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** 解析 Step 级 timeout(分钟)→ 毫秒;缺省回退引擎 defaultTimeoutMs。 */
|
|
535
|
+
private stepTimeoutMs(step: WorkflowStep): number {
|
|
536
|
+
return step.timeout === undefined ? this.config.defaultTimeoutMs : step.timeout * 60_000;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* 经可续接子代理执行 prompt/agent/skill/mcp Step(设计决策 D3:AI 会话一律
|
|
541
|
+
* `startContinuable` 真实子代理,与临时 `ctx.llm.stream()` 区分)。
|
|
542
|
+
*
|
|
543
|
+
* agent 定义 `agentParamsTemplate` 时先 `fillParams` 临时会话填充
|
|
544
|
+
* 「### 当前Step参数」;prompt 无参数模板(paramsText 恒 undefined)。随后三层
|
|
545
|
+
* provider/model 回退 → 构建结构化提示词 → `startContinuable`(parent=宿主主代理;
|
|
546
|
+
* subagent provider 名与模型路由解耦——provider 参数用 DSH 注册的 `fork`,模型
|
|
547
|
+
* 路由经 `request.agentOptions`)。子代理提问能力由 workflow 层经
|
|
548
|
+
* `subagents.registerContinuableSetup` 注入 child-scope `ask_user_question` 工具
|
|
549
|
+
* (双通道 ask 通道 1,见 workflow/ask-tool.ts;不再 toolFilter deny 全局工具——
|
|
550
|
+
* child-scope 同名注册天然遮蔽全局版)→ `onChildStarted` 上抛真实 childId →
|
|
551
|
+
* `collectSubagentOutput` 等 settle 终态(completed/aborted/error 路由)+ final output。
|
|
552
|
+
*/
|
|
553
|
+
private async runSubagent(command: SubagentCommand | PromptCommand, request: StepRunRequest): Promise<StepRunResult> {
|
|
554
|
+
// v6:按 step.inputFrom 过滤前置输出(未配置依赖 → 全量;配置后只注入声明的依赖,
|
|
555
|
+
// 多段同 stepId 去重取最近一段)——prompt 与 fillParams 共用过滤后的列表。
|
|
556
|
+
const previousOutputs = filterPreviousOutputs(request.step, request.previousOutputs);
|
|
557
|
+
const paramsText =
|
|
558
|
+
command.type === 'agent' && command.value.agentParamsTemplate !== undefined
|
|
559
|
+
? await this.fillParams(command, command.value.agentParamsTemplate, { ...request, previousOutputs })
|
|
560
|
+
: undefined;
|
|
561
|
+
const route = await this.resolveProviderModel(command.value);
|
|
562
|
+
const taskText = buildTaskText(
|
|
563
|
+
command,
|
|
564
|
+
resolvePassThroughVars(request.passThroughVars, request.loopTurn, request.loopMaxIterations),
|
|
565
|
+
this.config.skills,
|
|
566
|
+
this.config.mcps,
|
|
567
|
+
this.config.agents,
|
|
568
|
+
);
|
|
569
|
+
const promptText = buildPromptText(
|
|
570
|
+
taskText,
|
|
571
|
+
request.step.name ?? request.step.id,
|
|
572
|
+
request.executedId,
|
|
573
|
+
request.originalInput,
|
|
574
|
+
previousOutputs,
|
|
575
|
+
paramsText,
|
|
576
|
+
request.resolvedRules,
|
|
577
|
+
);
|
|
578
|
+
const started = await this.ctx.subagents.startContinuable({
|
|
579
|
+
// `provider` 是 DSH subagent provider 注册名(固定 `this.subagentProvider`,
|
|
580
|
+
// 缺省 'fork',DSH base bundle 已注册),与模型路由完全解耦:模型路由经
|
|
581
|
+
// `request.agentOptions`({ provider, model })承载(DSH continuation.ts:
|
|
582
|
+
// `request.agentOptions?.provider ?? parent.options.provider`)。不要把模型
|
|
583
|
+
// provider id(`route.provider`)填到这里——DSH 只认注册的 subagent provider
|
|
584
|
+
// 名(fork/spawn),否则运行时报
|
|
585
|
+
// `no subagent provider registered for "<model provider id>"`。
|
|
586
|
+
provider: this.subagentProvider,
|
|
587
|
+
label: `step ${request.step.id}`,
|
|
588
|
+
request: {
|
|
589
|
+
prompt: [{ type: 'text', text: promptText }],
|
|
590
|
+
parent: this.resolveParent(),
|
|
591
|
+
agentOptions: { provider: route.provider, model: route.model },
|
|
592
|
+
},
|
|
593
|
+
signal: request.signal ?? new AbortController().signal,
|
|
594
|
+
});
|
|
595
|
+
await this.config.onChildStarted?.(request.step.id, started.childId);
|
|
596
|
+
return this.collectSubagentOutput(command.type, started.childId, request.step.id, request.stageId, request.signal);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* 暂停后恢复子代理 Step:重新 settle + final output。
|
|
601
|
+
*
|
|
602
|
+
* 暂停的子代理 Step 被 `interrupt` 打断后(settle `aborted`),`resumeStep` 的
|
|
603
|
+
* `followup` cold-resume 重新驱动子代理;本方法在 `followup` 之后被引擎续跑路径调用,
|
|
604
|
+
* 重新等待子代理 settle 并取回最终 assistant 输出(等价于首次执行时的输出收集尾段)。
|
|
605
|
+
*/
|
|
606
|
+
async resumeSubagent(
|
|
607
|
+
commandType: SubagentCommandType,
|
|
608
|
+
childId: string,
|
|
609
|
+
stepId: string,
|
|
610
|
+
stageId: string,
|
|
611
|
+
signal?: AbortSignal,
|
|
612
|
+
): Promise<StepRunResult> {
|
|
613
|
+
return this.collectSubagentOutput(commandType, childId, stepId, stageId, signal);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* 收集已启动子代理的最终输出(首次执行与暂停续跑共用:settle 终态 + final output)。
|
|
618
|
+
*
|
|
619
|
+
* 完成判定对齐 DSH `subagent/end`:等 `awaitSubagentSettlement`(由 workflow 层注入,
|
|
620
|
+
* 经 `sandore/subagent-settled` 事件路由 DSH settle 终态)。按 `stopReason` 区分:
|
|
621
|
+
* - `completed` → Step 完成,取 settle 载荷 `output`(缺省回退 `finalAssistantOutput`)。
|
|
622
|
+
* - `aborted`(interrupt 暂停)→ 不算完成,抛 `STEP_PAUSED` sentinel 供 stage-executor
|
|
623
|
+
* 路由为 paused(不再依赖暂停登记表兜底)。
|
|
624
|
+
* - `error`/`max-tokens`/`refusal` → 落 `STEP_EXECUTION`(failed)。
|
|
625
|
+
*
|
|
626
|
+
* `awaitSubagentSettlement` 缺省时回退 `whenIdle()` + `finalAssistantOutput()`(兼容无注入
|
|
627
|
+
* 场景;此回退无法区分 abort,不适用于暂停语义——暂停路径必经注入的 settle)。
|
|
628
|
+
*
|
|
629
|
+
* 双通道 ask 重构后**不再有 ask 转接循环**(`[[SANDORE_ASK]]` 标记块机制已删):子代理
|
|
630
|
+
* 提问经 child-scope `ask_user_question` 工具在执行 turn 内同步完成(工具调用阻塞等待
|
|
631
|
+
* 引擎级 ask 答案),final output 即最终输出,无标记块解析。
|
|
632
|
+
*/
|
|
633
|
+
private async collectSubagentOutput(
|
|
634
|
+
commandType: SubagentCommandType,
|
|
635
|
+
childId: string,
|
|
636
|
+
stepId: string,
|
|
637
|
+
stageId: string,
|
|
638
|
+
signal?: AbortSignal,
|
|
639
|
+
): Promise<StepRunResult> {
|
|
640
|
+
const child = this.ctx.agents.get(childId);
|
|
641
|
+
if (child === undefined) {
|
|
642
|
+
throw new SandoreError(`subagent '${childId}' not found in registry`, 'STEP_EXECUTION');
|
|
643
|
+
}
|
|
644
|
+
const awaitSettlement = this.config.awaitSubagentSettlement;
|
|
645
|
+
// settle 终态 → final output:只有 DSH settle(completed)才算完成;aborted 抛暂停
|
|
646
|
+
// sentinel;error/max-tokens/refusal 落 failed。未注入时回退 whenIdle + finalAssistantOutput。
|
|
647
|
+
const settle = async (): Promise<ContentBlock[]> => {
|
|
648
|
+
if (awaitSettlement === undefined) {
|
|
649
|
+
await child.whenIdle();
|
|
650
|
+
const fallback = finalAssistantOutput(child.session.events);
|
|
651
|
+
if (fallback === undefined) {
|
|
652
|
+
throw new SandoreError('subagent produced no assistant output', 'STEP_EXECUTION');
|
|
653
|
+
}
|
|
654
|
+
return fallback;
|
|
655
|
+
}
|
|
656
|
+
const settlement: SubagentSettlement = await awaitSettlement(childId, signal ?? new AbortController().signal);
|
|
657
|
+
if (settlement.stopReason === 'completed') {
|
|
658
|
+
// settle 载荷 output 为只读视图(对齐 DSH lastAssistantMessage 借用语义),拷贝为可变数组。
|
|
659
|
+
const out = settlement.output ?? finalAssistantOutput(child.session.events);
|
|
660
|
+
if (out === undefined) {
|
|
661
|
+
throw new SandoreError('subagent produced no assistant output', 'STEP_EXECUTION');
|
|
662
|
+
}
|
|
663
|
+
return [...out];
|
|
664
|
+
}
|
|
665
|
+
if (settlement.stopReason === 'aborted') {
|
|
666
|
+
// interrupt 暂停:不算完成,抛 STEP_PAUSED sentinel 供 stage-executor 路由为 paused。
|
|
667
|
+
throw new SandoreError(`subagent '${childId}' interrupted (settle aborted)`, 'STEP_PAUSED', { fatal: false });
|
|
668
|
+
}
|
|
669
|
+
// error / max-tokens / refusal(及未知 reason)→ failed。
|
|
670
|
+
throw new SandoreError(`subagent '${childId}' settle ${settlement.stopReason}`, 'STEP_EXECUTION');
|
|
671
|
+
};
|
|
672
|
+
const output = await settle();
|
|
673
|
+
// §3.8A:子代理消息持久化(settle 后采集,随结果带出 → finishCommandNode 写
|
|
674
|
+
// executed.sessionMessages → onPersist 定格;前端内嵌消息框/失败·暂停最近 N 条)。
|
|
675
|
+
// 只保留最新 10 条(slice(-10)):真实子代理走 StepChatPanel 实时订阅不读
|
|
676
|
+
// sessionMessages;合成会话兜底(chatLinesOf)也只展示最近 N 条——截断减小
|
|
677
|
+
// 持久化体积,不影响兜底正确性。
|
|
678
|
+
// 取最新活 child(cold-resume 后的新 handle),回退 start child(mock FakeAgent 复用)。
|
|
679
|
+
const liveChild = this.ctx.agents.get(childId) ?? child;
|
|
680
|
+
return {
|
|
681
|
+
output,
|
|
682
|
+
sessionMeta: { childId, synthetic: false, command: commandType },
|
|
683
|
+
sessionMessages: collectSessionMessages(liveChild.session.events).slice(-10),
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* 用可续接子代理按模板填充参数(v6 改造:与 step-execute 同机制——DSH preset +
|
|
689
|
+
* 工具 + settle 收集输出,不再 llm.stream 临时会话);失败(error/aborted/抛错/
|
|
690
|
+
* 无输出)回退模板原文。
|
|
691
|
+
*/
|
|
692
|
+
private async fillParams(command: AgentCommand, template: string, request: StepRunRequest): Promise<string> {
|
|
693
|
+
const route = await this.resolveProviderModel(command.value);
|
|
694
|
+
const promptText = buildFillParamsPrompt(request.originalInput, request.previousOutputs, template);
|
|
695
|
+
try {
|
|
696
|
+
const started = await this.ctx.subagents.startContinuable({
|
|
697
|
+
provider: this.subagentProvider,
|
|
698
|
+
label: `fillParams ${request.step.id}`,
|
|
699
|
+
request: {
|
|
700
|
+
prompt: [{ type: 'text', text: promptText }],
|
|
701
|
+
parent: this.resolveParent(),
|
|
702
|
+
agentOptions: { provider: route.provider, model: route.model },
|
|
703
|
+
},
|
|
704
|
+
signal: request.signal ?? new AbortController().signal,
|
|
705
|
+
});
|
|
706
|
+
// settle 终态收集(与 collectSubagentOutput 同语义):completed → 输出;
|
|
707
|
+
// 其余 stopReason(error/max-tokens/refusal/aborted)回退模板原文。
|
|
708
|
+
const awaitSettlement = this.config.awaitSubagentSettlement;
|
|
709
|
+
const collect = async (): Promise<readonly ContentBlock[] | undefined> => {
|
|
710
|
+
if (awaitSettlement !== undefined) {
|
|
711
|
+
const settlement = await awaitSettlement(started.childId, request.signal ?? new AbortController().signal);
|
|
712
|
+
if (settlement.stopReason !== 'completed') {
|
|
713
|
+
return undefined;
|
|
714
|
+
}
|
|
715
|
+
const child = this.ctx.agents.get(started.childId);
|
|
716
|
+
return settlement.output ?? (child === undefined ? undefined : finalAssistantOutput(child.session.events));
|
|
717
|
+
}
|
|
718
|
+
const child = this.ctx.agents.get(started.childId);
|
|
719
|
+
if (child === undefined) {
|
|
720
|
+
return undefined;
|
|
721
|
+
}
|
|
722
|
+
await child.whenIdle();
|
|
723
|
+
return finalAssistantOutput(child.session.events);
|
|
724
|
+
};
|
|
725
|
+
const output = await collect();
|
|
726
|
+
if (output === undefined) {
|
|
727
|
+
this.ctx.logger.warn('sandore: fillParams subagent produced no output, falling back to template');
|
|
728
|
+
return template;
|
|
729
|
+
}
|
|
730
|
+
const text = textSummary(output);
|
|
731
|
+
return text === '' ? template : text;
|
|
732
|
+
} catch (error) {
|
|
733
|
+
this.ctx.logger.warn(`sandore: fillParams subagent failed: ${describeError(error)}`);
|
|
734
|
+
return template;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
private async resolveProviderModel(value: ModelRoutedValue): Promise<{ provider: string; model: string }> {
|
|
739
|
+
return resolveModelRoute(
|
|
740
|
+
value,
|
|
741
|
+
this.config,
|
|
742
|
+
this.ctx.llm,
|
|
743
|
+
(provider, model) =>
|
|
744
|
+
this.ctx.logger.warn(`sandore: step falls back to directory provider '${provider}' model '${model}'`),
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/** 子代理 parent:优先 config.hostAgent,缺省用最小桩对象。 */
|
|
749
|
+
private resolveParent(): Agent {
|
|
750
|
+
return this.config.hostAgent ?? createHostAgentStub();
|
|
751
|
+
}
|
|
752
|
+
}
|