@desplega.ai/agent-swarm 2.4.4 → 2.4.5

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 (221) hide show
  1. package/dist/{acp-adapter-thq04adh.js → acp-adapter-2wdkktv6.js} +1 -1
  2. package/dist/{actions-j6ywh6x5.js → actions-c40ebcm4.js} +18 -18
  3. package/dist/{amp-adapter-k8b5xmj8.js → amp-adapter-wb67m95z.js} +2 -2
  4. package/dist/{app-rb7vc02s.js → app-353vre8g.js} +3 -3
  5. package/dist/{assistant-ftta1zjf.js → assistant-sj1njh23.js} +21 -21
  6. package/dist/{boot-reembed-srp6v6af.js → boot-reembed-b9c3az3c.js} +1 -1
  7. package/dist/{boot-reembed-xns7h5h9.js → boot-reembed-qdkvxeem.js} +1 -1
  8. package/dist/{boot-scrub-sweep-3qqrh2mz.js → boot-scrub-sweep-m3wh8eks.js} +2 -2
  9. package/dist/{boot-seeds-9ks8hce7.js → boot-seeds-j2273s54.js} +4 -4
  10. package/dist/{claude-adapter-k5ksyres.js → claude-adapter-m7q0a085.js} +2 -2
  11. package/dist/{claude-managed-adapter-h2y889ge.js → claude-managed-adapter-5ptb6tzs.js} +2 -2
  12. package/dist/{claude-managed-setup-n5a5eqks.js → claude-managed-setup-smrqk5j5.js} +3 -3
  13. package/dist/{claude-sdk-session-2j3j4kwn.js → claude-sdk-session-38xnz21e.js} +8284 -8267
  14. package/dist/{cli-cynvc9fj.js → cli-018r4jq6.js} +5 -5
  15. package/dist/{cli-9fhkkpj5.js → cli-1f3qzw6y.js} +1 -1
  16. package/dist/{cli-dqb4n629.js → cli-1sp861s4.js} +7 -7
  17. package/dist/{cli-gqvh8d8a.js → cli-1syn2xy1.js} +10 -10
  18. package/dist/{cli-qkhc5aqy.js → cli-37j04k05.js} +11 -11
  19. package/dist/{cli-1d70crsy.js → cli-3a45zcgm.js} +1 -1
  20. package/dist/{cli-t7cyef5g.js → cli-43x64hv3.js} +4 -4
  21. package/dist/{cli-hj3debgw.js → cli-4zfqgjc5.js} +2 -2
  22. package/dist/{cli-55qmbp6z.js → cli-7c9qs530.js} +1 -1
  23. package/dist/{cli-9qtjfd60.js → cli-848hxdwj.js} +1 -1
  24. package/dist/{cli-pccfmtb5.js → cli-94gwyg7m.js} +7 -7
  25. package/dist/{cli-51y09c6p.js → cli-9hstj09h.js} +5 -5
  26. package/dist/{cli-zrwpctp4.js → cli-9yqgm9nd.js} +22 -22
  27. package/dist/{cli-esxerzg7.js → cli-a92r1127.js} +1 -1
  28. package/dist/{cli-b8k7epj1.js → cli-be2161h1.js} +7 -7
  29. package/dist/{cli-xc27fwzg.js → cli-bnes4z6b.js} +2 -2
  30. package/dist/{cli-zrk6jxv8.js → cli-cxxe61f6.js} +1 -1
  31. package/dist/{cli-amyhm3gj.js → cli-dk44dxec.js} +1 -1
  32. package/dist/{cli-nc35pxb7.js → cli-eww120g8.js} +1 -0
  33. package/dist/{cli-a0kcty51.js → cli-f01h25yt.js} +1 -1
  34. package/dist/{cli-82d3nkc3.js → cli-f0g3pgvf.js} +1 -1
  35. package/dist/{cli-1ephgwnq.js → cli-ftc1g5tj.js} +2 -2
  36. package/dist/{cli-2v045xyf.js → cli-fv5hepyy.js} +3 -3
  37. package/dist/{cli-bnz0r3kc.js → cli-g35m2xwm.js} +4 -4
  38. package/dist/{cli-qje9k2nh.js → cli-g9ypd5en.js} +12 -2
  39. package/dist/{cli-w6nadkh4.js → cli-j99azg3g.js} +3 -3
  40. package/dist/{cli-er83cjbn.js → cli-k8g16gz3.js} +416 -116
  41. package/dist/{cli-4dg55wsk.js → cli-kpw04va0.js} +1 -1
  42. package/dist/{cli-3cgngyd8.js → cli-ms85309d.js} +33 -15
  43. package/dist/{cli-v3yr2gbg.js → cli-n72rg53p.js} +1471 -635
  44. package/dist/{cli-37yb7307.js → cli-p14m2cnp.js} +1 -1
  45. package/dist/{cli-2zqpkvtt.js → cli-qfxc84gh.js} +1 -1
  46. package/dist/{cli-44159k28.js → cli-qwhy2qma.js} +1 -1
  47. package/dist/{cli-2y91wnbh.js → cli-st8aw1m6.js} +221 -9
  48. package/dist/{cli-vq97g1jq.js → cli-tw4p90jb.js} +92 -192
  49. package/dist/{cli-te6b5wvg.js → cli-v0yrfmdh.js} +1 -1
  50. package/dist/{cli-xj0re5tm.js → cli-v8fjakwf.js} +1 -1
  51. package/dist/{cli-p20r8w42.js → cli-wt0jf9hm.js} +215 -80
  52. package/dist/{cli-q6r7t299.js → cli-xpa64rek.js} +33 -11
  53. package/dist/{cli-vdm20wej.js → cli-z7d29rw9.js} +2 -2
  54. package/dist/cli.js +19 -19
  55. package/dist/{codex-adapter-968xwfy7.js → codex-adapter-tn6k5mkg.js} +1 -1
  56. package/dist/{codex-session-runner-ht4z99fa.js → codex-session-runner-7vjt7jbw.js} +1 -1
  57. package/dist/{commands-pcymaeg9.js → commands-t7rz9146.js} +2 -2
  58. package/dist/{controller-fpjyk46r.js → controller-m2jasma5.js} +2 -2
  59. package/dist/{controllers-connect-mwzx1fh6.js → controllers-connect-9ftpex99.js} +19 -19
  60. package/dist/{cursor-adapter-cp4s2e2f.js → cursor-adapter-mjv79e4z.js} +1 -1
  61. package/dist/{db-xsjg9jqq.js → db-6sfrn9g3.js} +1 -1
  62. package/dist/{db-sgd12bt9.js → db-rn673r06.js} +3 -3
  63. package/dist/{dispatcher-x0jfw2c9.js → dispatcher-c155xz0p.js} +17 -17
  64. package/dist/{dsh-adapter-jrq7ma7h.js → dsh-adapter-3mmszc1x.js} +1 -1
  65. package/dist/{external-channel-8njj4h3b.js → external-channel-35k6tkv8.js} +2 -2
  66. package/dist/{grok-adapter-kkr97v2f.js → grok-adapter-tfrq7tg1.js} +2 -2
  67. package/dist/handlers-7gw64dxw.js +39 -0
  68. package/dist/{heartbeat-wd03zhn0.js → heartbeat-dyg4npkp.js} +20 -20
  69. package/dist/{hook-f0vghj42.js → hook-drspbwpe.js} +1 -1
  70. package/dist/{hook-xzj2ts2p.js → hook-x26b31bg.js} +1 -1
  71. package/dist/{http-4hwxv62p.js → http-pk972xyw.js} +355 -216
  72. package/dist/{index-ww2vznkp.js → index-arytgj5q.js} +1 -1
  73. package/dist/{index-hg0x2cdv.js → index-bgdyvcnv.js} +17 -17
  74. package/dist/{index-c4ykd0f1.js → index-dr94zbnh.js} +13348 -11611
  75. package/dist/{index-gdh8jrdm.js → index-gabbcdn2.js} +17 -17
  76. package/dist/{index-97q4xn03.js → index-jwxkkex2.js} +129 -75
  77. package/dist/index-tfb26zhy.js +41 -0
  78. package/dist/{keepalive-hrh7xnr9.js → keepalive-7yrng7px.js} +3 -3
  79. package/dist/{lead-ee8vazz2.js → lead-7e02z839.js} +29 -29
  80. package/dist/{maintenance-e8wnwnqx.js → maintenance-e6cemdqt.js} +1 -1
  81. package/dist/{node-3v3f08f7.js → node-de6akczr.js} +10 -9
  82. package/dist/{oauth-refresh-sweep-zm0yj4sj.js → oauth-refresh-sweep-yw3xdbcj.js} +2 -2
  83. package/dist/{onboard-dtwcq603.js → onboard-0a9y5yrc.js} +2 -2
  84. package/dist/{otel-impl-w3fbhpn0.js → otel-impl-trjpkfb7.js} +1 -1
  85. package/dist/{pricing-refresh-j38dehfs.js → pricing-refresh-d2b5my0y.js} +3 -3
  86. package/dist/{rbac-roles-ejxx9b35.js → rbac-roles-hey3yv9d.js} +2 -2
  87. package/dist/{rbac-roles-ae8yttq4.js → rbac-roles-nxh5ahwk.js} +1 -1
  88. package/dist/{registry-rxw10xzg.js → registry-ajhexe44.js} +1 -1
  89. package/dist/{render-v2-n16n0phe.js → render-v2-rr3p2atg.js} +3 -3
  90. package/dist/{resume-z0fgjc38.js → resume-2efkf1s1.js} +5 -5
  91. package/dist/{session-access-cv3mq6mc.js → session-access-s2mttxpw.js} +4 -4
  92. package/dist/{setup-mafhj3yt.js → setup-vypma5yt.js} +1 -1
  93. package/dist/{work-objects-8b86mgjp.js → work-objects-rvhckdkp.js} +20 -20
  94. package/dist/{worker-16s75hm5.js → worker-5snytpbd.js} +29 -29
  95. package/openapi.json +9 -2
  96. package/package.json +7 -7
  97. package/src/apps/catalog.generated.json +206 -4
  98. package/src/apps/page-validator.ts +13 -1
  99. package/src/be/chunking.ts +42 -9
  100. package/src/be/db/tasks/draft-text.ts +15 -0
  101. package/src/be/db/tasks/read.ts +14 -1
  102. package/src/be/db.ts +5 -1
  103. package/src/be/memory/index-content.ts +36 -17
  104. package/src/be/memory/providers/openai-embedding.ts +115 -61
  105. package/src/be/memory/providers/sqlite-store.ts +69 -23
  106. package/src/be/memory/reranker.ts +31 -1
  107. package/src/be/memory/search-candidates.ts +47 -0
  108. package/src/be/memory/types.ts +14 -0
  109. package/src/be/scripts/typecheck.ts +2 -0
  110. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +106 -54
  111. package/src/be/seed-scripts/index.ts +2 -2
  112. package/src/be/swarm-config-guard.ts +24 -1
  113. package/src/be/task-attachment-store.ts +30 -0
  114. package/src/extensions/catalog.generated.json +4 -4
  115. package/src/extensions/contract-types.generated.ts +1 -1
  116. package/src/extensions/contract.ts +7 -0
  117. package/src/fs/provider.ts +13 -1
  118. package/src/heartbeat/heartbeat.ts +31 -11
  119. package/src/http/fs.ts +54 -34
  120. package/src/http/mcp-body.ts +37 -0
  121. package/src/http/mcp-user.ts +3 -5
  122. package/src/http/mcp.ts +6 -7
  123. package/src/http/memory.ts +26 -15
  124. package/src/http/page-proxy.ts +61 -5
  125. package/src/http/pages-public.ts +27 -44
  126. package/src/http/pages.ts +4 -4
  127. package/src/http/script-runs.ts +2 -0
  128. package/src/http/users.ts +12 -1
  129. package/src/integrations/chat-sdk/adapters.ts +7 -3
  130. package/src/integrations/chat-sdk/attachments.ts +90 -12
  131. package/src/integrations/chat-sdk/facts.ts +120 -0
  132. package/src/integrations/chat-sdk/inbound.ts +156 -8
  133. package/src/integrations/chat-sdk/limits.ts +24 -0
  134. package/src/integrations/chat-sdk/outbound-files.ts +186 -0
  135. package/src/integrations/chat-sdk/outbound.ts +234 -4
  136. package/src/integrations/chat-sdk/relay.ts +4 -1
  137. package/src/integrations/chat-sdk/router.ts +28 -2
  138. package/src/integrations/chat-sdk/steering.ts +9 -0
  139. package/src/integrations/chat-sdk/templates.ts +14 -0
  140. package/src/integrations/chat-sdk/thread-buffer.ts +4 -0
  141. package/src/mcp-limits.ts +20 -0
  142. package/src/pages/browser-sdk.ts +8 -3
  143. package/src/pages/feedback-overlay.ts +1 -1
  144. package/src/providers/amp-adapter.ts +1 -1
  145. package/src/providers/codex-adapter.ts +106 -119
  146. package/src/providers/codex-agents-md.ts +35 -229
  147. package/src/providers/cursor-adapter.ts +1 -1
  148. package/src/realtime/transport.ts +1 -2
  149. package/src/script-workflows/workflow-ctx.ts +1 -0
  150. package/src/scripts-runtime/types/stdlib.d.ts +2 -0
  151. package/src/scripts-runtime/types/swarm-extension.d.ts +8 -0
  152. package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
  153. package/src/server-user.ts +2 -0
  154. package/src/server.ts +2 -0
  155. package/src/slack/assistant.ts +2 -2
  156. package/src/slack/files.ts +2 -0
  157. package/src/slack/handlers.ts +4 -4
  158. package/src/slack/inbound-files.ts +39 -6
  159. package/src/slack/thread-buffer.ts +2 -1
  160. package/src/tasks/task-terminal-effects.ts +21 -17
  161. package/src/tests/apps-card-grid.test.ts +197 -0
  162. package/src/tests/apps-catalog-additions.test.ts +120 -0
  163. package/src/tests/apps-tree.test.ts +129 -0
  164. package/src/tests/chat-sdk-attachments-reply.test.ts +52 -0
  165. package/src/tests/chat-sdk-config.test.ts +11 -0
  166. package/src/tests/chat-sdk-facts.test.ts +23 -0
  167. package/src/tests/chat-sdk-inbound.test.ts +558 -4
  168. package/src/tests/chat-sdk-outbound-files.test.ts +652 -0
  169. package/src/tests/codex-adapter.test.ts +167 -242
  170. package/src/tests/config-api-only-keys.test.ts +9 -0
  171. package/src/tests/cors-allowlist.test.ts +1 -1
  172. package/src/tests/extensions-example-slack-reply-gate.test.ts +262 -91
  173. package/src/tests/heartbeat-checklist.test.ts +33 -0
  174. package/src/tests/heartbeat-jev-gate-credentials.test.ts +44 -0
  175. package/src/tests/heartbeat-jev-gate.test.ts +242 -1
  176. package/src/tests/kv-page-proxy.test.ts +91 -5
  177. package/src/tests/launch-password-rejection.test.ts +2 -2
  178. package/src/tests/mcp-input-caps.test.ts +294 -0
  179. package/src/tests/memory-chunked-search-starvation.test.ts +200 -0
  180. package/src/tests/memory-embedding-input-cap.test.ts +114 -0
  181. package/src/tests/memory.test.ts +14 -0
  182. package/src/tests/page-guest-session.test.ts +19 -8
  183. package/src/tests/page-proxy-authed.test.ts +1 -1
  184. package/src/tests/page-proxy.test.ts +17 -10
  185. package/src/tests/page-session.test.ts +102 -1
  186. package/src/tests/pages-authed-mode.test.ts +82 -12
  187. package/src/tests/pages-password-mode.test.ts +5 -5
  188. package/src/tests/realtime-transport.test.ts +13 -7
  189. package/src/tests/script-runs-http.test.ts +84 -0
  190. package/src/tests/scripts-http.test.ts +10 -1
  191. package/src/tests/swarm-config-export-to-env.test.ts +22 -3
  192. package/src/tests/task-audio-transcription.test.ts +299 -0
  193. package/src/tests/task-completion-memory-chunking.test.ts +434 -0
  194. package/src/tests/task-terminal-secret-scrub.test.ts +6 -1
  195. package/src/tests/transcription.test.ts +299 -0
  196. package/src/tests/workflow-system-one-decision-providers.test.ts +98 -1
  197. package/src/tests/workflow-system-one-decision.test.ts +2 -1
  198. package/src/tools/chat-access.ts +10 -0
  199. package/src/tools/chat-post.ts +31 -11
  200. package/src/tools/chat-reply.ts +32 -10
  201. package/src/tools/memory-search.ts +3 -1
  202. package/src/tools/system-one-decide.ts +17 -4
  203. package/src/types.ts +6 -0
  204. package/src/utils/config-env-export.ts +3 -1
  205. package/src/utils/page-session.ts +133 -20
  206. package/src/utils/transcription.ts +246 -0
  207. package/src/workflows/executors/system-one-decision.ts +28 -7
  208. package/src/workflows/executors/system-one-providers.ts +39 -4
  209. package/templates/extensions/slack-reply-gate/README.md +24 -22
  210. package/templates/extensions/slack-reply-gate/hooks.ts +146 -95
  211. package/templates/extensions/slack-reply-gate/manifest.json +2 -2
  212. package/templates/skills/apps/SKILL.md +28 -6
  213. package/templates/skills/apps/content.md +28 -6
  214. package/templates/skills/chat-sdk/content.md +6 -2
  215. package/templates/skills/code-quality/content.md +2 -1
  216. package/templates/skills/heartbeat-runbook/content.md +5 -5
  217. package/templates/skills/pages/content.md +6 -4
  218. package/templates/skills/swarm-scripts/SKILL.md +2 -2
  219. package/templates/skills/swarm-scripts/content.md +2 -2
  220. package/dist/handlers-0pvee9nw.js +0 -39
  221. package/dist/index-thm0y6a8.js +0 -41
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Each task owns a fresh thread inside an isolated session subprocess.
8
8
  * Native resume remains disabled. Follow-up tasks use the context preamble.
