@desplega.ai/agent-swarm 2.7.0 → 2.7.1
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/dist/{acp-adapter-ma1js5n7.js → acp-adapter-8dn5tq56.js} +6 -6
- package/dist/{actions-1vhmtvg9.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-sxqqvmdk.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-mwx1d5a9.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-yx1p6g82.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-7h78vmvv.js → boot-reembed-fxfw17m2.js} +10 -3
- package/dist/{boot-reembed-5h37a2c2.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-8mawd4gw.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-ca84dhnn.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-wqt4hfkq.js → claude-adapter-ahmyf231.js} +7 -7
- package/dist/{claude-managed-adapter-6fjqtr2z.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-rqv98sea.js → claude-managed-setup-mdc7dy6b.js} +2 -2
- package/dist/{claude-sdk-session-f29f3b2x.js → claude-sdk-session-n5wen9zk.js} +8728 -8599
- package/dist/{cli-m5e802r7.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-e1595bgx.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-2y7v133v.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-d20qvpr2.js → cli-2sjm3t4r.js} +1 -1
- package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
- package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
- package/dist/{cli-twctnkng.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-he9gbdx0.js → cli-5qt9vzmq.js} +39 -20
- package/dist/{cli-a6ds2epe.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-6srv653c.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-1hhbzkbb.js → cli-6w2yrrc0.js} +8 -8
- package/dist/{cli-f12mr20z.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-j3y0x2n4.js → cli-8feswf2v.js} +43 -33
- package/dist/{cli-07q03xm2.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ymjpkxgn.js → cli-9csfthbw.js} +8 -6
- package/dist/{cli-f1e0ptjj.js → cli-b41p764a.js} +21 -2
- package/dist/{cli-kv7sbc2c.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-54sj64fc.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-8mbc9za9.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-c668vvcf.js → cli-d8gaphtr.js} +1 -1
- package/dist/{cli-86ehn4s5.js → cli-dgtfysne.js} +2 -2
- package/dist/{cli-sjctjr48.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-j0ppywdr.js → cli-dv11pgpv.js} +12 -12
- package/dist/{cli-1m9x8dr5.js → cli-ekptgk2m.js} +5 -5
- package/dist/{cli-9gsm2rzp.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-s0mgx214.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-yd2nreqw.js → cli-hkxqdmnb.js} +2 -2
- package/dist/{cli-8wmj18cz.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-wqr7cea4.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sgqm2582.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-ay40dnj3.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-j9bpmykh.js → cli-kbtdftbw.js} +5 -5
- package/dist/{cli-x37r7qwf.js → cli-kj69b9tw.js} +25 -5
- package/dist/{cli-gsfk58nw.js → cli-kk9jy4nm.js} +1 -1
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-h5jeg46p.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-a89zdqz9.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-v6shg7na.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-6a9qfa8b.js → cli-pzafyqtk.js} +118 -48
- package/dist/{cli-48gxjmcz.js → cli-qvaa1gjp.js} +87 -24
- package/dist/{cli-qj442fws.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-aypd09nf.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-9pshx6rd.js → cli-s504c3w7.js} +1 -1
- package/dist/{cli-ptfq202x.js → cli-swkx9jm2.js} +3 -3
- package/dist/{cli-m30k9777.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-60sty27p.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-rd5m70gj.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-yxbsmx56.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-s09j9vp5.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
- package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
- package/dist/{cli-b3yzyc57.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-6mgdba42.js → cli-xbz6mcg4.js} +2 -2
- package/dist/{cli-2pvzmas1.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-f4dbh7a5.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +27 -19
- package/dist/{codex-adapter-zk59x2zh.js → codex-adapter-7wepzq15.js} +5 -5
- package/dist/{codex-hook-xyjhvxwd.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-kh6s4j3n.js → codex-session-runner-02sb49e4.js} +5 -5
- package/dist/{commands-68et1qv2.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-fyrbnzg4.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-nep9h2xv.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-yss3pg7t.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-28gjyvxj.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-8ftwvzba.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-hayexmkr.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-46wawff9.js → dsh-adapter-y8gyk130.js} +1 -1
- package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
- package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
- package/dist/{easter-egg-3d-8d1e0h4m.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-cjrfxfnm.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-xbmxcg09.js → external-channel-smz7jyth.js} +2 -2
- package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
- package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
- package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
- package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
- package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
- package/dist/{grok-adapter-ag4wks2a.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-psfvcgwp.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cyq4mryn.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-8pz04697.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-haj1nd6k.js → http-q6j4k989.js} +934 -180
- package/dist/{index-z5h22pes.js → index-5nb266jr.js} +17 -17
- package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
- package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
- package/dist/{index-zyq8ct52.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-syvhnvtw.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-y1ajx04a.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-jwt2bj6f.js → index-ejqdkkqx.js} +11 -10
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-q52f47pe.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-xdvsamvj.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/{lead-hqg57kdp.js → lead-c0pjcaw4.js} +36 -36
- package/dist/{maintenance-4y2xzh3a.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-jd4r7zbs.js → oauth-refresh-sweep-7qt0g55z.js} +2 -2
- package/dist/{onboard-18447q9q.js → onboard-k7x13fqg.js} +118 -5
- package/dist/{opencode-adapter-26b2frdf.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-f9tgr8yw.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-qn6qbga1.js → pi-mono-adapter-m2qnafxm.js} +63 -9
- package/dist/{pricing-refresh-dw2tgb53.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-s4nwj2m3.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-aygm2760.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-yfvdfr0x.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-1bmvjbf3.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-f7tpzedy.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-r5garej6.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-424wzv0r.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-8tygvxkq.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-xsw323b3.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-ggnghpgf.js → work-objects-0y07495y.js} +20 -20
- package/dist/{worker-sqjd1nqf.js → worker-d4q9sqbc.js} +36 -36
- package/openapi.json +1070 -9
- package/package.json +3 -3
- package/src/be/connector-codes.ts +65 -22
- package/src/be/memory/boot-reembed.ts +4 -1
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/embedding-recovery.ts +48 -0
- package/src/be/memory/index-content.ts +5 -2
- package/src/be/memory/providers/openai-embedding.ts +65 -14
- package/src/be/memory/task-learning.ts +110 -8
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/embeddings.ts +2 -2
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/cli.tsx +9 -0
- package/src/comb/config.ts +6 -1
- package/src/commands/onboard/agent-fs-version.ts +5 -0
- package/src/commands/onboard/compose-generator.ts +112 -1
- package/src/commands/onboard/env-generator.ts +9 -0
- package/src/commands/onboard/tei-image.ts +12 -0
- package/src/commands/onboard/types.ts +7 -0
- package/src/commands/onboard.tsx +11 -1
- package/src/commands/runner.ts +14 -1
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +4 -1
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/session-access.ts +24 -0
- package/src/http/sessions.ts +10 -6
- package/src/http/tasks.ts +85 -11
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/user-roles.ts +4 -1
- package/src/http/users.ts +272 -3
- package/src/http/utils.ts +22 -3
- package/src/prompts/base-prompt.ts +24 -9
- package/src/prompts/session-templates.ts +26 -2
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +6 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +58 -3
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/base-prompt.test.ts +69 -2
- package/src/tests/codex-adapter.test.ts +46 -8
- package/src/tests/codex-oauth-device.test.ts +12 -1
- package/src/tests/comb-config-guard.test.ts +20 -0
- package/src/tests/comb-links.test.ts +79 -1
- package/src/tests/comb-status.test.ts +44 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
- package/src/tests/fixtures/onboard/content.env.txt +29 -0
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/dev.env.txt +28 -0
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
- package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
- package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
- package/src/tests/fixtures/onboard/full.env.txt +36 -0
- package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/research.env.txt +28 -0
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
- package/src/tests/fixtures/onboard/solo.env.txt +26 -0
- package/src/tests/http-log-scrubbing.test.ts +34 -1
- package/src/tests/http-task-write-ownership.test.ts +5 -1
- package/src/tests/invites-http.test.ts +464 -0
- package/src/tests/memory-embedding-input-cap.test.ts +122 -1
- package/src/tests/memory-embedding-recovery.test.ts +137 -0
- package/src/tests/onboard-compose.test.ts +142 -0
- package/src/tests/onboard-env.test.ts +24 -0
- package/src/tests/onboard-non-interactive.test.ts +29 -0
- package/src/tests/onboard-tei-image.test.ts +36 -0
- package/src/tests/onboarding.test.ts +451 -4
- package/src/tests/openai-embedding-provider.test.ts +105 -2
- package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/prompt-template-session.test.ts +19 -3
- package/src/tests/rbac-admission-e2e.test.ts +22 -6
- package/src/tests/rbac-admission.test.ts +342 -4
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/rbac-roles.test.ts +9 -1
- package/src/tests/send-task-requester-gate.test.ts +64 -3
- package/src/tests/sessions-move.test.ts +39 -1
- package/src/tests/store-progress-learning.test.ts +95 -2
- package/src/tests/whoami-route.test.ts +41 -0
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-search.ts +1 -1
- package/src/tools/store-progress.ts +5 -1
- package/src/tools/utils.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/task-attachment-links.ts +15 -3
- package/templates/skills/swarm-docs/content.md +1 -1
- package/dist/handlers-tzmevqb5.js +0 -41
- package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
- package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
- package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
- package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
- /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import {
|
|
6
|
+
type ReconcileActiveTasksOptions,
|
|
7
|
+
type RunnerState,
|
|
8
|
+
type RunningTask,
|
|
9
|
+
reconcileActiveTasks,
|
|
10
|
+
} from "../commands/runner";
|
|
11
|
+
import { PiMonoSession } from "../providers/pi-mono-adapter";
|
|
12
|
+
import type { CostData, ProviderEvent, ProviderSessionConfig } from "../providers/types";
|
|
13
|
+
|
|
14
|
+
type AgentSessionEvent = Parameters<Parameters<AgentSession["subscribe"]>[0]>[0];
|
|
15
|
+
|
|
16
|
+
const tmpLogDir = `/tmp/pi-mono-cost-abort-test-${Date.now()}`;
|
|
17
|
+
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
mkdirSync(tmpLogDir, { recursive: true });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterAll(() => {
|
|
23
|
+
rmSync(tmpLogDir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function makeSessionConfig(logFile: string): ProviderSessionConfig {
|
|
27
|
+
return {
|
|
28
|
+
prompt: "Coordinate sub-agents and complete the lead task",
|
|
29
|
+
systemPrompt: "",
|
|
30
|
+
model: "anthropic/claude-opus-4-6",
|
|
31
|
+
role: "lead",
|
|
32
|
+
agentId: "lead-agent-id",
|
|
33
|
+
taskId: "lead-task-id",
|
|
34
|
+
apiUrl: "http://localhost:3013",
|
|
35
|
+
apiKey: "example-test-key",
|
|
36
|
+
cwd: "/tmp",
|
|
37
|
+
logFile,
|
|
38
|
+
iteration: 1,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("PiMonoSession & runner — long lead session cost preservation (#2073)", () => {
|
|
43
|
+
test("abort() unblocks waitForIdle() and preserves cost after terminal store-progress", async () => {
|
|
44
|
+
const listeners: Array<(event: AgentSessionEvent) => void> = [];
|
|
45
|
+
const mockSession = {
|
|
46
|
+
sessionId: "lead-pi-session-1",
|
|
47
|
+
isStreaming: true,
|
|
48
|
+
model: { provider: "anthropic", id: "claude-opus-4-6" },
|
|
49
|
+
subscribe(listener: (event: AgentSessionEvent) => void) {
|
|
50
|
+
listeners.push(listener);
|
|
51
|
+
return () => {
|
|
52
|
+
const idx = listeners.indexOf(listener);
|
|
53
|
+
if (idx >= 0) listeners.splice(idx, 1);
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
async prompt() {
|
|
57
|
+
for (const l of listeners) {
|
|
58
|
+
l({
|
|
59
|
+
type: "tool_execution_start",
|
|
60
|
+
toolCallId: "sp-1",
|
|
61
|
+
toolName: "agent-swarm_store-progress",
|
|
62
|
+
args: { status: "completed", output: "All sub-tasks merged" },
|
|
63
|
+
} as unknown as AgentSessionEvent);
|
|
64
|
+
l({
|
|
65
|
+
type: "tool_execution_end",
|
|
66
|
+
toolCallId: "sp-1",
|
|
67
|
+
toolName: "agent-swarm_store-progress",
|
|
68
|
+
result: { success: true },
|
|
69
|
+
isError: false,
|
|
70
|
+
} as unknown as AgentSessionEvent);
|
|
71
|
+
}
|
|
72
|
+
// Simulate a streaming turn that never emits agent_end before runner aborts it
|
|
73
|
+
},
|
|
74
|
+
getContextUsage: () => null,
|
|
75
|
+
getSessionStats: () => ({
|
|
76
|
+
tokens: { input: 42000, output: 3800, cacheRead: 120000, cacheWrite: 15000, total: 180800 },
|
|
77
|
+
cost: 1.85,
|
|
78
|
+
userMessages: 1,
|
|
79
|
+
assistantMessages: 14,
|
|
80
|
+
}),
|
|
81
|
+
abort: async () => {},
|
|
82
|
+
dispose: () => {},
|
|
83
|
+
} as unknown as AgentSession;
|
|
84
|
+
|
|
85
|
+
const logFile = join(tmpLogDir, `lead-abort-idle-${Date.now()}.log`);
|
|
86
|
+
const session = new PiMonoSession(mockSession, makeSessionConfig(logFile), false);
|
|
87
|
+
const events: ProviderEvent[] = [];
|
|
88
|
+
session.onEvent((e) => events.push(e));
|
|
89
|
+
|
|
90
|
+
// Allow prompt() to emit store-progress and enter waitForIdle()
|
|
91
|
+
await Bun.sleep(20);
|
|
92
|
+
|
|
93
|
+
// Mid-session result snapshot should already be emitted on terminal store-progress
|
|
94
|
+
const resultEventsBeforeAbort = events.filter((e) => e.type === "result");
|
|
95
|
+
expect(resultEventsBeforeAbort.length).toBeGreaterThanOrEqual(1);
|
|
96
|
+
|
|
97
|
+
// Runner reconciliation aborts the session because the task is now completed server-side
|
|
98
|
+
await session.abort();
|
|
99
|
+
|
|
100
|
+
const result = await session.waitForCompletion();
|
|
101
|
+
expect(result.exitCode).toBe(0);
|
|
102
|
+
expect(result.isError).toBe(false);
|
|
103
|
+
expect(result.cost).toBeDefined();
|
|
104
|
+
expect(result.cost?.totalCostUsd).toBe(1.85);
|
|
105
|
+
expect(result.cost?.inputTokens).toBe(42000);
|
|
106
|
+
expect(result.cost?.outputTokens).toBe(3800);
|
|
107
|
+
expect(result.cost?.cacheReadTokens).toBe(120000);
|
|
108
|
+
expect(result.cost?.cacheWriteTokens).toBe(15000);
|
|
109
|
+
expect(result.cost?.numTurns).toBe(15);
|
|
110
|
+
expect(result.cost?.provider).toBe("pi");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("prompt() rejection on abort after terminal store-progress still returns cost and exitCode 0", async () => {
|
|
114
|
+
const listeners: Array<(event: AgentSessionEvent) => void> = [];
|
|
115
|
+
let rejectPrompt: (err: Error) => void = () => {};
|
|
116
|
+
const promptBlocked = new Promise<void>((_, reject) => {
|
|
117
|
+
rejectPrompt = reject;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const mockSession = {
|
|
121
|
+
sessionId: "lead-pi-session-2",
|
|
122
|
+
isStreaming: true,
|
|
123
|
+
model: { provider: "anthropic", id: "claude-opus-4-6" },
|
|
124
|
+
subscribe(listener: (event: AgentSessionEvent) => void) {
|
|
125
|
+
listeners.push(listener);
|
|
126
|
+
return () => {
|
|
127
|
+
const idx = listeners.indexOf(listener);
|
|
128
|
+
if (idx >= 0) listeners.splice(idx, 1);
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
async prompt() {
|
|
132
|
+
for (const l of listeners) {
|
|
133
|
+
l({
|
|
134
|
+
type: "tool_execution_start",
|
|
135
|
+
toolCallId: "sp-2",
|
|
136
|
+
toolName: "store-progress",
|
|
137
|
+
args: { status: "completed", output: "Done" },
|
|
138
|
+
} as unknown as AgentSessionEvent);
|
|
139
|
+
l({
|
|
140
|
+
type: "tool_execution_end",
|
|
141
|
+
toolCallId: "sp-2",
|
|
142
|
+
toolName: "store-progress",
|
|
143
|
+
result: { ok: true },
|
|
144
|
+
isError: false,
|
|
145
|
+
} as unknown as AgentSessionEvent);
|
|
146
|
+
}
|
|
147
|
+
await promptBlocked;
|
|
148
|
+
},
|
|
149
|
+
getContextUsage: () => null,
|
|
150
|
+
getSessionStats: () => ({
|
|
151
|
+
tokens: { input: 18500, output: 2100, cacheRead: 50000, cacheWrite: 6000, total: 76600 },
|
|
152
|
+
cost: 0.92,
|
|
153
|
+
userMessages: 1,
|
|
154
|
+
assistantMessages: 8,
|
|
155
|
+
}),
|
|
156
|
+
abort: async () => {
|
|
157
|
+
rejectPrompt(new Error("Request aborted"));
|
|
158
|
+
},
|
|
159
|
+
dispose: () => {},
|
|
160
|
+
} as unknown as AgentSession;
|
|
161
|
+
|
|
162
|
+
const logFile = join(tmpLogDir, `lead-abort-throw-${Date.now()}.log`);
|
|
163
|
+
const session = new PiMonoSession(mockSession, makeSessionConfig(logFile), false);
|
|
164
|
+
|
|
165
|
+
await Bun.sleep(20);
|
|
166
|
+
await session.abort();
|
|
167
|
+
|
|
168
|
+
const result = await session.waitForCompletion();
|
|
169
|
+
expect(result.exitCode).toBe(0);
|
|
170
|
+
expect(result.isError).toBe(false);
|
|
171
|
+
expect(result.cost).toBeDefined();
|
|
172
|
+
expect(result.cost?.totalCostUsd).toBe(0.92);
|
|
173
|
+
expect(result.cost?.inputTokens).toBe(18500);
|
|
174
|
+
expect(result.cost?.outputTokens).toBe(2100);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("unexpected throw in runSession attaches partial session cost with isError: true", async () => {
|
|
178
|
+
const mockSession = {
|
|
179
|
+
sessionId: "lead-pi-session-3",
|
|
180
|
+
isStreaming: false,
|
|
181
|
+
model: { provider: "anthropic", id: "claude-opus-4-6" },
|
|
182
|
+
subscribe: () => () => {},
|
|
183
|
+
async prompt() {
|
|
184
|
+
throw new Error("MCP transport disconnected after 10 turns");
|
|
185
|
+
},
|
|
186
|
+
getContextUsage: () => null,
|
|
187
|
+
getSessionStats: () => ({
|
|
188
|
+
tokens: { input: 9000, output: 1100, cacheRead: 4000, cacheWrite: 1000, total: 15100 },
|
|
189
|
+
cost: 0.41,
|
|
190
|
+
userMessages: 1,
|
|
191
|
+
assistantMessages: 5,
|
|
192
|
+
}),
|
|
193
|
+
abort: async () => {},
|
|
194
|
+
dispose: () => {},
|
|
195
|
+
} as unknown as AgentSession;
|
|
196
|
+
|
|
197
|
+
const logFile = join(tmpLogDir, `lead-unexpected-throw-${Date.now()}.log`);
|
|
198
|
+
const session = new PiMonoSession(mockSession, makeSessionConfig(logFile), false);
|
|
199
|
+
|
|
200
|
+
const result = await session.waitForCompletion();
|
|
201
|
+
expect(result.exitCode).toBe(1);
|
|
202
|
+
expect(result.isError).toBe(true);
|
|
203
|
+
expect(result.cost).toBeDefined();
|
|
204
|
+
expect(result.cost?.isError).toBe(true);
|
|
205
|
+
expect(result.cost?.totalCostUsd).toBe(0.41);
|
|
206
|
+
expect(result.cost?.inputTokens).toBe(9000);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("reconcileActiveTasks preserves task.lastCost when settling an unresponsive aborted session", async () => {
|
|
210
|
+
const snapshotCost: CostData = {
|
|
211
|
+
sessionId: "runner-session-1",
|
|
212
|
+
taskId: "task-2073",
|
|
213
|
+
agentId: "lead-agent-id",
|
|
214
|
+
totalCostUsd: 2.34,
|
|
215
|
+
inputTokens: 64000,
|
|
216
|
+
outputTokens: 5200,
|
|
217
|
+
cacheReadTokens: 150000,
|
|
218
|
+
cacheWriteTokens: 20000,
|
|
219
|
+
durationMs: 1_200_000,
|
|
220
|
+
numTurns: 22,
|
|
221
|
+
model: "anthropic/claude-opus-4-6",
|
|
222
|
+
isError: false,
|
|
223
|
+
provider: "pi",
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const task: RunningTask = {
|
|
227
|
+
taskId: "task-2073",
|
|
228
|
+
session: {
|
|
229
|
+
sessionId: "pi-session-2073",
|
|
230
|
+
onEvent: () => {},
|
|
231
|
+
waitForCompletion: () => new Promise(() => {}),
|
|
232
|
+
abort: async () => {},
|
|
233
|
+
},
|
|
234
|
+
logFile: "/tmp/task-2073.log",
|
|
235
|
+
startTime: new Date(1_000),
|
|
236
|
+
promise: new Promise(() => {}),
|
|
237
|
+
result: null,
|
|
238
|
+
harnessProvider: "pi",
|
|
239
|
+
hasLocalEnvironment: true,
|
|
240
|
+
abortRequestedAt: 10_000,
|
|
241
|
+
serverTerminalStatus: "completed",
|
|
242
|
+
lastCost: { value: snapshotCost },
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const state: RunnerState = {
|
|
246
|
+
activeTasks: new Map([["task-2073", task]]),
|
|
247
|
+
deliveryMode: "poll",
|
|
248
|
+
maxConcurrent: 2,
|
|
249
|
+
startedAt: 0,
|
|
250
|
+
tasksProcessed: 0,
|
|
251
|
+
harnessProvider: "pi",
|
|
252
|
+
codexCreditsExhaustedCooldownMs: 60_000,
|
|
253
|
+
modelWindowBlocks: new Map(),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const opts: ReconcileActiveTasksOptions = {
|
|
257
|
+
cancelledSignaled: new Set(),
|
|
258
|
+
isCancelled: async () => false,
|
|
259
|
+
fetchStatus: async () => "completed",
|
|
260
|
+
intervalMs: 30_000,
|
|
261
|
+
abortGraceMs: 10_000,
|
|
262
|
+
now: () => 25_000,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
await reconcileActiveTasks(state, "lead", opts);
|
|
266
|
+
|
|
267
|
+
expect(task.result).not.toBeNull();
|
|
268
|
+
expect(task.result?.cost).toEqual(snapshotCost);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
@@ -16,11 +16,15 @@ test(
|
|
|
16
16
|
"COMB_ENABLED",
|
|
17
17
|
"APP_URL",
|
|
18
18
|
"AGENT_FS_API_URL",
|
|
19
|
+
"AGENT_FS_PROXY",
|
|
19
20
|
"AGENT_FS_LIVE_URL",
|
|
21
|
+
"AGENT_FS_LINK_TARGET",
|
|
20
22
|
"AGENT_FS_DEFAULT_ORG_ID",
|
|
21
23
|
"AGENT_FS_DEFAULT_DRIVE_ID",
|
|
22
24
|
"SWARM_ORG_NAME",
|
|
23
25
|
"SWARM_ORG_WEBSITE",
|
|
26
|
+
"EMBEDDING_QUERY_PREFIX",
|
|
27
|
+
"EMBEDDING_DOCUMENT_PREFIX",
|
|
24
28
|
];
|
|
25
29
|
try {
|
|
26
30
|
await Bun.write(
|
package/src/tests/preload.ts
CHANGED
|
@@ -32,11 +32,15 @@ for (const key of Object.keys(process.env)) {
|
|
|
32
32
|
"COMB_ENABLED",
|
|
33
33
|
"APP_URL",
|
|
34
34
|
"AGENT_FS_API_URL",
|
|
35
|
+
"AGENT_FS_PROXY",
|
|
35
36
|
"AGENT_FS_LIVE_URL",
|
|
37
|
+
"AGENT_FS_LINK_TARGET",
|
|
36
38
|
"AGENT_FS_DEFAULT_ORG_ID",
|
|
37
39
|
"AGENT_FS_DEFAULT_DRIVE_ID",
|
|
38
40
|
"SWARM_ORG_NAME",
|
|
39
41
|
"SWARM_ORG_WEBSITE",
|
|
42
|
+
"EMBEDDING_QUERY_PREFIX",
|
|
43
|
+
"EMBEDDING_DOCUMENT_PREFIX",
|
|
40
44
|
].includes(key)
|
|
41
45
|
) {
|
|
42
46
|
delete process.env[key];
|
|
@@ -41,6 +41,8 @@ const SYSTEM_TEMPLATES = [
|
|
|
41
41
|
"system.agent.tool_preload",
|
|
42
42
|
"system.agent.tool_discovery.search",
|
|
43
43
|
"system.agent.tool_discovery.amp",
|
|
44
|
+
"system.agent.tool_discovery.codex",
|
|
45
|
+
"system.agent.tool_discovery.opencode",
|
|
44
46
|
"system.agent.tool_discovery.direct",
|
|
45
47
|
"system.agent.tools_skills",
|
|
46
48
|
"system.agent.worker",
|
|
@@ -119,11 +121,11 @@ describe("Session templates: registration", () => {
|
|
|
119
121
|
expect(sessionSystemEventTypes("session")).toEqual([...SESSION_TEMPLATES].sort());
|
|
120
122
|
});
|
|
121
123
|
|
|
122
|
-
test("registers
|
|
124
|
+
test("registers 35 system and session templates in total", () => {
|
|
123
125
|
const all = getAllTemplateDefinitions();
|
|
124
126
|
const sessionSystem = all.filter((d) => d.category === "system" || d.category === "session");
|
|
125
|
-
//
|
|
126
|
-
expect(sessionSystem.length).toBe(
|
|
127
|
+
// 30 system blocks + 5 session composites.
|
|
128
|
+
expect(sessionSystem.length).toBe(35);
|
|
127
129
|
});
|
|
128
130
|
|
|
129
131
|
test("drops the v1 blocks that prompt v2 deleted", () => {
|
|
@@ -246,6 +248,20 @@ describe("Session templates: MUST pointers", () => {
|
|
|
246
248
|
expect(result.text).toContain("`implementing` skill");
|
|
247
249
|
expect(result.text).not.toContain("/researching");
|
|
248
250
|
});
|
|
251
|
+
|
|
252
|
+
test("the outputs block takes its file-link base from fileLinkBase", () => {
|
|
253
|
+
const definition = getAllTemplateDefinitions().find(
|
|
254
|
+
(d) => d.eventType === "system.agent.outputs",
|
|
255
|
+
);
|
|
256
|
+
expect(definition?.variables.map((v) => v.name)).toEqual(["fileLinkBase"]);
|
|
257
|
+
const result = resolveTemplate("system.agent.outputs", {
|
|
258
|
+
fileLinkBase: "http://localhost:5274",
|
|
259
|
+
});
|
|
260
|
+
expect(result.text).toContain(
|
|
261
|
+
"Give people the live app link `http://localhost:5274/file/~/<org>/<drive>/<path>`.",
|
|
262
|
+
);
|
|
263
|
+
expect(result.unresolved).toEqual([]);
|
|
264
|
+
});
|
|
249
265
|
});
|
|
250
266
|
|
|
251
267
|
// ============================================================================
|
|
@@ -247,12 +247,20 @@ describe("RBAC admission over real HTTP", () => {
|
|
|
247
247
|
|
|
248
248
|
rewriteUserToRequester(dbPath, userId);
|
|
249
249
|
|
|
250
|
-
|
|
250
|
+
// A requester creates its own task (`task.create.own`); a verb-less write
|
|
251
|
+
// stays operator-only.
|
|
252
|
+
const requesterTaskCreate = await api(server.base, "POST", "/api/tasks", {
|
|
251
253
|
bearer: userToken,
|
|
252
|
-
body: { task: "rbac admission
|
|
254
|
+
body: { task: "rbac admission requester task" },
|
|
253
255
|
});
|
|
254
|
-
expect(
|
|
255
|
-
|
|
256
|
+
expect(requesterTaskCreate.status).toBe(201);
|
|
257
|
+
|
|
258
|
+
const deniedDbQuery = await api(server.base, "POST", "/api/db-query", {
|
|
259
|
+
bearer: userToken,
|
|
260
|
+
body: { sql: "select 1" },
|
|
261
|
+
});
|
|
262
|
+
expect(deniedDbQuery.status).toBe(403);
|
|
263
|
+
expect(deniedDbQuery.body.error).toContain("admission: route has no permission verb");
|
|
256
264
|
|
|
257
265
|
// The ranking state carries the private claim: a requester cannot read it.
|
|
258
266
|
const deniedRanking = await api(server.base, "GET", "/api/ranking", { bearer: userToken });
|
|
@@ -378,8 +386,8 @@ describe("RBAC admission over real HTTP", () => {
|
|
|
378
386
|
"GET /api/ranking",
|
|
379
387
|
"GET /api/scripts/{id}/apis/{endpointId}/secret",
|
|
380
388
|
"PATCH /api/assets/app/{id}/key",
|
|
389
|
+
"POST /api/db-query",
|
|
381
390
|
"POST /api/scripts/{id}/apis",
|
|
382
|
-
"POST /api/tasks",
|
|
383
391
|
].every((resourceId) =>
|
|
384
392
|
userRows.some((row) => row.resourceId === resourceId && row.decision === "deny"),
|
|
385
393
|
) &&
|
|
@@ -410,9 +418,17 @@ describe("RBAC admission over real HTTP", () => {
|
|
|
410
418
|
"GET /api/ranking",
|
|
411
419
|
"GET /api/scripts/{id}/apis/{endpointId}/secret",
|
|
412
420
|
"PATCH /api/assets/app/{id}/key",
|
|
421
|
+
"POST /api/db-query",
|
|
413
422
|
"POST /api/scripts/{id}/apis",
|
|
414
|
-
"POST /api/tasks",
|
|
415
423
|
]);
|
|
424
|
+
expect(
|
|
425
|
+
userHttpRows.some(
|
|
426
|
+
(row) =>
|
|
427
|
+
row.resourceId === "POST /api/tasks" &&
|
|
428
|
+
row.verb === "task.create.own" &&
|
|
429
|
+
row.decision === "allow",
|
|
430
|
+
),
|
|
431
|
+
).toBe(true);
|
|
416
432
|
expect(
|
|
417
433
|
denyRows.some(
|
|
418
434
|
(row) =>
|