@desplega.ai/agent-swarm 1.145.0 → 1.147.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 (221) hide show
  1. package/README.md +2 -1
  2. package/dist/{acp-adapter-580t41c0.js → acp-adapter-9n319wqc.js} +61 -6
  3. package/dist/{actions-m662tjng.js → actions-2vxqvpr9.js} +9 -8
  4. package/dist/{app-zb3rndm8.js → app-w066xfy0.js} +6 -5
  5. package/dist/assistant-kt31dj2h.js +236 -0
  6. package/dist/{boot-reembed-072x00vn.js → boot-reembed-az5rassp.js} +7 -6
  7. package/dist/{boot-reembed-65zz2smt.js → boot-reembed-j3mm3rfz.js} +8 -7
  8. package/dist/{boot-scrub-logs-yz0ams04.js → boot-scrub-logs-b6h54817.js} +4 -4
  9. package/dist/{claude-adapter-paj270y2.js → claude-adapter-w1z04ann.js} +8 -6
  10. package/dist/{claude-managed-setup-w619jncn.js → claude-managed-setup-f5pmsyrp.js} +24 -3
  11. package/dist/{claude-sdk-session-swt80j9z.js → claude-sdk-session-f0cc1kec.js} +8 -6
  12. package/dist/{cli-dt553ktb.js → cli-0b6y7kxm.js} +1 -1
  13. package/dist/{cli-bxdhw615.js → cli-12fz972k.js} +4 -4
  14. package/dist/{cli-s2f20r5w.js → cli-1th1728g.js} +2 -2
  15. package/dist/{cli-rs6ec3b9.js → cli-3tgwnf43.js} +3 -3
  16. package/dist/{cli-gxnzc6wh.js → cli-4nr988h6.js} +1 -1
  17. package/dist/{cli-axs8e6d4.js → cli-53s590z8.js} +1 -1
  18. package/dist/{cli-ygbjp3z8.js → cli-54wwve05.js} +5 -4
  19. package/dist/{cli-v0asr5yv.js → cli-719k9j2c.js} +2 -2
  20. package/dist/{cli-anktweqr.js → cli-89xfd7f9.js} +21 -47
  21. package/dist/{cli-q34a0e10.js → cli-8v0g1yc8.js} +1 -1
  22. package/dist/cli-9d8ntetj.js +42 -0
  23. package/dist/{cli-z9fprcrf.js → cli-atcve9y8.js} +881 -738
  24. package/dist/{cli-jmb7g47h.js → cli-b5bq9crk.js} +14 -31
  25. package/dist/cli-d8brzxjb.js +190 -0
  26. package/dist/{cli-gzky0gkr.js → cli-de14znh8.js} +2 -2
  27. package/dist/{cli-xytwff7f.js → cli-dttph1bw.js} +2 -2
  28. package/dist/cli-ep19k3mb.js +19 -0
  29. package/dist/{cli-m1xa8be7.js → cli-f146mn5s.js} +7 -50
  30. package/dist/{cli-1fj0chdk.js → cli-f7dsy61y.js} +5 -3
  31. package/dist/{cli-f567d5c1.js → cli-ftht3zzj.js} +2782 -2589
  32. package/dist/{cli-a0g1gbt5.js → cli-hbv0kq6w.js} +2 -2
  33. package/dist/{cli-fhsz3twr.js → cli-jsp14b6c.js} +171 -1
  34. package/dist/{cli-7fnkj1hj.js → cli-kpv03zhj.js} +21 -17
  35. package/dist/{cli-m4j6trn4.js → cli-m8yrsg97.js} +2 -2
  36. package/dist/cli-m9sxbkhm.js +779 -0
  37. package/dist/cli-mmemxxdc.js +823 -0
  38. package/dist/{cli-tqtvj5pt.js → cli-n8508nre.js} +1 -1
  39. package/dist/{cli-z96t0thj.js → cli-p1d5b073.js} +37 -2
  40. package/dist/{cli-gh8y8f36.js → cli-rw51bq3j.js} +13 -10
  41. package/dist/{cli-59ej1c07.js → cli-smby5m17.js} +7 -6
  42. package/dist/{cli-65vqzz3v.js → cli-tzvk9haz.js} +94 -372
  43. package/dist/{cli-dc9k2ba9.js → cli-w9kb3bk1.js} +1 -1
  44. package/dist/{cli-kpev657q.js → cli-y6b6fb76.js} +25 -3
  45. package/dist/{cli-123q2get.js → cli-yz3djrm0.js} +9 -9
  46. package/dist/{cli-wvp6bvy4.js → cli-zhjmqxzh.js} +1 -1
  47. package/dist/cli.js +13 -13
  48. package/dist/{codex-adapter-k98vaw2r.js → codex-adapter-zw4dwsx7.js} +2 -2
  49. package/dist/{codex-hook-9pf19cks.js → codex-hook-bw8p5nh4.js} +2 -2
  50. package/dist/{codex-session-runner-tngtphx7.js → codex-session-runner-k981s65v.js} +2 -2
  51. package/dist/{commands-y8nbr3fv.js → commands-sykrxx7e.js} +4 -4
  52. package/dist/{db-30p4h2t1.js → db-sfbsc8wt.js} +6 -4
  53. package/dist/handlers-0aspcejm.js +603 -0
  54. package/dist/{hook-dcw4jfbj.js → hook-4nvqj2sb.js} +7 -5
  55. package/dist/{hook-21jexvfx.js → hook-hxccs7m5.js} +24 -49
  56. package/dist/{http-r4kq5g8d.js → http-y06z517d.js} +230 -94
  57. package/dist/{index-q3qja6ta.js → index-hssww7kj.js} +15 -14
  58. package/dist/{index-gay66b9w.js → index-k8znfx0z.js} +13 -12
  59. package/dist/{index-53zyht4z.js → index-tce1rss4.js} +371 -110
  60. package/dist/{index-nqvf26za.js → index-x5j894zf.js} +14 -13
  61. package/dist/{keepalive-xs8fa55y.js → keepalive-w733ax66.js} +6 -6
  62. package/dist/{lead-d9m2t4s0.js → lead-75bjqa9d.js} +29 -27
  63. package/dist/{maintenance-3zh82g6z.js → maintenance-w4f1zjck.js} +8 -7
  64. package/dist/{oauth-refresh-sweep-03snsye5.js → oauth-refresh-sweep-wdetky26.js} +5 -5
  65. package/dist/{onboard-0cg1925x.js → onboard-bb5net8s.js} +2 -2
  66. package/dist/{otel-impl-0ab95z27.js → otel-impl-0w7c14ap.js} +1 -1
  67. package/dist/{pi-mono-adapter-dtkz4e7j.js → pi-mono-adapter-2has4gnp.js} +12 -2
  68. package/dist/{pricing-refresh-e5jhzz4j.js → pricing-refresh-9nzsrgcq.js} +6 -6
  69. package/dist/{rbac-roles-p1mf40z3.js → rbac-roles-r3hqjp09.js} +5 -5
  70. package/dist/{rbac-roles-2w3cmrkc.js → rbac-roles-z1jrffbt.js} +4 -4
  71. package/dist/{registry-9kdck5ye.js → registry-v5wytg0s.js} +1 -2
  72. package/dist/{render-v2-xhawry3k.js → render-v2-vbr89fnb.js} +6 -5
  73. package/dist/{seed-pricing-t223sw91.js → seed-pricing-g62hy1pk.js} +7 -5
  74. package/dist/{setup-zcs0rce7.js → setup-gnfqnptp.js} +2 -2
  75. package/dist/{worker-qnp2ka62.js → worker-kznxp4qm.js} +29 -27
  76. package/openapi.json +263 -12
  77. package/package.json +1 -1
  78. package/plugin/commands/start-worker.md +4 -9
  79. package/plugin/pi-skills/start-worker/SKILL.md +4 -9
  80. package/src/be/db/agents.ts +556 -0
  81. package/src/be/db/context-versions.ts +117 -0
  82. package/src/be/db/runtime.ts +406 -0
  83. package/src/be/db/tasks/read.ts +1167 -0
  84. package/src/be/db/tasks/write.ts +953 -0
  85. package/src/be/db.ts +584 -3429
  86. package/src/be/migrations/147_scheduled_tasks_parent_task.sql +7 -0
  87. package/src/be/migrations/148_pages_svg.sql +123 -0
  88. package/src/be/migrations/149_session_tokens.sql +24 -0
  89. package/src/be/scripts/typecheck.ts +1 -0
  90. package/src/be/seed-pricing.ts +13 -8
  91. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  92. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  93. package/src/be/seed-scripts/catalog/star-history-refresh.ts +101 -0
  94. package/src/be/seed-scripts/catalog/star-history-renderer.ts +119 -0
  95. package/src/be/seed-scripts/index.ts +17 -0
  96. package/src/be/seed-skills/index.ts +14 -0
  97. package/src/be/swarm-config-guard.ts +5 -2
  98. package/src/be/task-attachment-store.ts +70 -0
  99. package/src/be/users.ts +77 -0
  100. package/src/commands/attachments-section.ts +37 -0
  101. package/src/commands/claude-managed-setup.ts +45 -3
  102. package/src/commands/claude-md-session.ts +408 -0
  103. package/src/commands/context-preamble.ts +15 -3
  104. package/src/commands/profile-sync.ts +205 -5
  105. package/src/commands/provider-credentials.ts +60 -27
  106. package/src/commands/runner.ts +63 -58
  107. package/src/commands/templates.ts +25 -2
  108. package/src/hooks/hook.ts +26 -67
  109. package/src/http/agents.ts +40 -1
  110. package/src/http/auth.ts +13 -1
  111. package/src/http/fs.ts +21 -51
  112. package/src/http/index.ts +2 -0
  113. package/src/http/mcp.ts +32 -0
  114. package/src/http/pages-public.ts +20 -4
  115. package/src/http/pages.ts +11 -3
  116. package/src/http/poll.ts +4 -0
  117. package/src/http/repos.ts +3 -3
  118. package/src/http/sessions.ts +82 -0
  119. package/src/http/tasks.ts +10 -2
  120. package/src/http/utils.ts +79 -1
  121. package/src/prompts/session-templates.ts +12 -8
  122. package/src/providers/acp-adapter.ts +62 -8
  123. package/src/providers/pi-mono-adapter.ts +23 -6
  124. package/src/providers/pricing-sources.md +11 -0
  125. package/src/scheduler/scheduler.test.ts +49 -2
  126. package/src/scheduler/scheduler.ts +4 -0
  127. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  128. package/src/scripts-runtime/types/stdlib.d.ts +8 -0
  129. package/src/scripts-runtime/types/swarm-sdk.d.ts +8 -0
  130. package/src/server.ts +2 -0
  131. package/src/slack/assistant.ts +87 -46
  132. package/src/slack/handlers.test.ts +1 -3
  133. package/src/slack/handlers.ts +102 -87
  134. package/src/slack/inbound-files.ts +466 -0
  135. package/src/slack/templates.ts +14 -0
  136. package/src/tasks/task-terminal-effects.ts +158 -0
  137. package/src/tests/acp-adapter.test.ts +369 -117
  138. package/src/tests/budget-claim-gate.test.ts +1 -0
  139. package/src/tests/build-pi-skills.test.ts +5 -7
  140. package/src/tests/claude-managed-setup.test.ts +52 -4
  141. package/src/tests/claude-md-session.test.ts +603 -0
  142. package/src/tests/codex-skill-resolver.test.ts +19 -0
  143. package/src/tests/context-versioning.test.ts +107 -0
  144. package/src/tests/cors-allowlist.test.ts +309 -0
  145. package/src/tests/credential-check.test.ts +157 -14
  146. package/src/tests/db-agents.test.ts +91 -0
  147. package/src/tests/db-context-versions.test.ts +37 -0
  148. package/src/tests/db-extraction-contract.test.ts +45 -0
  149. package/src/tests/db-runtime.test.ts +70 -0
  150. package/src/tests/db-task-read-contract.test.ts +624 -0
  151. package/src/tests/db-task-write-contract.test.ts +653 -0
  152. package/src/tests/db-tasks-read.test.ts +183 -0
  153. package/src/tests/db-tasks-write.test.ts +230 -0
  154. package/src/tests/defer-task.test.ts +389 -0
  155. package/src/tests/docs-index-footprint.test.js +2 -2
  156. package/src/tests/e2e-visuals-stage.test.ts +151 -0
  157. package/src/tests/file-lock.test.ts +144 -0
  158. package/src/tests/fixtures/file-lock-holder.ts +13 -0
  159. package/src/tests/fixtures/hold-file-lock.ts +25 -0
  160. package/src/tests/heartbeat-expiry.test.ts +188 -0
  161. package/src/tests/http-api-integration.test.ts +78 -1
  162. package/src/tests/pages-public-html.test.ts +43 -0
  163. package/src/tests/pages-svg-migration.test.ts +42 -0
  164. package/src/tests/preload.ts +4 -1
  165. package/src/tests/profile-sync-warning.test.ts +16 -0
  166. package/src/tests/profile-sync.test.ts +126 -0
  167. package/src/tests/prompt-template-session.test.ts +20 -4
  168. package/src/tests/provider-registration.test.ts +93 -0
  169. package/src/tests/rbac-e2e-helpers.test.ts +53 -0
  170. package/src/tests/rbac-e2e-helpers.ts +19 -2
  171. package/src/tests/reload-config.test.ts +26 -0
  172. package/src/tests/routing-affinity-inventory.test.ts +1 -1
  173. package/src/tests/runner-attachments-section.test.ts +22 -2
  174. package/src/tests/runner-context-preamble.test.ts +30 -0
  175. package/src/tests/runner-deployment-resume.test.ts +107 -0
  176. package/src/tests/runner-repo-autostash.test.ts +12 -0
  177. package/src/tests/script-connections.test.ts +4 -0
  178. package/src/tests/seed-scripts.test.ts +2 -2
  179. package/src/tests/session-tokens.test.ts +279 -0
  180. package/src/tests/slack-file-tools-attachments.test.ts +408 -0
  181. package/src/tests/slack-inbound-files.test.ts +847 -0
  182. package/src/tests/star-history-refresh.test.ts +102 -0
  183. package/src/tests/swarm-config-reserved-keys.test.ts +21 -0
  184. package/src/tests/swarm-repos.test.ts +20 -0
  185. package/src/tests/tool-annotations.test.ts +4 -3
  186. package/src/tests/ui-logs-parser.test.ts +176 -0
  187. package/src/tools/create-page.ts +4 -2
  188. package/src/tools/defer-task.ts +239 -0
  189. package/src/tools/repos/update-repo.ts +2 -2
  190. package/src/tools/schedules/create-schedule.ts +1 -0
  191. package/src/tools/schedules/list-schedules.ts +1 -0
  192. package/src/tools/schedules/patch-schedule.ts +1 -0
  193. package/src/tools/schedules/update-schedule.ts +1 -0
  194. package/src/tools/slack-download-file.ts +60 -29
  195. package/src/tools/slack-read.ts +73 -32
  196. package/src/tools/store-progress.ts +16 -146
  197. package/src/tools/tool-config.ts +1 -0
  198. package/src/tools/utils.ts +21 -0
  199. package/src/types.ts +37 -4
  200. package/src/utils/acp-session-token.ts +56 -0
  201. package/src/utils/file-lock.ts +111 -0
  202. package/src/utils/heartbeat-expiry.ts +78 -0
  203. package/src/utils/request-auth-context.ts +2 -0
  204. package/src/utils/task-attachment-links.ts +22 -0
  205. package/templates/skills/code-quality/content.md +8 -5
  206. package/templates/skills/download-task-attachment/content.md +2 -1
  207. package/{plugin/pi-skills → templates/skills}/review-offered-task/SKILL.md +5 -5
  208. package/templates/skills/review-offered-task/config.json +14 -0
  209. package/{plugin/commands/review-offered-task.md → templates/skills/review-offered-task/content.md} +3 -8
  210. package/templates/skills/scheduling/content.md +4 -0
  211. package/templates/skills/slack-interaction/content.md +2 -2
  212. package/templates/skills/work-on-task/SKILL.md +16 -0
  213. package/templates/skills/work-on-task/config.json +14 -0
  214. package/templates/skills/work-on-task/content.md +11 -0
  215. package/dist/assistant-0qt42nzg.js +0 -212
  216. package/dist/cli-fs1kkqgr.js +0 -317
  217. package/dist/cli-jnf5zghe.js +0 -269
  218. package/dist/cli-n1q8q14d.js +0 -177
  219. package/dist/handlers-yfp1qn81.js +0 -592
  220. package/plugin/commands/work-on-task.md +0 -39
  221. package/plugin/pi-skills/work-on-task/SKILL.md +0 -39
