@desplega.ai/agent-swarm 1.152.2 → 1.154.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-pfeje2v5.js → acp-adapter-eb2aj8xy.js} +4 -4
- package/dist/{actions-0sttc7f5.js → actions-kpnwp1kz.js} +31 -27
- package/dist/{anthropic-messages-2y8nktar.js → anthropic-messages-9vqsbznt.js} +112 -84
- package/dist/{app-p8teva95.js → app-zk1apkf6.js} +10 -7
- package/dist/{assistant-r45jnsrt.js → assistant-kwq4y50q.js} +32 -28
- package/dist/{azure-openai-responses-f38x2avq.js → azure-openai-responses-04qwdwfq.js} +27 -13
- package/dist/{boot-reembed-je26h5y7.js → boot-reembed-22hd3jjn.js} +9 -7
- package/dist/{boot-reembed-1qmybsd4.js → boot-reembed-qe168424.js} +10 -8
- package/dist/{boot-scrub-logs-gte3h1mm.js → boot-scrub-logs-pkdpsrjd.js} +7 -5
- package/dist/{claude-adapter-csc1mzka.js → claude-adapter-3fx90nww.js} +18 -16
- package/dist/{claude-managed-adapter-dsmkcrjy.js → claude-managed-adapter-ea2aga6w.js} +3 -3
- package/dist/{claude-sdk-session-fcd6f078.js → claude-sdk-session-z7fahf1k.js} +18 -16
- package/dist/{cli-r2fpe7cx.js → cli-00w2c66p.js} +61 -55
- package/dist/{cli-wbyk465z.js → cli-09y3tjxz.js} +1 -1
- package/dist/{cli-ht5rs63n.js → cli-0e31nm4n.js} +16 -7
- package/dist/cli-0x5qzvkg.js +4279 -0
- package/dist/{cli-d3p82erb.js → cli-18amys3y.js} +9 -9
- package/dist/{cli-4ahpr4sw.js → cli-1jpjafw4.js} +5 -5
- package/dist/{cli-jbmc0r7r.js → cli-243tzws8.js} +1 -1
- package/dist/{cli-ch38q5z4.js → cli-24k806sc.js} +6789 -2542
- package/dist/cli-3bkz2p7j.js +2828 -0
- package/dist/cli-3er6gcsn.js +1849 -0
- package/dist/cli-4k7jazkq.js +3339 -0
- package/dist/{cli-c4eeb5q2.js → cli-5b6b62tz.js} +1 -1
- package/dist/{cli-qd65dvr6.js → cli-5vs0nh43.js} +65 -44
- package/dist/{cli-z3wkng35.js → cli-6r3cgpk2.js} +224 -156
- package/dist/{cli-pgj4a5fb.js → cli-71cj99hx.js} +4 -3
- package/dist/cli-8sea27q4.js +14075 -0
- package/dist/{cli-jvy9qgjs.js → cli-913yakhv.js} +22 -7
- package/dist/{cli-pf9q3696.js → cli-927erqqt.js} +5 -2
- package/dist/cli-95jnpnqg.js +230 -0
- package/dist/{cli-wpv5rgg2.js → cli-abp3g4ax.js} +15 -2
- package/dist/{cli-ckz5t2hb.js → cli-acy6bfhr.js} +10 -7
- package/dist/{cli-4yc5d8fk.js → cli-avkd6mqv.js} +50 -0
- package/dist/{cli-8wrympd7.js → cli-bzr0ftht.js} +26 -7
- package/dist/{cli-qn042eh7.js → cli-c1v10xb2.js} +194 -1913
- package/dist/cli-cgj4hc12.js +18902 -0
- package/dist/{cli-2nnd56zt.js → cli-e0pg4zeh.js} +3 -2
- package/dist/{cli-r84ctdxn.js → cli-e31ehqwy.js} +2 -2
- package/dist/{cli-gphgnvpf.js → cli-e3229na9.js} +3 -3
- package/dist/{cli-gpqcs935.js → cli-e3cyf59d.js} +2 -2
- package/dist/{cli-s25avqg9.js → cli-e781c6f9.js} +1 -0
- package/dist/{cli-s2qjdd6z.js → cli-eetrvv91.js} +1 -1
- package/dist/{cli-j4nv2wqy.js → cli-fdzg6jhf.js} +10 -4
- package/dist/{cli-fm4ytsdk.js → cli-g2h29qar.js} +2 -2
- package/dist/{cli-zvgj5zg8.js → cli-ge67p080.js} +302 -3497
- package/dist/cli-h38x0qhg.js +6912 -0
- package/dist/{cli-9xvdm3bx.js → cli-h60qfasa.js} +742 -278
- package/dist/{cli-jew3jda5.js → cli-hws0vn86.js} +2 -2
- package/dist/{cli-j8dggbcr.js → cli-k0g1sjzm.js} +23 -31
- package/dist/{cli-xw7vak96.js → cli-kp3qs53m.js} +5 -5
- package/dist/{cli-rgvp6r2x.js → cli-mcnhgnn1.js} +193 -4
- package/dist/{cli-zvkq5csn.js → cli-myaxtgh8.js} +7 -6
- package/dist/{cli-56h18ng2.js → cli-n62atwh9.js} +1 -1
- package/dist/cli-n7h1kqpf.js +29 -0
- package/dist/{cli-fmy65jdn.js → cli-pf1v8xhb.js} +7 -5
- package/dist/{cli-xy1cchaf.js → cli-q85qhckh.js} +238 -11
- package/dist/{cli-3bpzj8me.js → cli-r1d3dzt6.js} +2 -2
- package/dist/{pi-mono-adapter-jcqt71cg.js → cli-rxkqvngf.js} +67343 -101083
- package/dist/{cli-kj76p1ez.js → cli-scwq51gn.js} +13 -7
- package/dist/{cli-b9dd0h3k.js → cli-stwsmd6k.js} +3 -2
- package/dist/{cli-c2mk1kgf.js → cli-v4en75c5.js} +8 -1
- package/dist/{cli-z61ykvam.js → cli-wg04s9kr.js} +4 -3
- package/dist/cli-yn0a417a.js +4069 -0
- package/dist/{cli-zfzznn7c.js → cli-ytxs7mn1.js} +26 -2
- package/dist/{cli-4j8k1m4w.js → cli-zt1227k1.js} +1162 -192
- package/dist/cli.js +28 -35
- package/dist/{codex-adapter-fxmpsabw.js → codex-adapter-ttdm995v.js} +12 -11
- package/dist/{codex-hook-8jexc34x.js → codex-hook-8gx7bcz0.js} +3 -3
- package/dist/{codex-session-runner-rr1e47ea.js → codex-session-runner-3v8nh4c8.js} +12 -11
- package/dist/{commands-0f5rxz51.js → commands-1cxwbhhq.js} +7 -5
- package/dist/{db-r3jxf3wy.js → db-y9zwsr79.js} +25 -5
- package/dist/{dispatcher-06vph6q4.js → dispatcher-9m9zss36.js} +30 -26
- package/dist/dsh-adapter-gsb2qqf9.js +19 -0
- package/dist/{e2b-413b8t4v.js → e2b-93x1k0j5.js} +6 -7
- package/dist/{google-generative-ai-ndjmj5fa.js → google-generative-ai-gwwrnzqn.js} +33 -73
- package/dist/{google-vertex-fr665tqs.js → google-vertex-5wfawpwz.js} +38 -68
- package/dist/{handlers-0hgtc910.js → handlers-36fm1bwn.js} +33 -29
- package/dist/{hook-9kvrv1er.js → hook-pntw4r8h.js} +12 -10
- package/dist/{hook-fbfhkat3.js → hook-vwq8y26p.js} +13 -11
- package/dist/{http-xbyx8k5j.js → http-20jesakk.js} +357 -261
- package/dist/{index-jsw3aynb.js → index-6artrehg.js} +30 -26
- package/dist/{cli-pdz9qde4.js → index-9ch1n761.js} +10 -11
- package/dist/{index-rnyahfn5.js → index-9n5z2492.js} +30 -26
- package/dist/{index-66xz5e9j.js → index-h5gs261z.js} +521 -9
- package/dist/{index-tfsdxyv6.js → index-j5wrf1yw.js} +40 -25
- package/dist/{index-2sp0384z.js → index-kkeftv5h.js} +738 -21
- package/dist/{index-sgk50mbg.js → index-m2xqpz5r.js} +31 -27
- package/dist/{index-gpfn1741.js → index-m9ks8a7t.js} +9 -7
- package/dist/index-w097eg0x.js +5 -0
- package/dist/jiti-loader-b4837mtm.js +34 -0
- package/dist/jiti-static-loader-q2g5ds0y.js +31267 -0
- package/dist/{keepalive-s3xpr7sw.js → keepalive-pwxvxd2c.js} +9 -7
- package/dist/{lead-rq4n5r16.js → lead-m789wqa3.js} +46 -41
- package/dist/{maintenance-f0atj1ab.js → maintenance-bvg6w7e2.js} +10 -8
- package/dist/{mistral-conversations-qhr84ph3.js → mistral-conversations-zx0kn554.js} +24 -18
- package/dist/{node-x1g4v0pq.js → node-5qtycf6a.js} +16 -11
- package/dist/{oauth-refresh-sweep-q0t3bxre.js → oauth-refresh-sweep-6v4bd737.js} +8 -6
- package/dist/{onboard-br5j1kch.js → onboard-snckpcj1.js} +115 -35
- package/dist/{openai-codex-responses-wy6a43wd.js → openai-codex-responses-4vjqzvjp.js} +33 -27
- package/dist/{openai-completions-32rdwmze.js → openai-completions-pxsmym7g.js} +52 -64
- package/dist/{openai-responses-6xwf7bzg.js → openai-responses-tmn0rqp0.js} +23 -24
- package/dist/{opencode-adapter-28axqa09.js → opencode-adapter-qbmv4tpy.js} +6 -6
- package/dist/{otel-impl-vdqge2nt.js → otel-impl-e0ytens5.js} +4 -4
- package/dist/{pi-messages-b1mbr6gp.js → pi-messages-txpjj39q.js} +3 -3
- package/dist/pi-mono-adapter-c4ggemhz.js +1316 -0
- package/dist/{pricing-refresh-hb3691fd.js → pricing-refresh-52g2ywrw.js} +9 -7
- package/dist/{rbac-roles-t78xt39b.js → rbac-roles-5455kfdn.js} +8 -6
- package/dist/{rbac-roles-jh280k9g.js → rbac-roles-hbf0xa52.js} +9 -7
- package/dist/{render-v2-7p8r2ymg.js → render-v2-npm8xdzh.js} +16 -9
- package/dist/{seed-pricing-hf889enh.js → seed-pricing-650wbr1n.js} +8 -6
- package/dist/{setup-w6mb03jv.js → setup-k1mew5rf.js} +4 -5
- package/dist/virtual-modules-2q5b2pk4.js +54 -0
- package/dist/{work-objects-nt8gy1kj.js → work-objects-2ea3kvwg.js} +33 -29
- package/dist/{worker-dwy617t0.js → worker-ec6hvc4m.js} +46 -41
- package/openapi.json +109 -16
- package/package.json +7 -6
- package/plugin/opencode-plugins/lib/memory-raters.ts +7 -0
- package/src/be/automation-preflight.ts +6 -4
- package/src/be/db/agents.ts +75 -2
- package/src/be/db/tasks/read.ts +2 -0
- package/src/be/db/tasks/write.ts +7 -1
- package/src/be/db.ts +140 -18
- package/src/be/extensions/db.ts +35 -4
- package/src/be/memory/raters/llm.ts +2 -6
- package/src/be/memory/raters/noop.ts +2 -3
- package/src/be/memory/raters/registry.ts +4 -10
- package/src/be/migrations/157_defer_provenance_and_marker.sql +27 -0
- package/src/be/models-catalog.ts +2 -0
- package/src/be/modelsdev-cache.json +213 -0
- package/src/be/rbac-audit.ts +2 -0
- package/src/be/seed/agent-fs-provision.ts +23 -0
- package/src/be/seed-pricing.ts +3 -1
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/commands/onboard/compose-generator.ts +16 -1
- package/src/commands/onboard/env-generator.ts +14 -1
- package/src/commands/onboard/manifest.ts +1 -0
- package/src/commands/onboard/steps/integration-slack.tsx +56 -16
- package/src/commands/onboard/steps/review.tsx +5 -0
- package/src/commands/onboard/types.ts +6 -0
- package/src/commands/onboard.tsx +5 -1
- package/src/commands/provider-credentials.ts +14 -2
- package/src/commands/runner.ts +18 -2
- package/src/extensions/identity.ts +15 -7
- package/src/extensions/lifecycle.ts +15 -21
- package/src/hooks/codex-hook.ts +7 -1
- package/src/http/agents.ts +12 -4
- package/src/http/config.ts +1 -1
- package/src/http/core.ts +5 -3
- package/src/http/extensions.ts +108 -34
- package/src/http/poll.ts +7 -0
- package/src/http/schedules.ts +10 -0
- package/src/http/script-runs.ts +35 -26
- package/src/http/status.ts +24 -11
- package/src/http/tasks.ts +6 -1
- package/src/prompts/base-prompt.ts +15 -7
- package/src/prompts/memories.ts +5 -7
- package/src/prompts/session-templates.ts +3 -2
- package/src/prompts/steering-delivery.ts +2 -0
- package/src/providers/claude-managed-models.ts +8 -0
- package/src/providers/codex-adapter.ts +3 -6
- package/src/providers/codex-models.ts +15 -1
- package/src/providers/dsh-adapter.ts +248 -0
- package/src/providers/index.ts +5 -1
- package/src/providers/modelsdev-reasoning.json +50 -0
- package/src/providers/pi-mono-extension.ts +2 -5
- package/src/rbac/legacy-policy.ts +15 -1
- package/src/rbac/types.ts +2 -0
- package/src/server.ts +7 -1
- package/src/slack/ack.ts +43 -8
- package/src/slack/app.ts +32 -10
- package/src/slack/config.ts +29 -0
- package/src/slack/handlers.ts +7 -1
- package/src/slack/render-v2.ts +186 -17
- package/src/slack/responses.ts +1 -3
- package/src/slack/task-output.ts +66 -8
- package/src/slack/thread-buffer.ts +27 -3
- package/src/telemetry.ts +2 -3
- package/src/tests/agent-fs-provision-seeder.test.ts +56 -2
- package/src/tests/agents-harness-provider.test.ts +19 -0
- package/src/tests/agents-list-model-display.test.ts +60 -1
- package/src/tests/base-prompt.test.ts +25 -0
- package/src/tests/claude-managed-adapter.test.ts +6 -0
- package/src/tests/codex-gpt6-release.test.ts +97 -0
- package/src/tests/codex-steering-hook.test.ts +2 -2
- package/src/tests/config-api-only-keys.test.ts +9 -0
- package/src/tests/context-window.test.ts +1 -0
- package/src/tests/db-task-read-contract.test.ts +9 -0
- package/src/tests/db-task-write-contract.test.ts +9 -0
- package/src/tests/defer-task.test.ts +115 -16
- package/src/tests/dsh-adapter.test.ts +276 -0
- package/src/tests/e2e-nightly-report.test.ts +3 -3
- package/src/tests/e2e-redact.test.ts +2 -0
- package/src/tests/env-flag.test.ts +6 -0
- package/src/tests/extension-agent-visibility.test.ts +474 -0
- package/src/tests/extensions-db.test.ts +41 -0
- package/src/tests/extensions-http.test.ts +148 -4
- package/src/tests/extensions-lifecycle.test.ts +21 -1
- package/src/tests/extensions-mcp-tools.test.ts +9 -5
- package/src/tests/fetch-resolved-env.test.ts +43 -0
- package/src/tests/internal-ai/complete-structured.test.ts +32 -9
- package/src/tests/internal-ai/credentials.test.ts +4 -4
- package/src/tests/internal-ai/models.test.ts +20 -0
- package/src/tests/memory-rate-tool.test.ts +7 -10
- package/src/tests/memory-rater-defaults.test.ts +23 -0
- package/src/tests/memory-rater-e2e.test.ts +2 -3
- package/src/tests/migration-046-budgets.test.ts +2 -0
- package/src/tests/model-control.test.ts +2 -2
- package/src/tests/onboard-compose.test.ts +14 -0
- package/src/tests/onboard-env.test.ts +17 -0
- package/src/tests/onboard-manifest.test.ts +12 -0
- package/src/tests/opus-5-5-registry.test.ts +48 -0
- package/src/tests/pricing-refresh.test.ts +1 -1
- package/src/tests/pricing-routes.test.ts +15 -3
- package/src/tests/provider-registration.test.ts +5 -2
- package/src/tests/rbac-admission-e2e.test.ts +54 -0
- package/src/tests/rbac-e2e-helpers.ts +2 -0
- package/src/tests/rbac-engine.test.ts +32 -0
- package/src/tests/rbac-wire-e2e.test.ts +33 -0
- package/src/tests/reasoning-effort.test.ts +8 -0
- package/src/tests/reload-config.test.ts +33 -0
- package/src/tests/script-connections.test.ts +3 -0
- package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
- package/src/tests/slack-config.test.ts +136 -0
- package/src/tests/slack-mode-lifecycle.test.ts +69 -0
- package/src/tests/slack-render-v2.test.ts +446 -16
- package/src/tests/slack-steering.test.ts +69 -1
- package/src/tests/slack-task-output.test.ts +33 -12
- package/src/tests/status.test.ts +22 -2
- package/src/tests/steering-core.test.ts +35 -0
- package/src/tests/steering-delivery.test.ts +39 -0
- package/src/tests/steering-transport.test.ts +2 -2
- package/src/tests/task-tool-preload-mcp.test.ts +3 -1
- package/src/tests/telemetry-init.test.ts +18 -0
- package/src/tests/workflow-live-recovery.test.ts +126 -0
- package/src/tests/workflow-llm.test.ts +3 -3
- package/src/tools/defer-task.ts +64 -26
- package/src/tools/extension-common.ts +4 -0
- package/src/tools/extension-install.ts +1 -1
- package/src/tools/extension-list.ts +4 -3
- package/src/tools/join-swarm.ts +1 -1
- package/src/tools/schedules/create-schedule.ts +11 -1
- package/src/tools/schedules/patch-schedule.ts +5 -0
- package/src/tools/schedules/update-schedule.ts +5 -0
- package/src/tools/send-task.ts +6 -0
- package/src/tools/utils.ts +1 -1
- package/src/types.ts +30 -8
- package/src/utils/context-window.ts +1 -0
- package/src/utils/credentials.ts +8 -0
- package/src/utils/internal-ai/complete-structured.ts +8 -0
- package/src/utils/internal-ai/models.ts +5 -5
- package/src/utils/memory-raters.ts +3 -0
- package/src/workflows/engine.ts +28 -0
- package/src/workflows/recovery.ts +7 -4
- package/templates/schedules/daily-status-report/content.md +25 -12
- package/templates/schedules/daily-workflow-health-audit/content.md +1 -1
- package/templates/skills/swarm-extensions/content.md +9 -5
- package/dist/cli-4wppfkcm.js +0 -16384
- package/dist/cli-55qrx3q8.js +0 -6041
- package/dist/cli-cg4vnyh4.js +0 -305
- package/dist/cli-jyetr64j.js +0 -52
- package/dist/cli-pp9ax9pa.js +0 -520
- package/dist/cli-qa66qaf0.js +0 -6076
- package/dist/cli-qghpvca2.js +0 -590
- package/dist/cli-rembwgt8.js +0 -861
- package/dist/cli-tcvj7j2r.js +0 -1971
- package/dist/cli-vfcth11k.js +0 -36
- package/dist/cli-w1d1bgkk.js +0 -1422
- package/dist/cli-y6x0gqtg.js +0 -5977
- package/dist/es5-tj3tn4jj.js +0 -844
- package/dist/index-1kq5hw2p.js +0 -467
- package/dist/index-1p8wdstt.js +0 -7
- package/dist/index-8fdjzb2f.js +0 -100
- package/dist/index-bxmwzbgw.js +0 -365
- package/dist/index-nzerxd5z.js +0 -7
- package/dist/index-pyymnzat.js +0 -1168
- package/dist/index-qv6atbab.js +0 -619
- package/dist/index-rja32qa7.js +0 -801
- package/dist/index-tzg5srfp.js +0 -211
- package/dist/index-vhncw983.js +0 -90
- package/dist/index-xk036zsk.js +0 -859
- package/dist/index-zyazbzcb.js +0 -6
- package/dist/{claude-managed-setup-48dwg9bv.js → claude-managed-setup-f5pmsyrp.js} +3 -3
|
@@ -0,0 +1,1316 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DefaultResourceLoader,
|
|
3
|
+
ModelRuntime,
|
|
4
|
+
SessionManager,
|
|
5
|
+
createAgentSession,
|
|
6
|
+
getAgentDir
|
|
7
|
+
} from "./cli-rxkqvngf.js";
|
|
8
|
+
import"./cli-5bfdm1ah.js";
|
|
9
|
+
import {
|
|
10
|
+
readPkgVersion
|
|
11
|
+
} from "./cli-2djtr51b.js";
|
|
12
|
+
import {
|
|
13
|
+
summarizeSession
|
|
14
|
+
} from "./cli-fdzg6jhf.js";
|
|
15
|
+
import {
|
|
16
|
+
checkToolLoop,
|
|
17
|
+
clearToolHistory
|
|
18
|
+
} from "./cli-hp1n6zq3.js";
|
|
19
|
+
import {
|
|
20
|
+
applyReasoningEffort
|
|
21
|
+
} from "./cli-avkd6mqv.js";
|
|
22
|
+
import"./cli-vj6h9kc2.js";
|
|
23
|
+
import"./cli-7nxes1hs.js";
|
|
24
|
+
import {
|
|
25
|
+
buildRatingsFromLlm,
|
|
26
|
+
fetchRetrievalsForTask,
|
|
27
|
+
getMemoryRaterNames,
|
|
28
|
+
postRatings
|
|
29
|
+
} from "./cli-3bkz2p7j.js";
|
|
30
|
+
import"./cli-e7d20cty.js";
|
|
31
|
+
import {
|
|
32
|
+
swarmRuntimeInstanceId
|
|
33
|
+
} from "./cli-re8z5p8p.js";
|
|
34
|
+
import {
|
|
35
|
+
DEFAULT_OPENROUTER_BASE_URL,
|
|
36
|
+
getOpenRouterBaseUrl
|
|
37
|
+
} from "./cli-3dpj7atn.js";
|
|
38
|
+
import {
|
|
39
|
+
exports_typebox,
|
|
40
|
+
getBuiltinModel,
|
|
41
|
+
getBuiltinModels
|
|
42
|
+
} from "./cli-8sea27q4.js";
|
|
43
|
+
import {
|
|
44
|
+
McpHttpClient
|
|
45
|
+
} from "./cli-epan13p1.js";
|
|
46
|
+
import"./cli-bsakbbkm.js";
|
|
47
|
+
import"./cli-3er6gcsn.js";
|
|
48
|
+
import"./cli-ej6gbxfb.js";
|
|
49
|
+
import"./cli-sd0mxa3c.js";
|
|
50
|
+
import"./cli-w5e1d1vv.js";
|
|
51
|
+
import {
|
|
52
|
+
init_secret_scrubber,
|
|
53
|
+
scrubSecrets
|
|
54
|
+
} from "./cli-r2ap2czm.js";
|
|
55
|
+
import"./cli-pw9rj5t9.js";
|
|
56
|
+
import"./cli-5b6b62tz.js";
|
|
57
|
+
import"./cli-btbzvq95.js";
|
|
58
|
+
import"./cli-1m2e086m.js";
|
|
59
|
+
import"./cli-mcnhgnn1.js";
|
|
60
|
+
import"./cli-bzr0ftht.js";
|
|
61
|
+
import {
|
|
62
|
+
__require,
|
|
63
|
+
__toESM
|
|
64
|
+
} from "./cli-bc05qbss.js";
|
|
65
|
+
|
|
66
|
+
// src/providers/pi-mono-adapter.ts
|
|
67
|
+
import { existsSync, lstatSync, symlinkSync, unlinkSync } from "node:fs";
|
|
68
|
+
import { join } from "node:path";
|
|
69
|
+
|
|
70
|
+
// src/utils/aws-error-classifier.ts
|
|
71
|
+
var AWS_ERROR_RULES = [
|
|
72
|
+
{
|
|
73
|
+
patterns: [
|
|
74
|
+
/ExpiredToken(?:Exception)?/,
|
|
75
|
+
/CredentialsProviderError/,
|
|
76
|
+
/Unable to locate credentials/,
|
|
77
|
+
/security token.*expired/i,
|
|
78
|
+
/expired.*security token/i,
|
|
79
|
+
/InvalidSignatureException/,
|
|
80
|
+
/UnrecognizedClientException/
|
|
81
|
+
],
|
|
82
|
+
category: "aws-auth",
|
|
83
|
+
message: "AWS credentials have expired or are missing. " + "Run `aws sso login` (or refresh your credentials via `aws configure`) and retry."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
patterns: [
|
|
87
|
+
/ThrottlingException/,
|
|
88
|
+
/TooManyRequestsException/,
|
|
89
|
+
/ServiceQuotaExceededException/,
|
|
90
|
+
/Rate exceeded/
|
|
91
|
+
],
|
|
92
|
+
category: "aws-throttle",
|
|
93
|
+
message: "AWS Bedrock request was throttled (rate limit or quota exceeded). " + "Wait and retry, or request a quota increase in the AWS Service Quotas console."
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
patterns: [/AccessDeniedException/, /not authorized to perform/i],
|
|
97
|
+
category: "aws-access",
|
|
98
|
+
message: "AWS authorization denied for Bedrock. " + "Verify the IAM role/user has the `bedrock:InvokeModel` permission for the target model ARN and region."
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
patterns: [
|
|
102
|
+
/ValidationException/,
|
|
103
|
+
/ResourceNotFoundException/,
|
|
104
|
+
/ModelTimeoutException/,
|
|
105
|
+
/ModelNotReadyException/
|
|
106
|
+
],
|
|
107
|
+
category: "aws-model",
|
|
108
|
+
message: "AWS Bedrock model error: the model ID may be invalid, unavailable in this region, or not yet ready. " + "Verify MODEL_OVERRIDE and the AWS region in your environment."
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
function classifyAwsSdkError(text) {
|
|
112
|
+
if (!text)
|
|
113
|
+
return null;
|
|
114
|
+
for (const rule of AWS_ERROR_RULES) {
|
|
115
|
+
if (rule.patterns.some((re) => re.test(text))) {
|
|
116
|
+
return { category: rule.category, message: rule.message };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/providers/pi-mono-adapter.ts
|
|
123
|
+
init_secret_scrubber();
|
|
124
|
+
|
|
125
|
+
// src/providers/pi-mono-extension.ts
|
|
126
|
+
init_secret_scrubber();
|
|
127
|
+
function apiHeaders(config) {
|
|
128
|
+
return {
|
|
129
|
+
"Content-Type": "application/json",
|
|
130
|
+
...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {},
|
|
131
|
+
"X-Agent-ID": config.agentId
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function fireAndForget(url, init) {
|
|
135
|
+
fetch(url, init).catch(() => {});
|
|
136
|
+
}
|
|
137
|
+
async function isTaskCancelled(config) {
|
|
138
|
+
try {
|
|
139
|
+
const resp = await fetch(`${config.apiUrl}/cancelled-tasks?taskId=${encodeURIComponent(config.taskId)}`, { method: "GET", headers: apiHeaders(config) });
|
|
140
|
+
if (!resp.ok)
|
|
141
|
+
return { cancelled: false };
|
|
142
|
+
const data = await resp.json();
|
|
143
|
+
const match = data.cancelled?.find((t) => t.id === config.taskId);
|
|
144
|
+
return match ? { cancelled: true, reason: match.failureReason } : { cancelled: false };
|
|
145
|
+
} catch {
|
|
146
|
+
return { cancelled: false };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function checkShouldBlockPolling(config) {
|
|
150
|
+
try {
|
|
151
|
+
const resp = await fetch(`${config.apiUrl}/me`, {
|
|
152
|
+
method: "GET",
|
|
153
|
+
headers: apiHeaders(config)
|
|
154
|
+
});
|
|
155
|
+
if (!resp.ok)
|
|
156
|
+
return false;
|
|
157
|
+
const data = await resp.json();
|
|
158
|
+
return data.shouldBlockPolling === true;
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function fetchTaskDetails(config) {
|
|
164
|
+
try {
|
|
165
|
+
const resp = await fetch(`${config.apiUrl}/api/tasks/${config.taskId}`, {
|
|
166
|
+
method: "GET",
|
|
167
|
+
headers: apiHeaders(config)
|
|
168
|
+
});
|
|
169
|
+
if (!resp.ok)
|
|
170
|
+
return null;
|
|
171
|
+
return await resp.json();
|
|
172
|
+
} catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function syncIdentityFilesToServer(config, changeSource = "session_sync") {
|
|
177
|
+
const updates = {};
|
|
178
|
+
const paths = {
|
|
179
|
+
soulMd: "/workspace/SOUL.md",
|
|
180
|
+
identityMd: "/workspace/IDENTITY.md",
|
|
181
|
+
toolsMd: "/workspace/TOOLS.md"
|
|
182
|
+
};
|
|
183
|
+
for (const [key, path] of Object.entries(paths)) {
|
|
184
|
+
try {
|
|
185
|
+
const file = Bun.file(path);
|
|
186
|
+
if (await file.exists()) {
|
|
187
|
+
const content = await file.text();
|
|
188
|
+
if (content.trim() && content.length <= 65536) {
|
|
189
|
+
updates[key] = content;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
} catch {}
|
|
193
|
+
}
|
|
194
|
+
if (Object.keys(updates).length === 0)
|
|
195
|
+
return;
|
|
196
|
+
try {
|
|
197
|
+
await fetch(`${config.apiUrl}/api/agents/${config.agentId}/profile`, {
|
|
198
|
+
method: "PUT",
|
|
199
|
+
headers: apiHeaders(config),
|
|
200
|
+
body: JSON.stringify({ ...updates, changeSource })
|
|
201
|
+
});
|
|
202
|
+
} catch {}
|
|
203
|
+
}
|
|
204
|
+
async function syncSetupScriptToServer(config, changeSource = "session_sync") {
|
|
205
|
+
try {
|
|
206
|
+
const file = Bun.file("/workspace/start-up.sh");
|
|
207
|
+
if (!await file.exists())
|
|
208
|
+
return;
|
|
209
|
+
const raw = await file.text();
|
|
210
|
+
if (!raw.trim())
|
|
211
|
+
return;
|
|
212
|
+
const markerStart = "# === Agent-managed setup (from DB) ===";
|
|
213
|
+
const markerEnd = "# === End agent-managed setup ===";
|
|
214
|
+
const startIdx = raw.indexOf(markerStart);
|
|
215
|
+
const endIdx = raw.indexOf(markerEnd);
|
|
216
|
+
let content;
|
|
217
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
218
|
+
content = raw.substring(startIdx + markerStart.length, endIdx).trim();
|
|
219
|
+
} else {
|
|
220
|
+
content = raw.replace(/^#!\/bin\/bash\n/, "").trim();
|
|
221
|
+
}
|
|
222
|
+
if (!content || content.length > 65536)
|
|
223
|
+
return;
|
|
224
|
+
await fetch(`${config.apiUrl}/api/agents/${config.agentId}/profile`, {
|
|
225
|
+
method: "PUT",
|
|
226
|
+
headers: apiHeaders(config),
|
|
227
|
+
body: JSON.stringify({ setupScript: content, changeSource })
|
|
228
|
+
});
|
|
229
|
+
} catch {}
|
|
230
|
+
}
|
|
231
|
+
async function autoIndexMemoryFile(config, editedPath) {
|
|
232
|
+
try {
|
|
233
|
+
const fileContent = await Bun.file(editedPath).text();
|
|
234
|
+
const isShared = editedPath.startsWith("/workspace/shared/");
|
|
235
|
+
const fileName = editedPath.split("/").pop() ?? "unnamed";
|
|
236
|
+
await fetch(`${config.apiUrl}/api/memory/index`, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers: apiHeaders(config),
|
|
239
|
+
body: JSON.stringify({
|
|
240
|
+
agentId: config.agentId,
|
|
241
|
+
content: fileContent,
|
|
242
|
+
name: fileName.replace(/\.\w+$/, ""),
|
|
243
|
+
scope: isShared ? "swarm" : "agent",
|
|
244
|
+
source: "file_index",
|
|
245
|
+
sourcePath: editedPath
|
|
246
|
+
})
|
|
247
|
+
});
|
|
248
|
+
} catch {}
|
|
249
|
+
}
|
|
250
|
+
async function fetchConcurrentContext(config) {
|
|
251
|
+
try {
|
|
252
|
+
const resp = await fetch(`${config.apiUrl}/api/concurrent-context`, {
|
|
253
|
+
method: "GET",
|
|
254
|
+
headers: apiHeaders(config)
|
|
255
|
+
});
|
|
256
|
+
if (!resp.ok)
|
|
257
|
+
return null;
|
|
258
|
+
const data = await resp.json();
|
|
259
|
+
const lines = [];
|
|
260
|
+
if (data.processingInboxMessages.length > 0) {
|
|
261
|
+
lines.push("=== CONCURRENT SESSION AWARENESS ===");
|
|
262
|
+
lines.push("");
|
|
263
|
+
lines.push("**Other sessions are currently processing these inbox messages:**");
|
|
264
|
+
for (const msg of data.processingInboxMessages) {
|
|
265
|
+
const preview = msg.content.length > 120 ? `${msg.content.slice(0, 120)}...` : msg.content;
|
|
266
|
+
lines.push(`- [${msg.source}] "${preview}" (received ${msg.createdAt})`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (data.recentTaskDelegations.length > 0) {
|
|
270
|
+
if (lines.length === 0)
|
|
271
|
+
lines.push("=== CONCURRENT SESSION AWARENESS ===");
|
|
272
|
+
lines.push("");
|
|
273
|
+
lines.push("**Recent task delegations (last 5 min):**");
|
|
274
|
+
for (const task of data.recentTaskDelegations) {
|
|
275
|
+
const preview = task.task.length > 120 ? `${task.task.slice(0, 120)}...` : task.task;
|
|
276
|
+
lines.push(`- "${preview}" → ${task.agentName ?? "unassigned"} [${task.status}]`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (data.activeSwarmTasks.length > 0) {
|
|
280
|
+
if (lines.length === 0)
|
|
281
|
+
lines.push("=== CONCURRENT SESSION AWARENESS ===");
|
|
282
|
+
lines.push("");
|
|
283
|
+
lines.push("**Currently active tasks across the swarm:**");
|
|
284
|
+
for (const task of data.activeSwarmTasks) {
|
|
285
|
+
const preview = task.task.length > 100 ? `${task.task.slice(0, 100)}...` : task.task;
|
|
286
|
+
lines.push(`- ${task.agentName ?? "unassigned"}: "${preview}" [${task.status}]`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (lines.length > 0) {
|
|
290
|
+
lines.push("");
|
|
291
|
+
lines.push("IMPORTANT: Avoid duplicating work that is already being handled by other sessions or agents.");
|
|
292
|
+
lines.push("=== END CONCURRENT SESSION AWARENESS ===");
|
|
293
|
+
return lines.join(`
|
|
294
|
+
`);
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
} catch {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async function summarizeSessionForPi(config, sessionFile, deps = {}) {
|
|
302
|
+
if (!sessionFile) {
|
|
303
|
+
console.warn("session_summary skipped (pi): session transcript file unavailable");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const _runSummarize = deps.runSummarize ?? summarizeSession;
|
|
307
|
+
const _fetchRetrievals = deps.fetchRetrievalsForTask ?? fetchRetrievalsForTask;
|
|
308
|
+
const _postRatings = deps.postRatings ?? postRatings;
|
|
309
|
+
const _buildRatings = deps.buildRatingsFromLlm ?? buildRatingsFromLlm;
|
|
310
|
+
try {
|
|
311
|
+
let transcript = "";
|
|
312
|
+
try {
|
|
313
|
+
const fullTranscript = await Bun.file(sessionFile).text();
|
|
314
|
+
transcript = fullTranscript.length > 20000 ? fullTranscript.slice(-20000) : fullTranscript;
|
|
315
|
+
} catch (err) {
|
|
316
|
+
console.warn(scrubSecrets(`session_summary skipped (pi): cannot read ${sessionFile}: ${String(err)}`));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (transcript.length <= 100) {
|
|
320
|
+
console.warn(`session_summary skipped (pi): transcript too short (${transcript.length} chars)`);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const sourceTaskId = config.taskId;
|
|
324
|
+
const agentId = config.agentId;
|
|
325
|
+
if (!sourceTaskId || !agentId) {
|
|
326
|
+
console.warn(`session_summary skipped (pi): ${!sourceTaskId ? "task id unavailable" : "agent id unavailable"}`);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const taskDetails = await fetchTaskDetails(config).catch(() => null);
|
|
330
|
+
const wantRatings = getMemoryRaterNames().includes("llm");
|
|
331
|
+
const retrievals = wantRatings ? await _fetchRetrievals({
|
|
332
|
+
apiUrl: config.apiUrl,
|
|
333
|
+
apiKey: config.apiKey,
|
|
334
|
+
agentId,
|
|
335
|
+
taskId: sourceTaskId
|
|
336
|
+
}).catch(() => []) : [];
|
|
337
|
+
const result = await _runSummarize({
|
|
338
|
+
harness: "pi",
|
|
339
|
+
transcript,
|
|
340
|
+
retrievals,
|
|
341
|
+
taskContext: {
|
|
342
|
+
sourceTaskId,
|
|
343
|
+
agentId,
|
|
344
|
+
prompt: taskDetails?.task
|
|
345
|
+
},
|
|
346
|
+
apiUrl: config.apiUrl,
|
|
347
|
+
apiKey: config.apiKey,
|
|
348
|
+
env: config.env
|
|
349
|
+
});
|
|
350
|
+
if (!result) {
|
|
351
|
+
console.warn("session_summary skipped (pi): summarizer returned no result");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const summary = result.summary.trim();
|
|
355
|
+
if (summary.length <= 20 || summary.toLowerCase().includes("no significant learnings")) {
|
|
356
|
+
console.debug(`session_summary skipped (pi): summary failed quality gate (${summary.length} chars)`);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const indexResp = await fetch(`${config.apiUrl}/api/memory/index`, {
|
|
360
|
+
method: "POST",
|
|
361
|
+
headers: apiHeaders(config),
|
|
362
|
+
body: JSON.stringify({
|
|
363
|
+
scope: "agent",
|
|
364
|
+
source: "session_summary",
|
|
365
|
+
sourceTaskId,
|
|
366
|
+
content: summary,
|
|
367
|
+
name: "session-summary",
|
|
368
|
+
agentId
|
|
369
|
+
})
|
|
370
|
+
});
|
|
371
|
+
if (!indexResp.ok) {
|
|
372
|
+
const text = await indexResp.text().catch(() => "");
|
|
373
|
+
console.error("session_summary: /api/memory/index POST failed (pi):", indexResp.status, text);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (wantRatings && result.ratings && result.ratings.length > 0) {
|
|
377
|
+
const ratingEvents = _buildRatings(result.ratings, retrievals);
|
|
378
|
+
if (ratingEvents.length > 0) {
|
|
379
|
+
await _postRatings({
|
|
380
|
+
apiUrl: config.apiUrl,
|
|
381
|
+
apiKey: config.apiKey,
|
|
382
|
+
agentId,
|
|
383
|
+
taskId: sourceTaskId,
|
|
384
|
+
events: ratingEvents
|
|
385
|
+
}).catch((err) => console.error("session_summary: postRatings failed (pi):", err));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} catch (err) {
|
|
389
|
+
console.error("session_summary failed (pi):", err);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function createSwarmHooksExtension(config) {
|
|
393
|
+
return (pi) => {
|
|
394
|
+
let lastContextPostTime = 0;
|
|
395
|
+
pi.on("session_start", async (_event, _ctx) => {
|
|
396
|
+
fireAndForget(`${config.apiUrl}/ping`, {
|
|
397
|
+
method: "POST",
|
|
398
|
+
headers: apiHeaders(config)
|
|
399
|
+
});
|
|
400
|
+
if (config.taskId) {
|
|
401
|
+
await clearToolHistory(config.taskId);
|
|
402
|
+
}
|
|
403
|
+
if (config.isLead) {
|
|
404
|
+
const ctx = await fetchConcurrentContext(config);
|
|
405
|
+
if (ctx) {
|
|
406
|
+
console.log(ctx);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
pi.on("tool_call", async (event, _ctx) => {
|
|
411
|
+
if (!config.isLead && config.taskId) {
|
|
412
|
+
const { cancelled, reason } = await isTaskCancelled(config);
|
|
413
|
+
if (cancelled) {
|
|
414
|
+
const cancelReason = reason || "Task cancelled by lead or creator";
|
|
415
|
+
return {
|
|
416
|
+
block: true,
|
|
417
|
+
reason: `\uD83D\uDED1 TASK CANCELLED: Your current task (${config.taskId.slice(0, 8)}) has been cancelled. Reason: "${cancelReason}". ` + `Stop working on this task immediately. Do NOT continue making tool calls. ` + `Use store-progress to acknowledge the cancellation and mark the task as failed, then wait for new tasks.`
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (!config.isLead && config.taskId) {
|
|
422
|
+
const toolName = event.toolName;
|
|
423
|
+
const toolInput = "input" in event ? event.input : {};
|
|
424
|
+
const loopResult = await checkToolLoop(config.taskId, toolName, toolInput);
|
|
425
|
+
if (loopResult.blocked) {
|
|
426
|
+
return {
|
|
427
|
+
block: true,
|
|
428
|
+
reason: `LOOP DETECTED: ${loopResult.reason} ` + "Stop repeating this action and try a fundamentally different approach. " + "If you're truly stuck, use store-progress to report the blocker."
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
if (loopResult.severity === "warning" && loopResult.reason) {
|
|
432
|
+
console.log(`Warning: ${loopResult.reason}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (event.toolName?.endsWith("poll-task")) {
|
|
436
|
+
const shouldBlock = await checkShouldBlockPolling(config);
|
|
437
|
+
if (shouldBlock) {
|
|
438
|
+
return {
|
|
439
|
+
block: true,
|
|
440
|
+
reason: "\uD83D\uDED1 POLLING LIMIT REACHED: You have exceeded the maximum empty poll attempts. " + "EXIT NOW - do not make any more tool calls."
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return;
|
|
445
|
+
});
|
|
446
|
+
pi.on("tool_result", async (event, ctx) => {
|
|
447
|
+
if (!config.isLead && config.taskId) {
|
|
448
|
+
fireAndForget(`${config.apiUrl}/api/active-sessions/heartbeat/${config.taskId}`, {
|
|
449
|
+
method: "PUT",
|
|
450
|
+
headers: apiHeaders(config)
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
fireAndForget(`${config.apiUrl}/api/agents/${config.agentId}/activity`, {
|
|
454
|
+
method: "PUT",
|
|
455
|
+
headers: apiHeaders(config)
|
|
456
|
+
});
|
|
457
|
+
const usage = ctx.getContextUsage?.();
|
|
458
|
+
if (config.taskId && usage?.tokens != null) {
|
|
459
|
+
const now = Date.now();
|
|
460
|
+
if (now - lastContextPostTime >= 30000) {
|
|
461
|
+
lastContextPostTime = now;
|
|
462
|
+
fireAndForget(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
|
|
463
|
+
method: "POST",
|
|
464
|
+
headers: apiHeaders(config),
|
|
465
|
+
body: JSON.stringify({
|
|
466
|
+
eventType: "progress",
|
|
467
|
+
sessionId: `pi-${config.taskId}`,
|
|
468
|
+
contextUsedTokens: usage.tokens,
|
|
469
|
+
contextTotalTokens: usage.contextWindow,
|
|
470
|
+
contextPercent: usage.percent
|
|
471
|
+
})
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const toolName = event.toolName;
|
|
476
|
+
const input = event.input;
|
|
477
|
+
const editedPath = input?.file_path || input?.path;
|
|
478
|
+
if ((toolName === "write" || toolName === "edit") && editedPath) {
|
|
479
|
+
if (editedPath === "/workspace/SOUL.md" || editedPath === "/workspace/IDENTITY.md" || editedPath === "/workspace/TOOLS.md") {
|
|
480
|
+
syncIdentityFilesToServer(config, "self_edit");
|
|
481
|
+
}
|
|
482
|
+
if (editedPath.startsWith("/workspace/start-up")) {
|
|
483
|
+
syncSetupScriptToServer(config, "self_edit");
|
|
484
|
+
}
|
|
485
|
+
if (editedPath.startsWith("/workspace/personal/memory/") || editedPath.startsWith("/workspace/shared/memory/")) {
|
|
486
|
+
autoIndexMemoryFile(config, editedPath);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (config.isLead && event.toolName?.endsWith("send-task")) {
|
|
490
|
+
console.log("Task sent successfully. Monitor progress using the get-task-details tool periodically.");
|
|
491
|
+
}
|
|
492
|
+
return;
|
|
493
|
+
});
|
|
494
|
+
pi.on("context", async (_event, ctx) => {
|
|
495
|
+
if (!config.taskId)
|
|
496
|
+
return;
|
|
497
|
+
try {
|
|
498
|
+
const taskDetails = await fetchTaskDetails(config);
|
|
499
|
+
if (taskDetails) {
|
|
500
|
+
const reminder = [
|
|
501
|
+
"=== GOAL REMINDER (injected before context compaction) ===",
|
|
502
|
+
`Task ID: ${taskDetails.id}`,
|
|
503
|
+
`Task: ${taskDetails.task}`
|
|
504
|
+
];
|
|
505
|
+
if (taskDetails.progress) {
|
|
506
|
+
reminder.push(`Current Progress: ${taskDetails.progress}`);
|
|
507
|
+
}
|
|
508
|
+
reminder.push("=== Continue working on this task after compaction ===");
|
|
509
|
+
console.log(reminder.join(`
|
|
510
|
+
`));
|
|
511
|
+
}
|
|
512
|
+
} catch {}
|
|
513
|
+
const usage = ctx.getContextUsage?.();
|
|
514
|
+
if (usage) {
|
|
515
|
+
fireAndForget(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
|
|
516
|
+
method: "POST",
|
|
517
|
+
headers: apiHeaders(config),
|
|
518
|
+
body: JSON.stringify({
|
|
519
|
+
eventType: "compaction",
|
|
520
|
+
sessionId: `pi-${config.taskId}`,
|
|
521
|
+
contextUsedTokens: usage.tokens ?? undefined,
|
|
522
|
+
contextTotalTokens: usage.contextWindow,
|
|
523
|
+
contextPercent: usage.percent ?? undefined
|
|
524
|
+
})
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
});
|
|
529
|
+
pi.on("input", async (_event, _ctx) => {
|
|
530
|
+
if (!config.isLead && config.taskId) {
|
|
531
|
+
const { cancelled, reason } = await isTaskCancelled(config);
|
|
532
|
+
if (cancelled) {
|
|
533
|
+
const cancelReason = reason || "Task cancelled by lead or creator";
|
|
534
|
+
console.log(`\uD83D\uDED1 TASK CANCELLED: ${cancelReason}. Stop working and use store-progress to acknowledge.`);
|
|
535
|
+
return { action: "handled" };
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return;
|
|
539
|
+
});
|
|
540
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
541
|
+
const usage = ctx.getContextUsage?.();
|
|
542
|
+
if (config.taskId && usage) {
|
|
543
|
+
await fetch(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
|
|
544
|
+
method: "POST",
|
|
545
|
+
headers: apiHeaders(config),
|
|
546
|
+
body: JSON.stringify({
|
|
547
|
+
eventType: "completion",
|
|
548
|
+
sessionId: `pi-${config.taskId}`,
|
|
549
|
+
contextTotalTokens: usage.contextWindow,
|
|
550
|
+
contextPercent: usage.percent ?? undefined,
|
|
551
|
+
contextUsedTokens: usage.tokens ?? undefined
|
|
552
|
+
})
|
|
553
|
+
}).catch(() => {});
|
|
554
|
+
}
|
|
555
|
+
await syncIdentityFilesToServer(config);
|
|
556
|
+
await syncSetupScriptToServer(config);
|
|
557
|
+
const sessionFile = ctx.sessionManager.getSessionFile?.();
|
|
558
|
+
if (!process.env.SKIP_SESSION_SUMMARY) {
|
|
559
|
+
await summarizeSessionForPi(config, sessionFile);
|
|
560
|
+
} else {
|
|
561
|
+
console.debug("session_summary skipped (pi): SKIP_SESSION_SUMMARY is set");
|
|
562
|
+
}
|
|
563
|
+
fireAndForget(`${config.apiUrl}/close`, {
|
|
564
|
+
method: "POST",
|
|
565
|
+
headers: apiHeaders(config)
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
// src/providers/pi-mono-adapter.ts
|
|
571
|
+
function modelToCredKeys(modelStr) {
|
|
572
|
+
if (!modelStr)
|
|
573
|
+
return null;
|
|
574
|
+
const lower = modelStr.toLowerCase();
|
|
575
|
+
if (lower === "opus" || lower === "sonnet" || lower === "haiku") {
|
|
576
|
+
return ["ANTHROPIC_API_KEY", "OPENROUTER_API_KEY"];
|
|
577
|
+
}
|
|
578
|
+
if (modelStr.includes("/")) {
|
|
579
|
+
const provider = modelStr.slice(0, modelStr.indexOf("/")).toLowerCase();
|
|
580
|
+
if (provider === "anthropic")
|
|
581
|
+
return ["ANTHROPIC_API_KEY"];
|
|
582
|
+
if (provider === "openrouter")
|
|
583
|
+
return ["OPENROUTER_API_KEY"];
|
|
584
|
+
if (provider === "openai")
|
|
585
|
+
return ["OPENAI_API_KEY"];
|
|
586
|
+
if (provider === "google")
|
|
587
|
+
return ["GOOGLE_API_KEY"];
|
|
588
|
+
}
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
function getHarnessDrivableBedrockModels() {
|
|
592
|
+
try {
|
|
593
|
+
return getBuiltinModels("amazon-bedrock").map((m) => ({ id: m.id, name: m.name }));
|
|
594
|
+
} catch {
|
|
595
|
+
return [];
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
async function runBedrockSdkProbeAndEnumerate(region) {
|
|
599
|
+
const { BedrockClient, ListFoundationModelsCommand, ListInferenceProfilesCommand } = await import("./index-kkeftv5h.js").then((m)=>__toESM(m.default,1));
|
|
600
|
+
const client = new BedrockClient({ region });
|
|
601
|
+
const invocable = new Set;
|
|
602
|
+
const fmResponse = await client.send(new ListFoundationModelsCommand({ byInferenceType: "ON_DEMAND", byOutputModality: "TEXT" }));
|
|
603
|
+
for (const m of fmResponse.modelSummaries ?? []) {
|
|
604
|
+
if (m.modelId && m.modelLifecycle?.status === "ACTIVE") {
|
|
605
|
+
invocable.add(m.modelId);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
const profileResponse = await client.send(new ListInferenceProfilesCommand({}));
|
|
609
|
+
for (const p of profileResponse.inferenceProfileSummaries ?? []) {
|
|
610
|
+
if (p.inferenceProfileId) {
|
|
611
|
+
invocable.add(p.inferenceProfileId);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return getHarnessDrivableBedrockModels().filter((m) => invocable.has(m.id));
|
|
615
|
+
}
|
|
616
|
+
async function checkPiMonoCredentials(env, opts = {}) {
|
|
617
|
+
const bedrockAuthMode = env.BEDROCK_AUTH_MODE?.toLowerCase();
|
|
618
|
+
const isBedrockBearer = bedrockAuthMode === "bearer";
|
|
619
|
+
const isBedrockSdk = bedrockAuthMode === "sdk" || bedrockAuthMode === undefined && env.MODEL_OVERRIDE?.toLowerCase().startsWith("amazon-bedrock/");
|
|
620
|
+
if (isBedrockBearer && !env.AWS_BEARER_TOKEN_BEDROCK) {
|
|
621
|
+
return {
|
|
622
|
+
ready: false,
|
|
623
|
+
missing: ["AWS_BEARER_TOKEN_BEDROCK"],
|
|
624
|
+
hint: "BEDROCK_AUTH_MODE=bearer requires AWS_BEARER_TOKEN_BEDROCK (a Bedrock API key); set it, or use BEDROCK_AUTH_MODE=sdk to authenticate through the AWS credential chain.",
|
|
625
|
+
bedrockModels: [],
|
|
626
|
+
bedrockRegion: env.AWS_REGION ?? ""
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
if (isBedrockSdk || isBedrockBearer) {
|
|
630
|
+
const region = env.AWS_REGION;
|
|
631
|
+
if (!region) {
|
|
632
|
+
return {
|
|
633
|
+
ready: false,
|
|
634
|
+
missing: [],
|
|
635
|
+
hint: "AWS_REGION is not set — set it to the region where your Bedrock models are accessible so model enumeration matches the inference region.",
|
|
636
|
+
bedrockModels: [],
|
|
637
|
+
bedrockRegion: ""
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
const probe = opts.bedrockProbe ?? (() => runBedrockSdkProbeAndEnumerate(region));
|
|
641
|
+
try {
|
|
642
|
+
const probeResult = await probe();
|
|
643
|
+
const bedrockModels = Array.isArray(probeResult) ? probeResult : [];
|
|
644
|
+
return {
|
|
645
|
+
ready: true,
|
|
646
|
+
missing: [],
|
|
647
|
+
satisfiedBy: isBedrockBearer ? "env" : "sdk-delegated",
|
|
648
|
+
hint: `Bedrock models invocable in ${region} enumerated (${bedrockModels.length} usable; ListFoundationModels + ListInferenceProfiles).`,
|
|
649
|
+
bedrockModels,
|
|
650
|
+
bedrockRegion: region
|
|
651
|
+
};
|
|
652
|
+
} catch (err) {
|
|
653
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
654
|
+
const classification = classifyAwsSdkError(errorMessage);
|
|
655
|
+
return {
|
|
656
|
+
ready: false,
|
|
657
|
+
missing: [],
|
|
658
|
+
hint: classification?.message ?? `AWS Bedrock enumeration failed (region: ${region}): ${errorMessage}`,
|
|
659
|
+
bedrockModels: [],
|
|
660
|
+
bedrockRegion: region
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const homeDir = opts.homeDir ?? env.HOME ?? "/root";
|
|
665
|
+
const fsProbe = opts.fs?.existsSync ?? existsSync;
|
|
666
|
+
const authFile = `${homeDir}/.pi/agent/auth.json`;
|
|
667
|
+
if (fsProbe(authFile)) {
|
|
668
|
+
return { ready: true, missing: [], satisfiedBy: "file" };
|
|
669
|
+
}
|
|
670
|
+
const requiredKeys = modelToCredKeys(env.MODEL_OVERRIDE);
|
|
671
|
+
if (requiredKeys) {
|
|
672
|
+
if (requiredKeys.some((k) => env[k])) {
|
|
673
|
+
return { ready: true, missing: [], satisfiedBy: "env" };
|
|
674
|
+
}
|
|
675
|
+
const keyList = requiredKeys.join(" / ");
|
|
676
|
+
return {
|
|
677
|
+
ready: false,
|
|
678
|
+
missing: [...requiredKeys, authFile],
|
|
679
|
+
hint: `MODEL_OVERRIDE=${env.MODEL_OVERRIDE} requires one of ${keyList}; or run \`pi auth login\` to create ${authFile}.`
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
if (env.ANTHROPIC_API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY) {
|
|
683
|
+
return { ready: true, missing: [], satisfiedBy: "env" };
|
|
684
|
+
}
|
|
685
|
+
return {
|
|
686
|
+
ready: false,
|
|
687
|
+
missing: ["ANTHROPIC_API_KEY", "OPENROUTER_API_KEY", "OPENAI_API_KEY", authFile],
|
|
688
|
+
hint: "Set one of ANTHROPIC_API_KEY / OPENROUTER_API_KEY / OPENAI_API_KEY (any one suffices), or run `pi auth login` to create ~/.pi/agent/auth.json."
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function jsonSchemaToTypeBox(schema) {
|
|
692
|
+
return exports_typebox.Unsafe(schema);
|
|
693
|
+
}
|
|
694
|
+
function mcpToolsToDefinitions(mcpClient, tools) {
|
|
695
|
+
return tools.map((tool) => ({
|
|
696
|
+
name: tool.name,
|
|
697
|
+
label: tool.name,
|
|
698
|
+
description: tool.description || tool.name,
|
|
699
|
+
parameters: jsonSchemaToTypeBox(tool.inputSchema),
|
|
700
|
+
async execute(_toolCallId, params) {
|
|
701
|
+
const result = await mcpClient.callTool(tool.name, params);
|
|
702
|
+
const text = result.content.map((c) => c.text ?? "").filter(Boolean).join(`
|
|
703
|
+
`);
|
|
704
|
+
if (result.isError) {
|
|
705
|
+
throw new Error(text || `Tool ${tool.name} failed with no error message`);
|
|
706
|
+
}
|
|
707
|
+
return {
|
|
708
|
+
content: [{ type: "text", text: text || "(no output)" }],
|
|
709
|
+
details: undefined
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
}));
|
|
713
|
+
}
|
|
714
|
+
var ANTHROPIC_SHORTNAME_OPENROUTER_MIRROR = {
|
|
715
|
+
fable: "anthropic/claude-fable-5",
|
|
716
|
+
opus: "anthropic/claude-opus-4.8",
|
|
717
|
+
sonnet: "anthropic/claude-sonnet-5",
|
|
718
|
+
haiku: "anthropic/claude-haiku-4.5"
|
|
719
|
+
};
|
|
720
|
+
function envHasAnthropicCred(env) {
|
|
721
|
+
return !!(env.ANTHROPIC_API_KEY || env.ANTHROPIC_OAUTH_TOKEN);
|
|
722
|
+
}
|
|
723
|
+
var PI_RUNTIME_API_KEYS = [
|
|
724
|
+
["OPENROUTER_API_KEY", "openrouter"],
|
|
725
|
+
["ANTHROPIC_API_KEY", "anthropic"],
|
|
726
|
+
["OPENAI_API_KEY", "openai"],
|
|
727
|
+
["GOOGLE_API_KEY", "google"]
|
|
728
|
+
];
|
|
729
|
+
async function createPiRuntimeAuth(env = process.env) {
|
|
730
|
+
const modelRuntime = await ModelRuntime.create();
|
|
731
|
+
for (const [envKey, provider] of PI_RUNTIME_API_KEYS) {
|
|
732
|
+
const apiKey = env[envKey];
|
|
733
|
+
if (apiKey) {
|
|
734
|
+
await modelRuntime.setRuntimeApiKey(provider, apiKey);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return modelRuntime;
|
|
738
|
+
}
|
|
739
|
+
var OPENROUTER_OVERRIDE_MARKER = ".agent-swarm-openrouter-override.json";
|
|
740
|
+
async function readJsonFile(path) {
|
|
741
|
+
try {
|
|
742
|
+
const file = Bun.file(path);
|
|
743
|
+
if (!await file.exists())
|
|
744
|
+
return;
|
|
745
|
+
const parsed = JSON.parse(await file.text());
|
|
746
|
+
return typeof parsed === "object" && parsed !== null ? parsed : undefined;
|
|
747
|
+
} catch {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
async function ensureOpenRouterModelsOverride(agentDir, env = process.env) {
|
|
752
|
+
const baseUrl = getOpenRouterBaseUrl(env);
|
|
753
|
+
const modelsPath = join(agentDir, "models.json");
|
|
754
|
+
const markerPath = join(agentDir, OPENROUTER_OVERRIDE_MARKER);
|
|
755
|
+
if (baseUrl === DEFAULT_OPENROUTER_BASE_URL) {
|
|
756
|
+
await revertOpenRouterModelsOverride(modelsPath, markerPath);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
let existing = {};
|
|
760
|
+
try {
|
|
761
|
+
const file = Bun.file(modelsPath);
|
|
762
|
+
if (await file.exists()) {
|
|
763
|
+
existing = JSON.parse(await file.text());
|
|
764
|
+
}
|
|
765
|
+
} catch (err) {
|
|
766
|
+
console.warn(`[pi-mono] ${modelsPath} is unreadable/invalid (${err instanceof Error ? err.message : err}); rewriting with the OpenRouter gateway override`);
|
|
767
|
+
existing = {};
|
|
768
|
+
}
|
|
769
|
+
const providers = typeof existing.providers === "object" && existing.providers !== null ? existing.providers : {};
|
|
770
|
+
const openrouter = typeof providers.openrouter === "object" && providers.openrouter !== null ? providers.openrouter : {};
|
|
771
|
+
const priorMarker = await readJsonFile(markerPath);
|
|
772
|
+
const currentBaseUrl = typeof openrouter.baseUrl === "string" ? openrouter.baseUrl : undefined;
|
|
773
|
+
const previous = priorMarker ? priorMarker.previous : currentBaseUrl !== baseUrl ? currentBaseUrl : undefined;
|
|
774
|
+
const next = {
|
|
775
|
+
...existing,
|
|
776
|
+
providers: {
|
|
777
|
+
...providers,
|
|
778
|
+
openrouter: { ...openrouter, baseUrl }
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
await Bun.write(modelsPath, `${JSON.stringify(next, null, 2)}
|
|
782
|
+
`);
|
|
783
|
+
const marker = {
|
|
784
|
+
baseUrl,
|
|
785
|
+
...previous !== undefined ? { previous } : {}
|
|
786
|
+
};
|
|
787
|
+
await Bun.write(markerPath, `${JSON.stringify(marker, null, 2)}
|
|
788
|
+
`);
|
|
789
|
+
}
|
|
790
|
+
async function revertOpenRouterModelsOverride(modelsPath, markerPath) {
|
|
791
|
+
const marker = await readJsonFile(markerPath);
|
|
792
|
+
if (!marker)
|
|
793
|
+
return;
|
|
794
|
+
const removeMarker = () => Bun.file(markerPath).delete();
|
|
795
|
+
const existing = await readJsonFile(modelsPath);
|
|
796
|
+
if (!existing) {
|
|
797
|
+
await removeMarker();
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const providers = typeof existing.providers === "object" && existing.providers !== null ? { ...existing.providers } : undefined;
|
|
801
|
+
const openrouter = providers && typeof providers.openrouter === "object" && providers.openrouter !== null ? { ...providers.openrouter } : undefined;
|
|
802
|
+
if (!providers || !openrouter || openrouter.baseUrl !== marker.baseUrl) {
|
|
803
|
+
await removeMarker();
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (marker.previous !== undefined) {
|
|
807
|
+
openrouter.baseUrl = marker.previous;
|
|
808
|
+
} else {
|
|
809
|
+
delete openrouter.baseUrl;
|
|
810
|
+
}
|
|
811
|
+
if (Object.keys(openrouter).length === 0) {
|
|
812
|
+
delete providers.openrouter;
|
|
813
|
+
} else {
|
|
814
|
+
providers.openrouter = openrouter;
|
|
815
|
+
}
|
|
816
|
+
const next = { ...existing, providers };
|
|
817
|
+
if (Object.keys(providers).length === 0) {
|
|
818
|
+
delete next.providers;
|
|
819
|
+
}
|
|
820
|
+
if (Object.keys(next).length === 0) {
|
|
821
|
+
await Bun.file(modelsPath).delete();
|
|
822
|
+
} else {
|
|
823
|
+
await Bun.write(modelsPath, `${JSON.stringify(next, null, 2)}
|
|
824
|
+
`);
|
|
825
|
+
}
|
|
826
|
+
await removeMarker();
|
|
827
|
+
}
|
|
828
|
+
function resolveModel(modelStr, env = process.env) {
|
|
829
|
+
if (!modelStr)
|
|
830
|
+
return;
|
|
831
|
+
const lower = modelStr.toLowerCase();
|
|
832
|
+
const isAnthropicShortname = lower === "opus" || lower === "sonnet" || lower === "haiku" || lower === "fable";
|
|
833
|
+
if (isAnthropicShortname && !envHasAnthropicCred(env) && env.OPENROUTER_API_KEY) {
|
|
834
|
+
const orModelId = ANTHROPIC_SHORTNAME_OPENROUTER_MIRROR[lower];
|
|
835
|
+
if (orModelId) {
|
|
836
|
+
try {
|
|
837
|
+
return getBuiltinModel("openrouter", orModelId);
|
|
838
|
+
} catch {}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
const shortnames = {
|
|
842
|
+
fable: ["anthropic", "claude-fable-5"],
|
|
843
|
+
opus: ["anthropic", "claude-opus-4-8"],
|
|
844
|
+
sonnet: ["anthropic", "claude-sonnet-5"],
|
|
845
|
+
haiku: ["anthropic", "claude-haiku-4-5-20251001"]
|
|
846
|
+
};
|
|
847
|
+
const mapping = shortnames[lower];
|
|
848
|
+
if (mapping) {
|
|
849
|
+
try {
|
|
850
|
+
return getBuiltinModel(mapping[0], mapping[1]);
|
|
851
|
+
} catch {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (modelStr.includes("/")) {
|
|
856
|
+
const slashIdx = modelStr.indexOf("/");
|
|
857
|
+
const provider = modelStr.slice(0, slashIdx);
|
|
858
|
+
const modelId = modelStr.slice(slashIdx + 1);
|
|
859
|
+
try {
|
|
860
|
+
return getBuiltinModel(provider, modelId);
|
|
861
|
+
} catch {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
for (const provider of ["anthropic", "openai", "google"]) {
|
|
866
|
+
try {
|
|
867
|
+
return getBuiltinModel(provider, modelStr);
|
|
868
|
+
} catch {}
|
|
869
|
+
}
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
function createAgentsMdSymlink(cwd) {
|
|
873
|
+
const claudeMd = join(cwd, "CLAUDE.md");
|
|
874
|
+
const agentsMd = join(cwd, "AGENTS.md");
|
|
875
|
+
if (existsSync(claudeMd) && !existsSync(agentsMd)) {
|
|
876
|
+
try {
|
|
877
|
+
symlinkSync("CLAUDE.md", agentsMd);
|
|
878
|
+
return true;
|
|
879
|
+
} catch {
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
function cleanupAgentsMdSymlink(cwd) {
|
|
886
|
+
const agentsMd = join(cwd, "AGENTS.md");
|
|
887
|
+
try {
|
|
888
|
+
if (existsSync(agentsMd) && lstatSync(agentsMd).isSymbolicLink()) {
|
|
889
|
+
unlinkSync(agentsMd);
|
|
890
|
+
}
|
|
891
|
+
} catch {}
|
|
892
|
+
}
|
|
893
|
+
function extractTextContent(content) {
|
|
894
|
+
if (typeof content === "string")
|
|
895
|
+
return content.trim();
|
|
896
|
+
if (!Array.isArray(content))
|
|
897
|
+
return "";
|
|
898
|
+
return content.filter((c) => typeof c === "object" && c !== null && c.type === "text").map((c) => c.text || "").join("").trim();
|
|
899
|
+
}
|
|
900
|
+
function extractPiAssistantText(message) {
|
|
901
|
+
if (!message || typeof message !== "object")
|
|
902
|
+
return "";
|
|
903
|
+
const msg = message;
|
|
904
|
+
if (msg.role !== "assistant")
|
|
905
|
+
return "";
|
|
906
|
+
return extractTextContent(msg.content);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
class PiMonoSession {
|
|
910
|
+
listeners = [];
|
|
911
|
+
eventQueue = [];
|
|
912
|
+
_sessionId;
|
|
913
|
+
completionPromise;
|
|
914
|
+
agentSession;
|
|
915
|
+
config;
|
|
916
|
+
createdSymlink;
|
|
917
|
+
logFileHandle;
|
|
918
|
+
lastEmittedMessage = "";
|
|
919
|
+
lastAssistantText = "";
|
|
920
|
+
sessionStartedAt = Date.now();
|
|
921
|
+
prevOutputTokens = 0;
|
|
922
|
+
terminalError = null;
|
|
923
|
+
appliedReasoningEffort;
|
|
924
|
+
sessionEnded = false;
|
|
925
|
+
constructor(agentSession, config, createdSymlink, appliedReasoningEffort = null) {
|
|
926
|
+
this.agentSession = agentSession;
|
|
927
|
+
this.config = config;
|
|
928
|
+
this.createdSymlink = createdSymlink;
|
|
929
|
+
this.appliedReasoningEffort = appliedReasoningEffort;
|
|
930
|
+
this.logFileHandle = Bun.file(config.logFile).writer();
|
|
931
|
+
this._sessionId = agentSession.sessionId;
|
|
932
|
+
this.sessionStartedAt = Date.now();
|
|
933
|
+
const piVersion = readPkgVersion("@earendil-works/pi-coding-agent");
|
|
934
|
+
this.emit({
|
|
935
|
+
type: "session_init",
|
|
936
|
+
sessionId: this._sessionId,
|
|
937
|
+
provider: "pi",
|
|
938
|
+
harnessVariant: "stock",
|
|
939
|
+
...piVersion ? { harnessVariantMeta: { version: piVersion } } : {}
|
|
940
|
+
});
|
|
941
|
+
this.agentSession.subscribe((event) => this.handleAgentEvent(event));
|
|
942
|
+
this.completionPromise = this.runSession();
|
|
943
|
+
}
|
|
944
|
+
reportedModel() {
|
|
945
|
+
const m = this.agentSession.model;
|
|
946
|
+
if (m)
|
|
947
|
+
return `${m.provider}/${m.id}`;
|
|
948
|
+
return this.config.model;
|
|
949
|
+
}
|
|
950
|
+
emit(event) {
|
|
951
|
+
const scrubbed = event.type === "raw_log" || event.type === "raw_stderr" ? { ...event, content: scrubSecrets(event.content) } : event;
|
|
952
|
+
this.logFileHandle.write(`${JSON.stringify({ ...scrubbed, timestamp: new Date().toISOString() })}
|
|
953
|
+
`);
|
|
954
|
+
if (this.listeners.length > 0) {
|
|
955
|
+
for (const listener of this.listeners) {
|
|
956
|
+
listener(scrubbed);
|
|
957
|
+
}
|
|
958
|
+
} else {
|
|
959
|
+
this.eventQueue.push(scrubbed);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
handleAgentEvent(event) {
|
|
963
|
+
switch (event.type) {
|
|
964
|
+
case "message_end": {
|
|
965
|
+
const endMsg = event.message;
|
|
966
|
+
if (endMsg.role === "assistant") {
|
|
967
|
+
if (endMsg.stopReason === "error") {
|
|
968
|
+
this.terminalError = endMsg.errorMessage ?? this.terminalError ?? "Unknown error";
|
|
969
|
+
break;
|
|
970
|
+
}
|
|
971
|
+
this.terminalError = null;
|
|
972
|
+
}
|
|
973
|
+
const text = extractPiAssistantText(event.message);
|
|
974
|
+
if (text) {
|
|
975
|
+
this.lastAssistantText = text;
|
|
976
|
+
}
|
|
977
|
+
if (text && text !== this.lastEmittedMessage) {
|
|
978
|
+
const model = this.reportedModel();
|
|
979
|
+
this.emit({
|
|
980
|
+
type: "raw_log",
|
|
981
|
+
content: JSON.stringify({
|
|
982
|
+
type: "assistant",
|
|
983
|
+
message: {
|
|
984
|
+
role: "assistant",
|
|
985
|
+
content: [{ type: "text", text }],
|
|
986
|
+
model
|
|
987
|
+
}
|
|
988
|
+
})
|
|
989
|
+
});
|
|
990
|
+
this.emit({ type: "message", role: "assistant", content: text });
|
|
991
|
+
this.lastEmittedMessage = text;
|
|
992
|
+
}
|
|
993
|
+
const usage = this.agentSession.getContextUsage();
|
|
994
|
+
if (usage && usage.tokens != null) {
|
|
995
|
+
const stats = this.agentSession.getSessionStats();
|
|
996
|
+
const currOutput = stats?.tokens?.output ?? 0;
|
|
997
|
+
const outputDelta = Math.max(0, currOutput - this.prevOutputTokens);
|
|
998
|
+
this.prevOutputTokens = currOutput;
|
|
999
|
+
this.emit({
|
|
1000
|
+
type: "context_usage",
|
|
1001
|
+
contextUsedTokens: usage.tokens,
|
|
1002
|
+
contextTotalTokens: usage.contextWindow,
|
|
1003
|
+
contextPercent: usage.percent ?? 0,
|
|
1004
|
+
outputTokens: outputDelta,
|
|
1005
|
+
contextFormula: "pi-delegated"
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
break;
|
|
1009
|
+
}
|
|
1010
|
+
case "tool_execution_start": {
|
|
1011
|
+
const model = this.reportedModel();
|
|
1012
|
+
this.emit({
|
|
1013
|
+
type: "raw_log",
|
|
1014
|
+
content: JSON.stringify({
|
|
1015
|
+
type: "assistant",
|
|
1016
|
+
message: {
|
|
1017
|
+
role: "assistant",
|
|
1018
|
+
content: [
|
|
1019
|
+
{ type: "tool_use", id: event.toolCallId, name: event.toolName, input: event.args }
|
|
1020
|
+
],
|
|
1021
|
+
model
|
|
1022
|
+
}
|
|
1023
|
+
})
|
|
1024
|
+
});
|
|
1025
|
+
this.emit({
|
|
1026
|
+
type: "tool_start",
|
|
1027
|
+
toolCallId: event.toolCallId,
|
|
1028
|
+
toolName: event.toolName,
|
|
1029
|
+
args: event.args
|
|
1030
|
+
});
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
case "tool_execution_end":
|
|
1034
|
+
this.emit({
|
|
1035
|
+
type: "raw_log",
|
|
1036
|
+
content: JSON.stringify({
|
|
1037
|
+
type: "assistant",
|
|
1038
|
+
message: {
|
|
1039
|
+
role: "assistant",
|
|
1040
|
+
content: [
|
|
1041
|
+
{
|
|
1042
|
+
type: "tool_result",
|
|
1043
|
+
tool_use_id: event.toolCallId,
|
|
1044
|
+
content: typeof event.result === "string" ? event.result : JSON.stringify(event.result)
|
|
1045
|
+
}
|
|
1046
|
+
]
|
|
1047
|
+
}
|
|
1048
|
+
})
|
|
1049
|
+
});
|
|
1050
|
+
this.emit({
|
|
1051
|
+
type: "tool_end",
|
|
1052
|
+
toolCallId: event.toolCallId,
|
|
1053
|
+
toolName: event.toolName,
|
|
1054
|
+
result: event.result
|
|
1055
|
+
});
|
|
1056
|
+
break;
|
|
1057
|
+
case "auto_retry_end": {
|
|
1058
|
+
if (event.success) {
|
|
1059
|
+
this.terminalError = null;
|
|
1060
|
+
} else {
|
|
1061
|
+
this.terminalError = event.finalError ?? this.terminalError ?? "Unknown error";
|
|
1062
|
+
}
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
async runSession() {
|
|
1068
|
+
try {
|
|
1069
|
+
await this.agentSession.prompt(this.config.prompt, {
|
|
1070
|
+
source: "rpc"
|
|
1071
|
+
});
|
|
1072
|
+
await this.waitForIdle();
|
|
1073
|
+
const stats = this.agentSession.getSessionStats();
|
|
1074
|
+
const cost = this.buildCostData(stats);
|
|
1075
|
+
if (this.terminalError) {
|
|
1076
|
+
const classification = classifyAwsSdkError(this.terminalError);
|
|
1077
|
+
const message = classification?.message ?? this.terminalError;
|
|
1078
|
+
const category = classification?.category;
|
|
1079
|
+
this.emit({ type: "error", message, category });
|
|
1080
|
+
return {
|
|
1081
|
+
exitCode: 1,
|
|
1082
|
+
sessionId: this._sessionId,
|
|
1083
|
+
cost,
|
|
1084
|
+
isError: true,
|
|
1085
|
+
errorCategory: category,
|
|
1086
|
+
failureReason: message,
|
|
1087
|
+
appliedReasoningEffort: this.appliedReasoningEffort
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
this.emit({
|
|
1091
|
+
type: "result",
|
|
1092
|
+
cost,
|
|
1093
|
+
isError: false
|
|
1094
|
+
});
|
|
1095
|
+
return {
|
|
1096
|
+
exitCode: 0,
|
|
1097
|
+
sessionId: this._sessionId,
|
|
1098
|
+
cost,
|
|
1099
|
+
output: this.lastAssistantText || undefined,
|
|
1100
|
+
isError: false,
|
|
1101
|
+
appliedReasoningEffort: this.appliedReasoningEffort
|
|
1102
|
+
};
|
|
1103
|
+
} catch (err) {
|
|
1104
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1105
|
+
const awsCatchError = classifyAwsSdkError(errorMessage);
|
|
1106
|
+
if (awsCatchError) {
|
|
1107
|
+
this.emit({
|
|
1108
|
+
type: "error",
|
|
1109
|
+
message: awsCatchError.message,
|
|
1110
|
+
category: awsCatchError.category
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
this.emit({ type: "raw_stderr", content: `[pi-mono] Error: ${errorMessage}
|
|
1114
|
+
` });
|
|
1115
|
+
return {
|
|
1116
|
+
exitCode: 1,
|
|
1117
|
+
sessionId: this._sessionId,
|
|
1118
|
+
isError: true,
|
|
1119
|
+
errorCategory: awsCatchError?.category,
|
|
1120
|
+
failureReason: awsCatchError?.message ?? errorMessage,
|
|
1121
|
+
appliedReasoningEffort: this.appliedReasoningEffort
|
|
1122
|
+
};
|
|
1123
|
+
} finally {
|
|
1124
|
+
this.sessionEnded = true;
|
|
1125
|
+
await this.logFileHandle.end();
|
|
1126
|
+
if (this.createdSymlink) {
|
|
1127
|
+
cleanupAgentsMdSymlink(this.config.cwd);
|
|
1128
|
+
}
|
|
1129
|
+
this.agentSession.dispose();
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
waitForIdle() {
|
|
1133
|
+
return new Promise((resolve) => {
|
|
1134
|
+
if (!this.agentSession.isStreaming) {
|
|
1135
|
+
resolve();
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
const unsub = this.agentSession.subscribe((event) => {
|
|
1139
|
+
if (event.type === "agent_end") {
|
|
1140
|
+
unsub();
|
|
1141
|
+
resolve();
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
buildCostData(stats) {
|
|
1147
|
+
return {
|
|
1148
|
+
sessionId: "",
|
|
1149
|
+
taskId: this.config.taskId,
|
|
1150
|
+
agentId: this.config.agentId,
|
|
1151
|
+
totalCostUsd: stats.cost || 0,
|
|
1152
|
+
inputTokens: stats.tokens.input,
|
|
1153
|
+
outputTokens: stats.tokens.output,
|
|
1154
|
+
cacheReadTokens: stats.tokens.cacheRead,
|
|
1155
|
+
cacheWriteTokens: stats.tokens.cacheWrite,
|
|
1156
|
+
durationMs: Date.now() - this.sessionStartedAt,
|
|
1157
|
+
numTurns: stats.userMessages + stats.assistantMessages,
|
|
1158
|
+
model: this.reportedModel(),
|
|
1159
|
+
isError: false,
|
|
1160
|
+
provider: "pi"
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
get sessionId() {
|
|
1164
|
+
return this._sessionId;
|
|
1165
|
+
}
|
|
1166
|
+
onEvent(listener) {
|
|
1167
|
+
this.listeners.push(listener);
|
|
1168
|
+
for (const event of this.eventQueue) {
|
|
1169
|
+
listener(event);
|
|
1170
|
+
}
|
|
1171
|
+
this.eventQueue = [];
|
|
1172
|
+
}
|
|
1173
|
+
async waitForCompletion() {
|
|
1174
|
+
return this.completionPromise;
|
|
1175
|
+
}
|
|
1176
|
+
async abort() {
|
|
1177
|
+
await this.agentSession.abort();
|
|
1178
|
+
}
|
|
1179
|
+
async deliverSteering({ mode, text }) {
|
|
1180
|
+
if (this.sessionEnded) {
|
|
1181
|
+
return { delivered: false, reason: "pi session already completed" };
|
|
1182
|
+
}
|
|
1183
|
+
try {
|
|
1184
|
+
if (mode === "steer")
|
|
1185
|
+
await this.agentSession.steer(text);
|
|
1186
|
+
else
|
|
1187
|
+
await this.agentSession.followUp(text);
|
|
1188
|
+
return { delivered: true, mode };
|
|
1189
|
+
} catch (err) {
|
|
1190
|
+
return { delivered: false, reason: String(err) };
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
class PiMonoAdapter {
|
|
1196
|
+
name = "pi";
|
|
1197
|
+
traits = {
|
|
1198
|
+
hasMcp: true,
|
|
1199
|
+
nativeSkillDiscovery: true,
|
|
1200
|
+
hasLocalEnvironment: true,
|
|
1201
|
+
steerModes: ["steer", "queue"]
|
|
1202
|
+
};
|
|
1203
|
+
lastCwd = ".";
|
|
1204
|
+
async createSession(config) {
|
|
1205
|
+
this.lastCwd = config.cwd;
|
|
1206
|
+
console.log(`\x1B[2m[${config.role}]\x1B[0m \x1B[35m▸\x1B[0m Spawning pi-mono for task ${config.taskId.slice(0, 8)}`);
|
|
1207
|
+
const createdSymlink = createAgentsMdSymlink(config.cwd);
|
|
1208
|
+
let customTools = [];
|
|
1209
|
+
if (config.apiUrl && config.apiKey) {
|
|
1210
|
+
try {
|
|
1211
|
+
const mcpClient = new McpHttpClient(config.apiUrl, config.apiKey, config.agentId, config.taskId);
|
|
1212
|
+
const runtimeInstanceId = swarmRuntimeInstanceId();
|
|
1213
|
+
if (runtimeInstanceId) {
|
|
1214
|
+
mcpClient.customHeaders["X-Runtime-Instance-ID"] = runtimeInstanceId;
|
|
1215
|
+
}
|
|
1216
|
+
await mcpClient.initialize();
|
|
1217
|
+
const tools = await mcpClient.listTools();
|
|
1218
|
+
customTools = mcpToolsToDefinitions(mcpClient, tools);
|
|
1219
|
+
console.log(`\x1B[2m[${config.role}]\x1B[0m Discovered ${tools.length} MCP tools from swarm`);
|
|
1220
|
+
} catch (err) {
|
|
1221
|
+
console.warn(`\x1B[33m[${config.role}] Failed to discover MCP tools: ${err}\x1B[0m`);
|
|
1222
|
+
}
|
|
1223
|
+
try {
|
|
1224
|
+
const mcpServersRes = await fetch(`${config.apiUrl}/api/agents/${config.agentId}/mcp-servers?resolveSecrets=true`, {
|
|
1225
|
+
headers: {
|
|
1226
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
1227
|
+
"X-Agent-ID": config.agentId
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
if (mcpServersRes.ok) {
|
|
1231
|
+
const mcpServersData = await mcpServersRes.json();
|
|
1232
|
+
const httpServers = mcpServersData.servers.filter((s) => s.isActive && s.isEnabled && (s.transport === "http" || s.transport === "sse") && s.url);
|
|
1233
|
+
for (const srv of httpServers) {
|
|
1234
|
+
try {
|
|
1235
|
+
const srvClient = new McpHttpClient(srv.url, "", "");
|
|
1236
|
+
srvClient.useRawUrl = true;
|
|
1237
|
+
let parsedHeaders = {};
|
|
1238
|
+
try {
|
|
1239
|
+
parsedHeaders = srv.headers ? JSON.parse(srv.headers) : {};
|
|
1240
|
+
} catch {}
|
|
1241
|
+
srvClient.customHeaders = {
|
|
1242
|
+
...parsedHeaders,
|
|
1243
|
+
...srv.resolvedHeaders || {}
|
|
1244
|
+
};
|
|
1245
|
+
await srvClient.initialize();
|
|
1246
|
+
const srvTools = await srvClient.listTools();
|
|
1247
|
+
const prefixed = mcpToolsToDefinitions(srvClient, srvTools).map((t) => ({
|
|
1248
|
+
...t,
|
|
1249
|
+
name: `mcp__${srv.name}__${t.name}`
|
|
1250
|
+
}));
|
|
1251
|
+
customTools.push(...prefixed);
|
|
1252
|
+
console.log(`\x1B[2m[${config.role}]\x1B[0m Discovered ${srvTools.length} tools from MCP server "${srv.name}"`);
|
|
1253
|
+
} catch (srvErr) {
|
|
1254
|
+
console.warn(`\x1B[33m[${config.role}] Failed to discover tools from MCP server "${srv.name}": ${srvErr}\x1B[0m`);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
} catch {}
|
|
1259
|
+
}
|
|
1260
|
+
const sessionEnv = config.env ?? process.env;
|
|
1261
|
+
await ensureOpenRouterModelsOverride(getAgentDir(), sessionEnv);
|
|
1262
|
+
const builtinModel = resolveModel(config.model, sessionEnv);
|
|
1263
|
+
const modelRuntime = await createPiRuntimeAuth(sessionEnv);
|
|
1264
|
+
const model = builtinModel ? modelRuntime.getModel(builtinModel.provider, builtinModel.id) ?? builtinModel : builtinModel;
|
|
1265
|
+
const swarmExtension = createSwarmHooksExtension({
|
|
1266
|
+
apiUrl: config.apiUrl,
|
|
1267
|
+
apiKey: config.apiKey,
|
|
1268
|
+
agentId: config.agentId,
|
|
1269
|
+
taskId: config.taskId,
|
|
1270
|
+
isLead: config.role === "lead",
|
|
1271
|
+
env: sessionEnv
|
|
1272
|
+
});
|
|
1273
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
1274
|
+
cwd: config.cwd,
|
|
1275
|
+
agentDir: getAgentDir(),
|
|
1276
|
+
appendSystemPrompt: config.systemPrompt ? [config.systemPrompt] : undefined,
|
|
1277
|
+
extensionFactories: [swarmExtension]
|
|
1278
|
+
});
|
|
1279
|
+
const reasoningApplication = applyReasoningEffort("pi", config.model, config.reasoningEffort);
|
|
1280
|
+
const reasoningSessionOptions = reasoningApplication.kind === "pi-session" ? reasoningApplication.sessionOptions : {};
|
|
1281
|
+
const appliedReasoningEffort = reasoningApplication.kind === "pi-session" ? config.reasoningEffort ?? null : null;
|
|
1282
|
+
const sessionOptions = {
|
|
1283
|
+
cwd: config.cwd,
|
|
1284
|
+
model,
|
|
1285
|
+
customTools,
|
|
1286
|
+
resourceLoader,
|
|
1287
|
+
modelRuntime,
|
|
1288
|
+
...reasoningSessionOptions
|
|
1289
|
+
};
|
|
1290
|
+
const { session } = await createAgentSession(sessionOptions);
|
|
1291
|
+
return new PiMonoSession(session, config, createdSymlink, appliedReasoningEffort);
|
|
1292
|
+
}
|
|
1293
|
+
async canResume(sessionId) {
|
|
1294
|
+
try {
|
|
1295
|
+
const sessionManager = SessionManager.create(this.lastCwd);
|
|
1296
|
+
const sessions = await sessionManager.list?.();
|
|
1297
|
+
return sessions?.some((s) => s.id === sessionId) ?? false;
|
|
1298
|
+
} catch {
|
|
1299
|
+
return false;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
formatCommand(commandName) {
|
|
1303
|
+
return `/skill:${commandName}`;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
export {
|
|
1307
|
+
PiMonoAdapter,
|
|
1308
|
+
PiMonoSession,
|
|
1309
|
+
checkPiMonoCredentials,
|
|
1310
|
+
createPiRuntimeAuth,
|
|
1311
|
+
ensureOpenRouterModelsOverride,
|
|
1312
|
+
extractPiAssistantText,
|
|
1313
|
+
mcpToolsToDefinitions,
|
|
1314
|
+
resolveModel,
|
|
1315
|
+
runBedrockSdkProbeAndEnumerate
|
|
1316
|
+
};
|