taskmeld 0.1.1

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 (204) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +172 -0
  3. package/README.zh-CN.md +172 -0
  4. package/dist/src/app/app-context-env.js +51 -0
  5. package/dist/src/app/create-app-context.js +127 -0
  6. package/dist/src/app/data-dir.js +29 -0
  7. package/dist/src/app/pipeline-config.js +105 -0
  8. package/dist/src/app/pipeline-plugin-config.js +2 -0
  9. package/dist/src/app/pipeline-registry.js +502 -0
  10. package/dist/src/app/pipeline-runtime.js +202 -0
  11. package/dist/src/app/runtime-store.js +151 -0
  12. package/dist/src/app/user-config.js +37 -0
  13. package/dist/src/artifacts/artifact-cleanup.js +192 -0
  14. package/dist/src/artifacts/artifact-index.js +262 -0
  15. package/dist/src/artifacts/artifact-rebuilder.js +120 -0
  16. package/dist/src/artifacts/storage-service.js +371 -0
  17. package/dist/src/cli/bootstrap.js +226 -0
  18. package/dist/src/cli/commands/agent.js +126 -0
  19. package/dist/src/cli/commands/artifact.js +175 -0
  20. package/dist/src/cli/commands/init.js +150 -0
  21. package/dist/src/cli/commands/pipeline/errors.js +37 -0
  22. package/dist/src/cli/commands/pipeline/result.js +179 -0
  23. package/dist/src/cli/commands/pipeline/selector.js +51 -0
  24. package/dist/src/cli/commands/pipeline/types.js +2 -0
  25. package/dist/src/cli/commands/pipeline/watch.js +67 -0
  26. package/dist/src/cli/commands/pipeline.js +339 -0
  27. package/dist/src/cli/commands/scheduler.js +81 -0
  28. package/dist/src/cli/commands/server.js +70 -0
  29. package/dist/src/cli/commands/system.js +21 -0
  30. package/dist/src/cli/errors.js +71 -0
  31. package/dist/src/cli/help.js +184 -0
  32. package/dist/src/cli/index.js +65 -0
  33. package/dist/src/cli/output.js +19 -0
  34. package/dist/src/cli/renderers/engine/json.js +67 -0
  35. package/dist/src/cli/renderers/engine/markdown.js +95 -0
  36. package/dist/src/cli/renderers/engine/types.js +2 -0
  37. package/dist/src/cli/renderers/engine/utils.js +32 -0
  38. package/dist/src/cli/renderers/index.js +27 -0
  39. package/dist/src/cli/renderers/specs/agent.js +78 -0
  40. package/dist/src/cli/renderers/specs/artifact.js +32 -0
  41. package/dist/src/cli/renderers/specs/index.js +36 -0
  42. package/dist/src/cli/renderers/specs/init.js +25 -0
  43. package/dist/src/cli/renderers/specs/pipeline.js +561 -0
  44. package/dist/src/cli/renderers/specs/scheduler.js +46 -0
  45. package/dist/src/cli/renderers/specs/server.js +38 -0
  46. package/dist/src/cli/renderers/specs/system.js +36 -0
  47. package/dist/src/cli/router.js +199 -0
  48. package/dist/src/cli/server-runtime-client.js +780 -0
  49. package/dist/src/cli/types.js +2 -0
  50. package/dist/src/gateway/frame-sanitizer.js +78 -0
  51. package/dist/src/gateway/gateway-client.js +462 -0
  52. package/dist/src/gateway/index.js +18 -0
  53. package/dist/src/gateway/types.js +2 -0
  54. package/dist/src/index.js +123 -0
  55. package/dist/src/logs/run-log-reader.js +141 -0
  56. package/dist/src/logs/run-log-service.js +42 -0
  57. package/dist/src/logs/run-log-types.js +2 -0
  58. package/dist/src/pipeline/agent-activity.js +191 -0
  59. package/dist/src/pipeline/artifact-storage.js +208 -0
  60. package/dist/src/pipeline/diagnostics/dependency-diagnostic.js +105 -0
  61. package/dist/src/pipeline/diagnostics/index.js +6 -0
  62. package/dist/src/pipeline/dispatch/pipeline-inbound-queue.js +215 -0
  63. package/dist/src/pipeline/dispatch/pipeline-link-dispatcher.js +66 -0
  64. package/dist/src/pipeline/dispatch/pipeline-link-store.js +94 -0
  65. package/dist/src/pipeline/dispatch/pipeline-queue-drainer.js +71 -0
  66. package/dist/src/pipeline/execution/dependency-check.js +52 -0
  67. package/dist/src/pipeline/execution/execution-result.js +2 -0
  68. package/dist/src/pipeline/execution/group-item-executor.js +128 -0
  69. package/dist/src/pipeline/execution/index.js +5 -0
  70. package/dist/src/pipeline/execution/node-item-executor.js +58 -0
  71. package/dist/src/pipeline/execution/node-runner.js +159 -0
  72. package/dist/src/pipeline/execution/readiness-state.js +10 -0
  73. package/dist/src/pipeline/execution/reject-handler.js +94 -0
  74. package/dist/src/pipeline/execution/rejected-artifact-archiver.js +45 -0
  75. package/dist/src/pipeline/execution/route-item-manager.js +253 -0
  76. package/dist/src/pipeline/execution/run-abort-controller.js +66 -0
  77. package/dist/src/pipeline/execution/run-state-helpers.js +257 -0
  78. package/dist/src/pipeline/execution/service.js +165 -0
  79. package/dist/src/pipeline/execution/session-registry.js +96 -0
  80. package/dist/src/pipeline/execution/structured-node-runner.js +411 -0
  81. package/dist/src/pipeline/execution-status.js +96 -0
  82. package/dist/src/pipeline/execution-timeout.js +21 -0
  83. package/dist/src/pipeline/identity/index.js +32 -0
  84. package/dist/src/pipeline/identity/types.js +2 -0
  85. package/dist/src/pipeline/item-batch-controller.js +227 -0
  86. package/dist/src/pipeline/output/pipeline-output-resolver.js +91 -0
  87. package/dist/src/pipeline/output/pipeline-output-store.js +60 -0
  88. package/dist/src/pipeline/runtime-model.js +173 -0
  89. package/dist/src/pipeline/scheduler/dependency-state.js +144 -0
  90. package/dist/src/pipeline/scheduler-service.js +314 -0
  91. package/dist/src/pipeline/state/group-item-state.js +50 -0
  92. package/dist/src/pipeline/state/group-run-state.js +41 -0
  93. package/dist/src/pipeline/state/index.js +20 -0
  94. package/dist/src/pipeline/state/node-item-state.js +67 -0
  95. package/dist/src/pipeline/state/node-run-state.js +51 -0
  96. package/dist/src/pipeline/state/types.js +2 -0
  97. package/dist/src/pipeline/state-machine.js +101 -0
  98. package/dist/src/pipeline/structured-output/contract.js +133 -0
  99. package/dist/src/pipeline/structured-output/index.js +22 -0
  100. package/dist/src/pipeline/structured-output/parser.js +214 -0
  101. package/dist/src/pipeline/structured-output/prompt.js +290 -0
  102. package/dist/src/pipeline/structured-output/waiter.js +139 -0
  103. package/dist/src/pipeline/template.js +135 -0
  104. package/dist/src/pipeline/timeline-log-store.js +57 -0
  105. package/dist/src/pipeline/tool-activity.js +94 -0
  106. package/dist/src/pipeline/types/pipeline-link.js +7 -0
  107. package/dist/src/pipeline/types/pipeline-output.js +11 -0
  108. package/dist/src/pipeline/types/workflow.js +2 -0
  109. package/dist/src/pipeline/workflow/branch-rules.js +74 -0
  110. package/dist/src/pipeline/workflow/defaults.js +48 -0
  111. package/dist/src/pipeline/workflow/io.js +89 -0
  112. package/dist/src/pipeline/workflow/normalize.js +347 -0
  113. package/dist/src/pipeline/workflow/routes.js +16 -0
  114. package/dist/src/pipeline/workflow/template-mapper.js +113 -0
  115. package/dist/src/pipeline/workflow/validate.js +312 -0
  116. package/dist/src/pipeline/workflow-graph.js +165 -0
  117. package/dist/src/server/api-handler.js +163 -0
  118. package/dist/src/server/http-utils.js +34 -0
  119. package/dist/src/server/middleware.js +61 -0
  120. package/dist/src/server/router.js +105 -0
  121. package/dist/src/server/routes/agents.js +189 -0
  122. package/dist/src/server/routes/artifacts.js +163 -0
  123. package/dist/src/server/routes/gateway.js +18 -0
  124. package/dist/src/server/routes/health.js +16 -0
  125. package/dist/src/server/routes/logs.js +73 -0
  126. package/dist/src/server/routes/pipeline-batch.js +163 -0
  127. package/dist/src/server/routes/pipeline-diagnostics.js +33 -0
  128. package/dist/src/server/routes/pipeline-identity.js +24 -0
  129. package/dist/src/server/routes/pipeline-links.js +117 -0
  130. package/dist/src/server/routes/pipeline-outputs.js +27 -0
  131. package/dist/src/server/routes/pipeline-queue.js +62 -0
  132. package/dist/src/server/routes/pipeline-runtime.js +162 -0
  133. package/dist/src/server/routes/pipeline-scheduler.js +69 -0
  134. package/dist/src/server/routes/pipeline-workflow.js +180 -0
  135. package/dist/src/server/routes/pipelines.js +96 -0
  136. package/dist/src/server/routes/sessions.js +244 -0
  137. package/dist/src/server/routes/timeline.js +14 -0
  138. package/dist/src/server/serve-static.js +42 -0
  139. package/dist/src/server/types.js +2 -0
  140. package/dist/src/services/agent-service.js +79 -0
  141. package/dist/src/services/artifact-service.js +74 -0
  142. package/dist/src/services/gateway-read-helpers.js +10 -0
  143. package/dist/src/services/index.js +23 -0
  144. package/dist/src/services/pipeline-service.js +529 -0
  145. package/dist/src/services/pipeline-status.js +93 -0
  146. package/dist/src/services/read-services.js +60 -0
  147. package/dist/src/services/scheduler-service.js +37 -0
  148. package/dist/src/services/session-service.js +227 -0
  149. package/dist/src/services/system-service.js +26 -0
  150. package/dist/src/transport/ws-broker.js +48 -0
  151. package/dist/src/utils/array.js +17 -0
  152. package/dist/src/utils/guards.js +5 -0
  153. package/dist/src/utils/session.js +60 -0
  154. package/dist/src/version.js +5 -0
  155. package/package.json +61 -0
  156. package/web/dist/assets/index-CWnfhkn-.js +65 -0
  157. package/web/dist/assets/index-gZ0xOfSO.css +1 -0
  158. package/web/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  159. package/web/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  160. package/web/dist/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  161. package/web/dist/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  162. package/web/dist/assets/jetbrains-mono-cyrillic-700-normal-BWTpRfYl.woff2 +0 -0
  163. package/web/dist/assets/jetbrains-mono-cyrillic-700-normal-CEoEElIJ.woff +0 -0
  164. package/web/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  165. package/web/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  166. package/web/dist/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  167. package/web/dist/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  168. package/web/dist/assets/jetbrains-mono-greek-700-normal-C6CZE3T8.woff2 +0 -0
  169. package/web/dist/assets/jetbrains-mono-greek-700-normal-DEigVDxa.woff +0 -0
  170. package/web/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  171. package/web/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  172. package/web/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  173. package/web/dist/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  174. package/web/dist/assets/jetbrains-mono-latin-700-normal-BYuf6tUa.woff2 +0 -0
  175. package/web/dist/assets/jetbrains-mono-latin-700-normal-D3wTyLJW.woff +0 -0
  176. package/web/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  177. package/web/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  178. package/web/dist/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  179. package/web/dist/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  180. package/web/dist/assets/jetbrains-mono-latin-ext-700-normal-CZipNAKV.woff2 +0 -0
  181. package/web/dist/assets/jetbrains-mono-latin-ext-700-normal-CxPITLHs.woff +0 -0
  182. package/web/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  183. package/web/dist/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  184. package/web/dist/assets/jetbrains-mono-vietnamese-700-normal-BDLVIk2r.woff +0 -0
  185. package/web/dist/assets/space-grotesk-latin-400-normal-BnQMeOim.woff +0 -0
  186. package/web/dist/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
  187. package/web/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
  188. package/web/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
  189. package/web/dist/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
  190. package/web/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
  191. package/web/dist/assets/space-grotesk-latin-ext-400-normal-CfP_5XZW.woff2 +0 -0
  192. package/web/dist/assets/space-grotesk-latin-ext-400-normal-DRPE3kg4.woff +0 -0
  193. package/web/dist/assets/space-grotesk-latin-ext-500-normal-3dgZTiw9.woff +0 -0
  194. package/web/dist/assets/space-grotesk-latin-ext-500-normal-DUe3BAxM.woff2 +0 -0
  195. package/web/dist/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
  196. package/web/dist/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
  197. package/web/dist/assets/space-grotesk-vietnamese-400-normal-B7xT_GF5.woff2 +0 -0
  198. package/web/dist/assets/space-grotesk-vietnamese-400-normal-BIWiOVfw.woff +0 -0
  199. package/web/dist/assets/space-grotesk-vietnamese-500-normal-BTqKIpxg.woff +0 -0
  200. package/web/dist/assets/space-grotesk-vietnamese-500-normal-BmEvtly_.woff2 +0 -0
  201. package/web/dist/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
  202. package/web/dist/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
  203. package/web/dist/favicon.svg +10 -0
  204. package/web/dist/index.html +14 -0
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRouteItemManager = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const readiness_state_1 = require("./readiness-state");
6
+ const state_machine_1 = require("../state-machine");
7
+ const identity_1 = require("../identity");
8
+ const routes_1 = require("../workflow/routes");
9
+ const state_1 = require("../state");
10
+ const ctx = (reason, extra) => ({ reason, ...extra });
11
+ const normalizeAllowedRoute = (rawRoute, allowedRoutes) => {
12
+ if (typeof rawRoute !== "string")
13
+ return null;
14
+ const trimmed = rawRoute.trim();
15
+ if (!trimmed)
16
+ return null;
17
+ if (allowedRoutes.length === 0)
18
+ return trimmed;
19
+ const direct = allowedRoutes.find((route) => route === trimmed);
20
+ if (direct)
21
+ return direct;
22
+ const lower = trimmed.toLowerCase();
23
+ // 与结构化校验保持一致:大小写不一致时使用工作流声明值,避免命中丢失。
24
+ return allowedRoutes.find((route) => route.toLowerCase() === lower) ?? null;
25
+ };
26
+ const collectRouteBuckets = (content, allowedRoutes) => {
27
+ if (!Array.isArray(content))
28
+ return [];
29
+ const counts = new Map();
30
+ for (const entry of content) {
31
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
32
+ continue;
33
+ const route = normalizeAllowedRoute(entry.route, allowedRoutes);
34
+ if (!route)
35
+ continue;
36
+ entry.route = route;
37
+ counts.set(route, (counts.get(route) ?? 0) + 1);
38
+ }
39
+ return [...counts.entries()].map(([route, count]) => ({ route, count }));
40
+ };
41
+ const createRouteItemManager = (options) => {
42
+ const clearDerivedRouteItemRuns = (item) => {
43
+ const run = options.state.getRun();
44
+ const prefix = `${item.itemKey}::${item.nodeId}:`;
45
+ run.itemRuns = (run.itemRuns ?? []).filter((candidate) => !candidate.itemKey.startsWith(prefix));
46
+ run.groupItemRuns = (run.groupItemRuns ?? []).filter((candidate) => !candidate.itemKey.startsWith(prefix));
47
+ };
48
+ const resetNodeItemRun = (target, status) => {
49
+ (0, state_1.markItemReset)(target, status, ctx("route_reset", { command: "retry_reset" }));
50
+ target.route = null;
51
+ target.attempt = 0;
52
+ target.loopCount = 0;
53
+ target.artifacts = [];
54
+ };
55
+ const resetGroupItemRun = (target, status) => {
56
+ (0, state_1.markGroupItemReset)(target, status, ctx("route_reset", { command: "retry_reset" }));
57
+ target.attempt = 0;
58
+ target.artifacts = [];
59
+ };
60
+ const copyNodeItemRun = (target, source) => {
61
+ target.status = (0, state_machine_1.transitionStatus)(target.status, source.status, "route_backfill");
62
+ target.route = source.route;
63
+ target.attempt = source.attempt;
64
+ target.loopCount = source.loopCount;
65
+ target.wakeAt = source.wakeAt;
66
+ target.startedAt = source.startedAt;
67
+ target.finishedAt = source.finishedAt;
68
+ target.lastError = source.lastError;
69
+ target.artifacts = source.artifacts;
70
+ };
71
+ const copyGroupItemRun = (target, source) => {
72
+ target.status = (0, state_machine_1.transitionStatus)(target.status, source.status, "route_backfill");
73
+ target.attempt = source.attempt;
74
+ target.startedAt = source.startedAt;
75
+ target.finishedAt = source.finishedAt;
76
+ target.lastError = source.lastError;
77
+ target.artifacts = source.artifacts;
78
+ };
79
+ const initializeDerivedRouteItemKey = (sourceItem, derivedItemKey, route) => {
80
+ const run = options.state.getRun();
81
+ options.graph.syncRunGroupsFromWorkflow(run);
82
+ if (!run.itemRuns)
83
+ run.itemRuns = [];
84
+ if (!run.groupItemRuns)
85
+ run.groupItemRuns = [];
86
+ const outgoingEdges = options.graph.getOutgoingEdges(sourceItem.nodeId);
87
+ const isRouteNode = (options.graph.getWorkflowNodeById(sourceItem.nodeId)?.routePolicy?.allowed.length ?? 0) > 0;
88
+ const startTargets = outgoingEdges
89
+ // 分流节点中 yes 是主线语义:只沿普通依赖边初始化;no/自定义值只沿路由边初始化。
90
+ .filter((edge) => (isRouteNode ? (route === routes_1.MAINLINE_ROUTE_VALUE ? edge.when === null : edge.when === route) : !edge.when))
91
+ .map((edge) => edge.to);
92
+ const reachable = options.state.collectReachableEntities(startTargets);
93
+ const ancestors = options.state.collectAncestorEntities([sourceItem.nodeId, ...startTargets]);
94
+ for (const node of run.nodes) {
95
+ let target = options.state.getItemRun(node.id, derivedItemKey);
96
+ if (!target) {
97
+ target = {
98
+ id: (0, node_crypto_1.randomUUID)(),
99
+ nodeId: node.id,
100
+ itemKey: derivedItemKey,
101
+ status: "blocked",
102
+ route: null,
103
+ attempt: 0,
104
+ loopCount: 0,
105
+ wakeAt: null,
106
+ startedAt: null,
107
+ finishedAt: null,
108
+ lastError: null,
109
+ artifacts: [],
110
+ };
111
+ run.itemRuns.push(target);
112
+ }
113
+ const source = options.state.getItemRun(node.id, sourceItem.itemKey);
114
+ if (node.id === sourceItem.nodeId) {
115
+ copyNodeItemRun(target, source ?? sourceItem);
116
+ (0, state_1.markItemSuccess)(target, ctx("route_init_success", { command: "route_backfill" }));
117
+ target.route = route;
118
+ continue;
119
+ }
120
+ if (ancestors.nodeIds.has(node.id)) {
121
+ if (source) {
122
+ copyNodeItemRun(target, source);
123
+ }
124
+ else {
125
+ resetNodeItemRun(target, options.state.computeInitialItemStatus(node.id));
126
+ }
127
+ continue;
128
+ }
129
+ if (reachable.nodeIds.has(node.id)) {
130
+ resetNodeItemRun(target, options.state.computeInitialItemStatus(node.id));
131
+ continue;
132
+ }
133
+ resetNodeItemRun(target, "skipped");
134
+ }
135
+ for (const group of run.groups ?? []) {
136
+ let target = options.state.getGroupItemRun(group.id, derivedItemKey);
137
+ if (!target) {
138
+ target = {
139
+ id: (0, node_crypto_1.randomUUID)(),
140
+ groupId: group.id,
141
+ itemKey: derivedItemKey,
142
+ status: "blocked",
143
+ attempt: 0,
144
+ startedAt: null,
145
+ finishedAt: null,
146
+ lastError: null,
147
+ artifacts: [],
148
+ };
149
+ run.groupItemRuns.push(target);
150
+ }
151
+ const source = options.state.getGroupItemRun(group.id, sourceItem.itemKey);
152
+ if (ancestors.groupIds.has(group.id)) {
153
+ if (source) {
154
+ copyGroupItemRun(target, source);
155
+ }
156
+ else {
157
+ resetGroupItemRun(target, options.state.computeInitialGroupItemStatus(group.id));
158
+ }
159
+ continue;
160
+ }
161
+ if (reachable.groupIds.has(group.id)) {
162
+ resetGroupItemRun(target, options.state.computeInitialGroupItemStatus(group.id));
163
+ continue;
164
+ }
165
+ resetGroupItemRun(target, "skipped");
166
+ }
167
+ };
168
+ const applyEnvelopeOutcomeToItem = async (item, envelope, opts) => {
169
+ if (!envelope)
170
+ return;
171
+ const workflowNode = options.graph.getWorkflowNodeById(item.nodeId);
172
+ const allowedRoutes = workflowNode?.routePolicy?.allowed ?? [];
173
+ const isRouteNode = allowedRoutes.length > 0;
174
+ item.route = null;
175
+ if (isRouteNode) {
176
+ clearDerivedRouteItemRuns(item);
177
+ const routeBuckets = collectRouteBuckets(envelope.artifacts[0]?.content, allowedRoutes);
178
+ for (const bucket of routeBuckets) {
179
+ const derivedItemKey = (0, identity_1.buildDerivedRouteItemKey)(item.itemKey, item.nodeId, bucket.route);
180
+ initializeDerivedRouteItemKey(item, derivedItemKey, bucket.route);
181
+ const nextItem = options.state.getItemRun(item.nodeId, derivedItemKey);
182
+ if (!nextItem)
183
+ continue;
184
+ (0, state_1.markItemSuccess)(nextItem, ctx("route_hit", { command: "route_backfill" }));
185
+ nextItem.route = bucket.route;
186
+ nextItem.attempt = Math.max(nextItem.attempt, item.attempt);
187
+ nextItem.startedAt = item.startedAt;
188
+ nextItem.finishedAt = item.finishedAt ?? new Date().toISOString();
189
+ nextItem.wakeAt = null;
190
+ nextItem.artifacts = item.artifacts;
191
+ if (!opts?.suppressOutgoing) {
192
+ for (const edge of options.graph.getOutgoingEdges(item.nodeId)) {
193
+ if (bucket.route === routes_1.MAINLINE_ROUTE_VALUE)
194
+ continue;
195
+ // 路由节点只按命中的 route 边推进,普通边在此一律忽略,避免主线被隐式直通。
196
+ if (edge.when !== bucket.route)
197
+ continue;
198
+ if (options.graph.isGroupId(edge.to)) {
199
+ options.state.ensureGroupItemKeyInitialized(derivedItemKey);
200
+ const downstreamGroup = options.state.getGroupItemRun(edge.to, derivedItemKey);
201
+ if (downstreamGroup && (0, readiness_state_1.canPromoteToQueuedByDependency)(downstreamGroup)) {
202
+ (0, state_1.markGroupItemQueued)(downstreamGroup, ctx("route_downstream"));
203
+ }
204
+ continue;
205
+ }
206
+ const downstream = options.state.getItemRun(edge.to, derivedItemKey);
207
+ if (!downstream)
208
+ continue;
209
+ if ((0, readiness_state_1.canPromoteToQueuedByDependency)(downstream)) {
210
+ (0, state_1.markItemQueued)(downstream, ctx("route_downstream"));
211
+ }
212
+ }
213
+ }
214
+ options.runtimeStore.pushTimeline(`分流命中: ${item.nodeId}#${item.itemKey} -> ${bucket.route} (${bucket.count} 条)`);
215
+ }
216
+ }
217
+ const sleepUntil = envelope.control?.sleepUntil;
218
+ if (typeof sleepUntil === "string" && sleepUntil.trim()) {
219
+ const parsed = Date.parse(sleepUntil);
220
+ if (Number.isFinite(parsed)) {
221
+ const wakeAt = new Date(parsed).toISOString();
222
+ (0, state_1.markItemWaiting)(item, { reason: "sleep_until", wakeAt });
223
+ }
224
+ }
225
+ if (!opts?.suppressOutgoing && !isRouteNode) {
226
+ for (const edge of options.graph.getOutgoingEdges(item.nodeId)) {
227
+ if (edge.when)
228
+ continue;
229
+ if (options.graph.isCrossBranchEdge({ from: item.nodeId, to: edge.to, when: null })) {
230
+ continue;
231
+ }
232
+ if (options.graph.isGroupId(edge.to)) {
233
+ options.state.ensureGroupItemKeyInitialized(item.itemKey);
234
+ const downstreamGroup = options.state.getGroupItemRun(edge.to, item.itemKey);
235
+ if (downstreamGroup && (0, readiness_state_1.canPromoteToQueuedByDependency)(downstreamGroup)) {
236
+ (0, state_1.markGroupItemQueued)(downstreamGroup, ctx("downstream_promote"));
237
+ }
238
+ continue;
239
+ }
240
+ const downstream = options.state.getItemRun(edge.to, item.itemKey);
241
+ if (!downstream)
242
+ continue;
243
+ if ((0, readiness_state_1.canPromoteToQueuedByDependency)(downstream)) {
244
+ (0, state_1.markItemQueued)(downstream, ctx("downstream_promote"));
245
+ }
246
+ }
247
+ }
248
+ };
249
+ return {
250
+ applyEnvelopeOutcomeToItem,
251
+ };
252
+ };
253
+ exports.createRouteItemManager = createRouteItemManager;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRunAbortController = void 0;
4
+ const createRunAbortController = () => {
5
+ const nodeExecutionControllers = new Map();
6
+ const drainControllers = new Map();
7
+ const registerController = (runId, ac, sessionId) => {
8
+ let controllers = nodeExecutionControllers.get(runId);
9
+ if (!controllers) {
10
+ controllers = new Set();
11
+ nodeExecutionControllers.set(runId, controllers);
12
+ }
13
+ const entry = { ac, sessionId };
14
+ controllers.add(entry);
15
+ return entry;
16
+ };
17
+ const unregisterController = (runId, entry) => {
18
+ const controllers = nodeExecutionControllers.get(runId);
19
+ if (!controllers)
20
+ return;
21
+ controllers.delete(entry);
22
+ if (controllers.size === 0) {
23
+ nodeExecutionControllers.delete(runId);
24
+ }
25
+ entry.ac.abort();
26
+ };
27
+ /**
28
+ * 中止指定流水线运行的所有节点执行。
29
+ * 1. 向每个活跃节点的远端 agent 会话发送 "/stop" 命令(fire-and-forget)
30
+ * 2. 触发本地 AbortController 中断轮询/排水循环
31
+ */
32
+ const abortRunControllers = (runId, client) => {
33
+ const controllers = nodeExecutionControllers.get(runId);
34
+ if (controllers) {
35
+ const sessionIds = new Set();
36
+ for (const entry of controllers) {
37
+ entry.ac.abort();
38
+ sessionIds.add(entry.sessionId);
39
+ }
40
+ nodeExecutionControllers.delete(runId);
41
+ for (const sessionId of sessionIds) {
42
+ client.sendReq("chat.send", { sessionKey: sessionId, message: "/stop" }, { sideEffect: true })
43
+ .catch(() => { });
44
+ }
45
+ }
46
+ const dc = drainControllers.get(runId);
47
+ if (dc) {
48
+ dc.abort();
49
+ drainControllers.delete(runId);
50
+ }
51
+ };
52
+ /**
53
+ * 获取或创建用于 drainPipeline 的中止信号。
54
+ * 每次新 run 会创建新的 AbortController,确保 stop/retry 只中断当前运行。
55
+ */
56
+ const getOrCreateDrainSignal = (runId) => {
57
+ let dc = drainControllers.get(runId);
58
+ if (!dc) {
59
+ dc = new AbortController();
60
+ drainControllers.set(runId, dc);
61
+ }
62
+ return dc.signal;
63
+ };
64
+ return { registerController, unregisterController, abortRunControllers, getOrCreateDrainSignal };
65
+ };
66
+ exports.createRunAbortController = createRunAbortController;
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRunStateHelpers = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const dependency_check_1 = require("./dependency-check");
6
+ const state_machine_1 = require("../state-machine");
7
+ const createRunStateHelpers = (options) => {
8
+ const getRun = () => options.runtimeStore.getRun();
9
+ const getNodeById = (nodeId) => getRun().nodes.find((node) => node.id === nodeId) ?? null;
10
+ const getGroupById = (groupId) => (getRun().groups ?? []).find((group) => group.id === groupId) ?? null;
11
+ const getItemRun = (nodeId, itemKey) => (getRun().itemRuns ?? []).find((item) => item.nodeId === nodeId && item.itemKey === itemKey) ?? null;
12
+ const getGroupItemRun = (groupId, itemKey) => (getRun().groupItemRuns ?? []).find((item) => item.groupId === groupId && item.itemKey === itemKey) ?? null;
13
+ const computeInitialItemStatus = (nodeId) => {
14
+ if (!options.graph.isWorkflowNodeEnabled(nodeId))
15
+ return "skipped";
16
+ if (options.graph.getParallelGroupByMemberNodeId(nodeId))
17
+ return "blocked";
18
+ const incoming = options.graph.getIncomingEdges(nodeId);
19
+ return incoming.length === 0 ? "queued" : "blocked";
20
+ };
21
+ const computeInitialGroupItemStatus = (groupId) => {
22
+ const incoming = options.graph.getIncomingEdges(groupId);
23
+ return incoming.length === 0 ? "queued" : "blocked";
24
+ };
25
+ const ensureGroupItemKeyInitialized = (itemKey) => {
26
+ const run = getRun();
27
+ options.graph.syncRunGroupsFromWorkflow(run);
28
+ if (!run.groupItemRuns)
29
+ run.groupItemRuns = [];
30
+ for (const group of run.groups ?? []) {
31
+ const existed = run.groupItemRuns.find((item) => item.groupId === group.id && item.itemKey === itemKey);
32
+ if (existed)
33
+ continue;
34
+ run.groupItemRuns.push({
35
+ id: (0, node_crypto_1.randomUUID)(),
36
+ groupId: group.id,
37
+ itemKey,
38
+ status: computeInitialGroupItemStatus(group.id),
39
+ attempt: 0,
40
+ startedAt: null,
41
+ finishedAt: null,
42
+ lastError: null,
43
+ artifacts: [],
44
+ });
45
+ }
46
+ };
47
+ const ensureItemRuns = () => {
48
+ const run = getRun();
49
+ if (!run.itemRuns)
50
+ run.itemRuns = [];
51
+ options.graph.syncRunGroupsFromWorkflow(run);
52
+ if (run.itemRuns.length === 0) {
53
+ run.itemRuns = options.graph.getTemplateNodes().flatMap((node) => {
54
+ const status = computeInitialItemStatus(node.id);
55
+ return options.defaultItemKeys.map((itemKey) => ({
56
+ id: (0, node_crypto_1.randomUUID)(),
57
+ nodeId: node.id,
58
+ itemKey,
59
+ status,
60
+ route: null,
61
+ attempt: 0,
62
+ loopCount: 0,
63
+ wakeAt: null,
64
+ startedAt: null,
65
+ finishedAt: null,
66
+ lastError: null,
67
+ artifacts: [],
68
+ }));
69
+ });
70
+ for (const itemKey of options.defaultItemKeys) {
71
+ ensureGroupItemKeyInitialized(itemKey);
72
+ }
73
+ return;
74
+ }
75
+ const knownNodeIds = new Set(run.nodes.map((node) => node.id));
76
+ run.itemRuns = run.itemRuns.filter((item) => knownNodeIds.has(item.nodeId));
77
+ };
78
+ const ensureItemKeyInitialized = (itemKey) => {
79
+ const run = getRun();
80
+ if (!run.itemRuns)
81
+ run.itemRuns = [];
82
+ for (const node of run.nodes) {
83
+ const existed = run.itemRuns.find((item) => item.nodeId === node.id && item.itemKey === itemKey);
84
+ if (existed)
85
+ continue;
86
+ run.itemRuns.push({
87
+ id: (0, node_crypto_1.randomUUID)(),
88
+ nodeId: node.id,
89
+ itemKey,
90
+ status: computeInitialItemStatus(node.id),
91
+ route: null,
92
+ attempt: 0,
93
+ loopCount: 0,
94
+ wakeAt: null,
95
+ startedAt: null,
96
+ finishedAt: null,
97
+ lastError: null,
98
+ artifacts: [],
99
+ });
100
+ }
101
+ ensureGroupItemKeyInitialized(itemKey);
102
+ };
103
+ const depCheckContext = {
104
+ isCrossBranchEdge: (edge) => options.graph.isCrossBranchEdge(edge),
105
+ isGroupId: (id) => options.graph.isGroupId(id),
106
+ isWorkflowNodeEnabled: (id) => options.graph.isWorkflowNodeEnabled(id),
107
+ isRoutePolicyNode: (id) => (options.graph.getWorkflowNodeById(id)?.routePolicy?.allowed.length ?? 0) > 0,
108
+ getGroupItemRun: (groupId, itemKey) => getGroupItemRun(groupId, itemKey),
109
+ getItemRun: (nodeId, itemKey) => getItemRun(nodeId, itemKey),
110
+ };
111
+ const isDependencySatisfied = (itemKey, edge) => (0, dependency_check_1.isDependencySatisfied)(itemKey, edge, depCheckContext);
112
+ const getEffectiveDependencyIdsForNodeItem = (nodeId, itemKey) => {
113
+ const directDependencyIds = options.graph.getIncomingEdges(nodeId)
114
+ .filter((edge) => isDependencySatisfied(itemKey, edge))
115
+ .map((edge) => edge.from);
116
+ const groupId = options.graph.getWorkflowNodeById(nodeId)?.parallelGroupId?.trim();
117
+ if (!groupId)
118
+ return [...new Set(directDependencyIds)];
119
+ const groupDependencyIds = options.graph.getIncomingEdges(groupId)
120
+ .filter((edge) => isDependencySatisfied(itemKey, edge))
121
+ .map((edge) => edge.from);
122
+ return [...new Set([...directDependencyIds, ...groupDependencyIds])];
123
+ };
124
+ const collectDownstreamSubgraph = (rootNodeId) => {
125
+ const nodeIds = new Set();
126
+ const groupIds = new Set();
127
+ const queue = [rootNodeId];
128
+ const seen = new Set();
129
+ while (queue.length > 0) {
130
+ const current = queue.shift();
131
+ if (!current || seen.has(current))
132
+ continue;
133
+ seen.add(current);
134
+ if (options.graph.isGroupId(current)) {
135
+ groupIds.add(current);
136
+ const group = options.graph.getWorkflowGroupById(current);
137
+ // 并行组本身不是执行节点,但它控制成员节点与后续 join 分支的状态。
138
+ // 回放/打回时必须穿透 group,把成员和 group 后面的节点一起纳入重置范围。
139
+ for (const memberId of group?.members ?? []) {
140
+ queue.push(memberId);
141
+ }
142
+ }
143
+ else {
144
+ nodeIds.add(current);
145
+ const ownerGroup = options.graph.getParallelGroupByMemberNodeId(current);
146
+ if (ownerGroup) {
147
+ queue.push(ownerGroup.id);
148
+ }
149
+ }
150
+ for (const edge of options.graph.getOutgoingEdges(current)) {
151
+ if (options.graph.isCrossBranchEdge(edge))
152
+ continue;
153
+ queue.push(edge.to);
154
+ }
155
+ }
156
+ return { nodeIds, groupIds };
157
+ };
158
+ const collectReachableEntities = (startIds) => {
159
+ const nodeIds = new Set();
160
+ const groupIds = new Set();
161
+ const queue = [...startIds];
162
+ const seen = new Set();
163
+ while (queue.length > 0) {
164
+ const current = queue.shift();
165
+ if (!current || seen.has(current))
166
+ continue;
167
+ seen.add(current);
168
+ if (options.graph.isGroupId(current)) {
169
+ groupIds.add(current);
170
+ }
171
+ else {
172
+ nodeIds.add(current);
173
+ }
174
+ for (const edge of options.graph.getOutgoingEdges(current)) {
175
+ queue.push(edge.to);
176
+ }
177
+ }
178
+ return { nodeIds, groupIds };
179
+ };
180
+ const collectAncestorEntities = (startIds) => {
181
+ const nodeIds = new Set();
182
+ const groupIds = new Set();
183
+ const queue = [...startIds];
184
+ const seen = new Set();
185
+ while (queue.length > 0) {
186
+ const current = queue.shift();
187
+ if (!current || seen.has(current))
188
+ continue;
189
+ seen.add(current);
190
+ if (options.graph.isGroupId(current)) {
191
+ groupIds.add(current);
192
+ }
193
+ else {
194
+ nodeIds.add(current);
195
+ }
196
+ for (const edge of options.graph.getIncomingEdges(current)) {
197
+ queue.push(edge.from);
198
+ }
199
+ }
200
+ return { nodeIds, groupIds };
201
+ };
202
+ const resetNodeForReplay = (node, opts) => {
203
+ const run = getRun();
204
+ if (!options.graph.isWorkflowNodeEnabled(node.id)) {
205
+ node.status = (0, state_machine_1.transitionStatus)(node.status, "skipped");
206
+ }
207
+ else {
208
+ const effectiveDepIds = new Set(options.graph.getIncomingEdges(node.id)
209
+ .filter((edge) => !options.graph.isCrossBranchEdge(edge))
210
+ .map((edge) => edge.from));
211
+ const groupId = options.graph.getWorkflowNodeById(node.id)?.parallelGroupId?.trim();
212
+ if (groupId) {
213
+ for (const edge of options.graph.getIncomingEdges(groupId)) {
214
+ if (!options.graph.isCrossBranchEdge(edge)) {
215
+ effectiveDepIds.add(edge.from);
216
+ }
217
+ }
218
+ }
219
+ node.status = (0, state_machine_1.transitionStatus)(node.status, [...effectiveDepIds].every((depId) => {
220
+ if (options.graph.isGroupId(depId)) {
221
+ return (run.groups ?? []).find((group) => group.id === depId)?.status === "success";
222
+ }
223
+ if (!options.graph.isWorkflowNodeEnabled(depId))
224
+ return true;
225
+ return run.nodes.find((current) => current.id === depId)?.status === "success";
226
+ })
227
+ ? "queued"
228
+ : "blocked");
229
+ }
230
+ node.artifacts = [];
231
+ node.startedAt = null;
232
+ node.finishedAt = null;
233
+ node.lastError = null;
234
+ if (opts?.clearRejectFeedbacks ?? true) {
235
+ node.rejectFeedbacks = [];
236
+ }
237
+ };
238
+ return {
239
+ getRun,
240
+ getNodeById,
241
+ getGroupById,
242
+ getItemRun,
243
+ getGroupItemRun,
244
+ computeInitialItemStatus,
245
+ computeInitialGroupItemStatus,
246
+ ensureGroupItemKeyInitialized,
247
+ ensureItemRuns,
248
+ ensureItemKeyInitialized,
249
+ isDependencySatisfied,
250
+ getEffectiveDependencyIdsForNodeItem,
251
+ collectDownstreamSubgraph,
252
+ collectReachableEntities,
253
+ collectAncestorEntities,
254
+ resetNodeForReplay,
255
+ };
256
+ };
257
+ exports.createRunStateHelpers = createRunStateHelpers;