@desplega.ai/agent-swarm 1.131.1 → 1.133.1

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 (250) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-jgcy477k.js → actions-cskpayd7.js} +11 -9
  3. package/dist/{anthropic-messages-rjmd2e7n.js → anthropic-messages-tpxtbx7b.js} +28 -12
  4. package/dist/{app-5da78bd4.js → app-ang77nbb.js} +8 -6
  5. package/dist/{assistant-f2vkk1hj.js → assistant-b4exebdw.js} +16 -13
  6. package/dist/{azure-openai-responses-c0yjmbfd.js → azure-openai-responses-m3921t4y.js} +8 -8
  7. package/dist/{boot-reembed-q7mp8hcr.js → boot-reembed-ps2aftpb.js} +9 -7
  8. package/dist/{boot-reembed-xmw8wcx6.js → boot-reembed-znyk3nxb.js} +10 -8
  9. package/dist/{boot-scrub-logs-dsqrv79n.js → boot-scrub-logs-2ax3xgz1.js} +7 -5
  10. package/dist/{claude-adapter-q6x7yspx.js → claude-adapter-3ct8kgxw.js} +13 -9
  11. package/dist/{claude-managed-adapter-wsg37bt8.js → claude-managed-adapter-akx4ntf2.js} +2 -2
  12. package/dist/{cli-114fvtmj.js → cli-13w2hwkh.js} +1 -1
  13. package/dist/{cli-4rkfg1wx.js → cli-1532szb1.js} +8 -6
  14. package/dist/{cli-g74vpjp2.js → cli-23d3rseb.js} +33 -17
  15. package/dist/{cli-3ty0t082.js → cli-2a75z9c5.js} +196 -7
  16. package/dist/{cli-pxfxkx5m.js → cli-2t30rj0d.js} +1 -1
  17. package/dist/{cli-rgrfzg8z.js → cli-3c3tdgab.js} +10 -10
  18. package/dist/{cli-6dp3ydxa.js → cli-3zzxa9jz.js} +15 -11
  19. package/dist/{cli-2f07m0xr.js → cli-4k6233gc.js} +1 -1
  20. package/dist/{cli-t8d349cy.js → cli-5p2tz05r.js} +2 -2
  21. package/dist/{cli-w2wxd56v.js → cli-5z2yhzvd.js} +127 -49
  22. package/dist/{cli-kd1ck4rx.js → cli-6zfq31nj.js} +9 -3
  23. package/dist/{cli-er3a4bb0.js → cli-8m9de10a.js} +6 -1
  24. package/dist/{cli-gm9cbhbq.js → cli-8t14vg9w.js} +27 -8
  25. package/dist/{cli-ccf9vy7q.js → cli-9nfc2es8.js} +6 -6
  26. package/dist/{cli-ycrnynpw.js → cli-9ta3pwms.js} +11 -4
  27. package/dist/{cli-7dm2zqy2.js → cli-avr0ns7z.js} +2 -2
  28. package/dist/{cli-n6h8qt8n.js → cli-c897wyhw.js} +12 -7
  29. package/dist/{cli-gecy3z2s.js → cli-dgk20ft7.js} +4 -4
  30. package/dist/cli-et1bbwqf.js +13 -0
  31. package/dist/{cli-bqw0mt1n.js → cli-f5w27pj3.js} +3 -3
  32. package/dist/{cli-2wadx7p2.js → cli-f8dre4bz.js} +2 -2
  33. package/dist/{cli-6pcz9h15.js → cli-ftwpv451.js} +170 -387
  34. package/dist/{cli-nrkkknat.js → cli-g854s2dw.js} +1 -1
  35. package/dist/{cli-793jvbsh.js → cli-ggmdxeqa.js} +13 -8
  36. package/dist/cli-hkwfe3qj.js +530 -0
  37. package/dist/{cli-5dsbcjjw.js → cli-jqwnqx9m.js} +1 -1
  38. package/dist/{cli-9dr5qzc0.js → cli-ny122vh4.js} +21 -10
  39. package/dist/{cli-sy7jk3tx.js → cli-pvzqhbs9.js} +2 -2
  40. package/dist/cli-pzq5rjym.js +15 -0
  41. package/dist/{cli-xpxde7rm.js → cli-qes8ef67.js} +48 -30
  42. package/dist/{cli-sajgpkqg.js → cli-qs9hhsvb.js} +1723 -325
  43. package/dist/cli-qys3rhhz.js +27 -0
  44. package/dist/{cli-se2p6rkb.js → cli-r6tpm37h.js} +2 -2
  45. package/dist/{cli-g1re8r05.js → cli-rch80c76.js} +2 -2
  46. package/dist/{cli-0258203w.js → cli-rpp0x4zy.js} +137 -17
  47. package/dist/{cli-5n6e5yfn.js → cli-s1k0wxhe.js} +12 -7
  48. package/dist/{cli-aca5pz9f.js → cli-sfd9v7sj.js} +1 -1
  49. package/dist/{cli-2rk0cx7s.js → cli-sh3ytsyj.js} +49 -39
  50. package/dist/{cli-zsspvvm2.js → cli-w1mp9jy7.js} +1 -1
  51. package/dist/{cli-6dpytdr2.js → cli-xdb4mpnp.js} +2 -2
  52. package/dist/{cli-57khth04.js → cli-xmyqv00z.js} +120 -4
  53. package/dist/{cli-jbcva0xj.js → cli-xv8t8p3c.js} +56 -31
  54. package/dist/{cli-f1am9szv.js → cli-xxv5pn4f.js} +1 -1
  55. package/dist/{cli-jevgrb4w.js → cli-y08jvxzx.js} +601 -140
  56. package/dist/{cli-50vbt00w.js → cli-zfbf3srj.js} +650 -245
  57. package/dist/{cli-ygrbmjja.js → cli-zmq89k34.js} +6 -6
  58. package/dist/cli.js +22 -22
  59. package/dist/{codex-adapter-htjkn22b.js → codex-adapter-bef93pq4.js} +9 -7
  60. package/dist/{codex-hook-hq54t9ft.js → codex-hook-e8dba65p.js} +3 -2
  61. package/dist/{codex-session-runner-wjfc7smz.js → codex-session-runner-5pkxys7x.js} +9 -7
  62. package/dist/{commands-xhrfwqsk.js → commands-e23s1bff.js} +9 -7
  63. package/dist/{db-anvwb22f.js → db-m3nabatn.js} +17 -5
  64. package/dist/{google-generative-ai-hysrd8qx.js → google-generative-ai-8maw55xh.js} +11 -8
  65. package/dist/{google-vertex-p5arrga0.js → google-vertex-8vjqmx8g.js} +11 -8
  66. package/dist/{handlers-b9kbc621.js → handlers-akazedvr.js} +16 -13
  67. package/dist/{hook-xkvn6c5h.js → hook-ze4cpf0v.js} +55 -41
  68. package/dist/{hook-cgk4wxmx.js → hook-zfqyyenw.js} +10 -7
  69. package/dist/{http-9xh6ta4r.js → http-sja0n6kw.js} +608 -147
  70. package/dist/index-0gr8vd6k.js +39 -0
  71. package/dist/{index-eaxyqcmj.js → index-354vkhdv.js} +154 -74
  72. package/dist/{index-070v4eyb.js → index-5qthek5t.js} +2 -3
  73. package/dist/{index-25wjsdv2.js → index-95t3cqq5.js} +16 -14
  74. package/dist/{index-9znhar3x.js → index-sea66619.js} +3 -4
  75. package/dist/{index-ywds4812.js → index-vpn36mma.js} +15 -13
  76. package/dist/{keepalive-vc7s2aa6.js → keepalive-3sp3meqj.js} +16 -10
  77. package/dist/{lead-r2yqfmx7.js → lead-mefeewkf.js} +39 -36
  78. package/dist/{maintenance-mw70k468.js → maintenance-8tbrbjfg.js} +10 -8
  79. package/dist/mistral-conversations-00zrp7g4.js +717 -0
  80. package/dist/{oauth-refresh-sweep-yecgjpk4.js → oauth-refresh-sweep-srjwjz6m.js} +8 -6
  81. package/dist/{onboard-xj0f73f9.js → onboard-mt2tgyjg.js} +7 -7
  82. package/dist/{openai-codex-responses-5g27pd6f.js → openai-codex-responses-map32h2s.js} +18 -18
  83. package/dist/{openai-completions-3vkmfz2a.js → openai-completions-w871b1cq.js} +12 -11
  84. package/dist/{openai-responses-dg4t7j7a.js → openai-responses-b0j7xnw8.js} +15 -12
  85. package/dist/{opencode-adapter-1gm38aba.js → opencode-adapter-b39jefcj.js} +5 -3
  86. package/dist/{openrouter-images-g3gm8931.js → openrouter-images-srfkywy0.js} +4 -4
  87. package/dist/{otel-impl-nxg0n0b3.js → otel-impl-z6pxt3j9.js} +224 -9
  88. package/dist/{pi-mono-adapter-4g5kby31.js → pi-mono-adapter-btvqmj58.js} +1138 -408
  89. package/dist/{pricing-refresh-2cn6effr.js → pricing-refresh-kyq8cezh.js} +9 -7
  90. package/dist/{rbac-roles-1hcxdtg7.js → rbac-roles-3ryfrfna.js} +8 -6
  91. package/dist/{rbac-roles-vynday5g.js → rbac-roles-6qfvbee1.js} +9 -7
  92. package/dist/{render-v2-tc0bh8kk.js → render-v2-p61ppjey.js} +8 -6
  93. package/dist/{seed-pricing-8kceb43f.js → seed-pricing-j56bbj4y.js} +8 -6
  94. package/dist/{setup-46xtgq3c.js → setup-tpr6c1kn.js} +2 -2
  95. package/dist/{worker-qrbz6hmv.js → worker-9w1v0mbb.js} +39 -36
  96. package/openapi.json +542 -16
  97. package/package.json +6 -6
  98. package/src/be/budget-refusal-notify.ts +14 -7
  99. package/src/be/db-queries/task-artifacts.ts +182 -0
  100. package/src/be/db.ts +783 -169
  101. package/src/be/events.ts +5 -0
  102. package/src/be/migrations/131_session_logs_created_at_index.sql +2 -0
  103. package/src/be/migrations/132_multi_runtime_instances.sql +44 -0
  104. package/src/be/migrations/133_workflow_run_trigger_type.sql +12 -0
  105. package/src/be/migrations/135_backfill_task_pull_request_attachments.sql +228 -0
  106. package/src/be/migrations/136_task_requester_provenance.sql +37 -0
  107. package/src/be/migrations/137_memory_retrieval_composite_index.sql +1 -0
  108. package/src/be/multi-runtime.ts +430 -0
  109. package/src/be/scripts/extract-schema.ts +3 -1
  110. package/src/be/scripts/typecheck.ts +3 -2
  111. package/src/be/seed-skills/index.ts +7 -0
  112. package/src/be/steering.ts +1 -1
  113. package/src/be/swarm-config-guard.ts +18 -0
  114. package/src/commands/credential-wait.ts +54 -0
  115. package/src/commands/onboard/steps/core-credentials.tsx +1 -1
  116. package/src/commands/onboard/steps/custom-templates.tsx +1 -1
  117. package/src/commands/onboard/steps/harness-credentials.tsx +1 -1
  118. package/src/commands/onboard/steps/health-check.tsx +1 -1
  119. package/src/commands/onboard/steps/start.tsx +1 -1
  120. package/src/commands/profile-sync-audit.ts +56 -0
  121. package/src/commands/profile-sync.ts +287 -15
  122. package/src/commands/provider-credentials.ts +46 -19
  123. package/src/commands/runner.ts +196 -33
  124. package/src/commands/templates.ts +22 -0
  125. package/src/github/handlers.ts +55 -9
  126. package/src/heartbeat/heartbeat.ts +71 -12
  127. package/src/hooks/hook.ts +49 -33
  128. package/src/http/active-sessions.ts +11 -1
  129. package/src/http/agents.ts +261 -30
  130. package/src/http/all-routes.ts +1 -0
  131. package/src/http/config.ts +13 -2
  132. package/src/http/core.ts +102 -4
  133. package/src/http/db-query-bounded.ts +313 -0
  134. package/src/http/db-query-shared.ts +249 -0
  135. package/src/http/db-query.ts +54 -42
  136. package/src/http/events.ts +2 -0
  137. package/src/http/index.ts +22 -2
  138. package/src/http/mcp-bridge.ts +37 -3
  139. package/src/http/mcp.ts +12 -0
  140. package/src/http/memory.ts +13 -2
  141. package/src/http/openapi.ts +1 -0
  142. package/src/http/poll.ts +70 -27
  143. package/src/http/route-def.ts +43 -9
  144. package/src/http/schedules.ts +2 -2
  145. package/src/http/scripts.ts +10 -1
  146. package/src/http/session-data.ts +44 -0
  147. package/src/http/tasks.ts +2 -2
  148. package/src/http/users.ts +74 -52
  149. package/src/http/webhooks.ts +5 -2
  150. package/src/http/workflows.ts +1 -4
  151. package/src/integrations/kapso/inbound.ts +1 -1
  152. package/src/jira/outbound.ts +39 -8
  153. package/src/jira/webhook-lifecycle.ts +13 -2
  154. package/src/linear/outbound.ts +43 -10
  155. package/src/oauth/keepalive.ts +13 -2
  156. package/src/prompts/base-prompt.ts +1 -1
  157. package/src/prompts/session-templates.ts +45 -7
  158. package/src/providers/claude-adapter.ts +6 -0
  159. package/src/providers/codex-adapter.ts +6 -0
  160. package/src/providers/opencode-adapter.ts +6 -0
  161. package/src/providers/pi-mono-adapter.ts +8 -0
  162. package/src/providers/pricing-sources.md +24 -0
  163. package/src/queue-stall-alarm.ts +216 -0
  164. package/src/scheduler/scheduler.ts +7 -0
  165. package/src/script-workflows/executor.ts +2 -1
  166. package/src/scripts-runtime/executors/native.ts +2 -1
  167. package/src/scripts-runtime/executors/types.ts +5 -0
  168. package/src/scripts-runtime/loader.ts +5 -0
  169. package/src/scripts-runtime/swarm-config.ts +8 -0
  170. package/src/scripts-runtime/swarm-sdk.ts +7 -0
  171. package/src/scripts-runtime/types/stdlib.d.ts +1 -0
  172. package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
  173. package/src/tests/budget-claim-gate.test.ts +20 -0
  174. package/src/tests/claude-adapter.test.ts +32 -0
  175. package/src/tests/codex-adapter.test.ts +30 -0
  176. package/src/tests/create-task-validation.test.ts +128 -0
  177. package/src/tests/credential-wait.test.ts +67 -1
  178. package/src/tests/db-query.test.ts +711 -2
  179. package/src/tests/http-api-integration.test.ts +81 -0
  180. package/src/tests/http-users.test.ts +166 -1
  181. package/src/tests/identity-field-budget.test.ts +176 -0
  182. package/src/tests/mcp-tools-user.test.ts +34 -0
  183. package/src/tests/mcp-user-route.test.ts +17 -0
  184. package/src/tests/metrics-http.test.ts +44 -0
  185. package/src/tests/multi-runtime-registration.test.ts +2703 -0
  186. package/src/tests/openapi-response-contract.test.ts +24 -0
  187. package/src/tests/openapi-route-headers.test.ts +83 -0
  188. package/src/tests/opencode-adapter.test.ts +40 -0
  189. package/src/tests/outbound-listener-rejection.test.ts +138 -0
  190. package/src/tests/pi-mono-adapter.test.ts +98 -0
  191. package/src/tests/profile-sync-warning.test.ts +277 -0
  192. package/src/tests/profile-sync.test.ts +232 -6
  193. package/src/tests/prompt-template-session.test.ts +32 -3
  194. package/src/tests/queue-stall-alarm.test.ts +226 -0
  195. package/src/tests/requester-comms.test.ts +32 -0
  196. package/src/tests/runner-requester-profile.test.ts +38 -0
  197. package/src/tests/schedule-target-type.test.ts +38 -0
  198. package/src/tests/script-connections.test.ts +13 -1
  199. package/src/tests/scripts-runtime-identity.test.ts +375 -0
  200. package/src/tests/scripts-runtime.test.ts +4 -3
  201. package/src/tests/sdk-allowlist.test.ts +47 -0
  202. package/src/tests/send-task-requested-by.test.ts +9 -0
  203. package/src/tests/session-costs.test.ts +506 -1
  204. package/src/tests/shutdown-error-scrubbing.test.ts +92 -0
  205. package/src/tests/steering-core.test.ts +28 -0
  206. package/src/tests/task-artifacts.test.ts +386 -0
  207. package/src/tests/task-attachments-schema.test.ts +17 -1
  208. package/src/tests/task-completion-idempotency.test.ts +57 -0
  209. package/src/tests/task-pull-request-backfill-migration.test.ts +321 -0
  210. package/src/tests/update-profile-api.test.ts +67 -39
  211. package/src/tests/update-profile-auth.test.ts +48 -4
  212. package/src/tests/user-attribution-migration.test.ts +54 -0
  213. package/src/tests/user-identity.test.ts +72 -0
  214. package/src/tests/workflow-async-v2.test.ts +39 -1
  215. package/src/tests/workflow-http-v2.test.ts +3 -2
  216. package/src/tests/workflow-mcp-trigger-schema.test.ts +52 -3
  217. package/src/tests/workflow-retry-v2.test.ts +45 -1
  218. package/src/tools/db-query.ts +18 -8
  219. package/src/tools/get-task-details.ts +24 -3
  220. package/src/tools/manage-user.ts +36 -3
  221. package/src/tools/poll-task.ts +93 -14
  222. package/src/tools/script-common.ts +5 -0
  223. package/src/tools/send-task.ts +1 -4
  224. package/src/tools/store-progress.ts +13 -2
  225. package/src/tools/swarm-config/delete-config.ts +9 -0
  226. package/src/tools/swarm-config/set-config.ts +3 -2
  227. package/src/tools/task-action.ts +30 -0
  228. package/src/tools/task-tool-ctx.ts +8 -1
  229. package/src/tools/update-profile.ts +16 -21
  230. package/src/tools/utils.ts +5 -0
  231. package/src/tools/workflows/trigger-workflow.ts +7 -1
  232. package/src/types.ts +165 -1
  233. package/src/utils/github-pull-request.ts +76 -0
  234. package/src/utils/identity-field-budget.ts +73 -0
  235. package/src/utils/multi-runtime.ts +20 -0
  236. package/src/utils/requester-comms.ts +24 -0
  237. package/src/workflows/engine.ts +22 -2
  238. package/src/workflows/resume.ts +19 -11
  239. package/src/workflows/retry-poller.ts +1 -0
  240. package/src/workflows/triggers.ts +4 -0
  241. package/templates/skills/db-query-guidance/config.json +14 -0
  242. package/templates/skills/db-query-guidance/content.md +57 -0
  243. package/dist/cli-3te4jdkm.js +0 -225
  244. package/dist/cli-bmze986d.js +0 -7
  245. package/dist/cli-kh3ba2zf.js +0 -607
  246. package/dist/index-vg4snay4.js +0 -36
  247. package/dist/mistral-conversations-81fwav0j.js +0 -30578
  248. package/dist/otel-nqa12wf3.js +0 -3149
  249. package/dist/{cli-wz9115x0.js → cli-zr9q011v.js} +6 -6
  250. package/dist/{pi-messages-tqj8q6b9.js → pi-messages-sssy32x4.js} +3 -3
