@botlearn-course/daemon 0.0.13 → 0.0.15
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/README.md +5 -2
- package/dist/agent-service-sandbox.d.ts +15 -0
- package/dist/agent-service-sandbox.js +227 -21
- package/dist/agent-service-ws-protocol.d.ts +1 -1
- package/dist/agent-service-ws-protocol.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/log.d.ts +9 -0
- package/dist/log.js +24 -2
- package/dist/mcp/course-skills-relay.d.ts +2 -0
- package/dist/mcp/course-skills-relay.js +54 -0
- package/dist/mcp/course-skills-server.d.ts +49 -0
- package/dist/mcp/course-skills-server.js +439 -0
- package/dist/run-dispatcher.d.ts +2 -0
- package/dist/run-dispatcher.js +61 -2
- package/dist/runtime-capabilities.d.ts +1 -0
- package/dist/runtime-capabilities.js +4 -0
- package/dist/runtime-skills.d.ts +93 -0
- package/dist/runtime-skills.js +401 -0
- package/dist/runtimes/deepseek-tui.d.ts +2 -1
- package/dist/runtimes/deepseek-tui.js +252 -25
- package/dist/runtimes/engine.d.ts +6 -0
- package/dist/runtimes/engine.js +6 -1
- package/dist/runtimes/progress.d.ts +2 -0
- package/dist/runtimes/progress.js +34 -0
- package/dist/tool-observation.d.ts +21 -0
- package/dist/tool-observation.js +120 -0
- package/dist/types.d.ts +26 -2
- package/package.json +1 -1
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
2
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
3
|
+
const LOGICAL_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
|
|
4
|
+
const STRICT_SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*)?$/;
|
|
5
|
+
const RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
6
|
+
const CAPABILITY_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
|
|
7
|
+
const MAX_SKILLS = 32;
|
|
8
|
+
const MAX_CATALOG_DESCRIPTION_CODE_POINTS = 500;
|
|
9
|
+
const MAX_PROVIDER_RESPONSE_BYTES = 512 * 1024;
|
|
10
|
+
const MAX_LOADED_CONTENT_BYTES = 256 * 1024;
|
|
11
|
+
const MAX_BINDING_LIFETIME_MS = 24 * 60 * 60 * 1000;
|
|
12
|
+
export const COURSE_SKILLS_BINDING_SCHEMA = "course-skills-binding/0.1";
|
|
13
|
+
export const COURSE_SKILLS_PROTOCOL = "course-skills/0.1";
|
|
14
|
+
export const COURSE_SKILLS_CALL_SCHEMA = "course-skills-call/0.1";
|
|
15
|
+
export class RuntimeSkillProviderError extends Error {
|
|
16
|
+
code;
|
|
17
|
+
constructor(code, message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.name = "RuntimeSkillProviderError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function parseRuntimeSkillProviderBinding(raw, now = Date.now(), options = {}) {
|
|
24
|
+
const record = skillProviderBindingRecord(raw);
|
|
25
|
+
validateBindingProtocol(record);
|
|
26
|
+
const endpoint = providerEndpoint(record.endpoint, options.allowInsecureLoopbackForTests === true);
|
|
27
|
+
const bearerToken = requiredString(record.bearerToken, "bearerToken", 16, 4096);
|
|
28
|
+
const expiresAt = requiredString(record.expiresAt, "expiresAt", 20, 64);
|
|
29
|
+
const expiresAtMs = Date.parse(expiresAt);
|
|
30
|
+
if (!RFC3339_PATTERN.test(expiresAt) ||
|
|
31
|
+
!Number.isFinite(expiresAtMs)
|
|
32
|
+
|| expiresAtMs <= now
|
|
33
|
+
|| expiresAtMs > now + MAX_BINDING_LIFETIME_MS) {
|
|
34
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_expired", "Skill Provider binding expiry is invalid");
|
|
35
|
+
}
|
|
36
|
+
const effectiveSkillGrants = parseEffectiveSkillGrants(record.effectiveSkillGrants);
|
|
37
|
+
return {
|
|
38
|
+
schemaVersion: COURSE_SKILLS_BINDING_SCHEMA,
|
|
39
|
+
protocol: COURSE_SKILLS_PROTOCOL,
|
|
40
|
+
endpoint,
|
|
41
|
+
bearerToken,
|
|
42
|
+
expiresAt: new Date(expiresAtMs).toISOString(),
|
|
43
|
+
effectiveSkillGrants,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parse only the stable grant identity of a replayed binding.
|
|
48
|
+
*
|
|
49
|
+
* Credential values remain the first accepted activation snapshot, so replay validation
|
|
50
|
+
* deliberately checks their shape but not their endpoint/expiry semantics.
|
|
51
|
+
*/
|
|
52
|
+
export function parseRuntimeSkillProviderGrantSet(raw) {
|
|
53
|
+
const record = skillProviderBindingRecord(raw);
|
|
54
|
+
validateBindingProtocol(record);
|
|
55
|
+
requiredString(record.endpoint, "endpoint", 10, 2048);
|
|
56
|
+
requiredString(record.bearerToken, "bearerToken", 16, 4096);
|
|
57
|
+
requiredString(record.expiresAt, "expiresAt", 20, 64);
|
|
58
|
+
return parseEffectiveSkillGrants(record.effectiveSkillGrants);
|
|
59
|
+
}
|
|
60
|
+
export async function prepareRuntimeSkillProvider(rawBinding, options = {}) {
|
|
61
|
+
const now = options.now ?? Date.now;
|
|
62
|
+
const binding = parseRuntimeSkillProviderBinding(rawBinding, now(), {
|
|
63
|
+
allowInsecureLoopbackForTests: options.allowInsecureLoopbackForTests,
|
|
64
|
+
});
|
|
65
|
+
const provider = new HttpRuntimeSkillProvider(binding, {
|
|
66
|
+
fetchFn: options.fetchFn,
|
|
67
|
+
now,
|
|
68
|
+
});
|
|
69
|
+
const catalog = await provider.listEligibleSkills();
|
|
70
|
+
return { binding, provider, catalog };
|
|
71
|
+
}
|
|
72
|
+
export class HttpRuntimeSkillProvider {
|
|
73
|
+
binding;
|
|
74
|
+
fetchFn;
|
|
75
|
+
now;
|
|
76
|
+
grantsByRef;
|
|
77
|
+
constructor(binding, options = {}) {
|
|
78
|
+
this.binding = binding;
|
|
79
|
+
this.fetchFn = options.fetchFn ?? fetch;
|
|
80
|
+
this.now = options.now ?? Date.now;
|
|
81
|
+
this.grantsByRef = new Map(binding.effectiveSkillGrants.map((grant) => [grant.ref, grant]));
|
|
82
|
+
}
|
|
83
|
+
async listEligibleSkills() {
|
|
84
|
+
const envelope = await this.call("list", {});
|
|
85
|
+
const data = resultData(envelope, "skill_catalog_listed");
|
|
86
|
+
if (!Array.isArray(data.skills)) {
|
|
87
|
+
throw providerResponseError("Skill Provider catalog is invalid");
|
|
88
|
+
}
|
|
89
|
+
const catalog = data.skills.map(parseCatalogEntry);
|
|
90
|
+
if (catalog.length !== this.binding.effectiveSkillGrants.length) {
|
|
91
|
+
throw new RuntimeSkillProviderError("skill_provider_catalog_mismatch", "Skill Provider catalog does not match the activation grants");
|
|
92
|
+
}
|
|
93
|
+
for (let index = 0; index < catalog.length; index += 1) {
|
|
94
|
+
const expected = this.binding.effectiveSkillGrants[index];
|
|
95
|
+
const actual = catalog[index];
|
|
96
|
+
if (actual.assetVersionId !== expected.assetVersionId
|
|
97
|
+
|| actual.ref !== expected.ref
|
|
98
|
+
|| actual.digest !== expected.digest) {
|
|
99
|
+
throw new RuntimeSkillProviderError("skill_provider_catalog_mismatch", "Skill Provider catalog does not match the activation grants");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return catalog;
|
|
103
|
+
}
|
|
104
|
+
async loadSkill(ref) {
|
|
105
|
+
const grant = this.requireEligible(ref);
|
|
106
|
+
const envelope = await this.call("load", { ref });
|
|
107
|
+
const data = resultData(envelope, "skill_loaded");
|
|
108
|
+
return parseLoadedSkill(data, grant);
|
|
109
|
+
}
|
|
110
|
+
async loadReference(ref, referencePath) {
|
|
111
|
+
const grant = this.requireEligible(ref);
|
|
112
|
+
const safePath = safeReferencePath(referencePath);
|
|
113
|
+
const envelope = await this.call("load_reference", { ref, path: safePath });
|
|
114
|
+
const data = resultData(envelope, "skill_reference_loaded");
|
|
115
|
+
const loaded = parseLoadedSkill(data, grant);
|
|
116
|
+
if (data.path !== safePath) {
|
|
117
|
+
throw providerResponseError("Skill Provider reference path does not match the request");
|
|
118
|
+
}
|
|
119
|
+
return { ...loaded, path: safePath };
|
|
120
|
+
}
|
|
121
|
+
requireEligible(ref) {
|
|
122
|
+
const grant = this.grantsByRef.get(ref);
|
|
123
|
+
if (!grant) {
|
|
124
|
+
throw new RuntimeSkillProviderError("skill_not_eligible", "Skill is not eligible for the current activation");
|
|
125
|
+
}
|
|
126
|
+
return grant;
|
|
127
|
+
}
|
|
128
|
+
async call(operation, input) {
|
|
129
|
+
if (Date.parse(this.binding.expiresAt) <= this.now()) {
|
|
130
|
+
throw new RuntimeSkillProviderError("agent_run_activation_expired", "Skill Provider binding has expired");
|
|
131
|
+
}
|
|
132
|
+
let response;
|
|
133
|
+
try {
|
|
134
|
+
response = await this.fetchFn(this.binding.endpoint, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: {
|
|
137
|
+
authorization: `Bearer ${this.binding.bearerToken}`,
|
|
138
|
+
"content-type": "application/json",
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify({
|
|
141
|
+
schemaVersion: COURSE_SKILLS_CALL_SCHEMA,
|
|
142
|
+
operation,
|
|
143
|
+
...input,
|
|
144
|
+
}),
|
|
145
|
+
signal: AbortSignal.timeout(10_000),
|
|
146
|
+
redirect: "error",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
throw new RuntimeSkillProviderError("skill_provider_unavailable", "Skill Provider request failed");
|
|
151
|
+
}
|
|
152
|
+
const envelope = await readBoundedJson(response);
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
const code = safeErrorCode(envelope.code) ?? "skill_provider_unavailable";
|
|
155
|
+
throw new RuntimeSkillProviderError(code, "Skill Provider rejected the request");
|
|
156
|
+
}
|
|
157
|
+
return envelope;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function safeReferencePath(value) {
|
|
161
|
+
if (typeof value !== "string"
|
|
162
|
+
|| value.length < 1
|
|
163
|
+
|| Buffer.byteLength(value, "utf8") > 240
|
|
164
|
+
|| value.includes("\\")
|
|
165
|
+
|| value.includes("\0")
|
|
166
|
+
|| value.startsWith("/")
|
|
167
|
+
|| value.split("/").some((part) => part === "" || part === "." || part === "..")) {
|
|
168
|
+
throw new RuntimeSkillProviderError("skill_reference_invalid", "Skill reference path is invalid");
|
|
169
|
+
}
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
function parseGrant(raw) {
|
|
173
|
+
const record = exactRecord(raw, ["assetVersionId", "ref", "digest"], "Skill grant");
|
|
174
|
+
const assetVersionId = requiredString(record.assetVersionId, "assetVersionId", 36, 36);
|
|
175
|
+
const ref = requiredString(record.ref, "ref", 5, 240);
|
|
176
|
+
const digest = requiredString(record.digest, "digest", 71, 71);
|
|
177
|
+
const separator = ref.lastIndexOf("@");
|
|
178
|
+
const logicalId = separator > 0 ? ref.slice(0, separator) : "";
|
|
179
|
+
const version = separator > 0 ? ref.slice(separator + 1) : "";
|
|
180
|
+
if (!UUID_PATTERN.test(assetVersionId)
|
|
181
|
+
|| !LOGICAL_ID_PATTERN.test(logicalId)
|
|
182
|
+
|| !STRICT_SEMVER_PATTERN.test(version)
|
|
183
|
+
|| !SHA256_PATTERN.test(digest)) {
|
|
184
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill grant identity is invalid");
|
|
185
|
+
}
|
|
186
|
+
return { assetVersionId, ref, digest };
|
|
187
|
+
}
|
|
188
|
+
function parseCatalogEntry(raw) {
|
|
189
|
+
try {
|
|
190
|
+
return parseCatalogEntryValue(raw);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
if (error instanceof RuntimeSkillProviderError
|
|
194
|
+
&& error.code === "skill_provider_invalid_response") {
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
throw providerResponseError("Skill Provider catalog entry is invalid");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function parseCatalogEntryValue(raw) {
|
|
201
|
+
const record = exactRecord(raw, [
|
|
202
|
+
"assetVersionId",
|
|
203
|
+
"ref",
|
|
204
|
+
"digest",
|
|
205
|
+
"name",
|
|
206
|
+
"description",
|
|
207
|
+
"version",
|
|
208
|
+
"requiredCapabilities",
|
|
209
|
+
], "Skill catalog entry");
|
|
210
|
+
const grant = parseGrant({
|
|
211
|
+
assetVersionId: record.assetVersionId,
|
|
212
|
+
ref: record.ref,
|
|
213
|
+
digest: record.digest,
|
|
214
|
+
});
|
|
215
|
+
const name = requiredString(record.name, "name", 1, 160);
|
|
216
|
+
const description = requiredString(record.description, "description", 1, 2000);
|
|
217
|
+
const version = requiredString(record.version, "version", 5, 64);
|
|
218
|
+
if (Array.from(description).length > MAX_CATALOG_DESCRIPTION_CODE_POINTS) {
|
|
219
|
+
throw providerResponseError("Skill catalog description is too long");
|
|
220
|
+
}
|
|
221
|
+
if (!Array.isArray(record.requiredCapabilities) || record.requiredCapabilities.length > 32) {
|
|
222
|
+
throw providerResponseError("Skill catalog capabilities are invalid");
|
|
223
|
+
}
|
|
224
|
+
const requiredCapabilities = record.requiredCapabilities.map((value) => {
|
|
225
|
+
if (typeof value !== "string" || !CAPABILITY_PATTERN.test(value)) {
|
|
226
|
+
throw providerResponseError("Skill catalog capability is invalid");
|
|
227
|
+
}
|
|
228
|
+
return value;
|
|
229
|
+
});
|
|
230
|
+
if (new Set(requiredCapabilities).size !== requiredCapabilities.length
|
|
231
|
+
|| !isUtf8Sorted(requiredCapabilities)) {
|
|
232
|
+
throw providerResponseError("Skill catalog capabilities must be unique and sorted");
|
|
233
|
+
}
|
|
234
|
+
const refVersion = grant.ref.slice(grant.ref.lastIndexOf("@") + 1);
|
|
235
|
+
if (version !== refVersion) {
|
|
236
|
+
throw providerResponseError("Skill catalog version does not match ref");
|
|
237
|
+
}
|
|
238
|
+
return { ...grant, name, description, version, requiredCapabilities };
|
|
239
|
+
}
|
|
240
|
+
function parseLoadedSkill(data, grant) {
|
|
241
|
+
try {
|
|
242
|
+
const ref = requiredString(data.ref, "ref", 5, 240);
|
|
243
|
+
const digest = requiredString(data.digest, "digest", 71, 71);
|
|
244
|
+
const content = requiredString(data.content, "content", 1, MAX_LOADED_CONTENT_BYTES);
|
|
245
|
+
const bytes = data.bytes;
|
|
246
|
+
const truncated = data.truncated;
|
|
247
|
+
const actualBytes = Buffer.byteLength(content, "utf8");
|
|
248
|
+
if (ref !== grant.ref
|
|
249
|
+
|| digest !== grant.digest
|
|
250
|
+
|| !Number.isInteger(bytes)
|
|
251
|
+
|| bytes !== actualBytes
|
|
252
|
+
|| actualBytes > MAX_LOADED_CONTENT_BYTES
|
|
253
|
+
|| typeof truncated !== "boolean") {
|
|
254
|
+
throw providerResponseError("Skill Provider content response is invalid");
|
|
255
|
+
}
|
|
256
|
+
return { ref, digest, content, bytes: actualBytes, truncated };
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
if (error instanceof RuntimeSkillProviderError
|
|
260
|
+
&& error.code === "skill_provider_invalid_response") {
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
throw providerResponseError("Skill Provider content response is invalid");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function providerEndpoint(value, allowInsecureLoopback) {
|
|
267
|
+
const raw = requiredString(value, "endpoint", 10, 2048);
|
|
268
|
+
let parsed;
|
|
269
|
+
try {
|
|
270
|
+
parsed = new URL(raw);
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill Provider endpoint is invalid");
|
|
274
|
+
}
|
|
275
|
+
if (parsed.username || parsed.password || parsed.hash) {
|
|
276
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill Provider endpoint cannot contain credentials or a fragment");
|
|
277
|
+
}
|
|
278
|
+
const loopback = parsed.hostname === "127.0.0.1"
|
|
279
|
+
|| parsed.hostname === "::1"
|
|
280
|
+
|| parsed.hostname === "[::1]"
|
|
281
|
+
|| parsed.hostname === "localhost";
|
|
282
|
+
if (parsed.protocol !== "https:"
|
|
283
|
+
&& !(allowInsecureLoopback && parsed.protocol === "http:" && loopback)) {
|
|
284
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill Provider endpoint must use HTTPS");
|
|
285
|
+
}
|
|
286
|
+
return parsed.toString();
|
|
287
|
+
}
|
|
288
|
+
function skillProviderBindingRecord(raw) {
|
|
289
|
+
return exactRecord(raw, [
|
|
290
|
+
"schemaVersion",
|
|
291
|
+
"protocol",
|
|
292
|
+
"endpoint",
|
|
293
|
+
"bearerToken",
|
|
294
|
+
"expiresAt",
|
|
295
|
+
"effectiveSkillGrants",
|
|
296
|
+
], "Skill Provider binding");
|
|
297
|
+
}
|
|
298
|
+
function validateBindingProtocol(record) {
|
|
299
|
+
if (record.schemaVersion !== COURSE_SKILLS_BINDING_SCHEMA) {
|
|
300
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill Provider binding schemaVersion is unsupported");
|
|
301
|
+
}
|
|
302
|
+
if (record.protocol !== COURSE_SKILLS_PROTOCOL) {
|
|
303
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill Provider protocol is unsupported");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function parseEffectiveSkillGrants(raw) {
|
|
307
|
+
if (!Array.isArray(raw)) {
|
|
308
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill Provider grants must be an array");
|
|
309
|
+
}
|
|
310
|
+
if (raw.length < 1 || raw.length > MAX_SKILLS) {
|
|
311
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", `Skill Provider grants must contain 1 to ${MAX_SKILLS} entries`);
|
|
312
|
+
}
|
|
313
|
+
const grants = raw.map(parseGrant);
|
|
314
|
+
const refs = grants.map((grant) => grant.ref);
|
|
315
|
+
if (new Set(refs).size !== refs.length) {
|
|
316
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill Provider grants contain duplicate refs");
|
|
317
|
+
}
|
|
318
|
+
if (!isUtf8Sorted(refs)) {
|
|
319
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", "Skill Provider grants must be sorted by ref UTF-8 bytes");
|
|
320
|
+
}
|
|
321
|
+
return grants;
|
|
322
|
+
}
|
|
323
|
+
async function readBoundedJson(response) {
|
|
324
|
+
const declared = Number(response.headers.get("content-length") ?? 0);
|
|
325
|
+
if (Number.isFinite(declared) && declared > MAX_PROVIDER_RESPONSE_BYTES) {
|
|
326
|
+
throw providerResponseError("Skill Provider response is too large");
|
|
327
|
+
}
|
|
328
|
+
if (!response.body)
|
|
329
|
+
throw providerResponseError("Skill Provider response has no body");
|
|
330
|
+
const reader = response.body.getReader();
|
|
331
|
+
const chunks = [];
|
|
332
|
+
let size = 0;
|
|
333
|
+
while (true) {
|
|
334
|
+
const { value, done } = await reader.read();
|
|
335
|
+
if (done)
|
|
336
|
+
break;
|
|
337
|
+
if (!value)
|
|
338
|
+
continue;
|
|
339
|
+
size += value.byteLength;
|
|
340
|
+
if (size > MAX_PROVIDER_RESPONSE_BYTES) {
|
|
341
|
+
await reader.cancel().catch(() => undefined);
|
|
342
|
+
throw providerResponseError("Skill Provider response is too large");
|
|
343
|
+
}
|
|
344
|
+
chunks.push(value);
|
|
345
|
+
}
|
|
346
|
+
const bytes = Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));
|
|
347
|
+
try {
|
|
348
|
+
const parsed = JSON.parse(bytes.toString("utf8"));
|
|
349
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
350
|
+
throw new Error("response is not an object");
|
|
351
|
+
}
|
|
352
|
+
return parsed;
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
throw providerResponseError("Skill Provider response is not valid JSON");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function resultData(envelope, expectedCode) {
|
|
359
|
+
if (envelope.schemaVersion !== "tool-result/0.1"
|
|
360
|
+
|| envelope.ok !== true
|
|
361
|
+
|| envelope.code !== expectedCode
|
|
362
|
+
|| !envelope.data
|
|
363
|
+
|| typeof envelope.data !== "object"
|
|
364
|
+
|| Array.isArray(envelope.data)) {
|
|
365
|
+
const code = safeErrorCode(envelope.code);
|
|
366
|
+
if (envelope.ok === false && code) {
|
|
367
|
+
throw new RuntimeSkillProviderError(code, "Skill Provider returned an error");
|
|
368
|
+
}
|
|
369
|
+
throw providerResponseError("Skill Provider result envelope is invalid");
|
|
370
|
+
}
|
|
371
|
+
return envelope.data;
|
|
372
|
+
}
|
|
373
|
+
function exactRecord(raw, allowedKeys, label) {
|
|
374
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
375
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", `${label} must be an object`);
|
|
376
|
+
}
|
|
377
|
+
const record = raw;
|
|
378
|
+
const allowed = new Set(allowedKeys);
|
|
379
|
+
if (Object.keys(record).some((key) => !allowed.has(key))) {
|
|
380
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", `${label} contains unsupported fields`);
|
|
381
|
+
}
|
|
382
|
+
return record;
|
|
383
|
+
}
|
|
384
|
+
function requiredString(value, field, minLength, maxLength) {
|
|
385
|
+
if (typeof value !== "string" || value.length < minLength || value.length > maxLength) {
|
|
386
|
+
throw new RuntimeSkillProviderError("skill_provider_binding_invalid", `Skill Provider ${field} is invalid`);
|
|
387
|
+
}
|
|
388
|
+
return value;
|
|
389
|
+
}
|
|
390
|
+
function isUtf8Sorted(values) {
|
|
391
|
+
return values.every((value, index) => index === 0
|
|
392
|
+
|| Buffer.compare(Buffer.from(values[index - 1], "utf8"), Buffer.from(value, "utf8")) <= 0);
|
|
393
|
+
}
|
|
394
|
+
function safeErrorCode(value) {
|
|
395
|
+
return typeof value === "string" && /^[a-z][a-z0-9_]{2,79}$/.test(value)
|
|
396
|
+
? value
|
|
397
|
+
: null;
|
|
398
|
+
}
|
|
399
|
+
function providerResponseError(message) {
|
|
400
|
+
return new RuntimeSkillProviderError("skill_provider_invalid_response", message);
|
|
401
|
+
}
|
|
@@ -50,8 +50,9 @@ export declare class DeepseekTuiAdapter implements EngineAdapter {
|
|
|
50
50
|
private clearCompactionRequired;
|
|
51
51
|
private managedActivationId;
|
|
52
52
|
private createThread;
|
|
53
|
-
private
|
|
53
|
+
private patchThreadSettings;
|
|
54
54
|
private compactThread;
|
|
55
|
+
private runtimeSystemContext;
|
|
55
56
|
private startTurnAndReadEvents;
|
|
56
57
|
private interruptTurn;
|
|
57
58
|
private readEvents;
|