@desplega.ai/agent-swarm 2.4.2 → 2.4.4
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-dv7hmx4r.js → acp-adapter-thq04adh.js} +4 -5
- package/dist/{actions-51nc1gny.js → actions-j6ywh6x5.js} +18 -18
- package/dist/{amp-adapter-abp5tdx8.js → amp-adapter-k8b5xmj8.js} +3 -3
- package/dist/{app-9h14qvr6.js → app-rb7vc02s.js} +3 -3
- package/dist/{assistant-wv1gz3mt.js → assistant-ftta1zjf.js} +20 -20
- package/dist/{boot-reembed-6yn53ms9.js → boot-reembed-srp6v6af.js} +1 -1
- package/dist/{boot-reembed-7fcka4z5.js → boot-reembed-xns7h5h9.js} +1 -1
- package/dist/{boot-scrub-sweep-pm1580ax.js → boot-scrub-sweep-3qqrh2mz.js} +3 -3
- package/dist/{boot-seeds-g5hfc1x8.js → boot-seeds-9ks8hce7.js} +4 -4
- package/dist/{claude-adapter-gf3kxhf0.js → claude-adapter-k5ksyres.js} +4 -4
- package/dist/{claude-managed-setup-h00hsgtz.js → claude-managed-setup-n5a5eqks.js} +1 -1
- package/dist/{claude-sdk-session-s2k1qd8y.js → claude-sdk-session-2j3j4kwn.js} +9822 -9749
- package/dist/{cli-pkht5hap.js → cli-1d70crsy.js} +2 -2
- package/dist/{cli-r99kc9yq.js → cli-1ephgwnq.js} +3 -3
- package/dist/{cli-7225gwj7.js → cli-1m17dc9d.js} +1 -1
- package/dist/{cli-bcj753sq.js → cli-2v045xyf.js} +3 -3
- package/dist/{cli-h38rjv87.js → cli-2y91wnbh.js} +12 -7
- package/dist/{cli-sv5gb0yb.js → cli-2zqpkvtt.js} +1 -1
- package/dist/{cli-xbzvet96.js → cli-36ckw0wx.js} +5 -4
- package/dist/{cli-8sxqsbbq.js → cli-37yb7307.js} +1 -1
- package/dist/{cli-hyfw2j1e.js → cli-3cgngyd8.js} +11 -11
- package/dist/{cli-a4gvwttf.js → cli-44159k28.js} +2 -2
- package/dist/{cli-7awf1fp8.js → cli-4dg55wsk.js} +3 -3
- package/dist/{cli-mhywgrrz.js → cli-4sydckyj.js} +1 -1
- package/dist/{cli-y2x52gs2.js → cli-51y09c6p.js} +20 -17
- package/dist/{cli-ahtpy9kf.js → cli-55qmbp6z.js} +10 -5
- package/dist/{cli-pw7v69vp.js → cli-625tb7aq.js} +1 -1
- package/dist/{cli-x5mq1fyq.js → cli-74c1wpm0.js} +3 -3
- package/dist/{cli-b1dfr1m4.js → cli-7a6p4e65.js} +1 -1
- package/dist/{cli-hqhzg1qq.js → cli-82d3nkc3.js} +1 -1
- package/dist/{cli-nmqdc2t3.js → cli-9fhkkpj5.js} +78 -12
- package/dist/{cli-h93c9vb3.js → cli-amyhm3gj.js} +1 -1
- package/dist/{cli-0qvtffe9.js → cli-b8k7epj1.js} +6 -6
- package/dist/{cli-1pa3rz69.js → cli-ba8ne1es.js} +6 -1
- package/dist/{cli-g0hgtz4p.js → cli-bnz0r3kc.js} +3 -3
- package/dist/{cli-0hyhdkdk.js → cli-cebvbc9t.js} +4 -1
- package/dist/{cli-efaqv1c7.js → cli-cynvc9fj.js} +35 -7
- package/dist/{cli-8zwe9mpk.js → cli-dqb4n629.js} +10 -10
- package/dist/{cli-7a91w22y.js → cli-e8xyjvfc.js} +1 -1
- package/dist/{cli-s6vr7r2d.js → cli-esxerzg7.js} +1 -1
- package/dist/{cli-w9g4sz81.js → cli-gf2850q7.js} +1 -1
- package/dist/{cli-10hhg0km.js → cli-gqvh8d8a.js} +7 -7
- package/dist/{cli-t30yj95q.js → cli-hj3debgw.js} +1 -1
- package/dist/{cli-fpmsqq59.js → cli-nc35pxb7.js} +3 -3
- package/dist/{cli-d3qqdy4c.js → cli-p20r8w42.js} +16 -14
- package/dist/{cli-shvfedvm.js → cli-pccfmtb5.js} +5 -5
- package/dist/{cli-02qm7src.js → cli-q6r7t299.js} +1037 -299
- package/dist/{cli-2wag8973.js → cli-qeaq3j6t.js} +1 -1
- package/dist/{cli-nn47d7e8.js → cli-qkhc5aqy.js} +15 -15
- package/dist/{cli-faypxz7q.js → cli-t7cyef5g.js} +4 -4
- package/dist/{cli-z4wd2ftv.js → cli-te6b5wvg.js} +149 -184
- package/dist/{cli-5rk36sjf.js → cli-v3yr2gbg.js} +119 -44
- package/dist/{cli-239yjk27.js → cli-vdm20wej.js} +5 -5
- package/dist/{cli-zfg9enh9.js → cli-vfha21c9.js} +6 -1
- package/dist/{cli-rdyw3mct.js → cli-vq97g1jq.js} +6 -5
- package/dist/{cli-n85dqg6d.js → cli-w6nadkh4.js} +4 -4
- package/dist/{cli-99jez72r.js → cli-w9etnnxk.js} +6 -1
- package/dist/{cli-saw9a6f6.js → cli-xc27fwzg.js} +2 -2
- package/dist/{cli-ftyypbg5.js → cli-xj0re5tm.js} +1 -1
- package/dist/{cli-fyzjmseb.js → cli-zrk6jxv8.js} +1 -1
- package/dist/{cli-y8n5k515.js → cli-zrwpctp4.js} +28 -28
- package/dist/cli.js +44 -19
- package/dist/{codex-adapter-ttygw382.js → codex-adapter-968xwfy7.js} +2 -2
- package/dist/{codex-hook-x7nejyrr.js → codex-hook-e2he7aca.js} +2 -2
- package/dist/{codex-session-runner-60b58vv3.js → codex-session-runner-ht4z99fa.js} +2 -2
- package/dist/{commands-edmdwa4x.js → commands-pcymaeg9.js} +3 -3
- package/dist/{controller-m263fg43.js → controller-fpjyk46r.js} +3 -3
- package/dist/{controllers-connect-5fym8sw1.js → controllers-connect-mwzx1fh6.js} +19 -19
- package/dist/{cursor-adapter-cz6nm6mj.js → cursor-adapter-cp4s2e2f.js} +2 -2
- package/dist/{db-2y0mgstc.js → db-sgd12bt9.js} +3 -3
- package/dist/{db-nn0hcmx5.js → db-xsjg9jqq.js} +2 -2
- package/dist/{dispatcher-0a9wwjcx.js → dispatcher-x0jfw2c9.js} +17 -17
- package/dist/{dsh-adapter-3n3net33.js → dsh-adapter-jrq7ma7h.js} +2 -2
- package/dist/{e2b-thdcxtjd.js → e2b-16d6ch39.js} +1 -1
- package/dist/{e2b-7am6tmq6.js → e2b-zn0b4w5w.js} +1 -1
- package/dist/{external-channel-zhvt95n3.js → external-channel-8njj4h3b.js} +2 -2
- package/dist/{grok-adapter-qnr1hp2y.js → grok-adapter-kkr97v2f.js} +5 -6
- package/dist/handlers-0pvee9nw.js +39 -0
- package/dist/{heartbeat-gz97r7ac.js → heartbeat-wd03zhn0.js} +20 -20
- package/dist/{hook-1mmdgjqs.js → hook-f0vghj42.js} +10 -5
- package/dist/{hook-x8rthca4.js → hook-xzj2ts2p.js} +2 -2
- package/dist/{http-r8f85xr9.js → http-4hwxv62p.js} +261 -123
- package/dist/{index-va3y63es.js → index-6srwvd5b.js} +1 -2
- package/dist/{index-y7rbcrc1.js → index-97q4xn03.js} +20 -11
- package/dist/{index-s98037v0.js → index-cbxkmfc5.js} +1 -2
- package/dist/{index-vzvfqzfj.js → index-gdh8jrdm.js} +17 -17
- package/dist/{index-aex0eba9.js → index-hg0x2cdv.js} +17 -17
- package/dist/index-thm0y6a8.js +41 -0
- package/dist/{index-smxyk04s.js → index-ww2vznkp.js} +1 -1
- package/dist/{keepalive-64c0f34c.js → keepalive-hrh7xnr9.js} +3 -3
- package/dist/{lead-b1da0f97.js → lead-ee8vazz2.js} +30 -31
- package/dist/{maintenance-mnxpar12.js → maintenance-e8wnwnqx.js} +1 -1
- package/dist/{oauth-refresh-sweep-36hs2v4h.js → oauth-refresh-sweep-zm0yj4sj.js} +2 -2
- package/dist/{onboard-xes8de61.js → onboard-dtwcq603.js} +3 -3
- package/dist/{opencode-adapter-3s7ax02e.js → opencode-adapter-x4tqzhkh.js} +3 -3
- package/dist/{otel-impl-fzc57kcq.js → otel-impl-w3fbhpn0.js} +1 -1
- package/dist/{pi-mono-adapter-ytp7gz8b.js → pi-mono-adapter-mcfkn9vf.js} +3 -3
- package/dist/{pricing-refresh-1nek2w7b.js → pricing-refresh-j38dehfs.js} +3 -3
- package/dist/{rbac-roles-ttyfaep2.js → rbac-roles-ae8yttq4.js} +3 -3
- package/dist/{rbac-roles-sqefb5hw.js → rbac-roles-ejxx9b35.js} +2 -2
- package/dist/{render-v2-vb527jwn.js → render-v2-n16n0phe.js} +3 -3
- package/dist/{resume-jcdzg1bn.js → resume-z0fgjc38.js} +5 -5
- package/dist/{session-access-zsp4wsp0.js → session-access-cv3mq6mc.js} +6 -4
- package/dist/{setup-1nb2zqd3.js → setup-mafhj3yt.js} +1 -1
- package/dist/{work-objects-61w08fwr.js → work-objects-8b86mgjp.js} +21 -21
- package/dist/{worker-c1e68hpa.js → worker-16s75hm5.js} +30 -31
- package/dist/worker-token-xf11yyds.js +136 -0
- package/openapi.json +295 -12
- package/package.json +5 -5
- package/src/apps/action-runner.ts +4 -0
- package/src/apps/catalog.generated.json +3 -0
- package/src/apps/page-validator.ts +8 -2
- package/src/be/db.ts +7 -4
- package/src/be/migrations/231_slack_tasks_last_updated_index.sql +5 -0
- package/src/be/migrations/232_agent_session_tokens.sql +41 -0
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +10 -1
- package/src/be/swarm-config-guard.ts +16 -0
- package/src/be/users.ts +169 -12
- package/src/cli.tsx +24 -0
- package/src/commands/worker-token.ts +164 -0
- package/src/github/handlers.ts +6 -5
- package/src/hooks/hook.ts +2 -1
- package/src/http/apps.ts +43 -5
- package/src/http/codex-oauth-keep-warm.ts +27 -9
- package/src/http/config.ts +5 -3
- package/src/http/core.ts +2 -0
- package/src/http/session-access.ts +51 -2
- package/src/http/sessions.ts +249 -10
- package/src/http/tasks.ts +4 -32
- package/src/providers/acp-adapter.ts +33 -13
- package/src/providers/acp-swarm-events.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +4 -4
- package/src/providers/codex-oauth/storage.ts +1 -1
- package/src/providers/opencode-adapter.ts +2 -2
- package/src/providers/pi-mono-extension.ts +2 -1
- package/src/rbac/legacy-policy.ts +3 -1
- package/src/rbac/permissions.ts +4 -0
- package/src/tests/acp-adapter.test.ts +10 -23
- package/src/tests/acp-fake-agent.ts +36 -0
- package/src/tests/acp-swarm-events.test.ts +1 -1
- package/src/tests/agent-session-tokens.test.ts +452 -0
- package/src/tests/apps-action-parent-task.test.ts +295 -0
- package/src/tests/claude-stop-hook.test.ts +47 -0
- package/src/tests/codex-adapter.test.ts +42 -0
- package/src/tests/codex-oauth-keep-warm.test.ts +85 -1
- package/src/tests/config-session-auth.test.ts +14 -0
- package/src/tests/docs-index-footprint.test.js +2 -2
- package/src/tests/env-flag.test.ts +15 -1
- package/src/tests/github-handlers-cancel-config.test.ts +29 -8
- package/src/tests/github-handlers-inline-comments.test.ts +9 -2
- package/src/tests/grok-adapter.test.ts +3 -3
- package/src/tests/heartbeat-jev-gate.test.ts +23 -0
- package/src/tests/migration-232-agent-session-tokens.test.ts +67 -0
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/runtime-child-token-cleanup.test.ts +3 -3
- package/src/tests/script-connections.test.ts +4 -0
- package/src/tests/session-route-posture.test.ts +2 -0
- package/src/tests/skill-script-refs.test.ts +63 -0
- package/src/tests/slack-render-v2-query-plans.test.ts +147 -0
- package/src/tests/worker-token-cli.test.ts +76 -0
- package/src/tests/workflow-executors.test.ts +4 -2
- package/src/tests/workflow-system-one-extraction.test.ts +805 -0
- package/src/types.ts +2 -1
- package/src/utils/env-flag.ts +9 -0
- package/src/utils/request-auth-context.ts +2 -1
- package/src/workflows/definition.ts +7 -1
- package/src/workflows/engine.ts +18 -12
- package/src/workflows/executors/index.ts +1 -0
- package/src/workflows/executors/registry.ts +2 -0
- package/src/workflows/executors/system-one-decision.ts +56 -255
- package/src/workflows/executors/system-one-extract-wire.ts +57 -0
- package/src/workflows/executors/system-one-extraction.ts +830 -0
- package/src/workflows/executors/system-one-providers.ts +277 -110
- package/src/workflows/executors/system-one-transport.ts +264 -0
- package/templates/official/lead/HEARTBEAT.md +1 -1
- package/templates/skills/apps/SKILL.md +4 -1
- package/templates/skills/apps/content.md +4 -1
- package/templates/skills/code-quality/content.md +9 -14
- package/templates/skills/heartbeat-runbook/content.md +3 -3
- package/dist/cli-v1sjhn52.js +0 -7
- package/dist/handlers-dkcarkd0.js +0 -39
- package/dist/index-5jecgp3n.js +0 -41
package/src/http/tasks.ts
CHANGED
|
@@ -87,6 +87,7 @@ import { buildTaskRequestedBy, TaskRequestedBySchema } from "./requested-by";
|
|
|
87
87
|
import { bodyTask, route } from "./route-def";
|
|
88
88
|
import {
|
|
89
89
|
canReadSession,
|
|
90
|
+
denyParentAttach,
|
|
90
91
|
readableByFor,
|
|
91
92
|
resolveSessionActor,
|
|
92
93
|
SESSION_READ_DENIED,
|
|
@@ -779,16 +780,6 @@ const updateTaskTitleRoute = route({
|
|
|
779
780
|
},
|
|
780
781
|
});
|
|
781
782
|
|
|
782
|
-
/** True unless the request acts for a user who lacks write access to the task's session. */
|
|
783
|
-
async function hasSessionWrite(
|
|
784
|
-
req: IncomingMessage,
|
|
785
|
-
myAgentId: string | undefined,
|
|
786
|
-
taskId: string,
|
|
787
|
-
): Promise<boolean> {
|
|
788
|
-
const access = await sessionAccessFor(await resolveSessionActor(req, myAgentId), taskId);
|
|
789
|
-
return hasSessionAccess(access, "write");
|
|
790
|
-
}
|
|
791
|
-
|
|
792
783
|
async function canSteerTask(
|
|
793
784
|
req: IncomingMessage,
|
|
794
785
|
myAgentId: string | undefined,
|
|
@@ -1069,29 +1060,10 @@ export async function handleTasks(
|
|
|
1069
1060
|
? await getTaskById(parsed.body.parentTaskId)
|
|
1070
1061
|
: null;
|
|
1071
1062
|
// The child inherits the parent's thread context, so an agent caller must own or
|
|
1072
|
-
// have created the parent
|
|
1063
|
+
// have created the parent, and a follow-up in someone else's session needs a
|
|
1064
|
+
// write share. The same gate guards app actions that chain under a task.
|
|
1073
1065
|
const principal = await resolveTaskWritePrincipal(req, myAgentId);
|
|
1074
|
-
if (
|
|
1075
|
-
parentTask &&
|
|
1076
|
-
!can({
|
|
1077
|
-
principal,
|
|
1078
|
-
verb: "task.parent.attach",
|
|
1079
|
-
resource: {
|
|
1080
|
-
kind: "task",
|
|
1081
|
-
taskId: parentTask.id,
|
|
1082
|
-
requestedByUserId: parentTask.requestedByUserId,
|
|
1083
|
-
creatorAgentId: parentTask.creatorAgentId,
|
|
1084
|
-
agentId: parentTask.agentId,
|
|
1085
|
-
},
|
|
1086
|
-
source: "http",
|
|
1087
|
-
}).allow
|
|
1088
|
-
) {
|
|
1089
|
-
jsonError(res, `You don't have context for parent task ${parentTask.id}.`, 403);
|
|
1090
|
-
return true;
|
|
1091
|
-
}
|
|
1092
|
-
// A follow-up in someone else's session needs a write share.
|
|
1093
|
-
if (parentTask && !(await hasSessionWrite(req, myAgentId, parentTask.id))) {
|
|
1094
|
-
jsonError(res, SESSION_WRITE_DENIED, 403);
|
|
1066
|
+
if (parentTask && (await denyParentAttach(req, res, myAgentId, parentTask, principal))) {
|
|
1095
1067
|
return true;
|
|
1096
1068
|
}
|
|
1097
1069
|
// A chat-sdk contextKey binds the task to a chat thread that its assignee and creator
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
+
// `/unstable` keeps the full ACP v1 surface the 1.x default import carried:
|
|
2
|
+
// turn `usage` on the prompt response is unstable, and the stable entrypoint
|
|
3
|
+
// does not type it. Both entrypoints speak ACP v1 on the wire.
|
|
1
4
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
5
|
+
client as acpClient,
|
|
6
|
+
type ClientContext,
|
|
4
7
|
type McpServer,
|
|
8
|
+
methods,
|
|
5
9
|
ndJsonStream,
|
|
6
10
|
PROTOCOL_VERSION,
|
|
7
11
|
type RequestPermissionRequest,
|
|
8
12
|
type RequestPermissionResponse,
|
|
9
13
|
type SessionConfigOption,
|
|
10
14
|
type SessionNotification,
|
|
15
|
+
type SetSessionConfigOptionRequest,
|
|
16
|
+
type SetSessionConfigOptionResponse,
|
|
11
17
|
type Usage,
|
|
12
|
-
} from "@agentclientprotocol/sdk";
|
|
18
|
+
} from "@agentclientprotocol/sdk/unstable";
|
|
13
19
|
import pkg from "../../package.json";
|
|
14
20
|
import type { AcpSessionConfigOption } from "../types";
|
|
15
21
|
import { mintAcpSessionToken, revokeAcpSessionToken } from "../utils/acp-session-token";
|
|
@@ -154,7 +160,7 @@ function boundedAcpLogScalar(value: unknown): unknown {
|
|
|
154
160
|
return typeof value === "string" ? value.slice(0, 500) : value;
|
|
155
161
|
}
|
|
156
162
|
|
|
157
|
-
class SwarmAcpClient
|
|
163
|
+
class SwarmAcpClient {
|
|
158
164
|
/** Latest cumulative USD cost the target reported for the session. */
|
|
159
165
|
reportedCostUsd: number | null = null;
|
|
160
166
|
|
|
@@ -199,7 +205,7 @@ class ACPSession implements ProviderSession {
|
|
|
199
205
|
private ephemeralTokenId: string | null = null;
|
|
200
206
|
|
|
201
207
|
constructor(
|
|
202
|
-
private readonly
|
|
208
|
+
private readonly agent: ClientContext,
|
|
203
209
|
private readonly client: SwarmAcpClient,
|
|
204
210
|
private readonly process: Bun.Subprocess<"pipe", "pipe", "pipe">,
|
|
205
211
|
private readonly config: ProviderSessionConfig,
|
|
@@ -234,7 +240,7 @@ class ACPSession implements ProviderSession {
|
|
|
234
240
|
if (this.aborted) return;
|
|
235
241
|
this.aborted = true;
|
|
236
242
|
try {
|
|
237
|
-
await this.
|
|
243
|
+
await this.agent.notify(methods.agent.session.cancel, { sessionId: this.sessionId });
|
|
238
244
|
await Promise.race([this.completionPromise, Bun.sleep(ACP_CANCEL_SETTLE_MS)]);
|
|
239
245
|
} catch (err) {
|
|
240
246
|
this.emit({
|
|
@@ -282,7 +288,7 @@ class ACPSession implements ProviderSession {
|
|
|
282
288
|
private async runPrompt(): Promise<void> {
|
|
283
289
|
let result: ProviderResult;
|
|
284
290
|
try {
|
|
285
|
-
const response = await this.
|
|
291
|
+
const response = await this.agent.request(methods.agent.session.prompt, {
|
|
286
292
|
sessionId: this.sessionId,
|
|
287
293
|
prompt: [{ type: "text", text: this.config.prompt }],
|
|
288
294
|
});
|
|
@@ -441,11 +447,16 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
441
447
|
else preSessionEvents.push(event);
|
|
442
448
|
}, target);
|
|
443
449
|
const stream = ndJsonStream(fileSinkWritableStream(proc.stdin), proc.stdout);
|
|
444
|
-
const
|
|
450
|
+
const agent = acpClient()
|
|
451
|
+
.onRequest(methods.client.session.requestPermission, ({ params }) =>
|
|
452
|
+
client.requestPermission(params),
|
|
453
|
+
)
|
|
454
|
+
.onNotification(methods.client.session.update, ({ params }) => client.sessionUpdate(params))
|
|
455
|
+
.connect(stream).agent;
|
|
445
456
|
|
|
446
457
|
let ephemeralToken: { tokenId: string; plaintext: string } | null = null;
|
|
447
458
|
try {
|
|
448
|
-
await
|
|
459
|
+
await agent.request(methods.agent.initialize, {
|
|
449
460
|
protocolVersion: PROTOCOL_VERSION,
|
|
450
461
|
clientInfo: { name: "agent-swarm", version: pkg.version },
|
|
451
462
|
clientCapabilities: { session: { configOptions: { boolean: {} } } },
|
|
@@ -473,7 +484,7 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
473
484
|
// shape for an ACP agent with no network route to the swarm API. Gated on
|
|
474
485
|
// `mcpCapabilities.acp` and UNSTABLE; not adopted here.
|
|
475
486
|
const sessionMeta = target.sessionMeta?.(config);
|
|
476
|
-
const newSession = await
|
|
487
|
+
const newSession = await agent.request(methods.agent.session.new, {
|
|
477
488
|
...(sessionMeta ? { _meta: sessionMeta } : {}),
|
|
478
489
|
cwd: config.cwd,
|
|
479
490
|
mcpServers: [
|
|
@@ -491,13 +502,16 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
491
502
|
],
|
|
492
503
|
});
|
|
493
504
|
const configOptions = await applyConfiguredOptions(
|
|
494
|
-
|
|
505
|
+
{
|
|
506
|
+
setSessionConfigOption: (params) =>
|
|
507
|
+
agent.request(methods.agent.session.setConfigOption, params),
|
|
508
|
+
},
|
|
495
509
|
newSession.sessionId,
|
|
496
510
|
newSession.configOptions ?? [],
|
|
497
511
|
target.configuredOptions(config),
|
|
498
512
|
);
|
|
499
513
|
session = new ACPSession(
|
|
500
|
-
|
|
514
|
+
agent,
|
|
501
515
|
client,
|
|
502
516
|
proc,
|
|
503
517
|
config,
|
|
@@ -537,8 +551,14 @@ export class ACPAdapter implements ProviderAdapter {
|
|
|
537
551
|
}
|
|
538
552
|
}
|
|
539
553
|
|
|
554
|
+
export interface AcpConfigOptionSetter {
|
|
555
|
+
setSessionConfigOption(
|
|
556
|
+
params: SetSessionConfigOptionRequest,
|
|
557
|
+
): Promise<SetSessionConfigOptionResponse>;
|
|
558
|
+
}
|
|
559
|
+
|
|
540
560
|
export async function applyConfiguredOptions(
|
|
541
|
-
connection:
|
|
561
|
+
connection: AcpConfigOptionSetter,
|
|
542
562
|
sessionId: string,
|
|
543
563
|
advertised: SessionConfigOption[],
|
|
544
564
|
configured: Record<string, string | boolean>,
|
|
@@ -44,7 +44,7 @@ import type {
|
|
|
44
44
|
* default; the image installs exactly this version and the per-task settings
|
|
45
45
|
* set `amp.updates.mode: "disabled"`. Bump it deliberately, with a live run.
|
|
46
46
|
*/
|
|
47
|
-
export const AMP_PACKAGE = "@ampcode/cli@0.0.
|
|
47
|
+
export const AMP_PACKAGE = "@ampcode/cli@0.0.1792506219-g025e7e";
|
|
48
48
|
|
|
49
49
|
/** MCP server name. Amp exposes its tools as `agent_swarm` / `store_progress` (dashes become underscores). */
|
|
50
50
|
const AMP_MCP_SERVER_NAME = "agent-swarm";
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
clampContextPercent,
|
|
38
38
|
computeContextUsedUnified,
|
|
39
39
|
} from "../utils/context-window";
|
|
40
|
-
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
40
|
+
import { isEnvFlagEnabled, isSessionSummarySkipped } from "../utils/env-flag";
|
|
41
41
|
import { SessionErrorTracker } from "../utils/error-tracker";
|
|
42
42
|
import { summarizeSession as runSummarize } from "../utils/internal-ai";
|
|
43
43
|
import { getMemoryRaterNames } from "../utils/memory-raters";
|
|
@@ -1739,8 +1739,8 @@ export class CodexSession implements ProviderSession {
|
|
|
1739
1739
|
} finally {
|
|
1740
1740
|
// Session-end summarization. Pure addition for codex — no behavior to
|
|
1741
1741
|
// preserve. Wrapped in its own try/catch so summary failure must NOT
|
|
1742
|
-
// block the existing log/AGENTS.md cleanup below.
|
|
1743
|
-
//
|
|
1742
|
+
// block the existing log/AGENTS.md cleanup below. `SKIP_SESSION_SUMMARY` is
|
|
1743
|
+
// read with `isSessionSummarySkipped`, the same as the claude Stop hook + pi/opencode.
|
|
1744
1744
|
//
|
|
1745
1745
|
// Skip the summary entirely when the session was aborted. The transcript
|
|
1746
1746
|
// is incomplete, the LLM call would retry 3× through openrouter and
|
|
@@ -1751,7 +1751,7 @@ export class CodexSession implements ProviderSession {
|
|
|
1751
1751
|
this.aborted ||
|
|
1752
1752
|
this.abortController?.signal.aborted === true ||
|
|
1753
1753
|
this.pendingResult?.exitCode === 130;
|
|
1754
|
-
if (
|
|
1754
|
+
if (!isSessionSummarySkipped() && !sessionWasAborted) {
|
|
1755
1755
|
try {
|
|
1756
1756
|
await this.summarizeAtEnd();
|
|
1757
1757
|
} catch (err) {
|
|
@@ -364,7 +364,7 @@ export async function persistCodexOAuth(
|
|
|
364
364
|
* once it was issued more than that long ago (see {@link isStaleByAge}) — on
|
|
365
365
|
* top of the near-expiry skew above, not instead of it. This is how the
|
|
366
366
|
* locked keep-warm sweep (`POST /api/oauth/keep-warm/codex`) reuses this same
|
|
367
|
-
* function to refresh slots proactively (
|
|
367
|
+
* function to refresh slots proactively (`maxAgeMs` default 5 days) rather
|
|
368
368
|
* than waiting for a task to draw a slot within `REFRESH_SKEW_MS` of expiry.
|
|
369
369
|
* Default behavior (opts omitted) is unchanged.
|
|
370
370
|
*
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
splitCredentialPool,
|
|
32
32
|
validateOpencodeCredentials,
|
|
33
33
|
} from "../utils/credentials";
|
|
34
|
-
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
34
|
+
import { isEnvFlagEnabled, isSessionSummarySkipped } from "../utils/env-flag";
|
|
35
35
|
import { fetchInstalledMcpServers } from "../utils/mcp-server-fetcher";
|
|
36
36
|
import { swarmRuntimeInstanceId } from "../utils/multi-runtime";
|
|
37
37
|
import {
|
|
@@ -797,7 +797,7 @@ export class OpencodeSession implements ProviderSession {
|
|
|
797
797
|
/** Identity sync, session summary, and session close (Stop-hook parity). */
|
|
798
798
|
private async runSessionEnd(signal: AbortSignal): Promise<void> {
|
|
799
799
|
await sessionHooks.syncIdentity(this.swarm, "session_sync", signal);
|
|
800
|
-
if (
|
|
800
|
+
if (isSessionSummarySkipped()) {
|
|
801
801
|
console.debug("session_summary skipped (opencode): SKIP_SESSION_SUMMARY is set");
|
|
802
802
|
} else {
|
|
803
803
|
const transcript = this.transcript.join("\n").slice(-TRANSCRIPT_TAIL_CHARS);
|
|
@@ -10,6 +10,7 @@ import type { ExtensionFactory, McpServerConfig } from "@earendil-works/pi-codin
|
|
|
10
10
|
import { buildRatingsFromLlm, fetchRetrievalsForTask, postRatings } from "../be/memory/raters/llm";
|
|
11
11
|
import { checkToolLoop, clearToolHistory } from "../hooks/tool-loop-detection";
|
|
12
12
|
import { renderGoalReminder } from "../prompts/goal-reminder";
|
|
13
|
+
import { isSessionSummarySkipped } from "../utils/env-flag";
|
|
13
14
|
import { summarizeSession as runSummarize } from "../utils/internal-ai";
|
|
14
15
|
import { getMemoryRaterNames } from "../utils/memory-raters";
|
|
15
16
|
import { getOpenRouterAttributionHeaders } from "../utils/openrouter-base-url";
|
|
@@ -743,7 +744,7 @@ export function createSwarmHooksExtension(config: SwarmHooksConfig): ExtensionFa
|
|
|
743
744
|
|
|
744
745
|
// Session summarization — get session file from context's session manager
|
|
745
746
|
const sessionFile = ctx.sessionManager.getSessionFile?.();
|
|
746
|
-
if (!
|
|
747
|
+
if (!isSessionSummarySkipped()) {
|
|
747
748
|
await summarizeSessionForPi(config, sessionFile);
|
|
748
749
|
} else {
|
|
749
750
|
console.debug("session_summary skipped (pi): SKIP_SESSION_SUMMARY is set");
|
|
@@ -318,7 +318,7 @@ export const LEGACY_POLICY = {
|
|
|
318
318
|
"config.write.any": leadOnly,
|
|
319
319
|
"config.delete.any": leadOnly,
|
|
320
320
|
"config.read.secrets": leadOnly,
|
|
321
|
-
// HTTP runtime tokens can read their own resolved config in src/http/config.ts.
|
|
321
|
+
// HTTP runtime and agent tokens can read their own resolved config in src/http/config.ts.
|
|
322
322
|
// Lead tokens retain config.read.secrets. This exception does not grant config administration.
|
|
323
323
|
"models.catalog.write": leadOrOperator,
|
|
324
324
|
"models.harness-support.write": agentOrOperator,
|
|
@@ -356,6 +356,8 @@ export const LEGACY_POLICY = {
|
|
|
356
356
|
"runtime_pool.write": operatorOrUser,
|
|
357
357
|
"runtime_pool.bind": leadOrOperatorOrUser,
|
|
358
358
|
"runtime_controller.write": operatorOrUser,
|
|
359
|
+
// A worker token stands in for the swarm API key on a worker, so only humans mint it.
|
|
360
|
+
"agent.token.manage": operatorOrUser,
|
|
359
361
|
"extension.write": extensionWrite,
|
|
360
362
|
"extension.activate": extensionActivation,
|
|
361
363
|
"extension.install.inline": leadOrOperatorOrUser,
|
package/src/rbac/permissions.ts
CHANGED
|
@@ -33,6 +33,10 @@ export const PERMISSIONS = {
|
|
|
33
33
|
description: "View or diff another agent's context history.",
|
|
34
34
|
namespace: "agent",
|
|
35
35
|
},
|
|
36
|
+
"agent.token.manage": {
|
|
37
|
+
description: "Mint, rotate, or revoke an agent-bound worker token.",
|
|
38
|
+
namespace: "agent",
|
|
39
|
+
},
|
|
36
40
|
"task.cancel.any": {
|
|
37
41
|
description: "Cancel any task (beyond tasks the caller created).",
|
|
38
42
|
namespace: "task",
|
|
@@ -3,7 +3,7 @@ import { chmodSync, existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
|
3
3
|
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
-
import type { Usage } from "@agentclientprotocol/sdk";
|
|
6
|
+
import type { Usage } from "@agentclientprotocol/sdk/unstable";
|
|
7
7
|
import { normalizeSessionLogs } from "../../apps/ui/src/logs-parser";
|
|
8
8
|
import {
|
|
9
9
|
closeDb,
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
resolveAcpTarget,
|
|
30
30
|
} from "../providers/acp-targets";
|
|
31
31
|
import type { ProviderEvent, ProviderSessionConfig } from "../providers/types";
|
|
32
|
+
import { ACP_SDK_PATH, serveFakeAcpAgent } from "./acp-fake-agent";
|
|
32
33
|
import { listenOnFreePort } from "./test-net";
|
|
33
34
|
|
|
34
35
|
const tmpDirs: string[] = [];
|
|
@@ -148,16 +149,11 @@ describe("ACPAdapter", () => {
|
|
|
148
149
|
}) => {
|
|
149
150
|
const cwd = makeTempDir();
|
|
150
151
|
const agentPath = join(cwd, "fake-acp-agent.ts");
|
|
151
|
-
const sdkPath = join(process.cwd(), "node_modules/@agentclientprotocol/sdk/dist/acp.js");
|
|
152
152
|
await Bun.write(
|
|
153
153
|
agentPath,
|
|
154
154
|
`
|
|
155
155
|
import { Readable, Writable } from "node:stream";
|
|
156
|
-
import {
|
|
157
|
-
AgentSideConnection,
|
|
158
|
-
PROTOCOL_VERSION,
|
|
159
|
-
ndJsonStream,
|
|
160
|
-
} from "${sdkPath}";
|
|
156
|
+
import { PROTOCOL_VERSION, agent, methods, ndJsonStream } from "${ACP_SDK_PATH}";
|
|
161
157
|
|
|
162
158
|
class FakeAgent {
|
|
163
159
|
constructor(connection) {
|
|
@@ -313,10 +309,7 @@ class FakeAgent {
|
|
|
313
309
|
async cancel() {}
|
|
314
310
|
}
|
|
315
311
|
|
|
316
|
-
|
|
317
|
-
const output = Readable.toWeb(process.stdin);
|
|
318
|
-
const stream = ndJsonStream(input, output);
|
|
319
|
-
new AgentSideConnection((connection) => new FakeAgent(connection), stream);
|
|
312
|
+
${serveFakeAcpAgent("FakeAgent")}
|
|
320
313
|
`,
|
|
321
314
|
);
|
|
322
315
|
|
|
@@ -952,7 +945,6 @@ new AgentSideConnection((connection) => new FakeAgent(connection), stream);
|
|
|
952
945
|
const binDir = makeTempDir();
|
|
953
946
|
const captureFile = join(binDir, "capture.json");
|
|
954
947
|
const agentPath = join(binDir, "fake-copilot-acp.ts");
|
|
955
|
-
const sdkPath = join(process.cwd(), "node_modules/@agentclientprotocol/sdk/dist/acp.js");
|
|
956
948
|
const repoAgentsMd = "# Repo rules\nKeep this file as is.\n";
|
|
957
949
|
await Bun.write(join(cwd, "AGENTS.md"), repoAgentsMd);
|
|
958
950
|
await Bun.write(
|
|
@@ -961,7 +953,7 @@ new AgentSideConnection((connection) => new FakeAgent(connection), stream);
|
|
|
961
953
|
import { readFileSync } from "node:fs";
|
|
962
954
|
import { join } from "node:path";
|
|
963
955
|
import { Readable, Writable } from "node:stream";
|
|
964
|
-
import {
|
|
956
|
+
import { PROTOCOL_VERSION, agent, methods, ndJsonStream } from "${ACP_SDK_PATH}";
|
|
965
957
|
class FakeCopilot {
|
|
966
958
|
constructor(connection) { this.connection = connection; }
|
|
967
959
|
async initialize() {
|
|
@@ -986,8 +978,7 @@ class FakeCopilot {
|
|
|
986
978
|
}
|
|
987
979
|
async cancel() {}
|
|
988
980
|
}
|
|
989
|
-
|
|
990
|
-
new AgentSideConnection((connection) => new FakeCopilot(connection), stream);
|
|
981
|
+
${serveFakeAcpAgent("FakeCopilot")}
|
|
991
982
|
`,
|
|
992
983
|
);
|
|
993
984
|
const copilotStub = join(binDir, "copilot");
|
|
@@ -1191,7 +1182,6 @@ new AgentSideConnection((connection) => new FakeCopilot(connection), stream);
|
|
|
1191
1182
|
test("completes before the revoke response and logs a failed revocation", async () => {
|
|
1192
1183
|
const cwd = makeTempDir();
|
|
1193
1184
|
const agentPath = join(cwd, "fake-acp-ephem-agent.ts");
|
|
1194
|
-
const sdkPath = join(process.cwd(), "node_modules/@agentclientprotocol/sdk/dist/acp.js");
|
|
1195
1185
|
|
|
1196
1186
|
const captureFile = join(cwd, "captured-auth.txt");
|
|
1197
1187
|
|
|
@@ -1199,7 +1189,7 @@ new AgentSideConnection((connection) => new FakeCopilot(connection), stream);
|
|
|
1199
1189
|
agentPath,
|
|
1200
1190
|
`
|
|
1201
1191
|
import { Readable, Writable } from "node:stream";
|
|
1202
|
-
import {
|
|
1192
|
+
import { PROTOCOL_VERSION, agent, methods, ndJsonStream } from "${ACP_SDK_PATH}";
|
|
1203
1193
|
class FakeAgent {
|
|
1204
1194
|
constructor(connection) { this.connection = connection; }
|
|
1205
1195
|
async initialize() {
|
|
@@ -1223,8 +1213,7 @@ class FakeAgent {
|
|
|
1223
1213
|
}
|
|
1224
1214
|
async cancel() {}
|
|
1225
1215
|
}
|
|
1226
|
-
|
|
1227
|
-
new AgentSideConnection((connection) => new FakeAgent(connection), stream);
|
|
1216
|
+
${serveFakeAcpAgent("FakeAgent")}
|
|
1228
1217
|
`,
|
|
1229
1218
|
);
|
|
1230
1219
|
|
|
@@ -1361,12 +1350,11 @@ new AgentSideConnection((connection) => new FakeAgent(connection), stream);
|
|
|
1361
1350
|
])("$name", async ({ costs, totalCostUsd, costSource }) => {
|
|
1362
1351
|
const cwd = makeTempDir();
|
|
1363
1352
|
const agentPath = join(cwd, "fake-acp-cost-agent.ts");
|
|
1364
|
-
const sdkPath = join(process.cwd(), "node_modules/@agentclientprotocol/sdk/dist/acp.js");
|
|
1365
1353
|
await Bun.write(
|
|
1366
1354
|
agentPath,
|
|
1367
1355
|
`
|
|
1368
1356
|
import { Readable, Writable } from "node:stream";
|
|
1369
|
-
import {
|
|
1357
|
+
import { PROTOCOL_VERSION, agent, methods, ndJsonStream } from "${ACP_SDK_PATH}";
|
|
1370
1358
|
class FakeAgent {
|
|
1371
1359
|
constructor(connection) { this.connection = connection; }
|
|
1372
1360
|
async initialize() {
|
|
@@ -1384,8 +1372,7 @@ class FakeAgent {
|
|
|
1384
1372
|
}
|
|
1385
1373
|
async cancel() {}
|
|
1386
1374
|
}
|
|
1387
|
-
|
|
1388
|
-
new AgentSideConnection((connection) => new FakeAgent(connection), stream);
|
|
1375
|
+
${serveFakeAcpAgent("FakeAgent")}
|
|
1389
1376
|
`,
|
|
1390
1377
|
);
|
|
1391
1378
|
const { close: closeTokenStub, apiUrl } = await startTokenStubServer(
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
// Fake ACP targets run from temp dirs, so they import the SDK by path. The
|
|
4
|
+
// unstable v1 entry point lets them return turn `usage` like OpenCode does.
|
|
5
|
+
export const ACP_SDK_PATH = join(
|
|
6
|
+
process.cwd(),
|
|
7
|
+
"node_modules/@agentclientprotocol/sdk/dist/v1/unstable/acp.js",
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Script tail that serves a fake agent class over stdio with the SDK's agent
|
|
12
|
+
* app API. The class implements the ACP agent methods and gets
|
|
13
|
+
* `{ sessionUpdate }` to notify the client. The script must import `agent`,
|
|
14
|
+
* `methods`, and `ndJsonStream` from ACP_SDK_PATH, and `Readable`/`Writable`
|
|
15
|
+
* from node:stream.
|
|
16
|
+
*/
|
|
17
|
+
export function serveFakeAcpAgent(className: string): string {
|
|
18
|
+
return `
|
|
19
|
+
const fake = new ${className}({
|
|
20
|
+
sessionUpdate: (params) => connection.client.notify(methods.client.session.update, params),
|
|
21
|
+
});
|
|
22
|
+
const app = agent()
|
|
23
|
+
.onRequest(methods.agent.initialize, ({ params }) => fake.initialize(params))
|
|
24
|
+
.onRequest(methods.agent.session.new, ({ params }) => fake.newSession(params))
|
|
25
|
+
.onRequest(methods.agent.session.prompt, ({ params }) => fake.prompt(params))
|
|
26
|
+
.onNotification(methods.agent.session.cancel, ({ params }) => fake.cancel(params));
|
|
27
|
+
if (fake.setSessionConfigOption) {
|
|
28
|
+
app.onRequest(methods.agent.session.setConfigOption, ({ params }) =>
|
|
29
|
+
fake.setSessionConfigOption(params),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const connection = app.connect(
|
|
33
|
+
ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin)),
|
|
34
|
+
);
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import type { SessionNotification } from "@agentclientprotocol/sdk";
|
|
2
|
+
import type { SessionNotification } from "@agentclientprotocol/sdk/unstable";
|
|
3
3
|
import { acpReportedCostUsd, translateAcpSessionNotification } from "../providers/acp-swarm-events";
|
|
4
4
|
|
|
5
5
|
function notification(update: SessionNotification["update"]): SessionNotification {
|