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,290 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildExternalPipelineArtifactInput = exports.createNodeCorrectionPrompt = exports.createNodeExecutionPrompt = exports.buildDependencyArtifactInputs = void 0;
4
+ const promises_1 = require("node:fs/promises");
5
+ const node_crypto_1 = require("node:crypto");
6
+ const node_path_1 = require("node:path");
7
+ const parser_1 = require("./parser");
8
+ const guards_1 = require("../../utils/guards");
9
+ const MAINLINE_ROUTE_VALUE = "yes";
10
+ const buildDependencyArtifactInputs = async (run, node, itemKey, dependencyIds) => {
11
+ const inputs = [];
12
+ const effectiveDependencyIds = dependencyIds?.length ? dependencyIds : node.dependsOn;
13
+ for (const depId of effectiveDependencyIds) {
14
+ const depNode = run.nodes.find((n) => n.id === depId);
15
+ const depGroup = run.groups?.find((g) => g.id === depId);
16
+ const depItem = itemKey ? run.itemRuns?.find((item) => item.nodeId === depId && item.itemKey === itemKey) : null;
17
+ const depGroupItem = itemKey ? run.groupItemRuns?.find((item) => item.groupId === depId && item.itemKey === itemKey) : null;
18
+ const depSource = depNode
19
+ ? {
20
+ id: depNode.id,
21
+ title: depNode.title,
22
+ agentId: depNode.executor.agentId,
23
+ artifacts: depItem?.artifacts?.length ? depItem.artifacts : depNode.artifacts,
24
+ route: depItem?.route ?? null,
25
+ }
26
+ : depGroup
27
+ ? {
28
+ id: depGroup.id,
29
+ title: depGroup.title,
30
+ agentId: depGroup.id,
31
+ artifacts: depGroupItem?.artifacts?.length ? depGroupItem.artifacts : depGroup.artifacts,
32
+ route: null,
33
+ }
34
+ : null;
35
+ if (!depSource || depSource.artifacts.length === 0)
36
+ continue;
37
+ const loaded = await Promise.all(depSource.artifacts.map(async (manifest) => {
38
+ try {
39
+ const raw = await (0, promises_1.readFile)(manifest.path, "utf8");
40
+ const parsed = JSON.parse(raw);
41
+ const obj = (0, guards_1.isRecord)(parsed) ? parsed : null;
42
+ const artifact = obj && (0, guards_1.isRecord)(obj.artifact) ? obj.artifact : null;
43
+ const rawContent = artifact ? artifact.content : raw;
44
+ const content = depSource.route &&
45
+ Array.isArray(rawContent) &&
46
+ rawContent.some((entry) => (0, guards_1.isRecord)(entry) && typeof entry.route === "string")
47
+ ? rawContent.filter((entry) => (0, guards_1.isRecord)(entry) && typeof entry.route === "string" && entry.route.trim() === depSource.route)
48
+ : rawContent;
49
+ const meta = (0, guards_1.isRecord)(artifact?.meta) ? artifact.meta : undefined;
50
+ return {
51
+ sourceNodeId: depSource.id,
52
+ sourceNodeTitle: depSource.title,
53
+ sourceAgentId: depSource.agentId,
54
+ type: manifest.type,
55
+ schemaVersion: manifest.schemaVersion,
56
+ name: manifest.name,
57
+ path: manifest.path,
58
+ hash: manifest.hash,
59
+ createdAt: manifest.createdAt,
60
+ content: (0, parser_1.toPromptContentText)(content),
61
+ meta,
62
+ };
63
+ }
64
+ catch {
65
+ return {
66
+ sourceNodeId: depSource.id,
67
+ sourceNodeTitle: depSource.title,
68
+ sourceAgentId: depSource.agentId,
69
+ type: manifest.type,
70
+ schemaVersion: manifest.schemaVersion,
71
+ name: manifest.name,
72
+ path: manifest.path,
73
+ hash: manifest.hash,
74
+ createdAt: manifest.createdAt,
75
+ content: "[artifact_read_failed]",
76
+ };
77
+ }
78
+ }));
79
+ for (const item of loaded) {
80
+ if (item)
81
+ inputs.push(item);
82
+ }
83
+ }
84
+ return inputs;
85
+ };
86
+ exports.buildDependencyArtifactInputs = buildDependencyArtifactInputs;
87
+ const createNodeExecutionPrompt = (ctx) => {
88
+ const spec = ctx.outputSpec;
89
+ const isRouteNode = ctx.allowedRoutes.length > 0;
90
+ const groupedDependencies = (() => {
91
+ const grouped = new Map();
92
+ for (const artifact of ctx.dependencyArtifacts) {
93
+ const key = `${artifact.sourceNodeId}|${artifact.sourceAgentId}`;
94
+ const current = grouped.get(key);
95
+ if (current) {
96
+ current.contents.push(artifact.content);
97
+ continue;
98
+ }
99
+ grouped.set(key, {
100
+ sourceNodeId: artifact.sourceNodeId,
101
+ sourceNodeTitle: artifact.sourceNodeTitle,
102
+ sourceAgentId: artifact.sourceAgentId,
103
+ contents: [artifact.content],
104
+ });
105
+ }
106
+ return Array.from(grouped.values());
107
+ })();
108
+ const lines = [
109
+ "# 流水线节点执行指令",
110
+ "",
111
+ "## 输出要求",
112
+ "- 只能返回一个合法 JSON 对象(ResultEnvelope)。",
113
+ "- 不要输出任何额外解释、前后缀、Markdown 之外的文本。",
114
+ "- `status=success` 时,`artifacts` 必须至少 1 条。",
115
+ "- `artifacts[].content` 支持任意 JSON 值(例如:`\"text\"`、`{\"k\":\"v\"}`、`[{\"k\":\"v1\"},{\"k\":\"v2\"}]`)。",
116
+ ...(isRouteNode ? ["- 分流节点的 `artifacts[0].content` 必须是数组,数组内每个对象都要包含 `route` 字段。"] : []),
117
+ "",
118
+ "## ResultEnvelope 固定字段",
119
+ `- version: \`2.0\``,
120
+ `- runId: \`${ctx.runId}\``,
121
+ `- nodeId: \`${ctx.nodeId}\``,
122
+ `- requestId: \`${ctx.requestId}\``,
123
+ `- sessionId: \`${ctx.sessionId}\``,
124
+ "- status: `success | failed`",
125
+ "- artifacts: `array`",
126
+ "",
127
+ "## 节点上下文",
128
+ `- 当前节点: \`${ctx.nodeId}\`(${ctx.nodeTitle})`,
129
+ `- 依赖节点: ${ctx.dependencies.length > 0 ? ctx.dependencies.map((id) => `\`${id}\``).join(", ") : "`none`"}`,
130
+ ];
131
+ lines.push("", "## 产物规格", `- type: \`${spec.type}\``, `- schemaVersion: \`${spec.schemaVersion}\``);
132
+ // 仅在节点明确开启分流时,才注入分流相关说明,避免无配置时污染提示词。
133
+ if (ctx.allowedRoutes.length > 0) {
134
+ lines.push("", "## 分流规则", `- 允许的 route 仅能是: ${ctx.allowedRoutes.map((route) => `\`${route}\``).join(", ")}`, `- \`${MAINLINE_ROUTE_VALUE}\` 表示继续主线,不需要配置分流目标。`, "- `artifacts[0].content` 必须输出为数组。", "- 数组中的每个对象都必须包含 `route` 字段。", "- 每个对象的 `route` 必须命中上述取值。", "- 系统会按 `content[*].route` 自动分组、汇总并推送到对应分支。");
135
+ if (ctx.routeTargets.length > 0) {
136
+ lines.push("- 当前配置的 route 目标如下:");
137
+ for (const target of ctx.routeTargets) {
138
+ lines.push(` - \`${target.route}\` -> \`${target.targetNodeId}\` (${target.targetNodeTitle}, agent:${target.targetAgentId}, lane:${target.lane})`);
139
+ }
140
+ }
141
+ }
142
+ // 仅在节点明确开启打回且存在上游依赖时,才注入打回相关说明,避免无配置时污染提示词。
143
+ if (ctx.allowReject && ctx.dependencies.length > 0) {
144
+ lines.push("", "## 打回配置", `- allowReject: \`${ctx.allowReject ? "true" : "false"}\``, `- maxRejectCount: \`${ctx.maxRejectCount}\``, "", "### 打回规则", "- 仅当上游内容不符合你的校验规范时,才允许打回。", "- 打回时必须返回 `status=failed`,且 `error.code=upstream_reject`。", "- 如需指定目标上游,可提供 `error.targets=[\"nodeId\"]`。", "- 不提供 `targets` 时,系统默认打回上一个直接上游节点。", "", "```json", "{\"code\":\"upstream_reject\",\"message\":\"打回原因\"}", "```");
145
+ }
146
+ if (isRouteNode) {
147
+ lines.push("", "## 分流节点 JSON 示例", "```json", JSON.stringify({
148
+ version: "2.0",
149
+ runId: "__RUN_ID__",
150
+ nodeId: "__NODE_ID__",
151
+ requestId: "__REQUEST_ID__",
152
+ sessionId: "__SESSION_ID__",
153
+ status: "success",
154
+ artifacts: [
155
+ {
156
+ type: "__TYPE__",
157
+ schemaVersion: 1,
158
+ name: "primary",
159
+ content: [
160
+ { label: "A", route: "yes", value: "..." },
161
+ { label: "B", route: "holo", value: "..." },
162
+ { label: "C", route: "no", value: "..." },
163
+ ],
164
+ meta: {},
165
+ },
166
+ ],
167
+ control: {
168
+ sleepUntil: null,
169
+ retryFromNodeId: null,
170
+ },
171
+ logs: [],
172
+ error: null,
173
+ }, null, 2), "```");
174
+ }
175
+ else {
176
+ lines.push("", "## JSON 输出示例", "```json", JSON.stringify({
177
+ version: "2.0",
178
+ runId: "__RUN_ID__",
179
+ nodeId: "__NODE_ID__",
180
+ requestId: "__REQUEST_ID__",
181
+ sessionId: "__SESSION_ID__",
182
+ status: "success",
183
+ artifacts: [
184
+ {
185
+ type: "__TYPE__",
186
+ schemaVersion: 1,
187
+ name: "primary",
188
+ content: "artifact content",
189
+ meta: {},
190
+ },
191
+ ],
192
+ control: {
193
+ sleepUntil: null,
194
+ retryFromNodeId: null,
195
+ },
196
+ logs: [],
197
+ error: null,
198
+ }, null, 2), "```");
199
+ }
200
+ lines.push("");
201
+ // External pipeline upstream artifact — injected before internal dependency artifacts
202
+ if (ctx.externalPipelineArtifact) {
203
+ const artifact = ctx.externalPipelineArtifact;
204
+ const fence = (0, parser_1.detectFenceLanguage)(artifact.content);
205
+ lines.push("## 外部流水线上游产物", "", `来源: 流水线 ${artifact.sourcePipelineId} 的最终输出`, "", "内容:", `\`\`\`${fence}`, artifact.content, "```");
206
+ }
207
+ if (groupedDependencies.length > 0) {
208
+ lines.push("## 上游输输出结构:");
209
+ for (const dep of groupedDependencies) {
210
+ const merged = dep.contents.join("\n");
211
+ const fence = (0, parser_1.detectFenceLanguage)(merged);
212
+ lines.push(`### 节点 \`${dep.sourceNodeId}\`(${dep.sourceNodeTitle})- agent \`${dep.sourceAgentId}\``, `\`\`\`${fence}`, merged, "```");
213
+ }
214
+ lines.push("");
215
+ }
216
+ // 节点目标是执行 prompt 的核心约束,不能因为存在打回反馈或分流说明而被覆盖掉。
217
+ // 这里固定保留“节点目标”段,再额外追加反馈,避免分流节点提示词缺少主任务目标。
218
+ lines.push("## 节点目标", ctx.instruction || "请按节点职责完成任务");
219
+ if (ctx.rejectFeedbacks.length > 0) {
220
+ lines.push("", "## 下游打回反馈(请优先修正)");
221
+ for (const item of ctx.rejectFeedbacks) {
222
+ lines.push(`- ${item}`);
223
+ }
224
+ }
225
+ return lines.join("\n");
226
+ };
227
+ exports.createNodeExecutionPrompt = createNodeExecutionPrompt;
228
+ const createNodeCorrectionPrompt = (ctx, lastViolation) => [
229
+ `结构校验未通过,错误码: ${lastViolation}`,
230
+ "请严格基于本次请求的固定字段重发完整 ResultEnvelope,不要沿用旧请求的顶层字段。",
231
+ `固定字段必须为: version="2.0", runId="${ctx.runId}", nodeId="${ctx.nodeId}", requestId="${ctx.requestId}", sessionId="${ctx.sessionId}"`,
232
+ `产物规格必须为: type="${ctx.outputSpec.type}", schemaVersion=${ctx.outputSpec.schemaVersion}`,
233
+ ctx.allowedRoutes && ctx.allowedRoutes.length > 0
234
+ ? `这是分流节点。允许的 route 只有: ${ctx.allowedRoutes.map((route) => `"${route}"`).join(", ")}。请把 route 写进 artifacts[0].content 的每个对象里。`
235
+ : ctx.externalPipelineArtifact
236
+ ? `本次请求包含来自流水线 ${ctx.externalPipelineArtifact.sourcePipelineId} 的外部上游产物。请继续基于该上游产物修正。`
237
+ : "请按当前结构输出结果。",
238
+ "请先把完整 ResultEnvelope 保存为当前工作目录下的 result.json,然后自行运行下面的校验命令,确认 JSON valid 后再继续:",
239
+ "```bash",
240
+ "cat result.json | python3 -m json.tool > /dev/null && echo \"JSON valid\" || echo \"JSON invalid\"",
241
+ "```",
242
+ "如果校验结果不是 JSON valid,请继续修正 result.json,直到通过为止。",
243
+ "必须输出完整 JSON 对象,不要只输出 artifacts 片段。",
244
+ "只输出修正后的合法 JSON ResultEnvelope,不要输出任何解释。",
245
+ ].join("\n");
246
+ exports.createNodeCorrectionPrompt = createNodeCorrectionPrompt;
247
+ const buildExternalPipelineArtifactInput = async (output) => {
248
+ try {
249
+ const { absolutePath } = output.artifactRef;
250
+ // Verify path is within artifact directory (basic path traversal check)
251
+ const normalizedPath = (0, node_path_1.resolve)(absolutePath);
252
+ if (!normalizedPath)
253
+ return null;
254
+ const raw = await (0, promises_1.readFile)(normalizedPath, "utf8");
255
+ const parsed = JSON.parse(raw);
256
+ const obj = (0, guards_1.isRecord)(parsed) ? parsed : null;
257
+ if (!obj)
258
+ return null;
259
+ const artifact = (0, guards_1.isRecord)(obj.artifact) ? obj.artifact : null;
260
+ if (!artifact)
261
+ return null;
262
+ // Verify hash
263
+ const fileHash = `sha256:${(0, node_crypto_1.createHash)("sha256").update(raw).digest("hex")}`;
264
+ if (fileHash !== output.artifactRef.hash)
265
+ return null;
266
+ const content = (0, parser_1.toPromptContentText)(artifact.content);
267
+ const meta = (0, guards_1.isRecord)(artifact.meta) ? artifact.meta : undefined;
268
+ return {
269
+ sourceKind: "pipeline_output",
270
+ sourcePipelineId: output.pipelineId,
271
+ sourceRunId: output.runId,
272
+ sourceBatchRunId: output.batchRunId,
273
+ sourceOutputId: output.outputId,
274
+ sourceOutputNodeId: output.outputNodeId,
275
+ sourceArtifactId: output.artifactId,
276
+ sourceArtifactHash: output.artifactRef.hash,
277
+ type: output.artifactRef.type,
278
+ schemaVersion: output.artifactRef.schemaVersion,
279
+ name: output.artifactRef.name,
280
+ path: normalizedPath,
281
+ createdAt: output.producedAt,
282
+ content,
283
+ meta,
284
+ };
285
+ }
286
+ catch {
287
+ return null;
288
+ }
289
+ };
290
+ exports.buildExternalPipelineArtifactInput = buildExternalPipelineArtifactInput;
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractViolationCode = exports.waitForStructuredEnvelope = exports.evaluateObservedEnvelopeWindow = exports.evaluateEnvelopeCandidates = exports.rememberObservedEnvelopes = exports.shouldFailFastForCompletedSession = void 0;
4
+ const execution_timeout_1 = require("../execution-timeout");
5
+ const parser_1 = require("./parser");
6
+ const contract_1 = require("./contract");
7
+ const STRUCTURED_RESULT_TIMEOUT_MS = (0, execution_timeout_1.getStructuredResultTimeoutMs)();
8
+ const STRUCTURED_RESULT_POLL_MS = (0, execution_timeout_1.getStructuredResultPollMs)();
9
+ const MAX_OBSERVED_ENVELOPES = 400;
10
+ const SESSION_COMPLETION_GRACE_MS = 1_200;
11
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
12
+ const shouldFailFastForCompletedSession = (sessionCompletedAt, now = Date.now()) => sessionCompletedAt !== null && now >= sessionCompletedAt + SESSION_COMPLETION_GRACE_MS;
13
+ exports.shouldFailFastForCompletedSession = shouldFailFastForCompletedSession;
14
+ const rememberObservedEnvelopes = (observed, payload, source) => {
15
+ const envelopes = (0, parser_1.collectEnvelopeCandidates)(payload);
16
+ const now = Date.now();
17
+ for (const envelope of envelopes) {
18
+ observed.push({
19
+ envelope,
20
+ observedAt: now,
21
+ source,
22
+ });
23
+ }
24
+ if (observed.length > MAX_OBSERVED_ENVELOPES) {
25
+ observed.splice(0, observed.length - MAX_OBSERVED_ENVELOPES);
26
+ }
27
+ return envelopes;
28
+ };
29
+ exports.rememberObservedEnvelopes = rememberObservedEnvelopes;
30
+ const evaluateEnvelopeCandidates = (envelopes, ctx) => {
31
+ const forRequestId = envelopes.filter((envelope) => envelope.requestId === ctx.requestId);
32
+ if (forRequestId.length > 0) {
33
+ let firstFailure = null;
34
+ for (const envelope of forRequestId) {
35
+ const checked = (0, contract_1.validateEnvelope)(envelope, ctx);
36
+ if (checked.ok)
37
+ return { envelope, seenCandidate: true };
38
+ if (!firstFailure)
39
+ firstFailure = checked.code;
40
+ }
41
+ return { violation: firstFailure ?? "result_envelope_missing", seenCandidate: true };
42
+ }
43
+ const related = envelopes.filter((envelope) => envelope.runId === ctx.runId &&
44
+ envelope.nodeId === ctx.nodeId &&
45
+ envelope.sessionId === ctx.sessionId);
46
+ if (related.length > 0) {
47
+ return { violation: "request_id_mismatch", seenCandidate: true };
48
+ }
49
+ return { seenCandidate: false };
50
+ };
51
+ exports.evaluateEnvelopeCandidates = evaluateEnvelopeCandidates;
52
+ const evaluateObservedEnvelopeWindow = (observed, ctx, options) => {
53
+ const confirmFinal = options?.confirmFinal === true;
54
+ const forRequestId = observed.filter((entry) => entry.envelope.requestId === ctx.requestId);
55
+ if (forRequestId.length > 0) {
56
+ if (!confirmFinal) {
57
+ // agent 会话未结束前,只记录“已见到候选”,不能提前认定成功/失败。
58
+ // 否则中途调试 JSON、半成品 envelope 都可能被过早消费。
59
+ return { seenCandidate: true };
60
+ }
61
+ let latestFailure = null;
62
+ for (let i = forRequestId.length - 1; i >= 0; i -= 1) {
63
+ const checked = (0, contract_1.validateEnvelope)(forRequestId[i].envelope, ctx);
64
+ if (checked.ok)
65
+ return { envelope: forRequestId[i].envelope, seenCandidate: true };
66
+ if (!latestFailure)
67
+ latestFailure = checked.code;
68
+ }
69
+ return { violation: latestFailure ?? "result_envelope_missing", seenCandidate: true };
70
+ }
71
+ const related = observed.filter((entry) => entry.envelope.runId === ctx.runId &&
72
+ entry.envelope.nodeId === ctx.nodeId &&
73
+ entry.envelope.sessionId === ctx.sessionId);
74
+ if (related.length > 0) {
75
+ if (!confirmFinal)
76
+ return { seenCandidate: true };
77
+ return { violation: "request_id_mismatch", seenCandidate: true };
78
+ }
79
+ return { seenCandidate: false };
80
+ };
81
+ exports.evaluateObservedEnvelopeWindow = evaluateObservedEnvelopeWindow;
82
+ /**
83
+ * 等待结构化回执。
84
+ *
85
+ * 当传入 AbortSignal 且被触发时,提前退出本地轮询循环。
86
+ * 远端 agent 的停止由上游 executionService 通过 "/stop" 命令处理。
87
+ *
88
+ * @param signal 可选的中止信号,用于流水线 stop/retry 时提前退出轮询。
89
+ */
90
+ const waitForStructuredEnvelope = async (emitter, ctx, initialViolation, hasSessionCompleted, signal) => {
91
+ const deadline = Date.now() + STRUCTURED_RESULT_TIMEOUT_MS;
92
+ let lastViolation = initialViolation;
93
+ const latest = [];
94
+ const MAX_LOCAL_ENVELOPES = 400;
95
+ const onCandidate = (entry) => {
96
+ latest.push(entry);
97
+ if (latest.length > MAX_LOCAL_ENVELOPES) {
98
+ latest.splice(0, latest.length - MAX_LOCAL_ENVELOPES);
99
+ }
100
+ };
101
+ emitter.on("candidate", onCandidate);
102
+ try {
103
+ while (Date.now() <= deadline) {
104
+ const currentWindow = [...latest];
105
+ const sessionCompletedAt = hasSessionCompleted?.() ?? null;
106
+ const canConfirm = (0, exports.shouldFailFastForCompletedSession)(sessionCompletedAt);
107
+ const checked = (0, exports.evaluateObservedEnvelopeWindow)(currentWindow, ctx, { confirmFinal: canConfirm });
108
+ if (checked.envelope) {
109
+ return checked.envelope;
110
+ }
111
+ if (canConfirm && checked.violation) {
112
+ lastViolation = checked.violation;
113
+ throw new Error(`contract_violation:${checked.violation}`);
114
+ }
115
+ if (canConfirm) {
116
+ throw new Error(`contract_violation:${lastViolation ?? "result_envelope_missing"}`);
117
+ }
118
+ await sleep(STRUCTURED_RESULT_POLL_MS);
119
+ if (signal?.aborted) {
120
+ throw new Error("aborted");
121
+ }
122
+ }
123
+ throw new Error(`contract_violation:${lastViolation ?? "result_envelope_missing"}`);
124
+ }
125
+ finally {
126
+ emitter.off("candidate", onCandidate);
127
+ }
128
+ };
129
+ exports.waitForStructuredEnvelope = waitForStructuredEnvelope;
130
+ const extractViolationCode = (error) => {
131
+ const raw = String(error ?? "");
132
+ const marker = "contract_violation:";
133
+ const idx = raw.indexOf(marker);
134
+ if (idx < 0)
135
+ return null;
136
+ const code = raw.slice(idx + marker.length).trim();
137
+ return code || null;
138
+ };
139
+ exports.extractViolationCode = extractViolationCode;
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildOutputId = exports.isValidLinkId = exports.buildJobId = exports.readWorkflowDefinitionFromRawDetailed = exports.readWorkflowDefinitionFromRaw = exports.validateWorkflowOutputConfig = exports.validateWorkflowDefinition = exports.savePipelineTemplateWithStorage = exports.savePipelineTemplate = exports.loadPipelineTemplateWithStorage = exports.loadPipelineTemplate = exports.saveWorkflowDefinitionWithStorage = exports.saveWorkflowDefinition = exports.loadWorkflowDefinitionWithStorage = exports.loadWorkflowDefinition = exports.migrateWorkflowDefinitionV2RawToV3 = exports.readTemplateNodesFromRaw = exports.normalizeWorkflowFallbacksWithStorage = exports.normalizeWorkflowFallbacks = exports.workflowToTemplateNodes = exports.mergeTemplateNodesIntoWorkflow = exports.defaultWorkflowDefinition = exports.defaultTemplateNodes = void 0;
4
+ const guards_1 = require("../utils/guards");
5
+ const normalize_1 = require("./workflow/normalize");
6
+ const io_1 = require("./workflow/io");
7
+ const validate_1 = require("./workflow/validate");
8
+ // ====== Re-exports from extracted modules ======
9
+ // Defaults
10
+ var defaults_1 = require("./workflow/defaults");
11
+ Object.defineProperty(exports, "defaultTemplateNodes", { enumerable: true, get: function () { return defaults_1.defaultTemplateNodes; } });
12
+ Object.defineProperty(exports, "defaultWorkflowDefinition", { enumerable: true, get: function () { return defaults_1.defaultWorkflowDefinition; } });
13
+ // Template mapper
14
+ var template_mapper_1 = require("./workflow/template-mapper");
15
+ Object.defineProperty(exports, "mergeTemplateNodesIntoWorkflow", { enumerable: true, get: function () { return template_mapper_1.mergeTemplateNodesIntoWorkflow; } });
16
+ Object.defineProperty(exports, "workflowToTemplateNodes", { enumerable: true, get: function () { return template_mapper_1.workflowToTemplateNodes; } });
17
+ // ====== Normalize fallbacks ======
18
+ const normalizeWorkflowFallbacks = (workflow) => {
19
+ return (0, exports.normalizeWorkflowFallbacksWithStorage)(workflow, {});
20
+ };
21
+ exports.normalizeWorkflowFallbacks = normalizeWorkflowFallbacks;
22
+ const normalizeWorkflowFallbacksWithStorage = (workflow, options) => {
23
+ const currentNodeById = new Map((0, io_1.loadWorkflowDefinitionWithStorage)(options).nodes.map((node) => [node.id, node]));
24
+ const normalizedNodes = workflow.nodes.map((node) => {
25
+ const prev = currentNodeById.get(node.id);
26
+ if (!prev)
27
+ return node;
28
+ const prevAgent = prev.executor.agentId.trim();
29
+ const nextAgent = node.executor.agentId.trim();
30
+ if (prevAgent === nextAgent)
31
+ return node;
32
+ return {
33
+ ...node,
34
+ executor: {
35
+ ...node.executor,
36
+ fallbackAgentId: null,
37
+ },
38
+ };
39
+ });
40
+ return {
41
+ ...workflow,
42
+ nodes: normalizedNodes,
43
+ };
44
+ };
45
+ exports.normalizeWorkflowFallbacksWithStorage = normalizeWorkflowFallbacksWithStorage;
46
+ // ====== Read template nodes from raw ======
47
+ const readTemplateNodesFromRaw = (value) => {
48
+ if (!Array.isArray(value))
49
+ return null;
50
+ const nodes = [];
51
+ for (const item of value) {
52
+ const normalized = (0, normalize_1.normalizeTemplateNode)(item);
53
+ if (!normalized)
54
+ return null;
55
+ nodes.push(normalized);
56
+ }
57
+ const ids = new Set(nodes.map((n) => n.id));
58
+ if (ids.size !== nodes.length)
59
+ return null;
60
+ for (const node of nodes) {
61
+ for (const dep of node.dependsOn) {
62
+ if (!ids.has(dep))
63
+ return null;
64
+ }
65
+ }
66
+ return nodes;
67
+ };
68
+ exports.readTemplateNodesFromRaw = readTemplateNodesFromRaw;
69
+ // ====== Migration ======
70
+ const migrateWorkflowDefinitionV2RawToV3 = (value) => {
71
+ if (!(0, guards_1.isRecord)(value) || value.version !== "2.0") {
72
+ return { ok: false, error: "invalid_workflow_definition", detail: "仅支持从 workflow v2.0 迁移" };
73
+ }
74
+ if (!Array.isArray(value.nodes) || !Array.isArray(value.edges) || !Array.isArray(value.groups)) {
75
+ return { ok: false, error: "invalid_workflow_definition", detail: "workflow.nodes/edges/groups 必须为数组" };
76
+ }
77
+ const nodes = [];
78
+ for (const item of value.nodes) {
79
+ const normalized = (0, normalize_1.normalizeWorkflowNode)(item);
80
+ if (!normalized)
81
+ return { ok: false, error: "invalid_workflow_definition", detail: "workflow.nodes 存在非法节点结构" };
82
+ nodes.push(normalized);
83
+ }
84
+ const edges = [];
85
+ for (const item of value.edges) {
86
+ const normalized = (0, normalize_1.normalizeWorkflowEdge)(item);
87
+ if (!normalized)
88
+ return { ok: false, error: "invalid_workflow_definition", detail: "workflow.edges 存在非法边结构" };
89
+ edges.push(normalized);
90
+ }
91
+ const groups = [];
92
+ for (const item of value.groups) {
93
+ const normalized = (0, normalize_1.normalizeWorkflowGroup)(item);
94
+ if (!normalized)
95
+ return { ok: false, error: "invalid_workflow_definition", detail: "workflow.groups 存在非法并行组结构" };
96
+ groups.push(normalized);
97
+ }
98
+ const workflow = {
99
+ version: "3.0",
100
+ scheduler: (0, normalize_1.normalizeWorkflowScheduler)(value.scheduler),
101
+ plugins: (0, normalize_1.normalizeWorkflowPlugins)(value.plugins),
102
+ output: { mode: "mainline_last", nodeId: null },
103
+ nodes,
104
+ edges,
105
+ groups,
106
+ };
107
+ const validation = (0, validate_1.validateWorkflowGraph)(workflow);
108
+ if (!validation.ok)
109
+ return { ok: false, error: validation.error, detail: validation.detail };
110
+ return { ok: true, workflow };
111
+ };
112
+ exports.migrateWorkflowDefinitionV2RawToV3 = migrateWorkflowDefinitionV2RawToV3;
113
+ // I/O functions re-exported from ./workflow/io
114
+ var io_2 = require("./workflow/io");
115
+ Object.defineProperty(exports, "loadWorkflowDefinition", { enumerable: true, get: function () { return io_2.loadWorkflowDefinition; } });
116
+ Object.defineProperty(exports, "loadWorkflowDefinitionWithStorage", { enumerable: true, get: function () { return io_2.loadWorkflowDefinitionWithStorage; } });
117
+ Object.defineProperty(exports, "saveWorkflowDefinition", { enumerable: true, get: function () { return io_2.saveWorkflowDefinition; } });
118
+ Object.defineProperty(exports, "saveWorkflowDefinitionWithStorage", { enumerable: true, get: function () { return io_2.saveWorkflowDefinitionWithStorage; } });
119
+ Object.defineProperty(exports, "loadPipelineTemplate", { enumerable: true, get: function () { return io_2.loadPipelineTemplate; } });
120
+ Object.defineProperty(exports, "loadPipelineTemplateWithStorage", { enumerable: true, get: function () { return io_2.loadPipelineTemplateWithStorage; } });
121
+ Object.defineProperty(exports, "savePipelineTemplate", { enumerable: true, get: function () { return io_2.savePipelineTemplate; } });
122
+ Object.defineProperty(exports, "savePipelineTemplateWithStorage", { enumerable: true, get: function () { return io_2.savePipelineTemplateWithStorage; } });
123
+ // Validation re-exported from ./workflow/validate
124
+ var validate_2 = require("./workflow/validate");
125
+ Object.defineProperty(exports, "validateWorkflowDefinition", { enumerable: true, get: function () { return validate_2.validateWorkflowDefinition; } });
126
+ Object.defineProperty(exports, "validateWorkflowOutputConfig", { enumerable: true, get: function () { return validate_2.validateWorkflowOutputConfig; } });
127
+ // Normalize/parse re-exported from ./workflow/normalize
128
+ var normalize_2 = require("./workflow/normalize");
129
+ Object.defineProperty(exports, "readWorkflowDefinitionFromRaw", { enumerable: true, get: function () { return normalize_2.readWorkflowDefinitionFromRaw; } });
130
+ Object.defineProperty(exports, "readWorkflowDefinitionFromRawDetailed", { enumerable: true, get: function () { return normalize_2.readWorkflowDefinitionFromRawDetailed; } });
131
+ var pipeline_link_1 = require("./types/pipeline-link");
132
+ Object.defineProperty(exports, "buildJobId", { enumerable: true, get: function () { return pipeline_link_1.buildJobId; } });
133
+ Object.defineProperty(exports, "isValidLinkId", { enumerable: true, get: function () { return pipeline_link_1.isValidLinkId; } });
134
+ var pipeline_output_1 = require("./types/pipeline-output");
135
+ Object.defineProperty(exports, "buildOutputId", { enumerable: true, get: function () { return pipeline_output_1.buildOutputId; } });
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTimelineLogStore = void 0;
4
+ const promises_1 = require("node:fs/promises");
5
+ const node_path_1 = require("node:path");
6
+ const LOG_FILE_NAME = "timeline.log";
7
+ const stringifyTimelineEntry = (entry) => {
8
+ const seen = new WeakSet();
9
+ return JSON.stringify(entry, (_key, value) => {
10
+ // 允许完整保留 detail 内容,但循环引用对象本身无法直接 JSON 化,
11
+ // 这里仅做兜底,避免日志持久化因为异常对象而中断主流程。
12
+ if (typeof value !== "object" || value === null)
13
+ return value;
14
+ if (seen.has(value))
15
+ return "[Circular]";
16
+ seen.add(value);
17
+ return value;
18
+ });
19
+ };
20
+ const createTimelineLogStore = (options) => {
21
+ let writeChain = Promise.resolve();
22
+ const getRunLogFile = (runId) => (0, node_path_1.join)(options.rootDir, runId, LOG_FILE_NAME);
23
+ const appendTimeline = (runId, item) => {
24
+ const logFile = getRunLogFile(runId);
25
+ let line = `${stringifyTimelineEntry({
26
+ id: item.id,
27
+ ts: item.createdAt,
28
+ level: item.level,
29
+ runId,
30
+ text: item.text,
31
+ ...(item.detail === undefined ? {} : { detail: item.detail }),
32
+ })}\n`;
33
+ // 单行大小兜底,防止超大 detail 撑爆日志文件
34
+ const MAX_LOG_LINE_BYTES = 512 * 1024;
35
+ if (Buffer.byteLength(line, "utf8") > MAX_LOG_LINE_BYTES) {
36
+ const truncated = line.slice(0, MAX_LOG_LINE_BYTES);
37
+ const suffix = "[TRUNCATED_LOG_LINE]\n";
38
+ const suffixBytes = Buffer.byteLength(suffix, "utf8");
39
+ line = truncated.slice(0, MAX_LOG_LINE_BYTES - suffixBytes) + suffix;
40
+ }
41
+ writeChain = writeChain
42
+ .catch(() => {
43
+ // 前一次写盘失败后继续后续写入,避免整个队列永久卡死。
44
+ })
45
+ .then(async () => {
46
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(logFile), { recursive: true });
47
+ await (0, promises_1.appendFile)(logFile, line, "utf8");
48
+ });
49
+ return writeChain.catch(() => {
50
+ // 日志持久化失败不能影响流水线运行,这里吞掉异常交给调用方按需记录。
51
+ });
52
+ };
53
+ return {
54
+ appendTimeline,
55
+ };
56
+ };
57
+ exports.createTimelineLogStore = createTimelineLogStore;