@desplega.ai/agent-swarm 1.147.0 → 1.148.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 (73) hide show
  1. package/README.md +11 -0
  2. package/dist/{acp-adapter-9n319wqc.js → acp-adapter-grez7xhd.js} +1 -1
  3. package/dist/{actions-2vxqvpr9.js → actions-kweznh5a.js} +6 -6
  4. package/dist/{app-w066xfy0.js → app-5fvvjv9m.js} +3 -3
  5. package/dist/{assistant-kt31dj2h.js → assistant-40m4w50s.js} +8 -8
  6. package/dist/{boot-reembed-j3mm3rfz.js → boot-reembed-9rxmb26n.js} +4 -4
  7. package/dist/{boot-reembed-az5rassp.js → boot-reembed-cn357y5n.js} +3 -3
  8. package/dist/{boot-scrub-logs-b6h54817.js → boot-scrub-logs-5tpp0x3y.js} +2 -2
  9. package/dist/{claude-adapter-w1z04ann.js → claude-adapter-9pz5q2pb.js} +3 -3
  10. package/dist/{claude-sdk-session-f0cc1kec.js → claude-sdk-session-v8w0s7qm.js} +3 -3
  11. package/dist/{cli-rw51bq3j.js → cli-0amc6myn.js} +1 -1
  12. package/dist/{cli-3tgwnf43.js → cli-0er5m69z.js} +3 -3
  13. package/dist/{cli-f7dsy61y.js → cli-3n7cbfjn.js} +1 -1
  14. package/dist/{cli-smby5m17.js → cli-56y3hch2.js} +5 -5
  15. package/dist/{cli-1th1728g.js → cli-5khqvvnq.js} +1 -1
  16. package/dist/{cli-ftht3zzj.js → cli-6s2y152y.js} +1 -1
  17. package/dist/{cli-53s590z8.js → cli-7gn7c2af.js} +1 -1
  18. package/dist/{cli-tzvk9haz.js → cli-9216xm24.js} +9 -9
  19. package/dist/{cli-w9kb3bk1.js → cli-anedxf1w.js} +3 -1
  20. package/dist/{cli-89xfd7f9.js → cli-brxyat7h.js} +1 -1
  21. package/dist/{cli-12fz972k.js → cli-cdjezzc7.js} +3 -3
  22. package/dist/{cli-zhjmqxzh.js → cli-dcem3pr1.js} +1 -1
  23. package/dist/{cli-b5bq9crk.js → cli-dk3me3qs.js} +7 -7
  24. package/dist/{cli-yz3djrm0.js → cli-f8j56xv2.js} +7 -7
  25. package/dist/{cli-m8yrsg97.js → cli-hp2est46.js} +2 -2
  26. package/dist/{cli-mmemxxdc.js → cli-hz8xhefy.js} +2 -2
  27. package/dist/{cli-8v0g1yc8.js → cli-hzhhrt7b.js} +1 -1
  28. package/dist/{cli-hbv0kq6w.js → cli-kzvhgvk3.js} +2 -2
  29. package/dist/{cli-719k9j2c.js → cli-m1p5cysp.js} +2 -2
  30. package/dist/{cli-dttph1bw.js → cli-n2cwscdd.js} +2 -2
  31. package/dist/{cli-54wwve05.js → cli-n9grk3yd.js} +3 -3
  32. package/dist/{cli-atcve9y8.js → cli-p73drk6p.js} +78 -37
  33. package/dist/{cli-f146mn5s.js → cli-qy6h009w.js} +5 -5
  34. package/dist/{cli-de14znh8.js → cli-tebsjpk7.js} +2 -2
  35. package/dist/{cli-4nr988h6.js → cli-v3e51x7w.js} +1 -1
  36. package/dist/{cli-p1d5b073.js → cli-xqcpg1st.js} +1 -1
  37. package/dist/{cli-n8508nre.js → cli-ztn0ga15.js} +1 -1
  38. package/dist/cli.js +13 -11
  39. package/dist/{codex-adapter-zw4dwsx7.js → codex-adapter-ehhj2qwc.js} +2 -2
  40. package/dist/{codex-session-runner-k981s65v.js → codex-session-runner-5hne566k.js} +2 -2
  41. package/dist/{commands-sykrxx7e.js → commands-xnvh951h.js} +2 -2
  42. package/dist/{db-sfbsc8wt.js → db-ycr5zfr4.js} +2 -2
  43. package/dist/{handlers-0aspcejm.js → handlers-1vt77kgq.js} +8 -8
  44. package/dist/{hook-hxccs7m5.js → hook-91ntpmff.js} +1 -1
  45. package/dist/{hook-4nvqj2sb.js → hook-qfwjydvn.js} +2 -2
  46. package/dist/{http-y06z517d.js → http-h6g7bv5b.js} +34 -34
  47. package/dist/{index-hssww7kj.js → index-0ez2aack.js} +11 -11
  48. package/dist/{index-tce1rss4.js → index-47ghesbn.js} +8 -8
  49. package/dist/{index-x5j894zf.js → index-antf9t4p.js} +10 -10
  50. package/dist/{index-k8znfx0z.js → index-d2mytx95.js} +9 -9
  51. package/dist/{keepalive-w733ax66.js → keepalive-c4sp7yvd.js} +4 -4
  52. package/dist/{lead-75bjqa9d.js → lead-4r7z9e8v.js} +22 -22
  53. package/dist/{maintenance-w4f1zjck.js → maintenance-yqjfdjas.js} +4 -4
  54. package/dist/{oauth-refresh-sweep-wdetky26.js → oauth-refresh-sweep-wtqgnh3c.js} +3 -3
  55. package/dist/{onboard-bb5net8s.js → onboard-a4krmh46.js} +2 -2
  56. package/dist/{otel-impl-0w7c14ap.js → otel-impl-725kq0vp.js} +1 -1
  57. package/dist/{pricing-refresh-9nzsrgcq.js → pricing-refresh-kqxd7w9h.js} +4 -4
  58. package/dist/{rbac-roles-z1jrffbt.js → rbac-roles-gws5xej6.js} +2 -2
  59. package/dist/{rbac-roles-r3hqjp09.js → rbac-roles-t3wygpth.js} +3 -3
  60. package/dist/{render-v2-vbr89fnb.js → render-v2-x1wxvn51.js} +3 -3
  61. package/dist/{seed-pricing-g62hy1pk.js → seed-pricing-t6y8d0vf.js} +3 -3
  62. package/dist/{setup-gnfqnptp.js → setup-x50py6xg.js} +2 -2
  63. package/dist/{worker-kznxp4qm.js → worker-zsz0qfxk.js} +22 -22
  64. package/openapi.json +1 -1
  65. package/package.json +3 -1
  66. package/src/tests/mcp-input-ergonomics.test.ts +386 -0
  67. package/src/tools/accept-steer.ts +0 -1
  68. package/src/tools/memory-get.ts +10 -3
  69. package/src/tools/memory-rate.ts +19 -7
  70. package/src/tools/memory-search.ts +2 -1
  71. package/src/tools/memory-store.ts +25 -5
  72. package/src/tools/send-task.ts +9 -2
  73. package/src/tools/store-progress.ts +35 -4