@@ -0,0 +1,27 @@
1
+ import {
2
+ __esm
3
+ } from "./cli-p9swy5t3.js";
4
+
5
+ // src/utils/env-flag.ts
6
+ function parseEnvFlag(raw, defaultValue) {
7
+ if (raw === undefined || raw === null)
8
+ return defaultValue;
9
+ const normalized = raw.trim().toLowerCase();
10
+ if (normalized === "")
11
+ return defaultValue;
12
+ if (TRUTHY.has(normalized))
13
+ return true;
14
+ if (FALSY.has(normalized))
15
+ return false;
16
+ return defaultValue;
17
+ }
18
+ function isEnvFlagEnabled(key, defaultValue, env = process.env) {
19
+ return parseEnvFlag(env[key], defaultValue);
20
+ }
21
+ var TRUTHY, FALSY;
22
+ var init_env_flag = __esm(() => {
23
+ TRUTHY = new Set(["true", "1"]);
24
+ FALSY = new Set(["false", "0"]);
25
+ });
26
+
27
+ export { parseEnvFlag, isEnvFlagEnabled, init_env_flag };
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  contentSha256,
3
3
  init_profile_sync
4
- } from "./cli-0258203w.js";
4
+ } from "./cli-rpp0x4zy.js";
5
5
  import {
6
6
  getDb,
7
7
  init_db,
8
8
  isSqliteVecAvailable
9
- } from "./cli-50vbt00w.js";
9
+ } from "./cli-zfbf3srj.js";
10
10
  import {
11
11
  __esm,
12
12
  __export,
@@ -4,11 +4,11 @@ import {
4
4
  getEmbeddingProvider,
5
5
  init_embedding,
6
6
  serializeEmbedding
7
- } from "./cli-se2p6rkb.js";
7
+ } from "./cli-r6tpm37h.js";
8
8
  import {
9
9
  getDb,
10
10
  init_db
11
- } from "./cli-50vbt00w.js";
11
+ } from "./cli-zfbf3srj.js";
12
12
  import {
13
13
  init_secret_scrubber,
14
14
  scrubSecrets
@@ -1,3 +1,12 @@
1
+ import {
2
+ IDENTITY_FIELD_BUDGETS,
3
+ init_identity_field_budget,
4
+ init_templates
5
+ } from "./cli-hkwfe3qj.js";
6
+ import {
7
+ init_resolver,
8
+ resolveTemplateAsync
9
+ } from "./cli-qes8ef67.js";
1
10
  import {
2
11
  MAX_PROFILE_FILE_LENGTH,
3
12
  init_constants
@@ -14,6 +23,100 @@ import {
14
23
  function contentSha256(content) {
15
24
  return new Bun.CryptoHasher("sha256").update(content).digest("hex");
16
25
  }
26
+ async function renderProfileSyncRejectionBanner(event) {
27
+ const data = event.data;
28
+ const field = data?.field;
29
+ const diskSize = data?.diskSize;
30
+ const dbSize = data?.dbSize;
31
+ const budget = data?.budget;
32
+ const delta = data?.delta;
33
+ if (typeof field !== "string" || !(field in PROFILE_FIELD_DETAILS) || typeof diskSize !== "number" || typeof dbSize !== "number" || typeof budget !== "number" || typeof delta !== "number") {
34
+ return null;
35
+ }
36
+ const details = PROFILE_FIELD_DETAILS[field];
37
+ const result = await resolveTemplateAsync("task.profile_sync_rejection", {
38
+ timestamp: event.createdAt,
39
+ field_label: details.label,
40
+ disk_size: diskSize,
41
+ db_size: dbSize,
42
+ budget,
43
+ delta: delta >= 0 ? `+${delta}` : String(delta),
44
+ event_id: event.id,
45
+ recovery_guidance: details.recoveryGuidance
46
+ });
47
+ return result.skipped ? null : result.text;
48
+ }
49
+ async function fetchProfileSyncRejectionBanner(config, fetchImpl = fetch, readFile = readFileIfExists) {
50
+ try {
51
+ const responses = await Promise.all(Object.keys(IDENTITY_FIELD_BUDGETS).map(async (field) => {
52
+ const fetchLatestEvent = async (event) => {
53
+ const query = new URLSearchParams({
54
+ event,
55
+ agentId: config.agentId,
56
+ dataField: field,
57
+ limit: "1"
58
+ });
59
+ const response = await fetchImpl(`${config.apiUrl}/api/events?${query}`, {
60
+ headers: {
61
+ Authorization: `Bearer ${config.apiKey}`,
62
+ "X-Agent-ID": config.agentId
63
+ }
64
+ });
65
+ if (!response.ok)
66
+ return null;
67
+ const payload = await response.json();
68
+ return payload.events?.[0] ?? null;
69
+ };
70
+ const [rejection, reconciliation] = await Promise.all([
71
+ fetchLatestEvent("system.profile_sync_rejected"),
72
+ fetchLatestEvent("system.profile_sync_reconciled")
73
+ ]);
74
+ return { rejection, reconciliation };
75
+ }));
76
+ const latestEvents = responses.flatMap(({ rejection, reconciliation }) => rejection ? [{ rejection, reconciliation }] : []);
77
+ if (latestEvents.length === 0)
78
+ return "";
79
+ let profile = null;
80
+ const profileResponse = await fetchImpl(`${config.apiUrl}/me`, {
81
+ headers: {
82
+ Authorization: `Bearer ${config.apiKey}`,
83
+ "X-Agent-ID": config.agentId
84
+ }
85
+ });
86
+ if (profileResponse.ok) {
87
+ profile = await profileResponse.json();
88
+ }
89
+ let disk = null;
90
+ if (config.claudeMdPath) {
91
+ disk = {
92
+ soulMd: await readFile(SOUL_MD_PATH),
93
+ identityMd: await readFile(IDENTITY_MD_PATH),
94
+ claudeMd: await readFile(config.claudeMdPath),
95
+ toolsMd: await readFile(TOOLS_MD_PATH)
96
+ };
97
+ }
98
+ const unresolved = latestEvents.flatMap(({ rejection, reconciliation }) => {
99
+ if (reconciliation && reconciliation.createdAt > rejection.createdAt)
100
+ return [];
101
+ const event = rejection;
102
+ const field = event.data?.field;
103
+ const rejectedDbHash = event.data?.dbHash;
104
+ const currentValue = typeof field === "string" ? profile?.[field] : undefined;
105
+ const diskValue = typeof field === "string" ? disk?.[field] : undefined;
106
+ if (typeof currentValue === "string" && diskValue === currentValue)
107
+ return [];
108
+ return !(typeof rejectedDbHash === "string" && typeof currentValue === "string" && contentSha256(currentValue) !== rejectedDbHash) ? [event] : [];
109
+ });
110
+ const banners = await Promise.all(unresolved.map(renderProfileSyncRejectionBanner));
111
+ return banners.filter((banner) => banner !== null).join("");
112
+ } catch {
113
+ return "";
114
+ }
115
+ }
116
+ async function prependProfileSyncRejectionBanner(prompt, config, fetchImpl = fetch, readFile = readFileIfExists) {
117
+ const banner = await fetchProfileSyncRejectionBanner(config, fetchImpl, readFile);
118
+ return banner ? { prompt: banner + prompt, injected: true } : { prompt, injected: false };
119
+ }
17
120
  async function writeIdentityBaselines(baselines) {
18
121
  await Bun.write(IDENTITY_BASELINES_PATH, JSON.stringify(baselines));
19
122
  }
@@ -47,6 +150,12 @@ function resolveClaudeMdPath(completedProviders) {
47
150
  const anyNonClaude = completedProviders.some((p) => p !== "claude");
48
151
  return anyNonClaude ? WORKSPACE_CLAUDE_MD_PATH : CLAUDE_MD_PATH;
49
152
  }
153
+ function buildIndependentIdentityPayloads(updates, changeSource) {
154
+ return Object.entries(updates).map(([field, value]) => ({
155
+ label: `identity.${field}`,
156
+ body: { [field]: value, changeSource }
157
+ }));
158
+ }
50
159
  function extractSetupScriptContent(raw, agentId = "unknown") {
51
160
  if (!raw.trim())
52
161
  return null;
@@ -70,9 +179,7 @@ function buildIdentityPayload(files, baselines, agentId = "unknown") {
70
179
  const updates = {};
71
180
  if (files.soulMd !== undefined) {
72
181
  const content = files.soulMd;
73
- if (baselines?.soulMd && contentSha256(content) === baselines.soulMd) {} else if (content.length > MAX_PROFILE_FILE_LENGTH) {
74
- warnProfileFileTooLarge(agentId, "soulMd", content.length);
75
- } else if (content.trim()) {
182
+ if (baselines?.soulMd && contentSha256(content) === baselines.soulMd) {} else if (content.trim()) {
76
183
  if (content.length < IDENTITY_FILE_MIN_LENGTH) {
77
184
  console.error(`[profile-sync] Skipping SOUL.md sync: content too short (${content.length} chars, minimum ${IDENTITY_FILE_MIN_LENGTH}). This prevents accidental profile corruption.`);
78
185
  } else {
@@ -82,9 +189,7 @@ function buildIdentityPayload(files, baselines, agentId = "unknown") {
82
189
  }
83
190
  if (files.identityMd !== undefined) {
84
191
  const content = files.identityMd;
85
- if (baselines?.identityMd && contentSha256(content) === baselines.identityMd) {} else if (content.length > MAX_PROFILE_FILE_LENGTH) {
86
- warnProfileFileTooLarge(agentId, "identityMd", content.length);
87
- } else if (content.trim()) {
192
+ if (baselines?.identityMd && contentSha256(content) === baselines.identityMd) {} else if (content.trim()) {
88
193
  if (content.length < IDENTITY_FILE_MIN_LENGTH) {
89
194
  console.error(`[profile-sync] Skipping IDENTITY.md sync: content too short (${content.length} chars, minimum ${IDENTITY_FILE_MIN_LENGTH}). This prevents accidental profile corruption.`);
90
195
  } else {
@@ -94,9 +199,7 @@ function buildIdentityPayload(files, baselines, agentId = "unknown") {
94
199
  }
95
200
  if (files.toolsMd !== undefined) {
96
201
  const content = files.toolsMd;
97
- if (baselines?.toolsMd && contentSha256(content) === baselines.toolsMd) {} else if (content.length > MAX_PROFILE_FILE_LENGTH) {
98
- warnProfileFileTooLarge(agentId, "toolsMd", content.length);
99
- } else if (content.trim()) {
202
+ if (baselines?.toolsMd && contentSha256(content) === baselines.toolsMd) {} else if (content.trim()) {
100
203
  updates.toolsMd = content;
101
204
  }
102
205
  }
@@ -130,15 +233,11 @@ async function collectProfilePayloads(fields, changeSource, readFile = readFileI
130
233
  toolsMd: await readFile(TOOLS_MD_PATH),
131
234
  heartbeatMd: await readFile(HEARTBEAT_MD_PATH)
132
235
  }, baselines, agentId);
133
- if (Object.keys(updates).length > 0) {
134
- payloads.push({ label: "identity", body: { ...updates, changeSource } });
135
- }
236
+ payloads.push(...buildIndependentIdentityPayloads(updates, changeSource));
136
237
  }
137
238
  if (fields.includes("claude")) {
138
239
  const raw = await readFile(claudeMdPath);
139
- if (raw && raw.length > MAX_PROFILE_FILE_LENGTH) {
140
- warnProfileFileTooLarge(agentId, "claudeMd", raw.length);
141
- } else if (raw?.trim()) {
240
+ if (raw?.trim()) {
142
241
  if (baselines?.claudeMd && contentSha256(raw) === baselines.claudeMd) {} else {
143
242
  payloads.push({ label: "claude", body: { claudeMd: raw, changeSource } });
144
243
  }
@@ -187,11 +286,32 @@ async function syncProfileFilesToServer(opts) {
187
286
  await postProfileUpdate(opts, payload);
188
287
  }
189
288
  }
190
- var SOUL_MD_PATH = "/workspace/SOUL.md", IDENTITY_MD_PATH = "/workspace/IDENTITY.md", TOOLS_MD_PATH = "/workspace/TOOLS.md", HEARTBEAT_MD_PATH = "/workspace/HEARTBEAT.md", SETUP_SCRIPT_PATH = "/workspace/start-up.sh", IDENTITY_BASELINES_PATH = "/tmp/identity-baselines.json", CLAUDE_MD_PATH, WORKSPACE_CLAUDE_MD_PATH = "/workspace/CLAUDE.md", IDENTITY_FILE_MIN_LENGTH = 500, SETUP_MARKER_START = "# === Agent-managed setup (from DB) ===", SETUP_MARKER_END = "# === End agent-managed setup ===";
289
+ var SOUL_MD_PATH = "/workspace/SOUL.md", IDENTITY_MD_PATH = "/workspace/IDENTITY.md", TOOLS_MD_PATH = "/workspace/TOOLS.md", HEARTBEAT_MD_PATH = "/workspace/HEARTBEAT.md", SETUP_SCRIPT_PATH = "/workspace/start-up.sh", IDENTITY_BASELINES_PATH = "/tmp/identity-baselines.json", PROFILE_FIELD_DETAILS, CLAUDE_MD_PATH, WORKSPACE_CLAUDE_MD_PATH = "/workspace/CLAUDE.md", IDENTITY_FILE_MIN_LENGTH = 500, SETUP_MARKER_START = "# === Agent-managed setup (from DB) ===", SETUP_MARKER_END = "# === End agent-managed setup ===";
191
290
  var init_profile_sync = __esm(() => {
291
+ init_resolver();
192
292
  init_constants();
293
+ init_identity_field_budget();
193
294
  init_secret_scrubber();
295
+ init_templates();
296
+ PROFILE_FIELD_DETAILS = {
297
+ soulMd: {
298
+ label: `soulMd (${SOUL_MD_PATH})`,
299
+ recoveryGuidance: `Inspect and shrink the live ${SOUL_MD_PATH} first, moving durable tail content into memory. After a worker restart, use ${SOUL_MD_PATH}.pre-boot-<timestamp>.bak as the fallback copy of the rejected content.`
300
+ },
301
+ identityMd: {
302
+ label: `identityMd (${IDENTITY_MD_PATH})`,
303
+ recoveryGuidance: `Inspect and shrink the live ${IDENTITY_MD_PATH} first, moving durable tail content into memory. After a worker restart, use ${IDENTITY_MD_PATH}.pre-boot-<timestamp>.bak as the fallback copy of the rejected content.`
304
+ },
305
+ claudeMd: {
306
+ label: "claudeMd (/workspace/CLAUDE.md for non-Claude harnesses; ~/.claude/CLAUDE.md for Claude)",
307
+ recoveryGuidance: "Inspect and shrink the live CLAUDE.md source first, moving durable tail content into memory. After a worker restart, non-Claude harnesses can use /workspace/CLAUDE.md.pre-boot-<timestamp>.bak as the fallback copy; Claude's native ~/.claude/CLAUDE.md source is not archived by that boot step."
308
+ },
309
+ toolsMd: {
310
+ label: `toolsMd (${TOOLS_MD_PATH})`,
311
+ recoveryGuidance: `Inspect and shrink the live ${TOOLS_MD_PATH} first, moving durable tail content into memory. After a worker restart, use ${TOOLS_MD_PATH}.pre-boot-<timestamp>.bak as the fallback copy of the rejected content.`
312
+ }
313
+ };
194
314
  CLAUDE_MD_PATH = `${process.env.HOME}/.claude/CLAUDE.md`;
195
315
  });
196
316
 
197
- export { contentSha256, writeIdentityBaselines, readIdentityBaselines, warnProfileFileTooLarge, writeProfileFileFromDb, resolveClaudeMdPath, syncProfileFilesToServer, init_profile_sync };
317
+ export { contentSha256, prependProfileSyncRejectionBanner, writeIdentityBaselines, readIdentityBaselines, warnProfileFileTooLarge, writeProfileFileFromDb, resolveClaudeMdPath, buildIndependentIdentityPayloads, syncProfileFilesToServer, init_profile_sync };
@@ -10,9 +10,10 @@ import {
10
10
  require_p_retry
11
11
  } from "./cli-b0p7rfnd.js";
12
12
  import {
13
+ getJsonSchemaToolParameters,
13
14
  resolveJsonSchemaStrictSampling,
14
15
  transformMessages
15
- } from "./cli-57khth04.js";
16
+ } from "./cli-xmyqv00z.js";
16
17
  import {
17
18
  sanitizeSurrogates
18
19
  } from "./cli-y2apkfe2.js";
@@ -25221,16 +25222,20 @@ function sanitizeForOpenApi(schema) {
25221
25222
  }
25222
25223
  return result;
25223
25224
  }
25224
- function convertTools(tools, useParameters = false) {
25225
+ function convertTools(tools, useParameters = false, supportsStrictMode = true) {
25225
25226
  if (tools.length === 0)
25226
25227
  return;
25227
25228
  return [
25228
25229
  {
25229
- functionDeclarations: tools.map((tool) => ({
25230
- name: tool.name,
25231
- description: tool.description,
25232
- ...useParameters ? { parameters: sanitizeForOpenApi(tool.parameters) } : { parametersJsonSchema: tool.parameters }
25233
- }))
25230
+ functionDeclarations: tools.map((tool) => {
25231
+ const strict = resolveJsonSchemaStrictSampling(tool, supportsStrictMode);
25232
+ const parameters = getJsonSchemaToolParameters(tool, strict);
25233
+ return {
25234
+ name: tool.name,
25235
+ description: tool.description,
25236
+ ...useParameters ? { parameters: sanitizeForOpenApi(parameters) } : { parametersJsonSchema: parameters }
25237
+ };
25238
+ })
25234
25239
  }
25235
25240
  ];
25236
25241
  }
@@ -4,7 +4,7 @@ import {
4
4
  incrKv,
5
5
  init_db,
6
6
  upsertKv
7
- } from "./cli-50vbt00w.js";
7
+ } from "./cli-zfbf3srj.js";
8
8
 
9
9
  // src/be/users.ts
10
10
  init_db();
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  summarizeSession
3
- } from "./cli-w2wxd56v.js";
3
+ } from "./cli-5z2yhzvd.js";
4
4
  import {
5
5
  checkToolLoop,
6
6
  clearToolHistory
@@ -11,29 +11,30 @@ import {
11
11
  fetchRetrievalsForTask,
12
12
  isLlmRaterEnabled,
13
13
  postRatings
14
- } from "./cli-2wadx7p2.js";
14
+ } from "./cli-f8dre4bz.js";
15
15
  import {
16
16
  getApiKey
17
17
  } from "./cli-f14fvzag.js";
18
18
  import {
19
+ buildIndependentIdentityPayloads,
19
20
  contentSha256,
20
21
  init_profile_sync,
21
22
  readIdentityBaselines,
22
23
  warnProfileFileTooLarge
23
- } from "./cli-0258203w.js";
24
+ } from "./cli-rpp0x4zy.js";
24
25
  import {
25
26
  MAX_PROFILE_FILE_LENGTH,
26
27
  getMcpBaseUrl,
27
28
  init_constants
28
29
  } from "./cli-x23me2mg.js";
30
+ import {
31
+ init_package,
32
+ package_default
33
+ } from "./cli-9nfc2es8.js";
29
34
  import {
30
35
  init_secret_scrubber,
31
36
  scrubSecrets
32
37
  } from "./cli-4j2mrzag.js";
33
- import {
34
- init_package,
35
- package_default
36
- } from "./cli-ccf9vy7q.js";
37
38
 
38
39
  // src/hooks/hook.ts
39
40
  init_package();
@@ -57,6 +58,31 @@ function shouldShowRegistrationNudge(opts) {
57
58
  return false;
58
59
  return true;
59
60
  }
61
+ async function postHookProfileUpdate({
62
+ url,
63
+ headers,
64
+ body,
65
+ label,
66
+ fetchImpl = fetch
67
+ }) {
68
+ try {
69
+ const response = await fetchImpl(url, {
70
+ method: "PUT",
71
+ headers: {
72
+ ...headers,
73
+ "Content-Type": "application/json"
74
+ },
75
+ body: JSON.stringify(body)
76
+ });
77
+ if (!response.ok) {
78
+ const responseBody = await response.text();
79
+ console.error(scrubSecrets(`[hook] ${label} profile sync failed: HTTP ${response.status}${responseBody ? ` — ${responseBody}` : ""}`));
80
+ }
81
+ } catch (error) {
82
+ const message = error instanceof Error ? error.message : String(error);
83
+ console.error(scrubSecrets(`[hook] ${label} profile sync errored: ${message}`));
84
+ }
85
+ }
60
86
  async function readTaskFile() {
61
87
  const taskFilePath = process.env.TASK_FILE;
62
88
  if (!taskFilePath) {
@@ -362,20 +388,12 @@ async function handleHook() {
362
388
  const content = await file.text();
363
389
  if (!content.trim())
364
390
  return;
365
- if (content.length > MAX_PROFILE_FILE_LENGTH) {
366
- warnProfileFileTooLarge(agentId, "claudeMd", content.length, "hook");
367
- return;
368
- }
369
- try {
370
- await fetch(`${getBaseUrl()}/api/agents/${agentId}/profile`, {
371
- method: "PUT",
372
- headers: {
373
- ...mcpConfig.headers,
374
- "Content-Type": "application/json"
375
- },
376
- body: JSON.stringify({ claudeMd: content, changeSource: "session_sync" })
377
- });
378
- } catch {}
391
+ await postHookProfileUpdate({
392
+ url: `${getBaseUrl()}/api/agents/${agentId}/profile`,
393
+ headers: mcpConfig.headers,
394
+ body: { claudeMd: content, changeSource: "session_sync" },
395
+ label: "claudeMd"
396
+ });
379
397
  };
380
398
  const IDENTITY_FILE_MIN_LENGTH = 500;
381
399
  const syncIdentityFilesToServer = async (agentId, changeSource = "session_sync") => {
@@ -386,9 +404,7 @@ async function handleHook() {
386
404
  const soulFile = Bun.file(SOUL_MD_PATH);
387
405
  if (await soulFile.exists()) {
388
406
  const content = await soulFile.text();
389
- if (baselines?.soulMd && contentSha256(content) === baselines.soulMd) {} else if (content.length > MAX_PROFILE_FILE_LENGTH) {
390
- warnProfileFileTooLarge(agentId, "soulMd", content.length, "hook");
391
- } else if (content.trim()) {
407
+ if (baselines?.soulMd && contentSha256(content) === baselines.soulMd) {} else if (content.trim()) {
392
408
  if (content.length < IDENTITY_FILE_MIN_LENGTH) {
393
409
  console.error(`[hook] Skipping SOUL.md sync: content too short (${content.length} chars, minimum ${IDENTITY_FILE_MIN_LENGTH}). This prevents accidental profile corruption.`);
394
410
  } else {
@@ -399,9 +415,7 @@ async function handleHook() {
399
415
  const identityFile = Bun.file(IDENTITY_MD_PATH);
400
416
  if (await identityFile.exists()) {
401
417
  const content = await identityFile.text();
402
- if (baselines?.identityMd && contentSha256(content) === baselines.identityMd) {} else if (content.length > MAX_PROFILE_FILE_LENGTH) {
403
- warnProfileFileTooLarge(agentId, "identityMd", content.length, "hook");
404
- } else if (content.trim()) {
418
+ if (baselines?.identityMd && contentSha256(content) === baselines.identityMd) {} else if (content.trim()) {
405
419
  if (content.length < IDENTITY_FILE_MIN_LENGTH) {
406
420
  console.error(`[hook] Skipping IDENTITY.md sync: content too short (${content.length} chars, minimum ${IDENTITY_FILE_MIN_LENGTH}). This prevents accidental profile corruption.`);
407
421
  } else {
@@ -412,9 +426,7 @@ async function handleHook() {
412
426
  const toolsMdFile = Bun.file(TOOLS_MD_PATH);
413
427
  if (await toolsMdFile.exists()) {
414
428
  const content = await toolsMdFile.text();
415
- if (baselines?.toolsMd && contentSha256(content) === baselines.toolsMd) {} else if (content.length > MAX_PROFILE_FILE_LENGTH) {
416
- warnProfileFileTooLarge(agentId, "toolsMd", content.length, "hook");
417
- } else if (content.trim()) {
429
+ if (baselines?.toolsMd && contentSha256(content) === baselines.toolsMd) {} else if (content.trim()) {
418
430
  updates.toolsMd = content;
419
431
  }
420
432
  }
@@ -429,16 +441,14 @@ async function handleHook() {
429
441
  }
430
442
  if (Object.keys(updates).length === 0)
431
443
  return;
432
- try {
433
- await fetch(`${getBaseUrl()}/api/agents/${agentId}/profile`, {
434
- method: "PUT",
435
- headers: {
436
- ...mcpConfig.headers,
437
- "Content-Type": "application/json"
438
- },
439
- body: JSON.stringify({ ...updates, changeSource })
444
+ for (const payload of buildIndependentIdentityPayloads(updates, changeSource)) {
445
+ await postHookProfileUpdate({
446
+ url: `${getBaseUrl()}/api/agents/${agentId}/profile`,
447
+ headers: mcpConfig.headers,
448
+ body: payload.body,
449
+ label: payload.label
440
450
  });
441
- } catch {}
451
+ }
442
452
  };
443
453
  const syncSetupScriptToServer = async (agentId, changeSource = "session_sync") => {
444
454
  if (!mcpConfig)
@@ -234,7 +234,7 @@ import {
234
234
  uuidv7,
235
235
  xid,
236
236
  xor
237
- } from "./cli-5dsbcjjw.js";
237
+ } from "./cli-jqwnqx9m.js";
238
238
  import {
239
239
  __esm,
240
240
  __export
@@ -3,11 +3,11 @@ import {
3
3
  getAgentWorkingOnThread,
4
4
  getAllAgents,
5
5
  init_db
6
- } from "./cli-50vbt00w.js";
6
+ } from "./cli-zfbf3srj.js";
7
7
  import {
8
8
  init_env_flag,
9
9
  isEnvFlagEnabled
10
- } from "./cli-xpxde7rm.js";
10
+ } from "./cli-qys3rhhz.js";
11
11
 
12
12
  // src/slack/router.ts
13
13
  init_db();
@@ -1,4 +1,112 @@
1
1
  // node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.js
2
+ class UnsupportedStrictJsonSchemaError extends Error {
3
+ }
4
+ var UNSUPPORTED_STRICT_SCHEMA_KEYS = [
5
+ "$ref",
6
+ "$defs",
7
+ "definitions",
8
+ "allOf",
9
+ "oneOf",
10
+ "patternProperties",
11
+ "dependentSchemas",
12
+ "dependencies",
13
+ "unevaluatedProperties",
14
+ "propertyNames",
15
+ "contains",
16
+ "prefixItems",
17
+ "not",
18
+ "if",
19
+ "then",
20
+ "else"
21
+ ];
22
+ function isJsonSchemaObject(value) {
23
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24
+ }
25
+ function isStructuredSchema(schema) {
26
+ if (!isJsonSchemaObject(schema))
27
+ return false;
28
+ const types = typeof schema.type === "string" ? [schema.type] : Array.isArray(schema.type) ? schema.type : [];
29
+ return types.includes("object") || types.includes("array") || schema.properties !== undefined || schema.items !== undefined;
30
+ }
31
+ function schemaAllowsNull(schema) {
32
+ if (!isJsonSchemaObject(schema))
33
+ return false;
34
+ if (schema.type === "null" || Array.isArray(schema.type) && schema.type.includes("null"))
35
+ return true;
36
+ if (schema.const === null || Array.isArray(schema.enum) && schema.enum.includes(null))
37
+ return true;
38
+ return Array.isArray(schema.anyOf) && schema.anyOf.some((variant) => schemaAllowsNull(variant));
39
+ }
40
+ function makeJsonSchemaNodeStrict(schema) {
41
+ if (!isJsonSchemaObject(schema)) {
42
+ throw new UnsupportedStrictJsonSchemaError("boolean schemas are unsupported");
43
+ }
44
+ for (const key of UNSUPPORTED_STRICT_SCHEMA_KEYS) {
45
+ if (schema[key] !== undefined) {
46
+ throw new UnsupportedStrictJsonSchemaError(`${key} schemas are unsupported`);
47
+ }
48
+ }
49
+ if (schema.anyOf !== undefined) {
50
+ if (!Array.isArray(schema.anyOf) || schema.anyOf.length === 0) {
51
+ throw new UnsupportedStrictJsonSchemaError("anyOf must contain at least one schema");
52
+ }
53
+ for (const variant of schema.anyOf) {
54
+ if (isStructuredSchema(variant)) {
55
+ throw new UnsupportedStrictJsonSchemaError("object and array unions are unsupported");
56
+ }
57
+ makeJsonSchemaNodeStrict(variant);
58
+ }
59
+ }
60
+ if (schema.items !== undefined) {
61
+ if (Array.isArray(schema.items)) {
62
+ throw new UnsupportedStrictJsonSchemaError("tuple schemas are unsupported");
63
+ }
64
+ makeJsonSchemaNodeStrict(schema.items);
65
+ }
66
+ const isObjectSchema = schema.type === "object";
67
+ if (schema.properties !== undefined && !isObjectSchema) {
68
+ throw new UnsupportedStrictJsonSchemaError("properties require type object");
69
+ }
70
+ if (!isObjectSchema)
71
+ return;
72
+ if (schema.additionalProperties !== undefined && schema.additionalProperties !== false) {
73
+ throw new UnsupportedStrictJsonSchemaError("schema-valued or true additionalProperties is unsupported");
74
+ }
75
+ if (schema.properties !== undefined && !isJsonSchemaObject(schema.properties)) {
76
+ throw new UnsupportedStrictJsonSchemaError("object properties must be a schema map");
77
+ }
78
+ if (schema.required !== undefined && (!Array.isArray(schema.required) || schema.required.some((key) => typeof key !== "string"))) {
79
+ throw new UnsupportedStrictJsonSchemaError("object required must be a string array");
80
+ }
81
+ const properties = schema.properties ?? {};
82
+ const propertyNames = Object.keys(properties);
83
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
84
+ if ([...required].some((key) => !propertyNames.includes(key))) {
85
+ throw new UnsupportedStrictJsonSchemaError("required contains an unknown property");
86
+ }
87
+ for (const [key, property] of Object.entries(properties)) {
88
+ makeJsonSchemaNodeStrict(property);
89
+ if (!required.has(key) && !schemaAllowsNull(property)) {
90
+ properties[key] = { anyOf: [property, { type: "null" }] };
91
+ }
92
+ }
93
+ schema.required = propertyNames;
94
+ schema.additionalProperties = false;
95
+ }
96
+ function makeStrictJsonSchema(schema) {
97
+ const cloned = structuredClone(schema);
98
+ if (!isJsonSchemaObject(cloned)) {
99
+ throw new UnsupportedStrictJsonSchemaError("root schema must have type object");
100
+ }
101
+ makeJsonSchemaNodeStrict(cloned);
102
+ if (cloned.type !== "object") {
103
+ throw new UnsupportedStrictJsonSchemaError("root schema must have type object");
104
+ }
105
+ return cloned;
106
+ }
107
+ function getJsonSchemaToolParameters(tool, strict) {
108
+ return strict === true ? makeStrictJsonSchema(tool.parameters) : tool.parameters;
109
+ }
2
110
  function getGrammarToolInput(toolName, arguments_, inputProperty) {
3
111
  const input = arguments_[inputProperty];
4
112
  if (typeof input !== "string") {
@@ -50,11 +158,19 @@ function inferGrammarInputProperty(tool) {
50
158
  }
51
159
  function resolveJsonSchemaStrictSampling(tool, supportsStrictMode) {
52
160
  const config = tool.constrainedSampling;
53
- if (!config || config.type !== "json_schema") {
161
+ if (!config || config.type !== "json_schema")
54
162
  return;
55
- }
56
163
  if (supportsStrictMode) {
57
- return true;
164
+ try {
165
+ makeStrictJsonSchema(tool.parameters);
166
+ return true;
167
+ } catch (error) {
168
+ if (!(error instanceof UnsupportedStrictJsonSchemaError))
169
+ throw error;
170
+ if (config.strict !== "require")
171
+ return;
172
+ throw new Error(`Tool "${tool.name}" requires JSON-schema constrained sampling, but ${error.message}.`);
173
+ }
58
174
  }
59
175
  if (config.strict === "require") {
60
176
  throw new Error(`Tool "${tool.name}" requires JSON-schema constrained sampling, but strict tools are unsupported.`);
@@ -413,4 +529,4 @@ function transformMessages(messages, model, normalizeToolCallId) {
413
529
  return result;
414
530
  }
415
531
 
416
- export { getGrammarToolInput, appendGrammarToolInputJsonDelta, resolveJsonSchemaStrictSampling, resolveGrammarConstrainedSampling, createGrammarToolInputProperties, clampMaxTokensToContext, buildBaseOptions, MIN_ANSWER_TOKENS, clampReasoning, adjustMaxTokensForThinking, transformMessages };
532
+ export { getJsonSchemaToolParameters, getGrammarToolInput, appendGrammarToolInputJsonDelta, resolveJsonSchemaStrictSampling, resolveGrammarConstrainedSampling, createGrammarToolInputProperties, clampMaxTokensToContext, buildBaseOptions, MIN_ANSWER_TOKENS, clampReasoning, adjustMaxTokensForThinking, transformMessages };