@desplega.ai/agent-swarm 1.152.1 → 1.153.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 (228) hide show
  1. package/README.md +2 -1
  2. package/dist/{acp-adapter-4s7q25ft.js → acp-adapter-ahmjxhek.js} +4 -4
  3. package/dist/{actions-cs6rhmvc.js → actions-jzrfpkmn.js} +30 -26
  4. package/dist/{anthropic-messages-2y8nktar.js → anthropic-messages-9vqsbznt.js} +112 -84
  5. package/dist/{app-j54aq0dj.js → app-03na2vwb.js} +9 -6
  6. package/dist/{assistant-w1pgn7vf.js → assistant-hfkxvhgt.js} +33 -29
  7. package/dist/{azure-openai-responses-f38x2avq.js → azure-openai-responses-04qwdwfq.js} +27 -13
  8. package/dist/{boot-reembed-smy30z32.js → boot-reembed-c98xrad3.js} +8 -6
  9. package/dist/{boot-reembed-42vgkrrj.js → boot-reembed-zt3qm9xn.js} +9 -7
  10. package/dist/{boot-scrub-logs-b6s72fsw.js → boot-scrub-logs-jcqd8ksj.js} +6 -4
  11. package/dist/{claude-adapter-fh7rmcj9.js → claude-adapter-mgryg8g3.js} +17 -15
  12. package/dist/{claude-managed-adapter-dsmkcrjy.js → claude-managed-adapter-ea2aga6w.js} +3 -3
  13. package/dist/{claude-sdk-session-xhynatn9.js → claude-sdk-session-jjf4zss1.js} +17 -15
  14. package/dist/{cli-r2fpe7cx.js → cli-00w2c66p.js} +61 -55
  15. package/dist/{cli-fm4ytsdk.js → cli-0d9nbqxf.js} +1 -1
  16. package/dist/cli-0x5qzvkg.js +4279 -0
  17. package/dist/{cli-d3p82erb.js → cli-18amys3y.js} +9 -9
  18. package/dist/{cli-wbyk465z.js → cli-1hwnb4hc.js} +1 -1
  19. package/dist/{cli-sakxd2rg.js → cli-3bcmp015.js} +5 -5
  20. package/dist/cli-3er6gcsn.js +1849 -0
  21. package/dist/{cli-c4eeb5q2.js → cli-5b6b62tz.js} +1 -1
  22. package/dist/{cli-sqmh7jj1.js → cli-5mbmvxnn.js} +5 -5
  23. package/dist/{cli-v9sk4dzw.js → cli-5taqtpze.js} +235 -8
  24. package/dist/{cli-dsyg66vt.js → cli-6ks9xf1n.js} +7 -5
  25. package/dist/cli-7g9s78jf.js +2827 -0
  26. package/dist/{cli-jtvs5m8q.js → cli-7pnz2v6s.js} +2 -2
  27. package/dist/{cli-jytf5mw9.js → cli-83wkg402.js} +2 -2
  28. package/dist/{cli-3pft2z6r.js → cli-a9scg8sv.js} +12 -10
  29. package/dist/{cli-4yc5d8fk.js → cli-avkd6mqv.js} +50 -0
  30. package/dist/{cli-ht5rs63n.js → cli-b2g6mzjc.js} +15 -6
  31. package/dist/{cli-3g61md6v.js → cli-bw0dfwkr.js} +1 -1
  32. package/dist/{cli-8wrympd7.js → cli-bzr0ftht.js} +26 -7
  33. package/dist/{cli-ks8a1cgq.js → cli-cdq08v3r.js} +2 -2
  34. package/dist/cli-cgj4hc12.js +18902 -0
  35. package/dist/cli-d6hk10xx.js +14075 -0
  36. package/dist/{cli-bdt7w021.js → cli-djvxhnc4.js} +31 -3324
  37. package/dist/{cli-s25avqg9.js → cli-e781c6f9.js} +1 -0
  38. package/dist/{cli-b4dyetqt.js → cli-eazstkad.js} +2 -2
  39. package/dist/{cli-s2qjdd6z.js → cli-eetrvv91.js} +1 -1
  40. package/dist/cli-h38x0qhg.js +6912 -0
  41. package/dist/{cli-9xvdm3bx.js → cli-h60qfasa.js} +742 -278
  42. package/dist/{pi-mono-adapter-jcqt71cg.js → cli-ja7mrygn.js} +67343 -101083
  43. package/dist/{cli-j8dggbcr.js → cli-k0g1sjzm.js} +23 -31
  44. package/dist/{cli-y8tayczk.js → cli-m04pt18h.js} +2 -2
  45. package/dist/{cli-rgvp6r2x.js → cli-mcnhgnn1.js} +193 -4
  46. package/dist/{cli-j4nv2wqy.js → cli-mzgyhxbe.js} +10 -4
  47. package/dist/{cli-wfve107r.js → cli-n0q9b7j4.js} +46 -1870
  48. package/dist/cli-n7h1kqpf.js +29 -0
  49. package/dist/{cli-ng4q6a9t.js → cli-nq0kgyxe.js} +7 -7
  50. package/dist/{cli-72tgtzr1.js → cli-qrqhc31j.js} +151 -125
  51. package/dist/{cli-vbk025fm.js → cli-r8tdds6s.js} +1 -1
  52. package/dist/{cli-ch38q5z4.js → cli-rj7de4b2.js} +6788 -2541
  53. package/dist/{cli-ftdjz1mk.js → cli-s5s1f2yn.js} +56 -40
  54. package/dist/{cli-kj76p1ez.js → cli-scwq51gn.js} +13 -7
  55. package/dist/{cli-7bwaerjt.js → cli-tdh4hjr2.js} +3 -2
  56. package/dist/{cli-c2mk1kgf.js → cli-v4en75c5.js} +8 -1
  57. package/dist/{cli-kf8qp2vf.js → cli-vw03hg5t.js} +3 -2
  58. package/dist/{cli-gfez0qmk.js → cli-ybpp1srm.js} +6 -6
  59. package/dist/cli-yn0a417a.js +4069 -0
  60. package/dist/{cli-gxf2ebyz.js → cli-ysc1kbet.js} +2 -2
  61. package/dist/{cli-zfzznn7c.js → cli-ytxs7mn1.js} +26 -2
  62. package/dist/cli-zcsyfw2z.js +3335 -0
  63. package/dist/{cli-4j8k1m4w.js → cli-zt1227k1.js} +1162 -192
  64. package/dist/cli-zy5w9gpt.js +230 -0
  65. package/dist/cli.js +26 -34
  66. package/dist/{codex-adapter-k6yq95sn.js → codex-adapter-8548daap.js} +12 -11
  67. package/dist/{codex-session-runner-3rp84pk8.js → codex-session-runner-2dad8p4x.js} +12 -11
  68. package/dist/{commands-pbr5jzy5.js → commands-355dbpa8.js} +6 -4
  69. package/dist/{db-z0xaa59a.js → db-4jjvwz1s.js} +6 -4
  70. package/dist/{dispatcher-pwpgsj0x.js → dispatcher-ca590nkz.js} +29 -25
  71. package/dist/dsh-adapter-rgvj6ykp.js +19 -0
  72. package/dist/{e2b-413b8t4v.js → e2b-93x1k0j5.js} +6 -7
  73. package/dist/{google-generative-ai-ndjmj5fa.js → google-generative-ai-xtw7tggr.js} +33 -73
  74. package/dist/{google-vertex-fr665tqs.js → google-vertex-zdqpms6n.js} +38 -68
  75. package/dist/{handlers-5mzk4mbg.js → handlers-2y6dqpvf.js} +32 -28
  76. package/dist/{hook-8bz33zee.js → hook-0s8knepr.js} +11 -9
  77. package/dist/{hook-kbh84zb1.js → hook-k0gec254.js} +12 -10
  78. package/dist/{http-pppxg5vr.js → http-jp2nb4zw.js} +300 -237
  79. package/dist/{index-k1hq1qt6.js → index-0wzg3sp3.js} +39 -24
  80. package/dist/{index-68bd9b5f.js → index-3mq6ce93.js} +29 -25
  81. package/dist/{index-z0zwjtkn.js → index-42s7eezq.js} +29 -25
  82. package/dist/{cli-pdz9qde4.js → index-9ch1n761.js} +10 -11
  83. package/dist/{index-y0te09pn.js → index-b56xkbc7.js} +8 -6
  84. package/dist/{index-66xz5e9j.js → index-h5gs261z.js} +521 -9
  85. package/dist/{index-2sp0384z.js → index-kkeftv5h.js} +738 -21
  86. package/dist/{index-mjpxsr6k.js → index-s37dbevy.js} +30 -26
  87. package/dist/index-w097eg0x.js +5 -0
  88. package/dist/jiti-loader-b4837mtm.js +34 -0
  89. package/dist/jiti-static-loader-q2g5ds0y.js +31267 -0
  90. package/dist/{keepalive-xvmwnh1d.js → keepalive-9pfw7t02.js} +8 -6
  91. package/dist/{lead-pf8nvxt8.js → lead-7vcmcdq4.js} +44 -39
  92. package/dist/{maintenance-jk4q0wgg.js → maintenance-x277jbpd.js} +9 -7
  93. package/dist/{mistral-conversations-qhr84ph3.js → mistral-conversations-zx0kn554.js} +24 -18
  94. package/dist/{node-x1g4v0pq.js → node-5qtycf6a.js} +16 -11
  95. package/dist/{oauth-refresh-sweep-6207x9sj.js → oauth-refresh-sweep-5fwa7th2.js} +7 -5
  96. package/dist/{onboard-b0csgm0q.js → onboard-17sv0f51.js} +103 -35
  97. package/dist/{openai-codex-responses-wy6a43wd.js → openai-codex-responses-4vjqzvjp.js} +33 -27
  98. package/dist/{openai-completions-32rdwmze.js → openai-completions-pxsmym7g.js} +52 -64
  99. package/dist/{openai-responses-6xwf7bzg.js → openai-responses-tmn0rqp0.js} +23 -24
  100. package/dist/{opencode-adapter-28axqa09.js → opencode-adapter-qbmv4tpy.js} +6 -6
  101. package/dist/{otel-impl-b6rfehth.js → otel-impl-srjy4be4.js} +4 -4
  102. package/dist/{pi-messages-b1mbr6gp.js → pi-messages-txpjj39q.js} +3 -3
  103. package/dist/pi-mono-adapter-946a0sdd.js +1316 -0
  104. package/dist/{pricing-refresh-4e201wtd.js → pricing-refresh-vetcm563.js} +8 -6
  105. package/dist/{rbac-roles-mkc5n5qa.js → rbac-roles-cfzgr6r1.js} +8 -6
  106. package/dist/{rbac-roles-zp6e4geh.js → rbac-roles-cmxaddmy.js} +7 -5
  107. package/dist/{render-v2-4fazz9tf.js → render-v2-f9hzff72.js} +9 -6
  108. package/dist/{seed-pricing-thxpn9vn.js → seed-pricing-jggk6rsy.js} +7 -5
  109. package/dist/{setup-wb0z8cat.js → setup-8mfceb9t.js} +4 -5
  110. package/dist/virtual-modules-ax661xp0.js +54 -0
  111. package/dist/{work-objects-h3498kwe.js → work-objects-vdsdcczj.js} +32 -28
  112. package/dist/{worker-2m2tzxp3.js → worker-cd0nhyv1.js} +44 -39
  113. package/openapi.json +36 -14
  114. package/package.json +6 -6
  115. package/src/be/automation-preflight.ts +6 -4
  116. package/src/be/extensions/db.ts +35 -4
  117. package/src/be/models-catalog.ts +2 -0
  118. package/src/be/modelsdev-cache.json +213 -0
  119. package/src/be/rbac-audit.ts +2 -0
  120. package/src/be/seed-pricing.ts +3 -1
  121. package/src/be/swarm-config-guard.ts +1 -0
  122. package/src/commands/onboard/compose-generator.ts +6 -1
  123. package/src/commands/onboard/env-generator.ts +6 -1
  124. package/src/commands/onboard/manifest.ts +1 -0
  125. package/src/commands/onboard/steps/integration-slack.tsx +56 -16
  126. package/src/commands/onboard/steps/review.tsx +5 -0
  127. package/src/commands/onboard/types.ts +6 -0
  128. package/src/commands/onboard.tsx +5 -1
  129. package/src/commands/provider-credentials.ts +14 -2
  130. package/src/extensions/lifecycle.ts +15 -21
  131. package/src/http/agents.ts +2 -2
  132. package/src/http/config.ts +1 -1
  133. package/src/http/core.ts +5 -3
  134. package/src/http/extensions.ts +108 -34
  135. package/src/http/status.ts +22 -10
  136. package/src/prompts/base-prompt.ts +15 -7
  137. package/src/providers/claude-managed-models.ts +8 -0
  138. package/src/providers/codex-models.ts +15 -1
  139. package/src/providers/dsh-adapter.ts +248 -0
  140. package/src/providers/index.ts +5 -1
  141. package/src/providers/modelsdev-reasoning.json +50 -0
  142. package/src/rbac/legacy-policy.ts +15 -1
  143. package/src/rbac/types.ts +2 -0
  144. package/src/server.ts +7 -1
  145. package/src/slack/app.ts +32 -10
  146. package/src/slack/assistant.ts +10 -1
  147. package/src/slack/config.ts +29 -0
  148. package/src/slack/handlers.ts +10 -1
  149. package/src/slack/thread-buffer.ts +14 -3
  150. package/src/telemetry.ts +2 -3
  151. package/src/tests/agents-harness-provider.test.ts +19 -0
  152. package/src/tests/agents-list-model-display.test.ts +60 -1
  153. package/src/tests/base-prompt.test.ts +25 -0
  154. package/src/tests/claude-managed-adapter.test.ts +6 -0
  155. package/src/tests/codex-gpt6-release.test.ts +97 -0
  156. package/src/tests/config-api-only-keys.test.ts +9 -0
  157. package/src/tests/context-window.test.ts +1 -0
  158. package/src/tests/dsh-adapter.test.ts +276 -0
  159. package/src/tests/e2e-nightly-report.test.ts +3 -3
  160. package/src/tests/e2e-redact.test.ts +2 -0
  161. package/src/tests/env-flag.test.ts +6 -0
  162. package/src/tests/extensions-db.test.ts +41 -0
  163. package/src/tests/extensions-http.test.ts +148 -4
  164. package/src/tests/extensions-lifecycle.test.ts +18 -0
  165. package/src/tests/extensions-mcp-tools.test.ts +9 -5
  166. package/src/tests/internal-ai/complete-structured.test.ts +32 -9
  167. package/src/tests/internal-ai/credentials.test.ts +4 -4
  168. package/src/tests/internal-ai/models.test.ts +20 -0
  169. package/src/tests/migration-046-budgets.test.ts +2 -0
  170. package/src/tests/model-control.test.ts +2 -2
  171. package/src/tests/onboard-compose.test.ts +14 -0
  172. package/src/tests/onboard-env.test.ts +17 -0
  173. package/src/tests/onboard-manifest.test.ts +12 -0
  174. package/src/tests/opus-5-5-registry.test.ts +48 -0
  175. package/src/tests/pricing-refresh.test.ts +1 -1
  176. package/src/tests/pricing-routes.test.ts +15 -3
  177. package/src/tests/provider-registration.test.ts +5 -2
  178. package/src/tests/rbac-admission-e2e.test.ts +54 -0
  179. package/src/tests/rbac-e2e-helpers.ts +2 -0
  180. package/src/tests/rbac-engine.test.ts +32 -0
  181. package/src/tests/rbac-wire-e2e.test.ts +33 -0
  182. package/src/tests/reasoning-effort.test.ts +8 -0
  183. package/src/tests/reload-config.test.ts +33 -0
  184. package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
  185. package/src/tests/slack-assistant.test.ts +134 -0
  186. package/src/tests/slack-config.test.ts +136 -0
  187. package/src/tests/slack-mode-lifecycle.test.ts +69 -0
  188. package/src/tests/status.test.ts +22 -2
  189. package/src/tests/task-tool-preload-mcp.test.ts +3 -1
  190. package/src/tests/telemetry-init.test.ts +18 -0
  191. package/src/tests/workflow-llm.test.ts +3 -3
  192. package/src/tools/extension-common.ts +4 -0
  193. package/src/tools/extension-install.ts +1 -1
  194. package/src/tools/extension-list.ts +4 -3
  195. package/src/types.ts +17 -8
  196. package/src/utils/context-window.ts +1 -0
  197. package/src/utils/credentials.ts +8 -0
  198. package/src/utils/internal-ai/complete-structured.ts +8 -0
  199. package/src/utils/internal-ai/models.ts +5 -5
  200. package/templates/schedules/daily-status-report/content.md +25 -12
  201. package/templates/schedules/daily-workflow-health-audit/content.md +1 -1
  202. package/templates/skills/swarm-extensions/content.md +9 -5
  203. package/dist/cli-4wppfkcm.js +0 -16384
  204. package/dist/cli-55qrx3q8.js +0 -6041
  205. package/dist/cli-cg4vnyh4.js +0 -305
  206. package/dist/cli-jyetr64j.js +0 -52
  207. package/dist/cli-pp9ax9pa.js +0 -520
  208. package/dist/cli-qa66qaf0.js +0 -6076
  209. package/dist/cli-qghpvca2.js +0 -590
  210. package/dist/cli-rembwgt8.js +0 -861
  211. package/dist/cli-tcvj7j2r.js +0 -1971
  212. package/dist/cli-vfcth11k.js +0 -36
  213. package/dist/cli-w1d1bgkk.js +0 -1422
  214. package/dist/cli-y6x0gqtg.js +0 -5977
  215. package/dist/es5-tj3tn4jj.js +0 -844
  216. package/dist/index-1kq5hw2p.js +0 -467
  217. package/dist/index-1p8wdstt.js +0 -7
  218. package/dist/index-8fdjzb2f.js +0 -100
  219. package/dist/index-bxmwzbgw.js +0 -365
  220. package/dist/index-nzerxd5z.js +0 -7
  221. package/dist/index-pyymnzat.js +0 -1168
  222. package/dist/index-qv6atbab.js +0 -619
  223. package/dist/index-rja32qa7.js +0 -801
  224. package/dist/index-tzg5srfp.js +0 -211
  225. package/dist/index-vhncw983.js +0 -90
  226. package/dist/index-xk036zsk.js +0 -859
  227. package/dist/index-zyazbzcb.js +0 -6
  228. package/dist/{claude-managed-setup-48dwg9bv.js → claude-managed-setup-f5pmsyrp.js} +3 -3
