better-dsh 0.2.3-d → 0.2.3-e
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/docs/50_test-reports/2026-09-11-control-prompt-into-eval-description/345/256/236/346/265/213/346/212/245/345/221/212.md +222 -0
- package/docs/50_test-reports/2026-09-12-fs-scheme-resolution-/345/256/236/346/265/213/346/212/245/345/221/212.md +81 -0
- package/docs/50_test-reports/2026-09-12-url-schemes-grammar-matrix/344/270/216catalog-centralize-/345/256/236/346/265/213/346/212/245/345/221/212.md +234 -0
- package/docs/50_test-reports/2026-09-12-url-schemes-recallable-context-design/351/252/214/350/257/201/346/212/245/345/221/212.md +160 -0
- package/docs/50_test-reports/2026-09-12-url-schemes-recallable-context-/345/256/236/346/265/213/345/211/247/346/234/254.md +44 -0
- package/docs/50_test-reports/2026-09-12-url-schemes-recallable-context-/345/256/236/346/265/213/346/212/245/345/221/212.md +89 -0
- package/docs/50_test-reports/2026-09-12-url-schemes-/345/205/255scheme/345/206/222/347/203/237/344/270/216/350/276/271/347/225/214/345/256/236/346/265/213/346/212/245/345/221/212.md +198 -0
- package/docs/50_test-reports/2026-09-13-hashline-off/344/270/213scheme/345/217/257/350/276/276/346/200/247/345/267/245/345/205/267/351/235/242/344/270/215/345/257/271/347/247/260-/345/256/236/346/265/213/346/212/245/345/221/212.md +246 -0
- package/docs/50_test-reports/2026-09-13-preact-ui-shell/345/256/236/346/265/213/346/212/245/345/221/212.md +50 -0
- package/docs/50_test-reports/v0.2.3c-mobile-wave/345/256/236/346/265/213/346/212/245/345/221/212.md +47 -0
- package/lib/fs-aware/sandbox-plugin.d.ts +71 -0
- package/lib/fs-aware/sandbox-plugin.js +249 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +547 -705
- package/lib/wrap-DC8O3SYz.js +721 -0
- package/package.json +6 -1
- package/url-schemes-instruction.md +22 -0
package/lib/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { a as RESERVED_ERROR_MEMBERS, i as RESERVED_BINDING_GLOBALS, n as DUNDER_MEMBER, o as ReplRuntime, r as PORTABLE_RESERVED_WORDS, t as isFlatBindableName } from "./py-sdk-Chvy92MB.js";
|
|
2
2
|
import { s as resolveKernelEnv } from "./kernel-env-hxaihi9C.js";
|
|
3
|
+
import { a as createDshHandler, c as createDvcHandler, d as registerDvcDevice, f as UrlSchemesError, i as createHttpHandler, l as dispatchDvcWrite, m as SCHEME_NAMES, n as wrapFsWithSchemes, o as UrlResolver, p as parseUrl, r as HTTP_SCHEMES, s as resolveDocsDir, u as listDvcDevices } from "./wrap-DC8O3SYz.js";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
import { Context } from "@deepseek-ai/cordis";
|
|
5
6
|
import { createHmac, randomBytes, randomUUID } from "node:crypto";
|
|
6
|
-
import { accessSync, constants, existsSync, mkdirSync, mkdtempSync,
|
|
7
|
+
import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
8
|
import * as path from "node:path";
|
|
8
9
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
9
10
|
import z from "@deepseek-ai/schemastery";
|
|
@@ -1728,7 +1729,7 @@ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
|
1728
1729
|
const KERNEL_OWNED_NAME = /^_+dashr/i;
|
|
1729
1730
|
/** Manifest format version; a snapshot written under a different version is not replayable. */
|
|
1730
1731
|
const SNAPSHOT_FORMAT = 1;
|
|
1731
|
-
function messageOf$
|
|
1732
|
+
function messageOf$2(error) {
|
|
1732
1733
|
return error instanceof Error ? error.message : String(error);
|
|
1733
1734
|
}
|
|
1734
1735
|
/** Key under which the per-session kernel map stores agentless runs. */
|
|
@@ -1841,7 +1842,7 @@ var DashrRuntime = class extends ReplRuntime {
|
|
|
1841
1842
|
} catch (error) {
|
|
1842
1843
|
return finalizeOutput([], void 0, {
|
|
1843
1844
|
kind: "worker-exit",
|
|
1844
|
-
message: `kernel failed to start: ${messageOf$
|
|
1845
|
+
message: `kernel failed to start: ${messageOf$2(error)}`
|
|
1845
1846
|
}, this.config.maxOutputBytes);
|
|
1846
1847
|
}
|
|
1847
1848
|
if (!entry.restoreAttempted) {
|
|
@@ -2031,7 +2032,7 @@ var DashrRuntime = class extends ReplRuntime {
|
|
|
2031
2032
|
} catch (error) {
|
|
2032
2033
|
return {
|
|
2033
2034
|
ok: false,
|
|
2034
|
-
message: messageOf$
|
|
2035
|
+
message: messageOf$2(error)
|
|
2035
2036
|
};
|
|
2036
2037
|
}
|
|
2037
2038
|
const value = snapshotJsonValue(resolved);
|
|
@@ -2157,7 +2158,7 @@ var DashrRuntime = class extends ReplRuntime {
|
|
|
2157
2158
|
} catch (error) {
|
|
2158
2159
|
return finalizeOutput([], void 0, {
|
|
2159
2160
|
kind: "worker-exit",
|
|
2160
|
-
message: `kernel died and respawn failed (${messageOf$
|
|
2161
|
+
message: `kernel died and respawn failed (${messageOf$2(error)}); the next run will retry a fresh kernel`
|
|
2161
2162
|
}, this.config.maxOutputBytes);
|
|
2162
2163
|
}
|
|
2163
2164
|
entry.restoreAttempted = true;
|
|
@@ -2258,252 +2259,14 @@ var DashrRuntime = class extends ReplRuntime {
|
|
|
2258
2259
|
const saved = await kernel.snapshotState(join(dir, "state.dill"), join(dir, "manifest.json"), spec);
|
|
2259
2260
|
if (saved.kind !== "ok" && saved.kind !== "skipped") this.logger.warn(`kernel namespace snapshot for ${JSON.stringify(entry.key)} failed: ${saved.reason}`);
|
|
2260
2261
|
} catch (error) {
|
|
2261
|
-
this.logger.warn(`kernel namespace snapshot for ${JSON.stringify(entry.key)} failed: ${messageOf$
|
|
2262
|
+
this.logger.warn(`kernel namespace snapshot for ${JSON.stringify(entry.key)} failed: ${messageOf$2(error)}`);
|
|
2262
2263
|
}
|
|
2263
2264
|
await kernel.dispose();
|
|
2264
2265
|
}
|
|
2265
2266
|
};
|
|
2266
2267
|
|
|
2267
2268
|
//#endregion
|
|
2268
|
-
//#region src/url-
|
|
2269
|
-
/** Structured error thrown by the URL-schema layer. */
|
|
2270
|
-
var UrlSchemaError = class extends Error {
|
|
2271
|
-
code;
|
|
2272
|
-
constructor(code, message) {
|
|
2273
|
-
super(message);
|
|
2274
|
-
this.name = "UrlSchemaError";
|
|
2275
|
-
this.code = code;
|
|
2276
|
-
}
|
|
2277
|
-
};
|
|
2278
|
-
/** Matches `scheme://rest` with a lowercase alphanumeric scheme. */
|
|
2279
|
-
const SCHEME_RE = /^([a-z][a-z0-9]*):\/\/(.*)$/;
|
|
2280
|
-
/** One line range token: `N`, `N-M`, or open `N-`. */
|
|
2281
|
-
const RANGE_RE = /^(\d+)(?:-(\d*))?$/;
|
|
2282
|
-
/**
|
|
2283
|
-
* Schemes whose URL syntax reserves `:` and `?` (ports, query strings): the
|
|
2284
|
-
* whole remainder is the path and NO selector is parsed. The http(s) handler
|
|
2285
|
-
* is documented as selector-free for exactly this reason — `:8443` is a port
|
|
2286
|
-
* and `?q=` a server query, never a line range or content filter.
|
|
2287
|
-
*/
|
|
2288
|
-
const SELECTOR_EXEMPT = {
|
|
2289
|
-
http: true,
|
|
2290
|
-
https: true
|
|
2291
|
-
};
|
|
2292
|
-
/**
|
|
2293
|
-
* Parse a `scheme://` URL into `{ scheme, path, selector }`.
|
|
2294
|
-
*
|
|
2295
|
-
* Throws a structured {@link UrlSchemaError} (code `URL_NO_SCHEME`) when the
|
|
2296
|
-
* URL has no `scheme://` prefix. Any lowercase scheme is accepted here; the
|
|
2297
|
-
* resolver rejects schemes with no registered handler. Selector-exempt
|
|
2298
|
-
* schemes ({@link SELECTOR_EXEMPT}) return the whole remainder as the path.
|
|
2299
|
-
*/
|
|
2300
|
-
function parseUrl(raw) {
|
|
2301
|
-
const m = SCHEME_RE.exec(raw);
|
|
2302
|
-
if (m === null) throw new UrlSchemaError("URL_NO_SCHEME", `URL "${raw}" has no scheme — expected "scheme://" (skill://, agent://, dsh://, ctx://, dvc://, http://, https://)`);
|
|
2303
|
-
const scheme = m[1];
|
|
2304
|
-
const rest = m[2];
|
|
2305
|
-
if (SELECTOR_EXEMPT[scheme] === true) return {
|
|
2306
|
-
scheme,
|
|
2307
|
-
path: rest,
|
|
2308
|
-
selector: null
|
|
2309
|
-
};
|
|
2310
|
-
const colon = rest.indexOf(":");
|
|
2311
|
-
const question = rest.indexOf("?");
|
|
2312
|
-
const hasColon = colon !== -1;
|
|
2313
|
-
const hasQuestion = question !== -1;
|
|
2314
|
-
if (!hasColon && !hasQuestion) return {
|
|
2315
|
-
scheme,
|
|
2316
|
-
path: rest,
|
|
2317
|
-
selector: null
|
|
2318
|
-
};
|
|
2319
|
-
const isQuery = hasQuestion && (!hasColon || question < colon);
|
|
2320
|
-
const marker = isQuery ? question : colon;
|
|
2321
|
-
const path$1 = rest.slice(0, marker);
|
|
2322
|
-
const selPart = rest.slice(marker + 1);
|
|
2323
|
-
return {
|
|
2324
|
-
scheme,
|
|
2325
|
-
path: path$1,
|
|
2326
|
-
selector: isQuery ? {
|
|
2327
|
-
kind: "query",
|
|
2328
|
-
q: selPart.startsWith("q=") ? selPart.slice(2) : selPart
|
|
2329
|
-
} : parseColonSelector(selPart)
|
|
2330
|
-
};
|
|
2331
|
-
}
|
|
2332
|
-
/** `:raw`, `:path/<subpath>`, or a `:N-M` line range list. */
|
|
2333
|
-
function parseColonSelector(selPart) {
|
|
2334
|
-
if (selPart === "raw") return { kind: "raw" };
|
|
2335
|
-
if (selPart.startsWith("path/")) return {
|
|
2336
|
-
kind: "path",
|
|
2337
|
-
value: selPart.slice(5)
|
|
2338
|
-
};
|
|
2339
|
-
return {
|
|
2340
|
-
kind: "lines",
|
|
2341
|
-
ranges: parseRanges(selPart)
|
|
2342
|
-
};
|
|
2343
|
-
}
|
|
2344
|
-
/** `N`, `N-M`, `N-`, comma-separated → `[[start, end], ...]` (1-based). */
|
|
2345
|
-
function parseRanges(spec) {
|
|
2346
|
-
return spec.split(",").map((part) => {
|
|
2347
|
-
const token = part.trim();
|
|
2348
|
-
const m = RANGE_RE.exec(token);
|
|
2349
|
-
if (m === null) throw new UrlSchemaError("URL_BAD_SELECTOR", `invalid line selector ":${spec}" — expected N, N-M, or N-M,N2-M2`);
|
|
2350
|
-
const start = Number(m[1]);
|
|
2351
|
-
if (start < 1) throw new UrlSchemaError("URL_BAD_SELECTOR", `line numbers are 1-based — got ${start} in ":${spec}"`);
|
|
2352
|
-
const end = m[2] === void 0 ? start : m[2] === "" ? Infinity : Number(m[2]);
|
|
2353
|
-
if (end !== Infinity && end < start) throw new UrlSchemaError("URL_BAD_SELECTOR", `line range ${start}-${end} is empty — end must be >= start`);
|
|
2354
|
-
return [start, end];
|
|
2355
|
-
});
|
|
2356
|
-
}
|
|
2357
|
-
/**
|
|
2358
|
-
* Apply a selector to already-resolved text.
|
|
2359
|
-
*
|
|
2360
|
-
* `null` and `{ kind: 'raw' }` return the text unchanged. `lines` performs a
|
|
2361
|
-
* 1-based inclusive slice (open tail via `Infinity`). `path` navigates a JSON
|
|
2362
|
-
* sub-resource by dot-path. `query` navigates JSON by dot-path when the text
|
|
2363
|
-
* parses as JSON, and otherwise keeps the lines containing the query string.
|
|
2364
|
-
*/
|
|
2365
|
-
function applySelector(text, sel) {
|
|
2366
|
-
if (sel === null) return text;
|
|
2367
|
-
switch (sel.kind) {
|
|
2368
|
-
case "raw": return text;
|
|
2369
|
-
case "lines": return applyLines(text, sel.ranges);
|
|
2370
|
-
case "path": return applyPath(text, sel.value);
|
|
2371
|
-
case "query": return applyQuery(text, sel.q);
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
/** 1-based inclusive line slicing; `Infinity` end = open tail. */
|
|
2375
|
-
function applyLines(text, ranges) {
|
|
2376
|
-
if (ranges.length === 0) return "";
|
|
2377
|
-
const lines = text.split("\n");
|
|
2378
|
-
const out = [];
|
|
2379
|
-
for (const [startRaw, endRaw] of ranges) {
|
|
2380
|
-
const start = Math.max(1, startRaw);
|
|
2381
|
-
const end = endRaw === Infinity ? lines.length : Math.min(lines.length, endRaw);
|
|
2382
|
-
if (start <= end) out.push(...lines.slice(start - 1, end));
|
|
2383
|
-
}
|
|
2384
|
-
return out.join("\n");
|
|
2385
|
-
}
|
|
2386
|
-
/** Navigate a JSON sub-resource by dot-path; non-JSON text passes through. */
|
|
2387
|
-
function applyPath(text, value) {
|
|
2388
|
-
if (value === "") return text;
|
|
2389
|
-
const parsed = tryParseJson(text);
|
|
2390
|
-
if (parsed === void 0) return text;
|
|
2391
|
-
const node = navigate(parsed, value);
|
|
2392
|
-
return node === void 0 ? text : stringifyNode(node);
|
|
2393
|
-
}
|
|
2394
|
-
/** JSON dot-path query when the text is JSON, else a substring line filter. */
|
|
2395
|
-
function applyQuery(text, q) {
|
|
2396
|
-
if (q === "") return text;
|
|
2397
|
-
const parsed = tryParseJson(text);
|
|
2398
|
-
if (parsed !== void 0) {
|
|
2399
|
-
const node = navigate(parsed, q);
|
|
2400
|
-
return node === void 0 ? text : stringifyNode(node);
|
|
2401
|
-
}
|
|
2402
|
-
return text.split("\n").filter((line) => line.includes(q)).join("\n");
|
|
2403
|
-
}
|
|
2404
|
-
function tryParseJson(text) {
|
|
2405
|
-
try {
|
|
2406
|
-
return JSON.parse(text);
|
|
2407
|
-
} catch {
|
|
2408
|
-
return;
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
/** Walk a dot-path through a parsed JSON value (numeric segments index arrays). */
|
|
2412
|
-
function navigate(node, path$1) {
|
|
2413
|
-
let current$1 = node;
|
|
2414
|
-
for (const segment of path$1.split(".")) {
|
|
2415
|
-
if (segment === "") return void 0;
|
|
2416
|
-
if (Array.isArray(current$1)) {
|
|
2417
|
-
const idx = Number(segment);
|
|
2418
|
-
if (!Number.isInteger(idx) || idx < 0) return void 0;
|
|
2419
|
-
current$1 = current$1[idx];
|
|
2420
|
-
} else if (current$1 !== null && typeof current$1 === "object") current$1 = current$1[segment];
|
|
2421
|
-
else return;
|
|
2422
|
-
}
|
|
2423
|
-
return current$1;
|
|
2424
|
-
}
|
|
2425
|
-
/** Render a navigated leaf as text. */
|
|
2426
|
-
function stringifyNode(node) {
|
|
2427
|
-
if (typeof node === "string") return node;
|
|
2428
|
-
try {
|
|
2429
|
-
return JSON.stringify(node) ?? "";
|
|
2430
|
-
} catch {
|
|
2431
|
-
return String(node);
|
|
2432
|
-
}
|
|
2433
|
-
}
|
|
2434
|
-
|
|
2435
|
-
//#endregion
|
|
2436
|
-
//#region src/url-schema/handlers/dvc.ts
|
|
2437
|
-
/** Module-level device registry: insertion-ordered, shared by every handler instance. */
|
|
2438
|
-
const devices = /* @__PURE__ */ new Map();
|
|
2439
|
-
/** Register (or replace) the device mounted under `name`. */
|
|
2440
|
-
function registerDvcDevice(name$2, device) {
|
|
2441
|
-
devices.set(name$2, device);
|
|
2442
|
-
}
|
|
2443
|
-
/** Read-only view of the registered devices, in registration order. */
|
|
2444
|
-
function listDvcDevices() {
|
|
2445
|
-
return devices;
|
|
2446
|
-
}
|
|
2447
|
-
/**
|
|
2448
|
-
* The first path segment is the device name (`dvc://name`, `dvc://name/sub`).
|
|
2449
|
-
* A leading `dvc://` is tolerated so callers may pass either the parsed path
|
|
2450
|
-
* (the write tool's contract) or the full URL.
|
|
2451
|
-
*/
|
|
2452
|
-
function deviceNameFromPath(path$1) {
|
|
2453
|
-
const trimmed = (path$1.startsWith("dvc://") ? path$1.slice(6) : path$1).replace(/^\/+/, "");
|
|
2454
|
-
const slash = trimmed.indexOf("/");
|
|
2455
|
-
return slash === -1 ? trimmed : trimmed.slice(0, slash);
|
|
2456
|
-
}
|
|
2457
|
-
/** Uniform `unknown`-error rendering for structured error messages. */
|
|
2458
|
-
function messageOf$2(error) {
|
|
2459
|
-
return error instanceof Error ? error.message : String(error);
|
|
2460
|
-
}
|
|
2461
|
-
/**
|
|
2462
|
-
* Build the `dvc://` scheme handler over the module-level device registry.
|
|
2463
|
-
* Bare `dvc://` yields the roster (`no devices mounted` while empty); a
|
|
2464
|
-
* registered `<device>` yields its summary plus a usage hint; an unregistered
|
|
2465
|
-
* name keeps the `unknown device` placeholder text.
|
|
2466
|
-
*/
|
|
2467
|
-
function createDvcHandler(_deps = {}) {
|
|
2468
|
-
return { async resolve(_env, path$1) {
|
|
2469
|
-
const name$2 = deviceNameFromPath(path$1);
|
|
2470
|
-
if (name$2 === "") {
|
|
2471
|
-
if (devices.size === 0) return "no devices mounted";
|
|
2472
|
-
return [...devices].map(([n, device$1]) => `${n}\t${device$1.summary}`).join("\n");
|
|
2473
|
-
}
|
|
2474
|
-
const device = devices.get(name$2);
|
|
2475
|
-
if (device === void 0) return `unknown device: ${name$2}`;
|
|
2476
|
-
return `${device.summary}\nusage: write dvc://${name$2} with a JSON args object to execute this device`;
|
|
2477
|
-
} };
|
|
2478
|
-
}
|
|
2479
|
-
/**
|
|
2480
|
-
* Write dispatch for `dvc://` URLs — called by the write tool's URL branch.
|
|
2481
|
-
* `path` addresses the device (parsed path or full `dvc://` URL); `content`
|
|
2482
|
-
* must be the JSON args payload.
|
|
2483
|
-
*
|
|
2484
|
-
* Routing and args failures (`DVC_NO_DEVICE`, `DVC_UNKNOWN_DEVICE`,
|
|
2485
|
-
* `DVC_BAD_ARGS`) throw synchronously — the placeholder wave's observable
|
|
2486
|
-
* contract — while a device-reported failure rejects the returned promise as
|
|
2487
|
-
* `DVC_DEVICE_ERROR` carrying the device name.
|
|
2488
|
-
*/
|
|
2489
|
-
function dispatchDvcWrite(path$1, content) {
|
|
2490
|
-
if (devices.size === 0) throw new UrlSchemaError("DVC_NO_DEVICE", "dvc:// write dispatch: no devices mounted to route the write to");
|
|
2491
|
-
const name$2 = deviceNameFromPath(path$1);
|
|
2492
|
-
const device = devices.get(name$2);
|
|
2493
|
-
if (device === void 0) throw new UrlSchemaError("DVC_UNKNOWN_DEVICE", `dvc:// write dispatch: no device named "${name$2}" (registered: ${[...devices.keys()].sort().join(", ")})`);
|
|
2494
|
-
let args;
|
|
2495
|
-
try {
|
|
2496
|
-
args = JSON.parse(content);
|
|
2497
|
-
} catch (error) {
|
|
2498
|
-
throw new UrlSchemaError("DVC_BAD_ARGS", `dvc:// write dispatch: device "${name$2}" requires a JSON args payload (${messageOf$2(error)})`);
|
|
2499
|
-
}
|
|
2500
|
-
return Promise.resolve().then(() => device.execute(args)).catch((error) => {
|
|
2501
|
-
throw new UrlSchemaError("DVC_DEVICE_ERROR", `dvc:// device "${name$2}" execute failed: ${messageOf$2(error)}`);
|
|
2502
|
-
});
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
//#endregion
|
|
2506
|
-
//#region src/url-schema/vendored/devices/ast/natives-loader.ts
|
|
2269
|
+
//#region src/url-schemes/vendored/devices/ast/natives-loader.ts
|
|
2507
2270
|
/** Platform tags with a published leaf package (mirrors the upstream loader). */
|
|
2508
2271
|
const SUPPORTED_PLATFORM_TAGS = [
|
|
2509
2272
|
"linux-x64",
|
|
@@ -2616,7 +2379,7 @@ function loadPiNatives(options = {}) {
|
|
|
2616
2379
|
}
|
|
2617
2380
|
|
|
2618
2381
|
//#endregion
|
|
2619
|
-
//#region src/url-
|
|
2382
|
+
//#region src/url-schemes/vendored/devices/ast/ast-device.ts
|
|
2620
2383
|
/** Per-run cap on distinct rewritten files (upstream `PI_MAX_AST_FILES` default). */
|
|
2621
2384
|
const MAX_FILES = 1e3;
|
|
2622
2385
|
/** Glob metacharacters that mark a path segment as a pattern (upstream `GLOB_PATH_CHARS`). */
|
|
@@ -2809,14 +2572,14 @@ const summaries = {
|
|
|
2809
2572
|
};
|
|
2810
2573
|
|
|
2811
2574
|
//#endregion
|
|
2812
|
-
//#region src/url-
|
|
2575
|
+
//#region src/url-schemes/vendored/devices/browser/browser-device.ts
|
|
2813
2576
|
let puppeteerModule;
|
|
2814
2577
|
/** Load puppeteer-core once; missing package → structured BROWSER_NO_PUPPETEER. */
|
|
2815
2578
|
async function loadPuppeteer() {
|
|
2816
2579
|
if (puppeteerModule === void 0) try {
|
|
2817
2580
|
puppeteerModule = await import("puppeteer-core");
|
|
2818
2581
|
} catch (err) {
|
|
2819
|
-
throw new
|
|
2582
|
+
throw new UrlSchemesError("BROWSER_NO_PUPPETEER", `puppeteer-core is not installed — the browser device needs it as a dependency (npm install puppeteer-core@^25.3.0): ${messageOf$1(err)}`);
|
|
2820
2583
|
}
|
|
2821
2584
|
return puppeteerModule;
|
|
2822
2585
|
}
|
|
@@ -2844,18 +2607,18 @@ function withCause(error, cause) {
|
|
|
2844
2607
|
}
|
|
2845
2608
|
/** Assert the args payload is a plain JSON object and return it as a record. */
|
|
2846
2609
|
function requireObjectArgs$1(args) {
|
|
2847
|
-
if (args === null || typeof args !== "object" || Array.isArray(args)) throw new
|
|
2610
|
+
if (args === null || typeof args !== "object" || Array.isArray(args)) throw new UrlSchemesError("BROWSER_BAD_ARGS", `browser device: args must be a JSON object with an "action" ("open" | "run" | "close"), got ${args === null ? "null" : Array.isArray(args) ? "array" : typeof args}`);
|
|
2848
2611
|
return args;
|
|
2849
2612
|
}
|
|
2850
2613
|
/** Reject fields outside the action's allowlist (typo guard, DVC_BAD_ARGS-style). */
|
|
2851
2614
|
function rejectUnknownFields$1(action, args, allowed) {
|
|
2852
2615
|
const unknown = Object.keys(args).filter((key) => !allowed.includes(key));
|
|
2853
|
-
if (unknown.length > 0) throw new
|
|
2616
|
+
if (unknown.length > 0) throw new UrlSchemesError("BROWSER_BAD_ARGS", `browser device: unknown field(s) ${unknown.map((key) => JSON.stringify(key)).join(", ")} for action "${action}" — allowed: ${allowed.map((key) => JSON.stringify(key)).join(", ")}`);
|
|
2854
2617
|
}
|
|
2855
2618
|
/** Require a non-empty string field; `field` names it in the error. */
|
|
2856
2619
|
function requireString(args, field) {
|
|
2857
2620
|
const value = args[field];
|
|
2858
|
-
if (typeof value !== "string" || value === "") throw new
|
|
2621
|
+
if (typeof value !== "string" || value === "") throw new UrlSchemesError("BROWSER_BAD_ARGS", `browser device: this action requires a non-empty "${field}" string`);
|
|
2859
2622
|
return value;
|
|
2860
2623
|
}
|
|
2861
2624
|
/** Resolve the Chrome executable: open args → env `DASHR_BROWSER_BIN` → distro default. */
|
|
@@ -2878,7 +2641,7 @@ async function gotoUrl(target, url) {
|
|
|
2878
2641
|
try {
|
|
2879
2642
|
await target.goto(url, { waitUntil: "domcontentloaded" });
|
|
2880
2643
|
} catch (error) {
|
|
2881
|
-
throw withCause(new
|
|
2644
|
+
throw withCause(new UrlSchemesError("BROWSER_GOTO_FAILED", `browser device: goto(${url}) failed: ${messageOf$1(error)}`), error);
|
|
2882
2645
|
}
|
|
2883
2646
|
}
|
|
2884
2647
|
/**
|
|
@@ -2890,7 +2653,7 @@ async function gotoUrl(target, url) {
|
|
|
2890
2653
|
*/
|
|
2891
2654
|
async function ensurePage(executablePath) {
|
|
2892
2655
|
if (page !== void 0) return page;
|
|
2893
|
-
if (!existsSync(executablePath)) throw new
|
|
2656
|
+
if (!existsSync(executablePath)) throw new UrlSchemesError("BROWSER_NO_CHROME", `browser device: Chrome executable not found at ${executablePath} — install Google Chrome (expected at ${DEFAULT_CHROME_PATH}), set DASHR_BROWSER_BIN, or pass "executablePath" in the open args`);
|
|
2894
2657
|
const puppeteer = await loadPuppeteer();
|
|
2895
2658
|
let launched;
|
|
2896
2659
|
try {
|
|
@@ -2901,13 +2664,13 @@ async function ensurePage(executablePath) {
|
|
|
2901
2664
|
protocolTimeout: PROTOCOL_TIMEOUT_MS
|
|
2902
2665
|
});
|
|
2903
2666
|
} catch (error) {
|
|
2904
|
-
throw withCause(new
|
|
2667
|
+
throw withCause(new UrlSchemesError("BROWSER_LAUNCH_FAILED", `browser device: Chrome launch failed (${executablePath}): ${messageOf$1(error)}`), error);
|
|
2905
2668
|
}
|
|
2906
2669
|
try {
|
|
2907
2670
|
page = await launched.newPage();
|
|
2908
2671
|
} catch (error) {
|
|
2909
2672
|
await closeQuietly(launched, () => launched.close());
|
|
2910
|
-
throw withCause(new
|
|
2673
|
+
throw withCause(new UrlSchemesError("BROWSER_LAUNCH_FAILED", `browser device: opening a page in the launched Chrome failed: ${messageOf$1(error)}`), error);
|
|
2911
2674
|
}
|
|
2912
2675
|
browser = launched;
|
|
2913
2676
|
return page;
|
|
@@ -2945,11 +2708,11 @@ function jsonSafe(value) {
|
|
|
2945
2708
|
}
|
|
2946
2709
|
/** Validate run's `type` sub-action shape: `{selector: string, text: string}`. */
|
|
2947
2710
|
function requireTypeSpec(action, value) {
|
|
2948
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new
|
|
2711
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new UrlSchemesError("BROWSER_BAD_ARGS", `browser device: "${action}" field "type" must be {"selector": string, "text": string}`);
|
|
2949
2712
|
const record = value;
|
|
2950
2713
|
const selector = record["selector"];
|
|
2951
2714
|
const text = record["text"];
|
|
2952
|
-
if (typeof selector !== "string" || selector === "" || typeof text !== "string") throw new
|
|
2715
|
+
if (typeof selector !== "string" || selector === "" || typeof text !== "string") throw new UrlSchemesError("BROWSER_BAD_ARGS", `browser device: "${action}" field "type" must be {"selector": string, "text": string}`);
|
|
2953
2716
|
return {
|
|
2954
2717
|
selector,
|
|
2955
2718
|
text
|
|
@@ -2957,7 +2720,7 @@ function requireTypeSpec(action, value) {
|
|
|
2957
2720
|
}
|
|
2958
2721
|
/** The live page for a run action — every run shape needs an open tab. */
|
|
2959
2722
|
function requireLivePage() {
|
|
2960
|
-
if (page === void 0) throw new
|
|
2723
|
+
if (page === void 0) throw new UrlSchemesError("BROWSER_NO_PAGE", "browser device: no page open — write {\"action\":\"open\",\"url\":\"...\"} to dvc://browser first");
|
|
2961
2724
|
return page;
|
|
2962
2725
|
}
|
|
2963
2726
|
/** `{"action":"run"}` — evaluate `code`, or run the goto/type/click convenience steps. */
|
|
@@ -2971,8 +2734,8 @@ async function runAction(args) {
|
|
|
2971
2734
|
]);
|
|
2972
2735
|
const hasCode = "code" in args;
|
|
2973
2736
|
const hasConvenience = "click" in args || "type" in args || "goto" in args;
|
|
2974
|
-
if (hasCode && hasConvenience) throw new
|
|
2975
|
-
if (!hasCode && !hasConvenience) throw new
|
|
2737
|
+
if (hasCode && hasConvenience) throw new UrlSchemesError("BROWSER_BAD_ARGS", "browser device: \"run\" takes either \"code\" or the convenience fields (\"goto\"/\"type\"/\"click\"), not both");
|
|
2738
|
+
if (!hasCode && !hasConvenience) throw new UrlSchemesError("BROWSER_BAD_ARGS", "browser device: \"run\" requires \"code\" or at least one of \"goto\"/\"type\"/\"click\"");
|
|
2976
2739
|
const goto = "goto" in args ? requireString(args, "goto") : void 0;
|
|
2977
2740
|
const typeSpec = "type" in args ? requireTypeSpec("run", args["type"]) : void 0;
|
|
2978
2741
|
const click = "click" in args ? requireString(args, "click") : void 0;
|
|
@@ -2986,7 +2749,7 @@ async function runAction(args) {
|
|
|
2986
2749
|
result: jsonSafe(await target.evaluate(expression))
|
|
2987
2750
|
};
|
|
2988
2751
|
} catch (error) {
|
|
2989
|
-
throw withCause(new
|
|
2752
|
+
throw withCause(new UrlSchemesError("BROWSER_EVAL_FAILED", `browser device: evaluate failed: ${messageOf$1(error)}`), error);
|
|
2990
2753
|
}
|
|
2991
2754
|
}
|
|
2992
2755
|
try {
|
|
@@ -2994,8 +2757,8 @@ async function runAction(args) {
|
|
|
2994
2757
|
if (typeSpec !== void 0) await target.type(typeSpec.selector, typeSpec.text);
|
|
2995
2758
|
if (click !== void 0) await target.click(click);
|
|
2996
2759
|
} catch (error) {
|
|
2997
|
-
if (error instanceof
|
|
2998
|
-
throw withCause(new
|
|
2760
|
+
if (error instanceof UrlSchemesError) throw error;
|
|
2761
|
+
throw withCause(new UrlSchemesError("BROWSER_STEP_FAILED", `browser device: run step failed: ${messageOf$1(error)}`), error);
|
|
2999
2762
|
}
|
|
3000
2763
|
return {
|
|
3001
2764
|
ok: true,
|
|
@@ -3006,7 +2769,7 @@ async function runAction(args) {
|
|
|
3006
2769
|
/** `{"action":"close"}` — teardown; state clears before any await so a hung close never wedges it. */
|
|
3007
2770
|
async function closeAction(args) {
|
|
3008
2771
|
rejectUnknownFields$1("close", args, ["action"]);
|
|
3009
|
-
if (page === void 0 && browser === void 0) throw new
|
|
2772
|
+
if (page === void 0 && browser === void 0) throw new UrlSchemesError("BROWSER_NOT_OPEN", "browser device: nothing to close — no browser is open (open one with {\"action\":\"open\",\"url\":\"...\"})");
|
|
3010
2773
|
const openPage = page;
|
|
3011
2774
|
const openBrowser = browser;
|
|
3012
2775
|
page = void 0;
|
|
@@ -3023,7 +2786,7 @@ const browserDevice = {
|
|
|
3023
2786
|
case "open": return openAction(record);
|
|
3024
2787
|
case "run": return runAction(record);
|
|
3025
2788
|
case "close": return closeAction(record);
|
|
3026
|
-
default: throw new
|
|
2789
|
+
default: throw new UrlSchemesError("BROWSER_BAD_ARGS", `browser device: unknown action ${JSON.stringify(record["action"] ?? null)} — expected "open", "run", or "close"`);
|
|
3027
2790
|
}
|
|
3028
2791
|
},
|
|
3029
2792
|
summary: "headless Chrome via puppeteer-core — open {url} / run {code|goto,type,click} / close; one tab per process (single-tab subset of omp's multi-tab browser)"
|
|
@@ -3039,7 +2802,7 @@ function registerBrowserDevice(registry = registerDvcDevice) {
|
|
|
3039
2802
|
}
|
|
3040
2803
|
|
|
3041
2804
|
//#endregion
|
|
3042
|
-
//#region src/url-
|
|
2805
|
+
//#region src/url-schemes/vendored/devices/lsp/lsp-jsonrpc.ts
|
|
3043
2806
|
/**
|
|
3044
2807
|
* Content-Length JSON-RPC framing for the vendored `dvc://lsp` device.
|
|
3045
2808
|
*
|
|
@@ -3167,7 +2930,7 @@ var MessageFramer = class {
|
|
|
3167
2930
|
};
|
|
3168
2931
|
|
|
3169
2932
|
//#endregion
|
|
3170
|
-
//#region src/url-
|
|
2933
|
+
//#region src/url-schemes/vendored/devices/lsp/lsp-language.ts
|
|
3171
2934
|
/** Extension (without dot) → LSP language id (defaults.json coverage). */
|
|
3172
2935
|
const EXTENSION_LANGUAGE_ID = {
|
|
3173
2936
|
rs: "rust",
|
|
@@ -3270,7 +3033,7 @@ function detectLanguageId(filePath) {
|
|
|
3270
3033
|
}
|
|
3271
3034
|
|
|
3272
3035
|
//#endregion
|
|
3273
|
-
//#region src/url-
|
|
3036
|
+
//#region src/url-schemes/vendored/devices/lsp/lsp-client.ts
|
|
3274
3037
|
/** Default per-request timeout when no explicit budget is given (upstream 30s). */
|
|
3275
3038
|
const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
3276
3039
|
/** Max time to wait for the `initialize` response (upstream WARMUP/init budget). */
|
|
@@ -3801,7 +3564,7 @@ if (typeof process !== "undefined") process.on("beforeExit", () => {
|
|
|
3801
3564
|
});
|
|
3802
3565
|
|
|
3803
3566
|
//#endregion
|
|
3804
|
-
//#region src/url-
|
|
3567
|
+
//#region src/url-schemes/vendored/devices/lsp/defaults.json
|
|
3805
3568
|
var defaults_default = {
|
|
3806
3569
|
"rust-analyzer": {
|
|
3807
3570
|
"command": "rust-analyzer",
|
|
@@ -4552,7 +4315,7 @@ var defaults_default = {
|
|
|
4552
4315
|
};
|
|
4553
4316
|
|
|
4554
4317
|
//#endregion
|
|
4555
|
-
//#region src/url-
|
|
4318
|
+
//#region src/url-schemes/vendored/devices/lsp/lsp-server-registry.ts
|
|
4556
4319
|
/** The verbatim upstream registry (54 servers), keyed by server name. */
|
|
4557
4320
|
const REGISTRY = defaults_default;
|
|
4558
4321
|
/** Server names in registry (insertion) order — deterministic selection. */
|
|
@@ -4674,7 +4437,7 @@ function installHintFor(command) {
|
|
|
4674
4437
|
}
|
|
4675
4438
|
|
|
4676
4439
|
//#endregion
|
|
4677
|
-
//#region src/url-
|
|
4440
|
+
//#region src/url-schemes/vendored/devices/lsp/lsp-device.ts
|
|
4678
4441
|
const ACTIONS = new Set([
|
|
4679
4442
|
"diagnostics",
|
|
4680
4443
|
"definition",
|
|
@@ -4694,16 +4457,16 @@ function messageOf(error) {
|
|
|
4694
4457
|
}
|
|
4695
4458
|
/** Assert the args payload is a plain JSON object and return it as a record. */
|
|
4696
4459
|
function requireObjectArgs(args) {
|
|
4697
|
-
if (args === null || typeof args !== "object" || Array.isArray(args)) throw new
|
|
4460
|
+
if (args === null || typeof args !== "object" || Array.isArray(args)) throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: args must be a JSON object with an "action" ("diagnostics" | "definition" | "references" | "hover"), got ${args === null ? "null" : Array.isArray(args) ? "array" : typeof args}`);
|
|
4698
4461
|
return args;
|
|
4699
4462
|
}
|
|
4700
4463
|
/** Resolve the request's server config: named `server` → `command` override → file-type lookup. */
|
|
4701
4464
|
function resolveServerConfig(args, filePath) {
|
|
4702
4465
|
const server = args.server;
|
|
4703
4466
|
if (server !== void 0) {
|
|
4704
|
-
if (typeof server !== "string" || server === "") throw new
|
|
4467
|
+
if (typeof server !== "string" || server === "") throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: \"server\" must be a non-empty server name");
|
|
4705
4468
|
const config = serverByName(server);
|
|
4706
|
-
if (config === void 0) throw new
|
|
4469
|
+
if (config === void 0) throw new UrlSchemesError("LSP_NO_SERVER", `lsp device: no defaults.json entry named "${server}"`);
|
|
4707
4470
|
return {
|
|
4708
4471
|
name: server,
|
|
4709
4472
|
config
|
|
@@ -4711,9 +4474,9 @@ function resolveServerConfig(args, filePath) {
|
|
|
4711
4474
|
}
|
|
4712
4475
|
const command = args.command;
|
|
4713
4476
|
if (command !== void 0) {
|
|
4714
|
-
if (typeof command !== "string" || command === "") throw new
|
|
4477
|
+
if (typeof command !== "string" || command === "") throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: \"command\" override must be a non-empty string");
|
|
4715
4478
|
const commandArgs = args.args;
|
|
4716
|
-
if (commandArgs !== void 0 && (!Array.isArray(commandArgs) || commandArgs.some((a) => typeof a !== "string"))) throw new
|
|
4479
|
+
if (commandArgs !== void 0 && (!Array.isArray(commandArgs) || commandArgs.some((a) => typeof a !== "string"))) throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: \"args\" override must be an array of strings");
|
|
4717
4480
|
return {
|
|
4718
4481
|
name: command,
|
|
4719
4482
|
config: {
|
|
@@ -4725,7 +4488,7 @@ function resolveServerConfig(args, filePath) {
|
|
|
4725
4488
|
};
|
|
4726
4489
|
}
|
|
4727
4490
|
const primary = primaryServerForFile(filePath);
|
|
4728
|
-
if (primary === null) throw new
|
|
4491
|
+
if (primary === null) throw new UrlSchemesError("LSP_NO_SERVER", `lsp device: no defaults.json language server covers "${path.basename(filePath)}" — pass "server" or "command"/"args" to pick one explicitly`);
|
|
4729
4492
|
return {
|
|
4730
4493
|
name: primary[0],
|
|
4731
4494
|
config: primary[1]
|
|
@@ -4736,7 +4499,7 @@ function requirePosition(args) {
|
|
|
4736
4499
|
const read = (field) => {
|
|
4737
4500
|
const value = args[field];
|
|
4738
4501
|
if (value === void 0) return 1;
|
|
4739
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) throw new
|
|
4502
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: "${field}" must be a 1-based positive integer (got ${JSON.stringify(value ?? null)})`);
|
|
4740
4503
|
return value;
|
|
4741
4504
|
};
|
|
4742
4505
|
return {
|
|
@@ -4753,13 +4516,13 @@ function requirePosition(args) {
|
|
|
4753
4516
|
async function obtainClient(args, filePath) {
|
|
4754
4517
|
const { name: name$2, config } = resolveServerConfig(args, filePath);
|
|
4755
4518
|
const resolvedCommand = resolveCommandPath(config.command, path.dirname(filePath));
|
|
4756
|
-
if (resolvedCommand === null) throw new
|
|
4519
|
+
if (resolvedCommand === null) throw new UrlSchemesError("LSP_SERVER_MISSING", `lsp device: language server binary "${config.command}" not found (server "${name$2}") — ${installHintFor(config.command)}`);
|
|
4757
4520
|
const root = findWorkspaceRoot(filePath, config.rootMarkers) ?? path.dirname(path.resolve(filePath));
|
|
4758
4521
|
let client;
|
|
4759
4522
|
try {
|
|
4760
4523
|
client = await getOrCreateClient(config, root, resolvedCommand);
|
|
4761
4524
|
} catch (error) {
|
|
4762
|
-
throw new
|
|
4525
|
+
throw new UrlSchemesError("LSP_INIT_FAILED", `lsp device: language server "${name$2}" (${config.command}) failed to initialize: ${messageOf(error)}`);
|
|
4763
4526
|
}
|
|
4764
4527
|
return {
|
|
4765
4528
|
name: name$2,
|
|
@@ -4871,7 +4634,7 @@ async function lspPositionRequest(client, method, params, retryWhileEmpty) {
|
|
|
4871
4634
|
result = await sendRequest(client, method, params);
|
|
4872
4635
|
} catch (error) {
|
|
4873
4636
|
const message = messageOf(error);
|
|
4874
|
-
if (!(retryWhileEmpty && /-3280[01]/.test(message) && Date.now() - client.readyAt <= COLD_START_RETRY_WINDOW_MS)) throw new
|
|
4637
|
+
if (!(retryWhileEmpty && /-3280[01]/.test(message) && Date.now() - client.readyAt <= COLD_START_RETRY_WINDOW_MS)) throw new UrlSchemesError("LSP_REQUEST_FAILED", `lsp device: ${method} failed on ${client.config.command}: ${message}`);
|
|
4875
4638
|
await new Promise((resolve$1) => setTimeout(resolve$1, COLD_START_RETRY_DELAY_MS));
|
|
4876
4639
|
continue;
|
|
4877
4640
|
}
|
|
@@ -4897,16 +4660,16 @@ const lspDevice = {
|
|
|
4897
4660
|
async execute(args) {
|
|
4898
4661
|
const record = requireObjectArgs(args);
|
|
4899
4662
|
const action = record.action;
|
|
4900
|
-
if (typeof action !== "string" || !ACTIONS.has(action)) throw new
|
|
4663
|
+
if (typeof action !== "string" || !ACTIONS.has(action)) throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: unknown action ${JSON.stringify(action ?? null)} — expected "diagnostics", "definition", "references", "hover", or "format"`);
|
|
4901
4664
|
const file = record.file;
|
|
4902
|
-
if (typeof file !== "string" || file === "") throw new
|
|
4665
|
+
if (typeof file !== "string" || file === "") throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: this action requires a non-empty \"file\" string");
|
|
4903
4666
|
const content = record.content;
|
|
4904
|
-
if (content !== void 0 && typeof content !== "string") throw new
|
|
4667
|
+
if (content !== void 0 && typeof content !== "string") throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: \"content\" override must be a string");
|
|
4905
4668
|
const saved = record.saved;
|
|
4906
|
-
if (saved !== void 0 && typeof saved !== "boolean") throw new
|
|
4907
|
-
if (saved === true && content === void 0) throw new
|
|
4669
|
+
if (saved !== void 0 && typeof saved !== "boolean") throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: \"saved\" flag must be a boolean");
|
|
4670
|
+
if (saved === true && content === void 0) throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: \"saved\" requires a \"content\" override (the save signal rides a content sync)");
|
|
4908
4671
|
const filePath = path.resolve(file);
|
|
4909
|
-
if (content === void 0 && !existsSync(filePath)) throw new
|
|
4672
|
+
if (content === void 0 && !existsSync(filePath)) throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: file not found: ${filePath}`);
|
|
4910
4673
|
const position = requirePosition(record);
|
|
4911
4674
|
const { name: name$2, client } = await obtainClient(record, filePath);
|
|
4912
4675
|
const base = {
|
|
@@ -4988,7 +4751,7 @@ const lspDevice = {
|
|
|
4988
4751
|
};
|
|
4989
4752
|
}
|
|
4990
4753
|
case "format": {
|
|
4991
|
-
if (content === void 0) throw new
|
|
4754
|
+
if (content === void 0) throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: \"format\" requires a \"content\" string to format");
|
|
4992
4755
|
const formatting = client.serverCapabilities?.documentFormattingProvider ?? client.serverCapabilities?.formattingProvider;
|
|
4993
4756
|
if (formatting === void 0 || formatting === false || formatting === null) return {
|
|
4994
4757
|
...base,
|
|
@@ -5014,7 +4777,7 @@ const lspDevice = {
|
|
|
5014
4777
|
changed: true
|
|
5015
4778
|
};
|
|
5016
4779
|
}
|
|
5017
|
-
default: throw new
|
|
4780
|
+
default: throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: unhandled action "${String(action)}"`);
|
|
5018
4781
|
}
|
|
5019
4782
|
},
|
|
5020
4783
|
summary: "LSP queries over stdio language servers (defaults.json registry) — diagnostics / definition / references / hover on {file,line,character}"
|
|
@@ -5028,22 +4791,7 @@ function installLspDevices(registry = registerDvcDevice) {
|
|
|
5028
4791
|
}
|
|
5029
4792
|
|
|
5030
4793
|
//#endregion
|
|
5031
|
-
//#region src/url-
|
|
5032
|
-
function resolveDocsDir() {
|
|
5033
|
-
let dir = dirname(fileURLToPath(import.meta.url));
|
|
5034
|
-
for (;;) {
|
|
5035
|
-
const candidate = join(dir, "docs");
|
|
5036
|
-
try {
|
|
5037
|
-
if (statSync(candidate).isDirectory()) return candidate;
|
|
5038
|
-
} catch {}
|
|
5039
|
-
const parent = dirname(dir);
|
|
5040
|
-
if (parent === dir) return void 0;
|
|
5041
|
-
dir = parent;
|
|
5042
|
-
}
|
|
5043
|
-
}
|
|
5044
|
-
|
|
5045
|
-
//#endregion
|
|
5046
|
-
//#region src/url-schema/handlers/agent.ts
|
|
4794
|
+
//#region src/url-schemes/handlers/agent.ts
|
|
5047
4795
|
/**
|
|
5048
4796
|
* Build the `agent://` scheme handler. Handlers return the FULL text of the
|
|
5049
4797
|
* resource; the resolver applies any `:raw`/`:N-M`/`/path`/`?q=` selector.
|
|
@@ -5067,13 +4815,13 @@ function createAgentHandler(deps) {
|
|
|
5067
4815
|
const grandChildren = children.filter((entry) => String(entry.parentId) === parent.rawId);
|
|
5068
4816
|
return await outputFor(requireAddress(await resolveAddress(rest, parent.rawId, grandChildren, family), rest, `unknown child agent "${rest}" of "${parent.rawId}"`), family);
|
|
5069
4817
|
}
|
|
5070
|
-
throw new
|
|
4818
|
+
throw new UrlSchemesError("AGENT_BAD_PATH", `agent:// path "${path$1}" has too many segments — expected <id>, <id>/transcript, or <id>/<child>`);
|
|
5071
4819
|
} };
|
|
5072
4820
|
}
|
|
5073
4821
|
/** The calling agent's raw session id, or a structured error without one. */
|
|
5074
4822
|
function requireCaller(env) {
|
|
5075
4823
|
const { agent } = env;
|
|
5076
|
-
if (agent === void 0) throw new
|
|
4824
|
+
if (agent === void 0) throw new UrlSchemesError("AGENT_NO_AGENT", "agent:// requires the calling agent in the resolver env (this context has none)");
|
|
5077
4825
|
return String(agent.id);
|
|
5078
4826
|
}
|
|
5079
4827
|
/**
|
|
@@ -5101,7 +4849,7 @@ async function resolveAddress(segment, selfRaw, candidates, family) {
|
|
|
5101
4849
|
}
|
|
5102
4850
|
/** Turn a failed address resolution into its structured error. */
|
|
5103
4851
|
function requireAddress(target, segment, message = `unknown agent "${segment}" — addressing is scoped to your family tree (self and descendants)`) {
|
|
5104
|
-
if (target === void 0) throw new
|
|
4852
|
+
if (target === void 0) throw new UrlSchemesError("AGENT_UNKNOWN_ID", message);
|
|
5105
4853
|
return target;
|
|
5106
4854
|
}
|
|
5107
4855
|
/** The newest entry by creation time (live header, else persisted metadata). */
|
|
@@ -5157,7 +4905,7 @@ function requireLiveSession(target, family) {
|
|
|
5157
4905
|
}
|
|
5158
4906
|
/** A structured AGENT_UNKNOWN_ID with the label-ambiguity hint appended. */
|
|
5159
4907
|
function unknownId(target, message) {
|
|
5160
|
-
return new
|
|
4908
|
+
return new UrlSchemesError("AGENT_UNKNOWN_ID", `${message}${target.ambiguous === void 0 ? "" : `; label "${target.ambiguous.label}" is ambiguous (${target.ambiguous.matches} children share it) — address the raw session id to disambiguate`}`);
|
|
5161
4909
|
}
|
|
5162
4910
|
/**
|
|
5163
4911
|
* Roster table: one row per continuable descendant, in enumeration (stable
|
|
@@ -5248,277 +4996,385 @@ function renderBlock(block) {
|
|
|
5248
4996
|
}
|
|
5249
4997
|
|
|
5250
4998
|
//#endregion
|
|
5251
|
-
//#region src/url-
|
|
5252
|
-
|
|
5253
|
-
const
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
"cwd"
|
|
5257
|
-
];
|
|
5258
|
-
/** Build the `ctx://` scheme handler over the resolver env. */
|
|
5259
|
-
function createCtxHandler() {
|
|
5260
|
-
return { async resolve(env, path$1) {
|
|
5261
|
-
const key = path$1.replace(/^\/+/, "").trim();
|
|
5262
|
-
if (key === "") return KEYS.join("\n");
|
|
5263
|
-
const { agent } = env;
|
|
5264
|
-
if (agent === void 0) throw new UrlSchemaError("CTX_NO_AGENT", "ctx:// requires a live agent in the resolver env (this context has none)");
|
|
5265
|
-
switch (key) {
|
|
5266
|
-
case "session": return JSON.stringify({
|
|
5267
|
-
id: agent.id,
|
|
5268
|
-
status: agent.status,
|
|
5269
|
-
origin: agent.session.header.origin,
|
|
5270
|
-
delegationDepth: agent.session.header.delegationDepth
|
|
5271
|
-
}, null, 2);
|
|
5272
|
-
case "model": return JSON.stringify({
|
|
5273
|
-
provider: agent.options.provider,
|
|
5274
|
-
model: agent.options.model,
|
|
5275
|
-
maxTokens: agent.options.maxTokens
|
|
5276
|
-
}, null, 2);
|
|
5277
|
-
case "cwd": return agent.session.header.cwd ?? "";
|
|
5278
|
-
default: throw new UrlSchemaError("CTX_UNKNOWN_KEY", `ctx://${key}: unknown snapshot key (known: ${KEYS.join(", ")})`);
|
|
5279
|
-
}
|
|
5280
|
-
} };
|
|
4999
|
+
//#region src/url-schemes/handlers/ctx.ts
|
|
5000
|
+
function blocksOf(event) {
|
|
5001
|
+
const message = event.data?.message;
|
|
5002
|
+
const content = event.data?.content;
|
|
5003
|
+
return message?.content ?? content ?? [];
|
|
5281
5004
|
}
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
//#region src/url-schema/handlers/dsh.ts
|
|
5285
|
-
/** File extensions considered readable harness documentation. */
|
|
5286
|
-
const DOC_EXTENSIONS = {
|
|
5287
|
-
".md": true,
|
|
5288
|
-
".markdown": true,
|
|
5289
|
-
".txt": true
|
|
5290
|
-
};
|
|
5291
|
-
/**
|
|
5292
|
-
* Key-name patterns for the defensive secret denylist, matched against a
|
|
5293
|
-
* normalized key (lowercased, separators removed). Complements schema-declared
|
|
5294
|
-
* `role('secret')` redaction for fields a schema did not mark secret.
|
|
5295
|
-
*/
|
|
5296
|
-
const SECRET_KEY_PATTERNS = [
|
|
5297
|
-
/secret/,
|
|
5298
|
-
/password/,
|
|
5299
|
-
/passwd/,
|
|
5300
|
-
/credential/,
|
|
5301
|
-
/token/,
|
|
5302
|
-
/apikey/,
|
|
5303
|
-
/authorization/,
|
|
5304
|
-
/privatekey/,
|
|
5305
|
-
/accesskey/
|
|
5306
|
-
];
|
|
5307
|
-
/** True when a settings field name names credentials/env/API-key material. */
|
|
5308
|
-
function isSecretKey(key) {
|
|
5309
|
-
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
5310
|
-
if (normalized === "env" || normalized === "environment") return true;
|
|
5311
|
-
return SECRET_KEY_PATTERNS.some((re) => re.test(normalized));
|
|
5312
|
-
}
|
|
5313
|
-
/** Deep-copy a settings value, dropping every key the denylist names. */
|
|
5314
|
-
function stripSecrets(value) {
|
|
5315
|
-
if (Array.isArray(value)) return value.map(stripSecrets);
|
|
5316
|
-
if (value !== null && typeof value === "object") {
|
|
5317
|
-
const out = {};
|
|
5318
|
-
for (const [key, item] of Object.entries(value)) {
|
|
5319
|
-
if (isSecretKey(key)) continue;
|
|
5320
|
-
out[key] = stripSecrets(item);
|
|
5321
|
-
}
|
|
5322
|
-
return out;
|
|
5323
|
-
}
|
|
5324
|
-
return value;
|
|
5005
|
+
function textOf(blocks) {
|
|
5006
|
+
return blocks.filter((b) => b.type === "text").map((b) => b.text ?? "").join("");
|
|
5325
5007
|
}
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
const dot = name$2.lastIndexOf(".");
|
|
5329
|
-
if (dot === -1) return false;
|
|
5330
|
-
return DOC_EXTENSIONS[name$2.slice(dot).toLowerCase()] === true;
|
|
5008
|
+
function sourceKind(event) {
|
|
5009
|
+
return event.data?.source?.kind ?? "";
|
|
5331
5010
|
}
|
|
5332
|
-
|
|
5333
|
-
|
|
5011
|
+
function sourcePlugin(event) {
|
|
5012
|
+
return event.data?.source?.plugin ?? "";
|
|
5013
|
+
}
|
|
5014
|
+
function resultTextOf(event) {
|
|
5015
|
+
return blocksOf(event).flatMap((b) => b.content ?? []).map((c) => c.text ?? "").join("\n");
|
|
5016
|
+
}
|
|
5017
|
+
/** All reasoning blocks in event order (one message may carry several). */
|
|
5018
|
+
function thinkingItems(events) {
|
|
5334
5019
|
const out = [];
|
|
5335
|
-
const
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5020
|
+
for (const e of events) {
|
|
5021
|
+
if (e.type !== "assistant/message") continue;
|
|
5022
|
+
const turnStep = e.data;
|
|
5023
|
+
for (const b of blocksOf(e)) {
|
|
5024
|
+
if (b.type !== "reasoning") continue;
|
|
5025
|
+
out.push({
|
|
5026
|
+
seq: e.seq,
|
|
5027
|
+
turn: turnStep?.turn,
|
|
5028
|
+
step: turnStep?.step,
|
|
5029
|
+
text: b.text ?? ""
|
|
5030
|
+
});
|
|
5031
|
+
}
|
|
5342
5032
|
}
|
|
5343
5033
|
return out;
|
|
5344
5034
|
}
|
|
5345
|
-
/**
|
|
5346
|
-
|
|
5347
|
-
const
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
let stat$1;
|
|
5351
|
-
try {
|
|
5352
|
-
stat$1 = await promises.stat(abs);
|
|
5353
|
-
} catch {
|
|
5354
|
-
throw new UrlSchemaError("URL_DOC_NOT_FOUND", `dsh://docs/${docPath}: document not found`);
|
|
5355
|
-
}
|
|
5356
|
-
if (stat$1.isDirectory()) return JSON.stringify(await listDocs(abs), null, 2);
|
|
5357
|
-
if (stat$1.isFile()) return await promises.readFile(abs, "utf8");
|
|
5358
|
-
throw new UrlSchemaError("URL_DOC_NOT_FOUND", `dsh://docs/${docPath}: not a regular file`);
|
|
5359
|
-
}
|
|
5360
|
-
/** Create the `dsh://` scheme handler, capturing `deps` by closure. */
|
|
5361
|
-
function createDshHandler(deps) {
|
|
5362
|
-
const { settings, docsDir } = deps;
|
|
5363
|
-
let docsDirPromise;
|
|
5364
|
-
function findDocsDir() {
|
|
5365
|
-
if (docsDirPromise === void 0) docsDirPromise = (async () => {
|
|
5366
|
-
const candidates = [];
|
|
5367
|
-
if (docsDir !== void 0) candidates.push(docsDir);
|
|
5368
|
-
const pkgRoot = dirname(dirname(dirname(dirname(fileURLToPath(import.meta.url)))));
|
|
5369
|
-
candidates.push(join(pkgRoot, "docs"), join(pkgRoot, "..", "docs"));
|
|
5370
|
-
for (const dir of candidates) try {
|
|
5371
|
-
if ((await promises.stat(dir)).isDirectory()) return dir;
|
|
5372
|
-
} catch {}
|
|
5373
|
-
})();
|
|
5374
|
-
return docsDirPromise;
|
|
5375
|
-
}
|
|
5376
|
-
return { async resolve(_env, path$1) {
|
|
5377
|
-
const [root, ...rest] = path$1.split("/");
|
|
5378
|
-
const restPath = rest.join("/");
|
|
5379
|
-
if (root === "docs") {
|
|
5380
|
-
const dir = await findDocsDir();
|
|
5381
|
-
if (dir === void 0) throw new UrlSchemaError("URL_DOCS_UNAVAILABLE", "dsh://docs: no docs directory found (provide `docsDir` to createDshHandler)");
|
|
5382
|
-
if (restPath === "") return JSON.stringify(await listDocs(dir), null, 2);
|
|
5383
|
-
return await readDoc(dir, restPath);
|
|
5384
|
-
}
|
|
5385
|
-
if (root === "config") {
|
|
5386
|
-
if (settings === void 0) throw new UrlSchemaError("URL_SETTINGS_UNAVAILABLE", "dsh://config: no ctx.settings service is mounted");
|
|
5387
|
-
const descriptors = settings.describe({ redactSecrets: true });
|
|
5388
|
-
if (restPath === "") {
|
|
5389
|
-
const out = {};
|
|
5390
|
-
for (const descriptor of descriptors) out[String(descriptor.ns)] = stripSecrets(descriptor.value);
|
|
5391
|
-
return JSON.stringify(out, null, 2);
|
|
5392
|
-
}
|
|
5393
|
-
const found = descriptors.find((descriptor) => String(descriptor.ns) === restPath);
|
|
5394
|
-
if (found === void 0) throw new UrlSchemaError("URL_UNKNOWN_SETTINGS_NAMESPACE", `dsh://config/${restPath}: unknown settings namespace`);
|
|
5395
|
-
return JSON.stringify(stripSecrets(found.value), null, 2);
|
|
5396
|
-
}
|
|
5397
|
-
throw new UrlSchemaError("URL_UNKNOWN_RESOURCE", `dsh://: unknown resource "${root || "(empty)"}" — expected "docs" or "config"`);
|
|
5398
|
-
} };
|
|
5035
|
+
/** A system message: `data.text` when present, else the message content blocks. */
|
|
5036
|
+
function systemTextOf(event) {
|
|
5037
|
+
const d = event.data;
|
|
5038
|
+
if (typeof d?.text === "string") return d.text;
|
|
5039
|
+
return textOf(d?.message?.content ?? d?.content ?? []);
|
|
5399
5040
|
}
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
/** `
|
|
5412
|
-
|
|
5413
|
-
"
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
};
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
function
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
});
|
|
5447
|
-
if (done) break;
|
|
5448
|
-
received += value.byteLength;
|
|
5449
|
-
if (received > MAX_BODY_BYTES) throw new UrlSchemaError("URL_HTTP_TOO_LARGE", `body of ${href} exceeded the ${MAX_BODY_BYTES}-byte (2 MiB) text limit after ${received} bytes`);
|
|
5450
|
-
text += decoder.decode(value, { stream: true });
|
|
5451
|
-
}
|
|
5452
|
-
return text + decoder.decode();
|
|
5453
|
-
} finally {
|
|
5454
|
-
await reader.cancel().catch(() => {});
|
|
5041
|
+
/** All `system/message` events in log order. */
|
|
5042
|
+
function systemItems(events) {
|
|
5043
|
+
return events.filter((e) => e.type === "system/message").map((e) => {
|
|
5044
|
+
const source = [sourceKind(e), sourcePlugin(e)].filter((p) => p !== "").join("/");
|
|
5045
|
+
return {
|
|
5046
|
+
seq: e.seq,
|
|
5047
|
+
source: source === "" ? "-" : source,
|
|
5048
|
+
text: systemTextOf(e)
|
|
5049
|
+
};
|
|
5050
|
+
});
|
|
5051
|
+
}
|
|
5052
|
+
/** All injected `user/message` events in log order (the complement of `user_prompts`). */
|
|
5053
|
+
function injectionItems(events) {
|
|
5054
|
+
return events.filter((e) => e.type === "user/message" && sourceKind(e) !== "user").map((e) => {
|
|
5055
|
+
const kind = sourceKind(e) || "unknown";
|
|
5056
|
+
return {
|
|
5057
|
+
seq: e.seq,
|
|
5058
|
+
source: kind,
|
|
5059
|
+
text: `[${String(e.seq).padStart(7, "0")}] INJECTED ${kind}\n${textOf(blocksOf(e))}`
|
|
5060
|
+
};
|
|
5061
|
+
});
|
|
5062
|
+
}
|
|
5063
|
+
/** ~100-char single-line preview with an ellipsis when truncated. */
|
|
5064
|
+
function previewOf(text, n = 100) {
|
|
5065
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
5066
|
+
return flat.length > n ? flat.slice(0, n) + "…" : flat;
|
|
5067
|
+
}
|
|
5068
|
+
function renderEntry(event, toolNameOf) {
|
|
5069
|
+
const seq = String(event.seq ?? 0).padStart(7, "0");
|
|
5070
|
+
if (event.type === "user/message") {
|
|
5071
|
+
if (sourcePlugin(event) === "compact") return `[${seq}] CHECKPOINT\n${textOf(blocksOf(event))}`;
|
|
5072
|
+
if (sourceKind(event) === "user") return `[${seq}] USER\n${textOf(blocksOf(event))}`;
|
|
5073
|
+
return `[${seq}] USER-INJECTED (${sourcePlugin(event) || sourceKind(event)})\n${textOf(blocksOf(event))}`;
|
|
5074
|
+
}
|
|
5075
|
+
if (event.type === "assistant/message") {
|
|
5076
|
+
const blocks = blocksOf(event);
|
|
5077
|
+
const text = textOf(blocks);
|
|
5078
|
+
const calls = blocks.filter((b) => b.type === "tool-call");
|
|
5079
|
+
const parts = [];
|
|
5080
|
+
if (text !== "") parts.push(`[${seq}] ASSISTANT\n${text}`);
|
|
5081
|
+
for (const call of calls) parts.push(`[${seq}] TOOL-CALL ${call.name ?? "?"}\n${call.arguments ?? ""}`);
|
|
5082
|
+
return parts.length > 0 ? parts.join("\n\n") : `[${seq}] ASSISTANT (no content)`;
|
|
5083
|
+
}
|
|
5084
|
+
if (event.type === "tool/result") {
|
|
5085
|
+
const callSeq = event.sourceEventSeqs?.[0];
|
|
5086
|
+
return `[${seq}] TOOL ${toolNameOf(callSeq) ?? "unknown-tool"}\n${resultTextOf(event)}`;
|
|
5455
5087
|
}
|
|
5088
|
+
return "";
|
|
5456
5089
|
}
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
const
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5090
|
+
function sectionPreviews(text, n = 100) {
|
|
5091
|
+
const out = {};
|
|
5092
|
+
for (const part of text.split(/^## /m).slice(1)) {
|
|
5093
|
+
const head = part.slice(0, part.indexOf("\n")).trim();
|
|
5094
|
+
out[head] = part.slice(part.indexOf("\n") + 1).replace(/\s+/g, " ").trim().slice(0, n);
|
|
5095
|
+
}
|
|
5096
|
+
return out;
|
|
5097
|
+
}
|
|
5098
|
+
function applyLines(canonical, ranges) {
|
|
5099
|
+
const lines = canonical.split("\n");
|
|
5100
|
+
return ranges.map(([a, b]) => lines.slice(Math.max(0, a - 1), b).join("\n")).join("\n");
|
|
5101
|
+
}
|
|
5102
|
+
function applyFace(prepared, canonical, selector, face = "prepared") {
|
|
5103
|
+
if (selector === void 0 || selector === null) return face === "canonical" ? canonical : prepared;
|
|
5104
|
+
if (selector.kind === "raw") return canonical;
|
|
5105
|
+
if (selector.kind === "lines") return applyLines(canonical, selector.ranges);
|
|
5106
|
+
throw new UrlSchemesError("CTX_BAD_SELECTOR", `ctx://: unsupported selector kind "${selector.kind}"`);
|
|
5107
|
+
}
|
|
5108
|
+
function createCtxHandler(deps) {
|
|
5109
|
+
const persistence = deps.sessionPersistence;
|
|
5110
|
+
async function loadEvents(agent) {
|
|
5111
|
+
if (persistence === void 0) throw new UrlSchemesError("CTX_NO_PERSISTENCE", "ctx:// recall requires the host session-persistence service (not mounted in this composition)");
|
|
5112
|
+
const handle = await persistence.open(agent.id, "read");
|
|
5467
5113
|
try {
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
signal: controller.signal
|
|
5472
|
-
});
|
|
5473
|
-
} catch (err) {
|
|
5474
|
-
if (timedOut || err instanceof Error && err.name === "AbortError") throw new UrlSchemaError("URL_HTTP_TIMEOUT", `HTTP GET ${url.href} aborted after the ${TIMEOUT_MS / 1e3} s deadline`);
|
|
5475
|
-
const cause = causeMessageOf(err);
|
|
5476
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
5477
|
-
throw new UrlSchemaError("URL_HTTP_FETCH_FAILED", `HTTP GET ${url.href} failed: ${message}${cause === null ? "" : ` (${cause})`}`);
|
|
5478
|
-
}
|
|
5479
|
-
if (!response.ok) {
|
|
5480
|
-
const statusText = response.statusText === "" ? "" : ` ${response.statusText}`;
|
|
5481
|
-
throw new UrlSchemaError("URL_HTTP_STATUS", `HTTP GET ${url.href} returned ${response.status}${statusText}`);
|
|
5482
|
-
}
|
|
5483
|
-
const declared = response.headers.get("content-length");
|
|
5484
|
-
if (declared !== null) {
|
|
5485
|
-
const bytes = Number(declared);
|
|
5486
|
-
if (Number.isFinite(bytes) && bytes > MAX_BODY_BYTES) throw new UrlSchemaError("URL_HTTP_TOO_LARGE", `body of ${url.href} is ${bytes} bytes, over the ${MAX_BODY_BYTES}-byte (2 MiB) text limit`);
|
|
5487
|
-
}
|
|
5488
|
-
const contentType = response.headers.get("content-type");
|
|
5489
|
-
if (!isTextContentType(contentType)) throw new UrlSchemaError("URL_HTTP_UNSUPPORTED_MEDIA", `content-type of ${url.href} is "${contentType ?? "(none)"}", outside the text whitelist (text/* or application/{json,xml,yaml,toml,xhtml+xml,javascript,plain}) — binary decoding is deliberately not guessed`);
|
|
5490
|
-
if (response.body === null) {
|
|
5491
|
-
const text = await response.text();
|
|
5492
|
-
const bytes = Buffer.byteLength(text, "utf8");
|
|
5493
|
-
if (bytes > MAX_BODY_BYTES) throw new UrlSchemaError("URL_HTTP_TOO_LARGE", `body of ${url.href} is ${bytes} bytes, over the ${MAX_BODY_BYTES}-byte (2 MiB) text limit`);
|
|
5494
|
-
return text;
|
|
5114
|
+
return (await handle.read()).events ?? [];
|
|
5115
|
+
} finally {
|
|
5116
|
+
await handle.close().catch(() => {});
|
|
5495
5117
|
}
|
|
5496
|
-
return await readBodyCapped(response.body, url.href);
|
|
5497
|
-
} finally {
|
|
5498
|
-
clearTimeout(timer);
|
|
5499
5118
|
}
|
|
5119
|
+
return {
|
|
5120
|
+
selectorAware: true,
|
|
5121
|
+
async resolve(env, path$1, selector) {
|
|
5122
|
+
const sel = selector ?? null;
|
|
5123
|
+
const raw = path$1.replace(/^\/+/, "").trim();
|
|
5124
|
+
if (raw === "") return [
|
|
5125
|
+
"ctx://session statistics snapshot of this session (:raw = full transcript)",
|
|
5126
|
+
"ctx://session/transcript full session transcript (line windows via :N-M)",
|
|
5127
|
+
"ctx://session/compactions compaction manifest (labels, nested chain, previews)",
|
|
5128
|
+
"ctx://session/compactions[<label|n>] one episode's summary (:raw[:N-M] = its original span)",
|
|
5129
|
+
"ctx://session/user_prompts[<n|seq>] the n-th real user prompt (0-based)",
|
|
5130
|
+
"ctx://session/tool_calls[<n|seq>] the n-th tool call (name + arguments + result)",
|
|
5131
|
+
"ctx://session/agent_responses[<n|seq>] the n-th assistant response",
|
|
5132
|
+
"ctx://session/thinking[<n|seq>] the n-th reasoning block (:raw = all blocks joined)",
|
|
5133
|
+
"ctx://session/system[<n|seq>] the n-th system message (:raw = all messages)",
|
|
5134
|
+
"ctx://session/injections[<n|seq>] the n-th injected user message (:raw = all joined)"
|
|
5135
|
+
].join("\n");
|
|
5136
|
+
const { agent } = env;
|
|
5137
|
+
if (agent === void 0) throw new UrlSchemesError("CTX_NO_AGENT", "ctx:// requires a live agent in the resolver env (this context has none)");
|
|
5138
|
+
if (raw.split("/")[0] !== "session") throw new UrlSchemesError("CTX_UNKNOWN_KEY", `ctx://${raw}: unknown key (known: session — model/cwd folded into the session info card; bare ctx:// lists the full roster)`);
|
|
5139
|
+
const events = await loadEvents(agent);
|
|
5140
|
+
const toolCallEvents = events.filter((e) => e.type === "tool/call");
|
|
5141
|
+
const toolNameByCallSeq = new Map(toolCallEvents.map((e) => [e.seq, e.data.name ?? "?"]));
|
|
5142
|
+
const toolNameOf = (seq) => seq === void 0 ? void 0 : toolNameByCallSeq.get(seq);
|
|
5143
|
+
const userPromptEvents = events.filter((e) => e.type === "user/message" && sourceKind(e) === "user");
|
|
5144
|
+
const agentResponseEvents = events.filter((e) => e.type === "assistant/message");
|
|
5145
|
+
const transcript = () => events.map((e) => renderEntry(e, toolNameOf)).filter((t) => t !== "").join("\n\n");
|
|
5146
|
+
const episodes = [];
|
|
5147
|
+
{
|
|
5148
|
+
let prevCheckpoint = null;
|
|
5149
|
+
const nameOf = toolNameOf;
|
|
5150
|
+
for (const e of events) {
|
|
5151
|
+
if (e.type !== "compaction/summary") continue;
|
|
5152
|
+
const d = e.data;
|
|
5153
|
+
const originalText = d.shadowedSeqs.map((seq) => events.find((ev) => ev.seq === seq)).filter((ev) => ev !== void 0).map((ev) => renderEntry(ev, nameOf)).filter((t) => t !== "").join("\n\n");
|
|
5154
|
+
episodes.push({
|
|
5155
|
+
label: e.seq,
|
|
5156
|
+
checkpointSeq: e.seq + 1,
|
|
5157
|
+
compactionId: d.compactionId,
|
|
5158
|
+
at: e.time,
|
|
5159
|
+
shadowedRange: d.shadowedRange,
|
|
5160
|
+
shadowedSeqs: d.shadowedSeqs,
|
|
5161
|
+
shadowedTokenCount: d.shadowedTokenCount,
|
|
5162
|
+
replacesCheckpoint: prevCheckpoint !== null && d.shadowedSeqs.includes(prevCheckpoint) ? prevCheckpoint : null,
|
|
5163
|
+
summaryText: d.summary.map((s) => s.text).join(""),
|
|
5164
|
+
originalText
|
|
5165
|
+
});
|
|
5166
|
+
prevCheckpoint = e.seq + 1;
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
5169
|
+
const sub = raw.slice(7).replace(/^\/+/, "");
|
|
5170
|
+
const segments = sub === "" ? [] : sub.split("/").filter((x) => x !== "");
|
|
5171
|
+
const parseSeg = (seg) => {
|
|
5172
|
+
const m = /^([a-z_]+)(?:\[(.+)\])?$/.exec(seg);
|
|
5173
|
+
if (m === null) throw new UrlSchemesError("CTX_BAD_PATH", `ctx://${raw}: unparseable segment "${seg}"`);
|
|
5174
|
+
return {
|
|
5175
|
+
name: m[1],
|
|
5176
|
+
bracket: m[2]
|
|
5177
|
+
};
|
|
5178
|
+
};
|
|
5179
|
+
const seg0 = segments[0] === void 0 ? void 0 : parseSeg(segments[0]);
|
|
5180
|
+
const seg1 = segments[1] === void 0 ? void 0 : parseSeg(segments[1]);
|
|
5181
|
+
if (seg0 === void 0 || seg0.name === "transcript") {
|
|
5182
|
+
seg0?.name;
|
|
5183
|
+
const canonical = transcript();
|
|
5184
|
+
return applyFace(buildSnapshot(agent, events, episodes), canonical, sel, seg0?.name === "transcript" ? "canonical" : "prepared");
|
|
5185
|
+
}
|
|
5186
|
+
if (seg0.name === "compactions") {
|
|
5187
|
+
if (seg0.bracket === void 0) {
|
|
5188
|
+
if (sel !== null && sel.kind !== "raw") throw new UrlSchemesError("CTX_BAD_SELECTOR", "ctx://session/compactions: only :raw (or no selector) is supported on the manifest");
|
|
5189
|
+
const body = episodes.map((e) => [
|
|
5190
|
+
`label=${e.label} (checkpoint_seq=${e.checkpointSeq})`,
|
|
5191
|
+
`compactionId=${e.compactionId}`,
|
|
5192
|
+
`at=${new Date(e.at ?? 0).toISOString()}`,
|
|
5193
|
+
`shadowed=${e.shadowedRange.start}..${e.shadowedRange.end} (${e.shadowedSeqs.length} items, ${e.shadowedTokenCount} tokens)`,
|
|
5194
|
+
`replaces_checkpoint=${e.replacesCheckpoint ?? "null"}`,
|
|
5195
|
+
`preview=${e.summaryText.slice(0, 120)}…`
|
|
5196
|
+
].join(" | ")).join("\n");
|
|
5197
|
+
return body === "" ? "(no compactions recorded)" : body;
|
|
5198
|
+
}
|
|
5199
|
+
const episode = pickEpisode(episodes, seg0.bracket);
|
|
5200
|
+
if (seg1 === void 0) {
|
|
5201
|
+
if (sel !== null && sel.kind === "raw" && episode.originalText.length > 65536) return episode.originalText + `\n\n[ctx:// note: original span is ${episode.originalText.length} chars — page with :N-M line windows or grep this URL]`;
|
|
5202
|
+
return applyFace(episode.summaryText, episode.originalText, sel, "prepared");
|
|
5203
|
+
}
|
|
5204
|
+
throw new UrlSchemesError("CTX_BAD_PATH", `ctx://${raw}: unknown sub-path "/${seg1.name}" ("/original" was superseded by the :raw / :raw:N-M selectors)`);
|
|
5205
|
+
}
|
|
5206
|
+
if (seg0.name === "thinking" || seg0.name === "system" || seg0.name === "injections") {
|
|
5207
|
+
const items = seg0.name === "thinking" ? thinkingItems(events) : seg0.name === "system" ? systemItems(events) : injectionItems(events);
|
|
5208
|
+
if (seg0.bracket === void 0) {
|
|
5209
|
+
const index = items.map((it, i) => seg0.name === "thinking" ? `[${i}] seq=${it.seq} turn=${it.turn ?? "-"} step=${it.step ?? "-"} ${previewOf(it.text)}` : `seq=${it.seq} source=${it.source ?? "-"} ${previewOf(it.text)}`).join("\n");
|
|
5210
|
+
return applyFace(index === "" ? `(no ${seg0.name} items recorded)` : index, items.map((it) => it.text).join("\n\n"), sel, "prepared");
|
|
5211
|
+
}
|
|
5212
|
+
const bySeq = items.find((it) => it.seq === Number(seg0.bracket));
|
|
5213
|
+
if (bySeq !== void 0) return applyFace(bySeq.text, bySeq.text, sel, "prepared");
|
|
5214
|
+
const ordinal = Number(seg0.bracket);
|
|
5215
|
+
if (!Number.isInteger(ordinal) || ordinal < 0 || ordinal >= items.length) throw new UrlSchemesError("CTX_NO_SUCH_ELEMENT", `ctx://…[${seg0.bracket}]: no such element (collection "${seg0.name}" has ${items.length} items, 0-based; labels are event seqs)`);
|
|
5216
|
+
return applyFace(items[ordinal].text, items[ordinal].text, sel, "prepared");
|
|
5217
|
+
}
|
|
5218
|
+
const collection = (name$2) => {
|
|
5219
|
+
if (name$2 === "user_prompts") return userPromptEvents.map((e) => ({
|
|
5220
|
+
seq: e.seq,
|
|
5221
|
+
text: `[${String(e.seq).padStart(7, "0")}] USER\n${textOf(blocksOf(e))}`
|
|
5222
|
+
}));
|
|
5223
|
+
if (name$2 === "tool_calls") {
|
|
5224
|
+
const resultByCallSeq = /* @__PURE__ */ new Map();
|
|
5225
|
+
for (const e of events) {
|
|
5226
|
+
if (e.type !== "tool/result") continue;
|
|
5227
|
+
const callSeq = e.sourceEventSeqs?.[0];
|
|
5228
|
+
if (callSeq !== void 0) resultByCallSeq.set(callSeq, resultTextOf(e));
|
|
5229
|
+
}
|
|
5230
|
+
return toolCallEvents.map((e) => ({
|
|
5231
|
+
seq: e.seq,
|
|
5232
|
+
text: `[${String(e.seq).padStart(7, "0")}] TOOL ${e.data.name ?? "?"}\narguments: ${e.data.arguments ?? ""}\nresult: ${resultByCallSeq.get(e.seq) ?? "(no result captured)"}`
|
|
5233
|
+
}));
|
|
5234
|
+
}
|
|
5235
|
+
if (name$2 === "agent_responses") return agentResponseEvents.map((e) => ({
|
|
5236
|
+
seq: e.seq,
|
|
5237
|
+
text: `[${String(e.seq).padStart(7, "0")}] ASSISTANT\n${textOf(blocksOf(e)) || "(tool-call only)"}`
|
|
5238
|
+
}));
|
|
5239
|
+
throw new UrlSchemesError("CTX_BAD_PATH", `ctx://${raw}: unknown collection "${name$2}"`);
|
|
5240
|
+
};
|
|
5241
|
+
if (seg0.bracket !== void 0 || seg0.name === "user_prompts" || seg0.name === "tool_calls" || seg0.name === "agent_responses") {
|
|
5242
|
+
const items = collection(seg0.name);
|
|
5243
|
+
const bracket = seg0.bracket ?? "";
|
|
5244
|
+
const bySeq = items.find((it) => it.seq === Number(bracket));
|
|
5245
|
+
if (bySeq !== void 0) return applyFace(bySeq.text, bySeq.text, sel, "prepared");
|
|
5246
|
+
const ordinal = Number(bracket);
|
|
5247
|
+
if (bracket === "" || !Number.isInteger(ordinal) || ordinal < 0 || ordinal >= items.length) throw new UrlSchemesError("CTX_NO_SUCH_ELEMENT", `ctx://…[${bracket}]: no such element (collection "${seg0.name}" has ${items.length} items, 0-based; labels are event seqs)`);
|
|
5248
|
+
return applyFace(items[ordinal].text, items[ordinal].text, sel, "prepared");
|
|
5249
|
+
}
|
|
5250
|
+
throw new UrlSchemesError("CTX_UNKNOWN_KEY", `ctx://${raw}: unknown path (known: session, session/transcript, session/compactions[...], session/user_prompts[...], session/tool_calls[...], session/agent_responses[...], session/thinking[...], session/system[...], session/injections[...]; bare ctx:// lists the full roster)`);
|
|
5251
|
+
function pickEpisode(list, bracket) {
|
|
5252
|
+
const byLabel = list.find((e) => String(e.label) === bracket);
|
|
5253
|
+
if (byLabel !== void 0) return byLabel;
|
|
5254
|
+
const ordinal = Number(bracket);
|
|
5255
|
+
if (Number.isInteger(ordinal) && ordinal >= 0 && ordinal < list.length) return list[ordinal];
|
|
5256
|
+
throw new UrlSchemesError("CTX_NO_SUCH_ELEMENT", `ctx://…[${bracket}]: no such compaction episode (labels: ${list.map((e) => e.label).join(", ") || "none"})`);
|
|
5257
|
+
}
|
|
5258
|
+
function buildSnapshot(agent$1, all, list) {
|
|
5259
|
+
const headers = all.filter((e) => e.type === "request/header");
|
|
5260
|
+
const system = (headers[headers.length - 1]?.data)?.header?.system;
|
|
5261
|
+
const counts = {};
|
|
5262
|
+
for (const e of all) if (e.type === "user/message") {
|
|
5263
|
+
const k = sourceKind(e) === "user" ? "user_prompts" : "injected_user_messages";
|
|
5264
|
+
counts[k] = (counts[k] ?? 0) + 1;
|
|
5265
|
+
} else if (e.type === "assistant/message") {
|
|
5266
|
+
counts.agent_messages = (counts.agent_messages ?? 0) + 1;
|
|
5267
|
+
for (const b of blocksOf(e)) counts[`block_${b.type}`] = (counts[`block_${b.type}`] ?? 0) + 1;
|
|
5268
|
+
} else if (e.type === "tool/call") counts.tool_calls = (counts.tool_calls ?? 0) + 1;
|
|
5269
|
+
else if (e.type === "tool/result") counts.tool_results = (counts.tool_results ?? 0) + 1;
|
|
5270
|
+
counts.compactions = list.length;
|
|
5271
|
+
for (const key of [
|
|
5272
|
+
"user_prompts",
|
|
5273
|
+
"injected_user_messages",
|
|
5274
|
+
"agent_messages",
|
|
5275
|
+
"block_reasoning",
|
|
5276
|
+
"block_text",
|
|
5277
|
+
"block_tool-call",
|
|
5278
|
+
"tool_calls",
|
|
5279
|
+
"tool_results"
|
|
5280
|
+
]) counts[key] = counts[key] ?? 0;
|
|
5281
|
+
const header = agent$1.session.header;
|
|
5282
|
+
return JSON.stringify({
|
|
5283
|
+
resource: "ctx://session",
|
|
5284
|
+
syntax: "/sub-path [<label|n>] [:N-M]; :raw = canonical full content; :raw:N-M ≡ :N-M",
|
|
5285
|
+
session: {
|
|
5286
|
+
id: header.id ?? agent$1.id,
|
|
5287
|
+
createdAt: header.createdAt,
|
|
5288
|
+
cwd: header.cwd,
|
|
5289
|
+
agentPreset: header.agentPreset,
|
|
5290
|
+
status: agent$1.status,
|
|
5291
|
+
origin: header.origin,
|
|
5292
|
+
delegationDepth: header.delegationDepth
|
|
5293
|
+
},
|
|
5294
|
+
storage: { format: "session.jsonl.zstd (read via sessionPersistence)" },
|
|
5295
|
+
totals: counts,
|
|
5296
|
+
segments: [...list.map((e) => ({
|
|
5297
|
+
segment: `compaction:${e.label}`,
|
|
5298
|
+
start: e.shadowedRange?.start ?? null,
|
|
5299
|
+
end: e.shadowedRange?.end ?? null,
|
|
5300
|
+
items: e.shadowedSeqs.length
|
|
5301
|
+
})), (() => {
|
|
5302
|
+
const lastSeq = all.reduce((m, e) => Math.max(m, e.seq), 0);
|
|
5303
|
+
const start = (list.at(-1)?.shadowedRange?.end ?? -1) + 1;
|
|
5304
|
+
return {
|
|
5305
|
+
segment: "live",
|
|
5306
|
+
start,
|
|
5307
|
+
end: lastSeq,
|
|
5308
|
+
items: all.filter((e) => e.seq >= start).length
|
|
5309
|
+
};
|
|
5310
|
+
})()],
|
|
5311
|
+
system_prompt: {
|
|
5312
|
+
chars: system?.length ?? 0,
|
|
5313
|
+
preview: system === void 0 ? "" : system.slice(0, 200)
|
|
5314
|
+
},
|
|
5315
|
+
compacted: list.map((e) => ({
|
|
5316
|
+
label: e.label,
|
|
5317
|
+
checkpoint_seq: e.checkpointSeq,
|
|
5318
|
+
compactionId: e.compactionId,
|
|
5319
|
+
at: e.at,
|
|
5320
|
+
shadowed_range: e.shadowedRange,
|
|
5321
|
+
shadowed_items: e.shadowedSeqs.length,
|
|
5322
|
+
shadowed_token_count: e.shadowedTokenCount,
|
|
5323
|
+
replaces_checkpoint: e.replacesCheckpoint,
|
|
5324
|
+
summary_preview: sectionPreviews(e.summaryText)
|
|
5325
|
+
})),
|
|
5326
|
+
hints: [
|
|
5327
|
+
"ctx://session:raw → full transcript",
|
|
5328
|
+
"ctx://session/compactions[<label>]:raw → original span",
|
|
5329
|
+
"ctx://session/thinking / ctx://session/system / ctx://session/injections → index-faced collections"
|
|
5330
|
+
]
|
|
5331
|
+
}, void 0, 2);
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
};
|
|
5500
5335
|
}
|
|
5336
|
+
|
|
5337
|
+
//#endregion
|
|
5338
|
+
//#region src/url-schemes/general-section.ts
|
|
5339
|
+
const GENERAL_SECTION_NAME = "url-schema:general";
|
|
5340
|
+
const GENERAL_SECTION_ORDER = 129;
|
|
5341
|
+
/** The section file's name, resolved against the nearest ancestor directory. */
|
|
5342
|
+
const SECTION_FILE = "url-schemes-instruction.md";
|
|
5501
5343
|
/**
|
|
5502
|
-
*
|
|
5503
|
-
*
|
|
5504
|
-
*
|
|
5344
|
+
* Load the section text at module load, walking up to the nearest copy.
|
|
5345
|
+
*
|
|
5346
|
+
* A fixed-depth `new URL('../…', import.meta.url)` breaks here: tsdown
|
|
5347
|
+
* inlines this module into `lib/index.js`, so the depth from the module to
|
|
5348
|
+
* the package root differs between source (`src/url-schemes/general-section.ts`)
|
|
5349
|
+
* and build (`lib/index.js`) — the same reason `docs-dir.ts` walks up. Near
|
|
5350
|
+
* layers are probed first, so an unrelated ancestor copy is only ever reached
|
|
5351
|
+
* when the package's own file is absent.
|
|
5505
5352
|
*/
|
|
5506
|
-
function
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5353
|
+
function loadSectionText() {
|
|
5354
|
+
const here$1 = fileURLToPath(import.meta.url);
|
|
5355
|
+
let dir = dirname(here$1);
|
|
5356
|
+
for (;;) {
|
|
5510
5357
|
try {
|
|
5511
|
-
|
|
5512
|
-
} catch
|
|
5513
|
-
|
|
5514
|
-
}
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5358
|
+
return readFileSync(join(dir, SECTION_FILE), "utf8");
|
|
5359
|
+
} catch {}
|
|
5360
|
+
const parent = dirname(dir);
|
|
5361
|
+
if (parent === dir) throw new Error(`${SECTION_FILE} not found from ${here$1} — it must ship with the package (package.json "files")`);
|
|
5362
|
+
dir = parent;
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
const SECTION_TEXT = loadSectionText();
|
|
5366
|
+
/** Render the section when the URL capability is on; `undefined` gates it off. */
|
|
5367
|
+
function generalSection(gates) {
|
|
5368
|
+
if (!gates.urlSchemes) return void 0;
|
|
5369
|
+
return {
|
|
5370
|
+
name: GENERAL_SECTION_NAME,
|
|
5371
|
+
order: GENERAL_SECTION_ORDER,
|
|
5372
|
+
text: SECTION_TEXT
|
|
5373
|
+
};
|
|
5518
5374
|
}
|
|
5519
5375
|
|
|
5520
5376
|
//#endregion
|
|
5521
|
-
//#region src/url-
|
|
5377
|
+
//#region src/url-schemes/handlers/skill.ts
|
|
5522
5378
|
/** Split `path` into `{ name, subpath }`; `subpath` is `null` for a bare name. */
|
|
5523
5379
|
function splitSkillPath(path$1) {
|
|
5524
5380
|
const slash = path$1.indexOf("/");
|
|
@@ -5536,22 +5392,24 @@ function splitSkillPath(path$1) {
|
|
|
5536
5392
|
function resolveResourcePath(baseDir, subpath) {
|
|
5537
5393
|
const full = join(baseDir, subpath);
|
|
5538
5394
|
const rel = relative(baseDir, full);
|
|
5539
|
-
if (rel === "" || rel === ".." || rel.startsWith("../")) throw new
|
|
5395
|
+
if (rel === "" || rel === ".." || rel.startsWith("../")) throw new UrlSchemesError("URL_SKILL_RESOURCE_ESCAPE", `skill resource path "${subpath}" escapes its directory`);
|
|
5540
5396
|
return full;
|
|
5541
5397
|
}
|
|
5542
5398
|
/**
|
|
5543
|
-
* Registry lookup options for one env
|
|
5544
|
-
* the
|
|
5545
|
-
*
|
|
5546
|
-
*
|
|
5547
|
-
* cwd-less
|
|
5399
|
+
* Registry lookup options for one env — the exact call shape of
|
|
5400
|
+
* `dsh-tool-skill`: the calling agent is the viewing `scope` (the agent is
|
|
5401
|
+
* its own scope key, so the layered registry resolves precisely as this
|
|
5402
|
+
* agent's composition sees it) and the workspace cwd comes from the agent's
|
|
5403
|
+
* session header. No agent in the env → `undefined` (the cwd-less global
|
|
5404
|
+
* view); the handler then reports the registry's own verdict.
|
|
5548
5405
|
*/
|
|
5549
5406
|
function lookupOptions(env) {
|
|
5550
|
-
const
|
|
5551
|
-
if (
|
|
5407
|
+
const agent = env.agent;
|
|
5408
|
+
if (agent === void 0) return void 0;
|
|
5409
|
+
const cwd = agent.session.header.cwd;
|
|
5552
5410
|
return {
|
|
5553
5411
|
...cwd === void 0 ? {} : { cwd },
|
|
5554
|
-
|
|
5412
|
+
scope: agent
|
|
5555
5413
|
};
|
|
5556
5414
|
}
|
|
5557
5415
|
/**
|
|
@@ -5565,9 +5423,9 @@ function createSkillHandler(deps) {
|
|
|
5565
5423
|
async resolve(env, path$1) {
|
|
5566
5424
|
const { name: name$2, subpath } = splitSkillPath(path$1);
|
|
5567
5425
|
const skill = await deps.skills.get(name$2, lookupOptions(env));
|
|
5568
|
-
if (skill === void 0) throw new
|
|
5426
|
+
if (skill === void 0) throw new UrlSchemesError("URL_SKILL_NOT_FOUND", `skill "${name$2}" is unknown or no longer available`);
|
|
5569
5427
|
if (subpath === null) return skill.content;
|
|
5570
|
-
if (skill.resourceBase === void 0 || skill.resourceBase.kind !== "directory") throw new
|
|
5428
|
+
if (skill.resourceBase === void 0 || skill.resourceBase.kind !== "directory") throw new UrlSchemesError("URL_SKILL_NO_RESOURCE_BASE", `skill "${name$2}" has no filesystem resource directory`);
|
|
5571
5429
|
const resourcePath = resolveResourcePath(skill.resourceBase.path, subpath);
|
|
5572
5430
|
const target = await deps.fs.resolve(resourcePath);
|
|
5573
5431
|
return await deps.fs.readText(target);
|
|
@@ -5589,9 +5447,10 @@ function createSkillHandler(deps) {
|
|
|
5589
5447
|
}
|
|
5590
5448
|
|
|
5591
5449
|
//#endregion
|
|
5592
|
-
//#region src/url-
|
|
5593
|
-
/** Tool names the wrappers shadow and delegate back to. */
|
|
5450
|
+
//#region src/url-schemes/native-capture.ts
|
|
5451
|
+
/** Tool names the chassis/wrappers shadow and delegate back to. */
|
|
5594
5452
|
const NATIVE_TOOL_NAMES = [
|
|
5453
|
+
"read",
|
|
5595
5454
|
"write",
|
|
5596
5455
|
"grep",
|
|
5597
5456
|
"glob"
|
|
@@ -5652,50 +5511,7 @@ function captureNativeTools(ctx, agent) {
|
|
|
5652
5511
|
}
|
|
5653
5512
|
|
|
5654
5513
|
//#endregion
|
|
5655
|
-
//#region src/url-
|
|
5656
|
-
/** Scheme→handler registry that resolves `scheme://` URLs end-to-end. */
|
|
5657
|
-
var UrlResolver = class {
|
|
5658
|
-
handlers = /* @__PURE__ */ new Map();
|
|
5659
|
-
/** Register (or replace) the handler for `scheme`. */
|
|
5660
|
-
register(scheme, handler) {
|
|
5661
|
-
this.handlers.set(scheme, handler);
|
|
5662
|
-
}
|
|
5663
|
-
/**
|
|
5664
|
-
* Resolve a `scheme://` URL to its selected text: parse the URL, dispatch
|
|
5665
|
-
* to the registered handler for the scheme, then apply the selector.
|
|
5666
|
-
*
|
|
5667
|
-
* Throws a structured {@link UrlSchemaError} for a scheme-less URL
|
|
5668
|
-
* (`URL_NO_SCHEME`, from {@link parseUrl}) or an unregistered scheme
|
|
5669
|
-
* (`URL_UNREGISTERED_SCHEME`).
|
|
5670
|
-
*/
|
|
5671
|
-
async resolve(env, url) {
|
|
5672
|
-
const parsed = parseUrl(url);
|
|
5673
|
-
const handler = this.handlers.get(parsed.scheme);
|
|
5674
|
-
if (handler === void 0) {
|
|
5675
|
-
const registered = [...this.handlers.keys()].sort().join(", ");
|
|
5676
|
-
throw new UrlSchemaError("URL_UNREGISTERED_SCHEME", `no handler registered for scheme "${parsed.scheme}" (registered: ${registered || "none"})`);
|
|
5677
|
-
}
|
|
5678
|
-
return applySelector(await handler.resolve(env, parsed.path), parsed.selector);
|
|
5679
|
-
}
|
|
5680
|
-
/**
|
|
5681
|
-
* Resolve a `scheme://` URL to its on-disk path when its handler is
|
|
5682
|
-
* path-backed: parse the URL, dispatch to the handler's optional
|
|
5683
|
-
* `resolvePath`, and return the real disk location. Returns `undefined`
|
|
5684
|
-
* for an unregistered scheme, a handler without `resolvePath`, or a path
|
|
5685
|
-
* the handler cannot map — callers then fall back to text resolution
|
|
5686
|
-
* (whose unregistered-scheme error is the structured generic one).
|
|
5687
|
-
* Selectors are NOT applied: they operate on resolved text, not paths.
|
|
5688
|
-
*/
|
|
5689
|
-
async resolvePath(env, url) {
|
|
5690
|
-
const parsed = parseUrl(url);
|
|
5691
|
-
const handler = this.handlers.get(parsed.scheme);
|
|
5692
|
-
if (handler?.resolvePath === void 0) return void 0;
|
|
5693
|
-
return await handler.resolvePath(env, parsed.path);
|
|
5694
|
-
}
|
|
5695
|
-
};
|
|
5696
|
-
|
|
5697
|
-
//#endregion
|
|
5698
|
-
//#region src/url-schema/tools/materialize.ts
|
|
5514
|
+
//#region src/url-schemes/tools/materialize.ts
|
|
5699
5515
|
/** Root preferred for materialization; temp dirs are `dashr-url-*` inside it. */
|
|
5700
5516
|
const SHM_ROOT = "/dev/shm";
|
|
5701
5517
|
/** Per-materialization shm ceiling (8 MiB); strictly larger goes to disk. */
|
|
@@ -5739,7 +5555,7 @@ async function withTempMaterialization(text, body) {
|
|
|
5739
5555
|
}
|
|
5740
5556
|
|
|
5741
5557
|
//#endregion
|
|
5742
|
-
//#region src/url-
|
|
5558
|
+
//#region src/url-schemes/tools/glob.ts
|
|
5743
5559
|
/** Build the resolver env from one tool execution (agent + session cwd + raw URL). */
|
|
5744
5560
|
function execEnv$1(exec, rawUrl) {
|
|
5745
5561
|
return {
|
|
@@ -5754,13 +5570,13 @@ function isSchemeUrl$2(raw) {
|
|
|
5754
5570
|
parseUrl(raw);
|
|
5755
5571
|
return true;
|
|
5756
5572
|
} catch (error) {
|
|
5757
|
-
if (error instanceof
|
|
5573
|
+
if (error instanceof UrlSchemesError && error.code === "URL_NO_SCHEME") return false;
|
|
5758
5574
|
throw error;
|
|
5759
5575
|
}
|
|
5760
5576
|
}
|
|
5761
5577
|
/** Structured error for calls that cannot run without the native delegate. */
|
|
5762
5578
|
function nativeUnavailable$1() {
|
|
5763
|
-
return new
|
|
5579
|
+
return new UrlSchemesError("NATIVE_GLOB_UNAVAILABLE", "the host did not deploy a native glob tool — URL-aware glob cannot delegate discovery");
|
|
5764
5580
|
}
|
|
5765
5581
|
/**
|
|
5766
5582
|
* Build the `glob` {@link ToolDefinition}: a `scheme://` URL lists the
|
|
@@ -5772,16 +5588,16 @@ function createGlobTool(deps) {
|
|
|
5772
5588
|
const { resolver, nativeGlob } = deps;
|
|
5773
5589
|
return defineTool({
|
|
5774
5590
|
name: "glob",
|
|
5775
|
-
description: "
|
|
5591
|
+
description: "Find files whose paths match a glob pattern. Returns matching file paths — never directories — including hidden and ignored files (VCS metadata directories are excluded). Up to 100 paths come back in modification-time order; a larger result returns the first 100 paths in modification-time order, says so, and reports where the complete sorted list was saved. This tool does not enumerate directory entries.",
|
|
5776
5592
|
parameters: {
|
|
5777
5593
|
pattern: {
|
|
5778
5594
|
type: "string",
|
|
5779
5595
|
required: true,
|
|
5780
|
-
description: "Glob pattern,
|
|
5596
|
+
description: "Glob pattern to match file paths against (e.g. \"**/*.ts\", \"src/**/*.test.js\"). A pattern with no \"/\" matches the basename at any depth, so \"*\" and \"*.ts\" both search the whole tree; include a separator to anchor the depth."
|
|
5781
5597
|
},
|
|
5782
5598
|
path: {
|
|
5783
5599
|
type: "string",
|
|
5784
|
-
description: "
|
|
5600
|
+
description: "Directory to search in. Defaults to the session workspace; a relative path resolves against it."
|
|
5785
5601
|
}
|
|
5786
5602
|
},
|
|
5787
5603
|
output: {
|
|
@@ -5845,7 +5661,7 @@ function createGlobTool(deps) {
|
|
|
5845
5661
|
}
|
|
5846
5662
|
|
|
5847
5663
|
//#endregion
|
|
5848
|
-
//#region src/url-
|
|
5664
|
+
//#region src/url-schemes/tools/grep.ts
|
|
5849
5665
|
/** Build the resolver env from one tool execution (agent + session cwd + raw URL). */
|
|
5850
5666
|
function execEnv(exec, rawUrl) {
|
|
5851
5667
|
return {
|
|
@@ -5860,13 +5676,13 @@ function isSchemeUrl$1(raw) {
|
|
|
5860
5676
|
parseUrl(raw);
|
|
5861
5677
|
return true;
|
|
5862
5678
|
} catch (error) {
|
|
5863
|
-
if (error instanceof
|
|
5679
|
+
if (error instanceof UrlSchemesError && error.code === "URL_NO_SCHEME") return false;
|
|
5864
5680
|
throw error;
|
|
5865
5681
|
}
|
|
5866
5682
|
}
|
|
5867
5683
|
/** Structured error for calls that cannot run without the native delegate. */
|
|
5868
5684
|
function nativeUnavailable() {
|
|
5869
|
-
return new
|
|
5685
|
+
return new UrlSchemesError("NATIVE_GREP_UNAVAILABLE", "the host did not deploy a native grep tool — URL-aware grep cannot delegate searches");
|
|
5870
5686
|
}
|
|
5871
5687
|
/**
|
|
5872
5688
|
* Build the `grep` {@link ToolDefinition}: a `scheme://` `path` translates
|
|
@@ -5878,7 +5694,7 @@ function createGrepTool(deps) {
|
|
|
5878
5694
|
const { resolver, nativeGrep } = deps;
|
|
5879
5695
|
return defineTool({
|
|
5880
5696
|
name: "grep",
|
|
5881
|
-
description: "Search file contents with a regular expression.
|
|
5697
|
+
description: "Search file contents with a ripgrep regular expression. Returns matching lines with line numbers, grouped by file. Returns the first 250 matches inline; a capped result reports where the complete match list was saved. Use read on a matched file for surrounding context.",
|
|
5882
5698
|
parameters: {
|
|
5883
5699
|
pattern: {
|
|
5884
5700
|
type: "string",
|
|
@@ -5887,11 +5703,11 @@ function createGrepTool(deps) {
|
|
|
5887
5703
|
},
|
|
5888
5704
|
path: {
|
|
5889
5705
|
type: "string",
|
|
5890
|
-
description: "
|
|
5706
|
+
description: "File or directory to search. Defaults to the session workspace; a relative path resolves against it."
|
|
5891
5707
|
},
|
|
5892
5708
|
include: {
|
|
5893
5709
|
type: "string",
|
|
5894
|
-
description: "
|
|
5710
|
+
description: "One glob filter for which files to search (e.g. \"*.ts\", \"*.{js,jsx}\"). Not a list; negation is not supported."
|
|
5895
5711
|
}
|
|
5896
5712
|
},
|
|
5897
5713
|
output: {
|
|
@@ -5951,7 +5767,7 @@ function createGrepTool(deps) {
|
|
|
5951
5767
|
}
|
|
5952
5768
|
|
|
5953
5769
|
//#endregion
|
|
5954
|
-
//#region src/url-
|
|
5770
|
+
//#region src/url-schemes/vendored/hashline/constants.js
|
|
5955
5771
|
const MAX_BYTES = 100 * 1024 * 1024;
|
|
5956
5772
|
const MAX_READ_LINE_BYTES = 200 * 1024;
|
|
5957
5773
|
const HASH_STORE_BUSY_TIMEOUT = 1e3;
|
|
@@ -5963,7 +5779,7 @@ const NOOP_LOOP_THRESHOLD = 3;
|
|
|
5963
5779
|
const NEW_CONTENT_NOT_STRING_MSG = "[E_BAD_SHAPE] \"replacement_text\" must be a string with \\n line separators, not an array. Do not pass an array of lines — pass the replacement text as one string: \"line1\\nline2\". Use \"\" to delete a range.";
|
|
5964
5780
|
|
|
5965
5781
|
//#endregion
|
|
5966
|
-
//#region src/url-
|
|
5782
|
+
//#region src/url-schemes/vendored/hashline/utils.js
|
|
5967
5783
|
function isRec(value) {
|
|
5968
5784
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5969
5785
|
}
|
|
@@ -6016,7 +5832,7 @@ function clipLine(line, maxLen = 200) {
|
|
|
6016
5832
|
}
|
|
6017
5833
|
|
|
6018
5834
|
//#endregion
|
|
6019
|
-
//#region src/url-
|
|
5835
|
+
//#region src/url-schemes/vendored/hashline/contract.js
|
|
6020
5836
|
const normalizedEdit = Symbol("normalizedEdit");
|
|
6021
5837
|
function isNormalizedEdit(input) {
|
|
6022
5838
|
return isRec(input) && input[normalizedEdit] === true;
|
|
@@ -6107,7 +5923,7 @@ function assertUndoRequest(request) {
|
|
|
6107
5923
|
}
|
|
6108
5924
|
|
|
6109
5925
|
//#endregion
|
|
6110
|
-
//#region src/url-
|
|
5926
|
+
//#region src/url-schemes/vendored/hashline/paths.js
|
|
6111
5927
|
/**
|
|
6112
5928
|
* On-disk home for dsh-better-edit state: ONE centralized store per harness
|
|
6113
5929
|
* home — `$DSH_HOME/storages/dsh-better-edit` (default
|
|
@@ -6124,11 +5940,11 @@ function hashStorePath() {
|
|
|
6124
5940
|
}
|
|
6125
5941
|
|
|
6126
5942
|
//#endregion
|
|
6127
|
-
//#region src/url-
|
|
5943
|
+
//#region src/url-schemes/vendored/hashline/fs-write.js
|
|
6128
5944
|
const STALE_TEMP_MS = 3600 * 1e3;
|
|
6129
5945
|
|
|
6130
5946
|
//#endregion
|
|
6131
|
-
//#region src/url-
|
|
5947
|
+
//#region src/url-schemes/vendored/hashline/hashline/hash-assign.js
|
|
6132
5948
|
let hasher = null;
|
|
6133
5949
|
function getH() {
|
|
6134
5950
|
if (hasher) return hasher;
|
|
@@ -6342,7 +6158,7 @@ function mapStableHashes(oldContent, oldHashes, newContent, removedHashes) {
|
|
|
6342
6158
|
}
|
|
6343
6159
|
|
|
6344
6160
|
//#endregion
|
|
6345
|
-
//#region src/url-
|
|
6161
|
+
//#region src/url-schemes/vendored/hashline/hash-store.js
|
|
6346
6162
|
function isValidHashList(value) {
|
|
6347
6163
|
if (!Array.isArray(value)) return false;
|
|
6348
6164
|
for (const hash of value) if (typeof hash !== "string" || !HASH_RE.test(hash)) return false;
|
|
@@ -6832,7 +6648,7 @@ async function upsertSnapshotFor(path$1, checksum, lineCount, hashes) {
|
|
|
6832
6648
|
}
|
|
6833
6649
|
|
|
6834
6650
|
//#endregion
|
|
6835
|
-
//#region src/url-
|
|
6651
|
+
//#region src/url-schemes/vendored/hashline/hashline/hash.js
|
|
6836
6652
|
let defaultHashSnapshotIO;
|
|
6837
6653
|
function snapshotIOFor(store) {
|
|
6838
6654
|
if (store) return {
|
|
@@ -6881,7 +6697,7 @@ async function lineHashes(content, path$1, previous, ioOrStore, persist) {
|
|
|
6881
6697
|
}
|
|
6882
6698
|
|
|
6883
6699
|
//#endregion
|
|
6884
|
-
//#region src/url-
|
|
6700
|
+
//#region src/url-schemes/vendored/hashline/session-view.js
|
|
6885
6701
|
const current = new AsyncLocalStorage();
|
|
6886
6702
|
function withWorkspace(cwd, fn) {
|
|
6887
6703
|
return current.run(cwd, fn);
|
|
@@ -7102,7 +6918,7 @@ async function scanDrift(input) {
|
|
|
7102
6918
|
}
|
|
7103
6919
|
|
|
7104
6920
|
//#endregion
|
|
7105
|
-
//#region src/url-
|
|
6921
|
+
//#region src/url-schemes/vendored/hashline/hashline/anchor-pipeline.js
|
|
7106
6922
|
function diagRef(ref) {
|
|
7107
6923
|
const trimmed = ref.trim();
|
|
7108
6924
|
if (!trimmed.length) return `[E_BAD_REF] Invalid anchor. Expected a 3-char alphanumeric anchor (e.g. "aB3").`;
|
|
@@ -7745,7 +7561,7 @@ function changedRange(original, result) {
|
|
|
7745
7561
|
}
|
|
7746
7562
|
|
|
7747
7563
|
//#endregion
|
|
7748
|
-
//#region src/url-
|
|
7564
|
+
//#region src/url-schemes/vendored/hashline/edit-diff.js
|
|
7749
7565
|
function detectEnding(content) {
|
|
7750
7566
|
const lfIdx = content.indexOf("\n");
|
|
7751
7567
|
if (lfIdx === -1) return content.indexOf("\r") >= 0 ? "\r" : "\n";
|
|
@@ -7860,7 +7676,7 @@ function genDiff(oldContent, newContent, contextLines = 2, newContentHashes, old
|
|
|
7860
7676
|
}
|
|
7861
7677
|
|
|
7862
7678
|
//#endregion
|
|
7863
|
-
//#region src/url-
|
|
7679
|
+
//#region src/url-schemes/vendored/hashline/file-view.js
|
|
7864
7680
|
const MAX_HASH_LINES$1 = HASH_SPACE;
|
|
7865
7681
|
const DEFAULT_MAX_LINES = 2e3;
|
|
7866
7682
|
const DEFAULT_MAX_BYTES = 50 * 1024;
|
|
@@ -8084,7 +7900,7 @@ async function readView(io, path$1, cwd, opts = {}) {
|
|
|
8084
7900
|
}
|
|
8085
7901
|
|
|
8086
7902
|
//#endregion
|
|
8087
|
-
//#region src/url-
|
|
7903
|
+
//#region src/url-schemes/vendored/hashline/fs-bridge.js
|
|
8088
7904
|
/**
|
|
8089
7905
|
* Map an `ctx.fs` failure onto the hashline model-facing vocabulary so the
|
|
8090
7906
|
* model sees the same structured error codes as the pure pipeline.
|
|
@@ -8159,7 +7975,7 @@ function ctxFsIO(fs, ctx) {
|
|
|
8159
7975
|
}
|
|
8160
7976
|
|
|
8161
7977
|
//#endregion
|
|
8162
|
-
//#region src/url-
|
|
7978
|
+
//#region src/url-schemes/vendored/hashline/read-and-serve.js
|
|
8163
7979
|
/** Appended when the file had non-UTF-8 bytes; editing rewrites it as UTF-8. */
|
|
8164
7980
|
const UTF8_REWRITE_NOTE = "[Non-UTF-8 bytes shown as U+FFFD; editing rewrites the file as UTF-8.]";
|
|
8165
7981
|
/**
|
|
@@ -8191,7 +8007,7 @@ async function readAndServe(io, rawPath, cwd, options) {
|
|
|
8191
8007
|
}
|
|
8192
8008
|
|
|
8193
8009
|
//#endregion
|
|
8194
|
-
//#region src/url-
|
|
8010
|
+
//#region src/url-schemes/tools/read.ts
|
|
8195
8011
|
/** Mirrors `parseUrl`'s scheme prefix so the fork matches the resolver exactly. */
|
|
8196
8012
|
const SCHEME_URL_RE = /^[a-z][a-z0-9]*:\/\//;
|
|
8197
8013
|
/**
|
|
@@ -8201,15 +8017,15 @@ const SCHEME_URL_RE = /^[a-z][a-z0-9]*:\/\//;
|
|
|
8201
8017
|
* scheme handlers read whichever fields they need off it.
|
|
8202
8018
|
*/
|
|
8203
8019
|
function createReadTool(deps) {
|
|
8204
|
-
const { resolver, fs, ctx } = deps;
|
|
8020
|
+
const { resolver, fs, ctx, gates, capturedRead } = deps;
|
|
8205
8021
|
const io = ctxFsIO(fs, ctx);
|
|
8206
8022
|
return defineTool({
|
|
8207
8023
|
name: "read",
|
|
8208
|
-
description: "Read a text file (each line returned as `HASH│content` with a 3-char hash anchor for later edit calls)
|
|
8024
|
+
description: "Read a text file (each line returned as `HASH│content` with a 3-char hash anchor for later edit calls). File reads page with offset/limit.",
|
|
8209
8025
|
parameters: {
|
|
8210
8026
|
path: {
|
|
8211
8027
|
type: "string",
|
|
8212
|
-
description: "File path (hashline-anchored read)
|
|
8028
|
+
description: "File path (hashline-anchored read)."
|
|
8213
8029
|
},
|
|
8214
8030
|
offset: {
|
|
8215
8031
|
type: "number",
|
|
@@ -8231,7 +8047,8 @@ function createReadTool(deps) {
|
|
|
8231
8047
|
const canonical = normalizeRequest(args);
|
|
8232
8048
|
assertReadRequest(canonical);
|
|
8233
8049
|
const rawPath = canonical.path;
|
|
8234
|
-
|
|
8050
|
+
const isScheme = SCHEME_URL_RE.test(rawPath);
|
|
8051
|
+
if (gates.urlSchemes && isScheme) {
|
|
8235
8052
|
const cwd = exec.agent?.session.header.cwd;
|
|
8236
8053
|
const env = cwd === void 0 ? {
|
|
8237
8054
|
agent: exec.agent,
|
|
@@ -8243,7 +8060,7 @@ function createReadTool(deps) {
|
|
|
8243
8060
|
};
|
|
8244
8061
|
return resolver.resolve(env, rawPath);
|
|
8245
8062
|
}
|
|
8246
|
-
return withWorkspace(execCwd(exec), async () => {
|
|
8063
|
+
if (gates.hashline && !isScheme) return withWorkspace(execCwd(exec), async () => {
|
|
8247
8064
|
const cwd = execCwd(exec);
|
|
8248
8065
|
const signal = exec.signal;
|
|
8249
8066
|
const { text, absolutePath } = await readAndServe(io, rawPath, cwd, {
|
|
@@ -8254,33 +8071,30 @@ function createReadTool(deps) {
|
|
|
8254
8071
|
await io.emitObserved(absolutePath, exec, signal);
|
|
8255
8072
|
return text;
|
|
8256
8073
|
});
|
|
8074
|
+
if (capturedRead !== void 0 && capturedRead.execute !== void 0) return capturedRead.execute(args, exec);
|
|
8075
|
+
throw new UrlSchemesError("NATIVE_READ_UNAVAILABLE", "no read delegate is available: urlSchemes and hashline gates are both disabled");
|
|
8257
8076
|
}
|
|
8258
8077
|
});
|
|
8259
8078
|
}
|
|
8260
8079
|
|
|
8261
8080
|
//#endregion
|
|
8262
|
-
//#region src/url-
|
|
8081
|
+
//#region src/url-schemes/tools/write.ts
|
|
8263
8082
|
/**
|
|
8264
8083
|
* Schemes the URL schema registers (v0.1.8d): the default write dispatch
|
|
8265
|
-
* keys off this
|
|
8084
|
+
* keys off this table; the integration step can replace the whole
|
|
8266
8085
|
* dispatch through `writeScheme` when it needs the live registry.
|
|
8086
|
+
*
|
|
8087
|
+
* Rendered from `catalog.ts` rather than written out here — this literal was
|
|
8088
|
+
* one of the hand-maintained copies that drifted from the scheme set.
|
|
8267
8089
|
*/
|
|
8268
|
-
const REGISTERED_SCHEMES =
|
|
8269
|
-
"agent",
|
|
8270
|
-
"ctx",
|
|
8271
|
-
"dsh",
|
|
8272
|
-
"dvc",
|
|
8273
|
-
"http",
|
|
8274
|
-
"https",
|
|
8275
|
-
"skill"
|
|
8276
|
-
];
|
|
8090
|
+
const REGISTERED_SCHEMES = SCHEME_NAMES;
|
|
8277
8091
|
/** Detects a `scheme://` prefix with the resolver layer's own parser. */
|
|
8278
8092
|
function isSchemeUrl(raw) {
|
|
8279
8093
|
try {
|
|
8280
8094
|
parseUrl(raw);
|
|
8281
8095
|
return true;
|
|
8282
8096
|
} catch (error) {
|
|
8283
|
-
if (error instanceof
|
|
8097
|
+
if (error instanceof UrlSchemesError && error.code === "URL_NO_SCHEME") return false;
|
|
8284
8098
|
throw error;
|
|
8285
8099
|
}
|
|
8286
8100
|
}
|
|
@@ -8298,9 +8112,9 @@ async function defaultSchemeWrite(scheme, path$1, content) {
|
|
|
8298
8112
|
after: JSON.stringify(result, null, 2)
|
|
8299
8113
|
};
|
|
8300
8114
|
}
|
|
8301
|
-
if (scheme === "ctx") throw new
|
|
8302
|
-
if (REGISTERED_SCHEMES.includes(scheme)) throw new
|
|
8303
|
-
throw new
|
|
8115
|
+
if (scheme === "ctx") throw new UrlSchemesError("URL_READ_ONLY", `ctx:// is a curated read-only snapshot — write to ctx://${path$1} is not supported`);
|
|
8116
|
+
if (REGISTERED_SCHEMES.includes(scheme)) throw new UrlSchemesError("URL_WRITE_UNSUPPORTED", `write to ${scheme}:// is not supported (read-only scheme, or its write channel is not wired yet)`);
|
|
8117
|
+
throw new UrlSchemesError("URL_UNREGISTERED_SCHEME", `no handler registered for scheme "${scheme}" (registered: ${REGISTERED_SCHEMES.join(", ")})`);
|
|
8304
8118
|
}
|
|
8305
8119
|
/**
|
|
8306
8120
|
* Build the `write` {@link ToolDefinition}: `scheme://` paths route to the
|
|
@@ -8312,17 +8126,17 @@ function createWriteTool(deps) {
|
|
|
8312
8126
|
const writeScheme = deps.writeScheme ?? defaultSchemeWrite;
|
|
8313
8127
|
return defineTool({
|
|
8314
8128
|
name: "write",
|
|
8315
|
-
description: "Create or fully replace a
|
|
8129
|
+
description: "Create or fully replace a UTF-8 text file.",
|
|
8316
8130
|
parameters: {
|
|
8317
8131
|
file_path: {
|
|
8318
8132
|
type: "string",
|
|
8319
8133
|
required: true,
|
|
8320
|
-
description: "
|
|
8134
|
+
description: "Path to write, resolved by the filesystem backend."
|
|
8321
8135
|
},
|
|
8322
8136
|
content: {
|
|
8323
8137
|
type: "string",
|
|
8324
8138
|
required: true,
|
|
8325
|
-
description: "Full
|
|
8139
|
+
description: "Full UTF-8 text content to write."
|
|
8326
8140
|
},
|
|
8327
8141
|
...sandbox !== void 0 && sandbox.escalationModes.length > 0 ? sandbox.schemaFields() : {}
|
|
8328
8142
|
},
|
|
@@ -8357,9 +8171,11 @@ function createWriteTool(deps) {
|
|
|
8357
8171
|
},
|
|
8358
8172
|
render: (_args, value) => {
|
|
8359
8173
|
const verb = value.operation === "update" ? "Updated" : value.operation === "execute" ? "Executed" : "Created";
|
|
8174
|
+
let text = value.diagnostics === void 0 ? `${verb} ${value.path}` : `${verb} ${value.path}\n${value.diagnostics}`;
|
|
8175
|
+
if (value.operation === "execute" && value.after !== void 0) text += `\n${value.after}`;
|
|
8360
8176
|
return [{
|
|
8361
8177
|
type: "text",
|
|
8362
|
-
text
|
|
8178
|
+
text
|
|
8363
8179
|
}];
|
|
8364
8180
|
}
|
|
8365
8181
|
},
|
|
@@ -8368,7 +8184,7 @@ function createWriteTool(deps) {
|
|
|
8368
8184
|
const parsed = parseUrl(args.file_path);
|
|
8369
8185
|
return writeScheme(parsed.scheme, parsed.path, args.content, {});
|
|
8370
8186
|
}
|
|
8371
|
-
if (nativeWrite === void 0) throw new
|
|
8187
|
+
if (nativeWrite === void 0) throw new UrlSchemesError("NATIVE_WRITE_UNAVAILABLE", "the host did not deploy a native write tool — URL-aware write cannot delegate filesystem writes");
|
|
8372
8188
|
let content = args.content;
|
|
8373
8189
|
try {
|
|
8374
8190
|
const formatted = await preWriteFormat?.(args.file_path, content);
|
|
@@ -8392,7 +8208,7 @@ function createWriteTool(deps) {
|
|
|
8392
8208
|
}
|
|
8393
8209
|
|
|
8394
8210
|
//#endregion
|
|
8395
|
-
//#region src/url-
|
|
8211
|
+
//#region src/url-schemes/vendored/hashline/sandbox.js
|
|
8396
8212
|
/**
|
|
8397
8213
|
* The filesystem escalation API: advertisement gating, per-call policy
|
|
8398
8214
|
* resolution, the one-approved wider retry, and denial-marker mapping. A pure
|
|
@@ -8478,7 +8294,7 @@ var FsSandboxController = class {
|
|
|
8478
8294
|
};
|
|
8479
8295
|
|
|
8480
8296
|
//#endregion
|
|
8481
|
-
//#region src/url-
|
|
8297
|
+
//#region src/url-schemes/vendored/hashline/undo-edit.js
|
|
8482
8298
|
/** Load the last undo row for a path from the active store, if any. */
|
|
8483
8299
|
async function readUndo(path$1) {
|
|
8484
8300
|
return (await loadHashStore()).getUndo(path$1);
|
|
@@ -8560,7 +8376,7 @@ async function clearUndo(path$1) {
|
|
|
8560
8376
|
}
|
|
8561
8377
|
|
|
8562
8378
|
//#endregion
|
|
8563
|
-
//#region src/url-
|
|
8379
|
+
//#region src/url-schemes/vendored/hashline/noop-guard.js
|
|
8564
8380
|
const noopLoopTracker = /* @__PURE__ */ new Map();
|
|
8565
8381
|
function noopPayloadKey(absolutePath, removeFrom, removeTo, replacementText) {
|
|
8566
8382
|
return JSON.stringify([
|
|
@@ -8584,7 +8400,7 @@ function clearNoopLoop(absolutePath) {
|
|
|
8584
8400
|
}
|
|
8585
8401
|
|
|
8586
8402
|
//#endregion
|
|
8587
|
-
//#region src/url-
|
|
8403
|
+
//#region src/url-schemes/vendored/hashline/edit-engine.js
|
|
8588
8404
|
/** The hashes a range edit removes, for stable re-hash bookkeeping. */
|
|
8589
8405
|
function collectRemovedHashes(edit, originalHashes) {
|
|
8590
8406
|
const removedHashes = /* @__PURE__ */ new Set();
|
|
@@ -8919,7 +8735,7 @@ async function persistUndoAndWrite(opts) {
|
|
|
8919
8735
|
}
|
|
8920
8736
|
|
|
8921
8737
|
//#endregion
|
|
8922
|
-
//#region src/url-
|
|
8738
|
+
//#region src/url-schemes/vendored/hashline/edit-response.js
|
|
8923
8739
|
function buildMetrics(args) {
|
|
8924
8740
|
const metrics = {
|
|
8925
8741
|
edits_attempted: args.editsAttempted,
|
|
@@ -9010,7 +8826,7 @@ function buildBatchResult(sections) {
|
|
|
9010
8826
|
}
|
|
9011
8827
|
|
|
9012
8828
|
//#endregion
|
|
9013
|
-
//#region src/url-
|
|
8829
|
+
//#region src/url-schemes/vendored/hashline/mutation.js
|
|
9014
8830
|
/**
|
|
9015
8831
|
* Deep seam: execute the full mutation lifecycle.
|
|
9016
8832
|
*
|
|
@@ -9113,7 +8929,7 @@ async function commit(opts) {
|
|
|
9113
8929
|
}
|
|
9114
8930
|
|
|
9115
8931
|
//#endregion
|
|
9116
|
-
//#region src/url-
|
|
8932
|
+
//#region src/url-schemes/vendored/hashline/prompts.js
|
|
9117
8933
|
/**
|
|
9118
8934
|
* Model-facing prompt text for the hashline tools, embedded so the bundle
|
|
9119
8935
|
* ships no external prompt files. Each tool's schema `description` is short;
|
|
@@ -9148,7 +8964,7 @@ const UNDO_GUIDANCE = {
|
|
|
9148
8964
|
};
|
|
9149
8965
|
|
|
9150
8966
|
//#endregion
|
|
9151
|
-
//#region src/url-
|
|
8967
|
+
//#region src/url-schemes/vendored/hashline/tool-edit.js
|
|
9152
8968
|
async function resolveNullPath(edits) {
|
|
9153
8969
|
if (edits.length === 0) return void 0;
|
|
9154
8970
|
const first = edits[0];
|
|
@@ -9248,7 +9064,7 @@ function registerEditTool(_rootCtx, agentCtx, io, sandbox) {
|
|
|
9248
9064
|
}
|
|
9249
9065
|
|
|
9250
9066
|
//#endregion
|
|
9251
|
-
//#region src/url-
|
|
9067
|
+
//#region src/url-schemes/vendored/hashline/tool-undo.js
|
|
9252
9068
|
/**
|
|
9253
9069
|
* Register the `undo_last_edit` tool on the calling agent's scope.
|
|
9254
9070
|
* @param _rootCtx - host context.
|
|
@@ -9348,7 +9164,7 @@ function registerUndoTool(_rootCtx, agentCtx, io, sandbox) {
|
|
|
9348
9164
|
}
|
|
9349
9165
|
|
|
9350
9166
|
//#endregion
|
|
9351
|
-
//#region src/url-
|
|
9167
|
+
//#region src/url-schemes/vendored/hashline/guidance/parse.js
|
|
9352
9168
|
/**
|
|
9353
9169
|
* Pure parser for per-preset guidance override files.
|
|
9354
9170
|
*
|
|
@@ -9419,7 +9235,7 @@ function isBlankOverride(content) {
|
|
|
9419
9235
|
}
|
|
9420
9236
|
|
|
9421
9237
|
//#endregion
|
|
9422
|
-
//#region src/url-
|
|
9238
|
+
//#region src/url-schemes/vendored/hashline/guidance/resolve.js
|
|
9423
9239
|
/**
|
|
9424
9240
|
* Render one tool's guidance as its intro line, a blank line, then bullets.
|
|
9425
9241
|
* Uniform across the four sections: no tool-schema description is duplicated
|
|
@@ -9517,7 +9333,7 @@ async function composeSections(presetId, homeDir) {
|
|
|
9517
9333
|
}
|
|
9518
9334
|
|
|
9519
9335
|
//#endregion
|
|
9520
|
-
//#region src/url-
|
|
9336
|
+
//#region src/url-schemes/vendored/hashline/guidance/materialize.js
|
|
9521
9337
|
/** The presets shipped by the harness, each seeded with editable guidance. */
|
|
9522
9338
|
const DEFAULT_PRESETS = [
|
|
9523
9339
|
"standard",
|
|
@@ -9693,9 +9509,9 @@ async function ensurePresetGuidance(homeDir) {
|
|
|
9693
9509
|
}
|
|
9694
9510
|
|
|
9695
9511
|
//#endregion
|
|
9696
|
-
//#region src/url-
|
|
9512
|
+
//#region src/url-schemes/index.ts
|
|
9697
9513
|
/** Cordis plugin name. */
|
|
9698
|
-
const name$1 = "dsh-url-
|
|
9514
|
+
const name$1 = "dsh-url-schemes";
|
|
9699
9515
|
/**
|
|
9700
9516
|
* Required host-plane services — including the live agent registry
|
|
9701
9517
|
* (`ctx.agents`, mounted by the `dsh-agent` row of `dsh-base`), which powers
|
|
@@ -9713,6 +9529,13 @@ const inject$1 = [
|
|
|
9713
9529
|
"agents",
|
|
9714
9530
|
"sessionPersistence"
|
|
9715
9531
|
];
|
|
9532
|
+
/** Every key defaults on — the service is opt-out, not opt-in. */
|
|
9533
|
+
function resolveGates(config) {
|
|
9534
|
+
return {
|
|
9535
|
+
urlSchemes: config?.urlSchemes !== false,
|
|
9536
|
+
hashline: config?.hashline !== false
|
|
9537
|
+
};
|
|
9538
|
+
}
|
|
9716
9539
|
/** Register the four URL-aware tools on one agent's own scope layer. */
|
|
9717
9540
|
/**
|
|
9718
9541
|
* The lsp feedback loop (native-tools Wave3): both hooks route through the
|
|
@@ -9765,25 +9588,29 @@ function buildLspWriteFeedback() {
|
|
|
9765
9588
|
postWrite: (filePath, content) => lspFeedback("diagnostics", filePath, content)
|
|
9766
9589
|
};
|
|
9767
9590
|
}
|
|
9768
|
-
function installAgentTools(rootCtx, agent, resolver) {
|
|
9591
|
+
function installAgentTools(rootCtx, agent, resolver, gates) {
|
|
9769
9592
|
agent.ctx.effect(async () => {
|
|
9770
9593
|
const native = captureNativeTools(rootCtx, agent);
|
|
9771
9594
|
const disposers = [];
|
|
9772
9595
|
const hashlineSandbox = new FsSandboxController(rootCtx);
|
|
9773
|
-
|
|
9596
|
+
const hashlineIo = ctxFsIO(rootCtx.fs, rootCtx);
|
|
9597
|
+
if (gates.hashline) disposers.push(agent.ctx.tools.register(createReadTool({
|
|
9774
9598
|
resolver,
|
|
9775
9599
|
fs: rootCtx.fs,
|
|
9776
|
-
ctx: rootCtx
|
|
9600
|
+
ctx: rootCtx,
|
|
9601
|
+
gates,
|
|
9602
|
+
capturedRead: native.read
|
|
9777
9603
|
})));
|
|
9778
|
-
disposers.push(agent.ctx.tools.register(createWriteTool({
|
|
9604
|
+
if (gates.urlSchemes) disposers.push(agent.ctx.tools.register(createWriteTool({
|
|
9779
9605
|
nativeWrite: native.write,
|
|
9780
9606
|
sandbox: hashlineSandbox,
|
|
9781
9607
|
...buildLspWriteFeedback()
|
|
9782
9608
|
})));
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9609
|
+
if (gates.hashline) {
|
|
9610
|
+
disposers.push(registerEditTool(rootCtx, agent.ctx, hashlineIo, hashlineSandbox));
|
|
9611
|
+
disposers.push(registerUndoTool(rootCtx, agent.ctx, hashlineIo, hashlineSandbox));
|
|
9612
|
+
}
|
|
9613
|
+
if (gates.hashline) {
|
|
9787
9614
|
const diagnosticsHook = buildLspWriteFeedback().postWrite;
|
|
9788
9615
|
disposers.push(agent.ctx.on("tools/post-execute", async (exec, result, next) => {
|
|
9789
9616
|
const decision = await next();
|
|
@@ -9822,15 +9649,19 @@ function installAgentTools(rootCtx, agent, resolver) {
|
|
|
9822
9649
|
}));
|
|
9823
9650
|
} catch {}
|
|
9824
9651
|
for (const section of sections) disposers.push(agent.ctx.systemPrompt.section(section));
|
|
9652
|
+
const general = generalSection(gates);
|
|
9653
|
+
if (general !== void 0) disposers.push(agent.ctx.systemPrompt.section(general));
|
|
9825
9654
|
} catch {}
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9655
|
+
if (gates.urlSchemes) {
|
|
9656
|
+
disposers.push(agent.ctx.tools.register(createGrepTool({
|
|
9657
|
+
resolver,
|
|
9658
|
+
nativeGrep: native.grep
|
|
9659
|
+
})));
|
|
9660
|
+
disposers.push(agent.ctx.tools.register(createGlobTool({
|
|
9661
|
+
resolver,
|
|
9662
|
+
nativeGlob: native.glob
|
|
9663
|
+
})));
|
|
9664
|
+
}
|
|
9834
9665
|
return () => {
|
|
9835
9666
|
for (const dispose of disposers) dispose();
|
|
9836
9667
|
};
|
|
@@ -9841,6 +9672,11 @@ function apply$1(ctx, config) {
|
|
|
9841
9672
|
initHasher().catch(() => {});
|
|
9842
9673
|
ensurePresetGuidance(configDir()).catch(() => {});
|
|
9843
9674
|
const resolver = new UrlResolver();
|
|
9675
|
+
if (resolveGates(config).urlSchemes && ctx.fs) try {
|
|
9676
|
+
wrapFsWithSchemes(ctx.fs, { settings: ctx.settings });
|
|
9677
|
+
} catch (error) {
|
|
9678
|
+
ctx.logger("dsh-url-schemes").warn(`fs scheme wrap failed; running on the stock filesystem service: ${error instanceof Error ? error.message : String(error)}`);
|
|
9679
|
+
}
|
|
9844
9680
|
resolver.register("skill", createSkillHandler({
|
|
9845
9681
|
skills: ctx.skills,
|
|
9846
9682
|
fs: ctx.fs
|
|
@@ -9856,7 +9692,7 @@ function apply$1(ctx, config) {
|
|
|
9856
9692
|
docsDir: resolveDocsDir()
|
|
9857
9693
|
}));
|
|
9858
9694
|
resolver.register("dvc", createDvcHandler());
|
|
9859
|
-
resolver.register("ctx", createCtxHandler());
|
|
9695
|
+
resolver.register("ctx", createCtxHandler({ sessionPersistence: ctx.sessionPersistence }));
|
|
9860
9696
|
registerAstDevices();
|
|
9861
9697
|
registerBrowserDevice();
|
|
9862
9698
|
installLspDevices();
|
|
@@ -9867,13 +9703,13 @@ function apply$1(ctx, config) {
|
|
|
9867
9703
|
if (registered.has(agent)) return;
|
|
9868
9704
|
registered.add(agent);
|
|
9869
9705
|
try {
|
|
9870
|
-
installAgentTools(ctx, agent, resolver);
|
|
9706
|
+
installAgentTools(ctx, agent, resolver, resolveGates(config));
|
|
9871
9707
|
} catch (error) {
|
|
9872
|
-
ctx.logger("dsh-url-
|
|
9708
|
+
ctx.logger("dsh-url-schemes").warn(`failed to install URL-aware tools for agent ${agent.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
9873
9709
|
}
|
|
9874
9710
|
});
|
|
9875
9711
|
}
|
|
9876
|
-
var
|
|
9712
|
+
var url_schemes_default = {
|
|
9877
9713
|
name: name$1,
|
|
9878
9714
|
inject: inject$1,
|
|
9879
9715
|
apply: apply$1
|
|
@@ -10982,6 +10818,8 @@ const MOBILE_CONFIG = z.object({
|
|
|
10982
10818
|
/** Runtime schema. */
|
|
10983
10819
|
const Config = z.intersect([DashrRuntime.Config, z.object({
|
|
10984
10820
|
maxParallelSubCalls: z.natural().min(1).default(10),
|
|
10821
|
+
urlSchemes: z.boolean().default(true),
|
|
10822
|
+
hashline: z.boolean().default(true),
|
|
10985
10823
|
trustedPageAuthorities: z.array(String).default(deriveDefaultPageAuthorities(process.env.DSH_TRUSTED_HOSTS)),
|
|
10986
10824
|
mobile: MOBILE_CONFIG
|
|
10987
10825
|
})]);
|
|
@@ -11003,12 +10841,16 @@ const EVAL_NAME = "eval";
|
|
|
11003
10841
|
* (`modelSelectionSettings: true`) lands on the agent's OWN layer, outside
|
|
11004
10842
|
* registry restriction reach, so it stays visible on every surface and the
|
|
11005
10843
|
* control prompt annotates it as an alias of the `agent` delegation tool.
|
|
10844
|
+
* `skill` was REMOVED from this list (2026-09-12, user ruling): the native
|
|
10845
|
+
* skill tool stays visible and self-surfaces its session catalog
|
|
10846
|
+
* (`<available_skills>` is gated on the tool's registry visibility — masking
|
|
10847
|
+
* the tool silenced the catalog). dashr's dual pipeline keeps `read
|
|
10848
|
+
* skill://<name>` as the addressing form alongside native invocation.
|
|
11006
10849
|
* own URL wrappers) is exempt from restrictions by construction — the
|
|
11007
10850
|
* capability it carries stays reachable through the captured-definition
|
|
11008
10851
|
* bridges below, never through the masked name.
|
|
11009
10852
|
*/
|
|
11010
10853
|
const MASKED_TOOL_NAMES = new Set([
|
|
11011
|
-
"skill",
|
|
11012
10854
|
"send_message",
|
|
11013
10855
|
"report",
|
|
11014
10856
|
"list_agents",
|
|
@@ -11547,7 +11389,7 @@ function pickRuntimeConfig(config) {
|
|
|
11547
11389
|
}
|
|
11548
11390
|
function apply(ctx, config) {
|
|
11549
11391
|
ctx.plugin(DashrRuntime, pickRuntimeConfig(config));
|
|
11550
|
-
ctx.plugin(
|
|
11392
|
+
ctx.plugin(url_schemes_default, config);
|
|
11551
11393
|
installFailover(ctx);
|
|
11552
11394
|
installWebTrust(ctx, config);
|
|
11553
11395
|
const logger = ctx.logger("dashr-repl");
|