@desplega.ai/agent-swarm 1.165.0 → 1.166.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 (188) hide show
  1. package/dist/{acp-adapter-w6zws6me.js → acp-adapter-yzfxn8vj.js} +4 -4
  2. package/dist/{actions-jna0b3wm.js → actions-qz3m692w.js} +20 -24
  3. package/dist/{amp-adapter-0eve81pz.js → amp-adapter-j1xbmsqz.js} +4 -4
  4. package/dist/{app-75hm5cfa.js → app-43vrdmv8.js} +6 -7
  5. package/dist/{assistant-yzd51j33.js → assistant-ertffmka.js} +21 -25
  6. package/dist/{boot-reembed-z158xq32.js → boot-reembed-998jtpdg.js} +9 -12
  7. package/dist/{boot-reembed-2ex2mcsh.js → boot-reembed-s7k7c6v2.js} +10 -12
  8. package/dist/boot-scrub-sweep-nepsk4t1.js +211 -0
  9. package/dist/{boot-seeds-2bmnh18p.js → boot-seeds-vvzs1zhk.js} +8 -8
  10. package/dist/{claude-adapter-jbj1mycz.js → claude-adapter-2zvmekrp.js} +9 -11
  11. package/dist/{claude-managed-adapter-17ggz09t.js → claude-managed-adapter-s49wbk8t.js} +3 -3
  12. package/dist/{claude-managed-setup-ptm1nzm2.js → claude-managed-setup-cvhqq8qn.js} +4 -4
  13. package/dist/{claude-sdk-session-9re1g7r2.js → claude-sdk-session-kkbxtw5q.js} +2618 -2600
  14. package/dist/{cli-k6xx3pve.js → cli-0nhdwxd5.js} +4 -4
  15. package/dist/{cli-n9ehc28t.js → cli-0qjcsxgz.js} +4 -4
  16. package/dist/{cli-zn2v3t8v.js → cli-3ddybbbx.js} +1 -1
  17. package/dist/{cli-5anb0ev3.js → cli-3qn4r334.js} +71 -14
  18. package/dist/{cli-1kzfvd1z.js → cli-4r1sjfc4.js} +9 -5
  19. package/dist/{cli-ba66wyhy.js → cli-66zh26sp.js} +6 -6
  20. package/dist/{cli-9xb89xca.js → cli-68r1j4sg.js} +6 -6
  21. package/dist/{cli-xnzrzets.js → cli-8kgvmkak.js} +2 -2
  22. package/dist/{cli-28smfvbn.js → cli-8t229cmr.js} +5 -5
  23. package/dist/{cli-9866bd07.js → cli-aqcey741.js} +1 -1
  24. package/dist/{cli-90v942q1.js → cli-cpfmqz3n.js} +8 -8
  25. package/dist/{cli-50eg13rj.js → cli-df56tqdw.js} +459 -282
  26. package/dist/{cli-8gpbwy09.js → cli-dw2q9f7g.js} +5 -5
  27. package/dist/{cli-0n4fasz0.js → cli-ecknp7p1.js} +15 -15
  28. package/dist/{cli-xb3c6aat.js → cli-f2n17zhp.js} +1 -1
  29. package/dist/{cli-ftry7975.js → cli-feh3dkah.js} +1 -1
  30. package/dist/{cli-amwhs8h2.js → cli-fn697bnt.js} +2 -2
  31. package/dist/{cli-prt9emct.js → cli-gt8azyet.js} +3 -3
  32. package/dist/{cli-59jn5twa.js → cli-h3x613ze.js} +2 -2
  33. package/dist/{cli-mmzwwptd.js → cli-hf5s9pgf.js} +1 -1
  34. package/dist/cli-j2jbr8af.js +516 -0
  35. package/dist/{cli-wfajtmf7.js → cli-jhm6hzjn.js} +593 -10
  36. package/dist/{cli-xtgtyafv.js → cli-jvj6a0cv.js} +190 -439
  37. package/dist/{cli-hpryr6em.js → cli-mb75wpxf.js} +1 -1
  38. package/dist/{cli-420bdgw0.js → cli-mv2520nm.js} +1 -1
  39. package/dist/{cli-pjxnr1jn.js → cli-mwegs4af.js} +3 -3
  40. package/dist/{cli-5b2fjjzh.js → cli-p4ztb80h.js} +2 -2
  41. package/dist/{cli-e7t4snpa.js → cli-rggdq8wq.js} +4 -4
  42. package/dist/{cli-bdv39kgj.js → cli-rsy933yz.js} +5 -5
  43. package/dist/{cli-gh5yqzpe.js → cli-v2a8x45d.js} +2 -2
  44. package/dist/{cli-bns5c8k9.js → cli-vzc73wet.js} +43 -47
  45. package/dist/cli-wt7ccy4r.js +2654 -0
  46. package/dist/{cli-j0pdrmwg.js → cli-x7kpr72a.js} +3 -3
  47. package/dist/{cli-x7afk5wm.js → cli-xvc3q9br.js} +1 -1
  48. package/dist/{cli-9s3h836g.js → cli-xvdgasgy.js} +1 -1
  49. package/dist/{cli-zvckv750.js → cli-y59h2pk9.js} +2 -2
  50. package/dist/{cli-qdv8kgz7.js → cli-yshaazs1.js} +13884 -3241
  51. package/dist/{cli-nfygw434.js → cli-ysnyhws9.js} +2 -2
  52. package/dist/{cli-fw6vdcq5.js → cli-zp67qg5x.js} +34 -1
  53. package/dist/{cli-ma3zvh39.js → cli-zy7vwsne.js} +6066 -5829
  54. package/dist/cli.js +24 -20
  55. package/dist/{codex-adapter-3qnxshje.js → codex-adapter-dw1wxqt8.js} +7 -7
  56. package/dist/{codex-login-brkxhhdg.js → codex-login-terbdsqq.js} +2 -2
  57. package/dist/{codex-session-runner-612wnapp.js → codex-session-runner-3cgb8y1g.js} +7 -7
  58. package/dist/{commands-2ybfcxkj.js → commands-mhxam6z2.js} +6 -6
  59. package/dist/{cursor-adapter-p3jgdehv.js → cursor-adapter-2qda3st7.js} +3 -3
  60. package/dist/{db-f4ans0jj.js → db-sjks418k.js} +8 -6
  61. package/dist/{devin-adapter-dhzt01ff.js → devin-adapter-bffjj5n4.js} +2 -2
  62. package/dist/{dispatcher-pk182apd.js → dispatcher-1es759rn.js} +18 -22
  63. package/dist/{dsh-adapter-7twaeps1.js → dsh-adapter-bek4cyw9.js} +3 -3
  64. package/dist/{e2b-y8t3jzyz.js → e2b-nd50eccg.js} +1 -1
  65. package/dist/{event-bus-w7ac4n3c.js → event-bus-vnfthafg.js} +2 -2
  66. package/dist/{grok-adapter-yt9pab3k.js → grok-adapter-ar9cw9jg.js} +5 -5
  67. package/dist/{handlers-g2wnr4t9.js → handlers-1gkbhktr.js} +21 -25
  68. package/dist/{hook-f7jt9vnm.js → hook-ds2zxq9v.js} +14 -16
  69. package/dist/{hook-bbdpnkb4.js → hook-vxa3tcsy.js} +8 -10
  70. package/dist/{http-w40q4w94.js → http-3h21z9md.js} +247 -164
  71. package/dist/{index-056j5kzn.js → index-9b65j1m5.js} +8 -8
  72. package/dist/{index-9bfssf54.js → index-bn6cy8ps.js} +18 -22
  73. package/dist/{index-cj1r9q83.js → index-dkfxpkmm.js} +19 -23
  74. package/dist/{index-7n70jwbb.js → index-f8xnqrye.js} +19 -21
  75. package/dist/{index-fdk1t4d6.js → index-n0rxa4ca.js} +11962 -9900
  76. package/dist/{index-k8xc083e.js → index-n99fycq7.js} +18 -22
  77. package/dist/{keepalive-1tj10tf4.js → keepalive-3xspa9jw.js} +11 -8
  78. package/dist/{lead-st0bg7v4.js → lead-6qphc124.js} +34 -38
  79. package/dist/{maintenance-nz71zats.js → maintenance-0teem1bb.js} +7 -9
  80. package/dist/{node-f0ykebbv.js → node-9sg7ajby.js} +25 -7
  81. package/dist/{oauth-refresh-sweep-aefzwvvq.js → oauth-refresh-sweep-sh9q25xp.js} +10 -7
  82. package/dist/{onboard-kgpr9mbn.js → onboard-54qmnqxe.js} +2 -2
  83. package/dist/{opencode-adapter-0c8e70dk.js → opencode-adapter-wjfa2wvd.js} +2 -2
  84. package/dist/{otel-impl-x8zp4y5a.js → otel-impl-ycqzp2a1.js} +2 -2
  85. package/dist/{pi-mono-adapter-t280ah5c.js → pi-mono-adapter-e77e9zst.js} +4 -4
  86. package/dist/{pricing-refresh-9e0q7cwk.js → pricing-refresh-atnkngqs.js} +7 -7
  87. package/dist/{rbac-roles-kcp6zge2.js → rbac-roles-9pjphh9q.js} +6 -6
  88. package/dist/{rbac-roles-790j3ga8.js → rbac-roles-j78wdksm.js} +6 -6
  89. package/dist/{render-v2-tch4nefj.js → render-v2-pxjs49fx.js} +6 -7
  90. package/dist/{setup-rcm303ns.js → setup-ymej0xtx.js} +2 -2
  91. package/dist/{work-objects-ctg7t70w.js → work-objects-3nh4ymzb.js} +22 -26
  92. package/dist/{worker-5dp0xm55.js → worker-qa9fkz5s.js} +34 -38
  93. package/dist/{x-v3yw66zs.js → x-ma4gnfgy.js} +2 -2
  94. package/openapi.json +5 -2
  95. package/package.json +9 -5
  96. package/src/be/automation-preflight.ts +9 -4
  97. package/src/be/boot-scrub-sweep.ts +302 -0
  98. package/src/be/crypto/key-bootstrap.ts +1 -1
  99. package/src/be/db/approvals.ts +15 -1
  100. package/src/be/db/codex-oauth-device-flows.ts +40 -0
  101. package/src/be/db/config-secrets.ts +31 -0
  102. package/src/be/db/runtime.ts +4 -0
  103. package/src/be/db/script-runs.ts +15 -0
  104. package/src/be/db/tasks/write.ts +1 -1
  105. package/src/be/db-queries/mcp-oauth.ts +5 -0
  106. package/src/be/db-queries/oauth.ts +77 -0
  107. package/src/be/db.ts +162 -59
  108. package/src/be/events.ts +17 -1
  109. package/src/be/memory/index-content.ts +2 -2
  110. package/src/be/memory/providers/sqlite-store.ts +63 -11
  111. package/src/be/memory/types.ts +14 -0
  112. package/src/be/migrations/201_workflow_replay_columns.sql +10 -0
  113. package/src/be/migrations/202_codex_oauth_device_flows.sql +24 -0
  114. package/src/be/scripts/db.ts +28 -4
  115. package/src/be/scripts/source-secrets.ts +37 -0
  116. package/src/be/scrub-json.ts +32 -0
  117. package/src/be/sealed-json.ts +59 -0
  118. package/src/be/secret-registry.ts +131 -0
  119. package/src/be/workflow-replay.ts +77 -0
  120. package/src/http/codex-oauth-device.ts +8 -10
  121. package/src/http/db-query-bounded.ts +3 -0
  122. package/src/http/db-query-shared.ts +107 -15
  123. package/src/http/db-query.ts +10 -2
  124. package/src/http/index.ts +23 -6
  125. package/src/http/script-runs.ts +9 -1
  126. package/src/http/scripts.ts +15 -0
  127. package/src/http/workflows.ts +3 -2
  128. package/src/oauth/mcp-wrapper.ts +1 -1
  129. package/src/providers/acp-adapter.ts +25 -11
  130. package/src/providers/acp-target-catalog.ts +35 -1
  131. package/src/providers/acp-targets.ts +69 -3
  132. package/src/providers/amp-adapter.ts +1 -1
  133. package/src/providers/cursor-adapter.ts +1 -1
  134. package/src/script-workflows/supervisor.ts +11 -2
  135. package/src/scripts-runtime/eval-harness.ts +2 -12
  136. package/src/scripts-runtime/executors/quickjs-bundle.ts +152 -0
  137. package/src/scripts-runtime/executors/quickjs-prelude.ts +285 -0
  138. package/src/scripts-runtime/executors/quickjs-runner.ts +580 -0
  139. package/src/scripts-runtime/executors/quickjs-worker.ts +81 -0
  140. package/src/scripts-runtime/executors/quickjs.ts +196 -0
  141. package/src/scripts-runtime/executors/registry.ts +2 -0
  142. package/src/scripts-runtime/executors/sourcemap.ts +79 -0
  143. package/src/scripts-runtime/script-error-hints.ts +14 -0
  144. package/src/server.ts +3 -1
  145. package/src/tests/acp-adapter.test.ts +127 -2
  146. package/src/tests/boot-scrub-sweep.test.ts +390 -0
  147. package/src/tests/check-text-columns.test.ts +145 -0
  148. package/src/tests/claude-sdk-process.fixture.ts +13 -2
  149. package/src/tests/codex-oauth-device.test.ts +23 -37
  150. package/src/tests/db-query-denylist.test.ts +202 -0
  151. package/src/tests/db-task-read-contract.test.ts +1 -0
  152. package/src/tests/db-task-write-contract.test.ts +1 -0
  153. package/src/tests/entrypoint-acp-target-binary.test.ts +31 -1
  154. package/src/tests/events-scrub.test.ts +88 -0
  155. package/src/tests/fixtures/secret-corpus.ts +320 -0
  156. package/src/tests/script-connections.test.ts +5 -0
  157. package/src/tests/script-executor-conformance.test.ts +3 -0
  158. package/src/tests/script-executor-quickjs.test.ts +442 -0
  159. package/src/tests/script-executor-registry.test.ts +6 -1
  160. package/src/tests/script-runs-http.test.ts +88 -1
  161. package/src/tests/script-runs-scrub.test.ts +275 -0
  162. package/src/tests/scripts-http.test.ts +82 -1
  163. package/src/tests/scrubbed-text-12e.test.ts +289 -0
  164. package/src/tests/sealed-key-guard.test.ts +166 -0
  165. package/src/tests/secret-canary-sweep.test.ts +958 -0
  166. package/src/tests/secret-registry.test.ts +355 -0
  167. package/src/tests/secret-rules.test.ts +230 -0
  168. package/src/tests/secret-scrubber.test.ts +3 -2
  169. package/src/tests/workflow-replay.test.ts +518 -0
  170. package/src/tests/workflow-scrub.test.ts +242 -0
  171. package/src/tools/db-query.ts +1 -1
  172. package/src/tools/inject-learning.ts +1 -1
  173. package/src/tools/workflows/get-workflow-run.ts +3 -2
  174. package/src/types.ts +1 -1
  175. package/src/utils/secret-rules.generated.ts +2055 -0
  176. package/src/utils/secret-scrubber.ts +363 -29
  177. package/src/workflows/engine.ts +39 -4
  178. package/src/workflows/recovery.ts +10 -1
  179. package/src/workflows/resume.ts +62 -3
  180. package/src/workflows/retry-poller.ts +3 -0
  181. package/dist/boot-scrub-logs-246yn076.js +0 -99
  182. package/dist/cli-3a4m1aws.js +0 -572
  183. package/dist/cli-a87b04k8.js +0 -951
  184. package/dist/cli-cvk6yzw5.js +0 -40
  185. package/dist/cli-h1spv8v4.js +0 -427
  186. package/dist/cli-jqt9hrnz.js +0 -168
  187. package/dist/cli-yhmwmv0g.js +0 -9281
  188. package/src/be/boot-scrub-logs.ts +0 -132
