cool-workflow 0.1.98 → 0.2.0

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 (306) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +11 -2
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/cli/dispatch.js +236 -0
  11. package/dist/cli/entry.js +120 -0
  12. package/dist/cli/io.js +21 -4
  13. package/dist/cli/parseargv.js +157 -0
  14. package/dist/cli.js +6 -33
  15. package/dist/core/capability-table.js +3534 -0
  16. package/dist/core/format/help.js +314 -0
  17. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  18. package/dist/core/hash.js +137 -0
  19. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  20. package/dist/core/multi-agent/collaboration.js +481 -0
  21. package/dist/core/multi-agent/coordinator.js +515 -0
  22. package/dist/core/multi-agent/eval-replay.js +306 -0
  23. package/dist/core/multi-agent/runtime.js +929 -0
  24. package/dist/core/multi-agent/topology.js +298 -0
  25. package/dist/core/multi-agent/trust-policy.js +197 -0
  26. package/dist/core/pipeline/commit-gate.js +320 -0
  27. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  28. package/dist/core/pipeline/dispatch.js +103 -0
  29. package/dist/core/pipeline/drive-decide.js +227 -0
  30. package/dist/core/pipeline/error-feedback.js +161 -0
  31. package/dist/core/pipeline/loop-expansion.js +124 -0
  32. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  33. package/dist/core/pipeline/runner.js +230 -0
  34. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  35. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  36. package/dist/core/state/node-projection.js +95 -0
  37. package/dist/core/state/node-snapshot.js +230 -0
  38. package/dist/core/state/run-paths.js +93 -0
  39. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  40. package/dist/core/state/schema.js +50 -0
  41. package/dist/core/state/state-explosion/digest.js +243 -0
  42. package/dist/core/state/state-explosion/graph.js +527 -0
  43. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  44. package/dist/core/state/state-explosion/report.js +187 -0
  45. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  46. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  47. package/dist/core/state/types.js +19 -0
  48. package/dist/{validation.js → core/state/validation.js} +64 -131
  49. package/dist/core/trust/evidence-grounding.js +134 -0
  50. package/dist/core/trust/ledger.js +199 -0
  51. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  52. package/dist/core/trust/telemetry-ledger.js +127 -0
  53. package/dist/core/types.js +20 -0
  54. package/dist/core/version.js +16 -0
  55. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  56. package/dist/mcp/dispatch.js +104 -0
  57. package/dist/mcp/server.js +144 -0
  58. package/dist/mcp-server.js +6 -84
  59. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  60. package/dist/shell/app-run-cli.js +88 -0
  61. package/dist/shell/audit-cli.js +259 -0
  62. package/dist/shell/audit-provenance.js +83 -0
  63. package/dist/shell/candidate-scoring-io.js +459 -0
  64. package/dist/shell/collaboration-io.js +264 -0
  65. package/dist/shell/commit-summary.js +104 -0
  66. package/dist/shell/commit.js +290 -0
  67. package/dist/shell/coordinator-io.js +476 -0
  68. package/dist/shell/demo-cli.js +19 -0
  69. package/dist/shell/dispatch.js +162 -0
  70. package/dist/{doctor.js → shell/doctor.js} +123 -56
  71. package/dist/shell/drive.js +873 -0
  72. package/dist/shell/error-feedback-io.js +305 -0
  73. package/dist/shell/eval-io.js +473 -0
  74. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  75. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  76. package/dist/shell/exec-backend-cli.js +88 -0
  77. package/dist/shell/execution-backend/agent.js +475 -0
  78. package/dist/shell/execution-backend/ci.js +15 -0
  79. package/dist/shell/execution-backend/container.js +69 -0
  80. package/dist/shell/execution-backend/envelopes.js +55 -0
  81. package/dist/shell/execution-backend/local.js +113 -0
  82. package/dist/shell/execution-backend/probes.js +175 -0
  83. package/dist/shell/execution-backend/registry.js +402 -0
  84. package/dist/shell/execution-backend/remote.js +128 -0
  85. package/dist/shell/execution-backend/types.js +11 -0
  86. package/dist/shell/feedback-cli.js +81 -0
  87. package/dist/shell/feedback-operations.js +48 -0
  88. package/dist/shell/fs-atomic.js +276 -0
  89. package/dist/shell/harness.js +98 -0
  90. package/dist/shell/ledger-cli.js +212 -0
  91. package/dist/shell/ledger-io.js +169 -0
  92. package/dist/shell/man-cli.js +89 -0
  93. package/dist/shell/metrics-cli.js +98 -0
  94. package/dist/shell/multi-agent-cli.js +1002 -0
  95. package/dist/shell/multi-agent-host.js +563 -0
  96. package/dist/shell/multi-agent-io.js +387 -0
  97. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  98. package/dist/shell/node-store.js +124 -0
  99. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  100. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  101. package/dist/{observability.js → shell/observability.js} +159 -332
  102. package/dist/{onramp.js → shell/onramp.js} +11 -0
  103. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  104. package/dist/shell/operator-ux.js +431 -0
  105. package/dist/shell/orchestrator.js +231 -0
  106. package/dist/shell/pipeline-cli.js +667 -0
  107. package/dist/shell/pipeline.js +217 -0
  108. package/dist/shell/reclamation-io.js +1366 -0
  109. package/dist/shell/registry-cli.js +329 -0
  110. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  111. package/dist/shell/report-cli.js +101 -0
  112. package/dist/shell/report-view-cli.js +117 -0
  113. package/dist/{orchestrator → shell}/report.js +289 -282
  114. package/dist/shell/reporter.js +62 -0
  115. package/dist/shell/run-export-cli.js +106 -0
  116. package/dist/shell/run-export.js +680 -0
  117. package/dist/shell/run-registry-io.js +1014 -0
  118. package/dist/shell/run-store.js +164 -0
  119. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  120. package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
  121. package/dist/shell/scheduling-io.js +311 -0
  122. package/dist/shell/state-cli.js +181 -0
  123. package/dist/shell/state-explosion-cli.js +197 -0
  124. package/dist/shell/telemetry-cli.js +85 -0
  125. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  126. package/dist/shell/telemetry-ledger-io.js +132 -0
  127. package/dist/{term.js → shell/term.js} +36 -46
  128. package/dist/shell/topology-io.js +361 -0
  129. package/dist/shell/trust-audit.js +471 -0
  130. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  131. package/dist/shell/verifier.js +48 -0
  132. package/dist/shell/workbench-host.js +250 -0
  133. package/dist/shell/workbench-text.js +18 -0
  134. package/dist/shell/workbench.js +175 -0
  135. package/dist/shell/worker-cli.js +124 -0
  136. package/dist/shell/worker-isolation.js +852 -0
  137. package/dist/shell/workflow-app-loader.js +650 -0
  138. package/docs/agent-delegation-drive.7.md +2 -0
  139. package/docs/cli-mcp-parity.7.md +280 -219
  140. package/docs/contract-migration-tooling.7.md +2 -0
  141. package/docs/control-plane-scheduling.7.md +2 -0
  142. package/docs/durable-state-and-locking.7.md +2 -0
  143. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  144. package/docs/execution-backends.7.md +2 -0
  145. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  146. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  147. package/docs/multi-agent-operator-ux.7.md +2 -0
  148. package/docs/node-snapshot-diff-replay.7.md +2 -0
  149. package/docs/observability-cost-accounting.7.md +2 -0
  150. package/docs/project-index.md +132 -71
  151. package/docs/real-execution-backends.7.md +2 -0
  152. package/docs/release-and-migration.7.md +2 -0
  153. package/docs/release-tooling.7.md +2 -0
  154. package/docs/run-registry-control-plane.7.md +2 -0
  155. package/docs/run-retention-reclamation.7.md +23 -0
  156. package/docs/state-explosion-management.7.md +2 -0
  157. package/docs/team-collaboration.7.md +2 -0
  158. package/docs/web-desktop-workbench.7.md +2 -0
  159. package/manifest/plugin.manifest.json +1 -1
  160. package/manifest/source-context-profiles.json +9 -13
  161. package/package.json +1 -1
  162. package/scripts/agents/cw-attest-wrap.js +2 -2
  163. package/scripts/bump-version.js +4 -3
  164. package/scripts/canonical-apps.js +4 -4
  165. package/scripts/dogfood-architecture-review.js +2 -3
  166. package/scripts/dogfood-release.js +3 -3
  167. package/scripts/gen-parity-doc.js +15 -2
  168. package/scripts/golden-path.js +4 -4
  169. package/scripts/onramp-check.js +1 -1
  170. package/scripts/parity-check.js +38 -21
  171. package/scripts/release-flow.js +2 -2
  172. package/scripts/sync-project-index.js +51 -27
  173. package/scripts/validate-run-state-schema.js +2 -2
  174. package/scripts/version-sync-check.js +30 -30
  175. package/dist/candidate-scoring.js +0 -729
  176. package/dist/capability-core.js +0 -1189
  177. package/dist/capability-registry.js +0 -885
  178. package/dist/cli/command-surface.js +0 -494
  179. package/dist/cli/format.js +0 -56
  180. package/dist/cli/handlers/audit.js +0 -82
  181. package/dist/cli/handlers/blackboard.js +0 -81
  182. package/dist/cli/handlers/candidate.js +0 -40
  183. package/dist/cli/handlers/clones.js +0 -34
  184. package/dist/cli/handlers/collaboration.js +0 -61
  185. package/dist/cli/handlers/eval.js +0 -40
  186. package/dist/cli/handlers/ledger.js +0 -169
  187. package/dist/cli/handlers/maintenance.js +0 -107
  188. package/dist/cli/handlers/multi-agent.js +0 -165
  189. package/dist/cli/handlers/node.js +0 -41
  190. package/dist/cli/handlers/operational.js +0 -155
  191. package/dist/cli/handlers/operator.js +0 -146
  192. package/dist/cli/handlers/registry.js +0 -68
  193. package/dist/cli/handlers/run.js +0 -153
  194. package/dist/cli/handlers/scheduling.js +0 -132
  195. package/dist/cli/handlers/workbench.js +0 -41
  196. package/dist/cli/handlers/worker.js +0 -45
  197. package/dist/cli/run-summary.js +0 -45
  198. package/dist/clones.js +0 -162
  199. package/dist/collaboration.js +0 -726
  200. package/dist/commit.js +0 -592
  201. package/dist/compare.js +0 -18
  202. package/dist/coordinator/classify.js +0 -45
  203. package/dist/coordinator/paths.js +0 -42
  204. package/dist/coordinator/util.js +0 -126
  205. package/dist/coordinator.js +0 -990
  206. package/dist/daemon.js +0 -44
  207. package/dist/dispatch.js +0 -250
  208. package/dist/drive.js +0 -864
  209. package/dist/error-feedback.js +0 -419
  210. package/dist/evidence-grounding.js +0 -184
  211. package/dist/execution-backend/agent.js +0 -354
  212. package/dist/execution-backend/probes.js +0 -112
  213. package/dist/execution-backend/util.js +0 -47
  214. package/dist/execution-backend.js +0 -961
  215. package/dist/gates.js +0 -48
  216. package/dist/harness.js +0 -61
  217. package/dist/ledger.js +0 -313
  218. package/dist/loop-expansion.js +0 -60
  219. package/dist/mcp/tool-call.js +0 -470
  220. package/dist/mcp/tool-definitions.js +0 -1066
  221. package/dist/mcp-surface.js +0 -30
  222. package/dist/multi-agent/graph.js +0 -84
  223. package/dist/multi-agent/helpers.js +0 -141
  224. package/dist/multi-agent/ids.js +0 -20
  225. package/dist/multi-agent/paths.js +0 -22
  226. package/dist/multi-agent-eval/normalize.js +0 -51
  227. package/dist/multi-agent-eval.js +0 -678
  228. package/dist/multi-agent-host.js +0 -777
  229. package/dist/multi-agent.js +0 -984
  230. package/dist/node-projection.js +0 -59
  231. package/dist/node-snapshot.js +0 -260
  232. package/dist/operator-ux.js +0 -631
  233. package/dist/orchestrator/app-operations.js +0 -211
  234. package/dist/orchestrator/audit-operations.js +0 -182
  235. package/dist/orchestrator/candidate-operations.js +0 -117
  236. package/dist/orchestrator/cli-options.js +0 -294
  237. package/dist/orchestrator/collaboration-operations.js +0 -86
  238. package/dist/orchestrator/feedback-operations.js +0 -81
  239. package/dist/orchestrator/host-operations.js +0 -78
  240. package/dist/orchestrator/lifecycle-operations.js +0 -650
  241. package/dist/orchestrator/migration-operations.js +0 -44
  242. package/dist/orchestrator/multi-agent-operations.js +0 -362
  243. package/dist/orchestrator/topology-operations.js +0 -84
  244. package/dist/orchestrator.js +0 -925
  245. package/dist/pipeline-runner.js +0 -285
  246. package/dist/reclamation/hash.js +0 -72
  247. package/dist/reclamation.js +0 -812
  248. package/dist/reporter.js +0 -67
  249. package/dist/run-export.js +0 -815
  250. package/dist/run-registry/derive.js +0 -175
  251. package/dist/run-registry/format.js +0 -124
  252. package/dist/run-registry/gc.js +0 -251
  253. package/dist/run-registry/policy.js +0 -16
  254. package/dist/run-registry/queue.js +0 -115
  255. package/dist/run-registry.js +0 -850
  256. package/dist/run-state-schema.js +0 -68
  257. package/dist/scheduling.js +0 -184
  258. package/dist/state-explosion.js +0 -1014
  259. package/dist/state.js +0 -367
  260. package/dist/telemetry-ledger.js +0 -196
  261. package/dist/topology.js +0 -565
  262. package/dist/triggers.js +0 -184
  263. package/dist/trust-audit.js +0 -644
  264. package/dist/types/blackboard.js +0 -2
  265. package/dist/types/candidate.js +0 -2
  266. package/dist/types/collaboration.js +0 -2
  267. package/dist/types/core.js +0 -2
  268. package/dist/types/drive.js +0 -10
  269. package/dist/types/error-feedback.js +0 -2
  270. package/dist/types/evidence-reasoning.js +0 -2
  271. package/dist/types/execution-backend.js +0 -2
  272. package/dist/types/multi-agent.js +0 -2
  273. package/dist/types/observability.js +0 -2
  274. package/dist/types/pipeline.js +0 -2
  275. package/dist/types/reclamation.js +0 -8
  276. package/dist/types/report-bundle.js +0 -6
  277. package/dist/types/result.js +0 -2
  278. package/dist/types/run-registry.js +0 -2
  279. package/dist/types/run.js +0 -2
  280. package/dist/types/sandbox.js +0 -2
  281. package/dist/types/schedule.js +0 -2
  282. package/dist/types/state-node.js +0 -2
  283. package/dist/types/topology.js +0 -2
  284. package/dist/types/trust.js +0 -2
  285. package/dist/types/workbench.js +0 -2
  286. package/dist/types/worker.js +0 -2
  287. package/dist/types/workflow-app.js +0 -2
  288. package/dist/types.js +0 -44
  289. package/dist/util/fingerprint.js +0 -19
  290. package/dist/util/fingerprint.test.js +0 -27
  291. package/dist/verifier.js +0 -78
  292. package/dist/version.js +0 -8
  293. package/dist/workbench-host.js +0 -192
  294. package/dist/workbench.js +0 -192
  295. package/dist/worker-accept/acceptance.js +0 -114
  296. package/dist/worker-accept/blackboard-fanout.js +0 -80
  297. package/dist/worker-accept/blackboard-linkage.js +0 -19
  298. package/dist/worker-accept/context.js +0 -2
  299. package/dist/worker-accept/telemetry-ledger.js +0 -126
  300. package/dist/worker-accept/validation.js +0 -77
  301. package/dist/worker-accept/verifier-completion.js +0 -73
  302. package/dist/worker-isolation/helpers.js +0 -51
  303. package/dist/worker-isolation/paths.js +0 -46
  304. package/dist/worker-isolation.js +0 -656
  305. package/dist/workflow-api.js +0 -131
  306. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ // mcp/dispatch.ts — the generic MCP tool-call reader over