@@ -2,8 +2,8 @@ import {
2
2
  forceRefreshAuthorizationOrThrow,
3
3
  getAuthorizationById,
4
4
  listAuthorizationSweepRows
5
- } from "./cli-zhjmqxzh.js";
6
- import"./cli-ftht3zzj.js";
5
+ } from "./cli-dcem3pr1.js";
6
+ import"./cli-6s2y152y.js";
7
7
  import"./cli-y6b6fb76.js";
8
8
  import"./cli-kpv03zhj.js";
9
9
  import"./cli-e29gmdmw.js";
@@ -11,7 +11,7 @@ import"./cli-c2mk1kgf.js";
11
11
  import"./cli-ej6gbxfb.js";
12
12
  import"./cli-sd0mxa3c.js";
13
13
  import"./cli-w5e1d1vv.js";
14
- import"./cli-w9kb3bk1.js";
14
+ import"./cli-anedxf1w.js";
15
15
  import {
16
16
  init_secret_scrubber,
17
17
  scrubSecrets
@@ -3,7 +3,7 @@ import {
3
3
  createDefaultMcpJson,
4
4
  createDefaultSettingsLocal,
5
5
  createHooksConfig
6
- } from "./cli-n8508nre.js";
6
+ } from "./cli-ztn0ga15.js";
7
7
  import {
8
8
  require_jsx_dev_runtime
9
9
  } from "./cli-s2qjdd6z.js";
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  init_package,
15
15
  package_default
16
- } from "./cli-w9kb3bk1.js";
16
+ } from "./cli-anedxf1w.js";
17
17
  import {
18
18
  Box_default,
19
19
  Select,
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  init_package,
6
6
  package_default
7
- } from "./cli-w9kb3bk1.js";
7
+ } from "./cli-anedxf1w.js";
8
8
  import {
9
9
  init_secret_scrubber,
10
10
  scrubSecrets
@@ -2,9 +2,9 @@ import {
2
2
  PRICING_REFRESH_INTERVAL_MS,
3
3
  refreshPricingFromModelsDev,
4
4
  startPricingRefreshLoop
5
- } from "./cli-m8yrsg97.js";
6
- import"./cli-rw51bq3j.js";
7
- import"./cli-ftht3zzj.js";
5
+ } from "./cli-hp2est46.js";
6
+ import"./cli-0amc6myn.js";
7
+ import"./cli-6s2y152y.js";
8
8
  import"./cli-y6b6fb76.js";
9
9
  import"./cli-kpv03zhj.js";
