@centralu/win32-x64 0.1.0-beta.39 → 0.1.0-beta.40
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/Centralu/centralu.exe +0 -0
- package/Centralu/resources/host/bundle-info.json +2 -2
- package/Centralu/resources/host/centralu-package.json +5 -5
- package/Centralu/resources/host/main.mjs +2644 -409
- package/Centralu/resources/host/pty-broker.mjs +1 -1
- package/Centralu/resources/host/remote-runtime.json +7 -7
- package/package.json +1 -1
|
@@ -1470,7 +1470,7 @@ var require_sender = __commonJS({
|
|
|
1470
1470
|
*/
|
|
1471
1471
|
static frame(data, options) {
|
|
1472
1472
|
let mask;
|
|
1473
|
-
let
|
|
1473
|
+
let merge3 = false;
|
|
1474
1474
|
let offset = 2;
|
|
1475
1475
|
let skipMasking = false;
|
|
1476
1476
|
if (options.mask) {
|
|
@@ -1503,7 +1503,7 @@ var require_sender = __commonJS({
|
|
|
1503
1503
|
}
|
|
1504
1504
|
} else {
|
|
1505
1505
|
dataLength = data.length;
|
|
1506
|
-
|
|
1506
|
+
merge3 = options.mask && options.readOnly && !skipMasking;
|
|
1507
1507
|
}
|
|
1508
1508
|
let payloadLength = dataLength;
|
|
1509
1509
|
if (dataLength >= 65536) {
|
|
@@ -1513,7 +1513,7 @@ var require_sender = __commonJS({
|
|
|
1513
1513
|
offset += 2;
|
|
1514
1514
|
payloadLength = 126;
|
|
1515
1515
|
}
|
|
1516
|
-
const target = Buffer.allocUnsafe(
|
|
1516
|
+
const target = Buffer.allocUnsafe(merge3 ? dataLength + offset : offset);
|
|
1517
1517
|
target[0] = options.fin ? options.opcode | 128 : options.opcode;
|
|
1518
1518
|
if (options.rsv1) target[0] |= 64;
|
|
1519
1519
|
target[1] = payloadLength;
|
|
@@ -1530,7 +1530,7 @@ var require_sender = __commonJS({
|
|
|
1530
1530
|
target[offset - 2] = mask[2];
|
|
1531
1531
|
target[offset - 1] = mask[3];
|
|
1532
1532
|
if (skipMasking) return [target, data];
|
|
1533
|
-
if (
|
|
1533
|
+
if (merge3) {
|
|
1534
1534
|
applyMask(data, mask, target, offset, dataLength);
|
|
1535
1535
|
return [target];
|
|
1536
1536
|
}
|
|
@@ -2286,7 +2286,7 @@ var require_websocket = __commonJS({
|
|
|
2286
2286
|
var http = __require("http");
|
|
2287
2287
|
var net2 = __require("net");
|
|
2288
2288
|
var tls = __require("tls");
|
|
2289
|
-
var { randomBytes:
|
|
2289
|
+
var { randomBytes: randomBytes13, createHash: createHash11 } = __require("crypto");
|
|
2290
2290
|
var { Duplex, Readable } = __require("stream");
|
|
2291
2291
|
var { URL: URL2 } = __require("url");
|
|
2292
2292
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2824,7 +2824,7 @@ var require_websocket = __commonJS({
|
|
|
2824
2824
|
}
|
|
2825
2825
|
}
|
|
2826
2826
|
const defaultPort = isSecure ? 443 : 80;
|
|
2827
|
-
const key =
|
|
2827
|
+
const key = randomBytes13(16).toString("base64");
|
|
2828
2828
|
const request = isSecure ? https.request : http.request;
|
|
2829
2829
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2830
2830
|
let perMessageDeflate;
|
|
@@ -43048,13 +43048,13 @@ function refuse(message, io2 = realIo) {
|
|
|
43048
43048
|
|
|
43049
43049
|
// packages/agent-host/src/main.ts
|
|
43050
43050
|
import { parseArgs } from "node:util";
|
|
43051
|
-
import { randomBytes as
|
|
43051
|
+
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
43052
43052
|
import { hostname as hostname5 } from "node:os";
|
|
43053
|
-
import { dirname as
|
|
43054
|
-
import { mkdirSync as
|
|
43053
|
+
import { dirname as dirname26, join as join57 } from "node:path";
|
|
43054
|
+
import { mkdirSync as mkdirSync20, writeSync as writeSync3 } from "node:fs";
|
|
43055
43055
|
|
|
43056
43056
|
// packages/protocol/src/version.generated.ts
|
|
43057
|
-
var APP_VERSION = "0.1.0-beta.
|
|
43057
|
+
var APP_VERSION = "0.1.0-beta.40";
|
|
43058
43058
|
|
|
43059
43059
|
// packages/protocol/src/brand.ts
|
|
43060
43060
|
var APP_NAME = "Centralu";
|
|
@@ -43998,14 +43998,14 @@ function promiseAllObject(promisesObj) {
|
|
|
43998
43998
|
}
|
|
43999
43999
|
function randomString(length = 10) {
|
|
44000
44000
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
44001
|
-
let
|
|
44001
|
+
let str5 = "";
|
|
44002
44002
|
for (let i = 0; i < length; i++) {
|
|
44003
|
-
|
|
44003
|
+
str5 += chars[Math.floor(Math.random() * chars.length)];
|
|
44004
44004
|
}
|
|
44005
|
-
return
|
|
44005
|
+
return str5;
|
|
44006
44006
|
}
|
|
44007
|
-
function esc(
|
|
44008
|
-
return JSON.stringify(
|
|
44007
|
+
function esc(str5) {
|
|
44008
|
+
return JSON.stringify(str5);
|
|
44009
44009
|
}
|
|
44010
44010
|
function slugify(input) {
|
|
44011
44011
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -44119,8 +44119,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
|
|
|
44119
44119
|
"symbol",
|
|
44120
44120
|
"undefined"
|
|
44121
44121
|
]);
|
|
44122
|
-
function escapeRegex(
|
|
44123
|
-
return
|
|
44122
|
+
function escapeRegex(str5) {
|
|
44123
|
+
return str5.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
44124
44124
|
}
|
|
44125
44125
|
function clone(inst, def, params) {
|
|
44126
44126
|
const cl2 = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -57740,6 +57740,7 @@ function isProjectId(value) {
|
|
|
57740
57740
|
}
|
|
57741
57741
|
var ToolName = external_exports.string().min(1);
|
|
57742
57742
|
var AppId = external_exports.string();
|
|
57743
|
+
var PermissionPreset = external_exports.enum(["safe", "normal", "edits", "auto"]);
|
|
57743
57744
|
|
|
57744
57745
|
// packages/protocol/src/entities/sessions.ts
|
|
57745
57746
|
var SessionState = external_exports.enum(["idle", "working", "waiting_approval", "waiting_input", "limited", "error"]);
|
|
@@ -57885,7 +57886,9 @@ var MachineOperation = external_exports.object({
|
|
|
57885
57886
|
step: external_exports.string(),
|
|
57886
57887
|
/** The version it brings the machine to, when there is one */
|
|
57887
57888
|
target: external_exports.string().nullable().default(null),
|
|
57888
|
-
at: external_exports.number()
|
|
57889
|
+
at: external_exports.number(),
|
|
57890
|
+
/** What `wait_idle` (sessions in a turn) or `wait_open` (open terminals and project commands) waits for: how many. Additive (#1579) */
|
|
57891
|
+
count: external_exports.number().optional()
|
|
57889
57892
|
});
|
|
57890
57893
|
var MachineAutostart = external_exports.object({
|
|
57891
57894
|
on: external_exports.boolean().catch(false),
|
|
@@ -58502,8 +58505,40 @@ var AdapterCapabilities = external_exports.object({
|
|
|
58502
58505
|
* process reads the prompt cache as a live one does (~100% at 2, 6 and 10 minutes on the 1 h lifetime), for +0.7 s
|
|
58503
58506
|
* (p50) before the first token (#561). Absent is false: a tool whose resume is slow or untested keeps its process.
|
|
58504
58507
|
*/
|
|
58505
|
-
sleep: external_exports.boolean().optional()
|
|
58508
|
+
sleep: external_exports.boolean().optional(),
|
|
58509
|
+
/**
|
|
58510
|
+
* The permission presets this tool can keep (docs/plans/acp-adapter.md §5.3). Absent: every preset. Cursor's ACP
|
|
58511
|
+
* server has no switch that makes it ask before every change, so it does not list `safe`, and a session asked to run
|
|
58512
|
+
* it under `safe` is refused (`launchRefusal`) rather than run under a promise it cannot keep.
|
|
58513
|
+
*/
|
|
58514
|
+
presets: external_exports.array(PermissionPreset).optional(),
|
|
58515
|
+
/**
|
|
58516
|
+
* Whether the tool can be kept from reading the repository's own files (`.cursor/rules`, `AGENTS.md`, its config)
|
|
58517
|
+
* when the project is not trusted (#92). Absent: true. False: it runs only in trusted projects.
|
|
58518
|
+
*/
|
|
58519
|
+
keepsRepoFilesOut: external_exports.boolean().optional(),
|
|
58520
|
+
/**
|
|
58521
|
+
* Whether the tool takes Centralu's role text (`systemPromptAppend`). Absent: true. False: it cannot be a session that
|
|
58522
|
+
* is given one (the orchestrator, a coordinator, an app's agent), nor one that must read no setting files.
|
|
58523
|
+
*/
|
|
58524
|
+
roleText: external_exports.boolean().optional()
|
|
58506
58525
|
});
|
|
58526
|
+
var ORCHESTRATOR_DEMANDS = { permissionPreset: "normal", projectTrusted: false, noSettingFiles: true, roleText: true };
|
|
58527
|
+
function launchRefusal(caps, label, demands) {
|
|
58528
|
+
if (caps.roleText === false && (demands.roleText || demands.noSettingFiles)) {
|
|
58529
|
+
return `${label} cannot take Centralu's role instructions yet, so it cannot run the orchestrator, a coordinator or an app's agent.`;
|
|
58530
|
+
}
|
|
58531
|
+
if (caps.keepsRepoFilesOut === false && !demands.projectTrusted) {
|
|
58532
|
+
return `${label} cannot be kept from reading a project's own files (such as AGENTS.md and its rules), so it runs only in trusted projects.`;
|
|
58533
|
+
}
|
|
58534
|
+
if (caps.presets && !caps.presets.includes(demands.permissionPreset)) {
|
|
58535
|
+
const kept = caps.presets.map((p) => PRESET_LABEL[p]).join(" or ");
|
|
58536
|
+
const asked = PRESET_LABEL[demands.permissionPreset];
|
|
58537
|
+
return `${label} cannot be made to ask before a change as ${asked} does, so it cannot run under ${asked}${kept ? ` (only under ${kept})` : ""}.`;
|
|
58538
|
+
}
|
|
58539
|
+
return null;
|
|
58540
|
+
}
|
|
58541
|
+
var PRESET_LABEL = { safe: "Safe", normal: "Normal", edits: "Edits", auto: "Auto" };
|
|
58507
58542
|
var ModelOption = external_exports.object({
|
|
58508
58543
|
id: external_exports.string(),
|
|
58509
58544
|
label: external_exports.string(),
|
|
@@ -58522,7 +58557,6 @@ var ModelOption = external_exports.object({
|
|
|
58522
58557
|
});
|
|
58523
58558
|
|
|
58524
58559
|
// packages/protocol/src/entities/approvals.ts
|
|
58525
|
-
var PermissionPreset = external_exports.enum(["safe", "normal", "edits", "auto"]);
|
|
58526
58560
|
var ApprovalDecision = external_exports.enum(["allow", "deny", "always"]);
|
|
58527
58561
|
var ProjectConsentKind = external_exports.enum(["delegate", "apps", "agents"]);
|
|
58528
58562
|
var ApprovalScope = external_exports.enum(["session", "project"]);
|
|
@@ -59440,8 +59474,8 @@ function alwaysAllowKey(detail) {
|
|
|
59440
59474
|
if (detail.kind === "file_edit" && detail.path.trim() !== "" && detail.path !== NO_PATH) return detail.path;
|
|
59441
59475
|
return "";
|
|
59442
59476
|
}
|
|
59443
|
-
function matchesAllowRule(key,
|
|
59444
|
-
return key ===
|
|
59477
|
+
function matchesAllowRule(key, matcher2) {
|
|
59478
|
+
return key === matcher2;
|
|
59445
59479
|
}
|
|
59446
59480
|
|
|
59447
59481
|
// packages/protocol/src/events.ts
|
|
@@ -64520,6 +64554,9 @@ function commandInterpreter(env = process.env) {
|
|
|
64520
64554
|
function clonesFiles(platform = HOST_PLATFORM) {
|
|
64521
64555
|
return platform === "darwin";
|
|
64522
64556
|
}
|
|
64557
|
+
function installsCliAsVersionFolders(platform = HOST_PLATFORM) {
|
|
64558
|
+
return platform === "win32";
|
|
64559
|
+
}
|
|
64523
64560
|
function locksRunningPrograms(platform = HOST_PLATFORM) {
|
|
64524
64561
|
return platform === "win32";
|
|
64525
64562
|
}
|
|
@@ -64529,6 +64566,13 @@ function usesShippedConpty(platform = HOST_PLATFORM) {
|
|
|
64529
64566
|
function locksWatchedFolders(platform = HOST_PLATFORM) {
|
|
64530
64567
|
return platform === "win32";
|
|
64531
64568
|
}
|
|
64569
|
+
function systemSettingsFolder(app2, env = process.env, platform = HOST_PLATFORM) {
|
|
64570
|
+
if (platform === "win32") return win32.join(env.ProgramData || "C:\\ProgramData", app2);
|
|
64571
|
+
return platform === "darwin" ? `/Library/Application Support/${app2}` : `/etc/${app2}`;
|
|
64572
|
+
}
|
|
64573
|
+
function openCodeMatchesAnyCase(platform = HOST_PLATFORM) {
|
|
64574
|
+
return platform === "win32";
|
|
64575
|
+
}
|
|
64532
64576
|
function programFile(name, platform = HOST_PLATFORM) {
|
|
64533
64577
|
return platform === "win32" ? `${name}.exe` : name;
|
|
64534
64578
|
}
|
|
@@ -66281,15 +66325,15 @@ function buildViewCsp(csp) {
|
|
|
66281
66325
|
const frame = sanitizeDomains(csp?.frameDomains);
|
|
66282
66326
|
const base2 = sanitizeDomains(csp?.baseUriDomains);
|
|
66283
66327
|
const r = resource.kept.join(" ");
|
|
66284
|
-
const
|
|
66328
|
+
const join58 = (...parts) => parts.filter(Boolean).join(" ");
|
|
66285
66329
|
const policy = [
|
|
66286
66330
|
"default-src 'none'",
|
|
66287
|
-
`script-src ${
|
|
66288
|
-
`style-src ${
|
|
66289
|
-
`img-src ${
|
|
66290
|
-
`font-src ${
|
|
66291
|
-
`media-src ${
|
|
66292
|
-
`worker-src ${
|
|
66331
|
+
`script-src ${join58("'unsafe-inline'", r)}`,
|
|
66332
|
+
`style-src ${join58("'unsafe-inline'", r)}`,
|
|
66333
|
+
`img-src ${join58("data: blob:", r)}`,
|
|
66334
|
+
`font-src ${join58("data:", r)}`,
|
|
66335
|
+
`media-src ${join58("data: blob:", r)}`,
|
|
66336
|
+
`worker-src ${join58("blob:", r)}`,
|
|
66293
66337
|
`connect-src ${connect2.kept.length ? connect2.kept.join(" ") : "'none'"}`,
|
|
66294
66338
|
`frame-src ${frame.kept.length ? frame.kept.join(" ") : "'none'"}`,
|
|
66295
66339
|
"form-action 'none'",
|
|
@@ -67050,8 +67094,8 @@ function launchFor(path2, deps = realDeps(), depth = 0) {
|
|
|
67050
67094
|
return plain3;
|
|
67051
67095
|
}
|
|
67052
67096
|
const dir = win325.dirname(path2);
|
|
67053
|
-
const
|
|
67054
|
-
const target =
|
|
67097
|
+
const relative10 = shimTarget(text2);
|
|
67098
|
+
const target = relative10 ? win325.resolve(dir, relative10) : nodeShimEntry(text2, dir, deps);
|
|
67055
67099
|
if (!target || !deps.exists(target)) return plain3;
|
|
67056
67100
|
if (/\.(c|m)?js$/i.test(target)) return { command: deps.node, args: [target] };
|
|
67057
67101
|
if (/\.exe$/i.test(target)) return { command: target, args: [] };
|
|
@@ -67073,9 +67117,9 @@ function nodeShimEntry(text2, dir, deps) {
|
|
|
67073
67117
|
const started = /^\s*"%NODE_EXE%"\s+"%(\w+)%"\s+%\*\s*$/m.exec(text2)?.[1];
|
|
67074
67118
|
if (!started) return null;
|
|
67075
67119
|
const set3 = (name) => new RegExp(`^\\s*SET\\s+"${name}=%~dp0\\\\?([^"%]+)"`, "im").exec(text2)?.[1]?.trim() ?? null;
|
|
67076
|
-
const
|
|
67077
|
-
if (!
|
|
67078
|
-
const base2 = win325.resolve(dir,
|
|
67120
|
+
const relative10 = set3(started);
|
|
67121
|
+
if (!relative10) return null;
|
|
67122
|
+
const base2 = win325.resolve(dir, relative10);
|
|
67079
67123
|
const prefixJs = set3("NPM_PREFIX_JS");
|
|
67080
67124
|
if (!prefixJs || !deps.runJs) return base2;
|
|
67081
67125
|
const prefix = deps.runJs(win325.resolve(dir, prefixJs))?.trim().split(/\r?\n/).pop()?.trim();
|
|
@@ -67251,6 +67295,24 @@ function shoot(handle, signal, os2 = REAL_OS) {
|
|
|
67251
67295
|
const tree = descendants(rows3, [pid]);
|
|
67252
67296
|
return rows3.filter((r) => tree.has(r.pid));
|
|
67253
67297
|
}
|
|
67298
|
+
function notedGroups(rows3, root3, known, self2) {
|
|
67299
|
+
if (rows3.length === 0) return new Set(known);
|
|
67300
|
+
const live2 = new Set(rows3.map((r) => r.pgid));
|
|
67301
|
+
const kept = new Set([...known].filter((g5) => live2.has(g5)));
|
|
67302
|
+
if (rows3.some((r) => r.pid === root3)) {
|
|
67303
|
+
for (const g5 of killTargets([...rows3], root3, self2)) if (g5 !== root3) kept.add(g5);
|
|
67304
|
+
}
|
|
67305
|
+
return kept;
|
|
67306
|
+
}
|
|
67307
|
+
async function noteGroups(root3, known, platform = process.platform) {
|
|
67308
|
+
if (platform === "win32") return /* @__PURE__ */ new Set();
|
|
67309
|
+
let rows3 = [];
|
|
67310
|
+
try {
|
|
67311
|
+
rows3 = parsePs((await runFile("ps", ["-A", "-o", "pid=,ppid=,pgid="], { encoding: "utf8", timeout: 2e3, windowsHide: true })).stdout);
|
|
67312
|
+
} catch {
|
|
67313
|
+
}
|
|
67314
|
+
return notedGroups(rows3, root3, known, process.pid);
|
|
67315
|
+
}
|
|
67254
67316
|
function killTree(handle, signal, os2 = REAL_OS) {
|
|
67255
67317
|
shoot(handle, signal, os2);
|
|
67256
67318
|
}
|
|
@@ -68233,7 +68295,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
68233
68295
|
}
|
|
68234
68296
|
exports._ = _;
|
|
68235
68297
|
const plus = new _Code("+");
|
|
68236
|
-
function
|
|
68298
|
+
function str5(strs, ...args) {
|
|
68237
68299
|
const expr = [safeStringify(strs[0])];
|
|
68238
68300
|
let i = 0;
|
|
68239
68301
|
while (i < args.length) {
|
|
@@ -68244,7 +68306,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
68244
68306
|
optimize(expr);
|
|
68245
68307
|
return new _Code(expr);
|
|
68246
68308
|
}
|
|
68247
|
-
exports.str =
|
|
68309
|
+
exports.str = str5;
|
|
68248
68310
|
function addCodeArg(code2, arg) {
|
|
68249
68311
|
if (arg instanceof _Code) code2.push(...arg._items);
|
|
68250
68312
|
else if (arg instanceof Name) code2.push(arg);
|
|
@@ -68277,7 +68339,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
68277
68339
|
if (typeof b2 == "string" && b2[0] === '"' && !(a instanceof Name)) return `"${a}${b2.slice(1)}`;
|
|
68278
68340
|
}
|
|
68279
68341
|
function strConcat(c1, c2) {
|
|
68280
|
-
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 :
|
|
68342
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str5`${c1}${c2}`;
|
|
68281
68343
|
}
|
|
68282
68344
|
exports.strConcat = strConcat;
|
|
68283
68345
|
function interpolate(x) {
|
|
@@ -69156,21 +69218,21 @@ var require_util = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
69156
69218
|
return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
|
|
69157
69219
|
}
|
|
69158
69220
|
exports.schemaRefOrVal = schemaRefOrVal;
|
|
69159
|
-
function unescapeFragment(
|
|
69160
|
-
return unescapeJsonPointer(decodeURIComponent(
|
|
69221
|
+
function unescapeFragment(str5) {
|
|
69222
|
+
return unescapeJsonPointer(decodeURIComponent(str5));
|
|
69161
69223
|
}
|
|
69162
69224
|
exports.unescapeFragment = unescapeFragment;
|
|
69163
|
-
function escapeFragment(
|
|
69164
|
-
return encodeURIComponent(escapeJsonPointer(
|
|
69225
|
+
function escapeFragment(str5) {
|
|
69226
|
+
return encodeURIComponent(escapeJsonPointer(str5));
|
|
69165
69227
|
}
|
|
69166
69228
|
exports.escapeFragment = escapeFragment;
|
|
69167
|
-
function escapeJsonPointer(
|
|
69168
|
-
if (typeof
|
|
69169
|
-
return
|
|
69229
|
+
function escapeJsonPointer(str5) {
|
|
69230
|
+
if (typeof str5 == "number") return `${str5}`;
|
|
69231
|
+
return str5.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
69170
69232
|
}
|
|
69171
69233
|
exports.escapeJsonPointer = escapeJsonPointer;
|
|
69172
|
-
function unescapeJsonPointer(
|
|
69173
|
-
return
|
|
69234
|
+
function unescapeJsonPointer(str5) {
|
|
69235
|
+
return str5.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
69174
69236
|
}
|
|
69175
69237
|
exports.unescapeJsonPointer = unescapeJsonPointer;
|
|
69176
69238
|
function eachItem(xs2, f) {
|
|
@@ -70056,8 +70118,8 @@ var require_json_schema_traverse = /* @__PURE__ */ __commonJSMin(((exports, modu
|
|
|
70056
70118
|
post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
|
|
70057
70119
|
}
|
|
70058
70120
|
}
|
|
70059
|
-
function escapeJsonPtr(
|
|
70060
|
-
return
|
|
70121
|
+
function escapeJsonPtr(str5) {
|
|
70122
|
+
return str5.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
70061
70123
|
}
|
|
70062
70124
|
}));
|
|
70063
70125
|
var require_resolve = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -70949,9 +71011,9 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
70949
71011
|
isIPV6: false
|
|
70950
71012
|
};
|
|
70951
71013
|
}
|
|
70952
|
-
function findToken(
|
|
71014
|
+
function findToken(str5, token2) {
|
|
70953
71015
|
let ind = 0;
|
|
70954
|
-
for (let i = 0; i <
|
|
71016
|
+
for (let i = 0; i < str5.length; i++) if (str5[i] === token2) ind++;
|
|
70955
71017
|
return ind;
|
|
70956
71018
|
}
|
|
70957
71019
|
function removeDotSegments(path2) {
|
|
@@ -71225,41 +71287,41 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
71225
71287
|
schemelessOptions.skipEscape = true;
|
|
71226
71288
|
return serialize(resolved, schemelessOptions);
|
|
71227
71289
|
}
|
|
71228
|
-
function resolveComponent(base2,
|
|
71290
|
+
function resolveComponent(base2, relative10, options, skipNormalization) {
|
|
71229
71291
|
const target = {};
|
|
71230
71292
|
if (!skipNormalization) {
|
|
71231
71293
|
base2 = parse6(serialize(base2, options), options);
|
|
71232
|
-
|
|
71294
|
+
relative10 = parse6(serialize(relative10, options), options);
|
|
71233
71295
|
}
|
|
71234
71296
|
options = options || {};
|
|
71235
|
-
if (!options.tolerant &&
|
|
71236
|
-
target.scheme =
|
|
71237
|
-
target.userinfo =
|
|
71238
|
-
target.host =
|
|
71239
|
-
target.port =
|
|
71240
|
-
target.path = removeDotSegments(
|
|
71241
|
-
target.query =
|
|
71297
|
+
if (!options.tolerant && relative10.scheme) {
|
|
71298
|
+
target.scheme = relative10.scheme;
|
|
71299
|
+
target.userinfo = relative10.userinfo;
|
|
71300
|
+
target.host = relative10.host;
|
|
71301
|
+
target.port = relative10.port;
|
|
71302
|
+
target.path = removeDotSegments(relative10.path || "");
|
|
71303
|
+
target.query = relative10.query;
|
|
71242
71304
|
} else {
|
|
71243
|
-
if (
|
|
71244
|
-
target.userinfo =
|
|
71245
|
-
target.host =
|
|
71246
|
-
target.port =
|
|
71247
|
-
target.path = removeDotSegments(
|
|
71248
|
-
target.query =
|
|
71305
|
+
if (relative10.userinfo !== void 0 || relative10.host !== void 0 || relative10.port !== void 0) {
|
|
71306
|
+
target.userinfo = relative10.userinfo;
|
|
71307
|
+
target.host = relative10.host;
|
|
71308
|
+
target.port = relative10.port;
|
|
71309
|
+
target.path = removeDotSegments(relative10.path || "");
|
|
71310
|
+
target.query = relative10.query;
|
|
71249
71311
|
} else {
|
|
71250
|
-
if (!
|
|
71312
|
+
if (!relative10.path) {
|
|
71251
71313
|
target.path = base2.path;
|
|
71252
|
-
if (
|
|
71314
|
+
if (relative10.query !== void 0) target.query = relative10.query;
|
|
71253
71315
|
else target.query = base2.query;
|
|
71254
71316
|
} else {
|
|
71255
|
-
if (
|
|
71317
|
+
if (relative10.path[0] === "/") target.path = removeDotSegments(relative10.path);
|
|
71256
71318
|
else {
|
|
71257
|
-
if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" +
|
|
71258
|
-
else if (!base2.path) target.path =
|
|
71259
|
-
else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) +
|
|
71319
|
+
if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" + relative10.path;
|
|
71320
|
+
else if (!base2.path) target.path = relative10.path;
|
|
71321
|
+
else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative10.path;
|
|
71260
71322
|
target.path = removeDotSegments(target.path);
|
|
71261
71323
|
}
|
|
71262
|
-
target.query =
|
|
71324
|
+
target.query = relative10.query;
|
|
71263
71325
|
}
|
|
71264
71326
|
target.userinfo = base2.userinfo;
|
|
71265
71327
|
target.host = base2.host;
|
|
@@ -71267,7 +71329,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
71267
71329
|
}
|
|
71268
71330
|
target.scheme = base2.scheme;
|
|
71269
71331
|
}
|
|
71270
|
-
target.fragment =
|
|
71332
|
+
target.fragment = relative10.fragment;
|
|
71271
71333
|
return target;
|
|
71272
71334
|
}
|
|
71273
71335
|
function equal(uriA, uriB, options) {
|
|
@@ -71466,7 +71528,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
71466
71528
|
const util_1 = require_util();
|
|
71467
71529
|
const $dataRefSchema = require_data();
|
|
71468
71530
|
const uri_1 = require_uri();
|
|
71469
|
-
const defaultRegExp = (
|
|
71531
|
+
const defaultRegExp = (str5, flags) => new RegExp(str5, flags);
|
|
71470
71532
|
defaultRegExp.code = "new RegExp";
|
|
71471
71533
|
const META_IGNORE_OPTIONS = [
|
|
71472
71534
|
"removeAdditional",
|
|
@@ -72172,16 +72234,16 @@ var require_multipleOf = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
72172
72234
|
}));
|
|
72173
72235
|
var require_ucs2length = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
72174
72236
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
72175
|
-
function ucs2length(
|
|
72176
|
-
const len =
|
|
72237
|
+
function ucs2length(str5) {
|
|
72238
|
+
const len = str5.length;
|
|
72177
72239
|
let length = 0;
|
|
72178
72240
|
let pos = 0;
|
|
72179
72241
|
let value;
|
|
72180
72242
|
while (pos < len) {
|
|
72181
72243
|
length++;
|
|
72182
|
-
value =
|
|
72244
|
+
value = str5.charCodeAt(pos++);
|
|
72183
72245
|
if (value >= 55296 && value <= 56319 && pos < len) {
|
|
72184
|
-
value =
|
|
72246
|
+
value = str5.charCodeAt(pos);
|
|
72185
72247
|
if ((value & 64512) === 56320) pos++;
|
|
72186
72248
|
}
|
|
72187
72249
|
}
|
|
@@ -74892,8 +74954,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
74892
74954
|
30,
|
|
74893
74955
|
31
|
|
74894
74956
|
];
|
|
74895
|
-
function date5(
|
|
74896
|
-
const matches2 = DATE.exec(
|
|
74957
|
+
function date5(str5) {
|
|
74958
|
+
const matches2 = DATE.exec(str5);
|
|
74897
74959
|
if (!matches2) return false;
|
|
74898
74960
|
const year = +matches2[1];
|
|
74899
74961
|
const month = +matches2[2];
|
|
@@ -74908,8 +74970,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
74908
74970
|
}
|
|
74909
74971
|
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
74910
74972
|
function getTime(strictTimeZone) {
|
|
74911
|
-
return function time3(
|
|
74912
|
-
const matches2 = TIME.exec(
|
|
74973
|
+
return function time3(str5) {
|
|
74974
|
+
const matches2 = TIME.exec(str5);
|
|
74913
74975
|
if (!matches2) return false;
|
|
74914
74976
|
const hr2 = +matches2[1];
|
|
74915
74977
|
const min = +matches2[2];
|
|
@@ -74946,8 +75008,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
74946
75008
|
const DATE_TIME_SEPARATOR = /t|\s/i;
|
|
74947
75009
|
function getDateTime(strictTimeZone) {
|
|
74948
75010
|
const time3 = getTime(strictTimeZone);
|
|
74949
|
-
return function date_time(
|
|
74950
|
-
const dateTime =
|
|
75011
|
+
return function date_time(str5) {
|
|
75012
|
+
const dateTime = str5.split(DATE_TIME_SEPARATOR);
|
|
74951
75013
|
return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
|
|
74952
75014
|
};
|
|
74953
75015
|
}
|
|
@@ -74968,13 +75030,13 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
74968
75030
|
}
|
|
74969
75031
|
const NOT_URI_FRAGMENT = /\/|:/;
|
|
74970
75032
|
const URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
74971
|
-
function uri(
|
|
74972
|
-
return NOT_URI_FRAGMENT.test(
|
|
75033
|
+
function uri(str5) {
|
|
75034
|
+
return NOT_URI_FRAGMENT.test(str5) && URI.test(str5);
|
|
74973
75035
|
}
|
|
74974
75036
|
const BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
|
|
74975
|
-
function byte(
|
|
75037
|
+
function byte(str5) {
|
|
74976
75038
|
BYTE.lastIndex = 0;
|
|
74977
|
-
return BYTE.test(
|
|
75039
|
+
return BYTE.test(str5);
|
|
74978
75040
|
}
|
|
74979
75041
|
const MIN_INT32 = -(2 ** 31);
|
|
74980
75042
|
const MAX_INT32 = 2 ** 31 - 1;
|
|
@@ -74988,10 +75050,10 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
74988
75050
|
return true;
|
|
74989
75051
|
}
|
|
74990
75052
|
const Z_ANCHOR = /[^\\]\\Z/;
|
|
74991
|
-
function regex(
|
|
74992
|
-
if (Z_ANCHOR.test(
|
|
75053
|
+
function regex(str5) {
|
|
75054
|
+
if (Z_ANCHOR.test(str5)) return false;
|
|
74993
75055
|
try {
|
|
74994
|
-
new RegExp(
|
|
75056
|
+
new RegExp(str5);
|
|
74995
75057
|
return true;
|
|
74996
75058
|
} catch (e) {
|
|
74997
75059
|
return false;
|
|
@@ -81172,8 +81234,8 @@ var Server = class extends Protocol {
|
|
|
81172
81234
|
_registerLoggingHandler() {
|
|
81173
81235
|
this.setRequestHandler("logging/setLevel", async (request, ctx) => {
|
|
81174
81236
|
const transportSessionId = ctx.sessionId || ctx.http?.req?.headers.get("mcp-session-id") || void 0;
|
|
81175
|
-
const { level } = request.params;
|
|
81176
|
-
const parseResult = parseSchema(LoggingLevelSchema,
|
|
81237
|
+
const { level: level2 } = request.params;
|
|
81238
|
+
const parseResult = parseSchema(LoggingLevelSchema, level2);
|
|
81177
81239
|
if (parseResult.success) this._loggingLevels.set(transportSessionId, parseResult.data);
|
|
81178
81240
|
return {};
|
|
81179
81241
|
});
|
|
@@ -81184,18 +81246,18 @@ var Server = class extends Protocol {
|
|
|
81184
81246
|
...ctx,
|
|
81185
81247
|
mcpReq: {
|
|
81186
81248
|
...ctx.mcpReq,
|
|
81187
|
-
log: (
|
|
81249
|
+
log: (level2, data, logger) => {
|
|
81188
81250
|
if (!this._capabilities.logging) return Promise.resolve();
|
|
81189
81251
|
let threshold;
|
|
81190
81252
|
if (this._servedModernEra()) {
|
|
81191
81253
|
threshold = ctx.mcpReq.envelope?.[LOG_LEVEL_META_KEY];
|
|
81192
81254
|
if (threshold === void 0) return Promise.resolve();
|
|
81193
81255
|
} else threshold = this._loggingLevels.get(ctx.sessionId) ?? this._loggingLevels.get(void 0);
|
|
81194
|
-
if (threshold !== void 0 && this.LOG_LEVEL_SEVERITY.get(
|
|
81256
|
+
if (threshold !== void 0 && this.LOG_LEVEL_SEVERITY.get(level2) < this.LOG_LEVEL_SEVERITY.get(threshold)) return Promise.resolve();
|
|
81195
81257
|
return ctx.mcpReq.notify({
|
|
81196
81258
|
method: "notifications/message",
|
|
81197
81259
|
params: {
|
|
81198
|
-
level,
|
|
81260
|
+
level: level2,
|
|
81199
81261
|
data,
|
|
81200
81262
|
logger
|
|
81201
81263
|
}
|
|
@@ -81213,10 +81275,10 @@ var Server = class extends Protocol {
|
|
|
81213
81275
|
};
|
|
81214
81276
|
}
|
|
81215
81277
|
_loggingLevels = /* @__PURE__ */ new Map();
|
|
81216
|
-
LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((
|
|
81217
|
-
isMessageIgnored = (
|
|
81278
|
+
LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level2, index) => [level2, index]));
|
|
81279
|
+
isMessageIgnored = (level2, sessionId) => {
|
|
81218
81280
|
const currentLevel = this._loggingLevels.get(sessionId);
|
|
81219
|
-
return currentLevel ? this.LOG_LEVEL_SEVERITY.get(
|
|
81281
|
+
return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level2) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
|
|
81220
81282
|
};
|
|
81221
81283
|
/**
|
|
81222
81284
|
* Registers new capabilities. This can only be called before connecting to a transport.
|
|
@@ -89604,7 +89666,7 @@ var require_code$12 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
89604
89666
|
}
|
|
89605
89667
|
exports._ = _;
|
|
89606
89668
|
const plus = new _Code("+");
|
|
89607
|
-
function
|
|
89669
|
+
function str5(strs, ...args) {
|
|
89608
89670
|
const expr = [safeStringify(strs[0])];
|
|
89609
89671
|
let i = 0;
|
|
89610
89672
|
while (i < args.length) {
|
|
@@ -89615,7 +89677,7 @@ var require_code$12 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
89615
89677
|
optimize(expr);
|
|
89616
89678
|
return new _Code(expr);
|
|
89617
89679
|
}
|
|
89618
|
-
exports.str =
|
|
89680
|
+
exports.str = str5;
|
|
89619
89681
|
function addCodeArg(code2, arg) {
|
|
89620
89682
|
if (arg instanceof _Code) code2.push(...arg._items);
|
|
89621
89683
|
else if (arg instanceof Name) code2.push(arg);
|
|
@@ -89648,7 +89710,7 @@ var require_code$12 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
89648
89710
|
if (typeof b2 == "string" && b2[0] === '"' && !(a instanceof Name)) return `"${a}${b2.slice(1)}`;
|
|
89649
89711
|
}
|
|
89650
89712
|
function strConcat(c1, c2) {
|
|
89651
|
-
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 :
|
|
89713
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str5`${c1}${c2}`;
|
|
89652
89714
|
}
|
|
89653
89715
|
exports.strConcat = strConcat;
|
|
89654
89716
|
function interpolate(x) {
|
|
@@ -90527,21 +90589,21 @@ var require_util2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
90527
90589
|
return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
|
|
90528
90590
|
}
|
|
90529
90591
|
exports.schemaRefOrVal = schemaRefOrVal;
|
|
90530
|
-
function unescapeFragment(
|
|
90531
|
-
return unescapeJsonPointer(decodeURIComponent(
|
|
90592
|
+
function unescapeFragment(str5) {
|
|
90593
|
+
return unescapeJsonPointer(decodeURIComponent(str5));
|
|
90532
90594
|
}
|
|
90533
90595
|
exports.unescapeFragment = unescapeFragment;
|
|
90534
|
-
function escapeFragment(
|
|
90535
|
-
return encodeURIComponent(escapeJsonPointer(
|
|
90596
|
+
function escapeFragment(str5) {
|
|
90597
|
+
return encodeURIComponent(escapeJsonPointer(str5));
|
|
90536
90598
|
}
|
|
90537
90599
|
exports.escapeFragment = escapeFragment;
|
|
90538
|
-
function escapeJsonPointer(
|
|
90539
|
-
if (typeof
|
|
90540
|
-
return
|
|
90600
|
+
function escapeJsonPointer(str5) {
|
|
90601
|
+
if (typeof str5 == "number") return `${str5}`;
|
|
90602
|
+
return str5.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
90541
90603
|
}
|
|
90542
90604
|
exports.escapeJsonPointer = escapeJsonPointer;
|
|
90543
|
-
function unescapeJsonPointer(
|
|
90544
|
-
return
|
|
90605
|
+
function unescapeJsonPointer(str5) {
|
|
90606
|
+
return str5.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
90545
90607
|
}
|
|
90546
90608
|
exports.unescapeJsonPointer = unescapeJsonPointer;
|
|
90547
90609
|
function eachItem(xs2, f) {
|
|
@@ -91427,8 +91489,8 @@ var require_json_schema_traverse2 = /* @__PURE__ */ __commonJSMin2(((exports, mo
|
|
|
91427
91489
|
post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
|
|
91428
91490
|
}
|
|
91429
91491
|
}
|
|
91430
|
-
function escapeJsonPtr(
|
|
91431
|
-
return
|
|
91492
|
+
function escapeJsonPtr(str5) {
|
|
91493
|
+
return str5.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
91432
91494
|
}
|
|
91433
91495
|
}));
|
|
91434
91496
|
var require_resolve2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
@@ -92320,9 +92382,9 @@ var require_utils2 = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
|
|
|
92320
92382
|
isIPV6: false
|
|
92321
92383
|
};
|
|
92322
92384
|
}
|
|
92323
|
-
function findToken(
|
|
92385
|
+
function findToken(str5, token2) {
|
|
92324
92386
|
let ind = 0;
|
|
92325
|
-
for (let i = 0; i <
|
|
92387
|
+
for (let i = 0; i < str5.length; i++) if (str5[i] === token2) ind++;
|
|
92326
92388
|
return ind;
|
|
92327
92389
|
}
|
|
92328
92390
|
function removeDotSegments(path2) {
|
|
@@ -92596,41 +92658,41 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
|
|
|
92596
92658
|
schemelessOptions.skipEscape = true;
|
|
92597
92659
|
return serialize(resolved, schemelessOptions);
|
|
92598
92660
|
}
|
|
92599
|
-
function resolveComponent(base2,
|
|
92661
|
+
function resolveComponent(base2, relative10, options, skipNormalization) {
|
|
92600
92662
|
const target = {};
|
|
92601
92663
|
if (!skipNormalization) {
|
|
92602
92664
|
base2 = parse6(serialize(base2, options), options);
|
|
92603
|
-
|
|
92665
|
+
relative10 = parse6(serialize(relative10, options), options);
|
|
92604
92666
|
}
|
|
92605
92667
|
options = options || {};
|
|
92606
|
-
if (!options.tolerant &&
|
|
92607
|
-
target.scheme =
|
|
92608
|
-
target.userinfo =
|
|
92609
|
-
target.host =
|
|
92610
|
-
target.port =
|
|
92611
|
-
target.path = removeDotSegments(
|
|
92612
|
-
target.query =
|
|
92668
|
+
if (!options.tolerant && relative10.scheme) {
|
|
92669
|
+
target.scheme = relative10.scheme;
|
|
92670
|
+
target.userinfo = relative10.userinfo;
|
|
92671
|
+
target.host = relative10.host;
|
|
92672
|
+
target.port = relative10.port;
|
|
92673
|
+
target.path = removeDotSegments(relative10.path || "");
|
|
92674
|
+
target.query = relative10.query;
|
|
92613
92675
|
} else {
|
|
92614
|
-
if (
|
|
92615
|
-
target.userinfo =
|
|
92616
|
-
target.host =
|
|
92617
|
-
target.port =
|
|
92618
|
-
target.path = removeDotSegments(
|
|
92619
|
-
target.query =
|
|
92676
|
+
if (relative10.userinfo !== void 0 || relative10.host !== void 0 || relative10.port !== void 0) {
|
|
92677
|
+
target.userinfo = relative10.userinfo;
|
|
92678
|
+
target.host = relative10.host;
|
|
92679
|
+
target.port = relative10.port;
|
|
92680
|
+
target.path = removeDotSegments(relative10.path || "");
|
|
92681
|
+
target.query = relative10.query;
|
|
92620
92682
|
} else {
|
|
92621
|
-
if (!
|
|
92683
|
+
if (!relative10.path) {
|
|
92622
92684
|
target.path = base2.path;
|
|
92623
|
-
if (
|
|
92685
|
+
if (relative10.query !== void 0) target.query = relative10.query;
|
|
92624
92686
|
else target.query = base2.query;
|
|
92625
92687
|
} else {
|
|
92626
|
-
if (
|
|
92688
|
+
if (relative10.path[0] === "/") target.path = removeDotSegments(relative10.path);
|
|
92627
92689
|
else {
|
|
92628
|
-
if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" +
|
|
92629
|
-
else if (!base2.path) target.path =
|
|
92630
|
-
else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) +
|
|
92690
|
+
if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" + relative10.path;
|
|
92691
|
+
else if (!base2.path) target.path = relative10.path;
|
|
92692
|
+
else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative10.path;
|
|
92631
92693
|
target.path = removeDotSegments(target.path);
|
|
92632
92694
|
}
|
|
92633
|
-
target.query =
|
|
92695
|
+
target.query = relative10.query;
|
|
92634
92696
|
}
|
|
92635
92697
|
target.userinfo = base2.userinfo;
|
|
92636
92698
|
target.host = base2.host;
|
|
@@ -92638,7 +92700,7 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
|
|
|
92638
92700
|
}
|
|
92639
92701
|
target.scheme = base2.scheme;
|
|
92640
92702
|
}
|
|
92641
|
-
target.fragment =
|
|
92703
|
+
target.fragment = relative10.fragment;
|
|
92642
92704
|
return target;
|
|
92643
92705
|
}
|
|
92644
92706
|
function equal(uriA, uriB, options) {
|
|
@@ -92837,7 +92899,7 @@ var require_core$32 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
92837
92899
|
const util_1 = require_util2();
|
|
92838
92900
|
const $dataRefSchema = require_data2();
|
|
92839
92901
|
const uri_1 = require_uri2();
|
|
92840
|
-
const defaultRegExp = (
|
|
92902
|
+
const defaultRegExp = (str5, flags) => new RegExp(str5, flags);
|
|
92841
92903
|
defaultRegExp.code = "new RegExp";
|
|
92842
92904
|
const META_IGNORE_OPTIONS = [
|
|
92843
92905
|
"removeAdditional",
|
|
@@ -93543,16 +93605,16 @@ var require_multipleOf2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
93543
93605
|
}));
|
|
93544
93606
|
var require_ucs2length2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
93545
93607
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
93546
|
-
function ucs2length(
|
|
93547
|
-
const len =
|
|
93608
|
+
function ucs2length(str5) {
|
|
93609
|
+
const len = str5.length;
|
|
93548
93610
|
let length = 0;
|
|
93549
93611
|
let pos = 0;
|
|
93550
93612
|
let value;
|
|
93551
93613
|
while (pos < len) {
|
|
93552
93614
|
length++;
|
|
93553
|
-
value =
|
|
93615
|
+
value = str5.charCodeAt(pos++);
|
|
93554
93616
|
if (value >= 55296 && value <= 56319 && pos < len) {
|
|
93555
|
-
value =
|
|
93617
|
+
value = str5.charCodeAt(pos);
|
|
93556
93618
|
if ((value & 64512) === 56320) pos++;
|
|
93557
93619
|
}
|
|
93558
93620
|
}
|
|
@@ -96263,8 +96325,8 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
96263
96325
|
30,
|
|
96264
96326
|
31
|
|
96265
96327
|
];
|
|
96266
|
-
function date5(
|
|
96267
|
-
const matches2 = DATE.exec(
|
|
96328
|
+
function date5(str5) {
|
|
96329
|
+
const matches2 = DATE.exec(str5);
|
|
96268
96330
|
if (!matches2) return false;
|
|
96269
96331
|
const year = +matches2[1];
|
|
96270
96332
|
const month = +matches2[2];
|
|
@@ -96279,8 +96341,8 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
96279
96341
|
}
|
|
96280
96342
|
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
96281
96343
|
function getTime(strictTimeZone) {
|
|
96282
|
-
return function time3(
|
|
96283
|
-
const matches2 = TIME.exec(
|
|
96344
|
+
return function time3(str5) {
|
|
96345
|
+
const matches2 = TIME.exec(str5);
|
|
96284
96346
|
if (!matches2) return false;
|
|
96285
96347
|
const hr2 = +matches2[1];
|
|
96286
96348
|
const min = +matches2[2];
|
|
@@ -96317,8 +96379,8 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
96317
96379
|
const DATE_TIME_SEPARATOR = /t|\s/i;
|
|
96318
96380
|
function getDateTime(strictTimeZone) {
|
|
96319
96381
|
const time3 = getTime(strictTimeZone);
|
|
96320
|
-
return function date_time(
|
|
96321
|
-
const dateTime =
|
|
96382
|
+
return function date_time(str5) {
|
|
96383
|
+
const dateTime = str5.split(DATE_TIME_SEPARATOR);
|
|
96322
96384
|
return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
|
|
96323
96385
|
};
|
|
96324
96386
|
}
|
|
@@ -96339,13 +96401,13 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
96339
96401
|
}
|
|
96340
96402
|
const NOT_URI_FRAGMENT = /\/|:/;
|
|
96341
96403
|
const URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
96342
|
-
function uri(
|
|
96343
|
-
return NOT_URI_FRAGMENT.test(
|
|
96404
|
+
function uri(str5) {
|
|
96405
|
+
return NOT_URI_FRAGMENT.test(str5) && URI.test(str5);
|
|
96344
96406
|
}
|
|
96345
96407
|
const BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
|
|
96346
|
-
function byte(
|
|
96408
|
+
function byte(str5) {
|
|
96347
96409
|
BYTE.lastIndex = 0;
|
|
96348
|
-
return BYTE.test(
|
|
96410
|
+
return BYTE.test(str5);
|
|
96349
96411
|
}
|
|
96350
96412
|
const MIN_INT32 = -(2 ** 31);
|
|
96351
96413
|
const MAX_INT32 = 2 ** 31 - 1;
|
|
@@ -96359,10 +96421,10 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
96359
96421
|
return true;
|
|
96360
96422
|
}
|
|
96361
96423
|
const Z_ANCHOR = /[^\\]\\Z/;
|
|
96362
|
-
function regex(
|
|
96363
|
-
if (Z_ANCHOR.test(
|
|
96424
|
+
function regex(str5) {
|
|
96425
|
+
if (Z_ANCHOR.test(str5)) return false;
|
|
96364
96426
|
try {
|
|
96365
|
-
new RegExp(
|
|
96427
|
+
new RegExp(str5);
|
|
96366
96428
|
return true;
|
|
96367
96429
|
} catch (e) {
|
|
96368
96430
|
return false;
|
|
@@ -98240,10 +98302,10 @@ var Client = class extends Protocol2 {
|
|
|
98240
98302
|
* Remains functional during the deprecation window (at least twelve months).
|
|
98241
98303
|
* Migrate to stderr logging (STDIO servers) or OpenTelemetry.
|
|
98242
98304
|
*/
|
|
98243
|
-
async setLoggingLevel(
|
|
98305
|
+
async setLoggingLevel(level2, options) {
|
|
98244
98306
|
return this.request({
|
|
98245
98307
|
method: "logging/setLevel",
|
|
98246
|
-
params: { level }
|
|
98308
|
+
params: { level: level2 }
|
|
98247
98309
|
}, options);
|
|
98248
98310
|
}
|
|
98249
98311
|
/** Retrieves a prompt by name from the server, passing the given arguments for template substitution. */
|
|
@@ -108706,14 +108768,43 @@ var RoutedCards = class {
|
|
|
108706
108768
|
}
|
|
108707
108769
|
};
|
|
108708
108770
|
|
|
108771
|
+
// packages/agent-host/src/mail/address.ts
|
|
108772
|
+
var SEGMENT = "[A-Za-z0-9][A-Za-z0-9._~-]{0,63}";
|
|
108773
|
+
var SEGMENT_RE = new RegExp(`^${SEGMENT}$`);
|
|
108774
|
+
var SESSION_RE = new RegExp(`^session:(${SEGMENT})/(${SEGMENT})$`);
|
|
108775
|
+
var APP_RE = new RegExp(`^app:(${SEGMENT})/(${SEGMENT})/(${SEGMENT})$`);
|
|
108776
|
+
var PERSON = "person";
|
|
108777
|
+
var CENTRALU = "centralu";
|
|
108778
|
+
function sessionAddress(host, sessionId) {
|
|
108779
|
+
return checked(`session:${host}/${sessionId}`);
|
|
108780
|
+
}
|
|
108781
|
+
function parseAddress(address) {
|
|
108782
|
+
if (address === PERSON) return { kind: "person" };
|
|
108783
|
+
if (address === CENTRALU) return { kind: "centralu" };
|
|
108784
|
+
const [, host, sessionId] = SESSION_RE.exec(address) ?? [];
|
|
108785
|
+
if (host !== void 0 && sessionId !== void 0) return { kind: "session", host, sessionId };
|
|
108786
|
+
const [, appHost, projectId, appId] = APP_RE.exec(address) ?? [];
|
|
108787
|
+
if (appHost !== void 0 && projectId !== void 0 && appId !== void 0) return { kind: "app", host: appHost, projectId, appId };
|
|
108788
|
+
return null;
|
|
108789
|
+
}
|
|
108790
|
+
function checked(address) {
|
|
108791
|
+
if (!parseAddress(address)) throw new Error(`Not a mail address: ${JSON.stringify(address)}`);
|
|
108792
|
+
return address;
|
|
108793
|
+
}
|
|
108794
|
+
|
|
108709
108795
|
// packages/agent-host/src/sessions/handoff-children.ts
|
|
108710
108796
|
var SETTLED_CHILD = "finished; waiting for more work";
|
|
108711
108797
|
function movedChildrenNote(moved, commands = "") {
|
|
108712
108798
|
return [
|
|
108713
108799
|
"## Work handed over with this note (added by Centralu)",
|
|
108714
108800
|
"",
|
|
108715
|
-
|
|
108716
|
-
|
|
108801
|
+
...moved.some((c) => c.thread) ? [
|
|
108802
|
+
"The previous session started or asked for these, or held these mail threads. They are yours now as each line says",
|
|
108803
|
+
"(a finished session waits for more work); use the handle with the tool it names."
|
|
108804
|
+
] : [
|
|
108805
|
+
"The previous session started or asked for these. They now answer to you as each line says (a finished one waits",
|
|
108806
|
+
"for more work); use the handle with the tool it names."
|
|
108807
|
+
],
|
|
108717
108808
|
...commands ? [commands] : [],
|
|
108718
108809
|
"",
|
|
108719
108810
|
...moved.map((c) => `- ${movedLabel(c)} \u2014 ${c.state} \u2014 handle: ${c.handle}. ${c.can}`),
|
|
@@ -108725,6 +108816,7 @@ function movedChildrenNote(moved, commands = "") {
|
|
|
108725
108816
|
}
|
|
108726
108817
|
function movedLabel(c) {
|
|
108727
108818
|
if (c.request) return "start_agent request";
|
|
108819
|
+
if (c.thread) return `mail thread "${oneLineText(c.name, 120)}"`;
|
|
108728
108820
|
return `"${oneLineText(c.name, 120)}"${c.project ? ` (project ${c.project})` : ""}`;
|
|
108729
108821
|
}
|
|
108730
108822
|
var HandoffChildren = class {
|
|
@@ -108827,18 +108919,30 @@ var HandoffChildren = class {
|
|
|
108827
108919
|
can: "The card stands in your session; if the person allows it, the session it starts is yours and its report comes to you. agent_session (status, wait, stop) works on the handle meanwhile."
|
|
108828
108920
|
});
|
|
108829
108921
|
}
|
|
108830
|
-
|
|
108922
|
+
const mailWhich = scope === "live" ? sessions.length ? { assignees: sessions } : null : "all";
|
|
108923
|
+
if (!moved.length && mailWhich === null) return [];
|
|
108924
|
+
let threads = [];
|
|
108831
108925
|
try {
|
|
108832
|
-
this.host.store.children.moveChildren(
|
|
108833
|
-
|
|
108834
|
-
|
|
108835
|
-
|
|
108836
|
-
|
|
108837
|
-
|
|
108926
|
+
this.host.store.children.moveChildren(
|
|
108927
|
+
from,
|
|
108928
|
+
to,
|
|
108929
|
+
{
|
|
108930
|
+
sessions,
|
|
108931
|
+
startRequests: starts.map((r) => ({ requestId: r.req.requestId, request: { ...r.req, callerId: to } })),
|
|
108932
|
+
armReports,
|
|
108933
|
+
// With no child moving, the memory side does not run, so the queue's rows do not move either
|
|
108934
|
+
queued: !removed && moved.length > 0
|
|
108935
|
+
},
|
|
108936
|
+
mailWhich === null ? void 0 : () => void (threads = this.host.mailHandOver(from, to, mailWhich))
|
|
108937
|
+
);
|
|
108838
108938
|
} catch (e) {
|
|
108839
|
-
console.error(`[agent-host] handoff left the children with ${from.slice(0, 8)}: ${e.message}`);
|
|
108939
|
+
console.error(`[agent-host] handoff left the children and the mail with ${from.slice(0, 8)}: ${e.message}`);
|
|
108840
108940
|
return [];
|
|
108841
108941
|
}
|
|
108942
|
+
const childCount = moved.length;
|
|
108943
|
+
moved.push(...threads.map((t) => movedThread(t)));
|
|
108944
|
+
if (!moved.length) return [];
|
|
108945
|
+
if (removed && threads.some((t) => t.unread > 0)) this.host.mailPoke(to);
|
|
108842
108946
|
for (const id3 of sessions) {
|
|
108843
108947
|
const s = this.host.meta.get(id3);
|
|
108844
108948
|
if (s.askedBy !== from && s.parentSessionId !== from) continue;
|
|
@@ -108846,7 +108950,7 @@ var HandoffChildren = class {
|
|
|
108846
108950
|
if (s.parentSessionId === from) s.parentSessionId = to;
|
|
108847
108951
|
this.host.emit({ type: "session_owner", sessionId: id3, askedBy: s.askedBy ?? null, parentSessionId: s.parentSessionId ?? null });
|
|
108848
108952
|
}
|
|
108849
|
-
this.host.children.moveOwner(from, to, { sessions, armReports, starts, queued: !removed }, () => {
|
|
108953
|
+
if (childCount) this.host.children.moveOwner(from, to, { sessions, armReports, starts, queued: !removed }, () => {
|
|
108850
108954
|
for (const r of starts) {
|
|
108851
108955
|
r.req.callerId = to;
|
|
108852
108956
|
const ask = this.host.hostCards.hostAsks.get(r.req.requestId);
|
|
@@ -108874,10 +108978,22 @@ var HandoffChildren = class {
|
|
|
108874
108978
|
text: `Handed over to \u201C${heir.name}\u201D [${to}], which follows them from now on: ${items.join("; ")}. This session no longer does.`
|
|
108875
108979
|
});
|
|
108876
108980
|
}
|
|
108877
|
-
console.error(`[agent-host] handoff ${from.slice(0, 8)} -> ${to.slice(0, 8)} (${scope}): moved ${sessions.length} session(s), ${starts.length} start request(s)`);
|
|
108981
|
+
console.error(`[agent-host] handoff ${from.slice(0, 8)} -> ${to.slice(0, 8)} (${scope}): moved ${sessions.length} session(s), ${starts.length} start request(s), ${threads.length} mail thread(s)`);
|
|
108878
108982
|
return moved;
|
|
108879
108983
|
}
|
|
108880
108984
|
};
|
|
108985
|
+
function movedThread(t) {
|
|
108986
|
+
const unread = t.unread ? `, ${t.unread} unread message${t.unread === 1 ? "" : "s"} now waiting for you` : "";
|
|
108987
|
+
const other = (address) => {
|
|
108988
|
+
const p = address === null ? null : parseAddress(address);
|
|
108989
|
+
if (!p) return "nobody";
|
|
108990
|
+
if (p.kind === "session") return `session ${p.sessionId}`;
|
|
108991
|
+
return p.kind === "person" ? "the person" : p.kind === "app" ? `app ${p.appId}` : "Centralu";
|
|
108992
|
+
};
|
|
108993
|
+
const can = t.kind === "closed" ? "It is closed; what the previous session had not read of it waits for you. Read it with the mail tool." : t.kind === "left" ? t.role === "owner" ? "You are its assignee and the previous session was its owner, so it did not move: once that session is archived, the person owns it." : "You own it and the previous session had its work, so it did not move: once that session is archived, it is open with nobody on it; reassign or close it." : t.role === "owner" ? `You own it now (assignee: ${other(t.thread.assignee)}); its reports come to you in it. Read and answer it with the mail tool.` : t.role === "assignee" ? `It is assigned to you now (owner: ${other(t.thread.owner)}): the work is yours to do, and your report goes to its owner. Read it with the mail tool.` : "You are in it in the previous session's place. Read it with the mail tool.";
|
|
108994
|
+
const state = t.kind === "closed" ? `done${t.thread.outcome ? ` (${t.thread.outcome})` : ""}` : t.thread.state;
|
|
108995
|
+
return { handle: t.thread.id, name: t.thread.subject, state: `${state}${unread}`, can, thread: true };
|
|
108996
|
+
}
|
|
108881
108997
|
|
|
108882
108998
|
// packages/agent-host/src/sessions/child-ownership.ts
|
|
108883
108999
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
@@ -108932,6 +109048,8 @@ var ChildOwnership = class {
|
|
|
108932
109048
|
* work's, as every turn was.
|
|
108933
109049
|
*/
|
|
108934
109050
|
starters = /* @__PURE__ */ new Map();
|
|
109051
|
+
/** Turns the tool started by itself, no message behind them (`turnStarted`, #1576); memory only, a swap forgets it */
|
|
109052
|
+
cliTurns = /* @__PURE__ */ new Set();
|
|
108935
109053
|
lastBy = /* @__PURE__ */ new Map();
|
|
108936
109054
|
/** Messages that went into the running turn and may not have been taken into it (#1377), by session */
|
|
108937
109055
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -109278,6 +109396,8 @@ var ChildOwnership = class {
|
|
|
109278
109396
|
const next = found && Date.now() - found.at < NEXT_STARTER_MS ? found : void 0;
|
|
109279
109397
|
const { at: _at, ...starter } = next ?? { by: "work", at: 0 };
|
|
109280
109398
|
this.starters.set(sessionId, starter);
|
|
109399
|
+
if (next) this.cliTurns.delete(sessionId);
|
|
109400
|
+
else this.cliTurns.add(sessionId);
|
|
109281
109401
|
this.persistTurn(sessionId);
|
|
109282
109402
|
const source = next ? this.nextSource.get(sessionId) ?? "caller" : "caller";
|
|
109283
109403
|
this.nextSource.delete(sessionId);
|
|
@@ -109298,6 +109418,7 @@ var ChildOwnership = class {
|
|
|
109298
109418
|
turnEnded(sessionId, taken) {
|
|
109299
109419
|
const starter = { ...this.starters.get(sessionId) ?? { by: "work" } };
|
|
109300
109420
|
this.starters.delete(sessionId);
|
|
109421
|
+
this.cliTurns.delete(sessionId);
|
|
109301
109422
|
const pending = this.pending.get(sessionId) ?? [];
|
|
109302
109423
|
this.pending.delete(sessionId);
|
|
109303
109424
|
for (const p of pending) {
|
|
@@ -109312,6 +109433,24 @@ var ChildOwnership = class {
|
|
|
109312
109433
|
this.store.settings.deleteAppSetting(reportTurnKey(sessionId));
|
|
109313
109434
|
return starter;
|
|
109314
109435
|
}
|
|
109436
|
+
/**
|
|
109437
|
+
* Whether the turn running is the paused work's continuing (#1576): the caller's report is held over the session's own
|
|
109438
|
+
* background work, and the turn the tool started when that work ended is the work's (`turnStarted`, no message began
|
|
109439
|
+
* it) with no person's message in it. That turn's end is the report the caller is owed, so it is the caller's too.
|
|
109440
|
+
* The one place that says so, as `reportBackIfAwaited` reads the same turn for its report
|
|
109441
|
+
*/
|
|
109442
|
+
continuesPause(sessionId, callerId) {
|
|
109443
|
+
if (this.reports.get(sessionId) !== callerId || !this.paused.has(sessionId)) return false;
|
|
109444
|
+
const owner = this.turns.get(sessionId)?.callerId;
|
|
109445
|
+
if (owner && owner !== callerId || this.mailTurns.get(sessionId) && this.mailTurns.get(sessionId)?.ownerId !== callerId) return false;
|
|
109446
|
+
const starter = this.starters.get(sessionId);
|
|
109447
|
+
return this.cliTurns.has(sessionId) && starter?.by === "work" && !this.personInTurn(sessionId);
|
|
109448
|
+
}
|
|
109449
|
+
/** Whether a person started the running turn or wrote into it (#1576): a refusal says "the person may be using it" for no other */
|
|
109450
|
+
personInTurn(sessionId) {
|
|
109451
|
+
const starter = this.starters.get(sessionId);
|
|
109452
|
+
return starter?.by !== "work" || !!starter.wrote || !!starter.added || (this.pending.get(sessionId) ?? []).some((p) => p.by === "person");
|
|
109453
|
+
}
|
|
109315
109454
|
/** Whose the session's last turn was (#1377): its last response is the person's answer when it was theirs */
|
|
109316
109455
|
lastTurnBy(sessionId) {
|
|
109317
109456
|
return this.lastBy.get(sessionId);
|
|
@@ -112244,7 +112383,8 @@ var AgentSessionActions = class {
|
|
|
112244
112383
|
};
|
|
112245
112384
|
}
|
|
112246
112385
|
const from = { sessionId: callerId, name: labelOf(this.host.store.messages, caller) };
|
|
112247
|
-
|
|
112386
|
+
const continuing = (!d2 || d2.settled) && inTurn(child.state) && this.host.children.continuesPause(child.id, callerId);
|
|
112387
|
+
if (d2 && !d2.settled && observing || (!d2 || d2.settled) && !continuing && this.host.busyForCaller(callerId, child.id)) {
|
|
112248
112388
|
if (!opts.ifBusy) return { ok: true, tool: child.tool, ...this.host.busyAnswer(callerId, child, "agent_session") };
|
|
112249
112389
|
if (opts.ifBusy === "queue") return { ok: true, tool: child.tool, ...this.host.enqueue(callerId, child, "agent_session", opts.text ?? "") };
|
|
112250
112390
|
if (opts.ifBusy === "fork" || opts.ifBusy === "fork_read") {
|
|
@@ -112258,16 +112398,18 @@ var AgentSessionActions = class {
|
|
|
112258
112398
|
if (!note2.available || !d2 || d2.settled) return { ok: false, error: `No note to "${child.name}": ${note2.why ?? "nothing is running there to add it to"}. Queue it or fork it instead.` };
|
|
112259
112399
|
this.host.autoBoundaryOpen(callerId, "a note into a turn that is not its own");
|
|
112260
112400
|
}
|
|
112261
|
-
if (d2 && !d2.settled) {
|
|
112401
|
+
if (d2 && !d2.settled || continuing) {
|
|
112262
112402
|
const text2 = agentNoteFrame(from.name, opts.text ?? "");
|
|
112263
112403
|
if (!this.host.adapters.get(child.tool)?.capabilities.midTurnInput || !this.host.handles.get(child.id)?.sendTracked) {
|
|
112264
112404
|
return { ok: false, error: `"${child.name}" cannot take messages while working. Wait for it (action "wait") or stop it first.` };
|
|
112265
112405
|
}
|
|
112266
|
-
const own = observing ? this.flow.observeTurn(child, true, true) : void 0;
|
|
112406
|
+
const own = observing || continuing ? this.flow.observeTurn(child, true, true) : void 0;
|
|
112267
112407
|
if (own) void own.run.wait.done.then(own.release, own.release);
|
|
112268
|
-
const record2 = own ? { run: own.run, waiters: 0, told: false } : void 0;
|
|
112408
|
+
const record2 = own && !continuing ? { run: own.run, waiters: 0, told: false } : void 0;
|
|
112269
112409
|
if (record2) this.host.children.setObserverNote(key, record2);
|
|
112270
|
-
const
|
|
112410
|
+
const run4 = own?.run ?? d2;
|
|
112411
|
+
if (!run4) return { ok: false, error: `No note to "${child.name}": nothing is running there to add it to.` };
|
|
112412
|
+
const wait = run4.wait;
|
|
112271
112413
|
const releaseHold = wait.holdInput();
|
|
112272
112414
|
const release = () => {
|
|
112273
112415
|
releaseHold();
|
|
@@ -112278,7 +112420,9 @@ var AgentSessionActions = class {
|
|
|
112278
112420
|
const fail4 = (e) => {
|
|
112279
112421
|
if (!record2) {
|
|
112280
112422
|
wait.fail(e);
|
|
112281
|
-
|
|
112423
|
+
release();
|
|
112424
|
+
if (continuing) void this.flow.tellNoteRefused(callerId, child.id, e.message);
|
|
112425
|
+
return;
|
|
112282
112426
|
}
|
|
112283
112427
|
wait.fail(new Error(`your note was not delivered: ${e.message}`));
|
|
112284
112428
|
release();
|
|
@@ -112298,11 +112442,13 @@ var AgentSessionActions = class {
|
|
|
112298
112442
|
return { ok: false, error: `Could not send it: ${e.message}` };
|
|
112299
112443
|
}
|
|
112300
112444
|
if (!observing) this.host.children.delegated(callerId, child.id, "agent_session", opts.text ?? "");
|
|
112301
|
-
const working = this.flow.agentWorking(
|
|
112445
|
+
const working = this.flow.agentWorking(run4, false, observing || continuing);
|
|
112446
|
+
if (continuing && working.ok && working.state === "working") return { ...working, reported: this.host.children.awaitingReport.get(child.id) === callerId, via: "note" };
|
|
112302
112447
|
return working.ok && observing ? { ...working, via: "note" } : working;
|
|
112303
112448
|
}
|
|
112304
112449
|
if (inTurn(child.state) || this.host.agentRuns.has(child.id) && !this.host.agentRuns.get(child.id).finished) {
|
|
112305
|
-
|
|
112450
|
+
const person = this.host.children.personInTurn(child.id);
|
|
112451
|
+
return { ok: false, error: `"${child.name}" is busy with ${person ? "something else (the person may be using it)" : "a turn you did not give it (another session, or its own tool, started it)"}. Try again later.` };
|
|
112306
112452
|
}
|
|
112307
112453
|
if (d2) this.host.children.dropDelegation(key);
|
|
112308
112454
|
const full = this.flow.starts.agentCeilingError(callerId, child.projectId ?? "");
|
|
@@ -112430,6 +112576,10 @@ var RemoteAgents = class {
|
|
|
112430
112576
|
toldCards = /* @__PURE__ */ new Set();
|
|
112431
112577
|
/** Whose each child's turn is, as settled in this host (`Whose`); one not here is read from its record (`whoseOf`) */
|
|
112432
112578
|
whose = /* @__PURE__ */ new Map();
|
|
112579
|
+
/** What made a turn another's, when it is told (#1576): a message from another session of this hub, else it is read as the person's */
|
|
112580
|
+
otherBy = /* @__PURE__ */ new Map();
|
|
112581
|
+
/** Notes sent into a running turn whose record has not come back yet (memory only): two quick notes both are the caller's own */
|
|
112582
|
+
notesSent = /* @__PURE__ */ new Map();
|
|
112433
112583
|
/** Children an instruction is on its way to: its own send's answer says what came of it, and the machine is not read meanwhile */
|
|
112434
112584
|
pending = /* @__PURE__ */ new Set();
|
|
112435
112585
|
/** Children whose turn's end the linked reports held back while whose the turn was could not be told (`reportGate`) */
|
|
@@ -112760,14 +112910,19 @@ var RemoteAgents = class {
|
|
|
112760
112910
|
if (now.running) {
|
|
112761
112911
|
const g5 = await this.gate(rec, reader);
|
|
112762
112912
|
if (!g5.live) {
|
|
112763
|
-
return { ok: false, error: `"${rec.name}" is busy on ${rec.machine} with
|
|
112913
|
+
return { ok: false, error: `"${rec.name}" is busy on ${rec.machine} with ${this.otherTurn(rec, g5)}. A queue or a fork does not reach another machine: try again once it is at rest.` };
|
|
112764
112914
|
}
|
|
112765
112915
|
const refused3 = this.gateError(callerId);
|
|
112766
112916
|
if (refused3) return { ok: false, error: refused3 };
|
|
112767
112917
|
const framed3 = agentNoteFrame(label, text2);
|
|
112768
112918
|
this.sentNow(rec.sessionId, framed3);
|
|
112919
|
+
this.notesSent.set(rec.sessionId, [...this.notesSent.get(rec.sessionId) ?? [], framed3]);
|
|
112769
112920
|
const sent2 = await reader.send(rec.sessionId, framed3, label, { depth: depthOf(this.host, callerId) + 1 });
|
|
112770
|
-
if (!sent2.ok)
|
|
112921
|
+
if (!sent2.ok) {
|
|
112922
|
+
const at2 = (this.notesSent.get(rec.sessionId) ?? []).lastIndexOf(framed3);
|
|
112923
|
+
if (at2 >= 0) this.notesSent.set(rec.sessionId, (this.notesSent.get(rec.sessionId) ?? []).filter((_, i) => i !== at2));
|
|
112924
|
+
return { ok: false, error: `Could not send to "${rec.name}" on ${rec.machine}: ${bare(sent2.error)}.` };
|
|
112925
|
+
}
|
|
112771
112926
|
return this.gateNow(rec).live ? this.working(rec, this.reportArmed(rec) || this.waits.has(rec.sessionId)) : this.wentElsewhere(rec);
|
|
112772
112927
|
}
|
|
112773
112928
|
const refused2 = this.gateError(callerId);
|
|
@@ -112776,6 +112931,7 @@ var RemoteAgents = class {
|
|
|
112776
112931
|
if (full) return { ok: false, error: full };
|
|
112777
112932
|
const framed2 = agentNoteFrame(label, text2);
|
|
112778
112933
|
this.save(this.records().map((r) => r.sessionId === rec.sessionId ? { ...r, turn: randomUUID17(), sent: framed2 } : r));
|
|
112934
|
+
this.notesSent.delete(rec.sessionId);
|
|
112779
112935
|
this.whose.set(rec.sessionId, "unknown");
|
|
112780
112936
|
this.pending.add(rec.sessionId);
|
|
112781
112937
|
this.host.children.setLinkedReport(rec.sessionId, { orchestratorId: callerId, busy: false });
|
|
@@ -112891,7 +113047,9 @@ var RemoteAgents = class {
|
|
|
112891
113047
|
const whose = this.whoseOf(rec);
|
|
112892
113048
|
const owned = whose === "caller";
|
|
112893
113049
|
const report = this.host.children.linkedReports.get(rec.sessionId);
|
|
112894
|
-
const
|
|
113050
|
+
const mine = report?.orchestratorId === rec.callerId;
|
|
113051
|
+
const continuesPause = mine && !!report.paused && report.busy && report.turn?.by === "work";
|
|
113052
|
+
const running2 = this.waits.has(rec.sessionId) || mine && (!report.paused || continuesPause);
|
|
112895
113053
|
return { owned, live: owned && running2, whose };
|
|
112896
113054
|
}
|
|
112897
113055
|
/** Whose the child's turn is (`Whose`): as settled here, else as its record kept it; `caller` is re-read after a restart */
|
|
@@ -112905,6 +113063,7 @@ var RemoteAgents = class {
|
|
|
112905
113063
|
*/
|
|
112906
113064
|
setWhose(rec, whose, why) {
|
|
112907
113065
|
const was = this.whoseOf(rec);
|
|
113066
|
+
if (was !== "other" || whose !== "other") this.otherBy.delete(rec.sessionId);
|
|
112908
113067
|
this.whose.set(rec.sessionId, whose);
|
|
112909
113068
|
const kept = whose === "unknown" ? void 0 : whose;
|
|
112910
113069
|
const stored = this.of(rec.sessionId);
|
|
@@ -112956,12 +113115,26 @@ var RemoteAgents = class {
|
|
|
112956
113115
|
if (whose === "unknown") this.setWhose(rec, "other");
|
|
112957
113116
|
return this.wentElsewhere(rec);
|
|
112958
113117
|
}
|
|
113118
|
+
/**
|
|
113119
|
+
* What the running turn is, said for a caller it is not live for (#1576): named by what started it, so only a message
|
|
113120
|
+
* of a person's on that machine is "the person may be using it". A turn the hub has no sight of the start of (a swap,
|
|
113121
|
+
* a machine that lists no background work) is said to be the child's own, not a person's
|
|
113122
|
+
*/
|
|
113123
|
+
otherTurn(rec, g5) {
|
|
113124
|
+
if (g5.whose === "unknown") return "a turn the hub cannot tell the owner of (the link dropped, or Centralu restarted, and the machine cannot be read now)";
|
|
113125
|
+
if (g5.whose === "other") {
|
|
113126
|
+
return this.otherBy.get(rec.sessionId) === "session" ? "a turn another session asked of it (it is not the one you gave it)" : "a turn you did not give it (the person may be using it there)";
|
|
113127
|
+
}
|
|
113128
|
+
const report = this.host.children.linkedReports.get(rec.sessionId);
|
|
113129
|
+
if (report && report.orchestratorId !== rec.callerId) return "a turn another session is owed the end of (it is not the one you gave it)";
|
|
113130
|
+
return "a turn it started by itself that this hub cannot tie to the work you gave it";
|
|
113131
|
+
}
|
|
112959
113132
|
/**
|
|
112960
113133
|
* What the caller gets of a child whose turn the gate does not let through: the fact and the state word. Nothing of
|
|
112961
113134
|
* the turn: no card, no words, and nothing owed or armed for it
|
|
112962
113135
|
*/
|
|
112963
113136
|
notYours(rec, g5, running2, state) {
|
|
112964
|
-
const why = g5.whose === "unknown" ? `The hub could not tell whose its last turn on ${rec.machine} was (the link dropped, or Centralu restarted, and the machine cannot be read now)` : running2 ? `It is in
|
|
113137
|
+
const why = g5.whose === "unknown" ? `The hub could not tell whose its last turn on ${rec.machine} was (the link dropped, or Centralu restarted, and the machine cannot be read now)` : running2 ? `It is in ${this.otherTurn(rec, g5)} on ${rec.machine}` : `Its last turn on ${rec.machine} was not one you gave it (the person's)`;
|
|
112965
113138
|
const notice = `${why}: nothing of that turn is yours to read, and none of it is reported to you.${running2 ? " Send to it once it is at rest." : ""}`;
|
|
112966
113139
|
if (running2) return { ...this.working(rec, false), notice };
|
|
112967
113140
|
return { ok: true, state: "at_rest", sessionId: rec.sessionId, sessionName: rec.name, tool: rec.tool, answer: "(not yours to read: a turn you did not give it)", sessionState: state, notice };
|
|
@@ -112981,13 +113154,23 @@ var RemoteAgents = class {
|
|
|
112981
113154
|
if (e.seq <= (this.lastSeq.get(sessionId) ?? -1)) return;
|
|
112982
113155
|
this.lastSeq.set(sessionId, e.seq);
|
|
112983
113156
|
}
|
|
113157
|
+
const own = this.notesSent.get(sessionId) ?? [];
|
|
113158
|
+
const earlier = typeof e.text === "string" && e.text !== rec.sent ? own.indexOf(e.text) : -1;
|
|
113159
|
+
if (earlier >= 0 && this.fromHubHere(rec, e.from)) {
|
|
113160
|
+
this.notesSent.set(sessionId, own.filter((_, i) => i !== earlier));
|
|
113161
|
+
if (this.whoseOf(rec) === "caller" && this.gateNow(rec).live) return;
|
|
113162
|
+
}
|
|
112984
113163
|
if (this.fromHubHere(rec, e.from) && e.text === rec.sent) {
|
|
113164
|
+
const at2 = own.indexOf(rec.sent ?? "");
|
|
113165
|
+
if (at2 >= 0) this.notesSent.set(sessionId, own.filter((_, i) => i !== at2));
|
|
112985
113166
|
if (this.whoseOf(rec) === "caller" && this.gateNow(rec).live) return;
|
|
112986
113167
|
if (this.busy.has(sessionId)) return this.setWhose(rec, "other", "took what you sent as a message into a turn already running there that you did not give it (the person's, most likely)");
|
|
112987
113168
|
this.tookInstruction(rec);
|
|
112988
113169
|
return;
|
|
112989
113170
|
}
|
|
112990
113171
|
this.setWhose(rec, "other", "took a message there that you did not send (the person on that machine, most likely)");
|
|
113172
|
+
if (this.fromHubHere(rec, e.from)) this.otherBy.set(sessionId, "session");
|
|
113173
|
+
else this.otherBy.delete(sessionId);
|
|
112991
113174
|
}
|
|
112992
113175
|
/**
|
|
112993
113176
|
* The machine recorded the instruction this hub last sent the child, with no other turn running there: the turn that
|
|
@@ -113278,6 +113461,8 @@ var RemoteAgents = class {
|
|
|
113278
113461
|
if (this.records().some((r) => r.sessionId === sessionId)) this.save(this.records().filter((r) => r.sessionId !== sessionId));
|
|
113279
113462
|
this.toldDown.delete(sessionId);
|
|
113280
113463
|
this.whose.delete(sessionId);
|
|
113464
|
+
this.otherBy.delete(sessionId);
|
|
113465
|
+
this.notesSent.delete(sessionId);
|
|
113281
113466
|
this.pending.delete(sessionId);
|
|
113282
113467
|
this.deferred.delete(sessionId);
|
|
113283
113468
|
this.busy.delete(sessionId);
|
|
@@ -114137,6 +114322,20 @@ var LaunchRecords = class {
|
|
|
114137
114322
|
rememberBackgroundTasks(m) {
|
|
114138
114323
|
this.host.store.settings.setAppSetting(this.backgroundTasksKey(m.id), JSON.stringify(m.backgroundTasks.filter((t) => t.status === "running")));
|
|
114139
114324
|
}
|
|
114325
|
+
/**
|
|
114326
|
+
* The running cost total of the conversation a session's tool reports (`CreateSessionOpts.runningCost`), as its last process
|
|
114327
|
+
* read it: kept so the next process, after a restart, a wake or a host swap, takes a turn's cost as what it added. Read only
|
|
114328
|
+
* for a resumed conversation; anything but a number at or above 0 is none.
|
|
114329
|
+
*/
|
|
114330
|
+
runningCostFor(sessionId, resumed) {
|
|
114331
|
+
const key = `running_cost:${sessionId}`;
|
|
114332
|
+
const raw = resumed ? this.host.store.settings.appSetting(key) : null;
|
|
114333
|
+
const last = raw === null ? null : Number(raw);
|
|
114334
|
+
return {
|
|
114335
|
+
last: last !== null && Number.isFinite(last) && last >= 0 ? last : null,
|
|
114336
|
+
keep: (total) => this.host.store.settings.setAppSetting(key, String(total))
|
|
114337
|
+
};
|
|
114338
|
+
}
|
|
114140
114339
|
/** The persisted prefix a process was started with. It is separate from SessionInfo: a screen never needs it. */
|
|
114141
114340
|
toolPrefixKey(sessionId) {
|
|
114142
114341
|
return `tool_prefix:${sessionId}`;
|
|
@@ -118056,30 +118255,6 @@ ${note}` : note;
|
|
|
118056
118255
|
}
|
|
118057
118256
|
};
|
|
118058
118257
|
|
|
118059
|
-
// packages/agent-host/src/mail/address.ts
|
|
118060
|
-
var SEGMENT = "[A-Za-z0-9][A-Za-z0-9._~-]{0,63}";
|
|
118061
|
-
var SEGMENT_RE = new RegExp(`^${SEGMENT}$`);
|
|
118062
|
-
var SESSION_RE = new RegExp(`^session:(${SEGMENT})/(${SEGMENT})$`);
|
|
118063
|
-
var APP_RE = new RegExp(`^app:(${SEGMENT})/(${SEGMENT})/(${SEGMENT})$`);
|
|
118064
|
-
var PERSON = "person";
|
|
118065
|
-
var CENTRALU = "centralu";
|
|
118066
|
-
function sessionAddress(host, sessionId) {
|
|
118067
|
-
return checked(`session:${host}/${sessionId}`);
|
|
118068
|
-
}
|
|
118069
|
-
function parseAddress(address) {
|
|
118070
|
-
if (address === PERSON) return { kind: "person" };
|
|
118071
|
-
if (address === CENTRALU) return { kind: "centralu" };
|
|
118072
|
-
const [, host, sessionId] = SESSION_RE.exec(address) ?? [];
|
|
118073
|
-
if (host !== void 0 && sessionId !== void 0) return { kind: "session", host, sessionId };
|
|
118074
|
-
const [, appHost, projectId, appId] = APP_RE.exec(address) ?? [];
|
|
118075
|
-
if (appHost !== void 0 && projectId !== void 0 && appId !== void 0) return { kind: "app", host: appHost, projectId, appId };
|
|
118076
|
-
return null;
|
|
118077
|
-
}
|
|
118078
|
-
function checked(address) {
|
|
118079
|
-
if (!parseAddress(address)) throw new Error(`Not a mail address: ${JSON.stringify(address)}`);
|
|
118080
|
-
return address;
|
|
118081
|
-
}
|
|
118082
|
-
|
|
118083
118258
|
// packages/agent-host/src/mail/ids.ts
|
|
118084
118259
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
118085
118260
|
var CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
@@ -118169,10 +118344,10 @@ function sweepMail(db, now, limits = {}) {
|
|
|
118169
118344
|
result.messages += db.prepare(`DELETE FROM mail_messages WHERE id IN (SELECT value FROM json_each(?))`).run(json2).changes;
|
|
118170
118345
|
};
|
|
118171
118346
|
const dropThreads = (where2, cut2) => {
|
|
118172
|
-
const
|
|
118347
|
+
const pick4 = db.prepare(`SELECT t.id FROM mail_threads t WHERE ${where2} ORDER BY t.created_at LIMIT @batch`);
|
|
118173
118348
|
for (; ; ) {
|
|
118174
118349
|
const done2 = db.transaction(() => {
|
|
118175
|
-
const ids =
|
|
118350
|
+
const ids = pick4.all({ cut: cut2, batch: l.batch }).map((r) => r.id);
|
|
118176
118351
|
if (ids.length === 0) return true;
|
|
118177
118352
|
const json2 = JSON.stringify(ids);
|
|
118178
118353
|
db.prepare(`DELETE FROM mail_receipts WHERE message_id IN (SELECT id FROM mail_messages WHERE thread_id IN (SELECT value FROM json_each(?)))`).run(json2);
|
|
@@ -118415,6 +118590,64 @@ var MailRecords = class {
|
|
|
118415
118590
|
const rows3 = this.db.prepare(`SELECT * FROM mail_threads WHERE ${column} = ? ORDER BY created_at, id`).all(address);
|
|
118416
118591
|
return rows3.map(toThread).filter((t) => !states || states.includes(t.state));
|
|
118417
118592
|
}
|
|
118593
|
+
/** The threads that are not done in which a party is still a participant, whatever its role: what a handoff moves of it (§4.1) */
|
|
118594
|
+
openThreadsWith(address) {
|
|
118595
|
+
const rows3 = this.db.prepare(
|
|
118596
|
+
`SELECT t.* FROM mail_threads t JOIN mail_participants p ON p.thread_id = t.id AND p.address = ? AND p.left_at IS NULL
|
|
118597
|
+
WHERE t.state <> 'done' ORDER BY t.created_at, t.id`
|
|
118598
|
+
).all(address);
|
|
118599
|
+
return rows3.map(toThread);
|
|
118600
|
+
}
|
|
118601
|
+
/** The done threads in which `address` has mail it has neither read nor handled: what a handoff's move of its receipts looks at too (§4.1) */
|
|
118602
|
+
doneThreadsWithUnread(address) {
|
|
118603
|
+
const rows3 = this.db.prepare(
|
|
118604
|
+
`SELECT t.* FROM mail_threads t WHERE t.state = 'done' AND EXISTS (
|
|
118605
|
+
SELECT 1 FROM mail_receipts r JOIN mail_messages m ON m.id = r.message_id
|
|
118606
|
+
WHERE m.thread_id = t.id AND r.recipient = ? AND r.read_at IS NULL AND r.handled_at IS NULL)
|
|
118607
|
+
ORDER BY t.created_at, t.id`
|
|
118608
|
+
).all(address);
|
|
118609
|
+
return rows3.map(toThread);
|
|
118610
|
+
}
|
|
118611
|
+
/**
|
|
118612
|
+
* A handoff's move of what `from` has not read in a thread to `to` (§4.1): each receipt neither read nor handled is `to`'s, and
|
|
118613
|
+
* waits to be delivered to it, delivered to `from` or not (what went into the original's conversation never reached its
|
|
118614
|
+
* successor). A hold about the original itself (its delivery `uncertain` or `failed`, its `loop`, `budget` or `policy`, each
|
|
118615
|
+
* judged for the original's wakes) is let go, for the delivery to judge again for the successor; one the person settled
|
|
118616
|
+
* (`released`, `dismissed`) stays. Not moved, and handled for `from` instead: a receipt for a message `to` sent itself (nobody
|
|
118617
|
+
* is sent their own words), and one for a message `to` already has a receipt for (it keeps its own). So nothing of the
|
|
118618
|
+
* original's stays waiting (`handleUnread`), and a restored original is not woken for mail of a thread it has left. Returns how many moved
|
|
118619
|
+
*/
|
|
118620
|
+
moveUnreadReceipts(threadId, from, to, now) {
|
|
118621
|
+
const moved = this.db.prepare(
|
|
118622
|
+
`UPDATE OR IGNORE mail_receipts
|
|
118623
|
+
SET recipient = @to, delivered_at = NULL, attempt_at = NULL, attempts = 0, last_error = NULL,
|
|
118624
|
+
held = CASE WHEN held IN ('uncertain', 'failed', 'loop', 'budget', 'policy') THEN NULL ELSE held END
|
|
118625
|
+
WHERE recipient = @from AND read_at IS NULL AND handled_at IS NULL
|
|
118626
|
+
AND message_id IN (SELECT id FROM mail_messages WHERE thread_id = @threadId AND sender <> @to)`
|
|
118627
|
+
).run({ threadId, from, to }).changes;
|
|
118628
|
+
this.handleUnread(threadId, from, now);
|
|
118629
|
+
return moved;
|
|
118630
|
+
}
|
|
118631
|
+
/** How many of `from`'s unread receipts in the thread `moveUnreadReceipts` would give `to`: not those of `to`'s own messages, nor those it has already */
|
|
118632
|
+
movableUnread(threadId, from, to) {
|
|
118633
|
+
const row = this.db.prepare(
|
|
118634
|
+
`SELECT COUNT(*) AS n FROM mail_receipts r JOIN mail_messages m ON m.id = r.message_id
|
|
118635
|
+
WHERE r.recipient = @from AND r.read_at IS NULL AND r.handled_at IS NULL AND m.thread_id = @threadId AND m.sender <> @to
|
|
118636
|
+
AND NOT EXISTS (SELECT 1 FROM mail_receipts o WHERE o.message_id = r.message_id AND o.recipient = @to)`
|
|
118637
|
+
).get({ threadId, from, to });
|
|
118638
|
+
return row?.n ?? 0;
|
|
118639
|
+
}
|
|
118640
|
+
/**
|
|
118641
|
+
* `from`'s receipts in a thread that are neither read nor handled are handled: a handoff's original left the thread, or is about to
|
|
118642
|
+
* when it goes (§4.1), and nothing of it waits there, so a restored original is not woken for a thread it no longer holds
|
|
118643
|
+
*/
|
|
118644
|
+
handleUnread(threadId, from, now) {
|
|
118645
|
+
this.db.prepare(
|
|
118646
|
+
`UPDATE mail_receipts SET handled_at = @now
|
|
118647
|
+
WHERE recipient = @from AND read_at IS NULL AND handled_at IS NULL
|
|
118648
|
+
AND message_id IN (SELECT id FROM mail_messages WHERE thread_id = @threadId)`
|
|
118649
|
+
).run({ threadId, from, now });
|
|
118650
|
+
}
|
|
118418
118651
|
/**
|
|
118419
118652
|
* What a party's mailbox holds (§9.3, §10.1): the threads it is in that are not done, and the done ones it has mail unread
|
|
118420
118653
|
* in, with how much it has not read. Unread first, then the latest change. A mail held back for the person (a loop, a spent
|
|
@@ -118945,6 +119178,19 @@ function scriptMessageFrame(credential, text2) {
|
|
|
118945
119178
|
` + quoted2(text2);
|
|
118946
119179
|
}
|
|
118947
119180
|
|
|
119181
|
+
// packages/agent-host/src/sessions/tool-fit.ts
|
|
119182
|
+
function sessionDemands(m, files, preset = m.permissionPreset) {
|
|
119183
|
+
return {
|
|
119184
|
+
permissionPreset: preset,
|
|
119185
|
+
...files.projectTrusted === void 0 ? {} : { projectTrusted: files.projectTrusted },
|
|
119186
|
+
...files.noSettingFiles ? { noSettingFiles: true } : {},
|
|
119187
|
+
roleText: m.kind === "orchestrator" || !!m.roleAppend
|
|
119188
|
+
};
|
|
119189
|
+
}
|
|
119190
|
+
function unfitError(reason) {
|
|
119191
|
+
return rpcError(reason, "refused", { retryable: false, data: { reason: "tool_unfit" } });
|
|
119192
|
+
}
|
|
119193
|
+
|
|
118948
119194
|
// packages/agent-host/src/sessions/session-start.ts
|
|
118949
119195
|
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
118950
119196
|
|
|
@@ -119367,7 +119613,8 @@ var SessionStarts = class {
|
|
|
119367
119613
|
...params.lsp !== void 0 ? { lsp: params.lsp } : {}
|
|
119368
119614
|
});
|
|
119369
119615
|
const from = this.host.handleSink();
|
|
119370
|
-
handle = await
|
|
119616
|
+
handle = await startFit(
|
|
119617
|
+
adapter,
|
|
119371
119618
|
this.launchOptions(info, {
|
|
119372
119619
|
tool: params.tool,
|
|
119373
119620
|
cwd,
|
|
@@ -119535,6 +119782,7 @@ var SessionStarts = class {
|
|
|
119535
119782
|
...this.autoCompactFor(m, !!o.adopt),
|
|
119536
119783
|
permissionPreset: o.permissionPreset,
|
|
119537
119784
|
resumeExternalId: o.resumeExternalId,
|
|
119785
|
+
runningCost: this.host.launches.runningCostFor(m.id, !!o.resumeExternalId),
|
|
119538
119786
|
...o.knownGoal ? { knownGoal: o.knownGoal } : {},
|
|
119539
119787
|
// Which settings files reach this session (decision 3, #92, #152) — what the session is,
|
|
119540
119788
|
// and the trust at this exact moment of launch. If this is a builder session, the
|
|
@@ -119678,7 +119926,8 @@ var SessionStarts = class {
|
|
|
119678
119926
|
if (!adopt) m.agentVersion = this.host.versionHint?.(tool) ?? null;
|
|
119679
119927
|
const kept = adopt ? this.host.launches.keptBackgroundTasks(sessionId) : null;
|
|
119680
119928
|
try {
|
|
119681
|
-
const creating =
|
|
119929
|
+
const creating = startFit(
|
|
119930
|
+
adapter,
|
|
119682
119931
|
this.launchOptions(m, {
|
|
119683
119932
|
tool,
|
|
119684
119933
|
cwd,
|
|
@@ -119776,6 +120025,15 @@ var SessionStarts = class {
|
|
|
119776
120025
|
}
|
|
119777
120026
|
}
|
|
119778
120027
|
};
|
|
120028
|
+
function startFit(adapter, opts, sink) {
|
|
120029
|
+
const reason = launchRefusal(adapter.capabilities, adapter.descriptor.label, {
|
|
120030
|
+
permissionPreset: opts.permissionPreset,
|
|
120031
|
+
projectTrusted: opts.projectTrusted,
|
|
120032
|
+
noSettingFiles: opts.noSettingFiles,
|
|
120033
|
+
roleText: !!opts.systemPromptAppend
|
|
120034
|
+
});
|
|
120035
|
+
return reason ? Promise.reject(unfitError(reason)) : adapter.createSession(opts, sink);
|
|
120036
|
+
}
|
|
119779
120037
|
|
|
119780
120038
|
// packages/agent-host/src/sessions/session-events.ts
|
|
119781
120039
|
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
@@ -120834,7 +121092,7 @@ ${adapterText}` : adapterText;
|
|
|
120834
121092
|
if (!handle) throw rpcError("The session is not running");
|
|
120835
121093
|
this.host.record.closeStream(sessionId);
|
|
120836
121094
|
this.host.cardRests.turnAsked(sessionId);
|
|
120837
|
-
handle.send(text2);
|
|
121095
|
+
handle.send(text2, { compact: true });
|
|
120838
121096
|
}
|
|
120839
121097
|
/**
|
|
120840
121098
|
* Turns a touched file into a path relative to the project (#185).
|
|
@@ -122628,6 +122886,11 @@ var InputReceipts = class {
|
|
|
122628
122886
|
// packages/agent-host/src/adapters/local-process.ts
|
|
122629
122887
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
122630
122888
|
import { PassThrough as PassThrough2 } from "node:stream";
|
|
122889
|
+
var HOST_WATCH = '{ g=; while read -r l; do case $l in +*) g="$g ${l#+}";; -*) n=; for p in $g; do [ "$p" = "${l#-}" ] || n="$n $p"; done; g=$n;; esac; done; for p in $g $(' + watchTreeGroups() + '); do kill -9 -"$p" 2>/dev/null; done; kill -9 0 2>/dev/null; } <&3 >/dev/null 2>&1 & exec "$@" 3<&-';
|
|
122890
|
+
function watchTreeGroups() {
|
|
122891
|
+
return `ps -A -o pid= -o ppid= -o pgid= 2>/dev/null | awk -v r="$$" '{ p[$1] = $2; g[$1] = $3 } END { d[r] = 1; do { c = 0; for (k in p) if (!(k in d) && (p[k] in d)) { d[k] = 1; c = 1 } } while (c); for (k in d) if (g[k] != g[r] && g[k] > 1) print g[k] }'`;
|
|
122892
|
+
}
|
|
122893
|
+
var GROUP_CHECK_MS = 2e3;
|
|
122631
122894
|
var STDERR_TAIL2 = 8 * 1024;
|
|
122632
122895
|
var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
|
|
122633
122896
|
constructor(child, spawnedAt, os2) {
|
|
@@ -122641,6 +122904,8 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
|
|
|
122641
122904
|
child.stderr?.on("data", (d2) => this.tail.push(d2));
|
|
122642
122905
|
child.stdin?.on("error", () => {
|
|
122643
122906
|
});
|
|
122907
|
+
child.stdio[3]?.on("error", () => {
|
|
122908
|
+
});
|
|
122644
122909
|
child.on("error", (e) => {
|
|
122645
122910
|
this.emit("error", e);
|
|
122646
122911
|
if (child.pid === void 0) this.onExit();
|
|
@@ -122655,12 +122920,18 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
|
|
|
122655
122920
|
stderr;
|
|
122656
122921
|
killed = false;
|
|
122657
122922
|
tail = new Tail(STDERR_TAIL2);
|
|
122923
|
+
/** The helper groups its tree had when last asked (`noteTree`) and that still have a member, swept once it has gone */
|
|
122924
|
+
groups = /* @__PURE__ */ new Set();
|
|
122925
|
+
/** While groups are held: they are read again, so one that has emptied is dropped before its number is reused */
|
|
122926
|
+
groupClock = null;
|
|
122927
|
+
noting = null;
|
|
122658
122928
|
/** Reported once the output has all arrived, so a reader never sees the exit before the last line */
|
|
122659
122929
|
exitReport = new ExitAfterDrain(() => this.emit("exit", this.child.exitCode, this.child.signalCode));
|
|
122660
122930
|
static spawn(spec, os2) {
|
|
122661
122931
|
let started;
|
|
122662
122932
|
try {
|
|
122663
|
-
|
|
122933
|
+
const watched = !!spec.endsWithHost && hasProcessGroups(os2?.platform);
|
|
122934
|
+
started = watched ? spawnGrouped("/bin/sh", ["-c", HOST_WATCH, "centralu-agent", spec.command, ...spec.args], { cwd: spec.cwd, env: spec.env, stdio: ["pipe", "pipe", "pipe", "pipe"] }, os2?.platform) : spawnGrouped(spec.command, spec.args, { cwd: spec.cwd, env: spec.env, stdio: ["pipe", "pipe", "pipe"] }, os2?.platform);
|
|
122664
122935
|
} catch (err) {
|
|
122665
122936
|
started = { child: notStarted(err), spawnedAt: Date.now() };
|
|
122666
122937
|
}
|
|
@@ -122680,6 +122951,38 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
|
|
|
122680
122951
|
get signalCode() {
|
|
122681
122952
|
return this.child.signalCode;
|
|
122682
122953
|
}
|
|
122954
|
+
noteTree() {
|
|
122955
|
+
const pid = this.child.pid;
|
|
122956
|
+
if (pid === void 0 || this.ended || !this.grouped) return Promise.resolve();
|
|
122957
|
+
this.noting ??= noteGroups(pid, this.groups, this.os?.platform).then((groups) => {
|
|
122958
|
+
if (this.ended) return;
|
|
122959
|
+
this.tellWatch(this.groups, groups);
|
|
122960
|
+
this.groups = groups;
|
|
122961
|
+
if (groups.size === 0) this.stopGroupClock();
|
|
122962
|
+
else if (!this.groupClock) {
|
|
122963
|
+
this.groupClock = setInterval(() => void this.noteTree(), GROUP_CHECK_MS);
|
|
122964
|
+
this.groupClock.unref?.();
|
|
122965
|
+
}
|
|
122966
|
+
}).finally(() => {
|
|
122967
|
+
this.noting = null;
|
|
122968
|
+
});
|
|
122969
|
+
return this.noting;
|
|
122970
|
+
}
|
|
122971
|
+
/**
|
|
122972
|
+
* The host watch (`HOST_WATCH`) is told each helper group as it is noted and as it is dropped, so a host killed outright
|
|
122973
|
+
* still ends them: they are in groups of their own, which the watch's own `kill -9 0` does not reach (#1562 Mac re-check:
|
|
122974
|
+
* Cursor's `zsh -c` and its `sleep 300` outlived a SIGKILLed host)
|
|
122975
|
+
*/
|
|
122976
|
+
tellWatch(before, after) {
|
|
122977
|
+
const watch3 = this.child.stdio[3];
|
|
122978
|
+
if (!watch3 || !("write" in watch3) || watch3.destroyed) return;
|
|
122979
|
+
const lines = [...[...after].filter((g5) => !before.has(g5)).map((g5) => `+${g5}`), ...[...before].filter((g5) => !after.has(g5)).map((g5) => `-${g5}`)];
|
|
122980
|
+
if (lines.length) watch3.write(lines.join("\n") + "\n");
|
|
122981
|
+
}
|
|
122982
|
+
stopGroupClock() {
|
|
122983
|
+
if (this.groupClock) clearInterval(this.groupClock);
|
|
122984
|
+
this.groupClock = null;
|
|
122985
|
+
}
|
|
122683
122986
|
/** The end of what the CLI wrote to stderr */
|
|
122684
122987
|
stderrTail() {
|
|
122685
122988
|
return this.tail.text;
|
|
@@ -122710,6 +123013,8 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
|
|
|
122710
123013
|
return true;
|
|
122711
123014
|
}
|
|
122712
123015
|
onExit() {
|
|
123016
|
+
const watch3 = this.child.stdio[3];
|
|
123017
|
+
if (watch3 && "destroy" in watch3) watch3.destroy();
|
|
122713
123018
|
this.sweep();
|
|
122714
123019
|
if (!this.stdout.readableEnded) this.stdout.once("end", () => this.exitReport.drained());
|
|
122715
123020
|
this.exitReport.exited(this.stdout.readableEnded);
|
|
@@ -122718,6 +123023,8 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
|
|
|
122718
123023
|
sweep() {
|
|
122719
123024
|
const pid = this.child.pid;
|
|
122720
123025
|
if (pid === void 0) return;
|
|
123026
|
+
this.stopGroupClock();
|
|
123027
|
+
for (const g5 of this.groups) stopGroup(g5, KILL_GRACE_MS);
|
|
122721
123028
|
const root3 = { pid, spawnedAt: this.spawnedAt, exitedAt: Date.now() };
|
|
122722
123029
|
void sweepExited(root3, this.os?.platform, this.os?.listProcesses ? { listProcesses: this.os.listProcesses, taskkill: this.os.taskkill } : void 0).then((ended) => {
|
|
122723
123030
|
if (ended.length > 0) console.error(`[agent] ended ${ended.length} process(es) pid ${pid} left running: ${ended.join(", ")}`);
|
|
@@ -122728,7 +123035,8 @@ function notStarted(err) {
|
|
|
122728
123035
|
const stand = new EventEmitter3();
|
|
122729
123036
|
const stdout = new PassThrough2();
|
|
122730
123037
|
stdout.end();
|
|
122731
|
-
|
|
123038
|
+
const stdin = new PassThrough2();
|
|
123039
|
+
Object.assign(stand, { stdin, stdout, stderr: null, stdio: [stdin, stdout, null], pid: void 0, exitCode: null, signalCode: null, kill: () => false });
|
|
122732
123040
|
process.nextTick(() => stand.emit("error", err));
|
|
122733
123041
|
return stand;
|
|
122734
123042
|
}
|
|
@@ -123167,13 +123475,13 @@ function systemEvents(m, sessionId) {
|
|
|
123167
123475
|
function informationalEvents(m, sessionId) {
|
|
123168
123476
|
const out = [];
|
|
123169
123477
|
const text2 = str(m.content).trim();
|
|
123170
|
-
const
|
|
123171
|
-
if (text2 &&
|
|
123478
|
+
const level2 = str(m.level);
|
|
123479
|
+
if (text2 && level2 !== "info") {
|
|
123172
123480
|
const hook = /\bblocked by hook\b/.test(text2);
|
|
123173
123481
|
out.push({
|
|
123174
123482
|
type: "notice",
|
|
123175
123483
|
sessionId,
|
|
123176
|
-
level:
|
|
123484
|
+
level: level2 === "warning" ? "warning" : "info",
|
|
123177
123485
|
text: text2,
|
|
123178
123486
|
from: "Claude Code",
|
|
123179
123487
|
label: hook ? "hook" : "notice",
|
|
@@ -123188,8 +123496,8 @@ function notificationEvents(m, sessionId) {
|
|
|
123188
123496
|
if (!text2 || str(m.key) === "error-compacting-conversation") return out;
|
|
123189
123497
|
const color = str(m.color);
|
|
123190
123498
|
const priority = str(m.priority);
|
|
123191
|
-
const
|
|
123192
|
-
out.push({ type: "notice", sessionId, level, text: text2, from: "Claude Code", label: "notice" });
|
|
123499
|
+
const level2 = color === "error" ? "error" : color === "warning" || priority === "high" || priority === "immediate" ? "warning" : "info";
|
|
123500
|
+
out.push({ type: "notice", sessionId, level: level2, text: text2, from: "Claude Code", label: "notice" });
|
|
123193
123501
|
return out;
|
|
123194
123502
|
}
|
|
123195
123503
|
function apiRetryEvents(m, sessionId) {
|
|
@@ -123898,8 +124206,8 @@ import { resolve as resolve18 } from "node:path";
|
|
|
123898
124206
|
// packages/agent-host/src/adapters/always-allow.ts
|
|
123899
124207
|
var AlwaysAllowRules = class {
|
|
123900
124208
|
matchers = /* @__PURE__ */ new Set();
|
|
123901
|
-
add(
|
|
123902
|
-
this.matchers.add(
|
|
124209
|
+
add(matcher2) {
|
|
124210
|
+
this.matchers.add(matcher2);
|
|
123903
124211
|
}
|
|
123904
124212
|
addAll(matchers) {
|
|
123905
124213
|
for (const m of matchers) this.matchers.add(m);
|
|
@@ -124002,13 +124310,13 @@ var ClaudeCards = class {
|
|
|
124002
124310
|
this.releaseOnCancel(requestId, options?.signal);
|
|
124003
124311
|
});
|
|
124004
124312
|
}
|
|
124005
|
-
respondApproval(requestId, decision, scope,
|
|
124313
|
+
respondApproval(requestId, decision, scope, matcher2) {
|
|
124006
124314
|
const p = this.pending.take(requestId);
|
|
124007
124315
|
if (!p) return false;
|
|
124008
124316
|
if (decision === "deny") {
|
|
124009
124317
|
p.resolve({ behavior: "deny", message: "Denied by user" });
|
|
124010
124318
|
} else {
|
|
124011
|
-
if (decision === "always" &&
|
|
124319
|
+
if (decision === "always" && matcher2) this.alwaysAllow.add(matcher2);
|
|
124012
124320
|
p.resolve({ behavior: "allow", updatedInput: p.input });
|
|
124013
124321
|
}
|
|
124014
124322
|
this.host.emit({ type: "approval_resolved", sessionId: this.host.sessionId, requestId, decision });
|
|
@@ -125315,8 +125623,8 @@ var ClaudeSession = class {
|
|
|
125315
125623
|
this.notify = null;
|
|
125316
125624
|
if (starting) this.emit({ type: "state_change", sessionId: this.sessionId, state: "working" });
|
|
125317
125625
|
}
|
|
125318
|
-
respondApproval(requestId, decision, scope,
|
|
125319
|
-
return this.cards.respondApproval(requestId, decision, scope,
|
|
125626
|
+
respondApproval(requestId, decision, scope, matcher2) {
|
|
125627
|
+
return this.cards.respondApproval(requestId, decision, scope, matcher2);
|
|
125320
125628
|
}
|
|
125321
125629
|
answerQuestion(requestId, answers) {
|
|
125322
125630
|
return this.cards.answerQuestion(requestId, answers);
|
|
@@ -127283,7 +127591,7 @@ function reconcileOpenedThread(sessionId, answered, recorded) {
|
|
|
127283
127591
|
// packages/agent-host/src/adapters/codex/sandbox.ts
|
|
127284
127592
|
import { realpath as realpath8 } from "node:fs/promises";
|
|
127285
127593
|
import { resolve as resolve19 } from "node:path";
|
|
127286
|
-
async function sandboxConfig(cwd, preset,
|
|
127594
|
+
async function sandboxConfig(cwd, preset, readConfig2) {
|
|
127287
127595
|
const roots = [];
|
|
127288
127596
|
try {
|
|
127289
127597
|
const stdout = await runGit(cwd, ["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"], { timeout: 5e3 });
|
|
@@ -127296,7 +127604,7 @@ async function sandboxConfig(cwd, preset, readConfig) {
|
|
|
127296
127604
|
} catch {
|
|
127297
127605
|
}
|
|
127298
127606
|
if (roots.length) {
|
|
127299
|
-
const config2 = await
|
|
127607
|
+
const config2 = await readConfig2();
|
|
127300
127608
|
const workspace = config2.sandbox_workspace_write;
|
|
127301
127609
|
if (Array.isArray(workspace?.writable_roots)) {
|
|
127302
127610
|
roots.push(...workspace.writable_roots.filter((p) => typeof p === "string"));
|
|
@@ -127476,7 +127784,7 @@ var CodexRequests = class {
|
|
|
127476
127784
|
applyRules(matchers) {
|
|
127477
127785
|
this.alwaysAllow.addAll(matchers);
|
|
127478
127786
|
}
|
|
127479
|
-
respondApproval(requestId, decision, _scope,
|
|
127787
|
+
respondApproval(requestId, decision, _scope, matcher2) {
|
|
127480
127788
|
const open6 = this.approvals.take(requestId);
|
|
127481
127789
|
if (open6 === void 0) return false;
|
|
127482
127790
|
const serverId = open6.serverId;
|
|
@@ -127489,7 +127797,7 @@ var CodexRequests = class {
|
|
|
127489
127797
|
this.host.emit({ type: "approval_resolved", sessionId: this.host.sessionId, requestId, decision });
|
|
127490
127798
|
return true;
|
|
127491
127799
|
}
|
|
127492
|
-
if (decision === "always" &&
|
|
127800
|
+
if (decision === "always" && matcher2) this.alwaysAllow.add(matcher2);
|
|
127493
127801
|
this.host.respond(serverId, { decision: toCodexDecision(decision) });
|
|
127494
127802
|
this.host.emit({ type: "approval_resolved", sessionId: this.host.sessionId, requestId, decision });
|
|
127495
127803
|
return true;
|
|
@@ -127519,12 +127827,12 @@ function requestOrigin(r) {
|
|
|
127519
127827
|
};
|
|
127520
127828
|
}
|
|
127521
127829
|
|
|
127522
|
-
// packages/agent-host/src/adapters/
|
|
127830
|
+
// packages/agent-host/src/adapters/bridge-path.ts
|
|
127523
127831
|
import { existsSync as existsSync16 } from "node:fs";
|
|
127524
127832
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
127525
127833
|
function bridgePath() {
|
|
127526
127834
|
const candidates = [
|
|
127527
|
-
new URL("./orchestrator-bridge.mjs", import.meta.url),
|
|
127835
|
+
new URL("./codex/orchestrator-bridge.mjs", import.meta.url),
|
|
127528
127836
|
// the source tree
|
|
127529
127837
|
new URL("./codex-orchestrator-bridge.mjs", import.meta.url)
|
|
127530
127838
|
// the bundled build-output layout
|
|
@@ -128290,7 +128598,7 @@ var CodexSession = class {
|
|
|
128290
128598
|
turns = new CodexTurns();
|
|
128291
128599
|
/**
|
|
128292
128600
|
* Messages that arrived while a compact/review turn was running (measured while dogfooding,
|
|
128293
|
-
* 2026-09-02,
|
|
128601
|
+
* 2026-09-02, a dogfooding session).
|
|
128294
128602
|
*
|
|
128295
128603
|
* In codex 0.147.0, turn/start **answers success while dropping the input** while a compact
|
|
128296
128604
|
* turn is running — the rollout kept only the settings application (thread_settings_applied)
|
|
@@ -128658,8 +128966,8 @@ var CodexSession = class {
|
|
|
128658
128966
|
outputSchemaParam() {
|
|
128659
128967
|
return this.opts.outputSchema ? { outputSchema: this.opts.outputSchema } : {};
|
|
128660
128968
|
}
|
|
128661
|
-
respondApproval(requestId, decision, _scope,
|
|
128662
|
-
return this.serverRequests.respondApproval(requestId, decision, _scope,
|
|
128969
|
+
respondApproval(requestId, decision, _scope, matcher2) {
|
|
128970
|
+
return this.serverRequests.respondApproval(requestId, decision, _scope, matcher2);
|
|
128663
128971
|
}
|
|
128664
128972
|
applyRules(matchers) {
|
|
128665
128973
|
this.serverRequests.applyRules(matchers);
|
|
@@ -129258,9 +129566,1776 @@ var CodexAdapter = class {
|
|
|
129258
129566
|
}
|
|
129259
129567
|
};
|
|
129260
129568
|
|
|
129569
|
+
// packages/agent-host/src/adapters/cursor-agent/index.ts
|
|
129570
|
+
import { execFile as execFile9 } from "node:child_process";
|
|
129571
|
+
import { promisify as promisify8 } from "node:util";
|
|
129572
|
+
|
|
129573
|
+
// packages/agent-host/src/adapters/frames/recorder.ts
|
|
129574
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync18, mkdirSync as mkdirSync19 } from "node:fs";
|
|
129575
|
+
import { join as join44 } from "node:path";
|
|
129576
|
+
var processes = /* @__PURE__ */ new Map();
|
|
129577
|
+
function frameRecorder(sessionId, env = process.env) {
|
|
129578
|
+
const dir = env.CC_RECORD_FRAMES;
|
|
129579
|
+
if (!dir) return void 0;
|
|
129580
|
+
mkdirSync19(dir, { recursive: true });
|
|
129581
|
+
let n = (processes.get(sessionId) ?? 0) + 1;
|
|
129582
|
+
while (existsSync18(join44(dir, `${sessionId}.${n}.jsonl`))) n++;
|
|
129583
|
+
processes.set(sessionId, n);
|
|
129584
|
+
const file2 = join44(dir, `${sessionId}.${n}.jsonl`);
|
|
129585
|
+
const start2 = Date.now();
|
|
129586
|
+
const write = (entry) => {
|
|
129587
|
+
try {
|
|
129588
|
+
appendFileSync2(file2, `${JSON.stringify({ t: Date.now() - start2, ...entry })}
|
|
129589
|
+
`);
|
|
129590
|
+
} catch (e) {
|
|
129591
|
+
console.error("[acp] recorder could not write", file2, errorMessage(e));
|
|
129592
|
+
}
|
|
129593
|
+
};
|
|
129594
|
+
return {
|
|
129595
|
+
frame: (direction, line) => write({ dir: direction, line }),
|
|
129596
|
+
exit: (code2, signal) => write({ dir: "exit", msg: { code: code2, sig: signal } })
|
|
129597
|
+
};
|
|
129598
|
+
}
|
|
129599
|
+
|
|
129600
|
+
// packages/agent-host/src/adapters/acp/session.ts
|
|
129601
|
+
import { isAbsolute as isAbsolute10, join as join45 } from "node:path";
|
|
129602
|
+
|
|
129603
|
+
// packages/agent-host/src/adapters/acp/connection.ts
|
|
129604
|
+
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
129605
|
+
var RpcError = class extends Error {
|
|
129606
|
+
constructor(code2, message, data) {
|
|
129607
|
+
super(message);
|
|
129608
|
+
this.code = code2;
|
|
129609
|
+
this.data = data;
|
|
129610
|
+
}
|
|
129611
|
+
code;
|
|
129612
|
+
data;
|
|
129613
|
+
};
|
|
129614
|
+
var METHOD_NOT_FOUND2 = -32601;
|
|
129615
|
+
var AUTH_REQUIRED = -32e3;
|
|
129616
|
+
var FRAME = external_exports.looseObject({
|
|
129617
|
+
id: external_exports.union([external_exports.string(), external_exports.number(), external_exports.null()]).optional(),
|
|
129618
|
+
method: external_exports.string().optional(),
|
|
129619
|
+
params: external_exports.unknown().optional(),
|
|
129620
|
+
result: external_exports.unknown().optional(),
|
|
129621
|
+
error: external_exports.looseObject({ code: external_exports.number(), message: external_exports.string(), data: external_exports.unknown().optional() }).optional()
|
|
129622
|
+
});
|
|
129623
|
+
var NO_TIMEOUT_MS = 2 ** 31 - 1;
|
|
129624
|
+
var AcpConnection = class {
|
|
129625
|
+
constructor(proc, handlers, label) {
|
|
129626
|
+
this.proc = proc;
|
|
129627
|
+
this.handlers = handlers;
|
|
129628
|
+
this.label = label;
|
|
129629
|
+
proc.on("error", (err) => this.finish(null, new Error(`${label} failed to start: ${err.message}`)));
|
|
129630
|
+
proc.stdin.on("error", () => {
|
|
129631
|
+
});
|
|
129632
|
+
const lines = lineSplitter((line) => this.onLine(line));
|
|
129633
|
+
proc.stdout.on("data", (chunk) => lines.push(chunk));
|
|
129634
|
+
proc.stderr?.on("data", (d2) => {
|
|
129635
|
+
const s = String(d2).trim();
|
|
129636
|
+
if (s) console.error(`[${label}]`, s.slice(0, 500));
|
|
129637
|
+
});
|
|
129638
|
+
proc.on("exit", (code2) => this.finish(code2, null));
|
|
129639
|
+
}
|
|
129640
|
+
proc;
|
|
129641
|
+
handlers;
|
|
129642
|
+
label;
|
|
129643
|
+
/**
|
|
129644
|
+
* String ids with a per-connection prefix: a host that adopts a process the previous host was talking to can
|
|
129645
|
+
* receive answers to that host's requests (§4.4), and plain counters on both would have collided.
|
|
129646
|
+
*/
|
|
129647
|
+
idPrefix = `c${randomBytes8(3).toString("hex")}-`;
|
|
129648
|
+
nextId = 1;
|
|
129649
|
+
pending = new PendingRequests();
|
|
129650
|
+
closed = false;
|
|
129651
|
+
finished = false;
|
|
129652
|
+
finish(code2, startError) {
|
|
129653
|
+
if (this.finished) return;
|
|
129654
|
+
this.finished = true;
|
|
129655
|
+
const expected = this.closed && !startError;
|
|
129656
|
+
this.closed = true;
|
|
129657
|
+
this.pending.rejectAll(startError ?? new Error(expected ? `request cancelled while closing ${this.label}` : `${this.label} exited`));
|
|
129658
|
+
this.handlers.onExit(code2, expected);
|
|
129659
|
+
}
|
|
129660
|
+
onLine(line) {
|
|
129661
|
+
if (!line.trim()) return;
|
|
129662
|
+
this.handlers.onFrame?.("in", line);
|
|
129663
|
+
let raw;
|
|
129664
|
+
try {
|
|
129665
|
+
raw = JSON.parse(line);
|
|
129666
|
+
} catch {
|
|
129667
|
+
console.error(`[${this.label}] non-JSON output:`, line.slice(0, 200));
|
|
129668
|
+
return;
|
|
129669
|
+
}
|
|
129670
|
+
const parsed = FRAME.safeParse(raw);
|
|
129671
|
+
if (!parsed.success) {
|
|
129672
|
+
console.error(`[${this.label}] a frame that is not a JSON-RPC message:`, line.slice(0, 200));
|
|
129673
|
+
return;
|
|
129674
|
+
}
|
|
129675
|
+
const msg = parsed.data;
|
|
129676
|
+
const id3 = msg.id ?? void 0;
|
|
129677
|
+
if (id3 !== void 0 && msg.method === void 0) {
|
|
129678
|
+
const error51 = msg.error ? new RpcError(msg.error.code, msg.error.message, msg.error.data) : null;
|
|
129679
|
+
const settled = error51 ? this.pending.reject(id3, error51) : this.pending.resolve(id3, msg.result);
|
|
129680
|
+
if (!settled) this.handlers.onStrayResponse?.(id3, msg.result, error51);
|
|
129681
|
+
return;
|
|
129682
|
+
}
|
|
129683
|
+
if (id3 !== void 0 && msg.method !== void 0) {
|
|
129684
|
+
void this.answer(id3, msg.method, msg.params);
|
|
129685
|
+
return;
|
|
129686
|
+
}
|
|
129687
|
+
if (msg.method !== void 0) this.handlers.onNotification(msg.method, msg.params);
|
|
129688
|
+
}
|
|
129689
|
+
async answer(id3, method, params) {
|
|
129690
|
+
try {
|
|
129691
|
+
const result = await this.handlers.onRequest(id3, method, params);
|
|
129692
|
+
this.write({ jsonrpc: "2.0", id: id3, result: result ?? null });
|
|
129693
|
+
} catch (err) {
|
|
129694
|
+
const e = err instanceof RpcError ? err : new RpcError(-32603, errorMessage(err));
|
|
129695
|
+
this.write({ jsonrpc: "2.0", id: id3, error: { code: e.code, message: e.message, ...e.data === void 0 ? {} : { data: e.data } } });
|
|
129696
|
+
}
|
|
129697
|
+
}
|
|
129698
|
+
/** The answer, unread: each caller parses it with the schema of what it asked (`wire.ts`) */
|
|
129699
|
+
request(method, params, timeoutMs = 12e4) {
|
|
129700
|
+
if (this.closed) return Promise.reject(new Error(`${this.label} has already exited`));
|
|
129701
|
+
const id3 = `${this.idPrefix}${this.nextId++}`;
|
|
129702
|
+
const answer = this.pending.wait(id3, timeoutMs, `No response for ${method}`);
|
|
129703
|
+
this.write({ jsonrpc: "2.0", id: id3, method, params });
|
|
129704
|
+
return answer;
|
|
129705
|
+
}
|
|
129706
|
+
notify(method, params) {
|
|
129707
|
+
this.write({ jsonrpc: "2.0", method, params });
|
|
129708
|
+
}
|
|
129709
|
+
get isClosed() {
|
|
129710
|
+
return this.closed;
|
|
129711
|
+
}
|
|
129712
|
+
stderrTail() {
|
|
129713
|
+
return this.proc.stderrTail?.() ?? "";
|
|
129714
|
+
}
|
|
129715
|
+
write(obj) {
|
|
129716
|
+
if (this.closed) return;
|
|
129717
|
+
const line = JSON.stringify(obj);
|
|
129718
|
+
this.handlers.onFrame?.("out", line);
|
|
129719
|
+
this.proc.stdin.write(line + "\n");
|
|
129720
|
+
}
|
|
129721
|
+
/** stdin EOF first, so the agent can end its own children; the whole tree is killed after `graceMs` */
|
|
129722
|
+
async dispose(graceMs = 2e3) {
|
|
129723
|
+
if (this.closed) return;
|
|
129724
|
+
this.closed = true;
|
|
129725
|
+
this.proc.stdin.end();
|
|
129726
|
+
const exited = this.finished || await waitForExit(this.proc, graceMs);
|
|
129727
|
+
if (!exited) this.proc.kill("SIGKILL");
|
|
129728
|
+
}
|
|
129729
|
+
/** Lets go of the process without closing it (a host swap under the keeper): no EOF, no signal */
|
|
129730
|
+
async detach() {
|
|
129731
|
+
if (this.closed) return;
|
|
129732
|
+
await this.proc.detach?.();
|
|
129733
|
+
this.closed = true;
|
|
129734
|
+
this.finished = true;
|
|
129735
|
+
this.pending.rejectAll(new Error(`request abandoned: this host let go of ${this.label}`));
|
|
129736
|
+
}
|
|
129737
|
+
};
|
|
129738
|
+
|
|
129739
|
+
// packages/agent-host/src/adapters/acp/known-updates.ts
|
|
129740
|
+
var KNOWN_UPDATES = /* @__PURE__ */ new Set([
|
|
129741
|
+
"agent_message_chunk",
|
|
129742
|
+
"agent_thought_chunk",
|
|
129743
|
+
"user_message_chunk",
|
|
129744
|
+
"tool_call",
|
|
129745
|
+
"tool_call_update",
|
|
129746
|
+
"plan",
|
|
129747
|
+
"available_commands_update",
|
|
129748
|
+
"current_mode_update",
|
|
129749
|
+
"config_option_update",
|
|
129750
|
+
"session_info_update",
|
|
129751
|
+
"usage_update",
|
|
129752
|
+
"compaction_update",
|
|
129753
|
+
"compaction_summary_chunk",
|
|
129754
|
+
"notice",
|
|
129755
|
+
"session/update",
|
|
129756
|
+
"session/request_permission"
|
|
129757
|
+
]);
|
|
129758
|
+
|
|
129759
|
+
// packages/agent-host/src/adapters/acp/wire.ts
|
|
129760
|
+
var nullish3 = (t) => t.nullish().transform((v) => v ?? void 0);
|
|
129761
|
+
var InitializeResult = external_exports.looseObject({
|
|
129762
|
+
protocolVersion: external_exports.number(),
|
|
129763
|
+
agentCapabilities: nullish3(
|
|
129764
|
+
external_exports.looseObject({
|
|
129765
|
+
loadSession: nullish3(external_exports.boolean()),
|
|
129766
|
+
promptCapabilities: nullish3(external_exports.looseObject({ image: nullish3(external_exports.boolean()) })),
|
|
129767
|
+
sessionCapabilities: nullish3(
|
|
129768
|
+
external_exports.looseObject({ resume: external_exports.unknown().optional(), list: external_exports.unknown().optional(), close: external_exports.unknown().optional() })
|
|
129769
|
+
)
|
|
129770
|
+
})
|
|
129771
|
+
),
|
|
129772
|
+
agentInfo: nullish3(external_exports.looseObject({ name: nullish3(external_exports.string()), version: nullish3(external_exports.string()) }))
|
|
129773
|
+
});
|
|
129774
|
+
var SelectOption = external_exports.looseObject({ value: external_exports.string(), name: nullish3(external_exports.string()) });
|
|
129775
|
+
var ConfigOption = external_exports.looseObject({
|
|
129776
|
+
id: external_exports.string(),
|
|
129777
|
+
category: nullish3(external_exports.string()),
|
|
129778
|
+
type: nullish3(external_exports.string()),
|
|
129779
|
+
currentValue: external_exports.unknown().optional(),
|
|
129780
|
+
options: nullish3(external_exports.array(external_exports.union([SelectOption, external_exports.looseObject({ options: external_exports.array(SelectOption) })])))
|
|
129781
|
+
});
|
|
129782
|
+
var OpenedSession = external_exports.looseObject({
|
|
129783
|
+
sessionId: nullish3(external_exports.string()),
|
|
129784
|
+
configOptions: nullish3(external_exports.array(ConfigOption))
|
|
129785
|
+
});
|
|
129786
|
+
var StopReason = external_exports.enum(["end_turn", "max_tokens", "max_turn_requests", "refusal", "cancelled"]);
|
|
129787
|
+
var PromptResult = external_exports.looseObject({
|
|
129788
|
+
stopReason: external_exports.union([StopReason, external_exports.string()]),
|
|
129789
|
+
usage: nullish3(
|
|
129790
|
+
external_exports.looseObject({
|
|
129791
|
+
inputTokens: nullish3(external_exports.number()),
|
|
129792
|
+
outputTokens: nullish3(external_exports.number()),
|
|
129793
|
+
cachedReadTokens: nullish3(external_exports.number()),
|
|
129794
|
+
cachedWriteTokens: nullish3(external_exports.number()),
|
|
129795
|
+
thoughtTokens: nullish3(external_exports.number())
|
|
129796
|
+
})
|
|
129797
|
+
)
|
|
129798
|
+
});
|
|
129799
|
+
var ContentBlock = external_exports.looseObject({
|
|
129800
|
+
type: external_exports.string(),
|
|
129801
|
+
text: nullish3(external_exports.string()),
|
|
129802
|
+
data: nullish3(external_exports.string()),
|
|
129803
|
+
mimeType: nullish3(external_exports.string())
|
|
129804
|
+
});
|
|
129805
|
+
var ToolCallContent = external_exports.looseObject({
|
|
129806
|
+
type: external_exports.string(),
|
|
129807
|
+
content: nullish3(ContentBlock),
|
|
129808
|
+
path: nullish3(external_exports.string()),
|
|
129809
|
+
oldText: nullish3(external_exports.string()),
|
|
129810
|
+
newText: nullish3(external_exports.string())
|
|
129811
|
+
});
|
|
129812
|
+
var CallStatus = external_exports.enum(["pending", "in_progress", "completed", "failed"]);
|
|
129813
|
+
var ToolCallFields = external_exports.looseObject({
|
|
129814
|
+
toolCallId: external_exports.string(),
|
|
129815
|
+
title: nullish3(external_exports.string()),
|
|
129816
|
+
kind: nullish3(external_exports.string()),
|
|
129817
|
+
status: nullish3(external_exports.union([CallStatus, external_exports.string()])),
|
|
129818
|
+
rawInput: external_exports.unknown().optional(),
|
|
129819
|
+
rawOutput: external_exports.unknown().optional(),
|
|
129820
|
+
content: nullish3(external_exports.array(ToolCallContent)),
|
|
129821
|
+
locations: nullish3(external_exports.array(external_exports.looseObject({ path: external_exports.string() })))
|
|
129822
|
+
});
|
|
129823
|
+
var Chunk = external_exports.looseObject({ content: ContentBlock, messageId: nullish3(external_exports.string()) });
|
|
129824
|
+
var SessionUpdate = external_exports.discriminatedUnion("sessionUpdate", [
|
|
129825
|
+
Chunk.extend({ sessionUpdate: external_exports.literal("agent_message_chunk") }),
|
|
129826
|
+
Chunk.extend({ sessionUpdate: external_exports.literal("agent_thought_chunk") }),
|
|
129827
|
+
Chunk.extend({ sessionUpdate: external_exports.literal("user_message_chunk") }),
|
|
129828
|
+
ToolCallFields.extend({ sessionUpdate: external_exports.literal("tool_call") }),
|
|
129829
|
+
ToolCallFields.extend({ sessionUpdate: external_exports.literal("tool_call_update") }),
|
|
129830
|
+
external_exports.looseObject({
|
|
129831
|
+
sessionUpdate: external_exports.literal("plan"),
|
|
129832
|
+
entries: external_exports.array(external_exports.looseObject({ content: external_exports.string(), status: external_exports.string(), priority: nullish3(external_exports.string()) }))
|
|
129833
|
+
}),
|
|
129834
|
+
external_exports.looseObject({
|
|
129835
|
+
sessionUpdate: external_exports.literal("available_commands_update"),
|
|
129836
|
+
availableCommands: external_exports.array(
|
|
129837
|
+
external_exports.looseObject({ name: external_exports.string(), description: nullish3(external_exports.string()), input: nullish3(external_exports.looseObject({ hint: nullish3(external_exports.string()) })) })
|
|
129838
|
+
)
|
|
129839
|
+
}),
|
|
129840
|
+
external_exports.looseObject({ sessionUpdate: external_exports.literal("session_info_update"), title: nullish3(external_exports.string()) }),
|
|
129841
|
+
external_exports.looseObject({
|
|
129842
|
+
sessionUpdate: external_exports.literal("usage_update"),
|
|
129843
|
+
used: external_exports.number(),
|
|
129844
|
+
size: external_exports.number(),
|
|
129845
|
+
cost: external_exports.looseObject({ amount: external_exports.number(), currency: external_exports.string() }).nullish()
|
|
129846
|
+
}),
|
|
129847
|
+
external_exports.looseObject({ sessionUpdate: external_exports.literal("current_mode_update"), currentModeId: external_exports.string() }),
|
|
129848
|
+
external_exports.looseObject({ sessionUpdate: external_exports.literal("config_option_update"), configOptions: external_exports.array(ConfigOption) }),
|
|
129849
|
+
external_exports.looseObject({
|
|
129850
|
+
sessionUpdate: external_exports.literal("compaction_update"),
|
|
129851
|
+
compactionId: external_exports.string(),
|
|
129852
|
+
status: external_exports.string(),
|
|
129853
|
+
summary: nullish3(external_exports.array(ContentBlock)),
|
|
129854
|
+
error: nullish3(external_exports.string())
|
|
129855
|
+
}),
|
|
129856
|
+
external_exports.looseObject({ sessionUpdate: external_exports.literal("compaction_summary_chunk"), compactionId: external_exports.string(), content: ContentBlock }),
|
|
129857
|
+
external_exports.looseObject({ sessionUpdate: external_exports.literal("notice"), severity: external_exports.string(), title: external_exports.string(), description: nullish3(external_exports.string()) })
|
|
129858
|
+
]);
|
|
129859
|
+
var SessionNotification = external_exports.looseObject({ sessionId: external_exports.string(), update: external_exports.looseObject({ sessionUpdate: external_exports.string() }) });
|
|
129860
|
+
function readUpdate(params) {
|
|
129861
|
+
const n = SessionNotification.safeParse(params);
|
|
129862
|
+
if (!n.success) return null;
|
|
129863
|
+
const u = SessionUpdate.safeParse(n.data.update);
|
|
129864
|
+
return u.success ? { sessionId: n.data.sessionId, update: u.data } : { sessionId: n.data.sessionId, other: n.data.update.sessionUpdate };
|
|
129865
|
+
}
|
|
129866
|
+
var PermissionOptionKind = external_exports.enum(["allow_once", "allow_always", "reject_once", "reject_always"]);
|
|
129867
|
+
var PermissionOption = external_exports.looseObject({ optionId: external_exports.string(), kind: external_exports.union([PermissionOptionKind, external_exports.string()]), name: nullish3(external_exports.string()) });
|
|
129868
|
+
var RequestPermission = external_exports.looseObject({
|
|
129869
|
+
sessionId: external_exports.string(),
|
|
129870
|
+
toolCall: ToolCallFields,
|
|
129871
|
+
options: external_exports.array(PermissionOption)
|
|
129872
|
+
});
|
|
129873
|
+
function blockText(block) {
|
|
129874
|
+
return block?.type === "text" ? block.text ?? "" : "";
|
|
129875
|
+
}
|
|
129876
|
+
|
|
129877
|
+
// packages/agent-host/src/adapters/acp/normalize.ts
|
|
129878
|
+
var KIND_TOOL = {
|
|
129879
|
+
execute: "Bash",
|
|
129880
|
+
edit: "Edit",
|
|
129881
|
+
delete: "Edit",
|
|
129882
|
+
move: "Edit",
|
|
129883
|
+
read: "Read",
|
|
129884
|
+
search: "Grep",
|
|
129885
|
+
fetch: "WebFetch",
|
|
129886
|
+
think: "Think"
|
|
129887
|
+
};
|
|
129888
|
+
var OPEN_TAG = "<think>";
|
|
129889
|
+
var CLOSE_TAG = "</think>";
|
|
129890
|
+
function heldTail(text2) {
|
|
129891
|
+
for (let n = Math.min(CLOSE_TAG.length - 1, text2.length); n > 0; n--) if (CLOSE_TAG.startsWith(text2.slice(text2.length - n))) return n;
|
|
129892
|
+
return 0;
|
|
129893
|
+
}
|
|
129894
|
+
var AcpNormalizer = class {
|
|
129895
|
+
constructor(sessionId, label = "The tool") {
|
|
129896
|
+
this.sessionId = sessionId;
|
|
129897
|
+
this.label = label;
|
|
129898
|
+
}
|
|
129899
|
+
sessionId;
|
|
129900
|
+
label;
|
|
129901
|
+
/** Calls announced as `tool_call` and not yet ended, by `toolCallId` */
|
|
129902
|
+
open = /* @__PURE__ */ new Map();
|
|
129903
|
+
/** Every field said of each call this turn, merged: a permission request may carry less than the call did (measured, Cursor) */
|
|
129904
|
+
known = /* @__PURE__ */ new Map();
|
|
129905
|
+
compactions = /* @__PURE__ */ new Map();
|
|
129906
|
+
segment = 0;
|
|
129907
|
+
lastChunk = "other";
|
|
129908
|
+
/** The events one update says. `available_commands_update` and the like are read by the session, not here */
|
|
129909
|
+
update(u) {
|
|
129910
|
+
const sessionId = this.sessionId;
|
|
129911
|
+
const held3 = u.sessionUpdate === "agent_message_chunk" ? [] : this.releaseHeld();
|
|
129912
|
+
return [...held3, ...this.translate(u, sessionId)];
|
|
129913
|
+
}
|
|
129914
|
+
translate(u, sessionId) {
|
|
129915
|
+
switch (u.sessionUpdate) {
|
|
129916
|
+
case "agent_message_chunk": {
|
|
129917
|
+
const text2 = blockText(u.content);
|
|
129918
|
+
if (u.content.type === "image" && u.content.data && u.content.mimeType) {
|
|
129919
|
+
return [{ type: "message_image", sessionId, mime: u.content.mimeType, data: u.content.data }];
|
|
129920
|
+
}
|
|
129921
|
+
if (this.lastChunk !== "message") this.segment++;
|
|
129922
|
+
this.lastChunk = "message";
|
|
129923
|
+
return this.messageText(u.messageId ?? `acp-${this.segment}`, text2);
|
|
129924
|
+
}
|
|
129925
|
+
case "agent_thought_chunk": {
|
|
129926
|
+
this.lastChunk = "thought";
|
|
129927
|
+
const text2 = blockText(u.content);
|
|
129928
|
+
return text2 ? [{ type: "reasoning_delta", sessionId, text: text2 }] : [];
|
|
129929
|
+
}
|
|
129930
|
+
case "tool_call":
|
|
129931
|
+
case "tool_call_update":
|
|
129932
|
+
this.lastChunk = "other";
|
|
129933
|
+
return this.toolCall(u);
|
|
129934
|
+
case "plan":
|
|
129935
|
+
return [planUpdate(sessionId, u.entries, "content")];
|
|
129936
|
+
case "session_info_update":
|
|
129937
|
+
return u.title ? [{ type: "session_title", sessionId, title: u.title, auto: true }] : [];
|
|
129938
|
+
case "compaction_summary_chunk":
|
|
129939
|
+
this.compactions.set(u.compactionId, (this.compactions.get(u.compactionId) ?? "") + blockText(u.content));
|
|
129940
|
+
return [];
|
|
129941
|
+
case "compaction_update":
|
|
129942
|
+
return this.compaction(u);
|
|
129943
|
+
case "notice":
|
|
129944
|
+
return [{ type: "notice", sessionId, level: u.severity === "error" ? "error" : u.severity === "warning" ? "warning" : "info", text: [u.title, u.description].filter(Boolean).join("\n") }];
|
|
129945
|
+
default:
|
|
129946
|
+
return [];
|
|
129947
|
+
}
|
|
129948
|
+
}
|
|
129949
|
+
/**
|
|
129950
|
+
* A message's text, without the `</think>` a model leaves where it put its reasoning in the reply (OpenCode's free
|
|
129951
|
+
* model, measured in its own store: a step's text is the reasoning, then `</think>`, then the reply, with no `<think>`
|
|
129952
|
+
* before it). The first such tag of a message is dropped with the space after it; a tag split over two chunks is held
|
|
129953
|
+
* until the next one says whether it is one. The reasoning before it stays reply text: it was shown as it came.
|
|
129954
|
+
*/
|
|
129955
|
+
messageText(messageId, text2) {
|
|
129956
|
+
const before = this.lastMessage === messageId ? [] : this.releaseHeld();
|
|
129957
|
+
const m = this.messages.get(messageId) ?? { held: "", settled: false, trim: false };
|
|
129958
|
+
this.messages.set(messageId, m);
|
|
129959
|
+
let out = m.held + text2;
|
|
129960
|
+
m.held = "";
|
|
129961
|
+
if (m.trim) {
|
|
129962
|
+
out = out.replace(/^\s+/, "");
|
|
129963
|
+
m.trim = out === "";
|
|
129964
|
+
}
|
|
129965
|
+
if (!m.settled) {
|
|
129966
|
+
const open6 = out.indexOf(OPEN_TAG);
|
|
129967
|
+
const close = out.indexOf(CLOSE_TAG);
|
|
129968
|
+
if (close !== -1 && (open6 === -1 || open6 > close)) {
|
|
129969
|
+
const after = out.slice(close + CLOSE_TAG.length).replace(/^\s+/, "");
|
|
129970
|
+
m.trim = after === "";
|
|
129971
|
+
out = out.slice(0, close) + after;
|
|
129972
|
+
m.settled = true;
|
|
129973
|
+
} else if (open6 !== -1) m.settled = true;
|
|
129974
|
+
else {
|
|
129975
|
+
const keep = heldTail(out);
|
|
129976
|
+
m.held = out.slice(out.length - keep);
|
|
129977
|
+
out = out.slice(0, out.length - keep);
|
|
129978
|
+
}
|
|
129979
|
+
}
|
|
129980
|
+
this.lastMessage = messageId;
|
|
129981
|
+
return [...before, ...out ? [{ type: "message_delta", sessionId: this.sessionId, role: "assistant", text: out, messageId }] : []];
|
|
129982
|
+
}
|
|
129983
|
+
/** What a message held back, once something else comes: it was no tag */
|
|
129984
|
+
releaseHeld() {
|
|
129985
|
+
const m = this.lastMessage === null ? void 0 : this.messages.get(this.lastMessage);
|
|
129986
|
+
if (!m?.held || this.lastMessage === null) return [];
|
|
129987
|
+
const text2 = m.held;
|
|
129988
|
+
m.held = "";
|
|
129989
|
+
return [{ type: "message_delta", sessionId: this.sessionId, role: "assistant", text: text2, messageId: this.lastMessage }];
|
|
129990
|
+
}
|
|
129991
|
+
messages = /* @__PURE__ */ new Map();
|
|
129992
|
+
lastMessage = null;
|
|
129993
|
+
/** Calls the person (or a Stop) refused this turn: whatever the agent says of them after, they did not run as asked */
|
|
129994
|
+
refused = /* @__PURE__ */ new Set();
|
|
129995
|
+
/**
|
|
129996
|
+
* What this turn has heard of a call, merged with `u`. `asked`: `u` is a permission request's, which may carry less than
|
|
129997
|
+
* the call did (no input, measured on Cursor) and a looser kind: Cursor asks about a `delete` call as `kind: edit`
|
|
129998
|
+
* (measured), so the call's own kind wins over the request's label
|
|
129999
|
+
*/
|
|
130000
|
+
call(u, asked = false) {
|
|
130001
|
+
const before = this.known.get(u.toolCallId);
|
|
130002
|
+
const merged = { ...before, ...definedFields(u) };
|
|
130003
|
+
return ToolCallFields.parse(asked && before?.kind ? { ...merged, kind: before.kind } : merged);
|
|
130004
|
+
}
|
|
130005
|
+
/** The call was answered no (a reject, or cancelled by a Stop): its end is said as not ok */
|
|
130006
|
+
refuse(callId) {
|
|
130007
|
+
this.refused.add(callId);
|
|
130008
|
+
}
|
|
130009
|
+
toolCall(u) {
|
|
130010
|
+
const id3 = u.toolCallId;
|
|
130011
|
+
const seen = this.call(u);
|
|
130012
|
+
this.known.set(id3, seen);
|
|
130013
|
+
const events2 = [];
|
|
130014
|
+
let call2 = this.open.get(id3);
|
|
130015
|
+
if (!call2) {
|
|
130016
|
+
const ready = hasInput(seen.rawInput) || seen.status !== void 0 && seen.status !== "pending";
|
|
130017
|
+
if (!ready) return [];
|
|
130018
|
+
call2 = { output: "" };
|
|
130019
|
+
this.open.set(id3, call2);
|
|
130020
|
+
events2.push({ type: "tool_call", sessionId: this.sessionId, callId: id3, summary: summaryOf2(seen), ...seen.rawInput === void 0 ? {} : { input: seen.rawInput } });
|
|
130021
|
+
if (seen.kind === "edit" || seen.kind === "delete" || seen.kind === "move") {
|
|
130022
|
+
const paths = pathsOf2(seen);
|
|
130023
|
+
if (paths.length) events2.push({ type: "files_touched", sessionId: this.sessionId, paths });
|
|
130024
|
+
}
|
|
130025
|
+
}
|
|
130026
|
+
const output = outputOf(u);
|
|
130027
|
+
if (output !== null && output.startsWith(call2.output) && output.length > call2.output.length && u.status !== "completed" && u.status !== "failed") {
|
|
130028
|
+
events2.push({ type: "tool_output_delta", sessionId: this.sessionId, callId: id3, text: output.slice(call2.output.length) });
|
|
130029
|
+
call2.output = output;
|
|
130030
|
+
}
|
|
130031
|
+
if (u.status === "completed" || u.status === "failed") {
|
|
130032
|
+
this.open.delete(id3);
|
|
130033
|
+
const refused2 = this.refused.delete(id3);
|
|
130034
|
+
const ok = u.status === "completed" && !refused2 && rawOutputOk(seen.rawOutput);
|
|
130035
|
+
const text2 = output ?? rawOutputText(seen.rawOutput) ?? call2.output;
|
|
130036
|
+
events2.push(toolResultEvent(this.sessionId, id3, ok, text2 || (refused2 ? "Not run: the request was refused." : ""), `${this.label} did not return the output`));
|
|
130037
|
+
}
|
|
130038
|
+
return events2;
|
|
130039
|
+
}
|
|
130040
|
+
/** Calls still open when the turn ended: closed as failed, so no card spins forever (`why`: what ended them) */
|
|
130041
|
+
endTurn(why = "the turn ended before this call finished") {
|
|
130042
|
+
const events2 = [...this.releaseHeld(), ...[...this.open.keys()].map((id3) => toolResultEvent(this.sessionId, id3, false, "", why))];
|
|
130043
|
+
this.open.clear();
|
|
130044
|
+
this.known.clear();
|
|
130045
|
+
this.refused.clear();
|
|
130046
|
+
this.messages.clear();
|
|
130047
|
+
this.lastChunk = "other";
|
|
130048
|
+
return events2;
|
|
130049
|
+
}
|
|
130050
|
+
compaction(u) {
|
|
130051
|
+
if (u.status === "in_progress") return [{ type: "activity", sessionId: this.sessionId, activity: "compacting" }];
|
|
130052
|
+
const summary = u.summary?.map(blockText).join("") || this.compactions.get(u.compactionId) || "";
|
|
130053
|
+
this.compactions.delete(u.compactionId);
|
|
130054
|
+
const done2 = { type: "activity", sessionId: this.sessionId, activity: null };
|
|
130055
|
+
if (u.status === "completed") return [done2, { type: "compaction", sessionId: this.sessionId, failed: false, ...summary ? { reason: firstLine2(summary, 300) } : {} }];
|
|
130056
|
+
if (u.status === "failed") return [done2, { type: "compaction", sessionId: this.sessionId, failed: true, ...u.error ? { reason: u.error } : {} }];
|
|
130057
|
+
return [done2];
|
|
130058
|
+
}
|
|
130059
|
+
};
|
|
130060
|
+
function tokenUsage(result) {
|
|
130061
|
+
const u = result.usage;
|
|
130062
|
+
if (!u || u.inputTokens === void 0 && u.outputTokens === void 0) return null;
|
|
130063
|
+
return {
|
|
130064
|
+
inputTokens: u.inputTokens ?? 0,
|
|
130065
|
+
outputTokens: (u.outputTokens ?? 0) + (u.thoughtTokens ?? 0),
|
|
130066
|
+
cacheReadTokens: u.cachedReadTokens ?? 0,
|
|
130067
|
+
cacheCreationTokens: u.cachedWriteTokens ?? 0
|
|
130068
|
+
};
|
|
130069
|
+
}
|
|
130070
|
+
function definedFields(u) {
|
|
130071
|
+
return Object.fromEntries(Object.entries(u).filter(([k2, v]) => v !== void 0 && k2 !== "sessionUpdate"));
|
|
130072
|
+
}
|
|
130073
|
+
function hasInput(v) {
|
|
130074
|
+
return typeof v === "object" && v !== null && Object.keys(v).some((k2) => k2 !== "cwd");
|
|
130075
|
+
}
|
|
130076
|
+
function pathsOf2(u) {
|
|
130077
|
+
const fromDiffs = (u.content ?? []).flatMap((c) => c.type === "diff" && c.path ? [c.path] : []);
|
|
130078
|
+
return [.../* @__PURE__ */ new Set([...(u.locations ?? []).map((l) => l.path), ...fromDiffs])];
|
|
130079
|
+
}
|
|
130080
|
+
function summaryOf2(u) {
|
|
130081
|
+
const tool = u.kind && KIND_TOOL[u.kind] || u.title || "tool";
|
|
130082
|
+
const command = u.kind === "execute" ? commandOf(u) : "";
|
|
130083
|
+
return { tool, title: firstLine2(command || u.title || tool), paths: pathsOf2(u) };
|
|
130084
|
+
}
|
|
130085
|
+
function outputOf(u) {
|
|
130086
|
+
if (!u.content?.length) return null;
|
|
130087
|
+
return u.content.map((c) => c.type === "content" ? blockText(c.content) : c.type === "diff" ? `${c.path ?? ""}
|
|
130088
|
+
${c.newText ?? ""}` : "").join("");
|
|
130089
|
+
}
|
|
130090
|
+
function rawOutputText(raw) {
|
|
130091
|
+
if (typeof raw === "string") return raw;
|
|
130092
|
+
const o = asObj(raw);
|
|
130093
|
+
if (typeof o.output === "string") return o.output;
|
|
130094
|
+
const streams = [o.stdout, o.stderr].filter((s) => typeof s === "string" && s.length > 0);
|
|
130095
|
+
if (streams.length) return streams.join(streams[0]?.endsWith("\n") ? "" : "\n");
|
|
130096
|
+
if (typeof o.error === "string") return o.error;
|
|
130097
|
+
return null;
|
|
130098
|
+
}
|
|
130099
|
+
function rawOutputOk(raw) {
|
|
130100
|
+
const o = asObj(raw);
|
|
130101
|
+
if (typeof o.exitCode === "number" && o.exitCode !== 0) return false;
|
|
130102
|
+
return o.success !== false;
|
|
130103
|
+
}
|
|
130104
|
+
function commandOf(u) {
|
|
130105
|
+
const command = asObj(u.rawInput).command;
|
|
130106
|
+
return typeof command === "string" ? command : "";
|
|
130107
|
+
}
|
|
130108
|
+
|
|
130109
|
+
// packages/agent-host/src/adapters/acp/session.ts
|
|
130110
|
+
var TREE_NOTE_MS = 2e3;
|
|
130111
|
+
var CANCELLED = { outcome: { outcome: "cancelled" } };
|
|
130112
|
+
var AcpSession = class {
|
|
130113
|
+
constructor(opts, emit, deps) {
|
|
130114
|
+
this.opts = opts;
|
|
130115
|
+
this.emit = emit;
|
|
130116
|
+
this.deps = deps;
|
|
130117
|
+
this.preset = opts.permissionPreset;
|
|
130118
|
+
this.normalizer = new AcpNormalizer(opts.sessionId, deps.label);
|
|
130119
|
+
this.unmapped = new UnmappedTypes(deps.label, opts.sessionId, KNOWN_UPDATES);
|
|
130120
|
+
this.conn = new AcpConnection(
|
|
130121
|
+
deps.process,
|
|
130122
|
+
{
|
|
130123
|
+
onNotification: (method, params) => this.onNotification(method, params),
|
|
130124
|
+
onRequest: (id3, method, params) => this.onRequest(id3, method, params),
|
|
130125
|
+
onExit: (code2, expected) => this.onExit(code2, expected),
|
|
130126
|
+
...deps.onFrame ? { onFrame: deps.onFrame } : {}
|
|
130127
|
+
},
|
|
130128
|
+
deps.label
|
|
130129
|
+
);
|
|
130130
|
+
this.ready = this.open();
|
|
130131
|
+
this.ready.catch(() => {
|
|
130132
|
+
});
|
|
130133
|
+
}
|
|
130134
|
+
opts;
|
|
130135
|
+
emit;
|
|
130136
|
+
deps;
|
|
130137
|
+
externalId = null;
|
|
130138
|
+
/**
|
|
130139
|
+
* A process a previous host left is ended and the conversation opened again in a new one (`AcpAdapter`): a turn it was
|
|
130140
|
+
* running is not carried on, so the host ends it as one the restart stopped (`SessionManager.adoptKept`)
|
|
130141
|
+
*/
|
|
130142
|
+
takesOverTurn = false;
|
|
130143
|
+
ready;
|
|
130144
|
+
phase = "opening";
|
|
130145
|
+
conn;
|
|
130146
|
+
normalizer;
|
|
130147
|
+
unmapped;
|
|
130148
|
+
cards = new PendingCards();
|
|
130149
|
+
cardIds = new CardIds();
|
|
130150
|
+
rules = new AlwaysAllowRules();
|
|
130151
|
+
queue = [];
|
|
130152
|
+
turn = null;
|
|
130153
|
+
/** While `session/load` is answered, every update is the replay of the conversation, not this turn (§4.3) */
|
|
130154
|
+
replaying = false;
|
|
130155
|
+
canClose = false;
|
|
130156
|
+
preset;
|
|
130157
|
+
commands = [];
|
|
130158
|
+
lastTurnInputs = [];
|
|
130159
|
+
/** Commands running now, by call id, and the clock that notes the tree while any runs */
|
|
130160
|
+
commandsRunning = /* @__PURE__ */ new Set();
|
|
130161
|
+
treeClock = null;
|
|
130162
|
+
/** The last context reading taken, for the compact turn's comparison */
|
|
130163
|
+
lastUsed = null;
|
|
130164
|
+
/** The session's running cost as last read at a turn's end: a turn's own cost is what it adds (OpenCode sends the total) */
|
|
130165
|
+
costTotal = null;
|
|
130166
|
+
get sessionId() {
|
|
130167
|
+
return this.opts.sessionId;
|
|
130168
|
+
}
|
|
130169
|
+
async open() {
|
|
130170
|
+
try {
|
|
130171
|
+
const init = InitializeResult.parse(
|
|
130172
|
+
await this.conn.request(
|
|
130173
|
+
"initialize",
|
|
130174
|
+
{
|
|
130175
|
+
protocolVersion: 1,
|
|
130176
|
+
clientInfo: { name: "centralu", version: "1" },
|
|
130177
|
+
// No fs and no terminal: the agent uses its own tools, whose approvals come as `session/request_permission`
|
|
130178
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false, session: { compaction: {} } }
|
|
130179
|
+
},
|
|
130180
|
+
6e4
|
|
130181
|
+
)
|
|
130182
|
+
);
|
|
130183
|
+
const version2 = init.agentInfo?.version ?? await this.deps.version();
|
|
130184
|
+
if (version2) this.emit({ type: "agent_version", sessionId: this.sessionId, version: version2 });
|
|
130185
|
+
this.canClose = init.agentCapabilities?.sessionCapabilities?.close !== void 0;
|
|
130186
|
+
const base2 = { cwd: this.opts.cwd, mcpServers: this.mcpServers() };
|
|
130187
|
+
const resume = this.opts.resumeExternalId;
|
|
130188
|
+
if (resume) {
|
|
130189
|
+
const caps = init.agentCapabilities;
|
|
130190
|
+
const method = caps?.loadSession ? "session/load" : caps?.sessionCapabilities?.resume !== void 0 ? "session/resume" : null;
|
|
130191
|
+
if (!method) throw rpcError(`${this.deps.label} cannot reopen a conversation`, "session_not_found");
|
|
130192
|
+
this.replaying = method === "session/load";
|
|
130193
|
+
try {
|
|
130194
|
+
OpenedSession.parse(await this.conn.request(method, { ...base2, sessionId: resume }, 15e4) ?? {});
|
|
130195
|
+
} finally {
|
|
130196
|
+
this.replaying = false;
|
|
130197
|
+
}
|
|
130198
|
+
this.externalId = resume;
|
|
130199
|
+
this.costTotal = this.opts.runningCost?.last ?? null;
|
|
130200
|
+
} else {
|
|
130201
|
+
const opened = OpenedSession.parse(await this.conn.request("session/new", base2, 12e4));
|
|
130202
|
+
if (!opened.sessionId) throw new Error(`${this.deps.label} opened a session without an id`);
|
|
130203
|
+
this.externalId = opened.sessionId;
|
|
130204
|
+
this.costTotal = 0;
|
|
130205
|
+
this.opts.runningCost?.keep(0);
|
|
130206
|
+
}
|
|
130207
|
+
this.phase = "ready";
|
|
130208
|
+
this.next();
|
|
130209
|
+
} catch (err) {
|
|
130210
|
+
throw this.openError(err);
|
|
130211
|
+
}
|
|
130212
|
+
}
|
|
130213
|
+
openError(err) {
|
|
130214
|
+
if (err instanceof RpcError && err.code === AUTH_REQUIRED) {
|
|
130215
|
+
return rpcError(`${this.deps.label} is not logged in. Log in from a terminal, then try again.`, "not_logged_in");
|
|
130216
|
+
}
|
|
130217
|
+
if (err instanceof RpcError) return rpcError(`${this.deps.label}: ${err.message}`, "internal");
|
|
130218
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
130219
|
+
}
|
|
130220
|
+
/** Centralu's own tools through the same stdio bridge Codex starts (§6). Apps are not attached to an ACP agent yet */
|
|
130221
|
+
mcpServers() {
|
|
130222
|
+
const bridge = this.opts.orchestratorBridge;
|
|
130223
|
+
if (!this.opts.orchestratorTools || !bridge) return [];
|
|
130224
|
+
return [
|
|
130225
|
+
{
|
|
130226
|
+
name: ORCHESTRATOR_MCP_NAME,
|
|
130227
|
+
command: process.execPath,
|
|
130228
|
+
args: [bridgePath()],
|
|
130229
|
+
env: [
|
|
130230
|
+
{ name: "CC_HOST_URL", value: bridge.url },
|
|
130231
|
+
{ name: "CC_BRIDGE_TOKEN", value: bridge.token },
|
|
130232
|
+
{ name: "CC_SESSION_ID", value: this.opts.sessionId }
|
|
130233
|
+
]
|
|
130234
|
+
}
|
|
130235
|
+
];
|
|
130236
|
+
}
|
|
130237
|
+
appAttachment() {
|
|
130238
|
+
return "failed";
|
|
130239
|
+
}
|
|
130240
|
+
send(text2, opts) {
|
|
130241
|
+
this.enqueue({ text: text2, ...opts?.id ? { id: opts.id } : {}, ...opts?.compact ? { compact: true } : {} });
|
|
130242
|
+
}
|
|
130243
|
+
sendTracked(text2, observer) {
|
|
130244
|
+
return new Promise((resolve21, reject) => this.enqueue({ text: text2, ...observer ? { observer } : {}, done: { resolve: resolve21, reject } }));
|
|
130245
|
+
}
|
|
130246
|
+
enqueue(input) {
|
|
130247
|
+
if (this.phase === "closed") {
|
|
130248
|
+
const e = new Error(`${this.deps.label} session is closed`);
|
|
130249
|
+
input.observer?.fail(e);
|
|
130250
|
+
input.done?.reject(e);
|
|
130251
|
+
return;
|
|
130252
|
+
}
|
|
130253
|
+
this.queue.push(input);
|
|
130254
|
+
this.next();
|
|
130255
|
+
}
|
|
130256
|
+
/** ACP runs one prompt at a time; a message sent during a turn waits for it (§5.4) */
|
|
130257
|
+
next() {
|
|
130258
|
+
if (this.phase !== "ready" || this.turn) return;
|
|
130259
|
+
const input = this.queue.shift();
|
|
130260
|
+
if (input) void this.run(input);
|
|
130261
|
+
}
|
|
130262
|
+
turnInputs() {
|
|
130263
|
+
return this.lastTurnInputs;
|
|
130264
|
+
}
|
|
130265
|
+
async run(input) {
|
|
130266
|
+
const turn = { input, stopAsked: false, cancelResent: false, usedBefore: this.lastUsed, used: null, compacted: false, costTotal: null };
|
|
130267
|
+
this.turn = turn;
|
|
130268
|
+
this.emit({ type: "state_change", sessionId: this.sessionId, state: "working" });
|
|
130269
|
+
input.observer?.accepted?.();
|
|
130270
|
+
let result = null;
|
|
130271
|
+
let failure3 = null;
|
|
130272
|
+
try {
|
|
130273
|
+
result = PromptResult.parse(
|
|
130274
|
+
await this.conn.request("session/prompt", { sessionId: this.externalId, prompt: [{ type: "text", text: input.text }] }, NO_TIMEOUT_MS)
|
|
130275
|
+
);
|
|
130276
|
+
} catch (err) {
|
|
130277
|
+
failure3 = err instanceof Error ? err : new Error(String(err));
|
|
130278
|
+
}
|
|
130279
|
+
if (turn.stopTimer) clearTimeout(turn.stopTimer);
|
|
130280
|
+
if (this.phase === "closed") return;
|
|
130281
|
+
for (const e of this.normalizer.endTurn()) this.emit(e);
|
|
130282
|
+
this.stopTreeClock();
|
|
130283
|
+
this.closeCards();
|
|
130284
|
+
this.lastTurnInputs = input.id ? [input.id] : [];
|
|
130285
|
+
this.turn = null;
|
|
130286
|
+
if (failure3) {
|
|
130287
|
+
this.emit({ type: "error", sessionId: this.sessionId, error: { code: "internal", message: `${this.deps.label}: ${failure3.message}`, retryable: true } });
|
|
130288
|
+
input.observer?.fail(failure3);
|
|
130289
|
+
input.done?.reject(failure3);
|
|
130290
|
+
} else if (result) {
|
|
130291
|
+
this.endTurn(result, turn);
|
|
130292
|
+
input.observer?.complete();
|
|
130293
|
+
input.done?.resolve();
|
|
130294
|
+
}
|
|
130295
|
+
this.next();
|
|
130296
|
+
}
|
|
130297
|
+
endTurn(result, turn) {
|
|
130298
|
+
const sessionId = this.sessionId;
|
|
130299
|
+
const tokens = tokenUsage(result);
|
|
130300
|
+
const cost = tokens ? this.turnCost(turn) : null;
|
|
130301
|
+
if (tokens) this.emit({ type: "usage_update", sessionId, tokens: cost === null ? tokens : { ...tokens, costUsd: cost } });
|
|
130302
|
+
if (result.stopReason === "end_turn" && this.inferredCompaction(turn)) {
|
|
130303
|
+
this.emit({ type: "compaction", sessionId, failed: false, trigger: "manual" });
|
|
130304
|
+
}
|
|
130305
|
+
switch (result.stopReason) {
|
|
130306
|
+
case "cancelled":
|
|
130307
|
+
this.emit({ type: "state_change", sessionId, state: "waiting_input", reason: turn.stopAsked ? "interrupted" : INTERRUPTED_BY_TOOL });
|
|
130308
|
+
return;
|
|
130309
|
+
case "refusal":
|
|
130310
|
+
this.emit({ type: "error", sessionId, error: { code: "internal", message: `${this.deps.label} refused to go on with this turn.`, retryable: false } });
|
|
130311
|
+
return;
|
|
130312
|
+
case "max_tokens":
|
|
130313
|
+
case "max_turn_requests":
|
|
130314
|
+
this.emit({
|
|
130315
|
+
type: "notice",
|
|
130316
|
+
sessionId,
|
|
130317
|
+
level: "warning",
|
|
130318
|
+
from: this.deps.label,
|
|
130319
|
+
text: result.stopReason === "max_tokens" ? `${this.deps.label} stopped: the reply reached its length limit.` : `${this.deps.label} stopped: the turn reached its limit of steps.`
|
|
130320
|
+
});
|
|
130321
|
+
this.emit({ type: "turn_complete", sessionId });
|
|
130322
|
+
return;
|
|
130323
|
+
default:
|
|
130324
|
+
this.emit({ type: "turn_complete", sessionId });
|
|
130325
|
+
}
|
|
130326
|
+
}
|
|
130327
|
+
/**
|
|
130328
|
+
* A command the tool runs may start helpers in a group of their own (Cursor's shell, measured on macOS), which outlive
|
|
130329
|
+
* the tool if it is killed: its tree is noted while a command runs, so they are ended with it (`noteTree`)
|
|
130330
|
+
*/
|
|
130331
|
+
watchCommands(e) {
|
|
130332
|
+
const proc = this.deps.process;
|
|
130333
|
+
if (!proc.noteTree) return;
|
|
130334
|
+
if (e.type === "tool_call" && e.summary.tool === "Bash") {
|
|
130335
|
+
this.commandsRunning.add(e.callId);
|
|
130336
|
+
void proc.noteTree();
|
|
130337
|
+
if (!this.treeClock) {
|
|
130338
|
+
this.treeClock = setInterval(() => void proc.noteTree?.(), TREE_NOTE_MS);
|
|
130339
|
+
this.treeClock.unref?.();
|
|
130340
|
+
}
|
|
130341
|
+
} else if (e.type === "tool_result") {
|
|
130342
|
+
this.commandsRunning.delete(e.callId);
|
|
130343
|
+
if (this.commandsRunning.size === 0) this.stopTreeClock();
|
|
130344
|
+
}
|
|
130345
|
+
}
|
|
130346
|
+
stopTreeClock() {
|
|
130347
|
+
this.commandsRunning.clear();
|
|
130348
|
+
if (this.treeClock) clearInterval(this.treeClock);
|
|
130349
|
+
this.treeClock = null;
|
|
130350
|
+
}
|
|
130351
|
+
/** What this turn added to the conversation's running cost, once a total from before it is known (`CreateSessionOpts.runningCost`); null otherwise */
|
|
130352
|
+
turnCost(turn) {
|
|
130353
|
+
if (turn.costTotal === null) return null;
|
|
130354
|
+
const before = this.costTotal;
|
|
130355
|
+
this.costTotal = turn.costTotal;
|
|
130356
|
+
this.opts.runningCost?.keep(turn.costTotal);
|
|
130357
|
+
return before === null || turn.costTotal < before ? null : turn.costTotal - before;
|
|
130358
|
+
}
|
|
130359
|
+
/** Centralu's own compact turn, with no protocol frame for it, whose reading went down (§5.6) */
|
|
130360
|
+
inferredCompaction(turn) {
|
|
130361
|
+
if (!this.deps.infersCompaction || !turn.input.compact || turn.compacted) return false;
|
|
130362
|
+
return turn.usedBefore !== null && turn.used !== null && turn.used < turn.usedBefore;
|
|
130363
|
+
}
|
|
130364
|
+
interrupt() {
|
|
130365
|
+
const turn = this.turn;
|
|
130366
|
+
if (!turn || this.phase !== "ready") return;
|
|
130367
|
+
turn.stopAsked = true;
|
|
130368
|
+
this.conn.notify("session/cancel", { sessionId: this.externalId });
|
|
130369
|
+
this.closeCards();
|
|
130370
|
+
if (!turn.stopTimer) {
|
|
130371
|
+
turn.stopTimer = setTimeout(() => {
|
|
130372
|
+
if (this.turn === turn) this.emit(stopNotConfirmed(this.sessionId, `${this.deps.label} has not confirmed the stop; the turn may still be running.`));
|
|
130373
|
+
}, this.deps.stopConfirmMs);
|
|
130374
|
+
turn.stopTimer.unref?.();
|
|
130375
|
+
}
|
|
130376
|
+
}
|
|
130377
|
+
onNotification(method, params) {
|
|
130378
|
+
if (method !== "session/update") {
|
|
130379
|
+
this.unmapped.note(method);
|
|
130380
|
+
return;
|
|
130381
|
+
}
|
|
130382
|
+
const read = readUpdate(params);
|
|
130383
|
+
if (!read || read.sessionId !== this.externalId || this.replaying) return;
|
|
130384
|
+
const turn = this.turn;
|
|
130385
|
+
this.resendLostCancel();
|
|
130386
|
+
if ("other" in read) {
|
|
130387
|
+
this.unmapped.note(read.other);
|
|
130388
|
+
return;
|
|
130389
|
+
}
|
|
130390
|
+
const u = read.update;
|
|
130391
|
+
switch (u.sessionUpdate) {
|
|
130392
|
+
case "available_commands_update":
|
|
130393
|
+
this.commands = u.availableCommands.map((c) => ({ name: c.name, ...c.description ? { description: c.description } : {}, ...c.input?.hint ? { argumentHint: c.input.hint } : {} }));
|
|
130394
|
+
return;
|
|
130395
|
+
case "usage_update":
|
|
130396
|
+
if (u.used > 0 && u.size > 0) {
|
|
130397
|
+
this.emit({ type: "context_update", sessionId: this.sessionId, used: u.used, window: u.size, exactness: "exact" });
|
|
130398
|
+
this.lastUsed = u.used;
|
|
130399
|
+
if (turn) turn.used = u.used;
|
|
130400
|
+
}
|
|
130401
|
+
if (turn && u.cost?.currency === "USD") turn.costTotal = u.cost.amount;
|
|
130402
|
+
return;
|
|
130403
|
+
default:
|
|
130404
|
+
for (const e of this.normalizer.update(u)) {
|
|
130405
|
+
this.watchCommands(e);
|
|
130406
|
+
if (e.type !== "compaction") {
|
|
130407
|
+
this.emit(e);
|
|
130408
|
+
continue;
|
|
130409
|
+
}
|
|
130410
|
+
if (turn) turn.compacted = true;
|
|
130411
|
+
this.emit({ ...e, trigger: turn?.input.compact ? "manual" : "auto" });
|
|
130412
|
+
}
|
|
130413
|
+
}
|
|
130414
|
+
}
|
|
130415
|
+
/**
|
|
130416
|
+
* A cancel that reached the agent before its turn had begun is lost (ACP's cancel is a notification, answered by nothing
|
|
130417
|
+
* but the turn's end): the turn going on after it, by an update or by a question, gets the cancel once more
|
|
130418
|
+
*/
|
|
130419
|
+
resendLostCancel() {
|
|
130420
|
+
const turn = this.turn;
|
|
130421
|
+
if (!turn?.stopAsked || turn.cancelResent) return;
|
|
130422
|
+
turn.cancelResent = true;
|
|
130423
|
+
this.conn.notify("session/cancel", { sessionId: this.externalId });
|
|
130424
|
+
}
|
|
130425
|
+
/** Answers a question cancelled, and remembers the call did not run as asked */
|
|
130426
|
+
refused(callId) {
|
|
130427
|
+
this.normalizer.refuse(callId);
|
|
130428
|
+
return CANCELLED;
|
|
130429
|
+
}
|
|
130430
|
+
/** Whether a question asked now belongs to a turn that still runs: none after Stop or after the turn's end */
|
|
130431
|
+
askable(turn) {
|
|
130432
|
+
return this.phase !== "closed" && turn !== null && this.turn === turn && !turn.stopAsked;
|
|
130433
|
+
}
|
|
130434
|
+
async onRequest(_id, method, params) {
|
|
130435
|
+
if (method !== "session/request_permission") {
|
|
130436
|
+
this.unmapped.note(method);
|
|
130437
|
+
throw new RpcError(METHOD_NOT_FOUND2, `Method not found: ${method}`);
|
|
130438
|
+
}
|
|
130439
|
+
const req = RequestPermission.parse(params);
|
|
130440
|
+
const turn = this.turn;
|
|
130441
|
+
this.resendLostCancel();
|
|
130442
|
+
if (!this.askable(turn)) return this.refused(req.toolCall.toolCallId);
|
|
130443
|
+
const call2 = this.normalizer.call(req.toolCall, true);
|
|
130444
|
+
const detail = detailOf(call2, this.opts.cwd);
|
|
130445
|
+
const once = pick3(req.options, "allow_once");
|
|
130446
|
+
const granted = once !== void 0 && await this.grantedWithoutCard(detail, call2);
|
|
130447
|
+
if (!this.askable(turn)) return this.refused(req.toolCall.toolCallId);
|
|
130448
|
+
if (granted && once) return selected(once);
|
|
130449
|
+
const requestId = this.cardIds.next("acp-req");
|
|
130450
|
+
return new Promise((resolve21) => {
|
|
130451
|
+
this.cards.set(requestId, { callId: req.toolCall.toolCallId, options: req.options, answer: resolve21 });
|
|
130452
|
+
this.emit({ type: "approval_request", sessionId: this.sessionId, requestId, detail });
|
|
130453
|
+
});
|
|
130454
|
+
}
|
|
130455
|
+
/** `auto`, Centralu's own tool, an `edits` grant inside the folder, or a rule the person saved: answered `allow_once` without a card */
|
|
130456
|
+
async grantedWithoutCard(detail, call2) {
|
|
130457
|
+
if (this.preset === "auto" || this.deps.centraluToolOf?.(call2)) return true;
|
|
130458
|
+
if (this.preset === "edits" && call2.kind === "edit" && detail.kind === "file_edit") {
|
|
130459
|
+
const paths = editPaths(call2).map((p) => isAbsolute10(p) ? p : join45(this.opts.cwd, p));
|
|
130460
|
+
if (await insideFolder(this.opts.cwd, paths)) return true;
|
|
130461
|
+
}
|
|
130462
|
+
const key = alwaysAllowKey(detail);
|
|
130463
|
+
return key !== null && this.rules.allows(key);
|
|
130464
|
+
}
|
|
130465
|
+
respondApproval(requestId, decision, _scope, matcher2) {
|
|
130466
|
+
const card = this.cards.take(requestId);
|
|
130467
|
+
if (!card) return false;
|
|
130468
|
+
if (decision === "always" && matcher2) this.rules.add(matcher2);
|
|
130469
|
+
const option = pick3(card.options, decision === "deny" ? "reject_once" : "allow_once");
|
|
130470
|
+
if (decision === "deny" || !option) this.normalizer.refuse(card.callId);
|
|
130471
|
+
card.answer(option ? selected(option) : CANCELLED);
|
|
130472
|
+
this.emit({ type: "approval_resolved", sessionId: this.sessionId, requestId, decision });
|
|
130473
|
+
return true;
|
|
130474
|
+
}
|
|
130475
|
+
applyRules(matchers) {
|
|
130476
|
+
this.rules.addAll(matchers);
|
|
130477
|
+
}
|
|
130478
|
+
setPermissionPreset(preset) {
|
|
130479
|
+
this.preset = preset;
|
|
130480
|
+
}
|
|
130481
|
+
async listCommands() {
|
|
130482
|
+
return this.commands;
|
|
130483
|
+
}
|
|
130484
|
+
closeCards() {
|
|
130485
|
+
this.cards.release((requestId, card) => {
|
|
130486
|
+
this.normalizer.refuse(card.callId);
|
|
130487
|
+
card.answer(CANCELLED);
|
|
130488
|
+
this.emit({ type: "approval_resolved", sessionId: this.sessionId, requestId, decision: "deny" });
|
|
130489
|
+
});
|
|
130490
|
+
}
|
|
130491
|
+
onExit(code2, expected) {
|
|
130492
|
+
if (expected || this.phase === "closed") return;
|
|
130493
|
+
const wasOpen = this.phase === "ready";
|
|
130494
|
+
this.phase = "closed";
|
|
130495
|
+
this.closeCards();
|
|
130496
|
+
const turn = this.turn;
|
|
130497
|
+
this.turn = null;
|
|
130498
|
+
if (turn?.stopTimer) clearTimeout(turn.stopTimer);
|
|
130499
|
+
this.dropQueue("were still queued when the agent exited");
|
|
130500
|
+
const failure3 = new Error(`${this.deps.label} exited${code2 === null ? "" : ` (code ${code2})`}`);
|
|
130501
|
+
if (turn) {
|
|
130502
|
+
for (const e of this.normalizer.endTurn()) this.emit(e);
|
|
130503
|
+
this.stopTreeClock();
|
|
130504
|
+
turn.input.observer?.fail(failure3);
|
|
130505
|
+
turn.input.done?.reject(failure3);
|
|
130506
|
+
}
|
|
130507
|
+
if (turn || !wasOpen) {
|
|
130508
|
+
const tail = this.conn.stderrTail().trim().split("\n").slice(-5).join("\n");
|
|
130509
|
+
this.emit({ type: "error", sessionId: this.sessionId, error: { code: "adapter_crashed", message: tail ? `${failure3.message}: ${tail}` : failure3.message, retryable: true } });
|
|
130510
|
+
} else {
|
|
130511
|
+
this.emit({ type: "session_live", sessionId: this.sessionId, live: false, reason: "asleep" });
|
|
130512
|
+
}
|
|
130513
|
+
}
|
|
130514
|
+
dropQueue(when) {
|
|
130515
|
+
const left = this.queue.splice(0);
|
|
130516
|
+
if (left.length === 0) return;
|
|
130517
|
+
const e = new Error(`${left.length} message(s) ${when}`);
|
|
130518
|
+
for (const i of left) {
|
|
130519
|
+
i.observer?.fail(e);
|
|
130520
|
+
i.done?.reject(e);
|
|
130521
|
+
}
|
|
130522
|
+
this.emit(undeliveredError(this.sessionId, left.length, when));
|
|
130523
|
+
}
|
|
130524
|
+
async dispose() {
|
|
130525
|
+
if (this.phase === "closed") return;
|
|
130526
|
+
const turn = this.turn;
|
|
130527
|
+
this.phase = "closed";
|
|
130528
|
+
this.closeCards();
|
|
130529
|
+
this.dropQueue("were still queued when the session closed");
|
|
130530
|
+
if (turn) {
|
|
130531
|
+
for (const e of this.normalizer.endTurn("Centralu stopped this session while the call ran")) this.emit(e);
|
|
130532
|
+
this.stopTreeClock();
|
|
130533
|
+
}
|
|
130534
|
+
if (turn && this.externalId) {
|
|
130535
|
+
if (turn.stopTimer) clearTimeout(turn.stopTimer);
|
|
130536
|
+
this.conn.notify("session/cancel", { sessionId: this.externalId });
|
|
130537
|
+
turn.input.observer?.fail(new Error("the session closed"));
|
|
130538
|
+
turn.input.done?.reject(new Error("the session closed"));
|
|
130539
|
+
}
|
|
130540
|
+
if (this.canClose && this.externalId && !this.conn.isClosed) {
|
|
130541
|
+
await this.conn.request("session/close", { sessionId: this.externalId }, 2e3).catch(() => {
|
|
130542
|
+
});
|
|
130543
|
+
}
|
|
130544
|
+
await this.conn.dispose();
|
|
130545
|
+
}
|
|
130546
|
+
};
|
|
130547
|
+
function pick3(options, kind) {
|
|
130548
|
+
return options.find((o) => o.kind === kind);
|
|
130549
|
+
}
|
|
130550
|
+
function selected(option) {
|
|
130551
|
+
return { outcome: { outcome: "selected", optionId: option.optionId } };
|
|
130552
|
+
}
|
|
130553
|
+
var str2 = (v) => typeof v === "string" ? v : "";
|
|
130554
|
+
function editPaths(call2) {
|
|
130555
|
+
const input = asObj(call2.rawInput);
|
|
130556
|
+
const fromDiffs = (call2.content ?? []).flatMap((c) => c.type === "diff" && c.path ? [c.path] : []);
|
|
130557
|
+
const named = [str2(input.filePath), str2(input.path), str2(input.file_path)].filter(Boolean);
|
|
130558
|
+
return [.../* @__PURE__ */ new Set([...fromDiffs, ...(call2.locations ?? []).map((l) => l.path), ...named])];
|
|
130559
|
+
}
|
|
130560
|
+
function detailOf(call2, cwd) {
|
|
130561
|
+
const input = asObj(call2.rawInput);
|
|
130562
|
+
if (call2.kind === "execute") return { kind: "command", command: str2(input.command) || call2.title || "", cwd: str2(input.cwd) || cwd };
|
|
130563
|
+
if (call2.kind === "edit" || call2.kind === "delete" || call2.kind === "move") {
|
|
130564
|
+
const diffs = (call2.content ?? []).flatMap((c) => c.type === "diff" && c.path ? [{ path: c.path, diff: c.newText ?? "" }] : []);
|
|
130565
|
+
const changes = diffs.length ? diffs : editPaths(call2).map((path2) => ({ path: path2, diff: str2(input.content) || str2(input.newString) || str2(input.new_string) }));
|
|
130566
|
+
return fileEditDetail(call2.kind === "delete" ? changes.map((c) => ({ ...c, change: "delete" })) : changes);
|
|
130567
|
+
}
|
|
130568
|
+
return { kind: "other", raw: JSON.stringify({ title: call2.title, kind: call2.kind, input: call2.rawInput }).slice(0, 2e3) };
|
|
130569
|
+
}
|
|
130570
|
+
|
|
130571
|
+
// packages/agent-host/src/adapters/acp/adapter.ts
|
|
130572
|
+
var STOP_CONFIRM_MS2 = 1e4;
|
|
130573
|
+
var AcpAdapter = class {
|
|
130574
|
+
constructor(profile, spawn6 = spawnLocalAgent, stopConfirmMs = STOP_CONFIRM_MS2) {
|
|
130575
|
+
this.profile = profile;
|
|
130576
|
+
this.spawn = spawn6;
|
|
130577
|
+
this.stopConfirmMs = stopConfirmMs;
|
|
130578
|
+
}
|
|
130579
|
+
profile;
|
|
130580
|
+
spawn;
|
|
130581
|
+
stopConfirmMs;
|
|
130582
|
+
get descriptor() {
|
|
130583
|
+
return this.profile.descriptor;
|
|
130584
|
+
}
|
|
130585
|
+
get capabilities() {
|
|
130586
|
+
return this.profile.capabilities;
|
|
130587
|
+
}
|
|
130588
|
+
get surface() {
|
|
130589
|
+
return this.profile.surface;
|
|
130590
|
+
}
|
|
130591
|
+
detect() {
|
|
130592
|
+
return this.profile.detect();
|
|
130593
|
+
}
|
|
130594
|
+
installedVersion() {
|
|
130595
|
+
return this.profile.installedVersion();
|
|
130596
|
+
}
|
|
130597
|
+
async createSession(opts, emit) {
|
|
130598
|
+
const label = this.profile.descriptor.label;
|
|
130599
|
+
const launch2 = await this.profile.launch(opts);
|
|
130600
|
+
if (!launch2) throw rpcError(`${label} is not installed. ${this.profile.descriptor.install}`, "tool_not_installed");
|
|
130601
|
+
const recorder = frameRecorder(opts.sessionId);
|
|
130602
|
+
const spec = { command: launch2.command, args: launch2.args, cwd: opts.cwd, env: { ...process.env, ...launch2.env } };
|
|
130603
|
+
const source = opts.processSource;
|
|
130604
|
+
if (source?.adopt) await closeAdopted(source.adopt, opts.sessionId, emit);
|
|
130605
|
+
const proc = source ? source.spawn(spec) : this.spawn({ ...spec, endsWithHost: true });
|
|
130606
|
+
if (recorder) proc.once("exit", (code2, signal) => recorder.exit(code2, signal));
|
|
130607
|
+
const session2 = new AcpSession(opts, emit, {
|
|
130608
|
+
label,
|
|
130609
|
+
process: proc,
|
|
130610
|
+
version: () => this.profile.installedVersion(),
|
|
130611
|
+
stopConfirmMs: this.stopConfirmMs,
|
|
130612
|
+
...this.profile.centraluToolOf ? { centraluToolOf: (call2) => this.profile.centraluToolOf?.(call2) ?? null } : {},
|
|
130613
|
+
...this.profile.infersCompaction ? { infersCompaction: true } : {},
|
|
130614
|
+
...recorder ? { onFrame: recorder.frame } : {}
|
|
130615
|
+
});
|
|
130616
|
+
try {
|
|
130617
|
+
await session2.ready;
|
|
130618
|
+
} catch (err) {
|
|
130619
|
+
await session2.dispose().catch(() => {
|
|
130620
|
+
});
|
|
130621
|
+
throw err;
|
|
130622
|
+
}
|
|
130623
|
+
return session2;
|
|
130624
|
+
}
|
|
130625
|
+
};
|
|
130626
|
+
var ADOPTED_EXIT_MS = 5e3;
|
|
130627
|
+
async function closeAdopted(adopt, sessionId, emit) {
|
|
130628
|
+
adopt.process.kill("SIGKILL");
|
|
130629
|
+
await waitForExit(adopt.process, ADOPTED_EXIT_MS);
|
|
130630
|
+
for (const call2 of adopt.openCalls) emit(toolResultEvent(sessionId, call2.callId, false, "", "Centralu restarted while this call ran; it was stopped"));
|
|
130631
|
+
}
|
|
130632
|
+
|
|
130633
|
+
// packages/agent-host/src/adapters/cursor-agent/install.ts
|
|
130634
|
+
import { existsSync as existsSync19, readdirSync as readdirSync11, realpathSync as realpathSync10 } from "node:fs";
|
|
130635
|
+
import { dirname as dirname22, join as join46, win32 as win327 } from "node:path";
|
|
130636
|
+
var realFs = {
|
|
130637
|
+
exists: existsSync19,
|
|
130638
|
+
list: (dir) => {
|
|
130639
|
+
try {
|
|
130640
|
+
return readdirSync11(dir);
|
|
130641
|
+
} catch {
|
|
130642
|
+
return [];
|
|
130643
|
+
}
|
|
130644
|
+
},
|
|
130645
|
+
real: (p) => {
|
|
130646
|
+
try {
|
|
130647
|
+
return realpathSync10(p);
|
|
130648
|
+
} catch {
|
|
130649
|
+
return p;
|
|
130650
|
+
}
|
|
130651
|
+
}
|
|
130652
|
+
};
|
|
130653
|
+
var VERSION = /^(\d{4})\.(\d{1,2})\.(\d{1,2})(?:-(\d{2})-(\d{2})-(\d{2}))?-[a-f0-9]+$/;
|
|
130654
|
+
function newestVersion(names) {
|
|
130655
|
+
const key = (n) => {
|
|
130656
|
+
const m = VERSION.exec(n);
|
|
130657
|
+
if (!m) return null;
|
|
130658
|
+
const pad = (s) => (s ?? "00").padStart(2, "0");
|
|
130659
|
+
return `${m[1]}${pad(m[2])}${pad(m[3])}${pad(m[4])}${pad(m[5])}${pad(m[6])}`;
|
|
130660
|
+
};
|
|
130661
|
+
const dated = names.flatMap((n) => {
|
|
130662
|
+
const k2 = key(n);
|
|
130663
|
+
return k2 ? [{ n, k: k2 }] : [];
|
|
130664
|
+
});
|
|
130665
|
+
dated.sort((a, b2) => a.k < b2.k ? 1 : a.k > b2.k ? -1 : 0);
|
|
130666
|
+
return dated[0]?.n ?? null;
|
|
130667
|
+
}
|
|
130668
|
+
function findCursor(env = process.env, platform, fs2 = realFs) {
|
|
130669
|
+
if (installsCliAsVersionFolders(platform)) {
|
|
130670
|
+
const roots = [whichTool("agent", env, platform), whichTool("cursor-agent", env, platform)].flatMap((p) => p ? [win327.dirname(p)] : []).concat(env.LOCALAPPDATA ? [win327.join(env.LOCALAPPDATA, "cursor-agent")] : []);
|
|
130671
|
+
for (const root3 of roots) {
|
|
130672
|
+
const version2 = newestVersion(fs2.list(win327.join(root3, "versions")));
|
|
130673
|
+
if (!version2) continue;
|
|
130674
|
+
const dir = win327.join(root3, "versions", version2);
|
|
130675
|
+
const node = win327.join(dir, "node.exe");
|
|
130676
|
+
const entry = win327.join(dir, "index.js");
|
|
130677
|
+
if (fs2.exists(node) && fs2.exists(entry)) return { version: version2, command: node, args: [entry], env: { CURSOR_INVOKED_AS: "agent.cmd" } };
|
|
130678
|
+
}
|
|
130679
|
+
return null;
|
|
130680
|
+
}
|
|
130681
|
+
for (const name of ["cursor-agent", "agent"]) {
|
|
130682
|
+
const found = whichTool(name, env, platform);
|
|
130683
|
+
if (!found) continue;
|
|
130684
|
+
const real3 = fs2.real(found);
|
|
130685
|
+
const dir = dirname22(real3);
|
|
130686
|
+
const version2 = dir.split(/[\\/]/).pop() ?? "";
|
|
130687
|
+
if (VERSION.test(version2) && fs2.exists(join46(dir, "index.js"))) return { version: version2, command: real3, args: [], env: {} };
|
|
130688
|
+
}
|
|
130689
|
+
return null;
|
|
130690
|
+
}
|
|
130691
|
+
|
|
130692
|
+
// packages/agent-host/src/adapters/cursor-agent/surface.ts
|
|
130693
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
130694
|
+
import { readdirSync as readdirSync12, readFileSync as readFileSync16 } from "node:fs";
|
|
130695
|
+
import { dirname as dirname23, join as join47 } from "node:path";
|
|
130696
|
+
import { promisify as promisify7 } from "node:util";
|
|
130697
|
+
|
|
130698
|
+
// packages/agent-host/src/adapters/cursor-agent/surface-baseline.ts
|
|
130699
|
+
var SURFACE_BASELINE3 = {
|
|
130700
|
+
recorded: "2026-10-26",
|
|
130701
|
+
version: "agent 2026.10.22-27dcf5f",
|
|
130702
|
+
offered: {
|
|
130703
|
+
command: [
|
|
130704
|
+
"about",
|
|
130705
|
+
"agent",
|
|
130706
|
+
"bedrock",
|
|
130707
|
+
"create-chat",
|
|
130708
|
+
"cursor/ask_question",
|
|
130709
|
+
"cursor/create_plan",
|
|
130710
|
+
"cursor/generate_image",
|
|
130711
|
+
"cursor/list_available_models",
|
|
130712
|
+
"cursor/task",
|
|
130713
|
+
"cursor/update_todos",
|
|
130714
|
+
"help",
|
|
130715
|
+
"login",
|
|
130716
|
+
"logout",
|
|
130717
|
+
"ls",
|
|
130718
|
+
"mcp",
|
|
130719
|
+
"models",
|
|
130720
|
+
"persist",
|
|
130721
|
+
"plugin",
|
|
130722
|
+
"prompt",
|
|
130723
|
+
"resume",
|
|
130724
|
+
"team",
|
|
130725
|
+
"update",
|
|
130726
|
+
"worker"
|
|
130727
|
+
],
|
|
130728
|
+
message: [
|
|
130729
|
+
"agent_message_chunk",
|
|
130730
|
+
"agent_thought_chunk",
|
|
130731
|
+
"available_commands_update",
|
|
130732
|
+
"current_mode_update",
|
|
130733
|
+
"plan",
|
|
130734
|
+
"session_info_update",
|
|
130735
|
+
"subagent_spawned",
|
|
130736
|
+
"subagent_state_update",
|
|
130737
|
+
"tool_call",
|
|
130738
|
+
"tool_call_update",
|
|
130739
|
+
"user_message_chunk"
|
|
130740
|
+
],
|
|
130741
|
+
capability: [
|
|
130742
|
+
"authMethods.cursor_login",
|
|
130743
|
+
"loadSession",
|
|
130744
|
+
"mcpCapabilities.http",
|
|
130745
|
+
"mcpCapabilities.sse",
|
|
130746
|
+
"promptCapabilities.audio",
|
|
130747
|
+
"promptCapabilities.embeddedContext",
|
|
130748
|
+
"promptCapabilities.image",
|
|
130749
|
+
"sessionCapabilities.list"
|
|
130750
|
+
]
|
|
130751
|
+
}
|
|
130752
|
+
};
|
|
130753
|
+
|
|
130754
|
+
// packages/agent-host/src/adapters/cursor-agent/surface.ts
|
|
130755
|
+
var exec6 = promisify7(execFile8);
|
|
130756
|
+
var Initialized = external_exports.looseObject({ agentCapabilities: external_exports.unknown().optional(), authMethods: external_exports.array(external_exports.looseObject({ id: external_exports.string() })).optional() });
|
|
130757
|
+
async function gather() {
|
|
130758
|
+
const install = findCursor();
|
|
130759
|
+
if (!install) throw new Error("Cursor CLI is not installed on this machine (https://cursor.com/install)");
|
|
130760
|
+
const env = { ...process.env, ...install.env };
|
|
130761
|
+
const { stdout: help } = await exec6(install.command, [...install.args, "--help"], { timeout: 3e4, windowsHide: true, env });
|
|
130762
|
+
const proc = spawnLocalAgent({ command: install.command, args: [...install.args, "acp"], cwd: process.cwd(), env });
|
|
130763
|
+
const conn = new AcpConnection(proc, { onNotification: () => {
|
|
130764
|
+
}, onRequest: async () => null, onExit: () => {
|
|
130765
|
+
} }, "cursor-surface");
|
|
130766
|
+
let init;
|
|
130767
|
+
try {
|
|
130768
|
+
init = await conn.request("initialize", { protocolVersion: 1, clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false }, clientInfo: { name: "centralu-surface", version: "1" } }, 6e4);
|
|
130769
|
+
} finally {
|
|
130770
|
+
await conn.dispose();
|
|
130771
|
+
}
|
|
130772
|
+
const dir = dirname23(install.args[0] ?? install.command);
|
|
130773
|
+
const names = /* @__PURE__ */ new Set();
|
|
130774
|
+
for (const f of readdirSync12(dir).filter((n) => n.endsWith(".js"))) {
|
|
130775
|
+
const text2 = readFileSync16(join47(dir, f), "utf8");
|
|
130776
|
+
if (!text2.includes("session/request_permission") && !text2.includes("cursor_login")) continue;
|
|
130777
|
+
for (const m of text2.matchAll(/sessionUpdate:"([a-z_]+)"|"(cursor\/[a-z_]+)"/g)) {
|
|
130778
|
+
const name = m[1] ?? m[2];
|
|
130779
|
+
if (name) names.add(name);
|
|
130780
|
+
}
|
|
130781
|
+
}
|
|
130782
|
+
return { version: install.version, help, initialize: JSON.stringify(init), bundle: JSON.stringify([...names].sort()) };
|
|
130783
|
+
}
|
|
130784
|
+
function capabilityNames(value, prefix = "") {
|
|
130785
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return prefix ? [prefix] : [];
|
|
130786
|
+
const entries = Object.entries(asObj(value));
|
|
130787
|
+
if (entries.length === 0) return prefix ? [prefix] : [];
|
|
130788
|
+
return entries.flatMap(([k2, v]) => typeof v === "object" && v !== null ? capabilityNames(v, prefix ? `${prefix}.${k2}` : k2) : [prefix ? `${prefix}.${k2}` : k2]);
|
|
130789
|
+
}
|
|
130790
|
+
function readCursorSurface(inputs) {
|
|
130791
|
+
const init = Initialized.parse(JSON.parse(inputs.initialize ?? "{}"));
|
|
130792
|
+
const commands = [...(inputs.help ?? "").matchAll(/^ {2}([a-z][\w-]*)(?: |$)/gm)].flatMap((m) => m[1] ? [m[1]] : []);
|
|
130793
|
+
const bundle = external_exports.array(external_exports.string()).parse(JSON.parse(inputs.bundle ?? "[]"));
|
|
130794
|
+
return {
|
|
130795
|
+
version: `agent ${inputs.version ?? "unknown"}`,
|
|
130796
|
+
offered: {
|
|
130797
|
+
command: [.../* @__PURE__ */ new Set([...commands, ...bundle.filter((n) => n.startsWith("cursor/"))])].sort(),
|
|
130798
|
+
message: bundle.filter((n) => !n.startsWith("cursor/")).sort(),
|
|
130799
|
+
capability: [...capabilityNames(init.agentCapabilities), ...(init.authMethods ?? []).map((a) => `authMethods.${a.id}`)].sort()
|
|
130800
|
+
}
|
|
130801
|
+
};
|
|
130802
|
+
}
|
|
130803
|
+
var BUNDLE = "read in the 2026.10.22-27dcf5f bundle (3635.index.js), 2026-10-26";
|
|
130804
|
+
var CURSOR_SURFACE = {
|
|
130805
|
+
gather,
|
|
130806
|
+
read: readCursorSurface,
|
|
130807
|
+
limitations: [
|
|
130808
|
+
{
|
|
130809
|
+
kind: "capability",
|
|
130810
|
+
name: "authMethods.cursor_login",
|
|
130811
|
+
treatment: "blocked",
|
|
130812
|
+
measured: "agent 2026.10.22-27dcf5f, not logged in: authenticate {methodId: cursor_login} started the browser login flow and did not answer in 8 s",
|
|
130813
|
+
offeredWhenMeasured: true,
|
|
130814
|
+
note: "Never called: a background session must not open a browser. Logging in stays `agent login` in a terminal."
|
|
130815
|
+
},
|
|
130816
|
+
{
|
|
130817
|
+
kind: "message",
|
|
130818
|
+
name: "current_mode_update",
|
|
130819
|
+
treatment: "dropped",
|
|
130820
|
+
measured: BUNDLE,
|
|
130821
|
+
offeredWhenMeasured: true,
|
|
130822
|
+
note: "Modes (agent, plan, ask) are not offered until the settings menu maps them (PR 2)."
|
|
130823
|
+
},
|
|
130824
|
+
{
|
|
130825
|
+
kind: "message",
|
|
130826
|
+
name: "subagent_spawned",
|
|
130827
|
+
treatment: "dropped",
|
|
130828
|
+
measured: `${BUNDLE}: sent only when the client advertises sub-agent support, which Centralu does not yet`,
|
|
130829
|
+
offeredWhenMeasured: true,
|
|
130830
|
+
note: "Sub-agents run as ordinary tool calls until they are mapped onto subagent_event (PR 2)."
|
|
130831
|
+
},
|
|
130832
|
+
{
|
|
130833
|
+
kind: "message",
|
|
130834
|
+
name: "subagent_state_update",
|
|
130835
|
+
treatment: "dropped",
|
|
130836
|
+
measured: `${BUNDLE}: as subagent_spawned`,
|
|
130837
|
+
offeredWhenMeasured: true,
|
|
130838
|
+
note: "As subagent_spawned."
|
|
130839
|
+
},
|
|
130840
|
+
...["cursor/ask_question", "cursor/create_plan", "cursor/update_todos", "cursor/task", "cursor/generate_image"].map((name) => ({
|
|
130841
|
+
kind: "command",
|
|
130842
|
+
name,
|
|
130843
|
+
treatment: "blocked",
|
|
130844
|
+
measured: `${BUNDLE}: Cursor's own request to the client`,
|
|
130845
|
+
offeredWhenMeasured: true,
|
|
130846
|
+
note: name === "cursor/ask_question" ? "Answered method-not-found, so Cursor asks each question as a session/request_permission whose options are the answers (its own fallback). Question cards come in PR 2." : "Answered method-not-found; mapped in PR 2."
|
|
130847
|
+
})),
|
|
130848
|
+
{
|
|
130849
|
+
kind: "command",
|
|
130850
|
+
name: "cursor/list_available_models",
|
|
130851
|
+
treatment: "dropped",
|
|
130852
|
+
measured: "agent 2026.10.22-27dcf5f, logged in: answers every model with its options (effort as category thought_level, context and fast as model_config)",
|
|
130853
|
+
offeredWhenMeasured: true,
|
|
130854
|
+
note: "The model list comes in PR 2. Setting a model through session/set_config_option also writes it to ~/.cursor/acp-config.json (measured), which would change the default of every Cursor session the person runs, so PR 1 runs Cursor\u2019s default model."
|
|
130855
|
+
}
|
|
130856
|
+
],
|
|
130857
|
+
baseline: SURFACE_BASELINE3
|
|
130858
|
+
};
|
|
130859
|
+
|
|
130860
|
+
// packages/agent-host/src/adapters/cursor-agent/index.ts
|
|
130861
|
+
var exec7 = promisify8(execFile9);
|
|
130862
|
+
var CAPABILITIES = {
|
|
130863
|
+
approvals: true,
|
|
130864
|
+
contextUsage: "none",
|
|
130865
|
+
resume: true,
|
|
130866
|
+
autoTitle: true,
|
|
130867
|
+
attachments: [],
|
|
130868
|
+
verbosities: [],
|
|
130869
|
+
exclusiveWriter: false,
|
|
130870
|
+
backgroundTasks: false,
|
|
130871
|
+
midTurnInput: false,
|
|
130872
|
+
gatesSteps: false,
|
|
130873
|
+
toolDeferral: false,
|
|
130874
|
+
instructionChannel: "server",
|
|
130875
|
+
processModel: "per-session",
|
|
130876
|
+
builtInSubagents: false,
|
|
130877
|
+
peerMessaging: false,
|
|
130878
|
+
sleep: false,
|
|
130879
|
+
presets: ["auto"],
|
|
130880
|
+
keepsRepoFilesOut: false,
|
|
130881
|
+
roleText: false
|
|
130882
|
+
};
|
|
130883
|
+
var DESCRIPTOR = {
|
|
130884
|
+
name: "cursor-agent",
|
|
130885
|
+
label: "Cursor",
|
|
130886
|
+
mark: "\u25B2",
|
|
130887
|
+
install: "curl https://cursor.com/install -fsS | bash (Windows: irm 'https://cursor.com/install?win32=true' | iex)",
|
|
130888
|
+
login: "agent login"
|
|
130889
|
+
};
|
|
130890
|
+
function centraluToolOfCall(call2) {
|
|
130891
|
+
if (call2.kind !== "other") return null;
|
|
130892
|
+
const m = /^([\w-]+): ([\w-]+)$/.exec(call2.title ?? "");
|
|
130893
|
+
return m?.[2] && m[1] === `${ORCHESTRATOR_MCP_NAME}-${m[2]}` ? m[2] : null;
|
|
130894
|
+
}
|
|
130895
|
+
var STATUS_FOR_MS = 6e4;
|
|
130896
|
+
function cursorProfile() {
|
|
130897
|
+
let status = null;
|
|
130898
|
+
return {
|
|
130899
|
+
descriptor: DESCRIPTOR,
|
|
130900
|
+
capabilities: CAPABILITIES,
|
|
130901
|
+
surface: CURSOR_SURFACE,
|
|
130902
|
+
async launch() {
|
|
130903
|
+
const install = findCursor();
|
|
130904
|
+
return install ? { command: install.command, args: [...install.args, "acp"], env: install.env } : null;
|
|
130905
|
+
},
|
|
130906
|
+
async installedVersion() {
|
|
130907
|
+
return findCursor()?.version ?? null;
|
|
130908
|
+
},
|
|
130909
|
+
centraluToolOf: centraluToolOfCall,
|
|
130910
|
+
async detect() {
|
|
130911
|
+
if (status && Date.now() - status.at < STATUS_FOR_MS) return status.result;
|
|
130912
|
+
const install = findCursor();
|
|
130913
|
+
let result;
|
|
130914
|
+
if (!install) {
|
|
130915
|
+
result = { tool: DESCRIPTOR.name, installed: false, loggedIn: false, detail: "Cursor CLI not found (install it from https://cursor.com/cli)" };
|
|
130916
|
+
} else {
|
|
130917
|
+
const out = await exec7(install.command, [...install.args, "status"], { timeout: 2e4, windowsHide: true, env: { ...process.env, ...install.env } }).then((r) => r.stdout, (e) => e.stdout ?? "");
|
|
130918
|
+
const loggedIn = /\bLogged in\b/.test(out) && !/Not logged in/i.test(out);
|
|
130919
|
+
result = { tool: DESCRIPTOR.name, installed: true, loggedIn, detail: loggedIn ? `agent ${install.version}` : `agent ${install.version} \xB7 login required` };
|
|
130920
|
+
}
|
|
130921
|
+
status = { at: Date.now(), result };
|
|
130922
|
+
return result;
|
|
130923
|
+
}
|
|
130924
|
+
};
|
|
130925
|
+
}
|
|
130926
|
+
var CursorAgentAdapter = class extends AcpAdapter {
|
|
130927
|
+
tool = "cursor-agent";
|
|
130928
|
+
constructor() {
|
|
130929
|
+
super(cursorProfile());
|
|
130930
|
+
}
|
|
130931
|
+
};
|
|
130932
|
+
|
|
130933
|
+
// packages/agent-host/src/adapters/opencode/index.ts
|
|
130934
|
+
import { execFile as execFile11 } from "node:child_process";
|
|
130935
|
+
import { promisify as promisify10 } from "node:util";
|
|
130936
|
+
|
|
130937
|
+
// packages/agent-host/src/adapters/opencode/config-files.ts
|
|
130938
|
+
import { existsSync as existsSync20, readdirSync as readdirSync13, readFileSync as readFileSync17 } from "node:fs";
|
|
130939
|
+
import { homedir as homedir13 } from "node:os";
|
|
130940
|
+
import { dirname as dirname24, extname as extname3, join as join48, relative as relative7 } from "node:path";
|
|
130941
|
+
function readOpenCodeConfigs(cwd, env, home = homedir13()) {
|
|
130942
|
+
const global2 = join48(env.XDG_CONFIG_HOME || join48(home, ".config"), "opencode");
|
|
130943
|
+
const project2 = !env.OPENCODE_DISABLE_PROJECT_CONFIG;
|
|
130944
|
+
const upward = project2 ? foldersUp(cwd) : [];
|
|
130945
|
+
const dotFolders = [...upward.map((d2) => join48(d2, ".opencode")), join48(home, ".opencode"), ...env.OPENCODE_CONFIG_DIR ? [env.OPENCODE_CONFIG_DIR] : []];
|
|
130946
|
+
const files = [
|
|
130947
|
+
...["config.json", "opencode.json", "opencode.jsonc"].map((f) => join48(global2, f)),
|
|
130948
|
+
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : [],
|
|
130949
|
+
...upward.flatMap((d2) => CONFIG_NAMES.map((f) => join48(d2, f))),
|
|
130950
|
+
...dotFolders.flatMap((d2) => CONFIG_NAMES.map((f) => join48(d2, f))),
|
|
130951
|
+
...CONFIG_NAMES.map((f) => join48(managedFolder(env), f))
|
|
130952
|
+
];
|
|
130953
|
+
return {
|
|
130954
|
+
layers: files.map(readConfig).filter((c) => c !== null),
|
|
130955
|
+
agentFiles: [...new Set([global2, ...dotFolders].flatMap(agentNames))]
|
|
130956
|
+
};
|
|
130957
|
+
}
|
|
130958
|
+
var CONFIG_NAMES = ["opencode.json", "opencode.jsonc"];
|
|
130959
|
+
function foldersUp(cwd) {
|
|
130960
|
+
const out = [];
|
|
130961
|
+
for (let d2 = cwd; ; d2 = dirname24(d2)) {
|
|
130962
|
+
out.push(d2);
|
|
130963
|
+
if (existsSync20(join48(d2, ".git")) || dirname24(d2) === d2) return out;
|
|
130964
|
+
}
|
|
130965
|
+
}
|
|
130966
|
+
function managedFolder(env) {
|
|
130967
|
+
if (env.OPENCODE_TEST_MANAGED_CONFIG_DIR) return env.OPENCODE_TEST_MANAGED_CONFIG_DIR;
|
|
130968
|
+
return systemSettingsFolder("opencode", env);
|
|
130969
|
+
}
|
|
130970
|
+
function readConfig(path2) {
|
|
130971
|
+
try {
|
|
130972
|
+
const v = JSON.parse(withoutComments(readFileSync17(path2, "utf8")));
|
|
130973
|
+
return isObj(v) ? v : null;
|
|
130974
|
+
} catch {
|
|
130975
|
+
return null;
|
|
130976
|
+
}
|
|
130977
|
+
}
|
|
130978
|
+
function withoutComments(text2) {
|
|
130979
|
+
let out = "";
|
|
130980
|
+
for (let i = 0; i < text2.length; i++) {
|
|
130981
|
+
const c = text2[i];
|
|
130982
|
+
if (c === '"') {
|
|
130983
|
+
const end = stringEnd(text2, i);
|
|
130984
|
+
out += text2.slice(i, end);
|
|
130985
|
+
i = end - 1;
|
|
130986
|
+
} else if (c === "/" && text2[i + 1] === "/") {
|
|
130987
|
+
while (i < text2.length && text2[i] !== "\n") i++;
|
|
130988
|
+
out += "\n";
|
|
130989
|
+
} else if (c === "/" && text2[i + 1] === "*") {
|
|
130990
|
+
i = text2.indexOf("*/", i + 2);
|
|
130991
|
+
if (i === -1) break;
|
|
130992
|
+
i++;
|
|
130993
|
+
} else out += c;
|
|
130994
|
+
}
|
|
130995
|
+
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
130996
|
+
}
|
|
130997
|
+
function stringEnd(text2, start2) {
|
|
130998
|
+
for (let i = start2 + 1; i < text2.length; i++) {
|
|
130999
|
+
if (text2[i] === "\\") i++;
|
|
131000
|
+
else if (text2[i] === '"') return i + 1;
|
|
131001
|
+
}
|
|
131002
|
+
return text2.length;
|
|
131003
|
+
}
|
|
131004
|
+
function agentNames(folder) {
|
|
131005
|
+
const named = (sub, deep) => markdown(join48(folder, sub), deep).map((f) => relative7(join48(folder, sub), f).replaceAll("\\", "/").slice(0, -extname3(f).length));
|
|
131006
|
+
return [...named("agent", true), ...named("agents", true), ...named("mode", false), ...named("modes", false)];
|
|
131007
|
+
}
|
|
131008
|
+
function markdown(folder, deep) {
|
|
131009
|
+
try {
|
|
131010
|
+
return readdirSync13(folder, { withFileTypes: true }).flatMap((e) => {
|
|
131011
|
+
const path2 = join48(folder, e.name);
|
|
131012
|
+
if (e.isDirectory()) return deep ? markdown(path2, deep) : [];
|
|
131013
|
+
return e.name.endsWith(".md") ? [path2] : [];
|
|
131014
|
+
});
|
|
131015
|
+
} catch {
|
|
131016
|
+
return [];
|
|
131017
|
+
}
|
|
131018
|
+
}
|
|
131019
|
+
|
|
131020
|
+
// packages/agent-host/src/adapters/opencode/config.ts
|
|
131021
|
+
function presetEnv(preset, env, configs, platform = HOST_PLATFORM) {
|
|
131022
|
+
if (preset === "auto") return {};
|
|
131023
|
+
const theirs = jsonObject(env.OPENCODE_CONFIG_CONTENT ?? "");
|
|
131024
|
+
const all = [...configs.layers, theirs, ...env.OPENCODE_PERMISSION === void 0 ? [] : [{ permission: jsonObject(env.OPENCODE_PERMISSION) }]];
|
|
131025
|
+
return holdEnv(preset === "safe" ? "safe" : "normal", theirs, all, configs.agentFiles, env.OPENCODE_PERMISSION, matcher(platform));
|
|
131026
|
+
}
|
|
131027
|
+
var NORMAL_ASKS = ["edit", "bash"];
|
|
131028
|
+
var SAFE_ASKS = ["edit", "bash", "webfetch", "websearch"];
|
|
131029
|
+
var NATIVE_AGENTS = ["build", "plan", "general", "explore"];
|
|
131030
|
+
var DEFAULT_ASKS = {
|
|
131031
|
+
read: [["*", "allow"], ["*.env", "ask"], ["*.env.*", "ask"], ["*.env.example", "allow"]],
|
|
131032
|
+
doom_loop: [["*", "ask"]]
|
|
131033
|
+
};
|
|
131034
|
+
function holdEnv(hold, theirs, all, agentFiles, permissionEnv, matches2) {
|
|
131035
|
+
const named = (group) => all.flatMap((c) => Object.keys(asObj(c[group])));
|
|
131036
|
+
const modes = new Set(named("mode"));
|
|
131037
|
+
const configured = [.../* @__PURE__ */ new Set([...named("agent"), ...modes])];
|
|
131038
|
+
const held3 = [.../* @__PURE__ */ new Set([...NATIVE_AGENTS, ...configured, ...agentFiles])];
|
|
131039
|
+
const primary = hold === "safe" ? "build" : [...all].reverse().map((c) => c.default_agent).find((a) => typeof a === "string") ?? "build";
|
|
131040
|
+
const entered = hold === "safe" ? held3 : [.../* @__PURE__ */ new Set([primary, ...NATIVE_AGENTS, ...configured])];
|
|
131041
|
+
const globalLevel = level(all);
|
|
131042
|
+
const levelOf = (name) => level([...all.map((c) => asObj(asObj(c.agent)[name])), ...all.map((c) => asObj(asObj(c.mode)[name]))]);
|
|
131043
|
+
const rulesFor = (levels, asks) => ({ ...ownRules(hold, levels, asks, matches2), ...taskRules(hold, levels, held3, primary, asks, matches2) });
|
|
131044
|
+
const globalRules = rulesFor([globalLevel], false);
|
|
131045
|
+
const entries = (group, names) => Object.fromEntries(
|
|
131046
|
+
names.map((name) => {
|
|
131047
|
+
const entry = asObj(asObj(theirs[group])[name]);
|
|
131048
|
+
return [name, { ...entry, permission: withRules(entry.permission, rulesFor([globalLevel, levelOf(name)], name === primary)) }];
|
|
131049
|
+
})
|
|
131050
|
+
);
|
|
131051
|
+
const config2 = {
|
|
131052
|
+
...theirs,
|
|
131053
|
+
permission: withRules(theirs.permission, globalRules),
|
|
131054
|
+
agent: { ...asObj(theirs.agent), ...entries("agent", entered.filter((n) => !modes.has(n))) },
|
|
131055
|
+
...entered.some((n) => modes.has(n)) ? { mode: { ...asObj(theirs.mode), ...entries("mode", entered.filter((n) => modes.has(n))) } } : {},
|
|
131056
|
+
...hold === "safe" ? { default_agent: "build" } : {}
|
|
131057
|
+
};
|
|
131058
|
+
const out = { OPENCODE_CONFIG_CONTENT: JSON.stringify(config2) };
|
|
131059
|
+
if (permissionEnv !== void 0 && hold === "safe") out.OPENCODE_PERMISSION = JSON.stringify(withRules(jsonObject(permissionEnv), globalRules));
|
|
131060
|
+
return out;
|
|
131061
|
+
}
|
|
131062
|
+
function ownRules(hold, levels, asks, matches2) {
|
|
131063
|
+
const presetTools = hold === "safe" ? SAFE_ASKS : NORMAL_ASKS;
|
|
131064
|
+
const askedHere = levels.flatMap((l) => Object.entries(l).filter(([key, v]) => !/[*?]/.test(key) && key !== "task" && rulesOfValue(v).some(([, a]) => a === "ask")).map(([key]) => key));
|
|
131065
|
+
const tools = [.../* @__PURE__ */ new Set([...Object.keys(DEFAULT_ASKS), ...askedHere, ...presetTools])];
|
|
131066
|
+
return Object.fromEntries(
|
|
131067
|
+
tools.flatMap((tool) => {
|
|
131068
|
+
const theirs = levels.flatMap((l) => toolRules(l, tool, matches2));
|
|
131069
|
+
const rules = hold === "safe" && SAFE_ASKS.includes(tool) ? [["*", "ask"], ...theirs.filter(([, a]) => a === "deny")] : [...DEFAULT_ASKS[tool] ?? (presetTools.includes(tool) ? [["*", "ask"]] : []), ...theirs];
|
|
131070
|
+
const ordered = Object.entries(inOrder(rules));
|
|
131071
|
+
const first = ordered.findIndex(([, a]) => a === "ask");
|
|
131072
|
+
if (first < 0) return [];
|
|
131073
|
+
const fromAsk = ordered.slice(first);
|
|
131074
|
+
return [[ownKey(tool), Object.fromEntries(asks ? fromAsk : fromAsk.map(([p, a]) => [p, a === "ask" ? "deny" : a]))]];
|
|
131075
|
+
})
|
|
131076
|
+
);
|
|
131077
|
+
}
|
|
131078
|
+
function taskRules(hold, levels, held3, primary, asks, matches2) {
|
|
131079
|
+
const theirs = levels.flatMap((l) => toolRules(l, "task", matches2));
|
|
131080
|
+
const denies = theirs.filter(([, a]) => a === "deny");
|
|
131081
|
+
const base2 = hold === "safe" ? [["*", "ask"], ...denies.some(([p]) => p === "*") ? [] : held3.map((n) => [n, "allow"])] : theirs;
|
|
131082
|
+
const rules = [...base2, [primary, "deny"], ...denies];
|
|
131083
|
+
return { [ownKey("task")]: inOrder(asks ? rules : rules.map(([p, a]) => [p, a === "ask" ? "deny" : a])) };
|
|
131084
|
+
}
|
|
131085
|
+
var ownKey = (tool) => `${tool}*`;
|
|
131086
|
+
var withRules = (p, rules) => ({ ...Object.fromEntries(Object.entries(permissionOf(p)).filter(([k2]) => !(k2 in rules))), ...rules });
|
|
131087
|
+
var permissionOf = (p) => typeof p === "string" ? { "*": p } : asObj(p);
|
|
131088
|
+
function merge2(a, b2) {
|
|
131089
|
+
if (!isObj(a) || !isObj(b2)) return b2;
|
|
131090
|
+
const out = { ...a, ...b2 };
|
|
131091
|
+
for (const k2 of Object.keys(b2)) if (k2 in a && isObj(a[k2]) && isObj(b2[k2])) out[k2] = merge2(a[k2], b2[k2]);
|
|
131092
|
+
return out;
|
|
131093
|
+
}
|
|
131094
|
+
function level(cs2) {
|
|
131095
|
+
const tools = cs2.map((c) => c.tools).filter(isObj).reduce(merge2, {});
|
|
131096
|
+
const fromTools = Object.fromEntries(Object.entries(asObj(tools)).map(([t, on2]) => [["write", "patch"].includes(t) ? "edit" : t, on2 ? "allow" : "deny"]));
|
|
131097
|
+
const permission = cs2.flatMap((c) => c.permission === void 0 ? [] : [permissionOf(c.permission)]).reduce(merge2, {});
|
|
131098
|
+
return asObj(merge2(fromTools, permission));
|
|
131099
|
+
}
|
|
131100
|
+
var rulesOfValue = (v) => typeof v === "string" ? [["*", v]] : Object.entries(asObj(v)).flatMap(([p, a]) => typeof a === "string" ? [[p, a]] : []);
|
|
131101
|
+
var toolRules = (l, tool, matches2) => Object.entries(l).filter(([key]) => matches2(tool, key)).flatMap(([, v]) => rulesOfValue(v));
|
|
131102
|
+
function inOrder(rules) {
|
|
131103
|
+
const out = /* @__PURE__ */ new Map();
|
|
131104
|
+
for (const [pattern, action] of rules) {
|
|
131105
|
+
if (pattern === "*") out.clear();
|
|
131106
|
+
out.delete(pattern);
|
|
131107
|
+
out.set(pattern, action);
|
|
131108
|
+
}
|
|
131109
|
+
return Object.fromEntries(out);
|
|
131110
|
+
}
|
|
131111
|
+
function matcher(platform) {
|
|
131112
|
+
const flags = openCodeMatchesAnyCase(platform) ? "si" : "s";
|
|
131113
|
+
return (value, pattern) => {
|
|
131114
|
+
const re2 = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
131115
|
+
return new RegExp(`^${re2.endsWith(" .*") ? `${re2.slice(0, -3)}( .*)?` : re2}$`, flags).test(value);
|
|
131116
|
+
};
|
|
131117
|
+
}
|
|
131118
|
+
|
|
131119
|
+
// packages/agent-host/src/adapters/opencode/install.ts
|
|
131120
|
+
function findOpenCode(env = process.env) {
|
|
131121
|
+
const path2 = whichTool("opencode", env);
|
|
131122
|
+
return path2 ? launchFor(path2) : null;
|
|
131123
|
+
}
|
|
131124
|
+
|
|
131125
|
+
// packages/agent-host/src/adapters/opencode/surface.ts
|
|
131126
|
+
import { execFile as execFile10 } from "node:child_process";
|
|
131127
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
131128
|
+
import { promisify as promisify9 } from "node:util";
|
|
131129
|
+
|
|
131130
|
+
// packages/agent-host/src/adapters/opencode/surface-baseline.ts
|
|
131131
|
+
var SURFACE_BASELINE4 = {
|
|
131132
|
+
recorded: "2026-10-25",
|
|
131133
|
+
version: "opencode 1.19.4",
|
|
131134
|
+
offered: {
|
|
131135
|
+
command: [
|
|
131136
|
+
"acp",
|
|
131137
|
+
"agent",
|
|
131138
|
+
"attach",
|
|
131139
|
+
"completion",
|
|
131140
|
+
"db",
|
|
131141
|
+
"debug",
|
|
131142
|
+
"export",
|
|
131143
|
+
"github",
|
|
131144
|
+
"import",
|
|
131145
|
+
"mcp",
|
|
131146
|
+
"models",
|
|
131147
|
+
"plugin",
|
|
131148
|
+
"pr",
|
|
131149
|
+
"providers",
|
|
131150
|
+
"run",
|
|
131151
|
+
"serve",
|
|
131152
|
+
"session",
|
|
131153
|
+
"stats",
|
|
131154
|
+
"uninstall",
|
|
131155
|
+
"upgrade",
|
|
131156
|
+
"web"
|
|
131157
|
+
],
|
|
131158
|
+
message: [
|
|
131159
|
+
"agent_message_chunk",
|
|
131160
|
+
"agent_thought_chunk",
|
|
131161
|
+
"available_commands_update",
|
|
131162
|
+
"config_option_update",
|
|
131163
|
+
"tool_call",
|
|
131164
|
+
"tool_call_update",
|
|
131165
|
+
"usage_update"
|
|
131166
|
+
],
|
|
131167
|
+
capability: [
|
|
131168
|
+
"authMethods.opencode-login",
|
|
131169
|
+
"loadSession",
|
|
131170
|
+
"mcpCapabilities.http",
|
|
131171
|
+
"mcpCapabilities.sse",
|
|
131172
|
+
"promptCapabilities.embeddedContext",
|
|
131173
|
+
"promptCapabilities.image",
|
|
131174
|
+
"sessionCapabilities.close",
|
|
131175
|
+
"sessionCapabilities.fork",
|
|
131176
|
+
"sessionCapabilities.list",
|
|
131177
|
+
"sessionCapabilities.resume"
|
|
131178
|
+
]
|
|
131179
|
+
}
|
|
131180
|
+
};
|
|
131181
|
+
|
|
131182
|
+
// packages/agent-host/src/adapters/opencode/surface.ts
|
|
131183
|
+
var exec8 = promisify9(execFile10);
|
|
131184
|
+
var Initialized2 = external_exports.looseObject({ agentCapabilities: external_exports.unknown().optional(), authMethods: external_exports.array(external_exports.looseObject({ id: external_exports.string() })).optional() });
|
|
131185
|
+
async function gather2() {
|
|
131186
|
+
const install = findOpenCode();
|
|
131187
|
+
if (!install) throw new Error("OpenCode is not installed on this machine (npm i -g opencode-ai)");
|
|
131188
|
+
const [{ stdout: version2 }, shown3] = await Promise.all([
|
|
131189
|
+
exec8(install.command, [...install.args, "--version"], { timeout: 3e4, windowsHide: true }),
|
|
131190
|
+
exec8(install.command, [...install.args, "--help"], { timeout: 3e4, windowsHide: true })
|
|
131191
|
+
]);
|
|
131192
|
+
const help = shown3.stdout + shown3.stderr;
|
|
131193
|
+
const proc = spawnLocalAgent({ command: install.command, args: [...install.args, "acp"], cwd: process.cwd(), env: process.env });
|
|
131194
|
+
const conn = new AcpConnection(proc, { onNotification: () => {
|
|
131195
|
+
}, onRequest: async () => null, onExit: () => {
|
|
131196
|
+
} }, "opencode-surface");
|
|
131197
|
+
let init;
|
|
131198
|
+
try {
|
|
131199
|
+
init = await conn.request("initialize", { protocolVersion: 1, clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false }, clientInfo: { name: "centralu-surface", version: "1" } }, 6e4);
|
|
131200
|
+
} finally {
|
|
131201
|
+
await conn.dispose();
|
|
131202
|
+
}
|
|
131203
|
+
const names = new Set([...readFileSync18(install.command).toString("latin1").matchAll(/sessionUpdate:"([a-z_]+)"/g)].flatMap((m) => m[1] ? [m[1]] : []));
|
|
131204
|
+
return { version: version2.trim(), help, initialize: JSON.stringify(init), bundle: JSON.stringify([...names].sort()) };
|
|
131205
|
+
}
|
|
131206
|
+
function capabilityNames2(value, prefix = "") {
|
|
131207
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return prefix ? [prefix] : [];
|
|
131208
|
+
const entries = Object.entries(asObj(value));
|
|
131209
|
+
if (entries.length === 0) return prefix ? [prefix] : [];
|
|
131210
|
+
return entries.flatMap(([k2, v]) => typeof v === "object" && v !== null ? capabilityNames2(v, prefix ? `${prefix}.${k2}` : k2) : [prefix ? `${prefix}.${k2}` : k2]);
|
|
131211
|
+
}
|
|
131212
|
+
function readOpenCodeSurface(inputs) {
|
|
131213
|
+
const init = Initialized2.parse(JSON.parse(inputs.initialize ?? "{}"));
|
|
131214
|
+
const commands = [...(inputs.help ?? "").matchAll(/^ {2}opencode ([a-z][\w-]*)/gm)].flatMap((m) => m[1] ? [m[1]] : []);
|
|
131215
|
+
return {
|
|
131216
|
+
version: `opencode ${inputs.version ?? "unknown"}`,
|
|
131217
|
+
offered: {
|
|
131218
|
+
command: [...new Set(commands)].sort(),
|
|
131219
|
+
message: external_exports.array(external_exports.string()).parse(JSON.parse(inputs.bundle ?? "[]")).sort(),
|
|
131220
|
+
capability: [...capabilityNames2(init.agentCapabilities), ...(init.authMethods ?? []).map((a) => `authMethods.${a.id}`)].sort()
|
|
131221
|
+
}
|
|
131222
|
+
};
|
|
131223
|
+
}
|
|
131224
|
+
var MEASURED = "opencode 1.19.4, 2026-10-26";
|
|
131225
|
+
var OPENCODE_SURFACE = {
|
|
131226
|
+
gather: gather2,
|
|
131227
|
+
read: readOpenCodeSurface,
|
|
131228
|
+
limitations: [
|
|
131229
|
+
{
|
|
131230
|
+
kind: "message",
|
|
131231
|
+
name: "compaction_update",
|
|
131232
|
+
treatment: "worked-around",
|
|
131233
|
+
measured: `${MEASURED}: /compact ran with the client advertising session.compaction and sent no compaction frame, only text and a smaller usage_update`,
|
|
131234
|
+
offeredWhenMeasured: false,
|
|
131235
|
+
note: "Its compaction is read from the compact turn Centralu started (infersCompaction, \xA75.6): ended end_turn with a smaller reading. When this kind appears, the frames can replace the inference."
|
|
131236
|
+
},
|
|
131237
|
+
{
|
|
131238
|
+
kind: "capability",
|
|
131239
|
+
name: "authMethods.opencode-login",
|
|
131240
|
+
treatment: "blocked",
|
|
131241
|
+
measured: `${MEASURED}: session/new works without authenticate (its own free models), and a provider's key is the person's OpenCode settings`,
|
|
131242
|
+
offeredWhenMeasured: true,
|
|
131243
|
+
note: "Never called: logging in stays `opencode auth login` in a terminal."
|
|
131244
|
+
},
|
|
131245
|
+
{
|
|
131246
|
+
kind: "capability",
|
|
131247
|
+
name: "sessionCapabilities.resume",
|
|
131248
|
+
treatment: "dropped",
|
|
131249
|
+
measured: `${MEASURED}: session/load answered in about 1 s, session/resume in about 45 s`,
|
|
131250
|
+
offeredWhenMeasured: true,
|
|
131251
|
+
note: "A conversation is reopened with session/load, whose replay is dropped."
|
|
131252
|
+
},
|
|
131253
|
+
{
|
|
131254
|
+
kind: "capability",
|
|
131255
|
+
name: "sessionCapabilities.fork",
|
|
131256
|
+
treatment: "dropped",
|
|
131257
|
+
measured: MEASURED,
|
|
131258
|
+
offeredWhenMeasured: true,
|
|
131259
|
+
note: "Forks start a new conversation with the handoff, as for any tool, until a fork maps onto session/fork."
|
|
131260
|
+
},
|
|
131261
|
+
{
|
|
131262
|
+
kind: "message",
|
|
131263
|
+
name: "config_option_update",
|
|
131264
|
+
treatment: "dropped",
|
|
131265
|
+
measured: MEASURED,
|
|
131266
|
+
offeredWhenMeasured: true,
|
|
131267
|
+
note: "The model and mode options are not offered in the settings menu yet; the person\u2019s OpenCode default model runs."
|
|
131268
|
+
}
|
|
131269
|
+
],
|
|
131270
|
+
baseline: SURFACE_BASELINE4
|
|
131271
|
+
};
|
|
131272
|
+
|
|
131273
|
+
// packages/agent-host/src/adapters/opencode/index.ts
|
|
131274
|
+
var exec9 = promisify10(execFile11);
|
|
131275
|
+
var CAPABILITIES2 = {
|
|
131276
|
+
approvals: true,
|
|
131277
|
+
contextUsage: "exact",
|
|
131278
|
+
resume: true,
|
|
131279
|
+
autoTitle: true,
|
|
131280
|
+
attachments: [],
|
|
131281
|
+
verbosities: [],
|
|
131282
|
+
exclusiveWriter: false,
|
|
131283
|
+
backgroundTasks: false,
|
|
131284
|
+
midTurnInput: false,
|
|
131285
|
+
gatesSteps: false,
|
|
131286
|
+
toolDeferral: false,
|
|
131287
|
+
instructionChannel: "server",
|
|
131288
|
+
processModel: "per-session",
|
|
131289
|
+
builtInSubagents: false,
|
|
131290
|
+
peerMessaging: false,
|
|
131291
|
+
sleep: false,
|
|
131292
|
+
keepsRepoFilesOut: false,
|
|
131293
|
+
roleText: false
|
|
131294
|
+
};
|
|
131295
|
+
var DESCRIPTOR2 = {
|
|
131296
|
+
name: "opencode",
|
|
131297
|
+
label: "OpenCode",
|
|
131298
|
+
mark: "O",
|
|
131299
|
+
install: "npm i -g opencode-ai (or: curl -fsSL https://opencode.ai/install | bash)",
|
|
131300
|
+
login: "opencode auth login"
|
|
131301
|
+
};
|
|
131302
|
+
var VERSION_FOR_MS = 6e4;
|
|
131303
|
+
function openCodeProfile() {
|
|
131304
|
+
let known = null;
|
|
131305
|
+
const version2 = async () => {
|
|
131306
|
+
if (known && Date.now() - known.at < VERSION_FOR_MS) return known.version;
|
|
131307
|
+
const l = findOpenCode();
|
|
131308
|
+
const v = l ? await exec9(l.command, [...l.args, "--version"], { timeout: 2e4, windowsHide: true }).then((r) => r.stdout.trim() || null, () => null) : null;
|
|
131309
|
+
known = { at: Date.now(), version: v };
|
|
131310
|
+
return v;
|
|
131311
|
+
};
|
|
131312
|
+
return {
|
|
131313
|
+
descriptor: DESCRIPTOR2,
|
|
131314
|
+
capabilities: CAPABILITIES2,
|
|
131315
|
+
surface: OPENCODE_SURFACE,
|
|
131316
|
+
infersCompaction: true,
|
|
131317
|
+
async launch(opts) {
|
|
131318
|
+
const l = findOpenCode();
|
|
131319
|
+
return l ? { command: l.command, args: [...l.args, "acp"], env: presetEnv(opts.permissionPreset, process.env, readOpenCodeConfigs(opts.cwd, process.env)) } : null;
|
|
131320
|
+
},
|
|
131321
|
+
installedVersion: version2,
|
|
131322
|
+
async detect() {
|
|
131323
|
+
if (!findOpenCode()) return { tool: DESCRIPTOR2.name, installed: false, loggedIn: false, detail: "OpenCode not found (npm i -g opencode-ai)" };
|
|
131324
|
+
const v = await version2();
|
|
131325
|
+
return { tool: DESCRIPTOR2.name, installed: true, loggedIn: true, detail: v ? `opencode ${v}` : "opencode" };
|
|
131326
|
+
}
|
|
131327
|
+
};
|
|
131328
|
+
}
|
|
131329
|
+
var OpenCodeAdapter = class extends AcpAdapter {
|
|
131330
|
+
tool = "opencode";
|
|
131331
|
+
constructor() {
|
|
131332
|
+
super(openCodeProfile());
|
|
131333
|
+
}
|
|
131334
|
+
};
|
|
131335
|
+
|
|
129261
131336
|
// packages/agent-host/src/adapters/registry.ts
|
|
129262
131337
|
function createAdapters() {
|
|
129263
|
-
const all = [new ClaudeAdapter(), new CodexAdapter()];
|
|
131338
|
+
const all = [new ClaudeAdapter(), new CodexAdapter(), new CursorAgentAdapter(), new OpenCodeAdapter()];
|
|
129264
131339
|
return new Map(all.map((a) => [a.tool, a]));
|
|
129265
131340
|
}
|
|
129266
131341
|
function registeredTool(adapters2, preferred) {
|
|
@@ -129306,6 +131381,14 @@ var ToolRegistry = class {
|
|
|
129306
131381
|
}
|
|
129307
131382
|
return this.firstTool(this.host.store.settings.appSetting("orchestrator_tool"));
|
|
129308
131383
|
}
|
|
131384
|
+
/**
|
|
131385
|
+
* Why `tool` cannot run a session that demands this (`launchRefusal`), or null: the one rule, for every path that puts
|
|
131386
|
+
* a tool on a session (`tool-fit.ts`). A tool this host does not run is `toolProblem`'s to say
|
|
131387
|
+
*/
|
|
131388
|
+
refusal(tool, demands) {
|
|
131389
|
+
const adapter = this.host.adapters.get(tool);
|
|
131390
|
+
return adapter ? launchRefusal(adapter.capabilities, adapter.descriptor.label, demands) : null;
|
|
131391
|
+
}
|
|
129309
131392
|
/** Why `tool` cannot start a session here now (not registered, not installed, not logged in), or null when it can */
|
|
129310
131393
|
async toolProblem(tool) {
|
|
129311
131394
|
const adapter = this.host.adapters.get(tool);
|
|
@@ -129351,13 +131434,13 @@ var ApprovalRules = class {
|
|
|
129351
131434
|
* restart (C-2). `matcher` is the card's own key, which the caller has checked (`respondApproval`).
|
|
129352
131435
|
* A session with no project (#994) has no project to remember for: its project answer is kept for the session.
|
|
129353
131436
|
*/
|
|
129354
|
-
rememberAlways(sessionId, sessionProjectId, scope,
|
|
131437
|
+
rememberAlways(sessionId, sessionProjectId, scope, matcher2) {
|
|
129355
131438
|
const projectId = scope === "project" ? sessionProjectId : null;
|
|
129356
131439
|
this.host.store.permissions.addApprovalRule({
|
|
129357
131440
|
scope: projectId !== null ? "project" : "session",
|
|
129358
131441
|
sessionId: projectId !== null ? void 0 : sessionId,
|
|
129359
131442
|
projectId: projectId ?? void 0,
|
|
129360
|
-
matcher,
|
|
131443
|
+
matcher: matcher2,
|
|
129361
131444
|
decision: "allow"
|
|
129362
131445
|
});
|
|
129363
131446
|
}
|
|
@@ -130331,6 +132414,8 @@ var OrchestratorSlot = class {
|
|
|
130331
132414
|
* session's own settings takes over — this value is never read again after that.
|
|
130332
132415
|
*/
|
|
130333
132416
|
configureOrchestrator(tool) {
|
|
132417
|
+
const refused2 = this.host.refusal(tool, ORCHESTRATOR_DEMANDS);
|
|
132418
|
+
if (refused2) throw unfitError(refused2);
|
|
130334
132419
|
this.host.store.settings.setAppSetting("orchestrator_tool", tool);
|
|
130335
132420
|
}
|
|
130336
132421
|
/** The orchestrator being created (#839): a second ask while it is made waits on this one, never makes another */
|
|
@@ -130367,7 +132452,7 @@ var OrchestratorSlot = class {
|
|
|
130367
132452
|
kind: "orchestrator",
|
|
130368
132453
|
cwd: orchestratorHome(),
|
|
130369
132454
|
tool: this.host.firstTool(configured),
|
|
130370
|
-
permissionPreset:
|
|
132455
|
+
permissionPreset: ORCHESTRATOR_DEMANDS.permissionPreset
|
|
130371
132456
|
});
|
|
130372
132457
|
this.host.rename(info.id, "Orchestrator");
|
|
130373
132458
|
return this.host.meta.get(info.id);
|
|
@@ -130487,6 +132572,8 @@ var SessionSettings = class {
|
|
|
130487
132572
|
host;
|
|
130488
132573
|
async updateSettings(sessionId, s, by = "person") {
|
|
130489
132574
|
const m = this.host.openSession(sessionId);
|
|
132575
|
+
const refused2 = s.permissionPreset ? this.host.toolRefusal(m, m.tool, s.permissionPreset) : null;
|
|
132576
|
+
if (refused2) throw unfitError(refused2);
|
|
130490
132577
|
if (s.model !== void 0 && s.model !== m.model) this.host.cacheLedger.note(sessionId, "model_change");
|
|
130491
132578
|
if (s.model !== void 0) m.model = s.model;
|
|
130492
132579
|
if (s.effort !== void 0) m.effort = s.effort;
|
|
@@ -130998,8 +133085,8 @@ var UsageRecorder = class {
|
|
|
130998
133085
|
};
|
|
130999
133086
|
|
|
131000
133087
|
// packages/agent-host/src/sessions/manager.ts
|
|
131001
|
-
import { homedir as
|
|
131002
|
-
import { join as
|
|
133088
|
+
import { homedir as homedir15 } from "node:os";
|
|
133089
|
+
import { join as join51 } from "node:path";
|
|
131003
133090
|
|
|
131004
133091
|
// packages/agent-host/src/dev-services/store.ts
|
|
131005
133092
|
import Database2 from "better-sqlite3";
|
|
@@ -131016,7 +133103,7 @@ function pragmaNumber(db, name) {
|
|
|
131016
133103
|
}
|
|
131017
133104
|
|
|
131018
133105
|
// packages/agent-host/src/dev-services/store.ts
|
|
131019
|
-
import { existsSync as
|
|
133106
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19 } from "node:fs";
|
|
131020
133107
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
131021
133108
|
|
|
131022
133109
|
// packages/agent-host/src/dev-services/store/app-runs.ts
|
|
@@ -131626,8 +133713,10 @@ var ChildOwnershipRecords = class {
|
|
|
131626
133713
|
* stored request names its caller too). A crash before the commit leaves every row on the original; after it, every
|
|
131627
133714
|
* row on the new session, so the next host (a swap or a restart) reads one owner either way. `armReports`: children
|
|
131628
133715
|
* whose report the original was waiting for in a call (its wait had taken the mark down), now owed to the new session.
|
|
133716
|
+
* `then`: the rest of the handoff's move that is stored elsewhere (the mail, `MailService.handOver`), run last in the same
|
|
133717
|
+
* transaction, so a refusal there leaves every row of both on the original.
|
|
131629
133718
|
*/
|
|
131630
|
-
moveChildren(from, to, move) {
|
|
133719
|
+
moveChildren(from, to, move, then) {
|
|
131631
133720
|
this.db.transaction(() => {
|
|
131632
133721
|
const owner = this.db.prepare(
|
|
131633
133722
|
`UPDATE sessions SET
|
|
@@ -131650,6 +133739,7 @@ var ChildOwnershipRecords = class {
|
|
|
131650
133739
|
ON CONFLICT(session_id) DO UPDATE SET caller_session_id = excluded.caller_session_id, marked_at = excluded.marked_at`
|
|
131651
133740
|
);
|
|
131652
133741
|
for (const id3 of move.armReports ?? []) arm.run(id3, to, Date.now());
|
|
133742
|
+
then?.();
|
|
131653
133743
|
})();
|
|
131654
133744
|
}
|
|
131655
133745
|
};
|
|
@@ -132180,7 +134270,7 @@ var UserAppAdoptionRecords = class {
|
|
|
132180
134270
|
|
|
132181
134271
|
// packages/agent-host/src/dev-services/store/migrate-runner.ts
|
|
132182
134272
|
import Database from "better-sqlite3";
|
|
132183
|
-
import { existsSync as
|
|
134273
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
132184
134274
|
|
|
132185
134275
|
// packages/agent-host/src/dev-services/store/store-keys.ts
|
|
132186
134276
|
var APP_SETTINGS_DDL = `
|
|
@@ -132524,7 +134614,7 @@ var MigrationRunner = class extends ReaderGate {
|
|
|
132524
134614
|
const probe = bare2;
|
|
132525
134615
|
const steps = probe.migrationSteps();
|
|
132526
134616
|
const known = steps.at(-1)?.to ?? 0;
|
|
132527
|
-
if (dbPath2 === ":memory:" || !
|
|
134617
|
+
if (dbPath2 === ":memory:" || !existsSync21(dbPath2)) return empty(known);
|
|
132528
134618
|
const db = new Database(dbPath2, { readonly: true, fileMustExist: true });
|
|
132529
134619
|
try {
|
|
132530
134620
|
probe.db = db;
|
|
@@ -133415,11 +135505,11 @@ var StepsV28ToV40 = class extends MigrationRange {
|
|
|
133415
135505
|
);
|
|
133416
135506
|
`);
|
|
133417
135507
|
const insert = this.db.prepare(`INSERT INTO messages_fts (rowid, body, session_id, seq) VALUES (?, ?, ?, ?)`);
|
|
133418
|
-
const
|
|
135508
|
+
const pick4 = this.db.prepare(
|
|
133419
135509
|
`SELECT rowid, session_id, seq, kind, payload FROM messages
|
|
133420
135510
|
WHERE kind IN ${kinds} AND session_id NOT IN (SELECT id FROM sessions WHERE deleted_at IS NOT NULL)`
|
|
133421
135511
|
);
|
|
133422
|
-
for (const r of
|
|
135512
|
+
for (const r of pick4.all()) {
|
|
133423
135513
|
const body = indexedText(r.kind, r.payload);
|
|
133424
135514
|
if (!body) continue;
|
|
133425
135515
|
insert.run(r.rowid, body, r.session_id, r.seq);
|
|
@@ -134935,7 +137025,7 @@ var TrashRecords = class {
|
|
|
134935
137025
|
*/
|
|
134936
137026
|
async purgeSession(sessionId, chunk = DELETE_CHUNK) {
|
|
134937
137027
|
if (!this.isTrashed(sessionId)) return false;
|
|
134938
|
-
const
|
|
137028
|
+
const pick4 = this.db.prepare(`SELECT rowid FROM messages WHERE session_id = ? LIMIT ?`);
|
|
134939
137029
|
const dropFts = this.db.prepare(`DELETE FROM messages_fts WHERE rowid = ?`);
|
|
134940
137030
|
const dropMsg = this.db.prepare(`DELETE FROM messages WHERE rowid = ?`);
|
|
134941
137031
|
const pickSub = this.db.prepare(`SELECT rowid FROM subagent_messages WHERE session_id = ? LIMIT ?`);
|
|
@@ -134944,7 +137034,7 @@ var TrashRecords = class {
|
|
|
134944
137034
|
const subs = pickSub.all(sessionId, chunk);
|
|
134945
137035
|
for (const { rowid } of subs) dropSub.run(rowid);
|
|
134946
137036
|
if (subs.length === chunk) return false;
|
|
134947
|
-
const rows3 =
|
|
137037
|
+
const rows3 = pick4.all(sessionId, chunk);
|
|
134948
137038
|
for (const { rowid } of rows3) {
|
|
134949
137039
|
dropFts.run(rowid);
|
|
134950
137040
|
dropMsg.run(rowid);
|
|
@@ -134975,12 +137065,12 @@ var TrashRecords = class {
|
|
|
134975
137065
|
}
|
|
134976
137066
|
/** One session's messages in seq order, one transaction per chunk, letting the event loop go between chunks (#179) */
|
|
134977
137067
|
async walkMessages(sessionId, chunk, withPayload, each2) {
|
|
134978
|
-
const
|
|
137068
|
+
const pick4 = this.db.prepare(
|
|
134979
137069
|
`SELECT rowid, seq${withPayload ? ", kind, payload" : ""} FROM messages WHERE session_id = ? AND seq > ? ORDER BY seq LIMIT ?`
|
|
134980
137070
|
);
|
|
134981
137071
|
let after = Number.MIN_SAFE_INTEGER;
|
|
134982
137072
|
const step = this.db.transaction(() => {
|
|
134983
|
-
const rows3 =
|
|
137073
|
+
const rows3 = pick4.all(sessionId, after, chunk);
|
|
134984
137074
|
each2(rows3);
|
|
134985
137075
|
const last = rows3.at(-1);
|
|
134986
137076
|
if (last) after = last.seq;
|
|
@@ -135156,14 +137246,14 @@ var SubagentStepRecords = class {
|
|
|
135156
137246
|
this.db.prepare(`SELECT MAX(trashed_at) as prev FROM subagent_messages WHERE session_id = ? AND trashed_at IS NOT NULL`).get(sessionId)
|
|
135157
137247
|
);
|
|
135158
137248
|
const at2 = prev !== null && prev >= now ? prev + 1 : now;
|
|
135159
|
-
const
|
|
137249
|
+
const pick4 = this.db.prepare(
|
|
135160
137250
|
`SELECT rowid, LENGTH(CAST(payload AS BLOB)) as bytes FROM subagent_messages
|
|
135161
137251
|
WHERE session_id = ? AND trashed_at IS NULL AND rowid <= ? LIMIT ?`
|
|
135162
137252
|
);
|
|
135163
137253
|
const mark = this.db.prepare(`UPDATE subagent_messages SET trashed_at = ? WHERE rowid = ?`);
|
|
135164
137254
|
const moved = { steps: 0, bytes: 0 };
|
|
135165
137255
|
await this.inChunks(() => {
|
|
135166
|
-
const rows3 =
|
|
137256
|
+
const rows3 = pick4.all(sessionId, last, chunk);
|
|
135167
137257
|
for (const r of rows3) {
|
|
135168
137258
|
mark.run(at2, r.rowid);
|
|
135169
137259
|
moved.steps++;
|
|
@@ -135176,10 +137266,10 @@ var SubagentStepRecords = class {
|
|
|
135176
137266
|
/** Puts a live session's trashed steps back under their cards. False when it had none in the trash */
|
|
135177
137267
|
async restore(sessionId, chunk = CHUNK) {
|
|
135178
137268
|
if (!this.hasTrashed(sessionId)) return false;
|
|
135179
|
-
const
|
|
137269
|
+
const pick4 = this.db.prepare(`SELECT rowid FROM subagent_messages WHERE session_id = ? AND trashed_at IS NOT NULL LIMIT ?`);
|
|
135180
137270
|
const unmark = this.db.prepare(`UPDATE subagent_messages SET trashed_at = NULL WHERE rowid = ?`);
|
|
135181
137271
|
await this.inChunks(() => {
|
|
135182
|
-
const rows3 =
|
|
137272
|
+
const rows3 = pick4.all(sessionId, chunk);
|
|
135183
137273
|
for (const r of rows3) unmark.run(r.rowid);
|
|
135184
137274
|
return rows3.length < chunk;
|
|
135185
137275
|
});
|
|
@@ -135193,12 +137283,12 @@ var SubagentStepRecords = class {
|
|
|
135193
137283
|
*/
|
|
135194
137284
|
async purge(sessionId, upTo, chunk = CHUNK) {
|
|
135195
137285
|
if (!this.hasTrashed(sessionId, upTo)) return false;
|
|
135196
|
-
const
|
|
137286
|
+
const pick4 = this.db.prepare(
|
|
135197
137287
|
`SELECT rowid FROM subagent_messages WHERE session_id = ? AND trashed_at IS NOT NULL AND trashed_at <= ? LIMIT ?`
|
|
135198
137288
|
);
|
|
135199
137289
|
const drop = this.db.prepare(`DELETE FROM subagent_messages WHERE rowid = ?`);
|
|
135200
137290
|
await this.inChunks(() => {
|
|
135201
|
-
const rows3 =
|
|
137291
|
+
const rows3 = pick4.all(sessionId, upTo, chunk);
|
|
135202
137292
|
for (const r of rows3) drop.run(r.rowid);
|
|
135203
137293
|
return rows3.length < chunk;
|
|
135204
137294
|
});
|
|
@@ -135236,7 +137326,7 @@ function resolveSchemaPath() {
|
|
|
135236
137326
|
new URL("../../../protocol/src/schema/schema.sql", import.meta.url)
|
|
135237
137327
|
// source tree
|
|
135238
137328
|
].map((u) => fileURLToPath4(u));
|
|
135239
|
-
const found = candidates.find((p) =>
|
|
137329
|
+
const found = candidates.find((p) => existsSync22(p));
|
|
135240
137330
|
if (!found) throw new Error(`schema.sql not found: ${candidates.join(", ")}`);
|
|
135241
137331
|
return found;
|
|
135242
137332
|
}
|
|
@@ -135271,7 +137361,7 @@ var Store = class extends MigrationRunner {
|
|
|
135271
137361
|
this.db.pragma("synchronous = FULL");
|
|
135272
137362
|
this.db.pragma("busy_timeout = 5000");
|
|
135273
137363
|
this.db.pragma(`journal_size_limit = ${WAL_SIZE_LIMIT}`);
|
|
135274
|
-
this.db.exec(
|
|
137364
|
+
this.db.exec(readFileSync19(SCHEMA_PATH, "utf8"));
|
|
135275
137365
|
this.migrate();
|
|
135276
137366
|
} catch (e) {
|
|
135277
137367
|
this.db.close();
|
|
@@ -135513,7 +137603,7 @@ async function gitBranchPr(projectCwd, branch, signal, timeoutMs = OK2.timeout)
|
|
|
135513
137603
|
|
|
135514
137604
|
// packages/agent-host/src/dev-services/file-search.ts
|
|
135515
137605
|
import { readdir as readdir11 } from "node:fs/promises";
|
|
135516
|
-
import { join as
|
|
137606
|
+
import { join as join49 } from "node:path";
|
|
135517
137607
|
var MAX_FILES2 = 2e4;
|
|
135518
137608
|
var TTL_MS2 = 15e3;
|
|
135519
137609
|
var WALK_DEPTH = 6;
|
|
@@ -135551,7 +137641,7 @@ async function walk(root3) {
|
|
|
135551
137641
|
if (e.name.startsWith(".") && e.name !== ".env") continue;
|
|
135552
137642
|
if (e.isDirectory()) {
|
|
135553
137643
|
if (SKIP.has(e.name) || depth >= WALK_DEPTH) continue;
|
|
135554
|
-
queue.push({ dir:
|
|
137644
|
+
queue.push({ dir: join49(dir, e.name), rel: wireJoin(rel, e.name), depth: depth + 1 });
|
|
135555
137645
|
} else {
|
|
135556
137646
|
out.push(wireJoin(rel, e.name).normalize("NFC"));
|
|
135557
137647
|
if (out.length >= MAX_FILES2) break;
|
|
@@ -136639,8 +138729,8 @@ var HostCards = class {
|
|
|
136639
138729
|
// packages/agent-host/src/dev-services/message-image.ts
|
|
136640
138730
|
import { constants as constants8 } from "node:fs";
|
|
136641
138731
|
import { open as open5, realpath as realpath9 } from "node:fs/promises";
|
|
136642
|
-
import { homedir as
|
|
136643
|
-
import { posix as posix3, sep as sep10, win32 as
|
|
138732
|
+
import { homedir as homedir14 } from "node:os";
|
|
138733
|
+
import { posix as posix3, sep as sep10, win32 as win328 } from "node:path";
|
|
136644
138734
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
136645
138735
|
var OPEN_FLAGS = constants8.O_RDONLY | constants8.O_NONBLOCK | (constants8.O_NOFOLLOW ?? 0);
|
|
136646
138736
|
var MESSAGE_IMAGE_KINDS = "PNG, JPEG, GIF or WebP";
|
|
@@ -136675,7 +138765,7 @@ function mentions(text2, written) {
|
|
|
136675
138765
|
}
|
|
136676
138766
|
return false;
|
|
136677
138767
|
}
|
|
136678
|
-
function resolveWrittenPath(written, cwd, home =
|
|
138768
|
+
function resolveWrittenPath(written, cwd, home = homedir14(), p = sep10 === "\\" ? win328 : posix3) {
|
|
136679
138769
|
const windows2 = p.sep === "\\";
|
|
136680
138770
|
let abs;
|
|
136681
138771
|
if (/^file:/i.test(written)) {
|
|
@@ -136742,13 +138832,13 @@ async function readMessageImage(abs, max = IMAGE_PREVIEW_MAX_BYTES) {
|
|
|
136742
138832
|
|
|
136743
138833
|
// packages/agent-host/src/dev-services/attachments.ts
|
|
136744
138834
|
import { lstat as lstat9, mkdir as mkdir6, readdir as readdir12, rm as rm8, writeFile as writeFile4 } from "node:fs/promises";
|
|
136745
|
-
import { extname as
|
|
136746
|
-
var root2 = () =>
|
|
138835
|
+
import { extname as extname4, join as join50 } from "node:path";
|
|
138836
|
+
var root2 = () => join50(dataRoot(), "attachments");
|
|
136747
138837
|
function sessionDir(sessionId) {
|
|
136748
138838
|
if (!isSessionId(sessionId)) {
|
|
136749
138839
|
throw rpcError(`Not a session id: ${sessionId}`);
|
|
136750
138840
|
}
|
|
136751
|
-
return
|
|
138841
|
+
return join50(root2(), sessionId);
|
|
136752
138842
|
}
|
|
136753
138843
|
var EXT = {
|
|
136754
138844
|
"image/png": ".png",
|
|
@@ -136759,8 +138849,8 @@ var EXT = {
|
|
|
136759
138849
|
async function saveAttachment(sessionId, name, mime, dataBase64) {
|
|
136760
138850
|
const dir = sessionDir(sessionId);
|
|
136761
138851
|
await mkdir6(dir, { recursive: true });
|
|
136762
|
-
const ext2 =
|
|
136763
|
-
const file2 =
|
|
138852
|
+
const ext2 = extname4(name) || EXT[mime] || "";
|
|
138853
|
+
const file2 = join50(dir, `${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext2}`);
|
|
136764
138854
|
const buf = Buffer.from(dataBase64, "base64");
|
|
136765
138855
|
await writeFile4(file2, buf);
|
|
136766
138856
|
return { kind: mime.startsWith("image/") ? "image" : "file", path: file2, name, mime, bytes: buf.length };
|
|
@@ -136781,10 +138871,10 @@ async function sweepAttachments(maxBytes = ATTACHMENTS_MAX_BYTES) {
|
|
|
136781
138871
|
return 0;
|
|
136782
138872
|
}
|
|
136783
138873
|
for (const d2 of dirs) {
|
|
136784
|
-
const dir =
|
|
138874
|
+
const dir = join50(root2(), d2);
|
|
136785
138875
|
const names = await readdir12(dir).catch(() => []);
|
|
136786
138876
|
for (const name of names) {
|
|
136787
|
-
const p =
|
|
138877
|
+
const p = join50(dir, name);
|
|
136788
138878
|
const s = await lstat9(p).catch(() => null);
|
|
136789
138879
|
if (s?.isFile()) files.push({ path: p, size: s.size, mtime: s.mtimeMs });
|
|
136790
138880
|
}
|
|
@@ -137230,10 +139320,10 @@ var ToolQueries = class {
|
|
|
137230
139320
|
import { rmSync as rmSync6 } from "node:fs";
|
|
137231
139321
|
|
|
137232
139322
|
// packages/agent-host/src/sessions/app-builder.ts
|
|
137233
|
-
import { relative as
|
|
139323
|
+
import { relative as relative8 } from "node:path";
|
|
137234
139324
|
function builderRole(app2, cwd) {
|
|
137235
139325
|
const name = JSON.stringify(frameField(app2.name ?? app2.appId));
|
|
137236
|
-
const rel =
|
|
139326
|
+
const rel = relative8(cwd, app2.dir) || ".";
|
|
137237
139327
|
const where2 = app2.projectId === null ? `This is a user-folder app (used across several projects). Your working folder is the app folder: ${app2.dir}` : app2.root ? `This project is the app: its manifest stands at the project's root, so your working folder is the app folder: ${app2.dir}` : `This is this project's app (committed to the repository and shared with the team). App folder: ${rel}/ (${app2.dir})`;
|
|
137238
139328
|
const idRule = app2.root ? "whose id names the app" : "whose id matches the folder name";
|
|
137239
139329
|
return `You are the session that builds the Centralu app ${name} (id ${app2.appId}). ${where2}
|
|
@@ -137687,7 +139777,7 @@ async function gitStatusForApp(summary, files) {
|
|
|
137687
139777
|
// packages/agent-host/src/sessions/session-trash.ts
|
|
137688
139778
|
import { basename as basename12 } from "node:path";
|
|
137689
139779
|
import { rm as rm9 } from "node:fs/promises";
|
|
137690
|
-
import { existsSync as
|
|
139780
|
+
import { existsSync as existsSync23, statSync as statSync14 } from "node:fs";
|
|
137691
139781
|
var SessionTrash = class {
|
|
137692
139782
|
constructor(host) {
|
|
137693
139783
|
this.host = host;
|
|
@@ -137815,8 +139905,35 @@ var SessionTrash = class {
|
|
|
137815
139905
|
if (this.host.readGrants.get(sessionId)?.size || this.host.delegationFlow.starts.agentConsents.has(sessionId)) {
|
|
137816
139906
|
out.push("What the person allowed it (reading other sessions, starting agents) is dropped; a restored session asks again.");
|
|
137817
139907
|
}
|
|
137818
|
-
const
|
|
137819
|
-
|
|
139908
|
+
const heir = this.host.handoffHeir(sessionId);
|
|
139909
|
+
const plan = heir ? this.host.mailHandOverPreview(sessionId, heir) : null;
|
|
139910
|
+
const mail2 = heir && plan ? null : this.host.mailWork(sessionId);
|
|
139911
|
+
const listed2 = (list) => list.slice(0, 3).map((x) => plainQuoted(x.subject, 60)).join(", ") + (list.length > 3 ? ", \u2026" : "");
|
|
139912
|
+
const threads = (n, what = "unfinished mail thread") => n === 1 ? `${/^[aeiou]/.test(what) ? "an" : "a"} ${what}` : `${n} ${what}s`;
|
|
139913
|
+
if (heir && plan) {
|
|
139914
|
+
const goes = plan.moved;
|
|
139915
|
+
if (goes.length) {
|
|
139916
|
+
const of2 = (role) => goes.filter((g5) => g5.role === role).length;
|
|
139917
|
+
const parts = [
|
|
139918
|
+
...of2("assignee") ? [`is the assignee of ${threads(of2("assignee"))}`] : [],
|
|
139919
|
+
...of2("owner") ? [`owns ${threads(of2("owner"))}`] : [],
|
|
139920
|
+
...of2("member") ? [`is in ${threads(of2("member"))} as a member`] : []
|
|
139921
|
+
];
|
|
139922
|
+
const joined = parts.length > 1 ? `${parts.slice(0, -1).join(", ")} and ${parts.at(-1)}` : parts[0];
|
|
139923
|
+
out.push(`It ${joined} (${listed2(goes)}); ${goes.length === 1 ? "it goes" : "they go"} to ${name(heir)}, which took over from it, and ${goes.length === 1 ? "stays" : "stay"} there if it is restored.`);
|
|
139924
|
+
}
|
|
139925
|
+
if (plan.closed.length) {
|
|
139926
|
+
out.push(`It has unread mail in ${threads(plan.closed.length, "closed mail thread")} (${listed2(plan.closed)}); that mail goes to ${name(heir)}.`);
|
|
139927
|
+
}
|
|
139928
|
+
const ownedLeft = plan.left.filter((l) => l.role === "owner");
|
|
139929
|
+
if (ownedLeft.length) {
|
|
139930
|
+
out.push(`It owns ${threads(ownedLeft.length)} whose assignee is ${name(heir)} (${listed2(ownedLeft)}); ${ownedLeft.length === 1 ? "it becomes" : "they become"} the person's.`);
|
|
139931
|
+
}
|
|
139932
|
+
const assignedLeft = plan.left.filter((l) => l.role === "assignee");
|
|
139933
|
+
if (assignedLeft.length) {
|
|
139934
|
+
out.push(`It is the assignee of ${threads(assignedLeft.length)} ${name(heir)} owns (${listed2(assignedLeft)}); ${assignedLeft.length === 1 ? "it goes" : "they go"} back to open with nobody on ${assignedLeft.length === 1 ? "it" : "them"}.`);
|
|
139935
|
+
}
|
|
139936
|
+
} else if (!mail2) out.push("Its mailbox could not be read, so what it holds there is not known.");
|
|
137820
139937
|
else {
|
|
137821
139938
|
const subjects = (list) => list.slice(0, 3).map((x) => plainQuoted(x, 60)).join(", ") + (list.length > 3 ? ", \u2026" : "");
|
|
137822
139939
|
if (mail2.assigned.length) {
|
|
@@ -137965,7 +140082,7 @@ var SessionTrash = class {
|
|
|
137965
140082
|
bytes: r.bytes + files,
|
|
137966
140083
|
conversationFile: !r.hasConversationFile ? "none" : record2.removeExternal ? "remove" : "keep",
|
|
137967
140084
|
// Only a worktree still on disk is listed — the merged-worktree cleanup removes the folder before the session archive is emptied
|
|
137968
|
-
worktree: r.worktree &&
|
|
140085
|
+
worktree: r.worktree && existsSync23(r.worktree.path) ? { ...r.worktree, remove: record2.removeWorktree } : null
|
|
137969
140086
|
};
|
|
137970
140087
|
})
|
|
137971
140088
|
);
|
|
@@ -138037,7 +140154,7 @@ var SessionTrash = class {
|
|
|
138037
140154
|
const home = projects.find((p) => p.id === record2.projectId) ?? (record2.projectPath ? projects.find((p) => p.path === record2.projectPath) : void 0);
|
|
138038
140155
|
if (home) {
|
|
138039
140156
|
projectId = home.id;
|
|
138040
|
-
} else if (record2.projectPath &&
|
|
140157
|
+
} else if (record2.projectPath && existsSync23(record2.projectPath) && statSync14(record2.projectPath).isDirectory()) {
|
|
138041
140158
|
this.host.store.projects.addProject({
|
|
138042
140159
|
id: record2.projectId,
|
|
138043
140160
|
path: record2.projectPath,
|
|
@@ -138106,8 +140223,8 @@ var SessionTrash = class {
|
|
|
138106
140223
|
const path2 = s.worktree.path;
|
|
138107
140224
|
const repo = record2.projectPath;
|
|
138108
140225
|
await gitWorktreeRemove(repo, path2, true).catch((e) => {
|
|
138109
|
-
if (!
|
|
138110
|
-
if (!
|
|
140226
|
+
if (!existsSync23(path2)) return;
|
|
140227
|
+
if (!existsSync23(repo)) {
|
|
138111
140228
|
throw rpcError(`The project folder ${repo} is not there any more (moved or deleted), so git cannot remove its worktree at ${path2}. It stays in the session archive: delete that folder yourself, then delete this entry again.`);
|
|
138112
140229
|
}
|
|
138113
140230
|
throw rpcError(`Could not remove the worktree at ${path2}: ${e.message.trim()}. It stays in the session archive; remove the folder or fix the cause, then delete it again.`);
|
|
@@ -138417,7 +140534,7 @@ ${list}` : `${label} has no ordinary session. The person has to open one there;
|
|
|
138417
140534
|
|
|
138418
140535
|
// packages/agent-host/src/sessions/manager.ts
|
|
138419
140536
|
var SessionManager = class {
|
|
138420
|
-
constructor(store2, adapters2, emit, endpoint, worktreeRoot =
|
|
140537
|
+
constructor(store2, adapters2, emit, endpoint, worktreeRoot = join51(homedir15(), DATA_DIR, "worktrees"), keeperOpts = {}) {
|
|
138421
140538
|
this.store = store2;
|
|
138422
140539
|
this.adapters = adapters2;
|
|
138423
140540
|
this.emit = emit;
|
|
@@ -139200,6 +141317,23 @@ var SessionManager = class {
|
|
|
139200
141317
|
const text2 = this.projectRegistry.projectTrusted(m.projectId) ? "Restarted the agent to apply this project's new trust: it now reads the project's own settings and instruction files (CLAUDE.md, AGENTS.md)." : "Restarted the agent to apply this project's removed trust: it now reads only your own settings, not the project's files.";
|
|
139201
141318
|
this.onEvent({ type: "notice", sessionId, level: "info", from: "Centralu", label: "restart", text: text2 });
|
|
139202
141319
|
}
|
|
141320
|
+
/**
|
|
141321
|
+
* The turn a kept process was running is over after a swap (#698): the session goes idle with the quit mark and its line,
|
|
141322
|
+
* and the report it owed goes now, rather than naming a later turn
|
|
141323
|
+
*/
|
|
141324
|
+
turnLostInSwap(m) {
|
|
141325
|
+
const change = { type: "state_change", sessionId: m.id, state: "idle", reason: "reattach_failed" };
|
|
141326
|
+
this.events.applyStateHint(change, m);
|
|
141327
|
+
this.interruptions.markStoppedByQuit(m, true);
|
|
141328
|
+
this.store.sessions.upsertSession(m);
|
|
141329
|
+
this.emit(change);
|
|
141330
|
+
const turnBy = this.children.turnEnded(m.id, null);
|
|
141331
|
+
if (this.children.awaitingReport.has(m.id)) void this.reports.reportBackIfAwaited(m.id, { type: "state_change", sessionId: m.id, state: "idle" }, void 0, void 0, turnBy);
|
|
141332
|
+
else {
|
|
141333
|
+
if (turnBy.by !== "person") void this.reports.tellAskerOfInterruption(m.id, this.children.turnOwners.get(m.id)?.callerId);
|
|
141334
|
+
void this.reports.reportToPersonIfOwed(m.id, { type: "state_change", sessionId: m.id, state: "idle" }, void 0, turnBy);
|
|
141335
|
+
}
|
|
141336
|
+
}
|
|
139203
141337
|
/**
|
|
139204
141338
|
* Re-attaches the tool processes a previous host left running in the keeper (#280 step 2), so
|
|
139205
141339
|
* those sessions come back live — mid-turn, with a waiting approval re-delivered by the tool —
|
|
@@ -139220,21 +141354,12 @@ var SessionManager = class {
|
|
|
139220
141354
|
const r = await this.resumeSession(k2.sessionId, k2.process).catch((err) => ({ resumed: false, reason: err.message }));
|
|
139221
141355
|
const adopted = r.resumed ? this.handles.get(k2.sessionId) : void 0;
|
|
139222
141356
|
if (adopted) this.adoptedProcesses.add(adopted);
|
|
141357
|
+
if (adopted?.takesOverTurn === false && inTurn(m.state)) this.turnLostInSwap(m);
|
|
139223
141358
|
if (this.children.queued.has(k2.sessionId)) this.queue.scheduleDrain(k2.sessionId);
|
|
139224
141359
|
if (!r.resumed) {
|
|
139225
141360
|
console.error(`[agent-host] could not re-attach ${k2.sessionId.slice(0, 8)}: ${r.reason ?? "unknown"}`);
|
|
139226
141361
|
if (inTurn(m.state)) {
|
|
139227
|
-
|
|
139228
|
-
this.events.applyStateHint(change, m);
|
|
139229
|
-
this.interruptions.markStoppedByQuit(m, true);
|
|
139230
|
-
this.store.sessions.upsertSession(m);
|
|
139231
|
-
this.emit(change);
|
|
139232
|
-
const turnBy = this.children.turnEnded(m.id, null);
|
|
139233
|
-
if (this.children.awaitingReport.has(m.id)) void this.reports.reportBackIfAwaited(m.id, { type: "state_change", sessionId: m.id, state: "idle" }, void 0, void 0, turnBy);
|
|
139234
|
-
else {
|
|
139235
|
-
if (turnBy.by !== "person") void this.reports.tellAskerOfInterruption(m.id, this.children.turnOwners.get(m.id)?.callerId);
|
|
139236
|
-
void this.reports.reportToPersonIfOwed(m.id, { type: "state_change", sessionId: m.id, state: "idle" }, void 0, turnBy);
|
|
139237
|
-
}
|
|
141362
|
+
this.turnLostInSwap(m);
|
|
139238
141363
|
} else if (this.children.pausedReports.has(m.id)) {
|
|
139239
141364
|
void this.reports.reportBackIfAwaited(m.id, { type: "state_change", sessionId: m.id, state: "idle" });
|
|
139240
141365
|
}
|
|
@@ -139468,6 +141593,10 @@ var SessionManager = class {
|
|
|
139468
141593
|
* A session is a slot, and the agent is a tool — the slot (name, order, history, grid panel) is left
|
|
139469
141594
|
* untouched, and only the tool underneath it is swapped.
|
|
139470
141595
|
*/
|
|
141596
|
+
/** Why `tool` cannot run this session as its next launch would, under `preset` (its own by default), or null (`tool-fit.ts`) */
|
|
141597
|
+
toolRefusal(m, tool = m.tool, preset = m.permissionPreset) {
|
|
141598
|
+
return this.toolRegistry.refusal(tool, sessionDemands(m, this.launchInputs.settingFilesFor(m), preset));
|
|
141599
|
+
}
|
|
139471
141600
|
async switchTool(sessionId, tool) {
|
|
139472
141601
|
this.notBeingRemoved(sessionId);
|
|
139473
141602
|
this.refuseWhileLettingGo();
|
|
@@ -139475,6 +141604,8 @@ var SessionManager = class {
|
|
|
139475
141604
|
if (m.tool === tool) return { ...m, live: this.handles.has(sessionId) };
|
|
139476
141605
|
const adapter = this.adapters.get(tool);
|
|
139477
141606
|
if (!adapter) throw rpcError(`Unknown tool: ${tool}`, "tool_not_installed");
|
|
141607
|
+
const refused2 = this.toolRefusal(m, tool);
|
|
141608
|
+
if (refused2) throw unfitError(refused2);
|
|
139478
141609
|
const d2 = await adapter.detect();
|
|
139479
141610
|
if (!d2.installed || !d2.loggedIn) {
|
|
139480
141611
|
throw rpcError(`Cannot use ${tool}: ${d2.detail}`, "tool_not_installed");
|
|
@@ -139506,7 +141637,7 @@ var SessionManager = class {
|
|
|
139506
141637
|
this.events.settleState(m, { type: "state_change", sessionId, state: "idle", reason: "tool_changed" });
|
|
139507
141638
|
return { ...m, live: false };
|
|
139508
141639
|
}
|
|
139509
|
-
respondApproval(sessionId, requestId, decision, scope,
|
|
141640
|
+
respondApproval(sessionId, requestId, decision, scope, matcher2) {
|
|
139510
141641
|
const m = this.meta.get(sessionId);
|
|
139511
141642
|
const ask = this.hostCards.hostAsks.get(requestId);
|
|
139512
141643
|
if (ask && ask.sessionId === sessionId) {
|
|
@@ -139521,7 +141652,7 @@ var SessionManager = class {
|
|
|
139521
141652
|
}
|
|
139522
141653
|
const pending = m?.pendingApproval;
|
|
139523
141654
|
const key = pending?.requestId === requestId ? alwaysAllowKey(pending.detail) : "";
|
|
139524
|
-
const remembered = decision === "always" &&
|
|
141655
|
+
const remembered = decision === "always" && matcher2 !== void 0 && key !== "" && matcher2 === key ? matcher2 : void 0;
|
|
139525
141656
|
const landed = this.requireHandle(sessionId).respondApproval(requestId, decision, scope, remembered);
|
|
139526
141657
|
if (!landed && this.hostCards.answeredApprovals.get(requestId) === sessionId) return;
|
|
139527
141658
|
if (landed) {
|
|
@@ -140067,6 +142198,15 @@ var SessionManager = class {
|
|
|
140067
142198
|
return null;
|
|
140068
142199
|
}
|
|
140069
142200
|
},
|
|
142201
|
+
mailHandOver: (...a) => self2.mailHooks?.handOver?.(...a) ?? [],
|
|
142202
|
+
mailHandOverPreview: (from, to) => {
|
|
142203
|
+
try {
|
|
142204
|
+
return self2.mailHooks?.handOverPreview?.(from, to) ?? { moved: [], closed: [], left: [] };
|
|
142205
|
+
} catch (e) {
|
|
142206
|
+
console.error(`[mail] ${from.slice(0, 8)}: what a handoff would move could not be read (${errorMessage(e)})`);
|
|
142207
|
+
return null;
|
|
142208
|
+
}
|
|
142209
|
+
},
|
|
140070
142210
|
mailGone: (id3) => {
|
|
140071
142211
|
try {
|
|
140072
142212
|
self2.mailHooks?.sessionGone(id3);
|
|
@@ -140233,6 +142373,8 @@ var SessionManager = class {
|
|
|
140233
142373
|
},
|
|
140234
142374
|
nameOf: (...a) => self2.reports.nameOf(...a),
|
|
140235
142375
|
toolProblem: (...a) => self2.toolRegistry.toolProblem(...a),
|
|
142376
|
+
toolRefusal: (...a) => self2.toolRefusal(...a),
|
|
142377
|
+
refusal: (...a) => self2.toolRegistry.refusal(...a),
|
|
140236
142378
|
trashSession: (...a) => self2.trashSession(...a),
|
|
140237
142379
|
purgeSession: (...a) => self2.purgeSession(...a),
|
|
140238
142380
|
trashIf: (...a) => self2.trash.trashIf(...a),
|
|
@@ -141394,6 +143536,91 @@ id: ${sessionIdOf(assignee)}` : "";
|
|
|
141394
143536
|
return moved;
|
|
141395
143537
|
});
|
|
141396
143538
|
}
|
|
143539
|
+
/**
|
|
143540
|
+
* A handoff moves the original session's mail to the session that took over (§4.1, #693, #738, #1574): in each thread that moves,
|
|
143541
|
+
* the successor takes the original's place, as owner, as assignee or as a member, and what the original has not read waits for the
|
|
143542
|
+
* successor instead (`moveUnreadReceipts`). Each thread keeps its state and leaves an `event`; nobody is woken inside the move
|
|
143543
|
+
* (it runs before the successor's first message); the unread mail waits for the successor's next rest or the caller's poke. The
|
|
143544
|
+
* host's own write, like `partyGone`'s: no policy or rate check, since the handoff's own checks came first (`HandoffChildren.moveChildren` trusts its caller the same way).
|
|
143545
|
+
*
|
|
143546
|
+
* `which`: `all` for an original that goes (every thread not done it is in), or the sessions whose work moved to the successor
|
|
143547
|
+
* while the original stays (the threads the original owns with one of them as assignee). A thread the successor already holds
|
|
143548
|
+
* the other side of (it owns what the original is assigned, or the reverse) stays where it is: the two sides of a thread are two
|
|
143549
|
+
* parties, and the original's departure moves it on as before (`partyGone`); it is returned as `left`, so the note says so. A
|
|
143550
|
+
* done thread the original has unread mail in (a closing report it never read) stays done and keeps its parties; the successor
|
|
143551
|
+
* joins it and the unread mail waits for it (`closed`). Called inside the transaction of the children's move
|
|
143552
|
+
* (`ChildOwnershipRecords.moveChildren`), so the mail and the children move together or not at all. Returns every thread it
|
|
143553
|
+
* looked at by what became of it, for the handoff note (`handOverPreview` reads the same plan without writing)
|
|
143554
|
+
*/
|
|
143555
|
+
handOver(fromSessionId, toSessionId, which) {
|
|
143556
|
+
if (fromSessionId === toSessionId || !this.records.hasThreads()) return [];
|
|
143557
|
+
const from = this.sessionAddress(fromSessionId);
|
|
143558
|
+
const to = this.sessionAddress(toSessionId);
|
|
143559
|
+
return this.run(() => {
|
|
143560
|
+
const now = this.now();
|
|
143561
|
+
const done2 = this.handOverPlan(from, to, which).map((p) => {
|
|
143562
|
+
const { thread: t, role, kind } = p;
|
|
143563
|
+
if (kind === "left") {
|
|
143564
|
+
this.records.handleUnread(t.id, from, now);
|
|
143565
|
+
return { ...p, unread: 0 };
|
|
143566
|
+
}
|
|
143567
|
+
if (kind === "closed") {
|
|
143568
|
+
this.records.putParticipantIfAbsent(t.id, to, "member", now);
|
|
143569
|
+
return { ...p, unread: this.records.moveUnreadReceipts(t.id, from, to, now) };
|
|
143570
|
+
}
|
|
143571
|
+
this.records.leaveParticipant(t.id, from, now);
|
|
143572
|
+
if (role === "member") this.records.putParticipantIfAbsent(t.id, to, "member", now);
|
|
143573
|
+
else this.records.putParticipant(t.id, to, role, now);
|
|
143574
|
+
const facts = { change: "handed_over", party: from, to, role };
|
|
143575
|
+
const after = role === "member" ? (this.insertEvent(t.id, "The session in this thread handed over: the session that took over is in it instead.", facts, now), t) : this.moveState(
|
|
143576
|
+
t,
|
|
143577
|
+
role === "owner" ? { owner: to } : { assignee: to },
|
|
143578
|
+
now,
|
|
143579
|
+
facts,
|
|
143580
|
+
role === "owner" ? "The owner handed over: the session that took over owns the thread." : "The assignee handed over: the session that took over has the work."
|
|
143581
|
+
);
|
|
143582
|
+
return { thread: after, role, kind, unread: this.records.moveUnreadReceipts(t.id, from, to, now) };
|
|
143583
|
+
});
|
|
143584
|
+
if (which === "all") for (const t of this.records.doneThreadsWithUnread(from)) this.records.handleUnread(t.id, from, now);
|
|
143585
|
+
return done2;
|
|
143586
|
+
});
|
|
143587
|
+
}
|
|
143588
|
+
/**
|
|
143589
|
+
* What `handOver` would do with the original's mail, read only: the archive preview of an original with an heir says it thread by
|
|
143590
|
+
* thread (#1574), from the same plan the move follows
|
|
143591
|
+
*/
|
|
143592
|
+
handOverPreview(fromSessionId, toSessionId, which = "all") {
|
|
143593
|
+
const preview = { moved: [], closed: [], left: [] };
|
|
143594
|
+
if (fromSessionId === toSessionId || !this.records.hasThreads()) return preview;
|
|
143595
|
+
for (const p of this.handOverPlan(this.sessionAddress(fromSessionId), this.sessionAddress(toSessionId), which)) preview[p.kind].push({ subject: p.thread.subject, role: p.role });
|
|
143596
|
+
return preview;
|
|
143597
|
+
}
|
|
143598
|
+
/**
|
|
143599
|
+
* The threads a handoff looks at, and what becomes of each (`HandedOverThread.kind`): those not done the original owns, is assigned or
|
|
143600
|
+
* is in, and the done ones it has unread mail in; with `assignees`, only those it owns whose assignee is one of them. A thread in a
|
|
143601
|
+
* state a newer build wrote is not touched
|
|
143602
|
+
*/
|
|
143603
|
+
handOverPlan(from, to, which) {
|
|
143604
|
+
const assignees = which === "all" ? null : new Set(which.assignees.map((id3) => this.sessionAddress(id3)));
|
|
143605
|
+
const open6 = ["open", "running", "review", "blocked"];
|
|
143606
|
+
const threads = /* @__PURE__ */ new Map();
|
|
143607
|
+
for (const t of [
|
|
143608
|
+
...this.records.threadsOfParty(from, "owner", open6),
|
|
143609
|
+
...this.records.threadsOfParty(from, "assignee", open6),
|
|
143610
|
+
...this.records.openThreadsWith(from),
|
|
143611
|
+
...this.records.doneThreadsWithUnread(from)
|
|
143612
|
+
]) threads.set(t.id, t);
|
|
143613
|
+
const plan = [];
|
|
143614
|
+
for (const t of threads.values()) {
|
|
143615
|
+
if (t.state === "unknown") continue;
|
|
143616
|
+
const role = t.owner === from ? "owner" : t.assignee === from ? "assignee" : "member";
|
|
143617
|
+
if (assignees && !(role === "owner" && t.assignee !== null && assignees.has(t.assignee))) continue;
|
|
143618
|
+
if (t.state === "done" && (assignees || this.records.movableUnread(t.id, from, to) === 0)) continue;
|
|
143619
|
+
const kind = t.state === "done" ? "closed" : role === "owner" && t.assignee === to || role === "assignee" && t.owner === to ? "left" : "moved";
|
|
143620
|
+
plan.push({ thread: t, role, kind });
|
|
143621
|
+
}
|
|
143622
|
+
return plan;
|
|
143623
|
+
}
|
|
141397
143624
|
/**
|
|
141398
143625
|
* A turn of delegated work ended (§6): Centralu's `report` in the thread, for its owner, and the thread's state moves with it: `review`
|
|
141399
143626
|
* when the work was reported, `running` when it paused on the session's own background work, and `done` (`dropped`, taken over) when the
|
|
@@ -142966,13 +145193,13 @@ function mailRelations(host) {
|
|
|
142966
145193
|
}
|
|
142967
145194
|
|
|
142968
145195
|
// packages/agent-host/src/rpc.ts
|
|
142969
|
-
import { relative as
|
|
145196
|
+
import { relative as relative9 } from "node:path";
|
|
142970
145197
|
|
|
142971
145198
|
// packages/agent-host/src/themes.ts
|
|
142972
|
-
import { randomBytes as
|
|
145199
|
+
import { randomBytes as randomBytes9 } from "node:crypto";
|
|
142973
145200
|
import { watch as watch2 } from "node:fs";
|
|
142974
145201
|
import { mkdir as mkdir7, readdir as readdir13, readFile as readFile7, rename as rename5, stat as stat9, unlink as unlink2, writeFile as writeFile5 } from "node:fs/promises";
|
|
142975
|
-
import { basename as basename13, join as
|
|
145202
|
+
import { basename as basename13, join as join52 } from "node:path";
|
|
142976
145203
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
142977
145204
|
var SETTLE_MS = 100;
|
|
142978
145205
|
var ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,59}$/;
|
|
@@ -143017,7 +145244,7 @@ var ThemeFiles = class {
|
|
|
143017
145244
|
* a token updates it, and an unchanged one does not touch the folder (or wake the watcher).
|
|
143018
145245
|
*/
|
|
143019
145246
|
async writeSchema() {
|
|
143020
|
-
const path2 =
|
|
145247
|
+
const path2 = join52(this.dir, THEME_SCHEMA_FILE);
|
|
143021
145248
|
const text2 = `${JSON.stringify(themeFileJsonSchema(), null, 2)}
|
|
143022
145249
|
`;
|
|
143023
145250
|
const current = await readFile7(path2, "utf8").catch(() => null);
|
|
@@ -143066,7 +145293,7 @@ var ThemeFiles = class {
|
|
|
143066
145293
|
/** The absolute path for an id, for the shell to trash or reveal. Refuses anything that is not an id. */
|
|
143067
145294
|
pathOf(id3) {
|
|
143068
145295
|
if (!ID_PATTERN.test(id3)) throw rpcError(`Not a theme id: ${id3}`);
|
|
143069
|
-
return
|
|
145296
|
+
return join52(this.dir, `${id3}.json`);
|
|
143070
145297
|
}
|
|
143071
145298
|
async freeId(name) {
|
|
143072
145299
|
const base2 = name.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "theme";
|
|
@@ -143078,11 +145305,11 @@ var ThemeFiles = class {
|
|
|
143078
145305
|
);
|
|
143079
145306
|
if (!taken) return id3;
|
|
143080
145307
|
}
|
|
143081
|
-
return `${base2}-${
|
|
145308
|
+
return `${base2}-${randomBytes9(4).toString("hex")}`;
|
|
143082
145309
|
}
|
|
143083
145310
|
/** Temp file and rename: a reader (the watcher, an editor, an agent) never sees half a file */
|
|
143084
145311
|
async atomicWrite(path2, text2) {
|
|
143085
|
-
const tmp = `${path2}.${
|
|
145312
|
+
const tmp = `${path2}.${randomBytes9(6).toString("hex")}.tmp`;
|
|
143086
145313
|
try {
|
|
143087
145314
|
await writeFile5(tmp, text2, "utf8");
|
|
143088
145315
|
await rename5(tmp, path2);
|
|
@@ -143095,11 +145322,11 @@ var ThemeFiles = class {
|
|
|
143095
145322
|
};
|
|
143096
145323
|
|
|
143097
145324
|
// packages/agent-host/src/dev-services/strays.ts
|
|
143098
|
-
import { execFile as
|
|
143099
|
-
import { realpathSync as
|
|
143100
|
-
import { promisify as
|
|
145325
|
+
import { execFile as execFile12 } from "node:child_process";
|
|
145326
|
+
import { realpathSync as realpathSync11 } from "node:fs";
|
|
145327
|
+
import { promisify as promisify11 } from "node:util";
|
|
143101
145328
|
import { userInfo } from "node:os";
|
|
143102
|
-
var
|
|
145329
|
+
var exec10 = promisify11(execFile12);
|
|
143103
145330
|
function parsePsRows(out) {
|
|
143104
145331
|
const rows3 = [];
|
|
143105
145332
|
for (const line of out.split("\n")) {
|
|
@@ -143165,7 +145392,7 @@ function pickStrays(rows3, cwdOf, roots, selfPid, held3 = []) {
|
|
|
143165
145392
|
}
|
|
143166
145393
|
async function psRows() {
|
|
143167
145394
|
try {
|
|
143168
|
-
const { stdout } = await
|
|
145395
|
+
const { stdout } = await exec10("ps", ["-U", userInfo().username, "-o", "pid=,ppid=,tty=,command="], {
|
|
143169
145396
|
maxBuffer: 8 * 1024 * 1024,
|
|
143170
145397
|
timeout: 5e3,
|
|
143171
145398
|
windowsHide: true
|
|
@@ -143178,7 +145405,7 @@ async function psRows() {
|
|
|
143178
145405
|
async function cwdsOf(pids) {
|
|
143179
145406
|
if (pids.length === 0) return /* @__PURE__ */ new Map();
|
|
143180
145407
|
try {
|
|
143181
|
-
const { stdout } = await
|
|
145408
|
+
const { stdout } = await exec10("lsof", ["-a", "-d", "cwd", "-p", pids.join(","), "-Fpn"], {
|
|
143182
145409
|
maxBuffer: 8 * 1024 * 1024,
|
|
143183
145410
|
timeout: 5e3,
|
|
143184
145411
|
windowsHide: true
|
|
@@ -143201,7 +145428,7 @@ function resolveRoots(roots) {
|
|
|
143201
145428
|
for (const r of roots) {
|
|
143202
145429
|
out.add(r);
|
|
143203
145430
|
try {
|
|
143204
|
-
out.add(
|
|
145431
|
+
out.add(realpathSync11(r));
|
|
143205
145432
|
} catch {
|
|
143206
145433
|
}
|
|
143207
145434
|
}
|
|
@@ -143383,8 +145610,8 @@ function createRpcHandler(mgr2, adapters2, { settleDeletedProjects, deleteOutloo
|
|
|
143383
145610
|
},
|
|
143384
145611
|
"agents.reportOwed": async (p) => ({ owed: mgr2.reportOwed(RpcMethods["agents.reportOwed"].params.parse(p).sessionId) }),
|
|
143385
145612
|
"agents.respondApproval": async (p) => {
|
|
143386
|
-
const { sessionId, requestId, decision, scope, matcher } = RpcMethods["agents.respondApproval"].params.parse(p);
|
|
143387
|
-
mgr2.respondApproval(sessionId, requestId, decision, scope,
|
|
145613
|
+
const { sessionId, requestId, decision, scope, matcher: matcher2 } = RpcMethods["agents.respondApproval"].params.parse(p);
|
|
145614
|
+
mgr2.respondApproval(sessionId, requestId, decision, scope, matcher2);
|
|
143388
145615
|
return { ok: true };
|
|
143389
145616
|
},
|
|
143390
145617
|
"agents.answerQuestion": async (p) => {
|
|
@@ -143735,7 +145962,7 @@ function createRpcHandler(mgr2, adapters2, { settleDeletedProjects, deleteOutloo
|
|
|
143735
145962
|
const project2 = (await mgr2.listProjects()).find((x) => x.id === projectId);
|
|
143736
145963
|
const app2 = apps.list().find((a) => a.appId === appId && a.projectId === projectId);
|
|
143737
145964
|
if (!project2 || !app2) throw rpcError(noSuchAppText({ projectId, appId }));
|
|
143738
|
-
const { repo, commits } = await gitLogPath(project2.path,
|
|
145965
|
+
const { repo, commits } = await gitLogPath(project2.path, relative9(project2.path, app2.dir), 20);
|
|
143739
145966
|
return { kind: "git", repo, commits };
|
|
143740
145967
|
},
|
|
143741
145968
|
"apps.restoreVersion": async (p) => {
|
|
@@ -144460,7 +146687,7 @@ var sameCause = (a, b2) => a !== null && b2 !== null && JSON.stringify(a) === JS
|
|
|
144460
146687
|
|
|
144461
146688
|
// packages/agent-host/src/dev-services/terminal.ts
|
|
144462
146689
|
import { createRequire } from "node:module";
|
|
144463
|
-
import { existsSync as
|
|
146690
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
144464
146691
|
|
|
144465
146692
|
// packages/agent-host/src/dev-services/pty-slot.ts
|
|
144466
146693
|
function isPtyModule(m) {
|
|
@@ -144792,9 +147019,9 @@ function commandShell(command, platform = process.platform, env = process.env) {
|
|
|
144792
147019
|
}
|
|
144793
147020
|
function shellPath() {
|
|
144794
147021
|
const fromEnv = process.env.SHELL;
|
|
144795
|
-
if (fromEnv &&
|
|
147022
|
+
if (fromEnv && existsSync24(fromEnv)) return fromEnv;
|
|
144796
147023
|
for (const candidate of ["/bin/zsh", "/bin/bash", "/bin/sh"]) {
|
|
144797
|
-
if (
|
|
147024
|
+
if (existsSync24(candidate)) return candidate;
|
|
144798
147025
|
}
|
|
144799
147026
|
return process.platform === "win32" ? "powershell.exe" : "/bin/sh";
|
|
144800
147027
|
}
|
|
@@ -144941,9 +147168,9 @@ var CommandRunner = class {
|
|
|
144941
147168
|
};
|
|
144942
147169
|
|
|
144943
147170
|
// packages/agent-host/src/updates.ts
|
|
144944
|
-
import { execFile as
|
|
144945
|
-
import { existsSync as
|
|
144946
|
-
import { join as
|
|
147171
|
+
import { execFile as execFile13, execFileSync as execFileSync3 } from "node:child_process";
|
|
147172
|
+
import { existsSync as existsSync25, readFileSync as readFileSync20 } from "node:fs";
|
|
147173
|
+
import { join as join53 } from "node:path";
|
|
144947
147174
|
var REGISTRY_URL = `https://registry.npmjs.org/${APP_SLUG}/latest`;
|
|
144948
147175
|
var CHECK_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
144949
147176
|
var SCHEDULED_TIMEOUT_MS = 3e3;
|
|
@@ -144963,13 +147190,13 @@ function hubUpdatesMachines(f) {
|
|
|
144963
147190
|
}
|
|
144964
147191
|
function existingInstalledCopy() {
|
|
144965
147192
|
const path2 = installedCopyPath();
|
|
144966
|
-
return
|
|
147193
|
+
return existsSync25(path2) ? path2 : null;
|
|
144967
147194
|
}
|
|
144968
147195
|
function readInstalledCopyVersion(copy, platform = HOST_PLATFORM) {
|
|
144969
147196
|
const source = installedCopyVersionSource(copy, platform);
|
|
144970
147197
|
if (!source) return null;
|
|
144971
147198
|
try {
|
|
144972
|
-
const text2 = source.kind === "command" ? execFileSync3(source.file, source.args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3, windowsHide: true }) :
|
|
147199
|
+
const text2 = source.kind === "command" ? execFileSync3(source.file, source.args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3, windowsHide: true }) : readFileSync20(join53(copy, source.name), "utf8");
|
|
144973
147200
|
return text2.trim() || null;
|
|
144974
147201
|
} catch {
|
|
144975
147202
|
return null;
|
|
@@ -144998,7 +147225,7 @@ function failureDetail(file2, stderr) {
|
|
|
144998
147225
|
function runCommand(file2, args, resolve21) {
|
|
144999
147226
|
const cmd = commandFor(file2, args, resolve21);
|
|
145000
147227
|
return new Promise((done2, reject) => {
|
|
145001
|
-
|
|
147228
|
+
execFile13(cmd.file, cmd.args, { timeout: INSTALL_TIMEOUT_MS, windowsHide: true }, (err, _stdout, stderr) => {
|
|
145002
147229
|
if (!err) return done2();
|
|
145003
147230
|
if (err.code === "ENOENT") {
|
|
145004
147231
|
return reject(new Error(`${file2} was not found on PATH (${err.message})`));
|
|
@@ -145729,8 +147956,8 @@ var SessionSleeper = class {
|
|
|
145729
147956
|
|
|
145730
147957
|
// packages/agent-host/src/dev-services/instance-lock.ts
|
|
145731
147958
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
145732
|
-
import { readFileSync as
|
|
145733
|
-
import { dirname as
|
|
147959
|
+
import { readFileSync as readFileSync21, unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "node:fs";
|
|
147960
|
+
import { dirname as dirname25, join as join54 } from "node:path";
|
|
145734
147961
|
import Database3 from "better-sqlite3";
|
|
145735
147962
|
var OWNERSHIP_FILE = "host-ownership.sqlite";
|
|
145736
147963
|
var held = /* @__PURE__ */ new Set();
|
|
@@ -145776,15 +148003,15 @@ function parseHolder(raw) {
|
|
|
145776
148003
|
function acquireInstanceLock(dbPath2, startOf = processStartTime, describe4 = {}, now = Date.now) {
|
|
145777
148004
|
if (dbPath2 === ":memory:") return { ok: true, release: () => {
|
|
145778
148005
|
} };
|
|
145779
|
-
const lockPath =
|
|
148006
|
+
const lockPath = join54(dirname25(dbPath2), "host.lock");
|
|
145780
148007
|
const readHolder = () => {
|
|
145781
148008
|
try {
|
|
145782
|
-
return parseHolder(
|
|
148009
|
+
return parseHolder(readFileSync21(lockPath, "utf8"));
|
|
145783
148010
|
} catch {
|
|
145784
148011
|
return null;
|
|
145785
148012
|
}
|
|
145786
148013
|
};
|
|
145787
|
-
const owner = takeOwnership(
|
|
148014
|
+
const owner = takeOwnership(join54(dirname25(dbPath2), OWNERSHIP_FILE));
|
|
145788
148015
|
if (!owner) {
|
|
145789
148016
|
const holder = readHolder();
|
|
145790
148017
|
const pid = holder && Number.isInteger(holder.pid) && holder.pid > 0 ? holder.pid : null;
|
|
@@ -145801,7 +148028,7 @@ function acquireInstanceLock(dbPath2, startOf = processStartTime, describe4 = {}
|
|
|
145801
148028
|
if (released) return;
|
|
145802
148029
|
released = true;
|
|
145803
148030
|
try {
|
|
145804
|
-
if (parseHolder(
|
|
148031
|
+
if (parseHolder(readFileSync21(lockPath, "utf8")).pid === process.pid) unlinkSync3(lockPath);
|
|
145805
148032
|
} catch {
|
|
145806
148033
|
}
|
|
145807
148034
|
giveUp(owner);
|
|
@@ -145880,8 +148107,8 @@ function methodParams(method) {
|
|
|
145880
148107
|
var EFFECTS = {
|
|
145881
148108
|
"agents.deleteSession": (p) => "The session moves to the session archive" + (p.deleteWorktree === true ? ", and its worktree is marked to be deleted when the session is deleted from the archive for good" : "") + (p.deleteExternal === true ? ", and the tool's own conversation file with it" : "") + ".",
|
|
145882
148109
|
"agents.archiveIfAtRest": () => "The session moves to the session archive if that loses nothing it holds.",
|
|
145883
|
-
"agents.deleteAgentChild": (p) => `The session moves to the session archive, and the worktree on the branch ${plainQuoted(
|
|
145884
|
-
"agents.deleteOrchestratorSkill": (p) => `The orchestrator skill ${plainQuoted(
|
|
148110
|
+
"agents.deleteAgentChild": (p) => `The session moves to the session archive, and the worktree on the branch ${plainQuoted(str3(p.worktreeBranch), 120)} is deleted when nothing in it would be lost.`,
|
|
148111
|
+
"agents.deleteOrchestratorSkill": (p) => `The orchestrator skill ${plainQuoted(str3(p.name), 120)} is deleted.`,
|
|
145885
148112
|
"projects.delete": () => "Centralu's records of the project are erased: its sessions, their conversations, its search index and approval rules. Its folder stays.",
|
|
145886
148113
|
"apps.remove": () => "The app's folder moves to app-trash in the data folder, and it is taken off every session it was attached to.",
|
|
145887
148114
|
"machines.remove": () => "The machine is unlinked: its sessions and projects leave this computer's lists.",
|
|
@@ -145942,7 +148169,7 @@ var quoted3 = (name) => plainQuoted(name, 120);
|
|
|
145942
148169
|
function fields2(v) {
|
|
145943
148170
|
return v !== null && typeof v === "object" && !Array.isArray(v) ? { ...v } : {};
|
|
145944
148171
|
}
|
|
145945
|
-
var
|
|
148172
|
+
var str3 = (v) => typeof v === "string" ? v : "";
|
|
145946
148173
|
|
|
145947
148174
|
// packages/agent-host/src/host-requests.ts
|
|
145948
148175
|
var HOST_REQUEST_LIMITS = { maxOpen: 8, lifetimeMs: 15 * 6e4, windowGoneMs: 3e4 };
|
|
@@ -145976,9 +148203,9 @@ var HostRequests = class {
|
|
|
145976
148203
|
const p = fields3(params);
|
|
145977
148204
|
switch (method) {
|
|
145978
148205
|
case "trash.purge":
|
|
145979
|
-
return this.viaTrash(method, from, signal, { action: "delete", sessionId:
|
|
148206
|
+
return this.viaTrash(method, from, signal, { action: "delete", sessionId: str4(p.sessionId), what: "session" }, () => ({ ok: true }));
|
|
145980
148207
|
case "trash.purgeSubagentSteps":
|
|
145981
|
-
return this.viaTrash(method, from, signal, { action: "delete", sessionId:
|
|
148208
|
+
return this.viaTrash(method, from, signal, { action: "delete", sessionId: str4(p.sessionId), what: "steps" }, () => ({ ok: true }));
|
|
145982
148209
|
case "trash.empty":
|
|
145983
148210
|
return this.viaTrash(method, from, signal, { action: "empty" }, (purged, text2, failed3) => ({ purged, failed: failed3, text: text2 }));
|
|
145984
148211
|
default:
|
|
@@ -146073,7 +148300,7 @@ var HostRequests = class {
|
|
|
146073
148300
|
return r;
|
|
146074
148301
|
} catch (e) {
|
|
146075
148302
|
if (m === "trash.purge") {
|
|
146076
|
-
const sessionId =
|
|
148303
|
+
const sessionId = str4(fields3(p).sessionId);
|
|
146077
148304
|
failed3.push({ sessionId, name: names.get(sessionId) ?? sessionId, error: e instanceof Error ? e.message : String(e) });
|
|
146078
148305
|
}
|
|
146079
148306
|
throw e;
|
|
@@ -146121,7 +148348,7 @@ var HostRequests = class {
|
|
|
146121
148348
|
function fields3(params) {
|
|
146122
148349
|
return params !== null && typeof params === "object" && !Array.isArray(params) ? { ...params } : {};
|
|
146123
148350
|
}
|
|
146124
|
-
var
|
|
148351
|
+
var str4 = (v) => typeof v === "string" ? v : "";
|
|
146125
148352
|
function refusalOf(outcome) {
|
|
146126
148353
|
if (outcome === "too_many_requests" || outcome === "no_window") return outcome;
|
|
146127
148354
|
return outcome === "denied" ? "denied" : outcome === "expired" ? "expired" : "withdrawn";
|
|
@@ -146331,7 +148558,7 @@ var Shutdown = class {
|
|
|
146331
148558
|
var SHUTDOWN_BOUND_MS = 1e4;
|
|
146332
148559
|
|
|
146333
148560
|
// packages/agent-host/src/lifecycle/endings.ts
|
|
146334
|
-
import { appendFileSync as
|
|
148561
|
+
import { appendFileSync as appendFileSync3 } from "node:fs";
|
|
146335
148562
|
var SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
146336
148563
|
var REPEAT_AFTER_MS = 1e3;
|
|
146337
148564
|
function deliberateRepeat(first, now) {
|
|
@@ -146380,7 +148607,7 @@ function crashRecorder(path2, log, maxBytes = MAX_LOG_BYTES) {
|
|
|
146380
148607
|
log(`[agent-host] ${kind} ${detail}`);
|
|
146381
148608
|
try {
|
|
146382
148609
|
rotateIfLarge(path2, maxBytes);
|
|
146383
|
-
|
|
148610
|
+
appendFileSync3(path2, `[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind}: ${detail}
|
|
146384
148611
|
`);
|
|
146385
148612
|
} catch {
|
|
146386
148613
|
}
|
|
@@ -146415,17 +148642,17 @@ function wireEndings(o) {
|
|
|
146415
148642
|
|
|
146416
148643
|
// packages/agent-host/src/lifecycle/stopped-mark.ts
|
|
146417
148644
|
import { rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
146418
|
-
import { join as
|
|
148645
|
+
import { join as join55 } from "node:path";
|
|
146419
148646
|
var STOPPED_MARK_FILE = "stopped.json";
|
|
146420
148647
|
function clearStoppedMark(dataDir) {
|
|
146421
148648
|
try {
|
|
146422
|
-
rmSync7(
|
|
148649
|
+
rmSync7(join55(dataDir, STOPPED_MARK_FILE), { force: true });
|
|
146423
148650
|
} catch {
|
|
146424
148651
|
}
|
|
146425
148652
|
}
|
|
146426
148653
|
function markQuit(dataDir, now = Date.now()) {
|
|
146427
148654
|
try {
|
|
146428
|
-
writeFileSync11(
|
|
148655
|
+
writeFileSync11(join55(dataDir, STOPPED_MARK_FILE), `${JSON.stringify({ v: 1, by: "quit", at: now })}
|
|
146429
148656
|
`);
|
|
146430
148657
|
} catch {
|
|
146431
148658
|
}
|
|
@@ -146566,7 +148793,7 @@ async function runDrain(bound, deps) {
|
|
|
146566
148793
|
}
|
|
146567
148794
|
|
|
146568
148795
|
// packages/agent-host/src/links/install.ts
|
|
146569
|
-
import { existsSync as
|
|
148796
|
+
import { existsSync as existsSync26, readFileSync as readFileSync22 } from "node:fs";
|
|
146570
148797
|
import { hostname as hostname4 } from "node:os";
|
|
146571
148798
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
146572
148799
|
import { gzipSync } from "node:zlib";
|
|
@@ -146575,7 +148802,7 @@ import { gzipSync } from "node:zlib";
|
|
|
146575
148802
|
import { createPublicKey, verify } from "node:crypto";
|
|
146576
148803
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
146577
148804
|
var PACKAGE_NAME = /^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
146578
|
-
var
|
|
148805
|
+
var VERSION2 = /^[0-9A-Za-z][0-9A-Za-z.+-]{0,63}$/;
|
|
146579
148806
|
var NotPublished = class extends Error {
|
|
146580
148807
|
constructor(id3) {
|
|
146581
148808
|
super(`${id3} is not on the npm registry`);
|
|
@@ -146596,7 +148823,7 @@ async function getJson(url2, f, id3) {
|
|
|
146596
148823
|
return res.json();
|
|
146597
148824
|
}
|
|
146598
148825
|
async function verifiedPackage(name, version2, opts = {}) {
|
|
146599
|
-
if (!PACKAGE_NAME.test(name) || !
|
|
148826
|
+
if (!PACKAGE_NAME.test(name) || !VERSION2.test(version2)) throw new Error(`Not a package to install: ${name}@${version2}`);
|
|
146600
148827
|
const registry2 = (opts.registry ?? NPM_REGISTRY).replace(/\/+$/, "");
|
|
146601
148828
|
const f = opts.fetch ?? globalThis.fetch;
|
|
146602
148829
|
const now = opts.now?.() ?? Date.now();
|
|
@@ -146694,7 +148921,7 @@ function updateNodeSentence(o) {
|
|
|
146694
148921
|
// packages/agent-host/src/links/tunnel.ts
|
|
146695
148922
|
import { execFileSync as execFileSync5, spawn as spawn5 } from "node:child_process";
|
|
146696
148923
|
import { createConnection as createConnection2, createServer as createServer3 } from "node:net";
|
|
146697
|
-
import { win32 as
|
|
148924
|
+
import { win32 as win329 } from "node:path";
|
|
146698
148925
|
import { hostname as hostname3 } from "node:os";
|
|
146699
148926
|
|
|
146700
148927
|
// packages/agent-host/src/links/ssh-run.ts
|
|
@@ -147315,8 +149542,8 @@ function ansiText(bytes2) {
|
|
|
147315
149542
|
ansiDecoder = null;
|
|
147316
149543
|
if (process.platform !== "win32") return null;
|
|
147317
149544
|
try {
|
|
147318
|
-
const root3 = process.env.SystemRoot &&
|
|
147319
|
-
const out = execFileSync5(
|
|
149545
|
+
const root3 = process.env.SystemRoot && win329.isAbsolute(process.env.SystemRoot) ? process.env.SystemRoot : "C:\\Windows";
|
|
149546
|
+
const out = execFileSync5(win329.join(root3, "System32", "reg.exe"), ["query", "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage", "/v", "ACP"], { encoding: "utf8", timeout: 5e3, windowsHide: true });
|
|
147320
149547
|
const page = /\bACP\s+REG_SZ\s+(\d+)/.exec(out)?.[1];
|
|
147321
149548
|
if (page) ansiDecoder = new TextDecoder(CODE_PAGE_NAMES[page] ?? `windows-${page}`, { fatal: true });
|
|
147322
149549
|
} catch {
|
|
@@ -147606,7 +149833,7 @@ async function installRemote(o) {
|
|
|
147606
149833
|
function bundled(name, source) {
|
|
147607
149834
|
for (const url2 of [new URL(`./${name}`, import.meta.url), new URL(source, import.meta.url)]) {
|
|
147608
149835
|
const file2 = fileURLToPath6(url2);
|
|
147609
|
-
if (
|
|
149836
|
+
if (existsSync26(file2)) return readFileSync22(file2, "utf8");
|
|
147610
149837
|
}
|
|
147611
149838
|
throw new Error(`${name} is missing from this host`);
|
|
147612
149839
|
}
|
|
@@ -147677,7 +149904,7 @@ async function removeAutostart(c) {
|
|
|
147677
149904
|
}
|
|
147678
149905
|
|
|
147679
149906
|
// packages/agent-host/src/links/machine-ids.ts
|
|
147680
|
-
import { randomBytes as
|
|
149907
|
+
import { randomBytes as randomBytes10 } from "node:crypto";
|
|
147681
149908
|
function isMachineId(value) {
|
|
147682
149909
|
return MACHINE_ID_RE.test(value);
|
|
147683
149910
|
}
|
|
@@ -147705,10 +149932,10 @@ function decodeNumber(value) {
|
|
|
147705
149932
|
}
|
|
147706
149933
|
function newMachineId(name, taken) {
|
|
147707
149934
|
const slug = name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/, "").slice(0, 24);
|
|
147708
|
-
const base2 = slug && MACHINE_ID_RE.test(slug) ? slug : `m-${
|
|
149935
|
+
const base2 = slug && MACHINE_ID_RE.test(slug) ? slug : `m-${randomBytes10(3).toString("hex")}`;
|
|
147709
149936
|
if (!taken.has(base2)) return base2;
|
|
147710
149937
|
for (let i = 2; i < 1e3; i++) if (!taken.has(`${base2}-${i}`)) return `${base2}-${i}`;
|
|
147711
|
-
return `m-${
|
|
149938
|
+
return `m-${randomBytes10(4).toString("hex")}`;
|
|
147712
149939
|
}
|
|
147713
149940
|
|
|
147714
149941
|
// packages/agent-host/src/links/qualifier.ts
|
|
@@ -149438,14 +151665,14 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
149438
151665
|
* again at once, and finds the managed launcher first
|
|
149439
151666
|
*/
|
|
149440
151667
|
async install() {
|
|
149441
|
-
const
|
|
149442
|
-
return this.operate("install",
|
|
149443
|
-
if (
|
|
151668
|
+
const pick4 = await this.versionToInstall();
|
|
151669
|
+
return this.operate("install", pick4.version, async (c, installer) => {
|
|
151670
|
+
if (pick4.note) this.deps.log?.(`[links] ${this.name}: ${pick4.note}`);
|
|
149444
151671
|
const r = await installRemote({
|
|
149445
151672
|
exec: c.exec,
|
|
149446
151673
|
spec: c.spec,
|
|
149447
151674
|
target: c.target,
|
|
149448
|
-
version:
|
|
151675
|
+
version: pick4.version,
|
|
149449
151676
|
runtime: installer.runtime(),
|
|
149450
151677
|
script: c.script,
|
|
149451
151678
|
registry: installer.registry,
|
|
@@ -149454,7 +151681,7 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
149454
151681
|
});
|
|
149455
151682
|
this.removedByPerson = false;
|
|
149456
151683
|
this.deps.log?.(`[links] ${this.name}: installed Centralu ${r.current.version} on Node ${r.current.node}${r.previous ? `; ${r.previous.version} kept to roll back to` : ""}`);
|
|
149457
|
-
return { ...this.installedNow(r), ...
|
|
151684
|
+
return { ...this.installedNow(r), ...pick4.note ? { note: pick4.note } : {} };
|
|
149458
151685
|
});
|
|
149459
151686
|
}
|
|
149460
151687
|
/**
|
|
@@ -149468,22 +151695,22 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
149468
151695
|
}
|
|
149469
151696
|
async updateNow(opts) {
|
|
149470
151697
|
if (opts.unasked === true) await this.probeActivity();
|
|
149471
|
-
const
|
|
151698
|
+
const pick4 = await this.versionToInstall();
|
|
149472
151699
|
const there = this.versions?.remote.version;
|
|
149473
|
-
if (there && releaseOf(there) !==
|
|
149474
|
-
throw linkError2(`${this.name} runs Centralu ${there}, newer than ${
|
|
151700
|
+
if (there && releaseOf(there) !== pick4.version && isNewerVersion(there, pick4.version)) {
|
|
151701
|
+
throw linkError2(`${this.name} runs Centralu ${there}, newer than ${pick4.version}, the version this computer would install; updating would downgrade it, so nothing was changed`);
|
|
149475
151702
|
}
|
|
149476
|
-
return this.operate("update",
|
|
149477
|
-
if (
|
|
151703
|
+
return this.operate("update", pick4.version, async (c, installer) => {
|
|
151704
|
+
if (pick4.note) this.deps.log?.(`[links] ${this.name}: ${pick4.note}`);
|
|
149478
151705
|
c.beforeStop = () => this.beforeStop(c, opts.when ?? "idle", opts.unasked === true);
|
|
149479
151706
|
const r = await updateRemote(c, {
|
|
149480
|
-
version:
|
|
151707
|
+
version: pick4.version,
|
|
149481
151708
|
runtime: installer.runtime(),
|
|
149482
151709
|
registry: installer.registry,
|
|
149483
151710
|
nodeDist: installer.nodeDist
|
|
149484
151711
|
});
|
|
149485
151712
|
this.deps.log?.(`[links] ${this.name}: updated to Centralu ${r.current.version}${r.previous ? `; ${r.previous.version} kept to roll back to` : ""}`);
|
|
149486
|
-
const note = [
|
|
151713
|
+
const note = [pick4.note, r.note].filter(Boolean).join(" ");
|
|
149487
151714
|
return { ...this.installedNow(r), ...note ? { note } : {} };
|
|
149488
151715
|
});
|
|
149489
151716
|
}
|
|
@@ -149546,42 +151773,48 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
149546
151773
|
let shown3 = null;
|
|
149547
151774
|
let seen = false;
|
|
149548
151775
|
let blind = 0;
|
|
149549
|
-
const show = (step, why) => {
|
|
149550
|
-
|
|
149551
|
-
shown3
|
|
149552
|
-
|
|
151776
|
+
const show = (step, why, count) => {
|
|
151777
|
+
const key = `${step}:${count ?? ""}`;
|
|
151778
|
+
if (shown3 === key) return;
|
|
151779
|
+
shown3 = key;
|
|
151780
|
+
c.step(step, count);
|
|
149553
151781
|
this.deps.log?.(`[links] ${this.name}: ${why}`);
|
|
149554
151782
|
};
|
|
149555
151783
|
const unread = () => show("wait_unread", "what runs there cannot be read now; the update keeps waiting, since the sessions there may be in a turn (Cancel ends it)");
|
|
149556
|
-
const busy = (
|
|
151784
|
+
const busy = (a) => {
|
|
151785
|
+
const turns = turnsRunning(a);
|
|
151786
|
+
const n = open6(a);
|
|
151787
|
+
if (turns > 0) show("wait_idle", `the update waits for ${turns} ${turns === 1 ? "session" : "sessions"} in a turn`, turns);
|
|
151788
|
+
else show("wait_open", `the update waits for ${n} open ${n === 1 ? "terminal or project command" : "terminals and project commands"}, which an update this computer starts by itself closes none of`, n);
|
|
151789
|
+
};
|
|
149557
151790
|
const open6 = (a) => unasked ? a.terminals + a.commandRuns : 0;
|
|
149558
151791
|
const holding = (a) => turnsRunning(a) + open6(a);
|
|
149559
151792
|
let openSince = null;
|
|
151793
|
+
const now = this.deps.now ?? Date.now;
|
|
149560
151794
|
const openLimit = this.deps.openWorkWaitMs ?? OPEN_WORK_WAIT_MS;
|
|
149561
151795
|
for (; ; ) {
|
|
149562
151796
|
const r = await read();
|
|
149563
151797
|
if (r.activity) {
|
|
149564
151798
|
seen = true;
|
|
149565
151799
|
blind = 0;
|
|
149566
|
-
if (
|
|
149567
|
-
openSince ??=
|
|
149568
|
-
if (
|
|
151800
|
+
if (open6(r.activity) > 0) {
|
|
151801
|
+
openSince ??= now();
|
|
151802
|
+
if (now() - openSince >= openLimit) {
|
|
149569
151803
|
throw new Error(
|
|
149570
151804
|
`Terminals or project commands stayed open on ${this.name}, and an update this computer starts by itself closes none of them; nothing was switched`
|
|
149571
151805
|
);
|
|
149572
151806
|
}
|
|
149573
151807
|
} else openSince = null;
|
|
149574
|
-
|
|
149575
|
-
if (running2 === 0) {
|
|
151808
|
+
if (holding(r.activity) === 0) {
|
|
149576
151809
|
await pause(settle, wait.signal);
|
|
149577
151810
|
const again = await read();
|
|
149578
151811
|
if (again.activity && holding(again.activity) === 0) {
|
|
149579
151812
|
if (shown3) c.step("stop");
|
|
149580
151813
|
return null;
|
|
149581
151814
|
}
|
|
149582
|
-
if (again.activity) busy(
|
|
151815
|
+
if (again.activity) busy(again.activity);
|
|
149583
151816
|
else unread();
|
|
149584
|
-
} else busy(
|
|
151817
|
+
} else busy(r.activity);
|
|
149585
151818
|
} else if (seen) {
|
|
149586
151819
|
unread();
|
|
149587
151820
|
} else if (r.cannot || ++blind > ACTIVITY_UNREAD_RETRIES) {
|
|
@@ -149754,13 +151987,13 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
149754
151987
|
* may be removing
|
|
149755
151988
|
*/
|
|
149756
151989
|
async autostart(on2) {
|
|
149757
|
-
const
|
|
149758
|
-
if (!
|
|
151990
|
+
const exec11 = this.tunnel.exec?.bind(this.tunnel);
|
|
151991
|
+
if (!exec11) throw linkError2(`This computer has no shell on ${this.name} to change how it starts`);
|
|
149759
151992
|
const action = on2 === void 0 ? "status" : on2 ? "on" : "off";
|
|
149760
151993
|
const done2 = this.begin("autostart", action, null);
|
|
149761
151994
|
let state;
|
|
149762
151995
|
try {
|
|
149763
|
-
const a = await autostartRemote({ exec:
|
|
151996
|
+
const a = await autostartRemote({ exec: exec11, spec: this.record.remote, target: this.record.sshTarget }, action);
|
|
149764
151997
|
state = { on: a.on, how: a.how, linger: a.linger };
|
|
149765
151998
|
this.bootStart = state;
|
|
149766
151999
|
if (on2 !== void 0) {
|
|
@@ -149882,9 +152115,9 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
149882
152115
|
const tunnelExec = this.tunnel.exec.bind(this.tunnel);
|
|
149883
152116
|
const done2 = this.begin(kind, null, target);
|
|
149884
152117
|
const at2 = Date.now();
|
|
149885
|
-
const step = (s) => {
|
|
152118
|
+
const step = (s, count) => {
|
|
149886
152119
|
this.deps.log?.(`[links] ${this.name}: ${kind}${target ? ` ${target}` : ""}: ${s}`);
|
|
149887
|
-
this.operation = { kind, step: s, target, at: at2 };
|
|
152120
|
+
this.operation = { kind, step: s, target, at: at2, ...count === void 0 ? {} : { count } };
|
|
149888
152121
|
this.deps.broadcast({ type: "machine_status", machine: this.info() });
|
|
149889
152122
|
};
|
|
149890
152123
|
let result;
|
|
@@ -149903,7 +152136,7 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
149903
152136
|
return result && typeof result === "object" && "machine" in result ? { ...result, machine: this.info() } : result;
|
|
149904
152137
|
}
|
|
149905
152138
|
/** What update.ts runs things there with */
|
|
149906
|
-
control(
|
|
152139
|
+
control(exec11, script, step) {
|
|
149907
152140
|
const hold = async () => {
|
|
149908
152141
|
this.held = true;
|
|
149909
152142
|
this.clearRetry();
|
|
@@ -149914,7 +152147,7 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
149914
152147
|
this.setStatus("updating", null);
|
|
149915
152148
|
};
|
|
149916
152149
|
return {
|
|
149917
|
-
exec:
|
|
152150
|
+
exec: exec11,
|
|
149918
152151
|
spec: this.record.remote,
|
|
149919
152152
|
target: this.record.sshTarget,
|
|
149920
152153
|
script,
|
|
@@ -150037,9 +152270,9 @@ function probeTarget(target, opts = {}) {
|
|
|
150037
152270
|
}
|
|
150038
152271
|
|
|
150039
152272
|
// packages/agent-host/src/links/ssh-config.ts
|
|
150040
|
-
import { readdirSync as
|
|
150041
|
-
import { homedir as
|
|
150042
|
-
import { isAbsolute as
|
|
152273
|
+
import { readdirSync as readdirSync14, readFileSync as readFileSync23, statSync as statSync15 } from "node:fs";
|
|
152274
|
+
import { homedir as homedir16 } from "node:os";
|
|
152275
|
+
import { isAbsolute as isAbsolute11, join as join56, normalize, parse as parse5, sep as sep11 } from "node:path";
|
|
150043
152276
|
var MAX_DEPTH = 16;
|
|
150044
152277
|
function splitConfigLine(raw) {
|
|
150045
152278
|
const m = /^\s*([^\s=#]+)\s*(?:=\s*)?([\s\S]*)$/.exec(raw);
|
|
@@ -150105,19 +152338,19 @@ function expandGlob(path2) {
|
|
|
150105
152338
|
const next = [];
|
|
150106
152339
|
for (const base2 of found) {
|
|
150107
152340
|
if (!MAGIC.test(seg)) {
|
|
150108
|
-
next.push(
|
|
152341
|
+
next.push(join56(base2, seg));
|
|
150109
152342
|
continue;
|
|
150110
152343
|
}
|
|
150111
152344
|
let names;
|
|
150112
152345
|
try {
|
|
150113
|
-
names =
|
|
152346
|
+
names = readdirSync14(base2);
|
|
150114
152347
|
} catch {
|
|
150115
152348
|
continue;
|
|
150116
152349
|
}
|
|
150117
152350
|
const re2 = segmentRe(seg);
|
|
150118
152351
|
for (const name of names.sort()) {
|
|
150119
152352
|
if (name.startsWith(".") && !seg.startsWith(".")) continue;
|
|
150120
|
-
if (re2.test(name)) next.push(
|
|
152353
|
+
if (re2.test(name)) next.push(join56(base2, name));
|
|
150121
152354
|
}
|
|
150122
152355
|
}
|
|
150123
152356
|
found = next;
|
|
@@ -150147,7 +152380,7 @@ function readSshConfig(opts) {
|
|
|
150147
152380
|
}
|
|
150148
152381
|
let text2;
|
|
150149
152382
|
try {
|
|
150150
|
-
text2 =
|
|
152383
|
+
text2 = readFileSync23(file2, "utf8");
|
|
150151
152384
|
} catch (err) {
|
|
150152
152385
|
if (depth > 0 && err.code !== "ENOENT") notes.push(`${tildePath(file2, opts.home)} could not be read`);
|
|
150153
152386
|
return;
|
|
@@ -150172,11 +152405,11 @@ function readSshConfig(opts) {
|
|
|
150172
152405
|
}
|
|
150173
152406
|
for (const arg of split2.args) {
|
|
150174
152407
|
let path2 = arg;
|
|
150175
|
-
if (path2.startsWith("~/") || process.platform === "win32" && path2.startsWith("~\\")) path2 =
|
|
152408
|
+
if (path2.startsWith("~/") || process.platform === "win32" && path2.startsWith("~\\")) path2 = join56(opts.home, path2.slice(2));
|
|
150176
152409
|
else if (path2.startsWith("~")) {
|
|
150177
152410
|
notes.push(`An Include of another user's folder (${where2(file2, n + 1)}) was not read`);
|
|
150178
152411
|
continue;
|
|
150179
|
-
} else if (!
|
|
152412
|
+
} else if (!isAbsolute11(path2)) path2 = join56(opts.home, ".ssh", path2);
|
|
150180
152413
|
for (const f of expandGlob(path2)) read(f, depth + 1);
|
|
150181
152414
|
}
|
|
150182
152415
|
}
|
|
@@ -150222,8 +152455,8 @@ function resolveAlias(alias, opts = {}) {
|
|
|
150222
152455
|
var MAX_HOSTS = 100;
|
|
150223
152456
|
var RESOLVE_AT_ONCE = 8;
|
|
150224
152457
|
async function listSshHosts(opts = {}) {
|
|
150225
|
-
const home = opts.home ??
|
|
150226
|
-
const file2 = opts.configFile ??
|
|
152458
|
+
const home = opts.home ?? homedir16();
|
|
152459
|
+
const file2 = opts.configFile ?? join56(home, ".ssh", "config");
|
|
150227
152460
|
const read = readSshConfig({ file: file2, home });
|
|
150228
152461
|
const notes = [...read.notes];
|
|
150229
152462
|
const listed2 = read.hosts.slice(0, MAX_HOSTS);
|
|
@@ -151759,7 +153992,7 @@ function keepUnseen(ordered, kept) {
|
|
|
151759
153992
|
}
|
|
151760
153993
|
|
|
151761
153994
|
// packages/agent-host/src/links/stored.ts
|
|
151762
|
-
import { randomBytes as
|
|
153995
|
+
import { randomBytes as randomBytes11 } from "node:crypto";
|
|
151763
153996
|
function storeRegistry(store2) {
|
|
151764
153997
|
return {
|
|
151765
153998
|
list: () => store2.machines.listLinkedMachines(),
|
|
@@ -151775,7 +154008,7 @@ var HUB_SELF_ID = /^hub-[0-9a-f]{8}$/;
|
|
|
151775
154008
|
function hubSelfId(store2) {
|
|
151776
154009
|
const kept = store2.settings.appSetting(HUB_SELF_KEY);
|
|
151777
154010
|
if (kept && HUB_SELF_ID.test(kept)) return kept;
|
|
151778
|
-
const made = `hub-${
|
|
154011
|
+
const made = `hub-${randomBytes11(4).toString("hex")}`;
|
|
151779
154012
|
store2.settings.updateAppSetting(HUB_SELF_KEY, (current) => current !== null && HUB_SELF_ID.test(current) ? current : made);
|
|
151780
154013
|
return store2.settings.appSetting(HUB_SELF_KEY) ?? made;
|
|
151781
154014
|
}
|
|
@@ -151896,8 +154129,8 @@ function reverseCalls(o) {
|
|
|
151896
154129
|
}
|
|
151897
154130
|
|
|
151898
154131
|
// packages/agent-host/src/lifecycle/work-job.ts
|
|
151899
|
-
import { execFile as
|
|
151900
|
-
import { promisify as
|
|
154132
|
+
import { execFile as execFile14 } from "node:child_process";
|
|
154133
|
+
import { promisify as promisify12 } from "node:util";
|
|
151901
154134
|
var WORK_JOB_LIMITS = 8192;
|
|
151902
154135
|
var JOIN_SCRIPT = (pid, limits) => `
|
|
151903
154136
|
$ErrorActionPreference = 'Stop'
|
|
@@ -151937,7 +154170,7 @@ public static class CentraluWorkJob {
|
|
|
151937
154170
|
'@
|
|
151938
154171
|
[CentraluWorkJob]::Join(${pid}, ${limits})
|
|
151939
154172
|
`;
|
|
151940
|
-
var run3 =
|
|
154173
|
+
var run3 = promisify12(execFile14);
|
|
151941
154174
|
var REAL_OS2 = {
|
|
151942
154175
|
needed: endsWorkWithJob(),
|
|
151943
154176
|
powershell: async (script) => {
|
|
@@ -151969,7 +154202,7 @@ function workJobLine(job) {
|
|
|
151969
154202
|
|
|
151970
154203
|
// packages/agent-host/src/dev-services/broker-pty.ts
|
|
151971
154204
|
import { spawn as startProcess } from "node:child_process";
|
|
151972
|
-
import { existsSync as
|
|
154205
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
151973
154206
|
import { createRequire as createRequire3 } from "node:module";
|
|
151974
154207
|
import { createInterface as createInterface2 } from "node:readline";
|
|
151975
154208
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
@@ -151995,7 +154228,7 @@ function parseBrokerEvent(line) {
|
|
|
151995
154228
|
}
|
|
151996
154229
|
function brokerPath() {
|
|
151997
154230
|
const path2 = fileURLToPath7(new URL("./pty-broker.mjs", import.meta.url));
|
|
151998
|
-
if (!
|
|
154231
|
+
if (!existsSync27(path2)) throw new Error(`terminal broker not found: ${path2}`);
|
|
151999
154232
|
return path2;
|
|
152000
154233
|
}
|
|
152001
154234
|
var BrokerPty = class {
|
|
@@ -152151,7 +154384,7 @@ function brokerPtyModule(broker, fallback = loadNodePty) {
|
|
|
152151
154384
|
}
|
|
152152
154385
|
|
|
152153
154386
|
// packages/agent-host/src/elevation.ts
|
|
152154
|
-
import { execFile as
|
|
154387
|
+
import { execFile as execFile15 } from "node:child_process";
|
|
152155
154388
|
function detectToken(o = {}) {
|
|
152156
154389
|
const source = elevationSource(o.platform ?? HOST_PLATFORM, o.env ?? process.env);
|
|
152157
154390
|
if (source.kind === "uid") {
|
|
@@ -152159,7 +154392,7 @@ function detectToken(o = {}) {
|
|
|
152159
154392
|
return Promise.resolve({ elevated: getuid ? getuid() === 0 : null, logon: null });
|
|
152160
154393
|
}
|
|
152161
154394
|
const run4 = o.run ?? ((file2, args) => new Promise(
|
|
152162
|
-
(resolve21, reject) =>
|
|
154395
|
+
(resolve21, reject) => execFile15(file2, args, { windowsHide: true, timeout: 1e4, encoding: "utf8" }, (err, stdout) => err ? reject(err) : resolve21(stdout))
|
|
152163
154396
|
));
|
|
152164
154397
|
return run4(source.file, source.args).then(
|
|
152165
154398
|
(out) => ({ elevated: elevatedFromGroups(out), logon: logonFromGroups(out) }),
|
|
@@ -152184,7 +154417,7 @@ var handedToken = launch.tokenOnStdin ? await readHandedToken(process.stdin).cat
|
|
|
152184
154417
|
console.error(`[agent-host] the launcher did not hand over the token: ${e.message}`);
|
|
152185
154418
|
process.exit(1);
|
|
152186
154419
|
}) : launch.token;
|
|
152187
|
-
var BUILD = true ? "
|
|
154420
|
+
var BUILD = true ? "f369f5c" : "dev";
|
|
152188
154421
|
var { values } = parseArgs({
|
|
152189
154422
|
options: {
|
|
152190
154423
|
port: { type: "string", default: "5175" },
|
|
@@ -152198,24 +154431,24 @@ var { values } = parseArgs({
|
|
|
152198
154431
|
}
|
|
152199
154432
|
});
|
|
152200
154433
|
var control = launch.underKeeper ? new ControlChannel(process.stdin) : null;
|
|
152201
|
-
var token = values.token || handedToken ||
|
|
154434
|
+
var token = values.token || handedToken || randomBytes12(16).toString("hex");
|
|
152202
154435
|
var keeperToken = launch.underKeeper && !values.token && handedToken ? token : null;
|
|
152203
|
-
var httpSecret = keeperToken ? deriveHttpSecret(keeperToken) :
|
|
154436
|
+
var httpSecret = keeperToken ? deriveHttpSecret(keeperToken) : randomBytes12(32).toString("base64url");
|
|
152204
154437
|
start.at("data folder");
|
|
152205
154438
|
var movedNote = null;
|
|
152206
154439
|
function defaultDbPath() {
|
|
152207
154440
|
const { dir, legacy } = chooseDataDir();
|
|
152208
154441
|
movedNote = legacy && migrateLegacyDataDir(legacy, dir) ? `[agent-host] data folder moved: ${legacy} -> ${dir}` : null;
|
|
152209
|
-
|
|
154442
|
+
mkdirSync20(dir, { recursive: true });
|
|
152210
154443
|
process.env.CC_DATA_DIR = dir;
|
|
152211
|
-
return
|
|
154444
|
+
return join57(dir, "store.db");
|
|
152212
154445
|
}
|
|
152213
154446
|
var dbPath = values.memory ? ":memory:" : values.db ?? defaultDbPath();
|
|
152214
154447
|
var privateFolder = privateDataFolderFor(dbPath);
|
|
152215
154448
|
var madePrivate = privateFolder ? keepDataFolderPrivate(privateFolder) : [];
|
|
152216
154449
|
start.at("log");
|
|
152217
154450
|
var stopLog = dbPath === ":memory:" ? () => {
|
|
152218
|
-
} : teeStderrToFile(hostLogPath(
|
|
154451
|
+
} : teeStderrToFile(hostLogPath(dirname26(dbPath)));
|
|
152219
154452
|
if (dbPath !== ":memory:") console.error(startupBanner({ build: BUILD, db: dbPath, pid: process.pid }));
|
|
152220
154453
|
if (movedNote) console.error(movedNote);
|
|
152221
154454
|
if (madePrivate.length > 0) console.error(`[agent-host] data folder made private: ${madePrivate.join(", ")}`);
|
|
@@ -152262,7 +154495,7 @@ if (!lock.ok) {
|
|
|
152262
154495
|
}
|
|
152263
154496
|
var ownership = lock;
|
|
152264
154497
|
process.on("exit", ownership.release);
|
|
152265
|
-
if (dbPath !== ":memory:") clearStoppedMark(
|
|
154498
|
+
if (dbPath !== ":memory:") clearStoppedMark(dirname26(dbPath));
|
|
152266
154499
|
start.at("hold signals");
|
|
152267
154500
|
var signals = holdSignals(process, (line) => console.error(line));
|
|
152268
154501
|
start.at("store");
|
|
@@ -152279,7 +154512,7 @@ start.at("work job");
|
|
|
152279
154512
|
var workJobSaid = workJobLine(await workJob);
|
|
152280
154513
|
if (workJobSaid) console.error(workJobSaid);
|
|
152281
154514
|
start.at("keeper children");
|
|
152282
|
-
var held2 = launch.underKeeper ? await connectHeldChildren(
|
|
154515
|
+
var held2 = launch.underKeeper ? await connectHeldChildren(dirname26(dbPath)) : null;
|
|
152283
154516
|
start.at("services");
|
|
152284
154517
|
var screens = new Gate();
|
|
152285
154518
|
var events = new Gate();
|
|
@@ -152296,7 +154529,7 @@ var mgr = new SessionManager(
|
|
|
152296
154529
|
},
|
|
152297
154530
|
(sessionId) => bridgeAddress(launch.frontDoor, port, token, sessionId),
|
|
152298
154531
|
// Worktrees are created next to the data folder — dev and the packaged app never touch each other's worktrees
|
|
152299
|
-
|
|
154532
|
+
join57(dirname26(dbPath), "worktrees"),
|
|
152300
154533
|
held2 ? { processes: held2.processes, keptSessions: held2.kept.sessionIds } : {}
|
|
152301
154534
|
);
|
|
152302
154535
|
mgr.useProjectsHome(() => projectsHome());
|
|
@@ -152390,6 +154623,8 @@ mail.onChange((c) => events.push({ type: "mail_changed", threadId: c.threadId, t
|
|
|
152390
154623
|
mgr.useMail({
|
|
152391
154624
|
askedBy: (id3) => askedBy(askedByHost, mailTurns, id3),
|
|
152392
154625
|
sessionGone: (id3) => void mail.sessionGone(id3),
|
|
154626
|
+
handOver: (from, to, which) => mail.handOver(from, to, which),
|
|
154627
|
+
handOverPreview: (from, to) => mail.handOverPreview(from, to),
|
|
152393
154628
|
openWork: (id3) => mail.openWorkOf(id3),
|
|
152394
154629
|
sessionChanged: (id3) => mailDelivery.poke(id3),
|
|
152395
154630
|
sessionEvent: (e, ended) => mailTurns.observe(e, ended),
|
|
@@ -152444,7 +154679,7 @@ var installByDefault = installsByDefault({
|
|
|
152444
154679
|
env: process.env
|
|
152445
154680
|
});
|
|
152446
154681
|
var { auto: autoUpdateCheck, autoApply: autoApplyUpdates } = updateSwitches(store.settings, { installByDefault });
|
|
152447
|
-
var themes = new ThemeFiles(
|
|
154682
|
+
var themes = new ThemeFiles(join57(dataRoot(), "themes"), () => events.push({ type: "themes_changed" }));
|
|
152448
154683
|
await themes.start().catch((e) => console.error(`[themes] the themes folder is unavailable: ${e.message}`));
|
|
152449
154684
|
var updates = new UpdateService((status) => events.push({ type: "update_status", status }), {
|
|
152450
154685
|
// A host from source never installs by itself, whatever is stored (#1501 review, `installsByItself`)
|
|
@@ -152758,13 +154993,13 @@ var endings = wireEndings({
|
|
|
152758
154993
|
heldChildren: held2 !== null,
|
|
152759
154994
|
leave: (mode, handOver) => void shutdown.leave(mode, handOver),
|
|
152760
154995
|
force: (why) => shutdown.force(why),
|
|
152761
|
-
record: crashRecorder(
|
|
154996
|
+
record: crashRecorder(join57(dirname26(dbPath), "host-errors.log"), (line) => console.error(line)),
|
|
152762
154997
|
stdin: process.stdin,
|
|
152763
154998
|
watchParent: values["watch-parent"] === true,
|
|
152764
154999
|
log: (line) => console.error(line),
|
|
152765
155000
|
// The person quit Centralu here: a linked hub must not start a host in this folder by itself (#1143)
|
|
152766
155001
|
quit: () => {
|
|
152767
|
-
if (dbPath !== ":memory:") markQuit(
|
|
155002
|
+
if (dbPath !== ":memory:") markQuit(dirname26(dbPath));
|
|
152768
155003
|
}
|
|
152769
155004
|
});
|
|
152770
155005
|
stopNow = endings.stopAsked;
|