@ganglion/xacpx 0.12.1 → 0.13.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/bridge/bridge-main.js +55 -7
- package/dist/cli.js +232 -60
- package/dist/commands/handlers/session-handler.d.ts +1 -0
- package/dist/commands/parse-command.d.ts +3 -0
- package/dist/control/control-service.d.ts +8 -0
- package/dist/i18n/types.d.ts +1 -0
- package/dist/plugin-api.js +2 -0
- package/dist/sessions/session-service.d.ts +1 -0
- package/dist/state/types.d.ts +4 -0
- package/dist/transport/types.d.ts +12 -0
- package/package.json +1 -1
|
@@ -940,6 +940,7 @@ ${detail}`,
|
|
|
940
940
|
sessionBlockedByTasks: (alias, count) => `Session "${alias}" has ${count} unfinished task(s). Cancel or wait for them to complete first.`,
|
|
941
941
|
sessionBlockedByTasksHint: "Use /tasks to list tasks, or /task cancel <id> to cancel one.",
|
|
942
942
|
sessionRemoved: (alias) => `Session "${alias}" removed.`,
|
|
943
|
+
sessionArchived: (alias) => `Archived session "${alias}". Send a message to restore it.`,
|
|
943
944
|
sessionRemovedWasActive: "This was the active session. Its chat context has been cleared.",
|
|
944
945
|
sessionRemovedWasActivePromoted: (alias) => `This was the active session. Switched back to the previous session "${alias}".`,
|
|
945
946
|
sessionTransportShared: (transportSession, count) => `Note: backend session "${transportSession}" is still referenced by ${count} other session(s) and was not closed.`,
|
|
@@ -2035,6 +2036,7 @@ ${detail}`,
|
|
|
2035
2036
|
sessionBlockedByTasks: (alias, count) => `会话「${alias}」下还有 ${count} 个未结束的任务,请先取消或等待完成。`,
|
|
2036
2037
|
sessionBlockedByTasksHint: "使用 /tasks 查看任务列表,或 /task cancel <id> 取消任务。",
|
|
2037
2038
|
sessionRemoved: (alias) => `已删除会话「${alias}」。`,
|
|
2039
|
+
sessionArchived: (alias) => `已归档会话「${alias}」。发送消息即可恢复。`,
|
|
2038
2040
|
sessionRemovedWasActive: "该会话是当前活跃会话,已自动清除相关聊天上下文。",
|
|
2039
2041
|
sessionRemovedWasActivePromoted: (alias) => `该会话是当前活跃会话,已切换回上一个会话「${alias}」。`,
|
|
2040
2042
|
sessionTransportShared: (transportSession, count) => `提示:后端会话「${transportSession}」仍被其他 ${count} 个会话引用,未关闭。`,
|
|
@@ -3424,6 +3426,31 @@ var init_agent_session_list = __esm(() => {
|
|
|
3424
3426
|
init_path();
|
|
3425
3427
|
});
|
|
3426
3428
|
|
|
3429
|
+
// src/transport/acpx-session-files.ts
|
|
3430
|
+
import { readdir, unlink as unlink2 } from "node:fs/promises";
|
|
3431
|
+
import { homedir as homedir4 } from "node:os";
|
|
3432
|
+
import { join as join3 } from "node:path";
|
|
3433
|
+
async function deleteAcpxSessionFiles(options) {
|
|
3434
|
+
const dir = options.sessionsDir ?? join3(homedir4(), ".acpx", "sessions");
|
|
3435
|
+
const safeId = encodeURIComponent(options.acpxRecordId);
|
|
3436
|
+
await unlink2(join3(dir, `${safeId}.json`)).catch(() => {
|
|
3437
|
+
return;
|
|
3438
|
+
});
|
|
3439
|
+
let entries;
|
|
3440
|
+
try {
|
|
3441
|
+
entries = await readdir(dir);
|
|
3442
|
+
} catch {
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
|
|
3446
|
+
for (const name of streamFiles) {
|
|
3447
|
+
await unlink2(join3(dir, name)).catch(() => {
|
|
3448
|
+
return;
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
var init_acpx_session_files = () => {};
|
|
3453
|
+
|
|
3427
3454
|
// src/bridge/bridge-main.ts
|
|
3428
3455
|
import { createInterface } from "node:readline";
|
|
3429
3456
|
|
|
@@ -3496,9 +3523,9 @@ init_terminate_process_tree();
|
|
|
3496
3523
|
init_prompt_output();
|
|
3497
3524
|
init_prompt_media();
|
|
3498
3525
|
init_streaming_prompt();
|
|
3499
|
-
import { copyFile, readdir } from "node:fs/promises";
|
|
3500
|
-
import { homedir as
|
|
3501
|
-
import { dirname as dirname2, join as
|
|
3526
|
+
import { copyFile, readdir as readdir2 } from "node:fs/promises";
|
|
3527
|
+
import { homedir as homedir5 } from "node:os";
|
|
3528
|
+
import { dirname as dirname2, join as join4, win32 } from "node:path";
|
|
3502
3529
|
import { spawn as spawn4 } from "node:child_process";
|
|
3503
3530
|
|
|
3504
3531
|
// src/bridge/parse-missing-optional-dep.ts
|
|
@@ -3518,6 +3545,7 @@ function parseMissingOptionalDep(text) {
|
|
|
3518
3545
|
init_discover_parent_package_paths();
|
|
3519
3546
|
init_acpx_queue_owner_launcher();
|
|
3520
3547
|
init_agent_session_list();
|
|
3548
|
+
init_acpx_session_files();
|
|
3521
3549
|
class EnsureSessionFailedError extends Error {
|
|
3522
3550
|
kind;
|
|
3523
3551
|
data;
|
|
@@ -3798,7 +3826,7 @@ class BridgeRuntime {
|
|
|
3798
3826
|
acpxRecordId = parsed.id;
|
|
3799
3827
|
}
|
|
3800
3828
|
const agentSessionId = typeof parsed.agentSessionId === "string" ? parsed.agentSessionId : undefined;
|
|
3801
|
-
if (acpxRecordId) {
|
|
3829
|
+
if (acpxRecordId && /^[\w.:-]+$/.test(acpxRecordId) && acpxRecordId.length >= 8) {
|
|
3802
3830
|
return { acpxRecordId, agentSessionId };
|
|
3803
3831
|
}
|
|
3804
3832
|
} catch {
|
|
@@ -3890,6 +3918,17 @@ class BridgeRuntime {
|
|
|
3890
3918
|
}
|
|
3891
3919
|
throw new Error(result.stderr || result.stdout || "sessions close failed");
|
|
3892
3920
|
}
|
|
3921
|
+
async deleteSession(input) {
|
|
3922
|
+
let acpxRecordId;
|
|
3923
|
+
try {
|
|
3924
|
+
({ acpxRecordId } = await this.readSessionRecord(input));
|
|
3925
|
+
} catch {
|
|
3926
|
+
return {};
|
|
3927
|
+
}
|
|
3928
|
+
await this.removeSession(input);
|
|
3929
|
+
await deleteAcpxSessionFiles({ acpxRecordId });
|
|
3930
|
+
return {};
|
|
3931
|
+
}
|
|
3893
3932
|
async shutdown() {
|
|
3894
3933
|
return {};
|
|
3895
3934
|
}
|
|
@@ -4080,14 +4119,14 @@ async function tryRepairAcpxSessionIndex(deps = {}) {
|
|
|
4080
4119
|
if (platform !== "win32") {
|
|
4081
4120
|
return false;
|
|
4082
4121
|
}
|
|
4083
|
-
const home = deps.home ?? process.env.HOME ?? process.env.USERPROFILE ??
|
|
4122
|
+
const home = deps.home ?? process.env.HOME ?? process.env.USERPROFILE ?? homedir5();
|
|
4084
4123
|
if (!home) {
|
|
4085
4124
|
return false;
|
|
4086
4125
|
}
|
|
4087
|
-
const pathJoin = platform === "win32" ? win32.join :
|
|
4126
|
+
const pathJoin = platform === "win32" ? win32.join : join4;
|
|
4088
4127
|
const sessionsDir = pathJoin(home, ".acpx", "sessions");
|
|
4089
4128
|
const indexPath = pathJoin(sessionsDir, "index.json");
|
|
4090
|
-
const readdirFn = deps.readdirFn ??
|
|
4129
|
+
const readdirFn = deps.readdirFn ?? readdir2;
|
|
4091
4130
|
const copyFileFn = deps.copyFileFn ?? copyFile;
|
|
4092
4131
|
let files;
|
|
4093
4132
|
try {
|
|
@@ -4135,6 +4174,7 @@ var BRIDGE_METHODS = new Set([
|
|
|
4135
4174
|
"getSessionModel",
|
|
4136
4175
|
"cancel",
|
|
4137
4176
|
"removeSession",
|
|
4177
|
+
"deleteSession",
|
|
4138
4178
|
"getAgentSessionId"
|
|
4139
4179
|
]);
|
|
4140
4180
|
var SESSION_SCOPED_METHODS = new Set([
|
|
@@ -4148,6 +4188,7 @@ var SESSION_SCOPED_METHODS = new Set([
|
|
|
4148
4188
|
"getSessionModel",
|
|
4149
4189
|
"cancel",
|
|
4150
4190
|
"removeSession",
|
|
4191
|
+
"deleteSession",
|
|
4151
4192
|
"getAgentSessionId"
|
|
4152
4193
|
]);
|
|
4153
4194
|
|
|
@@ -4355,6 +4396,13 @@ class BridgeServer {
|
|
|
4355
4396
|
cwd: requireString(params, "cwd"),
|
|
4356
4397
|
name: requireString(params, "name")
|
|
4357
4398
|
});
|
|
4399
|
+
case "deleteSession":
|
|
4400
|
+
return await this.runtime.deleteSession({
|
|
4401
|
+
agent: requireString(params, "agent"),
|
|
4402
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
4403
|
+
cwd: requireString(params, "cwd"),
|
|
4404
|
+
name: requireString(params, "name")
|
|
4405
|
+
});
|
|
4358
4406
|
case "getAgentSessionId":
|
|
4359
4407
|
return await this.runtime.getAgentSessionId({
|
|
4360
4408
|
agent: requireString(params, "agent"),
|
package/dist/cli.js
CHANGED
|
@@ -141,6 +141,7 @@ ${detail}`,
|
|
|
141
141
|
sessionBlockedByTasks: (alias, count) => `Session "${alias}" has ${count} unfinished task(s). Cancel or wait for them to complete first.`,
|
|
142
142
|
sessionBlockedByTasksHint: "Use /tasks to list tasks, or /task cancel <id> to cancel one.",
|
|
143
143
|
sessionRemoved: (alias) => `Session "${alias}" removed.`,
|
|
144
|
+
sessionArchived: (alias) => `Archived session "${alias}". Send a message to restore it.`,
|
|
144
145
|
sessionRemovedWasActive: "This was the active session. Its chat context has been cleared.",
|
|
145
146
|
sessionRemovedWasActivePromoted: (alias) => `This was the active session. Switched back to the previous session "${alias}".`,
|
|
146
147
|
sessionTransportShared: (transportSession, count) => `Note: backend session "${transportSession}" is still referenced by ${count} other session(s) and was not closed.`,
|
|
@@ -1236,6 +1237,7 @@ ${detail}`,
|
|
|
1236
1237
|
sessionBlockedByTasks: (alias, count) => `会话「${alias}」下还有 ${count} 个未结束的任务,请先取消或等待完成。`,
|
|
1237
1238
|
sessionBlockedByTasksHint: "使用 /tasks 查看任务列表,或 /task cancel <id> 取消任务。",
|
|
1238
1239
|
sessionRemoved: (alias) => `已删除会话「${alias}」。`,
|
|
1240
|
+
sessionArchived: (alias) => `已归档会话「${alias}」。发送消息即可恢复。`,
|
|
1239
1241
|
sessionRemovedWasActive: "该会话是当前活跃会话,已自动清除相关聊天上下文。",
|
|
1240
1242
|
sessionRemovedWasActivePromoted: (alias) => `该会话是当前活跃会话,已切换回上一个会话「${alias}」。`,
|
|
1241
1243
|
sessionTransportShared: (transportSession, count) => `提示:后端会话「${transportSession}」仍被其他 ${count} 个会话引用,未关闭。`,
|
|
@@ -4695,6 +4697,7 @@ function parseConfig(raw, options = {}) {
|
|
|
4695
4697
|
...typeof transport.command === "string" ? { command: transport.command } : {},
|
|
4696
4698
|
...typeof transport.sessionInitTimeoutMs === "number" ? { sessionInitTimeoutMs: transport.sessionInitTimeoutMs } : {},
|
|
4697
4699
|
...typeof transport.permissionPolicy === "string" ? { permissionPolicy: transport.permissionPolicy } : {},
|
|
4700
|
+
...typeof transport.preferLocalAgents === "boolean" ? { preferLocalAgents: transport.preferLocalAgents } : {},
|
|
4698
4701
|
type: transportType,
|
|
4699
4702
|
permissionMode,
|
|
4700
4703
|
nonInteractivePermissions,
|
|
@@ -20399,6 +20402,9 @@ function parseCommand(input) {
|
|
|
20399
20402
|
if (command === "/session" && parts[1] === "rm" && parts[2] && parts.length === 3) {
|
|
20400
20403
|
return { kind: "session.rm", alias: parts[2] };
|
|
20401
20404
|
}
|
|
20405
|
+
if (command === "/session" && parts[1] === "archive" && parts[2] && parts.length === 3) {
|
|
20406
|
+
return { kind: "session.archive", alias: parts[2] };
|
|
20407
|
+
}
|
|
20402
20408
|
if (command === "/ssn") {
|
|
20403
20409
|
if (parts.length === 1) {
|
|
20404
20410
|
return { kind: "session.native.list" };
|
|
@@ -20645,7 +20651,7 @@ function parseCommand(input) {
|
|
|
20645
20651
|
return { kind: "session.shortcut.new", agent: parts[2], ...shortcutTarget };
|
|
20646
20652
|
}
|
|
20647
20653
|
}
|
|
20648
|
-
if (command === "/session" && parts[1] && parts[1] !== "new" && parts[1] !== "attach" && parts[1] !== "reset" && parts[1] !== "rm") {
|
|
20654
|
+
if (command === "/session" && parts[1] && parts[1] !== "new" && parts[1] !== "attach" && parts[1] !== "reset" && parts[1] !== "rm" && parts[1] !== "archive") {
|
|
20649
20655
|
const shortcutTarget = readSessionShortcutTarget(parts, 2);
|
|
20650
20656
|
if (shortcutTarget) {
|
|
20651
20657
|
return { kind: "session.shortcut", agent: parts[1], ...shortcutTarget };
|
|
@@ -21109,6 +21115,7 @@ var init_command_policy = __esm(() => {
|
|
|
21109
21115
|
COMMAND_KIND_TO_LABEL = {
|
|
21110
21116
|
"session.reset": "/clear",
|
|
21111
21117
|
"session.rm": "/session rm",
|
|
21118
|
+
"session.archive": "/session archive",
|
|
21112
21119
|
"session.tail": "/session tail",
|
|
21113
21120
|
"replymode.set": "/replymode",
|
|
21114
21121
|
"replymode.reset": "/replymode reset",
|
|
@@ -22326,9 +22333,9 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
22326
22333
|
}
|
|
22327
22334
|
let transportTeardownWarning;
|
|
22328
22335
|
const shouldTeardownTransport = sharedAliasCount === 0;
|
|
22329
|
-
if (shouldTeardownTransport && context.transport.
|
|
22336
|
+
if (shouldTeardownTransport && context.transport.deleteSession) {
|
|
22330
22337
|
try {
|
|
22331
|
-
await context.transport.
|
|
22338
|
+
await context.transport.deleteSession(session3);
|
|
22332
22339
|
} catch (error2) {
|
|
22333
22340
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
22334
22341
|
transportTeardownWarning = message;
|
|
@@ -22361,7 +22368,19 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
22361
22368
|
return { text: lines.join(`
|
|
22362
22369
|
`) };
|
|
22363
22370
|
}
|
|
22371
|
+
async function handleSessionArchive(context, chatKey, alias, archive) {
|
|
22372
|
+
const internalAlias = await context.sessions.resolveAliasForChat(chatKey, alias);
|
|
22373
|
+
const session3 = await context.sessions.getSession(internalAlias);
|
|
22374
|
+
if (!session3) {
|
|
22375
|
+
return { text: t().session.sessionNotFound(alias) };
|
|
22376
|
+
}
|
|
22377
|
+
await archive(internalAlias);
|
|
22378
|
+
return { text: t().session.sessionArchived(alias) };
|
|
22379
|
+
}
|
|
22364
22380
|
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22381
|
+
if (session3.archived) {
|
|
22382
|
+
await context.sessions.setArchived(session3.alias, false);
|
|
22383
|
+
}
|
|
22365
22384
|
const effectiveReplyMode = resolveEffectiveReplyMode(context.config, chatKey, session3.replyMode);
|
|
22366
22385
|
if (!session3.replyMode)
|
|
22367
22386
|
session3.replyMode = effectiveReplyMode;
|
|
@@ -24795,6 +24814,8 @@ class CommandRouter {
|
|
|
24795
24814
|
return await handleSessionTail(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.lines);
|
|
24796
24815
|
case "session.rm":
|
|
24797
24816
|
return await handleSessionRemove(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias);
|
|
24817
|
+
case "session.archive":
|
|
24818
|
+
return await handleSessionArchive(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias, (internalAlias) => this.archiveSessionWithTransport(internalAlias));
|
|
24798
24819
|
case "groups":
|
|
24799
24820
|
return await handleGroupList(this.createHandlerContext(), chatKey, command.filter);
|
|
24800
24821
|
case "group.new":
|
|
@@ -24943,6 +24964,82 @@ class CommandRouter {
|
|
|
24943
24964
|
await release();
|
|
24944
24965
|
}
|
|
24945
24966
|
}
|
|
24967
|
+
async removeSessionWithTransport(internalAlias) {
|
|
24968
|
+
const session3 = await this.sessions.getSession(internalAlias);
|
|
24969
|
+
if (!session3) {
|
|
24970
|
+
throw new Error(`session "${internalAlias}" does not exist`);
|
|
24971
|
+
}
|
|
24972
|
+
if (this.orchestration) {
|
|
24973
|
+
const blocking = await this.orchestration.listSessionBlockingTasks(session3.transportSession);
|
|
24974
|
+
if (blocking.length > 0) {
|
|
24975
|
+
throw new Error(`session "${internalAlias}" has ${blocking.length} blocking task(s); cancel them before deleting`);
|
|
24976
|
+
}
|
|
24977
|
+
}
|
|
24978
|
+
const sharedAliasCount = this.sessions.countAliasesSharingTransport(session3.transportSession, internalAlias);
|
|
24979
|
+
const { wasActive } = await this.sessions.removeSession(internalAlias);
|
|
24980
|
+
if (this.orchestration) {
|
|
24981
|
+
try {
|
|
24982
|
+
await this.orchestration.purgeSessionReferences(session3.transportSession);
|
|
24983
|
+
} catch (error2) {
|
|
24984
|
+
await this.logger.error("session.orchestration_purge_failed", "failed to purge orchestration references after web remove", {
|
|
24985
|
+
alias: internalAlias,
|
|
24986
|
+
transportSession: session3.transportSession,
|
|
24987
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
24988
|
+
});
|
|
24989
|
+
}
|
|
24990
|
+
}
|
|
24991
|
+
let transportTornDown = false;
|
|
24992
|
+
let transportTeardownWarning;
|
|
24993
|
+
if (sharedAliasCount === 0 && this.transport.deleteSession) {
|
|
24994
|
+
try {
|
|
24995
|
+
await this.transport.deleteSession(session3);
|
|
24996
|
+
transportTornDown = true;
|
|
24997
|
+
} catch (error2) {
|
|
24998
|
+
transportTeardownWarning = error2 instanceof Error ? error2.message : String(error2);
|
|
24999
|
+
await this.logger.error("session.transport_delete_failed", "failed to delete acpx session after logical remove", {
|
|
25000
|
+
alias: internalAlias,
|
|
25001
|
+
transportSession: session3.transportSession,
|
|
25002
|
+
message: transportTeardownWarning
|
|
25003
|
+
});
|
|
25004
|
+
}
|
|
25005
|
+
}
|
|
25006
|
+
return {
|
|
25007
|
+
wasActive,
|
|
25008
|
+
sharedAliasCount,
|
|
25009
|
+
transportTornDown,
|
|
25010
|
+
...transportTeardownWarning ? { transportTeardownWarning } : {}
|
|
25011
|
+
};
|
|
25012
|
+
}
|
|
25013
|
+
async archiveSessionWithTransport(internalAlias) {
|
|
25014
|
+
const session3 = await this.sessions.getSession(internalAlias);
|
|
25015
|
+
if (!session3) {
|
|
25016
|
+
throw new Error(`session "${internalAlias}" does not exist`);
|
|
25017
|
+
}
|
|
25018
|
+
if (this.activeTurns?.isActiveAnywhere(internalAlias)) {
|
|
25019
|
+
throw new Error(`session "${internalAlias}" has a running turn; stop it before archiving`);
|
|
25020
|
+
}
|
|
25021
|
+
const shared = this.sessions.countAliasesSharingTransport(session3.transportSession, internalAlias) > 0;
|
|
25022
|
+
if (!shared) {
|
|
25023
|
+
try {
|
|
25024
|
+
await this.transport.cancel(session3);
|
|
25025
|
+
} catch {}
|
|
25026
|
+
if (this.transport.removeSession) {
|
|
25027
|
+
try {
|
|
25028
|
+
await this.transport.removeSession(session3);
|
|
25029
|
+
} catch (error2) {
|
|
25030
|
+
await this.logger.error("session.archive_close_failed", "failed to close acpx session on archive", {
|
|
25031
|
+
alias: internalAlias,
|
|
25032
|
+
transportSession: session3.transportSession,
|
|
25033
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
25034
|
+
});
|
|
25035
|
+
}
|
|
25036
|
+
}
|
|
25037
|
+
}
|
|
25038
|
+
await this.sessions.setArchived(internalAlias, true);
|
|
25039
|
+
}
|
|
25040
|
+
async unarchiveSession(internalAlias) {
|
|
25041
|
+
await this.sessions.setArchived(internalAlias, false);
|
|
25042
|
+
}
|
|
24946
25043
|
async listNativeSessionsForControl(agent3, workspace3) {
|
|
24947
25044
|
const listAgentSessions = this.transport.listAgentSessions?.bind(this.transport);
|
|
24948
25045
|
if (!listAgentSessions)
|
|
@@ -29659,6 +29756,10 @@ class SessionService {
|
|
|
29659
29756
|
const previousCurrent = prevCtx?.current_session;
|
|
29660
29757
|
const carriedPrevious = previousCurrent && previousCurrent !== internalAlias ? previousCurrent : prevCtx?.previous_session;
|
|
29661
29758
|
session3.last_used_at = new Date().toISOString();
|
|
29759
|
+
if (session3.archived) {
|
|
29760
|
+
delete session3.archived;
|
|
29761
|
+
delete session3.archived_at;
|
|
29762
|
+
}
|
|
29662
29763
|
const nextCtx = { ...prevCtx, current_session: internalAlias };
|
|
29663
29764
|
if (carriedPrevious) {
|
|
29664
29765
|
nextCtx.previous_session = carriedPrevious;
|
|
@@ -29860,6 +29961,22 @@ class SessionService {
|
|
|
29860
29961
|
}
|
|
29861
29962
|
return count;
|
|
29862
29963
|
}
|
|
29964
|
+
async setArchived(alias, archived) {
|
|
29965
|
+
await this.mutate(async () => {
|
|
29966
|
+
const session3 = this.state.sessions[alias];
|
|
29967
|
+
if (!session3) {
|
|
29968
|
+
throw new Error(`session "${alias}" does not exist`);
|
|
29969
|
+
}
|
|
29970
|
+
if (archived) {
|
|
29971
|
+
session3.archived = true;
|
|
29972
|
+
session3.archived_at = new Date(this.now()).toISOString();
|
|
29973
|
+
} else {
|
|
29974
|
+
delete session3.archived;
|
|
29975
|
+
delete session3.archived_at;
|
|
29976
|
+
}
|
|
29977
|
+
await this.persist();
|
|
29978
|
+
});
|
|
29979
|
+
}
|
|
29863
29980
|
async removeSession(alias) {
|
|
29864
29981
|
return await this.mutate(async () => {
|
|
29865
29982
|
const session3 = this.state.sessions[alias];
|
|
@@ -29974,7 +30091,8 @@ class SessionService {
|
|
|
29974
30091
|
modeId: session3.mode_id,
|
|
29975
30092
|
replyMode: session3.reply_mode,
|
|
29976
30093
|
effectiveReplyMode,
|
|
29977
|
-
cwd: workspaceConfig.cwd
|
|
30094
|
+
cwd: workspaceConfig.cwd,
|
|
30095
|
+
archived: session3.archived === true
|
|
29978
30096
|
};
|
|
29979
30097
|
}
|
|
29980
30098
|
async setSessionModel(alias, modelId) {
|
|
@@ -31391,6 +31509,9 @@ ${result.text}` : "" };
|
|
|
31391
31509
|
async removeSession(session3) {
|
|
31392
31510
|
await this.client.request("removeSession", this.toParams(session3));
|
|
31393
31511
|
}
|
|
31512
|
+
async deleteSession(session3) {
|
|
31513
|
+
await this.client.request("deleteSession", this.toParams(session3));
|
|
31514
|
+
}
|
|
31394
31515
|
async getAgentSessionId(session3) {
|
|
31395
31516
|
const result = await this.client.request("getAgentSessionId", this.toParams(session3));
|
|
31396
31517
|
return result.agentSessionId;
|
|
@@ -31959,6 +32080,31 @@ var init_agent_session_list = __esm(() => {
|
|
|
31959
32080
|
init_path();
|
|
31960
32081
|
});
|
|
31961
32082
|
|
|
32083
|
+
// src/transport/acpx-session-files.ts
|
|
32084
|
+
import { readdir as readdir3, unlink as unlink2 } from "node:fs/promises";
|
|
32085
|
+
import { homedir as homedir9 } from "node:os";
|
|
32086
|
+
import { join as join19 } from "node:path";
|
|
32087
|
+
async function deleteAcpxSessionFiles(options) {
|
|
32088
|
+
const dir = options.sessionsDir ?? join19(homedir9(), ".acpx", "sessions");
|
|
32089
|
+
const safeId = encodeURIComponent(options.acpxRecordId);
|
|
32090
|
+
await unlink2(join19(dir, `${safeId}.json`)).catch(() => {
|
|
32091
|
+
return;
|
|
32092
|
+
});
|
|
32093
|
+
let entries;
|
|
32094
|
+
try {
|
|
32095
|
+
entries = await readdir3(dir);
|
|
32096
|
+
} catch {
|
|
32097
|
+
return;
|
|
32098
|
+
}
|
|
32099
|
+
const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
|
|
32100
|
+
for (const name of streamFiles) {
|
|
32101
|
+
await unlink2(join19(dir, name)).catch(() => {
|
|
32102
|
+
return;
|
|
32103
|
+
});
|
|
32104
|
+
}
|
|
32105
|
+
}
|
|
32106
|
+
var init_acpx_session_files = () => {};
|
|
32107
|
+
|
|
31962
32108
|
// src/transport/acpx-cli/acpx-cli-transport.ts
|
|
31963
32109
|
import { createRequire as createRequire5 } from "node:module";
|
|
31964
32110
|
import { spawn as spawn9 } from "node:child_process";
|
|
@@ -32217,6 +32363,16 @@ ${baseText}` : "" };
|
|
|
32217
32363
|
const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
|
|
32218
32364
|
throw new Error(detail);
|
|
32219
32365
|
}
|
|
32366
|
+
async deleteSession(session3) {
|
|
32367
|
+
let acpxRecordId;
|
|
32368
|
+
try {
|
|
32369
|
+
({ acpxRecordId } = await this.readSessionRecord(session3));
|
|
32370
|
+
} catch {
|
|
32371
|
+
return;
|
|
32372
|
+
}
|
|
32373
|
+
await this.removeSession(session3);
|
|
32374
|
+
await deleteAcpxSessionFiles({ acpxRecordId });
|
|
32375
|
+
}
|
|
32220
32376
|
async hasSession(session3) {
|
|
32221
32377
|
const result = await this.runCommand(this.command, this.buildArgs(session3, [
|
|
32222
32378
|
"sessions",
|
|
@@ -32253,7 +32409,7 @@ ${baseText}` : "" };
|
|
|
32253
32409
|
const parsed = JSON.parse(result.stdout);
|
|
32254
32410
|
const acpxRecordId = typeof parsed.acpxRecordId === "string" ? parsed.acpxRecordId : typeof parsed.id === "string" ? parsed.id : undefined;
|
|
32255
32411
|
const agentSessionId = typeof parsed.agentSessionId === "string" ? parsed.agentSessionId : undefined;
|
|
32256
|
-
if (acpxRecordId) {
|
|
32412
|
+
if (acpxRecordId && /^[\w.:-]+$/.test(acpxRecordId) && acpxRecordId.length >= 8) {
|
|
32257
32413
|
return { acpxRecordId, agentSessionId };
|
|
32258
32414
|
}
|
|
32259
32415
|
} catch {
|
|
@@ -32532,6 +32688,7 @@ var init_acpx_cli_transport = __esm(() => {
|
|
|
32532
32688
|
init_terminate_process_tree();
|
|
32533
32689
|
init_acpx_queue_owner_launcher();
|
|
32534
32690
|
init_agent_session_list();
|
|
32691
|
+
init_acpx_session_files();
|
|
32535
32692
|
require4 = createRequire5(import.meta.url);
|
|
32536
32693
|
});
|
|
32537
32694
|
|
|
@@ -33003,8 +33160,8 @@ function createControlEventBus(logger2) {
|
|
|
33003
33160
|
|
|
33004
33161
|
// src/transport/native-session-history.ts
|
|
33005
33162
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
33006
|
-
import { homedir as
|
|
33007
|
-
import { join as
|
|
33163
|
+
import { homedir as homedir10 } from "node:os";
|
|
33164
|
+
import { join as join20 } from "node:path";
|
|
33008
33165
|
function classifyToolKind(name) {
|
|
33009
33166
|
const n = name.toLowerCase();
|
|
33010
33167
|
if (/(^|[^a-z])(read|cat|view|open)([^a-z]|$)/.test(n))
|
|
@@ -33116,8 +33273,8 @@ function mapAcpxMessagesToHistory(raw) {
|
|
|
33116
33273
|
}
|
|
33117
33274
|
async function readNativeSessionHistory(opts) {
|
|
33118
33275
|
try {
|
|
33119
|
-
const dir = opts.sessionsDir ??
|
|
33120
|
-
const indexRaw = await readFile14(
|
|
33276
|
+
const dir = opts.sessionsDir ?? join20(opts.homeDir ?? homedir10(), ".acpx", "sessions");
|
|
33277
|
+
const indexRaw = await readFile14(join20(dir, "index.json"), "utf8").catch(() => null);
|
|
33121
33278
|
if (!indexRaw)
|
|
33122
33279
|
return [];
|
|
33123
33280
|
const index = JSON.parse(indexRaw);
|
|
@@ -33126,7 +33283,7 @@ async function readNativeSessionHistory(opts) {
|
|
|
33126
33283
|
for (const entry of candidates) {
|
|
33127
33284
|
if (!entry.file)
|
|
33128
33285
|
continue;
|
|
33129
|
-
const recRaw = await readFile14(
|
|
33286
|
+
const recRaw = await readFile14(join20(dir, entry.file), "utf8").catch(() => null);
|
|
33130
33287
|
if (!recRaw)
|
|
33131
33288
|
continue;
|
|
33132
33289
|
const record3 = JSON.parse(recRaw);
|
|
@@ -33144,14 +33301,14 @@ var init_native_session_history = () => {};
|
|
|
33144
33301
|
// src/control/workspace-fs.ts
|
|
33145
33302
|
import { execFile } from "node:child_process";
|
|
33146
33303
|
import { promisify } from "node:util";
|
|
33147
|
-
import { homedir as
|
|
33148
|
-
import { readdir as
|
|
33304
|
+
import { homedir as homedir11 } from "node:os";
|
|
33305
|
+
import { readdir as readdir4, realpath, stat as stat3, open as open5 } from "node:fs/promises";
|
|
33149
33306
|
import { isAbsolute as isAbsolute3, relative, resolve as resolve3, sep } from "node:path";
|
|
33150
33307
|
function expandHome2(p) {
|
|
33151
33308
|
if (p === "~")
|
|
33152
|
-
return
|
|
33309
|
+
return homedir11();
|
|
33153
33310
|
if (p.startsWith("~/") || p.startsWith("~" + sep))
|
|
33154
|
-
return resolve3(
|
|
33311
|
+
return resolve3(homedir11(), p.slice(2));
|
|
33155
33312
|
return p;
|
|
33156
33313
|
}
|
|
33157
33314
|
|
|
@@ -33186,7 +33343,7 @@ class WorkspaceFs {
|
|
|
33186
33343
|
}
|
|
33187
33344
|
async listDirectory(workspace3, relPath) {
|
|
33188
33345
|
const { abs, rel } = await this.resolve(workspace3, relPath);
|
|
33189
|
-
const dirents = await
|
|
33346
|
+
const dirents = await readdir4(abs, { withFileTypes: true });
|
|
33190
33347
|
const entries = [];
|
|
33191
33348
|
for (const d of dirents.slice(0, MAX_ENTRIES)) {
|
|
33192
33349
|
if (d.isDirectory()) {
|
|
@@ -33240,7 +33397,7 @@ class WorkspaceFs {
|
|
|
33240
33397
|
const dir = queue.shift();
|
|
33241
33398
|
let dirents;
|
|
33242
33399
|
try {
|
|
33243
|
-
dirents = await
|
|
33400
|
+
dirents = await readdir4(dir, { withFileTypes: true });
|
|
33244
33401
|
} catch {
|
|
33245
33402
|
continue;
|
|
33246
33403
|
}
|
|
@@ -33389,7 +33546,8 @@ class ControlService {
|
|
|
33389
33546
|
agent: session3.agent,
|
|
33390
33547
|
workspace: session3.workspace,
|
|
33391
33548
|
transportSession: session3.transportSession,
|
|
33392
|
-
running: this.deps.activeTurns.isActiveAnywhere(session3.alias)
|
|
33549
|
+
running: this.deps.activeTurns.isActiveAnywhere(session3.alias),
|
|
33550
|
+
archived: session3.archived === true
|
|
33393
33551
|
}));
|
|
33394
33552
|
}
|
|
33395
33553
|
async listNativeSessions(_chatKey, agent3, workspace3) {
|
|
@@ -33419,15 +33577,26 @@ class ControlService {
|
|
|
33419
33577
|
agent: session3.agent,
|
|
33420
33578
|
workspace: session3.workspace,
|
|
33421
33579
|
transportSession: session3.transportSession,
|
|
33422
|
-
running: false
|
|
33580
|
+
running: false,
|
|
33581
|
+
archived: false
|
|
33423
33582
|
};
|
|
33424
33583
|
}
|
|
33425
33584
|
async removeSession(chatKey, alias) {
|
|
33426
33585
|
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33427
|
-
const result = await this.deps.
|
|
33586
|
+
const result = await this.deps.removeSessionWithTransport(internalAlias);
|
|
33428
33587
|
this.deps.events.emit({ type: "sessions-changed" });
|
|
33429
33588
|
return result;
|
|
33430
33589
|
}
|
|
33590
|
+
async archiveSession(chatKey, alias) {
|
|
33591
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33592
|
+
await this.deps.archiveSessionWithTransport(internalAlias);
|
|
33593
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
33594
|
+
}
|
|
33595
|
+
async unarchiveSession(chatKey, alias) {
|
|
33596
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33597
|
+
await this.deps.unarchiveSession(internalAlias);
|
|
33598
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
33599
|
+
}
|
|
33431
33600
|
listAgents() {
|
|
33432
33601
|
return this.deps.agents.list();
|
|
33433
33602
|
}
|
|
@@ -33692,7 +33861,7 @@ var init_control_service = __esm(() => {
|
|
|
33692
33861
|
|
|
33693
33862
|
// src/config/agent-catalog.ts
|
|
33694
33863
|
import { existsSync as existsSync3 } from "node:fs";
|
|
33695
|
-
import { delimiter as delimiter2, join as
|
|
33864
|
+
import { delimiter as delimiter2, join as join21 } from "node:path";
|
|
33696
33865
|
function isBinaryOnPath(binary) {
|
|
33697
33866
|
const path15 = process.env.PATH ?? "";
|
|
33698
33867
|
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
@@ -33701,7 +33870,7 @@ function isBinaryOnPath(binary) {
|
|
|
33701
33870
|
continue;
|
|
33702
33871
|
for (const ext of exts) {
|
|
33703
33872
|
try {
|
|
33704
|
-
if (existsSync3(
|
|
33873
|
+
if (existsSync3(join21(dir, binary + ext)))
|
|
33705
33874
|
return true;
|
|
33706
33875
|
} catch {}
|
|
33707
33876
|
}
|
|
@@ -33741,8 +33910,8 @@ __export(exports_main, {
|
|
|
33741
33910
|
buildApp: () => buildApp
|
|
33742
33911
|
});
|
|
33743
33912
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
33744
|
-
import { homedir as
|
|
33745
|
-
import { dirname as dirname12, join as
|
|
33913
|
+
import { homedir as homedir12 } from "node:os";
|
|
33914
|
+
import { dirname as dirname12, join as join22 } from "node:path";
|
|
33746
33915
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
33747
33916
|
function startProgressHeartbeat(orchestration3, config4, logger2, channel) {
|
|
33748
33917
|
const thresholdSeconds = config4.orchestration.progressHeartbeatSeconds;
|
|
@@ -34240,6 +34409,9 @@ async function buildApp(paths, deps = {}) {
|
|
|
34240
34409
|
createSessionWithTransport: (internalAlias, agent4, workspace3, model) => router3.createSessionWithTransport(internalAlias, agent4, workspace3, model),
|
|
34241
34410
|
listNativeSessions: (agent4, workspace3) => router3.listNativeSessionsForControl(agent4, workspace3),
|
|
34242
34411
|
attachNativeSessionWithTransport: (internalAlias, agent4, workspace3, agentSessionId, nativeMeta) => router3.attachNativeSessionWithTransport(internalAlias, agent4, workspace3, agentSessionId, nativeMeta),
|
|
34412
|
+
removeSessionWithTransport: (internalAlias) => router3.removeSessionWithTransport(internalAlias),
|
|
34413
|
+
archiveSessionWithTransport: (internalAlias) => router3.archiveSessionWithTransport(internalAlias),
|
|
34414
|
+
unarchiveSession: (internalAlias) => router3.unarchiveSession(internalAlias),
|
|
34243
34415
|
activeTurns,
|
|
34244
34416
|
scheduled: scheduledService,
|
|
34245
34417
|
orchestration: orchestration3,
|
|
@@ -34402,8 +34574,8 @@ async function main() {
|
|
|
34402
34574
|
}
|
|
34403
34575
|
}
|
|
34404
34576
|
async function prepareChannelMedia(configPath, config4) {
|
|
34405
|
-
const runtimeDir =
|
|
34406
|
-
const mediaRootDir =
|
|
34577
|
+
const runtimeDir = join22(dirname12(configPath), "runtime");
|
|
34578
|
+
const mediaRootDir = join22(runtimeDir, "media");
|
|
34407
34579
|
const mediaStore = new RuntimeMediaStore({ rootDir: mediaRootDir });
|
|
34408
34580
|
await mediaStore.cleanupExpired().catch((error2) => {
|
|
34409
34581
|
console.error("[xacpx] media cleanup failed:", error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -34412,16 +34584,16 @@ async function prepareChannelMedia(configPath, config4) {
|
|
|
34412
34584
|
return { mediaStore, channelDeps: { mediaStore, allowedMediaRoots } };
|
|
34413
34585
|
}
|
|
34414
34586
|
function resolveRuntimePaths() {
|
|
34415
|
-
const home = process.env.HOME ??
|
|
34587
|
+
const home = process.env.HOME ?? homedir12();
|
|
34416
34588
|
if (!home) {
|
|
34417
34589
|
throw new Error("Unable to resolve the current user home directory");
|
|
34418
34590
|
}
|
|
34419
|
-
const configPath = coreEnv("CONFIG") ??
|
|
34420
|
-
const runtimeDir =
|
|
34591
|
+
const configPath = coreEnv("CONFIG") ?? join22(coreHomeDir(home), "config.json");
|
|
34592
|
+
const runtimeDir = join22(dirname12(configPath), "runtime");
|
|
34421
34593
|
return {
|
|
34422
34594
|
configPath,
|
|
34423
|
-
statePath: coreEnv("STATE") ??
|
|
34424
|
-
perfLogPath:
|
|
34595
|
+
statePath: coreEnv("STATE") ?? join22(coreHomeDir(home), "state.json"),
|
|
34596
|
+
perfLogPath: join22(runtimeDir, "perf.log"),
|
|
34425
34597
|
orchestrationSocketPath: coreEnv("ORCHESTRATION_SOCKET") ?? resolveDaemonOrchestrationSocketPath(runtimeDir)
|
|
34426
34598
|
};
|
|
34427
34599
|
}
|
|
@@ -34433,13 +34605,13 @@ function resolveBridgeEntryPath() {
|
|
|
34433
34605
|
}
|
|
34434
34606
|
function resolveAppLogPath(configPath) {
|
|
34435
34607
|
const rootDir = dirname12(configPath);
|
|
34436
|
-
const runtimeDir =
|
|
34437
|
-
return
|
|
34608
|
+
const runtimeDir = join22(rootDir, "runtime");
|
|
34609
|
+
return join22(runtimeDir, "app.log");
|
|
34438
34610
|
}
|
|
34439
34611
|
function resolvePerfLogPath(configPath) {
|
|
34440
34612
|
const rootDir = dirname12(configPath);
|
|
34441
|
-
const runtimeDir =
|
|
34442
|
-
return
|
|
34613
|
+
const runtimeDir = join22(rootDir, "runtime");
|
|
34614
|
+
return join22(runtimeDir, "perf.log");
|
|
34443
34615
|
}
|
|
34444
34616
|
function resolveOrchestrationSocketPathFromConfigPath(configPath) {
|
|
34445
34617
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
@@ -34677,12 +34849,12 @@ var init_config_check = __esm(async () => {
|
|
|
34677
34849
|
});
|
|
34678
34850
|
|
|
34679
34851
|
// src/doctor/checks/daemon-check.ts
|
|
34680
|
-
import { readdir as
|
|
34852
|
+
import { readdir as readdir5, readFile as readFile15, rm as rm10 } from "node:fs/promises";
|
|
34681
34853
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
34682
|
-
import { homedir as
|
|
34683
|
-
import { join as
|
|
34854
|
+
import { homedir as homedir13 } from "node:os";
|
|
34855
|
+
import { join as join23 } from "node:path";
|
|
34684
34856
|
async function checkDaemon(options = {}) {
|
|
34685
|
-
const home = options.home ?? process.env.HOME ??
|
|
34857
|
+
const home = options.home ?? process.env.HOME ?? homedir13();
|
|
34686
34858
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
34687
34859
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
34688
34860
|
home,
|
|
@@ -34782,7 +34954,7 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
34782
34954
|
if (!fileName.endsWith(CONSUMER_LOCK_SUFFIX)) {
|
|
34783
34955
|
continue;
|
|
34784
34956
|
}
|
|
34785
|
-
const lockPath =
|
|
34957
|
+
const lockPath = join23(runtimeDir, fileName);
|
|
34786
34958
|
const lock2 = await deps.readConsumerLock(lockPath);
|
|
34787
34959
|
if (lock2 && !deps.isProcessRunning(lock2.pid)) {
|
|
34788
34960
|
stalePaths.push(lockPath);
|
|
@@ -34816,7 +34988,7 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
34816
34988
|
}
|
|
34817
34989
|
async function defaultListConsumerLocks(runtimeDir) {
|
|
34818
34990
|
try {
|
|
34819
|
-
return await
|
|
34991
|
+
return await readdir5(runtimeDir);
|
|
34820
34992
|
} catch {
|
|
34821
34993
|
return [];
|
|
34822
34994
|
}
|
|
@@ -34846,11 +35018,11 @@ var init_daemon_check = __esm(() => {
|
|
|
34846
35018
|
});
|
|
34847
35019
|
|
|
34848
35020
|
// src/doctor/checks/logs-check.ts
|
|
34849
|
-
import { stat as stat4, readdir as
|
|
34850
|
-
import { basename as basename3, join as
|
|
34851
|
-
import { homedir as
|
|
35021
|
+
import { stat as stat4, readdir as readdir6 } from "node:fs/promises";
|
|
35022
|
+
import { basename as basename3, join as join24 } from "node:path";
|
|
35023
|
+
import { homedir as homedir14 } from "node:os";
|
|
34852
35024
|
async function checkLogs(options = {}) {
|
|
34853
|
-
const home = options.home ?? process.env.HOME ??
|
|
35025
|
+
const home = options.home ?? process.env.HOME ?? homedir14();
|
|
34854
35026
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
34855
35027
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
34856
35028
|
home,
|
|
@@ -34882,7 +35054,7 @@ async function checkLogs(options = {}) {
|
|
|
34882
35054
|
const matched = entries.filter((entry) => isTrackedLogName(entry, tracked));
|
|
34883
35055
|
const files = [];
|
|
34884
35056
|
for (const name of matched) {
|
|
34885
|
-
const path15 =
|
|
35057
|
+
const path15 = join24(paths.runtimeDir, name);
|
|
34886
35058
|
try {
|
|
34887
35059
|
const fileStat = await probe.stat(path15);
|
|
34888
35060
|
if (fileStat.isDirectory()) {
|
|
@@ -34963,7 +35135,7 @@ function formatBytes(bytes) {
|
|
|
34963
35135
|
function createLogsFsProbe() {
|
|
34964
35136
|
return {
|
|
34965
35137
|
stat: async (path15) => await stat4(path15),
|
|
34966
|
-
readdir: async (path15) => await
|
|
35138
|
+
readdir: async (path15) => await readdir6(path15)
|
|
34967
35139
|
};
|
|
34968
35140
|
}
|
|
34969
35141
|
function formatError6(error2) {
|
|
@@ -35032,9 +35204,9 @@ var init_orchestration_health = __esm(() => {
|
|
|
35032
35204
|
});
|
|
35033
35205
|
|
|
35034
35206
|
// src/doctor/checks/orchestration-socket-check.ts
|
|
35035
|
-
import { homedir as
|
|
35207
|
+
import { homedir as homedir15 } from "node:os";
|
|
35036
35208
|
async function checkOrchestrationSocket(options = {}) {
|
|
35037
|
-
const home = options.home ?? process.env.HOME ??
|
|
35209
|
+
const home = options.home ?? process.env.HOME ?? homedir15();
|
|
35038
35210
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
35039
35211
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
35040
35212
|
home,
|
|
@@ -35209,9 +35381,9 @@ var init_plugin_check = __esm(async () => {
|
|
|
35209
35381
|
import { constants } from "node:fs";
|
|
35210
35382
|
import { access as access4, stat as stat5 } from "node:fs/promises";
|
|
35211
35383
|
import { dirname as dirname13 } from "node:path";
|
|
35212
|
-
import { homedir as
|
|
35384
|
+
import { homedir as homedir16 } from "node:os";
|
|
35213
35385
|
async function checkRuntime(options = {}) {
|
|
35214
|
-
const home = options.home ?? process.env.HOME ??
|
|
35386
|
+
const home = options.home ?? process.env.HOME ?? homedir16();
|
|
35215
35387
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
35216
35388
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
35217
35389
|
home,
|
|
@@ -35844,10 +36016,10 @@ var init_render_doctor = __esm(() => {
|
|
|
35844
36016
|
});
|
|
35845
36017
|
|
|
35846
36018
|
// src/doctor/doctor.ts
|
|
35847
|
-
import { homedir as
|
|
35848
|
-
import { join as
|
|
36019
|
+
import { homedir as homedir17 } from "node:os";
|
|
36020
|
+
import { join as join25 } from "node:path";
|
|
35849
36021
|
async function runDoctor(options = {}, deps = {}) {
|
|
35850
|
-
const home = deps.home ?? process.env.HOME ??
|
|
36022
|
+
const home = deps.home ?? process.env.HOME ?? homedir17();
|
|
35851
36023
|
const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
|
|
35852
36024
|
const sharedLoadConfig = createSharedLoadConfig(runtimePaths, deps.loadConfig ?? loadConfig);
|
|
35853
36025
|
const runners = [
|
|
@@ -36000,8 +36172,8 @@ function resolveDoctorRuntimePaths(home, resolver) {
|
|
|
36000
36172
|
return resolveRuntimePaths();
|
|
36001
36173
|
}
|
|
36002
36174
|
return {
|
|
36003
|
-
configPath:
|
|
36004
|
-
statePath:
|
|
36175
|
+
configPath: join25(coreHomeDir(home), "config.json"),
|
|
36176
|
+
statePath: join25(coreHomeDir(home), "state.json")
|
|
36005
36177
|
};
|
|
36006
36178
|
}
|
|
36007
36179
|
function depsUseExplicitRuntimeOverrides() {
|
|
@@ -36184,8 +36356,8 @@ var init_doctor2 = __esm(async () => {
|
|
|
36184
36356
|
// src/cli.ts
|
|
36185
36357
|
init_core_home();
|
|
36186
36358
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
36187
|
-
import { homedir as
|
|
36188
|
-
import { dirname as dirname14, join as
|
|
36359
|
+
import { homedir as homedir18 } from "node:os";
|
|
36360
|
+
import { dirname as dirname14, join as join26, sep as sep2 } from "node:path";
|
|
36189
36361
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
36190
36362
|
|
|
36191
36363
|
// src/runtime/migrate-core-home.ts
|
|
@@ -52439,7 +52611,7 @@ async function createCliScheduledTaskService() {
|
|
|
52439
52611
|
return new ScheduledTaskService(state, stateStore);
|
|
52440
52612
|
}
|
|
52441
52613
|
function resolveConfigPathForCurrentEnv() {
|
|
52442
|
-
return coreEnv("CONFIG") ??
|
|
52614
|
+
return coreEnv("CONFIG") ?? join26(coreHomeDir(requireHome2()), "config.json");
|
|
52443
52615
|
}
|
|
52444
52616
|
function resolveDaemonPathsForCurrentConfig() {
|
|
52445
52617
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
@@ -52790,7 +52962,7 @@ function decodeFirstRunOnboarding(raw) {
|
|
|
52790
52962
|
return null;
|
|
52791
52963
|
}
|
|
52792
52964
|
function requireHome2() {
|
|
52793
|
-
const home = process.env.HOME ??
|
|
52965
|
+
const home = process.env.HOME ?? homedir18();
|
|
52794
52966
|
if (!home) {
|
|
52795
52967
|
throw new Error("Unable to resolve the current user home directory");
|
|
52796
52968
|
}
|
|
@@ -52814,7 +52986,7 @@ function safeDaemonLogPaths() {
|
|
|
52814
52986
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
52815
52987
|
const paths = resolveDaemonPathsForCurrentConfig();
|
|
52816
52988
|
return {
|
|
52817
|
-
appLog:
|
|
52989
|
+
appLog: join26(dirname14(configPath), "runtime", "app.log"),
|
|
52818
52990
|
stderrLog: paths.stderrLog
|
|
52819
52991
|
};
|
|
52820
52992
|
} catch {
|
|
@@ -38,6 +38,7 @@ export declare function handleCancel(context: SessionHandlerContext, chatKey: st
|
|
|
38
38
|
export declare function handleSessionReset(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
39
39
|
export declare function handleSessionTail(context: SessionHandlerContext, chatKey: string, lines?: number): Promise<RouterResponse>;
|
|
40
40
|
export declare function handleSessionRemove(context: SessionHandlerContext, chatKey: string, alias: string): Promise<RouterResponse>;
|
|
41
|
+
export declare function handleSessionArchive(context: SessionHandlerContext, chatKey: string, alias: string, archive: (internalAlias: string) => Promise<void>): Promise<RouterResponse>;
|
|
41
42
|
export declare function handlePromptWithSession(context: SessionHandlerContext, session: ResolvedSession, chatKey: string, text: string, reply?: (text: string) => Promise<void>, replyContextToken?: string, accountId?: string, media?: PromptMediaInput, abortSignal?: AbortSignal, onToolEvent?: (event: ToolUseEvent) => void | Promise<void>, onThought?: (chunk: string) => void | Promise<void>, perfSpan?: PerfSpan, metadata?: ChatRequestMetadata, onPlan?: (entries: PlanEntry[]) => void | Promise<void>, onUsage?: (usage: {
|
|
42
43
|
used: number;
|
|
43
44
|
size: number;
|
|
@@ -15,6 +15,7 @@ export interface ControlSessionInfo {
|
|
|
15
15
|
workspace: string;
|
|
16
16
|
transportSession: string;
|
|
17
17
|
running: boolean;
|
|
18
|
+
archived: boolean;
|
|
18
19
|
}
|
|
19
20
|
export interface ControlAgentInfo {
|
|
20
21
|
name: string;
|
|
@@ -37,6 +38,11 @@ export interface ControlServiceDeps {
|
|
|
37
38
|
sessions: Pick<SessionService, "listAllResolvedSessions" | "removeSession" | "useSession" | "resolveAliasForChat" | "getSession" | "setSessionModel">;
|
|
38
39
|
transport: Pick<SessionTransport, "setModel" | "getSessionModel">;
|
|
39
40
|
createSessionWithTransport: (internalAlias: string, agent: string, workspace: string, model?: string) => Promise<ResolvedSession>;
|
|
41
|
+
removeSessionWithTransport: (internalAlias: string) => Promise<{
|
|
42
|
+
wasActive: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
archiveSessionWithTransport: (internalAlias: string) => Promise<void>;
|
|
45
|
+
unarchiveSession: (internalAlias: string) => Promise<void>;
|
|
40
46
|
listNativeSessions: (agent: string, workspace: string) => Promise<AgentSession[]>;
|
|
41
47
|
attachNativeSessionWithTransport: (internalAlias: string, agent: string, workspace: string, agentSessionId: string, nativeMeta?: {
|
|
42
48
|
title?: string | null;
|
|
@@ -121,6 +127,8 @@ export declare class ControlService {
|
|
|
121
127
|
removeSession(chatKey: string, alias: string): Promise<{
|
|
122
128
|
wasActive: boolean;
|
|
123
129
|
}>;
|
|
130
|
+
archiveSession(chatKey: string, alias: string): Promise<void>;
|
|
131
|
+
unarchiveSession(chatKey: string, alias: string): Promise<void>;
|
|
124
132
|
listAgents(): ControlAgentInfo[];
|
|
125
133
|
listWorkspaces(): ControlWorkspaceInfo[];
|
|
126
134
|
createWorkspace(name: string, cwd: string, description?: string): Promise<ControlWorkspaceInfo>;
|
package/dist/i18n/types.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ export interface SessionMessages {
|
|
|
50
50
|
sessionBlockedByTasks: (alias: string, count: number) => string;
|
|
51
51
|
sessionBlockedByTasksHint: string;
|
|
52
52
|
sessionRemoved: (alias: string) => string;
|
|
53
|
+
sessionArchived: (alias: string) => string;
|
|
53
54
|
sessionRemovedWasActive: string;
|
|
54
55
|
sessionRemovedWasActivePromoted: (alias: string) => string;
|
|
55
56
|
sessionTransportShared: (transportSession: string, count: number) => string;
|
package/dist/plugin-api.js
CHANGED
|
@@ -116,6 +116,7 @@ ${detail}`,
|
|
|
116
116
|
sessionBlockedByTasks: (alias, count) => `Session "${alias}" has ${count} unfinished task(s). Cancel or wait for them to complete first.`,
|
|
117
117
|
sessionBlockedByTasksHint: "Use /tasks to list tasks, or /task cancel <id> to cancel one.",
|
|
118
118
|
sessionRemoved: (alias) => `Session "${alias}" removed.`,
|
|
119
|
+
sessionArchived: (alias) => `Archived session "${alias}". Send a message to restore it.`,
|
|
119
120
|
sessionRemovedWasActive: "This was the active session. Its chat context has been cleared.",
|
|
120
121
|
sessionRemovedWasActivePromoted: (alias) => `This was the active session. Switched back to the previous session "${alias}".`,
|
|
121
122
|
sessionTransportShared: (transportSession, count) => `Note: backend session "${transportSession}" is still referenced by ${count} other session(s) and was not closed.`,
|
|
@@ -1211,6 +1212,7 @@ ${detail}`,
|
|
|
1211
1212
|
sessionBlockedByTasks: (alias, count) => `会话「${alias}」下还有 ${count} 个未结束的任务,请先取消或等待完成。`,
|
|
1212
1213
|
sessionBlockedByTasksHint: "使用 /tasks 查看任务列表,或 /task cancel <id> 取消任务。",
|
|
1213
1214
|
sessionRemoved: (alias) => `已删除会话「${alias}」。`,
|
|
1215
|
+
sessionArchived: (alias) => `已归档会话「${alias}」。发送消息即可恢复。`,
|
|
1214
1216
|
sessionRemovedWasActive: "该会话是当前活跃会话,已自动清除相关聊天上下文。",
|
|
1215
1217
|
sessionRemovedWasActivePromoted: (alias) => `该会话是当前活跃会话,已切换回上一个会话「${alias}」。`,
|
|
1216
1218
|
sessionTransportShared: (transportSession, count) => `提示:后端会话「${transportSession}」仍被其他 ${count} 个会话引用,未关闭。`,
|
|
@@ -106,6 +106,7 @@ export declare class SessionService {
|
|
|
106
106
|
getCurrentSession(chatKey: string): Promise<ResolvedSession | null>;
|
|
107
107
|
listSessions(chatKey: string): Promise<SessionListItem[]>;
|
|
108
108
|
countAliasesSharingTransport(transportSession: string, excludeAlias?: string): number;
|
|
109
|
+
setArchived(alias: string, archived: boolean): Promise<void>;
|
|
109
110
|
removeSession(alias: string): Promise<{
|
|
110
111
|
wasActive: boolean;
|
|
111
112
|
}>;
|
package/dist/state/types.d.ts
CHANGED
|
@@ -30,6 +30,10 @@ export interface LogicalSession {
|
|
|
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
32
|
reply_mode?: "stream" | "final" | "verbose";
|
|
33
|
+
/** True when the user archived this session: process closed, row greyed + sunk.
|
|
34
|
+
* Cleared on the next useSession (restore-on-message). */
|
|
35
|
+
archived?: boolean;
|
|
36
|
+
archived_at?: string;
|
|
33
37
|
created_at: string;
|
|
34
38
|
last_used_at: string;
|
|
35
39
|
}
|
|
@@ -55,6 +55,11 @@ export interface ResolvedSession {
|
|
|
55
55
|
* persisted state by alias) does not apply.
|
|
56
56
|
*/
|
|
57
57
|
transient?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* True when the user archived this session (process closed, greyed out in the
|
|
60
|
+
* web dashboard). Surfaced to the control/web path via ControlSessionInfo.
|
|
61
|
+
*/
|
|
62
|
+
archived?: boolean;
|
|
58
63
|
}
|
|
59
64
|
export interface AgentSession {
|
|
60
65
|
sessionId: string;
|
|
@@ -160,6 +165,13 @@ export interface SessionTransport {
|
|
|
160
165
|
listAgentSessions?(query: AgentSessionListQuery): Promise<AgentSessionListResult | undefined>;
|
|
161
166
|
resumeAgentSession?(session: ResolvedSession, agentSessionId: string): Promise<void>;
|
|
162
167
|
removeSession?(session: ResolvedSession): Promise<void>;
|
|
168
|
+
/**
|
|
169
|
+
* Hard-delete the transport session AND its on-disk history: close the acpx
|
|
170
|
+
* process, then delete acpx's record files. Distinct from removeSession (=
|
|
171
|
+
* `acpx sessions close`, which keeps history for resume). Optional: transports
|
|
172
|
+
* that can't delete omit it. A missing acpx session is a no-op (idempotent).
|
|
173
|
+
*/
|
|
174
|
+
deleteSession?(session: ResolvedSession): Promise<void>;
|
|
163
175
|
/**
|
|
164
176
|
* Read the underlying agent-native session id for an existing transport
|
|
165
177
|
* session. Used by `/clear` to keep a native session native: the fresh
|