@mrciphersmith/keryx 0.2.122 → 0.2.124
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 +1762 -475
- 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
|
}
|
|
@@ -43570,9 +43804,9 @@ var init_login = __esm(() => {
|
|
|
43570
43804
|
});
|
|
43571
43805
|
|
|
43572
43806
|
// src/tui/debug-watcher.ts
|
|
43573
|
-
import { spawn as spawn6, spawnSync as
|
|
43574
|
-
import { closeSync as closeSync3, constants as constants2, openSync as
|
|
43575
|
-
import
|
|
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";
|
|
43576
43810
|
function parseEpollFdinfo(text) {
|
|
43577
43811
|
const out = [];
|
|
43578
43812
|
for (const line of text.split(`
|
|
@@ -43595,7 +43829,7 @@ function detectStall(history, window = 3) {
|
|
|
43595
43829
|
return;
|
|
43596
43830
|
}
|
|
43597
43831
|
const recent = history.slice(-window);
|
|
43598
|
-
if (recent.every((s) => s.alive && s.readerArmed === false)) {
|
|
43832
|
+
if (recent.every((s) => s.alive && s.readerArmed === false && (s.pending === undefined || s.pending > 0))) {
|
|
43599
43833
|
return "reader-not-polled";
|
|
43600
43834
|
}
|
|
43601
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))) {
|
|
@@ -43616,7 +43850,7 @@ function isAlive(pid) {
|
|
|
43616
43850
|
}
|
|
43617
43851
|
function readText(file) {
|
|
43618
43852
|
try {
|
|
43619
|
-
return
|
|
43853
|
+
return readFileSync11(file, "utf8");
|
|
43620
43854
|
} catch {
|
|
43621
43855
|
return;
|
|
43622
43856
|
}
|
|
@@ -43633,7 +43867,7 @@ function listFds(pid) {
|
|
|
43633
43867
|
const fd = Number(name);
|
|
43634
43868
|
let link;
|
|
43635
43869
|
try {
|
|
43636
|
-
link =
|
|
43870
|
+
link = readlinkSync3(`/proc/${pid}/fd/${name}`);
|
|
43637
43871
|
} catch {
|
|
43638
43872
|
continue;
|
|
43639
43873
|
}
|
|
@@ -43659,26 +43893,77 @@ function threads(pid) {
|
|
|
43659
43893
|
return { tid, comm: (readText(`${base}/comm`) ?? "").trim(), wchan: (readText(`${base}/wchan`) ?? "").trim(), state };
|
|
43660
43894
|
});
|
|
43661
43895
|
}
|
|
43662
|
-
|
|
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
|
+
} };
|
|
43663
43945
|
if (process.platform !== "linux") {
|
|
43664
|
-
return
|
|
43665
|
-
return;
|
|
43666
|
-
};
|
|
43946
|
+
return none;
|
|
43667
43947
|
}
|
|
43668
43948
|
try {
|
|
43669
43949
|
const ffi = await import("bun:ffi");
|
|
43670
43950
|
const libc = ffi.dlopen("libc.so.6", {
|
|
43671
|
-
ioctl: { args: [ffi.FFIType.i32, ffi.FFIType.u64, ffi.FFIType.ptr], returns: ffi.FFIType.i32 }
|
|
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 }
|
|
43672
43953
|
});
|
|
43673
|
-
return
|
|
43674
|
-
|
|
43675
|
-
|
|
43676
|
-
|
|
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
|
+
}
|
|
43677
43964
|
};
|
|
43678
43965
|
} catch {
|
|
43679
|
-
return
|
|
43680
|
-
return;
|
|
43681
|
-
};
|
|
43966
|
+
return none;
|
|
43682
43967
|
}
|
|
43683
43968
|
}
|
|
43684
43969
|
function spawnDebugWatcher(run) {
|
|
@@ -43710,22 +43995,23 @@ async function runDebugWatcher(argv) {
|
|
|
43710
43995
|
process.exitCode = 2;
|
|
43711
43996
|
return;
|
|
43712
43997
|
}
|
|
43713
|
-
const logFile =
|
|
43714
|
-
const shellLog =
|
|
43998
|
+
const logFile = path196.join(dirArg, "watcher.ndjson");
|
|
43999
|
+
const shellLog = path196.join(dirArg, "shell.ndjson");
|
|
43715
44000
|
const log = (kind, data = {}) => {
|
|
43716
44001
|
try {
|
|
43717
44002
|
appendOwnerOnlyLine(logFile, JSON.stringify({ ts: new Date().toISOString(), watcher: process.pid, target: pid, kind, ...data }));
|
|
43718
44003
|
} catch {}
|
|
43719
44004
|
};
|
|
43720
44005
|
const linux = process.platform === "linux" && isAlive(pid) && readText(`/proc/${pid}/stat`) !== undefined;
|
|
43721
|
-
const
|
|
44006
|
+
const probes = await makeTtyProbes();
|
|
44007
|
+
const pendingOf = probes.pending;
|
|
43722
44008
|
let ttyPath;
|
|
43723
44009
|
let ttyFd;
|
|
43724
44010
|
if (linux) {
|
|
43725
44011
|
try {
|
|
43726
|
-
ttyPath =
|
|
44012
|
+
ttyPath = readlinkSync3(`/proc/${pid}/fd/0`);
|
|
43727
44013
|
if (isTtyLink(ttyPath)) {
|
|
43728
|
-
ttyFd =
|
|
44014
|
+
ttyFd = openSync4(ttyPath, constants2.O_RDONLY | constants2.O_NONBLOCK | constants2.O_NOCTTY);
|
|
43729
44015
|
}
|
|
43730
44016
|
} catch (error) {
|
|
43731
44017
|
log("tty.open-failed", { error: String(error) });
|
|
@@ -43734,13 +44020,14 @@ async function runDebugWatcher(argv) {
|
|
|
43734
44020
|
log("watcher.start", { linux, ttyPath, fionread: ttyFd !== undefined && pendingOf(ttyFd) !== undefined });
|
|
43735
44021
|
const history = [];
|
|
43736
44022
|
let lastLogged = "";
|
|
44023
|
+
let lastTermios = "";
|
|
43737
44024
|
let lastPeriodic = 0;
|
|
43738
44025
|
let lastSignal = 0;
|
|
43739
44026
|
const fullSnapshot = () => {
|
|
43740
44027
|
const fds = listFds(pid);
|
|
43741
44028
|
let stty;
|
|
43742
44029
|
if (ttyPath !== undefined) {
|
|
43743
|
-
const res =
|
|
44030
|
+
const res = spawnSync4("stty", ["-a", "-F", ttyPath], { encoding: "utf8", timeout: 2000 });
|
|
43744
44031
|
stty = (res.stdout ?? "").trim() || (res.stderr ?? "").trim();
|
|
43745
44032
|
}
|
|
43746
44033
|
return {
|
|
@@ -43766,7 +44053,7 @@ async function runDebugWatcher(argv) {
|
|
|
43766
44053
|
sample.pending = pendingOf(ttyFd);
|
|
43767
44054
|
}
|
|
43768
44055
|
try {
|
|
43769
|
-
sample.heartbeatAgeMs = Date.now() -
|
|
44056
|
+
sample.heartbeatAgeMs = Date.now() - statSync7(shellLog).mtimeMs;
|
|
43770
44057
|
} catch {
|
|
43771
44058
|
sample.heartbeatAgeMs = undefined;
|
|
43772
44059
|
}
|
|
@@ -43802,10 +44089,25 @@ async function runDebugWatcher(argv) {
|
|
|
43802
44089
|
}
|
|
43803
44090
|
return true;
|
|
43804
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);
|
|
43805
44105
|
await new Promise((resolve) => {
|
|
43806
44106
|
const timer = setInterval(() => {
|
|
43807
44107
|
if (!tick()) {
|
|
43808
44108
|
clearInterval(timer);
|
|
44109
|
+
if (termiosTimer !== undefined)
|
|
44110
|
+
clearInterval(termiosTimer);
|
|
43809
44111
|
resolve();
|
|
43810
44112
|
}
|
|
43811
44113
|
}, 1000);
|
|
@@ -43817,7 +44119,7 @@ async function runDebugWatcher(argv) {
|
|
|
43817
44119
|
}
|
|
43818
44120
|
log("watcher.stop");
|
|
43819
44121
|
}
|
|
43820
|
-
var EPOLLIN = 1, FIONREAD = 21531, isTtyLink = (link) => link.startsWith("/dev/pts/") || /^\/dev\/tty/.test(link);
|
|
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;
|
|
43821
44123
|
var init_debug_watcher = __esm(() => {
|
|
43822
44124
|
init_config_dir();
|
|
43823
44125
|
});
|
|
@@ -43918,23 +44220,23 @@ import path24 from "path";
|
|
|
43918
44220
|
// src/lib/project-registry.ts
|
|
43919
44221
|
init_config_dir();
|
|
43920
44222
|
import {
|
|
43921
|
-
chmodSync as
|
|
44223
|
+
chmodSync as chmodSync3,
|
|
43922
44224
|
closeSync as closeSync2,
|
|
43923
44225
|
copyFileSync,
|
|
43924
44226
|
existsSync as existsSync4,
|
|
43925
44227
|
fsyncSync,
|
|
43926
44228
|
openSync as openSync2,
|
|
43927
44229
|
realpathSync as realpathSync2,
|
|
43928
|
-
renameSync as
|
|
43929
|
-
statSync as
|
|
44230
|
+
renameSync as renameSync3,
|
|
44231
|
+
statSync as statSync5,
|
|
43930
44232
|
unlinkSync as unlinkSync2,
|
|
43931
|
-
writeFileSync as
|
|
44233
|
+
writeFileSync as writeFileSync4
|
|
43932
44234
|
} from "fs";
|
|
43933
44235
|
import path23 from "path";
|
|
43934
44236
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
43935
44237
|
|
|
43936
44238
|
// src/lib/file-lock.ts
|
|
43937
|
-
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";
|
|
43938
44240
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
43939
44241
|
import path22 from "path";
|
|
43940
44242
|
var LOCK_STALE_MS = 1e4;
|
|
@@ -43950,7 +44252,7 @@ function sleepSync(ms) {
|
|
|
43950
44252
|
}
|
|
43951
44253
|
function withFileLock2(lockPath, fn, options = {}) {
|
|
43952
44254
|
try {
|
|
43953
|
-
|
|
44255
|
+
mkdirSync3(path22.dirname(lockPath), { recursive: true, mode: 448 });
|
|
43954
44256
|
} catch {
|
|
43955
44257
|
return null;
|
|
43956
44258
|
}
|
|
@@ -43966,18 +44268,18 @@ function withFileLock2(lockPath, fn, options = {}) {
|
|
|
43966
44268
|
try {
|
|
43967
44269
|
handle = openSync(lockPath, "wx", 384);
|
|
43968
44270
|
try {
|
|
43969
|
-
|
|
44271
|
+
writeFileSync3(handle, nonce, { encoding: "utf8" });
|
|
43970
44272
|
} catch (cause) {
|
|
43971
44273
|
closeSync(handle);
|
|
43972
44274
|
handle = null;
|
|
43973
|
-
|
|
44275
|
+
rmSync2(lockPath, { force: true });
|
|
43974
44276
|
throw cause;
|
|
43975
44277
|
}
|
|
43976
44278
|
} catch {
|
|
43977
|
-
const age =
|
|
44279
|
+
const age = statSync4(lockPath, { throwIfNoEntry: false })?.mtimeMs;
|
|
43978
44280
|
if (age !== undefined && Date.now() - age > LOCK_STALE_MS) {
|
|
43979
44281
|
try {
|
|
43980
|
-
|
|
44282
|
+
rmSync2(lockPath, { force: true });
|
|
43981
44283
|
} catch {}
|
|
43982
44284
|
}
|
|
43983
44285
|
if (!announced && Date.now() - started > 1000) {
|
|
@@ -43994,8 +44296,8 @@ function withFileLock2(lockPath, fn, options = {}) {
|
|
|
43994
44296
|
closeSync(handle);
|
|
43995
44297
|
} catch {}
|
|
43996
44298
|
try {
|
|
43997
|
-
if (
|
|
43998
|
-
|
|
44299
|
+
if (readFileSync4(lockPath, "utf8") === nonce) {
|
|
44300
|
+
rmSync2(lockPath, { force: true });
|
|
43999
44301
|
}
|
|
44000
44302
|
} catch {}
|
|
44001
44303
|
}
|
|
@@ -44117,7 +44419,7 @@ function tightenBackup(file) {
|
|
|
44117
44419
|
return;
|
|
44118
44420
|
}
|
|
44119
44421
|
try {
|
|
44120
|
-
|
|
44422
|
+
chmodSync3(file, 384);
|
|
44121
44423
|
} catch {}
|
|
44122
44424
|
}
|
|
44123
44425
|
function quarantineDamagedRegistry(dir, onWarn) {
|
|
@@ -44145,13 +44447,13 @@ function saveProjectRegistry(registry, dir, onWarn) {
|
|
|
44145
44447
|
};
|
|
44146
44448
|
const handle = openSync2(temp, "wx", 384);
|
|
44147
44449
|
try {
|
|
44148
|
-
|
|
44450
|
+
writeFileSync4(handle, `${JSON.stringify(sorted, null, 2)}
|
|
44149
44451
|
`, { encoding: "utf8" });
|
|
44150
44452
|
fsyncSync(handle);
|
|
44151
44453
|
} finally {
|
|
44152
44454
|
closeSync2(handle);
|
|
44153
44455
|
}
|
|
44154
|
-
|
|
44456
|
+
renameSync3(temp, file);
|
|
44155
44457
|
return true;
|
|
44156
44458
|
} catch {
|
|
44157
44459
|
try {
|
|
@@ -44231,7 +44533,7 @@ function forgetProject(projectId, dir, onWarn) {
|
|
|
44231
44533
|
return outcome ?? "write-failed";
|
|
44232
44534
|
}
|
|
44233
44535
|
function isInitializedProject(projectPath) {
|
|
44234
|
-
return
|
|
44536
|
+
return statSync5(path23.join(projectPath, ".metaproject"), { throwIfNoEntry: false })?.isDirectory() === true;
|
|
44235
44537
|
}
|
|
44236
44538
|
function listProjects(dir, onWarn) {
|
|
44237
44539
|
const registry = loadProjectRegistry(dir, onWarn);
|
|
@@ -45182,7 +45484,7 @@ async function choice(question, choices, defaultValue) {
|
|
|
45182
45484
|
|
|
45183
45485
|
// src/lib/project-sandbox-policy.ts
|
|
45184
45486
|
init_paths();
|
|
45185
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
45487
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
45186
45488
|
import path29 from "path";
|
|
45187
45489
|
|
|
45188
45490
|
// src/harness/process/sandbox/network-run.ts
|
|
@@ -45376,7 +45678,7 @@ function loadProjectSandboxPolicy(cwdOrRoot) {
|
|
|
45376
45678
|
if (!existsSync7(file)) {
|
|
45377
45679
|
return {};
|
|
45378
45680
|
}
|
|
45379
|
-
const raw = JSON.parse(
|
|
45681
|
+
const raw = JSON.parse(readFileSync5(file, "utf8"));
|
|
45380
45682
|
return sanitizeProjectSandboxPolicy(raw);
|
|
45381
45683
|
} catch {
|
|
45382
45684
|
return {};
|
|
@@ -45399,8 +45701,8 @@ function writeProjectSandboxPolicySkeletonIfMissing(cwdOrRoot) {
|
|
|
45399
45701
|
if (existsSync7(file)) {
|
|
45400
45702
|
return false;
|
|
45401
45703
|
}
|
|
45402
|
-
|
|
45403
|
-
|
|
45704
|
+
mkdirSync4(path29.dirname(file), { recursive: true });
|
|
45705
|
+
writeFileSync5(file, projectSandboxPolicySkeleton(), { mode: 420 });
|
|
45404
45706
|
return true;
|
|
45405
45707
|
} catch {
|
|
45406
45708
|
return false;
|
|
@@ -45408,7 +45710,7 @@ function writeProjectSandboxPolicySkeletonIfMissing(cwdOrRoot) {
|
|
|
45408
45710
|
}
|
|
45409
45711
|
|
|
45410
45712
|
// src/gdskills/catalog.ts
|
|
45411
|
-
import { existsSync as existsSync8, readFileSync as
|
|
45713
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
45412
45714
|
import path30 from "path";
|
|
45413
45715
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
45414
45716
|
var GDSKILLS_PROFILES = [
|
|
@@ -45903,7 +46205,7 @@ function readBundledSkillRouting(category, name) {
|
|
|
45903
46205
|
if (file === undefined) {
|
|
45904
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.`);
|
|
45905
46207
|
}
|
|
45906
|
-
const { description, triggers } = parseSkillFrontmatter(
|
|
46208
|
+
const { description, triggers } = parseSkillFrontmatter(readFileSync6(file, "utf8"));
|
|
45907
46209
|
if (description === undefined || description.length === 0 || triggers === undefined || triggers.length === 0) {
|
|
45908
46210
|
throw new Error(`${file}: frontmatter must declare a non-empty \`description\` and \`triggers:\` list; the skill router scores both.`);
|
|
45909
46211
|
}
|
|
@@ -50624,7 +50926,7 @@ import path47 from "path";
|
|
|
50624
50926
|
// package.json
|
|
50625
50927
|
var package_default = {
|
|
50626
50928
|
name: "@mrciphersmith/keryx",
|
|
50627
|
-
version: "0.2.
|
|
50929
|
+
version: "0.2.124",
|
|
50628
50930
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
50629
50931
|
private: false,
|
|
50630
50932
|
publishConfig: {
|
|
@@ -60575,7 +60877,7 @@ function escapeRegExp6(value) {
|
|
|
60575
60877
|
}
|
|
60576
60878
|
|
|
60577
60879
|
// src/gdskills/bundled-eval.ts
|
|
60578
|
-
import { existsSync as existsSync25, readFileSync as
|
|
60880
|
+
import { existsSync as existsSync25, readFileSync as readFileSync8, readdirSync as readdirSync2 } from "fs";
|
|
60579
60881
|
import path147 from "path";
|
|
60580
60882
|
|
|
60581
60883
|
// src/gdskills/model-tier.ts
|
|
@@ -61514,7 +61816,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61514
61816
|
const skill = path147.basename(skillDir);
|
|
61515
61817
|
const rel = path147.relative(skillsRoot, file).split(path147.sep).join("/");
|
|
61516
61818
|
const category = path147.dirname(rel).split("/")[0] ?? "";
|
|
61517
|
-
const text =
|
|
61819
|
+
const text = readFileSync8(file, "utf8");
|
|
61518
61820
|
const add = (check, line, message) => {
|
|
61519
61821
|
findings.push({ check, skill, file: rel, line, message });
|
|
61520
61822
|
};
|
|
@@ -61664,7 +61966,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61664
61966
|
}
|
|
61665
61967
|
for (const file of bundledRuleFiles(root)) {
|
|
61666
61968
|
const rel = path147.relative(root, file).split(path147.sep).join("/");
|
|
61667
|
-
const text =
|
|
61969
|
+
const text = readFileSync8(file, "utf8");
|
|
61668
61970
|
const add = (check, line, message) => {
|
|
61669
61971
|
findings.push({ check, skill: path147.basename(file, path147.extname(file)), file: rel, line, message });
|
|
61670
61972
|
};
|
|
@@ -61674,7 +61976,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61674
61976
|
const skillDir = path147.dirname(file);
|
|
61675
61977
|
const skill = path147.basename(skillDir);
|
|
61676
61978
|
const rel = path147.relative(skillsRoot, file).split(path147.sep).join("/");
|
|
61677
|
-
const text =
|
|
61979
|
+
const text = readFileSync8(file, "utf8");
|
|
61678
61980
|
const add = (check, line, message) => {
|
|
61679
61981
|
findings.push({ check, skill, file: rel, line, message });
|
|
61680
61982
|
};
|
|
@@ -61703,7 +62005,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61703
62005
|
const dir = path147.dirname(file);
|
|
61704
62006
|
let canonicalComparable;
|
|
61705
62007
|
try {
|
|
61706
|
-
canonicalComparable = buildComparableText(
|
|
62008
|
+
canonicalComparable = buildComparableText(readFileSync8(file, "utf8"));
|
|
61707
62009
|
} catch {
|
|
61708
62010
|
continue;
|
|
61709
62011
|
}
|
|
@@ -61716,7 +62018,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
61716
62018
|
continue;
|
|
61717
62019
|
let buildComparable;
|
|
61718
62020
|
try {
|
|
61719
|
-
buildComparable = buildComparableText(
|
|
62021
|
+
buildComparable = buildComparableText(readFileSync8(buildPath, "utf8"));
|
|
61720
62022
|
} catch {
|
|
61721
62023
|
continue;
|
|
61722
62024
|
}
|
|
@@ -74022,11 +74324,11 @@ function wrapSeatbelt(command, profile) {
|
|
|
74022
74324
|
}
|
|
74023
74325
|
|
|
74024
74326
|
// src/harness/process/sandbox/bwrap.ts
|
|
74025
|
-
import { statSync as
|
|
74327
|
+
import { statSync as statSync6 } from "fs";
|
|
74026
74328
|
var BWRAP_PROGRAM = "bwrap";
|
|
74027
74329
|
function inspectMaskTarget(target) {
|
|
74028
74330
|
try {
|
|
74029
|
-
return
|
|
74331
|
+
return statSync6(target).isDirectory() ? "dir" : "file";
|
|
74030
74332
|
} catch {
|
|
74031
74333
|
return "missing";
|
|
74032
74334
|
}
|
|
@@ -76397,7 +76699,7 @@ function credentialSummary(raw) {
|
|
|
76397
76699
|
|
|
76398
76700
|
// src/mcp-servers/store.ts
|
|
76399
76701
|
init_config_dir();
|
|
76400
|
-
import { mkdirSync as
|
|
76702
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
76401
76703
|
import path170 from "path";
|
|
76402
76704
|
var SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
76403
76705
|
function userConfigFile(configDir) {
|
|
@@ -76446,8 +76748,8 @@ function writeDocument(file, doc, scope) {
|
|
|
76446
76748
|
writeOwnerOnlyFileAtomic(file, body);
|
|
76447
76749
|
return;
|
|
76448
76750
|
}
|
|
76449
|
-
|
|
76450
|
-
|
|
76751
|
+
mkdirSync7(path170.dirname(file), { recursive: true });
|
|
76752
|
+
writeFileSync8(file, body, { encoding: "utf8" });
|
|
76451
76753
|
}
|
|
76452
76754
|
function addServer(options) {
|
|
76453
76755
|
if (!SERVER_NAME_PATTERN.test(options.name)) {
|
|
@@ -79853,7 +80155,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
|
|
|
79853
80155
|
}
|
|
79854
80156
|
|
|
79855
80157
|
// src/commands/harness.ts
|
|
79856
|
-
import { readFileSync as
|
|
80158
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
79857
80159
|
import path182 from "path";
|
|
79858
80160
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
79859
80161
|
|
|
@@ -81766,7 +82068,7 @@ async function harnessCommand(args, deps) {
|
|
|
81766
82068
|
const result = await runOffline(input, config, runDeps);
|
|
81767
82069
|
structured = toStructured(result);
|
|
81768
82070
|
if (record !== undefined && record.length > 0) {
|
|
81769
|
-
|
|
82071
|
+
writeFileSync9(record, `${JSON.stringify(toRunRecord(result, {
|
|
81770
82072
|
runId: result.output.runId,
|
|
81771
82073
|
status: result.output.status,
|
|
81772
82074
|
recordedAt: result.output.startedAt
|
|
@@ -81812,7 +82114,7 @@ function parseReplayArgs(args) {
|
|
|
81812
82114
|
function readJsonFile2(file) {
|
|
81813
82115
|
let text;
|
|
81814
82116
|
try {
|
|
81815
|
-
text =
|
|
82117
|
+
text = readFileSync9(file, "utf8");
|
|
81816
82118
|
} catch (error) {
|
|
81817
82119
|
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
81818
82120
|
}
|
|
@@ -81867,7 +82169,7 @@ function harnessReplay(args, deps) {
|
|
|
81867
82169
|
return;
|
|
81868
82170
|
}
|
|
81869
82171
|
if (parsed.writeFixture !== undefined && parsed.writeFixture.length > 0) {
|
|
81870
|
-
|
|
82172
|
+
writeFileSync9(parsed.writeFixture, `${JSON.stringify(fixture.value, null, 2)}
|
|
81871
82173
|
`, {
|
|
81872
82174
|
encoding: "utf8",
|
|
81873
82175
|
mode: 384
|
|
@@ -82179,7 +82481,7 @@ function readSpecArg(args) {
|
|
|
82179
82481
|
if (specPath === undefined) {
|
|
82180
82482
|
throw new Error("keryx harness: a --spec <path> argument is required when no spec is injected.");
|
|
82181
82483
|
}
|
|
82182
|
-
return JSON.parse(
|
|
82484
|
+
return JSON.parse(readFileSync9(specPath, "utf8"));
|
|
82183
82485
|
}
|
|
82184
82486
|
var DEFAULT_CHILD_RESULT = `STATUS: DONE
|
|
82185
82487
|
Extension completed within its granted capabilities.`;
|
|
@@ -82286,7 +82588,7 @@ init_providers();
|
|
|
82286
82588
|
init_make_provider();
|
|
82287
82589
|
init_orient();
|
|
82288
82590
|
init_metaproject_adapter();
|
|
82289
|
-
import { randomUUID as
|
|
82591
|
+
import { randomUUID as randomUUID30 } from "crypto";
|
|
82290
82592
|
import * as readline2 from "readline";
|
|
82291
82593
|
|
|
82292
82594
|
// src/commands/agent-approval-context.ts
|
|
@@ -88542,7 +88844,7 @@ async function runGoalCommand(params) {
|
|
|
88542
88844
|
const wasOpened = slateSession.opened;
|
|
88543
88845
|
await ensureSlateOpened(slateSession, mintAttemptId, { provider: deps.providerId, model: deps.modelId });
|
|
88544
88846
|
if (!wasOpened && slateSession.opened) {
|
|
88545
|
-
const freshSlate = await
|
|
88847
|
+
const freshSlate = await readSlateSession(slateSession);
|
|
88546
88848
|
if (freshSlate !== undefined) {
|
|
88547
88849
|
history.push({ role: "user", content: renderAnchorsBlock(freshSlate.anchors), provenance: "project" });
|
|
88548
88850
|
io.onHistoryChange?.("tool");
|
|
@@ -88550,17 +88852,17 @@ async function runGoalCommand(params) {
|
|
|
88550
88852
|
}
|
|
88551
88853
|
if (parsed.workspaceId !== undefined) {
|
|
88552
88854
|
const workspaceId = parsed.workspaceId;
|
|
88553
|
-
await
|
|
88855
|
+
await writeSlateSession(slateSession, (prev) => {
|
|
88554
88856
|
const base = prev ?? { anchors: { root: "", touched: [] }, course: {}, seeds: [] };
|
|
88555
88857
|
return { ...base, workspaceId };
|
|
88556
88858
|
});
|
|
88557
88859
|
}
|
|
88558
88860
|
if (parsed.auto !== undefined) {
|
|
88559
|
-
const forCourse = await
|
|
88861
|
+
const forCourse = await readSlateSession(slateSession);
|
|
88560
88862
|
let flowRefForBinding = forCourse?.course.flowRef;
|
|
88561
88863
|
if (forCourse !== undefined && forCourse.course.flowRef === undefined) {
|
|
88562
88864
|
const flowId = await autoProvisionFlow(cwd, parsed.text);
|
|
88563
|
-
await
|
|
88865
|
+
await writeSlateSession(slateSession, (prev) => {
|
|
88564
88866
|
if (!prev)
|
|
88565
88867
|
throw new Error(`SLATE-27 bind: no open slate in ${slateSession.dir}`);
|
|
88566
88868
|
return { ...prev, course: { ...prev.course, flowRef: flowId } };
|
|
@@ -88583,7 +88885,7 @@ async function runGoalCommand(params) {
|
|
|
88583
88885
|
delete slateSession.autoGoalRounds;
|
|
88584
88886
|
let roundsLeft = roundsCap;
|
|
88585
88887
|
let round = 1;
|
|
88586
|
-
while (roundsLeft > 0 && slateSession.opened) {
|
|
88888
|
+
while (roundsLeft > 0 && slateSession.opened && !isSlateSessionDetached(slateSession)) {
|
|
88587
88889
|
roundsLeft -= 1;
|
|
88588
88890
|
round += 1;
|
|
88589
88891
|
const continuationText = await buildContinuationMessage(cwd, slateSession, round, roundsCap);
|
|
@@ -88596,8 +88898,18 @@ async function runGoalCommand(params) {
|
|
|
88596
88898
|
break;
|
|
88597
88899
|
}
|
|
88598
88900
|
}
|
|
88901
|
+
if (isSlateSessionDetached(slateSession)) {
|
|
88902
|
+
systemLine(io, `/goal --auto: stopped \u2014 another shell took this session over.
|
|
88903
|
+
`);
|
|
88904
|
+
return;
|
|
88905
|
+
}
|
|
88599
88906
|
const wasOpenBeforeVerifier = slateSession.opened;
|
|
88600
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
|
+
}
|
|
88601
88913
|
if (verdict === undefined) {
|
|
88602
88914
|
systemLine(io, `/goal --auto: verifier unavailable \u2014 outcome not independently checked.
|
|
88603
88915
|
`);
|
|
@@ -88612,12 +88924,12 @@ async function runGoalCommand(params) {
|
|
|
88612
88924
|
if (!wasOpenBeforeVerifier) {
|
|
88613
88925
|
try {
|
|
88614
88926
|
await ensureSlateOpened(slateSession, mintAttemptId, { provider: deps.providerId, model: deps.modelId });
|
|
88615
|
-
const reopened = await
|
|
88927
|
+
const reopened = await readSlateSession(slateSession);
|
|
88616
88928
|
if (reopened !== undefined) {
|
|
88617
88929
|
history.push({ role: "user", content: renderAnchorsBlock(reopened.anchors), provenance: "project" });
|
|
88618
88930
|
io.onHistoryChange?.("tool");
|
|
88619
88931
|
}
|
|
88620
|
-
await
|
|
88932
|
+
await writeSlateSession(slateSession, (prev) => {
|
|
88621
88933
|
if (!prev)
|
|
88622
88934
|
throw new Error(`SLATE-27 verifier-reopen: no open slate in ${slateSession.dir}`);
|
|
88623
88935
|
return {
|
|
@@ -88647,7 +88959,7 @@ async function runGoalCommand(params) {
|
|
|
88647
88959
|
|
|
88648
88960
|
// src/tui/tui-shell.ts
|
|
88649
88961
|
init_metaproject_adapter();
|
|
88650
|
-
import { spawnSync as
|
|
88962
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
88651
88963
|
|
|
88652
88964
|
// src/tui/transcript-blocks.ts
|
|
88653
88965
|
var EVICTED_BLOCK_TEXT = "(output no longer retained)";
|
|
@@ -94649,6 +94961,463 @@ function createForegroundAgentIoFacade(owner, token, io) {
|
|
|
94649
94961
|
};
|
|
94650
94962
|
}
|
|
94651
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
|
+
|
|
94652
95421
|
// src/tui/herdr-report.ts
|
|
94653
95422
|
init_debug_log();
|
|
94654
95423
|
import net from "net";
|
|
@@ -95027,7 +95796,7 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
95027
95796
|
// src/lib/version-check.ts
|
|
95028
95797
|
init_config_dir();
|
|
95029
95798
|
init_fs();
|
|
95030
|
-
import
|
|
95799
|
+
import path195 from "path";
|
|
95031
95800
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
95032
95801
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
95033
95802
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -95229,7 +95998,7 @@ async function checkVersion(options) {
|
|
|
95229
95998
|
const now = options.now ?? Date.now;
|
|
95230
95999
|
const timestamp = now();
|
|
95231
96000
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
95232
|
-
const cacheFile =
|
|
96001
|
+
const cacheFile = path195.join(configDir, "version-check.json");
|
|
95233
96002
|
const cache = parseCache(cacheFile);
|
|
95234
96003
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
95235
96004
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -95302,9 +96071,39 @@ init_debug_log();
|
|
|
95302
96071
|
|
|
95303
96072
|
// src/tui/renderer-debug.ts
|
|
95304
96073
|
init_debug_log();
|
|
96074
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
96075
|
+
import { readFileSync as readFileSync10, readlinkSync as readlinkSync2 } from "fs";
|
|
95305
96076
|
|
|
95306
96077
|
// src/tui/stdin-guard.ts
|
|
95307
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
|
+
}
|
|
95308
96107
|
var rendererListener;
|
|
95309
96108
|
function stdinSnapshot(stdin) {
|
|
95310
96109
|
const safe = (read) => {
|
|
@@ -95409,35 +96208,37 @@ function instrumentStdin(stdin) {
|
|
|
95409
96208
|
stdin.off?.(event, handler);
|
|
95410
96209
|
};
|
|
95411
96210
|
}
|
|
95412
|
-
function startStdinHeartbeat(
|
|
96211
|
+
function startStdinHeartbeat(source, opts = {}) {
|
|
95413
96212
|
const intervalMs = opts.intervalMs ?? 1000;
|
|
95414
96213
|
const cooldownMs = opts.cooldownMs ?? 1e4;
|
|
95415
96214
|
let lastRecovery = 0;
|
|
95416
96215
|
const timer = setInterval(() => {
|
|
96216
|
+
const stdin = resolveStdin(source);
|
|
95417
96217
|
const state = stdinSnapshot(stdin);
|
|
95418
96218
|
const stalled = stdinLooksStalled(state);
|
|
95419
96219
|
debugEvent("heartbeat", { stdin: state, stalled, ...opts.extra?.() ?? {} });
|
|
95420
96220
|
if (stalled && (opts.shouldRecover?.() ?? true) && Date.now() - lastRecovery > cooldownMs) {
|
|
95421
96221
|
lastRecovery = Date.now();
|
|
95422
|
-
const actions = recoverStdin(stdin, { cycle: true });
|
|
95423
|
-
debugEvent("stdin.stall.recovered-in-process", { before: state, actions, after: stdinSnapshot(
|
|
96222
|
+
const actions = opts.recover?.() ?? recoverStdin(stdin, { cycle: true });
|
|
96223
|
+
debugEvent("stdin.stall.recovered-in-process", { before: state, actions, after: stdinSnapshot(resolveStdin(source)) });
|
|
95424
96224
|
}
|
|
95425
96225
|
}, intervalMs);
|
|
95426
96226
|
timer.unref?.();
|
|
95427
96227
|
return () => clearInterval(timer);
|
|
95428
96228
|
}
|
|
95429
|
-
function installInputRecoverySignal(
|
|
96229
|
+
function installInputRecoverySignal(source, onRecovered, shouldRecover = () => true, recover) {
|
|
95430
96230
|
if (process.platform === "win32") {
|
|
95431
96231
|
return () => {};
|
|
95432
96232
|
}
|
|
95433
96233
|
const handler = () => {
|
|
96234
|
+
const stdin = resolveStdin(source);
|
|
95434
96235
|
if (!shouldRecover()) {
|
|
95435
96236
|
debugEvent("signal.SIGUSR2.skipped", { reason: "renderer suspended or destroyed", stdin: stdinSnapshot(stdin) });
|
|
95436
96237
|
return;
|
|
95437
96238
|
}
|
|
95438
96239
|
const before = stdinSnapshot(stdin);
|
|
95439
|
-
const actions = recoverStdin(stdin, { cycle: true });
|
|
95440
|
-
const after = stdinSnapshot(
|
|
96240
|
+
const actions = recover?.() ?? recoverStdin(stdin, { cycle: true });
|
|
96241
|
+
const after = stdinSnapshot(resolveStdin(source));
|
|
95441
96242
|
debugEvent("signal.SIGUSR2.recover", { before, actions, after, debug: isDebugEnabled() });
|
|
95442
96243
|
try {
|
|
95443
96244
|
onRecovered?.(actions);
|
|
@@ -95450,15 +96251,110 @@ function installInputRecoverySignal(stdin, onRecovered, shouldRecover = () => tr
|
|
|
95450
96251
|
}
|
|
95451
96252
|
|
|
95452
96253
|
// src/tui/renderer-debug.ts
|
|
95453
|
-
|
|
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 = {}) {
|
|
95454
96276
|
const internals = r;
|
|
95455
|
-
|
|
96277
|
+
const listener = internals.stdinListener;
|
|
96278
|
+
registerRendererInputListener(listener);
|
|
95456
96279
|
const shouldRecover = () => !r.isDestroyed && internals.controlState !== "explicit_suspended";
|
|
95457
96280
|
const teardown = [];
|
|
95458
|
-
|
|
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, () => {
|
|
95459
96348
|
r.requestRender();
|
|
95460
|
-
}, shouldRecover));
|
|
95461
|
-
teardown.push(() =>
|
|
96349
|
+
}, shouldRecover, recover));
|
|
96350
|
+
teardown.push(() => {
|
|
96351
|
+
unwatch();
|
|
96352
|
+
undoInstrument();
|
|
96353
|
+
registerRendererInputListener(undefined);
|
|
96354
|
+
if (current !== initial) {
|
|
96355
|
+
current.destroy?.();
|
|
96356
|
+
}
|
|
96357
|
+
});
|
|
95462
96358
|
if (!isDebugEnabled()) {
|
|
95463
96359
|
return () => {
|
|
95464
96360
|
for (const fn of teardown.splice(0))
|
|
@@ -95471,9 +96367,10 @@ function attachRendererGuards(r, stdin = process.stdin) {
|
|
|
95471
96367
|
height: r.height,
|
|
95472
96368
|
hasStdinListener: internals.stdinListener !== undefined
|
|
95473
96369
|
});
|
|
95474
|
-
|
|
95475
|
-
teardown.push(startStdinHeartbeat(
|
|
96370
|
+
undoInstrument = instrumentStdin(current);
|
|
96371
|
+
teardown.push(startStdinHeartbeat(() => current, {
|
|
95476
96372
|
shouldRecover,
|
|
96373
|
+
recover,
|
|
95477
96374
|
extra: () => ({
|
|
95478
96375
|
controlState: internals.controlState,
|
|
95479
96376
|
focused: internals.currentFocusedRenderable?.id
|
|
@@ -98425,7 +99322,7 @@ function parseGitHubRemote(remote) {
|
|
|
98425
99322
|
}
|
|
98426
99323
|
function runGitText(args, cwd) {
|
|
98427
99324
|
try {
|
|
98428
|
-
const proc =
|
|
99325
|
+
const proc = spawnSync3("git", args, {
|
|
98429
99326
|
cwd,
|
|
98430
99327
|
encoding: "utf8",
|
|
98431
99328
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -98442,7 +99339,7 @@ function runGitText(args, cwd) {
|
|
|
98442
99339
|
}
|
|
98443
99340
|
function runGhJson(repo, branch, cwd) {
|
|
98444
99341
|
try {
|
|
98445
|
-
const proc =
|
|
99342
|
+
const proc = spawnSync3("gh", ["pr", "view", branch, "--repo", repo, "--json", "url"], {
|
|
98446
99343
|
cwd,
|
|
98447
99344
|
encoding: "utf8",
|
|
98448
99345
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -98925,8 +99822,8 @@ async function applyRuntimeSwitchToSlate(params) {
|
|
|
98925
99822
|
if (params.slateSession === undefined || !params.slateSession.opened) {
|
|
98926
99823
|
return false;
|
|
98927
99824
|
}
|
|
98928
|
-
const result = await
|
|
98929
|
-
if (!result.changed) {
|
|
99825
|
+
const result = await recordSlateSessionTouch(params.slateSession, [], { runtime: params.runtime });
|
|
99826
|
+
if (result === undefined || !result.changed) {
|
|
98930
99827
|
return false;
|
|
98931
99828
|
}
|
|
98932
99829
|
params.history.push({
|
|
@@ -99896,21 +100793,40 @@ function formatSessionDate(iso) {
|
|
|
99896
100793
|
const short = iso.trim().replace("T", " ");
|
|
99897
100794
|
return short.length >= 16 ? short.slice(0, 16) : short;
|
|
99898
100795
|
}
|
|
99899
|
-
function
|
|
99900
|
-
|
|
99901
|
-
const r = chrome !== undefined ? chrome.renderer : rOrChrome;
|
|
99902
|
-
const all = sessions.map((s) => {
|
|
100796
|
+
function sessionPickerOptions(sessions, leaseState = () => "free") {
|
|
100797
|
+
return sessions.map((s) => {
|
|
99903
100798
|
const created = formatSessionDate(s.createdAt);
|
|
99904
100799
|
const updated = formatSessionDate(s.updatedAt);
|
|
99905
100800
|
const short = shortSessionId(s.id);
|
|
99906
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));
|
|
99907
100803
|
return {
|
|
99908
100804
|
value: s.id,
|
|
99909
|
-
label
|
|
100805
|
+
label,
|
|
99910
100806
|
description: `${s.projectPath} \xB7 created ${created} \xB7 updated ${updated}`,
|
|
99911
|
-
search: `${s.id} ${
|
|
100807
|
+
search: `${s.id} ${label} ${s.projectPath} ${s.title} ${created} ${updated}`.toLowerCase()
|
|
99912
100808
|
};
|
|
99913
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);
|
|
99914
100830
|
const listSpec = {
|
|
99915
100831
|
idPrefix: "sp",
|
|
99916
100832
|
items: all,
|
|
@@ -100017,11 +100933,13 @@ async function launchTuiAgentShell(opts) {
|
|
|
100017
100933
|
let liveJobs;
|
|
100018
100934
|
let detachExternal;
|
|
100019
100935
|
const foregroundOperation = createForegroundOperationOwner();
|
|
100936
|
+
const sessionLease = createTuiLeaseHolder();
|
|
100020
100937
|
try {
|
|
100021
100938
|
const r = renderer = await createShellRenderer(otui, {
|
|
100022
100939
|
onDestroy: () => {
|
|
100023
100940
|
foregroundOperation.cancel("renderer destroyed");
|
|
100024
100941
|
foregroundOperation.dispose();
|
|
100942
|
+
sessionLease.release();
|
|
100025
100943
|
mountedChrome?.destroy();
|
|
100026
100944
|
setAskUserHost(undefined);
|
|
100027
100945
|
setSubagentFleetListener(undefined);
|
|
@@ -100054,7 +100972,8 @@ async function launchTuiAgentShell(opts) {
|
|
|
100054
100972
|
saveShellConfig(sel.baseUrl === undefined ? { provider: sel.provider, model: sel.model } : { provider: sel.provider, model: sel.model, baseUrl: sel.baseUrl });
|
|
100055
100973
|
let currentSel = sel;
|
|
100056
100974
|
const suggestionGate = new NextStepSuggestionGate;
|
|
100057
|
-
|
|
100975
|
+
const liveSlateSession = () => whilePersisting(slateSession, () => sessionLease.canPersist());
|
|
100976
|
+
let deps = await opts.makeAgentDeps(sel, liveSlateSession);
|
|
100058
100977
|
liveDeps = deps;
|
|
100059
100978
|
let reasoningOverride;
|
|
100060
100979
|
let thinkDisplayMode = resolveThinkDisplayMode(loadShellConfig().thinkDisplay);
|
|
@@ -100127,7 +101046,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
100127
101046
|
}));
|
|
100128
101047
|
};
|
|
100129
101048
|
const refreshWorkspaceSidebar = async () => {
|
|
100130
|
-
const dir =
|
|
101049
|
+
const dir = liveSlateSession()?.dir;
|
|
100131
101050
|
const workspaceId = dir !== undefined ? (await readSlate(dir).catch(() => {
|
|
100132
101051
|
return;
|
|
100133
101052
|
}))?.workspaceId : undefined;
|
|
@@ -100753,6 +101672,9 @@ async function launchTuiAgentShell(opts) {
|
|
|
100753
101672
|
slateSession = freshSlateSessionRef(liveSession.dir, sessionCwd);
|
|
100754
101673
|
};
|
|
100755
101674
|
const onContextCompaction = (r) => {
|
|
101675
|
+
if (!sessionLease.canPersist()) {
|
|
101676
|
+
return;
|
|
101677
|
+
}
|
|
100756
101678
|
const persisted = persistCompacted(liveSession, r.context, archive, {
|
|
100757
101679
|
provider: currentSel.provider,
|
|
100758
101680
|
model: currentSel.model
|
|
@@ -100784,7 +101706,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
100784
101706
|
return;
|
|
100785
101707
|
}
|
|
100786
101708
|
chrome.hideMenu();
|
|
100787
|
-
const pickId = await chrome.withOverlay(() => pickSessionInTui(otui, chrome, rows));
|
|
101709
|
+
const pickId = await chrome.withOverlay(() => pickSessionInTui(otui, chrome, rows, leaseState));
|
|
100788
101710
|
input.focus();
|
|
100789
101711
|
if (pickId === undefined) {
|
|
100790
101712
|
return;
|
|
@@ -100797,98 +101719,142 @@ async function launchTuiAgentShell(opts) {
|
|
|
100797
101719
|
}
|
|
100798
101720
|
return found;
|
|
100799
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;
|
|
100800
101761
|
try {
|
|
100801
101762
|
if (opts.session?.pickOnStart === true && opts.session.resumeId === undefined) {
|
|
100802
101763
|
const rows = listSessions(sessionCwd).slice(0, 12);
|
|
100803
101764
|
if (rows.length === 0) {
|
|
100804
|
-
applyOpened(
|
|
100805
|
-
cwd: sessionCwd,
|
|
100806
|
-
provider: currentSel.provider,
|
|
100807
|
-
model: currentSel.model
|
|
100808
|
-
}));
|
|
101765
|
+
applyOpened(leasedOpen());
|
|
100809
101766
|
} else {
|
|
100810
101767
|
chrome.hideMenu();
|
|
100811
101768
|
const pickId = await showComposerChoice(otui, r, chrome.dock, {
|
|
100812
101769
|
title: "Resume session (this project)",
|
|
100813
101770
|
subtitle: "Esc = new session",
|
|
100814
101771
|
cancelId: "__new__",
|
|
100815
|
-
options:
|
|
100816
|
-
{
|
|
100817
|
-
id: "__new__",
|
|
100818
|
-
label: "New session",
|
|
100819
|
-
description: "Start fresh (old sessions stay on disk)",
|
|
100820
|
-
recommended: true
|
|
100821
|
-
},
|
|
100822
|
-
...rows.map((s) => ({
|
|
100823
|
-
id: s.id,
|
|
100824
|
-
label: s.title.length > 40 ? `${s.title.slice(0, 37)}\u2026` : s.title,
|
|
100825
|
-
description: `${shortSessionId(s.id)} \xB7 ctx ${s.messageCount} \xB7 ${s.updatedAt.slice(0, 16).replace("T", " ")}`
|
|
100826
|
-
}))
|
|
100827
|
-
]
|
|
101772
|
+
options: startupSessionChoices(rows, leaseState)
|
|
100828
101773
|
});
|
|
100829
101774
|
input.focus();
|
|
100830
101775
|
if (pickId === "__new__") {
|
|
100831
|
-
applyOpened(
|
|
100832
|
-
cwd: sessionCwd,
|
|
100833
|
-
provider: currentSel.provider,
|
|
100834
|
-
model: currentSel.model
|
|
100835
|
-
}));
|
|
101776
|
+
applyOpened(leasedOpen());
|
|
100836
101777
|
} else {
|
|
100837
|
-
|
|
100838
|
-
|
|
100839
|
-
|
|
100840
|
-
provider: currentSel.provider,
|
|
100841
|
-
model: currentSel.model
|
|
100842
|
-
}));
|
|
101778
|
+
const opened = leasedOpen({ resumeId: pickId });
|
|
101779
|
+
applyOpened(opened);
|
|
101780
|
+
showOpenedNotes(opened);
|
|
100843
101781
|
}
|
|
100844
101782
|
}
|
|
100845
101783
|
} else {
|
|
100846
|
-
const opened =
|
|
100847
|
-
cwd: sessionCwd,
|
|
101784
|
+
const opened = leasedOpen({
|
|
100848
101785
|
...opts.session?.continueLast === true ? { continueLast: true } : {},
|
|
100849
101786
|
...opts.session?.resumeId !== undefined ? { resumeId: opts.session.resumeId } : {},
|
|
100850
|
-
|
|
100851
|
-
|
|
101787
|
+
...opts.session?.fork === true ? { fork: true } : {},
|
|
101788
|
+
...opts.session?.takeOver === true ? { takeOver: true } : {}
|
|
100852
101789
|
});
|
|
100853
101790
|
applyOpened(opened);
|
|
100854
|
-
|
|
100855
|
-
|
|
100856
|
-
|
|
100857
|
-
|
|
100858
|
-
|
|
100859
|
-
|
|
100860
|
-
|
|
100861
|
-
|
|
100862
|
-
|
|
100863
|
-
|
|
100864
|
-
|
|
100865
|
-
|
|
100866
|
-
|
|
100867
|
-
|
|
100868
|
-
|
|
100869
|
-
|
|
100870
|
-
|
|
100871
|
-
|
|
100872
|
-
|
|
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);
|
|
100873
101829
|
}
|
|
101830
|
+
failure = undefined;
|
|
101831
|
+
} catch (leaseCause) {
|
|
101832
|
+
failure = leaseCause;
|
|
100874
101833
|
}
|
|
100875
101834
|
}
|
|
100876
|
-
|
|
100877
|
-
|
|
100878
|
-
|
|
100879
|
-
|
|
100880
|
-
|
|
100881
|
-
|
|
100882
|
-
|
|
100883
|
-
|
|
100884
|
-
|
|
100885
|
-
|
|
100886
|
-
|
|
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;
|
|
100887
101849
|
}
|
|
100888
101850
|
bindSlateToLiveSession();
|
|
100889
101851
|
if (history.length === 0) {
|
|
100890
101852
|
removeSplash = mountEmptyTranscriptSplash(otui, r, transcript);
|
|
100891
101853
|
}
|
|
101854
|
+
if (viewedReadOnly) {
|
|
101855
|
+
removeSplash?.();
|
|
101856
|
+
removeSplash = undefined;
|
|
101857
|
+
}
|
|
100892
101858
|
refreshWorkspaceSidebar();
|
|
100893
101859
|
refreshReviewSidebar();
|
|
100894
101860
|
const paintSessionHeader = () => {
|
|
@@ -100898,7 +101864,19 @@ async function launchTuiAgentShell(opts) {
|
|
|
100898
101864
|
const cx = liveSession.summary.compactCount > 0 ? ` \xB7 c\xD7${liveSession.summary.compactCount}` : "";
|
|
100899
101865
|
chrome.setTitle(`keryx \xB7 ${title} \xB7 ${sid}${cx} \xB7 ${label}`);
|
|
100900
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
|
+
});
|
|
100901
101876
|
const saveSession = () => {
|
|
101877
|
+
if (!sessionLease.canPersist()) {
|
|
101878
|
+
return;
|
|
101879
|
+
}
|
|
100902
101880
|
liveSession = persistHistory(liveSession, history, {
|
|
100903
101881
|
archive,
|
|
100904
101882
|
provider: currentSel.provider,
|
|
@@ -100951,12 +101929,17 @@ async function launchTuiAgentShell(opts) {
|
|
|
100951
101929
|
usage.resetUsage();
|
|
100952
101930
|
};
|
|
100953
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
|
+
}
|
|
100954
101941
|
resetSessionSurface();
|
|
100955
|
-
liveSession =
|
|
100956
|
-
cwd: sessionCwd,
|
|
100957
|
-
provider: currentSel.provider,
|
|
100958
|
-
model: currentSel.model
|
|
100959
|
-
});
|
|
101942
|
+
liveSession = opened.handle;
|
|
100960
101943
|
history = [];
|
|
100961
101944
|
archive = [];
|
|
100962
101945
|
nextArchiveIndex = 0;
|
|
@@ -100965,6 +101948,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
100965
101948
|
io.onSystem?.(`${note}
|
|
100966
101949
|
`);
|
|
100967
101950
|
}
|
|
101951
|
+
return true;
|
|
100968
101952
|
};
|
|
100969
101953
|
const resumeSessionInteractive = async () => {
|
|
100970
101954
|
const found = await pickRecentSession();
|
|
@@ -100973,15 +101957,12 @@ async function launchTuiAgentShell(opts) {
|
|
|
100973
101957
|
}
|
|
100974
101958
|
let opened;
|
|
100975
101959
|
try {
|
|
100976
|
-
opened =
|
|
100977
|
-
cwd: sessionCwd,
|
|
100978
|
-
resumeId: found.id,
|
|
100979
|
-
provider: currentSel.provider,
|
|
100980
|
-
model: currentSel.model
|
|
100981
|
-
});
|
|
101960
|
+
opened = leasedOpen({ resumeId: found.id });
|
|
100982
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
|
+
` : "";
|
|
100983
101964
|
io.onSystem?.(`Could not resume ${shortSessionId(found.id)}: ${cause instanceof Error ? cause.message : String(cause)}
|
|
100984
|
-
Staying in the current session.
|
|
101965
|
+
` + hint + `Staying in the current session.
|
|
100985
101966
|
`);
|
|
100986
101967
|
return;
|
|
100987
101968
|
}
|
|
@@ -101049,7 +102030,7 @@ Staying in the current session.
|
|
|
101049
102030
|
};
|
|
101050
102031
|
const showWorkspace = () => {
|
|
101051
102032
|
(async () => {
|
|
101052
|
-
const dir =
|
|
102033
|
+
const dir = liveSlateSession()?.dir;
|
|
101053
102034
|
const workspaceId = dir !== undefined ? (await readSlate(dir).catch(() => {
|
|
101054
102035
|
return;
|
|
101055
102036
|
}))?.workspaceId : undefined;
|
|
@@ -101268,7 +102249,7 @@ Staying in the current session.
|
|
|
101268
102249
|
};
|
|
101269
102250
|
const switchTo = async (ns) => {
|
|
101270
102251
|
currentSel = ns;
|
|
101271
|
-
deps = { ...await opts.makeAgentDeps(ns,
|
|
102252
|
+
deps = { ...await opts.makeAgentDeps(ns, liveSlateSession), onContextCompaction };
|
|
101272
102253
|
liveDeps = deps;
|
|
101273
102254
|
saveShellConfig(ns.baseUrl === undefined ? { provider: ns.provider, model: ns.model } : { provider: ns.provider, model: ns.model, baseUrl: ns.baseUrl });
|
|
101274
102255
|
updateModelLabels();
|
|
@@ -101556,7 +102537,7 @@ Staying in the current session.
|
|
|
101556
102537
|
});
|
|
101557
102538
|
let answer = "";
|
|
101558
102539
|
try {
|
|
101559
|
-
const base = await opts.makeAgentDeps(currentSel,
|
|
102540
|
+
const base = await opts.makeAgentDeps(currentSel, liveSlateSession);
|
|
101560
102541
|
const tools = base.tools.filter((t) => t.definition.risk === "read" && !SIDE_WORKER_DENIED_TOOL_NAMES.has(t.definition.name));
|
|
101561
102542
|
const sideDeps = {
|
|
101562
102543
|
...base,
|
|
@@ -101673,6 +102654,7 @@ Staying in the current session.
|
|
|
101673
102654
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
101674
102655
|
await deps.sweepBackgroundJobs?.();
|
|
101675
102656
|
jobs.removeAll();
|
|
102657
|
+
sessionLease.release();
|
|
101676
102658
|
r.off("theme_mode", onThemeMode);
|
|
101677
102659
|
r.destroy();
|
|
101678
102660
|
})();
|
|
@@ -101861,6 +102843,7 @@ Staying in the current session.
|
|
|
101861
102843
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
101862
102844
|
await deps.sweepBackgroundJobs?.();
|
|
101863
102845
|
jobs.removeAll();
|
|
102846
|
+
sessionLease.release();
|
|
101864
102847
|
r.off("theme_mode", onThemeMode);
|
|
101865
102848
|
r.destroy();
|
|
101866
102849
|
})();
|
|
@@ -101885,7 +102868,9 @@ Staying in the current session.
|
|
|
101885
102868
|
if (command.name === "/clear" || command.name === "/new") {
|
|
101886
102869
|
(async () => {
|
|
101887
102870
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
101888
|
-
startNewSession()
|
|
102871
|
+
if (!startNewSession()) {
|
|
102872
|
+
return;
|
|
102873
|
+
}
|
|
101889
102874
|
bindSlateToLiveSession();
|
|
101890
102875
|
refreshWorkspaceSidebar();
|
|
101891
102876
|
refreshReviewSidebar();
|
|
@@ -101916,6 +102901,10 @@ Staying in the current session.
|
|
|
101916
102901
|
return;
|
|
101917
102902
|
}
|
|
101918
102903
|
if (command.name === "/compact") {
|
|
102904
|
+
if (!sessionLease.canPersist()) {
|
|
102905
|
+
io.onSystem?.(LOST_LEASE_COMPACT_REFUSAL);
|
|
102906
|
+
return;
|
|
102907
|
+
}
|
|
101919
102908
|
const focus = line.trim().split(/\s+/).slice(1).join(" ").trim();
|
|
101920
102909
|
const packed = compactSession(liveSession, history, archive, {
|
|
101921
102910
|
keepLastUserTurns: 3,
|
|
@@ -102450,6 +103439,7 @@ ${tail}`,
|
|
|
102450
103439
|
} catch {
|
|
102451
103440
|
return false;
|
|
102452
103441
|
} finally {
|
|
103442
|
+
sessionLease.release();
|
|
102453
103443
|
await herdr.release();
|
|
102454
103444
|
try {
|
|
102455
103445
|
renderer?.destroy();
|
|
@@ -102850,6 +103840,7 @@ init_patch_risk();
|
|
|
102850
103840
|
init_permission_mode();
|
|
102851
103841
|
init_agent();
|
|
102852
103842
|
init_slate_lifecycle();
|
|
103843
|
+
init_slate_lifecycle();
|
|
102853
103844
|
function reportMcpProblems(runtime) {
|
|
102854
103845
|
for (const problem of runtime.problems()) {
|
|
102855
103846
|
console.error(`keryx mcp: ${problem.file} ${problem.message}`);
|
|
@@ -102896,6 +103887,132 @@ function readlineAgentHelpText() {
|
|
|
102896
103887
|
` + renderCommandHelp("agent", READLINE_AGENT_COMMANDS) + `Sessions are per-project: keryx shell -c | -r [id] | keryx sessions list
|
|
102897
103888
|
`;
|
|
102898
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
|
+
}
|
|
102899
104016
|
async function runShell(io, deps) {
|
|
102900
104017
|
let providerName = deps.initial.provider;
|
|
102901
104018
|
let modelName = deps.initial.model;
|
|
@@ -102914,17 +104031,22 @@ async function runShell(io, deps) {
|
|
|
102914
104031
|
let live;
|
|
102915
104032
|
let history = [];
|
|
102916
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;
|
|
102917
104043
|
if (sessionsOn) {
|
|
102918
104044
|
try {
|
|
102919
|
-
const
|
|
102920
|
-
|
|
102921
|
-
|
|
102922
|
-
|
|
102923
|
-
|
|
102924
|
-
...resumeId !== undefined ? { resumeId } : {},
|
|
102925
|
-
provider: providerName,
|
|
102926
|
-
model: modelName
|
|
102927
|
-
});
|
|
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
|
+
}
|
|
102928
104050
|
live = opened.handle;
|
|
102929
104051
|
history = opened.history;
|
|
102930
104052
|
archive = opened.archive.length > 0 ? [...opened.archive] : [...opened.history];
|
|
@@ -102937,7 +104059,12 @@ async function runShell(io, deps) {
|
|
|
102937
104059
|
`);
|
|
102938
104060
|
}
|
|
102939
104061
|
} catch (cause) {
|
|
102940
|
-
|
|
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 });
|
|
102941
104068
|
history = [];
|
|
102942
104069
|
archive = [];
|
|
102943
104070
|
system(`${cause instanceof Error ? cause.message : String(cause)}
|
|
@@ -102945,8 +104072,12 @@ Starting a new session.
|
|
|
102945
104072
|
`);
|
|
102946
104073
|
}
|
|
102947
104074
|
}
|
|
104075
|
+
const releaseLease = () => {
|
|
104076
|
+
releaseSessionLease(lease);
|
|
104077
|
+
holdLease(undefined);
|
|
104078
|
+
};
|
|
102948
104079
|
const save = () => {
|
|
102949
|
-
if (live === undefined) {
|
|
104080
|
+
if (live === undefined || !leaseWatch.canPersist()) {
|
|
102950
104081
|
return;
|
|
102951
104082
|
}
|
|
102952
104083
|
try {
|
|
@@ -102973,6 +104104,7 @@ Starting a new session.
|
|
|
102973
104104
|
const command = parts[0] ?? "";
|
|
102974
104105
|
const argument = parts.slice(1).join(" ");
|
|
102975
104106
|
if (command === "/exit" || command === "/quit") {
|
|
104107
|
+
releaseLease();
|
|
102976
104108
|
return;
|
|
102977
104109
|
}
|
|
102978
104110
|
if (command === "/help") {
|
|
@@ -103032,7 +104164,16 @@ ${formatThemeList(getThemeId())}`);
|
|
|
103032
104164
|
}
|
|
103033
104165
|
if (command === "/clear" || command === "/new") {
|
|
103034
104166
|
if (sessionsOn) {
|
|
103035
|
-
|
|
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
|
+
}
|
|
103036
104177
|
history = [];
|
|
103037
104178
|
archive = [];
|
|
103038
104179
|
system(`New session ${shortSessionId(live.summary.id)} (previous kept on disk)
|
|
@@ -103048,6 +104189,10 @@ ${formatThemeList(getThemeId())}`);
|
|
|
103048
104189
|
`);
|
|
103049
104190
|
continue;
|
|
103050
104191
|
}
|
|
104192
|
+
if (!leaseWatch.canPersist()) {
|
|
104193
|
+
system(LOST_LEASE_COMPACT_REFUSAL);
|
|
104194
|
+
continue;
|
|
104195
|
+
}
|
|
103051
104196
|
const focus = argument.trim();
|
|
103052
104197
|
const packed = compactSession(live, history, archive, {
|
|
103053
104198
|
keepLastUserTurns: 3,
|
|
@@ -103183,6 +104328,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
103183
104328
|
|
|
103184
104329
|
`);
|
|
103185
104330
|
}
|
|
104331
|
+
releaseLease();
|
|
103186
104332
|
}
|
|
103187
104333
|
function realMakeProvider(write) {
|
|
103188
104334
|
return (name, model, baseUrl) => {
|
|
@@ -103799,7 +104945,7 @@ ${text.trim()}`;
|
|
|
103799
104945
|
deps = {
|
|
103800
104946
|
...deps,
|
|
103801
104947
|
onContextCompaction: (r) => {
|
|
103802
|
-
if (live === undefined) {
|
|
104948
|
+
if (live === undefined || !leaseWatch.canPersist()) {
|
|
103803
104949
|
return;
|
|
103804
104950
|
}
|
|
103805
104951
|
const persisted = persistCompacted(live, r.context, archive, {
|
|
@@ -103812,17 +104958,44 @@ ${text.trim()}`;
|
|
|
103812
104958
|
`);
|
|
103813
104959
|
}
|
|
103814
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
|
+
};
|
|
103815
104992
|
if (sessionsOn) {
|
|
103816
104993
|
try {
|
|
103817
|
-
const
|
|
103818
|
-
|
|
103819
|
-
|
|
103820
|
-
|
|
103821
|
-
|
|
103822
|
-
...resumeId !== undefined ? { resumeId } : {},
|
|
103823
|
-
provider: deps.providerId,
|
|
103824
|
-
model: deps.modelId
|
|
103825
|
-
});
|
|
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
|
+
}
|
|
103826
104999
|
live = opened.handle;
|
|
103827
105000
|
history = opened.history;
|
|
103828
105001
|
archive = opened.archive.length > 0 ? [...opened.archive] : [...opened.history];
|
|
@@ -103835,7 +105008,12 @@ ${text.trim()}`;
|
|
|
103835
105008
|
`);
|
|
103836
105009
|
}
|
|
103837
105010
|
} catch (cause) {
|
|
103838
|
-
|
|
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({
|
|
103839
105017
|
cwd: sessionCwd,
|
|
103840
105018
|
provider: deps.providerId,
|
|
103841
105019
|
model: deps.modelId
|
|
@@ -103851,7 +105029,7 @@ New session ${shortSessionId(live.summary.id)}.
|
|
|
103851
105029
|
slateSession = live !== undefined ? { dir: live.dir, cwd: sessionCwd, opened: false } : undefined;
|
|
103852
105030
|
slateSessionBox.current = slateSession;
|
|
103853
105031
|
const save = () => {
|
|
103854
|
-
if (live === undefined) {
|
|
105032
|
+
if (live === undefined || !leaseWatch.canPersist()) {
|
|
103855
105033
|
return;
|
|
103856
105034
|
}
|
|
103857
105035
|
try {
|
|
@@ -103927,6 +105105,7 @@ ${GUTTER}${turnSeparator()}
|
|
|
103927
105105
|
if (line === undefined) {
|
|
103928
105106
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
103929
105107
|
await deps.sweepBackgroundJobs?.();
|
|
105108
|
+
releaseLease();
|
|
103930
105109
|
return;
|
|
103931
105110
|
}
|
|
103932
105111
|
rich.safeBoundary?.();
|
|
@@ -103937,6 +105116,7 @@ ${GUTTER}${turnSeparator()}
|
|
|
103937
105116
|
if (command === "/exit" || command === "/quit") {
|
|
103938
105117
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
103939
105118
|
await deps.sweepBackgroundJobs?.();
|
|
105119
|
+
releaseLease();
|
|
103940
105120
|
return;
|
|
103941
105121
|
}
|
|
103942
105122
|
if (command === "/help") {
|
|
@@ -103981,13 +105161,14 @@ ${GUTTER}${turnSeparator()}
|
|
|
103981
105161
|
`);
|
|
103982
105162
|
}
|
|
103983
105163
|
} else if (command === "/new" || command === "/clear") {
|
|
105164
|
+
const nextLive = sessionsOn ? switchNewSession() : undefined;
|
|
105165
|
+
if (nextLive === false) {
|
|
105166
|
+
rich.printPrompt();
|
|
105167
|
+
continue;
|
|
105168
|
+
}
|
|
103984
105169
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
103985
|
-
if (
|
|
103986
|
-
live =
|
|
103987
|
-
cwd: sessionCwd,
|
|
103988
|
-
provider: deps.providerId,
|
|
103989
|
-
model: deps.modelId
|
|
103990
|
-
});
|
|
105170
|
+
if (nextLive !== undefined) {
|
|
105171
|
+
live = nextLive;
|
|
103991
105172
|
history = [];
|
|
103992
105173
|
archive = [];
|
|
103993
105174
|
nextArchiveIndex = 0;
|
|
@@ -104005,6 +105186,8 @@ ${GUTTER}${turnSeparator()}
|
|
|
104005
105186
|
if (live === undefined) {
|
|
104006
105187
|
agentIo.onSystem?.(`No persistent session.
|
|
104007
105188
|
`);
|
|
105189
|
+
} else if (!leaseWatch.canPersist()) {
|
|
105190
|
+
agentIo.onSystem?.(LOST_LEASE_COMPACT_REFUSAL);
|
|
104008
105191
|
} else {
|
|
104009
105192
|
const packed = compactSession(live, history, archive, {
|
|
104010
105193
|
keepLastUserTurns: 3,
|
|
@@ -104291,6 +105474,14 @@ async function resolveTuiStartup(opts) {
|
|
|
104291
105474
|
appliedKeys
|
|
104292
105475
|
};
|
|
104293
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
|
+
}
|
|
104294
105485
|
function parseShellCliFlags(args) {
|
|
104295
105486
|
if (args.includes("--help") || args.includes("-h")) {
|
|
104296
105487
|
return { help: true, wantTui: true };
|
|
@@ -104313,6 +105504,8 @@ function parseShellCliFlags(args) {
|
|
|
104313
105504
|
let continueLast;
|
|
104314
105505
|
let resumeId;
|
|
104315
105506
|
let resumePick;
|
|
105507
|
+
let fork;
|
|
105508
|
+
let takeOver;
|
|
104316
105509
|
let permissionModeFlag;
|
|
104317
105510
|
let denyTools;
|
|
104318
105511
|
let printPromptArg;
|
|
@@ -104349,6 +105542,10 @@ function parseShellCliFlags(args) {
|
|
|
104349
105542
|
} else {
|
|
104350
105543
|
resumePick = true;
|
|
104351
105544
|
}
|
|
105545
|
+
} else if (arg === "--fork") {
|
|
105546
|
+
fork = true;
|
|
105547
|
+
} else if (arg === "--take-over") {
|
|
105548
|
+
takeOver = true;
|
|
104352
105549
|
} else if (arg === "--permission-mode") {
|
|
104353
105550
|
const next = valueAfter(i++);
|
|
104354
105551
|
if (!isPermissionMode(next))
|
|
@@ -104384,6 +105581,18 @@ function parseShellCliFlags(args) {
|
|
|
104384
105581
|
if (continueLast && (resumeId !== undefined || resumePick)) {
|
|
104385
105582
|
invalid("--continue and --resume cannot be combined");
|
|
104386
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
|
+
}
|
|
104387
105596
|
if (printPromptArg !== undefined && modeFlag === false) {
|
|
104388
105597
|
invalid("--print is agent-mode only and cannot be combined with --chat");
|
|
104389
105598
|
}
|
|
@@ -104396,6 +105605,8 @@ function parseShellCliFlags(args) {
|
|
|
104396
105605
|
...continueLast === true ? { continueLast: true } : {},
|
|
104397
105606
|
...resumeId !== undefined ? { resumeId } : {},
|
|
104398
105607
|
...resumePick === true ? { resumePick: true } : {},
|
|
105608
|
+
...fork === true ? { fork: true } : {},
|
|
105609
|
+
...takeOver === true ? { takeOver: true } : {},
|
|
104399
105610
|
...permissionModeFlag !== undefined ? { permissionModeFlag } : {},
|
|
104400
105611
|
...denyTools !== undefined ? { denyTools } : {},
|
|
104401
105612
|
...printPromptArg !== undefined ? { printPrompt: printPromptArg } : {},
|
|
@@ -104429,8 +105640,10 @@ async function shellCommand(args, runtime = {}) {
|
|
|
104429
105640
|
--base-url <url> Override the provider endpoint
|
|
104430
105641
|
--agent | --chat Choose agent (default) or chat mode
|
|
104431
105642
|
--tui | --no-tui Prefer TUI (default) or readline; last flag wins
|
|
104432
|
-
--continue, -c Continue the
|
|
105643
|
+
--continue, -c Continue the most recent session no other shell has open
|
|
104433
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
|
|
104434
105647
|
--permission-mode <mode> Agent permissions: ask, trust or auto
|
|
104435
105648
|
--deny-tools <a,b> Withhold these tools from the session entirely
|
|
104436
105649
|
--ask | --trust | --auto Permission shortcuts; last flag wins
|
|
@@ -104441,7 +105654,10 @@ async function shellCommand(args, runtime = {}) {
|
|
|
104441
105654
|
that re-arms terminal input if it stops; see debug/latest.txt
|
|
104442
105655
|
|
|
104443
105656
|
Session continuation and resume are mutually exclusive. Permission flags
|
|
104444
|
-
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.
|
|
104445
105661
|
|
|
104446
105662
|
A shell that stops reacting to keys can be recovered from another terminal
|
|
104447
105663
|
with: kill -USR2 <keryx pid>
|
|
@@ -104481,6 +105697,10 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104481
105697
|
const modelArg = flags.modelArg;
|
|
104482
105698
|
let baseUrl = flags.baseUrl;
|
|
104483
105699
|
let modeFlag = flags.modeFlag;
|
|
105700
|
+
const leaseFlagOpts = {
|
|
105701
|
+
...flags.fork === true ? { fork: true } : {},
|
|
105702
|
+
...flags.takeOver === true ? { takeOver: true } : {}
|
|
105703
|
+
};
|
|
104484
105704
|
{
|
|
104485
105705
|
await Promise.resolve().then(() => init_login());
|
|
104486
105706
|
const oauthFetch = (input, init) => globalThis.fetch(input, init);
|
|
@@ -104580,7 +105800,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104580
105800
|
sessionOverride: reasoningSessionOverride,
|
|
104581
105801
|
globalEffort: loadShellConfig(runtime.cacheDir).reasoningEffort
|
|
104582
105802
|
}),
|
|
104583
|
-
idSeq: () =>
|
|
105803
|
+
idSeq: () => randomUUID30(),
|
|
104584
105804
|
askUser: invokeAskUserHost,
|
|
104585
105805
|
sweepBackgroundJobs: () => jobRegistry.sweepAll(),
|
|
104586
105806
|
jobRegistry,
|
|
@@ -104611,29 +105831,40 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104611
105831
|
});
|
|
104612
105832
|
const tuiInitial = startup.initial;
|
|
104613
105833
|
const tuiDetected = startup.detected;
|
|
105834
|
+
const chatLeaseBox = { current: undefined };
|
|
104614
105835
|
try {
|
|
104615
105836
|
if (surface === "tui-chat") {
|
|
104616
105837
|
const chatFactory = realMakeProvider(() => {});
|
|
104617
105838
|
let chatResumeId = flags.resumeId;
|
|
105839
|
+
const chatNotes = [];
|
|
104618
105840
|
if (flags.resumePick === true && chatResumeId === undefined) {
|
|
104619
|
-
chatResumeId =
|
|
105841
|
+
chatResumeId = bareResumeTarget(cwd, (text) => {
|
|
105842
|
+
chatNotes.push(text);
|
|
105843
|
+
});
|
|
104620
105844
|
}
|
|
104621
105845
|
if (await (runtime.launchChat ?? launchTuiChatShell)({
|
|
104622
105846
|
detected: tuiDetected,
|
|
104623
105847
|
redetect,
|
|
104624
105848
|
...tuiInitial !== undefined ? { initial: tuiInitial } : {},
|
|
104625
|
-
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
|
+
},
|
|
104626
105855
|
makeShellDeps: (sel) => {
|
|
104627
105856
|
const resolvedModelParams = resolveProviderModelParamsByName(sel.provider, loadShellConfig(runtime.cacheDir), runtime.cacheDir);
|
|
104628
105857
|
return {
|
|
104629
105858
|
makeProvider: chatFactory,
|
|
104630
105859
|
clock: () => new Date().toISOString(),
|
|
104631
|
-
idSeq: () =>
|
|
105860
|
+
idSeq: () => randomUUID30(),
|
|
104632
105861
|
initial: Object.keys(resolvedModelParams).length > 0 ? { ...sel, modelParams: resolvedModelParams } : sel,
|
|
104633
105862
|
session: {
|
|
104634
105863
|
cwd,
|
|
104635
105864
|
...flags.continueLast === true ? { continueLast: true } : {},
|
|
104636
|
-
...chatResumeId !== undefined ? { resumeId: chatResumeId } : {}
|
|
105865
|
+
...chatResumeId !== undefined ? { resumeId: chatResumeId } : {},
|
|
105866
|
+
...leaseFlagOpts,
|
|
105867
|
+
leaseBox: chatLeaseBox
|
|
104637
105868
|
}
|
|
104638
105869
|
};
|
|
104639
105870
|
},
|
|
@@ -104653,7 +105884,8 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104653
105884
|
cwd,
|
|
104654
105885
|
...flags.continueLast === true ? { continueLast: true } : {},
|
|
104655
105886
|
...flags.resumeId !== undefined ? { resumeId: flags.resumeId } : {},
|
|
104656
|
-
...flags.resumePick === true ? { pickOnStart: true } : {}
|
|
105887
|
+
...flags.resumePick === true ? { pickOnStart: true } : {},
|
|
105888
|
+
...leaseFlagOpts
|
|
104657
105889
|
},
|
|
104658
105890
|
...flags.permissionModeFlag !== undefined ? { initialPermissionMode: flags.permissionModeFlag } : {},
|
|
104659
105891
|
versionCheck
|
|
@@ -104661,6 +105893,8 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104661
105893
|
return;
|
|
104662
105894
|
}
|
|
104663
105895
|
} finally {
|
|
105896
|
+
releaseSessionLease(chatLeaseBox.current);
|
|
105897
|
+
chatLeaseBox.current = undefined;
|
|
104664
105898
|
if (mcpRuntime !== undefined) {
|
|
104665
105899
|
await mcpRuntime.close();
|
|
104666
105900
|
reportMcpProblems(mcpRuntime);
|
|
@@ -104669,7 +105903,10 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104669
105903
|
}
|
|
104670
105904
|
const rl = readline2.createInterface({ input: process.stdin });
|
|
104671
105905
|
let readlineMcp;
|
|
105906
|
+
const leaseBox = { current: undefined };
|
|
104672
105907
|
const closeAndExit = (code) => () => {
|
|
105908
|
+
releaseSessionLease(leaseBox.current);
|
|
105909
|
+
leaseBox.current = undefined;
|
|
104673
105910
|
(async () => {
|
|
104674
105911
|
try {
|
|
104675
105912
|
await readlineMcp?.close();
|
|
@@ -104688,6 +105925,27 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104688
105925
|
}
|
|
104689
105926
|
};
|
|
104690
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
|
+
};
|
|
104691
105949
|
let provider;
|
|
104692
105950
|
let model;
|
|
104693
105951
|
try {
|
|
@@ -104727,7 +105985,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104727
105985
|
const deps = {
|
|
104728
105986
|
makeProvider: baseFactory,
|
|
104729
105987
|
clock: () => new Date().toISOString(),
|
|
104730
|
-
idSeq: () =>
|
|
105988
|
+
idSeq: () => randomUUID30(),
|
|
104731
105989
|
initial: {
|
|
104732
105990
|
provider,
|
|
104733
105991
|
model,
|
|
@@ -104799,7 +106057,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104799
106057
|
sessionOverride: reasoningSessionOverride,
|
|
104800
106058
|
globalEffort: loadShellConfig(runtime.cacheDir).reasoningEffort
|
|
104801
106059
|
}),
|
|
104802
|
-
idSeq: () =>
|
|
106060
|
+
idSeq: () => randomUUID30(),
|
|
104803
106061
|
askUser: invokeAskUserHost,
|
|
104804
106062
|
sweepBackgroundJobs: () => jobRegistry.sweepAll(),
|
|
104805
106063
|
...resetSubagentBudget !== undefined ? { resetSubagentBudget } : {},
|
|
@@ -104815,34 +106073,38 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
104815
106073
|
};
|
|
104816
106074
|
let resumeId = flags.resumeId;
|
|
104817
106075
|
if (flags.resumePick === true && resumeId === undefined) {
|
|
104818
|
-
resumeId =
|
|
106076
|
+
resumeId = bareResumeId();
|
|
104819
106077
|
}
|
|
104820
106078
|
const events = flags.eventsFile === undefined ? undefined : createFileEventSink(flags.eventsFile, (message) => {
|
|
104821
106079
|
emitSystem(`${message}
|
|
104822
106080
|
`);
|
|
104823
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
|
+
};
|
|
104824
106085
|
try {
|
|
104825
|
-
await
|
|
106086
|
+
finishLeased(await runWithLeaseChoice({
|
|
104826
106087
|
cwd: process.cwd(),
|
|
104827
106088
|
...flags.continueLast === true ? { continueLast: true } : {},
|
|
104828
|
-
...resumeId !== undefined ? { resumeId } : {}
|
|
104829
|
-
|
|
106089
|
+
...resumeId !== undefined ? { resumeId } : {},
|
|
106090
|
+
...leaseFlagOpts,
|
|
106091
|
+
leaseBox
|
|
106092
|
+
}, runRepl, leaseChoiceRuntime));
|
|
104830
106093
|
} finally {
|
|
104831
106094
|
await mcpRuntime.close();
|
|
104832
106095
|
}
|
|
104833
106096
|
} else {
|
|
104834
106097
|
let resumeId = flags.resumeId;
|
|
104835
106098
|
if (flags.resumePick === true && resumeId === undefined) {
|
|
104836
|
-
resumeId =
|
|
106099
|
+
resumeId = bareResumeId();
|
|
104837
106100
|
}
|
|
104838
|
-
await
|
|
104839
|
-
|
|
104840
|
-
|
|
104841
|
-
|
|
104842
|
-
|
|
104843
|
-
|
|
104844
|
-
|
|
104845
|
-
});
|
|
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));
|
|
104846
106108
|
}
|
|
104847
106109
|
} finally {
|
|
104848
106110
|
try {
|
|
@@ -104863,12 +106125,13 @@ async function sessionsCommand(args) {
|
|
|
104863
106125
|
const cwd = process.cwd();
|
|
104864
106126
|
if (sub === "list") {
|
|
104865
106127
|
const asJson = args.includes("--json");
|
|
104866
|
-
const
|
|
106128
|
+
const listed = listSessions(cwd).map((s) => ({ summary: s, lease: leaseColumn(cwd, s.id) }));
|
|
104867
106129
|
if (asJson) {
|
|
106130
|
+
const rows = listed.map(({ summary, lease }) => ({ ...summary, live: lease === "?" ? null : lease }));
|
|
104868
106131
|
console.log(JSON.stringify({ schemaVersion: 1, project: resolveProjectRoot2(cwd), sessions: rows }, null, 2));
|
|
104869
106132
|
return;
|
|
104870
106133
|
}
|
|
104871
|
-
if (
|
|
106134
|
+
if (listed.length === 0) {
|
|
104872
106135
|
console.log(`No sessions for project ${resolveProjectRoot2(cwd)}`);
|
|
104873
106136
|
console.log(`(store: ${projectSessionsDir(cwd)})`);
|
|
104874
106137
|
return;
|
|
@@ -104876,10 +106139,10 @@ async function sessionsCommand(args) {
|
|
|
104876
106139
|
console.log(`Project: ${resolveProjectRoot2(cwd)}`);
|
|
104877
106140
|
console.log(`Store: ${projectSessionsDir(cwd)}`);
|
|
104878
106141
|
console.log("");
|
|
104879
|
-
console.log(pad("ID", 10) + pad("UPDATED", 22) + pad("MSGS", 6) + pad("MODEL", 24) + "TITLE");
|
|
104880
|
-
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) {
|
|
104881
106144
|
const model = s.provider !== undefined && s.model !== undefined ? `${s.provider}/${s.model}` : s.model ?? "-";
|
|
104882
|
-
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));
|
|
104883
106146
|
}
|
|
104884
106147
|
console.log("");
|
|
104885
106148
|
console.log("Resume: keryx shell -r <id> Continue last: keryx shell -c");
|
|
@@ -104966,6 +106229,19 @@ async function sessionsCommand(args) {
|
|
|
104966
106229
|
printHelp17();
|
|
104967
106230
|
process.exitCode = 1;
|
|
104968
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
|
+
}
|
|
104969
106245
|
function pad(s, n) {
|
|
104970
106246
|
return s.length >= n ? `${s.slice(0, n - 1)} ` : s + " ".repeat(n - s.length);
|
|
104971
106247
|
}
|
|
@@ -104983,9 +106259,16 @@ Usage:
|
|
|
104983
106259
|
keryx sessions export <id> Export transcript as Markdown
|
|
104984
106260
|
keryx sessions path Print the on-disk sessions directory
|
|
104985
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
|
+
|
|
104986
106267
|
Shell:
|
|
104987
|
-
keryx shell -c Continue last session
|
|
106268
|
+
keryx shell -c Continue the last session no other shell has open
|
|
104988
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
|
|
104989
106272
|
`);
|
|
104990
106273
|
}
|
|
104991
106274
|
|
|
@@ -104993,7 +106276,7 @@ Shell:
|
|
|
104993
106276
|
init_fs();
|
|
104994
106277
|
import { readFile as readFile99 } from "fs/promises";
|
|
104995
106278
|
import { stdin } from "process";
|
|
104996
|
-
import
|
|
106279
|
+
import path197 from "path";
|
|
104997
106280
|
var MODULES = [
|
|
104998
106281
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
104999
106282
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -105039,8 +106322,8 @@ async function modulesCommand(args = []) {
|
|
|
105039
106322
|
return;
|
|
105040
106323
|
}
|
|
105041
106324
|
const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
105042
|
-
const metaprojectRoot =
|
|
105043
|
-
const manifestPath =
|
|
106325
|
+
const metaprojectRoot = path197.join(process.cwd(), ".metaproject");
|
|
106326
|
+
const manifestPath = path197.join(metaprojectRoot, "metaproject.json");
|
|
105044
106327
|
if (!await pathExists(manifestPath)) {
|
|
105045
106328
|
if (wantsJson) {
|
|
105046
106329
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -105154,26 +106437,26 @@ function printHelp18() {
|
|
|
105154
106437
|
}
|
|
105155
106438
|
|
|
105156
106439
|
// src/commands/serve.ts
|
|
105157
|
-
import { randomUUID as
|
|
106440
|
+
import { randomUUID as randomUUID33 } from "crypto";
|
|
105158
106441
|
init_serve_config();
|
|
105159
106442
|
|
|
105160
106443
|
// src/lib/serve-credential.ts
|
|
105161
106444
|
init_config_dir();
|
|
105162
|
-
import { createHash as createHash44, randomBytes as randomBytes5, randomUUID as
|
|
106445
|
+
import { createHash as createHash44, randomBytes as randomBytes5, randomUUID as randomUUID31 } from "crypto";
|
|
105163
106446
|
import {
|
|
105164
|
-
chmodSync as
|
|
106447
|
+
chmodSync as chmodSync5,
|
|
105165
106448
|
closeSync as closeSync4,
|
|
105166
106449
|
existsSync as existsSync34,
|
|
105167
106450
|
fsyncSync as fsyncSync2,
|
|
105168
|
-
openSync as
|
|
105169
|
-
renameSync as
|
|
105170
|
-
statSync as
|
|
106451
|
+
openSync as openSync5,
|
|
106452
|
+
renameSync as renameSync5,
|
|
106453
|
+
statSync as statSync8,
|
|
105171
106454
|
unlinkSync as unlinkSync3,
|
|
105172
|
-
writeFileSync as
|
|
106455
|
+
writeFileSync as writeFileSync10
|
|
105173
106456
|
} from "fs";
|
|
105174
|
-
import
|
|
106457
|
+
import path198 from "path";
|
|
105175
106458
|
function serveCredentialPath(dir) {
|
|
105176
|
-
return
|
|
106459
|
+
return path198.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
105177
106460
|
}
|
|
105178
106461
|
function constantTimeEqual(a, b) {
|
|
105179
106462
|
const width = Math.max(a.length, b.length);
|
|
@@ -105202,7 +106485,7 @@ function isGroupOrOtherAccessible(file) {
|
|
|
105202
106485
|
if (process.platform === "win32") {
|
|
105203
106486
|
return false;
|
|
105204
106487
|
}
|
|
105205
|
-
const mode =
|
|
106488
|
+
const mode = statSync8(file, { throwIfNoEntry: false })?.mode;
|
|
105206
106489
|
return mode !== undefined && (mode & 63) !== 0;
|
|
105207
106490
|
}
|
|
105208
106491
|
function readServeCredential(dir) {
|
|
@@ -105243,18 +106526,18 @@ function readServeCredential(dir) {
|
|
|
105243
106526
|
}
|
|
105244
106527
|
function writeStore(store, dir) {
|
|
105245
106528
|
const file = serveCredentialPath(dir);
|
|
105246
|
-
const temp = `${file}.${
|
|
106529
|
+
const temp = `${file}.${randomUUID31()}.tmp`;
|
|
105247
106530
|
try {
|
|
105248
106531
|
ensureKeryxConfigDir(dir);
|
|
105249
|
-
const handle =
|
|
106532
|
+
const handle = openSync5(temp, "wx", 384);
|
|
105250
106533
|
try {
|
|
105251
|
-
|
|
106534
|
+
writeFileSync10(handle, `${JSON.stringify(store, null, 2)}
|
|
105252
106535
|
`, { encoding: "utf8" });
|
|
105253
106536
|
fsyncSync2(handle);
|
|
105254
106537
|
} finally {
|
|
105255
106538
|
closeSync4(handle);
|
|
105256
106539
|
}
|
|
105257
|
-
|
|
106540
|
+
renameSync5(temp, file);
|
|
105258
106541
|
tighten2(file, 384);
|
|
105259
106542
|
return true;
|
|
105260
106543
|
} catch {
|
|
@@ -105269,7 +106552,7 @@ function tighten2(target, mode) {
|
|
|
105269
106552
|
return;
|
|
105270
106553
|
}
|
|
105271
106554
|
try {
|
|
105272
|
-
|
|
106555
|
+
chmodSync5(target, mode);
|
|
105273
106556
|
} catch {}
|
|
105274
106557
|
}
|
|
105275
106558
|
function withServeCredentialLock(dir, fn, onWaiting) {
|
|
@@ -105283,7 +106566,7 @@ function mintRecord(now) {
|
|
|
105283
106566
|
const salt = randomBytes5(32).toString("hex");
|
|
105284
106567
|
return {
|
|
105285
106568
|
token,
|
|
105286
|
-
record: { id:
|
|
106569
|
+
record: { id: randomUUID31(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
105287
106570
|
};
|
|
105288
106571
|
}
|
|
105289
106572
|
function issueServeToken(dir, now = () => new Date().toISOString(), onWaiting) {
|
|
@@ -105406,23 +106689,23 @@ class AuthFailureThrottle {
|
|
|
105406
106689
|
// src/lib/serve-turn-store.ts
|
|
105407
106690
|
init_config_dir();
|
|
105408
106691
|
import { createHash as createHash45 } from "crypto";
|
|
105409
|
-
import { existsSync as existsSync35, readdirSync as readdirSync4, rmSync as
|
|
105410
|
-
import
|
|
106692
|
+
import { existsSync as existsSync35, readdirSync as readdirSync4, rmSync as rmSync3 } from "fs";
|
|
106693
|
+
import path199 from "path";
|
|
105411
106694
|
var MAX_TURN_EVENTS = 1e4;
|
|
105412
106695
|
function turnsRoot(dir) {
|
|
105413
|
-
return
|
|
106696
|
+
return path199.join(keryxConfigDir(dir), "turns");
|
|
105414
106697
|
}
|
|
105415
106698
|
function turnDir(turnId, dir) {
|
|
105416
|
-
return
|
|
106699
|
+
return path199.join(turnsRoot(dir), turnId);
|
|
105417
106700
|
}
|
|
105418
106701
|
function keyPath(project, idempotencyKey, dir) {
|
|
105419
106702
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
105420
106703
|
const digest = createHash45("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
105421
|
-
return
|
|
106704
|
+
return path199.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
105422
106705
|
}
|
|
105423
106706
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
105424
106707
|
const digest = createHash45("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
105425
|
-
return
|
|
106708
|
+
return path199.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
105426
106709
|
}
|
|
105427
106710
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
105428
106711
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -105443,7 +106726,7 @@ function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
|
105443
106726
|
} catch {}
|
|
105444
106727
|
const record = turnId === undefined ? undefined : readTurnRecord(turnId, dir);
|
|
105445
106728
|
if (turnId === undefined || record === undefined || !record.ok) {
|
|
105446
|
-
|
|
106729
|
+
rmSync3(legacy, { force: true });
|
|
105447
106730
|
return null;
|
|
105448
106731
|
}
|
|
105449
106732
|
if (record.value.project !== project) {
|
|
@@ -105451,7 +106734,7 @@ function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
|
105451
106734
|
}
|
|
105452
106735
|
writeOwnerOnlyFile(scoped, `${JSON.stringify({ turnId }, null, 2)}
|
|
105453
106736
|
`);
|
|
105454
|
-
|
|
106737
|
+
rmSync3(legacy, { force: true });
|
|
105455
106738
|
return turnId;
|
|
105456
106739
|
}
|
|
105457
106740
|
function isServerFault(reason) {
|
|
@@ -105486,7 +106769,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
105486
106769
|
}
|
|
105487
106770
|
function createTurnRecord(record, dir) {
|
|
105488
106771
|
ensureTurnDir(record.turnId, dir);
|
|
105489
|
-
writeOwnerOnlyFile(
|
|
106772
|
+
writeOwnerOnlyFile(path199.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
105490
106773
|
`);
|
|
105491
106774
|
}
|
|
105492
106775
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -105495,12 +106778,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
105495
106778
|
}
|
|
105496
106779
|
const line = JSON.stringify(event);
|
|
105497
106780
|
try {
|
|
105498
|
-
appendOwnerOnlyLine(
|
|
106781
|
+
appendOwnerOnlyLine(path199.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
105499
106782
|
} catch (error) {
|
|
105500
106783
|
if (error?.code !== "ENOENT") {
|
|
105501
106784
|
throw error;
|
|
105502
106785
|
}
|
|
105503
|
-
appendOwnerOnlyLine(
|
|
106786
|
+
appendOwnerOnlyLine(path199.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
105504
106787
|
}
|
|
105505
106788
|
return true;
|
|
105506
106789
|
}
|
|
@@ -105508,7 +106791,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
105508
106791
|
if (!isTurnId(turnId)) {
|
|
105509
106792
|
return { ok: false, reason: "not-a-turn-id" };
|
|
105510
106793
|
}
|
|
105511
|
-
const read = readTurnFile(
|
|
106794
|
+
const read = readTurnFile(path199.join(turnDir(turnId, dir), "events.jsonl"));
|
|
105512
106795
|
if (!read.ok) {
|
|
105513
106796
|
if (isDefiniteAbsence2(read.reason)) {
|
|
105514
106797
|
return { ok: true, value: [] };
|
|
@@ -105536,7 +106819,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
105536
106819
|
if (!isTurnId(turnId)) {
|
|
105537
106820
|
return { ok: false, reason: "not-a-turn-id" };
|
|
105538
106821
|
}
|
|
105539
|
-
const read = readTurnFile(
|
|
106822
|
+
const read = readTurnFile(path199.join(turnDir(turnId, dir), "turn.json"));
|
|
105540
106823
|
if (!read.ok) {
|
|
105541
106824
|
return { ok: false, reason: read.reason };
|
|
105542
106825
|
}
|
|
@@ -105555,7 +106838,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
105555
106838
|
if (!record.ok) {
|
|
105556
106839
|
return false;
|
|
105557
106840
|
}
|
|
105558
|
-
writeOwnerOnlyFile(
|
|
106841
|
+
writeOwnerOnlyFile(path199.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
105559
106842
|
`);
|
|
105560
106843
|
return true;
|
|
105561
106844
|
}
|
|
@@ -105595,13 +106878,13 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
105595
106878
|
} catch {
|
|
105596
106879
|
return false;
|
|
105597
106880
|
}
|
|
105598
|
-
|
|
106881
|
+
rmSync3(file, { force: true });
|
|
105599
106882
|
return true;
|
|
105600
106883
|
}
|
|
105601
106884
|
|
|
105602
106885
|
// src/lib/serve-turn.ts
|
|
105603
|
-
import { randomUUID as
|
|
105604
|
-
import
|
|
106886
|
+
import { randomUUID as randomUUID32 } from "crypto";
|
|
106887
|
+
import path200 from "path";
|
|
105605
106888
|
init_service();
|
|
105606
106889
|
var REMOTE_ORIGIN = "remote:http";
|
|
105607
106890
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -105670,9 +106953,9 @@ function isUuid(value) {
|
|
|
105670
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);
|
|
105671
106954
|
}
|
|
105672
106955
|
function resolveProject(declared, dir) {
|
|
105673
|
-
const wanted =
|
|
106956
|
+
const wanted = path200.resolve(declared);
|
|
105674
106957
|
for (const entry of listProjects(dir, () => {})) {
|
|
105675
|
-
if (
|
|
106958
|
+
if (path200.resolve(entry.path) === wanted) {
|
|
105676
106959
|
return { ok: true, project: entry.path };
|
|
105677
106960
|
}
|
|
105678
106961
|
}
|
|
@@ -105719,7 +107002,7 @@ async function redactOut(security, text) {
|
|
|
105719
107002
|
async function runRemoteTurn(input) {
|
|
105720
107003
|
const scanRoot = input.scanRoot;
|
|
105721
107004
|
const security = createSecurityService(scanRoot);
|
|
105722
|
-
const newId = input.newId ?? (() =>
|
|
107005
|
+
const newId = input.newId ?? (() => randomUUID32());
|
|
105723
107006
|
const clock = input.clock ?? (() => new Date().toISOString());
|
|
105724
107007
|
const turnId = input.turnId ?? newId();
|
|
105725
107008
|
const sessionId = input.request.sessionId ?? newId();
|
|
@@ -105862,7 +107145,7 @@ function outcomeOf(status, gate, unresolvedBlockerIds) {
|
|
|
105862
107145
|
}
|
|
105863
107146
|
function createSubmitTurn(deps) {
|
|
105864
107147
|
return async (request, project) => {
|
|
105865
|
-
const turnId = (deps.newId ?? (() =>
|
|
107148
|
+
const turnId = (deps.newId ?? (() => randomUUID32()))();
|
|
105866
107149
|
const scanned = await scanPrompt(deps.dir, request.prompt);
|
|
105867
107150
|
if (scanned.rejected) {
|
|
105868
107151
|
return { kind: "rejected" };
|
|
@@ -106550,7 +107833,7 @@ function runConfig(args) {
|
|
|
106550
107833
|
return;
|
|
106551
107834
|
}
|
|
106552
107835
|
const credential = readServeCredential();
|
|
106553
|
-
const credentialId = credential.status === "ok" ? credential.record.id :
|
|
107836
|
+
const credentialId = credential.status === "ok" ? credential.record.id : randomUUID33();
|
|
106554
107837
|
const config = defaultServeConfig(credentialId, {
|
|
106555
107838
|
address: parsed.parsed.values.get("--bind") ?? DEFAULT_SERVE_BIND_ADDRESS,
|
|
106556
107839
|
port: port ?? DEFAULT_SERVE_PORT,
|
|
@@ -106708,7 +107991,7 @@ function printHelp19() {
|
|
|
106708
107991
|
import { spawn as spawn7 } from "child_process";
|
|
106709
107992
|
import { chmod as chmod4, mkdir as mkdir63, readFile as readFile100, readdir as readdir37, writeFile as writeFile54 } from "fs/promises";
|
|
106710
107993
|
import { access as access4, constants as constants3, existsSync as existsSync36 } from "fs";
|
|
106711
|
-
import
|
|
107994
|
+
import path201 from "path";
|
|
106712
107995
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
106713
107996
|
init_config2();
|
|
106714
107997
|
init_config();
|
|
@@ -106725,8 +108008,8 @@ async function updateCommand(args = []) {
|
|
|
106725
108008
|
return;
|
|
106726
108009
|
}
|
|
106727
108010
|
const projectRoot = process.cwd();
|
|
106728
|
-
const metaprojectRoot =
|
|
106729
|
-
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)}/`);
|
|
106730
108013
|
if (!await pathExists(metaprojectRoot)) {
|
|
106731
108014
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
106732
108015
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -106799,7 +108082,7 @@ async function updateCommand(args = []) {
|
|
|
106799
108082
|
nextSteps(steps);
|
|
106800
108083
|
}
|
|
106801
108084
|
async function previewServiceFiles(projectRoot, options) {
|
|
106802
|
-
const metaprojectRoot =
|
|
108085
|
+
const metaprojectRoot = path201.join(projectRoot, ".metaproject");
|
|
106803
108086
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
106804
108087
|
const manifest = manifestState.manifest;
|
|
106805
108088
|
const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
|
|
@@ -106828,12 +108111,12 @@ async function previewServiceFiles(projectRoot, options) {
|
|
|
106828
108111
|
});
|
|
106829
108112
|
}
|
|
106830
108113
|
async function refreshServiceFiles(projectRoot, options) {
|
|
106831
|
-
const metaprojectRoot =
|
|
108114
|
+
const metaprojectRoot = path201.join(projectRoot, ".metaproject");
|
|
106832
108115
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
106833
108116
|
const manifest = manifestState.manifest;
|
|
106834
108117
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
106835
108118
|
if (manifestState.migrated) {
|
|
106836
|
-
await writeFile54(
|
|
108119
|
+
await writeFile54(path201.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
106837
108120
|
`, "utf8");
|
|
106838
108121
|
}
|
|
106839
108122
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -106870,9 +108153,9 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106870
108153
|
enableSecurity,
|
|
106871
108154
|
enableSac
|
|
106872
108155
|
});
|
|
106873
|
-
await writeTextIfChanged4(
|
|
106874
|
-
await writeTextIfChanged4(
|
|
106875
|
-
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 }));
|
|
106876
108159
|
await writeRoutingEntrypointPair(metaprojectRoot, {
|
|
106877
108160
|
enableGdgraph,
|
|
106878
108161
|
enableGdctx,
|
|
@@ -106895,7 +108178,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106895
108178
|
}
|
|
106896
108179
|
}
|
|
106897
108180
|
});
|
|
106898
|
-
await writeTextIfChanged4(
|
|
108181
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
106899
108182
|
enableGdgraph,
|
|
106900
108183
|
enableGdctx,
|
|
106901
108184
|
enableGdwiki,
|
|
@@ -106907,7 +108190,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106907
108190
|
enableSecurity,
|
|
106908
108191
|
data: dashboardData
|
|
106909
108192
|
}));
|
|
106910
|
-
await writeTextIfMissing4(
|
|
108193
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
106911
108194
|
enableGdgraph,
|
|
106912
108195
|
enableGdctx,
|
|
106913
108196
|
enableGdwiki,
|
|
@@ -106920,31 +108203,31 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106920
108203
|
}));
|
|
106921
108204
|
if (enableGdgraph) {
|
|
106922
108205
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
106923
|
-
await writeTextIfChanged4(
|
|
106924
|
-
await writeTextIfChanged4(
|
|
106925
|
-
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());
|
|
106926
108209
|
await seedAssetsLock(metaprojectRoot);
|
|
106927
108210
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
106928
108211
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
106929
108212
|
}
|
|
106930
108213
|
}
|
|
106931
108214
|
if (enableGdctx) {
|
|
106932
|
-
await writeTextIfMissing4(
|
|
106933
|
-
await writeTextIfChanged4(
|
|
106934
|
-
await writeTextIfChanged4(
|
|
106935
|
-
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());
|
|
106936
108219
|
}
|
|
106937
108220
|
if (enableGdwiki) {
|
|
106938
|
-
await writeTextIfMissing4(
|
|
106939
|
-
await writeTextIfChanged4(
|
|
106940
|
-
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());
|
|
106941
108224
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
106942
108225
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
106943
108226
|
}
|
|
106944
108227
|
}
|
|
106945
108228
|
if (enableSac) {
|
|
106946
|
-
await writeTextIfMissing4(
|
|
106947
|
-
await writeTextIfChanged4(
|
|
108229
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
108230
|
+
await writeTextIfChanged4(path201.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
106948
108231
|
}
|
|
106949
108232
|
let gdskillsWarnings = [];
|
|
106950
108233
|
let gdskillsNotices = [];
|
|
@@ -106957,25 +108240,25 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106957
108240
|
}
|
|
106958
108241
|
}
|
|
106959
108242
|
if (enableHealth) {
|
|
106960
|
-
await writeTextIfMissing4(
|
|
106961
|
-
await writeTextIfChanged4(
|
|
106962
|
-
await writeTextIfChanged4(
|
|
106963
|
-
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());
|
|
106964
108247
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
106965
108248
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
106966
108249
|
}
|
|
106967
108250
|
}
|
|
106968
108251
|
if (enableTesting) {
|
|
106969
|
-
await writeTextIfMissing4(
|
|
108252
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
106970
108253
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
106971
108254
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
106972
108255
|
}));
|
|
106973
|
-
await writeTextIfChanged4(
|
|
106974
|
-
await writeTextIfChanged4(
|
|
106975
|
-
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());
|
|
106976
108259
|
if (enableGdwiki) {
|
|
106977
|
-
await writeTextIfMissing4(
|
|
106978
|
-
await writeTextIfMissing4(
|
|
108260
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
108261
|
+
await writeTextIfMissing4(path201.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
106979
108262
|
}
|
|
106980
108263
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
106981
108264
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -106988,24 +108271,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
106988
108271
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
106989
108272
|
}
|
|
106990
108273
|
if (enableMemory) {
|
|
106991
|
-
await writeTextIfMissing4(
|
|
106992
|
-
await writeTextIfMissing4(
|
|
106993
|
-
await writeTextIfChanged4(
|
|
106994
|
-
await writeTextIfChanged4(
|
|
106995
|
-
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());
|
|
106996
108279
|
}
|
|
106997
108280
|
if (enableTasks) {
|
|
106998
|
-
await writeTextIfChanged4(
|
|
106999
|
-
await writeTextIfChanged4(
|
|
107000
|
-
await writeTextIfChanged4(
|
|
107001
|
-
await writeTextIfChanged4(
|
|
107002
|
-
await writeTextIfChanged4(
|
|
107003
|
-
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());
|
|
107004
108287
|
}
|
|
107005
108288
|
if (enableSecurity) {
|
|
107006
|
-
await writeTextIfMissing4(
|
|
107007
|
-
await writeTextIfChanged4(
|
|
107008
|
-
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());
|
|
107009
108292
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
107010
108293
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
107011
108294
|
}
|
|
@@ -107058,13 +108341,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
107058
108341
|
};
|
|
107059
108342
|
}
|
|
107060
108343
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
107061
|
-
const metaprojectRoot =
|
|
108344
|
+
const metaprojectRoot = path201.join(projectRoot, ".metaproject");
|
|
107062
108345
|
if (!await pathExists(metaprojectRoot)) {
|
|
107063
108346
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
107064
108347
|
}
|
|
107065
108348
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
107066
108349
|
const data = await collectDashboardData(metaprojectRoot);
|
|
107067
|
-
const dashboardPath =
|
|
108350
|
+
const dashboardPath = path201.join(metaprojectRoot, "keryx-dashboard.html");
|
|
107068
108351
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
107069
108352
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
107070
108353
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -107084,7 +108367,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
107084
108367
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
107085
108368
|
return true;
|
|
107086
108369
|
}
|
|
107087
|
-
const hookPath =
|
|
108370
|
+
const hookPath = path201.join(projectRoot, ".git", "hooks", "post-commit");
|
|
107088
108371
|
if (!await pathExists(hookPath)) {
|
|
107089
108372
|
return false;
|
|
107090
108373
|
}
|
|
@@ -107104,11 +108387,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
107104
108387
|
if (testing) {
|
|
107105
108388
|
data.testing = testing;
|
|
107106
108389
|
}
|
|
107107
|
-
const wiki = await collectMarkdownPages(
|
|
108390
|
+
const wiki = await collectMarkdownPages(path201.join(metaprojectRoot, "wiki"), "wiki");
|
|
107108
108391
|
if (wiki.length > 0) {
|
|
107109
108392
|
data.wiki = { pages: wiki };
|
|
107110
108393
|
}
|
|
107111
|
-
const memory = await collectMarkdownPages(
|
|
108394
|
+
const memory = await collectMarkdownPages(path201.join(metaprojectRoot, "memory"), "memory");
|
|
107112
108395
|
if (memory.length > 0) {
|
|
107113
108396
|
data.memory = { entries: memory };
|
|
107114
108397
|
}
|
|
@@ -107123,7 +108406,7 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
107123
108406
|
return data;
|
|
107124
108407
|
}
|
|
107125
108408
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
107126
|
-
const flowsRoot =
|
|
108409
|
+
const flowsRoot = path201.join(metaprojectRoot, "flows");
|
|
107127
108410
|
if (!await pathExists(flowsRoot)) {
|
|
107128
108411
|
return null;
|
|
107129
108412
|
}
|
|
@@ -107135,7 +108418,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
107135
108418
|
}
|
|
107136
108419
|
const flows = [];
|
|
107137
108420
|
for (const dir of dirEntries) {
|
|
107138
|
-
const flowPath =
|
|
108421
|
+
const flowPath = path201.join(flowsRoot, dir, "flow.json");
|
|
107139
108422
|
if (!await pathExists(flowPath)) {
|
|
107140
108423
|
continue;
|
|
107141
108424
|
}
|
|
@@ -107143,7 +108426,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
107143
108426
|
const flow = JSON.parse(await readFile100(flowPath, "utf8"));
|
|
107144
108427
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
107145
108428
|
let acTotal = 0;
|
|
107146
|
-
const acPath =
|
|
108429
|
+
const acPath = path201.join(flowsRoot, dir, "acceptance-criteria.md");
|
|
107147
108430
|
if (await pathExists(acPath)) {
|
|
107148
108431
|
const acContent = await readFile100(acPath, "utf8");
|
|
107149
108432
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -107197,7 +108480,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
107197
108480
|
"data/testing/context.md"
|
|
107198
108481
|
];
|
|
107199
108482
|
for (const href of staticHrefs) {
|
|
107200
|
-
const filePath =
|
|
108483
|
+
const filePath = path201.join(metaprojectRoot, ...href.split("/"));
|
|
107201
108484
|
if (!await pathExists(filePath)) {
|
|
107202
108485
|
continue;
|
|
107203
108486
|
}
|
|
@@ -107214,7 +108497,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
107214
108497
|
return docs;
|
|
107215
108498
|
}
|
|
107216
108499
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
107217
|
-
const reportPath =
|
|
108500
|
+
const reportPath = path201.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
107218
108501
|
if (!await pathExists(reportPath)) {
|
|
107219
108502
|
return;
|
|
107220
108503
|
}
|
|
@@ -107323,8 +108606,8 @@ function metricToScope(metric) {
|
|
|
107323
108606
|
};
|
|
107324
108607
|
}
|
|
107325
108608
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
107326
|
-
const nodesPath =
|
|
107327
|
-
const edgesPath =
|
|
108609
|
+
const nodesPath = path201.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
108610
|
+
const edgesPath = path201.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
107328
108611
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
107329
108612
|
return;
|
|
107330
108613
|
}
|
|
@@ -107375,8 +108658,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
107375
108658
|
};
|
|
107376
108659
|
}
|
|
107377
108660
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
107378
|
-
const reportPath =
|
|
107379
|
-
const contextPath =
|
|
108661
|
+
const reportPath = path201.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
108662
|
+
const contextPath = path201.join(metaprojectRoot, "data", "testing", "context.md");
|
|
107380
108663
|
if (await pathExists(reportPath)) {
|
|
107381
108664
|
const report = JSON.parse(await readFile100(reportPath, "utf8"));
|
|
107382
108665
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -107405,7 +108688,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
107405
108688
|
const files = await listMarkdownFiles(root);
|
|
107406
108689
|
const pages = [];
|
|
107407
108690
|
for (const filePath of files.slice(0, 40)) {
|
|
107408
|
-
const relativePath =
|
|
108691
|
+
const relativePath = path201.relative(root, filePath).split(path201.sep).join("/");
|
|
107409
108692
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
107410
108693
|
continue;
|
|
107411
108694
|
}
|
|
@@ -107426,7 +108709,7 @@ async function listMarkdownFiles(root) {
|
|
|
107426
108709
|
const entries = await readdir37(root, { withFileTypes: true });
|
|
107427
108710
|
const files = [];
|
|
107428
108711
|
for (const entry of entries) {
|
|
107429
|
-
const fullPath =
|
|
108712
|
+
const fullPath = path201.join(root, entry.name);
|
|
107430
108713
|
if (entry.isDirectory()) {
|
|
107431
108714
|
files.push(...await listMarkdownFiles(fullPath));
|
|
107432
108715
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -107474,7 +108757,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
107474
108757
|
const manifest = {
|
|
107475
108758
|
schemaVersion: 1,
|
|
107476
108759
|
standardVersion: STANDARD_VERSION,
|
|
107477
|
-
name: `${
|
|
108760
|
+
name: `${path201.basename(path201.dirname(metaprojectRoot))}-metaproject`,
|
|
107478
108761
|
createdBy: "keryx",
|
|
107479
108762
|
profiles: computeProfiles(enabledModuleKeys),
|
|
107480
108763
|
paths: {
|
|
@@ -107564,11 +108847,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
107564
108847
|
metaproject: ".metaproject/index.md"
|
|
107565
108848
|
}
|
|
107566
108849
|
};
|
|
107567
|
-
await writeFile54(
|
|
108850
|
+
await writeFile54(path201.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
107568
108851
|
`, "utf8");
|
|
107569
108852
|
}
|
|
107570
108853
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
107571
|
-
const manifestPath =
|
|
108854
|
+
const manifestPath = path201.join(metaprojectRoot, "metaproject.json");
|
|
107572
108855
|
if (!await pathExists(manifestPath)) {
|
|
107573
108856
|
return;
|
|
107574
108857
|
}
|
|
@@ -107591,7 +108874,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
107591
108874
|
`, "utf8");
|
|
107592
108875
|
}
|
|
107593
108876
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
107594
|
-
const manifestPath =
|
|
108877
|
+
const manifestPath = path201.join(metaprojectRoot, "metaproject.json");
|
|
107595
108878
|
if (!await pathExists(manifestPath)) {
|
|
107596
108879
|
return;
|
|
107597
108880
|
}
|
|
@@ -107627,72 +108910,72 @@ async function updateRuntime(projectRoot) {
|
|
|
107627
108910
|
}
|
|
107628
108911
|
}
|
|
107629
108912
|
async function findRuntimeRoot(projectRoot) {
|
|
107630
|
-
const projectRuntime =
|
|
107631
|
-
if (await pathExists(
|
|
108913
|
+
const projectRuntime = path201.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
108914
|
+
if (await pathExists(path201.join(projectRuntime, ".git"))) {
|
|
107632
108915
|
return projectRuntime;
|
|
107633
108916
|
}
|
|
107634
108917
|
const home = process.env.HOME;
|
|
107635
108918
|
if (!home) {
|
|
107636
108919
|
return null;
|
|
107637
108920
|
}
|
|
107638
|
-
const globalRuntime =
|
|
107639
|
-
if (await pathExists(
|
|
108921
|
+
const globalRuntime = path201.join(home, ".keryx", "keryx");
|
|
108922
|
+
if (await pathExists(path201.join(globalRuntime, ".git"))) {
|
|
107640
108923
|
return globalRuntime;
|
|
107641
108924
|
}
|
|
107642
108925
|
return null;
|
|
107643
108926
|
}
|
|
107644
108927
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
107645
108928
|
const dirs = [
|
|
107646
|
-
|
|
107647
|
-
|
|
107648
|
-
|
|
107649
|
-
|
|
107650
|
-
|
|
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"),
|
|
107651
108934
|
...modules.enableGdgraph ? [
|
|
107652
|
-
|
|
107653
|
-
|
|
108935
|
+
path201.join(metaprojectRoot, "core", "gdgraph"),
|
|
108936
|
+
path201.join(metaprojectRoot, "skills", "gdgraph")
|
|
107654
108937
|
] : [],
|
|
107655
108938
|
...modules.enableGdctx ? [
|
|
107656
|
-
|
|
107657
|
-
|
|
108939
|
+
path201.join(metaprojectRoot, "core", "gdctx"),
|
|
108940
|
+
path201.join(metaprojectRoot, "skills", "gdctx")
|
|
107658
108941
|
] : [],
|
|
107659
108942
|
...modules.enableGdwiki ? [
|
|
107660
|
-
|
|
107661
|
-
|
|
108943
|
+
path201.join(metaprojectRoot, "skills", "gdwiki"),
|
|
108944
|
+
path201.join(metaprojectRoot, "wiki", "templates")
|
|
107662
108945
|
] : [],
|
|
107663
108946
|
...modules.enableHealth ? [
|
|
107664
|
-
|
|
107665
|
-
|
|
108947
|
+
path201.join(metaprojectRoot, "core", "health"),
|
|
108948
|
+
path201.join(metaprojectRoot, "skills", "health")
|
|
107666
108949
|
] : [],
|
|
107667
108950
|
...modules.enableTesting ? [
|
|
107668
|
-
|
|
107669
|
-
|
|
108951
|
+
path201.join(metaprojectRoot, "core", "testing"),
|
|
108952
|
+
path201.join(metaprojectRoot, "skills", "testing")
|
|
107670
108953
|
] : [],
|
|
107671
108954
|
...modules.enableMemory ? [
|
|
107672
|
-
|
|
107673
|
-
|
|
107674
|
-
|
|
108955
|
+
path201.join(metaprojectRoot, "core", "memory"),
|
|
108956
|
+
path201.join(metaprojectRoot, "skills", "memory"),
|
|
108957
|
+
path201.join(metaprojectRoot, "memory", "templates")
|
|
107675
108958
|
] : [],
|
|
107676
108959
|
...modules.enableTasks ? [
|
|
107677
|
-
|
|
107678
|
-
|
|
108960
|
+
path201.join(metaprojectRoot, "flows"),
|
|
108961
|
+
path201.join(metaprojectRoot, "skills", "flow")
|
|
107679
108962
|
] : [],
|
|
107680
108963
|
...modules.enableSecurity ? [
|
|
107681
|
-
|
|
108964
|
+
path201.join(metaprojectRoot, "core", "security")
|
|
107682
108965
|
] : [],
|
|
107683
108966
|
...modules.enableSac ? [
|
|
107684
|
-
|
|
108967
|
+
path201.join(metaprojectRoot, "skills", "sac")
|
|
107685
108968
|
] : []
|
|
107686
108969
|
];
|
|
107687
108970
|
await Promise.all(dirs.map((dir) => mkdir63(dir, { recursive: true })));
|
|
107688
108971
|
}
|
|
107689
108972
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
107690
|
-
const gdgraphCoreRoot =
|
|
108973
|
+
const gdgraphCoreRoot = path201.join(metaprojectRoot, "core", "gdgraph");
|
|
107691
108974
|
await mkdir63(gdgraphCoreRoot, { recursive: true });
|
|
107692
108975
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
107693
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
108976
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path201.join(gdgraphCoreRoot, file));
|
|
107694
108977
|
}
|
|
107695
|
-
await writeTextIfChanged4(
|
|
108978
|
+
await writeTextIfChanged4(path201.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
107696
108979
|
}
|
|
107697
108980
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
107698
108981
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
@@ -107700,7 +108983,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
|
107700
108983
|
return;
|
|
107701
108984
|
}
|
|
107702
108985
|
await mkdir63(hooksRoot, { recursive: true });
|
|
107703
|
-
const hookPath =
|
|
108986
|
+
const hookPath = path201.join(hooksRoot, hookName);
|
|
107704
108987
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
107705
108988
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
107706
108989
|
const managedBlock = `${blockStart}
|
|
@@ -107721,7 +109004,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
107721
109004
|
if (!hooksRoot) {
|
|
107722
109005
|
return;
|
|
107723
109006
|
}
|
|
107724
|
-
const hookPath =
|
|
109007
|
+
const hookPath = path201.join(hooksRoot, hookName);
|
|
107725
109008
|
if (!await pathExists(hookPath)) {
|
|
107726
109009
|
return;
|
|
107727
109010
|
}
|
|
@@ -107743,7 +109026,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
107743
109026
|
if (!hooksRoot) {
|
|
107744
109027
|
return false;
|
|
107745
109028
|
}
|
|
107746
|
-
const hookPath =
|
|
109029
|
+
const hookPath = path201.join(hooksRoot, "pre-push");
|
|
107747
109030
|
if (!await pathExists(hookPath)) {
|
|
107748
109031
|
return false;
|
|
107749
109032
|
}
|
|
@@ -107758,7 +109041,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
107758
109041
|
return (await readFile100(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
107759
109042
|
}
|
|
107760
109043
|
async function readManifest5(metaprojectRoot) {
|
|
107761
|
-
const manifestPath =
|
|
109044
|
+
const manifestPath = path201.join(metaprojectRoot, "metaproject.json");
|
|
107762
109045
|
if (!await pathExists(manifestPath)) {
|
|
107763
109046
|
return {
|
|
107764
109047
|
exists: false,
|
|
@@ -107827,7 +109110,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
107827
109110
|
}
|
|
107828
109111
|
async function anyPathExists(root, candidates) {
|
|
107829
109112
|
for (const candidate of candidates) {
|
|
107830
|
-
if (await pathExists(
|
|
109113
|
+
if (await pathExists(path201.join(root, candidate))) {
|
|
107831
109114
|
return true;
|
|
107832
109115
|
}
|
|
107833
109116
|
}
|
|
@@ -107850,13 +109133,13 @@ function parseUpdateArgs(args) {
|
|
|
107850
109133
|
};
|
|
107851
109134
|
}
|
|
107852
109135
|
async function runPostUpdateHooks(projectRoot) {
|
|
107853
|
-
const hooksDir =
|
|
109136
|
+
const hooksDir = path201.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
107854
109137
|
if (!await pathExists(hooksDir)) {
|
|
107855
109138
|
return;
|
|
107856
109139
|
}
|
|
107857
109140
|
const entries = (await readdir37(hooksDir)).sort();
|
|
107858
109141
|
for (const entry of entries) {
|
|
107859
|
-
const hookPath =
|
|
109142
|
+
const hookPath = path201.join(hooksDir, entry);
|
|
107860
109143
|
try {
|
|
107861
109144
|
await accessExecutable(hookPath);
|
|
107862
109145
|
} catch {
|
|
@@ -107897,14 +109180,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
107897
109180
|
if (await pathExists(filePath) && await readFile100(filePath, "utf8") === content) {
|
|
107898
109181
|
return;
|
|
107899
109182
|
}
|
|
107900
|
-
await mkdir63(
|
|
109183
|
+
await mkdir63(path201.dirname(filePath), { recursive: true });
|
|
107901
109184
|
await writeFile54(filePath, content, "utf8");
|
|
107902
109185
|
}
|
|
107903
109186
|
async function writeTextIfMissing4(filePath, content) {
|
|
107904
109187
|
if (await pathExists(filePath)) {
|
|
107905
109188
|
return;
|
|
107906
109189
|
}
|
|
107907
|
-
await mkdir63(
|
|
109190
|
+
await mkdir63(path201.dirname(filePath), { recursive: true });
|
|
107908
109191
|
await writeFile54(filePath, content, "utf8");
|
|
107909
109192
|
}
|
|
107910
109193
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -107912,7 +109195,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
107912
109195
|
if (await pathExists(to) && await readFile100(to, "utf8") === next) {
|
|
107913
109196
|
return;
|
|
107914
109197
|
}
|
|
107915
|
-
await mkdir63(
|
|
109198
|
+
await mkdir63(path201.dirname(to), { recursive: true });
|
|
107916
109199
|
await writeFile54(to, next, "utf8");
|
|
107917
109200
|
}
|
|
107918
109201
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -107921,7 +109204,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
107921
109204
|
return directPath;
|
|
107922
109205
|
}
|
|
107923
109206
|
if (relativePath.startsWith("../")) {
|
|
107924
|
-
const packagedSourcePath =
|
|
109207
|
+
const packagedSourcePath = path201.join(path201.dirname(fileURLToPath8(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
107925
109208
|
if (existsSync36(packagedSourcePath)) {
|
|
107926
109209
|
return packagedSourcePath;
|
|
107927
109210
|
}
|
|
@@ -107956,7 +109239,7 @@ function printHelp20() {
|
|
|
107956
109239
|
|
|
107957
109240
|
// src/commands/dashboard.ts
|
|
107958
109241
|
import { spawn as spawn8 } from "child_process";
|
|
107959
|
-
import
|
|
109242
|
+
import path202 from "path";
|
|
107960
109243
|
init_args();
|
|
107961
109244
|
async function dashboardCommand(args = []) {
|
|
107962
109245
|
const options = parseOptions(args);
|
|
@@ -107967,7 +109250,7 @@ async function dashboardCommand(args = []) {
|
|
|
107967
109250
|
}
|
|
107968
109251
|
if (subcommand === "build") {
|
|
107969
109252
|
const result = await buildDashboard();
|
|
107970
|
-
const rel =
|
|
109253
|
+
const rel = path202.relative(process.cwd(), result.path);
|
|
107971
109254
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
107972
109255
|
note(`Open it: keryx dashboard open`);
|
|
107973
109256
|
return;
|
|
@@ -107975,7 +109258,7 @@ async function dashboardCommand(args = []) {
|
|
|
107975
109258
|
if (subcommand === "open") {
|
|
107976
109259
|
const result = await buildDashboard();
|
|
107977
109260
|
await openFile(result.path);
|
|
107978
|
-
const rel =
|
|
109261
|
+
const rel = path202.relative(process.cwd(), result.path);
|
|
107979
109262
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
107980
109263
|
return;
|
|
107981
109264
|
}
|
|
@@ -108019,12 +109302,12 @@ function printHelp21() {
|
|
|
108019
109302
|
}
|
|
108020
109303
|
|
|
108021
109304
|
// src/commands/agents.ts
|
|
108022
|
-
import { readFileSync as
|
|
109305
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
108023
109306
|
|
|
108024
109307
|
// src/agents/bootstrap.ts
|
|
108025
109308
|
import { mkdir as mkdir64, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
|
|
108026
109309
|
import { homedir as homedir10 } from "os";
|
|
108027
|
-
import
|
|
109310
|
+
import path203 from "path";
|
|
108028
109311
|
init_fs();
|
|
108029
109312
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
108030
109313
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -108034,35 +109317,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
108034
109317
|
aliases: ["claude-code"],
|
|
108035
109318
|
label: "Claude Code",
|
|
108036
109319
|
fileName: "CLAUDE.md",
|
|
108037
|
-
filePath: (homeRoot) =>
|
|
109320
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".claude", "CLAUDE.md")
|
|
108038
109321
|
},
|
|
108039
109322
|
{
|
|
108040
109323
|
id: "opencode",
|
|
108041
109324
|
aliases: ["open-code"],
|
|
108042
109325
|
label: "OpenCode",
|
|
108043
109326
|
fileName: "AGENTS.md",
|
|
108044
|
-
filePath: (homeRoot) =>
|
|
109327
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
108045
109328
|
},
|
|
108046
109329
|
{
|
|
108047
109330
|
id: "zcode",
|
|
108048
109331
|
aliases: ["zed", "zed-code"],
|
|
108049
109332
|
label: "ZCode",
|
|
108050
109333
|
fileName: "AGENTS.md",
|
|
108051
|
-
filePath: (homeRoot) =>
|
|
109334
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".zcode", "AGENTS.md")
|
|
108052
109335
|
},
|
|
108053
109336
|
{
|
|
108054
109337
|
id: "codex",
|
|
108055
109338
|
aliases: [],
|
|
108056
109339
|
label: "Codex",
|
|
108057
109340
|
fileName: "AGENTS.md",
|
|
108058
|
-
filePath: (homeRoot) =>
|
|
109341
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".codex", "AGENTS.md")
|
|
108059
109342
|
},
|
|
108060
109343
|
{
|
|
108061
109344
|
id: "antigravity",
|
|
108062
109345
|
aliases: ["antigravuty", "antigravity-code"],
|
|
108063
109346
|
label: "Antigravity",
|
|
108064
109347
|
fileName: "AGENTS.md",
|
|
108065
|
-
filePath: (homeRoot) =>
|
|
109348
|
+
filePath: (homeRoot) => path203.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
108066
109349
|
}
|
|
108067
109350
|
];
|
|
108068
109351
|
function agentBootstrapRuntimeIds() {
|
|
@@ -108110,7 +109393,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
108110
109393
|
const dryRun = options.dryRun === true;
|
|
108111
109394
|
const wrote = next !== current;
|
|
108112
109395
|
if (wrote && !dryRun) {
|
|
108113
|
-
await mkdir64(
|
|
109396
|
+
await mkdir64(path203.dirname(filePath), { recursive: true });
|
|
108114
109397
|
await writeFile55(filePath, next, "utf8");
|
|
108115
109398
|
}
|
|
108116
109399
|
const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
@@ -108520,7 +109803,7 @@ function monitorCommand(args) {
|
|
|
108520
109803
|
}
|
|
108521
109804
|
let events;
|
|
108522
109805
|
try {
|
|
108523
|
-
events = parseAgentEvents(
|
|
109806
|
+
events = parseAgentEvents(readFileSync12(source, "utf8"));
|
|
108524
109807
|
} catch (error) {
|
|
108525
109808
|
console.error(`Failed to read/parse agent-event source "${source}": ${error.message}`);
|
|
108526
109809
|
process.exitCode = 1;
|
|
@@ -108647,7 +109930,7 @@ function printBootstrapHelp() {
|
|
|
108647
109930
|
// src/commands/metrics.ts
|
|
108648
109931
|
init_args();
|
|
108649
109932
|
import { readFile as readFile102 } from "fs/promises";
|
|
108650
|
-
import
|
|
109933
|
+
import path205 from "path";
|
|
108651
109934
|
|
|
108652
109935
|
// src/metrics/benchmark.ts
|
|
108653
109936
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -109464,7 +110747,7 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
109464
110747
|
|
|
109465
110748
|
// src/metrics/oracle-runner.ts
|
|
109466
110749
|
import { mkdir as mkdir65, writeFile as writeFile56 } from "fs/promises";
|
|
109467
|
-
import
|
|
110750
|
+
import path204 from "path";
|
|
109468
110751
|
|
|
109469
110752
|
// src/metrics/ir.ts
|
|
109470
110753
|
function toIdSet(ids) {
|
|
@@ -109910,9 +111193,9 @@ function buildEvidenceBundle(input, options = {}) {
|
|
|
109910
111193
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
109911
111194
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
109912
111195
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
109913
|
-
const dir =
|
|
111196
|
+
const dir = path204.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
109914
111197
|
await mkdir65(dir, { recursive: true });
|
|
109915
|
-
const write = (name, value) => writeFile56(
|
|
111198
|
+
const write = (name, value) => writeFile56(path204.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
109916
111199
|
`, "utf8");
|
|
109917
111200
|
await Promise.all([
|
|
109918
111201
|
write("inputs.json", bundle.inputs),
|
|
@@ -109952,7 +111235,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109952
111235
|
console.log("# metrics status");
|
|
109953
111236
|
console.log("");
|
|
109954
111237
|
console.log(`root: ${root}`);
|
|
109955
|
-
console.log(`enabled: ${await Bun.file(
|
|
111238
|
+
console.log(`enabled: ${await Bun.file(path205.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
109956
111239
|
const latest = await readLatestPointer(root);
|
|
109957
111240
|
console.log(`latest: ${latest.status}`);
|
|
109958
111241
|
return;
|
|
@@ -109964,7 +111247,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109964
111247
|
process.exitCode = 1;
|
|
109965
111248
|
return;
|
|
109966
111249
|
}
|
|
109967
|
-
const record = JSON.parse(await readFile102(
|
|
111250
|
+
const record = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
109968
111251
|
const result = validateRunRecord(record);
|
|
109969
111252
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
109970
111253
|
for (const error of result.errors)
|
|
@@ -109989,7 +111272,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
109989
111272
|
process.exitCode = 1;
|
|
109990
111273
|
return;
|
|
109991
111274
|
}
|
|
109992
|
-
const file =
|
|
111275
|
+
const file = path205.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
109993
111276
|
if (!await Bun.file(file).exists()) {
|
|
109994
111277
|
console.error(`Run not found: ${runId}`);
|
|
109995
111278
|
process.exitCode = 1;
|
|
@@ -110006,8 +111289,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
110006
111289
|
process.exitCode = 1;
|
|
110007
111290
|
return;
|
|
110008
111291
|
}
|
|
110009
|
-
const a = JSON.parse(await readFile102(
|
|
110010
|
-
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"));
|
|
110011
111294
|
const comparison = compareExecutionRuns(a, b);
|
|
110012
111295
|
console.log(stableJson(comparison));
|
|
110013
111296
|
return;
|
|
@@ -110041,8 +111324,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
110041
111324
|
return;
|
|
110042
111325
|
}
|
|
110043
111326
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
110044
|
-
await Bun.write(
|
|
110045
|
-
console.log(`manifest: ${
|
|
111327
|
+
await Bun.write(path205.resolve(projectRoot, out), stableJson(template));
|
|
111328
|
+
console.log(`manifest: ${path205.relative(projectRoot, path205.resolve(projectRoot, out))}`);
|
|
110046
111329
|
return;
|
|
110047
111330
|
}
|
|
110048
111331
|
if (subcommand === "benchmark" && args[1] === "run") {
|
|
@@ -110056,7 +111339,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
110056
111339
|
process.exitCode = 1;
|
|
110057
111340
|
return;
|
|
110058
111341
|
}
|
|
110059
|
-
const raw = JSON.parse(await readFile102(
|
|
111342
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
110060
111343
|
const input = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
110061
111344
|
const result = validatePairedBenchmark(input);
|
|
110062
111345
|
console.log(stableJson(result));
|
|
@@ -110068,7 +111351,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
110068
111351
|
process.exitCode = 1;
|
|
110069
111352
|
}
|
|
110070
111353
|
async function loadAffectedSets(projectRoot, file) {
|
|
110071
|
-
const raw = JSON.parse(await readFile102(
|
|
111354
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
110072
111355
|
const map = new Map;
|
|
110073
111356
|
for (const entry of raw.targets ?? []) {
|
|
110074
111357
|
if (typeof entry.target === "string")
|
|
@@ -110139,7 +111422,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
|
|
|
110139
111422
|
let tasks;
|
|
110140
111423
|
let model;
|
|
110141
111424
|
try {
|
|
110142
|
-
const raw = JSON.parse(await readFile102(
|
|
111425
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, resultsPath), "utf8"));
|
|
110143
111426
|
tasks = raw.tasks ?? [];
|
|
110144
111427
|
model = raw.model;
|
|
110145
111428
|
} catch (error) {
|
|
@@ -110170,7 +111453,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
|
|
|
110170
111453
|
let cases;
|
|
110171
111454
|
let model;
|
|
110172
111455
|
try {
|
|
110173
|
-
const raw = JSON.parse(await readFile102(
|
|
111456
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, resultsPath), "utf8"));
|
|
110174
111457
|
cases = raw.cases ?? [];
|
|
110175
111458
|
model = raw.model;
|
|
110176
111459
|
} catch (error) {
|
|
@@ -110195,7 +111478,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
|
|
|
110195
111478
|
let cases;
|
|
110196
111479
|
let model;
|
|
110197
111480
|
try {
|
|
110198
|
-
const raw = JSON.parse(await readFile102(
|
|
111481
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, resultsPath), "utf8"));
|
|
110199
111482
|
cases = raw.cases ?? [];
|
|
110200
111483
|
model = raw.model;
|
|
110201
111484
|
} catch (error) {
|
|
@@ -110220,7 +111503,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
|
|
|
110220
111503
|
let cases;
|
|
110221
111504
|
let model;
|
|
110222
111505
|
try {
|
|
110223
|
-
const raw = JSON.parse(await readFile102(
|
|
111506
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, resultsPath), "utf8"));
|
|
110224
111507
|
cases = raw.cases ?? [];
|
|
110225
111508
|
model = raw.model;
|
|
110226
111509
|
} catch (error) {
|
|
@@ -110282,12 +111565,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
110282
111565
|
}
|
|
110283
111566
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
110284
111567
|
if (outDir) {
|
|
110285
|
-
const resolvedOut =
|
|
111568
|
+
const resolvedOut = path205.resolve(projectRoot, outDir);
|
|
110286
111569
|
for (const input of inputs) {
|
|
110287
111570
|
for (const named of input.golds) {
|
|
110288
111571
|
const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
110289
|
-
const dir = await persistEvidenceBundle(
|
|
110290
|
-
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)}`);
|
|
110291
111574
|
}
|
|
110292
111575
|
}
|
|
110293
111576
|
}
|
|
@@ -110311,7 +111594,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
110311
111594
|
return allValid;
|
|
110312
111595
|
}
|
|
110313
111596
|
async function loadCoverageMap2(projectRoot, file) {
|
|
110314
|
-
const raw = JSON.parse(await readFile102(
|
|
111597
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
110315
111598
|
return raw.coverageMap ?? {};
|
|
110316
111599
|
}
|
|
110317
111600
|
async function runTestingLayer(projectRoot, args, ladder) {
|
|
@@ -110348,7 +111631,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
|
|
|
110348
111631
|
return result.valid;
|
|
110349
111632
|
}
|
|
110350
111633
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
110351
|
-
const raw = JSON.parse(await readFile102(
|
|
111634
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
110352
111635
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
110353
111636
|
}
|
|
110354
111637
|
async function runMemoryLayer(projectRoot, args, ladder) {
|
|
@@ -110385,11 +111668,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
|
|
|
110385
111668
|
return result.valid;
|
|
110386
111669
|
}
|
|
110387
111670
|
async function loadWikiGoldK(projectRoot, file) {
|
|
110388
|
-
const raw = JSON.parse(await readFile102(
|
|
111671
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
110389
111672
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
110390
111673
|
}
|
|
110391
111674
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
110392
|
-
const raw = JSON.parse(await readFile102(
|
|
111675
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
110393
111676
|
const map = new Map;
|
|
110394
111677
|
for (const entry of raw.targets ?? []) {
|
|
110395
111678
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -110445,7 +111728,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
|
|
|
110445
111728
|
return result.valid;
|
|
110446
111729
|
}
|
|
110447
111730
|
async function loadGdctxFacts(projectRoot, file) {
|
|
110448
|
-
const raw = JSON.parse(await readFile102(
|
|
111731
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, file), "utf8"));
|
|
110449
111732
|
const inputs = [];
|
|
110450
111733
|
for (const entry of raw.inputs ?? []) {
|
|
110451
111734
|
if (typeof entry.input === "string") {
|
|
@@ -110483,7 +111766,7 @@ async function collect(projectRoot, args) {
|
|
|
110483
111766
|
process.exitCode = 1;
|
|
110484
111767
|
return;
|
|
110485
111768
|
}
|
|
110486
|
-
const raw = JSON.parse(await readFile102(
|
|
111769
|
+
const raw = JSON.parse(await readFile102(path205.resolve(projectRoot, eventFile), "utf8"));
|
|
110487
111770
|
const events = Array.isArray(raw) ? raw : raw.events;
|
|
110488
111771
|
const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
|
|
110489
111772
|
const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -110499,11 +111782,11 @@ async function collect(projectRoot, args) {
|
|
|
110499
111782
|
parentRunId: optionValue(args, "--parent-run-id") ?? null
|
|
110500
111783
|
});
|
|
110501
111784
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
|
|
110502
|
-
console.log(`json: ${
|
|
110503
|
-
console.log(`markdown: ${
|
|
111785
|
+
console.log(`json: ${path205.relative(projectRoot, result.jsonPath)}`);
|
|
111786
|
+
console.log(`markdown: ${path205.relative(projectRoot, result.markdownPath)}`);
|
|
110504
111787
|
}
|
|
110505
111788
|
function metricsRoot(projectRoot) {
|
|
110506
|
-
return
|
|
111789
|
+
return path205.join(projectRoot, ".metaproject", "data", "metrics");
|
|
110507
111790
|
}
|
|
110508
111791
|
function printMetricsHelp() {
|
|
110509
111792
|
console.log(`keryx metrics
|
|
@@ -110555,7 +111838,7 @@ async function versionCommand(args, deps = {}) {
|
|
|
110555
111838
|
// src/commands/workspace.ts
|
|
110556
111839
|
init_args();
|
|
110557
111840
|
init_workspace_service();
|
|
110558
|
-
import { randomUUID as
|
|
111841
|
+
import { randomUUID as randomUUID34 } from "crypto";
|
|
110559
111842
|
import { writeFile as writeFile57 } from "fs/promises";
|
|
110560
111843
|
|
|
110561
111844
|
// src/sac/fwk-explain.ts
|
|
@@ -110634,7 +111917,7 @@ async function workspaceCommand(args) {
|
|
|
110634
111917
|
const component = optionValue(args, "--component");
|
|
110635
111918
|
if (!title)
|
|
110636
111919
|
throw new Error("Usage: keryx workspace create --title <title> [--component <workspace-relative-ref>]");
|
|
110637
|
-
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 } } : {} });
|
|
110638
111921
|
console.log(JSON.stringify(workspace, null, 2));
|
|
110639
111922
|
return;
|
|
110640
111923
|
}
|
|
@@ -110664,7 +111947,7 @@ async function workspaceCommand(args) {
|
|
|
110664
111947
|
const revision = optionValue(args, "--revision");
|
|
110665
111948
|
if (!workspaceId || !kind || !uri)
|
|
110666
111949
|
throw new Error("Usage: keryx workspace add-resource <workspace-id> --kind <kind> --uri <workspace-relative-ref> [--revision <revision>]");
|
|
110667
|
-
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));
|
|
110668
111951
|
return;
|
|
110669
111952
|
}
|
|
110670
111953
|
if (subcommand === "archive") {
|
|
@@ -110672,7 +111955,7 @@ async function workspaceCommand(args) {
|
|
|
110672
111955
|
const workspaceId = args[1];
|
|
110673
111956
|
if (!workspaceId)
|
|
110674
111957
|
throw new Error("Usage: keryx workspace archive <workspace-id>");
|
|
110675
|
-
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));
|
|
110676
111959
|
return;
|
|
110677
111960
|
}
|
|
110678
111961
|
if (subcommand === "remove-resource") {
|
|
@@ -110681,7 +111964,7 @@ async function workspaceCommand(args) {
|
|
|
110681
111964
|
const uri = optionValue(args, "--uri");
|
|
110682
111965
|
if (!workspaceId || !uri)
|
|
110683
111966
|
throw new Error("Usage: keryx workspace remove-resource <workspace-id> --uri <workspace-relative-ref>");
|
|
110684
|
-
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));
|
|
110685
111968
|
return;
|
|
110686
111969
|
}
|
|
110687
111970
|
if (subcommand === "rename") {
|
|
@@ -110690,7 +111973,7 @@ async function workspaceCommand(args) {
|
|
|
110690
111973
|
const title = optionValue(args, "--title");
|
|
110691
111974
|
if (!workspaceId || !title)
|
|
110692
111975
|
throw new Error("Usage: keryx workspace rename <workspace-id> --title <title>");
|
|
110693
|
-
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));
|
|
110694
111977
|
return;
|
|
110695
111978
|
}
|
|
110696
111979
|
if (subcommand === "overview") {
|
|
@@ -110702,7 +111985,7 @@ async function workspaceCommand(args) {
|
|
|
110702
111985
|
const maxTokens = Number(optionValue(args, "--max-tokens") ?? "4096");
|
|
110703
111986
|
if (!Number.isInteger(maxItems) || !Number.isInteger(maxTokens) || maxItems < 0 || maxTokens < 0)
|
|
110704
111987
|
throw new Error("--max-items and --max-tokens must be non-negative integers");
|
|
110705
|
-
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 } });
|
|
110706
111989
|
const normalized = normalizeFwkResult(result);
|
|
110707
111990
|
console.log(JSON.stringify(normalized, null, 2));
|
|
110708
111991
|
if (args.includes("--explain"))
|
|
@@ -110719,7 +112002,7 @@ async function workspaceCommand(args) {
|
|
|
110719
112002
|
const maxTokens = Number(optionValue(args, "--max-tokens") ?? "4096");
|
|
110720
112003
|
if (!Number.isInteger(maxItems) || !Number.isInteger(maxTokens) || maxItems < 0 || maxTokens < 0)
|
|
110721
112004
|
throw new Error("--max-items and --max-tokens must be non-negative integers");
|
|
110722
|
-
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 } });
|
|
110723
112006
|
const normalized = normalizeFwkResult(result);
|
|
110724
112007
|
console.log(JSON.stringify(normalized, null, 2));
|
|
110725
112008
|
if (args.includes("--explain"))
|
|
@@ -110741,14 +112024,14 @@ async function workspaceCommand(args) {
|
|
|
110741
112024
|
const session = findSession(cwd, sessionRef);
|
|
110742
112025
|
if (!session)
|
|
110743
112026
|
throw new Error(`no session matching "${sessionRef}" in this project \u2014 use \`keryx sessions list\``);
|
|
110744
|
-
const proposalId = `proposal-${
|
|
112027
|
+
const proposalId = `proposal-${randomUUID34().replace(/-/g, "").slice(0, 16)}`;
|
|
110745
112028
|
const notePath = proposalNotePath(cwd, workspaceId, proposalId);
|
|
110746
112029
|
const materializedNote = note === undefined ? undefined : prepareOutputForPersistence(await guardOutput({ cwd, content: note, target: "task", source: "generated", path: notePath }), note);
|
|
110747
112030
|
if (materializedNote !== undefined && !materializedNote.allowed)
|
|
110748
112031
|
throw new Error(materializedNote.reason);
|
|
110749
112032
|
const persistedNote = materializedNote?.allowed ? materializedNote.content : undefined;
|
|
110750
112033
|
const { service, wrapUpAuthority, authorizationServer } = createHarnessProposalLifecycleService(cwd, { workspaceId, ...persistedNote ? { note: persistedNote } : {} });
|
|
110751
|
-
const requestCorrelationId =
|
|
112034
|
+
const requestCorrelationId = randomUUID34();
|
|
110752
112035
|
const actor = await authorizationServer.actorContextFor(undefined, requestCorrelationId);
|
|
110753
112036
|
if (!actor)
|
|
110754
112037
|
throw new Error("trusted ActorContext is required");
|
|
@@ -110788,13 +112071,13 @@ async function workspaceCommand(args) {
|
|
|
110788
112071
|
const proposalId = args[2];
|
|
110789
112072
|
const decision = optionValue(args, "--decision");
|
|
110790
112073
|
const reason = optionValue(args, "--reason");
|
|
110791
|
-
const idempotencyKey = optionValue(args, "--idempotency-key") ??
|
|
112074
|
+
const idempotencyKey = optionValue(args, "--idempotency-key") ?? randomUUID34();
|
|
110792
112075
|
const confirmToken = optionValue(args, "--confirm-token");
|
|
110793
112076
|
if (!workspaceId || !proposalId || !decision)
|
|
110794
112077
|
throw new Error("Usage: keryx workspace review <workspace-id> <proposal-id> --decision <accepted|rejected|dismissed> [--reason <reason>] [--idempotency-key <key>] [--confirm-token <token>]");
|
|
110795
112078
|
if (decision === "accepted" && !confirmToken)
|
|
110796
112079
|
throw new Error("--decision accepted requires --confirm-token \u2014 run `keryx workspace confirm-review " + workspaceId + " " + proposalId + "` first");
|
|
110797
|
-
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 } : {} });
|
|
110798
112081
|
console.log(JSON.stringify(normalizeProposalLifecycleResult(result), null, 2));
|
|
110799
112082
|
return;
|
|
110800
112083
|
}
|
|
@@ -110819,7 +112102,7 @@ async function workspaceCommand(args) {
|
|
|
110819
112102
|
const artifactRef = optionValue(args, "--artifact");
|
|
110820
112103
|
if (!workspaceId || !to || !artifactRef)
|
|
110821
112104
|
throw new Error("Usage: keryx workspace handoff <workspace-id> --to <subject> --artifact <ref>");
|
|
110822
|
-
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 } } });
|
|
110823
112106
|
console.log(JSON.stringify(activity, null, 2));
|
|
110824
112107
|
return;
|
|
110825
112108
|
}
|
|
@@ -110828,7 +112111,7 @@ async function workspaceCommand(args) {
|
|
|
110828
112111
|
const workspaceId = args[1];
|
|
110829
112112
|
if (!workspaceId)
|
|
110830
112113
|
throw new Error("Usage: keryx workspace collaboration <workspace-id>");
|
|
110831
|
-
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));
|
|
110832
112115
|
return;
|
|
110833
112116
|
}
|
|
110834
112117
|
if (subcommand === "policy-readiness") {
|
|
@@ -110855,7 +112138,7 @@ async function workspaceCommand(args) {
|
|
|
110855
112138
|
const workspaceId = args[1];
|
|
110856
112139
|
const cwd = process.cwd();
|
|
110857
112140
|
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
110858
|
-
const actor = await authorizationServer.actorContextFor(undefined,
|
|
112141
|
+
const actor = await authorizationServer.actorContextFor(undefined, randomUUID34());
|
|
110859
112142
|
if (!actor)
|
|
110860
112143
|
throw new Error("trusted ActorContext is required");
|
|
110861
112144
|
if (workspaceId) {
|
|
@@ -111594,13 +112877,17 @@ Commands:
|
|
|
111594
112877
|
forgetting Read the deletion trail \u2014 was this removed, or did it never exist?
|
|
111595
112878
|
`);
|
|
111596
112879
|
}
|
|
112880
|
+
function exitCodeForError(error) {
|
|
112881
|
+
return error instanceof ShellFlagError ? error.exitCode : 1;
|
|
112882
|
+
}
|
|
111597
112883
|
if (import.meta.main) {
|
|
111598
112884
|
main().catch((error) => {
|
|
111599
112885
|
console.error(error instanceof Error ? error.message : String(error));
|
|
111600
|
-
process.exitCode =
|
|
112886
|
+
process.exitCode = exitCodeForError(error);
|
|
111601
112887
|
});
|
|
111602
112888
|
}
|
|
111603
112889
|
export {
|
|
111604
112890
|
CLI_ROUTES,
|
|
112891
|
+
exitCodeForError,
|
|
111605
112892
|
main
|
|
111606
112893
|
};
|