@mrciphersmith/keryx 0.2.130 → 0.2.131
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 +121 -15
- package/dist/core.js +48 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -24338,6 +24338,49 @@ var init_trusted_wrap_up = __esm(() => {
|
|
|
24338
24338
|
consumed = new WeakSet;
|
|
24339
24339
|
});
|
|
24340
24340
|
|
|
24341
|
+
// src/sac/workspace-reference.ts
|
|
24342
|
+
function requireWorkspaceReference(raw) {
|
|
24343
|
+
const normalized = normalizeWorkspaceReference(raw);
|
|
24344
|
+
if (!normalized.ok)
|
|
24345
|
+
throw new Error(normalized.reason);
|
|
24346
|
+
return normalized.uri;
|
|
24347
|
+
}
|
|
24348
|
+
function normalizeWorkspaceReference(raw) {
|
|
24349
|
+
const trimmed = raw.trim();
|
|
24350
|
+
if (trimmed.length === 0) {
|
|
24351
|
+
return { ok: false, reason: 'a workspace reference must be a non-empty workspace-relative path, e.g. "./src/harness/search"' };
|
|
24352
|
+
}
|
|
24353
|
+
if (UNSAFE_ABSOLUTE.test(trimmed) || UNSAFE_SCHEME.test(trimmed) || trimmed.includes("\\")) {
|
|
24354
|
+
return {
|
|
24355
|
+
ok: false,
|
|
24356
|
+
reason: `"${trimmed}" is not a workspace-relative path \u2014 an absolute path, a URL and a Windows path are all refused. Use a path inside the project, e.g. "./src/harness/search".`
|
|
24357
|
+
};
|
|
24358
|
+
}
|
|
24359
|
+
const withoutDotSlash = trimmed.startsWith("./") ? trimmed.slice(2) : trimmed;
|
|
24360
|
+
const withoutTrailingSlash = withoutDotSlash.replace(/\/+$/, "");
|
|
24361
|
+
if (withoutTrailingSlash.length === 0) {
|
|
24362
|
+
return { ok: false, reason: `"${trimmed}" names the project root rather than a path inside it \u2014 name a directory or file, e.g. "./src/harness/search".` };
|
|
24363
|
+
}
|
|
24364
|
+
const segments = withoutTrailingSlash.split("/");
|
|
24365
|
+
if (segments.includes("..") || segments.includes(".")) {
|
|
24366
|
+
return { ok: false, reason: `"${trimmed}" contains a "." or ".." path segment \u2014 a workspace reference must stay inside the project.` };
|
|
24367
|
+
}
|
|
24368
|
+
const uri = `./${withoutTrailingSlash}`;
|
|
24369
|
+
if (!WORKSPACE_REFERENCE_PATTERN.test(uri)) {
|
|
24370
|
+
return {
|
|
24371
|
+
ok: false,
|
|
24372
|
+
reason: `"${trimmed}" is not a usable workspace reference. The stored form is "${uri}" if the characters are the problem: ` + 'each path segment may contain only letters, digits, ".", "_" and "-", and no ".." segment is allowed.'
|
|
24373
|
+
};
|
|
24374
|
+
}
|
|
24375
|
+
return { ok: true, uri };
|
|
24376
|
+
}
|
|
24377
|
+
var WORKSPACE_REFERENCE_PATTERN, UNSAFE_ABSOLUTE, UNSAFE_SCHEME;
|
|
24378
|
+
var init_workspace_reference = __esm(() => {
|
|
24379
|
+
WORKSPACE_REFERENCE_PATTERN = /^\.\/(?!(?:.*\/)?\.\.(?:\/|$))(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+$/;
|
|
24380
|
+
UNSAFE_ABSOLUTE = /^[/\\]/;
|
|
24381
|
+
UNSAFE_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
24382
|
+
});
|
|
24383
|
+
|
|
24341
24384
|
// src/sac/index.ts
|
|
24342
24385
|
import { access as access2, readFile as readFile50, realpath as realpath6 } from "fs/promises";
|
|
24343
24386
|
import { createHash as createHash18 } from "crypto";
|
|
@@ -25085,11 +25128,12 @@ async function evaluateStrictSacGuard(input) {
|
|
|
25085
25128
|
}
|
|
25086
25129
|
var idPattern, subjectPattern, revisionPattern, correlationPattern, workspacePathPattern, receiptHashPattern, utcPattern, forbiddenPayloadKeys, normativeSchemaFiles, normativeSchemas, NORMATIVE_SCHEMA_DIR, SacReferenceError, trustedActors, roleRank;
|
|
25087
25130
|
var init_sac = __esm(() => {
|
|
25131
|
+
init_workspace_reference();
|
|
25088
25132
|
idPattern = /^[a-z][a-z0-9-]{2,63}$/;
|
|
25089
25133
|
subjectPattern = /^(?:user|team|service|agent):[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
25090
25134
|
revisionPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/;
|
|
25091
25135
|
correlationPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{15,255}$/;
|
|
25092
|
-
workspacePathPattern =
|
|
25136
|
+
workspacePathPattern = WORKSPACE_REFERENCE_PATTERN;
|
|
25093
25137
|
receiptHashPattern = /^[a-f0-9]{64}$/;
|
|
25094
25138
|
utcPattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?Z$/;
|
|
25095
25139
|
forbiddenPayloadKeys = new Set(["prompt", "transcript", "hiddenReasoning", "secret", "secrets", "rawContent"]);
|
|
@@ -51085,7 +51129,7 @@ import path47 from "path";
|
|
|
51085
51129
|
// package.json
|
|
51086
51130
|
var package_default = {
|
|
51087
51131
|
name: "@mrciphersmith/keryx",
|
|
51088
|
-
version: "0.2.
|
|
51132
|
+
version: "0.2.131",
|
|
51089
51133
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
51090
51134
|
private: false,
|
|
51091
51135
|
publishConfig: {
|
|
@@ -78293,6 +78337,7 @@ function toMcpTools(ops = METAPROJECT_OPERATIONS, adapterFor = (cwd) => createMe
|
|
|
78293
78337
|
}
|
|
78294
78338
|
|
|
78295
78339
|
// src/sac/fwk-service.ts
|
|
78340
|
+
init_workspace_reference();
|
|
78296
78341
|
init_assembly();
|
|
78297
78342
|
init_sac();
|
|
78298
78343
|
init_workspace_service();
|
|
@@ -78303,6 +78348,7 @@ import { appendFile as appendFile7, mkdir as mkdir59, open as open2, readFile as
|
|
|
78303
78348
|
import path174 from "path";
|
|
78304
78349
|
|
|
78305
78350
|
// src/sac/policy-experiment.ts
|
|
78351
|
+
init_workspace_reference();
|
|
78306
78352
|
import { createHash as createHash34, createHmac as createHmac2 } from "crypto";
|
|
78307
78353
|
|
|
78308
78354
|
// src/sac/receipt-integrity.ts
|
|
@@ -78366,7 +78412,7 @@ function stableValue(value) {
|
|
|
78366
78412
|
}
|
|
78367
78413
|
var stableJson2 = (value) => JSON.stringify(stableValue(value));
|
|
78368
78414
|
var sha2564 = (value) => createHash34("sha256").update(stableJson2(value), "utf8").digest("hex");
|
|
78369
|
-
var workspacePathPattern2 =
|
|
78415
|
+
var workspacePathPattern2 = WORKSPACE_REFERENCE_PATTERN;
|
|
78370
78416
|
var trustedOutcomes = new WeakMap;
|
|
78371
78417
|
var trustedSandboxEvidence = new WeakMap;
|
|
78372
78418
|
var trustedSandboxExecutions = new WeakMap;
|
|
@@ -78458,7 +78504,7 @@ var metadataOnly = (value) => {
|
|
|
78458
78504
|
var nowIso2 = (now) => now().toISOString();
|
|
78459
78505
|
var reportHashPattern = /^[a-f0-9]{64}$/;
|
|
78460
78506
|
var immutableVersionPattern = /(?:^|[-_.:])(?:latest|next|head|main|develop)(?:$|[-_.:])/i;
|
|
78461
|
-
var workspacePathPattern3 =
|
|
78507
|
+
var workspacePathPattern3 = WORKSPACE_REFERENCE_PATTERN;
|
|
78462
78508
|
var policyExperimentConfigPath = [".metaproject", "context-operations", "policy-experiment", "config.json"];
|
|
78463
78509
|
var stableValue2 = (value) => {
|
|
78464
78510
|
if (Array.isArray(value))
|
|
@@ -79333,6 +79379,7 @@ async function reclaimStaleExternalSlates(cwd, now = () => new Date) {
|
|
|
79333
79379
|
init_slate();
|
|
79334
79380
|
init_workspace_resolve();
|
|
79335
79381
|
init_redact();
|
|
79382
|
+
init_workspace_reference();
|
|
79336
79383
|
|
|
79337
79384
|
// src/mcp/tools.ts
|
|
79338
79385
|
import { randomUUID as randomUUID19 } from "crypto";
|
|
@@ -79545,7 +79592,8 @@ function buildToolRegistry() {
|
|
|
79545
79592
|
const title = stringParam2(params, "title")?.trim() ?? "";
|
|
79546
79593
|
if (title.length === 0)
|
|
79547
79594
|
throw new Error("sac.workspaceCreate requires a non-empty 'title'");
|
|
79548
|
-
const
|
|
79595
|
+
const rawComponent = stringParam2(params, "component");
|
|
79596
|
+
const component = rawComponent === undefined ? undefined : requireWorkspaceReference(rawComponent);
|
|
79549
79597
|
const workspace = await sacWorkspaceService(cwd).create({ request: undefined, requestCorrelationId: randomUUID19(), id: newWorkspaceId(), title, ...component ? { component: { kind: "component", uri: component } } : {} });
|
|
79550
79598
|
return workspace;
|
|
79551
79599
|
}
|
|
@@ -87467,7 +87515,12 @@ function workspaceCreateTool(cwd, getSessionDir) {
|
|
|
87467
87515
|
const title = typeof input.title === "string" ? input.title.trim() : "";
|
|
87468
87516
|
if (title.length === 0)
|
|
87469
87517
|
return { output: "workspace_create requires a non-empty 'title'", isError: true };
|
|
87470
|
-
const
|
|
87518
|
+
const rawComponent = typeof input.component === "string" && input.component.length > 0 ? input.component : undefined;
|
|
87519
|
+
const normalizedComponent = rawComponent === undefined ? undefined : normalizeWorkspaceReference(rawComponent);
|
|
87520
|
+
if (normalizedComponent !== undefined && !normalizedComponent.ok) {
|
|
87521
|
+
return { output: "workspace_create: " + normalizedComponent.reason, isError: true };
|
|
87522
|
+
}
|
|
87523
|
+
const component = normalizedComponent?.ok === true ? normalizedComponent.uri : undefined;
|
|
87471
87524
|
try {
|
|
87472
87525
|
const workspace = await service5(cwd).create({
|
|
87473
87526
|
request: undefined,
|
|
@@ -93273,14 +93326,25 @@ async function classifySession(session) {
|
|
|
93273
93326
|
sessionId: session.id,
|
|
93274
93327
|
...workspaceId !== undefined ? { workspaceId } : {},
|
|
93275
93328
|
lastSeenAt: session.updatedAt,
|
|
93329
|
+
reason: "wrap-up-failed",
|
|
93276
93330
|
wrapUpOutcome: { trigger: wrapUpOutcome.trigger, generatedAt: wrapUpOutcome.generatedAt, groups: wrapUpOutcome.groups }
|
|
93277
93331
|
}
|
|
93278
93332
|
};
|
|
93279
93333
|
}
|
|
93280
93334
|
if (!await isSlateEngaged(dir))
|
|
93281
93335
|
return;
|
|
93282
|
-
const
|
|
93283
|
-
|
|
93336
|
+
const slateRead = await readSlateState(dir);
|
|
93337
|
+
const workspaceId = slateRead.state === "ok" ? slateRead.slate.workspaceId : undefined;
|
|
93338
|
+
return {
|
|
93339
|
+
kind: "unknown",
|
|
93340
|
+
item: {
|
|
93341
|
+
type: "unknown",
|
|
93342
|
+
sessionId: session.id,
|
|
93343
|
+
...workspaceId !== undefined ? { workspaceId } : {},
|
|
93344
|
+
lastSeenAt: session.updatedAt,
|
|
93345
|
+
reason: slateRead.state === "unreadable" ? "slate-unreadable" : "no-resolution-recorded"
|
|
93346
|
+
}
|
|
93347
|
+
};
|
|
93284
93348
|
}
|
|
93285
93349
|
async function readExternalUnboundCandidates(cwd) {
|
|
93286
93350
|
const extDir = externalSlatesDir(cwd);
|
|
@@ -93533,6 +93597,17 @@ async function isSlateEngaged(dir) {
|
|
|
93533
93597
|
return false;
|
|
93534
93598
|
}
|
|
93535
93599
|
}
|
|
93600
|
+
async function readSlateState(dir) {
|
|
93601
|
+
try {
|
|
93602
|
+
const slate = await readSlate(dir);
|
|
93603
|
+
return slate === undefined ? { state: "absent" } : { state: "ok", slate };
|
|
93604
|
+
} catch (error) {
|
|
93605
|
+
return isNotFound2(error) ? { state: "absent" } : { state: "unreadable" };
|
|
93606
|
+
}
|
|
93607
|
+
}
|
|
93608
|
+
function isNotFound2(error) {
|
|
93609
|
+
return typeof error === "object" && error !== null && error.code === "ENOENT";
|
|
93610
|
+
}
|
|
93536
93611
|
async function safeReadSlate(dir) {
|
|
93537
93612
|
try {
|
|
93538
93613
|
return await readSlate(dir);
|
|
@@ -94498,7 +94573,7 @@ function summarizeReviewItem(item) {
|
|
|
94498
94573
|
case "unbound-candidate":
|
|
94499
94574
|
return `${item.sessionId} \u2014 ${item.summary}`;
|
|
94500
94575
|
case "unknown":
|
|
94501
|
-
return `${item.sessionId} \u2014 last seen ${item.lastSeenAt}`;
|
|
94576
|
+
return `${item.sessionId} \u2014 ${unknownReasonLabel(item.reason)} (last seen ${item.lastSeenAt})`;
|
|
94502
94577
|
}
|
|
94503
94578
|
}
|
|
94504
94579
|
function formatReviewListLines(items, selected) {
|
|
@@ -94510,6 +94585,26 @@ function formatReviewListLines(items, selected) {
|
|
|
94510
94585
|
return `${mark} ${TYPE_LABEL[item.type].padEnd(8)} ${summarizeReviewItem(item)}`;
|
|
94511
94586
|
});
|
|
94512
94587
|
}
|
|
94588
|
+
function unknownReasonLabel(reason) {
|
|
94589
|
+
switch (reason) {
|
|
94590
|
+
case "wrap-up-failed":
|
|
94591
|
+
return "wrap-up failed";
|
|
94592
|
+
case "slate-unreadable":
|
|
94593
|
+
return "slate.json unreadable";
|
|
94594
|
+
case "no-resolution-recorded":
|
|
94595
|
+
return "no resolution recorded";
|
|
94596
|
+
}
|
|
94597
|
+
}
|
|
94598
|
+
function unknownReasonDetail(reason) {
|
|
94599
|
+
switch (reason) {
|
|
94600
|
+
case "wrap-up-failed":
|
|
94601
|
+
return "a wrap-up dispatch ran and every group failed - the per-kind reasons are above where recorded; re-run wrap-up after fixing them.";
|
|
94602
|
+
case "slate-unreadable":
|
|
94603
|
+
return "this session's slate.json exists but could not be read. Its record is DAMAGED, not absent: inspect the file before assuming nothing happened.";
|
|
94604
|
+
case "no-resolution-recorded":
|
|
94605
|
+
return "Slate engagement with no proposal, terminal state, unbound-candidate or wrap-up-outcome artifact recorded.";
|
|
94606
|
+
}
|
|
94607
|
+
}
|
|
94513
94608
|
function describeReviewItem(item) {
|
|
94514
94609
|
switch (item.type) {
|
|
94515
94610
|
case "proposal":
|
|
@@ -94551,7 +94646,7 @@ function describeReviewItem(item) {
|
|
|
94551
94646
|
] : [
|
|
94552
94647
|
`Session ${item.sessionId}${item.workspaceId !== undefined ? ` (workspace ${item.workspaceId})` : ""}`,
|
|
94553
94648
|
`Last seen ${item.lastSeenAt}`,
|
|
94554
|
-
"
|
|
94649
|
+
"Why unknown: " + unknownReasonDetail(item.reason),
|
|
94555
94650
|
"",
|
|
94556
94651
|
`Investigate: keryx sessions list / keryx shell -r ${item.sessionId}`
|
|
94557
94652
|
];
|
|
@@ -115928,7 +116023,8 @@ async function workspaceCommand(args) {
|
|
|
115928
116023
|
if (subcommand === "create") {
|
|
115929
116024
|
rejectUnknownOptions(args.slice(1), new Set(["--title", "--component"]));
|
|
115930
116025
|
const title = optionValue(args, "--title");
|
|
115931
|
-
const
|
|
116026
|
+
const rawComponent = optionValue(args, "--component");
|
|
116027
|
+
const component = rawComponent === undefined ? undefined : requireWorkspaceReference(rawComponent);
|
|
115932
116028
|
if (!title)
|
|
115933
116029
|
throw new Error("Usage: keryx workspace create --title <title> [--component <workspace-relative-ref>]");
|
|
115934
116030
|
const workspace = await service6().create({ request: undefined, requestCorrelationId: randomUUID40(), id: newWorkspaceId(), title, ...component ? { component: { kind: "component", uri: component } } : {} });
|
|
@@ -115957,11 +116053,11 @@ async function workspaceCommand(args) {
|
|
|
115957
116053
|
rejectUnknownOptions(args.slice(2), new Set(["--kind", "--uri", "--revision"]));
|
|
115958
116054
|
const workspaceId = args[1];
|
|
115959
116055
|
const kind = optionValue(args, "--kind");
|
|
115960
|
-
const
|
|
116056
|
+
const rawUri = optionValue(args, "--uri");
|
|
115961
116057
|
const revision = optionValue(args, "--revision");
|
|
115962
|
-
if (!workspaceId || !kind || !
|
|
116058
|
+
if (!workspaceId || !kind || !rawUri)
|
|
115963
116059
|
throw new Error("Usage: keryx workspace add-resource <workspace-id> --kind <kind> --uri <workspace-relative-ref> [--revision <revision>]");
|
|
115964
|
-
console.log(JSON.stringify(await service6().addResource({ request: undefined, requestCorrelationId: randomUUID40(), workspaceId, resource: { kind, uri, ...revision ? { revision } : {} } }), null, 2));
|
|
116060
|
+
console.log(JSON.stringify(await service6().addResource({ request: undefined, requestCorrelationId: randomUUID40(), workspaceId, resource: { kind, uri: requireWorkspaceReference(rawUri), ...revision ? { revision } : {} } }), null, 2));
|
|
115965
116061
|
return;
|
|
115966
116062
|
}
|
|
115967
116063
|
if (subcommand === "archive") {
|
|
@@ -116235,7 +116331,7 @@ function renderCatchUp(report, includeLifecycleFlags = true) {
|
|
|
116235
116331
|
sections.push(renderSection("Pending proposals", report.proposals, (item) => `- Accept, reject, or dismiss ${item.kind} proposal ${item.proposalId} in workspace ${item.workspaceId}` + `${item.note !== undefined ? `: "${item.note}"` : ""}? ` + `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"}.`));
|
|
116236
116332
|
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.`));
|
|
116237
116333
|
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}).`));
|
|
116238
|
-
sections.push(renderSection("Unknown (no resolution recorded)", report.unknown, (item) => `- Session ${item.sessionId} was last seen ${item.lastSeenAt}
|
|
116334
|
+
sections.push(renderSection("Unknown (no resolution recorded)", report.unknown, (item) => `- Session ${item.sessionId} was last seen ${item.lastSeenAt}: ${describeUnknownReason(item)}. Investigate, or ignore? ` + `Recommendation: \`keryx sessions list\` / \`keryx shell -r ${item.sessionId}\` to see what happened.`));
|
|
116239
116335
|
sections.push(renderSection("Unreviewed SAC-owned changes (no proposal on record)", report.unreviewedPaths, (item) => `- Session ${item.sessionId} changed ${item.owner} path \`${item.path}\`${item.status !== undefined ? ` (Status: ${item.status})` : ""} at ${item.changedAt} with NO SAC proposal/receipt behind it \u2014 this looks like it bypassed review. Was this reviewed some other way, or should it be? ` + `Recommendation: \`keryx shell -r ${item.sessionId}\` to see what happened; if the content is good, route it through a real proposal (\`keryx workspace propose ...\`) before trusting it as durable knowledge.`));
|
|
116240
116336
|
if (includeLifecycleFlags) {
|
|
116241
116337
|
sections.push(renderSection("Lifecycle flags (component no longer in the graph)", report.lifecycleFlags, (item) => `- ${item.kind} \`${item.ref}\` scopes to \`${item.missingComponent}\`, which is no longer in the code graph (flagged ${item.flaggedAt}). Still relevant, or safe to clean up? ` + `Recommendation: this is report-only \u2014 nothing was archived/edited/removed automatically; ${item.kind === "workspace" ? "`keryx workspace archive " + item.ref + "`" : item.kind === "memory-entry" ? "`keryx memory supersede` or edit the entry directly" : "edit or remove the wiki page directly"} if you decide it's actually stale.`));
|
|
@@ -116244,6 +116340,16 @@ function renderCatchUp(report, includeLifecycleFlags = true) {
|
|
|
116244
116340
|
|
|
116245
116341
|
`);
|
|
116246
116342
|
}
|
|
116343
|
+
function describeUnknownReason(item) {
|
|
116344
|
+
switch (item.reason) {
|
|
116345
|
+
case "wrap-up-failed":
|
|
116346
|
+
return "a wrap-up dispatch ran and every group failed (re-run it after fixing credentials/conflicts)";
|
|
116347
|
+
case "slate-unreadable":
|
|
116348
|
+
return "its slate.json is present but could not be read - the session's own record is damaged, not merely unrecorded";
|
|
116349
|
+
case "no-resolution-recorded":
|
|
116350
|
+
return "it has Slate engagement but no proposal, terminal state, unbound-candidate or wrap-up-outcome artifact";
|
|
116351
|
+
}
|
|
116352
|
+
}
|
|
116247
116353
|
function renderSection(title, items, describe) {
|
|
116248
116354
|
const lines = [`== ${title} ==`];
|
|
116249
116355
|
if (items.length === 0)
|
package/dist/core.js
CHANGED
|
@@ -20387,10 +20387,12 @@ __export(exports_service9, {
|
|
|
20387
20387
|
normalizeCollaborationResult: () => normalizeCollaborationResult,
|
|
20388
20388
|
normalizeFwkResult: () => normalizeFwkResult,
|
|
20389
20389
|
normalizeProposalLifecycleResult: () => normalizeProposalLifecycleResult,
|
|
20390
|
+
normalizeWorkspaceReference: () => normalizeWorkspaceReference,
|
|
20390
20391
|
proposalNotePath: () => proposalNotePath,
|
|
20391
20392
|
readExternalSlate: () => readExternalSlate,
|
|
20392
20393
|
reclaimStaleExternalSlates: () => reclaimStaleExternalSlates,
|
|
20393
20394
|
redactSensitiveText: () => redactSensitiveText,
|
|
20395
|
+
requireWorkspaceReference: () => requireWorkspaceReference,
|
|
20394
20396
|
resolveOrCreateWorkspace: () => resolveOrCreateWorkspace,
|
|
20395
20397
|
resolvePolicyExperiment: () => resolvePolicyExperiment,
|
|
20396
20398
|
rollbackPolicyExperiment: () => rollbackPolicyExperiment,
|
|
@@ -20401,8 +20403,50 @@ __export(exports_service9, {
|
|
|
20401
20403
|
});
|
|
20402
20404
|
|
|
20403
20405
|
// src/sac/fwk-service.ts
|
|
20404
|
-
init_assembly();
|
|
20405
20406
|
import { createHash as createHash15, randomUUID as randomUUID4 } from "crypto";
|
|
20407
|
+
|
|
20408
|
+
// src/sac/workspace-reference.ts
|
|
20409
|
+
var WORKSPACE_REFERENCE_PATTERN = /^\.\/(?!(?:.*\/)?\.\.(?:\/|$))(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+$/;
|
|
20410
|
+
var UNSAFE_ABSOLUTE = /^[/\\]/;
|
|
20411
|
+
var UNSAFE_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
20412
|
+
function requireWorkspaceReference(raw) {
|
|
20413
|
+
const normalized = normalizeWorkspaceReference(raw);
|
|
20414
|
+
if (!normalized.ok)
|
|
20415
|
+
throw new Error(normalized.reason);
|
|
20416
|
+
return normalized.uri;
|
|
20417
|
+
}
|
|
20418
|
+
function normalizeWorkspaceReference(raw) {
|
|
20419
|
+
const trimmed = raw.trim();
|
|
20420
|
+
if (trimmed.length === 0) {
|
|
20421
|
+
return { ok: false, reason: 'a workspace reference must be a non-empty workspace-relative path, e.g. "./src/harness/search"' };
|
|
20422
|
+
}
|
|
20423
|
+
if (UNSAFE_ABSOLUTE.test(trimmed) || UNSAFE_SCHEME.test(trimmed) || trimmed.includes("\\")) {
|
|
20424
|
+
return {
|
|
20425
|
+
ok: false,
|
|
20426
|
+
reason: `"${trimmed}" is not a workspace-relative path \u2014 an absolute path, a URL and a Windows path are all refused. Use a path inside the project, e.g. "./src/harness/search".`
|
|
20427
|
+
};
|
|
20428
|
+
}
|
|
20429
|
+
const withoutDotSlash = trimmed.startsWith("./") ? trimmed.slice(2) : trimmed;
|
|
20430
|
+
const withoutTrailingSlash = withoutDotSlash.replace(/\/+$/, "");
|
|
20431
|
+
if (withoutTrailingSlash.length === 0) {
|
|
20432
|
+
return { ok: false, reason: `"${trimmed}" names the project root rather than a path inside it \u2014 name a directory or file, e.g. "./src/harness/search".` };
|
|
20433
|
+
}
|
|
20434
|
+
const segments = withoutTrailingSlash.split("/");
|
|
20435
|
+
if (segments.includes("..") || segments.includes(".")) {
|
|
20436
|
+
return { ok: false, reason: `"${trimmed}" contains a "." or ".." path segment \u2014 a workspace reference must stay inside the project.` };
|
|
20437
|
+
}
|
|
20438
|
+
const uri = `./${withoutTrailingSlash}`;
|
|
20439
|
+
if (!WORKSPACE_REFERENCE_PATTERN.test(uri)) {
|
|
20440
|
+
return {
|
|
20441
|
+
ok: false,
|
|
20442
|
+
reason: `"${trimmed}" is not a usable workspace reference. The stored form is "${uri}" if the characters are the problem: ` + 'each path segment may contain only letters, digits, ".", "_" and "-", and no ".." segment is allowed.'
|
|
20443
|
+
};
|
|
20444
|
+
}
|
|
20445
|
+
return { ok: true, uri };
|
|
20446
|
+
}
|
|
20447
|
+
|
|
20448
|
+
// src/sac/fwk-service.ts
|
|
20449
|
+
init_assembly();
|
|
20406
20450
|
import { appendFile as appendFile5, mkdir as mkdir28, open as open2, readFile as readFile40, rename as rename6, rm as rm5, stat as stat9, writeFile as writeFile21 } from "fs/promises";
|
|
20407
20451
|
import path78 from "path";
|
|
20408
20452
|
|
|
@@ -20415,7 +20459,7 @@ var idPattern = /^[a-z][a-z0-9-]{2,63}$/;
|
|
|
20415
20459
|
var subjectPattern = /^(?:user|team|service|agent):[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
20416
20460
|
var revisionPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/;
|
|
20417
20461
|
var correlationPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{15,255}$/;
|
|
20418
|
-
var workspacePathPattern =
|
|
20462
|
+
var workspacePathPattern = WORKSPACE_REFERENCE_PATTERN;
|
|
20419
20463
|
var receiptHashPattern = /^[a-f0-9]{64}$/;
|
|
20420
20464
|
var utcPattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?Z$/;
|
|
20421
20465
|
var forbiddenPayloadKeys = new Set(["prompt", "transcript", "hiddenReasoning", "secret", "secrets", "rawContent"]);
|
|
@@ -21661,7 +21705,7 @@ var stableJson2 = (value) => JSON.stringify(stableValue(value));
|
|
|
21661
21705
|
var sha2562 = (value) => createHash14("sha256").update(stableJson2(value), "utf8").digest("hex");
|
|
21662
21706
|
var sha256Content = (value) => createHash14("sha256").update(value).digest("hex");
|
|
21663
21707
|
var pseudonym = (key, value) => createHmac2("sha256", key).update(value, "utf8").digest("hex");
|
|
21664
|
-
var workspacePathPattern2 =
|
|
21708
|
+
var workspacePathPattern2 = WORKSPACE_REFERENCE_PATTERN;
|
|
21665
21709
|
var trustedOutcomes = new WeakMap;
|
|
21666
21710
|
var trustedSandboxEvidence = new WeakMap;
|
|
21667
21711
|
var trustedSandboxExecutions = new WeakMap;
|
|
@@ -22138,7 +22182,7 @@ var metadataOnly = (value) => {
|
|
|
22138
22182
|
var nowIso = (now) => now().toISOString();
|
|
22139
22183
|
var reportHashPattern = /^[a-f0-9]{64}$/;
|
|
22140
22184
|
var immutableVersionPattern = /(?:^|[-_.:])(?:latest|next|head|main|develop)(?:$|[-_.:])/i;
|
|
22141
|
-
var workspacePathPattern3 =
|
|
22185
|
+
var workspacePathPattern3 = WORKSPACE_REFERENCE_PATTERN;
|
|
22142
22186
|
var policyExperimentConfigPath = [".metaproject", "context-operations", "policy-experiment", "config.json"];
|
|
22143
22187
|
var stableValue2 = (value) => {
|
|
22144
22188
|
if (Array.isArray(value))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.131",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|