@frockbot/kernel-contracts 0.1.3 → 0.2.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/package.json +1 -1
- package/src/authoring.ts +64 -0
- package/src/catalog-change.test.ts +56 -0
- package/src/iframe-ui.test.ts +99 -0
- package/src/iframe-ui.ts +400 -0
- package/src/index.ts +3 -0
- package/src/isolate-context-catalog.generated.ts +25 -0
- package/src/isolate.test.ts +154 -43
- package/src/isolate.ts +689 -101
- package/src/loop-events.test.ts +80 -0
- package/src/loop-events.ts +711 -0
- package/src/send-to-user.ts +7 -56
- package/src/session.test.ts +16 -0
- package/src/tool-execution.ts +16 -0
- package/src/types.ts +229 -0
package/src/isolate.ts
CHANGED
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
// Everything decoded here is untrusted: Bot-authored code produces the results
|
|
20
20
|
// and the capability requests, and the isolate produces the health report.
|
|
21
21
|
import type { TurnAdmissionV1 } from "./tool-execution.js";
|
|
22
|
+
import {
|
|
23
|
+
BOT_ISOLATE_HOOK_EVENTS_V1,
|
|
24
|
+
isBotIsolateHookEventNameV1,
|
|
25
|
+
type BotIsolateHookEventNameV1,
|
|
26
|
+
type LoopEventPayloadMapV1,
|
|
27
|
+
} from "./loop-events.js";
|
|
22
28
|
import {
|
|
23
29
|
decodeTurnTypeV1,
|
|
24
30
|
type LlmStreamEvent,
|
|
@@ -28,21 +34,20 @@ import {
|
|
|
28
34
|
|
|
29
35
|
/**
|
|
30
36
|
* The wire contract version the kernel wrapper emits. Version 2 added
|
|
31
|
-
* per-tool turn admission
|
|
32
|
-
* tools are therefore offered on every turn type.
|
|
37
|
+
* per-tool turn admission. Version 3 added declared loop hooks and hook RPC.
|
|
33
38
|
*/
|
|
34
|
-
export const ISOLATE_CONTRACT_VERSION =
|
|
39
|
+
export const ISOLATE_CONTRACT_VERSION = 3;
|
|
35
40
|
|
|
36
41
|
/** Every contract version the kernel still decodes. */
|
|
37
|
-
export type IsolateContractVersion = 1 | 2;
|
|
42
|
+
export type IsolateContractVersion = 1 | 2 | 3;
|
|
38
43
|
|
|
39
|
-
const ISOLATE_CONTRACT_VERSIONS: readonly IsolateContractVersion[] = [1, 2];
|
|
44
|
+
const ISOLATE_CONTRACT_VERSIONS: readonly IsolateContractVersion[] = [1, 2, 3];
|
|
40
45
|
|
|
41
46
|
/** The upper bound on a single isolate invocation, enforced on both sides. */
|
|
42
47
|
export const ISOLATE_MAX_DEADLINE_MS = 60_000;
|
|
43
48
|
|
|
44
49
|
const MAX_ISOLATE_TOOLS = 64;
|
|
45
|
-
const
|
|
50
|
+
const MAX_ISOLATE_CONNECTIONS = 100;
|
|
46
51
|
const MAX_ISOLATE_CONTENT = 1_000_000;
|
|
47
52
|
const TOOL_NAME = /^[a-z][a-z0-9_]{0,63}$/;
|
|
48
53
|
|
|
@@ -73,12 +78,32 @@ export interface IsolateToolResultV1 {
|
|
|
73
78
|
isError: boolean;
|
|
74
79
|
}
|
|
75
80
|
|
|
81
|
+
export interface IsolateHookInvocationV1<
|
|
82
|
+
Event extends BotIsolateHookEventNameV1 = BotIsolateHookEventNameV1,
|
|
83
|
+
> {
|
|
84
|
+
schemaVersion: 1;
|
|
85
|
+
event: Event;
|
|
86
|
+
payload: LoopEventPayloadMapV1[Event];
|
|
87
|
+
botId: string;
|
|
88
|
+
sessionId: string;
|
|
89
|
+
runId: string;
|
|
90
|
+
turnId: string;
|
|
91
|
+
generationId: string;
|
|
92
|
+
deadlineMs: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type IsolateHookResultV1 =
|
|
96
|
+
| { schemaVersion: 1; status: "unchanged" }
|
|
97
|
+
| { schemaVersion: 1; status: "replaced"; replacement: unknown };
|
|
98
|
+
|
|
76
99
|
export interface IsolateHealthV1 {
|
|
77
100
|
schemaVersion: 1;
|
|
78
101
|
ok: boolean;
|
|
79
102
|
packageId: string;
|
|
80
103
|
contractVersion: IsolateContractVersion;
|
|
81
104
|
tools: IsolateToolDescriptorV1[];
|
|
105
|
+
/** Contract version 3 and later. */
|
|
106
|
+
hooks?: BotIsolateHookEventNameV1[];
|
|
82
107
|
}
|
|
83
108
|
|
|
84
109
|
/** What `IDENTITY` carries into the isolate. Structured-clonable, never a stub. */
|
|
@@ -88,25 +113,6 @@ export interface IsolateIdentityV1 {
|
|
|
88
113
|
packageId: string;
|
|
89
114
|
}
|
|
90
115
|
|
|
91
|
-
export type IsolateCapabilityKindV1 =
|
|
92
|
-
"tool" | "model" | "memory" | "notification" | "computer";
|
|
93
|
-
|
|
94
|
-
export interface IsolateCapabilityDescriptorV1 {
|
|
95
|
-
capabilityId: string;
|
|
96
|
-
kind: IsolateCapabilityKindV1;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface IsolateAuthorityRequestV1 {
|
|
100
|
-
capabilityId: string;
|
|
101
|
-
reason: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Self-modification never widens authority: the answer is always pending. */
|
|
105
|
-
export interface IsolatePendingDecisionV1 {
|
|
106
|
-
status: "pending-user-decision";
|
|
107
|
-
decisionId: string;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
116
|
/**
|
|
111
117
|
* A capability call the authority could not serve. It is a declared variant,
|
|
112
118
|
* not an exception: an error thrown across the loopback binding would surface
|
|
@@ -118,14 +124,121 @@ export interface IsolateCapabilityFailureV1 {
|
|
|
118
124
|
reason: string;
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
/** One safe Connection projection. Credentials and credential references never cross. */
|
|
128
|
+
export interface IsolateConnectionV1 {
|
|
129
|
+
connectionId: string;
|
|
130
|
+
packageId: string;
|
|
131
|
+
connectionTypeId: string;
|
|
132
|
+
displayName: string;
|
|
133
|
+
generation: string;
|
|
134
|
+
safeMetadata: Record<string, unknown>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The Bot's configured model, resolved through one of its ready Connections. */
|
|
138
|
+
export interface IsolateModelBindingV1 {
|
|
139
|
+
connectionId: string;
|
|
140
|
+
packageId: string;
|
|
141
|
+
provider: string;
|
|
142
|
+
providerModelId: string;
|
|
143
|
+
connectionGeneration: string;
|
|
144
|
+
catalogGeneration?: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** The per-Bot authority every Package in one Composition sees identically. */
|
|
148
|
+
export interface IsolateCapabilityListV1 {
|
|
149
|
+
status: "available";
|
|
150
|
+
connections: IsolateConnectionV1[];
|
|
151
|
+
model?: IsolateModelBindingV1;
|
|
152
|
+
tools: true;
|
|
153
|
+
memory: boolean;
|
|
154
|
+
workspace: boolean;
|
|
155
|
+
notify: true;
|
|
156
|
+
schedule: true;
|
|
157
|
+
}
|
|
123
158
|
|
|
124
159
|
export type IsolateCapabilityListOutcomeV1 =
|
|
125
|
-
|
|
160
|
+
IsolateCapabilityListV1 | IsolateCapabilityFailureV1;
|
|
161
|
+
|
|
162
|
+
/** An opaque, short-lived reference to a Connection — never its credential. */
|
|
163
|
+
export interface IsolateConnectionLeaseV1 {
|
|
164
|
+
status: "available";
|
|
165
|
+
leaseId: string;
|
|
166
|
+
connectionId: string;
|
|
167
|
+
generation: string;
|
|
168
|
+
expiresAt: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export type IsolateConnectionOutcomeV1 =
|
|
172
|
+
IsolateConnectionLeaseV1 | IsolateCapabilityFailureV1;
|
|
173
|
+
|
|
174
|
+
export interface IsolateToolRequestV1 {
|
|
175
|
+
callId: string;
|
|
176
|
+
name: string;
|
|
177
|
+
input: unknown;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export type IsolateToolOutcomeV1 =
|
|
181
|
+
| { status: "completed"; content: string; isError: boolean }
|
|
182
|
+
| IsolateCapabilityFailureV1;
|
|
183
|
+
|
|
184
|
+
export type IsolateMemoryScopeV1 = "bot" | "user" | "project";
|
|
185
|
+
export type IsolateMemoryTierV1 = "profile" | "log" | "note";
|
|
186
|
+
export interface IsolateMemoryReadRequestV1 {
|
|
187
|
+
scope: IsolateMemoryScopeV1;
|
|
188
|
+
projectId?: string;
|
|
189
|
+
}
|
|
190
|
+
export interface IsolateMemoryWriteRequestV1 extends IsolateMemoryReadRequestV1 {
|
|
191
|
+
tier?: IsolateMemoryTierV1;
|
|
192
|
+
fact: string;
|
|
193
|
+
}
|
|
194
|
+
export type IsolateMemoryOutcomeV1 =
|
|
195
|
+
{ status: "available"; value: unknown } | IsolateCapabilityFailureV1;
|
|
196
|
+
|
|
197
|
+
/** A root selector without a User id; the authority supplies its own User. */
|
|
198
|
+
export type IsolateWorkspaceRootV1 =
|
|
199
|
+
| { kind: "bot-instructions"; botId: string }
|
|
200
|
+
| { kind: "user-instructions" }
|
|
201
|
+
| { kind: "package-declared"; packageId: string; rootId: string };
|
|
202
|
+
export interface IsolateWorkspacePathV1 {
|
|
203
|
+
root: IsolateWorkspaceRootV1;
|
|
204
|
+
path: string;
|
|
205
|
+
}
|
|
206
|
+
export interface IsolateWorkspaceListRequestV1 {
|
|
207
|
+
root: IsolateWorkspaceRootV1;
|
|
208
|
+
prefix?: string;
|
|
209
|
+
cursor?: string;
|
|
210
|
+
limit?: number;
|
|
211
|
+
}
|
|
212
|
+
export interface IsolateWorkspaceWriteRequestV1 {
|
|
213
|
+
path: IsolateWorkspacePathV1;
|
|
214
|
+
bytes: Uint8Array;
|
|
215
|
+
expectedGenerationId: string | null;
|
|
216
|
+
mediaType?: string;
|
|
217
|
+
}
|
|
218
|
+
export interface IsolateWorkspaceDeleteRequestV1 {
|
|
219
|
+
path: IsolateWorkspacePathV1;
|
|
220
|
+
expectedGenerationId: string;
|
|
221
|
+
}
|
|
222
|
+
export type IsolateWorkspaceOutcomeV1 =
|
|
223
|
+
{ status: "available"; value: unknown } | IsolateCapabilityFailureV1;
|
|
224
|
+
|
|
225
|
+
export interface IsolateNotificationRequestV1 {
|
|
226
|
+
notificationId: string;
|
|
227
|
+
title: string;
|
|
228
|
+
body: string;
|
|
229
|
+
}
|
|
230
|
+
export type IsolateNotificationOutcomeV1 =
|
|
231
|
+
{ status: "recorded" } | IsolateCapabilityFailureV1;
|
|
232
|
+
|
|
233
|
+
/** A durable Routine operation attributed to one Package call. */
|
|
234
|
+
export interface IsolateScheduleRequestV1 {
|
|
235
|
+
callId: string;
|
|
236
|
+
input: unknown;
|
|
237
|
+
}
|
|
238
|
+
export type IsolateScheduleOutcomeV1 = IsolateToolOutcomeV1;
|
|
126
239
|
|
|
127
240
|
/**
|
|
128
|
-
*
|
|
241
|
+
* Model invocation through the Bot's configured model binding. Events cross the RPC
|
|
129
242
|
* boundary as an NDJSON byte stream — see `decodeIsolateModelEventV1`. A
|
|
130
243
|
* `ReadableStream` of JavaScript objects is not transferable over workerd RPC;
|
|
131
244
|
* a byte stream is, so the kernel encodes and the isolate decodes.
|
|
@@ -136,10 +249,9 @@ export type IsolateModelInvocationV1 =
|
|
|
136
249
|
requestId: string;
|
|
137
250
|
events: ReadableStream<Uint8Array>;
|
|
138
251
|
}
|
|
139
|
-
|
|
|
252
|
+
| IsolateCapabilityFailureV1;
|
|
140
253
|
|
|
141
|
-
export type IsolateModelOutcomeV1 =
|
|
142
|
-
IsolateModelInvocationV1 | IsolateCapabilityFailureV1;
|
|
254
|
+
export type IsolateModelOutcomeV1 = IsolateModelInvocationV1;
|
|
143
255
|
|
|
144
256
|
/**
|
|
145
257
|
* The wrapper `WorkerEntrypoint` the kernel generates. Bot code never
|
|
@@ -148,25 +260,126 @@ export type IsolateModelOutcomeV1 =
|
|
|
148
260
|
export interface BotIsolateEntrypoint {
|
|
149
261
|
health(): Promise<IsolateHealthV1>;
|
|
150
262
|
execute(invocation: IsolateToolInvocationV1): Promise<IsolateToolResultV1>;
|
|
263
|
+
hook(invocation: IsolateHookInvocationV1): Promise<IsolateHookResultV1>;
|
|
151
264
|
}
|
|
152
265
|
|
|
153
266
|
/**
|
|
154
267
|
* The loopback service binding the Bot's Durable Object mints for one isolate.
|
|
155
|
-
* Every method is
|
|
156
|
-
* Bot does not already hold.
|
|
268
|
+
* Every method is Bot-authority-derived: nothing here can hand out authority
|
|
269
|
+
* the Bot does not already hold.
|
|
157
270
|
*/
|
|
158
271
|
export interface BotCapabilitiesStub {
|
|
159
272
|
list(): Promise<IsolateCapabilityListOutcomeV1>;
|
|
160
|
-
/**
|
|
161
|
-
* D6 addendum. The kernel records the normalized request and acquires the
|
|
162
|
-
* credential lease through the existing provider path *before* forwarding.
|
|
163
|
-
* Without a matching enabled model Assignment the answer is a pending
|
|
164
|
-
* decision, never a grant.
|
|
165
|
-
*/
|
|
166
273
|
invokeModel(request: NormalizedModelRequest): Promise<IsolateModelOutcomeV1>;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
274
|
+
invokeTool(request: IsolateToolRequestV1): Promise<IsolateToolOutcomeV1>;
|
|
275
|
+
memoryRead(
|
|
276
|
+
request: IsolateMemoryReadRequestV1,
|
|
277
|
+
): Promise<IsolateMemoryOutcomeV1>;
|
|
278
|
+
memoryWrite(
|
|
279
|
+
request: IsolateMemoryWriteRequestV1,
|
|
280
|
+
): Promise<IsolateMemoryOutcomeV1>;
|
|
281
|
+
memoryForget(
|
|
282
|
+
request: IsolateMemoryWriteRequestV1,
|
|
283
|
+
): Promise<IsolateMemoryOutcomeV1>;
|
|
284
|
+
workspaceRead(
|
|
285
|
+
path: IsolateWorkspacePathV1,
|
|
286
|
+
): Promise<IsolateWorkspaceOutcomeV1>;
|
|
287
|
+
workspaceList(
|
|
288
|
+
request: IsolateWorkspaceListRequestV1,
|
|
289
|
+
): Promise<IsolateWorkspaceOutcomeV1>;
|
|
290
|
+
workspaceStat(
|
|
291
|
+
path: IsolateWorkspacePathV1,
|
|
292
|
+
): Promise<IsolateWorkspaceOutcomeV1>;
|
|
293
|
+
workspaceWrite(
|
|
294
|
+
request: IsolateWorkspaceWriteRequestV1,
|
|
295
|
+
): Promise<IsolateWorkspaceOutcomeV1>;
|
|
296
|
+
workspaceDelete(
|
|
297
|
+
request: IsolateWorkspaceDeleteRequestV1,
|
|
298
|
+
): Promise<IsolateWorkspaceOutcomeV1>;
|
|
299
|
+
connection(connectionId: string): Promise<IsolateConnectionOutcomeV1>;
|
|
300
|
+
notify(
|
|
301
|
+
request: IsolateNotificationRequestV1,
|
|
302
|
+
): Promise<IsolateNotificationOutcomeV1>;
|
|
303
|
+
schedule(
|
|
304
|
+
request: IsolateScheduleRequestV1,
|
|
305
|
+
): Promise<IsolateScheduleOutcomeV1>;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** The model outcome Bot-authored `package.js` receives after wrapper narrowing. */
|
|
309
|
+
export type BotPackageModelOutcomeV1 =
|
|
310
|
+
| {
|
|
311
|
+
status: "streaming";
|
|
312
|
+
requestId: string;
|
|
313
|
+
events: AsyncIterable<LlmStreamEvent>;
|
|
314
|
+
}
|
|
315
|
+
| IsolateCapabilityFailureV1;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* The common exact `ctx` passed to a Bot-authored Package's tool and hook.
|
|
319
|
+
* The model-facing declarations are generated from these interfaces, and the
|
|
320
|
+
* wrapper's implementation is compile- and test-checked against the same
|
|
321
|
+
* keys.
|
|
322
|
+
*/
|
|
323
|
+
export interface BotPackageContextV1 {
|
|
324
|
+
readonly tool?: string;
|
|
325
|
+
readonly event?: BotIsolateHookEventNameV1;
|
|
326
|
+
readonly botId: string;
|
|
327
|
+
readonly sessionId: string;
|
|
328
|
+
readonly runId: string;
|
|
329
|
+
readonly turnId: string;
|
|
330
|
+
readonly generationId: string;
|
|
331
|
+
readonly packageId: string;
|
|
332
|
+
readonly deadlineMs: number;
|
|
333
|
+
readonly bindings: string[];
|
|
334
|
+
readonly capabilities: {
|
|
335
|
+
list(): Promise<IsolateCapabilityListOutcomeV1>;
|
|
336
|
+
};
|
|
337
|
+
readonly model: {
|
|
338
|
+
invoke(request: NormalizedModelRequest): Promise<BotPackageModelOutcomeV1>;
|
|
339
|
+
};
|
|
340
|
+
readonly tools: {
|
|
341
|
+
/** Runs through the trusted registry's active-Composition and deny guards. */
|
|
342
|
+
invoke(request: IsolateToolRequestV1): Promise<IsolateToolOutcomeV1>;
|
|
343
|
+
};
|
|
344
|
+
readonly memory: {
|
|
345
|
+
read(request: IsolateMemoryReadRequestV1): Promise<IsolateMemoryOutcomeV1>;
|
|
346
|
+
write(
|
|
347
|
+
request: IsolateMemoryWriteRequestV1,
|
|
348
|
+
): Promise<IsolateMemoryOutcomeV1>;
|
|
349
|
+
forget(
|
|
350
|
+
request: IsolateMemoryWriteRequestV1,
|
|
351
|
+
): Promise<IsolateMemoryOutcomeV1>;
|
|
352
|
+
};
|
|
353
|
+
readonly workspace: {
|
|
354
|
+
read(path: IsolateWorkspacePathV1): Promise<IsolateWorkspaceOutcomeV1>;
|
|
355
|
+
list(
|
|
356
|
+
request: IsolateWorkspaceListRequestV1,
|
|
357
|
+
): Promise<IsolateWorkspaceOutcomeV1>;
|
|
358
|
+
stat(path: IsolateWorkspacePathV1): Promise<IsolateWorkspaceOutcomeV1>;
|
|
359
|
+
write(
|
|
360
|
+
request: IsolateWorkspaceWriteRequestV1,
|
|
361
|
+
): Promise<IsolateWorkspaceOutcomeV1>;
|
|
362
|
+
delete(
|
|
363
|
+
request: IsolateWorkspaceDeleteRequestV1,
|
|
364
|
+
): Promise<IsolateWorkspaceOutcomeV1>;
|
|
365
|
+
};
|
|
366
|
+
connection(connectionId: string): Promise<IsolateConnectionOutcomeV1>;
|
|
367
|
+
notify(
|
|
368
|
+
request: IsolateNotificationRequestV1,
|
|
369
|
+
): Promise<IsolateNotificationOutcomeV1>;
|
|
370
|
+
schedule(
|
|
371
|
+
request: IsolateScheduleRequestV1,
|
|
372
|
+
): Promise<IsolateScheduleOutcomeV1>;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export interface BotPackageExecutionContextV1 extends BotPackageContextV1 {
|
|
376
|
+
readonly tool: string;
|
|
377
|
+
readonly event?: never;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export interface BotPackageHookContextV1 extends BotPackageContextV1 {
|
|
381
|
+
readonly tool?: never;
|
|
382
|
+
readonly event: BotIsolateHookEventNameV1;
|
|
170
383
|
}
|
|
171
384
|
|
|
172
385
|
/**
|
|
@@ -198,30 +411,25 @@ export interface IsolateHost {
|
|
|
198
411
|
|
|
199
412
|
/**
|
|
200
413
|
* D2. The loader identity, and nothing else — a reused id silently serves the
|
|
201
|
-
* first code
|
|
202
|
-
* covers the
|
|
203
|
-
*
|
|
414
|
+
* first code and `env`, so every component here is content- or owner-derived.
|
|
415
|
+
* The hash covers the mounted wrapper and Package artifact plus the digest of
|
|
416
|
+
* every baked-in binding: User, Bot, Composition generation, Connections, and
|
|
417
|
+
* resolved model. The User prefix independently prevents cross-User reuse.
|
|
204
418
|
*/
|
|
205
419
|
export function isolateLoaderIdV1(input: {
|
|
206
420
|
userId: string;
|
|
207
|
-
botId: string;
|
|
208
421
|
artifactSetHash: string;
|
|
209
422
|
}): string {
|
|
210
423
|
const userId = boundedString(input.userId, "isolate loader userId", 256);
|
|
211
|
-
const botId = boundedString(input.botId, "isolate loader botId", 256);
|
|
212
424
|
const hash = boundedString(
|
|
213
425
|
input.artifactSetHash,
|
|
214
426
|
"isolate loader artifactSetHash",
|
|
215
427
|
128,
|
|
216
428
|
);
|
|
217
|
-
if (
|
|
218
|
-
/[:\s]/.test(userId) ||
|
|
219
|
-
/[:\s]/.test(botId) ||
|
|
220
|
-
!/^[0-9a-f]+$/.test(hash)
|
|
221
|
-
) {
|
|
429
|
+
if (/[:\s]/.test(userId) || !/^[0-9a-f]+$/.test(hash)) {
|
|
222
430
|
throw new Error("isolate loader id components are invalid");
|
|
223
431
|
}
|
|
224
|
-
return `bot-package:${userId}:${
|
|
432
|
+
return `bot-package:${userId}:${hash}`;
|
|
225
433
|
}
|
|
226
434
|
|
|
227
435
|
function record(value: unknown, label: string): Record<string, unknown> {
|
|
@@ -460,25 +668,114 @@ export function decodeIsolateToolResultV1(
|
|
|
460
668
|
};
|
|
461
669
|
}
|
|
462
670
|
|
|
463
|
-
export function
|
|
671
|
+
export function decodeIsolateHookInvocationV1(
|
|
464
672
|
input: unknown,
|
|
465
|
-
label = "isolate
|
|
466
|
-
):
|
|
673
|
+
label = "isolate hook invocation",
|
|
674
|
+
): IsolateHookInvocationV1 {
|
|
467
675
|
const value = record(input, label);
|
|
468
676
|
exactKeys(
|
|
469
677
|
value,
|
|
470
|
-
[
|
|
678
|
+
[
|
|
679
|
+
"schemaVersion",
|
|
680
|
+
"event",
|
|
681
|
+
"payload",
|
|
682
|
+
"botId",
|
|
683
|
+
"sessionId",
|
|
684
|
+
"runId",
|
|
685
|
+
"turnId",
|
|
686
|
+
"generationId",
|
|
687
|
+
"deadlineMs",
|
|
688
|
+
],
|
|
471
689
|
label,
|
|
472
690
|
);
|
|
473
691
|
if (value.schemaVersion !== 1) {
|
|
474
692
|
throw new Error(`${label}.schemaVersion is unsupported`);
|
|
475
693
|
}
|
|
694
|
+
if (!isBotIsolateHookEventNameV1(value.event)) {
|
|
695
|
+
throw new Error(`${label}.event is invalid`);
|
|
696
|
+
}
|
|
697
|
+
// The host authored the event snapshot. The event-specific replacement is
|
|
698
|
+
// decoded on the return seam; here JSON validation prevents a live object
|
|
699
|
+
// from being smuggled into Bot code by a future host caller.
|
|
700
|
+
jsonValue(value.payload, `${label}.payload`);
|
|
701
|
+
const deadlineMs = value.deadlineMs;
|
|
702
|
+
if (
|
|
703
|
+
!Number.isSafeInteger(deadlineMs) ||
|
|
704
|
+
(deadlineMs as number) <= 0 ||
|
|
705
|
+
(deadlineMs as number) > ISOLATE_MAX_DEADLINE_MS
|
|
706
|
+
) {
|
|
707
|
+
throw new Error(`${label}.deadlineMs is out of range`);
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
schemaVersion: 1,
|
|
711
|
+
event: value.event,
|
|
712
|
+
payload: value.payload as LoopEventPayloadMapV1[BotIsolateHookEventNameV1],
|
|
713
|
+
botId: boundedString(value.botId, `${label}.botId`, 256),
|
|
714
|
+
sessionId: boundedString(value.sessionId, `${label}.sessionId`, 257),
|
|
715
|
+
runId: boundedString(value.runId, `${label}.runId`, 128),
|
|
716
|
+
turnId: boundedString(value.turnId, `${label}.turnId`, 128),
|
|
717
|
+
generationId: boundedString(
|
|
718
|
+
value.generationId,
|
|
719
|
+
`${label}.generationId`,
|
|
720
|
+
256,
|
|
721
|
+
),
|
|
722
|
+
deadlineMs: deadlineMs as number,
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
export function decodeIsolateHookResultV1(
|
|
727
|
+
input: unknown,
|
|
728
|
+
label = "isolate hook result",
|
|
729
|
+
): IsolateHookResultV1 {
|
|
730
|
+
const value = record(input, label);
|
|
731
|
+
if (value.status === "unchanged") {
|
|
732
|
+
exactKeys(value, ["schemaVersion", "status"], label);
|
|
733
|
+
if (value.schemaVersion !== 1) {
|
|
734
|
+
throw new Error(`${label}.schemaVersion is unsupported`);
|
|
735
|
+
}
|
|
736
|
+
return { schemaVersion: 1, status: "unchanged" };
|
|
737
|
+
}
|
|
738
|
+
exactKeys(value, ["schemaVersion", "status", "replacement"], label);
|
|
739
|
+
if (value.schemaVersion !== 1) {
|
|
740
|
+
throw new Error(`${label}.schemaVersion is unsupported`);
|
|
741
|
+
}
|
|
742
|
+
if (value.status !== "replaced") {
|
|
743
|
+
throw new Error(`${label}.status is invalid`);
|
|
744
|
+
}
|
|
745
|
+
jsonValue(value.replacement, `${label}.replacement`);
|
|
746
|
+
return {
|
|
747
|
+
schemaVersion: 1,
|
|
748
|
+
status: "replaced",
|
|
749
|
+
replacement: value.replacement,
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
export function decodeIsolateHealthV1(
|
|
754
|
+
input: unknown,
|
|
755
|
+
label = "isolate health",
|
|
756
|
+
): IsolateHealthV1 {
|
|
757
|
+
const value = record(input, label);
|
|
758
|
+
if (value.schemaVersion !== 1) {
|
|
759
|
+
throw new Error(`${label}.schemaVersion is unsupported`);
|
|
760
|
+
}
|
|
476
761
|
const contractVersion = ISOLATE_CONTRACT_VERSIONS.find(
|
|
477
762
|
(candidate) => candidate === value.contractVersion,
|
|
478
763
|
);
|
|
479
764
|
if (contractVersion === undefined) {
|
|
480
765
|
throw new Error(`${label}.contractVersion is unsupported`);
|
|
481
766
|
}
|
|
767
|
+
exactKeys(
|
|
768
|
+
value,
|
|
769
|
+
[
|
|
770
|
+
"schemaVersion",
|
|
771
|
+
"ok",
|
|
772
|
+
"packageId",
|
|
773
|
+
"contractVersion",
|
|
774
|
+
"tools",
|
|
775
|
+
...(contractVersion >= 3 ? ["hooks"] : []),
|
|
776
|
+
],
|
|
777
|
+
label,
|
|
778
|
+
);
|
|
482
779
|
if (typeof value.ok !== "boolean") {
|
|
483
780
|
throw new Error(`${label}.ok must be a boolean`);
|
|
484
781
|
}
|
|
@@ -498,12 +795,32 @@ export function decodeIsolateHealthV1(
|
|
|
498
795
|
if (new Set(tools.map((tool) => tool.name)).size !== tools.length) {
|
|
499
796
|
throw new Error(`${label}.tools contains duplicate names`);
|
|
500
797
|
}
|
|
798
|
+
if (
|
|
799
|
+
contractVersion >= 3 &&
|
|
800
|
+
(!Array.isArray(value.hooks) ||
|
|
801
|
+
value.hooks.length > BOT_ISOLATE_HOOK_EVENTS_V1.length)
|
|
802
|
+
) {
|
|
803
|
+
throw new Error(`${label}.hooks must be a bounded array`);
|
|
804
|
+
}
|
|
805
|
+
const hooks =
|
|
806
|
+
contractVersion < 3
|
|
807
|
+
? []
|
|
808
|
+
: (value.hooks as unknown[]).map((hook, index) => {
|
|
809
|
+
if (!isBotIsolateHookEventNameV1(hook)) {
|
|
810
|
+
throw new Error(`${label}.hooks[${index}] is invalid`);
|
|
811
|
+
}
|
|
812
|
+
return hook;
|
|
813
|
+
});
|
|
814
|
+
if (new Set(hooks).size !== hooks.length) {
|
|
815
|
+
throw new Error(`${label}.hooks contains duplicates`);
|
|
816
|
+
}
|
|
501
817
|
return {
|
|
502
818
|
schemaVersion: 1,
|
|
503
819
|
ok: value.ok,
|
|
504
820
|
packageId: boundedString(value.packageId, `${label}.packageId`, 128),
|
|
505
821
|
contractVersion,
|
|
506
822
|
tools,
|
|
823
|
+
hooks,
|
|
507
824
|
};
|
|
508
825
|
}
|
|
509
826
|
|
|
@@ -524,73 +841,344 @@ export function decodeIsolateIdentityV1(
|
|
|
524
841
|
};
|
|
525
842
|
}
|
|
526
843
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
"
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
844
|
+
export function decodeIsolateConnectionV1(
|
|
845
|
+
input: unknown,
|
|
846
|
+
label = "isolate Connection",
|
|
847
|
+
): IsolateConnectionV1 {
|
|
848
|
+
const value = record(input, label);
|
|
849
|
+
exactKeys(
|
|
850
|
+
value,
|
|
851
|
+
[
|
|
852
|
+
"connectionId",
|
|
853
|
+
"packageId",
|
|
854
|
+
"connectionTypeId",
|
|
855
|
+
"displayName",
|
|
856
|
+
"generation",
|
|
857
|
+
"safeMetadata",
|
|
858
|
+
],
|
|
859
|
+
label,
|
|
860
|
+
);
|
|
861
|
+
const safeMetadata = record(value.safeMetadata, `${label}.safeMetadata`);
|
|
862
|
+
jsonValue(safeMetadata, `${label}.safeMetadata`);
|
|
863
|
+
return {
|
|
864
|
+
connectionId: boundedString(
|
|
865
|
+
value.connectionId,
|
|
866
|
+
`${label}.connectionId`,
|
|
867
|
+
256,
|
|
868
|
+
),
|
|
869
|
+
packageId: boundedString(value.packageId, `${label}.packageId`, 128),
|
|
870
|
+
connectionTypeId: boundedString(
|
|
871
|
+
value.connectionTypeId,
|
|
872
|
+
`${label}.connectionTypeId`,
|
|
873
|
+
128,
|
|
874
|
+
),
|
|
875
|
+
displayName: boundedString(value.displayName, `${label}.displayName`, 256),
|
|
876
|
+
generation: boundedString(value.generation, `${label}.generation`, 256),
|
|
877
|
+
safeMetadata,
|
|
878
|
+
};
|
|
879
|
+
}
|
|
534
880
|
|
|
535
|
-
export function
|
|
881
|
+
export function decodeIsolateModelBindingV1(
|
|
536
882
|
input: unknown,
|
|
537
|
-
label = "isolate
|
|
538
|
-
):
|
|
883
|
+
label = "isolate model binding",
|
|
884
|
+
): IsolateModelBindingV1 {
|
|
539
885
|
const value = record(input, label);
|
|
540
|
-
exactKeys(
|
|
541
|
-
|
|
542
|
-
|
|
886
|
+
exactKeys(
|
|
887
|
+
value,
|
|
888
|
+
[
|
|
889
|
+
"connectionId",
|
|
890
|
+
"packageId",
|
|
891
|
+
"provider",
|
|
892
|
+
"providerModelId",
|
|
893
|
+
"connectionGeneration",
|
|
894
|
+
],
|
|
895
|
+
label,
|
|
896
|
+
["catalogGeneration"],
|
|
897
|
+
);
|
|
543
898
|
return {
|
|
544
|
-
|
|
545
|
-
value.
|
|
546
|
-
`${label}.
|
|
899
|
+
connectionId: boundedString(
|
|
900
|
+
value.connectionId,
|
|
901
|
+
`${label}.connectionId`,
|
|
902
|
+
256,
|
|
903
|
+
),
|
|
904
|
+
packageId: boundedString(value.packageId, `${label}.packageId`, 128),
|
|
905
|
+
provider: boundedString(value.provider, `${label}.provider`, 128),
|
|
906
|
+
providerModelId: boundedString(
|
|
907
|
+
value.providerModelId,
|
|
908
|
+
`${label}.providerModelId`,
|
|
909
|
+
256,
|
|
910
|
+
),
|
|
911
|
+
connectionGeneration: boundedString(
|
|
912
|
+
value.connectionGeneration,
|
|
913
|
+
`${label}.connectionGeneration`,
|
|
547
914
|
256,
|
|
548
915
|
),
|
|
549
|
-
|
|
916
|
+
...(value.catalogGeneration === undefined
|
|
917
|
+
? {}
|
|
918
|
+
: {
|
|
919
|
+
catalogGeneration: boundedString(
|
|
920
|
+
value.catalogGeneration,
|
|
921
|
+
`${label}.catalogGeneration`,
|
|
922
|
+
256,
|
|
923
|
+
),
|
|
924
|
+
}),
|
|
550
925
|
};
|
|
551
926
|
}
|
|
552
927
|
|
|
553
928
|
export function decodeIsolateCapabilityListV1(
|
|
554
929
|
input: unknown,
|
|
555
930
|
label = "isolate capability list",
|
|
556
|
-
):
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
931
|
+
): IsolateCapabilityListV1 {
|
|
932
|
+
const value = record(input, label);
|
|
933
|
+
exactKeys(
|
|
934
|
+
value,
|
|
935
|
+
[
|
|
936
|
+
"status",
|
|
937
|
+
"connections",
|
|
938
|
+
"tools",
|
|
939
|
+
"memory",
|
|
940
|
+
"workspace",
|
|
941
|
+
"notify",
|
|
942
|
+
"schedule",
|
|
943
|
+
],
|
|
944
|
+
label,
|
|
945
|
+
["model"],
|
|
563
946
|
);
|
|
947
|
+
if (
|
|
948
|
+
value.status !== "available" ||
|
|
949
|
+
value.tools !== true ||
|
|
950
|
+
value.notify !== true ||
|
|
951
|
+
value.schedule !== true ||
|
|
952
|
+
typeof value.memory !== "boolean" ||
|
|
953
|
+
typeof value.workspace !== "boolean" ||
|
|
954
|
+
!Array.isArray(value.connections) ||
|
|
955
|
+
value.connections.length > MAX_ISOLATE_CONNECTIONS
|
|
956
|
+
) {
|
|
957
|
+
throw new Error(`${label} is invalid`);
|
|
958
|
+
}
|
|
959
|
+
return {
|
|
960
|
+
status: "available",
|
|
961
|
+
connections: value.connections.map((connection, index) =>
|
|
962
|
+
decodeIsolateConnectionV1(connection, `${label}.connections[${index}]`),
|
|
963
|
+
),
|
|
964
|
+
...(value.model === undefined
|
|
965
|
+
? {}
|
|
966
|
+
: { model: decodeIsolateModelBindingV1(value.model, `${label}.model`) }),
|
|
967
|
+
tools: true,
|
|
968
|
+
memory: value.memory,
|
|
969
|
+
workspace: value.workspace,
|
|
970
|
+
notify: true,
|
|
971
|
+
schedule: true,
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
export function decodeIsolateToolRequestV1(
|
|
976
|
+
input: unknown,
|
|
977
|
+
label = "isolate tool request",
|
|
978
|
+
): IsolateToolRequestV1 {
|
|
979
|
+
const value = record(input, label);
|
|
980
|
+
exactKeys(value, ["callId", "name", "input"], label);
|
|
981
|
+
jsonValue(value.input, `${label}.input`);
|
|
982
|
+
return {
|
|
983
|
+
callId: boundedString(value.callId, `${label}.callId`, 256),
|
|
984
|
+
name: boundedString(value.name, `${label}.name`, 128),
|
|
985
|
+
input: value.input,
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
export function decodeIsolateScheduleRequestV1(
|
|
990
|
+
input: unknown,
|
|
991
|
+
label = "isolate schedule request",
|
|
992
|
+
): IsolateScheduleRequestV1 {
|
|
993
|
+
const value = record(input, label);
|
|
994
|
+
exactKeys(value, ["callId", "input"], label);
|
|
995
|
+
jsonValue(value.input, `${label}.input`);
|
|
996
|
+
return {
|
|
997
|
+
callId: boundedString(value.callId, `${label}.callId`, 256),
|
|
998
|
+
input: value.input,
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
const MEMORY_SCOPES = ["bot", "user", "project"] as const;
|
|
1003
|
+
const MEMORY_TIERS = ["profile", "log", "note"] as const;
|
|
1004
|
+
|
|
1005
|
+
export function decodeIsolateMemoryReadRequestV1(
|
|
1006
|
+
input: unknown,
|
|
1007
|
+
label = "isolate Memory read request",
|
|
1008
|
+
): IsolateMemoryReadRequestV1 {
|
|
1009
|
+
const value = record(input, label);
|
|
1010
|
+
exactKeys(value, ["scope"], label, ["projectId"]);
|
|
1011
|
+
const scope = MEMORY_SCOPES.find((candidate) => candidate === value.scope);
|
|
1012
|
+
if (!scope) throw new Error(`${label}.scope is invalid`);
|
|
1013
|
+
if (scope === "project" && value.projectId === undefined) {
|
|
1014
|
+
throw new Error(`${label}.projectId is required`);
|
|
1015
|
+
}
|
|
1016
|
+
return {
|
|
1017
|
+
scope,
|
|
1018
|
+
...(value.projectId === undefined
|
|
1019
|
+
? {}
|
|
1020
|
+
: {
|
|
1021
|
+
projectId: boundedString(value.projectId, `${label}.projectId`, 128),
|
|
1022
|
+
}),
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
export function decodeIsolateMemoryWriteRequestV1(
|
|
1027
|
+
input: unknown,
|
|
1028
|
+
label = "isolate Memory write request",
|
|
1029
|
+
): IsolateMemoryWriteRequestV1 {
|
|
1030
|
+
const value = record(input, label);
|
|
1031
|
+
exactKeys(value, ["scope", "fact"], label, ["projectId", "tier"]);
|
|
1032
|
+
const scope = MEMORY_SCOPES.find((candidate) => candidate === value.scope);
|
|
1033
|
+
const tier =
|
|
1034
|
+
value.tier === undefined
|
|
1035
|
+
? undefined
|
|
1036
|
+
: MEMORY_TIERS.find((candidate) => candidate === value.tier);
|
|
1037
|
+
if (!scope || (value.tier !== undefined && !tier)) {
|
|
1038
|
+
throw new Error(`${label} is invalid`);
|
|
1039
|
+
}
|
|
1040
|
+
if (scope === "project" && value.projectId === undefined) {
|
|
1041
|
+
throw new Error(`${label}.projectId is required`);
|
|
1042
|
+
}
|
|
1043
|
+
return {
|
|
1044
|
+
scope,
|
|
1045
|
+
...(value.projectId === undefined
|
|
1046
|
+
? {}
|
|
1047
|
+
: {
|
|
1048
|
+
projectId: boundedString(value.projectId, `${label}.projectId`, 128),
|
|
1049
|
+
}),
|
|
1050
|
+
...(tier ? { tier } : {}),
|
|
1051
|
+
fact: boundedString(value.fact, `${label}.fact`, 2_000),
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function decodeIsolateWorkspaceRootV1(
|
|
1056
|
+
input: unknown,
|
|
1057
|
+
label: string,
|
|
1058
|
+
): IsolateWorkspaceRootV1 {
|
|
1059
|
+
const value = record(input, label);
|
|
1060
|
+
if (value.kind === "user-instructions") {
|
|
1061
|
+
exactKeys(value, ["kind"], label);
|
|
1062
|
+
return { kind: "user-instructions" };
|
|
1063
|
+
}
|
|
1064
|
+
if (value.kind === "bot-instructions") {
|
|
1065
|
+
exactKeys(value, ["kind", "botId"], label);
|
|
1066
|
+
return {
|
|
1067
|
+
kind: "bot-instructions",
|
|
1068
|
+
botId: boundedString(value.botId, `${label}.botId`, 256),
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
if (value.kind === "package-declared") {
|
|
1072
|
+
exactKeys(value, ["kind", "packageId", "rootId"], label);
|
|
1073
|
+
return {
|
|
1074
|
+
kind: "package-declared",
|
|
1075
|
+
packageId: boundedString(value.packageId, `${label}.packageId`, 128),
|
|
1076
|
+
rootId: boundedString(value.rootId, `${label}.rootId`, 128),
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
throw new Error(`${label}.kind is invalid`);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
export function decodeIsolateWorkspacePathV1(
|
|
1083
|
+
input: unknown,
|
|
1084
|
+
label = "isolate Workspace path",
|
|
1085
|
+
): IsolateWorkspacePathV1 {
|
|
1086
|
+
const value = record(input, label);
|
|
1087
|
+
exactKeys(value, ["root", "path"], label);
|
|
1088
|
+
return {
|
|
1089
|
+
root: decodeIsolateWorkspaceRootV1(value.root, `${label}.root`),
|
|
1090
|
+
path: boundedString(value.path, `${label}.path`, 1_024, true),
|
|
1091
|
+
};
|
|
564
1092
|
}
|
|
565
1093
|
|
|
566
|
-
export function
|
|
1094
|
+
export function decodeIsolateWorkspaceListRequestV1(
|
|
567
1095
|
input: unknown,
|
|
568
|
-
label = "isolate
|
|
569
|
-
):
|
|
1096
|
+
label = "isolate Workspace list request",
|
|
1097
|
+
): IsolateWorkspaceListRequestV1 {
|
|
570
1098
|
const value = record(input, label);
|
|
571
|
-
exactKeys(value, ["
|
|
1099
|
+
exactKeys(value, ["root"], label, ["prefix", "cursor", "limit"]);
|
|
1100
|
+
const limit = value.limit;
|
|
1101
|
+
if (
|
|
1102
|
+
limit !== undefined &&
|
|
1103
|
+
(!Number.isSafeInteger(limit) ||
|
|
1104
|
+
(limit as number) <= 0 ||
|
|
1105
|
+
(limit as number) > 1_000)
|
|
1106
|
+
) {
|
|
1107
|
+
throw new Error(`${label}.limit is invalid`);
|
|
1108
|
+
}
|
|
572
1109
|
return {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
1110
|
+
root: decodeIsolateWorkspaceRootV1(value.root, `${label}.root`),
|
|
1111
|
+
...(value.prefix === undefined
|
|
1112
|
+
? {}
|
|
1113
|
+
: {
|
|
1114
|
+
prefix: boundedString(value.prefix, `${label}.prefix`, 1_024, true),
|
|
1115
|
+
}),
|
|
1116
|
+
...(value.cursor === undefined
|
|
1117
|
+
? {}
|
|
1118
|
+
: { cursor: boundedString(value.cursor, `${label}.cursor`, 1_024) }),
|
|
1119
|
+
...(limit === undefined ? {} : { limit: limit as number }),
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
export function decodeIsolateWorkspaceWriteRequestV1(
|
|
1124
|
+
input: unknown,
|
|
1125
|
+
label = "isolate Workspace write request",
|
|
1126
|
+
): IsolateWorkspaceWriteRequestV1 {
|
|
1127
|
+
const value = record(input, label);
|
|
1128
|
+
exactKeys(value, ["path", "bytes", "expectedGenerationId"], label, [
|
|
1129
|
+
"mediaType",
|
|
1130
|
+
]);
|
|
1131
|
+
if (!(value.bytes instanceof Uint8Array)) {
|
|
1132
|
+
throw new Error(`${label}.bytes must be bytes`);
|
|
1133
|
+
}
|
|
1134
|
+
if (
|
|
1135
|
+
value.expectedGenerationId !== null &&
|
|
1136
|
+
typeof value.expectedGenerationId !== "string"
|
|
1137
|
+
) {
|
|
1138
|
+
throw new Error(`${label}.expectedGenerationId is invalid`);
|
|
1139
|
+
}
|
|
1140
|
+
return {
|
|
1141
|
+
path: decodeIsolateWorkspacePathV1(value.path, `${label}.path`),
|
|
1142
|
+
bytes: value.bytes,
|
|
1143
|
+
expectedGenerationId: value.expectedGenerationId as string | null,
|
|
1144
|
+
...(value.mediaType === undefined
|
|
1145
|
+
? {}
|
|
1146
|
+
: {
|
|
1147
|
+
mediaType: boundedString(value.mediaType, `${label}.mediaType`, 256),
|
|
1148
|
+
}),
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
export function decodeIsolateWorkspaceDeleteRequestV1(
|
|
1153
|
+
input: unknown,
|
|
1154
|
+
label = "isolate Workspace delete request",
|
|
1155
|
+
): IsolateWorkspaceDeleteRequestV1 {
|
|
1156
|
+
const value = record(input, label);
|
|
1157
|
+
exactKeys(value, ["path", "expectedGenerationId"], label);
|
|
1158
|
+
return {
|
|
1159
|
+
path: decodeIsolateWorkspacePathV1(value.path, `${label}.path`),
|
|
1160
|
+
expectedGenerationId: boundedString(
|
|
1161
|
+
value.expectedGenerationId,
|
|
1162
|
+
`${label}.expectedGenerationId`,
|
|
576
1163
|
256,
|
|
577
1164
|
),
|
|
578
|
-
reason: boundedString(value.reason, `${label}.reason`, 2048, true),
|
|
579
1165
|
};
|
|
580
1166
|
}
|
|
581
1167
|
|
|
582
|
-
export function
|
|
1168
|
+
export function decodeIsolateNotificationRequestV1(
|
|
583
1169
|
input: unknown,
|
|
584
|
-
label = "isolate
|
|
585
|
-
):
|
|
1170
|
+
label = "isolate notification request",
|
|
1171
|
+
): IsolateNotificationRequestV1 {
|
|
586
1172
|
const value = record(input, label);
|
|
587
|
-
exactKeys(value, ["
|
|
588
|
-
if (value.status !== "pending-user-decision") {
|
|
589
|
-
throw new Error(`${label}.status must be pending-user-decision`);
|
|
590
|
-
}
|
|
1173
|
+
exactKeys(value, ["notificationId", "title", "body"], label);
|
|
591
1174
|
return {
|
|
592
|
-
|
|
593
|
-
|
|
1175
|
+
notificationId: boundedString(
|
|
1176
|
+
value.notificationId,
|
|
1177
|
+
`${label}.notificationId`,
|
|
1178
|
+
256,
|
|
1179
|
+
),
|
|
1180
|
+
title: boundedString(value.title, `${label}.title`, 160),
|
|
1181
|
+
body: boundedString(value.body, `${label}.body`, 500),
|
|
594
1182
|
};
|
|
595
1183
|
}
|
|
596
1184
|
|
|
@@ -665,8 +1253,8 @@ export function decodeIsolateModelInvocationV1(
|
|
|
665
1253
|
label = "isolate model invocation",
|
|
666
1254
|
): IsolateModelInvocationV1 {
|
|
667
1255
|
const value = record(input, label);
|
|
668
|
-
if (value.status === "
|
|
669
|
-
return
|
|
1256
|
+
if (value.status === "unavailable") {
|
|
1257
|
+
return decodeIsolateCapabilityFailureV1(value, label);
|
|
670
1258
|
}
|
|
671
1259
|
exactKeys(value, ["status", "requestId", "events"], label);
|
|
672
1260
|
if (value.status !== "streaming") {
|