@mrciphersmith/keryx 0.3.64 → 0.3.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/dist/cli.js +1237 -410
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -20107,7 +20107,7 @@ var package_default;
|
|
|
20107
20107
|
var init_package = __esm(() => {
|
|
20108
20108
|
package_default = {
|
|
20109
20109
|
name: "@mrciphersmith/keryx",
|
|
20110
|
-
version: "0.3.
|
|
20110
|
+
version: "0.3.67",
|
|
20111
20111
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
20112
20112
|
private: false,
|
|
20113
20113
|
publishConfig: {
|
|
@@ -88435,6 +88435,58 @@ var MAX_TEXT_LENGTH = 300;
|
|
|
88435
88435
|
function emptyTally() {
|
|
88436
88436
|
return { answered: 0, matched: 0 };
|
|
88437
88437
|
}
|
|
88438
|
+
function median(values) {
|
|
88439
|
+
if (values.length === 0)
|
|
88440
|
+
return null;
|
|
88441
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
88442
|
+
const mid = Math.floor(sorted.length / 2);
|
|
88443
|
+
const low = sorted[mid - 1] ?? 0;
|
|
88444
|
+
const high = sorted[mid] ?? 0;
|
|
88445
|
+
return sorted.length % 2 === 1 ? high : Math.round((low + high) / 2);
|
|
88446
|
+
}
|
|
88447
|
+
function buildBackfilled(history, firstAnswer, reasons) {
|
|
88448
|
+
const out = {
|
|
88449
|
+
total: history.length,
|
|
88450
|
+
answered: 0,
|
|
88451
|
+
withRecommendation: 0,
|
|
88452
|
+
withoutRecommendation: 0,
|
|
88453
|
+
matchTally: emptyTally(),
|
|
88454
|
+
matchShare: null,
|
|
88455
|
+
deviations: []
|
|
88456
|
+
};
|
|
88457
|
+
for (const open of history) {
|
|
88458
|
+
if (open.recommendation === null)
|
|
88459
|
+
out.withoutRecommendation += 1;
|
|
88460
|
+
else
|
|
88461
|
+
out.withRecommendation += 1;
|
|
88462
|
+
const first = firstAnswer(open.id);
|
|
88463
|
+
if (first === undefined)
|
|
88464
|
+
continue;
|
|
88465
|
+
out.answered += 1;
|
|
88466
|
+
if (open.recommendation === null)
|
|
88467
|
+
continue;
|
|
88468
|
+
out.matchTally.answered += 1;
|
|
88469
|
+
if (first.choice === open.recommendation.optionId) {
|
|
88470
|
+
out.matchTally.matched += 1;
|
|
88471
|
+
continue;
|
|
88472
|
+
}
|
|
88473
|
+
const reason = reasons.get(open.id)?.reason;
|
|
88474
|
+
out.deviations.push({
|
|
88475
|
+
id: open.id,
|
|
88476
|
+
flow: open.flow,
|
|
88477
|
+
stage: open.stage,
|
|
88478
|
+
mode: open.mode,
|
|
88479
|
+
question: open.question,
|
|
88480
|
+
recommended: open.recommendation.optionId,
|
|
88481
|
+
chose: first.choice,
|
|
88482
|
+
changed: false,
|
|
88483
|
+
...open.source !== undefined ? { source: open.source } : {},
|
|
88484
|
+
...reason !== undefined ? { reason } : {}
|
|
88485
|
+
});
|
|
88486
|
+
}
|
|
88487
|
+
out.matchShare = out.matchTally.answered === 0 ? null : out.matchTally.matched / out.matchTally.answered;
|
|
88488
|
+
return out;
|
|
88489
|
+
}
|
|
88438
88490
|
function buildReport2(records, skipped = 0) {
|
|
88439
88491
|
const opens = [];
|
|
88440
88492
|
const answers = new Map;
|
|
@@ -88454,8 +88506,11 @@ function buildReport2(records, skipped = 0) {
|
|
|
88454
88506
|
reasons.set(record.id, record);
|
|
88455
88507
|
}
|
|
88456
88508
|
}
|
|
88509
|
+
const live = opens.filter((open) => open.backfilled !== true);
|
|
88510
|
+
const history = opens.filter((open) => open.backfilled === true);
|
|
88511
|
+
const firstAnswer = (id) => [...answers.get(id) ?? []].sort((a, b) => a.seq - b.seq)[0];
|
|
88457
88512
|
const report = {
|
|
88458
|
-
total:
|
|
88513
|
+
total: live.length,
|
|
88459
88514
|
answered: 0,
|
|
88460
88515
|
unanswered: 0,
|
|
88461
88516
|
withoutRecommendation: 0,
|
|
@@ -88463,9 +88518,11 @@ function buildReport2(records, skipped = 0) {
|
|
|
88463
88518
|
byMode: { ordinary: emptyTally(), blind: emptyTally() },
|
|
88464
88519
|
byStage: [],
|
|
88465
88520
|
deviations: [],
|
|
88466
|
-
irreversible:
|
|
88467
|
-
blindRefused:
|
|
88468
|
-
inferredFlow:
|
|
88521
|
+
irreversible: live.filter((open) => open.irreversible).length,
|
|
88522
|
+
blindRefused: live.filter((open) => open.blindRefused === true).length,
|
|
88523
|
+
inferredFlow: live.filter((open) => open.flow !== null && open.flowSource === "inferred").length,
|
|
88524
|
+
timing: { answered: 0, medianMs: null },
|
|
88525
|
+
backfilled: buildBackfilled(history, firstAnswer, reasons),
|
|
88469
88526
|
skipped
|
|
88470
88527
|
};
|
|
88471
88528
|
const stages = new Map;
|
|
@@ -88474,7 +88531,8 @@ function buildReport2(records, skipped = 0) {
|
|
|
88474
88531
|
stages.set(name, found);
|
|
88475
88532
|
return found;
|
|
88476
88533
|
};
|
|
88477
|
-
|
|
88534
|
+
const times = [];
|
|
88535
|
+
for (const open of live) {
|
|
88478
88536
|
if (open.blindRefused === true)
|
|
88479
88537
|
stageOf(open.stage).blindRefused += 1;
|
|
88480
88538
|
const list = (answers.get(open.id) ?? []).sort((a, b) => a.seq - b.seq);
|
|
@@ -88484,6 +88542,7 @@ function buildReport2(records, skipped = 0) {
|
|
|
88484
88542
|
continue;
|
|
88485
88543
|
}
|
|
88486
88544
|
report.answered += 1;
|
|
88545
|
+
times.push(first.timeToAnswerMs);
|
|
88487
88546
|
if (list.length > 1)
|
|
88488
88547
|
report.changed += 1;
|
|
88489
88548
|
if (open.recommendation === null) {
|
|
@@ -88513,6 +88572,7 @@ function buildReport2(records, skipped = 0) {
|
|
|
88513
88572
|
});
|
|
88514
88573
|
}
|
|
88515
88574
|
}
|
|
88575
|
+
report.timing = { answered: times.length, medianMs: median(times) };
|
|
88516
88576
|
report.byStage = [...stages.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([stage, tallies]) => ({ stage, ...tallies }));
|
|
88517
88577
|
return report;
|
|
88518
88578
|
}
|
|
@@ -88521,14 +88581,45 @@ function share(tally) {
|
|
|
88521
88581
|
return "n/a";
|
|
88522
88582
|
return `${Math.round(tally.matched / tally.answered * 100)}% (${tally.matched}/${tally.answered})`;
|
|
88523
88583
|
}
|
|
88584
|
+
function backfilledShare(report) {
|
|
88585
|
+
if (report.matchTally.answered === 0)
|
|
88586
|
+
return "n/a";
|
|
88587
|
+
return `${Math.round(report.matchTally.matched / report.matchTally.answered * 100)}% (${report.matchTally.matched}/${report.matchTally.answered})`;
|
|
88588
|
+
}
|
|
88589
|
+
function renderBackfilled(report) {
|
|
88590
|
+
const lines = [
|
|
88591
|
+
"\u0414\u043E (\u0438\u0441\u0442\u043E\u0440\u0438\u0447\u0435\u0441\u043A\u043E\u0435, \u0434\u043E\u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043E \u0437\u0430\u0434\u043D\u0438\u043C \u0447\u0438\u0441\u043B\u043E\u043C):",
|
|
88592
|
+
` \u0432\u0441\u0435\u0433\u043E: ${report.total}, \u0441 \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u0435\u0439: ${report.withRecommendation}, \u0441\u043E\u0432\u043F\u0430\u0434\u0435\u043D\u0438\u0435: ${backfilledShare(report)}`,
|
|
88593
|
+
` \u043E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0438\u0439: ${report.deviations.length}`
|
|
88594
|
+
];
|
|
88595
|
+
for (const dev of report.deviations) {
|
|
88596
|
+
const where = [dev.source === undefined ? undefined : oneLine2(dev.source), dev.flow === null ? "project" : `flow ${oneLine2(dev.flow)}`, oneLine2(dev.stage)].filter((part) => part !== undefined);
|
|
88597
|
+
lines.push(` ${oneLine2(dev.id)} [${where.join(", ")}]`);
|
|
88598
|
+
lines.push(` ${oneLine2(dev.question)}`);
|
|
88599
|
+
lines.push(` \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u043E\u0432\u0430\u043D\u043E ${oneLine2(dev.recommended)}, \u0432\u044B\u0431\u0440\u0430\u043D\u043E ${oneLine2(dev.chose)}`);
|
|
88600
|
+
lines.push(` \u043F\u0440\u0438\u0447\u0438\u043D\u0430: ${dev.reason === undefined ? "(\u043D\u0435 \u0443\u043A\u0430\u0437\u0430\u043D\u0430)" : oneLine2(dev.reason)}`);
|
|
88601
|
+
}
|
|
88602
|
+
return lines;
|
|
88603
|
+
}
|
|
88604
|
+
function seconds(ms) {
|
|
88605
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
88606
|
+
}
|
|
88524
88607
|
function renderReport(report) {
|
|
88525
88608
|
const skippedLine = report.skipped > 0 ? `Skipped ${report.skipped} unreadable journal record${report.skipped === 1 ? "" : "s"}.` : undefined;
|
|
88526
|
-
if (report.total === 0)
|
|
88609
|
+
if (report.total === 0 && report.backfilled.total === 0)
|
|
88527
88610
|
return skippedLine === undefined ? "No decisions recorded yet." : `No decisions recorded yet.
|
|
88528
88611
|
${skippedLine}`;
|
|
88612
|
+
if (report.total === 0) {
|
|
88613
|
+
const lines = ["No live decisions recorded yet.", "", ...renderBackfilled(report.backfilled)];
|
|
88614
|
+
if (skippedLine !== undefined)
|
|
88615
|
+
lines.push("", skippedLine);
|
|
88616
|
+
return lines.join(`
|
|
88617
|
+
`);
|
|
88618
|
+
}
|
|
88529
88619
|
const lines = [
|
|
88530
88620
|
`Decisions: ${report.total} recorded, ${report.answered} answered, ${report.unanswered} not answered yet`,
|
|
88531
88621
|
`Changed after the reveal: ${report.changed}. Without a recommendation: ${report.withoutRecommendation}.`,
|
|
88622
|
+
...report.timing.medianMs === null ? [] : [`Median time to answer: ${seconds(report.timing.medianMs)} (${report.timing.answered} answered).`],
|
|
88532
88623
|
"",
|
|
88533
88624
|
"Match with the recommendation, by mode (first answer):",
|
|
88534
88625
|
` ordinary ${share(report.byMode.ordinary)}`,
|
|
@@ -88555,15 +88646,27 @@ ${skippedLine}`;
|
|
|
88555
88646
|
if (report.inferredFlow > 0) {
|
|
88556
88647
|
lines.push("", `Flow attribution inferred (the one flow in progress, not named by KERYX_FLOW or the branch): ${report.inferredFlow} decision${report.inferredFlow === 1 ? "" : "s"}.`);
|
|
88557
88648
|
}
|
|
88649
|
+
if (report.backfilled.total > 0)
|
|
88650
|
+
lines.push("", ...renderBackfilled(report.backfilled));
|
|
88558
88651
|
if (skippedLine !== undefined)
|
|
88559
88652
|
lines.push("", skippedLine);
|
|
88560
88653
|
return lines.join(`
|
|
88561
88654
|
`);
|
|
88562
88655
|
}
|
|
88656
|
+
function percent(tally) {
|
|
88657
|
+
if (tally.answered === 0)
|
|
88658
|
+
return "\u043D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445";
|
|
88659
|
+
return `${Math.round(tally.matched / tally.answered * 100)}% (${tally.matched}/${tally.answered})`;
|
|
88660
|
+
}
|
|
88661
|
+
function renderReportLine(report) {
|
|
88662
|
+
const all = report.total + report.backfilled.total;
|
|
88663
|
+
return `\u0416\u0443\u0440\u043D\u0430\u043B \u0440\u0435\u0448\u0435\u043D\u0438\u0439: \u0432\u0441\u0435\u0433\u043E ${all} (\u0434\u043E: ${report.backfilled.total}, \u043F\u043E\u0441\u043B\u0435: ${report.total}). ` + `\u0421\u043E\u0432\u043F\u0430\u0434\u0435\u043D\u0438\u0435 \u0441 \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u0435\u0439: \u0432\u0438\u0434\u0438\u043C\u0430\u044F ${percent(report.byMode.ordinary)}, \u0441\u043A\u0440\u044B\u0442\u0430\u044F ${percent(report.byMode.blind)}; ` + `\u0434\u043E: ${percent(report.backfilled.matchTally)}.`;
|
|
88664
|
+
}
|
|
88563
88665
|
var init_report4 = () => {};
|
|
88564
88666
|
|
|
88565
88667
|
// src/decisions/store.ts
|
|
88566
|
-
import {
|
|
88668
|
+
import { randomUUID as randomUUID23 } from "crypto";
|
|
88669
|
+
import { appendFile as appendFile11, mkdir as mkdir52, open as open9, readFile as readFile102, rename as rename10, rm as rm13, stat as stat24 } from "fs/promises";
|
|
88567
88670
|
import path211 from "path";
|
|
88568
88671
|
async function journalRoot(cwd) {
|
|
88569
88672
|
const cached = rootCache.get(cwd);
|
|
@@ -88594,6 +88697,102 @@ async function appendRecord(cwd, record) {
|
|
|
88594
88697
|
await appendFile11(journalFile(root), `${JSON.stringify(record)}
|
|
88595
88698
|
`, { encoding: "utf8", mode: 384 });
|
|
88596
88699
|
}
|
|
88700
|
+
async function endsWithoutNewline2(file) {
|
|
88701
|
+
try {
|
|
88702
|
+
const text = await readFile102(file, "utf8");
|
|
88703
|
+
return text.length > 0 && !text.endsWith(`
|
|
88704
|
+
`);
|
|
88705
|
+
} catch {
|
|
88706
|
+
return false;
|
|
88707
|
+
}
|
|
88708
|
+
}
|
|
88709
|
+
async function appendRecords(cwd, records) {
|
|
88710
|
+
if (records.length === 0)
|
|
88711
|
+
return;
|
|
88712
|
+
const root = await journalRoot(cwd);
|
|
88713
|
+
await mkdir52(decisionsDir(root), { recursive: true });
|
|
88714
|
+
const file = journalFile(root);
|
|
88715
|
+
const lead = await endsWithoutNewline2(file) ? `
|
|
88716
|
+
` : "";
|
|
88717
|
+
await appendFile11(file, lead + records.map((record) => `${JSON.stringify(record)}
|
|
88718
|
+
`).join(""), { encoding: "utf8", mode: 384 });
|
|
88719
|
+
}
|
|
88720
|
+
function journalLockFile(root) {
|
|
88721
|
+
return `${journalFile(root)}.lock`;
|
|
88722
|
+
}
|
|
88723
|
+
function pidAlive(pid) {
|
|
88724
|
+
try {
|
|
88725
|
+
process.kill(pid, 0);
|
|
88726
|
+
return true;
|
|
88727
|
+
} catch (error) {
|
|
88728
|
+
return error.code === "EPERM";
|
|
88729
|
+
}
|
|
88730
|
+
}
|
|
88731
|
+
async function lockIsStale(file, staleMs) {
|
|
88732
|
+
let mtimeMs;
|
|
88733
|
+
try {
|
|
88734
|
+
mtimeMs = (await stat24(file)).mtimeMs;
|
|
88735
|
+
} catch {
|
|
88736
|
+
return false;
|
|
88737
|
+
}
|
|
88738
|
+
if (Date.now() - mtimeMs > staleMs)
|
|
88739
|
+
return true;
|
|
88740
|
+
try {
|
|
88741
|
+
const pid = JSON.parse(await readFile102(file, "utf8")).pid;
|
|
88742
|
+
return typeof pid === "number" && !pidAlive(pid);
|
|
88743
|
+
} catch {
|
|
88744
|
+
return false;
|
|
88745
|
+
}
|
|
88746
|
+
}
|
|
88747
|
+
async function ownsLock2(file, token) {
|
|
88748
|
+
try {
|
|
88749
|
+
return JSON.parse(await readFile102(file, "utf8")).token === token;
|
|
88750
|
+
} catch {
|
|
88751
|
+
return false;
|
|
88752
|
+
}
|
|
88753
|
+
}
|
|
88754
|
+
async function withJournalLock(cwd, fn, options = {}) {
|
|
88755
|
+
const staleMs = options.staleMs ?? JOURNAL_LOCK_STALE_MS;
|
|
88756
|
+
const timeoutMs = options.timeoutMs ?? JOURNAL_LOCK_TIMEOUT_MS;
|
|
88757
|
+
const pollMs = options.pollMs ?? 25;
|
|
88758
|
+
const root = await journalRoot(cwd);
|
|
88759
|
+
await mkdir52(decisionsDir(root), { recursive: true });
|
|
88760
|
+
const file = journalLockFile(root);
|
|
88761
|
+
const token = randomUUID23();
|
|
88762
|
+
const deadline = Date.now() + timeoutMs;
|
|
88763
|
+
for (;; ) {
|
|
88764
|
+
try {
|
|
88765
|
+
const handle = await open9(file, "wx", 384);
|
|
88766
|
+
try {
|
|
88767
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, at: new Date().toISOString(), token }));
|
|
88768
|
+
} finally {
|
|
88769
|
+
await handle.close();
|
|
88770
|
+
}
|
|
88771
|
+
break;
|
|
88772
|
+
} catch (error) {
|
|
88773
|
+
if (error.code !== "EEXIST")
|
|
88774
|
+
throw error;
|
|
88775
|
+
}
|
|
88776
|
+
if (await lockIsStale(file, staleMs)) {
|
|
88777
|
+
const aside = `${file}.stale-${process.pid}-${Date.now()}`;
|
|
88778
|
+
try {
|
|
88779
|
+
await rename10(file, aside);
|
|
88780
|
+
await rm13(aside, { force: true });
|
|
88781
|
+
} catch {}
|
|
88782
|
+
continue;
|
|
88783
|
+
}
|
|
88784
|
+
if (Date.now() >= deadline) {
|
|
88785
|
+
throw new Error(`the decisions journal is locked by another run (${file}); try again, or delete that file if no import is running`);
|
|
88786
|
+
}
|
|
88787
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
88788
|
+
}
|
|
88789
|
+
try {
|
|
88790
|
+
return await fn();
|
|
88791
|
+
} finally {
|
|
88792
|
+
if (await ownsLock2(file, token))
|
|
88793
|
+
await rm13(file, { force: true });
|
|
88794
|
+
}
|
|
88795
|
+
}
|
|
88597
88796
|
function isOption(value) {
|
|
88598
88797
|
if (value === null || typeof value !== "object")
|
|
88599
88798
|
return false;
|
|
@@ -88669,7 +88868,7 @@ async function readJournal(cwd) {
|
|
|
88669
88868
|
async function readRecords(cwd) {
|
|
88670
88869
|
return (await readJournal(cwd)).records;
|
|
88671
88870
|
}
|
|
88672
|
-
var rootCache, isString2 = (value) => typeof value === "string";
|
|
88871
|
+
var rootCache, JOURNAL_LOCK_STALE_MS = 60000, JOURNAL_LOCK_TIMEOUT_MS = 15000, isString2 = (value) => typeof value === "string";
|
|
88673
88872
|
var init_store6 = __esm(() => {
|
|
88674
88873
|
init_git_worktrees();
|
|
88675
88874
|
rootCache = new Map;
|
|
@@ -88935,6 +89134,8 @@ async function answerDecision(input) {
|
|
|
88935
89134
|
const open = records.find((r) => r.kind === "open" && r.id === input.id);
|
|
88936
89135
|
if (open === undefined)
|
|
88937
89136
|
throw new Error(`no open decision with id ${input.id}`);
|
|
89137
|
+
if (open.backfilled === true)
|
|
89138
|
+
throw new Error(`decision ${open.id} is a backfilled historical record; its answer is not changed`);
|
|
88938
89139
|
const other = input.other === true;
|
|
88939
89140
|
const choice = other ? oneLine2(input.choice) : input.choice.trim();
|
|
88940
89141
|
if (choice.length === 0)
|
|
@@ -88979,8 +89180,11 @@ async function answerDecision(input) {
|
|
|
88979
89180
|
}
|
|
88980
89181
|
async function recordReason(cwd, id, text, now = () => new Date, options = {}) {
|
|
88981
89182
|
const records = await readRecords(cwd);
|
|
88982
|
-
|
|
89183
|
+
const open = records.find((r) => r.kind === "open" && r.id === id);
|
|
89184
|
+
if (open === undefined)
|
|
88983
89185
|
throw new Error(`no open decision with id ${id}`);
|
|
89186
|
+
if (open.backfilled === true)
|
|
89187
|
+
throw new Error(`decision ${id} is a backfilled historical record; its reason is not changed`);
|
|
88984
89188
|
if (options.replace !== true && records.some((r) => r.kind === "reason" && r.id === id))
|
|
88985
89189
|
return false;
|
|
88986
89190
|
const reason = text === undefined ? "" : oneLine2(text);
|
|
@@ -89116,6 +89320,244 @@ var init_ask2 = __esm(() => {
|
|
|
89116
89320
|
init_journal2();
|
|
89117
89321
|
});
|
|
89118
89322
|
|
|
89323
|
+
// src/decisions/import.ts
|
|
89324
|
+
function parseEntry3(raw) {
|
|
89325
|
+
if (!isObject(raw))
|
|
89326
|
+
return "a decision must be a JSON object";
|
|
89327
|
+
const id = raw["id"];
|
|
89328
|
+
if (!isString3(id) || id.trim().length === 0)
|
|
89329
|
+
return "needs a non-empty string id";
|
|
89330
|
+
const at = raw["at"];
|
|
89331
|
+
if (!isString3(at) || !Number.isFinite(Date.parse(at)))
|
|
89332
|
+
return `${id}: "at" must be an ISO date`;
|
|
89333
|
+
const flow = raw["flow"] === undefined ? null : raw["flow"];
|
|
89334
|
+
if (flow !== null && !isString3(flow))
|
|
89335
|
+
return `${id}: "flow" must be a string or null`;
|
|
89336
|
+
const stageRaw = raw["stage"];
|
|
89337
|
+
if (stageRaw !== undefined && !isString3(stageRaw))
|
|
89338
|
+
return `${id}: "stage" must be a string`;
|
|
89339
|
+
const question = raw["question"];
|
|
89340
|
+
if (!isString3(question) || oneLine2(question).length === 0)
|
|
89341
|
+
return `${id}: needs a question`;
|
|
89342
|
+
const optionsRaw = raw["options"];
|
|
89343
|
+
if (!Array.isArray(optionsRaw) || optionsRaw.length < 2)
|
|
89344
|
+
return `${id}: needs at least two options`;
|
|
89345
|
+
const options = [];
|
|
89346
|
+
const ids = new Set;
|
|
89347
|
+
for (const item of optionsRaw) {
|
|
89348
|
+
if (!isObject(item) || !isString3(item["id"]) || item["id"].trim().length === 0)
|
|
89349
|
+
return `${id}: every option needs an id`;
|
|
89350
|
+
const optionId = item["id"];
|
|
89351
|
+
if (ids.has(optionId))
|
|
89352
|
+
return `${id}: duplicate option id: ${optionId}`;
|
|
89353
|
+
ids.add(optionId);
|
|
89354
|
+
const label = isString3(item["label"]) && item["label"].length > 0 ? item["label"] : optionId;
|
|
89355
|
+
options.push({ id: optionId, label, ...isString3(item["description"]) && item["description"].length > 0 ? { description: item["description"] } : {} });
|
|
89356
|
+
}
|
|
89357
|
+
let recommendation = null;
|
|
89358
|
+
const recRaw = raw["recommendation"];
|
|
89359
|
+
if (recRaw !== undefined && recRaw !== null) {
|
|
89360
|
+
if (!isObject(recRaw) || !isString3(recRaw["optionId"]))
|
|
89361
|
+
return `${id}: "recommendation" needs an optionId`;
|
|
89362
|
+
if (!ids.has(recRaw["optionId"]))
|
|
89363
|
+
return `${id}: the recommended option "${recRaw["optionId"]}" is not one of the options`;
|
|
89364
|
+
recommendation = { optionId: recRaw["optionId"], reason: isString3(recRaw["reason"]) ? oneLine2(recRaw["reason"]) : "" };
|
|
89365
|
+
}
|
|
89366
|
+
let answer = null;
|
|
89367
|
+
const answerRaw = raw["answer"];
|
|
89368
|
+
if (answerRaw !== undefined && answerRaw !== null) {
|
|
89369
|
+
if (!isObject(answerRaw) || !isString3(answerRaw["choice"]) || answerRaw["choice"].trim().length === 0)
|
|
89370
|
+
return `${id}: "answer" needs a choice`;
|
|
89371
|
+
const other = answerRaw["other"] === true;
|
|
89372
|
+
if (!other && !ids.has(answerRaw["choice"].trim())) {
|
|
89373
|
+
return `${id}: the answer "${answerRaw["choice"]}" is not one of the options (use "other": true for the human's own words)`;
|
|
89374
|
+
}
|
|
89375
|
+
answer = { choice: other ? oneLine2(answerRaw["choice"]) : answerRaw["choice"].trim(), other };
|
|
89376
|
+
}
|
|
89377
|
+
const sourceRaw = raw["source"];
|
|
89378
|
+
if (sourceRaw !== undefined && !isString3(sourceRaw))
|
|
89379
|
+
return `${id}: "source" must be a string`;
|
|
89380
|
+
const reasonRaw = raw["reason"];
|
|
89381
|
+
if (reasonRaw !== undefined && reasonRaw !== null && !isString3(reasonRaw))
|
|
89382
|
+
return `${id}: "reason" must be a string`;
|
|
89383
|
+
const reason = isString3(reasonRaw) ? oneLine2(reasonRaw) : "";
|
|
89384
|
+
const source = isString3(sourceRaw) ? oneLine2(sourceRaw) : "";
|
|
89385
|
+
return {
|
|
89386
|
+
id,
|
|
89387
|
+
at,
|
|
89388
|
+
flow,
|
|
89389
|
+
stage: isString3(stageRaw) && stageRaw.trim().length > 0 ? oneLine2(stageRaw) : DEFAULT_STAGE,
|
|
89390
|
+
question: oneLine2(question),
|
|
89391
|
+
options,
|
|
89392
|
+
recommendation,
|
|
89393
|
+
...source.length > 0 ? { source } : {},
|
|
89394
|
+
answer,
|
|
89395
|
+
...reason.length > 0 ? { reason } : {}
|
|
89396
|
+
};
|
|
89397
|
+
}
|
|
89398
|
+
function parseBackfill(text) {
|
|
89399
|
+
const entries = [];
|
|
89400
|
+
const errors = [];
|
|
89401
|
+
text.split(`
|
|
89402
|
+
`).forEach((line, index) => {
|
|
89403
|
+
if (line.trim().length === 0)
|
|
89404
|
+
return;
|
|
89405
|
+
let parsed;
|
|
89406
|
+
try {
|
|
89407
|
+
parsed = JSON.parse(line);
|
|
89408
|
+
} catch {
|
|
89409
|
+
errors.push({ line: index + 1, message: "not valid JSON" });
|
|
89410
|
+
return;
|
|
89411
|
+
}
|
|
89412
|
+
const entry = parseEntry3(parsed);
|
|
89413
|
+
if (typeof entry === "string")
|
|
89414
|
+
errors.push({ line: index + 1, message: entry });
|
|
89415
|
+
else
|
|
89416
|
+
entries.push(entry);
|
|
89417
|
+
});
|
|
89418
|
+
return { entries, errors };
|
|
89419
|
+
}
|
|
89420
|
+
function answerRecordOf(entry, at) {
|
|
89421
|
+
if (entry.answer === null)
|
|
89422
|
+
return;
|
|
89423
|
+
return {
|
|
89424
|
+
kind: "answer",
|
|
89425
|
+
id: entry.id,
|
|
89426
|
+
at,
|
|
89427
|
+
seq: 1,
|
|
89428
|
+
choice: entry.answer.choice,
|
|
89429
|
+
timeToAnswerMs: 0,
|
|
89430
|
+
changed: false,
|
|
89431
|
+
...entry.answer.other ? { other: true } : {}
|
|
89432
|
+
};
|
|
89433
|
+
}
|
|
89434
|
+
function reasonRecordOf(entry, at) {
|
|
89435
|
+
return entry.reason === undefined ? undefined : { kind: "reason", id: entry.id, at, reason: entry.reason };
|
|
89436
|
+
}
|
|
89437
|
+
function backfillRecords(entry) {
|
|
89438
|
+
const open = {
|
|
89439
|
+
kind: "open",
|
|
89440
|
+
id: entry.id,
|
|
89441
|
+
at: entry.at,
|
|
89442
|
+
flow: entry.flow,
|
|
89443
|
+
stage: entry.stage,
|
|
89444
|
+
question: entry.question,
|
|
89445
|
+
options: entry.options,
|
|
89446
|
+
recommendation: entry.recommendation,
|
|
89447
|
+
mode: "ordinary",
|
|
89448
|
+
order: entry.options.map((option) => option.id),
|
|
89449
|
+
showMark: entry.recommendation !== null,
|
|
89450
|
+
irreversible: false,
|
|
89451
|
+
backfilled: true,
|
|
89452
|
+
...entry.source !== undefined ? { source: entry.source } : {}
|
|
89453
|
+
};
|
|
89454
|
+
const records = [open];
|
|
89455
|
+
const answer = answerRecordOf(entry, entry.at);
|
|
89456
|
+
if (answer !== undefined)
|
|
89457
|
+
records.push(answer);
|
|
89458
|
+
const reason = reasonRecordOf(entry, entry.at);
|
|
89459
|
+
if (reason !== undefined)
|
|
89460
|
+
records.push(reason);
|
|
89461
|
+
return records;
|
|
89462
|
+
}
|
|
89463
|
+
async function importBackfill(cwd, text, options = {}) {
|
|
89464
|
+
if (options.dryRun === true)
|
|
89465
|
+
return importUnlocked(cwd, text, options);
|
|
89466
|
+
return withJournalLock(cwd, () => importUnlocked(cwd, text, options));
|
|
89467
|
+
}
|
|
89468
|
+
async function importUnlocked(cwd, text, options) {
|
|
89469
|
+
const { entries, errors } = parseBackfill(text);
|
|
89470
|
+
const records = await readRecords(cwd);
|
|
89471
|
+
const known = new Map;
|
|
89472
|
+
const answered = new Set;
|
|
89473
|
+
const reasoned = new Set;
|
|
89474
|
+
for (const record of records) {
|
|
89475
|
+
if (record.kind === "open")
|
|
89476
|
+
known.set(record.id, record);
|
|
89477
|
+
else if (record.kind === "answer")
|
|
89478
|
+
answered.add(record.id);
|
|
89479
|
+
else
|
|
89480
|
+
reasoned.add(record.id);
|
|
89481
|
+
}
|
|
89482
|
+
const result = {
|
|
89483
|
+
dryRun: options.dryRun === true,
|
|
89484
|
+
imported: 0,
|
|
89485
|
+
skipped: 0,
|
|
89486
|
+
withRecommendation: 0,
|
|
89487
|
+
answered: 0,
|
|
89488
|
+
deviations: 0,
|
|
89489
|
+
skippedIds: [],
|
|
89490
|
+
repaired: 0,
|
|
89491
|
+
repairedIds: [],
|
|
89492
|
+
malformed: errors
|
|
89493
|
+
};
|
|
89494
|
+
const toWrite = [];
|
|
89495
|
+
for (const entry of entries) {
|
|
89496
|
+
const existing = known.get(entry.id);
|
|
89497
|
+
if (existing !== undefined) {
|
|
89498
|
+
const answer = entry.answer;
|
|
89499
|
+
const repairable = existing.backfilled === true && answer !== null && !answered.has(entry.id) && (answer.other || existing.options.some((option) => option.id === answer.choice));
|
|
89500
|
+
if (!repairable || answer === null) {
|
|
89501
|
+
result.skipped += 1;
|
|
89502
|
+
result.skippedIds.push(entry.id);
|
|
89503
|
+
continue;
|
|
89504
|
+
}
|
|
89505
|
+
const missing = [answerRecordOf(entry, existing.at), reasoned.has(entry.id) ? undefined : reasonRecordOf(entry, existing.at)].filter((record) => record !== undefined);
|
|
89506
|
+
toWrite.push(...missing);
|
|
89507
|
+
answered.add(entry.id);
|
|
89508
|
+
result.repaired += 1;
|
|
89509
|
+
result.repairedIds.push(entry.id);
|
|
89510
|
+
result.answered += 1;
|
|
89511
|
+
if (existing.recommendation !== null && answer.choice !== existing.recommendation.optionId)
|
|
89512
|
+
result.deviations += 1;
|
|
89513
|
+
continue;
|
|
89514
|
+
}
|
|
89515
|
+
known.set(entry.id, backfillRecords(entry)[0]);
|
|
89516
|
+
answered.add(entry.id);
|
|
89517
|
+
result.imported += 1;
|
|
89518
|
+
if (entry.recommendation !== null)
|
|
89519
|
+
result.withRecommendation += 1;
|
|
89520
|
+
if (entry.answer !== null) {
|
|
89521
|
+
result.answered += 1;
|
|
89522
|
+
if (entry.recommendation !== null && entry.answer.choice !== entry.recommendation.optionId)
|
|
89523
|
+
result.deviations += 1;
|
|
89524
|
+
}
|
|
89525
|
+
toWrite.push(...backfillRecords(entry));
|
|
89526
|
+
}
|
|
89527
|
+
if (!result.dryRun)
|
|
89528
|
+
await appendRecords(cwd, toWrite);
|
|
89529
|
+
return result;
|
|
89530
|
+
}
|
|
89531
|
+
function renderImportResult(result) {
|
|
89532
|
+
const repaired = result.repaired > 0 ? `, repaired: ${result.repaired}` : "";
|
|
89533
|
+
const malformed = result.malformed.length > 0 ? `, malformed: ${result.malformed.length}` : "";
|
|
89534
|
+
const lines = [
|
|
89535
|
+
`${result.dryRun ? "Dry run, nothing written. " : ""}Imported: ${result.imported}, skipped: ${result.skipped}, with recommendation: ${result.withRecommendation}, answered: ${result.answered}, deviations: ${result.deviations}${repaired}${malformed}`
|
|
89536
|
+
];
|
|
89537
|
+
if (result.skippedIds.length > 0) {
|
|
89538
|
+
const shown = result.skippedIds.slice(0, 20).map((id) => oneLine2(id, 80));
|
|
89539
|
+
lines.push(`Skipped (already in the journal): ${shown.join(", ")}${result.skippedIds.length > shown.length ? `, ... and ${result.skippedIds.length - shown.length} more` : ""}`);
|
|
89540
|
+
}
|
|
89541
|
+
if (result.repairedIds.length > 0) {
|
|
89542
|
+
const shown = result.repairedIds.slice(0, 20).map((id) => oneLine2(id, 80));
|
|
89543
|
+
lines.push(`Repaired (the answer was missing from the journal and was added): ${shown.join(", ")}${result.repairedIds.length > shown.length ? `, ... and ${result.repairedIds.length - shown.length} more` : ""}`);
|
|
89544
|
+
}
|
|
89545
|
+
if (result.malformed.length > 0) {
|
|
89546
|
+
lines.push("Malformed (skipped):");
|
|
89547
|
+
for (const error of result.malformed.slice(0, 20))
|
|
89548
|
+
lines.push(` line ${error.line}: ${oneLine2(error.message, 200)}`);
|
|
89549
|
+
if (result.malformed.length > 20)
|
|
89550
|
+
lines.push(` ... and ${result.malformed.length - 20} more`);
|
|
89551
|
+
}
|
|
89552
|
+
return lines.join(`
|
|
89553
|
+
`);
|
|
89554
|
+
}
|
|
89555
|
+
var isString3 = (value) => typeof value === "string", isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
89556
|
+
var init_import = __esm(() => {
|
|
89557
|
+
init_journal2();
|
|
89558
|
+
init_store6();
|
|
89559
|
+
});
|
|
89560
|
+
|
|
89119
89561
|
// src/decisions/context.ts
|
|
89120
89562
|
async function currentBranch(cwd) {
|
|
89121
89563
|
try {
|
|
@@ -89203,7 +89645,7 @@ async function latestAnsweredDecision(cwd, flow) {
|
|
|
89203
89645
|
const answered = new Set(records.filter((r) => r.kind === "answer").map((r) => r.id));
|
|
89204
89646
|
for (let i = records.length - 1;i >= 0; i -= 1) {
|
|
89205
89647
|
const record = records[i];
|
|
89206
|
-
if (record !== undefined && record.kind === "open" && answered.has(record.id) && (flow === undefined || record.flow === flow))
|
|
89648
|
+
if (record !== undefined && record.kind === "open" && record.backfilled !== true && answered.has(record.id) && (flow === undefined || record.flow === flow))
|
|
89207
89649
|
return record.id;
|
|
89208
89650
|
}
|
|
89209
89651
|
return;
|
|
@@ -89261,6 +89703,8 @@ function resolveOptionId(open, typed) {
|
|
|
89261
89703
|
}
|
|
89262
89704
|
async function changeAnswer(input) {
|
|
89263
89705
|
const open = await resolveOpen(input.cwd, input.id, input.lastId, input.flow, input.session, "keryx decisions answer <id> --choice <option>");
|
|
89706
|
+
if (open.backfilled === true)
|
|
89707
|
+
throw new Error(`decision ${open.id} is a backfilled historical record; its answer is not changed`);
|
|
89264
89708
|
const choice = resolveOptionId(open, input.choice);
|
|
89265
89709
|
const answers = (await readRecords(input.cwd)).filter((r) => r.kind === "answer" && r.id === open.id);
|
|
89266
89710
|
const first = [...answers].sort((x, y) => x.seq - y.seq)[0];
|
|
@@ -89280,9 +89724,15 @@ async function loadReport(cwd) {
|
|
|
89280
89724
|
async function reportText(cwd) {
|
|
89281
89725
|
return renderReport(await loadReport(cwd));
|
|
89282
89726
|
}
|
|
89727
|
+
async function reportLine(cwd) {
|
|
89728
|
+
return renderReportLine(await loadReport(cwd));
|
|
89729
|
+
}
|
|
89283
89730
|
async function decisionCount(cwd) {
|
|
89284
89731
|
return (await loadReport(cwd)).total;
|
|
89285
89732
|
}
|
|
89733
|
+
async function backfilledCount(cwd) {
|
|
89734
|
+
return (await loadReport(cwd)).backfilled.total;
|
|
89735
|
+
}
|
|
89286
89736
|
var init_service13 = __esm(() => {
|
|
89287
89737
|
init_report4();
|
|
89288
89738
|
init_store6();
|
|
@@ -89290,13 +89740,14 @@ var init_service13 = __esm(() => {
|
|
|
89290
89740
|
init_blind();
|
|
89291
89741
|
init_journal2();
|
|
89292
89742
|
init_report4();
|
|
89743
|
+
init_import();
|
|
89293
89744
|
init_store6();
|
|
89294
89745
|
init_context2();
|
|
89295
89746
|
init_followup();
|
|
89296
89747
|
});
|
|
89297
89748
|
|
|
89298
89749
|
// src/tui/ask-user-bridge.ts
|
|
89299
|
-
import { randomUUID as
|
|
89750
|
+
import { randomUUID as randomUUID24 } from "crypto";
|
|
89300
89751
|
function setAskUserHost(fn) {
|
|
89301
89752
|
host = fn;
|
|
89302
89753
|
}
|
|
@@ -89331,7 +89782,7 @@ function journaledAskUser(cwd) {
|
|
|
89331
89782
|
var host, notice, lastDecisionId, sessionId;
|
|
89332
89783
|
var init_ask_user_bridge = __esm(() => {
|
|
89333
89784
|
init_service13();
|
|
89334
|
-
sessionId = `s-${
|
|
89785
|
+
sessionId = `s-${randomUUID24()}`;
|
|
89335
89786
|
});
|
|
89336
89787
|
|
|
89337
89788
|
// src/harness/tool/metaproject-operations.ts
|
|
@@ -90872,7 +91323,7 @@ var init_project_tools = __esm(() => {
|
|
|
90872
91323
|
});
|
|
90873
91324
|
|
|
90874
91325
|
// src/commands/interactive-agent-tools.ts
|
|
90875
|
-
import { randomUUID as
|
|
91326
|
+
import { randomUUID as randomUUID25 } from "crypto";
|
|
90876
91327
|
function denyInteractiveTools(tools, denied) {
|
|
90877
91328
|
if (denied.length === 0)
|
|
90878
91329
|
return [...tools];
|
|
@@ -90890,7 +91341,7 @@ function buildInteractiveAgentTools(input) {
|
|
|
90890
91341
|
const getSessionDir = input.getSessionDir ?? (() => {
|
|
90891
91342
|
return;
|
|
90892
91343
|
});
|
|
90893
|
-
const idSeq = input.idSeq ?? (() =>
|
|
91344
|
+
const idSeq = input.idSeq ?? (() => randomUUID25());
|
|
90894
91345
|
const clock = input.clock ?? (() => new Date().toISOString());
|
|
90895
91346
|
const jobRegistry = input.jobRegistry;
|
|
90896
91347
|
const project = buildProjectTools(input.cwd, input.metaprojectPort);
|
|
@@ -93393,9 +93844,9 @@ var init_registry3 = __esm(() => {
|
|
|
93393
93844
|
});
|
|
93394
93845
|
|
|
93395
93846
|
// src/harness/tool/builtin/spawn-subagent-tool.ts
|
|
93396
|
-
import { createHash as createHash60, randomUUID as
|
|
93847
|
+
import { createHash as createHash60, randomUUID as randomUUID26 } from "crypto";
|
|
93397
93848
|
import { execFile as execFile7 } from "child_process";
|
|
93398
|
-
import { lstat as lstat14, mkdtemp as mkdtemp2, readFile as readFile104, realpath as realpath17, rm as
|
|
93849
|
+
import { lstat as lstat14, mkdtemp as mkdtemp2, readFile as readFile104, realpath as realpath17, rm as rm14 } from "fs/promises";
|
|
93399
93850
|
import { tmpdir as tmpdir6 } from "os";
|
|
93400
93851
|
import path216 from "path";
|
|
93401
93852
|
import { promisify as promisify5 } from "util";
|
|
@@ -93585,7 +94036,7 @@ async function verifyWorktreeCandidate(candidate, commonDir, worktreesDir) {
|
|
|
93585
94036
|
}
|
|
93586
94037
|
}
|
|
93587
94038
|
function createSpawnSubagentTool(deps) {
|
|
93588
|
-
const idSeq = deps.idSeq ?? (() =>
|
|
94039
|
+
const idSeq = deps.idSeq ?? (() => randomUUID26());
|
|
93589
94040
|
const clock = deps.clock ?? (() => new Date().toISOString());
|
|
93590
94041
|
const parentRunId = deps.parentRunId ?? idSeq();
|
|
93591
94042
|
const parentSessionId = deps.parentSessionId ?? idSeq();
|
|
@@ -93946,7 +94397,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
93946
94397
|
provider = deps.makeProvider(runModel.provider, runModel.model, parent.baseUrl);
|
|
93947
94398
|
} catch (cause) {
|
|
93948
94399
|
if (ephemeralDir !== undefined) {
|
|
93949
|
-
await
|
|
94400
|
+
await rm14(ephemeralDir, { recursive: true, force: true }).catch(() => {});
|
|
93950
94401
|
}
|
|
93951
94402
|
await fireSubagentStop("Error").catch(() => {});
|
|
93952
94403
|
ledger.release(spawned.reservation.reservationId, {
|
|
@@ -94072,7 +94523,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
94072
94523
|
}
|
|
94073
94524
|
}
|
|
94074
94525
|
} finally {
|
|
94075
|
-
await
|
|
94526
|
+
await rm14(dirToClean, { recursive: true, force: true }).catch(() => {});
|
|
94076
94527
|
}
|
|
94077
94528
|
});
|
|
94078
94529
|
} catch (lockCause) {
|
|
@@ -94082,7 +94533,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
94082
94533
|
id: workerId,
|
|
94083
94534
|
entry: { kind: "system", text: `slate cleanup lock failed (ignored): ${lockMsg}` }
|
|
94084
94535
|
});
|
|
94085
|
-
await
|
|
94536
|
+
await rm14(dirToClean, { recursive: true, force: true }).catch(() => {});
|
|
94086
94537
|
}
|
|
94087
94538
|
};
|
|
94088
94539
|
try {
|
|
@@ -95887,7 +96338,7 @@ var init_dispatch2 = __esm(() => {
|
|
|
95887
96338
|
|
|
95888
96339
|
// src/harness/external/acp-fs.ts
|
|
95889
96340
|
import { constants as constants2, realpathSync as realpathSync15, statSync as statSync12 } from "fs";
|
|
95890
|
-
import { mkdir as mkdir53, open as
|
|
96341
|
+
import { mkdir as mkdir53, open as open10, readFile as readFile105 } from "fs/promises";
|
|
95891
96342
|
import path219 from "path";
|
|
95892
96343
|
function refusedMethodError(method, reason) {
|
|
95893
96344
|
return new AcpError(JSON_RPC_ERROR_CODES.methodNotFound, `${method} refused: ${reason}`, { reason });
|
|
@@ -95956,7 +96407,7 @@ async function writeTextFileInWorktree(worktree, target, content, requested, hoo
|
|
|
95956
96407
|
const finalTarget = path219.join(parentReal, path219.basename(target));
|
|
95957
96408
|
let handle;
|
|
95958
96409
|
try {
|
|
95959
|
-
handle = await
|
|
96410
|
+
handle = await open10(finalTarget, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_TRUNC | constants2.O_NOFOLLOW, 420);
|
|
95960
96411
|
} catch (error) {
|
|
95961
96412
|
const code = error.code;
|
|
95962
96413
|
if (code === "ELOOP" || code === "EMLINK") {
|
|
@@ -96121,7 +96572,7 @@ function describeOutcome(outcome) {
|
|
|
96121
96572
|
return outcome.reason;
|
|
96122
96573
|
return "an unexpected result";
|
|
96123
96574
|
}
|
|
96124
|
-
function
|
|
96575
|
+
function isObject2(value) {
|
|
96125
96576
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
96126
96577
|
}
|
|
96127
96578
|
function short(value, max = 500) {
|
|
@@ -96131,7 +96582,7 @@ function short(value, max = 500) {
|
|
|
96131
96582
|
function readOptions(raw) {
|
|
96132
96583
|
if (!Array.isArray(raw))
|
|
96133
96584
|
return [];
|
|
96134
|
-
return raw.filter((option) =>
|
|
96585
|
+
return raw.filter((option) => isObject2(option) && typeof option["optionId"] === "string" && typeof option["kind"] === "string");
|
|
96135
96586
|
}
|
|
96136
96587
|
async function superviseAcpRun(input, deps) {
|
|
96137
96588
|
const clientCapabilities = acpClientCapabilities(input.write);
|
|
@@ -96203,7 +96654,7 @@ async function superviseAcpRun(input, deps) {
|
|
|
96203
96654
|
let promptRequestId;
|
|
96204
96655
|
let boundSessionId;
|
|
96205
96656
|
const guardSession = (method, params) => {
|
|
96206
|
-
const claimed =
|
|
96657
|
+
const claimed = isObject2(params) ? params["sessionId"] : undefined;
|
|
96207
96658
|
let reason;
|
|
96208
96659
|
if (phase !== "prompting") {
|
|
96209
96660
|
reason = phase === "handshake" ? "no prompt is in progress yet; keryx answers only during this run's turn" : "this run's turn has ended; keryx no longer answers for it";
|
|
@@ -96234,9 +96685,9 @@ async function superviseAcpRun(input, deps) {
|
|
|
96234
96685
|
});
|
|
96235
96686
|
dispatcher.onRequest(ACP_CLIENT_METHODS.sessionRequestPermission, async (params, context) => {
|
|
96236
96687
|
guardSession(ACP_CLIENT_METHODS.sessionRequestPermission, params);
|
|
96237
|
-
const p =
|
|
96688
|
+
const p = isObject2(params) ? params : {};
|
|
96238
96689
|
const rawToolCall = p["toolCall"];
|
|
96239
|
-
const toolCall =
|
|
96690
|
+
const toolCall = isObject2(rawToolCall) && typeof rawToolCall["toolCallId"] === "string" ? rawToolCall : { toolCallId: "unidentified" };
|
|
96240
96691
|
const { answer, decision } = await answerAcpPermission({ requestId: context.id, toolCall, options: readOptions(p["options"]) }, permissionContext);
|
|
96241
96692
|
recordDecision(decision);
|
|
96242
96693
|
return { outcome: answer.outcome };
|
|
@@ -96244,7 +96695,7 @@ async function superviseAcpRun(input, deps) {
|
|
|
96244
96695
|
dispatcher.onRequest(ACP_CLIENT_METHODS.fsReadTextFile, async (params) => {
|
|
96245
96696
|
const method = ACP_CLIENT_METHODS.fsReadTextFile;
|
|
96246
96697
|
guardSession(method, params);
|
|
96247
|
-
const p =
|
|
96698
|
+
const p = isObject2(params) ? params : {};
|
|
96248
96699
|
try {
|
|
96249
96700
|
const { content } = await readTextFileInWorktree(input.cwd, p);
|
|
96250
96701
|
recordFs({ method, path: String(p["path"]), outcome: "served", bytes: Buffer.byteLength(content, "utf8") });
|
|
@@ -96257,7 +96708,7 @@ async function superviseAcpRun(input, deps) {
|
|
|
96257
96708
|
dispatcher.onRequest(ACP_CLIENT_METHODS.fsWriteTextFile, async (params, context) => {
|
|
96258
96709
|
const method = ACP_CLIENT_METHODS.fsWriteTextFile;
|
|
96259
96710
|
guardSession(method, params);
|
|
96260
|
-
const p =
|
|
96711
|
+
const p = isObject2(params) ? params : {};
|
|
96261
96712
|
const requested = typeof p["path"] === "string" ? p["path"] : String(p["path"]);
|
|
96262
96713
|
if (!input.write) {
|
|
96263
96714
|
const reason = "keryx did not advertise fs.writeTextFile (a read-only run)";
|
|
@@ -96294,7 +96745,7 @@ async function superviseAcpRun(input, deps) {
|
|
|
96294
96745
|
});
|
|
96295
96746
|
for (const [method, reason] of ACP_REFUSED_CLIENT_METHODS) {
|
|
96296
96747
|
dispatcher.onRequest(method, (params) => {
|
|
96297
|
-
const p =
|
|
96748
|
+
const p = isObject2(params) ? params : {};
|
|
96298
96749
|
recordFs({
|
|
96299
96750
|
method,
|
|
96300
96751
|
...typeof p["command"] === "string" ? { path: p["command"] } : {},
|
|
@@ -96308,7 +96759,7 @@ async function superviseAcpRun(input, deps) {
|
|
|
96308
96759
|
return;
|
|
96309
96760
|
});
|
|
96310
96761
|
dispatcher.onNotification(ACP_CLIENT_METHODS.sessionUpdate, (params) => {
|
|
96311
|
-
if (!
|
|
96762
|
+
if (!isObject2(params) || !isObject2(params["update"]))
|
|
96312
96763
|
return;
|
|
96313
96764
|
if (boundSessionId === undefined || params["sessionId"] !== boundSessionId) {
|
|
96314
96765
|
stderr.push(`[keryx] ignored a session/update for session ${JSON.stringify(params["sessionId"] ?? null)}`);
|
|
@@ -96368,7 +96819,7 @@ async function superviseAcpRun(input, deps) {
|
|
|
96368
96819
|
usage = { used: update.used, size: update.size };
|
|
96369
96820
|
}
|
|
96370
96821
|
const reported = update.cost;
|
|
96371
|
-
if (
|
|
96822
|
+
if (isObject2(reported) && typeof reported["amount"] === "number" && typeof reported["currency"] === "string") {
|
|
96372
96823
|
cost = { amount: reported["amount"], currency: reported["currency"] };
|
|
96373
96824
|
}
|
|
96374
96825
|
emit(cost !== undefined && cost.currency === "USD" ? { kind: "usage", costUnits: cost.amount } : { kind: "usage" });
|
|
@@ -96385,7 +96836,7 @@ async function superviseAcpRun(input, deps) {
|
|
|
96385
96836
|
if (phase === "prompting" && promptRequestId !== undefined && line.includes(promptRequestId)) {
|
|
96386
96837
|
try {
|
|
96387
96838
|
const value = JSON.parse(line);
|
|
96388
|
-
if (
|
|
96839
|
+
if (isObject2(value) && !("method" in value) && value["id"] === promptRequestId)
|
|
96389
96840
|
phase = "ended";
|
|
96390
96841
|
} catch {}
|
|
96391
96842
|
}
|
|
@@ -96465,16 +96916,16 @@ async function superviseAcpRun(input, deps) {
|
|
|
96465
96916
|
failure = `the agent did not answer initialize within ${input.timeoutMs}ms`;
|
|
96466
96917
|
return await finish();
|
|
96467
96918
|
}
|
|
96468
|
-
if (init.kind !== "result" || !
|
|
96919
|
+
if (init.kind !== "result" || !isObject2(init.result)) {
|
|
96469
96920
|
failure = `the agent did not answer initialize: ${describeOutcome(init)}`;
|
|
96470
96921
|
return await finish();
|
|
96471
96922
|
}
|
|
96472
96923
|
const initResult = init.result;
|
|
96473
96924
|
const version = initResult["protocolVersion"];
|
|
96474
96925
|
protocolVersion = typeof version === "number" ? version : undefined;
|
|
96475
|
-
if (
|
|
96926
|
+
if (isObject2(initResult["agentInfo"]))
|
|
96476
96927
|
agentInfo = initResult["agentInfo"];
|
|
96477
|
-
if (
|
|
96928
|
+
if (isObject2(initResult["agentCapabilities"]))
|
|
96478
96929
|
agentCapabilities = initResult["agentCapabilities"];
|
|
96479
96930
|
if (typeof version !== "number" || !ACP_SUPPORTED_PROTOCOL_VERSIONS.includes(version)) {
|
|
96480
96931
|
failure = `unsupported ACP protocol version: the agent answered initialize with protocolVersion ${JSON.stringify(version)}; ` + `keryx speaks ${ACP_SUPPORTED_PROTOCOL_VERSIONS.join(", ")}`;
|
|
@@ -96486,7 +96937,7 @@ async function superviseAcpRun(input, deps) {
|
|
|
96486
96937
|
failure = `the agent did not answer session/new within ${input.timeoutMs}ms`;
|
|
96487
96938
|
return await finish();
|
|
96488
96939
|
}
|
|
96489
|
-
if (created.kind !== "result" || !
|
|
96940
|
+
if (created.kind !== "result" || !isObject2(created.result) || typeof created.result["sessionId"] !== "string") {
|
|
96490
96941
|
failure = `the agent refused session/new: ${created.kind === "result" ? "no sessionId in the answer" : describeOutcome(created)}`;
|
|
96491
96942
|
return await finish();
|
|
96492
96943
|
}
|
|
@@ -96526,12 +96977,12 @@ ${resource.text}` });
|
|
|
96526
96977
|
const grace = afterMs(graceMs);
|
|
96527
96978
|
const late = await Promise.race([turn, grace.promise]);
|
|
96528
96979
|
grace.cancel();
|
|
96529
|
-
if (late !== TIMEOUT && late.kind === "result" &&
|
|
96980
|
+
if (late !== TIMEOUT && late.kind === "result" && isObject2(late.result) && typeof late.result["stopReason"] === "string") {
|
|
96530
96981
|
stopReason = late.result["stopReason"];
|
|
96531
96982
|
}
|
|
96532
96983
|
return await finish();
|
|
96533
96984
|
}
|
|
96534
|
-
if (answered.kind !== "result" || !
|
|
96985
|
+
if (answered.kind !== "result" || !isObject2(answered.result)) {
|
|
96535
96986
|
failure = `session/prompt did not complete: ${describeOutcome(answered)}`;
|
|
96536
96987
|
return await finish();
|
|
96537
96988
|
}
|
|
@@ -96610,7 +97061,7 @@ var init_acp_client = __esm(() => {
|
|
|
96610
97061
|
// src/harness/external/acp-run.ts
|
|
96611
97062
|
import { execFile as execFile9 } from "child_process";
|
|
96612
97063
|
import { existsSync as existsSync38, readFileSync as readFileSync20, writeFileSync as writeFileSync4 } from "fs";
|
|
96613
|
-
import { mkdtemp as mkdtemp3, rm as
|
|
97064
|
+
import { mkdtemp as mkdtemp3, rm as rm15 } from "fs/promises";
|
|
96614
97065
|
import { tmpdir as tmpdir7 } from "os";
|
|
96615
97066
|
import path220 from "path";
|
|
96616
97067
|
import { promisify as promisify7 } from "util";
|
|
@@ -96687,7 +97138,7 @@ async function captureWorktreePatch(worktreePath) {
|
|
|
96687
97138
|
} catch {
|
|
96688
97139
|
return;
|
|
96689
97140
|
} finally {
|
|
96690
|
-
await
|
|
97141
|
+
await rm15(dir, { recursive: true, force: true }).catch(() => {
|
|
96691
97142
|
return;
|
|
96692
97143
|
});
|
|
96693
97144
|
}
|
|
@@ -98048,7 +98499,7 @@ var init_codec2 = __esm(() => {
|
|
|
98048
98499
|
});
|
|
98049
98500
|
|
|
98050
98501
|
// src/harness/external/runtime.ts
|
|
98051
|
-
import { mkdtemp as mkdtemp4, rm as
|
|
98502
|
+
import { mkdtemp as mkdtemp4, rm as rm16, writeFile as writeFile34 } from "fs/promises";
|
|
98052
98503
|
import { tmpdir as tmpdir8 } from "os";
|
|
98053
98504
|
import path221 from "path";
|
|
98054
98505
|
function refuse2(status, output) {
|
|
@@ -98065,7 +98516,7 @@ async function prepareResultSchema() {
|
|
|
98065
98516
|
return { ok: true, schema, schemaText, schemaPath, schemaDir };
|
|
98066
98517
|
} catch (error) {
|
|
98067
98518
|
if (schemaDir !== undefined) {
|
|
98068
|
-
await
|
|
98519
|
+
await rm16(schemaDir, { recursive: true, force: true }).catch(() => {
|
|
98069
98520
|
return;
|
|
98070
98521
|
});
|
|
98071
98522
|
}
|
|
@@ -98172,7 +98623,7 @@ async function runExternalChild(input, deps) {
|
|
|
98172
98623
|
try {
|
|
98173
98624
|
await writeFile34(resultSchemaPathForCodec, JSON.stringify(toStrictOutputSchema(bundledResultSchema)), "utf8");
|
|
98174
98625
|
} catch (error) {
|
|
98175
|
-
await
|
|
98626
|
+
await rm16(schemaDir, { recursive: true, force: true }).catch(() => {
|
|
98176
98627
|
return;
|
|
98177
98628
|
});
|
|
98178
98629
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -98267,7 +98718,7 @@ async function runExternalChild(input, deps) {
|
|
|
98267
98718
|
});
|
|
98268
98719
|
}
|
|
98269
98720
|
} finally {
|
|
98270
|
-
await
|
|
98721
|
+
await rm16(schemaDir, { recursive: true, force: true }).catch(() => {
|
|
98271
98722
|
return;
|
|
98272
98723
|
});
|
|
98273
98724
|
}
|
|
@@ -98434,7 +98885,7 @@ var init_runtime2 = __esm(() => {
|
|
|
98434
98885
|
});
|
|
98435
98886
|
|
|
98436
98887
|
// src/harness/run-external-factory.ts
|
|
98437
|
-
import { randomUUID as
|
|
98888
|
+
import { randomUUID as randomUUID27 } from "crypto";
|
|
98438
98889
|
import { execFile as execFile10 } from "child_process";
|
|
98439
98890
|
import { mkdir as mkdir54 } from "fs/promises";
|
|
98440
98891
|
import { tmpdir as tmpdir9 } from "os";
|
|
@@ -98495,7 +98946,7 @@ async function createRunExternal(options) {
|
|
|
98495
98946
|
return;
|
|
98496
98947
|
}
|
|
98497
98948
|
const config = gate.config;
|
|
98498
|
-
const idSeq = options.idSeq ?? (() =>
|
|
98949
|
+
const idSeq = options.idSeq ?? (() => randomUUID27());
|
|
98499
98950
|
const worktreesDir = options.worktreesDir ?? path222.join(tmpdir9(), "keryx-external-worktrees");
|
|
98500
98951
|
const spawn = options.spawn ?? createBunSpawnPort();
|
|
98501
98952
|
const worktree = options.worktree ?? ensuringParentDir(createGitWorktreePort({ repoRoot: options.cwd, worktreesDir }), worktreesDir);
|
|
@@ -99434,7 +99885,7 @@ var init_client2 = __esm(() => {
|
|
|
99434
99885
|
|
|
99435
99886
|
// src/lib/file-lock.ts
|
|
99436
99887
|
import { closeSync as closeSync3, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync21, rmSync as rmSync3, statSync as statSync13, writeFileSync as writeFileSync6 } from "fs";
|
|
99437
|
-
import { randomUUID as
|
|
99888
|
+
import { randomUUID as randomUUID28 } from "crypto";
|
|
99438
99889
|
import path225 from "path";
|
|
99439
99890
|
function sleepSync(ms) {
|
|
99440
99891
|
const bunSleep = globalThis.Bun?.sleepSync;
|
|
@@ -99451,7 +99902,7 @@ function withFileLock2(lockPath, fn, options = {}) {
|
|
|
99451
99902
|
} catch {
|
|
99452
99903
|
return null;
|
|
99453
99904
|
}
|
|
99454
|
-
const nonce =
|
|
99905
|
+
const nonce = randomUUID28();
|
|
99455
99906
|
const started = Date.now();
|
|
99456
99907
|
const deadline = started + LOCK_TIMEOUT_MS;
|
|
99457
99908
|
let handle = null;
|
|
@@ -101709,7 +102160,7 @@ import {
|
|
|
101709
102160
|
writeFileSync as writeFileSync7
|
|
101710
102161
|
} from "fs";
|
|
101711
102162
|
import path232 from "path";
|
|
101712
|
-
import { randomUUID as
|
|
102163
|
+
import { randomUUID as randomUUID29 } from "crypto";
|
|
101713
102164
|
function projectRegistryPath(dir) {
|
|
101714
102165
|
return path232.join(keryxConfigDir(dir), "projects.json");
|
|
101715
102166
|
}
|
|
@@ -101824,7 +102275,7 @@ function quarantineDamagedRegistry(dir, onWarn) {
|
|
|
101824
102275
|
}
|
|
101825
102276
|
function saveProjectRegistry(registry, dir, onWarn) {
|
|
101826
102277
|
const file = projectRegistryPath(dir);
|
|
101827
|
-
const temp = `${file}.${
|
|
102278
|
+
const temp = `${file}.${randomUUID29()}.tmp`;
|
|
101828
102279
|
try {
|
|
101829
102280
|
ensureKeryxConfigDir(dir);
|
|
101830
102281
|
const sorted = {
|
|
@@ -101880,7 +102331,7 @@ function registerProject(projectPath, options = {}) {
|
|
|
101880
102331
|
return { ok: true, entry: existing, created: false };
|
|
101881
102332
|
}
|
|
101882
102333
|
const entry = {
|
|
101883
|
-
projectId:
|
|
102334
|
+
projectId: randomUUID29(),
|
|
101884
102335
|
path: absolute,
|
|
101885
102336
|
displayName: options.displayName ?? path232.basename(absolute),
|
|
101886
102337
|
state: "active",
|
|
@@ -109019,7 +109470,7 @@ function formatHygieneTags(hygiene) {
|
|
|
109019
109470
|
}
|
|
109020
109471
|
|
|
109021
109472
|
// src/sac/lifecycle-flag.ts
|
|
109022
|
-
import { randomUUID as
|
|
109473
|
+
import { randomUUID as randomUUID30 } from "crypto";
|
|
109023
109474
|
function normalize3(raw) {
|
|
109024
109475
|
return raw.replace(/^\.\//, "");
|
|
109025
109476
|
}
|
|
@@ -109038,7 +109489,7 @@ async function computeLifecycleFlags(cwd, now = () => new Date) {
|
|
|
109038
109489
|
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
109039
109490
|
strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }
|
|
109040
109491
|
});
|
|
109041
|
-
const workspaces = await workspaceService.list({ request: undefined, requestCorrelationId:
|
|
109492
|
+
const workspaces = await workspaceService.list({ request: undefined, requestCorrelationId: randomUUID30(), includeArchived: true });
|
|
109042
109493
|
for (const workspace of workspaces) {
|
|
109043
109494
|
const component = workspace.resources.find((r) => r.kind === "component")?.uri;
|
|
109044
109495
|
if (component !== undefined && !isStillPresent(component, valid)) {
|
|
@@ -109067,8 +109518,8 @@ var init_lifecycle_flag = __esm(() => {
|
|
|
109067
109518
|
});
|
|
109068
109519
|
|
|
109069
109520
|
// src/sac/catch-up.ts
|
|
109070
|
-
import { randomUUID as
|
|
109071
|
-
import { readdir as readdir36, stat as
|
|
109521
|
+
import { randomUUID as randomUUID31 } from "crypto";
|
|
109522
|
+
import { readdir as readdir36, stat as stat25 } from "fs/promises";
|
|
109072
109523
|
import path243 from "path";
|
|
109073
109524
|
async function buildCatchUp(input) {
|
|
109074
109525
|
const [proposals, sessionCategories, lifecycleFlagsAll, unreviewedPathsAll] = await Promise.all([
|
|
@@ -109083,7 +109534,7 @@ async function buildCatchUp(input) {
|
|
|
109083
109534
|
}
|
|
109084
109535
|
async function collectProposals(cwd, workspaceId) {
|
|
109085
109536
|
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
109086
|
-
const actor = await authorizationServer.actorContextFor(undefined,
|
|
109537
|
+
const actor = await authorizationServer.actorContextFor(undefined, randomUUID31());
|
|
109087
109538
|
if (!actor)
|
|
109088
109539
|
throw new Error("trusted ActorContext is required for catch-up");
|
|
109089
109540
|
const proposalService = createLocalProposalLifecycleService(cwd);
|
|
@@ -109286,7 +109737,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
109286
109737
|
async function attributeToSession(absolutePath, sessionsNewestFirst) {
|
|
109287
109738
|
let mtimeMs;
|
|
109288
109739
|
try {
|
|
109289
|
-
mtimeMs = (await
|
|
109740
|
+
mtimeMs = (await stat25(absolutePath)).mtimeMs;
|
|
109290
109741
|
} catch {
|
|
109291
109742
|
return;
|
|
109292
109743
|
}
|
|
@@ -109572,7 +110023,7 @@ var init_session = __esm(() => {
|
|
|
109572
110023
|
});
|
|
109573
110024
|
|
|
109574
110025
|
// src/tui/inspector-sources.ts
|
|
109575
|
-
import { randomUUID as
|
|
110026
|
+
import { randomUUID as randomUUID32 } from "crypto";
|
|
109576
110027
|
function workspaceFromManifest(manifest) {
|
|
109577
110028
|
return {
|
|
109578
110029
|
id: manifest.id,
|
|
@@ -109651,7 +110102,7 @@ async function loadInspectorWorkspaces(cwd) {
|
|
|
109651
110102
|
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
109652
110103
|
strictGuard: localSacGuard()
|
|
109653
110104
|
});
|
|
109654
|
-
const listed = await service.list({ request: undefined, requestCorrelationId:
|
|
110105
|
+
const listed = await service.list({ request: undefined, requestCorrelationId: randomUUID32() });
|
|
109655
110106
|
return listed.map(workspaceFromManifest);
|
|
109656
110107
|
} catch {
|
|
109657
110108
|
return [];
|
|
@@ -109664,7 +110115,7 @@ async function loadInspectorWorkspace(cwd, workspaceId) {
|
|
|
109664
110115
|
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
109665
110116
|
strictGuard: localSacGuard()
|
|
109666
110117
|
});
|
|
109667
|
-
const manifest = await service.show({ request: undefined, requestCorrelationId:
|
|
110118
|
+
const manifest = await service.show({ request: undefined, requestCorrelationId: randomUUID32(), workspaceId });
|
|
109668
110119
|
return workspaceFromManifest(manifest);
|
|
109669
110120
|
} catch {
|
|
109670
110121
|
return;
|
|
@@ -111983,7 +112434,7 @@ var init_repair = __esm(() => {
|
|
|
111983
112434
|
});
|
|
111984
112435
|
|
|
111985
112436
|
// src/review/managed.ts
|
|
111986
|
-
import { mkdir as mkdir55, readFile as readFile117, readdir as readdir38, realpath as realpath18, rm as
|
|
112437
|
+
import { mkdir as mkdir55, readFile as readFile117, readdir as readdir38, realpath as realpath18, rm as rm17, stat as stat26 } from "fs/promises";
|
|
111987
112438
|
import path246 from "path";
|
|
111988
112439
|
function reviewsRoot(cwd) {
|
|
111989
112440
|
return path246.join(cwd, ".metaproject", "reviews");
|
|
@@ -112210,7 +112661,7 @@ async function persistScreenRadius(packageDir, resolved) {
|
|
|
112210
112661
|
`);
|
|
112211
112662
|
}
|
|
112212
112663
|
if (resolved.source === "handoff" && resolved.file !== undefined) {
|
|
112213
|
-
await
|
|
112664
|
+
await rm17(resolved.file, { force: true });
|
|
112214
112665
|
}
|
|
112215
112666
|
}
|
|
112216
112667
|
function concurrencyPlan(input, coverage) {
|
|
@@ -112489,7 +112940,7 @@ function readFromWorkingTree(input) {
|
|
|
112489
112940
|
return null;
|
|
112490
112941
|
}
|
|
112491
112942
|
try {
|
|
112492
|
-
const info = await
|
|
112943
|
+
const info = await stat26(real);
|
|
112493
112944
|
if (!info.isFile() || info.size > MAX_LOCATE_FILE_BYTES) {
|
|
112494
112945
|
return null;
|
|
112495
112946
|
}
|
|
@@ -114338,6 +114789,9 @@ function channelsRoutePath(route) {
|
|
|
114338
114789
|
function isSessionId(value) {
|
|
114339
114790
|
return typeof value === "string" && SESSION_ID_PATTERN.test(value);
|
|
114340
114791
|
}
|
|
114792
|
+
function isApprovalId(value) {
|
|
114793
|
+
return typeof value === "string" && APPROVAL_ID_PATTERN.test(value);
|
|
114794
|
+
}
|
|
114341
114795
|
function approvalCallbackData(approvalId, decision) {
|
|
114342
114796
|
return `ap:${approvalId}:${decision}`;
|
|
114343
114797
|
}
|
|
@@ -114410,7 +114864,7 @@ class SseParser {
|
|
|
114410
114864
|
return frames;
|
|
114411
114865
|
}
|
|
114412
114866
|
}
|
|
114413
|
-
var REMOTE_API_PREFIX = "/v1/remote", REMOTE_SCHEMA_VERSION = "1.0.0", REMOTE_ROUTE_METHODS, CHANNELS_ROUTE_METHODS, MAX_REMOTE_BODY_BYTES, MAX_PROJECT_CHARS = 256, MAX_APPROVAL_PROMPT_CHARS = 3000, MAX_KEYBOARD_ROWS = 8, MAX_KEYBOARD_BUTTONS_PER_ROW = 8, MAX_BUTTON_TEXT_CHARS = 64, MAX_CALLBACK_DATA_BYTES = 64, DEFAULT_APPROVAL_TIMEOUT_MS, MAX_APPROVAL_TIMEOUT_MS, MIN_APPROVAL_TIMEOUT_MS = 100, DEFAULT_KEEPALIVE_MS = 15000, DEFAULT_ACK_TIMEOUT_MS = 30000, SESSION_ID_PATTERN, APPROVAL_CALLBACK_PATTERN, RESERVED_CALLBACK_PREFIX = "ap:", CHOICE_CALLBACK_PATTERN, RESERVED_CHOICE_PREFIX = "pk:", MAX_PROMPT_BUTTONS = 24, MESSAGE_STATES, SSE_KEEPALIVE_FRAME = `: keepalive
|
|
114867
|
+
var REMOTE_API_PREFIX = "/v1/remote", REMOTE_SCHEMA_VERSION = "1.0.0", REMOTE_ROUTE_METHODS, CHANNELS_ROUTE_METHODS, MAX_REMOTE_BODY_BYTES, MAX_PROJECT_CHARS = 256, MAX_APPROVAL_PROMPT_CHARS = 3000, MAX_KEYBOARD_ROWS = 8, MAX_KEYBOARD_BUTTONS_PER_ROW = 8, MAX_BUTTON_TEXT_CHARS = 64, MAX_CALLBACK_DATA_BYTES = 64, DEFAULT_APPROVAL_TIMEOUT_MS, MAX_APPROVAL_TIMEOUT_MS, MIN_APPROVAL_TIMEOUT_MS = 100, DEFAULT_APPROVAL_ACK_MS = 5000, DEFAULT_KEEPALIVE_MS = 15000, DEFAULT_ACK_TIMEOUT_MS = 30000, SESSION_ID_PATTERN, APPROVAL_ID_PATTERN, APPROVAL_CALLBACK_PATTERN, RESERVED_CALLBACK_PREFIX = "ap:", CHOICE_CALLBACK_PATTERN, RESERVED_CHOICE_PREFIX = "pk:", MAX_PROMPT_BUTTONS = 24, MESSAGE_STATES, SSE_KEEPALIVE_FRAME = `: keepalive
|
|
114414
114868
|
|
|
114415
114869
|
`;
|
|
114416
114870
|
var init_protocol2 = __esm(() => {
|
|
@@ -114423,6 +114877,7 @@ var init_protocol2 = __esm(() => {
|
|
|
114423
114877
|
prompt: "POST",
|
|
114424
114878
|
state: "POST",
|
|
114425
114879
|
ack: "POST",
|
|
114880
|
+
"approval-ack": "POST",
|
|
114426
114881
|
stream: "GET"
|
|
114427
114882
|
};
|
|
114428
114883
|
CHANNELS_ROUTE_METHODS = {
|
|
@@ -114438,6 +114893,7 @@ var init_protocol2 = __esm(() => {
|
|
|
114438
114893
|
DEFAULT_APPROVAL_TIMEOUT_MS = 5 * 60000;
|
|
114439
114894
|
MAX_APPROVAL_TIMEOUT_MS = 60 * 60000;
|
|
114440
114895
|
SESSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
114896
|
+
APPROVAL_ID_PATTERN = /^ap[0-9a-f]{12}$/;
|
|
114441
114897
|
APPROVAL_CALLBACK_PATTERN = /^ap:(ap[0-9a-f]{12}):(allow|deny)$/;
|
|
114442
114898
|
CHOICE_CALLBACK_PATTERN = /^pk:(pk[0-9a-f]{12}):(\d{1,3})$/;
|
|
114443
114899
|
MESSAGE_STATES = ["reading", "working", "done", "failed"];
|
|
@@ -114473,7 +114929,7 @@ var init_endpoint = __esm(() => {
|
|
|
114473
114929
|
});
|
|
114474
114930
|
|
|
114475
114931
|
// src/lib/serve-credential.ts
|
|
114476
|
-
import { createHash as createHash63, randomBytes as randomBytes11, randomUUID as
|
|
114932
|
+
import { createHash as createHash63, randomBytes as randomBytes11, randomUUID as randomUUID33 } from "crypto";
|
|
114477
114933
|
import {
|
|
114478
114934
|
chmodSync as chmodSync5,
|
|
114479
114935
|
closeSync as closeSync5,
|
|
@@ -114557,7 +115013,7 @@ function readServeCredential(dir) {
|
|
|
114557
115013
|
}
|
|
114558
115014
|
function writeStore(store, dir) {
|
|
114559
115015
|
const file = serveCredentialPath(dir);
|
|
114560
|
-
const temp = `${file}.${
|
|
115016
|
+
const temp = `${file}.${randomUUID33()}.tmp`;
|
|
114561
115017
|
try {
|
|
114562
115018
|
ensureKeryxConfigDir(dir);
|
|
114563
115019
|
const handle = openSync5(temp, "wx", 384);
|
|
@@ -114597,7 +115053,7 @@ function mintRecord(now) {
|
|
|
114597
115053
|
const salt = randomBytes11(32).toString("hex");
|
|
114598
115054
|
return {
|
|
114599
115055
|
token,
|
|
114600
|
-
record: { id:
|
|
115056
|
+
record: { id: randomUUID33(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
114601
115057
|
};
|
|
114602
115058
|
}
|
|
114603
115059
|
function issueServeToken(dir, now = () => new Date().toISOString(), onWaiting) {
|
|
@@ -114802,6 +115258,10 @@ class RemoteClient {
|
|
|
114802
115258
|
inboundChain = Promise.resolve();
|
|
114803
115259
|
waiters = new Map;
|
|
114804
115260
|
earlyDecisions = new Map;
|
|
115261
|
+
seenApprovals = new Map;
|
|
115262
|
+
earlyDecisionMs;
|
|
115263
|
+
unackedApprovals = new Map;
|
|
115264
|
+
lingerTimers = new Set;
|
|
114805
115265
|
choiceWaiters = new Map;
|
|
114806
115266
|
earlyChoices = new Map;
|
|
114807
115267
|
firstResult;
|
|
@@ -114818,10 +115278,20 @@ class RemoteClient {
|
|
|
114818
115278
|
this.sleep = options.sleep ?? abortableSleep2;
|
|
114819
115279
|
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
114820
115280
|
this.isAlive = options.isAlive ?? ownProcessIsAlive;
|
|
115281
|
+
this.earlyDecisionMs = options.earlyDecisionMs ?? DEFAULT_EARLY_DECISION_MS;
|
|
114821
115282
|
}
|
|
114822
115283
|
get connected() {
|
|
114823
115284
|
return this.streamOpen && !this.stopped;
|
|
114824
115285
|
}
|
|
115286
|
+
get unconfirmedApprovals() {
|
|
115287
|
+
const cutoff = Date.now() - UNCONFIRMED_APPROVAL_LINGER_MS;
|
|
115288
|
+
for (const [id, at] of [...this.unackedApprovals.entries()]) {
|
|
115289
|
+
if (at < cutoff) {
|
|
115290
|
+
this.unackedApprovals.delete(id);
|
|
115291
|
+
}
|
|
115292
|
+
}
|
|
115293
|
+
return this.unackedApprovals.size;
|
|
115294
|
+
}
|
|
114825
115295
|
start() {
|
|
114826
115296
|
if (this.started) {
|
|
114827
115297
|
return Promise.resolve({ ok: false, code: "already-started", message: "this client was already started", retrying: false });
|
|
@@ -114872,6 +115342,10 @@ class RemoteClient {
|
|
|
114872
115342
|
}
|
|
114873
115343
|
this.lifetime.abort();
|
|
114874
115344
|
this.streamAbort?.abort();
|
|
115345
|
+
for (const timer of this.lingerTimers) {
|
|
115346
|
+
clearTimeout(timer);
|
|
115347
|
+
}
|
|
115348
|
+
this.lingerTimers.clear();
|
|
114875
115349
|
this.failApprovals();
|
|
114876
115350
|
this.firstResult?.({ ok: false, code: "stopped", message: "the client was stopped before it connected", retrying: false });
|
|
114877
115351
|
this.firstResult = undefined;
|
|
@@ -114905,8 +115379,10 @@ class RemoteClient {
|
|
|
114905
115379
|
}
|
|
114906
115380
|
const early = this.earlyDecisions.get(approvalId);
|
|
114907
115381
|
if (early !== undefined) {
|
|
115382
|
+
clearTimeout(early.timer);
|
|
114908
115383
|
this.earlyDecisions.delete(approvalId);
|
|
114909
|
-
|
|
115384
|
+
this.settleApproval(approvalId, early.decision, true);
|
|
115385
|
+
return early.decision;
|
|
114910
115386
|
}
|
|
114911
115387
|
if (!this.connected) {
|
|
114912
115388
|
return "deny";
|
|
@@ -115148,8 +115624,7 @@ class RemoteClient {
|
|
|
115148
115624
|
return;
|
|
115149
115625
|
});
|
|
115150
115626
|
} else if (event === "approval") {
|
|
115151
|
-
|
|
115152
|
-
this.resolveApproval(approval.approvalId, approval.decision);
|
|
115627
|
+
this.handleApprovalFrame(parsed);
|
|
115153
115628
|
} else if (event === "choice") {
|
|
115154
115629
|
const choice = parsed;
|
|
115155
115630
|
this.resolveChoice(choice.promptId, choice.index);
|
|
@@ -115194,16 +115669,115 @@ class RemoteClient {
|
|
|
115194
115669
|
await this.post("ack", { sessionId: this.options.sessionId, updateId });
|
|
115195
115670
|
} catch {}
|
|
115196
115671
|
}
|
|
115672
|
+
handleApprovalFrame(approval) {
|
|
115673
|
+
const approvalId = approval?.approvalId;
|
|
115674
|
+
if (!isApprovalId(approvalId) || approval.decision !== "allow" && approval.decision !== "deny") {
|
|
115675
|
+
return;
|
|
115676
|
+
}
|
|
115677
|
+
const decision = approval.decision;
|
|
115678
|
+
const seen = this.seenApprovals.get(approvalId);
|
|
115679
|
+
if (seen === "pending") {
|
|
115680
|
+
return;
|
|
115681
|
+
}
|
|
115682
|
+
this.countUnconfirmed(approvalId);
|
|
115683
|
+
if (seen !== undefined) {
|
|
115684
|
+
this.ackApproval(approvalId, seen === "applied");
|
|
115685
|
+
return;
|
|
115686
|
+
}
|
|
115687
|
+
if (this.waiters.has(approvalId)) {
|
|
115688
|
+
this.settleApproval(approvalId, decision, true, () => this.resolveApproval(approvalId, decision));
|
|
115689
|
+
return;
|
|
115690
|
+
}
|
|
115691
|
+
this.rememberSeen(approvalId, "pending");
|
|
115692
|
+
this.parkEarlyDecision(approvalId, decision);
|
|
115693
|
+
}
|
|
115694
|
+
rememberSeen(approvalId, outcome) {
|
|
115695
|
+
this.seenApprovals.delete(approvalId);
|
|
115696
|
+
this.seenApprovals.set(approvalId, outcome);
|
|
115697
|
+
if (this.seenApprovals.size > MAX_SEEN_APPROVALS) {
|
|
115698
|
+
const oldest = this.seenApprovals.keys().next().value;
|
|
115699
|
+
if (oldest !== undefined) {
|
|
115700
|
+
this.seenApprovals.delete(oldest);
|
|
115701
|
+
}
|
|
115702
|
+
}
|
|
115703
|
+
}
|
|
115704
|
+
countUnconfirmed(approvalId) {
|
|
115705
|
+
this.unackedApprovals.delete(approvalId);
|
|
115706
|
+
this.unackedApprovals.set(approvalId, Date.now());
|
|
115707
|
+
this.notifyUnconfirmed();
|
|
115708
|
+
while (this.unackedApprovals.size > MAX_UNCONFIRMED_APPROVALS) {
|
|
115709
|
+
const oldest = this.unackedApprovals.keys().next().value;
|
|
115710
|
+
if (oldest === undefined) {
|
|
115711
|
+
break;
|
|
115712
|
+
}
|
|
115713
|
+
this.unackedApprovals.delete(oldest);
|
|
115714
|
+
}
|
|
115715
|
+
}
|
|
115716
|
+
settleApproval(approvalId, decision, applied, apply) {
|
|
115717
|
+
this.rememberSeen(approvalId, applied ? "applied" : "not-applied");
|
|
115718
|
+
try {
|
|
115719
|
+
this.options.onApprovalFrame?.({ approvalId, decision, applied });
|
|
115720
|
+
} catch {}
|
|
115721
|
+
apply?.();
|
|
115722
|
+
this.ackApproval(approvalId, applied);
|
|
115723
|
+
}
|
|
115724
|
+
parkEarlyDecision(approvalId, decision) {
|
|
115725
|
+
while (this.earlyDecisions.size >= MAX_EARLY_DECISIONS) {
|
|
115726
|
+
const oldest = this.earlyDecisions.keys().next().value;
|
|
115727
|
+
if (oldest === undefined) {
|
|
115728
|
+
break;
|
|
115729
|
+
}
|
|
115730
|
+
this.dropEarlyDecision(oldest);
|
|
115731
|
+
}
|
|
115732
|
+
const timer = setTimeout(() => this.dropEarlyDecision(approvalId), this.earlyDecisionMs);
|
|
115733
|
+
timer.unref?.();
|
|
115734
|
+
this.earlyDecisions.set(approvalId, { decision, timer });
|
|
115735
|
+
}
|
|
115736
|
+
dropEarlyDecision(approvalId) {
|
|
115737
|
+
const early = this.earlyDecisions.get(approvalId);
|
|
115738
|
+
if (early === undefined) {
|
|
115739
|
+
return;
|
|
115740
|
+
}
|
|
115741
|
+
clearTimeout(early.timer);
|
|
115742
|
+
this.earlyDecisions.delete(approvalId);
|
|
115743
|
+
if (!this.stopped) {
|
|
115744
|
+
this.settleApproval(approvalId, early.decision, false);
|
|
115745
|
+
}
|
|
115746
|
+
}
|
|
115747
|
+
async ackApproval(approvalId, applied) {
|
|
115748
|
+
let confirmed = false;
|
|
115749
|
+
try {
|
|
115750
|
+
const response = await this.post("approval-ack", { sessionId: this.options.sessionId, approvalId, applied });
|
|
115751
|
+
confirmed = response.ok;
|
|
115752
|
+
} catch {}
|
|
115753
|
+
if (this.stopped) {
|
|
115754
|
+
return;
|
|
115755
|
+
}
|
|
115756
|
+
if (confirmed) {
|
|
115757
|
+
this.unackedApprovals.delete(approvalId);
|
|
115758
|
+
this.notifyUnconfirmed();
|
|
115759
|
+
return;
|
|
115760
|
+
}
|
|
115761
|
+
this.lingerTimers.add(this.scheduleUnref(() => {
|
|
115762
|
+
this.notifyUnconfirmed();
|
|
115763
|
+
}, UNCONFIRMED_APPROVAL_LINGER_MS + 50));
|
|
115764
|
+
}
|
|
115765
|
+
notifyUnconfirmed() {
|
|
115766
|
+
try {
|
|
115767
|
+
this.options.onUnconfirmedChange?.();
|
|
115768
|
+
} catch {}
|
|
115769
|
+
}
|
|
115770
|
+
scheduleUnref(fn, ms) {
|
|
115771
|
+
const timer = setTimeout(() => {
|
|
115772
|
+
this.lingerTimers.delete(timer);
|
|
115773
|
+
fn();
|
|
115774
|
+
}, ms);
|
|
115775
|
+
timer.unref?.();
|
|
115776
|
+
return timer;
|
|
115777
|
+
}
|
|
115197
115778
|
resolveApproval(approvalId, decision) {
|
|
115198
115779
|
const waiter = this.waiters.get(approvalId);
|
|
115199
115780
|
if (waiter === undefined) {
|
|
115200
|
-
if (this.earlyDecisions.size >= MAX_EARLY_DECISIONS) {
|
|
115201
|
-
const oldest = this.earlyDecisions.keys().next().value;
|
|
115202
|
-
if (oldest !== undefined) {
|
|
115203
|
-
this.earlyDecisions.delete(oldest);
|
|
115204
|
-
}
|
|
115205
|
-
}
|
|
115206
|
-
this.earlyDecisions.set(approvalId, decision);
|
|
115207
115781
|
return;
|
|
115208
115782
|
}
|
|
115209
115783
|
clearTimeout(waiter.timer);
|
|
@@ -115240,11 +115814,13 @@ class RemoteClient {
|
|
|
115240
115814
|
this.waiters.delete(id);
|
|
115241
115815
|
waiter.resolve("deny");
|
|
115242
115816
|
}
|
|
115243
|
-
this.earlyDecisions.
|
|
115817
|
+
for (const id of [...this.earlyDecisions.keys()]) {
|
|
115818
|
+
this.dropEarlyDecision(id);
|
|
115819
|
+
}
|
|
115244
115820
|
this.failChoices();
|
|
115245
115821
|
}
|
|
115246
115822
|
}
|
|
115247
|
-
var UNVERIFIED_SERVE_MESSAGE = "the program answering on keryx serve's port did not prove it is the keryx serve this shell trusts; its answer was ignored. Restart `keryx serve` (an older serve cannot prove itself; update keryx on both sides)", FatalClientError, TransientClientError, DEFAULT_BACKOFF_INITIAL_MS = 250, DEFAULT_BACKOFF_MAX_MS = 15000, DEFAULT_REQUEST_TIMEOUT_MS = 1e4, MAX_EARLY_DECISIONS = 32, MAX_COMPLETED_IDS = 256;
|
|
115823
|
+
var UNVERIFIED_SERVE_MESSAGE = "the program answering on keryx serve's port did not prove it is the keryx serve this shell trusts; its answer was ignored. Restart `keryx serve` (an older serve cannot prove itself; update keryx on both sides)", FatalClientError, TransientClientError, DEFAULT_BACKOFF_INITIAL_MS = 250, DEFAULT_BACKOFF_MAX_MS = 15000, DEFAULT_REQUEST_TIMEOUT_MS = 1e4, MAX_EARLY_DECISIONS = 32, DEFAULT_EARLY_DECISION_MS = 2000, MAX_SEEN_APPROVALS = 256, UNCONFIRMED_APPROVAL_LINGER_MS = 60000, MAX_UNCONFIRMED_APPROVALS = 64, MAX_COMPLETED_IDS = 256;
|
|
115248
115824
|
var init_client3 = __esm(() => {
|
|
115249
115825
|
init_serve_config();
|
|
115250
115826
|
init_lease();
|
|
@@ -115924,10 +116500,12 @@ class RemoteBridge {
|
|
|
115924
116500
|
}
|
|
115925
116501
|
const online = this.clientConnected && client.connected;
|
|
115926
116502
|
const heartbeat = client.lastHeartbeatAt;
|
|
116503
|
+
const unconfirmed = client.unconfirmedApprovals ?? 0;
|
|
115927
116504
|
return {
|
|
115928
116505
|
state: online ? "on" : "offline",
|
|
115929
116506
|
...client.name !== undefined ? { name: client.name } : {},
|
|
115930
116507
|
...heartbeat !== undefined ? { heartbeatAgeMs: Math.max(0, this.now() - heartbeat) } : {},
|
|
116508
|
+
...unconfirmed > 0 ? { unconfirmedApprovals: unconfirmed } : {},
|
|
115931
116509
|
events: [...this.events]
|
|
115932
116510
|
};
|
|
115933
116511
|
}
|
|
@@ -115954,7 +116532,9 @@ class RemoteBridge {
|
|
|
115954
116532
|
...name !== undefined ? { name } : {},
|
|
115955
116533
|
...this.options.dir !== undefined ? { dir: this.options.dir } : {},
|
|
115956
116534
|
onLine: (text, meta) => this.accept(text, meta),
|
|
115957
|
-
onStatus: (status) => this.onStatus(status)
|
|
116535
|
+
onStatus: (status) => this.onStatus(status),
|
|
116536
|
+
onApprovalFrame: (frame) => this.host.notice(frame.applied ? `\u25C7 approval ${frame.approvalId} ${frame.decision === "allow" ? "allowed" : "denied"} from Telegram` : `\u25C7 approval ${frame.approvalId}: ${frame.decision === "allow" ? "an allow" : "a deny"} from Telegram arrived but nothing here was waiting for it; not applied`),
|
|
116537
|
+
onUnconfirmedChange: () => this.host.onChange?.()
|
|
115958
116538
|
};
|
|
115959
116539
|
const client = this.options.makeClient !== undefined ? this.options.makeClient(clientOptions) : new RemoteClient(clientOptions);
|
|
115960
116540
|
this.client = client;
|
|
@@ -116193,7 +116773,7 @@ function fileExists(file) {
|
|
|
116193
116773
|
const read = readConfigFile(file);
|
|
116194
116774
|
return read.ok || read.reason !== "absent";
|
|
116195
116775
|
}
|
|
116196
|
-
function
|
|
116776
|
+
function isObject3(value) {
|
|
116197
116777
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
116198
116778
|
}
|
|
116199
116779
|
function plural2(count, one, many) {
|
|
@@ -116220,7 +116800,7 @@ class ChannelsClient {
|
|
|
116220
116800
|
status() {
|
|
116221
116801
|
return this.call("channels-status", (body) => {
|
|
116222
116802
|
const telegram = body.telegram;
|
|
116223
|
-
return typeof body.machine === "string" &&
|
|
116803
|
+
return typeof body.machine === "string" && isObject3(telegram) && typeof telegram.state === "string" && typeof telegram.sessions === "number";
|
|
116224
116804
|
});
|
|
116225
116805
|
}
|
|
116226
116806
|
async startPairing(token) {
|
|
@@ -116389,12 +116969,12 @@ class ChannelsClient {
|
|
|
116389
116969
|
parsed = undefined;
|
|
116390
116970
|
}
|
|
116391
116971
|
if (!response.ok) {
|
|
116392
|
-
const error =
|
|
116972
|
+
const error = isObject3(parsed) && isObject3(parsed.error) ? parsed.error : undefined;
|
|
116393
116973
|
const code = typeof error?.code === "string" ? error.code : `http-${response.status}`;
|
|
116394
116974
|
const message = typeof error?.message === "string" ? error.message : `keryx serve refused the request (HTTP ${response.status})`;
|
|
116395
116975
|
return { ok: false, code, reason: message };
|
|
116396
116976
|
}
|
|
116397
|
-
if (!
|
|
116977
|
+
if (!isObject3(parsed) || !guard(parsed)) {
|
|
116398
116978
|
return { ok: false, code: "unexpected-response", reason: "keryx serve sent an answer this shell does not understand; update keryx on both sides" };
|
|
116399
116979
|
}
|
|
116400
116980
|
return { ok: true, value: parsed };
|
|
@@ -116414,7 +116994,7 @@ class ChannelsClient {
|
|
|
116414
116994
|
}
|
|
116415
116995
|
function refusedConnection(error) {
|
|
116416
116996
|
const seen = new Set;
|
|
116417
|
-
for (let current = error;
|
|
116997
|
+
for (let current = error;isObject3(current) || current instanceof Error; ) {
|
|
116418
116998
|
if (seen.has(current))
|
|
116419
116999
|
break;
|
|
116420
117000
|
seen.add(current);
|
|
@@ -117583,6 +118163,10 @@ function formatRemoteStatusLines(status) {
|
|
|
117583
118163
|
lines.push(`Last heartbeat: ${status.heartbeatAgeMs === undefined ? "none yet" : `${formatAge(status.heartbeatAgeMs)} ago`}`);
|
|
117584
118164
|
if (status.state === "offline")
|
|
117585
118165
|
lines.push("Serve is not reachable. The shell keeps retrying; nothing is lost from Telegram.");
|
|
118166
|
+
if (status.unconfirmedApprovals !== undefined && status.unconfirmedApprovals > 0) {
|
|
118167
|
+
const n = status.unconfirmedApprovals;
|
|
118168
|
+
lines.push(`Approvals not confirmed: ${n}. The topic shows ${n === 1 ? "it" : "them"} as "not confirmed" until serve takes the shell's ack.`);
|
|
118169
|
+
}
|
|
117586
118170
|
lines.push(`Turn it off with ${REMOTE_CONTROL_COMMAND} off, or press o here. Rename: ${REMOTE_CONTROL_COMMAND} off, then ${REMOTE_CONTROL_COMMAND} <name>.`);
|
|
117587
118171
|
return lines;
|
|
117588
118172
|
}
|
|
@@ -117703,7 +118287,11 @@ function projectRemoteRow(status, width) {
|
|
|
117703
118287
|
return { text: fit3("off", width), role: "muted", action: "open" };
|
|
117704
118288
|
if (status.state === "offline")
|
|
117705
118289
|
return { text: fit3("offline", width), role: "attention", action: "open" };
|
|
117706
|
-
|
|
118290
|
+
const topic = status.name ?? "connecting";
|
|
118291
|
+
const unconfirmed = status.unconfirmedApprovals ?? 0;
|
|
118292
|
+
if (unconfirmed > 0)
|
|
118293
|
+
return { text: fit3(`${topic} \xB7 ${unconfirmed} unconfirmed`, width), role: "attention", action: "open" };
|
|
118294
|
+
return { text: fit3(topic, width), role: "ok", action: "open" };
|
|
117707
118295
|
}
|
|
117708
118296
|
function mountRemotePanel(otui, renderer, parent, options) {
|
|
117709
118297
|
const core = otui;
|
|
@@ -120921,10 +121509,10 @@ var init_serve_turn_store = __esm(() => {
|
|
|
120921
121509
|
});
|
|
120922
121510
|
|
|
120923
121511
|
// src/lib/serve-approvals-store.ts
|
|
120924
|
-
import { randomUUID as
|
|
121512
|
+
import { randomUUID as randomUUID34 } from "crypto";
|
|
120925
121513
|
import { existsSync as existsSync45, readdirSync as readdirSync10 } from "fs";
|
|
120926
121514
|
import path261 from "path";
|
|
120927
|
-
function
|
|
121515
|
+
function isApprovalId2(value) {
|
|
120928
121516
|
return ID_PATTERN2.test(value);
|
|
120929
121517
|
}
|
|
120930
121518
|
function root(dir) {
|
|
@@ -120950,7 +121538,7 @@ function appendEvidence(event, dir) {
|
|
|
120950
121538
|
appendOwnerOnlyLine(ledgerFile(dir), JSON.stringify(event));
|
|
120951
121539
|
}
|
|
120952
121540
|
function createApproval(input, dir, now = new Date) {
|
|
120953
|
-
if (!
|
|
121541
|
+
if (!isApprovalId2(input.approvalId)) {
|
|
120954
121542
|
throw new Error("approvalId is not an approval id");
|
|
120955
121543
|
}
|
|
120956
121544
|
ensureKeryxSubdir(["approvals"], dir);
|
|
@@ -121017,7 +121605,7 @@ function parseResolution(text) {
|
|
|
121017
121605
|
}
|
|
121018
121606
|
}
|
|
121019
121607
|
function readRaw(id, dir) {
|
|
121020
|
-
if (!
|
|
121608
|
+
if (!isApprovalId2(id)) {
|
|
121021
121609
|
return { ok: false, reason: "not-an-approval-id" };
|
|
121022
121610
|
}
|
|
121023
121611
|
const read = readConfigFile(requestFile(id, dir));
|
|
@@ -121121,7 +121709,7 @@ function approvalIds(dir) {
|
|
|
121121
121709
|
if (!existsSync45(base)) {
|
|
121122
121710
|
return [];
|
|
121123
121711
|
}
|
|
121124
|
-
return readdirSync10(base).filter((name) => name.endsWith(".json") && !name.endsWith(".resolution.json")).map((name) => name.slice(0, -".json".length)).filter(
|
|
121712
|
+
return readdirSync10(base).filter((name) => name.endsWith(".json") && !name.endsWith(".resolution.json")).map((name) => name.slice(0, -".json".length)).filter(isApprovalId2);
|
|
121125
121713
|
}
|
|
121126
121714
|
function listApprovals(dir, options = {}) {
|
|
121127
121715
|
const views = [];
|
|
@@ -121197,7 +121785,7 @@ function toPublicApproval(view) {
|
|
|
121197
121785
|
};
|
|
121198
121786
|
}
|
|
121199
121787
|
function newApprovalId() {
|
|
121200
|
-
return
|
|
121788
|
+
return randomUUID34();
|
|
121201
121789
|
}
|
|
121202
121790
|
var APPROVAL_SCHEMA_VERSION = "1.0.0", SUMMARY_MAX = 500, SCOPE_MAX = 300, CONSEQUENCE_MAX = 300, ID_PATTERN2;
|
|
121203
121791
|
var init_serve_approvals_store = __esm(() => {
|
|
@@ -121718,10 +122306,12 @@ async function openDecisions(otui, chrome, options) {
|
|
|
121718
122306
|
const text = await reportText(cwd).catch(() => UNREADABLE);
|
|
121719
122307
|
return presentDecisions((hostOtui, hostChrome, input) => openModal(hostOtui, hostChrome, input), otui, chrome, { ...rest, text });
|
|
121720
122308
|
}
|
|
121721
|
-
function projectDecisionsPanel(count, width) {
|
|
121722
|
-
if (count <= 0)
|
|
122309
|
+
function projectDecisionsPanel(count, width, backfilled = 0) {
|
|
122310
|
+
if (count <= 0 && backfilled <= 0)
|
|
121723
122311
|
return { visible: false, text: "" };
|
|
121724
|
-
const
|
|
122312
|
+
const live = count > 0 ? `${count} decision${count === 1 ? "" : "s"}` : "";
|
|
122313
|
+
const before = backfilled > 0 ? `${count > 0 ? " + " : ""}${backfilled} before` : "";
|
|
122314
|
+
const label = `${live}${before}`;
|
|
121725
122315
|
const candidates = [`${label} \xB7 /decisions`, label];
|
|
121726
122316
|
const text = candidates.find((candidate) => candidate.length <= width) ?? label.slice(0, Math.max(1, width));
|
|
121727
122317
|
return { visible: true, text };
|
|
@@ -121744,6 +122334,7 @@ function mountDecisionsSidebar(options) {
|
|
|
121744
122334
|
const { chrome } = options;
|
|
121745
122335
|
const r = chrome.renderer;
|
|
121746
122336
|
const count = options.count ?? (() => decisionCount(options.cwd));
|
|
122337
|
+
const backfilledTotal = options.backfilled ?? (() => backfilledCount(options.cwd));
|
|
121747
122338
|
const box = new core.BoxRenderable(r, { id: "sb-decisions", flexDirection: "column", flexShrink: 0 });
|
|
121748
122339
|
options.parent.add(box);
|
|
121749
122340
|
let modal;
|
|
@@ -121789,7 +122380,13 @@ function mountDecisionsSidebar(options) {
|
|
|
121789
122380
|
} catch {
|
|
121790
122381
|
n = 0;
|
|
121791
122382
|
}
|
|
121792
|
-
|
|
122383
|
+
let before;
|
|
122384
|
+
try {
|
|
122385
|
+
before = await backfilledTotal();
|
|
122386
|
+
} catch {
|
|
122387
|
+
before = 0;
|
|
122388
|
+
}
|
|
122389
|
+
projected = projectDecisionsPanel(n, options.width, before);
|
|
121793
122390
|
paint();
|
|
121794
122391
|
};
|
|
121795
122392
|
const unsubscribeTheme = onThemeChange(guardedThemeRepaint("decisions-row", () => paint(true), () => disposed || isRenderableGone(box)));
|
|
@@ -121837,8 +122434,8 @@ var init_decisions_surface = __esm(() => {
|
|
|
121837
122434
|
|
|
121838
122435
|
// src/harness/external/write-run.ts
|
|
121839
122436
|
import { execFile as execFile11 } from "child_process";
|
|
121840
|
-
import { createHash as createHash66, randomUUID as
|
|
121841
|
-
import { mkdtemp as mkdtemp5, readlink, realpath as realpath19, rm as
|
|
122437
|
+
import { createHash as createHash66, randomUUID as randomUUID35 } from "crypto";
|
|
122438
|
+
import { mkdtemp as mkdtemp5, readlink, realpath as realpath19, rm as rm18 } from "fs/promises";
|
|
121842
122439
|
import { tmpdir as tmpdir11 } from "os";
|
|
121843
122440
|
import path262 from "path";
|
|
121844
122441
|
import { promisify as promisify9 } from "util";
|
|
@@ -121957,7 +122554,7 @@ async function captureWriteDiff(input) {
|
|
|
121957
122554
|
refusedPaths
|
|
121958
122555
|
};
|
|
121959
122556
|
} finally {
|
|
121960
|
-
await
|
|
122557
|
+
await rm18(indexDir, { recursive: true, force: true }).catch(() => {
|
|
121961
122558
|
return;
|
|
121962
122559
|
});
|
|
121963
122560
|
}
|
|
@@ -122031,7 +122628,7 @@ async function runExternalWriteChild(input, deps) {
|
|
|
122031
122628
|
});
|
|
122032
122629
|
}
|
|
122033
122630
|
if (ownedDir !== undefined)
|
|
122034
|
-
await
|
|
122631
|
+
await rm18(ownedDir, { recursive: true, force: true }).catch(() => {
|
|
122035
122632
|
return;
|
|
122036
122633
|
});
|
|
122037
122634
|
}
|
|
@@ -122056,7 +122653,7 @@ async function runExternalWriteChild(input, deps) {
|
|
|
122056
122653
|
}
|
|
122057
122654
|
}
|
|
122058
122655
|
function persistWriteRun(args) {
|
|
122059
|
-
const runId =
|
|
122656
|
+
const runId = randomUUID35();
|
|
122060
122657
|
const title = `External write ${args.agentId}: ${args.runStatus}`;
|
|
122061
122658
|
const handle = createSession({
|
|
122062
122659
|
cwd: args.projectRoot,
|
|
@@ -122172,7 +122769,7 @@ var init_write_run = __esm(() => {
|
|
|
122172
122769
|
import { execFile as execFile12 } from "child_process";
|
|
122173
122770
|
import { createHash as createHash67 } from "crypto";
|
|
122174
122771
|
import { existsSync as existsSync46, rmSync as rmSync5 } from "fs";
|
|
122175
|
-
import { mkdtemp as mkdtemp6, rm as
|
|
122772
|
+
import { mkdtemp as mkdtemp6, rm as rm19 } from "fs/promises";
|
|
122176
122773
|
import { tmpdir as tmpdir12 } from "os";
|
|
122177
122774
|
import path263 from "path";
|
|
122178
122775
|
function errorMessage3(error) {
|
|
@@ -122292,11 +122889,11 @@ async function landWriteRun(input, deps = {}) {
|
|
|
122292
122889
|
return;
|
|
122293
122890
|
});
|
|
122294
122891
|
if (ownedDir !== undefined)
|
|
122295
|
-
await
|
|
122892
|
+
await rm19(ownedDir, { recursive: true, force: true }).catch(() => {
|
|
122296
122893
|
return;
|
|
122297
122894
|
});
|
|
122298
122895
|
else
|
|
122299
|
-
await
|
|
122896
|
+
await rm19(path263.join(worktreesDir, worktreeId), { recursive: true, force: true }).catch(() => {
|
|
122300
122897
|
return;
|
|
122301
122898
|
});
|
|
122302
122899
|
await git(["worktree", "prune"], repo).catch(() => {
|
|
@@ -126285,7 +126882,7 @@ function recordTaskCost(store, record) {
|
|
|
126285
126882
|
const next = existing.length >= MAX_SAMPLES_PER_KEY ? [...existing.slice(existing.length - MAX_SAMPLES_PER_KEY + 1), record] : [...existing, record];
|
|
126286
126883
|
return { ...store, [key]: next };
|
|
126287
126884
|
}
|
|
126288
|
-
function
|
|
126885
|
+
function median2(values) {
|
|
126289
126886
|
if (values.length === 0)
|
|
126290
126887
|
return 0;
|
|
126291
126888
|
const sorted = [...values].sort((a, b) => a - b);
|
|
@@ -126294,9 +126891,9 @@ function median(values) {
|
|
|
126294
126891
|
}
|
|
126295
126892
|
function statsForKey(records, providerId, modelId, category) {
|
|
126296
126893
|
const n = records.length;
|
|
126297
|
-
const medianTokens =
|
|
126894
|
+
const medianTokens = median2(records.map((r) => r.totalTokens));
|
|
126298
126895
|
const knownCosts = records.map((r) => r.costUsd).filter((c) => c !== undefined);
|
|
126299
|
-
const medianCostUsd = knownCosts.length > 0 ?
|
|
126896
|
+
const medianCostUsd = knownCosts.length > 0 ? median2(knownCosts) : undefined;
|
|
126300
126897
|
const successRate = records.filter((r) => r.success).length / n;
|
|
126301
126898
|
return { providerId, modelId, category, n, medianTokens, ...medianCostUsd !== undefined ? { medianCostUsd } : {}, successRate };
|
|
126302
126899
|
}
|
|
@@ -128677,7 +129274,7 @@ var init_conform_report = __esm(() => {
|
|
|
128677
129274
|
});
|
|
128678
129275
|
|
|
128679
129276
|
// src/tui/conform-source.ts
|
|
128680
|
-
import { readFile as readFile127, readdir as readdir41, stat as
|
|
129277
|
+
import { readFile as readFile127, readdir as readdir41, stat as stat27 } from "fs/promises";
|
|
128681
129278
|
import path270 from "path";
|
|
128682
129279
|
async function readRecentConformDocs(cwd) {
|
|
128683
129280
|
try {
|
|
@@ -128720,7 +129317,7 @@ async function mostRecentReviewPackage(cwd) {
|
|
|
128720
129317
|
continue;
|
|
128721
129318
|
const dir = path270.join(reviewsDir, reviewEntry.name);
|
|
128722
129319
|
try {
|
|
128723
|
-
const info = await
|
|
129320
|
+
const info = await stat27(path270.join(dir, "report.md"));
|
|
128724
129321
|
if (best === undefined || info.mtimeMs > best.mtimeMs)
|
|
128725
129322
|
best = { dir, mtimeMs: info.mtimeMs };
|
|
128726
129323
|
} catch {}
|
|
@@ -129687,7 +130284,7 @@ var init_jev_rules = __esm(() => {
|
|
|
129687
130284
|
});
|
|
129688
130285
|
|
|
129689
130286
|
// src/commands/review-jev-rules.ts
|
|
129690
|
-
import { readFile as readFile130, readdir as readdir42, stat as
|
|
130287
|
+
import { readFile as readFile130, readdir as readdir42, stat as stat28 } from "fs/promises";
|
|
129691
130288
|
import { basename as basename3, dirname as dirname2, isAbsolute as isAbsolute3, join as join5, relative as relative3 } from "path";
|
|
129692
130289
|
function rejectUnknownJevRulesFlags(args) {
|
|
129693
130290
|
const unknown = args.filter((arg) => arg.startsWith("--") && !JEV_RULES_FLAGS.includes(arg.split("=")[0]));
|
|
@@ -129800,7 +130397,7 @@ async function discoverRuleSources(cwd, explicitPaths) {
|
|
|
129800
130397
|
const explicitDirSources = [];
|
|
129801
130398
|
for (const raw of explicitPaths) {
|
|
129802
130399
|
const resolved = isAbsolute3(raw) ? raw : join5(cwd, raw);
|
|
129803
|
-
const stats = await
|
|
130400
|
+
const stats = await stat28(resolved).catch(() => {
|
|
129804
130401
|
return;
|
|
129805
130402
|
});
|
|
129806
130403
|
if (stats === undefined) {
|
|
@@ -131677,7 +132274,7 @@ var init_jev_triage = __esm(() => {
|
|
|
131677
132274
|
});
|
|
131678
132275
|
|
|
131679
132276
|
// src/commands/review-jev-triage.ts
|
|
131680
|
-
import { readFile as readFile136, stat as
|
|
132277
|
+
import { readFile as readFile136, stat as stat29 } from "fs/promises";
|
|
131681
132278
|
import path276 from "path";
|
|
131682
132279
|
function rejectUnknownJevTriageFlags(args) {
|
|
131683
132280
|
const unknown = args.filter((arg) => arg.startsWith("--") && !JEV_TRIAGE_FLAGS.includes(arg.split("=")[0]));
|
|
@@ -131712,7 +132309,7 @@ function isMaxTokensExceededError2(error) {
|
|
|
131712
132309
|
async function loadReportRaw(reportPath) {
|
|
131713
132310
|
let filePath = reportPath;
|
|
131714
132311
|
try {
|
|
131715
|
-
const stats = await
|
|
132312
|
+
const stats = await stat29(reportPath);
|
|
131716
132313
|
if (stats.isDirectory())
|
|
131717
132314
|
filePath = path276.join(reportPath, "findings.json");
|
|
131718
132315
|
} catch {}
|
|
@@ -131857,7 +132454,7 @@ var init_review_jev_triage = __esm(() => {
|
|
|
131857
132454
|
});
|
|
131858
132455
|
|
|
131859
132456
|
// src/tui/jev-triage-command.ts
|
|
131860
|
-
import { readdir as readdir43, readFile as readFile137, stat as
|
|
132457
|
+
import { readdir as readdir43, readFile as readFile137, stat as stat30 } from "fs/promises";
|
|
131861
132458
|
import path277 from "path";
|
|
131862
132459
|
function isJevTriageCommand(name) {
|
|
131863
132460
|
return name === JEV_TRIAGE_COMMAND;
|
|
@@ -131886,7 +132483,7 @@ async function resolveLatestReviewPackage(cwd) {
|
|
|
131886
132483
|
const packageDir = path277.join(reviewsDir, reviewName);
|
|
131887
132484
|
const findingsPath = path277.join(packageDir, "findings.json");
|
|
131888
132485
|
try {
|
|
131889
|
-
const stats = await
|
|
132486
|
+
const stats = await stat30(findingsPath);
|
|
131890
132487
|
candidates.push({ dir: packageDir, mtimeMs: stats.mtimeMs });
|
|
131891
132488
|
} catch {
|
|
131892
132489
|
continue;
|
|
@@ -139215,7 +139812,7 @@ function maxOverFiles(files, values) {
|
|
|
139215
139812
|
}
|
|
139216
139813
|
|
|
139217
139814
|
// src/wiki/deep-enrich.ts
|
|
139218
|
-
import { randomUUID as
|
|
139815
|
+
import { randomUUID as randomUUID36, createHash as createHash68 } from "crypto";
|
|
139219
139816
|
function errorMessage4(cause) {
|
|
139220
139817
|
return cause instanceof Error ? cause.message : String(cause);
|
|
139221
139818
|
}
|
|
@@ -139251,7 +139848,7 @@ function buildDeepUserPrompt(page, original, extra) {
|
|
|
139251
139848
|
`);
|
|
139252
139849
|
}
|
|
139253
139850
|
async function enrichPageDeep(input) {
|
|
139254
|
-
const idSeq = input.idSeq ?? (() =>
|
|
139851
|
+
const idSeq = input.idSeq ?? (() => randomUUID36());
|
|
139255
139852
|
const clock = input.clock ?? (() => new Date().toISOString());
|
|
139256
139853
|
const toolCalls = [];
|
|
139257
139854
|
if (input.signal?.aborted) {
|
|
@@ -141490,6 +142087,98 @@ function drainIdleMainQueue(items, options) {
|
|
|
141490
142087
|
return true;
|
|
141491
142088
|
}
|
|
141492
142089
|
|
|
142090
|
+
// src/tui/remote-queue-wiring.ts
|
|
142091
|
+
function createRemoteQueueWiring(deps) {
|
|
142092
|
+
let pendingEdit;
|
|
142093
|
+
return {
|
|
142094
|
+
removeMainQueue(index) {
|
|
142095
|
+
const queue = deps.getQueue();
|
|
142096
|
+
if (index < 0 || index >= queue.length)
|
|
142097
|
+
return;
|
|
142098
|
+
const removed = queue[index];
|
|
142099
|
+
if (removed?.source === TG_SOURCE) {
|
|
142100
|
+
deps.getBridge()?.queuedLineRemoved(removed.question);
|
|
142101
|
+
}
|
|
142102
|
+
deps.setQueue(removeMainQueueItem(queue, index));
|
|
142103
|
+
deps.paint();
|
|
142104
|
+
},
|
|
142105
|
+
editMainQueue(index) {
|
|
142106
|
+
const edited = editMainQueueItem(deps.getQueue(), index);
|
|
142107
|
+
if (edited === undefined)
|
|
142108
|
+
return;
|
|
142109
|
+
deps.setQueue(edited.rest);
|
|
142110
|
+
pendingEdit = pendingQueueEditFor(edited.removed, index);
|
|
142111
|
+
deps.composer.setText(edited.text);
|
|
142112
|
+
deps.composer.focus();
|
|
142113
|
+
deps.paint();
|
|
142114
|
+
},
|
|
142115
|
+
requeuePendingEdit(line, displayLine) {
|
|
142116
|
+
if (pendingEdit === undefined)
|
|
142117
|
+
return false;
|
|
142118
|
+
const edit = pendingEdit;
|
|
142119
|
+
pendingEdit = undefined;
|
|
142120
|
+
deps.setQueue(reinsertEditedMainQueueItem(deps.getQueue(), edit, line, displayLine));
|
|
142121
|
+
deps.paint();
|
|
142122
|
+
return true;
|
|
142123
|
+
},
|
|
142124
|
+
hasPendingEdit: () => pendingEdit !== undefined,
|
|
142125
|
+
stopRemoteForSessionSwitch() {
|
|
142126
|
+
const bridge = deps.getBridge();
|
|
142127
|
+
if (bridge?.keepingTopic === true) {
|
|
142128
|
+
bridge.sessionLeaving();
|
|
142129
|
+
return;
|
|
142130
|
+
}
|
|
142131
|
+
if (bridge?.active !== true)
|
|
142132
|
+
return;
|
|
142133
|
+
const closing = bridge.disable();
|
|
142134
|
+
deps.say(`\u25C7 remote control is turning off: the session changed, and its Telegram topic is being deleted. Turn it on again with /remote-control <name>.
|
|
142135
|
+
`);
|
|
142136
|
+
closing.then(() => deps.say(`\u25C7 remote control off: the topic is deleted.
|
|
142137
|
+
`));
|
|
142138
|
+
},
|
|
142139
|
+
dropQueuedTelegramLines() {
|
|
142140
|
+
const { kept, dropped } = dropQueuedBySource(deps.getQueue(), TG_SOURCE);
|
|
142141
|
+
if (dropped.length === 0)
|
|
142142
|
+
return [];
|
|
142143
|
+
deps.setQueue(kept);
|
|
142144
|
+
deps.paint();
|
|
142145
|
+
return dropped.map((item) => item.question);
|
|
142146
|
+
},
|
|
142147
|
+
enqueueTelegramLine(text) {
|
|
142148
|
+
const queue = deps.getQueue();
|
|
142149
|
+
queue.push({ id: deps.nextId(), question: text, displayQuestion: deps.summarize(text), source: TG_SOURCE });
|
|
142150
|
+
deps.paint();
|
|
142151
|
+
deps.say(`\u25C7 a line from Telegram is queued as q${queue.length}.
|
|
142152
|
+
`);
|
|
142153
|
+
},
|
|
142154
|
+
runTelegramLine(text) {
|
|
142155
|
+
deps.runLine(text, "operator", TG_SOURCE);
|
|
142156
|
+
},
|
|
142157
|
+
runQueued(item) {
|
|
142158
|
+
deps.runLine(item.question, "operator", item.source);
|
|
142159
|
+
},
|
|
142160
|
+
turnStarted(source) {
|
|
142161
|
+
deps.getBridge()?.turnStarted(source);
|
|
142162
|
+
},
|
|
142163
|
+
assistantText(text) {
|
|
142164
|
+
deps.getBridge()?.assistantText(text);
|
|
142165
|
+
},
|
|
142166
|
+
wrapOnToolCall(base) {
|
|
142167
|
+
return (...args) => {
|
|
142168
|
+
deps.getBridge()?.toolCall();
|
|
142169
|
+
base?.(...args);
|
|
142170
|
+
};
|
|
142171
|
+
},
|
|
142172
|
+
turnSettled(outcome) {
|
|
142173
|
+
const bridge = deps.getBridge();
|
|
142174
|
+
return bridge === undefined ? Promise.resolve() : bridge.turnSettled({ failed: outcome.failed || outcome.aborted });
|
|
142175
|
+
}
|
|
142176
|
+
};
|
|
142177
|
+
}
|
|
142178
|
+
var init_remote_queue_wiring = __esm(() => {
|
|
142179
|
+
init_shell_bridge();
|
|
142180
|
+
});
|
|
142181
|
+
|
|
141493
142182
|
// src/tui/worker-fleet.ts
|
|
141494
142183
|
function clip4(s, max) {
|
|
141495
142184
|
if (s.length <= max) {
|
|
@@ -144740,7 +145429,7 @@ var init_reflect = __esm(() => {
|
|
|
144740
145429
|
});
|
|
144741
145430
|
|
|
144742
145431
|
// src/commands/memory.ts
|
|
144743
|
-
import { randomUUID as
|
|
145432
|
+
import { randomUUID as randomUUID37 } from "crypto";
|
|
144744
145433
|
import { readdir as readdir46 } from "fs/promises";
|
|
144745
145434
|
import { join as join9 } from "path";
|
|
144746
145435
|
function getService2() {
|
|
@@ -144928,7 +145617,7 @@ async function runSearch(args) {
|
|
|
144928
145617
|
}
|
|
144929
145618
|
if (args.includes("--json")) {
|
|
144930
145619
|
const projection = renderMemorySearchReport({
|
|
144931
|
-
runId:
|
|
145620
|
+
runId: randomUUID37(),
|
|
144932
145621
|
generatedAt: new Date,
|
|
144933
145622
|
search: result,
|
|
144934
145623
|
filters
|
|
@@ -145932,8 +146621,8 @@ var init_proxy = __esm(() => {
|
|
|
145932
146621
|
|
|
145933
146622
|
// src/commands/trigger-agent-task.ts
|
|
145934
146623
|
import { execFile as execFile13 } from "child_process";
|
|
145935
|
-
import { randomUUID as
|
|
145936
|
-
import { mkdtemp as mkdtemp7, mkdir as mkdir61, readdir as readdir47, rm as
|
|
146624
|
+
import { randomUUID as randomUUID38 } from "crypto";
|
|
146625
|
+
import { mkdtemp as mkdtemp7, mkdir as mkdir61, readdir as readdir47, rm as rm20, writeFile as writeFile38 } from "fs/promises";
|
|
145937
146626
|
import { homedir as homedir12, tmpdir as tmpdir13 } from "os";
|
|
145938
146627
|
import path290 from "path";
|
|
145939
146628
|
function scrubGrantedOutput(text, env, secrets = []) {
|
|
@@ -146055,12 +146744,12 @@ function lastAssistantText(history) {
|
|
|
146055
146744
|
async function pruneReports(dir, keep) {
|
|
146056
146745
|
const names = (await readdir47(dir).catch(() => [])).filter((n) => n.endsWith(".md")).sort();
|
|
146057
146746
|
for (const old of names.slice(0, Math.max(0, names.length - keep))) {
|
|
146058
|
-
await
|
|
146747
|
+
await rm20(path290.join(dir, old), { force: true }).catch(() => {});
|
|
146059
146748
|
}
|
|
146060
146749
|
}
|
|
146061
146750
|
async function runAgentTaskDispatch(projectRoot, entry, action, deps = {}) {
|
|
146062
146751
|
const now = deps.now ?? (() => new Date);
|
|
146063
|
-
const runId = deps.runId?.() ?? `sch-${now().toISOString().replace(/[-:.]/g, "").slice(0, 15)}-${
|
|
146752
|
+
const runId = deps.runId?.() ?? `sch-${now().toISOString().replace(/[-:.]/g, "").slice(0, 15)}-${randomUUID38().slice(0, 8)}`;
|
|
146064
146753
|
await ensureLocksDir(projectRoot);
|
|
146065
146754
|
try {
|
|
146066
146755
|
return await withFileLock(dispatchLockPath(projectRoot, `schedule-${entry.name}`), () => runLocked(projectRoot, entry, action, deps, runId, now), {
|
|
@@ -146100,7 +146789,7 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
|
|
|
146100
146789
|
await mkdir61(scratchHome, { recursive: true });
|
|
146101
146790
|
await mkdir61(grantedCwd, { recursive: true, mode: 448 });
|
|
146102
146791
|
const cleanup = async () => {
|
|
146103
|
-
await
|
|
146792
|
+
await rm20(scratch, { recursive: true, force: true }).catch(() => {});
|
|
146104
146793
|
};
|
|
146105
146794
|
let closeProxy = async () => {};
|
|
146106
146795
|
const networkDecisions = [];
|
|
@@ -146223,7 +146912,7 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
|
|
|
146223
146912
|
modelId: dispatch.model,
|
|
146224
146913
|
toolNames: tools.map((t) => t.definition.name)
|
|
146225
146914
|
}),
|
|
146226
|
-
idSeq: () =>
|
|
146915
|
+
idSeq: () => randomUUID38(),
|
|
146227
146916
|
unattended: true,
|
|
146228
146917
|
hardDeny: unattendedRefusal,
|
|
146229
146918
|
...shellHooks !== undefined ? { hooks: shellHooks } : {}
|
|
@@ -146738,7 +147427,7 @@ var init_digest_limits = __esm(() => {
|
|
|
146738
147427
|
});
|
|
146739
147428
|
|
|
146740
147429
|
// src/scheduler/digest-summary.ts
|
|
146741
|
-
import { randomUUID as
|
|
147430
|
+
import { randomUUID as randomUUID39 } from "crypto";
|
|
146742
147431
|
var SYSTEM, MAX_OUTPUT_TOKENS = 400, defaultSummarize = async (input) => {
|
|
146743
147432
|
const { action, entry } = input;
|
|
146744
147433
|
const dispatch = action.dispatch;
|
|
@@ -146768,7 +147457,7 @@ var SYSTEM, MAX_OUTPUT_TOKENS = 400, defaultSummarize = async (input) => {
|
|
|
146768
147457
|
local.abort();
|
|
146769
147458
|
});
|
|
146770
147459
|
const provider = guardUsage(built, () => meter.markUsageMissing());
|
|
146771
|
-
const requestId = `digest-${
|
|
147460
|
+
const requestId = `digest-${randomUUID39().slice(0, 8)}`;
|
|
146772
147461
|
const request = {
|
|
146773
147462
|
providerId: dispatch.provider,
|
|
146774
147463
|
modelId: dispatch.model,
|
|
@@ -146817,8 +147506,8 @@ var init_digest_summary = __esm(() => {
|
|
|
146817
147506
|
});
|
|
146818
147507
|
|
|
146819
147508
|
// src/scheduler/digest-run.ts
|
|
146820
|
-
import { randomUUID as
|
|
146821
|
-
import { mkdir as mkdir62, mkdtemp as mkdtemp8, rm as
|
|
147509
|
+
import { randomUUID as randomUUID40 } from "crypto";
|
|
147510
|
+
import { mkdir as mkdir62, mkdtemp as mkdtemp8, rm as rm21, writeFile as writeFile39 } from "fs/promises";
|
|
146822
147511
|
import path292 from "path";
|
|
146823
147512
|
function capForDelivery(text, reportPath) {
|
|
146824
147513
|
if (text.length <= DELIVERY_TEXT_MAX)
|
|
@@ -146838,7 +147527,7 @@ async function announceRefusal(projectRoot, name, topic, runId, reason, result,
|
|
|
146838
147527
|
}
|
|
146839
147528
|
async function runDigestDispatch(projectRoot, entry, action, deps = {}) {
|
|
146840
147529
|
const now = deps.now ?? (() => new Date);
|
|
146841
|
-
const runId = deps.runId?.() ?? `dig-${now().toISOString().replace(/[-:.]/g, "").slice(0, 15)}-${
|
|
147530
|
+
const runId = deps.runId?.() ?? `dig-${now().toISOString().replace(/[-:.]/g, "").slice(0, 15)}-${randomUUID40().slice(0, 8)}`;
|
|
146842
147531
|
await ensureLocksDir(projectRoot);
|
|
146843
147532
|
try {
|
|
146844
147533
|
return await withFileLock(dispatchLockPath(projectRoot, `schedule-${entry.name}`), () => runLocked2(projectRoot, entry, action, deps, runId, now), {
|
|
@@ -146950,7 +147639,7 @@ async function runLocked2(projectRoot, entry, action, deps, runId, now) {
|
|
|
146950
147639
|
crashed = error instanceof Error ? error.message : String(error);
|
|
146951
147640
|
} finally {
|
|
146952
147641
|
limits.dispose();
|
|
146953
|
-
await
|
|
147642
|
+
await rm21(scratch, { recursive: true, force: true }).catch(() => {});
|
|
146954
147643
|
}
|
|
146955
147644
|
const tools = DIGEST_TOOL_IDS.filter((id) => action.grants.tools.includes(id));
|
|
146956
147645
|
const attempted = tools.length * action.grants.repos.length;
|
|
@@ -151303,17 +151992,31 @@ async function launchTuiAgentShell(opts) {
|
|
|
151303
151992
|
guardCollector.onToolResult(name, toolResult);
|
|
151304
151993
|
baseOnToolResultForGuard?.(name, toolResult);
|
|
151305
151994
|
};
|
|
151995
|
+
const remoteQueue = createRemoteQueueWiring({
|
|
151996
|
+
getBridge: () => remoteBridge,
|
|
151997
|
+
getQueue: () => mainQueue,
|
|
151998
|
+
setQueue: (next) => {
|
|
151999
|
+
mainQueue = next;
|
|
152000
|
+
},
|
|
152001
|
+
nextId: () => `mq${mainQueueSeq++}`,
|
|
152002
|
+
summarize: summarizeSubmittedLine,
|
|
152003
|
+
paint: () => paintMainQueue(),
|
|
152004
|
+
composer: {
|
|
152005
|
+
setText: (text) => {
|
|
152006
|
+
input.value = text;
|
|
152007
|
+
},
|
|
152008
|
+
focus: () => input.focus()
|
|
152009
|
+
},
|
|
152010
|
+
say: (text) => io.onSystem?.(text),
|
|
152011
|
+
runLine: (line, origin, source) => runLine(line, origin, source)
|
|
152012
|
+
});
|
|
151306
152013
|
const baseOnAssistantTextForGuard = io.onAssistantText?.bind(io);
|
|
151307
152014
|
io.onAssistantText = (text) => {
|
|
151308
152015
|
guardCollector.onAssistantText(text);
|
|
151309
|
-
|
|
152016
|
+
remoteQueue.assistantText(text);
|
|
151310
152017
|
baseOnAssistantTextForGuard?.(text);
|
|
151311
152018
|
};
|
|
151312
|
-
|
|
151313
|
-
io.onToolCall = (name, toolInput) => {
|
|
151314
|
-
remoteBridge?.toolCall();
|
|
151315
|
-
baseOnToolCallForRemote?.(name, toolInput);
|
|
151316
|
-
};
|
|
152019
|
+
io.onToolCall = remoteQueue.wrapOnToolCall(io.onToolCall?.bind(io));
|
|
151317
152020
|
const refreshGuardSidebar = () => {
|
|
151318
152021
|
clearTranscriptChildren(sbGuard);
|
|
151319
152022
|
if (!guardEnabled)
|
|
@@ -151959,19 +152662,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
151959
152662
|
deps = { ...deps, onContextCompaction };
|
|
151960
152663
|
liveDeps = deps;
|
|
151961
152664
|
let splash = undefined;
|
|
151962
|
-
const stopRemoteForSessionSwitch = () =>
|
|
151963
|
-
if (remoteBridge?.keepingTopic === true) {
|
|
151964
|
-
remoteBridge.sessionLeaving();
|
|
151965
|
-
return;
|
|
151966
|
-
}
|
|
151967
|
-
if (remoteBridge?.active !== true)
|
|
151968
|
-
return;
|
|
151969
|
-
const closing = remoteBridge.disable();
|
|
151970
|
-
io.onSystem?.(`\u25C7 remote control is turning off: the session changed, and its Telegram topic is being deleted. Turn it on again with /remote-control <name>.
|
|
151971
|
-
`);
|
|
151972
|
-
closing.then(() => io.onSystem?.(`\u25C7 remote control off: the topic is deleted.
|
|
151973
|
-
`));
|
|
151974
|
-
};
|
|
152665
|
+
const stopRemoteForSessionSwitch = () => remoteQueue.stopRemoteForSessionSwitch();
|
|
151975
152666
|
const applyOpened = (opened, previewHistory) => {
|
|
151976
152667
|
if (opened.history.length > 0) {
|
|
151977
152668
|
splash?.removeIfShown();
|
|
@@ -153107,7 +153798,6 @@ ${formatThemeList(getThemeId())}`);
|
|
|
153107
153798
|
const sideQueue = [];
|
|
153108
153799
|
let mainQueue = [];
|
|
153109
153800
|
let mainQueueSeq = 0;
|
|
153110
|
-
let pendingQueueEdit;
|
|
153111
153801
|
const forceHandoff = createForegroundForceHandoff();
|
|
153112
153802
|
let sideWorkerRunning = false;
|
|
153113
153803
|
let sideClearTimeout;
|
|
@@ -153215,26 +153905,8 @@ ${formatThemeList(getThemeId())}`);
|
|
|
153215
153905
|
fleet.remove("agent:queue");
|
|
153216
153906
|
}
|
|
153217
153907
|
};
|
|
153218
|
-
const removeMainQueue = (index) =>
|
|
153219
|
-
|
|
153220
|
-
return;
|
|
153221
|
-
const removedItem = mainQueue[index];
|
|
153222
|
-
if (removedItem?.source === TG_SOURCE) {
|
|
153223
|
-
remoteBridge?.queuedLineRemoved(removedItem.question);
|
|
153224
|
-
}
|
|
153225
|
-
mainQueue = removeMainQueueItem(mainQueue, index);
|
|
153226
|
-
paintMainQueue();
|
|
153227
|
-
};
|
|
153228
|
-
const editMainQueue = (index) => {
|
|
153229
|
-
const edited = editMainQueueItem(mainQueue, index);
|
|
153230
|
-
if (edited === undefined)
|
|
153231
|
-
return;
|
|
153232
|
-
mainQueue = edited.rest;
|
|
153233
|
-
pendingQueueEdit = pendingQueueEditFor(edited.removed, index);
|
|
153234
|
-
input.value = edited.text;
|
|
153235
|
-
input.focus();
|
|
153236
|
-
paintMainQueue();
|
|
153237
|
-
};
|
|
153908
|
+
const removeMainQueue = (index) => remoteQueue.removeMainQueue(index);
|
|
153909
|
+
const editMainQueue = (index) => remoteQueue.editMainQueue(index);
|
|
153238
153910
|
const forceMainQueue = (index) => {
|
|
153239
153911
|
const item = mainQueue[index];
|
|
153240
153912
|
if (item === undefined)
|
|
@@ -153249,7 +153921,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
153249
153921
|
forceForegroundQueueItem(foregroundOperation, forceHandoff, item, {
|
|
153250
153922
|
announce: () => io.onSystem?.(`\u25C7 main turn interrupted \u2014 q${index + 1} will run next.
|
|
153251
153923
|
`),
|
|
153252
|
-
run: (next) =>
|
|
153924
|
+
run: (next) => remoteQueue.runQueued(next)
|
|
153253
153925
|
});
|
|
153254
153926
|
};
|
|
153255
153927
|
const handleQueueNavKey = (key) => {
|
|
@@ -153658,11 +154330,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
153658
154330
|
case "not-a-command":
|
|
153659
154331
|
break;
|
|
153660
154332
|
}
|
|
153661
|
-
if (
|
|
153662
|
-
const edit = pendingQueueEdit;
|
|
153663
|
-
pendingQueueEdit = undefined;
|
|
153664
|
-
mainQueue = reinsertEditedMainQueueItem(mainQueue, edit, line, displayLine);
|
|
153665
|
-
paintMainQueue();
|
|
154333
|
+
if (remoteQueue.requeuePendingEdit(line, displayLine)) {
|
|
153666
154334
|
return;
|
|
153667
154335
|
}
|
|
153668
154336
|
if (leaseView()?.held() === true) {
|
|
@@ -153703,7 +154371,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
153703
154371
|
takeForced: () => forceHandoff.takeNext(),
|
|
153704
154372
|
dispatch: (next) => {
|
|
153705
154373
|
paintMainQueue();
|
|
153706
|
-
|
|
154374
|
+
remoteQueue.runQueued(next);
|
|
153707
154375
|
}
|
|
153708
154376
|
});
|
|
153709
154377
|
} else {
|
|
@@ -154479,7 +155147,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
154479
155147
|
const next = forceHandoff.takeNext() ?? mainQueue.shift();
|
|
154480
155148
|
paintMainQueue();
|
|
154481
155149
|
if (next !== undefined)
|
|
154482
|
-
|
|
155150
|
+
remoteQueue.runQueued(next);
|
|
154483
155151
|
}
|
|
154484
155152
|
}
|
|
154485
155153
|
});
|
|
@@ -154491,7 +155159,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
154491
155159
|
sessions.clear();
|
|
154492
155160
|
deps.resetSubagentBudget?.();
|
|
154493
155161
|
const operation = foregroundOperation.begin();
|
|
154494
|
-
|
|
155162
|
+
remoteQueue.turnStarted(source);
|
|
154495
155163
|
blockIo.resetReasoningLiveState();
|
|
154496
155164
|
setMainAgent("running", "waiting");
|
|
154497
155165
|
startBusy("waiting for model");
|
|
@@ -154586,7 +155254,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
154586
155254
|
...slateSession !== undefined ? { slateSession, pruneArchive: true } : {}
|
|
154587
155255
|
}).finally(() => {
|
|
154588
155256
|
foregroundOperation.settle(operation);
|
|
154589
|
-
|
|
155257
|
+
remoteQueue.turnSettled({ failed: turnFailed, aborted: turnSignal.aborted });
|
|
154590
155258
|
if (foregroundOperation.isDisposed)
|
|
154591
155259
|
return;
|
|
154592
155260
|
refreshRewindSidebar();
|
|
@@ -154639,7 +155307,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
154639
155307
|
const next = forceHandoff.takeNext() ?? mainQueue.shift();
|
|
154640
155308
|
paintMainQueue();
|
|
154641
155309
|
if (next !== undefined) {
|
|
154642
|
-
|
|
155310
|
+
remoteQueue.runQueued(next);
|
|
154643
155311
|
} else {
|
|
154644
155312
|
busWakeController?.onSettle();
|
|
154645
155313
|
}
|
|
@@ -154683,7 +155351,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
154683
155351
|
const drained = forceHandoff.takeNext() ?? mainQueue.shift();
|
|
154684
155352
|
paintMainQueue();
|
|
154685
155353
|
if (drained !== undefined)
|
|
154686
|
-
|
|
155354
|
+
remoteQueue.runQueued(drained);
|
|
154687
155355
|
}
|
|
154688
155356
|
});
|
|
154689
155357
|
const captureShellOutput = async (run) => {
|
|
@@ -154795,14 +155463,8 @@ Turn: ${chrome.isBusy() ? "running" : "idle"} \xB7 queue: ${mainQueue.length} wa
|
|
|
154795
155463
|
sessionId: () => liveSession.summary.id,
|
|
154796
155464
|
project: () => sessionCwd,
|
|
154797
155465
|
isBusy: () => chrome.isBusy() || foregroundOperation.isActive || forceHandoff.isAwaitingSettlement || mainQueue.length > 0 || leaseView()?.held() === true,
|
|
154798
|
-
runLine: (text) =>
|
|
154799
|
-
enqueue: (text) =>
|
|
154800
|
-
const id = `mq${mainQueueSeq++}`;
|
|
154801
|
-
mainQueue.push({ id, question: text, displayQuestion: summarizeSubmittedLine(text), source: TG_SOURCE });
|
|
154802
|
-
paintMainQueue();
|
|
154803
|
-
io.onSystem?.(`\u25C7 a line from Telegram is queued as q${mainQueue.length}.
|
|
154804
|
-
`);
|
|
154805
|
-
},
|
|
155466
|
+
runLine: (text) => remoteQueue.runTelegramLine(text),
|
|
155467
|
+
enqueue: (text) => remoteQueue.enqueueTelegramLine(text),
|
|
154806
155468
|
notice: (text) => io.onSystem?.(`${text}
|
|
154807
155469
|
`),
|
|
154808
155470
|
cancelTurn: () => foregroundOperation.cancel("remote run time limit"),
|
|
@@ -154820,14 +155482,7 @@ Turn: ${chrome.isBusy() ? "running" : "idle"} \xB7 queue: ${mainQueue.length} wa
|
|
|
154820
155482
|
liveSession = recordRemoteOff(liveSession);
|
|
154821
155483
|
} catch {}
|
|
154822
155484
|
},
|
|
154823
|
-
dropQueuedTelegramLines: () =>
|
|
154824
|
-
const { kept, dropped } = dropQueuedBySource(mainQueue, TG_SOURCE);
|
|
154825
|
-
if (dropped.length === 0)
|
|
154826
|
-
return [];
|
|
154827
|
-
mainQueue = kept;
|
|
154828
|
-
paintMainQueue();
|
|
154829
|
-
return dropped.map((item) => item.question);
|
|
154830
|
-
},
|
|
155485
|
+
dropQueuedTelegramLines: () => remoteQueue.dropQueuedTelegramLines(),
|
|
154831
155486
|
runCommand: runRemoteCommand,
|
|
154832
155487
|
busyRefusal: (line) => {
|
|
154833
155488
|
const command = findAgentCommand(line, "agent");
|
|
@@ -155054,6 +155709,7 @@ var init_tui_shell = __esm(() => {
|
|
|
155054
155709
|
init_composer_choice();
|
|
155055
155710
|
init_filter_list();
|
|
155056
155711
|
init_shell_chrome();
|
|
155712
|
+
init_remote_queue_wiring();
|
|
155057
155713
|
init_queue_nav();
|
|
155058
155714
|
init_subagent_inspector();
|
|
155059
155715
|
init_subagent_session();
|
|
@@ -155829,7 +156485,7 @@ var init_debug_watcher = __esm(() => {
|
|
|
155829
156485
|
});
|
|
155830
156486
|
|
|
155831
156487
|
// src/commands/shell.ts
|
|
155832
|
-
import { randomUUID as
|
|
156488
|
+
import { randomUUID as randomUUID41 } from "crypto";
|
|
155833
156489
|
import { join as joinPath2 } from "path";
|
|
155834
156490
|
import * as readline2 from "readline";
|
|
155835
156491
|
function reportMcpProblems(runtime) {
|
|
@@ -158188,14 +158844,14 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
158188
158844
|
`);
|
|
158189
158845
|
}
|
|
158190
158846
|
const surface = chooseShellSurface(flags, runtime.isTty ?? process.stdout.isTTY === true);
|
|
158191
|
-
const shellHookSessionId =
|
|
158847
|
+
const shellHookSessionId = randomUUID41();
|
|
158192
158848
|
let shellHooks;
|
|
158193
158849
|
const getShellHooks = () => {
|
|
158194
158850
|
if (shellHooks === undefined) {
|
|
158195
158851
|
shellHooks = buildShellHookRuntime({
|
|
158196
158852
|
projectRoot: resolveProjectRoot2(process.cwd()),
|
|
158197
158853
|
sessionId: shellHookSessionId,
|
|
158198
|
-
runId:
|
|
158854
|
+
runId: randomUUID41(),
|
|
158199
158855
|
interactive: true,
|
|
158200
158856
|
profileId: "monitored-trusted-local",
|
|
158201
158857
|
...runtime.cacheDir === undefined ? {} : { configDir: runtime.cacheDir }
|
|
@@ -158300,7 +158956,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
158300
158956
|
sessionOverride: reasoningSessionOverride,
|
|
158301
158957
|
globalEffort: loadShellConfig(runtime.cacheDir).reasoningEffort
|
|
158302
158958
|
}),
|
|
158303
|
-
idSeq: () =>
|
|
158959
|
+
idSeq: () => randomUUID41(),
|
|
158304
158960
|
askUser: invokeAskUserHost,
|
|
158305
158961
|
sweepBackgroundJobs: () => jobRegistry.sweepAll(),
|
|
158306
158962
|
jobRegistry,
|
|
@@ -158371,7 +159027,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
158371
159027
|
return {
|
|
158372
159028
|
makeProvider: chatFactory,
|
|
158373
159029
|
clock: () => new Date().toISOString(),
|
|
158374
|
-
idSeq: () =>
|
|
159030
|
+
idSeq: () => randomUUID41(),
|
|
158375
159031
|
initial: Object.keys(resolvedModelParams).length > 0 ? { ...sel, modelParams: resolvedModelParams } : sel,
|
|
158376
159032
|
session: {
|
|
158377
159033
|
cwd,
|
|
@@ -158499,7 +159155,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
158499
159155
|
const deps = {
|
|
158500
159156
|
makeProvider: baseFactory,
|
|
158501
159157
|
clock: () => new Date().toISOString(),
|
|
158502
|
-
idSeq: () =>
|
|
159158
|
+
idSeq: () => randomUUID41(),
|
|
158503
159159
|
initial: {
|
|
158504
159160
|
provider,
|
|
158505
159161
|
model,
|
|
@@ -158580,7 +159236,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
158580
159236
|
sessionOverride: reasoningSessionOverride,
|
|
158581
159237
|
globalEffort: loadShellConfig(runtime.cacheDir).reasoningEffort
|
|
158582
159238
|
}),
|
|
158583
|
-
idSeq: () =>
|
|
159239
|
+
idSeq: () => randomUUID41(),
|
|
158584
159240
|
askUser: invokeAskUserHost,
|
|
158585
159241
|
mcpRuntime: () => mcpRuntime,
|
|
158586
159242
|
sweepBackgroundJobs: () => jobRegistry.sweepAll(),
|
|
@@ -163090,8 +163746,8 @@ var init_sweep = __esm(() => {
|
|
|
163090
163746
|
});
|
|
163091
163747
|
|
|
163092
163748
|
// src/retention/auto-sweep.ts
|
|
163093
|
-
import { mkdir as mkdir63, readFile as readFile158, rm as
|
|
163094
|
-
import { randomUUID as
|
|
163749
|
+
import { mkdir as mkdir63, readFile as readFile158, rm as rm22, stat as stat31, writeFile as writeFile40 } from "fs/promises";
|
|
163750
|
+
import { randomUUID as randomUUID42 } from "crypto";
|
|
163095
163751
|
import path309 from "path";
|
|
163096
163752
|
function autoSweepStampPath(cwd) {
|
|
163097
163753
|
return path309.join(cwd, ".metaproject", "data", "retention", "last-auto-sweep.json");
|
|
@@ -163123,7 +163779,7 @@ async function readAutoSweepStamp(cwd) {
|
|
|
163123
163779
|
}
|
|
163124
163780
|
async function claimIsLive(cwd, staleMs) {
|
|
163125
163781
|
const claimPath = autoSweepClaimPath(cwd);
|
|
163126
|
-
const held = await
|
|
163782
|
+
const held = await stat31(claimPath).catch(() => null);
|
|
163127
163783
|
if (held === null || Date.now() - held.mtimeMs > staleMs) {
|
|
163128
163784
|
return false;
|
|
163129
163785
|
}
|
|
@@ -163140,7 +163796,7 @@ async function claimIsLive(cwd, staleMs) {
|
|
|
163140
163796
|
}
|
|
163141
163797
|
}
|
|
163142
163798
|
async function acquireClaim(claimPath, staleMs) {
|
|
163143
|
-
const nonce = `${process.pid}:${
|
|
163799
|
+
const nonce = `${process.pid}:${randomUUID42()}`;
|
|
163144
163800
|
for (let attempt = 0;attempt < 2; attempt += 1) {
|
|
163145
163801
|
try {
|
|
163146
163802
|
await writeFile40(claimPath, nonce, { flag: "wx", mode: 384 });
|
|
@@ -163148,7 +163804,7 @@ async function acquireClaim(claimPath, staleMs) {
|
|
|
163148
163804
|
release: async () => {
|
|
163149
163805
|
try {
|
|
163150
163806
|
if (await readFile158(claimPath, "utf8") === nonce) {
|
|
163151
|
-
await
|
|
163807
|
+
await rm22(claimPath, { force: true });
|
|
163152
163808
|
}
|
|
163153
163809
|
} catch {}
|
|
163154
163810
|
}
|
|
@@ -163158,9 +163814,9 @@ async function acquireClaim(claimPath, staleMs) {
|
|
|
163158
163814
|
if (code !== "EEXIST") {
|
|
163159
163815
|
return null;
|
|
163160
163816
|
}
|
|
163161
|
-
const held = await
|
|
163817
|
+
const held = await stat31(claimPath).catch(() => null);
|
|
163162
163818
|
if (held !== null && Date.now() - held.mtimeMs > staleMs) {
|
|
163163
|
-
await
|
|
163819
|
+
await rm22(claimPath, { force: true }).catch(() => {
|
|
163164
163820
|
return;
|
|
163165
163821
|
});
|
|
163166
163822
|
continue;
|
|
@@ -163229,7 +163885,7 @@ var init_auto_sweep = __esm(() => {
|
|
|
163229
163885
|
|
|
163230
163886
|
// src/ctx/artifact-id.ts
|
|
163231
163887
|
import { randomBytes as randomBytes13 } from "crypto";
|
|
163232
|
-
import { mkdir as mkdir64, readFile as readFile159, rm as
|
|
163888
|
+
import { mkdir as mkdir64, readFile as readFile159, rm as rm23, writeFile as writeFile41 } from "fs/promises";
|
|
163233
163889
|
import path310 from "path";
|
|
163234
163890
|
function pinnedClock() {
|
|
163235
163891
|
const pin = process.env.KERYX_CTX_CLOCK_PIN_MS;
|
|
@@ -163278,7 +163934,7 @@ async function reserveArtifact(options) {
|
|
|
163278
163934
|
continue;
|
|
163279
163935
|
}
|
|
163280
163936
|
if (!await claim(summaryPath)) {
|
|
163281
|
-
await
|
|
163937
|
+
await rm23(rawPath, { force: true });
|
|
163282
163938
|
continue;
|
|
163283
163939
|
}
|
|
163284
163940
|
return { id, rawPath, summaryPath };
|
|
@@ -164754,7 +165410,7 @@ function readLiteral(source, start, quote) {
|
|
|
164754
165410
|
}
|
|
164755
165411
|
|
|
164756
165412
|
// src/wiki/freshness/queue.ts
|
|
164757
|
-
import { readFile as readFile161, rename as
|
|
165413
|
+
import { readFile as readFile161, rename as rename11, stat as stat32, unlink as unlink3, writeFile as writeFile43 } from "fs/promises";
|
|
164758
165414
|
import path312 from "path";
|
|
164759
165415
|
function queuePath(cwd) {
|
|
164760
165416
|
return path312.join(cwd, ".metaproject", "data", "wiki", QUEUE_FILE);
|
|
@@ -164776,7 +165432,7 @@ async function drainQueue(cwd) {
|
|
|
164776
165432
|
if (line.trim().length === 0) {
|
|
164777
165433
|
continue;
|
|
164778
165434
|
}
|
|
164779
|
-
const entry =
|
|
165435
|
+
const entry = parseEntry4(line);
|
|
164780
165436
|
if (entry === null) {
|
|
164781
165437
|
corruptLines += 1;
|
|
164782
165438
|
continue;
|
|
@@ -164798,7 +165454,7 @@ async function clearQueue(cwd) {
|
|
|
164798
165454
|
}
|
|
164799
165455
|
}
|
|
164800
165456
|
}
|
|
164801
|
-
function
|
|
165457
|
+
function parseEntry4(line) {
|
|
164802
165458
|
try {
|
|
164803
165459
|
const value = JSON.parse(line);
|
|
164804
165460
|
if (value.schemaVersion !== 1 || typeof value.rev !== "string" || !/^[0-9a-f]{40}$/.test(value.rev) || typeof value.recordedAt !== "string" || !Array.isArray(value.paths)) {
|
|
@@ -169551,7 +170207,7 @@ var init_sync2 = __esm(() => {
|
|
|
169551
170207
|
});
|
|
169552
170208
|
|
|
169553
170209
|
// src/gdskills/rule-references.ts
|
|
169554
|
-
import { stat as
|
|
170210
|
+
import { stat as stat33 } from "fs/promises";
|
|
169555
170211
|
import path328 from "path";
|
|
169556
170212
|
function ruleReferences(content) {
|
|
169557
170213
|
const refs = new Set;
|
|
@@ -169568,7 +170224,7 @@ function withKeryxDirSpelling(ref) {
|
|
|
169568
170224
|
}
|
|
169569
170225
|
async function isRegularFile(absolute) {
|
|
169570
170226
|
try {
|
|
169571
|
-
return (await
|
|
170227
|
+
return (await stat33(absolute)).isFile();
|
|
169572
170228
|
} catch {
|
|
169573
170229
|
return false;
|
|
169574
170230
|
}
|
|
@@ -169789,7 +170445,7 @@ var init_project_reviewers = __esm(() => {
|
|
|
169789
170445
|
});
|
|
169790
170446
|
|
|
169791
170447
|
// src/gdskills/import-skills.ts
|
|
169792
|
-
import { readdir as readdir54, readFile as readFile170, stat as
|
|
170448
|
+
import { readdir as readdir54, readFile as readFile170, stat as stat34 } from "fs/promises";
|
|
169793
170449
|
import { homedir as homedir14 } from "os";
|
|
169794
170450
|
import path330 from "path";
|
|
169795
170451
|
function emptyOnlyError(label) {
|
|
@@ -169830,7 +170486,7 @@ async function importProjectSkills(options) {
|
|
|
169830
170486
|
addFlagWarnings(imported, sources, existingFlags);
|
|
169831
170487
|
const rules = [];
|
|
169832
170488
|
for (const decision of ruleDecisions) {
|
|
169833
|
-
rules.push("row" in decision ? decision.row : options.dryRun ? unwrittenRow(decision.placement, [decision.dryRunReason, ...gateNotes(decision.gate, true)].filter(
|
|
170489
|
+
rules.push("row" in decision ? decision.row : options.dryRun ? unwrittenRow(decision.placement, [decision.dryRunReason, ...gateNotes(decision.gate, true)].filter(isString4).join(" \u2014 ") || undefined, decision.gate.security) : await writePlacedRule(options, decision.placement, decision.gate));
|
|
169834
170490
|
}
|
|
169835
170491
|
return {
|
|
169836
170492
|
from: options.from,
|
|
@@ -170150,7 +170806,7 @@ async function resolveImportSources(options) {
|
|
|
170150
170806
|
if (!await pathExists(resolved)) {
|
|
170151
170807
|
throw new Error(`keryx skills import: --from ${options.from} does not exist (resolved to ${resolved})`);
|
|
170152
170808
|
}
|
|
170153
|
-
const stats = await
|
|
170809
|
+
const stats = await stat34(resolved);
|
|
170154
170810
|
if (stats.isFile()) {
|
|
170155
170811
|
return [await sourceFromFile(options, resolved)];
|
|
170156
170812
|
}
|
|
@@ -170351,7 +171007,7 @@ function gateRowParts(gate, dryRun) {
|
|
|
170351
171007
|
return security;
|
|
170352
171008
|
}
|
|
170353
171009
|
function gateNotes(gate, dryRun) {
|
|
170354
|
-
return [gateRowParts(gate, dryRun).reason].filter(
|
|
171010
|
+
return [gateRowParts(gate, dryRun).reason].filter(isString4);
|
|
170355
171011
|
}
|
|
170356
171012
|
function plannedSkillText(content, header) {
|
|
170357
171013
|
const version = parseSkillFrontmatter(content).metadataVersion ?? "0.1.0";
|
|
@@ -170967,7 +171623,7 @@ Examples:
|
|
|
170967
171623
|
var BUNDLED_NAMES, PATHS_NONE_WARNING = 'paths: none \u2014 dispatched on every round. Declare `metadata.paths: "<glob>, <glob>"` in its frontmatter to gate it on the diff.', DEFAULT_FETCHER = async (url) => {
|
|
170968
171624
|
const response = await fetch(url);
|
|
170969
171625
|
return { ok: response.ok, status: response.status, text: await response.text() };
|
|
170970
|
-
}, DEPRECATED_REASON = "deprecated", KNOWN_CATEGORIES,
|
|
171626
|
+
}, DEPRECATED_REASON = "deprecated", KNOWN_CATEGORIES, isString4 = (value) => value !== undefined, REFUSED_NOTE = "A refused file was not written.", ALLOW_FLAGGED_HINT = "A prompt-injection refusal can be overridden after you read the file: run the command again with --allow-flagged.", IMPORT_HEADER_LABELS, IMPORT_HEADER_LINE, BYTE_ORDER_MARK = 65279, ALLOW_FLAGGED_FLAG = "--allow-flagged", SECURITY_GATE_HELP = `Security gate: a SKILL.md or rule is written as the gate returns it, so a secret is
|
|
170971
171627
|
masked and the row says it was redacted, and any other finding is reported on the
|
|
170972
171628
|
row. Text with a prompt-injection finding is refused in every security mode: the
|
|
170973
171629
|
row is \`refused\` (\`would-refuse\` on a dry run) with the gate's own summary,
|
|
@@ -181155,12 +181811,29 @@ var init_command_registry = __esm(() => {
|
|
|
181155
181811
|
{
|
|
181156
181812
|
module: "decisions",
|
|
181157
181813
|
command: "decisions report",
|
|
181158
|
-
summary: "Match share between the human's choice and the agent's recommendation, by mode (ordinary, blind) and by stage, " + "with every deviation and its reason. Deterministic, no model.",
|
|
181814
|
+
summary: "Match share between the human's choice and the agent's recommendation, by mode (ordinary, blind) and by stage, " + "with every deviation and its reason. Backfilled (imported, historical) decisions are reported in a block of their own and never counted in the live shares or the time to answer. Deterministic, no model.",
|
|
181159
181815
|
intent: ["decisions report", "\u043E\u0442\u0447\u0451\u0442 \u043F\u043E \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F\u043C", "how often does the human follow the recommendation", "blind mode results"],
|
|
181160
|
-
args: [
|
|
181816
|
+
args: [
|
|
181817
|
+
{ name: "json", type: "bool", required: false, desc: "print the report as JSON (with a `backfilled` section)" },
|
|
181818
|
+
{ name: "line", type: "bool", required: false, desc: "print ONE line in Russian for the daily topic message: the total and the match share of the visible, hidden and historical decisions" }
|
|
181819
|
+
],
|
|
181161
181820
|
json: true,
|
|
181162
181821
|
read: true
|
|
181163
181822
|
},
|
|
181823
|
+
{
|
|
181824
|
+
module: "decisions",
|
|
181825
|
+
command: "decisions import",
|
|
181826
|
+
summary: 'Import historical decisions from a JSON-lines file as backfilled records: the "before" arm of a comparison with the live journal. ' + "Each is marked backfilled, is never blind, keeps the time it was asked, and is reported apart from the live ones. " + "A line that is not a decision is skipped, named with its line number and counted as malformed, and the rest is imported; an id already in the journal is skipped, so a second import changes nothing (except that a backfilled decision missing its answer gets just that answer).",
|
|
181827
|
+
intent: ["decisions import", "import historical decisions", "backfill the recommendation journal", "\u0438\u043C\u043F\u043E\u0440\u0442 \u0438\u0441\u0442\u043E\u0440\u0438\u0447\u0435\u0441\u043A\u0438\u0445 \u0440\u0435\u0448\u0435\u043D\u0438\u0439", "\u0434\u043E\u0437\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u0436\u0443\u0440\u043D\u0430\u043B \u0440\u0435\u0448\u0435\u043D\u0438\u0439"],
|
|
181828
|
+
args: [
|
|
181829
|
+
{ name: "<file>", type: "string", required: true, desc: "a JSON-lines file, one decision per line: id, at, flow, stage, question, options, recommendation, source, answer, reason" },
|
|
181830
|
+
{ name: "dry-run", type: "bool", required: false, desc: "check and count without writing" },
|
|
181831
|
+
{ name: "json", type: "bool", required: false, desc: 'print the counts as JSON (a failure is {"error": "..."})' }
|
|
181832
|
+
],
|
|
181833
|
+
json: true,
|
|
181834
|
+
read: false,
|
|
181835
|
+
sideEffects: ["appends open, answer and reason records to .metaproject/data/decisions/journal.jsonl (nothing with --dry-run)"]
|
|
181836
|
+
},
|
|
181164
181837
|
{
|
|
181165
181838
|
module: "product",
|
|
181166
181839
|
command: "product index",
|
|
@@ -182014,7 +182687,7 @@ function isPassGate(gate) {
|
|
|
182014
182687
|
|
|
182015
182688
|
// src/commands/security-audit-harness.ts
|
|
182016
182689
|
import path347 from "path";
|
|
182017
|
-
import { stat as
|
|
182690
|
+
import { stat as stat35 } from "fs/promises";
|
|
182018
182691
|
function isSeverity(value) {
|
|
182019
182692
|
return value !== undefined && SEVERITIES3.includes(value);
|
|
182020
182693
|
}
|
|
@@ -182059,7 +182732,7 @@ async function handleAuditHarness(cwd, args = []) {
|
|
|
182059
182732
|
}
|
|
182060
182733
|
async function handleRun(cwd, args) {
|
|
182061
182734
|
const root = resolveRoot(cwd, args);
|
|
182062
|
-
const rootStat = await
|
|
182735
|
+
const rootStat = await stat35(root).catch(() => {
|
|
182063
182736
|
return;
|
|
182064
182737
|
});
|
|
182065
182738
|
if (!rootStat || !rootStat.isDirectory()) {
|
|
@@ -182146,7 +182819,7 @@ async function handleApply(cwd, args) {
|
|
|
182146
182819
|
process.exitCode = 1;
|
|
182147
182820
|
return;
|
|
182148
182821
|
}
|
|
182149
|
-
const rootStat = await
|
|
182822
|
+
const rootStat = await stat35(root).catch(() => {
|
|
182150
182823
|
return;
|
|
182151
182824
|
});
|
|
182152
182825
|
if (!rootStat || !rootStat.isDirectory()) {
|
|
@@ -184193,7 +184866,7 @@ var init_metaproject_tools2 = __esm(() => {
|
|
|
184193
184866
|
// src/mcp/tools.ts
|
|
184194
184867
|
import { readFile as readFile185, realpath as realpath22, writeFile as writeFile49 } from "fs/promises";
|
|
184195
184868
|
import path351 from "path";
|
|
184196
|
-
import { randomUUID as
|
|
184869
|
+
import { randomUUID as randomUUID43 } from "crypto";
|
|
184197
184870
|
function sacWorkspaceService(cwd) {
|
|
184198
184871
|
return new WorkspaceService({
|
|
184199
184872
|
workspaceRoot: cwd,
|
|
@@ -184279,7 +184952,7 @@ function buildToolRegistry() {
|
|
|
184279
184952
|
{ name: "sac.collaboration", module: "sac", description: "Read the collaboration references and activity for one Shared Agent Context workspace \u2014 who is participating and what has moved recently, redacted to what this actor may see. Local stdio only: over HTTP it returns `sac_transport_denied` without reading anything, because HTTP has no verified principal and must not inherit the local OS actor. Returns references and activity, not the items themselves \u2014 use sac.overview to enumerate items and sac.read to fetch one. Discover the workspaceId with sac.workspaceList.", inputSchema: OBJECT_SCHEMA({ workspaceId: { type: "string" } }, ["workspaceId"]), mutating: false, async invoke(cwd, params, context) {
|
|
184280
184953
|
if (context?.transport === "http")
|
|
184281
184954
|
return { code: "sac_transport_denied" };
|
|
184282
|
-
return normalizeCollaborationResult(await createLocalCollaborationService(cwd).overview({ workspaceId: stringParam2(params, "workspaceId") ?? "", request: undefined, requestCorrelationId:
|
|
184955
|
+
return normalizeCollaborationResult(await createLocalCollaborationService(cwd).overview({ workspaceId: stringParam2(params, "workspaceId") ?? "", request: undefined, requestCorrelationId: randomUUID43() }));
|
|
184283
184956
|
} },
|
|
184284
184957
|
{
|
|
184285
184958
|
name: "sac.overview",
|
|
@@ -184293,7 +184966,7 @@ function buildToolRegistry() {
|
|
|
184293
184966
|
const workspaceId = stringParam2(params, "workspaceId") ?? "";
|
|
184294
184967
|
const maxItems = typeof params.maxItems === "number" ? params.maxItems : 32;
|
|
184295
184968
|
const maxTokens = typeof params.maxTokens === "number" ? params.maxTokens : 4096;
|
|
184296
|
-
return normalizeFwkResult(await createLocalFwkReadService(cwd).overview({ workspaceId, request: undefined, requestCorrelationId:
|
|
184969
|
+
return normalizeFwkResult(await createLocalFwkReadService(cwd).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID43(), budget: { maxItems, maxTokens } }));
|
|
184297
184970
|
}
|
|
184298
184971
|
},
|
|
184299
184972
|
{
|
|
@@ -184309,7 +184982,7 @@ function buildToolRegistry() {
|
|
|
184309
184982
|
const itemId = stringParam2(params, "itemId") ?? "";
|
|
184310
184983
|
const maxItems = typeof params.maxItems === "number" ? params.maxItems : 1;
|
|
184311
184984
|
const maxTokens = typeof params.maxTokens === "number" ? params.maxTokens : 4096;
|
|
184312
|
-
return normalizeFwkResult(await createLocalFwkReadService(cwd).read({ workspaceId, itemId, request: undefined, requestCorrelationId:
|
|
184985
|
+
return normalizeFwkResult(await createLocalFwkReadService(cwd).read({ workspaceId, itemId, request: undefined, requestCorrelationId: randomUUID43(), budget: { maxItems, maxTokens } }));
|
|
184313
184986
|
}
|
|
184314
184987
|
},
|
|
184315
184988
|
{
|
|
@@ -184330,12 +185003,12 @@ function buildToolRegistry() {
|
|
|
184330
185003
|
if (!session)
|
|
184331
185004
|
throw new Error(`no session matching "${sessionRef}" in this project`);
|
|
184332
185005
|
const { service, wrapUpAuthority, authorizationServer } = createHarnessProposalLifecycleService(cwd, { workspaceId, ...note ? { note } : {} });
|
|
184333
|
-
const requestCorrelationId =
|
|
185006
|
+
const requestCorrelationId = randomUUID43();
|
|
184334
185007
|
const actor = await authorizationServer.actorContextFor(undefined, requestCorrelationId);
|
|
184335
185008
|
if (!actor)
|
|
184336
185009
|
throw new Error("trusted ActorContext is required");
|
|
184337
185010
|
const wrapUp = await wrapUpAuthority.issue({ actor, source: "session", sourceRef: sessionEvidenceRef(workspaceId, session.id) });
|
|
184338
|
-
const proposal = await service.create({ request: undefined, requestCorrelationId, workspaceId, id: `proposal-${
|
|
185011
|
+
const proposal = await service.create({ request: undefined, requestCorrelationId, workspaceId, id: `proposal-${randomUUID43().replace(/-/g, "").slice(0, 16)}`, proposalRevision, kind, wrapUp });
|
|
184339
185012
|
if (note)
|
|
184340
185013
|
await writeFile49(proposalNotePath(cwd, workspaceId, proposal.id), note, "utf8");
|
|
184341
185014
|
return normalizeProposalLifecycleResult(proposal);
|
|
@@ -184354,9 +185027,9 @@ function buildToolRegistry() {
|
|
|
184354
185027
|
const proposalId = stringParam2(params, "proposalId") ?? "";
|
|
184355
185028
|
const decision = stringParam2(params, "decision");
|
|
184356
185029
|
const reason = stringParam2(params, "reason");
|
|
184357
|
-
const idempotencyKey = stringParam2(params, "idempotencyKey") ??
|
|
185030
|
+
const idempotencyKey = stringParam2(params, "idempotencyKey") ?? randomUUID43();
|
|
184358
185031
|
const confirmToken = stringParam2(params, "confirmToken");
|
|
184359
|
-
const result = await createHarnessProposalLifecycleService(cwd, { workspaceId }).service.review({ request: undefined, requestCorrelationId:
|
|
185032
|
+
const result = await createHarnessProposalLifecycleService(cwd, { workspaceId }).service.review({ request: undefined, requestCorrelationId: randomUUID43(), workspaceId, proposalId, decision, idempotencyKey, interactive: true, ...reason ? { reason } : {}, ...confirmToken ? { confirmToken } : {} });
|
|
184360
185033
|
return normalizeProposalLifecycleResult(result);
|
|
184361
185034
|
}
|
|
184362
185035
|
},
|
|
@@ -184404,7 +185077,7 @@ function buildToolRegistry() {
|
|
|
184404
185077
|
throw new Error("sac.workspaceCreate requires a non-empty 'title'");
|
|
184405
185078
|
const rawComponent = stringParam2(params, "component");
|
|
184406
185079
|
const component = rawComponent === undefined ? undefined : requireWorkspaceReference(rawComponent);
|
|
184407
|
-
const workspace = await sacWorkspaceService(cwd).create({ request: undefined, requestCorrelationId:
|
|
185080
|
+
const workspace = await sacWorkspaceService(cwd).create({ request: undefined, requestCorrelationId: randomUUID43(), id: newWorkspaceId(), title, ...component ? { component: { kind: "component", uri: component } } : {} });
|
|
184408
185081
|
return workspace;
|
|
184409
185082
|
}
|
|
184410
185083
|
},
|
|
@@ -184465,7 +185138,7 @@ function buildToolRegistry() {
|
|
|
184465
185138
|
const text = redactSensitiveText(rawText);
|
|
184466
185139
|
const ts = new Date().toISOString();
|
|
184467
185140
|
const seed = {
|
|
184468
|
-
id: `seed-${
|
|
185141
|
+
id: `seed-${randomUUID43()}`,
|
|
184469
185142
|
text,
|
|
184470
185143
|
ts,
|
|
184471
185144
|
...kind ? { kind } : {},
|
|
@@ -184911,7 +185584,7 @@ var init_tools3 = __esm(() => {
|
|
|
184911
185584
|
|
|
184912
185585
|
// src/mcp/resources.ts
|
|
184913
185586
|
import path352 from "path";
|
|
184914
|
-
import { lstat as lstat19, readdir as readdir63, realpath as realpath23, stat as
|
|
185587
|
+
import { lstat as lstat19, readdir as readdir63, realpath as realpath23, stat as stat36 } from "fs/promises";
|
|
184915
185588
|
function mimeForPath(filePath) {
|
|
184916
185589
|
if (filePath.endsWith(".json") || filePath.endsWith(".jsonl")) {
|
|
184917
185590
|
return "application/json";
|
|
@@ -184954,7 +185627,7 @@ async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
|
184954
185627
|
}
|
|
184955
185628
|
for (const entry of entries) {
|
|
184956
185629
|
const full = path352.join(root, entry.name);
|
|
184957
|
-
const info = await
|
|
185630
|
+
const info = await stat36(full).catch(() => null);
|
|
184958
185631
|
if (!info || !isPathInside(ownerReal, await realpath23(full).catch(() => "")))
|
|
184959
185632
|
continue;
|
|
184960
185633
|
if (info.isDirectory()) {
|
|
@@ -185075,7 +185748,7 @@ async function readResource(cwd, roots, uri, harnessIdentity = null) {
|
|
|
185075
185748
|
throw new Error(`Resource not found: ${uri}`);
|
|
185076
185749
|
}
|
|
185077
185750
|
}
|
|
185078
|
-
const info = await
|
|
185751
|
+
const info = await stat36(resolved.absolute).catch(() => null);
|
|
185079
185752
|
if (!info || !info.isFile()) {
|
|
185080
185753
|
throw new Error(`Resource not found: ${uri}`);
|
|
185081
185754
|
}
|
|
@@ -186593,7 +187266,7 @@ var init_service18 = __esm(() => {
|
|
|
186593
187266
|
|
|
186594
187267
|
// src/commands/stack.ts
|
|
186595
187268
|
import path356 from "path";
|
|
186596
|
-
import { stat as
|
|
187269
|
+
import { stat as stat37 } from "fs/promises";
|
|
186597
187270
|
async function stackCommand(args, cwd = process.cwd()) {
|
|
186598
187271
|
const sub = args[0];
|
|
186599
187272
|
if (sub === undefined || sub === "--help" || sub === "-h") {
|
|
@@ -186625,7 +187298,7 @@ async function handleDetect(args, cwd) {
|
|
|
186625
187298
|
const write = !args.includes("--no-write");
|
|
186626
187299
|
let rootStat;
|
|
186627
187300
|
try {
|
|
186628
|
-
rootStat = await
|
|
187301
|
+
rootStat = await stat37(root);
|
|
186629
187302
|
} catch {
|
|
186630
187303
|
console.error(`--cwd directory does not exist: ${root}`);
|
|
186631
187304
|
process.exitCode = 1;
|
|
@@ -188125,7 +188798,7 @@ function checkApproval(input) {
|
|
|
188125
188798
|
// src/commands/harness.ts
|
|
188126
188799
|
import { readFileSync as readFileSync28, writeFileSync as writeFileSync10 } from "fs";
|
|
188127
188800
|
import path357 from "path";
|
|
188128
|
-
import { randomUUID as
|
|
188801
|
+
import { randomUUID as randomUUID44 } from "crypto";
|
|
188129
188802
|
import { realpathSync as realpathSync18 } from "fs";
|
|
188130
188803
|
import { homedir as homedir15, tmpdir as tmpdir14 } from "os";
|
|
188131
188804
|
function canonicalPath2(p) {
|
|
@@ -188152,7 +188825,7 @@ function resolveRuntime(deps) {
|
|
|
188152
188825
|
const env = deps?.env ?? process.env;
|
|
188153
188826
|
const clock = deps?.clock ?? (() => new Date().toISOString());
|
|
188154
188827
|
let idCounter = 0;
|
|
188155
|
-
const idSeq = deps?.idSeq ?? (() => `${
|
|
188828
|
+
const idSeq = deps?.idSeq ?? (() => `${randomUUID44()}-${idCounter++}`);
|
|
188156
188829
|
return { env, clock, idSeq };
|
|
188157
188830
|
}
|
|
188158
188831
|
function shellAllowProfile() {
|
|
@@ -188256,7 +188929,7 @@ async function harnessCommand(args, deps) {
|
|
|
188256
188929
|
const env = deps?.env ?? process.env;
|
|
188257
188930
|
const clock = deps?.clock ?? (() => new Date().toISOString());
|
|
188258
188931
|
let idCounter = 0;
|
|
188259
|
-
const idSeq = deps?.idSeq ?? (() => `${
|
|
188932
|
+
const idSeq = deps?.idSeq ?? (() => `${randomUUID44()}-${idCounter++}`);
|
|
188260
188933
|
const fetchImpl = deps?.fetch ?? globalThis.fetch;
|
|
188261
188934
|
if (provider === "anthropic") {
|
|
188262
188935
|
const apiKey = env.ANTHROPIC_API_KEY;
|
|
@@ -188397,7 +189070,7 @@ function harnessReplay(args, deps) {
|
|
|
188397
189070
|
}
|
|
188398
189071
|
const clock = deps?.clock ?? (() => new Date().toISOString());
|
|
188399
189072
|
let idCounter = 0;
|
|
188400
|
-
const idSeq = deps?.idSeq ?? (() => `${
|
|
189073
|
+
const idSeq = deps?.idSeq ?? (() => `${randomUUID44()}-${idCounter++}`);
|
|
188401
189074
|
const fixture = (() => {
|
|
188402
189075
|
if (parsed.fixture === undefined || parsed.fixture.length === 0) {
|
|
188403
189076
|
return { ok: true, value: buildReplayFixture(run, { idSeq }), built: true };
|
|
@@ -189112,7 +189785,7 @@ var init_permission = __esm(() => {
|
|
|
189112
189785
|
});
|
|
189113
189786
|
|
|
189114
189787
|
// src/acp/agent-io.ts
|
|
189115
|
-
import { randomUUID as
|
|
189788
|
+
import { randomUUID as randomUUID45 } from "crypto";
|
|
189116
189789
|
function toolKindFor(name) {
|
|
189117
189790
|
return TOOL_KIND_BY_NAME[name] ?? "other";
|
|
189118
189791
|
}
|
|
@@ -189127,7 +189800,7 @@ function createAcpAgentIo(sessionId, send, askPermission) {
|
|
|
189127
189800
|
const pendingByName = new Map;
|
|
189128
189801
|
const emit = (update) => send(sessionId, update);
|
|
189129
189802
|
const openToolCall = (name, input, claimed) => {
|
|
189130
|
-
const toolCallId =
|
|
189803
|
+
const toolCallId = randomUUID45();
|
|
189131
189804
|
const queue = pendingByName.get(name) ?? [];
|
|
189132
189805
|
queue.push({ toolCallId, input, claimed });
|
|
189133
189806
|
pendingByName.set(name, queue);
|
|
@@ -189214,7 +189887,7 @@ function createAcpAgentIo(sessionId, send, askPermission) {
|
|
|
189214
189887
|
const toolCallId = pending?.toolCallId;
|
|
189215
189888
|
emit({
|
|
189216
189889
|
sessionUpdate: "tool_call_update",
|
|
189217
|
-
toolCallId: toolCallId ??
|
|
189890
|
+
toolCallId: toolCallId ?? randomUUID45(),
|
|
189218
189891
|
status: result.isError ? "failed" : "completed",
|
|
189219
189892
|
content: [{ type: "content", content: textBlock(result.output) }],
|
|
189220
189893
|
rawOutput: result.output
|
|
@@ -189813,7 +190486,7 @@ var init_session_mcp = __esm(() => {
|
|
|
189813
190486
|
});
|
|
189814
190487
|
|
|
189815
190488
|
// src/acp/server.ts
|
|
189816
|
-
import { randomUUID as
|
|
190489
|
+
import { randomUUID as randomUUID46 } from "crypto";
|
|
189817
190490
|
function finishReasonToStopReason(finishReason) {
|
|
189818
190491
|
switch (finishReason) {
|
|
189819
190492
|
case "budget":
|
|
@@ -189833,7 +190506,7 @@ function refuse5(error) {
|
|
|
189833
190506
|
async function runAcpServer(options) {
|
|
189834
190507
|
let initialized = false;
|
|
189835
190508
|
let clientCapabilities;
|
|
189836
|
-
const idSeq = options.idSeq ?? (() =>
|
|
190509
|
+
const idSeq = options.idSeq ?? (() => randomUUID46());
|
|
189837
190510
|
const registry = new AcpSessionRegistry({
|
|
189838
190511
|
providerId: options.providerId,
|
|
189839
190512
|
modelId: options.modelId,
|
|
@@ -190476,7 +191149,7 @@ Switch with /model <model>; it applies from the next turn.`;
|
|
|
190476
191149
|
}
|
|
190477
191150
|
continue;
|
|
190478
191151
|
}
|
|
190479
|
-
const toolCallId = message.toolCallId ??
|
|
191152
|
+
const toolCallId = message.toolCallId ?? randomUUID46();
|
|
190480
191153
|
sendUpdate(sessionId, {
|
|
190481
191154
|
sessionUpdate: "tool_call_update",
|
|
190482
191155
|
toolCallId,
|
|
@@ -190715,7 +191388,7 @@ var MODEL_PLACEHOLDER = "{{model}}";
|
|
|
190715
191388
|
var init_fixture_provider = () => {};
|
|
190716
191389
|
|
|
190717
191390
|
// src/commands/acp.ts
|
|
190718
|
-
import { randomUUID as
|
|
191391
|
+
import { randomUUID as randomUUID47 } from "crypto";
|
|
190719
191392
|
function parseAcpArgs(args) {
|
|
190720
191393
|
const parsed = {};
|
|
190721
191394
|
for (let i = 0;i < args.length; i += 1) {
|
|
@@ -190969,7 +191642,7 @@ async function acpCommand(args) {
|
|
|
190969
191642
|
} : { providerUnavailable: resolution.message, providerId: "", modelId: "" },
|
|
190970
191643
|
agentInfo: { name: "keryx", version: package_default.version },
|
|
190971
191644
|
...parsed.dataDir !== undefined ? { dataDir: parsed.dataDir } : {},
|
|
190972
|
-
idSeq: () =>
|
|
191645
|
+
idSeq: () => randomUUID47()
|
|
190973
191646
|
});
|
|
190974
191647
|
} finally {
|
|
190975
191648
|
process.off("SIGTERM", onSignal);
|
|
@@ -191036,7 +191709,7 @@ var init_acp = __esm(() => {
|
|
|
191036
191709
|
});
|
|
191037
191710
|
|
|
191038
191711
|
// src/bus/prune.ts
|
|
191039
|
-
import { randomUUID as
|
|
191712
|
+
import { randomUUID as randomUUID48 } from "crypto";
|
|
191040
191713
|
async function expiryAlreadyLogged(root, leaseId) {
|
|
191041
191714
|
const { events } = await readEvents(root, await cursorAtStart(root));
|
|
191042
191715
|
return events.some((event) => event.kind === "lease-expired" && event.refs?.leaseId === leaseId);
|
|
@@ -191070,7 +191743,7 @@ async function pruneBus(root, options = {}) {
|
|
|
191070
191743
|
const file = leasePath(root, lease.leaseId);
|
|
191071
191744
|
try {
|
|
191072
191745
|
await appendEvent(root, {
|
|
191073
|
-
from: { instanceId:
|
|
191746
|
+
from: { instanceId: randomUUID48(), name: "system", origin: "system" },
|
|
191074
191747
|
to: ["*"],
|
|
191075
191748
|
toLabel: "@all",
|
|
191076
191749
|
kind: "lease-expired",
|
|
@@ -191123,7 +191796,7 @@ var init_prune3 = __esm(() => {
|
|
|
191123
191796
|
});
|
|
191124
191797
|
|
|
191125
191798
|
// src/commands/bus.ts
|
|
191126
|
-
import { randomUUID as
|
|
191799
|
+
import { randomUUID as randomUUID49 } from "crypto";
|
|
191127
191800
|
async function busCommand(args) {
|
|
191128
191801
|
const code = await runBusCommand(args);
|
|
191129
191802
|
if (code !== 0)
|
|
@@ -191276,7 +191949,7 @@ ${PAUSE_USAGE3}`);
|
|
|
191276
191949
|
if (toLabel === undefined || reason === undefined || reason.length === 0) {
|
|
191277
191950
|
throw new BusRefusal("invalid-event", PAUSE_USAGE3);
|
|
191278
191951
|
}
|
|
191279
|
-
const holder = { instanceId:
|
|
191952
|
+
const holder = { instanceId: randomUUID49(), name: "cli", origin: "cli" };
|
|
191280
191953
|
const lease = await createPauseLease(busRoot, {
|
|
191281
191954
|
holder,
|
|
191282
191955
|
toLabel,
|
|
@@ -191298,7 +191971,7 @@ async function resume(args, busRoot, ctx) {
|
|
|
191298
191971
|
if (leaseId === undefined) {
|
|
191299
191972
|
throw new BusRefusal("invalid-event", "usage: keryx bus resume <leaseId> [--json]");
|
|
191300
191973
|
}
|
|
191301
|
-
await resumePauseLease(busRoot, { leaseId, by: { instanceId:
|
|
191974
|
+
await resumePauseLease(busRoot, { leaseId, by: { instanceId: randomUUID49(), name: "cli", origin: "cli" }, now: ctx.now });
|
|
191302
191975
|
if (args.includes("--json")) {
|
|
191303
191976
|
ctx.out(JSON.stringify({ schemaVersion: 1, leaseId }, null, 2));
|
|
191304
191977
|
} else {
|
|
@@ -191602,7 +192275,7 @@ var init_modules = __esm(() => {
|
|
|
191602
192275
|
});
|
|
191603
192276
|
|
|
191604
192277
|
// src/lib/serve-approvals-broker.ts
|
|
191605
|
-
import { randomUUID as
|
|
192278
|
+
import { randomUUID as randomUUID50 } from "crypto";
|
|
191606
192279
|
function describeAsk(ask) {
|
|
191607
192280
|
const floors = ask.floors.length > 0 ? ` Needs a fresh answer every time (${ask.floors.join(", ")}).` : "";
|
|
191608
192281
|
return {
|
|
@@ -191658,7 +192331,7 @@ function createApprovalBroker(options) {
|
|
|
191658
192331
|
sessionId: ask.sessionId,
|
|
191659
192332
|
...text,
|
|
191660
192333
|
expiresAt: new Date(createdAt.getTime() + options.expirySeconds * 1000),
|
|
191661
|
-
correlationId:
|
|
192334
|
+
correlationId: randomUUID50(),
|
|
191662
192335
|
callFingerprint: ask.callFingerprint,
|
|
191663
192336
|
floors: ask.floors
|
|
191664
192337
|
}, options.dir, createdAt);
|
|
@@ -191800,7 +192473,7 @@ var init_serve_throttle = __esm(() => {
|
|
|
191800
192473
|
});
|
|
191801
192474
|
|
|
191802
192475
|
// src/lib/serve-turn.ts
|
|
191803
|
-
import { randomUUID as
|
|
192476
|
+
import { randomUUID as randomUUID51 } from "crypto";
|
|
191804
192477
|
import path359 from "path";
|
|
191805
192478
|
function validateTurnRequest(body) {
|
|
191806
192479
|
const problem = (message) => ({
|
|
@@ -191975,7 +192648,7 @@ async function redactOut(security, text) {
|
|
|
191975
192648
|
async function runRemoteTurn(input) {
|
|
191976
192649
|
const scanRoot = input.scanRoot;
|
|
191977
192650
|
const security = createSecurityService(scanRoot);
|
|
191978
|
-
const newId = input.newId ?? (() =>
|
|
192651
|
+
const newId = input.newId ?? (() => randomUUID51());
|
|
191979
192652
|
const clock = input.clock ?? (() => new Date().toISOString());
|
|
191980
192653
|
const turnId = input.turnId ?? newId();
|
|
191981
192654
|
const sessionId = input.request.sessionId ?? newId();
|
|
@@ -192158,7 +192831,7 @@ function outcomeOf(status, gate, unresolvedBlockerIds) {
|
|
|
192158
192831
|
}
|
|
192159
192832
|
function createSubmitTurn(deps) {
|
|
192160
192833
|
return async (request, project) => {
|
|
192161
|
-
const turnId = (deps.newId ?? (() =>
|
|
192834
|
+
const turnId = (deps.newId ?? (() => randomUUID51()))();
|
|
192162
192835
|
const scanned = await scanPrompt(deps.dir, request.prompt);
|
|
192163
192836
|
if (scanned.rejected) {
|
|
192164
192837
|
return { kind: "rejected" };
|
|
@@ -192173,7 +192846,7 @@ function createSubmitTurn(deps) {
|
|
|
192173
192846
|
}
|
|
192174
192847
|
let effected = false;
|
|
192175
192848
|
const detached = deps.approvals !== undefined && (deps.toolRegistry?.list().length ?? 0) > 0;
|
|
192176
|
-
const sessionId = request.sessionId ?? (deps.newId ?? (() =>
|
|
192849
|
+
const sessionId = request.sessionId ?? (deps.newId ?? (() => randomUUID51()))();
|
|
192177
192850
|
let onStarted = () => {
|
|
192178
192851
|
return;
|
|
192179
192852
|
};
|
|
@@ -192434,7 +193107,7 @@ function listApprovalsRoute(request, ctx) {
|
|
|
192434
193107
|
});
|
|
192435
193108
|
}
|
|
192436
193109
|
async function answerApprovalRoute(request, approvalId, ctx) {
|
|
192437
|
-
if (!
|
|
193110
|
+
if (!isApprovalId2(approvalId)) {
|
|
192438
193111
|
return approvalNotFound();
|
|
192439
193112
|
}
|
|
192440
193113
|
const declared = Number(request.headers.get("content-length") ?? "0");
|
|
@@ -192701,6 +193374,7 @@ var init_serve_server = __esm(() => {
|
|
|
192701
193374
|
["prompt", "POST"],
|
|
192702
193375
|
["state", "POST"],
|
|
192703
193376
|
["ack", "POST"],
|
|
193377
|
+
["approval-ack", "POST"],
|
|
192704
193378
|
["stream", "GET"],
|
|
192705
193379
|
["channels-status", "GET"],
|
|
192706
193380
|
["channels-pair", "POST"],
|
|
@@ -194059,7 +194733,7 @@ var init_durable_log = __esm(() => {
|
|
|
194059
194733
|
import { createHash as createHash80 } from "crypto";
|
|
194060
194734
|
import { unlinkSync as unlinkSync9 } from "fs";
|
|
194061
194735
|
import path361 from "path";
|
|
194062
|
-
function
|
|
194736
|
+
function parseEntry5(value) {
|
|
194063
194737
|
if (typeof value !== "object" || value === null) {
|
|
194064
194738
|
return;
|
|
194065
194739
|
}
|
|
@@ -194095,7 +194769,7 @@ class InboundQueues {
|
|
|
194095
194769
|
log(bindingKey) {
|
|
194096
194770
|
let log = this.logs.get(bindingKey);
|
|
194097
194771
|
if (log === undefined) {
|
|
194098
|
-
log = new DurableLog({ file: this.fileFor(bindingKey), parse:
|
|
194772
|
+
log = new DurableLog({ file: this.fileFor(bindingKey), parse: parseEntry5, maxEntries: MAX_INBOUND_PER_TOPIC });
|
|
194099
194773
|
log.load();
|
|
194100
194774
|
this.logs.set(bindingKey, log);
|
|
194101
194775
|
}
|
|
@@ -194736,9 +195410,9 @@ var init_rendering = __esm(() => {
|
|
|
194736
195410
|
});
|
|
194737
195411
|
|
|
194738
195412
|
// src/remote/outbound-queue.ts
|
|
194739
|
-
import { randomUUID as
|
|
195413
|
+
import { randomUUID as randomUUID52 } from "crypto";
|
|
194740
195414
|
import path363 from "path";
|
|
194741
|
-
function
|
|
195415
|
+
function parseEntry6(value) {
|
|
194742
195416
|
if (typeof value !== "object" || value === null) {
|
|
194743
195417
|
return;
|
|
194744
195418
|
}
|
|
@@ -194781,7 +195455,7 @@ class OutboundQueue {
|
|
|
194781
195455
|
this.log = new DurableLog({
|
|
194782
195456
|
file: path363.join(directory, OUTBOUND_FILE),
|
|
194783
195457
|
maxEntries: options.maxEntries ?? OUTBOUND_MAX_ENTRIES,
|
|
194784
|
-
parse:
|
|
195458
|
+
parse: parseEntry6
|
|
194785
195459
|
});
|
|
194786
195460
|
}
|
|
194787
195461
|
load() {
|
|
@@ -194798,7 +195472,7 @@ class OutboundQueue {
|
|
|
194798
195472
|
const entries = [];
|
|
194799
195473
|
parts.forEach((text, index) => {
|
|
194800
195474
|
const entry = {
|
|
194801
|
-
id:
|
|
195475
|
+
id: randomUUID52(),
|
|
194802
195476
|
chatId: message.chatId,
|
|
194803
195477
|
...message.threadId === undefined ? {} : { threadId: message.threadId },
|
|
194804
195478
|
text,
|
|
@@ -196188,6 +196862,7 @@ class RemoteHttpSurface {
|
|
|
196188
196862
|
timers;
|
|
196189
196863
|
keepaliveMs;
|
|
196190
196864
|
ackTimeoutMs;
|
|
196865
|
+
approvalAckMs;
|
|
196191
196866
|
newApprovalId;
|
|
196192
196867
|
newPromptId;
|
|
196193
196868
|
streams = new Map;
|
|
@@ -196196,6 +196871,8 @@ class RemoteHttpSurface {
|
|
|
196196
196871
|
bound = new Map;
|
|
196197
196872
|
pendingAcks = new Map;
|
|
196198
196873
|
approvals = new Map;
|
|
196874
|
+
awaiting = new Map;
|
|
196875
|
+
decided = new Map;
|
|
196199
196876
|
choices = new Map;
|
|
196200
196877
|
finished = new Map;
|
|
196201
196878
|
constructor(options) {
|
|
@@ -196204,6 +196881,7 @@ class RemoteHttpSurface {
|
|
|
196204
196881
|
this.timers = options.timers ?? realTimers;
|
|
196205
196882
|
this.keepaliveMs = options.keepaliveMs ?? DEFAULT_KEEPALIVE_MS;
|
|
196206
196883
|
this.ackTimeoutMs = options.ackTimeoutMs ?? DEFAULT_ACK_TIMEOUT_MS;
|
|
196884
|
+
this.approvalAckMs = options.approvalAckMs ?? DEFAULT_APPROVAL_ACK_MS;
|
|
196207
196885
|
this.newApprovalId = options.approvalIds ?? (() => `ap${randomBytes15(6).toString("hex")}`);
|
|
196208
196886
|
this.newPromptId = options.promptIds ?? (() => `pk${randomBytes15(6).toString("hex")}`);
|
|
196209
196887
|
this.consumer = {
|
|
@@ -196270,6 +196948,8 @@ class RemoteHttpSurface {
|
|
|
196270
196948
|
return this.messageState(hub, request);
|
|
196271
196949
|
case "ack":
|
|
196272
196950
|
return this.ack(hub, request);
|
|
196951
|
+
case "approval-ack":
|
|
196952
|
+
return this.approvalAck(hub, request);
|
|
196273
196953
|
case "stream":
|
|
196274
196954
|
return this.openStream(hub, request, io);
|
|
196275
196955
|
}
|
|
@@ -196288,6 +196968,9 @@ class RemoteHttpSurface {
|
|
|
196288
196968
|
for (const id of [...this.approvals.keys()]) {
|
|
196289
196969
|
this.dropApproval(id);
|
|
196290
196970
|
}
|
|
196971
|
+
for (const id of [...this.awaiting.keys()]) {
|
|
196972
|
+
this.dropAwaiting(id);
|
|
196973
|
+
}
|
|
196291
196974
|
for (const id of [...this.choices.keys()]) {
|
|
196292
196975
|
this.dropChoice(id);
|
|
196293
196976
|
}
|
|
@@ -196611,6 +197294,48 @@ ${asCodeBlock(redactSensitiveText(value.prompt))}`, {
|
|
|
196611
197294
|
this.complete(value.sessionId, value.updateId);
|
|
196612
197295
|
return ok2({ acknowledged: true });
|
|
196613
197296
|
}
|
|
197297
|
+
async approvalAck(hub, request) {
|
|
197298
|
+
const body = await readJsonBody(request);
|
|
197299
|
+
if (!body.ok) {
|
|
197300
|
+
return body.response;
|
|
197301
|
+
}
|
|
197302
|
+
const value = body.value;
|
|
197303
|
+
if (!isSessionId(value.sessionId)) {
|
|
197304
|
+
return invalid2("sessionId must be 1 to 64 characters of letters, digits, '-' and '_'.");
|
|
197305
|
+
}
|
|
197306
|
+
if (!isApprovalId(value.approvalId)) {
|
|
197307
|
+
return invalid2("approvalId must be 'ap' and 12 lowercase hex characters.");
|
|
197308
|
+
}
|
|
197309
|
+
if (!hub.hasSession(value.sessionId)) {
|
|
197310
|
+
return fail4(404, "unknown-session", "This session is not registered for remote control; register again.");
|
|
197311
|
+
}
|
|
197312
|
+
if (value.applied !== undefined && typeof value.applied !== "boolean") {
|
|
197313
|
+
return invalid2("applied must be true or false when present.");
|
|
197314
|
+
}
|
|
197315
|
+
const sessionId = value.sessionId;
|
|
197316
|
+
const id = value.approvalId;
|
|
197317
|
+
const waiting = this.awaiting.get(id);
|
|
197318
|
+
if (waiting !== undefined && waiting.sessionId === sessionId) {
|
|
197319
|
+
this.dropAwaiting(id);
|
|
197320
|
+
this.rememberDecided(id, sessionId);
|
|
197321
|
+
if (value.applied === false) {
|
|
197322
|
+
this.finishApproval(id, sessionId, waiting.messageId, waiting.text, {
|
|
197323
|
+
result: `Not applied at ${waiting.sentAt}: the shell was no longer waiting for this question, so the ${waiting.decision === "allow" ? "Allow" : "Deny"} from ${waiting.who} changed nothing.`,
|
|
197324
|
+
shortReply: "Not applied: the shell was no longer waiting for this question."
|
|
197325
|
+
});
|
|
197326
|
+
return ok2({ acknowledged: true });
|
|
197327
|
+
}
|
|
197328
|
+
this.finishApproval(id, sessionId, waiting.messageId, waiting.text, {
|
|
197329
|
+
result: waiting.decision === "allow" ? `Allowed by ${waiting.who} at ${waiting.sentAt}.` : `Denied by ${waiting.who} at ${waiting.sentAt}.`,
|
|
197330
|
+
shortReply: waiting.decision === "allow" ? "Approval granted." : "Approval denied."
|
|
197331
|
+
});
|
|
197332
|
+
return ok2({ acknowledged: true });
|
|
197333
|
+
}
|
|
197334
|
+
if (this.decided.get(id) === sessionId) {
|
|
197335
|
+
return ok2({ acknowledged: true });
|
|
197336
|
+
}
|
|
197337
|
+
return fail4(404, "unknown-approval", "No approval with that id was sent to this session.");
|
|
197338
|
+
}
|
|
196614
197339
|
openStream(hub, request, io) {
|
|
196615
197340
|
const sessionId = new URL(request.url).searchParams.get("sessionId");
|
|
196616
197341
|
if (!isSessionId(sessionId)) {
|
|
@@ -196767,10 +197492,14 @@ ${asCodeBlock(redactSensitiveText(value.prompt))}`, {
|
|
|
196767
197492
|
});
|
|
196768
197493
|
return;
|
|
196769
197494
|
}
|
|
196770
|
-
|
|
196771
|
-
|
|
196772
|
-
|
|
196773
|
-
|
|
197495
|
+
this.awaiting.set(id, {
|
|
197496
|
+
sessionId,
|
|
197497
|
+
decision: approval.decision,
|
|
197498
|
+
who: `user ${callback.fromId}`,
|
|
197499
|
+
sentAt: when,
|
|
197500
|
+
...messageId === undefined ? {} : { messageId },
|
|
197501
|
+
...entry.text === undefined ? {} : { text: entry.text },
|
|
197502
|
+
timer: this.timers.setTimeout(() => this.giveUpOnAck(id), this.approvalAckMs)
|
|
196774
197503
|
});
|
|
196775
197504
|
return;
|
|
196776
197505
|
}
|
|
@@ -196778,6 +197507,9 @@ ${asCodeBlock(redactSensitiveText(value.prompt))}`, {
|
|
|
196778
197507
|
this.expireApproval(id);
|
|
196779
197508
|
return;
|
|
196780
197509
|
}
|
|
197510
|
+
if (this.awaiting.has(id)) {
|
|
197511
|
+
return;
|
|
197512
|
+
}
|
|
196781
197513
|
const done = this.finished.get(id);
|
|
196782
197514
|
if (done !== undefined) {
|
|
196783
197515
|
if (done.sessionId === sessionId) {
|
|
@@ -196798,7 +197530,7 @@ ${asCodeBlock(redactSensitiveText(value.prompt))}`, {
|
|
|
196798
197530
|
}
|
|
196799
197531
|
}
|
|
196800
197532
|
carriesLivePrompt(messageId) {
|
|
196801
|
-
return [...this.approvals.values(), ...this.choices.values()].some((live) => live.messageId === messageId);
|
|
197533
|
+
return [...this.approvals.values(), ...this.awaiting.values(), ...this.choices.values()].some((live) => live.messageId === messageId);
|
|
196802
197534
|
}
|
|
196803
197535
|
pressChoice(sessionId, callback, choice, stream) {
|
|
196804
197536
|
const id = choice.promptId;
|
|
@@ -196881,7 +197613,7 @@ ${asCodeBlock(redactSensitiveText(value.prompt))}`, {
|
|
|
196881
197613
|
});
|
|
196882
197614
|
}
|
|
196883
197615
|
onApprovalSent(approvalId, sessionId, messageId, text) {
|
|
196884
|
-
const live = this.approvals.get(approvalId);
|
|
197616
|
+
const live = this.approvals.get(approvalId) ?? this.awaiting.get(approvalId);
|
|
196885
197617
|
if (live !== undefined) {
|
|
196886
197618
|
live.messageId = messageId;
|
|
196887
197619
|
live.text = text;
|
|
@@ -196934,6 +197666,37 @@ ${asCodeBlock(redactSensitiveText(value.prompt))}`, {
|
|
|
196934
197666
|
this.timers.clearTimeout(entry.timer);
|
|
196935
197667
|
this.approvals.delete(approvalId);
|
|
196936
197668
|
}
|
|
197669
|
+
dropAwaiting(approvalId) {
|
|
197670
|
+
const entry = this.awaiting.get(approvalId);
|
|
197671
|
+
if (entry === undefined) {
|
|
197672
|
+
return;
|
|
197673
|
+
}
|
|
197674
|
+
this.timers.clearTimeout(entry.timer);
|
|
197675
|
+
this.awaiting.delete(approvalId);
|
|
197676
|
+
}
|
|
197677
|
+
rememberDecided(approvalId, sessionId) {
|
|
197678
|
+
this.decided.delete(approvalId);
|
|
197679
|
+
this.decided.set(approvalId, sessionId);
|
|
197680
|
+
while (this.decided.size > MAX_DECIDED_APPROVALS) {
|
|
197681
|
+
const oldest = this.decided.keys().next().value;
|
|
197682
|
+
if (oldest === undefined) {
|
|
197683
|
+
break;
|
|
197684
|
+
}
|
|
197685
|
+
this.decided.delete(oldest);
|
|
197686
|
+
}
|
|
197687
|
+
}
|
|
197688
|
+
giveUpOnAck(approvalId) {
|
|
197689
|
+
const entry = this.awaiting.get(approvalId);
|
|
197690
|
+
if (entry === undefined) {
|
|
197691
|
+
return;
|
|
197692
|
+
}
|
|
197693
|
+
this.dropAwaiting(approvalId);
|
|
197694
|
+
this.rememberDecided(approvalId, entry.sessionId);
|
|
197695
|
+
this.finishApproval(approvalId, entry.sessionId, entry.messageId, entry.text, {
|
|
197696
|
+
result: `Sent to the shell at ${entry.sentAt}, not confirmed; the shell denies by itself if it did not receive it.`,
|
|
197697
|
+
shortReply: "Sent to the shell, not confirmed; it denies by itself if it did not receive it."
|
|
197698
|
+
});
|
|
197699
|
+
}
|
|
196937
197700
|
expireApproval(approvalId) {
|
|
196938
197701
|
const entry = this.approvals.get(approvalId);
|
|
196939
197702
|
if (entry === undefined) {
|
|
@@ -196970,6 +197733,11 @@ ${asCodeBlock(redactSensitiveText(value.prompt))}`, {
|
|
|
196970
197733
|
}
|
|
196971
197734
|
}
|
|
196972
197735
|
}
|
|
197736
|
+
for (const [id, entry] of [...this.awaiting.entries()]) {
|
|
197737
|
+
if (entry.sessionId === stream.sessionId && !this.closed) {
|
|
197738
|
+
this.giveUpOnAck(id);
|
|
197739
|
+
}
|
|
197740
|
+
}
|
|
196973
197741
|
for (const [id, entry] of [...this.choices.entries()]) {
|
|
196974
197742
|
if (entry.sessionId === stream.sessionId) {
|
|
196975
197743
|
this.dropChoice(id);
|
|
@@ -196995,7 +197763,7 @@ function asCodeBlock(text) {
|
|
|
196995
197763
|
${safe}
|
|
196996
197764
|
${fence}`;
|
|
196997
197765
|
}
|
|
196998
|
-
var MAX_REPLY_CHARS = 20000, MAX_PENDING_APPROVALS_PER_SESSION = 8, MAX_PENDING_CHOICES_PER_SESSION = 8, MAX_REMEMBERED_IDS_PER_SESSION = 512, MAX_PROJECT_NAME_CHARS = 128, CONTROL_CHARS2, JSON_HEADERS2, MAX_FINISHED_PROMPTS = 128, MAX_SETTLED_TEXT_CHARS = 3600, MAX_BACKTICK_RUN = 8;
|
|
197766
|
+
var MAX_REPLY_CHARS = 20000, MAX_PENDING_APPROVALS_PER_SESSION = 8, MAX_PENDING_CHOICES_PER_SESSION = 8, MAX_REMEMBERED_IDS_PER_SESSION = 512, MAX_PROJECT_NAME_CHARS = 128, CONTROL_CHARS2, JSON_HEADERS2, MAX_FINISHED_PROMPTS = 128, MAX_DECIDED_APPROVALS = 256, MAX_SETTLED_TEXT_CHARS = 3600, MAX_BACKTICK_RUN = 8;
|
|
196999
197767
|
var init_http_surface = __esm(() => {
|
|
197000
197768
|
init_service2();
|
|
197001
197769
|
init_hub();
|
|
@@ -197932,7 +198700,7 @@ var init_serve_digest = __esm(() => {
|
|
|
197932
198700
|
});
|
|
197933
198701
|
|
|
197934
198702
|
// src/commands/serve.ts
|
|
197935
|
-
import { randomUUID as
|
|
198703
|
+
import { randomUUID as randomUUID53 } from "crypto";
|
|
197936
198704
|
function parseArgs5(args, valueFlags, booleanFlags) {
|
|
197937
198705
|
const values = new Map;
|
|
197938
198706
|
const flags = new Set;
|
|
@@ -198258,7 +199026,7 @@ function runConfig(args) {
|
|
|
198258
199026
|
return;
|
|
198259
199027
|
}
|
|
198260
199028
|
const credential = readServeCredential();
|
|
198261
|
-
const credentialId = credential.status === "ok" ? credential.record.id :
|
|
199029
|
+
const credentialId = credential.status === "ok" ? credential.record.id : randomUUID53();
|
|
198262
199030
|
const config = defaultServeConfig(credentialId, {
|
|
198263
199031
|
address: parsed.parsed.values.get("--bind") ?? DEFAULT_SERVE_BIND_ADDRESS,
|
|
198264
199032
|
port: port ?? DEFAULT_SERVE_PORT,
|
|
@@ -199932,7 +200700,7 @@ ${settled.stderr}` };
|
|
|
199932
200700
|
var PROBE_TIMEOUT_MS = 1e4;
|
|
199933
200701
|
|
|
199934
200702
|
// src/commands/agents-external.ts
|
|
199935
|
-
import { randomUUID as
|
|
200703
|
+
import { randomUUID as randomUUID54 } from "crypto";
|
|
199936
200704
|
import { mkdir as mkdir73 } from "fs/promises";
|
|
199937
200705
|
import { tmpdir as tmpdir15 } from "os";
|
|
199938
200706
|
import path369 from "path";
|
|
@@ -200219,7 +200987,7 @@ async function runCommand4(args, deps, log) {
|
|
|
200219
200987
|
taskTitle: task.length > 80 ? `${task.slice(0, 77)}...` : task,
|
|
200220
200988
|
taskDescription: task,
|
|
200221
200989
|
acceptanceCriteria: [],
|
|
200222
|
-
worktreeId: `acp-${
|
|
200990
|
+
worktreeId: `acp-${randomUUID54()}`,
|
|
200223
200991
|
maxPromptBytes: gate.config.maxPromptBytes,
|
|
200224
200992
|
timeoutMs,
|
|
200225
200993
|
parentEnv: env,
|
|
@@ -201865,7 +202633,7 @@ var init_metrics2 = __esm(() => {
|
|
|
201865
202633
|
});
|
|
201866
202634
|
|
|
201867
202635
|
// src/metrics/ablation-runner.ts
|
|
201868
|
-
function
|
|
202636
|
+
function median3(values) {
|
|
201869
202637
|
if (values.length === 0)
|
|
201870
202638
|
return null;
|
|
201871
202639
|
const sorted = [...values].sort((a, b) => a - b);
|
|
@@ -201891,7 +202659,7 @@ function toolCallDistribution(samples) {
|
|
|
201891
202659
|
}));
|
|
201892
202660
|
return {
|
|
201893
202661
|
samples: runSamples,
|
|
201894
|
-
median:
|
|
202662
|
+
median: median3(values),
|
|
201895
202663
|
spread: spread(values),
|
|
201896
202664
|
reliability: ABLATION_RELIABILITY
|
|
201897
202665
|
};
|
|
@@ -201901,7 +202669,7 @@ function tokenCost(samples, source) {
|
|
|
201901
202669
|
if (known.length === 0)
|
|
201902
202670
|
return;
|
|
201903
202671
|
const note = `median of ${known.length}/${samples.length} seed samples with a reported token count`;
|
|
201904
|
-
return { tokens: { raw: measured(
|
|
202672
|
+
return { tokens: { raw: measured(median3(known), source, note) } };
|
|
201905
202673
|
}
|
|
201906
202674
|
function successRate(samples) {
|
|
201907
202675
|
const successes = samples.filter((s) => s.success).length;
|
|
@@ -203228,7 +203996,7 @@ function formatFwkExplain(result) {
|
|
|
203228
203996
|
}
|
|
203229
203997
|
|
|
203230
203998
|
// src/commands/workspace.ts
|
|
203231
|
-
import { randomUUID as
|
|
203999
|
+
import { randomUUID as randomUUID55 } from "crypto";
|
|
203232
204000
|
import { writeFile as writeFile51 } from "fs/promises";
|
|
203233
204001
|
function service8() {
|
|
203234
204002
|
return new WorkspaceService({
|
|
@@ -203249,7 +204017,7 @@ async function workspaceCommand(args) {
|
|
|
203249
204017
|
const component = rawComponent === undefined ? undefined : requireWorkspaceReference(rawComponent);
|
|
203250
204018
|
if (!title)
|
|
203251
204019
|
throw new Error("Usage: keryx workspace create --title <title> [--component <workspace-relative-ref>]");
|
|
203252
|
-
const workspace = await service8().create({ request: undefined, requestCorrelationId:
|
|
204020
|
+
const workspace = await service8().create({ request: undefined, requestCorrelationId: randomUUID55(), id: newWorkspaceId(), title, ...component ? { component: { kind: "component", uri: component } } : {} });
|
|
203253
204021
|
console.log(JSON.stringify(workspace, null, 2));
|
|
203254
204022
|
return;
|
|
203255
204023
|
}
|
|
@@ -203279,7 +204047,7 @@ async function workspaceCommand(args) {
|
|
|
203279
204047
|
const revision = optionValue(args, "--revision");
|
|
203280
204048
|
if (!workspaceId || !kind || !rawUri)
|
|
203281
204049
|
throw new Error("Usage: keryx workspace add-resource <workspace-id> --kind <kind> --uri <workspace-relative-ref> [--revision <revision>]");
|
|
203282
|
-
console.log(JSON.stringify(await service8().addResource({ request: undefined, requestCorrelationId:
|
|
204050
|
+
console.log(JSON.stringify(await service8().addResource({ request: undefined, requestCorrelationId: randomUUID55(), workspaceId, resource: { kind, uri: requireWorkspaceReference(rawUri), ...revision ? { revision } : {} } }), null, 2));
|
|
203283
204051
|
return;
|
|
203284
204052
|
}
|
|
203285
204053
|
if (subcommand === "archive") {
|
|
@@ -203287,7 +204055,7 @@ async function workspaceCommand(args) {
|
|
|
203287
204055
|
const workspaceId = args[1];
|
|
203288
204056
|
if (!workspaceId)
|
|
203289
204057
|
throw new Error("Usage: keryx workspace archive <workspace-id>");
|
|
203290
|
-
console.log(JSON.stringify(await service8().archive({ request: undefined, requestCorrelationId:
|
|
204058
|
+
console.log(JSON.stringify(await service8().archive({ request: undefined, requestCorrelationId: randomUUID55(), workspaceId }), null, 2));
|
|
203291
204059
|
return;
|
|
203292
204060
|
}
|
|
203293
204061
|
if (subcommand === "remove-resource") {
|
|
@@ -203296,7 +204064,7 @@ async function workspaceCommand(args) {
|
|
|
203296
204064
|
const uri = optionValue(args, "--uri");
|
|
203297
204065
|
if (!workspaceId || !uri)
|
|
203298
204066
|
throw new Error("Usage: keryx workspace remove-resource <workspace-id> --uri <workspace-relative-ref>");
|
|
203299
|
-
console.log(JSON.stringify(await service8().removeResource({ request: undefined, requestCorrelationId:
|
|
204067
|
+
console.log(JSON.stringify(await service8().removeResource({ request: undefined, requestCorrelationId: randomUUID55(), workspaceId, uri }), null, 2));
|
|
203300
204068
|
return;
|
|
203301
204069
|
}
|
|
203302
204070
|
if (subcommand === "rename") {
|
|
@@ -203305,7 +204073,7 @@ async function workspaceCommand(args) {
|
|
|
203305
204073
|
const title = optionValue(args, "--title");
|
|
203306
204074
|
if (!workspaceId || !title)
|
|
203307
204075
|
throw new Error("Usage: keryx workspace rename <workspace-id> --title <title>");
|
|
203308
|
-
console.log(JSON.stringify(await service8().rename({ request: undefined, requestCorrelationId:
|
|
204076
|
+
console.log(JSON.stringify(await service8().rename({ request: undefined, requestCorrelationId: randomUUID55(), workspaceId, title }), null, 2));
|
|
203309
204077
|
return;
|
|
203310
204078
|
}
|
|
203311
204079
|
if (subcommand === "overview") {
|
|
@@ -203317,7 +204085,7 @@ async function workspaceCommand(args) {
|
|
|
203317
204085
|
const maxTokens = Number(optionValue(args, "--max-tokens") ?? "4096");
|
|
203318
204086
|
if (!Number.isInteger(maxItems) || !Number.isInteger(maxTokens) || maxItems < 0 || maxTokens < 0)
|
|
203319
204087
|
throw new Error("--max-items and --max-tokens must be non-negative integers");
|
|
203320
|
-
const result = await createLocalFwkReadService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId:
|
|
204088
|
+
const result = await createLocalFwkReadService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID55(), budget: { maxItems, maxTokens } });
|
|
203321
204089
|
const normalized = normalizeFwkResult(result);
|
|
203322
204090
|
console.log(JSON.stringify(normalized, null, 2));
|
|
203323
204091
|
if (args.includes("--explain"))
|
|
@@ -203334,7 +204102,7 @@ async function workspaceCommand(args) {
|
|
|
203334
204102
|
const maxTokens = Number(optionValue(args, "--max-tokens") ?? "4096");
|
|
203335
204103
|
if (!Number.isInteger(maxItems) || !Number.isInteger(maxTokens) || maxItems < 0 || maxTokens < 0)
|
|
203336
204104
|
throw new Error("--max-items and --max-tokens must be non-negative integers");
|
|
203337
|
-
const result = await createLocalFwkReadService(process.cwd()).read({ workspaceId, itemId, request: undefined, requestCorrelationId:
|
|
204105
|
+
const result = await createLocalFwkReadService(process.cwd()).read({ workspaceId, itemId, request: undefined, requestCorrelationId: randomUUID55(), budget: { maxItems, maxTokens } });
|
|
203338
204106
|
const normalized = normalizeFwkResult(result);
|
|
203339
204107
|
console.log(JSON.stringify(normalized, null, 2));
|
|
203340
204108
|
if (args.includes("--explain"))
|
|
@@ -203356,14 +204124,14 @@ async function workspaceCommand(args) {
|
|
|
203356
204124
|
const session = findSession(cwd, sessionRef);
|
|
203357
204125
|
if (!session)
|
|
203358
204126
|
throw new Error(`no session matching "${sessionRef}" in this project \u2014 use \`keryx sessions list\``);
|
|
203359
|
-
const proposalId = `proposal-${
|
|
204127
|
+
const proposalId = `proposal-${randomUUID55().replace(/-/g, "").slice(0, 16)}`;
|
|
203360
204128
|
const notePath = proposalNotePath(cwd, workspaceId, proposalId);
|
|
203361
204129
|
const materializedNote = note === undefined ? undefined : prepareOutputForPersistence(await guardOutput({ cwd, content: note, target: "task", source: "generated", path: notePath }), note);
|
|
203362
204130
|
if (materializedNote !== undefined && !materializedNote.allowed)
|
|
203363
204131
|
throw new Error(materializedNote.reason);
|
|
203364
204132
|
const persistedNote = materializedNote?.allowed ? materializedNote.content : undefined;
|
|
203365
204133
|
const { service, wrapUpAuthority, authorizationServer } = createHarnessProposalLifecycleService(cwd, { workspaceId, ...persistedNote ? { note: persistedNote } : {} });
|
|
203366
|
-
const requestCorrelationId =
|
|
204134
|
+
const requestCorrelationId = randomUUID55();
|
|
203367
204135
|
const actor = await authorizationServer.actorContextFor(undefined, requestCorrelationId);
|
|
203368
204136
|
if (!actor)
|
|
203369
204137
|
throw new Error("trusted ActorContext is required");
|
|
@@ -203404,13 +204172,13 @@ async function workspaceCommand(args) {
|
|
|
203404
204172
|
const proposalId = args[2];
|
|
203405
204173
|
const decision = optionValue(args, "--decision");
|
|
203406
204174
|
const reason = optionValue(args, "--reason");
|
|
203407
|
-
const idempotencyKey = optionValue(args, "--idempotency-key") ??
|
|
204175
|
+
const idempotencyKey = optionValue(args, "--idempotency-key") ?? randomUUID55();
|
|
203408
204176
|
const confirmToken = optionValue(args, "--confirm-token");
|
|
203409
204177
|
if (!workspaceId || !proposalId || !decision)
|
|
203410
204178
|
throw new Error("Usage: keryx workspace review <workspace-id> <proposal-id> --decision <accepted|rejected|dismissed> [--reason <reason>] [--idempotency-key <key>] [--confirm-token <token>]");
|
|
203411
204179
|
if (decision === "accepted" && !confirmToken)
|
|
203412
204180
|
throw new Error("--decision accepted requires --confirm-token \u2014 run `keryx workspace confirm-review " + workspaceId + " " + proposalId + "` first");
|
|
203413
|
-
const result = await createHarnessProposalLifecycleService(process.cwd(), { workspaceId }).service.review({ request: undefined, requestCorrelationId:
|
|
204181
|
+
const result = await createHarnessProposalLifecycleService(process.cwd(), { workspaceId }).service.review({ request: undefined, requestCorrelationId: randomUUID55(), workspaceId, proposalId, decision, idempotencyKey, interactive: true, ...reason ? { reason } : {}, ...confirmToken ? { confirmToken } : {} });
|
|
203414
204182
|
console.log(JSON.stringify(normalizeProposalLifecycleResult(result), null, 2));
|
|
203415
204183
|
return;
|
|
203416
204184
|
}
|
|
@@ -203435,7 +204203,7 @@ async function workspaceCommand(args) {
|
|
|
203435
204203
|
const artifactRef = optionValue(args, "--artifact");
|
|
203436
204204
|
if (!workspaceId || !to || !artifactRef)
|
|
203437
204205
|
throw new Error("Usage: keryx workspace handoff <workspace-id> --to <subject> --artifact <ref>");
|
|
203438
|
-
const activity = await createLocalCollaborationService(process.cwd()).record({ request: undefined, requestCorrelationId:
|
|
204206
|
+
const activity = await createLocalCollaborationService(process.cwd()).record({ request: undefined, requestCorrelationId: randomUUID55(), workspaceId, activity: { kind: "handoff-recorded", handoff: { to, artifactRef } } });
|
|
203439
204207
|
console.log(JSON.stringify(activity, null, 2));
|
|
203440
204208
|
return;
|
|
203441
204209
|
}
|
|
@@ -203444,7 +204212,7 @@ async function workspaceCommand(args) {
|
|
|
203444
204212
|
const workspaceId = args[1];
|
|
203445
204213
|
if (!workspaceId)
|
|
203446
204214
|
throw new Error("Usage: keryx workspace collaboration <workspace-id>");
|
|
203447
|
-
console.log(JSON.stringify(await createLocalCollaborationService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId:
|
|
204215
|
+
console.log(JSON.stringify(await createLocalCollaborationService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID55() }), null, 2));
|
|
203448
204216
|
return;
|
|
203449
204217
|
}
|
|
203450
204218
|
if (subcommand === "policy-readiness") {
|
|
@@ -203471,7 +204239,7 @@ async function workspaceCommand(args) {
|
|
|
203471
204239
|
const workspaceId = args[1];
|
|
203472
204240
|
const cwd = process.cwd();
|
|
203473
204241
|
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
203474
|
-
const actor = await authorizationServer.actorContextFor(undefined,
|
|
204242
|
+
const actor = await authorizationServer.actorContextFor(undefined, randomUUID55());
|
|
203475
204243
|
if (!actor)
|
|
203476
204244
|
throw new Error("trusted ActorContext is required");
|
|
203477
204245
|
if (workspaceId) {
|
|
@@ -204614,6 +205382,8 @@ var init_forgetting = __esm(() => {
|
|
|
204614
205382
|
});
|
|
204615
205383
|
|
|
204616
205384
|
// src/commands/decisions.ts
|
|
205385
|
+
import { readFile as readFile190 } from "fs/promises";
|
|
205386
|
+
import path373 from "path";
|
|
204617
205387
|
function rejectUnknownFlags2(sub, args, accepted) {
|
|
204618
205388
|
const unknown = args.filter((argument) => argument.startsWith("--")).map((argument) => argument.split("=")[0]).filter((name) => !accepted.includes(name));
|
|
204619
205389
|
if (unknown.length > 0) {
|
|
@@ -204729,8 +205499,33 @@ async function runReason(args) {
|
|
|
204729
205499
|
}
|
|
204730
205500
|
console.log(recorded ? `decision ${id}: reason recorded` : `decision ${id}: the human was already asked once; nothing written`);
|
|
204731
205501
|
}
|
|
205502
|
+
async function runImport(args) {
|
|
205503
|
+
const file = positionalId(args);
|
|
205504
|
+
if (file === undefined)
|
|
205505
|
+
throw new Error("keryx decisions import needs the file: keryx decisions import <file.jsonl> [--dry-run] [--json]");
|
|
205506
|
+
const rest = args.slice(1);
|
|
205507
|
+
rejectUnknownFlags2("import", rest, IMPORT_FLAGS2);
|
|
205508
|
+
let text;
|
|
205509
|
+
try {
|
|
205510
|
+
text = await readFile190(path373.resolve(process.cwd(), file), "utf8");
|
|
205511
|
+
} catch {
|
|
205512
|
+
throw new Error(`cannot read ${file}`);
|
|
205513
|
+
}
|
|
205514
|
+
const result = await importBackfill(process.cwd(), text, { dryRun: rest.includes("--dry-run") });
|
|
205515
|
+
if (rest.includes("--json")) {
|
|
205516
|
+
console.log(JSON.stringify(result, null, 2));
|
|
205517
|
+
return;
|
|
205518
|
+
}
|
|
205519
|
+
console.log(renderImportResult(result));
|
|
205520
|
+
}
|
|
204732
205521
|
async function runReport3(args) {
|
|
204733
205522
|
rejectUnknownFlags2("report", args, REPORT_FLAGS);
|
|
205523
|
+
if (args.includes("--line") && args.includes("--json"))
|
|
205524
|
+
throw new Error("keryx decisions report: --line and --json cannot be combined");
|
|
205525
|
+
if (args.includes("--line")) {
|
|
205526
|
+
console.log(await reportLine(process.cwd()));
|
|
205527
|
+
return;
|
|
205528
|
+
}
|
|
204734
205529
|
if (args.includes("--json")) {
|
|
204735
205530
|
console.log(JSON.stringify(await loadReport(process.cwd()), null, 2));
|
|
204736
205531
|
return;
|
|
@@ -204752,8 +205547,14 @@ async function decisionsCommand(args = []) {
|
|
|
204752
205547
|
return await runReason(args.slice(1));
|
|
204753
205548
|
if (command === "report")
|
|
204754
205549
|
return await runReport3(args.slice(1));
|
|
205550
|
+
if (command === "import")
|
|
205551
|
+
return await runImport(args.slice(1));
|
|
204755
205552
|
} catch (cause) {
|
|
204756
|
-
|
|
205553
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
205554
|
+
if (command === "import" && args.includes("--json"))
|
|
205555
|
+
console.log(JSON.stringify({ error: message }, null, 2));
|
|
205556
|
+
else
|
|
205557
|
+
console.error(message);
|
|
204757
205558
|
process.exitCode = 1;
|
|
204758
205559
|
return;
|
|
204759
205560
|
}
|
|
@@ -204771,7 +205572,8 @@ Usage:
|
|
|
204771
205572
|
keryx decisions open --question "<text>" --option <id>=<label> [--option ...] [--recommend <id> --reason "<why>"] [--stage <name>] [--flow <id>] [--action <tag>] [--json]
|
|
204772
205573
|
keryx decisions answer <id> --choice <id> [--other] [--reason "<why>"] [--json]
|
|
204773
205574
|
keryx decisions reason <id> --text "<why>" [--json]
|
|
204774
|
-
keryx decisions report [--json]
|
|
205575
|
+
keryx decisions report [--json | --line]
|
|
205576
|
+
keryx decisions import <file.jsonl> [--dry-run] [--json]
|
|
204775
205577
|
|
|
204776
205578
|
Every agent question with options leaves one record: what was asked, what the
|
|
204777
205579
|
agent recommended and why, how it was shown, what the human chose and how long
|
|
@@ -204804,6 +205606,28 @@ it took. Call \`open\` BEFORE showing the question and \`answer\` after.
|
|
|
204804
205606
|
its reason. It also counts the questions that looked irreversible
|
|
204805
205607
|
and the ones where blind was refused because of it, per stage: a
|
|
204806
205608
|
high number on ordinary questions means the list over-matches.
|
|
205609
|
+
Backfilled decisions (see import) are reported in a separate block
|
|
205610
|
+
"\u0434\u043E (\u0438\u0441\u0442\u043E\u0440\u0438\u0447\u0435\u0441\u043A\u043E\u0435, \u0434\u043E\u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043E \u0437\u0430\u0434\u043D\u0438\u043C \u0447\u0438\u0441\u043B\u043E\u043C)" and never counted in
|
|
205611
|
+
the live shares or in the time to answer. --line prints ONE line in
|
|
205612
|
+
Russian for the daily topic message: the total (before, after) and the
|
|
205613
|
+
match share of the visible, the hidden and the historical decisions.
|
|
205614
|
+
import loads historical decisions from a JSON-lines file, one per line:
|
|
205615
|
+
{"id","at","flow","stage","question","options":[{"id","label"}],
|
|
205616
|
+
"recommendation":{"optionId","reason"}|null,"source",
|
|
205617
|
+
"answer":{"choice","other"?}|null,"reason"?}
|
|
205618
|
+
Every one is marked backfilled (its recommendation was written down
|
|
205619
|
+
after the fact), is never blind, and keeps the time it was asked in
|
|
205620
|
+
"at"; its time to answer is unknown and never used. The file is
|
|
205621
|
+
read line by line: a line that is not a decision is skipped, named
|
|
205622
|
+
(line number and why) and counted, and the rest is imported. An id
|
|
205623
|
+
already in the journal is skipped and reported, so the same file can
|
|
205624
|
+
be imported twice; a backfilled decision whose answer is missing from
|
|
205625
|
+
the journal (an interrupted write) gets just that answer on the next
|
|
205626
|
+
import. --dry-run checks and counts without writing. It prints:
|
|
205627
|
+
Imported: N, skipped: S, with recommendation: R, answered: A,
|
|
205628
|
+
deviations: D, plus ", repaired: R" and ", malformed: M" when
|
|
205629
|
+
there are any. With --json a failure is {"error": "..."}. A bare /decisions change never touches a backfilled
|
|
205630
|
+
decision.
|
|
204807
205631
|
|
|
204808
205632
|
The journal is one file per repository, .metaproject/data/decisions/journal.jsonl
|
|
204809
205633
|
under the main checkout (every worktree shares it; it is git-ignored). Inside a
|
|
@@ -204813,14 +205637,15 @@ the answer also adds a line to that flow's journal.md, except for an inferred
|
|
|
204813
205637
|
flow, which only stays in the project-wide journal. A failure here never
|
|
204814
205638
|
stops the question.`);
|
|
204815
205639
|
}
|
|
204816
|
-
var OPEN_FLAGS, ANSWER_FLAGS, REASON_FLAGS, REPORT_FLAGS;
|
|
205640
|
+
var OPEN_FLAGS, ANSWER_FLAGS, REASON_FLAGS, REPORT_FLAGS, IMPORT_FLAGS2;
|
|
204817
205641
|
var init_decisions2 = __esm(() => {
|
|
204818
205642
|
init_args();
|
|
204819
205643
|
init_service13();
|
|
204820
205644
|
OPEN_FLAGS = ["--question", "--option", "--options-json", "--recommend", "--reason", "--stage", "--flow", "--action", "--json"];
|
|
204821
205645
|
ANSWER_FLAGS = ["--choice", "--other", "--reason", "--json"];
|
|
204822
205646
|
REASON_FLAGS = ["--text", "--json"];
|
|
204823
|
-
REPORT_FLAGS = ["--json"];
|
|
205647
|
+
REPORT_FLAGS = ["--json", "--line"];
|
|
205648
|
+
IMPORT_FLAGS2 = ["--dry-run", "--json"];
|
|
204824
205649
|
});
|
|
204825
205650
|
|
|
204826
205651
|
// src/commands/governance.ts
|
|
@@ -205034,7 +205859,7 @@ var init_product = __esm(() => {
|
|
|
205034
205859
|
|
|
205035
205860
|
// src/commands/hooks.ts
|
|
205036
205861
|
import { existsSync as existsSync58, readFileSync as readFileSync32 } from "fs";
|
|
205037
|
-
import
|
|
205862
|
+
import path374 from "path";
|
|
205038
205863
|
function resolveHomeDir(deps) {
|
|
205039
205864
|
return resolveHooksHomeDir(process.env, deps.homeDir);
|
|
205040
205865
|
}
|
|
@@ -205045,10 +205870,10 @@ function optConfigDir(deps) {
|
|
|
205045
205870
|
return deps.configDir !== undefined ? { configDir: deps.configDir } : {};
|
|
205046
205871
|
}
|
|
205047
205872
|
function userHooksPath(homeDir) {
|
|
205048
|
-
return
|
|
205873
|
+
return path374.join(homeDir, ".keryx", "hooks.json");
|
|
205049
205874
|
}
|
|
205050
205875
|
function projectHooksPath(projectRoot) {
|
|
205051
|
-
return
|
|
205876
|
+
return path374.join(projectRoot, ".metaproject", "hooks.json");
|
|
205052
205877
|
}
|
|
205053
205878
|
function fail6(message) {
|
|
205054
205879
|
console.error(message);
|
|
@@ -205166,10 +205991,10 @@ async function runList7(args, deps) {
|
|
|
205166
205991
|
function argv0Resolves(argv0) {
|
|
205167
205992
|
if (argv0 === "keryx")
|
|
205168
205993
|
return true;
|
|
205169
|
-
if (
|
|
205994
|
+
if (path374.isAbsolute(argv0))
|
|
205170
205995
|
return existsSync58(argv0);
|
|
205171
205996
|
const pathEnv = process.env.PATH ?? "";
|
|
205172
|
-
return pathEnv.split(
|
|
205997
|
+
return pathEnv.split(path374.delimiter).filter((dir) => dir.length > 0).some((dir) => existsSync58(path374.join(dir, argv0)));
|
|
205173
205998
|
}
|
|
205174
205999
|
function checkArgvResolutions(registrations) {
|
|
205175
206000
|
const seen = new Set;
|
|
@@ -205623,7 +206448,7 @@ async function writeDocAtomic(filePath, doc, scope, cwd, homeDir) {
|
|
|
205623
206448
|
if (scope === "project") {
|
|
205624
206449
|
await writeContained(cwd, PROJECT_HOOKS_REL, body);
|
|
205625
206450
|
} else {
|
|
205626
|
-
const userConfigDir =
|
|
206451
|
+
const userConfigDir = path374.join(homeDir, ".keryx");
|
|
205627
206452
|
if (!existsSync58(userConfigDir)) {
|
|
205628
206453
|
await mkdirContained(homeDir, ".keryx");
|
|
205629
206454
|
}
|
|
@@ -205938,13 +206763,13 @@ var init_types12 = __esm(() => {
|
|
|
205938
206763
|
});
|
|
205939
206764
|
|
|
205940
206765
|
// src/bundle/paths.ts
|
|
205941
|
-
import { lstat as lstat20, readFile as
|
|
205942
|
-
import
|
|
206766
|
+
import { lstat as lstat20, readFile as readFile191 } from "fs/promises";
|
|
206767
|
+
import path375 from "path";
|
|
205943
206768
|
function scopeRoot(scope, ctx) {
|
|
205944
206769
|
if (scope === "user") {
|
|
205945
206770
|
return userStoreRoot(ctx.env ?? process.env, ctx.homeDir);
|
|
205946
206771
|
}
|
|
205947
|
-
return
|
|
206772
|
+
return path375.join(ctx.projectRoot, ".metaproject");
|
|
205948
206773
|
}
|
|
205949
206774
|
function isPortablePath(relPath) {
|
|
205950
206775
|
return relPath.split("/").every((segment) => PORTABLE_SEGMENT_RE.test(segment));
|
|
@@ -206118,10 +206943,10 @@ async function targetFor(entry, targetScope, ctx) {
|
|
|
206118
206943
|
return { ok: true, absolutePath: containment.absolutePath };
|
|
206119
206944
|
}
|
|
206120
206945
|
async function resolveContainedPathForWrite(root, relPath) {
|
|
206121
|
-
const absolute =
|
|
206122
|
-
const rootResolved =
|
|
206123
|
-
const relative =
|
|
206124
|
-
if (relative === "" || relative === ".." || relative.startsWith(`..${
|
|
206946
|
+
const absolute = path375.resolve(root, relPath);
|
|
206947
|
+
const rootResolved = path375.resolve(root);
|
|
206948
|
+
const relative = path375.relative(rootResolved, absolute);
|
|
206949
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path375.sep}`) || path375.isAbsolute(relative)) {
|
|
206125
206950
|
return {
|
|
206126
206951
|
ok: false,
|
|
206127
206952
|
refusal: { reason: BUNDLE_REFUSAL.pathEscape, path: relPath, message: `${relPath} resolves outside the scope root` }
|
|
@@ -206130,7 +206955,7 @@ async function resolveContainedPathForWrite(root, relPath) {
|
|
|
206130
206955
|
return { ok: true, absolutePath: absolute };
|
|
206131
206956
|
}
|
|
206132
206957
|
async function refuseSymlinkChain(root, relPath) {
|
|
206133
|
-
const rootResolved =
|
|
206958
|
+
const rootResolved = path375.resolve(root);
|
|
206134
206959
|
try {
|
|
206135
206960
|
const rootStat = await lstat20(rootResolved);
|
|
206136
206961
|
if (rootStat.isSymbolicLink()) {
|
|
@@ -206143,7 +206968,7 @@ async function refuseSymlinkChain(root, relPath) {
|
|
|
206143
206968
|
const segments = relPath.split("/");
|
|
206144
206969
|
let current = rootResolved;
|
|
206145
206970
|
for (const segment of segments) {
|
|
206146
|
-
current =
|
|
206971
|
+
current = path375.join(current, segment);
|
|
206147
206972
|
try {
|
|
206148
206973
|
const stat = await lstat20(current);
|
|
206149
206974
|
if (stat.isSymbolicLink()) {
|
|
@@ -206158,7 +206983,7 @@ async function refuseSymlinkChain(root, relPath) {
|
|
|
206158
206983
|
}
|
|
206159
206984
|
async function readTargetFile(absolutePath) {
|
|
206160
206985
|
try {
|
|
206161
|
-
const bytes = await
|
|
206986
|
+
const bytes = await readFile191(absolutePath);
|
|
206162
206987
|
return { ok: true, bytes };
|
|
206163
206988
|
} catch (err) {
|
|
206164
206989
|
const code = err.code;
|
|
@@ -206195,8 +207020,8 @@ var init_paths5 = __esm(() => {
|
|
|
206195
207020
|
});
|
|
206196
207021
|
|
|
206197
207022
|
// src/bundle/archive.ts
|
|
206198
|
-
import { readdir as readdir65, readFile as
|
|
206199
|
-
import
|
|
207023
|
+
import { readdir as readdir65, readFile as readFile192, lstat as lstat21 } from "fs/promises";
|
|
207024
|
+
import path376 from "path";
|
|
206200
207025
|
import { gzipSync, gunzipSync } from "zlib";
|
|
206201
207026
|
function resolveLimits(limits) {
|
|
206202
207027
|
const maxTotalBytes = limits?.maxTotalBytes ?? DEFAULT_BUNDLE_LIMITS.maxTotalBytes;
|
|
@@ -206279,11 +207104,11 @@ function buildBundleArchive(files, manifestBytes) {
|
|
|
206279
207104
|
const dirs = new Set;
|
|
206280
207105
|
const sortedPaths = [...files.keys()].sort();
|
|
206281
207106
|
for (const p of sortedPaths) {
|
|
206282
|
-
let dir =
|
|
207107
|
+
let dir = path376.dirname(p);
|
|
206283
207108
|
const segs = [];
|
|
206284
207109
|
while (dir !== "." && dir !== "/" && dir !== "") {
|
|
206285
207110
|
segs.unshift(dir);
|
|
206286
|
-
dir =
|
|
207111
|
+
dir = path376.dirname(dir);
|
|
206287
207112
|
}
|
|
206288
207113
|
for (const d of segs)
|
|
206289
207114
|
dirs.add(d);
|
|
@@ -206351,7 +207176,7 @@ function parseUstar(tar, limits) {
|
|
|
206351
207176
|
const name = prefix.length > 0 ? `${prefix}/${nameRaw}` : nameRaw;
|
|
206352
207177
|
const typeflag = String.fromCharCode(header[156] ?? 0);
|
|
206353
207178
|
const size = readOctal(header, 124, 12);
|
|
206354
|
-
if (
|
|
207179
|
+
if (path376.isAbsolute(name) || name.split("/").includes("..") || name.length === 0) {
|
|
206355
207180
|
return { ok: false, refusal: { reason: BUNDLE_REFUSAL.archiveInvalid, path: name, message: `unsafe entry name: ${name}` } };
|
|
206356
207181
|
}
|
|
206357
207182
|
const isDir = typeflag === "5";
|
|
@@ -206415,7 +207240,7 @@ async function openBundle(bundlePath, limits) {
|
|
|
206415
207240
|
async function walk(dir, relPrefix) {
|
|
206416
207241
|
const entries = await readdir65(dir, { withFileTypes: true });
|
|
206417
207242
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
206418
|
-
const abs =
|
|
207243
|
+
const abs = path376.join(dir, entry.name);
|
|
206419
207244
|
const rel = relPrefix.length > 0 ? `${relPrefix}/${entry.name}` : entry.name;
|
|
206420
207245
|
const entryStat = await lstat21(abs);
|
|
206421
207246
|
if (entryStat.isSymbolicLink()) {
|
|
@@ -206434,7 +207259,7 @@ async function openBundle(bundlePath, limits) {
|
|
|
206434
207259
|
if (entryStat.size > resolved.maxManifestBytes) {
|
|
206435
207260
|
return { reason: BUNDLE_REFUSAL.archiveTooLarge, path: rel, message: `bundle.json is ${entryStat.size} bytes, over the ${resolved.maxManifestBytes}-byte manifest cap` };
|
|
206436
207261
|
}
|
|
206437
|
-
manifestBytes = await
|
|
207262
|
+
manifestBytes = await readFile192(abs);
|
|
206438
207263
|
continue;
|
|
206439
207264
|
}
|
|
206440
207265
|
entryCount += 1;
|
|
@@ -206445,7 +207270,7 @@ async function openBundle(bundlePath, limits) {
|
|
|
206445
207270
|
if (totalBytes > resolved.maxTotalBytes) {
|
|
206446
207271
|
return { reason: BUNDLE_REFUSAL.archiveTooLarge, message: `bundle directory exceeds ${resolved.maxTotalBytes} bytes total` };
|
|
206447
207272
|
}
|
|
206448
|
-
const bytes = await
|
|
207273
|
+
const bytes = await readFile192(abs);
|
|
206449
207274
|
files.set(rel, bytes);
|
|
206450
207275
|
}
|
|
206451
207276
|
return null;
|
|
@@ -206462,7 +207287,7 @@ async function openBundle(bundlePath, limits) {
|
|
|
206462
207287
|
if (stat.size > resolved.maxCompressedBytes) {
|
|
206463
207288
|
return { ok: false, refusal: { reason: BUNDLE_REFUSAL.archiveTooLarge, message: `${bundlePath} is ${stat.size} bytes, over the ${resolved.maxCompressedBytes}-byte compressed-size cap` } };
|
|
206464
207289
|
}
|
|
206465
|
-
const raw = await
|
|
207290
|
+
const raw = await readFile192(bundlePath);
|
|
206466
207291
|
let tar;
|
|
206467
207292
|
try {
|
|
206468
207293
|
tar = gunzipSync(raw, { maxOutputLength: resolved.maxDecompressedBytes });
|
|
@@ -207292,9 +208117,9 @@ var init_learned_pattern_schema = __esm(() => {
|
|
|
207292
208117
|
|
|
207293
208118
|
// src/bundle/export.ts
|
|
207294
208119
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
207295
|
-
import { mkdir as mkdir75, readdir as readdir66, readFile as
|
|
208120
|
+
import { mkdir as mkdir75, readdir as readdir66, readFile as readFile193, writeFile as writeFile52, lstat as lstat22 } from "fs/promises";
|
|
207296
208121
|
import { existsSync as existsSync59 } from "fs";
|
|
207297
|
-
import
|
|
208122
|
+
import path377 from "path";
|
|
207298
208123
|
function globToRegExp5(glob) {
|
|
207299
208124
|
let out = "";
|
|
207300
208125
|
for (let i = 0;i < glob.length; i += 1) {
|
|
@@ -207377,7 +208202,7 @@ async function walkFiles2(dir, relPrefix, out, skipped) {
|
|
|
207377
208202
|
return;
|
|
207378
208203
|
}
|
|
207379
208204
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
207380
|
-
const abs =
|
|
208205
|
+
const abs = path377.join(dir, entry.name);
|
|
207381
208206
|
const rel = relPrefix.length > 0 ? `${relPrefix}/${entry.name}` : entry.name;
|
|
207382
208207
|
const st = await lstat22(abs);
|
|
207383
208208
|
if (st.isSymbolicLink()) {
|
|
@@ -207394,7 +208219,7 @@ async function walkFiles2(dir, relPrefix, out, skipped) {
|
|
|
207394
208219
|
async function collectSkills(root, scope, skipped) {
|
|
207395
208220
|
const candidates = [];
|
|
207396
208221
|
for (const baseDirName of scope === "project" ? ["skills", "project-skills"] : ["skills"]) {
|
|
207397
|
-
const base =
|
|
208222
|
+
const base = path377.join(root, baseDirName);
|
|
207398
208223
|
if (!existsSync59(base))
|
|
207399
208224
|
continue;
|
|
207400
208225
|
const files = [];
|
|
@@ -207407,7 +208232,7 @@ async function collectSkills(root, scope, skipped) {
|
|
|
207407
208232
|
async function collectRules(root, scope, skipped) {
|
|
207408
208233
|
if (scope === "user")
|
|
207409
208234
|
return [];
|
|
207410
|
-
const base =
|
|
208235
|
+
const base = path377.join(root, "rules");
|
|
207411
208236
|
if (!existsSync59(base))
|
|
207412
208237
|
return [];
|
|
207413
208238
|
const files = [];
|
|
@@ -207415,7 +208240,7 @@ async function collectRules(root, scope, skipped) {
|
|
|
207415
208240
|
return files.filter((f) => /\.(md|mdc)$/.test(f.path)).map((f) => ({ bundlePath: f.path, kind: "rule", absolutePath: f.abs }));
|
|
207416
208241
|
}
|
|
207417
208242
|
async function collectAgents(root, skipped) {
|
|
207418
|
-
const base =
|
|
208243
|
+
const base = path377.join(root, "agents");
|
|
207419
208244
|
if (!existsSync59(base))
|
|
207420
208245
|
return [];
|
|
207421
208246
|
let entries;
|
|
@@ -207426,7 +208251,7 @@ async function collectAgents(root, skipped) {
|
|
|
207426
208251
|
}
|
|
207427
208252
|
const candidates = [];
|
|
207428
208253
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
207429
|
-
const abs =
|
|
208254
|
+
const abs = path377.join(base, entry.name);
|
|
207430
208255
|
const st = await lstat22(abs);
|
|
207431
208256
|
if (st.isSymbolicLink()) {
|
|
207432
208257
|
skipped.push({ path: `agents/${entry.name}`, reason: "symlink" });
|
|
@@ -207441,7 +208266,7 @@ async function collectAgents(root, skipped) {
|
|
|
207441
208266
|
async function collectMemory(root, skipped) {
|
|
207442
208267
|
const candidates = [];
|
|
207443
208268
|
for (const { folder } of MEMORY_TYPES) {
|
|
207444
|
-
const base =
|
|
208269
|
+
const base = path377.join(root, "memory", folder);
|
|
207445
208270
|
if (!existsSync59(base))
|
|
207446
208271
|
continue;
|
|
207447
208272
|
let entries;
|
|
@@ -207451,7 +208276,7 @@ async function collectMemory(root, skipped) {
|
|
|
207451
208276
|
continue;
|
|
207452
208277
|
}
|
|
207453
208278
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
207454
|
-
const abs =
|
|
208279
|
+
const abs = path377.join(base, entry.name);
|
|
207455
208280
|
const st = await lstat22(abs);
|
|
207456
208281
|
if (st.isSymbolicLink()) {
|
|
207457
208282
|
skipped.push({ path: `memory/${folder}/${entry.name}`, reason: "symlink" });
|
|
@@ -207465,7 +208290,7 @@ async function collectMemory(root, skipped) {
|
|
|
207465
208290
|
return candidates;
|
|
207466
208291
|
}
|
|
207467
208292
|
async function collectHookConfig(root) {
|
|
207468
|
-
const abs =
|
|
208293
|
+
const abs = path377.join(root, "hooks.json");
|
|
207469
208294
|
if (!existsSync59(abs))
|
|
207470
208295
|
return [];
|
|
207471
208296
|
const st = await lstat22(abs);
|
|
@@ -207475,7 +208300,7 @@ async function collectHookConfig(root) {
|
|
|
207475
208300
|
}
|
|
207476
208301
|
async function collectLearnedPatterns(root, scope) {
|
|
207477
208302
|
const relDir = scope === "user" ? "learning/patterns" : "data/learning/candidates";
|
|
207478
|
-
const base =
|
|
208303
|
+
const base = path377.join(root, ...relDir.split("/"));
|
|
207479
208304
|
if (!existsSync59(base))
|
|
207480
208305
|
return [];
|
|
207481
208306
|
let entries;
|
|
@@ -207487,7 +208312,7 @@ async function collectLearnedPatterns(root, scope) {
|
|
|
207487
208312
|
const candidates = [];
|
|
207488
208313
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
207489
208314
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
207490
|
-
candidates.push({ bundlePath: `${relDir}/${entry.name}`, kind: "learned-pattern", absolutePath:
|
|
208315
|
+
candidates.push({ bundlePath: `${relDir}/${entry.name}`, kind: "learned-pattern", absolutePath: path377.join(base, entry.name) });
|
|
207491
208316
|
}
|
|
207492
208317
|
}
|
|
207493
208318
|
return candidates;
|
|
@@ -207528,10 +208353,10 @@ async function outDirIsEmpty(out) {
|
|
|
207528
208353
|
}
|
|
207529
208354
|
async function writeDirectoryBundle(out, manifestBytes, files) {
|
|
207530
208355
|
await mkdir75(out, { recursive: true });
|
|
207531
|
-
await writeFile52(
|
|
208356
|
+
await writeFile52(path377.join(out, "bundle.json"), manifestBytes);
|
|
207532
208357
|
for (const [rel, bytes] of files) {
|
|
207533
|
-
const abs =
|
|
207534
|
-
await mkdir75(
|
|
208358
|
+
const abs = path377.join(out, ...rel.split("/"));
|
|
208359
|
+
await mkdir75(path377.dirname(abs), { recursive: true });
|
|
207535
208360
|
await writeFile52(abs, bytes);
|
|
207536
208361
|
}
|
|
207537
208362
|
}
|
|
@@ -207563,7 +208388,7 @@ async function exportBundle(opts) {
|
|
|
207563
208388
|
} else {
|
|
207564
208389
|
const rawDigestEntries = [];
|
|
207565
208390
|
for (const candidate of filtered) {
|
|
207566
|
-
const bytes = await
|
|
208391
|
+
const bytes = await readFile193(candidate.absolutePath);
|
|
207567
208392
|
rawDigestEntries.push({ path: candidate.bundlePath, sha256: sha256Hex5(bytes) });
|
|
207568
208393
|
}
|
|
207569
208394
|
bundleId = contentDigestBundleId(opts.scope, rawDigestEntries);
|
|
@@ -207578,7 +208403,7 @@ async function exportBundle(opts) {
|
|
|
207578
208403
|
skipped.push({ path: candidate.bundlePath, reason: `non-portable-name: ${portable.refusal.message}` });
|
|
207579
208404
|
continue;
|
|
207580
208405
|
}
|
|
207581
|
-
let bytes = await
|
|
208406
|
+
let bytes = await readFile193(candidate.absolutePath);
|
|
207582
208407
|
if (candidate.kind === "hook-config") {
|
|
207583
208408
|
let parsed;
|
|
207584
208409
|
try {
|
|
@@ -207665,7 +208490,7 @@ async function exportBundle(opts) {
|
|
|
207665
208490
|
const archive = buildBundleArchive(files, manifestBytes);
|
|
207666
208491
|
if (!archive.ok)
|
|
207667
208492
|
return { ok: false, refusals: [archive.refusal] };
|
|
207668
|
-
await mkdir75(
|
|
208493
|
+
await mkdir75(path377.dirname(opts.out), { recursive: true });
|
|
207669
208494
|
await writeFile52(opts.out, archive.value);
|
|
207670
208495
|
} else {
|
|
207671
208496
|
if (existsSync59(opts.out) && !await outDirIsEmpty(opts.out)) {
|
|
@@ -207731,16 +208556,16 @@ var init_verify3 = __esm(() => {
|
|
|
207731
208556
|
});
|
|
207732
208557
|
|
|
207733
208558
|
// src/bundle/applied-state.ts
|
|
207734
|
-
import { readFile as
|
|
207735
|
-
import
|
|
208559
|
+
import { readFile as readFile194 } from "fs/promises";
|
|
208560
|
+
import path378 from "path";
|
|
207736
208561
|
function emptyState2() {
|
|
207737
208562
|
return { schemaVersion: APPLIED_STATE_SCHEMA_VERSION, entries: {} };
|
|
207738
208563
|
}
|
|
207739
208564
|
function appliedStatePath(scope, ctx) {
|
|
207740
208565
|
if (scope === "user") {
|
|
207741
|
-
return
|
|
208566
|
+
return path378.join(scopeRoot("user", ctx), "bundles", "applied-state.json");
|
|
207742
208567
|
}
|
|
207743
|
-
return
|
|
208568
|
+
return path378.join(scopeRoot(scope, ctx), "data", "bundles", "applied-state.json");
|
|
207744
208569
|
}
|
|
207745
208570
|
function isPlainEntryShape(value) {
|
|
207746
208571
|
if (typeof value !== "object" || value === null)
|
|
@@ -207799,7 +208624,7 @@ function migrateV1(v1) {
|
|
|
207799
208624
|
async function readAppliedState(ledgerPath) {
|
|
207800
208625
|
let raw;
|
|
207801
208626
|
try {
|
|
207802
|
-
raw = await
|
|
208627
|
+
raw = await readFile194(ledgerPath, "utf8");
|
|
207803
208628
|
} catch (err) {
|
|
207804
208629
|
const code = err.code;
|
|
207805
208630
|
if (code === "ENOENT") {
|
|
@@ -207822,14 +208647,14 @@ async function readAppliedState(ledgerPath) {
|
|
|
207822
208647
|
return { ok: false, reason: "corrupt-ledger", message: `applied-state ledger at ${ledgerPath} has an unrecognized shape` };
|
|
207823
208648
|
}
|
|
207824
208649
|
function containedRootAndRel(ledgerPath) {
|
|
207825
|
-
const marker = `${
|
|
208650
|
+
const marker = `${path378.sep}.metaproject${path378.sep}`;
|
|
207826
208651
|
const idx = ledgerPath.indexOf(marker);
|
|
207827
208652
|
if (idx >= 0) {
|
|
207828
|
-
return { root: ledgerPath.slice(0, idx), rel: ledgerPath.slice(idx +
|
|
208653
|
+
return { root: ledgerPath.slice(0, idx), rel: ledgerPath.slice(idx + path378.sep.length).split(path378.sep).join("/") };
|
|
207829
208654
|
}
|
|
207830
|
-
const bundlesDir =
|
|
207831
|
-
const root =
|
|
207832
|
-
const rel =
|
|
208655
|
+
const bundlesDir = path378.dirname(ledgerPath);
|
|
208656
|
+
const root = path378.dirname(bundlesDir);
|
|
208657
|
+
const rel = path378.join(path378.basename(bundlesDir), path378.basename(ledgerPath)).split(path378.sep).join("/");
|
|
207833
208658
|
return { root, rel };
|
|
207834
208659
|
}
|
|
207835
208660
|
async function writeAppliedState(ledgerPath, state) {
|
|
@@ -207849,14 +208674,14 @@ var init_applied_state = __esm(() => {
|
|
|
207849
208674
|
});
|
|
207850
208675
|
|
|
207851
208676
|
// src/lib/private-dir.ts
|
|
207852
|
-
import { lstat as lstat23, mkdir as mkdir76, open as
|
|
207853
|
-
import
|
|
208677
|
+
import { lstat as lstat23, mkdir as mkdir76, open as open11, readFile as readFile195 } from "fs/promises";
|
|
208678
|
+
import path379 from "path";
|
|
207854
208679
|
async function checkPrivateDirGitignore(dir, root) {
|
|
207855
208680
|
if (root !== undefined) {
|
|
207856
|
-
const rootResolved =
|
|
207857
|
-
const dirResolved =
|
|
207858
|
-
const relative =
|
|
207859
|
-
const relativePosix = relative.split(
|
|
208681
|
+
const rootResolved = path379.resolve(root);
|
|
208682
|
+
const dirResolved = path379.resolve(dir);
|
|
208683
|
+
const relative = path379.relative(rootResolved, dirResolved);
|
|
208684
|
+
const relativePosix = relative.split(path379.sep).join("/");
|
|
207860
208685
|
const refusal = await refuseEscapingSymlink(rootResolved, relativePosix);
|
|
207861
208686
|
if (refusal !== undefined) {
|
|
207862
208687
|
return {
|
|
@@ -207886,7 +208711,7 @@ async function checkPrivateDirGitignore(dir, root) {
|
|
|
207886
208711
|
message: `${dir} is a symlink; keryx will not manage a private directory reached through a symlink.`
|
|
207887
208712
|
};
|
|
207888
208713
|
}
|
|
207889
|
-
const gitignorePath =
|
|
208714
|
+
const gitignorePath = path379.join(dir, ".gitignore");
|
|
207890
208715
|
let stats;
|
|
207891
208716
|
try {
|
|
207892
208717
|
stats = await lstat23(gitignorePath);
|
|
@@ -207916,7 +208741,7 @@ async function checkPrivateDirGitignore(dir, root) {
|
|
|
207916
208741
|
}
|
|
207917
208742
|
let bytes;
|
|
207918
208743
|
try {
|
|
207919
|
-
bytes = await
|
|
208744
|
+
bytes = await readFile195(gitignorePath);
|
|
207920
208745
|
} catch (error) {
|
|
207921
208746
|
return {
|
|
207922
208747
|
ok: false,
|
|
@@ -207938,10 +208763,10 @@ async function ensurePrivateDirGitignore(dir, root) {
|
|
|
207938
208763
|
if (!check.ok || check.action === "present") {
|
|
207939
208764
|
return check;
|
|
207940
208765
|
}
|
|
207941
|
-
const gitignorePath =
|
|
208766
|
+
const gitignorePath = path379.join(dir, ".gitignore");
|
|
207942
208767
|
await mkdir76(dir, { recursive: true });
|
|
207943
208768
|
try {
|
|
207944
|
-
const handle = await
|
|
208769
|
+
const handle = await open11(gitignorePath, "wx");
|
|
207945
208770
|
try {
|
|
207946
208771
|
await handle.writeFile(PRIVATE_DIR_GITIGNORE, "utf8");
|
|
207947
208772
|
} finally {
|
|
@@ -208354,9 +209179,9 @@ var init_plan2 = __esm(() => {
|
|
|
208354
209179
|
});
|
|
208355
209180
|
|
|
208356
209181
|
// src/bundle/audit.ts
|
|
208357
|
-
import { mkdtemp as mkdtemp9, mkdir as mkdir77, rm as
|
|
209182
|
+
import { mkdtemp as mkdtemp9, mkdir as mkdir77, rm as rm24, writeFile as writeFile53 } from "fs/promises";
|
|
208358
209183
|
import { tmpdir as tmpdir16 } from "os";
|
|
208359
|
-
import
|
|
209184
|
+
import path380 from "path";
|
|
208360
209185
|
function entriesToStage(plan) {
|
|
208361
209186
|
return plan.entries.filter((e) => e.bucket === "new" || e.bucket === "update" || e.bucket === "conflict" && e.forced);
|
|
208362
209187
|
}
|
|
@@ -208366,12 +209191,12 @@ async function auditBundlePlan(plan, opts = {}) {
|
|
|
208366
209191
|
return { ok: true, refusals: [], report: null };
|
|
208367
209192
|
}
|
|
208368
209193
|
const runAudit = opts.runAudit ?? runHarnessAudit;
|
|
208369
|
-
const stagingDir = await mkdtemp9(
|
|
209194
|
+
const stagingDir = await mkdtemp9(path380.join(tmpdir16(), "keryx-bundle-audit-"));
|
|
208370
209195
|
try {
|
|
208371
209196
|
try {
|
|
208372
209197
|
for (const entry of toStage) {
|
|
208373
|
-
const abs =
|
|
208374
|
-
await mkdir77(
|
|
209198
|
+
const abs = path380.join(stagingDir, ...entry.path.split("/"));
|
|
209199
|
+
await mkdir77(path380.dirname(abs), { recursive: true });
|
|
208375
209200
|
await writeFile53(abs, entry.bytes);
|
|
208376
209201
|
}
|
|
208377
209202
|
} catch (err) {
|
|
@@ -208396,7 +209221,7 @@ async function auditBundlePlan(plan, opts = {}) {
|
|
|
208396
209221
|
}
|
|
208397
209222
|
return { ok: refusals.length === 0, refusals, report };
|
|
208398
209223
|
} finally {
|
|
208399
|
-
await
|
|
209224
|
+
await rm24(stagingDir, { recursive: true, force: true });
|
|
208400
209225
|
}
|
|
208401
209226
|
}
|
|
208402
209227
|
var init_audit = __esm(() => {
|
|
@@ -208405,8 +209230,8 @@ var init_audit = __esm(() => {
|
|
|
208405
209230
|
});
|
|
208406
209231
|
|
|
208407
209232
|
// src/bundle/apply.ts
|
|
208408
|
-
import { mkdir as mkdir78, rmdir as rmdir2, stat as
|
|
208409
|
-
import
|
|
209233
|
+
import { mkdir as mkdir78, rmdir as rmdir2, stat as stat38 } from "fs/promises";
|
|
209234
|
+
import path381 from "path";
|
|
208410
209235
|
function writableEntries(plan) {
|
|
208411
209236
|
return plan.entries.filter((e) => e.bucket === "new" || e.bucket === "update" || e.bucket === "conflict" && e.forced);
|
|
208412
209237
|
}
|
|
@@ -208477,7 +209302,7 @@ async function applyBundlePlan(plan, auditResult, opts = {}) {
|
|
|
208477
209302
|
throw new ApplyAbort({ ...previousFile.refusal, path: entry.path });
|
|
208478
209303
|
}
|
|
208479
209304
|
const previousBytes = previousFile.bytes ?? null;
|
|
208480
|
-
const createdDirs = await mkdirRecordingCreated(
|
|
209305
|
+
const createdDirs = await mkdirRecordingCreated(path381.dirname(entry.targetPath));
|
|
208481
209306
|
for (const dir of [...createdDirs].reverse())
|
|
208482
209307
|
rollback.push({ kind: "dir", absolutePath: dir });
|
|
208483
209308
|
rollback.push({ kind: "file", root, rel: entry.targetRelative, previousBytes });
|
|
@@ -208560,14 +209385,14 @@ async function applyBundlePlan(plan, auditResult, opts = {}) {
|
|
|
208560
209385
|
}
|
|
208561
209386
|
async function mkdirRecordingCreated(dir) {
|
|
208562
209387
|
const missing = [];
|
|
208563
|
-
let current =
|
|
209388
|
+
let current = path381.resolve(dir);
|
|
208564
209389
|
for (;; ) {
|
|
208565
209390
|
try {
|
|
208566
|
-
await
|
|
209391
|
+
await stat38(current);
|
|
208567
209392
|
break;
|
|
208568
209393
|
} catch {
|
|
208569
209394
|
missing.push(current);
|
|
208570
|
-
const parent =
|
|
209395
|
+
const parent = path381.dirname(current);
|
|
208571
209396
|
if (parent === current)
|
|
208572
209397
|
break;
|
|
208573
209398
|
current = parent;
|
|
@@ -208582,9 +209407,9 @@ function resolveLedgerPathForEntry(entry, opts) {
|
|
|
208582
209407
|
if (entry.targetScope === "user") {
|
|
208583
209408
|
return userStorePaths(opts.env ?? process.env, opts.homeDir).appliedState;
|
|
208584
209409
|
}
|
|
208585
|
-
const metaprojectIdx = entry.targetPath.indexOf(`${
|
|
208586
|
-
const metaprojectRoot = metaprojectIdx === -1 ?
|
|
208587
|
-
return
|
|
209410
|
+
const metaprojectIdx = entry.targetPath.indexOf(`${path381.sep}.metaproject${path381.sep}`);
|
|
209411
|
+
const metaprojectRoot = metaprojectIdx === -1 ? path381.join(path381.dirname(entry.targetPath), ".metaproject") : entry.targetPath.slice(0, metaprojectIdx + `${path381.sep}.metaproject`.length);
|
|
209412
|
+
return path381.join(metaprojectRoot, "data", "bundles", "applied-state.json");
|
|
208588
209413
|
}
|
|
208589
209414
|
var ApplyAbort;
|
|
208590
209415
|
var init_apply4 = __esm(() => {
|
|
@@ -208660,26 +209485,26 @@ var init_inspect = __esm(() => {
|
|
|
208660
209485
|
});
|
|
208661
209486
|
|
|
208662
209487
|
// src/bundle/uninstall.ts
|
|
208663
|
-
import
|
|
209488
|
+
import path382 from "path";
|
|
208664
209489
|
function isContained(root, candidate) {
|
|
208665
|
-
const rel =
|
|
208666
|
-
return rel === "" || !rel.startsWith(`..${
|
|
209490
|
+
const rel = path382.relative(root, candidate);
|
|
209491
|
+
return rel === "" || !rel.startsWith(`..${path382.sep}`) && rel !== ".." && !path382.isAbsolute(rel);
|
|
208667
209492
|
}
|
|
208668
209493
|
async function removeNowEmptyParents(root, relFilePath) {
|
|
208669
|
-
let relDir =
|
|
209494
|
+
let relDir = path382.posix.dirname(relFilePath);
|
|
208670
209495
|
while (relDir !== "." && relDir !== "/" && relDir !== "") {
|
|
208671
209496
|
if (PROTECTED_KIND_ROOTS.has(relDir))
|
|
208672
209497
|
break;
|
|
208673
209498
|
const removed = await rmdirIfEmptyContained(root, relDir).catch(() => false);
|
|
208674
209499
|
if (!removed)
|
|
208675
209500
|
break;
|
|
208676
|
-
relDir =
|
|
209501
|
+
relDir = path382.posix.dirname(relDir);
|
|
208677
209502
|
}
|
|
208678
209503
|
}
|
|
208679
209504
|
async function uninstallBundle(opts) {
|
|
208680
209505
|
const ctx = { projectRoot: opts.projectRoot, env: opts.env, homeDir: opts.homeDir };
|
|
208681
209506
|
const root = scopeRoot(opts.targetScope, ctx);
|
|
208682
|
-
const rootResolved =
|
|
209507
|
+
const rootResolved = path382.resolve(root);
|
|
208683
209508
|
const ledgerPath = appliedStatePath(opts.targetScope, ctx);
|
|
208684
209509
|
const ledgerRead = await readAppliedState(ledgerPath);
|
|
208685
209510
|
if (!ledgerRead.ok) {
|
|
@@ -208719,7 +209544,7 @@ async function uninstallBundle(opts) {
|
|
|
208719
209544
|
if (!kindCheck.ok) {
|
|
208720
209545
|
return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.corruptLedger, path: relPath, message: `applied-state ledger record "${relPath}" does not match its recorded kind "${record.kind}": ${kindCheck.refusal.message}` }], removed: [], kept: [], missing: [] };
|
|
208721
209546
|
}
|
|
208722
|
-
const absolute =
|
|
209547
|
+
const absolute = path382.resolve(rootResolved, normalized.path);
|
|
208723
209548
|
if (!isContained(rootResolved, absolute)) {
|
|
208724
209549
|
return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.corruptLedger, path: relPath, message: `applied-state ledger record "${relPath}" resolves outside the scope root` }], removed: [], kept: [], missing: [] };
|
|
208725
209550
|
}
|
|
@@ -208798,7 +209623,7 @@ var init_service20 = __esm(() => {
|
|
|
208798
209623
|
});
|
|
208799
209624
|
|
|
208800
209625
|
// src/commands/bundle.ts
|
|
208801
|
-
import
|
|
209626
|
+
import path383 from "path";
|
|
208802
209627
|
async function bundleCommand(args, cwd = process.cwd()) {
|
|
208803
209628
|
const sub = args[0];
|
|
208804
209629
|
if (sub === undefined || sub === "--help" || sub === "-h") {
|
|
@@ -208931,7 +209756,7 @@ async function handleExport(args, cwd) {
|
|
|
208931
209756
|
const outcome = await exportBundle({
|
|
208932
209757
|
projectRoot,
|
|
208933
209758
|
scope: scopeArg,
|
|
208934
|
-
out:
|
|
209759
|
+
out: path383.resolve(cwd, out),
|
|
208935
209760
|
include: include.length > 0 ? include : undefined,
|
|
208936
209761
|
kinds: kindsRaw.length > 0 ? kindsRaw : undefined,
|
|
208937
209762
|
bundleId,
|
|
@@ -208976,7 +209801,7 @@ async function handleImport(args, cwd) {
|
|
|
208976
209801
|
}
|
|
208977
209802
|
const projectRoot = resolveBundleProjectRoot(cwd);
|
|
208978
209803
|
if (external) {
|
|
208979
|
-
await handleImportExternal(
|
|
209804
|
+
await handleImportExternal(path383.resolve(cwd, target), projectRoot, { dryRun, json });
|
|
208980
209805
|
return;
|
|
208981
209806
|
}
|
|
208982
209807
|
const targetScopeArg = optionValue([...args], "--target-scope");
|
|
@@ -209003,7 +209828,7 @@ async function handleImport(args, cwd) {
|
|
|
209003
209828
|
return;
|
|
209004
209829
|
}
|
|
209005
209830
|
}
|
|
209006
|
-
const opened = await openBundle(
|
|
209831
|
+
const opened = await openBundle(path383.resolve(cwd, target));
|
|
209007
209832
|
if (!opened.ok) {
|
|
209008
209833
|
printRefusals([opened.refusal], json);
|
|
209009
209834
|
process.exitCode = 1;
|
|
@@ -209181,7 +210006,7 @@ async function handleInspect(args, cwd) {
|
|
|
209181
210006
|
return;
|
|
209182
210007
|
}
|
|
209183
210008
|
const projectRoot = resolveBundleProjectRoot(cwd);
|
|
209184
|
-
const result = await inspectBundle(
|
|
210009
|
+
const result = await inspectBundle(path383.resolve(cwd, bundlePath), {
|
|
209185
210010
|
projectRoot,
|
|
209186
210011
|
targetScope: targetScopeArg
|
|
209187
210012
|
});
|
|
@@ -209225,7 +210050,7 @@ async function handleVerify(args, cwd) {
|
|
|
209225
210050
|
failUsage("keryx bundle verify requires a <bundle> argument (or --external-imports)");
|
|
209226
210051
|
return;
|
|
209227
210052
|
}
|
|
209228
|
-
const outcome = await verifyBundlePath(
|
|
210053
|
+
const outcome = await verifyBundlePath(path383.resolve(cwd, bundlePath));
|
|
209229
210054
|
if (!outcome.ok) {
|
|
209230
210055
|
printRefusals(outcome.refusals, json);
|
|
209231
210056
|
process.exitCode = 1;
|
|
@@ -209368,23 +210193,23 @@ var init_bundle = __esm(() => {
|
|
|
209368
210193
|
|
|
209369
210194
|
// src/commands/learn.ts
|
|
209370
210195
|
import { existsSync as existsSync60 } from "fs";
|
|
209371
|
-
import { readFile as
|
|
210196
|
+
import { readFile as readFile196 } from "fs/promises";
|
|
209372
210197
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
209373
|
-
import
|
|
210198
|
+
import path384 from "path";
|
|
209374
210199
|
function resolveRoot2(deps) {
|
|
209375
210200
|
return resolveProjectRoot2(deps.cwd ?? process.cwd());
|
|
209376
210201
|
}
|
|
209377
210202
|
function resolveLearnRoot(startDir, env) {
|
|
209378
|
-
const hasMetaproject = (dir) => existsSync60(
|
|
210203
|
+
const hasMetaproject = (dir) => existsSync60(path384.join(dir, ".metaproject"));
|
|
209379
210204
|
const fromEnv = env.CLAUDE_PROJECT_DIR;
|
|
209380
210205
|
if (typeof fromEnv === "string" && fromEnv.length > 0 && hasMetaproject(fromEnv)) {
|
|
209381
210206
|
return fromEnv;
|
|
209382
210207
|
}
|
|
209383
|
-
let current =
|
|
210208
|
+
let current = path384.resolve(startDir);
|
|
209384
210209
|
for (;; ) {
|
|
209385
210210
|
if (hasMetaproject(current))
|
|
209386
210211
|
return current;
|
|
209387
|
-
const parent =
|
|
210212
|
+
const parent = path384.dirname(current);
|
|
209388
210213
|
if (parent === current)
|
|
209389
210214
|
return;
|
|
209390
210215
|
current = parent;
|
|
@@ -209480,7 +210305,7 @@ async function runObserveHook(deps) {
|
|
|
209480
210305
|
}
|
|
209481
210306
|
async function countObservationLines(filePath) {
|
|
209482
210307
|
try {
|
|
209483
|
-
const raw = await
|
|
210308
|
+
const raw = await readFile196(filePath, "utf8");
|
|
209484
210309
|
return raw.split(`
|
|
209485
210310
|
`).filter((line) => line.length > 0).length;
|
|
209486
210311
|
} catch {
|
|
@@ -210217,7 +211042,7 @@ var init_group_subcommands = __esm(() => {
|
|
|
210217
211042
|
["trigger", ["run", "install", "uninstall", "list", "status", "schedule", "resolve"]],
|
|
210218
211043
|
["schedule", ["add", "list", "show", "pause", "resume", "run", "remove"]],
|
|
210219
211044
|
["governance", ["report", "show"]],
|
|
210220
|
-
["decisions", ["open", "answer", "reason", "report"]],
|
|
211045
|
+
["decisions", ["open", "answer", "reason", "report", "import"]],
|
|
210221
211046
|
["product", ["index", "open"]],
|
|
210222
211047
|
["hooks", ["list", "validate", "test", "enable", "disable", "trust", "untrust"]],
|
|
210223
211048
|
["bundle", ["export", "import", "inspect", "verify", "uninstall"]],
|
|
@@ -210521,9 +211346,11 @@ var VERSION3, CLI_ROUTES, MCP_KNOWN_SUBCOMMANDS, USAGE_BODY = `Usage:
|
|
|
210521
211346
|
Spend, confirmations, signatures and gate outcomes,
|
|
210522
211347
|
unified across flows; writes latest.md/latest.json
|
|
210523
211348
|
keryx governance show [--json] Reprint the most recently written governance report
|
|
210524
|
-
keryx decisions open|answer|reason|report
|
|
211349
|
+
keryx decisions open|answer|reason|report [--json|--line]|import <file.jsonl> [--dry-run] [--json]
|
|
211350
|
+
Recommendation journal: record a question with options and its
|
|
210525
211351
|
recommendation before showing it, the human's choice after;
|
|
210526
|
-
report = match share by mode and stage, deviations (no model)
|
|
211352
|
+
report = match share by mode and stage, deviations (no model);
|
|
211353
|
+
import = earlier decisions from a JSON-lines file, kept apart
|
|
210527
211354
|
keryx product index [--json] Read every flow and requirements package into a disposable intent index; reports entries with no stated intent
|
|
210528
211355
|
keryx product open [--json] Intents closed in code with no recorded look back, each with its outcome criterion
|
|
210529
211356
|
keryx hooks list [--json] Resolved keryx shell lifecycle hooks (built-in -> user -> project)
|