better-dsh 0.2.4-a → 0.2.4-c

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.
Files changed (49) hide show
  1. package/cordis.patch.yml +7 -0
  2. package/docs/10_plans/2026-09-23-remote-execution-framework.md +236 -0
  3. package/docs/10_plans/2026-09-23-remote-framework-impl.md +2083 -0
  4. package/docs/10_plans/2026-09-26-lsp-ast-reminder.md +64 -0
  5. package/docs/20_specs/agent/spec.md +54 -0
  6. package/docs/20_specs/ast/spec.md +34 -0
  7. package/docs/20_specs/compaction-recall/spec.md +46 -0
  8. package/docs/20_specs/ctx/spec.md +107 -0
  9. package/docs/20_specs/dsh/spec.md +47 -0
  10. package/docs/20_specs/dvc/spec.md +87 -0
  11. package/docs/20_specs/escalation-guidance/spec.md +44 -0
  12. package/docs/20_specs/fs-scheme-resolution/spec.md +37 -0
  13. package/docs/20_specs/hash-edit/spec.md +41 -0
  14. package/docs/20_specs/http-read/spec.md +73 -0
  15. package/docs/20_specs/kernel-provisioning/spec.md +53 -0
  16. package/docs/20_specs/lsp/spec.md +121 -0
  17. package/docs/20_specs/mobile-layout/spec.md +108 -0
  18. package/docs/20_specs/model-failover/spec.md +20 -0
  19. package/docs/20_specs/plugins-page-components/spec.md +79 -0
  20. package/docs/20_specs/preact-ui-shell/spec.md +22 -0
  21. package/docs/20_specs/repl-dispatch-resilience/spec.md +21 -0
  22. package/docs/20_specs/skill/spec.md +58 -0
  23. package/docs/20_specs/tool-surface/spec.md +222 -0
  24. package/docs/20_specs/url-schema/spec.md +148 -0
  25. package/docs/20_specs/web-trust-fence/spec.md +43 -0
  26. package/docs/50_test-reports/2026-09-22-better-dsh/347/273/204/344/273/266/347/203/255/346/217/222/346/213/224/344/270/216/344/276/235/350/265/226/347/272/247/350/201/224/345/256/236/346/265/213/346/212/245/345/221/212.md +18 -1
  27. package/docs/50_test-reports/2026-09-23-remote-framework/345/256/236/346/265/213/346/212/245/345/221/212.md +92 -0
  28. package/docs/50_test-reports/2026-09-23-remote-framework/347/254/254/344/270/200/344/272/272/347/247/260/345/244/215/346/265/213/346/212/245/345/221/212.md +125 -0
  29. package/docs/50_test-reports/2026-09-23-ssh/345/267/245/345/205/267assisted-pty/344/274/232/350/257/235framing/345/256/236/346/265/213/346/212/245/345/221/212.md +207 -0
  30. package/docs/50_test-reports/2026-09-24-v0.1.7-rc.1/345/257/271/351/275/220/350/275/256-compaction/344/270/216failover/350/256/276/347/275/256/351/235/242/344/277/256/345/244/215/345/256/236/346/265/213/346/212/245/345/221/212.md +65 -0
  31. package/docs/50_test-reports/2026-09-27-lsp-ast-reminder-4999/345/256/236/346/265/213/346/212/245/345/221/212.md +53 -0
  32. package/lib/client/index.js +18 -18
  33. package/lib/compaction/index.d.ts +20 -10
  34. package/lib/compaction/index.js +42 -56
  35. package/lib/failover/index.d.ts +30 -6
  36. package/lib/failover/index.js +32 -26
  37. package/lib/fs-aware/sandbox-plugin.js +1 -1
  38. package/lib/index.js +4 -7
  39. package/lib/mobile/plugin.d.ts +1 -1
  40. package/lib/remote/plugin.d.ts +24 -0
  41. package/lib/remote/plugin.js +1095 -0
  42. package/lib/url-schemes/index.d.ts +14 -14
  43. package/lib/url-schemes/index.js +119 -15
  44. package/lib/{web-trust-CgpBoFv3.d.ts → web-trust-BihHCelF.d.ts} +10 -10
  45. package/lib/web-trust.d.ts +1 -1
  46. package/package.json +12 -4
  47. package/docs/10_plans/dashr-remote-tool-design.md +0 -337
  48. package/docs/10_plans/self-exo-cot-draft.md +0 -322
  49. package/docs/10_plans/self-scheme-and-monolog-draft.md +0 -357
