@mrciphersmith/keryx 0.2.38 → 0.2.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1511 -351
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -191,7 +191,9 @@ __export(exports_fs, {
|
|
|
191
191
|
toPosix: () => toPosix,
|
|
192
192
|
pathExists: () => pathExists,
|
|
193
193
|
isPathInside: () => isPathInside,
|
|
194
|
-
isNotFound: () => isNotFound
|
|
194
|
+
isNotFound: () => isNotFound,
|
|
195
|
+
isLockHeld: () => isLockHeld,
|
|
196
|
+
DEFAULT_LOCK_STALE_MS: () => DEFAULT_LOCK_STALE_MS
|
|
195
197
|
});
|
|
196
198
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
197
199
|
import { access, mkdir, readFile, rename, rm, stat, utimes, writeFile } from "fs/promises";
|
|
@@ -229,7 +231,7 @@ async function writeFileAtomic(filePath, content) {
|
|
|
229
231
|
async function withFileLock2(lockPath, fn, options = {}) {
|
|
230
232
|
const timeoutMs = options.timeoutMs ?? 5000;
|
|
231
233
|
const retryMs = options.retryMs ?? 25;
|
|
232
|
-
const staleMs = options.staleMs ??
|
|
234
|
+
const staleMs = options.staleMs ?? DEFAULT_LOCK_STALE_MS;
|
|
233
235
|
const heartbeatMs = options.heartbeatMs ?? Math.max(100, Math.floor(staleMs / 3));
|
|
234
236
|
const startedAt = Date.now();
|
|
235
237
|
const owner = { pid: process.pid, token: randomUUID4() };
|
|
@@ -270,6 +272,17 @@ async function withFileLock2(lockPath, fn, options = {}) {
|
|
|
270
272
|
}
|
|
271
273
|
}
|
|
272
274
|
}
|
|
275
|
+
async function isLockHeld(lockPath, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
276
|
+
try {
|
|
277
|
+
const stats = await stat(lockPath);
|
|
278
|
+
if (Date.now() - stats.mtimeMs <= staleMs)
|
|
279
|
+
return true;
|
|
280
|
+
const owner = await readLockOwner(path5.join(lockPath, "owner.json"));
|
|
281
|
+
return owner !== undefined && processIsAlive(owner.pid);
|
|
282
|
+
} catch {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
273
286
|
function isAlreadyExistsError(error) {
|
|
274
287
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
275
288
|
}
|
|
@@ -308,6 +321,7 @@ function processIsAlive(pid) {
|
|
|
308
321
|
function delay(ms) {
|
|
309
322
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
310
323
|
}
|
|
324
|
+
var DEFAULT_LOCK_STALE_MS = 30000;
|
|
311
325
|
var init_fs = () => {};
|
|
312
326
|
|
|
313
327
|
// src/lib/git-hooks.ts
|
|
@@ -14016,6 +14030,9 @@ function projectSessionsDir(projectPath, dataDir) {
|
|
|
14016
14030
|
function sessionDir(projectPath, sessionId, dataDir) {
|
|
14017
14031
|
return path8.join(projectSessionsDir(projectPath, dataDir), sessionId);
|
|
14018
14032
|
}
|
|
14033
|
+
function resolveOneShotWrapUpSessionDir(cwd, mintSessionId) {
|
|
14034
|
+
return sessionDir(cwd, mintSessionId());
|
|
14035
|
+
}
|
|
14019
14036
|
|
|
14020
14037
|
// src/harness/process/sandbox/network-run.ts
|
|
14021
14038
|
import { Worker } from "worker_threads";
|
|
@@ -35531,6 +35548,9 @@ function createSacAuthorizationServer(input2) {
|
|
|
35531
35548
|
return actor;
|
|
35532
35549
|
} });
|
|
35533
35550
|
}
|
|
35551
|
+
function isTrustedActorContext(actorContext) {
|
|
35552
|
+
return isRecord2(actorContext) && trustedActors.has(actorContext);
|
|
35553
|
+
}
|
|
35534
35554
|
var roleRank = { viewer: 1, editor: 2, owner: 3 };
|
|
35535
35555
|
function authorizationResult(allowed, code, baseline, required2, workspaceId) {
|
|
35536
35556
|
return { allowed, code, authorizeAtUse: async (resolve2) => {
|
|
@@ -35697,6 +35717,15 @@ class WorkspaceService {
|
|
|
35697
35717
|
async list(input2) {
|
|
35698
35718
|
const actor = await this.requireActor(input2.request, input2.requestCorrelationId);
|
|
35699
35719
|
await this.requireStrict("read");
|
|
35720
|
+
return this.enumerateVisible(actor, input2.includeArchived);
|
|
35721
|
+
}
|
|
35722
|
+
async listForActor(input2) {
|
|
35723
|
+
await this.requireStrict("read");
|
|
35724
|
+
if (!isTrustedActorContext(input2.actorContext))
|
|
35725
|
+
throw new WorkspaceServiceError("access_denied", "untrusted actor");
|
|
35726
|
+
return this.enumerateVisible(input2.actorContext, input2.includeArchived);
|
|
35727
|
+
}
|
|
35728
|
+
async enumerateVisible(actor, includeArchived) {
|
|
35700
35729
|
try {
|
|
35701
35730
|
await mkdir46(this.storageRoot, { recursive: true, mode: 448 });
|
|
35702
35731
|
} catch {
|
|
@@ -35710,7 +35739,7 @@ class WorkspaceService {
|
|
|
35710
35739
|
try {
|
|
35711
35740
|
const manifest = await this.readManifest(entry.name);
|
|
35712
35741
|
const role = currentRole(manifest, actor.subject);
|
|
35713
|
-
if (role && (
|
|
35742
|
+
if (role && (includeArchived === true || manifest.status !== "archived"))
|
|
35714
35743
|
visible.push(manifest);
|
|
35715
35744
|
} catch {}
|
|
35716
35745
|
}
|
|
@@ -35951,6 +35980,25 @@ function localWorkspaceAuthorizationServer(subject = `user:local-${process.getui
|
|
|
35951
35980
|
function newWorkspaceId() {
|
|
35952
35981
|
return `workspace-${randomUUID7().replace(/-/g, "").slice(0, 16)}`;
|
|
35953
35982
|
}
|
|
35983
|
+
async function resolveWorkspaceForActor(cwd, workspaceId) {
|
|
35984
|
+
const service4 = new WorkspaceService({
|
|
35985
|
+
workspaceRoot: cwd,
|
|
35986
|
+
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
35987
|
+
strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }
|
|
35988
|
+
});
|
|
35989
|
+
try {
|
|
35990
|
+
const manifest = await service4.show({ request: undefined, requestCorrelationId: randomUUID7(), workspaceId });
|
|
35991
|
+
return { ok: true, manifest };
|
|
35992
|
+
} catch (error2) {
|
|
35993
|
+
if (error2 instanceof WorkspaceServiceError) {
|
|
35994
|
+
return { ok: false, error: error2 };
|
|
35995
|
+
}
|
|
35996
|
+
return {
|
|
35997
|
+
ok: false,
|
|
35998
|
+
error: new WorkspaceServiceError("not_found", error2 instanceof Error ? error2.message : "workspace could not be resolved")
|
|
35999
|
+
};
|
|
36000
|
+
}
|
|
36001
|
+
}
|
|
35954
36002
|
|
|
35955
36003
|
// src/sac/fwk-service.ts
|
|
35956
36004
|
init_store2();
|
|
@@ -36706,7 +36754,7 @@ function normalizeFwkResult(result) {
|
|
|
36706
36754
|
// src/sac/proposal-lifecycle.ts
|
|
36707
36755
|
init_fs();
|
|
36708
36756
|
import { createHash as createHash18, randomUUID as randomUUID11 } from "crypto";
|
|
36709
|
-
import { appendFile as appendFile5, mkdir as mkdir50, readFile as readFile69 } from "fs/promises";
|
|
36757
|
+
import { appendFile as appendFile5, mkdir as mkdir50, readdir as readdir20, readFile as readFile69 } from "fs/promises";
|
|
36710
36758
|
import path124 from "path";
|
|
36711
36759
|
|
|
36712
36760
|
// src/sac/trusted-wrap-up.ts
|
|
@@ -36945,7 +36993,9 @@ function readSummaryFile(file) {
|
|
|
36945
36993
|
compactCount: typeof o.compactCount === "number" ? o.compactCount : 0,
|
|
36946
36994
|
...typeof o.provider === "string" ? { provider: o.provider } : {},
|
|
36947
36995
|
...typeof o.model === "string" ? { model: o.model } : {},
|
|
36948
|
-
...typeof o.parentSessionId === "string" ? { parentSessionId: o.parentSessionId } : {}
|
|
36996
|
+
...typeof o.parentSessionId === "string" ? { parentSessionId: o.parentSessionId } : {},
|
|
36997
|
+
...o.runMode === "interactive" || o.runMode === "unattended" ? { runMode: o.runMode } : {},
|
|
36998
|
+
...o.courseStatus === "unbound" || o.courseStatus === "active" || o.courseStatus === "blocked" || o.courseStatus === "done" ? { courseStatus: o.courseStatus } : {}
|
|
36949
36999
|
};
|
|
36950
37000
|
} catch {
|
|
36951
37001
|
return;
|
|
@@ -37703,6 +37753,51 @@ class ProposalLifecycleService {
|
|
|
37703
37753
|
});
|
|
37704
37754
|
} });
|
|
37705
37755
|
}
|
|
37756
|
+
async listProposedProposals(workspaceId) {
|
|
37757
|
+
const proposalsDir = path124.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals");
|
|
37758
|
+
let entries;
|
|
37759
|
+
try {
|
|
37760
|
+
entries = (await readdir20(proposalsDir)).filter((name) => name.endsWith(".json"));
|
|
37761
|
+
} catch (error2) {
|
|
37762
|
+
if (isNotFound(error2))
|
|
37763
|
+
return [];
|
|
37764
|
+
throw error2;
|
|
37765
|
+
}
|
|
37766
|
+
const proposals = [];
|
|
37767
|
+
for (const entry of entries) {
|
|
37768
|
+
try {
|
|
37769
|
+
const parsed = JSON.parse(await readFile69(path124.join(proposalsDir, entry), "utf8"));
|
|
37770
|
+
if (parsed.recordType === "proposal-created")
|
|
37771
|
+
proposals.push(parsed);
|
|
37772
|
+
} catch {}
|
|
37773
|
+
}
|
|
37774
|
+
const terminalIds = new Set((await this.records(this.ledgerPath(workspaceId))).filter((record) => record.recordType === "proposal-transition").map((record) => record.proposalId));
|
|
37775
|
+
return proposals.filter((proposal) => !terminalIds.has(proposal.id));
|
|
37776
|
+
}
|
|
37777
|
+
async listVisibleProposedProposals(actor) {
|
|
37778
|
+
const workspaces = await this.options.workspaces.listForActor({ actorContext: actor, includeArchived: true });
|
|
37779
|
+
const groups = [];
|
|
37780
|
+
for (const workspace of workspaces) {
|
|
37781
|
+
const proposals = await this.listProposedProposals(workspace.id);
|
|
37782
|
+
if (proposals.length > 0)
|
|
37783
|
+
groups.push({ workspace, proposals });
|
|
37784
|
+
}
|
|
37785
|
+
return groups;
|
|
37786
|
+
}
|
|
37787
|
+
async isEvidenceFresh(proposal, _actor) {
|
|
37788
|
+
const readEvidenceFile = this.options.readEvidenceFile ?? readWorkspaceFileNoFollow;
|
|
37789
|
+
for (const item of proposal.evidence) {
|
|
37790
|
+
try {
|
|
37791
|
+
const resolved = await resolveWorkspaceReference({ workspaceRoot: this.root, kind: item.kind, uri: item.uri });
|
|
37792
|
+
const content = readEvidenceFile(this.root, resolved).toString("utf8");
|
|
37793
|
+
if (hash(content) !== item.revision)
|
|
37794
|
+
return false;
|
|
37795
|
+
} catch {
|
|
37796
|
+
return false;
|
|
37797
|
+
}
|
|
37798
|
+
}
|
|
37799
|
+
return true;
|
|
37800
|
+
}
|
|
37706
37801
|
async targetWriteOrStale(proposal, actor, reviewerAuthority, input2, approvalRef, writeIntent, policyRevision) {
|
|
37707
37802
|
await this.options.beforeTargetWrite?.();
|
|
37708
37803
|
try {
|
|
@@ -37940,14 +38035,23 @@ function recordHash(value) {
|
|
|
37940
38035
|
function isPlatformUnavailableSecureReadError(error2) {
|
|
37941
38036
|
return error2 instanceof Error && error2.message === "safe descriptor source reads are unavailable on this platform";
|
|
37942
38037
|
}
|
|
37943
|
-
function
|
|
38038
|
+
function createLocalProposalLifecycleService(cwd) {
|
|
37944
38039
|
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
37945
38040
|
const workspaces = new WorkspaceService({ workspaceRoot: cwd, authorizationServer, strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" } });
|
|
38041
|
+
return new ProposalLifecycleService({ workspaceRoot: cwd, workspaces, authorizationServer, guard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }, policyRef: "./security/policy/local", policyRevision: "local-offline-v1", targetWriters: createLocalOwnerWriterAdapters(), wrapUpAuthority: createTrustedWrapUpAuthority({ now: () => new Date(0), resolveExplicitWrapUp: async () => {
|
|
38042
|
+
throw new Error("trusted wrap-up boundary unavailable");
|
|
38043
|
+
} }) });
|
|
38044
|
+
}
|
|
38045
|
+
function createHarnessProposalLifecycleService(cwd, opts) {
|
|
38046
|
+
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
38047
|
+
const nowOpt = opts.now !== undefined ? { now: opts.now } : {};
|
|
38048
|
+
const workspaces = new WorkspaceService({ workspaceRoot: cwd, authorizationServer, strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }, ...nowOpt });
|
|
37946
38049
|
const wrapUpAuthority = createTrustedWrapUpAuthority({
|
|
38050
|
+
...nowOpt,
|
|
37947
38051
|
resolveExplicitWrapUp: async (request) => {
|
|
37948
38052
|
if (request.source !== "session")
|
|
37949
38053
|
throw new Error(`this composition only resolves "session" wrap-ups, got "${request.source}"`);
|
|
37950
|
-
return resolveSessionWrapUp({ cwd, workspaceId: opts.workspaceId, sourceRef: request.sourceRef });
|
|
38054
|
+
return resolveSessionWrapUp({ cwd, workspaceId: opts.workspaceId, sourceRef: request.sourceRef, ...nowOpt });
|
|
37951
38055
|
}
|
|
37952
38056
|
});
|
|
37953
38057
|
const noteOpt = opts.note !== undefined ? { note: opts.note } : {};
|
|
@@ -37957,7 +38061,7 @@ function createHarnessProposalLifecycleService(cwd, opts) {
|
|
|
37957
38061
|
wiki: createWikiGuardedTargetWriter(createRealWikiOwnerWriter(cwd, noteOpt)),
|
|
37958
38062
|
skill: createSkillGuardedTargetWriter(createRealSkillOwnerWriter(cwd, noteOpt))
|
|
37959
38063
|
};
|
|
37960
|
-
const service4 = new ProposalLifecycleService({ workspaceRoot: cwd, workspaces, authorizationServer, guard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }, policyRef: "./security/policy/local", policyRevision: "local-offline-v1", targetWriters, wrapUpAuthority });
|
|
38064
|
+
const service4 = new ProposalLifecycleService({ workspaceRoot: cwd, workspaces, authorizationServer, guard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }, policyRef: "./security/policy/local", policyRevision: "local-offline-v1", targetWriters, wrapUpAuthority, ...nowOpt });
|
|
37961
38065
|
return { service: service4, wrapUpAuthority, authorizationServer };
|
|
37962
38066
|
}
|
|
37963
38067
|
function createWikiGuardedTargetWriter(input2) {
|
|
@@ -38361,7 +38465,7 @@ function buildToolRegistry() {
|
|
|
38361
38465
|
// src/mcp/resources.ts
|
|
38362
38466
|
init_fs();
|
|
38363
38467
|
import path126 from "path";
|
|
38364
|
-
import { readdir as
|
|
38468
|
+
import { readdir as readdir21, readFile as readFile72, stat as stat7 } from "fs/promises";
|
|
38365
38469
|
var URI_PREFIX = "metaproject://";
|
|
38366
38470
|
function mimeForPath(filePath) {
|
|
38367
38471
|
if (filePath.endsWith(".json") || filePath.endsWith(".jsonl")) {
|
|
@@ -38388,7 +38492,7 @@ async function walkFiles(root) {
|
|
|
38388
38492
|
const out = [];
|
|
38389
38493
|
let entries;
|
|
38390
38494
|
try {
|
|
38391
|
-
entries = await
|
|
38495
|
+
entries = await readdir21(root, { withFileTypes: true });
|
|
38392
38496
|
} catch {
|
|
38393
38497
|
return [];
|
|
38394
38498
|
}
|
|
@@ -38410,7 +38514,7 @@ async function listArtifacts(cwd) {
|
|
|
38410
38514
|
const listings = [];
|
|
38411
38515
|
let modules;
|
|
38412
38516
|
try {
|
|
38413
|
-
modules = await
|
|
38517
|
+
modules = await readdir21(base, { withFileTypes: true });
|
|
38414
38518
|
} catch {
|
|
38415
38519
|
return [];
|
|
38416
38520
|
}
|
|
@@ -38840,8 +38944,8 @@ Reports the workspace root and one enabled/disabled line per module. Use
|
|
|
38840
38944
|
|
|
38841
38945
|
// src/commands/harness.ts
|
|
38842
38946
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
38843
|
-
import
|
|
38844
|
-
import { randomUUID as
|
|
38947
|
+
import path134 from "path";
|
|
38948
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
38845
38949
|
|
|
38846
38950
|
// src/security/harness-scan.ts
|
|
38847
38951
|
init_guard();
|
|
@@ -40782,6 +40886,427 @@ function resolveAllowedDomains(env, projectRoot) {
|
|
|
40782
40886
|
|
|
40783
40887
|
// src/commands/harness.ts
|
|
40784
40888
|
init_providers();
|
|
40889
|
+
|
|
40890
|
+
// src/sac/machine-wrap-up.ts
|
|
40891
|
+
init_fs();
|
|
40892
|
+
import { createHash as createHash27, randomUUID as randomUUID14 } from "crypto";
|
|
40893
|
+
import { execFile } from "child_process";
|
|
40894
|
+
import { mkdir as mkdir53 } from "fs/promises";
|
|
40895
|
+
import path133 from "path";
|
|
40896
|
+
import { promisify } from "util";
|
|
40897
|
+
|
|
40898
|
+
// src/session/slate.ts
|
|
40899
|
+
init_fs();
|
|
40900
|
+
import { mkdir as mkdir52, readFile as readFile73, rm as rm7 } from "fs/promises";
|
|
40901
|
+
import path132 from "path";
|
|
40902
|
+
init_repomap();
|
|
40903
|
+
init_redact();
|
|
40904
|
+
function slatePath(dir) {
|
|
40905
|
+
return path132.join(dir, "slate.json");
|
|
40906
|
+
}
|
|
40907
|
+
function slateLockPath(dir) {
|
|
40908
|
+
return `${slatePath(dir)}.lock`;
|
|
40909
|
+
}
|
|
40910
|
+
async function readSlate(dir) {
|
|
40911
|
+
try {
|
|
40912
|
+
const raw = await readFile73(slatePath(dir), "utf8");
|
|
40913
|
+
return JSON.parse(raw);
|
|
40914
|
+
} catch (error2) {
|
|
40915
|
+
if (isNotFound(error2))
|
|
40916
|
+
return;
|
|
40917
|
+
throw error2;
|
|
40918
|
+
}
|
|
40919
|
+
}
|
|
40920
|
+
async function writeSlate(dir, update) {
|
|
40921
|
+
await mkdir52(dir, { recursive: true });
|
|
40922
|
+
return withFileLock2(slateLockPath(dir), async () => {
|
|
40923
|
+
const prev = await readSlate(dir);
|
|
40924
|
+
const next = update(prev);
|
|
40925
|
+
await writeFileAtomic(slatePath(dir), `${JSON.stringify(next, null, 2)}
|
|
40926
|
+
`);
|
|
40927
|
+
return next;
|
|
40928
|
+
});
|
|
40929
|
+
}
|
|
40930
|
+
async function archiveSlate(dir, attemptId) {
|
|
40931
|
+
if (!/^[A-Za-z0-9._-]+$/.test(attemptId))
|
|
40932
|
+
throw new Error(`invalid attemptId: ${JSON.stringify(attemptId)}`);
|
|
40933
|
+
await mkdir52(dir, { recursive: true });
|
|
40934
|
+
return withFileLock2(slateLockPath(dir), () => archiveIfExistsLocked(dir, attemptId));
|
|
40935
|
+
}
|
|
40936
|
+
async function archiveIfExistsLocked(dir, attemptId) {
|
|
40937
|
+
let raw;
|
|
40938
|
+
try {
|
|
40939
|
+
raw = await readFile73(slatePath(dir), "utf8");
|
|
40940
|
+
} catch (error2) {
|
|
40941
|
+
if (isNotFound(error2))
|
|
40942
|
+
return;
|
|
40943
|
+
throw error2;
|
|
40944
|
+
}
|
|
40945
|
+
const archiveDir = path132.join(dir, "slate-archive");
|
|
40946
|
+
await mkdir52(archiveDir, { recursive: true });
|
|
40947
|
+
await writeFileAtomic(path132.join(archiveDir, `${attemptId}.json`), raw);
|
|
40948
|
+
await rm7(slatePath(dir), { force: true });
|
|
40949
|
+
}
|
|
40950
|
+
async function openSlateAtomic(dir, mintAttemptId, build) {
|
|
40951
|
+
await mkdir52(dir, { recursive: true });
|
|
40952
|
+
return withFileLock2(slateLockPath(dir), async () => {
|
|
40953
|
+
const existing = await readSlate(dir);
|
|
40954
|
+
if (existing !== undefined) {
|
|
40955
|
+
await archiveIfExistsLocked(dir, mintAttemptId());
|
|
40956
|
+
}
|
|
40957
|
+
const next = build();
|
|
40958
|
+
await writeFileAtomic(slatePath(dir), `${JSON.stringify(next, null, 2)}
|
|
40959
|
+
`);
|
|
40960
|
+
await rm7(path132.join(dir, "terminal-state.json"), { force: true });
|
|
40961
|
+
return next;
|
|
40962
|
+
});
|
|
40963
|
+
}
|
|
40964
|
+
async function appendSeed(dir, seed) {
|
|
40965
|
+
return writeSlate(dir, (prev) => {
|
|
40966
|
+
if (!prev)
|
|
40967
|
+
throw new Error(`appendSeed: no open slate in ${dir}`);
|
|
40968
|
+
return { ...prev, seeds: [...prev.seeds, seed] };
|
|
40969
|
+
});
|
|
40970
|
+
}
|
|
40971
|
+
function dedupeSeeds(seeds) {
|
|
40972
|
+
const seen = new Set;
|
|
40973
|
+
const result = [];
|
|
40974
|
+
for (const seed of seeds) {
|
|
40975
|
+
const key = seed.text.trim();
|
|
40976
|
+
if (seen.has(key))
|
|
40977
|
+
continue;
|
|
40978
|
+
seen.add(key);
|
|
40979
|
+
result.push(seed);
|
|
40980
|
+
}
|
|
40981
|
+
return result;
|
|
40982
|
+
}
|
|
40983
|
+
var DEFAULT_RENDER_MAX_TOKENS = 2000;
|
|
40984
|
+
function redactAndBoundTouched(touched, budget, ctx) {
|
|
40985
|
+
const recentFirstRedacted = [...touched].reverse().map((entry) => redactSensitiveText(entry));
|
|
40986
|
+
const candidates = recentFirstRedacted.map((text, i) => ({
|
|
40987
|
+
id: `touched:${i}`,
|
|
40988
|
+
required: false,
|
|
40989
|
+
tokens: estimateTokens(text)
|
|
40990
|
+
}));
|
|
40991
|
+
const assembly = assembleContext({
|
|
40992
|
+
candidates,
|
|
40993
|
+
maxItems: candidates.length,
|
|
40994
|
+
maxTokens: Math.max(0, budget),
|
|
40995
|
+
...ctx
|
|
40996
|
+
});
|
|
40997
|
+
const selectedIds = new Set("code" in assembly ? [] : assembly.selected);
|
|
40998
|
+
return recentFirstRedacted.map((text, i) => ({ id: `touched:${i}`, text })).filter((entry) => selectedIds.has(entry.id)).reverse().map((entry) => entry.text);
|
|
40999
|
+
}
|
|
41000
|
+
function renderAnchorsBlock(anchors, opts) {
|
|
41001
|
+
const maxTokens = opts?.maxTokens ?? DEFAULT_RENDER_MAX_TOKENS;
|
|
41002
|
+
const rootLine = `root: ${anchors.root}`;
|
|
41003
|
+
const treeLine = anchors.tree !== undefined ? `tree: ${anchors.tree}` : undefined;
|
|
41004
|
+
const runtimeLine = anchors.runtime !== undefined ? `runtime: ${anchors.runtime.provider}/${anchors.runtime.model}` : undefined;
|
|
41005
|
+
const headEntries = [{ id: "root", text: rootLine, required: true }];
|
|
41006
|
+
if (treeLine !== undefined)
|
|
41007
|
+
headEntries.push({ id: "tree", text: treeLine, required: false });
|
|
41008
|
+
if (runtimeLine !== undefined)
|
|
41009
|
+
headEntries.push({ id: "runtime", text: runtimeLine, required: false });
|
|
41010
|
+
const headCandidates = headEntries.map((entry) => ({
|
|
41011
|
+
id: entry.id,
|
|
41012
|
+
required: entry.required,
|
|
41013
|
+
tokens: estimateTokens(entry.text)
|
|
41014
|
+
}));
|
|
41015
|
+
const headAssembly = assembleContext({
|
|
41016
|
+
candidates: headCandidates,
|
|
41017
|
+
maxItems: headCandidates.length,
|
|
41018
|
+
maxTokens,
|
|
41019
|
+
traceRef: "slate-anchors",
|
|
41020
|
+
configurationRevision: "slate-anchors-v1",
|
|
41021
|
+
policyRef: "slate-anchors",
|
|
41022
|
+
policyRevision: "v1"
|
|
41023
|
+
});
|
|
41024
|
+
const headSelectedIds = "code" in headAssembly ? new Set(["root"]) : new Set(headAssembly.selected);
|
|
41025
|
+
const headTokensUsed = "code" in headAssembly ? 0 : headCandidates.filter((c) => headSelectedIds.has(c.id)).reduce((sum, c) => sum + c.tokens, 0);
|
|
41026
|
+
const touchedBudget = "code" in headAssembly ? 0 : Math.max(0, maxTokens - headTokensUsed);
|
|
41027
|
+
const touchedLines = redactAndBoundTouched(anchors.touched, touchedBudget, {
|
|
41028
|
+
traceRef: "slate-anchors-touched",
|
|
41029
|
+
configurationRevision: "slate-anchors-v1",
|
|
41030
|
+
policyRef: "slate-anchors",
|
|
41031
|
+
policyRevision: "v1"
|
|
41032
|
+
}).map((text) => `- ${text}`);
|
|
41033
|
+
const lines = ["Anchors:", rootLine];
|
|
41034
|
+
if (treeLine !== undefined && headSelectedIds.has("tree"))
|
|
41035
|
+
lines.push(treeLine);
|
|
41036
|
+
if (runtimeLine !== undefined && headSelectedIds.has("runtime"))
|
|
41037
|
+
lines.push(runtimeLine);
|
|
41038
|
+
if (touchedLines.length > 0) {
|
|
41039
|
+
lines.push("touched:", ...touchedLines);
|
|
41040
|
+
}
|
|
41041
|
+
return lines.join(`
|
|
41042
|
+
`);
|
|
41043
|
+
}
|
|
41044
|
+
|
|
41045
|
+
// src/session/slate-course.ts
|
|
41046
|
+
init_store2();
|
|
41047
|
+
var unbound = { state: "unbound" };
|
|
41048
|
+
async function readCourse(cwd, flowRef) {
|
|
41049
|
+
if (!flowRef)
|
|
41050
|
+
return unbound;
|
|
41051
|
+
try {
|
|
41052
|
+
const dir = await resolveFlowDir(cwd, flowRef);
|
|
41053
|
+
const flow = await readFlow(cwd, dir);
|
|
41054
|
+
return { state: "bound", ...deriveFlowWork(flow, flowRef) };
|
|
41055
|
+
} catch {
|
|
41056
|
+
return unbound;
|
|
41057
|
+
}
|
|
41058
|
+
}
|
|
41059
|
+
async function courseFromSlate(cwd, slate) {
|
|
41060
|
+
return readCourse(cwd, slate?.course.flowRef);
|
|
41061
|
+
}
|
|
41062
|
+
|
|
41063
|
+
// src/sac/machine-wrap-up.ts
|
|
41064
|
+
init_single_turn();
|
|
41065
|
+
var execFileAsync = promisify(execFile);
|
|
41066
|
+
var WRAP_UP_TTL_MS2 = 60 * 60 * 1000;
|
|
41067
|
+
var DEFAULT_MODEL_TURN_TIMEOUT_MS = 30000;
|
|
41068
|
+
function describeSource(source) {
|
|
41069
|
+
return source === "parent" ? "parent" : `child:${source.childDispatchId}`;
|
|
41070
|
+
}
|
|
41071
|
+
function dedupedAttributedSeeds(slate) {
|
|
41072
|
+
const seen = new Set;
|
|
41073
|
+
const result = [];
|
|
41074
|
+
const take = (seeds, source) => {
|
|
41075
|
+
for (const seed of dedupeSeeds(seeds)) {
|
|
41076
|
+
const key = seed.text.trim();
|
|
41077
|
+
if (seen.has(key))
|
|
41078
|
+
continue;
|
|
41079
|
+
seen.add(key);
|
|
41080
|
+
result.push({ text: seed.text, kind: seed.kind ?? "follow-up", source });
|
|
41081
|
+
}
|
|
41082
|
+
};
|
|
41083
|
+
take(slate.seeds, "parent");
|
|
41084
|
+
const childDispatches = slate.childDispatches ?? {};
|
|
41085
|
+
for (const [dispatchId, dispatch] of Object.entries(childDispatches)) {
|
|
41086
|
+
take(dispatch.seeds, { childDispatchId: dispatchId });
|
|
41087
|
+
}
|
|
41088
|
+
return result;
|
|
41089
|
+
}
|
|
41090
|
+
function groupSeedsByKind(slate) {
|
|
41091
|
+
const map = new Map;
|
|
41092
|
+
for (const seed of dedupedAttributedSeeds(slate)) {
|
|
41093
|
+
const bucket = map.get(seed.kind);
|
|
41094
|
+
if (bucket)
|
|
41095
|
+
bucket.push(seed);
|
|
41096
|
+
else
|
|
41097
|
+
map.set(seed.kind, [seed]);
|
|
41098
|
+
}
|
|
41099
|
+
return map;
|
|
41100
|
+
}
|
|
41101
|
+
function sha2568(value) {
|
|
41102
|
+
return createHash27("sha256").update(value).digest("hex");
|
|
41103
|
+
}
|
|
41104
|
+
async function gitDiff(cwd) {
|
|
41105
|
+
try {
|
|
41106
|
+
const { stdout: stdout2 } = await execFileAsync("git", ["diff"], { cwd, maxBuffer: 16 * 1024 * 1024 });
|
|
41107
|
+
return stdout2;
|
|
41108
|
+
} catch {
|
|
41109
|
+
return "";
|
|
41110
|
+
}
|
|
41111
|
+
}
|
|
41112
|
+
function diffStatLine(diffText) {
|
|
41113
|
+
if (diffText.trim().length === 0)
|
|
41114
|
+
return "no working-tree changes";
|
|
41115
|
+
const added = (diffText.match(/^\+(?!\+\+)/gm) ?? []).length;
|
|
41116
|
+
const removed = (diffText.match(/^-(?!--)/gm) ?? []).length;
|
|
41117
|
+
return `working-tree diff: +${added}/-${removed} line(s)`;
|
|
41118
|
+
}
|
|
41119
|
+
function courseStatusLine(course) {
|
|
41120
|
+
if (course.state !== "bound")
|
|
41121
|
+
return "flow: unbound";
|
|
41122
|
+
return `flow ${course.flowRef.uri} snapshot=${course.flowRef.snapshot} completed=${course.completed.length} next=${course.next.length} blocked=${course.blocked.length}`;
|
|
41123
|
+
}
|
|
41124
|
+
function mechanicalSummary(diffText, course) {
|
|
41125
|
+
return `Mechanical wrap-up summary (model turn unavailable or timed out):
|
|
41126
|
+
${diffStatLine(diffText)}
|
|
41127
|
+
${courseStatusLine(course)}`;
|
|
41128
|
+
}
|
|
41129
|
+
async function resolveMachineWrapUp(input2) {
|
|
41130
|
+
const now = input2.now ?? (() => new Date);
|
|
41131
|
+
const diffText = await gitDiff(input2.cwd);
|
|
41132
|
+
const course = await readCourse(input2.cwd, input2.slate.course.flowRef);
|
|
41133
|
+
const seedsForKind = dedupedAttributedSeeds(input2.slate).filter((seed) => seed.kind === input2.kind);
|
|
41134
|
+
const flowSnapshotJson = `${JSON.stringify(course, null, 2)}
|
|
41135
|
+
`;
|
|
41136
|
+
const seedsJson = `${JSON.stringify(seedsForKind.map((seed) => ({ text: seed.text, source: describeSource(seed.source) })), null, 2)}
|
|
41137
|
+
`;
|
|
41138
|
+
const sourceRevision = sha2568([diffText, flowSnapshotJson, seedsJson].join("\x00"));
|
|
41139
|
+
const shortHash = sourceRevision.slice(0, 16);
|
|
41140
|
+
const system = "Summarize ONLY the machine evidence provided below \u2014 a git diff, a Flow snapshot, and the Seeds captured " + "this session for one proposal kind. Never invent facts that are not present in the evidence.";
|
|
41141
|
+
const user = `--- git diff ---
|
|
41142
|
+
${diffText.length > 0 ? diffText : "(no working-tree changes)"}
|
|
41143
|
+
|
|
41144
|
+
` + `--- flow snapshot ---
|
|
41145
|
+
${flowSnapshotJson}
|
|
41146
|
+
` + `--- seeds (${input2.kind}) ---
|
|
41147
|
+
${seedsJson}`;
|
|
41148
|
+
let modelResult;
|
|
41149
|
+
const modelTurnTimeoutMs = input2.modelTurnTimeoutMs ?? DEFAULT_MODEL_TURN_TIMEOUT_MS;
|
|
41150
|
+
const turn = runModelTurn({
|
|
41151
|
+
system,
|
|
41152
|
+
user,
|
|
41153
|
+
requestId: `machine-wrap-up-${shortHash}`,
|
|
41154
|
+
...input2.env !== undefined ? { env: input2.env } : {},
|
|
41155
|
+
...input2.providerFactory !== undefined ? { providerFactory: input2.providerFactory } : {}
|
|
41156
|
+
}).then((result) => {
|
|
41157
|
+
modelResult = result;
|
|
41158
|
+
return "done";
|
|
41159
|
+
});
|
|
41160
|
+
let timer;
|
|
41161
|
+
const expired = new Promise((resolve2) => {
|
|
41162
|
+
timer = setTimeout(() => resolve2("timeout"), modelTurnTimeoutMs);
|
|
41163
|
+
});
|
|
41164
|
+
let raceOutcome;
|
|
41165
|
+
try {
|
|
41166
|
+
raceOutcome = await Promise.race([turn, expired]);
|
|
41167
|
+
} finally {
|
|
41168
|
+
if (timer !== undefined)
|
|
41169
|
+
clearTimeout(timer);
|
|
41170
|
+
}
|
|
41171
|
+
let summary;
|
|
41172
|
+
if (raceOutcome === "timeout") {
|
|
41173
|
+
turn.catch(() => {});
|
|
41174
|
+
summary = mechanicalSummary(diffText, course);
|
|
41175
|
+
} else {
|
|
41176
|
+
const result = modelResult;
|
|
41177
|
+
if (result.text.trim().length === 0 && !result.credentialAvailable) {
|
|
41178
|
+
return { ok: false, code: "no_credential" };
|
|
41179
|
+
}
|
|
41180
|
+
summary = result.text.trim().length > 0 ? result.text.trim() : mechanicalSummary(diffText, course);
|
|
41181
|
+
}
|
|
41182
|
+
const evidenceDir = path133.join(input2.cwd, ".metaproject", "workspaces", input2.workspaceId, "machine-evidence");
|
|
41183
|
+
await mkdir53(evidenceDir, { recursive: true });
|
|
41184
|
+
const diffFile = `${input2.kind}.${shortHash}.diff.txt`;
|
|
41185
|
+
const flowFile = `${input2.kind}.${shortHash}.flow.json`;
|
|
41186
|
+
const seedsFile = `${input2.kind}.${shortHash}.seeds.json`;
|
|
41187
|
+
await writeFileAtomic(path133.join(evidenceDir, diffFile), diffText);
|
|
41188
|
+
await writeFileAtomic(path133.join(evidenceDir, flowFile), flowSnapshotJson);
|
|
41189
|
+
await writeFileAtomic(path133.join(evidenceDir, seedsFile), seedsJson);
|
|
41190
|
+
const observedAt = now().toISOString();
|
|
41191
|
+
const relBase = `./.metaproject/workspaces/${input2.workspaceId}/machine-evidence`;
|
|
41192
|
+
const evidence = [
|
|
41193
|
+
{ kind: "diff", uri: `${relBase}/${diffFile}`, revision: sha2568(diffText), observedAt },
|
|
41194
|
+
{ kind: "flow", uri: `${relBase}/${flowFile}`, revision: sha2568(flowSnapshotJson), observedAt },
|
|
41195
|
+
{ kind: "seeds", uri: `${relBase}/${seedsFile}`, revision: sha2568(seedsJson), observedAt }
|
|
41196
|
+
];
|
|
41197
|
+
return {
|
|
41198
|
+
ok: true,
|
|
41199
|
+
resolution: {
|
|
41200
|
+
workspaceId: input2.workspaceId,
|
|
41201
|
+
sourceRevision,
|
|
41202
|
+
summary,
|
|
41203
|
+
evidence,
|
|
41204
|
+
expiresAt: new Date(now().getTime() + WRAP_UP_TTL_MS2).toISOString()
|
|
41205
|
+
}
|
|
41206
|
+
};
|
|
41207
|
+
}
|
|
41208
|
+
async function writeUnboundCandidateArtifact(dir, trigger, now, grouped, nonEmptyKinds) {
|
|
41209
|
+
const archiveDir = path133.join(dir, "slate-archive");
|
|
41210
|
+
await mkdir53(archiveDir, { recursive: true });
|
|
41211
|
+
const nowIso3 = now().toISOString();
|
|
41212
|
+
const filename = `${nowIso3.replace(/[:.]/g, "-")}-unbound-candidate.json`;
|
|
41213
|
+
const content = {
|
|
41214
|
+
recordType: "unbound-candidate",
|
|
41215
|
+
trigger,
|
|
41216
|
+
generatedAt: nowIso3,
|
|
41217
|
+
groups: nonEmptyKinds.map((kind) => ({
|
|
41218
|
+
kind,
|
|
41219
|
+
seeds: (grouped.get(kind) ?? []).map((seed) => ({ text: seed.text, source: describeSource(seed.source) }))
|
|
41220
|
+
}))
|
|
41221
|
+
};
|
|
41222
|
+
await writeFileAtomic(path133.join(archiveDir, filename), `${JSON.stringify(content, null, 2)}
|
|
41223
|
+
`);
|
|
41224
|
+
}
|
|
41225
|
+
async function proposeOneGroup(params) {
|
|
41226
|
+
try {
|
|
41227
|
+
const resolved = await resolveMachineWrapUp({
|
|
41228
|
+
cwd: params.cwd,
|
|
41229
|
+
workspaceId: params.workspaceId,
|
|
41230
|
+
slate: params.slate,
|
|
41231
|
+
kind: params.kind,
|
|
41232
|
+
now: params.now,
|
|
41233
|
+
...params.env !== undefined ? { env: params.env } : {},
|
|
41234
|
+
...params.providerFactory !== undefined ? { providerFactory: params.providerFactory } : {},
|
|
41235
|
+
...params.modelTurnTimeoutMs !== undefined ? { modelTurnTimeoutMs: params.modelTurnTimeoutMs } : {}
|
|
41236
|
+
});
|
|
41237
|
+
if (!resolved.ok)
|
|
41238
|
+
return { kind: params.kind, outcome: "no_credential" };
|
|
41239
|
+
const flowEvidence = resolved.resolution.evidence.find((item) => item.kind === "flow");
|
|
41240
|
+
const sourceRef = (flowEvidence ?? resolved.resolution.evidence[0]).uri;
|
|
41241
|
+
const flowRef = params.slate.course.flowRef ?? "";
|
|
41242
|
+
const dedupHash = sha2568(`${params.workspaceId}:${flowRef}:${resolved.resolution.sourceRevision}:${params.kind}`);
|
|
41243
|
+
const proposalId = `wrapup-${dedupHash.slice(0, 32)}`;
|
|
41244
|
+
const wrapUpAuthority = createTrustedWrapUpAuthority({
|
|
41245
|
+
now: params.now,
|
|
41246
|
+
resolveExplicitWrapUp: async (request) => {
|
|
41247
|
+
if (request.source !== "flow") {
|
|
41248
|
+
throw new Error(`machine-wrap-up only resolves "flow" wrap-ups, got "${request.source}"`);
|
|
41249
|
+
}
|
|
41250
|
+
return resolved.resolution;
|
|
41251
|
+
}
|
|
41252
|
+
});
|
|
41253
|
+
const { service: service4, authorizationServer } = createHarnessProposalLifecycleService(params.cwd, {
|
|
41254
|
+
workspaceId: params.workspaceId,
|
|
41255
|
+
now: params.now
|
|
41256
|
+
});
|
|
41257
|
+
const requestCorrelationId = randomUUID14();
|
|
41258
|
+
const actor = await authorizationServer.actorContextFor(undefined, requestCorrelationId);
|
|
41259
|
+
if (!actor)
|
|
41260
|
+
throw new Error("trusted ActorContext is required for a machine wrap-up propose");
|
|
41261
|
+
const provenance = await wrapUpAuthority.issue({ actor, source: "flow", sourceRef });
|
|
41262
|
+
try {
|
|
41263
|
+
const proposal = await service4.create({
|
|
41264
|
+
request: undefined,
|
|
41265
|
+
requestCorrelationId,
|
|
41266
|
+
workspaceId: params.workspaceId,
|
|
41267
|
+
id: proposalId,
|
|
41268
|
+
proposalRevision: "1",
|
|
41269
|
+
kind: params.kind,
|
|
41270
|
+
wrapUp: provenance
|
|
41271
|
+
});
|
|
41272
|
+
return { kind: params.kind, outcome: "proposed", proposalId: proposal.id };
|
|
41273
|
+
} catch (error2) {
|
|
41274
|
+
if (error2 instanceof ProposalLifecycleError && error2.code === "conflict") {
|
|
41275
|
+
return { kind: params.kind, outcome: "conflict" };
|
|
41276
|
+
}
|
|
41277
|
+
throw error2;
|
|
41278
|
+
}
|
|
41279
|
+
} catch (error2) {
|
|
41280
|
+
const message2 = error2 instanceof Error ? error2.message : String(error2);
|
|
41281
|
+
return { kind: params.kind, outcome: "error", message: message2 };
|
|
41282
|
+
}
|
|
41283
|
+
}
|
|
41284
|
+
async function runWrapUp(input2) {
|
|
41285
|
+
const now = input2.now ?? (() => new Date);
|
|
41286
|
+
const grouped = groupSeedsByKind(input2.slate);
|
|
41287
|
+
const nonEmptyKinds = [...grouped.keys()].filter((kind) => (grouped.get(kind)?.length ?? 0) > 0);
|
|
41288
|
+
if (nonEmptyKinds.length === 0) {
|
|
41289
|
+
return { groups: [] };
|
|
41290
|
+
}
|
|
41291
|
+
if (input2.slate.workspaceId === undefined) {
|
|
41292
|
+
await writeUnboundCandidateArtifact(input2.dir, input2.trigger, now, grouped, nonEmptyKinds);
|
|
41293
|
+
return { groups: nonEmptyKinds.map((kind) => ({ kind, outcome: "unbound-candidate" })) };
|
|
41294
|
+
}
|
|
41295
|
+
const workspaceId = input2.slate.workspaceId;
|
|
41296
|
+
const groups = await Promise.all(nonEmptyKinds.map((kind) => proposeOneGroup({
|
|
41297
|
+
cwd: input2.cwd,
|
|
41298
|
+
workspaceId,
|
|
41299
|
+
slate: input2.slate,
|
|
41300
|
+
kind,
|
|
41301
|
+
now,
|
|
41302
|
+
...input2.env !== undefined ? { env: input2.env } : {},
|
|
41303
|
+
...input2.providerFactory !== undefined ? { providerFactory: input2.providerFactory } : {},
|
|
41304
|
+
...input2.modelTurnTimeoutMs !== undefined ? { modelTurnTimeoutMs: input2.modelTurnTimeoutMs } : {}
|
|
41305
|
+
})));
|
|
41306
|
+
return { groups };
|
|
41307
|
+
}
|
|
41308
|
+
|
|
41309
|
+
// src/commands/harness.ts
|
|
40785
41310
|
init_shell_config();
|
|
40786
41311
|
import { realpathSync as realpathSync3 } from "fs";
|
|
40787
41312
|
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
@@ -41201,7 +41726,7 @@ function resolveRuntime(deps) {
|
|
|
41201
41726
|
const env = deps?.env ?? process.env;
|
|
41202
41727
|
const clock = deps?.clock ?? (() => new Date().toISOString());
|
|
41203
41728
|
let idCounter = 0;
|
|
41204
|
-
const idSeq = deps?.idSeq ?? (() => `${
|
|
41729
|
+
const idSeq = deps?.idSeq ?? (() => `${randomUUID15()}-${idCounter++}`);
|
|
41205
41730
|
return { env, clock, idSeq };
|
|
41206
41731
|
}
|
|
41207
41732
|
function shellAllowProfile() {
|
|
@@ -41225,12 +41750,23 @@ var denyingExecutor = {
|
|
|
41225
41750
|
throw new Error(`no tool executor is configured for the harness CLI: ${invocation.call.toolName}`);
|
|
41226
41751
|
}
|
|
41227
41752
|
};
|
|
41753
|
+
var KNOWN_HARNESS_RUN_FLAGS = new Set([
|
|
41754
|
+
"--provider",
|
|
41755
|
+
"--model",
|
|
41756
|
+
"--base-url",
|
|
41757
|
+
"--record",
|
|
41758
|
+
"--unattended",
|
|
41759
|
+
"--goal",
|
|
41760
|
+
"--workspace"
|
|
41761
|
+
]);
|
|
41228
41762
|
function parseArgs2(args) {
|
|
41229
41763
|
let provider = "";
|
|
41230
41764
|
let model = "";
|
|
41231
41765
|
let baseUrl;
|
|
41232
41766
|
let record;
|
|
41233
41767
|
let unattended;
|
|
41768
|
+
let goal;
|
|
41769
|
+
let workspace;
|
|
41234
41770
|
const positional = [];
|
|
41235
41771
|
for (let i = 1;i < args.length; i++) {
|
|
41236
41772
|
const arg = args[i];
|
|
@@ -41244,17 +41780,27 @@ function parseArgs2(args) {
|
|
|
41244
41780
|
record = args[++i];
|
|
41245
41781
|
} else if (arg === "--unattended") {
|
|
41246
41782
|
unattended = true;
|
|
41783
|
+
} else if (arg === "--goal") {
|
|
41784
|
+
const next = args[i + 1];
|
|
41785
|
+
goal = next !== undefined && !KNOWN_HARNESS_RUN_FLAGS.has(next) ? args[++i] : undefined;
|
|
41786
|
+
} else if (arg === "--workspace") {
|
|
41787
|
+
const next = args[i + 1];
|
|
41788
|
+
workspace = next !== undefined && !KNOWN_HARNESS_RUN_FLAGS.has(next) ? args[++i] : undefined;
|
|
41247
41789
|
} else if (arg !== undefined) {
|
|
41248
41790
|
positional.push(arg);
|
|
41249
41791
|
}
|
|
41250
41792
|
}
|
|
41251
|
-
const parsed = { provider, model, prompt: positional.join(" ") };
|
|
41793
|
+
const parsed = { provider, model, prompt: goal !== undefined && goal.length > 0 ? goal : positional.join(" ") };
|
|
41252
41794
|
if (baseUrl !== undefined)
|
|
41253
41795
|
parsed.baseUrl = baseUrl;
|
|
41254
41796
|
if (record !== undefined)
|
|
41255
41797
|
parsed.record = record;
|
|
41256
41798
|
if (unattended !== undefined)
|
|
41257
41799
|
parsed.unattended = unattended;
|
|
41800
|
+
if (goal !== undefined)
|
|
41801
|
+
parsed.goal = goal;
|
|
41802
|
+
if (workspace !== undefined)
|
|
41803
|
+
parsed.workspace = workspace;
|
|
41258
41804
|
return parsed;
|
|
41259
41805
|
}
|
|
41260
41806
|
function toStructured(result) {
|
|
@@ -41288,16 +41834,27 @@ async function harnessCommand(args, deps) {
|
|
|
41288
41834
|
console.log(USAGE);
|
|
41289
41835
|
return;
|
|
41290
41836
|
}
|
|
41291
|
-
const { provider, model, baseUrl, prompt, record, unattended } = parseArgs2(args);
|
|
41837
|
+
const { provider, model, baseUrl, prompt, record, unattended, workspace } = parseArgs2(args);
|
|
41292
41838
|
const validProviders = new Set(HARNESS_PROVIDER_OPTIONS);
|
|
41293
41839
|
if (!validProviders.has(provider) || prompt.length === 0) {
|
|
41294
41840
|
console.log(USAGE);
|
|
41295
41841
|
return;
|
|
41296
41842
|
}
|
|
41843
|
+
if (args.includes("--workspace") && (workspace === undefined || workspace.length === 0)) {
|
|
41844
|
+
console.log('--workspace requires a value, e.g. keryx harness run --provider <p> --model <m> --workspace <id> "<prompt>". No run was started.');
|
|
41845
|
+
return;
|
|
41846
|
+
}
|
|
41847
|
+
if (workspace !== undefined && workspace.length > 0) {
|
|
41848
|
+
const resolved = await resolveWorkspaceForActor(process.cwd(), workspace);
|
|
41849
|
+
if (!resolved.ok) {
|
|
41850
|
+
console.log(`--workspace "${workspace}" was rejected (${resolved.error.code}): ${resolved.error.message}. No run was started.`);
|
|
41851
|
+
return;
|
|
41852
|
+
}
|
|
41853
|
+
}
|
|
41297
41854
|
const env = deps?.env ?? process.env;
|
|
41298
41855
|
const clock = deps?.clock ?? (() => new Date().toISOString());
|
|
41299
41856
|
let idCounter = 0;
|
|
41300
|
-
const idSeq = deps?.idSeq ?? (() => `${
|
|
41857
|
+
const idSeq = deps?.idSeq ?? (() => `${randomUUID15()}-${idCounter++}`);
|
|
41301
41858
|
const fetchImpl = deps?.fetch ?? globalThis.fetch;
|
|
41302
41859
|
if (provider === "anthropic") {
|
|
41303
41860
|
const apiKey = env.ANTHROPIC_API_KEY;
|
|
@@ -41363,6 +41920,16 @@ async function harnessCommand(args, deps) {
|
|
|
41363
41920
|
};
|
|
41364
41921
|
}
|
|
41365
41922
|
console.log(JSON.stringify(structured));
|
|
41923
|
+
try {
|
|
41924
|
+
const wrapUpDir = resolveOneShotWrapUpSessionDir(process.cwd(), idSeq);
|
|
41925
|
+
const prior = await readSlate(wrapUpDir);
|
|
41926
|
+
const wrapUpSlate = prior ?? { anchors: { root: process.cwd(), touched: [] }, course: {}, seeds: [] };
|
|
41927
|
+
if (workspace !== undefined && workspace.length > 0)
|
|
41928
|
+
wrapUpSlate.workspaceId = workspace;
|
|
41929
|
+
await runWrapUp({ trigger: "process-termination", cwd: process.cwd(), dir: wrapUpDir, slate: wrapUpSlate });
|
|
41930
|
+
} catch (error2) {
|
|
41931
|
+
console.error(`harness run: wrap-up trigger failed (ignored): ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
41932
|
+
}
|
|
41366
41933
|
}
|
|
41367
41934
|
function parseReplayArgs(args) {
|
|
41368
41935
|
const parsed = { json: false };
|
|
@@ -41414,7 +41981,7 @@ function harnessReplay(args, deps) {
|
|
|
41414
41981
|
}
|
|
41415
41982
|
const clock = deps?.clock ?? (() => new Date().toISOString());
|
|
41416
41983
|
let idCounter = 0;
|
|
41417
|
-
const idSeq = deps?.idSeq ?? (() => `${
|
|
41984
|
+
const idSeq = deps?.idSeq ?? (() => `${randomUUID15()}-${idCounter++}`);
|
|
41418
41985
|
const fixture = (() => {
|
|
41419
41986
|
if (parsed.fixture === undefined || parsed.fixture.length === 0) {
|
|
41420
41987
|
return { ok: true, value: buildReplayFixture(run, { idSeq }), built: true };
|
|
@@ -41665,7 +42232,7 @@ async function harnessExec(args, deps) {
|
|
|
41665
42232
|
env: commandEnv,
|
|
41666
42233
|
cwd
|
|
41667
42234
|
};
|
|
41668
|
-
const worktreeRoot =
|
|
42235
|
+
const worktreeRoot = path134.parse(path134.resolve(cwd, commandPath)).root || cwd;
|
|
41669
42236
|
const budget = {
|
|
41670
42237
|
reservationId: idSeq(),
|
|
41671
42238
|
maxRuntimeMs: maxRuntimeMs ?? EXEC_DEFAULT_RUNTIME_MS
|
|
@@ -41854,7 +42421,7 @@ function harnessWave(args, deps) {
|
|
|
41854
42421
|
// src/commands/shell.ts
|
|
41855
42422
|
init_make_provider();
|
|
41856
42423
|
init_orient();
|
|
41857
|
-
import { randomUUID as
|
|
42424
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
41858
42425
|
import * as readline2 from "readline";
|
|
41859
42426
|
|
|
41860
42427
|
// src/commands/agent-approval-context.ts
|
|
@@ -41896,6 +42463,9 @@ async function buildApprovalContext(port, command) {
|
|
|
41896
42463
|
`);
|
|
41897
42464
|
}
|
|
41898
42465
|
|
|
42466
|
+
// src/commands/interactive-agent-tools.ts
|
|
42467
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
42468
|
+
|
|
41899
42469
|
// src/harness/tool/builtin/ask-user-tool.ts
|
|
41900
42470
|
function createAskUserTool(ask) {
|
|
41901
42471
|
return {
|
|
@@ -41988,7 +42558,7 @@ function createAskUserTool(ask) {
|
|
|
41988
42558
|
}
|
|
41989
42559
|
|
|
41990
42560
|
// src/harness/tool/builtin/interactive-tools.ts
|
|
41991
|
-
import { readdir as
|
|
42561
|
+
import { readdir as readdir22 } from "fs/promises";
|
|
41992
42562
|
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep } from "path";
|
|
41993
42563
|
import { realpathSync as realpathSync4 } from "fs";
|
|
41994
42564
|
var MAX_READ_BYTES = 20000;
|
|
@@ -42040,7 +42610,7 @@ function builtinReadOnlyTools(root) {
|
|
|
42040
42610
|
return { output: `path escapes the project root: ${requested}`, isError: true };
|
|
42041
42611
|
}
|
|
42042
42612
|
try {
|
|
42043
|
-
const entries = await
|
|
42613
|
+
const entries = await readdir22(target, { withFileTypes: true });
|
|
42044
42614
|
const lines = entries.map((e) => e.isDirectory() ? `${e.name}/` : e.name).sort();
|
|
42045
42615
|
return { output: lines.length > 0 ? lines.join(`
|
|
42046
42616
|
`) : "(empty)", isError: false };
|
|
@@ -42189,13 +42759,13 @@ function builtinMetaprojectTools(root, run = makeKeryxRunner(root), port) {
|
|
|
42189
42759
|
if ("error" in pattern) {
|
|
42190
42760
|
return pattern.error;
|
|
42191
42761
|
}
|
|
42192
|
-
const
|
|
42762
|
+
const path135 = typeof input2.path === "string" && input2.path.length > 0 ? input2.path : undefined;
|
|
42193
42763
|
const args = ["ctx", "rg", pattern.value];
|
|
42194
|
-
if (
|
|
42195
|
-
const confined = confineToRoot(root,
|
|
42764
|
+
if (path135 !== undefined) {
|
|
42765
|
+
const confined = confineToRoot(root, path135);
|
|
42196
42766
|
if (confined === null) {
|
|
42197
42767
|
return {
|
|
42198
|
-
output: `search_code: path escapes the project root: ${
|
|
42768
|
+
output: `search_code: path escapes the project root: ${path135}`,
|
|
42199
42769
|
isError: true
|
|
42200
42770
|
};
|
|
42201
42771
|
}
|
|
@@ -42485,6 +43055,100 @@ function shellExecTool(root, run = makeCommandRunner(root)) {
|
|
|
42485
43055
|
};
|
|
42486
43056
|
}
|
|
42487
43057
|
|
|
43058
|
+
// src/harness/tool/builtin/slate-tool.ts
|
|
43059
|
+
init_redact();
|
|
43060
|
+
var SEED_TEXT_MAX_LENGTH = 4000;
|
|
43061
|
+
var SLATE_SEED_KINDS = [
|
|
43062
|
+
"decision",
|
|
43063
|
+
"wiki-update",
|
|
43064
|
+
"memory-entry",
|
|
43065
|
+
"follow-up",
|
|
43066
|
+
"contract-change",
|
|
43067
|
+
"risk"
|
|
43068
|
+
];
|
|
43069
|
+
function isSlateSeedKind(value) {
|
|
43070
|
+
return typeof value === "string" && SLATE_SEED_KINDS.includes(value);
|
|
43071
|
+
}
|
|
43072
|
+
function slateReadTool(cwd, getSessionDir) {
|
|
43073
|
+
return {
|
|
43074
|
+
definition: {
|
|
43075
|
+
name: "slate_read",
|
|
43076
|
+
description: "Read this session's Slate: the live Course projection (derived from the bound Flow, if any) and the Seeds recorded so far (draft hypotheses, not yet accepted). Input: {} (no arguments). Course/Seeds are NEVER auto-injected into the conversation \u2014 this tool is the only way to see them.",
|
|
43077
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
43078
|
+
risk: "read"
|
|
43079
|
+
},
|
|
43080
|
+
invoke: async () => {
|
|
43081
|
+
const dir = getSessionDir();
|
|
43082
|
+
if (dir === undefined) {
|
|
43083
|
+
return { output: "slate_read: no active session in this run", isError: true };
|
|
43084
|
+
}
|
|
43085
|
+
try {
|
|
43086
|
+
const slate = await readSlate(dir);
|
|
43087
|
+
const course = await courseFromSlate(cwd, slate);
|
|
43088
|
+
return {
|
|
43089
|
+
output: JSON.stringify({ course, seeds: slate?.seeds ?? [], workspaceId: slate?.workspaceId }, null, 2),
|
|
43090
|
+
isError: false
|
|
43091
|
+
};
|
|
43092
|
+
} catch (cause) {
|
|
43093
|
+
return {
|
|
43094
|
+
output: `slate_read failed: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
43095
|
+
isError: true
|
|
43096
|
+
};
|
|
43097
|
+
}
|
|
43098
|
+
}
|
|
43099
|
+
};
|
|
43100
|
+
}
|
|
43101
|
+
function slateWriteSeedTool(getSessionDir, idSeq, clock) {
|
|
43102
|
+
return {
|
|
43103
|
+
definition: {
|
|
43104
|
+
name: "slate_write_seed",
|
|
43105
|
+
description: "Record a Seed: a draft hypothesis, decision, or follow-up worth surfacing to a later review \u2014 never accepted project knowledge by itself. Input: { text: string, kind?: 'decision'|'wiki-update'|'memory-entry'|'follow-up'|'contract-change'|'risk' }.",
|
|
43106
|
+
inputSchema: {
|
|
43107
|
+
type: "object",
|
|
43108
|
+
properties: {
|
|
43109
|
+
text: { type: "string", maxLength: SEED_TEXT_MAX_LENGTH },
|
|
43110
|
+
kind: { type: "string", enum: [...SLATE_SEED_KINDS] }
|
|
43111
|
+
},
|
|
43112
|
+
required: ["text"],
|
|
43113
|
+
additionalProperties: false
|
|
43114
|
+
},
|
|
43115
|
+
risk: "read"
|
|
43116
|
+
},
|
|
43117
|
+
invoke: async (input2) => {
|
|
43118
|
+
const rawText = typeof input2.text === "string" ? input2.text : undefined;
|
|
43119
|
+
const text = rawText?.trim();
|
|
43120
|
+
if (text === undefined || text.length === 0) {
|
|
43121
|
+
return { output: "slate_write_seed requires a non-empty 'text'", isError: true };
|
|
43122
|
+
}
|
|
43123
|
+
let kind;
|
|
43124
|
+
if (input2.kind !== undefined) {
|
|
43125
|
+
if (!isSlateSeedKind(input2.kind)) {
|
|
43126
|
+
return {
|
|
43127
|
+
output: `slate_write_seed: unrecognized 'kind': ${JSON.stringify(input2.kind)}`,
|
|
43128
|
+
isError: true
|
|
43129
|
+
};
|
|
43130
|
+
}
|
|
43131
|
+
kind = input2.kind;
|
|
43132
|
+
}
|
|
43133
|
+
const dir = getSessionDir();
|
|
43134
|
+
if (dir === undefined) {
|
|
43135
|
+
return { output: "slate_write_seed: no active session in this run", isError: true };
|
|
43136
|
+
}
|
|
43137
|
+
try {
|
|
43138
|
+
const redactedText = redactSensitiveText(text);
|
|
43139
|
+
const seed = { id: idSeq(), text: redactedText, ts: clock(), ...kind !== undefined ? { kind } : {} };
|
|
43140
|
+
await appendSeed(dir, seed);
|
|
43141
|
+
return { output: JSON.stringify({ appended: seed }, null, 2), isError: false };
|
|
43142
|
+
} catch (cause) {
|
|
43143
|
+
return {
|
|
43144
|
+
output: `slate_write_seed failed: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
43145
|
+
isError: true
|
|
43146
|
+
};
|
|
43147
|
+
}
|
|
43148
|
+
}
|
|
43149
|
+
};
|
|
43150
|
+
}
|
|
43151
|
+
|
|
42488
43152
|
// src/harness/web/sandboxed-web-transport.ts
|
|
42489
43153
|
import { lookup as systemLookup } from "dns/promises";
|
|
42490
43154
|
|
|
@@ -42936,7 +43600,7 @@ function webSearchTool(service4) {
|
|
|
42936
43600
|
}
|
|
42937
43601
|
|
|
42938
43602
|
// src/harness/tool/builtin/workspace-context-tool.ts
|
|
42939
|
-
import { randomUUID as
|
|
43603
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
42940
43604
|
function parseBudget(input2, defaultMaxItems) {
|
|
42941
43605
|
const maxItems = input2.maxItems === undefined ? defaultMaxItems : input2.maxItems;
|
|
42942
43606
|
const maxTokens = input2.maxTokens === undefined ? 4096 : input2.maxTokens;
|
|
@@ -42966,7 +43630,7 @@ function workspaceOverviewTool(cwd) {
|
|
|
42966
43630
|
const budget = parseBudget(input2, 32);
|
|
42967
43631
|
if ("error" in budget)
|
|
42968
43632
|
return { output: budget.error, isError: true };
|
|
42969
|
-
const result = await createLocalFwkReadService(cwd).overview({ workspaceId, request: undefined, requestCorrelationId:
|
|
43633
|
+
const result = await createLocalFwkReadService(cwd).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID16(), budget });
|
|
42970
43634
|
return { output: JSON.stringify(normalizeFwkResult(result), null, 2), isError: false };
|
|
42971
43635
|
}
|
|
42972
43636
|
};
|
|
@@ -42994,7 +43658,7 @@ function workspaceReadTool(cwd) {
|
|
|
42994
43658
|
const budget = parseBudget(input2, 1);
|
|
42995
43659
|
if ("error" in budget)
|
|
42996
43660
|
return { output: budget.error, isError: true };
|
|
42997
|
-
const result = await createLocalFwkReadService(cwd).read({ workspaceId, itemId, request: undefined, requestCorrelationId:
|
|
43661
|
+
const result = await createLocalFwkReadService(cwd).read({ workspaceId, itemId, request: undefined, requestCorrelationId: randomUUID16(), budget });
|
|
42998
43662
|
return { output: JSON.stringify(normalizeFwkResult(result), null, 2), isError: false };
|
|
42999
43663
|
}
|
|
43000
43664
|
};
|
|
@@ -43014,6 +43678,11 @@ async function invokeAskUserHost(request) {
|
|
|
43014
43678
|
|
|
43015
43679
|
// src/commands/interactive-agent-tools.ts
|
|
43016
43680
|
function buildInteractiveAgentTools(input2) {
|
|
43681
|
+
const getSessionDir = input2.getSessionDir ?? (() => {
|
|
43682
|
+
return;
|
|
43683
|
+
});
|
|
43684
|
+
const idSeq = input2.idSeq ?? (() => randomUUID17());
|
|
43685
|
+
const clock = input2.clock ?? (() => new Date().toISOString());
|
|
43017
43686
|
return [
|
|
43018
43687
|
...builtinReadOnlyTools(input2.cwd),
|
|
43019
43688
|
...builtinMetaprojectTools(input2.cwd, makeKeryxRunner(input2.cwd), input2.metaprojectPort),
|
|
@@ -43023,6 +43692,8 @@ function buildInteractiveAgentTools(input2) {
|
|
|
43023
43692
|
workspaceOverviewTool(input2.cwd),
|
|
43024
43693
|
workspaceReadTool(input2.cwd),
|
|
43025
43694
|
createAskUserTool(invokeAskUserHost),
|
|
43695
|
+
slateReadTool(input2.cwd, getSessionDir),
|
|
43696
|
+
slateWriteSeedTool(getSessionDir, idSeq, clock),
|
|
43026
43697
|
input2.spawnTool
|
|
43027
43698
|
];
|
|
43028
43699
|
}
|
|
@@ -43031,7 +43702,7 @@ function buildInteractiveAgentTools(input2) {
|
|
|
43031
43702
|
init_config_dir();
|
|
43032
43703
|
init_shell_config();
|
|
43033
43704
|
import { existsSync as existsSync24 } from "fs";
|
|
43034
|
-
import
|
|
43705
|
+
import path135 from "path";
|
|
43035
43706
|
|
|
43036
43707
|
// src/lib/shell-syntax.ts
|
|
43037
43708
|
var METACHARACTERS = new Set([";", "&", "|", "<", ">", `
|
|
@@ -43344,7 +44015,7 @@ function touchesAgentCredentials(command) {
|
|
|
43344
44015
|
}
|
|
43345
44016
|
|
|
43346
44017
|
// src/lib/shell-permissions.ts
|
|
43347
|
-
import { createHash as
|
|
44018
|
+
import { createHash as createHash28 } from "crypto";
|
|
43348
44019
|
var PREFIX_BANNED = new Set([
|
|
43349
44020
|
"sh",
|
|
43350
44021
|
"bash",
|
|
@@ -43523,7 +44194,7 @@ function emptyShellPermissions() {
|
|
|
43523
44194
|
return { allow: [] };
|
|
43524
44195
|
}
|
|
43525
44196
|
function shellPermissionsPath(dir) {
|
|
43526
|
-
return
|
|
44197
|
+
return path135.join(path135.dirname(shellConfigPath(dir)), "permissions.json");
|
|
43527
44198
|
}
|
|
43528
44199
|
function loadShellPermissionsWithAudit(dir) {
|
|
43529
44200
|
try {
|
|
@@ -43561,7 +44232,7 @@ function loadShellPermissions(dir) {
|
|
|
43561
44232
|
function saveShellPermissions(perms, dir, options = {}) {
|
|
43562
44233
|
try {
|
|
43563
44234
|
const file = shellPermissionsPath(dir);
|
|
43564
|
-
ensureKeryxConfigDir(
|
|
44235
|
+
ensureKeryxConfigDir(path135.dirname(file));
|
|
43565
44236
|
const cleaned = Array.from(new Set(perms.allow.map((p) => p.trim()).filter((p) => p.length > 0)));
|
|
43566
44237
|
const body = {
|
|
43567
44238
|
allow: options.skipValidation === true ? cleaned : cleaned.filter((p) => validateShellPattern(p).ok)
|
|
@@ -43636,7 +44307,7 @@ function shellPermissionsFingerprint(dir) {
|
|
|
43636
44307
|
if (!read.ok) {
|
|
43637
44308
|
return "";
|
|
43638
44309
|
}
|
|
43639
|
-
return
|
|
44310
|
+
return createHash28("sha256").update(read.text, "utf8").digest("hex");
|
|
43640
44311
|
} catch {
|
|
43641
44312
|
return "";
|
|
43642
44313
|
}
|
|
@@ -43870,12 +44541,12 @@ function remoteRequest(providerId, endpoint, query, key) {
|
|
|
43870
44541
|
// src/lib/search-config.ts
|
|
43871
44542
|
init_config_dir();
|
|
43872
44543
|
import { existsSync as existsSync25 } from "fs";
|
|
43873
|
-
import
|
|
44544
|
+
import path136 from "path";
|
|
43874
44545
|
function searchConfigPath(dir) {
|
|
43875
|
-
return
|
|
44546
|
+
return path136.join(keryxConfigDir(dir), "search-providers.json");
|
|
43876
44547
|
}
|
|
43877
44548
|
function searchCredentialPath(dir) {
|
|
43878
|
-
return
|
|
44549
|
+
return path136.join(keryxConfigDir(dir), "search-credentials.json");
|
|
43879
44550
|
}
|
|
43880
44551
|
function readJson(file) {
|
|
43881
44552
|
try {
|
|
@@ -44001,7 +44672,10 @@ function createDefaultSearchProviderController(configDir) {
|
|
|
44001
44672
|
return new SearchProviderController(registry, configDir);
|
|
44002
44673
|
}
|
|
44003
44674
|
// src/harness/tool/builtin/spawn-subagent-tool.ts
|
|
44004
|
-
import { createHash as
|
|
44675
|
+
import { createHash as createHash29, randomUUID as randomUUID18 } from "crypto";
|
|
44676
|
+
import { mkdtemp, rm as rm8 } from "fs/promises";
|
|
44677
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
44678
|
+
import path138 from "path";
|
|
44005
44679
|
|
|
44006
44680
|
// src/harness/child/ledger.ts
|
|
44007
44681
|
function decrement(remaining, reservation) {
|
|
@@ -44315,85 +44989,13 @@ function foldChildSummary(summary) {
|
|
|
44315
44989
|
init_validator();
|
|
44316
44990
|
init_redact();
|
|
44317
44991
|
|
|
44318
|
-
// src/session/slate.ts
|
|
44319
|
-
init_fs();
|
|
44320
|
-
import { mkdir as mkdir52, readFile as readFile74, rm as rm7 } from "fs/promises";
|
|
44321
|
-
import path135 from "path";
|
|
44322
|
-
function slatePath(dir) {
|
|
44323
|
-
return path135.join(dir, "slate.json");
|
|
44324
|
-
}
|
|
44325
|
-
function slateLockPath(dir) {
|
|
44326
|
-
return `${slatePath(dir)}.lock`;
|
|
44327
|
-
}
|
|
44328
|
-
async function readSlate(dir) {
|
|
44329
|
-
try {
|
|
44330
|
-
const raw = await readFile74(slatePath(dir), "utf8");
|
|
44331
|
-
return JSON.parse(raw);
|
|
44332
|
-
} catch (error2) {
|
|
44333
|
-
if (isNotFound(error2))
|
|
44334
|
-
return;
|
|
44335
|
-
throw error2;
|
|
44336
|
-
}
|
|
44337
|
-
}
|
|
44338
|
-
async function archiveSlate(dir, attemptId) {
|
|
44339
|
-
if (!/^[A-Za-z0-9._-]+$/.test(attemptId))
|
|
44340
|
-
throw new Error(`invalid attemptId: ${JSON.stringify(attemptId)}`);
|
|
44341
|
-
await mkdir52(dir, { recursive: true });
|
|
44342
|
-
return withFileLock2(slateLockPath(dir), () => archiveIfExistsLocked(dir, attemptId));
|
|
44343
|
-
}
|
|
44344
|
-
async function archiveIfExistsLocked(dir, attemptId) {
|
|
44345
|
-
let raw;
|
|
44346
|
-
try {
|
|
44347
|
-
raw = await readFile74(slatePath(dir), "utf8");
|
|
44348
|
-
} catch (error2) {
|
|
44349
|
-
if (isNotFound(error2))
|
|
44350
|
-
return;
|
|
44351
|
-
throw error2;
|
|
44352
|
-
}
|
|
44353
|
-
const archiveDir = path135.join(dir, "slate-archive");
|
|
44354
|
-
await mkdir52(archiveDir, { recursive: true });
|
|
44355
|
-
await writeFileAtomic(path135.join(archiveDir, `${attemptId}.json`), raw);
|
|
44356
|
-
await rm7(slatePath(dir), { force: true });
|
|
44357
|
-
}
|
|
44358
|
-
async function openSlateAtomic(dir, mintAttemptId, build) {
|
|
44359
|
-
await mkdir52(dir, { recursive: true });
|
|
44360
|
-
return withFileLock2(slateLockPath(dir), async () => {
|
|
44361
|
-
const existing = await readSlate(dir);
|
|
44362
|
-
if (existing !== undefined) {
|
|
44363
|
-
await archiveIfExistsLocked(dir, mintAttemptId());
|
|
44364
|
-
}
|
|
44365
|
-
const next = build();
|
|
44366
|
-
await writeFileAtomic(slatePath(dir), `${JSON.stringify(next, null, 2)}
|
|
44367
|
-
`);
|
|
44368
|
-
return next;
|
|
44369
|
-
});
|
|
44370
|
-
}
|
|
44371
|
-
|
|
44372
|
-
// src/session/slate-course.ts
|
|
44373
|
-
init_store2();
|
|
44374
|
-
var unbound = { state: "unbound" };
|
|
44375
|
-
async function readCourse(cwd, flowRef) {
|
|
44376
|
-
if (!flowRef)
|
|
44377
|
-
return unbound;
|
|
44378
|
-
try {
|
|
44379
|
-
const dir = await resolveFlowDir(cwd, flowRef);
|
|
44380
|
-
const flow = await readFlow(cwd, dir);
|
|
44381
|
-
return { state: "bound", ...deriveFlowWork(flow, flowRef) };
|
|
44382
|
-
} catch {
|
|
44383
|
-
return unbound;
|
|
44384
|
-
}
|
|
44385
|
-
}
|
|
44386
|
-
async function courseFromSlate(cwd, slate) {
|
|
44387
|
-
return readCourse(cwd, slate?.course.flowRef);
|
|
44388
|
-
}
|
|
44389
|
-
|
|
44390
44992
|
// src/session/slate-lifecycle.ts
|
|
44391
|
-
import { execFile } from "child_process";
|
|
44392
|
-
import { promisify } from "util";
|
|
44393
|
-
var
|
|
44993
|
+
import { execFile as execFile2 } from "child_process";
|
|
44994
|
+
import { promisify as promisify2 } from "util";
|
|
44995
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
44394
44996
|
async function resolveTree(root) {
|
|
44395
44997
|
try {
|
|
44396
|
-
const { stdout: stdout2 } = await
|
|
44998
|
+
const { stdout: stdout2 } = await execFileAsync2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: root });
|
|
44397
44999
|
const branch = stdout2.trim();
|
|
44398
45000
|
return branch.length > 0 ? branch : undefined;
|
|
44399
45001
|
} catch {
|
|
@@ -44503,12 +45105,80 @@ function isClosePhrase(text) {
|
|
|
44503
45105
|
return true;
|
|
44504
45106
|
});
|
|
44505
45107
|
}
|
|
45108
|
+
async function recordSlateTouch(dir, touched, extra) {
|
|
45109
|
+
let changed = false;
|
|
45110
|
+
const slate = await writeSlate(dir, (prev) => {
|
|
45111
|
+
if (!prev)
|
|
45112
|
+
throw new Error(`recordSlateTouch: no open slate in ${dir}`);
|
|
45113
|
+
const existing = new Set(prev.anchors.touched);
|
|
45114
|
+
const additions = touched.filter((entry) => !existing.has(entry));
|
|
45115
|
+
const treeChanged = extra?.tree !== undefined && extra.tree !== prev.anchors.tree;
|
|
45116
|
+
const runtimeChanged = extra?.runtime !== undefined && (prev.anchors.runtime === undefined || prev.anchors.runtime.provider !== extra.runtime.provider || prev.anchors.runtime.model !== extra.runtime.model);
|
|
45117
|
+
changed = additions.length > 0 || treeChanged || runtimeChanged;
|
|
45118
|
+
if (!changed) {
|
|
45119
|
+
return prev;
|
|
45120
|
+
}
|
|
45121
|
+
const nextAnchors = {
|
|
45122
|
+
...prev.anchors,
|
|
45123
|
+
touched: additions.length > 0 ? [...prev.anchors.touched, ...additions] : prev.anchors.touched
|
|
45124
|
+
};
|
|
45125
|
+
if (extra?.tree !== undefined)
|
|
45126
|
+
nextAnchors.tree = extra.tree;
|
|
45127
|
+
if (extra?.runtime !== undefined)
|
|
45128
|
+
nextAnchors.runtime = extra.runtime;
|
|
45129
|
+
return { ...prev, anchors: nextAnchors };
|
|
45130
|
+
});
|
|
45131
|
+
return { changed, slate };
|
|
45132
|
+
}
|
|
44506
45133
|
var timestampMintCounter = 0;
|
|
44507
45134
|
function mintTimestampAttemptId(now = new Date) {
|
|
44508
45135
|
timestampMintCounter += 1;
|
|
44509
45136
|
return `${now.toISOString().replace(/:/g, "-")}-${timestampMintCounter}`;
|
|
44510
45137
|
}
|
|
44511
45138
|
|
|
45139
|
+
// src/session/slate-terminal-state.ts
|
|
45140
|
+
init_fs();
|
|
45141
|
+
init_repomap();
|
|
45142
|
+
init_redact();
|
|
45143
|
+
import path137 from "path";
|
|
45144
|
+
var DEFAULT_TERMINAL_STATE_MAX_TOKENS = 2000;
|
|
45145
|
+
function boundedRedactedAnchorsSnapshot(anchors, maxTokens) {
|
|
45146
|
+
const fence = anchors.fence?.map((entry) => redactSensitiveText(entry));
|
|
45147
|
+
const base = {
|
|
45148
|
+
root: anchors.root,
|
|
45149
|
+
...anchors.tree !== undefined ? { tree: anchors.tree } : {},
|
|
45150
|
+
...anchors.runtime !== undefined ? { runtime: anchors.runtime } : {},
|
|
45151
|
+
...fence !== undefined ? { fence } : {},
|
|
45152
|
+
touched: []
|
|
45153
|
+
};
|
|
45154
|
+
const baseTokens = estimateTokens(JSON.stringify(base));
|
|
45155
|
+
const touchedBudget = Math.max(0, maxTokens - baseTokens);
|
|
45156
|
+
const touched = redactAndBoundTouched(anchors.touched, touchedBudget, {
|
|
45157
|
+
traceRef: "slate-terminal-state-anchors",
|
|
45158
|
+
configurationRevision: "slate-terminal-state-anchors-v1",
|
|
45159
|
+
policyRef: "slate-terminal-state-anchors",
|
|
45160
|
+
policyRevision: "v1"
|
|
45161
|
+
});
|
|
45162
|
+
return { ...base, touched };
|
|
45163
|
+
}
|
|
45164
|
+
function renderTerminalStateBlock(state, opts) {
|
|
45165
|
+
const maxTokens = opts?.maxTokens ?? DEFAULT_TERMINAL_STATE_MAX_TOKENS;
|
|
45166
|
+
const anchorsSnapshot = boundedRedactedAnchorsSnapshot(state.anchorsSnapshot, maxTokens);
|
|
45167
|
+
return [
|
|
45168
|
+
"KERYX_TERMINAL_STATE",
|
|
45169
|
+
`status: ${state.status}`,
|
|
45170
|
+
`reason: ${state.reason}`,
|
|
45171
|
+
`occurredAt: ${state.occurredAt}`,
|
|
45172
|
+
`courseSnapshot: ${JSON.stringify(state.courseSnapshot)}`,
|
|
45173
|
+
`anchorsSnapshot: ${JSON.stringify(anchorsSnapshot)}`
|
|
45174
|
+
].join(`
|
|
45175
|
+
`);
|
|
45176
|
+
}
|
|
45177
|
+
async function writeTerminalState(dir, state) {
|
|
45178
|
+
await writeFileAtomic(path137.join(dir, "terminal-state.json"), `${JSON.stringify(state, null, 2)}
|
|
45179
|
+
`);
|
|
45180
|
+
}
|
|
45181
|
+
|
|
44512
45182
|
// src/commands/agent.ts
|
|
44513
45183
|
var DEFAULT_MAX_TOOL_CALLS = 48;
|
|
44514
45184
|
var ENV_AGENT_MAX_TOOL_CALLS = "KERYX_AGENT_MAX_TOOL_CALLS";
|
|
@@ -44692,6 +45362,25 @@ function parseToolInput(raw) {
|
|
|
44692
45362
|
return {};
|
|
44693
45363
|
}
|
|
44694
45364
|
}
|
|
45365
|
+
function extractTouchedFromToolInput(name, input2) {
|
|
45366
|
+
const pathLikeFields = ["path", "file", "dir", "target"];
|
|
45367
|
+
const out = [];
|
|
45368
|
+
for (const field3 of pathLikeFields) {
|
|
45369
|
+
const value = input2[field3];
|
|
45370
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
45371
|
+
out.push(value.trim());
|
|
45372
|
+
}
|
|
45373
|
+
}
|
|
45374
|
+
if (name === "spawn_subagent") {
|
|
45375
|
+
const label = typeof input2.label === "string" ? input2.label.trim() : "";
|
|
45376
|
+
const task = typeof input2.task === "string" ? input2.task.trim() : "";
|
|
45377
|
+
const marker2 = label.length > 0 ? label : task.length > 0 ? task.slice(0, 40) : "";
|
|
45378
|
+
if (marker2.length > 0) {
|
|
45379
|
+
out.push(`subagent:${marker2}`);
|
|
45380
|
+
}
|
|
45381
|
+
}
|
|
45382
|
+
return out;
|
|
45383
|
+
}
|
|
44695
45384
|
function stableStringify2(value) {
|
|
44696
45385
|
if (value === null || typeof value !== "object") {
|
|
44697
45386
|
return JSON.stringify(value);
|
|
@@ -44766,6 +45455,46 @@ function reserveToolAttempt(state, name, input2, risk) {
|
|
|
44766
45455
|
state.attempts.set(hash2, attempt);
|
|
44767
45456
|
return { ok: true, hash: hash2, attempt, chargedNew: isNew };
|
|
44768
45457
|
}
|
|
45458
|
+
async function resolveTerminalStateSnapshots(options) {
|
|
45459
|
+
const ref = options.slateSession;
|
|
45460
|
+
if (ref !== undefined && ref.opened) {
|
|
45461
|
+
try {
|
|
45462
|
+
const slate = await readSlate(ref.dir);
|
|
45463
|
+
if (slate !== undefined) {
|
|
45464
|
+
return { courseSnapshot: slate.course, anchorsSnapshot: slate.anchors };
|
|
45465
|
+
}
|
|
45466
|
+
} catch {}
|
|
45467
|
+
}
|
|
45468
|
+
return { courseSnapshot: {}, anchorsSnapshot: { root: "", touched: [] } };
|
|
45469
|
+
}
|
|
45470
|
+
async function emitTerminalState(io, deps, options, reason) {
|
|
45471
|
+
const { courseSnapshot, anchorsSnapshot } = await resolveTerminalStateSnapshots(options);
|
|
45472
|
+
const now = deps.now ?? (() => new Date().toISOString());
|
|
45473
|
+
const state = {
|
|
45474
|
+
status: "blocked",
|
|
45475
|
+
reason,
|
|
45476
|
+
courseSnapshot,
|
|
45477
|
+
anchorsSnapshot,
|
|
45478
|
+
occurredAt: now()
|
|
45479
|
+
};
|
|
45480
|
+
io.onTerminalState?.(state);
|
|
45481
|
+
const ref = options.slateSession;
|
|
45482
|
+
if (ref !== undefined && ref.opened) {
|
|
45483
|
+
try {
|
|
45484
|
+
await writeTerminalState(ref.dir, state);
|
|
45485
|
+
} catch {}
|
|
45486
|
+
}
|
|
45487
|
+
const block = renderTerminalStateBlock(state);
|
|
45488
|
+
if (io.onSystem !== undefined) {
|
|
45489
|
+
io.onSystem(`
|
|
45490
|
+
${block}
|
|
45491
|
+
`);
|
|
45492
|
+
} else {
|
|
45493
|
+
io.write(`
|
|
45494
|
+
${block}
|
|
45495
|
+
`);
|
|
45496
|
+
}
|
|
45497
|
+
}
|
|
44769
45498
|
async function runAgentTurn(io, deps, history, userLine, options = {}) {
|
|
44770
45499
|
try {
|
|
44771
45500
|
await runAgentTurnCore(io, deps, history, userLine, options);
|
|
@@ -44810,13 +45539,21 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
44810
45539
|
const actionRequest = isActionRequest(userLine);
|
|
44811
45540
|
if (options.slateSession !== undefined) {
|
|
44812
45541
|
try {
|
|
44813
|
-
if (isClosePhrase(userLine)) {
|
|
45542
|
+
if (options.skipCloseTrigger !== true && isClosePhrase(userLine)) {
|
|
44814
45543
|
await closeSlateSession(options.slateSession, () => deps.idSeq());
|
|
44815
45544
|
} else if (actionRequest) {
|
|
45545
|
+
const wasOpened = options.slateSession.opened;
|
|
44816
45546
|
await ensureSlateOpened(options.slateSession, () => deps.idSeq(), {
|
|
44817
45547
|
provider: deps.providerId,
|
|
44818
45548
|
model: deps.modelId
|
|
44819
45549
|
});
|
|
45550
|
+
if (!wasOpened && options.slateSession.opened) {
|
|
45551
|
+
const freshSlate = await readSlate(options.slateSession.dir);
|
|
45552
|
+
if (freshSlate !== undefined) {
|
|
45553
|
+
history.push({ role: "user", content: renderAnchorsBlock(freshSlate.anchors), provenance: "project" });
|
|
45554
|
+
io.onHistoryChange?.("tool");
|
|
45555
|
+
}
|
|
45556
|
+
}
|
|
44820
45557
|
}
|
|
44821
45558
|
} catch (err) {
|
|
44822
45559
|
io.onSystem?.(`slate open/close check failed (ignored): ${err instanceof Error ? err.message : String(err)}
|
|
@@ -44984,6 +45721,10 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
44984
45721
|
`);
|
|
44985
45722
|
return;
|
|
44986
45723
|
}
|
|
45724
|
+
if (deps.unattended === true && call.name === "ask_user") {
|
|
45725
|
+
await emitTerminalState(io, deps, options, "ask_user_unanswerable");
|
|
45726
|
+
return;
|
|
45727
|
+
}
|
|
44987
45728
|
if (untrustedContentSeen || batchContainsUntrustedWeb && call.name !== "web_fetch" && call.name !== "web_search") {
|
|
44988
45729
|
const result2 = {
|
|
44989
45730
|
output: "tool blocked: external web content cannot authorize further tool calls in this turn",
|
|
@@ -45026,6 +45767,21 @@ ${modelOutput}` : modelOutput,
|
|
|
45026
45767
|
if (result.untrusted === true && !result.isError) {
|
|
45027
45768
|
untrustedContentSeen = true;
|
|
45028
45769
|
}
|
|
45770
|
+
if (options.slateSession !== undefined && options.slateSession.opened === true) {
|
|
45771
|
+
try {
|
|
45772
|
+
const touchedPaths = extractTouchedFromToolInput(call.name, parseToolInput(call.input));
|
|
45773
|
+
const touch = await recordSlateTouch(options.slateSession.dir, touchedPaths, {
|
|
45774
|
+
runtime: { provider: deps.providerId, model: deps.modelId }
|
|
45775
|
+
});
|
|
45776
|
+
if (touch.changed) {
|
|
45777
|
+
history.push({ role: "user", content: renderAnchorsBlock(touch.slate.anchors), provenance: "project" });
|
|
45778
|
+
io.onHistoryChange?.("tool");
|
|
45779
|
+
}
|
|
45780
|
+
} catch (err) {
|
|
45781
|
+
io.onSystem?.(`slate touch update failed (ignored): ${err instanceof Error ? err.message : String(err)}
|
|
45782
|
+
`);
|
|
45783
|
+
}
|
|
45784
|
+
}
|
|
45029
45785
|
const shortIn = call.input.length > 80 ? `${call.input.slice(0, 77)}\u2026` : call.input;
|
|
45030
45786
|
const riskUsage = risk === "read" ? `, read ${readBudgetUsed(budget)}/${maxReadToolCalls}` : `, non-read ${nonReadBudgetUsed(budget)}/${maxNonReadToolCalls}`;
|
|
45031
45787
|
toolLog.push(`${call.name}(${shortIn}) \u2192 ${result.isError ? "error" : "ok"} [attempt ${reservation.attempt}/${maxAttempts}, unique ${budgetUsed(budget)}/${maxToolCalls}${riskUsage}]`);
|
|
@@ -45050,6 +45806,10 @@ ${hint}
|
|
|
45050
45806
|
}
|
|
45051
45807
|
const noProgress = !executedAny && calls.length > 0;
|
|
45052
45808
|
if (exhaustedBudget !== undefined || noProgress) {
|
|
45809
|
+
if (deps.unattended === true) {
|
|
45810
|
+
await emitTerminalState(io, deps, options, "budget_exhausted");
|
|
45811
|
+
return;
|
|
45812
|
+
}
|
|
45053
45813
|
await finishWithBudgetSummary(io, deps, history, parentRunId, {
|
|
45054
45814
|
maxUnique: maxToolCalls,
|
|
45055
45815
|
maxAttempts,
|
|
@@ -45206,6 +45966,7 @@ function emitSubagentFleet(event) {
|
|
|
45206
45966
|
}
|
|
45207
45967
|
|
|
45208
45968
|
// src/harness/tool/builtin/spawn-subagent-tool.ts
|
|
45969
|
+
init_fs();
|
|
45209
45970
|
var MAX_CHILD_SUMMARY_CHARS = 16000;
|
|
45210
45971
|
var ENV_SUBAGENT_TIMEOUT_MS = "KERYX_SUBAGENT_TIMEOUT_MS";
|
|
45211
45972
|
function resolveSubagentTimeoutMs(reservationMs, env = process.env) {
|
|
@@ -45230,13 +45991,13 @@ function boundSummary(text) {
|
|
|
45230
45991
|
return `${text.slice(0, MAX_CHILD_SUMMARY_CHARS)}
|
|
45231
45992
|
\u2026(truncated: ${dropped} more characters from the subagent)`;
|
|
45232
45993
|
}
|
|
45233
|
-
function
|
|
45234
|
-
return
|
|
45994
|
+
function sha2569(text) {
|
|
45995
|
+
return createHash29("sha256").update(text).digest("hex");
|
|
45235
45996
|
}
|
|
45236
45997
|
var parentShellPolicy = shellParentProfile;
|
|
45237
45998
|
var childReadOnlyPolicy = shellChildReadOnlyProfile;
|
|
45238
45999
|
function createSpawnSubagentTool(deps) {
|
|
45239
|
-
const idSeq = deps.idSeq ?? (() =>
|
|
46000
|
+
const idSeq = deps.idSeq ?? (() => randomUUID18());
|
|
45240
46001
|
const clock = deps.clock ?? (() => new Date().toISOString());
|
|
45241
46002
|
const parentRunId = deps.parentRunId ?? idSeq();
|
|
45242
46003
|
const parentSessionId = deps.parentSessionId ?? idSeq();
|
|
@@ -45281,7 +46042,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
45281
46042
|
parentRunId,
|
|
45282
46043
|
parentSessionId,
|
|
45283
46044
|
parentProvenance,
|
|
45284
|
-
contextManifestHash:
|
|
46045
|
+
contextManifestHash: sha2569(`${parentRunId}:${parentSessionId}`),
|
|
45285
46046
|
canonicalContractVersion: "1.0.0",
|
|
45286
46047
|
parentModel: { providerId: parent.providerId, modelId: parent.modelId },
|
|
45287
46048
|
parentPolicy: parentShellPolicy(),
|
|
@@ -45292,7 +46053,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
45292
46053
|
const attemptId = idSeq();
|
|
45293
46054
|
const branchId = idSeq();
|
|
45294
46055
|
const reservationId = idSeq();
|
|
45295
|
-
const artifactHash =
|
|
46056
|
+
const artifactHash = sha2569(task);
|
|
45296
46057
|
const spawned = spawnSubagent({
|
|
45297
46058
|
attempt: { attemptId, number: childSeq },
|
|
45298
46059
|
branchId,
|
|
@@ -45343,7 +46104,32 @@ function createSpawnSubagentTool(deps) {
|
|
|
45343
46104
|
...builtinReadOnlyTools(cwd),
|
|
45344
46105
|
...builtinMetaprojectTools(cwd, makeKeryxRunner(cwd), createMetaprojectAdapter(cwd))
|
|
45345
46106
|
];
|
|
45346
|
-
|
|
46107
|
+
let ephemeralDir;
|
|
46108
|
+
let openedChildSlate;
|
|
46109
|
+
let dispatchId = "";
|
|
46110
|
+
let closing = false;
|
|
46111
|
+
try {
|
|
46112
|
+
ephemeralDir = await mkdtemp(path138.join(tmpdir4(), "keryx-subagent-slate-"));
|
|
46113
|
+
dispatchId = path138.basename(ephemeralDir);
|
|
46114
|
+
openedChildSlate = await openSlate({
|
|
46115
|
+
dir: ephemeralDir,
|
|
46116
|
+
cwd,
|
|
46117
|
+
mintAttemptId: () => idSeq(),
|
|
46118
|
+
runtime: { provider: runModel.provider, model: runModel.model }
|
|
46119
|
+
});
|
|
46120
|
+
tools.push(slateWriteSeedTool(() => closing ? undefined : ephemeralDir, idSeq, clock));
|
|
46121
|
+
} catch {
|
|
46122
|
+
openedChildSlate = undefined;
|
|
46123
|
+
}
|
|
46124
|
+
let provider;
|
|
46125
|
+
try {
|
|
46126
|
+
provider = deps.makeProvider(runModel.provider, runModel.model, parent.baseUrl);
|
|
46127
|
+
} catch (cause) {
|
|
46128
|
+
if (ephemeralDir !== undefined) {
|
|
46129
|
+
await rm8(ephemeralDir, { recursive: true, force: true }).catch(() => {});
|
|
46130
|
+
}
|
|
46131
|
+
throw cause;
|
|
46132
|
+
}
|
|
45347
46133
|
const childDeps = {
|
|
45348
46134
|
provider,
|
|
45349
46135
|
providerId: runModel.provider,
|
|
@@ -45417,8 +46203,63 @@ function createSpawnSubagentTool(deps) {
|
|
|
45417
46203
|
maxToolCalls: childToolCalls
|
|
45418
46204
|
});
|
|
45419
46205
|
};
|
|
46206
|
+
const foldChildSlateAndCleanup = async (status) => {
|
|
46207
|
+
closing = true;
|
|
46208
|
+
if (ephemeralDir === undefined) {
|
|
46209
|
+
return;
|
|
46210
|
+
}
|
|
46211
|
+
const dirToClean = ephemeralDir;
|
|
46212
|
+
try {
|
|
46213
|
+
await withFileLock2(slateLockPath(dirToClean), async () => {
|
|
46214
|
+
try {
|
|
46215
|
+
const slateSession = deps.getSlateSession?.();
|
|
46216
|
+
if (openedChildSlate !== undefined && slateSession !== undefined) {
|
|
46217
|
+
try {
|
|
46218
|
+
const childSlate = await readSlate(dirToClean);
|
|
46219
|
+
const dispatch = {
|
|
46220
|
+
anchors: childSlate?.anchors ?? openedChildSlate.anchors,
|
|
46221
|
+
course: childSlate?.course ?? openedChildSlate.course,
|
|
46222
|
+
seeds: childSlate?.seeds ?? [],
|
|
46223
|
+
status
|
|
46224
|
+
};
|
|
46225
|
+
const parentDir = slateSession.dir;
|
|
46226
|
+
await writeSlate(parentDir, (prev) => {
|
|
46227
|
+
if (prev === undefined) {
|
|
46228
|
+
throw new Error(`foldChildSlateAndCleanup: no open parent slate in ${parentDir}`);
|
|
46229
|
+
}
|
|
46230
|
+
return {
|
|
46231
|
+
...prev,
|
|
46232
|
+
childDispatches: { ...prev.childDispatches ?? {}, [dispatchId]: dispatch }
|
|
46233
|
+
};
|
|
46234
|
+
});
|
|
46235
|
+
} catch (foldCause) {
|
|
46236
|
+
const foldMsg = foldCause instanceof Error ? foldCause.message : String(foldCause);
|
|
46237
|
+
emitSubagentFleet({
|
|
46238
|
+
kind: "log",
|
|
46239
|
+
id: workerId,
|
|
46240
|
+
entry: { kind: "system", text: `slate fold failed (ignored): ${foldMsg}` }
|
|
46241
|
+
});
|
|
46242
|
+
}
|
|
46243
|
+
}
|
|
46244
|
+
} finally {
|
|
46245
|
+
await rm8(dirToClean, { recursive: true, force: true }).catch(() => {});
|
|
46246
|
+
}
|
|
46247
|
+
});
|
|
46248
|
+
} catch (lockCause) {
|
|
46249
|
+
const lockMsg = lockCause instanceof Error ? lockCause.message : String(lockCause);
|
|
46250
|
+
emitSubagentFleet({
|
|
46251
|
+
kind: "log",
|
|
46252
|
+
id: workerId,
|
|
46253
|
+
entry: { kind: "system", text: `slate cleanup lock failed (ignored): ${lockMsg}` }
|
|
46254
|
+
});
|
|
46255
|
+
await rm8(dirToClean, { recursive: true, force: true }).catch(() => {});
|
|
46256
|
+
}
|
|
46257
|
+
};
|
|
45420
46258
|
try {
|
|
45421
46259
|
const history = [];
|
|
46260
|
+
if (openedChildSlate !== undefined) {
|
|
46261
|
+
history.push({ role: "user", content: renderAnchorsBlock(openedChildSlate.anchors), provenance: "project" });
|
|
46262
|
+
}
|
|
45422
46263
|
const userLine = `## Subagent task (${mode})
|
|
45423
46264
|
` + `${task}
|
|
45424
46265
|
|
|
@@ -45442,6 +46283,7 @@ function createSpawnSubagentTool(deps) {
|
|
|
45442
46283
|
turn.catch(() => {});
|
|
45443
46284
|
releaseBudget();
|
|
45444
46285
|
emitSubagentFleet({ kind: "upsert", id: workerId, label, status: "failed", detail: "timeout", task });
|
|
46286
|
+
await foldChildSlateAndCleanup("incomplete");
|
|
45445
46287
|
const partial = assistant.trim();
|
|
45446
46288
|
return {
|
|
45447
46289
|
output: `subagent ${label} (${workerId}) timed out after ${deadlineMs}ms and was abandoned ` + `(tighten or disable with ${ENV_SUBAGENT_TIMEOUT_MS})` + (partial.length > 0 ? `
|
|
@@ -45467,6 +46309,7 @@ ${boundSummary(partial)}` : ""),
|
|
|
45467
46309
|
model: `${runModel.provider}/${runModel.model}`,
|
|
45468
46310
|
task
|
|
45469
46311
|
});
|
|
46312
|
+
await foldChildSlateAndCleanup("completed");
|
|
45470
46313
|
return {
|
|
45471
46314
|
output: `subagent ${label} (${workerId}) ${mode} via ${runModel.provider}/${runModel.model}
|
|
45472
46315
|
` + `MAE reservation: tools\u2264${spawned.reservation.maxToolCalls ?? maxToolCalls} ` + `runtime\u2264${spawned.reservation.maxRuntimeMs}ms children=${ledger.childCount}
|
|
@@ -45487,6 +46330,7 @@ ${boundSummary(folded.text)}`,
|
|
|
45487
46330
|
detail: "error",
|
|
45488
46331
|
task
|
|
45489
46332
|
});
|
|
46333
|
+
await foldChildSlateAndCleanup("incomplete");
|
|
45490
46334
|
return { output: `subagent ${label} failed: ${msg}`, isError: true };
|
|
45491
46335
|
}
|
|
45492
46336
|
}
|
|
@@ -45497,12 +46341,12 @@ ${boundSummary(folded.text)}`,
|
|
|
45497
46341
|
import { homedir as homedir6 } from "os";
|
|
45498
46342
|
var ESC = "\x1B";
|
|
45499
46343
|
var CSI = `${ESC}[`;
|
|
45500
|
-
function collapseHome(
|
|
46344
|
+
function collapseHome(path139) {
|
|
45501
46345
|
const home = homedir6();
|
|
45502
|
-
if (home.length > 0 && (
|
|
45503
|
-
return `~${
|
|
46346
|
+
if (home.length > 0 && (path139 === home || path139.startsWith(`${home}/`))) {
|
|
46347
|
+
return `~${path139.slice(home.length)}`;
|
|
45504
46348
|
}
|
|
45505
|
-
return
|
|
46349
|
+
return path139;
|
|
45506
46350
|
}
|
|
45507
46351
|
|
|
45508
46352
|
// src/lib/live-render.ts
|
|
@@ -45605,12 +46449,85 @@ class LiveMarkdownBlock {
|
|
|
45605
46449
|
}
|
|
45606
46450
|
}
|
|
45607
46451
|
|
|
46452
|
+
// src/commands/goal-command.ts
|
|
46453
|
+
function parseGoalArgs(rest) {
|
|
46454
|
+
const trimmed = rest.trim();
|
|
46455
|
+
if (trimmed.length === 0) {
|
|
46456
|
+
return { error: "a goal <text> is required, e.g. /goal implement the login flow [--workspace <id>]" };
|
|
46457
|
+
}
|
|
46458
|
+
const tokens = trimmed.split(/\s+/);
|
|
46459
|
+
const lastToken = tokens[tokens.length - 1];
|
|
46460
|
+
const secondLastToken = tokens[tokens.length - 2];
|
|
46461
|
+
let workspaceId;
|
|
46462
|
+
let textTokens = tokens;
|
|
46463
|
+
if (lastToken === "--workspace") {
|
|
46464
|
+
return { error: "--workspace requires a value, e.g. /goal <text> --workspace <id>" };
|
|
46465
|
+
}
|
|
46466
|
+
if (secondLastToken === "--workspace") {
|
|
46467
|
+
workspaceId = lastToken;
|
|
46468
|
+
textTokens = tokens.slice(0, tokens.length - 2);
|
|
46469
|
+
}
|
|
46470
|
+
const text = textTokens.join(" ").trim();
|
|
46471
|
+
if (text.length === 0) {
|
|
46472
|
+
return { error: "a goal <text> is required, e.g. /goal implement the login flow [--workspace <id>]" };
|
|
46473
|
+
}
|
|
46474
|
+
return workspaceId !== undefined ? { text, workspaceId } : { text };
|
|
46475
|
+
}
|
|
46476
|
+
function systemLine(io, text) {
|
|
46477
|
+
if (io.onSystem !== undefined) {
|
|
46478
|
+
io.onSystem(text);
|
|
46479
|
+
} else {
|
|
46480
|
+
io.write(text);
|
|
46481
|
+
}
|
|
46482
|
+
}
|
|
46483
|
+
async function runGoalCommand(params) {
|
|
46484
|
+
const { raw, cwd, io, deps, history, slateSession, mintAttemptId } = params;
|
|
46485
|
+
const parsed = parseGoalArgs(raw);
|
|
46486
|
+
if ("error" in parsed) {
|
|
46487
|
+
systemLine(io, `/goal: ${parsed.error}
|
|
46488
|
+
`);
|
|
46489
|
+
return;
|
|
46490
|
+
}
|
|
46491
|
+
if (parsed.workspaceId !== undefined) {
|
|
46492
|
+
const resolved = await resolveWorkspaceForActor(cwd, parsed.workspaceId);
|
|
46493
|
+
if (!resolved.ok) {
|
|
46494
|
+
systemLine(io, `/goal: --workspace "${parsed.workspaceId}" was rejected (${resolved.error.code}): ${resolved.error.message}. ` + `The slate was not opened and the goal was not run.
|
|
46495
|
+
`);
|
|
46496
|
+
return;
|
|
46497
|
+
}
|
|
46498
|
+
}
|
|
46499
|
+
if (slateSession !== undefined) {
|
|
46500
|
+
try {
|
|
46501
|
+
const wasOpened = slateSession.opened;
|
|
46502
|
+
await ensureSlateOpened(slateSession, mintAttemptId, { provider: deps.providerId, model: deps.modelId });
|
|
46503
|
+
if (!wasOpened && slateSession.opened) {
|
|
46504
|
+
const freshSlate = await readSlate(slateSession.dir);
|
|
46505
|
+
if (freshSlate !== undefined) {
|
|
46506
|
+
history.push({ role: "user", content: renderAnchorsBlock(freshSlate.anchors), provenance: "project" });
|
|
46507
|
+
io.onHistoryChange?.("tool");
|
|
46508
|
+
}
|
|
46509
|
+
}
|
|
46510
|
+
if (parsed.workspaceId !== undefined) {
|
|
46511
|
+
const workspaceId = parsed.workspaceId;
|
|
46512
|
+
await writeSlate(slateSession.dir, (prev) => {
|
|
46513
|
+
const base = prev ?? { anchors: { root: "", touched: [] }, course: {}, seeds: [] };
|
|
46514
|
+
return { ...base, workspaceId };
|
|
46515
|
+
});
|
|
46516
|
+
}
|
|
46517
|
+
} catch (err) {
|
|
46518
|
+
systemLine(io, `/goal: slate bookkeeping failed (ignored): ${err instanceof Error ? err.message : String(err)}
|
|
46519
|
+
`);
|
|
46520
|
+
}
|
|
46521
|
+
}
|
|
46522
|
+
await runAgentTurn(io, deps, history, parsed.text, slateSession !== undefined ? { slateSession, skipCloseTrigger: true } : {});
|
|
46523
|
+
}
|
|
46524
|
+
|
|
45608
46525
|
// src/tui/tui-shell.ts
|
|
45609
46526
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
45610
46527
|
// package.json
|
|
45611
46528
|
var package_default = {
|
|
45612
46529
|
name: "@mrciphersmith/keryx",
|
|
45613
|
-
version: "0.2.
|
|
46530
|
+
version: "0.2.39",
|
|
45614
46531
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
45615
46532
|
private: false,
|
|
45616
46533
|
publishConfig: {
|
|
@@ -46135,7 +47052,7 @@ function openFlows(otui, chrome, options) {
|
|
|
46135
47052
|
|
|
46136
47053
|
// src/tui/inspector-sources.ts
|
|
46137
47054
|
init_store2();
|
|
46138
|
-
import { randomUUID as
|
|
47055
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
46139
47056
|
function workspaceFromManifest(manifest) {
|
|
46140
47057
|
return {
|
|
46141
47058
|
id: manifest.id,
|
|
@@ -46205,7 +47122,7 @@ async function loadInspectorWorkspaces(cwd) {
|
|
|
46205
47122
|
policyRevision: "local-offline-v1"
|
|
46206
47123
|
}
|
|
46207
47124
|
});
|
|
46208
|
-
const listed = await service4.list({ request: undefined, requestCorrelationId:
|
|
47125
|
+
const listed = await service4.list({ request: undefined, requestCorrelationId: randomUUID19() });
|
|
46209
47126
|
return listed.map(workspaceFromManifest);
|
|
46210
47127
|
} catch {
|
|
46211
47128
|
return [];
|
|
@@ -46563,6 +47480,11 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
46563
47480
|
modes: AGENT_ONLY
|
|
46564
47481
|
},
|
|
46565
47482
|
{ name: "/new", description: "Start a new session (old kept on disk)", modes: BOTH },
|
|
47483
|
+
{
|
|
47484
|
+
name: "/goal",
|
|
47485
|
+
description: "Deterministically start a goal \u2014 /goal <text> [--workspace <id>]",
|
|
47486
|
+
modes: AGENT_ONLY
|
|
47487
|
+
},
|
|
46566
47488
|
{ name: "/resume", description: "Resume a prior session in this project", modes: AGENT_ONLY },
|
|
46567
47489
|
{ name: "/sessions", description: "Open the session list and switch to one", modes: AGENT_ONLY },
|
|
46568
47490
|
{
|
|
@@ -46839,7 +47761,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
46839
47761
|
// src/lib/version-check.ts
|
|
46840
47762
|
init_config_dir();
|
|
46841
47763
|
init_fs();
|
|
46842
|
-
import
|
|
47764
|
+
import path139 from "path";
|
|
46843
47765
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
46844
47766
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
46845
47767
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -47041,7 +47963,7 @@ async function checkVersion(options) {
|
|
|
47041
47963
|
const now = options.now ?? Date.now;
|
|
47042
47964
|
const timestamp = now();
|
|
47043
47965
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
47044
|
-
const cacheFile =
|
|
47966
|
+
const cacheFile = path139.join(configDir, "version-check.json");
|
|
47045
47967
|
const cache = parseCache(cacheFile);
|
|
47046
47968
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
47047
47969
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -49252,6 +50174,22 @@ async function pickShellApproval(otui, r, dock, command, loadContext2, destructi
|
|
|
49252
50174
|
}
|
|
49253
50175
|
return "deny";
|
|
49254
50176
|
}
|
|
50177
|
+
async function applyRuntimeSwitchToSlate(params) {
|
|
50178
|
+
if (params.slateSession === undefined || !params.slateSession.opened) {
|
|
50179
|
+
return false;
|
|
50180
|
+
}
|
|
50181
|
+
const result = await recordSlateTouch(params.slateSession.dir, [], { runtime: params.runtime });
|
|
50182
|
+
if (!result.changed) {
|
|
50183
|
+
return false;
|
|
50184
|
+
}
|
|
50185
|
+
params.history.push({
|
|
50186
|
+
role: "user",
|
|
50187
|
+
content: renderAnchorsBlock(result.slate.anchors),
|
|
50188
|
+
provenance: "project"
|
|
50189
|
+
});
|
|
50190
|
+
params.onHistoryChange?.("tool");
|
|
50191
|
+
return true;
|
|
50192
|
+
}
|
|
49255
50193
|
function fmtTokens(n) {
|
|
49256
50194
|
return n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n);
|
|
49257
50195
|
}
|
|
@@ -49687,7 +50625,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
49687
50625
|
}
|
|
49688
50626
|
saveShellConfig(sel.baseUrl === undefined ? { provider: sel.provider, model: sel.model } : { provider: sel.provider, model: sel.model, baseUrl: sel.baseUrl });
|
|
49689
50627
|
let currentSel = sel;
|
|
49690
|
-
let deps = await opts.makeAgentDeps(sel);
|
|
50628
|
+
let deps = await opts.makeAgentDeps(sel, () => slateSession);
|
|
49691
50629
|
const FOOTER_IDLE = "/ commands \xB7 Ctrl+O blocks \xB7 Ctrl+C to exit";
|
|
49692
50630
|
const FOOTER_NAV = "blocks \xB7 \u2191/\u2193 move \xB7 Enter toggle \xB7 y copy \xB7 Esc exit";
|
|
49693
50631
|
const chrome = await createShellChrome(otui, r, {
|
|
@@ -50277,7 +51215,7 @@ Staying in the current session.
|
|
|
50277
51215
|
};
|
|
50278
51216
|
const switchTo = async (ns) => {
|
|
50279
51217
|
currentSel = ns;
|
|
50280
|
-
deps = await opts.makeAgentDeps(ns);
|
|
51218
|
+
deps = await opts.makeAgentDeps(ns, () => slateSession);
|
|
50281
51219
|
saveShellConfig(ns.baseUrl === undefined ? { provider: ns.provider, model: ns.model } : { provider: ns.provider, model: ns.model, baseUrl: ns.baseUrl });
|
|
50282
51220
|
updateModelLabels();
|
|
50283
51221
|
input2.focus();
|
|
@@ -50375,7 +51313,7 @@ Staying in the current session.
|
|
|
50375
51313
|
});
|
|
50376
51314
|
let answer = "";
|
|
50377
51315
|
try {
|
|
50378
|
-
const base = await opts.makeAgentDeps(currentSel);
|
|
51316
|
+
const base = await opts.makeAgentDeps(currentSel, () => slateSession);
|
|
50379
51317
|
const tools = base.tools.filter((t) => t.definition.risk === "read");
|
|
50380
51318
|
const sideDeps = {
|
|
50381
51319
|
...base,
|
|
@@ -50528,6 +51466,20 @@ Staying in the current session.
|
|
|
50528
51466
|
})();
|
|
50529
51467
|
return;
|
|
50530
51468
|
}
|
|
51469
|
+
if (command.name === "/goal") {
|
|
51470
|
+
(async () => {
|
|
51471
|
+
await runGoalCommand({
|
|
51472
|
+
raw: line.slice(command.name.length).trim(),
|
|
51473
|
+
cwd: sessionCwd,
|
|
51474
|
+
io,
|
|
51475
|
+
deps,
|
|
51476
|
+
history,
|
|
51477
|
+
slateSession,
|
|
51478
|
+
mintAttemptId: mintTimestampAttemptId
|
|
51479
|
+
});
|
|
51480
|
+
})();
|
|
51481
|
+
return;
|
|
51482
|
+
}
|
|
50531
51483
|
if (command.name === "/resume" || command.name === "/sessions") {
|
|
50532
51484
|
resumeSessionInteractive();
|
|
50533
51485
|
return;
|
|
@@ -50657,6 +51609,17 @@ Staying in the current session.
|
|
|
50657
51609
|
const chosen = await chrome.withOverlay(() => pickModelInTui(otui, r, models));
|
|
50658
51610
|
if (chosen !== undefined) {
|
|
50659
51611
|
await switchTo(currentSel.baseUrl === undefined ? { provider: currentSel.provider, model: chosen } : { provider: currentSel.provider, model: chosen, baseUrl: currentSel.baseUrl });
|
|
51612
|
+
try {
|
|
51613
|
+
await applyRuntimeSwitchToSlate({
|
|
51614
|
+
slateSession,
|
|
51615
|
+
runtime: { provider: currentSel.provider, model: currentSel.model },
|
|
51616
|
+
history,
|
|
51617
|
+
onHistoryChange: io.onHistoryChange
|
|
51618
|
+
});
|
|
51619
|
+
} catch (err) {
|
|
51620
|
+
io.onSystem?.(`slate anchors update failed (ignored): ${err instanceof Error ? err.message : String(err)}
|
|
51621
|
+
`);
|
|
51622
|
+
}
|
|
50660
51623
|
} else {
|
|
50661
51624
|
input2.focus();
|
|
50662
51625
|
}
|
|
@@ -51468,6 +52431,7 @@ var READLINE_AGENT_COMMANDS = [
|
|
|
51468
52431
|
"/search-provider",
|
|
51469
52432
|
"/search-connect",
|
|
51470
52433
|
"/new",
|
|
52434
|
+
"/goal",
|
|
51471
52435
|
"/clear",
|
|
51472
52436
|
"/compact",
|
|
51473
52437
|
"/status",
|
|
@@ -51990,7 +52954,7 @@ function formatUsage(usage) {
|
|
|
51990
52954
|
}
|
|
51991
52955
|
return style.dim(`${parts.join(" ")} tokens`);
|
|
51992
52956
|
}
|
|
51993
|
-
async function runAgentRepl(lines, rich, deps, metaprojectPort, sessionOpts) {
|
|
52957
|
+
async function runAgentRepl(lines, rich, deps, metaprojectPort, sessionOpts, slateSessionBox = { current: undefined }) {
|
|
51994
52958
|
const out = (s) => {
|
|
51995
52959
|
process.stdout.write(s);
|
|
51996
52960
|
};
|
|
@@ -52238,6 +53202,7 @@ New session ${shortSessionId(live.summary.id)}.
|
|
|
52238
53202
|
}
|
|
52239
53203
|
}
|
|
52240
53204
|
slateSession = live !== undefined ? { dir: live.dir, cwd: sessionCwd, opened: false } : undefined;
|
|
53205
|
+
slateSessionBox.current = slateSession;
|
|
52241
53206
|
const save = () => {
|
|
52242
53207
|
if (live === undefined) {
|
|
52243
53208
|
return;
|
|
@@ -52344,6 +53309,7 @@ New session ${shortSessionId(live.summary.id)}.
|
|
|
52344
53309
|
archive = [];
|
|
52345
53310
|
nextArchiveIndex = 0;
|
|
52346
53311
|
slateSession = { dir: live.dir, cwd: sessionCwd, opened: false };
|
|
53312
|
+
slateSessionBox.current = slateSession;
|
|
52347
53313
|
agentIo.onSystem?.(`New session ${shortSessionId(live.summary.id)} (previous kept on disk)
|
|
52348
53314
|
`);
|
|
52349
53315
|
} else {
|
|
@@ -52432,6 +53398,16 @@ New session ${shortSessionId(live.summary.id)}.
|
|
|
52432
53398
|
}
|
|
52433
53399
|
agentIo.onSystem?.(`Search provider '${providerId}' selected.
|
|
52434
53400
|
`);
|
|
53401
|
+
} else if (command === "/goal") {
|
|
53402
|
+
await runGoalCommand({
|
|
53403
|
+
raw: rest,
|
|
53404
|
+
cwd: sessionCwd,
|
|
53405
|
+
io: agentIo,
|
|
53406
|
+
deps,
|
|
53407
|
+
history,
|
|
53408
|
+
slateSession,
|
|
53409
|
+
mintAttemptId: mintTimestampAttemptId
|
|
53410
|
+
});
|
|
52435
53411
|
} else {
|
|
52436
53412
|
agentIo.onSystem?.(describeUnavailableCommand(command, "agent") ?? `Unknown command: ${command}. Type /help.
|
|
52437
53413
|
`);
|
|
@@ -52566,7 +53542,8 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52566
53542
|
const cwd = process.cwd();
|
|
52567
53543
|
const tuiProviderFactory = realMakeProvider(() => {});
|
|
52568
53544
|
const searchProviderController = createDefaultSearchProviderController();
|
|
52569
|
-
const makeAgentDeps = async (sel) => {
|
|
53545
|
+
const makeAgentDeps = async (sel, getSlateSession) => {
|
|
53546
|
+
const getSessionDir = () => getSlateSession()?.dir;
|
|
52570
53547
|
const agentProvider = tuiProviderFactory(sel.provider, sel.model, sel.baseUrl);
|
|
52571
53548
|
let orient = "";
|
|
52572
53549
|
try {
|
|
@@ -52589,7 +53566,8 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52589
53566
|
names.add(d.name);
|
|
52590
53567
|
}
|
|
52591
53568
|
return [...names].map((name) => ({ name }));
|
|
52592
|
-
}
|
|
53569
|
+
},
|
|
53570
|
+
getSlateSession
|
|
52593
53571
|
});
|
|
52594
53572
|
return {
|
|
52595
53573
|
provider: agentProvider,
|
|
@@ -52599,14 +53577,15 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52599
53577
|
cwd,
|
|
52600
53578
|
metaprojectPort,
|
|
52601
53579
|
searchController: searchProviderController,
|
|
52602
|
-
spawnTool
|
|
53580
|
+
spawnTool,
|
|
53581
|
+
getSessionDir
|
|
52603
53582
|
}),
|
|
52604
53583
|
systemInstruction: buildAgentSystemInstruction(orient, {
|
|
52605
53584
|
providerId: sel.provider,
|
|
52606
53585
|
modelId: sel.model
|
|
52607
53586
|
}),
|
|
52608
53587
|
maxToolCalls: resolveAgentMaxToolCalls(),
|
|
52609
|
-
idSeq: () =>
|
|
53588
|
+
idSeq: () => randomUUID20()
|
|
52610
53589
|
};
|
|
52611
53590
|
};
|
|
52612
53591
|
const redetect = () => detectProviders({
|
|
@@ -52637,7 +53616,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52637
53616
|
makeShellDeps: (sel) => ({
|
|
52638
53617
|
makeProvider: chatFactory,
|
|
52639
53618
|
clock: () => new Date().toISOString(),
|
|
52640
|
-
idSeq: () =>
|
|
53619
|
+
idSeq: () => randomUUID20(),
|
|
52641
53620
|
initial: sel,
|
|
52642
53621
|
session: {
|
|
52643
53622
|
cwd,
|
|
@@ -52707,7 +53686,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52707
53686
|
const deps = {
|
|
52708
53687
|
makeProvider: baseFactory,
|
|
52709
53688
|
clock: () => new Date().toISOString(),
|
|
52710
|
-
idSeq: () =>
|
|
53689
|
+
idSeq: () => randomUUID20(),
|
|
52711
53690
|
initial: baseUrl2 === undefined ? { provider, model } : { provider, model, baseUrl: baseUrl2 },
|
|
52712
53691
|
selectProviderModel: realSelectProviderModel(baseUrl2)
|
|
52713
53692
|
};
|
|
@@ -52726,6 +53705,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52726
53705
|
const metaprojectPort = createMetaprojectAdapter(process.cwd());
|
|
52727
53706
|
const agentCwd = process.cwd();
|
|
52728
53707
|
const searchProviderController = createDefaultSearchProviderController();
|
|
53708
|
+
const slateSessionBox = { current: undefined };
|
|
52729
53709
|
const spawnTool = createSpawnSubagentTool({
|
|
52730
53710
|
cwd: agentCwd,
|
|
52731
53711
|
getParentModel: () => ({
|
|
@@ -52734,7 +53714,8 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52734
53714
|
...baseUrl2 !== undefined ? { baseUrl: baseUrl2 } : {}
|
|
52735
53715
|
}),
|
|
52736
53716
|
makeProvider: (providerId, modelId, childBaseUrl) => baseFactory(providerId, modelId, childBaseUrl ?? baseUrl2),
|
|
52737
|
-
getDetectedProviders: () => [{ name: provider }]
|
|
53717
|
+
getDetectedProviders: () => [{ name: provider }],
|
|
53718
|
+
getSlateSession: () => slateSessionBox.current
|
|
52738
53719
|
});
|
|
52739
53720
|
const agentDeps = {
|
|
52740
53721
|
provider: agentProvider,
|
|
@@ -52744,14 +53725,15 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52744
53725
|
cwd: agentCwd,
|
|
52745
53726
|
metaprojectPort,
|
|
52746
53727
|
searchController: searchProviderController,
|
|
52747
|
-
spawnTool
|
|
53728
|
+
spawnTool,
|
|
53729
|
+
getSessionDir: () => slateSessionBox.current?.dir
|
|
52748
53730
|
}),
|
|
52749
53731
|
systemInstruction: buildAgentSystemInstruction(orient, {
|
|
52750
53732
|
providerId: provider,
|
|
52751
53733
|
modelId: model
|
|
52752
53734
|
}),
|
|
52753
53735
|
maxToolCalls: resolveAgentMaxToolCalls(),
|
|
52754
|
-
idSeq: () =>
|
|
53736
|
+
idSeq: () => randomUUID20()
|
|
52755
53737
|
};
|
|
52756
53738
|
let resumeId = flags.resumeId;
|
|
52757
53739
|
if (flags.resumePick === true && resumeId === undefined) {
|
|
@@ -52761,7 +53743,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
52761
53743
|
cwd: process.cwd(),
|
|
52762
53744
|
...flags.continueLast === true ? { continueLast: true } : {},
|
|
52763
53745
|
...resumeId !== undefined ? { resumeId } : {}
|
|
52764
|
-
});
|
|
53746
|
+
}, slateSessionBox);
|
|
52765
53747
|
} else {
|
|
52766
53748
|
let resumeId = flags.resumeId;
|
|
52767
53749
|
if (flags.resumePick === true && resumeId === undefined) {
|
|
@@ -52922,7 +53904,7 @@ Shell:
|
|
|
52922
53904
|
init_fs();
|
|
52923
53905
|
import { readFile as readFile75 } from "fs/promises";
|
|
52924
53906
|
import { stdin } from "process";
|
|
52925
|
-
import
|
|
53907
|
+
import path140 from "path";
|
|
52926
53908
|
var MODULES = [
|
|
52927
53909
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
52928
53910
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -52968,8 +53950,8 @@ async function modulesCommand(args2 = []) {
|
|
|
52968
53950
|
return;
|
|
52969
53951
|
}
|
|
52970
53952
|
const wantsJson = args2.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
52971
|
-
const metaprojectRoot =
|
|
52972
|
-
const manifestPath =
|
|
53953
|
+
const metaprojectRoot = path140.join(process.cwd(), ".metaproject");
|
|
53954
|
+
const manifestPath = path140.join(metaprojectRoot, "metaproject.json");
|
|
52973
53955
|
if (!await pathExists(manifestPath)) {
|
|
52974
53956
|
if (wantsJson) {
|
|
52975
53957
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -53083,18 +54065,18 @@ function printHelp16() {
|
|
|
53083
54065
|
}
|
|
53084
54066
|
|
|
53085
54067
|
// src/commands/serve.ts
|
|
53086
|
-
import { randomUUID as
|
|
54068
|
+
import { randomUUID as randomUUID23 } from "crypto";
|
|
53087
54069
|
|
|
53088
54070
|
// src/lib/serve-config.ts
|
|
53089
54071
|
init_config_dir();
|
|
53090
54072
|
import { existsSync as existsSync26 } from "fs";
|
|
53091
|
-
import
|
|
54073
|
+
import path141 from "path";
|
|
53092
54074
|
var SERVE_CONFIG_SCHEMA_VERSION = "1.0.0";
|
|
53093
54075
|
var DEFAULT_SERVE_BIND_ADDRESS = "127.0.0.1";
|
|
53094
54076
|
var DEFAULT_SERVE_PORT = 7377;
|
|
53095
54077
|
var DEFAULT_SERVE_PROFILE = "remote-restricted";
|
|
53096
54078
|
function serveConfigPath(dir) {
|
|
53097
|
-
return
|
|
54079
|
+
return path141.join(keryxConfigDir(dir), "serve.json");
|
|
53098
54080
|
}
|
|
53099
54081
|
function parseIpv4(value) {
|
|
53100
54082
|
const parts = value.split(".");
|
|
@@ -53410,7 +54392,7 @@ function saveServeConfig(config, dir, onWarn) {
|
|
|
53410
54392
|
|
|
53411
54393
|
// src/lib/serve-credential.ts
|
|
53412
54394
|
init_config_dir();
|
|
53413
|
-
import { createHash as
|
|
54395
|
+
import { createHash as createHash30, randomBytes as randomBytes2, randomUUID as randomUUID21 } from "crypto";
|
|
53414
54396
|
import {
|
|
53415
54397
|
chmodSync as chmodSync4,
|
|
53416
54398
|
closeSync as closeSync3,
|
|
@@ -53422,9 +54404,9 @@ import {
|
|
|
53422
54404
|
unlinkSync as unlinkSync3,
|
|
53423
54405
|
writeFileSync as writeFileSync8
|
|
53424
54406
|
} from "fs";
|
|
53425
|
-
import
|
|
54407
|
+
import path142 from "path";
|
|
53426
54408
|
function serveCredentialPath(dir) {
|
|
53427
|
-
return
|
|
54409
|
+
return path142.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
53428
54410
|
}
|
|
53429
54411
|
function constantTimeEqual(a, b) {
|
|
53430
54412
|
const width = Math.max(a.length, b.length);
|
|
@@ -53437,10 +54419,10 @@ function constantTimeEqual(a, b) {
|
|
|
53437
54419
|
return difference === 0;
|
|
53438
54420
|
}
|
|
53439
54421
|
function hashToken(salt, token) {
|
|
53440
|
-
return
|
|
54422
|
+
return createHash30("sha256").update(Buffer.from(salt, "hex")).update(Buffer.from(token, "utf8")).digest("hex");
|
|
53441
54423
|
}
|
|
53442
54424
|
function credentialFingerprint(record) {
|
|
53443
|
-
return
|
|
54425
|
+
return createHash30("sha256").update(`keryx-serve-fingerprint:${record.hash}`).digest("hex").slice(0, 8);
|
|
53444
54426
|
}
|
|
53445
54427
|
function isValidRecord(value) {
|
|
53446
54428
|
if (typeof value !== "object" || value === null) {
|
|
@@ -53494,7 +54476,7 @@ function readServeCredential(dir) {
|
|
|
53494
54476
|
}
|
|
53495
54477
|
function writeStore(store, dir) {
|
|
53496
54478
|
const file = serveCredentialPath(dir);
|
|
53497
|
-
const temp = `${file}.${
|
|
54479
|
+
const temp = `${file}.${randomUUID21()}.tmp`;
|
|
53498
54480
|
try {
|
|
53499
54481
|
ensureKeryxConfigDir(dir);
|
|
53500
54482
|
const handle = openSync3(temp, "wx", 384);
|
|
@@ -53534,7 +54516,7 @@ function mintRecord(now) {
|
|
|
53534
54516
|
const salt = randomBytes2(32).toString("hex");
|
|
53535
54517
|
return {
|
|
53536
54518
|
token,
|
|
53537
|
-
record: { id:
|
|
54519
|
+
record: { id: randomUUID21(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
53538
54520
|
};
|
|
53539
54521
|
}
|
|
53540
54522
|
function issueServeToken(dir, now = () => new Date().toISOString(), onWaiting) {
|
|
@@ -53653,24 +54635,24 @@ class AuthFailureThrottle {
|
|
|
53653
54635
|
|
|
53654
54636
|
// src/lib/serve-turn-store.ts
|
|
53655
54637
|
init_config_dir();
|
|
53656
|
-
import { createHash as
|
|
54638
|
+
import { createHash as createHash31 } from "crypto";
|
|
53657
54639
|
import { existsSync as existsSync28, readdirSync as readdirSync2, rmSync as rmSync2 } from "fs";
|
|
53658
|
-
import
|
|
54640
|
+
import path143 from "path";
|
|
53659
54641
|
var MAX_TURN_EVENTS = 1e4;
|
|
53660
54642
|
function turnsRoot(dir) {
|
|
53661
|
-
return
|
|
54643
|
+
return path143.join(keryxConfigDir(dir), "turns");
|
|
53662
54644
|
}
|
|
53663
54645
|
function turnDir(turnId, dir) {
|
|
53664
|
-
return
|
|
54646
|
+
return path143.join(turnsRoot(dir), turnId);
|
|
53665
54647
|
}
|
|
53666
54648
|
function keyPath(project, idempotencyKey, dir) {
|
|
53667
54649
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
53668
|
-
const digest2 =
|
|
53669
|
-
return
|
|
54650
|
+
const digest2 = createHash31("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
54651
|
+
return path143.join(turnsRoot(dir), "keys", `${digest2}.json`);
|
|
53670
54652
|
}
|
|
53671
54653
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
53672
|
-
const digest2 =
|
|
53673
|
-
return
|
|
54654
|
+
const digest2 = createHash31("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
54655
|
+
return path143.join(turnsRoot(dir), "keys", `${digest2}.json`);
|
|
53674
54656
|
}
|
|
53675
54657
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
53676
54658
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -53734,7 +54716,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
53734
54716
|
}
|
|
53735
54717
|
function createTurnRecord(record, dir) {
|
|
53736
54718
|
ensureTurnDir(record.turnId, dir);
|
|
53737
|
-
writeOwnerOnlyFile(
|
|
54719
|
+
writeOwnerOnlyFile(path143.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
53738
54720
|
`);
|
|
53739
54721
|
}
|
|
53740
54722
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -53743,12 +54725,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
53743
54725
|
}
|
|
53744
54726
|
const line = JSON.stringify(event);
|
|
53745
54727
|
try {
|
|
53746
|
-
appendOwnerOnlyLine(
|
|
54728
|
+
appendOwnerOnlyLine(path143.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
53747
54729
|
} catch (error2) {
|
|
53748
54730
|
if (error2?.code !== "ENOENT") {
|
|
53749
54731
|
throw error2;
|
|
53750
54732
|
}
|
|
53751
|
-
appendOwnerOnlyLine(
|
|
54733
|
+
appendOwnerOnlyLine(path143.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
53752
54734
|
}
|
|
53753
54735
|
return true;
|
|
53754
54736
|
}
|
|
@@ -53756,7 +54738,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
53756
54738
|
if (!isTurnId(turnId)) {
|
|
53757
54739
|
return { ok: false, reason: "not-a-turn-id" };
|
|
53758
54740
|
}
|
|
53759
|
-
const read = readTurnFile(
|
|
54741
|
+
const read = readTurnFile(path143.join(turnDir(turnId, dir), "events.jsonl"));
|
|
53760
54742
|
if (!read.ok) {
|
|
53761
54743
|
if (isDefiniteAbsence2(read.reason)) {
|
|
53762
54744
|
return { ok: true, value: [] };
|
|
@@ -53784,7 +54766,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
53784
54766
|
if (!isTurnId(turnId)) {
|
|
53785
54767
|
return { ok: false, reason: "not-a-turn-id" };
|
|
53786
54768
|
}
|
|
53787
|
-
const read = readTurnFile(
|
|
54769
|
+
const read = readTurnFile(path143.join(turnDir(turnId, dir), "turn.json"));
|
|
53788
54770
|
if (!read.ok) {
|
|
53789
54771
|
return { ok: false, reason: read.reason };
|
|
53790
54772
|
}
|
|
@@ -53803,7 +54785,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
53803
54785
|
if (!record.ok) {
|
|
53804
54786
|
return false;
|
|
53805
54787
|
}
|
|
53806
|
-
writeOwnerOnlyFile(
|
|
54788
|
+
writeOwnerOnlyFile(path143.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
53807
54789
|
`);
|
|
53808
54790
|
return true;
|
|
53809
54791
|
}
|
|
@@ -53848,8 +54830,8 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
53848
54830
|
}
|
|
53849
54831
|
|
|
53850
54832
|
// src/lib/serve-turn.ts
|
|
53851
|
-
import { randomUUID as
|
|
53852
|
-
import
|
|
54833
|
+
import { randomUUID as randomUUID22 } from "crypto";
|
|
54834
|
+
import path144 from "path";
|
|
53853
54835
|
init_service();
|
|
53854
54836
|
var REMOTE_ORIGIN = "remote:http";
|
|
53855
54837
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -53918,9 +54900,9 @@ function isUuid(value) {
|
|
|
53918
54900
|
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value);
|
|
53919
54901
|
}
|
|
53920
54902
|
function resolveProject(declared, dir) {
|
|
53921
|
-
const wanted =
|
|
54903
|
+
const wanted = path144.resolve(declared);
|
|
53922
54904
|
for (const entry of listProjects(dir, () => {})) {
|
|
53923
|
-
if (
|
|
54905
|
+
if (path144.resolve(entry.path) === wanted) {
|
|
53924
54906
|
return { ok: true, project: entry.path };
|
|
53925
54907
|
}
|
|
53926
54908
|
}
|
|
@@ -53967,7 +54949,7 @@ async function redactOut(security, text) {
|
|
|
53967
54949
|
async function runRemoteTurn(input2) {
|
|
53968
54950
|
const scanRoot = input2.scanRoot;
|
|
53969
54951
|
const security = createSecurityService(scanRoot);
|
|
53970
|
-
const newId = input2.newId ?? (() =>
|
|
54952
|
+
const newId = input2.newId ?? (() => randomUUID22());
|
|
53971
54953
|
const clock = input2.clock ?? (() => new Date().toISOString());
|
|
53972
54954
|
const turnId = input2.turnId ?? newId();
|
|
53973
54955
|
const sessionId = input2.request.sessionId ?? newId();
|
|
@@ -54110,7 +55092,7 @@ function outcomeOf(status, gate, unresolvedBlockerIds) {
|
|
|
54110
55092
|
}
|
|
54111
55093
|
function createSubmitTurn(deps) {
|
|
54112
55094
|
return async (request, project) => {
|
|
54113
|
-
const turnId = (deps.newId ?? (() =>
|
|
55095
|
+
const turnId = (deps.newId ?? (() => randomUUID22()))();
|
|
54114
55096
|
const scanned = await scanPrompt(deps.dir, request.prompt);
|
|
54115
55097
|
if (scanned.rejected) {
|
|
54116
55098
|
return { kind: "rejected" };
|
|
@@ -54797,7 +55779,7 @@ function runConfig(args2) {
|
|
|
54797
55779
|
return;
|
|
54798
55780
|
}
|
|
54799
55781
|
const credential2 = readServeCredential();
|
|
54800
|
-
const credentialId = credential2.status === "ok" ? credential2.record.id :
|
|
55782
|
+
const credentialId = credential2.status === "ok" ? credential2.record.id : randomUUID23();
|
|
54801
55783
|
const config = defaultServeConfig(credentialId, {
|
|
54802
55784
|
address: parsed.parsed.values.get("--bind") ?? DEFAULT_SERVE_BIND_ADDRESS,
|
|
54803
55785
|
port: port ?? DEFAULT_SERVE_PORT,
|
|
@@ -54953,9 +55935,9 @@ function printHelp17() {
|
|
|
54953
55935
|
|
|
54954
55936
|
// src/commands/update.ts
|
|
54955
55937
|
import { spawn as spawn5 } from "child_process";
|
|
54956
|
-
import { chmod as chmod4, mkdir as
|
|
55938
|
+
import { chmod as chmod4, mkdir as mkdir54, readFile as readFile76, readdir as readdir23, writeFile as writeFile47 } from "fs/promises";
|
|
54957
55939
|
import { access as access4, constants as constants2, existsSync as existsSync29 } from "fs";
|
|
54958
|
-
import
|
|
55940
|
+
import path145 from "path";
|
|
54959
55941
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
54960
55942
|
init_config();
|
|
54961
55943
|
init_config2();
|
|
@@ -54970,8 +55952,8 @@ async function updateCommand(args2 = []) {
|
|
|
54970
55952
|
return;
|
|
54971
55953
|
}
|
|
54972
55954
|
const projectRoot = process.cwd();
|
|
54973
|
-
const metaprojectRoot =
|
|
54974
|
-
banner("keryx update", `Refreshing the .metaproject workspace in ${
|
|
55955
|
+
const metaprojectRoot = path145.join(projectRoot, ".metaproject");
|
|
55956
|
+
banner("keryx update", `Refreshing the .metaproject workspace in ${path145.basename(projectRoot)}/`);
|
|
54975
55957
|
if (!await pathExists(metaprojectRoot)) {
|
|
54976
55958
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
54977
55959
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -55017,12 +55999,12 @@ async function updateCommand(args2 = []) {
|
|
|
55017
55999
|
nextSteps(steps);
|
|
55018
56000
|
}
|
|
55019
56001
|
async function refreshServiceFiles(projectRoot, options) {
|
|
55020
|
-
const metaprojectRoot =
|
|
56002
|
+
const metaprojectRoot = path145.join(projectRoot, ".metaproject");
|
|
55021
56003
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
55022
56004
|
const manifest = manifestState.manifest;
|
|
55023
56005
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
55024
56006
|
if (manifestState.migrated) {
|
|
55025
|
-
await writeFile47(
|
|
56007
|
+
await writeFile47(path145.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
55026
56008
|
`, "utf8");
|
|
55027
56009
|
}
|
|
55028
56010
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -55059,11 +56041,11 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
55059
56041
|
enableSecurity,
|
|
55060
56042
|
enableSac
|
|
55061
56043
|
});
|
|
55062
|
-
await writeTextIfChanged4(
|
|
55063
|
-
await writeTextIfChanged4(
|
|
55064
|
-
await writeTextIfChanged4(
|
|
55065
|
-
await writeTextIfChanged4(
|
|
55066
|
-
await writeTextIfChanged4(
|
|
56044
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
|
|
56045
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
|
|
56046
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "rules", "README.md"), renderProjectRulesReadme());
|
|
56047
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
|
|
56048
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "index.md"), renderIndexMarkdown({
|
|
55067
56049
|
enableGdgraph,
|
|
55068
56050
|
enableGdctx,
|
|
55069
56051
|
enableGdwiki,
|
|
@@ -55076,7 +56058,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
55076
56058
|
ruleSources,
|
|
55077
56059
|
hasDistilledEntrypoints: await hasDistilledEntrypoints(metaprojectRoot)
|
|
55078
56060
|
}));
|
|
55079
|
-
await writeTextIfChanged4(
|
|
56061
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
55080
56062
|
enableGdgraph,
|
|
55081
56063
|
enableGdctx,
|
|
55082
56064
|
enableGdwiki,
|
|
@@ -55088,7 +56070,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
55088
56070
|
enableSecurity,
|
|
55089
56071
|
data: dashboardData
|
|
55090
56072
|
}));
|
|
55091
|
-
await writeTextIfMissing4(
|
|
56073
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
55092
56074
|
enableGdgraph,
|
|
55093
56075
|
enableGdctx,
|
|
55094
56076
|
enableGdwiki,
|
|
@@ -55101,31 +56083,31 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
55101
56083
|
}));
|
|
55102
56084
|
if (enableGdgraph) {
|
|
55103
56085
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
55104
|
-
await writeTextIfChanged4(
|
|
55105
|
-
await writeTextIfChanged4(
|
|
55106
|
-
await writeTextIfChanged4(
|
|
56086
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
|
|
56087
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
|
|
56088
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
|
|
55107
56089
|
await seedAssetsLock(metaprojectRoot);
|
|
55108
56090
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
55109
56091
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
55110
56092
|
}
|
|
55111
56093
|
}
|
|
55112
56094
|
if (enableGdctx) {
|
|
55113
|
-
await writeTextIfMissing4(
|
|
55114
|
-
await writeTextIfChanged4(
|
|
55115
|
-
await writeTextIfChanged4(
|
|
55116
|
-
await writeTextIfChanged4(
|
|
56095
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
|
|
56096
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
|
|
56097
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
|
|
56098
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
|
|
55117
56099
|
}
|
|
55118
56100
|
if (enableGdwiki) {
|
|
55119
|
-
await writeTextIfMissing4(
|
|
55120
|
-
await writeTextIfChanged4(
|
|
55121
|
-
await writeTextIfChanged4(
|
|
56101
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
|
|
56102
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
|
|
56103
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
|
|
55122
56104
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
55123
56105
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
55124
56106
|
}
|
|
55125
56107
|
}
|
|
55126
56108
|
if (enableSac) {
|
|
55127
|
-
await writeTextIfMissing4(
|
|
55128
|
-
await writeTextIfChanged4(
|
|
56109
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
56110
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
55129
56111
|
}
|
|
55130
56112
|
if (enableGdskills) {
|
|
55131
56113
|
await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
|
|
@@ -55134,25 +56116,25 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
55134
56116
|
}
|
|
55135
56117
|
}
|
|
55136
56118
|
if (enableHealth) {
|
|
55137
|
-
await writeTextIfMissing4(
|
|
55138
|
-
await writeTextIfChanged4(
|
|
55139
|
-
await writeTextIfChanged4(
|
|
55140
|
-
await writeTextIfChanged4(
|
|
56119
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
|
|
56120
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
|
|
56121
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
|
|
56122
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
|
|
55141
56123
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
55142
56124
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
55143
56125
|
}
|
|
55144
56126
|
}
|
|
55145
56127
|
if (enableTesting) {
|
|
55146
|
-
await writeTextIfMissing4(
|
|
56128
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
55147
56129
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
55148
56130
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
55149
56131
|
}));
|
|
55150
|
-
await writeTextIfChanged4(
|
|
55151
|
-
await writeTextIfChanged4(
|
|
55152
|
-
await writeTextIfChanged4(
|
|
56132
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
|
|
56133
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
|
|
56134
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
|
|
55153
56135
|
if (enableGdwiki) {
|
|
55154
|
-
await writeTextIfMissing4(
|
|
55155
|
-
await writeTextIfMissing4(
|
|
56136
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
56137
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
55156
56138
|
}
|
|
55157
56139
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
55158
56140
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -55165,24 +56147,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
55165
56147
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
55166
56148
|
}
|
|
55167
56149
|
if (enableMemory) {
|
|
55168
|
-
await writeTextIfMissing4(
|
|
55169
|
-
await writeTextIfMissing4(
|
|
55170
|
-
await writeTextIfChanged4(
|
|
55171
|
-
await writeTextIfChanged4(
|
|
55172
|
-
await writeTextIfChanged4(
|
|
56150
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
|
|
56151
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
|
|
56152
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
|
|
56153
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
|
|
56154
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
|
|
55173
56155
|
}
|
|
55174
56156
|
if (enableTasks) {
|
|
55175
|
-
await writeTextIfChanged4(
|
|
55176
|
-
await writeTextIfChanged4(
|
|
55177
|
-
await writeTextIfChanged4(
|
|
55178
|
-
await writeTextIfChanged4(
|
|
55179
|
-
await writeTextIfChanged4(
|
|
55180
|
-
await writeTextIfChanged4(
|
|
56157
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
|
|
56158
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
|
|
56159
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
|
|
56160
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
|
|
56161
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
|
|
56162
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
|
|
55181
56163
|
}
|
|
55182
56164
|
if (enableSecurity) {
|
|
55183
|
-
await writeTextIfMissing4(
|
|
55184
|
-
await writeTextIfChanged4(
|
|
55185
|
-
await writeTextIfChanged4(
|
|
56165
|
+
await writeTextIfMissing4(path145.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
|
|
56166
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
|
|
56167
|
+
await writeTextIfChanged4(path145.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
|
|
55186
56168
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
55187
56169
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
55188
56170
|
}
|
|
@@ -55233,13 +56215,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
55233
56215
|
};
|
|
55234
56216
|
}
|
|
55235
56217
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
55236
|
-
const metaprojectRoot =
|
|
56218
|
+
const metaprojectRoot = path145.join(projectRoot, ".metaproject");
|
|
55237
56219
|
if (!await pathExists(metaprojectRoot)) {
|
|
55238
56220
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
55239
56221
|
}
|
|
55240
56222
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
55241
56223
|
const data = await collectDashboardData(metaprojectRoot);
|
|
55242
|
-
const dashboardPath =
|
|
56224
|
+
const dashboardPath = path145.join(metaprojectRoot, "keryx-dashboard.html");
|
|
55243
56225
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
55244
56226
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
55245
56227
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -55259,7 +56241,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
55259
56241
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
55260
56242
|
return true;
|
|
55261
56243
|
}
|
|
55262
|
-
const hookPath =
|
|
56244
|
+
const hookPath = path145.join(projectRoot, ".git", "hooks", "post-commit");
|
|
55263
56245
|
if (!await pathExists(hookPath)) {
|
|
55264
56246
|
return false;
|
|
55265
56247
|
}
|
|
@@ -55279,11 +56261,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
55279
56261
|
if (testing) {
|
|
55280
56262
|
data.testing = testing;
|
|
55281
56263
|
}
|
|
55282
|
-
const wiki = await collectMarkdownPages(
|
|
56264
|
+
const wiki = await collectMarkdownPages(path145.join(metaprojectRoot, "wiki"), "wiki");
|
|
55283
56265
|
if (wiki.length > 0) {
|
|
55284
56266
|
data.wiki = { pages: wiki };
|
|
55285
56267
|
}
|
|
55286
|
-
const memory = await collectMarkdownPages(
|
|
56268
|
+
const memory = await collectMarkdownPages(path145.join(metaprojectRoot, "memory"), "memory");
|
|
55287
56269
|
if (memory.length > 0) {
|
|
55288
56270
|
data.memory = { entries: memory };
|
|
55289
56271
|
}
|
|
@@ -55298,19 +56280,19 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
55298
56280
|
return data;
|
|
55299
56281
|
}
|
|
55300
56282
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
55301
|
-
const flowsRoot2 =
|
|
56283
|
+
const flowsRoot2 = path145.join(metaprojectRoot, "flows");
|
|
55302
56284
|
if (!await pathExists(flowsRoot2)) {
|
|
55303
56285
|
return null;
|
|
55304
56286
|
}
|
|
55305
56287
|
let dirEntries;
|
|
55306
56288
|
try {
|
|
55307
|
-
dirEntries = (await
|
|
56289
|
+
dirEntries = (await readdir23(flowsRoot2, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
|
|
55308
56290
|
} catch {
|
|
55309
56291
|
return null;
|
|
55310
56292
|
}
|
|
55311
56293
|
const flows = [];
|
|
55312
56294
|
for (const dir of dirEntries) {
|
|
55313
|
-
const flowPath =
|
|
56295
|
+
const flowPath = path145.join(flowsRoot2, dir, "flow.json");
|
|
55314
56296
|
if (!await pathExists(flowPath)) {
|
|
55315
56297
|
continue;
|
|
55316
56298
|
}
|
|
@@ -55318,7 +56300,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
55318
56300
|
const flow = JSON.parse(await readFile76(flowPath, "utf8"));
|
|
55319
56301
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
55320
56302
|
let acTotal = 0;
|
|
55321
|
-
const acPath2 =
|
|
56303
|
+
const acPath2 = path145.join(flowsRoot2, dir, "acceptance-criteria.md");
|
|
55322
56304
|
if (await pathExists(acPath2)) {
|
|
55323
56305
|
const acContent = await readFile76(acPath2, "utf8");
|
|
55324
56306
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -55372,7 +56354,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
55372
56354
|
"data/testing/context.md"
|
|
55373
56355
|
];
|
|
55374
56356
|
for (const href of staticHrefs) {
|
|
55375
|
-
const filePath =
|
|
56357
|
+
const filePath = path145.join(metaprojectRoot, ...href.split("/"));
|
|
55376
56358
|
if (!await pathExists(filePath)) {
|
|
55377
56359
|
continue;
|
|
55378
56360
|
}
|
|
@@ -55389,7 +56371,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
55389
56371
|
return docs;
|
|
55390
56372
|
}
|
|
55391
56373
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
55392
|
-
const reportPath2 =
|
|
56374
|
+
const reportPath2 = path145.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
55393
56375
|
if (!await pathExists(reportPath2)) {
|
|
55394
56376
|
return;
|
|
55395
56377
|
}
|
|
@@ -55498,8 +56480,8 @@ function metricToScope(metric) {
|
|
|
55498
56480
|
};
|
|
55499
56481
|
}
|
|
55500
56482
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
55501
|
-
const nodesPath =
|
|
55502
|
-
const edgesPath =
|
|
56483
|
+
const nodesPath = path145.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
56484
|
+
const edgesPath = path145.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
55503
56485
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
55504
56486
|
return;
|
|
55505
56487
|
}
|
|
@@ -55550,8 +56532,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
55550
56532
|
};
|
|
55551
56533
|
}
|
|
55552
56534
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
55553
|
-
const reportPath2 =
|
|
55554
|
-
const contextPath =
|
|
56535
|
+
const reportPath2 = path145.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
56536
|
+
const contextPath = path145.join(metaprojectRoot, "data", "testing", "context.md");
|
|
55555
56537
|
if (await pathExists(reportPath2)) {
|
|
55556
56538
|
const report = JSON.parse(await readFile76(reportPath2, "utf8"));
|
|
55557
56539
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -55580,7 +56562,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
55580
56562
|
const files = await listMarkdownFiles(root);
|
|
55581
56563
|
const pages = [];
|
|
55582
56564
|
for (const filePath of files.slice(0, 40)) {
|
|
55583
|
-
const relativePath =
|
|
56565
|
+
const relativePath = path145.relative(root, filePath).split(path145.sep).join("/");
|
|
55584
56566
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
55585
56567
|
continue;
|
|
55586
56568
|
}
|
|
@@ -55598,10 +56580,10 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
55598
56580
|
return pages;
|
|
55599
56581
|
}
|
|
55600
56582
|
async function listMarkdownFiles(root) {
|
|
55601
|
-
const entries = await
|
|
56583
|
+
const entries = await readdir23(root, { withFileTypes: true });
|
|
55602
56584
|
const files = [];
|
|
55603
56585
|
for (const entry of entries) {
|
|
55604
|
-
const fullPath =
|
|
56586
|
+
const fullPath = path145.join(root, entry.name);
|
|
55605
56587
|
if (entry.isDirectory()) {
|
|
55606
56588
|
files.push(...await listMarkdownFiles(fullPath));
|
|
55607
56589
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -55649,7 +56631,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
55649
56631
|
const manifest = {
|
|
55650
56632
|
schemaVersion: 1,
|
|
55651
56633
|
standardVersion: STANDARD_VERSION,
|
|
55652
|
-
name: `${
|
|
56634
|
+
name: `${path145.basename(path145.dirname(metaprojectRoot))}-metaproject`,
|
|
55653
56635
|
createdBy: "keryx",
|
|
55654
56636
|
profiles: computeProfiles(enabledModuleKeys2),
|
|
55655
56637
|
paths: {
|
|
@@ -55750,11 +56732,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
55750
56732
|
metaproject: ".metaproject/index.md"
|
|
55751
56733
|
}
|
|
55752
56734
|
};
|
|
55753
|
-
await writeFile47(
|
|
56735
|
+
await writeFile47(path145.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
55754
56736
|
`, "utf8");
|
|
55755
56737
|
}
|
|
55756
56738
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
55757
|
-
const manifestPath =
|
|
56739
|
+
const manifestPath = path145.join(metaprojectRoot, "metaproject.json");
|
|
55758
56740
|
if (!await pathExists(manifestPath)) {
|
|
55759
56741
|
return;
|
|
55760
56742
|
}
|
|
@@ -55777,7 +56759,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
55777
56759
|
`, "utf8");
|
|
55778
56760
|
}
|
|
55779
56761
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
55780
|
-
const manifestPath =
|
|
56762
|
+
const manifestPath = path145.join(metaprojectRoot, "metaproject.json");
|
|
55781
56763
|
if (!await pathExists(manifestPath)) {
|
|
55782
56764
|
return;
|
|
55783
56765
|
}
|
|
@@ -55813,80 +56795,80 @@ async function updateRuntime(projectRoot) {
|
|
|
55813
56795
|
}
|
|
55814
56796
|
}
|
|
55815
56797
|
async function findRuntimeRoot(projectRoot) {
|
|
55816
|
-
const projectRuntime =
|
|
55817
|
-
if (await pathExists(
|
|
56798
|
+
const projectRuntime = path145.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
56799
|
+
if (await pathExists(path145.join(projectRuntime, ".git"))) {
|
|
55818
56800
|
return projectRuntime;
|
|
55819
56801
|
}
|
|
55820
56802
|
const home = process.env.HOME;
|
|
55821
56803
|
if (!home) {
|
|
55822
56804
|
return null;
|
|
55823
56805
|
}
|
|
55824
|
-
const globalRuntime =
|
|
55825
|
-
if (await pathExists(
|
|
56806
|
+
const globalRuntime = path145.join(home, ".keryx", "keryx");
|
|
56807
|
+
if (await pathExists(path145.join(globalRuntime, ".git"))) {
|
|
55826
56808
|
return globalRuntime;
|
|
55827
56809
|
}
|
|
55828
56810
|
return null;
|
|
55829
56811
|
}
|
|
55830
56812
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
55831
56813
|
const dirs = [
|
|
55832
|
-
|
|
55833
|
-
|
|
55834
|
-
|
|
55835
|
-
|
|
55836
|
-
|
|
56814
|
+
path145.join(metaprojectRoot, "core"),
|
|
56815
|
+
path145.join(metaprojectRoot, "hooks", "post-update.d"),
|
|
56816
|
+
path145.join(metaprojectRoot, "modules"),
|
|
56817
|
+
path145.join(metaprojectRoot, "rules"),
|
|
56818
|
+
path145.join(metaprojectRoot, "skills", "project-rules"),
|
|
55837
56819
|
...modules.enableGdgraph ? [
|
|
55838
|
-
|
|
55839
|
-
|
|
56820
|
+
path145.join(metaprojectRoot, "core", "gdgraph"),
|
|
56821
|
+
path145.join(metaprojectRoot, "skills", "gdgraph")
|
|
55840
56822
|
] : [],
|
|
55841
56823
|
...modules.enableGdctx ? [
|
|
55842
|
-
|
|
55843
|
-
|
|
56824
|
+
path145.join(metaprojectRoot, "core", "gdctx"),
|
|
56825
|
+
path145.join(metaprojectRoot, "skills", "gdctx")
|
|
55844
56826
|
] : [],
|
|
55845
56827
|
...modules.enableGdwiki ? [
|
|
55846
|
-
|
|
55847
|
-
|
|
56828
|
+
path145.join(metaprojectRoot, "skills", "gdwiki"),
|
|
56829
|
+
path145.join(metaprojectRoot, "wiki", "templates")
|
|
55848
56830
|
] : [],
|
|
55849
56831
|
...modules.enableHealth ? [
|
|
55850
|
-
|
|
55851
|
-
|
|
56832
|
+
path145.join(metaprojectRoot, "core", "health"),
|
|
56833
|
+
path145.join(metaprojectRoot, "skills", "health")
|
|
55852
56834
|
] : [],
|
|
55853
56835
|
...modules.enableTesting ? [
|
|
55854
|
-
|
|
55855
|
-
|
|
56836
|
+
path145.join(metaprojectRoot, "core", "testing"),
|
|
56837
|
+
path145.join(metaprojectRoot, "skills", "testing")
|
|
55856
56838
|
] : [],
|
|
55857
56839
|
...modules.enableMemory ? [
|
|
55858
|
-
|
|
55859
|
-
|
|
55860
|
-
|
|
56840
|
+
path145.join(metaprojectRoot, "core", "memory"),
|
|
56841
|
+
path145.join(metaprojectRoot, "skills", "memory"),
|
|
56842
|
+
path145.join(metaprojectRoot, "memory", "templates")
|
|
55861
56843
|
] : [],
|
|
55862
56844
|
...modules.enableTasks ? [
|
|
55863
|
-
|
|
55864
|
-
|
|
56845
|
+
path145.join(metaprojectRoot, "flows"),
|
|
56846
|
+
path145.join(metaprojectRoot, "skills", "flow")
|
|
55865
56847
|
] : [],
|
|
55866
56848
|
...modules.enableSecurity ? [
|
|
55867
|
-
|
|
56849
|
+
path145.join(metaprojectRoot, "core", "security")
|
|
55868
56850
|
] : [],
|
|
55869
56851
|
...modules.enableSac ? [
|
|
55870
|
-
|
|
56852
|
+
path145.join(metaprojectRoot, "skills", "sac")
|
|
55871
56853
|
] : []
|
|
55872
56854
|
];
|
|
55873
|
-
await Promise.all(dirs.map((dir) =>
|
|
56855
|
+
await Promise.all(dirs.map((dir) => mkdir54(dir, { recursive: true })));
|
|
55874
56856
|
}
|
|
55875
56857
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
55876
|
-
const gdgraphCoreRoot =
|
|
55877
|
-
await
|
|
56858
|
+
const gdgraphCoreRoot = path145.join(metaprojectRoot, "core", "gdgraph");
|
|
56859
|
+
await mkdir54(gdgraphCoreRoot, { recursive: true });
|
|
55878
56860
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
55879
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
56861
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path145.join(gdgraphCoreRoot, file));
|
|
55880
56862
|
}
|
|
55881
|
-
await writeTextIfChanged4(
|
|
56863
|
+
await writeTextIfChanged4(path145.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
55882
56864
|
}
|
|
55883
56865
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
55884
56866
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
55885
56867
|
if (!hooksRoot) {
|
|
55886
56868
|
return;
|
|
55887
56869
|
}
|
|
55888
|
-
await
|
|
55889
|
-
const hookPath =
|
|
56870
|
+
await mkdir54(hooksRoot, { recursive: true });
|
|
56871
|
+
const hookPath = path145.join(hooksRoot, hookName);
|
|
55890
56872
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
55891
56873
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
55892
56874
|
const managedBlock = `${blockStart}
|
|
@@ -55907,7 +56889,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
55907
56889
|
if (!hooksRoot) {
|
|
55908
56890
|
return;
|
|
55909
56891
|
}
|
|
55910
|
-
const hookPath =
|
|
56892
|
+
const hookPath = path145.join(hooksRoot, hookName);
|
|
55911
56893
|
if (!await pathExists(hookPath)) {
|
|
55912
56894
|
return;
|
|
55913
56895
|
}
|
|
@@ -55929,7 +56911,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
55929
56911
|
if (!hooksRoot) {
|
|
55930
56912
|
return false;
|
|
55931
56913
|
}
|
|
55932
|
-
const hookPath =
|
|
56914
|
+
const hookPath = path145.join(hooksRoot, "pre-push");
|
|
55933
56915
|
if (!await pathExists(hookPath)) {
|
|
55934
56916
|
return false;
|
|
55935
56917
|
}
|
|
@@ -55944,7 +56926,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
55944
56926
|
return (await readFile76(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
55945
56927
|
}
|
|
55946
56928
|
async function readManifest5(metaprojectRoot) {
|
|
55947
|
-
const manifestPath =
|
|
56929
|
+
const manifestPath = path145.join(metaprojectRoot, "metaproject.json");
|
|
55948
56930
|
if (!await pathExists(manifestPath)) {
|
|
55949
56931
|
return {
|
|
55950
56932
|
exists: false,
|
|
@@ -56013,7 +56995,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
56013
56995
|
}
|
|
56014
56996
|
async function anyPathExists(root, candidates) {
|
|
56015
56997
|
for (const candidate of candidates) {
|
|
56016
|
-
if (await pathExists(
|
|
56998
|
+
if (await pathExists(path145.join(root, candidate))) {
|
|
56017
56999
|
return true;
|
|
56018
57000
|
}
|
|
56019
57001
|
}
|
|
@@ -56034,13 +57016,13 @@ function parseUpdateArgs(args2) {
|
|
|
56034
57016
|
};
|
|
56035
57017
|
}
|
|
56036
57018
|
async function runPostUpdateHooks(projectRoot) {
|
|
56037
|
-
const hooksDir =
|
|
57019
|
+
const hooksDir = path145.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
56038
57020
|
if (!await pathExists(hooksDir)) {
|
|
56039
57021
|
return;
|
|
56040
57022
|
}
|
|
56041
|
-
const entries = (await
|
|
57023
|
+
const entries = (await readdir23(hooksDir)).sort();
|
|
56042
57024
|
for (const entry of entries) {
|
|
56043
|
-
const hookPath =
|
|
57025
|
+
const hookPath = path145.join(hooksDir, entry);
|
|
56044
57026
|
try {
|
|
56045
57027
|
await accessExecutable(hookPath);
|
|
56046
57028
|
} catch {
|
|
@@ -56081,14 +57063,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
56081
57063
|
if (await pathExists(filePath) && await readFile76(filePath, "utf8") === content) {
|
|
56082
57064
|
return;
|
|
56083
57065
|
}
|
|
56084
|
-
await
|
|
57066
|
+
await mkdir54(path145.dirname(filePath), { recursive: true });
|
|
56085
57067
|
await writeFile47(filePath, content, "utf8");
|
|
56086
57068
|
}
|
|
56087
57069
|
async function writeTextIfMissing4(filePath, content) {
|
|
56088
57070
|
if (await pathExists(filePath)) {
|
|
56089
57071
|
return;
|
|
56090
57072
|
}
|
|
56091
|
-
await
|
|
57073
|
+
await mkdir54(path145.dirname(filePath), { recursive: true });
|
|
56092
57074
|
await writeFile47(filePath, content, "utf8");
|
|
56093
57075
|
}
|
|
56094
57076
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -56096,7 +57078,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
56096
57078
|
if (await pathExists(to) && await readFile76(to, "utf8") === next) {
|
|
56097
57079
|
return;
|
|
56098
57080
|
}
|
|
56099
|
-
await
|
|
57081
|
+
await mkdir54(path145.dirname(to), { recursive: true });
|
|
56100
57082
|
await writeFile47(to, next, "utf8");
|
|
56101
57083
|
}
|
|
56102
57084
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -56105,7 +57087,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
56105
57087
|
return directPath;
|
|
56106
57088
|
}
|
|
56107
57089
|
if (relativePath.startsWith("../")) {
|
|
56108
|
-
const packagedSourcePath =
|
|
57090
|
+
const packagedSourcePath = path145.join(path145.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
56109
57091
|
if (existsSync29(packagedSourcePath)) {
|
|
56110
57092
|
return packagedSourcePath;
|
|
56111
57093
|
}
|
|
@@ -56137,7 +57119,7 @@ function printHelp18() {
|
|
|
56137
57119
|
|
|
56138
57120
|
// src/commands/dashboard.ts
|
|
56139
57121
|
import { spawn as spawn6 } from "child_process";
|
|
56140
|
-
import
|
|
57122
|
+
import path146 from "path";
|
|
56141
57123
|
init_args();
|
|
56142
57124
|
async function dashboardCommand(args2 = []) {
|
|
56143
57125
|
const options = parseOptions(args2);
|
|
@@ -56148,7 +57130,7 @@ async function dashboardCommand(args2 = []) {
|
|
|
56148
57130
|
}
|
|
56149
57131
|
if (subcommand === "build") {
|
|
56150
57132
|
const result = await buildDashboard();
|
|
56151
|
-
const rel =
|
|
57133
|
+
const rel = path146.relative(process.cwd(), result.path);
|
|
56152
57134
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
56153
57135
|
note(`Open it: keryx dashboard open`);
|
|
56154
57136
|
return;
|
|
@@ -56156,7 +57138,7 @@ async function dashboardCommand(args2 = []) {
|
|
|
56156
57138
|
if (subcommand === "open") {
|
|
56157
57139
|
const result = await buildDashboard();
|
|
56158
57140
|
await openFile(result.path);
|
|
56159
|
-
const rel =
|
|
57141
|
+
const rel = path146.relative(process.cwd(), result.path);
|
|
56160
57142
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
56161
57143
|
return;
|
|
56162
57144
|
}
|
|
@@ -56203,9 +57185,9 @@ function printHelp19() {
|
|
|
56203
57185
|
import { readFileSync as readFileSync10 } from "fs";
|
|
56204
57186
|
|
|
56205
57187
|
// src/agents/bootstrap.ts
|
|
56206
|
-
import { mkdir as
|
|
57188
|
+
import { mkdir as mkdir55, readFile as readFile77, writeFile as writeFile48 } from "fs/promises";
|
|
56207
57189
|
import { homedir as homedir7 } from "os";
|
|
56208
|
-
import
|
|
57190
|
+
import path147 from "path";
|
|
56209
57191
|
init_fs();
|
|
56210
57192
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
56211
57193
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -56215,35 +57197,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
56215
57197
|
aliases: ["claude-code"],
|
|
56216
57198
|
label: "Claude Code",
|
|
56217
57199
|
fileName: "CLAUDE.md",
|
|
56218
|
-
filePath: (homeRoot) =>
|
|
57200
|
+
filePath: (homeRoot) => path147.join(homeRoot, ".claude", "CLAUDE.md")
|
|
56219
57201
|
},
|
|
56220
57202
|
{
|
|
56221
57203
|
id: "opencode",
|
|
56222
57204
|
aliases: ["open-code"],
|
|
56223
57205
|
label: "OpenCode",
|
|
56224
57206
|
fileName: "AGENTS.md",
|
|
56225
|
-
filePath: (homeRoot) =>
|
|
57207
|
+
filePath: (homeRoot) => path147.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
56226
57208
|
},
|
|
56227
57209
|
{
|
|
56228
57210
|
id: "zcode",
|
|
56229
57211
|
aliases: ["zed", "zed-code"],
|
|
56230
57212
|
label: "ZCode",
|
|
56231
57213
|
fileName: "AGENTS.md",
|
|
56232
|
-
filePath: (homeRoot) =>
|
|
57214
|
+
filePath: (homeRoot) => path147.join(homeRoot, ".zcode", "AGENTS.md")
|
|
56233
57215
|
},
|
|
56234
57216
|
{
|
|
56235
57217
|
id: "codex",
|
|
56236
57218
|
aliases: [],
|
|
56237
57219
|
label: "Codex",
|
|
56238
57220
|
fileName: "AGENTS.md",
|
|
56239
|
-
filePath: (homeRoot) =>
|
|
57221
|
+
filePath: (homeRoot) => path147.join(homeRoot, ".codex", "AGENTS.md")
|
|
56240
57222
|
},
|
|
56241
57223
|
{
|
|
56242
57224
|
id: "antigravity",
|
|
56243
57225
|
aliases: ["antigravuty", "antigravity-code"],
|
|
56244
57226
|
label: "Antigravity",
|
|
56245
57227
|
fileName: "AGENTS.md",
|
|
56246
|
-
filePath: (homeRoot) =>
|
|
57228
|
+
filePath: (homeRoot) => path147.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
56247
57229
|
}
|
|
56248
57230
|
];
|
|
56249
57231
|
function agentBootstrapRuntimeIds() {
|
|
@@ -56291,7 +57273,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
56291
57273
|
const dryRun = options.dryRun === true;
|
|
56292
57274
|
const wrote = next !== current;
|
|
56293
57275
|
if (wrote && !dryRun) {
|
|
56294
|
-
await
|
|
57276
|
+
await mkdir55(path147.dirname(filePath), { recursive: true });
|
|
56295
57277
|
await writeFile48(filePath, next, "utf8");
|
|
56296
57278
|
}
|
|
56297
57279
|
const status = dryRun ? statusFromContent(runtime, filePath, exists2, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
@@ -56639,7 +57621,7 @@ function printBootstrapHelp() {
|
|
|
56639
57621
|
// src/commands/metrics.ts
|
|
56640
57622
|
init_args();
|
|
56641
57623
|
import { readFile as readFile78 } from "fs/promises";
|
|
56642
|
-
import
|
|
57624
|
+
import path149 from "path";
|
|
56643
57625
|
|
|
56644
57626
|
// src/metrics/benchmark.ts
|
|
56645
57627
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -57409,8 +58391,8 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
57409
58391
|
}
|
|
57410
58392
|
|
|
57411
58393
|
// src/metrics/oracle-runner.ts
|
|
57412
|
-
import { mkdir as
|
|
57413
|
-
import
|
|
58394
|
+
import { mkdir as mkdir56, writeFile as writeFile49 } from "fs/promises";
|
|
58395
|
+
import path148 from "path";
|
|
57414
58396
|
|
|
57415
58397
|
// src/metrics/ir.ts
|
|
57416
58398
|
function toIdSet(ids) {
|
|
@@ -57852,9 +58834,9 @@ function buildEvidenceBundle(input2, options = {}) {
|
|
|
57852
58834
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
57853
58835
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
57854
58836
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
57855
|
-
const dir =
|
|
57856
|
-
await
|
|
57857
|
-
const write = (name, value) => writeFile49(
|
|
58837
|
+
const dir = path148.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
58838
|
+
await mkdir56(dir, { recursive: true });
|
|
58839
|
+
const write = (name, value) => writeFile49(path148.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
57858
58840
|
`, "utf8");
|
|
57859
58841
|
await Promise.all([
|
|
57860
58842
|
write("inputs.json", bundle.inputs),
|
|
@@ -57894,7 +58876,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
57894
58876
|
console.log("# metrics status");
|
|
57895
58877
|
console.log("");
|
|
57896
58878
|
console.log(`root: ${root}`);
|
|
57897
|
-
console.log(`enabled: ${await Bun.file(
|
|
58879
|
+
console.log(`enabled: ${await Bun.file(path149.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
57898
58880
|
const latest2 = await readLatestPointer(root);
|
|
57899
58881
|
console.log(`latest: ${latest2.status}`);
|
|
57900
58882
|
return;
|
|
@@ -57906,7 +58888,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
57906
58888
|
process.exitCode = 1;
|
|
57907
58889
|
return;
|
|
57908
58890
|
}
|
|
57909
|
-
const record2 = JSON.parse(await readFile78(
|
|
58891
|
+
const record2 = JSON.parse(await readFile78(path149.resolve(projectRoot, file), "utf8"));
|
|
57910
58892
|
const result = validateRunRecord(record2);
|
|
57911
58893
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
57912
58894
|
for (const error2 of result.errors)
|
|
@@ -57931,7 +58913,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
57931
58913
|
process.exitCode = 1;
|
|
57932
58914
|
return;
|
|
57933
58915
|
}
|
|
57934
|
-
const file =
|
|
58916
|
+
const file = path149.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
57935
58917
|
if (!await Bun.file(file).exists()) {
|
|
57936
58918
|
console.error(`Run not found: ${runId}`);
|
|
57937
58919
|
process.exitCode = 1;
|
|
@@ -57948,8 +58930,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
57948
58930
|
process.exitCode = 1;
|
|
57949
58931
|
return;
|
|
57950
58932
|
}
|
|
57951
|
-
const a = JSON.parse(await readFile78(
|
|
57952
|
-
const b = JSON.parse(await readFile78(
|
|
58933
|
+
const a = JSON.parse(await readFile78(path149.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
58934
|
+
const b = JSON.parse(await readFile78(path149.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
57953
58935
|
const comparison = compareExecutionRuns(a, b);
|
|
57954
58936
|
console.log(stableJson(comparison));
|
|
57955
58937
|
return;
|
|
@@ -57983,8 +58965,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
57983
58965
|
return;
|
|
57984
58966
|
}
|
|
57985
58967
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
57986
|
-
await Bun.write(
|
|
57987
|
-
console.log(`manifest: ${
|
|
58968
|
+
await Bun.write(path149.resolve(projectRoot, out), stableJson(template));
|
|
58969
|
+
console.log(`manifest: ${path149.relative(projectRoot, path149.resolve(projectRoot, out))}`);
|
|
57988
58970
|
return;
|
|
57989
58971
|
}
|
|
57990
58972
|
if (subcommand === "benchmark" && args2[1] === "run") {
|
|
@@ -57998,7 +58980,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
57998
58980
|
process.exitCode = 1;
|
|
57999
58981
|
return;
|
|
58000
58982
|
}
|
|
58001
|
-
const raw = JSON.parse(await readFile78(
|
|
58983
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, file), "utf8"));
|
|
58002
58984
|
const input2 = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
58003
58985
|
const result = validatePairedBenchmark(input2);
|
|
58004
58986
|
console.log(stableJson(result));
|
|
@@ -58010,7 +58992,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
58010
58992
|
process.exitCode = 1;
|
|
58011
58993
|
}
|
|
58012
58994
|
async function loadAffectedSets(projectRoot, file) {
|
|
58013
|
-
const raw = JSON.parse(await readFile78(
|
|
58995
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, file), "utf8"));
|
|
58014
58996
|
const map = new Map;
|
|
58015
58997
|
for (const entry of raw.targets ?? []) {
|
|
58016
58998
|
if (typeof entry.target === "string")
|
|
@@ -58081,7 +59063,7 @@ async function runHarnessLayer(projectRoot, args2, ladder) {
|
|
|
58081
59063
|
let tasks;
|
|
58082
59064
|
let model;
|
|
58083
59065
|
try {
|
|
58084
|
-
const raw = JSON.parse(await readFile78(
|
|
59066
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, resultsPath), "utf8"));
|
|
58085
59067
|
tasks = raw.tasks ?? [];
|
|
58086
59068
|
model = raw.model;
|
|
58087
59069
|
} catch (error2) {
|
|
@@ -58112,7 +59094,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args2, ladder) {
|
|
|
58112
59094
|
let cases;
|
|
58113
59095
|
let model;
|
|
58114
59096
|
try {
|
|
58115
|
-
const raw = JSON.parse(await readFile78(
|
|
59097
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, resultsPath), "utf8"));
|
|
58116
59098
|
cases = raw.cases ?? [];
|
|
58117
59099
|
model = raw.model;
|
|
58118
59100
|
} catch (error2) {
|
|
@@ -58137,7 +59119,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args2, ladder) {
|
|
|
58137
59119
|
let cases;
|
|
58138
59120
|
let model;
|
|
58139
59121
|
try {
|
|
58140
|
-
const raw = JSON.parse(await readFile78(
|
|
59122
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, resultsPath), "utf8"));
|
|
58141
59123
|
cases = raw.cases ?? [];
|
|
58142
59124
|
model = raw.model;
|
|
58143
59125
|
} catch (error2) {
|
|
@@ -58162,7 +59144,7 @@ async function runSafetyContainmentLayer(projectRoot, args2, ladder, caseClass)
|
|
|
58162
59144
|
let cases;
|
|
58163
59145
|
let model;
|
|
58164
59146
|
try {
|
|
58165
|
-
const raw = JSON.parse(await readFile78(
|
|
59147
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, resultsPath), "utf8"));
|
|
58166
59148
|
cases = raw.cases ?? [];
|
|
58167
59149
|
model = raw.model;
|
|
58168
59150
|
} catch (error2) {
|
|
@@ -58224,12 +59206,12 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
|
|
|
58224
59206
|
}
|
|
58225
59207
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
58226
59208
|
if (outDir) {
|
|
58227
|
-
const resolvedOut =
|
|
59209
|
+
const resolvedOut = path149.resolve(projectRoot, outDir);
|
|
58228
59210
|
for (const input2 of inputs) {
|
|
58229
59211
|
for (const named of input2.golds) {
|
|
58230
59212
|
const bundle = buildEvidenceBundle({ target: input2.target, system: input2.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
58231
|
-
const dir = await persistEvidenceBundle(
|
|
58232
|
-
console.error(`bundle[${named.kind}]: ${
|
|
59213
|
+
const dir = await persistEvidenceBundle(path149.join(resolvedOut, named.kind), bundle, ladder);
|
|
59214
|
+
console.error(`bundle[${named.kind}]: ${path149.relative(projectRoot, dir)}`);
|
|
58233
59215
|
}
|
|
58234
59216
|
}
|
|
58235
59217
|
}
|
|
@@ -58253,7 +59235,7 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
|
|
|
58253
59235
|
return allValid;
|
|
58254
59236
|
}
|
|
58255
59237
|
async function loadCoverageMap2(projectRoot, file) {
|
|
58256
|
-
const raw = JSON.parse(await readFile78(
|
|
59238
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, file), "utf8"));
|
|
58257
59239
|
return raw.coverageMap ?? {};
|
|
58258
59240
|
}
|
|
58259
59241
|
async function runTestingLayer(projectRoot, args2, ladder) {
|
|
@@ -58290,7 +59272,7 @@ async function runTestingLayer(projectRoot, args2, ladder) {
|
|
|
58290
59272
|
return result.valid;
|
|
58291
59273
|
}
|
|
58292
59274
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
58293
|
-
const raw = JSON.parse(await readFile78(
|
|
59275
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, file), "utf8"));
|
|
58294
59276
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
58295
59277
|
}
|
|
58296
59278
|
async function runMemoryLayer(projectRoot, args2, ladder) {
|
|
@@ -58327,11 +59309,11 @@ async function runMemoryLayer(projectRoot, args2, ladder) {
|
|
|
58327
59309
|
return result.valid;
|
|
58328
59310
|
}
|
|
58329
59311
|
async function loadWikiGoldK(projectRoot, file) {
|
|
58330
|
-
const raw = JSON.parse(await readFile78(
|
|
59312
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, file), "utf8"));
|
|
58331
59313
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
58332
59314
|
}
|
|
58333
59315
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
58334
|
-
const raw = JSON.parse(await readFile78(
|
|
59316
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, file), "utf8"));
|
|
58335
59317
|
const map = new Map;
|
|
58336
59318
|
for (const entry of raw.targets ?? []) {
|
|
58337
59319
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -58387,7 +59369,7 @@ async function runWikiLayer(projectRoot, args2, ladder) {
|
|
|
58387
59369
|
return result.valid;
|
|
58388
59370
|
}
|
|
58389
59371
|
async function loadGdctxFacts(projectRoot, file) {
|
|
58390
|
-
const raw = JSON.parse(await readFile78(
|
|
59372
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, file), "utf8"));
|
|
58391
59373
|
const inputs = [];
|
|
58392
59374
|
for (const entry of raw.inputs ?? []) {
|
|
58393
59375
|
if (typeof entry.input === "string") {
|
|
@@ -58425,7 +59407,7 @@ async function collect(projectRoot, args2) {
|
|
|
58425
59407
|
process.exitCode = 1;
|
|
58426
59408
|
return;
|
|
58427
59409
|
}
|
|
58428
|
-
const raw = JSON.parse(await readFile78(
|
|
59410
|
+
const raw = JSON.parse(await readFile78(path149.resolve(projectRoot, eventFile), "utf8"));
|
|
58429
59411
|
const events2 = Array.isArray(raw) ? raw : raw.events;
|
|
58430
59412
|
const startedAt = optionValue(args2, "--started-at") ?? events2[0]?.timestamp_utc ?? new Date().toISOString();
|
|
58431
59413
|
const finishedAt = optionValue(args2, "--finished-at") ?? events2.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -58441,11 +59423,11 @@ async function collect(projectRoot, args2) {
|
|
|
58441
59423
|
parentRunId: optionValue(args2, "--parent-run-id") ?? null
|
|
58442
59424
|
});
|
|
58443
59425
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record2, { cwd: projectRoot });
|
|
58444
|
-
console.log(`json: ${
|
|
58445
|
-
console.log(`markdown: ${
|
|
59426
|
+
console.log(`json: ${path149.relative(projectRoot, result.jsonPath)}`);
|
|
59427
|
+
console.log(`markdown: ${path149.relative(projectRoot, result.markdownPath)}`);
|
|
58446
59428
|
}
|
|
58447
59429
|
function metricsRoot(projectRoot) {
|
|
58448
|
-
return
|
|
59430
|
+
return path149.join(projectRoot, ".metaproject", "data", "metrics");
|
|
58449
59431
|
}
|
|
58450
59432
|
function printMetricsHelp() {
|
|
58451
59433
|
console.log(`keryx metrics
|
|
@@ -58496,7 +59478,7 @@ async function versionCommand(args2, deps = {}) {
|
|
|
58496
59478
|
|
|
58497
59479
|
// src/commands/workspace.ts
|
|
58498
59480
|
init_args();
|
|
58499
|
-
import { randomUUID as
|
|
59481
|
+
import { randomUUID as randomUUID25 } from "crypto";
|
|
58500
59482
|
import { writeFile as writeFile50 } from "fs/promises";
|
|
58501
59483
|
|
|
58502
59484
|
// src/sac/fwk-explain.ts
|
|
@@ -58544,6 +59526,136 @@ function formatFwkExplain(result) {
|
|
|
58544
59526
|
`);
|
|
58545
59527
|
}
|
|
58546
59528
|
|
|
59529
|
+
// src/sac/catch-up.ts
|
|
59530
|
+
init_fs();
|
|
59531
|
+
init_config_dir();
|
|
59532
|
+
import { randomUUID as randomUUID24 } from "crypto";
|
|
59533
|
+
import { readdir as readdir24 } from "fs/promises";
|
|
59534
|
+
import path150 from "path";
|
|
59535
|
+
async function buildCatchUp(input2) {
|
|
59536
|
+
const [proposals, sessionCategories] = await Promise.all([
|
|
59537
|
+
collectProposals(input2.cwd, input2.workspaceId),
|
|
59538
|
+
collectSessionCategories(input2.cwd)
|
|
59539
|
+
]);
|
|
59540
|
+
return { proposals, ...sessionCategories };
|
|
59541
|
+
}
|
|
59542
|
+
async function collectProposals(cwd, workspaceId) {
|
|
59543
|
+
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
59544
|
+
const actor = await authorizationServer.actorContextFor(undefined, randomUUID24());
|
|
59545
|
+
if (!actor)
|
|
59546
|
+
throw new Error("trusted ActorContext is required for catch-up");
|
|
59547
|
+
const proposalService = createLocalProposalLifecycleService(cwd);
|
|
59548
|
+
const groups = await proposalService.listVisibleProposedProposals(actor);
|
|
59549
|
+
const scoped = workspaceId === undefined ? groups : groups.filter((group) => group.workspace.id === workspaceId);
|
|
59550
|
+
const flattened = scoped.flatMap((group) => group.proposals.map((proposal) => ({ group, proposal })));
|
|
59551
|
+
return Promise.all(flattened.map(async ({ group, proposal }) => {
|
|
59552
|
+
const fresh = await proposalService.isEvidenceFresh(proposal, actor);
|
|
59553
|
+
return { type: "proposal", workspaceId: group.workspace.id, proposalId: proposal.id, fresh };
|
|
59554
|
+
}));
|
|
59555
|
+
}
|
|
59556
|
+
async function classifySession(session) {
|
|
59557
|
+
const dir = sessionDir(session.projectPath, session.id);
|
|
59558
|
+
if (await isLockHeld(slateLockPath(dir)))
|
|
59559
|
+
return;
|
|
59560
|
+
const terminalState = await readTerminalState(dir);
|
|
59561
|
+
if (terminalState !== undefined) {
|
|
59562
|
+
const workspaceId2 = (await safeReadSlate(dir))?.workspaceId;
|
|
59563
|
+
return { kind: "blocked", item: { type: "blocked", sessionId: session.id, ...workspaceId2 !== undefined ? { workspaceId: workspaceId2 } : {}, terminalState } };
|
|
59564
|
+
}
|
|
59565
|
+
const unboundCandidate = await readNewestUnboundCandidate(dir);
|
|
59566
|
+
if (unboundCandidate !== undefined) {
|
|
59567
|
+
return {
|
|
59568
|
+
kind: "unbound-candidate",
|
|
59569
|
+
item: { type: "unbound-candidate", sessionId: session.id, evidencePath: unboundCandidate.evidencePath, summary: unboundCandidate.summary }
|
|
59570
|
+
};
|
|
59571
|
+
}
|
|
59572
|
+
if (!await isSlateEngaged(dir))
|
|
59573
|
+
return;
|
|
59574
|
+
const workspaceId = (await safeReadSlate(dir))?.workspaceId;
|
|
59575
|
+
return { kind: "unknown", item: { type: "unknown", sessionId: session.id, ...workspaceId !== undefined ? { workspaceId } : {}, lastSeenAt: session.updatedAt } };
|
|
59576
|
+
}
|
|
59577
|
+
async function collectSessionCategories(cwd) {
|
|
59578
|
+
const classified = await Promise.all(listSessions(cwd).map((session) => classifySession(session)));
|
|
59579
|
+
const blocked2 = [];
|
|
59580
|
+
const unboundCandidates = [];
|
|
59581
|
+
const unknown = [];
|
|
59582
|
+
for (const category of classified) {
|
|
59583
|
+
if (category === undefined)
|
|
59584
|
+
continue;
|
|
59585
|
+
if (category.kind === "blocked")
|
|
59586
|
+
blocked2.push(category.item);
|
|
59587
|
+
else if (category.kind === "unbound-candidate")
|
|
59588
|
+
unboundCandidates.push(category.item);
|
|
59589
|
+
else
|
|
59590
|
+
unknown.push(category.item);
|
|
59591
|
+
}
|
|
59592
|
+
return { blocked: blocked2, unboundCandidates, unknown };
|
|
59593
|
+
}
|
|
59594
|
+
async function isSlateEngaged(dir) {
|
|
59595
|
+
if (await pathExists(path150.join(dir, "slate.json")))
|
|
59596
|
+
return true;
|
|
59597
|
+
if (await pathExists(path150.join(dir, "terminal-state.json")))
|
|
59598
|
+
return true;
|
|
59599
|
+
try {
|
|
59600
|
+
const entries = await readdir24(path150.join(dir, "slate-archive"));
|
|
59601
|
+
return entries.length > 0;
|
|
59602
|
+
} catch {
|
|
59603
|
+
return false;
|
|
59604
|
+
}
|
|
59605
|
+
}
|
|
59606
|
+
async function safeReadSlate(dir) {
|
|
59607
|
+
try {
|
|
59608
|
+
return await readSlate(dir);
|
|
59609
|
+
} catch {
|
|
59610
|
+
return;
|
|
59611
|
+
}
|
|
59612
|
+
}
|
|
59613
|
+
async function readTerminalState(dir) {
|
|
59614
|
+
const result = readConfigFile(path150.join(dir, "terminal-state.json"));
|
|
59615
|
+
if (!result.ok) {
|
|
59616
|
+
return;
|
|
59617
|
+
}
|
|
59618
|
+
try {
|
|
59619
|
+
return JSON.parse(result.text);
|
|
59620
|
+
} catch {
|
|
59621
|
+
return;
|
|
59622
|
+
}
|
|
59623
|
+
}
|
|
59624
|
+
async function readNewestUnboundCandidate(dir) {
|
|
59625
|
+
const archiveDir = path150.join(dir, "slate-archive");
|
|
59626
|
+
let entries;
|
|
59627
|
+
try {
|
|
59628
|
+
entries = (await readdir24(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
59629
|
+
} catch {
|
|
59630
|
+
return;
|
|
59631
|
+
}
|
|
59632
|
+
entries.sort();
|
|
59633
|
+
for (let i = entries.length - 1;i >= 0; i--) {
|
|
59634
|
+
const evidencePath = path150.join(archiveDir, entries[i]);
|
|
59635
|
+
const result = readConfigFile(evidencePath);
|
|
59636
|
+
if (!result.ok) {
|
|
59637
|
+
continue;
|
|
59638
|
+
}
|
|
59639
|
+
try {
|
|
59640
|
+
const parsed = JSON.parse(result.text);
|
|
59641
|
+
if (parsed.recordType !== "unbound-candidate")
|
|
59642
|
+
continue;
|
|
59643
|
+
return { evidencePath, summary: summarizeUnboundCandidate(parsed.groups) };
|
|
59644
|
+
} catch {
|
|
59645
|
+
continue;
|
|
59646
|
+
}
|
|
59647
|
+
}
|
|
59648
|
+
return;
|
|
59649
|
+
}
|
|
59650
|
+
function summarizeUnboundCandidate(groups) {
|
|
59651
|
+
const safeGroups = groups ?? [];
|
|
59652
|
+
if (safeGroups.length === 0)
|
|
59653
|
+
return "no seeds captured";
|
|
59654
|
+
const seedCount = safeGroups.reduce((sum, group) => sum + (group.seeds?.length ?? 0), 0);
|
|
59655
|
+
const kinds = safeGroups.map((group) => typeof group.kind === "string" ? group.kind : "unknown").join(", ");
|
|
59656
|
+
return `${seedCount} untriaged seed(s) across ${safeGroups.length} kind(s) (${kinds})`;
|
|
59657
|
+
}
|
|
59658
|
+
|
|
58547
59659
|
// src/commands/workspace.ts
|
|
58548
59660
|
var PROPOSAL_KINDS = ["decision", "wiki-update", "memory-entry", "follow-up", "contract-change", "risk"];
|
|
58549
59661
|
function service4() {
|
|
@@ -58564,14 +59676,14 @@ async function workspaceCommand(args2) {
|
|
|
58564
59676
|
const component = optionValue(args2, "--component");
|
|
58565
59677
|
if (!title)
|
|
58566
59678
|
throw new Error("Usage: keryx workspace create --title <title> [--component <workspace-relative-ref>]");
|
|
58567
|
-
const workspace = await service4().create({ request: undefined, requestCorrelationId:
|
|
59679
|
+
const workspace = await service4().create({ request: undefined, requestCorrelationId: randomUUID25(), id: newWorkspaceId(), title, ...component ? { component: { kind: "component", uri: component } } : {} });
|
|
58568
59680
|
console.log(JSON.stringify(workspace, null, 2));
|
|
58569
59681
|
return;
|
|
58570
59682
|
}
|
|
58571
59683
|
if (subcommand === "list") {
|
|
58572
59684
|
rejectUnknownOptions(args2.slice(1), new Set(["--include-archived"]));
|
|
58573
59685
|
const includeArchived = booleanFlag(args2, "--include-archived");
|
|
58574
|
-
console.log(JSON.stringify(await service4().list({ request: undefined, requestCorrelationId:
|
|
59686
|
+
console.log(JSON.stringify(await service4().list({ request: undefined, requestCorrelationId: randomUUID25(), includeArchived }), null, 2));
|
|
58575
59687
|
return;
|
|
58576
59688
|
}
|
|
58577
59689
|
if (subcommand === "show") {
|
|
@@ -58579,7 +59691,7 @@ async function workspaceCommand(args2) {
|
|
|
58579
59691
|
const id = args2[1];
|
|
58580
59692
|
if (!id)
|
|
58581
59693
|
throw new Error("Usage: keryx workspace show <workspace-id>");
|
|
58582
|
-
console.log(JSON.stringify(await service4().show({ request: undefined, requestCorrelationId:
|
|
59694
|
+
console.log(JSON.stringify(await service4().show({ request: undefined, requestCorrelationId: randomUUID25(), workspaceId: id }), null, 2));
|
|
58583
59695
|
return;
|
|
58584
59696
|
}
|
|
58585
59697
|
if (subcommand === "add-resource") {
|
|
@@ -58590,7 +59702,7 @@ async function workspaceCommand(args2) {
|
|
|
58590
59702
|
const revision = optionValue(args2, "--revision");
|
|
58591
59703
|
if (!workspaceId || !kind || !uri)
|
|
58592
59704
|
throw new Error("Usage: keryx workspace add-resource <workspace-id> --kind <kind> --uri <workspace-relative-ref> [--revision <revision>]");
|
|
58593
|
-
console.log(JSON.stringify(await service4().addResource({ request: undefined, requestCorrelationId:
|
|
59705
|
+
console.log(JSON.stringify(await service4().addResource({ request: undefined, requestCorrelationId: randomUUID25(), workspaceId, resource: { kind, uri, ...revision ? { revision } : {} } }), null, 2));
|
|
58594
59706
|
return;
|
|
58595
59707
|
}
|
|
58596
59708
|
if (subcommand === "archive") {
|
|
@@ -58598,7 +59710,7 @@ async function workspaceCommand(args2) {
|
|
|
58598
59710
|
const workspaceId = args2[1];
|
|
58599
59711
|
if (!workspaceId)
|
|
58600
59712
|
throw new Error("Usage: keryx workspace archive <workspace-id>");
|
|
58601
|
-
console.log(JSON.stringify(await service4().archive({ request: undefined, requestCorrelationId:
|
|
59713
|
+
console.log(JSON.stringify(await service4().archive({ request: undefined, requestCorrelationId: randomUUID25(), workspaceId }), null, 2));
|
|
58602
59714
|
return;
|
|
58603
59715
|
}
|
|
58604
59716
|
if (subcommand === "remove-resource") {
|
|
@@ -58607,7 +59719,7 @@ async function workspaceCommand(args2) {
|
|
|
58607
59719
|
const uri = optionValue(args2, "--uri");
|
|
58608
59720
|
if (!workspaceId || !uri)
|
|
58609
59721
|
throw new Error("Usage: keryx workspace remove-resource <workspace-id> --uri <workspace-relative-ref>");
|
|
58610
|
-
console.log(JSON.stringify(await service4().removeResource({ request: undefined, requestCorrelationId:
|
|
59722
|
+
console.log(JSON.stringify(await service4().removeResource({ request: undefined, requestCorrelationId: randomUUID25(), workspaceId, uri }), null, 2));
|
|
58611
59723
|
return;
|
|
58612
59724
|
}
|
|
58613
59725
|
if (subcommand === "rename") {
|
|
@@ -58616,7 +59728,7 @@ async function workspaceCommand(args2) {
|
|
|
58616
59728
|
const title = optionValue(args2, "--title");
|
|
58617
59729
|
if (!workspaceId || !title)
|
|
58618
59730
|
throw new Error("Usage: keryx workspace rename <workspace-id> --title <title>");
|
|
58619
|
-
console.log(JSON.stringify(await service4().rename({ request: undefined, requestCorrelationId:
|
|
59731
|
+
console.log(JSON.stringify(await service4().rename({ request: undefined, requestCorrelationId: randomUUID25(), workspaceId, title }), null, 2));
|
|
58620
59732
|
return;
|
|
58621
59733
|
}
|
|
58622
59734
|
if (subcommand === "overview") {
|
|
@@ -58628,7 +59740,7 @@ async function workspaceCommand(args2) {
|
|
|
58628
59740
|
const maxTokens = Number(optionValue(args2, "--max-tokens") ?? "4096");
|
|
58629
59741
|
if (!Number.isInteger(maxItems) || !Number.isInteger(maxTokens) || maxItems < 0 || maxTokens < 0)
|
|
58630
59742
|
throw new Error("--max-items and --max-tokens must be non-negative integers");
|
|
58631
|
-
const result = await createLocalFwkReadService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId:
|
|
59743
|
+
const result = await createLocalFwkReadService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID25(), budget: { maxItems, maxTokens } });
|
|
58632
59744
|
const normalized = normalizeFwkResult(result);
|
|
58633
59745
|
console.log(JSON.stringify(normalized, null, 2));
|
|
58634
59746
|
if (args2.includes("--explain"))
|
|
@@ -58645,7 +59757,7 @@ async function workspaceCommand(args2) {
|
|
|
58645
59757
|
const maxTokens = Number(optionValue(args2, "--max-tokens") ?? "4096");
|
|
58646
59758
|
if (!Number.isInteger(maxItems) || !Number.isInteger(maxTokens) || maxItems < 0 || maxTokens < 0)
|
|
58647
59759
|
throw new Error("--max-items and --max-tokens must be non-negative integers");
|
|
58648
|
-
const result = await createLocalFwkReadService(process.cwd()).read({ workspaceId, itemId, request: undefined, requestCorrelationId:
|
|
59760
|
+
const result = await createLocalFwkReadService(process.cwd()).read({ workspaceId, itemId, request: undefined, requestCorrelationId: randomUUID25(), budget: { maxItems, maxTokens } });
|
|
58649
59761
|
const normalized = normalizeFwkResult(result);
|
|
58650
59762
|
console.log(JSON.stringify(normalized, null, 2));
|
|
58651
59763
|
if (args2.includes("--explain"))
|
|
@@ -58668,12 +59780,12 @@ async function workspaceCommand(args2) {
|
|
|
58668
59780
|
if (!session)
|
|
58669
59781
|
throw new Error(`no session matching "${sessionRef}" in this project \u2014 use \`keryx sessions list\``);
|
|
58670
59782
|
const { service: service5, wrapUpAuthority, authorizationServer } = createHarnessProposalLifecycleService(cwd, { workspaceId, ...note2 ? { note: note2 } : {} });
|
|
58671
|
-
const requestCorrelationId =
|
|
59783
|
+
const requestCorrelationId = randomUUID25();
|
|
58672
59784
|
const actor = await authorizationServer.actorContextFor(undefined, requestCorrelationId);
|
|
58673
59785
|
if (!actor)
|
|
58674
59786
|
throw new Error("trusted ActorContext is required");
|
|
58675
59787
|
const wrapUp = await wrapUpAuthority.issue({ actor, source: "session", sourceRef: sessionEvidenceRef(workspaceId, session.id) });
|
|
58676
|
-
const proposal = await service5.create({ request: undefined, requestCorrelationId, workspaceId, id: `proposal-${
|
|
59788
|
+
const proposal = await service5.create({ request: undefined, requestCorrelationId, workspaceId, id: `proposal-${randomUUID25().replace(/-/g, "").slice(0, 16)}`, proposalRevision, kind, wrapUp });
|
|
58677
59789
|
if (note2)
|
|
58678
59790
|
await writeFile50(proposalNotePath(cwd, workspaceId, proposal.id), note2, "utf8");
|
|
58679
59791
|
console.log(JSON.stringify(normalizeProposalLifecycleResult(proposal), null, 2));
|
|
@@ -58685,10 +59797,10 @@ async function workspaceCommand(args2) {
|
|
|
58685
59797
|
const proposalId = args2[2];
|
|
58686
59798
|
const decision = optionValue(args2, "--decision");
|
|
58687
59799
|
const reason = optionValue(args2, "--reason");
|
|
58688
|
-
const idempotencyKey = optionValue(args2, "--idempotency-key") ??
|
|
59800
|
+
const idempotencyKey = optionValue(args2, "--idempotency-key") ?? randomUUID25();
|
|
58689
59801
|
if (!workspaceId || !proposalId || !decision)
|
|
58690
59802
|
throw new Error("Usage: keryx workspace review <workspace-id> <proposal-id> --decision <accepted|rejected|dismissed> [--reason <reason>] [--idempotency-key <key>]");
|
|
58691
|
-
const result = await createHarnessProposalLifecycleService(process.cwd(), { workspaceId }).service.review({ request: undefined, requestCorrelationId:
|
|
59803
|
+
const result = await createHarnessProposalLifecycleService(process.cwd(), { workspaceId }).service.review({ request: undefined, requestCorrelationId: randomUUID25(), workspaceId, proposalId, decision, idempotencyKey, interactive: true, ...reason ? { reason } : {} });
|
|
58692
59804
|
console.log(JSON.stringify(normalizeProposalLifecycleResult(result), null, 2));
|
|
58693
59805
|
return;
|
|
58694
59806
|
}
|
|
@@ -58697,7 +59809,7 @@ async function workspaceCommand(args2) {
|
|
|
58697
59809
|
const workspaceId = args2[1];
|
|
58698
59810
|
if (!workspaceId)
|
|
58699
59811
|
throw new Error("Usage: keryx workspace collaboration <workspace-id>");
|
|
58700
|
-
console.log(JSON.stringify(await createLocalCollaborationService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId:
|
|
59812
|
+
console.log(JSON.stringify(await createLocalCollaborationService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID25() }), null, 2));
|
|
58701
59813
|
return;
|
|
58702
59814
|
}
|
|
58703
59815
|
if (subcommand === "policy-readiness") {
|
|
@@ -58708,6 +59820,32 @@ async function workspaceCommand(args2) {
|
|
|
58708
59820
|
process.exitCode = 1;
|
|
58709
59821
|
return;
|
|
58710
59822
|
}
|
|
59823
|
+
if (subcommand === "catch-up") {
|
|
59824
|
+
rejectUnknownOptions(args2.slice(1), new Set(["--workspace", "--json"]));
|
|
59825
|
+
const workspaceId = optionValue(args2, "--workspace");
|
|
59826
|
+
const report = await buildCatchUp({ cwd: process.cwd(), ...workspaceId ? { workspaceId } : {} });
|
|
59827
|
+
if (args2.includes("--json"))
|
|
59828
|
+
console.log(JSON.stringify(report, null, 2));
|
|
59829
|
+
else
|
|
59830
|
+
console.log(renderCatchUp(report));
|
|
59831
|
+
return;
|
|
59832
|
+
}
|
|
59833
|
+
if (subcommand === "list-proposals") {
|
|
59834
|
+
rejectUnknownOptions(args2.slice(2), new Set);
|
|
59835
|
+
const workspaceId = args2[1];
|
|
59836
|
+
const cwd = process.cwd();
|
|
59837
|
+
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
59838
|
+
const actor = await authorizationServer.actorContextFor(undefined, randomUUID25());
|
|
59839
|
+
if (!actor)
|
|
59840
|
+
throw new Error("trusted ActorContext is required");
|
|
59841
|
+
if (workspaceId) {
|
|
59842
|
+
await service4().showForActor({ actorContext: actor, workspaceId });
|
|
59843
|
+
console.log(JSON.stringify(await createLocalProposalLifecycleService(cwd).listProposedProposals(workspaceId), null, 2));
|
|
59844
|
+
return;
|
|
59845
|
+
}
|
|
59846
|
+
console.log(JSON.stringify(await createLocalProposalLifecycleService(cwd).listVisibleProposedProposals(actor), null, 2));
|
|
59847
|
+
return;
|
|
59848
|
+
}
|
|
58711
59849
|
throw new Error(`Unknown workspace command: ${subcommand}`);
|
|
58712
59850
|
} catch (error2) {
|
|
58713
59851
|
console.error(error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -58752,7 +59890,29 @@ keryx workspace read <workspace-id> <item-id> [--max-items N] [--max-tokens N] [
|
|
|
58752
59890
|
keryx workspace propose <workspace-id> --kind <` + PROPOSAL_KINDS.join("|") + `> --session <session-id> [--note <one-line note>]
|
|
58753
59891
|
keryx workspace review <workspace-id> <proposal-id> --decision <accepted|rejected|dismissed> [--reason <reason>] [--idempotency-key <key>]
|
|
58754
59892
|
keryx workspace collaboration <workspace-id>
|
|
58755
|
-
keryx workspace policy-readiness
|
|
59893
|
+
keryx workspace policy-readiness
|
|
59894
|
+
keryx workspace catch-up [--workspace <workspace-id>] [--json]
|
|
59895
|
+
keryx workspace list-proposals [<workspace-id>]`);
|
|
59896
|
+
}
|
|
59897
|
+
function renderCatchUp(report) {
|
|
59898
|
+
const sections = [];
|
|
59899
|
+
sections.push(renderSection("Pending proposals", report.proposals, (item) => `- Accept, reject, or dismiss proposal ${item.proposalId} in workspace ${item.workspaceId}? ` + `Recommendation: ${item.fresh ? "evidence is fresh \u2014 review now (`keryx workspace review " + item.workspaceId + " " + item.proposalId + " --decision <accepted|rejected|dismissed>`)" : "evidence has drifted since this proposal was created \u2014 treat as stale, re-run wrap-up before deciding"}.`));
|
|
59900
|
+
sections.push(renderSection("Blocked sessions (stopped unattended)", report.blocked, (item) => `- Session ${item.sessionId} stopped unattended (${item.terminalState.reason}) at ${item.terminalState.occurredAt}. Resume it, or archive and move on? ` + `Recommendation: \`keryx shell -r ${item.sessionId}\` to resume and unblock it.`));
|
|
59901
|
+
sections.push(renderSection("Unbound candidates (wrap-up ran, no workspace bound)", report.unboundCandidates, (item) => `- Session ${item.sessionId} produced untriaged seeds with no workspace bound (${item.summary}). Bind to a workspace and propose, or discard? ` + `Recommendation: pick a workspace, then \`keryx workspace propose <workspace-id> --kind <kind> --session ${item.sessionId}\` (evidence: ${item.evidencePath}).`));
|
|
59902
|
+
sections.push(renderSection("Unknown (no resolution recorded)", report.unknown, (item) => `- Session ${item.sessionId} was last seen ${item.lastSeenAt} with no proposal, terminal state, or unbound-candidate artifact recorded. Investigate, or ignore? ` + `Recommendation: \`keryx sessions list\` / \`keryx shell -r ${item.sessionId}\` to see what happened.`));
|
|
59903
|
+
return sections.join(`
|
|
59904
|
+
|
|
59905
|
+
`);
|
|
59906
|
+
}
|
|
59907
|
+
function renderSection(title, items, describe) {
|
|
59908
|
+
const lines = [`== ${title} ==`];
|
|
59909
|
+
if (items.length === 0)
|
|
59910
|
+
lines.push("(none)");
|
|
59911
|
+
else
|
|
59912
|
+
for (const item of items)
|
|
59913
|
+
lines.push(describe(item));
|
|
59914
|
+
return lines.join(`
|
|
59915
|
+
`);
|
|
58756
59916
|
}
|
|
58757
59917
|
|
|
58758
59918
|
// src/cli.ts
|