acpx 0.15.0 → 0.16.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/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-Cen2Rb6S.js → cli-BkYIxj_N.js} +6 -14
- package/dist/cli-BkYIxj_N.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +137 -333
- package/dist/cli.js.map +1 -1
- package/dist/{client-Cvz6msGc.d.ts → client-5levcBIs.d.ts} +11 -9
- package/dist/client-5levcBIs.d.ts.map +1 -0
- package/dist/{flows-D-F3Y2o9.js → flows-Bxs4udrs.js} +205 -277
- package/dist/flows-Bxs4udrs.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-CRh8BJre.js → invocation-options-BY4lxkn3.js} +82 -58
- package/dist/invocation-options-BY4lxkn3.js.map +1 -0
- package/dist/{live-checkpoint-CYRs-D9t.js → live-checkpoint-BEfxBKCh.js} +1033 -1437
- package/dist/live-checkpoint-BEfxBKCh.js.map +1 -0
- package/dist/{output-CIVecnHU.js → output-uo5a27KB.js} +487 -490
- package/dist/output-uo5a27KB.js.map +1 -0
- package/dist/runtime.d.ts +105 -22
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +582 -366
- package/dist/runtime.js.map +1 -1
- package/dist/{session-options-DyIGRNfu.d.ts → session-options-t3d-F4dn.d.ts} +8 -6
- package/dist/session-options-t3d-F4dn.d.ts.map +1 -0
- package/package.json +27 -25
- package/skills/acpx/SKILL.md +19 -17
- package/dist/cli-Cen2Rb6S.js.map +0 -1
- package/dist/client-Cvz6msGc.d.ts.map +0 -1
- package/dist/flags-CRh8BJre.js.map +0 -1
- package/dist/flows-D-F3Y2o9.js.map +0 -1
- package/dist/live-checkpoint-CYRs-D9t.js.map +0 -1
- package/dist/output-CIVecnHU.js.map +0 -1
- package/dist/session-options-DyIGRNfu.d.ts.map +0 -1
package/dist/runtime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as isRequestedModelUnsupportedError, C as recordSessionUpdate, D as REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, Dt as withTimeout, E as AcpClient, L as textPrompt, Mt as extractAcpError, Nt as isAcpResourceNotFoundError, O as REQUESTED_MODEL_UNSUPPORTED_REASONS, S as recordPromptSubmission, T as advertisedModelState, Z as assertPersistedKeyPolicy, _ as reconcileAgentSessionId, a as applyConfigOptionSelection, c as applyModelSelection, f as persistSessionOptions, g as createInitialSessionRecord, gt as writePrivateJsonFile, h as applyLifecycleSnapshotToRecord, i as connectAndLoadSession, jt as normalizeOutputError, k as RequestedModelUnsupportedError, l as applyRequestedModelIfAdvertised, m as applyConversation, n as runPromptTurn, o as applyConfigOptionsToRecord, ot as parseSessionRecord, p as sessionOptionsFromRecord, r as withConnectedSession, s as applyInitialModelSelection, st as serializeSessionRecordForDisk, t as LiveSessionCheckpoint, u as setDesiredModeId, v as cloneSessionAcpxState, w as trimConversationForRuntime, x as recordClientOperation, y as cloneSessionConversation } from "./live-checkpoint-BEfxBKCh.js";
|
|
2
|
+
import { D as normalizeAgentCommandInput, a as createAgentRegistry, i as DEFAULT_AGENT_NAME } from "./agent-registry-DU6mhZBL.js";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import fs from "node:fs/promises";
|
|
4
5
|
import { randomUUID } from "node:crypto";
|
|
@@ -84,8 +85,7 @@ const STATUS_TEXT_RESOLVERS = {
|
|
|
84
85
|
available_commands_update: availableCommandsStatusText,
|
|
85
86
|
current_mode_update: currentModeStatusText,
|
|
86
87
|
config_option_update: configOptionStatusText,
|
|
87
|
-
session_info_update: sessionInfoStatusText
|
|
88
|
-
plan: planStatusText
|
|
88
|
+
session_info_update: sessionInfoStatusText
|
|
89
89
|
};
|
|
90
90
|
function availableCommandsStatusText(payload) {
|
|
91
91
|
const commands = Array.isArray(payload.availableCommands) ? payload.availableCommands : [];
|
|
@@ -108,6 +108,51 @@ function planStatusText(payload) {
|
|
|
108
108
|
const content = asTrimmedString((Array.isArray(payload.entries) ? payload.entries : []).find((entry) => isRecord(entry))?.content);
|
|
109
109
|
return content ? `plan: ${content}` : null;
|
|
110
110
|
}
|
|
111
|
+
function isPlanEntryStatus(value) {
|
|
112
|
+
return value === "pending" || value === "in_progress" || value === "completed";
|
|
113
|
+
}
|
|
114
|
+
function isPlanEntryPriority(value) {
|
|
115
|
+
return value === "high" || value === "medium" || value === "low";
|
|
116
|
+
}
|
|
117
|
+
function planUpdateEvent(payload) {
|
|
118
|
+
const raw = payload.entries;
|
|
119
|
+
const entries = normalizePlanEntries(raw);
|
|
120
|
+
if (Array.isArray(raw) && raw.length === 0) return {
|
|
121
|
+
type: "status",
|
|
122
|
+
text: "plan updated",
|
|
123
|
+
tag: "plan",
|
|
124
|
+
entries: []
|
|
125
|
+
};
|
|
126
|
+
const text = planStatusText(payload) ?? (entries.length > 0 ? `plan: ${entries[0]?.content}` : null);
|
|
127
|
+
if (!text) return null;
|
|
128
|
+
return {
|
|
129
|
+
type: "status",
|
|
130
|
+
text,
|
|
131
|
+
tag: "plan",
|
|
132
|
+
...entries.length > 0 ? { entries } : {}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function normalizePlanEntries(value) {
|
|
136
|
+
if (!Array.isArray(value)) return [];
|
|
137
|
+
const entries = [];
|
|
138
|
+
for (const entry of value) {
|
|
139
|
+
const normalized = normalizePlanEntry(entry);
|
|
140
|
+
if (normalized) entries.push(normalized);
|
|
141
|
+
}
|
|
142
|
+
return entries;
|
|
143
|
+
}
|
|
144
|
+
function normalizePlanEntry(value) {
|
|
145
|
+
if (!isRecord(value)) return;
|
|
146
|
+
const content = asTrimmedString(value.content);
|
|
147
|
+
const status = asTrimmedString(value.status);
|
|
148
|
+
if (!content || !isPlanEntryStatus(status)) return;
|
|
149
|
+
const priority = asTrimmedString(value.priority);
|
|
150
|
+
return {
|
|
151
|
+
content,
|
|
152
|
+
status,
|
|
153
|
+
...isPlanEntryPriority(priority) ? { priority } : {}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
111
156
|
/**
|
|
112
157
|
* Documented allowlist for text_delta.meta origin fields.
|
|
113
158
|
* Only these keys may be copied from ACP update `_meta`.
|
|
@@ -371,7 +416,7 @@ const PROMPT_EVENT_PARSERS = {
|
|
|
371
416
|
current_mode_update: (payload) => statusUpdateEvent("current_mode_update", payload),
|
|
372
417
|
config_option_update: (payload) => statusUpdateEvent("config_option_update", payload),
|
|
373
418
|
session_info_update: (payload) => statusUpdateEvent("session_info_update", payload),
|
|
374
|
-
plan:
|
|
419
|
+
plan: planUpdateEvent,
|
|
375
420
|
client_operation: clientOperationEvent,
|
|
376
421
|
update: updateStatusEvent,
|
|
377
422
|
done: () => null,
|
|
@@ -498,6 +543,88 @@ function updateStatusEvent(payload, tag) {
|
|
|
498
543
|
} : null;
|
|
499
544
|
}
|
|
500
545
|
//#endregion
|
|
546
|
+
//#region src/runtime/public/probe.ts
|
|
547
|
+
function isPrimitiveDetail(value) {
|
|
548
|
+
return value == null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol";
|
|
549
|
+
}
|
|
550
|
+
function formatFunctionDetail(value) {
|
|
551
|
+
return value.name ? `[Function ${value.name}]` : "[Function]";
|
|
552
|
+
}
|
|
553
|
+
function serializeRuntimeDetail(value) {
|
|
554
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
555
|
+
return JSON.stringify(value, (_key, nested) => {
|
|
556
|
+
if (nested instanceof Error) return nested.message || nested.name;
|
|
557
|
+
if (nested && typeof nested === "object") {
|
|
558
|
+
if (seen.has(nested)) return "[Circular]";
|
|
559
|
+
seen.add(nested);
|
|
560
|
+
}
|
|
561
|
+
return nested;
|
|
562
|
+
}) ?? "undefined";
|
|
563
|
+
}
|
|
564
|
+
function formatRuntimeDetail(value) {
|
|
565
|
+
if (value instanceof Error) return value.message || value.name;
|
|
566
|
+
if (typeof value === "string") return value;
|
|
567
|
+
if (isPrimitiveDetail(value)) return String(value);
|
|
568
|
+
if (typeof value === "function") return formatFunctionDetail(value);
|
|
569
|
+
try {
|
|
570
|
+
return serializeRuntimeDetail(value);
|
|
571
|
+
} catch {
|
|
572
|
+
return "unserializable object";
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function normalizeRuntimeDetails(details) {
|
|
576
|
+
return details?.map((detail) => formatRuntimeDetail(detail));
|
|
577
|
+
}
|
|
578
|
+
async function probeRuntime(options, deps = {}) {
|
|
579
|
+
const agentName = options.probeAgent?.trim() || "codex";
|
|
580
|
+
const agentCommand = normalizeAgentCommandInput(options.agentRegistry.resolve(agentName));
|
|
581
|
+
const client = createProbeClient(options, agentName, agentCommand, deps);
|
|
582
|
+
try {
|
|
583
|
+
await client.start();
|
|
584
|
+
return {
|
|
585
|
+
ok: true,
|
|
586
|
+
message: "embedded ACP runtime ready",
|
|
587
|
+
details: [
|
|
588
|
+
`agent=${agentName}`,
|
|
589
|
+
`command=${agentCommand.agentCommand}`,
|
|
590
|
+
`cwd=${options.cwd}`,
|
|
591
|
+
...client.initializeResult?.protocolVersion ? [`protocolVersion=${client.initializeResult.protocolVersion}`] : []
|
|
592
|
+
]
|
|
593
|
+
};
|
|
594
|
+
} catch (error) {
|
|
595
|
+
return {
|
|
596
|
+
ok: false,
|
|
597
|
+
message: "embedded ACP runtime probe failed",
|
|
598
|
+
details: [
|
|
599
|
+
`agent=${agentName}`,
|
|
600
|
+
`command=${agentCommand.agentCommand}`,
|
|
601
|
+
`cwd=${options.cwd}`,
|
|
602
|
+
formatRuntimeDetail(error)
|
|
603
|
+
]
|
|
604
|
+
};
|
|
605
|
+
} finally {
|
|
606
|
+
await client.close().catch(() => {});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
function createProbeClient(options, agentName, agentCommand, deps) {
|
|
610
|
+
const clientOptions = {
|
|
611
|
+
...agentCommand,
|
|
612
|
+
cwd: options.cwd,
|
|
613
|
+
agentProcessEnv: options.agentProcessEnv,
|
|
614
|
+
mcpServers: typeof options.mcpServers === "function" ? [] : [...options.mcpServers ?? []],
|
|
615
|
+
permissionMode: options.permissionMode,
|
|
616
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
617
|
+
permissionPolicy: options.permissionPolicy,
|
|
618
|
+
processLifecycle: options.processLifecycle,
|
|
619
|
+
processLaunchScope: {
|
|
620
|
+
kind: "runtime-probe",
|
|
621
|
+
agent: agentName
|
|
622
|
+
},
|
|
623
|
+
verbose: options.verbose
|
|
624
|
+
};
|
|
625
|
+
return deps.clientFactory?.(clientOptions) ?? new AcpClient(clientOptions);
|
|
626
|
+
}
|
|
627
|
+
//#endregion
|
|
501
628
|
//#region src/runtime/engine/reuse-policy.ts
|
|
502
629
|
function shouldReuseExistingRecord(record, params) {
|
|
503
630
|
if (record.acpx?.reset_on_next_ensure === true) return false;
|
|
@@ -513,6 +640,99 @@ function sameArgv(left, right) {
|
|
|
513
640
|
return leftValues.length === rightValues.length && leftValues.every((value, index) => value === rightValues[index]);
|
|
514
641
|
}
|
|
515
642
|
//#endregion
|
|
643
|
+
//#region src/runtime/engine/status.ts
|
|
644
|
+
function runtimeStatusFromRecord(record) {
|
|
645
|
+
return {
|
|
646
|
+
...record.lastRequestId ? { lastRequestId: record.lastRequestId } : {},
|
|
647
|
+
summary: statusSummary(record),
|
|
648
|
+
acpxRecordId: record.acpxRecordId,
|
|
649
|
+
backendSessionId: record.acpSessionId,
|
|
650
|
+
agentSessionId: record.agentSessionId,
|
|
651
|
+
...buildModelsField(record),
|
|
652
|
+
...buildUsageField(record),
|
|
653
|
+
...buildAvailableCommandsField(record),
|
|
654
|
+
details: {
|
|
655
|
+
cwd: record.cwd,
|
|
656
|
+
lastUsedAt: record.lastUsedAt,
|
|
657
|
+
closed: record.closed === true,
|
|
658
|
+
...record.acpx?.config_options !== void 0 ? { configOptions: structuredClone(record.acpx.config_options) } : {}
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function statusSummary(record) {
|
|
663
|
+
return [
|
|
664
|
+
`session=${record.acpxRecordId}`,
|
|
665
|
+
`backendSessionId=${record.acpSessionId}`,
|
|
666
|
+
record.agentSessionId ? `agentSessionId=${record.agentSessionId}` : null,
|
|
667
|
+
record.pid != null ? `pid=${record.pid}` : null,
|
|
668
|
+
record.closed ? "closed" : "open"
|
|
669
|
+
].filter(Boolean).join(" ");
|
|
670
|
+
}
|
|
671
|
+
function buildModelsField(record) {
|
|
672
|
+
const { available_models: available = [], current_model_id: currentModelId } = record.acpx ?? {};
|
|
673
|
+
if (available.length === 0 && currentModelId === void 0) return {};
|
|
674
|
+
return { models: {
|
|
675
|
+
...currentModelId !== void 0 ? { currentModelId } : {},
|
|
676
|
+
availableModelIds: [...available],
|
|
677
|
+
...buildModelNamesField(record.acpx)
|
|
678
|
+
} };
|
|
679
|
+
}
|
|
680
|
+
function buildModelNamesField(state) {
|
|
681
|
+
const models = advertisedModelState(state);
|
|
682
|
+
return models && (models.configId || state?.available_model_names !== void 0) ? { availableModels: models.availableModels } : {};
|
|
683
|
+
}
|
|
684
|
+
function tokenUsageToBreakdown(usage) {
|
|
685
|
+
if (!usage) return;
|
|
686
|
+
const breakdown = {};
|
|
687
|
+
assignUsageBreakdownField(breakdown, "inputTokens", usage.input_tokens);
|
|
688
|
+
assignUsageBreakdownField(breakdown, "outputTokens", usage.output_tokens);
|
|
689
|
+
assignUsageBreakdownField(breakdown, "cachedReadTokens", usage.cache_read_input_tokens);
|
|
690
|
+
assignUsageBreakdownField(breakdown, "cachedWriteTokens", usage.cache_creation_input_tokens);
|
|
691
|
+
assignUsageBreakdownField(breakdown, "thoughtTokens", usage.thought_tokens);
|
|
692
|
+
assignUsageBreakdownField(breakdown, "totalTokens", usage.total_tokens);
|
|
693
|
+
return Object.keys(breakdown).length > 0 ? breakdown : void 0;
|
|
694
|
+
}
|
|
695
|
+
function assignUsageBreakdownField(breakdown, key, value) {
|
|
696
|
+
if (value !== void 0) breakdown[key] = value;
|
|
697
|
+
}
|
|
698
|
+
function buildUsageField(record) {
|
|
699
|
+
const cumulative = tokenUsageToBreakdown(record.cumulative_token_usage);
|
|
700
|
+
const perRequestEntries = Object.entries(record.request_token_usage ?? {}).map(([id, value]) => [id, tokenUsageToBreakdown(value)]).filter((entry) => entry[1] !== void 0);
|
|
701
|
+
const perRequest = perRequestEntries.length > 0 ? Object.fromEntries(perRequestEntries) : void 0;
|
|
702
|
+
const cost = record.cumulative_cost;
|
|
703
|
+
const usage = {
|
|
704
|
+
...cumulative ? { cumulative } : {},
|
|
705
|
+
...cost ? { cost } : {},
|
|
706
|
+
...perRequest ? { perRequest } : {}
|
|
707
|
+
};
|
|
708
|
+
return Object.keys(usage).length > 0 ? { usage } : {};
|
|
709
|
+
}
|
|
710
|
+
function buildAvailableCommandsField(record) {
|
|
711
|
+
const commands = record.acpx?.available_commands;
|
|
712
|
+
if (!commands || commands.length === 0) return {};
|
|
713
|
+
const availableCommands = commands.map((command) => runtimeAvailableCommand(command)).filter((command) => command !== void 0);
|
|
714
|
+
return availableCommands.length > 0 ? { availableCommands } : {};
|
|
715
|
+
}
|
|
716
|
+
function runtimeAvailableCommand(command) {
|
|
717
|
+
if (typeof command === "string") {
|
|
718
|
+
const name = command.trim();
|
|
719
|
+
return name ? { name } : void 0;
|
|
720
|
+
}
|
|
721
|
+
const record = commandRecord(command);
|
|
722
|
+
if (!record) return;
|
|
723
|
+
const name = asOptionalString(record.name);
|
|
724
|
+
if (!name) return;
|
|
725
|
+
const runtimeCommand = { name };
|
|
726
|
+
const description = asOptionalString(record.description);
|
|
727
|
+
if (description) runtimeCommand.description = description;
|
|
728
|
+
if (typeof record.has_input === "boolean") runtimeCommand.hasInput = record.has_input;
|
|
729
|
+
return runtimeCommand;
|
|
730
|
+
}
|
|
731
|
+
function commandRecord(value) {
|
|
732
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
733
|
+
return value;
|
|
734
|
+
}
|
|
735
|
+
//#endregion
|
|
516
736
|
//#region src/runtime/engine/manager.ts
|
|
517
737
|
function createDeferred() {
|
|
518
738
|
let resolve;
|
|
@@ -617,27 +837,6 @@ function toPromptInput(text, attachments) {
|
|
|
617
837
|
}
|
|
618
838
|
return blocks.length > 0 ? blocks : textPrompt(text);
|
|
619
839
|
}
|
|
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
840
|
function createRecordId(sessionKey, mode) {
|
|
642
841
|
if (mode === "persistent") return sessionKey;
|
|
643
842
|
return `${sessionKey}:oneshot:${randomUUID()}`;
|
|
@@ -659,83 +858,6 @@ function legacyTerminalEventFromTurnResult(result) {
|
|
|
659
858
|
...result._meta === void 0 ? {} : { _meta: result._meta }
|
|
660
859
|
};
|
|
661
860
|
}
|
|
662
|
-
function statusSummary(record) {
|
|
663
|
-
return [
|
|
664
|
-
`session=${record.acpxRecordId}`,
|
|
665
|
-
`backendSessionId=${record.acpSessionId}`,
|
|
666
|
-
record.agentSessionId ? `agentSessionId=${record.agentSessionId}` : null,
|
|
667
|
-
record.pid != null ? `pid=${record.pid}` : null,
|
|
668
|
-
record.closed ? "closed" : "open"
|
|
669
|
-
].filter(Boolean).join(" ");
|
|
670
|
-
}
|
|
671
|
-
function buildModelsField(record) {
|
|
672
|
-
const available = record.acpx?.available_models;
|
|
673
|
-
const currentModelId = record.acpx?.current_model_id;
|
|
674
|
-
if (!available || available.length === 0) return currentModelId === void 0 ? {} : { models: {
|
|
675
|
-
currentModelId,
|
|
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;
|
|
696
|
-
}
|
|
697
|
-
function buildUsageField(record) {
|
|
698
|
-
const cumulative = tokenUsageToBreakdown(record.cumulative_token_usage);
|
|
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 } : {};
|
|
708
|
-
}
|
|
709
|
-
function buildAvailableCommandsField(record) {
|
|
710
|
-
const commands = record.acpx?.available_commands;
|
|
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 } : {};
|
|
714
|
-
}
|
|
715
|
-
function runtimeAvailableCommand(command) {
|
|
716
|
-
if (typeof command === "string") {
|
|
717
|
-
const name = command.trim();
|
|
718
|
-
return name ? { name } : void 0;
|
|
719
|
-
}
|
|
720
|
-
const record = commandRecord(command);
|
|
721
|
-
if (!record) return;
|
|
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;
|
|
729
|
-
}
|
|
730
|
-
function commandRecord(value) {
|
|
731
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
732
|
-
return value;
|
|
733
|
-
}
|
|
734
|
-
function trimmedField(value) {
|
|
735
|
-
if (typeof value !== "string") return;
|
|
736
|
-
const trimmed = value.trim();
|
|
737
|
-
return trimmed ? trimmed : void 0;
|
|
738
|
-
}
|
|
739
861
|
function advertisedConfigOptionIds(record) {
|
|
740
862
|
const configOptions = record.acpx?.config_options;
|
|
741
863
|
if (!configOptions) return;
|
|
@@ -780,20 +902,117 @@ var AcpRuntimeManager = class {
|
|
|
780
902
|
deps;
|
|
781
903
|
activeControllers = /* @__PURE__ */ new Map();
|
|
782
904
|
retainedSessionOwners = /* @__PURE__ */ new Map();
|
|
905
|
+
retiringSessionOwners = /* @__PURE__ */ new Set();
|
|
783
906
|
pendingOneShotRecordIds = /* @__PURE__ */ new Map();
|
|
784
907
|
ensureSessionLocks = /* @__PURE__ */ new Map();
|
|
785
908
|
runtimeOperationLocks = /* @__PURE__ */ new Map();
|
|
909
|
+
sessionTasks = /* @__PURE__ */ new Map();
|
|
786
910
|
closingActiveRecords = /* @__PURE__ */ new Set();
|
|
911
|
+
liveClients = /* @__PURE__ */ new Set();
|
|
912
|
+
pendingTasks = /* @__PURE__ */ new Set();
|
|
913
|
+
shuttingDown = false;
|
|
914
|
+
shutdownTask;
|
|
787
915
|
constructor(options, deps = {}) {
|
|
788
916
|
this.options = options;
|
|
789
917
|
this.deps = deps;
|
|
790
918
|
}
|
|
791
919
|
createClient(options) {
|
|
920
|
+
this.assertOpen();
|
|
792
921
|
const clientOptions = {
|
|
793
922
|
...options,
|
|
794
|
-
agentProcessEnv: this.options.agentProcessEnv
|
|
923
|
+
agentProcessEnv: this.options.agentProcessEnv,
|
|
924
|
+
processLifecycle: {
|
|
925
|
+
...options.processLifecycle,
|
|
926
|
+
onBeforeSpawn: async (launch) => {
|
|
927
|
+
this.assertOpen();
|
|
928
|
+
await options.processLifecycle?.onBeforeSpawn?.(launch);
|
|
929
|
+
this.assertOpen();
|
|
930
|
+
},
|
|
931
|
+
onSpawned: async (process) => {
|
|
932
|
+
this.assertOpen();
|
|
933
|
+
await options.processLifecycle?.onSpawned?.(process);
|
|
934
|
+
this.assertOpen();
|
|
935
|
+
}
|
|
936
|
+
}
|
|
795
937
|
};
|
|
796
|
-
|
|
938
|
+
const client = this.deps.clientFactory?.(clientOptions) ?? new AcpClient(clientOptions);
|
|
939
|
+
this.liveClients.add(client);
|
|
940
|
+
return client;
|
|
941
|
+
}
|
|
942
|
+
async probe() {
|
|
943
|
+
let client;
|
|
944
|
+
try {
|
|
945
|
+
return await probeRuntime(this.options, { clientFactory: (options) => client = this.createClient(options) });
|
|
946
|
+
} finally {
|
|
947
|
+
if (client) this.liveClients.delete(client);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
assertOpen() {
|
|
951
|
+
if (this.shuttingDown) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
952
|
+
}
|
|
953
|
+
shutdown() {
|
|
954
|
+
if (!this.shutdownTask) {
|
|
955
|
+
this.shuttingDown = true;
|
|
956
|
+
this.shutdownTask = this.shutdownOwnedClients();
|
|
957
|
+
}
|
|
958
|
+
return this.shutdownTask;
|
|
959
|
+
}
|
|
960
|
+
async shutdownOwnedClients() {
|
|
961
|
+
const closed = await Promise.allSettled([...this.liveClients].map(async (client) => {
|
|
962
|
+
client.requestCancelActivePrompt().catch(() => {});
|
|
963
|
+
await this.closeClient(client);
|
|
964
|
+
}));
|
|
965
|
+
await Promise.allSettled([
|
|
966
|
+
...this.ensureSessionLocks.values(),
|
|
967
|
+
...this.runtimeOperationLocks.values(),
|
|
968
|
+
...this.pendingTasks
|
|
969
|
+
]);
|
|
970
|
+
await Promise.all([...this.retainedSessionOwners.values()].map(async (owner) => {
|
|
971
|
+
this.removeRetainedSessionOwner(owner);
|
|
972
|
+
await this.stopSessionOwner(owner);
|
|
973
|
+
}));
|
|
974
|
+
const errors = [];
|
|
975
|
+
for (const result of closed) if (result.status === "rejected") errors.push(result.reason);
|
|
976
|
+
if (errors.length) throw new AggregateError(errors, "ACP runtime shutdown failed.");
|
|
977
|
+
this.retiringSessionOwners.clear();
|
|
978
|
+
}
|
|
979
|
+
trackTask(task) {
|
|
980
|
+
this.pendingTasks.add(task);
|
|
981
|
+
const remove = () => {
|
|
982
|
+
this.pendingTasks.delete(task);
|
|
983
|
+
};
|
|
984
|
+
task.then(remove, remove);
|
|
985
|
+
return task;
|
|
986
|
+
}
|
|
987
|
+
queueSessionTask(recordId, run, cancel) {
|
|
988
|
+
const tasks = this.sessionTasks.get(recordId) ?? /* @__PURE__ */ new Set();
|
|
989
|
+
this.sessionTasks.set(recordId, tasks);
|
|
990
|
+
const previous = [...tasks].at(-1)?.completion;
|
|
991
|
+
const result = (async () => {
|
|
992
|
+
await previous?.catch(() => {});
|
|
993
|
+
return await run();
|
|
994
|
+
})();
|
|
995
|
+
const task = {
|
|
996
|
+
completion: result.then(() => {}),
|
|
997
|
+
cancel
|
|
998
|
+
};
|
|
999
|
+
task.completion.catch(() => {});
|
|
1000
|
+
tasks.add(task);
|
|
1001
|
+
const remove = () => {
|
|
1002
|
+
tasks.delete(task);
|
|
1003
|
+
if (tasks.size === 0) this.sessionTasks.delete(recordId);
|
|
1004
|
+
};
|
|
1005
|
+
result.then(remove, remove);
|
|
1006
|
+
return this.trackTask(result);
|
|
1007
|
+
}
|
|
1008
|
+
async cancelSessionTasks(recordId) {
|
|
1009
|
+
const tasks = [...this.sessionTasks.get(recordId) ?? []];
|
|
1010
|
+
await Promise.all(tasks.flatMap((task) => task.cancel ? [task.cancel()] : []));
|
|
1011
|
+
return tasks;
|
|
1012
|
+
}
|
|
1013
|
+
async closeClient(client) {
|
|
1014
|
+
await client.close();
|
|
1015
|
+
this.liveClients.delete(client);
|
|
797
1016
|
}
|
|
798
1017
|
createSessionOwner(input) {
|
|
799
1018
|
const owner = {
|
|
@@ -900,12 +1119,27 @@ var AcpRuntimeManager = class {
|
|
|
900
1119
|
}
|
|
901
1120
|
async stopSessionOwner(owner) {
|
|
902
1121
|
await this.flushSessionOwner(owner).catch(() => {});
|
|
903
|
-
await owner.client
|
|
1122
|
+
await this.closeClient(owner.client).catch(() => {
|
|
1123
|
+
this.retiringSessionOwners.add(owner);
|
|
1124
|
+
});
|
|
904
1125
|
await owner.projection?.checkpoint.flush().catch(() => {});
|
|
905
1126
|
try {
|
|
906
1127
|
owner.client.clearEventHandlers();
|
|
907
1128
|
} catch {}
|
|
908
1129
|
}
|
|
1130
|
+
async retrySessionCleanup(recordId) {
|
|
1131
|
+
for (const owner of this.retiringSessionOwners) if (owner.recordId === recordId) {
|
|
1132
|
+
owner.retirement ??= this.finalizeSessionConnection({
|
|
1133
|
+
client: owner.client,
|
|
1134
|
+
owner
|
|
1135
|
+
}).then(() => {
|
|
1136
|
+
this.retiringSessionOwners.delete(owner);
|
|
1137
|
+
}).finally(() => {
|
|
1138
|
+
owner.retirement = void 0;
|
|
1139
|
+
});
|
|
1140
|
+
await owner.retirement;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
909
1143
|
async refreshClosedState(record) {
|
|
910
1144
|
if (!this.closingActiveRecords.has(record.acpxRecordId)) return record.closed === true;
|
|
911
1145
|
const latest = await this.options.sessionStore.load(record.acpxRecordId).catch(() => void 0);
|
|
@@ -933,9 +1167,20 @@ var AcpRuntimeManager = class {
|
|
|
933
1167
|
return true;
|
|
934
1168
|
}
|
|
935
1169
|
canRetainPersistentSessionOwner(owner, record) {
|
|
936
|
-
return owner.mode === "persistent" && !record.closed && !(owner.client.hasUnresolvedPrompt?.() ?? false) && owner.client.hasReusableSession(record.acpSessionId);
|
|
1170
|
+
return !this.shuttingDown && owner.mode === "persistent" && !record.closed && !(owner.client.hasUnresolvedPrompt?.() ?? false) && owner.client.hasReusableSession(record.acpSessionId);
|
|
1171
|
+
}
|
|
1172
|
+
resolveMcpServers(session) {
|
|
1173
|
+
const servers = this.options.mcpServers;
|
|
1174
|
+
const { sessionKey, cwd, agentCommand, agentArgv } = session;
|
|
1175
|
+
return [...typeof servers === "function" ? servers({
|
|
1176
|
+
sessionKey,
|
|
1177
|
+
cwd,
|
|
1178
|
+
agentCommand,
|
|
1179
|
+
agentArgv: agentArgv ? [...agentArgv] : void 0
|
|
1180
|
+
}) : servers ?? []];
|
|
937
1181
|
}
|
|
938
1182
|
async withRuntimeControlSession(record, sessionMode, run, replacingConfigOption) {
|
|
1183
|
+
await this.retrySessionCleanup(record.acpxRecordId);
|
|
939
1184
|
const owner = await this.readRetainedSessionOwner(record, { consume: false });
|
|
940
1185
|
if (owner) {
|
|
941
1186
|
const ownedRecord = owner.projection?.record ?? record;
|
|
@@ -954,34 +1199,42 @@ var AcpRuntimeManager = class {
|
|
|
954
1199
|
await this.flushSessionOwner(owner);
|
|
955
1200
|
}
|
|
956
1201
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1202
|
+
let controlClient;
|
|
1203
|
+
try {
|
|
1204
|
+
const result = await withConnectedSession({
|
|
1205
|
+
sessionRecordId: record.acpxRecordId,
|
|
1206
|
+
loadRecord: async (sessionRecordId) => await this.requireRecord(sessionRecordId),
|
|
1207
|
+
saveRecord: async (connectedRecord) => await this.options.sessionStore.save(connectedRecord),
|
|
1208
|
+
createClient: (options) => controlClient = this.createClient({
|
|
1209
|
+
...options,
|
|
1210
|
+
processLifecycle: this.options.processLifecycle,
|
|
1211
|
+
processLaunchScope: {
|
|
1212
|
+
kind: "runtime-session",
|
|
1213
|
+
sessionKey: record.name ?? record.acpxRecordId
|
|
1214
|
+
}
|
|
1215
|
+
}),
|
|
1216
|
+
mcpServers: this.resolveMcpServers({
|
|
1217
|
+
...record,
|
|
966
1218
|
sessionKey: record.name ?? record.acpxRecordId
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1219
|
+
}),
|
|
1220
|
+
permissionMode: this.options.permissionMode,
|
|
1221
|
+
nonInteractivePermissions: this.options.nonInteractivePermissions,
|
|
1222
|
+
permissionPolicy: this.options.permissionPolicy,
|
|
1223
|
+
onPermissionRequest: this.options.onPermissionRequest,
|
|
1224
|
+
elicitationModes: this.options.elicitationModes,
|
|
1225
|
+
verbose: this.options.verbose,
|
|
1226
|
+
timeoutMs: this.options.timeoutMs,
|
|
1227
|
+
resumePolicy: resumePolicyForSessionMode(sessionMode),
|
|
1228
|
+
replacingConfigOption,
|
|
1229
|
+
run
|
|
1230
|
+
});
|
|
1231
|
+
return {
|
|
1232
|
+
value: result.value,
|
|
1233
|
+
record: result.record
|
|
1234
|
+
};
|
|
1235
|
+
} finally {
|
|
1236
|
+
if (controlClient) this.liveClients.delete(controlClient);
|
|
1237
|
+
}
|
|
985
1238
|
}
|
|
986
1239
|
refreshOwnedRecordLifecycle(owner, record) {
|
|
987
1240
|
record.lastUsedAt = isoNow();
|
|
@@ -999,6 +1252,7 @@ var AcpRuntimeManager = class {
|
|
|
999
1252
|
return await this.withManagerLock(this.ensureSessionLocks, key, run);
|
|
1000
1253
|
}
|
|
1001
1254
|
async withManagerLock(locks, key, run) {
|
|
1255
|
+
this.assertOpen();
|
|
1002
1256
|
const previous = locks.get(key) ?? Promise.resolve();
|
|
1003
1257
|
let release;
|
|
1004
1258
|
const gate = new Promise((resolve) => {
|
|
@@ -1008,6 +1262,7 @@ var AcpRuntimeManager = class {
|
|
|
1008
1262
|
locks.set(key, tail);
|
|
1009
1263
|
await previous;
|
|
1010
1264
|
try {
|
|
1265
|
+
this.assertOpen();
|
|
1011
1266
|
return await run();
|
|
1012
1267
|
} finally {
|
|
1013
1268
|
release();
|
|
@@ -1030,7 +1285,8 @@ var AcpRuntimeManager = class {
|
|
|
1030
1285
|
async loadExistingRuntimeSession(input) {
|
|
1031
1286
|
const existingRecordId = input.mode === "persistent" ? input.sessionKey : this.pendingOneShotRecordIds.get(input.sessionKey);
|
|
1032
1287
|
if (!existingRecordId) return;
|
|
1033
|
-
|
|
1288
|
+
await this.retrySessionCleanup(existingRecordId);
|
|
1289
|
+
let record = await this.findSession(existingRecordId);
|
|
1034
1290
|
if (!record) return;
|
|
1035
1291
|
const owner = this.retainedSessionOwners.get(record.acpxRecordId);
|
|
1036
1292
|
if (owner) {
|
|
@@ -1042,6 +1298,9 @@ var AcpRuntimeManager = class {
|
|
|
1042
1298
|
owner
|
|
1043
1299
|
};
|
|
1044
1300
|
}
|
|
1301
|
+
async findSession(sessionKey) {
|
|
1302
|
+
return await this.options.sessionStore.load(sessionKey);
|
|
1303
|
+
}
|
|
1045
1304
|
canReuseRuntimeSession(input, agent, existing) {
|
|
1046
1305
|
if (!shouldReuseExistingRecord(existing.record, {
|
|
1047
1306
|
cwd: agent.cwd,
|
|
@@ -1068,7 +1327,10 @@ var AcpRuntimeManager = class {
|
|
|
1068
1327
|
agentCommand,
|
|
1069
1328
|
agentArgv,
|
|
1070
1329
|
cwd,
|
|
1071
|
-
mcpServers:
|
|
1330
|
+
mcpServers: this.resolveMcpServers({
|
|
1331
|
+
...agent,
|
|
1332
|
+
sessionKey: input.sessionKey
|
|
1333
|
+
}),
|
|
1072
1334
|
permissionMode: this.options.permissionMode,
|
|
1073
1335
|
nonInteractivePermissions: this.options.nonInteractivePermissions,
|
|
1074
1336
|
permissionPolicy: this.options.permissionPolicy,
|
|
@@ -1090,6 +1352,7 @@ var AcpRuntimeManager = class {
|
|
|
1090
1352
|
let retained = false;
|
|
1091
1353
|
try {
|
|
1092
1354
|
await client.start();
|
|
1355
|
+
this.assertOpen();
|
|
1093
1356
|
const session = await createOrLoadRuntimeSession(client, input.resumeSessionId, cwd);
|
|
1094
1357
|
const record = await this.prepareInitialRuntimeRecord({
|
|
1095
1358
|
input,
|
|
@@ -1107,15 +1370,15 @@ var AcpRuntimeManager = class {
|
|
|
1107
1370
|
if (!retained) {
|
|
1108
1371
|
owner.recordId = void 0;
|
|
1109
1372
|
client.clearEventHandlers();
|
|
1110
|
-
await
|
|
1373
|
+
await this.closeClient(client);
|
|
1111
1374
|
}
|
|
1112
1375
|
}
|
|
1113
1376
|
}
|
|
1114
1377
|
async prepareInitialRuntimeRecord(params) {
|
|
1115
1378
|
const { input, client, owner, agentCommand, agentArgv, cwd, session } = params;
|
|
1116
|
-
const record =
|
|
1379
|
+
const record = createInitialSessionRecord({
|
|
1117
1380
|
recordId: createRecordId(input.sessionKey, input.mode),
|
|
1118
|
-
|
|
1381
|
+
name: input.sessionKey,
|
|
1119
1382
|
sessionId: session.sessionId,
|
|
1120
1383
|
agentCommand,
|
|
1121
1384
|
agentArgv,
|
|
@@ -1135,9 +1398,7 @@ var AcpRuntimeManager = class {
|
|
|
1135
1398
|
agentCommand,
|
|
1136
1399
|
timeoutMs: this.options.timeoutMs
|
|
1137
1400
|
});
|
|
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));
|
|
1401
|
+
applyInitialModelSelection(record, session.sessionResult.models, input.sessionOptions?.model, modelApplication);
|
|
1141
1402
|
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
1142
1403
|
persistSessionOptions(record, input.sessionOptions);
|
|
1143
1404
|
return record;
|
|
@@ -1145,6 +1406,7 @@ var AcpRuntimeManager = class {
|
|
|
1145
1406
|
async retainInitializedSessionOwner(owner, record) {
|
|
1146
1407
|
owner.recordId = record.acpxRecordId;
|
|
1147
1408
|
await owner.projection?.checkpoint.checkpoint();
|
|
1409
|
+
this.assertOpen();
|
|
1148
1410
|
const previousOwner = this.retainedSessionOwners.get(record.acpxRecordId);
|
|
1149
1411
|
this.retainedSessionOwners.set(record.acpxRecordId, owner);
|
|
1150
1412
|
if (owner.mode === "oneshot") this.pendingOneShotRecordIds.set(owner.sessionKey, record.acpxRecordId);
|
|
@@ -1154,11 +1416,12 @@ var AcpRuntimeManager = class {
|
|
|
1154
1416
|
}
|
|
1155
1417
|
}
|
|
1156
1418
|
startTurn(input) {
|
|
1419
|
+
this.assertOpen();
|
|
1157
1420
|
let promptInput;
|
|
1158
1421
|
try {
|
|
1159
1422
|
promptInput = toPromptInput(input.text, input.attachments);
|
|
1160
1423
|
} catch (error) {
|
|
1161
|
-
this.closeRetainedOneShotHandle(input.handle).catch(() => {});
|
|
1424
|
+
this.trackTask(this.closeRetainedOneShotHandle(input.handle).catch(() => {}));
|
|
1162
1425
|
throw error;
|
|
1163
1426
|
}
|
|
1164
1427
|
const queue = new AsyncEventQueue();
|
|
@@ -1194,28 +1457,8 @@ var AcpRuntimeManager = class {
|
|
|
1194
1457
|
const abortHandler = () => {
|
|
1195
1458
|
requestCancel();
|
|
1196
1459
|
};
|
|
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({
|
|
1460
|
+
if (input.signal && !input.signal.aborted) input.signal.addEventListener("abort", abortHandler, { once: true });
|
|
1461
|
+
this.queueSessionTask(input.handle.acpxRecordId ?? input.handle.sessionKey, async () => this.runRuntimeTurnTask({
|
|
1219
1462
|
input,
|
|
1220
1463
|
promptInput,
|
|
1221
1464
|
queue,
|
|
@@ -1224,7 +1467,7 @@ var AcpRuntimeManager = class {
|
|
|
1224
1467
|
state,
|
|
1225
1468
|
settleResult,
|
|
1226
1469
|
abortHandler
|
|
1227
|
-
});
|
|
1470
|
+
}), requestCancel);
|
|
1228
1471
|
return {
|
|
1229
1472
|
requestId: input.requestId,
|
|
1230
1473
|
promptStarted: promptStarted.promise,
|
|
@@ -1246,30 +1489,39 @@ var AcpRuntimeManager = class {
|
|
|
1246
1489
|
let turn;
|
|
1247
1490
|
let terminalResult;
|
|
1248
1491
|
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);
|
|
1492
|
+
if (this.cancelRuntimeTurnBeforePrompt(task)) {
|
|
1493
|
+
await this.closeRetainedOneShotHandle(task.input.handle);
|
|
1260
1494
|
terminalResult = {
|
|
1261
|
-
status:
|
|
1262
|
-
|
|
1263
|
-
|
|
1495
|
+
status: "cancelled",
|
|
1496
|
+
stopReason: "cancelled"
|
|
1497
|
+
};
|
|
1498
|
+
} else {
|
|
1499
|
+
turn = await this.prepareRuntimeTurn(task);
|
|
1500
|
+
const { sessionId } = await this.connectRuntimeTurn(task, turn);
|
|
1501
|
+
this.assertOpen();
|
|
1502
|
+
await this.resolveRuntimeTurnReady(task, turn);
|
|
1503
|
+
if (this.cancelRuntimeTurnBeforePrompt(task)) terminalResult = {
|
|
1504
|
+
status: "cancelled",
|
|
1505
|
+
stopReason: "cancelled"
|
|
1264
1506
|
};
|
|
1507
|
+
else {
|
|
1508
|
+
await this.applyPendingRuntimeTurnCancel(task, turn);
|
|
1509
|
+
const response = await this.runRuntimePrompt(task, turn, sessionId);
|
|
1510
|
+
await this.saveCompletedRuntimeTurn(turn);
|
|
1511
|
+
terminalResult = {
|
|
1512
|
+
status: response.stopReason === "cancelled" ? "cancelled" : "completed",
|
|
1513
|
+
...response.stopReason ? { stopReason: response.stopReason } : {},
|
|
1514
|
+
...response._meta === void 0 ? {} : { _meta: response._meta }
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1265
1517
|
}
|
|
1266
1518
|
} catch (error) {
|
|
1267
1519
|
terminalResult = this.failRuntimeTurn(task, error);
|
|
1268
1520
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1521
|
+
const finalization = await settleAttempt(async () => this.finalizeRuntimeTurn(task, turn));
|
|
1522
|
+
if (!finalization.ok) {
|
|
1523
|
+
task.settleResult(this.failRuntimeTurn(task, finalization.error));
|
|
1524
|
+
throw finalization.error;
|
|
1273
1525
|
}
|
|
1274
1526
|
task.settleResult(terminalResult);
|
|
1275
1527
|
}
|
|
@@ -1283,7 +1535,8 @@ var AcpRuntimeManager = class {
|
|
|
1283
1535
|
conversation: turn.conversation,
|
|
1284
1536
|
promptMessageId: turn.promptMessageId,
|
|
1285
1537
|
onPromptRequestWritten: () => task.promptStarted.resolve(),
|
|
1286
|
-
onElicitation: task.input.onElicitation
|
|
1538
|
+
onElicitation: task.input.onElicitation,
|
|
1539
|
+
onPermissionRequest: task.input.onPermissionRequest
|
|
1287
1540
|
});
|
|
1288
1541
|
} finally {
|
|
1289
1542
|
turn.client.endPromptElicitation?.(sessionId);
|
|
@@ -1291,7 +1544,10 @@ var AcpRuntimeManager = class {
|
|
|
1291
1544
|
}
|
|
1292
1545
|
async prepareRuntimeTurn(task) {
|
|
1293
1546
|
const recordId = task.input.handle.acpxRecordId ?? task.input.handle.sessionKey;
|
|
1294
|
-
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () =>
|
|
1547
|
+
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => {
|
|
1548
|
+
await this.retrySessionCleanup(recordId);
|
|
1549
|
+
return await this.prepareRuntimeTurnWithOwnership(task);
|
|
1550
|
+
});
|
|
1295
1551
|
}
|
|
1296
1552
|
async prepareRuntimeTurnWithOwnership(task) {
|
|
1297
1553
|
const { record, retainedOwner, conversation, acpxState, promptMessageId } = await this.prepareRuntimeTurnState(task);
|
|
@@ -1398,7 +1654,10 @@ var AcpRuntimeManager = class {
|
|
|
1398
1654
|
agentCommand: record.agentCommand,
|
|
1399
1655
|
agentArgv: record.agentArgv,
|
|
1400
1656
|
cwd: record.cwd,
|
|
1401
|
-
mcpServers:
|
|
1657
|
+
mcpServers: this.resolveMcpServers({
|
|
1658
|
+
...record,
|
|
1659
|
+
sessionKey: record.name ?? record.acpxRecordId
|
|
1660
|
+
}),
|
|
1402
1661
|
permissionMode: this.options.permissionMode,
|
|
1403
1662
|
nonInteractivePermissions: this.options.nonInteractivePermissions,
|
|
1404
1663
|
permissionPolicy: this.options.permissionPolicy,
|
|
@@ -1506,7 +1765,7 @@ var AcpRuntimeManager = class {
|
|
|
1506
1765
|
const controller = task.state.activeController;
|
|
1507
1766
|
if (controller) this.activeControllers.set(turn.record.acpxRecordId, controller);
|
|
1508
1767
|
}
|
|
1509
|
-
async resolveRuntimeTurnReady(task, turn
|
|
1768
|
+
async resolveRuntimeTurnReady(task, turn) {
|
|
1510
1769
|
task.sessionReady.resolve();
|
|
1511
1770
|
turn.record.lastRequestId = task.input.requestId;
|
|
1512
1771
|
turn.record.lastPromptAt = isoNow();
|
|
@@ -1514,14 +1773,6 @@ var AcpRuntimeManager = class {
|
|
|
1514
1773
|
turn.record.closedAt = void 0;
|
|
1515
1774
|
turn.record.lastUsedAt = isoNow();
|
|
1516
1775
|
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
1776
|
}
|
|
1526
1777
|
cancelRuntimeTurnBeforePrompt(task) {
|
|
1527
1778
|
if (!task.state.pendingCancel && !task.input.signal?.aborted) return false;
|
|
@@ -1535,7 +1786,7 @@ var AcpRuntimeManager = class {
|
|
|
1535
1786
|
if (cancelled) task.state.pendingCancel = false;
|
|
1536
1787
|
return cancelled;
|
|
1537
1788
|
}
|
|
1538
|
-
async saveCompletedRuntimeTurn(turn
|
|
1789
|
+
async saveCompletedRuntimeTurn(turn) {
|
|
1539
1790
|
turn.record.acpSessionId = turn.activeSessionId;
|
|
1540
1791
|
reconcileAgentSessionId(turn.record, turn.record.agentSessionId);
|
|
1541
1792
|
turn.record.protocolVersion = turn.client.initializeResult?.protocolVersion;
|
|
@@ -1583,8 +1834,11 @@ var AcpRuntimeManager = class {
|
|
|
1583
1834
|
async closeRuntimeTurnClient(turn, pooled) {
|
|
1584
1835
|
if (!turn || pooled) return;
|
|
1585
1836
|
turn.owner.activeTurn = void 0;
|
|
1586
|
-
const failure = firstFailedAttempt([await settleAttempt(() => turn.client.clearEventHandlers()), await settleAttempt(async () => turn.client
|
|
1587
|
-
if (failure)
|
|
1837
|
+
const failure = firstFailedAttempt([await settleAttempt(() => turn.client.clearEventHandlers()), await settleAttempt(async () => this.closeClient(turn.client))]);
|
|
1838
|
+
if (failure) {
|
|
1839
|
+
this.retiringSessionOwners.add(turn.owner);
|
|
1840
|
+
throw failure.error;
|
|
1841
|
+
}
|
|
1588
1842
|
}
|
|
1589
1843
|
cleanupRuntimeTurn(task, turn) {
|
|
1590
1844
|
if (turn) {
|
|
@@ -1615,26 +1869,15 @@ var AcpRuntimeManager = class {
|
|
|
1615
1869
|
yield* turn.events;
|
|
1616
1870
|
yield legacyTerminalEventFromTurnResult(await turn.result);
|
|
1617
1871
|
}
|
|
1618
|
-
|
|
1872
|
+
getStatus(handle) {
|
|
1873
|
+
this.assertOpen();
|
|
1874
|
+
return this.trackTask(this.readStatus(handle));
|
|
1875
|
+
}
|
|
1876
|
+
async readStatus(handle) {
|
|
1619
1877
|
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1620
1878
|
const owner = this.retainedSessionOwners.get(recordId);
|
|
1621
1879
|
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
|
-
};
|
|
1880
|
+
return runtimeStatusFromRecord(await this.requireRecord(recordId));
|
|
1638
1881
|
}
|
|
1639
1882
|
async setMode(handle, mode, sessionMode = "persistent") {
|
|
1640
1883
|
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
@@ -1651,6 +1894,25 @@ var AcpRuntimeManager = class {
|
|
|
1651
1894
|
setDesiredModeId(targetRecord, mode);
|
|
1652
1895
|
await this.options.sessionStore.save(targetRecord);
|
|
1653
1896
|
}
|
|
1897
|
+
async setModel(handle, model, sessionMode = "persistent") {
|
|
1898
|
+
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1899
|
+
await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setModelWithOwnership(handle, model, sessionMode));
|
|
1900
|
+
}
|
|
1901
|
+
async setModelWithOwnership(handle, model, sessionMode) {
|
|
1902
|
+
const record = await this.requireRecord(handle.acpxRecordId ?? handle.sessionKey);
|
|
1903
|
+
const controller = this.activeControllers.get(record.acpxRecordId);
|
|
1904
|
+
if (controller) {
|
|
1905
|
+
const response = await controller.setSessionModel(model);
|
|
1906
|
+
record.acpx = applyModelSelection(record.acpx, model, response);
|
|
1907
|
+
await this.options.sessionStore.save(record);
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
const result = await this.withRuntimeControlSession(record, sessionMode, async ({ client, sessionId, record: connectedRecord }) => {
|
|
1911
|
+
const response = await client.setSessionModel(sessionId, model, advertisedModelState(connectedRecord.acpx));
|
|
1912
|
+
connectedRecord.acpx = applyModelSelection(connectedRecord.acpx, model, response);
|
|
1913
|
+
}, { key: "model" });
|
|
1914
|
+
await this.options.sessionStore.save(result.record);
|
|
1915
|
+
}
|
|
1654
1916
|
async setConfigOption(handle, key, value, sessionMode = "persistent") {
|
|
1655
1917
|
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1656
1918
|
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setConfigOptionWithOwnership(handle, key, value, sessionMode));
|
|
@@ -1680,12 +1942,31 @@ var AcpRuntimeManager = class {
|
|
|
1680
1942
|
async cancel(handle) {
|
|
1681
1943
|
await this.activeControllers.get(handle.acpxRecordId ?? handle.sessionKey)?.requestCancelActivePrompt();
|
|
1682
1944
|
}
|
|
1683
|
-
|
|
1945
|
+
prepareFreshSession(handle) {
|
|
1946
|
+
this.assertOpen();
|
|
1684
1947
|
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1948
|
+
const cancelled = this.cancelSessionTasks(recordId);
|
|
1949
|
+
cancelled.catch(() => {});
|
|
1950
|
+
return this.queueSessionTask(recordId, async () => {
|
|
1951
|
+
const tasks = await cancelled;
|
|
1952
|
+
await Promise.all(tasks.map((task) => task.completion));
|
|
1953
|
+
return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.closeRuntimeSession(handle, "prepare-fresh"));
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
close(handle, options = {}) {
|
|
1957
|
+
this.assertOpen();
|
|
1958
|
+
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1959
|
+
return this.trackTask(this.withManagerLock(this.runtimeOperationLocks, recordId, async () => {
|
|
1960
|
+
return await this.closeRuntimeSession(handle, options.discardPersistentState === true ? "discard" : "release");
|
|
1961
|
+
}));
|
|
1962
|
+
}
|
|
1963
|
+
async closeRuntimeSession(handle, intent) {
|
|
1964
|
+
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
|
1965
|
+
await this.retrySessionCleanup(recordId);
|
|
1685
1966
|
const record = await this.resolveRuntimeRecordForClose(recordId);
|
|
1686
1967
|
this.markActiveRuntimeRecordClosing(record);
|
|
1687
|
-
await this.
|
|
1688
|
-
await this.closeRuntimeRecordOwnership(record,
|
|
1968
|
+
if (intent !== "prepare-fresh") await this.cancelSessionTasks(recordId);
|
|
1969
|
+
await this.closeRuntimeRecordOwnership(record, intent);
|
|
1689
1970
|
record.closed = true;
|
|
1690
1971
|
record.closedAt = isoNow();
|
|
1691
1972
|
await this.options.sessionStore.save(record);
|
|
@@ -1698,14 +1979,20 @@ var AcpRuntimeManager = class {
|
|
|
1698
1979
|
markActiveRuntimeRecordClosing(record) {
|
|
1699
1980
|
if (this.activeControllers.has(record.acpxRecordId)) this.closingActiveRecords.add(record.acpxRecordId);
|
|
1700
1981
|
}
|
|
1701
|
-
async closeRuntimeRecordOwnership(record,
|
|
1702
|
-
if (
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1982
|
+
async closeRuntimeRecordOwnership(record, intent) {
|
|
1983
|
+
if (intent === "discard") await this.closeBackendSession(record);
|
|
1984
|
+
else if (intent === "prepare-fresh") {
|
|
1985
|
+
const owner = this.retainedSessionOwners.get(record.acpxRecordId);
|
|
1986
|
+
if (owner) {
|
|
1987
|
+
this.removeRetainedSessionOwner(owner);
|
|
1988
|
+
this.retiringSessionOwners.add(owner);
|
|
1989
|
+
}
|
|
1990
|
+
await this.retrySessionCleanup(record.acpxRecordId);
|
|
1708
1991
|
} else await this.closeRetainedSessionOwner(record.acpxRecordId);
|
|
1992
|
+
if (intent !== "release") record.acpx = {
|
|
1993
|
+
...record.acpx,
|
|
1994
|
+
reset_on_next_ensure: true
|
|
1995
|
+
};
|
|
1709
1996
|
}
|
|
1710
1997
|
async closeBackendSession(record) {
|
|
1711
1998
|
const connection = await this.acquireBackendCloseConnection(record);
|
|
@@ -1714,16 +2001,16 @@ var AcpRuntimeManager = class {
|
|
|
1714
2001
|
} catch (error) {
|
|
1715
2002
|
this.handleBackendSessionCloseError(record, error);
|
|
1716
2003
|
} finally {
|
|
1717
|
-
await this.
|
|
2004
|
+
await this.finalizeSessionConnection(connection);
|
|
1718
2005
|
}
|
|
1719
2006
|
}
|
|
1720
|
-
async
|
|
2007
|
+
async finalizeSessionConnection(connection) {
|
|
1721
2008
|
const failure = firstFailedAttempt([
|
|
1722
2009
|
await settleAttempt(async () => {
|
|
1723
2010
|
if (connection.owner) await this.flushSessionOwner(connection.owner);
|
|
1724
2011
|
}),
|
|
1725
2012
|
await settleAttempt(() => connection.owner?.client.clearEventHandlers()),
|
|
1726
|
-
await settleAttempt(async () => connection.client
|
|
2013
|
+
await settleAttempt(async () => this.closeClient(connection.client))
|
|
1727
2014
|
]);
|
|
1728
2015
|
if (failure) throw failure.error;
|
|
1729
2016
|
}
|
|
@@ -1733,23 +2020,7 @@ var AcpRuntimeManager = class {
|
|
|
1733
2020
|
client: owner.client,
|
|
1734
2021
|
owner
|
|
1735
2022
|
};
|
|
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
|
-
}) };
|
|
2023
|
+
return { client: this.createTurnClient(record) };
|
|
1753
2024
|
}
|
|
1754
2025
|
async requestBackendSessionClose(record, connection) {
|
|
1755
2026
|
if (!connection.owner) await withTimeout(connection.client.start(), this.options.timeoutMs);
|
|
@@ -1784,7 +2055,10 @@ var FileSessionStore = class {
|
|
|
1784
2055
|
return path.join(this.sessionDir, `${safeSessionId(sessionId)}.json`);
|
|
1785
2056
|
}
|
|
1786
2057
|
async ensureDir() {
|
|
1787
|
-
await fs.mkdir(this.sessionDir, {
|
|
2058
|
+
await fs.mkdir(this.sessionDir, {
|
|
2059
|
+
recursive: true,
|
|
2060
|
+
mode: 448
|
|
2061
|
+
});
|
|
1788
2062
|
}
|
|
1789
2063
|
async load(sessionId) {
|
|
1790
2064
|
await this.ensureDir();
|
|
@@ -1804,14 +2078,9 @@ var FileSessionStore = class {
|
|
|
1804
2078
|
return parseSessionRecord(parsed) ?? void 0;
|
|
1805
2079
|
}
|
|
1806
2080
|
async save(record) {
|
|
1807
|
-
await this.ensureDir();
|
|
1808
2081
|
const persisted = serializeSessionRecordForDisk(record);
|
|
1809
2082
|
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);
|
|
2083
|
+
await writePrivateJsonFile(this.filePath(record.acpxRecordId), persisted);
|
|
1815
2084
|
}
|
|
1816
2085
|
};
|
|
1817
2086
|
function createFileSessionStore(options) {
|
|
@@ -1856,125 +2125,20 @@ function writeHandleState(handle, state) {
|
|
|
1856
2125
|
handle.agentSessionId = state.agentSessionId;
|
|
1857
2126
|
}
|
|
1858
2127
|
//#endregion
|
|
1859
|
-
//#region src/runtime/public/probe.ts
|
|
1860
|
-
function isPrimitiveDetail(value) {
|
|
1861
|
-
return value == null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol";
|
|
1862
|
-
}
|
|
1863
|
-
function formatFunctionDetail(value) {
|
|
1864
|
-
return value.name ? `[Function ${value.name}]` : "[Function]";
|
|
1865
|
-
}
|
|
1866
|
-
function serializeRuntimeDetail(value) {
|
|
1867
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
1868
|
-
return JSON.stringify(value, (_key, nested) => {
|
|
1869
|
-
if (nested instanceof Error) return nested.message || nested.name;
|
|
1870
|
-
if (nested && typeof nested === "object") {
|
|
1871
|
-
if (seen.has(nested)) return "[Circular]";
|
|
1872
|
-
seen.add(nested);
|
|
1873
|
-
}
|
|
1874
|
-
return nested;
|
|
1875
|
-
}) ?? "undefined";
|
|
1876
|
-
}
|
|
1877
|
-
function formatRuntimeDetail(value) {
|
|
1878
|
-
if (value instanceof Error) return value.message || value.name;
|
|
1879
|
-
if (typeof value === "string") return value;
|
|
1880
|
-
if (isPrimitiveDetail(value)) return String(value);
|
|
1881
|
-
if (typeof value === "function") return formatFunctionDetail(value);
|
|
1882
|
-
try {
|
|
1883
|
-
return serializeRuntimeDetail(value);
|
|
1884
|
-
} catch {
|
|
1885
|
-
return "unserializable object";
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
function normalizeRuntimeDetails(details) {
|
|
1889
|
-
return details?.map((detail) => formatRuntimeDetail(detail));
|
|
1890
|
-
}
|
|
1891
|
-
async function probeRuntime(options, deps = {}) {
|
|
1892
|
-
const agentName = options.probeAgent?.trim() || "codex";
|
|
1893
|
-
const agentCommand = normalizeAgentCommandInput(options.agentRegistry.resolve(agentName));
|
|
1894
|
-
const client = createProbeClient(options, agentName, agentCommand, deps);
|
|
1895
|
-
try {
|
|
1896
|
-
await client.start();
|
|
1897
|
-
return {
|
|
1898
|
-
ok: true,
|
|
1899
|
-
message: "embedded ACP runtime ready",
|
|
1900
|
-
details: [
|
|
1901
|
-
`agent=${agentName}`,
|
|
1902
|
-
`command=${agentCommand.agentCommand}`,
|
|
1903
|
-
`cwd=${options.cwd}`,
|
|
1904
|
-
...client.initializeResult?.protocolVersion ? [`protocolVersion=${client.initializeResult.protocolVersion}`] : []
|
|
1905
|
-
]
|
|
1906
|
-
};
|
|
1907
|
-
} catch (error) {
|
|
1908
|
-
return {
|
|
1909
|
-
ok: false,
|
|
1910
|
-
message: "embedded ACP runtime probe failed",
|
|
1911
|
-
details: [
|
|
1912
|
-
`agent=${agentName}`,
|
|
1913
|
-
`command=${agentCommand.agentCommand}`,
|
|
1914
|
-
`cwd=${options.cwd}`,
|
|
1915
|
-
formatRuntimeDetail(error)
|
|
1916
|
-
]
|
|
1917
|
-
};
|
|
1918
|
-
} finally {
|
|
1919
|
-
await client.close().catch(() => {});
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
function createProbeClient(options, agentName, agentCommand, deps) {
|
|
1923
|
-
const clientOptions = {
|
|
1924
|
-
...agentCommand,
|
|
1925
|
-
cwd: options.cwd,
|
|
1926
|
-
agentProcessEnv: options.agentProcessEnv,
|
|
1927
|
-
mcpServers: [...options.mcpServers ?? []],
|
|
1928
|
-
permissionMode: options.permissionMode,
|
|
1929
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
1930
|
-
permissionPolicy: options.permissionPolicy,
|
|
1931
|
-
processLifecycle: options.processLifecycle,
|
|
1932
|
-
processLaunchScope: {
|
|
1933
|
-
kind: "runtime-probe",
|
|
1934
|
-
agent: agentName
|
|
1935
|
-
},
|
|
1936
|
-
verbose: options.verbose
|
|
1937
|
-
};
|
|
1938
|
-
return deps.clientFactory?.(clientOptions) ?? new AcpClient(clientOptions);
|
|
1939
|
-
}
|
|
1940
|
-
//#endregion
|
|
1941
2128
|
//#region src/runtime.ts
|
|
1942
2129
|
const ACPX_BACKEND_ID = "acpx";
|
|
1943
2130
|
const ACPX_CAPABILITIES = { controls: [
|
|
1944
2131
|
"session/set_mode",
|
|
2132
|
+
"session/set_model",
|
|
1945
2133
|
"session/set_config_option",
|
|
1946
2134
|
"session/status"
|
|
1947
2135
|
] };
|
|
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
2136
|
var AcpxRuntime = class {
|
|
1975
2137
|
options;
|
|
1976
2138
|
testOptions;
|
|
1977
2139
|
healthy = false;
|
|
2140
|
+
shutdownTask;
|
|
2141
|
+
probeTasks = /* @__PURE__ */ new Set();
|
|
1978
2142
|
manager = null;
|
|
1979
2143
|
managerPromise = null;
|
|
1980
2144
|
constructor(options, testOptions) {
|
|
@@ -2002,11 +2166,27 @@ var AcpxRuntime = class {
|
|
|
2002
2166
|
details: normalizeRuntimeDetails(report.details)
|
|
2003
2167
|
};
|
|
2004
2168
|
}
|
|
2005
|
-
|
|
2169
|
+
resolveSessionIdentity(input) {
|
|
2006
2170
|
const sessionName = input.sessionKey.trim();
|
|
2007
2171
|
if (!sessionName) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP session key is required.");
|
|
2008
2172
|
const agent = input.agent.trim();
|
|
2009
2173
|
if (!agent) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP agent id is required.");
|
|
2174
|
+
return {
|
|
2175
|
+
sessionName,
|
|
2176
|
+
agent
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
async findSession(input) {
|
|
2180
|
+
const { sessionName, agent } = this.resolveSessionIdentity(input);
|
|
2181
|
+
const record = await (await this.getManager()).findSession(sessionName);
|
|
2182
|
+
return record ? this.createSessionHandle({
|
|
2183
|
+
sessionKey: input.sessionKey,
|
|
2184
|
+
agent,
|
|
2185
|
+
mode: "persistent"
|
|
2186
|
+
}, record) : void 0;
|
|
2187
|
+
}
|
|
2188
|
+
async ensureSession(input) {
|
|
2189
|
+
const { sessionName, agent } = this.resolveSessionIdentity(input);
|
|
2010
2190
|
const record = await (await this.getManager()).ensureSession({
|
|
2011
2191
|
sessionKey: sessionName,
|
|
2012
2192
|
agent,
|
|
@@ -2015,6 +2195,12 @@ var AcpxRuntime = class {
|
|
|
2015
2195
|
resumeSessionId: input.resumeSessionId,
|
|
2016
2196
|
sessionOptions: input.sessionOptions
|
|
2017
2197
|
});
|
|
2198
|
+
return this.createSessionHandle({
|
|
2199
|
+
...input,
|
|
2200
|
+
agent
|
|
2201
|
+
}, record);
|
|
2202
|
+
}
|
|
2203
|
+
createSessionHandle(input, record) {
|
|
2018
2204
|
const handle = {
|
|
2019
2205
|
sessionKey: input.sessionKey,
|
|
2020
2206
|
backend: ACPX_BACKEND_ID,
|
|
@@ -2025,8 +2211,8 @@ var AcpxRuntime = class {
|
|
|
2025
2211
|
agentSessionId: record.agentSessionId
|
|
2026
2212
|
};
|
|
2027
2213
|
writeHandleState(handle, {
|
|
2028
|
-
name:
|
|
2029
|
-
agent,
|
|
2214
|
+
name: input.sessionKey.trim(),
|
|
2215
|
+
agent: input.agent,
|
|
2030
2216
|
cwd: record.cwd,
|
|
2031
2217
|
mode: input.mode,
|
|
2032
2218
|
acpxRecordId: record.acpxRecordId,
|
|
@@ -2046,7 +2232,8 @@ var AcpxRuntime = class {
|
|
|
2046
2232
|
requestId: input.requestId,
|
|
2047
2233
|
timeoutMs: input.timeoutMs,
|
|
2048
2234
|
signal: input.signal,
|
|
2049
|
-
onElicitation: input.onElicitation
|
|
2235
|
+
onElicitation: input.onElicitation,
|
|
2236
|
+
onPermissionRequest: input.onPermissionRequest
|
|
2050
2237
|
}));
|
|
2051
2238
|
return {
|
|
2052
2239
|
requestId: input.requestId,
|
|
@@ -2078,7 +2265,8 @@ var AcpxRuntime = class {
|
|
|
2078
2265
|
requestId: input.requestId,
|
|
2079
2266
|
timeoutMs: input.timeoutMs,
|
|
2080
2267
|
signal: input.signal,
|
|
2081
|
-
onElicitation: input.onElicitation
|
|
2268
|
+
onElicitation: input.onElicitation,
|
|
2269
|
+
onPermissionRequest: input.onPermissionRequest
|
|
2082
2270
|
});
|
|
2083
2271
|
}
|
|
2084
2272
|
async getCapabilities(input) {
|
|
@@ -2100,6 +2288,10 @@ var AcpxRuntime = class {
|
|
|
2100
2288
|
const { handle, state } = this.resolveManagerHandle(input.handle);
|
|
2101
2289
|
await (await this.getManager()).setMode(handle, input.mode, state.mode);
|
|
2102
2290
|
}
|
|
2291
|
+
async setModel(input) {
|
|
2292
|
+
const { handle, state } = this.resolveManagerHandle(input.handle);
|
|
2293
|
+
await (await this.getManager()).setModel(handle, input.model, state.mode);
|
|
2294
|
+
}
|
|
2103
2295
|
async setConfigOption(input) {
|
|
2104
2296
|
const { handle, state } = this.resolveManagerHandle(input.handle);
|
|
2105
2297
|
return await (await this.getManager()).setConfigOption(handle, input.key, input.value, state.mode);
|
|
@@ -2108,11 +2300,26 @@ var AcpxRuntime = class {
|
|
|
2108
2300
|
const { handle } = this.resolveManagerHandle(input.handle);
|
|
2109
2301
|
await (await this.getManager()).cancel(handle);
|
|
2110
2302
|
}
|
|
2303
|
+
async prepareFreshSession(input) {
|
|
2304
|
+
const { handle } = this.resolveManagerHandle(input.handle);
|
|
2305
|
+
await (await this.getManager()).prepareFreshSession(handle);
|
|
2306
|
+
}
|
|
2111
2307
|
async close(input) {
|
|
2112
2308
|
const { handle } = this.resolveManagerHandle(input.handle);
|
|
2113
2309
|
await (await this.getManager()).close(handle, { discardPersistentState: input.discardPersistentState });
|
|
2114
2310
|
}
|
|
2311
|
+
shutdown() {
|
|
2312
|
+
if (!this.shutdownTask) {
|
|
2313
|
+
this.healthy = false;
|
|
2314
|
+
const managerShutdown = this.managerPromise?.then((manager) => manager.shutdown());
|
|
2315
|
+
this.shutdownTask = Promise.allSettled([managerShutdown, ...this.probeTasks]).then(([manager]) => {
|
|
2316
|
+
if (manager.status === "rejected") throw manager.reason;
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
return this.shutdownTask;
|
|
2320
|
+
}
|
|
2115
2321
|
async getManager() {
|
|
2322
|
+
if (this.shutdownTask) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
2116
2323
|
if (this.manager) return this.manager;
|
|
2117
2324
|
if (!this.managerPromise) this.managerPromise = Promise.resolve(this.testOptions?.managerFactory?.(this.options) ?? new AcpRuntimeManager(this.options)).then((manager) => {
|
|
2118
2325
|
this.manager = manager;
|
|
@@ -2121,7 +2328,16 @@ var AcpxRuntime = class {
|
|
|
2121
2328
|
return await this.managerPromise;
|
|
2122
2329
|
}
|
|
2123
2330
|
async runProbe() {
|
|
2124
|
-
|
|
2331
|
+
if (this.shutdownTask) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
2332
|
+
const probe = this.testOptions?.probeRunner?.(this.options) ?? this.getManager().then((manager) => manager.probe());
|
|
2333
|
+
this.probeTasks.add(probe);
|
|
2334
|
+
try {
|
|
2335
|
+
const report = await probe;
|
|
2336
|
+
if (this.shutdownTask) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
2337
|
+
return report;
|
|
2338
|
+
} finally {
|
|
2339
|
+
this.probeTasks.delete(probe);
|
|
2340
|
+
}
|
|
2125
2341
|
}
|
|
2126
2342
|
resolveManagerHandle(handle) {
|
|
2127
2343
|
const state = this.resolveHandleState(handle);
|