@opengeni/contracts 0.9.0 → 0.15.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/dist/index.js CHANGED
@@ -1,10 +1,664 @@
1
1
  // src/index.ts
2
2
  import { z } from "zod";
3
+
4
+ // src/event-preview.ts
5
+ var SESSION_EVENT_PAYLOAD_MAX_BYTES = 64 * 1024;
6
+ var TARGET_PAYLOAD_BYTES = 60 * 1024;
7
+ var DEFAULT_STRING_BYTES = 8 * 1024;
8
+ var RETRY_STRING_BYTES = 1024;
9
+ var DEFAULT_ARRAY_ENTRIES = 40;
10
+ var RETRY_ARRAY_ENTRIES = 12;
11
+ var DEFAULT_OBJECT_FIELDS = 80;
12
+ var RETRY_OBJECT_FIELDS = 32;
13
+ var MAX_PREVIEW_DEPTH = 12;
14
+ var MAX_PREVIEW_NODES = 512;
15
+ var MAX_MEASUREMENT_NODES = 2048;
16
+ var MAX_MEASUREMENT_DEPTH = 64;
17
+ var MAX_DETAIL_RECORDS = 24;
18
+ var APPROX_BYTES_PER_TOKEN = 4;
19
+ var encoder = new TextEncoder();
20
+ var IDENTITY_FIELDS = /* @__PURE__ */ new Set([
21
+ "id",
22
+ "callId",
23
+ "call_id",
24
+ "name",
25
+ "toolName",
26
+ "type",
27
+ "status",
28
+ "code",
29
+ "isError",
30
+ "stream",
31
+ "commandId",
32
+ "sequence"
33
+ ]);
34
+ function sessionEventJsonBytes(value) {
35
+ return encoder.encode(stringifyForBoundary(value)).byteLength;
36
+ }
37
+ function measureSessionEventJson(value) {
38
+ return measureJsonBytes(value);
39
+ }
40
+ function approximateSessionEventTokens(bytes) {
41
+ return Math.ceil(Math.max(0, bytes) / APPROX_BYTES_PER_TOKEN);
42
+ }
43
+ function sessionEventMediaPreview(mediaType, inlineBytes) {
44
+ return {
45
+ type: "media_preview",
46
+ mediaType: mediaType || "application/octet-stream",
47
+ inlineBytes: inlineBytes === null ? null : Math.max(0, Math.floor(Number.isFinite(inlineBytes) ? inlineBytes : 0)),
48
+ fullOutputAvailable: false,
49
+ preview: "Inline media omitted from the audit timeline; source bytes were not retained."
50
+ };
51
+ }
52
+ function sessionEventMediaPreviewFromDataUrl(value) {
53
+ const match = /^data:([^;,]+)(?:;[^,]*)?;base64,/i.exec(value);
54
+ if (!match) return null;
55
+ const encodedLength = Math.max(0, value.length - match[0].length);
56
+ const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
57
+ return sessionEventMediaPreview(
58
+ match[1] ?? "application/octet-stream",
59
+ Math.max(0, Math.floor(encodedLength * 3 / 4) - padding)
60
+ );
61
+ }
62
+ function sessionEventPayloadTruncation(payload) {
63
+ if (!isPlainRecord(payload)) return null;
64
+ const value = payload.truncation;
65
+ if (!isPlainRecord(value) || value.truncated !== true) return null;
66
+ return value;
67
+ }
68
+ function boundSessionEventPayload(payload, options = {}) {
69
+ const maxBytes = Math.max(1024, Math.floor(options.maxBytes ?? SESSION_EVENT_PAYLOAD_MAX_BYTES));
70
+ const surface = options.surface ?? "durable_audit";
71
+ const originalMeasurement = measureJsonBytes(payload);
72
+ const originalBytes = originalMeasurement.bytes;
73
+ let state = previewState(DEFAULT_STRING_BYTES, DEFAULT_ARRAY_ENTRIES, DEFAULT_OBJECT_FIELDS);
74
+ let preview = previewValue(payload, state, "$", 0);
75
+ if (!state.changed && originalBytes !== null && originalBytes <= maxBytes) {
76
+ return payload;
77
+ }
78
+ let reason = payloadTruncationReason(originalMeasurement, state);
79
+ let bounded = attachTruncation(preview, boundaryMetadata(surface, reason, originalBytes, state));
80
+ if (sessionEventJsonBytes(bounded) > Math.min(maxBytes, TARGET_PAYLOAD_BYTES)) {
81
+ state = previewState(RETRY_STRING_BYTES, RETRY_ARRAY_ENTRIES, RETRY_OBJECT_FIELDS);
82
+ preview = previewValue(payload, state, "$", 0);
83
+ reason = payloadTruncationReason(originalMeasurement, state);
84
+ bounded = attachTruncation(preview, boundaryMetadata(surface, reason, originalBytes, state));
85
+ }
86
+ if (sessionEventJsonBytes(bounded) > maxBytes) {
87
+ const identity = identityPreview(payload);
88
+ state.changed = true;
89
+ recordDetail(state, {
90
+ path: "$",
91
+ kind: "object",
92
+ ...originalBytes === null ? {} : { originalBytes }
93
+ });
94
+ bounded = attachTruncation(
95
+ {
96
+ ...identity,
97
+ preview: "[event payload omitted: bounded audit preview exceeded the storage envelope]"
98
+ },
99
+ boundaryMetadata(surface, reason, originalBytes, state)
100
+ );
101
+ }
102
+ settleDeliveredSizes(bounded, maxBytes);
103
+ return bounded;
104
+ }
105
+ function previewState(stringBytes, arrayEntries, objectFields) {
106
+ return {
107
+ changed: false,
108
+ sawMedia: false,
109
+ details: [],
110
+ stringBytes,
111
+ arrayEntries,
112
+ objectFields,
113
+ remainingNodes: MAX_PREVIEW_NODES
114
+ };
115
+ }
116
+ function payloadTruncationReason(measurement, state) {
117
+ if (state.sawMedia) return "inline_media_not_retained";
118
+ return measurement.bytes === null ? measurement.reason : "payload_bytes_exceeded";
119
+ }
120
+ function previewValue(value, state, path, depth) {
121
+ if (state.remainingNodes <= 0) {
122
+ state.changed = true;
123
+ recordDetail(state, { path, kind: "budget" });
124
+ return "[nested value omitted at audit preview traversal boundary]";
125
+ }
126
+ state.remainingNodes -= 1;
127
+ if (typeof value === "string") {
128
+ const media = inlineMediaFact(value);
129
+ if (media) {
130
+ state.changed = true;
131
+ state.sawMedia = true;
132
+ recordDetail(state, {
133
+ path,
134
+ kind: "media",
135
+ originalBytes: utf8Bytes(value),
136
+ deliveredBytes: sessionEventJsonBytes(media),
137
+ mediaType: media.mediaType
138
+ });
139
+ return media;
140
+ }
141
+ const originalBytes = utf8Bytes(value);
142
+ if (originalBytes <= state.stringBytes) return value;
143
+ state.changed = true;
144
+ const delivered = truncateUtf8Middle(value, state.stringBytes, originalBytes);
145
+ recordDetail(state, {
146
+ path,
147
+ kind: "string",
148
+ originalBytes,
149
+ deliveredBytes: utf8Bytes(delivered)
150
+ });
151
+ return delivered;
152
+ }
153
+ if (typeof value === "bigint" || typeof value === "function" || typeof value === "symbol") {
154
+ state.changed = true;
155
+ recordDetail(state, { path, kind: "unserializable" });
156
+ return `[${typeof value} value omitted at audit serialization boundary]`;
157
+ }
158
+ if (value === null || typeof value !== "object") return value;
159
+ const binary = binaryFact(value);
160
+ if (binary) {
161
+ state.changed = true;
162
+ recordDetail(state, {
163
+ path,
164
+ kind: "binary",
165
+ originalBytes: binary.originalBytes,
166
+ deliveredBytes: sessionEventJsonBytes(binary.preview)
167
+ });
168
+ return binary.preview;
169
+ }
170
+ if (depth >= MAX_PREVIEW_DEPTH) {
171
+ state.changed = true;
172
+ recordDetail(state, { path, kind: "depth" });
173
+ return "[nested value omitted at audit preview depth boundary]";
174
+ }
175
+ if (Array.isArray(value)) {
176
+ const keep = Math.max(2, state.arrayEntries);
177
+ if (value.length <= keep) {
178
+ return Array.from(
179
+ { length: value.length },
180
+ (_, index) => previewArrayEntry(value, index, state, path, depth)
181
+ );
182
+ }
183
+ state.changed = true;
184
+ const head = Math.ceil(keep / 2);
185
+ const tail = Math.floor(keep / 2);
186
+ const omitted2 = value.length - head - tail;
187
+ recordDetail(state, { path, kind: "array", omittedEntries: omitted2 });
188
+ return [
189
+ ...Array.from(
190
+ { length: head },
191
+ (_, index) => previewArrayEntry(value, index, state, path, depth)
192
+ ),
193
+ { omittedEntries: omitted2, preview: "[middle array entries omitted]" },
194
+ ...Array.from(
195
+ { length: tail },
196
+ (_, index) => previewArrayEntry(value, value.length - tail + index, state, path, depth)
197
+ )
198
+ ];
199
+ }
200
+ if (value instanceof Date) {
201
+ if (!hasCanonicalDateSerialization(value)) {
202
+ state.changed = true;
203
+ recordDetail(state, { path, kind: "unserializable" });
204
+ return "[Date value with custom serialization omitted at audit boundary]";
205
+ }
206
+ try {
207
+ const epoch = Date.prototype.getTime.call(value);
208
+ return Number.isFinite(epoch) ? Date.prototype.toISOString.call(value) : null;
209
+ } catch {
210
+ state.changed = true;
211
+ recordDetail(state, { path, kind: "unserializable" });
212
+ return "[Date value omitted at audit serialization boundary]";
213
+ }
214
+ }
215
+ const record = value;
216
+ const { entries, omitted, accessorKeys } = selectObjectEntries(record, state.objectFields);
217
+ for (const key of accessorKeys) {
218
+ state.changed = true;
219
+ recordDetail(state, {
220
+ path: `${path}.${key}`,
221
+ kind: "unserializable"
222
+ });
223
+ }
224
+ if (omitted) {
225
+ state.changed = true;
226
+ recordDetail(state, {
227
+ path,
228
+ kind: "object",
229
+ // Counting every remaining field would defeat the traversal bound. `null`
230
+ // is deliberate: at least one field was omitted, but its exact count is
231
+ // unknown because the source was not fully enumerated.
232
+ omittedEntries: null
233
+ });
234
+ }
235
+ const out = {};
236
+ for (const [key, entry] of entries) {
237
+ out[key] = previewValue(entry, state, `${path}.${key}`, depth + 1);
238
+ }
239
+ if (omitted) {
240
+ out.omittedFields = "additional fields omitted; exact count not measured";
241
+ }
242
+ return out;
243
+ }
244
+ function previewArrayEntry(value, index, state, path, depth) {
245
+ let descriptor;
246
+ try {
247
+ descriptor = Object.getOwnPropertyDescriptor(value, String(index));
248
+ } catch {
249
+ state.changed = true;
250
+ recordDetail(state, { path: `${path}[${index}]`, kind: "unserializable" });
251
+ return "[array element omitted at audit serialization boundary]";
252
+ }
253
+ if (descriptor && !("value" in descriptor)) {
254
+ state.changed = true;
255
+ recordDetail(state, { path: `${path}[${index}]`, kind: "unserializable" });
256
+ return "[array accessor value omitted at audit serialization boundary]";
257
+ }
258
+ return previewValue(
259
+ descriptor && "value" in descriptor ? descriptor.value : void 0,
260
+ state,
261
+ `${path}[${index}]`,
262
+ depth + 1
263
+ );
264
+ }
265
+ function selectObjectEntries(record, maxFields) {
266
+ const entries = [];
267
+ const selected = /* @__PURE__ */ new Set();
268
+ const accessorKeys = [];
269
+ const select = (key) => {
270
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
271
+ if (!descriptor?.enumerable) return false;
272
+ selected.add(key);
273
+ if ("value" in descriptor) {
274
+ entries.push([key, descriptor.value]);
275
+ } else {
276
+ entries.push([key, "[accessor value omitted at audit serialization boundary]"]);
277
+ accessorKeys.push(key);
278
+ }
279
+ return true;
280
+ };
281
+ try {
282
+ for (const key of IDENTITY_FIELDS) {
283
+ if (entries.length >= maxFields) break;
284
+ select(key);
285
+ }
286
+ for (const key in record) {
287
+ if (!Object.prototype.hasOwnProperty.call(record, key) || selected.has(key)) continue;
288
+ if (entries.length >= maxFields) {
289
+ return { entries, omitted: true, accessorKeys };
290
+ }
291
+ select(key);
292
+ }
293
+ } catch {
294
+ return { entries, omitted: true, accessorKeys };
295
+ }
296
+ return { entries, omitted: false, accessorKeys };
297
+ }
298
+ function attachTruncation(preview, truncation) {
299
+ if (isPlainRecord(preview)) {
300
+ return { ...preview, truncation };
301
+ }
302
+ return { value: preview, truncation };
303
+ }
304
+ function boundaryMetadata(surface, reason, originalBytes, state) {
305
+ return {
306
+ truncated: true,
307
+ surface,
308
+ reason,
309
+ originalBytes,
310
+ deliveredBytes: 0,
311
+ omittedBytes: originalBytes,
312
+ estimatedOriginalTokens: originalBytes === null ? null : approximateSessionEventTokens(originalBytes),
313
+ estimatedDeliveredTokens: 0,
314
+ fullEvidence: { available: false, reason: "not_retained" },
315
+ details: state.details
316
+ };
317
+ }
318
+ function settleDeliveredSizes(payload, maxBytes) {
319
+ const truncation = payload.truncation;
320
+ convergeDeliveredSizes(payload, truncation);
321
+ if (sessionEventJsonBytes(payload) > maxBytes) {
322
+ payload.preview = "[event payload omitted at audit byte boundary]";
323
+ for (const key of Object.keys(payload)) {
324
+ if (key !== "preview" && key !== "truncation" && IDENTITY_FIELDS.has(key) === false) {
325
+ delete payload[key];
326
+ }
327
+ }
328
+ truncation.details = truncation.details.slice(0, 4);
329
+ convergeDeliveredSizes(payload, truncation);
330
+ }
331
+ }
332
+ function convergeDeliveredSizes(payload, truncation) {
333
+ for (let attempt = 0; attempt < 16; attempt += 1) {
334
+ const deliveredBytes = sessionEventJsonBytes(payload);
335
+ const omittedBytes = truncation.originalBytes === null ? null : Math.max(0, truncation.originalBytes - deliveredBytes);
336
+ const estimatedDeliveredTokens = approximateSessionEventTokens(deliveredBytes);
337
+ if (truncation.deliveredBytes === deliveredBytes && truncation.omittedBytes === omittedBytes && truncation.estimatedDeliveredTokens === estimatedDeliveredTokens) {
338
+ return;
339
+ }
340
+ truncation.deliveredBytes = deliveredBytes;
341
+ truncation.omittedBytes = omittedBytes;
342
+ truncation.estimatedDeliveredTokens = estimatedDeliveredTokens;
343
+ }
344
+ throw new RangeError("Session event byte accounting did not converge");
345
+ }
346
+ function recordDetail(state, detail) {
347
+ if (state.details.length < MAX_DETAIL_RECORDS) state.details.push(detail);
348
+ }
349
+ function identityPreview(value) {
350
+ if (!isPlainRecord(value)) return {};
351
+ const out = {};
352
+ for (const key of IDENTITY_FIELDS) {
353
+ let field;
354
+ try {
355
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
356
+ if (!descriptor?.enumerable || !("value" in descriptor)) continue;
357
+ field = descriptor.value;
358
+ } catch {
359
+ continue;
360
+ }
361
+ if (typeof field === "string") {
362
+ out[key] = truncateUtf8Middle(field, 256, utf8Bytes(field));
363
+ } else if (typeof field === "number" || typeof field === "boolean" || field === null) {
364
+ out[key] = field;
365
+ }
366
+ }
367
+ return out;
368
+ }
369
+ function inlineMediaFact(value) {
370
+ return sessionEventMediaPreviewFromDataUrl(value);
371
+ }
372
+ function binaryFact(value) {
373
+ if (ArrayBuffer.isView(value)) {
374
+ return {
375
+ originalBytes: value.byteLength,
376
+ preview: {
377
+ type: "binary_preview",
378
+ byteLength: value.byteLength,
379
+ fullOutputAvailable: false,
380
+ preview: "Inline binary value omitted from the audit timeline; bytes were not retained."
381
+ }
382
+ };
383
+ }
384
+ if (value instanceof ArrayBuffer) {
385
+ return {
386
+ originalBytes: value.byteLength,
387
+ preview: {
388
+ type: "binary_preview",
389
+ byteLength: value.byteLength,
390
+ fullOutputAvailable: false,
391
+ preview: "Inline binary value omitted from the audit timeline; bytes were not retained."
392
+ }
393
+ };
394
+ }
395
+ return null;
396
+ }
397
+ function truncateUtf8Middle(value, maxBytes, knownBytes = utf8Bytes(value)) {
398
+ if (knownBytes <= maxBytes) return value;
399
+ let omittedBytes = Math.max(0, knownBytes - maxBytes);
400
+ for (let attempt = 0; attempt < 4; attempt += 1) {
401
+ const marker2 = `\u2026[${omittedBytes} bytes omitted]\u2026`;
402
+ const contentBudget2 = Math.max(0, maxBytes - utf8Bytes(marker2));
403
+ if (contentBudget2 === 0) return marker2;
404
+ const left2 = utf8Prefix(value, Math.floor(contentBudget2 / 2));
405
+ const right2 = utf8Suffix(value, contentBudget2 - left2.bytes);
406
+ const exactOmittedBytes = Math.max(0, knownBytes - left2.bytes - right2.bytes);
407
+ if (exactOmittedBytes === omittedBytes) {
408
+ return `${value.slice(0, left2.end)}${marker2}${value.slice(right2.start)}`;
409
+ }
410
+ omittedBytes = exactOmittedBytes;
411
+ }
412
+ const marker = `\u2026[${omittedBytes} bytes omitted]\u2026`;
413
+ const contentBudget = Math.max(0, maxBytes - utf8Bytes(marker));
414
+ const left = utf8Prefix(value, Math.floor(contentBudget / 2));
415
+ const right = utf8Suffix(value, contentBudget - left.bytes);
416
+ return `${value.slice(0, left.end)}${marker}${value.slice(right.start)}`;
417
+ }
418
+ function utf8Prefix(value, maxBytes) {
419
+ let end = 0;
420
+ let bytes = 0;
421
+ while (end < value.length) {
422
+ const unit = utf8UnitAt(value, end);
423
+ if (bytes + unit.bytes > maxBytes) break;
424
+ bytes += unit.bytes;
425
+ end += unit.codeUnits;
426
+ }
427
+ return { end, bytes };
428
+ }
429
+ function utf8Suffix(value, maxBytes) {
430
+ let start = value.length;
431
+ let bytes = 0;
432
+ while (start > 0) {
433
+ const unit = utf8UnitBefore(value, start);
434
+ if (bytes + unit.bytes > maxBytes) break;
435
+ bytes += unit.bytes;
436
+ start -= unit.codeUnits;
437
+ }
438
+ return { start, bytes };
439
+ }
440
+ function utf8UnitAt(value, index) {
441
+ const code = value.charCodeAt(index);
442
+ if (code <= 127) return { codeUnits: 1, bytes: 1 };
443
+ if (code <= 2047) return { codeUnits: 1, bytes: 2 };
444
+ if (code >= 55296 && code <= 56319) {
445
+ const next = index + 1 < value.length ? value.charCodeAt(index + 1) : 0;
446
+ if (next >= 56320 && next <= 57343) {
447
+ return { codeUnits: 2, bytes: 4 };
448
+ }
449
+ }
450
+ return { codeUnits: 1, bytes: 3 };
451
+ }
452
+ function utf8UnitBefore(value, end) {
453
+ const code = value.charCodeAt(end - 1);
454
+ if (code >= 56320 && code <= 57343 && end >= 2) {
455
+ const previous = value.charCodeAt(end - 2);
456
+ if (previous >= 55296 && previous <= 56319) {
457
+ return { codeUnits: 2, bytes: 4 };
458
+ }
459
+ }
460
+ if (code <= 127) return { codeUnits: 1, bytes: 1 };
461
+ if (code <= 2047) return { codeUnits: 1, bytes: 2 };
462
+ return { codeUnits: 1, bytes: 3 };
463
+ }
464
+ function utf8Bytes(value) {
465
+ let bytes = 0;
466
+ for (let index = 0; index < value.length; ) {
467
+ const unit = utf8UnitAt(value, index);
468
+ bytes += unit.bytes;
469
+ index += unit.codeUnits;
470
+ }
471
+ return bytes;
472
+ }
473
+ function measureJsonBytes(value) {
474
+ const state = {
475
+ remainingNodes: MAX_MEASUREMENT_NODES,
476
+ seen: /* @__PURE__ */ new WeakSet(),
477
+ failureReason: null
478
+ };
479
+ try {
480
+ const bytes = measureJsonValue(value, state, "top", 0);
481
+ return bytes === null ? {
482
+ bytes: null,
483
+ reason: state.failureReason ?? "payload_measurement_bounded"
484
+ } : { bytes, reason: null };
485
+ } catch {
486
+ return { bytes: null, reason: "payload_measurement_bounded" };
487
+ }
488
+ }
489
+ function measureJsonValue(value, state, position, depth) {
490
+ if (state.remainingNodes <= 0 || depth > MAX_MEASUREMENT_DEPTH) {
491
+ return failJsonMeasurement(state, "payload_measurement_bounded");
492
+ }
493
+ state.remainingNodes -= 1;
494
+ if (value === null) return 4;
495
+ if (typeof value === "string") return jsonStringBytes(value);
496
+ if (typeof value === "boolean") return value ? 4 : 5;
497
+ if (typeof value === "number") {
498
+ const serialized = JSON.stringify(value);
499
+ return serialized === void 0 ? null : serialized.length;
500
+ }
501
+ if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") {
502
+ return position === "array" ? 4 : failJsonMeasurement(state, "payload_not_serializable");
503
+ }
504
+ if (typeof value === "bigint") {
505
+ return failJsonMeasurement(state, "payload_not_serializable");
506
+ }
507
+ if (typeof value !== "object") {
508
+ return failJsonMeasurement(state, "payload_not_serializable");
509
+ }
510
+ if (value instanceof Date) {
511
+ if (!hasCanonicalDateSerialization(value)) {
512
+ return failJsonMeasurement(state, "payload_measurement_bounded");
513
+ }
514
+ try {
515
+ const epoch = Date.prototype.getTime.call(value);
516
+ return Number.isFinite(epoch) ? jsonStringBytes(Date.prototype.toISOString.call(value)) : 4;
517
+ } catch {
518
+ return failJsonMeasurement(state, "payload_not_serializable");
519
+ }
520
+ }
521
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
522
+ return failJsonMeasurement(state, "payload_measurement_bounded");
523
+ }
524
+ if (state.seen.has(value)) {
525
+ return failJsonMeasurement(state, "payload_not_serializable");
526
+ }
527
+ state.seen.add(value);
528
+ try {
529
+ if (Array.isArray(value)) {
530
+ if (Object.getPrototypeOf(value) !== Array.prototype) {
531
+ return failJsonMeasurement(state, "payload_measurement_bounded");
532
+ }
533
+ const toJson2 = Object.getOwnPropertyDescriptor(value, "toJSON");
534
+ if (toJson2 && ("get" in toJson2 || typeof toJson2.value === "function")) {
535
+ return failJsonMeasurement(state, "payload_measurement_bounded");
536
+ }
537
+ if (value.length > state.remainingNodes) {
538
+ return failJsonMeasurement(state, "payload_measurement_bounded");
539
+ }
540
+ let bytes2 = 2;
541
+ for (let index = 0; index < value.length; index += 1) {
542
+ if (index > 0) bytes2 += 1;
543
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
544
+ if (descriptor && !("value" in descriptor)) {
545
+ return failJsonMeasurement(state, "payload_measurement_bounded");
546
+ }
547
+ const entry = descriptor && "value" in descriptor ? descriptor.value : void 0;
548
+ const measured = measureJsonValue(entry, state, "array", depth + 1);
549
+ if (measured === null) return null;
550
+ bytes2 += measured;
551
+ }
552
+ return bytes2;
553
+ }
554
+ const prototype = Object.getPrototypeOf(value);
555
+ if (prototype !== Object.prototype && prototype !== null) {
556
+ return failJsonMeasurement(state, "payload_measurement_bounded");
557
+ }
558
+ const toJson = Object.getOwnPropertyDescriptor(value, "toJSON");
559
+ if (toJson && ("get" in toJson || typeof toJson.value === "function")) {
560
+ return failJsonMeasurement(state, "payload_measurement_bounded");
561
+ }
562
+ let bytes = 2;
563
+ let fields = 0;
564
+ try {
565
+ for (const key in value) {
566
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
567
+ if (state.remainingNodes <= 0) {
568
+ return failJsonMeasurement(state, "payload_measurement_bounded");
569
+ }
570
+ state.remainingNodes -= 1;
571
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
572
+ if (!descriptor || !("value" in descriptor)) {
573
+ return failJsonMeasurement(state, "payload_measurement_bounded");
574
+ }
575
+ const entry = descriptor.value;
576
+ if (typeof entry === "undefined" || typeof entry === "function" || typeof entry === "symbol") {
577
+ continue;
578
+ }
579
+ const measured = measureJsonValue(entry, state, "object", depth + 1);
580
+ if (measured === null) return null;
581
+ if (fields > 0) bytes += 1;
582
+ bytes += jsonStringBytes(key) + 1 + measured;
583
+ fields += 1;
584
+ }
585
+ } catch {
586
+ return null;
587
+ }
588
+ return bytes;
589
+ } finally {
590
+ state.seen.delete(value);
591
+ }
592
+ }
593
+ function hasCanonicalDateSerialization(value) {
594
+ try {
595
+ if (Object.getPrototypeOf(value) !== Date.prototype) return false;
596
+ return Object.getOwnPropertyDescriptor(value, "toJSON") === void 0;
597
+ } catch {
598
+ return false;
599
+ }
600
+ }
601
+ function failJsonMeasurement(state, reason) {
602
+ state.failureReason ??= reason;
603
+ return null;
604
+ }
605
+ function jsonStringBytes(value) {
606
+ let bytes = 2;
607
+ for (let index = 0; index < value.length; index += 1) {
608
+ const code = value.charCodeAt(index);
609
+ if (code === 34 || code === 92 || code === 8 || code === 9 || code === 10 || code === 12 || code === 13) {
610
+ bytes += 2;
611
+ } else if (code <= 31) {
612
+ bytes += 6;
613
+ } else if (code <= 127) {
614
+ bytes += 1;
615
+ } else if (code <= 2047) {
616
+ bytes += 2;
617
+ } else if (code >= 55296 && code <= 56319) {
618
+ const next = index + 1 < value.length ? value.charCodeAt(index + 1) : 0;
619
+ if (next >= 56320 && next <= 57343) {
620
+ bytes += 4;
621
+ index += 1;
622
+ } else {
623
+ bytes += 6;
624
+ }
625
+ } else if (code >= 56320 && code <= 57343) {
626
+ bytes += 6;
627
+ } else {
628
+ bytes += 3;
629
+ }
630
+ }
631
+ return bytes;
632
+ }
633
+ function serializeForBoundary(value) {
634
+ try {
635
+ const serialized = JSON.stringify(value);
636
+ return {
637
+ value: serialized === void 0 ? "null" : serialized,
638
+ serializable: serialized !== void 0
639
+ };
640
+ } catch {
641
+ return {
642
+ value: JSON.stringify("[unserializable event payload omitted]"),
643
+ serializable: false
644
+ };
645
+ }
646
+ }
647
+ function stringifyForBoundary(value) {
648
+ return serializeForBoundary(value).value;
649
+ }
650
+ function isPlainRecord(value) {
651
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
652
+ }
653
+
654
+ // src/index.ts
3
655
  var SessionStatus = z.enum([
4
656
  "queued",
5
657
  "running",
6
658
  "idle",
7
659
  "requires_action",
660
+ "recovering",
661
+ "waiting_capacity",
8
662
  "failed",
9
663
  "cancelled"
10
664
  ]);
