@rebon/cli-darwin-arm64 0.17.2 → 0.18.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/bin/compose-runtime/package.json +17 -0
- package/bin/compose-runtime/payload/compose/credentials-runtime.js +38 -0
- package/bin/compose-runtime/payload/compose/llm-runtime.js +330 -0
- package/bin/compose-runtime/payload/compose/loop-assembly.js +149 -0
- package/bin/compose-runtime/payload/compose/serve-dispatch.js +82 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-anonymous-user-id.js +10 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-credentials.js +13 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-launch-environment.js +21 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-llm.js +313 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-settings.js +43 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-tools.js +20 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-web.js +10 -0
- package/bin/compose-runtime/payload/compose/shims/llm-adapter.js +32 -0
- package/bin/compose-runtime/payload/compose/shims/zod-lite.js +49 -0
- package/bin/compose-runtime/payload/compose/systemprompt-runtime.js +74 -0
- package/bin/compose-runtime/payload/compose/tools-runtime.js +228 -0
- package/bin/compose-runtime/payload/compose/web-runtime.js +204 -0
- package/bin/compose-runtime/payload/vendor/cordis/index.js +1530 -0
- package/bin/compose-runtime/payload/vendor/cosmokit/LICENSE +21 -0
- package/bin/compose-runtime/payload/vendor/cosmokit/index.mjs +357 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-agent +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-agent-loop +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-llm-core +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-llm-deepseek +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-logger-console +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-scope +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-session +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-system-prompt +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-timeout +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-timer +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tool-todo +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tool-web +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tools-schema +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-web-search-exa +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/agent-loop.js +1193 -0
- package/bin/compose-runtime/payload/vendor/dsh/agent.js +714 -0
- package/bin/compose-runtime/payload/vendor/dsh/llm-core.js +269 -0
- package/bin/compose-runtime/payload/vendor/dsh/llm-deepseek.js +672 -0
- package/bin/compose-runtime/payload/vendor/dsh/logger-console.js +83 -0
- package/bin/compose-runtime/payload/vendor/dsh/scope.js +287 -0
- package/bin/compose-runtime/payload/vendor/dsh/session.js +1668 -0
- package/bin/compose-runtime/payload/vendor/dsh/system-prompt.js +309 -0
- package/bin/compose-runtime/payload/vendor/dsh/timeout.js +100 -0
- package/bin/compose-runtime/payload/vendor/dsh/timer.js +128 -0
- package/bin/compose-runtime/payload/vendor/dsh/tool-todo.js +143 -0
- package/bin/compose-runtime/payload/vendor/dsh/tool-web.js +1527 -0
- package/bin/compose-runtime/payload/vendor/dsh/tools-schema.js +849 -0
- package/bin/compose-runtime/payload/vendor/dsh/web-search-exa.js +122 -0
- package/bin/compose-runtime/payload/vendor/eventsource-parser/LICENSE +21 -0
- package/bin/compose-runtime/payload/vendor/eventsource-parser/index.js +177 -0
- package/bin/compose-runtime/payload/vendor/eventsource-parser/stream.js +48 -0
- package/bin/compose-runtime/payload/vendor/schemastery/index.mjs +656 -0
- package/bin/compose-runtime/payload-manifests.json +48 -0
- package/bin/compose-runtime/src/bridge.mjs +142 -0
- package/bin/compose-runtime/src/credentials-runtime.mjs +50 -0
- package/bin/compose-runtime/src/eventsource-stream.mjs +48 -0
- package/bin/compose-runtime/src/index.mjs +7 -0
- package/bin/compose-runtime/src/llm-runtime.mjs +326 -0
- package/bin/compose-runtime/src/loader.mjs +78 -0
- package/bin/compose-runtime/src/loop-assembly.mjs +237 -0
- package/bin/compose-runtime/src/payload.mjs +49 -0
- package/bin/compose-runtime/src/plugin.mjs +59 -0
- package/bin/compose-runtime/src/realm.mjs +223 -0
- package/bin/compose-runtime/src/registry.mjs +191 -0
- package/bin/compose-runtime/src/resolve.mjs +115 -0
- package/bin/compose-runtime/src/systemprompt-runtime.mjs +67 -0
- package/bin/compose-runtime/src/tools-runtime.mjs +239 -0
- package/bin/compose-runtime/src/web-runtime.mjs +209 -0
- package/bin/plugin-host/package.json +11 -0
- package/bin/plugin-host/src/bridge.mjs +64 -0
- package/bin/plugin-host/src/cli.mjs +105 -0
- package/bin/plugin-host/src/framing.mjs +113 -0
- package/bin/plugin-host/src/host.mjs +624 -0
- package/bin/plugin-host/src/json.mjs +196 -0
- package/bin/plugin-host/src/lifecycle.mjs +114 -0
- package/bin/plugin-host/src/loader.mjs +142 -0
- package/bin/plugin-host/src/methods.mjs +256 -0
- package/bin/plugin-host/src/protocol.mjs +129 -0
- package/bin/plugin-host/src/sdk.mjs +7 -0
- package/bin/rebon +0 -0
- package/bin/rebon-boa-helper +0 -0
- package/package.json +4 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// Embedded-runtime shim of `@deepseek-ai/dsh-llm` — exactly the runtime
|
|
2
|
+
// symbols dsh provider plugins consume, with semantics transcribed from the
|
|
3
|
+
// dsh sources (packages/llm/llm/src). The service half of the real package
|
|
4
|
+
// (LlmRuntime) is NOT here: the composition host mounts its own `ctx.llm`
|
|
5
|
+
// (llm-runtime.js), and this module re-exports the shared LlmAdapter base
|
|
6
|
+
// class (llm-adapter.js) so both import paths hand plugins the same one.
|
|
7
|
+
import z from 'schemastery';
|
|
8
|
+
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout';
|
|
9
|
+
|
|
10
|
+
export { LlmAdapter } from './llm-adapter.js';
|
|
11
|
+
|
|
12
|
+
// ---- vendored helper core (loop round) ----
|
|
13
|
+
// The real dsh-llm helper surface the agent loop consumes — BlockAssembler,
|
|
14
|
+
// message factories, call-config marking, error-chain rendering — bundled
|
|
15
|
+
// verbatim from the dsh sources (vendor/dsh/llm-core.js) and re-exported
|
|
16
|
+
// here so every `@deepseek-ai/dsh-llm` import keeps resolving to this one
|
|
17
|
+
// module (single class/WeakSet identity for instanceof and mark checks).
|
|
18
|
+
export {
|
|
19
|
+
BlockAssembler,
|
|
20
|
+
createMessage,
|
|
21
|
+
freezeMessage,
|
|
22
|
+
createUserMessage,
|
|
23
|
+
createAssistantMessage,
|
|
24
|
+
createToolResultMessage,
|
|
25
|
+
deepFreeze,
|
|
26
|
+
markAgentLoopRequest,
|
|
27
|
+
isAgentLoopRequest,
|
|
28
|
+
callConfigEquals,
|
|
29
|
+
errorChain,
|
|
30
|
+
} from '../../vendor/dsh/llm-core.js';
|
|
31
|
+
|
|
32
|
+
// ---- error taxonomy (dsh error.ts) ----
|
|
33
|
+
|
|
34
|
+
/** Base class for all harness errors: stable machine `code` + cause chain. */
|
|
35
|
+
export class HarnessError extends Error {
|
|
36
|
+
constructor(message, code, options) {
|
|
37
|
+
super(message, options);
|
|
38
|
+
this.code = code;
|
|
39
|
+
this.name = new.target.name;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const CONTEXT_WINDOW_EXCEEDED_CODE = 'CONTEXT_WINDOW_EXCEEDED';
|
|
44
|
+
export const QUOTA_EXCEEDED_CODE = 'QUOTA';
|
|
45
|
+
export const EMPTY_RESPONSE_CODE = 'EMPTY_RESPONSE';
|
|
46
|
+
export const INVALID_CREDENTIAL_CODE = 'INVALID_CREDENTIAL';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Exhaustiveness helper (dsh never.ts, transcribed verbatim): a value that
|
|
50
|
+
* escaped its closed union throws with diagnostics at runtime. Consumed by
|
|
51
|
+
* the vendored dsh-tools schema layer.
|
|
52
|
+
*/
|
|
53
|
+
export function assertNever(value, context) {
|
|
54
|
+
const rendered = JSON.stringify(value) ?? String(value);
|
|
55
|
+
throw new Error(`unreachable variant${context ? ` in ${context}` : ''}: ${rendered}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const STRUCTURED_CONTEXT_OVERFLOW = new RegExp(
|
|
59
|
+
String.raw`(?:^|[^a-z0-9])context[\s_-](?:length|window)[\s_-]`
|
|
60
|
+
+ String.raw`(?:exceed(?:ed|s)?|overflow(?:ed)?|limit[\s_-]exceeded)(?:$|[^a-z0-9])`,
|
|
61
|
+
'i',
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const TOO_LARGE_FOR_CONTEXT = new RegExp(
|
|
65
|
+
String.raw`\b(?:request|prompt|input|messages?)\s+(?:is\s+|are\s+)?`
|
|
66
|
+
+ String.raw`too\s+(?:large|long)\s+for\s+(?:(?:this|the)\s+)?`
|
|
67
|
+
+ String.raw`(?:model(?:'s)?\s+)?context(?:\s+window)?\b`,
|
|
68
|
+
'i',
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const EXCEEDS_MODEL_CONTEXT = new RegExp(
|
|
72
|
+
String.raw`\b(?:input|prompt|request|messages?)\b.{0,40}`
|
|
73
|
+
+ String.raw`\b(?:exceed(?:s|ed)?|overflows?|is\s+larger\s+than)\b.{0,40}`
|
|
74
|
+
+ String.raw`\b(?:the\s+)?(?:model(?:'s)?\s+)?context(?:\s+(?:length|window))?\b`,
|
|
75
|
+
'i',
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
/** Recognize context-overflow wording (code/type/message joined into one string). */
|
|
79
|
+
export function isContextWindowExceededError(detail) {
|
|
80
|
+
return STRUCTURED_CONTEXT_OVERFLOW.test(detail)
|
|
81
|
+
|| /\b(?:maximum|max)(?:\s+(?:allowed|supported))?\s+context\s+(?:length|window)\b/i.test(detail)
|
|
82
|
+
|| TOO_LARGE_FOR_CONTEXT.test(detail)
|
|
83
|
+
|| /\b(?:input|prompt|request)\s+(?:is\s+)?too\s+(?:long|large)\s+for\s+(?:this|the)\s+model\b/i.test(detail)
|
|
84
|
+
|| EXCEEDS_MODEL_CONTEXT.test(detail);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Recognize terminal quota/balance wording, as opposed to a transient rate limit. */
|
|
88
|
+
export function isQuotaExceededError(detail) {
|
|
89
|
+
return /\binsufficient[\s_-]+(?:quota|balance|credits?)\b/i.test(detail)
|
|
90
|
+
|| /\b(?:quota|usage[\s_-]+limit)[\s_-]+(?:exceeded|exhausted|reached)\b/i.test(detail)
|
|
91
|
+
|| /\bexceed(?:ed|s)?[\s_-]+(?:(?:your|the)[\s_-]+)?(?:current[\s_-]+)?quota\b/i.test(detail)
|
|
92
|
+
|| /\b(?:balance|credits?)[\s_-]+(?:exhausted|depleted)\b/i.test(detail)
|
|
93
|
+
|| /\bout[\s_-]+of[\s_-]+(?:credits?|budget)\b/i.test(detail);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Typed error for LLM failures: validated provider facts beside the live
|
|
98
|
+
* Error (dsh index.ts, constructor contract preserved — bad arguments throw).
|
|
99
|
+
*/
|
|
100
|
+
export class LlmError extends HarnessError {
|
|
101
|
+
constructor(message, code, options) {
|
|
102
|
+
if (typeof message !== 'string' || message.length === 0) {
|
|
103
|
+
throw new Error('LlmError message must be a non-empty string');
|
|
104
|
+
}
|
|
105
|
+
if (typeof code !== 'string' || code.length === 0) {
|
|
106
|
+
throw new Error('LlmError code must be a non-empty string');
|
|
107
|
+
}
|
|
108
|
+
if (options?.status !== undefined
|
|
109
|
+
&& (!Number.isInteger(options.status) || options.status < 100 || options.status > 599)) {
|
|
110
|
+
throw new Error('LlmError status must be an integer from 100 through 599');
|
|
111
|
+
}
|
|
112
|
+
if (options?.providerRetryAfterMs !== undefined
|
|
113
|
+
&& (!Number.isFinite(options.providerRetryAfterMs) || options.providerRetryAfterMs <= 0)) {
|
|
114
|
+
throw new Error('LlmError providerRetryAfterMs must be a positive finite number');
|
|
115
|
+
}
|
|
116
|
+
if (options?.requestId !== undefined
|
|
117
|
+
&& (typeof options.requestId !== 'string' || options.requestId.length === 0)) {
|
|
118
|
+
throw new Error('LlmError requestId must be a non-empty string');
|
|
119
|
+
}
|
|
120
|
+
super(message, code, options);
|
|
121
|
+
this.name = 'LlmError';
|
|
122
|
+
this.failure = Object.freeze({
|
|
123
|
+
message,
|
|
124
|
+
code,
|
|
125
|
+
...(options?.status === undefined ? {} : { status: options.status }),
|
|
126
|
+
...(options?.providerRetryAfterMs === undefined
|
|
127
|
+
? {}
|
|
128
|
+
: { providerRetryAfterMs: options.providerRetryAfterMs }),
|
|
129
|
+
...(options?.requestId === undefined ? {} : { requestId: options.requestId }),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ---- api keys (dsh api-key.ts + assertUsableApiKey) ----
|
|
135
|
+
|
|
136
|
+
const LEGAL_API_KEY = /^[\x21-\x7E]+$/;
|
|
137
|
+
|
|
138
|
+
/** Judge one supplied API key, trimming surrounding whitespace first. */
|
|
139
|
+
export function normalizeApiKey(raw) {
|
|
140
|
+
const value = raw.trim();
|
|
141
|
+
if (value.length === 0) return { ok: false, reason: 'empty' };
|
|
142
|
+
if (!LEGAL_API_KEY.test(value)) return { ok: false, reason: 'illegalCharacters' };
|
|
143
|
+
return { ok: true, value };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Accept one supplied credential or refuse it as unusable (never echoes the value). */
|
|
147
|
+
export function assertUsableApiKey(raw, pkg, ref) {
|
|
148
|
+
const checked = normalizeApiKey(raw);
|
|
149
|
+
if (checked.ok) return checked.value;
|
|
150
|
+
throw new LlmError(
|
|
151
|
+
checked.reason === 'empty'
|
|
152
|
+
? `${pkg}: the API key resolved from ${ref} is blank; set ${ref} to the raw key`
|
|
153
|
+
+ ' (the web Models page writes it) or export it in the launching environment'
|
|
154
|
+
: `${pkg}: the API key resolved from ${ref} contains characters no HTTP header can carry;`
|
|
155
|
+
+ ` set ${ref} to the raw key alone (the web Models page writes it)`,
|
|
156
|
+
INVALID_CREDENTIAL_CODE,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---- attribution (dsh attribution.ts; embedded identity, fixed version) ----
|
|
161
|
+
|
|
162
|
+
export const APP_IDENTITY = {
|
|
163
|
+
product: 'deepseek-harness',
|
|
164
|
+
version: '0.0.0-rebon-embedded',
|
|
165
|
+
url: 'https://github.com/deepseek-ai/deepseek-harness',
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export function userAgent(identity = APP_IDENTITY) {
|
|
169
|
+
return `${identity.product}/${identity.version} (+${identity.url})`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function attributionHeaders(identity = APP_IDENTITY) {
|
|
173
|
+
return { 'user-agent': userAgent(identity) };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ---- branded ids (dsh brand.ts: brand = identity at runtime) ----
|
|
177
|
+
|
|
178
|
+
export function MessageId(id) {
|
|
179
|
+
return id;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function CallId(id) {
|
|
183
|
+
return id;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function ProviderRequestId(id) {
|
|
187
|
+
return id;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function ReasoningEffortId(id) {
|
|
191
|
+
return id;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ---- content helpers (dsh content.ts) ----
|
|
195
|
+
|
|
196
|
+
/** True when typed model content contains an image block, walking tool results. */
|
|
197
|
+
export function contentHasImage(content) {
|
|
198
|
+
return content.some((block) => block.type === 'image'
|
|
199
|
+
|| (block.type === 'tool-result' && contentHasImage(block.content)));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ---- retry policy (dsh retry-policy.ts, transcribed) ----
|
|
203
|
+
|
|
204
|
+
const DEFAULT_MAX_RETRIES = 2;
|
|
205
|
+
const DEFAULT_INITIAL_DELAY_MS = 500;
|
|
206
|
+
const DEFAULT_MAX_DELAY_MS = 10_000;
|
|
207
|
+
const DEFAULT_JITTER_RATIO = 0.1;
|
|
208
|
+
const DEFAULT_RETRYABLE_CODES = Object.freeze([
|
|
209
|
+
EMPTY_RESPONSE_CODE,
|
|
210
|
+
'RATE_LIMIT',
|
|
211
|
+
'SERVER',
|
|
212
|
+
'TIMEOUT',
|
|
213
|
+
'TRANSPORT',
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
const backoffSchema = z.object({
|
|
217
|
+
initialDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
|
|
218
|
+
maxDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
|
|
219
|
+
jitterRatio: z.number().min(0).max(1).default(DEFAULT_JITTER_RATIO),
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const normalPolicySchema = z.object({
|
|
223
|
+
mode: z.const('normal').required(),
|
|
224
|
+
maxRetries: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
|
|
225
|
+
retryableCodes: z.array(z.string()).default([...DEFAULT_RETRYABLE_CODES]),
|
|
226
|
+
backoff: backoffSchema,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const alwaysPolicySchema = z.object({
|
|
230
|
+
mode: z.const('always').required(),
|
|
231
|
+
backoff: backoffSchema,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
/** Cordis schema embedded by each concrete provider configuration. */
|
|
235
|
+
export const RetryPolicySchema = z.union([normalPolicySchema, alwaysPolicySchema]);
|
|
236
|
+
|
|
237
|
+
const NORMAL_POLICY_KEYS = new Set(['mode', 'maxRetries', 'retryableCodes', 'backoff']);
|
|
238
|
+
const ALWAYS_POLICY_KEYS = new Set(['mode', 'backoff']);
|
|
239
|
+
const BACKOFF_KEYS = new Set(['initialDelayMs', 'maxDelayMs', 'jitterRatio']);
|
|
240
|
+
|
|
241
|
+
function validateKeys(value, allowed, path) {
|
|
242
|
+
for (const key of Object.keys(value)) {
|
|
243
|
+
if (!allowed.has(key)) throw new Error(`${path}: unknown key "${key}"`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function resolveBackoff(config, path) {
|
|
248
|
+
if (config !== undefined) validateKeys(config, BACKOFF_KEYS, path);
|
|
249
|
+
const initialDelayMs = config?.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
250
|
+
const maxDelayMs = config?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
251
|
+
const jitterRatio = config?.jitterRatio ?? DEFAULT_JITTER_RATIO;
|
|
252
|
+
|
|
253
|
+
if (!Number.isFinite(initialDelayMs) || initialDelayMs <= 0 || initialDelayMs > MAX_TIMER_DELAY_MS) {
|
|
254
|
+
throw new Error(`${path}.initialDelayMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);
|
|
255
|
+
}
|
|
256
|
+
if (!Number.isFinite(maxDelayMs) || maxDelayMs <= 0 || maxDelayMs > MAX_TIMER_DELAY_MS) {
|
|
257
|
+
throw new Error(`${path}.maxDelayMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);
|
|
258
|
+
}
|
|
259
|
+
if (initialDelayMs > maxDelayMs) {
|
|
260
|
+
throw new Error(`${path}.initialDelayMs must be less than or equal to maxDelayMs`);
|
|
261
|
+
}
|
|
262
|
+
if (!Number.isFinite(jitterRatio) || jitterRatio < 0 || jitterRatio > 1) {
|
|
263
|
+
throw new Error(`${path}.jitterRatio must be between 0 and 1`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return Object.freeze({ initialDelayMs, maxDelayMs, jitterRatio });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Validate, default, and detach one provider-owned retry policy. */
|
|
270
|
+
export function resolveRetryPolicy(config, path) {
|
|
271
|
+
if (config === undefined) {
|
|
272
|
+
return Object.freeze({
|
|
273
|
+
mode: 'normal',
|
|
274
|
+
maxRetries: DEFAULT_MAX_RETRIES,
|
|
275
|
+
retryableCodes: DEFAULT_RETRYABLE_CODES,
|
|
276
|
+
...resolveBackoff(undefined, `${path}.backoff`),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
switch (config.mode) {
|
|
281
|
+
case 'normal': {
|
|
282
|
+
validateKeys(config, NORMAL_POLICY_KEYS, path);
|
|
283
|
+
const maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
284
|
+
const retryableCodes = config.retryableCodes ?? [...DEFAULT_RETRYABLE_CODES];
|
|
285
|
+
if (!Number.isSafeInteger(maxRetries) || maxRetries < 0) {
|
|
286
|
+
throw new Error(`${path}.maxRetries must be a non-negative safe integer`);
|
|
287
|
+
}
|
|
288
|
+
if (retryableCodes.length === 0) {
|
|
289
|
+
throw new Error(`${path}.retryableCodes must not be empty`);
|
|
290
|
+
}
|
|
291
|
+
if (retryableCodes.some((code) => typeof code !== 'string' || code.length === 0)) {
|
|
292
|
+
throw new Error(`${path}.retryableCodes must contain only non-empty strings`);
|
|
293
|
+
}
|
|
294
|
+
if (new Set(retryableCodes).size !== retryableCodes.length) {
|
|
295
|
+
throw new Error(`${path}.retryableCodes must not contain duplicates`);
|
|
296
|
+
}
|
|
297
|
+
return Object.freeze({
|
|
298
|
+
mode: 'normal',
|
|
299
|
+
maxRetries,
|
|
300
|
+
retryableCodes: Object.freeze([...retryableCodes]),
|
|
301
|
+
...resolveBackoff(config.backoff, `${path}.backoff`),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
case 'always':
|
|
305
|
+
validateKeys(config, ALWAYS_POLICY_KEYS, path);
|
|
306
|
+
return Object.freeze({
|
|
307
|
+
mode: 'always',
|
|
308
|
+
...resolveBackoff(config.backoff, `${path}.backoff`),
|
|
309
|
+
});
|
|
310
|
+
default:
|
|
311
|
+
throw new Error(`${path}.mode must be "normal" or "always"`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Embedded-runtime shim of `@deepseek-ai/dsh-settings`.
|
|
2
|
+
//
|
|
3
|
+
// The composition host has no user-settings document and no hot reload: a
|
|
4
|
+
// plugin's configuration is its composition entry, fixed for the process
|
|
5
|
+
// lifetime. `installSettingsSection` therefore resolves statically — the
|
|
6
|
+
// source is the entry config, `onChange` never fires — which matches dsh's
|
|
7
|
+
// own behavior in a composition that mounts no settings service (its real
|
|
8
|
+
// implementation rides `ctx.inject(['settings'], …)` and simply never runs).
|
|
9
|
+
|
|
10
|
+
const NAMESPACE_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
11
|
+
|
|
12
|
+
/** Brand a raw string as a SettingsNamespace (lowercase kebab-case). */
|
|
13
|
+
export function settingsNamespace(value) {
|
|
14
|
+
if (!NAMESPACE_PATTERN.test(value)) {
|
|
15
|
+
throw new TypeError(`settings namespace "${value}" must match ${String(NAMESPACE_PATTERN)}`);
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Structural JSON equality (dsh deepEqualJson semantics). */
|
|
21
|
+
export function deepEqualJson(a, b) {
|
|
22
|
+
if (a === b) return true;
|
|
23
|
+
if (typeof a !== typeof b) return false;
|
|
24
|
+
if (a === null || b === null || typeof a !== 'object') return false;
|
|
25
|
+
const aIsArray = Array.isArray(a);
|
|
26
|
+
if (aIsArray !== Array.isArray(b)) return false;
|
|
27
|
+
if (aIsArray) {
|
|
28
|
+
if (a.length !== b.length) return false;
|
|
29
|
+
return a.every((item, i) => deepEqualJson(item, b[i]));
|
|
30
|
+
}
|
|
31
|
+
const aKeys = Object.keys(a);
|
|
32
|
+
const bKeys = Object.keys(b);
|
|
33
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
34
|
+
return aKeys.every((key) => Object.hasOwn(b, key) && deepEqualJson(a[key], b[key]));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Static section install: the entry config is the whole source, once.
|
|
39
|
+
* `onChange` is deliberately never invoked — there is nothing to change.
|
|
40
|
+
*/
|
|
41
|
+
export function installSettingsSection(_ctx, _ns, _schema, entry, hooks) {
|
|
42
|
+
hooks.setSource(() => entry);
|
|
43
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// `@deepseek-ai/dsh-tools` wrapper module (loop round). The schema layer
|
|
2
|
+
// (defineTool + JSON Schema subset) stays the real vendored build; this
|
|
3
|
+
// wrapper adds the scheduler-contract constants that live in dsh's
|
|
4
|
+
// index.ts (NOT bundled — it is the 86KB ToolRuntime service half we
|
|
5
|
+
// deliberately do not vendor; the composition host's tools seat implements
|
|
6
|
+
// the same contract, see compose/tools-runtime.js).
|
|
7
|
+
//
|
|
8
|
+
// Symbol identity: every import of `@deepseek-ai/dsh-tools` resolves here,
|
|
9
|
+
// so agent-loop's `ctx.tools[TOOL_RUNTIME_SCHEDULER]` lookup and the seat's
|
|
10
|
+
// method installation share this one symbol.
|
|
11
|
+
export * from '../../vendor/dsh/tools-schema.js';
|
|
12
|
+
|
|
13
|
+
/** Scheduler entry point (dsh index.ts:466, transcribed contract). */
|
|
14
|
+
export const TOOL_RUNTIME_SCHEDULER = Symbol('@deepseek-ai/dsh-tools.scheduler');
|
|
15
|
+
|
|
16
|
+
/** Canonical error code for cancellation after a tool body was invoked. */
|
|
17
|
+
export const TOOL_ABORTED = 'ABORTED';
|
|
18
|
+
|
|
19
|
+
/** Canonical error code for cancellation before a tool body was invoked. */
|
|
20
|
+
export const TOOL_ABORTED_BEFORE_DISPATCH = 'ABORTED_BEFORE_DISPATCH';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Embedded-runtime shim of `@deepseek-ai/dsh-web` — the runtime symbols web
|
|
2
|
+
// provider plugins consume. The service half (WebRuntime, `ctx.web`) is NOT
|
|
3
|
+
// here: the composition host mounts its own seat (web-runtime.js); this
|
|
4
|
+
// module carries the shared error class so a provider's `throw new
|
|
5
|
+
// WebError(...)` and the runtime's own errors are one identity. Types are
|
|
6
|
+
// erased at transpile time.
|
|
7
|
+
import { HarnessError } from './dsh-llm.js';
|
|
8
|
+
|
|
9
|
+
/** dsh types.ts: typed web error with a machine-routable open-string code. */
|
|
10
|
+
export class WebError extends HarnessError {}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// dsh's abstract LLM adapter, on its own so that importing it costs nothing
|
|
2
|
+
// else.
|
|
3
|
+
//
|
|
4
|
+
// It used to live in `compose/llm-runtime.js`, which the `@deepseek-ai/dsh-llm`
|
|
5
|
+
// shim re-exported it from. That made the base class every provider plugin
|
|
6
|
+
// extends reachable only by evaluating a module that is bound to one host's
|
|
7
|
+
// transport — harmless under deno_core, fatal on real Node, where the same
|
|
8
|
+
// import chain reaches `globalThis.Deno.core` and every dsh package that
|
|
9
|
+
// touches `@deepseek-ai/dsh-llm` fails to load.
|
|
10
|
+
//
|
|
11
|
+
// The class is the contract; the runtime is one implementation of the seat
|
|
12
|
+
// that serves it. Keeping them in separate modules is what lets both hosts
|
|
13
|
+
// hand plugins the same base class.
|
|
14
|
+
|
|
15
|
+
/** dsh's abstract adapter: only `stream(options)` is required. */
|
|
16
|
+
export class LlmAdapter {
|
|
17
|
+
providerInfo(provider) {
|
|
18
|
+
return { id: provider, name: provider };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
providerRetryPolicy(_provider) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
listModels(_provider) {
|
|
26
|
+
return Promise.resolve([]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
resolveModel(provider, model) {
|
|
30
|
+
return Promise.resolve({ provider, id: model, name: model });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Embedded-runtime shim of `zod` — construction surface only.
|
|
2
|
+
//
|
|
3
|
+
// Why it exists: vendored dsh plugins (tool-todo) build zod schema OBJECTS at
|
|
4
|
+
// module load for the session-projection seam (`ctx.sessionProjections`),
|
|
5
|
+
// which this composition never mounts — the schemas are constructed and then
|
|
6
|
+
// never consulted. Bundling real zod would add ~530KB of dead code per
|
|
7
|
+
// vendored plugin, so this shim satisfies module load with inert builders.
|
|
8
|
+
//
|
|
9
|
+
// Honesty boundary: NO validation semantics are provided. Any attempt to
|
|
10
|
+
// actually validate through a shimmed schema (`parse`/`safeParse`) throws
|
|
11
|
+
// loudly instead of silently accepting — if a projection consumer ever lands
|
|
12
|
+
// (P2.9 session seat), replace this shim with a real vendored zod.
|
|
13
|
+
|
|
14
|
+
function inert(kind) {
|
|
15
|
+
return {
|
|
16
|
+
__zodLite: kind,
|
|
17
|
+
parse() {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'zod-lite shim: validation is not implemented (vendor real zod before consuming schemas)',
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
safeParse() {
|
|
23
|
+
throw new Error(
|
|
24
|
+
'zod-lite shim: validation is not implemented (vendor real zod before consuming schemas)',
|
|
25
|
+
);
|
|
26
|
+
},
|
|
27
|
+
optional() {
|
|
28
|
+
return inert(`${kind}?`);
|
|
29
|
+
},
|
|
30
|
+
nullable() {
|
|
31
|
+
return inert(`${kind}|null`);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const z = {
|
|
37
|
+
union: () => inert('union'),
|
|
38
|
+
array: () => inert('array'),
|
|
39
|
+
object: () => inert('object'),
|
|
40
|
+
string: () => inert('string'),
|
|
41
|
+
number: () => inert('number'),
|
|
42
|
+
boolean: () => inert('boolean'),
|
|
43
|
+
literal: () => inert('literal'),
|
|
44
|
+
null: () => inert('null'),
|
|
45
|
+
undefined: () => inert('undefined'),
|
|
46
|
+
unknown: () => inert('unknown'),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default z;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Minimal dsh-compatible `ctx.systemPrompt` seat for the composition host
|
|
2
|
+
// (P2.8-JS, docs/dsh-tools-assembly.md / todo-atomization P2.8).
|
|
3
|
+
//
|
|
4
|
+
// API shape follows dsh packages/core/system-prompt: plugins call
|
|
5
|
+
// `ctx.systemPrompt.section({name, order, text})` and get back the exact
|
|
6
|
+
// disposer. Faithful validation subset: duplicate names throw the dsh
|
|
7
|
+
// message, non-finite orders throw the dsh TypeError. Registrations mirror
|
|
8
|
+
// into the kernel's process-level `system-prompt` service — riding the F1
|
|
9
|
+
// effect ledger, so a hard-killed composition never leaves stale sections —
|
|
10
|
+
// and rebon's own prompt assembly (P2.8 registry) renders them inside its
|
|
11
|
+
// stable runtime-context plane, sorted by (order, name).
|
|
12
|
+
//
|
|
13
|
+
// Deliberately NOT provided (v1 honesty boundary): provider-function
|
|
14
|
+
// `text` (dsh resolves it per assembly with an AssembleContext we do not
|
|
15
|
+
// have — a static composition registers static text), `complete` sections
|
|
16
|
+
// (replacing rebon's own prompt belongs to the P3.12 loop seat, not a
|
|
17
|
+
// side door), `context()` / variables / tool-order surfaces. All fail
|
|
18
|
+
// loudly rather than silently degrade.
|
|
19
|
+
import { Service } from 'cordis';
|
|
20
|
+
import { callService } from 'rebon';
|
|
21
|
+
|
|
22
|
+
export default class RebonSystemPromptRuntime extends Service {
|
|
23
|
+
constructor(ctx) {
|
|
24
|
+
super(ctx, 'systemPrompt');
|
|
25
|
+
this.names = new Set();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Register one ordered prompt section; returns the exact disposer. */
|
|
29
|
+
section(entry) {
|
|
30
|
+
const name = entry?.name;
|
|
31
|
+
if (typeof name !== 'string' || name.trim().length === 0) {
|
|
32
|
+
throw new TypeError('systemPrompt.section: section.name must be a non-empty string');
|
|
33
|
+
}
|
|
34
|
+
if (!Number.isFinite(entry.order)) {
|
|
35
|
+
throw new TypeError(`prompt section "${name}" order must be a finite number`);
|
|
36
|
+
}
|
|
37
|
+
if (typeof entry.text === 'function') {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`prompt section "${name}": provider-function text is not supported in the embedded runtime (register static text)`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (typeof entry.text !== 'string') {
|
|
43
|
+
throw new TypeError(`prompt section "${name}" text must be a string`);
|
|
44
|
+
}
|
|
45
|
+
if (entry.complete) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`prompt section "${name}": complete-section replacement is not supported in the embedded runtime`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (this.names.has(name)) {
|
|
51
|
+
throw new Error(`prompt section "${name}" is already registered in this scope`);
|
|
52
|
+
}
|
|
53
|
+
const runtime = this;
|
|
54
|
+
// Caller-fork RAII through the Service proxy (llm/tools/web same
|
|
55
|
+
// pattern): plugin disposal unwinds the kernel mirror. Owner captured
|
|
56
|
+
// synchronously for ledger attribution (single-plugin unload sweep).
|
|
57
|
+
const owner = this.ctx[Symbol.for('rebon.composeOwner')] ?? '';
|
|
58
|
+
return this.ctx.effect(() => {
|
|
59
|
+
const registered = callService('system-prompt', 'register', {
|
|
60
|
+
name,
|
|
61
|
+
order: entry.order,
|
|
62
|
+
text: entry.text,
|
|
63
|
+
}, owner);
|
|
64
|
+
runtime.names.add(name);
|
|
65
|
+
const token = registered?.token;
|
|
66
|
+
return () => {
|
|
67
|
+
runtime.names.delete(name);
|
|
68
|
+
try {
|
|
69
|
+
callService('system-prompt', 'unregister', { name, token });
|
|
70
|
+
} catch {}
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|