@ganglion/xacpx 0.20.2 → 0.20.3-beta.1
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 +38 -11
- package/dist/cli.js +176 -85
- package/dist/i18n/types.d.ts +1 -0
- package/dist/plugin-api.js +2 -0
- package/dist/sessions/session-service.d.ts +24 -0
- package/package.json +1 -1
|
@@ -3524,18 +3524,19 @@ function buildToolUseEvent(update, driver) {
|
|
|
3524
3524
|
function normalizeCursorPlanUpdate(state, update) {
|
|
3525
3525
|
if (state.driver !== "cursor")
|
|
3526
3526
|
return;
|
|
3527
|
-
|
|
3528
|
-
if (!CURSOR_PLAN_TOOL_NAMES.has(title))
|
|
3527
|
+
if (!CURSOR_PLAN_TOOL_NAMES.has(cursorToolIdentity(update)))
|
|
3529
3528
|
return;
|
|
3530
3529
|
const input = cursorToolInput(update.rawInput);
|
|
3531
|
-
|
|
3530
|
+
const output = cursorToolInput(update.rawOutput);
|
|
3531
|
+
const todos = readCursorTodoList(input) ?? readCursorTodoList(output);
|
|
3532
|
+
if (todos === undefined)
|
|
3532
3533
|
return;
|
|
3533
|
-
const merge = input
|
|
3534
|
-
if (
|
|
3534
|
+
const merge = input?.merge === true || output?.merge === true;
|
|
3535
|
+
if (todos.length === 0) {
|
|
3535
3536
|
state.cursorPlanEntries.clear();
|
|
3536
3537
|
return [];
|
|
3537
3538
|
}
|
|
3538
|
-
const patches =
|
|
3539
|
+
const patches = todos.map((todo, index) => parseCursorPlanTodo(todo, index)).filter((todo) => todo !== undefined);
|
|
3539
3540
|
if (patches.length === 0)
|
|
3540
3541
|
return;
|
|
3541
3542
|
if (!merge)
|
|
@@ -3555,6 +3556,15 @@ function normalizeCursorPlanUpdate(state, update) {
|
|
|
3555
3556
|
}
|
|
3556
3557
|
return [...state.cursorPlanEntries.values()];
|
|
3557
3558
|
}
|
|
3559
|
+
function readCursorTodoList(input) {
|
|
3560
|
+
if (!input)
|
|
3561
|
+
return;
|
|
3562
|
+
for (const key of ["todos", "todoList"]) {
|
|
3563
|
+
if (Array.isArray(input[key]))
|
|
3564
|
+
return input[key];
|
|
3565
|
+
}
|
|
3566
|
+
return;
|
|
3567
|
+
}
|
|
3558
3568
|
function parseCursorPlanTodo(value, index) {
|
|
3559
3569
|
if (!isRecord2(value))
|
|
3560
3570
|
return;
|
|
@@ -3612,6 +3622,11 @@ function cursorToolInput(rawInput) {
|
|
|
3612
3622
|
function normalizeCursorToolName(title) {
|
|
3613
3623
|
return (title ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
3614
3624
|
}
|
|
3625
|
+
function cursorToolIdentity(update) {
|
|
3626
|
+
const input = cursorToolInput(update.rawInput);
|
|
3627
|
+
const declared = typeof input?.[CURSOR_TOOL_NAME_KEY] === "string" ? input[CURSOR_TOOL_NAME_KEY] : undefined;
|
|
3628
|
+
return normalizeCursorToolName(declared ?? update.title);
|
|
3629
|
+
}
|
|
3615
3630
|
function normalizeToolKind(update, driver) {
|
|
3616
3631
|
const kindRaw = update?.kind?.trim().toLowerCase() ?? "";
|
|
3617
3632
|
switch (kindRaw) {
|
|
@@ -3624,12 +3639,14 @@ function normalizeToolKind(update, driver) {
|
|
|
3624
3639
|
}
|
|
3625
3640
|
if (driver !== "cursor")
|
|
3626
3641
|
return "other";
|
|
3627
|
-
switch (
|
|
3642
|
+
switch (cursorToolIdentity(update ?? {})) {
|
|
3628
3643
|
case "read":
|
|
3629
3644
|
case "readfile":
|
|
3630
3645
|
return "read";
|
|
3631
3646
|
case "grep":
|
|
3632
3647
|
case "glob":
|
|
3648
|
+
case "find":
|
|
3649
|
+
case "codebasesearch":
|
|
3633
3650
|
case "search":
|
|
3634
3651
|
return "search";
|
|
3635
3652
|
case "shell":
|
|
@@ -3647,6 +3664,8 @@ function normalizeToolKind(update, driver) {
|
|
|
3647
3664
|
case "runsubagent":
|
|
3648
3665
|
case "switchmode":
|
|
3649
3666
|
case "todowrite":
|
|
3667
|
+
case "updatetodos":
|
|
3668
|
+
case "todoread":
|
|
3650
3669
|
case "createplan":
|
|
3651
3670
|
case "updateplan":
|
|
3652
3671
|
case "plan":
|
|
@@ -3656,8 +3675,7 @@ function normalizeToolKind(update, driver) {
|
|
|
3656
3675
|
}
|
|
3657
3676
|
}
|
|
3658
3677
|
function isCursorSubagentInput(rawInput, title) {
|
|
3659
|
-
|
|
3660
|
-
if (!CURSOR_SUBAGENT_TOOL_NAMES.has(normalizedTitle))
|
|
3678
|
+
if (!CURSOR_SUBAGENT_TOOL_NAMES.has(cursorToolIdentity({ title, rawInput })))
|
|
3661
3679
|
return false;
|
|
3662
3680
|
const input = cursorToolInput(rawInput);
|
|
3663
3681
|
return input !== undefined && readFirstString(input, ["subagent_type", "subagentType", "agent", "agentType", "prompt", "instructions"]) !== undefined;
|
|
@@ -3832,7 +3850,7 @@ function isGenericToolTitle(kind, title) {
|
|
|
3832
3850
|
}
|
|
3833
3851
|
return false;
|
|
3834
3852
|
}
|
|
3835
|
-
var SENTENCE_TERMINAL_AT_END, PARAGRAPH_BOUNDARY_AT_END, PARAGRAPH_BOUNDARY_AT_START, LINE_BREAK_AT_END, LINE_BREAK_AT_START, PARTIAL_CRLF_PARAGRAPH_BOUNDARY_AT_END, CURSOR_PLAN_TOOL_NAMES, CURSOR_SUBAGENT_TOOL_NAMES, USAGE_BREAKDOWN_FIELDS;
|
|
3853
|
+
var SENTENCE_TERMINAL_AT_END, PARAGRAPH_BOUNDARY_AT_END, PARAGRAPH_BOUNDARY_AT_START, LINE_BREAK_AT_END, LINE_BREAK_AT_START, PARTIAL_CRLF_PARAGRAPH_BOUNDARY_AT_END, CURSOR_TOOL_NAME_KEY = "_toolName", CURSOR_PLAN_TOOL_NAMES, CURSOR_SUBAGENT_TOOL_NAMES, USAGE_BREAKDOWN_FIELDS;
|
|
3836
3854
|
var init_streaming_prompt = __esm(() => {
|
|
3837
3855
|
init_background_followup();
|
|
3838
3856
|
init_tool_kind_emoji();
|
|
@@ -3842,7 +3860,14 @@ var init_streaming_prompt = __esm(() => {
|
|
|
3842
3860
|
LINE_BREAK_AT_END = /\r?\n[\t ]*$/;
|
|
3843
3861
|
LINE_BREAK_AT_START = /^[\t ]*\r?\n/;
|
|
3844
3862
|
PARTIAL_CRLF_PARAGRAPH_BOUNDARY_AT_END = /\r?\n[\t ]*\r$/;
|
|
3845
|
-
CURSOR_PLAN_TOOL_NAMES = new Set([
|
|
3863
|
+
CURSOR_PLAN_TOOL_NAMES = new Set([
|
|
3864
|
+
"todowrite",
|
|
3865
|
+
"createplan",
|
|
3866
|
+
"updateplan",
|
|
3867
|
+
"plan",
|
|
3868
|
+
"updatetodos",
|
|
3869
|
+
"todoread"
|
|
3870
|
+
]);
|
|
3846
3871
|
CURSOR_SUBAGENT_TOOL_NAMES = new Set(["task", "delegate", "runsubagent", "subagent"]);
|
|
3847
3872
|
USAGE_BREAKDOWN_FIELDS = [
|
|
3848
3873
|
["inputTokens", ["inputTokens", "input_tokens"]],
|
|
@@ -4034,6 +4059,7 @@ var init_session = __esm(() => {
|
|
|
4034
4059
|
`Switch to it with /use ${alias}, or remove it first with /session rm ${alias}.`
|
|
4035
4060
|
].join(`
|
|
4036
4061
|
`),
|
|
4062
|
+
sessionAliasCollided: (desiredAlias, finalAlias) => `The requested name "${desiredAlias}" is already in use (by an archived session). Creating the new session as "${finalAlias}" instead.`,
|
|
4037
4063
|
sessionAttachNotFound: (alias, agent, workspace) => [
|
|
4038
4064
|
"No existing session found to attach.",
|
|
4039
4065
|
`Check the session name and retry: /session attach ${alias} --agent ${agent} --ws ${workspace} --name <session-name>`
|
|
@@ -5159,6 +5185,7 @@ var init_session2 = __esm(() => {
|
|
|
5159
5185
|
`发送 /use ${alias} 切换到它,或先执行 /session rm ${alias} 删除后再创建。`
|
|
5160
5186
|
].join(`
|
|
5161
5187
|
`),
|
|
5188
|
+
sessionAliasCollided: (desiredAlias, finalAlias) => `你指定的会话名「${desiredAlias}」已被其他会话(含已睡眠/归档)占用,已自动改为「${finalAlias}」创建。`,
|
|
5162
5189
|
sessionAttachNotFound: (alias, agent2, workspace2) => [
|
|
5163
5190
|
"没有找到可绑定的已有会话。",
|
|
5164
5191
|
`请确认会话名是否正确,然后重新执行:/session attach ${alias} --agent ${agent2} --ws ${workspace2} --name <会话名>`
|
package/dist/cli.js
CHANGED
|
@@ -97,6 +97,7 @@ var init_session = __esm(() => {
|
|
|
97
97
|
`Switch to it with /use ${alias}, or remove it first with /session rm ${alias}.`
|
|
98
98
|
].join(`
|
|
99
99
|
`),
|
|
100
|
+
sessionAliasCollided: (desiredAlias, finalAlias) => `The requested name "${desiredAlias}" is already in use (by an archived session). Creating the new session as "${finalAlias}" instead.`,
|
|
100
101
|
sessionAttachNotFound: (alias, agent, workspace) => [
|
|
101
102
|
"No existing session found to attach.",
|
|
102
103
|
`Check the session name and retry: /session attach ${alias} --agent ${agent} --ws ${workspace} --name <session-name>`
|
|
@@ -1222,6 +1223,7 @@ var init_session2 = __esm(() => {
|
|
|
1222
1223
|
`发送 /use ${alias} 切换到它,或先执行 /session rm ${alias} 删除后再创建。`
|
|
1223
1224
|
].join(`
|
|
1224
1225
|
`),
|
|
1226
|
+
sessionAliasCollided: (desiredAlias, finalAlias) => `你指定的会话名「${desiredAlias}」已被其他会话(含已睡眠/归档)占用,已自动改为「${finalAlias}」创建。`,
|
|
1225
1227
|
sessionAttachNotFound: (alias, agent2, workspace2) => [
|
|
1226
1228
|
"没有找到可绑定的已有会话。",
|
|
1227
1229
|
`请确认会话名是否正确,然后重新执行:/session attach ${alias} --agent ${agent2} --ws ${workspace2} --name <会话名>`
|
|
@@ -41205,20 +41207,19 @@ async function handleSessions(context, chatKey) {
|
|
|
41205
41207
|
async function handleSessionNew(context, chatKey, alias, agent3, workspace3, model) {
|
|
41206
41208
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
41207
41209
|
const internalAlias = scopeDisplayAliasToInternal(channelId, alias);
|
|
41208
|
-
const
|
|
41209
|
-
if (
|
|
41210
|
-
return { text: t().session.sessionAlreadyExists(alias, existing.agent, existing.workspace) };
|
|
41211
|
-
}
|
|
41212
|
-
const releaseAliasReservation = context.sessions.tryReserveNewSessionAlias(internalAlias);
|
|
41213
|
-
if (!releaseAliasReservation) {
|
|
41210
|
+
const reserved = context.sessions.tryReserveFreeSessionAlias(internalAlias);
|
|
41211
|
+
if (!reserved) {
|
|
41214
41212
|
const claimed = context.sessions.getResolvedSessionByInternalAlias(internalAlias);
|
|
41215
41213
|
return {
|
|
41216
41214
|
text: claimed ? t().session.sessionAlreadyExists(alias, claimed.agent, claimed.workspace) : t().session.sessionAlreadyExists(alias, agent3, workspace3)
|
|
41217
41215
|
};
|
|
41218
41216
|
}
|
|
41217
|
+
const { alias: finalInternalAlias, release: releaseAliasReservation } = reserved;
|
|
41218
|
+
const finalDisplayAlias = toDisplaySessionAlias(finalInternalAlias);
|
|
41219
|
+
const aliasWasAdjusted = finalInternalAlias !== internalAlias;
|
|
41219
41220
|
try {
|
|
41220
|
-
const stableTransportSession = `${workspace3}:${
|
|
41221
|
-
const session3 = context.lifecycle.resolveSession(
|
|
41221
|
+
const stableTransportSession = `${workspace3}:${finalInternalAlias}`;
|
|
41222
|
+
const session3 = context.lifecycle.resolveSession(finalInternalAlias, agent3, workspace3, context.sessions.buildFreshTransportSession(stableTransportSession));
|
|
41222
41223
|
const normalizedModel = model?.trim();
|
|
41223
41224
|
if (normalizedModel) {
|
|
41224
41225
|
session3.model = normalizedModel;
|
|
@@ -41234,18 +41235,27 @@ async function handleSessionNew(context, chatKey, alias, agent3, workspace3, mod
|
|
|
41234
41235
|
} catch (error2) {
|
|
41235
41236
|
return context.recovery.renderSessionCreationError(session3, error2);
|
|
41236
41237
|
}
|
|
41237
|
-
await context.sessions.attachSession(
|
|
41238
|
+
await context.sessions.attachSession(finalInternalAlias, agent3, workspace3, session3.transportSession, session3.agentCommand, session3.acpxAgent, session3.agentArgv);
|
|
41238
41239
|
if (normalizedModel) {
|
|
41239
|
-
await context.sessions.setSessionModel(
|
|
41240
|
+
await context.sessions.setSessionModel(finalInternalAlias, normalizedModel);
|
|
41240
41241
|
}
|
|
41241
|
-
await context.sessions.useSession(chatKey,
|
|
41242
|
-
await refreshSessionTransportAgentCommandBestEffort(context,
|
|
41242
|
+
await context.sessions.useSession(chatKey, finalInternalAlias);
|
|
41243
|
+
await refreshSessionTransportAgentCommandBestEffort(context, finalInternalAlias, "session.agent_command_refresh_failed");
|
|
41243
41244
|
await context.logger.info("session.created", "created and selected logical session", {
|
|
41244
|
-
alias:
|
|
41245
|
+
alias: finalInternalAlias,
|
|
41245
41246
|
agent: agent3,
|
|
41246
41247
|
workspace: workspace3
|
|
41247
41248
|
});
|
|
41248
|
-
|
|
41249
|
+
if (aliasWasAdjusted) {
|
|
41250
|
+
return {
|
|
41251
|
+
text: [
|
|
41252
|
+
t().session.sessionAliasCollided(alias, finalDisplayAlias),
|
|
41253
|
+
t().session.sessionCreated(finalDisplayAlias)
|
|
41254
|
+
].join(`
|
|
41255
|
+
`)
|
|
41256
|
+
};
|
|
41257
|
+
}
|
|
41258
|
+
return { text: t().session.sessionCreated(finalDisplayAlias) };
|
|
41249
41259
|
} finally {
|
|
41250
41260
|
await releaseTransportReservation();
|
|
41251
41261
|
}
|
|
@@ -42845,65 +42855,67 @@ async function handleSessionShortcutCommand(context, ops, chatKey, agent3, targe
|
|
|
42845
42855
|
const baseAlias = `${workspace3.name}:${agent3}`;
|
|
42846
42856
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
42847
42857
|
const scopedBase = scopeDisplayAliasToInternal(channelId, baseAlias);
|
|
42848
|
-
const
|
|
42849
|
-
const
|
|
42850
|
-
if (!createNew && await hasLogicalSession(context,
|
|
42851
|
-
await context.sessions.useSession(chatKey,
|
|
42858
|
+
const desiredAlias = scopedBase;
|
|
42859
|
+
const desiredDisplay = toDisplaySessionAlias(desiredAlias);
|
|
42860
|
+
if (!createNew && await hasLogicalSession(context, desiredAlias, chatKey)) {
|
|
42861
|
+
await context.sessions.useSession(chatKey, desiredAlias);
|
|
42852
42862
|
await context.logger.info("session.shortcut.reused", "reused existing logical session", {
|
|
42853
|
-
alias,
|
|
42863
|
+
alias: desiredAlias,
|
|
42854
42864
|
workspace: workspace3.name,
|
|
42855
42865
|
agent: agent3
|
|
42856
42866
|
});
|
|
42857
42867
|
return {
|
|
42858
42868
|
text: [
|
|
42859
|
-
t().shortcut.reuseHeader(
|
|
42869
|
+
t().shortcut.reuseHeader(desiredDisplay),
|
|
42860
42870
|
t().shortcut.reuseWorkspace(workspace3.name),
|
|
42861
|
-
t().shortcut.reuseSession(
|
|
42871
|
+
t().shortcut.reuseSession(desiredDisplay)
|
|
42862
42872
|
].join(`
|
|
42863
42873
|
`)
|
|
42864
42874
|
};
|
|
42865
42875
|
}
|
|
42866
|
-
const
|
|
42867
|
-
if (!
|
|
42868
|
-
return renderShortcutSessionCreationError(workspace3,
|
|
42876
|
+
const reserved = context.sessions.tryReserveFreeSessionAlias(desiredAlias);
|
|
42877
|
+
if (!reserved) {
|
|
42878
|
+
return renderShortcutSessionCreationError(workspace3, desiredDisplay);
|
|
42869
42879
|
}
|
|
42880
|
+
const { alias: finalAlias, release: releaseAliasReservation } = reserved;
|
|
42881
|
+
const finalDisplay = toDisplaySessionAlias(finalAlias);
|
|
42870
42882
|
try {
|
|
42871
|
-
const stableTransportSession = channelId === "weixin" ?
|
|
42872
|
-
const session3 = ops.resolveSession(
|
|
42883
|
+
const stableTransportSession = channelId === "weixin" ? finalAlias : context.sessions.buildDefaultTransportSessionForChat(chatKey, finalDisplay);
|
|
42884
|
+
const session3 = ops.resolveSession(finalAlias, agent3, workspace3.name, context.sessions.buildFreshTransportSession(stableTransportSession));
|
|
42873
42885
|
const releaseTransportReservation = await ops.reserveTransportSession(stableTransportSession);
|
|
42874
42886
|
try {
|
|
42875
42887
|
try {
|
|
42876
42888
|
await ops.ensureTransportSession(session3);
|
|
42877
42889
|
const exists = await ops.checkTransportSession(session3);
|
|
42878
42890
|
if (!exists) {
|
|
42879
|
-
return renderShortcutSessionCreationError(workspace3,
|
|
42891
|
+
return renderShortcutSessionCreationError(workspace3, finalDisplay);
|
|
42880
42892
|
}
|
|
42881
42893
|
} catch (err) {
|
|
42882
42894
|
if (err instanceof AutoInstallFailedError)
|
|
42883
42895
|
throw err;
|
|
42884
|
-
return renderShortcutSessionCreationError(workspace3,
|
|
42896
|
+
return renderShortcutSessionCreationError(workspace3, finalDisplay);
|
|
42885
42897
|
}
|
|
42886
|
-
await context.sessions.attachSession(
|
|
42887
|
-
await context.sessions.useSession(chatKey,
|
|
42898
|
+
await context.sessions.attachSession(finalAlias, agent3, workspace3.name, session3.transportSession, session3.agentCommand, session3.acpxAgent, session3.agentArgv);
|
|
42899
|
+
await context.sessions.useSession(chatKey, finalAlias);
|
|
42888
42900
|
try {
|
|
42889
|
-
await ops.refreshSessionTransportAgentCommand(
|
|
42901
|
+
await ops.refreshSessionTransportAgentCommand(finalAlias);
|
|
42890
42902
|
} catch (error2) {
|
|
42891
42903
|
await context.logger.error("session.shortcut.agent_command_refresh_failed", "failed to refresh session agent command", {
|
|
42892
|
-
alias,
|
|
42904
|
+
alias: finalAlias,
|
|
42893
42905
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
42894
42906
|
});
|
|
42895
42907
|
}
|
|
42896
42908
|
await context.logger.info("session.shortcut.created", "created new logical session from shortcut", {
|
|
42897
|
-
alias,
|
|
42909
|
+
alias: finalAlias,
|
|
42898
42910
|
workspace: workspace3.name,
|
|
42899
42911
|
agent: agent3,
|
|
42900
42912
|
workspaceReused: workspace3.reused
|
|
42901
42913
|
});
|
|
42902
42914
|
return {
|
|
42903
42915
|
text: [
|
|
42904
|
-
t().shortcut.createdHeader(
|
|
42916
|
+
t().shortcut.createdHeader(finalDisplay),
|
|
42905
42917
|
workspace3.reused ? t().shortcut.createdReusedWorkspace(workspace3.name) : t().shortcut.createdNewWorkspace(workspace3.name, workspace3.cwd),
|
|
42906
|
-
t().shortcut.createdNewSession(
|
|
42918
|
+
t().shortcut.createdNewSession(finalDisplay)
|
|
42907
42919
|
].join(`
|
|
42908
42920
|
`)
|
|
42909
42921
|
};
|
|
@@ -42950,16 +42962,6 @@ async function resolveShortcutWorkspace(context, target) {
|
|
|
42950
42962
|
reused: false
|
|
42951
42963
|
};
|
|
42952
42964
|
}
|
|
42953
|
-
async function allocateUniqueSessionAlias(context, baseAlias, chatKey) {
|
|
42954
|
-
if (!await hasLogicalSession(context, baseAlias, chatKey)) {
|
|
42955
|
-
return baseAlias;
|
|
42956
|
-
}
|
|
42957
|
-
let suffix = 2;
|
|
42958
|
-
while (await hasLogicalSession(context, `${baseAlias}-${suffix}`, chatKey)) {
|
|
42959
|
-
suffix += 1;
|
|
42960
|
-
}
|
|
42961
|
-
return `${baseAlias}-${suffix}`;
|
|
42962
|
-
}
|
|
42963
42965
|
async function hasLogicalSession(context, alias, chatKey) {
|
|
42964
42966
|
const sessions = await context.sessions.listSessions(chatKey);
|
|
42965
42967
|
return sessions.some((session3) => session3.internalAlias === alias);
|
|
@@ -43082,17 +43084,43 @@ async function attachNativeSession(context, chatKey, target, session3, alias) {
|
|
|
43082
43084
|
const existing = await context.sessions.findAttachedNativeSession(chatKey, nativeTarget.agent, session3.sessionId);
|
|
43083
43085
|
if (existing) {
|
|
43084
43086
|
await context.sessions.useSession(chatKey, existing.alias);
|
|
43085
|
-
const
|
|
43087
|
+
const displayAlias = toDisplaySessionAlias(existing.alias);
|
|
43086
43088
|
return {
|
|
43087
|
-
text: t().nativeSession.alreadySwitched(nativeTarget.agentDisplayName,
|
|
43089
|
+
text: t().nativeSession.alreadySwitched(nativeTarget.agentDisplayName, displayAlias)
|
|
43088
43090
|
};
|
|
43089
43091
|
}
|
|
43092
|
+
const channelId = getChannelIdFromChatKey(chatKey);
|
|
43090
43093
|
const requestedAlias = alias?.trim() || buildDefaultNativeAlias(nativeTarget.agent, session3.sessionId);
|
|
43091
|
-
|
|
43092
|
-
|
|
43093
|
-
|
|
43094
|
-
|
|
43095
|
-
|
|
43094
|
+
let reserved = null;
|
|
43095
|
+
let finalDisplayAlias = requestedAlias;
|
|
43096
|
+
for (let attempt = 0;attempt < 5; attempt += 1) {
|
|
43097
|
+
const candidateDisplay = await allocateUniqueNativeAlias(context, chatKey, requestedAlias);
|
|
43098
|
+
const candidateInternal = scopeDisplayAliasToInternal(channelId, candidateDisplay);
|
|
43099
|
+
const release = context.sessions.tryReserveNewSessionAlias(candidateInternal);
|
|
43100
|
+
if (release) {
|
|
43101
|
+
reserved = { alias: candidateInternal, release };
|
|
43102
|
+
finalDisplayAlias = candidateDisplay;
|
|
43103
|
+
break;
|
|
43104
|
+
}
|
|
43105
|
+
}
|
|
43106
|
+
let finalInternalAlias;
|
|
43107
|
+
let releaseAliasReservation;
|
|
43108
|
+
if (reserved) {
|
|
43109
|
+
finalInternalAlias = reserved.alias;
|
|
43110
|
+
releaseAliasReservation = reserved.release;
|
|
43111
|
+
} else {
|
|
43112
|
+
const desiredInternal = scopeDisplayAliasToInternal(channelId, finalDisplayAlias);
|
|
43113
|
+
const free = context.sessions.tryReserveFreeSessionAlias(desiredInternal);
|
|
43114
|
+
if (!free) {
|
|
43115
|
+
return { text: t().session.sessionAlreadyExists(finalDisplayAlias, nativeTarget.agent, nativeTarget.workspace) };
|
|
43116
|
+
}
|
|
43117
|
+
finalInternalAlias = free.alias;
|
|
43118
|
+
releaseAliasReservation = free.release;
|
|
43119
|
+
finalDisplayAlias = toDisplaySessionAlias(finalInternalAlias);
|
|
43120
|
+
}
|
|
43121
|
+
const transportSession = context.sessions.buildDefaultTransportSessionForChat(chatKey, finalDisplayAlias);
|
|
43122
|
+
const resolvedSession = context.lifecycle.resolveSession(finalInternalAlias, nativeTarget.agent, nativeTarget.workspace, transportSession);
|
|
43123
|
+
const releaseTransportReservation = await context.lifecycle.reserveTransportSession(resolvedSession.transportSession);
|
|
43096
43124
|
try {
|
|
43097
43125
|
try {
|
|
43098
43126
|
await context.transport.resumeAgentSession(resolvedSession, session3.sessionId);
|
|
@@ -43104,7 +43132,7 @@ async function attachNativeSession(context, chatKey, target, session3, alias) {
|
|
|
43104
43132
|
return { text: t().nativeSession.attachVerificationFailed(target.agentDisplayName) };
|
|
43105
43133
|
}
|
|
43106
43134
|
await context.sessions.attachNativeSession({
|
|
43107
|
-
alias:
|
|
43135
|
+
alias: finalInternalAlias,
|
|
43108
43136
|
agent: nativeTarget.agent,
|
|
43109
43137
|
workspace: nativeTarget.workspace,
|
|
43110
43138
|
transportSession,
|
|
@@ -43115,13 +43143,17 @@ async function attachNativeSession(context, chatKey, target, session3, alias) {
|
|
|
43115
43143
|
title: session3.title,
|
|
43116
43144
|
updatedAt: session3.updatedAt
|
|
43117
43145
|
});
|
|
43118
|
-
await context.sessions.useSession(chatKey,
|
|
43119
|
-
await refreshAgentCommandBestEffort(context,
|
|
43146
|
+
await context.sessions.useSession(chatKey, finalInternalAlias);
|
|
43147
|
+
await refreshAgentCommandBestEffort(context, finalInternalAlias);
|
|
43120
43148
|
return {
|
|
43121
|
-
text: t().nativeSession.attachedAndSwitched(target.agentDisplayName, toDisplaySessionAlias(
|
|
43149
|
+
text: t().nativeSession.attachedAndSwitched(target.agentDisplayName, toDisplaySessionAlias(finalInternalAlias))
|
|
43122
43150
|
};
|
|
43123
43151
|
} finally {
|
|
43124
|
-
|
|
43152
|
+
try {
|
|
43153
|
+
await releaseTransportReservation();
|
|
43154
|
+
} finally {
|
|
43155
|
+
releaseAliasReservation();
|
|
43156
|
+
}
|
|
43125
43157
|
}
|
|
43126
43158
|
}
|
|
43127
43159
|
async function resolveNativeTarget(context, chatKey, input) {
|
|
@@ -44384,13 +44416,14 @@ class SessionControlService {
|
|
|
44384
44416
|
this.reserveLogicalTransportSession = deps.reserveLogicalTransportSession;
|
|
44385
44417
|
}
|
|
44386
44418
|
async createSessionWithTransport(internalAlias, agent3, workspace3, model) {
|
|
44387
|
-
const
|
|
44388
|
-
if (!
|
|
44389
|
-
throw new Error(`session "${internalAlias}" already exists`);
|
|
44419
|
+
const reserved = this.sessions.tryReserveFreeSessionAlias(internalAlias);
|
|
44420
|
+
if (!reserved) {
|
|
44421
|
+
throw new Error(`session "${internalAlias}" already exists and could not derive a free alias`);
|
|
44390
44422
|
}
|
|
44423
|
+
const { alias: finalInternalAlias, release: releaseAliasReservation } = reserved;
|
|
44391
44424
|
try {
|
|
44392
|
-
const stableTransportSession = `${workspace3}:${
|
|
44393
|
-
const session3 = this.sessions.resolveSession(
|
|
44425
|
+
const stableTransportSession = `${workspace3}:${finalInternalAlias}`;
|
|
44426
|
+
const session3 = this.sessions.resolveSession(finalInternalAlias, agent3, workspace3, this.sessions.buildFreshTransportSession(stableTransportSession));
|
|
44394
44427
|
const normalizedModel = model?.trim();
|
|
44395
44428
|
if (normalizedModel) {
|
|
44396
44429
|
session3.model = normalizedModel;
|
|
@@ -44402,15 +44435,15 @@ class SessionControlService {
|
|
|
44402
44435
|
if (!exists) {
|
|
44403
44436
|
throw new Error(`transport session "${session3.transportSession}" could not be verified`);
|
|
44404
44437
|
}
|
|
44405
|
-
await this.sessions.attachSession(
|
|
44438
|
+
await this.sessions.attachSession(finalInternalAlias, agent3, workspace3, session3.transportSession, session3.agentCommand, session3.acpxAgent, session3.agentArgv);
|
|
44406
44439
|
if (normalizedModel) {
|
|
44407
|
-
await this.sessions.setSessionModel(
|
|
44440
|
+
await this.sessions.setSessionModel(finalInternalAlias, normalizedModel);
|
|
44408
44441
|
}
|
|
44409
44442
|
try {
|
|
44410
|
-
await this.invoker.refreshSessionTransportAgentCommand(
|
|
44443
|
+
await this.invoker.refreshSessionTransportAgentCommand(finalInternalAlias);
|
|
44411
44444
|
} catch (error2) {
|
|
44412
44445
|
await this.logger.error("session.agent_command_refresh_failed", "failed to refresh session agent command", {
|
|
44413
|
-
alias:
|
|
44446
|
+
alias: finalInternalAlias,
|
|
44414
44447
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
44415
44448
|
});
|
|
44416
44449
|
}
|
|
@@ -44533,12 +44566,13 @@ class SessionControlService {
|
|
|
44533
44566
|
if (!this.transport.resumeAgentSession) {
|
|
44534
44567
|
throw new Error("the active transport does not support native sessions");
|
|
44535
44568
|
}
|
|
44536
|
-
const
|
|
44537
|
-
if (!
|
|
44538
|
-
throw new Error(`session "${internalAlias}" already exists`);
|
|
44569
|
+
const reserved = this.sessions.tryReserveFreeSessionAlias(internalAlias);
|
|
44570
|
+
if (!reserved) {
|
|
44571
|
+
throw new Error(`session "${internalAlias}" already exists and could not derive a free alias`);
|
|
44539
44572
|
}
|
|
44573
|
+
const { alias: finalInternalAlias, release: releaseAliasReservation } = reserved;
|
|
44540
44574
|
try {
|
|
44541
|
-
const session3 = this.sessions.resolveSession(
|
|
44575
|
+
const session3 = this.sessions.resolveSession(finalInternalAlias, agent3, workspace3, `${workspace3}:${finalInternalAlias}`);
|
|
44542
44576
|
const release = await this.reserveLogicalTransportSession(session3.transportSession);
|
|
44543
44577
|
try {
|
|
44544
44578
|
await this.transport.resumeAgentSession(session3, agentSessionId);
|
|
@@ -44547,7 +44581,7 @@ class SessionControlService {
|
|
|
44547
44581
|
throw new Error(`transport session "${session3.transportSession}" could not be verified`);
|
|
44548
44582
|
}
|
|
44549
44583
|
await this.sessions.attachNativeSession({
|
|
44550
|
-
alias:
|
|
44584
|
+
alias: finalInternalAlias,
|
|
44551
44585
|
agent: agent3,
|
|
44552
44586
|
workspace: workspace3,
|
|
44553
44587
|
transportSession: session3.transportSession,
|
|
@@ -44559,10 +44593,10 @@ class SessionControlService {
|
|
|
44559
44593
|
...nativeMeta?.updatedAt !== undefined ? { updatedAt: nativeMeta.updatedAt } : {}
|
|
44560
44594
|
});
|
|
44561
44595
|
try {
|
|
44562
|
-
await this.invoker.refreshSessionTransportAgentCommand(
|
|
44596
|
+
await this.invoker.refreshSessionTransportAgentCommand(finalInternalAlias);
|
|
44563
44597
|
} catch (error2) {
|
|
44564
44598
|
await this.logger.error("session.native.agent_command_refresh_failed", "failed to refresh native session agent command", {
|
|
44565
|
-
alias:
|
|
44599
|
+
alias: finalInternalAlias,
|
|
44566
44600
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
44567
44601
|
});
|
|
44568
44602
|
}
|
|
@@ -49832,6 +49866,37 @@ class SessionService {
|
|
|
49832
49866
|
this.pendingSessionAliasOperations.delete(alias);
|
|
49833
49867
|
};
|
|
49834
49868
|
}
|
|
49869
|
+
deriveFreeAlias(desiredAlias) {
|
|
49870
|
+
const taken = Object.keys(this.state.sessions);
|
|
49871
|
+
const takenSet = new Set(taken);
|
|
49872
|
+
if (!takenSet.has(desiredAlias))
|
|
49873
|
+
return desiredAlias;
|
|
49874
|
+
const sep4 = desiredAlias.lastIndexOf(":");
|
|
49875
|
+
const prefix = sep4 >= 0 ? desiredAlias.slice(0, sep4 + 1) : "";
|
|
49876
|
+
const base = sep4 >= 0 ? desiredAlias.slice(sep4 + 1) : desiredAlias;
|
|
49877
|
+
const suffixMatch = /-(\d+)$/.exec(base);
|
|
49878
|
+
let startN = 2;
|
|
49879
|
+
let stem = base;
|
|
49880
|
+
if (suffixMatch) {
|
|
49881
|
+
startN = Number(suffixMatch[1]) + 1;
|
|
49882
|
+
stem = base.slice(0, -suffixMatch[0].length);
|
|
49883
|
+
}
|
|
49884
|
+
for (let n = startN;n <= 9999; n += 1) {
|
|
49885
|
+
const candidate = `${prefix}${stem}-${n}`;
|
|
49886
|
+
if (!takenSet.has(candidate))
|
|
49887
|
+
return candidate;
|
|
49888
|
+
}
|
|
49889
|
+
throw new Error(`could not derive a free alias for "${desiredAlias}" after 9999 attempts`);
|
|
49890
|
+
}
|
|
49891
|
+
tryReserveFreeSessionAlias(desiredAlias) {
|
|
49892
|
+
for (let attempt = 0;attempt < 5; attempt += 1) {
|
|
49893
|
+
const alias = this.deriveFreeAlias(desiredAlias);
|
|
49894
|
+
const release = this.tryReserveNewSessionAlias(alias);
|
|
49895
|
+
if (release)
|
|
49896
|
+
return { alias, release };
|
|
49897
|
+
}
|
|
49898
|
+
return null;
|
|
49899
|
+
}
|
|
49835
49900
|
buildFreshTransportSession(stableTransportSession) {
|
|
49836
49901
|
const timestamp = Math.max(0, Math.trunc(this.now()));
|
|
49837
49902
|
const nonce = randomBytes2(8).readBigUInt64BE().toString().padStart(20, "0");
|
|
@@ -52917,18 +52982,19 @@ function buildToolUseEvent(update, driver) {
|
|
|
52917
52982
|
function normalizeCursorPlanUpdate(state, update) {
|
|
52918
52983
|
if (state.driver !== "cursor")
|
|
52919
52984
|
return;
|
|
52920
|
-
|
|
52921
|
-
if (!CURSOR_PLAN_TOOL_NAMES.has(title))
|
|
52985
|
+
if (!CURSOR_PLAN_TOOL_NAMES.has(cursorToolIdentity(update)))
|
|
52922
52986
|
return;
|
|
52923
52987
|
const input = cursorToolInput(update.rawInput);
|
|
52924
|
-
|
|
52988
|
+
const output = cursorToolInput(update.rawOutput);
|
|
52989
|
+
const todos = readCursorTodoList(input) ?? readCursorTodoList(output);
|
|
52990
|
+
if (todos === undefined)
|
|
52925
52991
|
return;
|
|
52926
|
-
const merge2 = input
|
|
52927
|
-
if (
|
|
52992
|
+
const merge2 = input?.merge === true || output?.merge === true;
|
|
52993
|
+
if (todos.length === 0) {
|
|
52928
52994
|
state.cursorPlanEntries.clear();
|
|
52929
52995
|
return [];
|
|
52930
52996
|
}
|
|
52931
|
-
const patches =
|
|
52997
|
+
const patches = todos.map((todo, index) => parseCursorPlanTodo(todo, index)).filter((todo) => todo !== undefined);
|
|
52932
52998
|
if (patches.length === 0)
|
|
52933
52999
|
return;
|
|
52934
53000
|
if (!merge2)
|
|
@@ -52948,6 +53014,15 @@ function normalizeCursorPlanUpdate(state, update) {
|
|
|
52948
53014
|
}
|
|
52949
53015
|
return [...state.cursorPlanEntries.values()];
|
|
52950
53016
|
}
|
|
53017
|
+
function readCursorTodoList(input) {
|
|
53018
|
+
if (!input)
|
|
53019
|
+
return;
|
|
53020
|
+
for (const key of ["todos", "todoList"]) {
|
|
53021
|
+
if (Array.isArray(input[key]))
|
|
53022
|
+
return input[key];
|
|
53023
|
+
}
|
|
53024
|
+
return;
|
|
53025
|
+
}
|
|
52951
53026
|
function parseCursorPlanTodo(value, index) {
|
|
52952
53027
|
if (!isRecord4(value))
|
|
52953
53028
|
return;
|
|
@@ -53005,6 +53080,11 @@ function cursorToolInput(rawInput) {
|
|
|
53005
53080
|
function normalizeCursorToolName(title) {
|
|
53006
53081
|
return (title ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
53007
53082
|
}
|
|
53083
|
+
function cursorToolIdentity(update) {
|
|
53084
|
+
const input = cursorToolInput(update.rawInput);
|
|
53085
|
+
const declared = typeof input?.[CURSOR_TOOL_NAME_KEY] === "string" ? input[CURSOR_TOOL_NAME_KEY] : undefined;
|
|
53086
|
+
return normalizeCursorToolName(declared ?? update.title);
|
|
53087
|
+
}
|
|
53008
53088
|
function normalizeToolKind(update, driver) {
|
|
53009
53089
|
const kindRaw = update?.kind?.trim().toLowerCase() ?? "";
|
|
53010
53090
|
switch (kindRaw) {
|
|
@@ -53017,12 +53097,14 @@ function normalizeToolKind(update, driver) {
|
|
|
53017
53097
|
}
|
|
53018
53098
|
if (driver !== "cursor")
|
|
53019
53099
|
return "other";
|
|
53020
|
-
switch (
|
|
53100
|
+
switch (cursorToolIdentity(update ?? {})) {
|
|
53021
53101
|
case "read":
|
|
53022
53102
|
case "readfile":
|
|
53023
53103
|
return "read";
|
|
53024
53104
|
case "grep":
|
|
53025
53105
|
case "glob":
|
|
53106
|
+
case "find":
|
|
53107
|
+
case "codebasesearch":
|
|
53026
53108
|
case "search":
|
|
53027
53109
|
return "search";
|
|
53028
53110
|
case "shell":
|
|
@@ -53040,6 +53122,8 @@ function normalizeToolKind(update, driver) {
|
|
|
53040
53122
|
case "runsubagent":
|
|
53041
53123
|
case "switchmode":
|
|
53042
53124
|
case "todowrite":
|
|
53125
|
+
case "updatetodos":
|
|
53126
|
+
case "todoread":
|
|
53043
53127
|
case "createplan":
|
|
53044
53128
|
case "updateplan":
|
|
53045
53129
|
case "plan":
|
|
@@ -53049,8 +53133,7 @@ function normalizeToolKind(update, driver) {
|
|
|
53049
53133
|
}
|
|
53050
53134
|
}
|
|
53051
53135
|
function isCursorSubagentInput(rawInput, title) {
|
|
53052
|
-
|
|
53053
|
-
if (!CURSOR_SUBAGENT_TOOL_NAMES.has(normalizedTitle))
|
|
53136
|
+
if (!CURSOR_SUBAGENT_TOOL_NAMES.has(cursorToolIdentity({ title, rawInput })))
|
|
53054
53137
|
return false;
|
|
53055
53138
|
const input = cursorToolInput(rawInput);
|
|
53056
53139
|
return input !== undefined && readFirstString(input, ["subagent_type", "subagentType", "agent", "agentType", "prompt", "instructions"]) !== undefined;
|
|
@@ -53225,7 +53308,7 @@ function isGenericToolTitle(kind, title) {
|
|
|
53225
53308
|
}
|
|
53226
53309
|
return false;
|
|
53227
53310
|
}
|
|
53228
|
-
var SENTENCE_TERMINAL_AT_END, PARAGRAPH_BOUNDARY_AT_END, PARAGRAPH_BOUNDARY_AT_START, LINE_BREAK_AT_END, LINE_BREAK_AT_START, PARTIAL_CRLF_PARAGRAPH_BOUNDARY_AT_END, CURSOR_PLAN_TOOL_NAMES, CURSOR_SUBAGENT_TOOL_NAMES, USAGE_BREAKDOWN_FIELDS;
|
|
53311
|
+
var SENTENCE_TERMINAL_AT_END, PARAGRAPH_BOUNDARY_AT_END, PARAGRAPH_BOUNDARY_AT_START, LINE_BREAK_AT_END, LINE_BREAK_AT_START, PARTIAL_CRLF_PARAGRAPH_BOUNDARY_AT_END, CURSOR_TOOL_NAME_KEY = "_toolName", CURSOR_PLAN_TOOL_NAMES, CURSOR_SUBAGENT_TOOL_NAMES, USAGE_BREAKDOWN_FIELDS;
|
|
53229
53312
|
var init_streaming_prompt = __esm(() => {
|
|
53230
53313
|
init_background_followup();
|
|
53231
53314
|
init_tool_kind_emoji();
|
|
@@ -53235,7 +53318,14 @@ var init_streaming_prompt = __esm(() => {
|
|
|
53235
53318
|
LINE_BREAK_AT_END = /\r?\n[\t ]*$/;
|
|
53236
53319
|
LINE_BREAK_AT_START = /^[\t ]*\r?\n/;
|
|
53237
53320
|
PARTIAL_CRLF_PARAGRAPH_BOUNDARY_AT_END = /\r?\n[\t ]*\r$/;
|
|
53238
|
-
CURSOR_PLAN_TOOL_NAMES = new Set([
|
|
53321
|
+
CURSOR_PLAN_TOOL_NAMES = new Set([
|
|
53322
|
+
"todowrite",
|
|
53323
|
+
"createplan",
|
|
53324
|
+
"updateplan",
|
|
53325
|
+
"plan",
|
|
53326
|
+
"updatetodos",
|
|
53327
|
+
"todoread"
|
|
53328
|
+
]);
|
|
53239
53329
|
CURSOR_SUBAGENT_TOOL_NAMES = new Set(["task", "delegate", "runsubagent", "subagent"]);
|
|
53240
53330
|
USAGE_BREAKDOWN_FIELDS = [
|
|
53241
53331
|
["inputTokens", ["inputTokens", "input_tokens"]],
|
|
@@ -57031,7 +57121,8 @@ class ControlService {
|
|
|
57031
57121
|
const session3 = agentSessionId ? await this.deps.attachNativeSessionWithTransport(internalAlias, agent3, workspace3, agentSessionId) : await this.deps.createSessionWithTransport(internalAlias, agent3, workspace3, model);
|
|
57032
57122
|
this.deps.events.emit({ type: "sessions-changed" });
|
|
57033
57123
|
if (nativeHistory.length > 0) {
|
|
57034
|
-
|
|
57124
|
+
const historyAlias = toDisplaySessionAlias(session3.alias);
|
|
57125
|
+
this.deps.events.emit({ type: "session-history", chatKey, sessionAlias: historyAlias, messages: nativeHistory });
|
|
57035
57126
|
}
|
|
57036
57127
|
return {
|
|
57037
57128
|
alias: toDisplaySessionAlias(session3.alias),
|
package/dist/i18n/types.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface SessionMessages {
|
|
|
12
12
|
sessionListItem: (alias: string, agent: string, workspace: string) => string;
|
|
13
13
|
sessionCreated: (alias: string) => string;
|
|
14
14
|
sessionAlreadyExists: (alias: string, agent: string, workspace: string) => string;
|
|
15
|
+
sessionAliasCollided: (desiredAlias: string, finalAlias: string) => string;
|
|
15
16
|
sessionAttachNotFound: (alias: string, agent: string, workspace: string) => string;
|
|
16
17
|
sessionAttached: (alias: string) => string;
|
|
17
18
|
sessionLifecycleBusy: (alias: string) => string;
|
package/dist/plugin-api.js
CHANGED
|
@@ -97,6 +97,7 @@ var init_session = __esm(() => {
|
|
|
97
97
|
`Switch to it with /use ${alias}, or remove it first with /session rm ${alias}.`
|
|
98
98
|
].join(`
|
|
99
99
|
`),
|
|
100
|
+
sessionAliasCollided: (desiredAlias, finalAlias) => `The requested name "${desiredAlias}" is already in use (by an archived session). Creating the new session as "${finalAlias}" instead.`,
|
|
100
101
|
sessionAttachNotFound: (alias, agent, workspace) => [
|
|
101
102
|
"No existing session found to attach.",
|
|
102
103
|
`Check the session name and retry: /session attach ${alias} --agent ${agent} --ws ${workspace} --name <session-name>`
|
|
@@ -1222,6 +1223,7 @@ var init_session2 = __esm(() => {
|
|
|
1222
1223
|
`发送 /use ${alias} 切换到它,或先执行 /session rm ${alias} 删除后再创建。`
|
|
1223
1224
|
].join(`
|
|
1224
1225
|
`),
|
|
1226
|
+
sessionAliasCollided: (desiredAlias, finalAlias) => `你指定的会话名「${desiredAlias}」已被其他会话(含已睡眠/归档)占用,已自动改为「${finalAlias}」创建。`,
|
|
1225
1227
|
sessionAttachNotFound: (alias, agent2, workspace2) => [
|
|
1226
1228
|
"没有找到可绑定的已有会话。",
|
|
1227
1229
|
`请确认会话名是否正确,然后重新执行:/session attach ${alias} --agent ${agent2} --ws ${workspace2} --name <会话名>`
|
|
@@ -124,6 +124,30 @@ export declare class SessionService {
|
|
|
124
124
|
tryReserveNewSessionAlias(alias: string): (() => void) | null;
|
|
125
125
|
/** Exclusively claim any lifecycle operation that can replace an alias binding. */
|
|
126
126
|
tryReserveSessionAliasOperation(alias: string): (() => void) | null;
|
|
127
|
+
/**
|
|
128
|
+
* Derive a free internal alias for a new session. When `desiredAlias` is already
|
|
129
|
+
* taken (by an active OR archived session), append `-2`, `-3`, … until a free
|
|
130
|
+
* slot is found.
|
|
131
|
+
*
|
|
132
|
+
* The alias may carry a channel prefix (`relay:demo`, `weixin:chat123/demo`); the
|
|
133
|
+
* numeric suffix is always appended to the last path segment after the rightmost
|
|
134
|
+
* `:` so prefix semantics are preserved (e.g. `relay:demo-2`, never `relay-2:demo`).
|
|
135
|
+
*/
|
|
136
|
+
deriveFreeAlias(desiredAlias: string): string;
|
|
137
|
+
/**
|
|
138
|
+
* Convenience: atomically derive a free alias (see `deriveFreeAlias`) AND
|
|
139
|
+
* reserve it via `tryReserveNewSessionAlias`. If the derived alias loses the
|
|
140
|
+
* race between the derive step and the reserve step (extremely rare because
|
|
141
|
+
* callers typically run this while holding the state mutex), retry up to a
|
|
142
|
+
* small, bounded number of times.
|
|
143
|
+
*
|
|
144
|
+
* Returns the chosen alias together with the reservation release callback, or
|
|
145
|
+
* `null` if no reservation could be obtained.
|
|
146
|
+
*/
|
|
147
|
+
tryReserveFreeSessionAlias(desiredAlias: string): {
|
|
148
|
+
alias: string;
|
|
149
|
+
release: () => void;
|
|
150
|
+
} | null;
|
|
127
151
|
/**
|
|
128
152
|
* Allocate a fresh acpx transport incarnation while preserving a stable logical
|
|
129
153
|
* coordinator identity. Explicit "new session" paths use this; archive/restore
|