weacpx 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/dist/bridge/bridge-main.js +122 -2
- package/dist/channels/channel-scope.d.ts +8 -0
- package/dist/channels/types.d.ts +6 -0
- package/dist/channels/weixin-channel.d.ts +1 -0
- package/dist/cli.js +882 -50
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9759,6 +9759,7 @@ function createEmptyState() {
|
|
|
9759
9759
|
return {
|
|
9760
9760
|
sessions: {},
|
|
9761
9761
|
chat_contexts: {},
|
|
9762
|
+
native_session_lists: {},
|
|
9762
9763
|
orchestration: createEmptyOrchestrationState(),
|
|
9763
9764
|
scheduled_tasks: {}
|
|
9764
9765
|
};
|
|
@@ -9977,11 +9978,14 @@ function parseOrchestrationState(raw, path3) {
|
|
|
9977
9978
|
function isReplyMode2(value) {
|
|
9978
9979
|
return value === "stream" || value === "final" || value === "verbose";
|
|
9979
9980
|
}
|
|
9981
|
+
function isSessionSource(value) {
|
|
9982
|
+
return value === undefined || value === "weacpx" || value === "agent-side";
|
|
9983
|
+
}
|
|
9980
9984
|
function isSessionRecord(value) {
|
|
9981
9985
|
if (!isRecord2(value)) {
|
|
9982
9986
|
return false;
|
|
9983
9987
|
}
|
|
9984
|
-
return isString(value.alias) && isString(value.agent) && isString(value.workspace) && isString(value.transport_session) && isOptionalString(value.transport_agent_command) && isOptionalString(value.mode_id) && (value.reply_mode === undefined || isReplyMode2(value.reply_mode)) && isString(value.created_at) && isString(value.last_used_at);
|
|
9988
|
+
return isString(value.alias) && isString(value.agent) && isString(value.workspace) && isString(value.transport_session) && isSessionSource(value.source) && isOptionalString(value.agent_session_id) && isOptionalString(value.agent_session_title) && isOptionalString(value.agent_session_updated_at) && isOptionalString(value.attached_at) && isOptionalString(value.transport_agent_command) && isOptionalString(value.mode_id) && (value.reply_mode === undefined || isReplyMode2(value.reply_mode)) && isString(value.created_at) && isString(value.last_used_at);
|
|
9985
9989
|
}
|
|
9986
9990
|
function parseSessions(raw, path3) {
|
|
9987
9991
|
const sessions = {};
|
|
@@ -10006,6 +10010,30 @@ function parseChatContexts(raw, path3) {
|
|
|
10006
10010
|
}
|
|
10007
10011
|
return chatContexts;
|
|
10008
10012
|
}
|
|
10013
|
+
function isNativeSessionCacheEntry(value) {
|
|
10014
|
+
if (!isRecord2(value)) {
|
|
10015
|
+
return false;
|
|
10016
|
+
}
|
|
10017
|
+
return isString(value.session_id) && isOptionalString(value.cwd) && (value.title === undefined || value.title === null || isString(value.title)) && isOptionalString(value.updated_at);
|
|
10018
|
+
}
|
|
10019
|
+
function isNativeSessionListCacheRecord(value) {
|
|
10020
|
+
if (!isRecord2(value)) {
|
|
10021
|
+
return false;
|
|
10022
|
+
}
|
|
10023
|
+
return isString(value.created_at) && isString(value.agent) && isOptionalString(value.workspace) && isString(value.cwd) && Array.isArray(value.sessions) && value.sessions.every(isNativeSessionCacheEntry) && (value.next_cursor === undefined || value.next_cursor === null || isString(value.next_cursor));
|
|
10024
|
+
}
|
|
10025
|
+
function parseNativeSessionLists(raw) {
|
|
10026
|
+
if (!isRecord2(raw)) {
|
|
10027
|
+
return {};
|
|
10028
|
+
}
|
|
10029
|
+
const nativeSessionLists = {};
|
|
10030
|
+
for (const [chatKey, value] of Object.entries(raw)) {
|
|
10031
|
+
if (isNativeSessionListCacheRecord(value)) {
|
|
10032
|
+
nativeSessionLists[chatKey] = value;
|
|
10033
|
+
}
|
|
10034
|
+
}
|
|
10035
|
+
return nativeSessionLists;
|
|
10036
|
+
}
|
|
10009
10037
|
function isScheduledTaskStatus(value) {
|
|
10010
10038
|
return value === "pending" || value === "triggering" || value === "executed" || value === "cancelled" || value === "missed" || value === "failed";
|
|
10011
10039
|
}
|
|
@@ -10050,6 +10078,7 @@ function parseState(raw, path3) {
|
|
|
10050
10078
|
return {
|
|
10051
10079
|
sessions: parsedSessions,
|
|
10052
10080
|
chat_contexts: parseChatContexts(chatContexts, path3),
|
|
10081
|
+
native_session_lists: parseNativeSessionLists(raw.native_session_lists),
|
|
10053
10082
|
orchestration,
|
|
10054
10083
|
scheduled_tasks: parseScheduledTasks(raw.scheduled_tasks, path3)
|
|
10055
10084
|
};
|
|
@@ -10167,6 +10196,13 @@ function resolveSessionAliasForInput(channelId, displayAlias, existingAliases) {
|
|
|
10167
10196
|
}
|
|
10168
10197
|
return scopedAlias;
|
|
10169
10198
|
}
|
|
10199
|
+
function scopeDisplayAliasToInternal(channelId, displayAlias) {
|
|
10200
|
+
const normalized = displayAlias.trim();
|
|
10201
|
+
if (normalized.length === 0) {
|
|
10202
|
+
throw new Error("display session alias must be non-empty");
|
|
10203
|
+
}
|
|
10204
|
+
return channelId === "weixin" ? normalized : toInternalSessionAlias(channelId, normalized);
|
|
10205
|
+
}
|
|
10170
10206
|
function buildDefaultTransportSession(channelId, displayAlias) {
|
|
10171
10207
|
const normalized = displayAlias.trim();
|
|
10172
10208
|
if (normalized.length === 0) {
|
|
@@ -14953,6 +14989,12 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
14953
14989
|
text: requestText,
|
|
14954
14990
|
...media.length > 0 ? { media } : {},
|
|
14955
14991
|
replyContextToken: contextToken,
|
|
14992
|
+
metadata: {
|
|
14993
|
+
channel: "weixin",
|
|
14994
|
+
chatType: full.group_id ? "group" : "direct",
|
|
14995
|
+
...full.from_user_id ? { senderId: full.from_user_id } : {},
|
|
14996
|
+
...full.group_id ? { groupId: full.group_id } : {}
|
|
14997
|
+
},
|
|
14956
14998
|
perfSpan
|
|
14957
14999
|
};
|
|
14958
15000
|
try {
|
|
@@ -16104,6 +16146,7 @@ var init_consumer_lock = __esm(() => {
|
|
|
16104
16146
|
// src/channels/weixin-channel.ts
|
|
16105
16147
|
class WeixinChannel {
|
|
16106
16148
|
id = "weixin";
|
|
16149
|
+
nativeSessionListFormat = "cards";
|
|
16107
16150
|
agent = null;
|
|
16108
16151
|
quota = null;
|
|
16109
16152
|
logger = null;
|
|
@@ -16911,6 +16954,7 @@ var init_command_list = __esm(() => {
|
|
|
16911
16954
|
"/pm",
|
|
16912
16955
|
"/session",
|
|
16913
16956
|
"/ss",
|
|
16957
|
+
"/ssn",
|
|
16914
16958
|
"/workspace",
|
|
16915
16959
|
"/ws",
|
|
16916
16960
|
"/use",
|
|
@@ -16993,6 +17037,51 @@ function parseCommand(input) {
|
|
|
16993
17037
|
if (command === "/session" && parts[1] === "rm" && parts[2] && parts.length === 3) {
|
|
16994
17038
|
return { kind: "session.rm", alias: parts[2] };
|
|
16995
17039
|
}
|
|
17040
|
+
if (command === "/ssn") {
|
|
17041
|
+
if (parts.length === 1) {
|
|
17042
|
+
return { kind: "session.native.list" };
|
|
17043
|
+
}
|
|
17044
|
+
const identifier = parts[1] ?? "";
|
|
17045
|
+
if (/^\d+$/.test(identifier)) {
|
|
17046
|
+
const selected = readNativeAttachCommand(parts, 1);
|
|
17047
|
+
if (!selected) {
|
|
17048
|
+
return { kind: "invalid", text: trimmed, recognizedCommand: "/ssn" };
|
|
17049
|
+
}
|
|
17050
|
+
return selected.alias ? { kind: "session.native.select", identifier, alias: selected.alias } : { kind: "session.native.select", identifier };
|
|
17051
|
+
}
|
|
17052
|
+
if (parts[1] === "attach") {
|
|
17053
|
+
if (!parts[2]) {
|
|
17054
|
+
return { kind: "invalid", text: trimmed, recognizedCommand: "/ssn" };
|
|
17055
|
+
}
|
|
17056
|
+
const attached = readNativeAttachCommand(parts, 2);
|
|
17057
|
+
if (attached) {
|
|
17058
|
+
return attached;
|
|
17059
|
+
}
|
|
17060
|
+
return { kind: "invalid", text: trimmed, recognizedCommand: "/ssn" };
|
|
17061
|
+
}
|
|
17062
|
+
const nativeList = readNativeListCommand(parts, 1);
|
|
17063
|
+
if (nativeList) {
|
|
17064
|
+
return nativeList;
|
|
17065
|
+
}
|
|
17066
|
+
return { kind: "invalid", text: trimmed, recognizedCommand: "/ssn" };
|
|
17067
|
+
}
|
|
17068
|
+
if (command === "/session" && parts[1] === "native") {
|
|
17069
|
+
const nativeList = readNativeListCommand(parts, 2);
|
|
17070
|
+
if (nativeList) {
|
|
17071
|
+
return nativeList;
|
|
17072
|
+
}
|
|
17073
|
+
return { kind: "invalid", text: trimmed, recognizedCommand: "/session" };
|
|
17074
|
+
}
|
|
17075
|
+
if (command === "/session" && parts[1] === "attach" && parts[2] === "native") {
|
|
17076
|
+
if (!parts[3]) {
|
|
17077
|
+
return { kind: "invalid", text: trimmed, recognizedCommand: "/session" };
|
|
17078
|
+
}
|
|
17079
|
+
const attached = readNativeAttachCommand(parts, 3);
|
|
17080
|
+
if (attached) {
|
|
17081
|
+
return attached;
|
|
17082
|
+
}
|
|
17083
|
+
return { kind: "invalid", text: trimmed, recognizedCommand: "/session" };
|
|
17084
|
+
}
|
|
16996
17085
|
if (command === "/group" && parts[1] === "new" && parts.length > 2) {
|
|
16997
17086
|
const title = parts.slice(2).join(" ");
|
|
16998
17087
|
if (title.trim().length > 0) {
|
|
@@ -17258,6 +17347,97 @@ function readSessionShortcutTarget(parts, startIndex) {
|
|
|
17258
17347
|
}
|
|
17259
17348
|
return null;
|
|
17260
17349
|
}
|
|
17350
|
+
function readNativeListCommand(parts, startIndex) {
|
|
17351
|
+
let agent = "";
|
|
17352
|
+
let cwd = "";
|
|
17353
|
+
let workspace = "";
|
|
17354
|
+
let all = false;
|
|
17355
|
+
let cursor = "";
|
|
17356
|
+
let invalid = false;
|
|
17357
|
+
if (startIndex < parts.length && !parts[startIndex]?.startsWith("-")) {
|
|
17358
|
+
agent = parts[startIndex] ?? "";
|
|
17359
|
+
startIndex += 1;
|
|
17360
|
+
}
|
|
17361
|
+
for (let index = startIndex;index < parts.length; index += 1) {
|
|
17362
|
+
const part = parts[index];
|
|
17363
|
+
if (part === "--all") {
|
|
17364
|
+
all = true;
|
|
17365
|
+
continue;
|
|
17366
|
+
}
|
|
17367
|
+
if (part === "--cursor") {
|
|
17368
|
+
const value = parts[index + 1] ?? "";
|
|
17369
|
+
if (index + 1 >= parts.length || value.startsWith("-")) {
|
|
17370
|
+
invalid = true;
|
|
17371
|
+
break;
|
|
17372
|
+
}
|
|
17373
|
+
cursor = value;
|
|
17374
|
+
index += 1;
|
|
17375
|
+
continue;
|
|
17376
|
+
}
|
|
17377
|
+
if (part === "--cwd" || part === "-d") {
|
|
17378
|
+
const value = parts[index + 1] ?? "";
|
|
17379
|
+
if (index + 1 >= parts.length || value.startsWith("-") || workspace) {
|
|
17380
|
+
invalid = true;
|
|
17381
|
+
break;
|
|
17382
|
+
}
|
|
17383
|
+
cwd = value;
|
|
17384
|
+
index += 1;
|
|
17385
|
+
continue;
|
|
17386
|
+
}
|
|
17387
|
+
if (part === "--ws" || part === "-ws") {
|
|
17388
|
+
const value = parts[index + 1] ?? "";
|
|
17389
|
+
if (index + 1 >= parts.length || value.startsWith("-") || cwd) {
|
|
17390
|
+
invalid = true;
|
|
17391
|
+
break;
|
|
17392
|
+
}
|
|
17393
|
+
workspace = value;
|
|
17394
|
+
index += 1;
|
|
17395
|
+
continue;
|
|
17396
|
+
}
|
|
17397
|
+
invalid = true;
|
|
17398
|
+
break;
|
|
17399
|
+
}
|
|
17400
|
+
if (invalid) {
|
|
17401
|
+
return null;
|
|
17402
|
+
}
|
|
17403
|
+
const result = {
|
|
17404
|
+
kind: "session.native.list"
|
|
17405
|
+
};
|
|
17406
|
+
if (agent.trim().length > 0)
|
|
17407
|
+
result.agent = agent;
|
|
17408
|
+
if (cwd.trim().length > 0)
|
|
17409
|
+
result.cwd = cwd;
|
|
17410
|
+
if (workspace.trim().length > 0)
|
|
17411
|
+
result.workspace = workspace;
|
|
17412
|
+
if (all)
|
|
17413
|
+
result.all = true;
|
|
17414
|
+
if (cursor.trim().length > 0)
|
|
17415
|
+
result.cursor = cursor;
|
|
17416
|
+
return Object.keys(result).length > 1 ? result : { kind: "session.native.list" };
|
|
17417
|
+
}
|
|
17418
|
+
function readNativeAttachCommand(parts, identifierIndex) {
|
|
17419
|
+
const identifier = parts[identifierIndex] ?? "";
|
|
17420
|
+
let alias = "";
|
|
17421
|
+
for (let index = identifierIndex + 1;index < parts.length; index += 1) {
|
|
17422
|
+
if (parts[index] === "-a" || parts[index] === "--alias") {
|
|
17423
|
+
const value = parts[index + 1] ?? "";
|
|
17424
|
+
if (index + 1 >= parts.length || value.startsWith("-")) {
|
|
17425
|
+
return null;
|
|
17426
|
+
}
|
|
17427
|
+
alias = value;
|
|
17428
|
+
index += 1;
|
|
17429
|
+
continue;
|
|
17430
|
+
}
|
|
17431
|
+
return null;
|
|
17432
|
+
}
|
|
17433
|
+
if (identifier.trim().length === 0 || identifier.startsWith("-")) {
|
|
17434
|
+
return null;
|
|
17435
|
+
}
|
|
17436
|
+
if (alias.trim().length > 0) {
|
|
17437
|
+
return { kind: "session.native.attach", identifier, alias };
|
|
17438
|
+
}
|
|
17439
|
+
return { kind: "session.native.attach", identifier };
|
|
17440
|
+
}
|
|
17261
17441
|
function normalizeCommand(command) {
|
|
17262
17442
|
if (command === "/ss")
|
|
17263
17443
|
return "/session";
|
|
@@ -17495,6 +17675,9 @@ var init_command_policy = __esm(() => {
|
|
|
17495
17675
|
"session.shortcut": "/session",
|
|
17496
17676
|
"session.shortcut.new": "/session",
|
|
17497
17677
|
"session.attach": "/session attach",
|
|
17678
|
+
"session.native.list": "/ssn",
|
|
17679
|
+
"session.native.select": "/ssn",
|
|
17680
|
+
"session.native.attach": "/ssn attach",
|
|
17498
17681
|
"later.create": "/later",
|
|
17499
17682
|
"later.list": "/later list",
|
|
17500
17683
|
"later.cancel": "/later cancel"
|
|
@@ -18155,7 +18338,7 @@ async function handleSessions(context, chatKey) {
|
|
|
18155
18338
|
}
|
|
18156
18339
|
async function handleSessionNew(context, chatKey, alias, agent, workspace) {
|
|
18157
18340
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
18158
|
-
const internalAlias =
|
|
18341
|
+
const internalAlias = scopeDisplayAliasToInternal(channelId, alias);
|
|
18159
18342
|
const session = context.lifecycle.resolveSession(internalAlias, agent, workspace, `${workspace}:${internalAlias}`);
|
|
18160
18343
|
const releaseTransportReservation = await context.lifecycle.reserveTransportSession(session.transportSession);
|
|
18161
18344
|
try {
|
|
@@ -18186,7 +18369,7 @@ async function handleSessionShortcut(context, chatKey, agent, target, createNew)
|
|
|
18186
18369
|
}
|
|
18187
18370
|
async function handleSessionAttach(context, chatKey, alias, agent, workspace, transportSession) {
|
|
18188
18371
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
18189
|
-
const internalAlias =
|
|
18372
|
+
const internalAlias = scopeDisplayAliasToInternal(channelId, alias);
|
|
18190
18373
|
const attached = context.lifecycle.resolveSession(internalAlias, agent, workspace, transportSession);
|
|
18191
18374
|
const releaseTransportReservation = await context.lifecycle.reserveTransportSession(attached.transportSession);
|
|
18192
18375
|
try {
|
|
@@ -18553,7 +18736,7 @@ async function markCoordinatorResultsInjectionFailed(context, taskIds, groupIds,
|
|
|
18553
18736
|
});
|
|
18554
18737
|
}
|
|
18555
18738
|
}
|
|
18556
|
-
var NO_CURRENT_SESSION_TEXT = "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。", DEFAULT_SESSION_TAIL_LINES = 50, MAX_SESSION_TAIL_LINES = 500, sessionHelp, modeHelp, replyModeHelp, statusHelp, cancelHelp;
|
|
18739
|
+
var NO_CURRENT_SESSION_TEXT = "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。", DEFAULT_SESSION_TAIL_LINES = 50, MAX_SESSION_TAIL_LINES = 500, sessionHelp, nativeSessionHelp, modeHelp, replyModeHelp, statusHelp, cancelHelp;
|
|
18557
18740
|
var init_session_handler = __esm(() => {
|
|
18558
18741
|
init_build_coordinator_prompt();
|
|
18559
18742
|
init_channel_scope();
|
|
@@ -18561,7 +18744,7 @@ var init_session_handler = __esm(() => {
|
|
|
18561
18744
|
sessionHelp = {
|
|
18562
18745
|
topic: "session",
|
|
18563
18746
|
aliases: ["ss", "sessions"],
|
|
18564
|
-
summary: "
|
|
18747
|
+
summary: "创建、复用、切换和重置 weacpx 逻辑会话。",
|
|
18565
18748
|
commands: [
|
|
18566
18749
|
{ usage: "/sessions", description: "查看当前会话列表" },
|
|
18567
18750
|
{ usage: "/session 或 /ss", description: "查看会话列表" },
|
|
@@ -18569,12 +18752,48 @@ var init_session_handler = __esm(() => {
|
|
|
18569
18752
|
{ usage: "/ss new <agent> (-d <path> | --ws <name>)", description: "强制新建会话" },
|
|
18570
18753
|
{ usage: "/ss new <alias> -a <name> --ws <name>", description: "按指定配置新建会话" },
|
|
18571
18754
|
{ usage: "/ss attach <alias> -a <name> --ws <name> --name <transport-session>", description: "绑定已有会话" },
|
|
18755
|
+
{ usage: "/ssn 或 /help ssn", description: "接入本地 native 会话(Codex 等 Agent 原生会话)" },
|
|
18572
18756
|
{ usage: "/session tail [N]", description: "补拉当前会话的历史输出(默认 50 行)" },
|
|
18573
18757
|
{ usage: "/session rm <alias>", description: "删除逻辑会话" },
|
|
18574
18758
|
{ usage: "/use <alias>", description: "切换当前会话" },
|
|
18575
18759
|
{ usage: "/session reset 或 /clear", description: "重置当前会话上下文" }
|
|
18576
18760
|
],
|
|
18577
|
-
examples: [
|
|
18761
|
+
examples: [
|
|
18762
|
+
"/ss codex -d /absolute/path/to/repo",
|
|
18763
|
+
"/ssn",
|
|
18764
|
+
"/ssn 1",
|
|
18765
|
+
"/use backend-fix",
|
|
18766
|
+
"/session rm old-session",
|
|
18767
|
+
"/session reset"
|
|
18768
|
+
]
|
|
18769
|
+
};
|
|
18770
|
+
nativeSessionHelp = {
|
|
18771
|
+
topic: "native",
|
|
18772
|
+
aliases: ["ssn", "native-session"],
|
|
18773
|
+
summary: "接入 Codex 等 Agent 的本地原生会话。",
|
|
18774
|
+
commands: [
|
|
18775
|
+
{ usage: "/ssn", description: "按当前 weacpx 会话上下文查看本地 native 会话" },
|
|
18776
|
+
{ usage: "/ssn <agent> --ws <workspace>", description: "查询指定工作区的本地 native 会话;只有一个候选时自动接入" },
|
|
18777
|
+
{ usage: "/ssn <agent> -d <path>", description: "按本机绝对路径查询;只有一个候选时自动接入" },
|
|
18778
|
+
{ usage: "/ssn <agent> --ws <workspace> --all", description: "跨 cwd 查看该 agent 的 native 会话" },
|
|
18779
|
+
{ usage: "/ssn 1", description: "接入或切换到最近一次列表里的第 1 个候选" },
|
|
18780
|
+
{ usage: "/ssn 1 -a <alias>", description: "接入第 1 个候选并指定 weacpx 别名(推荐,无需完整 sessionId)" },
|
|
18781
|
+
{ usage: "/ssn attach <sessionId> -a <alias>", description: "按原生 sessionId 接入(适合已知完整 id),并指定 weacpx 别名" },
|
|
18782
|
+
{ usage: "/ss attach native <sessionId> -a <alias>", description: "/ssn attach 的长写法" }
|
|
18783
|
+
],
|
|
18784
|
+
examples: [
|
|
18785
|
+
"/ssn codex --ws backend",
|
|
18786
|
+
"/ssn codex -d /absolute/path/to/repo",
|
|
18787
|
+
"/ssn",
|
|
18788
|
+
"/ssn 1",
|
|
18789
|
+
"/ssn 1 -a fix-ci"
|
|
18790
|
+
],
|
|
18791
|
+
notes: [
|
|
18792
|
+
"/ss 管 weacpx 逻辑会话;/ssn 只负责查询和接入 Agent 原生会话。",
|
|
18793
|
+
"接入后继续发普通消息,会继续同一个 Agent 原生会话,不是复制一份新上下文。",
|
|
18794
|
+
"如果当前 acpx 或 Agent 不支持 native 会话,请继续使用 /ss。",
|
|
18795
|
+
"完整说明见 docs/native-sessions.md。"
|
|
18796
|
+
]
|
|
18578
18797
|
};
|
|
18579
18798
|
modeHelp = {
|
|
18580
18799
|
topic: "mode",
|
|
@@ -19537,6 +19756,7 @@ var init_help_registry = __esm(() => {
|
|
|
19537
19756
|
init_later_handler();
|
|
19538
19757
|
HELP_TOPICS = [
|
|
19539
19758
|
sessionHelp,
|
|
19759
|
+
nativeSessionHelp,
|
|
19540
19760
|
workspaceHelp,
|
|
19541
19761
|
agentHelp,
|
|
19542
19762
|
permissionHelp,
|
|
@@ -19573,6 +19793,7 @@ function renderHelpIndex() {
|
|
|
19573
19793
|
return [
|
|
19574
19794
|
"常用入口:",
|
|
19575
19795
|
"- /ss <agent> (-d <path> | --ws <name>) - 快速新建或切到会话",
|
|
19796
|
+
"- /ssn <agent> (-d <path> | --ws <name>) - 接入本地 Agent 原生会话",
|
|
19576
19797
|
"- /use <alias> - 切换当前会话",
|
|
19577
19798
|
"- /status - 查看当前会话状态",
|
|
19578
19799
|
"",
|
|
@@ -19581,7 +19802,7 @@ function renderHelpIndex() {
|
|
|
19581
19802
|
"",
|
|
19582
19803
|
"查看专题说明:",
|
|
19583
19804
|
"- /help <topic>",
|
|
19584
|
-
"- 例如:/help ss、/help ws、/help pm"
|
|
19805
|
+
"- 例如:/help ss、/help ssn、/help ws、/help pm"
|
|
19585
19806
|
].join(`
|
|
19586
19807
|
`);
|
|
19587
19808
|
}
|
|
@@ -19663,7 +19884,7 @@ async function handleSessionShortcutCommand(context, ops, chatKey, agent, target
|
|
|
19663
19884
|
});
|
|
19664
19885
|
const baseAlias = `${workspace.name}:${agent}`;
|
|
19665
19886
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
19666
|
-
const scopedBase = channelId
|
|
19887
|
+
const scopedBase = scopeDisplayAliasToInternal(channelId, baseAlias);
|
|
19667
19888
|
const alias = createNew ? await allocateUniqueSessionAlias(context, scopedBase, chatKey) : scopedBase;
|
|
19668
19889
|
const display = toDisplaySessionAlias(alias);
|
|
19669
19890
|
if (!createNew && await hasLogicalSession(context, alias, chatKey)) {
|
|
@@ -19792,6 +20013,404 @@ var init_session_shortcut_handler = __esm(() => {
|
|
|
19792
20013
|
init_channel_scope();
|
|
19793
20014
|
});
|
|
19794
20015
|
|
|
20016
|
+
// src/commands/handlers/native-session-handler.ts
|
|
20017
|
+
async function handleNativeSessionList(context, chatKey, input) {
|
|
20018
|
+
const target = await resolveNativeTarget(context, chatKey, input);
|
|
20019
|
+
if (isRouterResponse(target)) {
|
|
20020
|
+
return target;
|
|
20021
|
+
}
|
|
20022
|
+
const listAgentSessions = context.transport.listAgentSessions?.bind(context.transport);
|
|
20023
|
+
if (!listAgentSessions) {
|
|
20024
|
+
return { text: `当前 transport 不支持列出本地会话,请继续使用 /ss。
|
|
20025
|
+
说明:/help ssn` };
|
|
20026
|
+
}
|
|
20027
|
+
const query = {
|
|
20028
|
+
agent: target.agent,
|
|
20029
|
+
agentCommand: target.agentCommand,
|
|
20030
|
+
cwd: target.cwd,
|
|
20031
|
+
...input.cursor ? { cursor: input.cursor } : {},
|
|
20032
|
+
...input.all ? {} : { filterCwd: target.cwd }
|
|
20033
|
+
};
|
|
20034
|
+
let result;
|
|
20035
|
+
try {
|
|
20036
|
+
result = await listAgentSessions(query);
|
|
20037
|
+
} catch (error2) {
|
|
20038
|
+
return { text: renderNativeListError(target, error2) };
|
|
20039
|
+
}
|
|
20040
|
+
if (!result) {
|
|
20041
|
+
return { text: `当前 transport 不支持列出本地会话,请继续使用 /ss。
|
|
20042
|
+
说明:/help ssn` };
|
|
20043
|
+
}
|
|
20044
|
+
await context.sessions.cacheNativeSessionList(chatKey, {
|
|
20045
|
+
agent: target.agent,
|
|
20046
|
+
workspace: target.workspace,
|
|
20047
|
+
cwd: target.cwd,
|
|
20048
|
+
sessions: result.sessions,
|
|
20049
|
+
...result.nextCursor !== undefined ? { nextCursor: result.nextCursor } : {}
|
|
20050
|
+
});
|
|
20051
|
+
if (result.sessions.length === 0) {
|
|
20052
|
+
return {
|
|
20053
|
+
text: [
|
|
20054
|
+
`没有找到本地 ${target.agentDisplayName} 会话(${target.workspaceLabel})。`,
|
|
20055
|
+
`你可以稍后再试,或先通过 /ss 保持当前逻辑会话。`
|
|
20056
|
+
].join(`
|
|
20057
|
+
`)
|
|
20058
|
+
};
|
|
20059
|
+
}
|
|
20060
|
+
const explicitAttachTarget = Boolean(input.workspace || input.cwd);
|
|
20061
|
+
if (explicitAttachTarget && !input.all && !input.cursor && result.sessions.length === 1) {
|
|
20062
|
+
return await attachNativeSession(context, chatKey, target, result.sessions[0], undefined);
|
|
20063
|
+
}
|
|
20064
|
+
const attachedEntries = await buildAttachedEntries(context, chatKey, target.agent, result.sessions);
|
|
20065
|
+
const nativeSessionListOptions = { format: context.resolveNativeSessionListFormat?.(chatKey) ?? "table" };
|
|
20066
|
+
return {
|
|
20067
|
+
text: renderNativeSessionList(target, result, attachedEntries, Boolean(input.all), nativeSessionListOptions)
|
|
20068
|
+
};
|
|
20069
|
+
}
|
|
20070
|
+
async function handleNativeSessionSelect(context, chatKey, identifier, alias) {
|
|
20071
|
+
const trimmed = identifier.trim();
|
|
20072
|
+
if (!trimmed) {
|
|
20073
|
+
return { text: `请选择要切换的 native 会话编号或 sessionId。
|
|
20074
|
+
说明:/help ssn` };
|
|
20075
|
+
}
|
|
20076
|
+
if (/^[0-9]+$/.test(trimmed)) {
|
|
20077
|
+
const cached2 = await context.sessions.getNativeSessionList(chatKey, NATIVE_SESSION_CACHE_TTL_MS);
|
|
20078
|
+
if (!cached2 || cached2.sessions.length === 0) {
|
|
20079
|
+
return { text: `当前没有可用的 native 会话列表,请先执行 /ssn 再选择。
|
|
20080
|
+
说明:/help ssn` };
|
|
20081
|
+
}
|
|
20082
|
+
const index = Number(trimmed) - 1;
|
|
20083
|
+
const session = cached2.sessions[index];
|
|
20084
|
+
if (!session) {
|
|
20085
|
+
return { text: "编号超出范围,请先执行 /ssn 重新获取列表。" };
|
|
20086
|
+
}
|
|
20087
|
+
const target2 = await resolveTargetFromCachedSession(context, chatKey, cached2, session);
|
|
20088
|
+
if (isRouterResponse(target2)) {
|
|
20089
|
+
return target2;
|
|
20090
|
+
}
|
|
20091
|
+
return await attachNativeSession(context, chatKey, target2, session, alias);
|
|
20092
|
+
}
|
|
20093
|
+
const target = await resolveNativeTarget(context, chatKey, {});
|
|
20094
|
+
if (isRouterResponse(target)) {
|
|
20095
|
+
return target;
|
|
20096
|
+
}
|
|
20097
|
+
return await attachNativeSession(context, chatKey, target, { sessionId: trimmed }, alias);
|
|
20098
|
+
}
|
|
20099
|
+
async function attachNativeSession(context, chatKey, target, session, alias) {
|
|
20100
|
+
if (!context.transport.resumeAgentSession) {
|
|
20101
|
+
return { text: "当前 transport 不支持接入本地会话,请继续使用 /ss。" };
|
|
20102
|
+
}
|
|
20103
|
+
const nativeTarget = target;
|
|
20104
|
+
const existing = await context.sessions.findAttachedNativeSession(chatKey, nativeTarget.agent, session.sessionId);
|
|
20105
|
+
if (existing) {
|
|
20106
|
+
await context.sessions.useSession(chatKey, existing.alias);
|
|
20107
|
+
const displayAlias2 = toDisplaySessionAlias(existing.alias);
|
|
20108
|
+
return {
|
|
20109
|
+
text: `已切换到已接入的本地会话:${nativeTarget.agentDisplayName} · ${displayAlias2}`
|
|
20110
|
+
};
|
|
20111
|
+
}
|
|
20112
|
+
const requestedAlias = alias?.trim() || buildDefaultNativeAlias(nativeTarget.agent, session.sessionId);
|
|
20113
|
+
const displayAlias = await allocateUniqueNativeAlias(context, chatKey, requestedAlias);
|
|
20114
|
+
const internalAlias = scopeDisplayAliasToInternal(getChannelIdFromChatKey(chatKey), displayAlias);
|
|
20115
|
+
const transportSession = context.sessions.buildDefaultTransportSessionForChat(chatKey, displayAlias);
|
|
20116
|
+
const resolvedSession = context.lifecycle.resolveSession(internalAlias, nativeTarget.agent, nativeTarget.workspace, transportSession);
|
|
20117
|
+
const releaseReservation = await context.lifecycle.reserveTransportSession(resolvedSession.transportSession);
|
|
20118
|
+
try {
|
|
20119
|
+
try {
|
|
20120
|
+
await context.transport.resumeAgentSession(resolvedSession, session.sessionId);
|
|
20121
|
+
} catch (error2) {
|
|
20122
|
+
return { text: renderNativeResumeError(target, error2) };
|
|
20123
|
+
}
|
|
20124
|
+
const verified = await context.lifecycle.checkTransportSession(resolvedSession);
|
|
20125
|
+
if (!verified) {
|
|
20126
|
+
return { text: `本地 ${target.agentDisplayName} 会话接入失败:未检测到已恢复的后端会话。` };
|
|
20127
|
+
}
|
|
20128
|
+
await context.sessions.attachNativeSession({
|
|
20129
|
+
alias: internalAlias,
|
|
20130
|
+
agent: nativeTarget.agent,
|
|
20131
|
+
workspace: nativeTarget.workspace,
|
|
20132
|
+
transportSession,
|
|
20133
|
+
...target.agentCommand ? { transportAgentCommand: target.agentCommand } : {},
|
|
20134
|
+
agentSessionId: session.sessionId,
|
|
20135
|
+
title: session.title,
|
|
20136
|
+
updatedAt: session.updatedAt
|
|
20137
|
+
});
|
|
20138
|
+
await context.sessions.useSession(chatKey, internalAlias);
|
|
20139
|
+
await refreshAgentCommandBestEffort(context, internalAlias);
|
|
20140
|
+
return {
|
|
20141
|
+
text: `已接入本地 ${target.agentDisplayName} 会话并切换:${toDisplaySessionAlias(internalAlias)}`
|
|
20142
|
+
};
|
|
20143
|
+
} finally {
|
|
20144
|
+
await releaseReservation();
|
|
20145
|
+
}
|
|
20146
|
+
}
|
|
20147
|
+
async function resolveNativeTarget(context, chatKey, input) {
|
|
20148
|
+
const currentSession = await context.sessions.getCurrentSession(chatKey);
|
|
20149
|
+
const agent = input.agent?.trim() || currentSession?.agent || "";
|
|
20150
|
+
if (!agent) {
|
|
20151
|
+
return {
|
|
20152
|
+
text: `请先选择上下文,例如:
|
|
20153
|
+
/ssn codex --ws project
|
|
20154
|
+
/ssn codex -d /Users/me/project
|
|
20155
|
+
说明:/help ssn`
|
|
20156
|
+
};
|
|
20157
|
+
}
|
|
20158
|
+
const agentConfig = context.config?.agents[agent];
|
|
20159
|
+
if (!agentConfig) {
|
|
20160
|
+
return { text: `Agent「${agent}」未注册。` };
|
|
20161
|
+
}
|
|
20162
|
+
const workspaceResolution = await resolveNativeWorkspace(context, input, currentSession);
|
|
20163
|
+
if (isRouterResponse(workspaceResolution)) {
|
|
20164
|
+
return workspaceResolution;
|
|
20165
|
+
}
|
|
20166
|
+
return {
|
|
20167
|
+
agent,
|
|
20168
|
+
agentDisplayName: displayAgentName(agent),
|
|
20169
|
+
agentCommand: resolveAgentCommand(agentConfig.driver, agentConfig.command),
|
|
20170
|
+
workspace: workspaceResolution.workspace,
|
|
20171
|
+
workspaceLabel: workspaceResolution.workspaceLabel,
|
|
20172
|
+
cwd: workspaceResolution.cwd,
|
|
20173
|
+
source: workspaceResolution.source
|
|
20174
|
+
};
|
|
20175
|
+
}
|
|
20176
|
+
async function resolveTargetFromCachedSession(context, chatKey, cached2, session) {
|
|
20177
|
+
if (session.cwd && !sameWorkspacePath(session.cwd, cached2.cwd)) {
|
|
20178
|
+
return await resolveNativeTarget(context, chatKey, {
|
|
20179
|
+
agent: cached2.agent,
|
|
20180
|
+
cwd: session.cwd
|
|
20181
|
+
});
|
|
20182
|
+
}
|
|
20183
|
+
return await resolveNativeTarget(context, chatKey, {
|
|
20184
|
+
agent: cached2.agent,
|
|
20185
|
+
...cached2.workspace ? { workspace: cached2.workspace } : { cwd: cached2.cwd }
|
|
20186
|
+
});
|
|
20187
|
+
}
|
|
20188
|
+
async function resolveNativeWorkspace(context, input, currentSession) {
|
|
20189
|
+
if (input.workspace) {
|
|
20190
|
+
const workspaceConfig = context.config?.workspaces[input.workspace];
|
|
20191
|
+
if (!workspaceConfig) {
|
|
20192
|
+
return { text: `工作区「${input.workspace}」未注册。` };
|
|
20193
|
+
}
|
|
20194
|
+
return {
|
|
20195
|
+
workspace: input.workspace,
|
|
20196
|
+
workspaceLabel: input.workspace,
|
|
20197
|
+
cwd: workspaceConfig.cwd,
|
|
20198
|
+
source: "workspace"
|
|
20199
|
+
};
|
|
20200
|
+
}
|
|
20201
|
+
if (input.cwd) {
|
|
20202
|
+
const cwd = normalizeWorkspacePath(input.cwd);
|
|
20203
|
+
const existing = Object.entries(context.config?.workspaces ?? {}).find(([, workspace]) => sameWorkspacePath(workspace.cwd, cwd));
|
|
20204
|
+
if (existing) {
|
|
20205
|
+
return {
|
|
20206
|
+
workspace: existing[0],
|
|
20207
|
+
workspaceLabel: existing[0],
|
|
20208
|
+
cwd: existing[1].cwd,
|
|
20209
|
+
source: "cwd"
|
|
20210
|
+
};
|
|
20211
|
+
}
|
|
20212
|
+
if (!await pathExists(cwd)) {
|
|
20213
|
+
return { text: `工作区路径不存在:${input.cwd}` };
|
|
20214
|
+
}
|
|
20215
|
+
if (!context.configStore || !context.config) {
|
|
20216
|
+
return { text: "当前没有加载可写入的配置,无法根据路径创建工作区。" };
|
|
20217
|
+
}
|
|
20218
|
+
const workspaceName = allocateWorkspaceName(sanitizeWorkspaceName(basenameForWorkspacePath(cwd)), context.config.workspaces);
|
|
20219
|
+
const updated = await context.configStore.upsertWorkspace(workspaceName, cwd);
|
|
20220
|
+
context.replaceConfig(updated);
|
|
20221
|
+
return {
|
|
20222
|
+
workspace: workspaceName,
|
|
20223
|
+
workspaceLabel: workspaceName,
|
|
20224
|
+
cwd,
|
|
20225
|
+
source: "cwd"
|
|
20226
|
+
};
|
|
20227
|
+
}
|
|
20228
|
+
if (currentSession) {
|
|
20229
|
+
return {
|
|
20230
|
+
workspace: currentSession.workspace,
|
|
20231
|
+
workspaceLabel: currentSession.workspace,
|
|
20232
|
+
cwd: currentSession.cwd,
|
|
20233
|
+
source: "workspace"
|
|
20234
|
+
};
|
|
20235
|
+
}
|
|
20236
|
+
return {
|
|
20237
|
+
text: `请先选择上下文,例如:
|
|
20238
|
+
/ssn codex --ws project
|
|
20239
|
+
/ssn codex -d /Users/me/project
|
|
20240
|
+
说明:/help ssn`
|
|
20241
|
+
};
|
|
20242
|
+
}
|
|
20243
|
+
async function buildAttachedEntries(context, chatKey, agent, sessions) {
|
|
20244
|
+
const currentSession = await context.sessions.getCurrentSession(chatKey);
|
|
20245
|
+
return await Promise.all(sessions.map(async (session) => {
|
|
20246
|
+
const attached = await context.sessions.findAttachedNativeSession(chatKey, agent, session.sessionId);
|
|
20247
|
+
if (!attached) {
|
|
20248
|
+
return { session };
|
|
20249
|
+
}
|
|
20250
|
+
return {
|
|
20251
|
+
session,
|
|
20252
|
+
attached: {
|
|
20253
|
+
alias: attached.alias,
|
|
20254
|
+
displayAlias: toDisplaySessionAlias(attached.alias),
|
|
20255
|
+
isCurrent: currentSession?.alias === attached.alias
|
|
20256
|
+
}
|
|
20257
|
+
};
|
|
20258
|
+
}));
|
|
20259
|
+
}
|
|
20260
|
+
function renderNativeSessionList(target, result, entries, includeAll, options = {}) {
|
|
20261
|
+
if (options.format === "cards") {
|
|
20262
|
+
return renderNativeSessionCardList(target, result, entries, includeAll);
|
|
20263
|
+
}
|
|
20264
|
+
return renderNativeSessionTableList(target, result, entries, includeAll);
|
|
20265
|
+
}
|
|
20266
|
+
function renderNativeSessionTableList(target, result, entries, includeAll) {
|
|
20267
|
+
const lines = [`本地 ${target.agentDisplayName} 会话(${target.workspaceLabel}):`];
|
|
20268
|
+
lines.push("| # | 标题 | 更新时间 | ID |");
|
|
20269
|
+
lines.push("|---|---|---|---|");
|
|
20270
|
+
entries.forEach((entry, index) => {
|
|
20271
|
+
const title = escapeMarkdownTableCell(renderNativeSessionTitle(entry.session.title, entry.session.sessionId));
|
|
20272
|
+
const updatedAt = entry.session.updatedAt ? formatNativeSessionTime(entry.session.updatedAt) : "-";
|
|
20273
|
+
const idParts = [entry.session.sessionId];
|
|
20274
|
+
if (entry.attached) {
|
|
20275
|
+
idParts.push(`已接入:${entry.attached.displayAlias}${entry.attached.isCurrent ? " [当前]" : ""}`);
|
|
20276
|
+
}
|
|
20277
|
+
lines.push(`| ${index + 1} | ${title} | ${escapeMarkdownTableCell(updatedAt)} | ${escapeMarkdownTableCell(idParts.join(" · "))} |`);
|
|
20278
|
+
});
|
|
20279
|
+
lines.push("");
|
|
20280
|
+
lines.push("操作:");
|
|
20281
|
+
lines.push("接入:/ssn 1");
|
|
20282
|
+
lines.push("指定别名:/ssn 1 -a fix-ci");
|
|
20283
|
+
lines.push("说明:/help ssn");
|
|
20284
|
+
if (result.nextCursor) {
|
|
20285
|
+
lines.push(`更多:${renderNextPageCommand(target, result.nextCursor, includeAll)}`);
|
|
20286
|
+
}
|
|
20287
|
+
return lines.join(`
|
|
20288
|
+
`);
|
|
20289
|
+
}
|
|
20290
|
+
function renderNativeSessionCardList(target, result, entries, includeAll) {
|
|
20291
|
+
const lines = [
|
|
20292
|
+
`本地 ${target.agentDisplayName} 会话(${target.workspaceLabel}):`,
|
|
20293
|
+
"回复编号接入,ID 尾号用于区分。"
|
|
20294
|
+
];
|
|
20295
|
+
entries.forEach((entry, index) => {
|
|
20296
|
+
const title = renderNativeSessionTitle(entry.session.title, entry.session.sessionId);
|
|
20297
|
+
const updatedAt = entry.session.updatedAt ? formatNativeSessionTime(entry.session.updatedAt) : "-";
|
|
20298
|
+
lines.push("");
|
|
20299
|
+
lines.push(`【${index + 1}】 ${title}`);
|
|
20300
|
+
lines.push(`时间:${updatedAt}`);
|
|
20301
|
+
lines.push(`ID:${formatSessionIdTail(entry.session.sessionId)}`);
|
|
20302
|
+
if (entry.attached) {
|
|
20303
|
+
lines.push(`已接入:${entry.attached.displayAlias}${entry.attached.isCurrent ? " [当前]" : ""}`);
|
|
20304
|
+
}
|
|
20305
|
+
});
|
|
20306
|
+
lines.push("");
|
|
20307
|
+
lines.push("操作:");
|
|
20308
|
+
lines.push("接入:/ssn 1");
|
|
20309
|
+
lines.push("指定别名:/ssn 1 -a fix-ci");
|
|
20310
|
+
lines.push("说明:/help ssn");
|
|
20311
|
+
if (result.nextCursor) {
|
|
20312
|
+
lines.push(`更多:${renderNextPageCommand(target, result.nextCursor, includeAll)}`);
|
|
20313
|
+
}
|
|
20314
|
+
return lines.join(`
|
|
20315
|
+
`);
|
|
20316
|
+
}
|
|
20317
|
+
function renderNativeSessionTitle(title, fallback) {
|
|
20318
|
+
const normalized = (title?.trim() || fallback).replace(/\s+/g, " ");
|
|
20319
|
+
const maxLength = 60;
|
|
20320
|
+
return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1)}…` : normalized;
|
|
20321
|
+
}
|
|
20322
|
+
function buildDefaultNativeAlias(agent, sessionId) {
|
|
20323
|
+
return `${agent}-${sessionIdTail(sessionId)}`;
|
|
20324
|
+
}
|
|
20325
|
+
function formatSessionIdTail(sessionId) {
|
|
20326
|
+
const tail = sessionIdTail(sessionId);
|
|
20327
|
+
return tail.length < sessionId.trim().length ? `…${tail}` : tail;
|
|
20328
|
+
}
|
|
20329
|
+
function sessionIdTail(sessionId) {
|
|
20330
|
+
const trimmed = sessionId.trim();
|
|
20331
|
+
if (trimmed.length <= 8) {
|
|
20332
|
+
return trimmed;
|
|
20333
|
+
}
|
|
20334
|
+
return trimmed.slice(-8);
|
|
20335
|
+
}
|
|
20336
|
+
function formatNativeSessionTime(value) {
|
|
20337
|
+
const date4 = new Date(value);
|
|
20338
|
+
if (Number.isNaN(date4.getTime())) {
|
|
20339
|
+
return value;
|
|
20340
|
+
}
|
|
20341
|
+
const pad = (input) => String(input).padStart(2, "0");
|
|
20342
|
+
return `${date4.getFullYear()}-${pad(date4.getMonth() + 1)}-${pad(date4.getDate())} ${pad(date4.getHours())}:${pad(date4.getMinutes())}`;
|
|
20343
|
+
}
|
|
20344
|
+
function escapeMarkdownTableCell(value) {
|
|
20345
|
+
return value.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
20346
|
+
}
|
|
20347
|
+
function renderNextPageCommand(target, nextCursor, includeAll) {
|
|
20348
|
+
const scope = target.source === "workspace" && target.workspace ? `--ws ${target.workspace}` : `-d ${target.cwd}`;
|
|
20349
|
+
const allFlag = includeAll ? " --all" : "";
|
|
20350
|
+
return `/ssn ${target.agent} ${scope}${allFlag} --cursor ${nextCursor}`;
|
|
20351
|
+
}
|
|
20352
|
+
async function allocateUniqueNativeAlias(context, chatKey, baseDisplayAlias) {
|
|
20353
|
+
const channelId = getChannelIdFromChatKey(chatKey);
|
|
20354
|
+
const visible = await context.sessions.listSessions(chatKey);
|
|
20355
|
+
const existing = new Set(visible.map((session) => session.internalAlias));
|
|
20356
|
+
const base = baseDisplayAlias.trim() || "native-session";
|
|
20357
|
+
const transportFor = (candidate) => context.sessions.buildDefaultTransportSessionForChat(chatKey, candidate);
|
|
20358
|
+
const isFree = (candidate) => !existing.has(scopeDisplayAliasToInternal(channelId, candidate)) && context.sessions.countAliasesSharingTransport(transportFor(candidate)) === 0;
|
|
20359
|
+
if (isFree(base)) {
|
|
20360
|
+
return base;
|
|
20361
|
+
}
|
|
20362
|
+
let suffix = 2;
|
|
20363
|
+
while (!isFree(`${base}-${suffix}`)) {
|
|
20364
|
+
suffix += 1;
|
|
20365
|
+
}
|
|
20366
|
+
return `${base}-${suffix}`;
|
|
20367
|
+
}
|
|
20368
|
+
async function refreshAgentCommandBestEffort(context, alias) {
|
|
20369
|
+
try {
|
|
20370
|
+
await context.lifecycle.refreshSessionTransportAgentCommand(alias);
|
|
20371
|
+
} catch (error2) {
|
|
20372
|
+
await context.logger.error("session.native.agent_command_refresh_failed", "failed to refresh native session agent command", {
|
|
20373
|
+
alias,
|
|
20374
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
20375
|
+
});
|
|
20376
|
+
}
|
|
20377
|
+
}
|
|
20378
|
+
function renderNativeListError(target, error2) {
|
|
20379
|
+
return [
|
|
20380
|
+
`本地 ${target.agentDisplayName} 会话查询失败:${formatErrorMessage(error2)}`,
|
|
20381
|
+
"请确认 acpx/Agent 支持 native 会话查询,或继续使用 /ss。",
|
|
20382
|
+
"说明:/help ssn"
|
|
20383
|
+
].join(`
|
|
20384
|
+
`);
|
|
20385
|
+
}
|
|
20386
|
+
function renderNativeResumeError(target, error2) {
|
|
20387
|
+
return [
|
|
20388
|
+
`本地 ${target.agentDisplayName} 会话接入失败:${formatErrorMessage(error2)}`,
|
|
20389
|
+
"请确认 acpx/Agent 支持 native 会话恢复,或继续使用 /ss。",
|
|
20390
|
+
"说明:/help ssn"
|
|
20391
|
+
].join(`
|
|
20392
|
+
`);
|
|
20393
|
+
}
|
|
20394
|
+
function formatErrorMessage(error2) {
|
|
20395
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
20396
|
+
}
|
|
20397
|
+
function isRouterResponse(value) {
|
|
20398
|
+
return typeof value.text === "string";
|
|
20399
|
+
}
|
|
20400
|
+
function displayAgentName(agent) {
|
|
20401
|
+
if (!agent) {
|
|
20402
|
+
return agent;
|
|
20403
|
+
}
|
|
20404
|
+
return agent.charAt(0).toUpperCase() + agent.slice(1);
|
|
20405
|
+
}
|
|
20406
|
+
var NATIVE_SESSION_CACHE_TTL_MS;
|
|
20407
|
+
var init_native_session_handler = __esm(() => {
|
|
20408
|
+
init_channel_scope();
|
|
20409
|
+
init_workspace_name();
|
|
20410
|
+
init_workspace_path();
|
|
20411
|
+
NATIVE_SESSION_CACHE_TTL_MS = 10 * 60 * 1000;
|
|
20412
|
+
});
|
|
20413
|
+
|
|
19795
20414
|
// src/commands/handlers/session-recovery-handler.ts
|
|
19796
20415
|
function renderTransportError(session, error2) {
|
|
19797
20416
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -20259,6 +20878,7 @@ class CommandRouter {
|
|
|
20259
20878
|
quota;
|
|
20260
20879
|
scheduled;
|
|
20261
20880
|
scheduledDelivery;
|
|
20881
|
+
resolveNativeSessionListFormat;
|
|
20262
20882
|
logger;
|
|
20263
20883
|
autoInstall = autoInstallOptionalDep;
|
|
20264
20884
|
discoverPaths = discoverParentPackagePaths;
|
|
@@ -20268,7 +20888,7 @@ class CommandRouter {
|
|
|
20268
20888
|
__setDiscoverPathsForTest(fn) {
|
|
20269
20889
|
this.discoverPaths = fn;
|
|
20270
20890
|
}
|
|
20271
|
-
constructor(sessions, transport, config2, configStore, logger2, resolveSessionAgentCommand = resolveSessionAgentCommandFromIndex, orchestration, quota, scheduled, scheduledDelivery) {
|
|
20891
|
+
constructor(sessions, transport, config2, configStore, logger2, resolveSessionAgentCommand = resolveSessionAgentCommandFromIndex, orchestration, quota, scheduled, scheduledDelivery, resolveNativeSessionListFormat) {
|
|
20272
20892
|
this.sessions = sessions;
|
|
20273
20893
|
this.transport = transport;
|
|
20274
20894
|
this.config = config2;
|
|
@@ -20278,6 +20898,7 @@ class CommandRouter {
|
|
|
20278
20898
|
this.quota = quota;
|
|
20279
20899
|
this.scheduled = scheduled;
|
|
20280
20900
|
this.scheduledDelivery = scheduledDelivery;
|
|
20901
|
+
this.resolveNativeSessionListFormat = resolveNativeSessionListFormat;
|
|
20281
20902
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
20282
20903
|
}
|
|
20283
20904
|
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
@@ -20352,6 +20973,12 @@ class CommandRouter {
|
|
|
20352
20973
|
return await handleSessionShortcut(this.createSessionHandlerContext(reply, perfSpan), chatKey, command.agent, command, true);
|
|
20353
20974
|
case "session.attach":
|
|
20354
20975
|
return await handleSessionAttach(this.createSessionHandlerContext(reply, perfSpan), chatKey, command.alias, command.agent, command.workspace, command.transportSession);
|
|
20976
|
+
case "session.native.list":
|
|
20977
|
+
return await handleNativeSessionList(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command);
|
|
20978
|
+
case "session.native.select":
|
|
20979
|
+
return await handleNativeSessionSelect(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.identifier, command.alias);
|
|
20980
|
+
case "session.native.attach":
|
|
20981
|
+
return await handleNativeSessionSelect(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.identifier, command.alias);
|
|
20355
20982
|
case "session.use":
|
|
20356
20983
|
return await handleSessionUse(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias);
|
|
20357
20984
|
case "mode.show":
|
|
@@ -20453,7 +21080,8 @@ class CommandRouter {
|
|
|
20453
21080
|
configStore: this.configStore,
|
|
20454
21081
|
logger: this.logger,
|
|
20455
21082
|
replaceConfig: (updated) => this.replaceConfig(updated),
|
|
20456
|
-
...this.quota ? { quota: this.quota } : {}
|
|
21083
|
+
...this.quota ? { quota: this.quota } : {},
|
|
21084
|
+
...this.resolveNativeSessionListFormat ? { resolveNativeSessionListFormat: this.resolveNativeSessionListFormat } : {}
|
|
20457
21085
|
};
|
|
20458
21086
|
}
|
|
20459
21087
|
createSessionHandlerContext(reply, perfSpan) {
|
|
@@ -20815,6 +21443,7 @@ var init_command_router = __esm(() => {
|
|
|
20815
21443
|
init_agent_handler();
|
|
20816
21444
|
init_workspace_handler();
|
|
20817
21445
|
init_session_shortcut_handler();
|
|
21446
|
+
init_native_session_handler();
|
|
20818
21447
|
init_later_handler();
|
|
20819
21448
|
init_session_recovery_handler();
|
|
20820
21449
|
init_auto_install_optional_dep();
|
|
@@ -25138,11 +25767,13 @@ class SessionService {
|
|
|
25138
25767
|
stateStore;
|
|
25139
25768
|
state;
|
|
25140
25769
|
stateMutex;
|
|
25770
|
+
now;
|
|
25141
25771
|
constructor(config2, stateStore, state, options = {}) {
|
|
25142
25772
|
this.config = config2;
|
|
25143
25773
|
this.stateStore = stateStore;
|
|
25144
25774
|
this.state = state;
|
|
25145
25775
|
this.stateMutex = options.stateMutex ?? new AsyncMutex;
|
|
25776
|
+
this.now = options.now ?? (() => Date.now());
|
|
25146
25777
|
}
|
|
25147
25778
|
async createSession(alias, agent, workspace) {
|
|
25148
25779
|
return await this.createLogicalSession(alias, agent, workspace, `${workspace}:${alias}`);
|
|
@@ -25176,6 +25807,14 @@ class SessionService {
|
|
|
25176
25807
|
async attachSession(alias, agent, workspace, transportSession, transportAgentCommand) {
|
|
25177
25808
|
return await this.createLogicalSession(alias, agent, workspace, transportSession, transportAgentCommand);
|
|
25178
25809
|
}
|
|
25810
|
+
async attachNativeSession(input) {
|
|
25811
|
+
return await this.createLogicalSession(input.alias, input.agent, input.workspace, input.transportSession, input.transportAgentCommand, {
|
|
25812
|
+
source: "agent-side",
|
|
25813
|
+
agentSessionId: input.agentSessionId,
|
|
25814
|
+
title: input.title,
|
|
25815
|
+
updatedAt: input.updatedAt
|
|
25816
|
+
});
|
|
25817
|
+
}
|
|
25179
25818
|
async getSession(alias) {
|
|
25180
25819
|
const session = this.state.sessions[alias];
|
|
25181
25820
|
if (!session) {
|
|
@@ -25190,6 +25829,22 @@ class SessionService {
|
|
|
25190
25829
|
const preferred = matches.find((session) => session.alias === expectedAlias && session.workspace === expectedWorkspace) ?? matches[0];
|
|
25191
25830
|
return preferred ? this.toResolvedSession(preferred) : null;
|
|
25192
25831
|
}
|
|
25832
|
+
async findAttachedNativeSession(chatKey, agent, agentSessionId) {
|
|
25833
|
+
const channelId = getChannelIdFromChatKey(chatKey);
|
|
25834
|
+
for (const session of Object.values(this.state.sessions)) {
|
|
25835
|
+
if (session.source !== "agent-side") {
|
|
25836
|
+
continue;
|
|
25837
|
+
}
|
|
25838
|
+
if (session.agent !== agent || session.agent_session_id !== agentSessionId) {
|
|
25839
|
+
continue;
|
|
25840
|
+
}
|
|
25841
|
+
if (!isSessionAliasVisibleInChannel(session.alias, channelId)) {
|
|
25842
|
+
continue;
|
|
25843
|
+
}
|
|
25844
|
+
return this.toResolvedSession(session);
|
|
25845
|
+
}
|
|
25846
|
+
return null;
|
|
25847
|
+
}
|
|
25193
25848
|
async useSession(chatKey, alias) {
|
|
25194
25849
|
await this.mutate(async () => {
|
|
25195
25850
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
@@ -25309,6 +25964,49 @@ class SessionService {
|
|
|
25309
25964
|
return { wasActive };
|
|
25310
25965
|
});
|
|
25311
25966
|
}
|
|
25967
|
+
async cacheNativeSessionList(chatKey, input) {
|
|
25968
|
+
await this.mutate(async () => {
|
|
25969
|
+
this.state.native_session_lists[chatKey] = {
|
|
25970
|
+
created_at: new Date(this.now()).toISOString(),
|
|
25971
|
+
agent: input.agent,
|
|
25972
|
+
...input.workspace !== undefined ? { workspace: input.workspace } : {},
|
|
25973
|
+
cwd: input.cwd,
|
|
25974
|
+
sessions: input.sessions.map((session) => ({
|
|
25975
|
+
session_id: session.sessionId,
|
|
25976
|
+
...session.cwd !== undefined ? { cwd: session.cwd } : {},
|
|
25977
|
+
...session.title !== undefined ? { title: session.title } : {},
|
|
25978
|
+
...session.updatedAt !== undefined ? { updated_at: session.updatedAt } : {}
|
|
25979
|
+
})),
|
|
25980
|
+
...input.nextCursor !== undefined ? { next_cursor: input.nextCursor } : {}
|
|
25981
|
+
};
|
|
25982
|
+
await this.persist();
|
|
25983
|
+
});
|
|
25984
|
+
}
|
|
25985
|
+
async getNativeSessionList(chatKey, ttlMs = 10 * 60 * 1000) {
|
|
25986
|
+
const cached2 = this.state.native_session_lists[chatKey];
|
|
25987
|
+
if (!cached2) {
|
|
25988
|
+
return null;
|
|
25989
|
+
}
|
|
25990
|
+
const createdAt = Date.parse(cached2.created_at);
|
|
25991
|
+
if (Number.isNaN(createdAt)) {
|
|
25992
|
+
return null;
|
|
25993
|
+
}
|
|
25994
|
+
if (this.now() - createdAt > ttlMs) {
|
|
25995
|
+
return null;
|
|
25996
|
+
}
|
|
25997
|
+
return {
|
|
25998
|
+
agent: cached2.agent,
|
|
25999
|
+
...cached2.workspace !== undefined ? { workspace: cached2.workspace } : {},
|
|
26000
|
+
cwd: cached2.cwd,
|
|
26001
|
+
sessions: cached2.sessions.map((session) => ({
|
|
26002
|
+
sessionId: session.session_id,
|
|
26003
|
+
...session.cwd !== undefined ? { cwd: session.cwd } : {},
|
|
26004
|
+
...session.title !== undefined ? { title: session.title } : {},
|
|
26005
|
+
...session.updated_at !== undefined ? { updatedAt: session.updated_at } : {}
|
|
26006
|
+
})),
|
|
26007
|
+
...cached2.next_cursor !== undefined ? { nextCursor: cached2.next_cursor } : {}
|
|
26008
|
+
};
|
|
26009
|
+
}
|
|
25312
26010
|
toResolvedSession(session) {
|
|
25313
26011
|
const agentConfig = this.config.agents[session.agent];
|
|
25314
26012
|
if (!agentConfig) {
|
|
@@ -25324,6 +26022,11 @@ class SessionService {
|
|
|
25324
26022
|
agentCommand: session.transport_agent_command ?? resolveAgentCommand(agentConfig.driver, agentConfig.command),
|
|
25325
26023
|
workspace: session.workspace,
|
|
25326
26024
|
transportSession: session.transport_session,
|
|
26025
|
+
source: session.source,
|
|
26026
|
+
agentSessionId: session.agent_session_id,
|
|
26027
|
+
agentSessionTitle: session.agent_session_title,
|
|
26028
|
+
agentSessionUpdatedAt: session.agent_session_updated_at,
|
|
26029
|
+
attachedAt: session.attached_at,
|
|
25327
26030
|
modeId: session.mode_id,
|
|
25328
26031
|
replyMode: session.reply_mode,
|
|
25329
26032
|
cwd: workspaceConfig.cwd
|
|
@@ -25351,20 +26054,25 @@ class SessionService {
|
|
|
25351
26054
|
async persist() {
|
|
25352
26055
|
await this.stateStore.save(this.state);
|
|
25353
26056
|
}
|
|
25354
|
-
async createLogicalSession(alias, agent, workspace, transportSession, transportAgentCommand) {
|
|
26057
|
+
async createLogicalSession(alias, agent, workspace, transportSession, transportAgentCommand, native) {
|
|
25355
26058
|
return await this.mutate(async () => {
|
|
25356
26059
|
this.validateSession(alias, agent, workspace);
|
|
25357
26060
|
if (this.state.orchestration.externalCoordinators[transportSession]) {
|
|
25358
26061
|
throw new Error(`transport session "${transportSession}" conflicts with an external coordinator`);
|
|
25359
26062
|
}
|
|
25360
26063
|
const existingSession = this.state.sessions[alias];
|
|
25361
|
-
const now = new Date().toISOString();
|
|
26064
|
+
const now = new Date(this.now()).toISOString();
|
|
25362
26065
|
const normalizedTransportAgentCommand = transportAgentCommand?.trim();
|
|
25363
26066
|
const session = {
|
|
25364
26067
|
alias,
|
|
25365
26068
|
agent,
|
|
25366
26069
|
workspace,
|
|
25367
26070
|
transport_session: transportSession,
|
|
26071
|
+
source: native?.source,
|
|
26072
|
+
agent_session_id: native?.agentSessionId,
|
|
26073
|
+
agent_session_title: native?.title ?? undefined,
|
|
26074
|
+
agent_session_updated_at: native?.updatedAt,
|
|
26075
|
+
attached_at: native ? now : undefined,
|
|
25368
26076
|
...normalizedTransportAgentCommand ? { transport_agent_command: normalizedTransportAgentCommand } : existingSession?.transport_agent_command ? { transport_agent_command: existingSession.transport_agent_command } : {},
|
|
25369
26077
|
mode_id: existingSession?.mode_id,
|
|
25370
26078
|
reply_mode: existingSession?.reply_mode,
|
|
@@ -26161,6 +26869,18 @@ class AcpxBridgeTransport {
|
|
|
26161
26869
|
lines
|
|
26162
26870
|
});
|
|
26163
26871
|
}
|
|
26872
|
+
async listAgentSessions(query) {
|
|
26873
|
+
return await this.client.request("listAgentSessions", { ...query });
|
|
26874
|
+
}
|
|
26875
|
+
async resumeAgentSession(session, agentSessionId) {
|
|
26876
|
+
await this.client.request("resumeAgentSession", {
|
|
26877
|
+
agent: session.agent,
|
|
26878
|
+
...session.agentCommand ? { agentCommand: session.agentCommand } : {},
|
|
26879
|
+
cwd: session.cwd,
|
|
26880
|
+
name: session.transportSession,
|
|
26881
|
+
agentSessionId
|
|
26882
|
+
});
|
|
26883
|
+
}
|
|
26164
26884
|
async prompt(session, text, reply, replyContext, options) {
|
|
26165
26885
|
const sink = reply ? createQuotaGatedReplySink({
|
|
26166
26886
|
reply,
|
|
@@ -26759,7 +27479,7 @@ import { join as join13 } from "node:path";
|
|
|
26759
27479
|
function buildWeacpxMcpServerSpec(input) {
|
|
26760
27480
|
const { command, args } = splitCommandLine(input.weacpxCommand);
|
|
26761
27481
|
return {
|
|
26762
|
-
name: "weacpx
|
|
27482
|
+
name: "weacpx",
|
|
26763
27483
|
type: "stdio",
|
|
26764
27484
|
command,
|
|
26765
27485
|
args: [
|
|
@@ -26956,6 +27676,76 @@ function permissionModeToFlag(permissionMode) {
|
|
|
26956
27676
|
}
|
|
26957
27677
|
}
|
|
26958
27678
|
|
|
27679
|
+
// src/transport/agent-session-list.ts
|
|
27680
|
+
import path15 from "node:path";
|
|
27681
|
+
function isUnknownFilterCwdOption(output) {
|
|
27682
|
+
return /(?:unknown|unrecognized) option/i.test(output) && output.includes("--filter-cwd");
|
|
27683
|
+
}
|
|
27684
|
+
async function runAgentSessionList(options) {
|
|
27685
|
+
let result = await options.runList(true);
|
|
27686
|
+
let filterLocally = false;
|
|
27687
|
+
if (result.code !== 0 && options.filterCwd && isUnknownFilterCwdOption(result.stdout + result.stderr)) {
|
|
27688
|
+
result = await options.runList(false);
|
|
27689
|
+
filterLocally = true;
|
|
27690
|
+
}
|
|
27691
|
+
if (result.code !== 0) {
|
|
27692
|
+
if ((result.stdout + result.stderr).includes("sessionCapabilities.list")) {
|
|
27693
|
+
return;
|
|
27694
|
+
}
|
|
27695
|
+
throw new Error(options.formatError(result));
|
|
27696
|
+
}
|
|
27697
|
+
return parseAgentSessionListOutput(result.stdout, filterLocally ? options.filterCwd : undefined);
|
|
27698
|
+
}
|
|
27699
|
+
function parseAgentSessionListOutput(stdout2, filterCwd) {
|
|
27700
|
+
let parsed;
|
|
27701
|
+
try {
|
|
27702
|
+
parsed = JSON.parse(stdout2);
|
|
27703
|
+
} catch {
|
|
27704
|
+
throw new Error("failed to parse acpx sessions list output");
|
|
27705
|
+
}
|
|
27706
|
+
if (!isAgentSessionListResult(parsed)) {
|
|
27707
|
+
return;
|
|
27708
|
+
}
|
|
27709
|
+
return filterCwd ? filterAgentSessionListByCwd(parsed, filterCwd) : parsed;
|
|
27710
|
+
}
|
|
27711
|
+
function isAgentSessionListResult(value) {
|
|
27712
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
27713
|
+
return false;
|
|
27714
|
+
const record3 = value;
|
|
27715
|
+
if (record3.source !== "agent" || !Array.isArray(record3.sessions))
|
|
27716
|
+
return false;
|
|
27717
|
+
return record3.sessions.every((session) => {
|
|
27718
|
+
if (!session || typeof session !== "object" || Array.isArray(session))
|
|
27719
|
+
return false;
|
|
27720
|
+
const item = session;
|
|
27721
|
+
return typeof item.sessionId === "string";
|
|
27722
|
+
});
|
|
27723
|
+
}
|
|
27724
|
+
function filterAgentSessionListByCwd(result, cwd) {
|
|
27725
|
+
return {
|
|
27726
|
+
...result,
|
|
27727
|
+
sessions: result.sessions.filter((session) => session.cwd && sameAgentSessionCwd(session.cwd, cwd))
|
|
27728
|
+
};
|
|
27729
|
+
}
|
|
27730
|
+
function sameAgentSessionCwd(left, right) {
|
|
27731
|
+
const normalizedLeft = normalizeAgentSessionCwd(left);
|
|
27732
|
+
const normalizedRight = normalizeAgentSessionCwd(right);
|
|
27733
|
+
if (isWindowsLikePath2(normalizedLeft) || isWindowsLikePath2(normalizedRight)) {
|
|
27734
|
+
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
27735
|
+
}
|
|
27736
|
+
return normalizedLeft === normalizedRight;
|
|
27737
|
+
}
|
|
27738
|
+
function normalizeAgentSessionCwd(input) {
|
|
27739
|
+
if (isWindowsLikePath2(input)) {
|
|
27740
|
+
return path15.win32.normalize(input).replace(/\\/g, "/");
|
|
27741
|
+
}
|
|
27742
|
+
return path15.posix.normalize(input.replace(/\\/g, "/"));
|
|
27743
|
+
}
|
|
27744
|
+
function isWindowsLikePath2(input) {
|
|
27745
|
+
return /^[a-zA-Z]:[\\/]/.test(input) || input.startsWith("\\\\");
|
|
27746
|
+
}
|
|
27747
|
+
var init_agent_session_list = () => {};
|
|
27748
|
+
|
|
26959
27749
|
// src/transport/acpx-cli/acpx-cli-transport.ts
|
|
26960
27750
|
import { createRequire as createRequire5 } from "node:module";
|
|
26961
27751
|
import { spawn as spawn9 } from "node:child_process";
|
|
@@ -27065,6 +27855,23 @@ class AcpxCliTransport {
|
|
|
27065
27855
|
timeoutMs: this.sessionInitTimeoutMs
|
|
27066
27856
|
});
|
|
27067
27857
|
}
|
|
27858
|
+
async listAgentSessions(query) {
|
|
27859
|
+
return await runAgentSessionList({
|
|
27860
|
+
filterCwd: query.filterCwd,
|
|
27861
|
+
runList: async (includeFilterCwd) => {
|
|
27862
|
+
const args = this.buildAgentQueryArgs(query, "json", [
|
|
27863
|
+
"sessions",
|
|
27864
|
+
"list",
|
|
27865
|
+
...includeFilterCwd && query.filterCwd ? ["--filter-cwd", query.filterCwd] : [],
|
|
27866
|
+
...query.cursor ? ["--cursor", query.cursor] : []
|
|
27867
|
+
]);
|
|
27868
|
+
return await this.runCommandWithTimeout(this.runCommand, args, {
|
|
27869
|
+
timeoutMs: this.sessionInitTimeoutMs
|
|
27870
|
+
});
|
|
27871
|
+
},
|
|
27872
|
+
formatError: (result) => normalizeCommandError(result) ?? `command failed with exit code ${result.code}`
|
|
27873
|
+
});
|
|
27874
|
+
}
|
|
27068
27875
|
async tailSessionHistory(session, lines) {
|
|
27069
27876
|
const candidates = [
|
|
27070
27877
|
["sessions", "history", "quiet", "-s", session.transportSession, String(lines)],
|
|
@@ -27133,6 +27940,20 @@ ${baseText}` : "" };
|
|
|
27133
27940
|
message: output.trim()
|
|
27134
27941
|
};
|
|
27135
27942
|
}
|
|
27943
|
+
async resumeAgentSession(session, agentSessionId) {
|
|
27944
|
+
const args = this.buildArgs(session, [
|
|
27945
|
+
"sessions",
|
|
27946
|
+
"new",
|
|
27947
|
+
"--name",
|
|
27948
|
+
session.transportSession,
|
|
27949
|
+
"--resume-session",
|
|
27950
|
+
agentSessionId
|
|
27951
|
+
]);
|
|
27952
|
+
const runResume = session.agentCommand ? this.run : this.runWithPty;
|
|
27953
|
+
await runResume.call(this, args, {
|
|
27954
|
+
timeoutMs: this.sessionInitTimeoutMs
|
|
27955
|
+
});
|
|
27956
|
+
}
|
|
27136
27957
|
async updatePermissionPolicy(policy) {
|
|
27137
27958
|
this.permissionMode = policy.permissionMode;
|
|
27138
27959
|
this.nonInteractivePermissions = policy.nonInteractivePermissions;
|
|
@@ -27366,6 +28187,13 @@ ${baseText}` : "" };
|
|
|
27366
28187
|
}
|
|
27367
28188
|
return [...prefix, session.agent, ...tail2];
|
|
27368
28189
|
}
|
|
28190
|
+
buildAgentQueryArgs(query, format, tail2) {
|
|
28191
|
+
const prefix = ["--format", format, "--cwd", query.cwd, ...this.buildPermissionArgs()];
|
|
28192
|
+
if (query.agentCommand) {
|
|
28193
|
+
return [...prefix, "--agent", query.agentCommand, ...tail2];
|
|
28194
|
+
}
|
|
28195
|
+
return [...prefix, query.agent, ...tail2];
|
|
28196
|
+
}
|
|
27369
28197
|
buildPromptArgs(session, text, promptFile) {
|
|
27370
28198
|
const prefix = [
|
|
27371
28199
|
"--format",
|
|
@@ -27431,6 +28259,7 @@ var init_acpx_cli_transport = __esm(() => {
|
|
|
27431
28259
|
init_node_pty_helper();
|
|
27432
28260
|
init_terminate_process_tree();
|
|
27433
28261
|
init_acpx_queue_owner_launcher();
|
|
28262
|
+
init_agent_session_list();
|
|
27434
28263
|
require4 = createRequire5(import.meta.url);
|
|
27435
28264
|
});
|
|
27436
28265
|
|
|
@@ -27644,6 +28473,9 @@ class MessageChannelRegistry {
|
|
|
27644
28473
|
}
|
|
27645
28474
|
await channel.sendScheduledMessage(input);
|
|
27646
28475
|
}
|
|
28476
|
+
nativeSessionListFormat(chatKey) {
|
|
28477
|
+
return this.getByChatKey(chatKey)?.nativeSessionListFormat ?? "table";
|
|
28478
|
+
}
|
|
27647
28479
|
createConsumerLocks() {
|
|
27648
28480
|
const result = [];
|
|
27649
28481
|
for (const channel of this.channels.values()) {
|
|
@@ -28228,7 +29060,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
28228
29060
|
listScheduledTasksFromRoute: async (input) => await listScheduledTasksFromRoute(input, { state, scheduled: scheduledService }),
|
|
28229
29061
|
cancelScheduledTaskFromRoute: async (input) => await cancelScheduledTaskFromRoute(input, { state, scheduled: scheduledService })
|
|
28230
29062
|
});
|
|
28231
|
-
const router = new CommandRouter(sessions, transport, config2, configStore, logger2, undefined, orchestration, quota, scheduledService, deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : undefined);
|
|
29063
|
+
const router = new CommandRouter(sessions, transport, config2, configStore, logger2, undefined, orchestration, quota, scheduledService, deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : undefined, deps.channel?.nativeSessionListFormat ? deps.channel.nativeSessionListFormat.bind(deps.channel) : undefined);
|
|
28232
29064
|
const agent = new ConsoleAgent(router, logger2);
|
|
28233
29065
|
const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
|
|
28234
29066
|
dispatchTask: buildScheduledDispatchTask({
|
|
@@ -28819,107 +29651,107 @@ async function checkRuntime(options = {}) {
|
|
|
28819
29651
|
}
|
|
28820
29652
|
function createRuntimeFsProbe() {
|
|
28821
29653
|
return {
|
|
28822
|
-
stat: async (
|
|
28823
|
-
access: async (
|
|
29654
|
+
stat: async (path16) => await stat3(path16),
|
|
29655
|
+
access: async (path16, mode) => await access4(path16, mode)
|
|
28824
29656
|
};
|
|
28825
29657
|
}
|
|
28826
|
-
async function checkDirectoryCreatable(label,
|
|
29658
|
+
async function checkDirectoryCreatable(label, path16, probe, platform) {
|
|
28827
29659
|
try {
|
|
28828
|
-
const stats = await probe.stat(
|
|
29660
|
+
const stats = await probe.stat(path16);
|
|
28829
29661
|
if (!stats.isDirectory()) {
|
|
28830
29662
|
return {
|
|
28831
29663
|
ok: false,
|
|
28832
|
-
detail: `${label}: ${
|
|
29664
|
+
detail: `${label}: ${path16} (exists but is not a directory)`
|
|
28833
29665
|
};
|
|
28834
29666
|
}
|
|
28835
|
-
await probe.access(
|
|
29667
|
+
await probe.access(path16, directoryAccessMode(platform));
|
|
28836
29668
|
return {
|
|
28837
29669
|
ok: true,
|
|
28838
|
-
detail: `${label}: ${
|
|
29670
|
+
detail: `${label}: ${path16} (writable)`
|
|
28839
29671
|
};
|
|
28840
29672
|
} catch (error2) {
|
|
28841
29673
|
if (!isMissingPathError(error2)) {
|
|
28842
29674
|
return {
|
|
28843
29675
|
ok: false,
|
|
28844
|
-
detail: `${label}: ${
|
|
29676
|
+
detail: `${label}: ${path16} (unusable: ${formatError6(error2)})`
|
|
28845
29677
|
};
|
|
28846
29678
|
}
|
|
28847
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
29679
|
+
const parentCheck = await checkCreatableAncestorDirectory(path16, probe, platform);
|
|
28848
29680
|
if (!parentCheck.ok) {
|
|
28849
29681
|
return {
|
|
28850
29682
|
ok: false,
|
|
28851
|
-
detail: `${label}: ${
|
|
29683
|
+
detail: `${label}: ${path16} (parent not writable: ${parentCheck.blockingPath})`
|
|
28852
29684
|
};
|
|
28853
29685
|
}
|
|
28854
29686
|
return {
|
|
28855
29687
|
ok: true,
|
|
28856
|
-
detail: `${label}: ${
|
|
29688
|
+
detail: `${label}: ${path16} (creatable via ${parentCheck.creatableFrom})`
|
|
28857
29689
|
};
|
|
28858
29690
|
}
|
|
28859
29691
|
}
|
|
28860
|
-
async function checkFileCreatable(label,
|
|
29692
|
+
async function checkFileCreatable(label, path16, probe, platform) {
|
|
28861
29693
|
try {
|
|
28862
|
-
const stats = await probe.stat(
|
|
29694
|
+
const stats = await probe.stat(path16);
|
|
28863
29695
|
if (stats.isDirectory()) {
|
|
28864
29696
|
return {
|
|
28865
29697
|
ok: false,
|
|
28866
|
-
detail: `${label}: ${
|
|
29698
|
+
detail: `${label}: ${path16} (exists but is a directory)`
|
|
28867
29699
|
};
|
|
28868
29700
|
}
|
|
28869
|
-
await probe.access(
|
|
29701
|
+
await probe.access(path16, constants.W_OK);
|
|
28870
29702
|
return {
|
|
28871
29703
|
ok: true,
|
|
28872
|
-
detail: `${label}: ${
|
|
29704
|
+
detail: `${label}: ${path16} (writable)`
|
|
28873
29705
|
};
|
|
28874
29706
|
} catch (error2) {
|
|
28875
29707
|
if (!isMissingPathError(error2)) {
|
|
28876
29708
|
return {
|
|
28877
29709
|
ok: false,
|
|
28878
|
-
detail: `${label}: ${
|
|
29710
|
+
detail: `${label}: ${path16} (unusable: ${formatError6(error2)})`
|
|
28879
29711
|
};
|
|
28880
29712
|
}
|
|
28881
|
-
const parentCheck = await checkCreatableAncestorDirectory(dirname14(
|
|
29713
|
+
const parentCheck = await checkCreatableAncestorDirectory(dirname14(path16), probe, platform);
|
|
28882
29714
|
if (!parentCheck.ok) {
|
|
28883
29715
|
return {
|
|
28884
29716
|
ok: false,
|
|
28885
|
-
detail: `${label}: ${
|
|
29717
|
+
detail: `${label}: ${path16} (parent not writable: ${parentCheck.blockingPath})`
|
|
28886
29718
|
};
|
|
28887
29719
|
}
|
|
28888
29720
|
return {
|
|
28889
29721
|
ok: true,
|
|
28890
|
-
detail: `${label}: ${
|
|
29722
|
+
detail: `${label}: ${path16} (creatable via ${parentCheck.creatableFrom})`
|
|
28891
29723
|
};
|
|
28892
29724
|
}
|
|
28893
29725
|
}
|
|
28894
|
-
async function checkCreatableAncestorDirectory(
|
|
29726
|
+
async function checkCreatableAncestorDirectory(path16, probe, platform) {
|
|
28895
29727
|
try {
|
|
28896
|
-
const stats = await probe.stat(
|
|
29728
|
+
const stats = await probe.stat(path16);
|
|
28897
29729
|
if (!stats.isDirectory()) {
|
|
28898
29730
|
return {
|
|
28899
29731
|
ok: false,
|
|
28900
|
-
creatableFrom:
|
|
28901
|
-
blockingPath:
|
|
29732
|
+
creatableFrom: path16,
|
|
29733
|
+
blockingPath: path16
|
|
28902
29734
|
};
|
|
28903
29735
|
}
|
|
28904
|
-
await probe.access(
|
|
29736
|
+
await probe.access(path16, directoryAccessMode(platform));
|
|
28905
29737
|
return {
|
|
28906
29738
|
ok: true,
|
|
28907
|
-
creatableFrom:
|
|
29739
|
+
creatableFrom: path16
|
|
28908
29740
|
};
|
|
28909
29741
|
} catch (error2) {
|
|
28910
29742
|
if (!isMissingPathError(error2)) {
|
|
28911
29743
|
return {
|
|
28912
29744
|
ok: false,
|
|
28913
|
-
creatableFrom:
|
|
28914
|
-
blockingPath:
|
|
29745
|
+
creatableFrom: path16,
|
|
29746
|
+
blockingPath: path16
|
|
28915
29747
|
};
|
|
28916
29748
|
}
|
|
28917
|
-
const parent = dirname14(
|
|
28918
|
-
if (parent ===
|
|
29749
|
+
const parent = dirname14(path16);
|
|
29750
|
+
if (parent === path16) {
|
|
28919
29751
|
return {
|
|
28920
29752
|
ok: false,
|
|
28921
|
-
creatableFrom:
|
|
28922
|
-
blockingPath:
|
|
29753
|
+
creatableFrom: path16,
|
|
29754
|
+
blockingPath: path16
|
|
28923
29755
|
};
|
|
28924
29756
|
}
|
|
28925
29757
|
const parentCheck = await checkCreatableAncestorDirectory(parent, probe, platform);
|
|
@@ -42176,11 +43008,11 @@ function buildWeacpxMcpToolRegistry(input) {
|
|
|
42176
43008
|
if (internalSessionTools && !isExternalCoordinator && !sourceHandle) {
|
|
42177
43009
|
tools.push({
|
|
42178
43010
|
name: "scheduled_create",
|
|
42179
|
-
description: "
|
|
43011
|
+
description: "Schedule a one-shot task to run a natural-language message at a future time, using the recorded chat route. By default — and like /later — the task runs in a FRESH TEMPORARY session (it snapshots the current agent and workspace but starts with brand-new history and is destroyed after running, so it does not pollute this conversation); the reply is still pushed back to this chat. Provide only timeText and message and OMIT mode to get this default. Routing, session, and account are resolved by weacpx.",
|
|
42180
43012
|
inputSchema: exports_external.object({
|
|
42181
43013
|
timeText: exports_external.string().min(1).describe("Time expression, e.g. 'in 2h', '30分钟后', 'tomorrow 09:00', or '周五 09:00'."),
|
|
42182
|
-
message: exports_external.string().min(1).describe("Natural-language message to
|
|
42183
|
-
mode: scheduledModeSchema.describe("
|
|
43014
|
+
message: exports_external.string().min(1).describe("Natural-language message to run at the scheduled time."),
|
|
43015
|
+
mode: scheduledModeSchema.describe("Optional; leave UNSET for the default temporary session (recommended). Set 'bound' ONLY when the user explicitly asks for the task to run inside this conversation's current session and share its context. 'temp' forces the temporary session.").optional()
|
|
42184
43016
|
}).strict(),
|
|
42185
43017
|
handler: async (args) => await asToolResult(async () => {
|
|
42186
43018
|
const input2 = args;
|
|
@@ -42655,7 +43487,7 @@ function createWeacpxMcpServer(options) {
|
|
|
42655
43487
|
const taskOptionsById = new Map;
|
|
42656
43488
|
const watchTasksById = new Map;
|
|
42657
43489
|
const server = new Server({
|
|
42658
|
-
name: "weacpx
|
|
43490
|
+
name: "weacpx",
|
|
42659
43491
|
version: readVersion()
|
|
42660
43492
|
}, {
|
|
42661
43493
|
capabilities: {
|