@@ -22,6 +22,7 @@ import { checkClaudeCredentials } from "../providers/claude-adapter";
22
22
  import { checkClaudeManagedCredentials } from "../providers/claude-managed-adapter";
23
23
  import { checkCodexCredentials } from "../providers/codex-adapter";
24
24
  import { checkDevinCredentials } from "../providers/devin-adapter";
25
+ import { checkDshCredentials } from "../providers/dsh-adapter";
25
26
  import { checkOpencodeCredentials } from "../providers/opencode-adapter";
26
27
  import type { CredCheckOptions, CredStatus } from "../providers/types";
27
28
  import type {
@@ -41,7 +42,8 @@ export type SupportedProvider =
41
42
  | "devin"
42
43
  | "opencode"
43
44
  | "pi"
44
- | "acp";
45
+ | "acp"
46
+ | "dsh";
45
47
 
46
48
  /**
47
49
  * True when the pi harness authenticates against Bedrock rather than a provider
@@ -105,6 +107,7 @@ export const REQUIRED_CRED_VARS_BY_PROVIDER: Record<SupportedProvider, readonly
105
107
  pi: ["ANTHROPIC_API_KEY", "OPENROUTER_API_KEY", "OPENAI_API_KEY"],
106
108
  // The ACP target process owns its own auth, so the swarm requires nothing.
107
109
  acp: [],
110
+ dsh: ["DEEPSEEK_API_KEY", "OPENROUTER_API_KEY"],
108
111
  };
109
112
 
110
113
  type CredentialChecker = (
@@ -114,6 +117,7 @@ type CredentialChecker = (
114
117
 
115
118
  /** The handlers used by the credential-readiness dispatcher. */
116
119
  export const CREDENTIAL_PROVIDER_CHECKERS: Record<SupportedProvider, CredentialChecker> = {
120
+ dsh: (env) => checkDshCredentials(env),
117
121
  claude: (env) => checkClaudeCredentials(env),
118
122
  "claude-managed": (env) => checkClaudeManagedCredentials(env),
119
123
  codex: (env, opts) => checkCodexCredentials(env, opts),
@@ -416,12 +420,20 @@ export async function validateProviderCredentials(provider: string): Promise<Liv
416
420
  latency_ms: r.latency_ms,
417
421
  };
418
422
  }