@@ -50,13 +50,13 @@ declare const inject: string[];
50
50
  * real patch-line config. Schema-level defaults (both ON — the service is
51
51
  * opt-out) survive every overlay layer, per the v0.2.2a ruling.
52
52
  */
53
- declare const Config: z<Schemastery.ObjectS<{
54
- urlSchemes: z<boolean, boolean>;
55
- hashline: z<boolean, boolean>;
56
- }>, Schemastery.ObjectT<{
57
- urlSchemes: z<boolean, boolean>;
58
- hashline: z<boolean, boolean>;
59
- }>>;
53
+ declare const Config: z<Schemastery.ObjectS<NoInfer<{
54
+ urlSchemes: z<boolean, boolean, "defined">;
55
+ hashline: z<boolean, boolean, "defined">;
56
+ }>>, Schemastery.ObjectT<NoInfer<{
57
+ urlSchemes: z<boolean, boolean, "defined">;
58
+ hashline: z<boolean, boolean, "defined">;
59
+ }>>, "plain">;
60
60
  /** Register the four URL-aware tools on one agent's own scope layer. */
61
61
  /**
62
62
  * The lsp feedback loop (native-tools Wave3): both hooks route through the
@@ -75,13 +75,13 @@ declare function apply(ctx: Context, config: UrlSchemesConfig | undefined): void
75
75
  declare const _default: {
76
76
  name: string;
77
77
  inject: string[];
78
- Config: z<Schemastery.ObjectS<{
79
- urlSchemes: z<boolean, boolean>;
80
- hashline: z<boolean, boolean>;
81
- }>, Schemastery.ObjectT<{
82
- urlSchemes: z<boolean, boolean>;
83
- hashline: z<boolean, boolean>;
84
- }>>;
78
+ Config: z<Schemastery.ObjectS<NoInfer<{
79
+ urlSchemes: z<boolean, boolean, "defined">;
80
+ hashline: z<boolean, boolean, "defined">;
81
+ }>>, Schemastery.ObjectT<NoInfer<{
82
+ urlSchemes: z<boolean, boolean, "defined">;
83
+ hashline: z<boolean, boolean, "defined">;
84
+ }>>, "plain">;
85
85
  apply: typeof apply;
86
86
  };
87
87
  //#endregion
@@ -8,7 +8,7 @@ import * as path from "node:path";
8
8
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
9
  import z from "@deepseek-ai/schemastery";
10
10
  import { spawn } from "node:child_process";
11
- import { tmpdir } from "node:os";
11
+ import { homedir, tmpdir } from "node:os";
12
12
  import { fileURLToPath, pathToFileURL } from "node:url";
13
13
  import { defineTool } from "@deepseek-ai/dsh-tools";
14
14
  import { FsError } from "@deepseek-ai/dsh-fs";
@@ -2134,6 +2134,96 @@ function installLspDevices(registry = registerDvcDevice) {
2134
2134
  registry("lsp", lspDevice);
2135
2135
  }
2136
2136
 
2137
+ //#endregion
2138
+ //#region src/devices/ast/ast-reminder.ts
2139
+ /**
2140
+ * AST-capable extension set — empirically probed against the shipped
2141
+ * pi-natives binary (2026-09-27): grouped by grammar family. TS family and
2142
+ * header files ride the same grammars as their proven siblings.
2143
+ */
2144
+ const AST_CODE_EXTENSIONS = new Set([
2145
+ ".py",
2146
+ ".pyi",
2147
+ ".js",
2148
+ ".jsx",
2149
+ ".mjs",
2150
+ ".cjs",
2151
+ ".ts",
2152
+ ".tsx",
2153
+ ".mts",
2154
+ ".cts",
2155
+ ".rs",
2156
+ ".go",
2157
+ ".c",
2158
+ ".cpp",
2159
+ ".h",
2160
+ ".hpp",
2161
+ ".html",
2162
+ ".css",
2163
+ ".json",
2164
+ ".yaml",
2165
+ ".yml",
2166
+ ".sh",
2167
+ ".rb"
2168
+ ]);
2169
+ /** Reminders per agent session (user ruling 2026-09-27: five is enough). */
2170
+ const AST_REMINDER_CAP = 5;
2171
+ /** The one-line availability notice (single line, both dvc:// devices). */
2172
+ const NOTICE = "ast_edit / ast_grep available — write dvc://ast_edit (structured rewrite) or dvc://ast_grep (pattern search)";
2173
+ /** Per-session emitted-reminder counters (disposed with the agent). */
2174
+ const counters = /* @__PURE__ */ new Map();
2175
+ /** Drop a session's reminder counter (agent dispose). */
2176
+ function disposeAstReminders(sessionId) {
2177
+ counters.delete(sessionId);
2178
+ }
2179
+ /** Lowercased extension of a path ('' when none). */
2180
+ function extensionOf(filePath) {
2181
+ return path.extname(filePath).toLowerCase();
2182
+ }
2183
+ /** True when an include/glob string names a file covered by the AST set. */
2184
+ function includeTargetsCode(include) {
2185
+ const flat = include.toLowerCase().replace(/[{},]/g, ".");
2186
+ for (const m of flat.matchAll(/\.([a-z0-9]+)/g)) if (AST_CODE_EXTENSIONS.has(`.${m[1]}`)) return true;
2187
+ return false;
2188
+ }
2189
+ /** Consume one emission slot for a session; false once the cap is exhausted. */
2190
+ function consume(sessionId) {
2191
+ const n = counters.get(sessionId) ?? 0;
2192
+ if (n >= AST_REMINDER_CAP) return false;
2193
+ counters.set(sessionId, n + 1);
2194
+ return true;
2195
+ }
2196
+ /**
2197
+ * Notice for a landed `write`/`edit` on one file: defined iff the target is
2198
+ * an AST-capable file and the session still has reminder budget.
2199
+ */
2200
+ function astFileNotice(sessionId, filePath) {
2201
+ if (!AST_CODE_EXTENSIONS.has(extensionOf(filePath))) return void 0;
2202
+ return consume(sessionId) ? NOTICE : void 0;
2203
+ }
2204
+ /**
2205
+ * Notice for a landed `grep`: fires for directory/default-path searches, set
2206
+ * includes, and code-file targets; silent only for clearly non-code files.
2207
+ * Consumes session budget when a notice is emitted.
2208
+ */
2209
+ function astGrepNotice(sessionId, args) {
2210
+ const fire = () => consume(sessionId) ? NOTICE : void 0;
2211
+ if (args === void 0) return fire();
2212
+ const { path: searchPath, include } = args;
2213
+ if (typeof include === "string" && include !== "") return includeTargetsCode(include) ? fire() : void 0;
2214
+ if (typeof searchPath !== "string" || searchPath === "") return fire();
2215
+ const ext = extensionOf(searchPath);
2216
+ if (ext === "") return fire();
2217
+ return AST_CODE_EXTENSIONS.has(ext) ? fire() : void 0;
2218
+ }
2219
+ /** Tool-dispatch shape: route `grep` down the search rule, the rest down the file rule. */
2220
+ function astReminderNotice(sessionId, tool, args) {
2221
+ if (tool === "grep") return astGrepNotice(sessionId, args);
2222
+ const filePath = args?.path ?? args?.file_path;
2223
+ if (typeof filePath !== "string" || filePath === "") return void 0;
2224
+ return astFileNotice(sessionId, filePath);
2225
+ }
2226
+
2137
2227
  //#endregion
