@desplega.ai/agent-swarm 1.131.1 → 1.134.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.
- package/README.md +3 -3
- package/dist/{actions-jgcy477k.js → actions-7kast243.js} +20 -18
- package/dist/{anthropic-messages-rjmd2e7n.js → anthropic-messages-706yad38.js} +33 -17
- package/dist/app-0g84t8wf.js +28 -0
- package/dist/{artifact-jysj3wrg.js → artifact-dje5ke4h.js} +37 -131
- package/dist/{assistant-f2vkk1hj.js → assistant-ac4htcky.js} +25 -22
- package/dist/{azure-openai-responses-c0yjmbfd.js → azure-openai-responses-be59fbsz.js} +13 -13
- package/dist/{boot-reembed-q7mp8hcr.js → boot-reembed-b1rcqn85.js} +13 -11
- package/dist/{boot-reembed-xmw8wcx6.js → boot-reembed-kw2541c4.js} +14 -12
- package/dist/{boot-scrub-logs-dsqrv79n.js → boot-scrub-logs-ja29t92g.js} +11 -9
- package/dist/{claude-adapter-q6x7yspx.js → claude-adapter-5cg1fv8s.js} +33 -29
- package/dist/{claude-managed-adapter-wsg37bt8.js → claude-managed-adapter-9z9sdytx.js} +9 -9
- package/dist/{claude-managed-setup-52z4mq9w.js → claude-managed-setup-3zqp51cr.js} +5 -5
- package/dist/{claude-1ex232fc.js → claude-wqn3dtca.js} +1 -1
- package/dist/{cli-4wgngmwv.js → cli-00w4hbam.js} +1 -1
- package/dist/{cli-er3a4bb0.js → cli-15429qdw.js} +6 -1
- package/dist/{cli-e5dabkx0.js → cli-1cwhyvta.js} +4 -4
- package/dist/{cli-zsspvvm2.js → cli-1ybr6vw2.js} +237 -237
- package/dist/{cli-xpxde7rm.js → cli-2wv4kj0e.js} +60 -31
- package/dist/cli-34w2584r.js +530 -0
- package/dist/{cli-se2p6rkb.js → cli-38taxznr.js} +6 -6
- package/dist/{cli-9sbhdpvb.js → cli-3cgfyfzm.js} +14 -14
- package/dist/{cli-5n6e5yfn.js → cli-3zmdzfjf.js} +79 -74
- package/dist/{cli-3ty0t082.js → cli-43x36mty.js} +197 -8
- package/dist/{cli-ycrnynpw.js → cli-480nehnh.js} +29 -22
- package/dist/{cli-0258203w.js → cli-492e7ap5.js} +140 -20
- package/dist/{cli-3fj60etf.js → cli-497b685t.js} +3 -3
- package/dist/{cli-pxfxkx5m.js → cli-4bap00k0.js} +1 -1
- package/dist/{cli-w2wxd56v.js → cli-4bc8vmhm.js} +848 -770
- package/dist/{cli-aca5pz9f.js → cli-589599p9.js} +1 -1
- package/dist/{cli-t930g0xh.js → cli-5tnr64pm.js} +1 -1
- package/dist/{cli-jxg3s9ey.js → cli-65d93ves.js} +7 -7
- package/dist/{cli-6dpytdr2.js → cli-6x363v1z.js} +2 -2
- package/dist/{cli-dxak0fqd.js → cli-787ph852.js} +11 -11
- package/dist/{cli-793jvbsh.js → cli-7bv8nckk.js} +16 -11
- package/dist/{cli-zyxs93y9.js → cli-7jcc9y1r.js} +3 -3
- package/dist/{cli-f1am9szv.js → cli-87t2gstf.js} +1 -1
- package/dist/{cli-9aryf9tv.js → cli-95m51ztz.js} +1 -1
- package/dist/{cli-4w4fmq9x.js → cli-9w49enmh.js} +2 -2
- package/dist/{cli-v69vk5h0.js → cli-9y5b02vr.js} +13 -13
- package/dist/{cli-x23me2mg.js → cli-a1rrkrb4.js} +1 -1
- package/dist/{cli-psrqtw2m.js → cli-a3pkfpqs.js} +2 -2
- package/dist/{cli-h0rgxd24.js → cli-a4ajmfvp.js} +6 -6
- package/dist/{cli-ygrbmjja.js → cli-aw8bb6sf.js} +6 -6
- package/dist/{cli-g74vpjp2.js → cli-brm3et47.js} +55 -22
- package/dist/{cli-czkae1fp.js → cli-c5ddbdfj.js} +9 -9
- package/dist/{cli-114fvtmj.js → cli-c79a9zrc.js} +1 -1
- package/dist/{cli-49n9f49x.js → cli-dx3dz0ms.js} +3 -3
- package/dist/{cli-6dp3ydxa.js → cli-e07exmsb.js} +18 -14
- package/dist/cli-et1bbwqf.js +13 -0
- package/dist/{cli-yazx6hap.js → cli-f3s9dry0.js} +14 -14
- package/dist/{cli-t8d349cy.js → cli-f4wj0s3m.js} +3 -3
- package/dist/{cli-d6n0thxw.js → cli-f64gdfxc.js} +1 -1
- package/dist/{cli-pwfsqc2x.js → cli-fyd58xgb.js} +55 -55
- package/dist/{cli-7dm2zqy2.js → cli-g0j14czv.js} +2 -2
- package/dist/{cli-b0p7rfnd.js → cli-g2p9bd1z.js} +14 -14
- package/dist/{cli-z905tvmm.js → cli-g814jbst.js} +2 -2
- package/dist/{cli-5dsbcjjw.js → cli-ghexzd2d.js} +970 -970
- package/dist/{cli-jvv0g8bh.js → cli-gpt9kf01.js} +15 -15
- package/dist/{cli-rgrfzg8z.js → cli-grt6qv91.js} +380 -380
- package/dist/{cli-wz9115x0.js → cli-gtafvdy0.js} +8 -8
- package/dist/{cli-q5dzzak0.js → cli-gy07xk4s.js} +2 -2
- package/dist/{cli-sy7jk3tx.js → cli-h0djenzm.js} +2 -2
- package/dist/{cli-nrkkknat.js → cli-h37gbjbf.js} +2 -281
- package/dist/{cli-jevgrb4w.js → cli-h39xxnq6.js} +1200 -491
- package/dist/{cli-gz9sbyk3.js → cli-jepmgn7r.js} +1 -1
- package/dist/{cli-ccf9vy7q.js → cli-jmvk6et8.js} +7 -7
- package/dist/{cli-2rk0cx7s.js → cli-jxj92gkt.js} +51 -41
- package/dist/{cli-bqw0mt1n.js → cli-k46t4664.js} +18 -3
- package/dist/{cli-50vbt00w.js → cli-m07313tr.js} +1153 -696
- package/dist/{cli-4j2mrzag.js → cli-mkq1rk7b.js} +1 -1
- package/dist/{cli-1z7048dy.js → cli-mnm7w46e.js} +1 -1
- package/dist/{cli-kd1ck4rx.js → cli-mp9sg4cg.js} +14 -8
- package/dist/{cli-jbcva0xj.js → cli-mrammd8c.js} +60 -35
- package/dist/cli-nanaxrz6.js +27 -0
- package/dist/{cli-p9swy5t3.js → cli-pq2ee5ja.js} +8 -6
- package/dist/{cli-2wadx7p2.js → cli-pt43pdp1.js} +2 -2
- package/dist/{cli-7sgrxg9a.js → cli-q6d0vvey.js} +4 -4
- package/dist/{cli-k9qkf1hw.js → cli-qf9kpsve.js} +9 -9
- package/dist/cli-qh4sqfws.js +15 -0
- package/dist/{cli-e15jhafb.js → cli-qntkxysp.js} +14 -14
- package/dist/{cli-sajgpkqg.js → cli-qs9hhsvb.js} +1723 -325
- package/dist/{cli-z2zcxes1.js → cli-qt5bt3vk.js} +1 -1
- package/dist/{cli-wx82tw1k.js → cli-qtekrty1.js} +3 -3
- package/dist/{cli-g1re8r05.js → cli-rh6f3s9n.js} +3 -3
- package/dist/{cli-n0krnc6d.js → cli-rzhcrw2p.js} +2 -2
- package/dist/{cli-gecy3z2s.js → cli-snw62ze5.js} +4 -4
- package/dist/{cli-n6h8qt8n.js → cli-snz3p4yf.js} +15 -10
- package/dist/{cli-r6kqqbj0.js → cli-tkf8n6cw.js} +11 -11
- package/dist/{cli-4rkfg1wx.js → cli-tkrr24mz.js} +14 -9
- package/dist/{cli-9dr5qzc0.js → cli-tv9f1kyb.js} +54 -43
- package/dist/{cli-gm9cbhbq.js → cli-vtz6yj3v.js} +28 -9
- package/dist/{cli-2f07m0xr.js → cli-wf86d5t7.js} +1 -1
- package/dist/{cli-7zd05xb4.js → cli-whrvct19.js} +45 -45
- package/dist/{cli-ap1czx8f.js → cli-wrrea8xm.js} +3 -3
- package/dist/{cli-57khth04.js → cli-xmyqv00z.js} +120 -4
- package/dist/{cli-cwy5hqbq.js → cli-yjmk7tzc.js} +1 -1
- package/dist/{cli-6pcz9h15.js → cli-zykjrwcx.js} +180 -394
- package/dist/cli.js +45 -45
- package/dist/{codex-adapter-htjkn22b.js → codex-adapter-hsq6mz3m.js} +21 -19
- package/dist/{codex-hook-hq54t9ft.js → codex-hook-8c500gq8.js} +8 -7
- package/dist/{codex-login-z6yhbkmk.js → codex-login-x7603481.js} +3 -3
- package/dist/{codex-session-runner-wjfc7smz.js → codex-session-runner-c1x3x143.js} +15 -13
- package/dist/{commands-xhrfwqsk.js → commands-rwtddtst.js} +11 -9
- package/dist/{db-anvwb22f.js → db-j2b355vz.js} +458 -444
- package/dist/{devin-adapter-v1sarqtw.js → devin-adapter-8qmhqpy3.js} +3 -3
- package/dist/{devtools-2y1r7yqk.js → devtools-j2rnj8w7.js} +4 -4
- package/dist/{e2b-jdkwemv2.js → e2b-znpx78eg.js} +16 -16
- package/dist/{es5-vjgtkgp6.js → es5-fmqrjzy3.js} +2 -2
- package/dist/{event-bus-axn823e9.js → event-bus-n8sb35pw.js} +4 -4
- package/dist/{getMachineId-bsd-6tc9wxdj.js → getMachineId-bsd-wpzbhkm6.js} +4 -4
- package/dist/{getMachineId-darwin-sjt9sgnc.js → getMachineId-darwin-45zabjy0.js} +4 -4
- package/dist/{getMachineId-linux-v5n18y9p.js → getMachineId-linux-warnp5pd.js} +3 -3
- package/dist/{getMachineId-unsupported-yrjyqw3d.js → getMachineId-unsupported-5spent2a.js} +3 -3
- package/dist/{getMachineId-win-w3r8063c.js → getMachineId-win-v4bvcw7a.js} +4 -4
- package/dist/{google-generative-ai-hysrd8qx.js → google-generative-ai-b0wzss55.js} +17 -14
- package/dist/{google-vertex-p5arrga0.js → google-vertex-e8q4dpya.js} +18 -15
- package/dist/{handlers-b9kbc621.js → handlers-hchgrxkh.js} +32 -29
- package/dist/{hook-xkvn6c5h.js → hook-p49egqc0.js} +66 -52
- package/dist/hook-spb00w76.js +34 -0
- package/dist/{http-9xh6ta4r.js → http-nh14kkms.js} +663 -181
- package/dist/index-1qthq7vc.js +6 -0
- package/dist/{index-0g68f0p6.js → index-20xqmn3v.js} +3 -3
- package/dist/{index-vdkr5z2w.js → index-3e03k6e4.js} +114 -114
- package/dist/{index-ywds4812.js → index-4cbyhq2g.js} +43 -41
- package/dist/index-67pzqfrb.js +36 -0
- package/dist/{index-bccrkdkn.js → index-7m8yxkw5.js} +16 -16
- package/dist/{index-eaxyqcmj.js → index-dvsk64sm.js} +163 -83
- package/dist/{index-dqkpnmqm.js → index-e19s2m8k.js} +18 -18
- package/dist/{index-j30k5zxn.js → index-emvxcy98.js} +16 -16
- package/dist/{index-7ndw6ecy.js → index-gqnmr60d.js} +9 -9
- package/dist/{index-9znhar3x.js → index-j02xvjtd.js} +184 -185
- package/dist/{index-1s2442yw.js → index-ns5kksc1.js} +7 -7
- package/dist/{index-dpy2hth8.js → index-pdxc8kfh.js} +71 -71
- package/dist/{index-z1m7pw0v.js → index-ptcqm8bc.js} +107 -107
- package/dist/{index-070v4eyb.js → index-pyp7rexm.js} +5 -6
- package/dist/{index-86znjz1p.js → index-shne41sd.js} +13 -13
- package/dist/{index-2qn2qndv.js → index-t36q2v4t.js} +183 -183
- package/dist/index-tcnvv8w6.js +7 -0
- package/dist/index-w91qbfv1.js +7 -0
- package/dist/{index-fzc0mwvj.js → index-yqweff5n.js} +32 -32
- package/dist/index-yrfphye1.js +39 -0
- package/dist/{keepalive-vc7s2aa6.js → keepalive-9k51nqhc.js} +21 -15
- package/dist/lead-2xq3dxbz.js +88 -0
- package/dist/maintenance-0c0g6nym.js +28 -0
- package/dist/mistral-conversations-2njwdwhj.js +717 -0
- package/dist/{multipart-parser-qaktc7p8.js → multipart-parser-6gftesw0.js} +2 -2
- package/dist/{oauth-refresh-sweep-yecgjpk4.js → oauth-refresh-sweep-273a64r2.js} +14 -12
- package/dist/{onboard-xj0f73f9.js → onboard-tsv9wwhz.js} +12 -12
- package/dist/{openai-codex-responses-5g27pd6f.js → openai-codex-responses-kt84eq86.js} +26 -26
- package/dist/{openai-completions-3vkmfz2a.js → openai-completions-mrypq7qp.js} +17 -16
- package/dist/{openai-responses-dg4t7j7a.js → openai-responses-qhrk21ez.js} +20 -17
- package/dist/{opencode-adapter-1gm38aba.js → opencode-adapter-rar9676j.js} +13 -11
- package/dist/{openrouter-images-g3gm8931.js → openrouter-images-684fysx9.js} +5 -5
- package/dist/{otel-impl-nxg0n0b3.js → otel-impl-wp1mfzd2.js} +752 -537
- package/dist/{photon_rs-ky7dj5qw.js → photon_rs-8dwt2pzy.js} +581 -581
- package/dist/{pi-messages-tqj8q6b9.js → pi-messages-txp9f97r.js} +7 -7
- package/dist/{pi-mono-adapter-4g5kby31.js → pi-mono-adapter-yz47kk8e.js} +1910 -1180
- package/dist/pricing-refresh-jw1yw2fn.js +22 -0
- package/dist/rbac-roles-dg964c8p.js +32 -0
- package/dist/{rbac-roles-1hcxdtg7.js → rbac-roles-p53cg5cv.js} +19 -17
- package/dist/{registry-02cy9z5z.js → registry-mh12y17v.js} +4 -4
- package/dist/render-v2-56vn2rk9.js +36 -0
- package/dist/seed-pricing-52xx6z63.js +19 -0
- package/dist/{setup-46xtgq3c.js → setup-e8gp79sy.js} +7 -7
- package/dist/token-util-zy908s1a.js +6 -0
- package/dist/{token-fxkn2zf2.js → token-xg5711x5.js} +4 -4
- package/dist/worker-s17f1g46.js +88 -0
- package/dist/{x-6mg4kfpe.js → x-xkfh0w0v.js} +5 -5
- package/openapi.json +566 -22
- package/package.json +6 -6
- package/src/be/budget-refusal-notify.ts +14 -7
- package/src/be/db-queries/task-artifacts.ts +182 -0
- package/src/be/db.ts +834 -171
- package/src/be/events.ts +5 -0
- package/src/be/migrations/131_session_logs_created_at_index.sql +2 -0
- package/src/be/migrations/132_multi_runtime_instances.sql +44 -0
- package/src/be/migrations/133_workflow_run_trigger_type.sql +12 -0
- package/src/be/migrations/135_backfill_task_pull_request_attachments.sql +228 -0
- package/src/be/migrations/136_task_requester_provenance.sql +37 -0
- package/src/be/migrations/137_memory_retrieval_composite_index.sql +1 -0
- package/src/be/migrations/runner.ts +17 -11
- package/src/be/multi-runtime.ts +430 -0
- package/src/be/scripts/extract-schema.ts +3 -1
- package/src/be/scripts/typecheck.ts +6 -2
- package/src/be/seed-prompt-templates.ts +25 -2
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/steering.ts +1 -1
- package/src/be/swarm-config-guard.ts +18 -0
- package/src/commands/credential-wait.ts +54 -0
- package/src/commands/onboard/steps/core-credentials.tsx +1 -1
- package/src/commands/onboard/steps/custom-templates.tsx +1 -1
- package/src/commands/onboard/steps/harness-credentials.tsx +1 -1
- package/src/commands/onboard/steps/health-check.tsx +1 -1
- package/src/commands/onboard/steps/start.tsx +1 -1
- package/src/commands/profile-sync-audit.ts +56 -0
- package/src/commands/profile-sync.ts +287 -15
- package/src/commands/provider-credentials.ts +46 -19
- package/src/commands/runner.ts +199 -33
- package/src/commands/templates.ts +22 -0
- package/src/github/handlers.ts +55 -9
- package/src/heartbeat/heartbeat.ts +109 -17
- package/src/hooks/hook.ts +49 -33
- package/src/http/active-sessions.ts +11 -1
- package/src/http/agents.ts +261 -30
- package/src/http/all-routes.ts +1 -0
- package/src/http/config.ts +13 -2
- package/src/http/core.ts +102 -4
- package/src/http/db-query-bounded.ts +313 -0
- package/src/http/db-query-shared.ts +249 -0
- package/src/http/db-query.ts +54 -42
- package/src/http/events.ts +2 -0
- package/src/http/index.ts +23 -2
- package/src/http/mcp-bridge.ts +37 -3
- package/src/http/mcp.ts +12 -0
- package/src/http/memory.ts +13 -2
- package/src/http/openapi.ts +1 -0
- package/src/http/poll.ts +70 -27
- package/src/http/prompt-templates.ts +36 -13
- package/src/http/route-def.ts +43 -9
- package/src/http/schedules.ts +2 -2
- package/src/http/scripts.ts +10 -1
- package/src/http/session-data.ts +44 -0
- package/src/http/tasks.ts +2 -2
- package/src/http/users.ts +74 -52
- package/src/http/webhooks.ts +5 -2
- package/src/http/workflows.ts +1 -4
- package/src/integrations/kapso/inbound.ts +1 -1
- package/src/jira/outbound.ts +39 -8
- package/src/jira/webhook-lifecycle.ts +13 -2
- package/src/linear/outbound.ts +43 -10
- package/src/oauth/keepalive.ts +13 -2
- package/src/prompts/base-prompt.ts +1 -1
- package/src/prompts/default-drift.ts +21 -0
- package/src/prompts/session-templates.ts +56 -7
- package/src/providers/claude-adapter.ts +6 -0
- package/src/providers/codex-adapter.ts +6 -0
- package/src/providers/opencode-adapter.ts +6 -0
- package/src/providers/pi-mono-adapter.ts +8 -0
- package/src/providers/pricing-sources.md +24 -0
- package/src/queue-stall-alarm.ts +216 -0
- package/src/rbac/legacy-policy.ts +3 -0
- package/src/rbac/permissions.ts +12 -0
- package/src/scheduler/scheduler.ts +7 -0
- package/src/script-workflows/executor.ts +2 -1
- package/src/scripts-runtime/executors/native.ts +2 -1
- package/src/scripts-runtime/executors/types.ts +5 -0
- package/src/scripts-runtime/loader.ts +5 -0
- package/src/scripts-runtime/sdk-allowlist.ts +3 -0
- package/src/scripts-runtime/swarm-config.ts +8 -0
- package/src/scripts-runtime/swarm-sdk.ts +7 -0
- package/src/scripts-runtime/types/stdlib.d.ts +4 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +4 -0
- package/src/server.ts +6 -0
- package/src/slack/channel-join.ts +22 -3
- package/src/slack/channel-lifecycle.ts +114 -0
- package/src/tests/budget-claim-gate.test.ts +20 -0
- package/src/tests/claude-adapter.test.ts +32 -0
- package/src/tests/codex-adapter.test.ts +30 -0
- package/src/tests/create-task-validation.test.ts +128 -0
- package/src/tests/credential-wait.test.ts +67 -1
- package/src/tests/db-query.test.ts +711 -2
- package/src/tests/heartbeat-checklist.test.ts +28 -0
- package/src/tests/http-api-integration.test.ts +81 -0
- package/src/tests/http-users.test.ts +166 -1
- package/src/tests/identity-field-budget.test.ts +176 -0
- package/src/tests/mcp-tools-user.test.ts +34 -0
- package/src/tests/mcp-user-route.test.ts +17 -0
- package/src/tests/metrics-http.test.ts +44 -0
- package/src/tests/migration-failure.test.ts +86 -0
- package/src/tests/multi-runtime-registration.test.ts +2810 -0
- package/src/tests/openapi-response-contract.test.ts +24 -0
- package/src/tests/openapi-route-headers.test.ts +83 -0
- package/src/tests/opencode-adapter.test.ts +40 -0
- package/src/tests/outbound-listener-rejection.test.ts +138 -0
- package/src/tests/pi-mono-adapter.test.ts +98 -0
- package/src/tests/preload.ts +2 -0
- package/src/tests/profile-sync-warning.test.ts +277 -0
- package/src/tests/profile-sync.test.ts +232 -6
- package/src/tests/prompt-template-resolver.test.ts +177 -6
- package/src/tests/prompt-template-session.test.ts +39 -3
- package/src/tests/queue-stall-alarm.test.ts +226 -0
- package/src/tests/rbac-charact-slack.test.ts +29 -0
- package/src/tests/rbac-engine.test.ts +3 -0
- package/src/tests/requester-comms.test.ts +32 -0
- package/src/tests/runner-requester-profile.test.ts +38 -0
- package/src/tests/schedule-target-type.test.ts +38 -0
- package/src/tests/script-connections.test.ts +13 -1
- package/src/tests/scripts-runtime-identity.test.ts +375 -0
- package/src/tests/scripts-runtime.test.ts +4 -3
- package/src/tests/sdk-allowlist.test.ts +47 -0
- package/src/tests/send-task-requested-by.test.ts +9 -0
- package/src/tests/session-costs.test.ts +506 -1
- package/src/tests/shutdown-error-scrubbing.test.ts +92 -0
- package/src/tests/slack-channel-lifecycle.test.ts +241 -0
- package/src/tests/slack-render-v2.test.ts +43 -0
- package/src/tests/steering-core.test.ts +28 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +22 -0
- package/src/tests/task-artifacts.test.ts +386 -0
- package/src/tests/task-attachments-schema.test.ts +17 -1
- package/src/tests/task-completion-idempotency.test.ts +57 -0
- package/src/tests/task-pull-request-backfill-migration.test.ts +321 -0
- package/src/tests/tool-annotations.test.ts +4 -0
- package/src/tests/update-profile-api.test.ts +67 -39
- package/src/tests/update-profile-auth.test.ts +48 -4
- package/src/tests/user-attribution-migration.test.ts +54 -0
- package/src/tests/user-identity.test.ts +72 -0
- package/src/tests/workflow-async-v2.test.ts +39 -1
- package/src/tests/workflow-http-v2.test.ts +3 -2
- package/src/tests/workflow-mcp-trigger-schema.test.ts +52 -3
- package/src/tests/workflow-retry-v2.test.ts +45 -1
- package/src/tools/db-query.ts +18 -8
- package/src/tools/get-task-details.ts +24 -3
- package/src/tools/manage-user.ts +36 -3
- package/src/tools/poll-task.ts +93 -14
- package/src/tools/script-common.ts +5 -0
- package/src/tools/send-task.ts +1 -4
- package/src/tools/slack-archive-channel.ts +70 -0
- package/src/tools/slack-create-channel.ts +74 -0
- package/src/tools/slack-invite-to-channel.ts +78 -0
- package/src/tools/store-progress.ts +13 -2
- package/src/tools/swarm-config/delete-config.ts +9 -0
- package/src/tools/swarm-config/set-config.ts +3 -2
- package/src/tools/task-action.ts +30 -0
- package/src/tools/task-tool-ctx.ts +8 -1
- package/src/tools/tool-config.ts +4 -1
- package/src/tools/update-profile.ts +16 -21
- package/src/tools/utils.ts +5 -0
- package/src/tools/workflows/trigger-workflow.ts +7 -1
- package/src/types.ts +165 -1
- package/src/utils/github-pull-request.ts +76 -0
- package/src/utils/identity-field-budget.ts +73 -0
- package/src/utils/multi-runtime.ts +20 -0
- package/src/utils/requester-comms.ts +24 -0
- package/src/workflows/engine.ts +22 -2
- package/src/workflows/resume.ts +19 -11
- package/src/workflows/retry-poller.ts +1 -0
- package/src/workflows/triggers.ts +4 -0
- package/templates/skills/db-query-guidance/config.json +14 -0
- package/templates/skills/db-query-guidance/content.md +57 -0
- package/dist/app-5da78bd4.js +0 -26
- package/dist/cli-3te4jdkm.js +0 -225
- package/dist/cli-bmze986d.js +0 -7
- package/dist/cli-kh3ba2zf.js +0 -607
- package/dist/hook-cgk4wxmx.js +0 -31
- package/dist/index-25wjsdv2.js +0 -34
- package/dist/index-76at2ymp.js +0 -7
- package/dist/index-hase6zbe.js +0 -6
- package/dist/index-vg4snay4.js +0 -36
- package/dist/index-vy8edt27.js +0 -7
- package/dist/lead-r2yqfmx7.js +0 -85
- package/dist/maintenance-mw70k468.js +0 -26
- package/dist/mistral-conversations-81fwav0j.js +0 -30578
- package/dist/otel-nqa12wf3.js +0 -3149
- package/dist/pricing-refresh-2cn6effr.js +0 -20
- package/dist/rbac-roles-vynday5g.js +0 -30
- package/dist/render-v2-tc0bh8kk.js +0 -34
- package/dist/seed-pricing-8kceb43f.js +0 -17
- package/dist/token-util-pqyc8kng.js +0 -6
- package/dist/worker-qrbz6hmv.js +0 -85
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
contentSha256,
|
|
4
|
+
fetchProfileSyncRejectionBanner,
|
|
5
|
+
prependProfileSyncRejectionBanner,
|
|
6
|
+
renderProfileSyncRejectionBanner,
|
|
7
|
+
TOOLS_MD_PATH,
|
|
8
|
+
WORKSPACE_CLAUDE_MD_PATH,
|
|
9
|
+
} from "../commands/profile-sync";
|
|
10
|
+
import type { SwarmEvent } from "../types";
|
|
11
|
+
|
|
12
|
+
const rejectionEvent: SwarmEvent = {
|
|
13
|
+
id: "00000000-0000-4000-8000-000000000001",
|
|
14
|
+
category: "system",
|
|
15
|
+
event: "system.profile_sync_rejected",
|
|
16
|
+
status: "error",
|
|
17
|
+
source: "api",
|
|
18
|
+
agentId: "agent-1",
|
|
19
|
+
createdAt: "2026-08-18T15:55:00.000Z",
|
|
20
|
+
data: {
|
|
21
|
+
field: "toolsMd",
|
|
22
|
+
diskSize: 84_574,
|
|
23
|
+
dbSize: 83_679,
|
|
24
|
+
budget: 20_000,
|
|
25
|
+
delta: 895,
|
|
26
|
+
dbHash: contentSha256("db tools"),
|
|
27
|
+
changeSource: "session_sync",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const reconciliationEvent: SwarmEvent = {
|
|
32
|
+
...rejectionEvent,
|
|
33
|
+
id: "00000000-0000-4000-8000-000000000003",
|
|
34
|
+
event: "system.profile_sync_reconciled",
|
|
35
|
+
status: "ok",
|
|
36
|
+
createdAt: "2026-08-18T16:00:00.000Z",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe("profile sync rejection session warning", () => {
|
|
40
|
+
test("carries field, disk size, DB size, budget, delta, and recovery path", async () => {
|
|
41
|
+
const banner = await renderProfileSyncRejectionBanner(rejectionEvent);
|
|
42
|
+
|
|
43
|
+
expect(banner).toContain("/workspace/TOOLS.md");
|
|
44
|
+
expect(banner).toContain("Disk size: 84574");
|
|
45
|
+
expect(banner).toContain("DB size: 83679");
|
|
46
|
+
expect(banner).toContain("budget: 20000");
|
|
47
|
+
expect(banner).toContain("delta: +895");
|
|
48
|
+
expect(banner).toContain(".pre-boot-<timestamp>.bak");
|
|
49
|
+
expect(banner.indexOf("live /workspace/TOOLS.md")).toBeLessThan(
|
|
50
|
+
banner.indexOf("/workspace/TOOLS.md.pre-boot-<timestamp>.bak"),
|
|
51
|
+
);
|
|
52
|
+
expect(banner).toContain(rejectionEvent.id);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("queries the latest persisted event for the current agent", async () => {
|
|
56
|
+
const requests: Request[] = [];
|
|
57
|
+
const fetchImpl = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
58
|
+
const request = new Request(input, init);
|
|
59
|
+
requests.push(request);
|
|
60
|
+
const field = new URL(request.url).searchParams.get("dataField");
|
|
61
|
+
const body = request.url.endsWith("/me")
|
|
62
|
+
? { toolsMd: "db tools" }
|
|
63
|
+
: { events: field === "toolsMd" ? [rejectionEvent] : [] };
|
|
64
|
+
return new Response(JSON.stringify(body), {
|
|
65
|
+
status: 200,
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
});
|
|
68
|
+
}) as typeof fetch;
|
|
69
|
+
|
|
70
|
+
const result = await prependProfileSyncRejectionBanner(
|
|
71
|
+
"ORIGINAL TASK PROMPT",
|
|
72
|
+
{
|
|
73
|
+
apiUrl: "https://api.example.test",
|
|
74
|
+
apiKey: "secret-key",
|
|
75
|
+
agentId: "agent-1",
|
|
76
|
+
},
|
|
77
|
+
fetchImpl,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect(requests[0]?.url).toContain("event=system.profile_sync_rejected");
|
|
81
|
+
expect(requests[0]?.url).toContain("agentId=agent-1");
|
|
82
|
+
expect(requests[0]?.url).toContain("limit=1");
|
|
83
|
+
expect(requests[0]?.url).toContain("dataField=soulMd");
|
|
84
|
+
expect(requests[0]?.headers.get("X-Agent-ID")).toBe("agent-1");
|
|
85
|
+
expect(requests.at(-1)?.url).toBe("https://api.example.test/me");
|
|
86
|
+
expect(result.injected).toBeTrue();
|
|
87
|
+
expect(result.prompt).toContain("PERSISTED PROFILE SYNC REJECTION");
|
|
88
|
+
expect(result.prompt).toEndWith("ORIGINAL TASK PROMPT");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("stops warning after the stored field changes", async () => {
|
|
92
|
+
const fetchImpl = (async (input: string | URL | Request) => {
|
|
93
|
+
const url = String(input);
|
|
94
|
+
const field = new URL(url).searchParams.get("dataField");
|
|
95
|
+
const body = url.endsWith("/me")
|
|
96
|
+
? { toolsMd: "reconciled tools" }
|
|
97
|
+
: { events: field === "toolsMd" ? [rejectionEvent] : [] };
|
|
98
|
+
return new Response(JSON.stringify(body), {
|
|
99
|
+
status: 200,
|
|
100
|
+
headers: { "Content-Type": "application/json" },
|
|
101
|
+
});
|
|
102
|
+
}) as typeof fetch;
|
|
103
|
+
|
|
104
|
+
const banner = await fetchProfileSyncRejectionBanner(
|
|
105
|
+
{
|
|
106
|
+
apiUrl: "https://api.example.test",
|
|
107
|
+
apiKey: "secret-key",
|
|
108
|
+
agentId: "agent-1",
|
|
109
|
+
},
|
|
110
|
+
fetchImpl,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(banner).toBe("");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("stops warning after a successful no-op reconciliation", async () => {
|
|
117
|
+
const fetchImpl = (async (input: string | URL | Request) => {
|
|
118
|
+
const url = new URL(String(input));
|
|
119
|
+
const field = url.searchParams.get("dataField");
|
|
120
|
+
const event = url.searchParams.get("event");
|
|
121
|
+
const body = url.pathname.endsWith("/me")
|
|
122
|
+
? { toolsMd: "db tools" }
|
|
123
|
+
: {
|
|
124
|
+
events:
|
|
125
|
+
field !== "toolsMd"
|
|
126
|
+
? []
|
|
127
|
+
: event === "system.profile_sync_reconciled"
|
|
128
|
+
? [reconciliationEvent]
|
|
129
|
+
: [rejectionEvent],
|
|
130
|
+
};
|
|
131
|
+
return new Response(JSON.stringify(body), {
|
|
132
|
+
status: 200,
|
|
133
|
+
headers: { "Content-Type": "application/json" },
|
|
134
|
+
});
|
|
135
|
+
}) as typeof fetch;
|
|
136
|
+
|
|
137
|
+
const banner = await fetchProfileSyncRejectionBanner(
|
|
138
|
+
{
|
|
139
|
+
apiUrl: "https://api.example.test",
|
|
140
|
+
apiKey: "secret-key",
|
|
141
|
+
agentId: "agent-1",
|
|
142
|
+
},
|
|
143
|
+
fetchImpl,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
expect(banner).toBe("");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("keeps warning when the latest reconciliation predates the rejection", async () => {
|
|
150
|
+
const olderReconciliation = {
|
|
151
|
+
...reconciliationEvent,
|
|
152
|
+
createdAt: "2026-08-18T15:50:00.000Z",
|
|
153
|
+
};
|
|
154
|
+
const fetchImpl = (async (input: string | URL | Request) => {
|
|
155
|
+
const url = new URL(String(input));
|
|
156
|
+
const field = url.searchParams.get("dataField");
|
|
157
|
+
const event = url.searchParams.get("event");
|
|
158
|
+
const body = url.pathname.endsWith("/me")
|
|
159
|
+
? { toolsMd: "db tools" }
|
|
160
|
+
: {
|
|
161
|
+
events:
|
|
162
|
+
field !== "toolsMd"
|
|
163
|
+
? []
|
|
164
|
+
: event === "system.profile_sync_reconciled"
|
|
165
|
+
? [olderReconciliation]
|
|
166
|
+
: [rejectionEvent],
|
|
167
|
+
};
|
|
168
|
+
return new Response(JSON.stringify(body), {
|
|
169
|
+
status: 200,
|
|
170
|
+
headers: { "Content-Type": "application/json" },
|
|
171
|
+
});
|
|
172
|
+
}) as typeof fetch;
|
|
173
|
+
|
|
174
|
+
const banner = await fetchProfileSyncRejectionBanner(
|
|
175
|
+
{
|
|
176
|
+
apiUrl: "https://api.example.test",
|
|
177
|
+
apiKey: "secret-key",
|
|
178
|
+
agentId: "agent-1",
|
|
179
|
+
},
|
|
180
|
+
fetchImpl,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
expect(banner).toContain("PERSISTED PROFILE SYNC REJECTION");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("clears a warning only when boot restored the local file to the DB value", async () => {
|
|
187
|
+
const fetchImpl = (async (input: string | URL | Request) => {
|
|
188
|
+
const url = new URL(String(input));
|
|
189
|
+
const field = url.searchParams.get("dataField");
|
|
190
|
+
const event = url.searchParams.get("event");
|
|
191
|
+
const body = url.pathname.endsWith("/me")
|
|
192
|
+
? { toolsMd: "db tools" }
|
|
193
|
+
: {
|
|
194
|
+
events:
|
|
195
|
+
field === "toolsMd" && event === "system.profile_sync_rejected"
|
|
196
|
+
? [rejectionEvent]
|
|
197
|
+
: [],
|
|
198
|
+
};
|
|
199
|
+
return new Response(JSON.stringify(body), {
|
|
200
|
+
status: 200,
|
|
201
|
+
headers: { "Content-Type": "application/json" },
|
|
202
|
+
});
|
|
203
|
+
}) as typeof fetch;
|
|
204
|
+
const config = {
|
|
205
|
+
apiUrl: "https://api.example.test",
|
|
206
|
+
apiKey: "secret-key",
|
|
207
|
+
agentId: "agent-1",
|
|
208
|
+
claudeMdPath: WORKSPACE_CLAUDE_MD_PATH,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const restored = await fetchProfileSyncRejectionBanner(config, fetchImpl, async (path) =>
|
|
212
|
+
path === TOOLS_MD_PATH ? "db tools" : undefined,
|
|
213
|
+
);
|
|
214
|
+
const stillDiverged = await fetchProfileSyncRejectionBanner(config, fetchImpl, async (path) =>
|
|
215
|
+
path === TOOLS_MD_PATH ? "rejected tools" : undefined,
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
expect(restored).toBe("");
|
|
219
|
+
expect(stillDiverged).toContain("PERSISTED PROFILE SYNC REJECTION");
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("surfaces the latest unresolved rejection for every affected field", async () => {
|
|
223
|
+
const claudeEvent: SwarmEvent = {
|
|
224
|
+
...rejectionEvent,
|
|
225
|
+
id: "00000000-0000-4000-8000-000000000002",
|
|
226
|
+
data: {
|
|
227
|
+
...rejectionEvent.data,
|
|
228
|
+
field: "claudeMd",
|
|
229
|
+
diskSize: 99_617,
|
|
230
|
+
dbSize: 99_602,
|
|
231
|
+
delta: 15,
|
|
232
|
+
dbHash: contentSha256("db claude"),
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
const fetchImpl = (async (input: string | URL | Request) => {
|
|
236
|
+
const url = String(input);
|
|
237
|
+
const field = new URL(url).searchParams.get("dataField");
|
|
238
|
+
const body = url.endsWith("/me")
|
|
239
|
+
? { toolsMd: "db tools", claudeMd: "db claude" }
|
|
240
|
+
: {
|
|
241
|
+
events:
|
|
242
|
+
field === "claudeMd" ? [claudeEvent] : field === "toolsMd" ? [rejectionEvent] : [],
|
|
243
|
+
};
|
|
244
|
+
return new Response(JSON.stringify(body), {
|
|
245
|
+
status: 200,
|
|
246
|
+
headers: { "Content-Type": "application/json" },
|
|
247
|
+
});
|
|
248
|
+
}) as typeof fetch;
|
|
249
|
+
|
|
250
|
+
const banner = await fetchProfileSyncRejectionBanner(
|
|
251
|
+
{
|
|
252
|
+
apiUrl: "https://api.example.test",
|
|
253
|
+
apiKey: "secret-key",
|
|
254
|
+
agentId: "agent-1",
|
|
255
|
+
},
|
|
256
|
+
fetchImpl,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
expect(banner).toContain("/workspace/CLAUDE.md");
|
|
260
|
+
expect(banner).toContain("~/.claude/CLAUDE.md");
|
|
261
|
+
expect(banner).toContain("is not archived by that boot step");
|
|
262
|
+
expect(banner).toContain("/workspace/TOOLS.md");
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("fails open when the audit event cannot be fetched", async () => {
|
|
266
|
+
const banner = await fetchProfileSyncRejectionBanner(
|
|
267
|
+
{
|
|
268
|
+
apiUrl: "https://api.example.test",
|
|
269
|
+
apiKey: "secret-key",
|
|
270
|
+
agentId: "agent-1",
|
|
271
|
+
},
|
|
272
|
+
(async () => new Response("unavailable", { status: 503 })) as typeof fetch,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
expect(banner).toBe("");
|
|
276
|
+
});
|
|
277
|
+
});
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { describe, expect, spyOn, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
buildIdentityPayload,
|
|
4
|
+
buildIndependentIdentityPayloads,
|
|
4
5
|
CLAUDE_MD_PATH,
|
|
5
6
|
collectProfilePayloads,
|
|
6
7
|
contentSha256,
|
|
7
8
|
extractSetupScriptContent,
|
|
8
9
|
type FileReader,
|
|
10
|
+
findProfileDivergences,
|
|
11
|
+
HEARTBEAT_MD_PATH,
|
|
9
12
|
IDENTITY_BASELINES_PATH,
|
|
10
13
|
IDENTITY_MD_PATH,
|
|
11
14
|
type IdentityBaselines,
|
|
@@ -17,7 +20,9 @@ import {
|
|
|
17
20
|
TOOLS_MD_PATH,
|
|
18
21
|
WORKSPACE_CLAUDE_MD_PATH,
|
|
19
22
|
} from "../commands/profile-sync";
|
|
23
|
+
import { profileSyncAuditExitCode, runProfileSyncAudit } from "../commands/profile-sync-audit";
|
|
20
24
|
import { MAX_PROFILE_FILE_LENGTH } from "../utils/constants";
|
|
25
|
+
import { IDENTITY_FIELD_BUDGETS } from "../utils/identity-field-budget";
|
|
21
26
|
|
|
22
27
|
const MARKER_START = "# === Agent-managed setup (from DB) ===";
|
|
23
28
|
const MARKER_END = "# === End agent-managed setup ===";
|
|
@@ -25,6 +30,182 @@ const MARKER_END = "# === End agent-managed setup ===";
|
|
|
25
30
|
// A SOUL/IDENTITY body long enough to clear the 500-char min-length guard.
|
|
26
31
|
const LONG = "x".repeat(600);
|
|
27
32
|
|
|
33
|
+
describe("profile divergence audit", () => {
|
|
34
|
+
test("compares all four budgeted fields plus ungated heartbeatMd", () => {
|
|
35
|
+
const divergences = findProfileDivergences(
|
|
36
|
+
{
|
|
37
|
+
soulMd: "disk soul",
|
|
38
|
+
identityMd: "disk identity",
|
|
39
|
+
claudeMd: "disk claude",
|
|
40
|
+
toolsMd: "disk tools",
|
|
41
|
+
heartbeatMd: "disk heartbeat",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
soulMd: "db soul",
|
|
45
|
+
identityMd: "db identity",
|
|
46
|
+
claudeMd: "db claude",
|
|
47
|
+
toolsMd: "db tools",
|
|
48
|
+
heartbeatMd: "db heartbeat",
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(divergences.map((entry) => entry.field)).toEqual([
|
|
53
|
+
"soulMd",
|
|
54
|
+
"identityMd",
|
|
55
|
+
"claudeMd",
|
|
56
|
+
"toolsMd",
|
|
57
|
+
"heartbeatMd",
|
|
58
|
+
]);
|
|
59
|
+
expect(divergences[0]?.budget).toBe(IDENTITY_FIELD_BUDGETS.soulMd);
|
|
60
|
+
expect(divergences[2]?.budget).toBe(IDENTITY_FIELD_BUDGETS.claudeMd);
|
|
61
|
+
expect(divergences[4]?.budget).toBeNull();
|
|
62
|
+
expect(divergences[4]?.diskSize).toBe("disk heartbeat".length);
|
|
63
|
+
expect(divergences[4]?.dbSize).toBe("db heartbeat".length);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("fetches the DB profile and reads the canonical local paths", async () => {
|
|
67
|
+
const local = {
|
|
68
|
+
[SOUL_MD_PATH]: "same soul",
|
|
69
|
+
[IDENTITY_MD_PATH]: "disk identity",
|
|
70
|
+
[WORKSPACE_CLAUDE_MD_PATH]: "same claude",
|
|
71
|
+
[TOOLS_MD_PATH]: "same tools",
|
|
72
|
+
[HEARTBEAT_MD_PATH]: "disk heartbeat",
|
|
73
|
+
};
|
|
74
|
+
const readFile: FileReader = async (path) => local[path as keyof typeof local];
|
|
75
|
+
let request: Request | null = null;
|
|
76
|
+
const fetchImpl = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
77
|
+
request = new Request(input, init);
|
|
78
|
+
return new Response(
|
|
79
|
+
JSON.stringify({
|
|
80
|
+
soulMd: "same soul",
|
|
81
|
+
identityMd: "db identity",
|
|
82
|
+
claudeMd: "same claude",
|
|
83
|
+
toolsMd: "same tools",
|
|
84
|
+
heartbeatMd: "db heartbeat",
|
|
85
|
+
}),
|
|
86
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
87
|
+
);
|
|
88
|
+
}) as typeof fetch;
|
|
89
|
+
|
|
90
|
+
const result = await runProfileSyncAudit(
|
|
91
|
+
{
|
|
92
|
+
agentId: "agent-1",
|
|
93
|
+
apiUrl: "https://api.example.test",
|
|
94
|
+
apiKey: "secret-key",
|
|
95
|
+
claudeMdPath: WORKSPACE_CLAUDE_MD_PATH,
|
|
96
|
+
},
|
|
97
|
+
{ fetchImpl, readFile },
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(request?.url).toBe("https://api.example.test/me");
|
|
101
|
+
expect(request?.headers.get("X-Agent-ID")).toBe("agent-1");
|
|
102
|
+
expect(result.divergences.map((entry) => entry.field)).toEqual(["identityMd", "heartbeatMd"]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("uses the registered effective provider to select the Claude profile path", async () => {
|
|
106
|
+
const readPaths: string[] = [];
|
|
107
|
+
const readFile: FileReader = async (path) => {
|
|
108
|
+
readPaths.push(path);
|
|
109
|
+
return "same";
|
|
110
|
+
};
|
|
111
|
+
const fetchImpl = (async () =>
|
|
112
|
+
new Response(
|
|
113
|
+
JSON.stringify({
|
|
114
|
+
harnessProvider: "codex",
|
|
115
|
+
soulMd: "same",
|
|
116
|
+
identityMd: "same",
|
|
117
|
+
claudeMd: "same",
|
|
118
|
+
toolsMd: "same",
|
|
119
|
+
heartbeatMd: "same",
|
|
120
|
+
}),
|
|
121
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
122
|
+
)) as typeof fetch;
|
|
123
|
+
|
|
124
|
+
const result = await runProfileSyncAudit(
|
|
125
|
+
{
|
|
126
|
+
agentId: "agent-codex",
|
|
127
|
+
apiUrl: "https://api.example.test",
|
|
128
|
+
apiKey: "secret-key",
|
|
129
|
+
},
|
|
130
|
+
{ fetchImpl, readFile },
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(result.divergences).toEqual([]);
|
|
134
|
+
expect(readPaths).toContain(WORKSPACE_CLAUDE_MD_PATH);
|
|
135
|
+
expect(readPaths).not.toContain(CLAUDE_MD_PATH);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("refuses to guess the audit path when no effective provider is registered", async () => {
|
|
139
|
+
const fetchImpl = (async () =>
|
|
140
|
+
new Response(JSON.stringify({}), {
|
|
141
|
+
status: 200,
|
|
142
|
+
headers: { "Content-Type": "application/json" },
|
|
143
|
+
})) as typeof fetch;
|
|
144
|
+
|
|
145
|
+
expect(
|
|
146
|
+
runProfileSyncAudit(
|
|
147
|
+
{
|
|
148
|
+
agentId: "agent-unregistered",
|
|
149
|
+
apiUrl: "https://api.example.test",
|
|
150
|
+
apiKey: "secret-key",
|
|
151
|
+
},
|
|
152
|
+
{ fetchImpl, readFile: async () => undefined },
|
|
153
|
+
),
|
|
154
|
+
).rejects.toThrow("registered harness provider");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("treats profile fields absent on disk and empty in the DB as equal", async () => {
|
|
158
|
+
const readFile: FileReader = async () => undefined;
|
|
159
|
+
const fetchImpl = (async () =>
|
|
160
|
+
new Response(JSON.stringify({ soulMd: "" }), {
|
|
161
|
+
status: 200,
|
|
162
|
+
headers: { "Content-Type": "application/json" },
|
|
163
|
+
})) as typeof fetch;
|
|
164
|
+
|
|
165
|
+
const result = await runProfileSyncAudit(
|
|
166
|
+
{
|
|
167
|
+
agentId: "agent-unset",
|
|
168
|
+
apiUrl: "https://api.example.test",
|
|
169
|
+
apiKey: "secret-key",
|
|
170
|
+
claudeMdPath: WORKSPACE_CLAUDE_MD_PATH,
|
|
171
|
+
},
|
|
172
|
+
{ fetchImpl, readFile },
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect(result.divergences).toEqual([]);
|
|
176
|
+
expect(profileSyncAuditExitCode(result.divergences)).toBe(0);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("reports a missing local file when the DB value is non-empty", () => {
|
|
180
|
+
const divergences = findProfileDivergences(
|
|
181
|
+
{
|
|
182
|
+
soulMd: undefined,
|
|
183
|
+
identityMd: "same identity",
|
|
184
|
+
claudeMd: "same claude",
|
|
185
|
+
toolsMd: "same tools",
|
|
186
|
+
heartbeatMd: "same heartbeat",
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
soulMd: "stored soul",
|
|
190
|
+
identityMd: "same identity",
|
|
191
|
+
claudeMd: "same claude",
|
|
192
|
+
toolsMd: "same tools",
|
|
193
|
+
heartbeatMd: "same heartbeat",
|
|
194
|
+
},
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(divergences).toHaveLength(1);
|
|
198
|
+
expect(divergences[0]).toMatchObject({
|
|
199
|
+
field: "soulMd",
|
|
200
|
+
diskMissing: true,
|
|
201
|
+
diskSize: null,
|
|
202
|
+
diskHash: null,
|
|
203
|
+
dbSize: "stored soul".length,
|
|
204
|
+
delta: null,
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
28
209
|
describe("extractSetupScriptContent (marker extraction)", () => {
|
|
29
210
|
test("extracts ONLY the content between the agent-managed markers", () => {
|
|
30
211
|
const raw = [
|
|
@@ -97,6 +278,19 @@ describe("buildIdentityPayload (min-length guard)", () => {
|
|
|
97
278
|
expect(payload.heartbeatMd).toBe("");
|
|
98
279
|
});
|
|
99
280
|
|
|
281
|
+
test("forwards oversized gated identity fields so the server ratchet can decide", () => {
|
|
282
|
+
const oversized = "x".repeat(MAX_PROFILE_FILE_LENGTH + 1);
|
|
283
|
+
const payload = buildIdentityPayload({
|
|
284
|
+
soulMd: oversized,
|
|
285
|
+
identityMd: oversized,
|
|
286
|
+
toolsMd: oversized,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
expect(payload.soulMd).toBe(oversized);
|
|
290
|
+
expect(payload.identityMd).toBe(oversized);
|
|
291
|
+
expect(payload.toolsMd).toBe(oversized);
|
|
292
|
+
});
|
|
293
|
+
|
|
100
294
|
test("logs the agent, field, actual length, and cap when a file is too large", () => {
|
|
101
295
|
const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
102
296
|
try {
|
|
@@ -142,7 +336,13 @@ describe("collectProfilePayloads (field gate)", () => {
|
|
|
142
336
|
"session_sync",
|
|
143
337
|
files,
|
|
144
338
|
);
|
|
145
|
-
expect(all.map((p) => p.label).sort()).toEqual([
|
|
339
|
+
expect(all.map((p) => p.label).sort()).toEqual([
|
|
340
|
+
"claude",
|
|
341
|
+
"identity.identityMd",
|
|
342
|
+
"identity.soulMd",
|
|
343
|
+
"identity.toolsMd",
|
|
344
|
+
"setup",
|
|
345
|
+
]);
|
|
146
346
|
});
|
|
147
347
|
|
|
148
348
|
test("a missing file yields no payload for that group (no empty POST)", async () => {
|
|
@@ -413,11 +613,13 @@ describe("collectProfilePayloads (baseline integration)", () => {
|
|
|
413
613
|
});
|
|
414
614
|
|
|
415
615
|
const payloads = await collectProfilePayloads(["identity"], "self_edit", files);
|
|
416
|
-
expect(payloads).toHaveLength(
|
|
417
|
-
// self_edit should include ALL files regardless of baselines
|
|
418
|
-
expect(payloads
|
|
419
|
-
|
|
420
|
-
|
|
616
|
+
expect(payloads).toHaveLength(3);
|
|
617
|
+
// self_edit should include ALL files regardless of baselines, independently.
|
|
618
|
+
expect(payloads.map((payload) => payload.body)).toEqual([
|
|
619
|
+
{ soulMd: identityContent, changeSource: "self_edit" },
|
|
620
|
+
{ identityMd: identityContent, changeSource: "self_edit" },
|
|
621
|
+
{ toolsMd: "tools", changeSource: "self_edit" },
|
|
622
|
+
]);
|
|
421
623
|
});
|
|
422
624
|
|
|
423
625
|
test("session_sync skips unchanged CLAUDE.md when baseline matches", async () => {
|
|
@@ -482,3 +684,27 @@ describe("collectProfilePayloads (baseline integration)", () => {
|
|
|
482
684
|
expect(payloads).toEqual([]);
|
|
483
685
|
});
|
|
484
686
|
});
|
|
687
|
+
|
|
688
|
+
describe("buildIndependentIdentityPayloads", () => {
|
|
689
|
+
test("splits fields so one rejected write cannot discard valid siblings", () => {
|
|
690
|
+
expect(
|
|
691
|
+
buildIndependentIdentityPayloads(
|
|
692
|
+
{ soulMd: "rejected growth", toolsMd: "valid edit", heartbeatMd: "ungated edit" },
|
|
693
|
+
"session_sync",
|
|
694
|
+
),
|
|
695
|
+
).toEqual([
|
|
696
|
+
{
|
|
697
|
+
label: "identity.soulMd",
|
|
698
|
+
body: { soulMd: "rejected growth", changeSource: "session_sync" },
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
label: "identity.toolsMd",
|
|
702
|
+
body: { toolsMd: "valid edit", changeSource: "session_sync" },
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
label: "identity.heartbeatMd",
|
|
706
|
+
body: { heartbeatMd: "ungated edit", changeSource: "session_sync" },
|
|
707
|
+
},
|
|
708
|
+
]);
|
|
709
|
+
});
|
|
710
|
+
});
|