3
+ // core/capability-table.ts.
4
+ //
5
+ // MILESTONE 2 (v2/PLAN.md build order, step 2). Replaces the old build's
6
+ // 196-arm `callTool` switch (src/mcp/tool-call.ts) with a data-driven
7
+ // lookup: resolve the row by tool name, check its declared required-arg
8
+ // groups (SPEC/mcp.md's `McpBinding.requiredArgs`), re-base `cwd` if
9
+ // given, then call the row's `mcp.handler`. Every future milestone adds
10
+ // capabilities as capability-table ROWS; this file is not touched again.
11
+ //
12
+ // `cwd` handling here (resolve, must be a directory, inject the resolved
13
+ // path back into a COPY of args) matches SPEC/mcp.md invariant 7. This
14
+ // milestone's own real handlers (`list`, `sandbox.list`, `status`) do not
15
+ // themselves need cwd, but the re-base is still performed uniformly so a
16
+ // later milestone's handler can rely on it without dispatch.ts changing.
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.toolDefinitions = toolDefinitions;
52
+ exports.requiredToolArguments = requiredToolArguments;
53
+ exports.callTool = callTool;
54
+ const fs = __importStar(require("node:fs"));
55
+ const path = __importStar(require("node:path"));
56
+ const capability_table_1 = require("../core/capability-table");
57
+ /** `toolDefinitions()` — the full, ordered `tools/list` array. */
58
+ function toolDefinitions() {
59
+ return (0, capability_table_1.mcpToolDefinitions)();
60
+ }
61
+ /** SPEC/mcp.md invariant 6 / src/mcp-surface.ts:7-25 — required-argument
62
+ * groups are declared data, not code. `undefined`/`null` become `{}`; a
63
+ * non-object throws; a group "keyA|keyB" passes when at least one named
64
+ * key is not `undefined`, not `null`, and not `""`. */
65
+ function requiredToolArguments(name, value) {
66
+ const args = value === undefined || value === null ? {} : value;
67
+ if (typeof args !== "object" || Array.isArray(args)) {
68
+ throw new Error(`MCP tool ${name} arguments must be an object.`);
69
+ }
70
+ const row = (0, capability_table_1.findCapabilityByMcpTool)(name);
71
+ const requiredArgs = row?.mcp?.requiredArgs ?? [];
72
+ for (const group of requiredArgs) {
73
+ const keys = group.split("|");
74
+ const satisfied = keys.some((key) => {
75
+ const v = args[key];
76
+ return v !== undefined && v !== null && v !== "";
77
+ });
78
+ if (!satisfied) {
79
+ throw new Error(`MCP tool ${name} missing required argument: ${keys.join(" or ")}`);
80
+ }
81
+ }
82
+ return args;
83
+ }
84
+ /** `callTool(name, args)` — resolves `cwd` (SPEC/mcp.md invariant 7),
85
+ * checks required args, then calls the row's `mcp.handler`. Throws
86
+ * `Unknown tool: <name>` for a name with no row (src/mcp/tool-call.ts:513
87
+ * parity). */
88
+ function callTool(name, rawArgs) {
89
+ const row = (0, capability_table_1.findCapabilityByMcpTool)(name);
90
+ if (!row || !row.mcp) {
91
+ throw new Error(`Unknown tool: ${name}`);
92
+ }
93
+ const args = requiredToolArguments(name, rawArgs);
94
+ const resolvedArgs = { ...args };
95
+ const cwdInput = args.cwd;
96
+ if (typeof cwdInput === "string" && cwdInput.length > 0) {
97
+ const resolved = path.resolve(cwdInput);
98
+ if (!fs.statSync(resolved).isDirectory()) {
99
+ throw new Error(`MCP cwd is not a directory: ${resolved}`);
100
+ }
101
+ resolvedArgs.cwd = resolved;
102
+ }
103
+ return row.mcp.handler(resolvedArgs);
104
+ }
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ // mcp/server.ts — the stdio JSON-RPC loop: initialize / tools/list /
3
+ // tools/call.
4
+ //
5
+ // MILESTONE 2 (v2/PLAN.md build order, step 2). Byte-exact port of the
6
+ // framing rules in SPEC/mcp.md's "JSON-RPC methods" / "Exact outputs" /
7
+ // "Invariants and error behavior" #9 / "Edge cases" sections:
8
+ // - transport: stdin/stdout, ONE JSON object per line, no
9
+ // Content-Length headers (mcp.md:13);
10
+ // - `initialize` -> protocolVersion/capabilities/serverInfo, ignoring
11
+ // params (mcp.md:19,263-269);
12
+ // - `tools/list` -> { tools: [...] } from core/capability-table.ts via
13
+ // mcp/dispatch.ts, ignoring params (mcp.md:20,271-277);
14
+ // - `tools/call` -> { content: [{ type: "text", text: <2-space pretty
15
+ // JSON string> }] } (mcp.md:21,279-285);
16
+ // - any other method: error -32601 if the request has an `id` key,
17
+ // otherwise NO reply at all (mcp.md:22,427-428);
18
+ // - a request with `"id": null` DOES get an error answer for an unknown
19
+ // method (the guard is `id !== undefined`, and `null !== undefined`)
20
+ // (mcp.md:427);
21
+ // - `initialize`/`tools/list`/`tools/call` answer even with no `id` key
22
+ // at all — the reply then omits `id` (JSON.stringify drops
23
+ // `undefined`) (mcp.md:428);
24
+ // - parse errors always answer with `id: null`, even when the broken
25
+ // line contained one (mcp.md:429, 290-291);
26
+ // - a line that parses to non-object JSON (number/string/array/null)
27
+ // gets -32600 "Invalid Request: not a JSON-RPC object" (mcp.md:292,
28
+ // 430);
29
+ // - many requests in one stdin chunk: split on every "\n", .trim() each
30
+ // line, skip empty lines (mcp.md:431);
31
+ // - `tools/call` with a missing/empty/whitespace-only `name` throws
32
+ // "MCP tools/call missing required field: name" as -32000 (mcp.md:432);
33
+ // - `arguments: null`/absent -> `{}` before the required-args check
34
+ // (mcp.md:433);
35
+ // - fail-closed input framing: MAX_LINE_BYTES = 16 * 1024 * 1024; when
36
+ // the unconsumed buffer exceeds this with no newline yet, the partial
37
+ // bytes are DROPPED and a -32700 "Parse error: request line exceeds
38
+ // 16777216 bytes" is sent with id: null (mcp.md:291,418).
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.startServer = startServer;
41
+ const version_1 = require("../core/version");
42
+ const dispatch_1 = require("./dispatch");
43
+ const MAX_LINE_BYTES = 16 * 1024 * 1024;
44
+ function writeMessage(message) {
45
+ process.stdout.write(`${JSON.stringify(message)}\n`);
46
+ }
47
+ function errorMessage(id, code, message) {
48
+ return { jsonrpc: "2.0", id: id === undefined ? null : id, error: { code, message } };
49
+ }
50
+ function resultMessage(id, result) {
51
+ const message = { jsonrpc: "2.0", result };
52
+ if (id !== undefined)
53
+ message.id = id;
54
+ return message;
55
+ }
56
+ /** Handles one already-parsed JSON-RPC request object. May write zero or
57
+ * one reply line to stdout. */
58
+ function handleRequest(message) {
59
+ const hasId = Object.prototype.hasOwnProperty.call(message, "id");
60
+ const id = message.id;
61
+ if (typeof message.method !== "string") {
62
+ // Not really reachable from valid JSON-RPC callers, but stay
63
+ // fail-closed rather than throw synchronously out of the read loop.
64
+ if (hasId)
65
+ writeMessage(errorMessage(id, -32601, `Unknown method: ${String(message.method)}`));
66
+ return;
67
+ }
68
+ try {
69
+ switch (message.method) {
70
+ case "initialize": {
71
+ writeMessage(resultMessage(id, {
72
+ protocolVersion: "2024-11-05",
73
+ capabilities: { tools: {} },
74
+ serverInfo: { name: "cool-workflow", version: version_1.CURRENT_COOL_WORKFLOW_VERSION },
75
+ }));
76
+ return;
77
+ }
78
+ case "tools/list": {
79
+ writeMessage(resultMessage(id, { tools: (0, dispatch_1.toolDefinitions)() }));
80
+ return;
81
+ }
82
+ case "tools/call": {
83
+ const params = message.params ?? {};
84
+ const name = params.name;
85
+ if (typeof name !== "string" || name.trim() === "") {
86
+ throw new Error("MCP tools/call missing required field: name");
87
+ }
88
+ const args = params.arguments;
89
+ const coreResult = (0, dispatch_1.callTool)(name, args ?? {});
90
+ writeMessage(resultMessage(id, { content: [{ type: "text", text: JSON.stringify(coreResult, null, 2) }] }));
91
+ return;
92
+ }
93
+ default: {
94
+ if (hasId)
95
+ writeMessage(errorMessage(id, -32601, `Unknown method: ${message.method}`));
96
+ return;
97
+ }
98
+ }
99
+ }
100
+ catch (error) {
101
+ const text = error instanceof Error ? error.message : String(error);
102
+ writeMessage(errorMessage(id, -32000, text));
103
+ }
104
+ }
105
+ /** Handles one raw (already-trimmed, non-empty) stdin line. */
106
+ function handleLine(line) {
107
+ let parsed;
108
+ try {
109
+ parsed = JSON.parse(line);
110
+ }
111
+ catch (error) {
112
+ const detail = error instanceof Error ? error.message : String(error);
113
+ writeMessage(errorMessage(null, -32700, `Parse error: ${detail}`));
114
+ return;
115
+ }
116
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
117
+ writeMessage(errorMessage(null, -32600, "Invalid Request: not a JSON-RPC object"));
118
+ return;
119
+ }
120
+ handleRequest(parsed);
121
+ }
122
+ /** Starts the stdio read loop. Never resolves — the server is long-lived
123
+ * and stops only when its stdin closes / the process exits. */
124
+ function startServer() {
125
+ process.stdin.setEncoding("utf8");
126
+ let buffer = "";
127
+ process.stdin.on("data", (chunk) => {
128
+ buffer += chunk;
129
+ for (;;) {
130
+ const newlineIndex = buffer.indexOf("\n");
131
+ if (newlineIndex === -1)
132
+ break;
133
+ const line = buffer.slice(0, newlineIndex);
134
+ buffer = buffer.slice(newlineIndex + 1);
135
+ const trimmed = line.trim();
136
+ if (trimmed)
137
+ handleLine(trimmed);
138
+ }
139
+ if (buffer.length > MAX_LINE_BYTES) {
140
+ buffer = "";
141
+ writeMessage(errorMessage(null, -32700, `Parse error: request line exceeds ${MAX_LINE_BYTES} bytes`));
142
+ }
143
+ });
144
+ }
@@ -1,87 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ // src/mcp-server.ts — the real MCP server binary entry point. Compiles to
4
+ // dist/mcp-server.js, a sibling of dist/cli.js. Byte-exact shape of the
5
+ // old build's src/mcp-server.ts (scripts/mcp-server.js requires it; a
6
+ // real MCP client launches it directly with `node`).
3
7
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const mcp_surface_1 = require("./mcp-surface");
5
- const version_1 = require("./version");
6
- process.stdin.setEncoding("utf8");
7
- let buffer = "";
8
- // A single line-delimited JSON-RPC request is bounded; an un-terminated stream
9
- // (a peer that never sends a newline, or a slow byte dribble) must not grow the
10
- // long-lived server's buffer without limit until it OOMs. Cap the unconsumed
11
- // buffer and fail closed on a frame that exceeds it.
12
- const MAX_LINE_BYTES = 16 * 1024 * 1024;
13
- process.stdin.on("data", (chunk) => {
14
- buffer += chunk;
15
- let newlineIndex;
16
- while ((newlineIndex = buffer.indexOf("\n")) >= 0) {
17
- const line = buffer.slice(0, newlineIndex).trim();
18
- buffer = buffer.slice(newlineIndex + 1);
19
- if (line)
20
- handleLine(line);
21
- }
22
- if (buffer.length > MAX_LINE_BYTES) {
23
- // No newline in an over-cap buffer => a malformed or hostile frame. Reject it
24
- // and drop the partial bytes rather than accumulate toward OOM.
25
- buffer = "";
26
- sendError(null, -32700, `Parse error: request line exceeds ${MAX_LINE_BYTES} bytes`);
27
- }
28
- });
29
- function handleLine(line) {
30
- let message;
31
- try {
32
- message = JSON.parse(line);
33
- }
34
- catch (error) {
35
- sendError(null, -32700, `Parse error: ${messageOf(error)}`);
36
- return;
37
- }
38
- if (message === null || typeof message !== "object" || Array.isArray(message)) {
39
- sendError(null, -32600, "Invalid Request: not a JSON-RPC object");
40
- return;
41
- }
42
- try {
43
- if (message.method === "initialize") {
44
- sendResult(message.id, {
45
- protocolVersion: "2024-11-05",
46
- capabilities: { tools: {} },
47
- serverInfo: { name: "cool-workflow", version: version_1.CURRENT_COOL_WORKFLOW_VERSION }
48
- });
49
- return;
50
- }
51
- if (message.method === "tools/list") {
52
- sendResult(message.id, { tools: (0, mcp_surface_1.toolDefinitions)() });
53
- return;
54
- }
55
- if (message.method === "tools/call") {
56
- const toolName = requiredToolName(message.params?.name);
57
- const toolArgs = (0, mcp_surface_1.requiredToolArguments)(toolName, message.params?.arguments);
58
- const result = (0, mcp_surface_1.callTool)(toolName, toolArgs);
59
- sendResult(message.id, {
60
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
61
- });
62
- return;
63
- }
64
- if (message.id !== undefined)
65
- sendError(message.id, -32601, `Unknown method: ${message.method}`);
66
- }
67
- catch (error) {
68
- sendError(message.id, -32000, messageOf(error));
69
- }
70
- }
71
- function requiredToolName(value) {
72
- if (typeof value !== "string" || !value.trim())
73
- throw new Error("MCP tools/call missing required field: name");
74
- return value;
75
- }
76
- function sendResult(id, result) {
77
- send({ jsonrpc: "2.0", id, result });
78
- }
79
- function sendError(id, code, message) {
80
- send({ jsonrpc: "2.0", id, error: { code, message } });
81
- }
82
- function send(message) {
83
- process.stdout.write(`${JSON.stringify(message)}\n`);
84
- }
85
- function messageOf(error) {
86
- return error instanceof Error ? error.message : String(error);
87
- }
8
+ const server_1 = require("./mcp/server");
9
+ (0, server_1.startServer)();
@@ -1,40 +1,78 @@
1
1
  "use strict";