2138
2228
  //#region src/url-schemes/handlers/agent.ts
2139
2229
  /**
@@ -2321,8 +2411,8 @@ function renderTranscript(session) {
2321
2411
  function messageLabel(message) {
2322
2412
  if (message.role === "assistant") return "assistant";
2323
2413
  if (message.role === "system") return "system";
2324
- const first = message.content[0];
2325
- return first !== void 0 && first.type === "tool-result" ? "tool result" : "user";
2414
+ if (message.role === "tool") return message.isError === true ? "tool result (error)" : "tool result";
2415
+ return "user";
2326
2416
  }
2327
2417
  /** Render typed model content to plain text. */
2328
2418
  function renderBlocks(content) {
@@ -2333,7 +2423,6 @@ function renderBlock(block) {
2333
2423
  case "text":
2334
2424
  case "reasoning": return block.text;
2335
2425
  case "tool-call": return `[tool: ${block.name}] ${block.arguments}`;
2336
- case "tool-result": return block.isError === true ? `[tool error] ${renderBlocks(block.content)}` : renderBlocks(block.content);
2337
2426
  case "image": return "[image]";
2338
2427
  default: return `[${block.type ?? "unknown"}]`;
2339
2428
  }
@@ -7121,11 +7210,29 @@ async function installAgentTools(rootCtx, agent, resolver, gates$1) {
7121
7210
  })));