10
10
  import"./cli-e29gmdmw.js";
@@ -12,7 +12,7 @@ import"./cli-c2mk1kgf.js";
12
12
  import"./cli-ej6gbxfb.js";
13
13
  import"./cli-sd0mxa3c.js";
14
14
  import"./cli-w5e1d1vv.js";
15
- import"./cli-w9kb3bk1.js";
15
+ import"./cli-anedxf1w.js";
16
16
  import"./cli-xqcq9y5e.js";
17
17
  import"./cli-zf8jjwex.js";
18
18
  import"./cli-bc05qbss.js";
@@ -5,7 +5,7 @@ import {
5
5
  getDb,
6
6
  getDbClient,
7
7
  init_db
8
- } from "./cli-ftht3zzj.js";
8
+ } from "./cli-6s2y152y.js";
9
9
  import"./cli-y6b6fb76.js";
10
10
  import"./cli-kpv03zhj.js";
11
11
  import"./cli-e29gmdmw.js";
@@ -13,7 +13,7 @@ import"./cli-c2mk1kgf.js";
13
13
  import"./cli-ej6gbxfb.js";
14
14
  import"./cli-sd0mxa3c.js";
15
15
  import"./cli-w5e1d1vv.js";
16
- import"./cli-w9kb3bk1.js";
16
+ import"./cli-anedxf1w.js";
17
17
  import"./cli-xqcq9y5e.js";
18
18
  import"./cli-zf8jjwex.js";
19
19
  import"./cli-bc05qbss.js";
@@ -7,9 +7,9 @@ import {
7
7
  getUserGrant,
8
8
  listUserRoles,
9
9
  runRbacCliCommand
10
- } from "./cli-8v0g1yc8.js";
10
+ } from "./cli-hzhhrt7b.js";
11
11
  import"./cli-cmp8g3sw.js";
12
- import"./cli-ftht3zzj.js";
12
+ import"./cli-6s2y152y.js";
13
13
  import"./cli-y6b6fb76.js";
14
14
  import"./cli-kpv03zhj.js";
15
15
  import"./cli-e29gmdmw.js";
@@ -17,7 +17,7 @@ import"./cli-c2mk1kgf.js";
17
17
  import"./cli-ej6gbxfb.js";
18
18
  import"./cli-sd0mxa3c.js";
19
19
  import"./cli-w5e1d1vv.js";
20
- import"./cli-w9kb3bk1.js";
20
+ import"./cli-anedxf1w.js";
21
21
  import"./cli-xqcq9y5e.js";
22
22
  import"./cli-zf8jjwex.js";
23
23
  import"./cli-bc05qbss.js";
@@ -9,11 +9,11 @@ import {
9
9
  processSlackRenderV2,
10
10
  renderThreadTree,
11
11
  streamOutcomeCard
12
- } from "./cli-b5bq9crk.js";
12
+ } from "./cli-dk3me3qs.js";
13
13
  import"./cli-q51yprfq.js";
14
14
  import"./cli-9d8ntetj.js";
15
15
  import"./cli-yatxqynm.js";
16
- import"./cli-ftht3zzj.js";
16
+ import"./cli-6s2y152y.js";
17
17
  import"./cli-y6b6fb76.js";
18
18
  import"./cli-kpv03zhj.js";
19
19
  import"./cli-e29gmdmw.js";
@@ -21,7 +21,7 @@ import"./cli-c2mk1kgf.js";
21
21
  import"./cli-ej6gbxfb.js";
22
22
  import"./cli-sd0mxa3c.js";
23
23
  import"./cli-w5e1d1vv.js";
24
- import"./cli-w9kb3bk1.js";
24
+ import"./cli-anedxf1w.js";
25
25
  import"./cli-xqcq9y5e.js";
26
26
  import"./cli-pzxba7gw.js";
27
27
  import"./cli-zf8jjwex.js";
@@ -2,8 +2,8 @@ import {
2
2
  buildModelsDevSeedRows,
3
3
  buildPricingSeedRows,
4
4
  seedPricingFromModelsDev
5
- } from "./cli-rw51bq3j.js";
6
- import"./cli-ftht3zzj.js";
5
+ } from "./cli-0amc6myn.js";
6
+ import"./cli-6s2y152y.js";
7
7
  import"./cli-y6b6fb76.js";
8
8
  import"./cli-kpv03zhj.js";
9
9
  import"./cli-e29gmdmw.js";
@@ -11,7 +11,7 @@ import"./cli-c2mk1kgf.js";
11
11
  import"./cli-ej6gbxfb.js";
12
12
  import"./cli-sd0mxa3c.js";
13
13
  import"./cli-w5e1d1vv.js";
14
- import"./cli-w9kb3bk1.js";
14
+ import"./cli-anedxf1w.js";
15
15
  import"./cli-xqcq9y5e.js";
16
16
  import"./cli-zf8jjwex.js";
