@mrciphersmith/keryx 0.2.128 → 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 +333 -89
- 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
|
}
|
|
@@ -85129,6 +85177,8 @@ function busInboxFullNotice(droppedTotal) {
|
|
|
85129
85177
|
return `bus: inbox full \u2014 ${droppedTotal} older message(s) dropped
|
|
85130
85178
|
`;
|
|
85131
85179
|
}
|
|
85180
|
+
var BUS_WAKE_CAPPED_NOTICE = `\u25C7 a peer message arrived; automatic wakes are capped, so it will be delivered with your next message.
|
|
85181
|
+
`;
|
|
85132
85182
|
function createLeaseHoldController(opts) {
|
|
85133
85183
|
let wasHeld = false;
|
|
85134
85184
|
return {
|
|
@@ -86977,7 +87027,8 @@ class SandboxedWebTransport {
|
|
|
86977
87027
|
...request.body !== undefined ? { body: request.body } : {},
|
|
86978
87028
|
...request.credential !== undefined ? { credential: request.credential } : {},
|
|
86979
87029
|
...request.signal !== undefined ? { signal: request.signal } : {},
|
|
86980
|
-
localOnly: request.capability === "local-search"
|
|
87030
|
+
localOnly: request.capability === "local-search",
|
|
87031
|
+
browserHeaders: request.capability === "public-search"
|
|
86981
87032
|
});
|
|
86982
87033
|
if (!raw.ok) {
|
|
86983
87034
|
return {
|
|
@@ -87024,7 +87075,8 @@ class SandboxedWebTransport {
|
|
|
87024
87075
|
address: target.value.address,
|
|
87025
87076
|
method: request.method,
|
|
87026
87077
|
...request.body !== undefined ? { body: request.body } : {},
|
|
87027
|
-
...request.credential !== undefined ? { credential: request.credential } : {}
|
|
87078
|
+
...request.credential !== undefined ? { credential: request.credential } : {},
|
|
87079
|
+
...request.browserHeaders === true ? { browserHeaders: true } : {}
|
|
87028
87080
|
}, request.signal);
|
|
87029
87081
|
if (!worker.ok)
|
|
87030
87082
|
return worker;
|
|
@@ -87062,6 +87114,54 @@ import { existsSync as existsSync29 } from "fs";
|
|
|
87062
87114
|
import { homedir as homedir7 } from "os";
|
|
87063
87115
|
var WORKER_TIMEOUT_MS = 1e4;
|
|
87064
87116
|
var MAX_WORKER_OUTPUT_BYTES = 192000;
|
|
87117
|
+
var BROWSER_USER_AGENTS = [
|
|
87118
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
|
87119
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
|
87120
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
|
|
87121
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0",
|
|
87122
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0",
|
|
87123
|
+
"Mozilla/5.0 (X11; Linux x86_64; rv:132.0) Gecko/20100101 Firefox/132.0",
|
|
87124
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Safari/605.1.15",
|
|
87125
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0"
|
|
87126
|
+
];
|
|
87127
|
+
var BROWSER_ACCEPT_LANGUAGES = [
|
|
87128
|
+
"en-US,en;q=0.9",
|
|
87129
|
+
"en-US,en;q=0.9,ru;q=0.8",
|
|
87130
|
+
"en-GB,en;q=0.9,en-US;q=0.8",
|
|
87131
|
+
"ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
|
|
87132
|
+
"en-CA,en;q=0.9,en-US;q=0.8"
|
|
87133
|
+
];
|
|
87134
|
+
var BROWSER_ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
|
|
87135
|
+
var BROWSER_STATIC_HEADERS = {
|
|
87136
|
+
"accept-encoding": "identity",
|
|
87137
|
+
connection: "keep-alive",
|
|
87138
|
+
"upgrade-insecure-requests": "1",
|
|
87139
|
+
"sec-fetch-dest": "document",
|
|
87140
|
+
"sec-fetch-mode": "navigate",
|
|
87141
|
+
"sec-fetch-site": "none",
|
|
87142
|
+
"sec-fetch-user": "?1",
|
|
87143
|
+
"cache-control": "max-age=0"
|
|
87144
|
+
};
|
|
87145
|
+
var BROWSER_HEADERS_SOURCE = [
|
|
87146
|
+
"(function (random) {",
|
|
87147
|
+
" var userAgents = " + JSON.stringify(BROWSER_USER_AGENTS) + ";",
|
|
87148
|
+
" var acceptLanguages = " + JSON.stringify(BROWSER_ACCEPT_LANGUAGES) + ";",
|
|
87149
|
+
" var staticHeaders = " + JSON.stringify(BROWSER_STATIC_HEADERS) + ";",
|
|
87150
|
+
" var pick = function (list) {",
|
|
87151
|
+
" var value = list[Math.floor(random() * list.length)];",
|
|
87152
|
+
" return value === undefined ? list[0] : value;",
|
|
87153
|
+
" };",
|
|
87154
|
+
" var headers = {",
|
|
87155
|
+
" accept: " + JSON.stringify(BROWSER_ACCEPT) + ",",
|
|
87156
|
+
' "accept-language": pick(acceptLanguages),',
|
|
87157
|
+
' "user-agent": pick(userAgents),',
|
|
87158
|
+
" };",
|
|
87159
|
+
" for (var name in staticHeaders) { headers[name] = staticHeaders[name]; }",
|
|
87160
|
+
' if (random() < 0.5) headers.dnt = "1";',
|
|
87161
|
+
" return headers;",
|
|
87162
|
+
"})"
|
|
87163
|
+
].join(`
|
|
87164
|
+
`);
|
|
87065
87165
|
var WORKER_SOURCE = String.raw`
|
|
87066
87166
|
const { request: httpsRequest } = await import("node:https");
|
|
87067
87167
|
const { request: httpRequest } = await import("node:http");
|
|
@@ -87070,7 +87170,10 @@ const input = JSON.parse(await Bun.stdin.text());
|
|
|
87070
87170
|
const fail = () => process.stdout.write(JSON.stringify({ ok: false, reason: "request failed or timed out" }));
|
|
87071
87171
|
try {
|
|
87072
87172
|
const timer = setTimeout(() => req.destroy(new Error("timeout")), input.timeoutMs);
|
|
87073
|
-
const
|
|
87173
|
+
const browserHeaders = ${BROWSER_HEADERS_SOURCE};
|
|
87174
|
+
const headers = input.browserHeaders
|
|
87175
|
+
? browserHeaders(Math.random)
|
|
87176
|
+
: { accept: "text/html, text/plain, application/json, application/xml, application/xhtml+xml" };
|
|
87074
87177
|
const payload = input.body && typeof input.body === "object" ? { ...input.body } : undefined;
|
|
87075
87178
|
if (input.credential && input.credential.injection === "header") headers[input.credential.name] = input.credential.value;
|
|
87076
87179
|
if (input.credential && input.credential.injection === "json-body") {
|
|
@@ -87294,7 +87397,7 @@ function webSearchTool(service) {
|
|
|
87294
87397
|
const response = await service.search(input.query.trim());
|
|
87295
87398
|
if (!response.ok) {
|
|
87296
87399
|
return {
|
|
87297
|
-
output: response.reason === "no-active-provider" ? "web_search: no active connected provider. Use /search-provider to configure one, test it, then use /search-connect to select it." : response.reason === "search-failed" ?
|
|
87400
|
+
output: response.reason === "no-active-provider" ? "web_search: no active connected provider. Use /search-provider to configure one, test it, then use /search-connect to select it." : response.reason === "search-failed" ? `web_search: search failed${response.detail !== undefined && response.detail.length > 0 ? ` (${response.detail})` : ""}. Wait a few minutes before trying again, and do not switch providers yourself.` : "web_search: active provider is unavailable; reconnect it with /search-provider before retrying.",
|
|
87298
87401
|
isError: true
|
|
87299
87402
|
};
|
|
87300
87403
|
}
|
|
@@ -87412,7 +87515,12 @@ function workspaceCreateTool(cwd, getSessionDir) {
|
|
|
87412
87515
|
const title = typeof input.title === "string" ? input.title.trim() : "";
|
|
87413
87516
|
if (title.length === 0)
|
|
87414
87517
|
return { output: "workspace_create requires a non-empty 'title'", isError: true };
|
|
87415
|
-
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;
|
|
87416
87524
|
try {
|
|
87417
87525
|
const workspace = await service5(cwd).create({
|
|
87418
87526
|
request: undefined,
|
|
@@ -88096,7 +88204,7 @@ function rememberExactShellGrant(command, sessionAllow, options) {
|
|
|
88096
88204
|
if (!offerExact) {
|
|
88097
88205
|
return "";
|
|
88098
88206
|
}
|
|
88099
|
-
const stored = allowShellPattern(exact);
|
|
88207
|
+
const stored = allowShellPattern(exact, options?.dir);
|
|
88100
88208
|
if (stored.length > 0) {
|
|
88101
88209
|
sessionAllow.add(stored);
|
|
88102
88210
|
}
|
|
@@ -88250,8 +88358,14 @@ var ANOMALY_MARKERS = [
|
|
|
88250
88358
|
"/anomaly.js",
|
|
88251
88359
|
"Unfortunately, bots use DuckDuckGo too"
|
|
88252
88360
|
];
|
|
88253
|
-
var MIN_GAP_MS =
|
|
88254
|
-
var MAX_GAP_MS =
|
|
88361
|
+
var MIN_GAP_MS = 4000;
|
|
88362
|
+
var MAX_GAP_MS = 8000;
|
|
88363
|
+
var DUCKDUCKGO_ANOMALY_BACKOFF_MS = [5000, 20000];
|
|
88364
|
+
var timing = {
|
|
88365
|
+
minGapMs: MIN_GAP_MS,
|
|
88366
|
+
maxGapMs: MAX_GAP_MS,
|
|
88367
|
+
backoffMs: DUCKDUCKGO_ANOMALY_BACKOFF_MS
|
|
88368
|
+
};
|
|
88255
88369
|
var lastSearchAt = 0;
|
|
88256
88370
|
function duckduckgoLiteUrl(query) {
|
|
88257
88371
|
return `${DUCKDUCKGO_LITE_ORIGIN}?q=${encodeURIComponent(query)}`;
|
|
@@ -88267,7 +88381,8 @@ async function maybeDelayDuckDuckGoSearch(signal) {
|
|
|
88267
88381
|
lastSearchAt = now;
|
|
88268
88382
|
return;
|
|
88269
88383
|
}
|
|
88270
|
-
const
|
|
88384
|
+
const { minGapMs, maxGapMs } = timing;
|
|
88385
|
+
const gap = minGapMs + Math.floor(Math.random() * Math.max(0, maxGapMs - minGapMs + 1));
|
|
88271
88386
|
const wait = gap - (now - lastSearchAt);
|
|
88272
88387
|
if (wait > 0)
|
|
88273
88388
|
await sleep2(wait, signal);
|
|
@@ -88387,6 +88502,30 @@ function sleep2(ms, signal) {
|
|
|
88387
88502
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
88388
88503
|
});
|
|
88389
88504
|
}
|
|
88505
|
+
async function waitDuckDuckGoBackoff(attempt, signal) {
|
|
88506
|
+
const wait = timing.backoffMs[attempt];
|
|
88507
|
+
if (wait === undefined)
|
|
88508
|
+
return false;
|
|
88509
|
+
if (wait > 0)
|
|
88510
|
+
await sleep2(wait, signal);
|
|
88511
|
+
return true;
|
|
88512
|
+
}
|
|
88513
|
+
|
|
88514
|
+
// src/harness/search/connection-message.ts
|
|
88515
|
+
function describeConnectionFailure(reason) {
|
|
88516
|
+
switch (reason) {
|
|
88517
|
+
case "missing-credential":
|
|
88518
|
+
return "missing credential";
|
|
88519
|
+
case "rate-limited":
|
|
88520
|
+
return "rate limited \u2014 the provider served its bot page to this machine; retrying will not help";
|
|
88521
|
+
case "incompatible-response":
|
|
88522
|
+
return "the endpoint answered, but not with search results";
|
|
88523
|
+
case "transport-failed":
|
|
88524
|
+
default:
|
|
88525
|
+
return "connection validation failed";
|
|
88526
|
+
}
|
|
88527
|
+
}
|
|
88528
|
+
var RATE_LIMITED_SEARCH_ERROR = "rate limited: DuckDuckGo served its bot-check page instead of results, even after retrying. Do not retry or rephrase; wait a few minutes or fetch a known URL directly";
|
|
88390
88529
|
|
|
88391
88530
|
// src/harness/search/registry.ts
|
|
88392
88531
|
var SEARXNG_DOCS_URL = "https://docs.searxng.org/admin/installation.html";
|
|
@@ -88497,29 +88636,31 @@ function createSearchProviderRegistry(transport, resolveCredential = () => {
|
|
|
88497
88636
|
query: DUCKDUCKGO_HEALTHCHECK_QUERY
|
|
88498
88637
|
});
|
|
88499
88638
|
if (isDuckDuckGoAnomaly(response.status, response.text))
|
|
88500
|
-
return { ok: false, reason: "
|
|
88639
|
+
return { ok: false, reason: "rate-limited" };
|
|
88501
88640
|
if (!response.ok || response.status < 200 || response.status >= 300)
|
|
88502
88641
|
return requestFailure(response);
|
|
88503
88642
|
const parsed = duckduckgoSearchResponse(DUCKDUCKGO_HEALTHCHECK_QUERY, response.text);
|
|
88504
|
-
return parsed.ok ? { ok: true } : { ok: false, reason: "
|
|
88643
|
+
return parsed.ok ? { ok: true } : { ok: false, reason: "rate-limited" };
|
|
88505
88644
|
},
|
|
88506
88645
|
async search(_fields, query, signal) {
|
|
88507
|
-
|
|
88508
|
-
|
|
88509
|
-
|
|
88510
|
-
|
|
88511
|
-
|
|
88512
|
-
|
|
88513
|
-
|
|
88514
|
-
|
|
88515
|
-
|
|
88516
|
-
|
|
88517
|
-
|
|
88646
|
+
for (let attempt = 0;; attempt += 1) {
|
|
88647
|
+
await maybeDelayDuckDuckGoSearch(signal);
|
|
88648
|
+
const response = await transport.request({
|
|
88649
|
+
providerId: "duckduckgo",
|
|
88650
|
+
capability: "public-search",
|
|
88651
|
+
url: duckduckgoLiteUrl(query),
|
|
88652
|
+
method: "GET",
|
|
88653
|
+
query,
|
|
88654
|
+
...signal ? { signal } : {}
|
|
88655
|
+
});
|
|
88656
|
+
const parsed = duckduckgoSearchResponse(query, response.text);
|
|
88657
|
+
if (!isDuckDuckGoAnomaly(response.status, response.text) && parsed.ok) {
|
|
88658
|
+
return parsed.value;
|
|
88659
|
+
}
|
|
88660
|
+
if (!await waitDuckDuckGoBackoff(attempt, signal)) {
|
|
88661
|
+
throw new Error(RATE_LIMITED_SEARCH_ERROR);
|
|
88662
|
+
}
|
|
88518
88663
|
}
|
|
88519
|
-
const parsed = duckduckgoSearchResponse(query, response.text);
|
|
88520
|
-
if (!parsed.ok)
|
|
88521
|
-
throw new Error("DuckDuckGo rate-limited this machine");
|
|
88522
|
-
return parsed.value;
|
|
88523
88664
|
}
|
|
88524
88665
|
};
|
|
88525
88666
|
const searxng = {
|
|
@@ -88751,8 +88892,12 @@ class SearchProviderController {
|
|
|
88751
88892
|
return { ok: false, reason: "no-active-provider" };
|
|
88752
88893
|
try {
|
|
88753
88894
|
return { ok: true, value: await descriptor.search(stored?.fields ?? {}, query, signal) };
|
|
88754
|
-
} catch {
|
|
88755
|
-
return {
|
|
88895
|
+
} catch (error) {
|
|
88896
|
+
return {
|
|
88897
|
+
ok: false,
|
|
88898
|
+
reason: "search-failed",
|
|
88899
|
+
...error instanceof Error && error.message.length > 0 ? { detail: error.message } : {}
|
|
88900
|
+
};
|
|
88756
88901
|
}
|
|
88757
88902
|
}
|
|
88758
88903
|
config() {
|
|
@@ -93181,14 +93326,25 @@ async function classifySession(session) {
|
|
|
93181
93326
|
sessionId: session.id,
|
|
93182
93327
|
...workspaceId !== undefined ? { workspaceId } : {},
|
|
93183
93328
|
lastSeenAt: session.updatedAt,
|
|
93329
|
+
reason: "wrap-up-failed",
|
|
93184
93330
|
wrapUpOutcome: { trigger: wrapUpOutcome.trigger, generatedAt: wrapUpOutcome.generatedAt, groups: wrapUpOutcome.groups }
|
|
93185
93331
|
}
|
|
93186
93332
|
};
|
|
93187
93333
|
}
|
|
93188
93334
|
if (!await isSlateEngaged(dir))
|
|
93189
93335
|
return;
|
|
93190
|
-
const
|
|
93191
|
-
|
|
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
|
+
};
|
|
93192
93348
|
}
|
|
93193
93349
|
async function readExternalUnboundCandidates(cwd) {
|
|
93194
93350
|
const extDir = externalSlatesDir(cwd);
|
|
@@ -93441,6 +93597,17 @@ async function isSlateEngaged(dir) {
|
|
|
93441
93597
|
return false;
|
|
93442
93598
|
}
|
|
93443
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
|
+
}
|
|
93444
93611
|
async function safeReadSlate(dir) {
|
|
93445
93612
|
try {
|
|
93446
93613
|
return await readSlate(dir);
|
|
@@ -94114,6 +94281,15 @@ function mountEmptyTranscriptSplash(otui, r, transcript) {
|
|
|
94114
94281
|
transcript.remove(box);
|
|
94115
94282
|
};
|
|
94116
94283
|
}
|
|
94284
|
+
function createSplashLifecycle(opts) {
|
|
94285
|
+
let remove = opts.initialHistoryLength === 0 ? opts.mount() : undefined;
|
|
94286
|
+
return {
|
|
94287
|
+
removeIfShown() {
|
|
94288
|
+
remove?.();
|
|
94289
|
+
remove = undefined;
|
|
94290
|
+
}
|
|
94291
|
+
};
|
|
94292
|
+
}
|
|
94117
94293
|
|
|
94118
94294
|
// src/tui/workspace-inspector.ts
|
|
94119
94295
|
var WORKSPACE_COMMAND = "/workspace";
|
|
@@ -94397,7 +94573,7 @@ function summarizeReviewItem(item) {
|
|
|
94397
94573
|
case "unbound-candidate":
|
|
94398
94574
|
return `${item.sessionId} \u2014 ${item.summary}`;
|
|
94399
94575
|
case "unknown":
|
|
94400
|
-
return `${item.sessionId} \u2014 last seen ${item.lastSeenAt}`;
|
|
94576
|
+
return `${item.sessionId} \u2014 ${unknownReasonLabel(item.reason)} (last seen ${item.lastSeenAt})`;
|
|
94401
94577
|
}
|
|
94402
94578
|
}
|
|
94403
94579
|
function formatReviewListLines(items, selected) {
|
|
@@ -94409,6 +94585,26 @@ function formatReviewListLines(items, selected) {
|
|
|
94409
94585
|
return `${mark} ${TYPE_LABEL[item.type].padEnd(8)} ${summarizeReviewItem(item)}`;
|
|
94410
94586
|
});
|
|
94411
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
|
+
}
|
|
94412
94608
|
function describeReviewItem(item) {
|
|
94413
94609
|
switch (item.type) {
|
|
94414
94610
|
case "proposal":
|
|
@@ -94450,7 +94646,7 @@ function describeReviewItem(item) {
|
|
|
94450
94646
|
] : [
|
|
94451
94647
|
`Session ${item.sessionId}${item.workspaceId !== undefined ? ` (workspace ${item.workspaceId})` : ""}`,
|
|
94452
94648
|
`Last seen ${item.lastSeenAt}`,
|
|
94453
|
-
"
|
|
94649
|
+
"Why unknown: " + unknownReasonDetail(item.reason),
|
|
94454
94650
|
"",
|
|
94455
94651
|
`Investigate: keryx sessions list / keryx shell -r ${item.sessionId}`
|
|
94456
94652
|
];
|
|
@@ -101903,6 +102099,44 @@ function parseBusCommand(line) {
|
|
|
101903
102099
|
return { kind: "error", reason: `unknown /bus subcommand "${first}"` };
|
|
101904
102100
|
}
|
|
101905
102101
|
|
|
102102
|
+
// src/tui/bus-join.ts
|
|
102103
|
+
function decideJoinAdoption(input) {
|
|
102104
|
+
if (input.disabled)
|
|
102105
|
+
return "off";
|
|
102106
|
+
if (input.destroyed)
|
|
102107
|
+
return "leave";
|
|
102108
|
+
return "adopt";
|
|
102109
|
+
}
|
|
102110
|
+
function buildBusJoinCallbacks(deps) {
|
|
102111
|
+
return {
|
|
102112
|
+
onEvent: (event) => {
|
|
102113
|
+
if (deps.isDestroyed())
|
|
102114
|
+
return;
|
|
102115
|
+
deps.onSystemLine(`${formatBusEventLine(event)}
|
|
102116
|
+
`);
|
|
102117
|
+
deps.pushToInbox({ ...event, body: event.body ?? "" });
|
|
102118
|
+
deps.markDelivered();
|
|
102119
|
+
},
|
|
102120
|
+
onPeers: (peers) => {
|
|
102121
|
+
if (deps.isDestroyed())
|
|
102122
|
+
return;
|
|
102123
|
+
deps.setFleetPeers(peers.map((peer) => ({
|
|
102124
|
+
name: peer.record.name,
|
|
102125
|
+
state: peer.state,
|
|
102126
|
+
status: peer.record.status,
|
|
102127
|
+
activity: peer.record.activity
|
|
102128
|
+
})));
|
|
102129
|
+
deps.paintFleet();
|
|
102130
|
+
deps.onInboxSizeObserved();
|
|
102131
|
+
deps.paintHoldBanner();
|
|
102132
|
+
deps.onLeaseHoldPoll();
|
|
102133
|
+
const delivered = deps.getDelivered();
|
|
102134
|
+
deps.onBusWakePoll(delivered);
|
|
102135
|
+
deps.resetDelivered();
|
|
102136
|
+
}
|
|
102137
|
+
};
|
|
102138
|
+
}
|
|
102139
|
+
|
|
101906
102140
|
// src/tui/shell-exit.ts
|
|
101907
102141
|
function leaveBusThenRelease(holder) {
|
|
101908
102142
|
holder.leaveBus();
|
|
@@ -102090,6 +102324,9 @@ var SIDE_WORKER_DENIED_TOOL_NAMES = new Set([
|
|
|
102090
102324
|
"shell_job_output",
|
|
102091
102325
|
"bus_send"
|
|
102092
102326
|
]);
|
|
102327
|
+
function isToolAvailableToSideWorker(tool) {
|
|
102328
|
+
return tool.definition.risk === "read" && !SIDE_WORKER_DENIED_TOOL_NAMES.has(tool.definition.name);
|
|
102329
|
+
}
|
|
102093
102330
|
function parseGitHubRemote(remote) {
|
|
102094
102331
|
const trimmed = remote.trim();
|
|
102095
102332
|
if (trimmed.length === 0) {
|
|
@@ -103057,7 +103294,7 @@ function runSearchProviderTestStep(otui, target, controller, provider, fields, c
|
|
|
103057
103294
|
}
|
|
103058
103295
|
if (!tested.ok) {
|
|
103059
103296
|
settled = "failure";
|
|
103060
|
-
const reason = tested.reason
|
|
103297
|
+
const reason = describeConnectionFailure(tested.reason);
|
|
103061
103298
|
status.content = otui.t`${otui.red("\u2717")} ${otui.bold(`'${provider.id}' test failed: ${reason}`)} ${otui.dim("(Esc to go back and retry)")}`;
|
|
103062
103299
|
return;
|
|
103063
103300
|
}
|
|
@@ -104521,11 +104758,10 @@ async function launchTuiAgentShell(opts) {
|
|
|
104521
104758
|
};
|
|
104522
104759
|
deps = { ...deps, onContextCompaction };
|
|
104523
104760
|
liveDeps = deps;
|
|
104524
|
-
let
|
|
104761
|
+
let splash = undefined;
|
|
104525
104762
|
const applyOpened = (opened, previewHistory) => {
|
|
104526
104763
|
if (opened.history.length > 0) {
|
|
104527
|
-
|
|
104528
|
-
removeSplash = undefined;
|
|
104764
|
+
splash?.removeIfShown();
|
|
104529
104765
|
}
|
|
104530
104766
|
liveSession = opened.handle;
|
|
104531
104767
|
history = previewHistory === true ? opened.history.slice(-SESSION_PREVIEW_MESSAGE_COUNT) : opened.history;
|
|
@@ -104683,17 +104919,37 @@ async function launchTuiAgentShell(opts) {
|
|
|
104683
104919
|
return true;
|
|
104684
104920
|
}
|
|
104685
104921
|
bindSlateToLiveSession();
|
|
104686
|
-
|
|
104687
|
-
|
|
104688
|
-
|
|
104922
|
+
splash = createSplashLifecycle({
|
|
104923
|
+
mount: () => mountEmptyTranscriptSplash(otui, r, transcript),
|
|
104924
|
+
initialHistoryLength: history.length
|
|
104925
|
+
});
|
|
104689
104926
|
if (viewedReadOnly) {
|
|
104690
|
-
|
|
104691
|
-
removeSplash = undefined;
|
|
104927
|
+
splash.removeIfShown();
|
|
104692
104928
|
}
|
|
104693
104929
|
refreshWorkspaceSidebar();
|
|
104694
104930
|
refreshReviewSidebar();
|
|
104695
104931
|
(async () => {
|
|
104696
104932
|
try {
|
|
104933
|
+
const { onEvent, onPeers } = buildBusJoinCallbacks({
|
|
104934
|
+
isDestroyed: () => destroyed,
|
|
104935
|
+
onSystemLine: (line) => io.onSystem?.(line),
|
|
104936
|
+
pushToInbox: (event) => busInbox.push(event),
|
|
104937
|
+
markDelivered: () => {
|
|
104938
|
+
busPollDeliveredEvent = true;
|
|
104939
|
+
},
|
|
104940
|
+
setFleetPeers: (peers) => {
|
|
104941
|
+
busFleetPeers = peers;
|
|
104942
|
+
},
|
|
104943
|
+
paintFleet,
|
|
104944
|
+
onInboxSizeObserved: () => busDropNotifier.onInboxSizeObserved(busInbox.size),
|
|
104945
|
+
paintHoldBanner,
|
|
104946
|
+
onLeaseHoldPoll: () => leaseHoldController?.onPoll(),
|
|
104947
|
+
onBusWakePoll: (delivered) => busWakeController?.onPoll(delivered),
|
|
104948
|
+
getDelivered: () => busPollDeliveredEvent,
|
|
104949
|
+
resetDelivered: () => {
|
|
104950
|
+
busPollDeliveredEvent = false;
|
|
104951
|
+
}
|
|
104952
|
+
});
|
|
104697
104953
|
const joined = await joinBus({
|
|
104698
104954
|
cwd: sessionCwd,
|
|
104699
104955
|
sessionId: liveSession.summary.id,
|
|
@@ -104708,37 +104964,15 @@ async function launchTuiAgentShell(opts) {
|
|
|
104708
104964
|
}),
|
|
104709
104965
|
onError: makeBusErrorReporter((line) => io.onSystem?.(`${line}
|
|
104710
104966
|
`)),
|
|
104711
|
-
onEvent
|
|
104712
|
-
|
|
104713
|
-
return;
|
|
104714
|
-
io.onSystem?.(`${formatBusEventLine(event)}
|
|
104715
|
-
`);
|
|
104716
|
-
busInbox.push({ ...event, body: event.body ?? "" });
|
|
104717
|
-
busPollDeliveredEvent = true;
|
|
104718
|
-
},
|
|
104719
|
-
onPeers: (peers) => {
|
|
104720
|
-
if (destroyed)
|
|
104721
|
-
return;
|
|
104722
|
-
busFleetPeers = peers.map((peer) => ({
|
|
104723
|
-
name: peer.record.name,
|
|
104724
|
-
state: peer.state,
|
|
104725
|
-
status: peer.record.status,
|
|
104726
|
-
activity: peer.record.activity
|
|
104727
|
-
}));
|
|
104728
|
-
paintFleet();
|
|
104729
|
-
busDropNotifier.onInboxSizeObserved(busInbox.size);
|
|
104730
|
-
paintHoldBanner();
|
|
104731
|
-
leaseHoldController?.onPoll();
|
|
104732
|
-
busWakeController?.onPoll(busPollDeliveredEvent);
|
|
104733
|
-
busPollDeliveredEvent = false;
|
|
104734
|
-
}
|
|
104967
|
+
onEvent,
|
|
104968
|
+
onPeers
|
|
104735
104969
|
});
|
|
104736
104970
|
if ("disabled" in joined) {
|
|
104737
104971
|
io.onSystem?.(`bus: off (${joined.disabled})
|
|
104738
104972
|
`);
|
|
104739
104973
|
return;
|
|
104740
104974
|
}
|
|
104741
|
-
if (destroyed) {
|
|
104975
|
+
if (decideJoinAdoption({ destroyed, disabled: false }) === "leave") {
|
|
104742
104976
|
joined.leave();
|
|
104743
104977
|
return;
|
|
104744
104978
|
}
|
|
@@ -105560,7 +105794,7 @@ Staying in the current session.
|
|
|
105560
105794
|
let answer = "";
|
|
105561
105795
|
try {
|
|
105562
105796
|
const base = await opts.makeAgentDeps(currentSel, liveSlateSession, undefined);
|
|
105563
|
-
const tools = base.tools.filter(
|
|
105797
|
+
const tools = base.tools.filter(isToolAvailableToSideWorker);
|
|
105564
105798
|
const sideDeps = {
|
|
105565
105799
|
...base,
|
|
105566
105800
|
tools,
|
|
@@ -105636,8 +105870,7 @@ Staying in the current session.
|
|
|
105636
105870
|
suggestionGate.cancel("new turn started");
|
|
105637
105871
|
if (origin === "operator") {
|
|
105638
105872
|
consecutiveAutoWakes = 0;
|
|
105639
|
-
|
|
105640
|
-
removeSplash = undefined;
|
|
105873
|
+
splash?.removeIfShown();
|
|
105641
105874
|
}
|
|
105642
105875
|
const displayLine = summarizeSubmittedLine(line);
|
|
105643
105876
|
if (chrome.isBusy()) {
|
|
@@ -105988,7 +106221,7 @@ Staying in the current session.
|
|
|
105988
106221
|
searchProviderController.configure(providerId, { ...descriptor.defaults, ...args.fields }, args.credential);
|
|
105989
106222
|
const tested = await searchProviderController.test(providerId);
|
|
105990
106223
|
if (!tested.ok) {
|
|
105991
|
-
const reason = tested.reason
|
|
106224
|
+
const reason = describeConnectionFailure(tested.reason);
|
|
105992
106225
|
io.onSystem?.(`Configured '${providerId}' but it is not connected yet: ${reason}. Run /search-provider ${providerId} key=<value> to re-test.
|
|
105993
106226
|
`);
|
|
105994
106227
|
return;
|
|
@@ -106491,8 +106724,7 @@ Usage: /reasoning <${REASONING_EFFORT_LEVELS.join("|")}>
|
|
|
106491
106724
|
cap: () => resolveMaxAutoWake(),
|
|
106492
106725
|
runWake: () => runLine("", "bus-message"),
|
|
106493
106726
|
printCapped: () => {
|
|
106494
|
-
io.onSystem?.(
|
|
106495
|
-
`);
|
|
106727
|
+
io.onSystem?.(BUS_WAKE_CAPPED_NOTICE);
|
|
106496
106728
|
},
|
|
106497
106729
|
hasBusDeps: () => deps.busInbox !== undefined
|
|
106498
106730
|
});
|
|
@@ -107987,8 +108219,8 @@ async function runAgentRepl(lines, rich, deps, metaprojectPort, sessionOpts, ini
|
|
|
107987
108219
|
}
|
|
107988
108220
|
};
|
|
107989
108221
|
const searchProviderController = createDefaultSearchProviderController();
|
|
107990
|
-
const sessionShellAllow = new Set(loadShellPermissions().allow);
|
|
107991
|
-
let fingerprintAtStart = shellPermissionsFingerprint();
|
|
108222
|
+
const sessionShellAllow = new Set(loadShellPermissions(configDir).allow);
|
|
108223
|
+
let fingerprintAtStart = shellPermissionsFingerprint(configDir);
|
|
107992
108224
|
let permissionMigrationShown = false;
|
|
107993
108225
|
let permissionTamperShown = false;
|
|
107994
108226
|
const iterator = lines[Symbol.asyncIterator]();
|
|
@@ -108225,10 +108457,11 @@ ${GUTTER}${style.yellow(`Run: ${evaled.command}`)} ${style.dim(prompt)}`);
|
|
|
108225
108457
|
const approved = always || /^y(es)?$/i.test(answer);
|
|
108226
108458
|
if (always && approved) {
|
|
108227
108459
|
const stored = rememberExactShellGrant(evaled.command, sessionShellAllow, {
|
|
108228
|
-
publishLease: evaled.publishLease
|
|
108460
|
+
publishLease: evaled.publishLease,
|
|
108461
|
+
...configDir !== undefined ? { dir: configDir } : {}
|
|
108229
108462
|
});
|
|
108230
108463
|
if (stored.length > 0) {
|
|
108231
|
-
fingerprintAtStart = shellPermissionsFingerprint();
|
|
108464
|
+
fingerprintAtStart = shellPermissionsFingerprint(configDir);
|
|
108232
108465
|
}
|
|
108233
108466
|
out(stored.length > 0 ? style.green(`approved \xB7 remembered \u201C${stored}\u201D
|
|
108234
108467
|
`) : style.yellow(`approved once \xB7 pattern cannot be remembered
|
|
@@ -108825,7 +109058,7 @@ ${GUTTER}${turnSeparator()}
|
|
|
108825
109058
|
searchProviderController.configure(providerId, { ...descriptor.defaults, ...args.fields }, args.credential);
|
|
108826
109059
|
const tested = await searchProviderController.test(providerId);
|
|
108827
109060
|
if (!tested.ok) {
|
|
108828
|
-
const reason = tested.reason
|
|
109061
|
+
const reason = describeConnectionFailure(tested.reason);
|
|
108829
109062
|
agentIo.onSystem?.(`Configured '${providerId}' but it is not connected yet: ${reason}. Run /search-provider ${providerId} key=<value> to re-test.
|
|
108830
109063
|
`);
|
|
108831
109064
|
continue;
|
|
@@ -111528,8 +111761,8 @@ async function runServe(args) {
|
|
|
111528
111761
|
resolve();
|
|
111529
111762
|
});
|
|
111530
111763
|
};
|
|
111531
|
-
process.
|
|
111532
|
-
process.
|
|
111764
|
+
process.on("SIGINT", finish);
|
|
111765
|
+
process.on("SIGTERM", finish);
|
|
111533
111766
|
});
|
|
111534
111767
|
}
|
|
111535
111768
|
function overlay(stored, parsed, port, runtimeAck) {
|
|
@@ -115790,7 +116023,8 @@ async function workspaceCommand(args) {
|
|
|
115790
116023
|
if (subcommand === "create") {
|
|
115791
116024
|
rejectUnknownOptions(args.slice(1), new Set(["--title", "--component"]));
|
|
115792
116025
|
const title = optionValue(args, "--title");
|
|
115793
|
-
const
|
|
116026
|
+
const rawComponent = optionValue(args, "--component");
|
|
116027
|
+
const component = rawComponent === undefined ? undefined : requireWorkspaceReference(rawComponent);
|
|
115794
116028
|
if (!title)
|
|
115795
116029
|
throw new Error("Usage: keryx workspace create --title <title> [--component <workspace-relative-ref>]");
|
|
115796
116030
|
const workspace = await service6().create({ request: undefined, requestCorrelationId: randomUUID40(), id: newWorkspaceId(), title, ...component ? { component: { kind: "component", uri: component } } : {} });
|
|
@@ -115819,11 +116053,11 @@ async function workspaceCommand(args) {
|
|
|
115819
116053
|
rejectUnknownOptions(args.slice(2), new Set(["--kind", "--uri", "--revision"]));
|
|
115820
116054
|
const workspaceId = args[1];
|
|
115821
116055
|
const kind = optionValue(args, "--kind");
|
|
115822
|
-
const
|
|
116056
|
+
const rawUri = optionValue(args, "--uri");
|
|
115823
116057
|
const revision = optionValue(args, "--revision");
|
|
115824
|
-
if (!workspaceId || !kind || !
|
|
116058
|
+
if (!workspaceId || !kind || !rawUri)
|
|
115825
116059
|
throw new Error("Usage: keryx workspace add-resource <workspace-id> --kind <kind> --uri <workspace-relative-ref> [--revision <revision>]");
|
|
115826
|
-
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));
|
|
115827
116061
|
return;
|
|
115828
116062
|
}
|
|
115829
116063
|
if (subcommand === "archive") {
|
|
@@ -116097,7 +116331,7 @@ function renderCatchUp(report, includeLifecycleFlags = true) {
|
|
|
116097
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"}.`));
|
|
116098
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.`));
|
|
116099
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}).`));
|
|
116100
|
-
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.`));
|
|
116101
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.`));
|
|
116102
116336
|
if (includeLifecycleFlags) {
|
|
116103
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.`));
|
|
@@ -116106,6 +116340,16 @@ function renderCatchUp(report, includeLifecycleFlags = true) {
|
|
|
116106
116340
|
|
|
116107
116341
|
`);
|
|
116108
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
|
+
}
|
|
116109
116353
|
function renderSection(title, items, describe) {
|
|
116110
116354
|
const lines = [`== ${title} ==`];
|
|
116111
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": {
|