@desplega.ai/agent-swarm 2.7.5 → 2.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/dist/{acp-adapter-g3ads55s.js → acp-adapter-bp91d37f.js} +4 -4
  2. package/dist/{actions-zwfykqqk.js → actions-yj0eeaqh.js} +18 -17
  3. package/dist/{amp-adapter-z3s0ssdt.js → amp-adapter-4pg9h34q.js} +1 -1
  4. package/dist/{app-vmp4fgep.js → app-909e97cr.js} +3 -3
  5. package/dist/{assistant-vwde653f.js → assistant-qdyb5fyy.js} +24 -22
  6. package/dist/{boot-reembed-375t7tbk.js → boot-reembed-fk0s9z18.js} +1 -1
  7. package/dist/{boot-reembed-gq9rcpnj.js → boot-reembed-gs1rzrnh.js} +1 -1
  8. package/dist/{boot-scrub-sweep-nd938kyf.js → boot-scrub-sweep-4qfd4kfx.js} +2 -2
  9. package/dist/{boot-seeds-aykx51v3.js → boot-seeds-yr00k13d.js} +4 -4
  10. package/dist/{boxd-k1j26g3h.js → boxd-vdmfssd9.js} +4 -4
  11. package/dist/{claude-adapter-8j4hm099.js → claude-adapter-qta1f1xn.js} +3 -3
  12. package/dist/{claude-managed-setup-0wyynpn1.js → claude-managed-setup-sc6j4dm0.js} +1 -1
  13. package/dist/{claude-sdk-session-q6wj5hmy.js → claude-sdk-session-zgrz6yvc.js} +9169 -9070
  14. package/dist/{cli-gjr2y2xk.js → cli-0agcv1v5.js} +4 -1
  15. package/dist/{cli-vqypxzh4.js → cli-0at8w34w.js} +7 -7
  16. package/dist/{cli-7a42nfy0.js → cli-0qatpy8q.js} +3 -3
  17. package/dist/{cli-p599f1qm.js → cli-0yv5z2pg.js} +1 -1
  18. package/dist/{cli-0gey5fzy.js → cli-386tyxxg.js} +2 -2
  19. package/dist/{cli-5pag6rmn.js → cli-3ypk9vk1.js} +1 -1
  20. package/dist/{cli-qxnf8z2z.js → cli-436532ck.js} +2 -2
  21. package/dist/{cli-z8eh3ex8.js → cli-4necwtsv.js} +4 -4
  22. package/dist/{cli-zh3fp8v2.js → cli-54t00gt8.js} +1 -1
  23. package/dist/{cli-hf6zt5m6.js → cli-69szn7w7.js} +17 -1
  24. package/dist/{cli-nn4d8dne.js → cli-89knwags.js} +2 -2
  25. package/dist/{cli-spknvxbq.js → cli-8bc1w0tw.js} +10 -10
  26. package/dist/{cli-rqkxrmgw.js → cli-8snvgs5r.js} +38 -13
  27. package/dist/{cli-rhw40jgb.js → cli-bjb3z51z.js} +2 -2
  28. package/dist/{cli-xtjhfmpc.js → cli-c60vetmm.js} +1 -1
  29. package/dist/{cli-0731hvbw.js → cli-decfb6v8.js} +2 -2
  30. package/dist/{cli-bhjt02p8.js → cli-drr5v6av.js} +1 -1
  31. package/dist/{cli-fqzx7gwa.js → cli-dxae2yg7.js} +2 -2
  32. package/dist/{cli-tqgg40ek.js → cli-e3r2neck.js} +8 -7
  33. package/dist/{cli-zppwg9p9.js → cli-ekwks2vs.js} +1 -1
  34. package/dist/{cli-nzxr556s.js → cli-es6va8jk.js} +26 -26
  35. package/dist/{cli-a47xy8eq.js → cli-faxe6thg.js} +1 -1
  36. package/dist/cli-gdhpcbhf.js +55 -0
  37. package/dist/{cli-82jg7fp9.js → cli-he5nxaa8.js} +1 -1
  38. package/dist/{cli-8veytvbh.js → cli-he9wchme.js} +1 -1
  39. package/dist/{cli-pywf3eh5.js → cli-hgaazk69.js} +1 -1
  40. package/dist/{cli-0bxqnvq9.js → cli-hr2e2fft.js} +28 -6
  41. package/dist/{cli-1ayk1eas.js → cli-k2gzwsj2.js} +135 -26
  42. package/dist/{cli-xbkdm710.js → cli-kqdg1bsb.js} +3 -3
  43. package/dist/cli-m2z8jagn.js +12078 -0
  44. package/dist/{cli-g61qxmge.js → cli-m48g6p95.js} +3 -3
  45. package/dist/{cli-7wspm5gm.js → cli-nhrvpaj7.js} +20 -4
  46. package/dist/{cli-afrrppsw.js → cli-nwj0ee2w.js} +2 -2
  47. package/dist/{cli-3h4nhjn8.js → cli-p5gp8pcx.js} +9 -7
  48. package/dist/{cli-rm6hqj3z.js → cli-pd7vagab.js} +33 -139
  49. package/dist/{cli-mjzynrse.js → cli-q0k1w5zd.js} +1 -1
  50. package/dist/{cli-8hs41zy2.js → cli-rmmmrjkd.js} +1 -1
  51. package/dist/{cli-kg8hchyg.js → cli-sa02jsqt.js} +1 -1
  52. package/dist/{cli-1338796d.js → cli-shpbx5ee.js} +142 -98
  53. package/dist/{cli-82ae98pz.js → cli-svj5090z.js} +2 -2
  54. package/dist/{cli-bnz7a7qf.js → cli-vvpx3gsx.js} +1 -1
  55. package/dist/{cli-mmwm7zbf.js → cli-w6dt9ged.js} +1 -1
  56. package/dist/{cli-wvaawacd.js → cli-waqbxg08.js} +5230 -66
  57. package/dist/{cli-j3jvnpe8.js → cli-wp9sp1qb.js} +1 -1
  58. package/dist/{cli-nmbmy64z.js → cli-xbbndvjy.js} +3 -3
  59. package/dist/{cli-vbyfwh92.js → cli-xds5f77d.js} +2 -2
  60. package/dist/{cli-7wcyk2s5.js → cli-xnqgpshy.js} +1 -1
  61. package/dist/{cli-rpm2sgv0.js → cli-xnrwazav.js} +19 -2
  62. package/dist/{cli-gf5hbmdr.js → cli-yjdgea32.js} +3 -3
  63. package/dist/{cli-mry0qzym.js → cli-yn4kxs06.js} +123 -10
  64. package/dist/{cli-hn9hxmz9.js → cli-yx4kfngt.js} +1 -1
  65. package/dist/{cli-y24sc29k.js → cli-yy3rhsdw.js} +30 -8
  66. package/dist/{cli-6s7vdcta.js → cli-zbbwez0r.js} +4 -4
  67. package/dist/{cli-z6jkx23q.js → cli-zm72anhg.js} +9 -9
  68. package/dist/cli.js +22 -21
  69. package/dist/{codex-adapter-q6k3gnyg.js → codex-adapter-ayd1fb6h.js} +2 -2
  70. package/dist/{codex-session-runner-3mwmk3d9.js → codex-session-runner-jesa32xc.js} +2 -2
  71. package/dist/{commands-evrenjn9.js → commands-hanewgf2.js} +9 -3
  72. package/dist/{controller-xjp8vswh.js → controller-dt1n3c1q.js} +8 -3
  73. package/dist/{controllers-connect-pp5jnxvc.js → controllers-connect-xx8x64n5.js} +20 -19
  74. package/dist/{cursor-adapter-yf47w4t0.js → cursor-adapter-9s4dwwdw.js} +1 -1
  75. package/dist/{db-zxpw6aht.js → db-3vttcbv4.js} +3 -3
  76. package/dist/{db-rgfsh91f.js → db-9dv0g1ab.js} +11 -3
  77. package/dist/{devtools-82fzqra8.js → devtools-qxfgbsqz.js} +1 -1
  78. package/dist/{dispatcher-smasgws7.js → dispatcher-tx9hd1sv.js} +17 -16
  79. package/dist/{docker-rrjhaqt9.js → docker-c1a3gqmf.js} +89 -22
  80. package/dist/{dsh-adapter-ftyy8dnp.js → dsh-adapter-007y4ejs.js} +1 -1
  81. package/dist/{e2b-qcaj675y.js → e2b-a19d28rz.js} +2 -2
  82. package/dist/{e2b-w1p0bnpm.js → e2b-we6hs6q5.js} +4 -4
  83. package/dist/{easter-egg-3d-yyts7p16.js → easter-egg-3d-rgbrqb8s.js} +1 -1
  84. package/dist/{embedding-space-1z0mbg95.js → embedding-space-ny1e2940.js} +6 -6
  85. package/dist/{embeddings-ygeg7tm7.js → embeddings-2t386t13.js} +1 -1
  86. package/dist/{execute-14qhqp4d.js → execute-pksh0ceh.js} +1 -1
  87. package/dist/{external-channel-ttgrs3sx.js → external-channel-phgmhmd7.js} +2 -2
  88. package/dist/{fx-adapter-gt6pncj2.js → fx-adapter-fy8rr921.js} +5 -5
  89. package/dist/{google-generative-ai-yvmdxyxa.js → google-generative-ai-8y3m3k4b.js} +1 -1
  90. package/dist/{google-vertex-jrtdh9gs.js → google-vertex-w3v98n7b.js} +1 -1
  91. package/dist/{grok-adapter-sm4m5k4g.js → grok-adapter-q0abvpaj.js} +5 -5
  92. package/dist/handlers-by3sfxyx.js +32 -0
  93. package/dist/{heartbeat-qahz4p62.js → heartbeat-kadyek8p.js} +20 -19
  94. package/dist/{hook-kj3d398m.js → hook-5zs3msf6.js} +2 -2
  95. package/dist/{hook-4fd5wq5w.js → hook-9k551xnj.js} +2 -2
  96. package/dist/{http-1ted0j1k.js → http-mhz9vw1f.js} +213 -80
  97. package/dist/index-4f2pvxae.js +37 -0
  98. package/dist/index-98gz7tmp.js +30 -0
  99. package/dist/{index-5rk6rfzr.js → index-fvzke5kx.js} +1 -1
  100. package/dist/index-k08z2cak.js +33 -0
  101. package/dist/{index-yqhz88yy.js → index-pxn30mse.js} +8 -8
  102. package/dist/{index-6rq3r5zb.js → index-q361cg2f.js} +1 -1
  103. package/dist/{index-7v51awbn.js → index-rvr00sg7.js} +20 -13
  104. package/dist/{index-q4b917zs.js → index-vdygbq10.js} +1 -1
  105. package/dist/{keepalive-6cf311t5.js → keepalive-w56f32cg.js} +3 -3
  106. package/dist/{lead-fjy418cw.js → lead-enq35nqp.js} +31 -30
  107. package/dist/{maintenance-a4bm1bmt.js → maintenance-7mdgxcea.js} +1 -1
  108. package/dist/{oauth-refresh-sweep-3h0dfw1k.js → oauth-refresh-sweep-29txy9vk.js} +2 -2
  109. package/dist/{onboard-myn5hraa.js → onboard-kpsrkarj.js} +4 -4
  110. package/dist/{opencode-adapter-p2nkb5am.js → opencode-adapter-j25vm3rr.js} +3 -3
  111. package/dist/{otel-impl-a1a0ef6b.js → otel-impl-6kkct7a8.js} +1 -1
  112. package/dist/{pi-mono-adapter-rzmpb72a.js → pi-mono-adapter-exhkzhn2.js} +4 -4
  113. package/dist/{pricing-refresh-83drrnxj.js → pricing-refresh-n2x7jh6k.js} +3 -3
  114. package/dist/{rbac-roles-s8y6ecb5.js → rbac-roles-81ypx6es.js} +1 -1
  115. package/dist/{rbac-roles-0fb6e7pj.js → rbac-roles-b5htkjby.js} +2 -2
  116. package/dist/{render-v2-twmbgrz4.js → render-v2-qn6jmpk4.js} +3 -3
  117. package/dist/{resume-zfqc36xj.js → resume-wd30wx23.js} +6 -6
  118. package/dist/{runtime-qwggdpqr.js → runtime-4hsg7h60.js} +1 -1
  119. package/dist/{session-access-sc9pn9da.js → session-access-5qvfevhk.js} +4 -4
  120. package/dist/{session-source-backfill-qh07xr8g.js → session-source-backfill-n1czavbr.js} +2 -2
  121. package/dist/{setup-4fr0m7a1.js → setup-dnb56qy5.js} +3 -3
  122. package/dist/{virtual-modules-fh4ha15b.js → virtual-modules-76xxv9fs.js} +2 -2
  123. package/dist/{work-objects-0wr8zxct.js → work-objects-4n21tfkm.js} +20 -19
  124. package/dist/{worker-follow-up-ktyyazyp.js → worker-follow-up-77hvzxww.js} +17 -16
  125. package/dist/{worker-0c5tyzqe.js → worker-gg1576j3.js} +31 -30
  126. package/openapi.json +93 -1
  127. package/package.json +8 -7
  128. package/src/be/db/script-runs.ts +23 -0
  129. package/src/be/db/tasks/read.ts +122 -3
  130. package/src/be/db-client.ts +8 -5
  131. package/src/be/db.ts +15 -4
  132. package/src/be/memory/providers/sqlite-store.ts +49 -12
  133. package/src/be/migrations/260_runtime_pool_cache.sql +20 -0
  134. package/src/be/migrations/262_session_visibility_indexes.sql +18 -0
  135. package/src/be/migrations/263_task_list_filter_index.sql +11 -0
  136. package/src/be/migrations/264_slack_thread_cover_index.sql +9 -0
  137. package/src/be/migrations/266_script_runs_source_task.sql +11 -0
  138. package/src/be/runtime-pools/cache-cleanup.ts +109 -0
  139. package/src/be/runtime-pools/db.ts +65 -6
  140. package/src/be/runtime-pools/remote-controller.ts +5 -0
  141. package/src/be/runtime-pools/supervisor.ts +33 -2
  142. package/src/be/scripts/typecheck.ts +22 -0
  143. package/src/be/seed-scripts/catalog/task-failure-audit.ts +5 -1
  144. package/src/commands/controller.ts +4 -0
  145. package/src/commands/entrypoint-api-readiness.ts +74 -0
  146. package/src/http/index.ts +1 -1
  147. package/src/http/list-total-cache.ts +14 -7
  148. package/src/http/memory.ts +38 -9
  149. package/src/http/script-runs.ts +174 -5
  150. package/src/http/status.ts +10 -2
  151. package/src/http/tasks.ts +4 -2
  152. package/src/providers/amp-adapter.ts +1 -1
  153. package/src/providers/fx-adapter.ts +1 -1
  154. package/src/runtime-providers/controller-protocol.ts +14 -1
  155. package/src/runtime-providers/controller.ts +5 -0
  156. package/src/runtime-providers/docker.ts +108 -23
  157. package/src/runtime-providers/types.ts +16 -0
  158. package/src/script-workflows/workflow-ctx.ts +8 -0
  159. package/src/scripts-runtime/types/stdlib.d.ts +22 -0
  160. package/src/scripts-runtime/types/swarm-sdk.d.ts +22 -0
  161. package/src/server-user.ts +1 -0
  162. package/src/slack/commands.ts +5 -1
  163. package/src/slack/handlers.ts +37 -2
  164. package/src/slack/left-threads.ts +27 -4
  165. package/src/tasks/follow-up-redelegation-guard.ts +155 -0
  166. package/src/tests/claude-artifacts-read-script.test.ts +209 -0
  167. package/src/tests/db-client.test.ts +21 -0
  168. package/src/tests/db-task-read-contract.test.ts +8 -2
  169. package/src/tests/db-task-write-contract.test.ts +5 -1
  170. package/src/tests/entrypoint-api-readiness.test.ts +416 -210
  171. package/src/tests/explicit-model-validation.test.ts +14 -8
  172. package/src/tests/follow-up-redelivery-guard.test.ts +432 -202
  173. package/src/tests/list-total-cache.test.ts +27 -2
  174. package/src/tests/memory-fts-populate.test.ts +11 -9
  175. package/src/tests/memory-retrieval-purge.test.ts +80 -0
  176. package/src/tests/migration-260-runtime-pool-cache.test.ts +118 -0
  177. package/src/tests/query-audit-plans.test.ts +27 -0
  178. package/src/tests/runtime-cache-cleanup.test.ts +367 -0
  179. package/src/tests/runtime-e2b-pools.test.ts +41 -0
  180. package/src/tests/runtime-pools-db.test.ts +98 -0
  181. package/src/tests/runtime-pools-http.test.ts +29 -0
  182. package/src/tests/runtime-providers-docker.test.ts +253 -0
  183. package/src/tests/runtime-remote-controller.test.ts +66 -2
  184. package/src/tests/runtime-supervisor.test.ts +66 -2
  185. package/src/tests/script-connections.test.ts +11 -0
  186. package/src/tests/script-runs-http.test.ts +684 -2
  187. package/src/tests/seed-scripts.test.ts +26 -0
  188. package/src/tests/slack-leave-thread.test.ts +145 -5
  189. package/src/tests/slack-render-v2-query-plans.test.ts +24 -2
  190. package/src/tests/status.test.ts +11 -0
  191. package/src/tests/tasks-readable-row.test.ts +155 -0
  192. package/src/tools/send-task.ts +35 -47
  193. package/src/tools/slack-leave-thread.ts +1 -1
  194. package/src/tools/task-dedup.ts +45 -21
  195. package/src/types.ts +38 -0
  196. package/templates/skills/composio-google-docs/content.md +10 -0
  197. package/templates/skills/script-workflows/content.md +19 -1
  198. package/templates/skills/swarm-docs/content.md +1 -0
  199. package/templates/skills/swarm-scripts/SKILL.md +2 -0
  200. package/templates/skills/swarm-scripts/content.md +2 -0
  201. package/dist/cli-dznrgaep.js +0 -30
  202. package/dist/cli-w3hcrxht.js +0 -6985
  203. package/dist/handlers-vwmba1m2.js +0 -31
  204. package/dist/index-5r71rf98.js +0 -32
  205. package/dist/index-anmwxs87.js +0 -29
  206. package/dist/index-spp0ra5c.js +0 -36
