@desplega.ai/agent-swarm 2.7.4 → 2.7.5
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 +1 -1
- package/dist/acp-adapter-g3ads55s.js +15 -0
- package/dist/{actions-gfrr9vb7.js → actions-zwfykqqk.js} +18 -18
- package/dist/{amp-adapter-0s58gm46.js → amp-adapter-z3s0ssdt.js} +3 -3
- package/dist/{app-stvq692g.js → app-vmp4fgep.js} +3 -3
- package/dist/{assistant-5s37fr9n.js → assistant-vwde653f.js} +28 -21
- package/dist/{boot-reembed-yzy1841n.js → boot-reembed-375t7tbk.js} +1 -1
- package/dist/{boot-reembed-cv82gz7y.js → boot-reembed-gq9rcpnj.js} +1 -1
- package/dist/{boot-scrub-sweep-8k4q95ap.js → boot-scrub-sweep-nd938kyf.js} +4 -4
- package/dist/{boot-seeds-ftt9htah.js → boot-seeds-aykx51v3.js} +4 -4
- package/dist/{boxd-515v1t8z.js → boxd-k1j26g3h.js} +1 -1
- package/dist/{claude-adapter-t00wrzeh.js → claude-adapter-8j4hm099.js} +7 -7
- package/dist/{claude-managed-adapter-g6vmjccb.js → claude-managed-adapter-dvs2xw5n.js} +2 -2
- package/dist/{claude-managed-setup-afpa82f7.js → claude-managed-setup-0wyynpn1.js} +3 -3
- package/dist/{claude-sdk-session-vj0c2azx.js → claude-sdk-session-q6wj5hmy.js} +9 -9
- package/dist/{cli-wndv9p7g.js → cli-0731hvbw.js} +6 -6
- package/dist/{cli-d2pzqej1.js → cli-0bxqnvq9.js} +719 -7
- package/dist/{cli-jkt52mgt.js → cli-0gey5fzy.js} +2 -2
- package/dist/{cli-1yekbyqm.js → cli-1338796d.js} +205 -48
- package/dist/{cli-1s1dqn1j.js → cli-1ayk1eas.js} +171 -123
- package/dist/{cli-a4btqqft.js → cli-1b7btd38.js} +1 -1
- package/dist/{cli-a2jw6ee5.js → cli-1p89xny0.js} +1 -1
- package/dist/{cli-v726afdm.js → cli-2pnswgh8.js} +1 -1
- package/dist/{cli-kvtc7aez.js → cli-3h4nhjn8.js} +42 -14
- package/dist/{cli-655xm9ms.js → cli-583frbyr.js} +1 -1
- package/dist/{cli-4r12t6w6.js → cli-5c4hjzne.js} +101 -4
- package/dist/{cli-az0bpf8p.js → cli-5pag6rmn.js} +3 -3
- package/dist/{cli-36g23z21.js → cli-6s7vdcta.js} +4 -4
- package/dist/{cli-rtk9g7av.js → cli-73jzwksr.js} +6 -2
- package/dist/{cli-jg0nfda6.js → cli-75gkpjvw.js} +52 -21
- package/dist/{cli-a81qevyg.js → cli-7a42nfy0.js} +20 -36
- package/dist/{cli-0sbkss7h.js → cli-7wcyk2s5.js} +2 -2
- package/dist/{cli-41z8xwj0.js → cli-7wspm5gm.js} +14 -4
- package/dist/{cli-byqfbnjw.js → cli-82ae98pz.js} +9 -9
- package/dist/{cli-bq2pcphn.js → cli-8hs41zy2.js} +1 -1
- package/dist/{cli-vqgnyh76.js → cli-98rw5dt5.js} +15 -3
- package/dist/{cli-9cctx1tn.js → cli-a22h291q.js} +1 -1
- package/dist/{cli-j4kt1mpb.js → cli-a47xy8eq.js} +3 -3
- package/dist/{cli-ycm6180s.js → cli-a7v854rf.js} +5 -3
- package/dist/{cli-98pke4vk.js → cli-ae7mrdf9.js} +2 -2
- package/dist/{cli-ahd90bwt.js → cli-afrrppsw.js} +6 -6
- package/dist/{cli-8yy525ad.js → cli-bhjt02p8.js} +2 -2
- package/dist/{cli-v1yw3wha.js → cli-bnz7a7qf.js} +4 -4
- package/dist/{cli-j4kw3rsx.js → cli-cm5yqn35.js} +18 -0
- package/dist/{cli-h55y2bjc.js → cli-dhar9qay.js} +2 -2
- package/dist/{cli-5gr1g711.js → cli-fqzx7gwa.js} +2 -2
- package/dist/{cli-tzrpv5sc.js → cli-ftyxyspd.js} +1 -1
- package/dist/cli-fw2w0kva.js +47 -0
- package/dist/{cli-2dfbv8qr.js → cli-fy6x31ps.js} +1 -0
- package/dist/{cli-mvckssyq.js → cli-g61qxmge.js} +5 -5
- package/dist/{cli-aq58fk5m.js → cli-g83s9ewh.js} +7 -1
- package/dist/{cli-224epwwf.js → cli-hn9hxmz9.js} +3 -3
- package/dist/{cli-5hjced2v.js → cli-j3jvnpe8.js} +6 -6
- package/dist/{cli-dae4sbgy.js → cli-jm2vfpfd.js} +2 -2
- package/dist/{cli-zn15c46z.js → cli-mjzynrse.js} +1 -1
- package/dist/{cli-a1fhj4n9.js → cli-mmwm7zbf.js} +2 -2
- package/dist/{cli-cq1pf336.js → cli-mry0qzym.js} +5 -5
- package/dist/{cli-bwty35gx.js → cli-mthwvjre.js} +1 -1
- package/dist/{cli-s0exq74r.js → cli-nmbmy64z.js} +3 -3
- package/dist/{cli-wcmnan2g.js → cli-nn4d8dne.js} +5 -5
- package/dist/{cli-jg4p5t36.js → cli-nzxr556s.js} +121 -51
- package/dist/{cli-bcz074zx.js → cli-p599f1qm.js} +1 -1
- package/dist/{cli-k76tgq03.js → cli-pywf3eh5.js} +2 -2
- package/dist/{cli-wk9md5px.js → cli-qxnf8z2z.js} +2 -2
- package/dist/{cli-je8yeyc5.js → cli-rhw40jgb.js} +5 -5
- package/dist/{cli-avgcz4ra.js → cli-rm6hqj3z.js} +406 -35
- package/dist/{cli-0fgr1w9b.js → cli-rpm2sgv0.js} +2 -2
- package/dist/{cli-t282jgab.js → cli-rqkxrmgw.js} +12 -12
- package/dist/{cli-2qpvmmkw.js → cli-s0qnd4wv.js} +1 -1
- package/dist/{cli-pwyevs29.js → cli-spknvxbq.js} +22 -21
- package/dist/{cli-9y354g3j.js → cli-srye3vdt.js} +1 -1
- package/dist/{cli-pe09p6dv.js → cli-tqgg40ek.js} +1 -1
- package/dist/{cli-f255yaqn.js → cli-vbyfwh92.js} +3 -3
- package/dist/{cli-gcbdjt47.js → cli-vfym7wmz.js} +6 -6
- package/dist/{cli-edmm9d9y.js → cli-vqypxzh4.js} +7 -7
- package/dist/{cli-1gm8jcyr.js → cli-wvaawacd.js} +13 -13
- package/dist/{cli-eny54typ.js → cli-wx3rhnm0.js} +1 -1
- package/dist/{cli-b1afgtj3.js → cli-xbkdm710.js} +3 -3
- package/dist/{cli-mc97xy2s.js → cli-xtjhfmpc.js} +16 -4
- package/dist/{cli-eqx6k993.js → cli-y24sc29k.js} +25 -13
- package/dist/{cli-ktmvb20s.js → cli-z6jkx23q.js} +12 -12
- package/dist/{cli-7n5sbw0z.js → cli-z8eh3ex8.js} +5 -5
- package/dist/cli-zh3fp8v2.js +151 -0
- package/dist/{cli-13jvmew3.js → cli-zppwg9p9.js} +7 -7
- package/dist/cli.js +18 -18
- package/dist/{codex-adapter-g0gctw29.js → codex-adapter-q6k3gnyg.js} +5 -5
- package/dist/{codex-hook-vrzz2xhr.js → codex-hook-9efm2x1m.js} +1 -1
- package/dist/{codex-login-0gg46ygn.js → codex-login-y0pmff1c.js} +1 -1
- package/dist/{codex-session-runner-dr2cxe1c.js → codex-session-runner-3mwmk3d9.js} +6 -6
- package/dist/{commands-1p07cvff.js → commands-evrenjn9.js} +2 -2
- package/dist/{controller-z0s7sv6r.js → controller-xjp8vswh.js} +4 -4
- package/dist/{controllers-connect-x9kvvhyk.js → controllers-connect-pp5jnxvc.js} +20 -20
- package/dist/{cursor-adapter-m5b98pvk.js → cursor-adapter-yf47w4t0.js} +2 -2
- package/dist/{db-rsh04kh3.js → db-rgfsh91f.js} +2 -2
- package/dist/{db-capk2raj.js → db-zxpw6aht.js} +3 -3
- package/dist/{devin-adapter-yxm87fe9.js → devin-adapter-r9phbnc9.js} +1 -1
- package/dist/{dispatcher-304q6fwa.js → dispatcher-smasgws7.js} +17 -17
- package/dist/{docker-7rxt0rry.js → docker-rrjhaqt9.js} +2 -2
- package/dist/{dsh-adapter-btewem0y.js → dsh-adapter-ftyy8dnp.js} +2 -2
- package/dist/{e2b-renc0k8q.js → e2b-qcaj675y.js} +2 -2
- package/dist/{e2b-7fc9b9bd.js → e2b-w1p0bnpm.js} +2 -2
- package/dist/{easter-egg-3d-fm1p3n4d.js → easter-egg-3d-yyts7p16.js} +1 -1
- package/dist/{embedding-space-s68v49ct.js → embedding-space-1z0mbg95.js} +6 -6
- package/dist/{embeddings-4ms3604w.js → embeddings-ygeg7tm7.js} +1 -1
- package/dist/{event-bus-dfjx2qdh.js → event-bus-g0ka3pbw.js} +1 -1
- package/dist/{execute-mcdgccjs.js → execute-14qhqp4d.js} +1 -1
- package/dist/{external-channel-9dxwfztn.js → external-channel-ttgrs3sx.js} +2 -2
- package/dist/fx-adapter-gt6pncj2.js +18 -0
- package/dist/{grok-adapter-05ecv4h0.js → grok-adapter-sm4m5k4g.js} +8 -8
- package/dist/handlers-vwmba1m2.js +31 -0
- package/dist/{heartbeat-3n94heh3.js → heartbeat-qahz4p62.js} +20 -20
- package/dist/{hook-zxwje86c.js → hook-4fd5wq5w.js} +4 -4
- package/dist/{hook-aadrn69x.js → hook-kj3d398m.js} +6 -6
- package/dist/{http-xzxk9mst.js → http-1ted0j1k.js} +100 -64
- package/dist/index-5r71rf98.js +32 -0
- package/dist/{index-d34f6r87.js → index-6rq3r5zb.js} +1 -1
- package/dist/{index-9j4zwcht.js → index-7v51awbn.js} +158 -672
- package/dist/index-anmwxs87.js +29 -0
- package/dist/{index-qeb82z37.js → index-q4b917zs.js} +1 -1
- package/dist/index-spp0ra5c.js +36 -0
- package/dist/{keepalive-w4aakbyk.js → keepalive-6cf311t5.js} +4 -4
- package/dist/lead-fjy418cw.js +69 -0
- package/dist/{maintenance-hbptpk6p.js → maintenance-a4bm1bmt.js} +1 -1
- package/dist/{oauth-refresh-sweep-217semrv.js → oauth-refresh-sweep-3h0dfw1k.js} +3 -3
- package/dist/{onboard-vz2s5df3.js → onboard-myn5hraa.js} +3 -3
- package/dist/{opencode-adapter-gtfd7d7v.js → opencode-adapter-p2nkb5am.js} +5 -5
- package/dist/{otel-impl-aefave37.js → otel-impl-a1a0ef6b.js} +2 -2
- package/dist/{pi-mono-adapter-f8h5yseh.js → pi-mono-adapter-rzmpb72a.js} +117 -23
- package/dist/{pricing-refresh-m1yhh9ya.js → pricing-refresh-83drrnxj.js} +3 -3
- package/dist/{rbac-roles-svj72cdf.js → rbac-roles-0fb6e7pj.js} +2 -2
- package/dist/{rbac-roles-rrw8twdz.js → rbac-roles-s8y6ecb5.js} +1 -1
- package/dist/{render-v2-amwbnfxh.js → render-v2-twmbgrz4.js} +3 -3
- package/dist/{resume-9cd6wf6z.js → resume-zfqc36xj.js} +7 -7
- package/dist/{runtime-fp4vrx7m.js → runtime-qwggdpqr.js} +1 -1
- package/dist/{session-access-21t6yjyg.js → session-access-sc9pn9da.js} +4 -4
- package/dist/{session-source-backfill-pxbzcsc6.js → session-source-backfill-qh07xr8g.js} +3 -3
- package/dist/{setup-4592tqz4.js → setup-4fr0m7a1.js} +1 -1
- package/dist/{virtual-modules-gzfnpmnz.js → virtual-modules-fh4ha15b.js} +2 -2
- package/dist/{work-objects-t9tcf174.js → work-objects-0wr8zxct.js} +20 -20
- package/dist/worker-0c5tyzqe.js +69 -0
- package/dist/{worker-follow-up-q32jh4yk.js → worker-follow-up-ktyyazyp.js} +17 -17
- package/dist/{x-b6wxntdt.js → x-vmmye1kb.js} +2 -2
- package/openapi.json +97 -32
- package/package.json +1 -1
- package/src/be/db/session-summaries.ts +8 -2
- package/src/be/db/session-tree.ts +174 -61
- package/src/be/db/session-visibility.ts +5 -4
- package/src/be/db.ts +8 -4
- package/src/be/migrations/259_session_summaries_open_tasks.sql +111 -0
- package/src/be/model-validation.ts +5 -1
- package/src/be/pricing-normalize.ts +7 -2
- package/src/be/scripts/typecheck.ts +1 -0
- package/src/be/seed/schedules-seeder.ts +135 -9
- package/src/be/seed/workflows-seeder.ts +65 -1
- package/src/be/seed-pricing.ts +23 -0
- package/src/be/steering.ts +50 -3
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/commands/provider-credentials.ts +29 -2
- package/src/commands/runner.ts +86 -5
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +27 -1
- package/src/extensions/dispatcher.ts +4 -0
- package/src/http/agents.ts +1 -0
- package/src/http/mcp-bridge.ts +4 -0
- package/src/integrations/chat-sdk/inbound.ts +15 -5
- package/src/integrations/chat-sdk/steering.ts +3 -1
- package/src/kv-reserved-namespaces.ts +2 -0
- package/src/prompts/base-prompt.ts +4 -1
- package/src/prompts/session-templates.ts +26 -0
- package/src/providers/acp-adapter.ts +190 -26
- package/src/providers/acp-targets.ts +306 -6
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +9 -44
- package/src/providers/codex-oauth/flow.ts +24 -3
- package/src/providers/codex-oauth/storage.ts +56 -17
- package/src/providers/codex-oauth/subscription.ts +67 -0
- package/src/providers/fx-adapter.ts +211 -0
- package/src/providers/index.ts +5 -1
- package/src/providers/mcp-loopback-proxy.ts +94 -0
- package/src/providers/pi-mono-adapter.ts +98 -13
- package/src/providers/pricing-sources.md +8 -0
- package/src/providers/reasoning-effort.ts +17 -1
- package/src/providers/types.ts +2 -1
- package/src/scheduler/scheduler.ts +36 -3
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +5 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +33 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +5 -0
- package/src/server.ts +7 -0
- package/src/slack/assistant.ts +17 -1
- package/src/slack/handlers.ts +18 -5
- package/src/slack/left-threads.ts +65 -0
- package/src/slack/steering.ts +3 -0
- package/src/slack/thread-buffer.ts +40 -16
- package/src/tests/acp-adapter.test.ts +79 -0
- package/src/tests/base-prompt.test.ts +12 -0
- package/src/tests/chat-sdk-inbound.test.ts +30 -0
- package/src/tests/chatgpt-subscription-model-groups.test.ts +62 -0
- package/src/tests/chatgpt-subscription-pi.test.ts +170 -0
- package/src/tests/chatgpt-subscription-session-bench.test.ts +198 -0
- package/src/tests/codex-oauth-credential-info.test.ts +40 -0
- package/src/tests/codex-oauth-refresh-egress.test.ts +194 -0
- package/src/tests/context-window.test.ts +7 -0
- package/src/tests/explicit-model-validation.test.ts +99 -0
- package/src/tests/extensions-run-context.test.ts +11 -0
- package/src/tests/extensions-steering.test.ts +353 -0
- package/src/tests/extensions-validate.test.ts +5 -0
- package/src/tests/fixtures/extensions/steering-policy/hooks.ts +39 -0
- package/src/tests/fixtures/extensions/steering-policy/manifest.json +7 -0
- package/src/tests/fixtures/fake-fx.ts +167 -0
- package/src/tests/fixtures/fx/acp-responses-recorded.json +157 -0
- package/src/tests/fixtures/fx/acp-updates-recorded.jsonl +15 -0
- package/src/tests/fx-adapter.test.ts +771 -0
- package/src/tests/grok-adapter.test.ts +36 -0
- package/src/tests/mcp-loopback-proxy.test.ts +193 -0
- package/src/tests/model-tier-resolution.test.ts +3 -3
- package/src/tests/pricing-refresh.test.ts +5 -5
- package/src/tests/prompt-template-session.test.ts +5 -3
- package/src/tests/provider-registration.test.ts +2 -1
- package/src/tests/query-audit-plans.test.ts +3 -2
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/seed-model-profiles.test.ts +2 -2
- package/src/tests/seed-schedules.test.ts +138 -0
- package/src/tests/session-cost-recompute-unit.test.ts +39 -0
- package/src/tests/session-costs-golden.test.ts +26 -0
- package/src/tests/session-summaries-plans.test.ts +120 -0
- package/src/tests/session-summaries.test.ts +72 -12
- package/src/tests/sessions-tree.test.ts +11 -3
- package/src/tests/slack-leave-thread.test.ts +431 -0
- package/src/tests/tool-annotations.test.ts +2 -1
- package/src/tests/workflow-engine-v2.test.ts +42 -0
- package/src/tests/workflow-script-principal.test.ts +207 -7
- package/src/tools/slack-leave-thread.ts +104 -0
- package/src/tools/steer-task.ts +3 -2
- package/src/tools/tool-config.ts +2 -1
- package/src/types.ts +19 -0
- package/src/utils/codex-auth-failure.ts +30 -0
- package/src/utils/context-window.ts +4 -1
- package/src/utils/credentials.ts +7 -0
- package/src/utils/fx-models.ts +43 -0
- package/src/workflows/cooldown.ts +3 -2
- package/src/workflows/engine.ts +24 -9
- package/templates/schedules/dreaming-daily/config.json +1 -1
- package/templates/schedules/dreaming-daily/content.md +6 -14
- package/templates/skills/swarm-docs/content.md +1 -0
- package/templates/workflows/dreaming/content.md +5 -1
- package/dist/acp-adapter-96dc64m2.js +0 -15
- package/dist/handlers-jg7v74ty.js +0 -31
- package/dist/index-8c1zbz59.js +0 -29
- package/dist/index-8k8tmfxd.js +0 -32
- package/dist/index-hq617xdj.js +0 -36
- package/dist/lead-567kw7x3.js +0 -68
- package/dist/worker-fjdqra6q.js +0 -68
|
@@ -4,14 +4,18 @@
|
|
|
4
4
|
import {
|
|
5
5
|
client as acpClient,
|
|
6
6
|
type ClientContext,
|
|
7
|
+
type ContentBlock,
|
|
8
|
+
type InitializeResponse,
|
|
7
9
|
type McpServer,
|
|
8
10
|
methods,
|
|
11
|
+
type NewSessionRequest,
|
|
9
12
|
ndJsonStream,
|
|
10
13
|
PROTOCOL_VERSION,
|
|
11
14
|
type RequestPermissionRequest,
|
|
12
15
|
type RequestPermissionResponse,
|
|
13
16
|
type SessionConfigOption,
|
|
14
17
|
type SessionNotification,
|
|
18
|
+
type SessionUpdate,
|
|
15
19
|
type SetSessionConfigOptionRequest,
|
|
16
20
|
type SetSessionConfigOptionResponse,
|
|
17
21
|
type Usage,
|
|
@@ -28,6 +32,11 @@ import {
|
|
|
28
32
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
29
33
|
import { acpReportedCostUsd, translateAcpSessionNotification } from "./acp-swarm-events";
|
|
30
34
|
import { type AcpTargetProfile, resolveAcpTarget } from "./acp-targets";
|
|
35
|
+
import {
|
|
36
|
+
type McpLoopbackProxy,
|
|
37
|
+
needsLoopbackProxy,
|
|
38
|
+
startMcpLoopbackProxy,
|
|
39
|
+
} from "./mcp-loopback-proxy";
|
|
31
40
|
import type {
|
|
32
41
|
CostData,
|
|
33
42
|
ProviderAdapter,
|
|
@@ -36,11 +45,13 @@ import type {
|
|
|
36
45
|
ProviderSession,
|
|
37
46
|
ProviderSessionConfig,
|
|
38
47
|
ProviderTraits,
|
|
48
|
+
SteerDelivery,
|
|
49
|
+
SteerDeliveryResult,
|
|
39
50
|
} from "./types";
|
|
40
51
|
|
|
41
52
|
type EventListener = (event: ProviderEvent) => void;
|
|
42
53
|
/** Providers that run on this ACP client. */
|
|
43
|
-
export type AcpProviderName = "acp" | "grok";
|
|
54
|
+
export type AcpProviderName = "acp" | "grok" | "fx";
|
|
44
55
|
const ACP_LOG_MAX_CHARS = 30_000;
|
|
45
56
|
/**
|
|
46
57
|
* How long an abort waits after `session/cancel` for the prompt to answer
|
|
@@ -163,6 +174,7 @@ function boundedAcpLogScalar(value: unknown): unknown {
|
|
|
163
174
|
class SwarmAcpClient {
|
|
164
175
|
/** Latest cumulative USD cost the target reported for the session. */
|
|
165
176
|
reportedCostUsd: number | null = null;
|
|
177
|
+
private readonly updateListeners = new Set<(update: SessionUpdate) => void>();
|
|
166
178
|
|
|
167
179
|
constructor(
|
|
168
180
|
private readonly emit: (event: ProviderEvent) => void,
|
|
@@ -183,11 +195,29 @@ class SwarmAcpClient {
|
|
|
183
195
|
const costUsd = acpReportedCostUsd(params.update);
|
|
184
196
|
if (costUsd !== null) this.reportedCostUsd = costUsd;
|
|
185
197
|
for (const event of translateAcpSessionNotification(params)) {
|
|
186
|
-
this.emit(this.target?.rewriteEvent?.(event) ?? event);
|
|
198
|
+
this.emit(this.target?.rewriteEvent?.(event, params.update) ?? event);
|
|
187
199
|
}
|
|
200
|
+
for (const listener of this.updateListeners) listener(params.update);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Observe raw session updates (a target's steer hook); returns the unsubscribe. */
|
|
204
|
+
onUpdate(listener: (update: SessionUpdate) => void): () => void {
|
|
205
|
+
this.updateListeners.add(listener);
|
|
206
|
+
return () => {
|
|
207
|
+
this.updateListeners.delete(listener);
|
|
208
|
+
};
|
|
188
209
|
}
|
|
189
210
|
}
|
|
190
211
|
|
|
212
|
+
/** What `ACPSession` needs beyond the connection, for the opt-in target hooks. */
|
|
213
|
+
interface AcpSessionExtras {
|
|
214
|
+
initResult: InitializeResponse;
|
|
215
|
+
/** `harnessVariantMeta` on `session_init` (the agent's `agentInfo.version`). */
|
|
216
|
+
harnessVariantMeta?: Record<string, unknown>;
|
|
217
|
+
/** Loopback MCP forwarders this session owns; stopped when it finishes. */
|
|
218
|
+
proxies: McpLoopbackProxy[];
|
|
219
|
+
}
|
|
220
|
+
|
|
191
221
|
class ACPSession implements ProviderSession {
|
|
192
222
|
readonly sessionId: string;
|
|
193
223
|
|
|
@@ -203,6 +233,8 @@ class ACPSession implements ProviderSession {
|
|
|
203
233
|
* the fallback operator key is in use (e.g. during tests or if the API is
|
|
204
234
|
* not yet upgraded). Revoked in finish(). */
|
|
205
235
|
private ephemeralTokenId: string | null = null;
|
|
236
|
+
/** Present only when the target has a `steer` hook. */
|
|
237
|
+
readonly deliverSteering?: (delivery: SteerDelivery) => Promise<SteerDeliveryResult>;
|
|
206
238
|
|
|
207
239
|
constructor(
|
|
208
240
|
private readonly agent: ClientContext,
|
|
@@ -214,14 +246,37 @@ class ACPSession implements ProviderSession {
|
|
|
214
246
|
private readonly target: AcpTargetProfile,
|
|
215
247
|
providerMeta?: Record<string, unknown>,
|
|
216
248
|
ephemeralTokenId?: string,
|
|
249
|
+
private readonly extras?: AcpSessionExtras,
|
|
217
250
|
) {
|
|
218
251
|
this.sessionId = sessionId;
|
|
219
252
|
this.ephemeralTokenId = ephemeralTokenId ?? null;
|
|
220
253
|
this.completionPromise = new Promise((resolve) => {
|
|
221
254
|
this.completionResolve = resolve;
|
|
222
255
|
});
|
|
256
|
+
const steer = target.steer;
|
|
257
|
+
if (steer && extras) {
|
|
258
|
+
const initResult = extras.initResult;
|
|
259
|
+
this.deliverSteering = async (delivery) => {
|
|
260
|
+
if (this.completed || this.aborted) {
|
|
261
|
+
return { delivered: false, reason: "The ACP session has already finished" };
|
|
262
|
+
}
|
|
263
|
+
return steer({
|
|
264
|
+
sessionId,
|
|
265
|
+
delivery,
|
|
266
|
+
initResult,
|
|
267
|
+
prompt: (params) => this.agent.request(methods.agent.session.prompt, params),
|
|
268
|
+
onUpdate: (listener) => this.client.onUpdate(listener),
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
}
|
|
223
272
|
void this.consumeStderr();
|
|
224
|
-
this.emit({
|
|
273
|
+
this.emit({
|
|
274
|
+
type: "session_init",
|
|
275
|
+
sessionId,
|
|
276
|
+
provider: providerName,
|
|
277
|
+
providerMeta,
|
|
278
|
+
...(extras?.harnessVariantMeta ? { harnessVariantMeta: extras.harnessVariantMeta } : {}),
|
|
279
|
+
});
|
|
225
280
|
void this.runPrompt();
|
|
226
281
|
}
|
|
227
282
|
|
|
@@ -385,6 +440,7 @@ class ACPSession implements ProviderSession {
|
|
|
385
440
|
if (this.completed) return;
|
|
386
441
|
this.completed = true;
|
|
387
442
|
this.completionResolve(result);
|
|
443
|
+
void stopProxies(this.extras?.proxies ?? []);
|
|
388
444
|
// Revoke the ephemeral token now that the session is done. Best-effort:
|
|
389
445
|
// the token expires on its own, so a failure here is not critical.
|
|
390
446
|
if (this.ephemeralTokenId) {
|
|
@@ -455,8 +511,9 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
455
511
|
.connect(stream).agent;
|
|
456
512
|
|
|
457
513
|
let ephemeralToken: { tokenId: string; plaintext: string } | null = null;
|
|
514
|
+
const proxies: McpLoopbackProxy[] = [];
|
|
458
515
|
try {
|
|
459
|
-
await agent.request(methods.agent.initialize, {
|
|
516
|
+
const initResult = await agent.request(methods.agent.initialize, {
|
|
460
517
|
protocolVersion: PROTOCOL_VERSION,
|
|
461
518
|
clientInfo: { name: "agent-swarm", version: pkg.version },
|
|
462
519
|
clientCapabilities: { session: { configOptions: { boolean: {} } } },
|
|
@@ -483,39 +540,76 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
483
540
|
// (mcp/connect, mcp/message, mcp/disconnect) instead of a network hop -- the
|
|
484
541
|
// shape for an ACP agent with no network route to the swarm API. Gated on
|
|
485
542
|
// `mcpCapabilities.acp` and UNSTABLE; not adopted here.
|
|
543
|
+
let mcpServers: McpServer[] = [
|
|
544
|
+
{
|
|
545
|
+
type: "http",
|
|
546
|
+
name: "swarm",
|
|
547
|
+
url: `${config.apiUrl.replace(/\/+$/, "")}/mcp`,
|
|
548
|
+
headers: [
|
|
549
|
+
{ name: "Authorization", value: `Bearer ${ephemeralToken.plaintext}` },
|
|
550
|
+
{ name: "X-Agent-ID", value: config.agentId },
|
|
551
|
+
{ name: "X-Source-Task-Id", value: config.taskId },
|
|
552
|
+
],
|
|
553
|
+
},
|
|
554
|
+
...toAcpMcpServers(installedServers),
|
|
555
|
+
];
|
|
556
|
+
if (target.requiresSecureMcpUrl) {
|
|
557
|
+
mcpServers = await secureMcpServers(
|
|
558
|
+
mcpServers,
|
|
559
|
+
initResult.agentCapabilities?.mcpCapabilities?.sse === true,
|
|
560
|
+
proxies,
|
|
561
|
+
);
|
|
562
|
+
}
|
|
486
563
|
const sessionMeta = target.sessionMeta?.(config);
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
564
|
+
const resumeId = target.resume ? config.resumeSessionId?.trim() : undefined;
|
|
565
|
+
let sessionId: string;
|
|
566
|
+
let advertisedOptions: SessionConfigOption[];
|
|
567
|
+
if (resumeId && initResult.agentCapabilities?.sessionCapabilities?.resume) {
|
|
568
|
+
// The target restores the session's history and system prompt itself.
|
|
569
|
+
const resumed = await agent.request(methods.agent.session.resume, {
|
|
570
|
+
...(sessionMeta ? { _meta: sessionMeta } : {}),
|
|
571
|
+
sessionId: resumeId,
|
|
572
|
+
cwd: config.cwd,
|
|
573
|
+
mcpServers,
|
|
574
|
+
});
|
|
575
|
+
sessionId = resumeId;
|
|
576
|
+
advertisedOptions = resumed.configOptions ?? [];
|
|
577
|
+
} else {
|
|
578
|
+
if (resumeId) {
|
|
579
|
+
console.warn(
|
|
580
|
+
"\x1b[33m[acp]\x1b[0m Target does not advertise session/resume; starting a new session",
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
const systemPrompt = await sessionSystemPromptBlocks(target, config, initResult);
|
|
584
|
+
// fx 0.0.15 takes `systemPrompt` as a bare content-block array (append
|
|
585
|
+
// mode) and rejects the `{ mode, content }` object the SDK types, so the
|
|
586
|
+
// request is built untyped.
|
|
587
|
+
const newSessionParams = {
|
|
588
|
+
...(sessionMeta ? { _meta: sessionMeta } : {}),
|
|
589
|
+
cwd: config.cwd,
|
|
590
|
+
mcpServers,
|
|
591
|
+
...(systemPrompt ? { systemPrompt } : {}),
|
|
592
|
+
} as NewSessionRequest;
|
|
593
|
+
const newSession = await agent.request(methods.agent.session.new, newSessionParams);
|
|
594
|
+
sessionId = newSession.sessionId;
|
|
595
|
+
advertisedOptions = newSession.configOptions ?? [];
|
|
596
|
+
}
|
|
504
597
|
const configOptions = await applyConfiguredOptions(
|
|
505
598
|
{
|
|
506
599
|
setSessionConfigOption: (params) =>
|
|
507
600
|
agent.request(methods.agent.session.setConfigOption, params),
|
|
508
601
|
},
|
|
509
|
-
|
|
510
|
-
|
|
602
|
+
sessionId,
|
|
603
|
+
advertisedOptions,
|
|
511
604
|
target.configuredOptions(config),
|
|
512
605
|
);
|
|
606
|
+
const agentVersion = initResult.agentInfo?.version?.trim();
|
|
513
607
|
session = new ACPSession(
|
|
514
608
|
agent,
|
|
515
609
|
client,
|
|
516
610
|
proc,
|
|
517
611
|
config,
|
|
518
|
-
|
|
612
|
+
sessionId,
|
|
519
613
|
this.name,
|
|
520
614
|
target,
|
|
521
615
|
{
|
|
@@ -523,6 +617,11 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
523
617
|
configOptions: sanitizeAcpConfigOptions(configOptions),
|
|
524
618
|
},
|
|
525
619
|
ephemeralToken.tokenId,
|
|
620
|
+
{
|
|
621
|
+
initResult,
|
|
622
|
+
...(agentVersion ? { harnessVariantMeta: { version: scrubSecrets(agentVersion) } } : {}),
|
|
623
|
+
proxies,
|
|
624
|
+
},
|
|
526
625
|
);
|
|
527
626
|
for (const event of preSessionEvents) session.emitFromAcp(event);
|
|
528
627
|
void session.waitForCompletion().finally(cleanupArtifact);
|
|
@@ -535,6 +634,7 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
535
634
|
void revokeAcpSessionToken(config.apiUrl, config.apiKey, ephemeralToken.tokenId);
|
|
536
635
|
}
|
|
537
636
|
await terminateProcessGroup(proc.pid);
|
|
637
|
+
await stopProxies(proxies);
|
|
538
638
|
await cleanupArtifact();
|
|
539
639
|
throw new Error(
|
|
540
640
|
`ACP target failed during startup: ${describeTargetError(target, scrubSecrets(formatError(err)))}`,
|
|
@@ -542,8 +642,9 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
542
642
|
}
|
|
543
643
|
}
|
|
544
644
|
|
|
545
|
-
async canResume(
|
|
546
|
-
|
|
645
|
+
async canResume(sessionId: string): Promise<boolean> {
|
|
646
|
+
const resume = this.options.target?.resume;
|
|
647
|
+
return resume ? resume.canResume(sessionId) : false;
|
|
547
648
|
}
|
|
548
649
|
|
|
549
650
|
formatCommand(commandName: string): string {
|
|
@@ -680,6 +781,69 @@ export function toAcpMcpServers(
|
|
|
680
781
|
return servers;
|
|
681
782
|
}
|
|
682
783
|
|
|
784
|
+
/**
|
|
785
|
+
* The `systemPrompt` content blocks a target's `sessionSystemPrompt` hook
|
|
786
|
+
* produces, when the agent advertises `sessionCapabilities.systemPrompt`.
|
|
787
|
+
*/
|
|
788
|
+
async function sessionSystemPromptBlocks(
|
|
789
|
+
target: AcpTargetProfile,
|
|
790
|
+
config: ProviderSessionConfig,
|
|
791
|
+
initResult: InitializeResponse,
|
|
792
|
+
): Promise<ContentBlock[] | undefined> {
|
|
793
|
+
if (!target.sessionSystemPrompt) return undefined;
|
|
794
|
+
if (!initResult.agentCapabilities?.sessionCapabilities?.systemPrompt) {
|
|
795
|
+
console.warn(
|
|
796
|
+
"\x1b[33m[acp]\x1b[0m Target does not advertise sessionCapabilities.systemPrompt; the swarm system prompt is not sent",
|
|
797
|
+
);
|
|
798
|
+
return undefined;
|
|
799
|
+
}
|
|
800
|
+
const plan = await target.sessionSystemPrompt(config);
|
|
801
|
+
return plan?.blocks.length ? plan.blocks : undefined;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* MCP servers a target that only accepts HTTPS or explicit-loopback URLs can
|
|
806
|
+
* reach: each plain-HTTP, non-loopback origin gets one 127.0.0.1 forwarder
|
|
807
|
+
* (added to `proxies`), and SSE servers are dropped unless `sseSupported`.
|
|
808
|
+
*/
|
|
809
|
+
export async function secureMcpServers(
|
|
810
|
+
servers: McpServer[],
|
|
811
|
+
sseSupported: boolean,
|
|
812
|
+
proxies: McpLoopbackProxy[],
|
|
813
|
+
): Promise<McpServer[]> {
|
|
814
|
+
const byOrigin = new Map<string, McpLoopbackProxy>();
|
|
815
|
+
const result: McpServer[] = [];
|
|
816
|
+
for (const server of servers) {
|
|
817
|
+
if (!("url" in server)) {
|
|
818
|
+
result.push(server);
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (server.type === "sse" && !sseSupported) {
|
|
822
|
+
console.warn(
|
|
823
|
+
`\x1b[33m[acp]\x1b[0m Skipping installed MCP server "${server.name}": the target does not support SSE MCP servers`,
|
|
824
|
+
);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (!needsLoopbackProxy(server.url)) {
|
|
828
|
+
result.push(server);
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
const url = new URL(server.url);
|
|
832
|
+
let proxy = byOrigin.get(url.origin);
|
|
833
|
+
if (!proxy) {
|
|
834
|
+
proxy = await startMcpLoopbackProxy(url.origin);
|
|
835
|
+
byOrigin.set(url.origin, proxy);
|
|
836
|
+
proxies.push(proxy);
|
|
837
|
+
}
|
|
838
|
+
result.push({ ...server, url: `${proxy.url}${url.pathname}${url.search}` });
|
|
839
|
+
}
|
|
840
|
+
return result;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
async function stopProxies(proxies: McpLoopbackProxy[]): Promise<void> {
|
|
844
|
+
await Promise.all(proxies.splice(0).map((proxy) => proxy.stop().catch(() => {})));
|
|
845
|
+
}
|
|
846
|
+
|
|
683
847
|
function describeTargetError(target: AcpTargetProfile, message: string): string {
|
|
684
848
|
return target.describeError?.(message) ?? message;
|
|
685
849
|
}
|