2
- // Agent Delegation Config (v0.1.38)POLICY expressed as DATA. WHICH agent
3
- // (claude / codex / ollama / an HTTP endpoint) fulfills each worker is a plain,
4
- // diffable config record, NEVER a CW dependency. Resolution order is
5
- // flags > env > a durable $CW_HOME/agent-config.json > fail-closed.
2
+ // shell/agent-config.tsagent delegation config: env/file/flag resolution
3
+ // order, CW_HOME resolution, builtin:<name> expansion, PATH auto-detect.
6
4
  //
7
- // BSD discipline:
8
- // - MECHANISM vs POLICY: the `agent` backend is mechanism; this file is the
9
- // policy data it reads. The kernel never learns which vendor was chosen.
10
- // - NO SECRETS IN COMMITTED STATE [load-bearing]: the durable config holds a
11
- // command-TEMPLATE + endpoint + operator-chosen model only. API keys come from
12
- // the AGENT's own inherited env, never written into .cw/ or the config file.
13
- // Any secret-looking arg is stripped before it is persisted OR shown.
14
- // - DETERMINISTIC SHOW: `agentConfigShow` is a pure projection of env + file —
15
- // no now-derived field — so `cw backend agent config show --json` is
16
- // byte-identical to the MCP tool (CLI<->MCP parity).
5
+ // MILESTONE 5 (v2/PLAN.md build order, step 5). Byte-exact port of
6
+ // plugins/cool-workflow/src/agent-config.ts.
17
7
  //
