@desplega.ai/agent-swarm 1.131.0 → 1.133.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/README.md +2 -2
- package/dist/{actions-w53mbns9.js → actions-cskpayd7.js} +11 -9
- package/dist/{anthropic-messages-rjmd2e7n.js → anthropic-messages-tpxtbx7b.js} +28 -12
- package/dist/{app-m9gwqsqv.js → app-ang77nbb.js} +8 -6
- package/dist/{assistant-bp65m6f3.js → assistant-b4exebdw.js} +16 -13
- package/dist/{azure-openai-responses-c0yjmbfd.js → azure-openai-responses-m3921t4y.js} +8 -8
- package/dist/{boot-reembed-62p0at2k.js → boot-reembed-ps2aftpb.js} +9 -7
- package/dist/{boot-reembed-xmfp6wdh.js → boot-reembed-znyk3nxb.js} +10 -8
- package/dist/{boot-scrub-logs-rr8q2dms.js → boot-scrub-logs-2ax3xgz1.js} +7 -5
- package/dist/{claude-adapter-2y47mrkd.js → claude-adapter-3ct8kgxw.js} +13 -9
- package/dist/{claude-managed-adapter-gv19tdew.js → claude-managed-adapter-akx4ntf2.js} +2 -2
- package/dist/{cli-t1xze5qh.js → cli-13w2hwkh.js} +1 -1
- package/dist/{cli-efxfnq6q.js → cli-1532szb1.js} +8 -6
- package/dist/{cli-d2tfkcga.js → cli-23d3rseb.js} +33 -17
- package/dist/{cli-nm5gtp1x.js → cli-2a75z9c5.js} +196 -7
- package/dist/{cli-fywms69x.js → cli-2t30rj0d.js} +1 -1
- package/dist/{cli-cvgjwnh1.js → cli-3c3tdgab.js} +10 -10
- package/dist/{cli-pm6xbh84.js → cli-3zzxa9jz.js} +46 -16
- package/dist/{cli-cdeg08wx.js → cli-4k6233gc.js} +1 -1
- package/dist/{cli-t55yw3a1.js → cli-5p2tz05r.js} +2 -2
- package/dist/{cli-0rcyq6d9.js → cli-5z2yhzvd.js} +125 -47
- package/dist/{cli-3kf51j15.js → cli-6zfq31nj.js} +9 -3
- package/dist/{cli-er3a4bb0.js → cli-8m9de10a.js} +6 -1
- package/dist/{cli-gm9cbhbq.js → cli-8t14vg9w.js} +27 -8
- package/dist/{cli-72q8m8gk.js → cli-9nfc2es8.js} +6 -6
- package/dist/{cli-5ze9rv97.js → cli-9ta3pwms.js} +11 -4
- package/dist/{cli-78xwvkhj.js → cli-avr0ns7z.js} +2 -2
- package/dist/{cli-n6h8qt8n.js → cli-c897wyhw.js} +12 -7
- package/dist/{cli-2q72qz2s.js → cli-dgk20ft7.js} +15 -6
- package/dist/cli-et1bbwqf.js +13 -0
- package/dist/{cli-bqw0mt1n.js → cli-f5w27pj3.js} +3 -3
- package/dist/{cli-2wadx7p2.js → cli-f8dre4bz.js} +2 -2
- package/dist/{cli-e7ckcg4v.js → cli-ftwpv451.js} +170 -387
- package/dist/{cli-g6b8x1h8.js → cli-g854s2dw.js} +1 -1
- package/dist/{cli-5kx36evn.js → cli-ggmdxeqa.js} +13 -8
- package/dist/cli-hkwfe3qj.js +530 -0
- package/dist/{cli-5dsbcjjw.js → cli-jqwnqx9m.js} +1 -1
- package/dist/{cli-9dr5qzc0.js → cli-ny122vh4.js} +21 -10
- package/dist/{cli-5v4rw6yd.js → cli-pvzqhbs9.js} +2 -2
- package/dist/cli-pzq5rjym.js +15 -0
- package/dist/{cli-xpxde7rm.js → cli-qes8ef67.js} +48 -30
- package/dist/{cli-sajgpkqg.js → cli-qs9hhsvb.js} +1723 -325
- package/dist/cli-qys3rhhz.js +27 -0
- package/dist/{cli-rzbpmxdr.js → cli-r6tpm37h.js} +2 -2
- package/dist/{cli-qv61hp37.js → cli-rch80c76.js} +2 -2
- package/dist/{cli-0258203w.js → cli-rpp0x4zy.js} +137 -17
- package/dist/{cli-5n6e5yfn.js → cli-s1k0wxhe.js} +12 -7
- package/dist/{cli-psh3c4va.js → cli-sfd9v7sj.js} +1 -1
- package/dist/{cli-70xqj0z3.js → cli-sh3ytsyj.js} +49 -39
- package/dist/{cli-zsspvvm2.js → cli-w1mp9jy7.js} +1 -1
- package/dist/{cli-d3x3x1c9.js → cli-xdb4mpnp.js} +2 -2
- package/dist/{cli-57khth04.js → cli-xmyqv00z.js} +120 -4
- package/dist/{cli-yw8vghx2.js → cli-xv8t8p3c.js} +65 -32
- package/dist/{cli-f1am9szv.js → cli-xxv5pn4f.js} +1 -1
- package/dist/{cli-t76vw5db.js → cli-y08jvxzx.js} +601 -140
- package/dist/{cli-a4jzdpa3.js → cli-zfbf3srj.js} +650 -245
- package/dist/{cli-a40rnf71.js → cli-zmq89k34.js} +6 -6
- package/dist/{cli-qmdnhpej.js → cli-zr9q011v.js} +23 -13
- package/dist/cli.js +22 -22
- package/dist/{codex-adapter-d24q75wk.js → codex-adapter-bef93pq4.js} +9 -7
- package/dist/{codex-hook-hq54t9ft.js → codex-hook-e8dba65p.js} +3 -2
- package/dist/{codex-session-runner-g50vcsex.js → codex-session-runner-5pkxys7x.js} +9 -7
- package/dist/{commands-c3nrc5hg.js → commands-e23s1bff.js} +9 -7
- package/dist/{db-nt7fg3tw.js → db-m3nabatn.js} +17 -5
- package/dist/{google-generative-ai-hysrd8qx.js → google-generative-ai-8maw55xh.js} +11 -8
- package/dist/{google-vertex-p5arrga0.js → google-vertex-8vjqmx8g.js} +11 -8
- package/dist/{handlers-fkr6swat.js → handlers-akazedvr.js} +16 -13
- package/dist/{hook-77wpvnjc.js → hook-ze4cpf0v.js} +55 -41
- package/dist/{hook-g019yehy.js → hook-zfqyyenw.js} +10 -7
- package/dist/{http-jewj0syk.js → http-sja0n6kw.js} +730 -150
- package/dist/index-0gr8vd6k.js +39 -0
- package/dist/{index-7n2xpbd4.js → index-354vkhdv.js} +154 -74
- package/dist/{index-070v4eyb.js → index-5qthek5t.js} +2 -3
- package/dist/{index-hfvggd0z.js → index-95t3cqq5.js} +16 -14
- package/dist/{index-9znhar3x.js → index-sea66619.js} +3 -4
- package/dist/{index-6gm5he8h.js → index-vpn36mma.js} +15 -13
- package/dist/{keepalive-j0fjvqfd.js → keepalive-3sp3meqj.js} +16 -10
- package/dist/{lead-1n8x30xe.js → lead-mefeewkf.js} +39 -36
- package/dist/{maintenance-gvev11sa.js → maintenance-8tbrbjfg.js} +10 -8
- package/dist/mistral-conversations-00zrp7g4.js +717 -0
- package/dist/{oauth-refresh-sweep-s9ecsvh0.js → oauth-refresh-sweep-srjwjz6m.js} +8 -6
- package/dist/{onboard-pvm5b38w.js → onboard-mt2tgyjg.js} +7 -7
- package/dist/{openai-codex-responses-5g27pd6f.js → openai-codex-responses-map32h2s.js} +18 -18
- package/dist/{openai-completions-3vkmfz2a.js → openai-completions-w871b1cq.js} +12 -11
- package/dist/{openai-responses-dg4t7j7a.js → openai-responses-b0j7xnw8.js} +15 -12
- package/dist/{opencode-adapter-1gm38aba.js → opencode-adapter-b39jefcj.js} +5 -3
- package/dist/{openrouter-images-g3gm8931.js → openrouter-images-srfkywy0.js} +4 -4
- package/dist/{otel-impl-p6jvgd9c.js → otel-impl-z6pxt3j9.js} +224 -9
- package/dist/{pi-mono-adapter-t091s27g.js → pi-mono-adapter-btvqmj58.js} +1138 -408
- package/dist/{pricing-refresh-k49rcyed.js → pricing-refresh-kyq8cezh.js} +9 -7
- package/dist/{rbac-roles-cr80g96t.js → rbac-roles-3ryfrfna.js} +8 -6
- package/dist/{rbac-roles-30pspvy7.js → rbac-roles-6qfvbee1.js} +9 -7
- package/dist/{render-v2-xm457ng1.js → render-v2-p61ppjey.js} +8 -6
- package/dist/{seed-pricing-yhpv35fr.js → seed-pricing-j56bbj4y.js} +8 -6
- package/dist/{setup-nytnmwwr.js → setup-tpr6c1kn.js} +2 -2
- package/dist/{worker-msytr2cw.js → worker-9w1v0mbb.js} +39 -36
- package/openapi.json +542 -16
- package/package.json +6 -6
- package/src/be/budget-refusal-notify.ts +14 -7
- package/src/be/db-queries/task-artifacts.ts +182 -0
- package/src/be/db.ts +783 -169
- package/src/be/events.ts +5 -0
- package/src/be/migrations/130_scratch_script_retention_grace.sql +6 -0
- package/src/be/migrations/131_session_logs_created_at_index.sql +2 -0
- package/src/be/migrations/132_multi_runtime_instances.sql +44 -0
- package/src/be/migrations/133_workflow_run_trigger_type.sql +12 -0
- package/src/be/migrations/135_backfill_task_pull_request_attachments.sql +228 -0
- package/src/be/migrations/136_task_requester_provenance.sql +37 -0
- package/src/be/migrations/137_memory_retrieval_composite_index.sql +1 -0
- package/src/be/multi-runtime.ts +430 -0
- package/src/be/scripts/db.ts +43 -1
- package/src/be/scripts/extract-schema.ts +3 -1
- package/src/be/scripts/retention.ts +164 -0
- package/src/be/scripts/run-saved.ts +16 -1
- package/src/be/scripts/typecheck.ts +3 -2
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/steering.ts +1 -1
- package/src/be/swarm-config-guard.ts +18 -0
- package/src/commands/credential-wait.ts +54 -0
- package/src/commands/onboard/steps/core-credentials.tsx +1 -1
- package/src/commands/onboard/steps/custom-templates.tsx +1 -1
- package/src/commands/onboard/steps/harness-credentials.tsx +1 -1
- package/src/commands/onboard/steps/health-check.tsx +1 -1
- package/src/commands/onboard/steps/start.tsx +1 -1
- package/src/commands/profile-sync-audit.ts +56 -0
- package/src/commands/profile-sync.ts +287 -15
- package/src/commands/provider-credentials.ts +46 -19
- package/src/commands/runner.ts +196 -33
- package/src/commands/templates.ts +22 -0
- package/src/github/handlers.ts +55 -9
- package/src/heartbeat/heartbeat.ts +71 -12
- package/src/hooks/hook.ts +49 -33
- package/src/http/active-sessions.ts +11 -1
- package/src/http/agents.ts +261 -30
- package/src/http/all-routes.ts +1 -0
- package/src/http/config.ts +13 -2
- package/src/http/core.ts +102 -4
- package/src/http/db-query-bounded.ts +313 -0
- package/src/http/db-query-shared.ts +249 -0
- package/src/http/db-query.ts +54 -42
- package/src/http/events.ts +2 -0
- package/src/http/index.ts +27 -2
- package/src/http/mcp-bridge.ts +37 -3
- package/src/http/mcp.ts +12 -0
- package/src/http/memory.ts +13 -2
- package/src/http/openapi.ts +1 -0
- package/src/http/poll.ts +70 -27
- package/src/http/route-def.ts +43 -9
- package/src/http/schedules.ts +2 -2
- package/src/http/scripts.ts +31 -4
- package/src/http/session-data.ts +44 -0
- package/src/http/tasks.ts +2 -2
- package/src/http/users.ts +74 -52
- package/src/http/webhooks.ts +5 -2
- package/src/http/workflows.ts +1 -4
- package/src/http/x.ts +14 -0
- package/src/integrations/kapso/inbound.ts +1 -1
- package/src/jira/outbound.ts +39 -8
- package/src/jira/webhook-lifecycle.ts +13 -2
- package/src/linear/outbound.ts +43 -10
- package/src/oauth/keepalive.ts +13 -2
- package/src/prompts/base-prompt.ts +1 -1
- package/src/prompts/session-templates.ts +45 -7
- package/src/providers/claude-adapter.ts +6 -0
- package/src/providers/claude-managed-models.ts +34 -10
- package/src/providers/codex-adapter.ts +6 -0
- package/src/providers/opencode-adapter.ts +6 -0
- package/src/providers/pi-mono-adapter.ts +8 -0
- package/src/providers/pricing-sources.md +24 -0
- package/src/queue-stall-alarm.ts +216 -0
- package/src/scheduler/scheduler.ts +7 -0
- package/src/script-workflows/executor.ts +2 -1
- package/src/scripts-runtime/executors/native.ts +2 -1
- package/src/scripts-runtime/executors/types.ts +5 -0
- package/src/scripts-runtime/loader.ts +5 -0
- package/src/scripts-runtime/swarm-config.ts +8 -0
- package/src/scripts-runtime/swarm-sdk.ts +7 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/tests/budget-claim-gate.test.ts +20 -0
- package/src/tests/claude-adapter.test.ts +32 -0
- package/src/tests/claude-managed-adapter.test.ts +38 -7
- package/src/tests/codex-adapter.test.ts +30 -0
- package/src/tests/create-task-validation.test.ts +128 -0
- package/src/tests/credential-wait.test.ts +67 -1
- package/src/tests/db-query.test.ts +711 -2
- package/src/tests/http-api-integration.test.ts +81 -0
- package/src/tests/http-users.test.ts +166 -1
- package/src/tests/identity-field-budget.test.ts +176 -0
- package/src/tests/mcp-tools-user.test.ts +34 -0
- package/src/tests/mcp-user-route.test.ts +17 -0
- package/src/tests/metrics-http.test.ts +44 -0
- package/src/tests/multi-runtime-registration.test.ts +2703 -0
- package/src/tests/openapi-response-contract.test.ts +24 -0
- package/src/tests/openapi-route-headers.test.ts +83 -0
- package/src/tests/opencode-adapter.test.ts +40 -0
- package/src/tests/outbound-listener-rejection.test.ts +138 -0
- package/src/tests/pi-mono-adapter.test.ts +98 -0
- package/src/tests/profile-sync-warning.test.ts +277 -0
- package/src/tests/profile-sync.test.ts +232 -6
- package/src/tests/prompt-template-session.test.ts +32 -3
- package/src/tests/queue-stall-alarm.test.ts +226 -0
- package/src/tests/requester-comms.test.ts +32 -0
- package/src/tests/runner-requester-profile.test.ts +38 -0
- package/src/tests/sandboxed-process.test.ts +45 -0
- package/src/tests/schedule-target-type.test.ts +38 -0
- package/src/tests/script-connections.test.ts +16 -3
- package/src/tests/scripts-external-api.test.ts +8 -3
- package/src/tests/scripts-http.test.ts +12 -0
- package/src/tests/scripts-retention.test.ts +384 -0
- package/src/tests/scripts-runtime-identity.test.ts +375 -0
- package/src/tests/scripts-runtime.test.ts +4 -3
- package/src/tests/sdk-allowlist.test.ts +47 -0
- package/src/tests/send-task-requested-by.test.ts +9 -0
- package/src/tests/session-costs.test.ts +506 -1
- package/src/tests/shutdown-error-scrubbing.test.ts +92 -0
- package/src/tests/steering-core.test.ts +28 -0
- package/src/tests/task-artifacts.test.ts +386 -0
- package/src/tests/task-attachments-schema.test.ts +17 -1
- package/src/tests/task-completion-idempotency.test.ts +57 -0
- package/src/tests/task-pull-request-backfill-migration.test.ts +321 -0
- package/src/tests/update-profile-api.test.ts +67 -39
- package/src/tests/update-profile-auth.test.ts +48 -4
- package/src/tests/user-attribution-migration.test.ts +54 -0
- package/src/tests/user-identity.test.ts +72 -0
- package/src/tests/workflow-async-v2.test.ts +39 -1
- package/src/tests/workflow-http-v2.test.ts +3 -2
- package/src/tests/workflow-mcp-trigger-schema.test.ts +52 -3
- package/src/tests/workflow-retry-v2.test.ts +45 -1
- package/src/tests/workflow-swarm-script.test.ts +14 -5
- package/src/tools/db-query.ts +18 -8
- package/src/tools/get-task-details.ts +24 -3
- package/src/tools/manage-user.ts +36 -3
- package/src/tools/poll-task.ts +93 -14
- package/src/tools/script-common.ts +5 -0
- package/src/tools/send-task.ts +1 -4
- package/src/tools/store-progress.ts +13 -2
- package/src/tools/swarm-config/delete-config.ts +9 -0
- package/src/tools/swarm-config/set-config.ts +3 -2
- package/src/tools/task-action.ts +30 -0
- package/src/tools/task-tool-ctx.ts +8 -1
- package/src/tools/update-profile.ts +16 -21
- package/src/tools/utils.ts +5 -0
- package/src/tools/workflows/trigger-workflow.ts +7 -1
- package/src/types.ts +165 -1
- package/src/utils/github-pull-request.ts +76 -0
- package/src/utils/identity-field-budget.ts +73 -0
- package/src/utils/multi-runtime.ts +20 -0
- package/src/utils/requester-comms.ts +24 -0
- package/src/utils/sandboxed-process.ts +40 -14
- package/src/workflows/engine.ts +22 -2
- package/src/workflows/executors/swarm-script.ts +24 -1
- package/src/workflows/resume.ts +19 -11
- package/src/workflows/retry-poller.ts +1 -0
- package/src/workflows/triggers.ts +4 -0
- package/templates/schedules/daily-compounding-reflection/content.md +12 -3
- package/templates/skills/db-query-guidance/config.json +14 -0
- package/templates/skills/db-query-guidance/content.md +57 -0
- package/dist/cli-3te4jdkm.js +0 -225
- package/dist/cli-bmze986d.js +0 -7
- package/dist/cli-kh3ba2zf.js +0 -607
- package/dist/index-hp1kdbjj.js +0 -36
- package/dist/mistral-conversations-81fwav0j.js +0 -30578
- package/dist/otel-nqa12wf3.js +0 -3149
- package/dist/{pi-messages-tqj8q6b9.js → pi-messages-sssy32x4.js} +3 -3
|
@@ -19,6 +19,8 @@ export type RunScriptInput = {
|
|
|
19
19
|
args?: unknown;
|
|
20
20
|
fsMode?: ScriptFsMode;
|
|
21
21
|
agentId: string;
|
|
22
|
+
/** Per-boot runtime identity of the invoking worker; system context, never script input. */
|
|
23
|
+
runtimeInstanceId?: string;
|
|
22
24
|
signal?: AbortSignal;
|
|
23
25
|
timeoutMs?: number;
|
|
24
26
|
mcpBaseUrl?: string;
|
|
@@ -50,6 +52,9 @@ async function buildConfigPayload(input: RunScriptInput): Promise<SwarmConfigPay
|
|
|
50
52
|
value: input.mcpBaseUrl ?? process.env.MCP_BASE_URL ?? "http://localhost:3013",
|
|
51
53
|
isSecret: false,
|
|
52
54
|
},
|
|
55
|
+
...(input.runtimeInstanceId
|
|
56
|
+
? { runtimeInstanceId: { value: input.runtimeInstanceId, isSecret: false as const } }
|
|
57
|
+
: {}),
|
|
53
58
|
},
|
|
54
59
|
user: input.userConfig ?? {},
|
|
55
60
|
egressSecrets: input.egressSecrets ?? (await buildEgressSecrets()),
|
|
@@ -5,6 +5,8 @@ export class SwarmConfig {
|
|
|
5
5
|
readonly apiKey: RedactedValue<string>;
|
|
6
6
|
readonly agentId: RedactedValue<string>;
|
|
7
7
|
readonly mcpBaseUrl: RedactedValue<string>;
|
|
8
|
+
/** Per-boot runtime identity of the invoking worker; system context, never script input. */
|
|
9
|
+
readonly runtimeInstanceId: RedactedValue<string> | undefined;
|
|
8
10
|
|
|
9
11
|
private readonly userValues: Map<string, RedactedValue<string>>;
|
|
10
12
|
|
|
@@ -21,6 +23,12 @@ export class SwarmConfig {
|
|
|
21
23
|
type: "system",
|
|
22
24
|
isSecret: payload.system.mcpBaseUrl.isSecret,
|
|
23
25
|
});
|
|
26
|
+
this.runtimeInstanceId = payload.system.runtimeInstanceId
|
|
27
|
+
? Redacted.make(payload.system.runtimeInstanceId.value, {
|
|
28
|
+
type: "system",
|
|
29
|
+
isSecret: payload.system.runtimeInstanceId.isSecret,
|
|
30
|
+
})
|
|
31
|
+
: undefined;
|
|
24
32
|
this.userValues = new Map(
|
|
25
33
|
Object.entries(payload.user ?? {}).map(([key, value]) => [
|
|
26
34
|
key,
|
|
@@ -14,6 +14,12 @@ function headers(config: SwarmConfig): Record<string, string> {
|
|
|
14
14
|
return {
|
|
15
15
|
Authorization: `Bearer ${Redacted.value(config.apiKey)}`,
|
|
16
16
|
"X-Agent-ID": Redacted.value(config.agentId),
|
|
17
|
+
// Per-boot identity of the invoking worker process (system context, not
|
|
18
|
+
// script input) — work-acquisition endpoints gate on it in multi-runtime
|
|
19
|
+
// mode.
|
|
20
|
+
...(config.runtimeInstanceId
|
|
21
|
+
? { "X-Runtime-Instance-ID": Redacted.value(config.runtimeInstanceId) }
|
|
22
|
+
: {}),
|
|
17
23
|
"Content-Type": "application/json",
|
|
18
24
|
};
|
|
19
25
|
}
|
|
@@ -168,6 +174,7 @@ function bridgeRequestFor(name: string, args: unknown): BridgeRequest | null {
|
|
|
168
174
|
hideCompleted: body.hideCompleted,
|
|
169
175
|
consecutiveErrorsMin: body.consecutiveErrorsMin,
|
|
170
176
|
lastRunStatus: body.lastRunStatus,
|
|
177
|
+
fields: body.includeFull ? "full" : undefined,
|
|
171
178
|
}),
|
|
172
179
|
};
|
|
173
180
|
case "schedule_create":
|
|
@@ -158,6 +158,7 @@ declare module "swarm-sdk" {
|
|
|
158
158
|
hideCompleted?: boolean;
|
|
159
159
|
consecutiveErrorsMin?: number;
|
|
160
160
|
lastRunStatus?: "failed" | "succeeded";
|
|
161
|
+
includeFull?: boolean;
|
|
161
162
|
}): Promise<unknown>;
|
|
162
163
|
// --- scripts ---
|
|
163
164
|
script_search(args: { query?: string; scope?: ScriptScope; limit?: number }): Promise<unknown>;
|
|
@@ -140,6 +140,7 @@ declare module "swarm-sdk" {
|
|
|
140
140
|
hideCompleted?: boolean;
|
|
141
141
|
consecutiveErrorsMin?: number;
|
|
142
142
|
lastRunStatus?: "failed" | "succeeded";
|
|
143
|
+
includeFull?: boolean;
|
|
143
144
|
}): Promise<unknown>;
|
|
144
145
|
// --- scripts ---
|
|
145
146
|
script_search(args: { query?: string; scope?: ScriptScope; limit?: number }): Promise<unknown>;
|
|
@@ -128,6 +128,7 @@ describe("Phase 3 — /api/poll budget admission gate", () => {
|
|
|
128
128
|
if ("error" in body) throw new Error("unexpected error response");
|
|
129
129
|
expect(body.trigger?.type).toBe("task_assigned");
|
|
130
130
|
expect((body.trigger as { taskId: string }).taskId).toBe(task.id);
|
|
131
|
+
expect(getAgentById(worker.id)?.status).toBe("busy");
|
|
131
132
|
});
|
|
132
133
|
|
|
133
134
|
test("pending task trigger includes requester role and notes", async () => {
|
|
@@ -272,6 +273,25 @@ describe("Phase 3 — /api/poll budget admission gate", () => {
|
|
|
272
273
|
expect(claimedTriggers).toHaveLength(1);
|
|
273
274
|
expect(nullTriggers).toHaveLength(1);
|
|
274
275
|
});
|
|
276
|
+
|
|
277
|
+
test("pool claim marks the agent busy before the poll transaction returns", async () => {
|
|
278
|
+
const worker = createAgent({
|
|
279
|
+
name: "claim-status",
|
|
280
|
+
isLead: false,
|
|
281
|
+
status: "idle",
|
|
282
|
+
maxTasks: 1,
|
|
283
|
+
});
|
|
284
|
+
const pooled = createTaskExtended("claim status task", {});
|
|
285
|
+
|
|
286
|
+
expect(getAgentById(worker.id)?.status).toBe("idle");
|
|
287
|
+
|
|
288
|
+
const { body } = await callPoll(worker.id);
|
|
289
|
+
|
|
290
|
+
if ("error" in body) throw new Error("unexpected error response");
|
|
291
|
+
expect(body.trigger?.type).toBe("task_assigned");
|
|
292
|
+
expect((body.trigger as { taskId: string }).taskId).toBe(pooled.id);
|
|
293
|
+
expect(getAgentById(worker.id)?.status).toBe("busy");
|
|
294
|
+
});
|
|
275
295
|
});
|
|
276
296
|
|
|
277
297
|
// ─── MCP task-action accept tests ───────────────────────────────────────────
|
|
@@ -687,6 +687,38 @@ describe("mergeMcpConfig (issue #369)", () => {
|
|
|
687
687
|
expect((entry.headers as Record<string, string>)["X-Source-Task-Id"]).toBe(TASK_ID);
|
|
688
688
|
});
|
|
689
689
|
|
|
690
|
+
test("agent-swarm server gets X-Runtime-Instance-ID when a runtime identity is given", () => {
|
|
691
|
+
const base = {
|
|
692
|
+
mcpServers: {
|
|
693
|
+
"agent-swarm": {
|
|
694
|
+
type: "http",
|
|
695
|
+
url: "http://localhost:3013/mcp",
|
|
696
|
+
headers: { Authorization: "Bearer KEY", "X-Agent-ID": "a1" },
|
|
697
|
+
},
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
const merged = mergeMcpConfig(base, null, TASK_ID, undefined, "runtime-boot-1");
|
|
701
|
+
const agentSwarm = merged.mcpServers["agent-swarm"] as Record<string, unknown>;
|
|
702
|
+
expect((agentSwarm.headers as Record<string, string>)["X-Runtime-Instance-ID"]).toBe(
|
|
703
|
+
"runtime-boot-1",
|
|
704
|
+
);
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
test("no runtime identity → no X-Runtime-Instance-ID header", () => {
|
|
708
|
+
const base = {
|
|
709
|
+
mcpServers: {
|
|
710
|
+
"agent-swarm": {
|
|
711
|
+
type: "http",
|
|
712
|
+
url: "http://localhost:3013/mcp",
|
|
713
|
+
headers: { Authorization: "Bearer KEY", "X-Agent-ID": "a1" },
|
|
714
|
+
},
|
|
715
|
+
},
|
|
716
|
+
};
|
|
717
|
+
const merged = mergeMcpConfig(base, null, TASK_ID);
|
|
718
|
+
const agentSwarm = merged.mcpServers["agent-swarm"] as Record<string, unknown>;
|
|
719
|
+
expect((agentSwarm.headers as Record<string, string>)["X-Runtime-Instance-ID"]).toBeUndefined();
|
|
720
|
+
});
|
|
721
|
+
|
|
690
722
|
test("does not mutate the input baseConfig", () => {
|
|
691
723
|
const base = {
|
|
692
724
|
mcpServers: {
|
|
@@ -831,11 +831,33 @@ describe("ClaudeManagedAdapter (Phase 4) — repo provisioning + cost data", ()
|
|
|
831
831
|
expect(cost).toBeCloseTo(4.5, 10);
|
|
832
832
|
});
|
|
833
833
|
|
|
834
|
-
test("computeClaudeManagedCostUsd
|
|
834
|
+
test("computeClaudeManagedCostUsd uses sonnet-5 introductory rate through 2026-08-31", () => {
|
|
835
|
+
// 1M input tokens × $2.00/Mtok = $2.00
|
|
836
|
+
// 100k output tokens × $10.00/Mtok = $1.00
|
|
837
|
+
// total = $3.00. The introductory rate applies through 2026-08-31.
|
|
838
|
+
const cost = computeClaudeManagedCostUsd(
|
|
839
|
+
"claude-sonnet-5",
|
|
840
|
+
1_000_000,
|
|
841
|
+
100_000,
|
|
842
|
+
0,
|
|
843
|
+
0,
|
|
844
|
+
Date.parse("2026-08-31T23:59:59.999Z"),
|
|
845
|
+
);
|
|
846
|
+
expect(cost).toBeCloseTo(3.0, 10);
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
test("computeClaudeManagedCostUsd uses sonnet-5 standard rate from 2026-09-01", () => {
|
|
835
850
|
// 1M input tokens × $3.00/Mtok = $3.00
|
|
836
851
|
// 100k output tokens × $15.00/Mtok = $1.50
|
|
837
|
-
// total = $4.50
|
|
838
|
-
const cost = computeClaudeManagedCostUsd(
|
|
852
|
+
// total = $4.50 from the first instant of 2026-09-01 UTC.
|
|
853
|
+
const cost = computeClaudeManagedCostUsd(
|
|
854
|
+
"claude-sonnet-5",
|
|
855
|
+
1_000_000,
|
|
856
|
+
100_000,
|
|
857
|
+
0,
|
|
858
|
+
0,
|
|
859
|
+
Date.parse("2026-09-01T00:00:00.000Z"),
|
|
860
|
+
);
|
|
839
861
|
expect(cost).toBeCloseTo(4.5, 10);
|
|
840
862
|
});
|
|
841
863
|
|
|
@@ -882,10 +904,19 @@ describe("ClaudeManagedAdapter (Phase 4) — repo provisioning + cost data", ()
|
|
|
882
904
|
cacheWritePerMillion: 12.5,
|
|
883
905
|
});
|
|
884
906
|
expect(CLAUDE_MANAGED_MODEL_PRICING["claude-sonnet-5"]).toEqual({
|
|
885
|
-
inputPerMillion:
|
|
886
|
-
outputPerMillion:
|
|
887
|
-
cacheReadPerMillion: 0.
|
|
888
|
-
cacheWritePerMillion:
|
|
907
|
+
inputPerMillion: 2.0,
|
|
908
|
+
outputPerMillion: 10.0,
|
|
909
|
+
cacheReadPerMillion: 0.2,
|
|
910
|
+
cacheWritePerMillion: 2.5,
|
|
911
|
+
scheduledChange: {
|
|
912
|
+
effectiveAt: "2026-09-01T00:00:00.000Z",
|
|
913
|
+
pricing: {
|
|
914
|
+
inputPerMillion: 3.0,
|
|
915
|
+
outputPerMillion: 15.0,
|
|
916
|
+
cacheReadPerMillion: 0.3,
|
|
917
|
+
cacheWritePerMillion: 3.75,
|
|
918
|
+
},
|
|
919
|
+
},
|
|
889
920
|
});
|
|
890
921
|
expect(CLAUDE_MANAGED_MODEL_PRICING["claude-sonnet-4-6"]).toBeDefined();
|
|
891
922
|
expect(CLAUDE_MANAGED_MODEL_PRICING["claude-opus-4-7"]).toBeDefined();
|
|
@@ -1011,6 +1011,36 @@ describe("buildCodexConfig", () => {
|
|
|
1011
1011
|
expect(emitted.filter((e) => e.type === "raw_stderr")).toHaveLength(0);
|
|
1012
1012
|
});
|
|
1013
1013
|
|
|
1014
|
+
test("forwards the per-boot runtime identity to the swarm MCP headers", async () => {
|
|
1015
|
+
globalThis.fetch = stubFetch({ servers: [], total: 0 });
|
|
1016
|
+
const prev = process.env.SWARM_RUNTIME_INSTANCE_ID;
|
|
1017
|
+
process.env.SWARM_RUNTIME_INSTANCE_ID = "codex-runtime-1";
|
|
1018
|
+
try {
|
|
1019
|
+
const merged = await buildCodexConfig(cfg(), "gpt-5.4", () => {});
|
|
1020
|
+
const headers = (merged.mcp_servers as Record<string, Record<string, unknown>>)["agent-swarm"]
|
|
1021
|
+
?.http_headers as Record<string, string>;
|
|
1022
|
+
expect(headers["X-Runtime-Instance-ID"]).toBe("codex-runtime-1");
|
|
1023
|
+
} finally {
|
|
1024
|
+
if (prev === undefined) delete process.env.SWARM_RUNTIME_INSTANCE_ID;
|
|
1025
|
+
else process.env.SWARM_RUNTIME_INSTANCE_ID = prev;
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
test("no runtime identity in the environment → no runtime header", async () => {
|
|
1030
|
+
globalThis.fetch = stubFetch({ servers: [], total: 0 });
|
|
1031
|
+
const prev = process.env.SWARM_RUNTIME_INSTANCE_ID;
|
|
1032
|
+
delete process.env.SWARM_RUNTIME_INSTANCE_ID;
|
|
1033
|
+
try {
|
|
1034
|
+
const merged = await buildCodexConfig(cfg(), "gpt-5.4", () => {});
|
|
1035
|
+
const headers = (merged.mcp_servers as Record<string, Record<string, unknown>>)["agent-swarm"]
|
|
1036
|
+
?.http_headers as Record<string, string>;
|
|
1037
|
+
expect(headers["X-Runtime-Instance-ID"]).toBeUndefined();
|
|
1038
|
+
} finally {
|
|
1039
|
+
if (prev === undefined) delete process.env.SWARM_RUNTIME_INSTANCE_ID;
|
|
1040
|
+
else process.env.SWARM_RUNTIME_INSTANCE_ID = prev;
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1014
1044
|
test("one HTTP-transport installed server → both 'agent-swarm' and installed server present", async () => {
|
|
1015
1045
|
globalThis.fetch = stubFetch({
|
|
1016
1046
|
servers: [
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { unlinkSync } from "node:fs";
|
|
3
|
+
import {
|
|
4
|
+
createServer as createHttpServer,
|
|
5
|
+
type IncomingMessage,
|
|
6
|
+
type Server,
|
|
7
|
+
type ServerResponse,
|
|
8
|
+
} from "node:http";
|
|
9
|
+
import { type CreateTaskOptions, closeDb, createTaskExtended, getDb, initDb } from "../be/db";
|
|
10
|
+
import { handleMcpBridge } from "../http/mcp-bridge";
|
|
11
|
+
import { getPathSegments } from "../http/utils";
|
|
12
|
+
|
|
13
|
+
const TEST_DB_PATH = "./test-create-task-validation.sqlite";
|
|
14
|
+
|
|
15
|
+
let server: Server;
|
|
16
|
+
let baseUrl: string;
|
|
17
|
+
|
|
18
|
+
function taskCount(): number {
|
|
19
|
+
return (getDb().prepare("SELECT COUNT(*) AS c FROM agent_tasks").get() as { c: number }).c;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
beforeAll(async () => {
|
|
23
|
+
initDb(TEST_DB_PATH);
|
|
24
|
+
server = createHttpServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
25
|
+
const myAgentId = req.headers["x-agent-id"] as string | undefined;
|
|
26
|
+
const pathSegments = getPathSegments(req.url || "");
|
|
27
|
+
const ok = await handleMcpBridge(req, res, pathSegments, undefined, myAgentId);
|
|
28
|
+
if (!ok) {
|
|
29
|
+
res.writeHead(404);
|
|
30
|
+
res.end("Not Found");
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
await new Promise<void>((resolve) => server.listen(0, resolve));
|
|
34
|
+
const addr = server.address();
|
|
35
|
+
if (!addr || typeof addr === "string") throw new Error("no port");
|
|
36
|
+
baseUrl = `http://127.0.0.1:${addr.port}`;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterAll(() => {
|
|
40
|
+
server.close();
|
|
41
|
+
closeDb();
|
|
42
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
43
|
+
try {
|
|
44
|
+
unlinkSync(TEST_DB_PATH + suffix);
|
|
45
|
+
} catch {
|
|
46
|
+
// ignore
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("scripts-bridge input validation", () => {
|
|
52
|
+
// REGRESSION TEST for the 2026-08-18 incident: an inline script POSTed
|
|
53
|
+
// priority: "high" to /api/mcp-bridge; the bridge invoked the send-task
|
|
54
|
+
// handler without parsing args, the TEXT value landed in the INTEGER
|
|
55
|
+
// priority column, and response validation then 500'd every task listing.
|
|
56
|
+
test("raw priority:'high' through the bridge returns 400 and writes no row", async () => {
|
|
57
|
+
const before = taskCount();
|
|
58
|
+
const res = await fetch(`${baseUrl}/api/mcp-bridge`, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: { "Content-Type": "application/json", "x-agent-id": "test-agent" },
|
|
61
|
+
body: JSON.stringify({
|
|
62
|
+
tool: "send-task",
|
|
63
|
+
args: { task: "incident regression probe", priority: "high" },
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
expect(res.status).toBe(400);
|
|
67
|
+
const body = (await res.json()) as { error?: string };
|
|
68
|
+
expect(body.error).toContain("Invalid arguments for tool 'send-task'");
|
|
69
|
+
expect(taskCount()).toBe(before);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("createTaskExtended input validation", () => {
|
|
74
|
+
test("string priority throws and writes no row", () => {
|
|
75
|
+
const before = taskCount();
|
|
76
|
+
expect(() =>
|
|
77
|
+
createTaskExtended("bad priority", { priority: "high" } as unknown as CreateTaskOptions),
|
|
78
|
+
).toThrow();
|
|
79
|
+
expect(taskCount()).toBe(before);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("priority 101 throws", () => {
|
|
83
|
+
expect(() => createTaskExtended("too high", { priority: 101 })).toThrow();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("priority -1 throws", () => {
|
|
87
|
+
expect(() => createTaskExtended("too low", { priority: -1 })).toThrow();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("non-integer priority throws", () => {
|
|
91
|
+
expect(() => createTaskExtended("fractional", { priority: 49.5 })).toThrow();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("absent priority defaults to 50", () => {
|
|
95
|
+
const t = createTaskExtended("default priority");
|
|
96
|
+
expect(t.priority).toBe(50);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("explicit priority 0 stays 0", () => {
|
|
100
|
+
const t = createTaskExtended("zero priority", { priority: 0 });
|
|
101
|
+
expect(t.priority).toBe(0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("unknown keys are stripped and the task is still created", () => {
|
|
105
|
+
const t = createTaskExtended("extra keys", {
|
|
106
|
+
priority: 10,
|
|
107
|
+
nonsense: true,
|
|
108
|
+
} as unknown as CreateTaskOptions);
|
|
109
|
+
expect(t.priority).toBe(10);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("empty task throws", () => {
|
|
113
|
+
expect(() => createTaskExtended("")).toThrow();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("valid full options pass through unchanged", () => {
|
|
117
|
+
const t = createTaskExtended("full options", {
|
|
118
|
+
priority: 90,
|
|
119
|
+
tags: ["urgent"],
|
|
120
|
+
taskType: "bug",
|
|
121
|
+
status: "backlog",
|
|
122
|
+
source: "api",
|
|
123
|
+
});
|
|
124
|
+
expect(t.priority).toBe(90);
|
|
125
|
+
expect(t.status).toBe("backlog");
|
|
126
|
+
expect(t.tags).toEqual(["urgent"]);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
awaitCredentials,
|
|
4
|
+
BootMaxWaitExceededError,
|
|
5
|
+
retryBootStep,
|
|
6
|
+
} from "../commands/credential-wait";
|
|
3
7
|
|
|
4
8
|
/**
|
|
5
9
|
* Capture-only logger so test output stays clean and we can assert on
|
|
@@ -280,3 +284,65 @@ describe("awaitCredentials", () => {
|
|
|
280
284
|
expect(probedPaths).toContain("/home/worker/.codex/auth.json");
|
|
281
285
|
});
|
|
282
286
|
});
|
|
287
|
+
|
|
288
|
+
describe("retryBootStep", () => {
|
|
289
|
+
test("returns after the first success without sleeping", async () => {
|
|
290
|
+
const sleeper = makeSleeper();
|
|
291
|
+
let calls = 0;
|
|
292
|
+
await retryBootStep(
|
|
293
|
+
async () => {
|
|
294
|
+
calls++;
|
|
295
|
+
},
|
|
296
|
+
{ sleep: sleeper.fn, log: () => {} },
|
|
297
|
+
);
|
|
298
|
+
expect(calls).toBe(1);
|
|
299
|
+
expect(sleeper.calls).toEqual([]);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("retries with exponential backoff until success", async () => {
|
|
303
|
+
const sleeper = makeSleeper();
|
|
304
|
+
const logger = makeLogger();
|
|
305
|
+
let calls = 0;
|
|
306
|
+
await retryBootStep(
|
|
307
|
+
async () => {
|
|
308
|
+
calls++;
|
|
309
|
+
if (calls < 3) throw new Error(`transient ${calls}`);
|
|
310
|
+
},
|
|
311
|
+
{ sleep: sleeper.fn, log: logger.fn, initialDelayMs: 100, maxDelayMs: 1000 },
|
|
312
|
+
);
|
|
313
|
+
expect(calls).toBe(3);
|
|
314
|
+
expect(sleeper.calls).toEqual([100, 200]);
|
|
315
|
+
expect(logger.lines).toHaveLength(2);
|
|
316
|
+
expect(logger.lines[0]).toContain("attempt 1/5");
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("propagates the last error once attempts are exhausted", async () => {
|
|
320
|
+
const sleeper = makeSleeper();
|
|
321
|
+
let calls = 0;
|
|
322
|
+
await expect(
|
|
323
|
+
retryBootStep(
|
|
324
|
+
async () => {
|
|
325
|
+
calls++;
|
|
326
|
+
throw new Error(`down ${calls}`);
|
|
327
|
+
},
|
|
328
|
+
{ attempts: 3, sleep: sleeper.fn, log: () => {} },
|
|
329
|
+
),
|
|
330
|
+
).rejects.toThrow("down 3");
|
|
331
|
+
expect(calls).toBe(3);
|
|
332
|
+
// No sleep after the final attempt.
|
|
333
|
+
expect(sleeper.calls).toHaveLength(2);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test("caps the backoff at maxDelayMs", async () => {
|
|
337
|
+
const sleeper = makeSleeper();
|
|
338
|
+
let calls = 0;
|
|
339
|
+
await retryBootStep(
|
|
340
|
+
async () => {
|
|
341
|
+
calls++;
|
|
342
|
+
if (calls < 5) throw new Error("transient");
|
|
343
|
+
},
|
|
344
|
+
{ sleep: sleeper.fn, log: () => {}, initialDelayMs: 400, maxDelayMs: 1000 },
|
|
345
|
+
);
|
|
346
|
+
expect(sleeper.calls).toEqual([400, 800, 1000, 1000]);
|
|
347
|
+
});
|
|
348
|
+
});
|