@@ -0,0 +1,242 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
+ import { unlinkSync } from "node:fs";
3
+ import { recordWorkflowPreflightFailure } from "../be/automation-preflight";
4
+ import {
5
+ closeDb,
6
+ createWorkflow,
7
+ createWorkflowRun,
8
+ createWorkflowRunStep,
9
+ getDbClient,
10
+ initDb,
11
+ updateWorkflowRun,
12
+ updateWorkflowRunStep,
13
+ } from "../be/db";
14
+ import { scrubJsonValue } from "../be/scrub-json";
15
+ import * as scrubber from "../utils/secret-scrubber";
16
+ import { checkpointStep, checkpointStepFailure } from "../workflows/checkpoint";
17
+ import { type SyntheticSecret, syntheticSecret } from "./synthetic-secret-helpers";
18
+
19
+ const TEST_DB_PATH = "./test-workflow-scrub.sqlite";
20
+
21
+ type RunRow = { triggerData: string | null; context: string | null; error: string | null };
22
+ type StepRow = {
23
+ input: string | null;
24
+ output: string | null;
25
+ error: string | null;
26
+ diagnostics: string | null;
27
+ idempotencyKey: string | null;
28
+ nextPort: string | null;
29
+ };
30
+
31
+ let secret: SyntheticSecret;
32
+ let workflowId: string;
33
+
34
+ async function readRun(id: string): Promise<RunRow> {
35
+ const row = await getDbClient().get<RunRow>(
36
+ "SELECT triggerData, context, error FROM workflow_runs WHERE id = ?",
37
+ [id],
38
+ );
39
+ if (!row) throw new Error(`run ${id} not found`);
40
+ return row;
41
+ }
42
+
43
+ async function readStep(id: string): Promise<StepRow> {
44
+ const row = await getDbClient().get<StepRow>(
45
+ "SELECT input, output, error, diagnostics, idempotencyKey, nextPort FROM workflow_run_steps WHERE id = ?",
46
+ [id],
47
+ );
48
+ if (!row) throw new Error(`step ${id} not found`);
49
+ return row;
50
+ }
51
+
52
+ /** Secret gone, a redaction marker present, and the non-secret context kept. */
53
+ function expectRedacted(stored: string | null, context: string): void {
54
+ expect(stored).not.toBeNull();
55
+ expect(stored).not.toContain(secret.value);
56
+ expect(stored).toContain(`[REDACTED:${secret.name}]`);
57
+ expect(stored).toContain(context);
58
+ }
59
+
60
+ async function newRun(): Promise<string> {
61
+ const id = crypto.randomUUID();
62
+ await createWorkflowRun({ id, workflowId });
63
+ return id;
64
+ }
65
+
66
+ async function newStep(runId: string, nodeId = "node-a"): Promise<string> {
67
+ const id = crypto.randomUUID();
68
+ await createWorkflowRunStep({ id, runId, nodeId, nodeType: "script" });
69
+ return id;
70
+ }
71
+
72
+ beforeAll(async () => {
73
+ initDb(TEST_DB_PATH);
74
+ const workflow = await createWorkflow({
75
+ name: `workflow-scrub-${crypto.randomUUID()}`,
76
+ definition: { nodes: [] },
77
+ });
78
+ workflowId = workflow.id;
79
+ });
80
+
81
+ // The test preload clears volatile secrets after every test, so register per test.
82
+ beforeEach(() => {
83
+ secret = syntheticSecret("workflow");
84
+ });
85
+
86
+ afterAll(() => {
87
+ secret.cleanup();
88
+ closeDb();
89
+ for (const suffix of ["", "-wal", "-shm"]) {
90
+ try {
91
+ unlinkSync(`${TEST_DB_PATH}${suffix}`);
92
+ } catch {
93
+ // ignore
94
+ }
95
+ }
96
+ });
97
+
98
+ describe("workflow_runs writers scrub at write", () => {
99
+ test("createWorkflowRun redacts triggerData and keeps valid JSON", async () => {
100
+ const id = crypto.randomUUID();
101
+ await createWorkflowRun({
102
+ id,
103
+ workflowId,
104
+ triggerType: "api",
105
+ triggerData: { source: "webhook-trigger", token: secret.value },
106
+ });
107
+ const row = await readRun(id);
108
+ expectRedacted(row.triggerData, "webhook-trigger");
109
+ expect(JSON.parse(row.triggerData!).source).toBe("webhook-trigger");
110
+ });
111
+
112
+ test("updateWorkflowRun redacts error and context; the exact context is sealed", async () => {
113
+ const id = await newRun();
114
+ await updateWorkflowRun(id, {
115
+ status: "failed",
116
+ error: `run exploded with ${secret.value} in the message`,
117
+ context: { input: { apiKey: secret.value, region: "eu-west-1" } },
118
+ });
119
+ const row = await readRun(id);
120
+ expectRedacted(row.error, "run exploded with");
121
+ expectRedacted(row.context, "eu-west-1");
122
+ // Resume rebuilds the live ctx from the sealed copy (workflow-replay.test.ts).
123
+ const sealed = await getDbClient().get<{ context_replay: string }>(
124
+ "SELECT context_replay FROM workflow_runs WHERE id = ?",
125
+ [id],
126
+ );
127
+ expect(sealed?.context_replay?.startsWith("sealed:v1:")).toBe(true);
128
+ expect(sealed?.context_replay).not.toContain(secret.value);
129
+ });
130
+
131
+ test("recordWorkflowPreflightFailure redacts triggerData and error, keeps the dedupe prefix", async () => {
132
+ const preflightWorkflow = await createWorkflow({
133
+ name: `workflow-scrub-preflight-${crypto.randomUUID()}`,
134
+ definition: { nodes: [] },
135
+ });
136
+ const first = await recordWorkflowPreflightFailure({
137
+ workflowId: preflightWorkflow.id,
138
+ triggerType: "schedule",
139
+ triggerData: { scheduleName: "nightly-sync", header: secret.value },
140
+ failureReason: `needs_setup: slack token ${secret.value} rejected`,
141
+ });
142
+ expect(first.recorded).toBe(true);
143
+ const row = await readRun(first.runId);
144
+ expectRedacted(row.triggerData, "nightly-sync");
145
+ expectRedacted(row.error, "needs_setup: slack token");
146
+
147
+ // The `needs_setup:%` dedupe still matches the scrubbed row.
148
+ const second = await recordWorkflowPreflightFailure({
149
+ workflowId: preflightWorkflow.id,
150
+ triggerType: "schedule",
151
+ triggerData: {},
152
+ failureReason: "needs_setup: again",
153
+ });
154
+ expect(second).toEqual({ runId: first.runId, recorded: false });
155
+ });
156
+ });
157
+
158
+ describe("workflow_run_steps writers scrub at write", () => {
159
+ test("createWorkflowRunStep redacts input and keeps the idempotency key byte-exact", async () => {
160
+ const runId = await newRun();
161
+ const id = crypto.randomUUID();
162
+ const idempotencyKey = `${runId}:node-a:0`;
163
+ await createWorkflowRunStep({
164
+ id,
165
+ runId,
166
+ nodeId: "node-a",
167
+ nodeType: "script",
168
+ input: { prompt: "summarize-the-repo", auth: secret.value, index: 3 },
169
+ idempotencyKey,
170
+ });
171
+ const row = await readStep(id);
172
+ expectRedacted(row.input, "summarize-the-repo");
173
+ expect(JSON.parse(row.input!).index).toBe(3);
174
+ expect(row.idempotencyKey).toBe(idempotencyKey);
175
+ });
176
+
177
+ test("updateWorkflowRunStep redacts output, error and diagnostics", async () => {
178
+ const runId = await newRun();
179
+ const id = await newStep(runId);
180
+ await updateWorkflowRunStep(id, {
181
+ status: "failed",
182
+ output: { stdout: "deploy-finished", leaked: secret.value },
183
+ error: `step failed: ${secret.value}`,
184
+ diagnostics: `trace line ${secret.value} end`,
185
+ nextPort: "default",
186
+ });
187
+ const row = await readStep(id);
188
+ expectRedacted(row.output, "deploy-finished");
189
+ expect(JSON.parse(row.output!).stdout).toBe("deploy-finished");
190
+ expectRedacted(row.error, "step failed:");
191
+ expectRedacted(row.diagnostics, "trace line");
192
+ expect(row.nextPort).toBe("default");
193
+ });
194
+
195
+ test("checkpointStep redacts the persisted step output", async () => {
196
+ const runId = await newRun();
197
+ const id = await newStep(runId, "node-out");
198
+ await checkpointStep(
199
+ runId,
200
+ id,
201
+ "node-out",
202
+ { output: { result: "build-green", token: secret.value }, nextPort: "pass" },
203
+ {},
204
+ );
205
+ const row = await readStep(id);
206
+ expectRedacted(row.output, "build-green");
207
+ expect(row.nextPort).toBe("pass");
208
+ });
209
+
210
+ test("checkpointStepFailure redacts the step error and the run error", async () => {
211
+ const runId = await newRun();
212
+ await updateWorkflowRun(runId, { status: "running" });
213
+ const id = await newStep(runId, "node-fail");
214
+ const result = await checkpointStepFailure(runId, id, `upstream 401 for ${secret.value}`, 0);
215
+ expect(result.shouldRetry).toBe(false);
216
+ expectRedacted((await readStep(id)).error, "upstream 401 for");
217
+ expectRedacted((await readRun(runId)).error, "Step failed: upstream 401 for");
218
+ });
219
+ });
220
+
221
+ describe("scrubJsonValue JSON-validity guard", () => {
222
+ test("falls back to per-leaf scrubbing when a whole-string redaction breaks JSON", () => {
223
+ // The real scrubber is JSON-escape aware, so force the failure shape the
224
+ // guard exists for: a whole-string pass that leaves an unbalanced quote.
225
+ const realScrub = scrubber.scrubSecrets;
226
+ const spy = spyOn(scrubber, "scrubSecrets").mockImplementation((text) => {
227
+ const out = realScrub(text);
228
+ return (text?.startsWith("{") ? out.replace("guard-context", '"') : out) as typeof out;
229
+ });
230
+ try {
231
+ const out = scrubJsonValue({ note: "guard-context", key: secret.value });
232
+ const parsed = JSON.parse(out) as { note: string; key: string };
233
+ expect(parsed.note).toBe("guard-context");
234
+ expect(parsed.key).toBe(`[REDACTED:${secret.name}]`);
235
+ expect(out).not.toContain(secret.value);
236
+ // One whole-string call, then one per leaf string.
237
+ expect(spy.mock.calls.length).toBeGreaterThan(1);
238
+ } finally {
239
+ spy.mockRestore();
240
+ }
241
+ });
242
+ });
@@ -26,7 +26,7 @@ export const registerDbQueryTool = (server: McpServer) => {
26
26
  {
27
27
  title: "Execute database query",
28
28
  description:
29
- "Execute a read-only SQL query against the swarm database (SQLite). Available to all authenticated agents — be aware results may include secrets (oauth_tokens, configs). Runs in a short-lived child process with a wall-clock budget by default (fails gracefully with a timeout or a 429-style concurrency error rather than freezing); results capped at a default row count (operator-configurable via `DB_QUERY_MCP_MAX_ROWS`) regardless of how many the query matched. See the sql parameter's description for which tables are unsafe to read whole, and the db-query-guidance skill for config knobs.",
29
+ "Execute a read-only SQL query against the swarm database (SQLite). Available to all authenticated agents. Queries that read credential tables (OAuth authorizations, apps and pending flows, user and session tokens, script API bearers, Codex device-login state) are rejected, and secret-shaped values in results are redacted. Runs in a short-lived child process with a wall-clock budget by default (fails gracefully with a timeout or a 429-style concurrency error rather than freezing); results capped at a default row count (operator-configurable via `DB_QUERY_MCP_MAX_ROWS`) regardless of how many the query matched. See the sql parameter's description for which tables are unsafe to read whole, and the db-query-guidance skill for config knobs.",
30
30
  annotations: { readOnlyHint: true },
31
31
  inputSchema: DbQueryToolInputSchema,
32
32
  outputSchema: swarmToolOutputSchema({
@@ -73,7 +73,7 @@ export const registerInjectLearningTool = (server: McpServer) => {
73
73
  // Generate and store embedding (async, best-effort)
74
74
  try {
75
75
  const provider = getEmbeddingProvider();
76
- const embedding = await provider.embed(content);
76
+ const embedding = await provider.embed(memory.content);
77
77
  if (embedding) {
78
78
  await store.updateEmbedding(memory.id, embedding, provider.name);
79
79
  }
@@ -54,11 +54,12 @@ export const registerGetWorkflowRunTool = (server: McpServer) => {
54
54
  },
55
55
  async ({ id }) => {
56
56
  try {
57
- const run = await getWorkflowRun(id);
57
+ // Display view: the scrubbed context and step outputs, never replay state.
58
+ const run = await getWorkflowRun(id, "display");
58
59
  if (!run) {
59
60
  return toolErr(`Workflow run not found: ${id}`, { data: { steps: [] } });
60
61
  }
61
- const steps = await getWorkflowRunStepsByRunId(id);
62
+ const steps = await getWorkflowRunStepsByRunId(id, "display");
62
63
  return toolOk(`Run ${id} status: ${run.status}.`, {
63
64
  details: renderSteps(steps),
64
65
  data: { run, steps },
package/src/types.ts CHANGED
@@ -1321,7 +1321,7 @@ export type AcpSessionConfigOption = z.infer<typeof AcpSessionConfigOptionSchema
1321
1321
 
1322
1322
  export const AgentAcpStatusSchema = z
1323
1323
  .object({
1324
- target: z.enum(["opencode", "custom"]),
1324
+ target: z.enum(["opencode", "gemini", "copilot", "custom"]),
1325
1325
  configOptions: z.array(AcpSessionConfigOptionSchema),
1326
1326
  reportedAt: z.number(),
1327
1327
  })