@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,356 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { parser } from 'stream-json';
|
|
4
|
+
import { filter } from 'stream-json/filters/filter.js';
|
|
5
|
+
import { stringer } from 'stream-json/stringer.js';
|
|
6
|
+
import chain from 'stream-chain';
|
|
7
|
+
import { many } from 'stream-chain/core';
|
|
8
|
+
import { DomainError, canonicalJson } from './store.mjs';
|
|
9
|
+
import { METHOD_SCHEMA, UNDERSTANDING_SCHEMA } from './learning-contracts.mjs';
|
|
10
|
+
|
|
11
|
+
export const IMPORT_LIMITS = Object.freeze({ maxFileBytes: 805306368, maxChunkBytes: 65536,
|
|
12
|
+
maxKeyCharacters: 1024, maxStringCharacters: 1000000, maxDepth: 64,
|
|
13
|
+
maxProjectionCharacters: 33554432, maxTokens: 3000000, maxRows: 10000, maxScopes: 200 });
|
|
14
|
+
const fail = (code, message) => { throw new DomainError(code, message); };
|
|
15
|
+
export const importDigest = value => createHash('sha256').update(canonicalJson(value)).digest('hex');
|
|
16
|
+
export const textDigest = value => createHash('sha256').update(value).digest('hex');
|
|
17
|
+
export const importMethodField = (row,key) => typeof row[key]==='string'&&row[key].trim()&&row[key].length<=METHOD_SCHEMA.properties[key].maxLength?row[key]:null;
|
|
18
|
+
const object = value => value && typeof value === 'object' && !Array.isArray(value);
|
|
19
|
+
const positive = value => Number.isSafeInteger(value) && value > 0;
|
|
20
|
+
const nonnegative = value => Number.isSafeInteger(value) && value >= 0;
|
|
21
|
+
const values = value => Array.isArray(value) ? value : object(value) ? Object.values(value) : [];
|
|
22
|
+
const validId = value => typeof value === 'string' && value.length > 0 && value.length <= 512 && !value.includes('\0');
|
|
23
|
+
const unavailable = row => !row || row.deleted || row.withdrawn || row.useProhibited || ['withdrawn','superseded','rejected','dismissed'].includes(row.status);
|
|
24
|
+
const runFields = new Set('id request answer status error answerValid clientId sessionId conversationId taskId version sourceMessageRef createdAt updatedAt completedAt startedAt invalidated deleted withdrawn useProhibited'.split(' '));
|
|
25
|
+
const eventFields = new Set('id type actor occurredAt observedAt version revision seq runId taskId sourceEventId deleted withdrawn useProhibited status payload'.split(' '));
|
|
26
|
+
const eventPayload = new Set('text request answer content detail role sourceNotice sourceProjection'.split(' '));
|
|
27
|
+
const stateFields = new Set('id recordType kind verification informationVersion updatedSeq applicability derivedFrom changeReason text scope basis confidence status evidence expiresAt sourceRunId maintainedBy revision createdAt updatedAt versions deleted useProhibited withdrawn'.split(' '));
|
|
28
|
+
const taskFields = new Set('id title goal status progress steps runIds informationVersion updatedSeq goalEvidence hypothesisRefs historyInitialization versions revision createdAt updatedAt evidence maintainedBy constraints decisions questions'.split(' '));
|
|
29
|
+
const envelopeFields = new Set('type format exportId exporter source schemaVersion mutationSequence collections excluded complete counts record kind digest'.split(' '));
|
|
30
|
+
|
|
31
|
+
// This adapter sees tokens, never JSON syntax. The official parser owns escape,
|
|
32
|
+
// UTF-8 and grammar handling. Keys are limited BEFORE being concatenated.
|
|
33
|
+
function keyGuard(limits) {
|
|
34
|
+
let key = null, depth = 0, numberLength = 0, heldKeyCharacters = 0;
|
|
35
|
+
const containers = [];
|
|
36
|
+
return token => {
|
|
37
|
+
if (token.name === 'startObject' || token.name === 'startArray') {
|
|
38
|
+
if (++depth > limits.maxDepth) fail('import_depth_limit', '导出嵌套过深。');
|
|
39
|
+
containers.push(token.name === 'startObject' ? {keys:new Set(),characters:0} : null);
|
|
40
|
+
} else if (token.name === 'endObject' || token.name === 'endArray') { depth--; heldKeyCharacters-=containers.pop()?.characters || 0; }
|
|
41
|
+
if (token.name === 'startKey') key = '';
|
|
42
|
+
else if (token.name === 'stringChunk' && key !== null) {
|
|
43
|
+
if (key.length + token.value.length > limits.maxKeyCharacters) fail('import_key_limit', '导出字段名过长。');
|
|
44
|
+
key += token.value;
|
|
45
|
+
} else if (token.name === 'endKey') {
|
|
46
|
+
const container = containers.at(-1),keys=container?.keys;
|
|
47
|
+
if (!keys || keys.has(key) || keys.size >= limits.maxRows) fail('import_duplicate_key', '导出对象含重复字段或过多字段。');
|
|
48
|
+
if(heldKeyCharacters+key.length>2097152)fail('import_key_limit','嵌套对象字段名总量超限。');
|
|
49
|
+
heldKeyCharacters+=key.length;container.characters+=key.length;
|
|
50
|
+
keys.add(key); const value = key; key = null;
|
|
51
|
+
return many([token, { name: 'keyValue', value }]);
|
|
52
|
+
}
|
|
53
|
+
if (token.name === 'startNumber') numberLength = 0;
|
|
54
|
+
if (token.name === 'numberChunk' && (numberLength += token.value.length) > 128)
|
|
55
|
+
fail('import_number_limit', '导出数字过长。');
|
|
56
|
+
return token;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function selected(path) {
|
|
60
|
+
if (!path.length) return true;
|
|
61
|
+
if (envelopeFields.has(path[0])) return true;
|
|
62
|
+
if (['readOnly','version'].includes(path[0])) return true;
|
|
63
|
+
if (path[0] !== 'snapshot') return false;
|
|
64
|
+
if (path.length === 1) return true;
|
|
65
|
+
const collection = path[1];
|
|
66
|
+
if (['schemaVersion','revision'].includes(collection)) return true;
|
|
67
|
+
const fields = {runs:runFields,eventLog:eventFields,states:stateFields,tasks:taskFields}[collection];
|
|
68
|
+
if (!fields) return false;
|
|
69
|
+
if (path.length < 4) return true;
|
|
70
|
+
if (!fields.has(path[3])) return false;
|
|
71
|
+
if (collection === 'eventLog' && path[3] === 'payload' && path.length > 4) return eventPayload.has(path[4]);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function stringGuard(limits) {
|
|
75
|
+
let length = 0,tokens=0;
|
|
76
|
+
return token => {
|
|
77
|
+
if(++tokens>limits.maxTokens)fail('import_token_limit','业务投影结构超过本次上限。');
|
|
78
|
+
if (token.name === 'startString' || token.name === 'startKey') length = 0;
|
|
79
|
+
if (token.name === 'stringChunk' && (length += token.value.length) > limits.maxStringCharacters)
|
|
80
|
+
fail('import_string_limit', '所选正文超过单项上限,未截断。');
|
|
81
|
+
return token;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The input is the host's native verified reader. Successful EOF is mandatory. */
|
|
86
|
+
export async function readLegacyImport({ stream, expectedBytes, expectedSha256, signal, limits = IMPORT_LIMITS }) {
|
|
87
|
+
if (!nonnegative(expectedBytes) || expectedBytes > limits.maxFileBytes || !/^[a-f0-9]{64}$/.test(expectedSha256))
|
|
88
|
+
fail('invalid_import_file', '附件大小或摘要无效。');
|
|
89
|
+
let bytes = 0, ended = false;
|
|
90
|
+
const digest = createHash('sha256'),utf8=new TextDecoder('utf-8',{fatal:true});
|
|
91
|
+
async function* input() {
|
|
92
|
+
for await (const chunk of stream) {
|
|
93
|
+
signal?.throwIfAborted();
|
|
94
|
+
if (!(chunk instanceof Uint8Array) || chunk.byteLength > limits.maxChunkBytes)
|
|
95
|
+
fail('import_chunk_limit', '附件读取块不符合原生读取合同。');
|
|
96
|
+
bytes += chunk.byteLength;
|
|
97
|
+
if (bytes > limits.maxFileBytes || bytes > expectedBytes) fail('import_byte_limit', '附件超出已声明大小。');
|
|
98
|
+
digest.update(chunk);
|
|
99
|
+
let decoded;try{decoded=utf8.decode(chunk,{stream:true});}catch{fail('invalid_import_json','导出包含损坏的 UTF-8 字节,未替换原话。');}
|
|
100
|
+
if(decoded)yield decoded;
|
|
101
|
+
}
|
|
102
|
+
let tail;try{tail=utf8.decode();}catch{fail('invalid_import_json','导出末尾的 UTF-8 字符不完整。');}
|
|
103
|
+
if(tail)yield tail;
|
|
104
|
+
signal?.throwIfAborted();
|
|
105
|
+
if (bytes !== expectedBytes || digest.digest('hex') !== expectedSha256) fail('import_digest_mismatch', '附件完整摘要不符。');
|
|
106
|
+
ended = true;
|
|
107
|
+
}
|
|
108
|
+
const pipeline = chain([Readable.from(input()), parser({jsonStreaming:true,packKeys:false,packStrings:false,packNumbers:false,
|
|
109
|
+
streamKeys:true,streamStrings:true,streamNumbers:true}), keyGuard(limits),
|
|
110
|
+
filter({filter:selected,acceptObjects:false,streamKeys:true,maxDepth:limits.maxDepth}), stringGuard(limits),
|
|
111
|
+
stringer({useValues:false,makeArray:true})]);
|
|
112
|
+
const pieces = []; let pending = [], pendingLength = 0, characters = 0;
|
|
113
|
+
try {
|
|
114
|
+
for await (const fragment of pipeline) {
|
|
115
|
+
signal?.throwIfAborted();
|
|
116
|
+
if ((characters += fragment.length) > limits.maxProjectionCharacters) fail('import_projection_limit', '业务投影超过上限,未截断。');
|
|
117
|
+
pending.push(fragment); pendingLength += fragment.length;
|
|
118
|
+
if (pendingLength >= 65536) { pieces.push(pending.join('')); pending = []; pendingLength = 0; }
|
|
119
|
+
}
|
|
120
|
+
} finally { pipeline.destroy(); }
|
|
121
|
+
if (!ended) fail('import_incomplete', '附件尚未完成原生完整性校验。');
|
|
122
|
+
pieces.push(pending.join(''));
|
|
123
|
+
let documents;
|
|
124
|
+
try { documents = JSON.parse(pieces.join('')); } catch { fail('invalid_import_json', '导出 JSON 不完整或无效。'); }
|
|
125
|
+
if (!Array.isArray(documents) || !documents.length) fail('invalid_import_format', '导出内容为空。');
|
|
126
|
+
return documents.length === 1 && object(documents[0].snapshot)
|
|
127
|
+
? normalizeOld2(documents[0], limits) : normalizeOld3(documents, limits);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function unique(rows, kind, limits) {
|
|
131
|
+
if (rows.length > limits.maxRows) fail('import_record_limit', '导出记录过多。');
|
|
132
|
+
const map = new Map();
|
|
133
|
+
for (const row of rows) {
|
|
134
|
+
if (!object(row) || !validId(row.id) || map.has(row.id)) fail('invalid_import_identity', '导出记录身份缺失或重复:' + kind);
|
|
135
|
+
map.set(row.id, row);
|
|
136
|
+
}
|
|
137
|
+
return map;
|
|
138
|
+
}
|
|
139
|
+
function scopeOf(scope, fallback, old3Kind) {
|
|
140
|
+
const result={global:false,sourceConversationId:fallback||null,supported:false};
|
|
141
|
+
// Actual old3 saveMethod has no scope field; its appliesTo/doesNotApplyTo
|
|
142
|
+
// strings define guidance applicability. Its current consumer is global.
|
|
143
|
+
if(old3Kind==='methods'&&scope===undefined)return {...result,global:true,sourceConversationId:null,supported:true};
|
|
144
|
+
if(old3Kind){
|
|
145
|
+
if(!object(scope)||Object.keys(scope).some(key=>!['conversationId','topics'].includes(key))||
|
|
146
|
+
scope.conversationId!==undefined&&!validId(scope.conversationId)||scope.topics!==undefined&&(!Array.isArray(scope.topics)||
|
|
147
|
+
scope.topics.length>UNDERSTANDING_SCHEMA.properties.topics.maxItems||scope.topics.some(x=>typeof x!=='string'||!x||x.length>UNDERSTANDING_SCHEMA.properties.topics.items.maxLength)))return result;
|
|
148
|
+
return {global:!scope.conversationId,sourceConversationId:scope.conversationId||null,supported:true,
|
|
149
|
+
...(scope.topics?.length?{topics:scope.topics}:{})};
|
|
150
|
+
}
|
|
151
|
+
if(scope==='global'||object(scope)&&scope.type==='global'&&Object.keys(scope).every(k=>k==='type'))return {...result,global:true,sourceConversationId:null,supported:true};
|
|
152
|
+
if(object(scope)&&['run','task'].includes(scope.type)&&validId(scope.id)&&Object.keys(scope).every(k=>['type','id'].includes(k)))
|
|
153
|
+
return {...result,sourceConversationId:scope.type==='task'?'task:'+scope.id:scope.id,supported:true};
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
function scopes(sources, conversations, limits) {
|
|
157
|
+
const groups = new Map();
|
|
158
|
+
for (const source of sources) {
|
|
159
|
+
const id = source.sourceConversationId;
|
|
160
|
+
if (!id) continue;
|
|
161
|
+
if (!groups.has(id)) groups.set(id, {sourceConversationId:id,title:String(conversations.get(id)?.title || '').slice(0,120),preview:source.content.slice(0,240),messageCount:0});
|
|
162
|
+
groups.get(id).messageCount++;
|
|
163
|
+
}
|
|
164
|
+
if (groups.size > limits.maxScopes) fail('import_scope_limit', '旧交流数量超过本次上限。');
|
|
165
|
+
return [...groups.values()];
|
|
166
|
+
}
|
|
167
|
+
function normalizedRecord(kind, row, reasonCodes, sourceIds, scope, fields = {}) {
|
|
168
|
+
return {id:kind+':'+row.id,kind,originalId:row.id,original:row,sourceIds,scope,
|
|
169
|
+
eligibility:reasonCodes.length ? 'needs_review' : 'enabled',reasonCodes,...fields};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function normalizeOld2(document, limits) {
|
|
173
|
+
const snapshot = document.snapshot, runs = unique(values(snapshot.runs),'runs',limits);
|
|
174
|
+
const events = unique(values(snapshot.eventLog),'events',limits), sources = [], sourceAliases = new Map();
|
|
175
|
+
for (const run of runs.values()) {
|
|
176
|
+
// Old2's ordinary sessionId is the literal "continuous", not a proof that
|
|
177
|
+
// distinct runs belong to one conversation. Preserve separate run groups.
|
|
178
|
+
const group = run.id;
|
|
179
|
+
for (const [role,field,type] of [['user','request','user/message'],['assistant','answer','main.answer']]) {
|
|
180
|
+
if (typeof run[field] !== 'string' || !run[field].trim()) continue;
|
|
181
|
+
const matches = [...events.values()].filter(event => event.runId === run.id && event.type === type);
|
|
182
|
+
const event = matches.length === 1 ? matches[0] : null;
|
|
183
|
+
const eventBody = event?.payload?.[field] ?? event?.payload?.text ?? event?.payload?.content ?? event?.payload?.detail;
|
|
184
|
+
const verified = event && eventBody === run[field] && event.actor?.verified === true && event.actor.kind === (role === 'user' ? 'user' : 'agent') && positive(event.version);
|
|
185
|
+
const originalId = verified ? event.id : run.id+':'+field;
|
|
186
|
+
const source = {id:'events:'+originalId,originalId,role,content:run[field],sourceConversationId:group,
|
|
187
|
+
occurredAt:event?.occurredAt || run.createdAt || run.startedAt || null,
|
|
188
|
+
available:!unavailable(event || {}) && !unavailable(run) && run.invalidated!==true,
|
|
189
|
+
original:verified ? event : {runId:run.id,field,status:run.status ?? null,error:run.error ?? null,answerValid:run.answerValid ?? null},
|
|
190
|
+
originalVersion:verified ? event.version : null,provenance:{kind:'legacy2_original',runId:run.id,field,verifiedEvent:Boolean(verified)}};
|
|
191
|
+
sources.push(source);
|
|
192
|
+
if (verified) sourceAliases.set(event.id,source);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (!sources.length) fail('empty_import', '未找到完整保存的旧原话。');
|
|
196
|
+
if (sources.length > limits.maxRows) fail('import_record_limit', '旧原话数量超过上限。');
|
|
197
|
+
const records = [];
|
|
198
|
+
for (const state of unique(values(snapshot.states),'states',limits).values()) {
|
|
199
|
+
const reasons = [], refs = [], scope = scopeOf(state.scope,state.sourceRunId);
|
|
200
|
+
if (typeof state.text !== 'string' || !state.text.trim() || !positive(state.revision) || state.status !== 'active' || (state.verification?.status ?? state.verification) !== 'supported' || state.expiresAt || unavailable(state)) reasons.push('old_state_not_current');
|
|
201
|
+
if(!scope.supported)reasons.push('scope_unsupported');
|
|
202
|
+
if (!Array.isArray(state.evidence) || !state.evidence.length || state.derivedFrom?.length) reasons.push('source_closure_unproven');
|
|
203
|
+
for (const ref of state.evidence || []) {
|
|
204
|
+
const source = sourceAliases.get(ref.eventId);
|
|
205
|
+
if (!source || !source.available || source.originalVersion !== ref.version || typeof ref.quote !== 'string' || !ref.quote || !source.content.includes(ref.quote)) { reasons.push('source_reference_unproven'); continue; }
|
|
206
|
+
if (ref.startCharacter !== undefined || ref.endCharacter !== undefined) {
|
|
207
|
+
const boundary=index=>index===0||index===source.content.length||!(/[\uD800-\uDBFF]/.test(source.content[index-1])&&/[\uDC00-\uDFFF]/.test(source.content[index]));
|
|
208
|
+
if (!nonnegative(ref.startCharacter) || !positive(ref.endCharacter) || ref.endCharacter>source.content.length ||
|
|
209
|
+
!boundary(ref.startCharacter)||!boundary(ref.endCharacter)||source.content.slice(ref.startCharacter,ref.endCharacter) !== ref.quote) reasons.push('source_range_mismatch');
|
|
210
|
+
}
|
|
211
|
+
if(state.basis==='user' && (source.role!=='user'||!userQuote(source.content,ref.quote)))reasons.push('user_authored_range_unproven');
|
|
212
|
+
refs.push(source.id);
|
|
213
|
+
}
|
|
214
|
+
records.push(normalizedRecord('understandings',state,[...new Set(reasons)],[...new Set(refs)],scope,
|
|
215
|
+
{content:(state.text || '')+(typeof state.applicability==='string'&&state.applicability?'\n适用条件:'+state.applicability:''),claimType:'inference',version:positive(state.revision)?state.revision:null}));
|
|
216
|
+
}
|
|
217
|
+
for (const task of unique(values(snapshot.tasks),'tasks',limits).values()) records.push(normalizedRecord('tasks',task,
|
|
218
|
+
['old_tasks_history_only'],[],scopeOf('conversation',null),{eligibility:'history',content:typeof task.goal==='string'?task.goal:typeof task.title==='string'?task.title:''}));
|
|
219
|
+
return {format:'arcfold2-database-snapshot',sources,records,sourceScopes:scopes(sources,new Map(),limits),
|
|
220
|
+
provenance:{version:document.version ?? null,schemaVersion:snapshot.schemaVersion ?? null,revision:snapshot.revision ?? null,runCount:runs.size},
|
|
221
|
+
history:[...runs.values()].map(record => ({kind:'runs',record}))};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Old2's Codex annotation packet contains quoted assistant text. Only its
|
|
225
|
+
// explicit request suffix is user-authored; ordinary source text is intact.
|
|
226
|
+
function userQuote(text,quote){
|
|
227
|
+
if(!/<\/?response-annotations\b|^\s*#{1,6}\s*(?:Response annotations|My request)\s*:/im.test(text))return text.includes(quote);
|
|
228
|
+
const opens=[...text.matchAll(/<response-annotations>/g)],closes=[...text.matchAll(/<\/response-annotations>/g)],headers=[...text.matchAll(/^## My request:[\t ]*(?:\r?\n|$)/gm)];
|
|
229
|
+
if(opens.length!==1||closes.length!==1||headers.length!==1||opens[0].index>=closes[0].index||closes[0].index+closes[0][0].length>headers[0].index||!/^\s*# Response annotations:[^]*$/.test(text.slice(0,opens[0].index)))return false;
|
|
230
|
+
try{const rows=JSON.parse(text.slice(opens[0].index+opens[0][0].length,closes[0].index));if(!Array.isArray(rows)||rows.some(row=>!object(row)||typeof row.text!=='string'))return false;}catch{return false;}
|
|
231
|
+
const body=text.slice(headers[0].index+headers[0][0].length);
|
|
232
|
+
return Boolean(body.trim())&&!/<\/?response-annotations\b|^\s*#{1,6}\s*(?:Response annotations|My request)\s*:/im.test(body)&&body.includes(quote);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const old3Kinds = new Set(['events','conversations','understandings','methods','goals','opportunities','adoptions','audit','feedback','deliveries','imports','source_controls']);
|
|
236
|
+
const excludedBusinessKeys = /^(?:config|settings|credentials?|secrets?|password|api[_-]?key|env|environment|transport|authorization|budget(?:_settings|_requests)?|jobs|leases|sourceSnapshot)$/i;
|
|
237
|
+
function assertBusinessProjection(value) {
|
|
238
|
+
if(!value||typeof value!=='object')return;
|
|
239
|
+
for(const [key,item]of Object.entries(value)){
|
|
240
|
+
if(excludedBusinessKeys.test(key))fail('invalid_import_export','业务导出不能携带运行配置、凭据、费用或旧数据库快照。');
|
|
241
|
+
assertBusinessProjection(item);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function normalizeOld3(documents, limits) {
|
|
245
|
+
const header = documents[0], footer = documents.at(-1), rows = documents.slice(1,-1);
|
|
246
|
+
if (header?.type !== 'header' || header.format !== 'arcfold-legacy3-business-export-v1' || !validId(header.exportId) ||
|
|
247
|
+
header.schemaVersion !== 2 || !nonnegative(header.mutationSequence) || !Array.isArray(header.collections) ||
|
|
248
|
+
header.collections.some(kind => !old3Kinds.has(kind)) || new Set(header.collections).size !== header.collections.length ||
|
|
249
|
+
header.collections.length !== old3Kinds.size || ![...old3Kinds].every(kind => header.collections.includes(kind)) ||
|
|
250
|
+
footer?.type !== 'footer' || footer.complete !== true || footer.digest !== importDigest(rows))
|
|
251
|
+
fail('invalid_import_export', '旧3导出缺少完整头、结束记录或内容摘要。');
|
|
252
|
+
assertBusinessProjection(header);
|
|
253
|
+
const tables = new Map(header.collections.map(kind=>[kind,[]]));
|
|
254
|
+
for (const row of rows) {
|
|
255
|
+
if (row?.type !== 'record' || !tables.has(row.kind) || !object(row.record)) fail('invalid_import_export','旧3导出包含未声明的记录类别。');
|
|
256
|
+
assertBusinessProjection(row.record);
|
|
257
|
+
tables.get(row.kind).push(row.record);
|
|
258
|
+
}
|
|
259
|
+
const maps = new Map();
|
|
260
|
+
for (const [kind,items] of tables) {
|
|
261
|
+
if (footer.counts?.[kind] !== items.length) fail('invalid_import_export','旧3导出记录计数不符。');
|
|
262
|
+
maps.set(kind,unique(items,kind,limits));
|
|
263
|
+
}
|
|
264
|
+
if (rows.length > limits.maxRows || Object.keys(footer.counts || {}).length !== tables.size) fail('import_record_limit','导出记录计数超出合同。');
|
|
265
|
+
const get = (kind,id) => maps.get(kind)?.get(id), sourceList = [], sourceMap = new Map();
|
|
266
|
+
for (const row of maps.get('events').values()) {
|
|
267
|
+
if (typeof row.content !== 'string' || typeof row.role!=='string' || !row.role || row.role.length>100)
|
|
268
|
+
fail('invalid_import_source','旧3原话缺少准确角色、正文或版本。');
|
|
269
|
+
const control = get('source_controls',row.id);
|
|
270
|
+
const source = {id:'events:'+row.id,originalId:row.id,role:row.role,content:row.content,
|
|
271
|
+
sourceConversationId:validId(row.conversationId)?row.conversationId:null,occurredAt:row.occurredAt ?? null,
|
|
272
|
+
available:['user','assistant','tool','system','material'].includes(row.role) && positive(row.revision) && !unavailable(row) && !unavailable(control || {}) && positive(row.createdSequence) && positive(row.updatedSequence) &&
|
|
273
|
+
row.createdSequence<=row.updatedSequence && row.updatedSequence<=header.mutationSequence,
|
|
274
|
+
original:row,originalVersion:positive(row.revision)?row.revision:null,
|
|
275
|
+
provenance:{kind:'legacy3_original',exportId:header.exportId,originalProvenance:row.provenance ?? null}};
|
|
276
|
+
sourceList.push(source);sourceMap.set(row.id,source);
|
|
277
|
+
}
|
|
278
|
+
const validation = new Map();
|
|
279
|
+
function qualify(kind,row,path=new Set()) {
|
|
280
|
+
const key=kind+':'+row.id;
|
|
281
|
+
if (validation.has(key)) return validation.get(key);
|
|
282
|
+
const reasons=[];
|
|
283
|
+
if (path.has(key)) return ['source_cycle'];
|
|
284
|
+
const next=new Set([...path,key]);
|
|
285
|
+
if(kind==='understandings'&&row.status!=='active')reasons.push('old_record_not_current');
|
|
286
|
+
if (unavailable(row) || !positive(row.revision) || !positive(row.version) ||
|
|
287
|
+
!positive(row.createdSequence) || !positive(row.updatedSequence) || row.updatedSequence>header.mutationSequence || row.createdSequence>row.updatedSequence)
|
|
288
|
+
reasons.push('old_record_not_current');
|
|
289
|
+
if (!Array.isArray(row.sourceIds) || !row.sourceIds.length || !Array.isArray(row.dependencies)) reasons.push('source_closure_unproven');
|
|
290
|
+
for (const id of row.sourceIds || []) {
|
|
291
|
+
const source=sourceMap.get(id);
|
|
292
|
+
if (!source?.available) reasons.push('source_unavailable');
|
|
293
|
+
if(!source || !(row.dependencies || []).some(ref=>ref.id===id && (ref.kind===undefined || ref.kind==='events') && ref.revision===source.originalVersion))
|
|
294
|
+
reasons.push('source_revision_unproven');
|
|
295
|
+
}
|
|
296
|
+
for (const ref of row.dependencies || []) {
|
|
297
|
+
const depKind=ref.kind || ['events','understandings','methods'].find(k=>get(k,ref.id)), dep=get(depKind,ref.id);
|
|
298
|
+
if (!dep || !['events','understandings','methods'].includes(depKind) || dep.revision!==ref.revision || unavailable(dep) || dep.status==='paused' ||
|
|
299
|
+
depKind==='events' && !sourceMap.get(dep.id)?.available) reasons.push('dependency_version_unproven');
|
|
300
|
+
else if (depKind!=='events') reasons.push(...qualify(depKind,dep,next));
|
|
301
|
+
}
|
|
302
|
+
validation.set(key,[...new Set(reasons)]);return validation.get(key);
|
|
303
|
+
}
|
|
304
|
+
const records=[];
|
|
305
|
+
for (const kind of ['understandings','methods','goals','opportunities']) for (const row of maps.get(kind)?.values() || []) {
|
|
306
|
+
const reasons=[...qualify(kind,row)], scope=scopeOf(row.scope,row.conversationId,kind);
|
|
307
|
+
if(!scope.supported)reasons.push('scope_unsupported');
|
|
308
|
+
let eligibility='enabled', usage;
|
|
309
|
+
if(kind==='understandings'&&(typeof row.content!=='string'||!row.content.trim()))reasons.push('old_record_not_current');
|
|
310
|
+
if (kind==='methods') {
|
|
311
|
+
if (!['title','guidance','appliesTo','doesNotApplyTo'].every(key=>importMethodField(row,key)!==null) ||
|
|
312
|
+
!['trial','paused','active'].includes(row.status)) reasons.push('method_contract_unproven');
|
|
313
|
+
if (!nonnegative(row.trialUses) || !positive(row.maxTrialUses)) reasons.push('method_trial_basis_unknown');
|
|
314
|
+
else {
|
|
315
|
+
const trial=[];let valid=true;
|
|
316
|
+
for (const adoption of maps.get('adoptions')?.values() || []) for (const selected of adoption.selectedVersions || []) {
|
|
317
|
+
if(selected.id!==row.id)continue;
|
|
318
|
+
if(!positive(adoption.revision)||!positive(adoption.createdSequence)||!positive(adoption.updatedSequence)||
|
|
319
|
+
adoption.createdSequence>adoption.updatedSequence||adoption.updatedSequence>header.mutationSequence)valid=false;
|
|
320
|
+
if(!positive(selected.semanticVersion ?? selected.version) || !positive(selected.revision)){valid=false;continue;}
|
|
321
|
+
if ((selected.semanticVersion ?? selected.version)!==row.version) continue;
|
|
322
|
+
if(selected.revision>row.revision)valid=false;
|
|
323
|
+
if (selected.trialUse!==null) { if(!positive(selected.trialUse)) valid=false;else trial.push(selected.trialUse); }
|
|
324
|
+
}
|
|
325
|
+
if (!valid || trial.length!==row.trialUses || new Set(trial).size!==trial.length || trial.some(n=>n>row.trialUses)) reasons.push('method_trial_history_mismatch');
|
|
326
|
+
else usage={trialUses:row.trialUses,maxTrialUses:row.maxTrialUses,remaining:Math.max(0,row.maxTrialUses-row.trialUses),status:row.status};
|
|
327
|
+
}
|
|
328
|
+
if (row.status==='active' && (!sourceMap.get(row.activationSourceId)?.available || !activeProof(row,get,[...(maps.get('audit')?.values() || [])],header.mutationSequence))) reasons.push('method_active_authority_unproven');
|
|
329
|
+
eligibility=row.status==='paused'?'paused':row.status==='trial'?'trial':'enabled';
|
|
330
|
+
}
|
|
331
|
+
// Historical user goal controls and preparation states are never revived.
|
|
332
|
+
if (['goals','opportunities'].includes(kind)) { reasons.push('historical_control_not_migrated');eligibility='history'; }
|
|
333
|
+
const originals=[...(row.sourceIds || [])];
|
|
334
|
+
if(kind==='methods'&&row.status==='active'&&sourceMap.has(row.activationSourceId)&&!originals.includes(row.activationSourceId))originals.push(row.activationSourceId);
|
|
335
|
+
records.push(normalizedRecord(kind,row,[...new Set(reasons)],originals.map(id=>'events:'+id),scope,
|
|
336
|
+
{eligibility:reasons.length && eligibility!=='history'?'needs_review':eligibility,
|
|
337
|
+
content:[row.content,row.guidance,row.outcome,row.description].find(value=>typeof value==='string')??'',version:row.version ?? null,
|
|
338
|
+
...(usage?{methodUsage:usage}:{})}));
|
|
339
|
+
}
|
|
340
|
+
return {format:header.format,sources:sourceList,records,sourceScopes:scopes(sourceList,maps.get('conversations') || new Map(),limits),
|
|
341
|
+
provenance:header,history:rows.filter(row=>!['events','understandings','methods','goals','opportunities'].includes(row.kind)).map(row=>({kind:row.kind,record:row.record}))};
|
|
342
|
+
}
|
|
343
|
+
function activeProof(method,get,audits,cutoff) {
|
|
344
|
+
const source=get('events',method.activationSourceId), p=source?.provenance;
|
|
345
|
+
if (!source || source.role!=='user' || !source.content?.trim() || unavailable(source) || p?.kind!=='explicit_method_authorization' || p.interaction!=='method_activation' ||
|
|
346
|
+
p.methodId!==method.id || p.methodVersion!==method.version || !positive(p.methodRevision) ||
|
|
347
|
+
p.appliesTo!==method.appliesTo || p.doesNotApplyTo!==method.doesNotApplyTo || method.validated!==false || method.revision<p.methodRevision+1) return false;
|
|
348
|
+
return audits.some(audit=>audit.action==='user_method_activation' && audit.kind==='methods' && audit.recordId===method.id &&
|
|
349
|
+
audit.sourceId===source.id && positive(audit.createdSequence) && positive(audit.updatedSequence) && audit.createdSequence<=audit.updatedSequence && audit.updatedSequence<=cutoff &&
|
|
350
|
+
audit.before?.id===method.id && audit.after?.id===method.id && ['trial','paused'].includes(audit.before?.status) &&
|
|
351
|
+
audit.before?.revision===p.methodRevision && audit.after?.revision===p.methodRevision+1 &&
|
|
352
|
+
audit.after?.status==='active' && audit.after?.validated===false && audit.after?.activationSourceId===source.id &&
|
|
353
|
+
['title','guidance','appliesTo','doesNotApplyTo','version','sourceIds','dependencies','scope'].every(key=>
|
|
354
|
+
canonicalJson(audit.before?.[key] ?? null)===canonicalJson(method[key] ?? null) &&
|
|
355
|
+
canonicalJson(audit.after?.[key] ?? null)===canonicalJson(method[key] ?? null)));
|
|
356
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { openOwnedWebBudget } from './web-budget-recovery.mjs';
|
|
2
|
+
import { isAbsolute } from 'node:path';
|
|
3
|
+
import { Agent, ProxyAgent } from 'undici';
|
|
4
|
+
import { getRuntimeGuard } from './runtime-guard.mjs';
|
|
5
|
+
import { BudgetLedger } from './budget.mjs';
|
|
6
|
+
import { OpenAIUsageLedger } from './openai-usage.mjs';
|
|
7
|
+
import { DeepSeekTransport } from './deepseek-transport.mjs';
|
|
8
|
+
import { OpenAITransport, OPENAI_EVIDENCE_VERSION } from './openai-transport.mjs';
|
|
9
|
+
import { nativeRequestIdentity, ownEvidenceDirectory, assertNativeRequestCurrent } from './native-request-authority.mjs';
|
|
10
|
+
|
|
11
|
+
function frozen(value) {
|
|
12
|
+
if (value && typeof value === 'object') { for (const child of Object.values(value)) frozen(child); Object.freeze(value); }
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
function dispatcherFor(network) {
|
|
16
|
+
if (network?.mode === 'direct' && Object.keys(network).length === 1) return new Agent();
|
|
17
|
+
if (network?.mode === 'proxy' && Object.keys(network).length === 2 && typeof network.url === 'string') {
|
|
18
|
+
const proxy = new URL(network.url);
|
|
19
|
+
if (['http:', 'https:'].includes(proxy.protocol) && !proxy.username && !proxy.password &&
|
|
20
|
+
proxy.pathname === '/' && !proxy.search && !proxy.hash)
|
|
21
|
+
return new ProxyAgent({ uri: proxy.href });
|
|
22
|
+
}
|
|
23
|
+
throw new Error('An explicit fixed direct or HTTP CONNECT proxy route is required');
|
|
24
|
+
}
|
|
25
|
+
export const name = 'arcfold-metered-provider-host';
|
|
26
|
+
export const inject = ['arcfoldBusiness', 'arcfoldBridge', 'sessions', 'llm'];
|
|
27
|
+
export async function apply(ctx, config = {}) {
|
|
28
|
+
if (!isAbsolute(config.artifactDirectory || '') || !config.routes ||
|
|
29
|
+
!Object.keys(config.routes).length || Object.keys(config.routes).some(p => !['openai','deepseek-official'].includes(p)))
|
|
30
|
+
throw new Error('An absolute evidence root and fixed owned provider routes are required');
|
|
31
|
+
const routes = frozen(structuredClone(config.routes));
|
|
32
|
+
const lease = await ownEvidenceDirectory(config.artifactDirectory);
|
|
33
|
+
const ledgers = [], transports = new Map();
|
|
34
|
+
const lifecycle = { stopping: new AbortController(), inflight: new Map(),
|
|
35
|
+
evidenceVersions: new Set(Object.keys(routes).map(p => p === 'openai' ? OPENAI_EVIDENCE_VERSION : 'arcfold-messages-transport-v1')) };
|
|
36
|
+
let closing;
|
|
37
|
+
const close = () => closing ??= (async () => {
|
|
38
|
+
try { const outcomes = await Promise.allSettled([...transports.values()].map(t => t.close()));
|
|
39
|
+
if (outcomes.some(outcome => outcome.status === 'rejected')) throw new Error('Owned transport cleanup failed'); }
|
|
40
|
+
finally { try { for (const ledger of ledgers) ledger.close(); } finally { await lease.close(); } }
|
|
41
|
+
})();
|
|
42
|
+
ctx.effect(() => close, 'drain shared sends before closing ledgers and the evidence lock');
|
|
43
|
+
try {
|
|
44
|
+
for (const [provider, route] of Object.entries(routes)) {
|
|
45
|
+
if (!isAbsolute(route.ledgerPath || '') || route.kind !== (provider === 'openai' ? 'openai' : 'deepseek') || provider === 'openai' && route.scope !== 'web-openai')
|
|
46
|
+
throw new Error('Each owned route requires its explicitly selected existing ledger');
|
|
47
|
+
if (route.mode !== undefined && (provider !== 'deepseek-official' || route.mode !== 'web-only' ||
|
|
48
|
+
route.scope !== 'web-unlimited' || Object.hasOwn(route, 'ceilingCny') || Object.hasOwn(route, 'authorizationRef')))
|
|
49
|
+
throw new Error('Web-only requires independent explicit web scope without a finite allowance');
|
|
50
|
+
const ledger = route.mode === 'web-only'
|
|
51
|
+
? openOwnedWebBudget({ artifactDirectory: config.artifactDirectory, ledgerPath: route.ledgerPath,
|
|
52
|
+
webAuthorization: route.webAuthorization, owner: lease }).ledger
|
|
53
|
+
: provider === 'openai'
|
|
54
|
+
? new OpenAIUsageLedger(route.ledgerPath, { scope: 'web-openai', webAuthorization: route.webAuthorization, create: false })
|
|
55
|
+
: new BudgetLedger(route.ledgerPath, { scope: route.scope, ceilingCny: route.ceilingCny,
|
|
56
|
+
authorizationRef: route.authorizationRef, webAuthorization: route.webAuthorization, create: false });
|
|
57
|
+
ledgers.push(ledger);
|
|
58
|
+
const dispatcher = dispatcherFor(route.network);
|
|
59
|
+
let transport;
|
|
60
|
+
try {
|
|
61
|
+
transport = new (provider === 'openai' ? OpenAITransport : DeepSeekTransport)({
|
|
62
|
+
ledger, artifactDirectory: config.artifactDirectory, provider, modelPolicies: route.modelPolicies,
|
|
63
|
+
endpoint: route.endpoint, allowLocalTestEndpoint: route.allowLocalTestEndpoint === true,
|
|
64
|
+
timeoutMs: route.timeoutMs, dispatcher, ownsDispatcher: true, lifecycle,
|
|
65
|
+
requestIdentity: options => nativeRequestIdentity(ctx.arcfoldBusiness, ctx.sessions, options),
|
|
66
|
+
beforeDispatch: options => {
|
|
67
|
+
getRuntimeGuard(ctx)?.assertDispatch(options);
|
|
68
|
+
return assertNativeRequestCurrent(ctx, options);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
} catch (error) { await dispatcher.close(); throw error; }
|
|
72
|
+
transports.set(provider, transport);
|
|
73
|
+
}
|
|
74
|
+
for (const transport of transports.values()) await transport.recover({ exclusive: true });
|
|
75
|
+
ctx.on('llm/stream', (options, next) => {
|
|
76
|
+
const transport = transports.get(options.provider);
|
|
77
|
+
if (transport) transport.validateInput(options);
|
|
78
|
+
return next();
|
|
79
|
+
}, { prepend: true });
|
|
80
|
+
const configurations = frozen(Object.fromEntries([...transports].map(([provider, transport]) => [provider, {
|
|
81
|
+
provider, endpoint: transport.endpoint, modelPolicies: structuredClone(transport.modelPolicies),
|
|
82
|
+
network: structuredClone(routes[provider].network),
|
|
83
|
+
}])));
|
|
84
|
+
const service = Object.freeze({
|
|
85
|
+
configuration: Object.freeze({ routes: configurations, evidenceDirectory: config.artifactDirectory }),
|
|
86
|
+
summary(provider) {
|
|
87
|
+
const ledger = transports.get(provider)?.ledger;
|
|
88
|
+
return ledger ? (provider === 'openai' ? ledger.scopeSummary() : ledger.summary()) : null;
|
|
89
|
+
},
|
|
90
|
+
createTransport(options) {
|
|
91
|
+
const transport = transports.get(options.provider);
|
|
92
|
+
if (!transport) throw new Error('This route has no mandatory owned transport');
|
|
93
|
+
return transport.createTransport(options);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
ctx.provide('arcfoldMeteredTransport', service);
|
|
97
|
+
if (transports.has('deepseek-official')) ctx.provide('arcfoldDeepSeekTransport', Object.freeze({
|
|
98
|
+
configuration: configurations['deepseek-official'], createTransport: options => service.createTransport(options),
|
|
99
|
+
}));
|
|
100
|
+
} catch (error) { await close(); throw error; }
|
|
101
|
+
}
|