@ganglion/xacpx 0.15.3 → 0.15.5
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.
|
@@ -4193,6 +4193,16 @@ class BridgeRuntime {
|
|
|
4193
4193
|
await deleteAcpxSessionFiles({ acpxRecordId });
|
|
4194
4194
|
return {};
|
|
4195
4195
|
}
|
|
4196
|
+
async freeWarmProcess(input) {
|
|
4197
|
+
let acpxRecordId;
|
|
4198
|
+
try {
|
|
4199
|
+
({ acpxRecordId } = await this.readSessionRecord(input));
|
|
4200
|
+
} catch {
|
|
4201
|
+
return {};
|
|
4202
|
+
}
|
|
4203
|
+
await terminateAcpxQueueOwner(acpxRecordId);
|
|
4204
|
+
return {};
|
|
4205
|
+
}
|
|
4196
4206
|
async shutdown() {
|
|
4197
4207
|
return {};
|
|
4198
4208
|
}
|
|
@@ -4440,6 +4450,7 @@ var BRIDGE_METHODS = new Set([
|
|
|
4440
4450
|
"cancel",
|
|
4441
4451
|
"removeSession",
|
|
4442
4452
|
"deleteSession",
|
|
4453
|
+
"freeWarmProcess",
|
|
4443
4454
|
"getAgentSessionId"
|
|
4444
4455
|
]);
|
|
4445
4456
|
var SESSION_SCOPED_METHODS = new Set([
|
|
@@ -4454,6 +4465,7 @@ var SESSION_SCOPED_METHODS = new Set([
|
|
|
4454
4465
|
"cancel",
|
|
4455
4466
|
"removeSession",
|
|
4456
4467
|
"deleteSession",
|
|
4468
|
+
"freeWarmProcess",
|
|
4457
4469
|
"getAgentSessionId"
|
|
4458
4470
|
]);
|
|
4459
4471
|
|
|
@@ -4677,6 +4689,13 @@ class BridgeServer {
|
|
|
4677
4689
|
cwd: requireString(params, "cwd"),
|
|
4678
4690
|
name: requireString(params, "name")
|
|
4679
4691
|
});
|
|
4692
|
+
case "freeWarmProcess":
|
|
4693
|
+
return await this.runtime.freeWarmProcess({
|
|
4694
|
+
agent: requireString(params, "agent"),
|
|
4695
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
4696
|
+
cwd: requireString(params, "cwd"),
|
|
4697
|
+
name: requireString(params, "name")
|
|
4698
|
+
});
|
|
4680
4699
|
case "getAgentSessionId":
|
|
4681
4700
|
return await this.runtime.getAgentSessionId({
|
|
4682
4701
|
agent: requireString(params, "agent"),
|
package/dist/cli.js
CHANGED
|
@@ -20164,7 +20164,7 @@ async function resolveSessionAgentCommandFromIndex(session3) {
|
|
|
20164
20164
|
const raw = await readFile12(resolve2(home, ".acpx", "sessions", "index.json"), "utf8");
|
|
20165
20165
|
const parsed = JSON.parse(raw);
|
|
20166
20166
|
const targetCwd = resolve2(session3.cwd);
|
|
20167
|
-
const match = parsed.entries?.find((entry) => entry.name === session3.transportSession && entry.cwd === targetCwd && typeof entry.agentCommand === "string" && entry.agentCommand.trim().length > 0);
|
|
20167
|
+
const match = parsed.entries?.find((entry) => entry.name === session3.transportSession && typeof entry.cwd === "string" && resolve2(entry.cwd) === targetCwd && typeof entry.agentCommand === "string" && entry.agentCommand.trim().length > 0);
|
|
20168
20168
|
return match?.agentCommand?.trim();
|
|
20169
20169
|
} catch {
|
|
20170
20170
|
return;
|
|
@@ -24733,7 +24733,7 @@ class CommandRouter {
|
|
|
24733
24733
|
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands) {
|
|
24734
24734
|
const startedAt = Date.now();
|
|
24735
24735
|
let command = parseCommand(input);
|
|
24736
|
-
if (metadata?.channel === "control" && command.kind !== "prompt") {
|
|
24736
|
+
if (metadata?.channel === "control" && command.kind !== "prompt" && command.kind !== "session.reset") {
|
|
24737
24737
|
command = { kind: "prompt", text: input.trim() };
|
|
24738
24738
|
}
|
|
24739
24739
|
await this.logger.debug("command.parsed", "parsed inbound command", {
|
|
@@ -24832,7 +24832,7 @@ class CommandRouter {
|
|
|
24832
24832
|
case "cancel":
|
|
24833
24833
|
return await handleCancel(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias);
|
|
24834
24834
|
case "session.reset":
|
|
24835
|
-
return await handleSessionReset(this.createSessionHandlerContext(reply, perfSpan), chatKey);
|
|
24835
|
+
return await handleSessionReset(this.createSessionHandlerContext(metadata?.channel === "control" ? undefined : reply, perfSpan), chatKey);
|
|
24836
24836
|
case "session.tail":
|
|
24837
24837
|
return await handleSessionTail(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.lines);
|
|
24838
24838
|
case "session.rm":
|
|
@@ -25046,6 +25046,15 @@ class CommandRouter {
|
|
|
25046
25046
|
try {
|
|
25047
25047
|
await this.transport.cancel(session3);
|
|
25048
25048
|
} catch {}
|
|
25049
|
+
try {
|
|
25050
|
+
await this.transport.freeWarmProcess?.(session3);
|
|
25051
|
+
} catch (error2) {
|
|
25052
|
+
await this.logger.error("session.free_warm_process_failed", "failed to free warm queue-owner on archive", {
|
|
25053
|
+
alias: internalAlias,
|
|
25054
|
+
transportSession: session3.transportSession,
|
|
25055
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
25056
|
+
});
|
|
25057
|
+
}
|
|
25049
25058
|
}
|
|
25050
25059
|
await this.sessions.setArchived(internalAlias, true);
|
|
25051
25060
|
}
|
|
@@ -30095,6 +30104,7 @@ class SessionService {
|
|
|
30095
30104
|
agent: session3.agent,
|
|
30096
30105
|
agentCommand: session3.transport_agent_command ?? resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, this.config.transport.preferLocalAgents !== false),
|
|
30097
30106
|
model: session3.model ?? agentConfig.model,
|
|
30107
|
+
displayName: session3.display_name,
|
|
30098
30108
|
workspace: session3.workspace,
|
|
30099
30109
|
transportSession: session3.transport_session,
|
|
30100
30110
|
source: session3.source,
|
|
@@ -30125,6 +30135,22 @@ class SessionService {
|
|
|
30125
30135
|
await this.persist();
|
|
30126
30136
|
});
|
|
30127
30137
|
}
|
|
30138
|
+
async setDisplayName(alias, name) {
|
|
30139
|
+
await this.mutate(async () => {
|
|
30140
|
+
const session3 = this.state.sessions[alias];
|
|
30141
|
+
if (!session3) {
|
|
30142
|
+
throw new Error(`session "${alias}" does not exist`);
|
|
30143
|
+
}
|
|
30144
|
+
const normalized = name?.trim();
|
|
30145
|
+
if (normalized) {
|
|
30146
|
+
session3.display_name = normalized;
|
|
30147
|
+
} else {
|
|
30148
|
+
delete session3.display_name;
|
|
30149
|
+
}
|
|
30150
|
+
session3.last_used_at = new Date(this.now()).toISOString();
|
|
30151
|
+
await this.persist();
|
|
30152
|
+
});
|
|
30153
|
+
}
|
|
30128
30154
|
async setCurrentSessionModel(chatKey, modelId) {
|
|
30129
30155
|
await this.mutate(async () => {
|
|
30130
30156
|
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
@@ -31556,6 +31582,9 @@ ${result.text}` : "" };
|
|
|
31556
31582
|
async deleteSession(session3) {
|
|
31557
31583
|
await this.client.request("deleteSession", this.toParams(session3));
|
|
31558
31584
|
}
|
|
31585
|
+
async freeWarmProcess(session3) {
|
|
31586
|
+
await this.client.request("freeWarmProcess", this.toParams(session3));
|
|
31587
|
+
}
|
|
31559
31588
|
async getAgentSessionId(session3) {
|
|
31560
31589
|
const result = await this.client.request("getAgentSessionId", this.toParams(session3));
|
|
31561
31590
|
return result.agentSessionId;
|
|
@@ -32554,7 +32583,7 @@ class AcpxCliTransport {
|
|
|
32554
32583
|
const structuredPrompt = await createStructuredPromptFile(text, options?.media);
|
|
32555
32584
|
const args = this.buildPromptArgs(session3, text, structuredPrompt?.filePath);
|
|
32556
32585
|
try {
|
|
32557
|
-
if (reply || options?.onSegment || options?.onToolEvent || options?.onThought || options?.onPlan) {
|
|
32586
|
+
if (reply || options?.onSegment || options?.onToolEvent || options?.onThought || options?.onPlan || options?.onUsage || options?.onCommands) {
|
|
32558
32587
|
const effectiveReplyMode = session3.effectiveReplyMode ?? session3.replyMode;
|
|
32559
32588
|
const formatToolCalls = (effectiveReplyMode ?? "verbose") === "verbose";
|
|
32560
32589
|
const rawStream = effectiveReplyMode === "stream";
|
|
@@ -32562,7 +32591,7 @@ class AcpxCliTransport {
|
|
|
32562
32591
|
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
32563
32592
|
toolEventMode = "text";
|
|
32564
32593
|
}
|
|
32565
|
-
const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent, options?.onThought, options?.onPlan, rawStream);
|
|
32594
|
+
const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent, options?.onThought, options?.onPlan, options?.onUsage, options?.onCommands, rawStream);
|
|
32566
32595
|
const baseText = getPromptText(result2);
|
|
32567
32596
|
if (!reply) {
|
|
32568
32597
|
return { text: baseText };
|
|
@@ -32671,6 +32700,15 @@ ${baseText}` : "" };
|
|
|
32671
32700
|
await this.removeSession(session3);
|
|
32672
32701
|
await deleteAcpxSessionFiles({ acpxRecordId });
|
|
32673
32702
|
}
|
|
32703
|
+
async freeWarmProcess(session3) {
|
|
32704
|
+
let acpxRecordId;
|
|
32705
|
+
try {
|
|
32706
|
+
({ acpxRecordId } = await this.readSessionRecord(session3));
|
|
32707
|
+
} catch {
|
|
32708
|
+
return;
|
|
32709
|
+
}
|
|
32710
|
+
await terminateAcpxQueueOwner(acpxRecordId);
|
|
32711
|
+
}
|
|
32674
32712
|
async hasSession(session3) {
|
|
32675
32713
|
const result = await this.runCommand(this.command, this.buildArgs(session3, [
|
|
32676
32714
|
"sessions",
|
|
@@ -32758,7 +32796,7 @@ ${baseText}` : "" };
|
|
|
32758
32796
|
})
|
|
32759
32797
|
]);
|
|
32760
32798
|
}
|
|
32761
|
-
async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent, onThought, onPlan, rawStream = false) {
|
|
32799
|
+
async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent, onThought, onPlan, onUsage, onCommands, rawStream = false) {
|
|
32762
32800
|
const hooks = this.streamingHooks;
|
|
32763
32801
|
const doSpawn = hooks.spawnPrompt ?? ((cmd, spawnArgs) => spawn9(cmd, spawnArgs, { stdio: ["ignore", "pipe", "pipe"] }));
|
|
32764
32802
|
const setIntervalFn = hooks.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
|
|
@@ -32780,9 +32818,15 @@ ${baseText}` : "" };
|
|
|
32780
32818
|
let thoughtError;
|
|
32781
32819
|
let planChain = Promise.resolve();
|
|
32782
32820
|
let planError;
|
|
32821
|
+
let usageChain = Promise.resolve();
|
|
32822
|
+
let usageError;
|
|
32823
|
+
let commandsChain = Promise.resolve();
|
|
32824
|
+
let commandsError;
|
|
32783
32825
|
const userOnToolEvent = onToolEvent;
|
|
32784
32826
|
const userOnThought = onThought;
|
|
32785
32827
|
const userOnPlan = onPlan;
|
|
32828
|
+
const userOnUsage = onUsage;
|
|
32829
|
+
const userOnCommands = onCommands;
|
|
32786
32830
|
const state = createStreamingPromptState(formatToolCalls, {
|
|
32787
32831
|
mode: toolEventMode,
|
|
32788
32832
|
rawStream,
|
|
@@ -32806,6 +32850,20 @@ ${baseText}` : "" };
|
|
|
32806
32850
|
planError ??= error2;
|
|
32807
32851
|
});
|
|
32808
32852
|
}
|
|
32853
|
+
} : {},
|
|
32854
|
+
...userOnUsage ? {
|
|
32855
|
+
onUsage: (usage) => {
|
|
32856
|
+
usageChain = usageChain.then(() => userOnUsage(usage)).catch((error2) => {
|
|
32857
|
+
usageError ??= error2;
|
|
32858
|
+
});
|
|
32859
|
+
}
|
|
32860
|
+
} : {},
|
|
32861
|
+
...userOnCommands ? {
|
|
32862
|
+
onCommands: (commands) => {
|
|
32863
|
+
commandsChain = commandsChain.then(() => userOnCommands(commands)).catch((error2) => {
|
|
32864
|
+
commandsError ??= error2;
|
|
32865
|
+
});
|
|
32866
|
+
}
|
|
32809
32867
|
} : {}
|
|
32810
32868
|
});
|
|
32811
32869
|
const sink = reply ? rawStream ? createVerbatimReplySink(reply) : createQuotaGatedReplySink({
|
|
@@ -32860,7 +32918,9 @@ ${baseText}` : "" };
|
|
|
32860
32918
|
segmentChain,
|
|
32861
32919
|
toolEventChain,
|
|
32862
32920
|
thoughtChain,
|
|
32863
|
-
planChain
|
|
32921
|
+
planChain,
|
|
32922
|
+
usageChain,
|
|
32923
|
+
commandsChain
|
|
32864
32924
|
]).then(() => {
|
|
32865
32925
|
const deferred = sink?.getPendingError();
|
|
32866
32926
|
if (deferred) {
|
|
@@ -32883,6 +32943,14 @@ ${baseText}` : "" };
|
|
|
32883
32943
|
reject(planError);
|
|
32884
32944
|
return;
|
|
32885
32945
|
}
|
|
32946
|
+
if (usageError) {
|
|
32947
|
+
reject(usageError);
|
|
32948
|
+
return;
|
|
32949
|
+
}
|
|
32950
|
+
if (commandsError) {
|
|
32951
|
+
reject(commandsError);
|
|
32952
|
+
return;
|
|
32953
|
+
}
|
|
32886
32954
|
resolve3({
|
|
32887
32955
|
result: { code: code ?? 1, stdout: stdout2, stderr },
|
|
32888
32956
|
overflowCount
|
|
@@ -33845,6 +33913,12 @@ class ControlService {
|
|
|
33845
33913
|
await this.deps.transport.setModel(session3, modelId);
|
|
33846
33914
|
await this.deps.sessions.setSessionModel(session3.alias, modelId);
|
|
33847
33915
|
}
|
|
33916
|
+
async setSessionDisplayName(chatKey, alias, displayName) {
|
|
33917
|
+
const session3 = await this.resolveControlSession(chatKey, alias);
|
|
33918
|
+
if (!session3)
|
|
33919
|
+
throw new Error("session not found");
|
|
33920
|
+
await this.deps.sessions.setDisplayName(session3.alias, displayName);
|
|
33921
|
+
}
|
|
33848
33922
|
async resolveControlSession(chatKey, alias) {
|
|
33849
33923
|
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33850
33924
|
return await this.deps.sessions.getSession(internalAlias);
|
|
@@ -33861,7 +33935,9 @@ class ControlService {
|
|
|
33861
33935
|
transportSession: session3.transportSession,
|
|
33862
33936
|
running: this.deps.activeTurns.isActiveAnywhere(session3.alias),
|
|
33863
33937
|
archived: session3.archived === true,
|
|
33864
|
-
...session3.
|
|
33938
|
+
...session3.source === "agent-side" ? { native: true } : {},
|
|
33939
|
+
...session3.agentCommand ? { agentCommand: session3.agentCommand } : {},
|
|
33940
|
+
...session3.displayName ? { displayName: session3.displayName } : {}
|
|
33865
33941
|
}));
|
|
33866
33942
|
}
|
|
33867
33943
|
async listNativeSessions(_chatKey, agent3, workspace3) {
|
|
@@ -34012,10 +34088,14 @@ class ControlService {
|
|
|
34012
34088
|
resolveSettled = resolve4;
|
|
34013
34089
|
});
|
|
34014
34090
|
this.inFlight.set(key, { controller, settled });
|
|
34091
|
+
let internalAlias;
|
|
34015
34092
|
let wasArchived = false;
|
|
34093
|
+
let priorTransportSession;
|
|
34016
34094
|
try {
|
|
34017
|
-
|
|
34018
|
-
|
|
34095
|
+
internalAlias = await this.deps.sessions.resolveAliasForChat(params.chatKey, params.sessionAlias);
|
|
34096
|
+
const prior = await this.deps.sessions.getSession(internalAlias);
|
|
34097
|
+
wasArchived = prior?.archived === true;
|
|
34098
|
+
priorTransportSession = prior?.transportSession;
|
|
34019
34099
|
} catch {}
|
|
34020
34100
|
try {
|
|
34021
34101
|
await this.deps.sessions.useSession(params.chatKey, params.sessionAlias);
|
|
@@ -34158,6 +34238,14 @@ ${chunk}` : chunk
|
|
|
34158
34238
|
} finally {
|
|
34159
34239
|
this.inFlight.delete(key);
|
|
34160
34240
|
resolveSettled();
|
|
34241
|
+
if (internalAlias && priorTransportSession) {
|
|
34242
|
+
try {
|
|
34243
|
+
const after = await this.deps.sessions.getSession(internalAlias);
|
|
34244
|
+
if (after && after.transportSession !== priorTransportSession) {
|
|
34245
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
34246
|
+
}
|
|
34247
|
+
} catch {}
|
|
34248
|
+
}
|
|
34161
34249
|
}
|
|
34162
34250
|
}
|
|
34163
34251
|
cancelTurn(chatKey, sessionAlias) {
|
|
@@ -18,11 +18,17 @@ export interface ControlSessionInfo {
|
|
|
18
18
|
transportSession: string;
|
|
19
19
|
running: boolean;
|
|
20
20
|
archived: boolean;
|
|
21
|
+
/** True when this logical session was attached to an existing agent-side (native) rollout
|
|
22
|
+
* rather than freshly created. Mirrors LogicalSession.source === "agent-side"; omitted for
|
|
23
|
+
* fresh xacpx sessions so the wire stays minimal. */
|
|
24
|
+
native?: boolean;
|
|
21
25
|
/** The agent adapter command this session runs (acpx-recorded, or the agent's resolved
|
|
22
26
|
* default). Surfaced so the web can avoid seeding a new session's model picker from a
|
|
23
27
|
* session on a different adapter version (whose advertised model ids may be in an
|
|
24
28
|
* incompatible format). Omitted when unknown. */
|
|
25
29
|
agentCommand?: string;
|
|
30
|
+
/** Cosmetic relay-web display label; omitted when unset so the wire stays minimal. */
|
|
31
|
+
displayName?: string;
|
|
26
32
|
}
|
|
27
33
|
export interface ControlAgentInfo {
|
|
28
34
|
name: string;
|
|
@@ -42,7 +48,7 @@ export interface ControlWorkspaceInfo {
|
|
|
42
48
|
}
|
|
43
49
|
export interface ControlServiceDeps {
|
|
44
50
|
agent: Pick<ChatAgent, "chat">;
|
|
45
|
-
sessions: Pick<SessionService, "listAllResolvedSessions" | "removeSession" | "useSession" | "resolveAliasForChat" | "getSession" | "setSessionModel">;
|
|
51
|
+
sessions: Pick<SessionService, "listAllResolvedSessions" | "removeSession" | "useSession" | "resolveAliasForChat" | "getSession" | "setSessionModel" | "setDisplayName">;
|
|
46
52
|
transport: Pick<SessionTransport, "setModel" | "getSessionModel">;
|
|
47
53
|
createSessionWithTransport: (internalAlias: string, agent: string, workspace: string, model?: string) => Promise<ResolvedSession>;
|
|
48
54
|
removeSessionWithTransport: (internalAlias: string) => Promise<{
|
|
@@ -132,6 +138,8 @@ export declare class ControlService {
|
|
|
132
138
|
}>;
|
|
133
139
|
/** Switch a session's model (acpx validates the id) and persist the override. */
|
|
134
140
|
setSessionModel(chatKey: string, alias: string, modelId: string): Promise<void>;
|
|
141
|
+
/** Set (or clear) a session's relay-web display label and persist it. */
|
|
142
|
+
setSessionDisplayName(chatKey: string, alias: string, displayName: string): Promise<void>;
|
|
135
143
|
/** Resolve a chat-scoped display alias to its ResolvedSession, or null. */
|
|
136
144
|
private resolveControlSession;
|
|
137
145
|
get events(): ControlEventBus;
|
|
@@ -116,6 +116,8 @@ export declare class SessionService {
|
|
|
116
116
|
private toResolvedSession;
|
|
117
117
|
/** Persist (or clear) a session's model override by internal alias. */
|
|
118
118
|
setSessionModel(alias: string, modelId: string | undefined): Promise<void>;
|
|
119
|
+
/** Set (or clear) a session's relay-web display label. Identity (`alias`) is untouched. */
|
|
120
|
+
setDisplayName(alias: string, name?: string): Promise<void>;
|
|
119
121
|
/** Persist (or clear) the model override of the chat's current session. */
|
|
120
122
|
setCurrentSessionModel(chatKey: string, modelId: string | undefined): Promise<void>;
|
|
121
123
|
setSessionTransportAgentCommand(alias: string, transportAgentCommand: string | undefined): Promise<void>;
|
package/dist/state/types.d.ts
CHANGED
|
@@ -29,6 +29,9 @@ export interface LogicalSession {
|
|
|
29
29
|
mode_id?: string;
|
|
30
30
|
/** Per-session LLM model override (e.g. `gpt-5.2[high]`); falls back to the agent config default. */
|
|
31
31
|
model?: string;
|
|
32
|
+
/** Per-session cosmetic display label shown in the relay-web dashboard only.
|
|
33
|
+
* Never affects identity (`alias`), `/use`, or the transport session. Cleared → UI shows alias. */
|
|
34
|
+
display_name?: string;
|
|
32
35
|
reply_mode?: "stream" | "final" | "verbose";
|
|
33
36
|
/** True when the user archived this session: process closed, row greyed + sunk.
|
|
34
37
|
* Cleared on the next useSession (restore-on-message). */
|
|
@@ -59,6 +59,9 @@ export interface ResolvedSession {
|
|
|
59
59
|
* no `--model` is passed and acpx uses the agent adapter's default.
|
|
60
60
|
*/
|
|
61
61
|
model?: string;
|
|
62
|
+
/** Cosmetic per-session display label (relay-web only). Mirrors LogicalSession.display_name;
|
|
63
|
+
* undefined when unset. Does not affect identity or transport. */
|
|
64
|
+
displayName?: string;
|
|
62
65
|
workspace: string;
|
|
63
66
|
transportSession: string;
|
|
64
67
|
source?: "xacpx" | "agent-side";
|
|
@@ -207,6 +210,15 @@ export interface SessionTransport {
|
|
|
207
210
|
* that can't delete omit it. A missing acpx session is a no-op (idempotent).
|
|
208
211
|
*/
|
|
209
212
|
deleteSession?(session: ResolvedSession): Promise<void>;
|
|
213
|
+
/**
|
|
214
|
+
* Terminate the warm queue-owner process for this session, freeing its
|
|
215
|
+
* resources, WITHOUT closing the acpx session (no `closed` flag, no metadata
|
|
216
|
+
* change) — the session stays open and resumes with full history on the next
|
|
217
|
+
* prompt. Idempotent: a missing warm process or missing session is a no-op.
|
|
218
|
+
* Used by archive to free the process now instead of waiting for acpx's TTL.
|
|
219
|
+
* Optional: transports that can't reap omit it.
|
|
220
|
+
*/
|
|
221
|
+
freeWarmProcess?(session: ResolvedSession): Promise<void>;
|
|
210
222
|
/**
|
|
211
223
|
* Read the underlying agent-native session id for an existing transport
|
|
212
224
|
* session. Used by `/clear` to keep a native session native: the fresh
|