acpx 0.15.1 → 0.17.0
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 -0
- package/dist/agent-registry-DU6mhZBL.js +785 -0
- package/dist/agent-registry-DU6mhZBL.js.map +1 -0
- package/dist/agent-registry.d.ts +70 -0
- package/dist/agent-registry.d.ts.map +1 -0
- package/dist/agent-registry.js +2 -0
- package/dist/{cli-BHAT_D3V.js → cli-CiyfEC7L.js} +7 -14
- package/dist/cli-CiyfEC7L.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +160 -349
- package/dist/cli.js.map +1 -1
- package/dist/{client-Cvz6msGc.d.ts → client-C6NUFqr-.d.ts} +21 -13
- package/dist/client-C6NUFqr-.d.ts.map +1 -0
- package/dist/{flows-BOI1XkMp.js → flows-CTOdIk-m.js} +208 -279
- package/dist/flows-CTOdIk-m.js.map +1 -0
- package/dist/flows.d.ts +6 -17
- package/dist/flows.d.ts.map +1 -1
- package/dist/flows.js +1 -1
- package/dist/{flags-BHcZWTov.js → invocation-options-DbCcOAzM.js} +83 -58
- package/dist/invocation-options-DbCcOAzM.js.map +1 -0
- package/dist/ipc-D3akHMEq.js +3350 -0
- package/dist/ipc-D3akHMEq.js.map +1 -0
- package/dist/output-BPPjvJTI.js +980 -0
- package/dist/output-BPPjvJTI.js.map +1 -0
- package/dist/{live-checkpoint-sB7dFOYR.js → queue-owner-runtime--IbbndGz.js} +5359 -4574
- package/dist/queue-owner-runtime--IbbndGz.js.map +1 -0
- package/dist/runtime.d.ts +197 -27
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +864 -409
- package/dist/runtime.js.map +1 -1
- package/dist/{session-options-DyIGRNfu.d.ts → session-options-Dp6mzNSy.d.ts} +8 -6
- package/dist/session-options-Dp6mzNSy.d.ts.map +1 -0
- package/dist/session-watch--UX5dh00.js +76 -0
- package/dist/session-watch--UX5dh00.js.map +1 -0
- package/dist/watch-BbBxO-kb.js +32 -0
- package/dist/watch-BbBxO-kb.js.map +1 -0
- package/package.json +27 -25
- package/skills/acpx/SKILL.md +24 -19
- package/dist/cli-BHAT_D3V.js.map +0 -1
- package/dist/client-Cvz6msGc.d.ts.map +0 -1
- package/dist/flags-BHcZWTov.js.map +0 -1
- package/dist/flows-BOI1XkMp.js.map +0 -1
- package/dist/live-checkpoint-sB7dFOYR.js.map +0 -1
- package/dist/output-8-D6Gd4Z.js +0 -4714
- package/dist/output-8-D6Gd4Z.js.map +0 -1
- package/dist/session-options-DyIGRNfu.d.ts.map +0 -1
package/dist/runtime.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as textPrompt, D as findSession, F as resolveSessionRecord, L as assertPersistedKeyPolicy, P as readSessionRecord, R as parseSessionRecord, gt as extractAcpError, mt as writePrivateJsonFile, t as tryCancelOnRunningOwner, vt as isAcpResourceNotFoundError, z as serializeSessionRecordForDisk } from "./ipc-D3akHMEq.js";
|
|
2
|
+
import { A as setDesiredModeId, B as recordClientOperation, C as withConnectedSession, D as applyInitialModelSelection, E as applyConfigOptionsToRecord, F as createInitialSessionRecord, G as AcpClient, H as recordSessionUpdate, I as reconcileAgentSessionId, J as RequestedModelUnsupportedError, K as REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, L as cloneSessionAcpxState, M as sessionOptionsFromRecord, N as applyConversation, O as applyModelSelection, P as applyLifecycleSnapshotToRecord, R as cloneSessionConversation, T as applyConfigOptionSelection, U as trimConversationForRuntime, V as recordPromptSubmission, W as advertisedModelState, Y as isRequestedModelUnsupportedError, _ as cancelSessionPrompt, c as LiveSessionCheckpoint, ct as normalizeOutputError, h as ensureSession, it as assertControlAuthority, j as persistSessionOptions, k as applyRequestedModelIfAdvertised, l as runPromptTurn, ot as withTimeout, q as REQUESTED_MODEL_UNSUPPORTED_REASONS, r as sendSession, s as DISCARD_OUTPUT_FORMATTER, v as closeSession, w as connectAndLoadSession } from "./queue-owner-runtime--IbbndGz.js";
|
|
3
|
+
import { D as normalizeAgentCommandInput, a as createAgentRegistry, i as DEFAULT_AGENT_NAME } from "./agent-registry-DU6mhZBL.js";
|
|
4
|
+
import { t as watchSession } from "./watch-BbBxO-kb.js";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
2
6
|
import path from "node:path";
|
|
3
7
|
import fs from "node:fs/promises";
|
|
4
8
|
import { randomUUID } from "node:crypto";
|
|
@@ -84,8 +88,7 @@ const STATUS_TEXT_RESOLVERS = {
|
|
|
84
88
|
available_commands_update: availableCommandsStatusText,
|
|
85
89
|
current_mode_update: currentModeStatusText,
|
|
86
90
|
config_option_update: configOptionStatusText,
|
|
87
|
-
session_info_update: sessionInfoStatusText
|
|
88
|
-
plan: planStatusText
|
|
91
|
+
session_info_update: sessionInfoStatusText
|
|
89
92
|
};
|
|
90
93
|
function availableCommandsStatusText(payload) {
|
|
91
94
|
const commands = Array.isArray(payload.availableCommands) ? payload.availableCommands : [];
|
|
@@ -108,6 +111,51 @@ function planStatusText(payload) {
|
|
|
108
111
|
const content = asTrimmedString((Array.isArray(payload.entries) ? payload.entries : []).find((entry) => isRecord(entry))?.content);
|
|
109
112
|
return content ? `plan: ${content}` : null;
|
|
110
113
|
}
|
|
114
|
+
function isPlanEntryStatus(value) {
|
|
115
|
+
return value === "pending" || value === "in_progress" || value === "completed";
|
|
116
|
+
}
|
|
117
|
+
function isPlanEntryPriority(value) {
|
|
118
|
+
return value === "high" || value === "medium" || value === "low";
|
|
119
|
+
}
|
|
120
|
+
function planUpdateEvent(payload) {
|
|
121
|
+
const raw = payload.entries;
|
|
122
|
+
const entries = normalizePlanEntries(raw);
|
|
123
|
+
if (Array.isArray(raw) && raw.length === 0) return {
|
|
124
|
+
type: "status",
|
|
125
|
+
text: "plan updated",
|
|
126
|
+
tag: "plan",
|
|
127
|
+
entries: []
|
|
128
|
+
};
|
|
129
|
+
const text = planStatusText(payload) ?? (entries.length > 0 ? `plan: ${entries[0]?.content}` : null);
|
|
130
|
+
if (!text) return null;
|
|
131
|
+
return {
|
|
132
|
+
type: "status",
|
|
133
|
+
text,
|
|
134
|
+
tag: "plan",
|
|
135
|
+
...entries.length > 0 ? { entries } : {}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function normalizePlanEntries(value) {
|
|
139
|
+
if (!Array.isArray(value)) return [];
|
|
140
|
+
const entries = [];
|
|
141
|
+
for (const entry of value) {
|
|
142
|
+
const normalized = normalizePlanEntry(entry);
|
|
143
|
+
if (normalized) entries.push(normalized);
|
|
144
|
+
}
|
|
145
|
+
return entries;
|
|
146
|
+
}
|
|
147
|
+
function normalizePlanEntry(value) {
|
|
148
|
+
if (!isRecord(value)) return;
|
|
149
|
+
const content = asTrimmedString(value.content);
|
|
150
|
+
const status = asTrimmedString(value.status);
|
|
151
|
+
if (!content || !isPlanEntryStatus(status)) return;
|
|
152
|
+
const priority = asTrimmedString(value.priority);
|
|
153
|
+
return {
|
|
154
|
+
content,
|
|
155
|
+
status,
|
|
156
|
+
...isPlanEntryPriority(priority) ? { priority } : {}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
111
159
|
/**
|
|
112
160
|
* Documented allowlist for text_delta.meta origin fields.
|
|
113
161
|
* Only these keys may be copied from ACP update `_meta`.
|
|
@@ -371,7 +419,7 @@ const PROMPT_EVENT_PARSERS = {
|
|
|
371
419
|
current_mode_update: (payload) => statusUpdateEvent("current_mode_update", payload),
|
|
372
420
|
config_option_update: (payload) => statusUpdateEvent("config_option_update", payload),
|
|
373
421
|
session_info_update: (payload) => statusUpdateEvent("session_info_update", payload),
|
|
374
|
-
plan:
|
|
422
|
+
plan: planUpdateEvent,
|
|
375
423
|
client_operation: clientOperationEvent,
|
|
376
424
|
update: updateStatusEvent,
|
|
377
425
|
done: () => null,
|
|
@@ -498,6 +546,88 @@ function updateStatusEvent(payload, tag) {
|
|
|
498
546
|
} : null;
|
|
499
547
|
}
|
|
500
548
|
//#endregion
|
|
549
|
+
//#region src/runtime/public/probe.ts
|
|
550
|
+
function isPrimitiveDetail(value) {
|
|
551
|
+
return value == null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol";
|
|
552
|
+
}
|
|
553
|
+
function formatFunctionDetail(value) {
|
|
554
|
+
return value.name ? `[Function ${value.name}]` : "[Function]";
|
|
555
|
+
}
|
|
556
|
+
function serializeRuntimeDetail(value) {
|
|
557
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
558
|
+
return JSON.stringify(value, (_key, nested) => {
|
|
559
|
+
if (nested instanceof Error) return nested.message || nested.name;
|
|
560
|
+
if (nested && typeof nested === "object") {
|
|
561
|
+
if (seen.has(nested)) return "[Circular]";
|
|
562
|
+
seen.add(nested);
|
|
563
|
+
}
|
|
564
|
+
return nested;
|
|
565
|
+
}) ?? "undefined";
|
|
566
|
+
}
|
|
567
|
+
function formatRuntimeDetail(value) {
|
|
568
|
+
if (value instanceof Error) return value.message || value.name;
|
|
569
|
+
if (typeof value === "string") return value;
|
|
570
|
+
if (isPrimitiveDetail(value)) return String(value);
|
|
571
|
+
if (typeof value === "function") return formatFunctionDetail(value);
|
|
572
|
+
try {
|
|
573
|
+
return serializeRuntimeDetail(value);
|
|
574
|
+
} catch {
|
|
575
|
+
return "unserializable object";
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function normalizeRuntimeDetails(details) {
|
|
579
|
+
return details?.map((detail) => formatRuntimeDetail(detail));
|
|
580
|
+
}
|
|
581
|
+
async function probeRuntime(options, deps = {}) {
|
|
582
|
+
const agentName = options.probeAgent?.trim() || "codex";
|
|
583
|
+
const agentCommand = normalizeAgentCommandInput(options.agentRegistry.resolve(agentName));
|
|
584
|
+
const client = createProbeClient(options, agentName, agentCommand, deps);
|
|
585
|
+
try {
|
|
586
|
+
await client.start();
|
|
587
|
+
return {
|
|
588
|
+
ok: true,
|
|
589
|
+
message: "embedded ACP runtime ready",
|
|
590
|
+
details: [
|
|
591
|
+
`agent=${agentName}`,
|
|
592
|
+
`command=${agentCommand.agentCommand}`,
|
|
593
|
+
`cwd=${options.cwd}`,
|
|
594
|
+
...client.initializeResult?.protocolVersion ? [`protocolVersion=${client.initializeResult.protocolVersion}`] : []
|
|
595
|
+
]
|
|
596
|
+
};
|
|
597
|
+
} catch (error) {
|
|
598
|
+
return {
|
|
599
|
+
ok: false,
|
|
600
|
+
message: "embedded ACP runtime probe failed",
|
|
601
|
+
details: [
|
|
602
|
+
`agent=${agentName}`,
|
|
603
|
+
`command=${agentCommand.agentCommand}`,
|
|
604
|
+
`cwd=${options.cwd}`,
|
|
605
|
+
formatRuntimeDetail(error)
|
|
606
|
+
]
|
|
607
|
+
};
|
|
608
|
+
} finally {
|
|
609
|
+
await client.close().catch(() => {});
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function createProbeClient(options, agentName, agentCommand, deps) {
|
|
613
|
+
const clientOptions = {
|
|
614
|
+
...agentCommand,
|
|
615
|
+
cwd: options.cwd,
|
|
616
|
+
agentProcessEnv: options.agentProcessEnv,
|
|
617
|
+
mcpServers: typeof options.mcpServers === "function" ? [] : [...options.mcpServers ?? []],
|
|
618
|
+
permissionMode: options.permissionMode,
|
|
619
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
620
|
+
permissionPolicy: options.permissionPolicy,
|
|
621
|
+
processLifecycle: options.processLifecycle,
|
|
622
|
+
processLaunchScope: {
|
|
623
|
+
kind: "runtime-probe",
|
|
624
|
+
agent: agentName
|
|
625
|
+
},
|
|
626
|
+
verbose: options.verbose
|
|
627
|
+
};
|
|
628
|
+
return deps.clientFactory?.(clientOptions) ?? new AcpClient(clientOptions);
|
|
629
|
+
}
|
|
630
|
+
//#endregion
|
|
501
631
|
//#region src/runtime/engine/reuse-policy.ts
|
|
502
632
|
function shouldReuseExistingRecord(record, params) {
|
|
503
633
|
if (record.acpx?.reset_on_next_ensure === true) return false;
|
|
@@ -513,7 +643,100 @@ function sameArgv(left, right) {
|
|
|
513
643
|
return leftValues.length === rightValues.length && leftValues.every((value, index) => value === rightValues[index]);
|
|
514
644
|
}
|
|
515
645
|
//#endregion
|
|
516
|
-
//#region src/runtime/engine/
|
|
646
|
+
//#region src/runtime/engine/status.ts
|
|
647
|
+
function runtimeStatusFromRecord(record) {
|
|
648
|
+
return {
|
|
649
|
+
...record.lastRequestId ? { lastRequestId: record.lastRequestId } : {},
|
|
650
|
+
summary: statusSummary(record),
|
|
651
|
+
acpxRecordId: record.acpxRecordId,
|
|
652
|
+
backendSessionId: record.acpSessionId,
|
|
653
|
+
agentSessionId: record.agentSessionId,
|
|
654
|
+
...buildModelsField(record),
|
|
655
|
+
...buildUsageField(record),
|
|
656
|
+
...buildAvailableCommandsField(record),
|
|
657
|
+
details: {
|
|
658
|
+
cwd: record.cwd,
|
|
659
|
+
lastUsedAt: record.lastUsedAt,
|
|
660
|
+
closed: record.closed === true,
|
|
661
|
+
...record.acpx?.config_options !== void 0 ? { configOptions: structuredClone(record.acpx.config_options) } : {}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function statusSummary(record) {
|
|
666
|
+
return [
|
|
667
|
+
`session=${record.acpxRecordId}`,
|
|
668
|
+
`backendSessionId=${record.acpSessionId}`,
|
|
669
|
+
record.agentSessionId ? `agentSessionId=${record.agentSessionId}` : null,
|
|
670
|
+
record.pid != null ? `pid=${record.pid}` : null,
|
|
671
|
+
record.closed ? "closed" : "open"
|
|
672
|
+
].filter(Boolean).join(" ");
|
|
673
|
+
}
|
|
674
|
+
function buildModelsField(record) {
|
|
675
|
+
const { available_models: available = [], current_model_id: currentModelId } = record.acpx ?? {};
|
|
676
|
+
if (available.length === 0 && currentModelId === void 0) return {};
|
|
677
|
+
return { models: {
|
|
678
|
+
...currentModelId !== void 0 ? { currentModelId } : {},
|
|
679
|
+
availableModelIds: [...available],
|
|
680
|
+
...buildModelNamesField(record.acpx)
|
|
681
|
+
} };
|
|
682
|
+
}
|
|
683
|
+
function buildModelNamesField(state) {
|
|
684
|
+
const models = advertisedModelState(state);
|
|
685
|
+
return models && (models.configId || state?.available_model_names !== void 0) ? { availableModels: models.availableModels } : {};
|
|
686
|
+
}
|
|
687
|
+
function tokenUsageToBreakdown(usage) {
|
|
688
|
+
if (!usage) return;
|
|
689
|
+
const breakdown = {};
|
|
690
|
+
assignUsageBreakdownField(breakdown, "inputTokens", usage.input_tokens);
|
|
691
|
+
assignUsageBreakdownField(breakdown, "outputTokens", usage.output_tokens);
|
|
692
|
+
assignUsageBreakdownField(breakdown, "cachedReadTokens", usage.cache_read_input_tokens);
|
|
693
|
+
assignUsageBreakdownField(breakdown, "cachedWriteTokens", usage.cache_creation_input_tokens);
|
|
694
|
+
assignUsageBreakdownField(breakdown, "thoughtTokens", usage.thought_tokens);
|
|
695
|
+
assignUsageBreakdownField(breakdown, "totalTokens", usage.total_tokens);
|
|
696
|
+
return Object.keys(breakdown).length > 0 ? breakdown : void 0;
|
|
697
|
+
}
|
|
698
|
+
function assignUsageBreakdownField(breakdown, key, value) {
|
|
699
|
+
if (value !== void 0) breakdown[key] = value;
|
|
700
|
+
}
|
|
701
|
+
function buildUsageField(record) {
|
|
702
|
+
const cumulative = tokenUsageToBreakdown(record.cumulative_token_usage);
|
|
703
|
+
const perRequestEntries = Object.entries(record.request_token_usage ?? {}).map(([id, value]) => [id, tokenUsageToBreakdown(value)]).filter((entry) => entry[1] !== void 0);
|
|
704
|
+
const perRequest = perRequestEntries.length > 0 ? Object.fromEntries(perRequestEntries) : void 0;
|
|
705
|
+
const cost = record.cumulative_cost;
|
|
706
|
+
const usage = {
|
|
707
|
+
...cumulative ? { cumulative } : {},
|
|
708
|
+
...cost ? { cost } : {},
|
|
709
|
+
...perRequest ? { perRequest } : {}
|
|
710
|
+
};
|
|
711
|
+
return Object.keys(usage).length > 0 ? { usage } : {};
|
|
712
|
+
}
|
|
713
|
+
function buildAvailableCommandsField(record) {
|
|
714
|
+
const commands = record.acpx?.available_commands;
|
|
715
|
+
if (!commands || commands.length === 0) return {};
|
|
716
|
+
const availableCommands = commands.map((command) => runtimeAvailableCommand(command)).filter((command) => command !== void 0);
|
|
717
|
+
return availableCommands.length > 0 ? { availableCommands } : {};
|
|
718
|
+
}
|
|
719
|
+
function runtimeAvailableCommand(command) {
|
|
720
|
+
if (typeof command === "string") {
|
|
721
|
+
const name = command.trim();
|
|
722
|
+
return name ? { name } : void 0;
|
|
723
|
+
}
|
|
724
|
+
const record = commandRecord(command);
|
|
725
|
+
if (!record) return;
|
|
726
|
+
const name = asOptionalString(record.name);
|
|
727
|
+
if (!name) return;
|
|
728
|
+
const runtimeCommand = { name };
|
|
729
|
+
const description = asOptionalString(record.description);
|
|
730
|
+
if (description) runtimeCommand.description = description;
|
|
731
|
+
if (typeof record.has_input === "boolean") runtimeCommand.hasInput = record.has_input;
|
|
732
|
+
return runtimeCommand;
|
|
733
|
+
}
|
|
734
|
+
function commandRecord(value) {
|
|
735
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
736
|
+
return value;
|
|
737
|
+
}
|
|
738
|
+
//#endregion
|
|
739
|
+
//#region src/runtime/engine/turn.ts
|
|
517
740
|
function createDeferred() {
|
|
518
741
|
let resolve;
|
|
519
742
|
let reject;
|
|
@@ -526,22 +749,6 @@ function createDeferred() {
|
|
|
526
749
|
reject
|
|
527
750
|
};
|
|
528
751
|
}
|
|
529
|
-
async function settleAttempt(run) {
|
|
530
|
-
try {
|
|
531
|
-
return {
|
|
532
|
-
ok: true,
|
|
533
|
-
value: await run()
|
|
534
|
-
};
|
|
535
|
-
} catch (error) {
|
|
536
|
-
return {
|
|
537
|
-
ok: false,
|
|
538
|
-
error
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
function firstFailedAttempt(attempts) {
|
|
543
|
-
return attempts.find((attempt) => !attempt.ok);
|
|
544
|
-
}
|
|
545
752
|
var AsyncEventQueue = class {
|
|
546
753
|
items = [];
|
|
547
754
|
waits = [];
|
|
@@ -578,17 +785,6 @@ var AsyncEventQueue = class {
|
|
|
578
785
|
}
|
|
579
786
|
}
|
|
580
787
|
};
|
|
581
|
-
function isoNow() {
|
|
582
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
583
|
-
}
|
|
584
|
-
function isUnsupportedSessionCloseError(error) {
|
|
585
|
-
const acp = extractAcpError(error);
|
|
586
|
-
if (!acp) return false;
|
|
587
|
-
if (acp.code === -32601 || acp.code === -32602) return true;
|
|
588
|
-
if (acp.code !== -32603 || !acp.data || typeof acp.data !== "object") return false;
|
|
589
|
-
const details = acp.data.details;
|
|
590
|
-
return typeof details === "string" && details.toLowerCase().includes("invalid params");
|
|
591
|
-
}
|
|
592
788
|
function toPromptInput(text, attachments) {
|
|
593
789
|
if (!attachments || attachments.length === 0) return text;
|
|
594
790
|
const blocks = [];
|
|
@@ -617,34 +813,6 @@ function toPromptInput(text, attachments) {
|
|
|
617
813
|
}
|
|
618
814
|
return blocks.length > 0 ? blocks : textPrompt(text);
|
|
619
815
|
}
|
|
620
|
-
function createInitialRecord(params) {
|
|
621
|
-
const now = isoNow();
|
|
622
|
-
return {
|
|
623
|
-
schema: "acpx.session.v1",
|
|
624
|
-
acpxRecordId: params.recordId,
|
|
625
|
-
acpSessionId: params.sessionId,
|
|
626
|
-
agentSessionId: params.agentSessionId,
|
|
627
|
-
agentCommand: params.agentCommand,
|
|
628
|
-
agentArgv: params.agentArgv,
|
|
629
|
-
cwd: params.cwd,
|
|
630
|
-
name: params.sessionName,
|
|
631
|
-
createdAt: now,
|
|
632
|
-
lastUsedAt: now,
|
|
633
|
-
lastSeq: 0,
|
|
634
|
-
eventLog: defaultSessionEventLog(params.recordId),
|
|
635
|
-
closed: false,
|
|
636
|
-
closedAt: void 0,
|
|
637
|
-
...createSessionConversation(now),
|
|
638
|
-
acpx: {}
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
function createRecordId(sessionKey, mode) {
|
|
642
|
-
if (mode === "persistent") return sessionKey;
|
|
643
|
-
return `${sessionKey}:oneshot:${randomUUID()}`;
|
|
644
|
-
}
|
|
645
|
-
function resumePolicyForSessionMode(mode) {
|
|
646
|
-
return mode === "persistent" ? "same-session-only" : "allow-new";
|
|
647
|
-
}
|
|
648
816
|
function legacyTerminalEventFromTurnResult(result) {
|
|
649
817
|
if (result.status === "failed") return {
|
|
650
818
|
type: "error",
|
|
@@ -659,82 +827,41 @@ function legacyTerminalEventFromTurnResult(result) {
|
|
|
659
827
|
...result._meta === void 0 ? {} : { _meta: result._meta }
|
|
660
828
|
};
|
|
661
829
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
availableModelIds: []
|
|
677
|
-
} };
|
|
678
|
-
return { models: {
|
|
679
|
-
...currentModelId !== void 0 ? { currentModelId } : {},
|
|
680
|
-
availableModelIds: [...available]
|
|
681
|
-
} };
|
|
682
|
-
}
|
|
683
|
-
function tokenUsageToBreakdown(usage) {
|
|
684
|
-
if (!usage) return;
|
|
685
|
-
const breakdown = {};
|
|
686
|
-
assignUsageBreakdownField(breakdown, "inputTokens", usage.input_tokens);
|
|
687
|
-
assignUsageBreakdownField(breakdown, "outputTokens", usage.output_tokens);
|
|
688
|
-
assignUsageBreakdownField(breakdown, "cachedReadTokens", usage.cache_read_input_tokens);
|
|
689
|
-
assignUsageBreakdownField(breakdown, "cachedWriteTokens", usage.cache_creation_input_tokens);
|
|
690
|
-
assignUsageBreakdownField(breakdown, "thoughtTokens", usage.thought_tokens);
|
|
691
|
-
assignUsageBreakdownField(breakdown, "totalTokens", usage.total_tokens);
|
|
692
|
-
return Object.keys(breakdown).length > 0 ? breakdown : void 0;
|
|
693
|
-
}
|
|
694
|
-
function assignUsageBreakdownField(breakdown, key, value) {
|
|
695
|
-
if (value !== void 0) breakdown[key] = value;
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region src/runtime/engine/manager.ts
|
|
832
|
+
async function settleAttempt(run) {
|
|
833
|
+
try {
|
|
834
|
+
return {
|
|
835
|
+
ok: true,
|
|
836
|
+
value: await run()
|
|
837
|
+
};
|
|
838
|
+
} catch (error) {
|
|
839
|
+
return {
|
|
840
|
+
ok: false,
|
|
841
|
+
error
|
|
842
|
+
};
|
|
843
|
+
}
|
|
696
844
|
}
|
|
697
|
-
function
|
|
698
|
-
|
|
699
|
-
const perRequestEntries = Object.entries(record.request_token_usage ?? {}).map(([id, value]) => [id, tokenUsageToBreakdown(value)]).filter((entry) => entry[1] !== void 0);
|
|
700
|
-
const perRequest = perRequestEntries.length > 0 ? Object.fromEntries(perRequestEntries) : void 0;
|
|
701
|
-
const cost = record.cumulative_cost;
|
|
702
|
-
const usage = {
|
|
703
|
-
...cumulative ? { cumulative } : {},
|
|
704
|
-
...cost ? { cost } : {},
|
|
705
|
-
...perRequest ? { perRequest } : {}
|
|
706
|
-
};
|
|
707
|
-
return Object.keys(usage).length > 0 ? { usage } : {};
|
|
845
|
+
function firstFailedAttempt(attempts) {
|
|
846
|
+
return attempts.find((attempt) => !attempt.ok);
|
|
708
847
|
}
|
|
709
|
-
function
|
|
710
|
-
|
|
711
|
-
if (!commands || commands.length === 0) return {};
|
|
712
|
-
const availableCommands = commands.map((command) => runtimeAvailableCommand(command)).filter((command) => command !== void 0);
|
|
713
|
-
return availableCommands.length > 0 ? { availableCommands } : {};
|
|
848
|
+
function isoNow() {
|
|
849
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
714
850
|
}
|
|
715
|
-
function
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
const name = trimmedField(record.name);
|
|
723
|
-
if (!name) return;
|
|
724
|
-
const runtimeCommand = { name };
|
|
725
|
-
const description = trimmedField(record.description);
|
|
726
|
-
if (description) runtimeCommand.description = description;
|
|
727
|
-
if (typeof record.has_input === "boolean") runtimeCommand.hasInput = record.has_input;
|
|
728
|
-
return runtimeCommand;
|
|
851
|
+
function isUnsupportedSessionCloseError(error) {
|
|
852
|
+
const acp = extractAcpError(error);
|
|
853
|
+
if (!acp) return false;
|
|
854
|
+
if (acp.code === -32601 || acp.code === -32602) return true;
|
|
855
|
+
if (acp.code !== -32603 || !acp.data || typeof acp.data !== "object") return false;
|
|
856
|
+
const details = acp.data.details;
|
|
857
|
+
return typeof details === "string" && details.toLowerCase().includes("invalid params");
|
|
729
858
|
}
|
|
730
|
-
function
|
|
731
|
-
if (
|
|
732
|
-
return
|
|
859
|
+
function createRecordId(sessionKey, mode) {
|
|
860
|
+
if (mode === "persistent") return sessionKey;
|
|
861
|
+
return `${sessionKey}:oneshot:${randomUUID()}`;
|
|
733
862
|
}
|
|
734
|
-
function
|
|
735
|
-
|
|
736
|
-
const trimmed = value.trim();
|
|
737
|
-
return trimmed ? trimmed : void 0;
|
|
863
|
+
function resumePolicyForSessionMode(mode) {
|
|
864
|
+
return mode === "persistent" ? "same-session-only" : "allow-new";
|
|
738
865
|
}
|
|
739
866
|
function advertisedConfigOptionIds(record) {
|
|
740
867
|
const configOptions = record.acpx?.config_options;
|
|
@@ -780,20 +907,117 @@ var AcpRuntimeManager = class {
|
|
|
780
907
|
deps;
|
|
781
908
|
activeControllers = /* @__PURE__ */ new Map();
|
|
782
909
|
retainedSessionOwners = /* @__PURE__ */ new Map();
|
|
910
|
+
retiringSessionOwners = /* @__PURE__ */ new Set();
|
|
783
911
|
pendingOneShotRecordIds = /* @__PURE__ */ new Map();
|
|
784
912
|
ensureSessionLocks = /* @__PURE__ */ new Map();
|
|
785
913
|
runtimeOperationLocks = /* @__PURE__ */ new Map();
|
|
914
|
+
sessionTasks = /* @__PURE__ */ new Map();
|
|
786
915
|
closingActiveRecords = /* @__PURE__ */ new Set();
|
|
916
|
+
liveClients = /* @__PURE__ */ new Set();
|
|
917
|
+
pendingTasks = /* @__PURE__ */ new Set();
|
|
918
|
+
shuttingDown = false;
|
|
919
|
+
shutdownTask;
|
|
787
920
|
constructor(options, deps = {}) {
|
|
788
921
|
this.options = options;
|
|
789
922
|
this.deps = deps;
|
|
790
923
|
}
|
|
791
924
|
createClient(options) {
|
|
925
|
+
this.assertOpen();
|
|
792
926
|
const clientOptions = {
|
|
793
927
|
...options,
|
|
794
|
-
agentProcessEnv: this.options.agentProcessEnv
|
|
928
|
+
agentProcessEnv: this.options.agentProcessEnv,
|
|
929
|
+
processLifecycle: {
|
|
930
|
+
...options.processLifecycle,
|
|
931
|
+
onBeforeSpawn: async (launch) => {
|
|
932
|
+
this.assertOpen();
|
|
933
|
+
await options.processLifecycle?.onBeforeSpawn?.(launch);
|
|
934
|
+
this.assertOpen();
|
|
935
|
+
},
|
|
936
|
+
onSpawned: async (process) => {
|
|
937
|
+
this.assertOpen();
|
|
938
|
+
await options.processLifecycle?.onSpawned?.(process);
|
|
939
|
+
this.assertOpen();
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
const client = this.deps.clientFactory?.(clientOptions) ?? new AcpClient(clientOptions);
|
|
944
|
+
this.liveClients.add(client);
|
|
945
|
+
return client;
|
|
946
|
+
}
|
|
947
|
+
async probe() {
|
|
948
|
+
let client;
|
|
949
|
+
try {
|
|
950
|
+
return await probeRuntime(this.options, { clientFactory: (options) => client = this.createClient(options) });
|
|
951
|
+
} finally {
|
|
952
|
+
if (client) this.liveClients.delete(client);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
assertOpen() {
|
|
956
|
+
if (this.shuttingDown) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
957
|
+
}
|
|
958
|
+
shutdown() {
|
|
959
|
+
if (!this.shutdownTask) {
|
|
960
|
+
this.shuttingDown = true;
|
|
961
|
+
this.shutdownTask = this.shutdownOwnedClients();
|
|
962
|
+
}
|
|
963
|
+
return this.shutdownTask;
|
|
964
|
+
}
|
|
965
|
+
async shutdownOwnedClients() {
|
|
966
|
+
const closed = await Promise.allSettled([...this.liveClients].map(async (client) => {
|
|
967
|
+
client.requestCancelActivePrompt().catch(() => {});
|
|
968
|
+
await this.closeClient(client);
|
|
969
|
+
}));
|
|
970
|
+
await Promise.allSettled([
|
|
971
|
+
...this.ensureSessionLocks.values(),
|
|
972
|
+
...this.runtimeOperationLocks.values(),
|
|
973
|
+
...this.pendingTasks
|
|
974
|
+
]);
|
|
975
|
+
await Promise.all([...this.retainedSessionOwners.values()].map(async (owner) => {
|
|
976
|
+
this.removeRetainedSessionOwner(owner);
|
|
977
|
+
await this.stopSessionOwner(owner);
|
|
978
|
+
}));
|
|
979
|
+
const errors = [];
|
|
980
|
+
for (const result of closed) if (result.status === "rejected") errors.push(result.reason);
|
|
981
|
+
if (errors.length) throw new AggregateError(errors, "ACP runtime shutdown failed.");
|
|
982
|
+
this.retiringSessionOwners.clear();
|
|
983
|
+
}
|
|
984
|
+
trackTask(task) {
|
|
985
|
+
this.pendingTasks.add(task);
|
|
986
|
+
const remove = () => {
|
|
987
|
+
this.pendingTasks.delete(task);
|
|
795
988
|
};
|
|
796
|
-
|
|
989
|
+
task.then(remove, remove);
|
|
990
|
+
return task;
|
|
991
|
+
}
|
|
992
|
+
queueSessionTask(recordId, run, cancel) {
|
|
993
|
+
const tasks = this.sessionTasks.get(recordId) ?? /* @__PURE__ */ new Set();
|
|
994
|
+
this.sessionTasks.set(recordId, tasks);
|
|
995
|
+
const previous = [...tasks].at(-1)?.completion;
|
|
996
|
+
const result = (async () => {
|
|
997
|
+
await previous?.catch(() => {});
|
|
998
|
+
return await run();
|
|
999
|
+
})();
|
|
1000
|
+
const task = {
|
|
1001
|
+
completion: result.then(() => {}),
|
|
1002
|
+
cancel
|
|
1003
|
+
};
|
|
1004
|
+
task.completion.catch(() => {});
|
|
1005
|
+
tasks.add(task);
|
|
1006
|
+
const remove = () => {
|
|
1007
|
+
tasks.delete(task);
|
|
1008
|
+
if (tasks.size === 0) this.sessionTasks.delete(recordId);
|
|
1009
|
+
};
|
|
1010
|
+
result.then(remove, remove);
|
|
1011
|
+
return this.trackTask(result);
|
|
1012
|
+
}
|
|
1013
|
+
async cancelSessionTasks(recordId) {
|
|
1014
|
+
const tasks = [...this.sessionTasks.get(recordId) ?? []];
|
|
1015
|
+
await Promise.all(tasks.flatMap((task) => task.cancel ? [task.cancel()] : []));
|
|
1016
|
+
return tasks;
|
|
1017
|
+
}
|
|
1018
|
+
async closeClient(client) {
|
|
1019
|
+
await client.close();
|
|
1020
|
+
this.liveClients.delete(client);
|
|
797
1021
|
}
|
|
798
1022
|
createSessionOwner(input) {
|
|
799
1023
|
const owner = {
|
|
@@ -900,12 +1124,27 @@ var AcpRuntimeManager = class {
|
|
|
900
1124
|
}
|
|
901
1125
|
async stopSessionOwner(owner) {
|
|
902
1126
|
await this.flushSessionOwner(owner).catch(() => {});
|
|
903
|
-
await owner.client
|
|
1127
|
+
await this.closeClient(owner.client).catch(() => {
|
|
1128
|
+
this.retiringSessionOwners.add(owner);
|
|
1129
|
+
});
|
|
904
1130
|
await owner.projection?.checkpoint.flush().catch(() => {});
|
|
905
1131
|
try {
|
|
906
1132
|
owner.client.clearEventHandlers();
|
|
907
1133
|
} catch {}
|
|
908
1134
|
}
|
|
1135
|
+
async retrySessionCleanup(recordId) {
|
|
1136
|
+
for (const owner of this.retiringSessionOwners) if (owner.recordId === recordId) {
|
|
1137
|
+
owner.retirement ??= this.finalizeSessionConnection({
|
|
1138
|
+
client: owner.client,
|
|
1139
|
+
owner
|
|
1140
|
+
}).then(() => {
|
|
1141
|
+
this.retiringSessionOwners.delete(owner);
|
|
1142
|
+
}).finally(() => {
|
|
1143
|
+
owner.retirement = void 0;
|
|
1144
|
+
});
|
|
1145
|
+
await owner.retirement;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
909
1148
|
async refreshClosedState(record) {
|
|
910
1149
|
if (!this.closingActiveRecords.has(record.acpxRecordId)) return record.closed === true;
|
|
911
1150
|
const latest = await this.options.sessionStore.load(record.acpxRecordId).catch(() => void 0);
|
|
@@ -933,10 +1172,23 @@ var AcpRuntimeManager = class {
|
|
|
933
1172
|
return true;
|
|
934
1173
|
}
|
|
935
1174
|
canRetainPersistentSessionOwner(owner, record) {
|
|
936
|
-
return owner.mode === "persistent" && !record.closed && !(owner.client.hasUnresolvedPrompt?.() ?? false) && owner.client.hasReusableSession(record.acpSessionId);
|
|
1175
|
+
return !this.shuttingDown && owner.mode === "persistent" && !record.closed && !(owner.client.hasUnresolvedPrompt?.() ?? false) && owner.client.hasReusableSession(record.acpSessionId);
|
|
1176
|
+
}
|
|
1177
|
+
resolveMcpServers(session) {
|
|
1178
|
+
const servers = this.options.mcpServers;
|
|
1179
|
+
const { sessionKey, cwd, agentCommand, agentArgv } = session;
|
|
1180
|
+
return [...typeof servers === "function" ? servers({
|
|
1181
|
+
sessionKey,
|
|
1182
|
+
cwd,
|
|
1183
|
+
agentCommand,
|
|
1184
|
+
agentArgv: agentArgv ? [...agentArgv] : void 0
|
|
1185
|
+
}) : servers ?? []];
|
|
937
1186
|
}
|
|
938
|
-
async withRuntimeControlSession(record, sessionMode, run, replacingConfigOption) {
|
|
1187
|
+
async withRuntimeControlSession(record, sessionMode, run, replacingConfigOption, authority) {
|
|
1188
|
+
await this.retrySessionCleanup(record.acpxRecordId);
|
|
1189
|
+
assertControlAuthority(authority);
|
|
939
1190
|
const owner = await this.readRetainedSessionOwner(record, { consume: false });
|
|
1191
|
+
assertControlAuthority(authority);
|
|
940
1192
|
if (owner) {
|
|
941
1193
|
const ownedRecord = owner.projection?.record ?? record;
|
|
942
1194
|
try {
|
|
@@ -954,34 +1206,43 @@ var AcpRuntimeManager = class {
|
|
|
954
1206
|
await this.flushSessionOwner(owner);
|
|
955
1207
|
}
|
|
956
1208
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1209
|
+
let controlClient;
|
|
1210
|
+
try {
|
|
1211
|
+
const result = await withConnectedSession({
|
|
1212
|
+
sessionRecordId: record.acpxRecordId,
|
|
1213
|
+
loadRecord: async (sessionRecordId) => await this.requireRecord(sessionRecordId),
|
|
1214
|
+
saveRecord: async (connectedRecord) => await this.options.sessionStore.save(connectedRecord),
|
|
1215
|
+
createClient: (options) => controlClient = this.createClient({
|
|
1216
|
+
...options,
|
|
1217
|
+
processLifecycle: this.options.processLifecycle,
|
|
1218
|
+
processLaunchScope: {
|
|
1219
|
+
kind: "runtime-session",
|
|
1220
|
+
sessionKey: record.name ?? record.acpxRecordId
|
|
1221
|
+
}
|
|
1222
|
+
}),
|
|
1223
|
+
mcpServers: this.resolveMcpServers({
|
|
1224
|
+
...record,
|
|
966
1225
|
sessionKey: record.name ?? record.acpxRecordId
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
}
|
|
1226
|
+
}),
|
|
1227
|
+
permissionMode: this.options.permissionMode,
|
|
1228
|
+
nonInteractivePermissions: this.options.nonInteractivePermissions,
|
|
1229
|
+
permissionPolicy: this.options.permissionPolicy,
|
|
1230
|
+
onPermissionRequest: this.options.onPermissionRequest,
|
|
1231
|
+
elicitationModes: this.options.elicitationModes,
|
|
1232
|
+
verbose: this.options.verbose,
|
|
1233
|
+
timeoutMs: this.options.timeoutMs,
|
|
1234
|
+
resumePolicy: resumePolicyForSessionMode(sessionMode),
|
|
1235
|
+
replacingConfigOption,
|
|
1236
|
+
authority,
|
|
1237
|
+
run
|
|
1238
|
+
});
|
|
1239
|
+
return {
|
|
1240
|
+
value: result.value,
|
|
1241
|
+
record: result.record
|
|
1242
|
+
};
|
|
1243
|
+
} finally {
|
|
1244
|
+
if (controlClient) this.liveClients.delete(controlClient);
|
|
1245
|
+
}
|
|
985
1246
|
}
|
|
986
1247
|
refreshOwnedRecordLifecycle(owner, record) {
|
|
987
1248
|
record.lastUsedAt = isoNow();
|
|
@@ -998,7 +1259,9 @@ var AcpRuntimeManager = class {
|
|
|
998
1259
|
const key = `${input.mode}\0${input.sessionKey}`;
|
|
999
1260
|
return await this.withManagerLock(this.ensureSessionLocks, key, run);
|
|
1000
1261
|
}
|
|
1001
|
-
async withManagerLock(locks, key, run) {
|
|
1262
|
+
async withManagerLock(locks, key, run, authority) {
|
|
1263
|
+
this.assertOpen();
|
|
1264
|
+
assertControlAuthority(authority);
|
|
1002
1265
|
const previous = locks.get(key) ?? Promise.resolve();
|
|
1003
1266
|
let release;
|
|
1004
1267
|
const gate = new Promise((resolve) => {
|
|
@@ -1008,6 +1271,8 @@ var AcpRuntimeManager = class {
|
|
|
1008
1271
|
locks.set(key, tail);
|
|
1009
1272
|
await previous;
|
|
1010
1273
|
try {
|
|
1274
|
+
this.assertOpen();
|
|
1275
|
+
assertControlAuthority(authority);
|
|
1011
1276
|
return await run();
|
|
1012
1277
|
} finally {
|
|
1013
1278
|
release();
|
|
@@ -1030,7 +1295,8 @@ var AcpRuntimeManager = class {
|
|
|
1030
1295
|
async loadExistingRuntimeSession(input) {
|
|
1031
1296
|
const existingRecordId = input.mode === "persistent" ? input.sessionKey : this.pendingOneShotRecordIds.get(input.sessionKey);
|
|
1032
1297
|
if (!existingRecordId) return;
|
|
1033
|
-
|
|
1298
|
+
await this.retrySessionCleanup(existingRecordId);
|
|
1299
|
+
let record = await this.findSession(existingRecordId);
|
|
1034
1300
|
if (!record) return;
|
|
1035
1301
|
const owner = this.retainedSessionOwners.get(record.acpxRecordId);
|
|
1036
1302
|
if (owner) {
|
|
@@ -1042,6 +1308,9 @@ var AcpRuntimeManager = class {
|
|
|
1042
1308
|
owner
|
|
1043
1309
|
};
|
|
1044
1310
|
}
|
|
1311
|
+
async findSession(sessionKey) {
|
|
1312
|
+
return await this.options.sessionStore.load(sessionKey);
|
|
1313
|
+
}
|
|
1045
1314
|
canReuseRuntimeSession(input, agent, existing) {
|
|
1046
1315
|
if (!shouldReuseExistingRecord(existing.record, {
|
|
1047
1316
|
cwd: agent.cwd,
|
|
@@ -1068,7 +1337,10 @@ var AcpRuntimeManager = class {
|
|
|
1068
1337
|
agentCommand,
|
|
1069
1338
|
agentArgv,
|
|
1070
1339
|
cwd,
|
|
1071
|
-
mcpServers:
|
|
1340
|
+
mcpServers: this.resolveMcpServers({
|
|
1341
|
+
...agent,
|
|
1342
|
+
sessionKey: input.sessionKey
|
|
1343
|
+
}),
|
|
1072
1344
|
permissionMode: this.options.permissionMode,
|
|
1073
1345
|
nonInteractivePermissions: this.options.nonInteractivePermissions,
|
|
1074
1346
|
permissionPolicy: this.options.permissionPolicy,
|
|
@@ -1090,6 +1362,7 @@ var AcpRuntimeManager = class {
|
|
|
1090
1362
|
let retained = false;
|
|
1091
1363
|
try {
|
|
1092
1364
|
await client.start();
|
|
1365
|
+
this.assertOpen();
|
|
1093
1366
|
const session = await createOrLoadRuntimeSession(client, input.resumeSessionId, cwd);
|
|
1094
1367
|
const record = await this.prepareInitialRuntimeRecord({
|
|
1095
1368
|
input,
|
|
@@ -1107,15 +1380,15 @@ var AcpRuntimeManager = class {
|
|
|
1107
1380
|
if (!retained) {
|
|
1108
1381
|
owner.recordId = void 0;
|
|
1109
1382
|
client.clearEventHandlers();
|
|
1110
|
-
await
|
|
1383
|
+
await this.closeClient(client);
|
|
1111
1384
|
}
|
|
1112
1385
|
}
|
|
1113
1386
|
}
|
|
1114
1387
|
async prepareInitialRuntimeRecord(params) {
|
|
1115
1388
|
const { input, client, owner, agentCommand, agentArgv, cwd, session } = params;
|
|
1116
|
-
const record =
|
|
1389
|
+
const record = createInitialSessionRecord({
|
|
1117
1390
|
recordId: createRecordId(input.sessionKey, input.mode),
|
|
1118
|
-
|
|
1391
|
+
name: input.sessionKey,
|
|
1119
1392
|
sessionId: session.sessionId,
|
|
1120
1393
|
agentCommand,
|
|
1121
1394
|
agentArgv,
|
|
@@ -1135,9 +1408,7 @@ var AcpRuntimeManager = class {
|
|
|
1135
1408
|
agentCommand,
|
|
1136
1409
|
timeoutMs: this.options.timeoutMs
|
|
1137
1410
|
});
|
|
1138
|
-
|
|
1139
|
-
syncAdvertisedModelState(record, modelApplication.response ? modelStateFromConfigOptions(modelApplication.response.configOptions) : session.sessionResult.models);
|
|
1140
|
-
if (modelApplication.applied) setCurrentModelId(record, currentModelIdFromSetModelResponse(modelApplication.response, input.sessionOptions?.model));
|
|
1411
|
+
applyInitialModelSelection(record, session.sessionResult.models, input.sessionOptions?.model, modelApplication);
|
|
1141
1412
|
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
1142
1413
|
persistSessionOptions(record, input.sessionOptions);
|
|
1143
1414
|
return record;
|
|
@@ -1145,6 +1416,7 @@ var AcpRuntimeManager = class {
|
|
|
1145
1416
|
async retainInitializedSessionOwner(owner, record) {
|
|
1146
1417
|
owner.recordId = record.acpxRecordId;
|
|
1147
1418
|
await owner.projection?.checkpoint.checkpoint();
|
|
1419
|
+
this.assertOpen();
|
|
1148
1420
|
const previousOwner = this.retainedSessionOwners.get(record.acpxRecordId);
|
|
1149
1421
|
this.retainedSessionOwners.set(record.acpxRecordId, owner);
|
|
1150
1422
|
if (owner.mode === "oneshot") this.pendingOneShotRecordIds.set(owner.sessionKey, record.acpxRecordId);
|
|
@@ -1154,11 +1426,12 @@ var AcpRuntimeManager = class {
|
|
|
1154
1426
|
}
|
|
1155
1427
|
}
|
|
1156
1428
|
startTurn(input) {
|
|
1429
|
+
this.assertOpen();
|
|
1157
1430
|
let promptInput;
|
|
1158
1431
|
try {
|
|
1159
1432
|
promptInput = toPromptInput(input.text, input.attachments);
|
|
1160
1433
|
} catch (error) {
|
|
1161
|
-
this.closeRetainedOneShotHandle(input.handle).catch(() => {});
|
|
1434
|
+
this.trackTask(this.closeRetainedOneShotHandle(input.handle).catch(() => {}));
|
|
1162
1435
|
throw error;
|
|
1163
1436
|
}
|
|
1164
1437
|
const queue = new AsyncEventQueue();
|
|
@@ -1194,28 +1467,8 @@ var AcpRuntimeManager = class {
|
|
|
1194
1467
|
const abortHandler = () => {
|
|
1195
1468
|
requestCancel();
|
|
1196
1469
|
};
|
|
1197
|
-
if (input.signal) {
|
|
1198
|
-
|
|
1199
|
-
promptStarted.reject(/* @__PURE__ */ new Error("ACP turn cancelled before prompt submission."));
|
|
1200
|
-
closeStream();
|
|
1201
|
-
this.closeRetainedOneShotHandle(input.handle).catch(() => {}).then(() => {
|
|
1202
|
-
settleResult({
|
|
1203
|
-
status: "cancelled",
|
|
1204
|
-
stopReason: "cancelled"
|
|
1205
|
-
});
|
|
1206
|
-
});
|
|
1207
|
-
return {
|
|
1208
|
-
requestId: input.requestId,
|
|
1209
|
-
promptStarted: promptStarted.promise,
|
|
1210
|
-
events: queue.iterate(),
|
|
1211
|
-
result: result.promise,
|
|
1212
|
-
cancel: async () => {},
|
|
1213
|
-
closeStream: async () => {}
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
|
-
input.signal.addEventListener("abort", abortHandler, { once: true });
|
|
1217
|
-
}
|
|
1218
|
-
this.runRuntimeTurnTask({
|
|
1470
|
+
if (input.signal && !input.signal.aborted) input.signal.addEventListener("abort", abortHandler, { once: true });
|
|
1471
|
+
this.queueSessionTask(input.handle.acpxRecordId ?? input.handle.sessionKey, async () => this.runRuntimeTurnTask({
|
|
1219
1472
|
input,
|
|
1220
1473
|
promptInput,
|
|
1221
1474
|
queue,
|
|
@@ -1224,7 +1477,7 @@ var AcpRuntimeManager = class {
|
|
|
1224
1477
|
state,
|
|
1225
1478
|
settleResult,
|
|
1226
1479
|
abortHandler
|
|
1227
|
-
});
|
|
1480
|
+
}), requestCancel);
|
|
1228
1481
|
return {
|
|
1229
1482
|
requestId: input.requestId,
|
|
1230
1483
|
promptStarted: promptStarted.promise,
|
|
@@ -1246,30 +1499,39 @@ var AcpRuntimeManager = class {
|
|
|
1246
1499
|
let turn;
|
|
1247
1500
|
let terminalResult;
|
|
1248
1501
|
try {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
await this.resolveRuntimeTurnReady(task, turn, resumed, loadError);
|
|
1252
|
-
if (this.cancelRuntimeTurnBeforePrompt(task)) terminalResult = {
|
|
1253
|
-
status: "cancelled",
|
|
1254
|
-
stopReason: "cancelled"
|
|
1255
|
-
};
|
|
1256
|
-
else {
|
|
1257
|
-
await this.applyPendingRuntimeTurnCancel(task, turn);
|
|
1258
|
-
const response = await this.runRuntimePrompt(task, turn, sessionId);
|
|
1259
|
-
await this.saveCompletedRuntimeTurn(turn, response.stopReason);
|
|
1502
|
+
if (this.cancelRuntimeTurnBeforePrompt(task)) {
|
|
1503
|
+
await this.closeRetainedOneShotHandle(task.input.handle);
|
|
1260
1504
|
terminalResult = {
|
|
1261
|
-
status:
|
|
1262
|
-
|
|
1263
|
-
|
|
1505
|
+
status: "cancelled",
|
|
1506
|
+
stopReason: "cancelled"
|
|
1507
|
+
};
|
|
1508
|
+
} else {
|
|
1509
|
+
turn = await this.prepareRuntimeTurn(task);
|
|
1510
|
+
const { sessionId } = await this.connectRuntimeTurn(task, turn);
|
|
1511
|
+
this.assertOpen();
|
|
1512
|
+
await this.resolveRuntimeTurnReady(task, turn);
|
|
1513
|
+
if (this.cancelRuntimeTurnBeforePrompt(task)) terminalResult = {
|
|
1514
|
+
status: "cancelled",
|
|
1515
|
+
stopReason: "cancelled"
|
|
1264
1516
|
};
|
|
1517
|
+
else {
|
|
1518
|
+
await this.applyPendingRuntimeTurnCancel(task, turn);
|
|
1519
|
+
const response = await this.runRuntimePrompt(task, turn, sessionId);
|
|
1520
|
+
await this.saveCompletedRuntimeTurn(turn);
|
|
1521
|
+
terminalResult = {
|
|
1522
|
+
status: response.stopReason === "cancelled" ? "cancelled" : "completed",
|
|
1523
|
+
...response.stopReason ? { stopReason: response.stopReason } : {},
|
|
1524
|
+
...response._meta === void 0 ? {} : { _meta: response._meta }
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1265
1527
|
}
|
|
1266
1528
|
} catch (error) {
|
|
1267
1529
|
terminalResult = this.failRuntimeTurn(task, error);
|
|
1268
1530
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1531
|
+
const finalization = await settleAttempt(async () => this.finalizeRuntimeTurn(task, turn));
|
|
1532
|
+
if (!finalization.ok) {
|
|
1533
|
+
task.settleResult(this.failRuntimeTurn(task, finalization.error));
|
|
1534
|
+
throw finalization.error;
|
|
1273
1535
|
}
|
|
1274
1536
|
task.settleResult(terminalResult);
|
|
1275
1537
|
}
|
|
@@ -1283,7 +1545,8 @@ var AcpRuntimeManager = class {
|
|
|
1283
1545
|
conversation: turn.conversation,
|
|
1284
1546
|
promptMessageId: turn.promptMessageId,
|
|
1285
1547
|
onPromptRequestWritten: () => task.promptStarted.resolve(),
|
|
1286
|
-
onElicitation: task.input.onElicitation
|
|
1548
|
+
onElicitation: task.input.onElicitation,
|
|
1549
|
+
onPermissionRequest: task.input.onPermissionRequest
|
|
1287
1550
|
});
|
|
1288
1551
|
} finally {
|
|
1289
1552
|
turn.client.endPromptElicitation?.(sessionId);
|
|
@@ -1291,7 +1554,10 @@ var AcpRuntimeManager = class {
|
|
|
1291
1554
|
}
|
|
1292
1555
|
async prepareRuntimeTurn(task) {
|
|
1293
1556
|
const recordId = task.input.handle.acpxRecordId ?? task.input.handle.sessionKey;
|
|
1294
|
-
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () =>
|
|
1557
|
+
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => {
|
|
1558
|
+
await this.retrySessionCleanup(recordId);
|
|
1559
|
+
return await this.prepareRuntimeTurnWithOwnership(task);
|
|
1560
|
+
});
|
|
1295
1561
|
}
|
|
1296
1562
|
async prepareRuntimeTurnWithOwnership(task) {
|
|
1297
1563
|
const { record, retainedOwner, conversation, acpxState, promptMessageId } = await this.prepareRuntimeTurnState(task);
|
|
@@ -1398,7 +1664,10 @@ var AcpRuntimeManager = class {
|
|
|
1398
1664
|
agentCommand: record.agentCommand,
|
|
1399
1665
|
agentArgv: record.agentArgv,
|
|
1400
1666
|
cwd: record.cwd,
|
|
1401
|
-
mcpServers:
|
|
1667
|
+
mcpServers: this.resolveMcpServers({
|
|
1668
|
+
...record,
|
|
1669
|
+
sessionKey: record.name ?? record.acpxRecordId
|
|
1670
|
+
}),
|
|
1402
1671
|
permissionMode: this.options.permissionMode,
|
|
1403
1672
|
nonInteractivePermissions: this.options.nonInteractivePermissions,
|
|
1404
1673
|
permissionPolicy: this.options.permissionPolicy,
|
|
@@ -1426,24 +1695,24 @@ var AcpRuntimeManager = class {
|
|
|
1426
1695
|
return {
|
|
1427
1696
|
hasActivePrompt: () => turn.client.hasActivePrompt(),
|
|
1428
1697
|
requestCancelActivePrompt: async () => await this.requestRuntimeTurnCancel(task, turn),
|
|
1429
|
-
setSessionMode: async (modeId) => {
|
|
1698
|
+
setSessionMode: async (modeId, authority) => {
|
|
1430
1699
|
await this.waitForRuntimeControlSession(task, turn);
|
|
1431
|
-
await turn.client.setSessionMode(turn.activeSessionId, modeId);
|
|
1700
|
+
await turn.client.setSessionMode(turn.activeSessionId, modeId, authority);
|
|
1432
1701
|
const nextState = cloneSessionAcpxState(turn.acpxState) ?? {};
|
|
1433
1702
|
nextState.desired_mode_id = modeId;
|
|
1434
1703
|
turn.acpxState = nextState;
|
|
1435
1704
|
},
|
|
1436
|
-
setSessionModel: async (modelId) => {
|
|
1705
|
+
setSessionModel: async (modelId, authority) => {
|
|
1437
1706
|
await this.waitForRuntimeControlSession(task, turn);
|
|
1438
1707
|
const models = advertisedModelState(turn.acpxState);
|
|
1439
|
-
const response = await turn.client.setSessionModel(turn.activeSessionId, modelId, models);
|
|
1708
|
+
const response = await turn.client.setSessionModel(turn.activeSessionId, modelId, models, authority);
|
|
1440
1709
|
turn.acpxState = applyModelSelection(turn.acpxState, modelId, response);
|
|
1441
1710
|
return response;
|
|
1442
1711
|
},
|
|
1443
|
-
setSessionConfigOption: async (configId, value) => {
|
|
1444
|
-
return (await task.state.activeController.setResolvedSessionConfigOption(configId, value)).response;
|
|
1712
|
+
setSessionConfigOption: async (configId, value, authority) => {
|
|
1713
|
+
return (await task.state.activeController.setResolvedSessionConfigOption(configId, value, authority)).response;
|
|
1445
1714
|
},
|
|
1446
|
-
setResolvedSessionConfigOption: async (configId, value) => await this.setRuntimeResolvedSessionConfigOption(task, turn, configId, value)
|
|
1715
|
+
setResolvedSessionConfigOption: async (configId, value, authority) => await this.setRuntimeResolvedSessionConfigOption(task, turn, configId, value, authority)
|
|
1447
1716
|
};
|
|
1448
1717
|
}
|
|
1449
1718
|
async waitForRuntimeControlSession(task, turn) {
|
|
@@ -1456,14 +1725,14 @@ var AcpRuntimeManager = class {
|
|
|
1456
1725
|
task.state.pendingCancel = true;
|
|
1457
1726
|
return true;
|
|
1458
1727
|
}
|
|
1459
|
-
async setRuntimeResolvedSessionConfigOption(task, turn, configId, value) {
|
|
1728
|
+
async setRuntimeResolvedSessionConfigOption(task, turn, configId, value, authority) {
|
|
1460
1729
|
await this.waitForRuntimeControlSession(task, turn);
|
|
1461
1730
|
const resolvedConfigId = resolveSupportedConfigOptionId({
|
|
1462
1731
|
...turn.record,
|
|
1463
1732
|
acpx: turn.acpxState ?? void 0
|
|
1464
1733
|
}, configId);
|
|
1465
1734
|
const modelConfigId = advertisedModelState(turn.acpxState)?.configId;
|
|
1466
|
-
const response = await turn.client.setSessionConfigOption(turn.activeSessionId, resolvedConfigId, value);
|
|
1735
|
+
const response = await turn.client.setSessionConfigOption(turn.activeSessionId, resolvedConfigId, value, authority);
|
|
1467
1736
|
turn.acpxState = applyConfigOptionSelection(turn.acpxState, resolvedConfigId, value, response, modelConfigId);
|
|
1468
1737
|
return {
|
|
1469
1738
|
configId: resolvedConfigId,
|
|
@@ -1506,7 +1775,7 @@ var AcpRuntimeManager = class {
|
|
|
1506
1775
|
const controller = task.state.activeController;
|
|
1507
1776
|
if (controller) this.activeControllers.set(turn.record.acpxRecordId, controller);
|
|
1508
1777
|
}
|
|
1509
|
-
async resolveRuntimeTurnReady(task, turn
|
|
1778
|
+
async resolveRuntimeTurnReady(task, turn) {
|
|
1510
1779
|
task.sessionReady.resolve();
|
|
1511
1780
|
turn.record.lastRequestId = task.input.requestId;
|
|
1512
1781
|
turn.record.lastPromptAt = isoNow();
|
|
@@ -1514,14 +1783,6 @@ var AcpRuntimeManager = class {
|
|
|
1514
1783
|
turn.record.closedAt = void 0;
|
|
1515
1784
|
turn.record.lastUsedAt = isoNow();
|
|
1516
1785
|
await turn.liveCheckpoint.checkpoint();
|
|
1517
|
-
this.emitRuntimeTurnLoadStatus(task, resumed, loadError);
|
|
1518
|
-
}
|
|
1519
|
-
emitRuntimeTurnLoadStatus(task, resumed, loadError) {
|
|
1520
|
-
if (!resumed && !loadError) return;
|
|
1521
|
-
this.emitRuntimeTurnEvent(task, {
|
|
1522
|
-
type: "status",
|
|
1523
|
-
text: loadError ? `session reconnect fallback: ${loadError}` : "session resumed"
|
|
1524
|
-
});
|
|
1525
1786
|
}
|
|
1526
1787
|
cancelRuntimeTurnBeforePrompt(task) {
|
|
1527
1788
|
if (!task.state.pendingCancel && !task.input.signal?.aborted) return false;
|
|
@@ -1535,7 +1796,7 @@ var AcpRuntimeManager = class {
|
|
|
1535
1796
|
if (cancelled) task.state.pendingCancel = false;
|
|
1536
1797
|
return cancelled;
|
|
1537
1798
|
}
|
|
1538
|
-
async saveCompletedRuntimeTurn(turn
|
|
1799
|
+
async saveCompletedRuntimeTurn(turn) {
|
|
1539
1800
|
turn.record.acpSessionId = turn.activeSessionId;
|
|
1540
1801
|
reconcileAgentSessionId(turn.record, turn.record.agentSessionId);
|
|
1541
1802
|
turn.record.protocolVersion = turn.client.initializeResult?.protocolVersion;
|
|
@@ -1583,8 +1844,11 @@ var AcpRuntimeManager = class {
|
|
|
1583
1844
|
async closeRuntimeTurnClient(turn, pooled) {
|
|
1584
1845
|
if (!turn || pooled) return;
|
|
1585
1846
|
turn.owner.activeTurn = void 0;
|
|
1586
|
-
const failure = firstFailedAttempt([await settleAttempt(() => turn.client.clearEventHandlers()), await settleAttempt(async () => turn.client
|
|
1587
|
-
if (failure)
|
|
1847
|
+
const failure = firstFailedAttempt([await settleAttempt(() => turn.client.clearEventHandlers()), await settleAttempt(async () => this.closeClient(turn.client))]);
|
|
1848
|
+
if (failure) {
|
|
1849
|
+
this.retiringSessionOwners.add(turn.owner);
|
|
1850
|
+
throw failure.error;
|
|
1851
|
+
}
|
|
1588
1852
|
}
|
|
1589
1853
|
cleanupRuntimeTurn(task, turn) {
|
|
1590
1854
|
if (turn) {
|
|
@@ -1615,51 +1879,62 @@ var AcpRuntimeManager = class {
|
|
|
1615
1879
|
yield* turn.events;
|
|
1616
1880
|
yield legacyTerminalEventFromTurnResult(await turn.result);
|
|
1617
1881
|
}
|
|
1618
|
-
|
|
1882
|
+
getStatus(handle) {
|
|
1883
|
+
this.assertOpen();
|
|
1884
|
+
return this.trackTask(this.readStatus(handle));
|
|
1885
|
+
}
|
|
1886
|
+
async readStatus(handle) {
|
|
1619
1887
|
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1620
1888
|
const owner = this.retainedSessionOwners.get(recordId);
|
|
1621
1889
|
if (owner) await this.flushSessionOwner(owner);
|
|
1622
|
-
|
|
1623
|
-
return {
|
|
1624
|
-
summary: statusSummary(record),
|
|
1625
|
-
acpxRecordId: record.acpxRecordId,
|
|
1626
|
-
backendSessionId: record.acpSessionId,
|
|
1627
|
-
agentSessionId: record.agentSessionId,
|
|
1628
|
-
...buildModelsField(record),
|
|
1629
|
-
...buildUsageField(record),
|
|
1630
|
-
...buildAvailableCommandsField(record),
|
|
1631
|
-
details: {
|
|
1632
|
-
cwd: record.cwd,
|
|
1633
|
-
lastUsedAt: record.lastUsedAt,
|
|
1634
|
-
closed: record.closed === true,
|
|
1635
|
-
...record.acpx?.config_options !== void 0 ? { configOptions: structuredClone(record.acpx.config_options) } : {}
|
|
1636
|
-
}
|
|
1637
|
-
};
|
|
1890
|
+
return runtimeStatusFromRecord(await this.requireRecord(recordId));
|
|
1638
1891
|
}
|
|
1639
|
-
async setMode(handle, mode, sessionMode = "persistent") {
|
|
1892
|
+
async setMode(handle, mode, sessionMode = "persistent", authority) {
|
|
1640
1893
|
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1641
|
-
await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setModeWithOwnership(handle, mode, sessionMode));
|
|
1894
|
+
await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setModeWithOwnership(handle, mode, sessionMode, authority), authority);
|
|
1642
1895
|
}
|
|
1643
|
-
async setModeWithOwnership(handle, mode, sessionMode) {
|
|
1896
|
+
async setModeWithOwnership(handle, mode, sessionMode, authority) {
|
|
1644
1897
|
const record = await this.requireRecord(handle.acpxRecordId ?? handle.sessionKey);
|
|
1898
|
+
assertControlAuthority(authority);
|
|
1645
1899
|
const controller = this.activeControllers.get(record.acpxRecordId);
|
|
1646
1900
|
let targetRecord = record;
|
|
1647
|
-
if (controller) await controller.setSessionMode(mode);
|
|
1901
|
+
if (controller) await controller.setSessionMode(mode, authority);
|
|
1648
1902
|
else targetRecord = (await this.withRuntimeControlSession(record, sessionMode, async ({ client, sessionId }) => {
|
|
1649
|
-
await client.setSessionMode(sessionId, mode);
|
|
1650
|
-
})).record;
|
|
1903
|
+
await client.setSessionMode(sessionId, mode, authority);
|
|
1904
|
+
}, void 0, authority)).record;
|
|
1651
1905
|
setDesiredModeId(targetRecord, mode);
|
|
1652
1906
|
await this.options.sessionStore.save(targetRecord);
|
|
1653
1907
|
}
|
|
1654
|
-
async
|
|
1908
|
+
async setModel(handle, model, sessionMode = "persistent", authority) {
|
|
1909
|
+
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1910
|
+
await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setModelWithOwnership(handle, model, sessionMode, authority), authority);
|
|
1911
|
+
}
|
|
1912
|
+
async setModelWithOwnership(handle, model, sessionMode, authority) {
|
|
1913
|
+
const record = await this.requireRecord(handle.acpxRecordId ?? handle.sessionKey);
|
|
1914
|
+
assertControlAuthority(authority);
|
|
1915
|
+
const controller = this.activeControllers.get(record.acpxRecordId);
|
|
1916
|
+
if (controller) {
|
|
1917
|
+
const response = await controller.setSessionModel(model, authority);
|
|
1918
|
+
record.acpx = applyModelSelection(record.acpx, model, response);
|
|
1919
|
+
await this.options.sessionStore.save(record);
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1922
|
+
const result = await this.withRuntimeControlSession(record, sessionMode, async ({ client, sessionId, record: connectedRecord }) => {
|
|
1923
|
+
const response = await client.setSessionModel(sessionId, model, advertisedModelState(connectedRecord.acpx), authority);
|
|
1924
|
+
connectedRecord.acpx = applyModelSelection(connectedRecord.acpx, model, response);
|
|
1925
|
+
}, { key: "model" }, authority);
|
|
1926
|
+
await this.options.sessionStore.save(result.record);
|
|
1927
|
+
}
|
|
1928
|
+
async setConfigOption(handle, key, value, sessionMode = "persistent", authority) {
|
|
1655
1929
|
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1656
|
-
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setConfigOptionWithOwnership(handle, key, value, sessionMode));
|
|
1930
|
+
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setConfigOptionWithOwnership(handle, key, value, sessionMode, authority), authority);
|
|
1657
1931
|
}
|
|
1658
|
-
async setConfigOptionWithOwnership(handle, key, value, sessionMode) {
|
|
1932
|
+
async setConfigOptionWithOwnership(handle, key, value, sessionMode, authority) {
|
|
1659
1933
|
const record = await this.requireRecord(handle.acpxRecordId ?? handle.sessionKey);
|
|
1934
|
+
assertControlAuthority(authority);
|
|
1660
1935
|
const controller = this.activeControllers.get(record.acpxRecordId);
|
|
1661
1936
|
if (controller) {
|
|
1662
|
-
const { configId, response } = await controller.setResolvedSessionConfigOption(key, value);
|
|
1937
|
+
const { configId, response } = await controller.setResolvedSessionConfigOption(key, value, authority);
|
|
1663
1938
|
record.acpx = applyConfigOptionSelection(record.acpx, configId, value, response);
|
|
1664
1939
|
await this.options.sessionStore.save(record);
|
|
1665
1940
|
return response;
|
|
@@ -1667,25 +1942,44 @@ var AcpRuntimeManager = class {
|
|
|
1667
1942
|
const result = await this.withRuntimeControlSession(record, sessionMode, async ({ client, sessionId, record: connectedRecord }) => {
|
|
1668
1943
|
const configId = resolveSupportedConfigOptionId(connectedRecord, key);
|
|
1669
1944
|
const modelConfigId = advertisedModelState(connectedRecord.acpx)?.configId;
|
|
1670
|
-
const response = await client.setSessionConfigOption(sessionId, configId, value);
|
|
1945
|
+
const response = await client.setSessionConfigOption(sessionId, configId, value, authority);
|
|
1671
1946
|
connectedRecord.acpx = applyConfigOptionSelection(connectedRecord.acpx, configId, value, response, modelConfigId);
|
|
1672
1947
|
return response;
|
|
1673
1948
|
}, {
|
|
1674
1949
|
key,
|
|
1675
1950
|
resolve: (connectedRecord) => resolveSupportedConfigOptionId(connectedRecord, key)
|
|
1676
|
-
});
|
|
1951
|
+
}, authority);
|
|
1677
1952
|
await this.options.sessionStore.save(result.record);
|
|
1678
1953
|
return result.value;
|
|
1679
1954
|
}
|
|
1680
1955
|
async cancel(handle) {
|
|
1681
1956
|
await this.activeControllers.get(handle.acpxRecordId ?? handle.sessionKey)?.requestCancelActivePrompt();
|
|
1682
1957
|
}
|
|
1683
|
-
|
|
1958
|
+
prepareFreshSession(handle) {
|
|
1959
|
+
this.assertOpen();
|
|
1960
|
+
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1961
|
+
const cancelled = this.cancelSessionTasks(recordId);
|
|
1962
|
+
cancelled.catch(() => {});
|
|
1963
|
+
return this.queueSessionTask(recordId, async () => {
|
|
1964
|
+
const tasks = await cancelled;
|
|
1965
|
+
await Promise.all(tasks.map((task) => task.completion));
|
|
1966
|
+
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.closeRuntimeSession(handle, "prepare-fresh"));
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
close(handle, options = {}) {
|
|
1970
|
+
this.assertOpen();
|
|
1971
|
+
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1972
|
+
return this.trackTask(this.withManagerLock(this.runtimeOperationLocks, recordId, async () => {
|
|
1973
|
+
return await this.closeRuntimeSession(handle, options.discardPersistentState === true ? "discard" : "release");
|
|
1974
|
+
}));
|
|
1975
|
+
}
|
|
1976
|
+
async closeRuntimeSession(handle, intent) {
|
|
1684
1977
|
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1978
|
+
await this.retrySessionCleanup(recordId);
|
|
1685
1979
|
const record = await this.resolveRuntimeRecordForClose(recordId);
|
|
1686
1980
|
this.markActiveRuntimeRecordClosing(record);
|
|
1687
|
-
await this.
|
|
1688
|
-
await this.closeRuntimeRecordOwnership(record,
|
|
1981
|
+
if (intent !== "prepare-fresh") await this.cancelSessionTasks(recordId);
|
|
1982
|
+
await this.closeRuntimeRecordOwnership(record, intent);
|
|
1689
1983
|
record.closed = true;
|
|
1690
1984
|
record.closedAt = isoNow();
|
|
1691
1985
|
await this.options.sessionStore.save(record);
|
|
@@ -1698,14 +1992,20 @@ var AcpRuntimeManager = class {
|
|
|
1698
1992
|
markActiveRuntimeRecordClosing(record) {
|
|
1699
1993
|
if (this.activeControllers.has(record.acpxRecordId)) this.closingActiveRecords.add(record.acpxRecordId);
|
|
1700
1994
|
}
|
|
1701
|
-
async closeRuntimeRecordOwnership(record,
|
|
1702
|
-
if (
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1995
|
+
async closeRuntimeRecordOwnership(record, intent) {
|
|
1996
|
+
if (intent === "discard") await this.closeBackendSession(record);
|
|
1997
|
+
else if (intent === "prepare-fresh") {
|
|
1998
|
+
const owner = this.retainedSessionOwners.get(record.acpxRecordId);
|
|
1999
|
+
if (owner) {
|
|
2000
|
+
this.removeRetainedSessionOwner(owner);
|
|
2001
|
+
this.retiringSessionOwners.add(owner);
|
|
2002
|
+
}
|
|
2003
|
+
await this.retrySessionCleanup(record.acpxRecordId);
|
|
1708
2004
|
} else await this.closeRetainedSessionOwner(record.acpxRecordId);
|
|
2005
|
+
if (intent !== "release") record.acpx = {
|
|
2006
|
+
...record.acpx,
|
|
2007
|
+
reset_on_next_ensure: true
|
|
2008
|
+
};
|
|
1709
2009
|
}
|
|
1710
2010
|
async closeBackendSession(record) {
|
|
1711
2011
|
const connection = await this.acquireBackendCloseConnection(record);
|
|
@@ -1714,16 +2014,16 @@ var AcpRuntimeManager = class {
|
|
|
1714
2014
|
} catch (error) {
|
|
1715
2015
|
this.handleBackendSessionCloseError(record, error);
|
|
1716
2016
|
} finally {
|
|
1717
|
-
await this.
|
|
2017
|
+
await this.finalizeSessionConnection(connection);
|
|
1718
2018
|
}
|
|
1719
2019
|
}
|
|
1720
|
-
async
|
|
2020
|
+
async finalizeSessionConnection(connection) {
|
|
1721
2021
|
const failure = firstFailedAttempt([
|
|
1722
2022
|
await settleAttempt(async () => {
|
|
1723
2023
|
if (connection.owner) await this.flushSessionOwner(connection.owner);
|
|
1724
2024
|
}),
|
|
1725
2025
|
await settleAttempt(() => connection.owner?.client.clearEventHandlers()),
|
|
1726
|
-
await settleAttempt(async () => connection.client
|
|
2026
|
+
await settleAttempt(async () => this.closeClient(connection.client))
|
|
1727
2027
|
]);
|
|
1728
2028
|
if (failure) throw failure.error;
|
|
1729
2029
|
}
|
|
@@ -1733,23 +2033,7 @@ var AcpRuntimeManager = class {
|
|
|
1733
2033
|
client: owner.client,
|
|
1734
2034
|
owner
|
|
1735
2035
|
};
|
|
1736
|
-
return { client: this.
|
|
1737
|
-
agentCommand: record.agentCommand,
|
|
1738
|
-
agentArgv: record.agentArgv,
|
|
1739
|
-
cwd: record.cwd,
|
|
1740
|
-
mcpServers: [...this.options.mcpServers ?? []],
|
|
1741
|
-
permissionMode: this.options.permissionMode,
|
|
1742
|
-
nonInteractivePermissions: this.options.nonInteractivePermissions,
|
|
1743
|
-
permissionPolicy: this.options.permissionPolicy,
|
|
1744
|
-
onPermissionRequest: this.options.onPermissionRequest,
|
|
1745
|
-
elicitationModes: this.options.elicitationModes,
|
|
1746
|
-
processLifecycle: this.options.processLifecycle,
|
|
1747
|
-
processLaunchScope: {
|
|
1748
|
-
kind: "runtime-session",
|
|
1749
|
-
sessionKey: record.name ?? record.acpxRecordId
|
|
1750
|
-
},
|
|
1751
|
-
verbose: this.options.verbose
|
|
1752
|
-
}) };
|
|
2036
|
+
return { client: this.createTurnClient(record) };
|
|
1753
2037
|
}
|
|
1754
2038
|
async requestBackendSessionClose(record, connection) {
|
|
1755
2039
|
if (!connection.owner) await withTimeout(connection.client.start(), this.options.timeoutMs);
|
|
@@ -1784,7 +2068,10 @@ var FileSessionStore = class {
|
|
|
1784
2068
|
return path.join(this.sessionDir, `${safeSessionId(sessionId)}.json`);
|
|
1785
2069
|
}
|
|
1786
2070
|
async ensureDir() {
|
|
1787
|
-
await fs.mkdir(this.sessionDir, {
|
|
2071
|
+
await fs.mkdir(this.sessionDir, {
|
|
2072
|
+
recursive: true,
|
|
2073
|
+
mode: 448
|
|
2074
|
+
});
|
|
1788
2075
|
}
|
|
1789
2076
|
async load(sessionId) {
|
|
1790
2077
|
await this.ensureDir();
|
|
@@ -1804,14 +2091,9 @@ var FileSessionStore = class {
|
|
|
1804
2091
|
return parseSessionRecord(parsed) ?? void 0;
|
|
1805
2092
|
}
|
|
1806
2093
|
async save(record) {
|
|
1807
|
-
await this.ensureDir();
|
|
1808
2094
|
const persisted = serializeSessionRecordForDisk(record);
|
|
1809
2095
|
assertPersistedKeyPolicy(persisted);
|
|
1810
|
-
|
|
1811
|
-
const tempFile = createAtomicWriteTempPath(file);
|
|
1812
|
-
const payload = JSON.stringify(persisted, null, 2);
|
|
1813
|
-
await fs.writeFile(tempFile, `${payload}\n`, "utf8");
|
|
1814
|
-
await fs.rename(tempFile, file);
|
|
2096
|
+
await writePrivateJsonFile(this.filePath(record.acpxRecordId), persisted);
|
|
1815
2097
|
}
|
|
1816
2098
|
};
|
|
1817
2099
|
function createFileSessionStore(options) {
|
|
@@ -1856,125 +2138,246 @@ function writeHandleState(handle, state) {
|
|
|
1856
2138
|
handle.agentSessionId = state.agentSessionId;
|
|
1857
2139
|
}
|
|
1858
2140
|
//#endregion
|
|
1859
|
-
//#region src/runtime/
|
|
1860
|
-
|
|
1861
|
-
|
|
2141
|
+
//#region src/runtime/shared.ts
|
|
2142
|
+
const SHARED_BACKEND = "acpx-shared";
|
|
2143
|
+
function invalidOption(message) {
|
|
2144
|
+
throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", message);
|
|
1862
2145
|
}
|
|
1863
|
-
function
|
|
1864
|
-
return
|
|
2146
|
+
function sharedHandle(record) {
|
|
2147
|
+
return {
|
|
2148
|
+
backend: SHARED_BACKEND,
|
|
2149
|
+
sessionKey: record.name ?? "",
|
|
2150
|
+
runtimeSessionName: record.acpxRecordId,
|
|
2151
|
+
acpxRecordId: record.acpxRecordId,
|
|
2152
|
+
backendSessionId: record.acpSessionId,
|
|
2153
|
+
agentSessionId: record.agentSessionId,
|
|
2154
|
+
cwd: record.cwd
|
|
2155
|
+
};
|
|
1865
2156
|
}
|
|
1866
|
-
function
|
|
1867
|
-
|
|
1868
|
-
return
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
2157
|
+
function sharedRecordId(handle) {
|
|
2158
|
+
if (handle.backend !== SHARED_BACKEND || !handle.acpxRecordId) return invalidOption("A shared runtime requires a handle from a shared session.");
|
|
2159
|
+
return handle.acpxRecordId;
|
|
2160
|
+
}
|
|
2161
|
+
function turnFailure(error) {
|
|
2162
|
+
const normalized = normalizeOutputError(error, {
|
|
2163
|
+
defaultCode: "RUNTIME",
|
|
2164
|
+
origin: "queue"
|
|
2165
|
+
});
|
|
2166
|
+
if (normalized.detailCode === "QUEUE_REQUEST_CANCELLED") return {
|
|
2167
|
+
status: "cancelled",
|
|
2168
|
+
stopReason: "cancelled"
|
|
2169
|
+
};
|
|
2170
|
+
return {
|
|
2171
|
+
status: "failed",
|
|
2172
|
+
error: {
|
|
2173
|
+
message: normalized.message,
|
|
2174
|
+
code: normalized.code,
|
|
2175
|
+
detailCode: normalized.detailCode,
|
|
2176
|
+
retryable: normalized.retryable
|
|
1873
2177
|
}
|
|
1874
|
-
|
|
1875
|
-
}) ?? "undefined";
|
|
2178
|
+
};
|
|
1876
2179
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2180
|
+
/** A client of the CLI's local session owner. Shutdown detaches this client. */
|
|
2181
|
+
var SharedAcpRuntime = class {
|
|
2182
|
+
registry;
|
|
2183
|
+
options;
|
|
2184
|
+
disconnect = new AbortController();
|
|
2185
|
+
pending = /* @__PURE__ */ new Set();
|
|
2186
|
+
constructor(options) {
|
|
2187
|
+
for (const name of [
|
|
2188
|
+
"sessionStore",
|
|
2189
|
+
"agentProcessEnv",
|
|
2190
|
+
"mcpServers",
|
|
2191
|
+
"processLifecycle",
|
|
2192
|
+
"onPermissionRequest",
|
|
2193
|
+
"elicitationModes"
|
|
2194
|
+
]) if (Object.hasOwn(options, name)) invalidOption(`${name} belongs to the in-process runtime and is not supported by shared sessions.`);
|
|
2195
|
+
this.options = {
|
|
2196
|
+
...options,
|
|
2197
|
+
cwd: path.resolve(options.cwd),
|
|
2198
|
+
authCredentials: options.authCredentials ? { ...options.authCredentials } : void 0
|
|
2199
|
+
};
|
|
2200
|
+
this.registry = options.agentRegistry ?? createAgentRegistry();
|
|
1886
2201
|
}
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2202
|
+
assertOpen() {
|
|
2203
|
+
if (this.disconnect.signal.aborted) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "Shared ACP runtime is shut down.");
|
|
2204
|
+
}
|
|
2205
|
+
track(task) {
|
|
2206
|
+
this.pending.add(task);
|
|
2207
|
+
task.then(() => this.pending.delete(task), () => this.pending.delete(task));
|
|
2208
|
+
return task;
|
|
2209
|
+
}
|
|
2210
|
+
sessionOptions(input) {
|
|
2211
|
+
if (!input.agent.trim()) invalidOption("ACP agent id is required.");
|
|
1897
2212
|
return {
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
`agent=${agentName}`,
|
|
1902
|
-
`command=${agentCommand.agentCommand}`,
|
|
1903
|
-
`cwd=${options.cwd}`,
|
|
1904
|
-
...client.initializeResult?.protocolVersion ? [`protocolVersion=${client.initializeResult.protocolVersion}`] : []
|
|
1905
|
-
]
|
|
2213
|
+
...normalizeAgentCommandInput(this.registry.resolve(input.agent)),
|
|
2214
|
+
cwd: path.resolve(input.cwd ?? this.options.cwd),
|
|
2215
|
+
name: input.sessionKey.trim() || void 0
|
|
1906
2216
|
};
|
|
1907
|
-
}
|
|
2217
|
+
}
|
|
2218
|
+
async findSession(input) {
|
|
2219
|
+
this.assertOpen();
|
|
2220
|
+
const record = await findSession(this.sessionOptions(input));
|
|
2221
|
+
return record ? sharedHandle(record) : void 0;
|
|
2222
|
+
}
|
|
2223
|
+
ensureSession(input) {
|
|
2224
|
+
this.assertOpen();
|
|
2225
|
+
if (input.mode !== "persistent") invalidOption("Shared sessions require persistent mode; use the in-process runtime for oneshot sessions.");
|
|
2226
|
+
const session = this.sessionOptions(input);
|
|
2227
|
+
return this.track(ensureSession({
|
|
2228
|
+
...this.options,
|
|
2229
|
+
...session,
|
|
2230
|
+
walkBoundary: session.cwd,
|
|
2231
|
+
sessionOptions: input.sessionOptions,
|
|
2232
|
+
resumeSessionId: input.resumeSessionId,
|
|
2233
|
+
handleProcessInterrupts: false,
|
|
2234
|
+
signal: this.disconnect.signal
|
|
2235
|
+
}).then(({ record }) => {
|
|
2236
|
+
this.assertOpen();
|
|
2237
|
+
return sharedHandle(record);
|
|
2238
|
+
}));
|
|
2239
|
+
}
|
|
2240
|
+
startTurn(input) {
|
|
2241
|
+
this.assertOpen();
|
|
2242
|
+
if (input.mode !== "prompt" || input.onPermissionRequest || input.onElicitation) invalidOption("Shared turns support prompt mode and the client's static permission policy; in-process callbacks and steering are unsupported.");
|
|
2243
|
+
if (!input.requestId.trim()) invalidOption("A shared turn requires a requestId.");
|
|
2244
|
+
const sessionId = sharedRecordId(input.handle);
|
|
2245
|
+
const prompt = toPromptInput(input.text, input.attachments);
|
|
2246
|
+
const events = new AsyncEventQueue();
|
|
2247
|
+
const promptStarted = createDeferred();
|
|
2248
|
+
promptStarted.promise.catch(() => {});
|
|
2249
|
+
let accepted = false;
|
|
2250
|
+
let settled = false;
|
|
2251
|
+
let cancelRequested = input.signal?.aborted === true;
|
|
2252
|
+
let cancellation;
|
|
2253
|
+
const cancel = async () => {
|
|
2254
|
+
cancelRequested = true;
|
|
2255
|
+
if (!accepted || settled) return;
|
|
2256
|
+
cancellation ??= tryCancelOnRunningOwner({
|
|
2257
|
+
sessionId,
|
|
2258
|
+
targetRequestId: input.requestId
|
|
2259
|
+
}).then(() => {});
|
|
2260
|
+
await cancellation;
|
|
2261
|
+
};
|
|
2262
|
+
const onAbort = () => {
|
|
2263
|
+
cancel().catch(() => {});
|
|
2264
|
+
};
|
|
2265
|
+
input.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2266
|
+
const run = async () => {
|
|
2267
|
+
if (cancelRequested) return {
|
|
2268
|
+
status: "cancelled",
|
|
2269
|
+
stopReason: "cancelled"
|
|
2270
|
+
};
|
|
2271
|
+
if ((await resolveSessionRecord(sessionId)).closed) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "Shared session is closed; ensure it again before submitting a turn.");
|
|
2272
|
+
if (cancelRequested) return {
|
|
2273
|
+
status: "cancelled",
|
|
2274
|
+
stopReason: "cancelled"
|
|
2275
|
+
};
|
|
2276
|
+
const result = await sendSession({
|
|
2277
|
+
...this.options,
|
|
2278
|
+
sessionId,
|
|
2279
|
+
requestId: input.requestId,
|
|
2280
|
+
prompt: typeof prompt === "string" ? textPrompt(prompt) : prompt,
|
|
2281
|
+
resumePolicy: "same-session-only",
|
|
2282
|
+
requireSharedRuntime: true,
|
|
2283
|
+
signal: this.disconnect.signal,
|
|
2284
|
+
timeoutMs: input.timeoutMs ?? this.options.timeoutMs,
|
|
2285
|
+
queueOwnerArgs: [fileURLToPath(import.meta.resolve("acpx/dist/cli.js")), "__queue-owner"],
|
|
2286
|
+
onQueueAccepted: () => {
|
|
2287
|
+
accepted = true;
|
|
2288
|
+
if (cancelRequested) cancel().catch(() => {});
|
|
2289
|
+
},
|
|
2290
|
+
onPromptStarted: () => promptStarted.resolve(),
|
|
2291
|
+
outputFormatter: {
|
|
2292
|
+
...DISCARD_OUTPUT_FORMATTER,
|
|
2293
|
+
onAcpMessage: (message) => {
|
|
2294
|
+
const event = parsePromptEventLine(JSON.stringify(message));
|
|
2295
|
+
if (event) events.push(event);
|
|
2296
|
+
}
|
|
2297
|
+
},
|
|
2298
|
+
errorEmissionPolicy: { queueErrorAlreadyEmitted: false },
|
|
2299
|
+
waitForCompletion: true
|
|
2300
|
+
});
|
|
2301
|
+
if ("queued" in result) throw new AcpRuntimeError("ACP_TURN_FAILED", "Shared turn ended without a completion result.");
|
|
2302
|
+
return {
|
|
2303
|
+
status: result.stopReason === "cancelled" ? "cancelled" : "completed",
|
|
2304
|
+
stopReason: result.stopReason,
|
|
2305
|
+
_meta: result._meta
|
|
2306
|
+
};
|
|
2307
|
+
};
|
|
2308
|
+
const result = this.track(run().catch(turnFailure).finally(() => {
|
|
2309
|
+
settled = true;
|
|
2310
|
+
promptStarted.reject(new AcpRuntimeError("ACP_TURN_FAILED", "Turn settled before its prompt was written."));
|
|
2311
|
+
input.signal?.removeEventListener("abort", onAbort);
|
|
2312
|
+
events.close();
|
|
2313
|
+
}));
|
|
1908
2314
|
return {
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
2315
|
+
requestId: input.requestId,
|
|
2316
|
+
promptStarted: promptStarted.promise,
|
|
2317
|
+
events: events.iterate(),
|
|
2318
|
+
result,
|
|
2319
|
+
cancel,
|
|
2320
|
+
closeStream: async () => {
|
|
2321
|
+
events.clear();
|
|
2322
|
+
events.close();
|
|
2323
|
+
}
|
|
1917
2324
|
};
|
|
1918
|
-
} finally {
|
|
1919
|
-
await client.close().catch(() => {});
|
|
1920
2325
|
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
2326
|
+
async *runTurn(input) {
|
|
2327
|
+
const turn = this.startTurn(input);
|
|
2328
|
+
yield* turn.events;
|
|
2329
|
+
yield legacyTerminalEventFromTurnResult(await turn.result);
|
|
2330
|
+
}
|
|
2331
|
+
async getStatus(input) {
|
|
2332
|
+
this.assertOpen();
|
|
2333
|
+
return runtimeStatusFromRecord(await resolveSessionRecord(sharedRecordId(input.handle)));
|
|
2334
|
+
}
|
|
2335
|
+
watchSession(input) {
|
|
2336
|
+
this.assertOpen();
|
|
2337
|
+
const signal = input.signal ? AbortSignal.any([input.signal, this.disconnect.signal]) : this.disconnect.signal;
|
|
2338
|
+
const recordId = sharedRecordId(input.handle);
|
|
2339
|
+
const record = readSessionRecord(recordId).then((value) => {
|
|
2340
|
+
if (!value || value.acpxRecordId !== recordId) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "Shared session record is unavailable.");
|
|
2341
|
+
return value;
|
|
2342
|
+
});
|
|
2343
|
+
record.catch(() => {});
|
|
2344
|
+
return watchSession({
|
|
2345
|
+
record,
|
|
2346
|
+
cursor: input.cursor,
|
|
2347
|
+
signal
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
async cancel(input) {
|
|
2351
|
+
this.assertOpen();
|
|
2352
|
+
await this.track(cancelSessionPrompt({ sessionId: sharedRecordId(input.handle) }));
|
|
2353
|
+
}
|
|
2354
|
+
async close(input) {
|
|
2355
|
+
this.assertOpen();
|
|
2356
|
+
await this.track(closeSession(sharedRecordId(input.handle)));
|
|
2357
|
+
}
|
|
2358
|
+
async shutdown() {
|
|
2359
|
+
this.disconnect.abort(new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "Shared client detached; submitted work may still be running."));
|
|
2360
|
+
await Promise.allSettled(this.pending);
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
function createSharedAcpRuntime(options) {
|
|
2364
|
+
return new SharedAcpRuntime(options);
|
|
1939
2365
|
}
|
|
1940
2366
|
//#endregion
|
|
1941
2367
|
//#region src/runtime.ts
|
|
1942
2368
|
const ACPX_BACKEND_ID = "acpx";
|
|
1943
2369
|
const ACPX_CAPABILITIES = { controls: [
|
|
1944
2370
|
"session/set_mode",
|
|
2371
|
+
"session/set_model",
|
|
1945
2372
|
"session/set_config_option",
|
|
1946
2373
|
"session/status"
|
|
1947
2374
|
] };
|
|
1948
|
-
function createAgentRegistry(params) {
|
|
1949
|
-
const overrides = normalizeRegistryOverrides(params?.overrides);
|
|
1950
|
-
return {
|
|
1951
|
-
resolve(agentName) {
|
|
1952
|
-
const normalizedAgentName = normalizeAgentName(agentName);
|
|
1953
|
-
return overrides[normalizedAgentName] ?? overrides[resolveCanonicalAgentName(agentName)] ?? resolveAgentArgv(agentName) ?? resolveAgentCommand(agentName);
|
|
1954
|
-
},
|
|
1955
|
-
list() {
|
|
1956
|
-
return listBuiltInAgents(overrides);
|
|
1957
|
-
}
|
|
1958
|
-
};
|
|
1959
|
-
}
|
|
1960
|
-
function normalizeRegistryOverrides(values) {
|
|
1961
|
-
const normalized = {};
|
|
1962
|
-
for (const [name, value] of Object.entries(values ?? {})) {
|
|
1963
|
-
const normalizedName = normalizeAgentName(name);
|
|
1964
|
-
if (!normalizedName) continue;
|
|
1965
|
-
const normalizedValue = normalizeRegistryOverride(value);
|
|
1966
|
-
if (normalizedValue) normalized[normalizedName] = normalizedValue;
|
|
1967
|
-
}
|
|
1968
|
-
return normalized;
|
|
1969
|
-
}
|
|
1970
|
-
function normalizeRegistryOverride(value) {
|
|
1971
|
-
if (typeof value === "string") return value.trim() || void 0;
|
|
1972
|
-
return value.length > 0 && value[0]?.length ? [...value] : void 0;
|
|
1973
|
-
}
|
|
1974
2375
|
var AcpxRuntime = class {
|
|
1975
2376
|
options;
|
|
1976
2377
|
testOptions;
|
|
1977
2378
|
healthy = false;
|
|
2379
|
+
shutdownTask;
|
|
2380
|
+
probeTasks = /* @__PURE__ */ new Set();
|
|
1978
2381
|
manager = null;
|
|
1979
2382
|
managerPromise = null;
|
|
1980
2383
|
constructor(options, testOptions) {
|
|
@@ -2002,11 +2405,27 @@ var AcpxRuntime = class {
|
|
|
2002
2405
|
details: normalizeRuntimeDetails(report.details)
|
|
2003
2406
|
};
|
|
2004
2407
|
}
|
|
2005
|
-
|
|
2408
|
+
resolveSessionIdentity(input) {
|
|
2006
2409
|
const sessionName = input.sessionKey.trim();
|
|
2007
2410
|
if (!sessionName) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP session key is required.");
|
|
2008
2411
|
const agent = input.agent.trim();
|
|
2009
2412
|
if (!agent) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP agent id is required.");
|
|
2413
|
+
return {
|
|
2414
|
+
sessionName,
|
|
2415
|
+
agent
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
async findSession(input) {
|
|
2419
|
+
const { sessionName, agent } = this.resolveSessionIdentity(input);
|
|
2420
|
+
const record = await (await this.getManager()).findSession(sessionName);
|
|
2421
|
+
return record ? this.createSessionHandle({
|
|
2422
|
+
sessionKey: input.sessionKey,
|
|
2423
|
+
agent,
|
|
2424
|
+
mode: "persistent"
|
|
2425
|
+
}, record) : void 0;
|
|
2426
|
+
}
|
|
2427
|
+
async ensureSession(input) {
|
|
2428
|
+
const { sessionName, agent } = this.resolveSessionIdentity(input);
|
|
2010
2429
|
const record = await (await this.getManager()).ensureSession({
|
|
2011
2430
|
sessionKey: sessionName,
|
|
2012
2431
|
agent,
|
|
@@ -2015,6 +2434,12 @@ var AcpxRuntime = class {
|
|
|
2015
2434
|
resumeSessionId: input.resumeSessionId,
|
|
2016
2435
|
sessionOptions: input.sessionOptions
|
|
2017
2436
|
});
|
|
2437
|
+
return this.createSessionHandle({
|
|
2438
|
+
...input,
|
|
2439
|
+
agent
|
|
2440
|
+
}, record);
|
|
2441
|
+
}
|
|
2442
|
+
createSessionHandle(input, record) {
|
|
2018
2443
|
const handle = {
|
|
2019
2444
|
sessionKey: input.sessionKey,
|
|
2020
2445
|
backend: ACPX_BACKEND_ID,
|
|
@@ -2025,8 +2450,8 @@ var AcpxRuntime = class {
|
|
|
2025
2450
|
agentSessionId: record.agentSessionId
|
|
2026
2451
|
};
|
|
2027
2452
|
writeHandleState(handle, {
|
|
2028
|
-
name:
|
|
2029
|
-
agent,
|
|
2453
|
+
name: input.sessionKey.trim(),
|
|
2454
|
+
agent: input.agent,
|
|
2030
2455
|
cwd: record.cwd,
|
|
2031
2456
|
mode: input.mode,
|
|
2032
2457
|
acpxRecordId: record.acpxRecordId,
|
|
@@ -2046,7 +2471,8 @@ var AcpxRuntime = class {
|
|
|
2046
2471
|
requestId: input.requestId,
|
|
2047
2472
|
timeoutMs: input.timeoutMs,
|
|
2048
2473
|
signal: input.signal,
|
|
2049
|
-
onElicitation: input.onElicitation
|
|
2474
|
+
onElicitation: input.onElicitation,
|
|
2475
|
+
onPermissionRequest: input.onPermissionRequest
|
|
2050
2476
|
}));
|
|
2051
2477
|
return {
|
|
2052
2478
|
requestId: input.requestId,
|
|
@@ -2078,7 +2504,8 @@ var AcpxRuntime = class {
|
|
|
2078
2504
|
requestId: input.requestId,
|
|
2079
2505
|
timeoutMs: input.timeoutMs,
|
|
2080
2506
|
signal: input.signal,
|
|
2081
|
-
onElicitation: input.onElicitation
|
|
2507
|
+
onElicitation: input.onElicitation,
|
|
2508
|
+
onPermissionRequest: input.onPermissionRequest
|
|
2082
2509
|
});
|
|
2083
2510
|
}
|
|
2084
2511
|
async getCapabilities(input) {
|
|
@@ -2098,21 +2525,40 @@ var AcpxRuntime = class {
|
|
|
2098
2525
|
}
|
|
2099
2526
|
async setMode(input) {
|
|
2100
2527
|
const { handle, state } = this.resolveManagerHandle(input.handle);
|
|
2101
|
-
await (await this.getManager()).setMode(handle, input.mode, state.mode);
|
|
2528
|
+
await (await this.getManager()).setMode(handle, input.mode, state.mode, input);
|
|
2529
|
+
}
|
|
2530
|
+
async setModel(input) {
|
|
2531
|
+
const { handle, state } = this.resolveManagerHandle(input.handle);
|
|
2532
|
+
await (await this.getManager()).setModel(handle, input.model, state.mode, input);
|
|
2102
2533
|
}
|
|
2103
2534
|
async setConfigOption(input) {
|
|
2104
2535
|
const { handle, state } = this.resolveManagerHandle(input.handle);
|
|
2105
|
-
return await (await this.getManager()).setConfigOption(handle, input.key, input.value, state.mode);
|
|
2536
|
+
return await (await this.getManager()).setConfigOption(handle, input.key, input.value, state.mode, input);
|
|
2106
2537
|
}
|
|
2107
2538
|
async cancel(input) {
|
|
2108
2539
|
const { handle } = this.resolveManagerHandle(input.handle);
|
|
2109
2540
|
await (await this.getManager()).cancel(handle);
|
|
2110
2541
|
}
|
|
2542
|
+
async prepareFreshSession(input) {
|
|
2543
|
+
const { handle } = this.resolveManagerHandle(input.handle);
|
|
2544
|
+
await (await this.getManager()).prepareFreshSession(handle);
|
|
2545
|
+
}
|
|
2111
2546
|
async close(input) {
|
|
2112
2547
|
const { handle } = this.resolveManagerHandle(input.handle);
|
|
2113
2548
|
await (await this.getManager()).close(handle, { discardPersistentState: input.discardPersistentState });
|
|
2114
2549
|
}
|
|
2550
|
+
shutdown() {
|
|
2551
|
+
if (!this.shutdownTask) {
|
|
2552
|
+
this.healthy = false;
|
|
2553
|
+
const managerShutdown = this.managerPromise?.then((manager) => manager.shutdown());
|
|
2554
|
+
this.shutdownTask = Promise.allSettled([managerShutdown, ...this.probeTasks]).then(([manager]) => {
|
|
2555
|
+
if (manager.status === "rejected") throw manager.reason;
|
|
2556
|
+
});
|
|
2557
|
+
}
|
|
2558
|
+
return this.shutdownTask;
|
|
2559
|
+
}
|
|
2115
2560
|
async getManager() {
|
|
2561
|
+
if (this.shutdownTask) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
2116
2562
|
if (this.manager) return this.manager;
|
|
2117
2563
|
if (!this.managerPromise) this.managerPromise = Promise.resolve(this.testOptions?.managerFactory?.(this.options) ?? new AcpRuntimeManager(this.options)).then((manager) => {
|
|
2118
2564
|
this.manager = manager;
|
|
@@ -2121,7 +2567,16 @@ var AcpxRuntime = class {
|
|
|
2121
2567
|
return await this.managerPromise;
|
|
2122
2568
|
}
|
|
2123
2569
|
async runProbe() {
|
|
2124
|
-
|
|
2570
|
+
if (this.shutdownTask) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
2571
|
+
const probe = this.testOptions?.probeRunner?.(this.options) ?? this.getManager().then((manager) => manager.probe());
|
|
2572
|
+
this.probeTasks.add(probe);
|
|
2573
|
+
try {
|
|
2574
|
+
const report = await probe;
|
|
2575
|
+
if (this.shutdownTask) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
2576
|
+
return report;
|
|
2577
|
+
} finally {
|
|
2578
|
+
this.probeTasks.delete(probe);
|
|
2579
|
+
}
|
|
2125
2580
|
}
|
|
2126
2581
|
resolveManagerHandle(handle) {
|
|
2127
2582
|
const state = this.resolveHandleState(handle);
|
|
@@ -2161,6 +2616,6 @@ function createRuntimeStore(options) {
|
|
|
2161
2616
|
return createFileSessionStore(options);
|
|
2162
2617
|
}
|
|
2163
2618
|
//#endregion
|
|
2164
|
-
export { ACPX_BACKEND_ID, AcpRuntimeError, AcpxRuntime, DEFAULT_AGENT_NAME, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, REQUESTED_MODEL_UNSUPPORTED_REASONS, RequestedModelUnsupportedError, createAcpRuntime, createAgentRegistry, createFileSessionStore, createRuntimeStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, isAcpRuntimeError, isRequestedModelUnsupportedError };
|
|
2619
|
+
export { ACPX_BACKEND_ID, AcpRuntimeError, AcpxRuntime, DEFAULT_AGENT_NAME, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, REQUESTED_MODEL_UNSUPPORTED_REASONS, RequestedModelUnsupportedError, SharedAcpRuntime, createAcpRuntime, createAgentRegistry, createFileSessionStore, createRuntimeStore, createSharedAcpRuntime, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, isAcpRuntimeError, isRequestedModelUnsupportedError };
|
|
2165
2620
|
|
|
2166
2621
|
//# sourceMappingURL=runtime.js.map
|