package/README.md CHANGED
@@ -13,6 +13,7 @@ agent-swarm.dev is an open-source operating system for AI work. A lead agent del
13
13
 
14
14
  - A lead agent that receives work from Slack, repositories, issue trackers, email, or the API
15
15
  - [Schema-validated task results](./MCP.md#send-task) for callers that need structured JSON output
16
+ - [Dashboard file attachments](https://docs.agent-swarm.dev/docs/ui#file-attachments) by drag-and-drop or file picker
16
17
  - Workers in isolated Docker containers with development environments
17
18
  - Memory and identity that persist across sessions
18
19
  - Workflows, schedules, scripts, and apps for recurring work
@@ -95,7 +96,7 @@ Read [CONTRIBUTING.md](./CONTRIBUTING.md), fork the repository, create a branch,
95
96
 
96
97
  ## Star History
97
98
 
98
- <picture><source media="(prefers-color-scheme: dark)" srcset="./assets/star-history-dark.svg" /> <source media="(prefers-color-scheme: light)" srcset="./assets/star-history-light.svg" /> <img alt="Star History Chart" src="./assets/star-history-light.svg" /></picture>
99
+ <picture><source media="(prefers-color-scheme: dark)" srcset="https://api.desplega.agent-swarm.dev/p/e3473addceb348c0bed6cb93bd5aa532" /> <source media="(prefers-color-scheme: light)" srcset="https://api.desplega.agent-swarm.dev/p/4b5cdc17c45e4a63845fb0a71ed9609c" /> <img alt="Star History Chart" src="https://api.desplega.agent-swarm.dev/p/4b5cdc17c45e4a63845fb0a71ed9609c" /></picture>
99
100
 
100
101
  ## License
101
102
 
@@ -29,7 +29,7 @@ import {
29
29
  import {
30
30
  init_package,
31
31
  package_default
32
- } from "./cli-dc9k2ba9.js";
32
+ } from "./cli-w9kb3bk1.js";
33
33
  import {
34
34
  init_secret_scrubber,
35
35
  scrubSecrets
@@ -4347,6 +4347,34 @@ class ClientSideConnection {
4347
4347
 
4348
4348
  // src/providers/acp-adapter.ts
4349
4349
  init_package();
4350
+
4351
+ // src/utils/acp-session-token.ts
4352
+ var ACP_SESSION_TOKEN_TTL_MS = 24 * 60 * 60 * 1000;
4353
+ async function mintAcpSessionToken(apiUrl, apiKey, agentId, taskId) {
4354
+ const res = await fetch(`${apiUrl.replace(/\/+$/, "")}/api/sessions/tokens`, {
4355
+ method: "POST",
4356
+ headers: {
4357
+ Authorization: `Bearer ${apiKey}`,
4358
+ "Content-Type": "application/json"
4359
+ },
4360
+ body: JSON.stringify({ agentId, taskId, ttlMs: ACP_SESSION_TOKEN_TTL_MS })
4361
+ });
4362
+ if (!res.ok) {
4363
+ throw new Error(`\x1B[33m[acp]\x1B[0m Session token mint failed (${res.status})`);
4364
+ }
4365
+ const data = await res.json();
4366
+ return { tokenId: data.tokenId, plaintext: data.plaintext };
4367
+ }
4368
+ async function revokeAcpSessionToken(apiUrl, apiKey, tokenId) {
4369
+ try {
4370
+ await fetch(`${apiUrl.replace(/\/+$/, "")}/api/sessions/tokens/${tokenId}`, {
4371
+ method: "DELETE",
4372
+ headers: { Authorization: `Bearer ${apiKey}` }
4373
+ });
4374
+ } catch {}
4375
+ }
4376
+
4377
+ // src/providers/acp-adapter.ts
4350
4378
  init_secret_scrubber();
4351
4379
 
4352
4380
  // src/providers/acp-swarm-events.ts
@@ -4748,11 +4776,13 @@ class ACPSession {
4748
4776
  output = "";
4749
4777
  completionPromise;
4750
4778
  completionResolve;
4751
- constructor(connection, process2, config, sessionId, providerMeta) {
4779
+ ephemeralTokenId = null;
4780
+ constructor(connection, process2, config, sessionId, providerMeta, ephemeralTokenId) {
4752
4781
  this.connection = connection;
4753
4782
  this.process = process2;
4754
4783
  this.config = config;
4755
4784
  this.sessionId = sessionId;
4785
+ this.ephemeralTokenId = ephemeralTokenId ?? null;
4756
4786
  this.completionPromise = new Promise((resolve) => {
4757
4787
  this.completionResolve = resolve;
4758
4788
  });
@@ -4821,8 +4851,21 @@ class ACPSession {
4821
4851
  sessionId: this.sessionId,
4822
4852
  prompt: [{ type: "text", text: this.config.prompt }]
4823
4853
  });
4854
+ this.emit({
4855
+ type: "raw_log",
4856
+ content: serializeAcpLog({
4857
+ type: "custom",
4858
+ name: "acp_prompt_response",
4859
+ data: {
4860
+ sessionId: this.sessionId,
4861
+ stopReason: response.stopReason,
4862
+ usage: response.usage ?? null,
4863
+ _meta: response._meta
4864
+ }
4865
+ })
4866
+ });
4824
4867
  const isError = response.stopReason === "refusal" || response.stopReason === "cancelled";
4825
- const cost = this.buildCostData(isError);
4868
+ const cost = this.buildCostData(isError, response.usage);
4826
4869
  result = {
4827
4870
  exitCode: isError ? 1 : 0,
4828
4871
  sessionId: this.sessionId,
@@ -4863,12 +4906,16 @@ class ACPSession {
4863
4906
  }
4864
4907
  } catch {}
4865
4908
  }
4866
- buildCostData(isError) {
4909
+ buildCostData(isError, usage) {
4867
4910
  return {
4868
4911
  sessionId: this.sessionId,
4869
4912
  taskId: this.config.taskId,
4870
4913
  agentId: this.config.agentId,
4871
4914
  totalCostUsd: 0,
4915
+ inputTokens: usage?.inputTokens,
4916
+ outputTokens: usage?.outputTokens,
4917
+ cacheReadTokens: usage?.cachedReadTokens ?? undefined,
4918
+ cacheWriteTokens: usage?.cachedWriteTokens ?? undefined,
4872
4919
  durationMs: Date.now() - this.startedAt,
4873
4920
  numTurns: 1,
4874
4921
  model: this.config.model,
@@ -4881,6 +4928,9 @@ class ACPSession {
4881
4928
  return;
4882
4929
  this.completed = true;
4883
4930
  this.completionResolve(result);
4931
+ if (this.ephemeralTokenId) {
4932
+ revokeAcpSessionToken(this.config.apiUrl, this.config.apiKey, this.ephemeralTokenId);
4933
+ }
4884
4934
  }
4885
4935
  }
4886
4936
 
@@ -4912,12 +4962,14 @@ class ACPAdapter {
4912
4962
  });
4913
4963
  const stream = ndJsonStream2(fileSinkWritableStream(proc.stdin), proc.stdout);
4914
4964
  const connection = new ClientSideConnection(() => client2, stream);
4965
+ let ephemeralToken = null;
4915
4966
  try {
4916
4967
  await connection.initialize({
4917
4968
  protocolVersion: PROTOCOL_VERSION,
4918
4969
  clientInfo: { name: "agent-swarm", version: package_default.version },
4919
4970
  clientCapabilities: { session: { configOptions: { boolean: {} } } }
4920
4971
  });
4972
+ ephemeralToken = await mintAcpSessionToken(config.apiUrl, config.apiKey, config.agentId, config.taskId);
4921
4973
  const installedServers = await fetchInstalledMcpServers(config.apiUrl, config.apiKey, config.agentId, "claude");
4922
4974
  const newSession = await connection.newSession({
4923
4975
  cwd: config.cwd,
@@ -4927,7 +4979,7 @@ class ACPAdapter {
4927
4979
  name: "swarm",
4928
4980
  url: `${config.apiUrl.replace(/\/+$/, "")}/mcp`,
4929
4981
  headers: [
4930
- { name: "Authorization", value: `Bearer ${config.apiKey}` },
4982
+ { name: "Authorization", value: `Bearer ${ephemeralToken.plaintext}` },
4931
4983
  { name: "X-Agent-ID", value: config.agentId },
4932
4984
  { name: "X-Source-Task-Id", value: config.taskId }
4933
4985
  ]
@@ -4939,11 +4991,14 @@ class ACPAdapter {
4939
4991
  session = new ACPSession(connection, proc, config, newSession.sessionId, {
4940
4992
  target: target.target,
4941
4993
  configOptions: sanitizeAcpConfigOptions(configOptions)
4942
- });
4994
+ }, ephemeralToken.tokenId);
4943
4995
  for (const event of preSessionEvents)
4944
4996
  session.emitFromAcp(event);
4945
4997
  return session;
4946
4998
  } catch (err) {
4999
+ if (ephemeralToken && !session) {
5000
+ revokeAcpSessionToken(config.apiUrl, config.apiKey, ephemeralToken.tokenId);
5001
+ }
4947
5002
  await terminateProcessGroup(proc.pid);
4948
5003
  throw new Error(`ACP target failed during startup: ${scrubSecrets(formatError(err))}`);
4949
5004
  }
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  resolveSlackUserId
3
- } from "./cli-xytwff7f.js";
4
- import"./cli-z96t0thj.js";
3
+ } from "./cli-dttph1bw.js";
4
+ import"./cli-p1d5b073.js";
5
5
  import {
6
6
  createTaskWithSiblingAwareness
7
- } from "./cli-axs8e6d4.js";
7
+ } from "./cli-53s590z8.js";
8
8
  import {
9
9
  buildCancelledBlocks,
10
10
  ensureSlackThreadTree,
@@ -12,8 +12,9 @@ import {
12
12
  getAgentEmoji,
13
13
  getTaskLink,
14
14
  isSlackRenderV2Enabled
15
- } from "./cli-jmb7g47h.js";
15
+ } from "./cli-b5bq9crk.js";
16
16
  import"./cli-q51yprfq.js";
17
+ import"./cli-9d8ntetj.js";
17
18
  import"./cli-yatxqynm.js";
18
19
  import {
19
20
  cancelTask,
@@ -23,15 +24,15 @@ import {
23
24
  init_context_key,
24
25
  init_db,
25
26
  slackContextKey
26
- } from "./cli-f567d5c1.js";
27
- import"./cli-kpev657q.js";
28
- import"./cli-7fnkj1hj.js";
27
+ } from "./cli-ftht3zzj.js";
28
+ import"./cli-y6b6fb76.js";
29
+ import"./cli-kpv03zhj.js";
29
30
  import"./cli-e29gmdmw.js";
30
31
  import"./cli-c2mk1kgf.js";
31
32
  import"./cli-ej6gbxfb.js";
32
33
  import"./cli-sd0mxa3c.js";
33
34
  import"./cli-w5e1d1vv.js";
34
- import"./cli-dc9k2ba9.js";
35
+ import"./cli-w9kb3bk1.js";
35
36
  import"./cli-xqcq9y5e.js";
36
37
  import"./cli-pzxba7gw.js";
37
38
  import"./cli-zf8jjwex.js";
@@ -3,18 +3,19 @@ import {
3
3
  initSlackApp,
4
4
  startSlackApp,
5
5
  stopSlackApp
6
- } from "./cli-jmb7g47h.js";
6
+ } from "./cli-b5bq9crk.js";
7
7
  import"./cli-q51yprfq.js";
8
+ import"./cli-9d8ntetj.js";
8
9
  import"./cli-yatxqynm.js";
9
- import"./cli-f567d5c1.js";
10
- import"./cli-kpev657q.js";
11
- import"./cli-7fnkj1hj.js";
10
+ import"./cli-ftht3zzj.js";
11
+ import"./cli-y6b6fb76.js";
12
+ import"./cli-kpv03zhj.js";
12
13
  import"./cli-e29gmdmw.js";
13
14
  import"./cli-c2mk1kgf.js";
14
15
  import"./cli-ej6gbxfb.js";
15
16
  import"./cli-sd0mxa3c.js";
16
17
  import"./cli-w5e1d1vv.js";
17
- import"./cli-dc9k2ba9.js";
18
+ import"./cli-w9kb3bk1.js";
18
19
  import"./cli-xqcq9y5e.js";
19
20
  import"./cli-pzxba7gw.js";
20
21
  import"./cli-zf8jjwex.js";
@@ -0,0 +1,236 @@
1
+ import {
2
+ bufferThreadMessage,
3
+ getBufferMessageCount,
4
+ wasEventSeen
5
+ } from "./cli-f146mn5s.js";
6
+ import {
7
+ bufferedFileFailures,
8
+ buildEffectiveText,
9
+ createSlackTaskWithFiles,
10
+ fetchSlackFiles,
11
+ hasOtherUserMention,
12
+ notifySlackFileFailures
13
+ } from "./cli-mmemxxdc.js";
14
+ import"./cli-jsp14b6c.js";
15
+ import {
16
+ resolveSlackUserId,
17
+ rewriteSlackMentions
18
+ } from "./cli-dttph1bw.js";
19
+ import"./cli-p1d5b073.js";
20
+ import"./cli-53s590z8.js";
21
+ import {
22
+ ackSlackMessage,
23
+ ensureSlackThreadTree,
24
+ isSlackRenderV2Enabled,
25
+ require_dist
26
+ } from "./cli-b5bq9crk.js";
27
+ import"./cli-q51yprfq.js";
28
+ import"./cli-9d8ntetj.js";
29
+ import"./cli-yatxqynm.js";
30
+ import {
31
+ getAgentWorkingOnThread,
32
+ getLeadAgent,
33
+ getMostRecentTaskInThread,
34
+ init_context_key,
35
+ init_db,
36
+ reactionName,
37
+ slackContextKey
38
+ } from "./cli-ftht3zzj.js";
39
+ import"./cli-y6b6fb76.js";
40
+ import {
41
+ init_resolver,
42
+ resolveTemplate
43
+ } from "./cli-kpv03zhj.js";
44
+ import"./cli-e29gmdmw.js";
45
+ import"./cli-c2mk1kgf.js";
46
+ import {
47
+ init_env_flag,
48
+ isEnvFlagEnabled
49
+ } from "./cli-ej6gbxfb.js";
50
+ import"./cli-sd0mxa3c.js";
51
+ import"./cli-w5e1d1vv.js";
52
+ import"./cli-w9kb3bk1.js";
53
+ import"./cli-xqcq9y5e.js";
54
+ import"./cli-pzxba7gw.js";
55
+ import"./cli-zf8jjwex.js";
56
+ import"./cli-bd957dgb.js";
57
+ import {
58
+ __callDispose,
59
+ __toESM,
60
+ __using
61
+ } from "./cli-bc05qbss.js";
62
+
63
+ // src/slack/assistant.ts
64
+ init_db();
65
+ init_resolver();
66
+ init_context_key();
67
+ var import_bolt = __toESM(require_dist(), 1);
68
+ init_env_flag();
69
+ var isAdditiveSlack = () => isEnvFlagEnabled("ADDITIVE_SLACK", false);
70
+ var cachedBotUserId = null;
71
+ function createAssistant() {
72
+ return new import_bolt.Assistant({
73
+ threadStarted: async ({ say, setSuggestedPrompts, saveThreadContext }) => {
74
+ try {
75
+ await saveThreadContext();
76
+ if (!isSlackRenderV2Enabled()) {
77
+ const greetingResult = resolveTemplate("slack.assistant.greeting", {});
78
+ await say(greetingResult.text);
79
+ }
80
+ await setSuggestedPrompts({
81
+ title: "Try these:",
82
+ prompts: [
83
+ { title: "Check status", message: "What's the current status of all agents?" },
84
+ { title: "Assign a task", message: "Can you help me with..." },
85
+ { title: "List recent tasks", message: "Show me the most recent tasks" }
86
+ ]
87
+ });
88
+ } catch (error) {
89
+ console.error("[Slack] Assistant threadStarted error:", error);
90
+ }
91
+ },
92
+ threadContextChanged: async ({ saveThreadContext }) => {
93
+ await saveThreadContext();
94
+ },
95
+ userMessage: async ({ message, body, say, setStatus, setTitle, getThreadContext, client }) => {
96
+ const eventId = body?.event_id;
97
+ if (wasEventSeen(eventId)) {
98
+ console.log(`[Slack] dropping Slack retry: event_id=${eventId}`);
99
+ return;
100
+ }
101
+ const safeSetStatus = async (status) => {
102
+ try {
103
+ await setStatus(status);
104
+ } catch (error) {
105
+ console.warn("[Slack] setStatus failed (thread may not be an assistant thread):", error);
106
+ }
107
+ };
108
+ const safeSetTitle = async (title) => {
109
+ try {
110
+ await setTitle(title);
111
+ } catch (error) {
112
+ console.warn("[Slack] setTitle failed (thread may not be an assistant thread):", error);
113
+ }
114
+ };
115
+ try {
116
+ let __stack = [];
117
+ try {
118
+ const msg = message;
119
+ const threadTs = msg.thread_ts || message.ts;
120
+ const channelId = message.channel;
121
+ const messageText = msg.text || "";
122
+ const files = msg.files;
123
+ const userId = msg.user || "";
124
+ if (!cachedBotUserId) {
125
+ try {
126
+ const authResult = await client.auth.test();
127
+ cachedBotUserId = authResult.user_id ?? null;
128
+ } catch (e) {
129
+ console.warn("[Slack] assistant: auth.test() failed — skipping bot-mention check", e);
130
+ }
131
+ }
132
+ if (cachedBotUserId) {
133
+ const botMentioned = messageText.includes(`<@${cachedBotUserId}>`);
134
+ if (!botMentioned && hasOtherUserMention(messageText, cachedBotUserId)) {
135
+ console.log(`[Slack] assistant: skipping message in ${channelId}/${threadTs} — mentions another user, not us`);
136
+ return;
137
+ }
138
+ }
139
+ const requestedByUserId = userId ? await resolveSlackUserId(client, userId, {
140
+ sampleEventType: "assistant_message",
141
+ sampleContext: messageText
142
+ }) : undefined;
143
+ const workingAgent = await getAgentWorkingOnThread(channelId, threadTs);
144
+ if (workingAgent && workingAgent.status !== "offline" && isAdditiveSlack()) {
145
+ const unattached2 = bufferedFileFailures(files);
146
+ bufferThreadMessage(channelId, threadTs, buildEffectiveText(messageText, files, unattached2), userId, message.ts);
147
+ const count = getBufferMessageCount(`${channelId}:${threadTs}`);
148
+ const event = count === 1 ? "accepted" : "buffered";
149
+ await ackSlackMessage(client, channelId, message.ts, reactionName(event), event);
150
+ await notifySlackFileFailures(client, channelId, threadTs, unattached2);
151
+ await safeSetStatus("Queuing follow-up...");
152
+ return;
153
+ }
154
+ if (files?.length)
155
+ await safeSetStatus("Downloading attachments...");
156
+ const inbound = __using(__stack, await fetchSlackFiles(client, files), 1);
157
+ const renderedMessageText = await rewriteSlackMentions(buildEffectiveText(messageText, inbound.files, inbound.failed));
158
+ if (workingAgent && workingAgent.status !== "offline") {
159
+ const latestTask = await getMostRecentTaskInThread(channelId, threadTs);
160
+ const { task: task2, unattached: unattached2 } = await createSlackTaskWithFiles(renderedMessageText, {
161
+ agentId: workingAgent.id,
162
+ source: "slack",
163
+ slackChannelId: channelId,
164
+ slackThreadTs: threadTs,
165
+ slackTriggerMessageTs: message.ts,
166
+ slackUserId: userId,
167
+ parentTaskId: latestTask?.id,
168
+ requestedByUserId,
169
+ contextKey: slackContextKey({ channelId, threadTs })
170
+ }, inbound);
171
+ await ackSlackMessage(client, channelId, message.ts, reactionName("accepted"), "accepted");
172
+ await notifySlackFileFailures(client, channelId, threadTs, unattached2);
173
+ if (isSlackRenderV2Enabled())
174
+ await ensureSlackThreadTree([task2.id]);
175
+ await safeSetStatus("Processing follow-up...");
176
+ return;
177
+ }
178
+ await safeSetStatus("Processing your request...");
179
+ if (renderedMessageText) {
180
+ const title = renderedMessageText.length > 50 ? `${renderedMessageText.slice(0, 47)}...` : renderedMessageText;
181
+ await safeSetTitle(title);
182
+ }
183
+ const ctx = await getThreadContext();
184
+ const channelContext = ctx && typeof ctx === "object" && "channel_id" in ctx && ctx.channel_id ? `
185
+
186
+ [User is viewing channel <#${ctx.channel_id}>]` : "";
187
+ const lead = await getLeadAgent();
188
+ if (!lead) {
189
+ const { task: task2, unattached: unattached2 } = await createSlackTaskWithFiles(renderedMessageText + channelContext, {
190
+ source: "slack",
191
+ slackChannelId: channelId,
192
+ slackThreadTs: threadTs,
193
+ slackTriggerMessageTs: message.ts,
194
+ slackUserId: userId,
195
+ requestedByUserId,
196
+ contextKey: slackContextKey({ channelId, threadTs })
197
+ }, inbound);
198
+ await ackSlackMessage(client, channelId, message.ts, reactionName("accepted"), "accepted");
199
+ await notifySlackFileFailures(client, channelId, threadTs, unattached2);
200
+ if (isSlackRenderV2Enabled()) {
201
+ await ensureSlackThreadTree([task2.id]);
202
+ } else {
203
+ const offlineResult = resolveTemplate("slack.assistant.offline", {});
204
+ await say(offlineResult.text);
205
+ }
206
+ return;
207
+ }
208
+ const { task, unattached } = await createSlackTaskWithFiles(renderedMessageText + channelContext, {
209
+ agentId: lead.id,
210
+ source: "slack",
211
+ slackChannelId: channelId,
212
+ slackThreadTs: threadTs,
213
+ slackTriggerMessageTs: message.ts,
214
+ slackUserId: userId,
215
+ requestedByUserId,
216
+ contextKey: slackContextKey({ channelId, threadTs })
217
+ }, inbound);
218
+ await ackSlackMessage(client, channelId, message.ts, reactionName("accepted"), "accepted");
219
+ await notifySlackFileFailures(client, channelId, threadTs, unattached);
220
+ if (isSlackRenderV2Enabled())
221
+ await ensureSlackThreadTree([task.id]);
222
+ } catch (_catch) {
223
+ var _err = _catch, _hasErr = 1;
224
+ } finally {
225
+ var _promise = __callDispose(__stack, _err, _hasErr);
226
+ _promise && await _promise;
227
+ }
228
+ } catch (error) {
229
+ console.error("[Slack] Assistant userMessage error:", error);
230
+ }
231
+ }
232
+ });
233
+ }
234
+ export {
235
+ createAssistant
236
+ };
@@ -3,21 +3,22 @@ import {
3
3
  getEmbeddingProvider,
4
4
  getMemoryStore,
5
5
  init_constants
6
- } from "./cli-s2f20r5w.js";
7
- import"./cli-fs1kkqgr.js";
6
+ } from "./cli-1th1728g.js";
7
+ import"./cli-m9sxbkhm.js";
8
8
  import"./cli-aehm1rbq.js";
9
+ import"./cli-9d8ntetj.js";
9
10
  import {
10
11
  getDbClient,
11
12
  init_db
12
- } from "./cli-f567d5c1.js";
13
- import"./cli-kpev657q.js";
14
- import"./cli-7fnkj1hj.js";
13
+ } from "./cli-ftht3zzj.js";
14
+ import"./cli-y6b6fb76.js";
15
+ import"./cli-kpv03zhj.js";
15
16
  import"./cli-e29gmdmw.js";
16
17
  import"./cli-c2mk1kgf.js";
17
18
  import"./cli-ej6gbxfb.js";
18
19
  import"./cli-sd0mxa3c.js";
19
20
  import"./cli-w5e1d1vv.js";
20
- import"./cli-dc9k2ba9.js";
21
+ import"./cli-w9kb3bk1.js";
21
22
  import"./cli-xqcq9y5e.js";
22
23
  import"./cli-zf8jjwex.js";
23
24
  import"./cli-bc05qbss.js";
@@ -1,22 +1,23 @@
1
1
  import {
2
2
  embedScript,
3
3
  embeddingProvider
4
- } from "./cli-a0g1gbt5.js";
5
- import"./cli-s2f20r5w.js";
6
- import"./cli-fs1kkqgr.js";
4
+ } from "./cli-hbv0kq6w.js";
5
+ import"./cli-1th1728g.js";
6
+ import"./cli-m9sxbkhm.js";
7
7
  import"./cli-aehm1rbq.js";
8
+ import"./cli-9d8ntetj.js";
8
9
  import {
9
10
  getDbClient,
10
11
  init_db
11
- } from "./cli-f567d5c1.js";
12
- import"./cli-kpev657q.js";
13
- import"./cli-7fnkj1hj.js";
12
+ } from "./cli-ftht3zzj.js";
13
+ import"./cli-y6b6fb76.js";
14
+ import"./cli-kpv03zhj.js";
14
15
  import"./cli-e29gmdmw.js";
