@toddzheng024/dscode-bundle 0.7.15 → 0.7.16

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/bootstrap.mjs CHANGED
@@ -18,6 +18,7 @@ export function apply(ctx) {
18
18
  // resolution the launcher runs happens here before the agent preset mounts.
19
19
  process.env.DSCODE_SKILL_ANCESTOR_DIRS = JSON.stringify(ancestorSkillDirs({ cwd: process.cwd(), home: homedir() }));
20
20
  process.env.DSCODE_INSTRUCTION_HOME = writeWorkspaceInstructions({ cwd: process.cwd(), home: homedir(), stateDir: home }) ?? home;
21
+ process.env.DSCODE_SANDBOX_RUNNER = join(root, 'plugins/tui-tools/sandbox-runner.mjs');
21
22
  ctx.provide('dscodePaths', { presets: join(root, 'presets'), hooks: hookConfig.path });
22
23
  const oldPath = process.env.PATH;
23
24
  const added = join(root, 'bin');
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.7.15",
2
+ "version": "0.7.16",
3
3
  "type": "module",
4
4
  "license": "MIT",
5
5
  "author": "Todd Zheng",
@@ -28,6 +28,46 @@ export function prefetchThresholdTokens(thresholdTokens, contextWindow, leadRati
28
28
  return Math.max(0, thresholdTokens - Math.floor(contextWindow * leadRatio));
29
29
  }
30
30
 
31
+ /**
32
+ * The window the session messages may occupy. An adapter that keeps the completion
33
+ * budget inside the context window rejects a request once messages plus completion
34
+ * exceed it, so a threshold priced from the full window sits above a ceiling the
35
+ * provider enforces first: the pressure path never fires and every compaction arrives
36
+ * through overflow recovery, which summarizes synchronously and stalls the turn.
37
+ *
38
+ * A reported budget is always subtracted: every OpenAI-compatible adapter counts
39
+ * `max_tokens` toward the same limit, and an adapter that reports none keeps the full
40
+ * window, so the subtraction can only make compaction earlier than the low-level
41
+ * threshold would, never later than the provider allows.
42
+ * @param context - the resolved model info context, `{ contextWindow }`.
43
+ * @param modelInfo - the resolved model info, whose `defaultMaxTokens` is that budget.
44
+ */
45
+ /** The completion budget an adapter reserves inside the window, 0 when it reports none. */
46
+ function completionReserve(contextWindow, modelInfo) {
47
+ const reserve = modelInfo?.defaultMaxTokens;
48
+ if (!Number.isInteger(reserve) || reserve <= 0 || reserve >= contextWindow) return 0;
49
+ return reserve;
50
+ }
51
+
52
+ export function effectiveContextWindow(context, modelInfo) {
53
+ const contextWindow = context?.contextWindow;
54
+ if (!Number.isInteger(contextWindow) || contextWindow <= 0) return contextWindow;
55
+ return contextWindow - completionReserve(contextWindow, modelInfo);
56
+ }
57
+
58
+ /**
59
+ * Whether a measured request envelope plus the reserved completion budget fits the models
60
+ * window, which is the rule the provider enforces. Overflow recovery uses it to tell a
61
+ * request that pruning alone returned under the window from one that still needs a summary.
62
+ * @param totalTokens - the measured request envelope.
63
+ * @param modelInfo - the resolved model info: `context.contextWindow` and `defaultMaxTokens`.
64
+ */
65
+ export function fitsInWindow(totalTokens, modelInfo) {
66
+ const contextWindow = modelInfo?.context?.contextWindow;
67
+ if (!Number.isInteger(totalTokens) || !Number.isInteger(contextWindow) || contextWindow <= 0) return false;
68
+ return totalTokens + completionReserve(contextWindow, modelInfo) <= contextWindow;
69
+ }
70
+
31
71
  /** The route's threshold ratio, waiting for the OpenRouter listing when it has not loaded yet. */
32
72
  export async function pricedThresholdRatio(provider, model, now = Date.now()) {
33
73
  if (provider === 'openrouter') await ensureOpenRouterModels({ home: process.env.DSH_HOME, now });
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ // A custom runner for @deepseek-ai/dsh-sandbox-local, selected through the `sandbox`
3
+ // row's `runnerCommand`. The provider appends a bwrap-compatible profile and then
4
+ // `--` and the command:
5
+ //
6
+ // dsh-sandbox-runner --ro-bind / / --dev /dev --unshare-pid --proc /proc
7
+ // --die-with-parent [--tmpfs /tmp] [--bind <root> <root>] -- <command...>
8
+ //
9
+ // We translate the profile into a Seatbelt one and add the single grant the built-in
10
+ // profile lacks: /dev/ptmx. Without it a confined command cannot allocate a PTY
11
+ // (posix_openpt returns EPERM), which silently breaks nested harnesses, tmux, expect
12
+ // and any node-pty based suite. When the kernel refuses to apply another profile —
13
+ // which is exactly what happens inside an already-confined process — the command
14
+ // inherits the enclosing profile instead of nesting a second one.
15
+ import { spawn, spawnSync } from 'node:child_process';
16
+ import { realpathSync } from 'node:fs';
17
+ import { tmpdir } from 'node:os';
18
+ import { resolve } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+
21
+ export const NAME = 'dscode-sandbox-runner';
22
+ // Configure this string as runnerFailureSignatures so the provider recognises our
23
+ // own failures instead of reading them as a denied command.
24
+ export const FATAL_PREFIX = `${NAME}: fatal: `;
25
+ // Informational output must never carry the configured failure signature: the provider
26
+ // turns any non-zero exit whose stderr matches it into a sandbox failure, so a notice
27
+ // would misreport a failing command as a broken runner.
28
+ export const NOTICE_PREFIX = `${NAME}: notice: `;
29
+ export const SANDBOX_EXEC = '/usr/bin/sandbox-exec';
30
+
31
+ const OPERAND_FLAGS = new Map([['--ro-bind', 2], ['--bind', 2], ['--tmpfs', 1], ['--dev', 1], ['--proc', 1], ['--dir', 1]]);
32
+
33
+ const canonical = path => {
34
+ try { return realpathSync(path); } catch { return resolve(path); }
35
+ };
36
+
37
+ const sbpl = path => `"${String(path).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
38
+
39
+ /** Split the appended bwrap-compatible profile from the command after `--`. */
40
+ export function parseProfile(argv) {
41
+ const writable = [];
42
+ let index = 0;
43
+ while (index < argv.length) {
44
+ const token = argv[index];
45
+ if (token === '--') { index += 1; break; }
46
+ if (!token.startsWith('--')) throw new Error(`unexpected profile argument ${JSON.stringify(token)}`);
47
+ const operands = OPERAND_FLAGS.get(token) ?? 0;
48
+ if (operands === 2) {
49
+ const [, destination] = [argv[index + 1], argv[index + 2]];
50
+ if (!destination) throw new Error(`${token} needs two operands`);
51
+ if (token === '--bind') writable.push(destination);
52
+ } else if (operands === 1 && !argv[index + 1]) throw new Error(`${token} needs an operand`);
53
+ index += operands + 1;
54
+ }
55
+ return { writable, command: argv.slice(index) };
56
+ }
57
+
58
+ /** Writable roots that mirror the provider's own Seatbelt grant, plus the temp areas. */
59
+ export function writableRoots(parsed, { temp = tmpdir() } = {}) {
60
+ return [...new Set([...parsed.writable, '/tmp', temp].map(canonical))];
61
+ }
62
+
63
+ /** The SBPL profile: upstream's deny-by-default write policy plus /dev/ptmx. */
64
+ export function seatbeltProfile(roots) {
65
+ const forms = [
66
+ '(version 1)',
67
+ '(allow default)',
68
+ '(deny file-write*)',
69
+ `(allow file-write* (literal ${sbpl('/dev/null')}))`,
70
+ `(allow file-write* (literal ${sbpl('/dev/ptmx')}))`,
71
+ ];
72
+ if (roots.length) forms.push(`(allow file-write* ${roots.map(root => `(subpath ${sbpl(root)})`).join(' ')})`);
73
+ return forms.join(' ');
74
+ }
75
+
76
+ /** Whether this process may apply a Seatbelt profile at all. */
77
+ export function seatbeltApplies(exec = SANDBOX_EXEC) {
78
+ const probe = spawnSync(exec, ['-p', '(version 1)(allow default)', '/usr/bin/true'], { stdio: ['ignore', 'ignore', 'pipe'], encoding: 'utf8' });
79
+ if (probe.error) return probe.error.code === 'ENOENT' ? { ok: false, reason: 'missing' } : { ok: false, reason: probe.error.message };
80
+ if (probe.status === 0) return { ok: true };
81
+ return { ok: false, reason: (probe.stderr ?? '').trim().split('\n').at(-1) || `exit ${probe.status}` };
82
+ }
83
+
84
+ const SIGNAL_CODES = { SIGHUP: 129, SIGINT: 130, SIGTERM: 143 };
85
+
86
+ function runUnder(program, args) {
87
+ return new Promise(resolvePromise => {
88
+ const child = spawn(program, args, { stdio: 'inherit' });
89
+ const forward = signal => () => { try { child.kill(signal); } catch { /* already gone */ } };
90
+ const handlers = Object.keys(SIGNAL_CODES).map(signal => [signal, forward(signal)]);
91
+ for (const [signal, handler] of handlers) process.on(signal, handler);
92
+ child.on('error', error => { process.stderr.write(`${FATAL_PREFIX}${error.message}\n`); resolvePromise(126); });
93
+ child.on('exit', (code, signal) => resolvePromise(code ?? SIGNAL_CODES[signal] ?? 1));
94
+ });
95
+ }
96
+
97
+ export async function run(argv, { exec = SANDBOX_EXEC, stderr = process.stderr } = {}) {
98
+ const parsed = parseProfile(argv);
99
+ if (parsed.command.length === 0) {
100
+ stderr.write(`${FATAL_PREFIX}no command after --\n`);
101
+ return 126;
102
+ }
103
+ const [program, ...args] = parsed.command;
104
+ const applies = seatbeltApplies(exec);
105
+ if (!applies.ok && applies.reason === 'missing') {
106
+ stderr.write(`${FATAL_PREFIX}${exec} is not available; refusing to run unconfined\n`);
107
+ return 126;
108
+ }
109
+ if (!applies.ok) {
110
+ // Applying a profile is what the kernel refuses inside an existing one, so this
111
+ // process is already confined: inherit that profile rather than nest a second.
112
+ stderr.write(`${NOTICE_PREFIX}inheriting the enclosing profile (${applies.reason})\n`);
113
+ return runUnder(program, args);
114
+ }
115
+ return runUnder(exec, ['-p', seatbeltProfile(writableRoots(parsed)), '--', program, ...args]);
116
+ }
117
+
118
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
119
+ run(process.argv.slice(2)).then(code => { process.exitCode = code; }).catch(error => {
120
+ process.stderr.write(`${FATAL_PREFIX}${error.message}\n`);
121
+ process.exitCode = 126;
122
+ });
123
+ }
@@ -1,6 +1,8 @@
1
+ // dscode-compaction-overflow-prune-v1
2
+ // dscode-compaction-reserve-v1
1
3
  // dscode-compaction-prefetch-v1
2
4
  // dscode-compaction-threshold-v1
3
- import { prefetchThresholdTokens as dscodePrefetchThresholdTokens, pricedCompactionPolicy as dscodePricedCompactionPolicy } from "../../plugins/compaction/threshold.mjs";
5
+ import { effectiveContextWindow as dscodeEffectiveContextWindow, fitsInWindow as dscodeFitsInWindow, prefetchThresholdTokens as dscodePrefetchThresholdTokens, pricedCompactionPolicy as dscodePricedCompactionPolicy } from "../../plugins/compaction/threshold.mjs";
4
6
  import z from "@deepseek-ai/schemastery";
5
7
  import { CompactionEngine, CompactionId, ManualCompactionError, compactCheckpointSource, toolPairingBalancedAfter, toolPairingBalancedBefore } from "@deepseek-ai/dsh-compaction";
6
8
  import { BlockAssembler, CONTEXT_WINDOW_EXCEEDED_CODE, LlmError, contentHasImage, createUserMessage, errorChain } from "@deepseek-ai/dsh-llm";
@@ -888,20 +890,23 @@ var BasicCompactionEngine = class extends CompactionEngine {
888
890
  }
889
891
  const prune = this.ctx.get("toolResultPruner");
890
892
  if (trigger === "context-overflow") {
893
+ const dscodePrunedGeneration = agent.session.surface.replaceGeneration;
891
894
  if (prune !== void 0) {
892
895
  prune.pruneSession(agent.session);
893
896
  measurement = meter.measure(agent.session);
894
897
  }
898
+ if (await this.dscodeOverflowFits(agent, target, dscodePrunedGeneration, measurement, signal)) return null;
895
899
  const range = selectCompactableRange(agent.session, measurement, 0);
896
900
  if (range === null) return null;
897
901
  return this.compactRegion(range.start, range.end, agent, signal);
898
902
  }
899
- const context = (await this.ctx.llm.resolveModelInfo(target.provider, target.model, signal)).context;
903
+ const dscodeModelInfo = await this.ctx.llm.resolveModelInfo(target.provider, target.model, signal);
904
+ const context = dscodeModelInfo.context;
900
905
  assertNoActiveCompaction(agent.session, "automatic pressure compaction");
901
906
  const targetKey = `${target.provider}/${target.model}`;
902
907
  if (context === void 0) throw new TargetPressureConfigError(targetKey, `compaction-basic: no context capacity for ${targetKey}; configure contextWindow on that adapter model`);
903
- const spec = resolveCompactSpec(await dscodePricedCompactionPolicy(this.config, policy), context.contextWindow);
904
- this.dscodePlanPrefetch(agent, measurement, spec, context.contextWindow, signal);
908
+ const spec = resolveCompactSpec(await dscodePricedCompactionPolicy(this.config, policy), dscodeEffectiveContextWindow(context, dscodeModelInfo));
909
+ this.dscodePlanPrefetch(agent, measurement, spec, spec.contextWindow, signal);
905
910
  if (measurement.totalTokens < spec.thresholdTokens) return null;
906
911
  if (prune !== void 0) {
907
912
  prune.pruneSession(agent.session);
@@ -1061,6 +1066,22 @@ var BasicCompactionEngine = class extends CompactionEngine {
1061
1066
  return null;
1062
1067
  }
1063
1068
  }
1069
+ /**
1070
+ * dscode: whether overflow-recovery pruning alone returned the failed request under the
1071
+ * window. The caller retries whenever the prune replaced the surface, so a summary here
1072
+ * would only add a model call and its stall to a request that already fits.
1073
+ * @param agent - agent recovering from a provider context overflow.
1074
+ * @param target - the routed provider/model that rejected the request.
1075
+ * @param generation - the surface generation before the prune.
1076
+ * @param measurement - the measurement taken after the prune.
1077
+ * @param signal - live turn cancellation signal.
1078
+ * @returns whether the retry may skip compaction.
1079
+ */
1080
+ async dscodeOverflowFits(agent, target, generation, measurement, signal) {
1081
+ if (agent.session.surface.replaceGeneration <= generation) return false;
1082
+ const info = await this.ctx.llm.resolveModelInfo(target.provider, target.model, signal);
1083
+ return dscodeFitsInWindow(measurement.totalTokens, info);
1084
+ }
1064
1085
  /** Bind the effective token meter and dynamically dispatched summarizer hook. */
1065
1086
  regionDependencies() {
1066
1087
  return {
@@ -26,7 +26,7 @@ import { internals } from './internals.mjs';
26
26
  import { syncModelCapabilities } from './model-capabilities.mjs';
27
27
  import { ensureProviderRoute as dscodeEnsureProviderRoute, migrateOpenRouterProfile as dscodeMigrateOpenRouter } from '../../../plugins/providers/catalog.mjs';
28
28
  import { grokStatusSnapshot } from '../../../plugins/grok/status.mjs';
29
- import { compactionPreview as dscodeCompactionPreview, pricedThresholdRatio as dscodePricedThresholdRatio } from '../../../plugins/compaction/threshold.mjs';
29
+ import { compactionPreview as dscodeCompactionPreview, effectiveContextWindow as dscodeEffectiveContextWindow, pricedThresholdRatio as dscodePricedThresholdRatio } from '../../../plugins/compaction/threshold.mjs';
30
30
  import { dscodeLoadOpenRouterAccountFor, dscodeManagementKeyStatus, dscodeSaveManagementKey } from './app.mjs';
31
31
  import { buildModelSelection, applyModelSelectionToConfig, loadModelDirectory, modelSelectionLabel, pendingModelSelection, resolveEffectiveSelection } from './models.mjs';
32
32
  import { discoverProviderModels, loadProviderSettings, removeProviderSettings, saveProviderCredential, saveProviderConfiguration, subscribeProviderSettings, unsetProviderCredential, } from './provider-settings.mjs';
@@ -1462,7 +1462,7 @@ async function run(ctx, startup, io) {
1462
1462
  const info = await llm.resolveModelInfo(row.provider, row.model);
1463
1463
  return dscodeCompactionPreview({
1464
1464
  used,
1465
- contextWindow: info?.context?.contextWindow,
1465
+ contextWindow: dscodeEffectiveContextWindow(info?.context, info),
1466
1466
  thresholdRatio: await dscodePricedThresholdRatio(row.provider, row.model),
1467
1467
  label: row.provider + '/' + row.model,
1468
1468
  });