@ganglion/xacpx 0.9.1 → 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/README.md +4 -2
- package/dist/bridge/bridge-main.js +39 -17
- package/dist/cli.js +415 -409
- package/dist/commands/command-hints.d.ts +1 -1
- package/dist/commands/command-list.d.ts +3 -3
- package/dist/commands/handlers/resolve-reply-mode.d.ts +13 -0
- package/dist/config/types.d.ts +1 -0
- package/dist/i18n/types.d.ts +6 -2
- package/dist/orchestration/coordinator-identity.d.ts +27 -0
- package/dist/orchestration/orchestration-service.d.ts +0 -23
- package/dist/plugin-api.d.ts +1 -1
- package/dist/plugin-api.js +27 -13
- package/dist/plugins/compatibility.d.ts +4 -1
- package/dist/plugins/types.d.ts +2 -2
- package/dist/state/types.d.ts +1 -1
- package/dist/transport/types.d.ts +9 -1
- package/dist/version.d.ts +2 -2
- package/dist/weixin/agent/interface.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -113,10 +113,11 @@ var init_session = __esm(() => {
|
|
|
113
113
|
replyModeHeader: "Current reply mode:",
|
|
114
114
|
replyModeSessionLabel: (alias) => `- Session: ${alias}`,
|
|
115
115
|
replyModeGlobalDefault: (value) => `- Global default: ${value}`,
|
|
116
|
+
replyModeChannelDefault: (value) => `- Channel default: ${value}`,
|
|
116
117
|
replyModeSessionOverride: (value) => `- Session override: ${value}`,
|
|
117
118
|
replyModeEffective: (value) => `- Effective: ${value}`,
|
|
118
119
|
replyModeSet: (replyMode) => `Current session reply mode set to: ${replyMode}`,
|
|
119
|
-
replyModeReset: (
|
|
120
|
+
replyModeReset: (effective) => `Session reply mode reset. Now effective: ${effective}`,
|
|
120
121
|
statusHeader: "Current session:",
|
|
121
122
|
statusNameLabel: (alias) => `- Name: ${alias}`,
|
|
122
123
|
statusAgentLabel: (agent) => `- Agent: ${agent}`,
|
|
@@ -659,6 +660,8 @@ var init_config = __esm(() => {
|
|
|
659
660
|
mustBePositiveNumber: (path) => `${path} must be a positive number.`,
|
|
660
661
|
channelTypeDisabled: "channel.type is a legacy single-channel field; /config set writes are disabled. Use `xacpx channel ...` to manage channels[], then restart xacpx.",
|
|
661
662
|
channelReplyModeInvalid: "channel.replyMode only supports: stream, final, verbose",
|
|
663
|
+
channelRuntimeNotFound: (id) => `Channel "${id}" does not exist; add it first with \`xacpx channel add ${id}\`.`,
|
|
664
|
+
channelRuntimeReplyModeInvalid: (id) => `channels.${id}.replyMode only supports: stream, final, verbose`,
|
|
662
665
|
wechatReplyModeInvalid: "wechat.replyMode only supports: stream, final, verbose",
|
|
663
666
|
wechatReplyModeMapped: (value) => `${value} (mapped to channel.replyMode)`,
|
|
664
667
|
agentNotFound: (name) => `Agent "${name}" does not exist. Create it first.`,
|
|
@@ -848,7 +851,6 @@ var init_cli_update = __esm(() => {
|
|
|
848
851
|
updateFailed: (name, error) => `${name} update failed: ${error}`,
|
|
849
852
|
targetNotFound: (name) => `Update target not found: ${name}`,
|
|
850
853
|
targetVersionUnknown: (name) => `${name}: cannot check latest version; skipped.`,
|
|
851
|
-
targetNotPinned: (name) => `${name} has no recorded version; use \`xacpx plugin update ${name}\` or specify a version explicitly.`,
|
|
852
854
|
multiTargetNonInteractive: "Installed plugins detected; in non-interactive mode use `xacpx update --all` or `xacpx update <name>`.",
|
|
853
855
|
selectionPrompt: "Select items to update (numbers, comma-separated; a=all; Enter to cancel): ",
|
|
854
856
|
selectionInvalid: (part) => `Invalid selection: ${part}`,
|
|
@@ -879,6 +881,8 @@ var init_channel_cli = __esm(() => {
|
|
|
879
881
|
channelRemoved: (id) => `Channel ${id} removed`,
|
|
880
882
|
cannotDisableLastEnabled: "Cannot disable the last enabled channel.",
|
|
881
883
|
channelEnabledToggled: (id, enabled) => `Channel ${id} ${enabled ? "enabled" : "disabled"}`,
|
|
884
|
+
channelReplyModeSet: (id, mode) => `Channel ${id} default reply mode set to: ${mode}`,
|
|
885
|
+
channelReplyModeInvalid: (mode) => `reply mode must be stream / final / verbose, got: ${mode}`,
|
|
882
886
|
channelAccountAlreadyExists: (type, accountId) => `Account ${accountId} already exists on channel ${type}; run xacpx channel rm ${type} --account ${accountId} first`,
|
|
883
887
|
channelAccountAdded: (type, accountId) => `Channel ${type} account ${accountId} added`,
|
|
884
888
|
channelReEnabled: (type) => `Channel ${type} was disabled; it has been automatically re-enabled.`,
|
|
@@ -1179,10 +1183,11 @@ var init_session2 = __esm(() => {
|
|
|
1179
1183
|
replyModeHeader: "当前 reply mode:",
|
|
1180
1184
|
replyModeSessionLabel: (alias) => `- 会话:${alias}`,
|
|
1181
1185
|
replyModeGlobalDefault: (value) => `- 全局默认:${value}`,
|
|
1186
|
+
replyModeChannelDefault: (value) => `- 频道默认:${value}`,
|
|
1182
1187
|
replyModeSessionOverride: (value) => `- 当前会话覆盖:${value}`,
|
|
1183
1188
|
replyModeEffective: (value) => `- 当前生效:${value}`,
|
|
1184
1189
|
replyModeSet: (replyMode) => `已设置当前会话 reply mode:${replyMode}`,
|
|
1185
|
-
replyModeReset: (
|
|
1190
|
+
replyModeReset: (effective) => `已重置当前会话 reply mode,当前生效:${effective}`,
|
|
1186
1191
|
statusHeader: "当前会话:",
|
|
1187
1192
|
statusNameLabel: (alias) => `- 名称:${alias}`,
|
|
1188
1193
|
statusAgentLabel: (agent2) => `- Agent:${agent2}`,
|
|
@@ -1725,6 +1730,8 @@ var init_config2 = __esm(() => {
|
|
|
1725
1730
|
mustBePositiveNumber: (path) => `${path} 必须是正数。`,
|
|
1726
1731
|
channelTypeDisabled: "channel.type 是旧单频道字段,/config set 已禁用写入;请使用 `xacpx channel ...` 管理 channels[],然后重启 xacpx。",
|
|
1727
1732
|
channelReplyModeInvalid: "channel.replyMode 只支持:stream、final、verbose",
|
|
1733
|
+
channelRuntimeNotFound: (id) => `频道「${id}」不存在;请先用 \`xacpx channel add ${id}\` 添加。`,
|
|
1734
|
+
channelRuntimeReplyModeInvalid: (id) => `channels.${id}.replyMode 只支持:stream、final、verbose`,
|
|
1728
1735
|
wechatReplyModeInvalid: "wechat.replyMode 只支持:stream、final、verbose",
|
|
1729
1736
|
wechatReplyModeMapped: (value) => `${value}(已映射到 channel.replyMode)`,
|
|
1730
1737
|
agentNotFound: (name) => `Agent「${name}」不存在,请先创建。`,
|
|
@@ -1914,7 +1921,6 @@ var init_cli_update2 = __esm(() => {
|
|
|
1914
1921
|
updateFailed: (name, error) => `${name} 更新失败:${error}`,
|
|
1915
1922
|
targetNotFound: (name) => `没有找到更新项:${name}`,
|
|
1916
1923
|
targetVersionUnknown: (name) => `${name} 无法检查最新版本,已跳过。`,
|
|
1917
|
-
targetNotPinned: (name) => `${name} 未记录当前版本;请先使用 \`xacpx plugin update ${name}\` 或显式选择版本。`,
|
|
1918
1924
|
multiTargetNonInteractive: "检测到已安装插件;非交互模式请使用 `xacpx update --all` 或 `xacpx update <name>`。",
|
|
1919
1925
|
selectionPrompt: "请选择要更新的项目(数字,逗号分隔,a=全部,回车取消):",
|
|
1920
1926
|
selectionInvalid: (part) => `无效选择:${part}`,
|
|
@@ -1945,6 +1951,8 @@ var init_channel_cli2 = __esm(() => {
|
|
|
1945
1951
|
channelRemoved: (id) => `频道 ${id} 已删除`,
|
|
1946
1952
|
cannotDisableLastEnabled: "不能禁用最后一个启用的频道。",
|
|
1947
1953
|
channelEnabledToggled: (id, enabled) => `频道 ${id} 已${enabled ? "启用" : "禁用"}`,
|
|
1954
|
+
channelReplyModeSet: (id, mode) => `频道 ${id} 的默认 reply mode 已设置为:${mode}`,
|
|
1955
|
+
channelReplyModeInvalid: (mode) => `reply mode 只支持 stream / final / verbose,收到:${mode}`,
|
|
1948
1956
|
channelAccountAlreadyExists: (type, accountId) => `频道 ${type} 的账号 ${accountId} 已存在;先 xacpx channel rm ${type} --account ${accountId}`,
|
|
1949
1957
|
channelAccountAdded: (type, accountId) => `频道 ${type} 账号 ${accountId} 已添加`,
|
|
1950
1958
|
channelReEnabled: (type) => `频道 ${type} 此前是 disabled 状态,已自动启用。`,
|
|
@@ -4647,6 +4655,9 @@ function parseRuntimeChannelConfig(raw, index) {
|
|
|
4647
4655
|
throw new Error(`channels[${index}].type must be a non-empty string`);
|
|
4648
4656
|
}
|
|
4649
4657
|
const enabled = raw.enabled !== false;
|
|
4658
|
+
if ("replyMode" in raw && !isReplyMode(raw.replyMode)) {
|
|
4659
|
+
throw new Error(`channels[${index}].replyMode must be stream, final, or verbose`);
|
|
4660
|
+
}
|
|
4650
4661
|
let options = undefined;
|
|
4651
4662
|
if ("feishu" in raw && isRecord(raw.feishu)) {
|
|
4652
4663
|
options = raw.feishu;
|
|
@@ -4657,6 +4668,7 @@ function parseRuntimeChannelConfig(raw, index) {
|
|
|
4657
4668
|
id,
|
|
4658
4669
|
type: raw.type,
|
|
4659
4670
|
enabled,
|
|
4671
|
+
...isReplyMode(raw.replyMode) ? { replyMode: raw.replyMode } : {},
|
|
4660
4672
|
...options ? { options } : {}
|
|
4661
4673
|
};
|
|
4662
4674
|
}
|
|
@@ -5394,7 +5406,7 @@ function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
|
5394
5406
|
const suffix = createHash("sha256").update(runtimeDir).digest("hex").slice(0, 12);
|
|
5395
5407
|
return {
|
|
5396
5408
|
kind: "named-pipe",
|
|
5397
|
-
path: `\\\\.\\pipe\\
|
|
5409
|
+
path: `\\\\.\\pipe\\xacpx-orchestration-${suffix}`
|
|
5398
5410
|
};
|
|
5399
5411
|
}
|
|
5400
5412
|
return {
|
|
@@ -11940,11 +11952,46 @@ function readVersion(moduleUrl = import.meta.url) {
|
|
|
11940
11952
|
}
|
|
11941
11953
|
return "unknown";
|
|
11942
11954
|
}
|
|
11943
|
-
var PACKAGE_NAME = "@ganglion/xacpx",
|
|
11955
|
+
var PACKAGE_NAME = "@ganglion/xacpx", XACPX_CORE_VERSION;
|
|
11944
11956
|
var init_version = __esm(() => {
|
|
11945
|
-
|
|
11957
|
+
XACPX_CORE_VERSION = readVersion();
|
|
11946
11958
|
});
|
|
11947
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
|
+
|
|
11948
11995
|
// src/orchestration/task-watch-timeouts.ts
|
|
11949
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;
|
|
11950
11997
|
var init_task_watch_timeouts = __esm(() => {
|
|
@@ -12000,6 +12047,14 @@ function escapeRegExp(s) {
|
|
|
12000
12047
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
12001
12048
|
}
|
|
12002
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
|
+
|
|
12003
12058
|
// src/util/text.ts
|
|
12004
12059
|
function truncateText(text, maxLength, ellipsis = "…") {
|
|
12005
12060
|
if (text.length <= maxLength)
|
|
@@ -12281,7 +12336,7 @@ function isReplyMode2(value) {
|
|
|
12281
12336
|
return value === "stream" || value === "final" || value === "verbose";
|
|
12282
12337
|
}
|
|
12283
12338
|
function isSessionSource(value) {
|
|
12284
|
-
return value === undefined || value === "weacpx" || value === "agent-side";
|
|
12339
|
+
return value === undefined || value === "weacpx" || value === "xacpx" || value === "agent-side";
|
|
12285
12340
|
}
|
|
12286
12341
|
function isSessionRecord(value) {
|
|
12287
12342
|
if (!isRecord2(value)) {
|
|
@@ -12813,7 +12868,7 @@ async function ensureCoreResolution(pluginHome) {
|
|
|
12813
12868
|
await copyFile(srcJs, dstJs);
|
|
12814
12869
|
} catch (error2) {
|
|
12815
12870
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
12816
|
-
console.warn(`
|
|
12871
|
+
console.warn(`xacpx: skipped plugin-api resolution shim for "${name}" — could not copy ${srcJs} (${message}). ` + `Channel plugins importing "${name}/plugin-api" at runtime may fail to load.`);
|
|
12817
12872
|
continue;
|
|
12818
12873
|
}
|
|
12819
12874
|
await writeFile4(join6(targetDir, "package.json"), JSON.stringify({
|
|
@@ -13712,7 +13767,7 @@ async function sendTyping(params) {
|
|
|
13712
13767
|
label: "sendTyping"
|
|
13713
13768
|
});
|
|
13714
13769
|
}
|
|
13715
|
-
var CHANNEL_VERSION, ILINK_APP_CLIENT_VERSION, ILINK_APP_ID, DEFAULT_BOT_AGENT = "
|
|
13770
|
+
var CHANNEL_VERSION, ILINK_APP_CLIENT_VERSION, ILINK_APP_ID, DEFAULT_BOT_AGENT = "xacpx", BOT_AGENT_MAX_LEN = 256, DEFAULT_LONG_POLL_TIMEOUT_MS = 35000, DEFAULT_API_TIMEOUT_MS = 15000, DEFAULT_CONFIG_TIMEOUT_MS = 1e4;
|
|
13716
13771
|
var init_api = __esm(() => {
|
|
13717
13772
|
init_version();
|
|
13718
13773
|
init_accounts();
|
|
@@ -19158,19 +19213,19 @@ function cmpTuple(a, b) {
|
|
|
19158
19213
|
return 0;
|
|
19159
19214
|
}
|
|
19160
19215
|
function validatePluginCompatibility(metadata, context) {
|
|
19161
|
-
const { packageName,
|
|
19216
|
+
const { packageName, currentXacpxVersion } = context;
|
|
19162
19217
|
const apiVersion = metadata.apiVersion;
|
|
19163
19218
|
if (typeof apiVersion !== "number") {
|
|
19164
19219
|
throw new Error(t().pluginCli.compatMissingApiVersion(packageName));
|
|
19165
19220
|
}
|
|
19166
|
-
if (!
|
|
19167
|
-
const supported =
|
|
19221
|
+
if (!XACPX_PLUGIN_API_SUPPORTED_VERSIONS.includes(apiVersion)) {
|
|
19222
|
+
const supported = XACPX_PLUGIN_API_SUPPORTED_VERSIONS.join(", ");
|
|
19168
19223
|
throw new Error(t().pluginCli.compatUnsupportedApiVersion(packageName, apiVersion, supported));
|
|
19169
19224
|
}
|
|
19170
|
-
if (!
|
|
19225
|
+
if (!currentXacpxVersion || currentXacpxVersion === "unknown") {
|
|
19171
19226
|
return;
|
|
19172
19227
|
}
|
|
19173
|
-
const normalizedCurrent = normalizeCoreVersionForCompat(
|
|
19228
|
+
const normalizedCurrent = normalizeCoreVersionForCompat(currentXacpxVersion);
|
|
19174
19229
|
const minVersion = metadata.minXacpxVersion ?? metadata.minWeacpxVersion;
|
|
19175
19230
|
const minVersionField = metadata.minXacpxVersion !== undefined ? "minXacpxVersion" : "minWeacpxVersion";
|
|
19176
19231
|
if (minVersion !== undefined) {
|
|
@@ -19185,7 +19240,7 @@ function validatePluginCompatibility(metadata, context) {
|
|
|
19185
19240
|
throw new Error(t().pluginCli.compatInvalidMinVersionDetail(packageName, minVersionField, detail));
|
|
19186
19241
|
}
|
|
19187
19242
|
if (!satisfied) {
|
|
19188
|
-
throw new Error(t().pluginCli.compatMinVersionNotSatisfied(packageName, minVersion,
|
|
19243
|
+
throw new Error(t().pluginCli.compatMinVersionNotSatisfied(packageName, minVersion, currentXacpxVersion));
|
|
19189
19244
|
}
|
|
19190
19245
|
}
|
|
19191
19246
|
const compatibleVersions = metadata.compatibleXacpxVersions ?? metadata.compatibleWeacpxVersions;
|
|
@@ -19202,14 +19257,17 @@ function validatePluginCompatibility(metadata, context) {
|
|
|
19202
19257
|
throw new Error(t().pluginCli.compatInvalidCompatibleVersionsDetail(packageName, compatibleField, detail));
|
|
19203
19258
|
}
|
|
19204
19259
|
if (!satisfied) {
|
|
19205
|
-
throw new Error(t().pluginCli.compatCompatibleVersionsNotSatisfied(packageName, compatibleVersions,
|
|
19260
|
+
throw new Error(t().pluginCli.compatCompatibleVersionsNotSatisfied(packageName, compatibleVersions, currentXacpxVersion));
|
|
19206
19261
|
}
|
|
19207
19262
|
}
|
|
19208
19263
|
}
|
|
19209
|
-
var
|
|
19264
|
+
var XACPX_PLUGIN_API_VERSION = 1, XACPX_PLUGIN_API_SUPPORTED_VERSIONS, XACPX_PLUGIN_MIN_CORE_VERSION = "0.5.0", WEACPX_PLUGIN_API_VERSION, WEACPX_PLUGIN_API_SUPPORTED_VERSIONS, WEACPX_PLUGIN_MIN_CORE_VERSION, SEMVER_RE;
|
|
19210
19265
|
var init_compatibility = __esm(() => {
|
|
19211
19266
|
init_i18n();
|
|
19212
|
-
|
|
19267
|
+
XACPX_PLUGIN_API_SUPPORTED_VERSIONS = [1];
|
|
19268
|
+
WEACPX_PLUGIN_API_VERSION = XACPX_PLUGIN_API_VERSION;
|
|
19269
|
+
WEACPX_PLUGIN_API_SUPPORTED_VERSIONS = XACPX_PLUGIN_API_SUPPORTED_VERSIONS;
|
|
19270
|
+
WEACPX_PLUGIN_MIN_CORE_VERSION = XACPX_PLUGIN_MIN_CORE_VERSION;
|
|
19213
19271
|
SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
19214
19272
|
});
|
|
19215
19273
|
|
|
@@ -19224,14 +19282,14 @@ function validateWeacpxPlugin(value, packageName, options = {}) {
|
|
|
19224
19282
|
if (!isRecord(plugin)) {
|
|
19225
19283
|
throw new Error(t().pluginCli.pluginNoDefaultExport(packageName));
|
|
19226
19284
|
}
|
|
19227
|
-
const
|
|
19285
|
+
const currentXacpxVersion = options.currentXacpxVersion ?? readVersion();
|
|
19228
19286
|
validatePluginCompatibility({
|
|
19229
19287
|
apiVersion: plugin.apiVersion,
|
|
19230
19288
|
minWeacpxVersion: plugin.minWeacpxVersion,
|
|
19231
19289
|
compatibleWeacpxVersions: plugin.compatibleWeacpxVersions,
|
|
19232
19290
|
minXacpxVersion: plugin.minXacpxVersion,
|
|
19233
19291
|
compatibleXacpxVersions: plugin.compatibleXacpxVersions
|
|
19234
|
-
}, { packageName,
|
|
19292
|
+
}, { packageName, currentXacpxVersion });
|
|
19235
19293
|
if ("name" in plugin && typeof plugin.name === "string" && plugin.name.trim() && plugin.name.trim() !== packageName) {
|
|
19236
19294
|
throw new Error(t().pluginCli.pluginNameMismatch(packageName, plugin.name));
|
|
19237
19295
|
}
|
|
@@ -19260,7 +19318,7 @@ function validateWeacpxPlugin(value, packageName, options = {}) {
|
|
|
19260
19318
|
}
|
|
19261
19319
|
}
|
|
19262
19320
|
const normalized = {
|
|
19263
|
-
apiVersion:
|
|
19321
|
+
apiVersion: XACPX_PLUGIN_API_VERSION,
|
|
19264
19322
|
...typeof plugin.name === "string" && plugin.name.trim() ? { name: plugin.name.trim() } : { name: packageName },
|
|
19265
19323
|
channels
|
|
19266
19324
|
};
|
|
@@ -19314,7 +19372,7 @@ async function loadConfiguredPlugins(input) {
|
|
|
19314
19372
|
throw new Error(`failed to load plugin ${config4.name}: ${message}`);
|
|
19315
19373
|
}
|
|
19316
19374
|
const plugin = validateWeacpxPlugin(moduleValue, config4.name, {
|
|
19317
|
-
...input.
|
|
19375
|
+
...input.currentXacpxVersion !== undefined ? { currentXacpxVersion: input.currentXacpxVersion } : {}
|
|
19318
19376
|
});
|
|
19319
19377
|
const channels = plugin.channels ?? [];
|
|
19320
19378
|
for (const channel of channels) {
|
|
@@ -19566,16 +19624,16 @@ var init_prompt_output = __esm(() => {
|
|
|
19566
19624
|
});
|
|
19567
19625
|
|
|
19568
19626
|
// src/commands/command-list.ts
|
|
19569
|
-
function
|
|
19627
|
+
function isKnownXacpxCommandPrefix(prefix) {
|
|
19570
19628
|
return KNOWN_COMMAND_PREFIX_SET.has(prefix.toLowerCase());
|
|
19571
19629
|
}
|
|
19572
|
-
function
|
|
19630
|
+
function isKnownXacpxCommandText(text) {
|
|
19573
19631
|
const firstToken = text.trim().split(/\s+/, 1)[0];
|
|
19574
|
-
return Boolean(firstToken &&
|
|
19632
|
+
return Boolean(firstToken && isKnownXacpxCommandPrefix(firstToken));
|
|
19575
19633
|
}
|
|
19576
|
-
var
|
|
19634
|
+
var XACPX_KNOWN_COMMAND_PREFIXES, KNOWN_COMMAND_PREFIX_SET;
|
|
19577
19635
|
var init_command_list = __esm(() => {
|
|
19578
|
-
|
|
19636
|
+
XACPX_KNOWN_COMMAND_PREFIXES = [
|
|
19579
19637
|
"/help",
|
|
19580
19638
|
"/agents",
|
|
19581
19639
|
"/workspaces",
|
|
@@ -19605,7 +19663,7 @@ var init_command_list = __esm(() => {
|
|
|
19605
19663
|
"/later",
|
|
19606
19664
|
"/lt"
|
|
19607
19665
|
];
|
|
19608
|
-
KNOWN_COMMAND_PREFIX_SET = new Set(
|
|
19666
|
+
KNOWN_COMMAND_PREFIX_SET = new Set(XACPX_KNOWN_COMMAND_PREFIXES);
|
|
19609
19667
|
});
|
|
19610
19668
|
|
|
19611
19669
|
// src/commands/parse-command.ts
|
|
@@ -20095,7 +20153,7 @@ function normalizeCommand(command) {
|
|
|
20095
20153
|
return command;
|
|
20096
20154
|
}
|
|
20097
20155
|
function isRecognizedCommand(command) {
|
|
20098
|
-
return
|
|
20156
|
+
return isKnownXacpxCommandPrefix(command);
|
|
20099
20157
|
}
|
|
20100
20158
|
function toPermissionMode(value) {
|
|
20101
20159
|
if (value === "allow")
|
|
@@ -20609,6 +20667,23 @@ function applySupportedConfigUpdate(config4, path14, rawValue) {
|
|
|
20609
20667
|
}
|
|
20610
20668
|
return { renderedValue: rawValue };
|
|
20611
20669
|
}
|
|
20670
|
+
const channelMatch = path14.match(/^channels\.([^.]+)\.replyMode$/);
|
|
20671
|
+
if (channelMatch) {
|
|
20672
|
+
const [, id] = channelMatch;
|
|
20673
|
+
if (!id) {
|
|
20674
|
+
return { error: c.pathNotSupported(path14) };
|
|
20675
|
+
}
|
|
20676
|
+
const channel = config4.channels.find((entry) => entry.id === id);
|
|
20677
|
+
if (!channel) {
|
|
20678
|
+
return { error: c.channelRuntimeNotFound(id) };
|
|
20679
|
+
}
|
|
20680
|
+
const parsed = parseEnum(rawValue, ["stream", "final", "verbose"]);
|
|
20681
|
+
if (!parsed) {
|
|
20682
|
+
return { error: c.channelRuntimeReplyModeInvalid(id) };
|
|
20683
|
+
}
|
|
20684
|
+
channel.replyMode = parsed;
|
|
20685
|
+
return { renderedValue: parsed };
|
|
20686
|
+
}
|
|
20612
20687
|
return { error: c.pathNotSupported(path14) };
|
|
20613
20688
|
}
|
|
20614
20689
|
function parseEnum(value, allowed) {
|
|
@@ -20638,6 +20713,7 @@ var init_config_handler = __esm(() => {
|
|
|
20638
20713
|
"logging.maxFiles",
|
|
20639
20714
|
"logging.retentionDays",
|
|
20640
20715
|
"channel.replyMode",
|
|
20716
|
+
"channels.<id>.replyMode",
|
|
20641
20717
|
"agents.<name>.driver",
|
|
20642
20718
|
"agents.<name>.command",
|
|
20643
20719
|
"workspaces.<name>.cwd",
|
|
@@ -20978,6 +21054,20 @@ var init_build_coordinator_prompt = __esm(() => {
|
|
|
20978
21054
|
init_render_delegate_question_package();
|
|
20979
21055
|
});
|
|
20980
21056
|
|
|
21057
|
+
// src/commands/handlers/resolve-reply-mode.ts
|
|
21058
|
+
function resolveChannelDefaultReplyMode(config4, chatKey) {
|
|
21059
|
+
if (!config4)
|
|
21060
|
+
return;
|
|
21061
|
+
const channelId = getChannelIdFromChatKey(chatKey);
|
|
21062
|
+
return config4.channels.find((channel) => channel.id === channelId)?.replyMode;
|
|
21063
|
+
}
|
|
21064
|
+
function resolveEffectiveReplyMode(config4, chatKey, sessionOverride) {
|
|
21065
|
+
return sessionOverride ?? resolveChannelDefaultReplyMode(config4, chatKey) ?? config4?.channel.replyMode ?? "verbose";
|
|
21066
|
+
}
|
|
21067
|
+
var init_resolve_reply_mode = __esm(() => {
|
|
21068
|
+
init_channel_scope();
|
|
21069
|
+
});
|
|
21070
|
+
|
|
20981
21071
|
// src/commands/handlers/session-list-marker.ts
|
|
20982
21072
|
function decorateUnread(label, hasUnread) {
|
|
20983
21073
|
return hasUnread ? `● ${label}` : label;
|
|
@@ -21282,14 +21372,16 @@ async function handleReplyModeShow(context, chatKey) {
|
|
|
21282
21372
|
return { text: t().session.noCurrent };
|
|
21283
21373
|
}
|
|
21284
21374
|
const globalDefault = context.config?.channel.replyMode ?? "verbose";
|
|
21375
|
+
const channelDefault = resolveChannelDefaultReplyMode(context.config, chatKey);
|
|
21285
21376
|
const sessionOverride = session3.replyMode;
|
|
21286
|
-
const effective =
|
|
21377
|
+
const effective = resolveEffectiveReplyMode(context.config, chatKey, sessionOverride);
|
|
21287
21378
|
const s = t().session;
|
|
21288
21379
|
return {
|
|
21289
21380
|
text: [
|
|
21290
21381
|
s.replyModeHeader,
|
|
21291
21382
|
s.replyModeSessionLabel(toDisplaySessionAlias(session3.alias)),
|
|
21292
21383
|
s.replyModeGlobalDefault(globalDefault),
|
|
21384
|
+
s.replyModeChannelDefault(channelDefault ?? s.modeNotSet),
|
|
21293
21385
|
s.replyModeSessionOverride(sessionOverride ?? s.modeNotSet),
|
|
21294
21386
|
s.replyModeEffective(effective)
|
|
21295
21387
|
].join(`
|
|
@@ -21310,8 +21402,8 @@ async function handleReplyModeReset(context, chatKey) {
|
|
|
21310
21402
|
return { text: t().session.noCurrent };
|
|
21311
21403
|
}
|
|
21312
21404
|
await context.sessions.setCurrentSessionReplyMode(chatKey, undefined);
|
|
21313
|
-
const
|
|
21314
|
-
return { text: t().session.replyModeReset(
|
|
21405
|
+
const fallback = resolveEffectiveReplyMode(context.config, chatKey, undefined);
|
|
21406
|
+
return { text: t().session.replyModeReset(fallback) };
|
|
21315
21407
|
}
|
|
21316
21408
|
async function handleStatus(context, chatKey) {
|
|
21317
21409
|
const session3 = await context.sessions.getCurrentSession(chatKey);
|
|
@@ -21458,14 +21550,14 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
21458
21550
|
`) };
|
|
21459
21551
|
}
|
|
21460
21552
|
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
21461
|
-
const effectiveReplyMode =
|
|
21553
|
+
const effectiveReplyMode = resolveEffectiveReplyMode(context.config, chatKey, session3.replyMode);
|
|
21462
21554
|
if (!session3.replyMode)
|
|
21463
21555
|
session3.replyMode = effectiveReplyMode;
|
|
21464
21556
|
const transportReply = effectiveReplyMode !== "final" ? reply : undefined;
|
|
21465
21557
|
if (context.orchestration) {
|
|
21466
21558
|
try {
|
|
21467
21559
|
await context.orchestration.recordCoordinatorRouteContext?.({
|
|
21468
|
-
coordinatorSession: session3.transportSession,
|
|
21560
|
+
coordinatorSession: stableCoordinatorSession(session3.transportSession),
|
|
21469
21561
|
chatKey,
|
|
21470
21562
|
sessionAlias: session3.alias,
|
|
21471
21563
|
...replyContextToken ? { replyContextToken } : {},
|
|
@@ -21548,7 +21640,7 @@ async function preparePromptWithFallback(context, session3, chatKey, text, reply
|
|
|
21548
21640
|
try {
|
|
21549
21641
|
return await buildCoordinatorPrompt({
|
|
21550
21642
|
orchestration: orchestration3,
|
|
21551
|
-
coordinatorSession: session3.transportSession,
|
|
21643
|
+
coordinatorSession: stableCoordinatorSession(session3.transportSession),
|
|
21552
21644
|
chatKey,
|
|
21553
21645
|
userText: text,
|
|
21554
21646
|
...replyContextToken ? { replyContextToken } : {},
|
|
@@ -21606,6 +21698,7 @@ var DEFAULT_SESSION_TAIL_LINES = 50, MAX_SESSION_TAIL_LINES = 500;
|
|
|
21606
21698
|
var init_session_handler = __esm(() => {
|
|
21607
21699
|
init_build_coordinator_prompt();
|
|
21608
21700
|
init_channel_scope();
|
|
21701
|
+
init_resolve_reply_mode();
|
|
21609
21702
|
init_workspace_name();
|
|
21610
21703
|
init_i18n();
|
|
21611
21704
|
});
|
|
@@ -21882,6 +21975,7 @@ async function handleDelegateRequest(context, chatKey, targetAgent, task, role,
|
|
|
21882
21975
|
if (!session3) {
|
|
21883
21976
|
return { text: t().orchestration.noCurrentSession };
|
|
21884
21977
|
}
|
|
21978
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
21885
21979
|
const orchestration3 = getOrchestration(context);
|
|
21886
21980
|
if (!orchestration3) {
|
|
21887
21981
|
return { text: renderOrchestrationUnavailable() };
|
|
@@ -21889,7 +21983,7 @@ async function handleDelegateRequest(context, chatKey, targetAgent, task, role,
|
|
|
21889
21983
|
const result = await orchestration3.requestDelegate({
|
|
21890
21984
|
sourceHandle: session3.transportSession,
|
|
21891
21985
|
sourceKind: "coordinator",
|
|
21892
|
-
coordinatorSession
|
|
21986
|
+
coordinatorSession,
|
|
21893
21987
|
workspace: session3.workspace,
|
|
21894
21988
|
targetAgent,
|
|
21895
21989
|
task,
|
|
@@ -21906,12 +22000,13 @@ async function handleGroupCreate(context, chatKey, title) {
|
|
|
21906
22000
|
if (!session3) {
|
|
21907
22001
|
return { text: t().orchestration.noCurrentSession };
|
|
21908
22002
|
}
|
|
22003
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
21909
22004
|
const orchestration3 = getOrchestration(context);
|
|
21910
22005
|
if (!orchestration3) {
|
|
21911
22006
|
return { text: renderOrchestrationUnavailable() };
|
|
21912
22007
|
}
|
|
21913
22008
|
const group = await orchestration3.createGroup({
|
|
21914
|
-
coordinatorSession
|
|
22009
|
+
coordinatorSession,
|
|
21915
22010
|
title
|
|
21916
22011
|
});
|
|
21917
22012
|
return { text: renderGroupCreated(group) };
|
|
@@ -21921,12 +22016,13 @@ async function handleGroupList(context, chatKey, filter) {
|
|
|
21921
22016
|
if (!session3) {
|
|
21922
22017
|
return { text: t().orchestration.noCurrentSession };
|
|
21923
22018
|
}
|
|
22019
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
21924
22020
|
const orchestration3 = getOrchestration(context);
|
|
21925
22021
|
if (!orchestration3) {
|
|
21926
22022
|
return { text: renderOrchestrationUnavailable() };
|
|
21927
22023
|
}
|
|
21928
22024
|
const groups = await orchestration3.listGroupSummaries({
|
|
21929
|
-
coordinatorSession
|
|
22025
|
+
coordinatorSession,
|
|
21930
22026
|
...filter ?? {}
|
|
21931
22027
|
});
|
|
21932
22028
|
return { text: renderGroupList(groups) };
|
|
@@ -21936,13 +22032,14 @@ async function handleGroupGet(context, chatKey, groupId) {
|
|
|
21936
22032
|
if (!session3) {
|
|
21937
22033
|
return { text: t().orchestration.noCurrentSession };
|
|
21938
22034
|
}
|
|
22035
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
21939
22036
|
const orchestration3 = getOrchestration(context);
|
|
21940
22037
|
if (!orchestration3) {
|
|
21941
22038
|
return { text: renderOrchestrationUnavailable() };
|
|
21942
22039
|
}
|
|
21943
22040
|
const group = await orchestration3.getGroupSummary({
|
|
21944
22041
|
groupId,
|
|
21945
|
-
coordinatorSession
|
|
22042
|
+
coordinatorSession
|
|
21946
22043
|
});
|
|
21947
22044
|
if (!group) {
|
|
21948
22045
|
return { text: t().orchestration.groupNotFound };
|
|
@@ -21954,20 +22051,21 @@ async function handleGroupCancel(context, chatKey, groupId) {
|
|
|
21954
22051
|
if (!session3) {
|
|
21955
22052
|
return { text: t().orchestration.noCurrentSession };
|
|
21956
22053
|
}
|
|
22054
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
21957
22055
|
const orchestration3 = getOrchestration(context);
|
|
21958
22056
|
if (!orchestration3) {
|
|
21959
22057
|
return { text: renderOrchestrationUnavailable() };
|
|
21960
22058
|
}
|
|
21961
22059
|
const group = await orchestration3.getGroupSummary({
|
|
21962
22060
|
groupId,
|
|
21963
|
-
coordinatorSession
|
|
22061
|
+
coordinatorSession
|
|
21964
22062
|
});
|
|
21965
22063
|
if (!group) {
|
|
21966
22064
|
return { text: t().orchestration.groupNotFound };
|
|
21967
22065
|
}
|
|
21968
22066
|
const cancelled = await orchestration3.cancelGroup({
|
|
21969
22067
|
groupId,
|
|
21970
|
-
coordinatorSession
|
|
22068
|
+
coordinatorSession
|
|
21971
22069
|
});
|
|
21972
22070
|
return { text: renderGroupCancelSuccess(cancelled) };
|
|
21973
22071
|
}
|
|
@@ -21976,13 +22074,14 @@ async function handleGroupDelegate(context, chatKey, groupId, targetAgent, task,
|
|
|
21976
22074
|
if (!session3) {
|
|
21977
22075
|
return { text: t().orchestration.noCurrentSession };
|
|
21978
22076
|
}
|
|
22077
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
21979
22078
|
const orchestration3 = getOrchestration(context);
|
|
21980
22079
|
if (!orchestration3) {
|
|
21981
22080
|
return { text: renderOrchestrationUnavailable() };
|
|
21982
22081
|
}
|
|
21983
22082
|
const group = await orchestration3.getGroupSummary({
|
|
21984
22083
|
groupId,
|
|
21985
|
-
coordinatorSession
|
|
22084
|
+
coordinatorSession
|
|
21986
22085
|
});
|
|
21987
22086
|
if (!group) {
|
|
21988
22087
|
return { text: t().orchestration.groupNotFound };
|
|
@@ -21994,12 +22093,13 @@ async function handleTaskList(context, chatKey, filter) {
|
|
|
21994
22093
|
if (!session3) {
|
|
21995
22094
|
return { text: t().orchestration.noCurrentSession };
|
|
21996
22095
|
}
|
|
22096
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
21997
22097
|
const orchestration3 = getOrchestration(context);
|
|
21998
22098
|
if (!orchestration3) {
|
|
21999
22099
|
return { text: renderOrchestrationUnavailable() };
|
|
22000
22100
|
}
|
|
22001
22101
|
const tasks = await orchestration3.listTasks({
|
|
22002
|
-
coordinatorSession
|
|
22102
|
+
coordinatorSession,
|
|
22003
22103
|
...filter ?? {}
|
|
22004
22104
|
});
|
|
22005
22105
|
return { text: renderTaskList2(tasks) };
|
|
@@ -22009,12 +22109,13 @@ async function handleTaskGet(context, chatKey, taskId) {
|
|
|
22009
22109
|
if (!session3) {
|
|
22010
22110
|
return { text: t().orchestration.noCurrentSession };
|
|
22011
22111
|
}
|
|
22112
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
22012
22113
|
const orchestration3 = getOrchestration(context);
|
|
22013
22114
|
if (!orchestration3) {
|
|
22014
22115
|
return { text: renderOrchestrationUnavailable() };
|
|
22015
22116
|
}
|
|
22016
22117
|
const task = await orchestration3.getTask(taskId);
|
|
22017
|
-
if (!task || task.coordinatorSession
|
|
22118
|
+
if (!task || !sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
|
|
22018
22119
|
return { text: t().orchestration.taskNotFound };
|
|
22019
22120
|
}
|
|
22020
22121
|
return { text: renderTaskSummary2(task) };
|
|
@@ -22024,12 +22125,13 @@ async function handleTaskApprove(context, chatKey, taskId) {
|
|
|
22024
22125
|
if (!session3) {
|
|
22025
22126
|
return { text: t().orchestration.noCurrentSession };
|
|
22026
22127
|
}
|
|
22128
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
22027
22129
|
const orchestration3 = getOrchestration(context);
|
|
22028
22130
|
if (!orchestration3) {
|
|
22029
22131
|
return { text: renderOrchestrationUnavailable() };
|
|
22030
22132
|
}
|
|
22031
22133
|
const task = await orchestration3.getTask(taskId);
|
|
22032
|
-
if (!task || task.coordinatorSession
|
|
22134
|
+
if (!task || !sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
|
|
22033
22135
|
return { text: t().orchestration.taskNotFound };
|
|
22034
22136
|
}
|
|
22035
22137
|
if (task.status !== "needs_confirmation") {
|
|
@@ -22037,7 +22139,7 @@ async function handleTaskApprove(context, chatKey, taskId) {
|
|
|
22037
22139
|
}
|
|
22038
22140
|
const approved = await orchestration3.approveTask({
|
|
22039
22141
|
taskId,
|
|
22040
|
-
coordinatorSession
|
|
22142
|
+
coordinatorSession
|
|
22041
22143
|
});
|
|
22042
22144
|
return { text: renderTaskApprovalSuccess2(approved) };
|
|
22043
22145
|
}
|
|
@@ -22046,12 +22148,13 @@ async function handleTaskReject(context, chatKey, taskId) {
|
|
|
22046
22148
|
if (!session3) {
|
|
22047
22149
|
return { text: t().orchestration.noCurrentSession };
|
|
22048
22150
|
}
|
|
22151
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
22049
22152
|
const orchestration3 = getOrchestration(context);
|
|
22050
22153
|
if (!orchestration3) {
|
|
22051
22154
|
return { text: renderOrchestrationUnavailable() };
|
|
22052
22155
|
}
|
|
22053
22156
|
const task = await orchestration3.getTask(taskId);
|
|
22054
|
-
if (!task || task.coordinatorSession
|
|
22157
|
+
if (!task || !sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
|
|
22055
22158
|
return { text: t().orchestration.taskNotFound };
|
|
22056
22159
|
}
|
|
22057
22160
|
if (task.status !== "needs_confirmation") {
|
|
@@ -22059,7 +22162,7 @@ async function handleTaskReject(context, chatKey, taskId) {
|
|
|
22059
22162
|
}
|
|
22060
22163
|
const rejected = await orchestration3.cancelTask({
|
|
22061
22164
|
taskId,
|
|
22062
|
-
coordinatorSession
|
|
22165
|
+
coordinatorSession
|
|
22063
22166
|
});
|
|
22064
22167
|
return { text: renderTaskRejectSuccess(rejected) };
|
|
22065
22168
|
}
|
|
@@ -22068,17 +22171,18 @@ async function handleTaskCancel(context, chatKey, taskId) {
|
|
|
22068
22171
|
if (!session3) {
|
|
22069
22172
|
return { text: t().orchestration.noCurrentSession };
|
|
22070
22173
|
}
|
|
22174
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
22071
22175
|
const orchestration3 = getOrchestration(context);
|
|
22072
22176
|
if (!orchestration3) {
|
|
22073
22177
|
return { text: renderOrchestrationUnavailable() };
|
|
22074
22178
|
}
|
|
22075
22179
|
const task = await orchestration3.getTask(taskId);
|
|
22076
|
-
if (!task || task.coordinatorSession
|
|
22180
|
+
if (!task || !sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
|
|
22077
22181
|
return { text: t().orchestration.taskNotFound };
|
|
22078
22182
|
}
|
|
22079
22183
|
const cancelled = await orchestration3.requestTaskCancellation({
|
|
22080
22184
|
taskId,
|
|
22081
|
-
coordinatorSession
|
|
22185
|
+
coordinatorSession
|
|
22082
22186
|
});
|
|
22083
22187
|
return { text: renderTaskCancelSuccess(cancelled) };
|
|
22084
22188
|
}
|
|
@@ -22087,11 +22191,12 @@ async function handleTasksClean(context, chatKey) {
|
|
|
22087
22191
|
if (!session3) {
|
|
22088
22192
|
return { text: t().orchestration.noCurrentSession };
|
|
22089
22193
|
}
|
|
22194
|
+
const coordinatorSession = stableCoordinatorSession(session3.transportSession);
|
|
22090
22195
|
const orchestration3 = getOrchestration(context);
|
|
22091
22196
|
if (!orchestration3) {
|
|
22092
22197
|
return { text: renderOrchestrationUnavailable() };
|
|
22093
22198
|
}
|
|
22094
|
-
const result = await orchestration3.cleanTasks(
|
|
22199
|
+
const result = await orchestration3.cleanTasks(coordinatorSession);
|
|
22095
22200
|
return { text: renderTasksCleanResult(result.removedTasks, result.removedBindings) };
|
|
22096
22201
|
}
|
|
22097
22202
|
async function getCurrentSession(context, chatKey) {
|
|
@@ -23657,23 +23762,43 @@ var init_translate_acpx_note = __esm(() => {
|
|
|
23657
23762
|
|
|
23658
23763
|
// src/commands/handlers/session-reset-handler.ts
|
|
23659
23764
|
async function handleSessionResetCommand(context, ops, chatKey) {
|
|
23660
|
-
const
|
|
23661
|
-
if (!
|
|
23765
|
+
const previous = await context.sessions.getCurrentSession(chatKey);
|
|
23766
|
+
if (!previous) {
|
|
23662
23767
|
return { text: t().misc.sessionResetNoCurrentSession };
|
|
23663
23768
|
}
|
|
23664
|
-
const
|
|
23769
|
+
const wasNative = previous.source === "agent-side";
|
|
23770
|
+
const resetSession = ops.resolveSession(previous.alias, previous.agent, previous.workspace, buildResetTransportSessionName(previous, ops.now()));
|
|
23665
23771
|
const releaseTransportReservation = await ops.reserveTransportSession(resetSession.transportSession);
|
|
23666
23772
|
try {
|
|
23667
23773
|
try {
|
|
23668
23774
|
await ops.ensureTransportSession(resetSession);
|
|
23669
23775
|
const exists = await ops.checkTransportSession(resetSession);
|
|
23670
23776
|
if (!exists) {
|
|
23671
|
-
return { text: t().misc.sessionResetFailed(
|
|
23777
|
+
return { text: t().misc.sessionResetFailed(previous.alias) };
|
|
23672
23778
|
}
|
|
23673
23779
|
} catch (error2) {
|
|
23674
23780
|
return renderTransportError(resetSession, error2);
|
|
23675
23781
|
}
|
|
23676
|
-
|
|
23782
|
+
let freshAgentSessionId;
|
|
23783
|
+
if (wasNative) {
|
|
23784
|
+
try {
|
|
23785
|
+
freshAgentSessionId = await context.transport.getAgentSessionId?.(resetSession);
|
|
23786
|
+
} catch (error2) {
|
|
23787
|
+
await context.logger.info("session.reset.native_id_unavailable", "failed to read fresh agent session id; falling back to xacpx session", { alias: resetSession.alias, error: error2 instanceof Error ? error2.message : String(error2) });
|
|
23788
|
+
}
|
|
23789
|
+
}
|
|
23790
|
+
if (wasNative && freshAgentSessionId) {
|
|
23791
|
+
await context.sessions.attachNativeSession({
|
|
23792
|
+
alias: resetSession.alias,
|
|
23793
|
+
agent: resetSession.agent,
|
|
23794
|
+
workspace: resetSession.workspace,
|
|
23795
|
+
transportSession: resetSession.transportSession,
|
|
23796
|
+
agentSessionId: freshAgentSessionId,
|
|
23797
|
+
updatedAt: new Date(ops.now()).toISOString()
|
|
23798
|
+
});
|
|
23799
|
+
} else {
|
|
23800
|
+
await context.sessions.attachSession(resetSession.alias, resetSession.agent, resetSession.workspace, resetSession.transportSession);
|
|
23801
|
+
}
|
|
23677
23802
|
await ops.refreshSessionTransportAgentCommand(resetSession.alias);
|
|
23678
23803
|
await context.sessions.useSession(chatKey, resetSession.alias);
|
|
23679
23804
|
await context.logger.info("session.reset", "reset current logical session", {
|
|
@@ -23681,8 +23806,19 @@ async function handleSessionResetCommand(context, ops, chatKey) {
|
|
|
23681
23806
|
agent: resetSession.agent,
|
|
23682
23807
|
workspace: resetSession.workspace,
|
|
23683
23808
|
transportSession: resetSession.transportSession,
|
|
23684
|
-
chatKey
|
|
23809
|
+
chatKey,
|
|
23810
|
+
native: wasNative && Boolean(freshAgentSessionId)
|
|
23685
23811
|
});
|
|
23812
|
+
if (wasNative && context.transport.removeSession && context.sessions.countAliasesSharingTransport(previous.transportSession) === 0) {
|
|
23813
|
+
try {
|
|
23814
|
+
await context.transport.removeSession(previous);
|
|
23815
|
+
} catch (error2) {
|
|
23816
|
+
await context.logger.info("session.reset.close_previous_failed", "failed to close previous native session after reset", {
|
|
23817
|
+
transportSession: previous.transportSession,
|
|
23818
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
23819
|
+
});
|
|
23820
|
+
}
|
|
23821
|
+
}
|
|
23686
23822
|
} finally {
|
|
23687
23823
|
await releaseTransportReservation();
|
|
23688
23824
|
}
|
|
@@ -24121,7 +24257,7 @@ class CommandRouter {
|
|
|
24121
24257
|
return await this.measureTransportCall("has_session", session3, () => this.transport.hasSession(session3));
|
|
24122
24258
|
}
|
|
24123
24259
|
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
24124
|
-
session3.mcpCoordinatorSession ??= session3.transportSession;
|
|
24260
|
+
session3.mcpCoordinatorSession ??= stableCoordinatorSession(session3.transportSession);
|
|
24125
24261
|
let done = false;
|
|
24126
24262
|
let abortRequested = false;
|
|
24127
24263
|
let cancelOnAbort;
|
|
@@ -24351,7 +24487,7 @@ class ConsoleAgent {
|
|
|
24351
24487
|
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent, request.onThought, request.perfSpan);
|
|
24352
24488
|
}
|
|
24353
24489
|
isKnownCommand(text) {
|
|
24354
|
-
return
|
|
24490
|
+
return isKnownXacpxCommandText(text);
|
|
24355
24491
|
}
|
|
24356
24492
|
async clearSession(conversationId) {
|
|
24357
24493
|
await this.router.clearSession?.(conversationId);
|
|
@@ -24372,7 +24508,7 @@ var init_console_agent = __esm(() => {
|
|
|
24372
24508
|
|
|
24373
24509
|
// src/orchestration/orchestration-server.ts
|
|
24374
24510
|
import { rm as rm8 } from "node:fs/promises";
|
|
24375
|
-
import {
|
|
24511
|
+
import { createServer } from "node:net";
|
|
24376
24512
|
|
|
24377
24513
|
class OrchestrationServer {
|
|
24378
24514
|
endpoint;
|
|
@@ -24600,7 +24736,7 @@ class OrchestrationServer {
|
|
|
24600
24736
|
if (!task) {
|
|
24601
24737
|
return null;
|
|
24602
24738
|
}
|
|
24603
|
-
if (task.coordinatorSession
|
|
24739
|
+
if (!sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
|
|
24604
24740
|
return null;
|
|
24605
24741
|
}
|
|
24606
24742
|
return task;
|
|
@@ -24865,29 +25001,6 @@ function requireTaskQuestions(params, key) {
|
|
|
24865
25001
|
};
|
|
24866
25002
|
});
|
|
24867
25003
|
}
|
|
24868
|
-
async function canConnectToEndpoint(path14) {
|
|
24869
|
-
return await new Promise((resolve3) => {
|
|
24870
|
-
const socket = createConnection2(path14);
|
|
24871
|
-
let settled = false;
|
|
24872
|
-
const finish = (result) => {
|
|
24873
|
-
if (settled) {
|
|
24874
|
-
return;
|
|
24875
|
-
}
|
|
24876
|
-
settled = true;
|
|
24877
|
-
socket.destroy();
|
|
24878
|
-
resolve3(result);
|
|
24879
|
-
};
|
|
24880
|
-
socket.once("connect", () => finish(true));
|
|
24881
|
-
socket.once("error", (error2) => {
|
|
24882
|
-
const code = error2.code;
|
|
24883
|
-
if (code === "ENOENT" || code === "ECONNREFUSED") {
|
|
24884
|
-
finish(false);
|
|
24885
|
-
return;
|
|
24886
|
-
}
|
|
24887
|
-
finish(true);
|
|
24888
|
-
});
|
|
24889
|
-
});
|
|
24890
|
-
}
|
|
24891
25004
|
async function listen(server, path14) {
|
|
24892
25005
|
await new Promise((resolve3, reject) => {
|
|
24893
25006
|
const onError = (error2) => {
|
|
@@ -24910,6 +25023,7 @@ var OrchestrationInvalidRequestError, ORCHESTRATION_RPC_METHODS;
|
|
|
24910
25023
|
var init_orchestration_server = __esm(() => {
|
|
24911
25024
|
init_orchestration_ipc();
|
|
24912
25025
|
init_task_watch_timeouts();
|
|
25026
|
+
init_endpoint_probe();
|
|
24913
25027
|
OrchestrationInvalidRequestError = class OrchestrationInvalidRequestError extends Error {
|
|
24914
25028
|
};
|
|
24915
25029
|
ORCHESTRATION_RPC_METHODS = new Set([
|
|
@@ -25110,7 +25224,7 @@ class OrchestrationService {
|
|
|
25110
25224
|
async getGroupSummary(input) {
|
|
25111
25225
|
const state = await this.deps.loadState();
|
|
25112
25226
|
const group = this.ensureGroups(state)[input.groupId];
|
|
25113
|
-
if (!group || group.coordinatorSession
|
|
25227
|
+
if (!group || !sameCoordinatorSession(group.coordinatorSession, input.coordinatorSession)) {
|
|
25114
25228
|
return null;
|
|
25115
25229
|
}
|
|
25116
25230
|
return this.buildGroupSummary(group, Object.values(state.orchestration.tasks).filter((task) => task.groupId === input.groupId));
|
|
@@ -25122,7 +25236,7 @@ class OrchestrationService {
|
|
|
25122
25236
|
const now = this.deps.now().getTime();
|
|
25123
25237
|
const sortField = input.sort ?? "updatedAt";
|
|
25124
25238
|
const order = input.order ?? "desc";
|
|
25125
|
-
return Object.values(this.ensureGroups(state)).filter((group) => group.coordinatorSession
|
|
25239
|
+
return Object.values(this.ensureGroups(state)).filter((group) => sameCoordinatorSession(group.coordinatorSession, input.coordinatorSession)).map((group) => ({
|
|
25126
25240
|
group,
|
|
25127
25241
|
summary: this.buildGroupSummary(group, tasks.filter((task) => task.groupId === group.groupId))
|
|
25128
25242
|
})).filter(({ summary }) => {
|
|
@@ -25612,7 +25726,7 @@ class OrchestrationService {
|
|
|
25612
25726
|
const workerSession = task.workerSession;
|
|
25613
25727
|
const taskStillOwnsWorkerSession = current?.workerSession === workerSession;
|
|
25614
25728
|
const currentBinding = state.orchestration.workerBindings[workerSession];
|
|
25615
|
-
const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && currentBinding.coordinatorSession
|
|
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;
|
|
25616
25730
|
const otherActiveOwner = Object.values(state.orchestration.tasks).some((candidate) => candidate.taskId !== task.taskId && candidate.workerSession === workerSession && (!this.isTerminalStatus(candidate.status) || candidate.reviewPending !== undefined));
|
|
25617
25731
|
const restoreOrDeleteBinding = () => {
|
|
25618
25732
|
if (!bindingStillBelongsToThisStartup || otherActiveOwner) {
|
|
@@ -25671,7 +25785,7 @@ class OrchestrationService {
|
|
|
25671
25785
|
current.updatedAt = now;
|
|
25672
25786
|
this.bumpGroupUpdated(state, current.groupId, now);
|
|
25673
25787
|
const currentBinding = state.orchestration.workerBindings[workerSession];
|
|
25674
|
-
const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && currentBinding.coordinatorSession
|
|
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;
|
|
25675
25789
|
const otherActiveOwner = Object.values(state.orchestration.tasks).some((candidate) => candidate.taskId !== task.taskId && candidate.workerSession === workerSession && (!this.isTerminalStatus(candidate.status) || candidate.reviewPending !== undefined));
|
|
25676
25790
|
if (bindingStillBelongsToThisStartup && !otherActiveOwner) {
|
|
25677
25791
|
if (input.previousBinding) {
|
|
@@ -25690,7 +25804,7 @@ class OrchestrationService {
|
|
|
25690
25804
|
const state = await this.deps.loadState();
|
|
25691
25805
|
const workerSession = task.workerSession;
|
|
25692
25806
|
const currentBinding = state.orchestration.workerBindings[workerSession];
|
|
25693
|
-
const bindingStillBelongsToThisStartup = currentBinding?.sourceHandle === workerSession && currentBinding.coordinatorSession
|
|
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;
|
|
25694
25808
|
if (!bindingStillBelongsToThisStartup) {
|
|
25695
25809
|
return false;
|
|
25696
25810
|
}
|
|
@@ -25863,7 +25977,7 @@ class OrchestrationService {
|
|
|
25863
25977
|
while (true) {
|
|
25864
25978
|
const state = await this.deps.loadState();
|
|
25865
25979
|
const task = state.orchestration.tasks[input.taskId];
|
|
25866
|
-
if (!task || task.coordinatorSession
|
|
25980
|
+
if (!task || !sameCoordinatorSession(task.coordinatorSession, input.coordinatorSession)) {
|
|
25867
25981
|
return { status: "not_found", task: null, events: [], nextAfterSeq: afterSeq };
|
|
25868
25982
|
}
|
|
25869
25983
|
const snapshot = { ...task };
|
|
@@ -25921,7 +26035,8 @@ class OrchestrationService {
|
|
|
25921
26035
|
return await this.mutate(async () => {
|
|
25922
26036
|
const state = await this.deps.loadState();
|
|
25923
26037
|
const now = this.deps.now().toISOString();
|
|
25924
|
-
const
|
|
26038
|
+
const routeKey = stableCoordinatorSession(input.coordinatorSession);
|
|
26039
|
+
const existing = this.ensureCoordinatorRoutes(state)[routeKey];
|
|
25925
26040
|
const sameChat = existing?.chatKey === input.chatKey;
|
|
25926
26041
|
const hasAccountId = input.accountId !== undefined;
|
|
25927
26042
|
const hasReplyContextToken = input.replyContextToken !== undefined;
|
|
@@ -25935,14 +26050,14 @@ class OrchestrationService {
|
|
|
25935
26050
|
replyContextToken: existing.replyContextToken
|
|
25936
26051
|
} : undefined;
|
|
25937
26052
|
const route = {
|
|
25938
|
-
coordinatorSession:
|
|
26053
|
+
coordinatorSession: routeKey,
|
|
25939
26054
|
chatKey: input.chatKey,
|
|
25940
26055
|
...input.sessionAlias ? { sessionAlias: input.sessionAlias } : {},
|
|
25941
26056
|
...replyRoute ? replyRoute : {},
|
|
25942
26057
|
...buildCoordinatorRouteChatMetadata(input, sameChat ? existing : undefined),
|
|
25943
26058
|
updatedAt: now
|
|
25944
26059
|
};
|
|
25945
|
-
this.ensureCoordinatorRoutes(state)[
|
|
26060
|
+
this.ensureCoordinatorRoutes(state)[routeKey] = route;
|
|
25946
26061
|
await this.deps.saveState(state);
|
|
25947
26062
|
return { ...route };
|
|
25948
26063
|
});
|
|
@@ -26186,7 +26301,7 @@ class OrchestrationService {
|
|
|
26186
26301
|
return task;
|
|
26187
26302
|
});
|
|
26188
26303
|
const now = this.deps.now().toISOString();
|
|
26189
|
-
const route = this.snapshotCoordinatorDeliveryRoute(this.ensureCoordinatorRoutes(state)[input.coordinatorSession]);
|
|
26304
|
+
const route = this.snapshotCoordinatorDeliveryRoute(this.ensureCoordinatorRoutes(state)[stableCoordinatorSession(input.coordinatorSession)]);
|
|
26190
26305
|
if (coordinatorState.activePackageId) {
|
|
26191
26306
|
const activePackage = this.ensureHumanQuestionPackages(state)[coordinatorState.activePackageId];
|
|
26192
26307
|
if (!activePackage) {
|
|
@@ -26288,7 +26403,7 @@ class OrchestrationService {
|
|
|
26288
26403
|
if (!packageRecord) {
|
|
26289
26404
|
throw new Error(`package "${input.packageId}" does not exist`);
|
|
26290
26405
|
}
|
|
26291
|
-
if (packageRecord.coordinatorSession
|
|
26406
|
+
if (!sameCoordinatorSession(packageRecord.coordinatorSession, input.coordinatorSession)) {
|
|
26292
26407
|
throw new Error(`package "${input.packageId}" belongs to coordinator "${packageRecord.coordinatorSession}", not "${input.coordinatorSession}"`);
|
|
26293
26408
|
}
|
|
26294
26409
|
if (packageRecord.status !== "active") {
|
|
@@ -26303,7 +26418,7 @@ class OrchestrationService {
|
|
|
26303
26418
|
}
|
|
26304
26419
|
let route = this.resolveFrozenPackageMessageRoute(message);
|
|
26305
26420
|
if (!route) {
|
|
26306
|
-
route = this.snapshotCoordinatorDeliveryRoute(this.ensureCoordinatorRoutes(state)[input.coordinatorSession]) ?? null;
|
|
26421
|
+
route = this.snapshotCoordinatorDeliveryRoute(this.ensureCoordinatorRoutes(state)[stableCoordinatorSession(input.coordinatorSession)]) ?? null;
|
|
26307
26422
|
if (route) {
|
|
26308
26423
|
Object.assign(message, this.serializeFrozenDeliveryRoute(route));
|
|
26309
26424
|
}
|
|
@@ -26372,7 +26487,7 @@ class OrchestrationService {
|
|
|
26372
26487
|
if (this.isExternalCoordinatorSession(state, coordinatorSession)) {
|
|
26373
26488
|
return null;
|
|
26374
26489
|
}
|
|
26375
|
-
const coordinatorState = state.orchestration.coordinatorQuestionState[coordinatorSession];
|
|
26490
|
+
const coordinatorState = state.orchestration.coordinatorQuestionState[stableCoordinatorSession(coordinatorSession)];
|
|
26376
26491
|
const activePackageId = coordinatorState?.activePackageId;
|
|
26377
26492
|
if (!activePackageId) {
|
|
26378
26493
|
return null;
|
|
@@ -26512,7 +26627,7 @@ class OrchestrationService {
|
|
|
26512
26627
|
const bindings = state.orchestration.workerBindings;
|
|
26513
26628
|
const terminalTaskIds = [];
|
|
26514
26629
|
for (const [taskId, task] of Object.entries(tasks)) {
|
|
26515
|
-
if (task.coordinatorSession
|
|
26630
|
+
if (sameCoordinatorSession(task.coordinatorSession, coordinatorSession) && this.isTerminalStatus(task.status) && task.reviewPending === undefined) {
|
|
26516
26631
|
terminalTaskIds.push(taskId);
|
|
26517
26632
|
}
|
|
26518
26633
|
}
|
|
@@ -26522,7 +26637,7 @@ class OrchestrationService {
|
|
|
26522
26637
|
const remainingWorkerSessions = new Set(Object.values(tasks).map((task) => task.workerSession).filter(Boolean));
|
|
26523
26638
|
let removedBindings = 0;
|
|
26524
26639
|
for (const [workerSession, binding] of Object.entries(bindings)) {
|
|
26525
|
-
if (binding.coordinatorSession
|
|
26640
|
+
if (!sameCoordinatorSession(binding.coordinatorSession, coordinatorSession)) {
|
|
26526
26641
|
continue;
|
|
26527
26642
|
}
|
|
26528
26643
|
if (!remainingWorkerSessions.has(workerSession)) {
|
|
@@ -26542,16 +26657,17 @@ class OrchestrationService {
|
|
|
26542
26657
|
}
|
|
26543
26658
|
async listSessionBlockingTasks(transportSession) {
|
|
26544
26659
|
const state = await this.deps.loadState();
|
|
26545
|
-
return Object.values(state.orchestration.tasks).filter((task) => (!this.isTerminalStatus(task.status) || task.reviewPending !== undefined) && (task.coordinatorSession
|
|
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 }));
|
|
26546
26661
|
}
|
|
26547
26662
|
async purgeSessionReferences(transportSession) {
|
|
26548
26663
|
return await this.mutate(async () => {
|
|
26549
26664
|
const state = await this.deps.loadState();
|
|
26665
|
+
const sessionIdentity = stableCoordinatorSession(transportSession);
|
|
26550
26666
|
const tasks = state.orchestration.tasks;
|
|
26551
26667
|
const bindings = state.orchestration.workerBindings;
|
|
26552
26668
|
const removedTaskIds = [];
|
|
26553
26669
|
for (const [taskId, task] of Object.entries(tasks)) {
|
|
26554
|
-
if (this.isTerminalStatus(task.status) && task.reviewPending === undefined && (task.coordinatorSession
|
|
26670
|
+
if (this.isTerminalStatus(task.status) && task.reviewPending === undefined && (sameCoordinatorSession(task.coordinatorSession, transportSession) || task.workerSession !== undefined && sameCoordinatorSession(task.workerSession, transportSession))) {
|
|
26555
26671
|
removedTaskIds.push(taskId);
|
|
26556
26672
|
}
|
|
26557
26673
|
}
|
|
@@ -26561,14 +26677,14 @@ class OrchestrationService {
|
|
|
26561
26677
|
const remainingWorkerSessions = new Set(Object.values(tasks).map((task) => task.workerSession).filter(Boolean));
|
|
26562
26678
|
let removedBindings = 0;
|
|
26563
26679
|
for (const [workerSession, binding] of Object.entries(bindings)) {
|
|
26564
|
-
const shouldPurgeBinding = workerSession
|
|
26680
|
+
const shouldPurgeBinding = sameCoordinatorSession(workerSession, transportSession) || sameCoordinatorSession(binding.coordinatorSession, transportSession);
|
|
26565
26681
|
if (shouldPurgeBinding && !remainingWorkerSessions.has(workerSession)) {
|
|
26566
26682
|
delete bindings[workerSession];
|
|
26567
26683
|
removedBindings += 1;
|
|
26568
26684
|
}
|
|
26569
26685
|
}
|
|
26570
|
-
const removedEmptyGroups = this.removeEmptyGroupsForCoordinator(state,
|
|
26571
|
-
const removedCoordinatorMetadata = this.removeCoordinatorMetadataIfUnused(state,
|
|
26686
|
+
const removedEmptyGroups = this.removeEmptyGroupsForCoordinator(state, sessionIdentity);
|
|
26687
|
+
const removedCoordinatorMetadata = this.removeCoordinatorMetadataIfUnused(state, sessionIdentity);
|
|
26572
26688
|
if (removedTaskIds.length > 0 || removedBindings > 0 || removedEmptyGroups || removedCoordinatorMetadata) {
|
|
26573
26689
|
await this.deps.saveState(state);
|
|
26574
26690
|
}
|
|
@@ -26578,106 +26694,28 @@ class OrchestrationService {
|
|
|
26578
26694
|
};
|
|
26579
26695
|
});
|
|
26580
26696
|
}
|
|
26581
|
-
async purgeExpiredResetCoordinators(input) {
|
|
26582
|
-
try {
|
|
26583
|
-
if (!Number.isFinite(input.cutoffDays) || input.cutoffDays < 0) {
|
|
26584
|
-
throw new Error(`cutoffDays must be a non-negative number, got ${String(input.cutoffDays)}`);
|
|
26585
|
-
}
|
|
26586
|
-
const result = await this.mutate(async () => {
|
|
26587
|
-
const state = await this.deps.loadState();
|
|
26588
|
-
const candidates = this.collectResetCoordinatorCandidates(state);
|
|
26589
|
-
const activeTransportSessions = new Set(Object.values(state.sessions).map((session3) => session3.transport_session));
|
|
26590
|
-
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
26591
|
-
const cutoffMs = input.cutoffDays * MS_PER_DAY;
|
|
26592
|
-
const nowMs = this.deps.now().getTime();
|
|
26593
|
-
let purgedCoordinators = 0;
|
|
26594
|
-
const removed = {
|
|
26595
|
-
tasks: 0,
|
|
26596
|
-
workerBindings: 0,
|
|
26597
|
-
groups: 0,
|
|
26598
|
-
coordinatorRoutes: 0,
|
|
26599
|
-
humanQuestionPackages: 0,
|
|
26600
|
-
coordinatorQuestionState: 0
|
|
26601
|
-
};
|
|
26602
|
-
for (const coordinatorSession of candidates) {
|
|
26603
|
-
if (activeTransportSessions.has(coordinatorSession)) {
|
|
26604
|
-
continue;
|
|
26605
|
-
}
|
|
26606
|
-
const activityAtMs = this.resolveResetCoordinatorActivityAtMs(state, coordinatorSession);
|
|
26607
|
-
if (activityAtMs === null) {
|
|
26608
|
-
continue;
|
|
26609
|
-
}
|
|
26610
|
-
if (nowMs - activityAtMs <= cutoffMs) {
|
|
26611
|
-
continue;
|
|
26612
|
-
}
|
|
26613
|
-
const delta = this.cascadeRemoveCoordinatorRecords(state, coordinatorSession);
|
|
26614
|
-
const changed = delta.tasks > 0 || delta.workerBindings > 0 || delta.groups > 0 || delta.coordinatorRoutes > 0 || delta.humanQuestionPackages > 0 || delta.coordinatorQuestionState > 0;
|
|
26615
|
-
if (!changed) {
|
|
26616
|
-
continue;
|
|
26617
|
-
}
|
|
26618
|
-
purgedCoordinators += 1;
|
|
26619
|
-
removed.tasks += delta.tasks;
|
|
26620
|
-
removed.workerBindings += delta.workerBindings;
|
|
26621
|
-
removed.groups += delta.groups;
|
|
26622
|
-
removed.coordinatorRoutes += delta.coordinatorRoutes;
|
|
26623
|
-
removed.humanQuestionPackages += delta.humanQuestionPackages;
|
|
26624
|
-
removed.coordinatorQuestionState += delta.coordinatorQuestionState;
|
|
26625
|
-
}
|
|
26626
|
-
const removedAny = removed.tasks > 0 || removed.workerBindings > 0 || removed.groups > 0 || removed.coordinatorRoutes > 0 || removed.humanQuestionPackages > 0 || removed.coordinatorQuestionState > 0;
|
|
26627
|
-
if (removedAny) {
|
|
26628
|
-
await this.deps.saveState(state);
|
|
26629
|
-
}
|
|
26630
|
-
return {
|
|
26631
|
-
candidates: candidates.length,
|
|
26632
|
-
purgedCoordinators,
|
|
26633
|
-
removed
|
|
26634
|
-
};
|
|
26635
|
-
});
|
|
26636
|
-
if (this.deps.logger) {
|
|
26637
|
-
this.deps.logger.info("orchestration.reset_gc.completed", "reset coordinator gc completed", {
|
|
26638
|
-
trigger: input.trigger,
|
|
26639
|
-
cutoffDays: input.cutoffDays,
|
|
26640
|
-
candidates: result.candidates,
|
|
26641
|
-
purgedCoordinators: result.purgedCoordinators,
|
|
26642
|
-
deletedCounts: result.removed
|
|
26643
|
-
});
|
|
26644
|
-
}
|
|
26645
|
-
return result;
|
|
26646
|
-
} catch (error2) {
|
|
26647
|
-
const logger2 = this.deps.logger;
|
|
26648
|
-
if (logger2) {
|
|
26649
|
-
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
26650
|
-
logger2.error("orchestration.reset_gc.failed", "reset coordinator gc failed", {
|
|
26651
|
-
trigger: input.trigger,
|
|
26652
|
-
cutoffDays: input.cutoffDays,
|
|
26653
|
-
message
|
|
26654
|
-
});
|
|
26655
|
-
}
|
|
26656
|
-
throw error2;
|
|
26657
|
-
}
|
|
26658
|
-
}
|
|
26659
26697
|
async listPendingCoordinatorResults(coordinatorSession) {
|
|
26660
26698
|
const state = await this.deps.loadState();
|
|
26661
26699
|
if (this.isExternalCoordinatorSession(state, coordinatorSession)) {
|
|
26662
26700
|
return [];
|
|
26663
26701
|
}
|
|
26664
|
-
return Object.values(state.orchestration.tasks).filter((task) => task.coordinatorSession
|
|
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 }));
|
|
26665
26703
|
}
|
|
26666
26704
|
async listPendingCoordinatorBlockers(coordinatorSession) {
|
|
26667
26705
|
const state = await this.deps.loadState();
|
|
26668
26706
|
if (this.isExternalCoordinatorSession(state, coordinatorSession)) {
|
|
26669
26707
|
return [];
|
|
26670
26708
|
}
|
|
26671
|
-
const coordinatorState = state.orchestration.coordinatorQuestionState[coordinatorSession];
|
|
26709
|
+
const coordinatorState = state.orchestration.coordinatorQuestionState[stableCoordinatorSession(coordinatorSession)];
|
|
26672
26710
|
const hiddenQueuedQuestionKeys = coordinatorState?.activePackageId ? new Set((coordinatorState.queuedQuestions ?? []).map((entry) => `${entry.taskId}:${entry.questionId}`)) : null;
|
|
26673
|
-
return Object.values(state.orchestration.tasks).filter((task) => task.coordinatorSession
|
|
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 }));
|
|
26674
26712
|
}
|
|
26675
26713
|
async listContestedCoordinatorResults(coordinatorSession) {
|
|
26676
26714
|
const state = await this.deps.loadState();
|
|
26677
26715
|
if (this.isExternalCoordinatorSession(state, coordinatorSession)) {
|
|
26678
26716
|
return [];
|
|
26679
26717
|
}
|
|
26680
|
-
return Object.values(state.orchestration.tasks).filter((task) => task.coordinatorSession
|
|
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 }));
|
|
26681
26719
|
}
|
|
26682
26720
|
async listPendingCoordinatorGroups(coordinatorSession) {
|
|
26683
26721
|
const state = await this.deps.loadState();
|
|
@@ -26686,7 +26724,7 @@ class OrchestrationService {
|
|
|
26686
26724
|
}
|
|
26687
26725
|
const groups = this.ensureGroups(state);
|
|
26688
26726
|
const tasks = Object.values(state.orchestration.tasks);
|
|
26689
|
-
return Object.values(groups).filter((group) => group.coordinatorSession
|
|
26727
|
+
return Object.values(groups).filter((group) => sameCoordinatorSession(group.coordinatorSession, coordinatorSession)).filter((group) => {
|
|
26690
26728
|
const groupTasks = tasks.filter((task) => task.groupId === group.groupId);
|
|
26691
26729
|
return this.canInjectGroupIntoCoordinator(state, group.groupId, groupTasks);
|
|
26692
26730
|
}).sort((left, right) => left.updatedAt.localeCompare(right.updatedAt)).map((group) => ({ ...group }));
|
|
@@ -26901,7 +26939,7 @@ class OrchestrationService {
|
|
|
26901
26939
|
if (input.sourceHandle !== undefined && task.sourceHandle !== input.sourceHandle) {
|
|
26902
26940
|
throw new Error(`task "${input.taskId}" belongs to source "${task.sourceHandle}", not "${input.sourceHandle}"`);
|
|
26903
26941
|
}
|
|
26904
|
-
if (input.coordinatorSession !== undefined && task.coordinatorSession
|
|
26942
|
+
if (input.coordinatorSession !== undefined && !sameCoordinatorSession(task.coordinatorSession, input.coordinatorSession)) {
|
|
26905
26943
|
throw new Error(`task "${input.taskId}" belongs to coordinator "${task.coordinatorSession}", not "${input.coordinatorSession}"`);
|
|
26906
26944
|
}
|
|
26907
26945
|
if (this.isTerminalStatus(task.status)) {
|
|
@@ -27328,11 +27366,11 @@ class OrchestrationService {
|
|
|
27328
27366
|
...binding.cwd ? { cwd: binding.cwd } : {}
|
|
27329
27367
|
};
|
|
27330
27368
|
}
|
|
27331
|
-
const coordinatorSession = Object.values(state.sessions).find((session3) => session3.transport_session
|
|
27369
|
+
const coordinatorSession = Object.values(state.sessions).find((session3) => sameCoordinatorSession(session3.transport_session, sourceHandle));
|
|
27332
27370
|
if (coordinatorSession) {
|
|
27333
27371
|
return {
|
|
27334
27372
|
sourceKind: "coordinator",
|
|
27335
|
-
coordinatorSession: sourceHandle,
|
|
27373
|
+
coordinatorSession: stableCoordinatorSession(sourceHandle),
|
|
27336
27374
|
workspace: coordinatorSession.workspace
|
|
27337
27375
|
};
|
|
27338
27376
|
}
|
|
@@ -27370,7 +27408,7 @@ class OrchestrationService {
|
|
|
27370
27408
|
if (role && policy.allowedAgentRequestRoles.length > 0 && !policy.allowedAgentRequestRoles.includes(role)) {
|
|
27371
27409
|
throw new Error(`role "${role}" is not allowed for agent-requested delegation`);
|
|
27372
27410
|
}
|
|
27373
|
-
const outstandingRequests = Object.values(state.orchestration.tasks).filter((task) => task.coordinatorSession
|
|
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"));
|
|
27374
27412
|
if (outstandingRequests.length >= policy.maxPendingAgentRequestsPerCoordinator) {
|
|
27375
27413
|
throw new Error("agent-requested delegation quota exceeded for this coordinator");
|
|
27376
27414
|
}
|
|
@@ -27474,13 +27512,13 @@ class OrchestrationService {
|
|
|
27474
27512
|
if (!filter) {
|
|
27475
27513
|
return true;
|
|
27476
27514
|
}
|
|
27477
|
-
return (filter.sourceHandle === undefined || task.sourceHandle === filter.sourceHandle) && (filter.coordinatorSession === undefined || task.coordinatorSession
|
|
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);
|
|
27478
27516
|
}
|
|
27479
27517
|
isTerminalStatus(status) {
|
|
27480
27518
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
27481
27519
|
}
|
|
27482
27520
|
assertCoordinatorOwnership(task, coordinatorSession) {
|
|
27483
|
-
if (task.coordinatorSession
|
|
27521
|
+
if (!sameCoordinatorSession(task.coordinatorSession, coordinatorSession)) {
|
|
27484
27522
|
throw new Error(`task "${task.taskId}" belongs to coordinator "${task.coordinatorSession}", not "${coordinatorSession}"`);
|
|
27485
27523
|
}
|
|
27486
27524
|
}
|
|
@@ -27493,7 +27531,7 @@ class OrchestrationService {
|
|
|
27493
27531
|
if (!group) {
|
|
27494
27532
|
throw new Error(`group "${groupId}" does not exist`);
|
|
27495
27533
|
}
|
|
27496
|
-
if (group.coordinatorSession
|
|
27534
|
+
if (!sameCoordinatorSession(group.coordinatorSession, coordinatorSession)) {
|
|
27497
27535
|
throw new Error(`group "${groupId}" belongs to coordinator "${group.coordinatorSession}", not "${coordinatorSession}"`);
|
|
27498
27536
|
}
|
|
27499
27537
|
}
|
|
@@ -27504,13 +27542,14 @@ class OrchestrationService {
|
|
|
27504
27542
|
return state.orchestration.humanQuestionPackages;
|
|
27505
27543
|
}
|
|
27506
27544
|
ensureCoordinatorQuestionState(state, coordinatorSession) {
|
|
27545
|
+
const key = stableCoordinatorSession(coordinatorSession);
|
|
27507
27546
|
if (!("coordinatorQuestionState" in state.orchestration) || !state.orchestration.coordinatorQuestionState) {
|
|
27508
27547
|
state.orchestration.coordinatorQuestionState = {};
|
|
27509
27548
|
}
|
|
27510
|
-
state.orchestration.coordinatorQuestionState[
|
|
27549
|
+
state.orchestration.coordinatorQuestionState[key] ??= {
|
|
27511
27550
|
queuedQuestions: []
|
|
27512
27551
|
};
|
|
27513
|
-
return state.orchestration.coordinatorQuestionState[
|
|
27552
|
+
return state.orchestration.coordinatorQuestionState[key];
|
|
27514
27553
|
}
|
|
27515
27554
|
ensureCoordinatorRoutes(state) {
|
|
27516
27555
|
if (!("coordinatorRoutes" in state.orchestration) || !state.orchestration.coordinatorRoutes) {
|
|
@@ -27691,7 +27730,7 @@ class OrchestrationService {
|
|
|
27691
27730
|
const referencedGroupIds = new Set(Object.values(state.orchestration.tasks).map((task) => task.groupId).filter((groupId) => typeof groupId === "string"));
|
|
27692
27731
|
let removedAny = false;
|
|
27693
27732
|
for (const [groupId, group] of Object.entries(groups)) {
|
|
27694
|
-
if (group.coordinatorSession
|
|
27733
|
+
if (!sameCoordinatorSession(group.coordinatorSession, coordinatorSession)) {
|
|
27695
27734
|
continue;
|
|
27696
27735
|
}
|
|
27697
27736
|
if (!referencedGroupIds.has(groupId)) {
|
|
@@ -27702,125 +27741,30 @@ class OrchestrationService {
|
|
|
27702
27741
|
return removedAny;
|
|
27703
27742
|
}
|
|
27704
27743
|
removeCoordinatorMetadataIfUnused(state, coordinatorSession) {
|
|
27705
|
-
const
|
|
27706
|
-
const
|
|
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));
|
|
27707
27747
|
if (hasCoordinatorTasks || hasCoordinatorBindings) {
|
|
27708
27748
|
return false;
|
|
27709
27749
|
}
|
|
27710
27750
|
let removedAny = false;
|
|
27711
27751
|
const packages = this.ensureHumanQuestionPackages(state);
|
|
27712
27752
|
for (const [packageId, packageRecord] of Object.entries(packages)) {
|
|
27713
|
-
if (packageRecord.coordinatorSession
|
|
27753
|
+
if (sameCoordinatorSession(packageRecord.coordinatorSession, coordinatorSession)) {
|
|
27714
27754
|
delete packages[packageId];
|
|
27715
27755
|
removedAny = true;
|
|
27716
27756
|
}
|
|
27717
27757
|
}
|
|
27718
|
-
if (state.orchestration.coordinatorQuestionState?.[
|
|
27719
|
-
delete state.orchestration.coordinatorQuestionState[
|
|
27758
|
+
if (state.orchestration.coordinatorQuestionState?.[key] !== undefined) {
|
|
27759
|
+
delete state.orchestration.coordinatorQuestionState[key];
|
|
27720
27760
|
removedAny = true;
|
|
27721
27761
|
}
|
|
27722
|
-
if (state.orchestration.coordinatorRoutes?.[
|
|
27723
|
-
delete state.orchestration.coordinatorRoutes[
|
|
27762
|
+
if (state.orchestration.coordinatorRoutes?.[key] !== undefined) {
|
|
27763
|
+
delete state.orchestration.coordinatorRoutes[key];
|
|
27724
27764
|
removedAny = true;
|
|
27725
27765
|
}
|
|
27726
27766
|
return removedAny;
|
|
27727
27767
|
}
|
|
27728
|
-
isResetCoordinatorSession(coordinatorSession) {
|
|
27729
|
-
return coordinatorSession.includes(":reset-");
|
|
27730
|
-
}
|
|
27731
|
-
collectResetCoordinatorCandidates(state) {
|
|
27732
|
-
const candidates = new Set;
|
|
27733
|
-
for (const coordinatorSession of Object.keys(state.orchestration.coordinatorRoutes ?? {})) {
|
|
27734
|
-
if (this.isResetCoordinatorSession(coordinatorSession)) {
|
|
27735
|
-
candidates.add(coordinatorSession);
|
|
27736
|
-
}
|
|
27737
|
-
}
|
|
27738
|
-
for (const task of Object.values(state.orchestration.tasks ?? {})) {
|
|
27739
|
-
if (this.isResetCoordinatorSession(task.coordinatorSession)) {
|
|
27740
|
-
candidates.add(task.coordinatorSession);
|
|
27741
|
-
}
|
|
27742
|
-
}
|
|
27743
|
-
return [...candidates];
|
|
27744
|
-
}
|
|
27745
|
-
parseDateMs(value) {
|
|
27746
|
-
if (!value) {
|
|
27747
|
-
return null;
|
|
27748
|
-
}
|
|
27749
|
-
const ms = new Date(value).getTime();
|
|
27750
|
-
return Number.isFinite(ms) ? ms : null;
|
|
27751
|
-
}
|
|
27752
|
-
resolveResetCoordinatorActivityAtMs(state, coordinatorSession) {
|
|
27753
|
-
const routeUpdatedAt = state.orchestration.coordinatorRoutes?.[coordinatorSession]?.updatedAt;
|
|
27754
|
-
const routeMs = this.parseDateMs(routeUpdatedAt);
|
|
27755
|
-
let tasksMs = null;
|
|
27756
|
-
for (const task of Object.values(state.orchestration.tasks ?? {})) {
|
|
27757
|
-
if (task.coordinatorSession !== coordinatorSession) {
|
|
27758
|
-
continue;
|
|
27759
|
-
}
|
|
27760
|
-
const candidate = this.parseDateMs(task.updatedAt);
|
|
27761
|
-
if (candidate === null) {
|
|
27762
|
-
continue;
|
|
27763
|
-
}
|
|
27764
|
-
tasksMs = tasksMs === null ? candidate : Math.max(tasksMs, candidate);
|
|
27765
|
-
}
|
|
27766
|
-
if (routeMs === null && tasksMs === null) {
|
|
27767
|
-
return null;
|
|
27768
|
-
}
|
|
27769
|
-
if (routeMs === null) {
|
|
27770
|
-
return tasksMs;
|
|
27771
|
-
}
|
|
27772
|
-
if (tasksMs === null) {
|
|
27773
|
-
return routeMs;
|
|
27774
|
-
}
|
|
27775
|
-
return Math.max(routeMs, tasksMs);
|
|
27776
|
-
}
|
|
27777
|
-
cascadeRemoveCoordinatorRecords(state, coordinatorSession) {
|
|
27778
|
-
const removed = {
|
|
27779
|
-
tasks: 0,
|
|
27780
|
-
workerBindings: 0,
|
|
27781
|
-
groups: 0,
|
|
27782
|
-
coordinatorRoutes: 0,
|
|
27783
|
-
humanQuestionPackages: 0,
|
|
27784
|
-
coordinatorQuestionState: 0
|
|
27785
|
-
};
|
|
27786
|
-
const tasks = state.orchestration.tasks;
|
|
27787
|
-
for (const [taskId, task] of Object.entries(tasks ?? {})) {
|
|
27788
|
-
if (task.coordinatorSession === coordinatorSession) {
|
|
27789
|
-
delete tasks[taskId];
|
|
27790
|
-
removed.tasks += 1;
|
|
27791
|
-
}
|
|
27792
|
-
}
|
|
27793
|
-
const workerBindings = state.orchestration.workerBindings;
|
|
27794
|
-
for (const [workerSession, binding] of Object.entries(workerBindings ?? {})) {
|
|
27795
|
-
if (binding.coordinatorSession === coordinatorSession) {
|
|
27796
|
-
delete workerBindings[workerSession];
|
|
27797
|
-
removed.workerBindings += 1;
|
|
27798
|
-
}
|
|
27799
|
-
}
|
|
27800
|
-
const groups = this.ensureGroups(state);
|
|
27801
|
-
for (const [groupId, group] of Object.entries(groups ?? {})) {
|
|
27802
|
-
if (group.coordinatorSession === coordinatorSession) {
|
|
27803
|
-
delete groups[groupId];
|
|
27804
|
-
removed.groups += 1;
|
|
27805
|
-
}
|
|
27806
|
-
}
|
|
27807
|
-
if (state.orchestration.coordinatorRoutes?.[coordinatorSession] !== undefined) {
|
|
27808
|
-
delete state.orchestration.coordinatorRoutes[coordinatorSession];
|
|
27809
|
-
removed.coordinatorRoutes += 1;
|
|
27810
|
-
}
|
|
27811
|
-
const packages = this.ensureHumanQuestionPackages(state);
|
|
27812
|
-
for (const [packageId, packageRecord] of Object.entries(packages ?? {})) {
|
|
27813
|
-
if (packageRecord.coordinatorSession === coordinatorSession) {
|
|
27814
|
-
delete packages[packageId];
|
|
27815
|
-
removed.humanQuestionPackages += 1;
|
|
27816
|
-
}
|
|
27817
|
-
}
|
|
27818
|
-
if (state.orchestration.coordinatorQuestionState?.[coordinatorSession] !== undefined) {
|
|
27819
|
-
delete state.orchestration.coordinatorQuestionState[coordinatorSession];
|
|
27820
|
-
removed.coordinatorQuestionState += 1;
|
|
27821
|
-
}
|
|
27822
|
-
return removed;
|
|
27823
|
-
}
|
|
27824
27768
|
bumpGroupUpdated(state, groupId, now) {
|
|
27825
27769
|
if (!groupId) {
|
|
27826
27770
|
return;
|
|
@@ -27994,7 +27938,7 @@ class OrchestrationService {
|
|
|
27994
27938
|
}
|
|
27995
27939
|
const validQueuedQuestions = coordinatorState.queuedQuestions.filter((entry) => {
|
|
27996
27940
|
const task = state.orchestration.tasks[entry.taskId];
|
|
27997
|
-
return task
|
|
27941
|
+
return task !== undefined && sameCoordinatorSession(task.coordinatorSession, coordinatorSession) && task.status === "blocked" && task.openQuestion?.status === "open" && task.openQuestion.questionId === entry.questionId;
|
|
27998
27942
|
});
|
|
27999
27943
|
if (validQueuedQuestions.length !== coordinatorState.queuedQuestions.length) {
|
|
28000
27944
|
coordinatorState.queuedQuestions = validQueuedQuestions;
|
|
@@ -28483,7 +28427,7 @@ async function createScheduledTaskFromRoute(input, deps) {
|
|
|
28483
28427
|
if (coordinatorSession.length === 0) {
|
|
28484
28428
|
throw new Error("coordinatorSession must be a non-empty string");
|
|
28485
28429
|
}
|
|
28486
|
-
const route = deps.state.orchestration.coordinatorRoutes[coordinatorSession];
|
|
28430
|
+
const route = deps.state.orchestration.coordinatorRoutes[stableCoordinatorSession(coordinatorSession)];
|
|
28487
28431
|
if (!route) {
|
|
28488
28432
|
throw new Error(`no chat route is recorded for coordinator session "${coordinatorSession}"`);
|
|
28489
28433
|
}
|
|
@@ -28510,7 +28454,7 @@ async function createScheduledTaskFromRoute(input, deps) {
|
|
|
28510
28454
|
if (!session3) {
|
|
28511
28455
|
throw new Error(`session "${route.sessionAlias}" recorded for coordinator session "${coordinatorSession}" was not found`);
|
|
28512
28456
|
}
|
|
28513
|
-
if (session3.transportSession
|
|
28457
|
+
if (!sameCoordinatorSession(session3.transportSession, coordinatorSession)) {
|
|
28514
28458
|
throw new Error(`session "${route.sessionAlias}" is no longer attached to coordinator session "${coordinatorSession}"`);
|
|
28515
28459
|
}
|
|
28516
28460
|
const executeAt = parseRouteScheduledTime(input.timeText, deps.now?.() ?? new Date);
|
|
@@ -28576,7 +28520,7 @@ function resolveOwnedCoordinatorRoute(coordinatorSession, state, label) {
|
|
|
28576
28520
|
if (session3.length === 0) {
|
|
28577
28521
|
throw new Error("coordinatorSession must be a non-empty string");
|
|
28578
28522
|
}
|
|
28579
|
-
const route = state.orchestration.coordinatorRoutes[session3];
|
|
28523
|
+
const route = state.orchestration.coordinatorRoutes[stableCoordinatorSession(session3)];
|
|
28580
28524
|
if (!route) {
|
|
28581
28525
|
throw new Error(`no chat route is recorded for coordinator session "${session3}"`);
|
|
28582
28526
|
}
|
|
@@ -28668,9 +28612,10 @@ class SessionService {
|
|
|
28668
28612
|
return this.state.chat_contexts[chatKey]?.current_session;
|
|
28669
28613
|
}
|
|
28670
28614
|
async getPreferredSessionForTransport(transportSession) {
|
|
28671
|
-
const
|
|
28672
|
-
const
|
|
28673
|
-
const
|
|
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];
|
|
28674
28619
|
const preferred = matches.find((session3) => session3.alias === expectedAlias && session3.workspace === expectedWorkspace) ?? matches[0];
|
|
28675
28620
|
return preferred ? this.toResolvedSession(preferred) : null;
|
|
28676
28621
|
}
|
|
@@ -29175,7 +29120,7 @@ class DebouncedStateStore {
|
|
|
29175
29120
|
}
|
|
29176
29121
|
|
|
29177
29122
|
// src/commands/command-hints.ts
|
|
29178
|
-
function
|
|
29123
|
+
function listXacpxCommandHints() {
|
|
29179
29124
|
const hints3 = [{ name: "/help", description: t().hints.helpDescription }];
|
|
29180
29125
|
for (const topic of listHelpTopics()) {
|
|
29181
29126
|
const name = PRIMARY_COMMAND_BY_TOPIC[topic.topic];
|
|
@@ -29222,7 +29167,6 @@ async function runConsole(paths, deps) {
|
|
|
29222
29167
|
let runtime = null;
|
|
29223
29168
|
let consumerLock;
|
|
29224
29169
|
let heartbeatTimer = null;
|
|
29225
|
-
let gcResetTimer = null;
|
|
29226
29170
|
let consumerLockAcquired = false;
|
|
29227
29171
|
let daemonRuntimeStarted = false;
|
|
29228
29172
|
const shutdownController = new AbortController;
|
|
@@ -29236,12 +29180,6 @@ async function runConsole(paths, deps) {
|
|
|
29236
29180
|
if (deps.afterBuild) {
|
|
29237
29181
|
await deps.afterBuild(runtime);
|
|
29238
29182
|
}
|
|
29239
|
-
try {
|
|
29240
|
-
await runtime.orchestration.service.purgeExpiredResetCoordinators({
|
|
29241
|
-
cutoffDays: 7,
|
|
29242
|
-
trigger: "startup"
|
|
29243
|
-
});
|
|
29244
|
-
} catch {}
|
|
29245
29183
|
try {
|
|
29246
29184
|
await runtime.orchestration.service.reconcileParallelSlots();
|
|
29247
29185
|
} catch (reconcileError) {
|
|
@@ -29297,6 +29235,7 @@ async function runConsole(paths, deps) {
|
|
|
29297
29235
|
throw error2;
|
|
29298
29236
|
}
|
|
29299
29237
|
}
|
|
29238
|
+
await runtime.reapStaleQueueOwners();
|
|
29300
29239
|
if (deps.beforeReady) {
|
|
29301
29240
|
await deps.beforeReady(runtime);
|
|
29302
29241
|
}
|
|
@@ -29310,10 +29249,6 @@ async function runConsole(paths, deps) {
|
|
|
29310
29249
|
heartbeatTimer = setIntervalFn(() => {
|
|
29311
29250
|
deps.daemonRuntime?.heartbeat().catch(() => {});
|
|
29312
29251
|
}, deps.heartbeatIntervalMs ?? 30000);
|
|
29313
|
-
const runtimeForGc = runtime;
|
|
29314
|
-
gcResetTimer = setIntervalFn(() => {
|
|
29315
|
-
runtimeForGc.orchestration.service.purgeExpiredResetCoordinators({ cutoffDays: 7, trigger: "interval" }).catch(() => {});
|
|
29316
|
-
}, 86400000);
|
|
29317
29252
|
}
|
|
29318
29253
|
const channelStartPromise = deps.channels.startAll({
|
|
29319
29254
|
agent: runtime.agent,
|
|
@@ -29323,8 +29258,8 @@ async function runConsole(paths, deps) {
|
|
|
29323
29258
|
activeTurns: runtime.activeTurns,
|
|
29324
29259
|
logger: runtime.logger,
|
|
29325
29260
|
perfTracer: runtime.perfTracer,
|
|
29326
|
-
commandHints:
|
|
29327
|
-
coreVersion:
|
|
29261
|
+
commandHints: listXacpxCommandHints(),
|
|
29262
|
+
coreVersion: XACPX_CORE_VERSION,
|
|
29328
29263
|
locale: getLocale()
|
|
29329
29264
|
});
|
|
29330
29265
|
channelStartPromise.catch(() => {});
|
|
@@ -29368,7 +29303,6 @@ async function runConsole(paths, deps) {
|
|
|
29368
29303
|
signalHandler,
|
|
29369
29304
|
clearIntervalFn,
|
|
29370
29305
|
heartbeatTimer,
|
|
29371
|
-
gcResetTimer,
|
|
29372
29306
|
...deps.daemonRuntime ? { daemonRuntime: deps.daemonRuntime } : {},
|
|
29373
29307
|
runtime,
|
|
29374
29308
|
consumerLock,
|
|
@@ -29394,9 +29328,6 @@ async function runCleanupSequence(input) {
|
|
|
29394
29328
|
if (input.heartbeatTimer !== null) {
|
|
29395
29329
|
input.clearIntervalFn(input.heartbeatTimer);
|
|
29396
29330
|
}
|
|
29397
|
-
if (input.gcResetTimer !== null) {
|
|
29398
|
-
input.clearIntervalFn(input.gcResetTimer);
|
|
29399
|
-
}
|
|
29400
29331
|
if (input.daemonRuntime && input.runtime) {
|
|
29401
29332
|
try {
|
|
29402
29333
|
await input.runtime.orchestration.server.stop();
|
|
@@ -30016,6 +29947,10 @@ ${result.text}` : "" };
|
|
|
30016
29947
|
async removeSession(session3) {
|
|
30017
29948
|
await this.client.request("removeSession", this.toParams(session3));
|
|
30018
29949
|
}
|
|
29950
|
+
async getAgentSessionId(session3) {
|
|
29951
|
+
const result = await this.client.request("getAgentSessionId", this.toParams(session3));
|
|
29952
|
+
return result.agentSessionId;
|
|
29953
|
+
}
|
|
30019
29954
|
async hasSession(session3) {
|
|
30020
29955
|
const result = await this.client.request("hasSession", this.toParams(session3));
|
|
30021
29956
|
return result.exists;
|
|
@@ -30510,10 +30445,10 @@ import { spawn as spawn8 } from "node:child_process";
|
|
|
30510
30445
|
import { readFile as readFile12, unlink } from "node:fs/promises";
|
|
30511
30446
|
import { homedir as homedir8 } from "node:os";
|
|
30512
30447
|
import { join as join17 } from "node:path";
|
|
30513
|
-
function
|
|
30514
|
-
const { command, args } = splitCommandLine(input.
|
|
30448
|
+
function buildXacpxMcpServerSpec(input) {
|
|
30449
|
+
const { command, args } = splitCommandLine(input.xacpxCommand);
|
|
30515
30450
|
return {
|
|
30516
|
-
name: "
|
|
30451
|
+
name: "xacpx",
|
|
30517
30452
|
type: "stdio",
|
|
30518
30453
|
command,
|
|
30519
30454
|
args: [
|
|
@@ -30540,7 +30475,7 @@ function buildQueueOwnerPayload(input) {
|
|
|
30540
30475
|
|
|
30541
30476
|
class AcpxQueueOwnerLauncher {
|
|
30542
30477
|
acpxCommand;
|
|
30543
|
-
|
|
30478
|
+
xacpxCommand;
|
|
30544
30479
|
spawnOwner;
|
|
30545
30480
|
terminateOwner;
|
|
30546
30481
|
baseEnv;
|
|
@@ -30549,7 +30484,7 @@ class AcpxQueueOwnerLauncher {
|
|
|
30549
30484
|
launchLocks = new Map;
|
|
30550
30485
|
constructor(options) {
|
|
30551
30486
|
this.acpxCommand = options.acpxCommand;
|
|
30552
|
-
this.
|
|
30487
|
+
this.xacpxCommand = options.xacpxCommand ?? resolveDefaultXacpxCommand(options.baseEnv ?? process.env);
|
|
30553
30488
|
this.spawnOwner = options.spawnOwner ?? defaultQueueOwnerSpawner;
|
|
30554
30489
|
this.terminateOwner = options.terminateOwner ?? createDefaultQueueOwnerTerminator(options.acpxCommand);
|
|
30555
30490
|
this.baseEnv = options.baseEnv ?? process.env;
|
|
@@ -30577,8 +30512,8 @@ class AcpxQueueOwnerLauncher {
|
|
|
30577
30512
|
nonInteractivePermissions: input.nonInteractivePermissions,
|
|
30578
30513
|
ttlMs: this.ttlMs,
|
|
30579
30514
|
maxQueueDepth: this.maxQueueDepth,
|
|
30580
|
-
mcpServers: [
|
|
30581
|
-
|
|
30515
|
+
mcpServers: [buildXacpxMcpServerSpec({
|
|
30516
|
+
xacpxCommand: this.xacpxCommand,
|
|
30582
30517
|
coordinatorSession: input.coordinatorSession,
|
|
30583
30518
|
...input.sourceHandle ? { sourceHandle: input.sourceHandle } : {}
|
|
30584
30519
|
})]
|
|
@@ -30633,13 +30568,13 @@ function splitCommandLine(value) {
|
|
|
30633
30568
|
current += "\\";
|
|
30634
30569
|
}
|
|
30635
30570
|
if (quote) {
|
|
30636
|
-
throw new Error("
|
|
30571
|
+
throw new Error("xacpx MCP command has an unterminated quote");
|
|
30637
30572
|
}
|
|
30638
30573
|
if (current.length > 0) {
|
|
30639
30574
|
parts.push(current);
|
|
30640
30575
|
}
|
|
30641
30576
|
if (parts.length === 0) {
|
|
30642
|
-
throw new Error("
|
|
30577
|
+
throw new Error("xacpx MCP command must not be empty");
|
|
30643
30578
|
}
|
|
30644
30579
|
return { command: parts[0], args: parts.slice(1) };
|
|
30645
30580
|
}
|
|
@@ -30685,7 +30620,7 @@ function queueLockFilePath(sessionId) {
|
|
|
30685
30620
|
function shortHash(value, length) {
|
|
30686
30621
|
return createHash3("sha256").update(value).digest("hex").slice(0, length);
|
|
30687
30622
|
}
|
|
30688
|
-
function
|
|
30623
|
+
function resolveDefaultXacpxCommand(env) {
|
|
30689
30624
|
const cliCommand = coreEnv("CLI_COMMAND", env);
|
|
30690
30625
|
if (cliCommand?.trim()) {
|
|
30691
30626
|
return cliCommand.trim();
|
|
@@ -31036,8 +30971,9 @@ ${baseText}` : "" };
|
|
|
31036
30971
|
try {
|
|
31037
30972
|
const parsed = JSON.parse(result.stdout);
|
|
31038
30973
|
const acpxRecordId = typeof parsed.acpxRecordId === "string" ? parsed.acpxRecordId : typeof parsed.id === "string" ? parsed.id : undefined;
|
|
30974
|
+
const agentSessionId = typeof parsed.agentSessionId === "string" ? parsed.agentSessionId : undefined;
|
|
31039
30975
|
if (acpxRecordId) {
|
|
31040
|
-
return { acpxRecordId };
|
|
30976
|
+
return { acpxRecordId, agentSessionId };
|
|
31041
30977
|
}
|
|
31042
30978
|
} catch {
|
|
31043
30979
|
const firstLine = result.stdout.trim().split(/\r?\n/, 1)[0];
|
|
@@ -31047,6 +30983,10 @@ ${baseText}` : "" };
|
|
|
31047
30983
|
}
|
|
31048
30984
|
throw new Error("failed to resolve acpx session record id");
|
|
31049
30985
|
}
|
|
30986
|
+
async getAgentSessionId(session3) {
|
|
30987
|
+
const record3 = await this.readSessionRecord(session3);
|
|
30988
|
+
return record3.agentSessionId;
|
|
30989
|
+
}
|
|
31050
30990
|
async run(args, options) {
|
|
31051
30991
|
const result = await this.runCommandWithTimeout(this.runCommand, args, options);
|
|
31052
30992
|
if (result.code !== 0) {
|
|
@@ -31411,6 +31351,17 @@ var init_queue_owner_reaper = __esm(() => {
|
|
|
31411
31351
|
});
|
|
31412
31352
|
|
|
31413
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
|
+
}
|
|
31414
31365
|
function workerBindingReapTargets(orchestration3, config4) {
|
|
31415
31366
|
const targets = [];
|
|
31416
31367
|
for (const [workerSession, binding] of Object.entries(orchestration3.workerBindings)) {
|
|
@@ -32132,7 +32083,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
32132
32083
|
}
|
|
32133
32084
|
},
|
|
32134
32085
|
findReusableWorkerSession: async ({ coordinatorSession, workspace: workspace3, cwd, targetAgent, role }) => {
|
|
32135
|
-
const binding = Object.entries(state.orchestration.workerBindings).find(([, current]) => current.ephemeral !== true && current.coordinatorSession
|
|
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);
|
|
32136
32087
|
return binding?.[0] ?? null;
|
|
32137
32088
|
},
|
|
32138
32089
|
logger: logger2
|
|
@@ -32190,6 +32141,33 @@ async function buildApp(paths, deps = {}) {
|
|
|
32190
32141
|
}),
|
|
32191
32142
|
logger: logger2
|
|
32192
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
|
+
};
|
|
32193
32171
|
return {
|
|
32194
32172
|
agent: agent3,
|
|
32195
32173
|
router: router3,
|
|
@@ -32210,41 +32188,14 @@ async function buildApp(paths, deps = {}) {
|
|
|
32210
32188
|
service: scheduledService,
|
|
32211
32189
|
scheduler: scheduledScheduler
|
|
32212
32190
|
},
|
|
32191
|
+
reapStaleQueueOwners: () => reapWarmQueueOwners("startup"),
|
|
32213
32192
|
dispose: async () => {
|
|
32214
32193
|
scheduledScheduler.stop();
|
|
32215
32194
|
if (progressHeartbeatInterval !== undefined) {
|
|
32216
32195
|
clearInterval(progressHeartbeatInterval);
|
|
32217
32196
|
}
|
|
32218
32197
|
await Promise.allSettled([...pendingWorkerDispatches]);
|
|
32219
|
-
|
|
32220
|
-
const targets = [
|
|
32221
|
-
...sessions.listAllResolvedSessions().map((session3) => ({
|
|
32222
|
-
agent: session3.agent,
|
|
32223
|
-
...session3.agentCommand ? { agentCommand: session3.agentCommand } : {},
|
|
32224
|
-
cwd: session3.cwd,
|
|
32225
|
-
transportSession: session3.transportSession
|
|
32226
|
-
})),
|
|
32227
|
-
...workerBindingReapTargets(state.orchestration, config4)
|
|
32228
|
-
];
|
|
32229
|
-
if (targets.length > 0) {
|
|
32230
|
-
const { terminated, attempted } = await reapQueueOwners(acpxCommand, targets, {
|
|
32231
|
-
onError: (target, error2) => {
|
|
32232
|
-
logger2.info("transport.queue_owner_reap.failed", "failed to reap queue owner on shutdown", {
|
|
32233
|
-
transport_session: target.transportSession,
|
|
32234
|
-
error: error2 instanceof Error ? error2.message : String(error2)
|
|
32235
|
-
}).catch(() => {});
|
|
32236
|
-
}
|
|
32237
|
-
});
|
|
32238
|
-
await logger2.info("transport.queue_owner_reap.completed", "reaped warm queue owners on shutdown", {
|
|
32239
|
-
terminated,
|
|
32240
|
-
attempted
|
|
32241
|
-
}).catch(() => {});
|
|
32242
|
-
}
|
|
32243
|
-
} catch (err) {
|
|
32244
|
-
await logger2.error("transport.queue_owner_reap.error", "queue owner reap failed during shutdown", {
|
|
32245
|
-
error: err instanceof Error ? err.message : String(err)
|
|
32246
|
-
}).catch(() => {});
|
|
32247
|
-
}
|
|
32198
|
+
await reapWarmQueueOwners("shutdown");
|
|
32248
32199
|
await debouncedStateStore.dispose();
|
|
32249
32200
|
if ("dispose" in transport && typeof transport.dispose === "function") {
|
|
32250
32201
|
await transport.dispose();
|
|
@@ -33583,7 +33534,7 @@ class DaemonRuntime {
|
|
|
33583
33534
|
}
|
|
33584
33535
|
}
|
|
33585
33536
|
|
|
33586
|
-
// src/mcp/
|
|
33537
|
+
// src/mcp/xacpx-mcp-server.ts
|
|
33587
33538
|
import { stdin, stdout } from "node:process";
|
|
33588
33539
|
|
|
33589
33540
|
// node_modules/@modelcontextprotocol/sdk/node_modules/zod/v4/core/core.js
|
|
@@ -45939,7 +45890,7 @@ class StdioServerTransport {
|
|
|
45939
45890
|
}
|
|
45940
45891
|
}
|
|
45941
45892
|
|
|
45942
|
-
// src/mcp/
|
|
45893
|
+
// src/mcp/xacpx-mcp-server.ts
|
|
45943
45894
|
init_version();
|
|
45944
45895
|
|
|
45945
45896
|
// src/mcp/resolve-endpoint.ts
|
|
@@ -45967,7 +45918,10 @@ function requireHome(env) {
|
|
|
45967
45918
|
return home;
|
|
45968
45919
|
}
|
|
45969
45920
|
|
|
45970
|
-
// src/mcp/
|
|
45921
|
+
// src/mcp/xacpx-mcp-server.ts
|
|
45922
|
+
init_endpoint_probe();
|
|
45923
|
+
|
|
45924
|
+
// src/mcp/xacpx-mcp-tools.ts
|
|
45971
45925
|
init_task_watch_timeouts();
|
|
45972
45926
|
init_quota_errors();
|
|
45973
45927
|
var taskStatusSchema = exports_external.enum([
|
|
@@ -45988,7 +45942,7 @@ var taskQuestionSchema = exports_external.object({
|
|
|
45988
45942
|
taskId: exports_external.string().min(1),
|
|
45989
45943
|
questionId: exports_external.string().min(1)
|
|
45990
45944
|
}).strict();
|
|
45991
|
-
function
|
|
45945
|
+
function buildXacpxMcpToolRegistry(input) {
|
|
45992
45946
|
const { transport, coordinatorSession, sourceHandle, isExternalCoordinator, internalSessionTools, availableAgents } = input;
|
|
45993
45947
|
const tools = [
|
|
45994
45948
|
{
|
|
@@ -46146,7 +46100,7 @@ function buildWeacpxMcpToolRegistry(input) {
|
|
|
46146
46100
|
}).strict(),
|
|
46147
46101
|
handler: async (args) => await asToolResult(async () => {
|
|
46148
46102
|
if (!sourceHandle || sourceHandle.trim().length === 0) {
|
|
46149
|
-
throw new Error("worker_raise_question requires a bound sourceHandle; start mcp-stdio with --source-handle or
|
|
46103
|
+
throw new Error("worker_raise_question requires a bound sourceHandle; start mcp-stdio with --source-handle or XACPX_SOURCE_HANDLE");
|
|
46150
46104
|
}
|
|
46151
46105
|
const result = await transport.workerRaiseQuestion({
|
|
46152
46106
|
sourceHandle,
|
|
@@ -46209,7 +46163,7 @@ function buildWeacpxMcpToolRegistry(input) {
|
|
|
46209
46163
|
if (internalSessionTools && !isExternalCoordinator && !sourceHandle) {
|
|
46210
46164
|
tools.push({
|
|
46211
46165
|
name: "scheduled_create",
|
|
46212
|
-
description: "Schedule a one-shot task to run a natural-language message at a future time, using the recorded chat route. By default — and like /later — the task runs in a FRESH TEMPORARY session (it snapshots the current agent and workspace but starts with brand-new history and is destroyed after running, so it does not pollute this conversation); the reply is still pushed back to this chat. Provide only timeText and message and OMIT mode to get this default. Routing, session, and account are resolved by
|
|
46166
|
+
description: "Schedule a one-shot task to run a natural-language message at a future time, using the recorded chat route. By default — and like /later — the task runs in a FRESH TEMPORARY session (it snapshots the current agent and workspace but starts with brand-new history and is destroyed after running, so it does not pollute this conversation); the reply is still pushed back to this chat. Provide only timeText and message and OMIT mode to get this default. Routing, session, and account are resolved by xacpx.",
|
|
46213
46167
|
inputSchema: exports_external.object({
|
|
46214
46168
|
timeText: exports_external.string().min(1).describe("Time expression, e.g. 'in 2h', '30分钟后', 'tomorrow 09:00', or '周五 09:00'."),
|
|
46215
46169
|
message: exports_external.string().min(1).describe("Natural-language message to run at the scheduled time."),
|
|
@@ -46482,7 +46436,7 @@ function formatToolError(error2) {
|
|
|
46482
46436
|
init_orchestration_ipc();
|
|
46483
46437
|
init_task_watch_timeouts();
|
|
46484
46438
|
import { randomUUID } from "node:crypto";
|
|
46485
|
-
import { createConnection } from "node:net";
|
|
46439
|
+
import { createConnection as createConnection2 } from "node:net";
|
|
46486
46440
|
|
|
46487
46441
|
class OrchestrationClient {
|
|
46488
46442
|
endpoint;
|
|
@@ -46550,7 +46504,7 @@ class OrchestrationClient {
|
|
|
46550
46504
|
async request(method, params, timeoutMs = this.timeoutMs) {
|
|
46551
46505
|
const id = this.createId();
|
|
46552
46506
|
return await new Promise((resolve, reject) => {
|
|
46553
|
-
const socket =
|
|
46507
|
+
const socket = createConnection2(this.endpoint.path);
|
|
46554
46508
|
let buffer = "";
|
|
46555
46509
|
let settled = false;
|
|
46556
46510
|
let timer;
|
|
@@ -46619,7 +46573,7 @@ function getWatchRequestTimeoutMs(watchTimeoutMs, defaultTimeoutMs) {
|
|
|
46619
46573
|
return Math.max(defaultTimeoutMs, boundedWatchTimeoutMs + TASK_WATCH_RPC_TIMEOUT_PADDING_MS);
|
|
46620
46574
|
}
|
|
46621
46575
|
|
|
46622
|
-
// src/mcp/
|
|
46576
|
+
// src/mcp/xacpx-mcp-transport.ts
|
|
46623
46577
|
function createOrchestrationTransport(endpoint, deps = {}) {
|
|
46624
46578
|
const client = deps.client ?? new OrchestrationClient(endpoint);
|
|
46625
46579
|
return {
|
|
@@ -46647,7 +46601,7 @@ function createOrchestrationTransport(endpoint, deps = {}) {
|
|
|
46647
46601
|
workerRaiseQuestion: async (input) => {
|
|
46648
46602
|
const sourceHandle = input.sourceHandle.trim();
|
|
46649
46603
|
if (sourceHandle.length === 0) {
|
|
46650
|
-
throw new Error("worker_raise_question requires a bound sourceHandle; start mcp-stdio with --source-handle or
|
|
46604
|
+
throw new Error("worker_raise_question requires a bound sourceHandle; start mcp-stdio with --source-handle or XACPX_SOURCE_HANDLE");
|
|
46651
46605
|
}
|
|
46652
46606
|
return await client.workerRaiseQuestion({
|
|
46653
46607
|
taskId: input.taskId,
|
|
@@ -46681,11 +46635,11 @@ function createOrchestrationTransport(endpoint, deps = {}) {
|
|
|
46681
46635
|
};
|
|
46682
46636
|
}
|
|
46683
46637
|
|
|
46684
|
-
// src/mcp/
|
|
46638
|
+
// src/mcp/xacpx-mcp-server.ts
|
|
46685
46639
|
var TASK_OPTIONS_CACHE_LIMIT = 1000;
|
|
46686
46640
|
var TASKS_LIST_PAGE_SIZE = 100;
|
|
46687
46641
|
var WATCH_TASKS_CACHE_LIMIT = 256;
|
|
46688
|
-
var
|
|
46642
|
+
var XACPX_MCP_SERVER_INSTRUCTIONS = [
|
|
46689
46643
|
"Use these tools to orchestrate work across other agents under your coordinator session.",
|
|
46690
46644
|
"",
|
|
46691
46645
|
"Delegate with delegate_request (one task) or delegate_batch (several at once). Each returns a taskId and a status.",
|
|
@@ -46696,12 +46650,12 @@ var WEACPX_MCP_SERVER_INSTRUCTIONS = [
|
|
|
46696
46650
|
"worker_raise_question is worker-side only — call it from inside a delegated task when you are blocked, not from the coordinator waiting on a delegation."
|
|
46697
46651
|
].join(`
|
|
46698
46652
|
`);
|
|
46699
|
-
function
|
|
46653
|
+
function createXacpxMcpServer(options) {
|
|
46700
46654
|
let getToolState;
|
|
46701
46655
|
const taskOptionsById = new Map;
|
|
46702
46656
|
const watchTasksById = new Map;
|
|
46703
46657
|
const server = new Server({
|
|
46704
|
-
name: "
|
|
46658
|
+
name: "xacpx",
|
|
46705
46659
|
version: readVersion()
|
|
46706
46660
|
}, {
|
|
46707
46661
|
capabilities: {
|
|
@@ -46712,8 +46666,8 @@ function createWeacpxMcpServer(options) {
|
|
|
46712
46666
|
requests: { tools: { call: {} } }
|
|
46713
46667
|
}
|
|
46714
46668
|
},
|
|
46715
|
-
instructions:
|
|
46716
|
-
taskStore:
|
|
46669
|
+
instructions: XACPX_MCP_SERVER_INSTRUCTIONS,
|
|
46670
|
+
taskStore: createXacpxTaskStore(async () => await getToolState(), taskOptionsById, watchTasksById)
|
|
46717
46671
|
});
|
|
46718
46672
|
let toolState = null;
|
|
46719
46673
|
let toolStatePromise = null;
|
|
@@ -46726,7 +46680,7 @@ function createWeacpxMcpServer(options) {
|
|
|
46726
46680
|
}
|
|
46727
46681
|
toolStatePromise = resolveMcpIdentity(server, options).then((identity) => {
|
|
46728
46682
|
if (!options.transport) {
|
|
46729
|
-
throw new Error("
|
|
46683
|
+
throw new Error("xacpx MCP transport is not configured");
|
|
46730
46684
|
}
|
|
46731
46685
|
toolState = buildToolState({
|
|
46732
46686
|
transport: options.transport,
|
|
@@ -46807,7 +46761,7 @@ function createWeacpxMcpServer(options) {
|
|
|
46807
46761
|
return server;
|
|
46808
46762
|
}
|
|
46809
46763
|
function buildToolState(options) {
|
|
46810
|
-
const tools =
|
|
46764
|
+
const tools = buildXacpxMcpToolRegistry(options);
|
|
46811
46765
|
return {
|
|
46812
46766
|
tools,
|
|
46813
46767
|
toolMap: new Map(tools.map((tool) => [tool.name, tool])),
|
|
@@ -46966,10 +46920,10 @@ function renderWatchMcpTaskResult(result, watchTaskId) {
|
|
|
46966
46920
|
structuredContent: { watchTaskId, ...result }
|
|
46967
46921
|
};
|
|
46968
46922
|
}
|
|
46969
|
-
function
|
|
46923
|
+
function createXacpxTaskStore(resolveState, taskOptionsById, watchTasksById) {
|
|
46970
46924
|
return {
|
|
46971
46925
|
createTask: async () => {
|
|
46972
|
-
throw new Error("
|
|
46926
|
+
throw new Error("xacpx native MCP tasks are created by delegate_request");
|
|
46973
46927
|
},
|
|
46974
46928
|
getTask: async (taskId) => {
|
|
46975
46929
|
const watchTask = watchTasksById.get(taskId);
|
|
@@ -46980,7 +46934,7 @@ function createWeacpxTaskStore(resolveState, taskOptionsById, watchTasksById) {
|
|
|
46980
46934
|
return task ? toMcpTask(task, taskOptionsById.get(taskId)) : null;
|
|
46981
46935
|
},
|
|
46982
46936
|
storeTaskResult: async () => {
|
|
46983
|
-
throw new Error("
|
|
46937
|
+
throw new Error("xacpx native MCP task results are stored by orchestration");
|
|
46984
46938
|
},
|
|
46985
46939
|
getTaskResult: async (taskId) => {
|
|
46986
46940
|
const watchTask = watchTasksById.get(taskId);
|
|
@@ -47004,7 +46958,7 @@ function createWeacpxTaskStore(resolveState, taskOptionsById, watchTasksById) {
|
|
|
47004
46958
|
await state.transport.cancelTask({ coordinatorSession: state.coordinatorSession, taskId });
|
|
47005
46959
|
return;
|
|
47006
46960
|
}
|
|
47007
|
-
throw new Error(`
|
|
46961
|
+
throw new Error(`xacpx MCP task status is read-only (${status}${statusMessage ? `: ${statusMessage}` : ""})`);
|
|
47008
46962
|
},
|
|
47009
46963
|
listTasks: async (cursor) => {
|
|
47010
46964
|
const state = await resolveState();
|
|
@@ -47193,7 +47147,9 @@ function installMcpStdioShutdownHooks(options) {
|
|
|
47193
47147
|
const setIntervalFn = options.setIntervalFn ?? ((callback, ms) => setInterval(callback, ms));
|
|
47194
47148
|
const clearIntervalFn = options.clearIntervalFn ?? ((handle) => clearInterval(handle));
|
|
47195
47149
|
const parentPid = options.parentPid ?? process.ppid;
|
|
47196
|
-
const parentCheckIntervalMs = options.parentCheckIntervalMs ??
|
|
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;
|
|
47197
47153
|
let disposed = false;
|
|
47198
47154
|
let triggered = false;
|
|
47199
47155
|
const triggerShutdown = (reason, context) => {
|
|
@@ -47226,6 +47182,31 @@ function installMcpStdioShutdownHooks(options) {
|
|
|
47226
47182
|
}, parentCheckIntervalMs);
|
|
47227
47183
|
parentTimer.unref?.();
|
|
47228
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
|
+
}
|
|
47229
47210
|
return () => {
|
|
47230
47211
|
if (disposed)
|
|
47231
47212
|
return;
|
|
@@ -47240,13 +47221,16 @@ function installMcpStdioShutdownHooks(options) {
|
|
|
47240
47221
|
if (parentTimer) {
|
|
47241
47222
|
clearIntervalFn(parentTimer);
|
|
47242
47223
|
}
|
|
47224
|
+
if (endpointTimer) {
|
|
47225
|
+
clearIntervalFn(endpointTimer);
|
|
47226
|
+
}
|
|
47243
47227
|
};
|
|
47244
47228
|
}
|
|
47245
|
-
function
|
|
47229
|
+
function parseIntervalMs(raw, fallback) {
|
|
47246
47230
|
if (raw === undefined || raw.trim().length === 0)
|
|
47247
|
-
return
|
|
47231
|
+
return fallback;
|
|
47248
47232
|
const parsed = Number(raw);
|
|
47249
|
-
return Number.isFinite(parsed) && parsed >= 0 ? parsed :
|
|
47233
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
47250
47234
|
}
|
|
47251
47235
|
function errorContext(error2) {
|
|
47252
47236
|
const record3 = error2;
|
|
@@ -47264,9 +47248,10 @@ function defaultIsProcessRunning3(pid) {
|
|
|
47264
47248
|
return code !== "ESRCH";
|
|
47265
47249
|
}
|
|
47266
47250
|
}
|
|
47267
|
-
async function
|
|
47268
|
-
const
|
|
47269
|
-
const
|
|
47251
|
+
async function runXacpxMcpServer(options) {
|
|
47252
|
+
const endpoint = options.endpoint ?? resolveDefaultOrchestrationEndpoint(process.env, process.platform);
|
|
47253
|
+
const transport = options.transport ?? createOrchestrationTransport(endpoint);
|
|
47254
|
+
const server = createXacpxMcpServer({
|
|
47270
47255
|
transport,
|
|
47271
47256
|
...options.coordinatorSession ? { coordinatorSession: options.coordinatorSession } : {},
|
|
47272
47257
|
...options.sourceHandle ? { sourceHandle: options.sourceHandle } : {},
|
|
@@ -47298,6 +47283,7 @@ async function runWeacpxMcpServer(options) {
|
|
|
47298
47283
|
stdin,
|
|
47299
47284
|
stdout,
|
|
47300
47285
|
shutdown,
|
|
47286
|
+
probeEndpoint: () => canConnectToEndpoint(endpoint.path, 4000),
|
|
47301
47287
|
onDiagnostic: options.onDiagnostic
|
|
47302
47288
|
});
|
|
47303
47289
|
await server.connect(stdio);
|
|
@@ -47330,7 +47316,6 @@ function sanitizeMcpClientName(input) {
|
|
|
47330
47316
|
fallback: "mcp-host"
|
|
47331
47317
|
});
|
|
47332
47318
|
}
|
|
47333
|
-
|
|
47334
47319
|
// src/mcp/parse-string-flag.ts
|
|
47335
47320
|
function parseStringFlag(args, env, options) {
|
|
47336
47321
|
let fromFlag = null;
|
|
@@ -47566,7 +47551,6 @@ async function handleUpdateCli(args, deps) {
|
|
|
47566
47551
|
kind: "plugin",
|
|
47567
47552
|
name: plugin.name,
|
|
47568
47553
|
currentVersion: plugin.version,
|
|
47569
|
-
pinned: Boolean(plugin.version),
|
|
47570
47554
|
latestVersion: await latestOf(plugin.name)
|
|
47571
47555
|
});
|
|
47572
47556
|
}
|
|
@@ -47575,12 +47559,12 @@ async function handleUpdateCli(args, deps) {
|
|
|
47575
47559
|
const target = targets[index];
|
|
47576
47560
|
deps.print(`${index + 1}. ${formatTarget(target)}`);
|
|
47577
47561
|
}
|
|
47578
|
-
const unavailable = targets.filter((target) => !target.latestVersion
|
|
47562
|
+
const unavailable = targets.filter((target) => !target.latestVersion);
|
|
47579
47563
|
if (all && unavailable.length > 0) {
|
|
47580
47564
|
deps.print(t().cliUpdate.unavailableAborted(unavailable.map((target) => target.name).join(", ")));
|
|
47581
47565
|
return 1;
|
|
47582
47566
|
}
|
|
47583
|
-
const candidates = targets.filter((target) => target.latestVersion && (target.
|
|
47567
|
+
const candidates = targets.filter((target) => target.latestVersion && (target.successorPackage ? true : target.currentVersion !== target.latestVersion));
|
|
47584
47568
|
const selected = await selectTargets(targets, candidates, { all, explicitTarget: explicitTargets[0], deps });
|
|
47585
47569
|
if (!selected.ok) {
|
|
47586
47570
|
deps.print(selected.message);
|
|
@@ -47673,8 +47657,6 @@ async function selectTargets(targets, candidates, input) {
|
|
|
47673
47657
|
return { ok: false, message: t().cliUpdate.targetNotFound(input.explicitTarget), exitCode: 1 };
|
|
47674
47658
|
if (!target.latestVersion)
|
|
47675
47659
|
return { ok: false, message: t().cliUpdate.targetVersionUnknown(target.name), exitCode: 1 };
|
|
47676
|
-
if (target.kind === "plugin" && !target.pinned)
|
|
47677
|
-
return { ok: false, message: t().cliUpdate.targetNotPinned(target.name), exitCode: 1 };
|
|
47678
47660
|
if (!target.successorPackage && target.currentVersion === target.latestVersion)
|
|
47679
47661
|
return { ok: true, targets: [] };
|
|
47680
47662
|
return { ok: true, targets: [target] };
|
|
@@ -47698,8 +47680,6 @@ async function selectTargets(targets, candidates, input) {
|
|
|
47698
47680
|
const target = targets[index - 1];
|
|
47699
47681
|
if (!target.latestVersion)
|
|
47700
47682
|
return { ok: false, message: t().cliUpdate.targetVersionUnknown(target.name), exitCode: 1 };
|
|
47701
|
-
if (target.kind === "plugin" && !target.pinned)
|
|
47702
|
-
return { ok: false, message: t().cliUpdate.targetNotPinned(target.name), exitCode: 1 };
|
|
47703
47683
|
if (!target.successorPackage && target.currentVersion === target.latestVersion)
|
|
47704
47684
|
continue;
|
|
47705
47685
|
if (!selected.includes(target))
|
|
@@ -47851,6 +47831,10 @@ async function handleChannelCli(args, deps) {
|
|
|
47851
47831
|
if (args.length < 2 || !args[1])
|
|
47852
47832
|
return null;
|
|
47853
47833
|
return await dispatchSetEnabled(args[1], false, args.slice(2), deps);
|
|
47834
|
+
case "set-reply-mode":
|
|
47835
|
+
if (args.length < 3 || !args[1] || !args[2])
|
|
47836
|
+
return null;
|
|
47837
|
+
return await setChannelReplyMode(args[1], args[2], args.slice(3), deps);
|
|
47854
47838
|
default:
|
|
47855
47839
|
return null;
|
|
47856
47840
|
}
|
|
@@ -48167,6 +48151,28 @@ async function setChannelEnabled(type, enabled, rawArgs, deps) {
|
|
|
48167
48151
|
deps.print(t().channelCli.channelEnabledToggled(channel.id, enabled));
|
|
48168
48152
|
return await maybeRestartAfterMutation(restartFlags.restart, deps);
|
|
48169
48153
|
}
|
|
48154
|
+
async function setChannelReplyMode(type, mode, rawArgs, deps) {
|
|
48155
|
+
const restartFlags = parseRestartFlags(rawArgs);
|
|
48156
|
+
if (!restartFlags.ok) {
|
|
48157
|
+
deps.print(restartFlags.message);
|
|
48158
|
+
return 1;
|
|
48159
|
+
}
|
|
48160
|
+
if (mode !== "stream" && mode !== "final" && mode !== "verbose") {
|
|
48161
|
+
deps.print(t().channelCli.channelReplyModeInvalid(mode));
|
|
48162
|
+
return 1;
|
|
48163
|
+
}
|
|
48164
|
+
const config4 = await deps.loadConfig();
|
|
48165
|
+
ensureChannelsArray(config4);
|
|
48166
|
+
const channel = findChannel(config4.channels, type);
|
|
48167
|
+
if (!channel) {
|
|
48168
|
+
deps.print(t().channelCli.channelNotFound(type));
|
|
48169
|
+
return 1;
|
|
48170
|
+
}
|
|
48171
|
+
channel.replyMode = mode;
|
|
48172
|
+
await deps.saveConfig(config4);
|
|
48173
|
+
deps.print(t().channelCli.channelReplyModeSet(channel.id, mode));
|
|
48174
|
+
return await maybeRestartAfterMutation(restartFlags.restart, deps);
|
|
48175
|
+
}
|
|
48170
48176
|
function requireMultiAccountProvider(type, deps) {
|
|
48171
48177
|
const provider = getChannelCliProvider(type);
|
|
48172
48178
|
if (!provider) {
|
|
@@ -48493,7 +48499,7 @@ async function inspectPlugins(input) {
|
|
|
48493
48499
|
}
|
|
48494
48500
|
try {
|
|
48495
48501
|
const plugin = validateWeacpxPlugin(moduleValue, configPlugin.name, {
|
|
48496
|
-
...input.
|
|
48502
|
+
...input.currentXacpxVersion !== undefined ? { currentXacpxVersion: input.currentXacpxVersion } : {}
|
|
48497
48503
|
});
|
|
48498
48504
|
const channels = plugin.channels ?? [];
|
|
48499
48505
|
const channelTypes = channels.map((channel) => channel.type);
|
|
@@ -49087,7 +49093,7 @@ init_i18n();
|
|
|
49087
49093
|
init_bootstrap();
|
|
49088
49094
|
async function prepareMcpCoordinatorStartup(input) {
|
|
49089
49095
|
const coordinatorSession = input.coordinatorSession.trim();
|
|
49090
|
-
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));
|
|
49091
49097
|
const workspace3 = input.workspace?.trim();
|
|
49092
49098
|
if (workspace3) {
|
|
49093
49099
|
if (existingSession) {
|
|
@@ -49133,10 +49139,10 @@ function createMcpStdioIdentityResolver(input) {
|
|
|
49133
49139
|
const workspace3 = input.workspace?.trim() || null;
|
|
49134
49140
|
const sourceHandle = input.sourceHandle?.trim() || null;
|
|
49135
49141
|
const resolvedWorkspace = workspace3;
|
|
49136
|
-
const resolvedCoordinatorSession = parsedCoordinatorSession ?? inferExternalCoordinatorSession({
|
|
49142
|
+
const resolvedCoordinatorSession = stableCoordinatorSession(parsedCoordinatorSession ?? inferExternalCoordinatorSession({
|
|
49137
49143
|
clientName: context.clientName,
|
|
49138
49144
|
...resolvedWorkspace ? { workspace: resolvedWorkspace } : { instanceId }
|
|
49139
|
-
});
|
|
49145
|
+
}));
|
|
49140
49146
|
const startup = await prepareMcpCoordinatorStartup({
|
|
49141
49147
|
coordinatorSession: resolvedCoordinatorSession,
|
|
49142
49148
|
...resolvedWorkspace ? { workspace: resolvedWorkspace } : {},
|
|
@@ -49836,7 +49842,7 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
49836
49842
|
`);
|
|
49837
49843
|
return 2;
|
|
49838
49844
|
}
|
|
49839
|
-
await
|
|
49845
|
+
await runXacpxMcpServer({
|
|
49840
49846
|
transport,
|
|
49841
49847
|
...coordinatorSession ? { coordinatorSession } : {},
|
|
49842
49848
|
...sourceHandle ? { sourceHandle } : {},
|