17
17
  import"./cli-bc05qbss.js";
@@ -4,14 +4,14 @@ import {
4
4
  createDefaultMcpJson,
5
5
  createDefaultSettingsLocal,
6
6
  createHooksConfig
7
- } from "./cli-n8508nre.js";
7
+ } from "./cli-ztn0ga15.js";
8
8
  import {
9
9
  require_jsx_dev_runtime
10
10
  } from "./cli-s2qjdd6z.js";
11
11
  import {
12
12
  getApiKey
13
13
  } from "./cli-f14fvzag.js";
14
- import"./cli-w9kb3bk1.js";
14
+ import"./cli-anedxf1w.js";
15
15
  import {
16
16
  Box_default,
17
17
  Spinner,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  runAgent
3
- } from "./cli-tzvk9haz.js";
3
+ } from "./cli-9216xm24.js";
4
4
  import"./cli-0b6y7kxm.js";
5
- import"./cli-4nr988h6.js";
5
+ import"./cli-v3e51x7w.js";
6
6
  import"./cli-cn6mmc7f.js";
7
7
  import"./cli-hsxj0yar.js";
8
8
  import"./cli-9b13ggd1.js";
@@ -12,11 +12,11 @@ import"./cli-r94z9p1z.js";
12
12
  import"./cli-2djtr51b.js";
13
13
  import {
14
14
  getEnabledCapabilities
15
- } from "./cli-atcve9y8.js";
15
+ } from "./cli-p73drk6p.js";
16
16
  import"./cli-8fc8de14.js";
17
17
  import"./cli-j490egb5.js";
18
- import"./cli-719k9j2c.js";
19
- import"./cli-89xfd7f9.js";
18
+ import"./cli-m1p5cysp.js";
19
+ import"./cli-brxyat7h.js";
20
20
  import"./cli-d8brzxjb.js";
21
21
  import"./cli-b5b30yzk.js";
22
22
  import"./cli-rvqz34y5.js";
@@ -27,40 +27,40 @@ import"./cli-s25avqg9.js";
27
27
  import"./cli-eree7rnv.js";
28
28
  import"./cli-xvkmedgn.js";
29
29
  import"./cli-4yc5d8fk.js";
30
- import"./cli-smby5m17.js";
31
- import"./cli-m8yrsg97.js";
32
- import"./cli-rw51bq3j.js";
33
- import"./cli-8v0g1yc8.js";
30
+ import"./cli-56y3hch2.js";
31
+ import"./cli-hp2est46.js";
32
+ import"./cli-0amc6myn.js";
33
+ import"./cli-hzhhrt7b.js";
34
34
  import"./cli-cmp8g3sw.js";
35
- import"./cli-de14znh8.js";
35
+ import"./cli-tebsjpk7.js";
36
36
  import"./cli-qgm2pfpq.js";
37
- import"./cli-f7dsy61y.js";
37
+ import"./cli-3n7cbfjn.js";
38
38
  import"./cli-07jd68a1.js";
39
- import"./cli-yz3djrm0.js";
39
+ import"./cli-f8j56xv2.js";
40
40
  import"./cli-1ebpar1q.js";
41
41
  import"./cli-e7d20cty.js";
42
42
  import"./cli-3dpj7atn.js";
43
- import"./cli-3tgwnf43.js";
43
+ import"./cli-0er5m69z.js";
44
44
  import"./cli-epan13p1.js";
45
- import"./cli-hbv0kq6w.js";
46
- import"./cli-1th1728g.js";
45
+ import"./cli-kzvhgvk3.js";
46
+ import"./cli-5khqvvnq.js";
47
47
  import"./cli-y89t4nca.js";
48
48
  import"./cli-f14fvzag.js";
49
49
  import"./cli-m9sxbkhm.js";
50
- import"./cli-zhjmqxzh.js";
50
+ import"./cli-dcem3pr1.js";
51
51
  import"./cli-fad8m16k.js";
52
52
  import"./cli-aehm1rbq.js";
53
53
  import"./cli-bsakbbkm.js";
54
54
  import"./cli-51tf7ac3.js";
55
- import"./cli-mmemxxdc.js";
55
+ import"./cli-hz8xhefy.js";
56
56
  import"./cli-jsp14b6c.js";
57
- import"./cli-p1d5b073.js";
58
- import"./cli-53s590z8.js";
59
- import"./cli-b5bq9crk.js";
57
+ import"./cli-xqcpg1st.js";
58
+ import"./cli-7gn7c2af.js";
59
+ import"./cli-dk3me3qs.js";
60
60
  import"./cli-q51yprfq.js";
61
61
  import"./cli-9d8ntetj.js";
62
62
  import"./cli-yatxqynm.js";
63
- import"./cli-ftht3zzj.js";
63
+ import"./cli-6s2y152y.js";
64
64
  import"./cli-y6b6fb76.js";
