@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.
Files changed (295) hide show
  1. package/LICENSE +201 -0
  2. package/cordis.patch.yml +39 -0
  3. package/dsh.plugin.json +16 -0
  4. package/lib/client/CommandEditor.d.ts +19 -0
  5. package/lib/client/DagEditor.d.ts +22 -0
  6. package/lib/client/DefinitionDetail.d.ts +13 -0
  7. package/lib/client/DefinitionList.d.ts +19 -0
  8. package/lib/client/DiscussionInput.d.ts +63 -0
  9. package/lib/client/DragHandle.d.ts +16 -0
  10. package/lib/client/EditorPane.d.ts +13 -0
  11. package/lib/client/FlowEditor.d.ts +31 -0
  12. package/lib/client/InstanceList.d.ts +24 -0
  13. package/lib/client/NewInstanceDialog.d.ts +19 -0
  14. package/lib/client/PreviewPanel.d.ts +13 -0
  15. package/lib/client/RunView.d.ts +79 -0
  16. package/lib/client/StepChatPanel.d.ts +34 -0
  17. package/lib/client/TreeNav.d.ts +27 -0
  18. package/lib/client/UnifiedTimeline.d.ts +11 -0
  19. package/lib/client/WorkflowButton.d.ts +11 -0
  20. package/lib/client/WorkflowList.d.ts +18 -0
  21. package/lib/client/WorkflowOverlay.d.ts +6 -0
  22. package/lib/client/chat/AssistantMarkdown.d.ts +22 -0
  23. package/lib/client/chat/CompactionItem.d.ts +24 -0
  24. package/lib/client/chat/ContextBody.d.ts +136 -0
  25. package/lib/client/chat/ContextInjectionRow.d.ts +25 -0
  26. package/lib/client/chat/GenericCommandCard.d.ts +10 -0
  27. package/lib/client/chat/MessageIconActions.d.ts +41 -0
  28. package/lib/client/chat/MessageItem.d.ts +65 -0
  29. package/lib/client/chat/ReasoningRow.d.ts +13 -0
  30. package/lib/client/chat/TurnTailNodeView.d.ts +29 -0
  31. package/lib/client/chat/UserMessageRow.d.ts +18 -0
  32. package/lib/client/chat/attachment/ImageLightbox.d.ts +26 -0
  33. package/lib/client/chat/attachment/MessageImage.d.ts +56 -0
  34. package/lib/client/chat/attachment/image-attachment.d.ts +41 -0
  35. package/lib/client/chat/attachment/image-labels.d.ts +21 -0
  36. package/lib/client/chat/message-chrome.d.ts +51 -0
  37. package/lib/client/chat/reference/ReferenceIcon.d.ts +20 -0
  38. package/lib/client/chat/turn-assistant.d.ts +12 -0
  39. package/lib/client/chat/turn-metrics.d.ts +42 -0
  40. package/lib/client/chat/use-calendar-day.d.ts +5 -0
  41. package/lib/client/chat/use-throttled-visual-update.d.ts +7 -0
  42. package/lib/client/chat-flow.d.ts +110 -0
  43. package/lib/client/composer-selects.d.ts +17 -0
  44. package/lib/client/dag-editor-model.d.ts +141 -0
  45. package/lib/client/describe-error.d.ts +9 -0
  46. package/lib/client/index.d.ts +6 -0
  47. package/lib/client/locales.d.ts +777 -0
  48. package/lib/client/model-faces.d.ts +156 -0
  49. package/lib/client/open-discussion.d.ts +24 -0
  50. package/lib/client/panel-model.d.ts +34 -0
  51. package/lib/client/remote.d.ts +34 -0
  52. package/lib/client/run-view-model.d.ts +304 -0
  53. package/lib/client/sandore-save.d.ts +41 -0
  54. package/lib/client/sandore-sse.d.ts +15 -0
  55. package/lib/client/step-chat-retry.d.ts +44 -0
  56. package/lib/client/tool/GenericToolCard.d.ts +28 -0
  57. package/lib/client/tool/ToolRow.d.ts +95 -0
  58. package/lib/client/tool/diff-card-model.d.ts +63 -0
  59. package/lib/client/tool/read-card-model.d.ts +67 -0
  60. package/lib/client/tool/search-card-model.d.ts +94 -0
  61. package/lib/client/tool/terminal-card-model.d.ts +86 -0
  62. package/lib/client/tool/tool-call-model.d.ts +74 -0
  63. package/lib/client/tool/web-card-model.d.ts +44 -0
  64. package/lib/client/use-focus-refresh.d.ts +1 -0
  65. package/lib/client/workflow-face.d.ts +68 -0
  66. package/lib/client/workflow-store.d.ts +80 -0
  67. package/lib/client/workspace-path.d.ts +15 -0
  68. package/lib/client.js +38389 -0
  69. package/lib/index.js +8854 -0
  70. package/lib/microflow/branch-evaluator.d.ts +290 -0
  71. package/lib/microflow/concurrency-limiter.d.ts +39 -0
  72. package/lib/microflow/errors.d.ts +39 -0
  73. package/lib/microflow/file-utils.d.ts +210 -0
  74. package/lib/microflow/finalize-manager.d.ts +414 -0
  75. package/lib/microflow/fork-session.d.ts +151 -0
  76. package/lib/microflow/index.d.ts +17 -0
  77. package/lib/microflow/internal.d.ts +56 -0
  78. package/lib/microflow/loop-manager.d.ts +127 -0
  79. package/lib/microflow/rules-resolver.d.ts +28 -0
  80. package/lib/microflow/stage-executor.d.ts +362 -0
  81. package/lib/microflow/step-navigator.d.ts +108 -0
  82. package/lib/microflow/step-runner.d.ts +190 -0
  83. package/lib/microflow/subagents.d.ts +380 -0
  84. package/lib/tool/index.d.ts +65 -0
  85. package/lib/typert.host.d.ts +16 -0
  86. package/lib/typert.host.js +25 -0
  87. package/lib/typert.remote-client.d.ts +114 -0
  88. package/lib/typert.remote-client.js +399 -0
  89. package/lib/workflow/ask-tool.d.ts +78 -0
  90. package/lib/workflow/dag-navigator.d.ts +136 -0
  91. package/lib/workflow/domain.d.ts +304 -0
  92. package/lib/workflow/engine-surface.d.ts +70 -0
  93. package/lib/workflow/errors.d.ts +13 -0
  94. package/lib/workflow/host-agent.d.ts +53 -0
  95. package/lib/workflow/index.d.ts +23 -0
  96. package/lib/workflow/interaction-manager.d.ts +170 -0
  97. package/lib/workflow/lifecycle-manager.d.ts +111 -0
  98. package/lib/workflow/persistence.d.ts +35 -0
  99. package/lib/workflow/plugin.d.ts +27 -0
  100. package/lib/workflow/reactive-state.d.ts +27 -0
  101. package/lib/workflow/recovery-manager.d.ts +158 -0
  102. package/lib/workflow/remote.d.ts +139 -0
  103. package/lib/workflow/schema.d.ts +1917 -0
  104. package/lib/workflow/seam.d.ts +254 -0
  105. package/lib/workflow/shell.d.ts +127 -0
  106. package/lib/workflow/sse.d.ts +16 -0
  107. package/lib/workflow/typert/typert.host.d.ts +16 -0
  108. package/lib/workflow/typert/typert.remote-client.d.ts +114 -0
  109. package/lib/workflow/types.d.ts +187 -0
  110. package/lib/workflow/wire.d.ts +256 -0
  111. package/lib/workflow/workflow-engine.d.ts +373 -0
  112. package/lib/workflow/yaml-error.d.ts +22 -0
  113. package/lib/workflow/yaml-parser.d.ts +23 -0
  114. package/package.json +140 -0
  115. package/packages/client/CommandEditor.tsx +73 -0
  116. package/packages/client/DagEditor.module.css +711 -0
  117. package/packages/client/DagEditor.tsx +1203 -0
  118. package/packages/client/DefinitionDetail.module.css +110 -0
  119. package/packages/client/DefinitionDetail.tsx +126 -0
  120. package/packages/client/DefinitionList.tsx +173 -0
  121. package/packages/client/DiscussionInput.tsx +763 -0
  122. package/packages/client/DragHandle.tsx +92 -0
  123. package/packages/client/EditorPane.tsx +70 -0
  124. package/packages/client/FlowEditor.tsx +227 -0
  125. package/packages/client/InstanceList.tsx +250 -0
  126. package/packages/client/NewInstanceDialog.module.css +244 -0
  127. package/packages/client/NewInstanceDialog.tsx +478 -0
  128. package/packages/client/PreviewPanel.tsx +55 -0
  129. package/packages/client/RunView.module.css +246 -0
  130. package/packages/client/RunView.tsx +314 -0
  131. package/packages/client/StepChatPanel.tsx +486 -0
  132. package/packages/client/TreeNav.tsx +294 -0
  133. package/packages/client/UnifiedTimeline.tsx +484 -0
  134. package/packages/client/WorkflowButton.module.css +72 -0
  135. package/packages/client/WorkflowButton.tsx +50 -0
  136. package/packages/client/WorkflowList.tsx +158 -0
  137. package/packages/client/WorkflowOverlay.module.css +390 -0
  138. package/packages/client/WorkflowOverlay.tsx +136 -0
  139. package/packages/client/WorkflowPanel.module.css +247 -0
  140. package/packages/client/chat/AssistantMarkdown.module.css +38 -0
  141. package/packages/client/chat/AssistantMarkdown.tsx +120 -0
  142. package/packages/client/chat/ChatView.module.css +198 -0
  143. package/packages/client/chat/CompactionItem.tsx +85 -0
  144. package/packages/client/chat/ContextBody.module.css +162 -0
  145. package/packages/client/chat/ContextBody.tsx +598 -0
  146. package/packages/client/chat/ContextInjectionRow.module.css +63 -0
  147. package/packages/client/chat/ContextInjectionRow.tsx +78 -0
  148. package/packages/client/chat/GenericCommandCard.module.css +87 -0
  149. package/packages/client/chat/GenericCommandCard.tsx +76 -0
  150. package/packages/client/chat/MessageIconActions.module.css +84 -0
  151. package/packages/client/chat/MessageIconActions.tsx +147 -0
  152. package/packages/client/chat/MessageItem.module.css +293 -0
  153. package/packages/client/chat/MessageItem.tsx +384 -0
  154. package/packages/client/chat/ReasoningRow.module.css +82 -0
  155. package/packages/client/chat/ReasoningRow.tsx +70 -0
  156. package/packages/client/chat/TurnTailNodeView.module.css +10 -0
  157. package/packages/client/chat/TurnTailNodeView.tsx +63 -0
  158. package/packages/client/chat/UserMessageRow.module.css +75 -0
  159. package/packages/client/chat/UserMessageRow.tsx +92 -0
  160. package/packages/client/chat/accessibility.module.css +9 -0
  161. package/packages/client/chat/attachment/ImageLightbox.module.css +45 -0
  162. package/packages/client/chat/attachment/ImageLightbox.tsx +70 -0
  163. package/packages/client/chat/attachment/MessageImage.module.css +70 -0
  164. package/packages/client/chat/attachment/MessageImage.tsx +136 -0
  165. package/packages/client/chat/attachment/image-attachment.ts +98 -0
  166. package/packages/client/chat/attachment/image-labels.ts +34 -0
  167. package/packages/client/chat/message-chrome.ts +96 -0
  168. package/packages/client/chat/reference/ReferenceIcon.tsx +40 -0
  169. package/packages/client/chat/turn-assistant.ts +15 -0
  170. package/packages/client/chat/turn-metrics.ts +102 -0
  171. package/packages/client/chat/use-calendar-day.ts +27 -0
  172. package/packages/client/chat/use-throttled-visual-update.ts +44 -0
  173. package/packages/client/chat-flow.spec.ts +357 -0
  174. package/packages/client/chat-flow.ts +278 -0
  175. package/packages/client/composer-selects.module.css +376 -0
  176. package/packages/client/composer-selects.tsx +531 -0
  177. package/packages/client/dag-editor-model.spec.ts +173 -0
  178. package/packages/client/dag-editor-model.ts +359 -0
  179. package/packages/client/describe-error.spec.ts +18 -0
  180. package/packages/client/describe-error.ts +12 -0
  181. package/packages/client/index.ts +144 -0
  182. package/packages/client/input.module.css +854 -0
  183. package/packages/client/is-conditional-check.spec.ts +34 -0
  184. package/packages/client/locales.ts +789 -0
  185. package/packages/client/model-faces.ts +225 -0
  186. package/packages/client/open-discussion.spec.ts +39 -0
  187. package/packages/client/open-discussion.ts +32 -0
  188. package/packages/client/panel-model.spec.ts +152 -0
  189. package/packages/client/panel-model.ts +72 -0
  190. package/packages/client/preview.module.css +95 -0
  191. package/packages/client/react-dom.d.ts +11 -0
  192. package/packages/client/reactflow-theme.css +91 -0
  193. package/packages/client/remote.spec.ts +41 -0
  194. package/packages/client/remote.ts +46 -0
  195. package/packages/client/run-view-model.spec.ts +641 -0
  196. package/packages/client/run-view-model.ts +636 -0
  197. package/packages/client/sandore-save.spec.ts +146 -0
  198. package/packages/client/sandore-save.ts +97 -0
  199. package/packages/client/sandore-sse.ts +59 -0
  200. package/packages/client/step-chat-retry.spec.ts +70 -0
  201. package/packages/client/step-chat-retry.ts +65 -0
  202. package/packages/client/timeline.module.css +503 -0
  203. package/packages/client/tool/GenericToolCard.tsx +99 -0
  204. package/packages/client/tool/ToolRow.module.css +308 -0
  205. package/packages/client/tool/ToolRow.tsx +328 -0
  206. package/packages/client/tool/diff-card-model.ts +103 -0
  207. package/packages/client/tool/read-card-model.ts +86 -0
  208. package/packages/client/tool/search-card-model.ts +166 -0
  209. package/packages/client/tool/terminal-card-model.ts +225 -0
  210. package/packages/client/tool/tool-call-model.ts +267 -0
  211. package/packages/client/tool/web-card-model.ts +80 -0
  212. package/packages/client/tree-nav.module.css +282 -0
  213. package/packages/client/use-focus-refresh.ts +23 -0
  214. package/packages/client/workflow-face.ts +72 -0
  215. package/packages/client/workflow-store.spec.ts +49 -0
  216. package/packages/client/workflow-store.ts +161 -0
  217. package/packages/client/workspace-path.ts +27 -0
  218. package/packages/css-modules.d.ts +6 -0
  219. package/packages/microflow/branch-evaluator.spec.ts +745 -0
  220. package/packages/microflow/branch-evaluator.ts +1037 -0
  221. package/packages/microflow/concurrency-limiter.ts +70 -0
  222. package/packages/microflow/errors.ts +64 -0
  223. package/packages/microflow/file-utils.spec.ts +604 -0
  224. package/packages/microflow/file-utils.ts +521 -0
  225. package/packages/microflow/finalize-manager.spec.ts +1977 -0
  226. package/packages/microflow/finalize-manager.ts +1464 -0
  227. package/packages/microflow/fork-session.spec.ts +184 -0
  228. package/packages/microflow/fork-session.ts +316 -0
  229. package/packages/microflow/index.ts +17 -0
  230. package/packages/microflow/internal.ts +166 -0
  231. package/packages/microflow/loop-manager.spec.ts +294 -0
  232. package/packages/microflow/loop-manager.ts +255 -0
  233. package/packages/microflow/rules-resolver.spec.ts +147 -0
  234. package/packages/microflow/rules-resolver.ts +58 -0
  235. package/packages/microflow/stage-executor.spec.ts +1577 -0
  236. package/packages/microflow/stage-executor.ts +1478 -0
  237. package/packages/microflow/step-navigator.spec.ts +301 -0
  238. package/packages/microflow/step-navigator.ts +300 -0
  239. package/packages/microflow/step-runner.spec.ts +1132 -0
  240. package/packages/microflow/step-runner.ts +752 -0
  241. package/packages/microflow/subagents.spec.ts +215 -0
  242. package/packages/microflow/subagents.ts +422 -0
  243. package/packages/tests/fixtures/test-workflow.yaml +128 -0
  244. package/packages/tests/mocks/agents.ts +206 -0
  245. package/packages/tests/mocks/llm.ts +46 -0
  246. package/packages/tests/mocks/session-title.ts +31 -0
  247. package/packages/tests/mocks/sessions.ts +131 -0
  248. package/packages/tests/mocks/shell.ts +60 -0
  249. package/packages/tests/mocks/storage-domain.ts +101 -0
  250. package/packages/tests/mocks/subagents.ts +197 -0
  251. package/packages/tests/mocks/subprocess.ts +59 -0
  252. package/packages/tests/mocks/user-questions.ts +33 -0
  253. package/packages/tests/mocks/workspace.ts +52 -0
  254. package/packages/tool/index.spec.ts +287 -0
  255. package/packages/tool/index.ts +348 -0
  256. package/packages/workflow/ask-tool.ts +173 -0
  257. package/packages/workflow/dag-navigator.spec.ts +236 -0
  258. package/packages/workflow/dag-navigator.ts +425 -0
  259. package/packages/workflow/domain.ts +26 -0
  260. package/packages/workflow/engine-surface.ts +71 -0
  261. package/packages/workflow/errors.ts +13 -0
  262. package/packages/workflow/host-agent.spec.ts +92 -0
  263. package/packages/workflow/host-agent.ts +62 -0
  264. package/packages/workflow/index.ts +25 -0
  265. package/packages/workflow/interaction-manager.spec.ts +390 -0
  266. package/packages/workflow/interaction-manager.ts +397 -0
  267. package/packages/workflow/lifecycle-manager.spec.ts +683 -0
  268. package/packages/workflow/lifecycle-manager.ts +288 -0
  269. package/packages/workflow/persistence.spec.ts +109 -0
  270. package/packages/workflow/persistence.ts +64 -0
  271. package/packages/workflow/plugin.ts +39 -0
  272. package/packages/workflow/reactive-state.spec.ts +69 -0
  273. package/packages/workflow/reactive-state.ts +52 -0
  274. package/packages/workflow/recovery-manager.spec.ts +606 -0
  275. package/packages/workflow/recovery-manager.ts +471 -0
  276. package/packages/workflow/remote.spec.ts +1045 -0
  277. package/packages/workflow/remote.ts +479 -0
  278. package/packages/workflow/schema.spec.ts +303 -0
  279. package/packages/workflow/schema.ts +562 -0
  280. package/packages/workflow/seam.ts +293 -0
  281. package/packages/workflow/shell.ts +142 -0
  282. package/packages/workflow/sse.ts +110 -0
  283. package/packages/workflow/typert/typert.host.d.ts +16 -0
  284. package/packages/workflow/typert/typert.host.js +25 -0
  285. package/packages/workflow/typert/typert.remote-client.d.ts +114 -0
  286. package/packages/workflow/typert/typert.remote-client.js +399 -0
  287. package/packages/workflow/types.ts +258 -0
  288. package/packages/workflow/wire.ts +267 -0
  289. package/packages/workflow/workflow-engine-e2e.spec.ts +262 -0
  290. package/packages/workflow/workflow-engine-real-e2e.spec.ts +922 -0
  291. package/packages/workflow/workflow-engine.spec.ts +4389 -0
  292. package/packages/workflow/workflow-engine.ts +2133 -0
  293. package/packages/workflow/yaml-error.ts +29 -0
  294. package/packages/workflow/yaml-parser.spec.ts +757 -0
  295. package/packages/workflow/yaml-parser.ts +287 -0
