@mrciphersmith/keryx 0.2.43 → 0.2.45
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 +1191 -650
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12095,6 +12095,10 @@ function touchesAgentCredentials(command) {
|
|
|
12095
12095
|
const text = command.toLowerCase();
|
|
12096
12096
|
return CREDENTIAL_MARKERS.some((marker2) => text.includes(marker2));
|
|
12097
12097
|
}
|
|
12098
|
+
function touchesSacConfirmReview(command) {
|
|
12099
|
+
const text = command.toLowerCase();
|
|
12100
|
+
return SAC_REVIEW_MARKERS.some((marker2) => text.includes(marker2));
|
|
12101
|
+
}
|
|
12098
12102
|
var CATASTROPHIC_TARGETS, RECURSIVE_FLAG, PRIVILEGE, INTERPRETERS, DOWNLOADERS, CONTAINER_RUNTIMES, PROTECTED_BRANCHES, BLOCK_DEVICE, rulePrivilege = (v) => PRIVILEGE.has(v.cmd), ruleBlockDeviceRedirect = (v) => BLOCK_DEVICE.test(v.raw) && /(?:^|\s)(?:>|>>)\s*\/dev\//.test(v.raw), ruleRm = (v) => v.cmd === "rm" && v.positionals.some(isCatastrophicTarget), ruleDd = (v) => v.cmd === "dd" && v.args.some((x) => /^of=/i.test(x) && BLOCK_DEVICE.test(x)), rulePermissionSweep = (v) => (v.cmd === "chmod" || v.cmd === "chown" || v.cmd === "chgrp") && hasRecursive(v.words) && v.positionals.some(isCatastrophicTarget), ruleHostState = (v) => {
|
|
12099
12103
|
if (v.cmd === "shutdown" || v.cmd === "reboot" || v.cmd === "halt" || v.cmd === "poweroff")
|
|
12100
12104
|
return true;
|
|
@@ -12115,7 +12119,7 @@ var CATASTROPHIC_TARGETS, RECURSIVE_FLAG, PRIVILEGE, INTERPRETERS, DOWNLOADERS,
|
|
|
12115
12119
|
return true;
|
|
12116
12120
|
const named = after.map((x) => x.split("/").pop()?.toLowerCase() ?? "");
|
|
12117
12121
|
return named.some((n) => PROTECTED_BRANCHES.includes(n));
|
|
12118
|
-
}, RULES3, CREDENTIAL_MARKERS;
|
|
12122
|
+
}, RULES3, CREDENTIAL_MARKERS, SAC_REVIEW_MARKERS;
|
|
12119
12123
|
var init_command_risk = __esm(() => {
|
|
12120
12124
|
init_shell_syntax();
|
|
12121
12125
|
CATASTROPHIC_TARGETS = new Set([
|
|
@@ -12180,6 +12184,7 @@ var init_command_risk = __esm(() => {
|
|
|
12180
12184
|
".local/share/keryx",
|
|
12181
12185
|
".config/keryx"
|
|
12182
12186
|
];
|
|
12187
|
+
SAC_REVIEW_MARKERS = ["confirm-review", "workspace review"];
|
|
12183
12188
|
});
|
|
12184
12189
|
|
|
12185
12190
|
// src/commands/permission-mode.ts
|
|
@@ -12187,11 +12192,11 @@ function isPermissionMode(value) {
|
|
|
12187
12192
|
return PERMISSION_MODES.includes(value);
|
|
12188
12193
|
}
|
|
12189
12194
|
function resolveApprovalDecision(input2) {
|
|
12190
|
-
const { mode, risk, destructive, credentials } = input2;
|
|
12195
|
+
const { mode, risk, destructive, credentials, sacReviewConfirmation } = input2;
|
|
12191
12196
|
if (risk === "read") {
|
|
12192
12197
|
return "auto";
|
|
12193
12198
|
}
|
|
12194
|
-
if (credentials) {
|
|
12199
|
+
if (credentials || sacReviewConfirmation) {
|
|
12195
12200
|
return "ask";
|
|
12196
12201
|
}
|
|
12197
12202
|
if (mode === "auto") {
|
|
@@ -12208,6 +12213,304 @@ var init_permission_mode = __esm(() => {
|
|
|
12208
12213
|
PERMISSION_MODES = ["ask", "trust", "auto"];
|
|
12209
12214
|
});
|
|
12210
12215
|
|
|
12216
|
+
// src/harness/policy/ranks.ts
|
|
12217
|
+
function axesOf(trustMode) {
|
|
12218
|
+
switch (trustMode) {
|
|
12219
|
+
case "read-only":
|
|
12220
|
+
return { authority: "read-only", inputTrust: "vetted" };
|
|
12221
|
+
case "trusted-local":
|
|
12222
|
+
return { authority: "acting", inputTrust: "vetted" };
|
|
12223
|
+
case "untrusted":
|
|
12224
|
+
return { authority: "acting", inputTrust: "unvetted" };
|
|
12225
|
+
default:
|
|
12226
|
+
return;
|
|
12227
|
+
}
|
|
12228
|
+
}
|
|
12229
|
+
function rankOf(map, value) {
|
|
12230
|
+
return Object.prototype.hasOwnProperty.call(map, value) ? map[value] : undefined;
|
|
12231
|
+
}
|
|
12232
|
+
function exceedingAxes(ceiling, candidate) {
|
|
12233
|
+
const low = axesOf(ceiling);
|
|
12234
|
+
const high = axesOf(candidate);
|
|
12235
|
+
if (low === undefined || high === undefined) {
|
|
12236
|
+
return [AUTHORITY_AXIS, INPUT_TRUST_AXIS];
|
|
12237
|
+
}
|
|
12238
|
+
const exceeded = [];
|
|
12239
|
+
if (AUTHORITY_RANK[high.authority] > AUTHORITY_RANK[low.authority]) {
|
|
12240
|
+
exceeded.push(AUTHORITY_AXIS);
|
|
12241
|
+
}
|
|
12242
|
+
if (INPUT_TRUST_RANK[high.inputTrust] > INPUT_TRUST_RANK[low.inputTrust]) {
|
|
12243
|
+
exceeded.push(INPUT_TRUST_AXIS);
|
|
12244
|
+
}
|
|
12245
|
+
return exceeded;
|
|
12246
|
+
}
|
|
12247
|
+
function broadeningAxes(parent, child) {
|
|
12248
|
+
const above = axesOf(parent);
|
|
12249
|
+
const below = axesOf(child);
|
|
12250
|
+
if (above === undefined || below === undefined) {
|
|
12251
|
+
return [AUTHORITY_AXIS, INPUT_TRUST_AXIS];
|
|
12252
|
+
}
|
|
12253
|
+
const broadened = [];
|
|
12254
|
+
if (AUTHORITY_RANK[below.authority] > AUTHORITY_RANK[above.authority]) {
|
|
12255
|
+
broadened.push(AUTHORITY_AXIS);
|
|
12256
|
+
}
|
|
12257
|
+
if (INPUT_TRUST_RANK[below.inputTrust] < INPUT_TRUST_RANK[above.inputTrust]) {
|
|
12258
|
+
broadened.push(INPUT_TRUST_AXIS);
|
|
12259
|
+
}
|
|
12260
|
+
return broadened;
|
|
12261
|
+
}
|
|
12262
|
+
var AUTHORITY_RANK, INPUT_TRUST_RANK, OUTCOME_RANK, ISOLATION_RANK, AUTHORITY_AXIS = "trustMode.authority", INPUT_TRUST_AXIS = "trustMode.inputTrust";
|
|
12263
|
+
var init_ranks = __esm(() => {
|
|
12264
|
+
AUTHORITY_RANK = {
|
|
12265
|
+
"read-only": 0,
|
|
12266
|
+
acting: 1
|
|
12267
|
+
};
|
|
12268
|
+
INPUT_TRUST_RANK = {
|
|
12269
|
+
unvetted: 0,
|
|
12270
|
+
vetted: 1
|
|
12271
|
+
};
|
|
12272
|
+
OUTCOME_RANK = {
|
|
12273
|
+
deny: 0,
|
|
12274
|
+
ask: 1,
|
|
12275
|
+
allow: 2
|
|
12276
|
+
};
|
|
12277
|
+
ISOLATION_RANK = {
|
|
12278
|
+
"not-required": 0,
|
|
12279
|
+
"required-fail-closed": 1
|
|
12280
|
+
};
|
|
12281
|
+
});
|
|
12282
|
+
|
|
12283
|
+
// src/harness/child/isolation.ts
|
|
12284
|
+
function inheritBudget(parentRemaining, childRequest) {
|
|
12285
|
+
if (childRequest.maxRuntimeMs > parentRemaining.maxRuntimeMs) {
|
|
12286
|
+
return {
|
|
12287
|
+
ok: false,
|
|
12288
|
+
reason: `child maxRuntimeMs ${childRequest.maxRuntimeMs} exceeds parent remaining ${parentRemaining.maxRuntimeMs}`
|
|
12289
|
+
};
|
|
12290
|
+
}
|
|
12291
|
+
if (childRequest.maxToolCalls !== undefined) {
|
|
12292
|
+
if (parentRemaining.maxToolCalls === undefined) {
|
|
12293
|
+
return {
|
|
12294
|
+
ok: false,
|
|
12295
|
+
reason: `child requests ${childRequest.maxToolCalls} tool calls but the parent exposes no tool-call budget to inherit`
|
|
12296
|
+
};
|
|
12297
|
+
}
|
|
12298
|
+
if (childRequest.maxToolCalls > parentRemaining.maxToolCalls) {
|
|
12299
|
+
return {
|
|
12300
|
+
ok: false,
|
|
12301
|
+
reason: `child maxToolCalls ${childRequest.maxToolCalls} exceeds parent remaining ${parentRemaining.maxToolCalls}`
|
|
12302
|
+
};
|
|
12303
|
+
}
|
|
12304
|
+
}
|
|
12305
|
+
const reservation = {
|
|
12306
|
+
reservationId: childRequest.reservationId,
|
|
12307
|
+
maxRuntimeMs: childRequest.maxRuntimeMs,
|
|
12308
|
+
...childRequest.maxToolCalls !== undefined ? { maxToolCalls: childRequest.maxToolCalls } : {}
|
|
12309
|
+
};
|
|
12310
|
+
return { ok: true, reservation };
|
|
12311
|
+
}
|
|
12312
|
+
function isKnownCapability(value) {
|
|
12313
|
+
return CAPABILITY_KEYS.includes(value);
|
|
12314
|
+
}
|
|
12315
|
+
function inheritPolicy(parent, childRequest) {
|
|
12316
|
+
if (axesOf(childRequest.trustMode) === undefined || axesOf(parent.trustMode) === undefined) {
|
|
12317
|
+
return {
|
|
12318
|
+
ok: false,
|
|
12319
|
+
reason: `unrecognized trustMode (child "${childRequest.trustMode}", parent "${parent.trustMode}")`
|
|
12320
|
+
};
|
|
12321
|
+
}
|
|
12322
|
+
const broadened = broadeningAxes(parent.trustMode, childRequest.trustMode);
|
|
12323
|
+
if (broadened.length > 0) {
|
|
12324
|
+
return {
|
|
12325
|
+
ok: false,
|
|
12326
|
+
reason: `child trustMode "${childRequest.trustMode}" is broader than parent "${parent.trustMode}" on ${broadened.join(", ")}`
|
|
12327
|
+
};
|
|
12328
|
+
}
|
|
12329
|
+
for (const capability of CAPABILITY_KEYS) {
|
|
12330
|
+
const childOutcome = childRequest.defaults[capability];
|
|
12331
|
+
const parentOutcome = parent.defaults[capability];
|
|
12332
|
+
const childRank = rankOf(OUTCOME_RANK, childOutcome);
|
|
12333
|
+
const parentRank = rankOf(OUTCOME_RANK, parentOutcome);
|
|
12334
|
+
if (childRank === undefined || parentRank === undefined) {
|
|
12335
|
+
return {
|
|
12336
|
+
ok: false,
|
|
12337
|
+
reason: `unrecognized capability outcome for "${capability}" (child "${childOutcome}", parent "${parentOutcome}")`
|
|
12338
|
+
};
|
|
12339
|
+
}
|
|
12340
|
+
if (childRank > parentRank) {
|
|
12341
|
+
return {
|
|
12342
|
+
ok: false,
|
|
12343
|
+
reason: `child capability "${capability}" default "${childOutcome}" is more permissive than parent "${parentOutcome}"`
|
|
12344
|
+
};
|
|
12345
|
+
}
|
|
12346
|
+
}
|
|
12347
|
+
const childIsolation = rankOf(ISOLATION_RANK, childRequest.requiredControls.isolation);
|
|
12348
|
+
const parentIsolation = rankOf(ISOLATION_RANK, parent.requiredControls.isolation);
|
|
12349
|
+
if (childIsolation === undefined || parentIsolation === undefined) {
|
|
12350
|
+
return {
|
|
12351
|
+
ok: false,
|
|
12352
|
+
reason: `unrecognized isolation control (child "${childRequest.requiredControls.isolation}", parent "${parent.requiredControls.isolation}")`
|
|
12353
|
+
};
|
|
12354
|
+
}
|
|
12355
|
+
if (childIsolation < parentIsolation) {
|
|
12356
|
+
return {
|
|
12357
|
+
ok: false,
|
|
12358
|
+
reason: `child isolation "${childRequest.requiredControls.isolation}" is weaker than parent "${parent.requiredControls.isolation}"`
|
|
12359
|
+
};
|
|
12360
|
+
}
|
|
12361
|
+
return { ok: true, policy: childRequest };
|
|
12362
|
+
}
|
|
12363
|
+
function childProvenance(parent, deps) {
|
|
12364
|
+
const taintIds = [...parent.taintIds ?? [], parent.provenanceId];
|
|
12365
|
+
const provenance = {
|
|
12366
|
+
provenanceId: deps.idSeq(),
|
|
12367
|
+
trustLevel: "derived",
|
|
12368
|
+
sourceKind: parent.sourceKind,
|
|
12369
|
+
taintIds
|
|
12370
|
+
};
|
|
12371
|
+
if (parent.sourceHash !== undefined) {
|
|
12372
|
+
provenance.sourceHash = parent.sourceHash;
|
|
12373
|
+
}
|
|
12374
|
+
return provenance;
|
|
12375
|
+
}
|
|
12376
|
+
var CAPABILITY_KEYS;
|
|
12377
|
+
var init_isolation = __esm(() => {
|
|
12378
|
+
init_ranks();
|
|
12379
|
+
CAPABILITY_KEYS = [
|
|
12380
|
+
"read",
|
|
12381
|
+
"write",
|
|
12382
|
+
"shell",
|
|
12383
|
+
"network",
|
|
12384
|
+
"delegate"
|
|
12385
|
+
];
|
|
12386
|
+
});
|
|
12387
|
+
|
|
12388
|
+
// src/harness/parallel/scheduler.ts
|
|
12389
|
+
function byTaskId(a, b) {
|
|
12390
|
+
return a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0;
|
|
12391
|
+
}
|
|
12392
|
+
function computeExcluded(tasks) {
|
|
12393
|
+
const excluded = new Set;
|
|
12394
|
+
for (const t of tasks) {
|
|
12395
|
+
if (t.cancelled === true)
|
|
12396
|
+
excluded.add(t.taskId);
|
|
12397
|
+
}
|
|
12398
|
+
let changed = true;
|
|
12399
|
+
while (changed) {
|
|
12400
|
+
changed = false;
|
|
12401
|
+
for (const t of tasks) {
|
|
12402
|
+
if (excluded.has(t.taskId))
|
|
12403
|
+
continue;
|
|
12404
|
+
if (t.dependsOn.some((dep) => excluded.has(dep))) {
|
|
12405
|
+
excluded.add(t.taskId);
|
|
12406
|
+
changed = true;
|
|
12407
|
+
}
|
|
12408
|
+
}
|
|
12409
|
+
}
|
|
12410
|
+
return excluded;
|
|
12411
|
+
}
|
|
12412
|
+
function decrementRemaining(remaining, reservation) {
|
|
12413
|
+
const maxRuntimeMs = remaining.maxRuntimeMs - reservation.maxRuntimeMs;
|
|
12414
|
+
if (remaining.maxToolCalls !== undefined && reservation.maxToolCalls !== undefined) {
|
|
12415
|
+
return { maxRuntimeMs, maxToolCalls: remaining.maxToolCalls - reservation.maxToolCalls };
|
|
12416
|
+
}
|
|
12417
|
+
return remaining.maxToolCalls !== undefined ? { maxRuntimeMs, maxToolCalls: remaining.maxToolCalls } : { maxRuntimeMs };
|
|
12418
|
+
}
|
|
12419
|
+
function planWaves(tasks, config, _deps) {
|
|
12420
|
+
if (!Number.isInteger(config.maxConcurrency) || config.maxConcurrency < 1) {
|
|
12421
|
+
return { ok: false, reason: `maxConcurrency must be a positive integer, got ${config.maxConcurrency}` };
|
|
12422
|
+
}
|
|
12423
|
+
const excluded = computeExcluded(tasks);
|
|
12424
|
+
const universe = tasks.filter((t) => !excluded.has(t.taskId));
|
|
12425
|
+
const scheduled = new Set;
|
|
12426
|
+
const waveTaskLists = [];
|
|
12427
|
+
while (scheduled.size < universe.length) {
|
|
12428
|
+
const ready = universe.filter((t) => !scheduled.has(t.taskId) && t.dependsOn.every((dep) => scheduled.has(dep))).sort(byTaskId);
|
|
12429
|
+
if (ready.length === 0) {
|
|
12430
|
+
return { ok: false, reason: "dependency cycle detected: no ready task set could be formed" };
|
|
12431
|
+
}
|
|
12432
|
+
const waveTasks = ready.slice(0, config.maxConcurrency);
|
|
12433
|
+
for (const t of waveTasks)
|
|
12434
|
+
scheduled.add(t.taskId);
|
|
12435
|
+
waveTaskLists.push(waveTasks);
|
|
12436
|
+
}
|
|
12437
|
+
let remaining = config.parentRemaining;
|
|
12438
|
+
const waves = [];
|
|
12439
|
+
for (const waveTasks of waveTaskLists) {
|
|
12440
|
+
const taskIds = [];
|
|
12441
|
+
const reservations = [];
|
|
12442
|
+
for (const t of waveTasks) {
|
|
12443
|
+
const granted = inheritBudget(remaining, t.budgetRequest);
|
|
12444
|
+
if (!granted.ok) {
|
|
12445
|
+
return { ok: false, reason: granted.reason };
|
|
12446
|
+
}
|
|
12447
|
+
taskIds.push(t.taskId);
|
|
12448
|
+
reservations.push(granted.reservation);
|
|
12449
|
+
remaining = decrementRemaining(remaining, granted.reservation);
|
|
12450
|
+
}
|
|
12451
|
+
waves.push({ taskIds, reservations });
|
|
12452
|
+
}
|
|
12453
|
+
return { ok: true, waves };
|
|
12454
|
+
}
|
|
12455
|
+
async function executeWaves(tasks, waves, deps) {
|
|
12456
|
+
const byTaskId2 = new Map;
|
|
12457
|
+
for (const t of tasks)
|
|
12458
|
+
byTaskId2.set(t.taskId, t);
|
|
12459
|
+
const results = new Map;
|
|
12460
|
+
for (let waveIndex = 0;waveIndex < waves.length; waveIndex++) {
|
|
12461
|
+
const wave = waves[waveIndex];
|
|
12462
|
+
if (wave === undefined)
|
|
12463
|
+
continue;
|
|
12464
|
+
const settled = await Promise.allSettled(wave.taskIds.map((taskId, i) => {
|
|
12465
|
+
const task = byTaskId2.get(taskId);
|
|
12466
|
+
if (task === undefined) {
|
|
12467
|
+
return Promise.reject(new Error(`executeWaves: wave ${waveIndex} references unknown taskId "${taskId}" (not in \`tasks\`)`));
|
|
12468
|
+
}
|
|
12469
|
+
const reservation = wave.reservations[i];
|
|
12470
|
+
if (reservation === undefined) {
|
|
12471
|
+
return Promise.reject(new Error(`executeWaves: wave ${waveIndex} taskIds/reservations length mismatch at index ${i} (malformed Wave)`));
|
|
12472
|
+
}
|
|
12473
|
+
return deps.run(task, reservation);
|
|
12474
|
+
}));
|
|
12475
|
+
const failedTaskIds = [];
|
|
12476
|
+
const causes = [];
|
|
12477
|
+
for (let i = 0;i < settled.length; i++) {
|
|
12478
|
+
const outcome = settled[i];
|
|
12479
|
+
const taskId = wave.taskIds[i];
|
|
12480
|
+
if (outcome === undefined || taskId === undefined)
|
|
12481
|
+
continue;
|
|
12482
|
+
if (outcome.status === "fulfilled") {
|
|
12483
|
+
results.set(taskId, outcome.value);
|
|
12484
|
+
} else {
|
|
12485
|
+
failedTaskIds.push(taskId);
|
|
12486
|
+
causes.push(outcome.reason);
|
|
12487
|
+
}
|
|
12488
|
+
}
|
|
12489
|
+
if (failedTaskIds.length > 0) {
|
|
12490
|
+
throw new WaveExecutionError(waveIndex, failedTaskIds, causes, new Map(results));
|
|
12491
|
+
}
|
|
12492
|
+
}
|
|
12493
|
+
return results;
|
|
12494
|
+
}
|
|
12495
|
+
var WaveExecutionError;
|
|
12496
|
+
var init_scheduler = __esm(() => {
|
|
12497
|
+
init_isolation();
|
|
12498
|
+
WaveExecutionError = class WaveExecutionError extends Error {
|
|
12499
|
+
waveIndex;
|
|
12500
|
+
failedTaskIds;
|
|
12501
|
+
causes;
|
|
12502
|
+
partialResults;
|
|
12503
|
+
constructor(waveIndex, failedTaskIds, causes, partialResults) {
|
|
12504
|
+
super(`executeWaves: wave ${waveIndex} had ${failedTaskIds.length} rejected task(s): ${failedTaskIds.join(", ")}`);
|
|
12505
|
+
this.name = "WaveExecutionError";
|
|
12506
|
+
this.waveIndex = waveIndex;
|
|
12507
|
+
this.failedTaskIds = failedTaskIds;
|
|
12508
|
+
this.causes = causes;
|
|
12509
|
+
this.partialResults = partialResults;
|
|
12510
|
+
}
|
|
12511
|
+
};
|
|
12512
|
+
});
|
|
12513
|
+
|
|
12211
12514
|
// src/ctx/assembly.ts
|
|
12212
12515
|
import { createHash as createHash6 } from "crypto";
|
|
12213
12516
|
import { mkdir as mkdir24, rename as rename2, writeFile as writeFile26 } from "fs/promises";
|
|
@@ -17198,7 +17501,7 @@ ${block}
|
|
|
17198
17501
|
}
|
|
17199
17502
|
async function runAgentTurn(io, deps, history, userLine, options = {}) {
|
|
17200
17503
|
try {
|
|
17201
|
-
await runAgentTurnCore(io, deps, history, userLine, options);
|
|
17504
|
+
return await runAgentTurnCore(io, deps, history, userLine, options);
|
|
17202
17505
|
} finally {
|
|
17203
17506
|
await closeSlateOnFlowDone(io, deps, options);
|
|
17204
17507
|
}
|
|
@@ -17240,7 +17543,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17240
17543
|
io.onSystem?.(`
|
|
17241
17544
|
[stopped] Model turn interrupted by user.
|
|
17242
17545
|
`);
|
|
17243
|
-
return;
|
|
17546
|
+
return {};
|
|
17244
17547
|
}
|
|
17245
17548
|
const toolByName = new Map(deps.tools.map((t) => [t.definition.name, t]));
|
|
17246
17549
|
const toolDefs = deps.tools.map((t) => t.definition);
|
|
@@ -17349,7 +17652,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17349
17652
|
system(`
|
|
17350
17653
|
[stopped] Model turn interrupted by user.
|
|
17351
17654
|
`);
|
|
17352
|
-
return;
|
|
17655
|
+
return {};
|
|
17353
17656
|
}
|
|
17354
17657
|
if (event.kind === "reasoning_delta") {
|
|
17355
17658
|
reasoningText += event.text ?? "";
|
|
@@ -17396,7 +17699,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17396
17699
|
system(`
|
|
17397
17700
|
[stopped] Model turn interrupted by user.
|
|
17398
17701
|
`);
|
|
17399
|
-
return;
|
|
17702
|
+
return {};
|
|
17400
17703
|
}
|
|
17401
17704
|
system(`
|
|
17402
17705
|
[error] ${cause instanceof Error ? cause.message : String(cause)}
|
|
@@ -17412,10 +17715,10 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17412
17715
|
system(`
|
|
17413
17716
|
[stopped] Model turn interrupted by user.
|
|
17414
17717
|
`);
|
|
17415
|
-
return;
|
|
17718
|
+
return {};
|
|
17416
17719
|
}
|
|
17417
17720
|
if (errored) {
|
|
17418
|
-
return;
|
|
17721
|
+
return {};
|
|
17419
17722
|
}
|
|
17420
17723
|
if (calls.length === 0) {
|
|
17421
17724
|
const shouldReprompt = actionRequest && (assistantText.length === 0 || modelClaimedAction(assistantText));
|
|
@@ -17436,27 +17739,35 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17436
17739
|
system(`
|
|
17437
17740
|
[warning] The provider/model did not emit a tool call for an explicit action request. ` + "Use a chat-safe fallback (`keryx shell --chat`) or switch to a tool-capable model.\n");
|
|
17438
17741
|
}
|
|
17439
|
-
return;
|
|
17742
|
+
return {};
|
|
17440
17743
|
}
|
|
17441
17744
|
if (isAborted()) {
|
|
17442
17745
|
system(`
|
|
17443
17746
|
[stopped] Model turn interrupted by user.
|
|
17444
17747
|
`);
|
|
17445
|
-
return;
|
|
17748
|
+
return {};
|
|
17446
17749
|
}
|
|
17447
17750
|
let exhaustedBudget;
|
|
17448
17751
|
let executedAny = false;
|
|
17449
17752
|
const batchContainsUntrustedWeb = calls.some((call) => call.name === "web_fetch" || call.name === "web_search");
|
|
17753
|
+
const reservationByCallId = new Map;
|
|
17754
|
+
for (const call of calls) {
|
|
17755
|
+
const callRisk = toolByName.get(call.name)?.definition.risk;
|
|
17756
|
+
reservationByCallId.set(call.id, reserveToolAttempt(budget, call.name, call.input, callRisk));
|
|
17757
|
+
}
|
|
17758
|
+
const spawnConcurrencyCandidates = calls.filter((call) => call.name === "spawn_subagent" && reservationByCallId.get(call.id)?.ok === true);
|
|
17759
|
+
const untrustedGateBlocksSpawns = untrustedContentSeen || batchContainsUntrustedWeb;
|
|
17760
|
+
const concurrentSpawnResults = spawnConcurrencyCandidates.length >= 2 && !untrustedGateBlocksSpawns ? await runConcurrentSpawnBatch(spawnConcurrencyCandidates, toolByName, io, deps) : undefined;
|
|
17450
17761
|
for (const call of calls) {
|
|
17451
17762
|
if (isAborted()) {
|
|
17452
17763
|
system(`
|
|
17453
17764
|
[stopped] Model turn interrupted by user.
|
|
17454
17765
|
`);
|
|
17455
|
-
return;
|
|
17766
|
+
return {};
|
|
17456
17767
|
}
|
|
17457
17768
|
if (deps.unattended === true && call.name === "ask_user") {
|
|
17458
17769
|
await emitTerminalState(io, deps, options, "ask_user_unanswerable");
|
|
17459
|
-
return;
|
|
17770
|
+
return {};
|
|
17460
17771
|
}
|
|
17461
17772
|
if (untrustedContentSeen || batchContainsUntrustedWeb && call.name !== "web_fetch" && call.name !== "web_search") {
|
|
17462
17773
|
const result2 = {
|
|
@@ -17470,7 +17781,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17470
17781
|
}
|
|
17471
17782
|
io.onToolCall?.(call.name, call.input);
|
|
17472
17783
|
const risk = toolByName.get(call.name)?.definition.risk;
|
|
17473
|
-
const reservation = reserveToolAttempt(budget, call.name, call.input, risk);
|
|
17784
|
+
const reservation = reservationByCallId.get(call.id) ?? reserveToolAttempt(budget, call.name, call.input, risk);
|
|
17474
17785
|
if (!reservation.ok) {
|
|
17475
17786
|
const result2 = { output: reservation.reason, isError: true };
|
|
17476
17787
|
io.onToolResult?.(call.name, result2);
|
|
@@ -17487,7 +17798,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
17487
17798
|
continue;
|
|
17488
17799
|
}
|
|
17489
17800
|
executedAny = true;
|
|
17490
|
-
const result = await executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.onAutoApproved);
|
|
17801
|
+
const result = concurrentSpawnResults?.get(call.id) ?? await executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.onAutoApproved);
|
|
17491
17802
|
io.onToolResult?.(call.name, result);
|
|
17492
17803
|
const modelOutput = redactSensitiveText(result.output);
|
|
17493
17804
|
history.push({
|
|
@@ -17539,9 +17850,10 @@ ${hint}
|
|
|
17539
17850
|
}
|
|
17540
17851
|
const noProgress = !executedAny && calls.length > 0;
|
|
17541
17852
|
if (exhaustedBudget !== undefined || noProgress) {
|
|
17853
|
+
const finishReason = exhaustedBudget !== undefined ? "budget" : "no-progress";
|
|
17542
17854
|
if (deps.unattended === true) {
|
|
17543
17855
|
await emitTerminalState(io, deps, options, "budget_exhausted");
|
|
17544
|
-
return;
|
|
17856
|
+
return { finishReason };
|
|
17545
17857
|
}
|
|
17546
17858
|
await finishWithBudgetSummary(io, deps, history, parentRunId, {
|
|
17547
17859
|
maxUnique: maxToolCalls,
|
|
@@ -17555,7 +17867,7 @@ ${hint}
|
|
|
17555
17867
|
...exhaustedBudget !== undefined ? { exhaustedBudget } : {},
|
|
17556
17868
|
noProgress
|
|
17557
17869
|
});
|
|
17558
|
-
return;
|
|
17870
|
+
return { finishReason };
|
|
17559
17871
|
}
|
|
17560
17872
|
}
|
|
17561
17873
|
}
|
|
@@ -17647,6 +17959,68 @@ function isApprovalFor(response, fingerprint) {
|
|
|
17647
17959
|
}
|
|
17648
17960
|
return response.fingerprint === undefined || response.fingerprint === fingerprint;
|
|
17649
17961
|
}
|
|
17962
|
+
async function runConcurrentSpawnBatch(spawnCalls, toolByName, io, deps) {
|
|
17963
|
+
const maxConcurrency = deps.maxSubagentConcurrency ?? DEFAULT_MAX_SUBAGENT_CONCURRENCY;
|
|
17964
|
+
const perTaskRuntimeMs = NOMINAL_CONCURRENT_SPAWN_RUNTIME_MS;
|
|
17965
|
+
const tasks = spawnCalls.map((call) => ({
|
|
17966
|
+
taskId: call.id,
|
|
17967
|
+
dependsOn: [],
|
|
17968
|
+
budgetRequest: { reservationId: call.id, maxRuntimeMs: perTaskRuntimeMs }
|
|
17969
|
+
}));
|
|
17970
|
+
const runOne = (call) => executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.onAutoApproved);
|
|
17971
|
+
const plan = planWaves(tasks, {
|
|
17972
|
+
maxConcurrency,
|
|
17973
|
+
parentRemaining: { maxRuntimeMs: perTaskRuntimeMs * tasks.length }
|
|
17974
|
+
});
|
|
17975
|
+
if (!plan.ok) {
|
|
17976
|
+
io.onSystem?.(`
|
|
17977
|
+
[warning] concurrent subagent wave planning denied (${plan.reason}); running sequentially.
|
|
17978
|
+
`);
|
|
17979
|
+
const results = new Map;
|
|
17980
|
+
for (const call of spawnCalls) {
|
|
17981
|
+
try {
|
|
17982
|
+
results.set(call.id, await runOne(call));
|
|
17983
|
+
} catch (cause) {
|
|
17984
|
+
const message2 = cause instanceof Error ? cause.message : String(cause);
|
|
17985
|
+
results.set(call.id, {
|
|
17986
|
+
output: `subagent call ${call.id} failed: sequential fallback error: ${message2}`,
|
|
17987
|
+
isError: true
|
|
17988
|
+
});
|
|
17989
|
+
}
|
|
17990
|
+
}
|
|
17991
|
+
return results;
|
|
17992
|
+
}
|
|
17993
|
+
try {
|
|
17994
|
+
return await executeWaves(tasks, plan.waves, {
|
|
17995
|
+
run: (task) => {
|
|
17996
|
+
const call = spawnCalls.find((c) => c.id === task.taskId);
|
|
17997
|
+
if (call === undefined) {
|
|
17998
|
+
return Promise.resolve({
|
|
17999
|
+
output: `internal error: unknown concurrent spawn taskId ${task.taskId}`,
|
|
18000
|
+
isError: true
|
|
18001
|
+
});
|
|
18002
|
+
}
|
|
18003
|
+
return runOne(call);
|
|
18004
|
+
}
|
|
18005
|
+
});
|
|
18006
|
+
} catch (cause) {
|
|
18007
|
+
const message2 = cause instanceof Error ? cause.message : String(cause);
|
|
18008
|
+
const isWaveError = cause instanceof WaveExecutionError;
|
|
18009
|
+
io.onSystem?.(`
|
|
18010
|
+
[warning] concurrent subagent wave failed${isWaveError ? "" : " (unexpected)"} (degraded): ${message2}
|
|
18011
|
+
`);
|
|
18012
|
+
const partialResults = cause instanceof WaveExecutionError ? cause.partialResults : undefined;
|
|
18013
|
+
const results = new Map;
|
|
18014
|
+
for (const call of spawnCalls) {
|
|
18015
|
+
const settled = partialResults?.get(call.id);
|
|
18016
|
+
results.set(call.id, settled ?? {
|
|
18017
|
+
output: `subagent call ${call.id} failed: concurrent wave error: ${message2}`,
|
|
18018
|
+
isError: true
|
|
18019
|
+
});
|
|
18020
|
+
}
|
|
18021
|
+
return results;
|
|
18022
|
+
}
|
|
18023
|
+
}
|
|
17650
18024
|
async function executeCall(call, toolByName, requestApproval, permissionMode, onAutoApproved) {
|
|
17651
18025
|
const tool = toolByName.get(call.name);
|
|
17652
18026
|
if (tool === undefined) {
|
|
@@ -17667,7 +18041,8 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
17667
18041
|
const command = typeof input2.command === "string" ? input2.command : "";
|
|
17668
18042
|
const destructive = risk === "destructive" || isDestructiveCommand(command);
|
|
17669
18043
|
const credentials = touchesAgentCredentials(command);
|
|
17670
|
-
const
|
|
18044
|
+
const sacReviewConfirmation = touchesSacConfirmReview(command);
|
|
18045
|
+
const decision = resolveApprovalDecision({ mode, risk, destructive, credentials, sacReviewConfirmation });
|
|
17671
18046
|
if (decision === "auto") {
|
|
17672
18047
|
onAutoApproved?.(call.name, call.input, { destructive, credentials });
|
|
17673
18048
|
} else {
|
|
@@ -17682,7 +18057,7 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
17682
18057
|
}
|
|
17683
18058
|
}
|
|
17684
18059
|
} else if (risk === "delegate") {
|
|
17685
|
-
const decision = resolveApprovalDecision({ mode, risk, destructive: false, credentials: false });
|
|
18060
|
+
const decision = resolveApprovalDecision({ mode, risk, destructive: false, credentials: false, sacReviewConfirmation: false });
|
|
17686
18061
|
if (decision === "auto") {
|
|
17687
18062
|
onAutoApproved?.(call.name, call.input, { destructive: false, credentials: false });
|
|
17688
18063
|
} else {
|
|
@@ -17697,190 +18072,20 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
17697
18072
|
}
|
|
17698
18073
|
return tool.invoke(input2);
|
|
17699
18074
|
}
|
|
17700
|
-
var DEFAULT_MAX_TOOL_CALLS = 48, ENV_AGENT_MAX_TOOL_CALLS = "KERYX_AGENT_MAX_TOOL_CALLS", MAX_AGENT_MAX_TOOL_CALLS = 256, DEFAULT_MAX_READ_TOOL_CALLS = 40, DEFAULT_MAX_NON_READ_TOOL_CALLS = 8, MAX_ATTEMPTS_PER_HASH = 3, ENV_AGENT_MAX_ATTEMPTS_PER_HASH = "KERYX_AGENT_MAX_ATTEMPTS_PER_HASH", MAX_AGENT_MAX_ATTEMPTS_PER_HASH = 10, REPEAT_FAILURE_HINT_THRESHOLD = 2, MAX_TOOLLESS_REPROMPTS = 1;
|
|
18075
|
+
var DEFAULT_MAX_TOOL_CALLS = 48, ENV_AGENT_MAX_TOOL_CALLS = "KERYX_AGENT_MAX_TOOL_CALLS", MAX_AGENT_MAX_TOOL_CALLS = 256, DEFAULT_MAX_READ_TOOL_CALLS = 40, DEFAULT_MAX_NON_READ_TOOL_CALLS = 8, DEFAULT_MAX_SUBAGENT_CONCURRENCY = 3, MAX_ATTEMPTS_PER_HASH = 3, ENV_AGENT_MAX_ATTEMPTS_PER_HASH = "KERYX_AGENT_MAX_ATTEMPTS_PER_HASH", MAX_AGENT_MAX_ATTEMPTS_PER_HASH = 10, REPEAT_FAILURE_HINT_THRESHOLD = 2, MAX_TOOLLESS_REPROMPTS = 1, NOMINAL_CONCURRENT_SPAWN_RUNTIME_MS;
|
|
17701
18076
|
var init_agent = __esm(() => {
|
|
17702
18077
|
init_validator();
|
|
17703
18078
|
init_command_risk();
|
|
17704
18079
|
init_permission_mode();
|
|
17705
18080
|
init_redact();
|
|
18081
|
+
init_scheduler();
|
|
17706
18082
|
init_slate();
|
|
17707
18083
|
init_slate_course();
|
|
17708
18084
|
init_workspace_resolve();
|
|
17709
18085
|
init_machine_wrap_up();
|
|
17710
18086
|
init_slate_lifecycle();
|
|
17711
18087
|
init_slate_terminal_state();
|
|
17712
|
-
|
|
17713
|
-
|
|
17714
|
-
// src/harness/policy/ranks.ts
|
|
17715
|
-
function axesOf(trustMode) {
|
|
17716
|
-
switch (trustMode) {
|
|
17717
|
-
case "read-only":
|
|
17718
|
-
return { authority: "read-only", inputTrust: "vetted" };
|
|
17719
|
-
case "trusted-local":
|
|
17720
|
-
return { authority: "acting", inputTrust: "vetted" };
|
|
17721
|
-
case "untrusted":
|
|
17722
|
-
return { authority: "acting", inputTrust: "unvetted" };
|
|
17723
|
-
default:
|
|
17724
|
-
return;
|
|
17725
|
-
}
|
|
17726
|
-
}
|
|
17727
|
-
function rankOf(map, value) {
|
|
17728
|
-
return Object.prototype.hasOwnProperty.call(map, value) ? map[value] : undefined;
|
|
17729
|
-
}
|
|
17730
|
-
function exceedingAxes(ceiling, candidate) {
|
|
17731
|
-
const low = axesOf(ceiling);
|
|
17732
|
-
const high = axesOf(candidate);
|
|
17733
|
-
if (low === undefined || high === undefined) {
|
|
17734
|
-
return [AUTHORITY_AXIS, INPUT_TRUST_AXIS];
|
|
17735
|
-
}
|
|
17736
|
-
const exceeded = [];
|
|
17737
|
-
if (AUTHORITY_RANK[high.authority] > AUTHORITY_RANK[low.authority]) {
|
|
17738
|
-
exceeded.push(AUTHORITY_AXIS);
|
|
17739
|
-
}
|
|
17740
|
-
if (INPUT_TRUST_RANK[high.inputTrust] > INPUT_TRUST_RANK[low.inputTrust]) {
|
|
17741
|
-
exceeded.push(INPUT_TRUST_AXIS);
|
|
17742
|
-
}
|
|
17743
|
-
return exceeded;
|
|
17744
|
-
}
|
|
17745
|
-
function broadeningAxes(parent, child) {
|
|
17746
|
-
const above = axesOf(parent);
|
|
17747
|
-
const below = axesOf(child);
|
|
17748
|
-
if (above === undefined || below === undefined) {
|
|
17749
|
-
return [AUTHORITY_AXIS, INPUT_TRUST_AXIS];
|
|
17750
|
-
}
|
|
17751
|
-
const broadened = [];
|
|
17752
|
-
if (AUTHORITY_RANK[below.authority] > AUTHORITY_RANK[above.authority]) {
|
|
17753
|
-
broadened.push(AUTHORITY_AXIS);
|
|
17754
|
-
}
|
|
17755
|
-
if (INPUT_TRUST_RANK[below.inputTrust] < INPUT_TRUST_RANK[above.inputTrust]) {
|
|
17756
|
-
broadened.push(INPUT_TRUST_AXIS);
|
|
17757
|
-
}
|
|
17758
|
-
return broadened;
|
|
17759
|
-
}
|
|
17760
|
-
var AUTHORITY_RANK, INPUT_TRUST_RANK, OUTCOME_RANK, ISOLATION_RANK, AUTHORITY_AXIS = "trustMode.authority", INPUT_TRUST_AXIS = "trustMode.inputTrust";
|
|
17761
|
-
var init_ranks = __esm(() => {
|
|
17762
|
-
AUTHORITY_RANK = {
|
|
17763
|
-
"read-only": 0,
|
|
17764
|
-
acting: 1
|
|
17765
|
-
};
|
|
17766
|
-
INPUT_TRUST_RANK = {
|
|
17767
|
-
unvetted: 0,
|
|
17768
|
-
vetted: 1
|
|
17769
|
-
};
|
|
17770
|
-
OUTCOME_RANK = {
|
|
17771
|
-
deny: 0,
|
|
17772
|
-
ask: 1,
|
|
17773
|
-
allow: 2
|
|
17774
|
-
};
|
|
17775
|
-
ISOLATION_RANK = {
|
|
17776
|
-
"not-required": 0,
|
|
17777
|
-
"required-fail-closed": 1
|
|
17778
|
-
};
|
|
17779
|
-
});
|
|
17780
|
-
|
|
17781
|
-
// src/harness/child/isolation.ts
|
|
17782
|
-
function inheritBudget(parentRemaining, childRequest) {
|
|
17783
|
-
if (childRequest.maxRuntimeMs > parentRemaining.maxRuntimeMs) {
|
|
17784
|
-
return {
|
|
17785
|
-
ok: false,
|
|
17786
|
-
reason: `child maxRuntimeMs ${childRequest.maxRuntimeMs} exceeds parent remaining ${parentRemaining.maxRuntimeMs}`
|
|
17787
|
-
};
|
|
17788
|
-
}
|
|
17789
|
-
if (childRequest.maxToolCalls !== undefined) {
|
|
17790
|
-
if (parentRemaining.maxToolCalls === undefined) {
|
|
17791
|
-
return {
|
|
17792
|
-
ok: false,
|
|
17793
|
-
reason: `child requests ${childRequest.maxToolCalls} tool calls but the parent exposes no tool-call budget to inherit`
|
|
17794
|
-
};
|
|
17795
|
-
}
|
|
17796
|
-
if (childRequest.maxToolCalls > parentRemaining.maxToolCalls) {
|
|
17797
|
-
return {
|
|
17798
|
-
ok: false,
|
|
17799
|
-
reason: `child maxToolCalls ${childRequest.maxToolCalls} exceeds parent remaining ${parentRemaining.maxToolCalls}`
|
|
17800
|
-
};
|
|
17801
|
-
}
|
|
17802
|
-
}
|
|
17803
|
-
const reservation = {
|
|
17804
|
-
reservationId: childRequest.reservationId,
|
|
17805
|
-
maxRuntimeMs: childRequest.maxRuntimeMs,
|
|
17806
|
-
...childRequest.maxToolCalls !== undefined ? { maxToolCalls: childRequest.maxToolCalls } : {}
|
|
17807
|
-
};
|
|
17808
|
-
return { ok: true, reservation };
|
|
17809
|
-
}
|
|
17810
|
-
function isKnownCapability(value) {
|
|
17811
|
-
return CAPABILITY_KEYS.includes(value);
|
|
17812
|
-
}
|
|
17813
|
-
function inheritPolicy(parent, childRequest) {
|
|
17814
|
-
if (axesOf(childRequest.trustMode) === undefined || axesOf(parent.trustMode) === undefined) {
|
|
17815
|
-
return {
|
|
17816
|
-
ok: false,
|
|
17817
|
-
reason: `unrecognized trustMode (child "${childRequest.trustMode}", parent "${parent.trustMode}")`
|
|
17818
|
-
};
|
|
17819
|
-
}
|
|
17820
|
-
const broadened = broadeningAxes(parent.trustMode, childRequest.trustMode);
|
|
17821
|
-
if (broadened.length > 0) {
|
|
17822
|
-
return {
|
|
17823
|
-
ok: false,
|
|
17824
|
-
reason: `child trustMode "${childRequest.trustMode}" is broader than parent "${parent.trustMode}" on ${broadened.join(", ")}`
|
|
17825
|
-
};
|
|
17826
|
-
}
|
|
17827
|
-
for (const capability of CAPABILITY_KEYS) {
|
|
17828
|
-
const childOutcome = childRequest.defaults[capability];
|
|
17829
|
-
const parentOutcome = parent.defaults[capability];
|
|
17830
|
-
const childRank = rankOf(OUTCOME_RANK, childOutcome);
|
|
17831
|
-
const parentRank = rankOf(OUTCOME_RANK, parentOutcome);
|
|
17832
|
-
if (childRank === undefined || parentRank === undefined) {
|
|
17833
|
-
return {
|
|
17834
|
-
ok: false,
|
|
17835
|
-
reason: `unrecognized capability outcome for "${capability}" (child "${childOutcome}", parent "${parentOutcome}")`
|
|
17836
|
-
};
|
|
17837
|
-
}
|
|
17838
|
-
if (childRank > parentRank) {
|
|
17839
|
-
return {
|
|
17840
|
-
ok: false,
|
|
17841
|
-
reason: `child capability "${capability}" default "${childOutcome}" is more permissive than parent "${parentOutcome}"`
|
|
17842
|
-
};
|
|
17843
|
-
}
|
|
17844
|
-
}
|
|
17845
|
-
const childIsolation = rankOf(ISOLATION_RANK, childRequest.requiredControls.isolation);
|
|
17846
|
-
const parentIsolation = rankOf(ISOLATION_RANK, parent.requiredControls.isolation);
|
|
17847
|
-
if (childIsolation === undefined || parentIsolation === undefined) {
|
|
17848
|
-
return {
|
|
17849
|
-
ok: false,
|
|
17850
|
-
reason: `unrecognized isolation control (child "${childRequest.requiredControls.isolation}", parent "${parent.requiredControls.isolation}")`
|
|
17851
|
-
};
|
|
17852
|
-
}
|
|
17853
|
-
if (childIsolation < parentIsolation) {
|
|
17854
|
-
return {
|
|
17855
|
-
ok: false,
|
|
17856
|
-
reason: `child isolation "${childRequest.requiredControls.isolation}" is weaker than parent "${parent.requiredControls.isolation}"`
|
|
17857
|
-
};
|
|
17858
|
-
}
|
|
17859
|
-
return { ok: true, policy: childRequest };
|
|
17860
|
-
}
|
|
17861
|
-
function childProvenance(parent, deps) {
|
|
17862
|
-
const taintIds = [...parent.taintIds ?? [], parent.provenanceId];
|
|
17863
|
-
const provenance = {
|
|
17864
|
-
provenanceId: deps.idSeq(),
|
|
17865
|
-
trustLevel: "derived",
|
|
17866
|
-
sourceKind: parent.sourceKind,
|
|
17867
|
-
taintIds
|
|
17868
|
-
};
|
|
17869
|
-
if (parent.sourceHash !== undefined) {
|
|
17870
|
-
provenance.sourceHash = parent.sourceHash;
|
|
17871
|
-
}
|
|
17872
|
-
return provenance;
|
|
17873
|
-
}
|
|
17874
|
-
var CAPABILITY_KEYS;
|
|
17875
|
-
var init_isolation = __esm(() => {
|
|
17876
|
-
init_ranks();
|
|
17877
|
-
CAPABILITY_KEYS = [
|
|
17878
|
-
"read",
|
|
17879
|
-
"write",
|
|
17880
|
-
"shell",
|
|
17881
|
-
"network",
|
|
17882
|
-
"delegate"
|
|
17883
|
-
];
|
|
18088
|
+
NOMINAL_CONCURRENT_SPAWN_RUNTIME_MS = 5 * 60000;
|
|
17884
18089
|
});
|
|
17885
18090
|
|
|
17886
18091
|
// src/harness/child/ledger.ts
|
|
@@ -44890,76 +45095,8 @@ function evaluateExtensionGrant(input2, deps) {
|
|
|
44890
45095
|
return { ok: true };
|
|
44891
45096
|
}
|
|
44892
45097
|
|
|
44893
|
-
// src/harness/parallel/scheduler.ts
|
|
44894
|
-
init_isolation();
|
|
44895
|
-
function byTaskId(a, b) {
|
|
44896
|
-
return a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0;
|
|
44897
|
-
}
|
|
44898
|
-
function computeExcluded(tasks) {
|
|
44899
|
-
const excluded = new Set;
|
|
44900
|
-
for (const t of tasks) {
|
|
44901
|
-
if (t.cancelled === true)
|
|
44902
|
-
excluded.add(t.taskId);
|
|
44903
|
-
}
|
|
44904
|
-
let changed = true;
|
|
44905
|
-
while (changed) {
|
|
44906
|
-
changed = false;
|
|
44907
|
-
for (const t of tasks) {
|
|
44908
|
-
if (excluded.has(t.taskId))
|
|
44909
|
-
continue;
|
|
44910
|
-
if (t.dependsOn.some((dep) => excluded.has(dep))) {
|
|
44911
|
-
excluded.add(t.taskId);
|
|
44912
|
-
changed = true;
|
|
44913
|
-
}
|
|
44914
|
-
}
|
|
44915
|
-
}
|
|
44916
|
-
return excluded;
|
|
44917
|
-
}
|
|
44918
|
-
function decrementRemaining(remaining, reservation) {
|
|
44919
|
-
const maxRuntimeMs = remaining.maxRuntimeMs - reservation.maxRuntimeMs;
|
|
44920
|
-
if (remaining.maxToolCalls !== undefined && reservation.maxToolCalls !== undefined) {
|
|
44921
|
-
return { maxRuntimeMs, maxToolCalls: remaining.maxToolCalls - reservation.maxToolCalls };
|
|
44922
|
-
}
|
|
44923
|
-
return remaining.maxToolCalls !== undefined ? { maxRuntimeMs, maxToolCalls: remaining.maxToolCalls } : { maxRuntimeMs };
|
|
44924
|
-
}
|
|
44925
|
-
function planWaves(tasks, config, _deps) {
|
|
44926
|
-
if (!Number.isInteger(config.maxConcurrency) || config.maxConcurrency < 1) {
|
|
44927
|
-
return { ok: false, reason: `maxConcurrency must be a positive integer, got ${config.maxConcurrency}` };
|
|
44928
|
-
}
|
|
44929
|
-
const excluded = computeExcluded(tasks);
|
|
44930
|
-
const universe = tasks.filter((t) => !excluded.has(t.taskId));
|
|
44931
|
-
const scheduled = new Set;
|
|
44932
|
-
const waveTaskLists = [];
|
|
44933
|
-
while (scheduled.size < universe.length) {
|
|
44934
|
-
const ready = universe.filter((t) => !scheduled.has(t.taskId) && t.dependsOn.every((dep) => scheduled.has(dep))).sort(byTaskId);
|
|
44935
|
-
if (ready.length === 0) {
|
|
44936
|
-
return { ok: false, reason: "dependency cycle detected: no ready task set could be formed" };
|
|
44937
|
-
}
|
|
44938
|
-
const waveTasks = ready.slice(0, config.maxConcurrency);
|
|
44939
|
-
for (const t of waveTasks)
|
|
44940
|
-
scheduled.add(t.taskId);
|
|
44941
|
-
waveTaskLists.push(waveTasks);
|
|
44942
|
-
}
|
|
44943
|
-
let remaining = config.parentRemaining;
|
|
44944
|
-
const waves = [];
|
|
44945
|
-
for (const waveTasks of waveTaskLists) {
|
|
44946
|
-
const taskIds = [];
|
|
44947
|
-
const reservations = [];
|
|
44948
|
-
for (const t of waveTasks) {
|
|
44949
|
-
const granted = inheritBudget(remaining, t.budgetRequest);
|
|
44950
|
-
if (!granted.ok) {
|
|
44951
|
-
return { ok: false, reason: granted.reason };
|
|
44952
|
-
}
|
|
44953
|
-
taskIds.push(t.taskId);
|
|
44954
|
-
reservations.push(granted.reservation);
|
|
44955
|
-
remaining = decrementRemaining(remaining, granted.reservation);
|
|
44956
|
-
}
|
|
44957
|
-
waves.push({ taskIds, reservations });
|
|
44958
|
-
}
|
|
44959
|
-
return { ok: true, waves };
|
|
44960
|
-
}
|
|
44961
|
-
|
|
44962
45098
|
// src/harness/extension/bound-wave.ts
|
|
45099
|
+
init_scheduler();
|
|
44963
45100
|
function buildPlannedAttemptEvidence(extension, deps) {
|
|
44964
45101
|
const causal = {
|
|
44965
45102
|
runId: extension.parentRunId,
|
|
@@ -45817,7 +45954,7 @@ function harnessWave(args2, deps) {
|
|
|
45817
45954
|
init_make_provider();
|
|
45818
45955
|
init_orient();
|
|
45819
45956
|
init_metaproject_adapter();
|
|
45820
|
-
import { randomUUID as
|
|
45957
|
+
import { randomUUID as randomUUID25 } from "crypto";
|
|
45821
45958
|
import * as readline2 from "readline";
|
|
45822
45959
|
|
|
45823
45960
|
// src/commands/agent-approval-context.ts
|
|
@@ -47104,6 +47241,9 @@ function buildInteractiveAgentTools(input2) {
|
|
|
47104
47241
|
];
|
|
47105
47242
|
}
|
|
47106
47243
|
|
|
47244
|
+
// src/commands/shell-approval.ts
|
|
47245
|
+
init_command_risk();
|
|
47246
|
+
|
|
47107
47247
|
// src/lib/shell-permissions.ts
|
|
47108
47248
|
init_config_dir();
|
|
47109
47249
|
init_shell_config();
|
|
@@ -47254,6 +47394,12 @@ function validateShellPattern(pattern) {
|
|
|
47254
47394
|
reason: "touches the agent's own permission/credential files; remembering it would let one approved command disable the approval gate for every future session"
|
|
47255
47395
|
};
|
|
47256
47396
|
}
|
|
47397
|
+
if (touchesSacConfirmReview(trimmed)) {
|
|
47398
|
+
return {
|
|
47399
|
+
ok: false,
|
|
47400
|
+
reason: "touches SAC's proposal-review/confirm-token family; that guarantee depends on a human answering a real approval prompt, so it is never remembered"
|
|
47401
|
+
};
|
|
47402
|
+
}
|
|
47257
47403
|
const banned = bannedPrefixGrant(trimmed, firstToken);
|
|
47258
47404
|
if (banned !== undefined) {
|
|
47259
47405
|
return { ok: false, reason: banned.reason };
|
|
@@ -47391,6 +47537,9 @@ function isShellCommandAllowed(command, allow) {
|
|
|
47391
47537
|
if (touchesAgentCredentials(cmd)) {
|
|
47392
47538
|
return false;
|
|
47393
47539
|
}
|
|
47540
|
+
if (touchesSacConfirmReview(cmd)) {
|
|
47541
|
+
return false;
|
|
47542
|
+
}
|
|
47394
47543
|
return allow.some((pat) => matchShellPattern(pat, cmd));
|
|
47395
47544
|
}
|
|
47396
47545
|
function shellPermissionsFingerprint(dir) {
|
|
@@ -47416,12 +47565,12 @@ function suggestShellPatterns(command) {
|
|
|
47416
47565
|
const collapsed = firstLine2.replace(/\s+/g, " ").trim();
|
|
47417
47566
|
const first = collapsed.split(" ")[0] ?? collapsed;
|
|
47418
47567
|
const prefix = first.length > 0 ? `${first} *` : exact;
|
|
47419
|
-
const
|
|
47568
|
+
const neverRemember = trimmed.length > 0 && (isDestructiveCommand(trimmed) || touchesSacConfirmReview(trimmed));
|
|
47420
47569
|
return {
|
|
47421
47570
|
exact,
|
|
47422
47571
|
prefix,
|
|
47423
|
-
offerExact: !
|
|
47424
|
-
offerPrefix: !
|
|
47572
|
+
offerExact: !neverRemember && validateShellPattern(exact).ok,
|
|
47573
|
+
offerPrefix: !neverRemember && validateShellPattern(prefix).ok
|
|
47425
47574
|
};
|
|
47426
47575
|
}
|
|
47427
47576
|
function parseShellExecCommand(inputJson) {
|
|
@@ -47444,16 +47593,18 @@ function evaluateShellApproval(input2) {
|
|
|
47444
47593
|
const command = parseShellExecCommand(input2.inputJson);
|
|
47445
47594
|
const destructive = input2.meta?.destructive === true;
|
|
47446
47595
|
const credentials = input2.meta?.credentials === true;
|
|
47596
|
+
const sacReviewConfirmation = touchesSacConfirmReview(command);
|
|
47447
47597
|
const audit = io.loadAudit();
|
|
47448
47598
|
for (const pattern of audit.permissions.allow) {
|
|
47449
47599
|
input2.sessionAllow.add(pattern);
|
|
47450
47600
|
}
|
|
47451
47601
|
const tampered = io.fingerprint() !== input2.fingerprintAtStart;
|
|
47452
|
-
const autoApprove = !destructive && !credentials && isShellCommandAllowed(command, [...input2.sessionAllow]);
|
|
47602
|
+
const autoApprove = !destructive && !credentials && !sacReviewConfirmation && isShellCommandAllowed(command, [...input2.sessionAllow]);
|
|
47453
47603
|
return {
|
|
47454
47604
|
command,
|
|
47455
47605
|
destructive,
|
|
47456
47606
|
credentials,
|
|
47607
|
+
sacReviewConfirmation,
|
|
47457
47608
|
autoApprove,
|
|
47458
47609
|
rejected: audit.rejected,
|
|
47459
47610
|
tampered
|
|
@@ -47478,6 +47629,9 @@ function formatShellApprovalHints(evaled) {
|
|
|
47478
47629
|
if (evaled.credentials) {
|
|
47479
47630
|
lines.push("touches agent credentials \u2014 will not be remembered");
|
|
47480
47631
|
}
|
|
47632
|
+
if (evaled.sacReviewConfirmation) {
|
|
47633
|
+
lines.push("SAC proposal review/confirm-token \u2014 will not be remembered");
|
|
47634
|
+
}
|
|
47481
47635
|
return lines;
|
|
47482
47636
|
}
|
|
47483
47637
|
|
|
@@ -47880,7 +48034,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
47880
48034
|
invoke: async (input2) => {
|
|
47881
48035
|
const task = typeof input2.task === "string" ? input2.task.trim() : "";
|
|
47882
48036
|
if (task.length === 0) {
|
|
47883
|
-
return { output: "spawn_subagent requires a non-empty 'task'", isError: true };
|
|
48037
|
+
return { status: "Error", output: "spawn_subagent requires a non-empty 'task'", isError: true };
|
|
47884
48038
|
}
|
|
47885
48039
|
const mode = input2.mode === "general" ? "general" : "read_only";
|
|
47886
48040
|
const maxToolCalls = typeof input2.max_tool_calls === "number" && input2.max_tool_calls > 0 ? Math.min(MAX_SUBAGENT_MAX_TOOL_CALLS, Math.floor(input2.max_tool_calls)) : DEFAULT_SUBAGENT_MAX_TOOL_CALLS;
|
|
@@ -47931,6 +48085,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
47931
48085
|
task
|
|
47932
48086
|
});
|
|
47933
48087
|
return {
|
|
48088
|
+
status: "Denied",
|
|
47934
48089
|
output: `spawn_subagent denied by MAE: ${spawned.reason}`,
|
|
47935
48090
|
isError: true
|
|
47936
48091
|
};
|
|
@@ -48120,6 +48275,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
48120
48275
|
|
|
48121
48276
|
` + "Return a concise summary of findings and any recommended next steps for the parent agent.";
|
|
48122
48277
|
const turn = runAgentTurn(io, childDeps, history, userLine, { signal: childAbort.signal });
|
|
48278
|
+
let turnResult;
|
|
48123
48279
|
if (deadlineMs > 0) {
|
|
48124
48280
|
let timer;
|
|
48125
48281
|
const expired = new Promise((resolve3) => {
|
|
@@ -48141,20 +48297,26 @@ function createSpawnSubagentTool(deps) {
|
|
|
48141
48297
|
await foldChildSlateAndCleanup("incomplete");
|
|
48142
48298
|
const partial = assistant.trim();
|
|
48143
48299
|
return {
|
|
48300
|
+
status: "Timeout",
|
|
48144
48301
|
output: `subagent ${label} (${workerId}) timed out after ${deadlineMs}ms and was abandoned ` + `(tighten or disable with ${ENV_SUBAGENT_TIMEOUT_MS})` + (partial.length > 0 ? `
|
|
48145
48302
|
--- partial output ---
|
|
48146
48303
|
${boundSummary(partial)}` : ""),
|
|
48147
|
-
isError: true
|
|
48304
|
+
isError: true,
|
|
48305
|
+
...partial.length > 0 ? { partial: boundSummary(partial) } : {}
|
|
48148
48306
|
};
|
|
48149
48307
|
}
|
|
48308
|
+
turnResult = await turn;
|
|
48150
48309
|
} else {
|
|
48151
|
-
await turn;
|
|
48310
|
+
turnResult = await turn;
|
|
48152
48311
|
}
|
|
48153
48312
|
closed = true;
|
|
48154
48313
|
releaseBudget();
|
|
48155
48314
|
const raw = assistant.trim().length > 0 ? assistant.trim() : history.filter((m) => m.role === "assistant").map((m) => m.content).join(`
|
|
48156
48315
|
`).trim() || "(subagent produced no text)";
|
|
48157
48316
|
const folded = foldChildSummary(raw);
|
|
48317
|
+
const finishReason = turnResult?.finishReason;
|
|
48318
|
+
const status = finishReason === "budget" ? "BudgetExhausted" : finishReason === "no-progress" ? "NoProgress" : "Completed";
|
|
48319
|
+
const isError = status !== "Completed";
|
|
48158
48320
|
emitSubagentFleet({
|
|
48159
48321
|
kind: "upsert",
|
|
48160
48322
|
id: workerId,
|
|
@@ -48166,11 +48328,13 @@ ${boundSummary(partial)}` : ""),
|
|
|
48166
48328
|
});
|
|
48167
48329
|
await foldChildSlateAndCleanup("completed");
|
|
48168
48330
|
return {
|
|
48331
|
+
status,
|
|
48332
|
+
isError,
|
|
48169
48333
|
output: `subagent ${label} (${workerId}) ${mode} via ${runModel.provider}/${runModel.model}
|
|
48170
48334
|
` + `MAE reservation: tools\u2264${spawned.reservation.maxToolCalls ?? maxToolCalls} ` + `runtime\u2264${spawned.reservation.maxRuntimeMs}ms children=${ledger.childCount}
|
|
48171
48335
|
` + `--- summary ---
|
|
48172
48336
|
${boundSummary(folded.text)}`,
|
|
48173
|
-
|
|
48337
|
+
...status !== "Completed" ? { partial: boundSummary(folded.text) } : {}
|
|
48174
48338
|
};
|
|
48175
48339
|
} catch (cause) {
|
|
48176
48340
|
closed = true;
|
|
@@ -48186,7 +48350,7 @@ ${boundSummary(folded.text)}`,
|
|
|
48186
48350
|
task
|
|
48187
48351
|
});
|
|
48188
48352
|
await foldChildSlateAndCleanup("incomplete");
|
|
48189
|
-
return { output: `subagent ${label} failed: ${msg}`, isError: true };
|
|
48353
|
+
return { status: "Error", output: `subagent ${label} failed: ${msg}`, isError: true };
|
|
48190
48354
|
}
|
|
48191
48355
|
}
|
|
48192
48356
|
};
|
|
@@ -48582,7 +48746,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
48582
48746
|
// package.json
|
|
48583
48747
|
var package_default = {
|
|
48584
48748
|
name: "@mrciphersmith/keryx",
|
|
48585
|
-
version: "0.2.
|
|
48749
|
+
version: "0.2.45",
|
|
48586
48750
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
48587
48751
|
private: false,
|
|
48588
48752
|
publishConfig: {
|
|
@@ -49832,8 +49996,195 @@ function openModal(otui, chrome, input2) {
|
|
|
49832
49996
|
// src/tui/inspector-sources.ts
|
|
49833
49997
|
init_store2();
|
|
49834
49998
|
init_workspace_service();
|
|
49999
|
+
import { randomUUID as randomUUID24 } from "crypto";
|
|
50000
|
+
|
|
50001
|
+
// src/sac/catch-up.ts
|
|
50002
|
+
init_fs();
|
|
50003
|
+
init_config_dir();
|
|
50004
|
+
init_paths();
|
|
49835
50005
|
init_slate();
|
|
50006
|
+
init_store3();
|
|
50007
|
+
init_proposal_lifecycle();
|
|
50008
|
+
init_workspace_service();
|
|
50009
|
+
import { randomUUID as randomUUID23 } from "crypto";
|
|
50010
|
+
import { readdir as readdir24 } from "fs/promises";
|
|
50011
|
+
import path145 from "path";
|
|
50012
|
+
|
|
50013
|
+
// src/sac/lifecycle-flag.ts
|
|
50014
|
+
init_store();
|
|
50015
|
+
init_service3();
|
|
50016
|
+
init_decision_dedup();
|
|
50017
|
+
init_workspace_service();
|
|
49836
50018
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
50019
|
+
function normalize3(raw) {
|
|
50020
|
+
return raw.replace(/^\.\//, "");
|
|
50021
|
+
}
|
|
50022
|
+
function isStillPresent(recorded, valid) {
|
|
50023
|
+
const normalized = normalize3(recorded);
|
|
50024
|
+
return valid.has(normalized) || valid.has(moduleNameFromProjectPath(normalized));
|
|
50025
|
+
}
|
|
50026
|
+
async function computeLifecycleFlags(cwd, now = () => new Date) {
|
|
50027
|
+
const valid = await validModuleNames(cwd);
|
|
50028
|
+
if (valid === undefined)
|
|
50029
|
+
return [];
|
|
50030
|
+
const flaggedAt = now().toISOString();
|
|
50031
|
+
const flags = [];
|
|
50032
|
+
const workspaceService = new WorkspaceService({
|
|
50033
|
+
workspaceRoot: cwd,
|
|
50034
|
+
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
50035
|
+
strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }
|
|
50036
|
+
});
|
|
50037
|
+
const workspaces = await workspaceService.list({ request: undefined, requestCorrelationId: randomUUID22(), includeArchived: true });
|
|
50038
|
+
for (const workspace of workspaces) {
|
|
50039
|
+
const component = workspace.resources.find((r) => r.kind === "component")?.uri;
|
|
50040
|
+
if (component !== undefined && !isStillPresent(component, valid)) {
|
|
50041
|
+
flags.push({ kind: "workspace", ref: workspace.id, missingComponent: normalize3(component), flaggedAt });
|
|
50042
|
+
}
|
|
50043
|
+
}
|
|
50044
|
+
for (const entry of await collectEntries(cwd)) {
|
|
50045
|
+
const module = entry.scopes.module;
|
|
50046
|
+
if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
|
|
50047
|
+
flags.push({ kind: "memory-entry", ref: entry.relativePath, missingComponent: normalize3(module), flaggedAt });
|
|
50048
|
+
}
|
|
50049
|
+
}
|
|
50050
|
+
for (const decision of await collectWikiDecisionEntries(cwd)) {
|
|
50051
|
+
const module = decision.scopes.module;
|
|
50052
|
+
if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
|
|
50053
|
+
flags.push({ kind: "wiki-decision", ref: decision.relativePath, missingComponent: normalize3(module), flaggedAt });
|
|
50054
|
+
}
|
|
50055
|
+
}
|
|
50056
|
+
return flags;
|
|
50057
|
+
}
|
|
50058
|
+
|
|
50059
|
+
// src/sac/catch-up.ts
|
|
50060
|
+
async function buildCatchUp(input2) {
|
|
50061
|
+
const [proposals, sessionCategories, lifecycleFlagsAll] = await Promise.all([
|
|
50062
|
+
collectProposals(input2.cwd, input2.workspaceId),
|
|
50063
|
+
collectSessionCategories(input2.cwd),
|
|
50064
|
+
computeLifecycleFlags(input2.cwd)
|
|
50065
|
+
]);
|
|
50066
|
+
const lifecycleFlags = input2.workspaceId === undefined ? lifecycleFlagsAll : lifecycleFlagsAll.filter((flag) => flag.kind !== "workspace" || flag.ref === input2.workspaceId);
|
|
50067
|
+
return { proposals, ...sessionCategories, lifecycleFlags };
|
|
50068
|
+
}
|
|
50069
|
+
async function collectProposals(cwd, workspaceId) {
|
|
50070
|
+
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
50071
|
+
const actor = await authorizationServer.actorContextFor(undefined, randomUUID23());
|
|
50072
|
+
if (!actor)
|
|
50073
|
+
throw new Error("trusted ActorContext is required for catch-up");
|
|
50074
|
+
const proposalService = createLocalProposalLifecycleService(cwd);
|
|
50075
|
+
const groups = await proposalService.listVisibleProposedProposals(actor);
|
|
50076
|
+
const scoped = workspaceId === undefined ? groups : groups.filter((group) => group.workspace.id === workspaceId);
|
|
50077
|
+
const flattened = scoped.flatMap((group) => group.proposals.map((proposal) => ({ group, proposal })));
|
|
50078
|
+
return Promise.all(flattened.map(async ({ group, proposal }) => {
|
|
50079
|
+
const fresh = await proposalService.isEvidenceFresh(proposal, actor);
|
|
50080
|
+
return { type: "proposal", workspaceId: group.workspace.id, proposalId: proposal.id, fresh };
|
|
50081
|
+
}));
|
|
50082
|
+
}
|
|
50083
|
+
async function classifySession(session) {
|
|
50084
|
+
const dir = sessionDir(session.projectPath, session.id);
|
|
50085
|
+
if (await isLockHeld(slateLockPath(dir)))
|
|
50086
|
+
return;
|
|
50087
|
+
const terminalState = await readTerminalState(dir);
|
|
50088
|
+
if (terminalState !== undefined) {
|
|
50089
|
+
const workspaceId2 = (await safeReadSlate(dir))?.workspaceId;
|
|
50090
|
+
return { kind: "blocked", item: { type: "blocked", sessionId: session.id, ...workspaceId2 !== undefined ? { workspaceId: workspaceId2 } : {}, terminalState } };
|
|
50091
|
+
}
|
|
50092
|
+
const unboundCandidate = await readNewestUnboundCandidate(dir);
|
|
50093
|
+
if (unboundCandidate !== undefined) {
|
|
50094
|
+
return {
|
|
50095
|
+
kind: "unbound-candidate",
|
|
50096
|
+
item: { type: "unbound-candidate", sessionId: session.id, evidencePath: unboundCandidate.evidencePath, summary: unboundCandidate.summary }
|
|
50097
|
+
};
|
|
50098
|
+
}
|
|
50099
|
+
if (!await isSlateEngaged(dir))
|
|
50100
|
+
return;
|
|
50101
|
+
const workspaceId = (await safeReadSlate(dir))?.workspaceId;
|
|
50102
|
+
return { kind: "unknown", item: { type: "unknown", sessionId: session.id, ...workspaceId !== undefined ? { workspaceId } : {}, lastSeenAt: session.updatedAt } };
|
|
50103
|
+
}
|
|
50104
|
+
async function collectSessionCategories(cwd) {
|
|
50105
|
+
const classified = await Promise.all(listSessions(cwd).map((session) => classifySession(session)));
|
|
50106
|
+
const blocked2 = [];
|
|
50107
|
+
const unboundCandidates = [];
|
|
50108
|
+
const unknown = [];
|
|
50109
|
+
for (const category of classified) {
|
|
50110
|
+
if (category === undefined)
|
|
50111
|
+
continue;
|
|
50112
|
+
if (category.kind === "blocked")
|
|
50113
|
+
blocked2.push(category.item);
|
|
50114
|
+
else if (category.kind === "unbound-candidate")
|
|
50115
|
+
unboundCandidates.push(category.item);
|
|
50116
|
+
else
|
|
50117
|
+
unknown.push(category.item);
|
|
50118
|
+
}
|
|
50119
|
+
return { blocked: blocked2, unboundCandidates, unknown };
|
|
50120
|
+
}
|
|
50121
|
+
async function isSlateEngaged(dir) {
|
|
50122
|
+
if (await pathExists(path145.join(dir, "slate.json")))
|
|
50123
|
+
return true;
|
|
50124
|
+
if (await pathExists(path145.join(dir, "terminal-state.json")))
|
|
50125
|
+
return true;
|
|
50126
|
+
try {
|
|
50127
|
+
const entries = await readdir24(path145.join(dir, "slate-archive"));
|
|
50128
|
+
return entries.length > 0;
|
|
50129
|
+
} catch {
|
|
50130
|
+
return false;
|
|
50131
|
+
}
|
|
50132
|
+
}
|
|
50133
|
+
async function safeReadSlate(dir) {
|
|
50134
|
+
try {
|
|
50135
|
+
return await readSlate(dir);
|
|
50136
|
+
} catch {
|
|
50137
|
+
return;
|
|
50138
|
+
}
|
|
50139
|
+
}
|
|
50140
|
+
async function readTerminalState(dir) {
|
|
50141
|
+
const result = readConfigFile(path145.join(dir, "terminal-state.json"));
|
|
50142
|
+
if (!result.ok) {
|
|
50143
|
+
return;
|
|
50144
|
+
}
|
|
50145
|
+
try {
|
|
50146
|
+
return JSON.parse(result.text);
|
|
50147
|
+
} catch {
|
|
50148
|
+
return;
|
|
50149
|
+
}
|
|
50150
|
+
}
|
|
50151
|
+
async function readNewestUnboundCandidate(dir) {
|
|
50152
|
+
const archiveDir = path145.join(dir, "slate-archive");
|
|
50153
|
+
let entries;
|
|
50154
|
+
try {
|
|
50155
|
+
entries = (await readdir24(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
50156
|
+
} catch {
|
|
50157
|
+
return;
|
|
50158
|
+
}
|
|
50159
|
+
entries.sort();
|
|
50160
|
+
for (let i = entries.length - 1;i >= 0; i--) {
|
|
50161
|
+
const evidencePath = path145.join(archiveDir, entries[i]);
|
|
50162
|
+
const result = readConfigFile(evidencePath);
|
|
50163
|
+
if (!result.ok) {
|
|
50164
|
+
continue;
|
|
50165
|
+
}
|
|
50166
|
+
try {
|
|
50167
|
+
const parsed = JSON.parse(result.text);
|
|
50168
|
+
if (parsed.recordType !== "unbound-candidate")
|
|
50169
|
+
continue;
|
|
50170
|
+
return { evidencePath, summary: summarizeUnboundCandidate(parsed.groups) };
|
|
50171
|
+
} catch {
|
|
50172
|
+
continue;
|
|
50173
|
+
}
|
|
50174
|
+
}
|
|
50175
|
+
return;
|
|
50176
|
+
}
|
|
50177
|
+
function summarizeUnboundCandidate(groups) {
|
|
50178
|
+
const safeGroups = groups ?? [];
|
|
50179
|
+
if (safeGroups.length === 0)
|
|
50180
|
+
return "no seeds captured";
|
|
50181
|
+
const seedCount = safeGroups.reduce((sum, group) => sum + (group.seeds?.length ?? 0), 0);
|
|
50182
|
+
const kinds = safeGroups.map((group) => typeof group.kind === "string" ? group.kind : "unknown").join(", ");
|
|
50183
|
+
return `${seedCount} untriaged seed(s) across ${safeGroups.length} kind(s) (${kinds})`;
|
|
50184
|
+
}
|
|
50185
|
+
|
|
50186
|
+
// src/tui/inspector-sources.ts
|
|
50187
|
+
init_slate();
|
|
49837
50188
|
|
|
49838
50189
|
// src/session/index.ts
|
|
49839
50190
|
init_paths();
|
|
@@ -49912,7 +50263,7 @@ async function loadInspectorWorkspaces(cwd) {
|
|
|
49912
50263
|
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
49913
50264
|
strictGuard: localSacGuard()
|
|
49914
50265
|
});
|
|
49915
|
-
const listed = await service5.list({ request: undefined, requestCorrelationId:
|
|
50266
|
+
const listed = await service5.list({ request: undefined, requestCorrelationId: randomUUID24() });
|
|
49916
50267
|
return listed.map(workspaceFromManifest);
|
|
49917
50268
|
} catch {
|
|
49918
50269
|
return [];
|
|
@@ -49925,7 +50276,7 @@ async function loadInspectorWorkspace(cwd, workspaceId) {
|
|
|
49925
50276
|
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
49926
50277
|
strictGuard: localSacGuard()
|
|
49927
50278
|
});
|
|
49928
|
-
const manifest = await service5.show({ request: undefined, requestCorrelationId:
|
|
50279
|
+
const manifest = await service5.show({ request: undefined, requestCorrelationId: randomUUID24(), workspaceId });
|
|
49929
50280
|
return workspaceFromManifest(manifest);
|
|
49930
50281
|
} catch {
|
|
49931
50282
|
return;
|
|
@@ -49998,6 +50349,16 @@ function formatWorkspaceLines(workspaces) {
|
|
|
49998
50349
|
const width = workspaces.reduce((max, workspace) => Math.max(max, workspace.id.length), 0);
|
|
49999
50350
|
return workspaces.map((workspace) => `${workspace.id.padEnd(width)} ${workspace.status} ${workspace.title}`);
|
|
50000
50351
|
}
|
|
50352
|
+
async function loadInspectorCatchUp(cwd) {
|
|
50353
|
+
try {
|
|
50354
|
+
return await buildCatchUp({ cwd });
|
|
50355
|
+
} catch {
|
|
50356
|
+
return { proposals: [], blocked: [], unboundCandidates: [], unknown: [], lifecycleFlags: [] };
|
|
50357
|
+
}
|
|
50358
|
+
}
|
|
50359
|
+
function catchUpItems(report) {
|
|
50360
|
+
return [...report.proposals, ...report.blocked, ...report.unboundCandidates, ...report.unknown];
|
|
50361
|
+
}
|
|
50001
50362
|
function formatSessionFlowLines(flows) {
|
|
50002
50363
|
if (flows.length === 0) {
|
|
50003
50364
|
return ["No flows recorded in this session."];
|
|
@@ -50490,6 +50851,332 @@ function openWorkspace(otui, chrome, options) {
|
|
|
50490
50851
|
return presentWorkspace((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
50491
50852
|
}
|
|
50492
50853
|
|
|
50854
|
+
// src/tui/review-inspector.ts
|
|
50855
|
+
var REVIEW_COMMAND = "/review";
|
|
50856
|
+
var REVIEW_FOOTER = [
|
|
50857
|
+
{ key: "[/]", label: "item" },
|
|
50858
|
+
{ key: "a y", label: "accept" },
|
|
50859
|
+
{ key: "\u2191/\u2193", label: "scroll" },
|
|
50860
|
+
{ key: "\u2190/\u2192", label: "tabs" },
|
|
50861
|
+
{ key: "esc", label: "close" }
|
|
50862
|
+
];
|
|
50863
|
+
function isReviewCommand(line) {
|
|
50864
|
+
const token = line.trim().split(/\s+/)[0] ?? "";
|
|
50865
|
+
return token === REVIEW_COMMAND;
|
|
50866
|
+
}
|
|
50867
|
+
var TYPE_LABEL = {
|
|
50868
|
+
proposal: "PROPOSAL",
|
|
50869
|
+
blocked: "BLOCKED",
|
|
50870
|
+
"unbound-candidate": "UNBOUND",
|
|
50871
|
+
unknown: "UNKNOWN"
|
|
50872
|
+
};
|
|
50873
|
+
function summarizeReviewItem(item) {
|
|
50874
|
+
switch (item.type) {
|
|
50875
|
+
case "proposal":
|
|
50876
|
+
return `${item.proposalId} in ${item.workspaceId}${item.fresh ? "" : " (stale)"}`;
|
|
50877
|
+
case "blocked":
|
|
50878
|
+
return `${item.sessionId} \u2014 ${item.terminalState.reason}`;
|
|
50879
|
+
case "unbound-candidate":
|
|
50880
|
+
return `${item.sessionId} \u2014 ${item.summary}`;
|
|
50881
|
+
case "unknown":
|
|
50882
|
+
return `${item.sessionId} \u2014 last seen ${item.lastSeenAt}`;
|
|
50883
|
+
}
|
|
50884
|
+
}
|
|
50885
|
+
function formatReviewListLines(items, selected) {
|
|
50886
|
+
if (items.length === 0) {
|
|
50887
|
+
return ["Nothing needs review right now."];
|
|
50888
|
+
}
|
|
50889
|
+
return items.map((item, index) => {
|
|
50890
|
+
const mark = index === selected ? ">" : " ";
|
|
50891
|
+
return `${mark} ${TYPE_LABEL[item.type].padEnd(8)} ${summarizeReviewItem(item)}`;
|
|
50892
|
+
});
|
|
50893
|
+
}
|
|
50894
|
+
function describeReviewItem(item) {
|
|
50895
|
+
switch (item.type) {
|
|
50896
|
+
case "proposal":
|
|
50897
|
+
return [
|
|
50898
|
+
`Proposal ${item.proposalId}`,
|
|
50899
|
+
`Workspace ${item.workspaceId}`,
|
|
50900
|
+
`Evidence ${item.fresh ? "fresh" : "stale \u2014 evidence has drifted since this proposal was created; re-run wrap-up before deciding"}`,
|
|
50901
|
+
"",
|
|
50902
|
+
`Reject/dismiss from a terminal: keryx workspace review ${item.workspaceId} ${item.proposalId} --decision <rejected|dismissed>`
|
|
50903
|
+
];
|
|
50904
|
+
case "blocked":
|
|
50905
|
+
return [
|
|
50906
|
+
`Session ${item.sessionId}${item.workspaceId !== undefined ? ` (workspace ${item.workspaceId})` : ""}`,
|
|
50907
|
+
`Stopped unattended: ${item.terminalState.reason}`,
|
|
50908
|
+
`Occurred ${item.terminalState.occurredAt}`,
|
|
50909
|
+
"",
|
|
50910
|
+
`Resume: keryx shell -r ${item.sessionId}`
|
|
50911
|
+
];
|
|
50912
|
+
case "unbound-candidate":
|
|
50913
|
+
return [
|
|
50914
|
+
`Session ${item.sessionId}`,
|
|
50915
|
+
`Untriaged seeds: ${item.summary}`,
|
|
50916
|
+
`Evidence ${item.evidencePath}`,
|
|
50917
|
+
"",
|
|
50918
|
+
`Bind: keryx workspace propose <workspace-id> --kind <kind> --session ${item.sessionId}`
|
|
50919
|
+
];
|
|
50920
|
+
case "unknown":
|
|
50921
|
+
return [
|
|
50922
|
+
`Session ${item.sessionId}${item.workspaceId !== undefined ? ` (workspace ${item.workspaceId})` : ""}`,
|
|
50923
|
+
`Last seen ${item.lastSeenAt}`,
|
|
50924
|
+
"No proposal, terminal state, or unbound-candidate artifact recorded.",
|
|
50925
|
+
"",
|
|
50926
|
+
`Investigate: keryx sessions list / keryx shell -r ${item.sessionId}`
|
|
50927
|
+
];
|
|
50928
|
+
}
|
|
50929
|
+
}
|
|
50930
|
+
function formatReviewDetailLines(item, status) {
|
|
50931
|
+
if (item === undefined) {
|
|
50932
|
+
return ["No item selected.", "", "Press Enter (or click a row) on the Review tab to view one."];
|
|
50933
|
+
}
|
|
50934
|
+
const lines = describeReviewItem(item);
|
|
50935
|
+
if (item.type !== "proposal") {
|
|
50936
|
+
return lines;
|
|
50937
|
+
}
|
|
50938
|
+
const withAction = [...lines, ""];
|
|
50939
|
+
if (status.kind === "armed") {
|
|
50940
|
+
withAction.push("Press [y] to CONFIRM accept, any other key cancels.");
|
|
50941
|
+
} else if (status.kind === "running") {
|
|
50942
|
+
withAction.push("Accepting\u2026 running `keryx workspace confirm-review` then `keryx workspace review`.");
|
|
50943
|
+
} else if (status.kind === "done" && status.outcome.ok) {
|
|
50944
|
+
withAction.push("\u2713 Accepted.");
|
|
50945
|
+
} else if (status.kind === "done" && !status.outcome.ok) {
|
|
50946
|
+
withAction.push(`\u2717 Accept failed: ${status.outcome.message}`);
|
|
50947
|
+
} else {
|
|
50948
|
+
withAction.push("[a] Accept this proposal");
|
|
50949
|
+
}
|
|
50950
|
+
return withAction;
|
|
50951
|
+
}
|
|
50952
|
+
function clampScroll3(offset, lineCount, height) {
|
|
50953
|
+
const max = Math.max(0, lineCount - height);
|
|
50954
|
+
return Math.min(max, Math.max(0, offset));
|
|
50955
|
+
}
|
|
50956
|
+
function windowLines3(lines, offset, height) {
|
|
50957
|
+
if (height < 1) {
|
|
50958
|
+
return [];
|
|
50959
|
+
}
|
|
50960
|
+
const start = clampScroll3(offset, lines.length, height);
|
|
50961
|
+
return lines.slice(start, start + height);
|
|
50962
|
+
}
|
|
50963
|
+
function scrollToReveal3(index, offset, height) {
|
|
50964
|
+
if (index < offset) {
|
|
50965
|
+
return index;
|
|
50966
|
+
}
|
|
50967
|
+
if (index >= offset + height) {
|
|
50968
|
+
return index - height + 1;
|
|
50969
|
+
}
|
|
50970
|
+
return offset;
|
|
50971
|
+
}
|
|
50972
|
+
function wrapLines3(text, width) {
|
|
50973
|
+
if (width === undefined || width < 8) {
|
|
50974
|
+
return text;
|
|
50975
|
+
}
|
|
50976
|
+
return text.split(`
|
|
50977
|
+
`).flatMap((line) => {
|
|
50978
|
+
if (line.length <= width) {
|
|
50979
|
+
return [line];
|
|
50980
|
+
}
|
|
50981
|
+
const chunks = [];
|
|
50982
|
+
for (let i = 0;i < line.length; i += width) {
|
|
50983
|
+
chunks.push(line.slice(i, i + width));
|
|
50984
|
+
}
|
|
50985
|
+
return chunks;
|
|
50986
|
+
}).join(`
|
|
50987
|
+
`);
|
|
50988
|
+
}
|
|
50989
|
+
function paintLines3(otui, renderer, body, lines, width) {
|
|
50990
|
+
if (otui === undefined || otui === null || body === undefined || body === null) {
|
|
50991
|
+
return;
|
|
50992
|
+
}
|
|
50993
|
+
const parent = body;
|
|
50994
|
+
const ctor = otui.TextRenderable;
|
|
50995
|
+
if (parent.add === undefined || ctor === undefined) {
|
|
50996
|
+
return;
|
|
50997
|
+
}
|
|
50998
|
+
const node = new ctor(renderer, { id: "review-body", content: wrapLines3(lines.join(`
|
|
50999
|
+
`), width) });
|
|
51000
|
+
parent.add(node);
|
|
51001
|
+
return node;
|
|
51002
|
+
}
|
|
51003
|
+
function presentReview(openModal2, otui, chrome, options) {
|
|
51004
|
+
const items = [...options.items];
|
|
51005
|
+
let selected = 0;
|
|
51006
|
+
let listScroll = 0;
|
|
51007
|
+
let detailScroll = 0;
|
|
51008
|
+
let status = { kind: "idle" };
|
|
51009
|
+
let listNode;
|
|
51010
|
+
let detailNode;
|
|
51011
|
+
let unsubscribeKey;
|
|
51012
|
+
const rendererHint = options.renderer ?? chrome?.renderer;
|
|
51013
|
+
const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
|
|
51014
|
+
let tabWidth;
|
|
51015
|
+
const listLines = () => formatReviewListLines(items, selected);
|
|
51016
|
+
const detailLines = () => wrapLines3(formatReviewDetailLines(items[selected], status).join(`
|
|
51017
|
+
`), tabWidth).split(`
|
|
51018
|
+
`);
|
|
51019
|
+
const paintSelection = () => {
|
|
51020
|
+
listScroll = scrollToReveal3(selected, listScroll, bodyRows);
|
|
51021
|
+
listScroll = clampScroll3(listScroll, items.length, bodyRows);
|
|
51022
|
+
detailScroll = clampScroll3(detailScroll, detailLines().length, bodyRows);
|
|
51023
|
+
if (listNode !== undefined) {
|
|
51024
|
+
listNode.content = windowLines3(listLines(), listScroll, bodyRows).join(`
|
|
51025
|
+
`);
|
|
51026
|
+
}
|
|
51027
|
+
if (detailNode !== undefined) {
|
|
51028
|
+
detailNode.content = windowLines3(detailLines(), detailScroll, bodyRows).join(`
|
|
51029
|
+
`);
|
|
51030
|
+
}
|
|
51031
|
+
};
|
|
51032
|
+
const moveSelection = (next) => {
|
|
51033
|
+
if (items.length === 0) {
|
|
51034
|
+
return;
|
|
51035
|
+
}
|
|
51036
|
+
const clamped = Math.min(items.length - 1, Math.max(0, next));
|
|
51037
|
+
if (clamped === selected) {
|
|
51038
|
+
return;
|
|
51039
|
+
}
|
|
51040
|
+
selected = clamped;
|
|
51041
|
+
detailScroll = 0;
|
|
51042
|
+
status = { kind: "idle" };
|
|
51043
|
+
paintSelection();
|
|
51044
|
+
};
|
|
51045
|
+
const runAccept = () => {
|
|
51046
|
+
const item = items[selected];
|
|
51047
|
+
if (item === undefined || item.type !== "proposal" || options.acceptProposal === undefined) {
|
|
51048
|
+
return;
|
|
51049
|
+
}
|
|
51050
|
+
status = { kind: "running" };
|
|
51051
|
+
paintSelection();
|
|
51052
|
+
options.acceptProposal(item).then((outcome) => {
|
|
51053
|
+
status = { kind: "done", outcome };
|
|
51054
|
+
if (outcome.ok) {
|
|
51055
|
+
options.onAccepted?.(item);
|
|
51056
|
+
}
|
|
51057
|
+
paintSelection();
|
|
51058
|
+
});
|
|
51059
|
+
};
|
|
51060
|
+
const handle = openModal2(otui, chrome, {
|
|
51061
|
+
title: REVIEW_COMMAND,
|
|
51062
|
+
tabs: [
|
|
51063
|
+
{ id: "list", label: "Review" },
|
|
51064
|
+
{ id: "detail", label: "Detail" }
|
|
51065
|
+
],
|
|
51066
|
+
initialTab: "list",
|
|
51067
|
+
footer: REVIEW_FOOTER,
|
|
51068
|
+
renderTab: (tabId, body, ctx) => {
|
|
51069
|
+
const renderer = options.renderer ?? chrome?.renderer;
|
|
51070
|
+
tabWidth = ctx?.width;
|
|
51071
|
+
if (tabId === "list") {
|
|
51072
|
+
listScroll = scrollToReveal3(selected, listScroll, bodyRows);
|
|
51073
|
+
listNode = paintLines3(otui, renderer, body, windowLines3(listLines(), listScroll, bodyRows));
|
|
51074
|
+
return;
|
|
51075
|
+
}
|
|
51076
|
+
detailScroll = clampScroll3(detailScroll, detailLines().length, bodyRows);
|
|
51077
|
+
detailNode = paintLines3(otui, renderer, body, windowLines3(detailLines(), detailScroll, bodyRows), tabWidth);
|
|
51078
|
+
},
|
|
51079
|
+
onClose: () => {
|
|
51080
|
+
unsubscribeKey?.();
|
|
51081
|
+
}
|
|
51082
|
+
});
|
|
51083
|
+
if (handle === undefined) {
|
|
51084
|
+
return;
|
|
51085
|
+
}
|
|
51086
|
+
if (options.onKeypress !== undefined) {
|
|
51087
|
+
unsubscribeKey = options.onKeypress((key) => {
|
|
51088
|
+
const token = key.name || key.sequence;
|
|
51089
|
+
if (items.length === 0) {
|
|
51090
|
+
return;
|
|
51091
|
+
}
|
|
51092
|
+
const onDetail = handle.activeTab() === "detail";
|
|
51093
|
+
if (onDetail && status.kind === "armed") {
|
|
51094
|
+
if (token === "y") {
|
|
51095
|
+
runAccept();
|
|
51096
|
+
} else {
|
|
51097
|
+
status = { kind: "idle" };
|
|
51098
|
+
paintSelection();
|
|
51099
|
+
}
|
|
51100
|
+
return;
|
|
51101
|
+
}
|
|
51102
|
+
if (token === "[" || token === "p") {
|
|
51103
|
+
moveSelection(selected - 1);
|
|
51104
|
+
return;
|
|
51105
|
+
}
|
|
51106
|
+
if (token === "]" || token === "n") {
|
|
51107
|
+
moveSelection(selected + 1);
|
|
51108
|
+
return;
|
|
51109
|
+
}
|
|
51110
|
+
if (token === "return" || token === "enter") {
|
|
51111
|
+
handle.setTab("detail");
|
|
51112
|
+
return;
|
|
51113
|
+
}
|
|
51114
|
+
if (onDetail && token === "a" && items[selected]?.type === "proposal" && options.acceptProposal !== undefined && status.kind !== "running") {
|
|
51115
|
+
status = { kind: "armed" };
|
|
51116
|
+
paintSelection();
|
|
51117
|
+
return;
|
|
51118
|
+
}
|
|
51119
|
+
if (token === "up" || token === "k") {
|
|
51120
|
+
if (onDetail) {
|
|
51121
|
+
detailScroll = clampScroll3(detailScroll - 1, detailLines().length, bodyRows);
|
|
51122
|
+
paintSelection();
|
|
51123
|
+
} else {
|
|
51124
|
+
moveSelection(selected - 1);
|
|
51125
|
+
}
|
|
51126
|
+
return;
|
|
51127
|
+
}
|
|
51128
|
+
if (token === "down" || token === "j") {
|
|
51129
|
+
if (onDetail) {
|
|
51130
|
+
detailScroll = clampScroll3(detailScroll + 1, detailLines().length, bodyRows);
|
|
51131
|
+
paintSelection();
|
|
51132
|
+
} else {
|
|
51133
|
+
moveSelection(selected + 1);
|
|
51134
|
+
}
|
|
51135
|
+
return;
|
|
51136
|
+
}
|
|
51137
|
+
if (onDetail && (token === "pageup" || token === "pagedown")) {
|
|
51138
|
+
const step = token === "pageup" ? -bodyRows : bodyRows;
|
|
51139
|
+
detailScroll = clampScroll3(detailScroll + step, detailLines().length, bodyRows);
|
|
51140
|
+
paintSelection();
|
|
51141
|
+
}
|
|
51142
|
+
});
|
|
51143
|
+
}
|
|
51144
|
+
return handle;
|
|
51145
|
+
}
|
|
51146
|
+
function openReview(otui, chrome, options) {
|
|
51147
|
+
return presentReview((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
51148
|
+
}
|
|
51149
|
+
|
|
51150
|
+
// src/tui/review-accept.ts
|
|
51151
|
+
function shQuote(value) {
|
|
51152
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
51153
|
+
}
|
|
51154
|
+
async function acceptProposalViaShell(run, workspaceId, proposalId) {
|
|
51155
|
+
const mint = await run(`keryx workspace confirm-review ${shQuote(workspaceId)} ${shQuote(proposalId)}`);
|
|
51156
|
+
if (mint.isError) {
|
|
51157
|
+
return { ok: false, message: mint.output };
|
|
51158
|
+
}
|
|
51159
|
+
let token;
|
|
51160
|
+
try {
|
|
51161
|
+
const parsed = JSON.parse(mint.output);
|
|
51162
|
+
const candidate = parsed?.token;
|
|
51163
|
+
if (typeof candidate !== "string" || candidate.length === 0) {
|
|
51164
|
+
throw new Error("no token in confirm-review output");
|
|
51165
|
+
}
|
|
51166
|
+
token = candidate;
|
|
51167
|
+
} catch (cause) {
|
|
51168
|
+
return {
|
|
51169
|
+
ok: false,
|
|
51170
|
+
message: `could not parse \`keryx workspace confirm-review\` output: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
51171
|
+
};
|
|
51172
|
+
}
|
|
51173
|
+
const accept = await run(`keryx workspace review ${shQuote(workspaceId)} ${shQuote(proposalId)} --decision accepted --confirm-token ${shQuote(token)}`);
|
|
51174
|
+
if (accept.isError) {
|
|
51175
|
+
return { ok: false, message: accept.output };
|
|
51176
|
+
}
|
|
51177
|
+
return { ok: true };
|
|
51178
|
+
}
|
|
51179
|
+
|
|
50493
51180
|
// src/tui/tui-shell.ts
|
|
50494
51181
|
init_slate();
|
|
50495
51182
|
|
|
@@ -50854,6 +51541,11 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
50854
51541
|
description: "Show this session's SAC workspace and its slates",
|
|
50855
51542
|
modes: AGENT_ONLY
|
|
50856
51543
|
},
|
|
51544
|
+
{
|
|
51545
|
+
name: "/review",
|
|
51546
|
+
description: "Show project-wide items needing review (proposals, blocked sessions)",
|
|
51547
|
+
modes: AGENT_ONLY
|
|
51548
|
+
},
|
|
50857
51549
|
{
|
|
50858
51550
|
name: "/compact",
|
|
50859
51551
|
description: "Compact model context \u2014 /compact [focus] (archive kept)",
|
|
@@ -51217,10 +51909,10 @@ init_shell_config();
|
|
|
51217
51909
|
// src/lib/permission-mode-config.ts
|
|
51218
51910
|
init_permission_mode();
|
|
51219
51911
|
init_config_dir();
|
|
51220
|
-
import
|
|
51912
|
+
import path146 from "path";
|
|
51221
51913
|
var EMPTY3 = { schemaVersion: 1, projects: {} };
|
|
51222
51914
|
function permissionModeConfigPath(dir) {
|
|
51223
|
-
return
|
|
51915
|
+
return path146.join(keryxConfigDir(dir), "permission-mode.json");
|
|
51224
51916
|
}
|
|
51225
51917
|
function withRegistryLock2(dir, fn) {
|
|
51226
51918
|
return withFileLock(`${permissionModeConfigPath(dir)}.lock`, fn, {
|
|
@@ -51565,7 +52257,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
51565
52257
|
// src/lib/version-check.ts
|
|
51566
52258
|
init_config_dir();
|
|
51567
52259
|
init_fs();
|
|
51568
|
-
import
|
|
52260
|
+
import path147 from "path";
|
|
51569
52261
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
51570
52262
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
51571
52263
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -51767,7 +52459,7 @@ async function checkVersion(options) {
|
|
|
51767
52459
|
const now = options.now ?? Date.now;
|
|
51768
52460
|
const timestamp = now();
|
|
51769
52461
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
51770
|
-
const cacheFile =
|
|
52462
|
+
const cacheFile = path147.join(configDir, "version-check.json");
|
|
51771
52463
|
const cache = parseCache(cacheFile);
|
|
51772
52464
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
51773
52465
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -53950,6 +54642,20 @@ async function launchTuiAgentShell(opts) {
|
|
|
53950
54642
|
currentSlates = slates;
|
|
53951
54643
|
sbWorkspaceV.content = workspace === undefined ? otui.t`${otui.dim("\u2014")}` : otui.t`${otui.dim(`${shortenCwd(workspace.title, SIDEBAR_TEXT_WIDTH)} \xB7 ${workspace.status} \xB7 ${slates.length} slate${slates.length === 1 ? "" : "s"}`)}`;
|
|
53952
54644
|
};
|
|
54645
|
+
sidebar.add(new otui.TextRenderable(r, { id: "sb-review-k", content: otui.t`${otui.dim("Review")}`, marginTop: 1 }));
|
|
54646
|
+
const sbReviewV = new otui.TextRenderable(r, {
|
|
54647
|
+
id: "sb-review-v",
|
|
54648
|
+
content: otui.t`${otui.dim("\u2014")}`,
|
|
54649
|
+
onMouseDown: () => {
|
|
54650
|
+
showReview();
|
|
54651
|
+
}
|
|
54652
|
+
});
|
|
54653
|
+
sidebar.add(sbReviewV);
|
|
54654
|
+
const refreshReviewSidebar = async () => {
|
|
54655
|
+
const cwd = opts.session?.cwd ?? process.cwd();
|
|
54656
|
+
const count = catchUpItems(await loadInspectorCatchUp(cwd)).length;
|
|
54657
|
+
sbReviewV.content = count === 0 ? otui.t`${otui.dim("\u2014 nothing to review")}` : otui.t`${otui.yellow(`${count} item${count === 1 ? "" : "s"} need review`)}`;
|
|
54658
|
+
};
|
|
53953
54659
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-ctx-k", content: otui.t`${otui.dim("Context")}`, marginTop: 1 }));
|
|
53954
54660
|
const sbContext = new otui.TextRenderable(r, { id: "sb-ctx-v", content: otui.t`${otui.dim("0 tokens")}` });
|
|
53955
54661
|
sidebar.add(sbContext);
|
|
@@ -54390,6 +55096,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
54390
55096
|
}
|
|
54391
55097
|
slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
|
|
54392
55098
|
refreshWorkspaceSidebar();
|
|
55099
|
+
refreshReviewSidebar();
|
|
54393
55100
|
const paintSessionHeader = () => {
|
|
54394
55101
|
const label = `${currentSel.provider}/${currentSel.model}`;
|
|
54395
55102
|
const sid = shortSessionId(liveSession.summary.id);
|
|
@@ -54559,6 +55266,21 @@ Staying in the current session.
|
|
|
54559
55266
|
});
|
|
54560
55267
|
})();
|
|
54561
55268
|
};
|
|
55269
|
+
const showReview = () => {
|
|
55270
|
+
(async () => {
|
|
55271
|
+
const cwd = inspectorCwd();
|
|
55272
|
+
const items = catchUpItems(await loadInspectorCatchUp(cwd));
|
|
55273
|
+
openReview(otui, chrome, {
|
|
55274
|
+
items,
|
|
55275
|
+
acceptProposal: (item) => acceptProposalViaShell(makeCommandRunner(cwd), item.workspaceId, item.proposalId),
|
|
55276
|
+
onAccepted: () => {
|
|
55277
|
+
refreshReviewSidebar();
|
|
55278
|
+
},
|
|
55279
|
+
renderer: r,
|
|
55280
|
+
...inspectorKeys
|
|
55281
|
+
});
|
|
55282
|
+
})();
|
|
55283
|
+
};
|
|
54562
55284
|
const updateModelLabels = () => {
|
|
54563
55285
|
paintSessionHeader();
|
|
54564
55286
|
const label = `${currentSel.provider}/${currentSel.model}`;
|
|
@@ -55078,6 +55800,7 @@ Staying in the current session.
|
|
|
55078
55800
|
startNewSession();
|
|
55079
55801
|
slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
|
|
55080
55802
|
refreshWorkspaceSidebar();
|
|
55803
|
+
refreshReviewSidebar();
|
|
55081
55804
|
sessions.clear();
|
|
55082
55805
|
deps.resetSubagentBudget?.();
|
|
55083
55806
|
io.onSystem?.(`New session ${shortSessionId(liveSession.summary.id)} (previous kept on disk \xB7 /resume)
|
|
@@ -55216,6 +55939,10 @@ Staying in the current session.
|
|
|
55216
55939
|
showWorkspace();
|
|
55217
55940
|
return;
|
|
55218
55941
|
}
|
|
55942
|
+
if (isReviewCommand(command.name)) {
|
|
55943
|
+
showReview();
|
|
55944
|
+
return;
|
|
55945
|
+
}
|
|
55219
55946
|
if (command.name === "/copy") {
|
|
55220
55947
|
const target = newestBlock();
|
|
55221
55948
|
if (target === undefined || !copyBlock(target.id)) {
|
|
@@ -55535,6 +56262,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
55535
56262
|
const secs = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
55536
56263
|
stopBusy();
|
|
55537
56264
|
refreshWorkspaceSidebar();
|
|
56265
|
+
refreshReviewSidebar();
|
|
55538
56266
|
setMainAgent(turnFailed ? "failed" : "done", turnFailed ? "error" : "idle");
|
|
55539
56267
|
try {
|
|
55540
56268
|
flushSessionCheckpoint();
|
|
@@ -56903,7 +57631,7 @@ ${indentBlock(style.dim(context), GUTTER)}`);
|
|
|
56903
57631
|
out(`${GUTTER}${style.yellow(hint)}
|
|
56904
57632
|
`);
|
|
56905
57633
|
}
|
|
56906
|
-
const rememberable = !evaled.destructive && !evaled.credentials;
|
|
57634
|
+
const rememberable = !evaled.destructive && !evaled.credentials && !evaled.sacReviewConfirmation;
|
|
56907
57635
|
const prompt = rememberable ? "[y/N/A=always] " : "[y/N] ";
|
|
56908
57636
|
out(`
|
|
56909
57637
|
${GUTTER}${style.yellow(`Run: ${evaled.command}`)} ${style.dim(prompt)}`);
|
|
@@ -57445,7 +58173,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
57445
58173
|
modelId: sel.model
|
|
57446
58174
|
}),
|
|
57447
58175
|
maxToolCalls: resolveAgentMaxToolCalls(),
|
|
57448
|
-
idSeq: () =>
|
|
58176
|
+
idSeq: () => randomUUID25(),
|
|
57449
58177
|
...resetSubagentBudget !== undefined ? { resetSubagentBudget } : {}
|
|
57450
58178
|
};
|
|
57451
58179
|
};
|
|
@@ -57477,7 +58205,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
57477
58205
|
makeShellDeps: (sel) => ({
|
|
57478
58206
|
makeProvider: chatFactory,
|
|
57479
58207
|
clock: () => new Date().toISOString(),
|
|
57480
|
-
idSeq: () =>
|
|
58208
|
+
idSeq: () => randomUUID25(),
|
|
57481
58209
|
initial: sel,
|
|
57482
58210
|
session: {
|
|
57483
58211
|
cwd,
|
|
@@ -57548,7 +58276,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
57548
58276
|
const deps = {
|
|
57549
58277
|
makeProvider: baseFactory,
|
|
57550
58278
|
clock: () => new Date().toISOString(),
|
|
57551
|
-
idSeq: () =>
|
|
58279
|
+
idSeq: () => randomUUID25(),
|
|
57552
58280
|
initial: baseUrl2 === undefined ? { provider, model } : { provider, model, baseUrl: baseUrl2 },
|
|
57553
58281
|
selectProviderModel: realSelectProviderModel(baseUrl2)
|
|
57554
58282
|
};
|
|
@@ -57599,7 +58327,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
57599
58327
|
modelId: model
|
|
57600
58328
|
}),
|
|
57601
58329
|
maxToolCalls: resolveAgentMaxToolCalls(),
|
|
57602
|
-
idSeq: () =>
|
|
58330
|
+
idSeq: () => randomUUID25(),
|
|
57603
58331
|
...resetSubagentBudget !== undefined ? { resetSubagentBudget } : {}
|
|
57604
58332
|
};
|
|
57605
58333
|
let resumeId = flags.resumeId;
|
|
@@ -57771,7 +58499,7 @@ Shell:
|
|
|
57771
58499
|
init_fs();
|
|
57772
58500
|
import { readFile as readFile78 } from "fs/promises";
|
|
57773
58501
|
import { stdin } from "process";
|
|
57774
|
-
import
|
|
58502
|
+
import path148 from "path";
|
|
57775
58503
|
var MODULES = [
|
|
57776
58504
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
57777
58505
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -57817,8 +58545,8 @@ async function modulesCommand(args2 = []) {
|
|
|
57817
58545
|
return;
|
|
57818
58546
|
}
|
|
57819
58547
|
const wantsJson = args2.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
57820
|
-
const metaprojectRoot =
|
|
57821
|
-
const manifestPath =
|
|
58548
|
+
const metaprojectRoot = path148.join(process.cwd(), ".metaproject");
|
|
58549
|
+
const manifestPath = path148.join(metaprojectRoot, "metaproject.json");
|
|
57822
58550
|
if (!await pathExists(manifestPath)) {
|
|
57823
58551
|
if (wantsJson) {
|
|
57824
58552
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -57932,18 +58660,18 @@ function printHelp16() {
|
|
|
57932
58660
|
}
|
|
57933
58661
|
|
|
57934
58662
|
// src/commands/serve.ts
|
|
57935
|
-
import { randomUUID as
|
|
58663
|
+
import { randomUUID as randomUUID28 } from "crypto";
|
|
57936
58664
|
|
|
57937
58665
|
// src/lib/serve-config.ts
|
|
57938
58666
|
init_config_dir();
|
|
57939
58667
|
import { existsSync as existsSync27 } from "fs";
|
|
57940
|
-
import
|
|
58668
|
+
import path149 from "path";
|
|
57941
58669
|
var SERVE_CONFIG_SCHEMA_VERSION = "1.0.0";
|
|
57942
58670
|
var DEFAULT_SERVE_BIND_ADDRESS = "127.0.0.1";
|
|
57943
58671
|
var DEFAULT_SERVE_PORT = 7377;
|
|
57944
58672
|
var DEFAULT_SERVE_PROFILE = "remote-restricted";
|
|
57945
58673
|
function serveConfigPath(dir) {
|
|
57946
|
-
return
|
|
58674
|
+
return path149.join(keryxConfigDir(dir), "serve.json");
|
|
57947
58675
|
}
|
|
57948
58676
|
function parseIpv4(value) {
|
|
57949
58677
|
const parts = value.split(".");
|
|
@@ -58259,7 +58987,7 @@ function saveServeConfig(config, dir, onWarn) {
|
|
|
58259
58987
|
|
|
58260
58988
|
// src/lib/serve-credential.ts
|
|
58261
58989
|
init_config_dir();
|
|
58262
|
-
import { createHash as createHash33, randomBytes as randomBytes3, randomUUID as
|
|
58990
|
+
import { createHash as createHash33, randomBytes as randomBytes3, randomUUID as randomUUID26 } from "crypto";
|
|
58263
58991
|
import {
|
|
58264
58992
|
chmodSync as chmodSync4,
|
|
58265
58993
|
closeSync as closeSync3,
|
|
@@ -58271,9 +58999,9 @@ import {
|
|
|
58271
58999
|
unlinkSync as unlinkSync3,
|
|
58272
59000
|
writeFileSync as writeFileSync8
|
|
58273
59001
|
} from "fs";
|
|
58274
|
-
import
|
|
59002
|
+
import path150 from "path";
|
|
58275
59003
|
function serveCredentialPath(dir) {
|
|
58276
|
-
return
|
|
59004
|
+
return path150.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
58277
59005
|
}
|
|
58278
59006
|
function constantTimeEqual(a, b) {
|
|
58279
59007
|
const width = Math.max(a.length, b.length);
|
|
@@ -58343,7 +59071,7 @@ function readServeCredential(dir) {
|
|
|
58343
59071
|
}
|
|
58344
59072
|
function writeStore(store, dir) {
|
|
58345
59073
|
const file = serveCredentialPath(dir);
|
|
58346
|
-
const temp = `${file}.${
|
|
59074
|
+
const temp = `${file}.${randomUUID26()}.tmp`;
|
|
58347
59075
|
try {
|
|
58348
59076
|
ensureKeryxConfigDir(dir);
|
|
58349
59077
|
const handle = openSync3(temp, "wx", 384);
|
|
@@ -58383,7 +59111,7 @@ function mintRecord(now) {
|
|
|
58383
59111
|
const salt = randomBytes3(32).toString("hex");
|
|
58384
59112
|
return {
|
|
58385
59113
|
token,
|
|
58386
|
-
record: { id:
|
|
59114
|
+
record: { id: randomUUID26(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
58387
59115
|
};
|
|
58388
59116
|
}
|
|
58389
59117
|
function issueServeToken(dir, now = () => new Date().toISOString(), onWaiting) {
|
|
@@ -58507,22 +59235,22 @@ class AuthFailureThrottle {
|
|
|
58507
59235
|
init_config_dir();
|
|
58508
59236
|
import { createHash as createHash34 } from "crypto";
|
|
58509
59237
|
import { existsSync as existsSync29, readdirSync as readdirSync2, rmSync as rmSync2 } from "fs";
|
|
58510
|
-
import
|
|
59238
|
+
import path151 from "path";
|
|
58511
59239
|
var MAX_TURN_EVENTS = 1e4;
|
|
58512
59240
|
function turnsRoot(dir) {
|
|
58513
|
-
return
|
|
59241
|
+
return path151.join(keryxConfigDir(dir), "turns");
|
|
58514
59242
|
}
|
|
58515
59243
|
function turnDir(turnId, dir) {
|
|
58516
|
-
return
|
|
59244
|
+
return path151.join(turnsRoot(dir), turnId);
|
|
58517
59245
|
}
|
|
58518
59246
|
function keyPath(project, idempotencyKey, dir) {
|
|
58519
59247
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
58520
59248
|
const digest2 = createHash34("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
58521
|
-
return
|
|
59249
|
+
return path151.join(turnsRoot(dir), "keys", `${digest2}.json`);
|
|
58522
59250
|
}
|
|
58523
59251
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
58524
59252
|
const digest2 = createHash34("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
58525
|
-
return
|
|
59253
|
+
return path151.join(turnsRoot(dir), "keys", `${digest2}.json`);
|
|
58526
59254
|
}
|
|
58527
59255
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
58528
59256
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -58586,7 +59314,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
58586
59314
|
}
|
|
58587
59315
|
function createTurnRecord(record, dir) {
|
|
58588
59316
|
ensureTurnDir(record.turnId, dir);
|
|
58589
|
-
writeOwnerOnlyFile(
|
|
59317
|
+
writeOwnerOnlyFile(path151.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
58590
59318
|
`);
|
|
58591
59319
|
}
|
|
58592
59320
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -58595,12 +59323,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
58595
59323
|
}
|
|
58596
59324
|
const line = JSON.stringify(event);
|
|
58597
59325
|
try {
|
|
58598
|
-
appendOwnerOnlyLine(
|
|
59326
|
+
appendOwnerOnlyLine(path151.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
58599
59327
|
} catch (error2) {
|
|
58600
59328
|
if (error2?.code !== "ENOENT") {
|
|
58601
59329
|
throw error2;
|
|
58602
59330
|
}
|
|
58603
|
-
appendOwnerOnlyLine(
|
|
59331
|
+
appendOwnerOnlyLine(path151.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
58604
59332
|
}
|
|
58605
59333
|
return true;
|
|
58606
59334
|
}
|
|
@@ -58608,7 +59336,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
58608
59336
|
if (!isTurnId(turnId)) {
|
|
58609
59337
|
return { ok: false, reason: "not-a-turn-id" };
|
|
58610
59338
|
}
|
|
58611
|
-
const read = readTurnFile(
|
|
59339
|
+
const read = readTurnFile(path151.join(turnDir(turnId, dir), "events.jsonl"));
|
|
58612
59340
|
if (!read.ok) {
|
|
58613
59341
|
if (isDefiniteAbsence2(read.reason)) {
|
|
58614
59342
|
return { ok: true, value: [] };
|
|
@@ -58636,7 +59364,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
58636
59364
|
if (!isTurnId(turnId)) {
|
|
58637
59365
|
return { ok: false, reason: "not-a-turn-id" };
|
|
58638
59366
|
}
|
|
58639
|
-
const read = readTurnFile(
|
|
59367
|
+
const read = readTurnFile(path151.join(turnDir(turnId, dir), "turn.json"));
|
|
58640
59368
|
if (!read.ok) {
|
|
58641
59369
|
return { ok: false, reason: read.reason };
|
|
58642
59370
|
}
|
|
@@ -58655,7 +59383,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
58655
59383
|
if (!record.ok) {
|
|
58656
59384
|
return false;
|
|
58657
59385
|
}
|
|
58658
|
-
writeOwnerOnlyFile(
|
|
59386
|
+
writeOwnerOnlyFile(path151.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
58659
59387
|
`);
|
|
58660
59388
|
return true;
|
|
58661
59389
|
}
|
|
@@ -58700,8 +59428,8 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
58700
59428
|
}
|
|
58701
59429
|
|
|
58702
59430
|
// src/lib/serve-turn.ts
|
|
58703
|
-
import { randomUUID as
|
|
58704
|
-
import
|
|
59431
|
+
import { randomUUID as randomUUID27 } from "crypto";
|
|
59432
|
+
import path152 from "path";
|
|
58705
59433
|
init_service();
|
|
58706
59434
|
var REMOTE_ORIGIN = "remote:http";
|
|
58707
59435
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -58770,9 +59498,9 @@ function isUuid(value) {
|
|
|
58770
59498
|
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);
|
|
58771
59499
|
}
|
|
58772
59500
|
function resolveProject(declared, dir) {
|
|
58773
|
-
const wanted =
|
|
59501
|
+
const wanted = path152.resolve(declared);
|
|
58774
59502
|
for (const entry of listProjects(dir, () => {})) {
|
|
58775
|
-
if (
|
|
59503
|
+
if (path152.resolve(entry.path) === wanted) {
|
|
58776
59504
|
return { ok: true, project: entry.path };
|
|
58777
59505
|
}
|
|
58778
59506
|
}
|
|
@@ -58819,7 +59547,7 @@ async function redactOut(security, text) {
|
|
|
58819
59547
|
async function runRemoteTurn(input2) {
|
|
58820
59548
|
const scanRoot = input2.scanRoot;
|
|
58821
59549
|
const security = createSecurityService(scanRoot);
|
|
58822
|
-
const newId = input2.newId ?? (() =>
|
|
59550
|
+
const newId = input2.newId ?? (() => randomUUID27());
|
|
58823
59551
|
const clock = input2.clock ?? (() => new Date().toISOString());
|
|
58824
59552
|
const turnId = input2.turnId ?? newId();
|
|
58825
59553
|
const sessionId = input2.request.sessionId ?? newId();
|
|
@@ -58962,7 +59690,7 @@ function outcomeOf(status, gate, unresolvedBlockerIds) {
|
|
|
58962
59690
|
}
|
|
58963
59691
|
function createSubmitTurn(deps) {
|
|
58964
59692
|
return async (request, project) => {
|
|
58965
|
-
const turnId = (deps.newId ?? (() =>
|
|
59693
|
+
const turnId = (deps.newId ?? (() => randomUUID27()))();
|
|
58966
59694
|
const scanned = await scanPrompt(deps.dir, request.prompt);
|
|
58967
59695
|
if (scanned.rejected) {
|
|
58968
59696
|
return { kind: "rejected" };
|
|
@@ -59649,7 +60377,7 @@ function runConfig(args2) {
|
|
|
59649
60377
|
return;
|
|
59650
60378
|
}
|
|
59651
60379
|
const credential2 = readServeCredential();
|
|
59652
|
-
const credentialId = credential2.status === "ok" ? credential2.record.id :
|
|
60380
|
+
const credentialId = credential2.status === "ok" ? credential2.record.id : randomUUID28();
|
|
59653
60381
|
const config = defaultServeConfig(credentialId, {
|
|
59654
60382
|
address: parsed.parsed.values.get("--bind") ?? DEFAULT_SERVE_BIND_ADDRESS,
|
|
59655
60383
|
port: port ?? DEFAULT_SERVE_PORT,
|
|
@@ -59805,9 +60533,9 @@ function printHelp17() {
|
|
|
59805
60533
|
|
|
59806
60534
|
// src/commands/update.ts
|
|
59807
60535
|
import { spawn as spawn5 } from "child_process";
|
|
59808
|
-
import { chmod as chmod4, mkdir as mkdir54, readFile as readFile79, readdir as
|
|
60536
|
+
import { chmod as chmod4, mkdir as mkdir54, readFile as readFile79, readdir as readdir25, writeFile as writeFile48 } from "fs/promises";
|
|
59809
60537
|
import { access as access4, constants as constants2, existsSync as existsSync30 } from "fs";
|
|
59810
|
-
import
|
|
60538
|
+
import path153 from "path";
|
|
59811
60539
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
59812
60540
|
init_config();
|
|
59813
60541
|
init_config2();
|
|
@@ -59823,8 +60551,8 @@ async function updateCommand(args2 = []) {
|
|
|
59823
60551
|
return;
|
|
59824
60552
|
}
|
|
59825
60553
|
const projectRoot = process.cwd();
|
|
59826
|
-
const metaprojectRoot =
|
|
59827
|
-
banner("keryx update", `Refreshing the .metaproject workspace in ${
|
|
60554
|
+
const metaprojectRoot = path153.join(projectRoot, ".metaproject");
|
|
60555
|
+
banner("keryx update", `Refreshing the .metaproject workspace in ${path153.basename(projectRoot)}/`);
|
|
59828
60556
|
if (!await pathExists(metaprojectRoot)) {
|
|
59829
60557
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
59830
60558
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -59870,12 +60598,12 @@ async function updateCommand(args2 = []) {
|
|
|
59870
60598
|
nextSteps(steps);
|
|
59871
60599
|
}
|
|
59872
60600
|
async function refreshServiceFiles(projectRoot, options) {
|
|
59873
|
-
const metaprojectRoot =
|
|
60601
|
+
const metaprojectRoot = path153.join(projectRoot, ".metaproject");
|
|
59874
60602
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
59875
60603
|
const manifest = manifestState.manifest;
|
|
59876
60604
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
59877
60605
|
if (manifestState.migrated) {
|
|
59878
|
-
await writeFile48(
|
|
60606
|
+
await writeFile48(path153.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
59879
60607
|
`, "utf8");
|
|
59880
60608
|
}
|
|
59881
60609
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -59912,11 +60640,11 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59912
60640
|
enableSecurity,
|
|
59913
60641
|
enableSac
|
|
59914
60642
|
});
|
|
59915
|
-
await writeTextIfChanged4(
|
|
59916
|
-
await writeTextIfChanged4(
|
|
59917
|
-
await writeTextIfChanged4(
|
|
59918
|
-
await writeTextIfChanged4(
|
|
59919
|
-
await writeTextIfChanged4(
|
|
60643
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
|
|
60644
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
|
|
60645
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "rules", "README.md"), renderProjectRulesReadme());
|
|
60646
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
|
|
60647
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "index.md"), renderIndexMarkdown({
|
|
59920
60648
|
enableGdgraph,
|
|
59921
60649
|
enableGdctx,
|
|
59922
60650
|
enableGdwiki,
|
|
@@ -59929,7 +60657,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59929
60657
|
ruleSources,
|
|
59930
60658
|
hasDistilledEntrypoints: await hasDistilledEntrypoints(metaprojectRoot)
|
|
59931
60659
|
}));
|
|
59932
|
-
await writeTextIfChanged4(
|
|
60660
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
59933
60661
|
enableGdgraph,
|
|
59934
60662
|
enableGdctx,
|
|
59935
60663
|
enableGdwiki,
|
|
@@ -59941,7 +60669,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59941
60669
|
enableSecurity,
|
|
59942
60670
|
data: dashboardData
|
|
59943
60671
|
}));
|
|
59944
|
-
await writeTextIfMissing4(
|
|
60672
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
59945
60673
|
enableGdgraph,
|
|
59946
60674
|
enableGdctx,
|
|
59947
60675
|
enableGdwiki,
|
|
@@ -59954,31 +60682,31 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59954
60682
|
}));
|
|
59955
60683
|
if (enableGdgraph) {
|
|
59956
60684
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
59957
|
-
await writeTextIfChanged4(
|
|
59958
|
-
await writeTextIfChanged4(
|
|
59959
|
-
await writeTextIfChanged4(
|
|
60685
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
|
|
60686
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
|
|
60687
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
|
|
59960
60688
|
await seedAssetsLock(metaprojectRoot);
|
|
59961
60689
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
59962
60690
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
59963
60691
|
}
|
|
59964
60692
|
}
|
|
59965
60693
|
if (enableGdctx) {
|
|
59966
|
-
await writeTextIfMissing4(
|
|
59967
|
-
await writeTextIfChanged4(
|
|
59968
|
-
await writeTextIfChanged4(
|
|
59969
|
-
await writeTextIfChanged4(
|
|
60694
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
|
|
60695
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
|
|
60696
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
|
|
60697
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
|
|
59970
60698
|
}
|
|
59971
60699
|
if (enableGdwiki) {
|
|
59972
|
-
await writeTextIfMissing4(
|
|
59973
|
-
await writeTextIfChanged4(
|
|
59974
|
-
await writeTextIfChanged4(
|
|
60700
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
|
|
60701
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
|
|
60702
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
|
|
59975
60703
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
59976
60704
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
59977
60705
|
}
|
|
59978
60706
|
}
|
|
59979
60707
|
if (enableSac) {
|
|
59980
|
-
await writeTextIfMissing4(
|
|
59981
|
-
await writeTextIfChanged4(
|
|
60708
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
60709
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
59982
60710
|
}
|
|
59983
60711
|
if (enableGdskills) {
|
|
59984
60712
|
await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
|
|
@@ -59987,25 +60715,25 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59987
60715
|
}
|
|
59988
60716
|
}
|
|
59989
60717
|
if (enableHealth) {
|
|
59990
|
-
await writeTextIfMissing4(
|
|
59991
|
-
await writeTextIfChanged4(
|
|
59992
|
-
await writeTextIfChanged4(
|
|
59993
|
-
await writeTextIfChanged4(
|
|
60718
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
|
|
60719
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
|
|
60720
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
|
|
60721
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
|
|
59994
60722
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
59995
60723
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
59996
60724
|
}
|
|
59997
60725
|
}
|
|
59998
60726
|
if (enableTesting) {
|
|
59999
|
-
await writeTextIfMissing4(
|
|
60727
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
60000
60728
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
60001
60729
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
60002
60730
|
}));
|
|
60003
|
-
await writeTextIfChanged4(
|
|
60004
|
-
await writeTextIfChanged4(
|
|
60005
|
-
await writeTextIfChanged4(
|
|
60731
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
|
|
60732
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
|
|
60733
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
|
|
60006
60734
|
if (enableGdwiki) {
|
|
60007
|
-
await writeTextIfMissing4(
|
|
60008
|
-
await writeTextIfMissing4(
|
|
60735
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
60736
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
60009
60737
|
}
|
|
60010
60738
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
60011
60739
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -60018,24 +60746,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
60018
60746
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
60019
60747
|
}
|
|
60020
60748
|
if (enableMemory) {
|
|
60021
|
-
await writeTextIfMissing4(
|
|
60022
|
-
await writeTextIfMissing4(
|
|
60023
|
-
await writeTextIfChanged4(
|
|
60024
|
-
await writeTextIfChanged4(
|
|
60025
|
-
await writeTextIfChanged4(
|
|
60749
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
|
|
60750
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
|
|
60751
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
|
|
60752
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
|
|
60753
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
|
|
60026
60754
|
}
|
|
60027
60755
|
if (enableTasks) {
|
|
60028
|
-
await writeTextIfChanged4(
|
|
60029
|
-
await writeTextIfChanged4(
|
|
60030
|
-
await writeTextIfChanged4(
|
|
60031
|
-
await writeTextIfChanged4(
|
|
60032
|
-
await writeTextIfChanged4(
|
|
60033
|
-
await writeTextIfChanged4(
|
|
60756
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
|
|
60757
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
|
|
60758
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
|
|
60759
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
|
|
60760
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
|
|
60761
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
|
|
60034
60762
|
}
|
|
60035
60763
|
if (enableSecurity) {
|
|
60036
|
-
await writeTextIfMissing4(
|
|
60037
|
-
await writeTextIfChanged4(
|
|
60038
|
-
await writeTextIfChanged4(
|
|
60764
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
|
|
60765
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
|
|
60766
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
|
|
60039
60767
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
60040
60768
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
60041
60769
|
}
|
|
@@ -60086,13 +60814,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
60086
60814
|
};
|
|
60087
60815
|
}
|
|
60088
60816
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
60089
|
-
const metaprojectRoot =
|
|
60817
|
+
const metaprojectRoot = path153.join(projectRoot, ".metaproject");
|
|
60090
60818
|
if (!await pathExists(metaprojectRoot)) {
|
|
60091
60819
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
60092
60820
|
}
|
|
60093
60821
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
60094
60822
|
const data = await collectDashboardData(metaprojectRoot);
|
|
60095
|
-
const dashboardPath =
|
|
60823
|
+
const dashboardPath = path153.join(metaprojectRoot, "keryx-dashboard.html");
|
|
60096
60824
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
60097
60825
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
60098
60826
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -60112,7 +60840,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
60112
60840
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
60113
60841
|
return true;
|
|
60114
60842
|
}
|
|
60115
|
-
const hookPath =
|
|
60843
|
+
const hookPath = path153.join(projectRoot, ".git", "hooks", "post-commit");
|
|
60116
60844
|
if (!await pathExists(hookPath)) {
|
|
60117
60845
|
return false;
|
|
60118
60846
|
}
|
|
@@ -60132,11 +60860,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
60132
60860
|
if (testing) {
|
|
60133
60861
|
data.testing = testing;
|
|
60134
60862
|
}
|
|
60135
|
-
const wiki = await collectMarkdownPages(
|
|
60863
|
+
const wiki = await collectMarkdownPages(path153.join(metaprojectRoot, "wiki"), "wiki");
|
|
60136
60864
|
if (wiki.length > 0) {
|
|
60137
60865
|
data.wiki = { pages: wiki };
|
|
60138
60866
|
}
|
|
60139
|
-
const memory = await collectMarkdownPages(
|
|
60867
|
+
const memory = await collectMarkdownPages(path153.join(metaprojectRoot, "memory"), "memory");
|
|
60140
60868
|
if (memory.length > 0) {
|
|
60141
60869
|
data.memory = { entries: memory };
|
|
60142
60870
|
}
|
|
@@ -60151,19 +60879,19 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
60151
60879
|
return data;
|
|
60152
60880
|
}
|
|
60153
60881
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
60154
|
-
const flowsRoot2 =
|
|
60882
|
+
const flowsRoot2 = path153.join(metaprojectRoot, "flows");
|
|
60155
60883
|
if (!await pathExists(flowsRoot2)) {
|
|
60156
60884
|
return null;
|
|
60157
60885
|
}
|
|
60158
60886
|
let dirEntries;
|
|
60159
60887
|
try {
|
|
60160
|
-
dirEntries = (await
|
|
60888
|
+
dirEntries = (await readdir25(flowsRoot2, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
|
|
60161
60889
|
} catch {
|
|
60162
60890
|
return null;
|
|
60163
60891
|
}
|
|
60164
60892
|
const flows = [];
|
|
60165
60893
|
for (const dir of dirEntries) {
|
|
60166
|
-
const flowPath =
|
|
60894
|
+
const flowPath = path153.join(flowsRoot2, dir, "flow.json");
|
|
60167
60895
|
if (!await pathExists(flowPath)) {
|
|
60168
60896
|
continue;
|
|
60169
60897
|
}
|
|
@@ -60171,7 +60899,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
60171
60899
|
const flow = JSON.parse(await readFile79(flowPath, "utf8"));
|
|
60172
60900
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
60173
60901
|
let acTotal = 0;
|
|
60174
|
-
const acPath2 =
|
|
60902
|
+
const acPath2 = path153.join(flowsRoot2, dir, "acceptance-criteria.md");
|
|
60175
60903
|
if (await pathExists(acPath2)) {
|
|
60176
60904
|
const acContent = await readFile79(acPath2, "utf8");
|
|
60177
60905
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -60225,7 +60953,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
60225
60953
|
"data/testing/context.md"
|
|
60226
60954
|
];
|
|
60227
60955
|
for (const href of staticHrefs) {
|
|
60228
|
-
const filePath =
|
|
60956
|
+
const filePath = path153.join(metaprojectRoot, ...href.split("/"));
|
|
60229
60957
|
if (!await pathExists(filePath)) {
|
|
60230
60958
|
continue;
|
|
60231
60959
|
}
|
|
@@ -60242,7 +60970,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
60242
60970
|
return docs;
|
|
60243
60971
|
}
|
|
60244
60972
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
60245
|
-
const reportPath2 =
|
|
60973
|
+
const reportPath2 = path153.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
60246
60974
|
if (!await pathExists(reportPath2)) {
|
|
60247
60975
|
return;
|
|
60248
60976
|
}
|
|
@@ -60351,8 +61079,8 @@ function metricToScope(metric) {
|
|
|
60351
61079
|
};
|
|
60352
61080
|
}
|
|
60353
61081
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
60354
|
-
const nodesPath =
|
|
60355
|
-
const edgesPath =
|
|
61082
|
+
const nodesPath = path153.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
61083
|
+
const edgesPath = path153.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
60356
61084
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
60357
61085
|
return;
|
|
60358
61086
|
}
|
|
@@ -60403,8 +61131,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
60403
61131
|
};
|
|
60404
61132
|
}
|
|
60405
61133
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
60406
|
-
const reportPath2 =
|
|
60407
|
-
const contextPath =
|
|
61134
|
+
const reportPath2 = path153.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
61135
|
+
const contextPath = path153.join(metaprojectRoot, "data", "testing", "context.md");
|
|
60408
61136
|
if (await pathExists(reportPath2)) {
|
|
60409
61137
|
const report = JSON.parse(await readFile79(reportPath2, "utf8"));
|
|
60410
61138
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -60433,7 +61161,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
60433
61161
|
const files = await listMarkdownFiles(root);
|
|
60434
61162
|
const pages = [];
|
|
60435
61163
|
for (const filePath of files.slice(0, 40)) {
|
|
60436
|
-
const relativePath =
|
|
61164
|
+
const relativePath = path153.relative(root, filePath).split(path153.sep).join("/");
|
|
60437
61165
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
60438
61166
|
continue;
|
|
60439
61167
|
}
|
|
@@ -60451,10 +61179,10 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
60451
61179
|
return pages;
|
|
60452
61180
|
}
|
|
60453
61181
|
async function listMarkdownFiles(root) {
|
|
60454
|
-
const entries = await
|
|
61182
|
+
const entries = await readdir25(root, { withFileTypes: true });
|
|
60455
61183
|
const files = [];
|
|
60456
61184
|
for (const entry of entries) {
|
|
60457
|
-
const fullPath =
|
|
61185
|
+
const fullPath = path153.join(root, entry.name);
|
|
60458
61186
|
if (entry.isDirectory()) {
|
|
60459
61187
|
files.push(...await listMarkdownFiles(fullPath));
|
|
60460
61188
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -60502,7 +61230,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
60502
61230
|
const manifest = {
|
|
60503
61231
|
schemaVersion: 1,
|
|
60504
61232
|
standardVersion: STANDARD_VERSION,
|
|
60505
|
-
name: `${
|
|
61233
|
+
name: `${path153.basename(path153.dirname(metaprojectRoot))}-metaproject`,
|
|
60506
61234
|
createdBy: "keryx",
|
|
60507
61235
|
profiles: computeProfiles(enabledModuleKeys2),
|
|
60508
61236
|
paths: {
|
|
@@ -60603,11 +61331,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
60603
61331
|
metaproject: ".metaproject/index.md"
|
|
60604
61332
|
}
|
|
60605
61333
|
};
|
|
60606
|
-
await writeFile48(
|
|
61334
|
+
await writeFile48(path153.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
60607
61335
|
`, "utf8");
|
|
60608
61336
|
}
|
|
60609
61337
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
60610
|
-
const manifestPath =
|
|
61338
|
+
const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
|
|
60611
61339
|
if (!await pathExists(manifestPath)) {
|
|
60612
61340
|
return;
|
|
60613
61341
|
}
|
|
@@ -60630,7 +61358,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
60630
61358
|
`, "utf8");
|
|
60631
61359
|
}
|
|
60632
61360
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
60633
|
-
const manifestPath =
|
|
61361
|
+
const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
|
|
60634
61362
|
if (!await pathExists(manifestPath)) {
|
|
60635
61363
|
return;
|
|
60636
61364
|
}
|
|
@@ -60666,72 +61394,72 @@ async function updateRuntime(projectRoot) {
|
|
|
60666
61394
|
}
|
|
60667
61395
|
}
|
|
60668
61396
|
async function findRuntimeRoot(projectRoot) {
|
|
60669
|
-
const projectRuntime =
|
|
60670
|
-
if (await pathExists(
|
|
61397
|
+
const projectRuntime = path153.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
61398
|
+
if (await pathExists(path153.join(projectRuntime, ".git"))) {
|
|
60671
61399
|
return projectRuntime;
|
|
60672
61400
|
}
|
|
60673
61401
|
const home = process.env.HOME;
|
|
60674
61402
|
if (!home) {
|
|
60675
61403
|
return null;
|
|
60676
61404
|
}
|
|
60677
|
-
const globalRuntime =
|
|
60678
|
-
if (await pathExists(
|
|
61405
|
+
const globalRuntime = path153.join(home, ".keryx", "keryx");
|
|
61406
|
+
if (await pathExists(path153.join(globalRuntime, ".git"))) {
|
|
60679
61407
|
return globalRuntime;
|
|
60680
61408
|
}
|
|
60681
61409
|
return null;
|
|
60682
61410
|
}
|
|
60683
61411
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
60684
61412
|
const dirs = [
|
|
60685
|
-
|
|
60686
|
-
|
|
60687
|
-
|
|
60688
|
-
|
|
60689
|
-
|
|
61413
|
+
path153.join(metaprojectRoot, "core"),
|
|
61414
|
+
path153.join(metaprojectRoot, "hooks", "post-update.d"),
|
|
61415
|
+
path153.join(metaprojectRoot, "modules"),
|
|
61416
|
+
path153.join(metaprojectRoot, "rules"),
|
|
61417
|
+
path153.join(metaprojectRoot, "skills", "project-rules"),
|
|
60690
61418
|
...modules.enableGdgraph ? [
|
|
60691
|
-
|
|
60692
|
-
|
|
61419
|
+
path153.join(metaprojectRoot, "core", "gdgraph"),
|
|
61420
|
+
path153.join(metaprojectRoot, "skills", "gdgraph")
|
|
60693
61421
|
] : [],
|
|
60694
61422
|
...modules.enableGdctx ? [
|
|
60695
|
-
|
|
60696
|
-
|
|
61423
|
+
path153.join(metaprojectRoot, "core", "gdctx"),
|
|
61424
|
+
path153.join(metaprojectRoot, "skills", "gdctx")
|
|
60697
61425
|
] : [],
|
|
60698
61426
|
...modules.enableGdwiki ? [
|
|
60699
|
-
|
|
60700
|
-
|
|
61427
|
+
path153.join(metaprojectRoot, "skills", "gdwiki"),
|
|
61428
|
+
path153.join(metaprojectRoot, "wiki", "templates")
|
|
60701
61429
|
] : [],
|
|
60702
61430
|
...modules.enableHealth ? [
|
|
60703
|
-
|
|
60704
|
-
|
|
61431
|
+
path153.join(metaprojectRoot, "core", "health"),
|
|
61432
|
+
path153.join(metaprojectRoot, "skills", "health")
|
|
60705
61433
|
] : [],
|
|
60706
61434
|
...modules.enableTesting ? [
|
|
60707
|
-
|
|
60708
|
-
|
|
61435
|
+
path153.join(metaprojectRoot, "core", "testing"),
|
|
61436
|
+
path153.join(metaprojectRoot, "skills", "testing")
|
|
60709
61437
|
] : [],
|
|
60710
61438
|
...modules.enableMemory ? [
|
|
60711
|
-
|
|
60712
|
-
|
|
60713
|
-
|
|
61439
|
+
path153.join(metaprojectRoot, "core", "memory"),
|
|
61440
|
+
path153.join(metaprojectRoot, "skills", "memory"),
|
|
61441
|
+
path153.join(metaprojectRoot, "memory", "templates")
|
|
60714
61442
|
] : [],
|
|
60715
61443
|
...modules.enableTasks ? [
|
|
60716
|
-
|
|
60717
|
-
|
|
61444
|
+
path153.join(metaprojectRoot, "flows"),
|
|
61445
|
+
path153.join(metaprojectRoot, "skills", "flow")
|
|
60718
61446
|
] : [],
|
|
60719
61447
|
...modules.enableSecurity ? [
|
|
60720
|
-
|
|
61448
|
+
path153.join(metaprojectRoot, "core", "security")
|
|
60721
61449
|
] : [],
|
|
60722
61450
|
...modules.enableSac ? [
|
|
60723
|
-
|
|
61451
|
+
path153.join(metaprojectRoot, "skills", "sac")
|
|
60724
61452
|
] : []
|
|
60725
61453
|
];
|
|
60726
61454
|
await Promise.all(dirs.map((dir) => mkdir54(dir, { recursive: true })));
|
|
60727
61455
|
}
|
|
60728
61456
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
60729
|
-
const gdgraphCoreRoot =
|
|
61457
|
+
const gdgraphCoreRoot = path153.join(metaprojectRoot, "core", "gdgraph");
|
|
60730
61458
|
await mkdir54(gdgraphCoreRoot, { recursive: true });
|
|
60731
61459
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
60732
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
61460
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path153.join(gdgraphCoreRoot, file));
|
|
60733
61461
|
}
|
|
60734
|
-
await writeTextIfChanged4(
|
|
61462
|
+
await writeTextIfChanged4(path153.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
60735
61463
|
}
|
|
60736
61464
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
60737
61465
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
@@ -60739,7 +61467,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
|
60739
61467
|
return;
|
|
60740
61468
|
}
|
|
60741
61469
|
await mkdir54(hooksRoot, { recursive: true });
|
|
60742
|
-
const hookPath =
|
|
61470
|
+
const hookPath = path153.join(hooksRoot, hookName);
|
|
60743
61471
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
60744
61472
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
60745
61473
|
const managedBlock = `${blockStart}
|
|
@@ -60760,7 +61488,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
60760
61488
|
if (!hooksRoot) {
|
|
60761
61489
|
return;
|
|
60762
61490
|
}
|
|
60763
|
-
const hookPath =
|
|
61491
|
+
const hookPath = path153.join(hooksRoot, hookName);
|
|
60764
61492
|
if (!await pathExists(hookPath)) {
|
|
60765
61493
|
return;
|
|
60766
61494
|
}
|
|
@@ -60782,7 +61510,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
60782
61510
|
if (!hooksRoot) {
|
|
60783
61511
|
return false;
|
|
60784
61512
|
}
|
|
60785
|
-
const hookPath =
|
|
61513
|
+
const hookPath = path153.join(hooksRoot, "pre-push");
|
|
60786
61514
|
if (!await pathExists(hookPath)) {
|
|
60787
61515
|
return false;
|
|
60788
61516
|
}
|
|
@@ -60797,7 +61525,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
60797
61525
|
return (await readFile79(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
60798
61526
|
}
|
|
60799
61527
|
async function readManifest5(metaprojectRoot) {
|
|
60800
|
-
const manifestPath =
|
|
61528
|
+
const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
|
|
60801
61529
|
if (!await pathExists(manifestPath)) {
|
|
60802
61530
|
return {
|
|
60803
61531
|
exists: false,
|
|
@@ -60866,7 +61594,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
60866
61594
|
}
|
|
60867
61595
|
async function anyPathExists(root, candidates) {
|
|
60868
61596
|
for (const candidate of candidates) {
|
|
60869
|
-
if (await pathExists(
|
|
61597
|
+
if (await pathExists(path153.join(root, candidate))) {
|
|
60870
61598
|
return true;
|
|
60871
61599
|
}
|
|
60872
61600
|
}
|
|
@@ -60887,13 +61615,13 @@ function parseUpdateArgs(args2) {
|
|
|
60887
61615
|
};
|
|
60888
61616
|
}
|
|
60889
61617
|
async function runPostUpdateHooks(projectRoot) {
|
|
60890
|
-
const hooksDir =
|
|
61618
|
+
const hooksDir = path153.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
60891
61619
|
if (!await pathExists(hooksDir)) {
|
|
60892
61620
|
return;
|
|
60893
61621
|
}
|
|
60894
|
-
const entries = (await
|
|
61622
|
+
const entries = (await readdir25(hooksDir)).sort();
|
|
60895
61623
|
for (const entry of entries) {
|
|
60896
|
-
const hookPath =
|
|
61624
|
+
const hookPath = path153.join(hooksDir, entry);
|
|
60897
61625
|
try {
|
|
60898
61626
|
await accessExecutable(hookPath);
|
|
60899
61627
|
} catch {
|
|
@@ -60934,14 +61662,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
60934
61662
|
if (await pathExists(filePath) && await readFile79(filePath, "utf8") === content) {
|
|
60935
61663
|
return;
|
|
60936
61664
|
}
|
|
60937
|
-
await mkdir54(
|
|
61665
|
+
await mkdir54(path153.dirname(filePath), { recursive: true });
|
|
60938
61666
|
await writeFile48(filePath, content, "utf8");
|
|
60939
61667
|
}
|
|
60940
61668
|
async function writeTextIfMissing4(filePath, content) {
|
|
60941
61669
|
if (await pathExists(filePath)) {
|
|
60942
61670
|
return;
|
|
60943
61671
|
}
|
|
60944
|
-
await mkdir54(
|
|
61672
|
+
await mkdir54(path153.dirname(filePath), { recursive: true });
|
|
60945
61673
|
await writeFile48(filePath, content, "utf8");
|
|
60946
61674
|
}
|
|
60947
61675
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -60949,7 +61677,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
60949
61677
|
if (await pathExists(to) && await readFile79(to, "utf8") === next) {
|
|
60950
61678
|
return;
|
|
60951
61679
|
}
|
|
60952
|
-
await mkdir54(
|
|
61680
|
+
await mkdir54(path153.dirname(to), { recursive: true });
|
|
60953
61681
|
await writeFile48(to, next, "utf8");
|
|
60954
61682
|
}
|
|
60955
61683
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -60958,7 +61686,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
60958
61686
|
return directPath;
|
|
60959
61687
|
}
|
|
60960
61688
|
if (relativePath.startsWith("../")) {
|
|
60961
|
-
const packagedSourcePath =
|
|
61689
|
+
const packagedSourcePath = path153.join(path153.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
60962
61690
|
if (existsSync30(packagedSourcePath)) {
|
|
60963
61691
|
return packagedSourcePath;
|
|
60964
61692
|
}
|
|
@@ -60990,7 +61718,7 @@ function printHelp18() {
|
|
|
60990
61718
|
|
|
60991
61719
|
// src/commands/dashboard.ts
|
|
60992
61720
|
import { spawn as spawn6 } from "child_process";
|
|
60993
|
-
import
|
|
61721
|
+
import path154 from "path";
|
|
60994
61722
|
init_args();
|
|
60995
61723
|
async function dashboardCommand(args2 = []) {
|
|
60996
61724
|
const options = parseOptions(args2);
|
|
@@ -61001,7 +61729,7 @@ async function dashboardCommand(args2 = []) {
|
|
|
61001
61729
|
}
|
|
61002
61730
|
if (subcommand === "build") {
|
|
61003
61731
|
const result = await buildDashboard();
|
|
61004
|
-
const rel =
|
|
61732
|
+
const rel = path154.relative(process.cwd(), result.path);
|
|
61005
61733
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
61006
61734
|
note(`Open it: keryx dashboard open`);
|
|
61007
61735
|
return;
|
|
@@ -61009,7 +61737,7 @@ async function dashboardCommand(args2 = []) {
|
|
|
61009
61737
|
if (subcommand === "open") {
|
|
61010
61738
|
const result = await buildDashboard();
|
|
61011
61739
|
await openFile(result.path);
|
|
61012
|
-
const rel =
|
|
61740
|
+
const rel = path154.relative(process.cwd(), result.path);
|
|
61013
61741
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
61014
61742
|
return;
|
|
61015
61743
|
}
|
|
@@ -61058,7 +61786,7 @@ import { readFileSync as readFileSync10 } from "fs";
|
|
|
61058
61786
|
// src/agents/bootstrap.ts
|
|
61059
61787
|
import { mkdir as mkdir55, readFile as readFile80, writeFile as writeFile49 } from "fs/promises";
|
|
61060
61788
|
import { homedir as homedir7 } from "os";
|
|
61061
|
-
import
|
|
61789
|
+
import path155 from "path";
|
|
61062
61790
|
init_fs();
|
|
61063
61791
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
61064
61792
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -61068,35 +61796,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
61068
61796
|
aliases: ["claude-code"],
|
|
61069
61797
|
label: "Claude Code",
|
|
61070
61798
|
fileName: "CLAUDE.md",
|
|
61071
|
-
filePath: (homeRoot) =>
|
|
61799
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".claude", "CLAUDE.md")
|
|
61072
61800
|
},
|
|
61073
61801
|
{
|
|
61074
61802
|
id: "opencode",
|
|
61075
61803
|
aliases: ["open-code"],
|
|
61076
61804
|
label: "OpenCode",
|
|
61077
61805
|
fileName: "AGENTS.md",
|
|
61078
|
-
filePath: (homeRoot) =>
|
|
61806
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
61079
61807
|
},
|
|
61080
61808
|
{
|
|
61081
61809
|
id: "zcode",
|
|
61082
61810
|
aliases: ["zed", "zed-code"],
|
|
61083
61811
|
label: "ZCode",
|
|
61084
61812
|
fileName: "AGENTS.md",
|
|
61085
|
-
filePath: (homeRoot) =>
|
|
61813
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".zcode", "AGENTS.md")
|
|
61086
61814
|
},
|
|
61087
61815
|
{
|
|
61088
61816
|
id: "codex",
|
|
61089
61817
|
aliases: [],
|
|
61090
61818
|
label: "Codex",
|
|
61091
61819
|
fileName: "AGENTS.md",
|
|
61092
|
-
filePath: (homeRoot) =>
|
|
61820
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".codex", "AGENTS.md")
|
|
61093
61821
|
},
|
|
61094
61822
|
{
|
|
61095
61823
|
id: "antigravity",
|
|
61096
61824
|
aliases: ["antigravuty", "antigravity-code"],
|
|
61097
61825
|
label: "Antigravity",
|
|
61098
61826
|
fileName: "AGENTS.md",
|
|
61099
|
-
filePath: (homeRoot) =>
|
|
61827
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
61100
61828
|
}
|
|
61101
61829
|
];
|
|
61102
61830
|
function agentBootstrapRuntimeIds() {
|
|
@@ -61144,7 +61872,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
61144
61872
|
const dryRun = options.dryRun === true;
|
|
61145
61873
|
const wrote = next !== current;
|
|
61146
61874
|
if (wrote && !dryRun) {
|
|
61147
|
-
await mkdir55(
|
|
61875
|
+
await mkdir55(path155.dirname(filePath), { recursive: true });
|
|
61148
61876
|
await writeFile49(filePath, next, "utf8");
|
|
61149
61877
|
}
|
|
61150
61878
|
const status = dryRun ? statusFromContent(runtime, filePath, exists2, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
@@ -61492,7 +62220,7 @@ function printBootstrapHelp() {
|
|
|
61492
62220
|
// src/commands/metrics.ts
|
|
61493
62221
|
init_args();
|
|
61494
62222
|
import { readFile as readFile81 } from "fs/promises";
|
|
61495
|
-
import
|
|
62223
|
+
import path157 from "path";
|
|
61496
62224
|
|
|
61497
62225
|
// src/metrics/benchmark.ts
|
|
61498
62226
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -62271,7 +62999,7 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
62271
62999
|
|
|
62272
63000
|
// src/metrics/oracle-runner.ts
|
|
62273
63001
|
import { mkdir as mkdir56, writeFile as writeFile50 } from "fs/promises";
|
|
62274
|
-
import
|
|
63002
|
+
import path156 from "path";
|
|
62275
63003
|
|
|
62276
63004
|
// src/metrics/ir.ts
|
|
62277
63005
|
function toIdSet(ids) {
|
|
@@ -62713,9 +63441,9 @@ function buildEvidenceBundle(input2, options = {}) {
|
|
|
62713
63441
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
62714
63442
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
62715
63443
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
62716
|
-
const dir =
|
|
63444
|
+
const dir = path156.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
62717
63445
|
await mkdir56(dir, { recursive: true });
|
|
62718
|
-
const write = (name, value) => writeFile50(
|
|
63446
|
+
const write = (name, value) => writeFile50(path156.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
62719
63447
|
`, "utf8");
|
|
62720
63448
|
await Promise.all([
|
|
62721
63449
|
write("inputs.json", bundle.inputs),
|
|
@@ -62755,7 +63483,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62755
63483
|
console.log("# metrics status");
|
|
62756
63484
|
console.log("");
|
|
62757
63485
|
console.log(`root: ${root}`);
|
|
62758
|
-
console.log(`enabled: ${await Bun.file(
|
|
63486
|
+
console.log(`enabled: ${await Bun.file(path157.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
62759
63487
|
const latest2 = await readLatestPointer(root);
|
|
62760
63488
|
console.log(`latest: ${latest2.status}`);
|
|
62761
63489
|
return;
|
|
@@ -62767,7 +63495,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62767
63495
|
process.exitCode = 1;
|
|
62768
63496
|
return;
|
|
62769
63497
|
}
|
|
62770
|
-
const record2 = JSON.parse(await readFile81(
|
|
63498
|
+
const record2 = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62771
63499
|
const result = validateRunRecord(record2);
|
|
62772
63500
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
62773
63501
|
for (const error2 of result.errors)
|
|
@@ -62792,7 +63520,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62792
63520
|
process.exitCode = 1;
|
|
62793
63521
|
return;
|
|
62794
63522
|
}
|
|
62795
|
-
const file =
|
|
63523
|
+
const file = path157.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
62796
63524
|
if (!await Bun.file(file).exists()) {
|
|
62797
63525
|
console.error(`Run not found: ${runId}`);
|
|
62798
63526
|
process.exitCode = 1;
|
|
@@ -62809,8 +63537,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62809
63537
|
process.exitCode = 1;
|
|
62810
63538
|
return;
|
|
62811
63539
|
}
|
|
62812
|
-
const a = JSON.parse(await readFile81(
|
|
62813
|
-
const b = JSON.parse(await readFile81(
|
|
63540
|
+
const a = JSON.parse(await readFile81(path157.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
63541
|
+
const b = JSON.parse(await readFile81(path157.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
62814
63542
|
const comparison = compareExecutionRuns(a, b);
|
|
62815
63543
|
console.log(stableJson(comparison));
|
|
62816
63544
|
return;
|
|
@@ -62844,8 +63572,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62844
63572
|
return;
|
|
62845
63573
|
}
|
|
62846
63574
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
62847
|
-
await Bun.write(
|
|
62848
|
-
console.log(`manifest: ${
|
|
63575
|
+
await Bun.write(path157.resolve(projectRoot, out), stableJson(template));
|
|
63576
|
+
console.log(`manifest: ${path157.relative(projectRoot, path157.resolve(projectRoot, out))}`);
|
|
62849
63577
|
return;
|
|
62850
63578
|
}
|
|
62851
63579
|
if (subcommand === "benchmark" && args2[1] === "run") {
|
|
@@ -62859,7 +63587,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62859
63587
|
process.exitCode = 1;
|
|
62860
63588
|
return;
|
|
62861
63589
|
}
|
|
62862
|
-
const raw = JSON.parse(await readFile81(
|
|
63590
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62863
63591
|
const input2 = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
62864
63592
|
const result = validatePairedBenchmark(input2);
|
|
62865
63593
|
console.log(stableJson(result));
|
|
@@ -62871,7 +63599,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62871
63599
|
process.exitCode = 1;
|
|
62872
63600
|
}
|
|
62873
63601
|
async function loadAffectedSets(projectRoot, file) {
|
|
62874
|
-
const raw = JSON.parse(await readFile81(
|
|
63602
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62875
63603
|
const map = new Map;
|
|
62876
63604
|
for (const entry of raw.targets ?? []) {
|
|
62877
63605
|
if (typeof entry.target === "string")
|
|
@@ -62942,7 +63670,7 @@ async function runHarnessLayer(projectRoot, args2, ladder) {
|
|
|
62942
63670
|
let tasks;
|
|
62943
63671
|
let model;
|
|
62944
63672
|
try {
|
|
62945
|
-
const raw = JSON.parse(await readFile81(
|
|
63673
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
|
|
62946
63674
|
tasks = raw.tasks ?? [];
|
|
62947
63675
|
model = raw.model;
|
|
62948
63676
|
} catch (error2) {
|
|
@@ -62973,7 +63701,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args2, ladder) {
|
|
|
62973
63701
|
let cases;
|
|
62974
63702
|
let model;
|
|
62975
63703
|
try {
|
|
62976
|
-
const raw = JSON.parse(await readFile81(
|
|
63704
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
|
|
62977
63705
|
cases = raw.cases ?? [];
|
|
62978
63706
|
model = raw.model;
|
|
62979
63707
|
} catch (error2) {
|
|
@@ -62998,7 +63726,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args2, ladder) {
|
|
|
62998
63726
|
let cases;
|
|
62999
63727
|
let model;
|
|
63000
63728
|
try {
|
|
63001
|
-
const raw = JSON.parse(await readFile81(
|
|
63729
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
|
|
63002
63730
|
cases = raw.cases ?? [];
|
|
63003
63731
|
model = raw.model;
|
|
63004
63732
|
} catch (error2) {
|
|
@@ -63023,7 +63751,7 @@ async function runSafetyContainmentLayer(projectRoot, args2, ladder, caseClass)
|
|
|
63023
63751
|
let cases;
|
|
63024
63752
|
let model;
|
|
63025
63753
|
try {
|
|
63026
|
-
const raw = JSON.parse(await readFile81(
|
|
63754
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
|
|
63027
63755
|
cases = raw.cases ?? [];
|
|
63028
63756
|
model = raw.model;
|
|
63029
63757
|
} catch (error2) {
|
|
@@ -63085,12 +63813,12 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
|
|
|
63085
63813
|
}
|
|
63086
63814
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
63087
63815
|
if (outDir) {
|
|
63088
|
-
const resolvedOut =
|
|
63816
|
+
const resolvedOut = path157.resolve(projectRoot, outDir);
|
|
63089
63817
|
for (const input2 of inputs) {
|
|
63090
63818
|
for (const named of input2.golds) {
|
|
63091
63819
|
const bundle = buildEvidenceBundle({ target: input2.target, system: input2.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
63092
|
-
const dir = await persistEvidenceBundle(
|
|
63093
|
-
console.error(`bundle[${named.kind}]: ${
|
|
63820
|
+
const dir = await persistEvidenceBundle(path157.join(resolvedOut, named.kind), bundle, ladder);
|
|
63821
|
+
console.error(`bundle[${named.kind}]: ${path157.relative(projectRoot, dir)}`);
|
|
63094
63822
|
}
|
|
63095
63823
|
}
|
|
63096
63824
|
}
|
|
@@ -63114,7 +63842,7 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
|
|
|
63114
63842
|
return allValid;
|
|
63115
63843
|
}
|
|
63116
63844
|
async function loadCoverageMap2(projectRoot, file) {
|
|
63117
|
-
const raw = JSON.parse(await readFile81(
|
|
63845
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
63118
63846
|
return raw.coverageMap ?? {};
|
|
63119
63847
|
}
|
|
63120
63848
|
async function runTestingLayer(projectRoot, args2, ladder) {
|
|
@@ -63151,7 +63879,7 @@ async function runTestingLayer(projectRoot, args2, ladder) {
|
|
|
63151
63879
|
return result.valid;
|
|
63152
63880
|
}
|
|
63153
63881
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
63154
|
-
const raw = JSON.parse(await readFile81(
|
|
63882
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
63155
63883
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
63156
63884
|
}
|
|
63157
63885
|
async function runMemoryLayer(projectRoot, args2, ladder) {
|
|
@@ -63188,11 +63916,11 @@ async function runMemoryLayer(projectRoot, args2, ladder) {
|
|
|
63188
63916
|
return result.valid;
|
|
63189
63917
|
}
|
|
63190
63918
|
async function loadWikiGoldK(projectRoot, file) {
|
|
63191
|
-
const raw = JSON.parse(await readFile81(
|
|
63919
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
63192
63920
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
63193
63921
|
}
|
|
63194
63922
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
63195
|
-
const raw = JSON.parse(await readFile81(
|
|
63923
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
63196
63924
|
const map = new Map;
|
|
63197
63925
|
for (const entry of raw.targets ?? []) {
|
|
63198
63926
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -63248,7 +63976,7 @@ async function runWikiLayer(projectRoot, args2, ladder) {
|
|
|
63248
63976
|
return result.valid;
|
|
63249
63977
|
}
|
|
63250
63978
|
async function loadGdctxFacts(projectRoot, file) {
|
|
63251
|
-
const raw = JSON.parse(await readFile81(
|
|
63979
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
63252
63980
|
const inputs = [];
|
|
63253
63981
|
for (const entry of raw.inputs ?? []) {
|
|
63254
63982
|
if (typeof entry.input === "string") {
|
|
@@ -63286,7 +64014,7 @@ async function collect(projectRoot, args2) {
|
|
|
63286
64014
|
process.exitCode = 1;
|
|
63287
64015
|
return;
|
|
63288
64016
|
}
|
|
63289
|
-
const raw = JSON.parse(await readFile81(
|
|
64017
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, eventFile), "utf8"));
|
|
63290
64018
|
const events2 = Array.isArray(raw) ? raw : raw.events;
|
|
63291
64019
|
const startedAt = optionValue(args2, "--started-at") ?? events2[0]?.timestamp_utc ?? new Date().toISOString();
|
|
63292
64020
|
const finishedAt = optionValue(args2, "--finished-at") ?? events2.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -63302,11 +64030,11 @@ async function collect(projectRoot, args2) {
|
|
|
63302
64030
|
parentRunId: optionValue(args2, "--parent-run-id") ?? null
|
|
63303
64031
|
});
|
|
63304
64032
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record2, { cwd: projectRoot });
|
|
63305
|
-
console.log(`json: ${
|
|
63306
|
-
console.log(`markdown: ${
|
|
64033
|
+
console.log(`json: ${path157.relative(projectRoot, result.jsonPath)}`);
|
|
64034
|
+
console.log(`markdown: ${path157.relative(projectRoot, result.markdownPath)}`);
|
|
63307
64035
|
}
|
|
63308
64036
|
function metricsRoot(projectRoot) {
|
|
63309
|
-
return
|
|
64037
|
+
return path157.join(projectRoot, ".metaproject", "data", "metrics");
|
|
63310
64038
|
}
|
|
63311
64039
|
function printMetricsHelp() {
|
|
63312
64040
|
console.log(`keryx metrics
|
|
@@ -63412,193 +64140,6 @@ init_session_wrap_up();
|
|
|
63412
64140
|
init_proposal_evidence();
|
|
63413
64141
|
init_review_confirm_token();
|
|
63414
64142
|
init_store3();
|
|
63415
|
-
|
|
63416
|
-
// src/sac/catch-up.ts
|
|
63417
|
-
init_fs();
|
|
63418
|
-
init_config_dir();
|
|
63419
|
-
init_paths();
|
|
63420
|
-
init_slate();
|
|
63421
|
-
init_store3();
|
|
63422
|
-
init_proposal_lifecycle();
|
|
63423
|
-
init_workspace_service();
|
|
63424
|
-
import { randomUUID as randomUUID28 } from "crypto";
|
|
63425
|
-
import { readdir as readdir25 } from "fs/promises";
|
|
63426
|
-
import path157 from "path";
|
|
63427
|
-
|
|
63428
|
-
// src/sac/lifecycle-flag.ts
|
|
63429
|
-
init_store();
|
|
63430
|
-
init_service3();
|
|
63431
|
-
init_decision_dedup();
|
|
63432
|
-
init_workspace_service();
|
|
63433
|
-
import { randomUUID as randomUUID27 } from "crypto";
|
|
63434
|
-
function normalize3(raw) {
|
|
63435
|
-
return raw.replace(/^\.\//, "");
|
|
63436
|
-
}
|
|
63437
|
-
function isStillPresent(recorded, valid) {
|
|
63438
|
-
const normalized = normalize3(recorded);
|
|
63439
|
-
return valid.has(normalized) || valid.has(moduleNameFromProjectPath(normalized));
|
|
63440
|
-
}
|
|
63441
|
-
async function computeLifecycleFlags(cwd, now = () => new Date) {
|
|
63442
|
-
const valid = await validModuleNames(cwd);
|
|
63443
|
-
if (valid === undefined)
|
|
63444
|
-
return [];
|
|
63445
|
-
const flaggedAt = now().toISOString();
|
|
63446
|
-
const flags = [];
|
|
63447
|
-
const workspaceService = new WorkspaceService({
|
|
63448
|
-
workspaceRoot: cwd,
|
|
63449
|
-
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
63450
|
-
strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }
|
|
63451
|
-
});
|
|
63452
|
-
const workspaces = await workspaceService.list({ request: undefined, requestCorrelationId: randomUUID27(), includeArchived: true });
|
|
63453
|
-
for (const workspace of workspaces) {
|
|
63454
|
-
const component = workspace.resources.find((r) => r.kind === "component")?.uri;
|
|
63455
|
-
if (component !== undefined && !isStillPresent(component, valid)) {
|
|
63456
|
-
flags.push({ kind: "workspace", ref: workspace.id, missingComponent: normalize3(component), flaggedAt });
|
|
63457
|
-
}
|
|
63458
|
-
}
|
|
63459
|
-
for (const entry of await collectEntries(cwd)) {
|
|
63460
|
-
const module = entry.scopes.module;
|
|
63461
|
-
if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
|
|
63462
|
-
flags.push({ kind: "memory-entry", ref: entry.relativePath, missingComponent: normalize3(module), flaggedAt });
|
|
63463
|
-
}
|
|
63464
|
-
}
|
|
63465
|
-
for (const decision of await collectWikiDecisionEntries(cwd)) {
|
|
63466
|
-
const module = decision.scopes.module;
|
|
63467
|
-
if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
|
|
63468
|
-
flags.push({ kind: "wiki-decision", ref: decision.relativePath, missingComponent: normalize3(module), flaggedAt });
|
|
63469
|
-
}
|
|
63470
|
-
}
|
|
63471
|
-
return flags;
|
|
63472
|
-
}
|
|
63473
|
-
|
|
63474
|
-
// src/sac/catch-up.ts
|
|
63475
|
-
async function buildCatchUp(input2) {
|
|
63476
|
-
const [proposals, sessionCategories, lifecycleFlagsAll] = await Promise.all([
|
|
63477
|
-
collectProposals(input2.cwd, input2.workspaceId),
|
|
63478
|
-
collectSessionCategories(input2.cwd),
|
|
63479
|
-
computeLifecycleFlags(input2.cwd)
|
|
63480
|
-
]);
|
|
63481
|
-
const lifecycleFlags = input2.workspaceId === undefined ? lifecycleFlagsAll : lifecycleFlagsAll.filter((flag) => flag.kind !== "workspace" || flag.ref === input2.workspaceId);
|
|
63482
|
-
return { proposals, ...sessionCategories, lifecycleFlags };
|
|
63483
|
-
}
|
|
63484
|
-
async function collectProposals(cwd, workspaceId) {
|
|
63485
|
-
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
63486
|
-
const actor = await authorizationServer.actorContextFor(undefined, randomUUID28());
|
|
63487
|
-
if (!actor)
|
|
63488
|
-
throw new Error("trusted ActorContext is required for catch-up");
|
|
63489
|
-
const proposalService = createLocalProposalLifecycleService(cwd);
|
|
63490
|
-
const groups = await proposalService.listVisibleProposedProposals(actor);
|
|
63491
|
-
const scoped = workspaceId === undefined ? groups : groups.filter((group) => group.workspace.id === workspaceId);
|
|
63492
|
-
const flattened = scoped.flatMap((group) => group.proposals.map((proposal) => ({ group, proposal })));
|
|
63493
|
-
return Promise.all(flattened.map(async ({ group, proposal }) => {
|
|
63494
|
-
const fresh = await proposalService.isEvidenceFresh(proposal, actor);
|
|
63495
|
-
return { type: "proposal", workspaceId: group.workspace.id, proposalId: proposal.id, fresh };
|
|
63496
|
-
}));
|
|
63497
|
-
}
|
|
63498
|
-
async function classifySession(session) {
|
|
63499
|
-
const dir = sessionDir(session.projectPath, session.id);
|
|
63500
|
-
if (await isLockHeld(slateLockPath(dir)))
|
|
63501
|
-
return;
|
|
63502
|
-
const terminalState = await readTerminalState(dir);
|
|
63503
|
-
if (terminalState !== undefined) {
|
|
63504
|
-
const workspaceId2 = (await safeReadSlate(dir))?.workspaceId;
|
|
63505
|
-
return { kind: "blocked", item: { type: "blocked", sessionId: session.id, ...workspaceId2 !== undefined ? { workspaceId: workspaceId2 } : {}, terminalState } };
|
|
63506
|
-
}
|
|
63507
|
-
const unboundCandidate = await readNewestUnboundCandidate(dir);
|
|
63508
|
-
if (unboundCandidate !== undefined) {
|
|
63509
|
-
return {
|
|
63510
|
-
kind: "unbound-candidate",
|
|
63511
|
-
item: { type: "unbound-candidate", sessionId: session.id, evidencePath: unboundCandidate.evidencePath, summary: unboundCandidate.summary }
|
|
63512
|
-
};
|
|
63513
|
-
}
|
|
63514
|
-
if (!await isSlateEngaged(dir))
|
|
63515
|
-
return;
|
|
63516
|
-
const workspaceId = (await safeReadSlate(dir))?.workspaceId;
|
|
63517
|
-
return { kind: "unknown", item: { type: "unknown", sessionId: session.id, ...workspaceId !== undefined ? { workspaceId } : {}, lastSeenAt: session.updatedAt } };
|
|
63518
|
-
}
|
|
63519
|
-
async function collectSessionCategories(cwd) {
|
|
63520
|
-
const classified = await Promise.all(listSessions(cwd).map((session) => classifySession(session)));
|
|
63521
|
-
const blocked2 = [];
|
|
63522
|
-
const unboundCandidates = [];
|
|
63523
|
-
const unknown = [];
|
|
63524
|
-
for (const category of classified) {
|
|
63525
|
-
if (category === undefined)
|
|
63526
|
-
continue;
|
|
63527
|
-
if (category.kind === "blocked")
|
|
63528
|
-
blocked2.push(category.item);
|
|
63529
|
-
else if (category.kind === "unbound-candidate")
|
|
63530
|
-
unboundCandidates.push(category.item);
|
|
63531
|
-
else
|
|
63532
|
-
unknown.push(category.item);
|
|
63533
|
-
}
|
|
63534
|
-
return { blocked: blocked2, unboundCandidates, unknown };
|
|
63535
|
-
}
|
|
63536
|
-
async function isSlateEngaged(dir) {
|
|
63537
|
-
if (await pathExists(path157.join(dir, "slate.json")))
|
|
63538
|
-
return true;
|
|
63539
|
-
if (await pathExists(path157.join(dir, "terminal-state.json")))
|
|
63540
|
-
return true;
|
|
63541
|
-
try {
|
|
63542
|
-
const entries = await readdir25(path157.join(dir, "slate-archive"));
|
|
63543
|
-
return entries.length > 0;
|
|
63544
|
-
} catch {
|
|
63545
|
-
return false;
|
|
63546
|
-
}
|
|
63547
|
-
}
|
|
63548
|
-
async function safeReadSlate(dir) {
|
|
63549
|
-
try {
|
|
63550
|
-
return await readSlate(dir);
|
|
63551
|
-
} catch {
|
|
63552
|
-
return;
|
|
63553
|
-
}
|
|
63554
|
-
}
|
|
63555
|
-
async function readTerminalState(dir) {
|
|
63556
|
-
const result = readConfigFile(path157.join(dir, "terminal-state.json"));
|
|
63557
|
-
if (!result.ok) {
|
|
63558
|
-
return;
|
|
63559
|
-
}
|
|
63560
|
-
try {
|
|
63561
|
-
return JSON.parse(result.text);
|
|
63562
|
-
} catch {
|
|
63563
|
-
return;
|
|
63564
|
-
}
|
|
63565
|
-
}
|
|
63566
|
-
async function readNewestUnboundCandidate(dir) {
|
|
63567
|
-
const archiveDir = path157.join(dir, "slate-archive");
|
|
63568
|
-
let entries;
|
|
63569
|
-
try {
|
|
63570
|
-
entries = (await readdir25(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
63571
|
-
} catch {
|
|
63572
|
-
return;
|
|
63573
|
-
}
|
|
63574
|
-
entries.sort();
|
|
63575
|
-
for (let i = entries.length - 1;i >= 0; i--) {
|
|
63576
|
-
const evidencePath = path157.join(archiveDir, entries[i]);
|
|
63577
|
-
const result = readConfigFile(evidencePath);
|
|
63578
|
-
if (!result.ok) {
|
|
63579
|
-
continue;
|
|
63580
|
-
}
|
|
63581
|
-
try {
|
|
63582
|
-
const parsed = JSON.parse(result.text);
|
|
63583
|
-
if (parsed.recordType !== "unbound-candidate")
|
|
63584
|
-
continue;
|
|
63585
|
-
return { evidencePath, summary: summarizeUnboundCandidate(parsed.groups) };
|
|
63586
|
-
} catch {
|
|
63587
|
-
continue;
|
|
63588
|
-
}
|
|
63589
|
-
}
|
|
63590
|
-
return;
|
|
63591
|
-
}
|
|
63592
|
-
function summarizeUnboundCandidate(groups) {
|
|
63593
|
-
const safeGroups = groups ?? [];
|
|
63594
|
-
if (safeGroups.length === 0)
|
|
63595
|
-
return "no seeds captured";
|
|
63596
|
-
const seedCount = safeGroups.reduce((sum, group) => sum + (group.seeds?.length ?? 0), 0);
|
|
63597
|
-
const kinds = safeGroups.map((group) => typeof group.kind === "string" ? group.kind : "unknown").join(", ");
|
|
63598
|
-
return `${seedCount} untriaged seed(s) across ${safeGroups.length} kind(s) (${kinds})`;
|
|
63599
|
-
}
|
|
63600
|
-
|
|
63601
|
-
// src/commands/workspace.ts
|
|
63602
64143
|
var PROPOSAL_KINDS2 = ["decision", "wiki-update", "memory-entry", "follow-up", "contract-change", "risk"];
|
|
63603
64144
|
function service5() {
|
|
63604
64145
|
return new WorkspaceService({
|