@arcfold/dsh 0.0.0-stage → 0.4.0

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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -2
  3. package/THIRD_PARTY_NOTICES.md +11 -0
  4. package/cordis.patch.yml +138 -0
  5. package/package.json +70 -4
  6. package/patches/deepseek-0.2.2-rc.1-transport.json +65 -0
  7. package/patches/deepseek-0.2.2-rc.1-transport.patch +61 -0
  8. package/presets/arcfold.patch.yml +110 -0
  9. package/runtime/README.md +19 -0
  10. package/runtime/bootstrap.json +15 -0
  11. package/runtime/desktop-python.tar.gz +0 -0
  12. package/runtime/host/package.json +11 -0
  13. package/runtime/host/pnpm-workspace.yaml +9 -0
  14. package/runtime/initial-policy.json +94 -0
  15. package/runtime/profile/pnpm-workspace.yaml +7 -0
  16. package/scripts/exact-package-patch.mjs +106 -0
  17. package/scripts/patch-deepseek-transport.mjs +14 -0
  18. package/src/agent.mjs +56 -0
  19. package/src/browse-scope.mjs +14 -0
  20. package/src/budget.mjs +369 -0
  21. package/src/business.mjs +334 -0
  22. package/src/context-material.mjs +119 -0
  23. package/src/deepseek-budget-plugin.mjs +58 -0
  24. package/src/deepseek-provider.mjs +57 -0
  25. package/src/deepseek-receipt.mjs +97 -0
  26. package/src/deepseek-transport.mjs +97 -0
  27. package/src/desktop/adapter.mjs +16 -0
  28. package/src/desktop/bootstrap.mjs +84 -0
  29. package/src/desktop/configuration.mjs +117 -0
  30. package/src/desktop/provider.mjs +46 -0
  31. package/src/desktop/python-runtime.mjs +105 -0
  32. package/src/desktop/setup-service.mjs +81 -0
  33. package/src/desktop/vendor/LICENSE.deepseek +21 -0
  34. package/src/desktop/vendor/NOTICE.md +5 -0
  35. package/src/desktop/vendor/deepseek-0.2.2-rc.1.mjs +2919 -0
  36. package/src/evidence-ownership.mjs +51 -0
  37. package/src/evidence.mjs +35 -0
  38. package/src/goal-context.mjs +52 -0
  39. package/src/goal-execution-tool.mjs +74 -0
  40. package/src/goal-execution.mjs +333 -0
  41. package/src/goal-tool.mjs +56 -0
  42. package/src/goal-work-control.mjs +63 -0
  43. package/src/goal-work.mjs +257 -0
  44. package/src/goals.mjs +578 -0
  45. package/src/importer.mjs +298 -0
  46. package/src/index.mjs +53 -0
  47. package/src/learning-contracts.mjs +220 -0
  48. package/src/learning-defaults.mjs +98 -0
  49. package/src/learning-goals.mjs +165 -0
  50. package/src/learning-plugin.mjs +28 -0
  51. package/src/learning-runner.mjs +275 -0
  52. package/src/learning.mjs +534 -0
  53. package/src/legacy-export.mjs +332 -0
  54. package/src/legacy-import-source.mjs +356 -0
  55. package/src/metered-provider-host.mjs +101 -0
  56. package/src/metered-transport.mjs +435 -0
  57. package/src/method-context.mjs +57 -0
  58. package/src/method-lifecycle.mjs +34 -0
  59. package/src/method-tool.mjs +61 -0
  60. package/src/methods.mjs +425 -0
  61. package/src/model-selection.mjs +41 -0
  62. package/src/native-context-call.mjs +88 -0
  63. package/src/native-continuation.mjs +217 -0
  64. package/src/native-request-authority.mjs +90 -0
  65. package/src/openai-models.mjs +4 -0
  66. package/src/openai-receipt.mjs +98 -0
  67. package/src/openai-transport.mjs +167 -0
  68. package/src/openai-usage.mjs +194 -0
  69. package/src/opportunities.mjs +295 -0
  70. package/src/preparation-artifacts.mjs +213 -0
  71. package/src/preparation-plugin.mjs +290 -0
  72. package/src/runtime-command.mjs +264 -0
  73. package/src/runtime-config.mjs +417 -0
  74. package/src/runtime-guard.mjs +284 -0
  75. package/src/runtime-host.mjs +212 -0
  76. package/src/runtime-init.mjs +278 -0
  77. package/src/runtime-installation.mjs +518 -0
  78. package/src/runtime-lifecycle.mjs +227 -0
  79. package/src/runtime-model-settings.mjs +111 -0
  80. package/src/runtime-readiness.mjs +285 -0
  81. package/src/runtime-workspace.mjs +18 -0
  82. package/src/session-bridge.mjs +365 -0
  83. package/src/store.mjs +394 -0
  84. package/src/tree/LICENSE +21 -0
  85. package/src/tree/attribution.mjs +120 -0
  86. package/src/tree/clusters.mjs +61 -0
  87. package/src/tree/consolidation.mjs +103 -0
  88. package/src/tree/context.mjs +211 -0
  89. package/src/tree/contracts.mjs +142 -0
  90. package/src/tree/experience.mjs +140 -0
  91. package/src/tree/experiment-budget.mjs +84 -0
  92. package/src/tree/hooks.mjs +254 -0
  93. package/src/tree/host-context.mjs +107 -0
  94. package/src/tree/host-process.mjs +288 -0
  95. package/src/tree/host.mjs +37 -0
  96. package/src/tree/improvement.mjs +106 -0
  97. package/src/tree/items.mjs +294 -0
  98. package/src/tree/jobs.mjs +184 -0
  99. package/src/tree/learning.mjs +202 -0
  100. package/src/tree/metrics.mjs +203 -0
  101. package/src/tree/model-owner.mjs +254 -0
  102. package/src/tree/observations.mjs +73 -0
  103. package/src/tree/prompts.mjs +500 -0
  104. package/src/tree/reports.mjs +67 -0
  105. package/src/tree/repository.mjs +236 -0
  106. package/src/tree/runtime.mjs +745 -0
  107. package/src/tree/script-writer.mjs +149 -0
  108. package/src/tree/scripts.mjs +208 -0
  109. package/src/tree/signals.mjs +118 -0
  110. package/src/tree/snapshots.mjs +379 -0
  111. package/src/tree/token-counter.mjs +75 -0
  112. package/src/tree/token-counter.py +37 -0
  113. package/src/tree/tokenizer-resources/LICENSE +21 -0
  114. package/src/tree/tokenizer-resources/README.md +31 -0
  115. package/src/tree/tokenizer-resources/deepseek_recipe-0.2.0-cp310-abi3-macosx_11_0_arm64.whl +0 -0
  116. package/src/tree/tokenizer-resources/manifest.json +61 -0
  117. package/src/tree/tokenizer-resources/native.pyi +1042 -0
  118. package/src/tree/tokenizer-resources/tokenizer-NOTICE.md +26 -0
  119. package/src/tree/tokenizer-resources/tokenizer-v4-NOTICE.md +27 -0
  120. package/src/tree/tokenizer-resources/tokenizer-v4.json +267359 -0
  121. package/src/tree/tokenizer-resources/tokenizer.json +267359 -0
  122. package/src/tree/tools.mjs +99 -0
  123. package/src/tree/tree.mjs +202 -0
  124. package/src/tree/ui.mjs +285 -0
  125. package/src/tree/verification.mjs +143 -0
  126. package/src/understandings.mjs +309 -0
  127. package/src/web-budget-recovery.mjs +181 -0