@@ -0,0 +1,479 @@
1
+ /**
2
+ * sandore Host RPC(Typert 协议)。
3
+ *
4
+ * `super(ctx, 'sandore')` 定 wire namespace,`@Remote('name')` 定 method,
5
+ * 构成端点 `sandore.<method>`。定义/Job CRUD 读写在 `ctx.sandorePersistence`
6
+ * 的三表上,实例生命周期(start/abort/reply/pause/resume/retry)转发
7
+ * `ctx.sandoreEngine`,Provider/Model 转发 `ctx.llm`。RPC 层只做 zod 参数校验
8
+ * + 转发/存储,无引擎业务逻辑。
9
+ * @module dsh-sandore/workflow/src/remote
10
+ */
11
+ import type { Context } from '@deepseek-ai/cordis'
12
+ import type { LlmModelInfo, LlmProviderInfo } from '@deepseek-ai/dsh-llm'
13
+ import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
14
+
15
+ import { SandoreError } from './errors.ts'
16
+ import type { AskUserQuestionAnswer, DagRun, DagStartRequest } from './types.ts'
17
+ import { parseWorkflowYaml, serializeWorkflowYaml } from './yaml-parser.ts'
18
+ import type { WorkflowDefinitionRecord, WorkflowTaskState } from './schema.ts'
19
+ import {
20
+ CreateDefinitionInputSchema,
21
+ ImportDefinitionInputSchema,
22
+ ListRevertBoundariesInputSchema,
23
+ PauseAllInputSchema,
24
+ PauseStepInputSchema,
25
+ RecoverInstanceInputSchema,
26
+ RejectQuestionInputSchema,
27
+ ReplyPermissionInputSchema,
28
+ ReplyQuestionInputSchema,
29
+ ResumeAllInputSchema,
30
+ ResumeStepInputSchema,
31
+ RetryStepInputSchema,
32
+ SendStepMessageInputSchema,
33
+ StartInstanceInputSchema,
34
+ UpdateDefinitionInputSchema,
35
+ UpdateInstancePermissionModeInputSchema,
36
+ } from './wire.ts'
37
+ import type {
38
+ ArtifactEntry,
39
+ CreateDefinitionInput,
40
+ ImportDefinitionInput,
41
+ ImportDefinitionResult,
42
+ InstanceSummary,
43
+ ListRevertBoundariesInput,
44
+ PauseAllInput,
45
+ PauseStepInput,
46
+ PingResponse,
47
+ RecoverInstanceInput,
48
+ RecoverOrphanedInstancesResult,
49
+ RejectQuestionInput,
50
+ ReplyPermissionInput,
51
+ ReplyQuestionInput,
52
+ ResumeAllInput,
53
+ ResumeStepInput,
54
+ RetryStepInput,
55
+ RevertBoundaryOption,
56
+ SendStepMessageInput,
57
+ StartInstanceInput,
58
+ UpdateDefinitionInput,
59
+ UpdateInstancePermissionModeInput,
60
+ WorkflowDefinitionEntry,
61
+ } from './wire.ts'
62
+
63
+ /** 实例的终态集合:`abortInstance` 对终态实例拒绝操作。 */
64
+ const TERMINAL_STATUSES: ReadonlySet<WorkflowTaskState['status']> = new Set(['completed', 'failed', 'aborted'])
65
+
66
+ /**
67
+ * sandore 的 Host RPC 服务。
68
+ *
69
+ * 定义/Job/实例 CRUD + 生命周期 RPC(reply/pause/resume/retry/recover)+ 产物查询
70
+ * + Provider/Model 转发;`ping` 保留。
71
+ */
72
+ export class SandoreRemote extends TypertRemoteService {
73
+ static inject = ['sandorePersistence', 'sandoreEngine', 'llm']
74
+
75
+ constructor(ctx: Context) {
76
+ super(ctx, 'sandore')
77
+ }
78
+
79
+ /** 健康检查往返,验证 Host/Client RPC 链路。 */
80
+ @Remote('ping')
81
+ ping(): PingResponse {
82
+ return { pong: true, service: 'sandore', time: new Date().toISOString() }
83
+ }
84
+
85
+ // ── 定义 CRUD ────────────────────────────────────────────────────────────
86
+
87
+ /** 列出全部工作流定义(含 `id` 键)。 */
88
+ @Remote('listDefinitions')
89
+ listDefinitions(): WorkflowDefinitionEntry[] {
90
+ const entries: WorkflowDefinitionEntry[] = []
91
+ for (const [id, record] of this.ctx.sandorePersistence.definitions.entries()) {
92
+ entries.push({ id, ...record })
93
+ }
94
+ return entries
95
+ }
96
+
97
+ /**
98
+ * 创建定义:解析+验证 YAML → 生成 `wfl-{uuid}` → 以规范 id 重新序列化存储。
99
+ * 非法 YAML 抛 `SandoreError`,`definitions` 表不新增记录。存储的 YAML 文本顶层
100
+ * `id` 与记录键一致(客户端传入的哨兵 id 如 `wfl-new` 不回写进持久化文本)。
101
+ */
102
+ @Remote('createDefinition')
103
+ async createDefinition(input: CreateDefinitionInput): Promise<WorkflowDefinitionEntry> {
104
+ const parsed = CreateDefinitionInputSchema.parse(input)
105
+ const definition = parseWorkflowYaml(parsed.yaml)
106
+ const id = `wfl-${globalThis.crypto.randomUUID()}`
107
+ const now = new Date().toISOString()
108
+ const record: WorkflowDefinitionRecord = {
109
+ yaml: serializeWorkflowYaml({ ...definition, id }),
110
+ name: definition.name,
111
+ version: definition.version,
112
+ ...(definition.description === undefined ? {} : { description: definition.description }),
113
+ createdAt: now,
114
+ updatedAt: now,
115
+ }
116
+ await this.ctx.sandorePersistence.definitions.put(id, record)
117
+ return { id, ...record }
118
+ }
119
+
120
+ /** 更新定义元数据(name/version/description),保留 yaml 文本。 */
121
+ @Remote('updateDefinition')
122
+ async updateDefinition(id: string, input: UpdateDefinitionInput): Promise<WorkflowDefinitionEntry> {
123
+ const parsed = UpdateDefinitionInputSchema.parse(input)
124
+ const existing = this.requireDefinition(id)
125
+ const now = new Date().toISOString()
126
+ const description = parsed.description ?? existing.description
127
+ const next: WorkflowDefinitionRecord = {
128
+ yaml: existing.yaml,
129
+ name: parsed.name ?? existing.name,
130
+ version: parsed.version ?? existing.version,
131
+ ...(description === undefined ? {} : { description }),
132
+ createdAt: existing.createdAt,
133
+ updatedAt: now,
134
+ }
135
+ await this.ctx.sandorePersistence.definitions.put(id, next)
136
+ return { id, ...next }
137
+ }
138
+
139
+ /** 删除定义(不级联删除实例);重复删除幂等(返回 false)。 */
140
+ @Remote('deleteDefinition')
141
+ async deleteDefinition(id: string): Promise<boolean> {
142
+ return await this.ctx.sandorePersistence.definitions.delete(id)
143
+ }
144
+
145
+ /** 读取定义的原始 YAML 文本。 */
146
+ @Remote('getYaml')
147
+ getYaml(id: string): string {
148
+ return this.requireDefinition(id).yaml
149
+ }
150
+
151
+ /** 重新验证后覆盖定义的 YAML 文本;非法 YAML 抛错且存储保持旧值。
152
+ * 元数据(name/version/description)从新 YAML 解析同步,保证列表展示与内容一致。 */
153
+ @Remote('updateYaml')
154
+ async updateYaml(id: string, yaml: string): Promise<WorkflowDefinitionEntry> {
155
+ const parsed = parseWorkflowYaml(yaml)
156
+ const existing = this.requireDefinition(id)
157
+ const now = new Date().toISOString()
158
+ const next: WorkflowDefinitionRecord = {
159
+ yaml,
160
+ name: parsed.name,
161
+ version: parsed.version,
162
+ ...(parsed.description === undefined ? {} : { description: parsed.description }),
163
+ createdAt: existing.createdAt,
164
+ updatedAt: now,
165
+ }
166
+ await this.ctx.sandorePersistence.definitions.put(id, next)
167
+ return { id, ...next }
168
+ }
169
+
170
+ /**
171
+ * 幂等导入工作流定义(缺陷 30):parseWorkflowYaml → 按 `matchBy` 在
172
+ * `definitions` 表查找现有 → 同版本 skipped / 不同版本 updateYaml / 不存在
173
+ * createDefinition。返回对齐基准 `{ created | updated | skipped }` 三态。
174
+ *
175
+ * 身份稳定性:`createDefinition` 强制生成新 `wfl-{uuid}` 并改写 YAML 顶层 id
176
+ * (remote.spec 锁定此行为),故默认 `matchBy:'name'`(应答文档缺陷 30 修复
177
+ * 计划点 2「关键前置」推荐后者——按 name 查找是唯一能在「复用 createDefinition/
178
+ * updateYaml 本体不改」约束下实现幂等导入的策略)。name 非唯一键:多匹配时取
179
+ * 最新 `updatedAt` 的记录(确定性策略,SA8),并在 `matchedCount` 注明命中数。
180
+ *
181
+ * `matchBy:'id'` 按 YAML 顶层 id 直接查 `definitions` 表;标准 `createDefinition`
182
+ * 路径产生的记录键为 `wfl-{uuid}` 而非 YAML 原始 id,故 'id' 模式仅在记录曾以
183
+ * YAML id 为键存储时可命中——对任意文件的幂等重放导入,请用默认 'name'。
184
+ *
185
+ * 文件读取归属:workflow 包为浏览器安全包(无 Node fs),调用方(CI 脚本/
186
+ * CLI)自行 `fs.readFileSync` 后调本 RPC;服务端路径导入应在 Host 侧另包一层。
187
+ */
188
+ @Remote('importDefinitionFromYaml')
189
+ async importDefinitionFromYaml(input: ImportDefinitionInput): Promise<ImportDefinitionResult> {
190
+ const parsed = ImportDefinitionInputSchema.parse(input)
191
+ const definition = parseWorkflowYaml(parsed.yaml)
192
+ const matchBy = parsed.matchBy ?? 'name'
193
+ const yaml = parsed.yaml
194
+
195
+ if (matchBy === 'id') {
196
+ const existing = this.ctx.sandorePersistence.definitions.get(definition.id)
197
+ if (existing === undefined) {
198
+ const created = await this.createDefinition({ yaml })
199
+ return { status: 'created', id: created.id, name: created.name, matchedCount: 0 }
200
+ }
201
+ if (existing.version === definition.version) {
202
+ return { status: 'skipped', id: definition.id, name: existing.name, matchedCount: 1 }
203
+ }
204
+ await this.updateYaml(definition.id, yaml)
205
+ return { status: 'updated', id: definition.id, name: definition.name, matchedCount: 1 }
206
+ }
207
+
208
+ // matchBy === 'name':收集全部同名记录。
209
+ const matches: Array<{ id: string; record: WorkflowDefinitionRecord }> = []
210
+ for (const [id, record] of this.ctx.sandorePersistence.definitions.entries()) {
211
+ if (record.name === definition.name) matches.push({ id, record })
212
+ }
213
+ if (matches.length === 0) {
214
+ const created = await this.createDefinition({ yaml })
215
+ return { status: 'created', id: created.id, name: created.name, matchedCount: 0 }
216
+ }
217
+ // 多匹配歧义(SA8):取最新 updatedAt(确定性);平手时按 Map 插入序取首个。
218
+ const matched = matches.reduce((latest, current) =>
219
+ current.record.updatedAt > latest.record.updatedAt ? current : latest,
220
+ )
221
+ if (matched.record.version === definition.version) {
222
+ return { status: 'skipped', id: matched.id, name: matched.record.name, matchedCount: matches.length }
223
+ }
224
+ await this.updateYaml(matched.id, yaml)
225
+ return { status: 'updated', id: matched.id, name: definition.name, matchedCount: matches.length }
226
+ }
227
+
228
+ // ── 实例生命周期 ─────────────────────────────────────────────────────────
229
+
230
+ /** 列出实例摘要(从 `state` 表派生,去 job 概念)。 */
231
+ @Remote('listInstances')
232
+ listInstances(): InstanceSummary[] {
233
+ const summaries: InstanceSummary[] = []
234
+ for (const [instanceId, state] of this.ctx.sandorePersistence.state.entries()) {
235
+ summaries.push({
236
+ instanceId,
237
+ workflowId: state.workflowId,
238
+ originalInput: state.originalInput,
239
+ status: state.status,
240
+ createdAt: state.createdAt,
241
+ updatedAt: state.updatedAt,
242
+ })
243
+ }
244
+ return summaries
245
+ }
246
+
247
+ /** 启动实例:校验定义存在后转发 `ctx.sandoreEngine.startInstance`(去 job 概念)。 */
248
+ @Remote('startInstance')
249
+ async startInstance(input: StartInstanceInput): Promise<DagRun> {
250
+ const parsed = StartInstanceInputSchema.parse(input)
251
+ if (this.ctx.sandorePersistence.definitions.get(parsed.workflowId) === undefined) {
252
+ throw new SandoreError(`workflow '${parsed.workflowId}' does not exist`, 'INVALID_OPERATION')
253
+ }
254
+ const request = {
255
+ workflowId: parsed.workflowId,
256
+ originalInput: parsed.originalInput,
257
+ // cwd 必填(wire schema 已校验):直接透传,hostAgent 据此 attach web workspace。
258
+ cwd: parsed.cwd,
259
+ ...(parsed.model === undefined ? {} : { model: parsed.model }),
260
+ ...(parsed.permission === undefined ? {} : { permission: parsed.permission }),
261
+ ...(parsed.hostSessionId === undefined ? {} : { hostSessionId: parsed.hostSessionId }),
262
+ } as DagStartRequest
263
+ return await this.ctx.sandoreEngine.startInstance(request)
264
+ }
265
+
266
+ /** 删除实例快照(`state` 表删除);重复删除幂等。 */
267
+ @Remote('deleteInstance')
268
+ async deleteInstance(instanceId: string): Promise<boolean> {
269
+ return await this.ctx.sandorePersistence.state.delete(instanceId)
270
+ }
271
+
272
+ /**
273
+ * 读取实例的完整状态快照:内存 active.state 第一优先级(收尾中间态立即可见),
274
+ * 未在内存(终态清表/未启动)回退持久化;两者皆无抛 NODE_NOT_FOUND。
275
+ */
276
+ @Remote('getInstanceState')
277
+ getInstanceState(instanceId: string): WorkflowTaskState {
278
+ const live = this.ctx.sandoreEngine.getInstanceState(instanceId)
279
+ if (live !== undefined) return live
280
+ return this.requireInstanceState(instanceId)
281
+ }
282
+
283
+ /** 中止运行中实例(转发引擎);终态实例抛 `INVALID_OPERATION`。 */
284
+ @Remote('abortInstance')
285
+ async abortInstance(instanceId: string): Promise<void> {
286
+ const state = this.requireInstanceState(instanceId)
287
+ if (TERMINAL_STATUSES.has(state.status)) {
288
+ throw new SandoreError(`instance '${instanceId}' is already in terminal state '${state.status}'`, 'INVALID_OPERATION')
289
+ }
290
+ await this.ctx.sandoreEngine.abortInstance(instanceId)
291
+ }
292
+
293
+ /**
294
+ * 恢复单个实例(崩溃恢复入口):前置 state 表终态守卫后转发
295
+ * `ctx.sandoreEngine.recoverInstance`。内存未命中但快照非终态的孤儿实例
296
+ * 经此端点重新进入内存登记表并续跑;内存已有的非终态实例为 no-op 返回
297
+ * 当前状态。终态实例拒绝(守卫对齐 `abortInstance`,避免无意义恢复)。
298
+ */
299
+ @Remote('recoverInstance')
300
+ async recoverInstance(input: RecoverInstanceInput): Promise<DagRun> {
301
+ const parsed = RecoverInstanceInputSchema.parse(input)
302
+ const state = this.requireInstanceState(parsed.instanceId)
303
+ if (TERMINAL_STATUSES.has(state.status)) {
304
+ throw new SandoreError(`instance '${parsed.instanceId}' is already in terminal state '${state.status}'`, 'INVALID_OPERATION')
305
+ }
306
+ return await this.ctx.sandoreEngine.recoverInstance(parsed.instanceId)
307
+ }
308
+
309
+ /**
310
+ * 批量恢复孤儿实例(对齐基准 GET /jobs 恢复触发):遍历 state 表全部非终态
311
+ * 实例逐个 `recoverInstance`,单实例失败不阻断(逐实例 try/catch),
312
+ * 返回 `{ recovered, errors }` 摘要。供前端在进程重启后一键恢复全部孤儿。
313
+ */
314
+ @Remote('recoverOrphanedInstances')
315
+ async recoverOrphanedInstances(): Promise<RecoverOrphanedInstancesResult> {
316
+ return await this.ctx.sandoreEngine.recoverOrphanedInstances()
317
+ }
318
+
319
+ // ── 生命周期 RPC ────────────────────────────────────────────────────────
320
+
321
+ /** 回复待处理问答(转发 `ctx.sandoreEngine.replyQuestion`)。 */
322
+ @Remote('replyQuestion')
323
+ async replyQuestion(input: ReplyQuestionInput): Promise<void> {
324
+ const parsed = ReplyQuestionInputSchema.parse(input)
325
+ // zod 已对 answers 结构做显式运行时校验;此处桥接 `exactOptionalPropertyTypes`
326
+ // 下 `custom?: string` 与 zod `.optional()` 推断 `string | undefined` 的类型差异。
327
+ await this.ctx.sandoreEngine.replyQuestion(parsed.instanceId, parsed.requestId, parsed.answers as AskUserQuestionAnswer)
328
+ }
329
+
330
+ /** 拒绝待处理问答(转发 `ctx.sandoreEngine.rejectQuestion`,标对应 Step failed,缺陷 26)。 */
331
+ @Remote('rejectQuestion')
332
+ async rejectQuestion(input: RejectQuestionInput): Promise<void> {
333
+ const parsed = RejectQuestionInputSchema.parse(input)
334
+ await this.ctx.sandoreEngine.rejectQuestion(parsed.instanceId, parsed.requestId)
335
+ }
336
+
337
+ /** 回复待处理权限请求(转发 `ctx.sandoreEngine.replyPermission`)。 */
338
+ @Remote('replyPermission')
339
+ async replyPermission(input: ReplyPermissionInput): Promise<void> {
340
+ const parsed = ReplyPermissionInputSchema.parse(input)
341
+ await this.ctx.sandoreEngine.replyPermission(parsed.instanceId, parsed.requestId, parsed.reply)
342
+ }
343
+
344
+ /** 暂停单个 Step(转发 `ctx.sandoreEngine.pauseStep`)。 */
345
+ @Remote('pauseStep')
346
+ async pauseStep(input: PauseStepInput): Promise<void> {
347
+ const parsed = PauseStepInputSchema.parse(input)
348
+ await this.ctx.sandoreEngine.pauseStep(parsed.instanceId, parsed.stepId)
349
+ }
350
+
351
+ /** 恢复单个 Step(转发 `ctx.sandoreEngine.resumeStep`,可选恢复提示词)。 */
352
+ @Remote('resumeStep')
353
+ async resumeStep(input: ResumeStepInput): Promise<void> {
354
+ const parsed = ResumeStepInputSchema.parse(input)
355
+ await this.ctx.sandoreEngine.resumeStep(parsed.instanceId, parsed.stepId, parsed.resumePrompt)
356
+ }
357
+
358
+ /** 暂停实例内全部 Step(转发 `ctx.sandoreEngine.pauseAllSteps`)。 */
359
+ @Remote('pauseAll')
360
+ async pauseAll(input: PauseAllInput): Promise<void> {
361
+ const parsed = PauseAllInputSchema.parse(input)
362
+ await this.ctx.sandoreEngine.pauseAllSteps(parsed.instanceId)
363
+ }
364
+
365
+ /** 恢复实例内全部 Step(转发 `ctx.sandoreEngine.resumeAllSteps`,可选恢复提示词)。 */
366
+ @Remote('resumeAll')
367
+ async resumeAll(input: ResumeAllInput): Promise<void> {
368
+ const parsed = ResumeAllInputSchema.parse(input)
369
+ await this.ctx.sandoreEngine.resumeAllSteps(parsed.instanceId, parsed.resumePrompt)
370
+ }
371
+
372
+ /** 重试失败 Step(转发 `ctx.sandoreEngine.retryStep`,revert 可选 revertBoundary)。 */
373
+ @Remote('retryStep')
374
+ async retryStep(input: RetryStepInput): Promise<void> {
375
+ const parsed = RetryStepInputSchema.parse(input)
376
+ await this.ctx.sandoreEngine.retryStep(parsed.instanceId, parsed.stepId, parsed.mode, parsed.message, parsed.revertBoundary)
377
+ }
378
+
379
+ /** 列出某 Step 子代理会话中可用的闭合 turn 回退点候选(revert 回退点选择数据源)。 */
380
+ @Remote('listRevertBoundaries')
381
+ listRevertBoundaries(input: ListRevertBoundariesInput): RevertBoundaryOption[] {
382
+ const parsed = ListRevertBoundariesInputSchema.parse(input)
383
+ return this.ctx.sandoreEngine.listRevertBoundaries(parsed.instanceId, parsed.stepId)
384
+ }
385
+
386
+ /**
387
+ * 向运行中实例的指定 Step 子代理追加一条用户 @mention 消息(缺陷 31)。
388
+ *
389
+ * zod 校验 + 终态守卫后转发 `ctx.sandoreEngine.sendStepMessage`(对齐
390
+ * `updateInstancePermissionMode` / `recoverInstance` / `abortInstance` 的两层
391
+ * 守卫:remote 层基于持久化 state 终态守卫,engine 层基于内存 ActiveInstance)。
392
+ * 引擎层 requireActive + 终态守卫 → 按 stepId 反查最后活跃段 childId →
393
+ * synthetic===false 才 subagents.followup(synthetic 抛 INVALID_OPERATION)→
394
+ * 追加 userMessages `{kind:'message', target:stepId}` → persistState → 双发
395
+ * `sandore/step-message`。错误透传(对齐 retryStep 风格,不吞 SandoreError)。
396
+ */
397
+ @Remote('sendStepMessage')
398
+ async sendStepMessage(input: SendStepMessageInput): Promise<void> {
399
+ const parsed = SendStepMessageInputSchema.parse(input)
400
+ const state = this.requireInstanceState(parsed.instanceId)
401
+ if (TERMINAL_STATUSES.has(state.status)) {
402
+ throw new SandoreError(`instance '${parsed.instanceId}' is already in terminal state '${state.status}'`, 'INVALID_OPERATION')
403
+ }
404
+ await this.ctx.sandoreEngine.sendStepMessage(parsed.instanceId, parsed.stepId, parsed.message)
405
+ }
406
+
407
+ /**
408
+ * 运行中切换实例权限模式(缺陷 29):zod 校验 + 终态守卫后转发
409
+ * `ctx.sandoreEngine.setPermissionMode`。对齐 abortInstance/recoverInstance 的
410
+ * 两层守卫(remote 层基于持久化 state 终态守卫,engine 层基于内存
411
+ * ActiveInstance);engine 层对宿主 session + hostAgent 重新应用 sandbox 模式
412
+ * + 审批策略,下次受限调用即生效。
413
+ */
414
+ @Remote('updateInstancePermissionMode')
415
+ async updateInstancePermissionMode(input: UpdateInstancePermissionModeInput): Promise<void> {
416
+ const parsed = UpdateInstancePermissionModeInputSchema.parse(input)
417
+ const state = this.requireInstanceState(parsed.instanceId)
418
+ if (TERMINAL_STATUSES.has(state.status)) {
419
+ throw new SandoreError(`instance '${parsed.instanceId}' is already in terminal state '${state.status}'`, 'INVALID_OPERATION')
420
+ }
421
+ await this.ctx.sandoreEngine.setPermissionMode(parsed.instanceId, parsed.mode)
422
+ }
423
+
424
+ // ── 产物 / Provider 转发 ─────────────────────────────────────────────────
425
+
426
+ /**
427
+ * 查询实例产物:从 `stageContracts` 的产物契约扁平化条目,可选按 stageId 过滤。
428
+ */
429
+ @Remote('getArtifacts')
430
+ getArtifacts(instanceId: string, stageId?: string): ArtifactEntry[] {
431
+ const state = this.requireInstanceState(instanceId)
432
+ const entries: ArtifactEntry[] = []
433
+ for (const [contractStageId, contract] of Object.entries(state.stageContracts)) {
434
+ if (stageId !== undefined && contractStageId !== stageId) continue
435
+ for (const artifact of contract.artifacts) {
436
+ entries.push({
437
+ stepId: artifact.stepId,
438
+ stageId: contractStageId,
439
+ outputToId: artifact.outputToId,
440
+ files: [...artifact.files],
441
+ ...(artifact.returnPrompt === undefined ? {} : { returnPrompt: artifact.returnPrompt }),
442
+ })
443
+ }
444
+ }
445
+ return entries
446
+ }
447
+
448
+ /** 列出 LLM Provider(透传 `ctx.llm.listProviders()`)。 */
449
+ @Remote('listProviders')
450
+ listProviders(): LlmProviderInfo[] {
451
+ return this.ctx.llm.listProviders()
452
+ }
453
+
454
+ /** 列出某 Provider 的模型(透传 `ctx.llm.listModels()`)。 */
455
+ @Remote('listModels')
456
+ async listModels(provider: string): Promise<LlmModelInfo[]> {
457
+ return await this.ctx.llm.listModels(provider)
458
+ }
459
+
460
+ // ── 私有守卫 ─────────────────────────────────────────────────────────────
461
+
462
+ private requireDefinition(id: string): WorkflowDefinitionEntry {
463
+ const record = this.ctx.sandorePersistence.definitions.get(id)
464
+ if (record === undefined) {
465
+ throw new SandoreError(`definition '${id}' not found`, 'NODE_NOT_FOUND')
466
+ }
467
+ return { id, ...record }
468
+ }
469
+
470
+ private requireInstanceState(instanceId: string): WorkflowTaskState {
471
+ const state = this.ctx.sandorePersistence.state.get(instanceId)
472
+ if (state === undefined) {
473
+ throw new SandoreError(`instance '${instanceId}' not found`, 'NODE_NOT_FOUND')
474
+ }
475
+ return state
476
+ }
477
+ }
478
+
479
+ export default SandoreRemote