65
65
  import"./cli-kpv03zhj.js";
66
66
  import"./cli-e29gmdmw.js";
@@ -68,7 +68,7 @@ import"./cli-c2mk1kgf.js";
68
68
  import"./cli-ej6gbxfb.js";
69
69
  import"./cli-sd0mxa3c.js";
70
70
  import"./cli-w5e1d1vv.js";
71
- import"./cli-w9kb3bk1.js";
71
+ import"./cli-anedxf1w.js";
72
72
  import"./cli-xqcq9y5e.js";
73
73
  import"./cli-pzxba7gw.js";
74
74
  import"./cli-f7kvq46p.js";
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Swarm API",
5
- "version": "1.147.0",
5
+ "version": "1.148.0",
6
6
  "description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
7
7
  },
8
8
  "servers": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "1.147.0",
3
+ "version": "1.148.0",
4
4
  "description": "agent-swarm.dev — an open-source operating system for AI work: a lead agent breaks goals into tasks, routes them to specialized workers such as Claude Code or Codex, runs each worker in an isolated container, and preserves shared memory, tools, schedules, and review gates so delegated work compounds across sessions.",
5
5
  "keywords": [
6
6
  "agent-swarm.dev",
@@ -111,6 +111,8 @@
111
111
  "prepare-release": "bun scripts/prepare-release.ts",
112
112
  "refresh:vendored-openapi": "bun scripts/refresh-vendored-openapi.ts",
113
113
  "sync-chart-version": "bun scripts/sync-chart-version.ts",
114
+ "sync-plugin-versions": "bun scripts/sync-plugin-versions.ts",
115
+ "check:plugin-versions": "bun scripts/sync-plugin-versions.ts --check",
114
116
  "bump:agent-fs": "bun scripts/bump-agent-fs.ts",
115
117
  "check-chart-version": "bun scripts/sync-chart-version.ts --check-if-package-version-changed",
116
118
  "cli": "bun src/cli.tsx",
@@ -0,0 +1,386 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import type { ZodType } from "zod";
4
+ import {
5
+ closeDb,
6
+ createAgent,
7
+ createSteeringMessage,
8
+ createTaskExtended,
9
+ createUser,
10
+ getAgentById,
11
+ getDbClient,
12
+ getSteeringMessageById,
13
+ getTaskById,
14
+ initDb,
15
+ markSteeringDelivered,
16
+ startTask,
17
+ } from "../be/db";
18
+ import { getMemoryStore } from "../be/memory";
19
+ import { registerAcceptSteerTool } from "../tools/accept-steer";
20
+ import { getTaskDetailsInputSchema } from "../tools/get-task-details";
21
+ import { registerMemoryGetTool } from "../tools/memory-get";
22
+ import { registerMemoryRateTool } from "../tools/memory-rate";
23
+ import { registerMemorySearchTool } from "../tools/memory-search";
24
+ import { registerMemoryStoreTool } from "../tools/memory-store";
25
+ import { registerSendTaskTool, sendTaskInputSchema } from "../tools/send-task";
26
+ import { registerStoreProgressTool } from "../tools/store-progress";
27
+ import type { SwarmToolResult } from "../tools/utils";
28
+
29
+ const agentId = "aaaa0000-0000-4000-8000-000000000901";
30
+ const otherId = "bbbb0000-0000-4000-8000-000000000902";
31
+ const server = new McpServer({ name: "input-regressions", version: "1.0.0" });
32
+ for (const register of [
33
+ registerAcceptSteerTool,
34
+ registerMemoryGetTool,
35
+ registerMemoryRateTool,
36
+ registerMemorySearchTool,
37
+ registerMemoryStoreTool,
38
+ registerSendTaskTool,
39
+ registerStoreProgressTool,
40
+ ]) {
41
+ register(server);
42
+ }
43
+ const registered = (
44
+ server as unknown as {
45
+ _registeredTools: Record<
46
+ string,
47
+ {
48
+ inputSchema: ZodType;
49
+ handler: (args: unknown, meta: unknown) => Promise<SwarmToolResult>;
50
+ }
51
+ >;
52
+ }
53
+ )._registeredTools;
54
+
55
+ async function call(name: string, args: unknown, sourceTaskId?: string, callerId = agentId) {
56
+ const tool = registered[name]!;
57
+ // Exercise the real registered schema before invoking the handler, as MCP does.
58
+ return tool.handler(tool.inputSchema.parse(args), {
59
+ sessionId: "input-regressions-session",
60
+ requestInfo: {
61
+ headers: {
62
+ "x-agent-id": callerId,
63
+ ...(sourceTaskId ? { "x-source-task-id": sourceTaskId } : {}),
64
+ },
65
+ },
66
+ });
67
+ }
68
+ async function task(owner = agentId) {
69
+ const row = await createTaskExtended("input schema regression", {
70
+ agentId: owner,
71
+ source: "system",
72
+ followUpConfig: { disabled: true },
73
+ });
74
+ await startTask(row.id);
75
+ return row;
76
+ }
77
+ const originalEmbeddingKey = process.env.EMBEDDING_API_KEY;
78
+ const originalOpenAiKey = process.env.OPENAI_API_KEY;
79
+ beforeAll(async () => {
80
+ process.env.EMBEDDING_API_KEY = "";
81
+ process.env.OPENAI_API_KEY = "";
82
+ initDb(":memory:");
83
+ await createAgent({ id: agentId, name: "Schema worker", isLead: false, status: "idle" });
84
+ await createAgent({ id: otherId, name: "Other schema worker", isLead: false, status: "idle" });
85
+ });
86
+ afterAll(() => {
87
+ closeDb();
88
+ if (originalEmbeddingKey === undefined) delete process.env.EMBEDDING_API_KEY;
89
+ else process.env.EMBEDDING_API_KEY = originalEmbeddingKey;
90
+ if (originalOpenAiKey === undefined) delete process.env.OPENAI_API_KEY;
91
+ else process.env.OPENAI_API_KEY = originalOpenAiKey;
92
+ });
93
+
94
+ describe("store-progress input recovery", () => {
95
+ for (const status of ["pending", "in_progress"]) {
96
+ test(`${status} stores progress without finishing or requeueing`, async () => {
97
+ const row = await task();
98
+ const result = await call("store-progress", {
99
+ taskId: row.id,
100
+ status,
101
+ progress: status,
102
+ output: "must not complete",
103
+ failureReason: "must not fail",
104
+ });
105
+ expect(result.structuredContent?.success).toBe(true);
106
+ const updated = await getTaskById(row.id);
107
+ expect(updated?.progress).toBe(status);
108
+ expect(updated?.status).toBe("in_progress");
109
+ expect(updated?.finishedAt).toBeUndefined();
110
+ expect(updated?.output).toBeUndefined();
111
+ expect((await getAgentById(agentId))?.status).toBe("busy");
112
+ expect(result.structuredContent?.message).not.toContain("marked as");
113
+ });
114
+ }
115
+ test("omitted taskId uses only the caller-owned source task", async () => {
116
+ const row = await task();
117
+ expect(
118
+ (await call("store-progress", { progress: "context works" }, row.id)).structuredContent
119
+ ?.success,
120
+ ).toBe(true);
121
+ expect((await getTaskById(row.id))?.progress).toBe("context works");
122
+ });
123
+ test("omitted taskId with no context returns actionable failure", async () => {
124
+ const result = await call("store-progress", { progress: "lost" });
125
+ expect(result.structuredContent?.success).toBe(false);
126
+ expect(result.structuredContent?.message).toContain("full task UUID");
127
+ });
128
+ test("omitted taskId cannot borrow another agent's source task", async () => {
129
+ const row = await task(otherId);
130
+ expect(
131
+ (await call("store-progress", { status: "completed" }, row.id)).structuredContent?.success,
132
+ ).toBe(false);
133
+ expect((await getTaskById(row.id))?.status).toBe("in_progress");
134
+ });
135
+ test("explicit target wins over source task", async () => {
136
+ const source = await task();
137
+ const target = await task();
138
+ await call("store-progress", { taskId: target.id, progress: "explicit" }, source.id);
139
+ expect((await getTaskById(source.id))?.progress).toBeUndefined();
140
+ expect((await getTaskById(target.id))?.progress).toBe("explicit");
141
+ });
142
+ test("terminal calls still finish, and progress aliases cannot revive them", async () => {
143
+ for (const status of ["completed", "failed"]) {
144
+ const row = await task();
145
+ const result = await call("store-progress", {
146
+ taskId: row.id,
147
+ status,
148
+ output: "done",
149
+ failureReason: "failed",
150
+ });
151
+ expect(result.structuredContent?.success).toBe(true);
152
+ const finished = await getTaskById(row.id);
153
+ expect(finished?.status).toBe(status);
154
+ await call("store-progress", { taskId: row.id, status: "pending", progress: "revive" });
155
+ expect((await getTaskById(row.id))?.status).toBe(status);
156
+ expect((await getTaskById(row.id))?.finishedAt).toBe(finished?.finishedAt);
157
+ }
158
+ });
159
+ test("malformed explicit IDs and unsupported statuses stay invalid", () => {
160
+ const schema = registered["store-progress"]!.inputSchema;
161
+ expect(schema.safeParse({ taskId: "deadbeef" }).success).toBe(false);
162
+ expect(schema.safeParse({ status: "cancelled" }).success).toBe(false);
163
+ });
164
+ });
165
+
166
+ describe("memory input recovery", () => {
167
+ test("search without intent returns a memory and records honest null intent", async () => {
168
+ const row = await task();
169
+ const memory = await getMemoryStore().store({
170
+ agentId,
171
+ scope: "agent",
172
+ name: "needlequartz",
173
+ content: "needlequartz",
174
+ source: "manual",
175
+ });
176
+ const result = await call("memory-search", { query: "needlequartz" }, row.id);
177
+ expect(result.structuredContent?.success).toBe(true);
178
+ const retrieval = await getDbClient().get<{ intent: string | null }>(
179
+ "SELECT intent FROM memory_retrieval WHERE memoryId = ? AND taskId = ?",
180
+ [memory.id, row.id],
181
+ );
182
+ expect(retrieval).toBeDefined();
183
+ expect(retrieval?.intent).toBeNull();
184
+ });
185
+ for (const field of ["id", "memoryId"]) {
186
+ test(`get accepts ${field} without intent and preserves access control`, async () => {
187
+ const row = await task();
188
+ const memory = await getMemoryStore().store({
189
+ agentId,
190
+ scope: "agent",
191
+ name: "private",
192
+ content: "private fact",
193
+ source: "manual",
194
+ });
195
+ expect(
196
+ (await call("memory-get", { [field]: memory.id }, row.id)).structuredContent?.success,
197
+ ).toBe(true);
198
+ expect(
199
+ (await call("memory-get", { [field]: memory.id }, undefined, otherId)).structuredContent
200
+ ?.success,
201
+ ).toBe(false);
202
+ expect(
203
+ (
204
+ await getDbClient().get<{ intent: string | null }>(
205
+ "SELECT intent FROM memory_retrieval WHERE memoryId = ? AND taskId = ?",
206
+ [memory.id, row.id],
207
+ )
208
+ )?.intent,
209
+ ).toBeNull();
210
+ });
211
+ }
212
+ for (const name of ["memory-get", "memory_rate"]) {
213
+ test(`${name} rejects missing and conflicting identifiers`, async () => {
214
+ expect((await call(name, { useful: true })).structuredContent?.success).toBe(false);
215
+ expect(
216
+ (await call(name, { id: agentId, memoryId: otherId, useful: true })).structuredContent
217
+ ?.success,
218
+ ).toBe(false);
219
+ });
220
+ }
221
+ test("rating aliases and long notes reach the endpoint within its 500-character limit", async () => {
222
+ const originalFetch = globalThis.fetch;
223
+ const events: Array<{ memoryId: string; reasoning: string }> = [];
224
+ globalThis.fetch = (async (_input, init) => {
225
+ events.push(JSON.parse(String(init?.body)).events[0]);
226
+ return new Response("{}", { status: 200 });
227
+ }) as typeof fetch;
228
+ try {
229
+ for (const field of ["id", "memoryId"]) {
230
+ expect(
231
+ (
232
+ await call(
233
+ "memory_rate",
234
+ { [field]: agentId, useful: true, note: "n".repeat(700) },
235
+ otherId,
236
+ )
237
+ ).structuredContent?.success,
238
+ ).toBe(true);
239
+ }
240
+ const aliasResult = await call("memory_rate", { memoryId: agentId, useful: true }, otherId);
241
+ expect(aliasResult.structuredContent?.message).toBe(`Memory ${agentId} rated as useful.`);
242
+ expect(
243
+ events.slice(0, 2).map(({ memoryId, reasoning }) => ({ memoryId, reasoning })),
244
+ ).toEqual([
245
+ { memoryId: agentId, reasoning: "n".repeat(500) },
246
+ { memoryId: agentId, reasoning: "n".repeat(500) },
247
+ ]);
248
+ } finally {
249
+ globalThis.fetch = originalFetch;
250
+ }
251
+ });
252
+ test("store derives missing title and normalizes comma-separated tags", async () => {
253
+ const result = await call("memory-store", {
254
+ content: "\n Useful title \nBody",
255
+ tags: "bun, typescript, ,schema",
256
+ });
257
+ expect(result.structuredContent?.success).toBe(true);
258
+ const ids = result.structuredContent?.memoryIds as string[];
259
+ const memory = await getMemoryStore().peek(ids[0]!);
260
+ expect(memory?.name).toBe("Useful title");
261
+ expect(memory?.tags).toEqual(["bun", "typescript", "schema"]);
262
+ });
263
+ test("store keeps explicit title and array tags, bounds derived titles", async () => {
264
+ for (const name of [undefined, "Explicit title"]) {
265
+ const result = await call("memory-store", {
266
+ content: "a".repeat(300),
267
+ name,
268
+ tags: ["one,two"],
269
+ });
270
+ const memory = await getMemoryStore().peek(
271
+ (result.structuredContent?.memoryIds as string[])[0]!,
272
+ );
273
+ expect(memory?.name).toBe(name ?? "a".repeat(200));
274
+ expect(memory?.tags).toEqual(["one,two"]);
275
+ }
276
+ });
277
+ test("invalid field types and empty content/query remain invalid", () => {
278
+ expect(registered["memory-store"]!.inputSchema.safeParse({ content: "" }).success).toBe(false);
279
+ expect(
280
+ registered["memory-store"]!.inputSchema.safeParse({ content: "body", tags: [2] }).success,
281
+ ).toBe(false);
282
+ expect(registered["memory-search"]!.inputSchema.safeParse({ query: "" }).success).toBe(false);
283
+ expect(registered["memory-get"]!.inputSchema.safeParse({ id: "deadbeef" }).success).toBe(false);
284
+ });
285
+ });
286
+
287
+ test("accept-steer stores long notes and still requires task ownership", async () => {
288
+ const row = await task();
289
+ const steering = await createSteeringMessage({
290
+ taskId: row.id,
291
+ body: "adjust scope",
292
+ mode: "queue",
293
+ source: "api",
294
+ createdByKind: "system",
295
+ });
296
+ await markSteeringDelivered(steering.id, "queue");
297
+ const note = "Accepted scope. ".repeat(100);
298
+ expect(
299
+ (await call("accept-steer", { steeringMessageId: steering.id, note }, undefined, otherId))
300
+ .structuredContent?.success,
301
+ ).toBe(false);
302
+ expect(
303
+ (await call("accept-steer", { steeringMessageId: steering.id, note })).structuredContent
304
+ ?.success,
305
+ ).toBe(true);
306
+ expect((await getSteeringMessageById(steering.id))?.handledNote).toBe(note);
307
+ });
308
+
309
+ test("send-task accepts actual registry user IDs without UUID hyphens", async () => {
310
+ const user = await createUser({
311
+ name: "Schema requester",
312
+ email: "schema-requester@example.test",
313
+ });
314
+ expect(user.id).toMatch(/^[a-f0-9]{32}$/);
315
+ const result = await call("send-task", {
316
+ task: "requester preservation",
317
+ requestedByUserId: user.id,
318
+ allowDuplicate: true,
319
+ });
320
+ expect(result.structuredContent?.success).toBe(true);
321
+ const created = result.structuredContent?.task as { id: string };
322
+ expect((await getTaskById(created.id))?.requestedByUserId).toBe(user.id);
323
+ });
324
+
325
+ test("send-task rejects unknown requester IDs without creating a task", async () => {
326
+ const before = await getDbClient().get<{ count: number }>(
327
+ "SELECT COUNT(*) AS count FROM agent_tasks",
328
+ );
329
+ const result = await call("send-task", {
330
+ task: "unknown requester",
331
+ requestedByUserId: "f".repeat(32),
332
+ allowDuplicate: true,
333
+ });
334
+ expect(result.structuredContent?.success).toBe(false);
335
+ expect(result.structuredContent?.message).toContain("existing registered user");
336
+ expect(
337
+ await getDbClient().get<{ count: number }>("SELECT COUNT(*) AS count FROM agent_tasks"),
338
+ ).toEqual(before);
339
+ });
340
+
341
+ test("send-task preserves inherited attribution when explicit requester is omitted", async () => {
342
+ const { id: requesterId } = await createUser({
343
+ name: "Inherited requester",
344
+ email: "inherited-requester@example.test",
345
+ });
346
+ const parent = await createTaskExtended("historical requester", {
347
+ agentId,
348
+ requestedByUserId: requesterId,
349
+ });
350
+ await startTask(parent.id);
351
+ const result = await call(
352
+ "send-task",
353
+ { task: "inherit requester", allowDuplicate: true, offerMode: true },
354
+ parent.id,
355
+ );
356
+ expect(result.structuredContent?.success).toBe(true);
357
+ const created = result.structuredContent?.task as { id: string };
358
+ expect((await getTaskById(created.id))?.requestedByUserId).toBe(requesterId);
359
+ });
360
+
361
+ test("send-task requester format rejects arbitrary strings and hyphenated UUIDs", () => {
362
+ for (const requestedByUserId of [
363
+ "requester",
364
+ agentId,
365
+ "A".repeat(32),
366
+ "x".repeat(32),
367
+ "",
368
+ "f".repeat(31),
369
+ ]) {
370
+ expect(
371
+ sendTaskInputSchema.safeParse({ task: "invalid requester", requestedByUserId }).success,
372
+ ).toBe(false);
373
+ }
374
+ });
375
+
376
+ test("task UUIDs, parent references, and asset namespaces remain strict", () => {
377
+ expect(
378
+ getTaskDetailsInputSchema.safeParse({ taskId: "00dd3d42-0000-0000-0000-000000000000" }).success,
379
+ ).toBe(false);
380
+ expect(
381
+ sendTaskInputSchema.safeParse({ task: "invalid parent", parentTaskId: "deadbeef" }).success,
382
+ ).toBe(false);
383
+ expect(
384
+ sendTaskInputSchema.safeParse({ task: "invalid namespace", key: "probe:never" }).success,
385
+ ).toBe(false);
386
+ });