@@ -0,0 +1,379 @@
1
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
+ import { join, isAbsolute } from 'node:path';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { ownEvidenceDirectory, assertEvidenceOwnership } from '../evidence-ownership.mjs';
5
+ import { canonicalJson, digest, fail } from './contracts.mjs';
6
+ import { beneath, canonicalPath, contentHash, processPolicy, confinedArgv, spawnConfined } from './host-process.mjs';
7
+
8
+ export const SNAPSHOT_LIMITS = Object.freeze({ fileBytes: 20 * 1024 * 1024, totalBytes: 256 * 1024 * 1024, retentionDays: 30, scanAttempts: 2 });
9
+ export function workspaceIdentity(workspace) {
10
+ const path = realpathSync(workspace), stat = statSync(path, { bigint: true });
11
+ if (!stat.isDirectory()) fail('WORKSPACE_IDENTITY', 'Workspace must be a real directory');
12
+ return { path, dev: String(stat.dev), ino: String(stat.ino) };
13
+ }
14
+ export function relativeFile(path) {
15
+ if (typeof path !== 'string' || !path || isAbsolute(path) || path.includes('\\') || path.includes('\0') || path.split('/').some(p => !p || p === '.' || p === '..')) fail('SNAPSHOT_PATH', 'A canonical relative file path is required');
16
+ return path;
17
+ }
18
+
19
+ /**
20
+ * Trusted fixed worker, serialized into its own confined process. It never
21
+ * loads workspace code. Production file reads/writes do not run in the host.
22
+ */
23
+ export async function workspaceWorker(request) {
24
+ const fs = await import('node:fs'), path = await import('node:path'), crypto = await import('node:crypto');
25
+ const hash = bytes => crypto.createHash('sha256').update(bytes).digest('hex');
26
+ const error = (code, message) => Object.assign(new Error(message), { code });
27
+ const inside = (target, root) => target === root || target.startsWith(root + path.sep);
28
+ const root = fs.realpathSync(request.workspace), directory = fs.realpathSync(request.runDirectory);
29
+ const identity = () => { const stat = fs.statSync(root, { bigint: true }); return { path: fs.realpathSync(request.workspace), dev: String(stat.dev), ino: String(stat.ino) }; };
30
+ const assertIdentity = () => { const current = identity(); if (['path', 'dev', 'ino'].some(key => current[key] !== request.workspaceIdentity[key])) throw error('WORKSPACE_IDENTITY', 'Workspace was replaced'); };
31
+ const exclusions = [], files = []; let totalBytes = 0;
32
+ const ignored = new Set(['.git', 'node_modules', 'dist', 'build', 'coverage', '.cache', '.ssh', '.aws', '.gnupg']);
33
+ const exclusion = (name, absolute) => {
34
+ if (request.protectedRoots.some(p => inside(absolute, p)) || inside(absolute, request.runtimeRoot)) return 'arcfold_private';
35
+ if (ignored.has(name)) return 'excluded_directory';
36
+ if (name.startsWith('.env') || ['.npmrc', '.netrc', 'credentials', 'credentials.json', 'id_rsa', 'id_ed25519'].includes(name) || /\.(pem|key|p12|pfx)$/i.test(name)) return 'credentials';
37
+ return null;
38
+ };
39
+ const checkedPath = (relative, allowMissingParents = false) => {
40
+ if (typeof relative !== 'string' || !relative || path.isAbsolute(relative) || relative.includes('\\') || relative.includes('\0') || relative.split('/').some(p => !p || p === '.' || p === '..')) throw error('SNAPSHOT_PATH', 'Invalid relative path');
41
+ let absolute = root;
42
+ for (const [index, name] of relative.split('/').entries()) {
43
+ absolute = path.join(absolute, name);
44
+ if (exclusion(name, absolute)) throw error('SNAPSHOT_EXCLUDED', 'Protected or excluded path');
45
+ try {
46
+ const stat = fs.lstatSync(absolute); if (stat.isSymbolicLink()) throw error('SNAPSHOT_SYMLINK', 'Symlink path refused');
47
+ if (index < relative.split('/').length - 1 && !stat.isDirectory()) throw error('SNAPSHOT_PATH', 'Parent must remain a directory');
48
+ } catch (e) { if (e.code !== 'ENOENT' || !allowMissingParents && index < relative.split('/').length - 1) throw e; }
49
+ }
50
+ return absolute;
51
+ };
52
+ const read = relative => {
53
+ assertIdentity(); const absolute = checkedPath(relative, true); let fd;
54
+ try {
55
+ fd = fs.openSync(absolute, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
56
+ const before = fs.fstatSync(fd); if (!before.isFile() || before.nlink !== 1 || (before.mode & 0o7000)) throw error('SNAPSHOT_FILE_TYPE', 'Only ordinary single-link files are supported');
57
+ if (before.size > request.limits.fileBytes) throw error('SNAPSHOT_FILE_SIZE', 'File exceeds content coverage');
58
+ const parts = []; let length = 0;
59
+ for (;;) { const buffer = Buffer.alloc(Math.min(65536, request.limits.fileBytes + 1 - length)), count = fs.readSync(fd, buffer, 0, buffer.length, null); if (!count) break; length += count; if (length > request.limits.fileBytes) throw error('SNAPSHOT_FILE_SIZE', 'File grew beyond content coverage'); parts.push(buffer.subarray(0, count)); }
60
+ const content = Buffer.concat(parts, length), after = fs.fstatSync(fd), current = fs.lstatSync(absolute);
61
+ if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ino !== current.ino || before.dev !== current.dev || current.isSymbolicLink() || !inside(fs.realpathSync(absolute), root)) throw error('SNAPSHOT_BUSY', 'File changed while read');
62
+ return { content, sha256: hash(content), size: content.length, mode: before.mode & 0o777 };
63
+ } catch (e) { if (e.code === 'ENOENT') return null; throw e; }
64
+ finally { if (fd !== undefined) fs.closeSync(fd); }
65
+ };
66
+ const journalPath = path.join(directory, 'worker-journal.json');
67
+ const journal = value => {
68
+ const next = path.join(directory, 'worker-journal.next'), body = { requestHash: request.requestHash, pid: process.pid, ...value };
69
+ const fd = fs.openSync(next, 'w', 0o600); try { fs.writeFileSync(fd, JSON.stringify(body)); fs.fsyncSync(fd); } finally { fs.closeSync(fd); }
70
+ fs.renameSync(next, journalPath); const parent = fs.openSync(directory, 'r'); try { fs.fsyncSync(parent); } finally { fs.closeSync(parent); }
71
+ if (request.faultAt === value.phase) process.exit(91);
72
+ return body;
73
+ };
74
+ let applied = [];
75
+ try {
76
+ assertIdentity();
77
+ if (request.operation === 'scan') {
78
+ const walk = relative => {
79
+ const absolute = relative ? checkedPath(relative) : root;
80
+ for (const name of fs.readdirSync(absolute).sort()) {
81
+ const child = relative ? relative + '/' + name : name, target = path.join(root, child), reason = exclusion(name, target);
82
+ if (reason) { exclusions.push({ path: child, reason }); continue; }
83
+ const stat = fs.lstatSync(target);
84
+ if (stat.isSymbolicLink()) { exclusions.push({ path: child, reason: 'symlink' }); continue; }
85
+ if (stat.isDirectory()) { walk(child); continue; }
86
+ if (!stat.isFile() || stat.nlink !== 1 || (stat.mode & 0o7000)) { exclusions.push({ path: child, reason: 'non_regular_or_linked' }); continue; }
87
+ if (stat.size > request.limits.fileBytes) { exclusions.push({ path: child, reason: 'file_size' }); continue; }
88
+ const file = read(child); if (!file) throw error('SNAPSHOT_BUSY', 'File disappeared while scanning');
89
+ totalBytes += file.size; if (totalBytes > request.limits.totalBytes) throw error('SNAPSHOT_TOTAL_SIZE', 'Workspace exceeds bounded snapshot storage');
90
+ if (request.copy) { const output = path.join(directory, 'files', file.sha256); if (!fs.existsSync(output)) fs.writeFileSync(output, file.content, { flag: 'wx', mode: 0o600 }); }
91
+ files.push({ path: child, sha256: file.sha256, size: file.size, mode: file.mode });
92
+ }
93
+ };
94
+ fs.mkdirSync(path.join(directory, 'files'), { recursive: true, mode: 0o700 }); walk(''); assertIdentity();
95
+ process.stdout.write(JSON.stringify({ ok: true, manifest: { files, exclusions, totalBytes } })); return;
96
+ }
97
+ if (request.operation !== 'restore') throw error('SNAPSHOT_OPERATION', 'Unsupported fixed worker operation');
98
+ journal({ phase: 'started', applied });
99
+ const currentHash = change => { const current = read(change.path); return current ? { sha256: current.sha256, mode: current.mode } : null; };
100
+ const fingerprintForWorker = file => ({ sha256: file.sha256, mode: file.mode });
101
+ const same = (a, b) => a === null && b === null || a !== null && b !== null && a.sha256 === b.sha256 && a.mode === b.mode;
102
+ for (const change of request.changes) if (!same(currentHash(change), change.current)) throw error('RESTORE_CONFLICT', 'Files changed after preview');
103
+ for (const change of request.changes) {
104
+ if (!change.target || change.skipWrite) continue;
105
+ const content = fs.readFileSync(path.join(directory, 'files', change.target.sha256)); if (hash(content) !== change.target.sha256) throw error('SNAPSHOT_HASH', 'Stored target content changed');
106
+ const components = change.path.split('/').slice(0, -1); let parent = '';
107
+ for (const name of components) { parent = parent ? parent + '/' + name : name; const absolute = checkedPath(parent, true); if (!fs.existsSync(absolute)) fs.mkdirSync(absolute, { mode: 0o755 }); checkedPath(parent); }
108
+ const stage = checkedPath(change.stage), previous = read(change.stage);
109
+ if (previous) { if (!same(fingerprintForWorker(previous), fingerprintForWorker(change.target))) throw error('RESTORE_CONFLICT', 'A staging file was changed'); continue; }
110
+ const fd = fs.openSync(stage, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW, change.target.mode);
111
+ try { fs.writeFileSync(fd, content); fs.fchmodSync(fd, change.target.mode); fs.fsyncSync(fd); } finally { fs.closeSync(fd); }
112
+ }
113
+ journal({ phase: 'staged', applied });
114
+ for (const [index, change] of request.changes.entries()) {
115
+ assertIdentity(); if (!same(currentHash(change), change.current)) throw error('RESTORE_CONFLICT', 'A file changed during restore');
116
+ const target = checkedPath(change.path, true);
117
+ if (!change.skipWrite) { if (change.target) fs.renameSync(checkedPath(change.stage), target); else if (fs.existsSync(target)) fs.unlinkSync(target); }
118
+ if (fs.existsSync(path.dirname(target))) { const parent = fs.openSync(path.dirname(target), 'r'); try { fs.fsyncSync(parent); } finally { fs.closeSync(parent); } }
119
+ applied.push(change.path); journal({ phase: 'applied:' + index, applied });
120
+ }
121
+ for (const change of request.changes) if (change.target) {
122
+ const stage = read(change.stage); if (!stage) continue;
123
+ if (!same(fingerprintForWorker(stage), fingerprintForWorker(change.target))) throw error('RESTORE_CONFLICT', 'A staging file was changed before cleanup');
124
+ fs.unlinkSync(checkedPath(change.stage));
125
+ }
126
+ for (const change of request.changes) if (!same(currentHash(change), change.target && { sha256: change.target.sha256, mode: change.target.mode })) throw error('RESTORE_CONFLICT', 'Restored content was changed concurrently');
127
+ const receipt = journal({ phase: 'completed', applied }); process.stdout.write(JSON.stringify({ ok: true, receipt }));
128
+ } catch (error) {
129
+ const failure = { code: error.code ?? 'SNAPSHOT_IO', message: error.message };
130
+ if (request.operation === 'restore') journal({ phase: 'failed', applied, error: failure });
131
+ process.stdout.write(JSON.stringify({ ok: false, error: failure })); process.exitCode = 1;
132
+ }
133
+ }
134
+
135
+ const workerSource = `await (${workspaceWorker.toString()})(JSON.parse((await import('node:fs')).readFileSync(process.argv[3], 'utf8')));\n`;
136
+ /** The same deny-by-default OS boundary as generated scripts, with fixed code. */
137
+ export class ConfinedWorkspaceIO {
138
+ constructor({ workspace, runtimeRoot, protectedRoots }) {
139
+ this.workspace = realpathSync(workspace); mkdirSync(runtimeRoot, { recursive: true, mode: 0o700 }); this.runtimeRoot = realpathSync(runtimeRoot);
140
+ this.protectedRoots = protectedRoots.map(canonicalPath); this.enforcement = 'full';
141
+ }
142
+ prepare(operation, extras = {}) {
143
+ const directory = mkdtempSync(join(this.runtimeRoot, 'snapshot-')), request = { operation, workspace: this.workspace, workspaceIdentity: workspaceIdentity(this.workspace), runDirectory: directory,
144
+ runtimeRoot: this.runtimeRoot, protectedRoots: this.protectedRoots, limits: SNAPSHOT_LIMITS, ...extras };
145
+ request.requestHash = digest(request); mkdirSync(join(directory, 'files'), { mode: 0o700 });
146
+ writeFileSync(join(directory, 'worker.mjs'), workerSource, { flag: 'wx', mode: 0o400 }); writeFileSync(join(directory, 'input.json'), canonicalJson(request), { flag: 'wx', mode: 0o400 });
147
+ return request;
148
+ }
149
+ async execute(request, { sessionPolicy = { mode: 'read-only', workspaceRoot: this.workspace }, signal } = {}) {
150
+ const writes = request.operation === 'restore' ? request.changes.flatMap(c => {
151
+ const parents = c.target ? c.path.split('/').slice(0, -1).map((_p, n, parts) => join(this.workspace, ...parts.slice(0, n + 1))).filter(p => !existsSync(p)) : [];
152
+ return [join(this.workspace, c.path), ...(c.target ? [join(this.workspace, c.stage), ...parents] : [])];
153
+ }) : [];
154
+ let wrapped;
155
+ try {
156
+ if (writes.length && sessionPolicy.mode === 'read-only') fail('RESTORE_POLICY', 'Current session does not permit file restoration');
157
+ const policy = processPolicy({ workspace: this.workspace, runtimeRoot: this.runtimeRoot, protectedRoots: this.protectedRoots, sessionPolicy, authorizedWriteRoots: writes });
158
+ wrapped = confinedArgv({ policy, runDirectory: request.runDirectory, argv: [process.execPath, join(request.runDirectory, 'worker.mjs'), '--input', join(request.runDirectory, 'input.json')] });
159
+ } catch (error) { error.beforeSpawn = true; throw error; }
160
+ const result = await spawnConfined(wrapped, { cwd: this.workspace, runDirectory: request.runDirectory, signal, timeoutMs: 120000, assertCurrent: () => {
161
+ if (digest(workspaceIdentity(this.workspace)) !== digest(request.workspaceIdentity) || readFileSync(join(request.runDirectory, 'worker.mjs'), 'utf8') !== workerSource || readFileSync(join(request.runDirectory, 'input.json'), 'utf8') !== canonicalJson(request)) throw Object.assign(new Error('Fixed worker input or workspace changed'), { code: 'SNAPSHOT_IDENTITY', beforeSpawn: true });
162
+ } });
163
+ let output; try { output = JSON.parse(result.stdout); } catch { output = { ok: false, error: { code: 'SNAPSHOT_WORKER', message: result.stderr || 'No complete fixed-worker receipt' } }; }
164
+ return { ...result, output };
165
+ }
166
+ async scan({ copy = false, signal } = {}) {
167
+ const request = this.prepare('scan', { copy });
168
+ try {
169
+ const result = await this.execute(request, { signal }); if (result.code !== 0 || !result.output.ok || result.truncated) fail(result.output.error?.code ?? 'SNAPSHOT_WORKER', result.output.error?.message ?? 'Incomplete scan');
170
+ const { manifest } = result.output, contents = new Map();
171
+ if (copy) for (const file of manifest.files) { const bytes = readFileSync(join(request.runDirectory, 'files', file.sha256)); if (contentHash(bytes) !== file.sha256) fail('SNAPSHOT_HASH', 'Captured bytes changed'); contents.set(file.path, bytes); }
172
+ return { manifest, contents };
173
+ } finally { rmSync(request.runDirectory, { recursive: true, force: true }); }
174
+ }
175
+ inspect(request) {
176
+ if (!beneath(request.runDirectory, this.runtimeRoot) || request.runDirectory === this.runtimeRoot) fail('SNAPSHOT_PATH', 'Unknown worker directory');
177
+ if (readFileSync(join(request.runDirectory, 'worker.mjs'), 'utf8') !== workerSource || readFileSync(join(request.runDirectory, 'input.json'), 'utf8') !== canonicalJson(request)) fail('SNAPSHOT_IDENTITY', 'Restore worker identity changed');
178
+ if (!existsSync(join(request.runDirectory, 'worker-journal.json'))) return { state: 'unknown', reason: 'no_worker_receipt' };
179
+ const journal = JSON.parse(readFileSync(join(request.runDirectory, 'worker-journal.json'), 'utf8'));
180
+ if (journal.requestHash !== request.requestHash) fail('SNAPSHOT_IDENTITY', 'Worker journal does not match persisted restore intent');
181
+ if (['completed', 'failed'].includes(journal.phase)) return { state: 'finished', journal };
182
+ try { process.kill(journal.pid, 0); return { state: 'unknown', reason: 'worker_may_be_alive', journal }; }
183
+ catch (error) { if (error.code !== 'ESRCH') return { state: 'unknown', reason: 'worker_status_unavailable', journal }; return { state: 'interrupted', journal }; }
184
+ }
185
+ cleanup(request) { if (!beneath(request.runDirectory, this.runtimeRoot) || request.runDirectory === this.runtimeRoot) fail('SNAPSHOT_PATH', 'Not an owned run directory'); rmSync(request.runDirectory, { recursive: true, force: true }); }
186
+ }
187
+
188
+ const program = { kind: 'program', id: 'tree-snapshots' };
189
+ const fingerprint = file => file ? { sha256: file.sha256, mode: file.mode } : null;
190
+ const unfinished = value => ['prepared', 'spawn_intent', 'partial', 'unknown'].includes(value.state);
191
+ export class SnapshotService {
192
+ constructor({ repository, workspace, storageRoot, runtimeRoot, protectedRoots, context, io, allowUnconfinedFixture = false, fault = () => {} }) {
193
+ this.repo = repository; this.identity = workspaceIdentity(workspace); this.workspace = this.identity.path; this.context = context; this.fault = fault;
194
+ mkdirSync(storageRoot, { recursive: true, mode: 0o700 }); this.storageRoot = realpathSync(storageRoot);
195
+ if (beneath(this.storageRoot, this.workspace)) {
196
+ // Parent workspaces are permitted only because both scanning and ordinary
197
+ // tools receive the same explicit protected root, never by hiding its name.
198
+ protectedRoots = [...protectedRoots, this.storageRoot];
199
+ }
200
+ this.protectedRoots = [...new Set([...protectedRoots, this.storageRoot].map(canonicalPath))];
201
+ this.directory = join(this.storageRoot, digest(this.identity)); mkdirSync(this.directory, { recursive: true, mode: 0o700 });
202
+ this.gitDirectory = join(this.directory, 'shadow.git');
203
+ this.io = io ?? new ConfinedWorkspaceIO({ workspace: this.workspace, runtimeRoot, protectedRoots: this.protectedRoots });
204
+ if (this.io.enforcement !== 'full' && !allowUnconfinedFixture) fail('SANDBOX_UNAVAILABLE', 'Snapshot I/O requires the fixed confined worker');
205
+ this.enforcement = this.io.enforcement;
206
+ }
207
+ assertIdentity() { if (digest(workspaceIdentity(this.workspace)) !== digest(this.identity)) fail('WORKSPACE_IDENTITY', 'Workspace realpath/device/inode changed'); }
208
+ user() { const actor = this.context?.().actor; if (actor?.kind !== 'user' || actor.id !== this.repo.meta().userId) fail('USER_RESTORE_REQUIRED', 'Only the owning user can preview or restore files'); return actor; }
209
+ async locked(operation) {
210
+ this.assertIdentity(); let lease;
211
+ try { lease = await ownEvidenceDirectory(join(this.directory, 'workspace-lock')); }
212
+ catch (error) { if (['EAGAIN', 'EWOULDBLOCK'].includes(error.code)) fail('SNAPSHOT_BUSY', 'Another capture or restore owns this workspace'); throw error; }
213
+ try { assertEvidenceOwnership(lease, join(this.directory, 'workspace-lock')); this.assertIdentity(); return await operation(lease); }
214
+ finally { await lease.close(); }
215
+ }
216
+ pending(exceptId = null) { return Object.values(this.repo.meta().restoreIntents ?? {}).filter(row => row.id !== exceptId && digest(row.workspaceIdentity) === digest(this.identity) && unfinished(row)); }
217
+ unresolvedScripts() { return this.repo.list('tree_script_runs', row => digest(row.workspaceIdentity) === digest(this.identity) && (['prepared', 'spawn_intent', 'running', 'unknown'].includes(row.processState ?? row.state) || row.state === 'revoked' && !row.result && row.processState !== 'not_started')); }
218
+ assertAvailable(exceptId = null, { retentionOnly = false } = {}) {
219
+ if (this.pending(exceptId).length) fail('RESTORE_PENDING', 'An unfinished restore must be reconciled before other file work');
220
+ if (!retentionOnly && this.unresolvedScripts().length) fail('SCRIPT_PENDING', 'An unobserved script may still be writing; do not race it with new file operations');
221
+ }
222
+ git(argv, input, binary = false) {
223
+ const result = execFileSync('/usr/bin/git', ['-c', 'core.hooksPath=/dev/null', '-c', 'core.fsmonitor=false', '--git-dir', this.gitDirectory, ...argv], {
224
+ cwd: this.directory, input, encoding: binary ? undefined : 'utf8', maxBuffer: SNAPSHOT_LIMITS.fileBytes + 65536, timeout: 10000,
225
+ env: { PATH: '/usr/bin:/bin', HOME: this.directory, TMPDIR: this.directory, GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null', GIT_AUTHOR_NAME: 'Arcfold Snapshot', GIT_AUTHOR_EMAIL: 'snapshot@localhost', GIT_COMMITTER_NAME: 'Arcfold Snapshot', GIT_COMMITTER_EMAIL: 'snapshot@localhost', LANG: 'C', TZ: 'UTC' }, stdio: ['pipe', 'pipe', 'pipe'],
226
+ }); return binary ? result : result.trim();
227
+ }
228
+ saveGit(manifest, contents, id) {
229
+ if (!existsSync(this.gitDirectory)) this.git(['init', '--bare']);
230
+ const tree = new Map();
231
+ for (const file of manifest.files) {
232
+ const bytes = contents.get(file.path); if (!bytes || contentHash(bytes) !== file.sha256) fail('SNAPSHOT_HASH', 'Stable capture content missing');
233
+ const blob = this.git(['hash-object', '-w', '--stdin'], bytes); file.blob = blob;
234
+ let directory = tree; const parts = file.path.split('/');
235
+ for (const part of parts.slice(0, -1)) { if (!directory.has(part)) directory.set(part, new Map()); directory = directory.get(part); }
236
+ directory.set(parts.at(-1), file);
237
+ }
238
+ const storeTree = directory => {
239
+ const records = [...directory.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([name, entry]) => entry instanceof Map ? `040000 tree ${storeTree(entry)}\t${name}\0` : `${entry.mode & 0o111 ? '100755' : '100644'} blob ${entry.blob}\t${name}\0`);
240
+ return this.git(['mktree', '-z'], Buffer.from(records.join('')));
241
+ };
242
+ const commit = this.git(['commit-tree', storeTree(tree), '-m', 'Arcfold content snapshot ' + id]); this.git(['update-ref', 'refs/snapshots/' + id, commit]); return commit;
243
+ }
244
+ protectedTargets(manifest, targets) {
245
+ for (const path of targets.map(relativeFile)) {
246
+ if (path.split('/').some(name => ['.git', 'node_modules', 'dist', 'build', 'coverage', '.cache', '.ssh', '.aws', '.gnupg', '.npmrc', '.netrc', 'credentials', 'credentials.json', 'id_rsa', 'id_ed25519'].includes(name) || name.startsWith('.env') || /\.(pem|key|p12|pfx)$/i.test(name))) fail('SNAPSHOT_UNCOVERED', 'Excluded paths cannot be script write targets, even before creation');
247
+ if (manifest.exclusions.some(row => path === row.path || path.startsWith(row.path + '/'))) fail('SNAPSHOT_UNCOVERED', 'A required write target is excluded: ' + path);
248
+ const absolute = canonicalPath(join(this.workspace, path));
249
+ if (!beneath(absolute, this.workspace) || this.protectedRoots.some(root => beneath(absolute, root))) fail('SNAPSHOT_UNCOVERED', 'A required write target is outside protected content coverage');
250
+ if (!manifest.files.some(file => file.path === path) && existsSync(absolute) && statSync(absolute).isDirectory()) fail('SNAPSHOT_UNCOVERED', 'Script targets must identify files, not an unbounded directory');
251
+ }
252
+ }
253
+ async stableScan(signal) {
254
+ for (let attempt = 0; attempt < SNAPSHOT_LIMITS.scanAttempts; attempt++) {
255
+ try {
256
+ const first = await this.io.scan({ copy: true, signal }), second = await this.io.scan({ copy: false, signal }); this.assertIdentity();
257
+ if (digest(first.manifest) === digest(second.manifest)) return first;
258
+ } catch (error) { if (!['SNAPSHOT_BUSY', 'ENOENT'].includes(error.code)) throw error; }
259
+ }
260
+ fail('SNAPSHOT_BUSY', 'Workspace did not stabilize in two capture attempts');
261
+ }
262
+ async capture({ requestId, reason, protectedPaths = [], signal }) {
263
+ return this.locked(() => { this.assertAvailable(); return this.captureLocked({ requestId, reason, protectedPaths, signal }); });
264
+ }
265
+ async captureLocked({ requestId, reason, protectedPaths = [], signal }) {
266
+ if (!requestId || !['main_turn', 'script', 'pre_restore'].includes(reason)) fail('SNAPSHOT_REQUEST', 'Stable request identity and a declared snapshot trigger are required');
267
+ const id = digest(['snapshot', this.identity, requestId]), specificationHash = digest({ reason, protectedPaths }), old = this.repo.get('tree_snapshots', id);
268
+ if (old) { if (old.specificationHash !== specificationHash) fail('IDEMPOTENCY_CONFLICT', 'Snapshot request changed'); return old; }
269
+ const { manifest, contents } = await this.stableScan(signal); this.protectedTargets(manifest, protectedPaths);
270
+ const commitId = this.saveGit(manifest, contents, id), manifestHash = digest(manifest);
271
+ this.repo.command('captureTreeSnapshot', { requestId: id, expected: {}, input: { id, reason, manifestHash, commitId } }, program, () => {
272
+ this.repo.save('tree_snapshots', { id, snapshotId: id, workspaceIdentity: this.identity, manifestHash, commitId, reason, specificationHash, manifest,
273
+ coverage: { state: manifest.exclusions.length ? 'partial' : 'full', exclusions: manifest.exclusions, includedFiles: manifest.files.length, totalBytes: manifest.totalBytes, protectedPaths, enforcement: this.enforcement }, restoreState: 'available', capturedAt: this.repo.now() }); return { snapshotId: id };
274
+ }); return this.repo.get('tree_snapshots', id);
275
+ }
276
+ snapshot(id) {
277
+ const snapshot = this.repo.get('tree_snapshots', id);
278
+ if (!snapshot || snapshot.restoreState === 'expired' || digest(snapshot.workspaceIdentity) !== digest(this.identity) || digest(snapshot.manifest) !== snapshot.manifestHash) fail('SNAPSHOT_UNAVAILABLE', 'Snapshot is missing or belongs to another workspace');
279
+ return snapshot;
280
+ }
281
+ async preview({ snapshotId, paths = null, requestId }) {
282
+ const actor = this.user(); if (!requestId) fail('SNAPSHOT_REQUEST', 'A preview request identity is required');
283
+ return this.locked(async () => {
284
+ this.assertAvailable(); const snapshot = this.snapshot(snapshotId), scan = await this.io.scan({ copy: false });
285
+ const selected = [...new Set(paths ?? [...snapshot.manifest.files, ...scan.manifest.files].map(f => f.path))].sort(); selected.forEach(relativeFile);
286
+ this.protectedTargets(scan.manifest, selected); this.protectedTargets(snapshot.manifest, selected);
287
+ const changes = selected.map(path => {
288
+ const target = snapshot.manifest.files.find(f => f.path === path) ?? null, current = scan.manifest.files.find(f => f.path === path) ?? null;
289
+ return { path, current: fingerprint(current), target: target ? { ...fingerprint(target), size: target.size, blob: target.blob } : null, kind: target === null ? 'added' : current === null ? 'deleted' : 'changed' };
290
+ }).filter(change => canonicalJson(change.current) !== canonicalJson(fingerprint(change.target)));
291
+ const expectedCurrentHashes = Object.fromEntries(changes.map(change => [change.path, change.current])), id = digest(['restore-preview', requestId]);
292
+ const value = { id, snapshotId, workspaceIdentity: this.identity, changes, expectedCurrentHashes, exclusions: scan.manifest.exclusions, actor, at: this.repo.now(), state: 'previewed' };
293
+ return this.repo.command('previewTreeRestore', { requestId: id, expected: {}, input: { snapshotId, paths, manifestHash: digest(scan.manifest) } }, actor, () => {
294
+ const meta = this.repo.meta(); this.repo.save('tree_meta', { ...meta, restorePreviews: { ...meta.restorePreviews, [id]: value } }, meta.revision); return value;
295
+ }).result;
296
+ });
297
+ }
298
+ saveRestore(id, updates, actor = program) {
299
+ const current = this.repo.meta().restoreIntents?.[id], value = { ...current, ...updates, id };
300
+ this.repo.command('recordTreeRestore', { requestId: digest([id, current?.revision ?? 0, updates]), expected: {}, input: { id, state: value.state } }, actor, () => {
301
+ const meta = this.repo.meta(), row = meta.restoreIntents?.[id];
302
+ if ((row?.revision ?? 0) !== (current?.revision ?? 0)) fail('RESTORE_CONFLICT', 'Restore intent changed');
303
+ this.repo.save('tree_meta', { ...meta, restoreIntents: { ...meta.restoreIntents, [id]: { ...value, revision: (row?.revision ?? 0) + 1, updatedAt: this.repo.now() } } }, meta.revision); return { id, state: value.state };
304
+ }); return this.repo.meta().restoreIntents[id];
305
+ }
306
+ loadTargets(request) {
307
+ for (const change of request.changes) if (change.target) {
308
+ if (!/^[a-f0-9]{40}$/.test(change.target.blob)) fail('SNAPSHOT_HASH', 'Invalid shadow Git object');
309
+ const bytes = this.git(['cat-file', 'blob', change.target.blob], undefined, true); if (contentHash(bytes) !== change.target.sha256) fail('SNAPSHOT_HASH', 'Shadow Git target changed');
310
+ writeFileSync(join(request.runDirectory, 'files', change.target.sha256), bytes, { mode: 0o400 });
311
+ }
312
+ }
313
+ async executeIntent(id, signal) {
314
+ const intent = this.saveRestore(id, { state: 'spawn_intent' }), request = intent.request; this.fault('spawn_intent', intent);
315
+ let result;
316
+ try { result = await this.io.execute(request, { sessionPolicy: this.context().sessionPolicy, signal }); }
317
+ catch (error) { return this.saveRestore(id, { state: error.beforeSpawn ? 'failed' : 'unknown', error: { code: error.code ?? 'RESTORE_IO', message: error.message } }); }
318
+ this.fault('worker_returned', { ...intent, result });
319
+ const observed = this.io.inspect(request);
320
+ if (result.code === 0 && !result.truncated && observed.journal?.phase === 'completed') {
321
+ const final = this.saveRestore(id, { state: 'completed', receipt: observed.journal, workerExited: true }); this.fault('committed', final); this.io.cleanup(request); return final;
322
+ }
323
+ return this.saveRestore(id, { state: 'partial', workerExited: true, observation: observed, error: result.output?.error ?? { code: 'RESTORE_INTERRUPTED', message: 'Restore did not produce a complete success receipt' } });
324
+ }
325
+ async apply({ previewId, expectedCurrentHashes, requestId, signal }) {
326
+ const actor = this.user(); if (!requestId || !expectedCurrentHashes) fail('SNAPSHOT_REQUEST', 'Restore needs an exact preview, expected file hashes and request identity');
327
+ const id = digest(['restore', requestId]), inputHash = digest({ previewId, expectedCurrentHashes, actor });
328
+ return this.locked(async () => {
329
+ const prior = this.repo.meta().restoreIntents?.[id]; if (prior) { if (prior.inputHash !== inputHash) fail('IDEMPOTENCY_CONFLICT', 'Restore request changed'); return prior; }
330
+ this.assertAvailable(); const preview = this.repo.meta().restorePreviews?.[previewId];
331
+ if (!preview || preview.actor.id !== actor.id || digest(preview.workspaceIdentity) !== digest(this.identity) || canonicalJson(preview.expectedCurrentHashes) !== canonicalJson(expectedCurrentHashes)) fail('RESTORE_CONFLICT', 'Preview identity or expected hashes changed');
332
+ const snapshot = this.snapshot(preview.snapshotId), scan = await this.io.scan({ copy: false, signal }); this.protectedTargets(scan.manifest, preview.changes.map(c => c.path));
333
+ for (const change of preview.changes) if (canonicalJson(fingerprint(scan.manifest.files.find(f => f.path === change.path))) !== canonicalJson(change.current)) fail('RESTORE_CONFLICT', 'A file changed after preview: ' + change.path);
334
+ const before = await this.captureLocked({ requestId: 'pre-restore:' + id, reason: 'pre_restore', protectedPaths: preview.changes.map(c => c.path), signal });
335
+ const changes = preview.changes.map((change, index) => ({ ...change, stage: change.path + '.arcfold-restore-' + id.slice(0, 16) + '-' + index }));
336
+ const request = this.io.prepare('restore', { changes });
337
+ this.loadTargets(request);
338
+ const intent = this.saveRestore(id, { state: 'prepared', inputHash, previewId, snapshotId: snapshot.id, beforeSnapshotId: before.id, workspaceIdentity: this.identity, actor, request, changes });
339
+ this.fault('prepared', intent); return this.executeIntent(id, signal);
340
+ });
341
+ }
342
+ async reconcile(restoreId, { signal } = {}) {
343
+ const actor = this.context?.().actor;
344
+ if (!actor || !['program', 'user'].includes(actor.kind) || actor.kind === 'user' && actor.id !== this.repo.meta().userId) fail('USER_RESTORE_REQUIRED', 'Only the trusted recovery owner can continue an existing user restore');
345
+ return this.locked(async () => {
346
+ const intent = this.repo.meta().restoreIntents?.[restoreId]; if (!intent || intent.actor.id !== this.repo.meta().userId || digest(intent.workspaceIdentity) !== digest(this.identity)) fail('RESTORE_UNAVAILABLE', 'No owned restore intent');
347
+ if (!unfinished(intent)) return intent;
348
+ this.assertAvailable(restoreId);
349
+ const observed = this.io.inspect(intent.request);
350
+ if (intent.state !== 'prepared' && observed.state === 'unknown' && !intent.workerExited) return this.saveRestore(restoreId, { state: 'unknown', observation: observed });
351
+ const scan = await this.io.scan({ copy: false, signal }), original = intent.changes; this.protectedTargets(scan.manifest, original.map(c => c.path));
352
+ const changes = [];
353
+ for (const change of original) {
354
+ const current = fingerprint(scan.manifest.files.find(f => f.path === change.path)), target = fingerprint(change.target);
355
+ if (canonicalJson(current) !== canonicalJson(change.current) && canonicalJson(current) !== canonicalJson(target)) return this.saveRestore(restoreId, { state: 'conflict', error: { code: 'RESTORE_CONFLICT', message: 'Recovery found a later edit; a new preview is required: ' + change.path } });
356
+ changes.push({ ...change, current, skipWrite: canonicalJson(current) === canonicalJson(target) });
357
+ }
358
+ if (observed.journal?.phase === 'completed' && changes.every(c => c.skipWrite)) { const final = this.saveRestore(restoreId, { state: 'completed', receipt: observed.journal }); this.io.cleanup(intent.request); return final; }
359
+ const request = this.io.prepare('restore', { changes }); this.loadTargets(request);
360
+ this.saveRestore(restoreId, { state: 'prepared', request, workerExited: false, priorRequests: [...(intent.priorRequests ?? []), intent.request], recoveryObservation: observed });
361
+ return this.executeIntent(restoreId, signal);
362
+ });
363
+ }
364
+ async prune(at = this.repo.now()) {
365
+ return this.locked(async () => {
366
+ this.assertAvailable(null, { retentionOnly: true }); const cutoff = Date.parse(at) - SNAPSHOT_LIMITS.retentionDays * 86400000;
367
+ const referenced = new Set(Object.values(this.repo.meta().restoreIntents ?? {}).filter(unfinished).flatMap(row => [row.snapshotId, row.beforeSnapshotId]));
368
+ for (const run of this.repo.list('tree_script_runs', run => !['succeeded', 'failed', 'revoked'].includes(run.state))) referenced.add(run.snapshotId);
369
+ for (const run of this.unresolvedScripts()) referenced.add(run.snapshotId);
370
+ for (const preview of Object.values(this.repo.meta().restorePreviews ?? {})) if (preview.state === 'previewed' && !Object.values(this.repo.meta().restoreIntents ?? {}).some(row => row.previewId === preview.id && !unfinished(row))) referenced.add(preview.snapshotId);
371
+ const expired = this.repo.list('tree_snapshots', row => digest(row.workspaceIdentity) === digest(this.identity) && row.restoreState !== 'expired' && Date.parse(row.capturedAt) < cutoff && !referenced.has(row.id));
372
+ if (!expired.length) return [];
373
+ this.repo.command('expireTreeSnapshots', { requestId: digest(['expire-snapshots', at, expired.map(r => r.id)]), expected: Object.fromEntries(expired.map(row => ['tree_snapshots:' + row.id, row.revision])), input: { snapshotIds: expired.map(row => row.id) } }, program, () => {
374
+ for (const row of expired) this.repo.save('tree_snapshots', { ...row, restoreState: 'expired' }, row.revision); return { snapshotIds: expired.map(row => row.id) };
375
+ });
376
+ for (const row of expired) this.git(['update-ref', '-d', 'refs/snapshots/' + row.id]); this.git(['gc', '--prune=now']); return expired.map(row => row.id);
377
+ });
378
+ }
379
+ }
@@ -0,0 +1,75 @@
1
+ import { readFileSync, realpathSync, mkdtempSync, writeFileSync, openSync, closeSync, unlinkSync, rmdirSync, constants } from 'node:fs';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { dirname, isAbsolute, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { DeepSeekAdapter, registerDeepSeekProvider } from '@deepseek-ai/dsh-llm-deepseek';
6
+ import { resolveAdapterOptions } from '@deepseek-ai/dsh-llm-deepseek-api-key';
7
+ import { createLaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment';
8
+ import { canonicalJson, digest, fail } from './contracts.mjs';
9
+
10
+ const directory = dirname(fileURLToPath(import.meta.url));
11
+ const projection = options => ({ provider: options.provider, model: options.model, reasoningEffort: options.reasoningEffort,
12
+ maxTokens: options.maxTokens, messages: options.messages, tools: options.tools ?? [], toolHistory: options.toolHistory ?? null });
13
+
14
+ /** Runs the actual patched serializer with a capture-only sender, then the
15
+ * official offline converter. The final authority guard consumes only an exact
16
+ * cached measurement for those same native bytes; no network fallback exists. */
17
+ export class TreeTokenCounter {
18
+ constructor({ python, moduleDirectory, adapter = { DeepSeekAdapter, registerDeepSeekProvider } }) {
19
+ if (process.platform !== 'darwin' || process.arch !== 'arm64') fail('TOKENIZER_PLATFORM', 'This frozen dependency has only been validated on macOS arm64');
20
+ if (!isAbsolute(python ?? '') || !isAbsolute(moduleDirectory ?? '')) fail('TOKENIZER_CONFIG', 'Explicit interpreter and private dependency directory required');
21
+ if (adapter.registerDeepSeekProvider?.transportSeam !== 'deepseek-messages-create-transport-v1' || typeof adapter.DeepSeekAdapter !== 'function') fail('TOKENIZER_ADAPTER', 'Exact capture transport patch required');
22
+ this.DeepSeekAdapter = adapter.DeepSeekAdapter;
23
+ this.python = realpathSync(python); this.moduleDirectory = moduleDirectory; this.cache = new Map();
24
+ const manifest = JSON.parse(readFileSync(join(directory, 'tokenizer-resources/manifest.json'), 'utf8'));
25
+ this.identity = digest({ manifest, interpreter: digest(readFileSync(this.python).toString('base64')), protocol: 'official-messages-v4-v41-text-only' });
26
+ this.invoke('initialize');
27
+ this.connection = resolveAdapterOptions({ baseURL: 'https://api.deepseek.com/anthropic', apiKeyEnv: 'OFFLINE_NO_CREDENTIAL',
28
+ maxTokens: 8192, reasoningEffort: 'off', models: Object.keys(manifest.modelMappings).map(id => ({ id, contextWindow: 1000000, maxTokens: 131072, inputModalities: ['text'] })),
29
+ retryPolicy: { mode: 'normal', maxRetries: 0 } }, createLaunchEnvironmentSnapshot([]));
30
+ }
31
+ invoke(mode, body) {
32
+ // macOS spawnSync's large stdin pipe intermittently stalled on identical
33
+ // frozen requests. An unlinked private regular file avoids that feed pipe;
34
+ // Python still reads the exact canonical bytes from fd0. No longer deadline
35
+ // or estimate fallback: failed measurements remain known-unsent failures.
36
+ let inputDirectory, inputPath, inputFd, result;
37
+ try {
38
+ if (body !== undefined) {
39
+ inputDirectory = mkdtempSync(join(this.moduleDirectory, '.counter-input-'));
40
+ inputPath = join(inputDirectory, 'request.json');
41
+ writeFileSync(inputPath, canonicalJson(body), { mode: 0o600, flag: 'wx' });
42
+ inputFd = openSync(inputPath, constants.O_RDONLY | constants.O_NOFOLLOW);
43
+ unlinkSync(inputPath); inputPath = undefined;
44
+ }
45
+ result = spawnSync(this.python, ['-I', '-B', join(directory, 'token-counter.py'), mode, this.moduleDirectory], {
46
+ stdio: [inputFd ?? 'ignore', 'pipe', 'pipe'], encoding: 'utf8', timeout: 30000, maxBuffer: 1024 * 1024,
47
+ env: { PATH: dirname(this.python), TOKENIZERS_PARALLELISM: 'false' },
48
+ });
49
+ } finally {
50
+ if (inputFd !== undefined) closeSync(inputFd);
51
+ if (inputPath) { try { unlinkSync(inputPath); } catch (error) { if (error.code !== 'ENOENT') throw error; } }
52
+ if (inputDirectory) rmdirSync(inputDirectory);
53
+ }
54
+ if (result.error || result.status !== 0) fail('TOKENIZER_UNAVAILABLE', result.error?.message ?? result.stderr.slice(-3000));
55
+ return JSON.parse(result.stdout);
56
+ }
57
+ async prepare(options) {
58
+ const key = digest(projection(options)), old = this.cache.get(key); if (old) return old;
59
+ if (options.messages.some(m => m.content.some(b => !['text', 'reasoning', 'tool-call', 'tool-addition', 'tool-removal'].includes(b.type)))) fail('TOKENIZER_CONTENT', 'Text-only counter refuses images and unknown content');
60
+ let wire; const captured = new Error('OFFLINE_SERIALIZATION_CAPTURED');
61
+ const adapter = new this.DeepSeekAdapter({ options: () => this.connection, resolveFiles: () => ({}), resolveAuth: async () => ({ headers: {} }), resolveUserId: () => 'offline-counter',
62
+ prepareExtensions: async () => ({ fields: {}, accept: async () => {} }),
63
+ createTransport: () => ({ send: async (_url, init) => { wire = JSON.parse(init.body); throw captured; } }) });
64
+ try { for await (const _ of adapter.request(options, this.connection, options.signal ?? new AbortController().signal, () => {})) fail('TOKENIZER_ADAPTER', 'Capture must not produce model chunks'); }
65
+ catch (error) { if (error !== captured) throw error; }
66
+ if (!wire) fail('TOKENIZER_ADAPTER', 'Actual adapter did not produce a wire request');
67
+ const result = this.invoke('count', wire);
68
+ if (!Number.isSafeInteger(result.tokens) || result.tokens < 0) fail('TOKENIZER_RESULT', 'Exact counter returned an invalid count');
69
+ const reading = { kind: 'exact', tokens: result.tokens, inputHash: key, wireHash: digest(wire), tokenizerIdentity: this.identity };
70
+ if (this.cache.size >= 256) this.cache.delete(this.cache.keys().next().value);
71
+ this.cache.set(key, reading); return reading;
72
+ }
73
+ count(options) { const reading = this.cache.get(digest(projection(options))); if (!reading) fail('EXACT_TOKEN_COUNT_REQUIRED', 'Current native input has no exact offline measurement'); return reading.tokens; }
74
+ request(options) { return projection(options); }
75
+ }
@@ -0,0 +1,37 @@
1
+ """Offline only: pinned official Messages conversion and model-specific encoding."""
2
+ import hashlib
3
+ import json
4
+ import pathlib
5
+ import sys
6
+ import zipfile
7
+
8
+ resources = pathlib.Path(__file__).with_name("tokenizer-resources")
9
+ manifest = json.loads((resources / "manifest.json").read_text())
10
+ for asset in manifest["assets"]:
11
+ if hashlib.sha256((resources / asset["name"]).read_bytes()).hexdigest() != asset["sha256"]:
12
+ raise ValueError("Frozen tokenizer resource changed: " + asset["name"])
13
+
14
+ modules = pathlib.Path(sys.argv[2]).resolve()
15
+ wheel = next(resources.glob("*.whl"))
16
+ if sys.argv[1] == "initialize":
17
+ modules.mkdir(parents=True, exist_ok=True, mode=0o700)
18
+ with zipfile.ZipFile(wheel) as archive:
19
+ for entry in archive.infolist():
20
+ path = pathlib.PurePosixPath(entry.filename)
21
+ if path.is_absolute() or ".." in path.parts or entry.external_attr >> 16 & 0o170000 == 0o120000:
22
+ raise ValueError("Unsafe frozen wheel member")
23
+ archive.extractall(modules)
24
+ print(json.dumps({"initialized": True}))
25
+ else:
26
+ # -I plus an explicit private dependency directory excludes user/site imports.
27
+ sys.path.insert(0, str(modules))
28
+ from deepseek_recipe import MessagesRequest, ConversionOptions, ModelVersion, DeepseekV4Encoding, DeepseekV41Encoding, Tokenizer
29
+ body = json.load(sys.stdin)
30
+ mapping = manifest["modelMappings"].get(body["model"])
31
+ if mapping is None:
32
+ raise ValueError("No frozen tokenizer mapping for this model")
33
+ versions = {"DeepseekV4Pro": ModelVersion.DeepseekV4Pro, "DeepseekV41Flash": ModelVersion.DeepseekV41Flash}
34
+ encodings = {"DeepseekV4Encoding": DeepseekV4Encoding, "DeepseekV41Encoding": DeepseekV41Encoding}
35
+ converted = MessagesRequest(body).convert(ConversionOptions().with_model_alias(body["model"], versions[mapping["modelVersion"]]))
36
+ encoding = encodings[mapping["encoding"]]().with_tokenizer(Tokenizer.from_file(str(resources / mapping["tokenizer"])))
37
+ print(json.dumps({"tokens": len(encoding.encode(converted.conversation))}))
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # Exact offline token measurement
2
+
3
+ The v3 mode requires an explicit absolute Python interpreter in
4
+ `goalTreeV3.tokenCounter.python`. The frozen wheel supports CPython >=3.10,
5
+ macOS 11 or later, arm64. This implementation was actually validated with
6
+ CPython 3.12.14 on this arm64 Mac. Linux support is deferred; unsupported
7
+ platforms fail closed. The old mode does not start this counter.
8
+
9
+ The package contains the official deepseek-recipe 0.2.0 wheel, V4.1 tokenizer,
10
+ MIT license and tokenizer notice. Exact URLs, original byte counts and SHA-256
11
+ hashes are in manifest.json. Nothing is fetched or installed at startup. The
12
+ Python interpreter and standard library are an explicit host prerequisite,
13
+ not bundled or silently installed. The engineering validation resource pins
14
+ the existing interpreter directory separately.
15
+
16
+ Startup verifies the resource hashes and extracts the audited wheel into the
17
+ private runtime dependency directory. The Python process uses isolated mode,
18
+ no user/site imports, a clean environment and a 30-second deadline. A missing
19
+ or incompatible interpreter, changed asset or failed count stops the request.
20
+ There is no character-ratio or network fallback.
21
+
22
+ The native patched DeepSeek adapter first serializes the complete request
23
+ (messages, system instructions, tools and protocol) with a capture-only
24
+ transport. The official Messages converter and V4.1 encoder count those same
25
+ bytes. Admission requires the exact cached measurement; experiment budget
26
+ reservations independently keep the conservative 1M input bound.
27
+
28
+ One local 30-second encoding timeout was observed in the B7 E01 test; the
29
+ unchanged rerun and broader regression passed. The failed measurement sent no
30
+ model request and was retained. This is not evidence that timeouts cannot
31
+ recur.
@@ -0,0 +1,61 @@
1
+ {
2
+ "recipeVersion": "0.2.0",
3
+ "sourceCommit": "935817c66f5d6e64838f419692545f06787e485f",
4
+ "license": "MIT with bundled notices",
5
+ "assets": [
6
+ {
7
+ "name": "deepseek_recipe-0.2.0-cp310-abi3-macosx_11_0_arm64.whl",
8
+ "url": "https://files.pythonhosted.org/packages/67/04/c9c4dda8c4c9a004a356888c68543458237b07b72431edc7e33e0cab740b/deepseek_recipe-0.2.0-cp310-abi3-macosx_11_0_arm64.whl",
9
+ "sha256": "624bb4d0fb3ed541b9e34d2ef96ff66057026ded7d6708b0b9088cc6d6723999",
10
+ "bytes": 9549388
11
+ },
12
+ {
13
+ "name": "tokenizer.json",
14
+ "url": "https://raw.githubusercontent.com/deepseek-ai/deepseek-recipe/935817c66f5d6e64838f419692545f06787e485f/static/tokenizers/v41/tokenizer.json",
15
+ "sha256": "81f64d1248a68ce3663e07ab3ee48b851e5df0e32d27cb98e4c9a268151e8d99",
16
+ "bytes": 6367247
17
+ },
18
+ {
19
+ "name": "LICENSE",
20
+ "url": "https://raw.githubusercontent.com/deepseek-ai/deepseek-recipe/935817c66f5d6e64838f419692545f06787e485f/LICENSE",
21
+ "sha256": "ebb4f09972aee8608be255debaf78451a68e95c290f55c240dec2ecfa16ea6be",
22
+ "bytes": 1065
23
+ },
24
+ {
25
+ "name": "tokenizer-NOTICE.md",
26
+ "url": "https://raw.githubusercontent.com/deepseek-ai/deepseek-recipe/935817c66f5d6e64838f419692545f06787e485f/static/tokenizers/v41/README.md",
27
+ "sha256": "711070ffbe350a07e30e81a40aed8ce0e37f444142aa42e25cab704fb06be269",
28
+ "bytes": 1158
29
+ },
30
+ {
31
+ "name": "native.pyi",
32
+ "url": "https://raw.githubusercontent.com/deepseek-ai/deepseek-recipe/935817c66f5d6e64838f419692545f06787e485f/deepseek-recipe-python/python/deepseek_recipe/_native.pyi",
33
+ "sha256": "e24cb343288b736a0cfadbada954fa0272f4393ae27222adee8ed080aa2b0e92",
34
+ "bytes": 40062
35
+ },
36
+ {
37
+ "name": "tokenizer-v4.json",
38
+ "url": "https://raw.githubusercontent.com/deepseek-ai/deepseek-recipe/935817c66f5d6e64838f419692545f06787e485f/static/tokenizers/v4/tokenizer.json",
39
+ "sha256": "97d2f31b020d18b5aee5c9b3d5b4efb10ea210f3fe3f7dffe3f1cd90542d6b19",
40
+ "bytes": 6367146
41
+ },
42
+ {
43
+ "name": "tokenizer-v4-NOTICE.md",
44
+ "url": "https://raw.githubusercontent.com/deepseek-ai/deepseek-recipe/935817c66f5d6e64838f419692545f06787e485f/static/tokenizers/v4/README.md",
45
+ "sha256": "b89168930f9e6f374ef704ec3adfcd19c9e1a6789704e213d9ba3ceae5e966ca",
46
+ "bytes": 1185
47
+ }
48
+ ],
49
+ "modelMappings": {
50
+ "deepseek-flash": {
51
+ "modelVersion": "DeepseekV41Flash",
52
+ "encoding": "DeepseekV41Encoding",
53
+ "tokenizer": "tokenizer.json"
54
+ },
55
+ "deepseek-v4-pro": {
56
+ "modelVersion": "DeepseekV4Pro",
57
+ "encoding": "DeepseekV4Encoding",
58
+ "tokenizer": "tokenizer-v4.json"
59
+ }
60
+ }
61
+ }