@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
|
@@ -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
|
|
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
|
|
904
|
-
this.dscodePlanPrefetch(agent, measurement, spec,
|
|
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 {
|
package/vendor/tui/lib/index.mjs
CHANGED
|
@@ -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
|
|
1465
|
+
contextWindow: dscodeEffectiveContextWindow(info?.context, info),
|
|
1466
1466
|
thresholdRatio: await dscodePricedThresholdRatio(row.provider, row.model),
|
|
1467
1467
|
label: row.provider + '/' + row.model,
|
|
1468
1468
|
});
|