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
@@ -2,19 +2,6 @@ import { Context } from "@deepseek-ai/cordis";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
 
4
4
  //#region src/compaction/config.ts
5
- /**
6
- * The `compaction-tuning` General-row config: the share of the routed context
7
- * window at which automatic condensation starts.
8
- *
9
- * Pure module — no `@deepseek-ai/*` value imports — so the host half and the
10
- * client half may both import it without dragging either runtime into the
11
- * browser bundle: the host takes the namespace and the schema defaults, the
12
- * client takes the namespace literal plus the stepper bounds.
13
- *
14
- * @module dashr/compaction/config
15
- */
16
- /** The settings namespace the General row reads and writes. */
17
- const COMPACTION_SETTINGS_NS = "compaction-tuning";
18
5
  /** Upstream `compaction-basic`'s own default: condense at 80% of the window. */
19
6
  const DEFAULT_THRESHOLD_RATIO = .8;
20
7
  /**
@@ -47,32 +34,39 @@ const name = "dashr-compaction-tuning";
47
34
  */
48
35
  const inject = [];
49
36
  /**
50
- * The `compaction-tuning` settings schema.
37
+ * The row settings schema.
51
38
  *
52
- * `thresholdRatio` is the field the General row drives; `retainTokens` is
53
- * composition-only (see {@link DEFAULT_RETAIN_TOKENS}) but still validated and
54
- * applied, so the recency window keeps one source of truth.
39
+ * `thresholdRatio` is the field the General row drives and is volatile (dsh
40
+ * 0.1.7 settings model): editing it from Settings lands as a profile-layer
41
+ * patch override on this row and the resolved ref rewrites in place.
42
+ * `retainTokens` is composition-only (see {@link DEFAULT_RETAIN_TOKENS}) and
43
+ * stays non-volatile — form writes never touch it, a patch file still can.
55
44
  */
56
45
  const Config = z.object({
57
- thresholdRatio: z.number().step(.01).min(THRESHOLD_MIN_RATIO).max(THRESHOLD_MAX_RATIO).default(DEFAULT_THRESHOLD_RATIO),
46
+ thresholdRatio: z.number().step(.01).min(THRESHOLD_MIN_RATIO).max(THRESHOLD_MAX_RATIO).default(DEFAULT_THRESHOLD_RATIO).volatile(),
58
47
  retainTokens: z.number().step(1).min(0).default(DEFAULT_RETAIN_TOKENS)
59
48
  });
60
49
  /**
61
- * Install the settings namespace and keep the live compaction engine in sync.
50
+ * Keep the live compaction engine on the user's threshold preference.
62
51
  * @param ctx - host context of the row that owns this capability.
63
- * @param config - composition base layer; schema defaults fill the rest.
52
+ * @param config - resolved row config; the volatile threshold is a live ref.
64
53
  */
65
54
  function installCompactionTuning(ctx, config = defaultCompactionTuningConfig) {
66
55
  const logger = ctx.logger("compaction-tuning");
67
- let source = () => Config(config);
56
+ const live = () => ({
57
+ thresholdRatio: typeof config.thresholdRatio === "number" ? config.thresholdRatio : config.thresholdRatio.get() ?? DEFAULT_THRESHOLD_RATIO,
58
+ retainTokens: config.retainTokens
59
+ });
68
60
  /** Smallest routed context window seen so far (advisory guard, best effort). */
69
61
  let minWindow;
70
62
  let probing = false;
71
63
  /**
72
- * Learn the routed context windows so a write can be refused when it would
64
+ * Learn the routed context windows and log when the current pairing would
73
65
  * leave `retainTokens >= floor(window x thresholdRatio)` — the condition
74
66
  * under which the engine raises `TargetPressureConfigError` per route and
75
- * automatic condensation quietly stops for that route.
67
+ * automatic condensation quietly stops for that route. Advisory only: the
68
+ * dsh 0.1.7 settings model has no custom write-validation seam, so a bad
69
+ * pairing surfaces through the engine's own per-route error instead.
76
70
  */
77
71
  const probeWindows = () => {
78
72
  if (probing || minWindow !== void 0) return;
@@ -89,7 +83,10 @@ function installCompactionTuning(ctx, config = defaultCompactionTuningConfig) {
89
83
  }
90
84
  if (smallest !== void 0) {
91
85
  minWindow = smallest;
92
- logger.info(`smallest routed context window: ${smallest} tokens`);
86
+ const value = live();
87
+ const thresholdTokens = Math.floor(smallest * value.thresholdRatio);
88
+ if (value.retainTokens >= thresholdTokens) logger.warn(`retainTokens (${value.retainTokens}) is at or above the threshold tokens for the smallest routed window (${smallest} x ${value.thresholdRatio} = ${thresholdTokens}); automatic condensation will error per route until the threshold rises`);
89
+ else logger.info(`smallest routed context window: ${smallest} tokens`);
93
90
  }
94
91
  } catch (error) {
95
92
  logger.warn(`context-window probe failed (retention guard disabled): ${error instanceof Error ? error.message : String(error)}`);
@@ -99,49 +96,38 @@ function installCompactionTuning(ctx, config = defaultCompactionTuningConfig) {
99
96
  })();
100
97
  };
