@ganglion/xacpx 0.10.1 → 0.11.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 +126 -9
- package/dist/channels/types.d.ts +16 -0
- package/dist/cli.js +1688 -371
- package/dist/commands/handlers/agent-handler.d.ts +1 -1
- package/dist/commands/handlers/session-handler.d.ts +7 -4
- package/dist/commands/parse-command.d.ts +7 -0
- package/dist/commands/router-types.d.ts +7 -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 +59 -0
- package/dist/control/control-service.d.ts +147 -0
- package/dist/control/workspace-fs.d.ts +52 -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 +27 -1
- package/dist/weixin/agent/interface.d.ts +3 -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) {
|
|
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);
|
|
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) {
|
|
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);
|
|
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);
|
|
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) {
|
|
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);
|
|
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) {
|
|
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);
|
|
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);
|
|
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);
|
|
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) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan, onPlan)
|
|
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) {
|
|
24897
25182
|
session3.mcpCoordinatorSession ??= stableCoordinatorSession(session3.transportSession);
|
|
24898
25183
|
let done = false;
|
|
24899
25184
|
let abortRequested = false;
|
|
@@ -24950,7 +25235,8 @@ class CommandRouter {
|
|
|
24950
25235
|
...media ? { media } : {},
|
|
24951
25236
|
...reply ? { onSegment } : {},
|
|
24952
25237
|
...onToolEvent ? { onToolEvent } : {},
|
|
24953
|
-
...onThought ? { onThought } : {}
|
|
25238
|
+
...onThought ? { onThought } : {},
|
|
25239
|
+
...onPlan ? { onPlan } : {}
|
|
24954
25240
|
}));
|
|
24955
25241
|
} catch (error2) {
|
|
24956
25242
|
localOutcome = isAbortError2(error2) || abortRequested ? "aborted" : "error";
|
|
@@ -24969,6 +25255,20 @@ class CommandRouter {
|
|
|
24969
25255
|
async setModeTransportSession(session3, modeId) {
|
|
24970
25256
|
return await this.measureTransportCall("set_mode", session3, () => this.transport.setMode(session3, modeId));
|
|
24971
25257
|
}
|
|
25258
|
+
async setModelTransportSession(session3, modelId) {
|
|
25259
|
+
if (!this.transport.setModel) {
|
|
25260
|
+
throw new Error("the active transport does not support switching models");
|
|
25261
|
+
}
|
|
25262
|
+
const setModel = this.transport.setModel.bind(this.transport);
|
|
25263
|
+
return await this.measureTransportCall("set_model", session3, () => setModel(session3, modelId));
|
|
25264
|
+
}
|
|
25265
|
+
async getModelTransportSession(session3) {
|
|
25266
|
+
if (!this.transport.getSessionModel) {
|
|
25267
|
+
return { current: session3.model, available: [] };
|
|
25268
|
+
}
|
|
25269
|
+
const getSessionModel = this.transport.getSessionModel.bind(this.transport);
|
|
25270
|
+
return await this.measureTransportCall("get_model", session3, () => getSessionModel(session3));
|
|
25271
|
+
}
|
|
24972
25272
|
async cancelTransportSession(session3) {
|
|
24973
25273
|
return await this.measureTransportCall("cancel", session3, () => this.transport.cancel(session3));
|
|
24974
25274
|
}
|
|
@@ -25028,6 +25328,7 @@ function inferTransportKind(transport) {
|
|
|
25028
25328
|
}
|
|
25029
25329
|
var init_command_router = __esm(() => {
|
|
25030
25330
|
init_app_logger();
|
|
25331
|
+
init_resolve_agent_command();
|
|
25031
25332
|
init_acpx_session_index();
|
|
25032
25333
|
init_prompt_output();
|
|
25033
25334
|
init_parse_command();
|
|
@@ -25121,7 +25422,7 @@ class ConsoleAgent {
|
|
|
25121
25422
|
...m.fileName ? { fileName: m.fileName } : {}
|
|
25122
25423
|
})) : undefined;
|
|
25123
25424
|
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);
|
|
25425
|
+
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);
|
|
25125
25426
|
}
|
|
25126
25427
|
isKnownCommand(text) {
|
|
25127
25428
|
return isKnownXacpxCommandText(text);
|
|
@@ -28935,12 +29236,14 @@ class ScheduledTaskScheduler {
|
|
|
28935
29236
|
setIntervalFn;
|
|
28936
29237
|
clearIntervalFn;
|
|
28937
29238
|
dispatchTask;
|
|
29239
|
+
onSettled;
|
|
28938
29240
|
logger;
|
|
28939
29241
|
intervalHandle = null;
|
|
28940
29242
|
ticking = false;
|
|
28941
29243
|
constructor(service, deps) {
|
|
28942
29244
|
this.service = service;
|
|
28943
29245
|
this.dispatchTask = deps.dispatchTask;
|
|
29246
|
+
this.onSettled = deps.onSettled;
|
|
28944
29247
|
this.intervalMs = deps.intervalMs ?? 5000;
|
|
28945
29248
|
this.dispatchTimeoutMs = deps.dispatchTimeoutMs ?? DEFAULT_DISPATCH_TIMEOUT_MS;
|
|
28946
29249
|
this.setIntervalFn = deps.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
|
|
@@ -28985,6 +29288,7 @@ class ScheduledTaskScheduler {
|
|
|
28985
29288
|
});
|
|
28986
29289
|
try {
|
|
28987
29290
|
await this.service.markFailed(task.id, error2);
|
|
29291
|
+
this.notifySettled(task);
|
|
28988
29292
|
} catch (markError) {
|
|
28989
29293
|
await this.logger?.error("scheduled.dispatch.mark_failed", "markFailed threw; task state may be stale", {
|
|
28990
29294
|
taskId: task.id,
|
|
@@ -28995,6 +29299,7 @@ class ScheduledTaskScheduler {
|
|
|
28995
29299
|
}
|
|
28996
29300
|
try {
|
|
28997
29301
|
await this.service.markExecuted(task.id);
|
|
29302
|
+
this.notifySettled(task);
|
|
28998
29303
|
} catch (markError) {
|
|
28999
29304
|
await this.logger?.error("scheduled.dispatch.mark_executed_failed", "markExecuted threw after a successful dispatch; leaving task state for startup reconciliation", {
|
|
29000
29305
|
taskId: task.id,
|
|
@@ -29006,6 +29311,18 @@ class ScheduledTaskScheduler {
|
|
|
29006
29311
|
this.ticking = false;
|
|
29007
29312
|
}
|
|
29008
29313
|
}
|
|
29314
|
+
notifySettled(task) {
|
|
29315
|
+
if (!this.onSettled)
|
|
29316
|
+
return;
|
|
29317
|
+
try {
|
|
29318
|
+
this.onSettled(task);
|
|
29319
|
+
} catch (error2) {
|
|
29320
|
+
this.logger?.error("scheduled.on_settled.failed", "scheduled onSettled listener threw", {
|
|
29321
|
+
taskId: task.id,
|
|
29322
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
29323
|
+
});
|
|
29324
|
+
}
|
|
29325
|
+
}
|
|
29009
29326
|
async dispatchWithTimeout(task) {
|
|
29010
29327
|
const controller = new AbortController;
|
|
29011
29328
|
let timer;
|
|
@@ -29041,7 +29358,8 @@ function buildScheduledDispatchTask(deps) {
|
|
|
29041
29358
|
};
|
|
29042
29359
|
}
|
|
29043
29360
|
async function dispatchBound(task, abortSignal, deps) {
|
|
29044
|
-
const
|
|
29361
|
+
const internalAlias = deps.resolveAliasForChat ? await deps.resolveAliasForChat(task.chat_key, task.session_alias) : task.session_alias;
|
|
29362
|
+
const session3 = await deps.getSession(internalAlias);
|
|
29045
29363
|
if (!session3) {
|
|
29046
29364
|
throw new Error(`session "${task.session_alias}" not found for scheduled task`);
|
|
29047
29365
|
}
|
|
@@ -29050,6 +29368,7 @@ async function dispatchBound(task, abortSignal, deps) {
|
|
|
29050
29368
|
chatKey: task.chat_key,
|
|
29051
29369
|
taskId: task.id,
|
|
29052
29370
|
sessionAlias: task.session_alias,
|
|
29371
|
+
executeAt: task.execute_at,
|
|
29053
29372
|
noticeText,
|
|
29054
29373
|
promptText: task.message,
|
|
29055
29374
|
abortSignal,
|
|
@@ -29070,6 +29389,7 @@ async function dispatchTemp(task, abortSignal, deps) {
|
|
|
29070
29389
|
chatKey: task.chat_key,
|
|
29071
29390
|
taskId: task.id,
|
|
29072
29391
|
sessionAlias: task.session_alias,
|
|
29392
|
+
executeAt: task.execute_at,
|
|
29073
29393
|
sessionDescriptor: { alias, agent: task.agent, workspace: task.workspace, transportSession },
|
|
29074
29394
|
noticeText,
|
|
29075
29395
|
promptText: task.message,
|
|
@@ -29259,6 +29579,7 @@ class SessionService {
|
|
|
29259
29579
|
workspace: workspace3,
|
|
29260
29580
|
transport_session: transportSession,
|
|
29261
29581
|
transport_agent_command: sameAgentExisting?.transport_agent_command,
|
|
29582
|
+
model: sameAgentExisting?.model,
|
|
29262
29583
|
created_at: existing?.created_at ?? new Date().toISOString(),
|
|
29263
29584
|
last_used_at: new Date().toISOString()
|
|
29264
29585
|
});
|
|
@@ -29629,10 +29950,13 @@ class SessionService {
|
|
|
29629
29950
|
if (!workspaceConfig) {
|
|
29630
29951
|
throw new Error(`session "${session3.alias}" references workspace "${session3.workspace}", but that workspace is no longer registered`);
|
|
29631
29952
|
}
|
|
29953
|
+
const channelId = getChannelIdFromChatKey(session3.alias);
|
|
29954
|
+
const effectiveReplyMode = channelId === "relay" ? "stream" : undefined;
|
|
29632
29955
|
return {
|
|
29633
29956
|
alias: session3.alias,
|
|
29634
29957
|
agent: session3.agent,
|
|
29635
|
-
agentCommand: session3.transport_agent_command ??
|
|
29958
|
+
agentCommand: session3.transport_agent_command ?? resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, this.config.transport.preferLocalAgents !== false),
|
|
29959
|
+
model: session3.model ?? agentConfig.model,
|
|
29636
29960
|
workspace: session3.workspace,
|
|
29637
29961
|
transportSession: session3.transport_session,
|
|
29638
29962
|
source: session3.source,
|
|
@@ -29642,9 +29966,46 @@ class SessionService {
|
|
|
29642
29966
|
attachedAt: session3.attached_at,
|
|
29643
29967
|
modeId: session3.mode_id,
|
|
29644
29968
|
replyMode: session3.reply_mode,
|
|
29969
|
+
effectiveReplyMode,
|
|
29645
29970
|
cwd: workspaceConfig.cwd
|
|
29646
29971
|
};
|
|
29647
29972
|
}
|
|
29973
|
+
async setSessionModel(alias, modelId) {
|
|
29974
|
+
await this.mutate(async () => {
|
|
29975
|
+
const session3 = this.state.sessions[alias];
|
|
29976
|
+
if (!session3) {
|
|
29977
|
+
throw new Error(`session "${alias}" does not exist`);
|
|
29978
|
+
}
|
|
29979
|
+
const normalized = modelId?.trim();
|
|
29980
|
+
if (normalized) {
|
|
29981
|
+
session3.model = normalized;
|
|
29982
|
+
} else {
|
|
29983
|
+
delete session3.model;
|
|
29984
|
+
}
|
|
29985
|
+
session3.last_used_at = new Date(this.now()).toISOString();
|
|
29986
|
+
await this.persist();
|
|
29987
|
+
});
|
|
29988
|
+
}
|
|
29989
|
+
async setCurrentSessionModel(chatKey, modelId) {
|
|
29990
|
+
await this.mutate(async () => {
|
|
29991
|
+
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
29992
|
+
if (!currentAlias) {
|
|
29993
|
+
throw new Error("no current session selected");
|
|
29994
|
+
}
|
|
29995
|
+
const session3 = this.state.sessions[currentAlias];
|
|
29996
|
+
if (!session3) {
|
|
29997
|
+
throw new Error("no current session selected");
|
|
29998
|
+
}
|
|
29999
|
+
const normalized = modelId?.trim();
|
|
30000
|
+
if (normalized) {
|
|
30001
|
+
session3.model = normalized;
|
|
30002
|
+
} else {
|
|
30003
|
+
delete session3.model;
|
|
30004
|
+
}
|
|
30005
|
+
session3.last_used_at = new Date(this.now()).toISOString();
|
|
30006
|
+
await this.persist();
|
|
30007
|
+
});
|
|
30008
|
+
}
|
|
29648
30009
|
async setSessionTransportAgentCommand(alias, transportAgentCommand) {
|
|
29649
30010
|
await this.mutate(async () => {
|
|
29650
30011
|
const session3 = this.state.sessions[alias];
|
|
@@ -29689,6 +30050,7 @@ class SessionService {
|
|
|
29689
30050
|
attached_at: native ? now : undefined,
|
|
29690
30051
|
...normalizedTransportAgentCommand ? { transport_agent_command: normalizedTransportAgentCommand } : sameAgentExisting?.transport_agent_command ? { transport_agent_command: sameAgentExisting.transport_agent_command } : {},
|
|
29691
30052
|
mode_id: sameAgentExisting?.mode_id,
|
|
30053
|
+
model: sameAgentExisting?.model,
|
|
29692
30054
|
reply_mode: sameAgentExisting?.reply_mode,
|
|
29693
30055
|
created_at: existingSession?.created_at ?? now,
|
|
29694
30056
|
last_used_at: now
|
|
@@ -29717,6 +30079,7 @@ class SessionService {
|
|
|
29717
30079
|
}
|
|
29718
30080
|
}
|
|
29719
30081
|
var init_session_service = __esm(() => {
|
|
30082
|
+
init_resolve_agent_command();
|
|
29720
30083
|
init_i18n();
|
|
29721
30084
|
init_channel_scope();
|
|
29722
30085
|
});
|
|
@@ -29740,6 +30103,13 @@ function createActiveTurnRegistry() {
|
|
|
29740
30103
|
},
|
|
29741
30104
|
isActive(chatKey, alias) {
|
|
29742
30105
|
return byChat.get(chatKey)?.has(alias) ?? false;
|
|
30106
|
+
},
|
|
30107
|
+
isActiveAnywhere(alias) {
|
|
30108
|
+
for (const set2 of byChat.values()) {
|
|
30109
|
+
if (set2.has(alias))
|
|
30110
|
+
return true;
|
|
30111
|
+
}
|
|
30112
|
+
return false;
|
|
29743
30113
|
}
|
|
29744
30114
|
};
|
|
29745
30115
|
}
|
|
@@ -29848,6 +30218,7 @@ var init_command_hints = __esm(() => {
|
|
|
29848
30218
|
config: "/config",
|
|
29849
30219
|
orchestration: "/delegate",
|
|
29850
30220
|
mode: "/mode",
|
|
30221
|
+
model: "/model",
|
|
29851
30222
|
replymode: "/replymode",
|
|
29852
30223
|
status: "/status",
|
|
29853
30224
|
cancel: "/cancel",
|
|
@@ -29966,7 +30337,8 @@ async function runConsole(paths, deps) {
|
|
|
29966
30337
|
perfTracer: runtime.perfTracer,
|
|
29967
30338
|
commandHints: listXacpxCommandHints(),
|
|
29968
30339
|
coreVersion: XACPX_CORE_VERSION,
|
|
29969
|
-
locale: getLocale()
|
|
30340
|
+
locale: getLocale(),
|
|
30341
|
+
control: runtime.control
|
|
29970
30342
|
});
|
|
29971
30343
|
channelStartPromise.catch(() => {});
|
|
29972
30344
|
let channelStartSettled = false;
|
|
@@ -30102,6 +30474,10 @@ function encodeBridgePromptThoughtEvent(event) {
|
|
|
30102
30474
|
return `${JSON.stringify(event)}
|
|
30103
30475
|
`;
|
|
30104
30476
|
}
|
|
30477
|
+
function encodeBridgePromptPlanEvent(event) {
|
|
30478
|
+
return `${JSON.stringify(event)}
|
|
30479
|
+
`;
|
|
30480
|
+
}
|
|
30105
30481
|
function encodeBridgeSessionProgressEvent(event) {
|
|
30106
30482
|
return `${JSON.stringify(event)}
|
|
30107
30483
|
`;
|
|
@@ -30111,8 +30487,228 @@ function encodeBridgeSessionNoteEvent(event) {
|
|
|
30111
30487
|
`;
|
|
30112
30488
|
}
|
|
30113
30489
|
|
|
30114
|
-
// src/
|
|
30490
|
+
// src/process/spawn-command.ts
|
|
30491
|
+
function resolveSpawnCommand(command, args) {
|
|
30492
|
+
if (SCRIPT_FILE_PATTERN.test(command)) {
|
|
30493
|
+
return {
|
|
30494
|
+
command: process.execPath,
|
|
30495
|
+
args: [command, ...args]
|
|
30496
|
+
};
|
|
30497
|
+
}
|
|
30498
|
+
return { command, args };
|
|
30499
|
+
}
|
|
30500
|
+
var SCRIPT_FILE_PATTERN;
|
|
30501
|
+
var init_spawn_command = __esm(() => {
|
|
30502
|
+
SCRIPT_FILE_PATTERN = /\.(c|m)?js$/i;
|
|
30503
|
+
});
|
|
30504
|
+
|
|
30505
|
+
// src/transport/acpx-queue-owner-launcher.ts
|
|
30506
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
30115
30507
|
import { spawn as spawn7 } from "node:child_process";
|
|
30508
|
+
import { readFile as readFile13, unlink } from "node:fs/promises";
|
|
30509
|
+
import { homedir as homedir8 } from "node:os";
|
|
30510
|
+
import { join as join17 } from "node:path";
|
|
30511
|
+
function buildXacpxMcpServerSpec(input) {
|
|
30512
|
+
const { command, args } = splitCommandLine(input.xacpxCommand);
|
|
30513
|
+
return {
|
|
30514
|
+
name: "xacpx",
|
|
30515
|
+
type: "stdio",
|
|
30516
|
+
command,
|
|
30517
|
+
args: [
|
|
30518
|
+
...args,
|
|
30519
|
+
"mcp-stdio",
|
|
30520
|
+
"--coordinator-session",
|
|
30521
|
+
input.coordinatorSession,
|
|
30522
|
+
...input.sourceHandle ? ["--source-handle", input.sourceHandle] : ["--internal-session-tools"]
|
|
30523
|
+
]
|
|
30524
|
+
};
|
|
30525
|
+
}
|
|
30526
|
+
function buildQueueOwnerPayload(input) {
|
|
30527
|
+
return {
|
|
30528
|
+
sessionId: input.sessionId,
|
|
30529
|
+
permissionMode: input.permissionMode,
|
|
30530
|
+
nonInteractivePermissions: input.nonInteractivePermissions,
|
|
30531
|
+
ttlMs: input.ttlMs ?? 300000,
|
|
30532
|
+
maxQueueDepth: input.maxQueueDepth ?? 16,
|
|
30533
|
+
...Number.isFinite(input.promptRetries) ? { promptRetries: input.promptRetries } : {},
|
|
30534
|
+
...input.sessionOptions ? { sessionOptions: input.sessionOptions } : {},
|
|
30535
|
+
mcpServers: input.mcpServers
|
|
30536
|
+
};
|
|
30537
|
+
}
|
|
30538
|
+
|
|
30539
|
+
class AcpxQueueOwnerLauncher {
|
|
30540
|
+
acpxCommand;
|
|
30541
|
+
xacpxCommand;
|
|
30542
|
+
spawnOwner;
|
|
30543
|
+
terminateOwner;
|
|
30544
|
+
baseEnv;
|
|
30545
|
+
ttlMs;
|
|
30546
|
+
maxQueueDepth;
|
|
30547
|
+
launchLocks = new Map;
|
|
30548
|
+
constructor(options) {
|
|
30549
|
+
this.acpxCommand = options.acpxCommand;
|
|
30550
|
+
this.xacpxCommand = options.xacpxCommand ?? resolveDefaultXacpxCommand(options.baseEnv ?? process.env);
|
|
30551
|
+
this.spawnOwner = options.spawnOwner ?? defaultQueueOwnerSpawner;
|
|
30552
|
+
this.terminateOwner = options.terminateOwner ?? createDefaultQueueOwnerTerminator(options.acpxCommand);
|
|
30553
|
+
this.baseEnv = options.baseEnv ?? process.env;
|
|
30554
|
+
this.ttlMs = options.ttlMs;
|
|
30555
|
+
this.maxQueueDepth = options.maxQueueDepth;
|
|
30556
|
+
}
|
|
30557
|
+
async launch(input) {
|
|
30558
|
+
const key = input.acpxRecordId;
|
|
30559
|
+
const previous = this.launchLocks.get(key) ?? Promise.resolve();
|
|
30560
|
+
const next = previous.then(() => this.doLaunch(input), () => this.doLaunch(input));
|
|
30561
|
+
const tracked = next.catch(() => {});
|
|
30562
|
+
this.launchLocks.set(key, tracked);
|
|
30563
|
+
tracked.finally(() => {
|
|
30564
|
+
if (this.launchLocks.get(key) === tracked) {
|
|
30565
|
+
this.launchLocks.delete(key);
|
|
30566
|
+
}
|
|
30567
|
+
});
|
|
30568
|
+
return next;
|
|
30569
|
+
}
|
|
30570
|
+
async doLaunch(input) {
|
|
30571
|
+
await this.terminateOwner(input.acpxRecordId);
|
|
30572
|
+
const payload = buildQueueOwnerPayload({
|
|
30573
|
+
sessionId: input.acpxRecordId,
|
|
30574
|
+
permissionMode: input.permissionMode,
|
|
30575
|
+
nonInteractivePermissions: input.nonInteractivePermissions,
|
|
30576
|
+
ttlMs: this.ttlMs,
|
|
30577
|
+
maxQueueDepth: this.maxQueueDepth,
|
|
30578
|
+
...input.sessionOptions ? { sessionOptions: input.sessionOptions } : {},
|
|
30579
|
+
mcpServers: [buildXacpxMcpServerSpec({
|
|
30580
|
+
xacpxCommand: this.xacpxCommand,
|
|
30581
|
+
coordinatorSession: input.coordinatorSession,
|
|
30582
|
+
...input.sourceHandle ? { sourceHandle: input.sourceHandle } : {}
|
|
30583
|
+
})]
|
|
30584
|
+
});
|
|
30585
|
+
const spawnSpec = resolveSpawnCommand(this.acpxCommand, ["__queue-owner"]);
|
|
30586
|
+
await this.spawnOwner(spawnSpec.command, spawnSpec.args, {
|
|
30587
|
+
env: {
|
|
30588
|
+
...stringEnv(this.baseEnv),
|
|
30589
|
+
XACPX_LANG: getLocale(),
|
|
30590
|
+
ACPX_QUEUE_OWNER_PAYLOAD: JSON.stringify(payload)
|
|
30591
|
+
}
|
|
30592
|
+
});
|
|
30593
|
+
}
|
|
30594
|
+
}
|
|
30595
|
+
function splitCommandLine(value) {
|
|
30596
|
+
const parts = [];
|
|
30597
|
+
let current = "";
|
|
30598
|
+
let quote = null;
|
|
30599
|
+
let escaping = false;
|
|
30600
|
+
for (const char of value) {
|
|
30601
|
+
if (escaping) {
|
|
30602
|
+
current += char;
|
|
30603
|
+
escaping = false;
|
|
30604
|
+
continue;
|
|
30605
|
+
}
|
|
30606
|
+
if (char === "\\" && quote !== "'") {
|
|
30607
|
+
escaping = true;
|
|
30608
|
+
continue;
|
|
30609
|
+
}
|
|
30610
|
+
if (quote) {
|
|
30611
|
+
if (char === quote) {
|
|
30612
|
+
quote = null;
|
|
30613
|
+
} else {
|
|
30614
|
+
current += char;
|
|
30615
|
+
}
|
|
30616
|
+
continue;
|
|
30617
|
+
}
|
|
30618
|
+
if (char === "'" || char === '"') {
|
|
30619
|
+
quote = char;
|
|
30620
|
+
continue;
|
|
30621
|
+
}
|
|
30622
|
+
if (/\s/.test(char)) {
|
|
30623
|
+
if (current.length > 0) {
|
|
30624
|
+
parts.push(current);
|
|
30625
|
+
current = "";
|
|
30626
|
+
}
|
|
30627
|
+
continue;
|
|
30628
|
+
}
|
|
30629
|
+
current += char;
|
|
30630
|
+
}
|
|
30631
|
+
if (escaping) {
|
|
30632
|
+
current += "\\";
|
|
30633
|
+
}
|
|
30634
|
+
if (quote) {
|
|
30635
|
+
throw new Error("xacpx MCP command has an unterminated quote");
|
|
30636
|
+
}
|
|
30637
|
+
if (current.length > 0) {
|
|
30638
|
+
parts.push(current);
|
|
30639
|
+
}
|
|
30640
|
+
if (parts.length === 0) {
|
|
30641
|
+
throw new Error("xacpx MCP command must not be empty");
|
|
30642
|
+
}
|
|
30643
|
+
return { command: parts[0], args: parts.slice(1) };
|
|
30644
|
+
}
|
|
30645
|
+
function stringEnv(env) {
|
|
30646
|
+
return Object.fromEntries(Object.entries(env).filter((entry) => typeof entry[1] === "string"));
|
|
30647
|
+
}
|
|
30648
|
+
async function defaultQueueOwnerSpawner(command, args, options) {
|
|
30649
|
+
await new Promise((resolve3, reject) => {
|
|
30650
|
+
const child = spawn7(command, args, {
|
|
30651
|
+
detached: true,
|
|
30652
|
+
stdio: "ignore",
|
|
30653
|
+
env: options.env,
|
|
30654
|
+
windowsHide: true
|
|
30655
|
+
});
|
|
30656
|
+
child.once("error", reject);
|
|
30657
|
+
child.once("spawn", () => {
|
|
30658
|
+
child.unref();
|
|
30659
|
+
resolve3();
|
|
30660
|
+
});
|
|
30661
|
+
});
|
|
30662
|
+
}
|
|
30663
|
+
function createDefaultQueueOwnerTerminator(_acpxCommand) {
|
|
30664
|
+
return async (sessionId) => {
|
|
30665
|
+
await terminateAcpxQueueOwner(sessionId);
|
|
30666
|
+
};
|
|
30667
|
+
}
|
|
30668
|
+
async function terminateAcpxQueueOwner(sessionId) {
|
|
30669
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
30670
|
+
let owner;
|
|
30671
|
+
try {
|
|
30672
|
+
owner = JSON.parse(await readFile13(lockPath, "utf8"));
|
|
30673
|
+
} catch {
|
|
30674
|
+
return;
|
|
30675
|
+
}
|
|
30676
|
+
if (typeof owner.pid === "number" && Number.isInteger(owner.pid) && owner.pid > 0) {
|
|
30677
|
+
await terminateProcessTree(owner.pid, { detachedProcessGroup: true });
|
|
30678
|
+
}
|
|
30679
|
+
await unlink(lockPath).catch(() => {});
|
|
30680
|
+
}
|
|
30681
|
+
function queueLockFilePath(sessionId) {
|
|
30682
|
+
return join17(homedir8(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
|
|
30683
|
+
}
|
|
30684
|
+
function shortHash(value, length) {
|
|
30685
|
+
return createHash3("sha256").update(value).digest("hex").slice(0, length);
|
|
30686
|
+
}
|
|
30687
|
+
function resolveDefaultXacpxCommand(env) {
|
|
30688
|
+
const cliCommand = coreEnv("CLI_COMMAND", env);
|
|
30689
|
+
if (cliCommand?.trim()) {
|
|
30690
|
+
return cliCommand.trim();
|
|
30691
|
+
}
|
|
30692
|
+
const daemonArg0 = coreEnv("DAEMON_ARG0", env);
|
|
30693
|
+
if (daemonArg0?.trim()) {
|
|
30694
|
+
return `${quoteCommandPart(process.execPath)} ${quoteCommandPart(daemonArg0.trim())}`;
|
|
30695
|
+
}
|
|
30696
|
+
if (process.argv[1]) {
|
|
30697
|
+
return `${quoteCommandPart(process.execPath)} ${quoteCommandPart(process.argv[1])}`;
|
|
30698
|
+
}
|
|
30699
|
+
return "xacpx";
|
|
30700
|
+
}
|
|
30701
|
+
function quoteCommandPart(value) {
|
|
30702
|
+
return quoteIfNeeded(value);
|
|
30703
|
+
}
|
|
30704
|
+
var init_acpx_queue_owner_launcher = __esm(() => {
|
|
30705
|
+
init_spawn_command();
|
|
30706
|
+
init_terminate_process_tree();
|
|
30707
|
+
init_i18n();
|
|
30708
|
+
});
|
|
30709
|
+
|
|
30710
|
+
// src/transport/acpx-bridge/acpx-bridge-client.ts
|
|
30711
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
30116
30712
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
30117
30713
|
import { createInterface } from "node:readline";
|
|
30118
30714
|
|
|
@@ -30179,6 +30775,11 @@ class AcpxBridgeClient {
|
|
|
30179
30775
|
type: "prompt.thought",
|
|
30180
30776
|
text: message.text
|
|
30181
30777
|
});
|
|
30778
|
+
} else if (message.event === "prompt.plan") {
|
|
30779
|
+
pending.onEvent?.({
|
|
30780
|
+
type: "prompt.plan",
|
|
30781
|
+
entries: message.entries
|
|
30782
|
+
});
|
|
30182
30783
|
} else if (message.event === "session.progress") {
|
|
30183
30784
|
pending.onEvent?.({
|
|
30184
30785
|
type: "session.progress",
|
|
@@ -30228,6 +30829,7 @@ class AcpxBridgeClient {
|
|
|
30228
30829
|
function buildBridgeSpawnEnv(options = {}) {
|
|
30229
30830
|
return {
|
|
30230
30831
|
XACPX_LANG: getLocale(),
|
|
30832
|
+
XACPX_CLI_COMMAND: options.cliCommand ?? resolveDefaultXacpxCommand(process.env),
|
|
30231
30833
|
XACPX_BRIDGE_ACPX_COMMAND: options.acpxCommand ?? "acpx",
|
|
30232
30834
|
XACPX_BRIDGE_PERMISSION_MODE: options.permissionMode ?? "approve-all",
|
|
30233
30835
|
XACPX_BRIDGE_NON_INTERACTIVE_PERMISSIONS: options.nonInteractivePermissions ?? "deny",
|
|
@@ -30254,7 +30856,7 @@ async function spawnAcpxBridgeClient(options = {}) {
|
|
|
30254
30856
|
execPath: process.execPath,
|
|
30255
30857
|
bridgeEntryPath
|
|
30256
30858
|
});
|
|
30257
|
-
const child =
|
|
30859
|
+
const child = spawn8(spawnSpec.command, spawnSpec.args, {
|
|
30258
30860
|
cwd: options.cwd ?? process.cwd(),
|
|
30259
30861
|
env: {
|
|
30260
30862
|
...process.env,
|
|
@@ -30306,6 +30908,7 @@ var init_acpx_bridge_client = __esm(() => {
|
|
|
30306
30908
|
init_errors();
|
|
30307
30909
|
init_terminate_process_tree();
|
|
30308
30910
|
init_i18n();
|
|
30911
|
+
init_acpx_queue_owner_launcher();
|
|
30309
30912
|
});
|
|
30310
30913
|
|
|
30311
30914
|
// src/transport/segment-aggregator.ts
|
|
@@ -30508,6 +31111,64 @@ ${headsUpText}`);
|
|
|
30508
31111
|
}
|
|
30509
31112
|
};
|
|
30510
31113
|
}
|
|
31114
|
+
function createVerbatimReplySink(reply) {
|
|
31115
|
+
let pendingError;
|
|
31116
|
+
const inFlight = new Set;
|
|
31117
|
+
const send = (text) => {
|
|
31118
|
+
const p = reply(text).catch((err) => {
|
|
31119
|
+
if (isQuotaDeferredError(err)) {
|
|
31120
|
+
if (!pendingError) {
|
|
31121
|
+
pendingError = err;
|
|
31122
|
+
}
|
|
31123
|
+
return;
|
|
31124
|
+
}
|
|
31125
|
+
});
|
|
31126
|
+
inFlight.add(p);
|
|
31127
|
+
p.finally(() => {
|
|
31128
|
+
inFlight.delete(p);
|
|
31129
|
+
});
|
|
31130
|
+
};
|
|
31131
|
+
return {
|
|
31132
|
+
feedSegment(segment) {
|
|
31133
|
+
if (segment.length === 0)
|
|
31134
|
+
return;
|
|
31135
|
+
send(segment);
|
|
31136
|
+
},
|
|
31137
|
+
finalize() {
|
|
31138
|
+
return { trailing: "", overflowCount: 0 };
|
|
31139
|
+
},
|
|
31140
|
+
getOverflowCount() {
|
|
31141
|
+
return 0;
|
|
31142
|
+
},
|
|
31143
|
+
getPendingError() {
|
|
31144
|
+
return pendingError;
|
|
31145
|
+
},
|
|
31146
|
+
async drain(opts) {
|
|
31147
|
+
const timeoutMs = opts?.timeoutMs ?? 30000;
|
|
31148
|
+
const deadline = Date.now() + timeoutMs;
|
|
31149
|
+
while (inFlight.size > 0) {
|
|
31150
|
+
const remaining = deadline - Date.now();
|
|
31151
|
+
if (remaining <= 0)
|
|
31152
|
+
return;
|
|
31153
|
+
let timer;
|
|
31154
|
+
const timeout = new Promise((resolve3) => {
|
|
31155
|
+
timer = setTimeout(resolve3, remaining);
|
|
31156
|
+
});
|
|
31157
|
+
try {
|
|
31158
|
+
await Promise.race([
|
|
31159
|
+
Promise.allSettled(Array.from(inFlight)).then(() => {
|
|
31160
|
+
return;
|
|
31161
|
+
}),
|
|
31162
|
+
timeout
|
|
31163
|
+
]);
|
|
31164
|
+
} finally {
|
|
31165
|
+
if (timer)
|
|
31166
|
+
clearTimeout(timer);
|
|
31167
|
+
}
|
|
31168
|
+
}
|
|
31169
|
+
}
|
|
31170
|
+
};
|
|
31171
|
+
}
|
|
30511
31172
|
function buildOverflowSummary(overflowCount) {
|
|
30512
31173
|
if (overflowCount <= 0)
|
|
30513
31174
|
return;
|
|
@@ -30565,7 +31226,8 @@ class AcpxBridgeTransport {
|
|
|
30565
31226
|
});
|
|
30566
31227
|
}
|
|
30567
31228
|
async prompt(session3, text, reply, replyContext, options) {
|
|
30568
|
-
const
|
|
31229
|
+
const streamMode = (session3.effectiveReplyMode ?? session3.replyMode) === "stream";
|
|
31230
|
+
const sink = reply ? streamMode ? createVerbatimReplySink(reply) : createQuotaGatedReplySink({
|
|
30569
31231
|
reply,
|
|
30570
31232
|
...replyContext ? { replyContext } : {}
|
|
30571
31233
|
}) : null;
|
|
@@ -30575,6 +31237,8 @@ class AcpxBridgeTransport {
|
|
|
30575
31237
|
let toolEventChain = Promise.resolve();
|
|
30576
31238
|
let thoughtError;
|
|
30577
31239
|
let thoughtChain = Promise.resolve();
|
|
31240
|
+
let planError;
|
|
31241
|
+
let planChain = Promise.resolve();
|
|
30578
31242
|
let toolEventMode = resolveToolEventMode(options);
|
|
30579
31243
|
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
30580
31244
|
toolEventMode = "text";
|
|
@@ -30617,10 +31281,21 @@ class AcpxBridgeTransport {
|
|
|
30617
31281
|
}
|
|
30618
31282
|
return;
|
|
30619
31283
|
}
|
|
31284
|
+
if (event.type === "prompt.plan") {
|
|
31285
|
+
const onPlan = options?.onPlan;
|
|
31286
|
+
if (onPlan) {
|
|
31287
|
+
const entries = event.entries;
|
|
31288
|
+
planChain = planChain.then(() => onPlan(entries)).catch((error2) => {
|
|
31289
|
+
planError ??= error2;
|
|
31290
|
+
});
|
|
31291
|
+
}
|
|
31292
|
+
return;
|
|
31293
|
+
}
|
|
30620
31294
|
});
|
|
30621
31295
|
await segmentChain;
|
|
30622
31296
|
await toolEventChain;
|
|
30623
31297
|
await thoughtChain;
|
|
31298
|
+
await planChain;
|
|
30624
31299
|
if (sink) {
|
|
30625
31300
|
const { overflowCount } = sink.finalize();
|
|
30626
31301
|
await sink.drain({ timeoutMs: 30000 });
|
|
@@ -30638,6 +31313,9 @@ class AcpxBridgeTransport {
|
|
|
30638
31313
|
if (thoughtError) {
|
|
30639
31314
|
throw thoughtError;
|
|
30640
31315
|
}
|
|
31316
|
+
if (planError) {
|
|
31317
|
+
throw planError;
|
|
31318
|
+
}
|
|
30641
31319
|
return { text: summary ? `${summary}
|
|
30642
31320
|
|
|
30643
31321
|
${result.text}` : "" };
|
|
@@ -30651,6 +31329,9 @@ ${result.text}` : "" };
|
|
|
30651
31329
|
if (thoughtError) {
|
|
30652
31330
|
throw thoughtError;
|
|
30653
31331
|
}
|
|
31332
|
+
if (planError) {
|
|
31333
|
+
throw planError;
|
|
31334
|
+
}
|
|
30654
31335
|
return result;
|
|
30655
31336
|
}
|
|
30656
31337
|
async setMode(session3, modeId) {
|
|
@@ -30659,6 +31340,15 @@ ${result.text}` : "" };
|
|
|
30659
31340
|
modeId
|
|
30660
31341
|
});
|
|
30661
31342
|
}
|
|
31343
|
+
async setModel(session3, modelId) {
|
|
31344
|
+
await this.client.request("setModel", {
|
|
31345
|
+
...this.toParams({ ...session3, model: modelId }),
|
|
31346
|
+
modelId
|
|
31347
|
+
});
|
|
31348
|
+
}
|
|
31349
|
+
async getSessionModel(session3) {
|
|
31350
|
+
return await this.client.request("getSessionModel", this.toParams(session3));
|
|
31351
|
+
}
|
|
30662
31352
|
async cancel(session3) {
|
|
30663
31353
|
return await this.client.request("cancel", this.toParams(session3));
|
|
30664
31354
|
}
|
|
@@ -30687,7 +31377,8 @@ ${result.text}` : "" };
|
|
|
30687
31377
|
name: session3.transportSession,
|
|
30688
31378
|
mcpCoordinatorSession: session3.mcpCoordinatorSession,
|
|
30689
31379
|
mcpSourceHandle: session3.mcpSourceHandle,
|
|
30690
|
-
replyMode: session3.replyMode ?? "verbose"
|
|
31380
|
+
replyMode: session3.effectiveReplyMode ?? session3.replyMode ?? "verbose",
|
|
31381
|
+
...session3.model?.trim() ? { model: session3.model.trim() } : {}
|
|
30691
31382
|
};
|
|
30692
31383
|
}
|
|
30693
31384
|
}
|
|
@@ -30695,21 +31386,6 @@ var init_acpx_bridge_transport = __esm(() => {
|
|
|
30695
31386
|
init_quota_gated_reply_sink();
|
|
30696
31387
|
});
|
|
30697
31388
|
|
|
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
31389
|
// src/transport/prompt-media.ts
|
|
30714
31390
|
import { mkdtemp, open as open4, rm as rm9, writeFile as writeFile7 } from "node:fs/promises";
|
|
30715
31391
|
import { tmpdir as defaultTmpdir } from "node:os";
|
|
@@ -30860,6 +31536,8 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
30860
31536
|
let toolEventMode;
|
|
30861
31537
|
let onToolEvent;
|
|
30862
31538
|
let onThought;
|
|
31539
|
+
let onPlan;
|
|
31540
|
+
let rawStream = false;
|
|
30863
31541
|
if (options === undefined) {
|
|
30864
31542
|
toolEventMode = "text";
|
|
30865
31543
|
onToolEvent = undefined;
|
|
@@ -30869,6 +31547,8 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
30869
31547
|
} else {
|
|
30870
31548
|
onToolEvent = options.onToolEvent;
|
|
30871
31549
|
onThought = options.onThought;
|
|
31550
|
+
onPlan = options.onPlan;
|
|
31551
|
+
rawStream = options.rawStream ?? false;
|
|
30872
31552
|
toolEventMode = resolveToolEventMode({
|
|
30873
31553
|
toolEventMode: options.mode,
|
|
30874
31554
|
onToolEvent
|
|
@@ -30882,13 +31562,15 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
30882
31562
|
formatToolCalls,
|
|
30883
31563
|
emittedToolCallIds: new Set,
|
|
30884
31564
|
toolEventMode,
|
|
31565
|
+
rawStream,
|
|
30885
31566
|
onToolEvent,
|
|
30886
31567
|
onThought,
|
|
31568
|
+
onPlan,
|
|
30887
31569
|
finalize() {
|
|
30888
31570
|
if (this.pendingLine.trim().length > 0) {
|
|
30889
31571
|
parseStreamingChunks(this, this.pendingLine);
|
|
30890
31572
|
}
|
|
30891
|
-
const remaining = this.buffer.trim();
|
|
31573
|
+
const remaining = this.rawStream ? this.buffer : this.buffer.trim();
|
|
30892
31574
|
this.buffer = "";
|
|
30893
31575
|
this.pendingLine = "";
|
|
30894
31576
|
return remaining;
|
|
@@ -30920,9 +31602,9 @@ function parseStreamingChunks(state, line) {
|
|
|
30920
31602
|
const update = event.params?.update;
|
|
30921
31603
|
if (!update)
|
|
30922
31604
|
return;
|
|
30923
|
-
if (
|
|
31605
|
+
if (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") {
|
|
30924
31606
|
const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
|
|
30925
|
-
const wantsText = state.toolEventMode === "text" || state.toolEventMode === "both";
|
|
31607
|
+
const wantsText = (state.toolEventMode === "text" || state.toolEventMode === "both") && state.formatToolCalls;
|
|
30926
31608
|
if (wantsStructured && state.onToolEvent) {
|
|
30927
31609
|
const toolEvent = buildToolUseEvent(update);
|
|
30928
31610
|
if (toolEvent)
|
|
@@ -30942,6 +31624,12 @@ function parseStreamingChunks(state, line) {
|
|
|
30942
31624
|
}
|
|
30943
31625
|
return;
|
|
30944
31626
|
}
|
|
31627
|
+
if (update.sessionUpdate === "plan") {
|
|
31628
|
+
const entries = Array.isArray(update.entries) ? update.entries.filter((x) => !!x && typeof x === "object" && typeof x.content === "string" && typeof x.status === "string") : [];
|
|
31629
|
+
if (entries.length > 0)
|
|
31630
|
+
state.onPlan?.(entries);
|
|
31631
|
+
return;
|
|
31632
|
+
}
|
|
30945
31633
|
const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
|
|
30946
31634
|
if (isThoughtChunk) {
|
|
30947
31635
|
const chunk2 = update.content.text;
|
|
@@ -30958,6 +31646,8 @@ function parseStreamingChunks(state, line) {
|
|
|
30958
31646
|
if (chunk.length === 0)
|
|
30959
31647
|
return;
|
|
30960
31648
|
state.buffer += chunk;
|
|
31649
|
+
if (state.rawStream)
|
|
31650
|
+
return;
|
|
30961
31651
|
let boundary;
|
|
30962
31652
|
while ((boundary = state.buffer.indexOf(`
|
|
30963
31653
|
|
|
@@ -31135,12 +31825,12 @@ var init_streaming_prompt = __esm(() => {
|
|
|
31135
31825
|
|
|
31136
31826
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
31137
31827
|
import { chmod as chmodFs } from "node:fs/promises";
|
|
31138
|
-
import { dirname as dirname11, join as
|
|
31828
|
+
import { dirname as dirname11, join as join18 } from "node:path";
|
|
31139
31829
|
function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
|
|
31140
31830
|
if (platform === "win32") {
|
|
31141
31831
|
return null;
|
|
31142
31832
|
}
|
|
31143
|
-
return
|
|
31833
|
+
return join18(dirname11(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
|
|
31144
31834
|
}
|
|
31145
31835
|
async function ensureNodePtyHelperExecutable(helperPath, chmod5 = chmodFs) {
|
|
31146
31836
|
if (!helperPath) {
|
|
@@ -31157,210 +31847,6 @@ async function ensureNodePtyHelperExecutable(helperPath, chmod5 = chmodFs) {
|
|
|
31157
31847
|
}
|
|
31158
31848
|
var init_node_pty_helper = () => {};
|
|
31159
31849
|
|
|
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
31850
|
// src/transport/permission-mode-flag.ts
|
|
31365
31851
|
function permissionModeToFlag(permissionMode) {
|
|
31366
31852
|
switch (permissionMode) {
|
|
@@ -31578,13 +32064,15 @@ class AcpxCliTransport {
|
|
|
31578
32064
|
const structuredPrompt = await createStructuredPromptFile(text, options?.media);
|
|
31579
32065
|
const args = this.buildPromptArgs(session3, text, structuredPrompt?.filePath);
|
|
31580
32066
|
try {
|
|
31581
|
-
if (reply || options?.onSegment || options?.onToolEvent || options?.onThought) {
|
|
31582
|
-
const
|
|
32067
|
+
if (reply || options?.onSegment || options?.onToolEvent || options?.onThought || options?.onPlan) {
|
|
32068
|
+
const effectiveReplyMode = session3.effectiveReplyMode ?? session3.replyMode;
|
|
32069
|
+
const formatToolCalls = (effectiveReplyMode ?? "verbose") === "verbose";
|
|
32070
|
+
const rawStream = effectiveReplyMode === "stream";
|
|
31583
32071
|
let toolEventMode = resolveToolEventMode(options);
|
|
31584
32072
|
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
31585
32073
|
toolEventMode = "text";
|
|
31586
32074
|
}
|
|
31587
|
-
const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent, options?.onThought);
|
|
32075
|
+
const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent, options?.onThought, options?.onPlan, rawStream);
|
|
31588
32076
|
const baseText = getPromptText(result2);
|
|
31589
32077
|
if (!reply) {
|
|
31590
32078
|
return { text: baseText };
|
|
@@ -31610,6 +32098,34 @@ ${baseText}` : "" };
|
|
|
31610
32098
|
modeId
|
|
31611
32099
|
]));
|
|
31612
32100
|
}
|
|
32101
|
+
async setModel(session3, modelId) {
|
|
32102
|
+
await this.run(this.buildArgs({ ...session3, model: modelId }, [
|
|
32103
|
+
"set",
|
|
32104
|
+
"-s",
|
|
32105
|
+
session3.transportSession,
|
|
32106
|
+
"model",
|
|
32107
|
+
modelId
|
|
32108
|
+
]));
|
|
32109
|
+
}
|
|
32110
|
+
async getSessionModel(session3) {
|
|
32111
|
+
const prefix = ["--format", "json", "--cwd", session3.cwd, ...this.buildPermissionArgs()];
|
|
32112
|
+
const tail2 = ["status", "-s", session3.transportSession];
|
|
32113
|
+
const args = session3.agentCommand ? [...prefix, "--agent", session3.agentCommand, ...tail2] : [...prefix, session3.agent, ...tail2];
|
|
32114
|
+
const result = await this.runCommandWithTimeout(this.runCommand, args);
|
|
32115
|
+
if (result.code !== 0) {
|
|
32116
|
+
const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
|
|
32117
|
+
throw new Error(detail);
|
|
32118
|
+
}
|
|
32119
|
+
try {
|
|
32120
|
+
const json = JSON.parse(result.stdout);
|
|
32121
|
+
return {
|
|
32122
|
+
current: typeof json.model === "string" ? json.model : undefined,
|
|
32123
|
+
available: Array.isArray(json.availableModels) ? json.availableModels.filter((m) => typeof m === "string") : []
|
|
32124
|
+
};
|
|
32125
|
+
} catch {
|
|
32126
|
+
return { available: [] };
|
|
32127
|
+
}
|
|
32128
|
+
}
|
|
31613
32129
|
async cancel(session3) {
|
|
31614
32130
|
const output = await this.run(this.buildArgs(session3, [
|
|
31615
32131
|
"cancel",
|
|
@@ -31673,7 +32189,8 @@ ${baseText}` : "" };
|
|
|
31673
32189
|
coordinatorSession: session3.mcpCoordinatorSession,
|
|
31674
32190
|
...session3.mcpSourceHandle ? { sourceHandle: session3.mcpSourceHandle } : {},
|
|
31675
32191
|
permissionMode: this.permissionMode,
|
|
31676
|
-
nonInteractivePermissions: this.nonInteractivePermissions
|
|
32192
|
+
nonInteractivePermissions: this.nonInteractivePermissions,
|
|
32193
|
+
...session3.model?.trim() ? { sessionOptions: { model: session3.model.trim() } } : {}
|
|
31677
32194
|
});
|
|
31678
32195
|
}
|
|
31679
32196
|
async readSessionRecord(session3) {
|
|
@@ -31741,13 +32258,13 @@ ${baseText}` : "" };
|
|
|
31741
32258
|
})
|
|
31742
32259
|
]);
|
|
31743
32260
|
}
|
|
31744
|
-
async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent, onThought) {
|
|
32261
|
+
async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent, onThought, onPlan, rawStream = false) {
|
|
31745
32262
|
const hooks = this.streamingHooks;
|
|
31746
32263
|
const doSpawn = hooks.spawnPrompt ?? ((cmd, spawnArgs) => spawn9(cmd, spawnArgs, { stdio: ["ignore", "pipe", "pipe"] }));
|
|
31747
32264
|
const setIntervalFn = hooks.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
|
|
31748
32265
|
const clearIntervalFn = hooks.clearIntervalFn ?? ((timer) => clearInterval(timer));
|
|
31749
|
-
const maxSegmentWaitMs = hooks.maxSegmentWaitMs ?? 30000;
|
|
31750
|
-
const flushCheckIntervalMs = hooks.flushCheckIntervalMs ?? 5000;
|
|
32266
|
+
const maxSegmentWaitMs = hooks.maxSegmentWaitMs ?? (rawStream ? 200 : 30000);
|
|
32267
|
+
const flushCheckIntervalMs = hooks.flushCheckIntervalMs ?? (rawStream ? 80 : 5000);
|
|
31751
32268
|
const now = hooks.now ?? (() => Date.now());
|
|
31752
32269
|
return await new Promise((resolve3, reject) => {
|
|
31753
32270
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
@@ -31761,10 +32278,14 @@ ${baseText}` : "" };
|
|
|
31761
32278
|
let toolEventError;
|
|
31762
32279
|
let thoughtChain = Promise.resolve();
|
|
31763
32280
|
let thoughtError;
|
|
32281
|
+
let planChain = Promise.resolve();
|
|
32282
|
+
let planError;
|
|
31764
32283
|
const userOnToolEvent = onToolEvent;
|
|
31765
32284
|
const userOnThought = onThought;
|
|
32285
|
+
const userOnPlan = onPlan;
|
|
31766
32286
|
const state = createStreamingPromptState(formatToolCalls, {
|
|
31767
32287
|
mode: toolEventMode,
|
|
32288
|
+
rawStream,
|
|
31768
32289
|
...userOnToolEvent ? {
|
|
31769
32290
|
onToolEvent: (event) => {
|
|
31770
32291
|
toolEventChain = toolEventChain.then(() => userOnToolEvent(event)).catch((error2) => {
|
|
@@ -31778,9 +32299,16 @@ ${baseText}` : "" };
|
|
|
31778
32299
|
thoughtError ??= error2;
|
|
31779
32300
|
});
|
|
31780
32301
|
}
|
|
32302
|
+
} : {},
|
|
32303
|
+
...userOnPlan ? {
|
|
32304
|
+
onPlan: (entries) => {
|
|
32305
|
+
planChain = planChain.then(() => userOnPlan(entries)).catch((error2) => {
|
|
32306
|
+
planError ??= error2;
|
|
32307
|
+
});
|
|
32308
|
+
}
|
|
31781
32309
|
} : {}
|
|
31782
32310
|
});
|
|
31783
|
-
const sink = reply ? createQuotaGatedReplySink({
|
|
32311
|
+
const sink = reply ? rawStream ? createVerbatimReplySink(reply) : createQuotaGatedReplySink({
|
|
31784
32312
|
reply,
|
|
31785
32313
|
...replyContext ? { replyContext } : {}
|
|
31786
32314
|
}) : null;
|
|
@@ -31794,7 +32322,7 @@ ${baseText}` : "" };
|
|
|
31794
32322
|
lastReplyAt = now();
|
|
31795
32323
|
};
|
|
31796
32324
|
const flushBuffer = () => {
|
|
31797
|
-
const remaining = state.buffer.trim();
|
|
32325
|
+
const remaining = rawStream ? state.buffer : state.buffer.trim();
|
|
31798
32326
|
if (remaining.length > 0) {
|
|
31799
32327
|
state.buffer = "";
|
|
31800
32328
|
feedSegment(remaining);
|
|
@@ -31831,7 +32359,8 @@ ${baseText}` : "" };
|
|
|
31831
32359
|
sink?.drain({ timeoutMs: 30000 }) ?? Promise.resolve(),
|
|
31832
32360
|
segmentChain,
|
|
31833
32361
|
toolEventChain,
|
|
31834
|
-
thoughtChain
|
|
32362
|
+
thoughtChain,
|
|
32363
|
+
planChain
|
|
31835
32364
|
]).then(() => {
|
|
31836
32365
|
const deferred = sink?.getPendingError();
|
|
31837
32366
|
if (deferred) {
|
|
@@ -31850,6 +32379,10 @@ ${baseText}` : "" };
|
|
|
31850
32379
|
reject(thoughtError);
|
|
31851
32380
|
return;
|
|
31852
32381
|
}
|
|
32382
|
+
if (planError) {
|
|
32383
|
+
reject(planError);
|
|
32384
|
+
return;
|
|
32385
|
+
}
|
|
31853
32386
|
resolve3({
|
|
31854
32387
|
result: { code: code ?? 1, stdout: stdout2, stderr },
|
|
31855
32388
|
overflowCount
|
|
@@ -31866,7 +32399,8 @@ ${baseText}` : "" };
|
|
|
31866
32399
|
"quiet",
|
|
31867
32400
|
"--cwd",
|
|
31868
32401
|
session3.cwd,
|
|
31869
|
-
...this.buildPermissionArgs()
|
|
32402
|
+
...this.buildPermissionArgs(),
|
|
32403
|
+
...this.buildModelArgs(session3)
|
|
31870
32404
|
];
|
|
31871
32405
|
if (session3.agentCommand) {
|
|
31872
32406
|
return [...prefix, "--agent", session3.agentCommand, ...tail2];
|
|
@@ -31888,6 +32422,7 @@ ${baseText}` : "" };
|
|
|
31888
32422
|
"--cwd",
|
|
31889
32423
|
session3.cwd,
|
|
31890
32424
|
...this.buildPermissionArgs(),
|
|
32425
|
+
...this.buildModelArgs(session3),
|
|
31891
32426
|
...this.buildQueueOwnerTtlArgs()
|
|
31892
32427
|
];
|
|
31893
32428
|
const tail2 = promptFile ? ["prompt", "-s", session3.transportSession, "--file", promptFile] : ["prompt", "-s", session3.transportSession, text];
|
|
@@ -31896,6 +32431,10 @@ ${baseText}` : "" };
|
|
|
31896
32431
|
}
|
|
31897
32432
|
return [...prefix, session3.agent, ...tail2];
|
|
31898
32433
|
}
|
|
32434
|
+
buildModelArgs(session3) {
|
|
32435
|
+
const model = session3.model?.trim();
|
|
32436
|
+
return model ? ["--model", model] : [];
|
|
32437
|
+
}
|
|
31899
32438
|
buildQueueOwnerTtlArgs() {
|
|
31900
32439
|
if (typeof this.queueOwnerTtlSeconds !== "number" || !Number.isFinite(this.queueOwnerTtlSeconds)) {
|
|
31901
32440
|
return [];
|
|
@@ -32092,7 +32631,7 @@ function workerBindingReapTargets(orchestration3, config4) {
|
|
|
32092
32631
|
if (!cwd) {
|
|
32093
32632
|
continue;
|
|
32094
32633
|
}
|
|
32095
|
-
const agentCommand =
|
|
32634
|
+
const agentCommand = resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, config4.transport.preferLocalAgents !== false);
|
|
32096
32635
|
targets.push({
|
|
32097
32636
|
agent: binding.targetAgent,
|
|
32098
32637
|
...agentCommand ? { agentCommand } : {},
|
|
@@ -32102,7 +32641,9 @@ function workerBindingReapTargets(orchestration3, config4) {
|
|
|
32102
32641
|
}
|
|
32103
32642
|
return targets;
|
|
32104
32643
|
}
|
|
32105
|
-
var init_collect_reap_targets = () => {
|
|
32644
|
+
var init_collect_reap_targets = __esm(() => {
|
|
32645
|
+
init_resolve_agent_command();
|
|
32646
|
+
});
|
|
32106
32647
|
|
|
32107
32648
|
// src/channels/channel-registry.ts
|
|
32108
32649
|
var exports_channel_registry = {};
|
|
@@ -32389,6 +32930,730 @@ var init_quota_manager = __esm(() => {
|
|
|
32389
32930
|
DEFAULT_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
32390
32931
|
});
|
|
32391
32932
|
|
|
32933
|
+
// src/control/control-event-bus.ts
|
|
32934
|
+
function createControlEventBus(logger2) {
|
|
32935
|
+
const listeners = new Set;
|
|
32936
|
+
return {
|
|
32937
|
+
subscribe(listener) {
|
|
32938
|
+
listeners.add(listener);
|
|
32939
|
+
return () => {
|
|
32940
|
+
listeners.delete(listener);
|
|
32941
|
+
};
|
|
32942
|
+
},
|
|
32943
|
+
emit(event) {
|
|
32944
|
+
for (const listener of [...listeners]) {
|
|
32945
|
+
try {
|
|
32946
|
+
listener(event);
|
|
32947
|
+
} catch (error2) {
|
|
32948
|
+
logger2?.error("control.event_listener_failed", "control event listener threw", {
|
|
32949
|
+
eventType: event.type,
|
|
32950
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
32951
|
+
});
|
|
32952
|
+
}
|
|
32953
|
+
}
|
|
32954
|
+
}
|
|
32955
|
+
};
|
|
32956
|
+
}
|
|
32957
|
+
|
|
32958
|
+
// src/transport/native-session-history.ts
|
|
32959
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
32960
|
+
import { homedir as homedir9 } from "node:os";
|
|
32961
|
+
import { join as join19 } from "node:path";
|
|
32962
|
+
function classifyToolKind(name) {
|
|
32963
|
+
const n = name.toLowerCase();
|
|
32964
|
+
if (/(^|[^a-z])(read|cat|view|open)([^a-z]|$)/.test(n))
|
|
32965
|
+
return "read";
|
|
32966
|
+
if (/(grep|search|find|glob|ripgrep|rg)/.test(n))
|
|
32967
|
+
return "search";
|
|
32968
|
+
if (/(edit|write|apply|patch|replace|create)/.test(n))
|
|
32969
|
+
return "edit";
|
|
32970
|
+
if (/(bash|shell|exec|run|terminal|command)/.test(n))
|
|
32971
|
+
return "execute";
|
|
32972
|
+
if (/(think|reason|plan)/.test(n))
|
|
32973
|
+
return "think";
|
|
32974
|
+
return "other";
|
|
32975
|
+
}
|
|
32976
|
+
function textOfUserContent(content) {
|
|
32977
|
+
if (!Array.isArray(content))
|
|
32978
|
+
return "";
|
|
32979
|
+
const out = [];
|
|
32980
|
+
for (const c of content) {
|
|
32981
|
+
if (c && typeof c === "object") {
|
|
32982
|
+
const o = c;
|
|
32983
|
+
if (typeof o.Text === "string")
|
|
32984
|
+
out.push(o.Text);
|
|
32985
|
+
else if (o.Mention && typeof o.Mention.content === "string")
|
|
32986
|
+
out.push(String(o.Mention.content));
|
|
32987
|
+
else if (o.Image)
|
|
32988
|
+
out.push("[image]");
|
|
32989
|
+
else if (o.Audio)
|
|
32990
|
+
out.push("[audio]");
|
|
32991
|
+
}
|
|
32992
|
+
}
|
|
32993
|
+
return out.join(`
|
|
32994
|
+
`);
|
|
32995
|
+
}
|
|
32996
|
+
function toolResultText(result) {
|
|
32997
|
+
if (!result || typeof result !== "object")
|
|
32998
|
+
return { isError: false };
|
|
32999
|
+
const r = result;
|
|
33000
|
+
const isError = r.is_error === true;
|
|
33001
|
+
if (typeof r.output === "string")
|
|
33002
|
+
return { text: r.output, isError };
|
|
33003
|
+
const content = r.content;
|
|
33004
|
+
if (content && typeof content.Text === "string")
|
|
33005
|
+
return { text: content.Text, isError };
|
|
33006
|
+
return { isError };
|
|
33007
|
+
}
|
|
33008
|
+
function toolUseEventOf(toolUse, result) {
|
|
33009
|
+
const id = typeof toolUse.id === "string" ? toolUse.id : "";
|
|
33010
|
+
const name = typeof toolUse.name === "string" ? toolUse.name : "tool";
|
|
33011
|
+
const rawInput = toolUse.input ?? (typeof toolUse.raw_input === "string" ? safeParse4(toolUse.raw_input) : undefined);
|
|
33012
|
+
const res = toolResultText(result);
|
|
33013
|
+
return {
|
|
33014
|
+
toolCallId: id,
|
|
33015
|
+
toolName: name,
|
|
33016
|
+
kind: classifyToolKind(name),
|
|
33017
|
+
...rawInput !== undefined ? { rawInput } : {},
|
|
33018
|
+
...res.text !== undefined ? { rawOutput: res.text } : {},
|
|
33019
|
+
status: result ? res.isError ? "error" : "success" : "success"
|
|
33020
|
+
};
|
|
33021
|
+
}
|
|
33022
|
+
function safeParse4(s) {
|
|
33023
|
+
try {
|
|
33024
|
+
return JSON.parse(s);
|
|
33025
|
+
} catch {
|
|
33026
|
+
return s;
|
|
33027
|
+
}
|
|
33028
|
+
}
|
|
33029
|
+
function mapAcpxMessagesToHistory(raw) {
|
|
33030
|
+
if (!Array.isArray(raw))
|
|
33031
|
+
return [];
|
|
33032
|
+
const out = [];
|
|
33033
|
+
for (const msg of raw) {
|
|
33034
|
+
if (msg === "Resume" || !msg || typeof msg !== "object")
|
|
33035
|
+
continue;
|
|
33036
|
+
const m = msg;
|
|
33037
|
+
if (m.User && typeof m.User === "object") {
|
|
33038
|
+
const text = textOfUserContent(m.User.content);
|
|
33039
|
+
out.push({ role: "user", text });
|
|
33040
|
+
continue;
|
|
33041
|
+
}
|
|
33042
|
+
if (m.Agent && typeof m.Agent === "object") {
|
|
33043
|
+
const agent3 = m.Agent;
|
|
33044
|
+
const toolResults = agent3.tool_results ?? {};
|
|
33045
|
+
const parts = [];
|
|
33046
|
+
const textChunks = [];
|
|
33047
|
+
for (const c of Array.isArray(agent3.content) ? agent3.content : []) {
|
|
33048
|
+
if (!c || typeof c !== "object")
|
|
33049
|
+
continue;
|
|
33050
|
+
const o = c;
|
|
33051
|
+
if (typeof o.Text === "string") {
|
|
33052
|
+
parts.push({ kind: "text", text: o.Text });
|
|
33053
|
+
textChunks.push(o.Text);
|
|
33054
|
+
} else if (o.Thinking && typeof o.Thinking.text === "string")
|
|
33055
|
+
parts.push({ kind: "reasoning", text: String(o.Thinking.text) });
|
|
33056
|
+
else if (typeof o.RedactedThinking === "string")
|
|
33057
|
+
parts.push({ kind: "reasoning", text: "[redacted reasoning]" });
|
|
33058
|
+
else if (o.ToolUse && typeof o.ToolUse === "object") {
|
|
33059
|
+
const tu = o.ToolUse;
|
|
33060
|
+
const result = typeof tu.id === "string" ? toolResults[tu.id] : undefined;
|
|
33061
|
+
parts.push({ kind: "tool", tool: toolUseEventOf(tu, result) });
|
|
33062
|
+
}
|
|
33063
|
+
}
|
|
33064
|
+
out.push({ role: "agent", text: textChunks.join(`
|
|
33065
|
+
|
|
33066
|
+
`), ...parts.length ? { parts } : {} });
|
|
33067
|
+
}
|
|
33068
|
+
}
|
|
33069
|
+
return out;
|
|
33070
|
+
}
|
|
33071
|
+
async function readNativeSessionHistory(opts) {
|
|
33072
|
+
try {
|
|
33073
|
+
const dir = opts.sessionsDir ?? join19(opts.homeDir ?? homedir9(), ".acpx", "sessions");
|
|
33074
|
+
const indexRaw = await readFile14(join19(dir, "index.json"), "utf8").catch(() => null);
|
|
33075
|
+
if (!indexRaw)
|
|
33076
|
+
return [];
|
|
33077
|
+
const index = JSON.parse(indexRaw);
|
|
33078
|
+
const candidates = (index.entries ?? []).filter((e) => e.acpSessionId === opts.agentSessionId && (!opts.agentCommand || !e.agentCommand || e.agentCommand === opts.agentCommand));
|
|
33079
|
+
let best = [];
|
|
33080
|
+
for (const entry of candidates) {
|
|
33081
|
+
if (!entry.file)
|
|
33082
|
+
continue;
|
|
33083
|
+
const recRaw = await readFile14(join19(dir, entry.file), "utf8").catch(() => null);
|
|
33084
|
+
if (!recRaw)
|
|
33085
|
+
continue;
|
|
33086
|
+
const record3 = JSON.parse(recRaw);
|
|
33087
|
+
const mapped = mapAcpxMessagesToHistory(record3.messages);
|
|
33088
|
+
if (mapped.length > best.length)
|
|
33089
|
+
best = mapped;
|
|
33090
|
+
}
|
|
33091
|
+
return best;
|
|
33092
|
+
} catch {
|
|
33093
|
+
return [];
|
|
33094
|
+
}
|
|
33095
|
+
}
|
|
33096
|
+
var init_native_session_history = () => {};
|
|
33097
|
+
|
|
33098
|
+
// src/control/workspace-fs.ts
|
|
33099
|
+
import { execFile } from "node:child_process";
|
|
33100
|
+
import { promisify } from "node:util";
|
|
33101
|
+
import { homedir as homedir10 } from "node:os";
|
|
33102
|
+
import { readdir as readdir3, realpath, stat as stat3, open as open5 } from "node:fs/promises";
|
|
33103
|
+
import { isAbsolute as isAbsolute3, relative, resolve as resolve3, sep } from "node:path";
|
|
33104
|
+
function expandHome2(p) {
|
|
33105
|
+
if (p === "~")
|
|
33106
|
+
return homedir10();
|
|
33107
|
+
if (p.startsWith("~/") || p.startsWith("~" + sep))
|
|
33108
|
+
return resolve3(homedir10(), p.slice(2));
|
|
33109
|
+
return p;
|
|
33110
|
+
}
|
|
33111
|
+
|
|
33112
|
+
class WorkspaceFs {
|
|
33113
|
+
listWorkspaces;
|
|
33114
|
+
constructor(listWorkspaces) {
|
|
33115
|
+
this.listWorkspaces = listWorkspaces;
|
|
33116
|
+
}
|
|
33117
|
+
async resolve(workspace3, relPath) {
|
|
33118
|
+
const ref = this.listWorkspaces().find((w) => w.name === workspace3);
|
|
33119
|
+
if (!ref)
|
|
33120
|
+
throw new Error("unknown-workspace");
|
|
33121
|
+
if (relPath && isAbsolute3(relPath))
|
|
33122
|
+
throw new Error("path-must-be-relative");
|
|
33123
|
+
let root;
|
|
33124
|
+
try {
|
|
33125
|
+
root = await realpath(expandHome2(ref.cwd));
|
|
33126
|
+
} catch {
|
|
33127
|
+
throw new Error("workspace-root-missing");
|
|
33128
|
+
}
|
|
33129
|
+
const requested = resolve3(root, relPath ?? ".");
|
|
33130
|
+
let abs;
|
|
33131
|
+
try {
|
|
33132
|
+
abs = await realpath(requested);
|
|
33133
|
+
} catch {
|
|
33134
|
+
throw new Error("not-found");
|
|
33135
|
+
}
|
|
33136
|
+
if (abs !== root && !abs.startsWith(root + sep))
|
|
33137
|
+
throw new Error("path-escapes-workspace");
|
|
33138
|
+
const rel = abs === root ? "" : relative(root, abs).split(sep).join("/");
|
|
33139
|
+
return { root, abs, rel };
|
|
33140
|
+
}
|
|
33141
|
+
async listDirectory(workspace3, relPath) {
|
|
33142
|
+
const { abs, rel } = await this.resolve(workspace3, relPath);
|
|
33143
|
+
const dirents = await readdir3(abs, { withFileTypes: true });
|
|
33144
|
+
const entries = [];
|
|
33145
|
+
for (const d of dirents.slice(0, MAX_ENTRIES)) {
|
|
33146
|
+
if (d.isDirectory()) {
|
|
33147
|
+
entries.push({ name: d.name, type: "dir" });
|
|
33148
|
+
} else if (d.isFile()) {
|
|
33149
|
+
let size;
|
|
33150
|
+
try {
|
|
33151
|
+
size = (await stat3(resolve3(abs, d.name))).size;
|
|
33152
|
+
} catch {
|
|
33153
|
+
size = undefined;
|
|
33154
|
+
}
|
|
33155
|
+
entries.push({ name: d.name, type: "file", size });
|
|
33156
|
+
}
|
|
33157
|
+
}
|
|
33158
|
+
entries.sort((a, b) => a.type !== b.type ? a.type === "dir" ? -1 : 1 : a.name.localeCompare(b.name));
|
|
33159
|
+
return { workspace: workspace3, path: rel, entries };
|
|
33160
|
+
}
|
|
33161
|
+
async readFile(workspace3, relPath) {
|
|
33162
|
+
const { abs, rel } = await this.resolve(workspace3, relPath);
|
|
33163
|
+
const info = await stat3(abs);
|
|
33164
|
+
if (!info.isFile())
|
|
33165
|
+
throw new Error("not-a-file");
|
|
33166
|
+
const fh = await open5(abs, "r");
|
|
33167
|
+
try {
|
|
33168
|
+
const buf = Buffer.alloc(Math.min(info.size, FILE_READ_CAP));
|
|
33169
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
|
|
33170
|
+
const slice = buf.subarray(0, bytesRead);
|
|
33171
|
+
const binary = slice.includes(0);
|
|
33172
|
+
return {
|
|
33173
|
+
workspace: workspace3,
|
|
33174
|
+
path: rel,
|
|
33175
|
+
content: binary ? "" : slice.toString("utf8"),
|
|
33176
|
+
size: info.size,
|
|
33177
|
+
truncated: info.size > FILE_READ_CAP,
|
|
33178
|
+
binary
|
|
33179
|
+
};
|
|
33180
|
+
} finally {
|
|
33181
|
+
await fh.close();
|
|
33182
|
+
}
|
|
33183
|
+
}
|
|
33184
|
+
async search(workspace3, query) {
|
|
33185
|
+
const { root } = await this.resolve(workspace3, undefined);
|
|
33186
|
+
const needle = query.trim().toLowerCase();
|
|
33187
|
+
const matches = [];
|
|
33188
|
+
if (!needle)
|
|
33189
|
+
return { workspace: workspace3, query, matches, truncated: false };
|
|
33190
|
+
let scanned = 0;
|
|
33191
|
+
let truncated = false;
|
|
33192
|
+
const queue = [root];
|
|
33193
|
+
while (queue.length) {
|
|
33194
|
+
const dir = queue.shift();
|
|
33195
|
+
let dirents;
|
|
33196
|
+
try {
|
|
33197
|
+
dirents = await readdir3(dir, { withFileTypes: true });
|
|
33198
|
+
} catch {
|
|
33199
|
+
continue;
|
|
33200
|
+
}
|
|
33201
|
+
for (const d of dirents) {
|
|
33202
|
+
if (++scanned > SEARCH_MAX_SCAN) {
|
|
33203
|
+
truncated = true;
|
|
33204
|
+
break;
|
|
33205
|
+
}
|
|
33206
|
+
if (d.isSymbolicLink())
|
|
33207
|
+
continue;
|
|
33208
|
+
if (d.isDirectory()) {
|
|
33209
|
+
if (!SEARCH_SKIP_DIRS.has(d.name))
|
|
33210
|
+
queue.push(resolve3(dir, d.name));
|
|
33211
|
+
} else if (d.isFile()) {
|
|
33212
|
+
const rel = relative(root, resolve3(dir, d.name)).split(sep).join("/");
|
|
33213
|
+
if (rel.toLowerCase().includes(needle)) {
|
|
33214
|
+
matches.push(rel);
|
|
33215
|
+
if (matches.length >= SEARCH_MAX_RESULTS) {
|
|
33216
|
+
truncated = true;
|
|
33217
|
+
break;
|
|
33218
|
+
}
|
|
33219
|
+
}
|
|
33220
|
+
}
|
|
33221
|
+
}
|
|
33222
|
+
if (truncated)
|
|
33223
|
+
break;
|
|
33224
|
+
}
|
|
33225
|
+
matches.sort();
|
|
33226
|
+
return { workspace: workspace3, query, matches, truncated };
|
|
33227
|
+
}
|
|
33228
|
+
async gitDiff(workspace3, relPath) {
|
|
33229
|
+
const { root, rel } = await this.resolve(workspace3, relPath);
|
|
33230
|
+
try {
|
|
33231
|
+
await execFileAsync("git", ["-C", root, "rev-parse", "--is-inside-work-tree"], { maxBuffer: GIT_MAX_BUFFER });
|
|
33232
|
+
} catch {
|
|
33233
|
+
throw new Error("not-a-git-repo");
|
|
33234
|
+
}
|
|
33235
|
+
const files = [];
|
|
33236
|
+
try {
|
|
33237
|
+
const { stdout: stdout2 } = await execFileAsync("git", ["-C", root, "status", "--porcelain"], { maxBuffer: GIT_MAX_BUFFER });
|
|
33238
|
+
for (const line of stdout2.split(`
|
|
33239
|
+
`)) {
|
|
33240
|
+
if (!line)
|
|
33241
|
+
continue;
|
|
33242
|
+
const status = line.slice(0, 2);
|
|
33243
|
+
let path15 = line.slice(3);
|
|
33244
|
+
const arrow = path15.indexOf(" -> ");
|
|
33245
|
+
if (arrow >= 0)
|
|
33246
|
+
path15 = path15.slice(arrow + 4);
|
|
33247
|
+
files.push({ path: path15, status });
|
|
33248
|
+
}
|
|
33249
|
+
} catch {}
|
|
33250
|
+
const diffArgs = (base) => ["-C", root, ...base, ...rel ? ["--", rel] : []];
|
|
33251
|
+
let diff = "";
|
|
33252
|
+
try {
|
|
33253
|
+
diff = (await execFileAsync("git", diffArgs(["diff", "HEAD"]), { maxBuffer: GIT_MAX_BUFFER })).stdout;
|
|
33254
|
+
} catch {
|
|
33255
|
+
try {
|
|
33256
|
+
diff = (await execFileAsync("git", diffArgs(["diff"]), { maxBuffer: GIT_MAX_BUFFER })).stdout;
|
|
33257
|
+
} catch {
|
|
33258
|
+
diff = "";
|
|
33259
|
+
}
|
|
33260
|
+
}
|
|
33261
|
+
const truncated = diff.length > DIFF_CAP;
|
|
33262
|
+
return { workspace: workspace3, files, diff: truncated ? diff.slice(0, DIFF_CAP) : diff, truncated };
|
|
33263
|
+
}
|
|
33264
|
+
}
|
|
33265
|
+
var execFileAsync, MAX_ENTRIES = 2000, FILE_READ_CAP, DIFF_CAP, GIT_MAX_BUFFER, SEARCH_MAX_RESULTS = 200, SEARCH_MAX_SCAN = 20000, SEARCH_SKIP_DIRS;
|
|
33266
|
+
var init_workspace_fs = __esm(() => {
|
|
33267
|
+
execFileAsync = promisify(execFile);
|
|
33268
|
+
FILE_READ_CAP = 256 * 1024;
|
|
33269
|
+
DIFF_CAP = 512 * 1024;
|
|
33270
|
+
GIT_MAX_BUFFER = 32 * 1024 * 1024;
|
|
33271
|
+
SEARCH_SKIP_DIRS = new Set([".git", "node_modules"]);
|
|
33272
|
+
});
|
|
33273
|
+
|
|
33274
|
+
// src/control/control-service.ts
|
|
33275
|
+
class ControlService {
|
|
33276
|
+
deps;
|
|
33277
|
+
constructor(deps) {
|
|
33278
|
+
this.deps = deps;
|
|
33279
|
+
}
|
|
33280
|
+
workspaceFs = new WorkspaceFs(() => this.deps.workspaces.list().map((w) => ({ name: w.name, cwd: w.cwd })));
|
|
33281
|
+
listDirectory(workspace3, path15) {
|
|
33282
|
+
return this.workspaceFs.listDirectory(workspace3, path15);
|
|
33283
|
+
}
|
|
33284
|
+
readWorkspaceFile(workspace3, path15) {
|
|
33285
|
+
return this.workspaceFs.readFile(workspace3, path15);
|
|
33286
|
+
}
|
|
33287
|
+
workspaceGitDiff(workspace3, path15) {
|
|
33288
|
+
return this.workspaceFs.gitDiff(workspace3, path15);
|
|
33289
|
+
}
|
|
33290
|
+
searchWorkspace(workspace3, query) {
|
|
33291
|
+
return this.workspaceFs.search(workspace3, query);
|
|
33292
|
+
}
|
|
33293
|
+
async getSessionModel(chatKey, alias) {
|
|
33294
|
+
const session3 = await this.resolveControlSession(chatKey, alias);
|
|
33295
|
+
if (!session3)
|
|
33296
|
+
return { available: [] };
|
|
33297
|
+
if (!this.deps.transport.getSessionModel)
|
|
33298
|
+
return { current: session3.model, available: [] };
|
|
33299
|
+
return await this.deps.transport.getSessionModel(session3);
|
|
33300
|
+
}
|
|
33301
|
+
async setSessionModel(chatKey, alias, modelId) {
|
|
33302
|
+
const session3 = await this.resolveControlSession(chatKey, alias);
|
|
33303
|
+
if (!session3)
|
|
33304
|
+
throw new Error("session not found");
|
|
33305
|
+
if (!this.deps.transport.setModel)
|
|
33306
|
+
throw new Error("the active transport does not support switching models");
|
|
33307
|
+
await this.deps.transport.setModel(session3, modelId);
|
|
33308
|
+
await this.deps.sessions.setSessionModel(session3.alias, modelId);
|
|
33309
|
+
}
|
|
33310
|
+
async resolveControlSession(chatKey, alias) {
|
|
33311
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33312
|
+
return await this.deps.sessions.getSession(internalAlias);
|
|
33313
|
+
}
|
|
33314
|
+
get events() {
|
|
33315
|
+
return this.deps.events;
|
|
33316
|
+
}
|
|
33317
|
+
listSessions(chatKey) {
|
|
33318
|
+
const channelId = getChannelIdFromChatKey(chatKey);
|
|
33319
|
+
return this.deps.sessions.listAllResolvedSessions().filter((session3) => isSessionAliasVisibleInChannel(session3.alias, channelId)).map((session3) => ({
|
|
33320
|
+
alias: toDisplaySessionAlias(session3.alias),
|
|
33321
|
+
agent: session3.agent,
|
|
33322
|
+
workspace: session3.workspace,
|
|
33323
|
+
transportSession: session3.transportSession,
|
|
33324
|
+
running: this.deps.activeTurns.isActiveAnywhere(session3.alias)
|
|
33325
|
+
}));
|
|
33326
|
+
}
|
|
33327
|
+
async listNativeSessions(_chatKey, agent3, workspace3) {
|
|
33328
|
+
const sessions = await this.deps.listNativeSessions(agent3, workspace3);
|
|
33329
|
+
return sessions.map((s) => ({
|
|
33330
|
+
sessionId: s.sessionId,
|
|
33331
|
+
title: s.title ?? null,
|
|
33332
|
+
...s.updatedAt !== undefined ? { updatedAt: s.updatedAt } : {},
|
|
33333
|
+
...s.cwd !== undefined ? { cwd: s.cwd } : {}
|
|
33334
|
+
}));
|
|
33335
|
+
}
|
|
33336
|
+
async createSession(chatKey, alias, agent3, workspace3, agentSessionId, model) {
|
|
33337
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33338
|
+
let nativeHistory = [];
|
|
33339
|
+
if (agentSessionId) {
|
|
33340
|
+
try {
|
|
33341
|
+
nativeHistory = await readNativeSessionHistory({ agentSessionId });
|
|
33342
|
+
} catch {}
|
|
33343
|
+
}
|
|
33344
|
+
const session3 = agentSessionId ? await this.deps.attachNativeSessionWithTransport(internalAlias, agent3, workspace3, agentSessionId) : await this.deps.createSessionWithTransport(internalAlias, agent3, workspace3, model);
|
|
33345
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
33346
|
+
if (nativeHistory.length > 0) {
|
|
33347
|
+
this.deps.events.emit({ type: "session-history", chatKey, sessionAlias: alias, messages: nativeHistory });
|
|
33348
|
+
}
|
|
33349
|
+
return {
|
|
33350
|
+
alias: toDisplaySessionAlias(session3.alias),
|
|
33351
|
+
agent: session3.agent,
|
|
33352
|
+
workspace: session3.workspace,
|
|
33353
|
+
transportSession: session3.transportSession,
|
|
33354
|
+
running: false
|
|
33355
|
+
};
|
|
33356
|
+
}
|
|
33357
|
+
async removeSession(chatKey, alias) {
|
|
33358
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33359
|
+
const result = await this.deps.sessions.removeSession(internalAlias);
|
|
33360
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
33361
|
+
return result;
|
|
33362
|
+
}
|
|
33363
|
+
listAgents() {
|
|
33364
|
+
return this.deps.agents.list();
|
|
33365
|
+
}
|
|
33366
|
+
listWorkspaces() {
|
|
33367
|
+
return this.deps.workspaces.list();
|
|
33368
|
+
}
|
|
33369
|
+
createWorkspace(name, cwd, description) {
|
|
33370
|
+
return this.deps.workspaces.create(name, cwd, description);
|
|
33371
|
+
}
|
|
33372
|
+
listAgentCatalog() {
|
|
33373
|
+
return this.deps.agents.catalog();
|
|
33374
|
+
}
|
|
33375
|
+
createAgent(name, driver) {
|
|
33376
|
+
return this.deps.agents.create(name, driver);
|
|
33377
|
+
}
|
|
33378
|
+
async removeAgent(name) {
|
|
33379
|
+
if (this.deps.sessions.listAllResolvedSessions().some((s) => s.agent === name)) {
|
|
33380
|
+
throw new Error(`agent "${name}" is in use by an existing session`);
|
|
33381
|
+
}
|
|
33382
|
+
await this.deps.agents.remove(name);
|
|
33383
|
+
}
|
|
33384
|
+
async removeWorkspace(name) {
|
|
33385
|
+
if (this.deps.sessions.listAllResolvedSessions().some((s) => s.workspace === name)) {
|
|
33386
|
+
throw new Error(`workspace "${name}" is in use by an existing session`);
|
|
33387
|
+
}
|
|
33388
|
+
await this.deps.workspaces.remove(name);
|
|
33389
|
+
}
|
|
33390
|
+
listScheduledTasks(chatKey) {
|
|
33391
|
+
return this.deps.scheduled.listRecentForChat(chatKey);
|
|
33392
|
+
}
|
|
33393
|
+
async createScheduledTask(input) {
|
|
33394
|
+
const task = await this.deps.scheduled.createTask(input);
|
|
33395
|
+
this.deps.events.emit({ type: "scheduled-changed", chatKey: input.chatKey });
|
|
33396
|
+
return task;
|
|
33397
|
+
}
|
|
33398
|
+
async cancelScheduledTask(id, chatKey) {
|
|
33399
|
+
const cancelled = await this.deps.scheduled.cancelPending(id, chatKey);
|
|
33400
|
+
if (cancelled) {
|
|
33401
|
+
this.deps.events.emit({ type: "scheduled-changed", chatKey });
|
|
33402
|
+
}
|
|
33403
|
+
return cancelled;
|
|
33404
|
+
}
|
|
33405
|
+
listOrchestrationTasks(filter) {
|
|
33406
|
+
return this.deps.orchestration.listTasks(filter);
|
|
33407
|
+
}
|
|
33408
|
+
getOrchestrationTask(taskId) {
|
|
33409
|
+
return this.deps.orchestration.getTask(taskId);
|
|
33410
|
+
}
|
|
33411
|
+
async cancelOrchestrationTask(input) {
|
|
33412
|
+
const task = await this.deps.orchestration.requestTaskCancellation(input);
|
|
33413
|
+
this.deps.events.emit({ type: "orchestration-changed" });
|
|
33414
|
+
return task;
|
|
33415
|
+
}
|
|
33416
|
+
inFlight = new Map;
|
|
33417
|
+
async prompt(input) {
|
|
33418
|
+
return this.executeTurn({
|
|
33419
|
+
chatKey: input.chatKey,
|
|
33420
|
+
sessionAlias: input.sessionAlias,
|
|
33421
|
+
text: input.text,
|
|
33422
|
+
senderId: input.senderId,
|
|
33423
|
+
...input.isOwner !== undefined ? { isOwner: input.isOwner } : {},
|
|
33424
|
+
...input.accountId !== undefined ? { accountId: input.accountId } : {}
|
|
33425
|
+
});
|
|
33426
|
+
}
|
|
33427
|
+
async runScheduledTurn(input) {
|
|
33428
|
+
return this.executeTurn({
|
|
33429
|
+
chatKey: input.chatKey,
|
|
33430
|
+
sessionAlias: input.sessionAlias,
|
|
33431
|
+
text: input.promptText,
|
|
33432
|
+
senderId: "scheduler",
|
|
33433
|
+
isOwner: true,
|
|
33434
|
+
...input.accountId !== undefined ? { accountId: input.accountId } : {},
|
|
33435
|
+
...input.abortSignal ? { abortSignal: input.abortSignal } : {},
|
|
33436
|
+
turnStarted: { prompt: input.promptText, scheduled: { taskId: input.taskId, executeAt: input.executeAt } }
|
|
33437
|
+
});
|
|
33438
|
+
}
|
|
33439
|
+
async executeTurn(params) {
|
|
33440
|
+
const key = turnKey(params.chatKey, params.sessionAlias);
|
|
33441
|
+
const existing = this.inFlight.get(key);
|
|
33442
|
+
if (existing) {
|
|
33443
|
+
if (!existing.controller.signal.aborted) {
|
|
33444
|
+
return { ok: false, errorMessage: "turn-already-running" };
|
|
33445
|
+
}
|
|
33446
|
+
await raceWithTimeout(existing.settled, CANCEL_DRAIN_TIMEOUT_MS);
|
|
33447
|
+
if (this.inFlight.has(key)) {
|
|
33448
|
+
return { ok: false, errorMessage: "turn-already-running" };
|
|
33449
|
+
}
|
|
33450
|
+
}
|
|
33451
|
+
const controller = new AbortController;
|
|
33452
|
+
if (params.abortSignal) {
|
|
33453
|
+
if (params.abortSignal.aborted)
|
|
33454
|
+
controller.abort();
|
|
33455
|
+
else
|
|
33456
|
+
params.abortSignal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
33457
|
+
}
|
|
33458
|
+
let resolveSettled;
|
|
33459
|
+
const settled = new Promise((resolve4) => {
|
|
33460
|
+
resolveSettled = resolve4;
|
|
33461
|
+
});
|
|
33462
|
+
this.inFlight.set(key, { controller, settled });
|
|
33463
|
+
try {
|
|
33464
|
+
await this.deps.sessions.useSession(params.chatKey, params.sessionAlias);
|
|
33465
|
+
} catch (error2) {
|
|
33466
|
+
this.inFlight.delete(key);
|
|
33467
|
+
resolveSettled();
|
|
33468
|
+
return { ok: false, errorMessage: toErrorMessage(error2) };
|
|
33469
|
+
}
|
|
33470
|
+
this.deps.events.emit({
|
|
33471
|
+
type: "turn-started",
|
|
33472
|
+
chatKey: params.chatKey,
|
|
33473
|
+
sessionAlias: params.sessionAlias,
|
|
33474
|
+
...params.turnStarted?.prompt ? { prompt: params.turnStarted.prompt } : {},
|
|
33475
|
+
...params.turnStarted?.scheduled ? { scheduled: params.turnStarted.scheduled } : {}
|
|
33476
|
+
});
|
|
33477
|
+
let streamMode = false;
|
|
33478
|
+
try {
|
|
33479
|
+
const resolved = await this.resolveControlSession(params.chatKey, params.sessionAlias);
|
|
33480
|
+
streamMode = (resolved?.effectiveReplyMode ?? resolved?.replyMode) === "stream";
|
|
33481
|
+
} catch {}
|
|
33482
|
+
let emittedChunk = false;
|
|
33483
|
+
const emitChunk = (chunk) => {
|
|
33484
|
+
if (!chunk)
|
|
33485
|
+
return;
|
|
33486
|
+
this.deps.events.emit({
|
|
33487
|
+
type: "turn-output",
|
|
33488
|
+
chatKey: params.chatKey,
|
|
33489
|
+
sessionAlias: params.sessionAlias,
|
|
33490
|
+
chunk: !streamMode && emittedChunk ? `
|
|
33491
|
+
|
|
33492
|
+
${chunk}` : chunk
|
|
33493
|
+
});
|
|
33494
|
+
emittedChunk = true;
|
|
33495
|
+
};
|
|
33496
|
+
try {
|
|
33497
|
+
const response = await this.deps.agent.chat({
|
|
33498
|
+
accountId: params.accountId ?? "control",
|
|
33499
|
+
conversationId: params.chatKey,
|
|
33500
|
+
text: params.text,
|
|
33501
|
+
metadata: buildControlMetadata(params.senderId, params.isOwner),
|
|
33502
|
+
abortSignal: controller.signal,
|
|
33503
|
+
reply: async (chunk) => {
|
|
33504
|
+
emitChunk(chunk);
|
|
33505
|
+
},
|
|
33506
|
+
onToolEvent: (event) => {
|
|
33507
|
+
this.deps.events.emit({
|
|
33508
|
+
type: "tool-event",
|
|
33509
|
+
chatKey: params.chatKey,
|
|
33510
|
+
sessionAlias: params.sessionAlias,
|
|
33511
|
+
event
|
|
33512
|
+
});
|
|
33513
|
+
},
|
|
33514
|
+
onThought: (chunk) => {
|
|
33515
|
+
this.deps.events.emit({
|
|
33516
|
+
type: "turn-thought",
|
|
33517
|
+
chatKey: params.chatKey,
|
|
33518
|
+
sessionAlias: params.sessionAlias,
|
|
33519
|
+
chunk
|
|
33520
|
+
});
|
|
33521
|
+
},
|
|
33522
|
+
onPlan: (entries) => {
|
|
33523
|
+
this.deps.events.emit({
|
|
33524
|
+
type: "plan",
|
|
33525
|
+
chatKey: params.chatKey,
|
|
33526
|
+
sessionAlias: params.sessionAlias,
|
|
33527
|
+
entries
|
|
33528
|
+
});
|
|
33529
|
+
}
|
|
33530
|
+
});
|
|
33531
|
+
if (response.text) {
|
|
33532
|
+
emitChunk(response.text);
|
|
33533
|
+
}
|
|
33534
|
+
this.deps.events.emit({
|
|
33535
|
+
type: "turn-finished",
|
|
33536
|
+
chatKey: params.chatKey,
|
|
33537
|
+
sessionAlias: params.sessionAlias,
|
|
33538
|
+
ok: true
|
|
33539
|
+
});
|
|
33540
|
+
return { ok: true, text: response.text };
|
|
33541
|
+
} catch (error2) {
|
|
33542
|
+
const errorMessage = toErrorMessage(error2);
|
|
33543
|
+
this.deps.events.emit({
|
|
33544
|
+
type: "turn-finished",
|
|
33545
|
+
chatKey: params.chatKey,
|
|
33546
|
+
sessionAlias: params.sessionAlias,
|
|
33547
|
+
ok: false,
|
|
33548
|
+
errorMessage,
|
|
33549
|
+
...controller.signal.aborted ? { cancelled: true } : {}
|
|
33550
|
+
});
|
|
33551
|
+
return { ok: false, errorMessage };
|
|
33552
|
+
} finally {
|
|
33553
|
+
this.inFlight.delete(key);
|
|
33554
|
+
resolveSettled();
|
|
33555
|
+
}
|
|
33556
|
+
}
|
|
33557
|
+
cancelTurn(chatKey, sessionAlias) {
|
|
33558
|
+
const entry = this.inFlight.get(turnKey(chatKey, sessionAlias));
|
|
33559
|
+
if (!entry) {
|
|
33560
|
+
return false;
|
|
33561
|
+
}
|
|
33562
|
+
entry.controller.abort();
|
|
33563
|
+
return true;
|
|
33564
|
+
}
|
|
33565
|
+
async executeCommand(input) {
|
|
33566
|
+
const chunks = [];
|
|
33567
|
+
const response = await this.deps.agent.chat({
|
|
33568
|
+
accountId: input.accountId ?? "control",
|
|
33569
|
+
conversationId: input.chatKey,
|
|
33570
|
+
text: input.text,
|
|
33571
|
+
metadata: buildControlMetadata(input.senderId, input.isOwner),
|
|
33572
|
+
reply: async (chunk) => {
|
|
33573
|
+
chunks.push(chunk);
|
|
33574
|
+
}
|
|
33575
|
+
});
|
|
33576
|
+
if (response.text) {
|
|
33577
|
+
chunks.push(response.text);
|
|
33578
|
+
}
|
|
33579
|
+
return chunks.join(`
|
|
33580
|
+
`);
|
|
33581
|
+
}
|
|
33582
|
+
}
|
|
33583
|
+
async function raceWithTimeout(promise2, ms) {
|
|
33584
|
+
let timer;
|
|
33585
|
+
const timeout = new Promise((resolve4) => {
|
|
33586
|
+
timer = setTimeout(resolve4, ms);
|
|
33587
|
+
});
|
|
33588
|
+
try {
|
|
33589
|
+
await Promise.race([promise2, timeout]);
|
|
33590
|
+
} finally {
|
|
33591
|
+
if (timer)
|
|
33592
|
+
clearTimeout(timer);
|
|
33593
|
+
}
|
|
33594
|
+
}
|
|
33595
|
+
function turnKey(chatKey, sessionAlias) {
|
|
33596
|
+
return `${chatKey} ${sessionAlias}`;
|
|
33597
|
+
}
|
|
33598
|
+
function toErrorMessage(error2) {
|
|
33599
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
33600
|
+
}
|
|
33601
|
+
function buildControlMetadata(senderId, isOwner) {
|
|
33602
|
+
return {
|
|
33603
|
+
channel: "control",
|
|
33604
|
+
chatType: "direct",
|
|
33605
|
+
senderId,
|
|
33606
|
+
...isOwner === undefined ? {} : { isOwner }
|
|
33607
|
+
};
|
|
33608
|
+
}
|
|
33609
|
+
var CANCEL_DRAIN_TIMEOUT_MS = 5000;
|
|
33610
|
+
var init_control_service = __esm(() => {
|
|
33611
|
+
init_channel_scope();
|
|
33612
|
+
init_native_session_history();
|
|
33613
|
+
init_workspace_fs();
|
|
33614
|
+
});
|
|
33615
|
+
|
|
33616
|
+
// src/config/agent-catalog.ts
|
|
33617
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
33618
|
+
import { delimiter as delimiter2, join as join20 } from "node:path";
|
|
33619
|
+
function isBinaryOnPath(binary) {
|
|
33620
|
+
const path15 = process.env.PATH ?? "";
|
|
33621
|
+
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
33622
|
+
for (const dir of path15.split(delimiter2)) {
|
|
33623
|
+
if (!dir)
|
|
33624
|
+
continue;
|
|
33625
|
+
for (const ext of exts) {
|
|
33626
|
+
try {
|
|
33627
|
+
if (existsSync3(join20(dir, binary + ext)))
|
|
33628
|
+
return true;
|
|
33629
|
+
} catch {}
|
|
33630
|
+
}
|
|
33631
|
+
}
|
|
33632
|
+
return false;
|
|
33633
|
+
}
|
|
33634
|
+
function listAgentCatalog(config4, probe = isBinaryOnPath) {
|
|
33635
|
+
const agents = config4.agents ?? {};
|
|
33636
|
+
const driverConfigured = (driver) => Object.entries(agents).some(([name, a]) => name === driver || a.driver === driver);
|
|
33637
|
+
return listAgentTemplates().map((driver) => {
|
|
33638
|
+
let installed;
|
|
33639
|
+
if (BUILTIN_DRIVERS.has(driver)) {
|
|
33640
|
+
installed = "builtin";
|
|
33641
|
+
} else {
|
|
33642
|
+
const binary = DRIVER_BINARIES[driver] ?? driver;
|
|
33643
|
+
installed = probe(binary) ? "yes" : "unknown";
|
|
33644
|
+
}
|
|
33645
|
+
return { driver, configured: driverConfigured(driver), installed };
|
|
33646
|
+
});
|
|
33647
|
+
}
|
|
33648
|
+
var BUILTIN_DRIVERS, DRIVER_BINARIES;
|
|
33649
|
+
var init_agent_catalog = __esm(() => {
|
|
33650
|
+
init_agent_templates();
|
|
33651
|
+
BUILTIN_DRIVERS = new Set(["codex", "claude"]);
|
|
33652
|
+
DRIVER_BINARIES = {
|
|
33653
|
+
cursor: "cursor-agent"
|
|
33654
|
+
};
|
|
33655
|
+
});
|
|
33656
|
+
|
|
32392
33657
|
// src/main.ts
|
|
32393
33658
|
var exports_main = {};
|
|
32394
33659
|
__export(exports_main, {
|
|
@@ -32399,8 +33664,8 @@ __export(exports_main, {
|
|
|
32399
33664
|
buildApp: () => buildApp
|
|
32400
33665
|
});
|
|
32401
33666
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
32402
|
-
import { homedir as
|
|
32403
|
-
import { dirname as dirname12, join as
|
|
33667
|
+
import { homedir as homedir11 } from "node:os";
|
|
33668
|
+
import { dirname as dirname12, join as join21 } from "node:path";
|
|
32404
33669
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
32405
33670
|
function startProgressHeartbeat(orchestration3, config4, logger2, channel) {
|
|
32406
33671
|
const thresholdSeconds = config4.orchestration.progressHeartbeatSeconds;
|
|
@@ -32676,7 +33941,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
32676
33941
|
return {
|
|
32677
33942
|
alias: input.workerSession,
|
|
32678
33943
|
agent: input.targetAgent,
|
|
32679
|
-
agentCommand:
|
|
33944
|
+
agentCommand: resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, config4.transport.preferLocalAgents !== false),
|
|
32680
33945
|
workspace: input.workspace,
|
|
32681
33946
|
transportSession: input.workerSession,
|
|
32682
33947
|
cwd: input.cwd
|
|
@@ -32890,9 +34155,52 @@ async function buildApp(paths, deps = {}) {
|
|
|
32890
34155
|
});
|
|
32891
34156
|
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
34157
|
const agent3 = new ConsoleAgent(router3, logger2);
|
|
34158
|
+
const controlEvents = createControlEventBus(logger2);
|
|
34159
|
+
const control = new ControlService({
|
|
34160
|
+
agent: agent3,
|
|
34161
|
+
sessions,
|
|
34162
|
+
transport,
|
|
34163
|
+
createSessionWithTransport: (internalAlias, agent4, workspace3, model) => router3.createSessionWithTransport(internalAlias, agent4, workspace3, model),
|
|
34164
|
+
listNativeSessions: (agent4, workspace3) => router3.listNativeSessionsForControl(agent4, workspace3),
|
|
34165
|
+
attachNativeSessionWithTransport: (internalAlias, agent4, workspace3, agentSessionId, nativeMeta) => router3.attachNativeSessionWithTransport(internalAlias, agent4, workspace3, agentSessionId, nativeMeta),
|
|
34166
|
+
activeTurns,
|
|
34167
|
+
scheduled: scheduledService,
|
|
34168
|
+
orchestration: orchestration3,
|
|
34169
|
+
events: controlEvents,
|
|
34170
|
+
agents: {
|
|
34171
|
+
list: () => Object.entries(config4.agents).map(([name, agentConfig]) => ({ name, driver: agentConfig.driver })),
|
|
34172
|
+
catalog: () => listAgentCatalog(config4),
|
|
34173
|
+
create: async (name, driver) => {
|
|
34174
|
+
const updated = await configStore.upsertAgent(name, { driver });
|
|
34175
|
+
replaceRuntimeConfig(config4, updated);
|
|
34176
|
+
return { name, driver };
|
|
34177
|
+
},
|
|
34178
|
+
remove: async (name) => {
|
|
34179
|
+
const updated = await configStore.removeAgent(name);
|
|
34180
|
+
replaceRuntimeConfig(config4, updated);
|
|
34181
|
+
}
|
|
34182
|
+
},
|
|
34183
|
+
workspaces: {
|
|
34184
|
+
list: () => Object.entries(config4.workspaces).map(([name, workspace3]) => ({
|
|
34185
|
+
name,
|
|
34186
|
+
cwd: workspace3.cwd,
|
|
34187
|
+
...workspace3.description ? { description: workspace3.description } : {}
|
|
34188
|
+
})),
|
|
34189
|
+
create: async (name, cwd, description) => {
|
|
34190
|
+
const updated = await configStore.upsertWorkspace(name, cwd, description);
|
|
34191
|
+
replaceRuntimeConfig(config4, updated);
|
|
34192
|
+
return { name, cwd, ...description ? { description } : {} };
|
|
34193
|
+
},
|
|
34194
|
+
remove: async (name) => {
|
|
34195
|
+
const updated = await configStore.removeWorkspace(name);
|
|
34196
|
+
replaceRuntimeConfig(config4, updated);
|
|
34197
|
+
}
|
|
34198
|
+
}
|
|
34199
|
+
});
|
|
32893
34200
|
const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
|
|
32894
34201
|
dispatchTask: buildScheduledDispatchTask({
|
|
32895
34202
|
getSession: (alias) => sessions.getSession(alias),
|
|
34203
|
+
resolveAliasForChat: (chatKey, alias) => sessions.resolveAliasForChat(chatKey, alias),
|
|
32896
34204
|
resolveSession: (alias, agent4, workspace3, transportSession) => sessions.resolveSession(alias, agent4, workspace3, transportSession),
|
|
32897
34205
|
sendScheduledMessage: async (input) => {
|
|
32898
34206
|
if (!deps.channel?.sendScheduledMessage) {
|
|
@@ -32903,6 +34211,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
32903
34211
|
...transport.removeSession ? { removeSession: (session3) => transport.removeSession(session3) } : {},
|
|
32904
34212
|
logger: logger2
|
|
32905
34213
|
}),
|
|
34214
|
+
onSettled: (task) => controlEvents.emit({ type: "scheduled-changed", chatKey: task.chat_key }),
|
|
32906
34215
|
logger: logger2
|
|
32907
34216
|
});
|
|
32908
34217
|
const reapWarmQueueOwners = async (phase) => {
|
|
@@ -32952,6 +34261,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
32952
34261
|
service: scheduledService,
|
|
32953
34262
|
scheduler: scheduledScheduler
|
|
32954
34263
|
},
|
|
34264
|
+
control,
|
|
32955
34265
|
reapStaleQueueOwners: () => reapWarmQueueOwners("startup"),
|
|
32956
34266
|
dispose: async () => {
|
|
32957
34267
|
scheduledScheduler.stop();
|
|
@@ -33015,8 +34325,8 @@ async function main() {
|
|
|
33015
34325
|
}
|
|
33016
34326
|
}
|
|
33017
34327
|
async function prepareChannelMedia(configPath, config4) {
|
|
33018
|
-
const runtimeDir =
|
|
33019
|
-
const mediaRootDir =
|
|
34328
|
+
const runtimeDir = join21(dirname12(configPath), "runtime");
|
|
34329
|
+
const mediaRootDir = join21(runtimeDir, "media");
|
|
33020
34330
|
const mediaStore = new RuntimeMediaStore({ rootDir: mediaRootDir });
|
|
33021
34331
|
await mediaStore.cleanupExpired().catch((error2) => {
|
|
33022
34332
|
console.error("[xacpx] media cleanup failed:", error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -33025,16 +34335,16 @@ async function prepareChannelMedia(configPath, config4) {
|
|
|
33025
34335
|
return { mediaStore, channelDeps: { mediaStore, allowedMediaRoots } };
|
|
33026
34336
|
}
|
|
33027
34337
|
function resolveRuntimePaths() {
|
|
33028
|
-
const home = process.env.HOME ??
|
|
34338
|
+
const home = process.env.HOME ?? homedir11();
|
|
33029
34339
|
if (!home) {
|
|
33030
34340
|
throw new Error("Unable to resolve the current user home directory");
|
|
33031
34341
|
}
|
|
33032
|
-
const configPath = coreEnv("CONFIG") ??
|
|
33033
|
-
const runtimeDir =
|
|
34342
|
+
const configPath = coreEnv("CONFIG") ?? join21(coreHomeDir(home), "config.json");
|
|
34343
|
+
const runtimeDir = join21(dirname12(configPath), "runtime");
|
|
33034
34344
|
return {
|
|
33035
34345
|
configPath,
|
|
33036
|
-
statePath: coreEnv("STATE") ??
|
|
33037
|
-
perfLogPath:
|
|
34346
|
+
statePath: coreEnv("STATE") ?? join21(coreHomeDir(home), "state.json"),
|
|
34347
|
+
perfLogPath: join21(runtimeDir, "perf.log"),
|
|
33038
34348
|
orchestrationSocketPath: coreEnv("ORCHESTRATION_SOCKET") ?? resolveDaemonOrchestrationSocketPath(runtimeDir)
|
|
33039
34349
|
};
|
|
33040
34350
|
}
|
|
@@ -33046,13 +34356,13 @@ function resolveBridgeEntryPath() {
|
|
|
33046
34356
|
}
|
|
33047
34357
|
function resolveAppLogPath(configPath) {
|
|
33048
34358
|
const rootDir = dirname12(configPath);
|
|
33049
|
-
const runtimeDir =
|
|
33050
|
-
return
|
|
34359
|
+
const runtimeDir = join21(rootDir, "runtime");
|
|
34360
|
+
return join21(runtimeDir, "app.log");
|
|
33051
34361
|
}
|
|
33052
34362
|
function resolvePerfLogPath(configPath) {
|
|
33053
34363
|
const rootDir = dirname12(configPath);
|
|
33054
|
-
const runtimeDir =
|
|
33055
|
-
return
|
|
34364
|
+
const runtimeDir = join21(rootDir, "runtime");
|
|
34365
|
+
return join21(runtimeDir, "perf.log");
|
|
33056
34366
|
}
|
|
33057
34367
|
function resolveOrchestrationSocketPathFromConfigPath(configPath) {
|
|
33058
34368
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
@@ -33068,6 +34378,7 @@ var init_main = __esm(async () => {
|
|
|
33068
34378
|
init_ensure_config();
|
|
33069
34379
|
init_load_config();
|
|
33070
34380
|
init_resolve_acpx_command();
|
|
34381
|
+
init_resolve_agent_command();
|
|
33071
34382
|
init_console_agent();
|
|
33072
34383
|
init_app_logger();
|
|
33073
34384
|
init_daemon_files();
|
|
@@ -33095,6 +34406,8 @@ var init_main = __esm(async () => {
|
|
|
33095
34406
|
init_inbound();
|
|
33096
34407
|
init_render_text();
|
|
33097
34408
|
init_quota_manager();
|
|
34409
|
+
init_control_service();
|
|
34410
|
+
init_agent_catalog();
|
|
33098
34411
|
init_perf_tracer();
|
|
33099
34412
|
init_bootstrap();
|
|
33100
34413
|
init_i18n();
|
|
@@ -33148,7 +34461,7 @@ function buildDetails(metadata, version2, verbose) {
|
|
|
33148
34461
|
}
|
|
33149
34462
|
async function defaultRunVersion(command) {
|
|
33150
34463
|
const spawnSpec = resolveSpawnCommand(command, ["--version"]);
|
|
33151
|
-
return await new Promise((
|
|
34464
|
+
return await new Promise((resolve4, reject) => {
|
|
33152
34465
|
const child = spawn11(spawnSpec.command, spawnSpec.args, {
|
|
33153
34466
|
stdio: ["ignore", "pipe", "pipe"]
|
|
33154
34467
|
});
|
|
@@ -33165,7 +34478,7 @@ async function defaultRunVersion(command) {
|
|
|
33165
34478
|
if (code === 0) {
|
|
33166
34479
|
const version2 = stdout2.trim() || stderr.trim();
|
|
33167
34480
|
if (version2.length > 0) {
|
|
33168
|
-
|
|
34481
|
+
resolve4(version2);
|
|
33169
34482
|
return;
|
|
33170
34483
|
}
|
|
33171
34484
|
}
|
|
@@ -33287,12 +34600,12 @@ var init_config_check = __esm(async () => {
|
|
|
33287
34600
|
});
|
|
33288
34601
|
|
|
33289
34602
|
// src/doctor/checks/daemon-check.ts
|
|
33290
|
-
import { readdir as
|
|
34603
|
+
import { readdir as readdir4, readFile as readFile15, rm as rm10 } from "node:fs/promises";
|
|
33291
34604
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
33292
|
-
import { homedir as
|
|
33293
|
-
import { join as
|
|
34605
|
+
import { homedir as homedir12 } from "node:os";
|
|
34606
|
+
import { join as join22 } from "node:path";
|
|
33294
34607
|
async function checkDaemon(options = {}) {
|
|
33295
|
-
const home = options.home ?? process.env.HOME ??
|
|
34608
|
+
const home = options.home ?? process.env.HOME ?? homedir12();
|
|
33296
34609
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
33297
34610
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
33298
34611
|
home,
|
|
@@ -33392,7 +34705,7 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
33392
34705
|
if (!fileName.endsWith(CONSUMER_LOCK_SUFFIX)) {
|
|
33393
34706
|
continue;
|
|
33394
34707
|
}
|
|
33395
|
-
const lockPath =
|
|
34708
|
+
const lockPath = join22(runtimeDir, fileName);
|
|
33396
34709
|
const lock2 = await deps.readConsumerLock(lockPath);
|
|
33397
34710
|
if (lock2 && !deps.isProcessRunning(lock2.pid)) {
|
|
33398
34711
|
stalePaths.push(lockPath);
|
|
@@ -33426,14 +34739,14 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
33426
34739
|
}
|
|
33427
34740
|
async function defaultListConsumerLocks(runtimeDir) {
|
|
33428
34741
|
try {
|
|
33429
|
-
return await
|
|
34742
|
+
return await readdir4(runtimeDir);
|
|
33430
34743
|
} catch {
|
|
33431
34744
|
return [];
|
|
33432
34745
|
}
|
|
33433
34746
|
}
|
|
33434
34747
|
async function defaultReadConsumerLock(path15) {
|
|
33435
34748
|
try {
|
|
33436
|
-
const raw = await
|
|
34749
|
+
const raw = await readFile15(path15, "utf8");
|
|
33437
34750
|
const parsed = JSON.parse(raw);
|
|
33438
34751
|
return typeof parsed.pid === "number" ? { pid: parsed.pid } : null;
|
|
33439
34752
|
} catch {
|
|
@@ -33456,11 +34769,11 @@ var init_daemon_check = __esm(() => {
|
|
|
33456
34769
|
});
|
|
33457
34770
|
|
|
33458
34771
|
// src/doctor/checks/logs-check.ts
|
|
33459
|
-
import { stat as
|
|
33460
|
-
import { basename as basename3, join as
|
|
33461
|
-
import { homedir as
|
|
34772
|
+
import { stat as stat4, readdir as readdir5 } from "node:fs/promises";
|
|
34773
|
+
import { basename as basename3, join as join23 } from "node:path";
|
|
34774
|
+
import { homedir as homedir13 } from "node:os";
|
|
33462
34775
|
async function checkLogs(options = {}) {
|
|
33463
|
-
const home = options.home ?? process.env.HOME ??
|
|
34776
|
+
const home = options.home ?? process.env.HOME ?? homedir13();
|
|
33464
34777
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
33465
34778
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
33466
34779
|
home,
|
|
@@ -33492,7 +34805,7 @@ async function checkLogs(options = {}) {
|
|
|
33492
34805
|
const matched = entries.filter((entry) => isTrackedLogName(entry, tracked));
|
|
33493
34806
|
const files = [];
|
|
33494
34807
|
for (const name of matched) {
|
|
33495
|
-
const path15 =
|
|
34808
|
+
const path15 = join23(paths.runtimeDir, name);
|
|
33496
34809
|
try {
|
|
33497
34810
|
const fileStat = await probe.stat(path15);
|
|
33498
34811
|
if (fileStat.isDirectory()) {
|
|
@@ -33572,8 +34885,8 @@ function formatBytes(bytes) {
|
|
|
33572
34885
|
}
|
|
33573
34886
|
function createLogsFsProbe() {
|
|
33574
34887
|
return {
|
|
33575
|
-
stat: async (path15) => await
|
|
33576
|
-
readdir: async (path15) => await
|
|
34888
|
+
stat: async (path15) => await stat4(path15),
|
|
34889
|
+
readdir: async (path15) => await readdir5(path15)
|
|
33577
34890
|
};
|
|
33578
34891
|
}
|
|
33579
34892
|
function formatError6(error2) {
|
|
@@ -33642,9 +34955,9 @@ var init_orchestration_health = __esm(() => {
|
|
|
33642
34955
|
});
|
|
33643
34956
|
|
|
33644
34957
|
// src/doctor/checks/orchestration-socket-check.ts
|
|
33645
|
-
import { homedir as
|
|
34958
|
+
import { homedir as homedir14 } from "node:os";
|
|
33646
34959
|
async function checkOrchestrationSocket(options = {}) {
|
|
33647
|
-
const home = options.home ?? process.env.HOME ??
|
|
34960
|
+
const home = options.home ?? process.env.HOME ?? homedir14();
|
|
33648
34961
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
33649
34962
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
33650
34963
|
home,
|
|
@@ -33817,11 +35130,11 @@ var init_plugin_check = __esm(async () => {
|
|
|
33817
35130
|
|
|
33818
35131
|
// src/doctor/checks/runtime-check.ts
|
|
33819
35132
|
import { constants } from "node:fs";
|
|
33820
|
-
import { access as access4, stat as
|
|
35133
|
+
import { access as access4, stat as stat5 } from "node:fs/promises";
|
|
33821
35134
|
import { dirname as dirname13 } from "node:path";
|
|
33822
|
-
import { homedir as
|
|
35135
|
+
import { homedir as homedir15 } from "node:os";
|
|
33823
35136
|
async function checkRuntime(options = {}) {
|
|
33824
|
-
const home = options.home ?? process.env.HOME ??
|
|
35137
|
+
const home = options.home ?? process.env.HOME ?? homedir15();
|
|
33825
35138
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
33826
35139
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
33827
35140
|
home,
|
|
@@ -33915,7 +35228,7 @@ function formatMode(mode) {
|
|
|
33915
35228
|
}
|
|
33916
35229
|
function createRuntimeFsProbe() {
|
|
33917
35230
|
return {
|
|
33918
|
-
stat: async (path15) => await
|
|
35231
|
+
stat: async (path15) => await stat5(path15),
|
|
33919
35232
|
access: async (path15, mode) => await access4(path15, mode)
|
|
33920
35233
|
};
|
|
33921
35234
|
}
|
|
@@ -34247,7 +35560,10 @@ function buildSession(options) {
|
|
|
34247
35560
|
return {
|
|
34248
35561
|
alias: "xacpx-doctor",
|
|
34249
35562
|
agent: options.agent,
|
|
34250
|
-
...
|
|
35563
|
+
...(() => {
|
|
35564
|
+
const agentCommand = resolveRuntimeAgentCommand(agentConfig.driver, agentConfig.command, options.config.transport.preferLocalAgents !== false);
|
|
35565
|
+
return agentCommand ? { agentCommand } : {};
|
|
35566
|
+
})(),
|
|
34251
35567
|
workspace: options.workspace,
|
|
34252
35568
|
transportSession: `xacpx-doctor-${timestamp}`,
|
|
34253
35569
|
replyMode: options.config.channel.replyMode,
|
|
@@ -34292,6 +35608,7 @@ var SMOKE_PROMPT = "Reply with exactly: ok";
|
|
|
34292
35608
|
var init_smoke_check = __esm(async () => {
|
|
34293
35609
|
init_load_config();
|
|
34294
35610
|
init_resolve_acpx_command();
|
|
35611
|
+
init_resolve_agent_command();
|
|
34295
35612
|
init_acpx_bridge_client();
|
|
34296
35613
|
init_acpx_bridge_transport();
|
|
34297
35614
|
init_acpx_cli_transport();
|
|
@@ -34450,10 +35767,10 @@ var init_render_doctor = __esm(() => {
|
|
|
34450
35767
|
});
|
|
34451
35768
|
|
|
34452
35769
|
// src/doctor/doctor.ts
|
|
34453
|
-
import { homedir as
|
|
34454
|
-
import { join as
|
|
35770
|
+
import { homedir as homedir16 } from "node:os";
|
|
35771
|
+
import { join as join24 } from "node:path";
|
|
34455
35772
|
async function runDoctor(options = {}, deps = {}) {
|
|
34456
|
-
const home = deps.home ?? process.env.HOME ??
|
|
35773
|
+
const home = deps.home ?? process.env.HOME ?? homedir16();
|
|
34457
35774
|
const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
|
|
34458
35775
|
const sharedLoadConfig = createSharedLoadConfig(runtimePaths, deps.loadConfig ?? loadConfig);
|
|
34459
35776
|
const runners = [
|
|
@@ -34606,8 +35923,8 @@ function resolveDoctorRuntimePaths(home, resolver) {
|
|
|
34606
35923
|
return resolveRuntimePaths();
|
|
34607
35924
|
}
|
|
34608
35925
|
return {
|
|
34609
|
-
configPath:
|
|
34610
|
-
statePath:
|
|
35926
|
+
configPath: join24(coreHomeDir(home), "config.json"),
|
|
35927
|
+
statePath: join24(coreHomeDir(home), "state.json")
|
|
34611
35928
|
};
|
|
34612
35929
|
}
|
|
34613
35930
|
function depsUseExplicitRuntimeOverrides() {
|
|
@@ -34790,8 +36107,8 @@ var init_doctor2 = __esm(async () => {
|
|
|
34790
36107
|
// src/cli.ts
|
|
34791
36108
|
init_core_home();
|
|
34792
36109
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
34793
|
-
import { homedir as
|
|
34794
|
-
import { dirname as dirname14, join as
|
|
36110
|
+
import { homedir as homedir17 } from "node:os";
|
|
36111
|
+
import { dirname as dirname14, join as join25, sep as sep2 } from "node:path";
|
|
34795
36112
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
34796
36113
|
|
|
34797
36114
|
// src/runtime/migrate-core-home.ts
|
|
@@ -47288,7 +48605,7 @@ init_core_home();
|
|
|
47288
48605
|
init_daemon_files();
|
|
47289
48606
|
init_orchestration_ipc();
|
|
47290
48607
|
import { homedir as homedir2 } from "node:os";
|
|
47291
|
-
import { join as
|
|
48608
|
+
import { join as join6 } from "node:path";
|
|
47292
48609
|
function resolveDefaultOrchestrationEndpoint(env = process.env, platform = process.platform) {
|
|
47293
48610
|
const orchestrationSocket = coreEnv("ORCHESTRATION_SOCKET", env);
|
|
47294
48611
|
if (typeof orchestrationSocket === "string" && orchestrationSocket.trim().length > 0) {
|
|
@@ -47296,7 +48613,7 @@ function resolveDefaultOrchestrationEndpoint(env = process.env, platform = proce
|
|
|
47296
48613
|
}
|
|
47297
48614
|
const home = requireHome(env);
|
|
47298
48615
|
const configOverride = coreEnv("CONFIG", env);
|
|
47299
|
-
const configPath = typeof configOverride === "string" && configOverride.trim().length > 0 ? configOverride.trim() :
|
|
48616
|
+
const configPath = typeof configOverride === "string" && configOverride.trim().length > 0 ? configOverride.trim() : join6(coreHomeDir(home), "config.json");
|
|
47300
48617
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
47301
48618
|
return resolveOrchestrationEndpoint(runtimeDir, platform);
|
|
47302
48619
|
}
|
|
@@ -48833,14 +50150,14 @@ function resolveTemplateChoice(answer, names) {
|
|
|
48833
50150
|
init_plugin_home();
|
|
48834
50151
|
import { spawn as spawn4 } from "node:child_process";
|
|
48835
50152
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
48836
|
-
import { dirname as dirname8, join as
|
|
50153
|
+
import { dirname as dirname8, join as join12 } from "node:path";
|
|
48837
50154
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
48838
50155
|
|
|
48839
50156
|
// src/plugins/package-manager.ts
|
|
48840
50157
|
init_plugin_home();
|
|
48841
50158
|
import { spawn as spawn3 } from "node:child_process";
|
|
48842
50159
|
import { rm as rm4 } from "node:fs/promises";
|
|
48843
|
-
import { join as
|
|
50160
|
+
import { join as join8 } from "node:path";
|
|
48844
50161
|
function shellSpawnPlan(args) {
|
|
48845
50162
|
const shell = process.platform === "win32";
|
|
48846
50163
|
return { shell, args: shell ? args.map((arg) => `"${arg}"`) : args };
|
|
@@ -48888,7 +50205,7 @@ async function installPluginPackage(input) {
|
|
|
48888
50205
|
const packageManager = input.packageManager ?? await detectPackageManager();
|
|
48889
50206
|
await normalizePluginHomeManifest(input.pluginHome);
|
|
48890
50207
|
if (packageManager === "bun") {
|
|
48891
|
-
await rm4(
|
|
50208
|
+
await rm4(join8(input.pluginHome, "bun.lock"), { force: true }).catch(() => {});
|
|
48892
50209
|
}
|
|
48893
50210
|
const spec = input.version ? `${input.packageName}@${input.version}` : input.packageName;
|
|
48894
50211
|
if (packageManager === "bun") {
|
|
@@ -49180,7 +50497,7 @@ async function runInherit(command, args) {
|
|
|
49180
50497
|
async function readPackageName() {
|
|
49181
50498
|
try {
|
|
49182
50499
|
const here = dirname8(fileURLToPath3(import.meta.url));
|
|
49183
|
-
for (const candidate of [
|
|
50500
|
+
for (const candidate of [join12(here, "..", "package.json"), join12(here, "..", "..", "package.json")]) {
|
|
49184
50501
|
try {
|
|
49185
50502
|
const parsed = JSON.parse(await readFile9(candidate, "utf8"));
|
|
49186
50503
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
@@ -49835,7 +51152,7 @@ async function setChannelAccountEnabled(type, accountId, enabled, rawArgs, deps)
|
|
|
49835
51152
|
init_core_home();
|
|
49836
51153
|
init_plugin_home();
|
|
49837
51154
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
49838
|
-
import { isAbsolute, join as
|
|
51155
|
+
import { isAbsolute, join as join14, resolve } from "node:path";
|
|
49839
51156
|
init_plugin_loader();
|
|
49840
51157
|
init_validate_plugin();
|
|
49841
51158
|
init_plugin_doctor();
|
|
@@ -49866,7 +51183,7 @@ function looksLikePath(spec) {
|
|
|
49866
51183
|
}
|
|
49867
51184
|
async function readDependencyEntries2(pluginHome) {
|
|
49868
51185
|
try {
|
|
49869
|
-
const raw = await readFile11(
|
|
51186
|
+
const raw = await readFile11(join14(pluginHome, "package.json"), "utf8");
|
|
49870
51187
|
const parsed = JSON.parse(raw);
|
|
49871
51188
|
const out = {};
|
|
49872
51189
|
for (const [name, value] of Object.entries(parsed.dependencies ?? {})) {
|
|
@@ -49892,7 +51209,7 @@ async function resolveLocalPluginName(installSpec, pluginHome, namesBeforeInstal
|
|
|
49892
51209
|
return name;
|
|
49893
51210
|
}
|
|
49894
51211
|
try {
|
|
49895
|
-
const raw = await readFile11(
|
|
51212
|
+
const raw = await readFile11(join14(installSpec, "package.json"), "utf8");
|
|
49896
51213
|
const parsed = JSON.parse(raw);
|
|
49897
51214
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
49898
51215
|
return parsed.name.trim();
|
|
@@ -51034,7 +52351,7 @@ async function createCliScheduledTaskService() {
|
|
|
51034
52351
|
return new ScheduledTaskService(state, stateStore);
|
|
51035
52352
|
}
|
|
51036
52353
|
function resolveConfigPathForCurrentEnv() {
|
|
51037
|
-
return coreEnv("CONFIG") ??
|
|
52354
|
+
return coreEnv("CONFIG") ?? join25(coreHomeDir(requireHome2()), "config.json");
|
|
51038
52355
|
}
|
|
51039
52356
|
function resolveDaemonPathsForCurrentConfig() {
|
|
51040
52357
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
@@ -51093,7 +52410,7 @@ async function defaultRun(options = {}) {
|
|
|
51093
52410
|
const firstRunOnboarding = options.firstRunOnboarding ?? decodeFirstRunOnboarding(coreEnv("FIRST_RUN_ONBOARDING"));
|
|
51094
52411
|
await runConsole2(runtimePaths, {
|
|
51095
52412
|
buildApp: (paths) => buildApp2(paths, {
|
|
51096
|
-
defaultLoggingLevel: resolveCliEntryPath2().includes(`${
|
|
52413
|
+
defaultLoggingLevel: resolveCliEntryPath2().includes(`${sep2}src${sep2}`) ? "debug" : "info",
|
|
51097
52414
|
channel: channelRegistry
|
|
51098
52415
|
}),
|
|
51099
52416
|
beforeReady: firstRunOnboarding ? async (runtime) => {
|
|
@@ -51104,7 +52421,7 @@ async function defaultRun(options = {}) {
|
|
|
51104
52421
|
daemonRuntime,
|
|
51105
52422
|
...firstLockCreator ? {
|
|
51106
52423
|
consumerLockFactory: (runtime) => firstLockCreator.create({
|
|
51107
|
-
lockFilePath: `${daemonPaths.runtimeDir}${
|
|
52424
|
+
lockFilePath: `${daemonPaths.runtimeDir}${sep2}${firstLockCreator.channel.id}-consumer.lock.json`,
|
|
51108
52425
|
onDiagnostic: async (event, context) => {
|
|
51109
52426
|
await runtime.logger.info(`${firstLockCreator.channel.id}.consumer_lock.${event}`, `${firstLockCreator.channel.id} consumer lock diagnostic`, context);
|
|
51110
52427
|
}
|
|
@@ -51295,7 +52612,7 @@ async function defaultPromptSecret(message) {
|
|
|
51295
52612
|
process.stdout.write(message);
|
|
51296
52613
|
process.stdin.setRawMode(true);
|
|
51297
52614
|
process.stdin.resume();
|
|
51298
|
-
return await new Promise((
|
|
52615
|
+
return await new Promise((resolve4, reject) => {
|
|
51299
52616
|
const chunks = [];
|
|
51300
52617
|
let inEscape = false;
|
|
51301
52618
|
const cleanup = () => {
|
|
@@ -51326,7 +52643,7 @@ async function defaultPromptSecret(message) {
|
|
|
51326
52643
|
if (char === "\r" || char === `
|
|
51327
52644
|
`) {
|
|
51328
52645
|
cleanup();
|
|
51329
|
-
|
|
52646
|
+
resolve4(chunks.join(""));
|
|
51330
52647
|
return;
|
|
51331
52648
|
}
|
|
51332
52649
|
if (char === "" || char === "\b") {
|
|
@@ -51381,7 +52698,7 @@ function decodeFirstRunOnboarding(raw) {
|
|
|
51381
52698
|
return null;
|
|
51382
52699
|
}
|
|
51383
52700
|
function requireHome2() {
|
|
51384
|
-
const home = process.env.HOME ??
|
|
52701
|
+
const home = process.env.HOME ?? homedir17();
|
|
51385
52702
|
if (!home) {
|
|
51386
52703
|
throw new Error("Unable to resolve the current user home directory");
|
|
51387
52704
|
}
|
|
@@ -51405,7 +52722,7 @@ function safeDaemonLogPaths() {
|
|
|
51405
52722
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
51406
52723
|
const paths = resolveDaemonPathsForCurrentConfig();
|
|
51407
52724
|
return {
|
|
51408
|
-
appLog:
|
|
52725
|
+
appLog: join25(dirname14(configPath), "runtime", "app.log"),
|
|
51409
52726
|
stderrLog: paths.stderrLog
|
|
51410
52727
|
};
|
|
51411
52728
|
} catch {
|