15
16
  import"./cli-c2mk1kgf.js";
16
17
  import"./cli-ej6gbxfb.js";
17
18
  import"./cli-sd0mxa3c.js";
18
19
  import"./cli-w5e1d1vv.js";
19
- import"./cli-dc9k2ba9.js";
20
+ import"./cli-w9kb3bk1.js";
20
21
  import"./cli-xqcq9y5e.js";
21
22
  import"./cli-zf8jjwex.js";
22
23
  import"./cli-bc05qbss.js";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  getDbClient,
3
3
  init_db
4
- } from "./cli-f567d5c1.js";
5
- import"./cli-kpev657q.js";
6
- import"./cli-7fnkj1hj.js";
4
+ } from "./cli-ftht3zzj.js";
5
+ import"./cli-y6b6fb76.js";
6
+ import"./cli-kpv03zhj.js";
7
7
  import"./cli-e29gmdmw.js";
8
8
  import"./cli-c2mk1kgf.js";
9
9
  import"./cli-ej6gbxfb.js";
10
10
  import"./cli-sd0mxa3c.js";
11
11
  import"./cli-w5e1d1vv.js";
12
- import"./cli-dc9k2ba9.js";
12
+ import"./cli-w9kb3bk1.js";
13
13
  import {
14
14
  init_secret_scrubber,
15
15
  scrubSecrets
@@ -17,8 +17,9 @@ import {
17
17
  resolveClaudeBridgeEnabled,
18
18
  resolveClaudeQueueSteeringOverride,
19
19
  runClaudeSessionSummary
20
- } from "./cli-v0asr5yv.js";
21
- import"./cli-anktweqr.js";
20
+ } from "./cli-719k9j2c.js";
21
+ import"./cli-89xfd7f9.js";
22
+ import"./cli-d8brzxjb.js";
22
23
  import"./cli-b5b30yzk.js";