101
98
  /**
102
- * Overwrite only this module's two fields on the engine's current policy and
103
- * hand the engine a fresh frozen object.
104
- * @param reason - short diagnostic tag for the log line.
99
+ * Hand the engine a frozen policy view carrying THIS module's two fields as
100
+ * getters over the live refs: every engine policy re-read (per event) sees
101
+ * the current General-row value with no re-apply step. Every other policy
102
+ * field is carried over verbatim; `retainRatio` is dropped whenever the
103
+ * absolute `retainTokens` is in force, because the engine treats the two as
104
+ * mutually exclusive. Idempotent — re-run when the engine mounts late.
105
105
  */
106
- const push = (reason) => {
106
+ const installLivePolicy = (reason) => {
107
107
  const compaction = ctx.get("compaction");
108
108
  if (compaction === void 0) return;
109
109
  const base = compaction.config;
110
110
  if (base === void 0) return;
111
- const value = source();
112
- const next = {
111
+ const current = live();
112
+ const view = {
113
113
  ...base,
114
- thresholdRatio: value.thresholdRatio,
115
- retainTokens: value.retainTokens
114
+ get thresholdRatio() {
115
+ return live().thresholdRatio;
116
+ },
117
+ get retainTokens() {
118
+ return live().retainTokens;
119
+ }
116
120
  };
117
- delete next.retainRatio;
118
- compaction.config = Object.freeze(next);
119
- logger.info(`applied (${reason}): thresholdRatio=${value.thresholdRatio} retainTokens=${value.retainTokens}`);
121
+ delete view.retainRatio;
122
+ compaction.config = Object.freeze(view);
123
+ logger.info(`live policy installed (${reason}): thresholdRatio=${current.thresholdRatio} retainTokens=${current.retainTokens}`);
120
124
  };
121
125
  ctx.inject(["compaction"], () => {
122
- push("compaction-ready");
126
+ installLivePolicy("compaction-ready");
123
127
  probeWindows();
124
128
  });
125
- ctx.inject(["settings"], (settingsCtx) => {
126
- if (settingsCtx.settings.get(COMPACTION_SETTINGS_NS) !== void 0) {
127
- logger.info("settings namespace already served; leaving it to its owner");
128
- return;
129
- }
130
- settingsCtx.settings.installSection(ctx, COMPACTION_SETTINGS_NS, Config, Config(config), {
131
- setSource: (current) => {
132
- source = current;
133
- },
134
- onChange: () => {
135
- push("settings-updated");
136
- },
137
- validate: (value) => {
138
- if (minWindow === void 0) return;
139
- const thresholdTokens = Math.floor(minWindow * value.thresholdRatio);
140
- if (value.retainTokens >= thresholdTokens) throw new Error(`retainTokens (${value.retainTokens}) must stay below the threshold tokens for every routed model; the smallest window here is ${minWindow}, so ${value.thresholdRatio} leaves only ${thresholdTokens}`);
141
- }
142
- });
143
- push("settings-installed");
144
- probeWindows();
129
+ ctx.inject(["settings"], (child) => {
130
+ child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
145
131
  });
146
132
  }
147
133
  /** Mount the tuning as the row's whole apply (the row config is the settings base). */
@@ -1,4 +1,4 @@
1
- import { Context } from "@deepseek-ai/cordis";
1
+ import { Context, Volatile } from "@deepseek-ai/cordis";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
 
4
4
  //#region src/failover/config.d.ts
@@ -19,12 +19,36 @@ declare const name = "dashr-failover";
19
19
  * (dormant) in compositions without a settings service.
20
20
  */
21
21
  declare const inject: string[];
22
- /** The `failover` settings schema: two optional fallback slots, both default "not set". */
22
+ /**
23
+ * The row settings schema: two optional fallback slots, both default "not set".
24
+ *
25
+ * Both fields are volatile (dsh 0.1.7 settings model): the settings General
26
+ * row edits them live through `remote.settings` — the write lands as a
27
+ * profile-layer patch override on THIS row's entry, the runtime rewrites the
28
+ * resolved refs in place, and every request re-reads them without a reload.
29
+ */
23
30
  declare const Config: z<FailoverConfig>;
24
- /** Install the failover. A no-op composition degrades gracefully (empty chain = pass-through). */
25
- declare function installFailover(ctx: Context, config?: FailoverConfig): void;
31
+ /**
32
+ * The row config as cordis hands it over post-resolution: volatile fields are
33
+ * live references, not plain strings.
34
+ */
35
+ interface ResolvedFailoverConfig {
36
+ fallback1: string | Volatile<string>;
37
+ fallback2: string | Volatile<string>;
38
+ }
39
+ /**
40
+ * Install the failover. A no-op composition degrades gracefully (empty chain = pass-through).
41
+ *
42
+ * dsh 0.1.7 settings model: the row entry IS the settings namespace. The
43
+ * volatile schema fields above make it form-editable through `remote.settings`
44
+ * (edits persist as a profile-layer patch override via the config editor), the
45
+ * resolved refs re-read live per request, and the native auto-generated form
46
+ * is suppressed in favor of this plugin's own General row — the same shape
47
+ * upstream `agent-default-model` uses for its selection surface.
48
+ */
49
+ declare function installFailover(ctx: Context, config?: ResolvedFailoverConfig): void;
26
50
  /** Mount the failover as the row's whole apply (the row config is the chain base). */
27
- declare function apply(ctx: Context, config: FailoverConfig | undefined): void;
51
+ declare function apply(ctx: Context, config: ResolvedFailoverConfig | undefined): void;
28
52
  declare const _default: {
29
53
  name: string;
30
54
  inject: string[];
@@ -32,4 +56,4 @@ declare const _default: {
32
56
  apply: typeof apply;
33
57
  };
34
58
  //#endregion
35
- export { Config, apply, _default as default, inject, installFailover, name };
59
+ export { Config, ResolvedFailoverConfig, apply, _default as default, inject, installFailover, name };
@@ -4,20 +4,6 @@ import z from "@deepseek-ai/schemastery";
4
4
  import { RetryId } from "@deepseek-ai/dsh-llm-retry";
5
5
 
6
6
  //#region src/failover/config.ts
7
- /**
8
- * The `failover` general-fallback config (host-plane, per-turn latch).
9
- *
10
- * Pure module — no `@deepseek-ai/*` value imports, so both the host module and
11
- * the client half may import it (the client type-only) without dragging a host
12
- * runtime dependency into the browser bundle.
13
- *
14
- * A route is a `provider/model` string (e.g. `deepseek/deepseek-v4-flash`).
15
- * The empty string `''` means "not set" — the slot is skipped.
16
- *
17
- * @module dashr/failover/config
18
- */
19
- /** The settings namespace the General row reads and writes. */
20
- const FAILOVER_SETTINGS_NS = "failover";
21
7
  /** Failure codes that activate a fallback switch. */
22
8
  const FAILOVER_TRIGGER_CODES = {
23
9
  AUTH: true,
@@ -54,11 +40,24 @@ const name = "dashr-failover";
54
40
  * (dormant) in compositions without a settings service.
55
41
  */
56
42
  const inject = [];
57
- /** The `failover` settings schema: two optional fallback slots, both default "not set". */
43
+ /**
44
+ * The row settings schema: two optional fallback slots, both default "not set".
45
+ *
46
+ * Both fields are volatile (dsh 0.1.7 settings model): the settings General
47
+ * row edits them live through `remote.settings` — the write lands as a
48
+ * profile-layer patch override on THIS row's entry, the runtime rewrites the
49
+ * resolved refs in place, and every request re-reads them without a reload.
50
+ */
58
51
  const Config = z.object({
59
- fallback1: z.string().default(""),
60
- fallback2: z.string().default("")
52
+ fallback1: z.string().default("").volatile(),
53
+ fallback2: z.string().default("").volatile()
61
54
  });
55
+ /** Read one field that may be a plain value (tests, defaults) or a live ref (composed row). */
56
+ function readSlot(slot) {
57
+ if (typeof slot === "string") return slot;
58
+ const value = slot.get();
59
+ return typeof value === "string" ? value : "";
60
+ }
62
61
  /** Replace provider/model on a request config, dropping the inherited reasoning effort. */
63
62
  function overrideConfig(seed, to) {
64
63
  const { reasoningEffort: _inherited,...rest } = seed;
@@ -68,17 +67,24 @@ function overrideConfig(seed, to) {
68
67
  model: to.model
69
68
  };
70
69
  }
71
- /** Install the failover. A no-op composition degrades gracefully (empty chain = pass-through). */
70
+ /**
71
+ * Install the failover. A no-op composition degrades gracefully (empty chain = pass-through).
72
+ *
73
+ * dsh 0.1.7 settings model: the row entry IS the settings namespace. The
74
+ * volatile schema fields above make it form-editable through `remote.settings`
75
+ * (edits persist as a profile-layer patch override via the config editor), the
76
+ * resolved refs re-read live per request, and the native auto-generated form
77
+ * is suppressed in favor of this plugin's own General row — the same shape
78
+ * upstream `agent-default-model` uses for its selection surface.
79
+ */
72
80
  function installFailover(ctx, config = defaultFailoverConfig) {
73
81
  const logger = ctx.logger("llm-failover");
74
- let source = () => Config(config);
75
- ctx.inject(["settings"], (sctx) => {
76
- sctx.settings.installSection(ctx, FAILOVER_SETTINGS_NS, Config, Config(config), {
77
- setSource: (current) => {
78
- source = current;
79
- },
80
- onChange: () => {}
81
- });
82
+ const source = () => ({
83
+ fallback1: readSlot(config.fallback1),
84
+ fallback2: readSlot(config.fallback2)
85
+ });
86
+ ctx.inject(["settings"], (child) => {
87
+ child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
82
88
  });
83
89
  const latches = /* @__PURE__ */ new Map();
84
90
  ctx.on("agent/disposed", ({ agent }) => {
@@ -6,7 +6,7 @@ import { FsError } from "@deepseek-ai/dsh-fs";
6
6
  import { writableRoots } from "@deepseek-ai/dsh-sandbox";
7
7
  import { stat } from "node:fs/promises";
8
8
 
9
- //#region node_modules/@deepseek-ai/dsh-fs-sandbox/lib/index.js
9
+ //#region ../upstream/deepseek-harness/packages/fs/fs-sandbox/lib/index.js
10
10
  /**
11
11
  * Path-containment mechanics for the filesystem sandbox. Canonical spellings
12
12
  * take the fast lexical path; filesystem identity supplies the conservative
package/lib/index.js CHANGED
@@ -2861,10 +2861,7 @@ function createLlmCompletionTool(deps) {
2861
2861
  type: "text",
2862
2862
  text: prompt
2863
2863
  }],
2864
- source: {
2865
- kind: "plugin",
2866
- plugin: "better-dsh"
2867
- }
2864
+ source: { kind: "plugin:better-dsh" }
2868
2865
  })],
2869
2866
  maxTokens,
2870
2867
  ...system !== void 0 ? { system } : {},
@@ -3528,14 +3525,14 @@ function apply(ctx, config) {
3528
3525
  })) runtimeCtx.tools.register(bridgeTool);
3529
3526
  runtimeCtx.tools.register(createLlmCompletionTool({ requireLlm: () => runtimeCtx.get("llm") }));
3530
3527
  const wireMasked = /* @__PURE__ */ new WeakSet();
3531
- runtimeCtx.on("agent/session-start", ({ agent }) => {
3532
- if (wireMasked.has(agent)) return;
3528
+ runtimeCtx.on("agent/created", ({ agent }) => {
3529
+ if (wireMasked.has(agent)) return void 0;
3533
3530
  wireMasked.add(agent);
3534
3531
  try {
3535
3532
  captureAllTools(runtimeCtx, agent);
3536
3533
  const visible = new Set(agent.ctx.tools.schemas(agent).map((schema) => schema.name));
3537
3534
  const deny = [...WIRE_MASKED_NAMES].filter((name$1) => visible.has(name$1));
3538
- if (deny.length === 0) return;
3535
+ if (deny.length === 0) return void 0;
3539
3536
  try {
3540
3537
  agent.ctx.tools.restrict({ deny });
3541
3538
  return;
@@ -1,4 +1,4 @@
1
- import { r as MobileConfig } from "../web-trust-CgpBoFv3.js";
1
+ import { r as MobileConfig } from "../web-trust-BihHCelF.js";
2
2
  import { Context } from "@deepseek-ai/cordis";
3
3
  import z from "@deepseek-ai/schemastery";
4
4
 
@@ -0,0 +1,24 @@
1
+ import { Context } from "@deepseek-ai/cordis";
2
+ import z from "@deepseek-ai/schemastery";
3
+
4
+ //#region src/remote/plugin.d.ts
5
+
6
+ declare const name = "dashr-remote";
7
+ declare const inject: string[];
8
+ /** 运行参数旋钮(Ruling P18):无别名表面——容器一律显式 'docker:' / 'incus:' 前缀。 */
9
+ interface RemoteRowConfig {
10
+ execTimeoutSec: number;
11
+ idleTtlSec: number;
12
+ maxOutputChars: number;
13
+ }
14
+ declare const Config: z<RemoteRowConfig>;
15
+ /** MUST stay an arrow(RM0 plugin.ts 同注:function 声明会被 cordis 当构造器 new 掉,返回的 disposer 丢失)。 */
16
+ declare const apply: (ctx: Context, config: RemoteRowConfig) => (() => void);
17
+ declare const _default: {
18
+ name: string;
19
+ inject: string[];
20
+ Config: z<RemoteRowConfig>;
21
+ apply: (ctx: Context, config: RemoteRowConfig) => (() => void);
22
+ };
23
+ //#endregion
24
+ export { Config, RemoteRowConfig, apply, _default as default, inject, name };