@mrciphersmith/keryx 0.2.121 → 0.2.123
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +2435 -455
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4194,7 +4194,19 @@ var init_exfil = __esm(() => {
|
|
|
4194
4194
|
|
|
4195
4195
|
// src/lib/fs.ts
|
|
4196
4196
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4197
|
+
import {
|
|
4198
|
+
chmodSync as chmodSync2,
|
|
4199
|
+
mkdirSync as mkdirSync2,
|
|
4200
|
+
readFileSync as readFileSync3,
|
|
4201
|
+
renameSync as renameSync2,
|
|
4202
|
+
rmdirSync,
|
|
4203
|
+
rmSync,
|
|
4204
|
+
statSync as statSync2,
|
|
4205
|
+
utimesSync,
|
|
4206
|
+
writeFileSync as writeFileSync2
|
|
4207
|
+
} from "fs";
|
|
4197
4208
|
import { access, mkdir, readFile, rename, rm, stat, utimes, writeFile } from "fs/promises";
|
|
4209
|
+
import { hostname } from "os";
|
|
4198
4210
|
import path8 from "path";
|
|
4199
4211
|
async function pathExists(filePath) {
|
|
4200
4212
|
try {
|
|
@@ -4319,7 +4331,197 @@ function processIsAlive(pid) {
|
|
|
4319
4331
|
function delay(ms) {
|
|
4320
4332
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4321
4333
|
}
|
|
4322
|
-
|
|
4334
|
+
function readLeaseOwnerSync(lockPath) {
|
|
4335
|
+
try {
|
|
4336
|
+
const value = JSON.parse(readFileSync3(path8.join(lockPath, LEASE_OWNER_FILE), "utf8"));
|
|
4337
|
+
if (value === null || typeof value !== "object")
|
|
4338
|
+
return;
|
|
4339
|
+
const record = value;
|
|
4340
|
+
const wellFormed = typeof record.token === "string" && record.token.length > 0 && Number.isSafeInteger(record.pid) && Number(record.pid) > 0 && typeof record.host === "string" && typeof record.heartbeatAt === "string" && !Number.isNaN(Date.parse(record.heartbeatAt));
|
|
4341
|
+
return wellFormed ? record : undefined;
|
|
4342
|
+
} catch {
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
function inspectLeaseSync(lockPath, opts) {
|
|
4347
|
+
const now = (opts.now ?? Date.now)();
|
|
4348
|
+
let dirMtimeMs;
|
|
4349
|
+
try {
|
|
4350
|
+
dirMtimeMs = statSync2(lockPath).mtimeMs;
|
|
4351
|
+
} catch (error) {
|
|
4352
|
+
if (isNotFound(error))
|
|
4353
|
+
return { state: "free" };
|
|
4354
|
+
throw error;
|
|
4355
|
+
}
|
|
4356
|
+
const holder = readLeaseOwnerSync(lockPath);
|
|
4357
|
+
if (holder === undefined) {
|
|
4358
|
+
return { state: now - dirMtimeMs <= opts.staleMs ? "live" : "gone", holder: undefined };
|
|
4359
|
+
}
|
|
4360
|
+
if (opts.reclaimIf?.(holder) === true) {
|
|
4361
|
+
return { state: "gone", holder };
|
|
4362
|
+
}
|
|
4363
|
+
if (now - Date.parse(holder.heartbeatAt) <= opts.staleMs) {
|
|
4364
|
+
return { state: "live", holder };
|
|
4365
|
+
}
|
|
4366
|
+
const thisHost = opts.host ?? hostname();
|
|
4367
|
+
const isAlive = opts.isAlive ?? processIsAlive;
|
|
4368
|
+
return { state: holder.host === thisHost && isAlive(holder.pid) ? "stale" : "gone", holder };
|
|
4369
|
+
}
|
|
4370
|
+
function acquireLeaseSync(lockPath, owner, opts) {
|
|
4371
|
+
return acquireOrReclaim(lockPath, owner, opts, false);
|
|
4372
|
+
}
|
|
4373
|
+
function reclaimStaleLeaseSync(lockPath, owner, opts) {
|
|
4374
|
+
return acquireOrReclaim(lockPath, owner, opts, true);
|
|
4375
|
+
}
|
|
4376
|
+
function acquireOrReclaim(lockPath, owner, opts, takeStale) {
|
|
4377
|
+
const now = opts.now ?? Date.now;
|
|
4378
|
+
mkdirSync2(path8.dirname(lockPath), { recursive: true, mode: 448 });
|
|
4379
|
+
for (let attempt = 0;attempt < MAX_LEASE_ATTEMPTS; attempt += 1) {
|
|
4380
|
+
const createdIno = tryCreateLease(lockPath, owner, opts.raceHook);
|
|
4381
|
+
if (createdIno !== undefined) {
|
|
4382
|
+
return { ok: true, handle: createLeaseHandle(lockPath, owner, createdIno, now, opts.raceHook) };
|
|
4383
|
+
}
|
|
4384
|
+
const seen = inspectLeaseSync(lockPath, opts);
|
|
4385
|
+
if (seen.state === "free")
|
|
4386
|
+
continue;
|
|
4387
|
+
if (seen.state === "live" || seen.state === "stale" && !takeStale) {
|
|
4388
|
+
return { ok: false, holder: seen.holder, state: seen.state };
|
|
4389
|
+
}
|
|
4390
|
+
removeLeaseDirIfUnchanged(lockPath, seen.holder?.token);
|
|
4391
|
+
}
|
|
4392
|
+
const last = inspectLeaseSync(lockPath, opts);
|
|
4393
|
+
return {
|
|
4394
|
+
ok: false,
|
|
4395
|
+
holder: last.state === "free" ? undefined : last.holder,
|
|
4396
|
+
state: last.state === "stale" ? "stale" : "live"
|
|
4397
|
+
};
|
|
4398
|
+
}
|
|
4399
|
+
function tryCreateLease(lockPath, owner, raceHook) {
|
|
4400
|
+
try {
|
|
4401
|
+
mkdirSync2(lockPath, { mode: 448 });
|
|
4402
|
+
} catch (error) {
|
|
4403
|
+
if (isAlreadyExistsError(error))
|
|
4404
|
+
return;
|
|
4405
|
+
throw error;
|
|
4406
|
+
}
|
|
4407
|
+
let createdIno;
|
|
4408
|
+
try {
|
|
4409
|
+
createdIno = statSync2(lockPath).ino;
|
|
4410
|
+
} catch (error) {
|
|
4411
|
+
if (isNotFound(error))
|
|
4412
|
+
return;
|
|
4413
|
+
throw error;
|
|
4414
|
+
}
|
|
4415
|
+
raceHook?.("create");
|
|
4416
|
+
const ownerPath = path8.join(lockPath, LEASE_OWNER_FILE);
|
|
4417
|
+
try {
|
|
4418
|
+
chmodSync2(lockPath, 448);
|
|
4419
|
+
writeFileSync2(ownerPath, JSON.stringify(owner), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
4420
|
+
} catch (error) {
|
|
4421
|
+
const stillOurs = inodeOf(lockPath) === createdIno;
|
|
4422
|
+
if (stillOurs) {
|
|
4423
|
+
if (!isAlreadyExistsError(error))
|
|
4424
|
+
rmSync(ownerPath, { force: true });
|
|
4425
|
+
try {
|
|
4426
|
+
rmdirSync(lockPath);
|
|
4427
|
+
} catch {}
|
|
4428
|
+
}
|
|
4429
|
+
if (!stillOurs || isAlreadyExistsError(error))
|
|
4430
|
+
return;
|
|
4431
|
+
throw error;
|
|
4432
|
+
}
|
|
4433
|
+
return createdIno;
|
|
4434
|
+
}
|
|
4435
|
+
function inodeOf(target) {
|
|
4436
|
+
try {
|
|
4437
|
+
return statSync2(target).ino;
|
|
4438
|
+
} catch {
|
|
4439
|
+
return;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
function moveLeaseAside(lockPath, removeIf, keepOnConflict) {
|
|
4443
|
+
const aside = `${lockPath}.aside.${process.pid}.${randomUUID2()}`;
|
|
4444
|
+
try {
|
|
4445
|
+
renameSync2(lockPath, aside);
|
|
4446
|
+
} catch {
|
|
4447
|
+
return;
|
|
4448
|
+
}
|
|
4449
|
+
if (!removeIf(readLeaseOwnerSync(aside))) {
|
|
4450
|
+
try {
|
|
4451
|
+
renameSync2(aside, lockPath);
|
|
4452
|
+
return;
|
|
4453
|
+
} catch {
|
|
4454
|
+
if (keepOnConflict)
|
|
4455
|
+
return;
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
rmSync(aside, { recursive: true, force: true });
|
|
4459
|
+
}
|
|
4460
|
+
function removeLeaseDirIfUnchanged(lockPath, expectedToken) {
|
|
4461
|
+
moveLeaseAside(lockPath, (moved) => moved === undefined || moved.token === expectedToken, false);
|
|
4462
|
+
}
|
|
4463
|
+
function createLeaseHandle(lockPath, initial, createdIno, now, raceHook) {
|
|
4464
|
+
let current = { ...initial };
|
|
4465
|
+
let released = false;
|
|
4466
|
+
const ownerPath = path8.join(lockPath, LEASE_OWNER_FILE);
|
|
4467
|
+
const ownership = () => {
|
|
4468
|
+
let ino;
|
|
4469
|
+
try {
|
|
4470
|
+
ino = statSync2(lockPath).ino;
|
|
4471
|
+
} catch (error) {
|
|
4472
|
+
const code = error?.code;
|
|
4473
|
+
return code === "ENOENT" || code === "ENOTDIR" ? "lost" : "unknown";
|
|
4474
|
+
}
|
|
4475
|
+
if (ino !== createdIno)
|
|
4476
|
+
return "lost";
|
|
4477
|
+
const record = readLeaseOwnerSync(lockPath);
|
|
4478
|
+
if (record === undefined)
|
|
4479
|
+
return "unknown";
|
|
4480
|
+
return record.token === initial.token ? "held" : "lost";
|
|
4481
|
+
};
|
|
4482
|
+
const holds = () => ownership() === "held";
|
|
4483
|
+
return {
|
|
4484
|
+
get owner() {
|
|
4485
|
+
return current;
|
|
4486
|
+
},
|
|
4487
|
+
refresh(patch) {
|
|
4488
|
+
if (released || !holds())
|
|
4489
|
+
return false;
|
|
4490
|
+
raceHook?.("refresh");
|
|
4491
|
+
const next = { ...current, ...patch, token: initial.token, heartbeatAt: new Date(now()).toISOString() };
|
|
4492
|
+
const tmp = path8.join(lockPath, `.${LEASE_OWNER_FILE}.${randomUUID2()}.tmp`);
|
|
4493
|
+
try {
|
|
4494
|
+
writeFileSync2(tmp, JSON.stringify(next), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
4495
|
+
if (!holds()) {
|
|
4496
|
+
rmSync(tmp, { force: true });
|
|
4497
|
+
return false;
|
|
4498
|
+
}
|
|
4499
|
+
renameSync2(tmp, ownerPath);
|
|
4500
|
+
} catch {
|
|
4501
|
+
rmSync(tmp, { force: true });
|
|
4502
|
+
return false;
|
|
4503
|
+
}
|
|
4504
|
+
current = next;
|
|
4505
|
+
try {
|
|
4506
|
+
const touched = new Date;
|
|
4507
|
+
utimesSync(lockPath, touched, touched);
|
|
4508
|
+
} catch {}
|
|
4509
|
+
return true;
|
|
4510
|
+
},
|
|
4511
|
+
holds,
|
|
4512
|
+
ownership,
|
|
4513
|
+
release() {
|
|
4514
|
+
if (released)
|
|
4515
|
+
return;
|
|
4516
|
+
released = true;
|
|
4517
|
+
if (!holds())
|
|
4518
|
+
return;
|
|
4519
|
+
raceHook?.("release");
|
|
4520
|
+
moveLeaseAside(lockPath, (moved) => moved?.token === initial.token, true);
|
|
4521
|
+
}
|
|
4522
|
+
};
|
|
4523
|
+
}
|
|
4524
|
+
var DEFAULT_LOCK_STALE_MS = 30000, LEASE_OWNER_FILE = "owner.json", MAX_LEASE_ATTEMPTS = 3;
|
|
4323
4525
|
var init_fs = () => {};
|
|
4324
4526
|
|
|
4325
4527
|
// src/lib/contained-path.ts
|
|
@@ -6599,7 +6801,7 @@ var init_self_protect = __esm(() => {
|
|
|
6599
6801
|
});
|
|
6600
6802
|
|
|
6601
6803
|
// src/lib/descriptor-read.ts
|
|
6602
|
-
import { constants, fstatSync, lstatSync as lstatSync2, statSync as
|
|
6804
|
+
import { constants, fstatSync, lstatSync as lstatSync2, statSync as statSync3 } from "fs";
|
|
6603
6805
|
import path18 from "path";
|
|
6604
6806
|
import { dlopen, FFIType, ptr } from "bun:ffi";
|
|
6605
6807
|
function readDescriptorChain(workspaceRoot, absolutePath, options = {}) {
|
|
@@ -6637,7 +6839,7 @@ function readDescriptorChain(workspaceRoot, absolutePath, options = {}) {
|
|
|
6637
6839
|
const name = components.at(-1);
|
|
6638
6840
|
if (!name)
|
|
6639
6841
|
throw new DescriptorReadError("invalid-path", "safe source path has no final component");
|
|
6640
|
-
const expected = options.expectedTargetIdentity ??
|
|
6842
|
+
const expected = options.expectedTargetIdentity ?? statSync3(absolutePath);
|
|
6641
6843
|
const finalFlags = constants.O_RDONLY | constants.O_NOFOLLOW | (options.requireRegularFile ? constants.O_NONBLOCK ?? 0 : 0);
|
|
6642
6844
|
const file = openAt(libc, directoryFd, name, finalFlags);
|
|
6643
6845
|
opened.push(file);
|
|
@@ -25250,12 +25452,12 @@ var init_guarded_owner_writer = () => {};
|
|
|
25250
25452
|
|
|
25251
25453
|
// src/session/store.ts
|
|
25252
25454
|
import {
|
|
25253
|
-
chmodSync as
|
|
25455
|
+
chmodSync as chmodSync4,
|
|
25254
25456
|
existsSync as existsSync15,
|
|
25255
|
-
mkdirSync as
|
|
25457
|
+
mkdirSync as mkdirSync5,
|
|
25256
25458
|
readdirSync,
|
|
25257
|
-
renameSync as
|
|
25258
|
-
writeFileSync as
|
|
25459
|
+
renameSync as renameSync4,
|
|
25460
|
+
writeFileSync as writeFileSync6
|
|
25259
25461
|
} from "fs";
|
|
25260
25462
|
import path98 from "path";
|
|
25261
25463
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
@@ -25336,7 +25538,7 @@ function ensureDir(dir, dataDir) {
|
|
|
25336
25538
|
if (shared) {
|
|
25337
25539
|
ensureKeryxConfigDir();
|
|
25338
25540
|
}
|
|
25339
|
-
|
|
25541
|
+
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
25340
25542
|
if (process.platform === "win32") {
|
|
25341
25543
|
return;
|
|
25342
25544
|
}
|
|
@@ -25355,13 +25557,13 @@ function ensureDir(dir, dataDir) {
|
|
|
25355
25557
|
}
|
|
25356
25558
|
function tighten(target) {
|
|
25357
25559
|
try {
|
|
25358
|
-
|
|
25560
|
+
chmodSync4(target, 448);
|
|
25359
25561
|
} catch {}
|
|
25360
25562
|
}
|
|
25361
25563
|
function atomicWriteText(file, body) {
|
|
25362
25564
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
25363
|
-
|
|
25364
|
-
|
|
25565
|
+
writeFileSync6(tmp, body, { encoding: "utf8", mode: 384 });
|
|
25566
|
+
renameSync4(tmp, file);
|
|
25365
25567
|
}
|
|
25366
25568
|
function atomicWriteJson(file, value) {
|
|
25367
25569
|
atomicWriteText(file, `${JSON.stringify(value, null, 2)}
|
|
@@ -28474,23 +28676,48 @@ async function openSlate(opts) {
|
|
|
28474
28676
|
async function closeSlate(dir, mintAttemptId) {
|
|
28475
28677
|
await archiveSlate(dir, mintAttemptId());
|
|
28476
28678
|
}
|
|
28679
|
+
function detachSlateSession(ref) {
|
|
28680
|
+
if (ref !== undefined) {
|
|
28681
|
+
ref.detached = true;
|
|
28682
|
+
}
|
|
28683
|
+
}
|
|
28684
|
+
function isSlateSessionDetached(ref) {
|
|
28685
|
+
return ref.detached === true;
|
|
28686
|
+
}
|
|
28687
|
+
function slateSessionDir(ref) {
|
|
28688
|
+
return isSlateSessionDetached(ref) ? undefined : ref.dir;
|
|
28689
|
+
}
|
|
28690
|
+
async function writeSlateSession(ref, update) {
|
|
28691
|
+
if (isSlateSessionDetached(ref)) {
|
|
28692
|
+
return;
|
|
28693
|
+
}
|
|
28694
|
+
return writeSlate(ref.dir, update);
|
|
28695
|
+
}
|
|
28696
|
+
async function readSlateSession(ref) {
|
|
28697
|
+
if (isSlateSessionDetached(ref)) {
|
|
28698
|
+
return;
|
|
28699
|
+
}
|
|
28700
|
+
return readSlate(ref.dir);
|
|
28701
|
+
}
|
|
28477
28702
|
async function ensureSlateOpened(ref, mintAttemptId, runtime) {
|
|
28703
|
+
if (isSlateSessionDetached(ref)) {
|
|
28704
|
+
return;
|
|
28705
|
+
}
|
|
28478
28706
|
if (ref.opened) {
|
|
28479
28707
|
const live = await readSlate(ref.dir);
|
|
28480
28708
|
if (live !== undefined) {
|
|
28481
28709
|
return;
|
|
28482
28710
|
}
|
|
28483
28711
|
}
|
|
28484
|
-
await
|
|
28485
|
-
|
|
28486
|
-
|
|
28487
|
-
|
|
28488
|
-
|
|
28489
|
-
});
|
|
28712
|
+
const anchors = await computeAnchors({ cwd: ref.cwd, ...runtime !== undefined ? { runtime } : {} });
|
|
28713
|
+
if (isSlateSessionDetached(ref)) {
|
|
28714
|
+
return;
|
|
28715
|
+
}
|
|
28716
|
+
await openSlateAtomic(ref.dir, mintAttemptId, () => ({ anchors, course: {}, seeds: [] }));
|
|
28490
28717
|
ref.opened = true;
|
|
28491
28718
|
}
|
|
28492
28719
|
async function closeSlateSession(ref, mintAttemptId) {
|
|
28493
|
-
if (ref === undefined) {
|
|
28720
|
+
if (ref === undefined || isSlateSessionDetached(ref)) {
|
|
28494
28721
|
return;
|
|
28495
28722
|
}
|
|
28496
28723
|
await closeSlate(ref.dir, mintAttemptId);
|
|
@@ -28540,6 +28767,12 @@ async function recordSlateTouch(dir, touched, extra) {
|
|
|
28540
28767
|
});
|
|
28541
28768
|
return { changed, slate };
|
|
28542
28769
|
}
|
|
28770
|
+
async function recordSlateSessionTouch(ref, touched, extra) {
|
|
28771
|
+
if (isSlateSessionDetached(ref)) {
|
|
28772
|
+
return;
|
|
28773
|
+
}
|
|
28774
|
+
return recordSlateTouch(ref.dir, touched, extra);
|
|
28775
|
+
}
|
|
28543
28776
|
function mintTimestampAttemptId(now = new Date) {
|
|
28544
28777
|
timestampMintCounter += 1;
|
|
28545
28778
|
return `${now.toISOString().replace(/:/g, "-")}-${timestampMintCounter}`;
|
|
@@ -29024,7 +29257,7 @@ async function resolveTerminalStateSnapshots(options) {
|
|
|
29024
29257
|
const ref = options.slateSession;
|
|
29025
29258
|
if (ref !== undefined && ref.opened) {
|
|
29026
29259
|
try {
|
|
29027
|
-
const slate = await
|
|
29260
|
+
const slate = await readSlateSession(ref);
|
|
29028
29261
|
if (slate !== undefined) {
|
|
29029
29262
|
return { courseSnapshot: slate.course, anchorsSnapshot: slate.anchors };
|
|
29030
29263
|
}
|
|
@@ -29044,9 +29277,10 @@ async function emitTerminalState(io, deps, options, reason) {
|
|
|
29044
29277
|
};
|
|
29045
29278
|
io.onTerminalState?.(state);
|
|
29046
29279
|
const ref = options.slateSession;
|
|
29047
|
-
|
|
29280
|
+
const terminalDir = ref !== undefined && ref.opened ? slateSessionDir(ref) : undefined;
|
|
29281
|
+
if (terminalDir !== undefined) {
|
|
29048
29282
|
try {
|
|
29049
|
-
await writeTerminalState(
|
|
29283
|
+
await writeTerminalState(terminalDir, state);
|
|
29050
29284
|
} catch {}
|
|
29051
29285
|
}
|
|
29052
29286
|
const block = renderTerminalStateBlock(state);
|
|
@@ -29082,7 +29316,7 @@ async function closeSlateOnFlowDone(io, deps, options) {
|
|
|
29082
29316
|
return;
|
|
29083
29317
|
}
|
|
29084
29318
|
try {
|
|
29085
|
-
const slate = await
|
|
29319
|
+
const slate = await readSlateSession(ref);
|
|
29086
29320
|
const course = await courseFromSlate(ref.cwd, slate);
|
|
29087
29321
|
if (isCourseDone(course)) {
|
|
29088
29322
|
if (slate !== undefined) {
|
|
@@ -29147,7 +29381,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
29147
29381
|
if (options.slateSession !== undefined) {
|
|
29148
29382
|
try {
|
|
29149
29383
|
if (options.skipCloseTrigger !== true && isClosePhrase(userLine)) {
|
|
29150
|
-
const liveSlate = await
|
|
29384
|
+
const liveSlate = await readSlateSession(options.slateSession);
|
|
29151
29385
|
if (liveSlate !== undefined) {
|
|
29152
29386
|
await dispatchWrapUpBestEffort(io, options, "explicit", options.slateSession.cwd, options.slateSession.dir, liveSlate);
|
|
29153
29387
|
}
|
|
@@ -29159,7 +29393,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
29159
29393
|
model: deps.modelId
|
|
29160
29394
|
});
|
|
29161
29395
|
if (!wasOpened && options.slateSession.opened) {
|
|
29162
|
-
const freshSlate = await
|
|
29396
|
+
const freshSlate = await readSlateSession(options.slateSession);
|
|
29163
29397
|
if (freshSlate !== undefined) {
|
|
29164
29398
|
history.push({ role: "user", content: renderAnchorsBlock(freshSlate.anchors), provenance: "project", ts: now() });
|
|
29165
29399
|
io.onHistoryChange?.("tool");
|
|
@@ -29577,10 +29811,10 @@ ${modelOutput}` : modelOutput,
|
|
|
29577
29811
|
if (options.slateSession !== undefined && options.slateSession.opened === true) {
|
|
29578
29812
|
try {
|
|
29579
29813
|
const touchedPaths = extractTouchedFromToolInput(call.name, parseToolInput3(call.input));
|
|
29580
|
-
const touch = await
|
|
29814
|
+
const touch = await recordSlateSessionTouch(options.slateSession, touchedPaths, {
|
|
29581
29815
|
runtime: { provider: deps.providerId, model: deps.modelId }
|
|
29582
29816
|
});
|
|
29583
|
-
if (touch
|
|
29817
|
+
if (touch?.changed === true) {
|
|
29584
29818
|
anchorsToAnnounce = touch.slate.anchors;
|
|
29585
29819
|
}
|
|
29586
29820
|
} catch (err) {
|
|
@@ -40654,7 +40888,7 @@ var init_deep_enrich = __esm(() => {
|
|
|
40654
40888
|
});
|
|
40655
40889
|
|
|
40656
40890
|
// src/wiki/enrich.ts
|
|
40657
|
-
import { existsSync as existsSync23, mkdirSync as
|
|
40891
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
40658
40892
|
import { readFile as readFile77, writeFile as writeFile42 } from "fs/promises";
|
|
40659
40893
|
import path137 from "path";
|
|
40660
40894
|
function resolveEnrichProviderModel(input) {
|
|
@@ -40672,7 +40906,7 @@ function loadResumeState(cwd) {
|
|
|
40672
40906
|
if (!existsSync23(file)) {
|
|
40673
40907
|
return { updatedAt: new Date().toISOString(), completed: [], failed: [] };
|
|
40674
40908
|
}
|
|
40675
|
-
const raw = JSON.parse(
|
|
40909
|
+
const raw = JSON.parse(readFileSync7(file, "utf8"));
|
|
40676
40910
|
if (raw === null || typeof raw !== "object") {
|
|
40677
40911
|
return { updatedAt: new Date().toISOString(), completed: [], failed: [] };
|
|
40678
40912
|
}
|
|
@@ -40695,8 +40929,8 @@ function isStringRecord(value) {
|
|
|
40695
40929
|
function saveResumeState(cwd, state) {
|
|
40696
40930
|
try {
|
|
40697
40931
|
const file = resumeStatePath(cwd);
|
|
40698
|
-
|
|
40699
|
-
|
|
40932
|
+
mkdirSync6(path137.dirname(file), { recursive: true });
|
|
40933
|
+
writeFileSync7(file, `${JSON.stringify({ ...state, updatedAt: new Date().toISOString() }, null, 2)}
|
|
40700
40934
|
`, "utf8");
|
|
40701
40935
|
} catch {}
|
|
40702
40936
|
}
|
|
@@ -42980,6 +43214,103 @@ async function startStdioTransport(server) {
|
|
|
42980
43214
|
await server.connect(transport);
|
|
42981
43215
|
}
|
|
42982
43216
|
|
|
43217
|
+
// src/tui/debug-log.ts
|
|
43218
|
+
import path192 from "path";
|
|
43219
|
+
function isDebugEnabled() {
|
|
43220
|
+
return activeRun !== undefined;
|
|
43221
|
+
}
|
|
43222
|
+
function currentDebugRun() {
|
|
43223
|
+
return activeRun;
|
|
43224
|
+
}
|
|
43225
|
+
function debugEvent(kind, data = {}) {
|
|
43226
|
+
const run = activeRun;
|
|
43227
|
+
if (run === undefined) {
|
|
43228
|
+
return;
|
|
43229
|
+
}
|
|
43230
|
+
seq += 1;
|
|
43231
|
+
try {
|
|
43232
|
+
appendOwnerOnlyLine(run.shellLog, safeStringify({ ts: new Date().toISOString(), seq, pid: process.pid, kind, ...data }));
|
|
43233
|
+
} catch {}
|
|
43234
|
+
}
|
|
43235
|
+
function callerStack() {
|
|
43236
|
+
const raw = new Error("stack").stack ?? "";
|
|
43237
|
+
return raw.split(`
|
|
43238
|
+
`).slice(1).filter((line) => !line.includes("debug-log.ts")).slice(0, 14).map((line) => line.trim()).join(" | ");
|
|
43239
|
+
}
|
|
43240
|
+
function startDebugRun(opts = {}) {
|
|
43241
|
+
const stamp = (opts.now ?? new Date).toISOString().replace(/[:.]/g, "-");
|
|
43242
|
+
const name = `${stamp}-${process.pid}`;
|
|
43243
|
+
const dir = ensureKeryxSubdir(["debug", name], opts.configDir);
|
|
43244
|
+
const run = {
|
|
43245
|
+
dir,
|
|
43246
|
+
shellLog: path192.join(dir, "shell.ndjson"),
|
|
43247
|
+
watcherLog: path192.join(dir, "watcher.ndjson")
|
|
43248
|
+
};
|
|
43249
|
+
try {
|
|
43250
|
+
writeOwnerOnlyFile(path192.join(path192.dirname(dir), "latest.txt"), `${dir}
|
|
43251
|
+
`);
|
|
43252
|
+
} catch {}
|
|
43253
|
+
activeRun = run;
|
|
43254
|
+
seq = 0;
|
|
43255
|
+
return run;
|
|
43256
|
+
}
|
|
43257
|
+
function summarizeInputChunk(chunk) {
|
|
43258
|
+
const buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
43259
|
+
const text = buf.toString("utf8");
|
|
43260
|
+
let printable = 0;
|
|
43261
|
+
let control = 0;
|
|
43262
|
+
const sequences = [];
|
|
43263
|
+
for (let i = 0;i < text.length; i++) {
|
|
43264
|
+
const ch = text[i] ?? "";
|
|
43265
|
+
const code = ch.charCodeAt(0);
|
|
43266
|
+
if (ch === "\x1B") {
|
|
43267
|
+
let j = i + 1;
|
|
43268
|
+
while (j < text.length && j - i < 48) {
|
|
43269
|
+
const c = text.charCodeAt(j);
|
|
43270
|
+
if (j > i + 1 && c >= 64 && c <= 126 && text[j] !== "[" && text[j] !== "<" && text[j] !== ";") {
|
|
43271
|
+
break;
|
|
43272
|
+
}
|
|
43273
|
+
j++;
|
|
43274
|
+
}
|
|
43275
|
+
if (sequences.length < 12) {
|
|
43276
|
+
sequences.push(Buffer.from(text.slice(i, j + 1)).toString("hex"));
|
|
43277
|
+
}
|
|
43278
|
+
control++;
|
|
43279
|
+
i = j;
|
|
43280
|
+
} else if (code < 32 || code === 127) {
|
|
43281
|
+
control++;
|
|
43282
|
+
if (sequences.length < 12) {
|
|
43283
|
+
sequences.push(Buffer.from(ch).toString("hex"));
|
|
43284
|
+
}
|
|
43285
|
+
} else {
|
|
43286
|
+
printable++;
|
|
43287
|
+
}
|
|
43288
|
+
}
|
|
43289
|
+
return { bytes: buf.length, printable, control, sequences };
|
|
43290
|
+
}
|
|
43291
|
+
function safeStringify(value) {
|
|
43292
|
+
const seen = new WeakSet;
|
|
43293
|
+
return JSON.stringify(value, (_key, v) => {
|
|
43294
|
+
if (typeof v === "bigint") {
|
|
43295
|
+
return v.toString();
|
|
43296
|
+
}
|
|
43297
|
+
if (v instanceof Error) {
|
|
43298
|
+
return { name: v.name, message: v.message, stack: v.stack };
|
|
43299
|
+
}
|
|
43300
|
+
if (v !== null && typeof v === "object") {
|
|
43301
|
+
if (seen.has(v)) {
|
|
43302
|
+
return "[circular]";
|
|
43303
|
+
}
|
|
43304
|
+
seen.add(v);
|
|
43305
|
+
}
|
|
43306
|
+
return v;
|
|
43307
|
+
});
|
|
43308
|
+
}
|
|
43309
|
+
var activeRun, seq = 0;
|
|
43310
|
+
var init_debug_log = __esm(() => {
|
|
43311
|
+
init_config_dir();
|
|
43312
|
+
});
|
|
43313
|
+
|
|
42983
43314
|
// src/lib/oauth/device-code.ts
|
|
42984
43315
|
function requestInit(base, signal) {
|
|
42985
43316
|
return signal === undefined ? base : { ...base, signal };
|
|
@@ -43472,6 +43803,327 @@ var init_login = __esm(() => {
|
|
|
43472
43803
|
SESSION_REFRESHED_GRANTS = ["grok", "github-copilot"];
|
|
43473
43804
|
});
|
|
43474
43805
|
|
|
43806
|
+
// src/tui/debug-watcher.ts
|
|
43807
|
+
import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
|
|
43808
|
+
import { closeSync as closeSync3, constants as constants2, openSync as openSync4, readdirSync as readdirSync3, readFileSync as readFileSync11, readlinkSync as readlinkSync3, statSync as statSync7 } from "fs";
|
|
43809
|
+
import path196 from "path";
|
|
43810
|
+
function parseEpollFdinfo(text) {
|
|
43811
|
+
const out = [];
|
|
43812
|
+
for (const line of text.split(`
|
|
43813
|
+
`)) {
|
|
43814
|
+
const match = /^tfd:\s*(\d+)\s+events:\s*([0-9a-fA-F]+)/.exec(line.trim());
|
|
43815
|
+
if (match?.[1] !== undefined && match[2] !== undefined) {
|
|
43816
|
+
out.push({ tfd: Number(match[1]), events: Number.parseInt(match[2], 16) });
|
|
43817
|
+
}
|
|
43818
|
+
}
|
|
43819
|
+
return out;
|
|
43820
|
+
}
|
|
43821
|
+
function ttyReaderArmed(ttyFds, epoll) {
|
|
43822
|
+
if (ttyFds.length === 0) {
|
|
43823
|
+
return;
|
|
43824
|
+
}
|
|
43825
|
+
return epoll.some((entry) => ttyFds.includes(entry.tfd) && (entry.events & EPOLLIN) !== 0);
|
|
43826
|
+
}
|
|
43827
|
+
function detectStall(history, window = 3) {
|
|
43828
|
+
if (history.length < window) {
|
|
43829
|
+
return;
|
|
43830
|
+
}
|
|
43831
|
+
const recent = history.slice(-window);
|
|
43832
|
+
if (recent.every((s) => s.alive && s.readerArmed === false)) {
|
|
43833
|
+
return "reader-not-polled";
|
|
43834
|
+
}
|
|
43835
|
+
if (recent.every((s) => s.alive && (s.pending ?? 0) > 0) && recent.every((s, i) => i === 0 || (s.pending ?? 0) >= (recent[i - 1]?.pending ?? 0))) {
|
|
43836
|
+
return "input-not-drained";
|
|
43837
|
+
}
|
|
43838
|
+
if (recent.every((s) => s.alive && (s.heartbeatAgeMs ?? 0) > 5000)) {
|
|
43839
|
+
return "event-loop-silent";
|
|
43840
|
+
}
|
|
43841
|
+
return;
|
|
43842
|
+
}
|
|
43843
|
+
function isAlive(pid) {
|
|
43844
|
+
try {
|
|
43845
|
+
process.kill(pid, 0);
|
|
43846
|
+
return true;
|
|
43847
|
+
} catch {
|
|
43848
|
+
return false;
|
|
43849
|
+
}
|
|
43850
|
+
}
|
|
43851
|
+
function readText(file) {
|
|
43852
|
+
try {
|
|
43853
|
+
return readFileSync11(file, "utf8");
|
|
43854
|
+
} catch {
|
|
43855
|
+
return;
|
|
43856
|
+
}
|
|
43857
|
+
}
|
|
43858
|
+
function listFds(pid) {
|
|
43859
|
+
const out = [];
|
|
43860
|
+
let entries;
|
|
43861
|
+
try {
|
|
43862
|
+
entries = readdirSync3(`/proc/${pid}/fd`);
|
|
43863
|
+
} catch {
|
|
43864
|
+
return out;
|
|
43865
|
+
}
|
|
43866
|
+
for (const name of entries) {
|
|
43867
|
+
const fd = Number(name);
|
|
43868
|
+
let link;
|
|
43869
|
+
try {
|
|
43870
|
+
link = readlinkSync3(`/proc/${pid}/fd/${name}`);
|
|
43871
|
+
} catch {
|
|
43872
|
+
continue;
|
|
43873
|
+
}
|
|
43874
|
+
const flags = /flags:\s*(\S+)/.exec(readText(`/proc/${pid}/fdinfo/${name}`) ?? "")?.[1];
|
|
43875
|
+
out.push({ fd, link, flags });
|
|
43876
|
+
}
|
|
43877
|
+
return out;
|
|
43878
|
+
}
|
|
43879
|
+
function epollEntries(pid, fds) {
|
|
43880
|
+
return fds.filter((f) => f.link === "anon_inode:[eventpoll]").map((f) => ({ epfd: f.fd, entries: parseEpollFdinfo(readText(`/proc/${pid}/fdinfo/${f.fd}`) ?? "") }));
|
|
43881
|
+
}
|
|
43882
|
+
function threads(pid) {
|
|
43883
|
+
let tids;
|
|
43884
|
+
try {
|
|
43885
|
+
tids = readdirSync3(`/proc/${pid}/task`);
|
|
43886
|
+
} catch {
|
|
43887
|
+
return [];
|
|
43888
|
+
}
|
|
43889
|
+
return tids.map((tid) => {
|
|
43890
|
+
const base = `/proc/${pid}/task/${tid}`;
|
|
43891
|
+
const stat = readText(`${base}/stat`) ?? "";
|
|
43892
|
+
const state = /\)\s+(\S)/.exec(stat)?.[1] ?? "?";
|
|
43893
|
+
return { tid, comm: (readText(`${base}/comm`) ?? "").trim(), wchan: (readText(`${base}/wchan`) ?? "").trim(), state };
|
|
43894
|
+
});
|
|
43895
|
+
}
|
|
43896
|
+
function ttyHolders(ttyPath) {
|
|
43897
|
+
const out = [];
|
|
43898
|
+
let pids;
|
|
43899
|
+
try {
|
|
43900
|
+
pids = readdirSync3("/proc").filter((name) => /^\d+$/.test(name));
|
|
43901
|
+
} catch {
|
|
43902
|
+
return out;
|
|
43903
|
+
}
|
|
43904
|
+
for (const pid of pids) {
|
|
43905
|
+
let fdNames;
|
|
43906
|
+
try {
|
|
43907
|
+
fdNames = readdirSync3(`/proc/${pid}/fd`);
|
|
43908
|
+
} catch {
|
|
43909
|
+
continue;
|
|
43910
|
+
}
|
|
43911
|
+
const fds = [];
|
|
43912
|
+
for (const fd of fdNames) {
|
|
43913
|
+
try {
|
|
43914
|
+
if (readlinkSync3(`/proc/${pid}/fd/${fd}`) === ttyPath)
|
|
43915
|
+
fds.push(Number(fd));
|
|
43916
|
+
} catch {}
|
|
43917
|
+
}
|
|
43918
|
+
if (fds.length === 0)
|
|
43919
|
+
continue;
|
|
43920
|
+
const cmd = (readText(`/proc/${pid}/cmdline`) ?? "").split("\x00").join(" ").trim().slice(0, 300);
|
|
43921
|
+
const ppid = /\)\s+\S\s+(\d+)/.exec(readText(`/proc/${pid}/stat`) ?? "")?.[1];
|
|
43922
|
+
out.push({ pid: Number(pid), ...ppid !== undefined ? { ppid } : {}, cmd, fds });
|
|
43923
|
+
}
|
|
43924
|
+
return out;
|
|
43925
|
+
}
|
|
43926
|
+
function decodeTermios(buf) {
|
|
43927
|
+
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
43928
|
+
const iflag = view.getUint32(0, true);
|
|
43929
|
+
const lflag = view.getUint32(12, true);
|
|
43930
|
+
return {
|
|
43931
|
+
iflag,
|
|
43932
|
+
lflag,
|
|
43933
|
+
vmin: buf[CC_OFFSET + VMIN] ?? 0,
|
|
43934
|
+
vtime: buf[CC_OFFSET + VTIME] ?? 0,
|
|
43935
|
+
icanon: (lflag & ICANON) !== 0,
|
|
43936
|
+
echo: (lflag & ECHO) !== 0
|
|
43937
|
+
};
|
|
43938
|
+
}
|
|
43939
|
+
async function makeTtyProbes() {
|
|
43940
|
+
const none = { pending: () => {
|
|
43941
|
+
return;
|
|
43942
|
+
}, termios: () => {
|
|
43943
|
+
return;
|
|
43944
|
+
} };
|
|
43945
|
+
if (process.platform !== "linux") {
|
|
43946
|
+
return none;
|
|
43947
|
+
}
|
|
43948
|
+
try {
|
|
43949
|
+
const ffi = await import("bun:ffi");
|
|
43950
|
+
const libc = ffi.dlopen("libc.so.6", {
|
|
43951
|
+
ioctl: { args: [ffi.FFIType.i32, ffi.FFIType.u64, ffi.FFIType.ptr], returns: ffi.FFIType.i32 },
|
|
43952
|
+
tcgetattr: { args: [ffi.FFIType.i32, ffi.FFIType.ptr], returns: ffi.FFIType.i32 }
|
|
43953
|
+
});
|
|
43954
|
+
return {
|
|
43955
|
+
pending: (fd) => {
|
|
43956
|
+
const out = new Int32Array(1);
|
|
43957
|
+
const rc = libc.symbols.ioctl(fd, FIONREAD, ffi.ptr(out));
|
|
43958
|
+
return rc === 0 ? out[0] ?? 0 : undefined;
|
|
43959
|
+
},
|
|
43960
|
+
termios: (fd) => {
|
|
43961
|
+
const buf = new Uint8Array(TERMIOS_SIZE);
|
|
43962
|
+
return libc.symbols.tcgetattr(fd, ffi.ptr(buf)) === 0 ? decodeTermios(buf) : undefined;
|
|
43963
|
+
}
|
|
43964
|
+
};
|
|
43965
|
+
} catch {
|
|
43966
|
+
return none;
|
|
43967
|
+
}
|
|
43968
|
+
}
|
|
43969
|
+
function spawnDebugWatcher(run) {
|
|
43970
|
+
const entry = process.argv[1];
|
|
43971
|
+
const args = [
|
|
43972
|
+
...entry !== undefined ? [entry] : [],
|
|
43973
|
+
"shell",
|
|
43974
|
+
"--debug-watcher",
|
|
43975
|
+
"--pid",
|
|
43976
|
+
String(process.pid),
|
|
43977
|
+
"--dir",
|
|
43978
|
+
run.dir
|
|
43979
|
+
];
|
|
43980
|
+
try {
|
|
43981
|
+
const child = spawn6(process.execPath, args, { detached: true, stdio: "ignore", env: process.env });
|
|
43982
|
+
child.unref();
|
|
43983
|
+
return child.pid;
|
|
43984
|
+
} catch {
|
|
43985
|
+
return;
|
|
43986
|
+
}
|
|
43987
|
+
}
|
|
43988
|
+
async function runDebugWatcher(argv) {
|
|
43989
|
+
const pidArg = argv[argv.indexOf("--pid") + 1];
|
|
43990
|
+
const dirArg = argv[argv.indexOf("--dir") + 1];
|
|
43991
|
+
const pid = Number(pidArg);
|
|
43992
|
+
if (!Number.isInteger(pid) || pid <= 0 || dirArg === undefined || !argv.includes("--dir")) {
|
|
43993
|
+
process.stderr.write(`usage: keryx shell --debug-watcher --pid <pid> --dir <debug run dir>
|
|
43994
|
+
`);
|
|
43995
|
+
process.exitCode = 2;
|
|
43996
|
+
return;
|
|
43997
|
+
}
|
|
43998
|
+
const logFile = path196.join(dirArg, "watcher.ndjson");
|
|
43999
|
+
const shellLog = path196.join(dirArg, "shell.ndjson");
|
|
44000
|
+
const log = (kind, data = {}) => {
|
|
44001
|
+
try {
|
|
44002
|
+
appendOwnerOnlyLine(logFile, JSON.stringify({ ts: new Date().toISOString(), watcher: process.pid, target: pid, kind, ...data }));
|
|
44003
|
+
} catch {}
|
|
44004
|
+
};
|
|
44005
|
+
const linux = process.platform === "linux" && isAlive(pid) && readText(`/proc/${pid}/stat`) !== undefined;
|
|
44006
|
+
const probes = await makeTtyProbes();
|
|
44007
|
+
const pendingOf = probes.pending;
|
|
44008
|
+
let ttyPath;
|
|
44009
|
+
let ttyFd;
|
|
44010
|
+
if (linux) {
|
|
44011
|
+
try {
|
|
44012
|
+
ttyPath = readlinkSync3(`/proc/${pid}/fd/0`);
|
|
44013
|
+
if (isTtyLink(ttyPath)) {
|
|
44014
|
+
ttyFd = openSync4(ttyPath, constants2.O_RDONLY | constants2.O_NONBLOCK | constants2.O_NOCTTY);
|
|
44015
|
+
}
|
|
44016
|
+
} catch (error) {
|
|
44017
|
+
log("tty.open-failed", { error: String(error) });
|
|
44018
|
+
}
|
|
44019
|
+
}
|
|
44020
|
+
log("watcher.start", { linux, ttyPath, fionread: ttyFd !== undefined && pendingOf(ttyFd) !== undefined });
|
|
44021
|
+
const history = [];
|
|
44022
|
+
let lastLogged = "";
|
|
44023
|
+
let lastTermios = "";
|
|
44024
|
+
let lastPeriodic = 0;
|
|
44025
|
+
let lastSignal = 0;
|
|
44026
|
+
const fullSnapshot = () => {
|
|
44027
|
+
const fds = listFds(pid);
|
|
44028
|
+
let stty;
|
|
44029
|
+
if (ttyPath !== undefined) {
|
|
44030
|
+
const res = spawnSync4("stty", ["-a", "-F", ttyPath], { encoding: "utf8", timeout: 2000 });
|
|
44031
|
+
stty = (res.stdout ?? "").trim() || (res.stderr ?? "").trim();
|
|
44032
|
+
}
|
|
44033
|
+
return {
|
|
44034
|
+
stat: (readText(`/proc/${pid}/stat`) ?? "").trim(),
|
|
44035
|
+
status: (readText(`/proc/${pid}/status`) ?? "").split(`
|
|
44036
|
+
`).filter((l) => /^(State|Threads|SigBlk|SigIgn|SigCgt|VmRSS):/.test(l)),
|
|
44037
|
+
fds,
|
|
44038
|
+
epoll: epollEntries(pid, fds),
|
|
44039
|
+
threads: threads(pid),
|
|
44040
|
+
stty
|
|
44041
|
+
};
|
|
44042
|
+
};
|
|
44043
|
+
const tick = () => {
|
|
44044
|
+
const alive = isAlive(pid);
|
|
44045
|
+
const sample = { alive };
|
|
44046
|
+
if (alive && linux) {
|
|
44047
|
+
const fds = listFds(pid);
|
|
44048
|
+
const ttyFds = fds.filter((f) => isTtyLink(f.link)).map((f) => f.fd);
|
|
44049
|
+
const entries = epollEntries(pid, fds).flatMap((e) => e.entries);
|
|
44050
|
+
sample.readerArmed = ttyReaderArmed(ttyFds, entries);
|
|
44051
|
+
}
|
|
44052
|
+
if (alive && ttyFd !== undefined) {
|
|
44053
|
+
sample.pending = pendingOf(ttyFd);
|
|
44054
|
+
}
|
|
44055
|
+
try {
|
|
44056
|
+
sample.heartbeatAgeMs = Date.now() - statSync7(shellLog).mtimeMs;
|
|
44057
|
+
} catch {
|
|
44058
|
+
sample.heartbeatAgeMs = undefined;
|
|
44059
|
+
}
|
|
44060
|
+
history.push(sample);
|
|
44061
|
+
if (history.length > 30)
|
|
44062
|
+
history.shift();
|
|
44063
|
+
if (!alive) {
|
|
44064
|
+
log("target.exit");
|
|
44065
|
+
return false;
|
|
44066
|
+
}
|
|
44067
|
+
const key = JSON.stringify({ a: sample.readerArmed, p: (sample.pending ?? 0) > 0 });
|
|
44068
|
+
const now = Date.now();
|
|
44069
|
+
if (key !== lastLogged || now - lastPeriodic > 1e4) {
|
|
44070
|
+
log("sample", { ...sample });
|
|
44071
|
+
lastLogged = key;
|
|
44072
|
+
lastPeriodic = now;
|
|
44073
|
+
}
|
|
44074
|
+
const stall = detectStall(history);
|
|
44075
|
+
if (stall !== undefined && now - lastSignal > 15000) {
|
|
44076
|
+
lastSignal = now;
|
|
44077
|
+
log("stall.detected", { reason: stall, recent: history.slice(-5), snapshot: linux ? fullSnapshot() : {} });
|
|
44078
|
+
if (stall !== "event-loop-silent" && process.platform !== "win32") {
|
|
44079
|
+
try {
|
|
44080
|
+
process.kill(pid, "SIGUSR2");
|
|
44081
|
+
log("recover.signal-sent", { signal: "SIGUSR2" });
|
|
44082
|
+
} catch (error) {
|
|
44083
|
+
log("recover.signal-failed", { error: String(error) });
|
|
44084
|
+
}
|
|
44085
|
+
setTimeout(() => {
|
|
44086
|
+
log("recover.after", { snapshot: linux ? fullSnapshot() : {}, pending: ttyFd !== undefined ? pendingOf(ttyFd) : undefined });
|
|
44087
|
+
}, 2000);
|
|
44088
|
+
}
|
|
44089
|
+
}
|
|
44090
|
+
return true;
|
|
44091
|
+
};
|
|
44092
|
+
const termiosTimer = ttyFd === undefined ? undefined : setInterval(() => {
|
|
44093
|
+
const termios = probes.termios(ttyFd);
|
|
44094
|
+
const key = JSON.stringify(termios);
|
|
44095
|
+
if (termios !== undefined && key !== lastTermios) {
|
|
44096
|
+
const suspicious = termios.vmin === 0 || termios.icanon;
|
|
44097
|
+
log("termios", {
|
|
44098
|
+
...termios,
|
|
44099
|
+
suspicious,
|
|
44100
|
+
...suspicious && lastTermios !== "" && ttyPath !== undefined ? { ttyHolders: ttyHolders(ttyPath) } : {}
|
|
44101
|
+
});
|
|
44102
|
+
lastTermios = key;
|
|
44103
|
+
}
|
|
44104
|
+
}, 50);
|
|
44105
|
+
await new Promise((resolve) => {
|
|
44106
|
+
const timer = setInterval(() => {
|
|
44107
|
+
if (!tick()) {
|
|
44108
|
+
clearInterval(timer);
|
|
44109
|
+
if (termiosTimer !== undefined)
|
|
44110
|
+
clearInterval(termiosTimer);
|
|
44111
|
+
resolve();
|
|
44112
|
+
}
|
|
44113
|
+
}, 1000);
|
|
44114
|
+
});
|
|
44115
|
+
if (ttyFd !== undefined) {
|
|
44116
|
+
try {
|
|
44117
|
+
closeSync3(ttyFd);
|
|
44118
|
+
} catch {}
|
|
44119
|
+
}
|
|
44120
|
+
log("watcher.stop");
|
|
44121
|
+
}
|
|
44122
|
+
var EPOLLIN = 1, FIONREAD = 21531, isTtyLink = (link) => link.startsWith("/dev/pts/") || /^\/dev\/tty/.test(link), TERMIOS_SIZE = 60, CC_OFFSET = 17, VTIME = 5, VMIN = 6, ICANON = 2, ECHO = 8;
|
|
44123
|
+
var init_debug_watcher = __esm(() => {
|
|
44124
|
+
init_config_dir();
|
|
44125
|
+
});
|
|
44126
|
+
|
|
43475
44127
|
// src/cli.ts
|
|
43476
44128
|
init_single_turn();
|
|
43477
44129
|
init_model_turn_port();
|
|
@@ -43568,23 +44220,23 @@ import path24 from "path";
|
|
|
43568
44220
|
// src/lib/project-registry.ts
|
|
43569
44221
|
init_config_dir();
|
|
43570
44222
|
import {
|
|
43571
|
-
chmodSync as
|
|
44223
|
+
chmodSync as chmodSync3,
|
|
43572
44224
|
closeSync as closeSync2,
|
|
43573
44225
|
copyFileSync,
|
|
43574
44226
|
existsSync as existsSync4,
|
|
43575
44227
|
fsyncSync,
|
|
43576
44228
|
openSync as openSync2,
|
|
43577
44229
|
realpathSync as realpathSync2,
|
|
43578
|
-
renameSync as
|
|
43579
|
-
statSync as
|
|
44230
|
+
renameSync as renameSync3,
|
|
44231
|
+
statSync as statSync5,
|
|
43580
44232
|
unlinkSync as unlinkSync2,
|
|
43581
|
-
writeFileSync as
|
|
44233
|
+
writeFileSync as writeFileSync4
|
|
43582
44234
|
} from "fs";
|
|
43583
44235
|
import path23 from "path";
|
|
43584
44236
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
43585
44237
|
|
|
43586
44238
|
// src/lib/file-lock.ts
|
|
43587
|
-
import { closeSync, mkdirSync as
|
|
44239
|
+
import { closeSync, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
43588
44240
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
43589
44241
|
import path22 from "path";
|
|
43590
44242
|
var LOCK_STALE_MS = 1e4;
|
|
@@ -43600,7 +44252,7 @@ function sleepSync(ms) {
|
|
|
43600
44252
|
}
|
|
43601
44253
|
function withFileLock2(lockPath, fn, options = {}) {
|
|
43602
44254
|
try {
|
|
43603
|
-
|
|
44255
|
+
mkdirSync3(path22.dirname(lockPath), { recursive: true, mode: 448 });
|
|
43604
44256
|
} catch {
|
|
43605
44257
|
return null;
|
|
43606
44258
|
}
|
|
@@ -43616,18 +44268,18 @@ function withFileLock2(lockPath, fn, options = {}) {
|
|
|
43616
44268
|
try {
|
|
43617
44269
|
handle = openSync(lockPath, "wx", 384);
|
|
43618
44270
|
try {
|
|
43619
|
-
|
|
44271
|
+
writeFileSync3(handle, nonce, { encoding: "utf8" });
|
|
43620
44272
|
} catch (cause) {
|
|
43621
44273
|
closeSync(handle);
|
|
43622
44274
|
handle = null;
|
|
43623
|
-
|
|
44275
|
+
rmSync2(lockPath, { force: true });
|
|
43624
44276
|
throw cause;
|
|
43625
44277
|
}
|
|
43626
44278
|
} catch {
|
|
43627
|
-
const age =
|
|
44279
|
+
const age = statSync4(lockPath, { throwIfNoEntry: false })?.mtimeMs;
|
|
43628
44280
|
if (age !== undefined && Date.now() - age > LOCK_STALE_MS) {
|
|
43629
44281
|
try {
|
|
43630
|
-
|
|
44282
|
+
rmSync2(lockPath, { force: true });
|
|
43631
44283
|
} catch {}
|
|
43632
44284
|
}
|
|
43633
44285
|
if (!announced && Date.now() - started > 1000) {
|
|
@@ -43644,8 +44296,8 @@ function withFileLock2(lockPath, fn, options = {}) {
|
|
|
43644
44296
|
closeSync(handle);
|
|
43645
44297
|
} catch {}
|
|
43646
44298
|
try {
|
|
43647
|
-
if (
|
|
43648
|
-
|
|
44299
|
+
if (readFileSync4(lockPath, "utf8") === nonce) {
|
|
44300
|
+
rmSync2(lockPath, { force: true });
|
|
43649
44301
|
}
|
|
43650
44302
|
} catch {}
|
|
43651
44303
|
}
|
|
@@ -43767,7 +44419,7 @@ function tightenBackup(file) {
|
|
|
43767
44419
|
return;
|
|
43768
44420
|
}
|
|
43769
44421
|
try {
|
|
43770
|
-
|
|
44422
|
+
chmodSync3(file, 384);
|
|
43771
44423
|
} catch {}
|
|
43772
44424
|
}
|
|
43773
44425
|
function quarantineDamagedRegistry(dir, onWarn) {
|
|
@@ -43795,13 +44447,13 @@ function saveProjectRegistry(registry, dir, onWarn) {
|
|
|
43795
44447
|
};
|
|
43796
44448
|
const handle = openSync2(temp, "wx", 384);
|
|
43797
44449
|
try {
|
|
43798
|
-
|
|
44450
|
+
writeFileSync4(handle, `${JSON.stringify(sorted, null, 2)}
|
|
43799
44451
|
`, { encoding: "utf8" });
|
|
43800
44452
|
fsyncSync(handle);
|
|
43801
44453
|
} finally {
|
|
43802
44454
|
closeSync2(handle);
|
|
43803
44455
|
}
|
|
43804
|
-
|
|
44456
|
+
renameSync3(temp, file);
|
|
43805
44457
|
return true;
|
|
43806
44458
|
} catch {
|
|
43807
44459
|
try {
|
|
@@ -43881,7 +44533,7 @@ function forgetProject(projectId, dir, onWarn) {
|
|
|
43881
44533
|
return outcome ?? "write-failed";
|
|
43882
44534
|
}
|
|
43883
44535
|
function isInitializedProject(projectPath) {
|
|
43884
|
-
return
|
|
44536
|
+
return statSync5(path23.join(projectPath, ".metaproject"), { throwIfNoEntry: false })?.isDirectory() === true;
|
|
43885
44537
|
}
|
|
43886
44538
|
function listProjects(dir, onWarn) {
|
|
43887
44539
|
const registry = loadProjectRegistry(dir, onWarn);
|
|
@@ -44832,7 +45484,7 @@ async function choice(question, choices, defaultValue) {
|
|
|
44832
45484
|
|
|
44833
45485
|
// src/lib/project-sandbox-policy.ts
|
|
44834
45486
|
init_paths();
|
|
44835
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
45487
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
44836
45488
|
import path29 from "path";
|
|
44837
45489
|
|
|
44838
45490
|
// src/harness/process/sandbox/network-run.ts
|
|
@@ -45026,7 +45678,7 @@ function loadProjectSandboxPolicy(cwdOrRoot) {
|
|
|
45026
45678
|
if (!existsSync7(file)) {
|
|
45027
45679
|
return {};
|
|
45028
45680
|
}
|
|
45029
|
-
const raw = JSON.parse(
|
|
45681
|
+
const raw = JSON.parse(readFileSync5(file, "utf8"));
|
|
45030
45682
|
return sanitizeProjectSandboxPolicy(raw);
|
|
45031
45683
|
} catch {
|
|
45032
45684
|
return {};
|
|
@@ -45049,8 +45701,8 @@ function writeProjectSandboxPolicySkeletonIfMissing(cwdOrRoot) {
|
|
|
45049
45701
|
if (existsSync7(file)) {
|
|
45050
45702
|
return false;
|
|
45051
45703
|
}
|
|
45052
|
-
|
|
45053
|
-
|
|
45704
|
+
mkdirSync4(path29.dirname(file), { recursive: true });
|
|
45705
|
+
writeFileSync5(file, projectSandboxPolicySkeleton(), { mode: 420 });
|
|
45054
45706
|
return true;
|
|
45055
45707
|
} catch {
|
|
45056
45708
|
return false;
|
|
@@ -45058,7 +45710,7 @@ function writeProjectSandboxPolicySkeletonIfMissing(cwdOrRoot) {
|
|
|
45058
45710
|
}
|
|
45059
45711
|
|
|
45060
45712
|
// src/gdskills/catalog.ts
|
|
45061
|
-
import { existsSync as existsSync8, readFileSync as
|
|
45713
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
45062
45714
|
import path30 from "path";
|
|
45063
45715
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
45064
45716
|
var GDSKILLS_PROFILES = [
|
|
@@ -45553,7 +46205,7 @@ function readBundledSkillRouting(category, name) {
|
|
|
45553
46205
|
if (file === undefined) {
|
|
45554
46206
|
throw new Error(`bundled skill "${category}/${name}" ships no SKILL.md under src/gdskills/bundled/skills, and BUNDLED_GDSKILLS reads its description and triggers from there. Declare it with renderedSkill() if it has no file.`);
|
|
45555
46207
|
}
|
|
45556
|
-
const { description, triggers } = parseSkillFrontmatter(
|
|
46208
|
+
const { description, triggers } = parseSkillFrontmatter(readFileSync6(file, "utf8"));
|
|
45557
46209
|
if (description === undefined || description.length === 0 || triggers === undefined || triggers.length === 0) {
|
|
45558
46210
|
throw new Error(`${file}: frontmatter must declare a non-empty \`description\` and \`triggers:\` list; the skill router scores both.`);
|
|
45559
46211
|
}
|
|
@@ -50274,7 +50926,7 @@ import path47 from "path";
|
|
|
50274
50926
|
// package.json
|
|
50275
50927
|
var package_default = {
|
|
50276
50928
|
name: "@mrciphersmith/keryx",
|
|
50277
|
-
version: "0.2.
|
|
50929
|
+
version: "0.2.123",
|
|
50278
50930
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
50279
50931
|
private: false,
|
|
50280
50932
|
publishConfig: {
|
|
@@ -60225,7 +60877,7 @@ function escapeRegExp6(value) {
|
|
|
60225
60877
|
}
|
|
60226
60878
|
|
|
60227
60879
|
// src/gdskills/bundled-eval.ts
|
|
60228
|
-
import { existsSync as existsSync25, readFileSync as
|
|
60880
|
+
import { existsSync as existsSync25, readFileSync as readFileSync8, readdirSync as readdirSync2 } from "fs";
|
|
60229
60881
|
import path147 from "path";
|
|
60230
60882
|
|
|
60231
60883
|
// src/gdskills/model-tier.ts
|
|
@@ -61164,7 +61816,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61164
61816
|
const skill = path147.basename(skillDir);
|
|
61165
61817
|
const rel = path147.relative(skillsRoot, file).split(path147.sep).join("/");
|
|
61166
61818
|
const category = path147.dirname(rel).split("/")[0] ?? "";
|
|
61167
|
-
const text =
|
|
61819
|
+
const text = readFileSync8(file, "utf8");
|
|
61168
61820
|
const add = (check, line, message) => {
|
|
61169
61821
|
findings.push({ check, skill, file: rel, line, message });
|
|
61170
61822
|
};
|
|
@@ -61314,7 +61966,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61314
61966
|
}
|
|
61315
61967
|
for (const file of bundledRuleFiles(root)) {
|
|
61316
61968
|
const rel = path147.relative(root, file).split(path147.sep).join("/");
|
|
61317
|
-
const text =
|
|
61969
|
+
const text = readFileSync8(file, "utf8");
|
|
61318
61970
|
const add = (check, line, message) => {
|
|
61319
61971
|
findings.push({ check, skill: path147.basename(file, path147.extname(file)), file: rel, line, message });
|
|
61320
61972
|
};
|
|
@@ -61324,7 +61976,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61324
61976
|
const skillDir = path147.dirname(file);
|
|
61325
61977
|
const skill = path147.basename(skillDir);
|
|
61326
61978
|
const rel = path147.relative(skillsRoot, file).split(path147.sep).join("/");
|
|
61327
|
-
const text =
|
|
61979
|
+
const text = readFileSync8(file, "utf8");
|
|
61328
61980
|
const add = (check, line, message) => {
|
|
61329
61981
|
findings.push({ check, skill, file: rel, line, message });
|
|
61330
61982
|
};
|
|
@@ -61353,7 +62005,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61353
62005
|
const dir = path147.dirname(file);
|
|
61354
62006
|
let canonicalComparable;
|
|
61355
62007
|
try {
|
|
61356
|
-
canonicalComparable = buildComparableText(
|
|
62008
|
+
canonicalComparable = buildComparableText(readFileSync8(file, "utf8"));
|
|
61357
62009
|
} catch {
|
|
61358
62010
|
continue;
|
|
61359
62011
|
}
|
|
@@ -61366,7 +62018,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61366
62018
|
continue;
|
|
61367
62019
|
let buildComparable;
|
|
61368
62020
|
try {
|
|
61369
|
-
buildComparable = buildComparableText(
|
|
62021
|
+
buildComparable = buildComparableText(readFileSync8(buildPath, "utf8"));
|
|
61370
62022
|
} catch {
|
|
61371
62023
|
continue;
|
|
61372
62024
|
}
|
|
@@ -73672,11 +74324,11 @@ function wrapSeatbelt(command, profile) {
|
|
|
73672
74324
|
}
|
|
73673
74325
|
|
|
73674
74326
|
// src/harness/process/sandbox/bwrap.ts
|
|
73675
|
-
import { statSync as
|
|
74327
|
+
import { statSync as statSync6 } from "fs";
|
|
73676
74328
|
var BWRAP_PROGRAM = "bwrap";
|
|
73677
74329
|
function inspectMaskTarget(target) {
|
|
73678
74330
|
try {
|
|
73679
|
-
return
|
|
74331
|
+
return statSync6(target).isDirectory() ? "dir" : "file";
|
|
73680
74332
|
} catch {
|
|
73681
74333
|
return "missing";
|
|
73682
74334
|
}
|
|
@@ -76047,7 +76699,7 @@ function credentialSummary(raw) {
|
|
|
76047
76699
|
|
|
76048
76700
|
// src/mcp-servers/store.ts
|
|
76049
76701
|
init_config_dir();
|
|
76050
|
-
import { mkdirSync as
|
|
76702
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
76051
76703
|
import path170 from "path";
|
|
76052
76704
|
var SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
76053
76705
|
function userConfigFile(configDir) {
|
|
@@ -76096,8 +76748,8 @@ function writeDocument(file, doc, scope) {
|
|
|
76096
76748
|
writeOwnerOnlyFileAtomic(file, body);
|
|
76097
76749
|
return;
|
|
76098
76750
|
}
|
|
76099
|
-
|
|
76100
|
-
|
|
76751
|
+
mkdirSync7(path170.dirname(file), { recursive: true });
|
|
76752
|
+
writeFileSync8(file, body, { encoding: "utf8" });
|
|
76101
76753
|
}
|
|
76102
76754
|
function addServer(options) {
|
|
76103
76755
|
if (!SERVER_NAME_PATTERN.test(options.name)) {
|
|
@@ -79503,7 +80155,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
|
|
|
79503
80155
|
}
|
|
79504
80156
|
|
|
79505
80157
|
// src/commands/harness.ts
|
|
79506
|
-
import { readFileSync as
|
|
80158
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
79507
80159
|
import path182 from "path";
|
|
79508
80160
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
79509
80161
|
|
|
@@ -81416,7 +82068,7 @@ async function harnessCommand(args, deps) {
|
|
|
81416
82068
|
const result = await runOffline(input, config, runDeps);
|
|
81417
82069
|
structured = toStructured(result);
|
|
81418
82070
|
if (record !== undefined && record.length > 0) {
|
|
81419
|
-
|
|
82071
|
+
writeFileSync9(record, `${JSON.stringify(toRunRecord(result, {
|
|
81420
82072
|
runId: result.output.runId,
|
|
81421
82073
|
status: result.output.status,
|
|
81422
82074
|
recordedAt: result.output.startedAt
|
|
@@ -81462,7 +82114,7 @@ function parseReplayArgs(args) {
|
|
|
81462
82114
|
function readJsonFile2(file) {
|
|
81463
82115
|
let text;
|
|
81464
82116
|
try {
|
|
81465
|
-
text =
|
|
82117
|
+
text = readFileSync9(file, "utf8");
|
|
81466
82118
|
} catch (error) {
|
|
81467
82119
|
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
81468
82120
|
}
|
|
@@ -81517,7 +82169,7 @@ function harnessReplay(args, deps) {
|
|
|
81517
82169
|
return;
|
|
81518
82170
|
}
|
|
81519
82171
|
if (parsed.writeFixture !== undefined && parsed.writeFixture.length > 0) {
|
|
81520
|
-
|
|
82172
|
+
writeFileSync9(parsed.writeFixture, `${JSON.stringify(fixture.value, null, 2)}
|
|
81521
82173
|
`, {
|
|
81522
82174
|
encoding: "utf8",
|
|
81523
82175
|
mode: 384
|
|
@@ -81829,7 +82481,7 @@ function readSpecArg(args) {
|
|
|
81829
82481
|
if (specPath === undefined) {
|
|
81830
82482
|
throw new Error("keryx harness: a --spec <path> argument is required when no spec is injected.");
|
|
81831
82483
|
}
|
|
81832
|
-
return JSON.parse(
|
|
82484
|
+
return JSON.parse(readFileSync9(specPath, "utf8"));
|
|
81833
82485
|
}
|
|
81834
82486
|
var DEFAULT_CHILD_RESULT = `STATUS: DONE
|
|
81835
82487
|
Extension completed within its granted capabilities.`;
|
|
@@ -81936,7 +82588,7 @@ init_providers();
|
|
|
81936
82588
|
init_make_provider();
|
|
81937
82589
|
init_orient();
|
|
81938
82590
|
init_metaproject_adapter();
|
|
81939
|
-
import { randomUUID as
|
|
82591
|
+
import { randomUUID as randomUUID30 } from "crypto";
|
|
81940
82592
|
import * as readline2 from "readline";
|
|
81941
82593
|
|
|
81942
82594
|
// src/commands/agent-approval-context.ts
|
|
@@ -88192,7 +88844,7 @@ async function runGoalCommand(params) {
|
|
|
88192
88844
|
const wasOpened = slateSession.opened;
|
|
88193
88845
|
await ensureSlateOpened(slateSession, mintAttemptId, { provider: deps.providerId, model: deps.modelId });
|
|
88194
88846
|
if (!wasOpened && slateSession.opened) {
|
|
88195
|
-
const freshSlate = await
|
|
88847
|
+
const freshSlate = await readSlateSession(slateSession);
|
|
88196
88848
|
if (freshSlate !== undefined) {
|
|
88197
88849
|
history.push({ role: "user", content: renderAnchorsBlock(freshSlate.anchors), provenance: "project" });
|
|
88198
88850
|
io.onHistoryChange?.("tool");
|
|
@@ -88200,17 +88852,17 @@ async function runGoalCommand(params) {
|
|
|
88200
88852
|
}
|
|
88201
88853
|
if (parsed.workspaceId !== undefined) {
|
|
88202
88854
|
const workspaceId = parsed.workspaceId;
|
|
88203
|
-
await
|
|
88855
|
+
await writeSlateSession(slateSession, (prev) => {
|
|
88204
88856
|
const base = prev ?? { anchors: { root: "", touched: [] }, course: {}, seeds: [] };
|
|
88205
88857
|
return { ...base, workspaceId };
|
|
88206
88858
|
});
|
|
88207
88859
|
}
|
|
88208
88860
|
if (parsed.auto !== undefined) {
|
|
88209
|
-
const forCourse = await
|
|
88861
|
+
const forCourse = await readSlateSession(slateSession);
|
|
88210
88862
|
let flowRefForBinding = forCourse?.course.flowRef;
|
|
88211
88863
|
if (forCourse !== undefined && forCourse.course.flowRef === undefined) {
|
|
88212
88864
|
const flowId = await autoProvisionFlow(cwd, parsed.text);
|
|
88213
|
-
await
|
|
88865
|
+
await writeSlateSession(slateSession, (prev) => {
|
|
88214
88866
|
if (!prev)
|
|
88215
88867
|
throw new Error(`SLATE-27 bind: no open slate in ${slateSession.dir}`);
|
|
88216
88868
|
return { ...prev, course: { ...prev.course, flowRef: flowId } };
|
|
@@ -88233,7 +88885,7 @@ async function runGoalCommand(params) {
|
|
|
88233
88885
|
delete slateSession.autoGoalRounds;
|
|
88234
88886
|
let roundsLeft = roundsCap;
|
|
88235
88887
|
let round = 1;
|
|
88236
|
-
while (roundsLeft > 0 && slateSession.opened) {
|
|
88888
|
+
while (roundsLeft > 0 && slateSession.opened && !isSlateSessionDetached(slateSession)) {
|
|
88237
88889
|
roundsLeft -= 1;
|
|
88238
88890
|
round += 1;
|
|
88239
88891
|
const continuationText = await buildContinuationMessage(cwd, slateSession, round, roundsCap);
|
|
@@ -88246,8 +88898,18 @@ async function runGoalCommand(params) {
|
|
|
88246
88898
|
break;
|
|
88247
88899
|
}
|
|
88248
88900
|
}
|
|
88901
|
+
if (isSlateSessionDetached(slateSession)) {
|
|
88902
|
+
systemLine(io, `/goal --auto: stopped \u2014 another shell took this session over.
|
|
88903
|
+
`);
|
|
88904
|
+
return;
|
|
88905
|
+
}
|
|
88249
88906
|
const wasOpenBeforeVerifier = slateSession.opened;
|
|
88250
88907
|
const verdict = await runGoalVerifier(deps, parsed.text, cwd, slateSession, history, io, mintAttemptId);
|
|
88908
|
+
if (isSlateSessionDetached(slateSession)) {
|
|
88909
|
+
systemLine(io, `/goal --auto: stopped \u2014 another shell took this session over.
|
|
88910
|
+
`);
|
|
88911
|
+
return;
|
|
88912
|
+
}
|
|
88251
88913
|
if (verdict === undefined) {
|
|
88252
88914
|
systemLine(io, `/goal --auto: verifier unavailable \u2014 outcome not independently checked.
|
|
88253
88915
|
`);
|
|
@@ -88262,12 +88924,12 @@ async function runGoalCommand(params) {
|
|
|
88262
88924
|
if (!wasOpenBeforeVerifier) {
|
|
88263
88925
|
try {
|
|
88264
88926
|
await ensureSlateOpened(slateSession, mintAttemptId, { provider: deps.providerId, model: deps.modelId });
|
|
88265
|
-
const reopened = await
|
|
88927
|
+
const reopened = await readSlateSession(slateSession);
|
|
88266
88928
|
if (reopened !== undefined) {
|
|
88267
88929
|
history.push({ role: "user", content: renderAnchorsBlock(reopened.anchors), provenance: "project" });
|
|
88268
88930
|
io.onHistoryChange?.("tool");
|
|
88269
88931
|
}
|
|
88270
|
-
await
|
|
88932
|
+
await writeSlateSession(slateSession, (prev) => {
|
|
88271
88933
|
if (!prev)
|
|
88272
88934
|
throw new Error(`SLATE-27 verifier-reopen: no open slate in ${slateSession.dir}`);
|
|
88273
88935
|
return {
|
|
@@ -88297,7 +88959,7 @@ async function runGoalCommand(params) {
|
|
|
88297
88959
|
|
|
88298
88960
|
// src/tui/tui-shell.ts
|
|
88299
88961
|
init_metaproject_adapter();
|
|
88300
|
-
import { spawnSync as
|
|
88962
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
88301
88963
|
|
|
88302
88964
|
// src/tui/transcript-blocks.ts
|
|
88303
88965
|
var EVICTED_BLOCK_TEXT = "(output no longer retained)";
|
|
@@ -90550,6 +91212,9 @@ function classifyBusyDispatch(params) {
|
|
|
90550
91212
|
return "not-a-command";
|
|
90551
91213
|
}
|
|
90552
91214
|
|
|
91215
|
+
// src/tui/tui-shell.ts
|
|
91216
|
+
init_debug_log();
|
|
91217
|
+
|
|
90553
91218
|
// src/tui/boot-animation.ts
|
|
90554
91219
|
var DEFAULT_BOOT_DURATION_MS = 350;
|
|
90555
91220
|
var WORDMARK = ["========================", " K E R Y X ", "========================"];
|
|
@@ -94050,10 +94715,10 @@ function describeElicitationPrompt(tool, inputJson) {
|
|
|
94050
94715
|
// src/lib/permission-mode-config.ts
|
|
94051
94716
|
init_permission_mode();
|
|
94052
94717
|
init_config_dir();
|
|
94053
|
-
import
|
|
94718
|
+
import path193 from "path";
|
|
94054
94719
|
var EMPTY3 = { schemaVersion: 1, projects: {} };
|
|
94055
94720
|
function permissionModeConfigPath(dir) {
|
|
94056
|
-
return
|
|
94721
|
+
return path193.join(keryxConfigDir(dir), "permission-mode.json");
|
|
94057
94722
|
}
|
|
94058
94723
|
function withRegistryLock2(dir, fn) {
|
|
94059
94724
|
return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
|
|
@@ -94296,7 +94961,465 @@ function createForegroundAgentIoFacade(owner, token, io) {
|
|
|
94296
94961
|
};
|
|
94297
94962
|
}
|
|
94298
94963
|
|
|
94964
|
+
// src/session/lease.ts
|
|
94965
|
+
init_fs();
|
|
94966
|
+
init_paths();
|
|
94967
|
+
init_store3();
|
|
94968
|
+
import { randomUUID as randomUUID29 } from "crypto";
|
|
94969
|
+
import { hostname as hostname2 } from "os";
|
|
94970
|
+
import path194 from "path";
|
|
94971
|
+
var SESSION_LEASE_STALE_MS = 15000;
|
|
94972
|
+
var SESSION_LEASE_HEARTBEAT_MS = 5000;
|
|
94973
|
+
var instanceId;
|
|
94974
|
+
function processInstanceId() {
|
|
94975
|
+
instanceId ??= randomUUID29();
|
|
94976
|
+
return instanceId;
|
|
94977
|
+
}
|
|
94978
|
+
var MIN_ENV_STALE_MS = 250;
|
|
94979
|
+
var MIN_ENV_HEARTBEAT_MS = 50;
|
|
94980
|
+
function envInt(name) {
|
|
94981
|
+
const raw = process.env[name];
|
|
94982
|
+
return raw !== undefined && /^\d+$/.test(raw) ? Number(raw) : undefined;
|
|
94983
|
+
}
|
|
94984
|
+
function leaseTimingEnvEnabled() {
|
|
94985
|
+
return process.env.KERYX_TEST_LEASE_TIMING === "1";
|
|
94986
|
+
}
|
|
94987
|
+
function resolveSessionLeaseTiming(timing = {}) {
|
|
94988
|
+
const fromEnv = leaseTimingEnvEnabled();
|
|
94989
|
+
const envStale = fromEnv ? envInt("KERYX_SESSION_LEASE_STALE_MS") : undefined;
|
|
94990
|
+
const staleMs = timing.staleMs ?? (envStale !== undefined && envStale >= MIN_ENV_STALE_MS ? envStale : SESSION_LEASE_STALE_MS);
|
|
94991
|
+
const maxHeartbeat = Math.floor(staleMs / 3);
|
|
94992
|
+
const envHeartbeat = fromEnv ? envInt("KERYX_SESSION_LEASE_HEARTBEAT_MS") : undefined;
|
|
94993
|
+
const heartbeatMs = timing.heartbeatMs ?? (envHeartbeat !== undefined && envHeartbeat >= MIN_ENV_HEARTBEAT_MS && envHeartbeat <= maxHeartbeat ? envHeartbeat : Math.max(1, Math.min(SESSION_LEASE_HEARTBEAT_MS, maxHeartbeat)));
|
|
94994
|
+
return { ...timing, staleMs, heartbeatMs };
|
|
94995
|
+
}
|
|
94996
|
+
var resolveTiming = resolveSessionLeaseTiming;
|
|
94997
|
+
function isMine(holder) {
|
|
94998
|
+
return holder !== undefined && holder.instanceId === processInstanceId() && holder.pid === process.pid;
|
|
94999
|
+
}
|
|
95000
|
+
function leaseOptions(timing) {
|
|
95001
|
+
return {
|
|
95002
|
+
staleMs: timing.staleMs,
|
|
95003
|
+
...timing.now !== undefined ? { now: timing.now } : {},
|
|
95004
|
+
...timing.isAlive !== undefined ? { isAlive: timing.isAlive } : {},
|
|
95005
|
+
...timing.host !== undefined ? { host: timing.host } : {}
|
|
95006
|
+
};
|
|
95007
|
+
}
|
|
95008
|
+
function sessionLeasePath(cwd, sessionId, dataDir) {
|
|
95009
|
+
return path194.join(sessionDir(resolveProjectRoot2(cwd), sessionId, dataDir), "active.lease");
|
|
95010
|
+
}
|
|
95011
|
+
function sessionLeaseState(cwd, sessionId, dataDir, timing) {
|
|
95012
|
+
const resolved = resolveTiming(timing);
|
|
95013
|
+
const seen = inspectLeaseSync(sessionLeasePath(cwd, sessionId, dataDir), leaseOptions(resolved));
|
|
95014
|
+
if (seen.state === "free" || seen.state === "gone")
|
|
95015
|
+
return { state: "free" };
|
|
95016
|
+
if (isMine(seen.holder))
|
|
95017
|
+
return { state: "mine", holder: seen.holder };
|
|
95018
|
+
return { state: seen.state, ...seen.holder !== undefined ? { holder: seen.holder } : {} };
|
|
95019
|
+
}
|
|
95020
|
+
function latestUnleasedSession(cwd, dataDir, timing) {
|
|
95021
|
+
let skipped;
|
|
95022
|
+
for (const summary of listSessions(cwd, dataDir)) {
|
|
95023
|
+
const { state, holder } = sessionLeaseState(cwd, summary.id, dataDir, timing);
|
|
95024
|
+
if (state === "free" || state === "mine") {
|
|
95025
|
+
return { summary, ...skipped !== undefined ? { skipped } : {} };
|
|
95026
|
+
}
|
|
95027
|
+
skipped ??= { summary, holder, state };
|
|
95028
|
+
}
|
|
95029
|
+
return { summary: undefined, ...skipped !== undefined ? { skipped } : {} };
|
|
95030
|
+
}
|
|
95031
|
+
function describeLeaseHolder(holder) {
|
|
95032
|
+
if (holder === undefined)
|
|
95033
|
+
return "another instance (still starting)";
|
|
95034
|
+
if (typeof holder.name === "string" && holder.name.length > 0)
|
|
95035
|
+
return `@${holder.name} (pid ${holder.pid})`;
|
|
95036
|
+
return `instance ${String(holder.instanceId).slice(0, 8)} (pid ${holder.pid})`;
|
|
95037
|
+
}
|
|
95038
|
+
|
|
95039
|
+
class SessionLeasedError extends Error {
|
|
95040
|
+
summary;
|
|
95041
|
+
holder;
|
|
95042
|
+
state;
|
|
95043
|
+
constructor(summary, holder, state, opts = {}) {
|
|
95044
|
+
const where = `session ${shortSessionId(summary.id)} is open in ${describeLeaseHolder(holder)}`;
|
|
95045
|
+
const hint = opts.takeOverRefused === true ? "; the holder is live, so --take-over is refused; use --fork" : state === "stale" ? "; use --fork or --take-over" : "; use --fork";
|
|
95046
|
+
super(`${where}${hint}`);
|
|
95047
|
+
this.name = "SessionLeasedError";
|
|
95048
|
+
this.summary = summary;
|
|
95049
|
+
this.holder = holder;
|
|
95050
|
+
this.state = state;
|
|
95051
|
+
}
|
|
95052
|
+
}
|
|
95053
|
+
var heldByThisProcess = new Map;
|
|
95054
|
+
function wrapHandle(sessionId, lockPath, inner, heartbeatMs) {
|
|
95055
|
+
let released = false;
|
|
95056
|
+
let lost = false;
|
|
95057
|
+
let lostTo;
|
|
95058
|
+
const lostListeners = new Set;
|
|
95059
|
+
let timer;
|
|
95060
|
+
const onExit = () => {
|
|
95061
|
+
handle.release();
|
|
95062
|
+
};
|
|
95063
|
+
const stopHeartbeat = () => {
|
|
95064
|
+
if (timer !== undefined)
|
|
95065
|
+
clearInterval(timer);
|
|
95066
|
+
timer = undefined;
|
|
95067
|
+
};
|
|
95068
|
+
const markLost = () => {
|
|
95069
|
+
if (lost || released)
|
|
95070
|
+
return;
|
|
95071
|
+
lost = true;
|
|
95072
|
+
lostTo = readLeaseOwnerSync(lockPath);
|
|
95073
|
+
stopHeartbeat();
|
|
95074
|
+
if (heldByThisProcess.get(lockPath) === handle)
|
|
95075
|
+
heldByThisProcess.delete(lockPath);
|
|
95076
|
+
const listeners = [...lostListeners];
|
|
95077
|
+
lostListeners.clear();
|
|
95078
|
+
for (const cb of listeners) {
|
|
95079
|
+
try {
|
|
95080
|
+
cb();
|
|
95081
|
+
} catch {}
|
|
95082
|
+
}
|
|
95083
|
+
};
|
|
95084
|
+
const handle = {
|
|
95085
|
+
sessionId,
|
|
95086
|
+
lockPath,
|
|
95087
|
+
get owner() {
|
|
95088
|
+
return inner.owner;
|
|
95089
|
+
},
|
|
95090
|
+
get released() {
|
|
95091
|
+
return released;
|
|
95092
|
+
},
|
|
95093
|
+
get heartbeatTimer() {
|
|
95094
|
+
return timer;
|
|
95095
|
+
},
|
|
95096
|
+
get lost() {
|
|
95097
|
+
return lost;
|
|
95098
|
+
},
|
|
95099
|
+
get lostTo() {
|
|
95100
|
+
return lostTo;
|
|
95101
|
+
},
|
|
95102
|
+
refresh(patch) {
|
|
95103
|
+
if (released || lost)
|
|
95104
|
+
return false;
|
|
95105
|
+
if (inner.refresh(patch))
|
|
95106
|
+
return true;
|
|
95107
|
+
if (inner.ownership() === "lost")
|
|
95108
|
+
markLost();
|
|
95109
|
+
return false;
|
|
95110
|
+
},
|
|
95111
|
+
checkLost() {
|
|
95112
|
+
if (!lost && !released && inner.ownership() === "lost")
|
|
95113
|
+
markLost();
|
|
95114
|
+
return lost;
|
|
95115
|
+
},
|
|
95116
|
+
onLost(cb) {
|
|
95117
|
+
if (lost) {
|
|
95118
|
+
cb();
|
|
95119
|
+
return () => {};
|
|
95120
|
+
}
|
|
95121
|
+
lostListeners.add(cb);
|
|
95122
|
+
return () => {
|
|
95123
|
+
lostListeners.delete(cb);
|
|
95124
|
+
};
|
|
95125
|
+
},
|
|
95126
|
+
release() {
|
|
95127
|
+
if (released)
|
|
95128
|
+
return;
|
|
95129
|
+
released = true;
|
|
95130
|
+
stopHeartbeat();
|
|
95131
|
+
lostListeners.clear();
|
|
95132
|
+
process.off("exit", onExit);
|
|
95133
|
+
if (heldByThisProcess.get(lockPath) === handle)
|
|
95134
|
+
heldByThisProcess.delete(lockPath);
|
|
95135
|
+
if (!lost)
|
|
95136
|
+
inner.release();
|
|
95137
|
+
}
|
|
95138
|
+
};
|
|
95139
|
+
timer = setInterval(() => {
|
|
95140
|
+
handle.refresh();
|
|
95141
|
+
}, heartbeatMs);
|
|
95142
|
+
timer.unref?.();
|
|
95143
|
+
process.on("exit", onExit);
|
|
95144
|
+
heldByThisProcess.set(lockPath, handle);
|
|
95145
|
+
return handle;
|
|
95146
|
+
}
|
|
95147
|
+
function mintOwner(now) {
|
|
95148
|
+
const at = new Date(now()).toISOString();
|
|
95149
|
+
return {
|
|
95150
|
+
schemaVersion: 1,
|
|
95151
|
+
token: randomUUID29(),
|
|
95152
|
+
pid: process.pid,
|
|
95153
|
+
host: hostname2(),
|
|
95154
|
+
instanceId: processInstanceId(),
|
|
95155
|
+
name: null,
|
|
95156
|
+
acquiredAt: at,
|
|
95157
|
+
heartbeatAt: at
|
|
95158
|
+
};
|
|
95159
|
+
}
|
|
95160
|
+
function leaseSession(cwd, summary, dataDir, timing, takeOver) {
|
|
95161
|
+
const lockPath = sessionLeasePath(cwd, summary.id, dataDir);
|
|
95162
|
+
const existing = heldByThisProcess.get(lockPath);
|
|
95163
|
+
if (existing !== undefined && !existing.released && !existing.lost)
|
|
95164
|
+
return { handle: existing, minted: false };
|
|
95165
|
+
const owner = mintOwner(timing.now ?? Date.now);
|
|
95166
|
+
const opts = {
|
|
95167
|
+
...leaseOptions(timing),
|
|
95168
|
+
reclaimIf: (holder) => isMine(holder)
|
|
95169
|
+
};
|
|
95170
|
+
const result = takeOver ? reclaimStaleLeaseSync(lockPath, owner, opts) : acquireLeaseSync(lockPath, owner, opts);
|
|
95171
|
+
if (!result.ok) {
|
|
95172
|
+
throw new SessionLeasedError(summary, result.holder, result.state, {
|
|
95173
|
+
takeOverRefused: takeOver && result.state === "live"
|
|
95174
|
+
});
|
|
95175
|
+
}
|
|
95176
|
+
return { handle: wrapHandle(summary.id, lockPath, result.handle, timing.heartbeatMs), minted: true };
|
|
95177
|
+
}
|
|
95178
|
+
function describeLeaseLoss(handle) {
|
|
95179
|
+
const short = shortSessionId(handle.sessionId);
|
|
95180
|
+
const by = handle.lostTo !== undefined ? describeLeaseHolder(handle.lostTo) : "another instance";
|
|
95181
|
+
return `session ${short} was taken over by ${by}; this shell no longer saves it. ` + `Use /new, or restart with -r ${short} --fork to keep your turns.
|
|
95182
|
+
`;
|
|
95183
|
+
}
|
|
95184
|
+
var LOST_LEASE_COMPACT_REFUSAL = `Not compacted: another shell took this session over, and this shell no longer saves it. Use /new.
|
|
95185
|
+
`;
|
|
95186
|
+
function watchLeaseLoss(notify) {
|
|
95187
|
+
let current;
|
|
95188
|
+
let unsubscribe;
|
|
95189
|
+
return {
|
|
95190
|
+
track(next) {
|
|
95191
|
+
if (next === current)
|
|
95192
|
+
return;
|
|
95193
|
+
unsubscribe?.();
|
|
95194
|
+
unsubscribe = undefined;
|
|
95195
|
+
current = next;
|
|
95196
|
+
if (next !== undefined) {
|
|
95197
|
+
unsubscribe = next.onLost(() => {
|
|
95198
|
+
notify(describeLeaseLoss(next), next);
|
|
95199
|
+
});
|
|
95200
|
+
}
|
|
95201
|
+
},
|
|
95202
|
+
canPersist() {
|
|
95203
|
+
return current === undefined || !current.checkLost();
|
|
95204
|
+
}
|
|
95205
|
+
};
|
|
95206
|
+
}
|
|
95207
|
+
function whilePersisting(value, canPersist) {
|
|
95208
|
+
return value !== undefined && !canPersist() ? undefined : value;
|
|
95209
|
+
}
|
|
95210
|
+
function gateBoxByLease(box, canPersist) {
|
|
95211
|
+
let value = box.current;
|
|
95212
|
+
Object.defineProperty(box, "current", {
|
|
95213
|
+
configurable: true,
|
|
95214
|
+
enumerable: true,
|
|
95215
|
+
get: () => whilePersisting(value, canPersist),
|
|
95216
|
+
set: (next) => {
|
|
95217
|
+
value = next;
|
|
95218
|
+
}
|
|
95219
|
+
});
|
|
95220
|
+
}
|
|
95221
|
+
function releaseSessionLease(handle) {
|
|
95222
|
+
handle?.release();
|
|
95223
|
+
}
|
|
95224
|
+
function openLeasedSession(opts) {
|
|
95225
|
+
const { takeOver, fork, staleMs, heartbeatMs, now, isAlive, host, resumeId: rawResumeId, continueLast, ...base } = opts;
|
|
95226
|
+
const timing = resolveTiming({
|
|
95227
|
+
...staleMs !== undefined ? { staleMs } : {},
|
|
95228
|
+
...heartbeatMs !== undefined ? { heartbeatMs } : {},
|
|
95229
|
+
...now !== undefined ? { now } : {},
|
|
95230
|
+
...isAlive !== undefined ? { isAlive } : {},
|
|
95231
|
+
...host !== undefined ? { host } : {}
|
|
95232
|
+
});
|
|
95233
|
+
const { cwd, dataDir } = base;
|
|
95234
|
+
const resumeId = rawResumeId !== undefined && rawResumeId.length > 0 ? rawResumeId : undefined;
|
|
95235
|
+
const openWith = (target, lease) => {
|
|
95236
|
+
try {
|
|
95237
|
+
const opened = openSession({ ...base, ...target !== undefined ? { resumeId: target } : {} });
|
|
95238
|
+
return { ...opened, lease: lease?.handle ?? leaseSession(cwd, opened.handle.summary, dataDir, timing, false).handle };
|
|
95239
|
+
} catch (error) {
|
|
95240
|
+
if (lease?.minted === true)
|
|
95241
|
+
releaseSessionLease(lease.handle);
|
|
95242
|
+
throw error;
|
|
95243
|
+
}
|
|
95244
|
+
};
|
|
95245
|
+
if (fork === true) {
|
|
95246
|
+
if (resumeId === undefined) {
|
|
95247
|
+
throw new Error("--fork needs a session id (-r <id>).");
|
|
95248
|
+
}
|
|
95249
|
+
const forked = forkSession({
|
|
95250
|
+
cwd,
|
|
95251
|
+
sourceIdOrPrefix: resumeId,
|
|
95252
|
+
...dataDir !== undefined ? { dataDir } : {}
|
|
95253
|
+
});
|
|
95254
|
+
const summary = forked.handle.summary;
|
|
95255
|
+
return openWith(summary.id, leaseSession(cwd, summary, dataDir, timing, false));
|
|
95256
|
+
}
|
|
95257
|
+
if (resumeId !== undefined) {
|
|
95258
|
+
const found = findSession(cwd, resumeId, dataDir);
|
|
95259
|
+
if (found === undefined) {
|
|
95260
|
+
return openWith(resumeId);
|
|
95261
|
+
}
|
|
95262
|
+
return openWith(found.id, leaseSession(cwd, found, dataDir, timing, takeOver === true));
|
|
95263
|
+
}
|
|
95264
|
+
let skipped;
|
|
95265
|
+
if (continueLast === true) {
|
|
95266
|
+
for (let attempt = 0;attempt < 3; attempt += 1) {
|
|
95267
|
+
const pick = latestUnleasedSession(cwd, dataDir, timing);
|
|
95268
|
+
skipped ??= pick.skipped;
|
|
95269
|
+
if (pick.summary === undefined)
|
|
95270
|
+
break;
|
|
95271
|
+
let lease;
|
|
95272
|
+
try {
|
|
95273
|
+
lease = leaseSession(cwd, pick.summary, dataDir, timing, false);
|
|
95274
|
+
} catch (error) {
|
|
95275
|
+
if (!(error instanceof SessionLeasedError))
|
|
95276
|
+
throw error;
|
|
95277
|
+
skipped ??= { summary: error.summary, holder: error.holder, state: error.state };
|
|
95278
|
+
continue;
|
|
95279
|
+
}
|
|
95280
|
+
return { ...openWith(pick.summary.id, lease), ...skipped !== undefined ? { skipped } : {} };
|
|
95281
|
+
}
|
|
95282
|
+
}
|
|
95283
|
+
return { ...openWith(undefined), ...skipped !== undefined ? { skipped } : {} };
|
|
95284
|
+
}
|
|
95285
|
+
function switchLeasedSession(current, openTarget) {
|
|
95286
|
+
const next = openTarget();
|
|
95287
|
+
if (current !== undefined && current !== next.lease) {
|
|
95288
|
+
releaseSessionLease(current);
|
|
95289
|
+
}
|
|
95290
|
+
return next;
|
|
95291
|
+
}
|
|
95292
|
+
|
|
95293
|
+
// src/session/lease-choice.ts
|
|
95294
|
+
init_store3();
|
|
95295
|
+
function leasedChoiceRows(error) {
|
|
95296
|
+
return [
|
|
95297
|
+
{ choice: "fork", label: "fork (default): continue in a copy of the session" },
|
|
95298
|
+
{ choice: "view", label: "view (read-only), then start a new session" },
|
|
95299
|
+
{ choice: "cancel", label: "cancel" },
|
|
95300
|
+
...error.state === "stale" ? [{ choice: "take-over", label: "take over (the holder is stale)" }] : []
|
|
95301
|
+
];
|
|
95302
|
+
}
|
|
95303
|
+
function describeLeasedSession(error) {
|
|
95304
|
+
const stale = error.state === "stale" ? " (stale)" : "";
|
|
95305
|
+
return `Session ${shortSessionId(error.summary.id)} \xB7 ${error.summary.title} is open in ${describeLeaseHolder(error.holder)}${stale}.`;
|
|
95306
|
+
}
|
|
95307
|
+
function describeSkippedSession(skipped) {
|
|
95308
|
+
const stale = skipped.state === "stale" ? " (stale)" : "";
|
|
95309
|
+
return `Skipped session ${shortSessionId(skipped.summary.id)} \xB7 ${skipped.summary.title}: open in ${describeLeaseHolder(skipped.holder)}${stale}
|
|
95310
|
+
`;
|
|
95311
|
+
}
|
|
95312
|
+
|
|
95313
|
+
// src/tui/tui-session-lease.ts
|
|
95314
|
+
function leaseMarker(state) {
|
|
95315
|
+
if (state === "live")
|
|
95316
|
+
return "\u25CF live";
|
|
95317
|
+
if (state === "stale")
|
|
95318
|
+
return "\u25CC stale";
|
|
95319
|
+
return "";
|
|
95320
|
+
}
|
|
95321
|
+
function leaseStateLookup(cwd, dataDir) {
|
|
95322
|
+
return (sessionId) => {
|
|
95323
|
+
try {
|
|
95324
|
+
return sessionLeaseState(cwd, sessionId, dataDir).state;
|
|
95325
|
+
} catch {
|
|
95326
|
+
return "free";
|
|
95327
|
+
}
|
|
95328
|
+
};
|
|
95329
|
+
}
|
|
95330
|
+
function withLeaseMarker(label, state) {
|
|
95331
|
+
const marker = leaseMarker(state);
|
|
95332
|
+
return marker.length > 0 ? `${label} ${marker}` : label;
|
|
95333
|
+
}
|
|
95334
|
+
function leasedChoiceRequest(error) {
|
|
95335
|
+
const descriptions = {
|
|
95336
|
+
fork: "Continue in a copy of the session; the other shell keeps the original",
|
|
95337
|
+
view: "Show the session read-only, then start a new session",
|
|
95338
|
+
cancel: "Exit without opening a session",
|
|
95339
|
+
"take-over": "Reclaim the lease: the holder has stopped refreshing it"
|
|
95340
|
+
};
|
|
95341
|
+
return {
|
|
95342
|
+
title: describeLeasedSession(error),
|
|
95343
|
+
subtitle: "Esc = cancel",
|
|
95344
|
+
cancelId: "cancel",
|
|
95345
|
+
options: leasedChoiceRows(error).map((row) => ({
|
|
95346
|
+
id: row.choice,
|
|
95347
|
+
label: row.label,
|
|
95348
|
+
description: descriptions[row.choice],
|
|
95349
|
+
...row.choice === "fork" ? { recommended: true } : {}
|
|
95350
|
+
}))
|
|
95351
|
+
};
|
|
95352
|
+
}
|
|
95353
|
+
function toLeasedChoice(error, id) {
|
|
95354
|
+
const offered = leasedChoiceRows(error).find((row) => row.choice === id);
|
|
95355
|
+
return offered?.choice ?? "cancel";
|
|
95356
|
+
}
|
|
95357
|
+
async function resolveLeasedStartup(first, deps) {
|
|
95358
|
+
let error = first;
|
|
95359
|
+
for (;; ) {
|
|
95360
|
+
const choice = await deps.choose(error);
|
|
95361
|
+
if (choice === "cancel") {
|
|
95362
|
+
return { kind: "cancelled" };
|
|
95363
|
+
}
|
|
95364
|
+
if (choice === "view") {
|
|
95365
|
+
try {
|
|
95366
|
+
deps.showReadOnly(deps.exportSession(error.summary.id));
|
|
95367
|
+
} catch (cause) {
|
|
95368
|
+
deps.notice(`Could not render the session: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
95369
|
+
}
|
|
95370
|
+
return { kind: "opened", opened: deps.open({}) };
|
|
95371
|
+
}
|
|
95372
|
+
try {
|
|
95373
|
+
const target = choice === "fork" ? { resumeId: error.summary.id, fork: true } : { resumeId: error.summary.id, takeOver: true };
|
|
95374
|
+
return { kind: "opened", opened: deps.open(target) };
|
|
95375
|
+
} catch (cause) {
|
|
95376
|
+
if (!(cause instanceof SessionLeasedError)) {
|
|
95377
|
+
throw cause;
|
|
95378
|
+
}
|
|
95379
|
+
deps.notice(cause.message);
|
|
95380
|
+
error = cause;
|
|
95381
|
+
}
|
|
95382
|
+
}
|
|
95383
|
+
}
|
|
95384
|
+
function createTuiLeaseHolder() {
|
|
95385
|
+
let current;
|
|
95386
|
+
let lossListener;
|
|
95387
|
+
const watch = watchLeaseLoss((message) => lossListener?.(message));
|
|
95388
|
+
const set = (next) => {
|
|
95389
|
+
current = next;
|
|
95390
|
+
watch.track(next);
|
|
95391
|
+
};
|
|
95392
|
+
return {
|
|
95393
|
+
get current() {
|
|
95394
|
+
return current;
|
|
95395
|
+
},
|
|
95396
|
+
hold(next) {
|
|
95397
|
+
if (current !== undefined && current !== next) {
|
|
95398
|
+
releaseSessionLease(current);
|
|
95399
|
+
}
|
|
95400
|
+
set(next);
|
|
95401
|
+
},
|
|
95402
|
+
switchTo(openTarget) {
|
|
95403
|
+
const next = switchLeasedSession(current, openTarget);
|
|
95404
|
+
set(next.lease);
|
|
95405
|
+
return next;
|
|
95406
|
+
},
|
|
95407
|
+
release() {
|
|
95408
|
+
const held = current;
|
|
95409
|
+
set(undefined);
|
|
95410
|
+
releaseSessionLease(held);
|
|
95411
|
+
},
|
|
95412
|
+
canPersist() {
|
|
95413
|
+
return watch.canPersist();
|
|
95414
|
+
},
|
|
95415
|
+
onLost(listener) {
|
|
95416
|
+
lossListener = listener;
|
|
95417
|
+
}
|
|
95418
|
+
};
|
|
95419
|
+
}
|
|
95420
|
+
|
|
94299
95421
|
// src/tui/herdr-report.ts
|
|
95422
|
+
init_debug_log();
|
|
94300
95423
|
import net from "net";
|
|
94301
95424
|
var SOURCE = "herdr:keryx";
|
|
94302
95425
|
var AGENT = "keryx";
|
|
@@ -94336,6 +95459,7 @@ function createHerdrReporter(clock = () => Date.now(), connect = writeOnce) {
|
|
|
94336
95459
|
if (state === last)
|
|
94337
95460
|
return;
|
|
94338
95461
|
last = state;
|
|
95462
|
+
debugEvent("herdr.report", { state });
|
|
94339
95463
|
send("pane.report_agent", { state });
|
|
94340
95464
|
},
|
|
94341
95465
|
release() {
|
|
@@ -94371,6 +95495,14 @@ function writeOnce(socketPath, request) {
|
|
|
94371
95495
|
}
|
|
94372
95496
|
|
|
94373
95497
|
// src/tui/composer-choice.ts
|
|
95498
|
+
init_debug_log();
|
|
95499
|
+
var DEFAULT_ACCEPT_DELAY_MS = 400;
|
|
95500
|
+
function resolveAcceptDelay(request) {
|
|
95501
|
+
if (request.acceptDelayMs !== undefined) {
|
|
95502
|
+
return Math.max(0, request.acceptDelayMs);
|
|
95503
|
+
}
|
|
95504
|
+
return DEFAULT_ACCEPT_DELAY_MS;
|
|
95505
|
+
}
|
|
94374
95506
|
var MAX_SUBTITLE_CHARS = 8000;
|
|
94375
95507
|
var MAX_SUBTITLE_ROWS = 10;
|
|
94376
95508
|
var SUBTITLE_INLINE_MAX_CHARS = 76;
|
|
@@ -94408,12 +95540,17 @@ function lockDock(dock) {
|
|
|
94408
95540
|
async function showComposerChoice(otui, r, dock, request) {
|
|
94409
95541
|
const enqueue = request.enqueue !== false;
|
|
94410
95542
|
if (!enqueue && dockBusy(dock)) {
|
|
95543
|
+
debugEvent("choice.busy-cancel", { title: request.title });
|
|
94411
95544
|
request.onBusy?.();
|
|
94412
95545
|
return request.cancelId;
|
|
94413
95546
|
}
|
|
95547
|
+
const queuedAt = Date.now();
|
|
95548
|
+
debugEvent("choice.request", { title: request.title, waitingBehind: dockDepth.get(dock) ?? 0 });
|
|
94414
95549
|
const unlock = await lockDock(dock);
|
|
95550
|
+
debugEvent("choice.lock-acquired", { title: request.title, waitedMs: Date.now() - queuedAt });
|
|
94415
95551
|
try {
|
|
94416
95552
|
if (request.signal?.aborted === true) {
|
|
95553
|
+
debugEvent("choice.aborted-before-open", { title: request.title });
|
|
94417
95554
|
return request.cancelId;
|
|
94418
95555
|
}
|
|
94419
95556
|
return await presentComposerChoice(otui, r, dock, request);
|
|
@@ -94423,6 +95560,7 @@ async function showComposerChoice(otui, r, dock, request) {
|
|
|
94423
95560
|
}
|
|
94424
95561
|
function presentComposerChoice(otui, r, dock, request) {
|
|
94425
95562
|
if (dock.visible === true) {
|
|
95563
|
+
debugEvent("choice.dock-already-visible", { title: request.title });
|
|
94426
95564
|
request.onBusy?.();
|
|
94427
95565
|
return Promise.resolve(request.cancelId);
|
|
94428
95566
|
}
|
|
@@ -94434,6 +95572,9 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
94434
95572
|
const recommendedIdx = options.findIndex((o) => o.recommended === true);
|
|
94435
95573
|
let selected = recommendedIdx >= 0 ? recommendedIdx : 0;
|
|
94436
95574
|
dock.visible = true;
|
|
95575
|
+
const openedAt = Date.now();
|
|
95576
|
+
const acceptDelayMs = resolveAcceptDelay(request);
|
|
95577
|
+
let lastTypedAt = 0;
|
|
94437
95578
|
request.onOpen?.();
|
|
94438
95579
|
const theme = getTheme();
|
|
94439
95580
|
const rawSubtitle = request.subtitle !== undefined && request.subtitle.length > 0 ? request.subtitle : undefined;
|
|
@@ -94501,13 +95642,14 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
94501
95642
|
};
|
|
94502
95643
|
let settled = false;
|
|
94503
95644
|
const onAbort = () => {
|
|
94504
|
-
finish(request.cancelId);
|
|
95645
|
+
finish(request.cancelId, "abort");
|
|
94505
95646
|
};
|
|
94506
|
-
const finish = (id) => {
|
|
95647
|
+
const finish = (id, via = "key") => {
|
|
94507
95648
|
if (settled) {
|
|
94508
95649
|
return;
|
|
94509
95650
|
}
|
|
94510
95651
|
settled = true;
|
|
95652
|
+
debugEvent("choice.finish", { title: request.title, id, via, openMs: Date.now() - openedAt });
|
|
94511
95653
|
cleanup();
|
|
94512
95654
|
resolve(id);
|
|
94513
95655
|
};
|
|
@@ -94518,7 +95660,7 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
94518
95660
|
width: "100%",
|
|
94519
95661
|
flexDirection: "column",
|
|
94520
95662
|
onMouseDown: () => {
|
|
94521
|
-
finish(o.id);
|
|
95663
|
+
finish(o.id, "click");
|
|
94522
95664
|
}
|
|
94523
95665
|
});
|
|
94524
95666
|
const label = new otui.TextRenderable(r, { id: `ch-opt-l-${i}-${Date.now()}`, content: o.displayLabel });
|
|
@@ -94576,8 +95718,11 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
94576
95718
|
dock.visible = false;
|
|
94577
95719
|
}
|
|
94578
95720
|
const onKey = (key) => {
|
|
95721
|
+
if (!key.ctrl && typeof key.sequence === "string" && key.sequence.length === 1 && key.sequence >= " ") {
|
|
95722
|
+
lastTypedAt = Date.now();
|
|
95723
|
+
}
|
|
94579
95724
|
if (key.name === "escape") {
|
|
94580
|
-
finish(request.cancelId);
|
|
95725
|
+
finish(request.cancelId, "escape");
|
|
94581
95726
|
key.preventDefault();
|
|
94582
95727
|
key.stopPropagation();
|
|
94583
95728
|
return;
|
|
@@ -94614,6 +95759,17 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
94614
95759
|
return;
|
|
94615
95760
|
}
|
|
94616
95761
|
if (key.name === "return" || key.name === "linefeed" || key.name === "kpenter") {
|
|
95762
|
+
const now = Date.now();
|
|
95763
|
+
if (now - openedAt < acceptDelayMs || now - lastTypedAt < acceptDelayMs) {
|
|
95764
|
+
debugEvent("choice.enter-ignored", {
|
|
95765
|
+
title: request.title,
|
|
95766
|
+
sinceOpenMs: now - openedAt,
|
|
95767
|
+
sinceTypingMs: lastTypedAt === 0 ? undefined : now - lastTypedAt
|
|
95768
|
+
});
|
|
95769
|
+
key.preventDefault();
|
|
95770
|
+
key.stopPropagation();
|
|
95771
|
+
return;
|
|
95772
|
+
}
|
|
94617
95773
|
const chosen = options[selected];
|
|
94618
95774
|
if (chosen !== undefined) {
|
|
94619
95775
|
finish(chosen.id);
|
|
@@ -94627,13 +95783,20 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
94627
95783
|
request.signal.addEventListener("abort", onAbort, { once: true });
|
|
94628
95784
|
}
|
|
94629
95785
|
optionsScroll.focus();
|
|
95786
|
+
debugEvent("choice.open", {
|
|
95787
|
+
title: request.title,
|
|
95788
|
+
options: options.map((o) => o.id),
|
|
95789
|
+
selected: options[selected]?.id,
|
|
95790
|
+
scrollableSubtitle: hasScrollableSubtitle,
|
|
95791
|
+
acceptDelayMs
|
|
95792
|
+
});
|
|
94630
95793
|
});
|
|
94631
95794
|
}
|
|
94632
95795
|
|
|
94633
95796
|
// src/lib/version-check.ts
|
|
94634
95797
|
init_config_dir();
|
|
94635
95798
|
init_fs();
|
|
94636
|
-
import
|
|
95799
|
+
import path195 from "path";
|
|
94637
95800
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
94638
95801
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
94639
95802
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -94835,7 +95998,7 @@ async function checkVersion(options) {
|
|
|
94835
95998
|
const now = options.now ?? Date.now;
|
|
94836
95999
|
const timestamp = now();
|
|
94837
96000
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
94838
|
-
const cacheFile =
|
|
96001
|
+
const cacheFile = path195.join(configDir, "version-check.json");
|
|
94839
96002
|
const cache = parseCache(cacheFile);
|
|
94840
96003
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
94841
96004
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -94903,6 +96066,364 @@ async function checkVersion(options) {
|
|
|
94903
96066
|
return resultFor(options.currentVersion, current, latestVersion, "registry");
|
|
94904
96067
|
}
|
|
94905
96068
|
|
|
96069
|
+
// src/tui/shell-chrome.ts
|
|
96070
|
+
init_debug_log();
|
|
96071
|
+
|
|
96072
|
+
// src/tui/renderer-debug.ts
|
|
96073
|
+
init_debug_log();
|
|
96074
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
96075
|
+
import { readFileSync as readFileSync10, readlinkSync as readlinkSync2 } from "fs";
|
|
96076
|
+
|
|
96077
|
+
// src/tui/stdin-guard.ts
|
|
96078
|
+
init_debug_log();
|
|
96079
|
+
import { openSync as openSync3, readlinkSync } from "fs";
|
|
96080
|
+
import tty from "tty";
|
|
96081
|
+
var resolveStdin = (source) => typeof source === "function" ? source() : source;
|
|
96082
|
+
function stdinIsDead(stdin) {
|
|
96083
|
+
return stdin.destroyed === true || stdin.readableEnded === true;
|
|
96084
|
+
}
|
|
96085
|
+
function openTerminalInput() {
|
|
96086
|
+
let target = "/dev/tty";
|
|
96087
|
+
if (process.platform === "linux") {
|
|
96088
|
+
try {
|
|
96089
|
+
const link = readlinkSync("/proc/self/fd/0");
|
|
96090
|
+
if (link.startsWith("/dev/pts/") || link.startsWith("/dev/tty")) {
|
|
96091
|
+
target = link;
|
|
96092
|
+
}
|
|
96093
|
+
} catch {}
|
|
96094
|
+
}
|
|
96095
|
+
try {
|
|
96096
|
+
const fd = openSync3(target, "r");
|
|
96097
|
+
const stream = new tty.ReadStream(fd);
|
|
96098
|
+
stream.setRawMode(false);
|
|
96099
|
+
stream.setRawMode(true);
|
|
96100
|
+
debugEvent("stdin.reopened", { path: target, fd });
|
|
96101
|
+
return stream;
|
|
96102
|
+
} catch (error) {
|
|
96103
|
+
debugEvent("stdin.reopen-failed", { path: target, error: error instanceof Error ? error.message : String(error) });
|
|
96104
|
+
return;
|
|
96105
|
+
}
|
|
96106
|
+
}
|
|
96107
|
+
var rendererListener;
|
|
96108
|
+
function stdinSnapshot(stdin) {
|
|
96109
|
+
const safe = (read) => {
|
|
96110
|
+
try {
|
|
96111
|
+
return read();
|
|
96112
|
+
} catch (error) {
|
|
96113
|
+
return `error: ${error instanceof Error ? error.message : String(error)}`;
|
|
96114
|
+
}
|
|
96115
|
+
};
|
|
96116
|
+
return {
|
|
96117
|
+
isTTY: safe(() => stdin.isTTY === true),
|
|
96118
|
+
isRaw: safe(() => stdin.isRaw),
|
|
96119
|
+
paused: safe(() => stdin.isPaused()),
|
|
96120
|
+
flowing: safe(() => stdin.readableFlowing),
|
|
96121
|
+
ended: safe(() => stdin.readableEnded),
|
|
96122
|
+
destroyed: safe(() => stdin.destroyed),
|
|
96123
|
+
buffered: safe(() => stdin.readableLength),
|
|
96124
|
+
dataListeners: safe(() => stdin.listenerCount("data")),
|
|
96125
|
+
readableListeners: safe(() => stdin.listenerCount("readable")),
|
|
96126
|
+
rendererAttached: safe(() => rendererListener === undefined ? undefined : stdin.listeners("data").includes(rendererListener))
|
|
96127
|
+
};
|
|
96128
|
+
}
|
|
96129
|
+
function stdinLooksStalled(snapshot) {
|
|
96130
|
+
return snapshot.paused === true || snapshot.flowing === false || snapshot.ended === true || snapshot.destroyed === true || snapshot.dataListeners === 0 || snapshot.rendererAttached === false || snapshot.isTTY === true && snapshot.isRaw === false;
|
|
96131
|
+
}
|
|
96132
|
+
function registerRendererInputListener(listener) {
|
|
96133
|
+
rendererListener = listener;
|
|
96134
|
+
}
|
|
96135
|
+
function recoverStdin(stdin, opts = {}) {
|
|
96136
|
+
const actions = [];
|
|
96137
|
+
const listener = opts.listener ?? rendererListener;
|
|
96138
|
+
const step = (name, run) => {
|
|
96139
|
+
try {
|
|
96140
|
+
run();
|
|
96141
|
+
actions.push(name);
|
|
96142
|
+
} catch (error) {
|
|
96143
|
+
actions.push(`${name} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
96144
|
+
}
|
|
96145
|
+
};
|
|
96146
|
+
if (stdin.isTTY === true && typeof stdin.setRawMode === "function" && stdin.isRaw !== true) {
|
|
96147
|
+
step("setRawMode(true)", () => stdin.setRawMode?.(true));
|
|
96148
|
+
}
|
|
96149
|
+
if (listener !== undefined && !stdin.listeners("data").includes(listener)) {
|
|
96150
|
+
step("re-attach renderer data listener", () => stdin.on("data", listener));
|
|
96151
|
+
}
|
|
96152
|
+
if (opts.cycle === true) {
|
|
96153
|
+
step("pause()", () => stdin.pause());
|
|
96154
|
+
}
|
|
96155
|
+
step("resume()", () => stdin.resume());
|
|
96156
|
+
return actions;
|
|
96157
|
+
}
|
|
96158
|
+
function instrumentStdin(stdin) {
|
|
96159
|
+
const target = stdin;
|
|
96160
|
+
const restores = [];
|
|
96161
|
+
const wrap = (method, describe = () => ({})) => {
|
|
96162
|
+
const original = target[method];
|
|
96163
|
+
if (typeof original !== "function") {
|
|
96164
|
+
return;
|
|
96165
|
+
}
|
|
96166
|
+
const fn = original;
|
|
96167
|
+
target[method] = function(...args) {
|
|
96168
|
+
debugEvent(`stdin.${method}`, { ...describe(args), before: stdinSnapshot(stdin), stack: callerStack() });
|
|
96169
|
+
return fn.apply(this, args);
|
|
96170
|
+
};
|
|
96171
|
+
restores.push(() => {
|
|
96172
|
+
target[method] = original;
|
|
96173
|
+
});
|
|
96174
|
+
};
|
|
96175
|
+
const eventName = (args) => ({ event: String(args[0]) });
|
|
96176
|
+
wrap("pause");
|
|
96177
|
+
wrap("resume");
|
|
96178
|
+
wrap("setRawMode", (args) => ({ mode: args[0] }));
|
|
96179
|
+
wrap("removeListener", eventName);
|
|
96180
|
+
wrap("off", eventName);
|
|
96181
|
+
wrap("removeAllListeners", eventName);
|
|
96182
|
+
wrap("destroy");
|
|
96183
|
+
wrap("unref");
|
|
96184
|
+
wrap("unpipe");
|
|
96185
|
+
wrap("setEncoding", (args) => ({ encoding: args[0] }));
|
|
96186
|
+
const lifecycle = [];
|
|
96187
|
+
for (const event of ["end", "close", "error", "pause", "resume"]) {
|
|
96188
|
+
const handler = (...args) => {
|
|
96189
|
+
debugEvent(`stdin.event.${event}`, { args: args.map((a) => a instanceof Error ? a : String(a)), state: stdinSnapshot(stdin) });
|
|
96190
|
+
};
|
|
96191
|
+
lifecycle.push([event, handler]);
|
|
96192
|
+
}
|
|
96193
|
+
const onData = (chunk) => {
|
|
96194
|
+
debugEvent("stdin.data", summarizeInputChunk(chunk));
|
|
96195
|
+
};
|
|
96196
|
+
lifecycle.push(["data", onData]);
|
|
96197
|
+
for (const [event, handler] of lifecycle) {
|
|
96198
|
+
if (event === "error" && stdin.listenerCount("error") === 0) {
|
|
96199
|
+
continue;
|
|
96200
|
+
}
|
|
96201
|
+
stdin.on(event, handler);
|
|
96202
|
+
}
|
|
96203
|
+
debugEvent("stdin.instrumented", { state: stdinSnapshot(stdin) });
|
|
96204
|
+
return () => {
|
|
96205
|
+
for (const restore of restores)
|
|
96206
|
+
restore();
|
|
96207
|
+
for (const [event, handler] of lifecycle)
|
|
96208
|
+
stdin.off?.(event, handler);
|
|
96209
|
+
};
|
|
96210
|
+
}
|
|
96211
|
+
function startStdinHeartbeat(source, opts = {}) {
|
|
96212
|
+
const intervalMs = opts.intervalMs ?? 1000;
|
|
96213
|
+
const cooldownMs = opts.cooldownMs ?? 1e4;
|
|
96214
|
+
let lastRecovery = 0;
|
|
96215
|
+
const timer = setInterval(() => {
|
|
96216
|
+
const stdin = resolveStdin(source);
|
|
96217
|
+
const state = stdinSnapshot(stdin);
|
|
96218
|
+
const stalled = stdinLooksStalled(state);
|
|
96219
|
+
debugEvent("heartbeat", { stdin: state, stalled, ...opts.extra?.() ?? {} });
|
|
96220
|
+
if (stalled && (opts.shouldRecover?.() ?? true) && Date.now() - lastRecovery > cooldownMs) {
|
|
96221
|
+
lastRecovery = Date.now();
|
|
96222
|
+
const actions = opts.recover?.() ?? recoverStdin(stdin, { cycle: true });
|
|
96223
|
+
debugEvent("stdin.stall.recovered-in-process", { before: state, actions, after: stdinSnapshot(resolveStdin(source)) });
|
|
96224
|
+
}
|
|
96225
|
+
}, intervalMs);
|
|
96226
|
+
timer.unref?.();
|
|
96227
|
+
return () => clearInterval(timer);
|
|
96228
|
+
}
|
|
96229
|
+
function installInputRecoverySignal(source, onRecovered, shouldRecover = () => true, recover) {
|
|
96230
|
+
if (process.platform === "win32") {
|
|
96231
|
+
return () => {};
|
|
96232
|
+
}
|
|
96233
|
+
const handler = () => {
|
|
96234
|
+
const stdin = resolveStdin(source);
|
|
96235
|
+
if (!shouldRecover()) {
|
|
96236
|
+
debugEvent("signal.SIGUSR2.skipped", { reason: "renderer suspended or destroyed", stdin: stdinSnapshot(stdin) });
|
|
96237
|
+
return;
|
|
96238
|
+
}
|
|
96239
|
+
const before = stdinSnapshot(stdin);
|
|
96240
|
+
const actions = recover?.() ?? recoverStdin(stdin, { cycle: true });
|
|
96241
|
+
const after = stdinSnapshot(resolveStdin(source));
|
|
96242
|
+
debugEvent("signal.SIGUSR2.recover", { before, actions, after, debug: isDebugEnabled() });
|
|
96243
|
+
try {
|
|
96244
|
+
onRecovered?.(actions);
|
|
96245
|
+
} catch {}
|
|
96246
|
+
};
|
|
96247
|
+
process.on("SIGUSR2", handler);
|
|
96248
|
+
return () => {
|
|
96249
|
+
process.off("SIGUSR2", handler);
|
|
96250
|
+
};
|
|
96251
|
+
}
|
|
96252
|
+
|
|
96253
|
+
// src/tui/renderer-debug.ts
|
|
96254
|
+
var MAX_REOPENS_PER_MINUTE = 5;
|
|
96255
|
+
function eofDiagnostics() {
|
|
96256
|
+
if (process.platform !== "linux") {
|
|
96257
|
+
return {};
|
|
96258
|
+
}
|
|
96259
|
+
let tty;
|
|
96260
|
+
try {
|
|
96261
|
+
tty = readlinkSync2("/proc/self/fd/0");
|
|
96262
|
+
} catch {
|
|
96263
|
+
tty = undefined;
|
|
96264
|
+
}
|
|
96265
|
+
let stat;
|
|
96266
|
+
try {
|
|
96267
|
+
stat = readFileSync10("/proc/self/stat", "utf8").trim();
|
|
96268
|
+
} catch {
|
|
96269
|
+
stat = "";
|
|
96270
|
+
}
|
|
96271
|
+
const fields = stat.replace(/^.*\)\s+/, "").split(" ");
|
|
96272
|
+
const stty = tty !== undefined ? spawnSync2("stty", ["-a", "-F", tty], { encoding: "utf8", timeout: 1500 }).stdout?.trim() : undefined;
|
|
96273
|
+
return { tty, pgrp: fields[2], session: fields[3], tpgid: fields[5], stty };
|
|
96274
|
+
}
|
|
96275
|
+
function attachRendererGuards(r, initial = process.stdin, deps = {}) {
|
|
96276
|
+
const internals = r;
|
|
96277
|
+
const listener = internals.stdinListener;
|
|
96278
|
+
registerRendererInputListener(listener);
|
|
96279
|
+
const shouldRecover = () => !r.isDestroyed && internals.controlState !== "explicit_suspended";
|
|
96280
|
+
const teardown = [];
|
|
96281
|
+
let current = initial;
|
|
96282
|
+
let unwatch = () => {};
|
|
96283
|
+
let undoInstrument = () => {};
|
|
96284
|
+
const reopenTimes = [];
|
|
96285
|
+
const reopen = (reason) => {
|
|
96286
|
+
const now = Date.now();
|
|
96287
|
+
while (reopenTimes.length > 0 && now - (reopenTimes[0] ?? 0) > 60000)
|
|
96288
|
+
reopenTimes.shift();
|
|
96289
|
+
if (reopenTimes.length >= MAX_REOPENS_PER_MINUTE) {
|
|
96290
|
+
debugEvent("stdin.reopen-gave-up", { reason, reopensLastMinute: reopenTimes.length });
|
|
96291
|
+
return ["reopen skipped: rate limit"];
|
|
96292
|
+
}
|
|
96293
|
+
reopenTimes.push(now);
|
|
96294
|
+
const next = (deps.openInput ?? openTerminalInput)();
|
|
96295
|
+
if (next === undefined) {
|
|
96296
|
+
return ["reopen failed"];
|
|
96297
|
+
}
|
|
96298
|
+
const previous = current;
|
|
96299
|
+
unwatch();
|
|
96300
|
+
undoInstrument();
|
|
96301
|
+
undoInstrument = () => {};
|
|
96302
|
+
if (listener !== undefined) {
|
|
96303
|
+
try {
|
|
96304
|
+
previous.off?.("data", listener);
|
|
96305
|
+
} catch {}
|
|
96306
|
+
next.on("data", listener);
|
|
96307
|
+
}
|
|
96308
|
+
current = next;
|
|
96309
|
+
internals.stdin = next;
|
|
96310
|
+
watch(next);
|
|
96311
|
+
if (isDebugEnabled()) {
|
|
96312
|
+
undoInstrument = instrumentStdin(next);
|
|
96313
|
+
}
|
|
96314
|
+
next.resume();
|
|
96315
|
+
debugEvent("stdin.replaced", { reason, state: stdinSnapshot(next) });
|
|
96316
|
+
r.requestRender();
|
|
96317
|
+
return [`reopened terminal input (${reason})`];
|
|
96318
|
+
};
|
|
96319
|
+
function watch(stream) {
|
|
96320
|
+
const onDead = (event) => () => {
|
|
96321
|
+
if (current !== stream) {
|
|
96322
|
+
return;
|
|
96323
|
+
}
|
|
96324
|
+
if (isDebugEnabled()) {
|
|
96325
|
+
debugEvent(`stdin.dead.${event}`, { state: stdinSnapshot(stream), ...eofDiagnostics() });
|
|
96326
|
+
}
|
|
96327
|
+
if (!shouldRecover()) {
|
|
96328
|
+
return;
|
|
96329
|
+
}
|
|
96330
|
+
setTimeout(() => {
|
|
96331
|
+
if (current === stream && shouldRecover()) {
|
|
96332
|
+
reopen(event);
|
|
96333
|
+
}
|
|
96334
|
+
}, 0);
|
|
96335
|
+
};
|
|
96336
|
+
const onEnd = onDead("end");
|
|
96337
|
+
const onClose = onDead("close");
|
|
96338
|
+
stream.on("end", onEnd);
|
|
96339
|
+
stream.on("close", onClose);
|
|
96340
|
+
unwatch = () => {
|
|
96341
|
+
stream.off?.("end", onEnd);
|
|
96342
|
+
stream.off?.("close", onClose);
|
|
96343
|
+
};
|
|
96344
|
+
}
|
|
96345
|
+
watch(current);
|
|
96346
|
+
const recover = () => stdinIsDead(current) ? reopen("recover") : recoverStdin(current, { cycle: true });
|
|
96347
|
+
teardown.push(installInputRecoverySignal(() => current, () => {
|
|
96348
|
+
r.requestRender();
|
|
96349
|
+
}, shouldRecover, recover));
|
|
96350
|
+
teardown.push(() => {
|
|
96351
|
+
unwatch();
|
|
96352
|
+
undoInstrument();
|
|
96353
|
+
registerRendererInputListener(undefined);
|
|
96354
|
+
if (current !== initial) {
|
|
96355
|
+
current.destroy?.();
|
|
96356
|
+
}
|
|
96357
|
+
});
|
|
96358
|
+
if (!isDebugEnabled()) {
|
|
96359
|
+
return () => {
|
|
96360
|
+
for (const fn of teardown.splice(0))
|
|
96361
|
+
fn();
|
|
96362
|
+
};
|
|
96363
|
+
}
|
|
96364
|
+
debugEvent("renderer.created", {
|
|
96365
|
+
controlState: internals.controlState,
|
|
96366
|
+
width: r.width,
|
|
96367
|
+
height: r.height,
|
|
96368
|
+
hasStdinListener: internals.stdinListener !== undefined
|
|
96369
|
+
});
|
|
96370
|
+
undoInstrument = instrumentStdin(current);
|
|
96371
|
+
teardown.push(startStdinHeartbeat(() => current, {
|
|
96372
|
+
shouldRecover,
|
|
96373
|
+
recover,
|
|
96374
|
+
extra: () => ({
|
|
96375
|
+
controlState: internals.controlState,
|
|
96376
|
+
focused: internals.currentFocusedRenderable?.id
|
|
96377
|
+
})
|
|
96378
|
+
}));
|
|
96379
|
+
const onKey = (key) => {
|
|
96380
|
+
const printable = key.sequence.length === 1 && key.sequence >= " " && !key.ctrl && !key.meta;
|
|
96381
|
+
queueMicrotask(() => {
|
|
96382
|
+
debugEvent("key", {
|
|
96383
|
+
name: printable ? "<printable>" : key.name,
|
|
96384
|
+
ctrl: key.ctrl,
|
|
96385
|
+
meta: key.meta,
|
|
96386
|
+
shift: key.shift,
|
|
96387
|
+
prevented: key.defaultPrevented,
|
|
96388
|
+
stopped: key.propagationStopped,
|
|
96389
|
+
focused: internals.currentFocusedRenderable?.id
|
|
96390
|
+
});
|
|
96391
|
+
});
|
|
96392
|
+
};
|
|
96393
|
+
internals._internalKeyInput?.onInternal("keypress", onKey);
|
|
96394
|
+
teardown.push(() => internals._internalKeyInput?.offInternal("keypress", onKey));
|
|
96395
|
+
const emitter = r;
|
|
96396
|
+
for (const event of ["resize", "focus", "blur", "capabilities"]) {
|
|
96397
|
+
const fn = (...args) => {
|
|
96398
|
+
debugEvent(`renderer.${event}`, event === "capabilities" ? {} : { args: args.map(String) });
|
|
96399
|
+
};
|
|
96400
|
+
emitter.on?.(event, fn);
|
|
96401
|
+
teardown.push(() => emitter.off?.(event, fn));
|
|
96402
|
+
}
|
|
96403
|
+
const target = r;
|
|
96404
|
+
for (const method of ["suspend", "resume", "pause", "stop", "destroy"]) {
|
|
96405
|
+
const original = target[method];
|
|
96406
|
+
if (typeof original !== "function")
|
|
96407
|
+
continue;
|
|
96408
|
+
const fn = original;
|
|
96409
|
+
target[method] = function(...args) {
|
|
96410
|
+
debugEvent(`renderer.${method}`, { controlState: internals.controlState, stack: callerStack() });
|
|
96411
|
+
return fn.apply(this, args);
|
|
96412
|
+
};
|
|
96413
|
+
teardown.push(() => {
|
|
96414
|
+
target[method] = original;
|
|
96415
|
+
});
|
|
96416
|
+
}
|
|
96417
|
+
return () => {
|
|
96418
|
+
debugEvent("renderer.teardown");
|
|
96419
|
+
for (const fn of teardown.splice(0)) {
|
|
96420
|
+
try {
|
|
96421
|
+
fn();
|
|
96422
|
+
} catch {}
|
|
96423
|
+
}
|
|
96424
|
+
};
|
|
96425
|
+
}
|
|
96426
|
+
|
|
94906
96427
|
// src/tui/shell-chrome.ts
|
|
94907
96428
|
function onKeypress3(r, handler) {
|
|
94908
96429
|
r._internalKeyInput.onInternal("keypress", handler);
|
|
@@ -95008,14 +96529,20 @@ function prefixFilter(commands, query) {
|
|
|
95008
96529
|
return commands.filter((c) => c.name.slice(1).toLowerCase().startsWith(needle));
|
|
95009
96530
|
}
|
|
95010
96531
|
async function createShellRenderer(otui, opts = {}) {
|
|
95011
|
-
|
|
96532
|
+
const guards = {};
|
|
96533
|
+
const renderer = await otui.createCliRenderer({
|
|
95012
96534
|
exitOnCtrlC: true,
|
|
95013
96535
|
screenMode: "alternate-screen",
|
|
95014
96536
|
clearOnShutdown: true,
|
|
95015
96537
|
useMouse: true,
|
|
95016
96538
|
backgroundColor: getTheme().bg,
|
|
95017
|
-
|
|
96539
|
+
onDestroy: () => {
|
|
96540
|
+
guards.detach?.();
|
|
96541
|
+
opts.onDestroy?.();
|
|
96542
|
+
}
|
|
95018
96543
|
});
|
|
96544
|
+
guards.detach = attachRendererGuards(renderer);
|
|
96545
|
+
return renderer;
|
|
95019
96546
|
}
|
|
95020
96547
|
async function createShellChrome(otui, renderer, opts) {
|
|
95021
96548
|
const r = renderer;
|
|
@@ -95178,10 +96705,12 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
95178
96705
|
};
|
|
95179
96706
|
const withOverlay = async (run) => {
|
|
95180
96707
|
overlayDepth += 1;
|
|
96708
|
+
debugEvent("overlay.enter", { depth: overlayDepth, dockVisible: dock.visible });
|
|
95181
96709
|
try {
|
|
95182
96710
|
return await run();
|
|
95183
96711
|
} finally {
|
|
95184
96712
|
overlayDepth -= 1;
|
|
96713
|
+
debugEvent("overlay.exit", { depth: overlayDepth, dockVisible: dock.visible });
|
|
95185
96714
|
}
|
|
95186
96715
|
};
|
|
95187
96716
|
const addOverlaySource = (isActive) => {
|
|
@@ -95430,9 +96959,11 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
95430
96959
|
});
|
|
95431
96960
|
textarea.onSubmit = () => {
|
|
95432
96961
|
if (overlayActive()) {
|
|
96962
|
+
debugEvent("composer.submit-blocked", { reason: "overlay active", dockVisible: dock.visible, overlayDepth });
|
|
95433
96963
|
return;
|
|
95434
96964
|
}
|
|
95435
96965
|
const line = input.value.trim();
|
|
96966
|
+
debugEvent("composer.submit", { chars: line.length, slash: line.startsWith("/") });
|
|
95436
96967
|
input.value = "";
|
|
95437
96968
|
hideMenu();
|
|
95438
96969
|
syncComposerHeight();
|
|
@@ -95550,6 +97081,15 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
95550
97081
|
appliedTheme = theme;
|
|
95551
97082
|
};
|
|
95552
97083
|
const unsubTheme = onThemeChange((theme) => applyTheme(theme));
|
|
97084
|
+
{
|
|
97085
|
+
const run = currentDebugRun();
|
|
97086
|
+
if (run !== undefined) {
|
|
97087
|
+
setTimeout(() => {
|
|
97088
|
+
if (alive)
|
|
97089
|
+
showToast(`debug log: ${run.dir}`);
|
|
97090
|
+
}, 0);
|
|
97091
|
+
}
|
|
97092
|
+
}
|
|
95553
97093
|
return {
|
|
95554
97094
|
renderer: r,
|
|
95555
97095
|
root: rootRow,
|
|
@@ -97782,7 +99322,7 @@ function parseGitHubRemote(remote) {
|
|
|
97782
99322
|
}
|
|
97783
99323
|
function runGitText(args, cwd) {
|
|
97784
99324
|
try {
|
|
97785
|
-
const proc =
|
|
99325
|
+
const proc = spawnSync3("git", args, {
|
|
97786
99326
|
cwd,
|
|
97787
99327
|
encoding: "utf8",
|
|
97788
99328
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -97799,7 +99339,7 @@ function runGitText(args, cwd) {
|
|
|
97799
99339
|
}
|
|
97800
99340
|
function runGhJson(repo, branch, cwd) {
|
|
97801
99341
|
try {
|
|
97802
|
-
const proc =
|
|
99342
|
+
const proc = spawnSync3("gh", ["pr", "view", branch, "--repo", repo, "--json", "url"], {
|
|
97803
99343
|
cwd,
|
|
97804
99344
|
encoding: "utf8",
|
|
97805
99345
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -98114,6 +99654,7 @@ function attachBlockIo(io, addBlock, chrome = {}, thinkDisplay = () => "auto") {
|
|
|
98114
99654
|
});
|
|
98115
99655
|
};
|
|
98116
99656
|
io.onToolCall = (name, input) => {
|
|
99657
|
+
debugEvent("tool.call", { name, inputChars: input.length });
|
|
98117
99658
|
chrome.onToolCall?.(name, input);
|
|
98118
99659
|
const args = summarizeToolArgs(input);
|
|
98119
99660
|
addBlock({
|
|
@@ -98125,6 +99666,7 @@ function attachBlockIo(io, addBlock, chrome = {}, thinkDisplay = () => "auto") {
|
|
|
98125
99666
|
}, { hint: "ctrl+o", tone: "cyan" });
|
|
98126
99667
|
};
|
|
98127
99668
|
io.onToolResult = (name, result) => {
|
|
99669
|
+
debugEvent("tool.result", { name, isError: result.isError === true, outputChars: result.output.length });
|
|
98128
99670
|
chrome.onToolResult?.(name, result);
|
|
98129
99671
|
const { summary, lineCount, hidden } = collapseToolOutput(result.output);
|
|
98130
99672
|
const more = hidden > 0 ? ` \xB7 +${hidden} more` : "";
|
|
@@ -98280,8 +99822,8 @@ async function applyRuntimeSwitchToSlate(params) {
|
|
|
98280
99822
|
if (params.slateSession === undefined || !params.slateSession.opened) {
|
|
98281
99823
|
return false;
|
|
98282
99824
|
}
|
|
98283
|
-
const result = await
|
|
98284
|
-
if (!result.changed) {
|
|
99825
|
+
const result = await recordSlateSessionTouch(params.slateSession, [], { runtime: params.runtime });
|
|
99826
|
+
if (result === undefined || !result.changed) {
|
|
98285
99827
|
return false;
|
|
98286
99828
|
}
|
|
98287
99829
|
params.history.push({
|
|
@@ -98292,6 +99834,9 @@ async function applyRuntimeSwitchToSlate(params) {
|
|
|
98292
99834
|
params.onHistoryChange?.("tool");
|
|
98293
99835
|
return true;
|
|
98294
99836
|
}
|
|
99837
|
+
function freshSlateSessionRef(sessionDir, cwd) {
|
|
99838
|
+
return { dir: sessionDir, cwd, opened: false };
|
|
99839
|
+
}
|
|
98295
99840
|
function fmtTokens(n) {
|
|
98296
99841
|
return n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n);
|
|
98297
99842
|
}
|
|
@@ -99248,21 +100793,40 @@ function formatSessionDate(iso) {
|
|
|
99248
100793
|
const short = iso.trim().replace("T", " ");
|
|
99249
100794
|
return short.length >= 16 ? short.slice(0, 16) : short;
|
|
99250
100795
|
}
|
|
99251
|
-
function
|
|
99252
|
-
|
|
99253
|
-
const r = chrome !== undefined ? chrome.renderer : rOrChrome;
|
|
99254
|
-
const all = sessions.map((s) => {
|
|
100796
|
+
function sessionPickerOptions(sessions, leaseState = () => "free") {
|
|
100797
|
+
return sessions.map((s) => {
|
|
99255
100798
|
const created = formatSessionDate(s.createdAt);
|
|
99256
100799
|
const updated = formatSessionDate(s.updatedAt);
|
|
99257
100800
|
const short = shortSessionId(s.id);
|
|
99258
100801
|
const title = s.title.length > 52 ? `${s.title.slice(0, 49)}\u2026` : s.title;
|
|
100802
|
+
const label = withLeaseMarker(`${short} \xB7 ${title}`, leaseState(s.id));
|
|
99259
100803
|
return {
|
|
99260
100804
|
value: s.id,
|
|
99261
|
-
label
|
|
100805
|
+
label,
|
|
99262
100806
|
description: `${s.projectPath} \xB7 created ${created} \xB7 updated ${updated}`,
|
|
99263
|
-
search: `${s.id} ${
|
|
100807
|
+
search: `${s.id} ${label} ${s.projectPath} ${s.title} ${created} ${updated}`.toLowerCase()
|
|
99264
100808
|
};
|
|
99265
100809
|
});
|
|
100810
|
+
}
|
|
100811
|
+
function startupSessionChoices(rows, leaseState = () => "free") {
|
|
100812
|
+
return [
|
|
100813
|
+
{
|
|
100814
|
+
id: "__new__",
|
|
100815
|
+
label: "New session",
|
|
100816
|
+
description: "Start fresh (old sessions stay on disk)",
|
|
100817
|
+
recommended: true
|
|
100818
|
+
},
|
|
100819
|
+
...rows.map((s) => ({
|
|
100820
|
+
id: s.id,
|
|
100821
|
+
label: withLeaseMarker(s.title.length > 40 ? `${s.title.slice(0, 37)}\u2026` : s.title, leaseState(s.id)),
|
|
100822
|
+
description: `${shortSessionId(s.id)} \xB7 ctx ${s.messageCount} \xB7 ${s.updatedAt.slice(0, 16).replace("T", " ")}`
|
|
100823
|
+
}))
|
|
100824
|
+
];
|
|
100825
|
+
}
|
|
100826
|
+
function pickSessionInTui(otui, rOrChrome, sessions, leaseState) {
|
|
100827
|
+
const chrome = isModalChrome(rOrChrome) ? rOrChrome : undefined;
|
|
100828
|
+
const r = chrome !== undefined ? chrome.renderer : rOrChrome;
|
|
100829
|
+
const all = sessionPickerOptions(sessions, leaseState);
|
|
99266
100830
|
const listSpec = {
|
|
99267
100831
|
idPrefix: "sp",
|
|
99268
100832
|
items: all,
|
|
@@ -99369,11 +100933,13 @@ async function launchTuiAgentShell(opts) {
|
|
|
99369
100933
|
let liveJobs;
|
|
99370
100934
|
let detachExternal;
|
|
99371
100935
|
const foregroundOperation = createForegroundOperationOwner();
|
|
100936
|
+
const sessionLease = createTuiLeaseHolder();
|
|
99372
100937
|
try {
|
|
99373
100938
|
const r = renderer = await createShellRenderer(otui, {
|
|
99374
100939
|
onDestroy: () => {
|
|
99375
100940
|
foregroundOperation.cancel("renderer destroyed");
|
|
99376
100941
|
foregroundOperation.dispose();
|
|
100942
|
+
sessionLease.release();
|
|
99377
100943
|
mountedChrome?.destroy();
|
|
99378
100944
|
setAskUserHost(undefined);
|
|
99379
100945
|
setSubagentFleetListener(undefined);
|
|
@@ -99406,7 +100972,8 @@ async function launchTuiAgentShell(opts) {
|
|
|
99406
100972
|
saveShellConfig(sel.baseUrl === undefined ? { provider: sel.provider, model: sel.model } : { provider: sel.provider, model: sel.model, baseUrl: sel.baseUrl });
|
|
99407
100973
|
let currentSel = sel;
|
|
99408
100974
|
const suggestionGate = new NextStepSuggestionGate;
|
|
99409
|
-
|
|
100975
|
+
const liveSlateSession = () => whilePersisting(slateSession, () => sessionLease.canPersist());
|
|
100976
|
+
let deps = await opts.makeAgentDeps(sel, liveSlateSession);
|
|
99410
100977
|
liveDeps = deps;
|
|
99411
100978
|
let reasoningOverride;
|
|
99412
100979
|
let thinkDisplayMode = resolveThinkDisplayMode(loadShellConfig().thinkDisplay);
|
|
@@ -99432,6 +100999,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
99432
100999
|
let busyStartedAt = 0;
|
|
99433
101000
|
const setBusyPhase = (phase) => {
|
|
99434
101001
|
busyPhase = phase;
|
|
101002
|
+
debugEvent("busy.phase", { phase });
|
|
99435
101003
|
chrome.setBusyPhase(phase);
|
|
99436
101004
|
};
|
|
99437
101005
|
const startBusy = (phase = "waiting for model") => {
|
|
@@ -99478,7 +101046,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
99478
101046
|
}));
|
|
99479
101047
|
};
|
|
99480
101048
|
const refreshWorkspaceSidebar = async () => {
|
|
99481
|
-
const dir =
|
|
101049
|
+
const dir = liveSlateSession()?.dir;
|
|
99482
101050
|
const workspaceId = dir !== undefined ? (await readSlate(dir).catch(() => {
|
|
99483
101051
|
return;
|
|
99484
101052
|
}))?.workspaceId : undefined;
|
|
@@ -99654,6 +101222,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
99654
101222
|
})();
|
|
99655
101223
|
};
|
|
99656
101224
|
const setMainAgent = (status, detail) => {
|
|
101225
|
+
debugEvent("main-agent", { status, detail });
|
|
99657
101226
|
fleet.upsert({
|
|
99658
101227
|
id: MAIN_AGENT_ID,
|
|
99659
101228
|
label: "main",
|
|
@@ -100099,7 +101668,13 @@ async function launchTuiAgentShell(opts) {
|
|
|
100099
101668
|
let nextArchiveIndex = 0;
|
|
100100
101669
|
let sessionPersistTimer;
|
|
100101
101670
|
let slateSession;
|
|
101671
|
+
const bindSlateToLiveSession = () => {
|
|
101672
|
+
slateSession = freshSlateSessionRef(liveSession.dir, sessionCwd);
|
|
101673
|
+
};
|
|
100102
101674
|
const onContextCompaction = (r) => {
|
|
101675
|
+
if (!sessionLease.canPersist()) {
|
|
101676
|
+
return;
|
|
101677
|
+
}
|
|
100103
101678
|
const persisted = persistCompacted(liveSession, r.context, archive, {
|
|
100104
101679
|
provider: currentSel.provider,
|
|
100105
101680
|
model: currentSel.model
|
|
@@ -100131,7 +101706,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
100131
101706
|
return;
|
|
100132
101707
|
}
|
|
100133
101708
|
chrome.hideMenu();
|
|
100134
|
-
const pickId = await chrome.withOverlay(() => pickSessionInTui(otui, chrome, rows));
|
|
101709
|
+
const pickId = await chrome.withOverlay(() => pickSessionInTui(otui, chrome, rows, leaseState));
|
|
100135
101710
|
input.focus();
|
|
100136
101711
|
if (pickId === undefined) {
|
|
100137
101712
|
return;
|
|
@@ -100144,98 +101719,142 @@ async function launchTuiAgentShell(opts) {
|
|
|
100144
101719
|
}
|
|
100145
101720
|
return found;
|
|
100146
101721
|
};
|
|
101722
|
+
const leasedOpen = (target = {}) => sessionLease.switchTo(() => openLeasedSession({
|
|
101723
|
+
cwd: sessionCwd,
|
|
101724
|
+
...target,
|
|
101725
|
+
provider: currentSel.provider,
|
|
101726
|
+
model: currentSel.model
|
|
101727
|
+
}));
|
|
101728
|
+
const leaseState = leaseStateLookup(sessionCwd);
|
|
101729
|
+
const showOpenedNotes = (opened) => {
|
|
101730
|
+
if (opened.skipped !== undefined) {
|
|
101731
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
101732
|
+
id: `sessskip${uid++}`,
|
|
101733
|
+
content: otui.t`${otui.yellow(describeSkippedSession(opened.skipped).replace(/\n+$/, ""))}`,
|
|
101734
|
+
marginTop: 1
|
|
101735
|
+
}));
|
|
101736
|
+
}
|
|
101737
|
+
if (opened.archiveDegraded !== undefined) {
|
|
101738
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
101739
|
+
id: `sessdeg${uid++}`,
|
|
101740
|
+
content: otui.t`${otui.yellow(`archive unavailable \u2014 resumed from the active context (${opened.archiveDegraded})`)}`,
|
|
101741
|
+
marginTop: 1
|
|
101742
|
+
}));
|
|
101743
|
+
}
|
|
101744
|
+
if (opened.resumed) {
|
|
101745
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
101746
|
+
id: `sess${uid++}`,
|
|
101747
|
+
content: otui.t`${otui.dim(`session ${shortSessionId(liveSession.summary.id)} \xB7 ${liveSession.summary.title} \xB7 ctx ${history.length} \xB7 archive ${archive.length}`)}`,
|
|
101748
|
+
marginTop: 1
|
|
101749
|
+
}));
|
|
101750
|
+
for (const m of history.filter((x) => x.role === "user").slice(-5)) {
|
|
101751
|
+
const t = m.content.length > 100 ? `${m.content.slice(0, 97)}\u2026` : m.content;
|
|
101752
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
101753
|
+
id: `sessu${uid++}`,
|
|
101754
|
+
content: otui.t`${otui.dim(` \u276F ${t}`)}`
|
|
101755
|
+
}));
|
|
101756
|
+
}
|
|
101757
|
+
}
|
|
101758
|
+
};
|
|
101759
|
+
let startupCancelled = false;
|
|
101760
|
+
let viewedReadOnly = false;
|
|
100147
101761
|
try {
|
|
100148
101762
|
if (opts.session?.pickOnStart === true && opts.session.resumeId === undefined) {
|
|
100149
101763
|
const rows = listSessions(sessionCwd).slice(0, 12);
|
|
100150
101764
|
if (rows.length === 0) {
|
|
100151
|
-
applyOpened(
|
|
100152
|
-
cwd: sessionCwd,
|
|
100153
|
-
provider: currentSel.provider,
|
|
100154
|
-
model: currentSel.model
|
|
100155
|
-
}));
|
|
101765
|
+
applyOpened(leasedOpen());
|
|
100156
101766
|
} else {
|
|
100157
101767
|
chrome.hideMenu();
|
|
100158
101768
|
const pickId = await showComposerChoice(otui, r, chrome.dock, {
|
|
100159
101769
|
title: "Resume session (this project)",
|
|
100160
101770
|
subtitle: "Esc = new session",
|
|
100161
101771
|
cancelId: "__new__",
|
|
100162
|
-
options:
|
|
100163
|
-
{
|
|
100164
|
-
id: "__new__",
|
|
100165
|
-
label: "New session",
|
|
100166
|
-
description: "Start fresh (old sessions stay on disk)",
|
|
100167
|
-
recommended: true
|
|
100168
|
-
},
|
|
100169
|
-
...rows.map((s) => ({
|
|
100170
|
-
id: s.id,
|
|
100171
|
-
label: s.title.length > 40 ? `${s.title.slice(0, 37)}\u2026` : s.title,
|
|
100172
|
-
description: `${shortSessionId(s.id)} \xB7 ctx ${s.messageCount} \xB7 ${s.updatedAt.slice(0, 16).replace("T", " ")}`
|
|
100173
|
-
}))
|
|
100174
|
-
]
|
|
101772
|
+
options: startupSessionChoices(rows, leaseState)
|
|
100175
101773
|
});
|
|
100176
101774
|
input.focus();
|
|
100177
101775
|
if (pickId === "__new__") {
|
|
100178
|
-
applyOpened(
|
|
100179
|
-
cwd: sessionCwd,
|
|
100180
|
-
provider: currentSel.provider,
|
|
100181
|
-
model: currentSel.model
|
|
100182
|
-
}));
|
|
101776
|
+
applyOpened(leasedOpen());
|
|
100183
101777
|
} else {
|
|
100184
|
-
|
|
100185
|
-
|
|
100186
|
-
|
|
100187
|
-
provider: currentSel.provider,
|
|
100188
|
-
model: currentSel.model
|
|
100189
|
-
}));
|
|
101778
|
+
const opened = leasedOpen({ resumeId: pickId });
|
|
101779
|
+
applyOpened(opened);
|
|
101780
|
+
showOpenedNotes(opened);
|
|
100190
101781
|
}
|
|
100191
101782
|
}
|
|
100192
101783
|
} else {
|
|
100193
|
-
const opened =
|
|
100194
|
-
cwd: sessionCwd,
|
|
101784
|
+
const opened = leasedOpen({
|
|
100195
101785
|
...opts.session?.continueLast === true ? { continueLast: true } : {},
|
|
100196
101786
|
...opts.session?.resumeId !== undefined ? { resumeId: opts.session.resumeId } : {},
|
|
100197
|
-
|
|
100198
|
-
|
|
101787
|
+
...opts.session?.fork === true ? { fork: true } : {},
|
|
101788
|
+
...opts.session?.takeOver === true ? { takeOver: true } : {}
|
|
100199
101789
|
});
|
|
100200
101790
|
applyOpened(opened);
|
|
100201
|
-
|
|
100202
|
-
|
|
100203
|
-
|
|
100204
|
-
|
|
100205
|
-
|
|
100206
|
-
|
|
100207
|
-
|
|
100208
|
-
|
|
100209
|
-
|
|
100210
|
-
|
|
100211
|
-
|
|
100212
|
-
|
|
100213
|
-
|
|
100214
|
-
|
|
100215
|
-
|
|
100216
|
-
|
|
100217
|
-
|
|
100218
|
-
|
|
100219
|
-
|
|
101791
|
+
showOpenedNotes(opened);
|
|
101792
|
+
}
|
|
101793
|
+
} catch (cause) {
|
|
101794
|
+
let failure = cause;
|
|
101795
|
+
if (cause instanceof SessionLeasedError) {
|
|
101796
|
+
try {
|
|
101797
|
+
const outcome = await resolveLeasedStartup(cause, {
|
|
101798
|
+
choose: async (error) => {
|
|
101799
|
+
chrome.hideMenu();
|
|
101800
|
+
const id = await showComposerChoice(otui, r, chrome.dock, leasedChoiceRequest(error));
|
|
101801
|
+
input.focus();
|
|
101802
|
+
return toLeasedChoice(error, id);
|
|
101803
|
+
},
|
|
101804
|
+
open: (target) => leasedOpen(target),
|
|
101805
|
+
exportSession: (sessionId) => exportSessionMarkdown(sessionCwd, sessionId),
|
|
101806
|
+
showReadOnly: (markdown) => {
|
|
101807
|
+
viewedReadOnly = true;
|
|
101808
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
101809
|
+
id: `sessview${uid++}`,
|
|
101810
|
+
content: otui.t`${otui.dim(`${markdown.replace(/\n+$/, "")}
|
|
101811
|
+
|
|
101812
|
+
\u2014 read-only view; a new session starts below \u2014`)}`,
|
|
101813
|
+
marginTop: 1
|
|
101814
|
+
}));
|
|
101815
|
+
},
|
|
101816
|
+
notice: (text) => {
|
|
101817
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
101818
|
+
id: `sesslease${uid++}`,
|
|
101819
|
+
content: otui.t`${otui.yellow(text)}`,
|
|
101820
|
+
marginTop: 1
|
|
101821
|
+
}));
|
|
101822
|
+
}
|
|
101823
|
+
});
|
|
101824
|
+
if (outcome.kind === "cancelled") {
|
|
101825
|
+
startupCancelled = true;
|
|
101826
|
+
} else {
|
|
101827
|
+
applyOpened(outcome.opened);
|
|
101828
|
+
showOpenedNotes(outcome.opened);
|
|
100220
101829
|
}
|
|
101830
|
+
failure = undefined;
|
|
101831
|
+
} catch (leaseCause) {
|
|
101832
|
+
failure = leaseCause;
|
|
100221
101833
|
}
|
|
100222
101834
|
}
|
|
100223
|
-
|
|
100224
|
-
|
|
100225
|
-
|
|
100226
|
-
|
|
100227
|
-
|
|
100228
|
-
|
|
100229
|
-
|
|
100230
|
-
|
|
100231
|
-
|
|
100232
|
-
|
|
100233
|
-
|
|
101835
|
+
if (failure !== undefined) {
|
|
101836
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
101837
|
+
id: `sesserr${uid++}`,
|
|
101838
|
+
content: otui.t`${otui.red(failure instanceof Error ? failure.message : String(failure))}`,
|
|
101839
|
+
marginTop: 1
|
|
101840
|
+
}));
|
|
101841
|
+
applyOpened(leasedOpen());
|
|
101842
|
+
}
|
|
101843
|
+
}
|
|
101844
|
+
if (startupCancelled) {
|
|
101845
|
+
r.off("theme_mode", onThemeMode);
|
|
101846
|
+
r.destroy();
|
|
101847
|
+
await done;
|
|
101848
|
+
return true;
|
|
100234
101849
|
}
|
|
100235
|
-
|
|
101850
|
+
bindSlateToLiveSession();
|
|
100236
101851
|
if (history.length === 0) {
|
|
100237
101852
|
removeSplash = mountEmptyTranscriptSplash(otui, r, transcript);
|
|
100238
101853
|
}
|
|
101854
|
+
if (viewedReadOnly) {
|
|
101855
|
+
removeSplash?.();
|
|
101856
|
+
removeSplash = undefined;
|
|
101857
|
+
}
|
|
100239
101858
|
refreshWorkspaceSidebar();
|
|
100240
101859
|
refreshReviewSidebar();
|
|
100241
101860
|
const paintSessionHeader = () => {
|
|
@@ -100245,7 +101864,19 @@ async function launchTuiAgentShell(opts) {
|
|
|
100245
101864
|
const cx = liveSession.summary.compactCount > 0 ? ` \xB7 c\xD7${liveSession.summary.compactCount}` : "";
|
|
100246
101865
|
chrome.setTitle(`keryx \xB7 ${title} \xB7 ${sid}${cx} \xB7 ${label}`);
|
|
100247
101866
|
};
|
|
101867
|
+
sessionLease.onLost((message) => {
|
|
101868
|
+
detachSlateSession(slateSession);
|
|
101869
|
+
slateSession = undefined;
|
|
101870
|
+
if (sessionPersistTimer !== undefined) {
|
|
101871
|
+
clearTimeout(sessionPersistTimer);
|
|
101872
|
+
sessionPersistTimer = undefined;
|
|
101873
|
+
}
|
|
101874
|
+
io.onSystem?.(message);
|
|
101875
|
+
});
|
|
100248
101876
|
const saveSession = () => {
|
|
101877
|
+
if (!sessionLease.canPersist()) {
|
|
101878
|
+
return;
|
|
101879
|
+
}
|
|
100249
101880
|
liveSession = persistHistory(liveSession, history, {
|
|
100250
101881
|
archive,
|
|
100251
101882
|
provider: currentSel.provider,
|
|
@@ -100298,12 +101929,17 @@ async function launchTuiAgentShell(opts) {
|
|
|
100298
101929
|
usage.resetUsage();
|
|
100299
101930
|
};
|
|
100300
101931
|
const startNewSession = (note) => {
|
|
101932
|
+
let opened;
|
|
101933
|
+
try {
|
|
101934
|
+
opened = leasedOpen();
|
|
101935
|
+
} catch (cause) {
|
|
101936
|
+
io.onSystem?.(`Could not start a new session: ${cause instanceof Error ? cause.message : String(cause)}
|
|
101937
|
+
Staying in the current session.
|
|
101938
|
+
`);
|
|
101939
|
+
return false;
|
|
101940
|
+
}
|
|
100301
101941
|
resetSessionSurface();
|
|
100302
|
-
liveSession =
|
|
100303
|
-
cwd: sessionCwd,
|
|
100304
|
-
provider: currentSel.provider,
|
|
100305
|
-
model: currentSel.model
|
|
100306
|
-
});
|
|
101942
|
+
liveSession = opened.handle;
|
|
100307
101943
|
history = [];
|
|
100308
101944
|
archive = [];
|
|
100309
101945
|
nextArchiveIndex = 0;
|
|
@@ -100312,6 +101948,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
100312
101948
|
io.onSystem?.(`${note}
|
|
100313
101949
|
`);
|
|
100314
101950
|
}
|
|
101951
|
+
return true;
|
|
100315
101952
|
};
|
|
100316
101953
|
const resumeSessionInteractive = async () => {
|
|
100317
101954
|
const found = await pickRecentSession();
|
|
@@ -100320,19 +101957,18 @@ async function launchTuiAgentShell(opts) {
|
|
|
100320
101957
|
}
|
|
100321
101958
|
let opened;
|
|
100322
101959
|
try {
|
|
100323
|
-
opened =
|
|
100324
|
-
cwd: sessionCwd,
|
|
100325
|
-
resumeId: found.id,
|
|
100326
|
-
provider: currentSel.provider,
|
|
100327
|
-
model: currentSel.model
|
|
100328
|
-
});
|
|
101960
|
+
opened = leasedOpen({ resumeId: found.id });
|
|
100329
101961
|
} catch (cause) {
|
|
101962
|
+
const hint = cause instanceof SessionLeasedError ? `To continue it in this shell, start one with: keryx shell -r ${shortSessionId(found.id)} --fork
|
|
101963
|
+
` : "";
|
|
100330
101964
|
io.onSystem?.(`Could not resume ${shortSessionId(found.id)}: ${cause instanceof Error ? cause.message : String(cause)}
|
|
100331
|
-
Staying in the current session.
|
|
101965
|
+
` + hint + `Staying in the current session.
|
|
100332
101966
|
`);
|
|
100333
101967
|
return;
|
|
100334
101968
|
}
|
|
100335
101969
|
applyOpened(opened, true);
|
|
101970
|
+
bindSlateToLiveSession();
|
|
101971
|
+
refreshWorkspaceSidebar();
|
|
100336
101972
|
paintSessionHeader();
|
|
100337
101973
|
if (opened.archiveDegraded !== undefined) {
|
|
100338
101974
|
io.onSystem?.(`Archive unavailable \u2014 resumed from the active context (${opened.archiveDegraded})
|
|
@@ -100394,7 +102030,7 @@ Staying in the current session.
|
|
|
100394
102030
|
};
|
|
100395
102031
|
const showWorkspace = () => {
|
|
100396
102032
|
(async () => {
|
|
100397
|
-
const dir =
|
|
102033
|
+
const dir = liveSlateSession()?.dir;
|
|
100398
102034
|
const workspaceId = dir !== undefined ? (await readSlate(dir).catch(() => {
|
|
100399
102035
|
return;
|
|
100400
102036
|
}))?.workspaceId : undefined;
|
|
@@ -100613,7 +102249,7 @@ Staying in the current session.
|
|
|
100613
102249
|
};
|
|
100614
102250
|
const switchTo = async (ns) => {
|
|
100615
102251
|
currentSel = ns;
|
|
100616
|
-
deps = { ...await opts.makeAgentDeps(ns,
|
|
102252
|
+
deps = { ...await opts.makeAgentDeps(ns, liveSlateSession), onContextCompaction };
|
|
100617
102253
|
liveDeps = deps;
|
|
100618
102254
|
saveShellConfig(ns.baseUrl === undefined ? { provider: ns.provider, model: ns.model } : { provider: ns.provider, model: ns.model, baseUrl: ns.baseUrl });
|
|
100619
102255
|
updateModelLabels();
|
|
@@ -100901,7 +102537,7 @@ Staying in the current session.
|
|
|
100901
102537
|
});
|
|
100902
102538
|
let answer = "";
|
|
100903
102539
|
try {
|
|
100904
|
-
const base = await opts.makeAgentDeps(currentSel,
|
|
102540
|
+
const base = await opts.makeAgentDeps(currentSel, liveSlateSession);
|
|
100905
102541
|
const tools = base.tools.filter((t) => t.definition.risk === "read" && !SIDE_WORKER_DENIED_TOOL_NAMES.has(t.definition.name));
|
|
100906
102542
|
const sideDeps = {
|
|
100907
102543
|
...base,
|
|
@@ -101018,6 +102654,7 @@ Staying in the current session.
|
|
|
101018
102654
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
101019
102655
|
await deps.sweepBackgroundJobs?.();
|
|
101020
102656
|
jobs.removeAll();
|
|
102657
|
+
sessionLease.release();
|
|
101021
102658
|
r.off("theme_mode", onThemeMode);
|
|
101022
102659
|
r.destroy();
|
|
101023
102660
|
})();
|
|
@@ -101206,6 +102843,7 @@ Staying in the current session.
|
|
|
101206
102843
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
101207
102844
|
await deps.sweepBackgroundJobs?.();
|
|
101208
102845
|
jobs.removeAll();
|
|
102846
|
+
sessionLease.release();
|
|
101209
102847
|
r.off("theme_mode", onThemeMode);
|
|
101210
102848
|
r.destroy();
|
|
101211
102849
|
})();
|
|
@@ -101230,8 +102868,10 @@ Staying in the current session.
|
|
|
101230
102868
|
if (command.name === "/clear" || command.name === "/new") {
|
|
101231
102869
|
(async () => {
|
|
101232
102870
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
101233
|
-
startNewSession()
|
|
101234
|
-
|
|
102871
|
+
if (!startNewSession()) {
|
|
102872
|
+
return;
|
|
102873
|
+
}
|
|
102874
|
+
bindSlateToLiveSession();
|
|
101235
102875
|
refreshWorkspaceSidebar();
|
|
101236
102876
|
refreshReviewSidebar();
|
|
101237
102877
|
sessions.clear();
|
|
@@ -101261,6 +102901,10 @@ Staying in the current session.
|
|
|
101261
102901
|
return;
|
|
101262
102902
|
}
|
|
101263
102903
|
if (command.name === "/compact") {
|
|
102904
|
+
if (!sessionLease.canPersist()) {
|
|
102905
|
+
io.onSystem?.(LOST_LEASE_COMPACT_REFUSAL);
|
|
102906
|
+
return;
|
|
102907
|
+
}
|
|
101264
102908
|
const focus = line.trim().split(/\s+/).slice(1).join(" ").trim();
|
|
101265
102909
|
const packed = compactSession(liveSession, history, archive, {
|
|
101266
102910
|
keepLastUserTurns: 3,
|
|
@@ -101795,6 +103439,7 @@ ${tail}`,
|
|
|
101795
103439
|
} catch {
|
|
101796
103440
|
return false;
|
|
101797
103441
|
} finally {
|
|
103442
|
+
sessionLease.release();
|
|
101798
103443
|
await herdr.release();
|
|
101799
103444
|
try {
|
|
101800
103445
|
renderer?.destroy();
|
|
@@ -102195,6 +103840,7 @@ init_patch_risk();
|
|
|
102195
103840
|
init_permission_mode();
|
|
102196
103841
|
init_agent();
|
|
102197
103842
|
init_slate_lifecycle();
|
|
103843
|
+
init_slate_lifecycle();
|
|
102198
103844
|
function reportMcpProblems(runtime) {
|
|
102199
103845
|
for (const problem of runtime.problems()) {
|
|
102200
103846
|
console.error(`keryx mcp: ${problem.file} ${problem.message}`);
|
|
@@ -102241,6 +103887,132 @@ function readlineAgentHelpText() {
|
|
|
102241
103887
|
` + renderCommandHelp("agent", READLINE_AGENT_COMMANDS) + `Sessions are per-project: keryx shell -c | -r [id] | keryx sessions list
|
|
102242
103888
|
`;
|
|
102243
103889
|
}
|
|
103890
|
+
function bareResumeTarget(cwd, emit) {
|
|
103891
|
+
const pick = latestUnleasedSession(cwd);
|
|
103892
|
+
if (pick.skipped !== undefined) {
|
|
103893
|
+
emit(describeSkippedSession(pick.skipped));
|
|
103894
|
+
}
|
|
103895
|
+
return pick.summary?.id;
|
|
103896
|
+
}
|
|
103897
|
+
function leasedOpenOptions(session, cwd, provider, model) {
|
|
103898
|
+
return {
|
|
103899
|
+
cwd,
|
|
103900
|
+
...session?.continueLast === true ? { continueLast: true } : {},
|
|
103901
|
+
...session?.resumeId !== undefined ? { resumeId: session.resumeId } : {},
|
|
103902
|
+
...session?.fork === true ? { fork: true } : {},
|
|
103903
|
+
...session?.takeOver === true ? { takeOver: true } : {},
|
|
103904
|
+
provider,
|
|
103905
|
+
model
|
|
103906
|
+
};
|
|
103907
|
+
}
|
|
103908
|
+
function openFreshLeasedSession(open, cwd, provider, model) {
|
|
103909
|
+
try {
|
|
103910
|
+
const opened = open({ cwd, provider, model });
|
|
103911
|
+
return { handle: opened.handle, lease: opened.lease };
|
|
103912
|
+
} catch {
|
|
103913
|
+
return;
|
|
103914
|
+
}
|
|
103915
|
+
}
|
|
103916
|
+
async function resolveLeasedChoice(io, error) {
|
|
103917
|
+
const rows = leasedChoiceRows(error);
|
|
103918
|
+
io.write(`${describeLeasedSession(error)}
|
|
103919
|
+
` + rows.map((row, index) => ` ${index + 1}) ${row.label}
|
|
103920
|
+
`).join(""));
|
|
103921
|
+
for (;; ) {
|
|
103922
|
+
io.write(`Choose [1-${rows.length}, default 1]: `);
|
|
103923
|
+
const line = await io.readLine();
|
|
103924
|
+
if (line === undefined) {
|
|
103925
|
+
io.write(`
|
|
103926
|
+
`);
|
|
103927
|
+
return "cancel";
|
|
103928
|
+
}
|
|
103929
|
+
const answer = line.trim().toLowerCase();
|
|
103930
|
+
if (answer.length === 0) {
|
|
103931
|
+
return "fork";
|
|
103932
|
+
}
|
|
103933
|
+
const byNumber = /^\d+$/.test(answer) ? rows[Number(answer) - 1] : undefined;
|
|
103934
|
+
const byName = rows.find((row) => row.choice === answer || row.choice.replace("-", " ") === answer || row.choice.replace("-", "") === answer);
|
|
103935
|
+
const picked = byNumber ?? byName;
|
|
103936
|
+
if (picked !== undefined) {
|
|
103937
|
+
return picked.choice;
|
|
103938
|
+
}
|
|
103939
|
+
io.write(`Not one of the choices: ${line.trim()}
|
|
103940
|
+
`);
|
|
103941
|
+
}
|
|
103942
|
+
}
|
|
103943
|
+
async function runWithLeaseChoice(session, run, runtime) {
|
|
103944
|
+
let current = session;
|
|
103945
|
+
for (;; ) {
|
|
103946
|
+
try {
|
|
103947
|
+
await run(current);
|
|
103948
|
+
return;
|
|
103949
|
+
} catch (error) {
|
|
103950
|
+
if (!(error instanceof SessionLeasedError)) {
|
|
103951
|
+
throw error;
|
|
103952
|
+
}
|
|
103953
|
+
if (!runtime.isTty) {
|
|
103954
|
+
runtime.writeError(`${error.message}
|
|
103955
|
+
`);
|
|
103956
|
+
return 1;
|
|
103957
|
+
}
|
|
103958
|
+
const choice = await resolveLeasedChoice(runtime.io, error);
|
|
103959
|
+
const base = {
|
|
103960
|
+
cwd: current.cwd,
|
|
103961
|
+
...current.enabled !== undefined ? { enabled: current.enabled } : {},
|
|
103962
|
+
...current.leaseBox !== undefined ? { leaseBox: current.leaseBox } : {},
|
|
103963
|
+
...current.openLeased !== undefined ? { openLeased: current.openLeased } : {}
|
|
103964
|
+
};
|
|
103965
|
+
if (choice === "cancel") {
|
|
103966
|
+
return 0;
|
|
103967
|
+
}
|
|
103968
|
+
if (choice === "view") {
|
|
103969
|
+
try {
|
|
103970
|
+
const render = runtime.exportSession ?? ((cwd, id) => exportSessionMarkdown(cwd, id));
|
|
103971
|
+
runtime.io.write(`${render(current.cwd, error.summary.id)}
|
|
103972
|
+
`);
|
|
103973
|
+
} catch (cause) {
|
|
103974
|
+
runtime.io.write(`Could not render the session: ${cause instanceof Error ? cause.message : String(cause)}
|
|
103975
|
+
`);
|
|
103976
|
+
}
|
|
103977
|
+
current = base;
|
|
103978
|
+
} else if (choice === "fork") {
|
|
103979
|
+
current = { ...base, resumeId: error.summary.id, fork: true };
|
|
103980
|
+
} else {
|
|
103981
|
+
current = { ...base, resumeId: error.summary.id, takeOver: true };
|
|
103982
|
+
}
|
|
103983
|
+
}
|
|
103984
|
+
}
|
|
103985
|
+
}
|
|
103986
|
+
async function runLeasedChatShell(io, deps, run = runShell, notes = []) {
|
|
103987
|
+
const system = (text) => {
|
|
103988
|
+
if (io.onSystem !== undefined) {
|
|
103989
|
+
io.onSystem(text);
|
|
103990
|
+
} else {
|
|
103991
|
+
io.write(text);
|
|
103992
|
+
}
|
|
103993
|
+
};
|
|
103994
|
+
for (const note of notes) {
|
|
103995
|
+
system(note);
|
|
103996
|
+
}
|
|
103997
|
+
if (deps.session === undefined) {
|
|
103998
|
+
await run(io, deps);
|
|
103999
|
+
return;
|
|
104000
|
+
}
|
|
104001
|
+
let iterator;
|
|
104002
|
+
const runtime = {
|
|
104003
|
+
isTty: true,
|
|
104004
|
+
io: {
|
|
104005
|
+
write: system,
|
|
104006
|
+
readLine: async () => {
|
|
104007
|
+
iterator ??= io.lines[Symbol.asyncIterator]();
|
|
104008
|
+
const next = await iterator.next();
|
|
104009
|
+
return next.done === true ? undefined : next.value;
|
|
104010
|
+
}
|
|
104011
|
+
},
|
|
104012
|
+
writeError: system
|
|
104013
|
+
};
|
|
104014
|
+
return runWithLeaseChoice(deps.session, (session) => run(io, { ...deps, session }), runtime);
|
|
104015
|
+
}
|
|
102244
104016
|
async function runShell(io, deps) {
|
|
102245
104017
|
let providerName = deps.initial.provider;
|
|
102246
104018
|
let modelName = deps.initial.model;
|
|
@@ -102259,17 +104031,22 @@ async function runShell(io, deps) {
|
|
|
102259
104031
|
let live;
|
|
102260
104032
|
let history = [];
|
|
102261
104033
|
let archive = [];
|
|
104034
|
+
let lease;
|
|
104035
|
+
const leaseWatch = watchLeaseLoss((message) => system(message));
|
|
104036
|
+
const holdLease = (next) => {
|
|
104037
|
+
lease = next;
|
|
104038
|
+
leaseWatch.track(next);
|
|
104039
|
+
if (deps.session?.leaseBox !== undefined)
|
|
104040
|
+
deps.session.leaseBox.current = next;
|
|
104041
|
+
};
|
|
104042
|
+
const openLeased = deps.session?.openLeased ?? openLeasedSession;
|
|
102262
104043
|
if (sessionsOn) {
|
|
102263
104044
|
try {
|
|
102264
|
-
const
|
|
102265
|
-
|
|
102266
|
-
|
|
102267
|
-
|
|
102268
|
-
|
|
102269
|
-
...resumeId !== undefined ? { resumeId } : {},
|
|
102270
|
-
provider: providerName,
|
|
102271
|
-
model: modelName
|
|
102272
|
-
});
|
|
104045
|
+
const opened = openLeased(leasedOpenOptions(deps.session, sessionCwd, providerName, modelName));
|
|
104046
|
+
holdLease(opened.lease);
|
|
104047
|
+
if (opened.skipped !== undefined) {
|
|
104048
|
+
system(describeSkippedSession(opened.skipped));
|
|
104049
|
+
}
|
|
102273
104050
|
live = opened.handle;
|
|
102274
104051
|
history = opened.history;
|
|
102275
104052
|
archive = opened.archive.length > 0 ? [...opened.archive] : [...opened.history];
|
|
@@ -102282,7 +104059,12 @@ async function runShell(io, deps) {
|
|
|
102282
104059
|
`);
|
|
102283
104060
|
}
|
|
102284
104061
|
} catch (cause) {
|
|
102285
|
-
|
|
104062
|
+
if (cause instanceof SessionLeasedError) {
|
|
104063
|
+
throw cause;
|
|
104064
|
+
}
|
|
104065
|
+
const fresh = openFreshLeasedSession(openLeased, sessionCwd, providerName, modelName);
|
|
104066
|
+
holdLease(fresh?.lease);
|
|
104067
|
+
live = fresh?.handle ?? createSession({ cwd: sessionCwd, provider: providerName, model: modelName });
|
|
102286
104068
|
history = [];
|
|
102287
104069
|
archive = [];
|
|
102288
104070
|
system(`${cause instanceof Error ? cause.message : String(cause)}
|
|
@@ -102290,8 +104072,12 @@ Starting a new session.
|
|
|
102290
104072
|
`);
|
|
102291
104073
|
}
|
|
102292
104074
|
}
|
|
104075
|
+
const releaseLease = () => {
|
|
104076
|
+
releaseSessionLease(lease);
|
|
104077
|
+
holdLease(undefined);
|
|
104078
|
+
};
|
|
102293
104079
|
const save = () => {
|
|
102294
|
-
if (live === undefined) {
|
|
104080
|
+
if (live === undefined || !leaseWatch.canPersist()) {
|
|
102295
104081
|
return;
|
|
102296
104082
|
}
|
|
102297
104083
|
try {
|
|
@@ -102318,6 +104104,7 @@ Starting a new session.
|
|
|
102318
104104
|
const command = parts[0] ?? "";
|
|
102319
104105
|
const argument = parts.slice(1).join(" ");
|
|
102320
104106
|
if (command === "/exit" || command === "/quit") {
|
|
104107
|
+
releaseLease();
|
|
102321
104108
|
return;
|
|
102322
104109
|
}
|
|
102323
104110
|
if (command === "/help") {
|
|
@@ -102377,7 +104164,16 @@ ${formatThemeList(getThemeId())}`);
|
|
|
102377
104164
|
}
|
|
102378
104165
|
if (command === "/clear" || command === "/new") {
|
|
102379
104166
|
if (sessionsOn) {
|
|
102380
|
-
|
|
104167
|
+
try {
|
|
104168
|
+
const next = switchLeasedSession(lease, () => openLeased({ cwd: sessionCwd, provider: providerName, model: modelName }));
|
|
104169
|
+
holdLease(next.lease);
|
|
104170
|
+
live = next.handle;
|
|
104171
|
+
} catch (cause) {
|
|
104172
|
+
system(`${cause instanceof Error ? cause.message : String(cause)}
|
|
104173
|
+
Kept the current session.
|
|
104174
|
+
`);
|
|
104175
|
+
continue;
|
|
104176
|
+
}
|
|
102381
104177
|
history = [];
|
|
102382
104178
|
archive = [];
|
|
102383
104179
|
system(`New session ${shortSessionId(live.summary.id)} (previous kept on disk)
|
|
@@ -102393,6 +104189,10 @@ ${formatThemeList(getThemeId())}`);
|
|
|
102393
104189
|
`);
|
|
102394
104190
|
continue;
|
|
102395
104191
|
}
|
|
104192
|
+
if (!leaseWatch.canPersist()) {
|
|
104193
|
+
system(LOST_LEASE_COMPACT_REFUSAL);
|
|
104194
|
+
continue;
|
|
104195
|
+
}
|
|
102396
104196
|
const focus = argument.trim();
|
|
102397
104197
|
const packed = compactSession(live, history, archive, {
|
|
102398
104198
|
keepLastUserTurns: 3,
|
|
@@ -102528,6 +104328,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
102528
104328
|
|
|
102529
104329
|
`);
|
|
102530
104330
|
}
|
|
104331
|
+
releaseLease();
|
|
102531
104332
|
}
|
|
102532
104333
|
function realMakeProvider(write) {
|
|
102533
104334
|
return (name, model, baseUrl) => {
|
|
@@ -103144,7 +104945,7 @@ ${text.trim()}`;
|
|
|
103144
104945
|
deps = {
|
|
103145
104946
|
...deps,
|
|
103146
104947
|
onContextCompaction: (r) => {
|
|
103147
|
-
if (live === undefined) {
|
|
104948
|
+
if (live === undefined || !leaseWatch.canPersist()) {
|
|
103148
104949
|
return;
|
|
103149
104950
|
}
|
|
103150
104951
|
const persisted = persistCompacted(live, r.context, archive, {
|
|
@@ -103157,17 +104958,44 @@ ${text.trim()}`;
|
|
|
103157
104958
|
`);
|
|
103158
104959
|
}
|
|
103159
104960
|
};
|
|
104961
|
+
let lease;
|
|
104962
|
+
const leaseWatch = watchLeaseLoss((message) => {
|
|
104963
|
+
detachSlateSession(slateSession);
|
|
104964
|
+
slateSession = undefined;
|
|
104965
|
+
slateSessionBox.current = undefined;
|
|
104966
|
+
agentIo.onSystem?.(message);
|
|
104967
|
+
});
|
|
104968
|
+
gateBoxByLease(slateSessionBox, () => leaseWatch.canPersist());
|
|
104969
|
+
const holdLease = (next) => {
|
|
104970
|
+
lease = next;
|
|
104971
|
+
leaseWatch.track(next);
|
|
104972
|
+
if (sessionOpts?.leaseBox !== undefined)
|
|
104973
|
+
sessionOpts.leaseBox.current = next;
|
|
104974
|
+
};
|
|
104975
|
+
const openLeased = sessionOpts?.openLeased ?? openLeasedSession;
|
|
104976
|
+
const switchNewSession = () => {
|
|
104977
|
+
try {
|
|
104978
|
+
const next = switchLeasedSession(lease, () => openLeased({ cwd: sessionCwd, provider: deps.providerId, model: deps.modelId }));
|
|
104979
|
+
holdLease(next.lease);
|
|
104980
|
+
return next.handle;
|
|
104981
|
+
} catch (cause) {
|
|
104982
|
+
agentIo.onSystem?.(`${cause instanceof Error ? cause.message : String(cause)}
|
|
104983
|
+
Kept the current session.
|
|
104984
|
+
`);
|
|
104985
|
+
return false;
|
|
104986
|
+
}
|
|
104987
|
+
};
|
|
104988
|
+
const releaseLease = () => {
|
|
104989
|
+
releaseSessionLease(lease);
|
|
104990
|
+
holdLease(undefined);
|
|
104991
|
+
};
|
|
103160
104992
|
if (sessionsOn) {
|
|
103161
104993
|
try {
|
|
103162
|
-
const
|
|
103163
|
-
|
|
103164
|
-
|
|
103165
|
-
|
|
103166
|
-
|
|
103167
|
-
...resumeId !== undefined ? { resumeId } : {},
|
|
103168
|
-
provider: deps.providerId,
|
|
103169
|
-
model: deps.modelId
|
|
103170
|
-
});
|
|
104994
|
+
const opened = openLeased(leasedOpenOptions(sessionOpts, sessionCwd, deps.providerId, deps.modelId));
|
|
104995
|
+
holdLease(opened.lease);
|
|
104996
|
+
if (opened.skipped !== undefined) {
|
|
104997
|
+
agentIo.onSystem?.(describeSkippedSession(opened.skipped));
|
|
104998
|
+
}
|
|
103171
104999
|
live = opened.handle;
|
|
103172
105000
|
history = opened.history;
|
|
103173
105001
|
archive = opened.archive.length > 0 ? [...opened.archive] : [...opened.history];
|
|
@@ -103180,7 +105008,12 @@ ${text.trim()}`;
|
|
|
103180
105008
|
`);
|
|
103181
105009
|
}
|
|
103182
105010
|
} catch (cause) {
|
|
103183
|
-
|
|
105011
|
+
if (cause instanceof SessionLeasedError) {
|
|
105012
|
+
throw cause;
|
|
105013
|
+
}
|
|
105014
|
+
const fresh = openFreshLeasedSession(openLeased, sessionCwd, deps.providerId, deps.modelId);
|
|
105015
|
+
holdLease(fresh?.lease);
|
|
105016
|
+
live = fresh?.handle ?? createSession({
|
|
103184
105017
|
cwd: sessionCwd,
|
|
103185
105018
|
provider: deps.providerId,
|
|
103186
105019
|
model: deps.modelId
|
|
@@ -103196,7 +105029,7 @@ New session ${shortSessionId(live.summary.id)}.
|
|
|
103196
105029
|
slateSession = live !== undefined ? { dir: live.dir, cwd: sessionCwd, opened: false } : undefined;
|
|
103197
105030
|
slateSessionBox.current = slateSession;
|
|
103198
105031
|
const save = () => {
|
|
103199
|
-
if (live === undefined) {
|
|
105032
|
+
if (live === undefined || !leaseWatch.canPersist()) {
|
|
103200
105033
|
return;
|
|
103201
105034
|
}
|
|
103202
105035
|
try {
|
|
@@ -103272,6 +105105,7 @@ ${GUTTER}${turnSeparator()}
|
|
|
103272
105105
|
if (line === undefined) {
|
|
103273
105106
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
103274
105107
|
await deps.sweepBackgroundJobs?.();
|
|
105108
|
+
releaseLease();
|
|
103275
105109
|
return;
|
|
103276
105110
|
}
|
|
103277
105111
|
rich.safeBoundary?.();
|
|
@@ -103282,6 +105116,7 @@ ${GUTTER}${turnSeparator()}
|
|
|
103282
105116
|
if (command === "/exit" || command === "/quit") {
|
|
103283
105117
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
103284
105118
|
await deps.sweepBackgroundJobs?.();
|
|
105119
|
+
releaseLease();
|
|
103285
105120
|
return;
|
|
103286
105121
|
}
|
|
103287
105122
|
if (command === "/help") {
|
|
@@ -103326,13 +105161,14 @@ ${GUTTER}${turnSeparator()}
|
|
|
103326
105161
|
`);
|
|
103327
105162
|
}
|
|
103328
105163
|
} else if (command === "/new" || command === "/clear") {
|
|
105164
|
+
const nextLive = sessionsOn ? switchNewSession() : undefined;
|
|
105165
|
+
if (nextLive === false) {
|
|
105166
|
+
rich.printPrompt();
|
|
105167
|
+
continue;
|
|
105168
|
+
}
|
|
103329
105169
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
103330
|
-
if (
|
|
103331
|
-
live =
|
|
103332
|
-
cwd: sessionCwd,
|
|
103333
|
-
provider: deps.providerId,
|
|
103334
|
-
model: deps.modelId
|
|
103335
|
-
});
|
|
105170
|
+
if (nextLive !== undefined) {
|
|
105171
|
+
live = nextLive;
|
|
103336
105172
|
history = [];
|
|
103337
105173
|
archive = [];
|
|
103338
105174
|
nextArchiveIndex = 0;
|
|
@@ -103350,6 +105186,8 @@ ${GUTTER}${turnSeparator()}
|
|
|
103350
105186
|
if (live === undefined) {
|
|
103351
105187
|
agentIo.onSystem?.(`No persistent session.
|
|
103352
105188
|
`);
|
|
105189
|
+
} else if (!leaseWatch.canPersist()) {
|
|
105190
|
+
agentIo.onSystem?.(LOST_LEASE_COMPACT_REFUSAL);
|
|
103353
105191
|
} else {
|
|
103354
105192
|
const packed = compactSession(live, history, archive, {
|
|
103355
105193
|
keepLastUserTurns: 3,
|
|
@@ -103636,6 +105474,14 @@ async function resolveTuiStartup(opts) {
|
|
|
103636
105474
|
appliedKeys
|
|
103637
105475
|
};
|
|
103638
105476
|
}
|
|
105477
|
+
|
|
105478
|
+
class ShellFlagError extends Error {
|
|
105479
|
+
exitCode = 2;
|
|
105480
|
+
constructor(message) {
|
|
105481
|
+
super(`${message}. See keryx shell --help.`);
|
|
105482
|
+
this.name = "ShellFlagError";
|
|
105483
|
+
}
|
|
105484
|
+
}
|
|
103639
105485
|
function parseShellCliFlags(args) {
|
|
103640
105486
|
if (args.includes("--help") || args.includes("-h")) {
|
|
103641
105487
|
return { help: true, wantTui: true };
|
|
@@ -103658,11 +105504,14 @@ function parseShellCliFlags(args) {
|
|
|
103658
105504
|
let continueLast;
|
|
103659
105505
|
let resumeId;
|
|
103660
105506
|
let resumePick;
|
|
105507
|
+
let fork;
|
|
105508
|
+
let takeOver;
|
|
103661
105509
|
let permissionModeFlag;
|
|
103662
105510
|
let denyTools;
|
|
103663
105511
|
let printPromptArg;
|
|
103664
105512
|
let eventsFile;
|
|
103665
105513
|
let eventsMaxField;
|
|
105514
|
+
let debug;
|
|
103666
105515
|
for (let i = 0;i < args.length; i++) {
|
|
103667
105516
|
const arg = args[i];
|
|
103668
105517
|
if (arg === "--provider") {
|
|
@@ -103693,6 +105542,10 @@ function parseShellCliFlags(args) {
|
|
|
103693
105542
|
} else {
|
|
103694
105543
|
resumePick = true;
|
|
103695
105544
|
}
|
|
105545
|
+
} else if (arg === "--fork") {
|
|
105546
|
+
fork = true;
|
|
105547
|
+
} else if (arg === "--take-over") {
|
|
105548
|
+
takeOver = true;
|
|
103696
105549
|
} else if (arg === "--permission-mode") {
|
|
103697
105550
|
const next = valueAfter(i++);
|
|
103698
105551
|
if (!isPermissionMode(next))
|
|
@@ -103713,6 +105566,8 @@ function parseShellCliFlags(args) {
|
|
|
103713
105566
|
i += 1;
|
|
103714
105567
|
} else if (arg === "--events-file") {
|
|
103715
105568
|
eventsFile = valueAfter(i++);
|
|
105569
|
+
} else if (arg === "--debug") {
|
|
105570
|
+
debug = true;
|
|
103716
105571
|
} else if (arg === "--events-max-field") {
|
|
103717
105572
|
const raw = valueAfter(i++);
|
|
103718
105573
|
const parsed = Number(raw);
|
|
@@ -103726,6 +105581,18 @@ function parseShellCliFlags(args) {
|
|
|
103726
105581
|
if (continueLast && (resumeId !== undefined || resumePick)) {
|
|
103727
105582
|
invalid("--continue and --resume cannot be combined");
|
|
103728
105583
|
}
|
|
105584
|
+
const leaseFlag = fork === true ? "--fork" : takeOver === true ? "--take-over" : undefined;
|
|
105585
|
+
if (leaseFlag !== undefined) {
|
|
105586
|
+
if (continueLast === true) {
|
|
105587
|
+
throw new ShellFlagError(`${leaseFlag} cannot be combined with --continue; use -r <id> ${leaseFlag}`);
|
|
105588
|
+
}
|
|
105589
|
+
if (fork === true && takeOver === true) {
|
|
105590
|
+
throw new ShellFlagError("--fork and --take-over cannot be combined; choose one");
|
|
105591
|
+
}
|
|
105592
|
+
if (resumeId === undefined) {
|
|
105593
|
+
throw new ShellFlagError(`${leaseFlag} needs an explicit session id; use -r <id> ${leaseFlag}`);
|
|
105594
|
+
}
|
|
105595
|
+
}
|
|
103729
105596
|
if (printPromptArg !== undefined && modeFlag === false) {
|
|
103730
105597
|
invalid("--print is agent-mode only and cannot be combined with --chat");
|
|
103731
105598
|
}
|
|
@@ -103738,11 +105605,14 @@ function parseShellCliFlags(args) {
|
|
|
103738
105605
|
...continueLast === true ? { continueLast: true } : {},
|
|
103739
105606
|
...resumeId !== undefined ? { resumeId } : {},
|
|
103740
105607
|
...resumePick === true ? { resumePick: true } : {},
|
|
105608
|
+
...fork === true ? { fork: true } : {},
|
|
105609
|
+
...takeOver === true ? { takeOver: true } : {},
|
|
103741
105610
|
...permissionModeFlag !== undefined ? { permissionModeFlag } : {},
|
|
103742
105611
|
...denyTools !== undefined ? { denyTools } : {},
|
|
103743
105612
|
...printPromptArg !== undefined ? { printPrompt: printPromptArg } : {},
|
|
103744
105613
|
...eventsFile !== undefined ? { eventsFile } : {},
|
|
103745
|
-
...eventsMaxField !== undefined ? { eventsMaxField } : {}
|
|
105614
|
+
...eventsMaxField !== undefined ? { eventsMaxField } : {},
|
|
105615
|
+
...debug === true ? { debug: true } : {}
|
|
103746
105616
|
};
|
|
103747
105617
|
}
|
|
103748
105618
|
function chooseShellSurface(flags, isTty) {
|
|
@@ -103755,6 +105625,11 @@ function chooseShellSurface(flags, isTty) {
|
|
|
103755
105625
|
return flags.modeFlag === false ? "tui-chat" : "tui-agent";
|
|
103756
105626
|
}
|
|
103757
105627
|
async function shellCommand(args, runtime = {}) {
|
|
105628
|
+
if (args[0] === "--debug-watcher") {
|
|
105629
|
+
await Promise.resolve().then(() => init_debug_watcher());
|
|
105630
|
+
await runDebugWatcher(args.slice(1));
|
|
105631
|
+
return;
|
|
105632
|
+
}
|
|
103758
105633
|
const flags = parseShellCliFlags(args);
|
|
103759
105634
|
if (flags.help) {
|
|
103760
105635
|
console.log(`Usage: keryx shell [options]
|
|
@@ -103765,21 +105640,55 @@ async function shellCommand(args, runtime = {}) {
|
|
|
103765
105640
|
--base-url <url> Override the provider endpoint
|
|
103766
105641
|
--agent | --chat Choose agent (default) or chat mode
|
|
103767
105642
|
--tui | --no-tui Prefer TUI (default) or readline; last flag wins
|
|
103768
|
-
--continue, -c Continue the
|
|
105643
|
+
--continue, -c Continue the most recent session no other shell has open
|
|
103769
105644
|
--resume, -r [id-or-title] Resume a session, or open the session picker
|
|
105645
|
+
--fork With -r <id>: continue in a fork of that session
|
|
105646
|
+
--take-over With -r <id>: take the session from a stale holder
|
|
103770
105647
|
--permission-mode <mode> Agent permissions: ask, trust or auto
|
|
103771
105648
|
--deny-tools <a,b> Withhold these tools from the session entirely
|
|
103772
105649
|
--ask | --trust | --auto Permission shortcuts; last flag wins
|
|
103773
105650
|
--print, -p <prompt> Run one agent turn on this prompt and exit
|
|
103774
105651
|
--events-file <path> Append an NDJSON transcript (turns, tools, usage)
|
|
103775
105652
|
--events-max-field <n> Per-field character cap in that transcript (default 4000)
|
|
105653
|
+
--debug Log the session (input, keys, dialogs, tools) and start a watcher
|
|
105654
|
+
that re-arms terminal input if it stops; see debug/latest.txt
|
|
103776
105655
|
|
|
103777
105656
|
Session continuation and resume are mutually exclusive. Permission flags
|
|
103778
|
-
are ignored in chat mode. Without a TTY, resume without an ID uses the latest
|
|
105657
|
+
are ignored in chat mode. Without a TTY, resume without an ID uses the latest
|
|
105658
|
+
session no other shell has open. A session open in another shell is refused;
|
|
105659
|
+
--fork and --take-over (stale holders only) need an explicit -r <id> and
|
|
105660
|
+
cannot be combined with each other or with --continue.
|
|
105661
|
+
|
|
105662
|
+
A shell that stops reacting to keys can be recovered from another terminal
|
|
105663
|
+
with: kill -USR2 <keryx pid>
|
|
103779
105664
|
|
|
103780
105665
|
Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
103781
105666
|
return;
|
|
103782
105667
|
}
|
|
105668
|
+
if (flags.debug === true) {
|
|
105669
|
+
await Promise.resolve().then(() => init_debug_log());
|
|
105670
|
+
await Promise.resolve().then(() => init_debug_watcher());
|
|
105671
|
+
const run = startDebugRun(runtime.cacheDir !== undefined ? { configDir: runtime.cacheDir } : {});
|
|
105672
|
+
const watcherPid = spawnDebugWatcher(run);
|
|
105673
|
+
debugEvent("session.start", {
|
|
105674
|
+
version: package_default.version,
|
|
105675
|
+
argv: args,
|
|
105676
|
+
bun: typeof Bun !== "undefined" ? Bun.version : undefined,
|
|
105677
|
+
platform: process.platform,
|
|
105678
|
+
execPath: process.execPath,
|
|
105679
|
+
cwd: process.cwd(),
|
|
105680
|
+
stdinTTY: process.stdin.isTTY === true,
|
|
105681
|
+
stdoutTTY: process.stdout.isTTY === true,
|
|
105682
|
+
env: Object.fromEntries(["TERM", "TERM_PROGRAM", "COLORTERM", "TMUX", "HERDR_ENV", "HERDR_PANE_ID", "SSH_TTY", "LANG"].map((k) => [k, process.env[k]])),
|
|
105683
|
+
watcherPid,
|
|
105684
|
+
dir: run.dir
|
|
105685
|
+
});
|
|
105686
|
+
process.on("exit", (code) => {
|
|
105687
|
+
debugEvent("session.exit", { code });
|
|
105688
|
+
process.stderr.write(`keryx debug log: ${run.dir}
|
|
105689
|
+
`);
|
|
105690
|
+
});
|
|
105691
|
+
}
|
|
103783
105692
|
const versionCheck = (runtime.checkVersion ?? (() => checkVersion({
|
|
103784
105693
|
currentVersion: package_default.version,
|
|
103785
105694
|
...runtime.cacheDir !== undefined ? { cacheDir: runtime.cacheDir } : {}
|
|
@@ -103788,6 +105697,10 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
103788
105697
|
const modelArg = flags.modelArg;
|
|
103789
105698
|
let baseUrl = flags.baseUrl;
|
|
103790
105699
|
let modeFlag = flags.modeFlag;
|
|
105700
|
+
const leaseFlagOpts = {
|
|
105701
|
+
...flags.fork === true ? { fork: true } : {},
|
|
105702
|
+
...flags.takeOver === true ? { takeOver: true } : {}
|
|
105703
|
+
};
|
|
103791
105704
|
{
|
|
103792
105705
|
await Promise.resolve().then(() => init_login());
|
|
103793
105706
|
const oauthFetch = (input, init) => globalThis.fetch(input, init);
|
|
@@ -103887,7 +105800,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
103887
105800
|
sessionOverride: reasoningSessionOverride,
|
|
103888
105801
|
globalEffort: loadShellConfig(runtime.cacheDir).reasoningEffort
|
|
103889
105802
|
}),
|
|
103890
|
-
idSeq: () =>
|
|
105803
|
+
idSeq: () => randomUUID30(),
|
|
103891
105804
|
askUser: invokeAskUserHost,
|
|
103892
105805
|
sweepBackgroundJobs: () => jobRegistry.sweepAll(),
|
|
103893
105806
|
jobRegistry,
|
|
@@ -103918,29 +105831,40 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
103918
105831
|
});
|
|
103919
105832
|
const tuiInitial = startup.initial;
|
|
103920
105833
|
const tuiDetected = startup.detected;
|
|
105834
|
+
const chatLeaseBox = { current: undefined };
|
|
103921
105835
|
try {
|
|
103922
105836
|
if (surface === "tui-chat") {
|
|
103923
105837
|
const chatFactory = realMakeProvider(() => {});
|
|
103924
105838
|
let chatResumeId = flags.resumeId;
|
|
105839
|
+
const chatNotes = [];
|
|
103925
105840
|
if (flags.resumePick === true && chatResumeId === undefined) {
|
|
103926
|
-
chatResumeId =
|
|
105841
|
+
chatResumeId = bareResumeTarget(cwd, (text) => {
|
|
105842
|
+
chatNotes.push(text);
|
|
105843
|
+
});
|
|
103927
105844
|
}
|
|
103928
105845
|
if (await (runtime.launchChat ?? launchTuiChatShell)({
|
|
103929
105846
|
detected: tuiDetected,
|
|
103930
105847
|
redetect,
|
|
103931
105848
|
...tuiInitial !== undefined ? { initial: tuiInitial } : {},
|
|
103932
|
-
runShell,
|
|
105849
|
+
runShell: async (chatIo, chatDeps) => {
|
|
105850
|
+
const code = await runLeasedChatShell(chatIo, chatDeps, runShell, chatNotes.splice(0));
|
|
105851
|
+
if (code !== undefined) {
|
|
105852
|
+
process.exitCode = code;
|
|
105853
|
+
}
|
|
105854
|
+
},
|
|
103933
105855
|
makeShellDeps: (sel) => {
|
|
103934
105856
|
const resolvedModelParams = resolveProviderModelParamsByName(sel.provider, loadShellConfig(runtime.cacheDir), runtime.cacheDir);
|
|
103935
105857
|
return {
|
|
103936
105858
|
makeProvider: chatFactory,
|
|
103937
105859
|
clock: () => new Date().toISOString(),
|
|
103938
|
-
idSeq: () =>
|
|
105860
|
+
idSeq: () => randomUUID30(),
|
|
103939
105861
|
initial: Object.keys(resolvedModelParams).length > 0 ? { ...sel, modelParams: resolvedModelParams } : sel,
|
|
103940
105862
|
session: {
|
|
103941
105863
|
cwd,
|
|
103942
105864
|
...flags.continueLast === true ? { continueLast: true } : {},
|
|
103943
|
-
...chatResumeId !== undefined ? { resumeId: chatResumeId } : {}
|
|
105865
|
+
...chatResumeId !== undefined ? { resumeId: chatResumeId } : {},
|
|
105866
|
+
...leaseFlagOpts,
|
|
105867
|
+
leaseBox: chatLeaseBox
|
|
103944
105868
|
}
|
|
103945
105869
|
};
|
|
103946
105870
|
},
|
|
@@ -103960,7 +105884,8 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
103960
105884
|
cwd,
|
|
103961
105885
|
...flags.continueLast === true ? { continueLast: true } : {},
|
|
103962
105886
|
...flags.resumeId !== undefined ? { resumeId: flags.resumeId } : {},
|
|
103963
|
-
...flags.resumePick === true ? { pickOnStart: true } : {}
|
|
105887
|
+
...flags.resumePick === true ? { pickOnStart: true } : {},
|
|
105888
|
+
...leaseFlagOpts
|
|
103964
105889
|
},
|
|
103965
105890
|
...flags.permissionModeFlag !== undefined ? { initialPermissionMode: flags.permissionModeFlag } : {},
|
|
103966
105891
|
versionCheck
|
|
@@ -103968,6 +105893,8 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
103968
105893
|
return;
|
|
103969
105894
|
}
|
|
103970
105895
|
} finally {
|
|
105896
|
+
releaseSessionLease(chatLeaseBox.current);
|
|
105897
|
+
chatLeaseBox.current = undefined;
|
|
103971
105898
|
if (mcpRuntime !== undefined) {
|
|
103972
105899
|
await mcpRuntime.close();
|
|
103973
105900
|
reportMcpProblems(mcpRuntime);
|
|
@@ -103976,7 +105903,10 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
103976
105903
|
}
|
|
103977
105904
|
const rl = readline2.createInterface({ input: process.stdin });
|
|
103978
105905
|
let readlineMcp;
|
|
105906
|
+
const leaseBox = { current: undefined };
|
|
103979
105907
|
const closeAndExit = (code) => () => {
|
|
105908
|
+
releaseSessionLease(leaseBox.current);
|
|
105909
|
+
leaseBox.current = undefined;
|
|
103980
105910
|
(async () => {
|
|
103981
105911
|
try {
|
|
103982
105912
|
await readlineMcp?.close();
|
|
@@ -103995,6 +105925,27 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
103995
105925
|
}
|
|
103996
105926
|
};
|
|
103997
105927
|
const { io, emitSystem, printHeader, printPrompt, destroy } = createRichIo(sharedLines, versionCheck);
|
|
105928
|
+
const leaseChoiceRuntime = {
|
|
105929
|
+
isTty: process.stdin.isTTY === true && oneShotPrompt === undefined,
|
|
105930
|
+
io: {
|
|
105931
|
+
write: (text) => {
|
|
105932
|
+
io.write(text);
|
|
105933
|
+
},
|
|
105934
|
+
readLine: async () => {
|
|
105935
|
+
const next = await lineIterator.next();
|
|
105936
|
+
return next.done === true ? undefined : next.value;
|
|
105937
|
+
}
|
|
105938
|
+
},
|
|
105939
|
+
writeError: (text) => {
|
|
105940
|
+
process.stderr.write(text);
|
|
105941
|
+
}
|
|
105942
|
+
};
|
|
105943
|
+
const bareResumeId = () => bareResumeTarget(process.cwd(), emitSystem);
|
|
105944
|
+
const finishLeased = (code) => {
|
|
105945
|
+
if (code !== undefined) {
|
|
105946
|
+
process.exitCode = code;
|
|
105947
|
+
}
|
|
105948
|
+
};
|
|
103998
105949
|
let provider;
|
|
103999
105950
|
let model;
|
|
104000
105951
|
try {
|
|
@@ -104034,7 +105985,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104034
105985
|
const deps = {
|
|
104035
105986
|
makeProvider: baseFactory,
|
|
104036
105987
|
clock: () => new Date().toISOString(),
|
|
104037
|
-
idSeq: () =>
|
|
105988
|
+
idSeq: () => randomUUID30(),
|
|
104038
105989
|
initial: {
|
|
104039
105990
|
provider,
|
|
104040
105991
|
model,
|
|
@@ -104106,7 +106057,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104106
106057
|
sessionOverride: reasoningSessionOverride,
|
|
104107
106058
|
globalEffort: loadShellConfig(runtime.cacheDir).reasoningEffort
|
|
104108
106059
|
}),
|
|
104109
|
-
idSeq: () =>
|
|
106060
|
+
idSeq: () => randomUUID30(),
|
|
104110
106061
|
askUser: invokeAskUserHost,
|
|
104111
106062
|
sweepBackgroundJobs: () => jobRegistry.sweepAll(),
|
|
104112
106063
|
...resetSubagentBudget !== undefined ? { resetSubagentBudget } : {},
|
|
@@ -104122,34 +106073,38 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104122
106073
|
};
|
|
104123
106074
|
let resumeId = flags.resumeId;
|
|
104124
106075
|
if (flags.resumePick === true && resumeId === undefined) {
|
|
104125
|
-
resumeId =
|
|
106076
|
+
resumeId = bareResumeId();
|
|
104126
106077
|
}
|
|
104127
106078
|
const events = flags.eventsFile === undefined ? undefined : createFileEventSink(flags.eventsFile, (message) => {
|
|
104128
106079
|
emitSystem(`${message}
|
|
104129
106080
|
`);
|
|
104130
106081
|
}, flags.eventsMaxField);
|
|
106082
|
+
const runRepl = async (session) => {
|
|
106083
|
+
await runAgentRepl(sharedLines, { printPrompt, safeBoundary: io.onSafeBoundary }, agentDeps, metaprojectPort, session, flags.permissionModeFlag, slateSessionBox, events, runtime.cacheDir);
|
|
106084
|
+
};
|
|
104131
106085
|
try {
|
|
104132
|
-
await
|
|
106086
|
+
finishLeased(await runWithLeaseChoice({
|
|
104133
106087
|
cwd: process.cwd(),
|
|
104134
106088
|
...flags.continueLast === true ? { continueLast: true } : {},
|
|
104135
|
-
...resumeId !== undefined ? { resumeId } : {}
|
|
104136
|
-
|
|
106089
|
+
...resumeId !== undefined ? { resumeId } : {},
|
|
106090
|
+
...leaseFlagOpts,
|
|
106091
|
+
leaseBox
|
|
106092
|
+
}, runRepl, leaseChoiceRuntime));
|
|
104137
106093
|
} finally {
|
|
104138
106094
|
await mcpRuntime.close();
|
|
104139
106095
|
}
|
|
104140
106096
|
} else {
|
|
104141
106097
|
let resumeId = flags.resumeId;
|
|
104142
106098
|
if (flags.resumePick === true && resumeId === undefined) {
|
|
104143
|
-
resumeId =
|
|
106099
|
+
resumeId = bareResumeId();
|
|
104144
106100
|
}
|
|
104145
|
-
await
|
|
104146
|
-
|
|
104147
|
-
|
|
104148
|
-
|
|
104149
|
-
|
|
104150
|
-
|
|
104151
|
-
|
|
104152
|
-
});
|
|
106101
|
+
finishLeased(await runWithLeaseChoice({
|
|
106102
|
+
cwd: process.cwd(),
|
|
106103
|
+
...flags.continueLast === true ? { continueLast: true } : {},
|
|
106104
|
+
...resumeId !== undefined ? { resumeId } : {},
|
|
106105
|
+
...leaseFlagOpts,
|
|
106106
|
+
leaseBox
|
|
106107
|
+
}, (session) => runShell(io, { ...deps, session }), leaseChoiceRuntime));
|
|
104153
106108
|
}
|
|
104154
106109
|
} finally {
|
|
104155
106110
|
try {
|
|
@@ -104170,12 +106125,13 @@ async function sessionsCommand(args) {
|
|
|
104170
106125
|
const cwd = process.cwd();
|
|
104171
106126
|
if (sub === "list") {
|
|
104172
106127
|
const asJson = args.includes("--json");
|
|
104173
|
-
const
|
|
106128
|
+
const listed = listSessions(cwd).map((s) => ({ summary: s, lease: leaseColumn(cwd, s.id) }));
|
|
104174
106129
|
if (asJson) {
|
|
106130
|
+
const rows = listed.map(({ summary, lease }) => ({ ...summary, live: lease === "?" ? null : lease }));
|
|
104175
106131
|
console.log(JSON.stringify({ schemaVersion: 1, project: resolveProjectRoot2(cwd), sessions: rows }, null, 2));
|
|
104176
106132
|
return;
|
|
104177
106133
|
}
|
|
104178
|
-
if (
|
|
106134
|
+
if (listed.length === 0) {
|
|
104179
106135
|
console.log(`No sessions for project ${resolveProjectRoot2(cwd)}`);
|
|
104180
106136
|
console.log(`(store: ${projectSessionsDir(cwd)})`);
|
|
104181
106137
|
return;
|
|
@@ -104183,10 +106139,10 @@ async function sessionsCommand(args) {
|
|
|
104183
106139
|
console.log(`Project: ${resolveProjectRoot2(cwd)}`);
|
|
104184
106140
|
console.log(`Store: ${projectSessionsDir(cwd)}`);
|
|
104185
106141
|
console.log("");
|
|
104186
|
-
console.log(pad("ID", 10) + pad("UPDATED", 22) + pad("MSGS", 6) + pad("MODEL", 24) + "TITLE");
|
|
104187
|
-
for (const s of
|
|
106142
|
+
console.log(pad("ID", 10) + pad("UPDATED", 22) + pad("MSGS", 6) + pad("LIVE", 7) + pad("MODEL", 24) + "TITLE");
|
|
106143
|
+
for (const { summary: s, lease } of listed) {
|
|
104188
106144
|
const model = s.provider !== undefined && s.model !== undefined ? `${s.provider}/${s.model}` : s.model ?? "-";
|
|
104189
|
-
console.log(pad(shortSessionId(s.id), 10) + pad(s.updatedAt.slice(0, 19).replace("T", " "), 22) + pad(String(s.messageCount), 6) + pad(clip8(model, 22), 24) + (s.parentSessionId !== undefined ? `\u21B3 ${s.title}` : s.title));
|
|
106145
|
+
console.log(pad(shortSessionId(s.id), 10) + pad(s.updatedAt.slice(0, 19).replace("T", " "), 22) + pad(String(s.messageCount), 6) + pad(lease ?? "", 7) + pad(clip8(model, 22), 24) + (s.parentSessionId !== undefined ? `\u21B3 ${s.title}` : s.title));
|
|
104190
106146
|
}
|
|
104191
106147
|
console.log("");
|
|
104192
106148
|
console.log("Resume: keryx shell -r <id> Continue last: keryx shell -c");
|
|
@@ -104273,6 +106229,19 @@ async function sessionsCommand(args) {
|
|
|
104273
106229
|
printHelp17();
|
|
104274
106230
|
process.exitCode = 1;
|
|
104275
106231
|
}
|
|
106232
|
+
function leaseColumn(cwd, sessionId) {
|
|
106233
|
+
let state;
|
|
106234
|
+
try {
|
|
106235
|
+
state = sessionLeaseState(cwd, sessionId).state;
|
|
106236
|
+
} catch {
|
|
106237
|
+
return "?";
|
|
106238
|
+
}
|
|
106239
|
+
if (state === "live" || state === "mine")
|
|
106240
|
+
return "live";
|
|
106241
|
+
if (state === "stale")
|
|
106242
|
+
return "stale";
|
|
106243
|
+
return null;
|
|
106244
|
+
}
|
|
104276
106245
|
function pad(s, n) {
|
|
104277
106246
|
return s.length >= n ? `${s.slice(0, n - 1)} ` : s + " ".repeat(n - s.length);
|
|
104278
106247
|
}
|
|
@@ -104290,9 +106259,16 @@ Usage:
|
|
|
104290
106259
|
keryx sessions export <id> Export transcript as Markdown
|
|
104291
106260
|
keryx sessions path Print the on-disk sessions directory
|
|
104292
106261
|
|
|
106262
|
+
List columns: ID, UPDATED, MSGS, LIVE, MODEL, TITLE. LIVE is "live" when a
|
|
106263
|
+
shell has the session open, "stale" when its holder stopped heartbeating, and
|
|
106264
|
+
blank when no shell holds it. In --json every row carries "live": "live" |
|
|
106265
|
+
"stale" | null.
|
|
106266
|
+
|
|
104293
106267
|
Shell:
|
|
104294
|
-
keryx shell -c Continue last session
|
|
106268
|
+
keryx shell -c Continue the last session no other shell has open
|
|
104295
106269
|
keryx shell -r [id] Resume session (id / short id / title)
|
|
106270
|
+
keryx shell -r <id> --fork Fork a session another shell has open
|
|
106271
|
+
keryx shell -r <id> --take-over Take over a session whose holder is stale
|
|
104296
106272
|
`);
|
|
104297
106273
|
}
|
|
104298
106274
|
|
|
@@ -104300,7 +106276,7 @@ Shell:
|
|
|
104300
106276
|
init_fs();
|
|
104301
106277
|
import { readFile as readFile99 } from "fs/promises";
|
|
104302
106278
|
import { stdin } from "process";
|
|
104303
|
-
import
|
|
106279
|
+
import path197 from "path";
|
|
104304
106280
|
var MODULES = [
|
|
104305
106281
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
104306
106282
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -104346,8 +106322,8 @@ async function modulesCommand(args = []) {
|
|
|
104346
106322
|
return;
|
|
104347
106323
|
}
|
|
104348
106324
|
const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
104349
|
-
const metaprojectRoot =
|
|
104350
|
-
const manifestPath =
|
|
106325
|
+
const metaprojectRoot = path197.join(process.cwd(), ".metaproject");
|
|
106326
|
+
const manifestPath = path197.join(metaprojectRoot, "metaproject.json");
|
|
104351
106327
|
if (!await pathExists(manifestPath)) {
|
|
104352
106328
|
if (wantsJson) {
|
|
104353
106329
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -104461,26 +106437,26 @@ function printHelp18() {
|
|
|
104461
106437
|
}
|
|
104462
106438
|
|
|
104463
106439
|
// src/commands/serve.ts
|
|
104464
|
-
import { randomUUID as
|
|
106440
|
+
import { randomUUID as randomUUID33 } from "crypto";
|
|
104465
106441
|
init_serve_config();
|
|
104466
106442
|
|
|
104467
106443
|
// src/lib/serve-credential.ts
|
|
104468
106444
|
init_config_dir();
|
|
104469
|
-
import { createHash as createHash44, randomBytes as randomBytes5, randomUUID as
|
|
106445
|
+
import { createHash as createHash44, randomBytes as randomBytes5, randomUUID as randomUUID31 } from "crypto";
|
|
104470
106446
|
import {
|
|
104471
|
-
chmodSync as
|
|
104472
|
-
closeSync as
|
|
106447
|
+
chmodSync as chmodSync5,
|
|
106448
|
+
closeSync as closeSync4,
|
|
104473
106449
|
existsSync as existsSync34,
|
|
104474
106450
|
fsyncSync as fsyncSync2,
|
|
104475
|
-
openSync as
|
|
104476
|
-
renameSync as
|
|
104477
|
-
statSync as
|
|
106451
|
+
openSync as openSync5,
|
|
106452
|
+
renameSync as renameSync5,
|
|
106453
|
+
statSync as statSync8,
|
|
104478
106454
|
unlinkSync as unlinkSync3,
|
|
104479
|
-
writeFileSync as
|
|
106455
|
+
writeFileSync as writeFileSync10
|
|
104480
106456
|
} from "fs";
|
|
104481
|
-
import
|
|
106457
|
+
import path198 from "path";
|
|
104482
106458
|
function serveCredentialPath(dir) {
|
|
104483
|
-
return
|
|
106459
|
+
return path198.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
104484
106460
|
}
|
|
104485
106461
|
function constantTimeEqual(a, b) {
|
|
104486
106462
|
const width = Math.max(a.length, b.length);
|
|
@@ -104509,7 +106485,7 @@ function isGroupOrOtherAccessible(file) {
|
|
|
104509
106485
|
if (process.platform === "win32") {
|
|
104510
106486
|
return false;
|
|
104511
106487
|
}
|
|
104512
|
-
const mode =
|
|
106488
|
+
const mode = statSync8(file, { throwIfNoEntry: false })?.mode;
|
|
104513
106489
|
return mode !== undefined && (mode & 63) !== 0;
|
|
104514
106490
|
}
|
|
104515
106491
|
function readServeCredential(dir) {
|
|
@@ -104550,18 +106526,18 @@ function readServeCredential(dir) {
|
|
|
104550
106526
|
}
|
|
104551
106527
|
function writeStore(store, dir) {
|
|
104552
106528
|
const file = serveCredentialPath(dir);
|
|
104553
|
-
const temp = `${file}.${
|
|
106529
|
+
const temp = `${file}.${randomUUID31()}.tmp`;
|
|
104554
106530
|
try {
|
|
104555
106531
|
ensureKeryxConfigDir(dir);
|
|
104556
|
-
const handle =
|
|
106532
|
+
const handle = openSync5(temp, "wx", 384);
|
|
104557
106533
|
try {
|
|
104558
|
-
|
|
106534
|
+
writeFileSync10(handle, `${JSON.stringify(store, null, 2)}
|
|
104559
106535
|
`, { encoding: "utf8" });
|
|
104560
106536
|
fsyncSync2(handle);
|
|
104561
106537
|
} finally {
|
|
104562
|
-
|
|
106538
|
+
closeSync4(handle);
|
|
104563
106539
|
}
|
|
104564
|
-
|
|
106540
|
+
renameSync5(temp, file);
|
|
104565
106541
|
tighten2(file, 384);
|
|
104566
106542
|
return true;
|
|
104567
106543
|
} catch {
|
|
@@ -104576,7 +106552,7 @@ function tighten2(target, mode) {
|
|
|
104576
106552
|
return;
|
|
104577
106553
|
}
|
|
104578
106554
|
try {
|
|
104579
|
-
|
|
106555
|
+
chmodSync5(target, mode);
|
|
104580
106556
|
} catch {}
|
|
104581
106557
|
}
|
|
104582
106558
|
function withServeCredentialLock(dir, fn, onWaiting) {
|
|
@@ -104590,7 +106566,7 @@ function mintRecord(now) {
|
|
|
104590
106566
|
const salt = randomBytes5(32).toString("hex");
|
|
104591
106567
|
return {
|
|
104592
106568
|
token,
|
|
104593
|
-
record: { id:
|
|
106569
|
+
record: { id: randomUUID31(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
104594
106570
|
};
|
|
104595
106571
|
}
|
|
104596
106572
|
function issueServeToken(dir, now = () => new Date().toISOString(), onWaiting) {
|
|
@@ -104713,23 +106689,23 @@ class AuthFailureThrottle {
|
|
|
104713
106689
|
// src/lib/serve-turn-store.ts
|
|
104714
106690
|
init_config_dir();
|
|
104715
106691
|
import { createHash as createHash45 } from "crypto";
|
|
104716
|
-
import { existsSync as existsSync35, readdirSync as
|
|
104717
|
-
import
|
|
106692
|
+
import { existsSync as existsSync35, readdirSync as readdirSync4, rmSync as rmSync3 } from "fs";
|
|
106693
|
+
import path199 from "path";
|
|
104718
106694
|
var MAX_TURN_EVENTS = 1e4;
|
|
104719
106695
|
function turnsRoot(dir) {
|
|
104720
|
-
return
|
|
106696
|
+
return path199.join(keryxConfigDir(dir), "turns");
|
|
104721
106697
|
}
|
|
104722
106698
|
function turnDir(turnId, dir) {
|
|
104723
|
-
return
|
|
106699
|
+
return path199.join(turnsRoot(dir), turnId);
|
|
104724
106700
|
}
|
|
104725
106701
|
function keyPath(project, idempotencyKey, dir) {
|
|
104726
106702
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
104727
106703
|
const digest = createHash45("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
104728
|
-
return
|
|
106704
|
+
return path199.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
104729
106705
|
}
|
|
104730
106706
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
104731
106707
|
const digest = createHash45("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
104732
|
-
return
|
|
106708
|
+
return path199.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
104733
106709
|
}
|
|
104734
106710
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
104735
106711
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -104750,7 +106726,7 @@ function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
|
104750
106726
|
} catch {}
|
|
104751
106727
|
const record = turnId === undefined ? undefined : readTurnRecord(turnId, dir);
|
|
104752
106728
|
if (turnId === undefined || record === undefined || !record.ok) {
|
|
104753
|
-
|
|
106729
|
+
rmSync3(legacy, { force: true });
|
|
104754
106730
|
return null;
|
|
104755
106731
|
}
|
|
104756
106732
|
if (record.value.project !== project) {
|
|
@@ -104758,7 +106734,7 @@ function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
|
104758
106734
|
}
|
|
104759
106735
|
writeOwnerOnlyFile(scoped, `${JSON.stringify({ turnId }, null, 2)}
|
|
104760
106736
|
`);
|
|
104761
|
-
|
|
106737
|
+
rmSync3(legacy, { force: true });
|
|
104762
106738
|
return turnId;
|
|
104763
106739
|
}
|
|
104764
106740
|
function isServerFault(reason) {
|
|
@@ -104793,7 +106769,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
104793
106769
|
}
|
|
104794
106770
|
function createTurnRecord(record, dir) {
|
|
104795
106771
|
ensureTurnDir(record.turnId, dir);
|
|
104796
|
-
writeOwnerOnlyFile(
|
|
106772
|
+
writeOwnerOnlyFile(path199.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
104797
106773
|
`);
|
|
104798
106774
|
}
|
|
104799
106775
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -104802,12 +106778,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
104802
106778
|
}
|
|
104803
106779
|
const line = JSON.stringify(event);
|
|
104804
106780
|
try {
|
|
104805
|
-
appendOwnerOnlyLine(
|
|
106781
|
+
appendOwnerOnlyLine(path199.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
104806
106782
|
} catch (error) {
|
|
104807
106783
|
if (error?.code !== "ENOENT") {
|
|
104808
106784
|
throw error;
|
|
104809
106785
|
}
|
|
104810
|
-
appendOwnerOnlyLine(
|
|
106786
|
+
appendOwnerOnlyLine(path199.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
104811
106787
|
}
|
|
104812
106788
|
return true;
|
|
104813
106789
|
}
|
|
@@ -104815,7 +106791,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
104815
106791
|
if (!isTurnId(turnId)) {
|
|
104816
106792
|
return { ok: false, reason: "not-a-turn-id" };
|
|
104817
106793
|
}
|
|
104818
|
-
const read = readTurnFile(
|
|
106794
|
+
const read = readTurnFile(path199.join(turnDir(turnId, dir), "events.jsonl"));
|
|
104819
106795
|
if (!read.ok) {
|
|
104820
106796
|
if (isDefiniteAbsence2(read.reason)) {
|
|
104821
106797
|
return { ok: true, value: [] };
|
|
@@ -104843,7 +106819,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
104843
106819
|
if (!isTurnId(turnId)) {
|
|
104844
106820
|
return { ok: false, reason: "not-a-turn-id" };
|
|
104845
106821
|
}
|
|
104846
|
-
const read = readTurnFile(
|
|
106822
|
+
const read = readTurnFile(path199.join(turnDir(turnId, dir), "turn.json"));
|
|
104847
106823
|
if (!read.ok) {
|
|
104848
106824
|
return { ok: false, reason: read.reason };
|
|
104849
106825
|
}
|
|
@@ -104862,7 +106838,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
104862
106838
|
if (!record.ok) {
|
|
104863
106839
|
return false;
|
|
104864
106840
|
}
|
|
104865
|
-
writeOwnerOnlyFile(
|
|
106841
|
+
writeOwnerOnlyFile(path199.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
104866
106842
|
`);
|
|
104867
106843
|
return true;
|
|
104868
106844
|
}
|
|
@@ -104902,13 +106878,13 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
104902
106878
|
} catch {
|
|
104903
106879
|
return false;
|
|
104904
106880
|
}
|
|
104905
|
-
|
|
106881
|
+
rmSync3(file, { force: true });
|
|
104906
106882
|
return true;
|
|
104907
106883
|
}
|
|
104908
106884
|
|
|
104909
106885
|
// src/lib/serve-turn.ts
|
|
104910
|
-
import { randomUUID as
|
|
104911
|
-
import
|
|
106886
|
+
import { randomUUID as randomUUID32 } from "crypto";
|
|
106887
|
+
import path200 from "path";
|
|
104912
106888
|
init_service();
|
|
104913
106889
|
var REMOTE_ORIGIN = "remote:http";
|
|
104914
106890
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -104977,9 +106953,9 @@ function isUuid(value) {
|
|
|
104977
106953
|
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value);
|
|
104978
106954
|
}
|
|
104979
106955
|
function resolveProject(declared, dir) {
|
|
104980
|
-
const wanted =
|
|
106956
|
+
const wanted = path200.resolve(declared);
|
|
104981
106957
|
for (const entry of listProjects(dir, () => {})) {
|
|
104982
|
-
if (
|
|
106958
|
+
if (path200.resolve(entry.path) === wanted) {
|
|
104983
106959
|
return { ok: true, project: entry.path };
|
|
104984
106960
|
}
|
|
104985
106961
|
}
|
|
@@ -105026,7 +107002,7 @@ async function redactOut(security, text) {
|
|
|
105026
107002
|
async function runRemoteTurn(input) {
|
|
105027
107003
|
const scanRoot = input.scanRoot;
|
|
105028
107004
|
const security = createSecurityService(scanRoot);
|
|
105029
|
-
const newId = input.newId ?? (() =>
|
|
107005
|
+
const newId = input.newId ?? (() => randomUUID32());
|
|
105030
107006
|
const clock = input.clock ?? (() => new Date().toISOString());
|
|
105031
107007
|
const turnId = input.turnId ?? newId();
|
|
105032
107008
|
const sessionId = input.request.sessionId ?? newId();
|
|
@@ -105169,7 +107145,7 @@ function outcomeOf(status, gate, unresolvedBlockerIds) {
|
|
|
105169
107145
|
}
|
|
105170
107146
|
function createSubmitTurn(deps) {
|
|
105171
107147
|
return async (request, project) => {
|
|
105172
|
-
const turnId = (deps.newId ?? (() =>
|
|
107148
|
+
const turnId = (deps.newId ?? (() => randomUUID32()))();
|
|
105173
107149
|
const scanned = await scanPrompt(deps.dir, request.prompt);
|
|
105174
107150
|
if (scanned.rejected) {
|
|
105175
107151
|
return { kind: "rejected" };
|
|
@@ -105857,7 +107833,7 @@ function runConfig(args) {
|
|
|
105857
107833
|
return;
|
|
105858
107834
|
}
|
|
105859
107835
|
const credential = readServeCredential();
|
|
105860
|
-
const credentialId = credential.status === "ok" ? credential.record.id :
|
|
107836
|
+
const credentialId = credential.status === "ok" ? credential.record.id : randomUUID33();
|
|
105861
107837
|
const config = defaultServeConfig(credentialId, {
|
|
105862
107838
|
address: parsed.parsed.values.get("--bind") ?? DEFAULT_SERVE_BIND_ADDRESS,
|
|
105863
107839
|
port: port ?? DEFAULT_SERVE_PORT,
|
|
@@ -106012,10 +107988,10 @@ function printHelp19() {
|
|
|
106012
107988
|
}
|
|
106013
107989
|
|
|
106014
107990
|
// src/commands/update.ts
|
|
106015
|
-
import { spawn as
|
|
107991
|
+
import { spawn as spawn7 } from "child_process";
|
|
106016
107992
|
import { chmod as chmod4, mkdir as mkdir63, readFile as readFile100, readdir as readdir37, writeFile as writeFile54 } from "fs/promises";
|
|
106017
|
-
import { access as access4, constants as
|
|
106018
|
-
import
|
|
107993
|
+
import { access as access4, constants as constants3, existsSync as existsSync36 } from "fs";
|
|
107994
|
+
import path201 from "path";
|
|
106019
107995
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
106020
107996
|
init_config2();
|
|
106021
107997
|
init_config();
|
|
@@ -106032,8 +108008,8 @@ async function updateCommand(args = []) {
|
|
|
106032
108008
|
return;
|
|
106033
108009
|
}
|
|
106034
108010
|
const projectRoot = process.cwd();
|
|
106035
|
-
const metaprojectRoot =
|
|
106036
|
-
banner("keryx update", `Refreshing the .metaproject workspace in ${
|
|
108011
|
+
const metaprojectRoot = path201.join(projectRoot, ".metaproject");
|
|
108012
|
+
banner("keryx update", `Refreshing the .metaproject workspace in ${path201.basename(projectRoot)}/`);
|
|
106037
108013
|
if (!await pathExists(metaprojectRoot)) {
|
|
106038
108014
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
106039
108015
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -106106,7 +108082,7 @@ async function updateCommand(args = []) {
|
|
|
106106
108082
|
nextSteps(steps);
|
|
106107
108083
|
}
|
|
106108
108084
|
async function previewServiceFiles(projectRoot, options) {
|
|
106109
|
-
const metaprojectRoot =
|
|
108085
|
+
const metaprojectRoot = path201.join(projectRoot, ".metaproject");
|
|
106110
108086
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
106111
108087
|
const manifest = manifestState.manifest;
|
|
106112
108088
|
const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
|
|
@@ -106135,12 +108111,12 @@ async function previewServiceFiles(projectRoot, options) {
|
|
|
106135
108111
|
});
|
|
106136
108112
|
}
|
|
106137
108113
|
async function refreshServiceFiles(projectRoot, options) {
|
|
106138
|
-
const metaprojectRoot =
|
|
108114
|
+
const metaprojectRoot = path201.join(projectRoot, ".metaproject");
|
|
106139
108115
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
106140
108116
|
const manifest = manifestState.manifest;
|
|
106141
108117
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
106142
108118
|
if (manifestState.migrated) {
|
|
106143
|
-
await writeFile54(
|
|
108119
|
+
await writeFile54(path201.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
106144
108120
|
`, "utf8");
|
|
106145
108121
|
}
|
|
106146
108122
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -106177,9 +108153,9 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106177
108153
|
enableSecurity,
|
|
106178
108154
|
enableSac
|
|
106179
108155
|
});
|
|
106180
|
-
await writeTextIfChanged4(
|
|
106181
|
-
await writeTextIfChanged4(
|
|
106182
|
-
await writeTextIfChanged4(
|
|
108156
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
|
|
108157
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
|
|
108158
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
|
|
106183
108159
|
await writeRoutingEntrypointPair(metaprojectRoot, {
|
|
106184
108160
|
enableGdgraph,
|
|
106185
108161
|
enableGdctx,
|
|
@@ -106202,7 +108178,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106202
108178
|
}
|
|
106203
108179
|
}
|
|
106204
108180
|
});
|
|
106205
|
-
await writeTextIfChanged4(
|
|
108181
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
106206
108182
|
enableGdgraph,
|
|
106207
108183
|
enableGdctx,
|
|
106208
108184
|
enableGdwiki,
|
|
@@ -106214,7 +108190,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106214
108190
|
enableSecurity,
|
|
106215
108191
|
data: dashboardData
|
|
106216
108192
|
}));
|
|
106217
|
-
await writeTextIfMissing4(
|
|
108193
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
106218
108194
|
enableGdgraph,
|
|
106219
108195
|
enableGdctx,
|
|
106220
108196
|
enableGdwiki,
|
|
@@ -106227,31 +108203,31 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106227
108203
|
}));
|
|
106228
108204
|
if (enableGdgraph) {
|
|
106229
108205
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
106230
|
-
await writeTextIfChanged4(
|
|
106231
|
-
await writeTextIfChanged4(
|
|
106232
|
-
await writeTextIfChanged4(
|
|
108206
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
|
|
108207
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
|
|
108208
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
|
|
106233
108209
|
await seedAssetsLock(metaprojectRoot);
|
|
106234
108210
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
106235
108211
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
106236
108212
|
}
|
|
106237
108213
|
}
|
|
106238
108214
|
if (enableGdctx) {
|
|
106239
|
-
await writeTextIfMissing4(
|
|
106240
|
-
await writeTextIfChanged4(
|
|
106241
|
-
await writeTextIfChanged4(
|
|
106242
|
-
await writeTextIfChanged4(
|
|
108215
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
|
|
108216
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
|
|
108217
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
|
|
108218
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
|
|
106243
108219
|
}
|
|
106244
108220
|
if (enableGdwiki) {
|
|
106245
|
-
await writeTextIfMissing4(
|
|
106246
|
-
await writeTextIfChanged4(
|
|
106247
|
-
await writeTextIfChanged4(
|
|
108221
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
|
|
108222
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
|
|
108223
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
|
|
106248
108224
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
106249
108225
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
106250
108226
|
}
|
|
106251
108227
|
}
|
|
106252
108228
|
if (enableSac) {
|
|
106253
|
-
await writeTextIfMissing4(
|
|
106254
|
-
await writeTextIfChanged4(
|
|
108229
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
108230
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
106255
108231
|
}
|
|
106256
108232
|
let gdskillsWarnings = [];
|
|
106257
108233
|
let gdskillsNotices = [];
|
|
@@ -106264,25 +108240,25 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106264
108240
|
}
|
|
106265
108241
|
}
|
|
106266
108242
|
if (enableHealth) {
|
|
106267
|
-
await writeTextIfMissing4(
|
|
106268
|
-
await writeTextIfChanged4(
|
|
106269
|
-
await writeTextIfChanged4(
|
|
106270
|
-
await writeTextIfChanged4(
|
|
108243
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
|
|
108244
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
|
|
108245
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
|
|
108246
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
|
|
106271
108247
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
106272
108248
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
106273
108249
|
}
|
|
106274
108250
|
}
|
|
106275
108251
|
if (enableTesting) {
|
|
106276
|
-
await writeTextIfMissing4(
|
|
108252
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
106277
108253
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
106278
108254
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
106279
108255
|
}));
|
|
106280
|
-
await writeTextIfChanged4(
|
|
106281
|
-
await writeTextIfChanged4(
|
|
106282
|
-
await writeTextIfChanged4(
|
|
108256
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
|
|
108257
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
|
|
108258
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
|
|
106283
108259
|
if (enableGdwiki) {
|
|
106284
|
-
await writeTextIfMissing4(
|
|
106285
|
-
await writeTextIfMissing4(
|
|
108260
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
108261
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
106286
108262
|
}
|
|
106287
108263
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
106288
108264
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -106295,24 +108271,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106295
108271
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
106296
108272
|
}
|
|
106297
108273
|
if (enableMemory) {
|
|
106298
|
-
await writeTextIfMissing4(
|
|
106299
|
-
await writeTextIfMissing4(
|
|
106300
|
-
await writeTextIfChanged4(
|
|
106301
|
-
await writeTextIfChanged4(
|
|
106302
|
-
await writeTextIfChanged4(
|
|
108274
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
|
|
108275
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
|
|
108276
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
|
|
108277
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
|
|
108278
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
|
|
106303
108279
|
}
|
|
106304
108280
|
if (enableTasks) {
|
|
106305
|
-
await writeTextIfChanged4(
|
|
106306
|
-
await writeTextIfChanged4(
|
|
106307
|
-
await writeTextIfChanged4(
|
|
106308
|
-
await writeTextIfChanged4(
|
|
106309
|
-
await writeTextIfChanged4(
|
|
106310
|
-
await writeTextIfChanged4(
|
|
108281
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
|
|
108282
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
|
|
108283
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
|
|
108284
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
|
|
108285
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
|
|
108286
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
|
|
106311
108287
|
}
|
|
106312
108288
|
if (enableSecurity) {
|
|
106313
|
-
await writeTextIfMissing4(
|
|
106314
|
-
await writeTextIfChanged4(
|
|
106315
|
-
await writeTextIfChanged4(
|
|
108289
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
|
|
108290
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
|
|
108291
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
|
|
106316
108292
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
106317
108293
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
106318
108294
|
}
|
|
@@ -106365,13 +108341,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106365
108341
|
};
|
|
106366
108342
|
}
|
|
106367
108343
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
106368
|
-
const metaprojectRoot =
|
|
108344
|
+
const metaprojectRoot = path201.join(projectRoot, ".metaproject");
|
|
106369
108345
|
if (!await pathExists(metaprojectRoot)) {
|
|
106370
108346
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
106371
108347
|
}
|
|
106372
108348
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
106373
108349
|
const data = await collectDashboardData(metaprojectRoot);
|
|
106374
|
-
const dashboardPath =
|
|
108350
|
+
const dashboardPath = path201.join(metaprojectRoot, "keryx-dashboard.html");
|
|
106375
108351
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
106376
108352
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
106377
108353
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -106391,7 +108367,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
106391
108367
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
106392
108368
|
return true;
|
|
106393
108369
|
}
|
|
106394
|
-
const hookPath =
|
|
108370
|
+
const hookPath = path201.join(projectRoot, ".git", "hooks", "post-commit");
|
|
106395
108371
|
if (!await pathExists(hookPath)) {
|
|
106396
108372
|
return false;
|
|
106397
108373
|
}
|
|
@@ -106411,11 +108387,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
106411
108387
|
if (testing) {
|
|
106412
108388
|
data.testing = testing;
|
|
106413
108389
|
}
|
|
106414
|
-
const wiki = await collectMarkdownPages(
|
|
108390
|
+
const wiki = await collectMarkdownPages(path201.join(metaprojectRoot, "wiki"), "wiki");
|
|
106415
108391
|
if (wiki.length > 0) {
|
|
106416
108392
|
data.wiki = { pages: wiki };
|
|
106417
108393
|
}
|
|
106418
|
-
const memory = await collectMarkdownPages(
|
|
108394
|
+
const memory = await collectMarkdownPages(path201.join(metaprojectRoot, "memory"), "memory");
|
|
106419
108395
|
if (memory.length > 0) {
|
|
106420
108396
|
data.memory = { entries: memory };
|
|
106421
108397
|
}
|
|
@@ -106430,7 +108406,7 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
106430
108406
|
return data;
|
|
106431
108407
|
}
|
|
106432
108408
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
106433
|
-
const flowsRoot =
|
|
108409
|
+
const flowsRoot = path201.join(metaprojectRoot, "flows");
|
|
106434
108410
|
if (!await pathExists(flowsRoot)) {
|
|
106435
108411
|
return null;
|
|
106436
108412
|
}
|
|
@@ -106442,7 +108418,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
106442
108418
|
}
|
|
106443
108419
|
const flows = [];
|
|
106444
108420
|
for (const dir of dirEntries) {
|
|
106445
|
-
const flowPath =
|
|
108421
|
+
const flowPath = path201.join(flowsRoot, dir, "flow.json");
|
|
106446
108422
|
if (!await pathExists(flowPath)) {
|
|
106447
108423
|
continue;
|
|
106448
108424
|
}
|
|
@@ -106450,7 +108426,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
106450
108426
|
const flow = JSON.parse(await readFile100(flowPath, "utf8"));
|
|
106451
108427
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
106452
108428
|
let acTotal = 0;
|
|
106453
|
-
const acPath =
|
|
108429
|
+
const acPath = path201.join(flowsRoot, dir, "acceptance-criteria.md");
|
|
106454
108430
|
if (await pathExists(acPath)) {
|
|
106455
108431
|
const acContent = await readFile100(acPath, "utf8");
|
|
106456
108432
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -106504,7 +108480,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
106504
108480
|
"data/testing/context.md"
|
|
106505
108481
|
];
|
|
106506
108482
|
for (const href of staticHrefs) {
|
|
106507
|
-
const filePath =
|
|
108483
|
+
const filePath = path201.join(metaprojectRoot, ...href.split("/"));
|
|
106508
108484
|
if (!await pathExists(filePath)) {
|
|
106509
108485
|
continue;
|
|
106510
108486
|
}
|
|
@@ -106521,7 +108497,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
106521
108497
|
return docs;
|
|
106522
108498
|
}
|
|
106523
108499
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
106524
|
-
const reportPath =
|
|
108500
|
+
const reportPath = path201.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
106525
108501
|
if (!await pathExists(reportPath)) {
|
|
106526
108502
|
return;
|
|
106527
108503
|
}
|
|
@@ -106630,8 +108606,8 @@ function metricToScope(metric) {
|
|
|
106630
108606
|
};
|
|
106631
108607
|
}
|
|
106632
108608
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
106633
|
-
const nodesPath =
|
|
106634
|
-
const edgesPath =
|
|
108609
|
+
const nodesPath = path201.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
108610
|
+
const edgesPath = path201.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
106635
108611
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
106636
108612
|
return;
|
|
106637
108613
|
}
|
|
@@ -106682,8 +108658,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
106682
108658
|
};
|
|
106683
108659
|
}
|
|
106684
108660
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
106685
|
-
const reportPath =
|
|
106686
|
-
const contextPath =
|
|
108661
|
+
const reportPath = path201.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
108662
|
+
const contextPath = path201.join(metaprojectRoot, "data", "testing", "context.md");
|
|
106687
108663
|
if (await pathExists(reportPath)) {
|
|
106688
108664
|
const report = JSON.parse(await readFile100(reportPath, "utf8"));
|
|
106689
108665
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -106712,7 +108688,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
106712
108688
|
const files = await listMarkdownFiles(root);
|
|
106713
108689
|
const pages = [];
|
|
106714
108690
|
for (const filePath of files.slice(0, 40)) {
|
|
106715
|
-
const relativePath =
|
|
108691
|
+
const relativePath = path201.relative(root, filePath).split(path201.sep).join("/");
|
|
106716
108692
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
106717
108693
|
continue;
|
|
106718
108694
|
}
|
|
@@ -106733,7 +108709,7 @@ async function listMarkdownFiles(root) {
|
|
|
106733
108709
|
const entries = await readdir37(root, { withFileTypes: true });
|
|
106734
108710
|
const files = [];
|
|
106735
108711
|
for (const entry of entries) {
|
|
106736
|
-
const fullPath =
|
|
108712
|
+
const fullPath = path201.join(root, entry.name);
|
|
106737
108713
|
if (entry.isDirectory()) {
|
|
106738
108714
|
files.push(...await listMarkdownFiles(fullPath));
|
|
106739
108715
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -106781,7 +108757,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
106781
108757
|
const manifest = {
|
|
106782
108758
|
schemaVersion: 1,
|
|
106783
108759
|
standardVersion: STANDARD_VERSION,
|
|
106784
|
-
name: `${
|
|
108760
|
+
name: `${path201.basename(path201.dirname(metaprojectRoot))}-metaproject`,
|
|
106785
108761
|
createdBy: "keryx",
|
|
106786
108762
|
profiles: computeProfiles(enabledModuleKeys),
|
|
106787
108763
|
paths: {
|
|
@@ -106871,11 +108847,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
106871
108847
|
metaproject: ".metaproject/index.md"
|
|
106872
108848
|
}
|
|
106873
108849
|
};
|
|
106874
|
-
await writeFile54(
|
|
108850
|
+
await writeFile54(path201.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
106875
108851
|
`, "utf8");
|
|
106876
108852
|
}
|
|
106877
108853
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
106878
|
-
const manifestPath =
|
|
108854
|
+
const manifestPath = path201.join(metaprojectRoot, "metaproject.json");
|
|
106879
108855
|
if (!await pathExists(manifestPath)) {
|
|
106880
108856
|
return;
|
|
106881
108857
|
}
|
|
@@ -106898,7 +108874,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
106898
108874
|
`, "utf8");
|
|
106899
108875
|
}
|
|
106900
108876
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
106901
|
-
const manifestPath =
|
|
108877
|
+
const manifestPath = path201.join(metaprojectRoot, "metaproject.json");
|
|
106902
108878
|
if (!await pathExists(manifestPath)) {
|
|
106903
108879
|
return;
|
|
106904
108880
|
}
|
|
@@ -106934,72 +108910,72 @@ async function updateRuntime(projectRoot) {
|
|
|
106934
108910
|
}
|
|
106935
108911
|
}
|
|
106936
108912
|
async function findRuntimeRoot(projectRoot) {
|
|
106937
|
-
const projectRuntime =
|
|
106938
|
-
if (await pathExists(
|
|
108913
|
+
const projectRuntime = path201.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
108914
|
+
if (await pathExists(path201.join(projectRuntime, ".git"))) {
|
|
106939
108915
|
return projectRuntime;
|
|
106940
108916
|
}
|
|
106941
108917
|
const home = process.env.HOME;
|
|
106942
108918
|
if (!home) {
|
|
106943
108919
|
return null;
|
|
106944
108920
|
}
|
|
106945
|
-
const globalRuntime =
|
|
106946
|
-
if (await pathExists(
|
|
108921
|
+
const globalRuntime = path201.join(home, ".keryx", "keryx");
|
|
108922
|
+
if (await pathExists(path201.join(globalRuntime, ".git"))) {
|
|
106947
108923
|
return globalRuntime;
|
|
106948
108924
|
}
|
|
106949
108925
|
return null;
|
|
106950
108926
|
}
|
|
106951
108927
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
106952
108928
|
const dirs = [
|
|
106953
|
-
|
|
106954
|
-
|
|
106955
|
-
|
|
106956
|
-
|
|
106957
|
-
|
|
108929
|
+
path201.join(metaprojectRoot, "core"),
|
|
108930
|
+
path201.join(metaprojectRoot, "hooks", "post-update.d"),
|
|
108931
|
+
path201.join(metaprojectRoot, "modules"),
|
|
108932
|
+
path201.join(metaprojectRoot, "rules"),
|
|
108933
|
+
path201.join(metaprojectRoot, "skills", "project-rules"),
|
|
106958
108934
|
...modules.enableGdgraph ? [
|
|
106959
|
-
|
|
106960
|
-
|
|
108935
|
+
path201.join(metaprojectRoot, "core", "gdgraph"),
|
|
108936
|
+
path201.join(metaprojectRoot, "skills", "gdgraph")
|
|
106961
108937
|
] : [],
|
|
106962
108938
|
...modules.enableGdctx ? [
|
|
106963
|
-
|
|
106964
|
-
|
|
108939
|
+
path201.join(metaprojectRoot, "core", "gdctx"),
|
|
108940
|
+
path201.join(metaprojectRoot, "skills", "gdctx")
|
|
106965
108941
|
] : [],
|
|
106966
108942
|
...modules.enableGdwiki ? [
|
|
106967
|
-
|
|
106968
|
-
|
|
108943
|
+
path201.join(metaprojectRoot, "skills", "gdwiki"),
|
|
108944
|
+
path201.join(metaprojectRoot, "wiki", "templates")
|
|
106969
108945
|
] : [],
|
|
106970
108946
|
...modules.enableHealth ? [
|
|
106971
|
-
|
|
106972
|
-
|
|
108947
|
+
path201.join(metaprojectRoot, "core", "health"),
|
|
108948
|
+
path201.join(metaprojectRoot, "skills", "health")
|
|
106973
108949
|
] : [],
|
|
106974
108950
|
...modules.enableTesting ? [
|
|
106975
|
-
|
|
106976
|
-
|
|
108951
|
+
path201.join(metaprojectRoot, "core", "testing"),
|
|
108952
|
+
path201.join(metaprojectRoot, "skills", "testing")
|
|
106977
108953
|
] : [],
|
|
106978
108954
|
...modules.enableMemory ? [
|
|
106979
|
-
|
|
106980
|
-
|
|
106981
|
-
|
|
108955
|
+
path201.join(metaprojectRoot, "core", "memory"),
|
|
108956
|
+
path201.join(metaprojectRoot, "skills", "memory"),
|
|
108957
|
+
path201.join(metaprojectRoot, "memory", "templates")
|
|
106982
108958
|
] : [],
|
|
106983
108959
|
...modules.enableTasks ? [
|
|
106984
|
-
|
|
106985
|
-
|
|
108960
|
+
path201.join(metaprojectRoot, "flows"),
|
|
108961
|
+
path201.join(metaprojectRoot, "skills", "flow")
|
|
106986
108962
|
] : [],
|
|
106987
108963
|
...modules.enableSecurity ? [
|
|
106988
|
-
|
|
108964
|
+
path201.join(metaprojectRoot, "core", "security")
|
|
106989
108965
|
] : [],
|
|
106990
108966
|
...modules.enableSac ? [
|
|
106991
|
-
|
|
108967
|
+
path201.join(metaprojectRoot, "skills", "sac")
|
|
106992
108968
|
] : []
|
|
106993
108969
|
];
|
|
106994
108970
|
await Promise.all(dirs.map((dir) => mkdir63(dir, { recursive: true })));
|
|
106995
108971
|
}
|
|
106996
108972
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
106997
|
-
const gdgraphCoreRoot =
|
|
108973
|
+
const gdgraphCoreRoot = path201.join(metaprojectRoot, "core", "gdgraph");
|
|
106998
108974
|
await mkdir63(gdgraphCoreRoot, { recursive: true });
|
|
106999
108975
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
107000
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
108976
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path201.join(gdgraphCoreRoot, file));
|
|
107001
108977
|
}
|
|
107002
|
-
await writeTextIfChanged4(
|
|
108978
|
+
await writeTextIfChanged4(path201.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
107003
108979
|
}
|
|
107004
108980
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
107005
108981
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
@@ -107007,7 +108983,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
|
107007
108983
|
return;
|
|
107008
108984
|
}
|
|
107009
108985
|
await mkdir63(hooksRoot, { recursive: true });
|
|
107010
|
-
const hookPath =
|
|
108986
|
+
const hookPath = path201.join(hooksRoot, hookName);
|
|
107011
108987
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
107012
108988
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
107013
108989
|
const managedBlock = `${blockStart}
|
|
@@ -107028,7 +109004,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
107028
109004
|
if (!hooksRoot) {
|
|
107029
109005
|
return;
|
|
107030
109006
|
}
|
|
107031
|
-
const hookPath =
|
|
109007
|
+
const hookPath = path201.join(hooksRoot, hookName);
|
|
107032
109008
|
if (!await pathExists(hookPath)) {
|
|
107033
109009
|
return;
|
|
107034
109010
|
}
|
|
@@ -107050,7 +109026,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
107050
109026
|
if (!hooksRoot) {
|
|
107051
109027
|
return false;
|
|
107052
109028
|
}
|
|
107053
|
-
const hookPath =
|
|
109029
|
+
const hookPath = path201.join(hooksRoot, "pre-push");
|
|
107054
109030
|
if (!await pathExists(hookPath)) {
|
|
107055
109031
|
return false;
|
|
107056
109032
|
}
|
|
@@ -107065,7 +109041,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
107065
109041
|
return (await readFile100(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
107066
109042
|
}
|
|
107067
109043
|
async function readManifest5(metaprojectRoot) {
|
|
107068
|
-
const manifestPath =
|
|
109044
|
+
const manifestPath = path201.join(metaprojectRoot, "metaproject.json");
|
|
107069
109045
|
if (!await pathExists(manifestPath)) {
|
|
107070
109046
|
return {
|
|
107071
109047
|
exists: false,
|
|
@@ -107134,7 +109110,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
107134
109110
|
}
|
|
107135
109111
|
async function anyPathExists(root, candidates) {
|
|
107136
109112
|
for (const candidate of candidates) {
|
|
107137
|
-
if (await pathExists(
|
|
109113
|
+
if (await pathExists(path201.join(root, candidate))) {
|
|
107138
109114
|
return true;
|
|
107139
109115
|
}
|
|
107140
109116
|
}
|
|
@@ -107157,13 +109133,13 @@ function parseUpdateArgs(args) {
|
|
|
107157
109133
|
};
|
|
107158
109134
|
}
|
|
107159
109135
|
async function runPostUpdateHooks(projectRoot) {
|
|
107160
|
-
const hooksDir =
|
|
109136
|
+
const hooksDir = path201.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
107161
109137
|
if (!await pathExists(hooksDir)) {
|
|
107162
109138
|
return;
|
|
107163
109139
|
}
|
|
107164
109140
|
const entries = (await readdir37(hooksDir)).sort();
|
|
107165
109141
|
for (const entry of entries) {
|
|
107166
|
-
const hookPath =
|
|
109142
|
+
const hookPath = path201.join(hooksDir, entry);
|
|
107167
109143
|
try {
|
|
107168
109144
|
await accessExecutable(hookPath);
|
|
107169
109145
|
} catch {
|
|
@@ -107175,7 +109151,7 @@ async function runPostUpdateHooks(projectRoot) {
|
|
|
107175
109151
|
}
|
|
107176
109152
|
async function accessExecutable(filePath) {
|
|
107177
109153
|
await new Promise((resolve, reject) => {
|
|
107178
|
-
access4(filePath,
|
|
109154
|
+
access4(filePath, constants3.X_OK, (error) => {
|
|
107179
109155
|
if (error) {
|
|
107180
109156
|
reject(error);
|
|
107181
109157
|
return;
|
|
@@ -107186,7 +109162,7 @@ async function accessExecutable(filePath) {
|
|
|
107186
109162
|
}
|
|
107187
109163
|
async function run(command, args, cwd) {
|
|
107188
109164
|
await new Promise((resolve, reject) => {
|
|
107189
|
-
const child =
|
|
109165
|
+
const child = spawn7(command, args, {
|
|
107190
109166
|
cwd,
|
|
107191
109167
|
stdio: "inherit"
|
|
107192
109168
|
});
|
|
@@ -107204,14 +109180,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
107204
109180
|
if (await pathExists(filePath) && await readFile100(filePath, "utf8") === content) {
|
|
107205
109181
|
return;
|
|
107206
109182
|
}
|
|
107207
|
-
await mkdir63(
|
|
109183
|
+
await mkdir63(path201.dirname(filePath), { recursive: true });
|
|
107208
109184
|
await writeFile54(filePath, content, "utf8");
|
|
107209
109185
|
}
|
|
107210
109186
|
async function writeTextIfMissing4(filePath, content) {
|
|
107211
109187
|
if (await pathExists(filePath)) {
|
|
107212
109188
|
return;
|
|
107213
109189
|
}
|
|
107214
|
-
await mkdir63(
|
|
109190
|
+
await mkdir63(path201.dirname(filePath), { recursive: true });
|
|
107215
109191
|
await writeFile54(filePath, content, "utf8");
|
|
107216
109192
|
}
|
|
107217
109193
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -107219,7 +109195,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
107219
109195
|
if (await pathExists(to) && await readFile100(to, "utf8") === next) {
|
|
107220
109196
|
return;
|
|
107221
109197
|
}
|
|
107222
|
-
await mkdir63(
|
|
109198
|
+
await mkdir63(path201.dirname(to), { recursive: true });
|
|
107223
109199
|
await writeFile54(to, next, "utf8");
|
|
107224
109200
|
}
|
|
107225
109201
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -107228,7 +109204,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
107228
109204
|
return directPath;
|
|
107229
109205
|
}
|
|
107230
109206
|
if (relativePath.startsWith("../")) {
|
|
107231
|
-
const packagedSourcePath =
|
|
109207
|
+
const packagedSourcePath = path201.join(path201.dirname(fileURLToPath8(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
107232
109208
|
if (existsSync36(packagedSourcePath)) {
|
|
107233
109209
|
return packagedSourcePath;
|
|
107234
109210
|
}
|
|
@@ -107262,8 +109238,8 @@ function printHelp20() {
|
|
|
107262
109238
|
}
|
|
107263
109239
|
|
|
107264
109240
|
// src/commands/dashboard.ts
|
|
107265
|
-
import { spawn as
|
|
107266
|
-
import
|
|
109241
|
+
import { spawn as spawn8 } from "child_process";
|
|
109242
|
+
import path202 from "path";
|
|
107267
109243
|
init_args();
|
|
107268
109244
|
async function dashboardCommand(args = []) {
|
|
107269
109245
|
const options = parseOptions(args);
|
|
@@ -107274,7 +109250,7 @@ async function dashboardCommand(args = []) {
|
|
|
107274
109250
|
}
|
|
107275
109251
|
if (subcommand === "build") {
|
|
107276
109252
|
const result = await buildDashboard();
|
|
107277
|
-
const rel =
|
|
109253
|
+
const rel = path202.relative(process.cwd(), result.path);
|
|
107278
109254
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
107279
109255
|
note(`Open it: keryx dashboard open`);
|
|
107280
109256
|
return;
|
|
@@ -107282,7 +109258,7 @@ async function dashboardCommand(args = []) {
|
|
|
107282
109258
|
if (subcommand === "open") {
|
|
107283
109259
|
const result = await buildDashboard();
|
|
107284
109260
|
await openFile(result.path);
|
|
107285
|
-
const rel =
|
|
109261
|
+
const rel = path202.relative(process.cwd(), result.path);
|
|
107286
109262
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
107287
109263
|
return;
|
|
107288
109264
|
}
|
|
@@ -107301,7 +109277,7 @@ async function openFile(filePath) {
|
|
|
107301
109277
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
107302
109278
|
const args = process.platform === "win32" ? ["/c", "start", "", filePath] : [filePath];
|
|
107303
109279
|
await new Promise((resolve, reject) => {
|
|
107304
|
-
const child =
|
|
109280
|
+
const child = spawn8(command, args, { stdio: "ignore", detached: true });
|
|
107305
109281
|
child.on("error", reject);
|
|
107306
109282
|
child.on("close", (code) => {
|
|
107307
109283
|
if (code === 0) {
|
|
@@ -107326,12 +109302,12 @@ function printHelp21() {
|
|
|
107326
109302
|
}
|
|
107327
109303
|
|
|
107328
109304
|
// src/commands/agents.ts
|
|
107329
|
-
import { readFileSync as
|
|
109305
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
107330
109306
|
|
|
107331
109307
|
// src/agents/bootstrap.ts
|
|
107332
109308
|
import { mkdir as mkdir64, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
|
|
107333
109309
|
import { homedir as homedir10 } from "os";
|
|
107334
|
-
import
|
|
109310
|
+
import path203 from "path";
|
|
107335
109311
|
init_fs();
|
|
107336
109312
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
107337
109313
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -107341,35 +109317,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
107341
109317
|
aliases: ["claude-code"],
|
|
107342
109318
|
label: "Claude Code",
|
|
107343
109319
|
fileName: "CLAUDE.md",
|
|
107344
|
-
filePath: (homeRoot) =>
|
|
109320
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".claude", "CLAUDE.md")
|
|
107345
109321
|
},
|
|
107346
109322
|
{
|
|
107347
109323
|
id: "opencode",
|
|
107348
109324
|
aliases: ["open-code"],
|
|
107349
109325
|
label: "OpenCode",
|
|
107350
109326
|
fileName: "AGENTS.md",
|
|
107351
|
-
filePath: (homeRoot) =>
|
|
109327
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
107352
109328
|
},
|
|
107353
109329
|
{
|
|
107354
109330
|
id: "zcode",
|
|
107355
109331
|
aliases: ["zed", "zed-code"],
|
|
107356
109332
|
label: "ZCode",
|
|
107357
109333
|
fileName: "AGENTS.md",
|
|
107358
|
-
filePath: (homeRoot) =>
|
|
109334
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".zcode", "AGENTS.md")
|
|
107359
109335
|
},
|
|
107360
109336
|
{
|
|
107361
109337
|
id: "codex",
|
|
107362
109338
|
aliases: [],
|
|
107363
109339
|
label: "Codex",
|
|
107364
109340
|
fileName: "AGENTS.md",
|
|
107365
|
-
filePath: (homeRoot) =>
|
|
109341
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".codex", "AGENTS.md")
|
|
107366
109342
|
},
|
|
107367
109343
|
{
|
|
107368
109344
|
id: "antigravity",
|
|
107369
109345
|
aliases: ["antigravuty", "antigravity-code"],
|
|
107370
109346
|
label: "Antigravity",
|
|
107371
109347
|
fileName: "AGENTS.md",
|
|
107372
|
-
filePath: (homeRoot) =>
|
|
109348
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
107373
109349
|
}
|
|
107374
109350
|
];
|
|
107375
109351
|
function agentBootstrapRuntimeIds() {
|
|
@@ -107417,7 +109393,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
107417
109393
|
const dryRun = options.dryRun === true;
|
|
107418
109394
|
const wrote = next !== current;
|
|
107419
109395
|
if (wrote && !dryRun) {
|
|
107420
|
-
await mkdir64(
|
|
109396
|
+
await mkdir64(path203.dirname(filePath), { recursive: true });
|
|
107421
109397
|
await writeFile55(filePath, next, "utf8");
|
|
107422
109398
|
}
|
|
107423
109399
|
const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
@@ -107827,7 +109803,7 @@ function monitorCommand(args) {
|
|
|
107827
109803
|
}
|
|
107828
109804
|
let events;
|
|
107829
109805
|
try {
|
|
107830
|
-
events = parseAgentEvents(
|
|
109806
|
+
events = parseAgentEvents(readFileSync12(source, "utf8"));
|
|
107831
109807
|
} catch (error) {
|
|
107832
109808
|
console.error(`Failed to read/parse agent-event source "${source}": ${error.message}`);
|
|
107833
109809
|
process.exitCode = 1;
|
|
@@ -107954,7 +109930,7 @@ function printBootstrapHelp() {
|
|
|
107954
109930
|
// src/commands/metrics.ts
|
|
107955
109931
|
init_args();
|
|
107956
109932
|
import { readFile as readFile102 } from "fs/promises";
|
|
107957
|
-
import
|
|
109933
|
+
import path205 from "path";
|
|
107958
109934
|
|
|
107959
109935
|
// src/metrics/benchmark.ts
|
|
107960
109936
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -108771,7 +110747,7 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
108771
110747
|
|
|
108772
110748
|
// src/metrics/oracle-runner.ts
|
|
108773
110749
|
import { mkdir as mkdir65, writeFile as writeFile56 } from "fs/promises";
|
|
108774
|
-
import
|
|
110750
|
+
import path204 from "path";
|
|
108775
110751
|
|
|
108776
110752
|
// src/metrics/ir.ts
|
|
108777
110753
|
function toIdSet(ids) {
|
|
@@ -109217,9 +111193,9 @@ function buildEvidenceBundle(input, options = {}) {
|
|
|
109217
111193
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
109218
111194
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
109219
111195
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
109220
|
-
const dir =
|
|
111196
|
+
const dir = path204.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
109221
111197
|
await mkdir65(dir, { recursive: true });
|
|
109222
|
-
const write = (name, value) => writeFile56(
|
|
111198
|
+
const write = (name, value) => writeFile56(path204.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
109223
111199
|
`, "utf8");
|
|
109224
111200
|
await Promise.all([
|
|
109225
111201
|
write("inputs.json", bundle.inputs),
|
|
@@ -109259,7 +111235,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109259
111235
|
console.log("# metrics status");
|
|
109260
111236
|
console.log("");
|
|
109261
111237
|
console.log(`root: ${root}`);
|
|
109262
|
-
console.log(`enabled: ${await Bun.file(
|
|
111238
|
+
console.log(`enabled: ${await Bun.file(path205.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
109263
111239
|
const latest = await readLatestPointer(root);
|
|
109264
111240
|
console.log(`latest: ${latest.status}`);
|
|
109265
111241
|
return;
|
|
@@ -109271,7 +111247,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109271
111247
|
process.exitCode = 1;
|
|
109272
111248
|
return;
|
|
109273
111249
|
}
|
|
109274
|
-
const record = JSON.parse(await readFile102(
|
|
111250
|
+
const record = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109275
111251
|
const result = validateRunRecord(record);
|
|
109276
111252
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
109277
111253
|
for (const error of result.errors)
|
|
@@ -109296,7 +111272,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109296
111272
|
process.exitCode = 1;
|
|
109297
111273
|
return;
|
|
109298
111274
|
}
|
|
109299
|
-
const file =
|
|
111275
|
+
const file = path205.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
109300
111276
|
if (!await Bun.file(file).exists()) {
|
|
109301
111277
|
console.error(`Run not found: ${runId}`);
|
|
109302
111278
|
process.exitCode = 1;
|
|
@@ -109313,8 +111289,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109313
111289
|
process.exitCode = 1;
|
|
109314
111290
|
return;
|
|
109315
111291
|
}
|
|
109316
|
-
const a = JSON.parse(await readFile102(
|
|
109317
|
-
const b = JSON.parse(await readFile102(
|
|
111292
|
+
const a = JSON.parse(await readFile102(path205.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
111293
|
+
const b = JSON.parse(await readFile102(path205.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
109318
111294
|
const comparison = compareExecutionRuns(a, b);
|
|
109319
111295
|
console.log(stableJson(comparison));
|
|
109320
111296
|
return;
|
|
@@ -109348,8 +111324,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109348
111324
|
return;
|
|
109349
111325
|
}
|
|
109350
111326
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
109351
|
-
await Bun.write(
|
|
109352
|
-
console.log(`manifest: ${
|
|
111327
|
+
await Bun.write(path205.resolve(projectRoot, out), stableJson(template));
|
|
111328
|
+
console.log(`manifest: ${path205.relative(projectRoot, path205.resolve(projectRoot, out))}`);
|
|
109353
111329
|
return;
|
|
109354
111330
|
}
|
|
109355
111331
|
if (subcommand === "benchmark" && args[1] === "run") {
|
|
@@ -109363,7 +111339,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109363
111339
|
process.exitCode = 1;
|
|
109364
111340
|
return;
|
|
109365
111341
|
}
|
|
109366
|
-
const raw = JSON.parse(await readFile102(
|
|
111342
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109367
111343
|
const input = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
109368
111344
|
const result = validatePairedBenchmark(input);
|
|
109369
111345
|
console.log(stableJson(result));
|
|
@@ -109375,7 +111351,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109375
111351
|
process.exitCode = 1;
|
|
109376
111352
|
}
|
|
109377
111353
|
async function loadAffectedSets(projectRoot, file) {
|
|
109378
|
-
const raw = JSON.parse(await readFile102(
|
|
111354
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109379
111355
|
const map = new Map;
|
|
109380
111356
|
for (const entry of raw.targets ?? []) {
|
|
109381
111357
|
if (typeof entry.target === "string")
|
|
@@ -109446,7 +111422,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
|
|
|
109446
111422
|
let tasks;
|
|
109447
111423
|
let model;
|
|
109448
111424
|
try {
|
|
109449
|
-
const raw = JSON.parse(await readFile102(
|
|
111425
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, resultsPath), "utf8"));
|
|
109450
111426
|
tasks = raw.tasks ?? [];
|
|
109451
111427
|
model = raw.model;
|
|
109452
111428
|
} catch (error) {
|
|
@@ -109477,7 +111453,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
|
|
|
109477
111453
|
let cases;
|
|
109478
111454
|
let model;
|
|
109479
111455
|
try {
|
|
109480
|
-
const raw = JSON.parse(await readFile102(
|
|
111456
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, resultsPath), "utf8"));
|
|
109481
111457
|
cases = raw.cases ?? [];
|
|
109482
111458
|
model = raw.model;
|
|
109483
111459
|
} catch (error) {
|
|
@@ -109502,7 +111478,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
|
|
|
109502
111478
|
let cases;
|
|
109503
111479
|
let model;
|
|
109504
111480
|
try {
|
|
109505
|
-
const raw = JSON.parse(await readFile102(
|
|
111481
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, resultsPath), "utf8"));
|
|
109506
111482
|
cases = raw.cases ?? [];
|
|
109507
111483
|
model = raw.model;
|
|
109508
111484
|
} catch (error) {
|
|
@@ -109527,7 +111503,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
|
|
|
109527
111503
|
let cases;
|
|
109528
111504
|
let model;
|
|
109529
111505
|
try {
|
|
109530
|
-
const raw = JSON.parse(await readFile102(
|
|
111506
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, resultsPath), "utf8"));
|
|
109531
111507
|
cases = raw.cases ?? [];
|
|
109532
111508
|
model = raw.model;
|
|
109533
111509
|
} catch (error) {
|
|
@@ -109589,12 +111565,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
109589
111565
|
}
|
|
109590
111566
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
109591
111567
|
if (outDir) {
|
|
109592
|
-
const resolvedOut =
|
|
111568
|
+
const resolvedOut = path205.resolve(projectRoot, outDir);
|
|
109593
111569
|
for (const input of inputs) {
|
|
109594
111570
|
for (const named of input.golds) {
|
|
109595
111571
|
const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
109596
|
-
const dir = await persistEvidenceBundle(
|
|
109597
|
-
console.error(`bundle[${named.kind}]: ${
|
|
111572
|
+
const dir = await persistEvidenceBundle(path205.join(resolvedOut, named.kind), bundle, ladder);
|
|
111573
|
+
console.error(`bundle[${named.kind}]: ${path205.relative(projectRoot, dir)}`);
|
|
109598
111574
|
}
|
|
109599
111575
|
}
|
|
109600
111576
|
}
|
|
@@ -109618,7 +111594,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
109618
111594
|
return allValid;
|
|
109619
111595
|
}
|
|
109620
111596
|
async function loadCoverageMap2(projectRoot, file) {
|
|
109621
|
-
const raw = JSON.parse(await readFile102(
|
|
111597
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109622
111598
|
return raw.coverageMap ?? {};
|
|
109623
111599
|
}
|
|
109624
111600
|
async function runTestingLayer(projectRoot, args, ladder) {
|
|
@@ -109655,7 +111631,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
|
|
|
109655
111631
|
return result.valid;
|
|
109656
111632
|
}
|
|
109657
111633
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
109658
|
-
const raw = JSON.parse(await readFile102(
|
|
111634
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109659
111635
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
109660
111636
|
}
|
|
109661
111637
|
async function runMemoryLayer(projectRoot, args, ladder) {
|
|
@@ -109692,11 +111668,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
|
|
|
109692
111668
|
return result.valid;
|
|
109693
111669
|
}
|
|
109694
111670
|
async function loadWikiGoldK(projectRoot, file) {
|
|
109695
|
-
const raw = JSON.parse(await readFile102(
|
|
111671
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109696
111672
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
109697
111673
|
}
|
|
109698
111674
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
109699
|
-
const raw = JSON.parse(await readFile102(
|
|
111675
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109700
111676
|
const map = new Map;
|
|
109701
111677
|
for (const entry of raw.targets ?? []) {
|
|
109702
111678
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -109752,7 +111728,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
|
|
|
109752
111728
|
return result.valid;
|
|
109753
111729
|
}
|
|
109754
111730
|
async function loadGdctxFacts(projectRoot, file) {
|
|
109755
|
-
const raw = JSON.parse(await readFile102(
|
|
111731
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109756
111732
|
const inputs = [];
|
|
109757
111733
|
for (const entry of raw.inputs ?? []) {
|
|
109758
111734
|
if (typeof entry.input === "string") {
|
|
@@ -109790,7 +111766,7 @@ async function collect(projectRoot, args) {
|
|
|
109790
111766
|
process.exitCode = 1;
|
|
109791
111767
|
return;
|
|
109792
111768
|
}
|
|
109793
|
-
const raw = JSON.parse(await readFile102(
|
|
111769
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, eventFile), "utf8"));
|
|
109794
111770
|
const events = Array.isArray(raw) ? raw : raw.events;
|
|
109795
111771
|
const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
|
|
109796
111772
|
const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -109806,11 +111782,11 @@ async function collect(projectRoot, args) {
|
|
|
109806
111782
|
parentRunId: optionValue(args, "--parent-run-id") ?? null
|
|
109807
111783
|
});
|
|
109808
111784
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
|
|
109809
|
-
console.log(`json: ${
|
|
109810
|
-
console.log(`markdown: ${
|
|
111785
|
+
console.log(`json: ${path205.relative(projectRoot, result.jsonPath)}`);
|
|
111786
|
+
console.log(`markdown: ${path205.relative(projectRoot, result.markdownPath)}`);
|
|
109811
111787
|
}
|
|
109812
111788
|
function metricsRoot(projectRoot) {
|
|
109813
|
-
return
|
|
111789
|
+
return path205.join(projectRoot, ".metaproject", "data", "metrics");
|
|
109814
111790
|
}
|
|
109815
111791
|
function printMetricsHelp() {
|
|
109816
111792
|
console.log(`keryx metrics
|
|
@@ -109862,7 +111838,7 @@ async function versionCommand(args, deps = {}) {
|
|
|
109862
111838
|
// src/commands/workspace.ts
|
|
109863
111839
|
init_args();
|
|
109864
111840
|
init_workspace_service();
|
|
109865
|
-
import { randomUUID as
|
|
111841
|
+
import { randomUUID as randomUUID34 } from "crypto";
|
|
109866
111842
|
import { writeFile as writeFile57 } from "fs/promises";
|
|
109867
111843
|
|
|
109868
111844
|
// src/sac/fwk-explain.ts
|
|
@@ -109941,7 +111917,7 @@ async function workspaceCommand(args) {
|
|
|
109941
111917
|
const component = optionValue(args, "--component");
|
|
109942
111918
|
if (!title)
|
|
109943
111919
|
throw new Error("Usage: keryx workspace create --title <title> [--component <workspace-relative-ref>]");
|
|
109944
|
-
const workspace = await service6().create({ request: undefined, requestCorrelationId:
|
|
111920
|
+
const workspace = await service6().create({ request: undefined, requestCorrelationId: randomUUID34(), id: newWorkspaceId(), title, ...component ? { component: { kind: "component", uri: component } } : {} });
|
|
109945
111921
|
console.log(JSON.stringify(workspace, null, 2));
|
|
109946
111922
|
return;
|
|
109947
111923
|
}
|
|
@@ -109971,7 +111947,7 @@ async function workspaceCommand(args) {
|
|
|
109971
111947
|
const revision = optionValue(args, "--revision");
|
|
109972
111948
|
if (!workspaceId || !kind || !uri)
|
|
109973
111949
|
throw new Error("Usage: keryx workspace add-resource <workspace-id> --kind <kind> --uri <workspace-relative-ref> [--revision <revision>]");
|
|
109974
|
-
console.log(JSON.stringify(await service6().addResource({ request: undefined, requestCorrelationId:
|
|
111950
|
+
console.log(JSON.stringify(await service6().addResource({ request: undefined, requestCorrelationId: randomUUID34(), workspaceId, resource: { kind, uri, ...revision ? { revision } : {} } }), null, 2));
|
|
109975
111951
|
return;
|
|
109976
111952
|
}
|
|
109977
111953
|
if (subcommand === "archive") {
|
|
@@ -109979,7 +111955,7 @@ async function workspaceCommand(args) {
|
|
|
109979
111955
|
const workspaceId = args[1];
|
|
109980
111956
|
if (!workspaceId)
|
|
109981
111957
|
throw new Error("Usage: keryx workspace archive <workspace-id>");
|
|
109982
|
-
console.log(JSON.stringify(await service6().archive({ request: undefined, requestCorrelationId:
|
|
111958
|
+
console.log(JSON.stringify(await service6().archive({ request: undefined, requestCorrelationId: randomUUID34(), workspaceId }), null, 2));
|
|
109983
111959
|
return;
|
|
109984
111960
|
}
|
|
109985
111961
|
if (subcommand === "remove-resource") {
|
|
@@ -109988,7 +111964,7 @@ async function workspaceCommand(args) {
|
|
|
109988
111964
|
const uri = optionValue(args, "--uri");
|
|
109989
111965
|
if (!workspaceId || !uri)
|
|
109990
111966
|
throw new Error("Usage: keryx workspace remove-resource <workspace-id> --uri <workspace-relative-ref>");
|
|
109991
|
-
console.log(JSON.stringify(await service6().removeResource({ request: undefined, requestCorrelationId:
|
|
111967
|
+
console.log(JSON.stringify(await service6().removeResource({ request: undefined, requestCorrelationId: randomUUID34(), workspaceId, uri }), null, 2));
|
|
109992
111968
|
return;
|
|
109993
111969
|
}
|
|
109994
111970
|
if (subcommand === "rename") {
|
|
@@ -109997,7 +111973,7 @@ async function workspaceCommand(args) {
|
|
|
109997
111973
|
const title = optionValue(args, "--title");
|
|
109998
111974
|
if (!workspaceId || !title)
|
|
109999
111975
|
throw new Error("Usage: keryx workspace rename <workspace-id> --title <title>");
|
|
110000
|
-
console.log(JSON.stringify(await service6().rename({ request: undefined, requestCorrelationId:
|
|
111976
|
+
console.log(JSON.stringify(await service6().rename({ request: undefined, requestCorrelationId: randomUUID34(), workspaceId, title }), null, 2));
|
|
110001
111977
|
return;
|
|
110002
111978
|
}
|
|
110003
111979
|
if (subcommand === "overview") {
|
|
@@ -110009,7 +111985,7 @@ async function workspaceCommand(args) {
|
|
|
110009
111985
|
const maxTokens = Number(optionValue(args, "--max-tokens") ?? "4096");
|
|
110010
111986
|
if (!Number.isInteger(maxItems) || !Number.isInteger(maxTokens) || maxItems < 0 || maxTokens < 0)
|
|
110011
111987
|
throw new Error("--max-items and --max-tokens must be non-negative integers");
|
|
110012
|
-
const result = await createLocalFwkReadService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId:
|
|
111988
|
+
const result = await createLocalFwkReadService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID34(), budget: { maxItems, maxTokens } });
|
|
110013
111989
|
const normalized = normalizeFwkResult(result);
|
|
110014
111990
|
console.log(JSON.stringify(normalized, null, 2));
|
|
110015
111991
|
if (args.includes("--explain"))
|
|
@@ -110026,7 +112002,7 @@ async function workspaceCommand(args) {
|
|
|
110026
112002
|
const maxTokens = Number(optionValue(args, "--max-tokens") ?? "4096");
|
|
110027
112003
|
if (!Number.isInteger(maxItems) || !Number.isInteger(maxTokens) || maxItems < 0 || maxTokens < 0)
|
|
110028
112004
|
throw new Error("--max-items and --max-tokens must be non-negative integers");
|
|
110029
|
-
const result = await createLocalFwkReadService(process.cwd()).read({ workspaceId, itemId, request: undefined, requestCorrelationId:
|
|
112005
|
+
const result = await createLocalFwkReadService(process.cwd()).read({ workspaceId, itemId, request: undefined, requestCorrelationId: randomUUID34(), budget: { maxItems, maxTokens } });
|
|
110030
112006
|
const normalized = normalizeFwkResult(result);
|
|
110031
112007
|
console.log(JSON.stringify(normalized, null, 2));
|
|
110032
112008
|
if (args.includes("--explain"))
|
|
@@ -110048,14 +112024,14 @@ async function workspaceCommand(args) {
|
|
|
110048
112024
|
const session = findSession(cwd, sessionRef);
|
|
110049
112025
|
if (!session)
|
|
110050
112026
|
throw new Error(`no session matching "${sessionRef}" in this project \u2014 use \`keryx sessions list\``);
|
|
110051
|
-
const proposalId = `proposal-${
|
|
112027
|
+
const proposalId = `proposal-${randomUUID34().replace(/-/g, "").slice(0, 16)}`;
|
|
110052
112028
|
const notePath = proposalNotePath(cwd, workspaceId, proposalId);
|
|
110053
112029
|
const materializedNote = note === undefined ? undefined : prepareOutputForPersistence(await guardOutput({ cwd, content: note, target: "task", source: "generated", path: notePath }), note);
|
|
110054
112030
|
if (materializedNote !== undefined && !materializedNote.allowed)
|
|
110055
112031
|
throw new Error(materializedNote.reason);
|
|
110056
112032
|
const persistedNote = materializedNote?.allowed ? materializedNote.content : undefined;
|
|
110057
112033
|
const { service, wrapUpAuthority, authorizationServer } = createHarnessProposalLifecycleService(cwd, { workspaceId, ...persistedNote ? { note: persistedNote } : {} });
|
|
110058
|
-
const requestCorrelationId =
|
|
112034
|
+
const requestCorrelationId = randomUUID34();
|
|
110059
112035
|
const actor = await authorizationServer.actorContextFor(undefined, requestCorrelationId);
|
|
110060
112036
|
if (!actor)
|
|
110061
112037
|
throw new Error("trusted ActorContext is required");
|
|
@@ -110095,13 +112071,13 @@ async function workspaceCommand(args) {
|
|
|
110095
112071
|
const proposalId = args[2];
|
|
110096
112072
|
const decision = optionValue(args, "--decision");
|
|
110097
112073
|
const reason = optionValue(args, "--reason");
|
|
110098
|
-
const idempotencyKey = optionValue(args, "--idempotency-key") ??
|
|
112074
|
+
const idempotencyKey = optionValue(args, "--idempotency-key") ?? randomUUID34();
|
|
110099
112075
|
const confirmToken = optionValue(args, "--confirm-token");
|
|
110100
112076
|
if (!workspaceId || !proposalId || !decision)
|
|
110101
112077
|
throw new Error("Usage: keryx workspace review <workspace-id> <proposal-id> --decision <accepted|rejected|dismissed> [--reason <reason>] [--idempotency-key <key>] [--confirm-token <token>]");
|
|
110102
112078
|
if (decision === "accepted" && !confirmToken)
|
|
110103
112079
|
throw new Error("--decision accepted requires --confirm-token \u2014 run `keryx workspace confirm-review " + workspaceId + " " + proposalId + "` first");
|
|
110104
|
-
const result = await createHarnessProposalLifecycleService(process.cwd(), { workspaceId }).service.review({ request: undefined, requestCorrelationId:
|
|
112080
|
+
const result = await createHarnessProposalLifecycleService(process.cwd(), { workspaceId }).service.review({ request: undefined, requestCorrelationId: randomUUID34(), workspaceId, proposalId, decision, idempotencyKey, interactive: true, ...reason ? { reason } : {}, ...confirmToken ? { confirmToken } : {} });
|
|
110105
112081
|
console.log(JSON.stringify(normalizeProposalLifecycleResult(result), null, 2));
|
|
110106
112082
|
return;
|
|
110107
112083
|
}
|
|
@@ -110126,7 +112102,7 @@ async function workspaceCommand(args) {
|
|
|
110126
112102
|
const artifactRef = optionValue(args, "--artifact");
|
|
110127
112103
|
if (!workspaceId || !to || !artifactRef)
|
|
110128
112104
|
throw new Error("Usage: keryx workspace handoff <workspace-id> --to <subject> --artifact <ref>");
|
|
110129
|
-
const activity = await createLocalCollaborationService(process.cwd()).record({ request: undefined, requestCorrelationId:
|
|
112105
|
+
const activity = await createLocalCollaborationService(process.cwd()).record({ request: undefined, requestCorrelationId: randomUUID34(), workspaceId, activity: { kind: "handoff-recorded", handoff: { to, artifactRef } } });
|
|
110130
112106
|
console.log(JSON.stringify(activity, null, 2));
|
|
110131
112107
|
return;
|
|
110132
112108
|
}
|
|
@@ -110135,7 +112111,7 @@ async function workspaceCommand(args) {
|
|
|
110135
112111
|
const workspaceId = args[1];
|
|
110136
112112
|
if (!workspaceId)
|
|
110137
112113
|
throw new Error("Usage: keryx workspace collaboration <workspace-id>");
|
|
110138
|
-
console.log(JSON.stringify(await createLocalCollaborationService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId:
|
|
112114
|
+
console.log(JSON.stringify(await createLocalCollaborationService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID34() }), null, 2));
|
|
110139
112115
|
return;
|
|
110140
112116
|
}
|
|
110141
112117
|
if (subcommand === "policy-readiness") {
|
|
@@ -110162,7 +112138,7 @@ async function workspaceCommand(args) {
|
|
|
110162
112138
|
const workspaceId = args[1];
|
|
110163
112139
|
const cwd = process.cwd();
|
|
110164
112140
|
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
110165
|
-
const actor = await authorizationServer.actorContextFor(undefined,
|
|
112141
|
+
const actor = await authorizationServer.actorContextFor(undefined, randomUUID34());
|
|
110166
112142
|
if (!actor)
|
|
110167
112143
|
throw new Error("trusted ActorContext is required");
|
|
110168
112144
|
if (workspaceId) {
|
|
@@ -110901,13 +112877,17 @@ Commands:
|
|
|
110901
112877
|
forgetting Read the deletion trail \u2014 was this removed, or did it never exist?
|
|
110902
112878
|
`);
|
|
110903
112879
|
}
|
|
112880
|
+
function exitCodeForError(error) {
|
|
112881
|
+
return error instanceof ShellFlagError ? error.exitCode : 1;
|
|
112882
|
+
}
|
|
110904
112883
|
if (import.meta.main) {
|
|
110905
112884
|
main().catch((error) => {
|
|
110906
112885
|
console.error(error instanceof Error ? error.message : String(error));
|
|
110907
|
-
process.exitCode =
|
|
112886
|
+
process.exitCode = exitCodeForError(error);
|
|
110908
112887
|
});
|
|
110909
112888
|
}
|
|
110910
112889
|
export {
|
|
110911
112890
|
CLI_ROUTES,
|
|
112891
|
+
exitCodeForError,
|
|
110912
112892
|
main
|
|
110913
112893
|
};
|