23
24
  import"./cli-rvqz34y5.js";
24
25
  import"./cli-hp1n6zq3.js";
@@ -34,16 +35,17 @@ import"./cli-e7d20cty.js";
34
35
  import"./cli-3dpj7atn.js";
35
36
  import"./cli-y89t4nca.js";
36
37
  import"./cli-f14fvzag.js";
37
- import"./cli-fs1kkqgr.js";
38
+ import"./cli-m9sxbkhm.js";
38
39
  import"./cli-bsakbbkm.js";
39
- import"./cli-kpev657q.js";
40
- import"./cli-7fnkj1hj.js";
40
+ import"./cli-9d8ntetj.js";
41
+ import"./cli-y6b6fb76.js";
42
+ import"./cli-kpv03zhj.js";
41
43
  import"./cli-e29gmdmw.js";
42
44
  import"./cli-c2mk1kgf.js";
43
45
  import"./cli-ej6gbxfb.js";
44
46
  import"./cli-sd0mxa3c.js";
45
47
  import"./cli-w5e1d1vv.js";
46
- import"./cli-dc9k2ba9.js";
48
+ import"./cli-w9kb3bk1.js";
47
49
  import"./cli-xqcq9y5e.js";
48
50
  import"./cli-knd276ed.js";
49
51
  import"./cli-c4eeb5q2.js";
