@ganglion/xacpx 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/bridge/bridge-main.js +148 -9
- package/dist/channels/types.d.ts +16 -0
- package/dist/cli.js +1759 -371
- package/dist/commands/handlers/agent-handler.d.ts +1 -1
- package/dist/commands/handlers/session-handler.d.ts +13 -4
- package/dist/commands/parse-command.d.ts +7 -0
- package/dist/commands/router-types.d.ts +10 -2
- package/dist/config/agent-catalog.d.ts +14 -0
- package/dist/config/local-agent-bin.d.ts +17 -0
- package/dist/config/resolve-agent-command.d.ts +11 -0
- package/dist/config/types.d.ts +10 -0
- package/dist/control/control-event-bus.d.ts +65 -0
- package/dist/control/control-service.d.ts +147 -0
- package/dist/control/workspace-fs.d.ts +64 -0
- package/dist/i18n/types.d.ts +12 -0
- package/dist/plugin-api.d.ts +3 -0
- package/dist/plugin-api.js +53 -0
- package/dist/scheduled/scheduled-service.d.ts +3 -0
- package/dist/sessions/active-turn-registry.d.ts +2 -0
- package/dist/sessions/session-service.d.ts +4 -0
- package/dist/state/types.d.ts +2 -0
- package/dist/transport/native-session-history.d.ts +34 -0
- package/dist/transport/types.d.ts +37 -1
- package/dist/weixin/agent/interface.d.ts +8 -1
- package/package.json +17 -2
package/dist/cli.js
CHANGED
|
@@ -115,6 +115,14 @@ var init_session = __esm(() => {
|
|
|
115
115
|
modeModeLabel: (modeId) => `- mode: ${modeId}`,
|
|
116
116
|
modeNotSet: "not set",
|
|
117
117
|
modeSet: (modeId) => `Current session mode set to: ${modeId}`,
|
|
118
|
+
modelHeader: "Current model:",
|
|
119
|
+
modelSessionLabel: (alias) => `- Session: ${alias}`,
|
|
120
|
+
modelModelLabel: (modelId) => `- model: ${modelId}`,
|
|
121
|
+
modelNotSet: "not set (using agent default)",
|
|
122
|
+
modelAvailableLabel: (models) => `- available: ${models}`,
|
|
123
|
+
modelSet: (modelId) => `Current session model switched to: ${modelId}`,
|
|
124
|
+
modelSetFailed: (modelId, detail) => `Failed to switch model: ${modelId}
|
|
125
|
+
${detail}`,
|
|
118
126
|
replyModeHeader: "Current reply mode:",
|
|
119
127
|
replyModeSessionLabel: (alias) => `- Session: ${alias}`,
|
|
120
128
|
replyModeGlobalDefault: (value) => `- Global default: ${value}`,
|
|
@@ -191,6 +199,11 @@ var init_session = __esm(() => {
|
|
|
191
199
|
modeHelpCmdShowDesc: "Show the saved mode of the current session",
|
|
192
200
|
modeHelpCmdSet: "/mode <id>",
|
|
193
201
|
modeHelpCmdSetDesc: "Set the current session mode",
|
|
202
|
+
modelHelpSummary: "View or switch the LLM model for the current session.",
|
|
203
|
+
modelHelpCmdShow: "/model",
|
|
204
|
+
modelHelpCmdShowDesc: "Show the current session model and the available ones",
|
|
205
|
+
modelHelpCmdSet: "/model <id>",
|
|
206
|
+
modelHelpCmdSetDesc: "Switch the current session model (e.g. gpt-5.2[high])",
|
|
194
207
|
replyModeHelpSummary: "View or set the reply output mode for the current logical session.",
|
|
195
208
|
replyModeHelpCmdShow: "/replymode",
|
|
196
209
|
replyModeHelpCmdShowDesc: "Show global default, current override, and effective value",
|
|
@@ -1194,6 +1207,14 @@ var init_session2 = __esm(() => {
|
|
|
1194
1207
|
modeModeLabel: (modeId) => `- mode:${modeId}`,
|
|
1195
1208
|
modeNotSet: "未设置",
|
|
1196
1209
|
modeSet: (modeId) => `已设置当前会话 mode:${modeId}`,
|
|
1210
|
+
modelHeader: "当前 model:",
|
|
1211
|
+
modelSessionLabel: (alias) => `- 会话:${alias}`,
|
|
1212
|
+
modelModelLabel: (modelId) => `- model:${modelId}`,
|
|
1213
|
+
modelNotSet: "未设置(使用 agent 默认)",
|
|
1214
|
+
modelAvailableLabel: (models) => `- 可选:${models}`,
|
|
1215
|
+
modelSet: (modelId) => `已切换当前会话 model:${modelId}`,
|
|
1216
|
+
modelSetFailed: (modelId, detail) => `切换 model 失败:${modelId}
|
|
1217
|
+
${detail}`,
|
|
1197
1218
|
replyModeHeader: "当前 reply mode:",
|
|
1198
1219
|
replyModeSessionLabel: (alias) => `- 会话:${alias}`,
|
|
1199
1220
|
replyModeGlobalDefault: (value) => `- 全局默认:${value}`,
|
|
@@ -1270,6 +1291,11 @@ var init_session2 = __esm(() => {
|
|
|
1270
1291
|
modeHelpCmdShowDesc: "查看当前会话已保存的 mode",
|
|
1271
1292
|
modeHelpCmdSet: "/mode <id>",
|
|
1272
1293
|
modeHelpCmdSetDesc: "设置当前会话 mode",
|
|
1294
|
+
modelHelpSummary: "查看或切换当前会话的 LLM model。",
|
|
1295
|
+
modelHelpCmdShow: "/model",
|
|
1296
|
+
modelHelpCmdShowDesc: "查看当前会话 model 及可选项",
|
|
1297
|
+
modelHelpCmdSet: "/model <id>",
|
|
1298
|
+
modelHelpCmdSetDesc: "切换当前会话 model(如 gpt-5.2[high])",
|
|
1273
1299
|
replyModeHelpSummary: "查看或设置当前逻辑会话的回复输出模式。",
|
|
1274
1300
|
replyModeHelpCmdShow: "/replymode",
|
|
1275
1301
|
replyModeHelpCmdShowDesc: "查看全局默认、当前覆盖和实际生效值",
|
|
@@ -4402,6 +4428,53 @@ var init_plugin_renames = __esm(() => {
|
|
|
4402
4428
|
]);
|
|
4403
4429
|
});
|
|
4404
4430
|
|
|
4431
|
+
// src/config/local-agent-bin.ts
|
|
4432
|
+
import { statSync } from "node:fs";
|
|
4433
|
+
import { delimiter, join as join3 } from "node:path";
|
|
4434
|
+
function executableExtensions(platform, env) {
|
|
4435
|
+
return platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter((e) => e.length > 0) : [""];
|
|
4436
|
+
}
|
|
4437
|
+
function defaultIsExecutableFile(p) {
|
|
4438
|
+
try {
|
|
4439
|
+
const st = statSync(p);
|
|
4440
|
+
if (!st.isFile())
|
|
4441
|
+
return false;
|
|
4442
|
+
return process.platform === "win32" || (st.mode & 73) !== 0;
|
|
4443
|
+
} catch {
|
|
4444
|
+
return false;
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
function isExecutableOnPath(name, env = process.env, isExecutableFile = defaultIsExecutableFile) {
|
|
4448
|
+
const pathValue = env.PATH ?? env.Path ?? "";
|
|
4449
|
+
if (!pathValue)
|
|
4450
|
+
return false;
|
|
4451
|
+
const exts = executableExtensions(process.platform, env);
|
|
4452
|
+
for (const dir of pathValue.split(delimiter)) {
|
|
4453
|
+
if (!dir)
|
|
4454
|
+
continue;
|
|
4455
|
+
for (const ext of exts) {
|
|
4456
|
+
if (isExecutableFile(join3(dir, name + ext)))
|
|
4457
|
+
return true;
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4460
|
+
return false;
|
|
4461
|
+
}
|
|
4462
|
+
function resolveLocalAgentCommand(driver, onPath = (name) => isExecutableOnPath(name)) {
|
|
4463
|
+
const spec = LOCAL_AGENT_BINS[driver];
|
|
4464
|
+
if (!spec)
|
|
4465
|
+
return;
|
|
4466
|
+
if (!onPath(spec.bin))
|
|
4467
|
+
return;
|
|
4468
|
+
return [spec.bin, ...spec.args].join(" ");
|
|
4469
|
+
}
|
|
4470
|
+
var LOCAL_AGENT_BINS;
|
|
4471
|
+
var init_local_agent_bin = __esm(() => {
|
|
4472
|
+
LOCAL_AGENT_BINS = {
|
|
4473
|
+
opencode: { bin: "opencode", args: ["acp"] },
|
|
4474
|
+
kilocode: { bin: "kilocode", args: ["acp"] }
|
|
4475
|
+
};
|
|
4476
|
+
});
|
|
4477
|
+
|
|
4405
4478
|
// src/config/resolve-agent-command.ts
|
|
4406
4479
|
function resolveAgentCommand(driver, command) {
|
|
4407
4480
|
if (!command) {
|
|
@@ -4412,10 +4485,20 @@ function resolveAgentCommand(driver, command) {
|
|
|
4412
4485
|
}
|
|
4413
4486
|
return command;
|
|
4414
4487
|
}
|
|
4488
|
+
function resolveRuntimeAgentCommand(driver, command, preferLocal = true) {
|
|
4489
|
+
const explicit = resolveAgentCommand(driver, command);
|
|
4490
|
+
if (explicit) {
|
|
4491
|
+
return explicit;
|
|
4492
|
+
}
|
|
4493
|
+
return preferLocal ? resolveLocalAgentCommand(driver) : undefined;
|
|
4494
|
+
}
|
|
4415
4495
|
function isLegacyCodexCommand(command) {
|
|
4416
4496
|
const normalized = command.trim().replaceAll("\\", "/").toLowerCase();
|
|
4417
4497
|
return normalized === "./node_modules/.bin/codex-acp" || normalized === "./node_modules/.bin/codex-acp.exe" || normalized.endsWith("/node_modules/.bin/codex-acp") || normalized.endsWith("/node_modules/.bin/codex-acp.exe") || normalized.includes("/@zed-industries/codex-acp/bin/codex-acp.js") || normalized.includes("@zed-industries/codex-acp/bin/codex-acp.js");
|
|
4418
4498
|
}
|
|
4499
|
+
var init_resolve_agent_command = __esm(() => {
|
|
4500
|
+
init_local_agent_bin();
|
|
4501
|
+
});
|
|
4419
4502
|
|
|
4420
4503
|
// src/config/load-config.ts
|
|
4421
4504
|
import { readFile } from "node:fs/promises";
|
|
@@ -4505,6 +4588,9 @@ function parseConfig(raw, options = {}) {
|
|
|
4505
4588
|
if ("queueOwnerTtlSeconds" in transport && (typeof transport.queueOwnerTtlSeconds !== "number" || !Number.isFinite(transport.queueOwnerTtlSeconds) || transport.queueOwnerTtlSeconds < 0)) {
|
|
4506
4589
|
throw new Error("transport.queueOwnerTtlSeconds must be a non-negative number (0 = keep alive forever)");
|
|
4507
4590
|
}
|
|
4591
|
+
if ("preferLocalAgents" in transport && typeof transport.preferLocalAgents !== "boolean") {
|
|
4592
|
+
throw new Error("transport.preferLocalAgents must be a boolean");
|
|
4593
|
+
}
|
|
4508
4594
|
if (!isRecord(raw.agents)) {
|
|
4509
4595
|
throw new Error("agents must be an object");
|
|
4510
4596
|
}
|
|
@@ -4572,9 +4658,11 @@ function parseConfig(raw, options = {}) {
|
|
|
4572
4658
|
for (const [name, agent3] of Object.entries(rawAgents)) {
|
|
4573
4659
|
const driver = agent3.driver;
|
|
4574
4660
|
const command = typeof agent3.command === "string" ? resolveAgentCommand(driver, agent3.command) : undefined;
|
|
4661
|
+
const model = typeof agent3.model === "string" && agent3.model.trim().length > 0 ? agent3.model.trim() : undefined;
|
|
4575
4662
|
agents[name] = {
|
|
4576
4663
|
driver,
|
|
4577
|
-
...command ? { command } : {}
|
|
4664
|
+
...command ? { command } : {},
|
|
4665
|
+
...model ? { model } : {}
|
|
4578
4666
|
};
|
|
4579
4667
|
}
|
|
4580
4668
|
const rawWorkspaces = raw.workspaces;
|
|
@@ -4759,6 +4847,7 @@ var DEFAULT_PERF_LOG_CONFIG, DEFAULT_LOGGING_CONFIG, DEFAULT_PERMISSION_MODE = "
|
|
|
4759
4847
|
var init_load_config = __esm(() => {
|
|
4760
4848
|
init_workspace_path();
|
|
4761
4849
|
init_plugin_renames();
|
|
4850
|
+
init_resolve_agent_command();
|
|
4762
4851
|
init_resolve_locale();
|
|
4763
4852
|
DEFAULT_PERF_LOG_CONFIG = {
|
|
4764
4853
|
enabled: false,
|
|
@@ -4836,7 +4925,8 @@ class ConfigStore {
|
|
|
4836
4925
|
const agents = ensureRecordAt(raw, "agents");
|
|
4837
4926
|
agents[name] = {
|
|
4838
4927
|
driver: agent3.driver,
|
|
4839
|
-
...agent3.command ? { command: agent3.command } : {}
|
|
4928
|
+
...agent3.command ? { command: agent3.command } : {},
|
|
4929
|
+
...agent3.model ? { model: agent3.model } : {}
|
|
4840
4930
|
};
|
|
4841
4931
|
});
|
|
4842
4932
|
}
|
|
@@ -5131,6 +5221,7 @@ var init_ensure_config = __esm(() => {
|
|
|
5131
5221
|
init_config_store();
|
|
5132
5222
|
init_default_workspace();
|
|
5133
5223
|
init_load_config();
|
|
5224
|
+
init_resolve_agent_command();
|
|
5134
5225
|
BUILTIN_DEFAULT_CONFIG_TEMPLATE = {
|
|
5135
5226
|
transport: {
|
|
5136
5227
|
type: "acpx-bridge"
|
|
@@ -5647,7 +5738,7 @@ var init_create_daemon_controller = __esm(() => {
|
|
|
5647
5738
|
|
|
5648
5739
|
// src/orchestration/orchestration-ipc.ts
|
|
5649
5740
|
import { createHash } from "node:crypto";
|
|
5650
|
-
import { join as
|
|
5741
|
+
import { join as join4 } from "node:path";
|
|
5651
5742
|
function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
5652
5743
|
if (platform === "win32") {
|
|
5653
5744
|
const suffix = createHash("sha256").update(runtimeDir).digest("hex").slice(0, 12);
|
|
@@ -5658,7 +5749,7 @@ function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
|
5658
5749
|
}
|
|
5659
5750
|
return {
|
|
5660
5751
|
kind: "unix",
|
|
5661
|
-
path:
|
|
5752
|
+
path: join4(runtimeDir, "orchestration.sock")
|
|
5662
5753
|
};
|
|
5663
5754
|
}
|
|
5664
5755
|
function createOrchestrationEndpoint(path2, platform = process.platform) {
|
|
@@ -5678,20 +5769,20 @@ function encodeOrchestrationRpcResponse(response) {
|
|
|
5678
5769
|
var init_orchestration_ipc = () => {};
|
|
5679
5770
|
|
|
5680
5771
|
// src/daemon/daemon-files.ts
|
|
5681
|
-
import { dirname as dirname3, join as
|
|
5772
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
5682
5773
|
function resolveDaemonPaths(options) {
|
|
5683
|
-
const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) :
|
|
5774
|
+
const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : join5(coreHomeDir(options.home), "runtime"));
|
|
5684
5775
|
return {
|
|
5685
5776
|
runtimeDir,
|
|
5686
|
-
pidFile:
|
|
5687
|
-
statusFile:
|
|
5688
|
-
stdoutLog:
|
|
5689
|
-
stderrLog:
|
|
5690
|
-
appLog:
|
|
5777
|
+
pidFile: join5(runtimeDir, "daemon.pid"),
|
|
5778
|
+
statusFile: join5(runtimeDir, "status.json"),
|
|
5779
|
+
stdoutLog: join5(runtimeDir, "stdout.log"),
|
|
5780
|
+
stderrLog: join5(runtimeDir, "stderr.log"),
|
|
5781
|
+
appLog: join5(runtimeDir, "app.log")
|
|
5691
5782
|
};
|
|
5692
5783
|
}
|
|
5693
5784
|
function resolveRuntimeDirFromConfigPath(configPath) {
|
|
5694
|
-
return
|
|
5785
|
+
return join5(dirname3(configPath), "runtime");
|
|
5695
5786
|
}
|
|
5696
5787
|
function resolveDaemonOrchestrationSocketPath(runtimeDir, platform = process.platform) {
|
|
5697
5788
|
return resolveOrchestrationEndpoint(runtimeDir, platform).path;
|
|
@@ -13109,6 +13200,14 @@ class ScheduledTaskService {
|
|
|
13109
13200
|
listPending(chatKey) {
|
|
13110
13201
|
return this.listPendingAllChats().filter((task) => task.chat_key === chatKey);
|
|
13111
13202
|
}
|
|
13203
|
+
listRecentForChat(chatKey, opts) {
|
|
13204
|
+
const terminalLimit = opts?.terminalLimit ?? 20;
|
|
13205
|
+
const mine = Object.values(this.state.scheduled_tasks).filter((task) => task.chat_key === chatKey);
|
|
13206
|
+
const upcoming = mine.filter((task) => task.status === "pending" || task.status === "triggering").sort((left, right) => left.execute_at.localeCompare(right.execute_at));
|
|
13207
|
+
const settledAt = (task) => task.executed_at ?? task.failed_at ?? task.cancelled_at ?? task.missed_at ?? task.triggered_at ?? task.created_at;
|
|
13208
|
+
const terminal = mine.filter((task) => task.status === "executed" || task.status === "failed" || task.status === "missed" || task.status === "cancelled").sort((left, right) => settledAt(right).localeCompare(settledAt(left))).slice(0, terminalLimit);
|
|
13209
|
+
return [...upcoming, ...terminal];
|
|
13210
|
+
}
|
|
13112
13211
|
listPendingAllChats() {
|
|
13113
13212
|
return Object.values(this.state.scheduled_tasks).filter((task) => task.status === "pending").sort((left, right) => left.execute_at.localeCompare(right.execute_at));
|
|
13114
13213
|
}
|
|
@@ -13232,14 +13331,14 @@ var init_scheduled_service = () => {};
|
|
|
13232
13331
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
13233
13332
|
import { copyFile, mkdir as mkdir4, readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
|
|
13234
13333
|
import { homedir as homedir3 } from "node:os";
|
|
13235
|
-
import { dirname as dirname4, join as
|
|
13334
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
13236
13335
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
13237
13336
|
function resolveCoreRoot() {
|
|
13238
13337
|
try {
|
|
13239
13338
|
let dir = dirname4(fileURLToPath2(import.meta.url));
|
|
13240
13339
|
for (let depth = 0;depth < 12; depth++) {
|
|
13241
13340
|
try {
|
|
13242
|
-
const pkg = JSON.parse(readFileSync2(
|
|
13341
|
+
const pkg = JSON.parse(readFileSync2(join7(dir, "package.json"), "utf-8"));
|
|
13243
13342
|
if (pkg.name && CORE_ROOT_NAMES.includes(pkg.name))
|
|
13244
13343
|
return dir;
|
|
13245
13344
|
} catch {}
|
|
@@ -13257,10 +13356,10 @@ async function ensureCoreResolution(pluginHome) {
|
|
|
13257
13356
|
const root = resolveCoreRoot();
|
|
13258
13357
|
if (!root)
|
|
13259
13358
|
return;
|
|
13260
|
-
const srcJs =
|
|
13359
|
+
const srcJs = join7(root, "dist", "plugin-api.js");
|
|
13261
13360
|
for (const name of SHIM_SPECIFIERS) {
|
|
13262
|
-
const targetDir =
|
|
13263
|
-
const dstJs =
|
|
13361
|
+
const targetDir = join7(pluginHome, "node_modules", name);
|
|
13362
|
+
const dstJs = join7(targetDir, "plugin-api.js");
|
|
13264
13363
|
await mkdir4(targetDir, { recursive: true });
|
|
13265
13364
|
try {
|
|
13266
13365
|
await copyFile(srcJs, dstJs);
|
|
@@ -13269,7 +13368,7 @@ async function ensureCoreResolution(pluginHome) {
|
|
|
13269
13368
|
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.`);
|
|
13270
13369
|
continue;
|
|
13271
13370
|
}
|
|
13272
|
-
await writeFile4(
|
|
13371
|
+
await writeFile4(join7(targetDir, "package.json"), JSON.stringify({
|
|
13273
13372
|
name,
|
|
13274
13373
|
type: "module",
|
|
13275
13374
|
exports: {
|
|
@@ -13298,10 +13397,10 @@ function resolvePluginHome(input = {}) {
|
|
|
13298
13397
|
if (envOverride)
|
|
13299
13398
|
return envOverride;
|
|
13300
13399
|
const home = coerceMissing(input.home) ?? coerceMissing(process.env.HOME) ?? homedir3();
|
|
13301
|
-
return
|
|
13400
|
+
return join7(coreHomeDir(home), "plugins");
|
|
13302
13401
|
}
|
|
13303
13402
|
async function normalizePluginHomeManifest(pluginHome) {
|
|
13304
|
-
const manifestPath =
|
|
13403
|
+
const manifestPath = join7(pluginHome, "package.json");
|
|
13305
13404
|
let raw;
|
|
13306
13405
|
try {
|
|
13307
13406
|
raw = await readFile7(manifestPath, "utf8");
|
|
@@ -13323,7 +13422,7 @@ async function normalizePluginHomeManifest(pluginHome) {
|
|
|
13323
13422
|
}
|
|
13324
13423
|
async function ensurePluginHome(pluginHome) {
|
|
13325
13424
|
await mkdir4(pluginHome, { recursive: true, mode: 448 });
|
|
13326
|
-
await writeFile4(
|
|
13425
|
+
await writeFile4(join7(pluginHome, "package.json"), JSON.stringify({ private: true, type: "module" }, null, 2) + `
|
|
13327
13426
|
`, { flag: "wx" }).catch((error2) => {
|
|
13328
13427
|
if (error2.code !== "EEXIST")
|
|
13329
13428
|
throw error2;
|
|
@@ -17373,7 +17472,7 @@ function normalizeMediaArray(media) {
|
|
|
17373
17472
|
|
|
17374
17473
|
// src/logging/rotating-file-writer.ts
|
|
17375
17474
|
import { readdir as readdir2, rename as rename2, rm as rm6, stat as stat2 } from "node:fs/promises";
|
|
17376
|
-
import { basename, dirname as dirname5, join as
|
|
17475
|
+
import { basename, dirname as dirname5, join as join9 } from "node:path";
|
|
17377
17476
|
async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
17378
17477
|
let currentSize = 0;
|
|
17379
17478
|
try {
|
|
@@ -17423,7 +17522,7 @@ async function cleanupExpiredRotatedLogs(filePath, retentionDays, now) {
|
|
|
17423
17522
|
if (!file.startsWith(prefix) || !/^\d+$/.test(file.slice(prefix.length))) {
|
|
17424
17523
|
continue;
|
|
17425
17524
|
}
|
|
17426
|
-
const candidate =
|
|
17525
|
+
const candidate = join9(parentDir, file);
|
|
17427
17526
|
let details;
|
|
17428
17527
|
try {
|
|
17429
17528
|
details = await stat2(candidate);
|
|
@@ -19101,10 +19200,10 @@ var init_scheduled_turn = __esm(() => {
|
|
|
19101
19200
|
|
|
19102
19201
|
// src/weixin/monitor/consumer-lock.ts
|
|
19103
19202
|
import { mkdir as mkdir6, open as open3, readFile as readFile8, rm as rm7 } from "node:fs/promises";
|
|
19104
|
-
import { dirname as dirname7, join as
|
|
19203
|
+
import { dirname as dirname7, join as join10 } from "node:path";
|
|
19105
19204
|
import { homedir as homedir4 } from "node:os";
|
|
19106
19205
|
function createWeixinConsumerLock(options = {}) {
|
|
19107
|
-
const lockFilePath = options.lockFilePath ??
|
|
19206
|
+
const lockFilePath = options.lockFilePath ?? join10(coreHomeDir(homedir4()), "runtime", "weixin-consumer.lock.json");
|
|
19108
19207
|
const isProcessRunning = options.isProcessRunning ?? defaultIsProcessRunning4;
|
|
19109
19208
|
const onDiagnostic = options.onDiagnostic;
|
|
19110
19209
|
return {
|
|
@@ -19778,9 +19877,9 @@ __export(exports_plugin_loader, {
|
|
|
19778
19877
|
});
|
|
19779
19878
|
import { createRequire as createRequire2 } from "node:module";
|
|
19780
19879
|
import { pathToFileURL } from "node:url";
|
|
19781
|
-
import { join as
|
|
19880
|
+
import { join as join11 } from "node:path";
|
|
19782
19881
|
async function importPluginFromHome(packageName, pluginHome) {
|
|
19783
|
-
const requireFromHome = createRequire2(
|
|
19882
|
+
const requireFromHome = createRequire2(join11(pluginHome, "package.json"));
|
|
19784
19883
|
const entry = requireFromHome.resolve(packageName);
|
|
19785
19884
|
return await import(pathToFileURL(entry).href);
|
|
19786
19885
|
}
|
|
@@ -19825,13 +19924,13 @@ var init_plugin_loader = __esm(() => {
|
|
|
19825
19924
|
|
|
19826
19925
|
// src/plugins/plugin-doctor.ts
|
|
19827
19926
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
19828
|
-
import { join as
|
|
19927
|
+
import { join as join13 } from "node:path";
|
|
19829
19928
|
function suggestedPluginPackageForChannel(type) {
|
|
19830
19929
|
return findKnownPluginByChannel(type)?.packageName ?? `<npm-package-that-provides-${type}>`;
|
|
19831
19930
|
}
|
|
19832
19931
|
async function readDependencyEntries(pluginHome) {
|
|
19833
19932
|
try {
|
|
19834
|
-
const raw = await readFile10(
|
|
19933
|
+
const raw = await readFile10(join13(pluginHome, "package.json"), "utf8");
|
|
19835
19934
|
const parsed = JSON.parse(raw);
|
|
19836
19935
|
const out = {};
|
|
19837
19936
|
for (const [name, value] of Object.entries(parsed.dependencies ?? {})) {
|
|
@@ -20265,6 +20364,8 @@ function parseCommand(input) {
|
|
|
20265
20364
|
return { kind: "session.reset" };
|
|
20266
20365
|
if (command === "/mode" && parts.length === 1)
|
|
20267
20366
|
return { kind: "mode.show" };
|
|
20367
|
+
if (command === "/model" && parts.length === 1)
|
|
20368
|
+
return { kind: "model.show" };
|
|
20268
20369
|
if (command === "/replymode" && parts.length === 1)
|
|
20269
20370
|
return { kind: "replymode.show" };
|
|
20270
20371
|
if (command === "/config" && parts.length === 1)
|
|
@@ -20403,6 +20504,9 @@ function parseCommand(input) {
|
|
|
20403
20504
|
if (command === "/mode" && parts[1]) {
|
|
20404
20505
|
return { kind: "mode.set", modeId: parts[1] };
|
|
20405
20506
|
}
|
|
20507
|
+
if (command === "/model" && parts[1]) {
|
|
20508
|
+
return { kind: "model.set", modelId: parts.slice(1).join(" ") };
|
|
20509
|
+
}
|
|
20406
20510
|
if (command === "/replymode" && parts[1] === "reset" && parts.length === 2) {
|
|
20407
20511
|
return { kind: "replymode.reset" };
|
|
20408
20512
|
}
|
|
@@ -20410,7 +20514,14 @@ function parseCommand(input) {
|
|
|
20410
20514
|
return { kind: "replymode.set", replyMode: parts[1] };
|
|
20411
20515
|
}
|
|
20412
20516
|
if (command === "/agent" && parts[1] === "add" && parts[2]) {
|
|
20413
|
-
|
|
20517
|
+
let model = "";
|
|
20518
|
+
for (let index = 3;index < parts.length; index += 1) {
|
|
20519
|
+
if ((parts[index] === "--model" || parts[index] === "-m") && index + 1 < parts.length) {
|
|
20520
|
+
model = parts[index + 1] ?? "";
|
|
20521
|
+
index += 1;
|
|
20522
|
+
}
|
|
20523
|
+
}
|
|
20524
|
+
return model.trim().length > 0 ? { kind: "agent.add", template: parts[2], model: model.trim() } : { kind: "agent.add", template: parts[2] };
|
|
20414
20525
|
}
|
|
20415
20526
|
if (command === "/agent" && parts[1] === "rm" && parts[2]) {
|
|
20416
20527
|
return { kind: "agent.rm", name: parts[2] };
|
|
@@ -20488,6 +20599,7 @@ function parseCommand(input) {
|
|
|
20488
20599
|
const alias = parts[2];
|
|
20489
20600
|
let agent3 = "";
|
|
20490
20601
|
let workspace3 = "";
|
|
20602
|
+
let model = "";
|
|
20491
20603
|
let invalid = false;
|
|
20492
20604
|
for (let index = 3;index < parts.length; index += 1) {
|
|
20493
20605
|
if (parts[index] === "--agent" || parts[index] === "-a") {
|
|
@@ -20506,12 +20618,20 @@ function parseCommand(input) {
|
|
|
20506
20618
|
workspace3 = parts[index + 1] ?? "";
|
|
20507
20619
|
index += 1;
|
|
20508
20620
|
continue;
|
|
20621
|
+
} else if (parts[index] === "--model" || parts[index] === "-m") {
|
|
20622
|
+
if (index + 1 >= parts.length) {
|
|
20623
|
+
invalid = true;
|
|
20624
|
+
break;
|
|
20625
|
+
}
|
|
20626
|
+
model = parts[index + 1] ?? "";
|
|
20627
|
+
index += 1;
|
|
20628
|
+
continue;
|
|
20509
20629
|
}
|
|
20510
20630
|
invalid = true;
|
|
20511
20631
|
break;
|
|
20512
20632
|
}
|
|
20513
20633
|
if (!invalid && alias.trim().length > 0 && agent3.trim().length > 0 && workspace3.trim().length > 0) {
|
|
20514
|
-
return { kind: "session.new", alias, agent: agent3, workspace: workspace3 };
|
|
20634
|
+
return model.trim().length > 0 ? { kind: "session.new", alias, agent: agent3, workspace: workspace3, model: model.trim() } : { kind: "session.new", alias, agent: agent3, workspace: workspace3 };
|
|
20515
20635
|
}
|
|
20516
20636
|
}
|
|
20517
20637
|
const shortcutTarget = readSessionShortcutTarget(parts, 3);
|
|
@@ -20967,6 +21087,7 @@ var init_command_policy = __esm(() => {
|
|
|
20967
21087
|
"session.tail",
|
|
20968
21088
|
"status",
|
|
20969
21089
|
"mode.show",
|
|
21090
|
+
"model.show",
|
|
20970
21091
|
"replymode.show",
|
|
20971
21092
|
"config.show",
|
|
20972
21093
|
"permission.status",
|
|
@@ -20986,6 +21107,7 @@ var init_command_policy = __esm(() => {
|
|
|
20986
21107
|
"replymode.set": "/replymode",
|
|
20987
21108
|
"replymode.reset": "/replymode reset",
|
|
20988
21109
|
"mode.set": "/mode",
|
|
21110
|
+
"model.set": "/model",
|
|
20989
21111
|
"permission.mode.set": "/permission",
|
|
20990
21112
|
"permission.auto.set": "/permission auto",
|
|
20991
21113
|
"config.set": "/config set",
|
|
@@ -21772,6 +21894,19 @@ function modeHelp() {
|
|
|
21772
21894
|
examples: ["/mode", "/mode plan"]
|
|
21773
21895
|
};
|
|
21774
21896
|
}
|
|
21897
|
+
function modelHelp() {
|
|
21898
|
+
const s = t().session;
|
|
21899
|
+
return {
|
|
21900
|
+
topic: "model",
|
|
21901
|
+
aliases: [],
|
|
21902
|
+
summary: s.modelHelpSummary,
|
|
21903
|
+
commands: [
|
|
21904
|
+
{ usage: s.modelHelpCmdShow, description: s.modelHelpCmdShowDesc },
|
|
21905
|
+
{ usage: s.modelHelpCmdSet, description: s.modelHelpCmdSetDesc }
|
|
21906
|
+
],
|
|
21907
|
+
examples: ["/model", "/model gpt-5.2[high]"]
|
|
21908
|
+
};
|
|
21909
|
+
}
|
|
21775
21910
|
function replyModeHelp() {
|
|
21776
21911
|
const s = t().session;
|
|
21777
21912
|
return {
|
|
@@ -21845,7 +21980,7 @@ async function handleSessions(context, chatKey) {
|
|
|
21845
21980
|
`)
|
|
21846
21981
|
};
|
|
21847
21982
|
}
|
|
21848
|
-
async function handleSessionNew(context, chatKey, alias, agent3, workspace3) {
|
|
21983
|
+
async function handleSessionNew(context, chatKey, alias, agent3, workspace3, model) {
|
|
21849
21984
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
21850
21985
|
const internalAlias = scopeDisplayAliasToInternal(channelId, alias);
|
|
21851
21986
|
const existing = context.sessions.getResolvedSessionByInternalAlias(internalAlias);
|
|
@@ -21853,6 +21988,10 @@ async function handleSessionNew(context, chatKey, alias, agent3, workspace3) {
|
|
|
21853
21988
|
return { text: t().session.sessionAlreadyExists(alias, existing.agent, existing.workspace) };
|
|
21854
21989
|
}
|
|
21855
21990
|
const session3 = context.lifecycle.resolveSession(internalAlias, agent3, workspace3, `${workspace3}:${internalAlias}`);
|
|
21991
|
+
const normalizedModel = model?.trim();
|
|
21992
|
+
if (normalizedModel) {
|
|
21993
|
+
session3.model = normalizedModel;
|
|
21994
|
+
}
|
|
21856
21995
|
const releaseTransportReservation = await context.lifecycle.reserveTransportSession(session3.transportSession);
|
|
21857
21996
|
try {
|
|
21858
21997
|
try {
|
|
@@ -21865,6 +22004,9 @@ async function handleSessionNew(context, chatKey, alias, agent3, workspace3) {
|
|
|
21865
22004
|
return context.recovery.renderSessionCreationError(session3, error2);
|
|
21866
22005
|
}
|
|
21867
22006
|
await context.sessions.attachSession(internalAlias, agent3, workspace3, session3.transportSession);
|
|
22007
|
+
if (normalizedModel) {
|
|
22008
|
+
await context.sessions.setSessionModel(internalAlias, normalizedModel);
|
|
22009
|
+
}
|
|
21868
22010
|
await context.sessions.useSession(chatKey, internalAlias);
|
|
21869
22011
|
await refreshSessionTransportAgentCommandBestEffort(context, internalAlias, "session.agent_command_refresh_failed");
|
|
21870
22012
|
await context.logger.info("session.created", "created and selected logical session", {
|
|
@@ -21991,6 +22133,43 @@ async function handleModeSet(context, chatKey, modeId) {
|
|
|
21991
22133
|
await context.sessions.setCurrentSessionMode(chatKey, modeId);
|
|
21992
22134
|
return { text: t().session.modeSet(modeId) };
|
|
21993
22135
|
}
|
|
22136
|
+
async function handleModelShow(context, chatKey) {
|
|
22137
|
+
const session3 = await context.sessions.getCurrentSession(chatKey);
|
|
22138
|
+
if (!session3) {
|
|
22139
|
+
return { text: t().session.noCurrent };
|
|
22140
|
+
}
|
|
22141
|
+
const s = t().session;
|
|
22142
|
+
let current = session3.model;
|
|
22143
|
+
let available = [];
|
|
22144
|
+
try {
|
|
22145
|
+
const queried = await context.interaction.getModelTransportSession(session3);
|
|
22146
|
+
current = queried.current ?? session3.model;
|
|
22147
|
+
available = queried.available;
|
|
22148
|
+
} catch {}
|
|
22149
|
+
const lines = [
|
|
22150
|
+
s.modelHeader,
|
|
22151
|
+
s.modelSessionLabel(toDisplaySessionAlias(session3.alias)),
|
|
22152
|
+
s.modelModelLabel(current ?? s.modelNotSet)
|
|
22153
|
+
];
|
|
22154
|
+
if (available.length > 0) {
|
|
22155
|
+
lines.push(s.modelAvailableLabel(available.join(", ")));
|
|
22156
|
+
}
|
|
22157
|
+
return { text: lines.join(`
|
|
22158
|
+
`) };
|
|
22159
|
+
}
|
|
22160
|
+
async function handleModelSet(context, chatKey, modelId) {
|
|
22161
|
+
const session3 = await context.sessions.getCurrentSession(chatKey);
|
|
22162
|
+
if (!session3) {
|
|
22163
|
+
return { text: t().session.noCurrent };
|
|
22164
|
+
}
|
|
22165
|
+
try {
|
|
22166
|
+
await context.interaction.setModelTransportSession(session3, modelId);
|
|
22167
|
+
} catch (error2) {
|
|
22168
|
+
return { text: t().session.modelSetFailed(modelId, error2 instanceof Error ? error2.message : String(error2)) };
|
|
22169
|
+
}
|
|
22170
|
+
await context.sessions.setCurrentSessionModel(chatKey, modelId);
|
|
22171
|
+
return { text: t().session.modelSet(modelId) };
|
|
22172
|
+
}
|
|
21994
22173
|
async function handleReplyModeShow(context, chatKey) {
|
|
21995
22174
|
const session3 = await context.sessions.getCurrentSession(chatKey);
|
|
21996
22175
|
if (!session3) {
|
|
@@ -22176,7 +22355,7 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
22176
22355
|
return { text: lines.join(`
|
|
22177
22356
|
`) };
|
|
22178
22357
|
}
|
|
22179
|
-
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
22358
|
+
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22180
22359
|
const effectiveReplyMode = resolveEffectiveReplyMode(context.config, chatKey, session3.replyMode);
|
|
22181
22360
|
if (!session3.replyMode)
|
|
22182
22361
|
session3.replyMode = effectiveReplyMode;
|
|
@@ -22208,7 +22387,7 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
|
|
|
22208
22387
|
const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session3, chatKey, text, replyContextToken, accountId);
|
|
22209
22388
|
try {
|
|
22210
22389
|
const replyContext = transportReply && context.quota && getChannelIdFromChatKey(chatKey) === "weixin" ? { chatKey, quota: context.quota } : undefined;
|
|
22211
|
-
const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan);
|
|
22390
|
+
const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage);
|
|
22212
22391
|
if (claimHumanReply) {
|
|
22213
22392
|
try {
|
|
22214
22393
|
await context.orchestration?.claimActiveHumanReply?.(claimHumanReply);
|
|
@@ -22228,23 +22407,23 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
|
|
|
22228
22407
|
throw error2;
|
|
22229
22408
|
}
|
|
22230
22409
|
}
|
|
22231
|
-
async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
22410
|
+
async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22232
22411
|
try {
|
|
22233
|
-
return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
22412
|
+
return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22234
22413
|
} catch (error2) {
|
|
22235
22414
|
const recovered = await context.recovery.tryRecoverMissingSession(session3, error2);
|
|
22236
22415
|
if (recovered) {
|
|
22237
|
-
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
22416
|
+
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22238
22417
|
}
|
|
22239
22418
|
return context.recovery.renderTransportError(session3, error2);
|
|
22240
22419
|
}
|
|
22241
22420
|
}
|
|
22242
|
-
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
22421
|
+
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22243
22422
|
const session3 = metadata?.boundSessionAlias ? context.sessions.getResolvedSessionByInternalAlias(metadata.boundSessionAlias) : await context.sessions.getCurrentSession(chatKey);
|
|
22244
22423
|
if (!session3) {
|
|
22245
22424
|
return { text: t().session.noCurrent };
|
|
22246
22425
|
}
|
|
22247
|
-
return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
22426
|
+
return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22248
22427
|
}
|
|
22249
22428
|
function toCoordinatorRouteChatMetadata(metadata) {
|
|
22250
22429
|
if (!metadata) {
|
|
@@ -22917,7 +23096,7 @@ function agentHelp() {
|
|
|
22917
23096
|
function handleAgents(context) {
|
|
22918
23097
|
return { text: context.config ? renderAgents(context.config) : "No config loaded." };
|
|
22919
23098
|
}
|
|
22920
|
-
async function handleAgentAdd(context, templateName) {
|
|
23099
|
+
async function handleAgentAdd(context, templateName, model) {
|
|
22921
23100
|
const a = t().agent;
|
|
22922
23101
|
if (!context.config || !context.configStore) {
|
|
22923
23102
|
return { text: a.noWritableConfig };
|
|
@@ -22927,13 +23106,18 @@ async function handleAgentAdd(context, templateName) {
|
|
|
22927
23106
|
return { text: a.unsupportedTemplate(listAgentTemplates().join("、")) };
|
|
22928
23107
|
}
|
|
22929
23108
|
const existing = context.config.agents[templateName];
|
|
23109
|
+
const normalizedModel = model?.trim();
|
|
23110
|
+
const desired = normalizedModel ? { ...template, model: normalizedModel } : existing?.model ? { ...template, model: existing.model } : template;
|
|
22930
23111
|
if (existing) {
|
|
22931
|
-
if (sameAgentConfig(existing,
|
|
22932
|
-
|
|
23112
|
+
if (sameAgentConfig(existing, desired)) {
|
|
23113
|
+
if ((existing.model ?? undefined) === (desired.model ?? undefined)) {
|
|
23114
|
+
return { text: a.alreadyExists(templateName) };
|
|
23115
|
+
}
|
|
23116
|
+
} else {
|
|
23117
|
+
return { text: a.alreadyExistsDifferent(templateName) };
|
|
22933
23118
|
}
|
|
22934
|
-
return { text: a.alreadyExistsDifferent(templateName) };
|
|
22935
23119
|
}
|
|
22936
|
-
const updated = await context.configStore.upsertAgent(templateName,
|
|
23120
|
+
const updated = await context.configStore.upsertAgent(templateName, desired);
|
|
22937
23121
|
context.replaceConfig(updated);
|
|
22938
23122
|
return { text: a.saved(templateName) };
|
|
22939
23123
|
}
|
|
@@ -23297,6 +23481,7 @@ function buildHelpTopics() {
|
|
|
23297
23481
|
configHelp(),
|
|
23298
23482
|
orchestrationHelp(),
|
|
23299
23483
|
modeHelp(),
|
|
23484
|
+
modelHelp(),
|
|
23300
23485
|
replyModeHelp(),
|
|
23301
23486
|
statusHelp(),
|
|
23302
23487
|
cancelHelp(),
|
|
@@ -23727,7 +23912,7 @@ async function resolveNativeTarget(context, chatKey, input) {
|
|
|
23727
23912
|
return {
|
|
23728
23913
|
agent: agent3,
|
|
23729
23914
|
agentDisplayName: displayAgentName(agent3),
|
|
23730
|
-
agentCommand:
|
|
23915
|
+
agentCommand: resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, context.config?.transport.preferLocalAgents !== false),
|
|
23731
23916
|
workspace: workspaceResolution.workspace,
|
|
23732
23917
|
workspaceLabel: workspaceResolution.workspaceLabel,
|
|
23733
23918
|
cwd: workspaceResolution.cwd,
|
|
@@ -23965,6 +24150,7 @@ function displayAgentName(agent3) {
|
|
|
23965
24150
|
}
|
|
23966
24151
|
var NATIVE_SESSION_CACHE_TTL_MS;
|
|
23967
24152
|
var init_native_session_handler = __esm(() => {
|
|
24153
|
+
init_resolve_agent_command();
|
|
23968
24154
|
init_channel_scope();
|
|
23969
24155
|
init_workspace_name();
|
|
23970
24156
|
init_workspace_path();
|
|
@@ -24084,7 +24270,7 @@ import { spawn as spawn5 } from "node:child_process";
|
|
|
24084
24270
|
import { createWriteStream } from "node:fs";
|
|
24085
24271
|
import { mkdir as mkdir8 } from "node:fs/promises";
|
|
24086
24272
|
import { homedir as homedir6 } from "node:os";
|
|
24087
|
-
import { join as
|
|
24273
|
+
import { join as join15 } from "node:path";
|
|
24088
24274
|
async function autoInstallOptionalDep(pkg, parentPackages, options = {}) {
|
|
24089
24275
|
const runCli = options.runCli ?? defaultRunCli;
|
|
24090
24276
|
const openLog = options.openLog ?? defaultLogSink;
|
|
@@ -24199,10 +24385,10 @@ ${err.message}`, reason: "spawn" });
|
|
|
24199
24385
|
});
|
|
24200
24386
|
});
|
|
24201
24387
|
}, defaultLogSink = async () => {
|
|
24202
|
-
const dir =
|
|
24388
|
+
const dir = join15(coreHomeDir(homedir6()), "logs");
|
|
24203
24389
|
await mkdir8(dir, { recursive: true });
|
|
24204
24390
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "").replace(/-/g, "");
|
|
24205
|
-
const path14 =
|
|
24391
|
+
const path14 = join15(dir, `auto-install-${timestamp}.log`);
|
|
24206
24392
|
const stream = createWriteStream(path14, { flags: "a" });
|
|
24207
24393
|
return {
|
|
24208
24394
|
path: path14,
|
|
@@ -24229,7 +24415,7 @@ import { spawn as spawn6 } from "node:child_process";
|
|
|
24229
24415
|
import { createRequire as createRequire3 } from "node:module";
|
|
24230
24416
|
import { access as access3 } from "node:fs/promises";
|
|
24231
24417
|
import { homedir as homedir7 } from "node:os";
|
|
24232
|
-
import { dirname as dirname10, join as
|
|
24418
|
+
import { dirname as dirname10, join as join16 } from "node:path";
|
|
24233
24419
|
function deriveParentPackageName(platformPackage) {
|
|
24234
24420
|
return platformPackage.replace(/-(?:linux|darwin|win32|windows|freebsd|openbsd|sunos|aix)(?:-(?:x64|arm64|ia32|arm|ppc64|s390x))?(?:-(?:baseline|musl|gnu|gnueabihf|musleabihf|msvc))?$/, "");
|
|
24235
24421
|
}
|
|
@@ -24242,7 +24428,7 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
|
|
|
24242
24428
|
const queryRoot = deps.queryPackageManagerRoot ?? defaultQueryPackageManagerRoot;
|
|
24243
24429
|
const parentName = deriveParentPackageName(platformPackage);
|
|
24244
24430
|
const rawCandidates = [];
|
|
24245
|
-
const bunGlobalRoot = env.BUN_INSTALL ?
|
|
24431
|
+
const bunGlobalRoot = env.BUN_INSTALL ? join16(env.BUN_INSTALL, "install", "global", "node_modules") : join16(home, ".bun", "install", "global", "node_modules");
|
|
24246
24432
|
const [npmRoot, pnpmRoot, yarnRoot] = await Promise.all([
|
|
24247
24433
|
queryRoot("npm"),
|
|
24248
24434
|
queryRoot("pnpm"),
|
|
@@ -24265,20 +24451,20 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
|
|
|
24265
24451
|
if (resolved)
|
|
24266
24452
|
rawCandidates.push({ path: resolved, manager: classify(resolved) });
|
|
24267
24453
|
}
|
|
24268
|
-
rawCandidates.push({ path:
|
|
24454
|
+
rawCandidates.push({ path: join16(bunGlobalRoot, parentName), manager: "bun" });
|
|
24269
24455
|
if (npmRoot)
|
|
24270
|
-
rawCandidates.push({ path:
|
|
24456
|
+
rawCandidates.push({ path: join16(npmRoot, parentName), manager: "npm" });
|
|
24271
24457
|
if (pnpmRoot)
|
|
24272
|
-
rawCandidates.push({ path:
|
|
24458
|
+
rawCandidates.push({ path: join16(pnpmRoot, parentName), manager: "pnpm" });
|
|
24273
24459
|
if (yarnRoot)
|
|
24274
|
-
rawCandidates.push({ path:
|
|
24460
|
+
rawCandidates.push({ path: join16(yarnRoot, parentName), manager: "yarn" });
|
|
24275
24461
|
const seen = new Set;
|
|
24276
24462
|
const verified = [];
|
|
24277
24463
|
for (const candidate of rawCandidates) {
|
|
24278
24464
|
if (seen.has(candidate.path))
|
|
24279
24465
|
continue;
|
|
24280
24466
|
seen.add(candidate.path);
|
|
24281
|
-
if (await fsExists(
|
|
24467
|
+
if (await fsExists(join16(candidate.path, "package.json"))) {
|
|
24282
24468
|
verified.push(candidate);
|
|
24283
24469
|
}
|
|
24284
24470
|
}
|
|
@@ -24349,7 +24535,7 @@ async function defaultQueryPackageManagerRoot(tool) {
|
|
|
24349
24535
|
const trimmed = stdout2.trim().split(/\r?\n/).pop()?.trim() ?? "";
|
|
24350
24536
|
if (!trimmed)
|
|
24351
24537
|
return done(null);
|
|
24352
|
-
done(spec.postfix ?
|
|
24538
|
+
done(spec.postfix ? join16(trimmed, spec.postfix) : trimmed);
|
|
24353
24539
|
});
|
|
24354
24540
|
});
|
|
24355
24541
|
}
|
|
@@ -24496,9 +24682,12 @@ class CommandRouter {
|
|
|
24496
24682
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
24497
24683
|
this.activeTurns = activeTurns;
|
|
24498
24684
|
}
|
|
24499
|
-
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
24685
|
+
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
|
|
24500
24686
|
const startedAt = Date.now();
|
|
24501
|
-
|
|
24687
|
+
let command = parseCommand(input);
|
|
24688
|
+
if (metadata?.channel === "control" && command.kind !== "prompt") {
|
|
24689
|
+
command = { kind: "prompt", text: input.trim() };
|
|
24690
|
+
}
|
|
24502
24691
|
await this.logger.debug("command.parsed", "parsed inbound command", {
|
|
24503
24692
|
chatKey,
|
|
24504
24693
|
kind: command.kind
|
|
@@ -24535,7 +24724,7 @@ class CommandRouter {
|
|
|
24535
24724
|
case "agents":
|
|
24536
24725
|
return handleAgents(this.createHandlerContext());
|
|
24537
24726
|
case "agent.add":
|
|
24538
|
-
return await handleAgentAdd(this.createHandlerContext(), command.template);
|
|
24727
|
+
return await handleAgentAdd(this.createHandlerContext(), command.template, command.model);
|
|
24539
24728
|
case "agent.rm":
|
|
24540
24729
|
return await handleAgentRemove(this.createHandlerContext(), command.name);
|
|
24541
24730
|
case "permission.status":
|
|
@@ -24559,7 +24748,7 @@ class CommandRouter {
|
|
|
24559
24748
|
case "sessions":
|
|
24560
24749
|
return await handleSessions(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
24561
24750
|
case "session.new":
|
|
24562
|
-
return await handleSessionNew(this.createSessionHandlerContext(reply, perfSpan), chatKey, command.alias, command.agent, command.workspace);
|
|
24751
|
+
return await handleSessionNew(this.createSessionHandlerContext(reply, perfSpan), chatKey, command.alias, command.agent, command.workspace, command.model);
|
|
24563
24752
|
case "session.shortcut":
|
|
24564
24753
|
return await handleSessionShortcut(this.createSessionHandlerContext(reply, perfSpan), chatKey, command.agent, command, false);
|
|
24565
24754
|
case "session.shortcut.new":
|
|
@@ -24580,6 +24769,10 @@ class CommandRouter {
|
|
|
24580
24769
|
return await handleModeShow(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
24581
24770
|
case "mode.set":
|
|
24582
24771
|
return await handleModeSet(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.modeId);
|
|
24772
|
+
case "model.show":
|
|
24773
|
+
return await handleModelShow(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
24774
|
+
case "model.set":
|
|
24775
|
+
return await handleModelSet(this.createSessionHandlerContext(reply, perfSpan), chatKey, command.modelId);
|
|
24583
24776
|
case "replymode.show":
|
|
24584
24777
|
return await handleReplyModeShow(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
24585
24778
|
case "replymode.set":
|
|
@@ -24649,16 +24842,16 @@ class CommandRouter {
|
|
|
24649
24842
|
...this.sessions.resolveSession(descriptor.alias, descriptor.agent, descriptor.workspace, descriptor.transportSession),
|
|
24650
24843
|
transient: true
|
|
24651
24844
|
};
|
|
24652
|
-
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
24845
|
+
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24653
24846
|
}
|
|
24654
24847
|
if (metadata?.scheduledSessionAlias) {
|
|
24655
24848
|
const scheduledSession = await this.sessions.getSession(metadata.scheduledSessionAlias);
|
|
24656
24849
|
if (!scheduledSession) {
|
|
24657
24850
|
throw new Error(`session "${metadata.scheduledSessionAlias}" not found for scheduled prompt`);
|
|
24658
24851
|
}
|
|
24659
|
-
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
24852
|
+
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24660
24853
|
}
|
|
24661
|
-
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata);
|
|
24854
|
+
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24662
24855
|
}
|
|
24663
24856
|
}
|
|
24664
24857
|
});
|
|
@@ -24710,11 +24903,103 @@ class CommandRouter {
|
|
|
24710
24903
|
refreshSessionTransportAgentCommand: (alias) => this.refreshSessionTransportAgentCommand(alias)
|
|
24711
24904
|
};
|
|
24712
24905
|
}
|
|
24906
|
+
async createSessionWithTransport(internalAlias, agent3, workspace3, model) {
|
|
24907
|
+
const existing = this.sessions.getResolvedSessionByInternalAlias(internalAlias);
|
|
24908
|
+
if (existing) {
|
|
24909
|
+
throw new Error(`session "${internalAlias}" already exists`);
|
|
24910
|
+
}
|
|
24911
|
+
const session3 = this.sessions.resolveSession(internalAlias, agent3, workspace3, `${workspace3}:${internalAlias}`);
|
|
24912
|
+
const normalizedModel = model?.trim();
|
|
24913
|
+
if (normalizedModel) {
|
|
24914
|
+
session3.model = normalizedModel;
|
|
24915
|
+
}
|
|
24916
|
+
const release = await this.reserveLogicalTransportSession(session3.transportSession);
|
|
24917
|
+
try {
|
|
24918
|
+
await this.ensureTransportSession(session3);
|
|
24919
|
+
const exists = await this.checkTransportSession(session3);
|
|
24920
|
+
if (!exists) {
|
|
24921
|
+
throw new Error(`transport session "${session3.transportSession}" could not be verified`);
|
|
24922
|
+
}
|
|
24923
|
+
await this.sessions.attachSession(internalAlias, agent3, workspace3, session3.transportSession);
|
|
24924
|
+
if (normalizedModel) {
|
|
24925
|
+
await this.sessions.setSessionModel(internalAlias, normalizedModel);
|
|
24926
|
+
}
|
|
24927
|
+
try {
|
|
24928
|
+
await this.refreshSessionTransportAgentCommand(internalAlias);
|
|
24929
|
+
} catch (error2) {
|
|
24930
|
+
await this.logger.error("session.agent_command_refresh_failed", "failed to refresh session agent command", {
|
|
24931
|
+
alias: internalAlias,
|
|
24932
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
24933
|
+
});
|
|
24934
|
+
}
|
|
24935
|
+
return session3;
|
|
24936
|
+
} finally {
|
|
24937
|
+
await release();
|
|
24938
|
+
}
|
|
24939
|
+
}
|
|
24940
|
+
async listNativeSessionsForControl(agent3, workspace3) {
|
|
24941
|
+
const listAgentSessions = this.transport.listAgentSessions?.bind(this.transport);
|
|
24942
|
+
if (!listAgentSessions)
|
|
24943
|
+
return [];
|
|
24944
|
+
const agentConfig = this.config?.agents[agent3];
|
|
24945
|
+
const workspaceConfig = this.config?.workspaces[workspace3];
|
|
24946
|
+
if (!agentConfig || !workspaceConfig) {
|
|
24947
|
+
throw new Error(`unknown agent "${agent3}" or workspace "${workspace3}"`);
|
|
24948
|
+
}
|
|
24949
|
+
const agentCommand = resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, this.config?.transport.preferLocalAgents !== false);
|
|
24950
|
+
const result = await listAgentSessions({
|
|
24951
|
+
agent: agent3,
|
|
24952
|
+
...agentCommand ? { agentCommand } : {},
|
|
24953
|
+
cwd: workspaceConfig.cwd,
|
|
24954
|
+
filterCwd: workspaceConfig.cwd
|
|
24955
|
+
});
|
|
24956
|
+
return result?.sessions ?? [];
|
|
24957
|
+
}
|
|
24958
|
+
async attachNativeSessionWithTransport(internalAlias, agent3, workspace3, agentSessionId, nativeMeta) {
|
|
24959
|
+
if (!this.transport.resumeAgentSession) {
|
|
24960
|
+
throw new Error("the active transport does not support native sessions");
|
|
24961
|
+
}
|
|
24962
|
+
const existing = this.sessions.getResolvedSessionByInternalAlias(internalAlias);
|
|
24963
|
+
if (existing) {
|
|
24964
|
+
throw new Error(`session "${internalAlias}" already exists`);
|
|
24965
|
+
}
|
|
24966
|
+
const session3 = this.sessions.resolveSession(internalAlias, agent3, workspace3, `${workspace3}:${internalAlias}`);
|
|
24967
|
+
const release = await this.reserveLogicalTransportSession(session3.transportSession);
|
|
24968
|
+
try {
|
|
24969
|
+
await this.transport.resumeAgentSession(session3, agentSessionId);
|
|
24970
|
+
const exists = await this.checkTransportSession(session3);
|
|
24971
|
+
if (!exists) {
|
|
24972
|
+
throw new Error(`transport session "${session3.transportSession}" could not be verified`);
|
|
24973
|
+
}
|
|
24974
|
+
await this.sessions.attachNativeSession({
|
|
24975
|
+
alias: internalAlias,
|
|
24976
|
+
agent: agent3,
|
|
24977
|
+
workspace: workspace3,
|
|
24978
|
+
transportSession: session3.transportSession,
|
|
24979
|
+
agentSessionId,
|
|
24980
|
+
...nativeMeta?.title !== undefined ? { title: nativeMeta.title } : {},
|
|
24981
|
+
...nativeMeta?.updatedAt !== undefined ? { updatedAt: nativeMeta.updatedAt } : {}
|
|
24982
|
+
});
|
|
24983
|
+
try {
|
|
24984
|
+
await this.refreshSessionTransportAgentCommand(internalAlias);
|
|
24985
|
+
} catch (error2) {
|
|
24986
|
+
await this.logger.error("session.native.agent_command_refresh_failed", "failed to refresh native session agent command", {
|
|
24987
|
+
alias: internalAlias,
|
|
24988
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
24989
|
+
});
|
|
24990
|
+
}
|
|
24991
|
+
return session3;
|
|
24992
|
+
} finally {
|
|
24993
|
+
await release();
|
|
24994
|
+
}
|
|
24995
|
+
}
|
|
24713
24996
|
createSessionInteractionOps(perfSpan) {
|
|
24714
24997
|
return {
|
|
24715
24998
|
setModeTransportSession: (session3, modeId) => this.setModeTransportSession(session3, modeId),
|
|
24999
|
+
setModelTransportSession: (session3, modelId) => this.setModelTransportSession(session3, modelId),
|
|
25000
|
+
getModelTransportSession: (session3) => this.getModelTransportSession(session3),
|
|
24716
25001
|
cancelTransportSession: (session3) => this.cancelTransportSession(session3),
|
|
24717
|
-
promptTransportSession: (session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan)
|
|
25002
|
+
promptTransportSession: (session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride, onPlan, onUsage) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan, onPlan, onUsage)
|
|
24718
25003
|
};
|
|
24719
25004
|
}
|
|
24720
25005
|
createSessionRenderRecoveryOps() {
|
|
@@ -24893,7 +25178,7 @@ class CommandRouter {
|
|
|
24893
25178
|
async checkTransportSession(session3) {
|
|
24894
25179
|
return await this.measureTransportCall("has_session", session3, () => this.transport.hasSession(session3));
|
|
24895
25180
|
}
|
|
24896
|
-
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
25181
|
+
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
|
|
24897
25182
|
session3.mcpCoordinatorSession ??= stableCoordinatorSession(session3.transportSession);
|
|
24898
25183
|
let done = false;
|
|
24899
25184
|
let abortRequested = false;
|
|
@@ -24950,7 +25235,9 @@ class CommandRouter {
|
|
|
24950
25235
|
...media ? { media } : {},
|
|
24951
25236
|
...reply ? { onSegment } : {},
|
|
24952
25237
|
...onToolEvent ? { onToolEvent } : {},
|
|
24953
|
-
...onThought ? { onThought } : {}
|
|
25238
|
+
...onThought ? { onThought } : {},
|
|
25239
|
+
...onPlan ? { onPlan } : {},
|
|
25240
|
+
...onUsage ? { onUsage } : {}
|
|
24954
25241
|
}));
|
|
24955
25242
|
} catch (error2) {
|
|
24956
25243
|
localOutcome = isAbortError2(error2) || abortRequested ? "aborted" : "error";
|
|
@@ -24969,6 +25256,20 @@ class CommandRouter {
|
|
|
24969
25256
|
async setModeTransportSession(session3, modeId) {
|
|
24970
25257
|
return await this.measureTransportCall("set_mode", session3, () => this.transport.setMode(session3, modeId));
|
|
24971
25258
|
}
|
|
25259
|
+
async setModelTransportSession(session3, modelId) {
|
|
25260
|
+
if (!this.transport.setModel) {
|
|
25261
|
+
throw new Error("the active transport does not support switching models");
|
|
25262
|
+
}
|
|
25263
|
+
const setModel = this.transport.setModel.bind(this.transport);
|
|
25264
|
+
return await this.measureTransportCall("set_model", session3, () => setModel(session3, modelId));
|
|
25265
|
+
}
|
|
25266
|
+
async getModelTransportSession(session3) {
|
|
25267
|
+
if (!this.transport.getSessionModel) {
|
|
25268
|
+
return { current: session3.model, available: [] };
|
|
25269
|
+
}
|
|
25270
|
+
const getSessionModel = this.transport.getSessionModel.bind(this.transport);
|
|
25271
|
+
return await this.measureTransportCall("get_model", session3, () => getSessionModel(session3));
|
|
25272
|
+
}
|
|
24972
25273
|
async cancelTransportSession(session3) {
|
|
24973
25274
|
return await this.measureTransportCall("cancel", session3, () => this.transport.cancel(session3));
|
|
24974
25275
|
}
|
|
@@ -25028,6 +25329,7 @@ function inferTransportKind(transport) {
|
|
|
25028
25329
|
}
|
|
25029
25330
|
var init_command_router = __esm(() => {
|
|
25030
25331
|
init_app_logger();
|
|
25332
|
+
init_resolve_agent_command();
|
|
25031
25333
|
init_acpx_session_index();
|
|
25032
25334
|
init_prompt_output();
|
|
25033
25335
|
init_parse_command();
|
|
@@ -25121,7 +25423,7 @@ class ConsoleAgent {
|
|
|
25121
25423
|
...m.fileName ? { fileName: m.fileName } : {}
|
|
25122
25424
|
})) : undefined;
|
|
25123
25425
|
request.perfSpan?.mark("agent.dispatched");
|
|
25124
|
-
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);
|
|
25426
|
+
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, request.onPlan, request.onUsage);
|
|
25125
25427
|
}
|
|
25126
25428
|
isKnownCommand(text) {
|
|
25127
25429
|
return isKnownXacpxCommandText(text);
|
|
@@ -28935,12 +29237,14 @@ class ScheduledTaskScheduler {
|
|
|
28935
29237
|
setIntervalFn;
|
|
28936
29238
|
clearIntervalFn;
|
|
28937
29239
|
dispatchTask;
|
|
29240
|
+
onSettled;
|
|
28938
29241
|
logger;
|
|
28939
29242
|
intervalHandle = null;
|
|
28940
29243
|
ticking = false;
|
|
28941
29244
|
constructor(service, deps) {
|
|
28942
29245
|
this.service = service;
|
|
28943
29246
|
this.dispatchTask = deps.dispatchTask;
|
|
29247
|
+
this.onSettled = deps.onSettled;
|
|
28944
29248
|
this.intervalMs = deps.intervalMs ?? 5000;
|
|
28945
29249
|
this.dispatchTimeoutMs = deps.dispatchTimeoutMs ?? DEFAULT_DISPATCH_TIMEOUT_MS;
|
|
28946
29250
|
this.setIntervalFn = deps.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
|
|
@@ -28985,6 +29289,7 @@ class ScheduledTaskScheduler {
|
|
|
28985
29289
|
});
|
|
28986
29290
|
try {
|
|
28987
29291
|
await this.service.markFailed(task.id, error2);
|
|
29292
|
+
this.notifySettled(task);
|
|
28988
29293
|
} catch (markError) {
|
|
28989
29294
|
await this.logger?.error("scheduled.dispatch.mark_failed", "markFailed threw; task state may be stale", {
|
|
28990
29295
|
taskId: task.id,
|
|
@@ -28995,6 +29300,7 @@ class ScheduledTaskScheduler {
|
|
|
28995
29300
|
}
|
|
28996
29301
|
try {
|
|
28997
29302
|
await this.service.markExecuted(task.id);
|
|
29303
|
+
this.notifySettled(task);
|
|
28998
29304
|
} catch (markError) {
|
|
28999
29305
|
await this.logger?.error("scheduled.dispatch.mark_executed_failed", "markExecuted threw after a successful dispatch; leaving task state for startup reconciliation", {
|
|
29000
29306
|
taskId: task.id,
|
|
@@ -29006,6 +29312,18 @@ class ScheduledTaskScheduler {
|
|
|
29006
29312
|
this.ticking = false;
|
|
29007
29313
|
}
|
|
29008
29314
|
}
|
|
29315
|
+
notifySettled(task) {
|
|
29316
|
+
if (!this.onSettled)
|
|
29317
|
+
return;
|
|
29318
|
+
try {
|
|
29319
|
+
this.onSettled(task);
|
|
29320
|
+
} catch (error2) {
|
|
29321
|
+
this.logger?.error("scheduled.on_settled.failed", "scheduled onSettled listener threw", {
|
|
29322
|
+
taskId: task.id,
|
|
29323
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
29324
|
+
});
|
|
29325
|
+
}
|
|
29326
|
+
}
|
|
29009
29327
|
async dispatchWithTimeout(task) {
|
|
29010
29328
|
const controller = new AbortController;
|
|
29011
29329
|
let timer;
|
|
@@ -29041,7 +29359,8 @@ function buildScheduledDispatchTask(deps) {
|
|
|
29041
29359
|
};
|
|
29042
29360
|
}
|
|
29043
29361
|
async function dispatchBound(task, abortSignal, deps) {
|
|
29044
|
-
const
|
|
29362
|
+
const internalAlias = deps.resolveAliasForChat ? await deps.resolveAliasForChat(task.chat_key, task.session_alias) : task.session_alias;
|
|
29363
|
+
const session3 = await deps.getSession(internalAlias);
|
|
29045
29364
|
if (!session3) {
|
|
29046
29365
|
throw new Error(`session "${task.session_alias}" not found for scheduled task`);
|
|
29047
29366
|
}
|
|
@@ -29050,6 +29369,7 @@ async function dispatchBound(task, abortSignal, deps) {
|
|
|
29050
29369
|
chatKey: task.chat_key,
|
|
29051
29370
|
taskId: task.id,
|
|
29052
29371
|
sessionAlias: task.session_alias,
|
|
29372
|
+
executeAt: task.execute_at,
|
|
29053
29373
|
noticeText,
|
|
29054
29374
|
promptText: task.message,
|
|
29055
29375
|
abortSignal,
|
|
@@ -29070,6 +29390,7 @@ async function dispatchTemp(task, abortSignal, deps) {
|
|
|
29070
29390
|
chatKey: task.chat_key,
|
|
29071
29391
|
taskId: task.id,
|
|
29072
29392
|
sessionAlias: task.session_alias,
|
|
29393
|
+
executeAt: task.execute_at,
|
|
29073
29394
|
sessionDescriptor: { alias, agent: task.agent, workspace: task.workspace, transportSession },
|
|
29074
29395
|
noticeText,
|
|
29075
29396
|
promptText: task.message,
|
|
@@ -29259,6 +29580,7 @@ class SessionService {
|
|
|
29259
29580
|
workspace: workspace3,
|
|
29260
29581
|
transport_session: transportSession,
|
|
29261
29582
|
transport_agent_command: sameAgentExisting?.transport_agent_command,
|
|
29583
|
+
model: sameAgentExisting?.model,
|
|
29262
29584
|
created_at: existing?.created_at ?? new Date().toISOString(),
|
|
29263
29585
|
last_used_at: new Date().toISOString()
|
|
29264
29586
|
});
|
|
@@ -29629,10 +29951,13 @@ class SessionService {
|
|
|
29629
29951
|
if (!workspaceConfig) {
|
|
29630
29952
|
throw new Error(`session "${session3.alias}" references workspace "${session3.workspace}", but that workspace is no longer registered`);
|
|
29631
29953
|
}
|
|
29954
|
+
const channelId = getChannelIdFromChatKey(session3.alias);
|
|
29955
|
+
const effectiveReplyMode = channelId === "relay" ? "stream" : undefined;
|
|
29632
29956
|
return {
|
|
29633
29957
|
alias: session3.alias,
|
|
29634
29958
|
agent: session3.agent,
|
|
29635
|
-
agentCommand: session3.transport_agent_command ??
|
|
29959
|
+
agentCommand: session3.transport_agent_command ?? resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, this.config.transport.preferLocalAgents !== false),
|
|
29960
|
+
model: session3.model ?? agentConfig.model,
|
|
29636
29961
|
workspace: session3.workspace,
|
|
29637
29962
|
transportSession: session3.transport_session,
|
|
29638
29963
|
source: session3.source,
|
|
@@ -29642,9 +29967,46 @@ class SessionService {
|
|
|
29642
29967
|
attachedAt: session3.attached_at,
|
|
29643
29968
|
modeId: session3.mode_id,
|
|
29644
29969
|
replyMode: session3.reply_mode,
|
|
29970
|
+
effectiveReplyMode,
|
|
29645
29971
|
cwd: workspaceConfig.cwd
|
|
29646
29972
|
};
|
|
29647
29973
|
}
|
|
29974
|
+
async setSessionModel(alias, modelId) {
|
|
29975
|
+
await this.mutate(async () => {
|
|
29976
|
+
const session3 = this.state.sessions[alias];
|
|
29977
|
+
if (!session3) {
|
|
29978
|
+
throw new Error(`session "${alias}" does not exist`);
|
|
29979
|
+
}
|
|
29980
|
+
const normalized = modelId?.trim();
|
|
29981
|
+
if (normalized) {
|
|
29982
|
+
session3.model = normalized;
|
|
29983
|
+
} else {
|
|
29984
|
+
delete session3.model;
|
|
29985
|
+
}
|
|
29986
|
+
session3.last_used_at = new Date(this.now()).toISOString();
|
|
29987
|
+
await this.persist();
|
|
29988
|
+
});
|
|
29989
|
+
}
|
|
29990
|
+
async setCurrentSessionModel(chatKey, modelId) {
|
|
29991
|
+
await this.mutate(async () => {
|
|
29992
|
+
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
29993
|
+
if (!currentAlias) {
|
|
29994
|
+
throw new Error("no current session selected");
|
|
29995
|
+
}
|
|
29996
|
+
const session3 = this.state.sessions[currentAlias];
|
|
29997
|
+
if (!session3) {
|
|
29998
|
+
throw new Error("no current session selected");
|
|
29999
|
+
}
|
|
30000
|
+
const normalized = modelId?.trim();
|
|
30001
|
+
if (normalized) {
|
|
30002
|
+
session3.model = normalized;
|
|
30003
|
+
} else {
|
|
30004
|
+
delete session3.model;
|
|
30005
|
+
}
|
|
30006
|
+
session3.last_used_at = new Date(this.now()).toISOString();
|
|
30007
|
+
await this.persist();
|
|
30008
|
+
});
|
|
30009
|
+
}
|
|
29648
30010
|
async setSessionTransportAgentCommand(alias, transportAgentCommand) {
|
|
29649
30011
|
await this.mutate(async () => {
|
|
29650
30012
|
const session3 = this.state.sessions[alias];
|
|
@@ -29689,6 +30051,7 @@ class SessionService {
|
|
|
29689
30051
|
attached_at: native ? now : undefined,
|
|
29690
30052
|
...normalizedTransportAgentCommand ? { transport_agent_command: normalizedTransportAgentCommand } : sameAgentExisting?.transport_agent_command ? { transport_agent_command: sameAgentExisting.transport_agent_command } : {},
|
|
29691
30053
|
mode_id: sameAgentExisting?.mode_id,
|
|
30054
|
+
model: sameAgentExisting?.model,
|
|
29692
30055
|
reply_mode: sameAgentExisting?.reply_mode,
|
|
29693
30056
|
created_at: existingSession?.created_at ?? now,
|
|
29694
30057
|
last_used_at: now
|
|
@@ -29717,6 +30080,7 @@ class SessionService {
|
|
|
29717
30080
|
}
|
|
29718
30081
|
}
|
|
29719
30082
|
var init_session_service = __esm(() => {
|
|
30083
|
+
init_resolve_agent_command();
|
|
29720
30084
|
init_i18n();
|
|
29721
30085
|
init_channel_scope();
|
|
29722
30086
|
});
|
|
@@ -29740,6 +30104,13 @@ function createActiveTurnRegistry() {
|
|
|
29740
30104
|
},
|
|
29741
30105
|
isActive(chatKey, alias) {
|
|
29742
30106
|
return byChat.get(chatKey)?.has(alias) ?? false;
|
|
30107
|
+
},
|
|
30108
|
+
isActiveAnywhere(alias) {
|
|
30109
|
+
for (const set2 of byChat.values()) {
|
|
30110
|
+
if (set2.has(alias))
|
|
30111
|
+
return true;
|
|
30112
|
+
}
|
|
30113
|
+
return false;
|
|
29743
30114
|
}
|
|
29744
30115
|
};
|
|
29745
30116
|
}
|
|
@@ -29848,6 +30219,7 @@ var init_command_hints = __esm(() => {
|
|
|
29848
30219
|
config: "/config",
|
|
29849
30220
|
orchestration: "/delegate",
|
|
29850
30221
|
mode: "/mode",
|
|
30222
|
+
model: "/model",
|
|
29851
30223
|
replymode: "/replymode",
|
|
29852
30224
|
status: "/status",
|
|
29853
30225
|
cancel: "/cancel",
|
|
@@ -29966,7 +30338,8 @@ async function runConsole(paths, deps) {
|
|
|
29966
30338
|
perfTracer: runtime.perfTracer,
|
|
29967
30339
|
commandHints: listXacpxCommandHints(),
|
|
29968
30340
|
coreVersion: XACPX_CORE_VERSION,
|
|
29969
|
-
locale: getLocale()
|
|
30341
|
+
locale: getLocale(),
|
|
30342
|
+
control: runtime.control
|
|
29970
30343
|
});
|
|
29971
30344
|
channelStartPromise.catch(() => {});
|
|
29972
30345
|
let channelStartSettled = false;
|
|
@@ -30102,6 +30475,14 @@ function encodeBridgePromptThoughtEvent(event) {
|
|
|
30102
30475
|
return `${JSON.stringify(event)}
|
|
30103
30476
|
`;
|
|
30104
30477
|
}
|
|
30478
|
+
function encodeBridgePromptPlanEvent(event) {
|
|
30479
|
+
return `${JSON.stringify(event)}
|
|
30480
|
+
`;
|
|
30481
|
+
}
|
|
30482
|
+
function encodeBridgePromptUsageEvent(event) {
|
|
30483
|
+
return `${JSON.stringify(event)}
|
|
30484
|
+
`;
|
|
30485
|
+
}
|
|
30105
30486
|
function encodeBridgeSessionProgressEvent(event) {
|
|
30106
30487
|
return `${JSON.stringify(event)}
|
|
30107
30488
|
`;
|
|
@@ -30111,8 +30492,228 @@ function encodeBridgeSessionNoteEvent(event) {
|
|
|
30111
30492
|
`;
|
|
30112
30493
|
}
|
|
30113
30494
|
|
|
30114
|
-
// src/
|
|
30495
|
+
// src/process/spawn-command.ts
|
|
30496
|
+
function resolveSpawnCommand(command, args) {
|
|
30497
|
+
if (SCRIPT_FILE_PATTERN.test(command)) {
|
|
30498
|
+
return {
|
|
30499
|
+
command: process.execPath,
|
|
30500
|
+
args: [command, ...args]
|
|
30501
|
+
};
|
|
30502
|
+
}
|
|
30503
|
+
return { command, args };
|
|
30504
|
+
}
|
|
30505
|
+
var SCRIPT_FILE_PATTERN;
|
|
30506
|
+
var init_spawn_command = __esm(() => {
|
|
30507
|
+
SCRIPT_FILE_PATTERN = /\.(c|m)?js$/i;
|
|
30508
|
+
});
|
|
30509
|
+
|
|
30510
|
+
// src/transport/acpx-queue-owner-launcher.ts
|
|
30511
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
30115
30512
|
import { spawn as spawn7 } from "node:child_process";
|
|
30513
|
+
import { readFile as readFile13, unlink } from "node:fs/promises";
|
|
30514
|
+
import { homedir as homedir8 } from "node:os";
|
|
30515
|
+
import { join as join17 } from "node:path";
|
|
30516
|
+
function buildXacpxMcpServerSpec(input) {
|
|
30517
|
+
const { command, args } = splitCommandLine(input.xacpxCommand);
|
|
30518
|
+
return {
|
|
30519
|
+
name: "xacpx",
|
|
30520
|
+
type: "stdio",
|
|
30521
|
+
command,
|
|
30522
|
+
args: [
|
|
30523
|
+
...args,
|
|
30524
|
+
"mcp-stdio",
|
|
30525
|
+
"--coordinator-session",
|
|
30526
|
+
input.coordinatorSession,
|
|
30527
|
+
...input.sourceHandle ? ["--source-handle", input.sourceHandle] : ["--internal-session-tools"]
|
|
30528
|
+
]
|
|
30529
|
+
};
|
|
30530
|
+
}
|
|
30531
|
+
function buildQueueOwnerPayload(input) {
|
|
30532
|
+
return {
|
|
30533
|
+
sessionId: input.sessionId,
|
|
30534
|
+
permissionMode: input.permissionMode,
|
|
30535
|
+
nonInteractivePermissions: input.nonInteractivePermissions,
|
|
30536
|
+
ttlMs: input.ttlMs ?? 300000,
|
|
30537
|
+
maxQueueDepth: input.maxQueueDepth ?? 16,
|
|
30538
|
+
...Number.isFinite(input.promptRetries) ? { promptRetries: input.promptRetries } : {},
|
|
30539
|
+
...input.sessionOptions ? { sessionOptions: input.sessionOptions } : {},
|
|
30540
|
+
mcpServers: input.mcpServers
|
|
30541
|
+
};
|
|
30542
|
+
}
|
|
30543
|
+
|
|
30544
|
+
class AcpxQueueOwnerLauncher {
|
|
30545
|
+
acpxCommand;
|
|
30546
|
+
xacpxCommand;
|
|
30547
|
+
spawnOwner;
|
|
30548
|
+
terminateOwner;
|
|
30549
|
+
baseEnv;
|
|
30550
|
+
ttlMs;
|
|
30551
|
+
maxQueueDepth;
|
|
30552
|
+
launchLocks = new Map;
|
|
30553
|
+
constructor(options) {
|
|
30554
|
+
this.acpxCommand = options.acpxCommand;
|
|
30555
|
+
this.xacpxCommand = options.xacpxCommand ?? resolveDefaultXacpxCommand(options.baseEnv ?? process.env);
|
|
30556
|
+
this.spawnOwner = options.spawnOwner ?? defaultQueueOwnerSpawner;
|
|
30557
|
+
this.terminateOwner = options.terminateOwner ?? createDefaultQueueOwnerTerminator(options.acpxCommand);
|
|
30558
|
+
this.baseEnv = options.baseEnv ?? process.env;
|
|
30559
|
+
this.ttlMs = options.ttlMs;
|
|
30560
|
+
this.maxQueueDepth = options.maxQueueDepth;
|
|
30561
|
+
}
|
|
30562
|
+
async launch(input) {
|
|
30563
|
+
const key = input.acpxRecordId;
|
|
30564
|
+
const previous = this.launchLocks.get(key) ?? Promise.resolve();
|
|
30565
|
+
const next = previous.then(() => this.doLaunch(input), () => this.doLaunch(input));
|
|
30566
|
+
const tracked = next.catch(() => {});
|
|
30567
|
+
this.launchLocks.set(key, tracked);
|
|
30568
|
+
tracked.finally(() => {
|
|
30569
|
+
if (this.launchLocks.get(key) === tracked) {
|
|
30570
|
+
this.launchLocks.delete(key);
|
|
30571
|
+
}
|
|
30572
|
+
});
|
|
30573
|
+
return next;
|
|
30574
|
+
}
|
|
30575
|
+
async doLaunch(input) {
|
|
30576
|
+
await this.terminateOwner(input.acpxRecordId);
|
|
30577
|
+
const payload = buildQueueOwnerPayload({
|
|
30578
|
+
sessionId: input.acpxRecordId,
|
|
30579
|
+
permissionMode: input.permissionMode,
|
|
30580
|
+
nonInteractivePermissions: input.nonInteractivePermissions,
|
|
30581
|
+
ttlMs: this.ttlMs,
|
|
30582
|
+
maxQueueDepth: this.maxQueueDepth,
|
|
30583
|
+
...input.sessionOptions ? { sessionOptions: input.sessionOptions } : {},
|
|
30584
|
+
mcpServers: [buildXacpxMcpServerSpec({
|
|
30585
|
+
xacpxCommand: this.xacpxCommand,
|
|
30586
|
+
coordinatorSession: input.coordinatorSession,
|
|
30587
|
+
...input.sourceHandle ? { sourceHandle: input.sourceHandle } : {}
|
|
30588
|
+
})]
|
|
30589
|
+
});
|
|
30590
|
+
const spawnSpec = resolveSpawnCommand(this.acpxCommand, ["__queue-owner"]);
|
|
30591
|
+
await this.spawnOwner(spawnSpec.command, spawnSpec.args, {
|
|
30592
|
+
env: {
|
|
30593
|
+
...stringEnv(this.baseEnv),
|
|
30594
|
+
XACPX_LANG: getLocale(),
|
|
30595
|
+
ACPX_QUEUE_OWNER_PAYLOAD: JSON.stringify(payload)
|
|
30596
|
+
}
|
|
30597
|
+
});
|
|
30598
|
+
}
|
|
30599
|
+
}
|
|
30600
|
+
function splitCommandLine(value) {
|
|
30601
|
+
const parts = [];
|
|
30602
|
+
let current = "";
|
|
30603
|
+
let quote = null;
|
|
30604
|
+
let escaping = false;
|
|
30605
|
+
for (const char of value) {
|
|
30606
|
+
if (escaping) {
|
|
30607
|
+
current += char;
|
|
30608
|
+
escaping = false;
|
|
30609
|
+
continue;
|
|
30610
|
+
}
|
|
30611
|
+
if (char === "\\" && quote !== "'") {
|
|
30612
|
+
escaping = true;
|
|
30613
|
+
continue;
|
|
30614
|
+
}
|
|
30615
|
+
if (quote) {
|
|
30616
|
+
if (char === quote) {
|
|
30617
|
+
quote = null;
|
|
30618
|
+
} else {
|
|
30619
|
+
current += char;
|
|
30620
|
+
}
|
|
30621
|
+
continue;
|
|
30622
|
+
}
|
|
30623
|
+
if (char === "'" || char === '"') {
|
|
30624
|
+
quote = char;
|
|
30625
|
+
continue;
|
|
30626
|
+
}
|
|
30627
|
+
if (/\s/.test(char)) {
|
|
30628
|
+
if (current.length > 0) {
|
|
30629
|
+
parts.push(current);
|
|
30630
|
+
current = "";
|
|
30631
|
+
}
|
|
30632
|
+
continue;
|
|
30633
|
+
}
|
|
30634
|
+
current += char;
|
|
30635
|
+
}
|
|
30636
|
+
if (escaping) {
|
|
30637
|
+
current += "\\";
|
|
30638
|
+
}
|
|
30639
|
+
if (quote) {
|
|
30640
|
+
throw new Error("xacpx MCP command has an unterminated quote");
|
|
30641
|
+
}
|
|
30642
|
+
if (current.length > 0) {
|
|
30643
|
+
parts.push(current);
|
|
30644
|
+
}
|
|
30645
|
+
if (parts.length === 0) {
|
|
30646
|
+
throw new Error("xacpx MCP command must not be empty");
|
|
30647
|
+
}
|
|
30648
|
+
return { command: parts[0], args: parts.slice(1) };
|
|
30649
|
+
}
|
|
30650
|
+
function stringEnv(env) {
|
|
30651
|
+
return Object.fromEntries(Object.entries(env).filter((entry) => typeof entry[1] === "string"));
|
|
30652
|
+
}
|
|
30653
|
+
async function defaultQueueOwnerSpawner(command, args, options) {
|
|
30654
|
+
await new Promise((resolve3, reject) => {
|
|
30655
|
+
const child = spawn7(command, args, {
|
|
30656
|
+
detached: true,
|
|
30657
|
+
stdio: "ignore",
|
|
30658
|
+
env: options.env,
|
|
30659
|
+
windowsHide: true
|
|
30660
|
+
});
|
|
30661
|
+
child.once("error", reject);
|
|
30662
|
+
child.once("spawn", () => {
|
|
30663
|
+
child.unref();
|
|
30664
|
+
resolve3();
|
|
30665
|
+
});
|
|
30666
|
+
});
|
|
30667
|
+
}
|
|
30668
|
+
function createDefaultQueueOwnerTerminator(_acpxCommand) {
|
|
30669
|
+
return async (sessionId) => {
|
|
30670
|
+
await terminateAcpxQueueOwner(sessionId);
|
|
30671
|
+
};
|
|
30672
|
+
}
|
|
30673
|
+
async function terminateAcpxQueueOwner(sessionId) {
|
|
30674
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
30675
|
+
let owner;
|
|
30676
|
+
try {
|
|
30677
|
+
owner = JSON.parse(await readFile13(lockPath, "utf8"));
|
|
30678
|
+
} catch {
|
|
30679
|
+
return;
|
|
30680
|
+
}
|
|
30681
|
+
if (typeof owner.pid === "number" && Number.isInteger(owner.pid) && owner.pid > 0) {
|
|
30682
|
+
await terminateProcessTree(owner.pid, { detachedProcessGroup: true });
|
|
30683
|
+
}
|
|
30684
|
+
await unlink(lockPath).catch(() => {});
|
|
30685
|
+
}
|
|
30686
|
+
function queueLockFilePath(sessionId) {
|
|
30687
|
+
return join17(homedir8(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
|
|
30688
|
+
}
|
|
30689
|
+
function shortHash(value, length) {
|
|
30690
|
+
return createHash3("sha256").update(value).digest("hex").slice(0, length);
|
|
30691
|
+
}
|
|
30692
|
+
function resolveDefaultXacpxCommand(env) {
|
|
30693
|
+
const cliCommand = coreEnv("CLI_COMMAND", env);
|
|
30694
|
+
if (cliCommand?.trim()) {
|
|
30695
|
+
return cliCommand.trim();
|
|
30696
|
+
}
|
|
30697
|
+
const daemonArg0 = coreEnv("DAEMON_ARG0", env);
|
|
30698
|
+
if (daemonArg0?.trim()) {
|
|
30699
|
+
return `${quoteCommandPart(process.execPath)} ${quoteCommandPart(daemonArg0.trim())}`;
|
|
30700
|
+
}
|
|
30701
|
+
if (process.argv[1]) {
|
|
30702
|
+
return `${quoteCommandPart(process.execPath)} ${quoteCommandPart(process.argv[1])}`;
|
|
30703
|
+
}
|
|
30704
|
+
return "xacpx";
|
|
30705
|
+
}
|
|
30706
|
+
function quoteCommandPart(value) {
|
|
30707
|
+
return quoteIfNeeded(value);
|
|
30708
|
+
}
|
|
30709
|
+
var init_acpx_queue_owner_launcher = __esm(() => {
|
|
30710
|
+
init_spawn_command();
|
|
30711
|
+
init_terminate_process_tree();
|
|
30712
|
+
init_i18n();
|
|
30713
|
+
});
|
|
30714
|
+
|
|
30715
|
+
// src/transport/acpx-bridge/acpx-bridge-client.ts
|
|
30716
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
30116
30717
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
30117
30718
|
import { createInterface } from "node:readline";
|
|
30118
30719
|
|
|
@@ -30179,6 +30780,17 @@ class AcpxBridgeClient {
|
|
|
30179
30780
|
type: "prompt.thought",
|
|
30180
30781
|
text: message.text
|
|
30181
30782
|
});
|
|
30783
|
+
} else if (message.event === "prompt.plan") {
|
|
30784
|
+
pending.onEvent?.({
|
|
30785
|
+
type: "prompt.plan",
|
|
30786
|
+
entries: message.entries
|
|
30787
|
+
});
|
|
30788
|
+
} else if (message.event === "prompt.usage") {
|
|
30789
|
+
pending.onEvent?.({
|
|
30790
|
+
type: "prompt.usage",
|
|
30791
|
+
used: message.used,
|
|
30792
|
+
size: message.size
|
|
30793
|
+
});
|
|
30182
30794
|
} else if (message.event === "session.progress") {
|
|
30183
30795
|
pending.onEvent?.({
|
|
30184
30796
|
type: "session.progress",
|
|
@@ -30228,6 +30840,7 @@ class AcpxBridgeClient {
|
|
|
30228
30840
|
function buildBridgeSpawnEnv(options = {}) {
|
|
30229
30841
|
return {
|
|
30230
30842
|
XACPX_LANG: getLocale(),
|
|
30843
|
+
XACPX_CLI_COMMAND: options.cliCommand ?? resolveDefaultXacpxCommand(process.env),
|
|
30231
30844
|
XACPX_BRIDGE_ACPX_COMMAND: options.acpxCommand ?? "acpx",
|
|
30232
30845
|
XACPX_BRIDGE_PERMISSION_MODE: options.permissionMode ?? "approve-all",
|
|
30233
30846
|
XACPX_BRIDGE_NON_INTERACTIVE_PERMISSIONS: options.nonInteractivePermissions ?? "deny",
|
|
@@ -30254,7 +30867,7 @@ async function spawnAcpxBridgeClient(options = {}) {
|
|
|
30254
30867
|
execPath: process.execPath,
|
|
30255
30868
|
bridgeEntryPath
|
|
30256
30869
|
});
|
|
30257
|
-
const child =
|
|
30870
|
+
const child = spawn8(spawnSpec.command, spawnSpec.args, {
|
|
30258
30871
|
cwd: options.cwd ?? process.cwd(),
|
|
30259
30872
|
env: {
|
|
30260
30873
|
...process.env,
|
|
@@ -30306,6 +30919,7 @@ var init_acpx_bridge_client = __esm(() => {
|
|
|
30306
30919
|
init_errors();
|
|
30307
30920
|
init_terminate_process_tree();
|
|
30308
30921
|
init_i18n();
|
|
30922
|
+
init_acpx_queue_owner_launcher();
|
|
30309
30923
|
});
|
|
30310
30924
|
|
|
30311
30925
|
// src/transport/segment-aggregator.ts
|
|
@@ -30508,6 +31122,64 @@ ${headsUpText}`);
|
|
|
30508
31122
|
}
|
|
30509
31123
|
};
|
|
30510
31124
|
}
|
|
31125
|
+
function createVerbatimReplySink(reply) {
|
|
31126
|
+
let pendingError;
|
|
31127
|
+
const inFlight = new Set;
|
|
31128
|
+
const send = (text) => {
|
|
31129
|
+
const p = reply(text).catch((err) => {
|
|
31130
|
+
if (isQuotaDeferredError(err)) {
|
|
31131
|
+
if (!pendingError) {
|
|
31132
|
+
pendingError = err;
|
|
31133
|
+
}
|
|
31134
|
+
return;
|
|
31135
|
+
}
|
|
31136
|
+
});
|
|
31137
|
+
inFlight.add(p);
|
|
31138
|
+
p.finally(() => {
|
|
31139
|
+
inFlight.delete(p);
|
|
31140
|
+
});
|
|
31141
|
+
};
|
|
31142
|
+
return {
|
|
31143
|
+
feedSegment(segment) {
|
|
31144
|
+
if (segment.length === 0)
|
|
31145
|
+
return;
|
|
31146
|
+
send(segment);
|
|
31147
|
+
},
|
|
31148
|
+
finalize() {
|
|
31149
|
+
return { trailing: "", overflowCount: 0 };
|
|
31150
|
+
},
|
|
31151
|
+
getOverflowCount() {
|
|
31152
|
+
return 0;
|
|
31153
|
+
},
|
|
31154
|
+
getPendingError() {
|
|
31155
|
+
return pendingError;
|
|
31156
|
+
},
|
|
31157
|
+
async drain(opts) {
|
|
31158
|
+
const timeoutMs = opts?.timeoutMs ?? 30000;
|
|
31159
|
+
const deadline = Date.now() + timeoutMs;
|
|
31160
|
+
while (inFlight.size > 0) {
|
|
31161
|
+
const remaining = deadline - Date.now();
|
|
31162
|
+
if (remaining <= 0)
|
|
31163
|
+
return;
|
|
31164
|
+
let timer;
|
|
31165
|
+
const timeout = new Promise((resolve3) => {
|
|
31166
|
+
timer = setTimeout(resolve3, remaining);
|
|
31167
|
+
});
|
|
31168
|
+
try {
|
|
31169
|
+
await Promise.race([
|
|
31170
|
+
Promise.allSettled(Array.from(inFlight)).then(() => {
|
|
31171
|
+
return;
|
|
31172
|
+
}),
|
|
31173
|
+
timeout
|
|
31174
|
+
]);
|
|
31175
|
+
} finally {
|
|
31176
|
+
if (timer)
|
|
31177
|
+
clearTimeout(timer);
|
|
31178
|
+
}
|
|
31179
|
+
}
|
|
31180
|
+
}
|
|
31181
|
+
};
|
|
31182
|
+
}
|
|
30511
31183
|
function buildOverflowSummary(overflowCount) {
|
|
30512
31184
|
if (overflowCount <= 0)
|
|
30513
31185
|
return;
|
|
@@ -30565,7 +31237,8 @@ class AcpxBridgeTransport {
|
|
|
30565
31237
|
});
|
|
30566
31238
|
}
|
|
30567
31239
|
async prompt(session3, text, reply, replyContext, options) {
|
|
30568
|
-
const
|
|
31240
|
+
const streamMode = (session3.effectiveReplyMode ?? session3.replyMode) === "stream";
|
|
31241
|
+
const sink = reply ? streamMode ? createVerbatimReplySink(reply) : createQuotaGatedReplySink({
|
|
30569
31242
|
reply,
|
|
30570
31243
|
...replyContext ? { replyContext } : {}
|
|
30571
31244
|
}) : null;
|
|
@@ -30575,6 +31248,10 @@ class AcpxBridgeTransport {
|
|
|
30575
31248
|
let toolEventChain = Promise.resolve();
|
|
30576
31249
|
let thoughtError;
|
|
30577
31250
|
let thoughtChain = Promise.resolve();
|
|
31251
|
+
let planError;
|
|
31252
|
+
let planChain = Promise.resolve();
|
|
31253
|
+
let usageError;
|
|
31254
|
+
let usageChain = Promise.resolve();
|
|
30578
31255
|
let toolEventMode = resolveToolEventMode(options);
|
|
30579
31256
|
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
30580
31257
|
toolEventMode = "text";
|
|
@@ -30617,10 +31294,32 @@ class AcpxBridgeTransport {
|
|
|
30617
31294
|
}
|
|
30618
31295
|
return;
|
|
30619
31296
|
}
|
|
31297
|
+
if (event.type === "prompt.plan") {
|
|
31298
|
+
const onPlan = options?.onPlan;
|
|
31299
|
+
if (onPlan) {
|
|
31300
|
+
const entries = event.entries;
|
|
31301
|
+
planChain = planChain.then(() => onPlan(entries)).catch((error2) => {
|
|
31302
|
+
planError ??= error2;
|
|
31303
|
+
});
|
|
31304
|
+
}
|
|
31305
|
+
return;
|
|
31306
|
+
}
|
|
31307
|
+
if (event.type === "prompt.usage") {
|
|
31308
|
+
const onUsage = options?.onUsage;
|
|
31309
|
+
if (onUsage) {
|
|
31310
|
+
const usage = { used: event.used, size: event.size };
|
|
31311
|
+
usageChain = usageChain.then(() => onUsage(usage)).catch((error2) => {
|
|
31312
|
+
usageError ??= error2;
|
|
31313
|
+
});
|
|
31314
|
+
}
|
|
31315
|
+
return;
|
|
31316
|
+
}
|
|
30620
31317
|
});
|
|
30621
31318
|
await segmentChain;
|
|
30622
31319
|
await toolEventChain;
|
|
30623
31320
|
await thoughtChain;
|
|
31321
|
+
await planChain;
|
|
31322
|
+
await usageChain;
|
|
30624
31323
|
if (sink) {
|
|
30625
31324
|
const { overflowCount } = sink.finalize();
|
|
30626
31325
|
await sink.drain({ timeoutMs: 30000 });
|
|
@@ -30638,6 +31337,12 @@ class AcpxBridgeTransport {
|
|
|
30638
31337
|
if (thoughtError) {
|
|
30639
31338
|
throw thoughtError;
|
|
30640
31339
|
}
|
|
31340
|
+
if (planError) {
|
|
31341
|
+
throw planError;
|
|
31342
|
+
}
|
|
31343
|
+
if (usageError) {
|
|
31344
|
+
throw usageError;
|
|
31345
|
+
}
|
|
30641
31346
|
return { text: summary ? `${summary}
|
|
30642
31347
|
|
|
30643
31348
|
${result.text}` : "" };
|
|
@@ -30651,6 +31356,12 @@ ${result.text}` : "" };
|
|
|
30651
31356
|
if (thoughtError) {
|
|
30652
31357
|
throw thoughtError;
|
|
30653
31358
|
}
|
|
31359
|
+
if (planError) {
|
|
31360
|
+
throw planError;
|
|
31361
|
+
}
|
|
31362
|
+
if (usageError) {
|
|
31363
|
+
throw usageError;
|
|
31364
|
+
}
|
|
30654
31365
|
return result;
|
|
30655
31366
|
}
|
|
30656
31367
|
async setMode(session3, modeId) {
|
|
@@ -30659,6 +31370,15 @@ ${result.text}` : "" };
|
|
|
30659
31370
|
modeId
|
|
30660
31371
|
});
|
|
30661
31372
|
}
|
|
31373
|
+
async setModel(session3, modelId) {
|
|
31374
|
+
await this.client.request("setModel", {
|
|
31375
|
+
...this.toParams({ ...session3, model: modelId }),
|
|
31376
|
+
modelId
|
|
31377
|
+
});
|
|
31378
|
+
}
|
|
31379
|
+
async getSessionModel(session3) {
|
|
31380
|
+
return await this.client.request("getSessionModel", this.toParams(session3));
|
|
31381
|
+
}
|
|
30662
31382
|
async cancel(session3) {
|
|
30663
31383
|
return await this.client.request("cancel", this.toParams(session3));
|
|
30664
31384
|
}
|
|
@@ -30687,7 +31407,8 @@ ${result.text}` : "" };
|
|
|
30687
31407
|
name: session3.transportSession,
|
|
30688
31408
|
mcpCoordinatorSession: session3.mcpCoordinatorSession,
|
|
30689
31409
|
mcpSourceHandle: session3.mcpSourceHandle,
|
|
30690
|
-
replyMode: session3.replyMode ?? "verbose"
|
|
31410
|
+
replyMode: session3.effectiveReplyMode ?? session3.replyMode ?? "verbose",
|
|
31411
|
+
...session3.model?.trim() ? { model: session3.model.trim() } : {}
|
|
30691
31412
|
};
|
|
30692
31413
|
}
|
|
30693
31414
|
}
|
|
@@ -30695,21 +31416,6 @@ var init_acpx_bridge_transport = __esm(() => {
|
|
|
30695
31416
|
init_quota_gated_reply_sink();
|
|
30696
31417
|
});
|
|
30697
31418
|
|
|
30698
|
-
// src/process/spawn-command.ts
|
|
30699
|
-
function resolveSpawnCommand(command, args) {
|
|
30700
|
-
if (SCRIPT_FILE_PATTERN.test(command)) {
|
|
30701
|
-
return {
|
|
30702
|
-
command: process.execPath,
|
|
30703
|
-
args: [command, ...args]
|
|
30704
|
-
};
|
|
30705
|
-
}
|
|
30706
|
-
return { command, args };
|
|
30707
|
-
}
|
|
30708
|
-
var SCRIPT_FILE_PATTERN;
|
|
30709
|
-
var init_spawn_command = __esm(() => {
|
|
30710
|
-
SCRIPT_FILE_PATTERN = /\.(c|m)?js$/i;
|
|
30711
|
-
});
|
|
30712
|
-
|
|
30713
31419
|
// src/transport/prompt-media.ts
|
|
30714
31420
|
import { mkdtemp, open as open4, rm as rm9, writeFile as writeFile7 } from "node:fs/promises";
|
|
30715
31421
|
import { tmpdir as defaultTmpdir } from "node:os";
|
|
@@ -30860,6 +31566,9 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
30860
31566
|
let toolEventMode;
|
|
30861
31567
|
let onToolEvent;
|
|
30862
31568
|
let onThought;
|
|
31569
|
+
let onPlan;
|
|
31570
|
+
let onUsage;
|
|
31571
|
+
let rawStream = false;
|
|
30863
31572
|
if (options === undefined) {
|
|
30864
31573
|
toolEventMode = "text";
|
|
30865
31574
|
onToolEvent = undefined;
|
|
@@ -30869,6 +31578,9 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
30869
31578
|
} else {
|
|
30870
31579
|
onToolEvent = options.onToolEvent;
|
|
30871
31580
|
onThought = options.onThought;
|
|
31581
|
+
onPlan = options.onPlan;
|
|
31582
|
+
onUsage = options.onUsage;
|
|
31583
|
+
rawStream = options.rawStream ?? false;
|
|
30872
31584
|
toolEventMode = resolveToolEventMode({
|
|
30873
31585
|
toolEventMode: options.mode,
|
|
30874
31586
|
onToolEvent
|
|
@@ -30882,13 +31594,16 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
30882
31594
|
formatToolCalls,
|
|
30883
31595
|
emittedToolCallIds: new Set,
|
|
30884
31596
|
toolEventMode,
|
|
31597
|
+
rawStream,
|
|
30885
31598
|
onToolEvent,
|
|
30886
31599
|
onThought,
|
|
31600
|
+
onPlan,
|
|
31601
|
+
onUsage,
|
|
30887
31602
|
finalize() {
|
|
30888
31603
|
if (this.pendingLine.trim().length > 0) {
|
|
30889
31604
|
parseStreamingChunks(this, this.pendingLine);
|
|
30890
31605
|
}
|
|
30891
|
-
const remaining = this.buffer.trim();
|
|
31606
|
+
const remaining = this.rawStream ? this.buffer : this.buffer.trim();
|
|
30892
31607
|
this.buffer = "";
|
|
30893
31608
|
this.pendingLine = "";
|
|
30894
31609
|
return remaining;
|
|
@@ -30920,9 +31635,9 @@ function parseStreamingChunks(state, line) {
|
|
|
30920
31635
|
const update = event.params?.update;
|
|
30921
31636
|
if (!update)
|
|
30922
31637
|
return;
|
|
30923
|
-
if (
|
|
31638
|
+
if (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") {
|
|
30924
31639
|
const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
|
|
30925
|
-
const wantsText = state.toolEventMode === "text" || state.toolEventMode === "both";
|
|
31640
|
+
const wantsText = (state.toolEventMode === "text" || state.toolEventMode === "both") && state.formatToolCalls;
|
|
30926
31641
|
if (wantsStructured && state.onToolEvent) {
|
|
30927
31642
|
const toolEvent = buildToolUseEvent(update);
|
|
30928
31643
|
if (toolEvent)
|
|
@@ -30942,6 +31657,19 @@ function parseStreamingChunks(state, line) {
|
|
|
30942
31657
|
}
|
|
30943
31658
|
return;
|
|
30944
31659
|
}
|
|
31660
|
+
if (update.sessionUpdate === "plan") {
|
|
31661
|
+
const entries = Array.isArray(update.entries) ? update.entries.filter((x) => !!x && typeof x === "object" && typeof x.content === "string" && typeof x.status === "string") : [];
|
|
31662
|
+
if (entries.length > 0)
|
|
31663
|
+
state.onPlan?.(entries);
|
|
31664
|
+
return;
|
|
31665
|
+
}
|
|
31666
|
+
if (update.sessionUpdate === "usage_update") {
|
|
31667
|
+
const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
|
|
31668
|
+
const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
|
|
31669
|
+
if (used !== undefined && size !== undefined && size > 0)
|
|
31670
|
+
state.onUsage?.({ used, size });
|
|
31671
|
+
return;
|
|
31672
|
+
}
|
|
30945
31673
|
const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
|
|
30946
31674
|
if (isThoughtChunk) {
|
|
30947
31675
|
const chunk2 = update.content.text;
|
|
@@ -30958,6 +31686,8 @@ function parseStreamingChunks(state, line) {
|
|
|
30958
31686
|
if (chunk.length === 0)
|
|
30959
31687
|
return;
|
|
30960
31688
|
state.buffer += chunk;
|
|
31689
|
+
if (state.rawStream)
|
|
31690
|
+
return;
|
|
30961
31691
|
let boundary;
|
|
30962
31692
|
while ((boundary = state.buffer.indexOf(`
|
|
30963
31693
|
|
|
@@ -31135,12 +31865,12 @@ var init_streaming_prompt = __esm(() => {
|
|
|
31135
31865
|
|
|
31136
31866
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
31137
31867
|
import { chmod as chmodFs } from "node:fs/promises";
|
|
31138
|
-
import { dirname as dirname11, join as
|
|
31868
|
+
import { dirname as dirname11, join as join18 } from "node:path";
|
|
31139
31869
|
function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
|
|
31140
31870
|
if (platform === "win32") {
|
|
31141
31871
|
return null;
|
|
31142
31872
|
}
|
|
31143
|
-
return
|
|
31873
|
+
return join18(dirname11(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
|
|
31144
31874
|
}
|
|
31145
31875
|
async function ensureNodePtyHelperExecutable(helperPath, chmod5 = chmodFs) {
|
|
31146
31876
|
if (!helperPath) {
|
|
@@ -31157,210 +31887,6 @@ async function ensureNodePtyHelperExecutable(helperPath, chmod5 = chmodFs) {
|
|
|
31157
31887
|
}
|
|
31158
31888
|
var init_node_pty_helper = () => {};
|
|
31159
31889
|
|
|
31160
|
-
// src/transport/acpx-queue-owner-launcher.ts
|
|
31161
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
31162
|
-
import { spawn as spawn8 } from "node:child_process";
|
|
31163
|
-
import { readFile as readFile13, unlink } from "node:fs/promises";
|
|
31164
|
-
import { homedir as homedir8 } from "node:os";
|
|
31165
|
-
import { join as join17 } from "node:path";
|
|
31166
|
-
function buildXacpxMcpServerSpec(input) {
|
|
31167
|
-
const { command, args } = splitCommandLine(input.xacpxCommand);
|
|
31168
|
-
return {
|
|
31169
|
-
name: "xacpx",
|
|
31170
|
-
type: "stdio",
|
|
31171
|
-
command,
|
|
31172
|
-
args: [
|
|
31173
|
-
...args,
|
|
31174
|
-
"mcp-stdio",
|
|
31175
|
-
"--coordinator-session",
|
|
31176
|
-
input.coordinatorSession,
|
|
31177
|
-
...input.sourceHandle ? ["--source-handle", input.sourceHandle] : ["--internal-session-tools"]
|
|
31178
|
-
]
|
|
31179
|
-
};
|
|
31180
|
-
}
|
|
31181
|
-
function buildQueueOwnerPayload(input) {
|
|
31182
|
-
return {
|
|
31183
|
-
sessionId: input.sessionId,
|
|
31184
|
-
permissionMode: input.permissionMode,
|
|
31185
|
-
nonInteractivePermissions: input.nonInteractivePermissions,
|
|
31186
|
-
ttlMs: input.ttlMs ?? 300000,
|
|
31187
|
-
maxQueueDepth: input.maxQueueDepth ?? 16,
|
|
31188
|
-
...Number.isFinite(input.promptRetries) ? { promptRetries: input.promptRetries } : {},
|
|
31189
|
-
...input.sessionOptions ? { sessionOptions: input.sessionOptions } : {},
|
|
31190
|
-
mcpServers: input.mcpServers
|
|
31191
|
-
};
|
|
31192
|
-
}
|
|
31193
|
-
|
|
31194
|
-
class AcpxQueueOwnerLauncher {
|
|
31195
|
-
acpxCommand;
|
|
31196
|
-
xacpxCommand;
|
|
31197
|
-
spawnOwner;
|
|
31198
|
-
terminateOwner;
|
|
31199
|
-
baseEnv;
|
|
31200
|
-
ttlMs;
|
|
31201
|
-
maxQueueDepth;
|
|
31202
|
-
launchLocks = new Map;
|
|
31203
|
-
constructor(options) {
|
|
31204
|
-
this.acpxCommand = options.acpxCommand;
|
|
31205
|
-
this.xacpxCommand = options.xacpxCommand ?? resolveDefaultXacpxCommand(options.baseEnv ?? process.env);
|
|
31206
|
-
this.spawnOwner = options.spawnOwner ?? defaultQueueOwnerSpawner;
|
|
31207
|
-
this.terminateOwner = options.terminateOwner ?? createDefaultQueueOwnerTerminator(options.acpxCommand);
|
|
31208
|
-
this.baseEnv = options.baseEnv ?? process.env;
|
|
31209
|
-
this.ttlMs = options.ttlMs;
|
|
31210
|
-
this.maxQueueDepth = options.maxQueueDepth;
|
|
31211
|
-
}
|
|
31212
|
-
async launch(input) {
|
|
31213
|
-
const key = input.acpxRecordId;
|
|
31214
|
-
const previous = this.launchLocks.get(key) ?? Promise.resolve();
|
|
31215
|
-
const next = previous.then(() => this.doLaunch(input), () => this.doLaunch(input));
|
|
31216
|
-
const tracked = next.catch(() => {});
|
|
31217
|
-
this.launchLocks.set(key, tracked);
|
|
31218
|
-
tracked.finally(() => {
|
|
31219
|
-
if (this.launchLocks.get(key) === tracked) {
|
|
31220
|
-
this.launchLocks.delete(key);
|
|
31221
|
-
}
|
|
31222
|
-
});
|
|
31223
|
-
return next;
|
|
31224
|
-
}
|
|
31225
|
-
async doLaunch(input) {
|
|
31226
|
-
await this.terminateOwner(input.acpxRecordId);
|
|
31227
|
-
const payload = buildQueueOwnerPayload({
|
|
31228
|
-
sessionId: input.acpxRecordId,
|
|
31229
|
-
permissionMode: input.permissionMode,
|
|
31230
|
-
nonInteractivePermissions: input.nonInteractivePermissions,
|
|
31231
|
-
ttlMs: this.ttlMs,
|
|
31232
|
-
maxQueueDepth: this.maxQueueDepth,
|
|
31233
|
-
mcpServers: [buildXacpxMcpServerSpec({
|
|
31234
|
-
xacpxCommand: this.xacpxCommand,
|
|
31235
|
-
coordinatorSession: input.coordinatorSession,
|
|
31236
|
-
...input.sourceHandle ? { sourceHandle: input.sourceHandle } : {}
|
|
31237
|
-
})]
|
|
31238
|
-
});
|
|
31239
|
-
const spawnSpec = resolveSpawnCommand(this.acpxCommand, ["__queue-owner"]);
|
|
31240
|
-
await this.spawnOwner(spawnSpec.command, spawnSpec.args, {
|
|
31241
|
-
env: {
|
|
31242
|
-
...stringEnv(this.baseEnv),
|
|
31243
|
-
XACPX_LANG: getLocale(),
|
|
31244
|
-
ACPX_QUEUE_OWNER_PAYLOAD: JSON.stringify(payload)
|
|
31245
|
-
}
|
|
31246
|
-
});
|
|
31247
|
-
}
|
|
31248
|
-
}
|
|
31249
|
-
function splitCommandLine(value) {
|
|
31250
|
-
const parts = [];
|
|
31251
|
-
let current = "";
|
|
31252
|
-
let quote = null;
|
|
31253
|
-
let escaping = false;
|
|
31254
|
-
for (const char of value) {
|
|
31255
|
-
if (escaping) {
|
|
31256
|
-
current += char;
|
|
31257
|
-
escaping = false;
|
|
31258
|
-
continue;
|
|
31259
|
-
}
|
|
31260
|
-
if (char === "\\" && quote !== "'") {
|
|
31261
|
-
escaping = true;
|
|
31262
|
-
continue;
|
|
31263
|
-
}
|
|
31264
|
-
if (quote) {
|
|
31265
|
-
if (char === quote) {
|
|
31266
|
-
quote = null;
|
|
31267
|
-
} else {
|
|
31268
|
-
current += char;
|
|
31269
|
-
}
|
|
31270
|
-
continue;
|
|
31271
|
-
}
|
|
31272
|
-
if (char === "'" || char === '"') {
|
|
31273
|
-
quote = char;
|
|
31274
|
-
continue;
|
|
31275
|
-
}
|
|
31276
|
-
if (/\s/.test(char)) {
|
|
31277
|
-
if (current.length > 0) {
|
|
31278
|
-
parts.push(current);
|
|
31279
|
-
current = "";
|
|
31280
|
-
}
|
|
31281
|
-
continue;
|
|
31282
|
-
}
|
|
31283
|
-
current += char;
|
|
31284
|
-
}
|
|
31285
|
-
if (escaping) {
|
|
31286
|
-
current += "\\";
|
|
31287
|
-
}
|
|
31288
|
-
if (quote) {
|
|
31289
|
-
throw new Error("xacpx MCP command has an unterminated quote");
|
|
31290
|
-
}
|
|
31291
|
-
if (current.length > 0) {
|
|
31292
|
-
parts.push(current);
|
|
31293
|
-
}
|
|
31294
|
-
if (parts.length === 0) {
|
|
31295
|
-
throw new Error("xacpx MCP command must not be empty");
|
|
31296
|
-
}
|
|
31297
|
-
return { command: parts[0], args: parts.slice(1) };
|
|
31298
|
-
}
|
|
31299
|
-
function stringEnv(env) {
|
|
31300
|
-
return Object.fromEntries(Object.entries(env).filter((entry) => typeof entry[1] === "string"));
|
|
31301
|
-
}
|
|
31302
|
-
async function defaultQueueOwnerSpawner(command, args, options) {
|
|
31303
|
-
await new Promise((resolve3, reject) => {
|
|
31304
|
-
const child = spawn8(command, args, {
|
|
31305
|
-
detached: true,
|
|
31306
|
-
stdio: "ignore",
|
|
31307
|
-
env: options.env,
|
|
31308
|
-
windowsHide: true
|
|
31309
|
-
});
|
|
31310
|
-
child.once("error", reject);
|
|
31311
|
-
child.once("spawn", () => {
|
|
31312
|
-
child.unref();
|
|
31313
|
-
resolve3();
|
|
31314
|
-
});
|
|
31315
|
-
});
|
|
31316
|
-
}
|
|
31317
|
-
function createDefaultQueueOwnerTerminator(_acpxCommand) {
|
|
31318
|
-
return async (sessionId) => {
|
|
31319
|
-
await terminateAcpxQueueOwner(sessionId);
|
|
31320
|
-
};
|
|
31321
|
-
}
|
|
31322
|
-
async function terminateAcpxQueueOwner(sessionId) {
|
|
31323
|
-
const lockPath = queueLockFilePath(sessionId);
|
|
31324
|
-
let owner;
|
|
31325
|
-
try {
|
|
31326
|
-
owner = JSON.parse(await readFile13(lockPath, "utf8"));
|
|
31327
|
-
} catch {
|
|
31328
|
-
return;
|
|
31329
|
-
}
|
|
31330
|
-
if (typeof owner.pid === "number" && Number.isInteger(owner.pid) && owner.pid > 0) {
|
|
31331
|
-
await terminateProcessTree(owner.pid, { detachedProcessGroup: true });
|
|
31332
|
-
}
|
|
31333
|
-
await unlink(lockPath).catch(() => {});
|
|
31334
|
-
}
|
|
31335
|
-
function queueLockFilePath(sessionId) {
|
|
31336
|
-
return join17(homedir8(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
|
|
31337
|
-
}
|
|
31338
|
-
function shortHash(value, length) {
|
|
31339
|
-
return createHash3("sha256").update(value).digest("hex").slice(0, length);
|
|
31340
|
-
}
|
|
31341
|
-
function resolveDefaultXacpxCommand(env) {
|
|
31342
|
-
const cliCommand = coreEnv("CLI_COMMAND", env);
|
|
31343
|
-
if (cliCommand?.trim()) {
|
|
31344
|
-
return cliCommand.trim();
|
|
31345
|
-
}
|
|
31346
|
-
const daemonArg0 = coreEnv("DAEMON_ARG0", env);
|
|
31347
|
-
if (daemonArg0?.trim()) {
|
|
31348
|
-
return `${quoteCommandPart(process.execPath)} ${quoteCommandPart(daemonArg0.trim())}`;
|
|
31349
|
-
}
|
|
31350
|
-
if (process.argv[1]) {
|
|
31351
|
-
return `${quoteCommandPart(process.execPath)} ${quoteCommandPart(process.argv[1])}`;
|
|
31352
|
-
}
|
|
31353
|
-
return "xacpx";
|
|
31354
|
-
}
|
|
31355
|
-
function quoteCommandPart(value) {
|
|
31356
|
-
return quoteIfNeeded(value);
|
|
31357
|
-
}
|
|
31358
|
-
var init_acpx_queue_owner_launcher = __esm(() => {
|
|
31359
|
-
init_spawn_command();
|
|
31360
|
-
init_terminate_process_tree();
|
|
31361
|
-
init_i18n();
|
|
31362
|
-
});
|
|
31363
|
-
|
|
31364
31890
|
// src/transport/permission-mode-flag.ts
|
|
31365
31891
|
function permissionModeToFlag(permissionMode) {
|
|
31366
31892
|
switch (permissionMode) {
|
|
@@ -31578,13 +32104,15 @@ class AcpxCliTransport {
|
|
|
31578
32104
|
const structuredPrompt = await createStructuredPromptFile(text, options?.media);
|
|
31579
32105
|
const args = this.buildPromptArgs(session3, text, structuredPrompt?.filePath);
|
|
31580
32106
|
try {
|
|
31581
|
-
if (reply || options?.onSegment || options?.onToolEvent || options?.onThought) {
|
|
31582
|
-
const
|
|
32107
|
+
if (reply || options?.onSegment || options?.onToolEvent || options?.onThought || options?.onPlan) {
|
|
32108
|
+
const effectiveReplyMode = session3.effectiveReplyMode ?? session3.replyMode;
|
|
32109
|
+
const formatToolCalls = (effectiveReplyMode ?? "verbose") === "verbose";
|
|
32110
|
+
const rawStream = effectiveReplyMode === "stream";
|
|
31583
32111
|
let toolEventMode = resolveToolEventMode(options);
|
|
31584
32112
|
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
31585
32113
|
toolEventMode = "text";
|
|
31586
32114
|
}
|
|
31587
|
-
const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent, options?.onThought);
|
|
32115
|
+
const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent, options?.onThought, options?.onPlan, rawStream);
|
|
31588
32116
|
const baseText = getPromptText(result2);
|
|
31589
32117
|
if (!reply) {
|
|
31590
32118
|
return { text: baseText };
|
|
@@ -31610,6 +32138,34 @@ ${baseText}` : "" };
|
|
|
31610
32138
|
modeId
|
|
31611
32139
|
]));
|
|
31612
32140
|
}
|
|
32141
|
+
async setModel(session3, modelId) {
|
|
32142
|
+
await this.run(this.buildArgs({ ...session3, model: modelId }, [
|
|
32143
|
+
"set",
|
|
32144
|
+
"-s",
|
|
32145
|
+
session3.transportSession,
|
|
32146
|
+
"model",
|
|
32147
|
+
modelId
|
|
32148
|
+
]));
|
|
32149
|
+
}
|
|
32150
|
+
async getSessionModel(session3) {
|
|
32151
|
+
const prefix = ["--format", "json", "--cwd", session3.cwd, ...this.buildPermissionArgs()];
|
|
32152
|
+
const tail2 = ["status", "-s", session3.transportSession];
|
|
32153
|
+
const args = session3.agentCommand ? [...prefix, "--agent", session3.agentCommand, ...tail2] : [...prefix, session3.agent, ...tail2];
|
|
32154
|
+
const result = await this.runCommandWithTimeout(this.runCommand, args);
|
|
32155
|
+
if (result.code !== 0) {
|
|
32156
|
+
const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
|
|
32157
|
+
throw new Error(detail);
|
|
32158
|
+
}
|
|
32159
|
+
try {
|
|
32160
|
+
const json = JSON.parse(result.stdout);
|
|
32161
|
+
return {
|
|
32162
|
+
current: typeof json.model === "string" ? json.model : undefined,
|
|
32163
|
+
available: Array.isArray(json.availableModels) ? json.availableModels.filter((m) => typeof m === "string") : []
|
|
32164
|
+
};
|
|
32165
|
+
} catch {
|
|
32166
|
+
return { available: [] };
|
|
32167
|
+
}
|
|
32168
|
+
}
|
|
31613
32169
|
async cancel(session3) {
|
|
31614
32170
|
const output = await this.run(this.buildArgs(session3, [
|
|
31615
32171
|
"cancel",
|
|
@@ -31673,7 +32229,8 @@ ${baseText}` : "" };
|
|
|
31673
32229
|
coordinatorSession: session3.mcpCoordinatorSession,
|
|
31674
32230
|
...session3.mcpSourceHandle ? { sourceHandle: session3.mcpSourceHandle } : {},
|
|
31675
32231
|
permissionMode: this.permissionMode,
|
|
31676
|
-
nonInteractivePermissions: this.nonInteractivePermissions
|
|
32232
|
+
nonInteractivePermissions: this.nonInteractivePermissions,
|
|
32233
|
+
...session3.model?.trim() ? { sessionOptions: { model: session3.model.trim() } } : {}
|
|
31677
32234
|
});
|
|
31678
32235
|
}
|
|
31679
32236
|
async readSessionRecord(session3) {
|
|
@@ -31741,13 +32298,13 @@ ${baseText}` : "" };
|
|
|
31741
32298
|
})
|
|
31742
32299
|
]);
|
|
31743
32300
|
}
|
|
31744
|
-
async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent, onThought) {
|
|
32301
|
+
async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent, onThought, onPlan, rawStream = false) {
|
|
31745
32302
|
const hooks = this.streamingHooks;
|
|
31746
32303
|
const doSpawn = hooks.spawnPrompt ?? ((cmd, spawnArgs) => spawn9(cmd, spawnArgs, { stdio: ["ignore", "pipe", "pipe"] }));
|
|
31747
32304
|
const setIntervalFn = hooks.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
|
|
31748
32305
|
const clearIntervalFn = hooks.clearIntervalFn ?? ((timer) => clearInterval(timer));
|
|
31749
|
-
const maxSegmentWaitMs = hooks.maxSegmentWaitMs ?? 30000;
|
|
31750
|
-
const flushCheckIntervalMs = hooks.flushCheckIntervalMs ?? 5000;
|
|
32306
|
+
const maxSegmentWaitMs = hooks.maxSegmentWaitMs ?? (rawStream ? 200 : 30000);
|
|
32307
|
+
const flushCheckIntervalMs = hooks.flushCheckIntervalMs ?? (rawStream ? 80 : 5000);
|
|
31751
32308
|
const now = hooks.now ?? (() => Date.now());
|
|
31752
32309
|
return await new Promise((resolve3, reject) => {
|
|
31753
32310
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
@@ -31761,10 +32318,14 @@ ${baseText}` : "" };
|
|
|
31761
32318
|
let toolEventError;
|
|
31762
32319
|
let thoughtChain = Promise.resolve();
|
|
31763
32320
|
let thoughtError;
|
|
32321
|
+
let planChain = Promise.resolve();
|
|
32322
|
+
let planError;
|
|
31764
32323
|
const userOnToolEvent = onToolEvent;
|
|
31765
32324
|
const userOnThought = onThought;
|
|
32325
|
+
const userOnPlan = onPlan;
|
|
31766
32326
|
const state = createStreamingPromptState(formatToolCalls, {
|
|
31767
32327
|
mode: toolEventMode,
|
|
32328
|
+
rawStream,
|
|
31768
32329
|
...userOnToolEvent ? {
|
|
31769
32330
|
onToolEvent: (event) => {
|
|
31770
32331
|
toolEventChain = toolEventChain.then(() => userOnToolEvent(event)).catch((error2) => {
|
|
@@ -31778,9 +32339,16 @@ ${baseText}` : "" };
|
|
|
31778
32339
|
thoughtError ??= error2;
|
|
31779
32340
|
});
|
|
31780
32341
|
}
|
|
32342
|
+
} : {},
|
|
32343
|
+
...userOnPlan ? {
|
|
32344
|
+
onPlan: (entries) => {
|
|
32345
|
+
planChain = planChain.then(() => userOnPlan(entries)).catch((error2) => {
|
|
32346
|
+
planError ??= error2;
|
|
32347
|
+
});
|
|
32348
|
+
}
|
|
31781
32349
|
} : {}
|
|
31782
32350
|
});
|
|
31783
|
-
const sink = reply ? createQuotaGatedReplySink({
|
|
32351
|
+
const sink = reply ? rawStream ? createVerbatimReplySink(reply) : createQuotaGatedReplySink({
|
|
31784
32352
|
reply,
|
|
31785
32353
|
...replyContext ? { replyContext } : {}
|
|
31786
32354
|
}) : null;
|
|
@@ -31794,7 +32362,7 @@ ${baseText}` : "" };
|
|
|
31794
32362
|
lastReplyAt = now();
|
|
31795
32363
|
};
|
|
31796
32364
|
const flushBuffer = () => {
|
|
31797
|
-
const remaining = state.buffer.trim();
|
|
32365
|
+
const remaining = rawStream ? state.buffer : state.buffer.trim();
|
|
31798
32366
|
if (remaining.length > 0) {
|
|
31799
32367
|
state.buffer = "";
|
|
31800
32368
|
feedSegment(remaining);
|
|
@@ -31831,7 +32399,8 @@ ${baseText}` : "" };
|
|
|
31831
32399
|
sink?.drain({ timeoutMs: 30000 }) ?? Promise.resolve(),
|
|
31832
32400
|
segmentChain,
|
|
31833
32401
|
toolEventChain,
|
|
31834
|
-
thoughtChain
|
|
32402
|
+
thoughtChain,
|
|
32403
|
+
planChain
|
|
31835
32404
|
]).then(() => {
|
|
31836
32405
|
const deferred = sink?.getPendingError();
|
|
31837
32406
|
if (deferred) {
|
|
@@ -31850,6 +32419,10 @@ ${baseText}` : "" };
|
|
|
31850
32419
|
reject(thoughtError);
|
|
31851
32420
|
return;
|
|
31852
32421
|
}
|
|
32422
|
+
if (planError) {
|
|
32423
|
+
reject(planError);
|
|
32424
|
+
return;
|
|
32425
|
+
}
|
|
31853
32426
|
resolve3({
|
|
31854
32427
|
result: { code: code ?? 1, stdout: stdout2, stderr },
|
|
31855
32428
|
overflowCount
|
|
@@ -31866,7 +32439,8 @@ ${baseText}` : "" };
|
|
|
31866
32439
|
"quiet",
|
|
31867
32440
|
"--cwd",
|
|
31868
32441
|
session3.cwd,
|
|
31869
|
-
...this.buildPermissionArgs()
|
|
32442
|
+
...this.buildPermissionArgs(),
|
|
32443
|
+
...this.buildModelArgs(session3)
|
|
31870
32444
|
];
|
|
31871
32445
|
if (session3.agentCommand) {
|
|
31872
32446
|
return [...prefix, "--agent", session3.agentCommand, ...tail2];
|
|
@@ -31888,6 +32462,7 @@ ${baseText}` : "" };
|
|
|
31888
32462
|
"--cwd",
|
|
31889
32463
|
session3.cwd,
|
|
31890
32464
|
...this.buildPermissionArgs(),
|
|
32465
|
+
...this.buildModelArgs(session3),
|
|
31891
32466
|
...this.buildQueueOwnerTtlArgs()
|
|
31892
32467
|
];
|
|
31893
32468
|
const tail2 = promptFile ? ["prompt", "-s", session3.transportSession, "--file", promptFile] : ["prompt", "-s", session3.transportSession, text];
|
|
@@ -31896,6 +32471,10 @@ ${baseText}` : "" };
|
|
|
31896
32471
|
}
|
|
31897
32472
|
return [...prefix, session3.agent, ...tail2];
|
|
31898
32473
|
}
|
|
32474
|
+
buildModelArgs(session3) {
|
|
32475
|
+
const model = session3.model?.trim();
|
|
32476
|
+
return model ? ["--model", model] : [];
|
|
32477
|
+
}
|
|
31899
32478
|
buildQueueOwnerTtlArgs() {
|
|
31900
32479
|
if (typeof this.queueOwnerTtlSeconds !== "number" || !Number.isFinite(this.queueOwnerTtlSeconds)) {
|
|
31901
32480
|
return [];
|
|
@@ -32092,7 +32671,7 @@ function workerBindingReapTargets(orchestration3, config4) {
|
|
|
32092
32671
|
if (!cwd) {
|
|
32093
32672
|
continue;
|
|
32094
32673
|
}
|
|
32095
|
-
const agentCommand =
|
|
32674
|
+
const agentCommand = resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, config4.transport.preferLocalAgents !== false);
|
|
32096
32675
|
targets.push({
|
|
32097
32676
|
agent: binding.targetAgent,
|
|
32098
32677
|
...agentCommand ? { agentCommand } : {},
|
|
@@ -32102,7 +32681,9 @@ function workerBindingReapTargets(orchestration3, config4) {
|
|
|
32102
32681
|
}
|
|
32103
32682
|
return targets;
|
|
32104
32683
|
}
|
|
32105
|
-
var init_collect_reap_targets = () => {
|
|
32684
|
+
var init_collect_reap_targets = __esm(() => {
|
|
32685
|
+
init_resolve_agent_command();
|
|
32686
|
+
});
|
|
32106
32687
|
|
|
32107
32688
|
// src/channels/channel-registry.ts
|
|
32108
32689
|
var exports_channel_registry = {};
|
|
@@ -32389,6 +32970,761 @@ var init_quota_manager = __esm(() => {
|
|
|
32389
32970
|
DEFAULT_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
32390
32971
|
});
|
|
32391
32972
|
|
|
32973
|
+
// src/control/control-event-bus.ts
|
|
32974
|
+
function createControlEventBus(logger2) {
|
|
32975
|
+
const listeners = new Set;
|
|
32976
|
+
return {
|
|
32977
|
+
subscribe(listener) {
|
|
32978
|
+
listeners.add(listener);
|
|
32979
|
+
return () => {
|
|
32980
|
+
listeners.delete(listener);
|
|
32981
|
+
};
|
|
32982
|
+
},
|
|
32983
|
+
emit(event) {
|
|
32984
|
+
for (const listener of [...listeners]) {
|
|
32985
|
+
try {
|
|
32986
|
+
listener(event);
|
|
32987
|
+
} catch (error2) {
|
|
32988
|
+
logger2?.error("control.event_listener_failed", "control event listener threw", {
|
|
32989
|
+
eventType: event.type,
|
|
32990
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
32991
|
+
});
|
|
32992
|
+
}
|
|
32993
|
+
}
|
|
32994
|
+
}
|
|
32995
|
+
};
|
|
32996
|
+
}
|
|
32997
|
+
|
|
32998
|
+
// src/transport/native-session-history.ts
|
|
32999
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
33000
|
+
import { homedir as homedir9 } from "node:os";
|
|
33001
|
+
import { join as join19 } from "node:path";
|
|
33002
|
+
function classifyToolKind(name) {
|
|
33003
|
+
const n = name.toLowerCase();
|
|
33004
|
+
if (/(^|[^a-z])(read|cat|view|open)([^a-z]|$)/.test(n))
|
|
33005
|
+
return "read";
|
|
33006
|
+
if (/(grep|search|find|glob|ripgrep|rg)/.test(n))
|
|
33007
|
+
return "search";
|
|
33008
|
+
if (/(edit|write|apply|patch|replace|create)/.test(n))
|
|
33009
|
+
return "edit";
|
|
33010
|
+
if (/(bash|shell|exec|run|terminal|command)/.test(n))
|
|
33011
|
+
return "execute";
|
|
33012
|
+
if (/(think|reason|plan)/.test(n))
|
|
33013
|
+
return "think";
|
|
33014
|
+
return "other";
|
|
33015
|
+
}
|
|
33016
|
+
function textOfUserContent(content) {
|
|
33017
|
+
if (!Array.isArray(content))
|
|
33018
|
+
return "";
|
|
33019
|
+
const out = [];
|
|
33020
|
+
for (const c of content) {
|
|
33021
|
+
if (c && typeof c === "object") {
|
|
33022
|
+
const o = c;
|
|
33023
|
+
if (typeof o.Text === "string")
|
|
33024
|
+
out.push(o.Text);
|
|
33025
|
+
else if (o.Mention && typeof o.Mention.content === "string")
|
|
33026
|
+
out.push(String(o.Mention.content));
|
|
33027
|
+
else if (o.Image)
|
|
33028
|
+
out.push("[image]");
|
|
33029
|
+
else if (o.Audio)
|
|
33030
|
+
out.push("[audio]");
|
|
33031
|
+
}
|
|
33032
|
+
}
|
|
33033
|
+
return out.join(`
|
|
33034
|
+
`);
|
|
33035
|
+
}
|
|
33036
|
+
function toolResultText(result) {
|
|
33037
|
+
if (!result || typeof result !== "object")
|
|
33038
|
+
return { isError: false };
|
|
33039
|
+
const r = result;
|
|
33040
|
+
const isError = r.is_error === true;
|
|
33041
|
+
if (typeof r.output === "string")
|
|
33042
|
+
return { text: r.output, isError };
|
|
33043
|
+
const content = r.content;
|
|
33044
|
+
if (content && typeof content.Text === "string")
|
|
33045
|
+
return { text: content.Text, isError };
|
|
33046
|
+
return { isError };
|
|
33047
|
+
}
|
|
33048
|
+
function toolUseEventOf(toolUse, result) {
|
|
33049
|
+
const id = typeof toolUse.id === "string" ? toolUse.id : "";
|
|
33050
|
+
const name = typeof toolUse.name === "string" ? toolUse.name : "tool";
|
|
33051
|
+
const rawInput = toolUse.input ?? (typeof toolUse.raw_input === "string" ? safeParse4(toolUse.raw_input) : undefined);
|
|
33052
|
+
const res = toolResultText(result);
|
|
33053
|
+
return {
|
|
33054
|
+
toolCallId: id,
|
|
33055
|
+
toolName: name,
|
|
33056
|
+
kind: classifyToolKind(name),
|
|
33057
|
+
...rawInput !== undefined ? { rawInput } : {},
|
|
33058
|
+
...res.text !== undefined ? { rawOutput: res.text } : {},
|
|
33059
|
+
status: result ? res.isError ? "error" : "success" : "success"
|
|
33060
|
+
};
|
|
33061
|
+
}
|
|
33062
|
+
function safeParse4(s) {
|
|
33063
|
+
try {
|
|
33064
|
+
return JSON.parse(s);
|
|
33065
|
+
} catch {
|
|
33066
|
+
return s;
|
|
33067
|
+
}
|
|
33068
|
+
}
|
|
33069
|
+
function mapAcpxMessagesToHistory(raw) {
|
|
33070
|
+
if (!Array.isArray(raw))
|
|
33071
|
+
return [];
|
|
33072
|
+
const out = [];
|
|
33073
|
+
for (const msg of raw) {
|
|
33074
|
+
if (msg === "Resume" || !msg || typeof msg !== "object")
|
|
33075
|
+
continue;
|
|
33076
|
+
const m = msg;
|
|
33077
|
+
if (m.User && typeof m.User === "object") {
|
|
33078
|
+
const text = textOfUserContent(m.User.content);
|
|
33079
|
+
out.push({ role: "user", text });
|
|
33080
|
+
continue;
|
|
33081
|
+
}
|
|
33082
|
+
if (m.Agent && typeof m.Agent === "object") {
|
|
33083
|
+
const agent3 = m.Agent;
|
|
33084
|
+
const toolResults = agent3.tool_results ?? {};
|
|
33085
|
+
const parts = [];
|
|
33086
|
+
const textChunks = [];
|
|
33087
|
+
for (const c of Array.isArray(agent3.content) ? agent3.content : []) {
|
|
33088
|
+
if (!c || typeof c !== "object")
|
|
33089
|
+
continue;
|
|
33090
|
+
const o = c;
|
|
33091
|
+
if (typeof o.Text === "string") {
|
|
33092
|
+
parts.push({ kind: "text", text: o.Text });
|
|
33093
|
+
textChunks.push(o.Text);
|
|
33094
|
+
} else if (o.Thinking && typeof o.Thinking.text === "string")
|
|
33095
|
+
parts.push({ kind: "reasoning", text: String(o.Thinking.text) });
|
|
33096
|
+
else if (typeof o.RedactedThinking === "string")
|
|
33097
|
+
parts.push({ kind: "reasoning", text: "[redacted reasoning]" });
|
|
33098
|
+
else if (o.ToolUse && typeof o.ToolUse === "object") {
|
|
33099
|
+
const tu = o.ToolUse;
|
|
33100
|
+
const result = typeof tu.id === "string" ? toolResults[tu.id] : undefined;
|
|
33101
|
+
parts.push({ kind: "tool", tool: toolUseEventOf(tu, result) });
|
|
33102
|
+
}
|
|
33103
|
+
}
|
|
33104
|
+
out.push({ role: "agent", text: textChunks.join(`
|
|
33105
|
+
|
|
33106
|
+
`), ...parts.length ? { parts } : {} });
|
|
33107
|
+
}
|
|
33108
|
+
}
|
|
33109
|
+
return out;
|
|
33110
|
+
}
|
|
33111
|
+
async function readNativeSessionHistory(opts) {
|
|
33112
|
+
try {
|
|
33113
|
+
const dir = opts.sessionsDir ?? join19(opts.homeDir ?? homedir9(), ".acpx", "sessions");
|
|
33114
|
+
const indexRaw = await readFile14(join19(dir, "index.json"), "utf8").catch(() => null);
|
|
33115
|
+
if (!indexRaw)
|
|
33116
|
+
return [];
|
|
33117
|
+
const index = JSON.parse(indexRaw);
|
|
33118
|
+
const candidates = (index.entries ?? []).filter((e) => e.acpSessionId === opts.agentSessionId && (!opts.agentCommand || !e.agentCommand || e.agentCommand === opts.agentCommand));
|
|
33119
|
+
let best = [];
|
|
33120
|
+
for (const entry of candidates) {
|
|
33121
|
+
if (!entry.file)
|
|
33122
|
+
continue;
|
|
33123
|
+
const recRaw = await readFile14(join19(dir, entry.file), "utf8").catch(() => null);
|
|
33124
|
+
if (!recRaw)
|
|
33125
|
+
continue;
|
|
33126
|
+
const record3 = JSON.parse(recRaw);
|
|
33127
|
+
const mapped = mapAcpxMessagesToHistory(record3.messages);
|
|
33128
|
+
if (mapped.length > best.length)
|
|
33129
|
+
best = mapped;
|
|
33130
|
+
}
|
|
33131
|
+
return best;
|
|
33132
|
+
} catch {
|
|
33133
|
+
return [];
|
|
33134
|
+
}
|
|
33135
|
+
}
|
|
33136
|
+
var init_native_session_history = () => {};
|
|
33137
|
+
|
|
33138
|
+
// src/control/workspace-fs.ts
|
|
33139
|
+
import { execFile } from "node:child_process";
|
|
33140
|
+
import { promisify } from "node:util";
|
|
33141
|
+
import { homedir as homedir10 } from "node:os";
|
|
33142
|
+
import { readdir as readdir3, realpath, stat as stat3, open as open5 } from "node:fs/promises";
|
|
33143
|
+
import { isAbsolute as isAbsolute3, relative, resolve as resolve3, sep } from "node:path";
|
|
33144
|
+
function expandHome2(p) {
|
|
33145
|
+
if (p === "~")
|
|
33146
|
+
return homedir10();
|
|
33147
|
+
if (p.startsWith("~/") || p.startsWith("~" + sep))
|
|
33148
|
+
return resolve3(homedir10(), p.slice(2));
|
|
33149
|
+
return p;
|
|
33150
|
+
}
|
|
33151
|
+
|
|
33152
|
+
class WorkspaceFs {
|
|
33153
|
+
listWorkspaces;
|
|
33154
|
+
constructor(listWorkspaces) {
|
|
33155
|
+
this.listWorkspaces = listWorkspaces;
|
|
33156
|
+
}
|
|
33157
|
+
async resolve(workspace3, relPath) {
|
|
33158
|
+
const ref = this.listWorkspaces().find((w) => w.name === workspace3);
|
|
33159
|
+
if (!ref)
|
|
33160
|
+
throw new Error("unknown-workspace");
|
|
33161
|
+
if (relPath && isAbsolute3(relPath))
|
|
33162
|
+
throw new Error("path-must-be-relative");
|
|
33163
|
+
let root;
|
|
33164
|
+
try {
|
|
33165
|
+
root = await realpath(expandHome2(ref.cwd));
|
|
33166
|
+
} catch {
|
|
33167
|
+
throw new Error("workspace-root-missing");
|
|
33168
|
+
}
|
|
33169
|
+
const requested = resolve3(root, relPath ?? ".");
|
|
33170
|
+
let abs;
|
|
33171
|
+
try {
|
|
33172
|
+
abs = await realpath(requested);
|
|
33173
|
+
} catch {
|
|
33174
|
+
throw new Error("not-found");
|
|
33175
|
+
}
|
|
33176
|
+
if (abs !== root && !abs.startsWith(root + sep))
|
|
33177
|
+
throw new Error("path-escapes-workspace");
|
|
33178
|
+
const rel = abs === root ? "" : relative(root, abs).split(sep).join("/");
|
|
33179
|
+
return { root, abs, rel };
|
|
33180
|
+
}
|
|
33181
|
+
async listDirectory(workspace3, relPath) {
|
|
33182
|
+
const { abs, rel } = await this.resolve(workspace3, relPath);
|
|
33183
|
+
const dirents = await readdir3(abs, { withFileTypes: true });
|
|
33184
|
+
const entries = [];
|
|
33185
|
+
for (const d of dirents.slice(0, MAX_ENTRIES)) {
|
|
33186
|
+
if (d.isDirectory()) {
|
|
33187
|
+
entries.push({ name: d.name, type: "dir" });
|
|
33188
|
+
} else if (d.isFile()) {
|
|
33189
|
+
let size;
|
|
33190
|
+
try {
|
|
33191
|
+
size = (await stat3(resolve3(abs, d.name))).size;
|
|
33192
|
+
} catch {
|
|
33193
|
+
size = undefined;
|
|
33194
|
+
}
|
|
33195
|
+
entries.push({ name: d.name, type: "file", size });
|
|
33196
|
+
}
|
|
33197
|
+
}
|
|
33198
|
+
entries.sort((a, b) => a.type !== b.type ? a.type === "dir" ? -1 : 1 : a.name.localeCompare(b.name));
|
|
33199
|
+
return { workspace: workspace3, path: rel, entries };
|
|
33200
|
+
}
|
|
33201
|
+
async readFile(workspace3, relPath) {
|
|
33202
|
+
const { abs, rel } = await this.resolve(workspace3, relPath);
|
|
33203
|
+
const info = await stat3(abs);
|
|
33204
|
+
if (!info.isFile())
|
|
33205
|
+
throw new Error("not-a-file");
|
|
33206
|
+
const fh = await open5(abs, "r");
|
|
33207
|
+
try {
|
|
33208
|
+
const buf = Buffer.alloc(Math.min(info.size, FILE_READ_CAP));
|
|
33209
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
|
|
33210
|
+
const slice = buf.subarray(0, bytesRead);
|
|
33211
|
+
const binary = slice.includes(0);
|
|
33212
|
+
return {
|
|
33213
|
+
workspace: workspace3,
|
|
33214
|
+
path: rel,
|
|
33215
|
+
content: binary ? "" : slice.toString("utf8"),
|
|
33216
|
+
size: info.size,
|
|
33217
|
+
truncated: info.size > FILE_READ_CAP,
|
|
33218
|
+
binary
|
|
33219
|
+
};
|
|
33220
|
+
} finally {
|
|
33221
|
+
await fh.close();
|
|
33222
|
+
}
|
|
33223
|
+
}
|
|
33224
|
+
async search(workspace3, query) {
|
|
33225
|
+
const { root } = await this.resolve(workspace3, undefined);
|
|
33226
|
+
const needle = query.trim().toLowerCase();
|
|
33227
|
+
const matches = [];
|
|
33228
|
+
if (!needle)
|
|
33229
|
+
return { workspace: workspace3, query, matches, truncated: false };
|
|
33230
|
+
let scanned = 0;
|
|
33231
|
+
let truncated = false;
|
|
33232
|
+
const queue = [root];
|
|
33233
|
+
while (queue.length) {
|
|
33234
|
+
const dir = queue.shift();
|
|
33235
|
+
let dirents;
|
|
33236
|
+
try {
|
|
33237
|
+
dirents = await readdir3(dir, { withFileTypes: true });
|
|
33238
|
+
} catch {
|
|
33239
|
+
continue;
|
|
33240
|
+
}
|
|
33241
|
+
for (const d of dirents) {
|
|
33242
|
+
if (++scanned > SEARCH_MAX_SCAN) {
|
|
33243
|
+
truncated = true;
|
|
33244
|
+
break;
|
|
33245
|
+
}
|
|
33246
|
+
if (d.isSymbolicLink())
|
|
33247
|
+
continue;
|
|
33248
|
+
if (d.isDirectory()) {
|
|
33249
|
+
if (!SEARCH_SKIP_DIRS.has(d.name))
|
|
33250
|
+
queue.push(resolve3(dir, d.name));
|
|
33251
|
+
} else if (d.isFile()) {
|
|
33252
|
+
const rel = relative(root, resolve3(dir, d.name)).split(sep).join("/");
|
|
33253
|
+
if (rel.toLowerCase().includes(needle)) {
|
|
33254
|
+
matches.push(rel);
|
|
33255
|
+
if (matches.length >= SEARCH_MAX_RESULTS) {
|
|
33256
|
+
truncated = true;
|
|
33257
|
+
break;
|
|
33258
|
+
}
|
|
33259
|
+
}
|
|
33260
|
+
}
|
|
33261
|
+
}
|
|
33262
|
+
if (truncated)
|
|
33263
|
+
break;
|
|
33264
|
+
}
|
|
33265
|
+
matches.sort();
|
|
33266
|
+
return { workspace: workspace3, query, matches, truncated };
|
|
33267
|
+
}
|
|
33268
|
+
async gitDiff(workspace3, relPath) {
|
|
33269
|
+
const { root, rel } = await this.resolve(workspace3, relPath);
|
|
33270
|
+
try {
|
|
33271
|
+
await execFileAsync("git", ["-C", root, "rev-parse", "--is-inside-work-tree"], { maxBuffer: GIT_MAX_BUFFER });
|
|
33272
|
+
} catch {
|
|
33273
|
+
throw new Error("not-a-git-repo");
|
|
33274
|
+
}
|
|
33275
|
+
const files = [];
|
|
33276
|
+
try {
|
|
33277
|
+
const { stdout: stdout2 } = await execFileAsync("git", ["-C", root, "status", "--porcelain"], { maxBuffer: GIT_MAX_BUFFER });
|
|
33278
|
+
for (const line of stdout2.split(`
|
|
33279
|
+
`)) {
|
|
33280
|
+
if (!line)
|
|
33281
|
+
continue;
|
|
33282
|
+
const status = line.slice(0, 2);
|
|
33283
|
+
let path15 = line.slice(3);
|
|
33284
|
+
const arrow = path15.indexOf(" -> ");
|
|
33285
|
+
if (arrow >= 0)
|
|
33286
|
+
path15 = path15.slice(arrow + 4);
|
|
33287
|
+
files.push({ path: path15, status });
|
|
33288
|
+
}
|
|
33289
|
+
} catch {}
|
|
33290
|
+
const diffArgs = (base) => ["-C", root, ...base, ...rel ? ["--", rel] : []];
|
|
33291
|
+
let diff = "";
|
|
33292
|
+
try {
|
|
33293
|
+
diff = (await execFileAsync("git", diffArgs(["diff", "HEAD"]), { maxBuffer: GIT_MAX_BUFFER })).stdout;
|
|
33294
|
+
} catch {
|
|
33295
|
+
try {
|
|
33296
|
+
diff = (await execFileAsync("git", diffArgs(["diff"]), { maxBuffer: GIT_MAX_BUFFER })).stdout;
|
|
33297
|
+
} catch {
|
|
33298
|
+
diff = "";
|
|
33299
|
+
}
|
|
33300
|
+
}
|
|
33301
|
+
const truncated = diff.length > DIFF_CAP;
|
|
33302
|
+
return { workspace: workspace3, files, diff: truncated ? diff.slice(0, DIFF_CAP) : diff, truncated, ...await this.gitContext(root) };
|
|
33303
|
+
}
|
|
33304
|
+
async gitContext(root) {
|
|
33305
|
+
const run = async (...args) => {
|
|
33306
|
+
try {
|
|
33307
|
+
return (await execFileAsync("git", ["-C", root, ...args], { maxBuffer: GIT_MAX_BUFFER })).stdout.trim();
|
|
33308
|
+
} catch {
|
|
33309
|
+
return null;
|
|
33310
|
+
}
|
|
33311
|
+
};
|
|
33312
|
+
const ctx = {};
|
|
33313
|
+
const head = await run("rev-parse", "--abbrev-ref", "HEAD");
|
|
33314
|
+
if (head === "HEAD")
|
|
33315
|
+
ctx.detached = true;
|
|
33316
|
+
else if (head)
|
|
33317
|
+
ctx.branch = head;
|
|
33318
|
+
const top = await run("rev-parse", "--show-toplevel");
|
|
33319
|
+
if (top) {
|
|
33320
|
+
const gitDir = await run("rev-parse", "--absolute-git-dir");
|
|
33321
|
+
const commonDir = await run("rev-parse", "--path-format=absolute", "--git-common-dir");
|
|
33322
|
+
ctx.worktree = { root: top, linked: !!gitDir && !!commonDir && gitDir !== commonDir };
|
|
33323
|
+
}
|
|
33324
|
+
return ctx;
|
|
33325
|
+
}
|
|
33326
|
+
}
|
|
33327
|
+
var execFileAsync, MAX_ENTRIES = 2000, FILE_READ_CAP, DIFF_CAP, GIT_MAX_BUFFER, SEARCH_MAX_RESULTS = 200, SEARCH_MAX_SCAN = 20000, SEARCH_SKIP_DIRS;
|
|
33328
|
+
var init_workspace_fs = __esm(() => {
|
|
33329
|
+
execFileAsync = promisify(execFile);
|
|
33330
|
+
FILE_READ_CAP = 256 * 1024;
|
|
33331
|
+
DIFF_CAP = 512 * 1024;
|
|
33332
|
+
GIT_MAX_BUFFER = 32 * 1024 * 1024;
|
|
33333
|
+
SEARCH_SKIP_DIRS = new Set([".git", "node_modules"]);
|
|
33334
|
+
});
|
|
33335
|
+
|
|
33336
|
+
// src/control/control-service.ts
|
|
33337
|
+
class ControlService {
|
|
33338
|
+
deps;
|
|
33339
|
+
constructor(deps) {
|
|
33340
|
+
this.deps = deps;
|
|
33341
|
+
}
|
|
33342
|
+
workspaceFs = new WorkspaceFs(() => this.deps.workspaces.list().map((w) => ({ name: w.name, cwd: w.cwd })));
|
|
33343
|
+
listDirectory(workspace3, path15) {
|
|
33344
|
+
return this.workspaceFs.listDirectory(workspace3, path15);
|
|
33345
|
+
}
|
|
33346
|
+
readWorkspaceFile(workspace3, path15) {
|
|
33347
|
+
return this.workspaceFs.readFile(workspace3, path15);
|
|
33348
|
+
}
|
|
33349
|
+
workspaceGitDiff(workspace3, path15) {
|
|
33350
|
+
return this.workspaceFs.gitDiff(workspace3, path15);
|
|
33351
|
+
}
|
|
33352
|
+
searchWorkspace(workspace3, query) {
|
|
33353
|
+
return this.workspaceFs.search(workspace3, query);
|
|
33354
|
+
}
|
|
33355
|
+
async getSessionModel(chatKey, alias) {
|
|
33356
|
+
const session3 = await this.resolveControlSession(chatKey, alias);
|
|
33357
|
+
if (!session3)
|
|
33358
|
+
return { available: [] };
|
|
33359
|
+
if (!this.deps.transport.getSessionModel)
|
|
33360
|
+
return { current: session3.model, available: [] };
|
|
33361
|
+
return await this.deps.transport.getSessionModel(session3);
|
|
33362
|
+
}
|
|
33363
|
+
async setSessionModel(chatKey, alias, modelId) {
|
|
33364
|
+
const session3 = await this.resolveControlSession(chatKey, alias);
|
|
33365
|
+
if (!session3)
|
|
33366
|
+
throw new Error("session not found");
|
|
33367
|
+
if (!this.deps.transport.setModel)
|
|
33368
|
+
throw new Error("the active transport does not support switching models");
|
|
33369
|
+
await this.deps.transport.setModel(session3, modelId);
|
|
33370
|
+
await this.deps.sessions.setSessionModel(session3.alias, modelId);
|
|
33371
|
+
}
|
|
33372
|
+
async resolveControlSession(chatKey, alias) {
|
|
33373
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33374
|
+
return await this.deps.sessions.getSession(internalAlias);
|
|
33375
|
+
}
|
|
33376
|
+
get events() {
|
|
33377
|
+
return this.deps.events;
|
|
33378
|
+
}
|
|
33379
|
+
listSessions(chatKey) {
|
|
33380
|
+
const channelId = getChannelIdFromChatKey(chatKey);
|
|
33381
|
+
return this.deps.sessions.listAllResolvedSessions().filter((session3) => isSessionAliasVisibleInChannel(session3.alias, channelId)).map((session3) => ({
|
|
33382
|
+
alias: toDisplaySessionAlias(session3.alias),
|
|
33383
|
+
agent: session3.agent,
|
|
33384
|
+
workspace: session3.workspace,
|
|
33385
|
+
transportSession: session3.transportSession,
|
|
33386
|
+
running: this.deps.activeTurns.isActiveAnywhere(session3.alias)
|
|
33387
|
+
}));
|
|
33388
|
+
}
|
|
33389
|
+
async listNativeSessions(_chatKey, agent3, workspace3) {
|
|
33390
|
+
const sessions = await this.deps.listNativeSessions(agent3, workspace3);
|
|
33391
|
+
return sessions.map((s) => ({
|
|
33392
|
+
sessionId: s.sessionId,
|
|
33393
|
+
title: s.title ?? null,
|
|
33394
|
+
...s.updatedAt !== undefined ? { updatedAt: s.updatedAt } : {},
|
|
33395
|
+
...s.cwd !== undefined ? { cwd: s.cwd } : {}
|
|
33396
|
+
}));
|
|
33397
|
+
}
|
|
33398
|
+
async createSession(chatKey, alias, agent3, workspace3, agentSessionId, model) {
|
|
33399
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33400
|
+
let nativeHistory = [];
|
|
33401
|
+
if (agentSessionId) {
|
|
33402
|
+
try {
|
|
33403
|
+
nativeHistory = await readNativeSessionHistory({ agentSessionId });
|
|
33404
|
+
} catch {}
|
|
33405
|
+
}
|
|
33406
|
+
const session3 = agentSessionId ? await this.deps.attachNativeSessionWithTransport(internalAlias, agent3, workspace3, agentSessionId) : await this.deps.createSessionWithTransport(internalAlias, agent3, workspace3, model);
|
|
33407
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
33408
|
+
if (nativeHistory.length > 0) {
|
|
33409
|
+
this.deps.events.emit({ type: "session-history", chatKey, sessionAlias: alias, messages: nativeHistory });
|
|
33410
|
+
}
|
|
33411
|
+
return {
|
|
33412
|
+
alias: toDisplaySessionAlias(session3.alias),
|
|
33413
|
+
agent: session3.agent,
|
|
33414
|
+
workspace: session3.workspace,
|
|
33415
|
+
transportSession: session3.transportSession,
|
|
33416
|
+
running: false
|
|
33417
|
+
};
|
|
33418
|
+
}
|
|
33419
|
+
async removeSession(chatKey, alias) {
|
|
33420
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33421
|
+
const result = await this.deps.sessions.removeSession(internalAlias);
|
|
33422
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
33423
|
+
return result;
|
|
33424
|
+
}
|
|
33425
|
+
listAgents() {
|
|
33426
|
+
return this.deps.agents.list();
|
|
33427
|
+
}
|
|
33428
|
+
listWorkspaces() {
|
|
33429
|
+
return this.deps.workspaces.list();
|
|
33430
|
+
}
|
|
33431
|
+
createWorkspace(name, cwd, description) {
|
|
33432
|
+
return this.deps.workspaces.create(name, cwd, description);
|
|
33433
|
+
}
|
|
33434
|
+
listAgentCatalog() {
|
|
33435
|
+
return this.deps.agents.catalog();
|
|
33436
|
+
}
|
|
33437
|
+
createAgent(name, driver) {
|
|
33438
|
+
return this.deps.agents.create(name, driver);
|
|
33439
|
+
}
|
|
33440
|
+
async removeAgent(name) {
|
|
33441
|
+
if (this.deps.sessions.listAllResolvedSessions().some((s) => s.agent === name)) {
|
|
33442
|
+
throw new Error(`agent "${name}" is in use by an existing session`);
|
|
33443
|
+
}
|
|
33444
|
+
await this.deps.agents.remove(name);
|
|
33445
|
+
}
|
|
33446
|
+
async removeWorkspace(name) {
|
|
33447
|
+
if (this.deps.sessions.listAllResolvedSessions().some((s) => s.workspace === name)) {
|
|
33448
|
+
throw new Error(`workspace "${name}" is in use by an existing session`);
|
|
33449
|
+
}
|
|
33450
|
+
await this.deps.workspaces.remove(name);
|
|
33451
|
+
}
|
|
33452
|
+
listScheduledTasks(chatKey) {
|
|
33453
|
+
return this.deps.scheduled.listRecentForChat(chatKey);
|
|
33454
|
+
}
|
|
33455
|
+
async createScheduledTask(input) {
|
|
33456
|
+
const task = await this.deps.scheduled.createTask(input);
|
|
33457
|
+
this.deps.events.emit({ type: "scheduled-changed", chatKey: input.chatKey });
|
|
33458
|
+
return task;
|
|
33459
|
+
}
|
|
33460
|
+
async cancelScheduledTask(id, chatKey) {
|
|
33461
|
+
const cancelled = await this.deps.scheduled.cancelPending(id, chatKey);
|
|
33462
|
+
if (cancelled) {
|
|
33463
|
+
this.deps.events.emit({ type: "scheduled-changed", chatKey });
|
|
33464
|
+
}
|
|
33465
|
+
return cancelled;
|
|
33466
|
+
}
|
|
33467
|
+
listOrchestrationTasks(filter) {
|
|
33468
|
+
return this.deps.orchestration.listTasks(filter);
|
|
33469
|
+
}
|
|
33470
|
+
getOrchestrationTask(taskId) {
|
|
33471
|
+
return this.deps.orchestration.getTask(taskId);
|
|
33472
|
+
}
|
|
33473
|
+
async cancelOrchestrationTask(input) {
|
|
33474
|
+
const task = await this.deps.orchestration.requestTaskCancellation(input);
|
|
33475
|
+
this.deps.events.emit({ type: "orchestration-changed" });
|
|
33476
|
+
return task;
|
|
33477
|
+
}
|
|
33478
|
+
inFlight = new Map;
|
|
33479
|
+
async prompt(input) {
|
|
33480
|
+
return this.executeTurn({
|
|
33481
|
+
chatKey: input.chatKey,
|
|
33482
|
+
sessionAlias: input.sessionAlias,
|
|
33483
|
+
text: input.text,
|
|
33484
|
+
senderId: input.senderId,
|
|
33485
|
+
...input.isOwner !== undefined ? { isOwner: input.isOwner } : {},
|
|
33486
|
+
...input.accountId !== undefined ? { accountId: input.accountId } : {}
|
|
33487
|
+
});
|
|
33488
|
+
}
|
|
33489
|
+
async runScheduledTurn(input) {
|
|
33490
|
+
return this.executeTurn({
|
|
33491
|
+
chatKey: input.chatKey,
|
|
33492
|
+
sessionAlias: input.sessionAlias,
|
|
33493
|
+
text: input.promptText,
|
|
33494
|
+
senderId: "scheduler",
|
|
33495
|
+
isOwner: true,
|
|
33496
|
+
...input.accountId !== undefined ? { accountId: input.accountId } : {},
|
|
33497
|
+
...input.abortSignal ? { abortSignal: input.abortSignal } : {},
|
|
33498
|
+
turnStarted: { prompt: input.promptText, scheduled: { taskId: input.taskId, executeAt: input.executeAt } }
|
|
33499
|
+
});
|
|
33500
|
+
}
|
|
33501
|
+
async executeTurn(params) {
|
|
33502
|
+
const key = turnKey(params.chatKey, params.sessionAlias);
|
|
33503
|
+
const existing = this.inFlight.get(key);
|
|
33504
|
+
if (existing) {
|
|
33505
|
+
if (!existing.controller.signal.aborted) {
|
|
33506
|
+
return { ok: false, errorMessage: "turn-already-running" };
|
|
33507
|
+
}
|
|
33508
|
+
await raceWithTimeout(existing.settled, CANCEL_DRAIN_TIMEOUT_MS);
|
|
33509
|
+
if (this.inFlight.has(key)) {
|
|
33510
|
+
return { ok: false, errorMessage: "turn-already-running" };
|
|
33511
|
+
}
|
|
33512
|
+
}
|
|
33513
|
+
const controller = new AbortController;
|
|
33514
|
+
if (params.abortSignal) {
|
|
33515
|
+
if (params.abortSignal.aborted)
|
|
33516
|
+
controller.abort();
|
|
33517
|
+
else
|
|
33518
|
+
params.abortSignal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
33519
|
+
}
|
|
33520
|
+
let resolveSettled;
|
|
33521
|
+
const settled = new Promise((resolve4) => {
|
|
33522
|
+
resolveSettled = resolve4;
|
|
33523
|
+
});
|
|
33524
|
+
this.inFlight.set(key, { controller, settled });
|
|
33525
|
+
try {
|
|
33526
|
+
await this.deps.sessions.useSession(params.chatKey, params.sessionAlias);
|
|
33527
|
+
} catch (error2) {
|
|
33528
|
+
this.inFlight.delete(key);
|
|
33529
|
+
resolveSettled();
|
|
33530
|
+
return { ok: false, errorMessage: toErrorMessage(error2) };
|
|
33531
|
+
}
|
|
33532
|
+
this.deps.events.emit({
|
|
33533
|
+
type: "turn-started",
|
|
33534
|
+
chatKey: params.chatKey,
|
|
33535
|
+
sessionAlias: params.sessionAlias,
|
|
33536
|
+
...params.turnStarted?.prompt ? { prompt: params.turnStarted.prompt } : {},
|
|
33537
|
+
...params.turnStarted?.scheduled ? { scheduled: params.turnStarted.scheduled } : {}
|
|
33538
|
+
});
|
|
33539
|
+
let streamMode = false;
|
|
33540
|
+
try {
|
|
33541
|
+
const resolved = await this.resolveControlSession(params.chatKey, params.sessionAlias);
|
|
33542
|
+
streamMode = (resolved?.effectiveReplyMode ?? resolved?.replyMode) === "stream";
|
|
33543
|
+
} catch {}
|
|
33544
|
+
let emittedChunk = false;
|
|
33545
|
+
const emitChunk = (chunk) => {
|
|
33546
|
+
if (!chunk)
|
|
33547
|
+
return;
|
|
33548
|
+
this.deps.events.emit({
|
|
33549
|
+
type: "turn-output",
|
|
33550
|
+
chatKey: params.chatKey,
|
|
33551
|
+
sessionAlias: params.sessionAlias,
|
|
33552
|
+
chunk: !streamMode && emittedChunk ? `
|
|
33553
|
+
|
|
33554
|
+
${chunk}` : chunk
|
|
33555
|
+
});
|
|
33556
|
+
emittedChunk = true;
|
|
33557
|
+
};
|
|
33558
|
+
try {
|
|
33559
|
+
const response = await this.deps.agent.chat({
|
|
33560
|
+
accountId: params.accountId ?? "control",
|
|
33561
|
+
conversationId: params.chatKey,
|
|
33562
|
+
text: params.text,
|
|
33563
|
+
metadata: buildControlMetadata(params.senderId, params.isOwner),
|
|
33564
|
+
abortSignal: controller.signal,
|
|
33565
|
+
reply: async (chunk) => {
|
|
33566
|
+
emitChunk(chunk);
|
|
33567
|
+
},
|
|
33568
|
+
onToolEvent: (event) => {
|
|
33569
|
+
this.deps.events.emit({
|
|
33570
|
+
type: "tool-event",
|
|
33571
|
+
chatKey: params.chatKey,
|
|
33572
|
+
sessionAlias: params.sessionAlias,
|
|
33573
|
+
event
|
|
33574
|
+
});
|
|
33575
|
+
},
|
|
33576
|
+
onThought: (chunk) => {
|
|
33577
|
+
this.deps.events.emit({
|
|
33578
|
+
type: "turn-thought",
|
|
33579
|
+
chatKey: params.chatKey,
|
|
33580
|
+
sessionAlias: params.sessionAlias,
|
|
33581
|
+
chunk
|
|
33582
|
+
});
|
|
33583
|
+
},
|
|
33584
|
+
onPlan: (entries) => {
|
|
33585
|
+
this.deps.events.emit({
|
|
33586
|
+
type: "plan",
|
|
33587
|
+
chatKey: params.chatKey,
|
|
33588
|
+
sessionAlias: params.sessionAlias,
|
|
33589
|
+
entries
|
|
33590
|
+
});
|
|
33591
|
+
},
|
|
33592
|
+
onUsage: (usage) => {
|
|
33593
|
+
this.deps.events.emit({
|
|
33594
|
+
type: "turn-usage",
|
|
33595
|
+
chatKey: params.chatKey,
|
|
33596
|
+
sessionAlias: params.sessionAlias,
|
|
33597
|
+
used: usage.used,
|
|
33598
|
+
size: usage.size
|
|
33599
|
+
});
|
|
33600
|
+
}
|
|
33601
|
+
});
|
|
33602
|
+
if (response.text) {
|
|
33603
|
+
emitChunk(response.text);
|
|
33604
|
+
}
|
|
33605
|
+
this.deps.events.emit({
|
|
33606
|
+
type: "turn-finished",
|
|
33607
|
+
chatKey: params.chatKey,
|
|
33608
|
+
sessionAlias: params.sessionAlias,
|
|
33609
|
+
ok: true
|
|
33610
|
+
});
|
|
33611
|
+
return { ok: true, text: response.text };
|
|
33612
|
+
} catch (error2) {
|
|
33613
|
+
const errorMessage = toErrorMessage(error2);
|
|
33614
|
+
this.deps.events.emit({
|
|
33615
|
+
type: "turn-finished",
|
|
33616
|
+
chatKey: params.chatKey,
|
|
33617
|
+
sessionAlias: params.sessionAlias,
|
|
33618
|
+
ok: false,
|
|
33619
|
+
errorMessage,
|
|
33620
|
+
...controller.signal.aborted ? { cancelled: true } : {}
|
|
33621
|
+
});
|
|
33622
|
+
return { ok: false, errorMessage };
|
|
33623
|
+
} finally {
|
|
33624
|
+
this.inFlight.delete(key);
|
|
33625
|
+
resolveSettled();
|
|
33626
|
+
}
|
|
33627
|
+
}
|
|
33628
|
+
cancelTurn(chatKey, sessionAlias) {
|
|
33629
|
+
const entry = this.inFlight.get(turnKey(chatKey, sessionAlias));
|
|
33630
|
+
if (!entry) {
|
|
33631
|
+
return false;
|
|
33632
|
+
}
|
|
33633
|
+
entry.controller.abort();
|
|
33634
|
+
return true;
|
|
33635
|
+
}
|
|
33636
|
+
async executeCommand(input) {
|
|
33637
|
+
const chunks = [];
|
|
33638
|
+
const response = await this.deps.agent.chat({
|
|
33639
|
+
accountId: input.accountId ?? "control",
|
|
33640
|
+
conversationId: input.chatKey,
|
|
33641
|
+
text: input.text,
|
|
33642
|
+
metadata: buildControlMetadata(input.senderId, input.isOwner),
|
|
33643
|
+
reply: async (chunk) => {
|
|
33644
|
+
chunks.push(chunk);
|
|
33645
|
+
}
|
|
33646
|
+
});
|
|
33647
|
+
if (response.text) {
|
|
33648
|
+
chunks.push(response.text);
|
|
33649
|
+
}
|
|
33650
|
+
return chunks.join(`
|
|
33651
|
+
`);
|
|
33652
|
+
}
|
|
33653
|
+
}
|
|
33654
|
+
async function raceWithTimeout(promise2, ms) {
|
|
33655
|
+
let timer;
|
|
33656
|
+
const timeout = new Promise((resolve4) => {
|
|
33657
|
+
timer = setTimeout(resolve4, ms);
|
|
33658
|
+
});
|
|
33659
|
+
try {
|
|
33660
|
+
await Promise.race([promise2, timeout]);
|
|
33661
|
+
} finally {
|
|
33662
|
+
if (timer)
|
|
33663
|
+
clearTimeout(timer);
|
|
33664
|
+
}
|
|
33665
|
+
}
|
|
33666
|
+
function turnKey(chatKey, sessionAlias) {
|
|
33667
|
+
return `${chatKey} ${sessionAlias}`;
|
|
33668
|
+
}
|
|
33669
|
+
function toErrorMessage(error2) {
|
|
33670
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
33671
|
+
}
|
|
33672
|
+
function buildControlMetadata(senderId, isOwner) {
|
|
33673
|
+
return {
|
|
33674
|
+
channel: "control",
|
|
33675
|
+
chatType: "direct",
|
|
33676
|
+
senderId,
|
|
33677
|
+
...isOwner === undefined ? {} : { isOwner }
|
|
33678
|
+
};
|
|
33679
|
+
}
|
|
33680
|
+
var CANCEL_DRAIN_TIMEOUT_MS = 5000;
|
|
33681
|
+
var init_control_service = __esm(() => {
|
|
33682
|
+
init_channel_scope();
|
|
33683
|
+
init_native_session_history();
|
|
33684
|
+
init_workspace_fs();
|
|
33685
|
+
});
|
|
33686
|
+
|
|
33687
|
+
// src/config/agent-catalog.ts
|
|
33688
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
33689
|
+
import { delimiter as delimiter2, join as join20 } from "node:path";
|
|
33690
|
+
function isBinaryOnPath(binary) {
|
|
33691
|
+
const path15 = process.env.PATH ?? "";
|
|
33692
|
+
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
33693
|
+
for (const dir of path15.split(delimiter2)) {
|
|
33694
|
+
if (!dir)
|
|
33695
|
+
continue;
|
|
33696
|
+
for (const ext of exts) {
|
|
33697
|
+
try {
|
|
33698
|
+
if (existsSync3(join20(dir, binary + ext)))
|
|
33699
|
+
return true;
|
|
33700
|
+
} catch {}
|
|
33701
|
+
}
|
|
33702
|
+
}
|
|
33703
|
+
return false;
|
|
33704
|
+
}
|
|
33705
|
+
function listAgentCatalog(config4, probe = isBinaryOnPath) {
|
|
33706
|
+
const agents = config4.agents ?? {};
|
|
33707
|
+
const driverConfigured = (driver) => Object.entries(agents).some(([name, a]) => name === driver || a.driver === driver);
|
|
33708
|
+
return listAgentTemplates().map((driver) => {
|
|
33709
|
+
let installed;
|
|
33710
|
+
if (BUILTIN_DRIVERS.has(driver)) {
|
|
33711
|
+
installed = "builtin";
|
|
33712
|
+
} else {
|
|
33713
|
+
const binary = DRIVER_BINARIES[driver] ?? driver;
|
|
33714
|
+
installed = probe(binary) ? "yes" : "unknown";
|
|
33715
|
+
}
|
|
33716
|
+
return { driver, configured: driverConfigured(driver), installed };
|
|
33717
|
+
});
|
|
33718
|
+
}
|
|
33719
|
+
var BUILTIN_DRIVERS, DRIVER_BINARIES;
|
|
33720
|
+
var init_agent_catalog = __esm(() => {
|
|
33721
|
+
init_agent_templates();
|
|
33722
|
+
BUILTIN_DRIVERS = new Set(["codex", "claude"]);
|
|
33723
|
+
DRIVER_BINARIES = {
|
|
33724
|
+
cursor: "cursor-agent"
|
|
33725
|
+
};
|
|
33726
|
+
});
|
|
33727
|
+
|
|
32392
33728
|
// src/main.ts
|
|
32393
33729
|
var exports_main = {};
|
|
32394
33730
|
__export(exports_main, {
|
|
@@ -32399,8 +33735,8 @@ __export(exports_main, {
|
|
|
32399
33735
|
buildApp: () => buildApp
|
|
32400
33736
|
});
|
|
32401
33737
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
32402
|
-
import { homedir as
|
|
32403
|
-
import { dirname as dirname12, join as
|
|
33738
|
+
import { homedir as homedir11 } from "node:os";
|
|
33739
|
+
import { dirname as dirname12, join as join21 } from "node:path";
|
|
32404
33740
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
32405
33741
|
function startProgressHeartbeat(orchestration3, config4, logger2, channel) {
|
|
32406
33742
|
const thresholdSeconds = config4.orchestration.progressHeartbeatSeconds;
|
|
@@ -32676,7 +34012,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
32676
34012
|
return {
|
|
32677
34013
|
alias: input.workerSession,
|
|
32678
34014
|
agent: input.targetAgent,
|
|
32679
|
-
agentCommand:
|
|
34015
|
+
agentCommand: resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, config4.transport.preferLocalAgents !== false),
|
|
32680
34016
|
workspace: input.workspace,
|
|
32681
34017
|
transportSession: input.workerSession,
|
|
32682
34018
|
cwd: input.cwd
|
|
@@ -32890,9 +34226,52 @@ async function buildApp(paths, deps = {}) {
|
|
|
32890
34226
|
});
|
|
32891
34227
|
const router3 = new CommandRouter(sessions, transport, config4, configStore, logger2, undefined, orchestration3, quota, scheduledService, deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : undefined, deps.channel?.nativeSessionListFormat ? deps.channel.nativeSessionListFormat.bind(deps.channel) : undefined, activeTurns);
|
|
32892
34228
|
const agent3 = new ConsoleAgent(router3, logger2);
|
|
34229
|
+
const controlEvents = createControlEventBus(logger2);
|
|
34230
|
+
const control = new ControlService({
|
|
34231
|
+
agent: agent3,
|
|
34232
|
+
sessions,
|
|
34233
|
+
transport,
|
|
34234
|
+
createSessionWithTransport: (internalAlias, agent4, workspace3, model) => router3.createSessionWithTransport(internalAlias, agent4, workspace3, model),
|
|
34235
|
+
listNativeSessions: (agent4, workspace3) => router3.listNativeSessionsForControl(agent4, workspace3),
|
|
34236
|
+
attachNativeSessionWithTransport: (internalAlias, agent4, workspace3, agentSessionId, nativeMeta) => router3.attachNativeSessionWithTransport(internalAlias, agent4, workspace3, agentSessionId, nativeMeta),
|
|
34237
|
+
activeTurns,
|
|
34238
|
+
scheduled: scheduledService,
|
|
34239
|
+
orchestration: orchestration3,
|
|
34240
|
+
events: controlEvents,
|
|
34241
|
+
agents: {
|
|
34242
|
+
list: () => Object.entries(config4.agents).map(([name, agentConfig]) => ({ name, driver: agentConfig.driver })),
|
|
34243
|
+
catalog: () => listAgentCatalog(config4),
|
|
34244
|
+
create: async (name, driver) => {
|
|
34245
|
+
const updated = await configStore.upsertAgent(name, { driver });
|
|
34246
|
+
replaceRuntimeConfig(config4, updated);
|
|
34247
|
+
return { name, driver };
|
|
34248
|
+
},
|
|
34249
|
+
remove: async (name) => {
|
|
34250
|
+
const updated = await configStore.removeAgent(name);
|
|
34251
|
+
replaceRuntimeConfig(config4, updated);
|
|
34252
|
+
}
|
|
34253
|
+
},
|
|
34254
|
+
workspaces: {
|
|
34255
|
+
list: () => Object.entries(config4.workspaces).map(([name, workspace3]) => ({
|
|
34256
|
+
name,
|
|
34257
|
+
cwd: workspace3.cwd,
|
|
34258
|
+
...workspace3.description ? { description: workspace3.description } : {}
|
|
34259
|
+
})),
|
|
34260
|
+
create: async (name, cwd, description) => {
|
|
34261
|
+
const updated = await configStore.upsertWorkspace(name, cwd, description);
|
|
34262
|
+
replaceRuntimeConfig(config4, updated);
|
|
34263
|
+
return { name, cwd, ...description ? { description } : {} };
|
|
34264
|
+
},
|
|
34265
|
+
remove: async (name) => {
|
|
34266
|
+
const updated = await configStore.removeWorkspace(name);
|
|
34267
|
+
replaceRuntimeConfig(config4, updated);
|
|
34268
|
+
}
|
|
34269
|
+
}
|
|
34270
|
+
});
|
|
32893
34271
|
const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
|
|
32894
34272
|
dispatchTask: buildScheduledDispatchTask({
|
|
32895
34273
|
getSession: (alias) => sessions.getSession(alias),
|
|
34274
|
+
resolveAliasForChat: (chatKey, alias) => sessions.resolveAliasForChat(chatKey, alias),
|
|
32896
34275
|
resolveSession: (alias, agent4, workspace3, transportSession) => sessions.resolveSession(alias, agent4, workspace3, transportSession),
|
|
32897
34276
|
sendScheduledMessage: async (input) => {
|
|
32898
34277
|
if (!deps.channel?.sendScheduledMessage) {
|
|
@@ -32903,6 +34282,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
32903
34282
|
...transport.removeSession ? { removeSession: (session3) => transport.removeSession(session3) } : {},
|
|
32904
34283
|
logger: logger2
|
|
32905
34284
|
}),
|
|
34285
|
+
onSettled: (task) => controlEvents.emit({ type: "scheduled-changed", chatKey: task.chat_key }),
|
|
32906
34286
|
logger: logger2
|
|
32907
34287
|
});
|
|
32908
34288
|
const reapWarmQueueOwners = async (phase) => {
|
|
@@ -32952,6 +34332,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
32952
34332
|
service: scheduledService,
|
|
32953
34333
|
scheduler: scheduledScheduler
|
|
32954
34334
|
},
|
|
34335
|
+
control,
|
|
32955
34336
|
reapStaleQueueOwners: () => reapWarmQueueOwners("startup"),
|
|
32956
34337
|
dispose: async () => {
|
|
32957
34338
|
scheduledScheduler.stop();
|
|
@@ -33015,8 +34396,8 @@ async function main() {
|
|
|
33015
34396
|
}
|
|
33016
34397
|
}
|
|
33017
34398
|
async function prepareChannelMedia(configPath, config4) {
|
|
33018
|
-
const runtimeDir =
|
|
33019
|
-
const mediaRootDir =
|
|
34399
|
+
const runtimeDir = join21(dirname12(configPath), "runtime");
|
|
34400
|
+
const mediaRootDir = join21(runtimeDir, "media");
|
|
33020
34401
|
const mediaStore = new RuntimeMediaStore({ rootDir: mediaRootDir });
|
|
33021
34402
|
await mediaStore.cleanupExpired().catch((error2) => {
|
|
33022
34403
|
console.error("[xacpx] media cleanup failed:", error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -33025,16 +34406,16 @@ async function prepareChannelMedia(configPath, config4) {
|
|
|
33025
34406
|
return { mediaStore, channelDeps: { mediaStore, allowedMediaRoots } };
|
|
33026
34407
|
}
|
|
33027
34408
|
function resolveRuntimePaths() {
|
|
33028
|
-
const home = process.env.HOME ??
|
|
34409
|
+
const home = process.env.HOME ?? homedir11();
|
|
33029
34410
|
if (!home) {
|
|
33030
34411
|
throw new Error("Unable to resolve the current user home directory");
|
|
33031
34412
|
}
|
|
33032
|
-
const configPath = coreEnv("CONFIG") ??
|
|
33033
|
-
const runtimeDir =
|
|
34413
|
+
const configPath = coreEnv("CONFIG") ?? join21(coreHomeDir(home), "config.json");
|
|
34414
|
+
const runtimeDir = join21(dirname12(configPath), "runtime");
|
|
33034
34415
|
return {
|
|
33035
34416
|
configPath,
|
|
33036
|
-
statePath: coreEnv("STATE") ??
|
|
33037
|
-
perfLogPath:
|
|
34417
|
+
statePath: coreEnv("STATE") ?? join21(coreHomeDir(home), "state.json"),
|
|
34418
|
+
perfLogPath: join21(runtimeDir, "perf.log"),
|
|
33038
34419
|
orchestrationSocketPath: coreEnv("ORCHESTRATION_SOCKET") ?? resolveDaemonOrchestrationSocketPath(runtimeDir)
|
|
33039
34420
|
};
|
|
33040
34421
|
}
|
|
@@ -33046,13 +34427,13 @@ function resolveBridgeEntryPath() {
|
|
|
33046
34427
|
}
|
|
33047
34428
|
function resolveAppLogPath(configPath) {
|
|
33048
34429
|
const rootDir = dirname12(configPath);
|
|
33049
|
-
const runtimeDir =
|
|
33050
|
-
return
|
|
34430
|
+
const runtimeDir = join21(rootDir, "runtime");
|
|
34431
|
+
return join21(runtimeDir, "app.log");
|
|
33051
34432
|
}
|
|
33052
34433
|
function resolvePerfLogPath(configPath) {
|
|
33053
34434
|
const rootDir = dirname12(configPath);
|
|
33054
|
-
const runtimeDir =
|
|
33055
|
-
return
|
|
34435
|
+
const runtimeDir = join21(rootDir, "runtime");
|
|
34436
|
+
return join21(runtimeDir, "perf.log");
|
|
33056
34437
|
}
|
|
33057
34438
|
function resolveOrchestrationSocketPathFromConfigPath(configPath) {
|
|
33058
34439
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
@@ -33068,6 +34449,7 @@ var init_main = __esm(async () => {
|
|
|
33068
34449
|
init_ensure_config();
|
|
33069
34450
|
init_load_config();
|
|
33070
34451
|
init_resolve_acpx_command();
|
|
34452
|
+
init_resolve_agent_command();
|
|
33071
34453
|
init_console_agent();
|
|
33072
34454
|
init_app_logger();
|
|
33073
34455
|
init_daemon_files();
|
|
@@ -33095,6 +34477,8 @@ var init_main = __esm(async () => {
|
|
|
33095
34477
|
init_inbound();
|
|
33096
34478
|
init_render_text();
|
|
33097
34479
|
init_quota_manager();
|
|
34480
|
+
init_control_service();
|
|
34481
|
+
init_agent_catalog();
|
|
33098
34482
|
init_perf_tracer();
|
|
33099
34483
|
init_bootstrap();
|
|
33100
34484
|
init_i18n();
|
|
@@ -33148,7 +34532,7 @@ function buildDetails(metadata, version2, verbose) {
|
|
|
33148
34532
|
}
|
|
33149
34533
|
async function defaultRunVersion(command) {
|
|
33150
34534
|
const spawnSpec = resolveSpawnCommand(command, ["--version"]);
|
|
33151
|
-
return await new Promise((
|
|
34535
|
+
return await new Promise((resolve4, reject) => {
|
|
33152
34536
|
const child = spawn11(spawnSpec.command, spawnSpec.args, {
|
|
33153
34537
|
stdio: ["ignore", "pipe", "pipe"]
|
|
33154
34538
|
});
|
|
@@ -33165,7 +34549,7 @@ async function defaultRunVersion(command) {
|
|
|
33165
34549
|
if (code === 0) {
|
|
33166
34550
|
const version2 = stdout2.trim() || stderr.trim();
|
|
33167
34551
|
if (version2.length > 0) {
|
|
33168
|
-
|
|
34552
|
+
resolve4(version2);
|
|
33169
34553
|
return;
|
|
33170
34554
|
}
|
|
33171
34555
|
}
|
|
@@ -33287,12 +34671,12 @@ var init_config_check = __esm(async () => {
|
|
|
33287
34671
|
});
|
|
33288
34672
|
|
|
33289
34673
|
// src/doctor/checks/daemon-check.ts
|
|
33290
|
-
import { readdir as
|
|
34674
|
+
import { readdir as readdir4, readFile as readFile15, rm as rm10 } from "node:fs/promises";
|
|
33291
34675
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
33292
|
-
import { homedir as
|
|
33293
|
-
import { join as
|
|
34676
|
+
import { homedir as homedir12 } from "node:os";
|
|
34677
|
+
import { join as join22 } from "node:path";
|
|
33294
34678
|
async function checkDaemon(options = {}) {
|
|
33295
|
-
const home = options.home ?? process.env.HOME ??
|
|
34679
|
+
const home = options.home ?? process.env.HOME ?? homedir12();
|
|
33296
34680
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
33297
34681
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
33298
34682
|
home,
|
|
@@ -33392,7 +34776,7 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
33392
34776
|
if (!fileName.endsWith(CONSUMER_LOCK_SUFFIX)) {
|
|
33393
34777
|
continue;
|
|
33394
34778
|
}
|
|
33395
|
-
const lockPath =
|
|
34779
|
+
const lockPath = join22(runtimeDir, fileName);
|
|
33396
34780
|
const lock2 = await deps.readConsumerLock(lockPath);
|
|
33397
34781
|
if (lock2 && !deps.isProcessRunning(lock2.pid)) {
|
|
33398
34782
|
stalePaths.push(lockPath);
|
|
@@ -33426,14 +34810,14 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
33426
34810
|
}
|
|
33427
34811
|
async function defaultListConsumerLocks(runtimeDir) {
|
|
33428
34812
|
try {
|
|
33429
|
-
return await
|
|
34813
|
+
return await readdir4(runtimeDir);
|
|
33430
34814
|
} catch {
|
|
33431
34815
|
return [];
|
|
33432
34816
|
}
|
|
33433
34817
|
}
|
|
33434
34818
|
async function defaultReadConsumerLock(path15) {
|
|
33435
34819
|
try {
|
|
33436
|
-
const raw = await
|
|
34820
|
+
const raw = await readFile15(path15, "utf8");
|
|
33437
34821
|
const parsed = JSON.parse(raw);
|
|
33438
34822
|
return typeof parsed.pid === "number" ? { pid: parsed.pid } : null;
|
|
33439
34823
|
} catch {
|
|
@@ -33456,11 +34840,11 @@ var init_daemon_check = __esm(() => {
|
|
|
33456
34840
|
});
|
|
33457
34841
|
|
|
33458
34842
|
// src/doctor/checks/logs-check.ts
|
|
33459
|
-
import { stat as
|
|
33460
|
-
import { basename as basename3, join as
|
|
33461
|
-
import { homedir as
|
|
34843
|
+
import { stat as stat4, readdir as readdir5 } from "node:fs/promises";
|
|
34844
|
+
import { basename as basename3, join as join23 } from "node:path";
|
|
34845
|
+
import { homedir as homedir13 } from "node:os";
|
|
33462
34846
|
async function checkLogs(options = {}) {
|
|
33463
|
-
const home = options.home ?? process.env.HOME ??
|
|
34847
|
+
const home = options.home ?? process.env.HOME ?? homedir13();
|
|
33464
34848
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
33465
34849
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
33466
34850
|
home,
|
|
@@ -33492,7 +34876,7 @@ async function checkLogs(options = {}) {
|
|
|
33492
34876
|
const matched = entries.filter((entry) => isTrackedLogName(entry, tracked));
|
|
33493
34877
|
const files = [];
|
|
33494
34878
|
for (const name of matched) {
|
|
33495
|
-
const path15 =
|
|
34879
|
+
const path15 = join23(paths.runtimeDir, name);
|
|
33496
34880
|
try {
|
|
33497
34881
|
const fileStat = await probe.stat(path15);
|
|
33498
34882
|
if (fileStat.isDirectory()) {
|
|
@@ -33572,8 +34956,8 @@ function formatBytes(bytes) {
|
|
|
33572
34956
|
}
|
|
33573
34957
|
function createLogsFsProbe() {
|
|
33574
34958
|
return {
|
|
33575
|
-
stat: async (path15) => await
|
|
33576
|
-
readdir: async (path15) => await
|
|
34959
|
+
stat: async (path15) => await stat4(path15),
|
|
34960
|
+
readdir: async (path15) => await readdir5(path15)
|
|
33577
34961
|
};
|
|
33578
34962
|
}
|
|
33579
34963
|
function formatError6(error2) {
|
|
@@ -33642,9 +35026,9 @@ var init_orchestration_health = __esm(() => {
|
|
|
33642
35026
|
});
|
|
33643
35027
|
|
|
33644
35028
|
// src/doctor/checks/orchestration-socket-check.ts
|
|
33645
|
-
import { homedir as
|
|
35029
|
+
import { homedir as homedir14 } from "node:os";
|
|
33646
35030
|
async function checkOrchestrationSocket(options = {}) {
|
|
33647
|
-
const home = options.home ?? process.env.HOME ??
|
|
35031
|
+
const home = options.home ?? process.env.HOME ?? homedir14();
|
|
33648
35032
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
33649
35033
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
33650
35034
|
home,
|
|
@@ -33817,11 +35201,11 @@ var init_plugin_check = __esm(async () => {
|
|
|
33817
35201
|
|
|
33818
35202
|
// src/doctor/checks/runtime-check.ts
|
|
33819
35203
|
import { constants } from "node:fs";
|
|
33820
|
-
import { access as access4, stat as
|
|
35204
|
+
import { access as access4, stat as stat5 } from "node:fs/promises";
|
|
33821
35205
|
import { dirname as dirname13 } from "node:path";
|
|
33822
|
-
import { homedir as
|
|
35206
|
+
import { homedir as homedir15 } from "node:os";
|
|
33823
35207
|
async function checkRuntime(options = {}) {
|
|
33824
|
-
const home = options.home ?? process.env.HOME ??
|
|
35208
|
+
const home = options.home ?? process.env.HOME ?? homedir15();
|
|
33825
35209
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
33826
35210
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
33827
35211
|
home,
|
|
@@ -33915,7 +35299,7 @@ function formatMode(mode) {
|
|
|
33915
35299
|
}
|
|
33916
35300
|
function createRuntimeFsProbe() {
|
|
33917
35301
|
return {
|
|
33918
|
-
stat: async (path15) => await
|
|
35302
|
+
stat: async (path15) => await stat5(path15),
|
|
33919
35303
|
access: async (path15, mode) => await access4(path15, mode)
|
|
33920
35304
|
};
|
|
33921
35305
|
}
|
|
@@ -34247,7 +35631,10 @@ function buildSession(options) {
|
|
|
34247
35631
|
return {
|
|
34248
35632
|
alias: "xacpx-doctor",
|
|
34249
35633
|
agent: options.agent,
|
|
34250
|
-
...
|
|
35634
|
+
...(() => {
|
|
35635
|
+
const agentCommand = resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, options.config.transport.preferLocalAgents !== false);
|
|
35636
|
+
return agentCommand ? { agentCommand } : {};
|
|
35637
|
+
})(),
|
|
34251
35638
|
workspace: options.workspace,
|
|
34252
35639
|
transportSession: `xacpx-doctor-${timestamp}`,
|
|
34253
35640
|
replyMode: options.config.channel.replyMode,
|
|
@@ -34292,6 +35679,7 @@ var SMOKE_PROMPT = "Reply with exactly: ok";
|
|
|
34292
35679
|
var init_smoke_check = __esm(async () => {
|
|
34293
35680
|
init_load_config();
|
|
34294
35681
|
init_resolve_acpx_command();
|
|
35682
|
+
init_resolve_agent_command();
|
|
34295
35683
|
init_acpx_bridge_client();
|
|
34296
35684
|
init_acpx_bridge_transport();
|
|
34297
35685
|
init_acpx_cli_transport();
|
|
@@ -34450,10 +35838,10 @@ var init_render_doctor = __esm(() => {
|
|
|
34450
35838
|
});
|
|
34451
35839
|
|
|
34452
35840
|
// src/doctor/doctor.ts
|
|
34453
|
-
import { homedir as
|
|
34454
|
-
import { join as
|
|
35841
|
+
import { homedir as homedir16 } from "node:os";
|
|
35842
|
+
import { join as join24 } from "node:path";
|
|
34455
35843
|
async function runDoctor(options = {}, deps = {}) {
|
|
34456
|
-
const home = deps.home ?? process.env.HOME ??
|
|
35844
|
+
const home = deps.home ?? process.env.HOME ?? homedir16();
|
|
34457
35845
|
const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
|
|
34458
35846
|
const sharedLoadConfig = createSharedLoadConfig(runtimePaths, deps.loadConfig ?? loadConfig);
|
|
34459
35847
|
const runners = [
|
|
@@ -34606,8 +35994,8 @@ function resolveDoctorRuntimePaths(home, resolver) {
|
|
|
34606
35994
|
return resolveRuntimePaths();
|
|
34607
35995
|
}
|
|
34608
35996
|
return {
|
|
34609
|
-
configPath:
|
|
34610
|
-
statePath:
|
|
35997
|
+
configPath: join24(coreHomeDir(home), "config.json"),
|
|
35998
|
+
statePath: join24(coreHomeDir(home), "state.json")
|
|
34611
35999
|
};
|
|
34612
36000
|
}
|
|
34613
36001
|
function depsUseExplicitRuntimeOverrides() {
|
|
@@ -34790,8 +36178,8 @@ var init_doctor2 = __esm(async () => {
|
|
|
34790
36178
|
// src/cli.ts
|
|
34791
36179
|
init_core_home();
|
|
34792
36180
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
34793
|
-
import { homedir as
|
|
34794
|
-
import { dirname as dirname14, join as
|
|
36181
|
+
import { homedir as homedir17 } from "node:os";
|
|
36182
|
+
import { dirname as dirname14, join as join25, sep as sep2 } from "node:path";
|
|
34795
36183
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
34796
36184
|
|
|
34797
36185
|
// src/runtime/migrate-core-home.ts
|
|
@@ -47288,7 +48676,7 @@ init_core_home();
|
|
|
47288
48676
|
init_daemon_files();
|
|
47289
48677
|
init_orchestration_ipc();
|
|
47290
48678
|
import { homedir as homedir2 } from "node:os";
|
|
47291
|
-
import { join as
|
|
48679
|
+
import { join as join6 } from "node:path";
|
|
47292
48680
|
function resolveDefaultOrchestrationEndpoint(env = process.env, platform = process.platform) {
|
|
47293
48681
|
const orchestrationSocket = coreEnv("ORCHESTRATION_SOCKET", env);
|
|
47294
48682
|
if (typeof orchestrationSocket === "string" && orchestrationSocket.trim().length > 0) {
|
|
@@ -47296,7 +48684,7 @@ function resolveDefaultOrchestrationEndpoint(env = process.env, platform = proce
|
|
|
47296
48684
|
}
|
|
47297
48685
|
const home = requireHome(env);
|
|
47298
48686
|
const configOverride = coreEnv("CONFIG", env);
|
|
47299
|
-
const configPath = typeof configOverride === "string" && configOverride.trim().length > 0 ? configOverride.trim() :
|
|
48687
|
+
const configPath = typeof configOverride === "string" && configOverride.trim().length > 0 ? configOverride.trim() : join6(coreHomeDir(home), "config.json");
|
|
47300
48688
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
47301
48689
|
return resolveOrchestrationEndpoint(runtimeDir, platform);
|
|
47302
48690
|
}
|
|
@@ -48833,14 +50221,14 @@ function resolveTemplateChoice(answer, names) {
|
|
|
48833
50221
|
init_plugin_home();
|
|
48834
50222
|
import { spawn as spawn4 } from "node:child_process";
|
|
48835
50223
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
48836
|
-
import { dirname as dirname8, join as
|
|
50224
|
+
import { dirname as dirname8, join as join12 } from "node:path";
|
|
48837
50225
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
48838
50226
|
|
|
48839
50227
|
// src/plugins/package-manager.ts
|
|
48840
50228
|
init_plugin_home();
|
|
48841
50229
|
import { spawn as spawn3 } from "node:child_process";
|
|
48842
50230
|
import { rm as rm4 } from "node:fs/promises";
|
|
48843
|
-
import { join as
|
|
50231
|
+
import { join as join8 } from "node:path";
|
|
48844
50232
|
function shellSpawnPlan(args) {
|
|
48845
50233
|
const shell = process.platform === "win32";
|
|
48846
50234
|
return { shell, args: shell ? args.map((arg) => `"${arg}"`) : args };
|
|
@@ -48888,7 +50276,7 @@ async function installPluginPackage(input) {
|
|
|
48888
50276
|
const packageManager = input.packageManager ?? await detectPackageManager();
|
|
48889
50277
|
await normalizePluginHomeManifest(input.pluginHome);
|
|
48890
50278
|
if (packageManager === "bun") {
|
|
48891
|
-
await rm4(
|
|
50279
|
+
await rm4(join8(input.pluginHome, "bun.lock"), { force: true }).catch(() => {});
|
|
48892
50280
|
}
|
|
48893
50281
|
const spec = input.version ? `${input.packageName}@${input.version}` : input.packageName;
|
|
48894
50282
|
if (packageManager === "bun") {
|
|
@@ -49180,7 +50568,7 @@ async function runInherit(command, args) {
|
|
|
49180
50568
|
async function readPackageName() {
|
|
49181
50569
|
try {
|
|
49182
50570
|
const here = dirname8(fileURLToPath3(import.meta.url));
|
|
49183
|
-
for (const candidate of [
|
|
50571
|
+
for (const candidate of [join12(here, "..", "package.json"), join12(here, "..", "..", "package.json")]) {
|
|
49184
50572
|
try {
|
|
49185
50573
|
const parsed = JSON.parse(await readFile9(candidate, "utf8"));
|
|
49186
50574
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
@@ -49835,7 +51223,7 @@ async function setChannelAccountEnabled(type, accountId, enabled, rawArgs, deps)
|
|
|
49835
51223
|
init_core_home();
|
|
49836
51224
|
init_plugin_home();
|
|
49837
51225
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
49838
|
-
import { isAbsolute, join as
|
|
51226
|
+
import { isAbsolute, join as join14, resolve } from "node:path";
|
|
49839
51227
|
init_plugin_loader();
|
|
49840
51228
|
init_validate_plugin();
|
|
49841
51229
|
init_plugin_doctor();
|
|
@@ -49866,7 +51254,7 @@ function looksLikePath(spec) {
|
|
|
49866
51254
|
}
|
|
49867
51255
|
async function readDependencyEntries2(pluginHome) {
|
|
49868
51256
|
try {
|
|
49869
|
-
const raw = await readFile11(
|
|
51257
|
+
const raw = await readFile11(join14(pluginHome, "package.json"), "utf8");
|
|
49870
51258
|
const parsed = JSON.parse(raw);
|
|
49871
51259
|
const out = {};
|
|
49872
51260
|
for (const [name, value] of Object.entries(parsed.dependencies ?? {})) {
|
|
@@ -49892,7 +51280,7 @@ async function resolveLocalPluginName(installSpec, pluginHome, namesBeforeInstal
|
|
|
49892
51280
|
return name;
|
|
49893
51281
|
}
|
|
49894
51282
|
try {
|
|
49895
|
-
const raw = await readFile11(
|
|
51283
|
+
const raw = await readFile11(join14(installSpec, "package.json"), "utf8");
|
|
49896
51284
|
const parsed = JSON.parse(raw);
|
|
49897
51285
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
49898
51286
|
return parsed.name.trim();
|
|
@@ -51034,7 +52422,7 @@ async function createCliScheduledTaskService() {
|
|
|
51034
52422
|
return new ScheduledTaskService(state, stateStore);
|
|
51035
52423
|
}
|
|
51036
52424
|
function resolveConfigPathForCurrentEnv() {
|
|
51037
|
-
return coreEnv("CONFIG") ??
|
|
52425
|
+
return coreEnv("CONFIG") ?? join25(coreHomeDir(requireHome2()), "config.json");
|
|
51038
52426
|
}
|
|
51039
52427
|
function resolveDaemonPathsForCurrentConfig() {
|
|
51040
52428
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
@@ -51093,7 +52481,7 @@ async function defaultRun(options = {}) {
|
|
|
51093
52481
|
const firstRunOnboarding = options.firstRunOnboarding ?? decodeFirstRunOnboarding(coreEnv("FIRST_RUN_ONBOARDING"));
|
|
51094
52482
|
await runConsole2(runtimePaths, {
|
|
51095
52483
|
buildApp: (paths) => buildApp2(paths, {
|
|
51096
|
-
defaultLoggingLevel: resolveCliEntryPath2().includes(`${
|
|
52484
|
+
defaultLoggingLevel: resolveCliEntryPath2().includes(`${sep2}src${sep2}`) ? "debug" : "info",
|
|
51097
52485
|
channel: channelRegistry
|
|
51098
52486
|
}),
|
|
51099
52487
|
beforeReady: firstRunOnboarding ? async (runtime) => {
|
|
@@ -51104,7 +52492,7 @@ async function defaultRun(options = {}) {
|
|
|
51104
52492
|
daemonRuntime,
|
|
51105
52493
|
...firstLockCreator ? {
|
|
51106
52494
|
consumerLockFactory: (runtime) => firstLockCreator.create({
|
|
51107
|
-
lockFilePath: `${daemonPaths.runtimeDir}${
|
|
52495
|
+
lockFilePath: `${daemonPaths.runtimeDir}${sep2}${firstLockCreator.channel.id}-consumer.lock.json`,
|
|
51108
52496
|
onDiagnostic: async (event, context) => {
|
|
51109
52497
|
await runtime.logger.info(`${firstLockCreator.channel.id}.consumer_lock.${event}`, `${firstLockCreator.channel.id} consumer lock diagnostic`, context);
|
|
51110
52498
|
}
|
|
@@ -51295,7 +52683,7 @@ async function defaultPromptSecret(message) {
|
|
|
51295
52683
|
process.stdout.write(message);
|
|
51296
52684
|
process.stdin.setRawMode(true);
|
|
51297
52685
|
process.stdin.resume();
|
|
51298
|
-
return await new Promise((
|
|
52686
|
+
return await new Promise((resolve4, reject) => {
|
|
51299
52687
|
const chunks = [];
|
|
51300
52688
|
let inEscape = false;
|
|
51301
52689
|
const cleanup = () => {
|
|
@@ -51326,7 +52714,7 @@ async function defaultPromptSecret(message) {
|
|
|
51326
52714
|
if (char === "\r" || char === `
|
|
51327
52715
|
`) {
|
|
51328
52716
|
cleanup();
|
|
51329
|
-
|
|
52717
|
+
resolve4(chunks.join(""));
|
|
51330
52718
|
return;
|
|
51331
52719
|
}
|
|
51332
52720
|
if (char === "" || char === "\b") {
|
|
@@ -51381,7 +52769,7 @@ function decodeFirstRunOnboarding(raw) {
|
|
|
51381
52769
|
return null;
|
|
51382
52770
|
}
|
|
51383
52771
|
function requireHome2() {
|
|
51384
|
-
const home = process.env.HOME ??
|
|
52772
|
+
const home = process.env.HOME ?? homedir17();
|
|
51385
52773
|
if (!home) {
|
|
51386
52774
|
throw new Error("Unable to resolve the current user home directory");
|
|
51387
52775
|
}
|
|
@@ -51405,7 +52793,7 @@ function safeDaemonLogPaths() {
|
|
|
51405
52793
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
51406
52794
|
const paths = resolveDaemonPathsForCurrentConfig();
|
|
51407
52795
|
return {
|
|
51408
|
-
appLog:
|
|
52796
|
+
appLog: join25(dirname14(configPath), "runtime", "app.log"),
|
|
51409
52797
|
stderrLog: paths.stderrLog
|
|
51410
52798
|
};
|
|
51411
52799
|
} catch {
|