@@ -0,0 +1,209 @@
1
+ /**
2
+ * examples/claude-artifacts-read/claude-artifacts-read.sh must never write
3
+ * outside <outdir> and never replace a file with content that failed its
4
+ * sha256 check. A mock `claude` (CLAUDE_BIN) replays canned stream-json, so
5
+ * no network or auth is involved.
6
+ */
7
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
8
+ import { createHash } from "node:crypto";
9
+ import {
10
+ lstat,
11
+ mkdir,
12
+ mkdtemp,
13
+ readdir,
14
+ readFile,
15
+ readlink,
16
+ rm,
17
+ symlink,
18
+ writeFile,
19
+ } from "node:fs/promises";
20
+ import { tmpdir } from "node:os";
21
+ import { join, resolve } from "node:path";
22
+ import { CHILD_PROCESS_TEST_BUDGET_MS, runChild } from "./test-proc";
23
+
24
+ const SCRIPT = resolve(
25
+ import.meta.dir,
26
+ "../../examples/claude-artifacts-read/claude-artifacts-read.sh",
27
+ );
28
+
29
+ const MOCK_CLAUDE = `#!/usr/bin/env bash
30
+ case "$2" in
31
+ *list_files*) cat "$MOCK_DIR/list.jsonl" ;;
32
+ *) cat "$MOCK_DIR/read.jsonl" ;;
33
+ esac
34
+ `;
35
+
36
+ const sha256 = (s: string) => createHash("sha256").update(s).digest("hex");
37
+
38
+ function toolResult(blocks: string[]): string {
39
+ return JSON.stringify({
40
+ type: "user",
41
+ message: {
42
+ content: [{ type: "tool_result", content: blocks.map((text) => ({ type: "text", text })) }],
43
+ },
44
+ });
45
+ }
46
+
47
+ let root: string;
48
+ let out: string;
49
+ let outside: string;
50
+ let mockDir: string;
51
+
52
+ /**
53
+ * Stage a listing whose sha256 for each path is `listed` and a read that
54
+ * returns `served`, then run the script into `out`.
55
+ */
56
+ async function run(files: { path: string; listed: string; served: string }[]) {
57
+ const init = JSON.stringify({
58
+ type: "system",
59
+ subtype: "init",
60
+ claude_code_version: "2.1.314",
61
+ tools: ["mcp__claude-artifacts__list_files", "mcp__claude-artifacts__read_file"],
62
+ });
63
+ const manifest = {
64
+ slug: "art_mock",
65
+ files: files.map((f) => ({
66
+ path: f.path,
67
+ content_type: "text/html",
68
+ size: f.listed.length,
69
+ sha256: sha256(f.listed),
70
+ })),
71
+ };
72
+ await writeFile(
73
+ join(mockDir, "list.jsonl"),
74
+ `${init}\n${toolResult([JSON.stringify(manifest)])}\n`,
75
+ );
76
+ const reads = files.map((f) =>
77
+ toolResult([
78
+ JSON.stringify({ path: f.path }),
79
+ `<untrusted-document-abc123>${f.served}</untrusted-document-abc123>`,
80
+ ]),
81
+ );
82
+ await writeFile(join(mockDir, "read.jsonl"), `${init}\n${reads.join("\n")}\n`);
83
+ return runChild(["bash", SCRIPT, "https://claude.ai/artifact/mock", out], {
84
+ env: {
85
+ PATH: process.env.PATH,
86
+ HOME: root,
87
+ CLAUDE_BIN: join(mockDir, "claude"),
88
+ MOCK_DIR: mockDir,
89
+ },
90
+ });
91
+ }
92
+
93
+ beforeEach(async () => {
94
+ root = await mkdtemp(join(tmpdir(), "claude-artifacts-read-"));
95
+ out = join(root, "out");
96
+ outside = join(root, "outside");
97
+ mockDir = join(root, "mock");
98
+ await mkdir(out);
99
+ await mkdir(outside);
100
+ await mkdir(mockDir);
101
+ await writeFile(join(mockDir, "claude"), MOCK_CLAUDE, { mode: 0o755 });
102
+ });
103
+
104
+ afterEach(async () => {
105
+ await rm(root, { recursive: true, force: true });
106
+ });
107
+
108
+ describe("claude-artifacts-read.sh", () => {
109
+ test(
110
+ "a sha256 mismatch leaves an existing file untouched",
111
+ async () => {
112
+ await mkdir(join(out, "project"));
113
+ await writeFile(join(out, "project/a.html"), "previous good copy");
114
+
115
+ const r = await run([
116
+ { path: "project/a.html", listed: "real content", served: "truncated" },
117
+ ]);
118
+
119
+ expect(r.exitCode).toBe(1);
120
+ expect(r.stderr).toContain("sha256 mismatch: project/a.html");
121
+ expect(await readFile(join(out, "project/a.html"), "utf8")).toBe("previous good copy");
122
+ expect(await readdir(join(out, "project"))).toEqual(["a.html"]);
123
+ },
124
+ CHILD_PROCESS_TEST_BUDGET_MS,
125
+ );
126
+
127
+ test(
128
+ "a sha256 mismatch never writes through a destination symlink",
129
+ async () => {
130
+ await writeFile(join(outside, "target.txt"), "external");
131
+ await symlink(join(outside, "target.txt"), join(out, "a.html"));
132
+
133
+ const r = await run([{ path: "a.html", listed: "real content", served: "truncated" }]);
134
+
135
+ expect(r.exitCode).toBe(1);
136
+ expect(await readFile(join(outside, "target.txt"), "utf8")).toBe("external");
137
+ },
138
+ CHILD_PROCESS_TEST_BUDGET_MS,
139
+ );
140
+
141
+ test(
142
+ "a verified file is not written through a destination symlink",
143
+ async () => {
144
+ await writeFile(join(outside, "target.txt"), "external");
145
+ await symlink(join(outside, "target.txt"), join(out, "a.html"));
146
+
147
+ const r = await run([{ path: "a.html", listed: "real content", served: "real content" }]);
148
+
149
+ expect(r.exitCode).toBe(1);
150
+ expect(r.stderr).toContain("refused");
151
+ expect(await readFile(join(outside, "target.txt"), "utf8")).toBe("external");
152
+ expect(await readlink(join(out, "a.html"))).toBe(join(outside, "target.txt"));
153
+ },
154
+ CHILD_PROCESS_TEST_BUDGET_MS,
155
+ );
156
+
157
+ test(
158
+ "a verified file is not written through a parent directory symlink",
159
+ async () => {
160
+ await symlink(outside, join(out, "project"));
161
+
162
+ const r = await run([
163
+ { path: "project/a.html", listed: "real content", served: "real content" },
164
+ ]);
165
+
166
+ expect(r.exitCode).toBe(1);
167
+ expect(r.stderr).toContain("refused");
168
+ expect(await readdir(outside)).toEqual([]);
169
+ },
170
+ CHILD_PROCESS_TEST_BUDGET_MS,
171
+ );
172
+
173
+ test(
174
+ "manifest.json is not written through a symlink",
175
+ async () => {
176
+ await writeFile(join(outside, "target.json"), "external");
177
+ await symlink(join(outside, "target.json"), join(out, "manifest.json"));
178
+
179
+ const r = await run([{ path: "a.html", listed: "real content", served: "real content" }]);
180
+
181
+ expect(r.exitCode).toBe(1);
182
+ expect(await readFile(join(outside, "target.json"), "utf8")).toBe("external");
183
+ // The run stops before any artifact file is written.
184
+ await expect(lstat(join(out, "a.html"))).rejects.toThrow();
185
+ },
186
+ CHILD_PROCESS_TEST_BUDGET_MS,
187
+ );
188
+
189
+ test(
190
+ "verified files and the manifest replace existing regular files",
191
+ async () => {
192
+ await mkdir(join(out, "project"));
193
+ await writeFile(join(out, "project/a.html"), "old");
194
+ await writeFile(join(out, "manifest.json"), "old");
195
+
196
+ const r = await run([
197
+ { path: "project/a.html", listed: "real content", served: "real content" },
198
+ ]);
199
+
200
+ expect(r.exitCode).toBe(0);
201
+ expect(r.stdout).toContain("ok: project/a.html");
202
+ expect(await readFile(join(out, "project/a.html"), "utf8")).toBe("real content");
203
+ expect(JSON.parse(await readFile(join(out, "manifest.json"), "utf8")).slug).toBe("art_mock");
204
+ expect(await readdir(join(out, "project"))).toEqual(["a.html"]);
205
+ expect((await lstat(join(out, "project/a.html"))).isFile()).toBe(true);
206
+ },
207
+ CHILD_PROCESS_TEST_BUDGET_MS,
208
+ );
209
+ });
@@ -475,6 +475,27 @@ describe("db-client slow statement warning", () => {
475
475
  }
476
476
  });