9
- * The adapter resolves skills, maintains AGENTS.md, and reports swarm events.
9
+ * The adapter resolves skills, supplies session instructions, and reports swarm events.
10
10
  *
11
11
  * Per-session configuration travels in `thread/start` over stdin. This keeps
12
12
  * MCP credentials out of process arguments. The configuration overrides the
@@ -48,7 +48,7 @@ import {
48
48
  terminateProcessGroup,
49
49
  } from "../utils/process-group";
50
50
  import { scrubSecrets } from "../utils/secret-scrubber";
51
- import { type CodexAgentsMdHandle, writeCodexAgentsMd } from "./codex-agents-md";
51
+ import { recoverLegacyCodexAgentsMd } from "./codex-agents-md";
52
52
  import {
53
53
  CodexAppServer,
54
54
  type CodexAppServerNotification,
@@ -951,7 +951,6 @@ export interface SummarizeSessionForCodexDeps {
951
951
  export class CodexSession implements ProviderSession {
952
952
  private readonly thread: CodexThread;
953
953
  private readonly config: ProviderSessionConfig;
954
- private readonly agentsMdHandle: CodexAgentsMdHandle;
955
954
  private readonly resolvedModel: string;
956
955
  private readonly contextWindow: number;
957
956
  private readonly skillsDir: string;
@@ -991,7 +990,7 @@ export class CodexSession implements ProviderSession {
991
990
  /**
992
991
  * Result captured by `settle` but held back from `resolveCompletion` until
993
992
  * `runSession`'s `finally` block has fully cleaned up (log writer flush,
994
- * AGENTS.md cleanup, session summary). Without this, callers awaiting
993
+ * app-server close, session summary). Without this, callers awaiting
995
994
  * `waitForCompletion` would race the cleanup. Phase 3 added the session
996
995
  * summary path which materially relies on this ordering for testability.
997
996
  */
@@ -1000,7 +999,6 @@ export class CodexSession implements ProviderSession {
1000
999
  constructor(
1001
1000
  thread: CodexThread,
1002
1001
  config: ProviderSessionConfig,
1003
- agentsMdHandle: CodexAgentsMdHandle,
1004
1002
  resolvedModel: string,
1005
1003
  initialEvents: ProviderEvent[] = [],
1006
1004
  skillsDir?: string,
@@ -1008,7 +1006,6 @@ export class CodexSession implements ProviderSession {
1008
1006
  ) {
1009
1007
  this.thread = thread;
1010
1008
  this.config = config;
1011
- this.agentsMdHandle = agentsMdHandle;
1012
1009
  this.resolvedModel = resolvedModel;
1013
1010
  this.contextWindow = getCodexContextWindow(resolvedModel);
1014
1011
  // `CODEX_SKILLS_DIR` lets tests / non-Docker installs point at a custom
@@ -1739,7 +1736,7 @@ export class CodexSession implements ProviderSession {
1739
1736
  } finally {
1740
1737
  // Session-end summarization. Pure addition for codex — no behavior to
1741
1738
  // preserve. Wrapped in its own try/catch so summary failure must NOT
1742
- // block the existing log/AGENTS.md cleanup below. `SKIP_SESSION_SUMMARY` is
1739
+ // block the existing log/transport cleanup below. `SKIP_SESSION_SUMMARY` is
1743
1740
  // read with `isSessionSummarySkipped`, the same as the claude Stop hook + pi/opencode.
1744
1741
  //
1745
1742
  // Skip the summary entirely when the session was aborted. The transcript
@@ -1778,7 +1775,6 @@ export class CodexSession implements ProviderSession {
1778
1775
  } catch {
1779
1776
  // Ignore log writer cleanup failures.
1780
1777
  }
1781
- await this.agentsMdHandle.cleanup();
1782
1778
 
1783
1779
  // Resolve `waitForCompletion()` only AFTER all cleanup has finished so
1784
1780
  // downstream observers (tests + the runner's `.then(...)` chain) don't
@@ -1941,123 +1937,114 @@ export async function createInProcessCodexSession(
1941
1937
  threadFactory?: CodexThreadFactory;
1942
1938
  } = {},
1943
1939
  ): Promise<CodexSession> {
1944
- // Codex ingests per-session instructions via AGENTS.md in the cwd. Write
1945
- // (or refresh) the managed block before we spin up the thread.
1946
- const agentsMdHandle = await writeCodexAgentsMd(config.cwd, config.systemPrompt);
1947
-
1948
- try {
1949
- // Resolve the model once and thread it through. Claude shortnames map
1950
- // to Codex equivalents; everything else passes through verbatim — the
1951
- // CLI is the source of truth for valid model identifiers.
1952
- const resolvedModel = resolveCodexModel(config.model);
1953
-
1954
- // Buffer warnings emitted during config-building so they're not lost
1955
- // before `CodexSession.onEvent` attaches a listener. The buffer is
1956
- // replayed into the session's event stream right after construction
1957
- // via the `initialEvents` constructor parameter.
1958
- const preSessionEvents: ProviderEvent[] = [];
1959
- const bufferedEmit = (event: ProviderEvent) => {
1960
- preSessionEvents.push(event);
1961
- };
1940
+ await recoverLegacyCodexAgentsMd(config.cwd);
1941
+
1942
+ // Resolve the model once and thread it through. Claude shortnames map
1943
+ // to Codex equivalents; everything else passes through verbatim — the
1944
+ // CLI is the source of truth for valid model identifiers.
1945
+ const resolvedModel = resolveCodexModel(config.model);
1946
+
1947
+ // Buffer warnings emitted during config-building so they're not lost
1948
+ // before `CodexSession.onEvent` attaches a listener. The buffer is
1949
+ // replayed into the session's event stream right after construction
1950
+ // via the `initialEvents` constructor parameter.
1951
+ const preSessionEvents: ProviderEvent[] = [];
1952
+ const bufferedEmit = (event: ProviderEvent) => {
1953
+ preSessionEvents.push(event);
1954
+ };
1962
1955
 
1963
- const mergedConfig = await buildCodexConfig(config, resolvedModel, bufferedEmit);
1964
-
1965
- // Auth resolution. `codex_oauth` (in the swarm config store) wins over
1966
- // `OPENAI_API_KEY` so users can keep an OpenAI key set for embeddings
1967
- // without it shadowing their ChatGPT login. The entrypoint already runs
1968
- // this same precedence at boot — this block handles local dev (where
1969
- // the entrypoint didn't run) and any case where auth.json is stale.
1970
- const authMode = await resolveCodexAuthMode(config, bufferedEmit);
1971
-
1972
- // The app-server receives an explicit environment. Construct a
1973
- // minimal env explicitly so the spawned Codex CLI can find its binary
1974
- // (PATH) and HOME (for ~/.codex/auth.json). `OPENAI_API_KEY` is only
1975
- // forwarded when auth.json is NOT in chatgpt mode — otherwise it would
1976
- // override the OAuth login at the Codex CLI layer.
1977
- const env: Record<string, string> = {
1978
- PATH: process.env.PATH ?? "",
1979
- HOME: process.env.HOME ?? "",
1980
- ...(authMode !== "chatgpt" && process.env.OPENAI_API_KEY
1981
- ? { OPENAI_API_KEY: process.env.OPENAI_API_KEY }
1982
- : {}),
1983
- ...(process.env.NODE_EXTRA_CA_CERTS
1984
- ? { NODE_EXTRA_CA_CERTS: process.env.NODE_EXTRA_CA_CERTS }
1985
- : {}),
1986
- CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERY: CTX_MODE_NUDGE_EVERY,
1987
- ...(config.env ?? {}),
1988
- // Gated cross-service OTel linking: when SWARM_ENABLE_HARNESS_OTEL (or
1989
- // the deprecated SWARM_ENABLE_CLAUDE_CODE_OTEL alias) is on, inject
1990
- // TRACEPARENT from the active worker span so Codex's spans nest under
1991
- // our worker.session trace. Codex's Rust OTEL SDK reads W3C trace
1992
- // context from the env via the default tracecontext propagator.
1993
- // Returns {} (no-op) when off; spread last so the computed value wins.
1994
- ...buildOtelTraceparentEnv(config.env ?? process.env),
1995
- SWARM_CODEX_APP_SERVER: "1",
1996
- };
1997
- if (authMode === "chatgpt") delete env.OPENAI_API_KEY;
1956
+ const mergedConfig = await buildCodexConfig(config, resolvedModel, bufferedEmit);
1957
+
1958
+ // Auth resolution. `codex_oauth` (in the swarm config store) wins over
1959
+ // `OPENAI_API_KEY` so users can keep an OpenAI key set for embeddings
1960
+ // without it shadowing their ChatGPT login. The entrypoint already runs
1961
+ // this same precedence at boot — this block handles local dev (where
1962
+ // the entrypoint didn't run) and any case where auth.json is stale.
1963
+ const authMode = await resolveCodexAuthMode(config, bufferedEmit);
1964
+
1965
+ // The app-server receives an explicit environment. Construct a
1966
+ // minimal env explicitly so the spawned Codex CLI can find its binary
1967
+ // (PATH) and HOME (for ~/.codex/auth.json). `OPENAI_API_KEY` is only
1968
+ // forwarded when auth.json is NOT in chatgpt mode — otherwise it would
1969
+ // override the OAuth login at the Codex CLI layer.
1970
+ const env: Record<string, string> = {
1971
+ PATH: process.env.PATH ?? "",
1972
+ HOME: process.env.HOME ?? "",
1973
+ ...(authMode !== "chatgpt" && process.env.OPENAI_API_KEY
1974
+ ? { OPENAI_API_KEY: process.env.OPENAI_API_KEY }
1975
+ : {}),
1976
+ ...(process.env.NODE_EXTRA_CA_CERTS
1977
+ ? { NODE_EXTRA_CA_CERTS: process.env.NODE_EXTRA_CA_CERTS }
1978
+ : {}),
1979
+ CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERY: CTX_MODE_NUDGE_EVERY,
1980
+ ...(config.env ?? {}),
1981
+ // Gated cross-service OTel linking: when SWARM_ENABLE_HARNESS_OTEL (or
1982
+ // the deprecated SWARM_ENABLE_CLAUDE_CODE_OTEL alias) is on, inject
1983
+ // TRACEPARENT from the active worker span so Codex's spans nest under
1984
+ // our worker.session trace. Codex's Rust OTEL SDK reads W3C trace
1985
+ // context from the env via the default tracecontext propagator.
1986
+ // Returns {} (no-op) when off; spread last so the computed value wins.
1987
+ ...buildOtelTraceparentEnv(config.env ?? process.env),
1988
+ SWARM_CODEX_APP_SERVER: "1",
1989
+ };
1990
+ if (authMode === "chatgpt") delete env.OPENAI_API_KEY;
1998
1991
 
1999
- const codexPathOverride = process.env.CODEX_PATH_OVERRIDE;
1992
+ const codexPathOverride = process.env.CODEX_PATH_OVERRIDE;
2000
1993
 
2001
- // Native resume is deprecated. Follow-up continuity is delivered via the
2002
- // context preamble (see src/commands/context-preamble.ts). Any stray
2003
- // resumeSessionId is logged and ignored — we always start a fresh thread.
2004
- if (config.resumeSessionId) {
2005
- console.warn(
2006
- "[codex-adapter] resumeSessionId ignored — native resume is disabled by deprecation plan",
2007
- );
2008
- }
2009
- let thread: CodexThread;
2010
- let liveSession: CodexSession | undefined;
2011
- if (opts.threadFactory) {
2012
- thread = await opts.threadFactory({
2013
- config,
2014
- resolvedModel,
2015
- env,
2016
- codexConfig: mergedConfig,
2017
- });
2018
- } else {
2019
- const appServerFactory = opts.appServerFactory ?? ((options) => new CodexAppServer(options));
2020
- const client = appServerFactory({
2021
- ...(codexPathOverride ? { codexPath: codexPathOverride } : {}),
2022
- env,
2023
- onStderr: (line) => {
2024
- if (liveSession) liveSession.emitTransportStderr(line);
2025
- else bufferedEmit({ type: "raw_stderr", content: `${line}\n` });
2026
- },
2027
- });
2028
- try {
2029
- const response = await client.request<{ thread: { id: string } }>("thread/start", {
2030
- model: resolvedModel,
2031
- cwd: config.cwd,
2032
- approvalPolicy: "never",
2033
- sandbox: "danger-full-access",
2034
- config: mergedConfig,
2035
- ephemeral: true,
2036
- });
2037
- thread = new AppServerCodexThread(client, response.thread.id);
2038
- } catch (error) {
2039
- await client.close();
2040
- throw error;
2041
- }
2042
- }
2043
-
2044
- const session = new CodexSession(
2045
- thread,
1994
+ // Native resume is deprecated. Follow-up continuity is delivered via the
1995
+ // context preamble (see src/commands/context-preamble.ts). Any stray
1996
+ // resumeSessionId is logged and ignored — we always start a fresh thread.
1997
+ if (config.resumeSessionId) {
1998
+ console.warn(
1999
+ "[codex-adapter] resumeSessionId ignored — native resume is disabled by deprecation plan",
2000
+ );
2001
+ }
2002
+ let thread: CodexThread;
2003
+ let liveSession: CodexSession | undefined;
2004
+ if (opts.threadFactory) {
2005
+ thread = await opts.threadFactory({
2046
2006
  config,
2047
- agentsMdHandle,
2048
2007
  resolvedModel,
2049
- preSessionEvents,
2050
- opts.skillsDir,
2051
- opts.summarizeDeps ?? {},
2052
- );
2053
- liveSession = session;
2054
- return session;
2055
- } catch (err) {
2056
- // If we failed to construct the thread, clean up the managed AGENTS.md
2057
- // block so we don't leak state on the filesystem.
2058
- await agentsMdHandle.cleanup();
2059
- throw err;
2008
+ env,
2009
+ codexConfig: mergedConfig,
2010
+ });
2011
+ } else {
2012
+ const appServerFactory = opts.appServerFactory ?? ((options) => new CodexAppServer(options));
2013
+ const client = appServerFactory({
2014
+ ...(codexPathOverride ? { codexPath: codexPathOverride } : {}),
2015
+ env,
2016
+ onStderr: (line) => {
2017
+ if (liveSession) liveSession.emitTransportStderr(line);
2018
+ else bufferedEmit({ type: "raw_stderr", content: `${line}\n` });
2019
+ },
2020
+ });
2021
+ try {
2022
+ const response = await client.request<{ thread: { id: string } }>("thread/start", {
2023
+ model: resolvedModel,
2024
+ cwd: config.cwd,
2025
+ developerInstructions: config.systemPrompt,
2026
+ approvalPolicy: "never",
2027
+ sandbox: "danger-full-access",
2028
+ config: mergedConfig,
2029
+ ephemeral: true,
2030
+ });
2031
+ thread = new AppServerCodexThread(client, response.thread.id);
2032
+ } catch (error) {
2033
+ await client.close();
2034
+ throw error;
2035
+ }
2060
2036
  }
2037
+
2038
+ const session = new CodexSession(
2039
+ thread,
2040
+ config,
2041
+ resolvedModel,
2042
+ preSessionEvents,
2043
+ opts.skillsDir,
2044
+ opts.summarizeDeps ?? {},
2045
+ );
2046
+ liveSession = session;
2047
+ return session;
2061
2048
  }
2062
2049
 
2063
2050
  /**
@@ -1,242 +1,48 @@
1
- /**
2
- * Codex AGENTS.md helper.
3
- *
4
- * Codex reads `AGENTS.md` from the session cwd at startup and uses its contents
5
- * as the agent's base instructions. There is no `ThreadStartParams.systemPrompt`
6
- * / `developerInstructions` equivalent wired through the SDK 0.118 public
7
- * surface, so the only way to inject our per-session `config.systemPrompt` is
8
- * to write it into `AGENTS.md` before `thread.runStreamed()` is called.
9
- *
10
- * To avoid stomping on any existing `AGENTS.md` content (or any `CLAUDE.md`
11
- * that the repo already ships with), we manage a delimited block:
12
- *
13
- * <swarm_system_prompt>
14
- * ...our prompt...
15
- * </swarm_system_prompt>
16
- *
17
- * Rules:
18
- * - No existing `AGENTS.md`:
19
- * - If `CLAUDE.md` exists in cwd, prepend the block to the CLAUDE.md
20
- * contents so Codex sees our prompt plus the repo's existing Claude
21
- * instructions.
22
- * - Otherwise, write just the block.
23
- * - Mark `createdFresh: true` so cleanup removes the file entirely.
24
- * - `AGENTS.md` is a symlink (many repos ship `AGENTS.md -> CLAUDE.md`):
25
- * never write through it. Swap in a real file holding the block plus the
26
- * link target's contents, and restore the symlink on cleanup. The target is
27
- * only read, and edits only carried back to it, when its real path is a
28
- * regular file inside the cwd. A crash-recovery record (cwd + link target)
29
- * is kept OUTSIDE the repo, so the next session can restore the link if
30
- * this one crashed before cleanup. Repo content never names a path.
31
- * - Existing `AGENTS.md` already contains the block: replace the block with
32
- * the fresh contents.
33
- * - Existing `AGENTS.md` without the block: prepend the block.
34
- *
35
- * Cleanup mirrors the creation logic — if we created the file fresh, delete
36
- * it; if we swapped out a symlink, restore it; otherwise re-read the current
37
- * AGENTS.md and strip just the managed block so anything the agent appended
38
- * during the session is preserved.
39
- *
40
- * The helper is deliberately isolated from the adapter so it can be
41
- * unit-tested without pulling in the Codex SDK.
42
- */
43
-
44
- import { lstat, mkdir, readlink, realpath, symlink, unlink } from "node:fs/promises";
1
+ /** Recover AGENTS.md changes left by sessions from before native instructions. */
2
+ import { lstat, realpath, symlink, unlink } from "node:fs/promises";
45
3
  import { homedir } from "node:os";
46
- import { dirname, join, resolve, sep } from "node:path";
4
+ import { join, resolve, sep } from "node:path";
47
5
 
48
- const BLOCK_OPEN = "<swarm_system_prompt>";
49
- const BLOCK_CLOSE = "</swarm_system_prompt>";
50
6
  const BLOCK_REGEX = /<swarm_system_prompt>[\s\S]*?<\/swarm_system_prompt>\n?/;
51
- const DEFAULT_STATE_DIR = join(homedir(), ".cache", "agent-swarm", "codex-agents-md");
52
-
53
- export interface CodexAgentsMdHandle {
54
- cleanup(): Promise<void>;
55
- }
56
-
57
- export interface CodexAgentsMdOptions {
58
- /** Where symlink crash-recovery records live. Must be outside any repo. */
59
- stateDir?: string;
60
- }
61
-
62
- interface SymlinkRecord {
63
- cwd: string;
64
- linkTarget: string;
65
- }
66
-
67
- const NOOP_HANDLE: CodexAgentsMdHandle = {
68
- cleanup: async () => {},
69
- };
70
-
71
- async function readIfExists(path: string): Promise<string | null> {
72
- const file = Bun.file(path);
73
- return (await file.exists()) ? await file.text() : null;
74
- }
75
-
76
- function recordPathFor(stateDir: string, realCwd: string): string {
77
- const key = new Bun.CryptoHasher("sha256").update(realCwd).digest("hex");
78
- return join(stateDir, `${key}.json`);
79
- }
80
-
81
- async function readRecord(recordPath: string, realCwd: string): Promise<SymlinkRecord | null> {
82
- try {
83
- const record = JSON.parse(await Bun.file(recordPath).text()) as Partial<SymlinkRecord>;
84
- if (record.cwd === realCwd && typeof record.linkTarget === "string") {
85
- return { cwd: record.cwd, linkTarget: record.linkTarget };
86
- }
87
- } catch {
88
- // Missing or unreadable record: nothing to recover.
89
- }
90
- return null;
91
- }
7
+ const STATE_DIR = join(homedir(), ".cache", "agent-swarm", "codex-agents-md");
92
8
 
93
9
  /**
94
- * Real path of the symlink target when it is a regular file inside the cwd,
95
- * otherwise null. Out-of-tree and dangling targets are never read or written.
10
+ * Strip a legacy prompt block and restore a swapped symlink from the old
11
+ * out-of-repo recovery record. New sessions never create blocks or records.
96
12
  */
97
- async function inTreeTarget(
98
- realCwd: string,
99
- agentsMdPath: string,
100
- linkTarget: string,
101
- ): Promise<string | null> {
102
- const target = await realpath(resolve(dirname(agentsMdPath), linkTarget)).catch(() => null);
103
- const prefix = realCwd.endsWith(sep) ? realCwd : `${realCwd}${sep}`;
104
- if (target === null || !target.startsWith(prefix)) {
105
- return null;
106
- }
107
- const stat = await lstat(target).catch(() => null);
108
- return stat?.isFile() ? target : null;
109
- }
110
-
111
- /**
112
- * Put the `AGENTS.md -> linkTarget` symlink back in place of our swapped-in
113
- * real file. Edits the agent made to AGENTS.md during the session would have
114
- * landed in the link target had the link been in place, so carry them over
115
- * (block stripped) when the target is in-tree and its contents differ.
116
- */
117
- async function restoreSymlink(
118
- agentsMdPath: string,
119
- realCwd: string,
120
- linkTarget: string,
121
- ): Promise<void> {
13
+ export async function recoverLegacyCodexAgentsMd(cwd: string | undefined): Promise<void> {
14
+ if (!cwd) return;
15
+ const realCwd = await realpath(cwd).catch(() => resolve(cwd));
16
+ const agentsMdPath = join(realCwd, "AGENTS.md");
17
+ const key = new Bun.CryptoHasher("sha256").update(realCwd).digest("hex");
18
+ const recordPath = join(STATE_DIR, `${key}.json`);
19
+ const record = (await Bun.file(recordPath)
20
+ .json()
21
+ .catch(() => null)) as { cwd?: unknown; linkTarget?: unknown } | null;
22
+ const linkTarget =
23
+ record?.cwd === realCwd && typeof record.linkTarget === "string" ? record.linkTarget : null;
122
24
  const stat = await lstat(agentsMdPath).catch(() => null);
123
25
  const current = stat?.isFile() ? await Bun.file(agentsMdPath).text() : null;
124
- await unlink(agentsMdPath).catch(() => {});
125
- await symlink(linkTarget, agentsMdPath);
126
- if (current === null) {
127
- return;
128
- }
129
- const target = await inTreeTarget(realCwd, agentsMdPath, linkTarget);
130
- if (target === null) {
131
- return;
132
- }
133
- const stripped = current.replace(BLOCK_REGEX, "");
134
- if (stripped !== (await Bun.file(target).text())) {
135
- await Bun.write(target, stripped);
136
- }
137
- }
138
-
139
- /**
140
- * Write (or refresh) a managed `<swarm_system_prompt>` block inside
141
- * `${cwd}/AGENTS.md`. Returns a handle whose `cleanup()` reverses the edit.
142
- *
143
- * No-ops gracefully when `cwd` or `systemPrompt` is falsy.
144
- */
145
- export async function writeCodexAgentsMd(
146
- cwd: string | undefined,
147
- systemPrompt: string | undefined,
148
- options: CodexAgentsMdOptions = {},
149
- ): Promise<CodexAgentsMdHandle> {
150
- if (!cwd || !systemPrompt) {
151
- return NOOP_HANDLE;
152
- }
153
-
154
- const agentsMdPath = join(cwd, "AGENTS.md");
155
- const claudeMdPath = join(cwd, "CLAUDE.md");
156
- const stateDir = options.stateDir ?? DEFAULT_STATE_DIR;
157
- const realCwd = await realpath(cwd).catch(() => resolve(cwd));
158
- const recordPath = recordPathFor(stateDir, realCwd);
159
- const block = `${BLOCK_OPEN}\n${systemPrompt}\n${BLOCK_CLOSE}`;
160
-
161
- let stat = await lstat(agentsMdPath).catch(() => null);
162
-
163
- // A previous session swapped a symlink out and crashed before cleanup. Only
164
- // our own out-of-repo record can trigger a restore, and only while AGENTS.md
165
- // is still the real file holding our block.
166
- const record = await readRecord(recordPath, realCwd);
167
- if (record) {
168
- if (stat?.isFile() && BLOCK_REGEX.test(await Bun.file(agentsMdPath).text())) {
169
- await restoreSymlink(agentsMdPath, realCwd, record.linkTarget);
170
- stat = await lstat(agentsMdPath);
171
- }
172
- await unlink(recordPath).catch(() => {});
173
- }
174
-
175
- if (stat?.isSymbolicLink()) {
176
- const linkTarget = await readlink(agentsMdPath);
177
- const target = await inTreeTarget(realCwd, agentsMdPath, linkTarget);
178
- const targetContent = target !== null ? await Bun.file(target).text() : "";
179
- // Record before swapping, so a crash at any later point stays recoverable.
180
- await mkdir(stateDir, { recursive: true });
181
- await Bun.write(recordPath, JSON.stringify({ cwd: realCwd, linkTarget }));
182
- // Never write through the link: replace it with a real file for the session.
183
- await unlink(agentsMdPath);
184
- await Bun.write(agentsMdPath, `${block}\n${targetContent.replace(BLOCK_REGEX, "")}`);
185
-
186
- return {
187
- async cleanup(): Promise<void> {
188
- try {
189
- await restoreSymlink(agentsMdPath, realCwd, linkTarget);
190
- await unlink(recordPath).catch(() => {});
191
- } catch {
192
- // Cleanup is best-effort; the record lets the next session finish
193
- // the restore.
26
+ const stripped = current === null ? null : current.replace(BLOCK_REGEX, "");
27
+
28
+ if (linkTarget !== null && (!stat || (stripped !== null && stripped !== current))) {
29
+ // Carry edits back before removing the recovery copy, so a failed write
30
+ // leaves both the copy and the record available for the next attempt.
31
+ if (stripped !== null) {
32
+ const target = await realpath(resolve(realCwd, linkTarget)).catch(() => null);
33
+ const prefix = realCwd.endsWith(sep) ? realCwd : `${realCwd}${sep}`;
34
+ if (target !== null && target !== agentsMdPath && target.startsWith(prefix)) {
35
+ const targetStat = await lstat(target);
36
+ if (targetStat.isFile() && stripped !== (await Bun.file(target).text())) {
37
+ await Bun.write(target, stripped);
194
38
  }
195
- },
196
- };
197
- }
198
-
199
- let createdFresh = false;
200
- let newContent: string;
201
-
202
- if (!stat) {
203
- // No AGENTS.md yet — prefer CLAUDE.md content as a base if present.
204
- const claudeContent = await readIfExists(claudeMdPath);
205
- newContent = claudeContent !== null ? `${block}\n\n${claudeContent}` : `${block}\n`;
206
- createdFresh = true;
207
- } else {
208
- const existingContent = await Bun.file(agentsMdPath).text();
209
- if (BLOCK_REGEX.test(existingContent)) {
210
- // Replace the stale block in place.
211
- newContent = existingContent.replace(BLOCK_REGEX, `${block}\n`);
212
- } else {
213
- // Prepend the block with a single newline so cleanup's strip restores
214
- // the original bytes exactly.
215
- newContent = `${block}\n${existingContent}`;
39
+ }
216
40
  }
41
+ if (stat) await unlink(agentsMdPath);
42
+ await symlink(linkTarget, agentsMdPath);
43
+ } else if (stripped !== null && stripped !== current) {
44
+ await Bun.write(agentsMdPath, stripped);
217
45
  }
218
46
 
219
- await Bun.write(agentsMdPath, newContent);
220
-
221
- return {
222
- async cleanup(): Promise<void> {
223
- try {
224
- if (createdFresh) {
225
- // Best-effort delete — ignore errors so we never throw from finally.
226
- await Bun.$`rm -f ${agentsMdPath}`.quiet().nothrow();
227
- return;
228
- }
229
- const currentFile = Bun.file(agentsMdPath);
230
- if (!(await currentFile.exists())) {
231
- return;
232
- }
233
- const currentContent = await currentFile.text();
234
- const stripped = currentContent.replace(BLOCK_REGEX, "");
235
- await Bun.write(agentsMdPath, stripped);
236
- } catch {
237
- // Cleanup is best-effort; swallow errors so we don't mask the real
238
- // completion/failure path.
239
- }
240
- },
241
- };
47
+ if (linkTarget !== null) await unlink(recordPath);
242
48
  }
@@ -35,7 +35,7 @@ import type {
35
35
  } from "./types";
36
36
 
37
37
  /** Pinned in package.json and Dockerfile.worker; bump all three together. */
38
- export const CURSOR_SDK_VERSION = "1.0.38";
38
+ export const CURSOR_SDK_VERSION = "1.0.40";
39
39
 
40
40
  /** MCP server name; Cursor reports its tools as `mcp` calls with this `providerIdentifier`. */
41
41
  const CURSOR_MCP_SERVER_NAME = "agent-swarm";
@@ -159,8 +159,7 @@ async function authenticate(req: IncomingMessage): Promise<AuthenticatedConnecti
159
159
  throw new Error("Invalid page origin");
160
160
  const page = await getPage(pageId);
161
161
  if (!page) throw new Error("Page not found");
162
- const cookie = await extractAndVerifyCookie(req);
163
- const session = cookie?.pageId === pageId ? cookie : null;
162
+ const session = await extractAndVerifyCookie(req, pageId);
164
163
  if (!session && page.authMode !== "public") throw new Error("Page session required");
165
164
  if (session?.uid && (await findUserById(session.uid))?.status !== "active")
166
165
  throw new Error("Viewer is inactive");
@@ -36,6 +36,7 @@ type AgentTaskConfig = {
36
36
  routingNote?: string;
37
37
  tags?: string[];
38
38
  priority?: number;
39
+ dependsOn?: string[];
39
40
  offerMode?: boolean;
40
41
  dir?: string;
41
42
  vcsRepo?: string;
@@ -53,6 +53,8 @@ declare module "swarm-sdk" {
53
53
  routingNote?: string;
54
54
  tags?: string[];
55
55
  priority?: number;
56
+ /** Task IDs that must complete before this task starts. */
57
+ dependsOn?: string[];
56
58
  offerMode?: boolean;
57
59
  dir?: string;
58
60
  vcsRepo?: string;
@@ -67,6 +67,7 @@ declare module "swarm-extension" {
67
67
  sourceTaskId?: string | undefined;
68
68
  outputSchema?: Record<string, unknown> | undefined;
69
69
  inheritParentOutputSchema?: boolean | undefined;
70
+ inheritParentRequester?: boolean | undefined;
70
71
  inheritParentRoutingAffinity?: boolean | undefined;
71
72
  inheritParentFollowUpConfig?: boolean | undefined;
72
73
  followUpConfig?:
@@ -485,8 +486,15 @@ declare module "swarm-extension" {
485
486
  userName?: string;
486
487
  /** The buffered messages, joined. */
487
488
  text: string;
489
+ /** True for an @mention and for a reply to one of the bot's messages. */
488
490
  botMentioned: boolean;
489
491
  isDirectMessage: boolean;
492
+ /** Platform chat type, such as Telegram `private`, `group`, `supergroup` or `channel`. */
493
+ chatType?: string;
494
+ /** Set for a Telegram supergroup with topics. */
495
+ isForum?: boolean;
496
+ /** A buffered message replies to one of the bot's own messages. */
497
+ replyToBot: boolean;
490
498
  /** Set when earlier swarm tasks exist in this thread. */
491
499
  threadContext?: {
492
500
  adapter: string;
@@ -35,6 +35,8 @@ declare module "swarm-sdk" {
35
35
  routingNote?: string;
36
36
  tags?: string[];
37
37
  priority?: number;
38
+ /** Task IDs that must complete before this task starts. */
39
+ dependsOn?: string[];
38
40
  offerMode?: boolean;
39
41
  dir?: string;
40
42
  vcsRepo?: string;
@@ -4,6 +4,7 @@ import * as z from "zod";
4
4
  import pkg from "../package.json";
5
5
  import { enqueueAdmissionRow } from "./be/rbac-audit";
6
6
  import { getUserGrant } from "./be/rbac-roles";
7
+ import { MCP_MAX_TOOL_INPUT_ELEMENTS } from "./mcp-limits";
7
8
  import {
8
9
  type AdmissionRbac,
9
10
  decideToolAdmission,
@@ -107,6 +108,7 @@ export function createUserServer(user: User): McpServer {
107
108
  capabilities: {
108
109
  logging: {},
109
110
  },
111
+ maxToolInputElements: MCP_MAX_TOOL_INPUT_ELEMENTS,
110
112
  },
111
113
  );
112
114