@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,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内存 mock storageDomain(测试专用,非发布面)。
|
|
3
|
+
*
|
|
4
|
+
* 用 `Map` 实现 `KvTable` 的等价语义(读同步、写 await、`delete` 幂等、
|
|
5
|
+
* `update` 缺 key 抛 `missing-key`),并提供 `open(spec)`/`get(name)` 的
|
|
6
|
+
* `DomainFacility` 形状,供 `persistence.spec.ts` / `remote.spec.ts` 以
|
|
7
|
+
* `ctx.provide('storageDomain', …)` 注入,避免在单元测试中拉起真实后端。
|
|
8
|
+
* @module dsh-sandore/workflow/tests/mocks/storage-domain
|
|
9
|
+
*/
|
|
10
|
+
import { DomainError, type Domain, type DomainSpec, type KvTable } from '@deepseek-ai/dsh-storage-domain'
|
|
11
|
+
|
|
12
|
+
/** Map 版 `KvTable`:`K` 为记录键,`V` 为记录值。 */
|
|
13
|
+
export class MemoryKvTable<K extends string, V> implements KvTable<K, V> {
|
|
14
|
+
private readonly records = new Map<K, V>()
|
|
15
|
+
|
|
16
|
+
get(key: K): V | undefined {
|
|
17
|
+
return this.records.get(key)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
entries(): IterableIterator<[K, V]> {
|
|
21
|
+
return this.records.entries()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
keys(): IterableIterator<K> {
|
|
25
|
+
return this.records.keys()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get size(): number {
|
|
29
|
+
return this.records.size
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async put(key: K, value: V): Promise<void> {
|
|
33
|
+
this.records.set(key, value)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async delete(key: K): Promise<boolean> {
|
|
37
|
+
return this.records.delete(key)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async update(key: K, fn: (current: V) => V): Promise<V> {
|
|
41
|
+
const current = this.records.get(key)
|
|
42
|
+
if (current === undefined) {
|
|
43
|
+
throw new DomainError('missing-key', `key '${String(key)}' is missing`)
|
|
44
|
+
}
|
|
45
|
+
const next = fn(current)
|
|
46
|
+
this.records.set(key, next)
|
|
47
|
+
return next
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 单个已打开域的内存实现:按 spec 声明的表名建空表,`close()` 幂等。 */
|
|
52
|
+
export class MemoryDomain {
|
|
53
|
+
readonly name: string
|
|
54
|
+
|
|
55
|
+
private readonly tableMap = new Map<string, MemoryKvTable<string, unknown>>()
|
|
56
|
+
private isClosed = false
|
|
57
|
+
|
|
58
|
+
constructor(name: string, tableNames: readonly string[]) {
|
|
59
|
+
this.name = name
|
|
60
|
+
for (const tableName of tableNames) {
|
|
61
|
+
this.tableMap.set(tableName, new MemoryKvTable())
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
table<K extends string, V>(name: string): KvTable<K, V> {
|
|
66
|
+
const table = this.tableMap.get(name)
|
|
67
|
+
if (table === undefined) {
|
|
68
|
+
throw new Error(`memory domain '${this.name}' has no table '${name}'`)
|
|
69
|
+
}
|
|
70
|
+
return table as unknown as KvTable<K, V>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async close(): Promise<void> {
|
|
74
|
+
this.isClosed = true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get closed(): boolean {
|
|
78
|
+
return this.isClosed
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** 内存 `DomainFacility` 形状:`open` 建域(拒绝重复打开),`get` 供断言诊断。 */
|
|
83
|
+
export class MemoryStorageDomain {
|
|
84
|
+
private readonly domains = new Map<string, MemoryDomain>()
|
|
85
|
+
|
|
86
|
+
async open<S extends DomainSpec>(spec: S): Promise<Domain<S>> {
|
|
87
|
+
const name = spec.name
|
|
88
|
+
const existing = this.domains.get(name)
|
|
89
|
+
if (existing !== undefined && !existing.closed) {
|
|
90
|
+
throw new DomainError('already-open', `domain '${name}' is already open`)
|
|
91
|
+
}
|
|
92
|
+
const domain = new MemoryDomain(name, Object.keys(spec.tables))
|
|
93
|
+
this.domains.set(name, domain)
|
|
94
|
+
// 测试 mock:MemoryDomain 是 Domain<S> 的等价实现(spec 未声明 global,句柄为 never)。
|
|
95
|
+
return domain as unknown as Domain<S>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get(name: string): MemoryDomain | undefined {
|
|
99
|
+
return this.domains.get(name)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内存 mock subagents(测试专用,非发布面)。
|
|
3
|
+
*
|
|
4
|
+
* 提供 `startContinuable`/`followup`/`interrupt`/`start` 等价语义:`startContinuable`
|
|
5
|
+
* 记录委派请求并 resolve 为 `{ childId, messageId }`(inbox 接受即 resolve)。以
|
|
6
|
+
* `ctx.provide('subagents', …)` 注入。
|
|
7
|
+
*
|
|
8
|
+
* **settle 仿真**(对齐 DSH `SubagentContinuationManager`):continuable 子代理「完成 =
|
|
9
|
+
* settle」。本 mock 不后台驱动 settle——由测试经 `settleChild(childId, stopReason)`
|
|
10
|
+
* 显式触发(模拟子代理自然完成/出错);`interrupt` 自动 settle(`aborted`)(模拟 DSH
|
|
11
|
+
* interrupt → 子代理静止 → settle 销毁)。settle 经 `sandore/subagent-settled` 事件
|
|
12
|
+
* 发射,供 workflow-engine 的 `awaitSubagentSettlement`(`ctx.on('sandore/subagent-settled')`)
|
|
13
|
+
* 路由到对应 childId 的等待者——与真实 DSH `subagent/end`→`sandore/subagent-settled`
|
|
14
|
+
* 转译同一通道。`followup`(settle 后)模拟 cold-resume:状态回 `running`,等待测试
|
|
15
|
+
* 再次 `settleChild`(续跑产出)。
|
|
16
|
+
* @module dsh-sandore/workflow/tests/mocks/subagents
|
|
17
|
+
*/
|
|
18
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
19
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
20
|
+
|
|
21
|
+
import type {
|
|
22
|
+
Agent,
|
|
23
|
+
ContinuableSetupContribution,
|
|
24
|
+
ContinuableStart,
|
|
25
|
+
ContinuableStartSpec,
|
|
26
|
+
MessageId,
|
|
27
|
+
SessionId,
|
|
28
|
+
SubagentFollowupOptions,
|
|
29
|
+
SubagentInterruptAuthority,
|
|
30
|
+
SubagentListEntry,
|
|
31
|
+
SubagentResult,
|
|
32
|
+
SubagentRun,
|
|
33
|
+
SubagentRuntime,
|
|
34
|
+
SubagentSettlement,
|
|
35
|
+
SubagentStartRequest,
|
|
36
|
+
SubagentStopReason,
|
|
37
|
+
} from '../../microflow/subagents';
|
|
38
|
+
|
|
39
|
+
/** 一次 `followup` 调用的记录。 */
|
|
40
|
+
export interface FakeFollowup {
|
|
41
|
+
parent: Agent;
|
|
42
|
+
childId: SessionId;
|
|
43
|
+
content: ContentBlock[];
|
|
44
|
+
options: SubagentFollowupOptions;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 可续接子代理状态机状态:`running` 活跃、`interrupted` 被 interrupt(已 settle aborted)。 */
|
|
48
|
+
export type FakeChildStatus = 'running' | 'interrupted';
|
|
49
|
+
|
|
50
|
+
/** 内存 mock subagents:记录调用、维护 interrupt→settle 状态机、发射 settle 事件。 */
|
|
51
|
+
export class FakeSubagents implements SubagentRuntime {
|
|
52
|
+
readonly continuableStarts: ContinuableStartSpec[] = [];
|
|
53
|
+
readonly followups: FakeFollowup[] = [];
|
|
54
|
+
readonly interrupts: Array<{ targetSessionId: SessionId; authority: SubagentInterruptAuthority }> = [];
|
|
55
|
+
readonly startCalls: Array<{ name: string; request: SubagentStartRequest }> = [];
|
|
56
|
+
readonly listChildrenCalls: Array<{ parentSessionId: SessionId; signal?: AbortSignal }> = [];
|
|
57
|
+
/** 已注册的 continuable setup 贡献(registerContinuableSetup 记录,供 gate 断言)。 */
|
|
58
|
+
readonly continuableSetups: ContinuableSetupContribution[] = [];
|
|
59
|
+
/** 已发射的 settle 事件载荷(按发射顺序,供断言)。 */
|
|
60
|
+
readonly settlements: Array<SubagentSettlement> = [];
|
|
61
|
+
/** 可选:followup 抛错注入(缺陷 11 测试:recover() followup 失败落 failed)。 */
|
|
62
|
+
followupError: Error | undefined = undefined;
|
|
63
|
+
private readonly childParent = new Map<SessionId, SessionId>();
|
|
64
|
+
private readonly childStates = new Map<SessionId, FakeChildStatus>();
|
|
65
|
+
|
|
66
|
+
constructor(
|
|
67
|
+
private readonly ctx?: Context,
|
|
68
|
+
/** 取活子代理(auto-settle 判定 status 用;测试 harness 注入 `(id) => agents.get(id)`)。 */
|
|
69
|
+
private readonly getAgent?: (id: SessionId) => { readonly status: 'idle' | 'running'; readonly session: { readonly events: readonly { type: string }[] } } | undefined,
|
|
70
|
+
) {}
|
|
71
|
+
|
|
72
|
+
async startContinuable(spec: ContinuableStartSpec): Promise<ContinuableStart> {
|
|
73
|
+
// 本 mock 有意不校验 `spec.provider` 是否为已注册的 subagent provider 名(宽松接受
|
|
74
|
+
// 任意字符串,供各 spec 以任意 provider 启动)。真实 DSH 只认注册名(fork/spawn),
|
|
75
|
+
// 把模型 provider id 当 subagent provider 名传入会在运行时报
|
|
76
|
+
// `no subagent provider registered for "<model provider id>"`——该约束由
|
|
77
|
+
// step-runner.spec.ts 对 `continuableStarts[0].provider === 'fork'` 的显式断言覆盖。
|
|
78
|
+
this.continuableStarts.push(spec);
|
|
79
|
+
const index = this.continuableStarts.length;
|
|
80
|
+
const childId = `child-${index}`;
|
|
81
|
+
this.childParent.set(childId, spec.request.parent.id);
|
|
82
|
+
this.childStates.set(childId, 'running');
|
|
83
|
+
// 真实 DSH:子代理静止(idle + 无 owned children + 无 accepted)即 settle。mock 无真实
|
|
84
|
+
// LLM,以 `child.status === 'idle'` 为「子代理已跑完」代理信号——`startContinuable` 后
|
|
85
|
+
// 若 child 已 idle(测试预填输出 / 空输出跑完)即自动 settle(completed);`running`(挂起
|
|
86
|
+
// /暂停测试)则不自动 settle,由测试 `settleChild`/`interrupt` 驱动。经 `sandore/
|
|
87
|
+
// subagent-settled` 事件 + 引擎缓冲路由(buffer 解决早于 awaiter 的竞态)。
|
|
88
|
+
this.maybeAutoSettle(childId);
|
|
89
|
+
return { childId, messageId: `msg-${index}` };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async followup(parent: Agent, childId: SessionId, content: ContentBlock[], options: SubagentFollowupOptions): Promise<MessageId> {
|
|
93
|
+
if (this.followupError !== undefined) {
|
|
94
|
+
throw this.followupError;
|
|
95
|
+
}
|
|
96
|
+
this.followups.push({ parent, childId, content, options });
|
|
97
|
+
// cold-resume 仿真:settle 后的 followup 重建 Activation,状态回 running。
|
|
98
|
+
this.childStates.set(childId, 'running');
|
|
99
|
+
// 若 child 已 idle(续跑产出 / 空输出跑完),自动 settle(completed) 模拟续跑完成。
|
|
100
|
+
this.maybeAutoSettle(childId);
|
|
101
|
+
return `msg-${this.followups.length}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interrupt(targetSessionId: SessionId, authority: SubagentInterruptAuthority): void {
|
|
105
|
+
this.interrupts.push({ targetSessionId, authority });
|
|
106
|
+
this.childStates.set(targetSessionId, 'interrupted');
|
|
107
|
+
// 模拟 DSH interrupt → 子代理静止 → settle(aborted):自动发射 settle 事件。
|
|
108
|
+
// 真实 DSH 中 `SubagentContinuationManager.interrupt` 发 `agent.cancel` 后,
|
|
109
|
+
// `watchSettlement` 观察到静止即 dispose + notifySettlement(aborted) + emit subagent/end。
|
|
110
|
+
this.emitSettlement({ childId: targetSessionId, stopReason: 'aborted' });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async listChildren(parentSessionId: SessionId, signal?: AbortSignal): Promise<SubagentListEntry[]> {
|
|
114
|
+
this.listChildrenCalls.push(signal === undefined ? { parentSessionId } : { parentSessionId, signal });
|
|
115
|
+
const entries: SubagentListEntry[] = [];
|
|
116
|
+
for (const [id, state] of this.childStates) {
|
|
117
|
+
if (this.childParent.get(id) !== parentSessionId) continue;
|
|
118
|
+
entries.push({
|
|
119
|
+
kind: 'child',
|
|
120
|
+
id,
|
|
121
|
+
activity: state === 'running' ? 'running' : 'inactive',
|
|
122
|
+
hasChildren: false,
|
|
123
|
+
mode: 'continuable',
|
|
124
|
+
label: 'test child',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return entries;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** 记录注册的 continuable setup 贡献(不实际安装;测试可手动调用贡献模拟 child 安装)。 */
|
|
131
|
+
registerContinuableSetup(contribution: ContinuableSetupContribution): () => void {
|
|
132
|
+
this.continuableSetups.push(contribution);
|
|
133
|
+
return () => {};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** 查询某可续接子代理当前状态(`startContinuable` 后为 running,interrupt 后为 interrupted)。 */
|
|
137
|
+
childStatus(childId: SessionId): FakeChildStatus | undefined {
|
|
138
|
+
return this.childStates.get(childId);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 测试手动触发子代理 settle(模拟自然完成/出错)。发射 `sandore/subagent-settled`
|
|
143
|
+
* 事件,供 workflow-engine `awaitSubagentSettlement` 路由。`output` 缺省时由
|
|
144
|
+
* `collectSubagentOutput` 回退 `finalAssistantOutput(child.session.events)` 读取。
|
|
145
|
+
*/
|
|
146
|
+
settleChild(childId: SessionId, stopReason: SubagentStopReason, output?: readonly ContentBlock[]): void {
|
|
147
|
+
this.childStates.set(childId, stopReason === 'completed' ? 'running' : 'interrupted');
|
|
148
|
+
this.emitSettlement({
|
|
149
|
+
childId,
|
|
150
|
+
stopReason,
|
|
151
|
+
...(output === undefined ? {} : { output }),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** 发射 `sandore/subagent-settled` 事件(与 DSH 适配层 `subagent/end`→本事件同通道)。 */
|
|
156
|
+
private emitSettlement(settlement: SubagentSettlement): void {
|
|
157
|
+
this.settlements.push(settlement);
|
|
158
|
+
if (this.ctx === undefined) return;
|
|
159
|
+
// 与 sandore `emitContained` 同形:unscoped dispatch,同 ctx 的 `ctx.on` 监听器接收。
|
|
160
|
+
for (const callback of this.ctx.events.dispatch('emit', ['sandore/subagent-settled', settlement])) {
|
|
161
|
+
try {
|
|
162
|
+
const returned: unknown = callback(settlement);
|
|
163
|
+
void Promise.resolve(returned).catch(() => {});
|
|
164
|
+
} catch {
|
|
165
|
+
// 监听器异常不应影响 mock 状态机。
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 若 `child.status === 'idle'`(子代理已跑完),自动 settle(completed) 模拟真实 DSH
|
|
172
|
+
* 子代理静止即 settle。经微任务延迟(让 collectSubagentOutput 先注册 awaiter 或落入引擎
|
|
173
|
+
* 缓冲)。`running`(挂起/暂停测试)则不自动 settle——由测试 `settleChild`/`interrupt` 驱动。
|
|
174
|
+
*/
|
|
175
|
+
private maybeAutoSettle(childId: SessionId): void {
|
|
176
|
+
if (this.getAgent === undefined) return;
|
|
177
|
+
const child = this.getAgent(childId);
|
|
178
|
+
if (child === undefined || child.status !== 'idle') return;
|
|
179
|
+
queueMicrotask(() => {
|
|
180
|
+
const latest = this.getAgent?.(childId);
|
|
181
|
+
if (latest !== undefined && latest.status === 'idle') {
|
|
182
|
+
this.settleChild(childId, 'completed');
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async start(name: string, request: SubagentStartRequest): Promise<SubagentRun> {
|
|
188
|
+
this.startCalls.push({ name, request });
|
|
189
|
+
const result: SubagentResult = { output: [], stopReason: 'completed' };
|
|
190
|
+
return {
|
|
191
|
+
id: `run-${this.startCalls.length}`,
|
|
192
|
+
localAgent: undefined,
|
|
193
|
+
result: Promise.resolve(result),
|
|
194
|
+
dispose: async () => {},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内存 mock subprocess(测试专用,非发布面)。
|
|
3
|
+
*
|
|
4
|
+
* 提供 `resolveExecutable`/`spawn` 等价语义:`resolveExecutable` 记录命令并返回规范
|
|
5
|
+
* 路径,`spawn` 记录规格并返回可注入行为的内存句柄。以 `ctx.provide('subprocess', …)`
|
|
6
|
+
* 注入。
|
|
7
|
+
* @module dsh-sandore/workflow/tests/mocks/subprocess
|
|
8
|
+
*/
|
|
9
|
+
import type {
|
|
10
|
+
SubprocessHandle,
|
|
11
|
+
SubprocessOutcome,
|
|
12
|
+
SubprocessRuntime,
|
|
13
|
+
SubprocessSpawnSpec,
|
|
14
|
+
} from '../../workflow/shell.ts';
|
|
15
|
+
|
|
16
|
+
/** 生成一次成功(exitCode 0)的子进程结果。 */
|
|
17
|
+
export function successOutcome(): SubprocessOutcome {
|
|
18
|
+
return { exitCode: 0, signal: null };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 内存子进程句柄:`done` 由 `outcomeHandler` 决定,`terminate`/`waitForExit` 记录调用。 */
|
|
22
|
+
export class FakeSubprocessHandle implements SubprocessHandle {
|
|
23
|
+
readonly pid: number;
|
|
24
|
+
readonly collected: SubprocessHandle['collected'];
|
|
25
|
+
readonly done: Promise<SubprocessOutcome>;
|
|
26
|
+
terminated = false;
|
|
27
|
+
|
|
28
|
+
constructor(pid: number, outcomeHandler: () => SubprocessOutcome = successOutcome) {
|
|
29
|
+
this.pid = pid;
|
|
30
|
+
this.collected = {};
|
|
31
|
+
this.done = Promise.resolve(outcomeHandler());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
terminate(): void {
|
|
35
|
+
this.terminated = true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async waitForExit(): Promise<boolean> {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 内存 mock subprocess:记录 resolve/spawn 调用并可注入行为。 */
|
|
44
|
+
export class FakeSubprocess implements SubprocessRuntime {
|
|
45
|
+
readonly resolved: string[] = [];
|
|
46
|
+
readonly specs: SubprocessSpawnSpec[] = [];
|
|
47
|
+
resolveHandler: (command: string) => string = command => `/usr/bin/${command}`;
|
|
48
|
+
outcomeHandler: () => SubprocessOutcome = successOutcome;
|
|
49
|
+
|
|
50
|
+
async resolveExecutable(command: string): Promise<string> {
|
|
51
|
+
this.resolved.push(command);
|
|
52
|
+
return this.resolveHandler(command);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
spawn(spec: SubprocessSpawnSpec): SubprocessHandle {
|
|
56
|
+
this.specs.push(spec);
|
|
57
|
+
return new FakeSubprocessHandle(this.specs.length, this.outcomeHandler);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内存 mock userQuestions(测试专用,非发布面)。
|
|
3
|
+
*
|
|
4
|
+
* 提供 `ask` 等价语义:记录每次调用,支持「可编程 handler」与「答案队列」两种
|
|
5
|
+
* 回复注入;无 handler 且队列空时挂起(等待引擎 `replyQuestion` 兼容路径或测试
|
|
6
|
+
* 手动 resolve)。以 `ctx.provide('userQuestions', …)` 注入。
|
|
7
|
+
* @module dsh-sandore/workflow/tests/mocks/user-questions
|
|
8
|
+
*/
|
|
9
|
+
import type { AskUserQuestionAnswer } from '../../workflow/types.ts';
|
|
10
|
+
import type { UserQuestions, UserQuestionsRequest } from '../../workflow/seam.ts';
|
|
11
|
+
|
|
12
|
+
/** 内存 mock userQuestions:记录调用并支持可编程回复。 */
|
|
13
|
+
export class FakeUserQuestions implements UserQuestions {
|
|
14
|
+
/** 每次 `ask` 调用的入参记录。 */
|
|
15
|
+
readonly calls: UserQuestionsRequest[] = [];
|
|
16
|
+
/** 待回复答案队列(`ask` 依次弹出)。 */
|
|
17
|
+
readonly answers: AskUserQuestionAnswer[] = [];
|
|
18
|
+
/** 可编程回复 handler(优先级高于答案队列)。 */
|
|
19
|
+
resolveNext?: (request: UserQuestionsRequest) => Promise<AskUserQuestionAnswer>;
|
|
20
|
+
|
|
21
|
+
ask(request: UserQuestionsRequest): Promise<AskUserQuestionAnswer> {
|
|
22
|
+
this.calls.push(request);
|
|
23
|
+
if (this.resolveNext !== undefined) {
|
|
24
|
+
return this.resolveNext(request);
|
|
25
|
+
}
|
|
26
|
+
const answer = this.answers.shift();
|
|
27
|
+
if (answer !== undefined) {
|
|
28
|
+
return Promise.resolve(answer);
|
|
29
|
+
}
|
|
30
|
+
// 无编程回复:挂起(永不 resolve),由兼容路径或测试手动驱动。
|
|
31
|
+
return new Promise<AskUserQuestionAnswer>(() => {});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内存 mock workspace(测试专用,非发布面)。
|
|
3
|
+
*
|
|
4
|
+
* 对齐引擎接缝类型 `WorkspaceRegistrySeam`/`WorkspaceSeam`(签名同 DSH
|
|
5
|
+
* `WorkspaceRegistry.resolveByPath` / `WorkspaceRegistry.create` /
|
|
6
|
+
* `Workspace.attachSession`):FakeWorkspaceRegistry 按路径 Map 解析,
|
|
7
|
+
* `create` 把路径登记为新 workspace(mock 不做目录存在性校验——校验是 DSH
|
|
8
|
+
* 真实实体职责),FakeWorkspace 只记录 attach 的会话 id(不做 header cwd
|
|
9
|
+
* 校验——校验是 DSH 真实实体职责,mock 只验证引擎的 attach 调用面)。以
|
|
10
|
+
* `ctx.provide('workspaceRegistry', …)` 注入。
|
|
11
|
+
* @module dsh-sandore/workflow/tests/mocks/workspace
|
|
12
|
+
*/
|
|
13
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
14
|
+
|
|
15
|
+
/** 单个 fake workspace:`attachSession` 记录会话 id(不校验 header cwd)。 */
|
|
16
|
+
export class FakeWorkspace {
|
|
17
|
+
/** 已 attach 的会话 id(按调用顺序)。 */
|
|
18
|
+
readonly attached: SessionId[] = [];
|
|
19
|
+
|
|
20
|
+
/** @param path - 该 workspace 的规范目录路径(`resolveByPath` 的匹配键)。 */
|
|
21
|
+
constructor(readonly path: string) {}
|
|
22
|
+
|
|
23
|
+
attachSession(sessionId: SessionId): Promise<void> {
|
|
24
|
+
this.attached.push(sessionId);
|
|
25
|
+
return Promise.resolve();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** fake workspace 注册表:按路径 Map 解析(未命中返回 undefined)。 */
|
|
30
|
+
export class FakeWorkspaceRegistry {
|
|
31
|
+
private readonly byPath = new Map<string, FakeWorkspace>();
|
|
32
|
+
|
|
33
|
+
/** @param workspaces - 登记进注册表的 fake workspace 列表。 */
|
|
34
|
+
constructor(workspaces: readonly FakeWorkspace[]) {
|
|
35
|
+
for (const workspace of workspaces) {
|
|
36
|
+
this.byPath.set(workspace.path, workspace);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
resolveByPath(path: string): Promise<FakeWorkspace | undefined> {
|
|
41
|
+
return Promise.resolve(this.byPath.get(path));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 按路径登记一个新 workspace(已存在则返回原实体,对齐 DSH create 语义)。 */
|
|
45
|
+
create(path: string): Promise<FakeWorkspace> {
|
|
46
|
+
const existing = this.byPath.get(path);
|
|
47
|
+
if (existing !== undefined) return Promise.resolve(existing);
|
|
48
|
+
const workspace = new FakeWorkspace(path);
|
|
49
|
+
this.byPath.set(path, workspace);
|
|
50
|
+
return Promise.resolve(workspace);
|
|
51
|
+
}
|
|
52
|
+
}
|