7122
7211
  }
7123
7212
  const sessionId = agent.id;
7213
+ const appendSuffixes = (decision, result, suffixes) => {
7214
+ if (suffixes.length === 0) return decision;
7215
+ const record = decision;
7216
+ if (record?.kind !== "accept") return decision;
7217
+ record.content = [...record.content ?? result.content ?? [], ...suffixes.map((text) => ({
7218
+ type: "text",
7219
+ text: `\n${text}`
7220
+ }))];
7221
+ return decision;
7222
+ };
7124
7223
  disposers.push(agent.ctx.on("tools/post-execute", async (exec, result, next) => {
7125
7224
  const decision = await next();
7126
7225
  const name$1 = exec.name;
7127
- if (name$1 !== "read" && name$1 !== "edit" && name$1 !== "write") return decision;
7226
+ if (name$1 !== "read" && name$1 !== "edit" && name$1 !== "write" && name$1 !== "grep") return decision;
7128
7227
  const args = exec.arguments;
7228
+ const suffixes = [];
7229
+ if (name$1 === "grep") {
7230
+ if (!result.isError) {
7231
+ const ast$1 = astReminderNotice(sessionId, "grep", args);
7232
+ if (ast$1 !== void 0) suffixes.push(ast$1);
7233
+ }
7234
+ return appendSuffixes(decision, result, suffixes);
7235
+ }
7129
7236
  const filePath = args?.path ?? args?.file_path;
7130
7237
  if (typeof filePath !== "string" || filePath === "") return decision;
7131
7238
  if (name$1 === "read") {
@@ -7135,15 +7242,12 @@ async function installAgentTools(rootCtx, agent, resolver, gates$1) {
7135
7242
  lspGateSyncOnLand(sessionId, name$1, filePath);
7136
7243
  if (result.isError) return decision;
7137
7244
  const notice = lspGateNotice(sessionId, filePath);
7138
- if (notice === void 0) return decision;
7139
- const record = decision;
7140
- if (record.kind !== "accept") return decision;
7141
- record.content = [...record.content ?? result.content ?? [], {
7142
- type: "text",
7143
- text: `\n${notice}`
7144
- }];
7145
- return decision;
7245
+ if (notice !== void 0) suffixes.push(notice);
7246
+ const ast = astReminderNotice(sessionId, name$1, args);
7247
+ if (ast !== void 0) suffixes.push(ast);
7248
+ return appendSuffixes(decision, result, suffixes);
7146
7249
  }));
7250
+ disposers.push(() => disposeAstReminders(sessionId));
7147
7251
  disposers.push(() => disposeLspGate(sessionId));
7148
7252
  return () => {
7149
7253
  for (const dispose of disposers) dispose();
@@ -7181,8 +7285,8 @@ function apply(ctx, config) {
7181
7285
  const httpHandler = createHttpHandler();
7182
7286
  for (const scheme of HTTP_SCHEMES) resolver.register(scheme, httpHandler);
7183
7287
  const registered = /* @__PURE__ */ new WeakSet();
7184
- ctx.on("agent/session-start", ({ agent }) => {
7185
- if (registered.has(agent)) return;
7288
+ ctx.on("agent/created", ({ agent }) => {
7289
+ if (registered.has(agent)) return void 0;
7186
7290
  registered.add(agent);
7187
7291
  installAgentTools(ctx, agent, resolver, resolveGates(config)).catch((error) => {
7188
7292
  ctx.logger("dsh-url-schemes").warn(`failed to install URL-aware tools for agent ${agent.id}: ${error instanceof Error ? error.message : String(error)}`);
@@ -73,11 +73,11 @@ interface TrustConfig {
73
73
  trustedPageAuthorities?: readonly string[];
74
74
  }
75
75
  /** Row config schema (schema-level default derives from `DSH_TRUSTED_HOSTS`). */
76
- declare const Config: z<Schemastery.ObjectS<{
77
- trustedPageAuthorities: z<string[], string[]>;
78
- }>, Schemastery.ObjectT<{
79
- trustedPageAuthorities: z<string[], string[]>;
80
- }>>;
76
+ declare const Config: z<Schemastery.ObjectS<NoInfer<{
77
+ trustedPageAuthorities: z<string[], string[], "defined">;
78
+ }>>, Schemastery.ObjectT<NoInfer<{
79
+ trustedPageAuthorities: z<string[], string[], "defined">;
80
+ }>>, "plain">;
81
81
  /** Cordis plugin name. */
82
82
  declare const name = "dashr-web-trust";
83
83
  /**
@@ -95,11 +95,11 @@ declare function apply(ctx: Context, config: TrustConfig | undefined): void;
95
95
  declare const _default: {
96
96
  name: string;
97
97
  inject: string[];
98
- Config: z<Schemastery.ObjectS<{
99
- trustedPageAuthorities: z<string[], string[]>;
100
- }>, Schemastery.ObjectT<{
101
- trustedPageAuthorities: z<string[], string[]>;
102
- }>>;
98
+ Config: z<Schemastery.ObjectS<NoInfer<{
99
+ trustedPageAuthorities: z<string[], string[], "defined">;
100
+ }>>, Schemastery.ObjectT<NoInfer<{
101
+ trustedPageAuthorities: z<string[], string[], "defined">;
102
+ }>>, "plain">;
103
103
  apply: typeof apply;
104
104
  };
105
105
  //#endregion
@@ -1,2 +1,2 @@
1
- import { a as _default, c as buildTrustScript, d as name, i as TrustConfig, l as deriveDefaultPageAuthorities, n as Config, o as apply, r as MobileConfig, s as buildMobileScript, t as BootScriptRow, u as inject } from "./web-trust-CgpBoFv3.js";
1
+ import { a as _default, c as buildTrustScript, d as name, i as TrustConfig, l as deriveDefaultPageAuthorities, n as Config, o as apply, r as MobileConfig, s as buildMobileScript, t as BootScriptRow, u as inject } from "./web-trust-BihHCelF.js";
2
2
  export { BootScriptRow, Config, MobileConfig, TrustConfig, apply, buildMobileScript, buildTrustScript, _default as default, deriveDefaultPageAuthorities, inject, name };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-dsh",
3
- "version": "0.2.4-a",
3
+ "version": "0.2.4-c",
4
4
  "description": "Make your dsh ready for serious coding tasks. (Tools x Schemas)^REPL. `skill://`, `ctx://`, `agent://`, `dvc://`, `dsh://`, IPython REPL, Context as Variables, cross compactions recallable, full context revive. `hash-edit`, `dvc://browser`, subagent as function, workflow as function.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -34,6 +34,10 @@
34
34
  "types": "./lib/mobile/plugin.d.ts",
35
35
  "default": "./lib/mobile/plugin.js"
36
36
  },
37
+ "./remote": {
38
+ "types": "./lib/remote/plugin.d.ts",
39
+ "default": "./lib/remote/plugin.js"
40
+ },
37
41
  "./fs-aware-sandbox": {
38
42
  "types": "./lib/fs-aware/sandbox-plugin.d.ts",
39
43
  "default": "./lib/fs-aware/sandbox-plugin.js"
@@ -57,7 +61,9 @@
57
61
  "scripts": {
58
62
  "prebuild": "node scripts/copy-docs.mjs",
59
63
  "build": "tsdown && npm run build-client",
60
- "typecheck": "tsc --noEmit",
64
+ "typecheck": "npm run typecheck:host && npm run typecheck:client",
65
+ "typecheck:host": "tsc --noEmit",
66
+ "typecheck:client": "tsc -p tsconfig.client.json",
61
67
  "test": "vitest --run",
62
68
  "test:watch": "vitest",
63
69
  "kernel:venv": "node scripts/kernel-provision.mjs",
@@ -332,11 +338,13 @@
332
338
  },
333
339
  "devDependencies": {
334
340
  "@types/node": "^22.10.5",
341
+ "@types/react": "~18.3.31",
335
342
  "node-addon-require-builtin": "^0.1.5",
343
+ "react": "^18.3.1",
336
344
  "tsdown": "^0.15.7",
345
+ "tsx": "^4.22.4",
337
346
  "typescript": "^5.9.3",
338
- "vitest": "^4.1.10",
339
- "tsx": "^4.22.4"
347
+ "vitest": "^4.1.10"
340
348
  },
341
349
  "repository": {
342
350
  "type": "git",