18
- // See docs/agent-delegation-drive.7.md.
19
- var __importDefault = (this && this.__importDefault) || function (mod) {
20
- return (mod && mod.__esModule) ? mod : { "default": mod };
21
- };
8
+ // Resolution order per field: flags > env > a durable
9
+ // $CW_HOME/agent-config.json > auto-detect > none. NO SECRETS IN COMMITTED
10
+ // STATE: the durable config holds a command-TEMPLATE + endpoint + operator-
11
+ // chosen model only; a secret-looking arg is stripped before persisting or
12
+ // showing.
13
+ //
14
+ // Evidence: SPEC/execution-backend.md "Agent delegation config".
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
31
+ var __importStar = (this && this.__importStar) || (function () {
32
+ var ownKeys = function(o) {
33
+ ownKeys = Object.getOwnPropertyNames || function (o) {
34
+ var ar = [];
35
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
+ return ar;
37
+ };
38
+ return ownKeys(o);
39
+ };
40
+ return function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ })();
22
48
  Object.defineProperty(exports, "__esModule", { value: true });
23
49
  exports.AGENT_CONFIG_SCHEMA_VERSION = void 0;
50
+ exports.resolveCwHome = resolveCwHome;
24
51
  exports.agentConfigPath = agentConfigPath;
25
52
  exports.loadAgentConfigFile = loadAgentConfigFile;