@@ -383,7 +1037,7 @@ var Permission = z.enum([
383
1037
  "sessions:create",
384
1038
  "sessions:read",
385
1039
  "sessions:control",
386
- // Sandbox-surfacing (master-spine §C.3 / crosscut PART 1.2). stream:view is a
1040
+ // sandbox workspace (sandbox contract §C.3 / crosscut PART 1.2). stream:view is a
387
1041
  // REAL, distinct permission — strictly BROADER than sessions:read — because the
388
1042
  // pixel plane (Channel B) is UN-REDACTED: a viewer of raw pixels can see cloud
389
1043
  // creds the agent cat's into a terminal, which the redacted Channel-A event log
@@ -414,8 +1068,12 @@ var Permission = z.enum([
414
1068
  "api_keys:manage",
415
1069
  "connections:read",
416
1070
  "connections:write",
1071
+ /** @deprecated alias of variable-sets:manage */
417
1072
  "environments:manage",
1073
+ /** @deprecated alias of variable-sets:use */
418
1074
  "environments:use",
1075
+ "variable-sets:manage",
1076
+ "variable-sets:use",
419
1077
  // Attach or rotate per-session third-party MCP server credentials. Deliberately
420
1078
  // not part of the worker's default first-party MCP permission set: a sandboxed
421
1079
  // agent must not be able to hand itself new bearer credentials.
@@ -431,7 +1089,14 @@ var Permission = z.enum([
431
1089
  // enrollment grants WHOLE-MACHINE access to a user's own hardware — a high-trust,
432
1090
  // admin-shaped action. workspace:admin is the super-wildcard over both.
433
1091
  "enrollments:read",
434
- "enrollments:manage"
1092
+ "enrollments:manage",
1093
+ // Rigs (workspace-scoped, versioned sandbox machine definitions). rigs:use is
1094
+ // read + propose-change (the agent-native, additive path a sandboxed session
1095
+ // is trusted with); rigs:manage is create/edit/activate/promote/delete (the
1096
+ // admin-shaped path that mints or rolls versions). workspace:admin is the
1097
+ // super-wildcard over both.
1098
+ "rigs:use",
1099
+ "rigs:manage"
435
1100
  ]);
436
1101
  function prefixedMcpToolName(registryId2, toolName) {
437
1102
  return `${registryId2}__${toolName}`;
@@ -459,12 +1124,316 @@ var Workspace = z.object({
459
1124
  // Per-workspace agent persona template (white-label override). null means
460
1125
  // the deployment default (OPENGENI_AGENT_INSTRUCTIONS_TEMPLATE /
461
1126
  // DEFAULT_AGENT_INSTRUCTIONS) is used. The runtime always injects the
462
- // non-bypassable CORE (goal-loop ownership + environment block), so an
1127
+ // non-bypassable CORE (goal-loop ownership + variableSet block), so an
463
1128
  // override restyles the persona without dropping that contract.
464
1129
  agentInstructions: z.string().nullable(),
1130
+ // Growth-ready per-workspace settings bag (migration 0045). Known keys are
1131
+ // validated by WorkspaceSettingsSchema; unknown keys are preserved across
1132
+ // PATCH merges so newer settings survive an older server.
1133
+ settings: z.record(z.string(), z.unknown()),
1134
+ inferenceControl: z.object({
1135
+ state: z.enum(["active", "paused"]),
1136
+ revision: z.number().int().nonnegative(),
1137
+ reason: z.string().nullable(),
1138
+ changedBy: z.string().nullable(),
1139
+ changedAt: z.string().nullable()
1140
+ }),
1141
+ // Workspace default rig used by session/scheduled-task create fallback.
1142
+ defaultRigId: z.string().uuid().nullable(),
465
1143
  createdAt: z.string(),
466
1144
  updatedAt: z.string()
467
1145
  });
1146
+ var WorkspaceTranscriptionTarget = z.object({
1147
+ provider: z.string().trim().min(1).max(128),
1148
+ model: z.string().trim().min(1).max(256).nullable(),
1149
+ credentialMode: z.enum(["managed", "byok"]),
1150
+ // A workspace-scoped connection reference, never credential material.
1151
+ credentialConnectionId: z.string().uuid().nullable(),
1152
+ region: z.string().trim().min(1).max(128).nullable()
1153
+ }).strict().superRefine((target, context) => {
1154
+ if (target.provider === "azure-speech" && target.credentialMode !== "byok") {
1155
+ context.addIssue({
1156
+ code: "custom",
1157
+ path: ["credentialMode"],
1158
+ message: "Azure Speech is supported only through workspace BYOK"
1159
+ });
1160
+ }
1161
+ if (target.credentialMode === "byok" && target.credentialConnectionId === null) {
1162
+ context.addIssue({
1163
+ code: "custom",
1164
+ path: ["credentialConnectionId"],
1165
+ message: "BYOK transcription targets require a workspace connection reference"
1166
+ });
1167
+ }
1168
+ if (target.credentialMode === "managed" && target.credentialConnectionId !== null) {
1169
+ context.addIssue({
1170
+ code: "custom",
1171
+ path: ["credentialConnectionId"],
1172
+ message: "managed transcription targets cannot name a BYOK connection"
1173
+ });
1174
+ }
1175
+ });
1176
+ var TranscriptionErrorCode = z.enum([
1177
+ "permission_denied",
1178
+ "not_supported",
1179
+ "network",
1180
+ "provider",
1181
+ "policy_blocked",
1182
+ "timeout",
1183
+ "cancelled",
1184
+ "unknown"
1185
+ ]);
1186
+ var TranscriptionTimeSpan = z.object({
1187
+ startMilliseconds: z.number().finite().nonnegative(),
1188
+ endMilliseconds: z.number().finite().nonnegative()
1189
+ }).strict().superRefine((span, context) => {
1190
+ if (span.endMilliseconds < span.startMilliseconds) {
1191
+ context.addIssue({
1192
+ code: "custom",
1193
+ path: ["endMilliseconds"],
1194
+ message: "transcription spans must not end before they start"
1195
+ });
1196
+ }
1197
+ });
1198
+ var TranscriptionSpeaker = z.object({
1199
+ id: z.string().trim().min(1).max(128),
1200
+ label: z.string().trim().min(1).max(128).optional()
1201
+ }).strict();
1202
+ var TranscriptionWord = z.object({
1203
+ text: z.string().min(1).max(4096),
1204
+ span: TranscriptionTimeSpan,
1205
+ confidence: z.number().finite().min(0).max(1).optional(),
1206
+ speaker: TranscriptionSpeaker.optional()
1207
+ }).strict();
1208
+ var TranscriptionResultMetadata = z.object({
1209
+ detectedLanguage: z.string().trim().min(1).max(64).optional(),
1210
+ span: TranscriptionTimeSpan.optional(),
1211
+ confidence: z.number().finite().min(0).max(1).optional(),
1212
+ speaker: TranscriptionSpeaker.optional(),
1213
+ words: z.array(TranscriptionWord).max(1e4).optional()
1214
+ }).strict().superRefine((metadata, context) => {
1215
+ let previousStart = -1;
1216
+ for (const [index, word] of (metadata.words ?? []).entries()) {
1217
+ if (word.span.startMilliseconds < previousStart) {
1218
+ context.addIssue({
1219
+ code: "custom",
1220
+ path: ["words", index, "span", "startMilliseconds"],
1221
+ message: "transcription words must be ordered by start time"
1222
+ });
1223
+ }
1224
+ previousStart = word.span.startMilliseconds;
1225
+ if (metadata.span && (word.span.startMilliseconds < metadata.span.startMilliseconds || word.span.endMilliseconds > metadata.span.endMilliseconds)) {
1226
+ context.addIssue({
1227
+ code: "custom",
1228
+ path: ["words", index, "span"],
1229
+ message: "transcription word spans must fall within the result span"
1230
+ });
1231
+ }
1232
+ }
1233
+ });
1234
+ var TranscriptionEventBase = z.object({
1235
+ localSessionId: z.string().min(1).max(256),
1236
+ sequence: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
1237
+ occurredAt: z.string().datetime({ offset: true })
1238
+ }).strict();
1239
+ var TranscriptionEvent = z.discriminatedUnion("type", [
1240
+ TranscriptionEventBase.extend({ type: z.literal("permission.requested") }),
1241
+ TranscriptionEventBase.extend({
1242
+ type: z.literal("session.opened"),
1243
+ providerSessionId: z.string().min(1).max(512)
1244
+ }),
1245
+ TranscriptionEventBase.extend({
1246
+ type: z.literal("transcript.partial"),
1247
+ segmentId: z.string().min(1).max(512),
1248
+ text: z.string().max(1e6),
1249
+ metadata: TranscriptionResultMetadata.optional()
1250
+ }),
1251
+ TranscriptionEventBase.extend({
1252
+ type: z.literal("transcript.final"),
1253
+ segmentId: z.string().min(1).max(512),
1254
+ text: z.string().max(1e6),
1255
+ providerAcceptanceId: z.string().min(1).max(512),
1256
+ metadata: TranscriptionResultMetadata.optional()
1257
+ }),
1258
+ TranscriptionEventBase.extend({
1259
+ type: z.literal("usage"),
1260
+ audioMilliseconds: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
1261
+ costUsd: z.number().finite().nonnegative().max(1e9).nullable()
1262
+ }),
1263
+ TranscriptionEventBase.extend({
1264
+ type: z.literal("session.reconnecting"),
1265
+ attempt: z.number().int().nonnegative().max(1e4),
1266
+ reason: z.string().min(1).max(256)
1267
+ }),
1268
+ TranscriptionEventBase.extend({
1269
+ type: z.literal("session.error"),
1270
+ code: TranscriptionErrorCode,
1271
+ recoverable: z.boolean()
1272
+ }),
1273
+ TranscriptionEventBase.extend({
1274
+ type: z.literal("session.closed"),
1275
+ reason: z.enum(["completed", "cancelled", "error", "replaced"])
1276
+ })
1277
+ ]);
1278
+ var WorkspaceTranscriptionPolicy = z.object({
1279
+ enabled: z.boolean(),
1280
+ acceptanceId: z.string().uuid().nullable(),
1281
+ primary: WorkspaceTranscriptionTarget.nullable(),
1282
+ language: z.string().trim().min(1).max(64).nullable(),
1283
+ autoDetectLanguage: z.boolean(),
1284
+ diarization: z.object({
1285
+ enabled: z.boolean(),
1286
+ maxSpeakers: z.number().int().min(2).max(100).nullable()
1287
+ }).strict(),
1288
+ retention: z.object({
1289
+ mode: z.enum(["none", "provider-policy"]),
1290
+ maxDays: z.number().int().nonnegative().max(3650).nullable()
1291
+ }).strict(),
1292
+ privacy: z.object({
1293
+ allowProviderLogging: z.boolean(),
1294
+ allowProviderTraining: z.boolean()
1295
+ }).strict(),
1296
+ fallback: z.object({
1297
+ mode: z.enum(["disabled", "explicit"]),
1298
+ targets: z.array(WorkspaceTranscriptionTarget).max(8)
1299
+ }).strict(),
1300
+ cost: z.object({
1301
+ currency: z.literal("USD"),
1302
+ maxPerHour: z.number().finite().nonnegative().max(1e4).nullable(),
1303
+ maxPerMonth: z.number().finite().nonnegative().max(1e6).nullable()
1304
+ }).strict()
1305
+ }).strict().superRefine((policy, context) => {
1306
+ if (policy.enabled && policy.acceptanceId === null) {
1307
+ context.addIssue({
1308
+ code: "custom",
1309
+ path: ["acceptanceId"],
1310
+ message: "enabled transcription requires an accepted policy identity"
1311
+ });
1312
+ }
1313
+ if (policy.enabled && policy.primary === null) {
1314
+ context.addIssue({
1315
+ code: "custom",
1316
+ path: ["primary"],
1317
+ message: "enabled transcription requires a primary target"
1318
+ });
1319
+ }
1320
+ if (policy.enabled && !policy.autoDetectLanguage && policy.language === null) {
1321
+ context.addIssue({
1322
+ code: "custom",
1323
+ path: ["language"],
1324
+ message: "enabled transcription requires a language or accepted automatic detection"
1325
+ });
1326
+ }
1327
+ if (policy.autoDetectLanguage && policy.language !== null) {
1328
+ context.addIssue({
1329
+ code: "custom",
1330
+ path: ["language"],
1331
+ message: "automatic language detection and a fixed language are mutually exclusive"
1332
+ });
1333
+ }
1334
+ if (!policy.diarization.enabled && policy.diarization.maxSpeakers !== null) {
1335
+ context.addIssue({
1336
+ code: "custom",
1337
+ path: ["diarization", "maxSpeakers"],
1338
+ message: "disabled diarization cannot retain a speaker limit"
1339
+ });
1340
+ }
1341
+ if (policy.fallback.mode === "disabled" && policy.fallback.targets.length > 0) {
1342
+ context.addIssue({
1343
+ code: "custom",
1344
+ path: ["fallback", "targets"],
1345
+ message: "disabled fallback cannot retain accepted targets"
1346
+ });
1347
+ }
1348
+ if (policy.fallback.mode === "explicit" && policy.fallback.targets.length === 0) {
1349
+ context.addIssue({
1350
+ code: "custom",
1351
+ path: ["fallback", "targets"],
1352
+ message: "explicit fallback requires at least one accepted target"
1353
+ });
1354
+ }
1355
+ const targetKeys = [policy.primary, ...policy.fallback.targets].filter((target) => target !== null).map(
1356
+ (target) => [
1357
+ target.provider,
1358
+ target.model ?? "",
1359
+ target.credentialMode,
1360
+ target.credentialConnectionId ?? "",
1361
+ target.region ?? ""
1362
+ ].join("\0")
1363
+ );
1364
+ if (new Set(targetKeys).size !== targetKeys.length) {
1365
+ context.addIssue({
1366
+ code: "custom",
1367
+ path: ["fallback", "targets"],
1368
+ message: "transcription targets must be unique"
1369
+ });
1370
+ }
1371
+ });
1372
+ var WorkspaceSettingsSchema = z.object({
1373
+ memoryEnabled: z.boolean().optional(),
1374
+ transcription: WorkspaceTranscriptionPolicy.optional()
1375
+ }).passthrough();
1376
+ function resolveWorkspaceMemoryEnabled(settings) {
1377
+ const parsed = WorkspaceSettingsSchema.safeParse(settings ?? {});
1378
+ return parsed.success ? parsed.data.memoryEnabled === true : false;
1379
+ }
1380
+ var UpdateWorkspaceSettingsRequest = z.object({
1381
+ memoryEnabled: z.boolean().optional(),
1382
+ transcription: WorkspaceTranscriptionPolicy.optional()
1383
+ }).passthrough();
1384
+ var SetWorkspaceDefaultRigRequest = z.object({
1385
+ rigId: z.string().uuid().nullable()
1386
+ });
1387
+ var UpdateWorkspaceModelPolicyRequest = z.object({
1388
+ allowedProviders: z.array(z.string().min(1).max(128)).max(64).nullable().optional(),
1389
+ allowedModels: z.array(z.string().min(1).max(256)).max(256).nullable().optional()
1390
+ });
1391
+ var turnInitiatorIdentityFields = {
1392
+ subjectId: z.string().min(1),
1393
+ /** Immutable display snapshot; never an authorization input. */
1394
+ label: z.string().min(1).optional()
1395
+ };
1396
+ var UNATTRIBUTED_LEGACY_INITIATOR_SUBJECT_ID = "unattributed-legacy";
1397
+ var ServiceTurnInitiator = z.object({
1398
+ kind: z.literal("service"),
1399
+ subjectId: z.string().min(1).max(1024).refine((value) => value !== UNATTRIBUTED_LEGACY_INITIATOR_SUBJECT_ID, {
1400
+ message: "unattributed-legacy is reserved for migrated rows"
1401
+ }),
1402
+ /** Immutable display snapshot; never an authorization input. */
1403
+ label: z.string().min(1).max(256).optional()
1404
+ });
1405
+ var TurnInitiatorContext = z.record(z.string(), z.unknown());
1406
+ var reservedServiceTurnInitiatorContextKeys = /* @__PURE__ */ new Set([
1407
+ "backfill",
1408
+ "label",
1409
+ "provenanceError",
1410
+ "via",
1411
+ "viaTruncated"
1412
+ ]);
1413
+ var ServiceTurnInitiatorContext = TurnInitiatorContext.superRefine((value, ctx) => {
1414
+ for (const key of reservedServiceTurnInitiatorContextKeys) {
1415
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
1416
+ ctx.addIssue({
1417
+ code: z.ZodIssueCode.custom,
1418
+ path: [key],
1419
+ message: `${key} is reserved OpenGeni initiator context`
1420
+ });
1421
+ }
1422
+ }
1423
+ try {
1424
+ if (new TextEncoder().encode(JSON.stringify(value)).byteLength > 4096) {
1425
+ ctx.addIssue({
1426
+ code: z.ZodIssueCode.custom,
1427
+ message: "service initiator context exceeds 4096 UTF-8 bytes"
1428
+ });
1429
+ }
1430
+ } catch {
1431
+ ctx.addIssue({
1432
+ code: z.ZodIssueCode.custom,
1433
+ message: "service initiator context must be JSON-serializable"
1434
+ });
1435
+ }
1436
+ });
468
1437
  var AccountGrant = z.object({
469
1438
  accountId: z.string().uuid(),
470
1439
  subjectId: z.string().min(1),
@@ -479,7 +1448,11 @@ var AccessGrant = z.object({
479
1448
  subjectId: z.string().min(1),
480
1449
  subjectLabel: z.string().optional(),
481
1450
  permissions: z.array(Permission),
482
- metadata: z.record(z.string(), z.unknown()).optional()
1451
+ metadata: z.record(z.string(), z.unknown()).optional(),
1452
+ // Optional trusted causal principal for a command submitted by an embedding
1453
+ // host. Authorization still uses subjectId + permissions above.
1454
+ serviceInitiator: ServiceTurnInitiator.optional(),
1455
+ serviceInitiatorContext: ServiceTurnInitiatorContext.optional()
483
1456
  });
484
1457
  var AccessContext = z.object({
485
1458
  mode: ProductAccessMode,
@@ -496,35 +1469,85 @@ var DelegatedAccessTokenPayload = z.object({
496
1469
  subjectId: z.string().min(1),
497
1470
  subjectLabel: z.string().optional(),
498
1471
  permissions: z.array(Permission).min(1),
1472
+ // Trusted embedding hosts can sign a causal service principal separately
1473
+ // from the grant subject that authorizes the request. The claim is consumed
1474
+ // only when a command creates a new session/turn.
1475
+ serviceInitiator: ServiceTurnInitiator.optional(),
1476
+ serviceInitiatorContext: ServiceTurnInitiatorContext.optional(),
499
1477
  // Worker-asserted session scope for first-party MCP calls (HMAC-signed, not
500
1478
  // agent-controlled); enables session-scoped tools such as goal management.
501
1479
  sessionId: z.string().uuid().optional(),
1480
+ // The turn making the call (the caller's identity), HMAC-signed by the worker
1481
+ // at turn setup. Lets a tool classify WHO is calling from the token itself,
1482
+ // instead of racily re-reading the session's live active_turn_id — e.g. the
1483
+ // sacred-pause guard must know if the CALLER is a machine child-notification
1484
+ // turn, and the active pointer can flip to another turn mid-check.
1485
+ turnId: z.string().uuid().optional(),
1486
+ // Exact execution owner. Agent control commands are accepted only while this
1487
+ // attempt still owns the signed turn.
1488
+ attemptId: z.string().uuid().optional(),
1489
+ executionGeneration: z.number().int().positive().optional(),
502
1490
  exp: z.number().int().positive()
1491
+ }).superRefine((payload, ctx) => {
1492
+ if (payload.serviceInitiatorContext && !payload.serviceInitiator) {
1493
+ ctx.addIssue({
1494
+ code: z.ZodIssueCode.custom,
1495
+ path: ["serviceInitiatorContext"],
1496
+ message: "serviceInitiatorContext requires serviceInitiator"
1497
+ });
1498
+ }
1499
+ if (payload.serviceInitiator && (payload.turnId !== void 0 || payload.attemptId !== void 0 || payload.executionGeneration !== void 0)) {
1500
+ ctx.addIssue({
1501
+ code: z.ZodIssueCode.custom,
1502
+ path: ["serviceInitiator"],
1503
+ message: "serviceInitiator cannot replace an exact agent-attempt initiator"
1504
+ });
1505
+ }
503
1506
  });
1507
+ var delegatedAccessTokenPrefix = "ogd_";
1508
+ var delegatedServiceAccessTokenPrefix = "ogd2_";
504
1509
  async function signDelegatedAccessToken(secret, payload) {
505
- const encodedPayload = base64UrlEncode(JSON.stringify(DelegatedAccessTokenPayload.parse(payload)));
506
- const signature = await hmacSha256Base64Url(secret, encodedPayload);
507
- return `ogd_${encodedPayload}.${signature}`;
1510
+ const parsed = DelegatedAccessTokenPayload.parse(payload);
1511
+ const prefix = parsed.serviceInitiator ? delegatedServiceAccessTokenPrefix : delegatedAccessTokenPrefix;
1512
+ const encodedPayload = base64UrlEncode(JSON.stringify(parsed));
1513
+ const signature = await hmacSha256Base64Url(
1514
+ secret,
1515
+ prefix === delegatedServiceAccessTokenPrefix ? `${prefix}${encodedPayload}` : encodedPayload
1516
+ );
1517
+ return `${prefix}${encodedPayload}.${signature}`;
508
1518
  }
509
1519
  async function verifyDelegatedAccessToken(secret, token, nowSeconds = Math.floor(Date.now() / 1e3)) {
510
- if (!token.startsWith("ogd_")) {
1520
+ const prefix = token.startsWith(delegatedServiceAccessTokenPrefix) ? delegatedServiceAccessTokenPrefix : token.startsWith(delegatedAccessTokenPrefix) ? delegatedAccessTokenPrefix : null;
1521
+ if (!prefix) {
511
1522
  return null;
512
1523
  }
513
- const withoutPrefix = token.slice("ogd_".length);
1524
+ const withoutPrefix = token.slice(prefix.length);
514
1525
  const dot = withoutPrefix.lastIndexOf(".");
515
1526
  if (dot <= 0) {
516
1527
  return null;
517
1528
  }
518
1529
  const encodedPayload = withoutPrefix.slice(0, dot);
519
1530
  const signature = withoutPrefix.slice(dot + 1);
520
- const expected = await hmacSha256Base64Url(secret, encodedPayload);
1531
+ const expected = await hmacSha256Base64Url(
1532
+ secret,
1533
+ prefix === delegatedServiceAccessTokenPrefix ? `${prefix}${encodedPayload}` : encodedPayload
1534
+ );
521
1535
  if (!constantTimeEqual(signature, expected)) {
522
1536
  return null;
523
1537
  }
524
- const payload = DelegatedAccessTokenPayload.safeParse(JSON.parse(base64UrlDecode(encodedPayload)));
1538
+ let decoded;
1539
+ try {
1540
+ decoded = JSON.parse(base64UrlDecode(encodedPayload));
1541
+ } catch {
1542
+ return null;
1543
+ }
1544
+ const payload = DelegatedAccessTokenPayload.safeParse(decoded);
525
1545
  if (!payload.success || payload.data.exp < nowSeconds) {
526
1546
  return null;
527
1547
  }
1548
+ if (prefix === delegatedServiceAccessTokenPrefix !== (payload.data.serviceInitiator !== void 0)) {
1549
+ return null;
1550
+ }
528
1551
  return payload.data;
529
1552
  }
530
1553
  var EnrollmentBearerPayload = z.object({
@@ -813,12 +1836,86 @@ var EntitlementValue = z.union([z.boolean(), z.string(), z.number(), z.array(z.s
813
1836
  var Entitlements = z.record(z.string().min(1), EntitlementValue);
814
1837
  var LimitDecision = z.discriminatedUnion("allowed", [
815
1838
  z.object({ allowed: z.literal(true) }),
816
- z.object({ allowed: z.literal(false), code: z.string(), message: z.string() })
1839
+ z.object({
1840
+ allowed: z.literal(false),
1841
+ code: z.string(),
1842
+ message: z.string()
1843
+ })
817
1844
  ]);
818
1845
  var EntitlementDecision = z.discriminatedUnion("allowed", [
819
1846
  z.object({ allowed: z.literal(true), quantity: z.number().optional() }),
820
- z.object({ allowed: z.literal(false), reason: z.string(), code: z.string().optional(), quantity: z.number().optional() })
1847
+ z.object({
1848
+ allowed: z.literal(false),
1849
+ reason: z.string(),
1850
+ code: z.string().optional(),
1851
+ quantity: z.number().optional()
1852
+ })
821
1853
  ]);
1854
+ var GitCredentialProvider = z.enum(["github", "gitlab", "azure_devops"]);
1855
+ var GitCredentialBindingId = z.string().min(1).max(256);
1856
+ var GitRepositoryAccess = z.enum(["read", "write"]);
1857
+ var GitProviderRepositoryId = z.union([z.number().int().positive(), z.string().min(1)]);
1858
+ var GitCredentialRepositoryRef = z.object({
1859
+ provider: GitCredentialProvider.optional(),
1860
+ credentialBindingId: GitCredentialBindingId.optional(),
1861
+ access: GitRepositoryAccess.optional(),
1862
+ uri: z.string().min(1),
1863
+ ref: z.string().min(1),
1864
+ repositoryId: GitProviderRepositoryId.optional(),
1865
+ installationId: GitProviderRepositoryId.optional(),
1866
+ projectId: GitProviderRepositoryId.optional(),
1867
+ connectionId: z.string().min(1).optional()
1868
+ });
1869
+ var McpConnectionResourceScope = z.object({
1870
+ /** Provider-stable repository identity, serialized as a string on the wire. */
1871
+ id: z.string().min(1).max(512),
1872
+ kind: z.literal("repository")
1873
+ }).strict();
1874
+ var McpConnectionResourceScopes = z.array(McpConnectionResourceScope).min(1).max(256).superRefine((resources, context) => {
1875
+ const seen = /* @__PURE__ */ new Set();
1876
+ for (const [index, resource] of resources.entries()) {
1877
+ const key = `${resource.kind}\0${resource.id}`;
1878
+ if (seen.has(key)) {
1879
+ context.addIssue({
1880
+ code: "custom",
1881
+ message: "selectedResources must not contain duplicates",
1882
+ path: [index]
1883
+ });
1884
+ }
1885
+ seen.add(key);
1886
+ }
1887
+ });
1888
+ var McpServerConnectionRef = z.object({
1889
+ /** Opaque host or standalone connection identifier. */
1890
+ connectionId: z.string().min(1).optional(),
1891
+ /** Stable provider family (for example github, gitlab, or azure_devops). */
1892
+ provider: z.string().min(1).max(128).optional(),
1893
+ /** Provider host or tenant domain. */
1894
+ providerDomain: z.string().min(1),
1895
+ kind: z.enum(["oauth2", "api_key", "app_install", "delegated"]).optional(),
1896
+ scopes: z.array(z.string().min(1)).optional(),
1897
+ /** OAuth resource indicator. This is distinct from selectedResources. */
1898
+ resource: z.string().min(1).optional(),
1899
+ /** Exact provider resources this MCP binding is allowed to operate on. */
1900
+ selectedResources: McpConnectionResourceScopes.optional(),
1901
+ subjectScope: z.enum(["workspace", "subject"]).optional()
1902
+ }).strict().superRefine((reference, context) => {
1903
+ if (!reference.selectedResources) return;
1904
+ if (!reference.connectionId) {
1905
+ context.addIssue({
1906
+ code: "custom",
1907
+ message: "selectedResources requires connectionId",
1908
+ path: ["connectionId"]
1909
+ });
1910
+ }
1911
+ if (!reference.provider) {
1912
+ context.addIssue({
1913
+ code: "custom",
1914
+ message: "selectedResources requires provider",
1915
+ path: ["provider"]
1916
+ });
1917
+ }
1918
+ });
822
1919
  var BillingBalance = z.object({
823
1920
  accountId: z.string().uuid(),
824
1921
  balanceMicros: z.number().int(),
@@ -844,17 +1941,109 @@ var RepositoryResourceRef = z.object({
844
1941
  ref: z.string().min(1),
845
1942
  mountPath: z.string().min(1).optional(),
846
1943
  subpath: z.string().min(1).optional(),
1944
+ provider: GitCredentialProvider.optional(),
1945
+ credentialBindingId: GitCredentialBindingId.optional(),
1946
+ access: GitRepositoryAccess.optional(),
1947
+ repositoryId: GitProviderRepositoryId.optional(),
1948
+ installationId: GitProviderRepositoryId.optional(),
1949
+ projectId: GitProviderRepositoryId.optional(),
1950
+ connectionId: z.string().min(1).optional(),
847
1951
  githubInstallationId: z.number().int().positive().optional(),
848
1952
  githubRepositoryId: z.number().int().positive().optional()
849
1953
  });
1954
+ function positiveGitProviderInteger(value) {
1955
+ if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
1956
+ if (typeof value === "string" && /^\d+$/.test(value) && Number(value) > 0) {
1957
+ return Number(value);
1958
+ }
1959
+ return null;
1960
+ }
1961
+ function gitCredentialProviderForRepository(resource) {
1962
+ if (resource.provider) return resource.provider;
1963
+ if (positiveGitProviderInteger(resource.githubInstallationId) && positiveGitProviderInteger(resource.githubRepositoryId)) {
1964
+ return "github";
1965
+ }
1966
+ return null;
1967
+ }
1968
+ function gitCredentialBindingIdForRepository(resource, provider = gitCredentialProviderForRepository(resource)) {
1969
+ if (!provider) return null;
1970
+ const installationId = provider === "github" ? positiveGitProviderInteger(resource.githubInstallationId ?? resource.installationId) : null;
1971
+ return resource.credentialBindingId ?? resource.connectionId ?? (installationId ? `github-installation:${installationId}` : provider);
1972
+ }
850
1973
  var FileResourceRef = z.object({
851
1974
  kind: z.literal("file"),
852
1975
  fileId: z.string().uuid(),
853
1976
  mountPath: z.string().min(1).optional()
854
1977
  });
855
1978
  var ResourceRef = z.discriminatedUnion("kind", [RepositoryResourceRef, FileResourceRef]);
1979
+ var ResourceMountPathError = class extends Error {
1980
+ constructor(message) {
1981
+ super(message);
1982
+ this.name = "ResourceMountPathError";
1983
+ }
1984
+ };
1985
+ function normalizeResourceMountPath(path) {
1986
+ const normalizedSeparators = path.trim().replace(/\\/g, "/");
1987
+ if (!normalizedSeparators || normalizedSeparators.startsWith("/") || /^[A-Za-z]:\//.test(normalizedSeparators) || normalizedSeparators.includes("\0")) {
1988
+ throw new ResourceMountPathError(`invalid resource mount path: ${path}`);
1989
+ }
1990
+ const segments = normalizedSeparators.split("/");
1991
+ if (segments.some(
1992
+ (segment) => !segment || segment === "." || segment === ".." || /[<>:"|?*\u0000-\u001f]/.test(segment) || /[ .]$/.test(segment) || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(segment)
1993
+ )) {
1994
+ throw new ResourceMountPathError(`invalid resource mount path: ${path}`);
1995
+ }
1996
+ return segments.join("/");
1997
+ }
1998
+ function normalizeRepositorySubpath(path) {
1999
+ const relative = path.trim().replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
2000
+ return normalizeResourceMountPath(relative);
2001
+ }
2002
+ function resourceMountPathCollisionKey(path) {
2003
+ return normalizeResourceMountPath(path).normalize("NFKC").toLowerCase();
2004
+ }
2005
+ function defaultRepositoryMountPath(uri) {
2006
+ let url;
2007
+ try {
2008
+ url = new URL(uri);
2009
+ } catch {
2010
+ throw new ResourceMountPathError(`invalid repository URI for mount path: ${uri}`);
2011
+ }
2012
+ if (url.protocol !== "https:" || !url.host) {
2013
+ throw new ResourceMountPathError(`invalid repository URI for mount path: ${uri}`);
2014
+ }
2015
+ const repositoryPath = url.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
2016
+ const segments = repositoryPath.split("/").filter(Boolean);
2017
+ if (segments.length < 2) {
2018
+ throw new ResourceMountPathError(`repository URI must include owner and repo: ${uri}`);
2019
+ }
2020
+ return normalizeResourceMountPath(
2021
+ `repos/${encodeURIComponent(url.host.toLowerCase())}/${segments.join("/")}`
2022
+ );
2023
+ }
2024
+ function resourceMountPath(resource) {
2025
+ if (resource.mountPath) return normalizeResourceMountPath(resource.mountPath);
2026
+ return resource.kind === "file" ? normalizeResourceMountPath(`files/${resource.fileId}`) : defaultRepositoryMountPath(resource.uri);
2027
+ }
2028
+ function assertUniqueResourceMountPaths(resources) {
2029
+ const mounted = /* @__PURE__ */ new Set();
2030
+ for (const resource of resources) {
2031
+ const path = resourceMountPath(resource);
2032
+ const key = resourceMountPathCollisionKey(path);
2033
+ if (mounted.has(key)) {
2034
+ throw new ResourceRefConflictError(`resource mount path is already attached: ${path}`);
2035
+ }
2036
+ mounted.add(key);
2037
+ }
2038
+ }
856
2039
  var FileStatus = z.enum(["pending_upload", "ready", "failed", "expired", "deleted"]);
857
- var FileUploadStatus = z.enum(["pending", "completed", "expired", "failed"]);
2040
+ var FileUploadStatus = z.enum([
2041
+ "pending",
2042
+ "cleanup_pending",
2043
+ "completed",
2044
+ "expired",
2045
+ "failed"
2046
+ ]);
858
2047
  var FileAsset = z.object({
859
2048
  id: z.string().uuid(),
860
2049
  workspaceId: z.string().uuid(),
@@ -891,7 +2080,16 @@ var FileDownloadUrlResponse = z.object({
891
2080
  expiresAt: z.string()
892
2081
  });
893
2082
  var DocumentStatus = z.enum(["queued", "indexing", "ready", "failed"]);
894
- var KnowledgeSourceKind = z.enum(["manual_upload", "meeting_transcript", "repository", "email", "chat", "document", "web", "other"]);
2083
+ var KnowledgeSourceKind = z.enum([
2084
+ "manual_upload",
2085
+ "meeting_transcript",
2086
+ "repository",
2087
+ "email",
2088
+ "chat",
2089
+ "document",
2090
+ "web",
2091
+ "other"
2092
+ ]);
895
2093
  var DocumentSearchMode = z.enum(["hybrid", "vector", "keyword"]);
896
2094
  var DocumentBase = z.object({
897
2095
  id: z.string().uuid(),
@@ -972,8 +2170,21 @@ var DocumentSearchRequest = z.object({
972
2170
  aclTags: z.array(z.string().min(1)).optional(),
973
2171
  limit: z.number().int().positive().max(50).default(5)
974
2172
  });
975
- var KnowledgeMemoryStatus = z.enum(["proposed", "approved", "rejected"]);
976
- var KnowledgeMemoryKind = z.enum(["semantic", "episodic", "procedural", "decision", "preference"]);
2173
+ var KnowledgeMemoryStatus = z.enum([
2174
+ "proposed",
2175
+ "approved",
2176
+ "rejected",
2177
+ "active",
2178
+ "superseded",
2179
+ "archived"
2180
+ ]);
2181
+ var KnowledgeMemoryKind = z.enum([
2182
+ "semantic",
2183
+ "episodic",
2184
+ "procedural",
2185
+ "decision",
2186
+ "preference"
2187
+ ]);
977
2188
  var KnowledgeSourceRef = z.object({
978
2189
  kind: z.enum(["document_chunk", "document", "session_event", "memory", "external"]),
979
2190
  id: z.string().min(1),
@@ -994,18 +2205,31 @@ var KnowledgeMemory = z.object({
994
2205
  createdBySessionId: z.string().uuid().nullable(),
995
2206
  reviewedBy: z.string().nullable(),
996
2207
  reviewedAt: z.string().nullable(),
2208
+ // Workspace Memory V1 fields. usageCount/lastUsedAt feed end-state ranking and
2209
+ // decay; supersedesId/supersededById link correction chains; validFrom/validUntil
2210
+ // are the point-in-time window. embedding/embeddingModel/textHash are internal
2211
+ // and never exposed on the wire.
2212
+ pinned: z.boolean(),
2213
+ usageCount: z.number().int(),
2214
+ lastUsedAt: z.string().nullable(),
2215
+ supersedesId: z.string().uuid().nullable(),
2216
+ supersededById: z.string().uuid().nullable(),
2217
+ validFrom: z.string(),
2218
+ validUntil: z.string().nullable(),
997
2219
  createdAt: z.string(),
998
2220
  updatedAt: z.string()
999
2221
  });
1000
2222
  var CreateKnowledgeMemoryRequest = z.object({
1001
- status: KnowledgeMemoryStatus.default("proposed"),
2223
+ status: KnowledgeMemoryStatus.default("active"),
1002
2224
  kind: KnowledgeMemoryKind.default("semantic"),
1003
2225
  scope: z.string().min(1).default("workspace"),
1004
2226
  text: z.string().min(1),
1005
2227
  sourceRefs: z.array(KnowledgeSourceRef).default([]),
1006
2228
  confidence: z.number().min(0).max(1).default(0.5),
1007
2229
  metadata: z.record(z.string(), z.unknown()).default({}),
1008
- createdBySessionId: z.string().uuid().optional()
2230
+ createdBySessionId: z.string().uuid().optional(),
2231
+ pinned: z.boolean().optional(),
2232
+ replacesId: z.string().min(1).optional()
1009
2233
  });
1010
2234
  var UpdateKnowledgeMemoryRequest = z.object({
1011
2235
  status: KnowledgeMemoryStatus.optional(),
@@ -1015,7 +2239,9 @@ var UpdateKnowledgeMemoryRequest = z.object({
1015
2239
  sourceRefs: z.array(KnowledgeSourceRef).optional(),
1016
2240
  confidence: z.number().min(0).max(1).optional(),
1017
2241
  metadata: z.record(z.string(), z.unknown()).optional(),
1018
- reviewedBy: z.string().min(1).optional()
2242
+ reviewedBy: z.string().min(1).optional(),
2243
+ // Human audit action: pin (never decays) / unpin.
2244
+ pinned: z.boolean().optional()
1019
2245
  });
1020
2246
  var KnowledgeMemorySearchRequest = z.object({
1021
2247
  query: z.string().min(1).optional(),
@@ -1024,6 +2250,23 @@ var KnowledgeMemorySearchRequest = z.object({
1024
2250
  scope: z.string().min(1).optional(),
1025
2251
  limit: z.number().int().positive().max(100).default(20)
1026
2252
  });
2253
+ var WorkspaceMemorySearchMode = z.enum(["hybrid", "vector", "keyword"]);
2254
+ var WorkspaceMemorySearchRequest = z.object({
2255
+ query: z.string().min(1),
2256
+ kind: KnowledgeMemoryKind.optional(),
2257
+ limit: z.number().int().positive().max(20).optional(),
2258
+ mode: WorkspaceMemorySearchMode.optional()
2259
+ });
2260
+ var WorkspaceMemorySearchResult = z.object({
2261
+ memory: KnowledgeMemory,
2262
+ score: z.number(),
2263
+ matchType: WorkspaceMemorySearchMode,
2264
+ vectorScore: z.number().nullable(),
2265
+ keywordScore: z.number().nullable()
2266
+ });
2267
+ var WorkspaceMemorySearchResponse = z.object({
2268
+ results: z.array(WorkspaceMemorySearchResult)
2269
+ });
1027
2270
  var ToolRef = z.object({
1028
2271
  kind: z.literal("mcp"),
1029
2272
  id: z.string().min(1),
@@ -1036,13 +2279,16 @@ var ToolRef = z.object({
1036
2279
  optional: z.boolean().optional()
1037
2280
  });
1038
2281
  var registryId = /^[A-Za-z0-9_-]+$/;
1039
- var httpsUrl = z.string().url().refine((value) => {
1040
- try {
1041
- return new URL(value).protocol === "https:";
1042
- } catch {
1043
- return false;
1044
- }
1045
- }, { message: "URL must use https" });
2282
+ var httpsUrl = z.string().url().refine(
2283
+ (value) => {
2284
+ try {
2285
+ return new URL(value).protocol === "https:";
2286
+ } catch {
2287
+ return false;
2288
+ }
2289
+ },
2290
+ { message: "URL must use https" }
2291
+ );
1046
2292
  var SessionMcpServerInput = z.object({
1047
2293
  id: z.string().min(1).regex(registryId),
1048
2294
  name: z.string().min(1).optional(),
@@ -1058,7 +2304,10 @@ var SessionMcpServerInput = z.object({
1058
2304
  requireApproval: z.union([z.boolean(), z.array(z.string().min(1))]).optional(),
1059
2305
  // Write-only credential headers. Values are encrypted at rest and never
1060
2306
  // returned in session responses or events; response metadata exposes names.
1061
- headers: z.record(z.string(), z.string()).optional()
2307
+ headers: z.record(z.string(), z.string()).optional(),
2308
+ // Non-secret opaque pointer resolved at request time by the standalone
2309
+ // connection broker or an embedding host's mcpCredentials port.
2310
+ connectionRef: McpServerConnectionRef.optional()
1062
2311
  });
1063
2312
  var SessionMcpCredentialUpdateInput = z.object({
1064
2313
  id: z.string().min(1).regex(registryId),
@@ -1069,7 +2318,8 @@ var SessionMcpServerMetadata = z.object({
1069
2318
  name: z.string().min(1).nullable(),
1070
2319
  url: httpsUrl,
1071
2320
  headerNames: z.array(z.string()).default([]),
1072
- credentialVersion: z.number().int().positive()
2321
+ credentialVersion: z.number().int().positive(),
2322
+ connectionRef: McpServerConnectionRef.nullable().default(null)
1073
2323
  }).strict();
1074
2324
  var ResourceRefConflictError = class extends Error {
1075
2325
  constructor(message) {
@@ -1096,9 +2346,18 @@ function mergeToolRefs(existing, additions) {
1096
2346
  return order.map((key) => byKey.get(key));
1097
2347
  }
1098
2348
  function mergeResourceRefs(existing, additions, options = {}) {
2349
+ if (options.rejectConflicts) {
2350
+ assertUniqueResourceMountPaths(existing);
2351
+ }
1099
2352
  const out = [...existing];
1100
- const mountPaths = new Map(existing.flatMap((resource) => resource.mountPath ? [[resource.mountPath, stableJson(resource)]] : []));
1101
- const identities = new Map(existing.map((resource) => [resourceIdentityKey(resource), stableJson(resource)]));
2353
+ const mountPaths = new Map(
2354
+ existing.map(
2355
+ (resource) => [resourceMountPathCollisionKey(resourceMountPath(resource)), stableJson(resource)]
2356
+ )
2357
+ );
2358
+ const identities = new Map(
2359
+ existing.map((resource) => [resourceIdentityKey(resource), stableJson(resource)])
2360
+ );
1102
2361
  const exact = new Set(existing.map(stableJson));
1103
2362
  for (const resource of additions) {
1104
2363
  const serialized = stableJson(resource);
@@ -1106,22 +2365,23 @@ function mergeResourceRefs(existing, additions, options = {}) {
1106
2365
  continue;
1107
2366
  }
1108
2367
  if (options.rejectConflicts) {
1109
- const existingAtMount = resource.mountPath ? mountPaths.get(resource.mountPath) : void 0;
2368
+ const mountPath = resourceMountPath(resource);
2369
+ const existingAtMount = mountPaths.get(resourceMountPathCollisionKey(mountPath));
1110
2370
  if (existingAtMount && existingAtMount !== serialized) {
1111
- throw new ResourceRefConflictError(`resource mount path is already attached: ${resource.mountPath}`);
2371
+ throw new ResourceRefConflictError(`resource mount path is already attached: ${mountPath}`);
1112
2372
  }
1113
2373
  const identity = resourceIdentityKey(resource);
1114
2374
  const existingIdentity = identities.get(identity);
1115
2375
  if (existingIdentity && existingIdentity !== serialized) {
1116
- throw new ResourceRefConflictError(`resource is already attached with different settings: ${identity}`);
2376
+ throw new ResourceRefConflictError(
2377
+ `resource is already attached with different settings: ${identity}`
2378
+ );
1117
2379
  }
1118
2380
  }
1119
2381
  out.push(resource);
1120
2382
  exact.add(serialized);
1121
2383
  identities.set(resourceIdentityKey(resource), serialized);
1122
- if (resource.mountPath) {
1123
- mountPaths.set(resource.mountPath, serialized);
1124
- }
2384
+ mountPaths.set(resourceMountPathCollisionKey(resourceMountPath(resource)), serialized);
1125
2385
  }
1126
2386
  return out;
1127
2387
  }
@@ -1143,17 +2403,39 @@ function sortJson(value) {
1143
2403
  return value.map(sortJson);
1144
2404
  }
1145
2405
  if (value && typeof value === "object") {
1146
- return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, nested]) => [key, sortJson(nested)]));
2406
+ return Object.fromEntries(
2407
+ Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, nested]) => [key, sortJson(nested)])
2408
+ );
1147
2409
  }
1148
2410
  return value;
1149
2411
  }
1150
- var SessionTurnStatus = z.enum(["queued", "running", "requires_action", "completed", "failed", "cancelled"]);
1151
- var SessionTurnSource = z.enum(["user", "scheduled_task", "api", "goal"]);
2412
+ var SessionTurnStatus = z.enum([
2413
+ "queued",
2414
+ "running",
2415
+ "requires_action",
2416
+ "recovering",
2417
+ "waiting_capacity",
2418
+ "completed",
2419
+ "failed",
2420
+ "cancelled",
2421
+ "superseded",
2422
+ "withdrawn_for_edit"
2423
+ ]);
2424
+ var SessionTurnSource = z.enum([
2425
+ "user",
2426
+ "scheduled_task",
2427
+ "api",
2428
+ "goal",
2429
+ "system",
2430
+ "compaction"
2431
+ ]);
2432
+ var SessionControlState = z.enum(["active", "paused"]);
2433
+ var WorkspaceInferenceState = z.enum(["active", "paused"]);
1152
2434
  var SessionGoalStatus = z.enum(["active", "paused", "completed"]);
1153
2435
  var SessionGoalCreatedBy = z.enum(["api", "agent", "scheduled_task"]);
1154
2436
  var SessionGoalPausedReason = z.enum([
1155
2437
  "agent",
1156
- "user_interrupt",
2438
+ "user_pause",
1157
2439
  "api",
1158
2440
  "no_progress",
1159
2441
  "max_auto_continuations",
@@ -1191,11 +2473,17 @@ var UpdateSessionGoalRequest = z.object({
1191
2473
  var UpdateSessionRequest = z.object({
1192
2474
  title: z.string().min(1).max(200)
1193
2475
  });
2476
+ var UpdateSessionPinRequest = z.object({
2477
+ pinned: z.boolean(),
2478
+ expectedVersion: z.number().int().nonnegative().optional()
2479
+ });
1194
2480
  var ClearSessionContextRequest = z.object({
1195
2481
  confirm: z.literal(true)
1196
2482
  });
1197
2483
  var CLEARED_RUN_STATE_MARKER = "$opengeniCleared";
1198
- var CLEARED_RUN_STATE_BLOB = JSON.stringify({ [CLEARED_RUN_STATE_MARKER]: true });
2484
+ var CLEARED_RUN_STATE_BLOB = JSON.stringify({
2485
+ [CLEARED_RUN_STATE_MARKER]: true
2486
+ });
1199
2487
  function isClearedRunStateBlob(serialized) {
1200
2488
  if (!serialized) {
1201
2489
  return false;
@@ -1209,11 +2497,110 @@ function isClearedRunStateBlob(serialized) {
1209
2497
  }
1210
2498
  var CompactSessionContextRequest = z.object({}).strict();
1211
2499
  var CompactSessionContextResult = z.object({
1212
- // queued: a client-side (Azure) compaction will run before the next turn.
1213
- // noop: nothing to do (server-managed provider, mode off, or no history).
1214
- status: z.enum(["queued", "noop"]),
2500
+ // pending: an active/paused session will compact at its next safe boundary.
2501
+ // completed: an idle compaction-only activity completed synchronously.
2502
+ // noop: there is no active history to compact.
2503
+ status: z.enum(["pending", "completed", "noop"]),
1215
2504
  message: z.string()
1216
2505
  });
2506
+ var TurnInitiator = z.object({
2507
+ kind: z.enum(["subject", "service"]),
2508
+ ...turnInitiatorIdentityFields
2509
+ });
2510
+ var SessionAuthorizationSurface = z.enum([
2511
+ "http",
2512
+ "core",
2513
+ "stream",
2514
+ "first_party_mcp",
2515
+ "toolspace"
2516
+ ]);
2517
+ var SessionAuthorizationOperation = z.enum([
2518
+ "session.read",
2519
+ "session.events.read",
2520
+ "session.stream.read",
2521
+ "session.stream.acknowledge",
2522
+ "session.turns.read",
2523
+ "session.append",
2524
+ "session.steer",
2525
+ "session.control",
2526
+ "session.queue.read",
2527
+ "session.queue.control",
2528
+ "session.composer.read",
2529
+ "session.composer.write",
2530
+ "session.lineage.read",
2531
+ "session.capture.read",
2532
+ "session.files.read",
2533
+ "session.files.write",
2534
+ "session.git.read",
2535
+ "session.terminal.read",
2536
+ "session.terminal.control",
2537
+ "session.viewer.read",
2538
+ "session.viewer.control",
2539
+ "session.first_party_mcp.call",
2540
+ "session.toolspace.call",
2541
+ "session.pin.write",
2542
+ "session.codex_account.write",
2543
+ "session.context.write",
2544
+ "session.approval.write",
2545
+ "session.human_input.read",
2546
+ "session.human_input.write",
2547
+ "session.title.write",
2548
+ "session.goal.read",
2549
+ "session.goal.write",
2550
+ "session.child.create"
2551
+ ]);
2552
+ var SessionAuthorizationActor = z.discriminatedUnion("kind", [
2553
+ z.object({
2554
+ kind: z.literal("subject"),
2555
+ subjectId: z.string().min(1),
2556
+ subjectLabel: z.string().min(1).optional()
2557
+ }),
2558
+ z.object({
2559
+ kind: z.literal("agent_attempt"),
2560
+ /** Technical, authenticated first-party caller (not the host authority). */
2561
+ subjectId: z.string().min(1),
2562
+ callerSessionId: z.string().uuid(),
2563
+ callerRootSessionId: z.string().uuid(),
2564
+ turnId: z.string().uuid(),
2565
+ attemptId: z.string().uuid(),
2566
+ executionGeneration: z.number().int().positive(),
2567
+ /** Frozen authority that admitted the calling turn. */
2568
+ initiator: TurnInitiator,
2569
+ initiatorContext: TurnInitiatorContext
2570
+ })
2571
+ ]);
2572
+ var SessionAuthorizationTarget = z.object({
2573
+ sessionId: z.string().uuid(),
2574
+ /** Server-resolved workspace lineage root; never accepted from a caller. */
2575
+ rootSessionId: z.string().uuid()
2576
+ });
2577
+ var SessionAuthorizationDecision = z.discriminatedUnion("allowed", [
2578
+ z.object({
2579
+ allowed: z.literal(true),
2580
+ /**
2581
+ * Whether related-session metadata may be projected with the target.
2582
+ * `target` is the fail-closed default for exact shares; `root` permits the
2583
+ * target's full lineage tree. This does not authorize a separate operation
2584
+ * against another session, which always requires its own decision.
2585
+ */
2586
+ relatedSessionAccess: z.enum(["target", "root"]).optional(),
2587
+ /** A host may request a tighter stream reauthorization bound. */
2588
+ reauthorizeAfterMs: z.number().int().min(1e3).max(6e4).optional()
2589
+ }),
2590
+ z.object({
2591
+ allowed: z.literal(false),
2592
+ reason: z.enum(["not_found", "forbidden", "revoked"])
2593
+ })
2594
+ ]);
2595
+ var SESSION_AUTHORIZATION_LIST_SCOPE_MAX_IDS = 1e4;
2596
+ var SessionAuthorizationListScope = z.discriminatedUnion("kind", [
2597
+ z.object({ kind: z.literal("all") }),
2598
+ z.object({
2599
+ kind: z.literal("scoped"),
2600
+ rootSessionIds: z.array(z.string().uuid()).max(SESSION_AUTHORIZATION_LIST_SCOPE_MAX_IDS),
2601
+ sessionIds: z.array(z.string().uuid()).max(SESSION_AUTHORIZATION_LIST_SCOPE_MAX_IDS)
2602
+ })
2603
+ ]);
1217
2604
  var SessionTurn = z.object({
1218
2605
  id: z.string().uuid(),
1219
2606
  workspaceId: z.string().uuid(),
@@ -1222,7 +2609,7 @@ var SessionTurn = z.object({
1222
2609
  temporalWorkflowId: z.string(),
1223
2610
  status: SessionTurnStatus,
1224
2611
  source: SessionTurnSource,
1225
- position: z.number().int().positive(),
2612
+ position: z.number().int(),
1226
2613
  prompt: z.string().min(1),
1227
2614
  resources: z.array(ResourceRef),
1228
2615
  tools: z.array(ToolRef),
@@ -1232,55 +2619,492 @@ var SessionTurn = z.object({
1232
2619
  // Per-turn OS override. NULL = inherit the session's sandboxOs.
1233
2620
  sandboxOs: SandboxOs.nullable(),
1234
2621
  metadata: z.record(z.string(), z.unknown()),
2622
+ version: z.number().int().positive(),
2623
+ executionGeneration: z.number().int().nonnegative(),
2624
+ activeAttemptId: z.string().uuid().nullable(),
2625
+ lineage: z.record(z.string(), z.unknown()),
2626
+ initiator: TurnInitiator,
2627
+ initiatorContext: TurnInitiatorContext,
2628
+ cancelledBy: z.string().nullable(),
2629
+ cancelReason: z.string().nullable(),
1235
2630
  startedAt: z.string().nullable(),
1236
2631
  finishedAt: z.string().nullable(),
1237
2632
  createdAt: z.string(),
1238
2633
  updatedAt: z.string()
1239
2634
  });
1240
- var UpdateSessionTurnRequest = z.object({
1241
- prompt: z.string().min(1).optional(),
1242
- resources: z.array(ResourceRef).optional(),
1243
- tools: z.array(ToolRef).optional(),
1244
- model: z.string().min(1).optional(),
1245
- reasoningEffort: ReasoningEffort.optional(),
1246
- sandboxBackend: SandboxBackend.optional(),
1247
- metadata: z.record(z.string(), z.unknown()).optional()
2635
+ var EffectiveControlBlocker = z.object({
2636
+ kind: z.enum(["session", "workspace"]),
2637
+ sessionId: z.string().uuid().optional(),
2638
+ displayName: z.string().min(1),
2639
+ actor: z.string().nullable(),
2640
+ reason: z.string().nullable(),
2641
+ changedAt: z.string().nullable(),
2642
+ revision: z.number().int().nonnegative()
2643
+ });
2644
+ var EffectiveControlResumeOption = z.object({
2645
+ scope: z.enum(["selected", "session", "workspace"]),
2646
+ targetId: z.string().uuid().optional(),
2647
+ selectedStateAfter: SessionControlState,
2648
+ remainingPrimaryBlocker: EffectiveControlBlocker.optional(),
2649
+ impactCopy: z.string().min(1)
2650
+ });
2651
+ var EffectiveSessionControl = z.object({
2652
+ state: SessionControlState,
2653
+ controlVersion: z.number().int().nonnegative(),
2654
+ controlEtag: z.string().min(1),
2655
+ directState: SessionControlState,
2656
+ primaryBlocker: EffectiveControlBlocker.nullable(),
2657
+ additionalBlockerCount: z.number().int().nonnegative(),
2658
+ blockers: z.array(EffectiveControlBlocker),
2659
+ resumeOptions: z.array(EffectiveControlResumeOption),
2660
+ override: z.object({
2661
+ rootSessionId: z.string().uuid(),
2662
+ revision: z.number().int().nonnegative()
2663
+ }).nullable(),
2664
+ settlement: z.object({
2665
+ state: z.literal("stopping"),
2666
+ attemptCount: z.number().int().positive(),
2667
+ interruptionPendingCount: z.number().int().nonnegative(),
2668
+ quiescencePendingCount: z.number().int().nonnegative()
2669
+ }).nullable()
2670
+ });
2671
+ var SESSION_OPERATION_KEY_MAX_CHARS = 256;
2672
+ var SessionOperationKey = z.string().min(1).max(SESSION_OPERATION_KEY_MAX_CHARS);
2673
+ var SessionCommandReceipt = z.object({
2674
+ id: z.string().uuid(),
2675
+ action: z.string().min(1),
2676
+ operationKey: z.string().min(1).max(SESSION_OPERATION_KEY_MAX_CHARS),
2677
+ targetSessionId: z.string().uuid().nullable(),
2678
+ targetTurnId: z.string().uuid().nullable(),
2679
+ appliedControlRevision: z.number().int().nonnegative().nullable(),
2680
+ appliedQueueVersion: z.number().int().nonnegative().nullable(),
2681
+ appliedTurnVersion: z.number().int().positive().nullable(),
2682
+ appliedDraftRevision: z.number().int().positive().nullable(),
2683
+ createdAt: z.string()
1248
2684
  });
1249
- var ReorderSessionTurnsRequest = z.object({
1250
- turnIds: z.array(z.string().uuid()).min(1)
2685
+ var ComposerDraft = z.object({
2686
+ revision: z.number().int().nonnegative(),
2687
+ text: z.string(),
2688
+ resources: z.array(ResourceRef),
2689
+ tools: z.array(ToolRef),
2690
+ model: z.string().min(1),
2691
+ reasoningEffort: ReasoningEffort,
2692
+ sourceTurnId: z.string().uuid().nullable(),
2693
+ sourceTurnVersion: z.number().int().positive().nullable(),
2694
+ updatedAt: z.string().nullable()
2695
+ });
2696
+ var SessionQueueSnapshot = z.object({
2697
+ version: z.number().int().nonnegative(),
2698
+ effectiveControl: EffectiveSessionControl,
2699
+ /**
2700
+ * True while the latest attempt is interrupted but has not durably proved
2701
+ * quiescence: no more inference, user-visible output, or workspace-persistence
2702
+ * authority. Temporal cancellation/terminalization is not that proof. This is
2703
+ * distinct from ordinary capacity queueing, remains accurate with an empty
2704
+ * visible queue, and is independent of Steer-row metadata or withdrawal.
2705
+ */
2706
+ stoppingPreviousAttempt: z.boolean(),
2707
+ items: z.array(SessionTurn)
2708
+ });
2709
+ var MoveSessionQueueItemRequest = z.object({
2710
+ clientEventId: SessionOperationKey,
2711
+ expectedQueueVersion: z.number().int().nonnegative(),
2712
+ beforeTurnId: z.string().uuid().nullable()
2713
+ });
2714
+ var EditSessionQueueItemRequest = z.object({
2715
+ clientEventId: SessionOperationKey,
2716
+ expectedTurnVersion: z.number().int().positive(),
2717
+ expectedDraftRevision: z.number().int().nonnegative(),
2718
+ replaceDraft: z.boolean()
2719
+ });
2720
+ var SteerSessionQueueItemRequest = z.object({
2721
+ clientEventId: SessionOperationKey,
2722
+ expectedTurnVersion: z.number().int().positive(),
2723
+ controlEtag: z.string().min(1).optional()
2724
+ });
2725
+ var DeleteSessionQueueItemRequest = z.object({
2726
+ clientEventId: SessionOperationKey,
2727
+ expectedTurnVersion: z.number().int().positive(),
2728
+ reason: z.string().min(1).optional()
2729
+ });
2730
+ var SaveComposerDraftRequest = ComposerDraft.pick({
2731
+ text: true,
2732
+ resources: true,
2733
+ tools: true,
2734
+ model: true,
2735
+ reasoningEffort: true
2736
+ }).extend({ expectedRevision: z.number().int().nonnegative() });
2737
+ var WORKSPACE_CONTROL_REASON_MAX_BYTES = 8 * 1024;
2738
+ var WORKSPACE_CONTROL_ACTOR_MAX_BYTES = 1024;
2739
+ var WORKSPACE_CONTROL_EVENT_MAX_BYTES = 16 * 1024;
2740
+ var WorkspaceControlReason = z.string().min(1).refine((value) => !value.includes("\0"), "reason must not contain NUL bytes").refine(
2741
+ (value) => workspaceControlUtf8Bytes(value) <= WORKSPACE_CONTROL_REASON_MAX_BYTES,
2742
+ `reason must not exceed ${WORKSPACE_CONTROL_REASON_MAX_BYTES} UTF-8 bytes`
2743
+ );
2744
+ var SessionControlRequest = z.object({
2745
+ action: z.enum(["pause", "resume"]),
2746
+ reason: WorkspaceControlReason.optional(),
2747
+ clientEventId: SessionOperationKey,
2748
+ expectedControlEtag: z.string().min(1).optional()
2749
+ });
2750
+ var WorkspaceInferenceControlRequest = z.object({
2751
+ action: z.enum(["pause", "resume"]),
2752
+ reason: WorkspaceControlReason.optional(),
2753
+ clientEventId: SessionOperationKey,
2754
+ expectedRevision: z.number().int().nonnegative().optional()
2755
+ });
2756
+ var WorkspaceInferenceControlResponse = z.object({
2757
+ receipt: SessionCommandReceipt,
2758
+ state: SessionControlState,
2759
+ revision: z.number().int().nonnegative(),
2760
+ interruptionCount: z.number().int().nonnegative(),
2761
+ wakeCount: z.number().int().nonnegative()
2762
+ });
2763
+ var WorkspaceControlEventTruncation = z.object({
2764
+ truncated: z.literal(true),
2765
+ surface: z.enum([
2766
+ "durable_control",
2767
+ "database_guard",
2768
+ "http_projection",
2769
+ "nats_legacy_guard",
2770
+ "sse_legacy_guard"
2771
+ ]),
2772
+ deliveredBytes: z.number().int().nonnegative(),
2773
+ fields: z.array(
2774
+ z.object({
2775
+ field: z.enum(["reason", "actor"]),
2776
+ originalBytes: z.number().int().nonnegative(),
2777
+ deliveredBytes: z.number().int().nonnegative(),
2778
+ omittedBytes: z.number().int().nonnegative()
2779
+ })
2780
+ ),
2781
+ fullEvidence: z.object({
2782
+ available: z.literal(false),
2783
+ reason: z.literal("not_retained")
2784
+ })
2785
+ });
2786
+ var WorkspaceControlEvent = z.object({
2787
+ id: z.string().uuid(),
2788
+ workspaceId: z.string().uuid(),
2789
+ sequence: z.number().int().positive(),
2790
+ revision: z.number().int().positive(),
2791
+ type: z.literal("workspace.control.changed"),
2792
+ scope: z.enum(["workspace", "session"]),
2793
+ rootSessionId: z.string().uuid().nullable(),
2794
+ action: z.enum(["pause", "resume"]),
2795
+ automatic: z.boolean(),
2796
+ reason: z.string().nullable(),
2797
+ actor: z.string().min(1),
2798
+ occurredAt: z.string(),
2799
+ truncation: WorkspaceControlEventTruncation.nullable().optional()
2800
+ });
2801
+ function workspaceControlUtf8Bytes(value) {
2802
+ return new TextEncoder().encode(value).byteLength;
2803
+ }
2804
+ function boundWorkspaceControlEvent(event, options = {}) {
2805
+ const existingFields = new Map(
2806
+ (event.truncation?.fields ?? []).map((field) => [field.field, field])
2807
+ );
2808
+ const reason = event.reason === null ? null : boundWorkspaceControlText(event.reason, WORKSPACE_CONTROL_REASON_MAX_BYTES);
2809
+ const actor = boundWorkspaceControlText(event.actor, WORKSPACE_CONTROL_ACTOR_MAX_BYTES);
2810
+ const reasonBytes = reason === null ? 0 : workspaceControlUtf8Bytes(reason);
2811
+ const actorBytes = workspaceControlUtf8Bytes(actor);
2812
+ const reasonOriginalBytes = event.reason === null ? null : Math.max(
2813
+ workspaceControlUtf8Bytes(event.reason),
2814
+ normalizedWorkspaceControlOriginalBytes(options.reasonOriginalBytes),
2815
+ existingFields.get("reason")?.originalBytes ?? 0
2816
+ );
2817
+ const actorOriginalBytes = Math.max(
2818
+ workspaceControlUtf8Bytes(event.actor),
2819
+ normalizedWorkspaceControlOriginalBytes(options.actorOriginalBytes),
2820
+ existingFields.get("actor")?.originalBytes ?? 0
2821
+ );
2822
+ const fields = [];
2823
+ if (reasonOriginalBytes !== null && reasonOriginalBytes > reasonBytes) {
2824
+ fields.push({
2825
+ field: "reason",
2826
+ originalBytes: reasonOriginalBytes,
2827
+ deliveredBytes: reasonBytes,
2828
+ omittedBytes: reasonOriginalBytes - reasonBytes
2829
+ });
2830
+ }
2831
+ if (actorOriginalBytes > actorBytes) {
2832
+ fields.push({
2833
+ field: "actor",
2834
+ originalBytes: actorOriginalBytes,
2835
+ deliveredBytes: actorBytes,
2836
+ omittedBytes: actorOriginalBytes - actorBytes
2837
+ });
2838
+ }
2839
+ if (fields.length === 0 && event.truncation == null) {
2840
+ if (sessionEventJsonBytes(event) > WORKSPACE_CONTROL_EVENT_MAX_BYTES) {
2841
+ throw new RangeError("Workspace control event exceeds its bounded envelope");
2842
+ }
2843
+ return event;
2844
+ }
2845
+ const truncation = {
2846
+ truncated: true,
2847
+ surface: event.truncation?.surface ?? options.surface ?? "durable_control",
2848
+ deliveredBytes: 0,
2849
+ fields,
2850
+ fullEvidence: { available: false, reason: "not_retained" }
2851
+ };
2852
+ const bounded = {
2853
+ ...event,
2854
+ reason,
2855
+ actor,
2856
+ truncation
2857
+ };
2858
+ settleWorkspaceControlDeliveredBytes(bounded, truncation);
2859
+ const deliveredBytes = sessionEventJsonBytes(bounded);
2860
+ if (deliveredBytes > WORKSPACE_CONTROL_EVENT_MAX_BYTES) {
2861
+ throw new RangeError(
2862
+ `Bounded workspace control event exceeds its final envelope (${deliveredBytes} > ${WORKSPACE_CONTROL_EVENT_MAX_BYTES} bytes)`
2863
+ );
2864
+ }
2865
+ return bounded;
2866
+ }
2867
+ function boundWorkspaceControlText(value, maxBytes) {
2868
+ const encoder2 = new TextEncoder();
2869
+ const decoder = new TextDecoder();
2870
+ const bytes = encoder2.encode(value);
2871
+ if (bytes.byteLength <= maxBytes) return value;
2872
+ const marker = "\u2026[truncated]";
2873
+ const prefixBudget = Math.max(0, maxBytes - encoder2.encode(marker).byteLength);
2874
+ let prefixEnd = Math.min(prefixBudget, bytes.byteLength);
2875
+ while (prefixEnd > 0 && prefixEnd < bytes.byteLength && (bytes[prefixEnd] & 192) === 128) {
2876
+ prefixEnd -= 1;
2877
+ }
2878
+ return `${decoder.decode(bytes.subarray(0, prefixEnd))}${marker}`;
2879
+ }
2880
+ function normalizedWorkspaceControlOriginalBytes(value) {
2881
+ return value === null || value === void 0 || !Number.isFinite(value) ? 0 : Math.max(0, Math.floor(value));
2882
+ }
2883
+ function settleWorkspaceControlDeliveredBytes(event, truncation) {
2884
+ for (let attempt = 0; attempt < 16; attempt += 1) {
2885
+ const deliveredBytes2 = sessionEventJsonBytes(event);
2886
+ if (truncation.deliveredBytes === deliveredBytes2) return;
2887
+ truncation.deliveredBytes = deliveredBytes2;
2888
+ }
2889
+ const deliveredBytes = sessionEventJsonBytes(event);
2890
+ if (truncation.deliveredBytes !== deliveredBytes) {
2891
+ throw new RangeError("Workspace control event byte accounting did not converge");
2892
+ }
2893
+ }
2894
+ var SystemUpdateClassification = z.enum(["success", "failure", "action_required", "info"]);
2895
+ var SessionSystemUpdateKind = z.enum([
2896
+ "scheduled_occurrence",
2897
+ "goal_continuation",
2898
+ "agent_message",
2899
+ "agent_steer_instruction",
2900
+ "child_terminal_result"
2901
+ ]);
2902
+ var SessionSystemUpdatePayload = z.discriminatedUnion("type", [
2903
+ z.object({
2904
+ type: z.literal("scheduled_occurrence"),
2905
+ text: z.string().min(1),
2906
+ scheduledTaskId: z.string().uuid(),
2907
+ scheduledTaskRunId: z.string().uuid(),
2908
+ resources: z.array(ResourceRef).optional(),
2909
+ tools: z.array(ToolRef).optional()
2910
+ }).passthrough(),
2911
+ z.object({
2912
+ type: z.literal("goal_continuation"),
2913
+ goalId: z.string().uuid(),
2914
+ goalVersion: z.number().int().positive(),
2915
+ prompt: z.string().min(1),
2916
+ reason: z.string().optional()
2917
+ }).passthrough(),
2918
+ z.object({
2919
+ type: z.literal("agent_message"),
2920
+ text: z.string().min(1),
2921
+ operationId: z.string().uuid()
2922
+ }).passthrough(),
2923
+ z.object({
2924
+ type: z.literal("agent_steer_instruction"),
2925
+ instruction: z.string().min(1),
2926
+ operationId: z.string().uuid()
2927
+ }).passthrough(),
2928
+ z.object({
2929
+ type: z.literal("child_terminal_result"),
2930
+ childSessionId: z.string().uuid(),
2931
+ status: z.enum(["idle", "failed"])
2932
+ }).passthrough()
2933
+ ]);
2934
+ var SessionSystemUpdateState = z.enum([
2935
+ "pending",
2936
+ "deferred",
2937
+ "delivered",
2938
+ "cancelled",
2939
+ "superseded",
2940
+ "failed"
2941
+ ]);
2942
+ var SessionSystemUpdate = z.object({
2943
+ id: z.string().uuid(),
2944
+ sessionId: z.string().uuid(),
2945
+ kind: SessionSystemUpdateKind,
2946
+ classification: SystemUpdateClassification,
2947
+ sourceId: z.string(),
2948
+ dedupeKey: z.string(),
2949
+ summary: z.string(),
2950
+ payload: SessionSystemUpdatePayload,
2951
+ lineage: z.record(z.string(), z.unknown()),
2952
+ state: SessionSystemUpdateState,
2953
+ deliveredTurnId: z.string().uuid().nullable(),
2954
+ deliveredAt: z.string().nullable(),
2955
+ createdAt: z.string()
1251
2956
  });
1252
- var WorkspaceEnvironmentVariableName = z.string().regex(/^[A-Z][A-Z0-9_]*$/).max(128);
1253
- var WorkspaceEnvironmentVariableMetadata = z.object({
1254
- name: WorkspaceEnvironmentVariableName,
2957
+ var VariableSetVariableName = z.string().regex(/^[A-Z][A-Z0-9_]*$/).max(128);
2958
+ function withVariableSetIdAlias(shape) {
2959
+ return z.preprocess((input) => {
2960
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
2961
+ return input;
2962
+ }
2963
+ const record = input;
2964
+ if (record.variableSetId !== void 0 || record.environmentId === void 0) {
2965
+ return record;
2966
+ }
2967
+ return { ...record, variableSetId: record.environmentId };
2968
+ }, z.object(shape));
2969
+ }
2970
+ var VariableSetVariableMetadata = z.object({
2971
+ name: VariableSetVariableName,
1255
2972
  version: z.number().int().positive(),
1256
2973
  createdAt: z.string(),
1257
2974
  updatedAt: z.string()
1258
2975
  });
1259
- var WorkspaceEnvironment = z.object({
2976
+ var WorkspaceEnvironmentVariableMetadata = VariableSetVariableMetadata;
2977
+ var VariableSet = z.object({
1260
2978
  id: z.string().uuid(),
1261
2979
  accountId: z.string().uuid(),
1262
2980
  workspaceId: z.string().uuid(),
1263
2981
  name: z.string(),
1264
2982
  description: z.string().nullable(),
1265
- variables: z.array(WorkspaceEnvironmentVariableMetadata),
2983
+ variables: z.array(VariableSetVariableMetadata),
1266
2984
  createdAt: z.string(),
1267
2985
  updatedAt: z.string()
1268
2986
  });
1269
- var CreateWorkspaceEnvironmentRequest = z.object({
2987
+ var WorkspaceEnvironment = VariableSet;
2988
+ var CreateVariableSetRequest = z.object({
1270
2989
  name: z.string().min(1).max(120),
1271
2990
  description: z.string().max(2e3).optional(),
1272
- variables: z.array(z.object({
1273
- name: WorkspaceEnvironmentVariableName,
1274
- value: z.string().min(1).max(32768)
1275
- })).default([])
2991
+ variables: z.array(
2992
+ z.object({
2993
+ name: VariableSetVariableName,
2994
+ value: z.string().min(1).max(32768)
2995
+ })
2996
+ ).default([])
1276
2997
  });
1277
- var UpdateWorkspaceEnvironmentRequest = z.object({
2998
+ var CreateWorkspaceEnvironmentRequest = CreateVariableSetRequest;
2999
+ var UpdateVariableSetRequest = z.object({
1278
3000
  name: z.string().min(1).max(120).optional(),
1279
3001
  description: z.string().max(2e3).nullable().optional()
1280
3002
  });
1281
- var SetWorkspaceEnvironmentVariableRequest = z.object({
3003
+ var UpdateWorkspaceEnvironmentRequest = UpdateVariableSetRequest;
3004
+ var SetVariableSetVariableRequest = z.object({
1282
3005
  value: z.string().min(1).max(32768)
1283
3006
  });
3007
+ var SetWorkspaceEnvironmentVariableRequest = SetVariableSetVariableRequest;
3008
+ var RigCheck = z.object({
3009
+ name: z.string().min(1).max(120),
3010
+ command: z.string().min(1).max(8192)
3011
+ });
3012
+ var RigVersion = z.object({
3013
+ id: z.string().uuid(),
3014
+ rigId: z.string().uuid(),
3015
+ version: z.number().int().positive(),
3016
+ image: z.string().nullable(),
3017
+ setupScript: z.string().nullable(),
3018
+ checks: z.array(RigCheck),
3019
+ credentialHooks: z.array(z.string()),
3020
+ defaultVariableSetIds: z.array(z.string().uuid()),
3021
+ changelog: z.string().nullable(),
3022
+ // Attribution: 'user:<subject>' | 'session:<id>' | 'system'.
3023
+ createdBy: z.string().nullable(),
3024
+ active: z.boolean(),
3025
+ createdAt: z.string()
3026
+ });
3027
+ var RigVerificationHealth = z.object({
3028
+ checkHealth: z.enum(["passing", "failing", "unknown"]),
3029
+ lastVerifiedAt: z.string().nullable()
3030
+ });
3031
+ var Rig = z.object({
3032
+ id: z.string().uuid(),
3033
+ accountId: z.string().uuid(),
3034
+ workspaceId: z.string().uuid(),
3035
+ name: z.string(),
3036
+ description: z.string().nullable(),
3037
+ createdBy: z.string().nullable(),
3038
+ // The rig's currently-active version (present after create; nullable so a
3039
+ // partial/list read can omit it without a schema change).
3040
+ activeVersion: RigVersion.nullable(),
3041
+ // Summary for the currently active version. null only when there is no active
3042
+ // version; otherwise "unknown" means the active version has no verification.
3043
+ activeVersionHealth: RigVerificationHealth.nullable(),
3044
+ versionCount: z.number().int().nonnegative(),
3045
+ createdAt: z.string(),
3046
+ updatedAt: z.string()
3047
+ });
3048
+ var RigChangeKind = z.enum(["setup_append", "definition_edit"]);
3049
+ var RigChangeStatus = z.enum(["proposed", "verifying", "merged", "rejected", "failed"]);
3050
+ var RigCheckResult = z.object({
3051
+ name: z.string(),
3052
+ command: z.string(),
3053
+ exitCode: z.number().int().nullable(),
3054
+ output: z.string().optional()
3055
+ });
3056
+ var RigChangeVerification = z.object({
3057
+ startedAt: z.string().optional(),
3058
+ finishedAt: z.string().optional(),
3059
+ log: z.string().optional(),
3060
+ checkResults: z.array(RigCheckResult).optional()
3061
+ }).passthrough();
3062
+ var RigChange = z.object({
3063
+ id: z.string().uuid(),
3064
+ rigId: z.string().uuid(),
3065
+ baseVersionId: z.string().uuid().nullable(),
3066
+ kind: RigChangeKind,
3067
+ payload: z.record(z.string(), z.unknown()),
3068
+ status: RigChangeStatus,
3069
+ proposedBy: z.string().nullable(),
3070
+ verification: RigChangeVerification.nullable(),
3071
+ resultVersionId: z.string().uuid().nullable(),
3072
+ createdAt: z.string(),
3073
+ updatedAt: z.string()
3074
+ });
3075
+ var CreateRigRequest = z.object({
3076
+ name: z.string().min(1).max(120),
3077
+ description: z.string().max(2e3).optional(),
3078
+ // Initial (version 1) content, inline.
3079
+ image: z.string().max(1024).optional(),
3080
+ setupScript: z.string().max(131072).optional(),
3081
+ checks: z.array(RigCheck).max(100).default([]),
3082
+ credentialHooks: z.array(z.string().min(1).max(200)).max(50).default([]),
3083
+ defaultVariableSetIds: z.array(z.string().uuid()).max(25).default([])
3084
+ });
3085
+ var UpdateRigRequest = z.object({
3086
+ name: z.string().min(1).max(120).optional(),
3087
+ description: z.string().max(2e3).nullable().optional()
3088
+ });
3089
+ var RigSetupAppendPayload = z.object({
3090
+ command: z.string().min(1).max(8192),
3091
+ note: z.string().max(2e3).optional()
3092
+ });
3093
+ var RigDefinitionEditPayload = z.object({
3094
+ image: z.string().max(1024).nullish(),
3095
+ setupScript: z.string().max(131072).nullish(),
3096
+ checks: z.array(RigCheck).max(100).optional(),
3097
+ credentialHooks: z.array(z.string().min(1).max(200)).max(50).optional(),
3098
+ defaultVariableSetIds: z.array(z.string().uuid()).max(25).optional(),
3099
+ changelog: z.string().max(4096).nullish()
3100
+ });
3101
+ var ProposeRigChangeRequest = z.discriminatedUnion("kind", [
3102
+ z.object({ kind: z.literal("setup_append"), payload: RigSetupAppendPayload }),
3103
+ z.object({
3104
+ kind: z.literal("definition_edit"),
3105
+ payload: RigDefinitionEditPayload
3106
+ })
3107
+ ]);
1284
3108
  var ScheduledTaskStatus = z.enum(["active", "paused"]);
1285
3109
  var ScheduledTaskRunStatus = z.enum(["queued", "dispatched", "failed"]);
1286
3110
  var ScheduledTaskRunMode = z.enum(["new_session_per_run", "reusable_session"]);
@@ -1328,7 +3152,13 @@ var ScheduledTask = z.object({
1328
3152
  overlapPolicy: ScheduledTaskOverlapPolicy,
1329
3153
  agentConfig: ScheduledTaskAgentConfig,
1330
3154
  reusableSessionId: z.string().uuid().nullable(),
1331
- environmentId: z.string().uuid().nullable(),
3155
+ variableSetId: z.string().uuid().nullable().default(null),
3156
+ /** @deprecated use variableSetId */
3157
+ environmentId: z.string().uuid().nullable().default(null),
3158
+ // The rig each run binds to (M3). Stored on the task; the ACTIVE version is
3159
+ // resolved PER FIRE (at dispatch), so a task always runs the rig's current
3160
+ // version rather than one frozen at task-create time. Null ⇒ rig-less runs.
3161
+ rigId: z.string().uuid().nullable().default(null),
1332
3162
  metadata: z.record(z.string(), z.unknown()),
1333
3163
  createdAt: z.string(),
1334
3164
  updatedAt: z.string()
@@ -1348,24 +3178,31 @@ var ScheduledTaskRun = z.object({
1348
3178
  createdAt: z.string(),
1349
3179
  updatedAt: z.string()
1350
3180
  });
1351
- var CreateScheduledTaskRequest = z.object({
3181
+ var CreateScheduledTaskRequest = withVariableSetIdAlias({
1352
3182
  name: z.string().min(1),
1353
3183
  schedule: ScheduledTaskScheduleSpec,
1354
3184
  runMode: ScheduledTaskRunMode.default("new_session_per_run"),
1355
3185
  overlapPolicy: ScheduledTaskOverlapPolicy.default("allow_concurrent"),
1356
3186
  agentConfig: ScheduledTaskAgentConfig,
1357
3187
  status: ScheduledTaskStatus.default("active"),
3188
+ variableSetId: z.string().uuid().nullable().optional(),
1358
3189
  environmentId: z.string().uuid().nullable().optional(),
3190
+ // The rig each run binds to (M3); its active version is resolved per fire.
3191
+ rigId: z.string().uuid().nullable().optional(),
1359
3192
  metadata: z.record(z.string(), z.unknown()).default({})
1360
3193
  });
1361
- var UpdateScheduledTaskRequest = z.object({
3194
+ var UpdateScheduledTaskRequest = withVariableSetIdAlias({
1362
3195
  name: z.string().min(1).optional(),
1363
3196
  schedule: ScheduledTaskScheduleSpec.optional(),
1364
3197
  runMode: ScheduledTaskRunMode.optional(),
1365
3198
  overlapPolicy: ScheduledTaskOverlapPolicy.optional(),
1366
3199
  agentConfig: ScheduledTaskAgentConfig.optional(),
1367
3200
  status: ScheduledTaskStatus.optional(),
3201
+ variableSetId: z.string().uuid().nullable().optional(),
1368
3202
  environmentId: z.string().uuid().nullable().optional(),
3203
+ // The rig each run binds to (M3); null clears it. Its active version is
3204
+ // resolved per fire, so an update takes effect on the next dispatch.
3205
+ rigId: z.string().uuid().nullable().optional(),
1369
3206
  metadata: z.record(z.string(), z.unknown()).optional()
1370
3207
  });
1371
3208
  var TriggerScheduledTaskRequest = z.object({
@@ -1421,12 +3258,20 @@ var CapabilityPackSkill = z.object({
1421
3258
  const seen = /* @__PURE__ */ new Set();
1422
3259
  skill.files.forEach((file, index) => {
1423
3260
  if (seen.has(file.path)) {
1424
- ctx.addIssue({ code: "custom", message: `duplicate skill file path: ${file.path}`, path: ["files", index, "path"] });
3261
+ ctx.addIssue({
3262
+ code: "custom",
3263
+ message: `duplicate skill file path: ${file.path}`,
3264
+ path: ["files", index, "path"]
3265
+ });
1425
3266
  }
1426
3267
  seen.add(file.path);
1427
3268
  });
1428
3269
  if (!skill.files.some((file) => file.path === "SKILL.md")) {
1429
- ctx.addIssue({ code: "custom", message: "skill must include a top-level SKILL.md file", path: ["files"] });
3270
+ ctx.addIssue({
3271
+ code: "custom",
3272
+ message: "skill must include a top-level SKILL.md file",
3273
+ path: ["files"]
3274
+ });
1430
3275
  }
1431
3276
  });
1432
3277
  function isSafePackSkillRelativePath(path) {
@@ -1435,39 +3280,71 @@ function isSafePackSkillRelativePath(path) {
1435
3280
  }
1436
3281
  return path.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
1437
3282
  }
1438
- var CapabilityPack = z.object({
1439
- id: z.string().min(1),
1440
- name: z.string().min(1),
3283
+ var CapabilityPackVariableSet = z.object({
1441
3284
  description: z.string().min(1),
1442
- role: z.string().min(1),
1443
- category: z.string().min(1),
1444
- version: z.string().min(1),
1445
- // Container image ref (digest-pinned recommended) the pack's sessions run
1446
- // in. At most one enabled pack per workspace may declare one; with none,
1447
- // sessions use the deployment-wide image settings.
1448
- sandboxImage: z.string().trim().min(1).max(512).optional(),
1449
- // Skills delivered into the sandbox skill index when the pack is enabled.
1450
- skills: z.array(CapabilityPackSkill).max(32).superRefine((skills, ctx) => {
1451
- const seen = /* @__PURE__ */ new Set();
1452
- skills.forEach((skill, index) => {
1453
- const key = skill.name.toLowerCase();
1454
- if (seen.has(key)) {
1455
- ctx.addIssue({ code: "custom", message: `duplicate pack skill name: ${skill.name}`, path: [index, "name"] });
1456
- }
1457
- seen.add(key);
1458
- });
1459
- }).default([]),
1460
- tools: z.array(ToolRef).default([]),
1461
- connectors: z.array(CapabilityPackConnector).default([]),
1462
- knowledge: z.array(CapabilityPackKnowledge).default([]),
1463
- scheduledTaskTemplates: z.array(CapabilityPackScheduledTaskTemplate).default([]),
1464
- environment: z.object({
1465
- description: z.string().min(1),
1466
- requiredVariables: z.array(WorkspaceEnvironmentVariableName).default([]),
1467
- required: z.boolean().default(false)
1468
- }).optional(),
1469
- metadata: z.record(z.string(), z.unknown()).default({})
3285
+ requiredVariables: z.array(VariableSetVariableName).default([]),
3286
+ required: z.boolean().default(false)
1470
3287
  });
3288
+ var CapabilityPack = z.preprocess(
3289
+ (input) => {
3290
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
3291
+ return input;
3292
+ }
3293
+ const record = input;
3294
+ if (record.variableSet !== void 0) {
3295
+ return record;
3296
+ }
3297
+ if (record.environment !== void 0) {
3298
+ const { environment: _environment, ...rest } = record;
3299
+ return { ...rest, variableSet: record.environment };
3300
+ }
3301
+ if (record.requiredVariables !== void 0) {
3302
+ const { requiredVariables: _requiredVariables, ...rest } = record;
3303
+ return {
3304
+ ...rest,
3305
+ variableSet: {
3306
+ description: "Required variables",
3307
+ requiredVariables: record.requiredVariables,
3308
+ required: Array.isArray(record.requiredVariables) && record.requiredVariables.length > 0
3309
+ }
3310
+ };
3311
+ }
3312
+ return record;
3313
+ },
3314
+ z.object({
3315
+ id: z.string().min(1),
3316
+ name: z.string().min(1),
3317
+ description: z.string().min(1),
3318
+ role: z.string().min(1),
3319
+ category: z.string().min(1),
3320
+ version: z.string().min(1),
3321
+ // Container image ref (digest-pinned recommended) the pack's sessions run
3322
+ // in. At most one enabled pack per workspace may declare one; with none,
3323
+ // sessions use the deployment-wide image settings.
3324
+ sandboxImage: z.string().trim().min(1).max(512).optional(),
3325
+ // Skills delivered into the sandbox skill index when the pack is enabled.
3326
+ skills: z.array(CapabilityPackSkill).max(32).superRefine((skills, ctx) => {
3327
+ const seen = /* @__PURE__ */ new Set();
3328
+ skills.forEach((skill, index) => {
3329
+ const key = skill.name.toLowerCase();
3330
+ if (seen.has(key)) {
3331
+ ctx.addIssue({
3332
+ code: "custom",
3333
+ message: `duplicate pack skill name: ${skill.name}`,
3334
+ path: [index, "name"]
3335
+ });
3336
+ }
3337
+ seen.add(key);
3338
+ });
3339
+ }).default([]),
3340
+ tools: z.array(ToolRef).default([]),
3341
+ connectors: z.array(CapabilityPackConnector).default([]),
3342
+ knowledge: z.array(CapabilityPackKnowledge).default([]),
3343
+ scheduledTaskTemplates: z.array(CapabilityPackScheduledTaskTemplate).default([]),
3344
+ variableSet: CapabilityPackVariableSet.optional(),
3345
+ metadata: z.record(z.string(), z.unknown()).default({})
3346
+ })
3347
+ );
1471
3348
  var RegisterCapabilityPackRequest = CapabilityPack;
1472
3349
  var WorkspaceRegisteredPack = z.object({
1473
3350
  accountId: z.string().uuid(),
@@ -1487,7 +3364,8 @@ var PackInstallation = z.object({
1487
3364
  enabledAt: z.string(),
1488
3365
  updatedAt: z.string()
1489
3366
  });
1490
- var EnablePackRequest = z.object({
3367
+ var EnablePackRequest = withVariableSetIdAlias({
3368
+ variableSetId: z.string().uuid().optional(),
1491
3369
  environmentId: z.string().uuid().optional(),
1492
3370
  metadata: z.record(z.string(), z.unknown()).default({})
1493
3371
  });
@@ -1555,14 +3433,6 @@ var CreateSocialPostRequest = z.object({
1555
3433
  });
1556
3434
  var ConnectionKind = z.enum(["oauth2", "api_key", "app_install", "delegated"]);
1557
3435
  var ConnectionStatus = z.enum(["active", "needs_reauth", "revoked", "error"]);
1558
- var McpServerConnectionRef = z.object({
1559
- connectionId: z.string().uuid().optional(),
1560
- providerDomain: z.string().min(1),
1561
- kind: ConnectionKind.optional(),
1562
- scopes: z.array(z.string().min(1)).optional(),
1563
- resource: z.string().min(1).optional(),
1564
- subjectScope: z.enum(["workspace", "subject"]).optional()
1565
- }).strict();
1566
3436
  var ConnectionMetadata = z.object({
1567
3437
  id: z.string().uuid(),
1568
3438
  accountId: z.string().uuid(),
@@ -1615,7 +3485,12 @@ var OAuthStartRequest = z.object({
1615
3485
  resource: z.string().url().optional(),
1616
3486
  requestedScopes: z.array(z.string().min(1)).default([]),
1617
3487
  returnPath: z.string().min(1).optional(),
1618
- connectionId: z.string().uuid().optional()
3488
+ connectionId: z.string().uuid().optional(),
3489
+ oauthClient: z.object({
3490
+ clientId: z.string().min(1),
3491
+ clientSecret: z.string().min(1).optional(),
3492
+ tokenEndpointAuthMethod: z.enum(["none", "client_secret_post", "client_secret_basic"]).optional()
3493
+ }).optional()
1619
3494
  }).refine((value) => Boolean(value.mcpUrl ?? value.resource), {
1620
3495
  message: "mcpUrl is required",
1621
3496
  path: ["mcpUrl"]
@@ -1646,7 +3521,14 @@ var MarketingDailyAnalysisTaskRequest = z.object({
1646
3521
  overlapPolicy: ScheduledTaskOverlapPolicy.default("skip")
1647
3522
  });
1648
3523
  var CapabilityKind = z.enum(["pack", "mcp", "api", "skill", "plugin"]);
1649
- var CapabilitySource = z.enum(["built_in", "configured", "public_registry", "registry", "manual"]);
3524
+ var CapabilitySource = z.enum([
3525
+ "built_in",
3526
+ "library",
3527
+ "configured",
3528
+ "public_registry",
3529
+ "registry",
3530
+ "manual"
3531
+ ]);
1650
3532
  var CapabilityInstallationStatus = z.enum(["active", "disabled"]);
1651
3533
  var CapabilityCatalogAuthKind = z.enum(["oauth2", "api_key", "none", "unknown"]);
1652
3534
  var CapabilityCatalogTier = z.enum(["verified", "community"]);
@@ -1686,6 +3568,15 @@ var CapabilityCatalogItem = z.object({
1686
3568
  runtime: CapabilityRuntime.default({ available: false, notes: null }),
1687
3569
  enabled: z.boolean().default(false),
1688
3570
  enabledReason: z.string().nullable().default(null),
3571
+ // The connection backing this enabled installation, when the enable-time
3572
+ // connectionRef resolved to one (null for header/credential-free items —
3573
+ // that means "no connection involved", not "broken"). Lets the UI match
3574
+ // connection health by id instead of guessing from providerDomain alone.
3575
+ connectionRef: z.object({
3576
+ connectionId: z.string().min(1),
3577
+ providerDomain: z.string().min(1),
3578
+ kind: z.string().min(1)
3579
+ }).nullable().default(null),
1689
3580
  metadata: z.record(z.string(), z.unknown()).default({}),
1690
3581
  createdAt: z.string().optional(),
1691
3582
  updatedAt: z.string().optional()
@@ -1716,23 +3607,24 @@ var CreateCapabilityCatalogItemRequest = z.object({
1716
3607
  authModel: z.string().min(1).optional(),
1717
3608
  metadata: z.record(z.string(), z.unknown()).default({})
1718
3609
  });
1719
- var EnableCapabilityRequest = z.object({
3610
+ var EnableCapabilityRequest = withVariableSetIdAlias({
1720
3611
  config: z.record(z.string(), z.unknown()).default({}),
1721
3612
  metadata: z.record(z.string(), z.unknown()).default({}),
1722
3613
  connectionRef: McpServerConnectionRef.optional(),
1723
3614
  /**
1724
3615
  * Credential headers for remote MCP capabilities (for example an
1725
3616
  * Authorization bearer token). Values are encrypted at rest with the
1726
- * workspace-environments key, injected only into the runtime MCP client,
3617
+ * workspace-variable-sets key, injected only into the runtime MCP client,
1727
3618
  * and never returned by the API — responses expose header names only.
1728
3619
  */
1729
3620
  headers: z.record(z.string(), z.string()).default({}),
1730
3621
  /**
1731
- * Initial environment attachment for kind=pack capabilities. Mirrors the
3622
+ * Initial variableSet attachment for kind=pack capabilities. Mirrors the
1732
3623
  * dedicated POST /packs/:id/enable body: required to enable an
1733
- * environment.required pack through the unified capability-enable path,
3624
+ * variableSet.required pack through the unified capability-enable path,
1734
3625
  * optional otherwise. Ignored by non-pack capabilities.
1735
3626
  */
3627
+ variableSetId: z.string().uuid().optional(),
1736
3628
  environmentId: z.string().uuid().optional()
1737
3629
  });
1738
3630
  var CapabilityCatalogResponse = z.object({
@@ -1759,6 +3651,9 @@ var Session = z.object({
1759
3651
  resources: z.array(ResourceRef),
1760
3652
  tools: z.array(ToolRef),
1761
3653
  metadata: z.record(z.string(), z.unknown()),
3654
+ /** Frozen creator fact used only for creation attribution/idempotent repair. */
3655
+ createdBy: TurnInitiator,
3656
+ createdByContext: TurnInitiatorContext,
1762
3657
  model: z.string(),
1763
3658
  sandboxBackend: SandboxBackend,
1764
3659
  // The OS the session's box runs. Defaults to 'linux' (today's only OS).
@@ -1774,7 +3669,16 @@ var Session = z.object({
1774
3669
  // stale in-flight op and retry against the new active sandbox.
1775
3670
  activeSandboxId: z.string().uuid().nullable(),
1776
3671
  activeEpoch: z.number().int().nonnegative(),
1777
- environmentId: z.string().uuid().nullable(),
3672
+ variableSetId: z.string().uuid().nullable().default(null),
3673
+ /** @deprecated use variableSetId */
3674
+ environmentId: z.string().uuid().nullable().default(null),
3675
+ // The rig this session rides (M3 runtime binding). Both are resolved and
3676
+ // FROZEN at session create: rigId names the rig, rigVersionId pins the exact
3677
+ // active version the session's box/env/setup/doctrine are built from for the
3678
+ // session's whole life (a later promote does NOT move an existing session).
3679
+ // Both null ⇒ a rig-less session (byte-for-byte today's behavior).
3680
+ rigId: z.string().uuid().nullable().default(null),
3681
+ rigVersionId: z.string().uuid().nullable().default(null),
1778
3682
  // Non-default first-party MCP token permissions (manager-style sessions);
1779
3683
  // null means the fixed worker default set.
1780
3684
  firstPartyMcpPermissions: z.array(Permission).nullable(),
@@ -1793,9 +3697,13 @@ var Session = z.object({
1793
3697
  temporalWorkflowId: z.string().nullable(),
1794
3698
  activeTurnId: z.string().uuid().nullable(),
1795
3699
  // Actual input tokens of the last model call of the most recent turn; the
1796
- // pre-turn client-side context-compaction trigger reads it as its budget
3700
+ // pre-turn portable context-compaction trigger reads it as its budget
1797
3701
  // signal. Null until a turn with usage has completed.
1798
3702
  lastInputTokens: z.number().int().nonnegative().nullable(),
3703
+ queueVersion: z.number().int().nonnegative(),
3704
+ queueHeadPosition: z.number().int(),
3705
+ queueTailPosition: z.number().int(),
3706
+ effectiveControl: EffectiveSessionControl,
1799
3707
  lastSequence: z.number().int().nonnegative(),
1800
3708
  // Multi-account Codex (P1). codexPinnedCredentialId: the account this session is
1801
3709
  // manually PINNED to (null ⇒ follow the workspace active pointer).
@@ -1803,32 +3711,91 @@ var Session = z.object({
1803
3711
  // "Running on:" indicator's source). Both are credential-row ids, null until set.
1804
3712
  codexPinnedCredentialId: z.string().uuid().nullable(),
1805
3713
  codexLastCredentialId: z.string().uuid().nullable(),
3714
+ /** Personal (authenticated subject) workspace pin state, never workspace-global. */
3715
+ pinned: z.boolean().default(false),
3716
+ /** Stable pin ordering key; null when this subject has not pinned the session. */
3717
+ pinnedAt: z.string().nullable().default(null),
3718
+ /** Optimistic pin-state revision; zero represents an absent pin relation. */
3719
+ pinVersion: z.number().int().nonnegative().default(0),
3720
+ /**
3721
+ * Server-authoritative hierarchy summary populated on session-list reads.
3722
+ * Detail reads may omit it. The rail uses this instead of guessing a tree
3723
+ * from whichever global recency page happened to be loaded.
3724
+ */
3725
+ treeStats: z.object({
3726
+ directChildren: z.number().int().nonnegative(),
3727
+ totalDescendants: z.number().int().nonnegative(),
3728
+ runningDescendants: z.number().int().nonnegative(),
3729
+ queuedDescendants: z.number().int().nonnegative(),
3730
+ attentionDescendants: z.number().int().nonnegative(),
3731
+ pausedDescendants: z.number().int().nonnegative(),
3732
+ failedDescendants: z.number().int().nonnegative(),
3733
+ /** Counts are lower bounds rather than exact totals when true. */
3734
+ truncated: z.boolean().default(false)
3735
+ }).optional(),
1806
3736
  createdAt: z.string(),
1807
3737
  updatedAt: z.string()
1808
3738
  });
3739
+ var CreateSessionResponse = Session.extend({
3740
+ initialTurnId: z.string().uuid().nullable()
3741
+ });
3742
+ var SessionListResponse = z.object({
3743
+ pinned: z.array(Session),
3744
+ /** True when older matching pins were omitted from this bounded page. */
3745
+ pinnedTruncated: z.boolean().optional(),
3746
+ sessions: z.array(Session),
3747
+ nextCursor: z.string().nullable()
3748
+ });
3749
+ var LineageNode = z.lazy(
3750
+ () => z.object({
3751
+ session: Session,
3752
+ children: z.array(LineageNode)
3753
+ })
3754
+ );
3755
+ var SessionLineageResponse = z.object({
3756
+ ancestors: z.array(Session),
3757
+ children: z.array(LineageNode),
3758
+ truncated: z.boolean().default(false)
3759
+ });
1809
3760
  var SessionEventType = z.enum([
1810
3761
  "session.created",
3762
+ // Defensive read/transport projection for a malformed or historically
3763
+ // oversized retained event envelope. The original row stays durable; this
3764
+ // explicit synthetic type prevents unbounded free-form envelope fields from
3765
+ // crossing NATS, SSE, REST, or browser boundaries.
3766
+ "session.event.envelope_omitted",
1811
3767
  "session.status.changed",
1812
3768
  "session.requiresAction",
3769
+ "session.humanInput.requested",
3770
+ "session.context.compaction.requested",
1813
3771
  "session.context.compacted",
3772
+ "session.context.compaction.skipped",
1814
3773
  "session.context.cleared",
1815
3774
  "user.message",
1816
- "user.interrupt",
3775
+ "user.pause",
1817
3776
  "user.approvalDecision",
3777
+ "user.humanInputResponse",
1818
3778
  "turn.queued",
1819
- "turn.updated",
1820
3779
  "turn.started",
1821
3780
  "turn.completed",
1822
3781
  "turn.failed",
1823
3782
  "turn.cancelled",
1824
- "turn.preempted",
3783
+ "turn.superseded",
3784
+ "turn.recovery.requested",
3785
+ "turn.capacity_waiting",
1825
3786
  "agent.message.delta",
1826
3787
  "agent.message.completed",
1827
3788
  "agent.reasoning.delta",
1828
3789
  "agent.toolCall.created",
1829
3790
  "agent.toolCall.output",
3791
+ "agent.model.usage",
1830
3792
  "tool.auth_needed",
3793
+ "credential.auth_needed",
1831
3794
  "agent.updated",
3795
+ "rig.setup.started",
3796
+ "rig.setup.completed",
3797
+ "rig.setup.skipped",
3798
+ "rig.setup.failed",
1832
3799
  "sandbox.operation.started",
1833
3800
  "sandbox.operation.completed",
1834
3801
  "sandbox.operation.failed",
@@ -1839,7 +3806,23 @@ var SessionEventType = z.enum([
1839
3806
  "goal.completed",
1840
3807
  "goal.paused",
1841
3808
  "goal.resumed",
3809
+ "goal.cleared",
1842
3810
  "goal.continuation",
3811
+ "system.update.pending",
3812
+ "system.update.delivered",
3813
+ "session.control.paused",
3814
+ "session.control.resumed",
3815
+ "session.control.steer_requested",
3816
+ "workspace.inference.paused",
3817
+ "workspace.inference.resumed",
3818
+ "session.queue.changed",
3819
+ "session.queue.prompt.cancelled",
3820
+ "session.queue.history",
3821
+ // A terminal/stale activity callback is retained as an audit wrapper rather
3822
+ // than being dropped or emitted as though it belonged to the current turn.
3823
+ "turn.event.rejected_late",
3824
+ "memory.saved",
3825
+ "memory.corrected",
1843
3826
  // Channel-B desktop pixel-plane signals (07-channel-b §1.2). The pixel socket
1844
3827
  // carries opaque RFB and cannot carry a control message the client can act on,
1845
3828
  // so these ride the durable, sequenced, gap-filled Channel-A SSE spine.
@@ -1851,8 +3834,8 @@ var SessionEventType = z.enum([
1851
3834
  // a viewer detached / was reaped
1852
3835
  "stream.revoked",
1853
3836
  // a grant was revoked → connected clients MUST disconnect now
1854
- // Channel-B recording signals (P4.3 / module 05 §3.4). The "agent films itself
1855
- // proving the fix" loop: ffmpeg x11grab of the SAME :0 humans watch → artifact
3837
+ // Desktop recording signals. The capture loop records the same display humans
3838
+ // watch, then stores the finalized artifact for replay.
1856
3839
  // → storage. The artifact ref rides the AVAILABLE event (storageKey, NOT a
1857
3840
  // long-lived URL — clients mint a short-TTL signed GET via the route).
1858
3841
  "recording.started",
@@ -1861,8 +3844,8 @@ var SessionEventType = z.enum([
1861
3844
  // finalized: bytes PUT to storage, replayable
1862
3845
  "recording.failed",
1863
3846
  // ffmpeg/box-death/rollover/upload error — no artifact
1864
- // Channel-A structured-service notifications (P4.4 / modules/08-channel-a.md
1865
- // §2.2). The A2 reads (fs/git/terminal exec) are SYNCHRONOUS API-direct point
3847
+ // Structured-service notifications. File, Git, and terminal reads are
3848
+ // synchronous API-direct point
1866
3849
  // queries (their result is the HTTP response, NEVER an event). What rides A1
1867
3850
  // here are the side-effect NOTIFICATIONS — a path changed, git state changed,
1868
3851
  // a pty opened/printed/exited — durable, sequenced, gap-filled like every
@@ -1883,19 +3866,239 @@ var SessionEventType = z.enum([
1883
3866
  // Multi-account Codex (P1): the account a session's turn runs on changed
1884
3867
  // (manual switch in P1; failover/rotation in P3 reuse the same event). Drives
1885
3868
  // the in-session "Running on:" indicator's live flip.
1886
- "codex.account.switched"
3869
+ "codex.account.switched",
3870
+ // credential allocator per-turn selection audit. Payload is metadata only: credential row
3871
+ // id, bounded strategy/reason, and pool counts — never token material.
3872
+ "codex.credential.selected",
3873
+ // credential allocator durable zero-capacity wait lifecycle. Runtime/system events only;
3874
+ // no synthetic user message is created when capacity returns.
3875
+ "codex.capacity.waiting",
3876
+ "codex.capacity.resumed",
3877
+ "codex.capacity.superseded",
3878
+ // Sandbox durability observability (sandbox-file-persistence). The 2026-07
3879
+ // incidents (mid-session box death with /workspace loss; a fatal manifest-env
3880
+ // delta on a live box) were near-unattributable because box lifecycle left no
3881
+ // durable trace — only worker logs, which rotate within hours. These events
3882
+ // make every box transition and env recomputation drift readable from the DB
3883
+ // alone. Payloads carry ids/flags/key NAMES only — never env values (secrets).
3884
+ "sandbox.box.created",
3885
+ // box cold-created/cold-restored ({hydrated: "archive"|"none"})
3886
+ "sandbox.box.lost",
3887
+ // resume-by-id found the box gone (provider NotFound)
3888
+ "sandbox.box.terminated",
3889
+ // reaper drain terminated the box ({actor, persisted})
3890
+ "sandbox.box.snapshot",
3891
+ // mid-session /workspace snapshot persisted ({trigger})
3892
+ "sandbox.env.drift",
3893
+ // recomputed manifest env != live box env (key names only)
3894
+ // Active-sandbox pointer reconcile (issue #341 invariant B). Turn start found the
3895
+ // persisted (active_sandbox_id, active_epoch) pointing at a target the turn cannot
3896
+ // establish — a deleted/absent sandbox, a Modal sibling with no establisher, or a
3897
+ // selfhosted sandbox with no enrollment — and reset it to the session HOME under
3898
+ // the epoch fence instead of routing every op into the dead target. A VISIBLE,
3899
+ // never-silent downgrade: payload carries the typed reason + from/to epoch, never a
3900
+ // target id or command content. Announce-only; hits the timeline projection default
3901
+ // (no rendered item) like the other sandbox.* diagnostics.
3902
+ "session.route.reconciled",
3903
+ // Workbench v2 turn-end workspace capture. ANNOUNCE-ONLY: a new
3904
+ // capture revision was persisted at turn end; the client refetches the latest
3905
+ // capture. It carries metadata only (revision/turnId/capturedAt/leaseEpoch/stats),
3906
+ // never file content. Hits the timeline projection default case (ignored) — it
3907
+ // must NEVER gain a rendered timeline item without regenerating the golden
3908
+ // snapshots (golden-grammar gate).
3909
+ "workspace.revision.captured",
3910
+ // Repository discovery could not prove a complete capture. The worker
3911
+ // persisted a failed/degraded revision marker and clients must fall back to
3912
+ // the live box rather than trust a zero-repository snapshot.
3913
+ "workspace.revision.degraded",
3914
+ // Connected Machine (selfhosted) op-outcome observability (failure-visibility
3915
+ // doctrine, out-of-band plane). SESSION-scoped facts only: these fire for the
3916
+ // session whose turn ran the op (the two-planes rule — machine-plane facts like
3917
+ // pressure live in the M10 metrics DB, never as session events). Payloads carry
3918
+ // the op kind + a typed fault class + attempt count — NEVER command content.
3919
+ //
3920
+ // `machine.op.failed` fires ONLY for INFRASTRUCTURE fault classes (offline,
3921
+ // draining-exhausted, payload-too-large, reconnecting-timeout, OS/stream/protocol)
3922
+ // — a semantic miss the model asked about (a missing path, a consent gate, a
3923
+ // nonzero exit) is an OUTCOME, not an infra fault, and never fires this.
3924
+ // `machine.op.recovered` is the healed-fault leading indicator (a blip/backpressure
3925
+ // the transport absorbed): announce-only, quiet. Both hit the timeline projection's
3926
+ // quiet status-tick tier (the severity split's "degraded"); adding a rendered item
3927
+ // requires regenerating the golden snapshots (the golden-grammar gate).
3928
+ "machine.op.failed",
3929
+ "machine.op.recovered",
3930
+ // Connected Machine (selfhosted) LINK-plane observability (failure-visibility
3931
+ // doctrine). SESSION-scoped, ANNOUNCE-ONLY facts fanned out to the sessions that
3932
+ // had an active op running on the machine when its control link changed — never
3933
+ // to idle/historical sessions. Payloads carry ids / a typed reason / key-names
3934
+ // only, NEVER command content.
3935
+ //
3936
+ // `machine.link.lost` — the machine announced a clean GoingOffline (its control
3937
+ // link is going away) while a session had a running turn on it. `machine.link.
3938
+ // restored` — a reconnect Hello re-established the link that was previously lost.
3939
+ // `machine.runner.restarted` — the additional signal that the going-offline was
3940
+ // a self-update restart specifically (link.lost also fires for it; this
3941
+ // distinguishes a restart from a plain stop / host shutdown). All three hit the
3942
+ // timeline projection's quiet default tier (no rendered item); adding a rendered
3943
+ // item requires regenerating the golden snapshots (the golden-grammar gate).
3944
+ "machine.link.lost",
3945
+ "machine.link.restored",
3946
+ "machine.runner.restarted"
1887
3947
  ]);
3948
+ var SessionEventSemanticClass = z.enum([
3949
+ "control",
3950
+ "terminal",
3951
+ "failure",
3952
+ "checkpoint",
3953
+ "tool_receipt",
3954
+ "provider_account"
3955
+ ]);
3956
+ var SessionEventPayloadMode = z.enum(["none", "summary", "full"]);
3957
+ var SessionEventReadMode = z.enum(["monitoring", "forensic"]);
3958
+ var SessionEventReadDirection = z.enum(["after", "before"]);
3959
+ var SESSION_EVENT_RAW_DELTA_TYPES = [
3960
+ "agent.message.delta",
3961
+ "agent.reasoning.delta",
3962
+ "sandbox.command.output.delta",
3963
+ "terminal.pty.output.delta"
3964
+ ];
3965
+ var SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
3966
+ control: [
3967
+ "session.status.changed",
3968
+ "session.requiresAction",
3969
+ "session.humanInput.requested",
3970
+ "user.pause",
3971
+ "user.approvalDecision",
3972
+ "user.humanInputResponse",
3973
+ "goal.set",
3974
+ "goal.updated",
3975
+ "goal.completed",
3976
+ "goal.paused",
3977
+ "goal.resumed",
3978
+ "goal.cleared",
3979
+ "goal.continuation",
3980
+ "system.update.pending",
3981
+ "system.update.delivered",
3982
+ "session.control.paused",
3983
+ "session.control.resumed",
3984
+ "session.control.steer_requested",
3985
+ "workspace.inference.paused",
3986
+ "workspace.inference.resumed",
3987
+ "session.queue.changed",
3988
+ "session.queue.prompt.cancelled"
3989
+ ],
3990
+ terminal: [
3991
+ "turn.completed",
3992
+ "turn.failed",
3993
+ "turn.cancelled",
3994
+ "turn.superseded",
3995
+ "goal.completed",
3996
+ "goal.paused",
3997
+ "rig.setup.completed",
3998
+ "rig.setup.skipped",
3999
+ "rig.setup.failed",
4000
+ "sandbox.operation.completed",
4001
+ "sandbox.operation.failed",
4002
+ "recording.available",
4003
+ "recording.failed",
4004
+ "terminal.pty.exited"
4005
+ ],
4006
+ failure: [
4007
+ "session.event.envelope_omitted",
4008
+ "turn.failed",
4009
+ "tool.auth_needed",
4010
+ "credential.auth_needed",
4011
+ "rig.setup.failed",
4012
+ "sandbox.operation.failed",
4013
+ "recording.failed",
4014
+ "sandbox.box.lost",
4015
+ "workspace.revision.degraded",
4016
+ "machine.op.failed",
4017
+ "machine.link.lost"
4018
+ ],
4019
+ checkpoint: [
4020
+ "session.context.compaction.requested",
4021
+ "session.context.compacted",
4022
+ "session.context.compaction.skipped",
4023
+ "session.context.cleared",
4024
+ "turn.recovery.requested",
4025
+ "session.queue.history",
4026
+ "sandbox.box.snapshot",
4027
+ "workspace.revision.captured"
4028
+ ],
4029
+ tool_receipt: [
4030
+ "agent.toolCall.created",
4031
+ "agent.toolCall.output",
4032
+ "tool.auth_needed",
4033
+ "artifact.created"
4034
+ ],
4035
+ provider_account: [
4036
+ "agent.model.usage",
4037
+ "codex.account.switched",
4038
+ "codex.credential.selected",
4039
+ "codex.capacity.waiting",
4040
+ "codex.capacity.resumed",
4041
+ "codex.capacity.superseded",
4042
+ "sandbox.box.created",
4043
+ "sandbox.box.lost",
4044
+ "sandbox.box.terminated",
4045
+ "sandbox.box.snapshot",
4046
+ "sandbox.env.drift",
4047
+ "session.route.reconciled",
4048
+ "machine.op.failed",
4049
+ "machine.op.recovered",
4050
+ "machine.link.lost",
4051
+ "machine.link.restored",
4052
+ "machine.runner.restarted"
4053
+ ]
4054
+ };
4055
+ function resolveSessionEventTypeFilters(input) {
4056
+ const included = new Set(input.includeTypes ?? []);
4057
+ for (const semanticClass of input.includeClasses ?? []) {
4058
+ for (const type of SESSION_EVENT_SEMANTIC_CLASS_TYPES[semanticClass]) included.add(type);
4059
+ }
4060
+ const excluded = new Set(input.excludeTypes ?? []);
4061
+ for (const semanticClass of input.excludeClasses ?? []) {
4062
+ for (const type of SESSION_EVENT_SEMANTIC_CLASS_TYPES[semanticClass]) excluded.add(type);
4063
+ }
4064
+ for (const type of input.defaultExcludeTypes ?? []) {
4065
+ if (!included.has(type)) excluded.add(type);
4066
+ }
4067
+ for (const type of excluded) included.delete(type);
4068
+ return { includeTypes: [...included], excludeTypes: [...excluded] };
4069
+ }
1888
4070
  var ToolAuthNeededPayload = z.object({
1889
4071
  serverId: z.string().min(1),
1890
4072
  toolName: z.string().min(1).nullable().optional(),
1891
4073
  providerDomain: z.string().min(1),
1892
- connectionId: z.string().uuid().nullable().optional(),
1893
- reason: z.enum(["missing_connection", "expired", "insufficient_scope", "refresh_failed"]),
4074
+ provider: z.string().min(1).max(128).optional(),
4075
+ // Embedded hosts may use an opaque connection identity; never assume an
4076
+ // OpenGeni UUID on the public event wire.
4077
+ connectionId: z.string().min(1).nullable().optional(),
4078
+ reason: z.enum([
4079
+ "missing_connection",
4080
+ "expired",
4081
+ "insufficient_scope",
4082
+ "refresh_failed",
4083
+ "unsupported_auth",
4084
+ "resource_scope_unavailable"
4085
+ ]),
1894
4086
  scopes: z.array(z.string().min(1)).optional(),
1895
4087
  resource: z.string().min(1).optional(),
4088
+ selectedResources: McpConnectionResourceScopes.optional(),
1896
4089
  authorizationUrl: z.string().url().optional(),
1897
4090
  subjectId: z.string().min(1).nullable().optional()
1898
4091
  });
4092
+ var CredentialAuthNeededPayload = z.object({
4093
+ credentialClass: z.literal("run"),
4094
+ providerDomain: z.string().min(1).optional(),
4095
+ connectionId: z.string().min(1).optional(),
4096
+ reason: z.enum(["missing_connection", "expired", "insufficient_scope", "refresh_failed"]),
4097
+ scopes: z.array(z.string().min(1)).optional(),
4098
+ resource: z.string().min(1).optional(),
4099
+ authorizationUrl: z.string().url().optional(),
4100
+ message: z.string().min(1).optional()
4101
+ });
1899
4102
  var StreamUrlRotatedPayload = z.object({
1900
4103
  url: z.string().url(),
1901
4104
  token: z.string().nullable(),
@@ -1977,15 +4180,17 @@ var SandboxCommandOutputDeltaPayload = z.object({
1977
4180
  });
1978
4181
  var FsChangeKind = z.enum(["created", "modified", "deleted", "renamed"]);
1979
4182
  var FsChangedPayload = z.object({
1980
- changes: z.array(z.object({
1981
- path: z.string(),
1982
- // workspace-relative POSIX path
1983
- kind: FsChangeKind,
1984
- isDir: z.boolean().default(false),
1985
- sizeBytes: z.number().int().nonnegative().nullable().default(null),
1986
- oldPath: z.string().optional()
1987
- // for "renamed"
1988
- })).min(1),
4183
+ changes: z.array(
4184
+ z.object({
4185
+ path: z.string(),
4186
+ // workspace-relative POSIX path
4187
+ kind: FsChangeKind,
4188
+ isDir: z.boolean().default(false),
4189
+ sizeBytes: z.number().int().nonnegative().nullable().default(null),
4190
+ oldPath: z.string().optional()
4191
+ // for "renamed"
4192
+ })
4193
+ ).min(1),
1989
4194
  source: z.enum(["write", "watch", "agent"]).default("write"),
1990
4195
  // Monotonic FS revision (per-lease, paired with leaseEpoch for staleness).
1991
4196
  revision: z.number().int().nonnegative(),
@@ -2028,16 +4233,18 @@ var TerminalPtyExitedPayload = z.object({
2028
4233
  reason: z.enum(["exit", "killed", "owner_gone", "timeout"])
2029
4234
  });
2030
4235
  var FsNodeType = z.enum(["file", "dir", "symlink", "other"]);
2031
- var FsTreeNode = z.lazy(() => z.object({
2032
- name: z.string(),
2033
- path: z.string(),
2034
- type: FsNodeType,
2035
- sizeBytes: z.number().int().nonnegative().nullable(),
2036
- mtimeMs: z.number().int().nonnegative().nullable(),
2037
- mode: z.number().int().nullable(),
2038
- children: z.array(FsTreeNode).optional(),
2039
- truncated: z.boolean().default(false)
2040
- }));
4236
+ var FsTreeNode = z.lazy(
4237
+ () => z.object({
4238
+ name: z.string(),
4239
+ path: z.string(),
4240
+ type: FsNodeType,
4241
+ sizeBytes: z.number().int().nonnegative().nullable(),
4242
+ mtimeMs: z.number().int().nonnegative().nullable(),
4243
+ mode: z.number().int().nullable(),
4244
+ children: z.array(FsTreeNode).optional(),
4245
+ truncated: z.boolean().default(false)
4246
+ })
4247
+ );
2041
4248
  var FsListRequest = z.object({
2042
4249
  path: z.string().default(""),
2043
4250
  // "" = workspace root
@@ -2089,7 +4296,9 @@ var FsDeleteRequest = z.object({
2089
4296
  recursive: z.boolean().default(false)
2090
4297
  // required true to delete a non-empty dir
2091
4298
  });
2092
- var FsDeleteResponse = z.object({ revision: z.number().int().nonnegative() });
4299
+ var FsDeleteResponse = z.object({
4300
+ revision: z.number().int().nonnegative()
4301
+ });
2093
4302
  var FsMoveRequest = z.object({
2094
4303
  path: z.string(),
2095
4304
  newPath: z.string(),
@@ -2186,6 +4395,9 @@ var GitDiffRequest = z.object({
2186
4395
  // diff selectors, mutually exclusive precedence: refs > staged > worktree
2187
4396
  staged: z.boolean().default(false),
2188
4397
  // --cached (index vs HEAD)
4398
+ // Workspace review includes after-images that ordinary `git diff` omits.
4399
+ // Explicit so commit/staged consumers keep native Git semantics by default.
4400
+ includeUntracked: z.boolean().default(false),
2189
4401
  fromRef: z.string().optional(),
2190
4402
  toRef: z.string().optional(),
2191
4403
  pathspec: z.array(z.string()).default([]),
@@ -2196,6 +4408,125 @@ var GitDiffResponse = z.object({
2196
4408
  files: z.array(GitFileDiff),
2197
4409
  revision: z.number().int().nonnegative()
2198
4410
  });
4411
+ var WorkspaceCaptureFile = z.object({
4412
+ path: z.string(),
4413
+ status: GitFileStatusCode,
4414
+ // sha256 of the captured after-image bytes; null when deleted / tooLarge.
4415
+ hash: z.string().nullable(),
4416
+ // git blob sha of the HEAD version — the wake-on-edit flush guard (design
4417
+ // §10.1). null when the path is new/untracked (no HEAD blob).
4418
+ baseHash: z.string().nullable(),
4419
+ // Content-addressed storage key of the after-image; null when deleted /
4420
+ // tooLarge / binary (no inline content captured).
4421
+ contentRef: z.string().nullable(),
4422
+ sizeBytes: z.number().int().nonnegative(),
4423
+ isBinary: z.boolean().default(false),
4424
+ // >5MB per-file content guard tripped: content NOT captured, render "open live".
4425
+ tooLarge: z.boolean().default(false),
4426
+ deleted: z.boolean().default(false)
4427
+ });
4428
+ var WorkspaceCaptureRepo = z.object({
4429
+ root: z.string(),
4430
+ head: z.string().nullable(),
4431
+ detached: z.boolean().default(false),
4432
+ upstream: z.string().nullable(),
4433
+ ahead: z.number().int().nonnegative().default(0),
4434
+ behind: z.number().int().nonnegative().default(0),
4435
+ status: z.array(GitFileStatus),
4436
+ diff: z.array(GitFileDiff)
4437
+ });
4438
+ var WorkspaceCaptureDegradedReason = z.enum([
4439
+ "repository_discovery_command_failed",
4440
+ "repository_discovery_timed_out",
4441
+ "repository_discovery_result_limit_exceeded"
4442
+ ]);
4443
+ var WorkspaceCaptureStats = z.object({
4444
+ repoCount: z.number().int().nonnegative(),
4445
+ fileCount: z.number().int().nonnegative(),
4446
+ additions: z.number().int().nonnegative(),
4447
+ deletions: z.number().int().nonnegative(),
4448
+ totalBytes: z.number().int().nonnegative(),
4449
+ tooLargeCount: z.number().int().nonnegative(),
4450
+ binaryCount: z.number().int().nonnegative(),
4451
+ treeEntryCount: z.number().int().nonnegative(),
4452
+ treeTruncated: z.boolean().default(false),
4453
+ durationMs: z.number().int().nonnegative(),
4454
+ // sha256 over the change surface (per-file path/hash/status + per-repo diff
4455
+ // summary, tree/mtime excluded). The empty-turn gate skips a capture whose
4456
+ // fingerprint equals the previous revision's — "no new revision when nothing
4457
+ // changed" holds even when the tree stays dirty across read-only turns.
4458
+ fingerprint: z.string().optional()
4459
+ });
4460
+ var WorkspaceCaptureManifest = z.object({
4461
+ version: z.literal(1),
4462
+ revision: z.number().int().nonnegative(),
4463
+ capturedAt: z.string(),
4464
+ turnId: z.string().nullable(),
4465
+ leaseEpoch: z.number().int().nonnegative(),
4466
+ treeIndex: FsTreeNode,
4467
+ treeTruncated: z.boolean().default(false),
4468
+ repos: z.array(WorkspaceCaptureRepo),
4469
+ files: z.array(WorkspaceCaptureFile),
4470
+ stats: WorkspaceCaptureStats
4471
+ });
4472
+ var WorkspaceRevisionCapturedPayload = z.object({
4473
+ revision: z.number().int().nonnegative(),
4474
+ turnId: z.string().nullable(),
4475
+ capturedAt: z.string(),
4476
+ leaseEpoch: z.number().int().nonnegative(),
4477
+ stats: WorkspaceCaptureStats
4478
+ });
4479
+ var WorkspaceRevisionDegradedPayload = z.object({
4480
+ revision: z.number().int().nonnegative(),
4481
+ turnId: z.string().nullable(),
4482
+ capturedAt: z.string(),
4483
+ leaseEpoch: z.number().int().nonnegative(),
4484
+ reason: WorkspaceCaptureDegradedReason
4485
+ });
4486
+ var WorkspaceCaptureSignedUrl = z.object({
4487
+ url: z.string().url(),
4488
+ expiresAt: z.string()
4489
+ });
4490
+ var GetWorkspaceCaptureResponse = z.discriminatedUnion("available", [
4491
+ z.object({
4492
+ available: z.literal(false),
4493
+ // Optional for additive compatibility with older servers. New servers set
4494
+ // these fields when the newest durable revision is an explicit degraded
4495
+ // marker rather than "no capture exists yet".
4496
+ degradedReason: WorkspaceCaptureDegradedReason.nullable().optional(),
4497
+ revision: z.number().int().nonnegative().nullable().optional(),
4498
+ capturedAt: z.string().nullable().optional(),
4499
+ turnId: z.string().nullable().optional(),
4500
+ leaseEpoch: z.number().int().nonnegative().nullable().optional()
4501
+ }),
4502
+ z.object({
4503
+ available: z.literal(true),
4504
+ revision: z.number().int().nonnegative(),
4505
+ capturedAt: z.string(),
4506
+ turnId: z.string().nullable(),
4507
+ leaseEpoch: z.number().int().nonnegative(),
4508
+ sizeBytes: z.number().int().nonnegative(),
4509
+ stats: WorkspaceCaptureStats,
4510
+ manifest: WorkspaceCaptureManifest.nullable().default(null),
4511
+ manifestUrl: WorkspaceCaptureSignedUrl.nullable().default(null)
4512
+ })
4513
+ ]);
4514
+ var GetWorkspaceCaptureFileResponse = z.object({
4515
+ path: z.string(),
4516
+ revision: z.number().int().nonnegative(),
4517
+ status: GitFileStatusCode,
4518
+ hash: z.string().nullable(),
4519
+ baseHash: z.string().nullable(),
4520
+ sizeBytes: z.number().int().nonnegative(),
4521
+ isBinary: z.boolean(),
4522
+ tooLarge: z.boolean(),
4523
+ encoding: FsEncoding.nullable().default(null),
4524
+ // set iff content is inline
4525
+ content: z.string().nullable().default(null),
4526
+ // inline ≤256KB (per encoding)
4527
+ contentUrl: WorkspaceCaptureSignedUrl.nullable().default(null)
4528
+ // signed >256KB
4529
+ });
2199
4530
  var GitLogRequest = z.object({
2200
4531
  path: z.string().default(""),
2201
4532
  ref: z.string().default("HEAD"),
@@ -2207,14 +4538,25 @@ var GitCommit = z.object({
2207
4538
  sha: z.string(),
2208
4539
  shortSha: z.string(),
2209
4540
  parents: z.array(z.string()),
2210
- author: z.object({ name: z.string(), email: z.string(), timestamp: z.number().int() }),
2211
- committer: z.object({ name: z.string(), email: z.string(), timestamp: z.number().int() }),
4541
+ author: z.object({
4542
+ name: z.string(),
4543
+ email: z.string(),
4544
+ timestamp: z.number().int()
4545
+ }),
4546
+ committer: z.object({
4547
+ name: z.string(),
4548
+ email: z.string(),
4549
+ timestamp: z.number().int()
4550
+ }),
2212
4551
  subject: z.string(),
2213
4552
  body: z.string(),
2214
4553
  refs: z.array(z.string()).default([])
2215
4554
  // decorations: branch/tag pointers
2216
4555
  });
2217
- var GitLogResponse = z.object({ commits: z.array(GitCommit), hasMore: z.boolean() });
4556
+ var GitLogResponse = z.object({
4557
+ commits: z.array(GitCommit),
4558
+ hasMore: z.boolean()
4559
+ });
2218
4560
  var GitShowRequest = z.object({
2219
4561
  path: z.string().default(""),
2220
4562
  ref: z.string(),
@@ -2229,7 +4571,12 @@ var GitShowResponse = z.object({
2229
4571
  // null when fetching a raw blob
2230
4572
  files: z.array(GitFileDiff),
2231
4573
  // commit diff vs first parent
2232
- blob: z.object({ content: z.string(), encoding: FsEncoding, sizeBytes: z.number().int(), truncated: z.boolean() }).nullable(),
4574
+ blob: z.object({
4575
+ content: z.string(),
4576
+ encoding: FsEncoding,
4577
+ sizeBytes: z.number().int(),
4578
+ truncated: z.boolean()
4579
+ }).nullable(),
2233
4580
  revision: z.number().int().nonnegative()
2234
4581
  });
2235
4582
  var TerminalExecRequest = z.object({
@@ -2266,11 +4613,22 @@ var PtyOpenResponse = z.object({
2266
4613
  supportsInput: z.boolean()
2267
4614
  // false on backends without writeStdin
2268
4615
  });
2269
- var PtyWriteRequest = z.object({ ptyId: z.string().uuid(), data: z.string() });
2270
- var PtyResizeRequest = z.object({ ptyId: z.string().uuid(), cols: z.number().int().positive(), rows: z.number().int().positive() });
4616
+ var PtyWriteRequest = z.object({
4617
+ ptyId: z.string().uuid(),
4618
+ data: z.string()
4619
+ });
4620
+ var PtyResizeRequest = z.object({
4621
+ ptyId: z.string().uuid(),
4622
+ cols: z.number().int().positive(),
4623
+ rows: z.number().int().positive()
4624
+ });
2271
4625
  var PtyCloseRequest = z.object({ ptyId: z.string().uuid() });
2272
4626
  var SessionStructuredCapabilities = z.object({
2273
- FileSystem: z.object({ available: z.boolean(), readOnly: z.boolean(), root: z.string() }),
4627
+ FileSystem: z.object({
4628
+ available: z.boolean(),
4629
+ readOnly: z.boolean(),
4630
+ root: z.string()
4631
+ }),
2274
4632
  Terminal: z.object({
2275
4633
  events: z.boolean(),
2276
4634
  // command.output firehose (always on if a box exists)
@@ -2289,11 +4647,407 @@ var SessionEvent = z.object({
2289
4647
  type: SessionEventType,
2290
4648
  payload: z.unknown().default({}),
2291
4649
  occurredAt: z.string(),
2292
- clientEventId: z.string().min(1).nullable().optional(),
2293
- turnId: z.string().uuid().nullable().optional()
4650
+ clientEventId: SessionOperationKey.nullable().optional(),
4651
+ turnId: z.string().uuid().nullable().optional(),
4652
+ turnGeneration: z.number().int().nonnegative().nullable().optional(),
4653
+ turnAttemptId: z.string().uuid().nullable().optional(),
4654
+ turnAssociation: z.enum(["current", "late_rejected", "duplicate"]).nullable().optional(),
4655
+ duplicateOfEventId: z.string().uuid().nullable().optional(),
4656
+ duplicateReason: z.string().min(1).max(1024).nullable().optional()
4657
+ });
4658
+ var OPENGENI_HOST_EXPORT_SCHEMA_REVISION = "2026-07-host-export-v1";
4659
+ var HostExportCursor = z.string().regex(/^(0|[1-9][0-9]*)$/);
4660
+ var HostExportConsumerId = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
4661
+ var HostExportInitiator = TurnInitiator.extend({
4662
+ subjectId: z.string().min(1).max(1024),
4663
+ label: z.string().min(1).max(256).optional()
4664
+ });
4665
+ var HostExportInitiatorContext = TurnInitiatorContext.refine(
4666
+ (value) => {
4667
+ try {
4668
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength <= 4096;
4669
+ } catch {
4670
+ return false;
4671
+ }
4672
+ },
4673
+ { message: "Host export initiator context exceeds 4096 UTF-8 bytes" }
4674
+ );
4675
+ var HostExportAttribution = {
4676
+ initiator: HostExportInitiator.nullable(),
4677
+ initiatorContext: HostExportInitiatorContext,
4678
+ origin: SessionTurnSource.nullable()
4679
+ };
4680
+ var HostSessionEvent = SessionEvent.extend({
4681
+ type: z.string().min(1).max(256),
4682
+ clientEventId: z.string().max(1024).nullable().optional(),
4683
+ turnAssociation: z.string().min(1).max(64).nullable().optional(),
4684
+ duplicateReason: z.string().max(4096).nullable().optional()
4685
+ });
4686
+ var HostUsageEvent = UsageEvent.extend({
4687
+ subjectId: z.string().max(1024).nullable(),
4688
+ eventType: z.string().min(1).max(256),
4689
+ unit: z.string().min(1).max(128),
4690
+ sourceResourceType: z.string().max(256).nullable(),
4691
+ sourceResourceId: z.string().max(2048).nullable(),
4692
+ idempotencyKey: z.string().min(1).max(2048),
4693
+ billingProviderEventId: z.string().max(2048).nullable()
4694
+ });
4695
+ var HostEventExport = z.object({
4696
+ schemaRevision: z.literal(OPENGENI_HOST_EXPORT_SCHEMA_REVISION),
4697
+ cursor: HostExportCursor,
4698
+ idempotencyKey: z.string().min(1).max(2048),
4699
+ accountId: z.string().uuid(),
4700
+ workspaceId: z.string().uuid(),
4701
+ /**
4702
+ * Immutable root of event.sessionId's session lineage at capture time. Null
4703
+ * only for an unresolved pre-lineage/legacy export row.
4704
+ */
4705
+ rootSessionId: z.string().uuid().nullable(),
4706
+ ...HostExportAttribution,
4707
+ event: HostSessionEvent
2294
4708
  });
2295
- var CreateSessionRequest = z.object({
4709
+ var HostUsageExport = z.object({
4710
+ schemaRevision: z.literal(OPENGENI_HOST_EXPORT_SCHEMA_REVISION),
4711
+ cursor: HostExportCursor,
4712
+ accountId: z.string().uuid(),
4713
+ workspaceId: z.string().uuid(),
4714
+ sessionId: z.string().uuid().nullable(),
4715
+ /** Null when sessionId is null or an unresolved pre-lineage legacy row. */
4716
+ rootSessionId: z.string().uuid().nullable(),
4717
+ turnId: z.string().uuid().nullable(),
4718
+ turnAttemptId: z.string().uuid().nullable(),
4719
+ ...HostExportAttribution,
4720
+ usage: HostUsageEvent
4721
+ });
4722
+ var HostEventExportBatch = z.object({
4723
+ schemaRevision: z.literal(OPENGENI_HOST_EXPORT_SCHEMA_REVISION),
4724
+ consumerId: HostExportConsumerId,
4725
+ leaseToken: z.string().uuid(),
4726
+ checkpoint: HostExportCursor,
4727
+ throughCursor: HostExportCursor,
4728
+ events: z.array(HostEventExport).min(1).max(256)
4729
+ });
4730
+ var HostUsageExportBatch = z.object({
4731
+ schemaRevision: z.literal(OPENGENI_HOST_EXPORT_SCHEMA_REVISION),
4732
+ consumerId: HostExportConsumerId,
4733
+ leaseToken: z.string().uuid(),
4734
+ checkpoint: HostExportCursor,
4735
+ throughCursor: HostExportCursor,
4736
+ events: z.array(HostUsageExport).min(1).max(256)
4737
+ });
4738
+ var SESSION_EVENT_TYPE_MAX_BYTES = 256;
4739
+ var SESSION_EVENT_CLIENT_EVENT_ID_MAX_BYTES = SESSION_OPERATION_KEY_MAX_CHARS * 4;
4740
+ var SESSION_EVENT_TURN_ASSOCIATION_MAX_BYTES = 64;
4741
+ var SESSION_EVENT_DUPLICATE_REASON_MAX_BYTES = 4 * 1024;
4742
+ var SESSION_EVENT_ENVELOPE_MAX_BYTES = 80 * 1024;
4743
+ function boundSessionEvent(event, options = {}) {
4744
+ const surface = options.surface ?? "durable_audit";
4745
+ const maxBytes = Math.max(8 * 1024, options.maxBytes ?? SESSION_EVENT_ENVELOPE_MAX_BYTES);
4746
+ const originalBytes = measureSessionEventJson(event).bytes;
4747
+ const source = sessionEventOwnDataFields(event);
4748
+ const id = canonicalSessionEventUuid(source.id, SESSION_EVENT_ZERO_UUID);
4749
+ const workspaceId = canonicalSessionEventUuid(source.workspaceId, SESSION_EVENT_ZERO_UUID);
4750
+ const sessionId = canonicalSessionEventUuid(source.sessionId, SESSION_EVENT_ZERO_UUID);
4751
+ const sequence = source.sequence.readable && typeof source.sequence.value === "number" && Number.isSafeInteger(source.sequence.value) && source.sequence.value > 0 ? source.sequence.value : 1;
4752
+ const occurredAt = source.occurredAt.readable && typeof source.occurredAt.value === "string" && sessionEventUtf8Bytes(source.occurredAt.value) <= 256 ? source.occurredAt.value : "1970-01-01T00:00:00.000Z";
4753
+ const rawType = source.type.readable ? source.type.value : void 0;
4754
+ const typeIsSafe = typeof rawType === "string" && sessionEventUtf8Bytes(rawType) <= SESSION_EVENT_TYPE_MAX_BYTES && !rawType.includes("\n") && !rawType.includes("\r");
4755
+ const rawClientEventId = source.clientEventId.readable ? source.clientEventId.value : void 0;
4756
+ const clientEventId = boundOptionalSessionEventText(
4757
+ typeof rawClientEventId === "string" || rawClientEventId === null ? rawClientEventId : void 0,
4758
+ SESSION_EVENT_CLIENT_EVENT_ID_MAX_BYTES
4759
+ );
4760
+ const rawTurnAssociation = source.turnAssociation.readable ? source.turnAssociation.value : void 0;
4761
+ const turnAssociation = rawTurnAssociation === null || rawTurnAssociation === void 0 || rawTurnAssociation === "current" || rawTurnAssociation === "late_rejected" || rawTurnAssociation === "duplicate" ? rawTurnAssociation : null;
4762
+ const rawDuplicateReason = source.duplicateReason.readable ? source.duplicateReason.value : void 0;
4763
+ const duplicateReason = boundOptionalSessionEventText(
4764
+ typeof rawDuplicateReason === "string" || rawDuplicateReason === null ? rawDuplicateReason : void 0,
4765
+ SESSION_EVENT_DUPLICATE_REASON_MAX_BYTES
4766
+ );
4767
+ const turnId = canonicalOptionalSessionEventUuid(source.turnId);
4768
+ const turnGeneration = canonicalSessionEventGeneration(source.turnGeneration);
4769
+ const turnAttemptId = canonicalOptionalSessionEventUuid(source.turnAttemptId);
4770
+ const duplicateOfEventId = canonicalOptionalSessionEventUuid(source.duplicateOfEventId);
4771
+ const envelopeFields = [
4772
+ sessionEventCustomSerializerProjection(event),
4773
+ sessionEventAdditionalTopLevelFieldProjection(event),
4774
+ !typeIsSafe ? sessionEventEnvelopeFieldProjection(
4775
+ "type",
4776
+ rawType,
4777
+ "session.event.envelope_omitted",
4778
+ source.type.readable
4779
+ ) : null,
4780
+ !source.clientEventId.readable || rawClientEventId !== clientEventId ? sessionEventEnvelopeFieldProjection(
4781
+ "clientEventId",
4782
+ rawClientEventId,
4783
+ clientEventId,
4784
+ source.clientEventId.readable
4785
+ ) : null,
4786
+ !source.turnAssociation.readable || rawTurnAssociation !== turnAssociation ? sessionEventEnvelopeFieldProjection(
4787
+ "turnAssociation",
4788
+ rawTurnAssociation,
4789
+ turnAssociation,
4790
+ source.turnAssociation.readable
4791
+ ) : null,
4792
+ !source.duplicateReason.readable || rawDuplicateReason !== duplicateReason ? sessionEventEnvelopeFieldProjection(
4793
+ "duplicateReason",
4794
+ rawDuplicateReason,
4795
+ duplicateReason,
4796
+ source.duplicateReason.readable
4797
+ ) : null,
4798
+ ...sessionEventCanonicalFieldProjections(source, {
4799
+ id,
4800
+ workspaceId,
4801
+ sessionId,
4802
+ sequence,
4803
+ occurredAt
4804
+ }),
4805
+ ...sessionEventOptionalFieldProjections(source, {
4806
+ turnId,
4807
+ turnGeneration,
4808
+ turnAttemptId,
4809
+ duplicateOfEventId
4810
+ }),
4811
+ !source.payload.readable ? sessionEventEnvelopeFieldProjection("payload", void 0, null, false) : null
4812
+ ].filter((field) => field !== null);
4813
+ const rawPayload = source.payload.readable ? source.payload.value : "[event payload accessor omitted at bounded projection boundary]";
4814
+ const payload = envelopeFields.length === 0 ? boundSessionEventPayload(rawPayload, { surface }) : boundSessionEventPayload(
4815
+ {
4816
+ preview: "[legacy event envelope normalized at bounded projection boundary]",
4817
+ originalEventBytes: originalBytes,
4818
+ originalType: typeof rawType === "string" ? boundSessionEventText(rawType, 256) : null,
4819
+ envelopeProjection: {
4820
+ truncated: true,
4821
+ surface,
4822
+ fields: envelopeFields
4823
+ },
4824
+ fullEvidence: { available: false, reason: "not_retained" }
4825
+ },
4826
+ { surface, maxBytes: 8 * 1024 }
4827
+ );
4828
+ const bounded = {
4829
+ id,
4830
+ workspaceId,
4831
+ sessionId,
4832
+ sequence,
4833
+ type: typeIsSafe ? rawType : "session.event.envelope_omitted",
4834
+ payload,
4835
+ occurredAt,
4836
+ ...sessionEventShouldEmitOptionalField(source.clientEventId) ? { clientEventId } : {},
4837
+ ...sessionEventShouldEmitOptionalField(source.turnId) ? { turnId } : {},
4838
+ ...sessionEventShouldEmitOptionalField(source.turnGeneration) ? { turnGeneration } : {},
4839
+ ...sessionEventShouldEmitOptionalField(source.turnAttemptId) ? { turnAttemptId } : {},
4840
+ ...sessionEventShouldEmitOptionalField(source.turnAssociation) ? { turnAssociation } : {},
4841
+ ...sessionEventShouldEmitOptionalField(source.duplicateOfEventId) ? { duplicateOfEventId } : {},
4842
+ ...sessionEventShouldEmitOptionalField(source.duplicateReason) ? { duplicateReason } : {}
4843
+ };
4844
+ if (sessionEventJsonBytes(bounded) <= maxBytes) return bounded;
4845
+ const fallback = {
4846
+ id,
4847
+ workspaceId,
4848
+ sessionId,
4849
+ sequence,
4850
+ type: "session.event.envelope_omitted",
4851
+ payload: boundSessionEventPayload(
4852
+ {
4853
+ preview: "[legacy event envelope omitted at bounded projection boundary]",
4854
+ originalEventBytes: originalBytes,
4855
+ originalType: typeof rawType === "string" ? boundSessionEventText(rawType, 256) : null,
4856
+ fullEvidence: { available: false, reason: "not_retained" }
4857
+ },
4858
+ { surface, maxBytes: 4 * 1024 }
4859
+ ),
4860
+ occurredAt,
4861
+ ...sessionEventShouldEmitOptionalField(source.clientEventId) ? { clientEventId } : {},
4862
+ ...sessionEventShouldEmitOptionalField(source.turnId) ? { turnId } : {},
4863
+ ...sessionEventShouldEmitOptionalField(source.turnGeneration) ? { turnGeneration } : {},
4864
+ ...sessionEventShouldEmitOptionalField(source.turnAttemptId) ? { turnAttemptId } : {},
4865
+ ...sessionEventShouldEmitOptionalField(source.turnAssociation) ? { turnAssociation } : {},
4866
+ ...sessionEventShouldEmitOptionalField(source.duplicateOfEventId) ? { duplicateOfEventId } : {},
4867
+ ...sessionEventShouldEmitOptionalField(source.duplicateReason) ? { duplicateReason } : {}
4868
+ };
4869
+ const deliveredBytes = sessionEventJsonBytes(fallback);
4870
+ if (deliveredBytes > maxBytes) {
4871
+ throw new RangeError(
4872
+ `Bounded session event exceeds its final envelope (${deliveredBytes} > ${maxBytes} bytes)`
4873
+ );
4874
+ }
4875
+ return fallback;
4876
+ }
4877
+ function sessionEventEnvelopeFieldProjection(field, original, delivered, originalReadable = true) {
4878
+ return {
4879
+ field,
4880
+ originalBytes: originalReadable ? typeof original === "string" ? sessionEventUtf8Bytes(original) : typeof original === "number" || typeof original === "boolean" ? sessionEventJsonBytes(original) : original === null || original === void 0 ? 0 : null : null,
4881
+ deliveredBytes: typeof delivered === "string" ? sessionEventUtf8Bytes(delivered) : typeof delivered === "number" || typeof delivered === "boolean" ? sessionEventJsonBytes(delivered) : 0
4882
+ };
4883
+ }
4884
+ function sessionEventCustomSerializerProjection(event) {
4885
+ const projection = {
4886
+ field: "toJSON",
4887
+ originalBytes: null,
4888
+ deliveredBytes: 0
4889
+ };
4890
+ let candidate = event;
4891
+ try {
4892
+ for (let depth = 0; depth <= SESSION_EVENT_PROTOTYPE_MAX_DEPTH; depth += 1) {
4893
+ if (candidate === null) return null;
4894
+ const descriptor = Object.getOwnPropertyDescriptor(candidate, "toJSON");
4895
+ if (descriptor) {
4896
+ return !("value" in descriptor) || typeof descriptor.value === "function" ? projection : null;
4897
+ }
4898
+ candidate = Object.getPrototypeOf(candidate);
4899
+ }
4900
+ return projection;
4901
+ } catch {
4902
+ return projection;
4903
+ }
4904
+ }
4905
+ var SESSION_EVENT_PROTOTYPE_MAX_DEPTH = 32;
4906
+ var SESSION_EVENT_ZERO_UUID = "00000000-0000-4000-8000-000000000000";
4907
+ var SESSION_EVENT_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;
4908
+ var SESSION_EVENT_OWN_DATA_FIELDS = [
4909
+ "id",
4910
+ "workspaceId",
4911
+ "sessionId",
4912
+ "sequence",
4913
+ "type",
4914
+ "payload",
4915
+ "occurredAt",
4916
+ "clientEventId",
4917
+ "turnId",
4918
+ "turnGeneration",
4919
+ "turnAttemptId",
4920
+ "turnAssociation",
4921
+ "duplicateOfEventId",
4922
+ "duplicateReason"
4923
+ ];
4924
+ var SESSION_EVENT_KNOWN_ENUMERABLE_FIELDS = /* @__PURE__ */ new Set([
4925
+ ...SESSION_EVENT_OWN_DATA_FIELDS,
4926
+ "toJSON"
4927
+ ]);
4928
+ function sessionEventAdditionalTopLevelFieldProjection(event) {
4929
+ const projection = {
4930
+ field: "additionalTopLevelFields",
4931
+ originalBytes: null,
4932
+ deliveredBytes: 0
4933
+ };
4934
+ let inspected = 0;
4935
+ try {
4936
+ for (const key in event) {
4937
+ inspected += 1;
4938
+ if (inspected > SESSION_EVENT_KNOWN_ENUMERABLE_FIELDS.size + 1) return projection;
4939
+ const descriptor = Object.getOwnPropertyDescriptor(event, key);
4940
+ if (descriptor?.enumerable && !SESSION_EVENT_KNOWN_ENUMERABLE_FIELDS.has(key)) {
4941
+ return projection;
4942
+ }
4943
+ }
4944
+ return null;
4945
+ } catch {
4946
+ return projection;
4947
+ }
4948
+ }
4949
+ function sessionEventOwnDataFields(event) {
4950
+ return Object.fromEntries(
4951
+ SESSION_EVENT_OWN_DATA_FIELDS.map((key) => {
4952
+ try {
4953
+ const descriptor = Object.getOwnPropertyDescriptor(event, key);
4954
+ if (!descriptor) return [key, { readable: true, value: void 0 }];
4955
+ return [
4956
+ key,
4957
+ "value" in descriptor ? { readable: true, value: descriptor.value } : { readable: false }
4958
+ ];
4959
+ } catch {
4960
+ return [key, { readable: false }];
4961
+ }
4962
+ })
4963
+ );
4964
+ }
4965
+ function canonicalSessionEventUuid(field, fallback) {
4966
+ return field.readable && typeof field.value === "string" && SESSION_EVENT_UUID_PATTERN.test(field.value) ? field.value : fallback;
4967
+ }
4968
+ function canonicalOptionalSessionEventUuid(field) {
4969
+ return field.readable && typeof field.value === "string" && SESSION_EVENT_UUID_PATTERN.test(field.value) ? field.value : null;
4970
+ }
4971
+ function canonicalSessionEventGeneration(field) {
4972
+ return field.readable && typeof field.value === "number" && Number.isSafeInteger(field.value) && field.value >= 0 ? field.value : null;
4973
+ }
4974
+ function sessionEventShouldEmitOptionalField(field) {
4975
+ return !field.readable || field.value !== void 0;
4976
+ }
4977
+ function sessionEventCanonicalFieldProjections(source, delivered) {
4978
+ return ["id", "workspaceId", "sessionId", "sequence", "occurredAt"].flatMap(
4979
+ (field) => {
4980
+ const original = source[field].readable ? source[field].value : void 0;
4981
+ return source[field].readable && original === delivered[field] ? [] : [
4982
+ sessionEventEnvelopeFieldProjection(
4983
+ field,
4984
+ original,
4985
+ delivered[field],
4986
+ source[field].readable
4987
+ )
4988
+ ];
4989
+ }
4990
+ );
4991
+ }
4992
+ function sessionEventOptionalFieldProjections(source, delivered) {
4993
+ return ["turnId", "turnGeneration", "turnAttemptId", "duplicateOfEventId"].flatMap(
4994
+ (field) => {
4995
+ const original = source[field].readable ? source[field].value : void 0;
4996
+ const canonicalOriginal = original ?? null;
4997
+ return source[field].readable && canonicalOriginal === delivered[field] ? [] : [
4998
+ sessionEventEnvelopeFieldProjection(
4999
+ field,
5000
+ original,
5001
+ delivered[field],
5002
+ source[field].readable
5003
+ )
5004
+ ];
5005
+ }
5006
+ );
5007
+ }
5008
+ function boundOptionalSessionEventText(value, maxBytes) {
5009
+ return typeof value === "string" ? boundSessionEventText(value, maxBytes) : value;
5010
+ }
5011
+ function boundSessionEventText(value, maxBytes) {
5012
+ const encoder2 = new TextEncoder();
5013
+ const decoder = new TextDecoder();
5014
+ const bytes = encoder2.encode(value);
5015
+ if (bytes.byteLength <= maxBytes) return value;
5016
+ const marker = "\u2026[truncated]";
5017
+ const markerBytes = encoder2.encode(marker).byteLength;
5018
+ const prefixBudget = Math.max(0, maxBytes - markerBytes);
5019
+ let prefixEnd = Math.min(prefixBudget, bytes.byteLength);
5020
+ while (prefixEnd > 0 && prefixEnd < bytes.byteLength && (bytes[prefixEnd] & 192) === 128) {
5021
+ prefixEnd -= 1;
5022
+ }
5023
+ return `${decoder.decode(bytes.subarray(0, prefixEnd))}${marker}`;
5024
+ }
5025
+ function sessionEventUtf8Bytes(value) {
5026
+ return new TextEncoder().encode(value).byteLength;
5027
+ }
5028
+ var SessionQueueMutationResponse = z.object({
5029
+ receipt: SessionCommandReceipt,
5030
+ snapshot: SessionQueueSnapshot,
5031
+ draft: ComposerDraft.optional()
5032
+ });
5033
+ var SessionControlResponse = z.object({
5034
+ receipt: SessionCommandReceipt,
5035
+ effectiveControl: EffectiveSessionControl,
5036
+ interruptionCount: z.number().int().nonnegative(),
5037
+ wakeCount: z.number().int().nonnegative()
5038
+ });
5039
+ var CreateSessionRequest = withVariableSetIdAlias({
5040
+ /**
5041
+ * Optional UUID preallocated by an embedding host. This lets the host durably
5042
+ * link its own projection before OpenGeni admits the initial turn. Replays
5043
+ * must pair it with the same idempotency key; OpenGeni never derives host
5044
+ * identity or authorization from the UUID.
5045
+ */
5046
+ requestedSessionId: z.string().uuid().optional(),
2296
5047
  initialMessage: z.string().min(1),
5048
+ // System-level host context for the initial turn only. Unlike `instructions`,
5049
+ // this does not persist into later turns and is never emitted as a user event.
5050
+ turnInstructions: z.string().trim().min(1).max(32768).optional(),
2297
5051
  // Per-session agent persona/system instructions (org-visible metadata, NOT a
2298
5052
  // secret). Rides the SAME system-level instructions channel the per-workspace
2299
5053
  // agentInstructions rides, composed AFTER the workspace persona so it refines
@@ -2301,9 +5055,16 @@ var CreateSessionRequest = z.object({
2301
5055
  // leaking them into the user-visible timeline (it is NEVER emitted as an
2302
5056
  // event, unlike goal/initialMessage). Trimmed, non-empty. The 32768-char cap
2303
5057
  // matches the codebase's largest free-form string convention (workspace
2304
- // environment variable values). Absent ⇒ byte-identical to today.
5058
+ // variable set variable values). Absent ⇒ byte-identical to today.
2305
5059
  instructions: z.string().trim().min(1).max(32768).optional(),
5060
+ // For an agent-created child, omission inherits the trusted immediate
5061
+ // parent's repository/file context. An explicit array, including [], is
5062
+ // authoritative. Top-level omission remains []. Presence is resolved from
5063
+ // the raw request because this Zod default erases absent-vs-empty.
2306
5064
  resources: z.array(ResourceRef).default([]),
5065
+ // The same child omission rule applies to selected MCP tool refs. Top-level
5066
+ // omission still applies workspace-default capability MCP tools; explicit []
5067
+ // suppresses those defaults (the first-party OpenGeni server remains added).
2307
5068
  tools: z.array(ToolRef).default([]),
2308
5069
  metadata: z.record(z.string(), z.unknown()).default({}),
2309
5070
  model: z.string().min(1).optional(),
@@ -2320,11 +5081,17 @@ var CreateSessionRequest = z.object({
2320
5081
  // (the agent's resolve_cwd handles both). Only valid WITH targetSandboxId
2321
5082
  // (workingDir alone is a 422); omitted ⇒ the machine's default workspace_root.
2322
5083
  workingDir: z.string().min(1).optional(),
2323
- // Workspace environment attachment is fixed at session creation; follow-up
5084
+ // Variable set attachment is fixed at session creation; follow-up
2324
5085
  // user.message events cannot switch or add one.
5086
+ variableSetId: z.string().uuid().optional(),
2325
5087
  environmentId: z.string().uuid().optional(),
5088
+ // The rig to bind this session to (M3). Its ACTIVE version is resolved and
5089
+ // FROZEN onto the session at create. Omitted ⇒ the workspace's default rig
5090
+ // (workspaces.default_rig_id) when set, else a rig-less session (today's
5091
+ // behavior). An id that does not name a rig in the workspace is a 422.
5092
+ rigId: z.string().uuid().optional(),
2326
5093
  goal: GoalSpec.optional(),
2327
- clientEventId: z.string().min(1).optional(),
5094
+ clientEventId: SessionOperationKey.optional(),
2328
5095
  // Workspace-scoped CREATE idempotency key: collapses concurrent/retried
2329
5096
  // create calls carrying the same key to a single session (partial unique
2330
5097
  // index on (workspace_id, create_idempotency_key)). Distinct from
@@ -2332,13 +5099,19 @@ var CreateSessionRequest = z.object({
2332
5099
  // creation of a brand-new session. Absent means no create-dedup (each call
2333
5100
  // is an independent create).
2334
5101
  idempotencyKey: z.string().min(1).max(200).optional(),
2335
- // Permissions the session's first-party MCP token should carry instead of
2336
- // the fixed worker default how an operator hands a manager-style session
2337
- // the orchestration/environment/github tools. Capped at creation: every
2338
- // requested permission must be held by the creating grant (no escalation).
5102
+ // Permissions the session's first-party MCP token should carry. A top-level
5103
+ // omission uses the deployment's worker default; a child omission inherits
5104
+ // the creating session's effective grant. An explicit set is capped at
5105
+ // creation: every requested permission must be held by the creating grant.
5106
+ // A goal-bearing session whose explicit/effective set omits goals:manage is
5107
+ // rejected; creation never silently expands a child beyond that set.
2339
5108
  firstPartyMcpPermissions: z.array(Permission).optional(),
2340
- // Third-party MCP servers attached only to this session. Credential headers are
2341
- // write-only: create responses and events expose only SessionMcpServerMetadata.
5109
+ // Third-party MCP servers attached only to this session. For an agent-created
5110
+ // child, omission snapshots its trusted immediate parent's server definitions,
5111
+ // policies, connection refs, and encrypted credentials. Explicit arrays,
5112
+ // including [], are authoritative; non-empty explicit arrays require attach
5113
+ // permission. Credential headers are write-only: create responses and events
5114
+ // expose only SessionMcpServerMetadata.
2342
5115
  mcpServers: z.array(SessionMcpServerInput).default([]),
2343
5116
  // Shared-sandbox placement (addendum 05 §D.1). Three-way union; OMITTED ⇒
2344
5117
  // today's behavior (a context-dependent default resolved server-side: from
@@ -2352,47 +5125,195 @@ var CreateSessionRequest = z.object({
2352
5125
  // A shared spawn inherits the box's (backend, os) — it is literally the same
2353
5126
  // box; the child cannot pick its own backend. Cross-workspace sharing is
2354
5127
  // forbidden by construction (the parent/group reads are RLS-workspace-scoped).
2355
- // ENV-AWARE: the box's environment is fixed at creation, so a share requires
2356
- // the SAME environmentId as the creator's box. On a mismatch the inherited
5128
+ // ENV-AWARE: the box's variable set is fixed at creation, so a share requires
5129
+ // the SAME variableSetId as the creator's box. On a mismatch the inherited
2357
5130
  // default silently falls back to an own box; an explicit "shared"/{groupId}
2358
5131
  // request 422s at create (instead of the first turn dying on the SDK's
2359
5132
  // manifest-env guard).
2360
- sandbox: z.union([
2361
- z.literal("shared"),
2362
- z.literal("new"),
2363
- z.object({ groupId: z.string().uuid() })
2364
- ]).optional()
5133
+ sandbox: z.union([z.literal("shared"), z.literal("new"), z.object({ groupId: z.string().uuid() })]).optional()
5134
+ });
5135
+ var HumanInputQuestionKind = z.enum(["text", "single_select", "multi_select"]);
5136
+ var HumanInputOption = z.object({
5137
+ id: z.string().min(1).max(64),
5138
+ label: z.string().min(1).max(256),
5139
+ description: z.string().max(2048).nullable().optional()
5140
+ });
5141
+ var HumanInputQuestion = z.object({
5142
+ id: z.string().min(1).max(64),
5143
+ kind: HumanInputQuestionKind,
5144
+ prompt: z.string().min(1).max(4096),
5145
+ label: z.string().min(1).max(128).nullable().optional(),
5146
+ helpText: z.string().max(2048).nullable().optional(),
5147
+ options: z.array(HumanInputOption).max(20).default([]),
5148
+ required: z.boolean().default(true),
5149
+ allowOther: z.boolean().default(false),
5150
+ validation: z.object({
5151
+ minLength: z.number().int().nonnegative().max(8192).nullable().optional(),
5152
+ maxLength: z.number().int().positive().max(8192).nullable().optional(),
5153
+ minSelections: z.number().int().nonnegative().max(20).nullable().optional(),
5154
+ maxSelections: z.number().int().positive().max(20).nullable().optional()
5155
+ }).nullable().optional()
5156
+ }).superRefine((question, ctx) => {
5157
+ const optionIds = new Set(question.options.map((option) => option.id));
5158
+ if (optionIds.size !== question.options.length) {
5159
+ ctx.addIssue({
5160
+ code: "custom",
5161
+ path: ["options"],
5162
+ message: "option ids must be unique"
5163
+ });
5164
+ }
5165
+ if (question.kind === "text") {
5166
+ if (question.options.length > 0) {
5167
+ ctx.addIssue({
5168
+ code: "custom",
5169
+ path: ["options"],
5170
+ message: "text questions cannot have options"
5171
+ });
5172
+ }
5173
+ if (question.allowOther) {
5174
+ ctx.addIssue({
5175
+ code: "custom",
5176
+ path: ["allowOther"],
5177
+ message: "text questions do not use Other"
5178
+ });
5179
+ }
5180
+ } else if (question.options.length === 0) {
5181
+ ctx.addIssue({
5182
+ code: "custom",
5183
+ path: ["options"],
5184
+ message: "select questions require options"
5185
+ });
5186
+ }
5187
+ const validation = question.validation;
5188
+ if (validation?.minLength != null && validation?.maxLength != null && validation.minLength > validation.maxLength) {
5189
+ ctx.addIssue({
5190
+ code: "custom",
5191
+ path: ["validation"],
5192
+ message: "minLength exceeds maxLength"
5193
+ });
5194
+ }
5195
+ if (validation?.minSelections != null && validation?.maxSelections != null && validation.minSelections > validation.maxSelections) {
5196
+ ctx.addIssue({
5197
+ code: "custom",
5198
+ path: ["validation"],
5199
+ message: "minSelections exceeds maxSelections"
5200
+ });
5201
+ }
5202
+ });
5203
+ var HumanInputRequestStatus = z.enum([
5204
+ "pending",
5205
+ "answered",
5206
+ "skipped",
5207
+ "expired",
5208
+ "cancelled"
5209
+ ]);
5210
+ var RequestHumanInputToolInput = z.object({
5211
+ questions: z.array(HumanInputQuestion).min(1).max(20),
5212
+ allowSkip: z.boolean().default(false),
5213
+ expiresInSeconds: z.number().int().positive().max(30 * 24 * 60 * 60).nullable().optional()
5214
+ });
5215
+ var HumanInputAnswer = z.object({
5216
+ questionId: z.string().min(1).max(64),
5217
+ values: z.array(z.string().max(8192)).max(20),
5218
+ other: z.string().max(8192).nullable().optional()
5219
+ });
5220
+ var HumanInputResponse = z.discriminatedUnion("outcome", [
5221
+ z.object({
5222
+ outcome: z.literal("answered"),
5223
+ answers: z.array(HumanInputAnswer).max(20)
5224
+ }),
5225
+ z.object({ outcome: z.literal("skipped") }),
5226
+ z.object({ outcome: z.literal("expired") }),
5227
+ z.object({ outcome: z.literal("cancelled") })
5228
+ ]);
5229
+ var SubmitHumanInputResponseRequest = z.discriminatedUnion("outcome", [
5230
+ z.object({
5231
+ outcome: z.literal("answered"),
5232
+ answers: z.array(HumanInputAnswer).max(20)
5233
+ }),
5234
+ z.object({ outcome: z.literal("skipped") })
5235
+ ]);
5236
+ var SessionHumanInputRequest = z.object({
5237
+ id: z.string().uuid(),
5238
+ workspaceId: z.string().uuid(),
5239
+ sessionId: z.string().uuid(),
5240
+ turnId: z.string().uuid(),
5241
+ turnGeneration: z.number().int().positive(),
5242
+ creationAttemptId: z.string().uuid(),
5243
+ toolCallId: z.string().min(1).max(1024),
5244
+ status: HumanInputRequestStatus,
5245
+ questions: z.array(HumanInputQuestion).min(1).max(20),
5246
+ allowSkip: z.boolean(),
5247
+ response: HumanInputResponse.nullable(),
5248
+ respondedBy: z.string().max(1024).nullable(),
5249
+ respondedAt: z.string().nullable(),
5250
+ expiresAt: z.string().nullable(),
5251
+ createdAt: z.string(),
5252
+ updatedAt: z.string()
2365
5253
  });
5254
+ function approvalIdentifier(value) {
5255
+ if (!value || typeof value !== "object") return null;
5256
+ const approval = value;
5257
+ const rawItem = approval.rawItem && typeof approval.rawItem === "object" ? approval.rawItem : null;
5258
+ const candidate = rawItem?.callId ?? rawItem?.id ?? approval.id ?? approval.name;
5259
+ if (typeof candidate !== "string" && typeof candidate !== "number") return null;
5260
+ return String(candidate);
5261
+ }
2366
5262
  var ClientSessionEvent = z.discriminatedUnion("type", [
2367
5263
  z.object({
2368
5264
  type: z.literal("user.message"),
2369
- clientEventId: z.string().min(1).optional(),
5265
+ clientEventId: SessionOperationKey.optional(),
2370
5266
  payload: z.object({
2371
5267
  text: z.string().min(1),
5268
+ // System-level host context for this exact turn only. Persisted on the
5269
+ // turn for retry/recovery, never copied into the visible user message.
5270
+ turnInstructions: z.string().trim().min(1).max(32768).optional(),
2372
5271
  resources: z.array(ResourceRef).default([]),
2373
5272
  tools: z.array(ToolRef).default([]),
2374
5273
  model: z.string().min(1).optional(),
2375
5274
  reasoningEffort: ReasoningEffort.optional(),
5275
+ controlEtag: z.string().min(1).optional(),
5276
+ expectedDraftRevision: z.number().int().nonnegative().optional(),
2376
5277
  // Header-value rotation only. URL/name/tool settings are immutable after
2377
5278
  // session create; persisted events expose metadata, never header values.
2378
5279
  mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional()
2379
5280
  })
2380
5281
  }),
2381
- z.object({
2382
- type: z.literal("user.interrupt"),
2383
- clientEventId: z.string().min(1).optional(),
2384
- payload: z.object({ reason: z.string().optional() }).default({})
2385
- }),
2386
5282
  z.object({
2387
5283
  type: z.literal("user.approvalDecision"),
2388
- clientEventId: z.string().min(1).optional(),
5284
+ clientEventId: SessionOperationKey.optional(),
2389
5285
  payload: z.object({
2390
- approvalId: z.string().min(1),
5286
+ approvalId: z.string().min(1).max(SESSION_OPERATION_KEY_MAX_CHARS),
2391
5287
  decision: z.enum(["approve", "reject"]),
2392
5288
  message: z.string().optional()
2393
5289
  })
5290
+ }),
5291
+ z.object({
5292
+ type: z.literal("user.humanInputResponse"),
5293
+ clientEventId: SessionOperationKey.optional(),
5294
+ payload: z.object({
5295
+ requestId: z.string().uuid(),
5296
+ response: SubmitHumanInputResponseRequest
5297
+ })
2394
5298
  })
2395
5299
  ]);
5300
+ var SteerSessionMessageRequest = z.object({
5301
+ text: z.string().min(1),
5302
+ // Same per-turn system-level context as a queued user.message.
5303
+ turnInstructions: z.string().trim().min(1).max(32768).optional(),
5304
+ resources: z.array(ResourceRef).default([]),
5305
+ tools: z.array(ToolRef).default([]),
5306
+ model: z.string().min(1).optional(),
5307
+ reasoningEffort: ReasoningEffort.optional(),
5308
+ clientEventId: SessionOperationKey.optional(),
5309
+ controlEtag: z.string().min(1).optional(),
5310
+ expectedDraftRevision: z.number().int().nonnegative().optional(),
5311
+ mcpCredentialUpdates: z.array(SessionMcpCredentialUpdateInput).optional()
5312
+ });
5313
+ var SteerSessionMessageResponse = z.object({
5314
+ accepted: SessionEvent,
5315
+ turn: SessionTurn
5316
+ });
2396
5317
  var SessionBusMessage = z.object({
2397
5318
  workspaceId: z.string().uuid(),
2398
5319
  sessionId: z.string().uuid(),
@@ -2416,6 +5337,29 @@ var GitHubRepository = z.object({
2416
5337
  accountLogin: z.string(),
2417
5338
  accountType: z.string().nullable()
2418
5339
  });
5340
+ var GitHubRepositoryScope = z.enum(["all", "selected"]);
5341
+ var GitHubInstallationBinding = z.object({
5342
+ installationId: z.number().int().positive(),
5343
+ accountLogin: z.string().nullable(),
5344
+ accountType: z.string().nullable(),
5345
+ repositoryScope: GitHubRepositoryScope,
5346
+ repositoryCount: z.number().int().nonnegative(),
5347
+ createdAt: z.string(),
5348
+ updatedAt: z.string()
5349
+ });
5350
+ var GitHubAppInfo = z.object({
5351
+ configured: z.boolean(),
5352
+ appId: z.string().nullable(),
5353
+ clientId: z.string().nullable(),
5354
+ appSlug: z.string().nullable(),
5355
+ installUrl: z.string().nullable(),
5356
+ linkUrl: z.string().nullable(),
5357
+ installations: z.array(GitHubInstallationBinding),
5358
+ missing: z.array(z.string())
5359
+ });
5360
+ var GitHubRepositoriesResponse = z.object({
5361
+ repositories: z.array(GitHubRepository)
5362
+ });
2419
5363
  var ClientAuthConfig = z.discriminatedUnion("mode", [
2420
5364
  z.object({
2421
5365
  mode: z.literal("none")
@@ -2544,7 +5488,7 @@ var ViewerHolder = z.object({
2544
5488
  leaseEpoch: z.number().int().nonnegative(),
2545
5489
  viewerHeartbeatIntervalMs: z.number().int().positive(),
2546
5490
  // The desktop pixel tunnel URL the viewer connects to directly; null until
2547
- // P4 mints it (gated until then).
5491
+ // a viewer grant is minted (gated until then).
2548
5492
  dataPlaneUrl: z.string().nullable()
2549
5493
  });
2550
5494
  var AcknowledgeStreamRequest = z.object({
@@ -2611,7 +5555,13 @@ var DeviceEnrollmentApproveResponse = z.object({
2611
5555
  var DeviceEnrollmentPollRequest = z.object({
2612
5556
  deviceCode: z.string().min(1).max(256)
2613
5557
  });
2614
- var DeviceEnrollmentState = z.enum(["pending", "authorized", "denied", "expired", "disabled"]);
5558
+ var DeviceEnrollmentState = z.enum([
5559
+ "pending",
5560
+ "authorized",
5561
+ "denied",
5562
+ "expired",
5563
+ "disabled"
5564
+ ]);
2615
5565
  var EnrollmentCredentialsResponse = z.object({
2616
5566
  agentId: z.string().uuid(),
2617
5567
  workspaceId: z.string().uuid(),
@@ -2774,7 +5724,18 @@ var SwapActiveSandboxResponse = z.object({
2774
5724
  swapped: z.boolean(),
2775
5725
  activeSandboxId: z.string().nullable(),
2776
5726
  activeEpoch: z.number().int(),
2777
- reason: z.string().optional()
5727
+ reason: z.string().optional(),
5728
+ // Typed rejection discriminant (issue #341). Present only when swapped is false,
5729
+ // so a client distinguishes a deleted/absent target from an unaddressable
5730
+ // enrollment from a backend the turn cannot establish from a lost epoch race —
5731
+ // without parsing the human reason string.
5732
+ code: z.enum([
5733
+ "stale_pointer",
5734
+ "offline_enrollment",
5735
+ "unsupported_backend_context",
5736
+ "transient_establishment",
5737
+ "concurrent_swap"
5738
+ ]).optional()
2778
5739
  });
2779
5740
  var MachineMetricsSeriesResponse = z.object({
2780
5741
  samples: z.array(MetricSample)
@@ -2788,8 +5749,11 @@ var ClientModel = z.object({
2788
5749
  api: z.enum(["responses", "chat"]),
2789
5750
  contextWindowTokens: z.number().int().positive().optional()
2790
5751
  });
5752
+ var OPENGENI_API_CONTRACT_REVISION = "2026-07-turn-instructions-v1";
5753
+ var OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract";
2791
5754
  var ClientConfig = z.object({
2792
5755
  deploymentRevision: z.string(),
5756
+ apiContractRevision: z.literal(OPENGENI_API_CONTRACT_REVISION),
2793
5757
  // Release-train version of the server (absent on dev/source builds). The
2794
5758
  // compatibility policy lives in docs/architecture.md — clients within the
2795
5759
  // same major are supported; evolution is additive within a major.
@@ -2801,10 +5765,12 @@ var ClientConfig = z.object({
2801
5765
  models: z.array(ClientModel).default([]),
2802
5766
  defaultReasoningEffort: ReasoningEffort,
2803
5767
  allowedReasoningEfforts: z.array(ReasoningEffort).min(1),
2804
- mcpServers: z.array(z.object({
2805
- id: z.string(),
2806
- name: z.string()
2807
- })).default([]),
5768
+ mcpServers: z.array(
5769
+ z.object({
5770
+ id: z.string(),
5771
+ name: z.string()
5772
+ })
5773
+ ).default([]),
2808
5774
  fileUploads: z.object({
2809
5775
  enabled: z.boolean(),
2810
5776
  maxSizeBytes: z.number().int().positive()
@@ -2850,6 +5816,18 @@ function constantTimeEqual(actual, expected) {
2850
5816
  }
2851
5817
  return diff === 0;
2852
5818
  }
5819
+ function evaluateWorkspaceModelPolicy(policy, candidate) {
5820
+ if (!policy) {
5821
+ return { allowed: true };
5822
+ }
5823
+ if (policy.allowedProviders !== null && !policy.allowedProviders.includes(candidate.providerId)) {
5824
+ return { allowed: false, reason: "provider" };
5825
+ }
5826
+ if (policy.allowedModels !== null && !policy.allowedModels.includes(candidate.modelId)) {
5827
+ return { allowed: false, reason: "model" };
5828
+ }
5829
+ return { allowed: true };
5830
+ }
2853
5831
  export {
2854
5832
  AccessContext,
2855
5833
  AccessGrant,
@@ -2891,6 +5869,7 @@ export {
2891
5869
  CompactSessionContextRequest,
2892
5870
  CompactSessionContextResult,
2893
5871
  CompleteFileUploadResponse,
5872
+ ComposerDraft,
2894
5873
  ConnectionCredentialBundle,
2895
5874
  ConnectionKind,
2896
5875
  ConnectionMetadata,
@@ -2906,14 +5885,19 @@ export {
2906
5885
  CreateFileUploadRequest,
2907
5886
  CreateFileUploadResponse,
2908
5887
  CreateKnowledgeMemoryRequest,
5888
+ CreateRigRequest,
2909
5889
  CreateScheduledTaskRequest,
2910
5890
  CreateSessionRequest,
5891
+ CreateSessionResponse,
2911
5892
  CreateSocialConnectionRequest,
2912
5893
  CreateSocialPostRequest,
5894
+ CreateVariableSetRequest,
2913
5895
  CreateWorkspaceEnvironmentRequest,
2914
5896
  CreateWorkspaceRequest,
5897
+ CredentialAuthNeededPayload,
2915
5898
  DESKTOP_STREAM_PORT,
2916
5899
  DelegatedAccessTokenPayload,
5900
+ DeleteSessionQueueItemRequest,
2917
5901
  DeviceEnrollmentApproveRequest,
2918
5902
  DeviceEnrollmentApproveResponse,
2919
5903
  DeviceEnrollmentDenyRequest,
@@ -2933,6 +5917,10 @@ export {
2933
5917
  DocumentSearchRequest,
2934
5918
  DocumentSearchResult,
2935
5919
  DocumentStatus,
5920
+ EditSessionQueueItemRequest,
5921
+ EffectiveControlBlocker,
5922
+ EffectiveControlResumeOption,
5923
+ EffectiveSessionControl,
2936
5924
  EnableCapabilityRequest,
2937
5925
  EnablePackRequest,
2938
5926
  EnrollTokenExchangeRequest,
@@ -2971,8 +5959,13 @@ export {
2971
5959
  FsTreeNode,
2972
5960
  FsWriteRequest,
2973
5961
  FsWriteResponse,
5962
+ GetWorkspaceCaptureFileResponse,
5963
+ GetWorkspaceCaptureResponse,
2974
5964
  GitChangedPayload,
2975
5965
  GitCommit,
5966
+ GitCredentialBindingId,
5967
+ GitCredentialProvider,
5968
+ GitCredentialRepositoryRef,
2976
5969
  GitDiffHunk,
2977
5970
  GitDiffLine,
2978
5971
  GitDiffLineType,
@@ -2981,15 +5974,36 @@ export {
2981
5974
  GitFileDiff,
2982
5975
  GitFileStatus,
2983
5976
  GitFileStatusCode,
5977
+ GitHubAppInfo,
2984
5978
  GitHubAppManifestCreate,
5979
+ GitHubInstallationBinding,
5980
+ GitHubRepositoriesResponse,
2985
5981
  GitHubRepository,
5982
+ GitHubRepositoryScope,
2986
5983
  GitLogRequest,
2987
5984
  GitLogResponse,
5985
+ GitRepositoryAccess,
2988
5986
  GitShowRequest,
2989
5987
  GitShowResponse,
2990
5988
  GitStatusRequest,
2991
5989
  GitStatusResponse,
2992
5990
  GoalSpec,
5991
+ HostEventExport,
5992
+ HostEventExportBatch,
5993
+ HostExportConsumerId,
5994
+ HostExportCursor,
5995
+ HostExportInitiator,
5996
+ HostExportInitiatorContext,
5997
+ HostSessionEvent,
5998
+ HostUsageEvent,
5999
+ HostUsageExport,
6000
+ HostUsageExportBatch,
6001
+ HumanInputAnswer,
6002
+ HumanInputOption,
6003
+ HumanInputQuestion,
6004
+ HumanInputQuestionKind,
6005
+ HumanInputRequestStatus,
6006
+ HumanInputResponse,
2993
6007
  IntegrationClientMetadata,
2994
6008
  KnowledgeMemory,
2995
6009
  KnowledgeMemoryKind,
@@ -2999,6 +6013,7 @@ export {
2999
6013
  KnowledgeSourceRef,
3000
6014
  LimitAction,
3001
6015
  LimitDecision,
6016
+ LineageNode,
3002
6017
  ListConnectionsResponse,
3003
6018
  ListEnrollmentsResponse,
3004
6019
  ListWorkspaceMembersResponse,
@@ -3009,16 +6024,22 @@ export {
3009
6024
  MachinesResponse,
3010
6025
  ManagedAccount,
3011
6026
  MarketingDailyAnalysisTaskRequest,
6027
+ McpConnectionResourceScope,
3012
6028
  McpServerConnectionRef,
3013
6029
  MetricSample,
3014
6030
  MintEnrollTokenRequest,
3015
6031
  MintEnrollTokenResponse,
6032
+ MoveSessionQueueItemRequest,
3016
6033
  OAuthStartRequest,
3017
6034
  OAuthStartResponse,
6035
+ OPENGENI_API_CONTRACT_HEADER,
6036
+ OPENGENI_API_CONTRACT_REVISION,
6037
+ OPENGENI_HOST_EXPORT_SCHEMA_REVISION,
3018
6038
  PackInstallation,
3019
6039
  PackInstallationStatus,
3020
6040
  Permission,
3021
6041
  ProductAccessMode,
6042
+ ProposeRigChangeRequest,
3022
6043
  PtyCloseRequest,
3023
6044
  PtyOpenRequest,
3024
6045
  PtyOpenResponse,
@@ -3034,15 +6055,38 @@ export {
3034
6055
  RecordingStartedPayload,
3035
6056
  RegisterCapabilityPackRequest,
3036
6057
  RelayTokenPayload,
3037
- ReorderSessionTurnsRequest,
3038
6058
  RepositoryResourceRef,
6059
+ RequestHumanInputToolInput,
6060
+ ResourceMountPathError,
3039
6061
  ResourceRef,
3040
6062
  ResourceRefConflictError,
3041
6063
  RevokeEnrollmentResponse,
6064
+ Rig,
6065
+ RigChange,
6066
+ RigChangeKind,
6067
+ RigChangeStatus,
6068
+ RigChangeVerification,
6069
+ RigCheck,
6070
+ RigCheckResult,
6071
+ RigDefinitionEditPayload,
6072
+ RigSetupAppendPayload,
6073
+ RigVerificationHealth,
6074
+ RigVersion,
6075
+ SESSION_AUTHORIZATION_LIST_SCOPE_MAX_IDS,
6076
+ SESSION_EVENT_CLIENT_EVENT_ID_MAX_BYTES,
6077
+ SESSION_EVENT_DUPLICATE_REASON_MAX_BYTES,
6078
+ SESSION_EVENT_ENVELOPE_MAX_BYTES,
6079
+ SESSION_EVENT_PAYLOAD_MAX_BYTES,
6080
+ SESSION_EVENT_RAW_DELTA_TYPES,
6081
+ SESSION_EVENT_SEMANTIC_CLASS_TYPES,
6082
+ SESSION_EVENT_TURN_ASSOCIATION_MAX_BYTES,
6083
+ SESSION_EVENT_TYPE_MAX_BYTES,
6084
+ SESSION_OPERATION_KEY_MAX_CHARS,
3042
6085
  SandboxBackend,
3043
6086
  SandboxCapabilityName,
3044
6087
  SandboxCommandOutputDeltaPayload,
3045
6088
  SandboxOs,
6089
+ SaveComposerDraftRequest,
3046
6090
  ScheduledTask,
3047
6091
  ScheduledTaskAgentConfig,
3048
6092
  ScheduledTaskOverlapPolicy,
@@ -3052,36 +6096,68 @@ export {
3052
6096
  ScheduledTaskScheduleSpec,
3053
6097
  ScheduledTaskStatus,
3054
6098
  ScheduledTaskTriggerType,
6099
+ ServiceTurnInitiator,
6100
+ ServiceTurnInitiatorContext,
3055
6101
  Session,
6102
+ SessionAuthorizationActor,
6103
+ SessionAuthorizationDecision,
6104
+ SessionAuthorizationListScope,
6105
+ SessionAuthorizationOperation,
6106
+ SessionAuthorizationSurface,
6107
+ SessionAuthorizationTarget,
3056
6108
  SessionBusMessage,
3057
6109
  SessionCapabilities,
6110
+ SessionCommandReceipt,
6111
+ SessionControlRequest,
6112
+ SessionControlResponse,
6113
+ SessionControlState,
3058
6114
  SessionEvent,
6115
+ SessionEventPayloadMode,
6116
+ SessionEventReadDirection,
6117
+ SessionEventReadMode,
6118
+ SessionEventSemanticClass,
3059
6119
  SessionEventType,
3060
6120
  SessionGoal,
3061
6121
  SessionGoalCreatedBy,
3062
6122
  SessionGoalPausedReason,
3063
6123
  SessionGoalStatus,
6124
+ SessionHumanInputRequest,
6125
+ SessionLineageResponse,
6126
+ SessionListResponse,
3064
6127
  SessionMcpCredentialUpdateInput,
3065
6128
  SessionMcpServerInput,
3066
6129
  SessionMcpServerMetadata,
6130
+ SessionQueueMutationResponse,
6131
+ SessionQueueSnapshot,
3067
6132
  SessionStatus,
3068
6133
  SessionStructuredCapabilities,
6134
+ SessionSystemUpdate,
6135
+ SessionSystemUpdateKind,
6136
+ SessionSystemUpdatePayload,
6137
+ SessionSystemUpdateState,
3069
6138
  SessionTurn,
3070
6139
  SessionTurnSource,
3071
6140
  SessionTurnStatus,
6141
+ SetVariableSetVariableRequest,
6142
+ SetWorkspaceDefaultRigRequest,
3072
6143
  SetWorkspaceEnvironmentVariableRequest,
3073
6144
  SocialConnection,
3074
6145
  SocialConnectionStatus,
3075
6146
  SocialPost,
3076
6147
  SocialProvider,
3077
6148
  StaticUsageLimits,
6149
+ SteerSessionMessageRequest,
6150
+ SteerSessionMessageResponse,
6151
+ SteerSessionQueueItemRequest,
3078
6152
  StreamClosedPayload,
3079
6153
  StreamOpenedPayload,
3080
6154
  StreamRevokedPayload,
3081
6155
  StreamTokenPayload,
3082
6156
  StreamUrlRotatedPayload,
6157
+ SubmitHumanInputResponseRequest,
3083
6158
  SwapActiveSandboxRequest,
3084
6159
  SwapActiveSandboxResponse,
6160
+ SystemUpdateClassification,
3085
6161
  TERMINAL_STREAM_PORT,
3086
6162
  TerminalExecRequest,
3087
6163
  TerminalExecResponse,
@@ -3090,34 +6166,93 @@ export {
3090
6166
  TerminalPtyStartedPayload,
3091
6167
  ToolAuthNeededPayload,
3092
6168
  ToolRef,
6169
+ TranscriptionErrorCode,
6170
+ TranscriptionEvent,
6171
+ TranscriptionResultMetadata,
6172
+ TranscriptionSpeaker,
6173
+ TranscriptionTimeSpan,
6174
+ TranscriptionWord,
3093
6175
  TriggerScheduledTaskRequest,
6176
+ TurnInitiator,
6177
+ TurnInitiatorContext,
6178
+ UNATTRIBUTED_LEGACY_INITIATOR_SUBJECT_ID,
3094
6179
  UpdateConnectionRequest,
3095
6180
  UpdateKnowledgeMemoryRequest,
6181
+ UpdateRigRequest,
3096
6182
  UpdateScheduledTaskRequest,
3097
6183
  UpdateSessionGoalRequest,
6184
+ UpdateSessionPinRequest,
3098
6185
  UpdateSessionRequest,
3099
- UpdateSessionTurnRequest,
6186
+ UpdateVariableSetRequest,
3100
6187
  UpdateWorkspaceEnvironmentRequest,
3101
6188
  UpdateWorkspaceMemberRequest,
6189
+ UpdateWorkspaceModelPolicyRequest,
3102
6190
  UpdateWorkspaceRequest,
6191
+ UpdateWorkspaceSettingsRequest,
3103
6192
  UsageEvent,
3104
6193
  UsageEventType,
3105
6194
  UsageLimitsMode,
6195
+ VariableSet,
6196
+ VariableSetVariableMetadata,
6197
+ VariableSetVariableName,
3106
6198
  ViewerHeartbeatRequest,
3107
6199
  ViewerHeartbeatResponse,
3108
6200
  ViewerHolder,
6201
+ WORKSPACE_CONTROL_ACTOR_MAX_BYTES,
6202
+ WORKSPACE_CONTROL_EVENT_MAX_BYTES,
6203
+ WORKSPACE_CONTROL_REASON_MAX_BYTES,
3109
6204
  Workspace,
6205
+ WorkspaceCaptureDegradedReason,
6206
+ WorkspaceCaptureFile,
6207
+ WorkspaceCaptureManifest,
6208
+ WorkspaceCaptureRepo,
6209
+ WorkspaceCaptureSignedUrl,
6210
+ WorkspaceCaptureStats,
6211
+ WorkspaceControlEvent,
6212
+ WorkspaceControlEventTruncation,
3110
6213
  WorkspaceEnvironment,
3111
6214
  WorkspaceEnvironmentVariableMetadata,
3112
- WorkspaceEnvironmentVariableName,
6215
+ WorkspaceInferenceControlRequest,
6216
+ WorkspaceInferenceControlResponse,
6217
+ WorkspaceInferenceState,
3113
6218
  WorkspaceMember,
6219
+ WorkspaceMemorySearchMode,
6220
+ WorkspaceMemorySearchRequest,
6221
+ WorkspaceMemorySearchResponse,
6222
+ WorkspaceMemorySearchResult,
3114
6223
  WorkspaceRegisteredPack,
6224
+ WorkspaceRevisionCapturedPayload,
6225
+ WorkspaceRevisionDegradedPayload,
6226
+ WorkspaceSettingsSchema,
6227
+ WorkspaceTranscriptionPolicy,
6228
+ WorkspaceTranscriptionTarget,
6229
+ approvalIdentifier,
6230
+ approximateSessionEventTokens,
6231
+ assertUniqueResourceMountPaths,
6232
+ boundSessionEvent,
6233
+ boundSessionEventPayload,
6234
+ boundWorkspaceControlEvent,
6235
+ defaultRepositoryMountPath,
6236
+ evaluateWorkspaceModelPolicy,
6237
+ gitCredentialBindingIdForRepository,
6238
+ gitCredentialProviderForRepository,
3115
6239
  isClearedRunStateBlob,
6240
+ measureSessionEventJson,
3116
6241
  mergeResourceRefs,
3117
6242
  mergeToolRefs,
6243
+ normalizeRepositorySubpath,
6244
+ normalizeResourceMountPath,
3118
6245
  prefixedMcpToolName,
3119
6246
  reasoningEffortForMetadata,
6247
+ resolveSessionEventTypeFilters,
6248
+ resolveWorkspaceMemoryEnabled,
3120
6249
  resourceIdentityKey,
6250
+ resourceMountPath,
6251
+ resourceMountPathCollisionKey,
6252
+ sessionEventJsonBytes,
6253
+ sessionEventMediaPreview,
6254
+ sessionEventMediaPreviewFromDataUrl,
6255
+ sessionEventPayloadTruncation,
3121
6256
  signDelegatedAccessToken,
3122
6257
  signEnrollToken,
3123
6258
  signEnrollmentBearer,
@@ -3128,6 +6263,7 @@ export {
3128
6263
  verifyEnrollToken,
3129
6264
  verifyEnrollmentBearer,
3130
6265
  verifyRelayToken,
3131
- verifyStreamToken
6266
+ verifyStreamToken,
6267
+ workspaceControlUtf8Bytes
3132
6268
  };
3133
6269
  //# sourceMappingURL=index.js.map