@@ -4,6 +4,9 @@ import {
4
4
  ConflictError,
5
5
  toFile
6
6
  } from "./cli-j490egb5.js";
7
+ import {
8
+ buildSkillContent
9
+ } from "./cli-ep19k3mb.js";
7
10
  import"./cli-e7d20cty.js";
8
11
  import {
9
12
  getApiKey
@@ -85,7 +88,9 @@ async function promptHiddenInput(label, _defaultValue, helpText) {
85
88
  // src/commands/claude-managed-setup.ts
86
89
  var DEFAULT_AGENT_MODEL = "claude-sonnet-5";
87
90
  var SKILLS_DIR_RELATIVE = "plugin/commands";
91
+ var SEEDED_SKILLS_DIR_RELATIVE = "templates/skills";
88
92
  var SKILLS_BETA_HEADER = "skills-2025-10-02";
93
+ var SEEDED_SKILLS_TO_UPLOAD = ["work-on-task", "review-offered-task"];
89
94
  var CONFIG_KEY_AGENT_ID = "managed_agent_id";
90
95
  var CONFIG_KEY_ENVIRONMENT_ID = "managed_environment_id";
91
96
  var CONFIG_KEY_API_KEY = "anthropic_api_key";
@@ -122,7 +127,8 @@ Options:
122
127
 
123
128
  This command:
124
129
  1. Creates an Anthropic-side environment (cloud, unrestricted networking).
125
- 2. Uploads each plugin/commands/*.md as a managed-agents skill (skips on 409).
130
+ 2. Uploads each plugin/commands/*.md plus the turn-prompt skills under
131
+ templates/skills/ as managed-agents skills (skips on 409).
126
132
  3. Creates a managed-agents agent referencing the uploaded skills + the
127
133
  swarm MCP server (MCP_BASE_URL/mcp).
128
134
  4. Persists the resulting IDs to swarm_config (managed_agent_id,
@@ -186,6 +192,16 @@ async function loadSkillFiles(skillsDir) {
186
192
  }
187
193
  return out;
188
194
  }
195
+ async function loadSeededSkillFiles(templatesDir, names = SEEDED_SKILLS_TO_UPLOAD) {
196
+ const out = [];
197
+ for (const name of names) {
198
+ const dir = path.join(templatesDir, name);
199
+ const config = JSON.parse(await readFile(path.join(dir, "config.json"), "utf8"));
200
+ const body = await readFile(path.join(dir, "content.md"), "utf8");
201
+ out.push({ slug: name, absPath: dir, content: buildSkillContent(config, body) });
202
+ }
203
+ return out;
204
+ }
189
205
  async function uploadSkill(client, slug, content, log, existingByTitle) {
190
206
  if (existingByTitle?.has(slug)) {
191
207
  const id = existingByTitle.get(slug);
@@ -317,8 +333,10 @@ async function runClaudeManagedSetupFlow(config, deps = {}) {
317
333
  const fetchCfg = deps.fetchConfig ?? fetchConfigByKey;
318
334
  const upsert = deps.upsert ?? upsertConfig;
319
335
  const loadSkills = deps.loadSkills ?? loadSkillFiles;
336
+ const loadSeededSkills = deps.loadSeededSkills ?? loadSeededSkillFiles;
320
337
  const uploadOne = deps.uploadOne ?? uploadSkill;
321
338
  const skillsDir = deps.skillsDir ?? path.resolve(process.cwd(), SKILLS_DIR_RELATIVE);
339
+ const seededSkillsDir = deps.seededSkillsDir ?? path.resolve(process.cwd(), SEEDED_SKILLS_DIR_RELATIVE);
322
340
  if (!config.force) {
323
341
  const existing = await fetchCfg(config.apiUrl, config.apiKey, CONFIG_KEY_AGENT_ID);
324
342
  if (existing?.value) {
@@ -342,8 +360,11 @@ async function runClaudeManagedSetupFlow(config, deps = {}) {
342
360
  }
343
361
  });
344
362
  log(` + environment id=${env.id}`);
345
- log(`Uploading skills from ${skillsDir} ...`);
346
- const skillFiles = await loadSkills(skillsDir);
363
+ log(`Uploading skills from ${skillsDir} and ${seededSkillsDir} ...`);
364
+ const skillFiles = [
365
+ ...await loadSkills(skillsDir),
366
+ ...await loadSeededSkills(seededSkillsDir)
367
+ ];
347
368
  log(` found ${skillFiles.length} skill markdown file(s)`);
348
369
  const existingByTitle = new Map;
349
370
  try {