423
+ case "dsh":
424
+ return checkDshCredentials(env).ready
425
+ ? presenceCheckOk()
426
+ : {
427
+ ok: false,
428
+ error: "Set DEEPSEEK_API_KEY or OPENROUTER_API_KEY for dsh.",
429
+ latency_ms: Date.now() - startedAt,
430
+ };
419
431
  case "acp":
420
432
  return presenceCheckOk();
421
433
  default:
422
434
  return {
423
435
  ok: false,
424
- error: `Unknown provider "${provider}". Supported: claude, claude-managed, codex, devin, opencode, pi, acp.`,
436
+ error: `Unknown provider "${provider}". Supported: claude, claude-managed, codex, devin, opencode, pi, acp, dsh.`,
425
437
  latency_ms: Date.now() - startedAt,
426
438
  };
427
439
  }
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  activateExtensionVersionSnapshot,
3
3
  getExtensionById,
4
- getExtensionFiles,
5
4
  getExtensionVersion,
6
5
  insertExtensionRun,
7
6
  listExtensions,
@@ -66,28 +65,23 @@ function maxUpdatedAt(extensions: Extension[]): string {
66
65
  }
67
66
 
68
67
  async function loadableExtension(record: Extension): Promise<LoadableExtension> {
69
- if (record.activeVersion !== record.version) {
70
- const version = await getExtensionVersion(record.id, record.activeVersion);
71
- if (!version) {
72
- throw new ExtensionLifecycleError(
73
- `Extension version ${record.activeVersion} was not found`,
74
- 404,
75
- );
76
- }
77
- return {
78
- record: {
79
- ...record,
80
- manifestJson: version.manifestJson,
81
- contentHash: version.contentHash,
82
- },
83
- manifest: ExtensionManifestSchema.parse(JSON.parse(version.manifestJson)),
84
- files: JSON.parse(version.filesJson) as Record<string, string>,
85
- };
68
+ // Always load the immutable selected version. The mutable current-file projection
69
+ // can advance while an agent stages a draft after we read the extension row.
70
+ const version = await getExtensionVersion(record.id, record.activeVersion);
71
+ if (!version) {
72
+ throw new ExtensionLifecycleError(
73
+ `Extension version ${record.activeVersion} was not found`,
74
+ 404,
75
+ );
86
76
  }
87
77
  return {
88
- record,
89
- manifest: ExtensionManifestSchema.parse(JSON.parse(record.manifestJson)),
90
- files: await getExtensionFiles(record.id),
78
+ record: {
79
+ ...record,
80
+ manifestJson: version.manifestJson,
81
+ contentHash: version.contentHash,
82
+ },
83
+ manifest: ExtensionManifestSchema.parse(JSON.parse(version.manifestJson)),
84
+ files: JSON.parse(version.filesJson) as Record<string, string>,
91
85
  };
92
86
  }
93
87
 
@@ -181,7 +181,7 @@ const setAgentHarnessProviderRoute = route({
181
181
  },
182
182
  });
