@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.
- package/LICENSE +21 -0
- package/README.md +8 -2
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/cordis.patch.yml +138 -0
- package/package.json +70 -4
- package/patches/deepseek-0.2.2-rc.1-transport.json +65 -0
- package/patches/deepseek-0.2.2-rc.1-transport.patch +61 -0
- package/presets/arcfold.patch.yml +110 -0
- package/runtime/README.md +19 -0
- package/runtime/bootstrap.json +15 -0
- package/runtime/desktop-python.tar.gz +0 -0
- package/runtime/host/package.json +11 -0
- package/runtime/host/pnpm-workspace.yaml +9 -0
- package/runtime/initial-policy.json +94 -0
- package/runtime/profile/pnpm-workspace.yaml +7 -0
- package/scripts/exact-package-patch.mjs +106 -0
- package/scripts/patch-deepseek-transport.mjs +14 -0
- package/src/agent.mjs +56 -0
- package/src/browse-scope.mjs +14 -0
- package/src/budget.mjs +369 -0
- package/src/business.mjs +334 -0
- package/src/context-material.mjs +119 -0
- package/src/deepseek-budget-plugin.mjs +58 -0
- package/src/deepseek-provider.mjs +57 -0
- package/src/deepseek-receipt.mjs +97 -0
- package/src/deepseek-transport.mjs +97 -0
- package/src/desktop/adapter.mjs +16 -0
- package/src/desktop/bootstrap.mjs +84 -0
- package/src/desktop/configuration.mjs +117 -0
- package/src/desktop/provider.mjs +46 -0
- package/src/desktop/python-runtime.mjs +105 -0
- package/src/desktop/setup-service.mjs +81 -0
- package/src/desktop/vendor/LICENSE.deepseek +21 -0
- package/src/desktop/vendor/NOTICE.md +5 -0
- package/src/desktop/vendor/deepseek-0.2.2-rc.1.mjs +2919 -0
- package/src/evidence-ownership.mjs +51 -0
- package/src/evidence.mjs +35 -0
- package/src/goal-context.mjs +52 -0
- package/src/goal-execution-tool.mjs +74 -0
- package/src/goal-execution.mjs +333 -0
- package/src/goal-tool.mjs +56 -0
- package/src/goal-work-control.mjs +63 -0
- package/src/goal-work.mjs +257 -0
- package/src/goals.mjs +578 -0
- package/src/importer.mjs +298 -0
- package/src/index.mjs +53 -0
- package/src/learning-contracts.mjs +220 -0
- package/src/learning-defaults.mjs +98 -0
- package/src/learning-goals.mjs +165 -0
- package/src/learning-plugin.mjs +28 -0
- package/src/learning-runner.mjs +275 -0
- package/src/learning.mjs +534 -0
- package/src/legacy-export.mjs +332 -0
- package/src/legacy-import-source.mjs +356 -0
- package/src/metered-provider-host.mjs +101 -0
- package/src/metered-transport.mjs +435 -0
- package/src/method-context.mjs +57 -0
- package/src/method-lifecycle.mjs +34 -0
- package/src/method-tool.mjs +61 -0
- package/src/methods.mjs +425 -0
- package/src/model-selection.mjs +41 -0
- package/src/native-context-call.mjs +88 -0
- package/src/native-continuation.mjs +217 -0
- package/src/native-request-authority.mjs +90 -0
- package/src/openai-models.mjs +4 -0
- package/src/openai-receipt.mjs +98 -0
- package/src/openai-transport.mjs +167 -0
- package/src/openai-usage.mjs +194 -0
- package/src/opportunities.mjs +295 -0
- package/src/preparation-artifacts.mjs +213 -0
- package/src/preparation-plugin.mjs +290 -0
- package/src/runtime-command.mjs +264 -0
- package/src/runtime-config.mjs +417 -0
- package/src/runtime-guard.mjs +284 -0
- package/src/runtime-host.mjs +212 -0
- package/src/runtime-init.mjs +278 -0
- package/src/runtime-installation.mjs +518 -0
- package/src/runtime-lifecycle.mjs +227 -0
- package/src/runtime-model-settings.mjs +111 -0
- package/src/runtime-readiness.mjs +285 -0
- package/src/runtime-workspace.mjs +18 -0
- package/src/session-bridge.mjs +365 -0
- package/src/store.mjs +394 -0
- package/src/tree/LICENSE +21 -0
- package/src/tree/attribution.mjs +120 -0
- package/src/tree/clusters.mjs +61 -0
- package/src/tree/consolidation.mjs +103 -0
- package/src/tree/context.mjs +211 -0
- package/src/tree/contracts.mjs +142 -0
- package/src/tree/experience.mjs +140 -0
- package/src/tree/experiment-budget.mjs +84 -0
- package/src/tree/hooks.mjs +254 -0
- package/src/tree/host-context.mjs +107 -0
- package/src/tree/host-process.mjs +288 -0
- package/src/tree/host.mjs +37 -0
- package/src/tree/improvement.mjs +106 -0
- package/src/tree/items.mjs +294 -0
- package/src/tree/jobs.mjs +184 -0
- package/src/tree/learning.mjs +202 -0
- package/src/tree/metrics.mjs +203 -0
- package/src/tree/model-owner.mjs +254 -0
- package/src/tree/observations.mjs +73 -0
- package/src/tree/prompts.mjs +500 -0
- package/src/tree/reports.mjs +67 -0
- package/src/tree/repository.mjs +236 -0
- package/src/tree/runtime.mjs +745 -0
- package/src/tree/script-writer.mjs +149 -0
- package/src/tree/scripts.mjs +208 -0
- package/src/tree/signals.mjs +118 -0
- package/src/tree/snapshots.mjs +379 -0
- package/src/tree/token-counter.mjs +75 -0
- package/src/tree/token-counter.py +37 -0
- package/src/tree/tokenizer-resources/LICENSE +21 -0
- package/src/tree/tokenizer-resources/README.md +31 -0
- package/src/tree/tokenizer-resources/deepseek_recipe-0.2.0-cp310-abi3-macosx_11_0_arm64.whl +0 -0
- package/src/tree/tokenizer-resources/manifest.json +61 -0
- package/src/tree/tokenizer-resources/native.pyi +1042 -0
- package/src/tree/tokenizer-resources/tokenizer-NOTICE.md +26 -0
- package/src/tree/tokenizer-resources/tokenizer-v4-NOTICE.md +27 -0
- package/src/tree/tokenizer-resources/tokenizer-v4.json +267359 -0
- package/src/tree/tokenizer-resources/tokenizer.json +267359 -0
- package/src/tree/tools.mjs +99 -0
- package/src/tree/tree.mjs +202 -0
- package/src/tree/ui.mjs +285 -0
- package/src/tree/verification.mjs +143 -0
- package/src/understandings.mjs +309 -0
- package/src/web-budget-recovery.mjs +181 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { OPENAI_MODELS, OPENAI_REASONING_EFFORTS } from './openai-models.mjs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
|
+
import { canonicalJson } from './store.mjs';
|
|
9
|
+
import { preflightWebBudget, webBudgetPath } from './web-budget-recovery.mjs';
|
|
10
|
+
|
|
11
|
+
const CONFIG_SCHEMA = 'arcfold-local-v1';
|
|
12
|
+
const SCHEMAS = [CONFIG_SCHEMA, 'arcfold-local-v2'];
|
|
13
|
+
const ROUTE_FIELDS = ['provider', 'model', 'reasoningEffort', 'maxTokens'];
|
|
14
|
+
const TRANSPORT_FIELDS = ['apiKeyRef', 'ledgerPath', 'artifactDirectory', 'scope', 'ceilingCny',
|
|
15
|
+
'authorizationRef', 'webAuthorization', 'endpoint', 'modelPolicies', 'timeoutMs', 'allowLocalTestEndpoint'];
|
|
16
|
+
export const runtimeHash = value => createHash('sha256').update(typeof value === 'string' ? value : canonicalJson(value)).digest('hex');
|
|
17
|
+
const requireValue = (condition, message) => { if (!condition) throw new Error(message); };
|
|
18
|
+
|
|
19
|
+
function object(value, fields, name) {
|
|
20
|
+
requireValue(value && typeof value === 'object' && !Array.isArray(value), `${name} must be an object`);
|
|
21
|
+
requireValue(Object.keys(value).every(key => fields.includes(key)), `${name} has unsupported fields; store credential references only`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Resolve existing ancestors too: two spellings of one data directory share one owner. */
|
|
25
|
+
export function canonicalRuntimePath(path, label = 'path') {
|
|
26
|
+
requireValue(typeof path === 'string' && isAbsolute(path) && !path.includes('\0'), `${label} must be an absolute path`);
|
|
27
|
+
let ancestor = resolve(path);
|
|
28
|
+
const tail = [];
|
|
29
|
+
while (!existsSync(ancestor)) {
|
|
30
|
+
const parent = dirname(ancestor);
|
|
31
|
+
requireValue(parent !== ancestor, `Cannot resolve ${label}`);
|
|
32
|
+
tail.unshift(relative(parent, ancestor)); ancestor = parent;
|
|
33
|
+
}
|
|
34
|
+
return join(realpathSync(ancestor), ...tail);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readConfig(path) {
|
|
38
|
+
const configPath = canonicalRuntimePath(path, 'configPath');
|
|
39
|
+
requireValue(lstatSync(configPath).isFile(), 'The runtime configuration must be a regular file');
|
|
40
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
41
|
+
requireValue(SCHEMAS.includes(config?.schema), 'Unsupported Arcfold runtime configuration');
|
|
42
|
+
return { configPath, config };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Stop/status deliberately do not depend on a usable model or code installation. */
|
|
46
|
+
export function loadRuntimeIdentity(path) {
|
|
47
|
+
const { configPath, config } = readConfig(path);
|
|
48
|
+
const dataRoot = canonicalRuntimePath(config.dataRoot, 'dataRoot');
|
|
49
|
+
return { configPath, dataRoot, runtimeDirectory: join(dataRoot, 'runtime') };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function route(value, name) {
|
|
53
|
+
if (value === null) return null;
|
|
54
|
+
object(value, ROUTE_FIELDS, name);
|
|
55
|
+
for (const key of ['provider', 'model', 'reasoningEffort'])
|
|
56
|
+
requireValue(typeof value[key] === 'string' && value[key].length > 0 && value[key].length <= 256, `${name}.${key} is required`);
|
|
57
|
+
requireValue(Number.isSafeInteger(value.maxTokens) && value.maxTokens > 0, `${name}.maxTokens must be a positive integer`);
|
|
58
|
+
return structuredClone(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** One read projection. Legacy files and their original fee authorization are never rewritten. */
|
|
62
|
+
export function runtimeTransport(config) {
|
|
63
|
+
if (!config.transport) return { artifactDirectory: null, routes: {} };
|
|
64
|
+
if (config.schema === 'arcfold-local-v2') return structuredClone(config.transport);
|
|
65
|
+
const { artifactDirectory, ...route } = config.transport;
|
|
66
|
+
return { artifactDirectory, routes: { 'deepseek-official': { ...route, kind: 'deepseek', network: { mode: 'direct' } } } };
|
|
67
|
+
}
|
|
68
|
+
function text(value, name, max = 4096) {
|
|
69
|
+
requireValue(typeof value === 'string' && value.trim().length > 0 && value.length <= max && !value.includes('\0'), `${name} is required`);
|
|
70
|
+
}
|
|
71
|
+
function webAuthority(value, original) {
|
|
72
|
+
object(value, ['authorizationRef', 'authorizedAt', 'authorizationEvidence'], 'webAuthorization');
|
|
73
|
+
for (const key of ['authorizationRef', 'authorizedAt', 'authorizationEvidence']) text(value[key], 'webAuthorization.' + key, 8000);
|
|
74
|
+
requireValue(value.authorizationRef !== original && /^\d{4}-\d{2}-\d{2}T/.test(value.authorizedAt) && Number.isFinite(Date.parse(value.authorizedAt)),
|
|
75
|
+
'Web use requires its separate explicit authorization and timestamp');
|
|
76
|
+
}
|
|
77
|
+
function network(value) {
|
|
78
|
+
object(value, ['mode', 'url'], 'network');
|
|
79
|
+
if (value.mode === 'direct') return requireValue(Object.keys(value).length === 1, 'A direct route cannot include a proxy');
|
|
80
|
+
requireValue(value.mode === 'proxy' && Object.keys(value).length === 2 && typeof value.url === 'string', 'An explicit fixed direct or proxy network is required');
|
|
81
|
+
const proxy = new URL(value.url);
|
|
82
|
+
requireValue(['http:', 'https:'].includes(proxy.protocol) && !proxy.username && !proxy.password && proxy.pathname === '/' && !proxy.search && !proxy.hash,
|
|
83
|
+
'Use a fixed HTTP CONNECT proxy without credentials, query or path');
|
|
84
|
+
}
|
|
85
|
+
function validateV2Transport(t) {
|
|
86
|
+
object(t, ['artifactDirectory', 'routes'], 'transport'); canonicalRuntimePath(t.artifactDirectory, 'artifactDirectory');
|
|
87
|
+
object(t.routes, ['deepseek-official', 'openai'], 'routes');
|
|
88
|
+
requireValue(Object.keys(t.routes).length > 0, 'Configure at least one explicit route or use transport:null');
|
|
89
|
+
for (const [provider, value] of Object.entries(t.routes)) {
|
|
90
|
+
const openai = provider === 'openai';
|
|
91
|
+
object(value, ['kind', 'apiKeyRef', 'ledgerPath', 'scope', 'webAuthorization', 'endpoint', 'modelPolicies', 'timeoutMs',
|
|
92
|
+
'allowLocalTestEndpoint', 'network', ...(openai ? [] : ['ceilingCny', 'authorizationRef', 'mode'])], provider);
|
|
93
|
+
requireValue(value.kind === (openai ? 'openai' : 'deepseek'), 'Provider and transport kind must agree');
|
|
94
|
+
requireValue(typeof value.apiKeyRef === 'string' && /^[A-Za-z_][A-Za-z0-9_]*$/.test(value.apiKeyRef), 'Use an explicit native credential reference');
|
|
95
|
+
canonicalRuntimePath(value.ledgerPath, 'ledgerPath'); network(value.network);
|
|
96
|
+
requireValue(value.timeoutMs === undefined || Number.isSafeInteger(value.timeoutMs) && value.timeoutMs > 0, 'timeoutMs must be a positive integer');
|
|
97
|
+
requireValue(value.allowLocalTestEndpoint === undefined || typeof value.allowLocalTestEndpoint === 'boolean', 'Synthetic endpoint permission must be explicit');
|
|
98
|
+
const endpoint = new URL(value.endpoint), path = openai ? '/v1/responses' : '/anthropic/v1/messages';
|
|
99
|
+
requireValue(!endpoint.username && !endpoint.password && !endpoint.search && !endpoint.hash && endpoint.pathname === path &&
|
|
100
|
+
(value.allowLocalTestEndpoint === true ? endpoint.protocol === 'http:' && endpoint.hostname === '127.0.0.1' && endpoint.port
|
|
101
|
+
: endpoint.href === (openai ? 'https://api.openai.com/v1/responses' : 'https://api.deepseek.com/anthropic/v1/messages')),
|
|
102
|
+
'Use the fixed official endpoint or an explicitly synthetic loopback endpoint');
|
|
103
|
+
if (openai) { requireValue(value.scope === 'web-openai', 'OpenAI requires independent web-openai accounting'); webAuthority(value.webAuthorization); }
|
|
104
|
+
else {
|
|
105
|
+
requireValue(['limited', 'web-unlimited'].includes(value.scope), 'DeepSeek requires an explicit existing fee scope');
|
|
106
|
+
if (value.mode === 'web-only') {
|
|
107
|
+
requireValue(value.scope === 'web-unlimited' && !Object.hasOwn(value, 'ceilingCny') &&
|
|
108
|
+
!Object.hasOwn(value, 'authorizationRef'), 'Web-only mode cannot carry the original finite allowance');
|
|
109
|
+
webAuthority(value.webAuthorization);
|
|
110
|
+
webBudgetPath(t.artifactDirectory, value.ledgerPath);
|
|
111
|
+
} else {
|
|
112
|
+
requireValue(value.mode === undefined, 'Unsupported DeepSeek ledger mode');
|
|
113
|
+
requireValue(Number.isFinite(value.ceilingCny) && value.ceilingCny > 0 && Number.isSafeInteger(Math.round(value.ceilingCny * 1e6)), 'The original ledger ceiling is required');
|
|
114
|
+
text(value.authorizationRef, 'authorizationRef');
|
|
115
|
+
if (value.scope === 'web-unlimited') webAuthority(value.webAuthorization, value.authorizationRef);
|
|
116
|
+
else requireValue(value.webAuthorization === undefined, 'Limited scope cannot carry a different web authorization');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
requireValue(value.modelPolicies && typeof value.modelPolicies === 'object' && !Array.isArray(value.modelPolicies) && Object.keys(value.modelPolicies).length > 0, 'Explicit model policies are required');
|
|
120
|
+
if (openai) requireValue(Object.keys(value.modelPolicies).every(model => OPENAI_MODELS.includes(model)), 'Only explicit supported OpenAI models are allowed');
|
|
121
|
+
for (const [model, policy] of Object.entries(value.modelPolicies)) {
|
|
122
|
+
text(model, 'model', 256);
|
|
123
|
+
object(policy, ['contextWindow', 'maxOutputTokens', 'requestMaxTokens', 'reasoningEfforts', ...(openai ? [] : ['prices', 'priceVersion'])], 'model policy');
|
|
124
|
+
const minimum = openai ? 16 : 1;
|
|
125
|
+
requireValue(Number.isSafeInteger(policy.contextWindow) && policy.contextWindow > 0 && Number.isSafeInteger(policy.maxOutputTokens) && policy.maxOutputTokens >= minimum &&
|
|
126
|
+
(policy.requestMaxTokens === undefined || Number.isSafeInteger(policy.requestMaxTokens) && policy.requestMaxTokens >= minimum && policy.requestMaxTokens <= policy.maxOutputTokens), 'Invalid explicit model token bounds');
|
|
127
|
+
requireValue(Array.isArray(policy.reasoningEfforts) && policy.reasoningEfforts.length && new Set(policy.reasoningEfforts).size === policy.reasoningEfforts.length &&
|
|
128
|
+
policy.reasoningEfforts.every(e => (openai ? OPENAI_REASONING_EFFORTS : ['off', 'low', 'high', 'max']).includes(e)), 'Invalid model reasoning policy');
|
|
129
|
+
if (!openai) {
|
|
130
|
+
text(policy.priceVersion, 'priceVersion'); object(policy.prices, ['input', 'output', 'cachedInput'], 'prices');
|
|
131
|
+
requireValue(['input', 'output', 'cachedInput'].every(k => Number.isFinite(policy.prices[k]) && policy.prices[k] >= 0), 'Explicit original DeepSeek prices are required');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export function validateRuntimeConfig(config) {
|
|
137
|
+
object(config, ['schema', 'installationFile', 'dataRoot', 'workspace', 'listen', 'main', 'learn', 'transport'], 'configuration');
|
|
138
|
+
requireValue(SCHEMAS.includes(config.schema), 'Unsupported Arcfold runtime configuration');
|
|
139
|
+
for (const key of ['installationFile', 'dataRoot', 'workspace']) canonicalRuntimePath(config[key], key);
|
|
140
|
+
requireValue(disjoint(canonicalRuntimePath(config.workspace), dirname(canonicalRuntimePath(config.installationFile))), 'Keep workspace separate from installed code');
|
|
141
|
+
object(config.listen, ['host', 'port'], 'listen');
|
|
142
|
+
requireValue(config.listen.host === '127.0.0.1', 'This local product currently binds only to 127.0.0.1');
|
|
143
|
+
requireValue(Number.isSafeInteger(config.listen.port) && config.listen.port >= 0 && config.listen.port <= 65535, 'Invalid local port');
|
|
144
|
+
route(config.main, 'main'); route(config.learn, 'learn');
|
|
145
|
+
if (config.transport !== null && config.schema === 'arcfold-local-v2') validateV2Transport(config.transport);
|
|
146
|
+
else if (config.transport !== null) {
|
|
147
|
+
const t = config.transport; object(t, TRANSPORT_FIELDS, 'transport');
|
|
148
|
+
requireValue(typeof t.apiKeyRef === 'string' && /^[A-Za-z_][A-Za-z0-9_]*$/.test(t.apiKeyRef), 'transport.apiKeyRef must name a native credential reference');
|
|
149
|
+
canonicalRuntimePath(t.ledgerPath, 'ledgerPath'); canonicalRuntimePath(t.artifactDirectory, 'artifactDirectory');
|
|
150
|
+
requireValue(['limited', 'web-unlimited'].includes(t.scope), 'An explicit fee scope is required');
|
|
151
|
+
requireValue(Number.isFinite(t.ceilingCny) && t.ceilingCny >= 0 && typeof t.authorizationRef === 'string' && t.authorizationRef.length > 0,
|
|
152
|
+
'The original ledger authorization is required; initialization never creates or raises it');
|
|
153
|
+
requireValue(t.modelPolicies && typeof t.modelPolicies === 'object' && !Array.isArray(t.modelPolicies) && Object.keys(t.modelPolicies).length > 0,
|
|
154
|
+
'Explicit model accounting policies are required');
|
|
155
|
+
const endpoint = new URL(t.endpoint);
|
|
156
|
+
requireValue(!endpoint.username && !endpoint.password && !endpoint.search && !endpoint.hash && endpoint.pathname === '/anthropic/v1/messages', 'Invalid Messages endpoint');
|
|
157
|
+
const synthetic = t.allowLocalTestEndpoint === true;
|
|
158
|
+
requireValue(synthetic ? endpoint.protocol === 'http:' && endpoint.hostname === '127.0.0.1' && endpoint.port
|
|
159
|
+
: endpoint.protocol === 'https:' && endpoint.hostname === 'api.deepseek.com' && !endpoint.port,
|
|
160
|
+
'Use the official DeepSeek endpoint or an explicitly synthetic loopback endpoint');
|
|
161
|
+
if (t.scope === 'web-unlimited') {
|
|
162
|
+
object(t.webAuthorization, ['authorizationRef', 'authorizedAt', 'authorizationEvidence'], 'webAuthorization');
|
|
163
|
+
requireValue(t.webAuthorization.authorizationRef !== t.authorizationRef &&
|
|
164
|
+
typeof t.webAuthorization.authorizationEvidence === 'string' && t.webAuthorization.authorizationEvidence.length > 0 &&
|
|
165
|
+
Number.isFinite(Date.parse(t.webAuthorization.authorizedAt)), 'Unlimited web use requires its separate explicit authorization');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return structuredClone(config);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** One legal shared adapter default; this never changes the saved user choices. */
|
|
172
|
+
export function runtimeProviderDefaults(config, provider = 'deepseek-official') {
|
|
173
|
+
const transport = runtimeTransport(config).routes[provider];
|
|
174
|
+
if (!transport) return { available: false, reason: 'transport-not-configured' };
|
|
175
|
+
const policies = Object.values(transport.modelPolicies ?? {});
|
|
176
|
+
if (policies.some(policy => !policy || typeof policy !== 'object' || Array.isArray(policy)))
|
|
177
|
+
return { available: false, reason: 'model-catalog-controls-invalid' };
|
|
178
|
+
const limits = policies.map(policy => policy.requestMaxTokens ?? policy.maxOutputTokens);
|
|
179
|
+
if (!policies.length || limits.some(limit => !Number.isSafeInteger(limit) || limit < 1) ||
|
|
180
|
+
policies.some(policy => !Array.isArray(policy.reasoningEfforts)))
|
|
181
|
+
return { available: false, reason: 'model-catalog-controls-invalid' };
|
|
182
|
+
const efforts = (provider === 'openai' ? OPENAI_REASONING_EFFORTS : ['off', 'low', 'high', 'max']).filter(effort => policies.every(policy => policy.reasoningEfforts.includes(effort)));
|
|
183
|
+
if (!efforts.length) return { available: false, reason: 'model-catalog-has-no-common-controls' };
|
|
184
|
+
const maxTokens = Math.min(...limits);
|
|
185
|
+
const choices = [config.main, config.learn].filter(Boolean);
|
|
186
|
+
// A valid output preference remains usable after a native model correction;
|
|
187
|
+
// choosing connection defaults does not authorize its provider/model route.
|
|
188
|
+
const output = choices.find(selection => Number.isSafeInteger(selection.maxTokens) && selection.maxTokens >= (provider === 'openai' ? 16 : 1) && selection.maxTokens <= maxTokens);
|
|
189
|
+
const reasoning = choices.find(selection => efforts.includes(selection.reasoningEffort));
|
|
190
|
+
return { available: true, maxTokens: output?.maxTokens ?? maxTokens, reasoningEffort: reasoning?.reasoningEffort ?? efforts[0] };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Availability is not a credential probe and does not issue a model request. */
|
|
194
|
+
export function runtimeRouteAvailability(config) {
|
|
195
|
+
const routes = runtimeTransport(config).routes;
|
|
196
|
+
const check = (selection, role) => {
|
|
197
|
+
if (!selection || !selection.provider || !selection.model) return { available: false, reason: `${role}-not-configured` };
|
|
198
|
+
const policy = routes[selection.provider]?.modelPolicies?.[selection.model];
|
|
199
|
+
const defaults = runtimeProviderDefaults(config, selection.provider);
|
|
200
|
+
if (!policy)
|
|
201
|
+
return { available: false, reason: 'selected-route-not-supported' };
|
|
202
|
+
if (!defaults.available) return { available: false, reason: defaults.reason };
|
|
203
|
+
if (!policy.reasoningEfforts?.includes(selection.reasoningEffort) || !Number.isSafeInteger(selection.maxTokens) || selection.maxTokens < (selection.provider === 'openai' ? 16 : 1) ||
|
|
204
|
+
selection.maxTokens > (policy.requestMaxTokens ?? policy.maxOutputTokens))
|
|
205
|
+
return { available: false, reason: 'selection-outside-model-policy' };
|
|
206
|
+
return { available: true, provider: selection.provider, model: selection.model,
|
|
207
|
+
reasoningEffort: selection.reasoningEffort, maxTokens: selection.maxTokens };
|
|
208
|
+
};
|
|
209
|
+
return { main: check(config.main, 'main'), learn: check(config.learn, 'learn') };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function disjoint(a, b) {
|
|
213
|
+
return a !== b && !a.startsWith(b + sep) && !b.startsWith(a + sep);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function checkExistingBusiness(filename, expected) {
|
|
217
|
+
if (!existsSync(filename)) return;
|
|
218
|
+
const db = new DatabaseSync(filename, { readOnly: true });
|
|
219
|
+
try {
|
|
220
|
+
requireValue(db.prepare("SELECT value FROM business_meta WHERE key='schema_version'").get()?.value === String(expected),
|
|
221
|
+
'The selected code version is incompatible with the existing business database');
|
|
222
|
+
} finally { db.close(); }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function checkExistingLedger(t) {
|
|
226
|
+
requireValue(existsSync(t.ledgerPath), 'An existing authorized ledger is required; no new research balance will be created');
|
|
227
|
+
const db = new DatabaseSync(t.ledgerPath, { readOnly: true });
|
|
228
|
+
try {
|
|
229
|
+
for (const table of ['records', 'store_meta', ...(t.kind === 'openai' ? [] : ['budget_settings', 'budget_requests'])])
|
|
230
|
+
requireValue(db.prepare("SELECT 1 FROM sqlite_schema WHERE type='table' AND name=?").get(table), 'The existing ledger schema is incomplete');
|
|
231
|
+
if (t.kind !== 'openai') {
|
|
232
|
+
const settings = db.prepare('SELECT * FROM budget_settings WHERE singleton=1').get();
|
|
233
|
+
requireValue(settings?.schema_version === 1 && settings.authorization_ref === t.authorizationRef &&
|
|
234
|
+
settings.ceiling_micro === Math.round(t.ceilingCny * 1e6), 'The configured authorization does not match the original ledger');
|
|
235
|
+
}
|
|
236
|
+
requireValue(db.prepare("SELECT value FROM store_meta WHERE key='schema_version'").get()?.value === '2', 'Unsupported existing ledger schema');
|
|
237
|
+
if (t.webAuthorization) {
|
|
238
|
+
const openai = t.kind === 'openai', auth = t.webAuthorization;
|
|
239
|
+
const id = (openai ? 'openai-web-' : 'budget-web-') + runtimeHash(auth.authorizationRef);
|
|
240
|
+
const row = db.prepare('SELECT body FROM records WHERE kind=? AND id=?').get(openai ? 'openai_authorizations' : 'budget_authorizations', id);
|
|
241
|
+
const specification = { scope: t.scope, ...(openai ? { provider: 'openai', accounting: 'tokens_only' } : {}),
|
|
242
|
+
...auth, authorizedAt: new Date(auth.authorizedAt).toISOString() };
|
|
243
|
+
requireValue(!row || canonicalJson(JSON.parse(row.body).specification) === canonicalJson(specification), 'The existing web authorization conflicts with the configured evidence');
|
|
244
|
+
}
|
|
245
|
+
} finally { db.close(); }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function loadRuntimePlan(path) {
|
|
249
|
+
const { configPath, config: raw } = readConfig(path);
|
|
250
|
+
const config = validateRuntimeConfig(raw), identity = loadRuntimeIdentity(configPath);
|
|
251
|
+
const { readInstallation } = await import('./runtime-installation.mjs');
|
|
252
|
+
const installation = await readInstallation(canonicalRuntimePath(config.installationFile, 'installationFile'));
|
|
253
|
+
const workspace = canonicalRuntimePath(config.workspace, 'workspace');
|
|
254
|
+
requireValue(lstatSync(workspace).isDirectory(), 'The selected workspace must already exist');
|
|
255
|
+
requireValue(disjoint(identity.dataRoot, workspace) && disjoint(identity.dataRoot, installation.installationDir),
|
|
256
|
+
'Keep user data separate from workspace and installed code');
|
|
257
|
+
requireValue(disjoint(workspace, installation.installationDir), 'Keep workspace separate from installed code');
|
|
258
|
+
requireValue(installation.compatibility?.businessSchema === 1 && installation.compatibility?.nativeFormat === 4,
|
|
259
|
+
'This launcher does not support the selected version data formats');
|
|
260
|
+
const businessFilename = join(identity.dataRoot, 'business.sqlite');
|
|
261
|
+
checkExistingBusiness(businessFilename, installation.compatibility.businessSchema);
|
|
262
|
+
const transport = runtimeTransport(config);
|
|
263
|
+
for (const route of Object.values(transport.routes)) {
|
|
264
|
+
if (route.mode === 'web-only') preflightWebBudget({ artifactDirectory: transport.artifactDirectory, ledgerPath: route.ledgerPath });
|
|
265
|
+
else checkExistingLedger(route);
|
|
266
|
+
}
|
|
267
|
+
return { ...identity, configurationHash: runtimeHash(config), nativeHome: join(identity.dataRoot, 'native'),
|
|
268
|
+
businessFilename, workspace, installation, config, routeAvailability: runtimeRouteAvailability(config) };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** First-use config only; never resets an existing file, ledger or profile. */
|
|
272
|
+
export function initializeRuntimeConfig({ configPath, installationFile, dataRoot, workspace, port = 4381 }) {
|
|
273
|
+
const config = validateRuntimeConfig({ schema: CONFIG_SCHEMA,
|
|
274
|
+
installationFile: canonicalRuntimePath(installationFile, 'installationFile'), dataRoot: canonicalRuntimePath(dataRoot, 'dataRoot'),
|
|
275
|
+
workspace: canonicalRuntimePath(workspace, 'workspace'), listen: { host: '127.0.0.1', port }, main: null, learn: null, transport: null });
|
|
276
|
+
const path = canonicalRuntimePath(configPath, 'configPath');
|
|
277
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
278
|
+
writeFileSync(path, JSON.stringify(config, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
|
|
279
|
+
return { configPath: path, mode: 'configuration-only', modelsConfigured: false };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const CLOSED_ROUTES = ['llm-deepseek', 'llm-deepseek-account', 'llm-pi-ai', 'deepseek-llm-api-extensions',
|
|
283
|
+
'session-log-deepseek', 'plugin-package-inventory-deepseek', 'llm-retry', 'session-title-llm',
|
|
284
|
+
'schedule', 'ui-schedule', 'subagent-spawn-in-process', 'subagent-fork-in-process',
|
|
285
|
+
'web-search-deepseek', 'web-deepseek-api', 'preset-standard', 'preset-minimal', 'preset-ptc', 'preset-cordis'];
|
|
286
|
+
|
|
287
|
+
export function runtimeDeploymentPatches(plan) {
|
|
288
|
+
const { config } = plan, t = config.transport, shared = config.schema === 'arcfold-local-v2';
|
|
289
|
+
const normalized = runtimeTransport(config);
|
|
290
|
+
const defaults = Object.fromEntries(Object.keys(normalized.routes).map(provider => [provider, runtimeProviderDefaults(config, provider)]));
|
|
291
|
+
const enabledRoutes = Object.fromEntries(Object.entries(normalized.routes).filter(([provider]) => defaults[provider].available));
|
|
292
|
+
const available = runtimeRouteAvailability(config);
|
|
293
|
+
// The provider catalog exists independently of the initial main choice. A native
|
|
294
|
+
// conversation may select another model within the same explicit accounting policy.
|
|
295
|
+
const providerDefaults = runtimeProviderDefaults(config), providerEnabled = providerDefaults.available;
|
|
296
|
+
const initialMain = config.main ? { provider: config.main.provider, model: config.main.model,
|
|
297
|
+
reasoningEffort: config.main.reasoningEffort } : { provider: '', model: '' };
|
|
298
|
+
const patches = [
|
|
299
|
+
...CLOSED_ROUTES.map(id => ({ id, disabled: true })),
|
|
300
|
+
{ id: 'goal-round-driver', disabled: false },
|
|
301
|
+
{ id: 'hmr', disabled: true },
|
|
302
|
+
{ id: 'client-hmr', disabled: true },
|
|
303
|
+
{ id: 'ui-settings-models', disabled: true },
|
|
304
|
+
{ id: 'arcfold-ui', inject: ['arcfoldRuntimeModels'] },
|
|
305
|
+
{ id: 'llm', inject: ['arcfoldRuntimeGuard'] },
|
|
306
|
+
// The bundle expression runs only after the root service is available. Its
|
|
307
|
+
// config remains below user/home patches, so native saves can replace it.
|
|
308
|
+
{ id: 'agent-default-model', disabled: false, inject: ['arcfoldRuntimeGuard'] },
|
|
309
|
+
{ id: 'arcfold-business', disabled: false, config: { filename: plan.businessFilename } },
|
|
310
|
+
{ id: 'arcfold-metered-budget', disabled: shared || !providerEnabled, config: !shared && t ? {
|
|
311
|
+
...t, provider: 'deepseek-official', apiKeyRef: undefined,
|
|
312
|
+
} : {} },
|
|
313
|
+
{ id: 'arcfold-metered-provider', disabled: !providerEnabled, config: providerEnabled ? {
|
|
314
|
+
apiKeyRef: normalized.routes['deepseek-official'].apiKeyRef, maxTokens: providerDefaults.maxTokens,
|
|
315
|
+
reasoningEffort: providerDefaults.reasoningEffort,
|
|
316
|
+
} : {} },
|
|
317
|
+
{ id: 'arcfold-shared-metered-host', disabled: !shared || !Object.keys(enabledRoutes).length,
|
|
318
|
+
config: shared ? { artifactDirectory: normalized.artifactDirectory, routes: enabledRoutes } : {} },
|
|
319
|
+
{ id: 'arcfold-openai-provider', disabled: !shared || !enabledRoutes.openai,
|
|
320
|
+
config: shared && enabledRoutes.openai ? { apiKeyRef: enabledRoutes.openai.apiKeyRef,
|
|
321
|
+
maxTokens: defaults.openai.maxTokens, reasoningEffort: defaults.openai.reasoningEffort } : {} },
|
|
322
|
+
{ id: 'arcfold-preparation', config: { preset: 'arcfold-preparation', maxTokens: 8192, cwd: plan.workspace } },
|
|
323
|
+
{ id: 'arcfold-learning', disabled: !available.learn.available, inject: ['arcfoldRuntimeModels'], config: available.learn.available ? {
|
|
324
|
+
preset: 'arcfold-learning', ...config.learn, cwd: plan.workspace,
|
|
325
|
+
} : {} },
|
|
326
|
+
{ id: 'agent-preset-registry', config: { default: 'arcfold-main' } },
|
|
327
|
+
// The native browser creates its own first-use workspace independently of
|
|
328
|
+
// process.cwd(). Gate that API until the explicit deployment path is registered.
|
|
329
|
+
{ id: 'workspace-controller', inject: ['arcfoldRuntimeWorkspace'] },
|
|
330
|
+
{ id: 'working-directory', config: { defaultDirectory: plan.workspace } },
|
|
331
|
+
{ id: 'sandbox-policy', config: { mode: 'workspace-write', workspaceRoot: plan.workspace } },
|
|
332
|
+
{ id: 'approval', config: { policy: 'ask' } },
|
|
333
|
+
{ id: 'webserver', config: { ...config.listen, compression: 'gzip', compressionLevel: 1, compressionThresholdBytes: 1024 } },
|
|
334
|
+
{ id: 'web-runtime', inject: ['webStartup', 'arcfoldRuntimeWorkspace'], config: { openBrowser: false, printUrl: false, surfaceContext: true } },
|
|
335
|
+
{ insert: [{ id: 'arcfold-runtime-model-settings',
|
|
336
|
+
name: pathToFileURL(join(dirname(fileURLToPath(import.meta.url)), 'runtime-model-settings.mjs')).href, config: { runtime: config } },
|
|
337
|
+
{ id: 'arcfold-runtime-workspace', name: pathToFileURL(join(dirname(fileURLToPath(import.meta.url)), 'runtime-workspace.mjs')).href,
|
|
338
|
+
config: { workspace: plan.workspace } },
|
|
339
|
+
{ id: 'arcfold-runtime-guard', name: pathToFileURL(join(dirname(fileURLToPath(import.meta.url)), 'runtime-guard.mjs')).href,
|
|
340
|
+
config: { modelSelectionRequired: true, initialMain, mainMaxTokens: config.main?.maxTokens ?? null,
|
|
341
|
+
routeAvailability: available, learn: config.learn, modelPolicies: normalized.routes['deepseek-official']?.modelPolicies ?? {},
|
|
342
|
+
...(shared ? { routes: Object.fromEntries(Object.entries(enabledRoutes).map(([provider, route]) => [provider, {
|
|
343
|
+
provider, endpoint: route.endpoint, modelPolicies: route.modelPolicies, network: route.network }])) } : {}) } }] },
|
|
344
|
+
];
|
|
345
|
+
// JSON patches contain no secrets and no undefined pseudo-fields.
|
|
346
|
+
return JSON.parse(JSON.stringify(patches));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Called only by the lock-owning child, before official runProfile. */
|
|
350
|
+
export async function prepareRuntimeBoot(plan, runDirectory) {
|
|
351
|
+
const current = await loadRuntimePlan(plan.configPath);
|
|
352
|
+
requireValue(current.configurationHash === plan.configurationHash && current.installation.sourceCommit === plan.installation.sourceCommit,
|
|
353
|
+
'Configuration or selected version changed during startup');
|
|
354
|
+
requireValue(realpathSync(process.cwd()) === plan.workspace && canonicalRuntimePath(process.env.DSH_HOME, 'DSH_HOME') === plan.nativeHome,
|
|
355
|
+
'The child must select its explicit workspace and durable native home before boot');
|
|
356
|
+
mkdirSync(plan.nativeHome, { recursive: true, mode: 0o700 });
|
|
357
|
+
const require = createRequire(plan.installation.installAnchor);
|
|
358
|
+
const boot = await import(pathToFileURL(require.resolve('@deepseek-ai/dsh-app-boot')).href);
|
|
359
|
+
const profile = boot.loadProfileDirectory('arcfold', plan.installation.profileDir, plan.installation.installAnchor, { userLayer: false });
|
|
360
|
+
requireValue(profile.skippedBundles.length === 0, 'A required installed bundle was skipped');
|
|
361
|
+
requireValue(canonicalJson(profile.layers.map(layer => layer.packageName)) === canonicalJson([
|
|
362
|
+
'@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app', '@arcfold/dsh']), 'Unexpected installed bundle composition');
|
|
363
|
+
const originalRows = boot.composeEntries([profile.layers.flatMap(layer => layer.patches)]);
|
|
364
|
+
const nativePatchPath = join(plan.nativeHome, 'arcfold.patch.yml');
|
|
365
|
+
const homePatches = boot.loadOptionalPatches('arcfold', join(plan.nativeHome, 'cordis.patch.yml')) ?? [];
|
|
366
|
+
const userPatches = boot.loadOptionalPatches('arcfold', nativePatchPath) ?? [];
|
|
367
|
+
const patches = runtimeDeploymentPatches(plan);
|
|
368
|
+
profile.patchPath = nativePatchPath;
|
|
369
|
+
profile.patches = userPatches;
|
|
370
|
+
const rows = boot.composeEntries([profile.layers.flatMap(layer => layer.patches), userPatches, homePatches, patches]);
|
|
371
|
+
const allowed = new Map(originalRows.map(row => [row.id, row.name]));
|
|
372
|
+
for (const row of patches.flatMap(patch => patch.insert ?? [])) allowed.set(row.id, row.name);
|
|
373
|
+
requireValue(rows.length === allowed.size && new Set(rows.map(row => row.id)).size === rows.length,
|
|
374
|
+
'The installed composition contains added, missing or duplicate entries');
|
|
375
|
+
for (const row of rows) {
|
|
376
|
+
requireValue(allowed.get(row.id) === row.name, `Unsupported component replacement: ${row.id}`);
|
|
377
|
+
requireValue(!row.group && !row.isolate && !row.intercept, `Unsupported runtime scope change: ${row.id}`);
|
|
378
|
+
}
|
|
379
|
+
const permission = rows.find(row => row.id === 'permission');
|
|
380
|
+
requireValue(canonicalJson(permission.config.presets['workspace-write']) === canonicalJson({ sandbox: 'workspace-write', approval: 'ask' }),
|
|
381
|
+
'The native workspace-write and ask permission boundary must be preserved');
|
|
382
|
+
const patchPath = join(runDirectory, 'deployment.patch.json');
|
|
383
|
+
writeFileSync(patchPath, JSON.stringify(patches, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
|
|
384
|
+
return { environment: boot.loadLayeredEnv('arcfold', plan.workspace), profile: plan.installation.profileName,
|
|
385
|
+
resolvedProfile: { profile, installAnchor: plan.installation.installAnchor }, patchFiles: [patchPath],
|
|
386
|
+
args: ['--no-open', '--port', String(plan.config.listen.port)],
|
|
387
|
+
packageManager: { ...plan.installation.packageManager, env: {} } };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Pure first-use configuration. Authorization and publication belong to installed runtime-init. */
|
|
391
|
+
export function createInitialRuntimeConfig({ installationFile, dataRoot, workspace, port = 4381,
|
|
392
|
+
main, learn, network: selectedNetwork = { mode: 'direct' }, credentialRefs, webAuthorizations, policy }) {
|
|
393
|
+
requireValue(policy?.schema === 'arcfold-initial-policy-v1' && policy.requestMaxTokens === 128000,
|
|
394
|
+
'Unsupported initial model policy');
|
|
395
|
+
const root = canonicalRuntimePath(dataRoot, 'dataRoot'), artifactDirectory = join(root, 'requests');
|
|
396
|
+
object(credentialRefs, ['deepseek-official', 'openai'], 'credentialRefs');
|
|
397
|
+
requireValue(Object.keys(credentialRefs).length === 2, 'Both native credential references are required');
|
|
398
|
+
const selection = (value, label) => {
|
|
399
|
+
object(value, ['provider','model','reasoningEffort'], label);
|
|
400
|
+
requireValue(Object.keys(value).length === 3, 'Supply all three model choices');
|
|
401
|
+
return { ...value, maxTokens: policy.requestMaxTokens };
|
|
402
|
+
};
|
|
403
|
+
const routes = Object.fromEntries(['deepseek-official', 'openai'].map(provider => [provider, {
|
|
404
|
+
...structuredClone(policy.routes[provider]), apiKeyRef: credentialRefs[provider],
|
|
405
|
+
ledgerPath: join(artifactDirectory, provider === 'openai' ? 'openai-web.sqlite' : 'deepseek-web.sqlite'),
|
|
406
|
+
...(provider === 'openai' ? { scope: 'web-openai' } : { mode: 'web-only', scope: 'web-unlimited' }),
|
|
407
|
+
webAuthorization: structuredClone(webAuthorizations[provider]), network: structuredClone(selectedNetwork),
|
|
408
|
+
}]));
|
|
409
|
+
const config = validateRuntimeConfig({ schema: 'arcfold-local-v2',
|
|
410
|
+
installationFile: canonicalRuntimePath(installationFile, 'installationFile'), dataRoot: root,
|
|
411
|
+
workspace: canonicalRuntimePath(workspace, 'workspace'), listen: { host: '127.0.0.1', port },
|
|
412
|
+
main: selection(main ?? policy.initialMain, 'main'), learn: selection(learn ?? main ?? policy.initialMain, 'learn'),
|
|
413
|
+
transport: { artifactDirectory, routes } });
|
|
414
|
+
const availability = runtimeRouteAvailability(config);
|
|
415
|
+
requireValue(availability.main.available && availability.learn.available, 'Initial selection is outside the deployed model policy');
|
|
416
|
+
return config;
|
|
417
|
+
}
|