@desplega.ai/agent-swarm 1.159.0 → 1.161.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 +2 -2
- package/dist/{acp-adapter-qhch0n2v.js → acp-adapter-xx489vvb.js} +286 -75
- package/dist/{actions-dtsh1mfb.js → actions-ykjkn0m9.js} +32 -30
- package/dist/{anthropic-messages-f2h06k64.js → anthropic-messages-5qx80j9k.js} +99 -14
- package/dist/{app-y1b9srva.js → app-0hdqa4k1.js} +13 -11
- package/dist/{assistant-31hwy5xj.js → assistant-am2g5g8a.js} +40 -32
- package/dist/{azure-openai-responses-0k1mfax5.js → azure-openai-responses-fszbkp0e.js} +3 -3
- package/dist/{boot-reembed-48d2qpkt.js → boot-reembed-gt5gjkt4.js} +14 -12
- package/dist/{boot-reembed-w0bryhkr.js → boot-reembed-xy9t7avg.js} +15 -13
- package/dist/{boot-scrub-logs-t65wbrn7.js → boot-scrub-logs-9x0985y5.js} +12 -10
- package/dist/boot-seeds-trv0d4vg.js +29 -0
- package/dist/{claude-adapter-1t6611ne.js → claude-adapter-jb6e1e54.js} +32 -26
- package/dist/{claude-managed-adapter-werp659j.js → claude-managed-adapter-yxkfxg8j.js} +7 -7
- package/dist/{claude-managed-setup-y6rdgbc1.js → claude-managed-setup-d1q9hv2b.js} +26 -27
- package/dist/{claude-sdk-session-zhkyk8yw.js → claude-sdk-session-zga700s2.js} +11737 -11256
- package/dist/{cli-79aq9cgb.js → cli-0383bp7f.js} +1222 -406
- package/dist/{cli-xgpf00sk.js → cli-0c7ata9h.js} +24 -23
- package/dist/{cli-qjxy52w7.js → cli-121ph74b.js} +2 -2
- package/dist/{cli-20035trh.js → cli-17xpf7cm.js} +14 -7
- package/dist/{cli-cqt19wg6.js → cli-1a2j50m0.js} +11 -2
- package/dist/{cli-g9df2m37.js → cli-1cz0c18q.js} +249 -200
- package/dist/{cli-fjtp3463.js → cli-1er0rhz8.js} +13 -12
- package/dist/{cli-ca2w7zv7.js → cli-2hhfhfq5.js} +11 -6
- package/dist/{cli-npssxgaz.js → cli-2pdaysab.js} +52 -2
- package/dist/{cli-rykfmgwg.js → cli-2x0e561b.js} +1 -1
- package/dist/{cli-503ya656.js → cli-35pj2yva.js} +20 -150
- package/dist/cli-3qhyh31j.js +166 -0
- package/dist/{cli-cygyqj3b.js → cli-3zc55ctx.js} +8 -6
- package/dist/{cli-96qd5psa.js → cli-58e8yr0d.js} +1 -1
- package/dist/{cli-4aj25szq.js → cli-5nbmpy8w.js} +2 -2
- package/dist/{cli-mg24hkt4.js → cli-5p3x4zna.js} +2 -2
- package/dist/{cli-vwbbaecp.js → cli-7119ja3w.js} +3 -3
- package/dist/{cli-mzkqpxee.js → cli-798vkbks.js} +2 -2
- package/dist/{cli-3gvrr8vj.js → cli-82c1pacn.js} +111 -54
- package/dist/{cli-hgqfttfc.js → cli-89s4ngw3.js} +1 -1
- package/dist/{cli-9b13ggd1.js → cli-89v6kyq7.js} +1 -0
- package/dist/{cli-b6thtzac.js → cli-8g6mc14k.js} +7 -7
- package/dist/{cli-eg7cdg68.js → cli-9m03pzga.js} +5 -5
- package/dist/cli-9s68w752.js +303 -0
- package/dist/{cli-48527fpw.js → cli-a8c82p2t.js} +16 -15
- package/dist/{cli-5f2hq128.js → cli-ad4qxm6j.js} +12 -11
- package/dist/{cli-e4xnc0qp.js → cli-akfj32vj.js} +33 -7
- package/dist/{cli-ejpygnqz.js → cli-bh7jed3k.js} +212 -22
- package/dist/{cli-0z2v4nhw.js → cli-cat6h73s.js} +1 -1
- package/dist/{cli-e0ndxhtk.js → cli-cfkrztza.js} +190 -13
- package/dist/{cli-kc3dp84d.js → cli-d56tgd45.js} +216 -59
- package/dist/cli-dbmwby6a.js +9 -0
- package/dist/{cli-0n2e4x49.js → cli-ecfxwnyb.js} +15 -2
- package/dist/{cli-gzyh0j0g.js → cli-ee8bc46j.js} +66 -166
- package/dist/{cli-kdnehe0t.js → cli-et8m9xsv.js} +1 -1
- package/dist/cli-f9w1semm.js +413 -0
- package/dist/cli-gt4hxb8p.js +99 -0
- package/dist/{cli-qv4jv8bx.js → cli-hgmnf38m.js} +2 -2
- package/dist/{cli-10ym38qc.js → cli-hn04t9db.js} +3 -3
- package/dist/{cli-qvnnx4ct.js → cli-jpf7e1zq.js} +105 -243
- package/dist/cli-jyy6pm2f.js +333872 -0
- package/dist/{cli-dvjpkkjd.js → cli-kgd3p0bm.js} +7 -7
- package/dist/cli-ma3zvh39.js +10404 -0
- package/dist/{cli-r2ap2czm.js → cli-nexqx4ve.js} +6 -0
- package/dist/{cli-j9z8mfx3.js → cli-nsdxtcbg.js} +37 -20
- package/dist/{cli-wgjx0q33.js → cli-phfg8vy8.js} +392 -53
- package/dist/{cli-rgjprct3.js → cli-ptr8h7kp.js} +19 -1
- package/dist/{cli-s8s499xy.js → cli-qbksc292.js} +3 -2
- package/dist/{cli-bv0a38v9.js → cli-qwsnmjdd.js} +515 -229
- package/dist/{cli-68bmme5p.js → cli-r6fsxc76.js} +40018 -52654
- package/dist/{cli-rrqmqr1s.js → cli-rtx9dygp.js} +1 -1
- package/dist/{cli-rfezr056.js → cli-sqm3r5ps.js} +83 -15
- package/dist/{cli-zw65mab6.js → cli-t6v2ah3r.js} +67 -13
- package/dist/{cli-vn0qv9cj.js → cli-t845n9xk.js} +28 -14
- package/dist/cli-trc24wk2.js +143 -0
- package/dist/{cli-wd9hz40d.js → cli-tre4bdgq.js} +5 -5
- package/dist/{cli-9yg65c1j.js → cli-tsk3nz8a.js} +29 -1
- package/dist/{cli-phsaj4xq.js → cli-v0pvvhfq.js} +1 -1
- package/dist/{cli-hj65wsts.js → cli-vnnz5ncg.js} +4695 -4915
- package/dist/{cli-cpvsaqsw.js → cli-w0bwte75.js} +8 -5
- package/dist/{cli-z9x0sh1t.js → cli-we0q7t0e.js} +3 -3
- package/dist/{cli-r94z9p1z.js → cli-wgbezhw2.js} +3 -0
- package/dist/{cli-19jenss3.js → cli-xk79303e.js} +51 -17
- package/dist/{cli-31nkw3g3.js → cli-ybgv4kw2.js} +15 -8
- package/dist/{cli-ardpzhjm.js → cli-yrg7c73k.js} +13 -6
- package/dist/{cli-de1396j2.js → cli-ywybs5as.js} +7 -1
- package/dist/{cli-vhjwh6cz.js → cli-yy6dew39.js} +2 -2
- package/dist/{cli-bgv70kym.js → cli-zb6xbk61.js} +3 -2
- package/dist/{cli-4tvgz99m.js → cli-ze1d3wzv.js} +79 -27
- package/dist/cli.js +33 -32
- package/dist/{cloudflare-workers-ai-system-one-1sg586qv.js → cloudflare-workers-ai-system-one-k6fbxw9d.js} +2 -2
- package/dist/{codex-adapter-jqfqqa21.js → codex-adapter-04v97vep.js} +23 -20
- package/dist/{codex-hook-5eys3dya.js → codex-hook-20me6byw.js} +2 -2
- package/dist/{codex-login-he9vrn90.js → codex-login-cf7mzsgg.js} +48 -3
- package/dist/{codex-session-runner-zd341dsm.js → codex-session-runner-ax69mqq3.js} +21 -20
- package/dist/{commands-vfsysz4s.js → commands-18gy08n1.js} +12 -10
- package/dist/{db-s3znhvbn.js → db-d2pvjpyt.js} +22 -10
- package/dist/{devin-adapter-8vgkwh29.js → devin-adapter-dpsv6hd9.js} +1 -1
- package/dist/{dispatcher-5xn0c7rf.js → dispatcher-8wbwyzjg.js} +31 -29
- package/dist/{dsh-adapter-894mgfvs.js → dsh-adapter-ysqk9snv.js} +9 -4
- package/dist/{e2b-ez07znam.js → e2b-esmt1r16.js} +3 -1
- package/dist/{execute-71ysgv35.js → execute-cmagrbrz.js} +235 -52
- package/dist/{google-generative-ai-1nfcdphp.js → google-generative-ai-k6n5nbpp.js} +3 -3
- package/dist/{google-vertex-1tqcd7b1.js → google-vertex-44xxtcvp.js} +3 -3
- package/dist/{handlers-5w4jwycr.js → handlers-6b69m5kp.js} +34 -32
- package/dist/{hook-z0hbxj85.js → hook-k3mydf2n.js} +16 -14
- package/dist/{hook-853bxrr6.js → hook-srnkt0hb.js} +16 -14
- package/dist/{http-akc3wy3m.js → http-hrf80ca1.js} +836 -189
- package/dist/{index-ttpbhnjg.js → index-1asbaj62.js} +32 -30
- package/dist/{index-cd151qkq.js → index-45evq4ee.js} +31 -29
- package/dist/{index-k661dzzr.js → index-9rxdvj6w.js} +32 -30
- package/dist/index-k4nj18qs.js +31 -0
- package/dist/{index-5sz16ksh.js → index-xmevk2b9.js} +89 -61
- package/dist/{keepalive-8p9rpvz5.js → keepalive-sfdz6ksp.js} +14 -12
- package/dist/{lead-jp69z5h3.js → lead-ej91e9hw.js} +52 -50
- package/dist/{llama-cpp-classify-t2tccams.js → llama-cpp-classify-tgj35k8d.js} +1 -1
- package/dist/{maintenance-fwx4ev0k.js → maintenance-80dnd7ke.js} +15 -13
- package/dist/{mistral-conversations-bhvwqcnk.js → mistral-conversations-pc3ntnxt.js} +1 -1
- package/dist/node-f0ykebbv.js +2107 -0
- package/dist/node-wyamey5r.js +19 -0
- package/dist/{oauth-refresh-sweep-cwcq1ctx.js → oauth-refresh-sweep-m1etk2mx.js} +13 -11
- package/dist/{onboard-2b4q0t84.js → onboard-0q8sdfqc.js} +2 -2
- package/dist/{openai-codex-responses-nekthgks.js → openai-codex-responses-n7n683p2.js} +2 -2
- package/dist/{openai-completions-hxtzwj0s.js → openai-completions-jpaj5g2b.js} +2 -2
- package/dist/{openai-responses-w4r118w7.js → openai-responses-9a3nme9j.js} +3 -3
- package/dist/{opencode-adapter-zz7xvjj6.js → opencode-adapter-4wcgmvgf.js} +13 -11
- package/dist/{openrouter-images-b6sqqjgn.js → openrouter-images-8yx3487g.js} +1 -1
- package/dist/{otel-impl-g8rs4vwr.js → otel-impl-hnqm9mxq.js} +2 -2
- package/dist/pi-logo-animation-e9a0t68z.js +1003 -0
- package/dist/{pi-mono-adapter-n0me28wt.js → pi-mono-adapter-d6sqm9wr.js} +301 -78
- package/dist/{pricing-refresh-0v77385v.js → pricing-refresh-y60z64pm.js} +14 -13
- package/dist/quickjs-r1pq2107.wasm +0 -0
- package/dist/{rbac-roles-krjyazbj.js → rbac-roles-8pa0754b.js} +13 -11
- package/dist/{render-v2-3wjsp9aw.js → render-v2-418s1vj7.js} +13 -11
- package/dist/{runtime-ktjysacx.js → runtime-3pknc168.js} +5 -4
- package/dist/{setup-yeqryke6.js → setup-yghdkj4x.js} +2 -2
- package/dist/{typesafe-system-one-96960jxp.js → typesafe-system-one-2ef30pec.js} +2 -2
- package/dist/{virtual-modules-dzdeq1sf.js → virtual-modules-jcceevt5.js} +8 -6
- package/dist/{work-objects-3pakpkw0.js → work-objects-j1sz20v9.js} +34 -32
- package/dist/{worker-85testgj.js → worker-d4gcvkjt.js} +52 -50
- package/dist/{x-n3e0v3xa.js → x-f1gt1pz3.js} +2 -2
- package/openapi.json +587 -19
- package/package.json +16 -15
- package/src/azure-devops/api.ts +121 -0
- package/src/azure-devops/auth.ts +78 -0
- package/src/azure-devops/handlers.ts +413 -0
- package/src/azure-devops/index.ts +27 -0
- package/src/azure-devops/templates.ts +108 -0
- package/src/azure-devops/types.ts +90 -0
- package/src/be/db/api-keys.ts +70 -0
- package/src/be/db/tasks/read.ts +1 -1
- package/src/be/db/tasks/write.ts +16 -0
- package/src/be/db-client.ts +11 -2
- package/src/be/db-retention.ts +95 -32
- package/src/be/db.ts +187 -24
- package/src/be/memory/constants.ts +16 -0
- package/src/be/memory/graph-expansion.ts +8 -1
- package/src/be/memory/index-content.ts +20 -1
- package/src/be/memory/key-guard.ts +93 -0
- package/src/be/memory/key-paths.ts +103 -0
- package/src/be/memory/providers/sqlite-store.ts +203 -60
- package/src/be/memory/raters/llm-summarizer.ts +10 -4
- package/src/be/memory/raters/llm.ts +15 -5
- package/src/be/memory/raters/store.ts +6 -3
- package/src/be/memory/raters/types.ts +11 -0
- package/src/be/memory/reranker.ts +38 -7
- package/src/be/memory/types.ts +12 -1
- package/src/be/migrations/185_api_key_auth_failures.sql +6 -0
- package/src/be/migrations/186_api_key_seat_mismatch.sql +8 -0
- package/src/be/migrations/190_memory_rating_model.sql +7 -0
- package/src/be/migrations/191_context_versions_previous_index.sql +8 -0
- package/src/be/model-tier-keys.ts +16 -1
- package/src/be/model-tier-resolution.ts +63 -18
- package/src/be/model-validation.ts +86 -10
- package/src/be/pricing-normalize.ts +4 -2
- package/src/be/scripts/typecheck.ts +4 -3
- package/src/be/seed/runner.ts +18 -12
- package/src/be/seed/types.ts +1 -0
- package/src/be/seed-pricing.ts +16 -0
- package/src/be/seed-scripts/catalog/task-failure-audit.ts +26 -26
- package/src/be/swarm-config-guard.ts +13 -1
- package/src/commands/claude-managed-setup.ts +33 -31
- package/src/commands/codex-login.ts +67 -0
- package/src/commands/credential-outcome-report.ts +191 -0
- package/src/commands/provider-credentials.ts +28 -15
- package/src/commands/rate-limit-outcome.ts +34 -4
- package/src/commands/runner.ts +479 -228
- package/src/commands/seat-mismatch-report.ts +46 -0
- package/src/e2b/env.ts +2 -0
- package/src/extensions/apply-task-create.ts +27 -1
- package/src/extensions/catalog.generated.json +17 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/github/handlers.ts +10 -74
- package/src/github/reactions.ts +119 -138
- package/src/github/task-reactions.ts +34 -11
- package/src/heartbeat/heartbeat.ts +15 -2
- package/src/hooks/hook.ts +1 -1
- package/src/http/agents.ts +15 -2
- package/src/http/api-keys.ts +140 -2
- package/src/http/codex-oauth-keep-warm.ts +3 -5
- package/src/http/core.ts +12 -0
- package/src/http/extensions.ts +51 -27
- package/src/http/index.ts +4 -0
- package/src/http/mcp-user.ts +8 -13
- package/src/http/mcp.ts +19 -12
- package/src/http/memory.ts +91 -18
- package/src/http/poll.ts +9 -1
- package/src/http/schedules.ts +17 -2
- package/src/http/script-runs.ts +12 -0
- package/src/http/session-data.ts +6 -4
- package/src/http/stats.ts +1 -0
- package/src/http/status.ts +15 -1
- package/src/http/tasks.ts +80 -8
- package/src/http/users.ts +1 -1
- package/src/http/webhooks.ts +120 -2
- package/src/http/workflows.ts +11 -0
- package/src/mcp-client/http-client.ts +5 -0
- package/src/memory/recall-query.ts +74 -0
- package/src/prompts/base-prompt.ts +15 -4
- package/src/prompts/session-templates.ts +22 -1
- package/src/providers/acp-adapter.ts +1 -0
- package/src/providers/claude-adapter.ts +98 -21
- package/src/providers/claude-managed-adapter.ts +30 -2
- package/src/providers/claude-sdk-session.ts +3 -1
- package/src/providers/codex-adapter.ts +11 -11
- package/src/providers/codex-skill-resolver.ts +6 -0
- package/src/providers/devin-adapter.ts +1 -0
- package/src/providers/dsh-adapter.ts +205 -7
- package/src/providers/harness-version.ts +83 -7
- package/src/providers/opencode-adapter.ts +77 -4
- package/src/providers/pi-binary-root/src/extensions/codemode/worker.ts +11 -0
- package/src/providers/pi-codemode-runtime.ts +17 -0
- package/src/providers/pi-mono-adapter.ts +387 -83
- package/src/providers/pi-mono-extension.ts +17 -2
- package/src/providers/pricing-sources.md +5 -1
- package/src/providers/reasoning-effort.ts +10 -2
- package/src/providers/types.ts +9 -1
- package/src/rbac/legacy-policy.ts +20 -0
- package/src/rbac/permissions.ts +15 -0
- package/src/scripts-runtime/types/stdlib.d.ts +13 -5
- package/src/scripts-runtime/types/swarm-extension.d.ts +4 -2
- package/src/scripts-runtime/types/swarm-sdk.d.ts +13 -5
- package/src/slack/assistant.ts +13 -0
- package/src/slack/render-v2.ts +87 -37
- package/src/slack/session-status.ts +541 -0
- package/src/tasks/context-key.ts +38 -1
- package/src/tasks/sibling-awareness.ts +3 -2
- package/src/telemetry.ts +5 -4
- package/src/templates.d.ts +6 -0
- package/src/tests/api-key-auth-failure-route.test.ts +142 -0
- package/src/tests/api-key-tracking.test.ts +368 -1
- package/src/tests/azure-devops-auth.test.ts +100 -0
- package/src/tests/azure-devops-handlers.test.ts +324 -0
- package/src/tests/azure-devops-webhook-malformed.test.ts +96 -0
- package/src/tests/base-prompt.test.ts +40 -2
- package/src/tests/claude-adapter.test.ts +58 -0
- package/src/tests/claude-default-route.test.ts +260 -0
- package/src/tests/claude-managed-adapter.test.ts +47 -0
- package/src/tests/claude-managed-setup.test.ts +48 -0
- package/src/tests/claude-prompt-body-once.test.ts +117 -0
- package/src/tests/claude-routes-e2e.test.ts +316 -0
- package/src/tests/codex-auth-failure.test.ts +52 -0
- package/src/tests/codex-login.test.ts +116 -0
- package/src/tests/context-versions-retention.test.ts +314 -0
- package/src/tests/core-auth.test.ts +7 -0
- package/src/tests/credential-exhaustion-policy.test.ts +118 -3
- package/src/tests/credential-outcome-report.test.ts +171 -0
- package/src/tests/credential-status-api.test.ts +103 -2
- package/src/tests/db-retention.test.ts +14 -14
- package/src/tests/db-task-read-contract.test.ts +6 -0
- package/src/tests/db-task-write-contract.test.ts +533 -1
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dokploy-deploy.test.ts +315 -0
- package/src/tests/dsh-adapter.test.ts +134 -2
- package/src/tests/dsh-usage.test.ts +153 -0
- package/src/tests/explicit-model-validation.test.ts +253 -2
- package/src/tests/extensions-catalog.test.ts +1 -0
- package/src/tests/extensions-example-deploy-awareness.test.ts +301 -0
- package/src/tests/extensions-http.test.ts +137 -10
- package/src/tests/extensions-mcp-tools.test.ts +28 -0
- package/src/tests/extensions-pre-task.test.ts +73 -0
- package/src/tests/fetch-resolved-env.test.ts +70 -0
- package/src/tests/fixtures/azure-devops/pull-request-commented.json +129 -0
- package/src/tests/fixtures/azure-devops/pull-request-created.json +88 -0
- package/src/tests/fixtures/fake-claude-routes.ts +229 -0
- package/src/tests/github-handlers-reactions.test.ts +486 -0
- package/src/tests/github-reactions.test.ts +280 -0
- package/src/tests/hang-watchdog-worker.ts +47 -0
- package/src/tests/hang-watchdog.test.ts +45 -0
- package/src/tests/hang-watchdog.ts +30 -0
- package/src/tests/harness-cli-version.test.ts +62 -0
- package/src/tests/harness-model-guard.test.ts +123 -0
- package/src/tests/harness-version.test.ts +70 -3
- package/src/tests/http-api-integration.test.ts +21 -0
- package/src/tests/http-task-write-ownership.test.ts +195 -0
- package/src/tests/internal-ai/complete-structured.test.ts +94 -0
- package/src/tests/internal-ai/credentials.test.ts +22 -0
- package/src/tests/internal-ai/models.test.ts +36 -2
- package/src/tests/internal-ai/schema-parity.test.ts +25 -1
- package/src/tests/internal-ai/summarize-session.test.ts +23 -7
- package/src/tests/mcp-user-route.test.ts +133 -0
- package/src/tests/memory-fts-populate.test.ts +139 -0
- package/src/tests/memory-http-recall-gating.test.ts +125 -1
- package/src/tests/memory-key-paths.test.ts +1055 -0
- package/src/tests/memory-rate-endpoint.test.ts +66 -0
- package/src/tests/memory-rater-llm-summarizer.test.ts +2 -1
- package/src/tests/memory-rater-llm.test.ts +73 -0
- package/src/tests/memory-rater-store.test.ts +31 -0
- package/src/tests/memory-reranker.test.ts +40 -0
- package/src/tests/model-rate-limit-windows.test.ts +19 -0
- package/src/tests/model-tier-resolution.test.ts +92 -0
- package/src/tests/opencode-adapter.test.ts +214 -2
- package/src/tests/opencode-plugin.test.ts +4 -0
- package/src/tests/pi-codemode.test.ts +324 -0
- package/src/tests/pi-mono-adapter.test.ts +3 -0
- package/src/tests/pi-native-mcp.test.ts +193 -0
- package/src/tests/pi-tool-exposure.test.ts +374 -0
- package/src/tests/preload.ts +9 -0
- package/src/tests/prompt-template-session.test.ts +5 -3
- package/src/tests/provider-registration.test.ts +0 -2
- package/src/tests/providers/pi-cost.test.ts +190 -4
- package/src/tests/rate-limit-event.test.ts +65 -0
- package/src/tests/rate-limit-outcome.test.ts +90 -0
- package/src/tests/rbac-engine.test.ts +41 -2
- package/src/tests/runner-key-completion-outcome.test.ts +112 -0
- package/src/tests/runner-key-outcome-late-reset.test.ts +224 -0
- package/src/tests/runner-registration-retry.test.ts +149 -0
- package/src/tests/runner-slot-reconcile.test.ts +199 -0
- package/src/tests/script-connections.test.ts +8 -0
- package/src/tests/seat-mismatch-report.test.ts +85 -0
- package/src/tests/seed-scripts.test.ts +49 -0
- package/src/tests/seed.test.ts +25 -0
- package/src/tests/session-costs-recompute-all-providers.test.ts +23 -0
- package/src/tests/slack-render-v2.test.ts +96 -0
- package/src/tests/slack-session-status.test.ts +626 -0
- package/src/tests/status.test.ts +29 -0
- package/src/tests/steering-promotion.test.ts +51 -0
- package/src/tests/store-progress-ownership.test.ts +136 -0
- package/src/tests/subscription-plans.test.ts +28 -0
- package/src/tests/task-citations.test.ts +3 -2
- package/src/tests/task-reactions.test.ts +29 -6
- package/src/tests/vcs-azure-devops.test.ts +73 -0
- package/src/tests/webhooks-task-blocked.test.ts +23 -0
- package/src/tests/workflow-agent-task.test.ts +30 -0
- package/src/tests/workflow-llm.test.ts +14 -0
- package/src/tests/workflow-triggers-v2.test.ts +320 -0
- package/src/tools/context-diff.ts +20 -0
- package/src/tools/extension-install.ts +27 -16
- package/src/tools/memory-edit.ts +47 -3
- package/src/tools/memory-search.ts +13 -1
- package/src/tools/memory-store.ts +55 -3
- package/src/tools/schedules/create-schedule.ts +1 -1
- package/src/tools/schedules/patch-schedule.ts +16 -3
- package/src/tools/schedules/update-schedule.ts +16 -3
- package/src/tools/send-task.ts +10 -7
- package/src/tools/store-progress.ts +49 -4
- package/src/tools/task-action.ts +2 -1
- package/src/tools/workflows/create-workflow.ts +8 -2
- package/src/tools/workflows/update-workflow.ts +8 -2
- package/src/types.ts +54 -7
- package/src/utils/codex-auth-failure.ts +34 -0
- package/src/utils/credentials.test.ts +31 -1
- package/src/utils/credentials.ts +92 -39
- package/src/utils/dsh-usage.ts +53 -0
- package/src/utils/error-tracker.ts +80 -0
- package/src/utils/harness-cli-version.ts +6 -4
- package/src/utils/internal-ai/complete-structured.ts +26 -3
- package/src/utils/internal-ai/credentials.ts +23 -9
- package/src/utils/internal-ai/index.ts +8 -1
- package/src/utils/internal-ai/models.ts +34 -8
- package/src/utils/internal-ai/summarize-session.ts +22 -8
- package/src/utils/model-rate-limit-windows.ts +12 -0
- package/src/utils/runtime-model-catalog.ts +15 -0
- package/src/utils/secret-scrubber.ts +8 -0
- package/src/utils/spawn-model-guard.ts +43 -0
- package/src/utils/subscription-plans.ts +22 -0
- package/src/vcs/azure-devops.ts +80 -0
- package/src/vcs/index.ts +5 -1
- package/src/vcs/types.ts +2 -2
- package/src/workflows/executors/agent-task.ts +7 -6
- package/src/workflows/model-validation.ts +45 -0
- package/src/workflows/patch-definition.ts +5 -0
- package/src/workflows/triggers.ts +85 -0
- package/templates/community/open-prompt-showreel/PLAYBOOK.md +336 -0
- package/templates/community/open-prompt-showreel/README.md +33 -0
- package/templates/community/open-prompt-showreel/example/tla-races/facts.ts +145 -0
- package/templates/community/open-prompt-showreel/example/tla-races/facts.v3.json +140 -0
- package/templates/community/open-prompt-showreel/example/tla-races/music.mist-city.json +8 -0
- package/templates/community/open-prompt-showreel/example/tla-races/reel.html +805 -0
- package/templates/community/open-prompt-showreel/example/tla-races/smoke.sh +48 -0
- package/templates/community/open-prompt-showreel/lead-prompt.md +89 -0
- package/templates/community/open-prompt-showreel/setup.sh +167 -0
- package/templates/community/open-prompt-showreel/skills/motion-design-replication/SKILL.md +85 -0
- package/templates/community/open-prompt-showreel/skills/motion-design-video-analysis/SKILL.md +115 -0
- package/templates/community/open-prompt-showreel/skills/open-prompt-showreel/SKILL.md +147 -0
- package/templates/community/open-prompt-showreel/skills/video-generation/SKILL.md +195 -0
- package/templates/community/open-prompt-showreel/tools/fetch-music.sh +65 -0
- package/templates/community/open-prompt-showreel/tools/music-analyze.py +69 -0
- package/templates/community/open-prompt-showreel/tools/music-current.sh +34 -0
- package/templates/community/open-prompt-showreel/tools/music-cut.sh +36 -0
- package/templates/community/open-prompt-showreel/tools/music-refine.py +39 -0
- package/templates/community/open-prompt-showreel/tools/render.mjs +157 -0
- package/templates/community/open-prompt-showreel/tools/sheet.sh +44 -0
- package/templates/community/open-prompt-showreel/tools/web-compress.sh +17 -0
- package/templates/community/open-prompt-showreel/tools/x-reencode.sh +21 -0
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/deploy-awareness/README.md +28 -0
- package/templates/extensions/deploy-awareness/hooks.ts +152 -0
- package/templates/extensions/deploy-awareness/manifest.json +8 -0
- package/templates/skills/memory/config.json +2 -2
- package/templates/skills/memory/content.md +59 -5
- package/templates/skills/swarm-extensions/config.json +1 -1
- package/templates/skills/swarm-extensions/content.md +28 -5
- package/templates/skills/workflow-iterate/content.md +1 -0
- package/dist/boot-seeds-h7c32hvm.js +0 -27
- package/dist/cli-2djtr51b.js +0 -48
- package/dist/cli-7zttzetj.js +0 -324
- package/dist/cli-j490egb5.js +0 -5632
- package/dist/cli-m4excttz.js +0 -333481
- package/dist/index-bjmjh9h4.js +0 -29
|
@@ -1,20 +1,43 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
-
import {
|
|
1
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { readInstalledCliPackageVersion, readPkgVersion } from "../providers/harness-version";
|
|
3
6
|
|
|
4
7
|
describe("readPkgVersion", () => {
|
|
5
|
-
test("reads pi version from the
|
|
8
|
+
test("reads the pi version from the installed package without spawning", () => {
|
|
9
|
+
const spawn = mock(() => ({ stdout: "9.9.9\n", stderr: "", status: 0 }));
|
|
10
|
+
const requirePackageJson = mock(() => ({ version: "0.0.0" }));
|
|
11
|
+
const readInstalledVersion = mock(() => "0.79.1");
|
|
12
|
+
|
|
13
|
+
const version = readPkgVersion("@earendil-works/pi-coding-agent", {
|
|
14
|
+
requirePackageJson,
|
|
15
|
+
spawn,
|
|
16
|
+
readInstalledVersion,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(version).toBe("0.79.1");
|
|
20
|
+
expect(readInstalledVersion).toHaveBeenCalledWith("pi", "@earendil-works/pi-coding-agent");
|
|
21
|
+
expect(spawn).not.toHaveBeenCalled();
|
|
22
|
+
expect(requirePackageJson).not.toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("reads pi version from the CLI when the installed package cannot be found", () => {
|
|
6
26
|
const spawn = mock(() => ({ stdout: "0.79.1\n", stderr: "", status: 0 }));
|
|
7
27
|
const requirePackageJson = mock(() => ({ version: "0.0.0" }));
|
|
8
28
|
|
|
9
29
|
const version = readPkgVersion("@earendil-works/pi-coding-agent", {
|
|
10
30
|
requirePackageJson,
|
|
11
31
|
spawn,
|
|
32
|
+
readInstalledVersion: () => undefined,
|
|
12
33
|
});
|
|
13
34
|
|
|
14
35
|
expect(version).toBe("0.79.1");
|
|
15
36
|
expect(spawn).toHaveBeenCalledWith("pi", ["--version"], {
|
|
16
37
|
encoding: "utf8",
|
|
17
38
|
stdio: ["ignore", "pipe", "pipe"],
|
|
39
|
+
timeout: 5_000,
|
|
40
|
+
killSignal: "SIGKILL",
|
|
18
41
|
});
|
|
19
42
|
expect(requirePackageJson).not.toHaveBeenCalled();
|
|
20
43
|
});
|
|
@@ -26,12 +49,15 @@ describe("readPkgVersion", () => {
|
|
|
26
49
|
const version = readPkgVersion("@opencode-ai/sdk", {
|
|
27
50
|
requirePackageJson,
|
|
28
51
|
spawn,
|
|
52
|
+
readInstalledVersion: () => undefined,
|
|
29
53
|
});
|
|
30
54
|
|
|
31
55
|
expect(version).toBe("1.16.2");
|
|
32
56
|
expect(spawn).toHaveBeenCalledWith("opencode", ["--version"], {
|
|
33
57
|
encoding: "utf8",
|
|
34
58
|
stdio: ["ignore", "pipe", "pipe"],
|
|
59
|
+
timeout: 5_000,
|
|
60
|
+
killSignal: "SIGKILL",
|
|
35
61
|
});
|
|
36
62
|
expect(requirePackageJson).not.toHaveBeenCalled();
|
|
37
63
|
});
|
|
@@ -40,8 +66,49 @@ describe("readPkgVersion", () => {
|
|
|
40
66
|
const version = readPkgVersion("@earendil-works/pi-coding-agent", {
|
|
41
67
|
requirePackageJson: () => ({ version: "0.79.1" }),
|
|
42
68
|
spawn: mock(() => ({ stdout: "", stderr: "", status: 0 })),
|
|
69
|
+
readInstalledVersion: () => undefined,
|
|
43
70
|
});
|
|
44
71
|
|
|
45
72
|
expect(version).toBe("0.79.1");
|
|
46
73
|
});
|
|
47
74
|
});
|
|
75
|
+
|
|
76
|
+
describe("readInstalledCliPackageVersion", () => {
|
|
77
|
+
const originalPath = process.env.PATH;
|
|
78
|
+
const tmpRoots: string[] = [];
|
|
79
|
+
|
|
80
|
+
afterEach(() => {
|
|
81
|
+
process.env.PATH = originalPath;
|
|
82
|
+
for (const dir of tmpRoots.splice(0)) rmSync(dir, { recursive: true, force: true });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/** A global-install layout: `<root>/bin/fake-cli` -> `<root>/lib/node_modules/@acme/fake/dist/bundle/cli.js`. */
|
|
86
|
+
function installFakeCli(packageName: string, version: string): string {
|
|
87
|
+
const root = mkdtempSync(join(tmpdir(), "harness-version-"));
|
|
88
|
+
tmpRoots.push(root);
|
|
89
|
+
const pkgDir = join(root, "lib", "node_modules", "@acme", "fake");
|
|
90
|
+
mkdirSync(join(pkgDir, "dist", "bundle"), { recursive: true });
|
|
91
|
+
mkdirSync(join(root, "bin"), { recursive: true });
|
|
92
|
+
writeFileSync(join(pkgDir, "package.json"), JSON.stringify({ name: packageName, version }));
|
|
93
|
+
// A nested package.json without a matching name must be walked past.
|
|
94
|
+
writeFileSync(join(pkgDir, "dist", "package.json"), JSON.stringify({ type: "module" }));
|
|
95
|
+
writeFileSync(join(pkgDir, "dist", "bundle", "cli.js"), "#!/usr/bin/env node\n");
|
|
96
|
+
symlinkSync(join(pkgDir, "dist", "bundle", "cli.js"), join(root, "bin", "fake-cli"));
|
|
97
|
+
return join(root, "bin");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
test("resolves the owning package version through the PATH symlink", () => {
|
|
101
|
+
process.env.PATH = installFakeCli("@acme/fake", "1.2.3");
|
|
102
|
+
expect(readInstalledCliPackageVersion("fake-cli", "@acme/fake")).toBe("1.2.3");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("returns undefined when the owning package is a different one", () => {
|
|
106
|
+
process.env.PATH = installFakeCli("@acme/other", "1.2.3");
|
|
107
|
+
expect(readInstalledCliPackageVersion("fake-cli", "@acme/fake")).toBeUndefined();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("returns undefined when the command is not on PATH", () => {
|
|
111
|
+
process.env.PATH = installFakeCli("@acme/fake", "1.2.3");
|
|
112
|
+
expect(readInstalledCliPackageVersion("not-installed-cli", "@acme/fake")).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -711,6 +711,27 @@ describe("Tasks", () => {
|
|
|
711
711
|
expect(body.alreadyFinished).toBe(true);
|
|
712
712
|
});
|
|
713
713
|
|
|
714
|
+
test("POST /api/tasks/:id/finish — records the provider of a task whose harness never started", async () => {
|
|
715
|
+
const agentId = randomUUID();
|
|
716
|
+
expect((await post("/api/agents", { agentId, body: { name: "SpawnFailWorker" } })).status).toBe(
|
|
717
|
+
201,
|
|
718
|
+
);
|
|
719
|
+
const created = await post("/api/tasks", {
|
|
720
|
+
agentId: ids.leadAgent,
|
|
721
|
+
body: { task: "Spawn failure provider test", agentId, routingReason: "human_pinned" },
|
|
722
|
+
});
|
|
723
|
+
const taskId = created.body.id as string;
|
|
724
|
+
expect((await get("/api/poll", { agentId })).body.trigger?.taskId).toBe(taskId);
|
|
725
|
+
|
|
726
|
+
const finished = await post(`/api/tasks/${taskId}/finish`, {
|
|
727
|
+
agentId,
|
|
728
|
+
body: { status: "failed", failureReason: "Spawn failed: no binary", provider: "dsh" },
|
|
729
|
+
});
|
|
730
|
+
expect(finished.status).toBe(200);
|
|
731
|
+
expect(finished.body.task.status).toBe("failed");
|
|
732
|
+
expect(finished.body.task.provider).toBe("dsh");
|
|
733
|
+
});
|
|
734
|
+
|
|
714
735
|
test("POST /api/tasks/:id/finish — terminal retries reject, no-op, or force text only", async () => {
|
|
715
736
|
const agentId = randomUUID();
|
|
716
737
|
const registered = await post("/api/agents", {
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
createServer as createHttpServer,
|
|
4
|
+
type IncomingMessage,
|
|
5
|
+
type Server,
|
|
6
|
+
type ServerResponse,
|
|
7
|
+
} from "node:http";
|
|
8
|
+
import {
|
|
9
|
+
closeDb,
|
|
10
|
+
completeTask,
|
|
11
|
+
createAgent,
|
|
12
|
+
createTaskExtended,
|
|
13
|
+
getTaskById,
|
|
14
|
+
initDb,
|
|
15
|
+
startTask,
|
|
16
|
+
} from "../be/db";
|
|
17
|
+
import { handleTasks } from "../http/tasks";
|
|
18
|
+
import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
19
|
+
import { type HttpRequestAuth, setRequestAuth } from "../utils/request-auth-context";
|
|
20
|
+
import { listenOnFreePort } from "./test-net";
|
|
21
|
+
|
|
22
|
+
// Regression: an agent got 200 from HTTP progress and HTTP cancel on another
|
|
23
|
+
// agent's in-progress task. Progress follows task.progress.write (assignee,
|
|
24
|
+
// lead, human); cancel follows the MCP cancel-task policy for agents (lead or
|
|
25
|
+
// creator). Humans and the keyed runner wrapper keep access.
|
|
26
|
+
const ownerId = "aaaa0000-0000-4000-8000-000000000b01";
|
|
27
|
+
const otherId = "bbbb0000-0000-4000-8000-000000000b02";
|
|
28
|
+
const creatorId = "cccc0000-0000-4000-8000-000000000b03";
|
|
29
|
+
const leadId = "dddd0000-0000-4000-8000-000000000b04";
|
|
30
|
+
const userId = "eeee0000-0000-4000-8000-000000000b05";
|
|
31
|
+
|
|
32
|
+
type Caller =
|
|
33
|
+
| { as: "operator"; agentId?: string }
|
|
34
|
+
| { as: "session"; agentId: string }
|
|
35
|
+
| { as: "user" };
|
|
36
|
+
|
|
37
|
+
let server: Server;
|
|
38
|
+
let baseUrl: string;
|
|
39
|
+
|
|
40
|
+
function authFor(caller: Caller): HttpRequestAuth {
|
|
41
|
+
if (caller.as === "session") {
|
|
42
|
+
return { kind: "agent", agentId: caller.agentId, taskId: "session-task" };
|
|
43
|
+
}
|
|
44
|
+
if (caller.as === "user") {
|
|
45
|
+
return { kind: "user", userId, user: { id: userId } as never };
|
|
46
|
+
}
|
|
47
|
+
return { kind: "operator", fingerprint: "http-task-write-ownership" };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function post(path: string, caller: Caller, body: unknown) {
|
|
51
|
+
const headers: Record<string, string> = {
|
|
52
|
+
"Content-Type": "application/json",
|
|
53
|
+
"x-test-caller": JSON.stringify(caller),
|
|
54
|
+
};
|
|
55
|
+
if (caller.as === "operator" && caller.agentId) headers["X-Agent-ID"] = caller.agentId;
|
|
56
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers,
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
return response.status;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function inProgressTask() {
|
|
65
|
+
const row = await createTaskExtended("http write ownership", {
|
|
66
|
+
agentId: ownerId,
|
|
67
|
+
creatorAgentId: creatorId,
|
|
68
|
+
source: "system",
|
|
69
|
+
followUpConfig: { disabled: true },
|
|
70
|
+
});
|
|
71
|
+
await startTask(row.id);
|
|
72
|
+
return row;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
beforeAll(async () => {
|
|
76
|
+
initDb(":memory:");
|
|
77
|
+
await createAgent({ id: ownerId, name: "Owner", isLead: false, status: "idle" });
|
|
78
|
+
await createAgent({ id: otherId, name: "Other", isLead: false, status: "idle" });
|
|
79
|
+
await createAgent({ id: creatorId, name: "Creator", isLead: false, status: "idle" });
|
|
80
|
+
await createAgent({ id: leadId, name: "Lead", isLead: true, status: "idle" });
|
|
81
|
+
|
|
82
|
+
server = createHttpServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
83
|
+
const caller = JSON.parse(String(req.headers["x-test-caller"])) as Caller;
|
|
84
|
+
setRequestAuth(req, authFor(caller));
|
|
85
|
+
res.setHeader("Content-Type", "application/json");
|
|
86
|
+
const pathSegments = getPathSegments(req.url ?? "");
|
|
87
|
+
const query = parseQueryParams(req.url ?? "");
|
|
88
|
+
const myAgentId = req.headers["x-agent-id"] as string | undefined;
|
|
89
|
+
if (await handleTasks(req, res, pathSegments, query, myAgentId)) return;
|
|
90
|
+
res.writeHead(404);
|
|
91
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
92
|
+
});
|
|
93
|
+
const port = await listenOnFreePort(server);
|
|
94
|
+
baseUrl = `http://127.0.0.1:${port}`;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
afterAll(async () => {
|
|
98
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
99
|
+
closeDb();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("POST /api/tasks/{id}/progress ownership", () => {
|
|
103
|
+
test("another agent on the shared key gets 403 and writes nothing", async () => {
|
|
104
|
+
const task = await inProgressTask();
|
|
105
|
+
const status = await post(
|
|
106
|
+
`/api/tasks/${task.id}/progress`,
|
|
107
|
+
{ as: "operator", agentId: otherId },
|
|
108
|
+
{ progress: "not mine" },
|
|
109
|
+
);
|
|
110
|
+
expect(status).toBe(403);
|
|
111
|
+
expect((await getTaskById(task.id))?.progress).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("another agent on a session token gets 403", async () => {
|
|
115
|
+
const task = await inProgressTask();
|
|
116
|
+
const status = await post(
|
|
117
|
+
`/api/tasks/${task.id}/progress`,
|
|
118
|
+
{ as: "session", agentId: otherId },
|
|
119
|
+
{ progress: "not mine" },
|
|
120
|
+
);
|
|
121
|
+
expect(status).toBe(403);
|
|
122
|
+
expect((await getTaskById(task.id))?.progress).toBeUndefined();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("the assignee, a lead, the keyed runner wrapper, and a user may write", async () => {
|
|
126
|
+
const task = await inProgressTask();
|
|
127
|
+
const callers: Caller[] = [
|
|
128
|
+
{ as: "operator", agentId: ownerId },
|
|
129
|
+
{ as: "operator", agentId: leadId },
|
|
130
|
+
{ as: "operator" },
|
|
131
|
+
{ as: "user" },
|
|
132
|
+
];
|
|
133
|
+
for (const [i, caller] of callers.entries()) {
|
|
134
|
+
const status = await post(`/api/tasks/${task.id}/progress`, caller, {
|
|
135
|
+
progress: `step ${i}`,
|
|
136
|
+
});
|
|
137
|
+
expect(status, JSON.stringify(caller)).toBe(200);
|
|
138
|
+
expect((await getTaskById(task.id))?.progress).toBe(`step ${i}`);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("POST /api/tasks/{id}/progress on a terminal task", () => {
|
|
144
|
+
test("late harness progress after completion is a no-op", async () => {
|
|
145
|
+
const task = await inProgressTask();
|
|
146
|
+
const caller: Caller = { as: "operator", agentId: ownerId };
|
|
147
|
+
expect(await post(`/api/tasks/${task.id}/progress`, caller, { progress: "Reading" })).toBe(200);
|
|
148
|
+
await completeTask(task.id, "done");
|
|
149
|
+
expect(await post(`/api/tasks/${task.id}/progress`, caller, { progress: "step_end" })).toBe(
|
|
150
|
+
200,
|
|
151
|
+
);
|
|
152
|
+
const after = await getTaskById(task.id);
|
|
153
|
+
expect(after?.status).toBe("completed");
|
|
154
|
+
expect(after?.progress).toBe("Reading");
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe("POST /api/tasks/{id}/cancel ownership", () => {
|
|
159
|
+
test("an agent that is neither lead nor creator gets 403 and the task keeps running", async () => {
|
|
160
|
+
const task = await inProgressTask();
|
|
161
|
+
const status = await post(
|
|
162
|
+
`/api/tasks/${task.id}/cancel`,
|
|
163
|
+
{ as: "operator", agentId: otherId },
|
|
164
|
+
{ reason: "not mine" },
|
|
165
|
+
);
|
|
166
|
+
expect(status).toBe(403);
|
|
167
|
+
expect((await getTaskById(task.id))?.status).toBe("in_progress");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("the assignee alone cannot cancel, matching MCP cancel-task", async () => {
|
|
171
|
+
const task = await inProgressTask();
|
|
172
|
+
const status = await post(
|
|
173
|
+
`/api/tasks/${task.id}/cancel`,
|
|
174
|
+
{ as: "operator", agentId: ownerId },
|
|
175
|
+
{},
|
|
176
|
+
);
|
|
177
|
+
expect(status).toBe(403);
|
|
178
|
+
expect((await getTaskById(task.id))?.status).toBe("in_progress");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("the creator, a lead, the operator, and a user may cancel", async () => {
|
|
182
|
+
const callers: Caller[] = [
|
|
183
|
+
{ as: "operator", agentId: creatorId },
|
|
184
|
+
{ as: "session", agentId: leadId },
|
|
185
|
+
{ as: "operator" },
|
|
186
|
+
{ as: "user" },
|
|
187
|
+
];
|
|
188
|
+
for (const caller of callers) {
|
|
189
|
+
const task = await inProgressTask();
|
|
190
|
+
const status = await post(`/api/tasks/${task.id}/cancel`, caller, { reason: "stop" });
|
|
191
|
+
expect(status, JSON.stringify(caller)).toBe(200);
|
|
192
|
+
expect((await getTaskById(task.id))?.status).toBe("cancelled");
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { SummaryWithRatingsSchema } from "../../be/memory/raters/llm.js";
|
|
4
5
|
import {
|
|
5
6
|
completeStructured,
|
|
7
|
+
completeStructuredWithModel,
|
|
6
8
|
defaultSpawnClaudeCli,
|
|
7
9
|
} from "../../utils/internal-ai/complete-structured.js";
|
|
8
10
|
import type { ResolvedCredential } from "../../utils/internal-ai/credentials.js";
|
|
11
|
+
import { summaryToolSchema } from "../../utils/internal-ai/summarize-session.js";
|
|
9
12
|
|
|
10
13
|
const ResultZodSchema = z.object({
|
|
11
14
|
summary: z.string(),
|
|
@@ -69,6 +72,39 @@ describe("completeStructured", () => {
|
|
|
69
72
|
expect(result).toEqual({ summary: "ok", count: 7 });
|
|
70
73
|
});
|
|
71
74
|
|
|
75
|
+
test("session summary with a blank referencesSource is accepted on the first call", async () => {
|
|
76
|
+
let invocations = 0;
|
|
77
|
+
const args = {
|
|
78
|
+
summary: "learned things",
|
|
79
|
+
ratings: [
|
|
80
|
+
{ id: "m1", score: 0.9, reasoning: "useful", referencesSource: "linear:DES-1" },
|
|
81
|
+
{ id: "m2", score: 0.1, reasoning: "not used", referencesSource: "" },
|
|
82
|
+
{ id: "m3", score: 0.5, reasoning: "neutral", referencesSource: " " },
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
const result = await completeStructured({
|
|
86
|
+
zodSchema: SummaryWithRatingsSchema,
|
|
87
|
+
toolSchema: summaryToolSchema,
|
|
88
|
+
toolName: "record_session_summary",
|
|
89
|
+
toolDescription: "Record the session summary.",
|
|
90
|
+
systemPrompt: "sys",
|
|
91
|
+
userPrompt: "user",
|
|
92
|
+
_credentialOverride: {
|
|
93
|
+
kind: "openrouter",
|
|
94
|
+
apiKey: "test",
|
|
95
|
+
modelDefault: "openrouter/deepseek/deepseek-v4.1-flash",
|
|
96
|
+
},
|
|
97
|
+
_complete: async () => {
|
|
98
|
+
invocations++;
|
|
99
|
+
return makeMsg([
|
|
100
|
+
{ type: "toolCall", id: "call_1", name: "record_session_summary", arguments: args },
|
|
101
|
+
]);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
expect(invocations).toBe(1);
|
|
105
|
+
expect(result?.ratings).toHaveLength(3);
|
|
106
|
+
});
|
|
107
|
+
|
|
72
108
|
for (const { label, text, expected } of [
|
|
73
109
|
{
|
|
74
110
|
label: "bare JSON",
|
|
@@ -493,3 +529,61 @@ describe("defaultSpawnClaudeCli", () => {
|
|
|
493
529
|
}
|
|
494
530
|
});
|
|
495
531
|
});
|
|
532
|
+
|
|
533
|
+
describe("completeStructuredWithModel", () => {
|
|
534
|
+
const baseOpts = {
|
|
535
|
+
zodSchema: ResultZodSchema,
|
|
536
|
+
toolSchema: ResultToolSchema,
|
|
537
|
+
toolName: "record_result",
|
|
538
|
+
toolDescription: "Record the result.",
|
|
539
|
+
systemPrompt: "sys",
|
|
540
|
+
userPrompt: "user",
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
test("pi-ai path: reports the credential's resolved model next to the data", async () => {
|
|
544
|
+
const result = await completeStructuredWithModel({
|
|
545
|
+
...baseOpts,
|
|
546
|
+
_credentialOverride: {
|
|
547
|
+
kind: "openrouter",
|
|
548
|
+
apiKey: "test",
|
|
549
|
+
modelDefault: "openrouter/deepseek/deepseek-v4.1-flash",
|
|
550
|
+
},
|
|
551
|
+
_complete: async () =>
|
|
552
|
+
makeMsg([
|
|
553
|
+
{
|
|
554
|
+
type: "toolCall",
|
|
555
|
+
id: "call_1",
|
|
556
|
+
name: "record_result",
|
|
557
|
+
arguments: { summary: "ok", count: 7 },
|
|
558
|
+
},
|
|
559
|
+
]),
|
|
560
|
+
});
|
|
561
|
+
expect(result).toEqual({
|
|
562
|
+
data: { summary: "ok", count: 7 },
|
|
563
|
+
model: "openrouter/deepseek/deepseek-v4.1-flash",
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test("assistant-text fallback also reports the model", async () => {
|
|
568
|
+
const result = await completeStructuredWithModel({
|
|
569
|
+
...baseOpts,
|
|
570
|
+
_credentialOverride: {
|
|
571
|
+
kind: "openrouter",
|
|
572
|
+
apiKey: "test",
|
|
573
|
+
modelDefault: "openrouter/deepseek/deepseek-v4.1-flash",
|
|
574
|
+
},
|
|
575
|
+
_complete: async () => makeMsg([{ type: "text", text: '{"summary":"t","count":2}' }]),
|
|
576
|
+
});
|
|
577
|
+
expect(result?.model).toBe("openrouter/deepseek/deepseek-v4.1-flash");
|
|
578
|
+
expect(result?.data).toEqual({ summary: "t", count: 2 });
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test("claude-cli path: reports the cli model alias", async () => {
|
|
582
|
+
const result = await completeStructuredWithModel({
|
|
583
|
+
...baseOpts,
|
|
584
|
+
_credentialOverride: { kind: "claude-cli", modelDefault: "haiku" } as ResolvedCredential,
|
|
585
|
+
_spawnClaudeCli: async () => JSON.stringify({ summary: "cli", count: 1 }),
|
|
586
|
+
});
|
|
587
|
+
expect(result).toEqual({ data: { summary: "cli", count: 1 }, model: "haiku" });
|
|
588
|
+
});
|
|
589
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
|
+
DEFAULT_MODEL,
|
|
3
4
|
type ResolveCredentialOptions,
|
|
4
5
|
resolveCredential,
|
|
5
6
|
} from "../../utils/internal-ai/credentials.js";
|
|
@@ -34,6 +35,27 @@ describe("resolveCredential", () => {
|
|
|
34
35
|
}
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
test("modelDefault follows the pinned rater model, not DEFAULT_MODEL", async () => {
|
|
39
|
+
const saved = process.env.MEMORY_RATER_MODEL;
|
|
40
|
+
delete process.env.MEMORY_RATER_MODEL;
|
|
41
|
+
const before = { ...DEFAULT_MODEL };
|
|
42
|
+
try {
|
|
43
|
+
DEFAULT_MODEL.openrouter = "openrouter/some/bumped-model";
|
|
44
|
+
DEFAULT_MODEL.anthropic = "anthropic/bumped";
|
|
45
|
+
const viaOpenRouter = await resolveCredential(
|
|
46
|
+
makeOpts({ env: { OPENROUTER_API_KEY: "or-1" } }),
|
|
47
|
+
);
|
|
48
|
+
expect(viaOpenRouter?.modelDefault).toBe("openrouter/deepseek/deepseek-v4.1-flash");
|
|
49
|
+
const viaAnthropic = await resolveCredential(
|
|
50
|
+
makeOpts({ env: { ANTHROPIC_API_KEY: "example-sk-ant-1" } }),
|
|
51
|
+
);
|
|
52
|
+
expect(viaAnthropic?.modelDefault).toBe("anthropic/claude-haiku-4-5");
|
|
53
|
+
} finally {
|
|
54
|
+
Object.assign(DEFAULT_MODEL, before);
|
|
55
|
+
if (saved !== undefined) process.env.MEMORY_RATER_MODEL = saved;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
37
59
|
test("ANTHROPIC_API_KEY when no openrouter", async () => {
|
|
38
60
|
const cred = await resolveCredential(
|
|
39
61
|
makeOpts({ env: { ANTHROPIC_API_KEY: "example-sk-ant-1" } }),
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_MODEL,
|
|
5
|
+
MEMORY_RATER_DEFAULT_MODEL,
|
|
6
|
+
parseModelStr,
|
|
7
|
+
resolveRaterModelString,
|
|
8
|
+
} from "../../utils/internal-ai/models.js";
|
|
4
9
|
|
|
5
10
|
describe("internal-ai default models", () => {
|
|
6
11
|
for (const [kind, modelString] of Object.entries(DEFAULT_MODEL)) {
|
|
@@ -18,3 +23,32 @@ describe("internal-ai default models", () => {
|
|
|
18
23
|
});
|
|
19
24
|
}
|
|
20
25
|
});
|
|
26
|
+
|
|
27
|
+
describe("memory rater default models", () => {
|
|
28
|
+
const savedEnv = process.env.MEMORY_RATER_MODEL;
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
if (savedEnv === undefined) delete process.env.MEMORY_RATER_MODEL;
|
|
31
|
+
else process.env.MEMORY_RATER_MODEL = savedEnv;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
for (const [kind, modelString] of Object.entries(MEMORY_RATER_DEFAULT_MODEL)) {
|
|
35
|
+
if (kind === "claude-cli") continue;
|
|
36
|
+
|
|
37
|
+
test(`${kind} rater default resolves in the pinned pi-ai catalog`, () => {
|
|
38
|
+
const [provider, modelId] = parseModelStr(modelString);
|
|
39
|
+
const model = getBuiltinModel(
|
|
40
|
+
provider as Parameters<typeof getBuiltinModel>[0],
|
|
41
|
+
modelId as never,
|
|
42
|
+
);
|
|
43
|
+
expect(model).toBeDefined();
|
|
44
|
+
expect(model.id).toBe(modelId);
|
|
45
|
+
expect(model.provider).toBe(provider);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test("MEMORY_RATER_MODEL still overrides every kind", () => {
|
|
50
|
+
process.env.MEMORY_RATER_MODEL = "openrouter/anthropic/claude-sonnet-4-5";
|
|
51
|
+
expect(resolveRaterModelString("openrouter")).toBe("openrouter/anthropic/claude-sonnet-4-5");
|
|
52
|
+
expect(resolveRaterModelString("claude-cli")).toBe("openrouter/anthropic/claude-sonnet-4-5");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -4,7 +4,7 @@ import { SummaryWithRatingsSchema } from "../../be/memory/raters/llm.js";
|
|
|
4
4
|
import { summaryToolSchema } from "../../utils/internal-ai/summarize-session.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Valid + invalid fixtures. Both validators (zod via `safeParse`,
|
|
8
8
|
* typebox via `Value.Check`) must agree on every fixture.
|
|
9
9
|
*
|
|
10
10
|
* Note: zod's `SummaryWithRatingsSchema` defaults `ratings` to `[]` when
|
|
@@ -54,6 +54,24 @@ const VALID_CASES: unknown[] = [
|
|
|
54
54
|
summary: "max-reason",
|
|
55
55
|
ratings: [{ id: "m1", score: 0.5, reasoning: "x".repeat(500) }],
|
|
56
56
|
},
|
|
57
|
+
// blank referencesSource: models emit "" for "no source". One blank value
|
|
58
|
+
// must not reject the whole summary; buildRatingsFromLlm drops the edge.
|
|
59
|
+
{
|
|
60
|
+
summary: "empty-ref",
|
|
61
|
+
ratings: [{ id: "m1", score: 0.5, reasoning: "ok", referencesSource: "" }],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
summary: "whitespace-ref",
|
|
65
|
+
ratings: [{ id: "m1", score: 0.5, reasoning: "ok", referencesSource: " " }],
|
|
66
|
+
},
|
|
67
|
+
// one blank among valid ratings.
|
|
68
|
+
{
|
|
69
|
+
summary: "mixed-refs",
|
|
70
|
+
ratings: [
|
|
71
|
+
{ id: "m1", score: 0.9, reasoning: "ok", referencesSource: "linear:DES-1" },
|
|
72
|
+
{ id: "m2", score: 0.1, reasoning: "ok", referencesSource: "" },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
57
75
|
];
|
|
58
76
|
|
|
59
77
|
const INVALID_CASES: unknown[] = [
|
|
@@ -74,6 +92,12 @@ const INVALID_CASES: unknown[] = [
|
|
|
74
92
|
{ summary: "ok", ratings: [{ id: "m1", score: -0.1, reasoning: "x" }] },
|
|
75
93
|
// rating with non-string id.
|
|
76
94
|
{ summary: "ok", ratings: [{ id: 7, score: 0.5, reasoning: "x" }] },
|
|
95
|
+
// referencesSource still bounded and typed: only blank became valid.
|
|
96
|
+
{ summary: "ok", ratings: [{ id: "m1", score: 0.5, reasoning: "x", referencesSource: 7 }] },
|
|
97
|
+
{
|
|
98
|
+
summary: "ok",
|
|
99
|
+
ratings: [{ id: "m1", score: 0.5, reasoning: "x", referencesSource: "x".repeat(513) }],
|
|
100
|
+
},
|
|
77
101
|
];
|
|
78
102
|
|
|
79
103
|
describe("schema-parity: SummaryWithRatingsSchema (zod) vs summaryToolSchema (typebox)", () => {
|
|
@@ -11,7 +11,7 @@ Tool[Edit]: input={"file":"/tmp/x","old":"a","new":"b"} output="ok"
|
|
|
11
11
|
Assistant: Done.`.padEnd(200, "x");
|
|
12
12
|
|
|
13
13
|
describe("summarizeSession", () => {
|
|
14
|
-
test("pass-through: injected _completeStructured
|
|
14
|
+
test("pass-through: injected _completeStructured data is returned with the resolved model", async () => {
|
|
15
15
|
const fake: z.infer<typeof SummaryWithRatingsSchema> = {
|
|
16
16
|
summary: "Learned: X uses Y",
|
|
17
17
|
ratings: [{ id: "mem-1", score: 0.9, reasoning: "very useful" }],
|
|
@@ -23,9 +23,25 @@ describe("summarizeSession", () => {
|
|
|
23
23
|
taskContext: { sourceTaskId: "task-1", agentId: "agent-1" },
|
|
24
24
|
apiUrl: "http://localhost:3013",
|
|
25
25
|
apiKey: "k",
|
|
26
|
-
_completeStructured: (async () =>
|
|
26
|
+
_completeStructured: (async () => ({
|
|
27
|
+
data: fake,
|
|
28
|
+
model: "openrouter/deepseek/deepseek-v4.1-flash",
|
|
29
|
+
})) as any,
|
|
27
30
|
});
|
|
28
|
-
expect(result).toEqual(fake);
|
|
31
|
+
expect(result).toEqual({ ...fake, model: "openrouter/deepseek/deepseek-v4.1-flash" });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("returns null when _completeStructured resolves no result", async () => {
|
|
35
|
+
const result = await summarizeSession({
|
|
36
|
+
harness: "pi",
|
|
37
|
+
transcript: LONG_TRANSCRIPT,
|
|
38
|
+
retrievals: [],
|
|
39
|
+
taskContext: { sourceTaskId: "task-1", agentId: "agent-1" },
|
|
40
|
+
apiUrl: "http://localhost:3013",
|
|
41
|
+
apiKey: "k",
|
|
42
|
+
_completeStructured: (async () => null) as any,
|
|
43
|
+
});
|
|
44
|
+
expect(result).toBeNull();
|
|
29
45
|
});
|
|
30
46
|
|
|
31
47
|
test("retrievals are injected into the userPrompt via buildSummaryWithRatingsPrompt", async () => {
|
|
@@ -39,7 +55,7 @@ describe("summarizeSession", () => {
|
|
|
39
55
|
apiKey: "k",
|
|
40
56
|
_completeStructured: (async (opts: { userPrompt: string }) => {
|
|
41
57
|
capturedUserPrompt = opts.userPrompt;
|
|
42
|
-
return { summary: "x", ratings: [] };
|
|
58
|
+
return { data: { summary: "x", ratings: [] }, model: "m" };
|
|
43
59
|
}) as any,
|
|
44
60
|
});
|
|
45
61
|
expect(capturedUserPrompt).toContain("mem-abc");
|
|
@@ -62,7 +78,7 @@ describe("summarizeSession", () => {
|
|
|
62
78
|
apiKey: "k",
|
|
63
79
|
_completeStructured: (async (opts: { userPrompt: string }) => {
|
|
64
80
|
capturedUserPrompt = opts.userPrompt;
|
|
65
|
-
return { summary: "x", ratings: [] };
|
|
81
|
+
return { data: { summary: "x", ratings: [] }, model: "m" };
|
|
66
82
|
}) as any,
|
|
67
83
|
});
|
|
68
84
|
expect(capturedUserPrompt).toContain("Task: do the thing");
|
|
@@ -79,7 +95,7 @@ describe("summarizeSession", () => {
|
|
|
79
95
|
apiKey: "k",
|
|
80
96
|
_completeStructured: (async () => {
|
|
81
97
|
invocations++;
|
|
82
|
-
return { summary: "x", ratings: [] };
|
|
98
|
+
return { data: { summary: "x", ratings: [] }, model: "m" };
|
|
83
99
|
}) as any,
|
|
84
100
|
});
|
|
85
101
|
expect(result).toBeNull();
|
|
@@ -97,7 +113,7 @@ describe("summarizeSession", () => {
|
|
|
97
113
|
apiKey: "k",
|
|
98
114
|
_completeStructured: (async (opts: { callerTag?: string }) => {
|
|
99
115
|
capturedTag = opts.callerTag ?? "";
|
|
100
|
-
return { summary: "x", ratings: [] };
|
|
116
|
+
return { data: { summary: "x", ratings: [] }, model: "m" };
|
|
101
117
|
}) as any,
|
|
102
118
|
});
|
|
103
119
|
expect(capturedTag).toBe("session-summary:pi");
|