183
183
 
184
- const LocalHarnessProviderSchema = z.enum(["claude", "codex", "pi", "opencode", "acp"]);
184
+ const LocalHarnessProviderSchema = z.enum(["claude", "codex", "pi", "opencode", "acp", "dsh"]);
185
185
  const AcpRuntimeConfigSchema = z
186
186
  .object({
187
187
  target: z.enum(ACP_TARGET_IDS),
@@ -1046,7 +1046,7 @@ export async function handleAgentsRest(
1046
1046
  })
1047
1047
  )[0]?.value ?? "");
1048
1048
  const allowedLevels =
1049
- harness_provider === "acp"
1049
+ harness_provider === "acp" || harness_provider === "dsh"
1050
1050
  ? []
1051
1051
  : reasoningCapability(harness_provider, modelForValidation ?? "").levels;
1052
1052
  if (!allowedLevels.includes(reasoning_effort)) {
@@ -37,7 +37,7 @@ const SECRETS_FORCE_MASK_NOTE =
37
37
  // bootstrap admin key would let a compromised worker administer the shared org.
38
38
  // The API materializes these into its own process.env at boot via
39
39
  // getInjectableGlobalConfigs, so no HTTP consumer legitimately needs them.
40
- const API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY"]);
40
+ const API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY", "SLACK_SIGNING_SECRET"]);
41
41
 
42
42
  function stripApiOnlyKeys<T extends { key: string }>(configs: T[]): T[] {
43
43
  return configs.filter((config) => !API_ONLY_CONFIG_KEYS.has(config.key));
package/src/http/core.ts CHANGED
@@ -150,6 +150,10 @@ async function reloadGlobalConfigsAndIntegrationsInner(): Promise<ReloadConfigRe
150
150
 
151
151
  const updated = await loadGlobalConfigsIntoEnv(true);
152
152
 
153
+ // Retire the old transport before any other integration can fail during
154
+ // reload. HTTP/invalid selection must never leave the previous socket live.
155
+ await stopSlackApp();
156
+
153
157
  // File-storage provider selection reads process.env once and memoizes; the
154
158
  // env we just (re)hydrated may flip it (local-fs → agent-fs after late
155
159
  // provisioning, or a rotated bootstrap key). Reset so the next fs request
@@ -206,9 +210,7 @@ async function reloadGlobalConfigsAndIntegrationsInner(): Promise<ReloadConfigRe
206
210
  resetJira();
207
211
  if (await initJira()) integrations.push("jira");
208
212
 
209
- await stopSlackApp();
210
- await startSlackApp();
211
- integrations.push("slack");
213
+ if (await startSlackApp()) integrations.push("slack");
212
214
 
213
215
  return {
214
216
  configsLoaded: updated.length,
@@ -4,9 +4,11 @@ import { resolveHttpAuditUserId } from "../be/audit-user";
4
4
  import { getAgentById } from "../be/db";
5
5
  import {
6
6
  deleteExtension,
7
+ ExtensionOwnershipError,
7
8
  getExtensionById,
8
9
  getExtensionByName,
9
10
  getExtensionFiles,
11
+ type InstallExtensionResult,
10
12
  installExtension,
11
13
  listExtensionRuns,
12
14
  listExtensions,
@@ -23,8 +25,15 @@ import {
23
25
  reloadExtension,
24
26
  } from "../extensions/lifecycle";
25
27
  import { ExtensionConfigError } from "../extensions/loader";
26
- import { can, type PermissionVerb, type RbacPrincipal } from "../rbac";
27
28
  import {
29
+ can,
30
+ LEGACY_RULES,
31
+ type PermissionVerb,
32
+ type RbacPrincipal,
33
+ type RbacResource,
34
+ } from "../rbac";
35
+ import {
36
+ type Extension,
28
37
  ExtensionInstallBodySchema,
29
38
  ExtensionManifestSchema,
30
39
  ExtensionRunSchema,
@@ -44,6 +53,8 @@ const installRoute = route({
44
53
  pattern: ["api", "extensions", "install"],
45
54
  operationId: "extensions_install",
46
55
  summary: "Validate and install an extension bundle",
56
+ description:
57
+ "Any authenticated agent can install a disabled draft owned by its agent ID. Workers can update only their own bundles; activation remains lead/operator-only.",
47
58
  tags: ["Extensions"],
48
59
  body: ExtensionInstallBodySchema,
49
60
  responses: {
@@ -153,6 +164,8 @@ const patchRoute = route({
153
164
  pattern: ["api", "extensions", null],
154
165
  operationId: "extensions_update",
155
166
  summary: "Update extension priority, config, or description",
167
+ description:
168
+ "Workers may edit their own disabled extensions. Leads, operators, and dashboard users retain access to all extensions.",
156
169
  tags: ["Extensions"],
157
170
  params: idParamsSchema,
158
171
  body: z
@@ -177,6 +190,8 @@ const deleteRoute = route({
177
190
  pattern: ["api", "extensions", null],
178
191
  operationId: "extensions_delete",
179
192
  summary: "Uninstall a disabled extension",
193
+ description:
194
+ "Workers may uninstall their own disabled extensions. Leads, operators, and dashboard users retain access to all extensions.",
180
195
  tags: ["Extensions"],
181
196
  params: idParamsSchema,
182
197
  responses: {
@@ -316,14 +331,23 @@ async function extensionPrincipal(
316
331
  return { kind: "agent", agentId: "", isLead: false };
317
332
  }
318
333
 
334
+ /** Restrict ordinary agents; retain the existing blanket access for elevated writers. */
335
+ function ownerOnlyAgentId(principal: RbacPrincipal): string | undefined {
336
+ return principal.kind === "agent" &&
337
+ !LEGACY_RULES["lead-or-operator-or-user"].evaluate(principal, undefined)
338
+ ? principal.agentId
339
+ : undefined;
340
+ }
341
+
319
342
  async function requirePermission(
320
343
  req: IncomingMessage,
321
344
  res: ServerResponse,
322
345
  callerAgentId: string | undefined,
323
346
  verb: Extract<PermissionVerb, "extension.write" | "extension.activate">,
347
+ resource: RbacResource = { kind: "none" },
324
348
  ): Promise<RbacPrincipal | null> {
325
349
  const principal = await extensionPrincipal(req, callerAgentId);
326
- const decision = can({ principal, verb, resource: { kind: "none" }, source: "http" });
350
+ const decision = can({ principal, verb, resource, source: "http" });
327
351
  if (decision.allow) return principal;
328
352
  jsonError(res, `Forbidden: ${decision.reason}`, 403);
329
353
  return null;
@@ -331,11 +355,13 @@ async function requirePermission(
331
355
 
332
356
  function respondLifecycleError(res: ServerResponse, error: unknown): void {
333
357
  const status =
334
- error instanceof ExtensionConfigError
335
- ? 400
336
- : error instanceof ExtensionLifecycleError
337
- ? error.status
338
- : 500;
358
+ error instanceof ExtensionOwnershipError
359
+ ? 403
360
+ : error instanceof ExtensionConfigError
361
+ ? 400
362
+ : error instanceof ExtensionLifecycleError
363
+ ? error.status
364
+ : 500;
339
365
  jsonError(res, scrubSecrets(error instanceof Error ? error.message : String(error)), status);
340
366
  }
341
367
 
@@ -349,26 +375,41 @@ export async function handleExtensions(
349
375
  if (installRoute.match(req.method, pathSegments)) {
350
376
  const parsed = await installRoute.parse(req, res, pathSegments, queryParams);
351
377
  if (!parsed) return true;
352
- const principal = await requirePermission(req, res, agentId, "extension.write");
378
+ const existing = await getExtensionByName(parsed.body.manifest.name);
379
+ const principal = await requirePermission(req, res, agentId, "extension.write", {
380
+ kind: "extension",
381
+ extensionId: existing?.id,
382
+ createdByAgentId: existing?.createdByAgentId,
383
+ });
353
384
  if (!principal) return true;
354
385
  const validation = await validateBundle(parsed.body);
355
386
  if (!validation.ok) {
356
387
  json(res, { error: "extension_validation_failed", diagnostics: validation.diagnostics }, 400);
357
388
  return true;
358
389
  }
359
- const existing = await getExtensionByName(validation.manifest.name);
360
390
  const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
361
391
  const updatedBy = await resolveHttpAuditUserId(req, writerAgentId);
362
392
  const shouldActivate = principal.kind !== "agent" && existing?.enabled === true;
363
- const result = await installExtension({
364
- manifest: validation.manifest,
365
- files: parsed.body.files,
366
- priority: parsed.body.priority,
367
- config: parsed.body.config,
368
- agentId: writerAgentId,
369
- createdBy: updatedBy,
370
- activate: shouldActivate,
371
- });
393
+ // Enforce ownership again inside the upsert transaction: validation can yield
394
+ // while another agent installs the same name.
395
+ const ownerOnly = ownerOnlyAgentId(principal);
396
+ let result: InstallExtensionResult;
397
+ try {
398
+ result = await installExtension({
399
+ manifest: validation.manifest,
400
+ files: parsed.body.files,
401
+ priority: parsed.body.priority,
402
+ config: parsed.body.config,
403
+ agentId: writerAgentId,
404
+ createdBy: updatedBy,
405
+ activate: shouldActivate,
406
+ ownerOnly,
407
+ });
408
+ } catch (error) {
409
+ if (!(error instanceof ExtensionOwnershipError)) throw error;
410
+ jsonError(res, error.message, 403);
411
+ return true;
412
+ }
372
413
  let extension = result.extension;
373
414
  if (shouldActivate) {
374
415
  try {
@@ -446,19 +487,42 @@ export async function handleExtensions(
446
487
  if (patchRoute.match(req.method, pathSegments)) {
447
488
  const parsed = await patchRoute.parse(req, res, pathSegments, queryParams);
448
489
  if (!parsed) return true;
449
- const principal = await requirePermission(req, res, agentId, "extension.write");
490
+ const stored = await getExtensionById(parsed.params.id);
491
+ if (!stored) {
492
+ jsonError(res, "Extension not found", 404);
493
+ return true;
494
+ }
495
+ const principal = await requirePermission(req, res, agentId, "extension.write", {
496
+ kind: "extension",
497
+ extensionId: stored.id,
498
+ createdByAgentId: stored.createdByAgentId,
499
+ });
450
500
  if (!principal) return true;
451
501
  const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
452
- const stored = parsed.body.config ? await getExtensionById(parsed.params.id) : null;
453
- const config =
454
- parsed.body.config && stored
455
- ? preserveRedactedConfigValues(parsed.body.config, stored.configJson)
456
- : parsed.body.config;
457
- let extension = await updateExtensionMeta(parsed.params.id, {
458
- ...parsed.body,
459
- ...(config === undefined ? {} : { config }),
460
- updatedBy: await resolveHttpAuditUserId(req, writerAgentId),
461
- });
502
+ const ownerOnly = ownerOnlyAgentId(principal);
503
+ // PATCH reloads live code; ordinary owners may edit only disabled drafts.
504
+ if (
505
+ stored.enabled &&
506
+ ownerOnly &&
507
+ !(await requirePermission(req, res, agentId, "extension.activate"))
508
+ ) {
509
+ return true;
510
+ }
511
+ const config = parsed.body.config
512
+ ? preserveRedactedConfigValues(parsed.body.config, stored.configJson)
513
+ : parsed.body.config;
514
+ let extension: Extension | null;
515
+ try {
516
+ extension = await updateExtensionMeta(parsed.params.id, {
517
+ ...parsed.body,
518
+ ...(config === undefined ? {} : { config }),
519
+ updatedBy: await resolveHttpAuditUserId(req, writerAgentId),
520
+ ownerOnly,
521
+ });
522
+ } catch (error) {
523
+ respondLifecycleError(res, error);
524
+ return true;
525
+ }
462
526
  if (!extension) {
463
527
  jsonError(res, "Extension not found", 404);
464
528
  return true;
@@ -478,18 +542,28 @@ export async function handleExtensions(
478
542
  if (deleteRoute.match(req.method, pathSegments)) {
479
543
  const parsed = await deleteRoute.parse(req, res, pathSegments, queryParams);
480
544
  if (!parsed) return true;
481
- const principal = await requirePermission(req, res, agentId, "extension.write");
482
- if (!principal) return true;
483
- const extension = await getExtensionById(parsed.params.id);
484
- if (!extension) {
545
+ const stored = await getExtensionById(parsed.params.id);
546
+ if (!stored) {
485
547
  jsonError(res, "Extension not found", 404);
486
548
  return true;
487
549
  }
550
+ const principal = await requirePermission(req, res, agentId, "extension.write", {
551
+ kind: "extension",
552
+ extensionId: stored.id,
553
+ createdByAgentId: stored.createdByAgentId,
554
+ });
555
+ if (!principal) return true;
556
+ const extension = stored;
488
557
  if (extension.enabled) {
489
558
  jsonError(res, "Disable the extension before uninstalling it", 409);
490
559
  return true;
491
560
  }
492
- await deleteExtension(extension.id);
561
+ try {
562
+ await deleteExtension(extension.id, ownerOnlyAgentId(principal));
563
+ } catch (error) {
564
+ respondLifecycleError(res, error);
565
+ return true;
566
+ }
493
567
  deleteRoute.respond(res, 200, { deleted: true });
494
568
  return true;
495
569
  }
@@ -34,6 +34,7 @@ import {
34
34
  } from "../be/db";
35
35
  import { getEmbeddingProvider } from "../be/memory";
36
36
  import { getFileStorageProvider } from "../fs/registry";
37
+ import { getSlackConfiguration } from "../slack/config";
37
38
  import { type AgentCredStatus, AutomationIntegrationIdSchema, ProviderNameSchema } from "../types";
38
39
  import { route } from "./route-def";
39
40
  import { json, jsonError } from "./utils";
@@ -407,26 +408,37 @@ function embeddingsMilestone(): SetupMilestone {
407
408
  }
408
409
 
409
410
  function slackMilestone(state: AutomationSetupStates["slack"]): SetupMilestone {
410
- const disable = process.env.SLACK_DISABLE;
411
- const disabled = disable === "true" || disable === "1";
411
+ const config = getSlackConfiguration();
412
412
 
413
413
  if (state === "unverified") {
414
+ let hint: string;
415
+ if (config.disabled) {
416
+ hint = "Slack is explicitly disabled (SLACK_DISABLE=true).";
417
+ } else if (!config.mode) {
418
+ hint = "Invalid SLACK_MODE. Use socket or http.";
419
+ } else if (config.mode === "http" && config.missingCredentials.length === 0) {
420
+ hint =
421
+ "HTTP credentials are configured, but HTTP ingress is unavailable until the receiver is installed.";
422
+ } else {
423
+ hint = `Set ${config.missingCredentials.join(" + ")} for Slack ${config.mode} mode.`;
424
+ }
414
425
  return {
415
426
  id: "slack",
416
- label: "Slack connected",
427
+ label: "Slack configured",
417
428
  state: "unverified",
418
- hint: disabled
419
- ? "Slack is explicitly disabled (SLACK_DISABLE=true)."
420
- : "Set SLACK_BOT_TOKEN + SLACK_APP_TOKEN to connect Slack.",
429
+ hint,
421
430
  action_url: automationIntegrationFixUrl("slack"),
422
431
  };
423
432
  }
424
- // Socket Mode connection state isn't surfaced today — Phase 2+ enhancement.
425
- // For now treat env-present as `verified` so the UX matches the brainstorm.
433
+
426
434
  return {
427
435
  id: "slack",
428
- label: "Slack connected",
429
- state: "verified",
436
+ label: "Slack configured",
437
+ state: "configured",
438
+ hint:
439
+ config.mode === "http"
440
+ ? "HTTP credentials are configured, but HTTP ingress is unavailable until the receiver is installed."
441
+ : "Socket Mode credentials are configured; a live connection has not been verified.",
430
442
  action_url: automationIntegrationFixUrl("slack"),
431
443
  };
432
444
  }
@@ -16,6 +16,7 @@
16
16
  */
17
17
 
18
18
  import type { ProviderTraits } from "../providers/types";
19
+ import { getSlackConfiguration } from "../slack/config";
19
20
  import type { ProviderName } from "../types";
20
21
  import { isSteeringEnabled } from "../utils/steering-enabled";
21
22
  import { matchesDefaultClaudeMd, matchesDefaultIdentityMd } from "./defaults";
@@ -54,11 +55,18 @@ const CLAUDE_MD_INJECT_PROVIDERS: ReadonlySet<string> = new Set(["codex", "openc
54
55
  /** Providers that get the repo CLAUDE.md inlined until native loading is verified. */
55
56
  const REPO_CLAUDE_MD_INLINE_PROVIDERS: ReadonlySet<string> = new Set(["opencode"]);
56
57
 
57
- export function areSlackPromptToolsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
58
- const slackDisable = env.SLACK_DISABLE;
59
- if (slackDisable === "true" || slackDisable === "1") return false;
60
-
61
- return Boolean(env.SLACK_BOT_TOKEN && env.SLACK_APP_TOKEN);
58
+ export function areSlackPromptToolsEnabled(
59
+ env: NodeJS.ProcessEnv = process.env,
60
+ serverCapabilities?: string[],
61
+ ): boolean {
62
+ const config = getSlackConfiguration(env);
63
+ if (config.disabled || !config.mode) return false;
64
+
65
+ // Preserve the socket credential gate. For the HTTP contract, workers use
66
+ // the server tool capability without needing the API-owned signing secret.
67
+ if (serverCapabilities && !serverCapabilities.includes("slack")) return false;
68
+ if (config.mode === "http" && serverCapabilities) return true;
69
+ return config.missingCredentials.length === 0;
62
70
  }
63
71
 
64
72
  export type BasePromptArgs = {
@@ -185,8 +193,8 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
185
193
 
186
194
  // H. Slack. One block for both roles; the scripts-only variant covers Slack
187
195
  // via ctx.swarm.slack_* for Slack-originated tasks.
188
- const slackPromptToolsEnabled = areSlackPromptToolsEnabled();
189
- if (hasMcp && slackPromptToolsEnabled && !scriptsOnlyMode && serverHasCapability("slack", true)) {
196
+ const slackPromptToolsEnabled = areSlackPromptToolsEnabled(process.env, args.serverCapabilities);
197
+ if (hasMcp && slackPromptToolsEnabled && !scriptsOnlyMode) {
190
198
  const slackResult = await resolveTemplateAsync("system.agent.slack", {});
191
199
  prompt += slackResult.text;
192
200
  }
@@ -27,6 +27,7 @@
27
27
  export const CLAUDE_MANAGED_MODELS = [
28
28
  "claude-fable-5-1",
29
29
  "claude-mythos-5-1",
30
+ "claude-opus-5-5",
30
31
  "claude-opus-5",
31
32
  "claude-fable-5",
32
33
  "claude-mythos-5",
@@ -69,6 +70,7 @@ export interface ClaudeManagedModelPricing extends ClaudeManagedTokenPricing {
69
70
  * - claude-mythos-5-1: $10 / $50 / $0.25 / $12.50 (invite only, verified 2026-09-02)
70
71
  * - claude-fable-5: $10 / $50 / $1.00 / $12.50 (verified 2026-06-10)
71
72
  * - claude-mythos-5: $10 / $50 / $1.00 / $12.50 (limited availability, verified 2026-06-10)
73
+ * - claude-opus-5-5: $4 / $20 / $0.20 / $5.00 (verified 2026-09-22)
72
74
  * - claude-opus-5: $5 / $25 / $0.50 / $6.25 (verified 2026-07-25)
73
75
  * - claude-sonnet-5: $2 / $10 / $0.20 / $2.50 (standard rate, verified 2026-08-29)
74
76
  * - claude-sonnet-4-6: $3 / $15 / $0.30 / $3.75 (in / out / cache-read / cache-write)
@@ -90,6 +92,12 @@ export const CLAUDE_MANAGED_MODEL_PRICING: Record<ClaudeManagedModel, ClaudeMana
90
92
  cacheReadPerMillion: 0.25,
91
93
  cacheWritePerMillion: 12.5,
92
94
  },
95
+ "claude-opus-5-5": {
96
+ inputPerMillion: 4.0,
97
+ outputPerMillion: 20.0,
98
+ cacheReadPerMillion: 0.2,
99
+ cacheWritePerMillion: 5.0,
100
+ },
93
101
  "claude-opus-5": {
94
102
  inputPerMillion: 5.0,
95
103
  outputPerMillion: 25.0,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Codex API-addressable models, verified from https://developers.openai.com/api/docs/models
3
- * and https://developers.openai.com/api/docs/deprecations as of 2026-09-05.
3
+ * and https://developers.openai.com/api/docs/deprecations as of 2026-09-23.
4
4
  *
5
5
  * NOTE: `gpt-5.3-codex-spark` is intentionally excluded. It is a ChatGPT Pro
6
6
  * research preview and is NOT API-addressable via the Codex SDK at launch.
@@ -20,6 +20,8 @@ import modelsDevCache from "../be/modelsdev-cache.json";
20
20
  */
21
21
  export const CODEX_MODELS = [
22
22
  "gpt-6-astra", // most capable model for complex reasoning, coding, and agentic work
23
+ "gpt-6-sol", // complex coding and agentic workflows
24
+ "gpt-6-luna", // focused, high-volume tasks
23
25
  "gpt-5.6-sol", // frontier GPT-5.6 tier for complex reasoning/coding
24
26
  "gpt-5.6-terra", // balanced GPT-5.6 tier
25
27
  "gpt-5.6-luna", // fast/cheap GPT-5.6 tier for high-volume workloads
@@ -83,6 +85,8 @@ const MODELSDEV_OPENAI_MODELS =
83
85
 
84
86
  const FALLBACK_CODEX_MODEL_CONTEXT_WINDOWS: Record<CodexModel, number> = {
85
87
  "gpt-6-astra": 1_050_000,
88
+ "gpt-6-sol": 1_050_000,
89
+ "gpt-6-luna": 1_050_000,
86
90
  "gpt-5.6-sol": 1_050_000,
87
91
  "gpt-5.6-terra": 1_050_000,
88
92
  "gpt-5.6-luna": 1_050_000,
@@ -153,6 +157,16 @@ export const FALLBACK_CODEX_MODEL_PRICING: Record<CodexModel, CodexModelPricing>
153
157
  cachedInputPerMillion: 1.0,
154
158
  outputPerMillion: 50.0,
155
159
  },
160
+ "gpt-6-sol": {
161
+ inputPerMillion: 2.0,
162
+ cachedInputPerMillion: 0.2,
163
+ outputPerMillion: 10.0,
164
+ },
165
+ "gpt-6-luna": {
166
+ inputPerMillion: 0.1,
167
+ cachedInputPerMillion: 0.01,
168
+ outputPerMillion: 0.5,
169
+ },
156
170
  "gpt-5.6-sol": {
157
171
  inputPerMillion: 4.0,
158
172
  cachedInputPerMillion: 0.4,