@ganglion/xacpx 0.9.2 → 0.9.3

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/cli.js CHANGED
@@ -851,7 +851,6 @@ var init_cli_update = __esm(() => {
851
851
  updateFailed: (name, error) => `${name} update failed: ${error}`,
852
852
  targetNotFound: (name) => `Update target not found: ${name}`,
853
853
  targetVersionUnknown: (name) => `${name}: cannot check latest version; skipped.`,
854
- targetNotPinned: (name) => `${name} has no recorded version; use \`xacpx plugin update ${name}\` or specify a version explicitly.`,
855
854
  multiTargetNonInteractive: "Installed plugins detected; in non-interactive mode use `xacpx update --all` or `xacpx update <name>`.",
856
855
  selectionPrompt: "Select items to update (numbers, comma-separated; a=all; Enter to cancel): ",
857
856
  selectionInvalid: (part) => `Invalid selection: ${part}`,
@@ -1922,7 +1921,6 @@ var init_cli_update2 = __esm(() => {
1922
1921
  updateFailed: (name, error) => `${name} 更新失败:${error}`,
1923
1922
  targetNotFound: (name) => `没有找到更新项:${name}`,
1924
1923
  targetVersionUnknown: (name) => `${name} 无法检查最新版本,已跳过。`,
1925
- targetNotPinned: (name) => `${name} 未记录当前版本;请先使用 \`xacpx plugin update ${name}\` 或显式选择版本。`,
1926
1924
  multiTargetNonInteractive: "检测到已安装插件;非交互模式请使用 `xacpx update --all` 或 `xacpx update <name>`。",
1927
1925
  selectionPrompt: "请选择要更新的项目(数字,逗号分隔,a=全部,回车取消):",
1928
1926
  selectionInvalid: (part) => `无效选择:${part}`,
@@ -11959,6 +11957,41 @@ var init_version = __esm(() => {
11959
11957
  XACPX_CORE_VERSION = readVersion();
11960
11958
  });
11961
11959
 
11960
+ // src/orchestration/endpoint-probe.ts
11961
+ import { createConnection } from "node:net";
11962
+ async function canConnectToEndpoint(path3, timeoutMs) {
11963
+ return await new Promise((resolve) => {
11964
+ const socket = createConnection(path3);
11965
+ let settled = false;
11966
+ let timer;
11967
+ const finish = (result) => {
11968
+ if (settled) {
11969
+ return;
11970
+ }
11971
+ settled = true;
11972
+ if (timer) {
11973
+ clearTimeout(timer);
11974
+ }
11975
+ socket.destroy();
11976
+ resolve(result);
11977
+ };
11978
+ if (timeoutMs !== undefined && timeoutMs > 0) {
11979
+ timer = setTimeout(() => finish(true), timeoutMs);
11980
+ timer.unref?.();
11981
+ }
11982
+ socket.once("connect", () => finish(true));
11983
+ socket.once("error", (error2) => {
11984
+ const code = error2.code;
11985
+ if (code === "ENOENT" || code === "ECONNREFUSED") {
11986
+ finish(false);
11987
+ return;
11988
+ }
11989
+ finish(true);
11990
+ });
11991
+ });
11992
+ }
11993
+ var init_endpoint_probe = () => {};
11994
+
11962
11995
  // src/orchestration/task-watch-timeouts.ts
11963
11996
  var DEFAULT_TASK_WATCH_TIMEOUT_MS = 60000, MAX_TASK_WATCH_TIMEOUT_MS, DEFAULT_TASK_WATCH_POLL_INTERVAL_MS = 1000, MAX_TASK_WATCH_POLL_INTERVAL_MS = 1e4, TASK_WATCH_RPC_TIMEOUT_PADDING_MS = 5000;
11964
11997
  var init_task_watch_timeouts = __esm(() => {
@@ -12014,6 +12047,14 @@ function escapeRegExp(s) {
12014
12047
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12015
12048
  }
12016
12049
 
12050
+ // src/orchestration/coordinator-identity.ts
12051
+ function stableCoordinatorSession(transportSession) {
12052
+ return transportSession.replace(/:reset-\d+$/, "");
12053
+ }
12054
+ function sameCoordinatorSession(a, b) {
12055
+ return stableCoordinatorSession(a) === stableCoordinatorSession(b);
12056
+ }
12057
+
12017
12058
  // src/util/text.ts
12018
12059
  function truncateText(text, maxLength, ellipsis = "…") {
12019
12060
  if (text.length <= maxLength)
@@ -21516,7 +21557,7 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
21516
21557
  if (context.orchestration) {
21517
21558
  try {
21518
21559
  await context.orchestration.recordCoordinatorRouteContext?.({
21519
- coordinatorSession: session3.transportSession,
21560
+ coordinatorSession: stableCoordinatorSession(session3.transportSession),
21520
21561
  chatKey,
21521
21562
  sessionAlias: session3.alias,
21522
21563
  ...replyContextToken ? { replyContextToken } : {},
@@ -21599,7 +21640,7 @@ async function preparePromptWithFallback(context, session3, chatKey, text, reply
21599
21640
  try {
21600
21641
  return await buildCoordinatorPrompt({
21601
21642
  orchestration: orchestration3,
21602
- coordinatorSession: session3.transportSession,
21643
+ coordinatorSession: stableCoordinatorSession(session3.transportSession),
21603
21644
  chatKey,
21604
21645
  userText: text,
21605
21646
  ...replyContextToken ? { replyContextToken } : {},
@@ -21934,6 +21975,7 @@ async function handleDelegateRequest(context, chatKey, targetAgent, task, role,
21934
21975
  if (!session3) {
21935
21976
  return { text: t().orchestration.noCurrentSession };
21936
21977
  }
21978
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
21937
21979
  const orchestration3 = getOrchestration(context);
21938
21980
  if (!orchestration3) {
21939
21981
  return { text: renderOrchestrationUnavailable() };
@@ -21941,7 +21983,7 @@ async function handleDelegateRequest(context, chatKey, targetAgent, task, role,
21941
21983
  const result = await orchestration3.requestDelegate({
21942
21984
  sourceHandle: session3.transportSession,
21943
21985
  sourceKind: "coordinator",
21944
- coordinatorSession: session3.transportSession,
21986
+ coordinatorSession,
21945
21987
  workspace: session3.workspace,
21946
21988
  targetAgent,
21947
21989
  task,
@@ -21958,12 +22000,13 @@ async function handleGroupCreate(context, chatKey, title) {
21958
22000
  if (!session3) {
21959
22001
  return { text: t().orchestration.noCurrentSession };
21960
22002
  }
22003
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
21961
22004
  const orchestration3 = getOrchestration(context);
21962
22005
  if (!orchestration3) {
21963
22006
  return { text: renderOrchestrationUnavailable() };
21964
22007
  }
21965
22008
  const group = await orchestration3.createGroup({
21966
- coordinatorSession: session3.transportSession,
22009
+ coordinatorSession,
21967
22010
  title
21968
22011
  });
21969
22012
  return { text: renderGroupCreated(group) };
@@ -21973,12 +22016,13 @@ async function handleGroupList(context, chatKey, filter) {
21973
22016
  if (!session3) {
21974
22017
  return { text: t().orchestration.noCurrentSession };
21975
22018
  }
22019
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
21976
22020
  const orchestration3 = getOrchestration(context);
21977
22021
  if (!orchestration3) {
21978
22022
  return { text: renderOrchestrationUnavailable() };
21979
22023
  }
21980
22024
  const groups = await orchestration3.listGroupSummaries({
21981
- coordinatorSession: session3.transportSession,
22025
+ coordinatorSession,
21982
22026
  ...filter ?? {}
21983
22027
  });
21984
22028
  return { text: renderGroupList(groups) };
@@ -21988,13 +22032,14 @@ async function handleGroupGet(context, chatKey, groupId) {
21988
22032
  if (!session3) {
21989
22033
  return { text: t().orchestration.noCurrentSession };
21990
22034
  }
22035
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
21991
22036
  const orchestration3 = getOrchestration(context);
21992
22037
  if (!orchestration3) {
21993
22038
  return { text: renderOrchestrationUnavailable() };
21994
22039
  }
21995
22040
  const group = await orchestration3.getGroupSummary({
21996
22041
  groupId,
21997
- coordinatorSession: session3.transportSession
22042
+ coordinatorSession
21998
22043
  });
21999
22044
  if (!group) {
22000
22045
  return { text: t().orchestration.groupNotFound };
@@ -22006,20 +22051,21 @@ async function handleGroupCancel(context, chatKey, groupId) {
22006
22051
  if (!session3) {
22007
22052
  return { text: t().orchestration.noCurrentSession };
22008
22053
  }
22054
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
22009
22055
  const orchestration3 = getOrchestration(context);
22010
22056
  if (!orchestration3) {
22011
22057
  return { text: renderOrchestrationUnavailable() };
22012
22058
  }
22013
22059
  const group = await orchestration3.getGroupSummary({
22014
22060
  groupId,
22015
- coordinatorSession: session3.transportSession
22061
+ coordinatorSession
22016
22062
  });
22017
22063
  if (!group) {
22018
22064
  return { text: t().orchestration.groupNotFound };
22019
22065
  }
22020
22066
  const cancelled = await orchestration3.cancelGroup({
22021
22067
  groupId,
22022
- coordinatorSession: session3.transportSession
22068
+ coordinatorSession
22023
22069
  });
22024
22070
  return { text: renderGroupCancelSuccess(cancelled) };
22025
22071
  }
@@ -22028,13 +22074,14 @@ async function handleGroupDelegate(context, chatKey, groupId, targetAgent, task,
22028
22074
  if (!session3) {
22029
22075
  return { text: t().orchestration.noCurrentSession };
22030
22076
  }
22077
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
22031
22078
  const orchestration3 = getOrchestration(context);
22032
22079
  if (!orchestration3) {
22033
22080
  return { text: renderOrchestrationUnavailable() };
22034
22081
  }
22035
22082
  const group = await orchestration3.getGroupSummary({
22036
22083
  groupId,
22037
- coordinatorSession: session3.transportSession
22084
+ coordinatorSession
22038
22085
  });
22039
22086
  if (!group) {
22040
22087
  return { text: t().orchestration.groupNotFound };
@@ -22046,12 +22093,13 @@ async function handleTaskList(context, chatKey, filter) {
22046
22093
  if (!session3) {
22047
22094
  return { text: t().orchestration.noCurrentSession };
22048
22095
  }
22096
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
22049
22097
  const orchestration3 = getOrchestration(context);
22050
22098
  if (!orchestration3) {
22051
22099
  return { text: renderOrchestrationUnavailable() };
22052
22100
  }
22053
22101
  const tasks = await orchestration3.listTasks({
22054
- coordinatorSession: session3.transportSession,
22102
+ coordinatorSession,
22055
22103
  ...filter ?? {}
22056
22104
  });
22057
22105
  return { text: renderTaskList2(tasks) };
@@ -22061,12 +22109,13 @@ async function handleTaskGet(context, chatKey, taskId) {
22061
22109
  if (!session3) {
22062
22110
  return { text: t().orchestration.noCurrentSession };
22063
22111
  }
22112
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
22064
22113
  const orchestration3 = getOrchestration(context);
22065
22114
  if (!orchestration3) {
22066
22115
  return { text: renderOrchestrationUnavailable() };
22067
22116
  }
22068
22117
  const task = await orchestration3.getTask(taskId);
22069
- if (!task || task.coordinatorSession !== session3.transportSession) {
22118
+ if (!task || !sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
22070
22119
  return { text: t().orchestration.taskNotFound };
22071
22120
  }
22072
22121
  return { text: renderTaskSummary2(task) };
@@ -22076,12 +22125,13 @@ async function handleTaskApprove(context, chatKey, taskId) {
22076
22125
  if (!session3) {
22077
22126
  return { text: t().orchestration.noCurrentSession };
22078
22127
  }
22128
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
22079
22129
  const orchestration3 = getOrchestration(context);
22080
22130
  if (!orchestration3) {
22081
22131
  return { text: renderOrchestrationUnavailable() };
22082
22132
  }
22083
22133
  const task = await orchestration3.getTask(taskId);
22084
- if (!task || task.coordinatorSession !== session3.transportSession) {
22134
+ if (!task || !sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
22085
22135
  return { text: t().orchestration.taskNotFound };
22086
22136
  }
22087
22137
  if (task.status !== "needs_confirmation") {
@@ -22089,7 +22139,7 @@ async function handleTaskApprove(context, chatKey, taskId) {
22089
22139
  }
22090
22140
  const approved = await orchestration3.approveTask({
22091
22141
  taskId,
22092
- coordinatorSession: session3.transportSession
22142
+ coordinatorSession
22093
22143
  });
22094
22144
  return { text: renderTaskApprovalSuccess2(approved) };
22095
22145
  }
@@ -22098,12 +22148,13 @@ async function handleTaskReject(context, chatKey, taskId) {
22098
22148
  if (!session3) {
22099
22149
  return { text: t().orchestration.noCurrentSession };
22100
22150
  }
22151
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
22101
22152
  const orchestration3 = getOrchestration(context);
22102
22153
  if (!orchestration3) {
22103
22154
  return { text: renderOrchestrationUnavailable() };
22104
22155
  }
22105
22156
  const task = await orchestration3.getTask(taskId);
22106
- if (!task || task.coordinatorSession !== session3.transportSession) {
22157
+ if (!task || !sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
22107
22158
  return { text: t().orchestration.taskNotFound };
22108
22159
  }
22109
22160
  if (task.status !== "needs_confirmation") {
@@ -22111,7 +22162,7 @@ async function handleTaskReject(context, chatKey, taskId) {
22111
22162
  }
22112
22163
  const rejected = await orchestration3.cancelTask({
22113
22164
  taskId,
22114
- coordinatorSession: session3.transportSession
22165
+ coordinatorSession
22115
22166
  });
22116
22167
  return { text: renderTaskRejectSuccess(rejected) };
22117
22168
  }
@@ -22120,17 +22171,18 @@ async function handleTaskCancel(context, chatKey, taskId) {
22120
22171
  if (!session3) {
22121
22172
  return { text: t().orchestration.noCurrentSession };
22122
22173
  }
22174
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
22123
22175
  const orchestration3 = getOrchestration(context);
22124
22176
  if (!orchestration3) {
22125
22177
  return { text: renderOrchestrationUnavailable() };
22126
22178
  }
22127
22179
  const task = await orchestration3.getTask(taskId);
22128
- if (!task || task.coordinatorSession !== session3.transportSession) {
22180
+ if (!task || !sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
22129
22181
  return { text: t().orchestration.taskNotFound };
22130
22182
  }
22131
22183
  const cancelled = await orchestration3.requestTaskCancellation({
22132
22184
  taskId,
22133
- coordinatorSession: session3.transportSession
22185
+ coordinatorSession
22134
22186
  });
22135
22187
  return { text: renderTaskCancelSuccess(cancelled) };
22136
22188
  }
@@ -22139,11 +22191,12 @@ async function handleTasksClean(context, chatKey) {
22139
22191
  if (!session3) {
22140
22192
  return { text: t().orchestration.noCurrentSession };
22141
22193
  }
22194
+ const coordinatorSession = stableCoordinatorSession(session3.transportSession);
22142
22195
  const orchestration3 = getOrchestration(context);
22143
22196
  if (!orchestration3) {
22144
22197
  return { text: renderOrchestrationUnavailable() };
22145
22198
  }
22146
- const result = await orchestration3.cleanTasks(session3.transportSession);
22199
+ const result = await orchestration3.cleanTasks(coordinatorSession);
22147
22200
  return { text: renderTasksCleanResult(result.removedTasks, result.removedBindings) };
22148
22201
  }
22149
22202
  async function getCurrentSession(context, chatKey) {
@@ -24204,7 +24257,7 @@ class CommandRouter {
24204
24257
  return await this.measureTransportCall("has_session", session3, () => this.transport.hasSession(session3));
24205
24258
  }
24206
24259
  async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan) {
24207
- session3.mcpCoordinatorSession ??= session3.transportSession;
24260
+ session3.mcpCoordinatorSession ??= stableCoordinatorSession(session3.transportSession);
24208
24261
  let done = false;
24209
24262
  let abortRequested = false;
24210
24263
  let cancelOnAbort;
@@ -24455,7 +24508,7 @@ var init_console_agent = __esm(() => {
24455
24508
 
24456
24509
  // src/orchestration/orchestration-server.ts
24457
24510
  import { rm as rm8 } from "node:fs/promises";
24458
- import { createConnection as createConnection2, createServer } from "node:net";
24511
+ import { createServer } from "node:net";
24459
24512
 
24460
24513
  class OrchestrationServer {
24461
24514
  endpoint;
@@ -24683,7 +24736,7 @@ class OrchestrationServer {
24683
24736
  if (!task) {
24684
24737
  return null;
24685
24738
  }
24686
- if (task.coordinatorSession !== coordinatorSession) {
24739
+ if (!sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
24687
24740
  return null;
24688
24741
  }
24689
24742
  return task;
@@ -24948,29 +25001,6 @@ function requireTaskQuestions(params, key) {
24948
25001
  };
24949
25002
  });
24950
25003
  }
24951
- async function canConnectToEndpoint(path14) {
24952
- return await new Promise((resolve3) => {
24953
- const socket = createConnection2(path14);
24954
- let settled = false;
24955
- const finish = (result) => {
24956
- if (settled) {
24957
- return;
24958
- }
24959
- settled = true;
24960
- socket.destroy();
24961
- resolve3(result);
24962
- };
24963
- socket.once("connect", () => finish(true));
24964
- socket.once("error", (error2) => {
24965
- const code = error2.code;
24966
- if (code === "ENOENT" || code === "ECONNREFUSED") {
24967
- finish(false);
24968
- return;
24969
- }
24970
- finish(true);
24971
- });
24972
- });
24973
- }
24974
25004
  async function listen(server, path14) {
24975
25005
  await new Promise((resolve3, reject) => {
24976
25006
  const onError = (error2) => {
@@ -24993,6 +25023,7 @@ var OrchestrationInvalidRequestError, ORCHESTRATION_RPC_METHODS;
24993
25023
  var init_orchestration_server = __esm(() => {
24994
25024
  init_orchestration_ipc();
24995
25025
  init_task_watch_timeouts();
25026
+ init_endpoint_probe();
24996
25027
  OrchestrationInvalidRequestError = class OrchestrationInvalidRequestError extends Error {
24997
25028
  };
24998
25029
  ORCHESTRATION_RPC_METHODS = new Set([
@@ -25193,7 +25224,7 @@ class OrchestrationService {
25193
25224
  async getGroupSummary(input) {
25194
25225
  const state = await this.deps.loadState();
25195
25226
  const group = this.ensureGroups(state)[input.groupId];
25196
- if (!group || group.coordinatorSession !== input.coordinatorSession) {
25227
+ if (!group || !sameCoordinatorSession(group.coordinatorSession, input.coordinatorSession)) {
25197
25228
  return null;
25198
25229
  }
25199
25230
  return this.buildGroupSummary(group, Object.values(state.orchestration.tasks).filter((task) => task.groupId === input.groupId));
@@ -25205,7 +25236,7 @@ class OrchestrationService {
25205
25236
  const now = this.deps.now().getTime();
25206
25237
  const sortField = input.sort ?? "updatedAt";
25207
25238
  const order = input.order ?? "desc";
25208
- return Object.values(this.ensureGroups(state)).filter((group) => group.coordinatorSession === input.coordinatorSession).map((group) => ({
25239
+ return Object.values(this.ensureGroups(state)).filter((group) => sameCoordinatorSession(group.coordinatorSession, input.coordinatorSession)).map((group) => ({
25209
25240
  group,
25210
25241
  summary: this.buildGroupSummary(group, tasks.filter((task) => task.groupId === group.groupId))
25211
25242
  })).filter(({ summary }) => {
@@ -25695,7 +25726,7 @@ class OrchestrationService {
25695
25726
  const workerSession = task.workerSession;
25696
25727
  const taskStillOwnsWorkerSession = current?.workerSession === workerSession;
25697
25728
  const currentBinding = state.orchestration.workerBindings[workerSession];
25698
- const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && currentBinding.coordinatorSession === task.coordinatorSession && currentBinding.workspace === task.workspace && currentBinding.cwd === task.cwd && currentBinding.targetAgent === task.targetAgent && currentBinding.role === task.role;
25729
+ const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && sameCoordinatorSession(currentBinding.coordinatorSession, task.coordinatorSession) && currentBinding.workspace === task.workspace && currentBinding.cwd === task.cwd && currentBinding.targetAgent === task.targetAgent && currentBinding.role === task.role;
25699
25730
  const otherActiveOwner = Object.values(state.orchestration.tasks).some((candidate) => candidate.taskId !== task.taskId && candidate.workerSession === workerSession && (!this.isTerminalStatus(candidate.status) || candidate.reviewPending !== undefined));
25700
25731
  const restoreOrDeleteBinding = () => {
25701
25732
  if (!bindingStillBelongsToThisStartup || otherActiveOwner) {
@@ -25754,7 +25785,7 @@ class OrchestrationService {
25754
25785
  current.updatedAt = now;
25755
25786
  this.bumpGroupUpdated(state, current.groupId, now);
25756
25787
  const currentBinding = state.orchestration.workerBindings[workerSession];
25757
- const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && currentBinding.coordinatorSession === task.coordinatorSession && currentBinding.workspace === task.workspace && currentBinding.cwd === task.cwd && currentBinding.targetAgent === task.targetAgent && currentBinding.role === task.role;
25788
+ const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && sameCoordinatorSession(currentBinding.coordinatorSession, task.coordinatorSession) && currentBinding.workspace === task.workspace && currentBinding.cwd === task.cwd && currentBinding.targetAgent === task.targetAgent && currentBinding.role === task.role;
25758
25789
  const otherActiveOwner = Object.values(state.orchestration.tasks).some((candidate) => candidate.taskId !== task.taskId && candidate.workerSession === workerSession && (!this.isTerminalStatus(candidate.status) || candidate.reviewPending !== undefined));
25759
25790
  if (bindingStillBelongsToThisStartup && !otherActiveOwner) {
25760
25791
  if (input.previousBinding) {
@@ -25773,7 +25804,7 @@ class OrchestrationService {
25773
25804
  const state = await this.deps.loadState();
25774
25805
  const workerSession = task.workerSession;
25775
25806
  const currentBinding = state.orchestration.workerBindings[workerSession];
25776
- const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && currentBinding.coordinatorSession === task.coordinatorSession && currentBinding.workspace === task.workspace && currentBinding.cwd === task.cwd && currentBinding.targetAgent === task.targetAgent && currentBinding.role === task.role;
25807
+ const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && sameCoordinatorSession(currentBinding.coordinatorSession, task.coordinatorSession) && currentBinding.workspace === task.workspace && currentBinding.cwd === task.cwd && currentBinding.targetAgent === task.targetAgent && currentBinding.role === task.role;
25777
25808
  if (!bindingStillBelongsToThisStartup) {
25778
25809
  return false;
25779
25810
  }
@@ -25946,7 +25977,7 @@ class OrchestrationService {
25946
25977
  while (true) {
25947
25978
  const state = await this.deps.loadState();
25948
25979
  const task = state.orchestration.tasks[input.taskId];
25949
- if (!task || task.coordinatorSession !== input.coordinatorSession) {
25980
+ if (!task || !sameCoordinatorSession(task.coordinatorSession, input.coordinatorSession)) {
25950
25981
  return { status: "not_found", task: null, events: [], nextAfterSeq: afterSeq };
25951
25982
  }
25952
25983
  const snapshot = { ...task };
@@ -26004,7 +26035,8 @@ class OrchestrationService {
26004
26035
  return await this.mutate(async () => {
26005
26036
  const state = await this.deps.loadState();
26006
26037
  const now = this.deps.now().toISOString();
26007
- const existing = this.ensureCoordinatorRoutes(state)[input.coordinatorSession];
26038
+ const routeKey = stableCoordinatorSession(input.coordinatorSession);
26039
+ const existing = this.ensureCoordinatorRoutes(state)[routeKey];
26008
26040
  const sameChat = existing?.chatKey === input.chatKey;
26009
26041
  const hasAccountId = input.accountId !== undefined;
26010
26042
  const hasReplyContextToken = input.replyContextToken !== undefined;
@@ -26018,14 +26050,14 @@ class OrchestrationService {
26018
26050
  replyContextToken: existing.replyContextToken
26019
26051
  } : undefined;
26020
26052
  const route = {
26021
- coordinatorSession: input.coordinatorSession,
26053
+ coordinatorSession: routeKey,
26022
26054
  chatKey: input.chatKey,
26023
26055
  ...input.sessionAlias ? { sessionAlias: input.sessionAlias } : {},
26024
26056
  ...replyRoute ? replyRoute : {},
26025
26057
  ...buildCoordinatorRouteChatMetadata(input, sameChat ? existing : undefined),
26026
26058
  updatedAt: now
26027
26059
  };
26028
- this.ensureCoordinatorRoutes(state)[input.coordinatorSession] = route;
26060
+ this.ensureCoordinatorRoutes(state)[routeKey] = route;
26029
26061
  await this.deps.saveState(state);
26030
26062
  return { ...route };
26031
26063
  });
@@ -26269,7 +26301,7 @@ class OrchestrationService {
26269
26301
  return task;
26270
26302
  });
26271
26303
  const now = this.deps.now().toISOString();
26272
- const route = this.snapshotCoordinatorDeliveryRoute(this.ensureCoordinatorRoutes(state)[input.coordinatorSession]);
26304
+ const route = this.snapshotCoordinatorDeliveryRoute(this.ensureCoordinatorRoutes(state)[stableCoordinatorSession(input.coordinatorSession)]);
26273
26305
  if (coordinatorState.activePackageId) {
26274
26306
  const activePackage = this.ensureHumanQuestionPackages(state)[coordinatorState.activePackageId];
26275
26307
  if (!activePackage) {
@@ -26371,7 +26403,7 @@ class OrchestrationService {
26371
26403
  if (!packageRecord) {
26372
26404
  throw new Error(`package "${input.packageId}" does not exist`);
26373
26405
  }
26374
- if (packageRecord.coordinatorSession !== input.coordinatorSession) {
26406
+ if (!sameCoordinatorSession(packageRecord.coordinatorSession, input.coordinatorSession)) {
26375
26407
  throw new Error(`package "${input.packageId}" belongs to coordinator "${packageRecord.coordinatorSession}", not "${input.coordinatorSession}"`);
26376
26408
  }
26377
26409
  if (packageRecord.status !== "active") {
@@ -26386,7 +26418,7 @@ class OrchestrationService {
26386
26418
  }
26387
26419
  let route = this.resolveFrozenPackageMessageRoute(message);
26388
26420
  if (!route) {
26389
- route = this.snapshotCoordinatorDeliveryRoute(this.ensureCoordinatorRoutes(state)[input.coordinatorSession]) ?? null;
26421
+ route = this.snapshotCoordinatorDeliveryRoute(this.ensureCoordinatorRoutes(state)[stableCoordinatorSession(input.coordinatorSession)]) ?? null;
26390
26422
  if (route) {
26391
26423
  Object.assign(message, this.serializeFrozenDeliveryRoute(route));
26392
26424
  }
@@ -26455,7 +26487,7 @@ class OrchestrationService {
26455
26487
  if (this.isExternalCoordinatorSession(state, coordinatorSession)) {
26456
26488
  return null;
26457
26489
  }
26458
- const coordinatorState = state.orchestration.coordinatorQuestionState[coordinatorSession];
26490
+ const coordinatorState = state.orchestration.coordinatorQuestionState[stableCoordinatorSession(coordinatorSession)];
26459
26491
  const activePackageId = coordinatorState?.activePackageId;
26460
26492
  if (!activePackageId) {
26461
26493
  return null;
@@ -26595,7 +26627,7 @@ class OrchestrationService {
26595
26627
  const bindings = state.orchestration.workerBindings;
26596
26628
  const terminalTaskIds = [];
26597
26629
  for (const [taskId, task] of Object.entries(tasks)) {
26598
- if (task.coordinatorSession === coordinatorSession && this.isTerminalStatus(task.status) && task.reviewPending === undefined) {
26630
+ if (sameCoordinatorSession(task.coordinatorSession, coordinatorSession) && this.isTerminalStatus(task.status) && task.reviewPending === undefined) {
26599
26631
  terminalTaskIds.push(taskId);
26600
26632
  }
26601
26633
  }
@@ -26605,7 +26637,7 @@ class OrchestrationService {
26605
26637
  const remainingWorkerSessions = new Set(Object.values(tasks).map((task) => task.workerSession).filter(Boolean));
26606
26638
  let removedBindings = 0;
26607
26639
  for (const [workerSession, binding] of Object.entries(bindings)) {
26608
- if (binding.coordinatorSession !== coordinatorSession) {
26640
+ if (!sameCoordinatorSession(binding.coordinatorSession, coordinatorSession)) {
26609
26641
  continue;
26610
26642
  }
26611
26643
  if (!remainingWorkerSessions.has(workerSession)) {
@@ -26625,16 +26657,17 @@ class OrchestrationService {
26625
26657
  }
26626
26658
  async listSessionBlockingTasks(transportSession) {
26627
26659
  const state = await this.deps.loadState();
26628
- return Object.values(state.orchestration.tasks).filter((task) => (!this.isTerminalStatus(task.status) || task.reviewPending !== undefined) && (task.coordinatorSession === transportSession || task.workerSession === transportSession)).map((task) => ({ ...task }));
26660
+ return Object.values(state.orchestration.tasks).filter((task) => (!this.isTerminalStatus(task.status) || task.reviewPending !== undefined) && (sameCoordinatorSession(task.coordinatorSession, transportSession) || task.workerSession !== undefined && sameCoordinatorSession(task.workerSession, transportSession))).map((task) => ({ ...task }));
26629
26661
  }
26630
26662
  async purgeSessionReferences(transportSession) {
26631
26663
  return await this.mutate(async () => {
26632
26664
  const state = await this.deps.loadState();
26665
+ const sessionIdentity = stableCoordinatorSession(transportSession);
26633
26666
  const tasks = state.orchestration.tasks;
26634
26667
  const bindings = state.orchestration.workerBindings;
26635
26668
  const removedTaskIds = [];
26636
26669
  for (const [taskId, task] of Object.entries(tasks)) {
26637
- if (this.isTerminalStatus(task.status) && task.reviewPending === undefined && (task.coordinatorSession === transportSession || task.workerSession === transportSession)) {
26670
+ if (this.isTerminalStatus(task.status) && task.reviewPending === undefined && (sameCoordinatorSession(task.coordinatorSession, transportSession) || task.workerSession !== undefined && sameCoordinatorSession(task.workerSession, transportSession))) {
26638
26671
  removedTaskIds.push(taskId);
26639
26672
  }
26640
26673
  }
@@ -26644,14 +26677,14 @@ class OrchestrationService {
26644
26677
  const remainingWorkerSessions = new Set(Object.values(tasks).map((task) => task.workerSession).filter(Boolean));
26645
26678
  let removedBindings = 0;
26646
26679
  for (const [workerSession, binding] of Object.entries(bindings)) {
26647
- const shouldPurgeBinding = workerSession === transportSession || binding.coordinatorSession === transportSession;
26680
+ const shouldPurgeBinding = sameCoordinatorSession(workerSession, transportSession) || sameCoordinatorSession(binding.coordinatorSession, transportSession);
26648
26681
  if (shouldPurgeBinding && !remainingWorkerSessions.has(workerSession)) {
26649
26682
  delete bindings[workerSession];
26650
26683
  removedBindings += 1;
26651
26684
  }
26652
26685
  }
26653
- const removedEmptyGroups = this.removeEmptyGroupsForCoordinator(state, transportSession);
26654
- const removedCoordinatorMetadata = this.removeCoordinatorMetadataIfUnused(state, transportSession);
26686
+ const removedEmptyGroups = this.removeEmptyGroupsForCoordinator(state, sessionIdentity);
26687
+ const removedCoordinatorMetadata = this.removeCoordinatorMetadataIfUnused(state, sessionIdentity);
26655
26688
  if (removedTaskIds.length > 0 || removedBindings > 0 || removedEmptyGroups || removedCoordinatorMetadata) {
26656
26689
  await this.deps.saveState(state);
26657
26690
  }
@@ -26661,106 +26694,28 @@ class OrchestrationService {
26661
26694
  };
26662
26695
  });
26663
26696
  }
26664
- async purgeExpiredResetCoordinators(input) {
26665
- try {
26666
- if (!Number.isFinite(input.cutoffDays) || input.cutoffDays < 0) {
26667
- throw new Error(`cutoffDays must be a non-negative number, got ${String(input.cutoffDays)}`);
26668
- }
26669
- const result = await this.mutate(async () => {
26670
- const state = await this.deps.loadState();
26671
- const candidates = this.collectResetCoordinatorCandidates(state);
26672
- const activeTransportSessions = new Set(Object.values(state.sessions).map((session3) => session3.transport_session));
26673
- const MS_PER_DAY = 24 * 60 * 60 * 1000;
26674
- const cutoffMs = input.cutoffDays * MS_PER_DAY;
26675
- const nowMs = this.deps.now().getTime();
26676
- let purgedCoordinators = 0;
26677
- const removed = {
26678
- tasks: 0,
26679
- workerBindings: 0,
26680
- groups: 0,
26681
- coordinatorRoutes: 0,
26682
- humanQuestionPackages: 0,
26683
- coordinatorQuestionState: 0
26684
- };
26685
- for (const coordinatorSession of candidates) {
26686
- if (activeTransportSessions.has(coordinatorSession)) {
26687
- continue;
26688
- }
26689
- const activityAtMs = this.resolveResetCoordinatorActivityAtMs(state, coordinatorSession);
26690
- if (activityAtMs === null) {
26691
- continue;
26692
- }
26693
- if (nowMs - activityAtMs <= cutoffMs) {
26694
- continue;
26695
- }
26696
- const delta = this.cascadeRemoveCoordinatorRecords(state, coordinatorSession);
26697
- const changed = delta.tasks > 0 || delta.workerBindings > 0 || delta.groups > 0 || delta.coordinatorRoutes > 0 || delta.humanQuestionPackages > 0 || delta.coordinatorQuestionState > 0;
26698
- if (!changed) {
26699
- continue;
26700
- }
26701
- purgedCoordinators += 1;
26702
- removed.tasks += delta.tasks;
26703
- removed.workerBindings += delta.workerBindings;
26704
- removed.groups += delta.groups;
26705
- removed.coordinatorRoutes += delta.coordinatorRoutes;
26706
- removed.humanQuestionPackages += delta.humanQuestionPackages;
26707
- removed.coordinatorQuestionState += delta.coordinatorQuestionState;
26708
- }
26709
- const removedAny = removed.tasks > 0 || removed.workerBindings > 0 || removed.groups > 0 || removed.coordinatorRoutes > 0 || removed.humanQuestionPackages > 0 || removed.coordinatorQuestionState > 0;
26710
- if (removedAny) {
26711
- await this.deps.saveState(state);
26712
- }
26713
- return {
26714
- candidates: candidates.length,
26715
- purgedCoordinators,
26716
- removed
26717
- };
26718
- });
26719
- if (this.deps.logger) {
26720
- this.deps.logger.info("orchestration.reset_gc.completed", "reset coordinator gc completed", {
26721
- trigger: input.trigger,
26722
- cutoffDays: input.cutoffDays,
26723
- candidates: result.candidates,
26724
- purgedCoordinators: result.purgedCoordinators,
26725
- deletedCounts: result.removed
26726
- });
26727
- }
26728
- return result;
26729
- } catch (error2) {
26730
- const logger2 = this.deps.logger;
26731
- if (logger2) {
26732
- const message = error2 instanceof Error ? error2.message : String(error2);
26733
- logger2.error("orchestration.reset_gc.failed", "reset coordinator gc failed", {
26734
- trigger: input.trigger,
26735
- cutoffDays: input.cutoffDays,
26736
- message
26737
- });
26738
- }
26739
- throw error2;
26740
- }
26741
- }
26742
26697
  async listPendingCoordinatorResults(coordinatorSession) {
26743
26698
  const state = await this.deps.loadState();
26744
26699
  if (this.isExternalCoordinatorSession(state, coordinatorSession)) {
26745
26700
  return [];
26746
26701
  }
26747
- return Object.values(state.orchestration.tasks).filter((task) => task.coordinatorSession === coordinatorSession && this.canInjectTaskIntoCoordinator(state, task) && (task.injectionPending === true || task.coordinatorInjectedAt === undefined)).sort((left, right) => left.updatedAt.localeCompare(right.updatedAt)).map((task) => ({ ...task }));
26702
+ return Object.values(state.orchestration.tasks).filter((task) => sameCoordinatorSession(task.coordinatorSession, coordinatorSession) && this.canInjectTaskIntoCoordinator(state, task) && (task.injectionPending === true || task.coordinatorInjectedAt === undefined)).sort((left, right) => left.updatedAt.localeCompare(right.updatedAt)).map((task) => ({ ...task }));
26748
26703
  }
26749
26704
  async listPendingCoordinatorBlockers(coordinatorSession) {
26750
26705
  const state = await this.deps.loadState();
26751
26706
  if (this.isExternalCoordinatorSession(state, coordinatorSession)) {
26752
26707
  return [];
26753
26708
  }
26754
- const coordinatorState = state.orchestration.coordinatorQuestionState[coordinatorSession];
26709
+ const coordinatorState = state.orchestration.coordinatorQuestionState[stableCoordinatorSession(coordinatorSession)];
26755
26710
  const hiddenQueuedQuestionKeys = coordinatorState?.activePackageId ? new Set((coordinatorState.queuedQuestions ?? []).map((entry) => `${entry.taskId}:${entry.questionId}`)) : null;
26756
- return Object.values(state.orchestration.tasks).filter((task) => task.coordinatorSession === coordinatorSession && task.status === "blocked" && task.openQuestion?.status === "open" && !hiddenQueuedQuestionKeys?.has(`${task.taskId}:${task.openQuestion.questionId}`)).sort((left, right) => left.updatedAt.localeCompare(right.updatedAt)).map((task) => ({ ...task }));
26711
+ return Object.values(state.orchestration.tasks).filter((task) => sameCoordinatorSession(task.coordinatorSession, coordinatorSession) && task.status === "blocked" && task.openQuestion?.status === "open" && !hiddenQueuedQuestionKeys?.has(`${task.taskId}:${task.openQuestion.questionId}`)).sort((left, right) => left.updatedAt.localeCompare(right.updatedAt)).map((task) => ({ ...task }));
26757
26712
  }
26758
26713
  async listContestedCoordinatorResults(coordinatorSession) {
26759
26714
  const state = await this.deps.loadState();
26760
26715
  if (this.isExternalCoordinatorSession(state, coordinatorSession)) {
26761
26716
  return [];
26762
26717
  }
26763
- return Object.values(state.orchestration.tasks).filter((task) => task.coordinatorSession === coordinatorSession && task.reviewPending !== undefined).sort((left, right) => left.updatedAt.localeCompare(right.updatedAt)).map((task) => ({ ...task }));
26718
+ return Object.values(state.orchestration.tasks).filter((task) => sameCoordinatorSession(task.coordinatorSession, coordinatorSession) && task.reviewPending !== undefined).sort((left, right) => left.updatedAt.localeCompare(right.updatedAt)).map((task) => ({ ...task }));
26764
26719
  }
26765
26720
  async listPendingCoordinatorGroups(coordinatorSession) {
26766
26721
  const state = await this.deps.loadState();
@@ -26769,7 +26724,7 @@ class OrchestrationService {
26769
26724
  }
26770
26725
  const groups = this.ensureGroups(state);
26771
26726
  const tasks = Object.values(state.orchestration.tasks);
26772
- return Object.values(groups).filter((group) => group.coordinatorSession === coordinatorSession).filter((group) => {
26727
+ return Object.values(groups).filter((group) => sameCoordinatorSession(group.coordinatorSession, coordinatorSession)).filter((group) => {
26773
26728
  const groupTasks = tasks.filter((task) => task.groupId === group.groupId);
26774
26729
  return this.canInjectGroupIntoCoordinator(state, group.groupId, groupTasks);
26775
26730
  }).sort((left, right) => left.updatedAt.localeCompare(right.updatedAt)).map((group) => ({ ...group }));
@@ -26984,7 +26939,7 @@ class OrchestrationService {
26984
26939
  if (input.sourceHandle !== undefined && task.sourceHandle !== input.sourceHandle) {
26985
26940
  throw new Error(`task "${input.taskId}" belongs to source "${task.sourceHandle}", not "${input.sourceHandle}"`);
26986
26941
  }
26987
- if (input.coordinatorSession !== undefined && task.coordinatorSession !== input.coordinatorSession) {
26942
+ if (input.coordinatorSession !== undefined && !sameCoordinatorSession(task.coordinatorSession, input.coordinatorSession)) {
26988
26943
  throw new Error(`task "${input.taskId}" belongs to coordinator "${task.coordinatorSession}", not "${input.coordinatorSession}"`);
26989
26944
  }
26990
26945
  if (this.isTerminalStatus(task.status)) {
@@ -27411,11 +27366,11 @@ class OrchestrationService {
27411
27366
  ...binding.cwd ? { cwd: binding.cwd } : {}
27412
27367
  };
27413
27368
  }
27414
- const coordinatorSession = Object.values(state.sessions).find((session3) => session3.transport_session === sourceHandle);
27369
+ const coordinatorSession = Object.values(state.sessions).find((session3) => sameCoordinatorSession(session3.transport_session, sourceHandle));
27415
27370
  if (coordinatorSession) {
27416
27371
  return {
27417
27372
  sourceKind: "coordinator",
27418
- coordinatorSession: sourceHandle,
27373
+ coordinatorSession: stableCoordinatorSession(sourceHandle),
27419
27374
  workspace: coordinatorSession.workspace
27420
27375
  };
27421
27376
  }
@@ -27453,7 +27408,7 @@ class OrchestrationService {
27453
27408
  if (role && policy.allowedAgentRequestRoles.length > 0 && !policy.allowedAgentRequestRoles.includes(role)) {
27454
27409
  throw new Error(`role "${role}" is not allowed for agent-requested delegation`);
27455
27410
  }
27456
- const outstandingRequests = Object.values(state.orchestration.tasks).filter((task) => task.coordinatorSession === coordinatorSession && task.sourceKind !== "human" && (task.status === "needs_confirmation" || task.status === "running" || task.status === "queued"));
27411
+ const outstandingRequests = Object.values(state.orchestration.tasks).filter((task) => sameCoordinatorSession(task.coordinatorSession, coordinatorSession) && task.sourceKind !== "human" && (task.status === "needs_confirmation" || task.status === "running" || task.status === "queued"));
27457
27412
  if (outstandingRequests.length >= policy.maxPendingAgentRequestsPerCoordinator) {
27458
27413
  throw new Error("agent-requested delegation quota exceeded for this coordinator");
27459
27414
  }
@@ -27557,13 +27512,13 @@ class OrchestrationService {
27557
27512
  if (!filter) {
27558
27513
  return true;
27559
27514
  }
27560
- return (filter.sourceHandle === undefined || task.sourceHandle === filter.sourceHandle) && (filter.coordinatorSession === undefined || task.coordinatorSession === filter.coordinatorSession) && (filter.workspace === undefined || task.workspace === filter.workspace) && (filter.targetAgent === undefined || task.targetAgent === filter.targetAgent) && (filter.role === undefined || task.role === filter.role) && (filter.status === undefined || task.status === filter.status);
27515
+ return (filter.sourceHandle === undefined || task.sourceHandle === filter.sourceHandle) && (filter.coordinatorSession === undefined || sameCoordinatorSession(task.coordinatorSession, filter.coordinatorSession)) && (filter.workspace === undefined || task.workspace === filter.workspace) && (filter.targetAgent === undefined || task.targetAgent === filter.targetAgent) && (filter.role === undefined || task.role === filter.role) && (filter.status === undefined || task.status === filter.status);
27561
27516
  }
27562
27517
  isTerminalStatus(status) {
27563
27518
  return status === "completed" || status === "failed" || status === "cancelled";
27564
27519
  }
27565
27520
  assertCoordinatorOwnership(task, coordinatorSession) {
27566
- if (task.coordinatorSession !== coordinatorSession) {
27521
+ if (!sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
27567
27522
  throw new Error(`task "${task.taskId}" belongs to coordinator "${task.coordinatorSession}", not "${coordinatorSession}"`);
27568
27523
  }
27569
27524
  }
@@ -27576,7 +27531,7 @@ class OrchestrationService {
27576
27531
  if (!group) {
27577
27532
  throw new Error(`group "${groupId}" does not exist`);
27578
27533
  }
27579
- if (group.coordinatorSession !== coordinatorSession) {
27534
+ if (!sameCoordinatorSession(group.coordinatorSession, coordinatorSession)) {
27580
27535
  throw new Error(`group "${groupId}" belongs to coordinator "${group.coordinatorSession}", not "${coordinatorSession}"`);
27581
27536
  }
27582
27537
  }
@@ -27587,13 +27542,14 @@ class OrchestrationService {
27587
27542
  return state.orchestration.humanQuestionPackages;
27588
27543
  }
27589
27544
  ensureCoordinatorQuestionState(state, coordinatorSession) {
27545
+ const key = stableCoordinatorSession(coordinatorSession);
27590
27546
  if (!("coordinatorQuestionState" in state.orchestration) || !state.orchestration.coordinatorQuestionState) {
27591
27547
  state.orchestration.coordinatorQuestionState = {};
27592
27548
  }
27593
- state.orchestration.coordinatorQuestionState[coordinatorSession] ??= {
27549
+ state.orchestration.coordinatorQuestionState[key] ??= {
27594
27550
  queuedQuestions: []
27595
27551
  };
27596
- return state.orchestration.coordinatorQuestionState[coordinatorSession];
27552
+ return state.orchestration.coordinatorQuestionState[key];
27597
27553
  }
27598
27554
  ensureCoordinatorRoutes(state) {
27599
27555
  if (!("coordinatorRoutes" in state.orchestration) || !state.orchestration.coordinatorRoutes) {
@@ -27774,7 +27730,7 @@ class OrchestrationService {
27774
27730
  const referencedGroupIds = new Set(Object.values(state.orchestration.tasks).map((task) => task.groupId).filter((groupId) => typeof groupId === "string"));
27775
27731
  let removedAny = false;
27776
27732
  for (const [groupId, group] of Object.entries(groups)) {
27777
- if (group.coordinatorSession !== coordinatorSession) {
27733
+ if (!sameCoordinatorSession(group.coordinatorSession, coordinatorSession)) {
27778
27734
  continue;
27779
27735
  }
27780
27736
  if (!referencedGroupIds.has(groupId)) {
@@ -27785,125 +27741,30 @@ class OrchestrationService {
27785
27741
  return removedAny;
27786
27742
  }
27787
27743
  removeCoordinatorMetadataIfUnused(state, coordinatorSession) {
27788
- const hasCoordinatorTasks = Object.values(state.orchestration.tasks).some((task) => task.coordinatorSession === coordinatorSession);
27789
- const hasCoordinatorBindings = Object.values(state.orchestration.workerBindings).some((binding) => binding.coordinatorSession === coordinatorSession);
27744
+ const key = stableCoordinatorSession(coordinatorSession);
27745
+ const hasCoordinatorTasks = Object.values(state.orchestration.tasks).some((task) => sameCoordinatorSession(task.coordinatorSession, coordinatorSession));
27746
+ const hasCoordinatorBindings = Object.values(state.orchestration.workerBindings).some((binding) => sameCoordinatorSession(binding.coordinatorSession, coordinatorSession));
27790
27747
  if (hasCoordinatorTasks || hasCoordinatorBindings) {
27791
27748
  return false;
27792
27749
  }
27793
27750
  let removedAny = false;
27794
27751
  const packages = this.ensureHumanQuestionPackages(state);
27795
27752
  for (const [packageId, packageRecord] of Object.entries(packages)) {
27796
- if (packageRecord.coordinatorSession === coordinatorSession) {
27753
+ if (sameCoordinatorSession(packageRecord.coordinatorSession, coordinatorSession)) {
27797
27754
  delete packages[packageId];
27798
27755
  removedAny = true;
27799
27756
  }
27800
27757
  }
27801
- if (state.orchestration.coordinatorQuestionState?.[coordinatorSession] !== undefined) {
27802
- delete state.orchestration.coordinatorQuestionState[coordinatorSession];
27758
+ if (state.orchestration.coordinatorQuestionState?.[key] !== undefined) {
27759
+ delete state.orchestration.coordinatorQuestionState[key];
27803
27760
  removedAny = true;
27804
27761
  }
27805
- if (state.orchestration.coordinatorRoutes?.[coordinatorSession] !== undefined) {
27806
- delete state.orchestration.coordinatorRoutes[coordinatorSession];
27762
+ if (state.orchestration.coordinatorRoutes?.[key] !== undefined) {
27763
+ delete state.orchestration.coordinatorRoutes[key];
27807
27764
  removedAny = true;
27808
27765
  }
27809
27766
  return removedAny;
27810
27767
  }
27811
- isResetCoordinatorSession(coordinatorSession) {
27812
- return coordinatorSession.includes(":reset-");
27813
- }
27814
- collectResetCoordinatorCandidates(state) {
27815
- const candidates = new Set;
27816
- for (const coordinatorSession of Object.keys(state.orchestration.coordinatorRoutes ?? {})) {
27817
- if (this.isResetCoordinatorSession(coordinatorSession)) {
27818
- candidates.add(coordinatorSession);
27819
- }
27820
- }
27821
- for (const task of Object.values(state.orchestration.tasks ?? {})) {
27822
- if (this.isResetCoordinatorSession(task.coordinatorSession)) {
27823
- candidates.add(task.coordinatorSession);
27824
- }
27825
- }
27826
- return [...candidates];
27827
- }
27828
- parseDateMs(value) {
27829
- if (!value) {
27830
- return null;
27831
- }
27832
- const ms = new Date(value).getTime();
27833
- return Number.isFinite(ms) ? ms : null;
27834
- }
27835
- resolveResetCoordinatorActivityAtMs(state, coordinatorSession) {
27836
- const routeUpdatedAt = state.orchestration.coordinatorRoutes?.[coordinatorSession]?.updatedAt;
27837
- const routeMs = this.parseDateMs(routeUpdatedAt);
27838
- let tasksMs = null;
27839
- for (const task of Object.values(state.orchestration.tasks ?? {})) {
27840
- if (task.coordinatorSession !== coordinatorSession) {
27841
- continue;
27842
- }
27843
- const candidate = this.parseDateMs(task.updatedAt);
27844
- if (candidate === null) {
27845
- continue;
27846
- }
27847
- tasksMs = tasksMs === null ? candidate : Math.max(tasksMs, candidate);
27848
- }
27849
- if (routeMs === null && tasksMs === null) {
27850
- return null;
27851
- }
27852
- if (routeMs === null) {
27853
- return tasksMs;
27854
- }
27855
- if (tasksMs === null) {
27856
- return routeMs;
27857
- }
27858
- return Math.max(routeMs, tasksMs);
27859
- }
27860
- cascadeRemoveCoordinatorRecords(state, coordinatorSession) {
27861
- const removed = {
27862
- tasks: 0,
27863
- workerBindings: 0,
27864
- groups: 0,
27865
- coordinatorRoutes: 0,
27866
- humanQuestionPackages: 0,
27867
- coordinatorQuestionState: 0
27868
- };
27869
- const tasks = state.orchestration.tasks;
27870
- for (const [taskId, task] of Object.entries(tasks ?? {})) {
27871
- if (task.coordinatorSession === coordinatorSession) {
27872
- delete tasks[taskId];
27873
- removed.tasks += 1;
27874
- }
27875
- }
27876
- const workerBindings = state.orchestration.workerBindings;
27877
- for (const [workerSession, binding] of Object.entries(workerBindings ?? {})) {
27878
- if (binding.coordinatorSession === coordinatorSession) {
27879
- delete workerBindings[workerSession];
27880
- removed.workerBindings += 1;
27881
- }
27882
- }
27883
- const groups = this.ensureGroups(state);
27884
- for (const [groupId, group] of Object.entries(groups ?? {})) {
27885
- if (group.coordinatorSession === coordinatorSession) {
27886
- delete groups[groupId];
27887
- removed.groups += 1;
27888
- }
27889
- }
27890
- if (state.orchestration.coordinatorRoutes?.[coordinatorSession] !== undefined) {
27891
- delete state.orchestration.coordinatorRoutes[coordinatorSession];
27892
- removed.coordinatorRoutes += 1;
27893
- }
27894
- const packages = this.ensureHumanQuestionPackages(state);
27895
- for (const [packageId, packageRecord] of Object.entries(packages ?? {})) {
27896
- if (packageRecord.coordinatorSession === coordinatorSession) {
27897
- delete packages[packageId];
27898
- removed.humanQuestionPackages += 1;
27899
- }
27900
- }
27901
- if (state.orchestration.coordinatorQuestionState?.[coordinatorSession] !== undefined) {
27902
- delete state.orchestration.coordinatorQuestionState[coordinatorSession];
27903
- removed.coordinatorQuestionState += 1;
27904
- }
27905
- return removed;
27906
- }
27907
27768
  bumpGroupUpdated(state, groupId, now) {
27908
27769
  if (!groupId) {
27909
27770
  return;
@@ -28077,7 +27938,7 @@ class OrchestrationService {
28077
27938
  }
28078
27939
  const validQueuedQuestions = coordinatorState.queuedQuestions.filter((entry) => {
28079
27940
  const task = state.orchestration.tasks[entry.taskId];
28080
- return task?.coordinatorSession === coordinatorSession && task.status === "blocked" && task.openQuestion?.status === "open" && task.openQuestion.questionId === entry.questionId;
27941
+ return task !== undefined && sameCoordinatorSession(task.coordinatorSession, coordinatorSession) && task.status === "blocked" && task.openQuestion?.status === "open" && task.openQuestion.questionId === entry.questionId;
28081
27942
  });
28082
27943
  if (validQueuedQuestions.length !== coordinatorState.queuedQuestions.length) {
28083
27944
  coordinatorState.queuedQuestions = validQueuedQuestions;
@@ -28566,7 +28427,7 @@ async function createScheduledTaskFromRoute(input, deps) {
28566
28427
  if (coordinatorSession.length === 0) {
28567
28428
  throw new Error("coordinatorSession must be a non-empty string");
28568
28429
  }
28569
- const route = deps.state.orchestration.coordinatorRoutes[coordinatorSession];
28430
+ const route = deps.state.orchestration.coordinatorRoutes[stableCoordinatorSession(coordinatorSession)];
28570
28431
  if (!route) {
28571
28432
  throw new Error(`no chat route is recorded for coordinator session "${coordinatorSession}"`);
28572
28433
  }
@@ -28593,7 +28454,7 @@ async function createScheduledTaskFromRoute(input, deps) {
28593
28454
  if (!session3) {
28594
28455
  throw new Error(`session "${route.sessionAlias}" recorded for coordinator session "${coordinatorSession}" was not found`);
28595
28456
  }
28596
- if (session3.transportSession !== coordinatorSession) {
28457
+ if (!sameCoordinatorSession(session3.transportSession, coordinatorSession)) {
28597
28458
  throw new Error(`session "${route.sessionAlias}" is no longer attached to coordinator session "${coordinatorSession}"`);
28598
28459
  }
28599
28460
  const executeAt = parseRouteScheduledTime(input.timeText, deps.now?.() ?? new Date);
@@ -28659,7 +28520,7 @@ function resolveOwnedCoordinatorRoute(coordinatorSession, state, label) {
28659
28520
  if (session3.length === 0) {
28660
28521
  throw new Error("coordinatorSession must be a non-empty string");
28661
28522
  }
28662
- const route = state.orchestration.coordinatorRoutes[session3];
28523
+ const route = state.orchestration.coordinatorRoutes[stableCoordinatorSession(session3)];
28663
28524
  if (!route) {
28664
28525
  throw new Error(`no chat route is recorded for coordinator session "${session3}"`);
28665
28526
  }
@@ -28751,9 +28612,10 @@ class SessionService {
28751
28612
  return this.state.chat_contexts[chatKey]?.current_session;
28752
28613
  }
28753
28614
  async getPreferredSessionForTransport(transportSession) {
28754
- const matches = Object.values(this.state.sessions).filter((session3) => session3.transport_session === transportSession).sort((left, right) => right.last_used_at.localeCompare(left.last_used_at));
28755
- const expectedAlias = transportSession.split(":").at(-1);
28756
- const expectedWorkspace = transportSession.split(":")[0];
28615
+ const target = stableCoordinatorSession(transportSession);
28616
+ const matches = Object.values(this.state.sessions).filter((session3) => stableCoordinatorSession(session3.transport_session) === target).sort((left, right) => right.last_used_at.localeCompare(left.last_used_at));
28617
+ const expectedAlias = target.split(":").at(-1);
28618
+ const expectedWorkspace = target.split(":")[0];
28757
28619
  const preferred = matches.find((session3) => session3.alias === expectedAlias && session3.workspace === expectedWorkspace) ?? matches[0];
28758
28620
  return preferred ? this.toResolvedSession(preferred) : null;
28759
28621
  }
@@ -29305,7 +29167,6 @@ async function runConsole(paths, deps) {
29305
29167
  let runtime = null;
29306
29168
  let consumerLock;
29307
29169
  let heartbeatTimer = null;
29308
- let gcResetTimer = null;
29309
29170
  let consumerLockAcquired = false;
29310
29171
  let daemonRuntimeStarted = false;
29311
29172
  const shutdownController = new AbortController;
@@ -29319,12 +29180,6 @@ async function runConsole(paths, deps) {
29319
29180
  if (deps.afterBuild) {
29320
29181
  await deps.afterBuild(runtime);
29321
29182
  }
29322
- try {
29323
- await runtime.orchestration.service.purgeExpiredResetCoordinators({
29324
- cutoffDays: 7,
29325
- trigger: "startup"
29326
- });
29327
- } catch {}
29328
29183
  try {
29329
29184
  await runtime.orchestration.service.reconcileParallelSlots();
29330
29185
  } catch (reconcileError) {
@@ -29380,6 +29235,7 @@ async function runConsole(paths, deps) {
29380
29235
  throw error2;
29381
29236
  }
29382
29237
  }
29238
+ await runtime.reapStaleQueueOwners();
29383
29239
  if (deps.beforeReady) {
29384
29240
  await deps.beforeReady(runtime);
29385
29241
  }
@@ -29393,10 +29249,6 @@ async function runConsole(paths, deps) {
29393
29249
  heartbeatTimer = setIntervalFn(() => {
29394
29250
  deps.daemonRuntime?.heartbeat().catch(() => {});
29395
29251
  }, deps.heartbeatIntervalMs ?? 30000);
29396
- const runtimeForGc = runtime;
29397
- gcResetTimer = setIntervalFn(() => {
29398
- runtimeForGc.orchestration.service.purgeExpiredResetCoordinators({ cutoffDays: 7, trigger: "interval" }).catch(() => {});
29399
- }, 86400000);
29400
29252
  }
29401
29253
  const channelStartPromise = deps.channels.startAll({
29402
29254
  agent: runtime.agent,
@@ -29451,7 +29303,6 @@ async function runConsole(paths, deps) {
29451
29303
  signalHandler,
29452
29304
  clearIntervalFn,
29453
29305
  heartbeatTimer,
29454
- gcResetTimer,
29455
29306
  ...deps.daemonRuntime ? { daemonRuntime: deps.daemonRuntime } : {},
29456
29307
  runtime,
29457
29308
  consumerLock,
@@ -29477,9 +29328,6 @@ async function runCleanupSequence(input) {
29477
29328
  if (input.heartbeatTimer !== null) {
29478
29329
  input.clearIntervalFn(input.heartbeatTimer);
29479
29330
  }
29480
- if (input.gcResetTimer !== null) {
29481
- input.clearIntervalFn(input.gcResetTimer);
29482
- }
29483
29331
  if (input.daemonRuntime && input.runtime) {
29484
29332
  try {
29485
29333
  await input.runtime.orchestration.server.stop();
@@ -31503,6 +31351,17 @@ var init_queue_owner_reaper = __esm(() => {
31503
31351
  });
31504
31352
 
31505
31353
  // src/transport/collect-reap-targets.ts
31354
+ function collectReapTargets(sessions, orchestration3, config4) {
31355
+ return [
31356
+ ...sessions.listAllResolvedSessions().map((session3) => ({
31357
+ agent: session3.agent,
31358
+ ...session3.agentCommand ? { agentCommand: session3.agentCommand } : {},
31359
+ cwd: session3.cwd,
31360
+ transportSession: session3.transportSession
31361
+ })),
31362
+ ...workerBindingReapTargets(orchestration3, config4)
31363
+ ];
31364
+ }
31506
31365
  function workerBindingReapTargets(orchestration3, config4) {
31507
31366
  const targets = [];
31508
31367
  for (const [workerSession, binding] of Object.entries(orchestration3.workerBindings)) {
@@ -32224,7 +32083,7 @@ async function buildApp(paths, deps = {}) {
32224
32083
  }
32225
32084
  },
32226
32085
  findReusableWorkerSession: async ({ coordinatorSession, workspace: workspace3, cwd, targetAgent, role }) => {
32227
- const binding = Object.entries(state.orchestration.workerBindings).find(([, current]) => current.ephemeral !== true && current.coordinatorSession === coordinatorSession && current.workspace === workspace3 && current.cwd === cwd && current.targetAgent === targetAgent && current.role === role);
32086
+ const binding = Object.entries(state.orchestration.workerBindings).find(([, current]) => current.ephemeral !== true && sameCoordinatorSession(current.coordinatorSession, coordinatorSession) && current.workspace === workspace3 && current.cwd === cwd && current.targetAgent === targetAgent && current.role === role);
32228
32087
  return binding?.[0] ?? null;
32229
32088
  },
32230
32089
  logger: logger2
@@ -32282,6 +32141,33 @@ async function buildApp(paths, deps = {}) {
32282
32141
  }),
32283
32142
  logger: logger2
32284
32143
  });
32144
+ const reapWarmQueueOwners = async (phase) => {
32145
+ try {
32146
+ const targets = collectReapTargets(sessions, state.orchestration, config4);
32147
+ if (targets.length === 0) {
32148
+ return;
32149
+ }
32150
+ const { terminated, attempted } = await reapQueueOwners(acpxCommand, targets, {
32151
+ onError: (target, error2) => {
32152
+ logger2.info("transport.queue_owner_reap.failed", "failed to reap queue owner", {
32153
+ phase,
32154
+ transport_session: target.transportSession,
32155
+ error: error2 instanceof Error ? error2.message : String(error2)
32156
+ }).catch(() => {});
32157
+ }
32158
+ });
32159
+ await logger2.info("transport.queue_owner_reap.completed", "reaped warm queue owners", {
32160
+ phase,
32161
+ terminated,
32162
+ attempted
32163
+ }).catch(() => {});
32164
+ } catch (err) {
32165
+ await logger2.error("transport.queue_owner_reap.error", "queue owner reap failed", {
32166
+ phase,
32167
+ error: err instanceof Error ? err.message : String(err)
32168
+ }).catch(() => {});
32169
+ }
32170
+ };
32285
32171
  return {
32286
32172
  agent: agent3,
32287
32173
  router: router3,
@@ -32302,41 +32188,14 @@ async function buildApp(paths, deps = {}) {
32302
32188
  service: scheduledService,
32303
32189
  scheduler: scheduledScheduler
32304
32190
  },
32191
+ reapStaleQueueOwners: () => reapWarmQueueOwners("startup"),
32305
32192
  dispose: async () => {
32306
32193
  scheduledScheduler.stop();
32307
32194
  if (progressHeartbeatInterval !== undefined) {
32308
32195
  clearInterval(progressHeartbeatInterval);
32309
32196
  }
32310
32197
  await Promise.allSettled([...pendingWorkerDispatches]);
32311
- try {
32312
- const targets = [
32313
- ...sessions.listAllResolvedSessions().map((session3) => ({
32314
- agent: session3.agent,
32315
- ...session3.agentCommand ? { agentCommand: session3.agentCommand } : {},
32316
- cwd: session3.cwd,
32317
- transportSession: session3.transportSession
32318
- })),
32319
- ...workerBindingReapTargets(state.orchestration, config4)
32320
- ];
32321
- if (targets.length > 0) {
32322
- const { terminated, attempted } = await reapQueueOwners(acpxCommand, targets, {
32323
- onError: (target, error2) => {
32324
- logger2.info("transport.queue_owner_reap.failed", "failed to reap queue owner on shutdown", {
32325
- transport_session: target.transportSession,
32326
- error: error2 instanceof Error ? error2.message : String(error2)
32327
- }).catch(() => {});
32328
- }
32329
- });
32330
- await logger2.info("transport.queue_owner_reap.completed", "reaped warm queue owners on shutdown", {
32331
- terminated,
32332
- attempted
32333
- }).catch(() => {});
32334
- }
32335
- } catch (err) {
32336
- await logger2.error("transport.queue_owner_reap.error", "queue owner reap failed during shutdown", {
32337
- error: err instanceof Error ? err.message : String(err)
32338
- }).catch(() => {});
32339
- }
32198
+ await reapWarmQueueOwners("shutdown");
32340
32199
  await debouncedStateStore.dispose();
32341
32200
  if ("dispose" in transport && typeof transport.dispose === "function") {
32342
32201
  await transport.dispose();
@@ -46059,6 +45918,9 @@ function requireHome(env) {
46059
45918
  return home;
46060
45919
  }
46061
45920
 
45921
+ // src/mcp/xacpx-mcp-server.ts
45922
+ init_endpoint_probe();
45923
+
46062
45924
  // src/mcp/xacpx-mcp-tools.ts
46063
45925
  init_task_watch_timeouts();
46064
45926
  init_quota_errors();
@@ -46574,7 +46436,7 @@ function formatToolError(error2) {
46574
46436
  init_orchestration_ipc();
46575
46437
  init_task_watch_timeouts();
46576
46438
  import { randomUUID } from "node:crypto";
46577
- import { createConnection } from "node:net";
46439
+ import { createConnection as createConnection2 } from "node:net";
46578
46440
 
46579
46441
  class OrchestrationClient {
46580
46442
  endpoint;
@@ -46642,7 +46504,7 @@ class OrchestrationClient {
46642
46504
  async request(method, params, timeoutMs = this.timeoutMs) {
46643
46505
  const id = this.createId();
46644
46506
  return await new Promise((resolve, reject) => {
46645
- const socket = createConnection(this.endpoint.path);
46507
+ const socket = createConnection2(this.endpoint.path);
46646
46508
  let buffer = "";
46647
46509
  let settled = false;
46648
46510
  let timer;
@@ -47285,7 +47147,9 @@ function installMcpStdioShutdownHooks(options) {
47285
47147
  const setIntervalFn = options.setIntervalFn ?? ((callback, ms) => setInterval(callback, ms));
47286
47148
  const clearIntervalFn = options.clearIntervalFn ?? ((handle) => clearInterval(handle));
47287
47149
  const parentPid = options.parentPid ?? process.ppid;
47288
- const parentCheckIntervalMs = options.parentCheckIntervalMs ?? parseParentCheckIntervalMs(coreEnv("MCP_PARENT_CHECK_INTERVAL_MS"));
47150
+ const parentCheckIntervalMs = options.parentCheckIntervalMs ?? parseIntervalMs(coreEnv("MCP_PARENT_CHECK_INTERVAL_MS"), 5000);
47151
+ const endpointCheckIntervalMs = options.endpointCheckIntervalMs ?? parseIntervalMs(coreEnv("MCP_ENDPOINT_CHECK_INTERVAL_MS"), 1e4);
47152
+ const endpointFailureThreshold = options.endpointFailureThreshold ?? 3;
47289
47153
  let disposed = false;
47290
47154
  let triggered = false;
47291
47155
  const triggerShutdown = (reason, context) => {
@@ -47318,6 +47182,31 @@ function installMcpStdioShutdownHooks(options) {
47318
47182
  }, parentCheckIntervalMs);
47319
47183
  parentTimer.unref?.();
47320
47184
  }
47185
+ let endpointTimer;
47186
+ const probeEndpoint = options.probeEndpoint;
47187
+ if (probeEndpoint && endpointCheckIntervalMs > 0 && endpointFailureThreshold > 0) {
47188
+ let consecutiveDead = 0;
47189
+ let probing = false;
47190
+ const runEndpointCheck = async () => {
47191
+ if (disposed || triggered || probing)
47192
+ return;
47193
+ probing = true;
47194
+ try {
47195
+ if (await probeEndpoint()) {
47196
+ consecutiveDead = 0;
47197
+ return;
47198
+ }
47199
+ consecutiveDead += 1;
47200
+ if (consecutiveDead >= endpointFailureThreshold) {
47201
+ triggerShutdown("daemon_endpoint_dead", { consecutiveFailures: consecutiveDead });
47202
+ }
47203
+ } catch {} finally {
47204
+ probing = false;
47205
+ }
47206
+ };
47207
+ endpointTimer = setIntervalFn(runEndpointCheck, endpointCheckIntervalMs);
47208
+ endpointTimer.unref?.();
47209
+ }
47321
47210
  return () => {
47322
47211
  if (disposed)
47323
47212
  return;
@@ -47332,13 +47221,16 @@ function installMcpStdioShutdownHooks(options) {
47332
47221
  if (parentTimer) {
47333
47222
  clearIntervalFn(parentTimer);
47334
47223
  }
47224
+ if (endpointTimer) {
47225
+ clearIntervalFn(endpointTimer);
47226
+ }
47335
47227
  };
47336
47228
  }
47337
- function parseParentCheckIntervalMs(raw) {
47229
+ function parseIntervalMs(raw, fallback) {
47338
47230
  if (raw === undefined || raw.trim().length === 0)
47339
- return 5000;
47231
+ return fallback;
47340
47232
  const parsed = Number(raw);
47341
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : 5000;
47233
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
47342
47234
  }
47343
47235
  function errorContext(error2) {
47344
47236
  const record3 = error2;
@@ -47357,7 +47249,8 @@ function defaultIsProcessRunning3(pid) {
47357
47249
  }
47358
47250
  }
47359
47251
  async function runXacpxMcpServer(options) {
47360
- const transport = options.transport ?? createOrchestrationTransport(options.endpoint ?? resolveDefaultOrchestrationEndpoint(process.env, process.platform));
47252
+ const endpoint = options.endpoint ?? resolveDefaultOrchestrationEndpoint(process.env, process.platform);
47253
+ const transport = options.transport ?? createOrchestrationTransport(endpoint);
47361
47254
  const server = createXacpxMcpServer({
47362
47255
  transport,
47363
47256
  ...options.coordinatorSession ? { coordinatorSession: options.coordinatorSession } : {},
@@ -47390,6 +47283,7 @@ async function runXacpxMcpServer(options) {
47390
47283
  stdin,
47391
47284
  stdout,
47392
47285
  shutdown,
47286
+ probeEndpoint: () => canConnectToEndpoint(endpoint.path, 4000),
47393
47287
  onDiagnostic: options.onDiagnostic
47394
47288
  });
47395
47289
  await server.connect(stdio);
@@ -47422,7 +47316,6 @@ function sanitizeMcpClientName(input) {
47422
47316
  fallback: "mcp-host"
47423
47317
  });
47424
47318
  }
47425
-
47426
47319
  // src/mcp/parse-string-flag.ts
47427
47320
  function parseStringFlag(args, env, options) {
47428
47321
  let fromFlag = null;
@@ -47658,7 +47551,6 @@ async function handleUpdateCli(args, deps) {
47658
47551
  kind: "plugin",
47659
47552
  name: plugin.name,
47660
47553
  currentVersion: plugin.version,
47661
- pinned: Boolean(plugin.version),
47662
47554
  latestVersion: await latestOf(plugin.name)
47663
47555
  });
47664
47556
  }
@@ -47667,12 +47559,12 @@ async function handleUpdateCli(args, deps) {
47667
47559
  const target = targets[index];
47668
47560
  deps.print(`${index + 1}. ${formatTarget(target)}`);
47669
47561
  }
47670
- const unavailable = targets.filter((target) => !target.latestVersion || target.kind === "plugin" && !target.pinned);
47562
+ const unavailable = targets.filter((target) => !target.latestVersion);
47671
47563
  if (all && unavailable.length > 0) {
47672
47564
  deps.print(t().cliUpdate.unavailableAborted(unavailable.map((target) => target.name).join(", ")));
47673
47565
  return 1;
47674
47566
  }
47675
- const candidates = targets.filter((target) => target.latestVersion && (target.kind !== "plugin" || target.pinned) && (target.successorPackage ? true : target.currentVersion !== target.latestVersion));
47567
+ const candidates = targets.filter((target) => target.latestVersion && (target.successorPackage ? true : target.currentVersion !== target.latestVersion));
47676
47568
  const selected = await selectTargets(targets, candidates, { all, explicitTarget: explicitTargets[0], deps });
47677
47569
  if (!selected.ok) {
47678
47570
  deps.print(selected.message);
@@ -47765,8 +47657,6 @@ async function selectTargets(targets, candidates, input) {
47765
47657
  return { ok: false, message: t().cliUpdate.targetNotFound(input.explicitTarget), exitCode: 1 };
47766
47658
  if (!target.latestVersion)
47767
47659
  return { ok: false, message: t().cliUpdate.targetVersionUnknown(target.name), exitCode: 1 };
47768
- if (target.kind === "plugin" && !target.pinned)
47769
- return { ok: false, message: t().cliUpdate.targetNotPinned(target.name), exitCode: 1 };
47770
47660
  if (!target.successorPackage && target.currentVersion === target.latestVersion)
47771
47661
  return { ok: true, targets: [] };
47772
47662
  return { ok: true, targets: [target] };
@@ -47790,8 +47680,6 @@ async function selectTargets(targets, candidates, input) {
47790
47680
  const target = targets[index - 1];
47791
47681
  if (!target.latestVersion)
47792
47682
  return { ok: false, message: t().cliUpdate.targetVersionUnknown(target.name), exitCode: 1 };
47793
- if (target.kind === "plugin" && !target.pinned)
47794
- return { ok: false, message: t().cliUpdate.targetNotPinned(target.name), exitCode: 1 };
47795
47683
  if (!target.successorPackage && target.currentVersion === target.latestVersion)
47796
47684
  continue;
47797
47685
  if (!selected.includes(target))
@@ -49205,7 +49093,7 @@ init_i18n();
49205
49093
  init_bootstrap();
49206
49094
  async function prepareMcpCoordinatorStartup(input) {
49207
49095
  const coordinatorSession = input.coordinatorSession.trim();
49208
- const existingSession = Object.values(input.state.sessions).find((session3) => session3.transport_session === coordinatorSession);
49096
+ const existingSession = Object.values(input.state.sessions).find((session3) => stableCoordinatorSession(session3.transport_session) === stableCoordinatorSession(coordinatorSession));
49209
49097
  const workspace3 = input.workspace?.trim();
49210
49098
  if (workspace3) {
49211
49099
  if (existingSession) {
@@ -49251,10 +49139,10 @@ function createMcpStdioIdentityResolver(input) {
49251
49139
  const workspace3 = input.workspace?.trim() || null;
49252
49140
  const sourceHandle = input.sourceHandle?.trim() || null;
49253
49141
  const resolvedWorkspace = workspace3;
49254
- const resolvedCoordinatorSession = parsedCoordinatorSession ?? inferExternalCoordinatorSession({
49142
+ const resolvedCoordinatorSession = stableCoordinatorSession(parsedCoordinatorSession ?? inferExternalCoordinatorSession({
49255
49143
  clientName: context.clientName,
49256
49144
  ...resolvedWorkspace ? { workspace: resolvedWorkspace } : { instanceId }
49257
- });
49145
+ }));
49258
49146
  const startup = await prepareMcpCoordinatorStartup({
49259
49147
  coordinatorSession: resolvedCoordinatorSession,
49260
49148
  ...resolvedWorkspace ? { workspace: resolvedWorkspace } : {},