26
53
  exports.resolveAgentConfig = resolveAgentConfig;
27
54
  exports.agentConfigured = agentConfigured;
28
55
  exports.setAgentConfigFile = setAgentConfigFile;
29
56
  exports.agentConfigShow = agentConfigShow;
30
- const node_fs_1 = __importDefault(require("node:fs"));
31
- const node_path_1 = __importDefault(require("node:path"));
32
- const run_registry_1 = require("./run-registry");
33
- const execution_backend_1 = require("./execution-backend");
34
- const state_1 = require("./state");
57
+ exports.backendAgentConfigSet = backendAgentConfigSet;
58
+ exports.backendAgentConfigShow = backendAgentConfigShow;
59
+ const fs = __importStar(require("node:fs"));
60
+ const os = __importStar(require("node:os"));
61
+ const path = __importStar(require("node:path"));
62
+ const fs_atomic_1 = require("./fs-atomic");
63
+ const agent_1 = require("./execution-backend/agent");
35
64
  exports.AGENT_CONFIG_SCHEMA_VERSION = 1;
65
+ /** CW_HOME resolution: CW_HOME > XDG_STATE_HOME/cool-workflow > ~/.local/state/cool-workflow. */
66
+ function resolveCwHome(env = process.env) {
67
+ if (env.CW_HOME && String(env.CW_HOME).trim())
68
+ return path.resolve(String(env.CW_HOME));
69
+ if (env.XDG_STATE_HOME && String(env.XDG_STATE_HOME).trim()) {
70
+ return path.join(path.resolve(String(env.XDG_STATE_HOME)), "cool-workflow");
71
+ }
72
+ return path.join(os.homedir(), ".local", "state", "cool-workflow");
73
+ }
36
74
  function agentConfigPath(env = process.env) {
37
- return node_path_1.default.join((0, run_registry_1.resolveCwHome)(env), "agent-config.json");
75
+ return path.join(resolveCwHome(env), "agent-config.json");
38
76
  }