477
477
 
478
+ test("does not count a slow database open against the first statement", async () => {
479
+ // The first call opens the database (and applies migrations) through the
480
+ // client's getter. That cost is boot work, not the statement's.
481
+ let opened = false;
482
+ const lazy = createBunSqliteClient(() => {
483
+ if (!opened) {
484
+ opened = true;
485
+ Bun.sleepSync(150);
486
+ }
487
+ return raw;
488
+ });
489
+ const warn = spyOn(console, "warn").mockImplementation(() => {});
490
+ try {
491
+ await lazy.query("SELECT name FROM items");
492
+ expect(opened).toBe(true);
493
+ expect(slowWarnings(warn)).toHaveLength(0);
494
+ } finally {
495
+ warn.mockRestore();
496
+ }
497
+ });
498
+
478
499
  test("does not log fast statements", async () => {
479
500
  const warn = spyOn(console, "warn").mockImplementation(() => {});
480
501
  try {
@@ -144,6 +144,7 @@ value:completeApprovalCancellationNotificationClaim
144
144
  value:completeTask
145
145
  value:computeContentHash
146
146
  value:countActiveScriptRuns
147
+ value:countAgentDispatchesInThread
147
148
  value:countAllMetrics
148
149
  value:countAllPages
149
150
  value:countKv
@@ -218,9 +219,10 @@ value:extensionAgentAssignmentError
218
219
  value:failPendingResumeIfUnclaimed
219
220
  value:failTask
220
221
  value:findActiveTasksByVcs
221
- value:findCompletedTaskInThread
222
+ value:findCompletedTasksInThread
222
223
  value:findExistingLinearTrackerContextWork
223
224
  value:findRecentCancelledTaskInThread
225
+ value:findRecentFailedTaskInThread
224
226
  value:findRecentSimilarTasks
225
227
  value:findTaskByAgentMailThread
226
228
  value:findTaskByGitHub
@@ -369,6 +371,7 @@ value:getScheduledTasks
369
371
  value:getScriptRun
370
372
  value:getScriptRunByIdempotencyKey
371
373
  value:getScriptRunExecutionArgs
374
+ value:getScriptRunFollowUpGuard
372
375
  value:getScriptRunJournalStep
373
376
  value:getServiceByAgentAndName
374
377
  value:getServiceById
@@ -561,6 +564,7 @@ value:setApiKeyName
561
564
  value:setApiKeyPlan
562
565
  value:setBudgetRefusalFollowUpTaskId
563
566
  value:setFavorite
567
+ value:setScriptRunFollowUpGuard
564
568
  value:setSlackMessageTracking
565
569
  value:setUserFavorite
566
570
  value:settleSupersededTaskDependents
@@ -622,10 +626,12 @@ type:PendingChatRelay
622
626
  type:TaskFilters
623
627
  value:SUPERSEDED_RESUME_REPAIR_WINDOW_MS
624
628
  value:TASK_LINEAGE_LIMITS
629
+ value:countAgentDispatchesInThread
625
630
  value:findActiveTasksByVcs
626
- value:findCompletedTaskInThread
631
+ value:findCompletedTasksInThread
627
632
  value:findExistingLinearTrackerContextWork
628
633
  value:findRecentCancelledTaskInThread
634
+ value:findRecentFailedTaskInThread
629
635
  value:findTaskByGitHub
630
636
  value:findTaskByVcs
631
637
  value:getAgentCurrentTask
@@ -167,6 +167,7 @@ value:completeApprovalCancellationNotificationClaim
167
167
  value:completeTask
168
168
  value:computeContentHash
169
169
  value:countActiveScriptRuns
170
+ value:countAgentDispatchesInThread
170
171
  value:countAllMetrics
171
172
  value:countAllPages
172
173
  value:countKv
@@ -241,9 +242,10 @@ value:extensionAgentAssignmentError
241
242
  value:failPendingResumeIfUnclaimed
242
243
  value:failTask
243
244
  value:findActiveTasksByVcs
244
- value:findCompletedTaskInThread
245
+ value:findCompletedTasksInThread
245
246
  value:findExistingLinearTrackerContextWork
246
247
  value:findRecentCancelledTaskInThread
248
+ value:findRecentFailedTaskInThread
247
249
  value:findRecentSimilarTasks
248
250
  value:findTaskByAgentMailThread
249
251
  value:findTaskByGitHub
@@ -392,6 +394,7 @@ value:getScheduledTasks
392
394
  value:getScriptRun
393
395
  value:getScriptRunByIdempotencyKey
394
396
  value:getScriptRunExecutionArgs
397
+ value:getScriptRunFollowUpGuard
395
398
  value:getScriptRunJournalStep
396
399
  value:getServiceByAgentAndName
397
400
  value:getServiceById
@@ -584,6 +587,7 @@ value:setApiKeyName
584
587
  value:setApiKeyPlan
585
588
  value:setBudgetRefusalFollowUpTaskId
586
589
  value:setFavorite
590
+ value:setScriptRunFollowUpGuard
587
591
  value:setSlackMessageTracking
588
592
  value:setUserFavorite
589
593
  value:settleSupersededTaskDependents