@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
|
@@ -29,6 +29,11 @@ describe("extension bundle validation", () => {
|
|
|
29
29
|
expect(await validateBundle(bundle)).toEqual({ ok: true, manifest: bundle.manifest });
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
test("accepts the steering event and message rewrite types", async () => {
|
|
33
|
+
const bundle = await loadBundleFixture("steering-policy");
|
|
34
|
+
expect(await validateBundle(bundle)).toEqual({ ok: true, manifest: bundle.manifest });
|
|
35
|
+
});
|
|
36
|
+
|
|
32
37
|
test("accepts zod and stdlib imports", async () => {
|
|
33
38
|
const bundle = await loadBundleFixture("minimal");
|
|
34
39
|
bundle.files["hooks.ts"] = `import { table } from "stdlib";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { block, modify, type SwarmExtension } from "swarm-extension";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export const config = z.object({
|
|
5
|
+
action: z.enum(["continue", "modify", "block", "empty", "retarget", "throw"]),
|
|
6
|
+
prefix: z.string().default("rewritten: "),
|
|
7
|
+
slackUserId: z.string().optional(),
|
|
8
|
+
});
|
|
9
|
+
const manifest = {
|
|
10
|
+
name: "steering-policy",
|
|
11
|
+
description: "Records, rewrites, or blocks steering messages",
|
|
12
|
+
version: "1.0.0",
|
|
13
|
+
runtime: "api",
|
|
14
|
+
assets: { hooks: "hooks.ts" },
|
|
15
|
+
config,
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
const extension: SwarmExtension<typeof manifest> = (api) => {
|
|
19
|
+
api.on("pre.task.steer", async (event, ctx) => {
|
|
20
|
+
await ctx.state.set("last", event);
|
|
21
|
+
if (ctx.config.slackUserId && event.slack?.userId !== ctx.config.slackUserId) return;
|
|
22
|
+
switch (ctx.config.action) {
|
|
23
|
+
case "continue":
|
|
24
|
+
return { action: "continue" };
|
|
25
|
+
case "modify":
|
|
26
|
+
return modify({ message: ctx.config.prefix + event.message });
|
|
27
|
+
case "block":
|
|
28
|
+
return block("Steering denied by policy");
|
|
29
|
+
case "empty":
|
|
30
|
+
return modify({ message: " \n " });
|
|
31
|
+
case "retarget":
|
|
32
|
+
return modify({ message: "Unauthorized rewrite", mode: "steer" });
|
|
33
|
+
case "throw":
|
|
34
|
+
throw new Error("Steering policy failed");
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default extension;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A fake `fx acp` for adapter tests. Plain newline-delimited JSON-RPC, not the
|
|
3
|
+
* ACP SDK's agent app: fx 0.0.15 takes `systemPrompt` as a bare content-block
|
|
4
|
+
* array, which the SDK's request validation rejects.
|
|
5
|
+
*
|
|
6
|
+
* It answers with the recorded fx 0.0.15 responses and replays the recorded
|
|
7
|
+
* session updates (`fixtures/fx/`). Everything it receives (argv, the fx
|
|
8
|
+
* env, `~/.fx/settings.json`, every request) goes to `capture.json` in its cwd.
|
|
9
|
+
*
|
|
10
|
+
* FAKE_FX_MODE:
|
|
11
|
+
* - `ok` (default): one turn; a steer received during it is echoed where the
|
|
12
|
+
* recording echoed one, and its request answers `steering: "absorbed"`.
|
|
13
|
+
* - `steer-ignored`: the steer request answers without `_meta.fx.steering`.
|
|
14
|
+
* - `wait-for-cancel`: starts a `sleep` child, then holds the turn until
|
|
15
|
+
* `session/cancel` and answers `cancelled` with usage.
|
|
16
|
+
* FAKE_FX_MCP_CALL=1: POSTs `tools/list` to the swarm MCP URL it was given.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
|
|
21
|
+
const fixtureDir = process.env.FAKE_FX_FIXTURE_DIR ?? "";
|
|
22
|
+
const mode = process.env.FAKE_FX_MODE ?? "ok";
|
|
23
|
+
const responses = JSON.parse(readFileSync(join(fixtureDir, "acp-responses-recorded.json"), "utf8"));
|
|
24
|
+
const updates: Array<Record<string, unknown>> = readFileSync(
|
|
25
|
+
join(fixtureDir, "acp-updates-recorded.jsonl"),
|
|
26
|
+
"utf8",
|
|
27
|
+
)
|
|
28
|
+
.trim()
|
|
29
|
+
.split("\n")
|
|
30
|
+
.map((line) => JSON.parse(line));
|
|
31
|
+
|
|
32
|
+
const settingsPath = join(process.env.HOME ?? "", ".fx", "settings.json");
|
|
33
|
+
const capture: Record<string, unknown> = {
|
|
34
|
+
argv: process.argv.slice(2),
|
|
35
|
+
env: Object.fromEntries(
|
|
36
|
+
Object.entries(process.env).filter(([key]) =>
|
|
37
|
+
/^(FX_|OPENROUTER_|OPENAI_|AI_GATEWAY_|ANTHROPIC_|XAI_|CLAUDE_)/.test(key),
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
settings: existsSync(settingsPath) ? JSON.parse(readFileSync(settingsPath, "utf8")) : null,
|
|
41
|
+
requests: [] as Array<{ method: string; params: unknown }>,
|
|
42
|
+
};
|
|
43
|
+
const save = () => writeFileSync("capture.json", JSON.stringify(capture));
|
|
44
|
+
save();
|
|
45
|
+
|
|
46
|
+
const send = (message: unknown) => process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
47
|
+
const notify = (sessionId: string, update: unknown) =>
|
|
48
|
+
send({ jsonrpc: "2.0", method: "session/update", params: { sessionId, update } });
|
|
49
|
+
|
|
50
|
+
let steerText: string | null = null;
|
|
51
|
+
let steerWaiter: (() => void) | null = null;
|
|
52
|
+
const steerReplies: Array<() => void> = [];
|
|
53
|
+
let cancelTurn: (() => void) | null = null;
|
|
54
|
+
|
|
55
|
+
async function runTurn(id: number, sessionId: string): Promise<void> {
|
|
56
|
+
if (process.env.FAKE_FX_MCP_CALL === "1") {
|
|
57
|
+
const newSession = (capture.requests as Array<{ method: string; params: any }>).find(
|
|
58
|
+
(request) => request.method === "session/new" || request.method === "session/resume",
|
|
59
|
+
);
|
|
60
|
+
const swarm = newSession?.params.mcpServers.find((server: any) => server.name === "swarm");
|
|
61
|
+
const headers = Object.fromEntries(
|
|
62
|
+
swarm.headers.map((header: { name: string; value: string }) => [header.name, header.value]),
|
|
63
|
+
);
|
|
64
|
+
const response = await fetch(swarm.url, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: { ...headers, "Content-Type": "application/json" },
|
|
67
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list" }),
|
|
68
|
+
});
|
|
69
|
+
capture.mcpCall = { url: swarm.url, status: response.status, body: await response.json() };
|
|
70
|
+
save();
|
|
71
|
+
}
|
|
72
|
+
for (const update of updates) {
|
|
73
|
+
const meta = update._meta as { fx?: { steering?: unknown } } | undefined;
|
|
74
|
+
if (update.sessionUpdate === "user_message_chunk" && meta?.fx?.steering) {
|
|
75
|
+
// The recording echoed a steer here. Wait briefly for one to arrive.
|
|
76
|
+
if (steerText === null && mode !== "wait-for-cancel") {
|
|
77
|
+
await new Promise<void>((resolve) => {
|
|
78
|
+
steerWaiter = resolve;
|
|
79
|
+
setTimeout(resolve, 3_000);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (steerText === null || mode === "steer-ignored") continue;
|
|
83
|
+
notify(sessionId, { ...update, content: { type: "text", text: steerText } });
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
notify(sessionId, update);
|
|
87
|
+
}
|
|
88
|
+
if (mode === "wait-for-cancel") {
|
|
89
|
+
const child = Bun.spawn(["sleep", "300"], { stdout: "ignore", stderr: "ignore" });
|
|
90
|
+
capture.childPid = child.pid;
|
|
91
|
+
save();
|
|
92
|
+
await new Promise<void>((resolve) => {
|
|
93
|
+
cancelTurn = resolve;
|
|
94
|
+
});
|
|
95
|
+
send({
|
|
96
|
+
jsonrpc: "2.0",
|
|
97
|
+
id,
|
|
98
|
+
result: { stopReason: "cancelled", usage: { inputTokens: 1200, outputTokens: 30 } },
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
send({ jsonrpc: "2.0", id, result: responses.prompt });
|
|
103
|
+
for (const reply of steerReplies.splice(0)) reply();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function handle(message: any): Promise<void> {
|
|
107
|
+
if (message.method === undefined) return;
|
|
108
|
+
(capture.requests as Array<{ method: string; params: unknown }>).push({
|
|
109
|
+
method: message.method,
|
|
110
|
+
params: message.params,
|
|
111
|
+
});
|
|
112
|
+
save();
|
|
113
|
+
const { id, method, params } = message;
|
|
114
|
+
switch (method) {
|
|
115
|
+
case "initialize":
|
|
116
|
+
send({ jsonrpc: "2.0", id, result: responses.initialize });
|
|
117
|
+
return;
|
|
118
|
+
case "session/new":
|
|
119
|
+
send({ jsonrpc: "2.0", id, result: responses.newSession });
|
|
120
|
+
return;
|
|
121
|
+
case "session/resume": {
|
|
122
|
+
const { sessionId: _, ...rest } = responses.newSession;
|
|
123
|
+
send({ jsonrpc: "2.0", id, result: rest });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
case "session/set_config_option":
|
|
127
|
+
case "session/set_mode":
|
|
128
|
+
send({ jsonrpc: "2.0", id, result: { configOptions: responses.newSession.configOptions } });
|
|
129
|
+
return;
|
|
130
|
+
case "session/prompt": {
|
|
131
|
+
if (params._meta?.fx?.steer) {
|
|
132
|
+
steerText = params.prompt[0].text;
|
|
133
|
+
steerWaiter?.();
|
|
134
|
+
steerReplies.push(() =>
|
|
135
|
+
send({
|
|
136
|
+
jsonrpc: "2.0",
|
|
137
|
+
id,
|
|
138
|
+
result: mode === "steer-ignored" ? { stopReason: "end_turn" } : responses.steer,
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
await runTurn(id, params.sessionId);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
case "session/cancel":
|
|
147
|
+
cancelTurn?.();
|
|
148
|
+
return;
|
|
149
|
+
default:
|
|
150
|
+
if (id !== undefined) {
|
|
151
|
+
send({ jsonrpc: "2.0", id, error: { code: -32601, message: `${method} not supported` } });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const decoder = new TextDecoder();
|
|
157
|
+
let buffer = "";
|
|
158
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
159
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
160
|
+
let newline = buffer.indexOf("\n");
|
|
161
|
+
while (newline >= 0) {
|
|
162
|
+
const line = buffer.slice(0, newline).trim();
|
|
163
|
+
buffer = buffer.slice(newline + 1);
|
|
164
|
+
if (line) void handle(JSON.parse(line));
|
|
165
|
+
newline = buffer.indexOf("\n");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"initialize": {
|
|
3
|
+
"protocolVersion": 1,
|
|
4
|
+
"agentCapabilities": {
|
|
5
|
+
"loadSession": true,
|
|
6
|
+
"promptCapabilities": {
|
|
7
|
+
"image": true,
|
|
8
|
+
"audio": false,
|
|
9
|
+
"embeddedContext": true
|
|
10
|
+
},
|
|
11
|
+
"mcpCapabilities": {
|
|
12
|
+
"http": true,
|
|
13
|
+
"sse": false,
|
|
14
|
+
"acp": false
|
|
15
|
+
},
|
|
16
|
+
"sessionCapabilities": {
|
|
17
|
+
"list": {},
|
|
18
|
+
"resume": {},
|
|
19
|
+
"close": {},
|
|
20
|
+
"systemPrompt": {}
|
|
21
|
+
},
|
|
22
|
+
"_meta": {
|
|
23
|
+
"fx": {
|
|
24
|
+
"steering": true
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"agentInfo": {
|
|
29
|
+
"name": "fx",
|
|
30
|
+
"title": "fx",
|
|
31
|
+
"version": "0.0.15"
|
|
32
|
+
},
|
|
33
|
+
"authMethods": []
|
|
34
|
+
},
|
|
35
|
+
"newSession": {
|
|
36
|
+
"sessionId": "fxSession001",
|
|
37
|
+
"configOptions": [
|
|
38
|
+
{
|
|
39
|
+
"id": "provider",
|
|
40
|
+
"name": "Provider",
|
|
41
|
+
"category": "model",
|
|
42
|
+
"type": "select",
|
|
43
|
+
"currentValue": "openrouter",
|
|
44
|
+
"options": [
|
|
45
|
+
{
|
|
46
|
+
"value": "gateway",
|
|
47
|
+
"name": "Vercel AI Gateway"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"value": "codex",
|
|
51
|
+
"name": "Codex subscription"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"value": "grok",
|
|
55
|
+
"name": "Grok subscription"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"value": "openrouter",
|
|
59
|
+
"name": "openrouter"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"value": "openai",
|
|
63
|
+
"name": "openai"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "model",
|
|
69
|
+
"name": "Model",
|
|
70
|
+
"category": "model",
|
|
71
|
+
"type": "select",
|
|
72
|
+
"currentValue": "openai/gpt-5-mini",
|
|
73
|
+
"options": [
|
|
74
|
+
{
|
|
75
|
+
"value": "openai/gpt-5-mini",
|
|
76
|
+
"name": "openai/gpt-5-mini"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "mode",
|
|
82
|
+
"name": "Session Mode",
|
|
83
|
+
"description": "Controls how the agent requests permission",
|
|
84
|
+
"category": "mode",
|
|
85
|
+
"type": "select",
|
|
86
|
+
"currentValue": "full-access",
|
|
87
|
+
"options": [
|
|
88
|
+
{
|
|
89
|
+
"value": "auto",
|
|
90
|
+
"name": "Auto",
|
|
91
|
+
"description": "Check actions with an automatic safety review instead of asking",
|
|
92
|
+
"_meta": {
|
|
93
|
+
"fx": {
|
|
94
|
+
"permissionMode": "auto"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"value": "ask",
|
|
100
|
+
"name": "Ask",
|
|
101
|
+
"description": "Request permission before making any changes",
|
|
102
|
+
"_meta": {
|
|
103
|
+
"fx": {
|
|
104
|
+
"permissionMode": "ask"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"value": "full-access",
|
|
110
|
+
"name": "Full access",
|
|
111
|
+
"description": "Run every action without asking, outside the sandbox",
|
|
112
|
+
"_meta": {
|
|
113
|
+
"fx": {
|
|
114
|
+
"permissionMode": "yolo"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"modes": {
|
|
122
|
+
"currentModeId": "full-access",
|
|
123
|
+
"availableModes": [
|
|
124
|
+
{
|
|
125
|
+
"id": "auto",
|
|
126
|
+
"name": "Auto",
|
|
127
|
+
"description": "Check actions with an automatic safety review instead of asking"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"id": "ask",
|
|
131
|
+
"name": "Ask",
|
|
132
|
+
"description": "Request permission before making any changes"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "full-access",
|
|
136
|
+
"name": "Full access",
|
|
137
|
+
"description": "Run every action without asking, outside the sandbox"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"prompt": {
|
|
143
|
+
"stopReason": "end_turn",
|
|
144
|
+
"usage": {
|
|
145
|
+
"inputTokens": 58620,
|
|
146
|
+
"outputTokens": 1183
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"steer": {
|
|
150
|
+
"stopReason": "end_turn",
|
|
151
|
+
"_meta": {
|
|
152
|
+
"fx": {
|
|
153
|
+
"steering": "absorbed"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{"sessionUpdate":"agent_message_chunk","messageId":"b86bad695b6f80234f85a6ae84770a00","content":{"type":"text","text":"[context] 114 always-loaded MCP tools exceeded the mcp_selected_schema_bytes budget and stay available through capability_search"}}
|
|
2
|
+
{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"**Following developer instructions**\n\nI need to ensure I follow the developer's instructions perfectly. First, I’ll send a brief update to the user about the goal and the next step. Then, I’ll call th…"}}
|
|
3
|
+
{"sessionUpdate":"agent_message_chunk","messageId":"e6788e35f07ecb7d4b22767ec6028538","content":{"type":"text","text":"Goal: fetch the swarm agent list with mcp_swarm_get-swarm as the next step, then run the sleep command. I'll call mcp_swarm_get-swarm now."}}
|
|
4
|
+
{"sessionUpdate":"tool_call","toolCallId":"call_kK1rqCaYaXn16uKH9e6fmb7v","name":"mcp_swarm_get-swarm","title":"Get the agent swarm","kind":"other","status":"pending","rawInput":{},"_meta":{"fx":{"toolCall":{"internal":false,"mcp":{"server":"swarm","tool":"get-swarm"}}}}}
|
|
5
|
+
{"sessionUpdate":"tool_call_update","toolCallId":"call_kK1rqCaYaXn16uKH9e6fmb7v","status":"in_progress"}
|
|
6
|
+
{"sessionUpdate":"tool_call_update","toolCallId":"call_kK1rqCaYaXn16uKH9e6fmb7v","status":"completed","content":[{"type":"content","content":{"type":"text","text":"{\"server\":\"swarm\",\"tool\":\"mcp_swarm_get-swarm\",\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"Found 1 agent(s) in the swarm. Requested by session: 00000000-0000-4000-8000-0000000000b2\\n\\n- fx-probe (idle"}}]}
|
|
7
|
+
{"sessionUpdate":"user_message_chunk","messageId":"41657d84ac6ea6200d169f93414327bb","content":{"type":"text","text":"Steering: also include the word STEERED in your final reply."},"_meta":{"fx":{"steering":{"requestId":4}}}}
|
|
8
|
+
{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"**Preparing shell command update**\n\nI’m thinking about how to provide updates during longer tasks. Specifically, I’ll send a brief message before starting a major phase of the work. So, I'll prepare a…"}}
|
|
9
|
+
{"sessionUpdate":"agent_message_chunk","messageId":"bbef5430c871c3e3e256a2d366116413","content":{"type":"text","text":"Now I'll run the shell command `sleep 6 && echo slept > slept.txt` and report its result. I'll run it now."}}
|
|
10
|
+
{"sessionUpdate":"tool_call","toolCallId":"call_61i3wzVhpMAqWhlx7P3ptjxN","name":"shell","title":"Running","kind":"execute","status":"pending","rawInput":{"action":"run","command":"sleep 6 && echo slept > slept.txt","timeout_ms":120000},"_meta":{"fx":{"toolCall":{"internal":false}}}}
|
|
11
|
+
{"sessionUpdate":"tool_call_update","toolCallId":"call_61i3wzVhpMAqWhlx7P3ptjxN","status":"in_progress"}
|
|
12
|
+
{"sessionUpdate":"tool_call_update","toolCallId":"call_61i3wzVhpMAqWhlx7P3ptjxN","status":"completed","content":[{"type":"content","content":{"type":"text","text":""}}],"command_result":{"kind":"command","command":"sleep 6 && echo slept > slept.txt","cwd":"/workspace/task","exit_code":0,"signal":null,"timed_out":false,"duration_ms":6012,"stdout_bytes":0,"stderr_bytes":0,"truncated":false,"output_file":null,"stdout_file":null,"stderr_file":null}}
|
|
13
|
+
{"sessionUpdate":"agent_message_chunk","messageId":"08b3f1a8b8c18d0153eb85eeecf38aab","content":{"type":"text","text":"Results:\n- mcp_swarm_get-swarm returned 1 agent.\n- Shell command `sleep 6 && echo slept > slept.txt` ran and exited with code 0 after ~6012 ms.\n- The command produced no stdout/stderr output; it created (or overwrote) the file `slept.txt` containing the text `slept`.\n\nSTEERED BANANA"}}
|
|
14
|
+
{"sessionUpdate":"session_info_update","title":"Step 1: call the swarm MCP tool `get-swarm`","updatedAt":"2026-10-29T16:12:50Z"}
|
|
15
|
+
{"sessionUpdate":"usage_update","used":20196,"size":400000}
|