39
77
  function trimmed(value) {
40
78
  if (typeof value !== "string")
@@ -42,8 +80,6 @@ function trimmed(value) {
42
80
  const out = value.trim();
43
81
  return out ? out : undefined;
44
82
  }
45
- /** Parse a boolean from a flag (boolean) or an env/file string. Returns undefined
46
- * for unset/unrecognized so `firstDefined` falls through to the next layer. */
47
83
  function boolish(value) {
48
84
  if (typeof value === "boolean")
49
85
  return value;
@@ -68,8 +104,9 @@ function firstDefined(...values) {
68
104
  return value;
69
105
  return undefined;
70
106
  }
71
- /** Split a single command string ("claude -p {{manifest}}") into binary + argv
72
- * template — NEVER shell-interpreted. An explicit args array always wins. */
107
+ /** Split a single command string ("claude -p {{manifest}}") into binary +
108
+ * argv template — NEVER shell-interpreted. An explicit args array always
109
+ * wins. */
73
110
  function splitCommand(command, args) {
74
111
  if (!command)
75
112
  return { command: undefined, args };
@@ -85,10 +122,10 @@ function splitCommand(command, args) {
85
122
  * treated as absent (fail closed). */
86
123
  function loadAgentConfigFile(env = process.env) {
87
124
  const file = agentConfigPath(env);
88
- if (!node_fs_1.default.existsSync(file))
125
+ if (!fs.existsSync(file))
89
126
  return undefined;
90
127
  try {
91
- const parsed = JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
128
+ const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
92
129
  return {
93
130
  schemaVersion: 1,
94
131
  command: trimmed(parsed.command),
@@ -98,7 +135,7 @@ function loadAgentConfigFile(env = process.env) {
98
135
  timeoutMs: typeof parsed.timeoutMs === "number" ? parsed.timeoutMs : undefined,
99
136
  attestPublicKey: trimmed(parsed.attestPublicKey),
100
137
  requireAttestedTelemetry: boolish(parsed.requireAttestedTelemetry),
101
- source: "file"
138
+ source: "file",
102
139
  };
103
140
  }
104
141
  catch {
@@ -116,7 +153,7 @@ function agentConfigFromEnv(env) {
116
153
  timeoutMs: trimmed(env.CW_AGENT_TIMEOUT_MS) ? Number(env.CW_AGENT_TIMEOUT_MS) : undefined,
117
154
  attestPublicKey: trimmed(env.CW_AGENT_ATTEST_PUBKEY),
118
155
  requireAttestedTelemetry: boolish(env.CW_REQUIRE_ATTESTED_TELEMETRY),
119
- source: "env"
156
+ source: "env",
120
157
  };
121
158
  }
122
159
  function agentConfigFromArgs(args) {
@@ -133,51 +170,41 @@ function agentConfigFromArgs(args) {
133
170
  timeoutMs: rawTimeout !== undefined ? Number(rawTimeout) : undefined,
134
171
  attestPublicKey: trimmed(args.agentAttestPublicKey ?? args["agent-attest-public-key"]),
135
172
  requireAttestedTelemetry: boolish(args.requireAttestedTelemetry ?? args["require-attested-telemetry"]),
136
- source: "flag"
173
+ source: "flag",
137
174
  };
138
175
  }
139
- // Bundled agent templates, addressable by a stable name so an operator (or an
140
- // npx/global install, where $(pwd)-relative paths don't exist) can configure a
141
- // WORKING agent without knowing where the package landed on disk:
142
- // --agent-command builtin:claude (or CW_AGENT_COMMAND=builtin:claude)
143
- // resolves to the packaged wrapper invocation. Still pure config — the template
144
- // is an out-of-process delegation script; CW never calls a model API.
145
- //
146
- // The builtin set is DATA, not a kernel TS literal (FreeBSD-audit L15): it lives
147
- // in scripts/agents/builtin-templates.json (vendor name -> wrapper script name).
148
- // Adding a vendor is a content/distribution step (drop a wrapper + a JSON line),
149
- // not a kernel edit — keeping CW vendor-agnostic at the source level.
150
- /** Detect which known agent CLIs are on PATH. Returns the first found, or undefined.
151
- * Skipped when CW_NO_AUTO_AGENT=1 (test sandboxes, CI without real agents). */
176
+ /** Detect which known agent CLIs are on PATH. Returns the first found, or
177
+ * undefined. Skipped when CW_NO_AUTO_AGENT=1. */
152
178
  function detectAgentFromPath(env = process.env) {
153
179
  if (env.CW_NO_AUTO_AGENT === "1")
154
180
  return undefined;
155
181
  const known = ["claude", "codex", "gemini", "opencode"];
156
- const dirs = (env.PATH || "").split(node_path_1.default.delimiter).filter(Boolean);
182
+ const dirs = (env.PATH || "").split(path.delimiter).filter(Boolean);
157
183
  const exts = process.platform === "win32" ? (env.PATHEXT || ".EXE;.CMD;.BAT").split(";") : [""];
184
+ const templates = builtinAgentTemplates();
158
185
  for (const name of known) {
159
- // Also check if the builtin template exists (so we don't suggest an unsupported agent).
160
- const templates = builtinAgentTemplates();
161
186
  if (!templates[name])
162
187
  continue;
163
188
  for (const dir of dirs) {
164
189
  for (const ext of exts) {
165
190
  try {
166
- if (node_fs_1.default.statSync(node_path_1.default.join(dir, name + ext)).isFile())
191
+ if (fs.statSync(path.join(dir, name + ext)).isFile())
167
192
  return name;
168
193
  }
169
- catch { /* keep looking */ }
194
+ catch {
195
+ /* keep looking */
196
+ }
170
197
  }
171
198
  }
172
199
  }
173
200
  return undefined;
174
201
  }
175
202
  function builtinAgentTemplates() {
176
- const agentsDir = node_path_1.default.join(__dirname, "..", "scripts", "agents");
177
- const manifest = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(agentsDir, "builtin-templates.json"), "utf8"));
203
+ const agentsDir = path.join(__dirname, "..", "..", "scripts", "agents");
204
+ const manifest = JSON.parse(fs.readFileSync(path.join(agentsDir, "builtin-templates.json"), "utf8"));
178
205
  const out = {};
179
206
  for (const [name, script] of Object.entries(manifest.templates || {})) {
180
- out[name] = `node ${node_path_1.default.join(agentsDir, script)} {{input}} {{result}}`;
207
+ out[name] = `node ${path.join(agentsDir, script)} {{input}} {{result}}`;
181
208
  }
182
209
  return out;
183
210
  }
@@ -192,8 +219,9 @@ function expandBuiltinAgentCommand(command) {
192
219
  }
193
220
  return template;
194
221
  }
195
- /** Resolve the EFFECTIVE agent config: flags > env > file > none. The returned
196
- * `source` names the layer the command/endpoint came from. */
222
+ /** Resolve the EFFECTIVE agent config: flags > env > file > auto-detect >
223
+ * none. The returned `source` names the layer the command/endpoint came
224
+ * from. */
197
225
  function resolveAgentConfig(args = {}, env = process.env) {
198
226
  const flagCfg = agentConfigFromArgs(args);
199
227
  const envCfg = agentConfigFromEnv(env);
@@ -212,9 +240,6 @@ function resolveAgentConfig(args = {}, env = process.env) {
212
240
  : fileCfg && (fileCfg.command || fileCfg.endpoint)
213
241
  ? "file"
214
242
  : "none";
215
- // Auto-detect: if no agent is configured, check PATH for known agent CLIs and
216
- // resolve via builtin:<name>. This makes `cw quickstart` work without
217
- // --agent-command on machines where claude/codex/gemini/opencode are installed.
218
243
  let finalCommand = command;
219
244
  let finalSource = source;
220
245
  let finalModel = model;
@@ -223,27 +248,38 @@ function resolveAgentConfig(args = {}, env = process.env) {
223
248
  if (detected) {
224
249
  finalCommand = expandBuiltinAgentCommand(`builtin:${detected}`);
225
250
  finalSource = "auto";
226
- // Store the detected vendor name as the model hint so doctor can show it.
227
251
  if (!finalModel)
228
252
  finalModel = `builtin:${detected}`;
229
253
  }
230
254
  }
231
- return { schemaVersion: 1, command: finalCommand, args: cfgArgs, endpoint, model: finalModel, timeoutMs, attestPublicKey, requireAttestedTelemetry, source: finalSource };
255
+ return {
256
+ schemaVersion: 1,
257
+ command: finalCommand,
258
+ args: cfgArgs,
259
+ endpoint,
260
+ model: finalModel,
261
+ timeoutMs,
262
+ attestPublicKey,
263
+ requireAttestedTelemetry,
264
+ source: finalSource,
265
+ };
232
266
  }
233
267
  /** True iff a command-template OR endpoint is configured (after resolution). */
234
268
  function agentConfigured(args = {}, env = process.env) {
235
269
  const resolved = resolveAgentConfig(args, env);
236
270
  return Boolean(resolved.command || resolved.endpoint);
237
271
  }
238
- /** Secret-stripped copy safe to persist or show — never carries raw credentials. */
272
+ /** Secret-stripped copy safe to persist or show — never carries raw
273
+ * credentials. */
239
274
  function redacted(config) {
240
275
  return {
241
276
  ...config,
242
- args: config.args ? (0, execution_backend_1.stripSecretArgs)(config.args) : undefined
277
+ args: config.args ? (0, agent_1.stripSecretArgs)(config.args) : undefined,
243
278
  };
244
279
  }
245
- /** Persist the durable config (secret-stripped). Returns the stored, redacted
246
- * config. API keys are NEVER written — they come from the agent's own env. */
280
+ /** Persist the durable config (secret-stripped). Returns the stored,
281
+ * redacted config. API keys are NEVER written — they come from the
282
+ * agent's own env. */
247
283
  function setAgentConfigFile(patch, env = process.env) {
248
284
  const current = loadAgentConfigFile(env) || { schemaVersion: 1 };
249
285
  const incoming = agentConfigFromArgs(patch);
@@ -256,16 +292,16 @@ function setAgentConfigFile(patch, env = process.env) {
256
292
  timeoutMs: firstDefined(incoming.timeoutMs, current.timeoutMs),
257
293
  attestPublicKey: firstDefined(incoming.attestPublicKey, current.attestPublicKey),
258
294
  requireAttestedTelemetry: firstDefined(incoming.requireAttestedTelemetry, current.requireAttestedTelemetry),
259
- source: "file"
295
+ source: "file",
260
296
  };
261
297
  const stored = redacted(merged);
262
298
  const file = agentConfigPath(env);
263
- node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true });
264
- (0, state_1.writeJson)(file, stored);
299
+ (0, fs_atomic_1.writeJson)(file, stored);
265
300
  return stored;
266
301
  }
267
- /** Read-only, deterministic projection of the effective config (secret-stripped).
268
- * No now-derived field — safe for CLI<->MCP payload parity. */
302
+ /** Read-only, deterministic projection of the effective config
303
+ * (secret-stripped). No now-derived field — safe for CLI<->MCP payload
304
+ * parity. */
269
305
  function agentConfigShow(args = {}, env = process.env) {
270
306
  const resolved = resolveAgentConfig(args, env);
271
307
  return {
@@ -274,6 +310,17 @@ function agentConfigShow(args = {}, env = process.env) {
274
310
  source: resolved.source,
275
311
  config: redacted(resolved),
276
312
  path: agentConfigPath(env),
277
- fileExists: node_fs_1.default.existsSync(agentConfigPath(env))
313
+ fileExists: fs.existsSync(agentConfigPath(env)),
278
314
  };
279
315
  }
316
+ /** Persist the durable agent config (secret-stripped) and return the new
317
+ * state. */
318
+ function backendAgentConfigSet(args, env = process.env) {
319
+ setAgentConfigFile(args, env);
320
+ return agentConfigShow(args, env);
321
+ }
322
+ /** Read-only, deterministic projection of the effective agent config
323
+ * (secret-stripped). */
324
+ function backendAgentConfigShow(args, env = process.env) {
325
+ return agentConfigShow(args, env);
326
+ }