@opengeni/contracts 0.1.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/src/index.ts ADDED
@@ -0,0 +1,1481 @@
1
+ import { z } from "zod";
2
+
3
+ export const SessionStatus = z.enum([
4
+ "queued",
5
+ "running",
6
+ "idle",
7
+ "requires_action",
8
+ "failed",
9
+ "cancelled",
10
+ ]);
11
+ export type SessionStatus = z.infer<typeof SessionStatus>;
12
+
13
+ export const SandboxBackend = z.enum(["docker", "modal", "local", "none"]);
14
+ export type SandboxBackend = z.infer<typeof SandboxBackend>;
15
+
16
+ export const ReasoningEffort = z.enum(["none", "minimal", "low", "medium", "high", "xhigh"]);
17
+ export type ReasoningEffort = z.infer<typeof ReasoningEffort>;
18
+
19
+ export const ErrorCode = z.enum([
20
+ "unauthenticated",
21
+ "forbidden",
22
+ "not_found",
23
+ "validation_failed",
24
+ "conflict",
25
+ "idempotency_conflict",
26
+ "limit_exceeded",
27
+ "provider_verification_failed",
28
+ "upstream_unavailable",
29
+ "internal_error",
30
+ ]);
31
+ export type ErrorCode = z.infer<typeof ErrorCode>;
32
+
33
+ export const ErrorEnvelope = z.object({
34
+ error: z.object({
35
+ code: ErrorCode,
36
+ message: z.string(),
37
+ requestId: z.string().optional(),
38
+ details: z.record(z.string(), z.unknown()).optional(),
39
+ }),
40
+ });
41
+ export type ErrorEnvelope = z.infer<typeof ErrorEnvelope>;
42
+
43
+ export const PageInfo = z.object({
44
+ limit: z.number().int().positive(),
45
+ nextCursor: z.string().nullable(),
46
+ hasMore: z.boolean(),
47
+ });
48
+ export type PageInfo = z.infer<typeof PageInfo>;
49
+
50
+ export function paginated<T extends z.ZodTypeAny>(item: T) {
51
+ return z.object({
52
+ data: z.array(item),
53
+ page: PageInfo,
54
+ });
55
+ }
56
+
57
+ export const Permission = z.enum([
58
+ "account:read",
59
+ "account:admin",
60
+ "members:manage",
61
+ "workspace:create",
62
+ "billing:read",
63
+ "billing:manage",
64
+ "workspace:read",
65
+ "workspace:admin",
66
+ "sessions:create",
67
+ "sessions:read",
68
+ "sessions:control",
69
+ "files:upload",
70
+ "files:read",
71
+ "documents:manage",
72
+ "documents:search",
73
+ "scheduled_tasks:manage",
74
+ "scheduled_tasks:run",
75
+ "github:manage",
76
+ "github:use",
77
+ "api_keys:manage",
78
+ "environments:manage",
79
+ "environments:use",
80
+ "goals:manage",
81
+ ]);
82
+ export type Permission = z.infer<typeof Permission>;
83
+
84
+ export const ProductAccessMode = z.enum(["local", "configured", "managed"]);
85
+ export type ProductAccessMode = z.infer<typeof ProductAccessMode>;
86
+
87
+ export const BillingMode = z.enum(["disabled", "stripe"]);
88
+ export type BillingMode = z.infer<typeof BillingMode>;
89
+
90
+ export const EntitlementsMode = z.enum(["none", "static", "managed"]);
91
+ export type EntitlementsMode = z.infer<typeof EntitlementsMode>;
92
+
93
+ export const UsageLimitsMode = z.enum(["none", "static", "managed"]);
94
+ export type UsageLimitsMode = z.infer<typeof UsageLimitsMode>;
95
+
96
+ export const AccountRole = z.enum(["owner", "admin", "member"]);
97
+ export type AccountRole = z.infer<typeof AccountRole>;
98
+
99
+ export const ManagedAccount = z.object({
100
+ id: z.string().uuid(),
101
+ name: z.string(),
102
+ externalSource: z.string().nullable(),
103
+ externalId: z.string().nullable(),
104
+ createdAt: z.string(),
105
+ updatedAt: z.string(),
106
+ });
107
+ export type ManagedAccount = z.infer<typeof ManagedAccount>;
108
+
109
+ export const Workspace = z.object({
110
+ id: z.string().uuid(),
111
+ accountId: z.string().uuid(),
112
+ name: z.string(),
113
+ slug: z.string().nullable(),
114
+ externalSource: z.string().nullable(),
115
+ externalId: z.string().nullable(),
116
+ // Per-workspace agent persona template (white-label override). null means
117
+ // the deployment default (OPENGENI_AGENT_INSTRUCTIONS_TEMPLATE /
118
+ // DEFAULT_AGENT_INSTRUCTIONS) is used. The runtime always injects the
119
+ // non-bypassable CORE (goal-loop ownership + environment block), so an
120
+ // override restyles the persona without dropping that contract.
121
+ agentInstructions: z.string().nullable(),
122
+ createdAt: z.string(),
123
+ updatedAt: z.string(),
124
+ });
125
+ export type Workspace = z.infer<typeof Workspace>;
126
+
127
+ export const AccountGrant = z.object({
128
+ accountId: z.string().uuid(),
129
+ subjectId: z.string().min(1),
130
+ subjectLabel: z.string().optional(),
131
+ role: AccountRole.optional(),
132
+ permissions: z.array(Permission),
133
+ metadata: z.record(z.string(), z.unknown()).optional(),
134
+ });
135
+ export type AccountGrant = z.infer<typeof AccountGrant>;
136
+
137
+ export const AccessGrant = z.object({
138
+ workspaceId: z.string().uuid(),
139
+ accountId: z.string().uuid(),
140
+ subjectId: z.string().min(1),
141
+ subjectLabel: z.string().optional(),
142
+ permissions: z.array(Permission),
143
+ metadata: z.record(z.string(), z.unknown()).optional(),
144
+ });
145
+ export type AccessGrant = z.infer<typeof AccessGrant>;
146
+
147
+ export const AccessContext = z.object({
148
+ mode: ProductAccessMode,
149
+ subjectId: z.string().min(1),
150
+ subjectLabel: z.string().optional(),
151
+ accountGrants: z.array(AccountGrant),
152
+ workspaceGrants: z.array(AccessGrant),
153
+ defaultAccountId: z.string().uuid().nullable(),
154
+ defaultWorkspaceId: z.string().uuid().nullable(),
155
+ });
156
+ export type AccessContext = z.infer<typeof AccessContext>;
157
+
158
+ export const DelegatedAccessTokenPayload = z.object({
159
+ accountId: z.string().uuid(),
160
+ workspaceId: z.string().uuid(),
161
+ subjectId: z.string().min(1),
162
+ subjectLabel: z.string().optional(),
163
+ permissions: z.array(Permission).min(1),
164
+ // Worker-asserted session scope for first-party MCP calls (HMAC-signed, not
165
+ // agent-controlled); enables session-scoped tools such as goal management.
166
+ sessionId: z.string().uuid().optional(),
167
+ exp: z.number().int().positive(),
168
+ });
169
+ export type DelegatedAccessTokenPayload = z.infer<typeof DelegatedAccessTokenPayload>;
170
+
171
+ export async function signDelegatedAccessToken(secret: string, payload: DelegatedAccessTokenPayload): Promise<string> {
172
+ const encodedPayload = base64UrlEncode(JSON.stringify(DelegatedAccessTokenPayload.parse(payload)));
173
+ const signature = await hmacSha256Base64Url(secret, encodedPayload);
174
+ return `ogd_${encodedPayload}.${signature}`;
175
+ }
176
+
177
+ export async function verifyDelegatedAccessToken(secret: string, token: string, nowSeconds = Math.floor(Date.now() / 1000)): Promise<DelegatedAccessTokenPayload | null> {
178
+ if (!token.startsWith("ogd_")) {
179
+ return null;
180
+ }
181
+ const withoutPrefix = token.slice("ogd_".length);
182
+ const dot = withoutPrefix.lastIndexOf(".");
183
+ if (dot <= 0) {
184
+ return null;
185
+ }
186
+ const encodedPayload = withoutPrefix.slice(0, dot);
187
+ const signature = withoutPrefix.slice(dot + 1);
188
+ const expected = await hmacSha256Base64Url(secret, encodedPayload);
189
+ if (!constantTimeEqual(signature, expected)) {
190
+ return null;
191
+ }
192
+ const payload = DelegatedAccessTokenPayload.safeParse(JSON.parse(base64UrlDecode(encodedPayload)));
193
+ if (!payload.success || payload.data.exp < nowSeconds) {
194
+ return null;
195
+ }
196
+ return payload.data;
197
+ }
198
+
199
+ export const CreateWorkspaceRequest = z.object({
200
+ accountId: z.string().uuid().optional(),
201
+ name: z.string().min(1),
202
+ slug: z.string().min(1).optional(),
203
+ externalSource: z.string().min(1).optional(),
204
+ externalId: z.string().min(1).optional(),
205
+ // White-label persona override for this workspace's agent. null/omitted uses
206
+ // the deployment default template.
207
+ agentInstructions: z.string().min(1).nullable().optional(),
208
+ });
209
+ export type CreateWorkspaceRequest = z.infer<typeof CreateWorkspaceRequest>;
210
+
211
+ export const UpdateWorkspaceRequest = z.object({
212
+ name: z.string().min(1).optional(),
213
+ slug: z.string().min(1).nullable().optional(),
214
+ // White-label persona override. Pass null to clear it back to the deployment
215
+ // default; omit to leave it unchanged.
216
+ agentInstructions: z.string().min(1).nullable().optional(),
217
+ });
218
+ export type UpdateWorkspaceRequest = z.infer<typeof UpdateWorkspaceRequest>;
219
+
220
+ export const ApiKey = z.object({
221
+ id: z.string().uuid(),
222
+ accountId: z.string().uuid(),
223
+ workspaceId: z.string().uuid().nullable(),
224
+ name: z.string(),
225
+ prefix: z.string(),
226
+ permissions: z.array(Permission),
227
+ expiresAt: z.string().nullable(),
228
+ revokedAt: z.string().nullable(),
229
+ lastUsedAt: z.string().nullable(),
230
+ createdAt: z.string(),
231
+ updatedAt: z.string(),
232
+ });
233
+ export type ApiKey = z.infer<typeof ApiKey>;
234
+
235
+ export const CreateApiKeyRequest = z.object({
236
+ name: z.string().min(1),
237
+ workspaceId: z.string().uuid().optional(),
238
+ permissions: z.array(Permission).min(1),
239
+ expiresAt: z.string().datetime({ offset: true }).optional(),
240
+ });
241
+ export type CreateApiKeyRequest = z.infer<typeof CreateApiKeyRequest>;
242
+
243
+ export const CreateApiKeyResponse = z.object({
244
+ apiKey: ApiKey,
245
+ token: z.string().min(1),
246
+ });
247
+ export type CreateApiKeyResponse = z.infer<typeof CreateApiKeyResponse>;
248
+
249
+ export const UsageEventType = z.enum([
250
+ "agent_run.created",
251
+ "agent_run.completed",
252
+ "model.tokens",
253
+ "model.cost",
254
+ "file.uploaded",
255
+ "file.deleted",
256
+ "document.indexed",
257
+ "scheduled_task.fired",
258
+ "api_key.request",
259
+ ]);
260
+ export type UsageEventType = z.infer<typeof UsageEventType>;
261
+
262
+ export const UsageEvent = z.object({
263
+ id: z.string().uuid(),
264
+ workspaceId: z.string().uuid(),
265
+ accountId: z.string().uuid(),
266
+ subjectId: z.string().nullable(),
267
+ eventType: UsageEventType,
268
+ quantity: z.number(),
269
+ unit: z.string(),
270
+ sourceResourceType: z.string().nullable(),
271
+ sourceResourceId: z.string().nullable(),
272
+ idempotencyKey: z.string(),
273
+ occurredAt: z.string(),
274
+ recordedAt: z.string(),
275
+ exportedToBillingAt: z.string().nullable(),
276
+ billingProviderEventId: z.string().nullable(),
277
+ });
278
+ export type UsageEvent = z.infer<typeof UsageEvent>;
279
+
280
+ export const LimitAction = z.enum([
281
+ "agent_run:create",
282
+ "tokens:consume",
283
+ "file:upload",
284
+ "document:index",
285
+ "schedule:create",
286
+ "workspace:create",
287
+ "api_key:create",
288
+ ]);
289
+ export type LimitAction = z.infer<typeof LimitAction>;
290
+
291
+ export const StaticUsageLimits = z.object({
292
+ maxWorkspacesPerAccount: z.number().int().positive().optional(),
293
+ maxApiKeysPerWorkspace: z.number().int().positive().optional(),
294
+ maxSchedulesPerWorkspace: z.number().int().positive().optional(),
295
+ maxFileUploadBytes: z.number().int().positive().optional(),
296
+ maxMonthlyAgentRunsPerWorkspace: z.number().int().positive().optional(),
297
+ maxMonthlyTokensPerWorkspace: z.number().int().positive().optional(),
298
+ maxMonthlyCostMicrosPerAccount: z.number().int().positive().optional(),
299
+ maxDocumentIndexedChunksPerWorkspace: z.number().int().positive().optional(),
300
+ });
301
+ export type StaticUsageLimits = z.infer<typeof StaticUsageLimits>;
302
+
303
+ export const EntitlementValue = z.union([z.boolean(), z.string(), z.number(), z.array(z.string())]);
304
+ export type EntitlementValue = z.infer<typeof EntitlementValue>;
305
+
306
+ export const Entitlements = z.record(z.string().min(1), EntitlementValue);
307
+ export type Entitlements = z.infer<typeof Entitlements>;
308
+
309
+ export const LimitDecision = z.discriminatedUnion("allowed", [
310
+ z.object({ allowed: z.literal(true) }),
311
+ z.object({ allowed: z.literal(false), code: z.string(), message: z.string() }),
312
+ ]);
313
+ export type LimitDecision = z.infer<typeof LimitDecision>;
314
+
315
+ export const BillingBalance = z.object({
316
+ accountId: z.string().uuid(),
317
+ balanceMicros: z.number().int(),
318
+ currency: z.literal("usd"),
319
+ updatedAt: z.string(),
320
+ });
321
+ export type BillingBalance = z.infer<typeof BillingBalance>;
322
+
323
+ export const CreateCheckoutRequest = z.object({
324
+ accountId: z.string().uuid().optional(),
325
+ amountUsd: z.number().min(5).max(10_000).refine(
326
+ (value) => Number.isFinite(value) && Math.abs(value - Math.round(value * 100) / 100) < 1e-9,
327
+ { message: "amountUsd must use cent precision" },
328
+ ),
329
+ successUrl: z.string().url().optional(),
330
+ cancelUrl: z.string().url().optional(),
331
+ });
332
+ export type CreateCheckoutRequest = z.infer<typeof CreateCheckoutRequest>;
333
+
334
+ export const CreateCheckoutResponse = z.object({
335
+ checkoutSessionId: z.string(),
336
+ url: z.string().url(),
337
+ });
338
+ export type CreateCheckoutResponse = z.infer<typeof CreateCheckoutResponse>;
339
+
340
+ export const RepositoryResourceRef = z.object({
341
+ kind: z.literal("repository"),
342
+ uri: z.string().min(1),
343
+ ref: z.string().min(1),
344
+ mountPath: z.string().min(1).optional(),
345
+ subpath: z.string().min(1).optional(),
346
+ githubInstallationId: z.number().int().positive().optional(),
347
+ githubRepositoryId: z.number().int().positive().optional(),
348
+ });
349
+ export type RepositoryResourceRef = z.infer<typeof RepositoryResourceRef>;
350
+
351
+ export const FileResourceRef = z.object({
352
+ kind: z.literal("file"),
353
+ fileId: z.string().uuid(),
354
+ mountPath: z.string().min(1).optional(),
355
+ });
356
+ export type FileResourceRef = z.infer<typeof FileResourceRef>;
357
+
358
+ export const ResourceRef = z.discriminatedUnion("kind", [RepositoryResourceRef, FileResourceRef]);
359
+ export type ResourceRef = z.infer<typeof ResourceRef>;
360
+
361
+ export const FileStatus = z.enum(["pending_upload", "ready", "failed", "expired", "deleted"]);
362
+ export type FileStatus = z.infer<typeof FileStatus>;
363
+
364
+ export const FileUploadStatus = z.enum(["pending", "completed", "expired", "failed"]);
365
+ export type FileUploadStatus = z.infer<typeof FileUploadStatus>;
366
+
367
+ export const FileAsset = z.object({
368
+ id: z.string().uuid(),
369
+ workspaceId: z.string().uuid(),
370
+ status: FileStatus,
371
+ filename: z.string(),
372
+ safeFilename: z.string(),
373
+ contentType: z.string(),
374
+ sizeBytes: z.number().int().nonnegative(),
375
+ sha256: z.string().nullable(),
376
+ bucket: z.string(),
377
+ objectKey: z.string(),
378
+ createdAt: z.string(),
379
+ updatedAt: z.string(),
380
+ });
381
+ export type FileAsset = z.infer<typeof FileAsset>;
382
+
383
+ export const CreateFileUploadRequest = z.object({
384
+ filename: z.string().min(1),
385
+ contentType: z.string().min(1),
386
+ sizeBytes: z.number().int().positive(),
387
+ sha256: z.string().min(1).optional(),
388
+ });
389
+ export type CreateFileUploadRequest = z.infer<typeof CreateFileUploadRequest>;
390
+
391
+ export const CreateFileUploadResponse = z.object({
392
+ fileId: z.string().uuid(),
393
+ uploadId: z.string().uuid(),
394
+ putUrl: z.string().url(),
395
+ requiredHeaders: z.record(z.string(), z.string()),
396
+ expiresAt: z.string(),
397
+ maxSizeBytes: z.number().int().positive(),
398
+ });
399
+ export type CreateFileUploadResponse = z.infer<typeof CreateFileUploadResponse>;
400
+
401
+ export const CompleteFileUploadResponse = z.object({
402
+ file: FileAsset,
403
+ });
404
+ export type CompleteFileUploadResponse = z.infer<typeof CompleteFileUploadResponse>;
405
+
406
+ export const FileDownloadUrlResponse = z.object({
407
+ url: z.string().url(),
408
+ expiresAt: z.string(),
409
+ });
410
+ export type FileDownloadUrlResponse = z.infer<typeof FileDownloadUrlResponse>;
411
+
412
+ export const DocumentStatus = z.enum(["queued", "indexing", "ready", "failed"]);
413
+ export type DocumentStatus = z.infer<typeof DocumentStatus>;
414
+
415
+ export const DocumentBase = z.object({
416
+ id: z.string().uuid(),
417
+ workspaceId: z.string().uuid(),
418
+ name: z.string(),
419
+ description: z.string().nullable(),
420
+ createdAt: z.string(),
421
+ updatedAt: z.string(),
422
+ });
423
+ export type DocumentBase = z.infer<typeof DocumentBase>;
424
+
425
+ export const Document = z.object({
426
+ id: z.string().uuid(),
427
+ workspaceId: z.string().uuid(),
428
+ baseId: z.string().uuid(),
429
+ fileId: z.string().uuid(),
430
+ status: DocumentStatus,
431
+ title: z.string(),
432
+ parser: z.string(),
433
+ chunkCount: z.number().int().nonnegative(),
434
+ error: z.string().nullable(),
435
+ createdAt: z.string(),
436
+ updatedAt: z.string(),
437
+ });
438
+ export type Document = z.infer<typeof Document>;
439
+
440
+ export const DocumentSearchResult = z.object({
441
+ chunkId: z.string().uuid(),
442
+ workspaceId: z.string().uuid(),
443
+ documentId: z.string().uuid(),
444
+ baseId: z.string().uuid(),
445
+ fileId: z.string().uuid(),
446
+ title: z.string(),
447
+ text: z.string(),
448
+ score: z.number(),
449
+ chunkIndex: z.number().int().nonnegative(),
450
+ metadata: z.record(z.string(), z.unknown()),
451
+ });
452
+ export type DocumentSearchResult = z.infer<typeof DocumentSearchResult>;
453
+
454
+ export const CreateDocumentBaseRequest = z.object({
455
+ name: z.string().min(1),
456
+ description: z.string().optional(),
457
+ });
458
+ export type CreateDocumentBaseRequest = z.infer<typeof CreateDocumentBaseRequest>;
459
+
460
+ export const AddDocumentRequest = z.object({
461
+ fileId: z.string().uuid(),
462
+ });
463
+ export type AddDocumentRequest = z.infer<typeof AddDocumentRequest>;
464
+
465
+ export const DocumentSearchRequest = z.object({
466
+ query: z.string().min(1),
467
+ limit: z.number().int().positive().max(20).default(5),
468
+ });
469
+ export type DocumentSearchRequest = z.infer<typeof DocumentSearchRequest>;
470
+
471
+ export const ToolRef = z.object({
472
+ kind: z.literal("mcp"),
473
+ id: z.string().min(1),
474
+ });
475
+ export type ToolRef = z.infer<typeof ToolRef>;
476
+
477
+ export class ResourceRefConflictError extends Error {
478
+ constructor(message: string) {
479
+ super(message);
480
+ this.name = "ResourceRefConflictError";
481
+ }
482
+ }
483
+
484
+ export function mergeToolRefs(existing: ToolRef[], additions: ToolRef[]): ToolRef[] {
485
+ const seen = new Set<string>();
486
+ const out: ToolRef[] = [];
487
+ for (const tool of [...existing, ...additions]) {
488
+ const key = `${tool.kind}:${tool.id}`;
489
+ if (seen.has(key)) {
490
+ continue;
491
+ }
492
+ seen.add(key);
493
+ out.push(tool);
494
+ }
495
+ return out;
496
+ }
497
+
498
+ export function mergeResourceRefs(
499
+ existing: ResourceRef[],
500
+ additions: ResourceRef[],
501
+ options: { rejectConflicts?: boolean } = {},
502
+ ): ResourceRef[] {
503
+ const out = [...existing];
504
+ const mountPaths = new Map(existing.flatMap((resource) => resource.mountPath ? [[resource.mountPath, stableJson(resource)] as const] : []));
505
+ const identities = new Map(existing.map((resource) => [resourceIdentityKey(resource), stableJson(resource)] as const));
506
+ const exact = new Set(existing.map(stableJson));
507
+
508
+ for (const resource of additions) {
509
+ const serialized = stableJson(resource);
510
+ if (exact.has(serialized)) {
511
+ continue;
512
+ }
513
+ if (options.rejectConflicts) {
514
+ const existingAtMount = resource.mountPath ? mountPaths.get(resource.mountPath) : undefined;
515
+ if (existingAtMount && existingAtMount !== serialized) {
516
+ throw new ResourceRefConflictError(`resource mount path is already attached: ${resource.mountPath}`);
517
+ }
518
+ const identity = resourceIdentityKey(resource);
519
+ const existingIdentity = identities.get(identity);
520
+ if (existingIdentity && existingIdentity !== serialized) {
521
+ throw new ResourceRefConflictError(`resource is already attached with different settings: ${identity}`);
522
+ }
523
+ }
524
+ out.push(resource);
525
+ exact.add(serialized);
526
+ identities.set(resourceIdentityKey(resource), serialized);
527
+ if (resource.mountPath) {
528
+ mountPaths.set(resource.mountPath, serialized);
529
+ }
530
+ }
531
+ return out;
532
+ }
533
+
534
+ export function reasoningEffortForMetadata(metadata: Record<string, unknown>, fallback: ReasoningEffort): ReasoningEffort {
535
+ const value = metadata.reasoningEffort;
536
+ return value === "none" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh"
537
+ ? value
538
+ : fallback;
539
+ }
540
+
541
+ export function stableJson(value: unknown): string {
542
+ return JSON.stringify(sortJson(value));
543
+ }
544
+
545
+ export function resourceIdentityKey(resource: ResourceRef): string {
546
+ if (resource.kind === "file") {
547
+ return `file:${resource.fileId}`;
548
+ }
549
+ return `repository:${resource.uri}`;
550
+ }
551
+
552
+ function sortJson(value: unknown): unknown {
553
+ if (Array.isArray(value)) {
554
+ return value.map(sortJson);
555
+ }
556
+ if (value && typeof value === "object") {
557
+ return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, nested]) => [key, sortJson(nested)]));
558
+ }
559
+ return value;
560
+ }
561
+
562
+ export const SessionTurnStatus = z.enum(["queued", "running", "requires_action", "completed", "failed", "cancelled"]);
563
+ export type SessionTurnStatus = z.infer<typeof SessionTurnStatus>;
564
+
565
+ export const SessionTurnSource = z.enum(["user", "scheduled_task", "api", "goal"]);
566
+ export type SessionTurnSource = z.infer<typeof SessionTurnSource>;
567
+
568
+ export const SessionGoalStatus = z.enum(["active", "paused", "completed"]);
569
+ export type SessionGoalStatus = z.infer<typeof SessionGoalStatus>;
570
+
571
+ export const SessionGoalCreatedBy = z.enum(["api", "agent", "scheduled_task"]);
572
+ export type SessionGoalCreatedBy = z.infer<typeof SessionGoalCreatedBy>;
573
+
574
+ export const SessionGoalPausedReason = z.enum([
575
+ "agent",
576
+ "user_interrupt",
577
+ "api",
578
+ "no_progress",
579
+ "max_auto_continuations",
580
+ "limits",
581
+ ]);
582
+ export type SessionGoalPausedReason = z.infer<typeof SessionGoalPausedReason>;
583
+
584
+ export const SessionGoal = z.object({
585
+ id: z.string().uuid(),
586
+ accountId: z.string().uuid(),
587
+ workspaceId: z.string().uuid(),
588
+ sessionId: z.string().uuid(),
589
+ status: SessionGoalStatus,
590
+ text: z.string(),
591
+ successCriteria: z.string().nullable(),
592
+ evidence: z.string().nullable(),
593
+ rationale: z.string().nullable(),
594
+ pausedReason: z.string().nullable(),
595
+ createdBy: SessionGoalCreatedBy,
596
+ version: z.number().int().positive(),
597
+ autoContinuations: z.number().int().nonnegative(),
598
+ noProgressStreak: z.number().int().nonnegative(),
599
+ maxAutoContinuations: z.number().int().positive().nullable(),
600
+ metadata: z.record(z.string(), z.unknown()),
601
+ createdAt: z.string(),
602
+ updatedAt: z.string(),
603
+ });
604
+ export type SessionGoal = z.infer<typeof SessionGoal>;
605
+
606
+ export const GoalSpec = z.object({
607
+ text: z.string().min(1),
608
+ successCriteria: z.string().min(1).optional(),
609
+ maxAutoContinuations: z.number().int().positive().optional(),
610
+ });
611
+ export type GoalSpec = z.infer<typeof GoalSpec>;
612
+
613
+ export const UpdateSessionGoalRequest = z.object({
614
+ status: z.enum(["paused", "active"]),
615
+ rationale: z.string().min(1).optional(),
616
+ });
617
+ export type UpdateSessionGoalRequest = z.infer<typeof UpdateSessionGoalRequest>;
618
+
619
+ // Operator context controls (slash-command palette: /clear, /compact). These
620
+ // are session/operator actions, NOT a structured way to talk to the agent —
621
+ // the human↔agent channel stays plain chat. Both require `sessions:control`.
622
+
623
+ /**
624
+ * Clear a session's conversation context. `confirm` must be the literal `true`
625
+ * so an accidental/empty POST cannot wipe context — the destructive intent is
626
+ * explicit on the wire, mirroring the client-side confirm affordance.
627
+ */
628
+ export const ClearSessionContextRequest = z.object({
629
+ confirm: z.literal(true),
630
+ });
631
+ export type ClearSessionContextRequest = z.infer<typeof ClearSessionContextRequest>;
632
+
633
+ /**
634
+ * The marker key on the sentinel run-state blob written by a context clear. The
635
+ * blob ({@link CLEARED_RUN_STATE_BLOB}) is NOT a real Agents-SDK serialized run
636
+ * state — it carries no `$schemaVersion`/history, so `RunState.fromString` would
637
+ * throw on it. Every read path that deserializes a run-state blob MUST first
638
+ * check {@link isClearedRunStateBlob} and treat a match as "no prior state"
639
+ * (a fresh, empty start), which is exactly what a clear means. This is the
640
+ * shared contract that keeps the db (writer) and the runtime (reader) in sync.
641
+ */
642
+ export const CLEARED_RUN_STATE_MARKER = "$opengeniCleared" as const;
643
+
644
+ /** The canonical sentinel serializedRunState value a context clear stores. */
645
+ export const CLEARED_RUN_STATE_BLOB = JSON.stringify({ [CLEARED_RUN_STATE_MARKER]: true });
646
+
647
+ /**
648
+ * True when a serialized run-state blob is the cleared sentinel rather than a
649
+ * real Agents-SDK run state. Recognized leniently (any object carrying the
650
+ * marker key set truthy) so a future field addition to the sentinel does not
651
+ * resurrect the pre-clear context. Anything that is not the sentinel — including
652
+ * malformed JSON — returns false so genuine blobs/corruption are handled by the
653
+ * normal deserialize path.
654
+ */
655
+ export function isClearedRunStateBlob(serialized: string | null | undefined): boolean {
656
+ if (!serialized) {
657
+ return false;
658
+ }
659
+ try {
660
+ const parsed = JSON.parse(serialized) as unknown;
661
+ return typeof parsed === "object"
662
+ && parsed !== null
663
+ && (parsed as Record<string, unknown>)[CLEARED_RUN_STATE_MARKER] === true;
664
+ } catch {
665
+ return false;
666
+ }
667
+ }
668
+
669
+ /** Trigger conversation compaction now. No body fields today (forward-room). */
670
+ export const CompactSessionContextRequest = z.object({}).strict();
671
+ export type CompactSessionContextRequest = z.infer<typeof CompactSessionContextRequest>;
672
+
673
+ /** Outcome of a manual /compact trigger. */
674
+ export const CompactSessionContextResult = z.object({
675
+ // queued: a client-side (Azure) compaction will run before the next turn.
676
+ // noop: nothing to do (server-managed provider, mode off, or no history).
677
+ status: z.enum(["queued", "noop"]),
678
+ message: z.string(),
679
+ });
680
+ export type CompactSessionContextResult = z.infer<typeof CompactSessionContextResult>;
681
+
682
+ export const SessionTurn = z.object({
683
+ id: z.string().uuid(),
684
+ workspaceId: z.string().uuid(),
685
+ sessionId: z.string().uuid(),
686
+ triggerEventId: z.string().uuid(),
687
+ temporalWorkflowId: z.string(),
688
+ status: SessionTurnStatus,
689
+ source: SessionTurnSource,
690
+ position: z.number().int().positive(),
691
+ prompt: z.string().min(1),
692
+ resources: z.array(ResourceRef),
693
+ tools: z.array(ToolRef),
694
+ model: z.string().min(1),
695
+ reasoningEffort: ReasoningEffort,
696
+ sandboxBackend: SandboxBackend,
697
+ metadata: z.record(z.string(), z.unknown()),
698
+ startedAt: z.string().nullable(),
699
+ finishedAt: z.string().nullable(),
700
+ createdAt: z.string(),
701
+ updatedAt: z.string(),
702
+ });
703
+ export type SessionTurn = z.infer<typeof SessionTurn>;
704
+
705
+ export const UpdateSessionTurnRequest = z.object({
706
+ prompt: z.string().min(1).optional(),
707
+ resources: z.array(ResourceRef).optional(),
708
+ tools: z.array(ToolRef).optional(),
709
+ model: z.string().min(1).optional(),
710
+ reasoningEffort: ReasoningEffort.optional(),
711
+ sandboxBackend: SandboxBackend.optional(),
712
+ metadata: z.record(z.string(), z.unknown()).optional(),
713
+ });
714
+ export type UpdateSessionTurnRequest = z.infer<typeof UpdateSessionTurnRequest>;
715
+
716
+ export const ReorderSessionTurnsRequest = z.object({
717
+ turnIds: z.array(z.string().uuid()).min(1),
718
+ });
719
+ export type ReorderSessionTurnsRequest = z.infer<typeof ReorderSessionTurnsRequest>;
720
+
721
+ export const WorkspaceEnvironmentVariableName = z.string().regex(/^[A-Z][A-Z0-9_]*$/).max(128);
722
+ export type WorkspaceEnvironmentVariableName = z.infer<typeof WorkspaceEnvironmentVariableName>;
723
+
724
+ // Metadata only by design: no schema in this file ever carries a variable value
725
+ // back to a client. Values are write-only and decrypted exclusively inside the
726
+ // worker at sandbox materialization time.
727
+ export const WorkspaceEnvironmentVariableMetadata = z.object({
728
+ name: WorkspaceEnvironmentVariableName,
729
+ version: z.number().int().positive(),
730
+ createdAt: z.string(),
731
+ updatedAt: z.string(),
732
+ });
733
+ export type WorkspaceEnvironmentVariableMetadata = z.infer<typeof WorkspaceEnvironmentVariableMetadata>;
734
+
735
+ export const WorkspaceEnvironment = z.object({
736
+ id: z.string().uuid(),
737
+ accountId: z.string().uuid(),
738
+ workspaceId: z.string().uuid(),
739
+ name: z.string(),
740
+ description: z.string().nullable(),
741
+ variables: z.array(WorkspaceEnvironmentVariableMetadata),
742
+ createdAt: z.string(),
743
+ updatedAt: z.string(),
744
+ });
745
+ export type WorkspaceEnvironment = z.infer<typeof WorkspaceEnvironment>;
746
+
747
+ export const CreateWorkspaceEnvironmentRequest = z.object({
748
+ name: z.string().min(1).max(120),
749
+ description: z.string().max(2000).optional(),
750
+ variables: z.array(z.object({
751
+ name: WorkspaceEnvironmentVariableName,
752
+ value: z.string().min(1).max(32768),
753
+ })).default([]),
754
+ });
755
+ export type CreateWorkspaceEnvironmentRequest = z.infer<typeof CreateWorkspaceEnvironmentRequest>;
756
+
757
+ export const UpdateWorkspaceEnvironmentRequest = z.object({
758
+ name: z.string().min(1).max(120).optional(),
759
+ description: z.string().max(2000).nullable().optional(),
760
+ });
761
+ export type UpdateWorkspaceEnvironmentRequest = z.infer<typeof UpdateWorkspaceEnvironmentRequest>;
762
+
763
+ export const SetWorkspaceEnvironmentVariableRequest = z.object({
764
+ value: z.string().min(1).max(32768),
765
+ });
766
+ export type SetWorkspaceEnvironmentVariableRequest = z.infer<typeof SetWorkspaceEnvironmentVariableRequest>;
767
+
768
+ export const ScheduledTaskStatus = z.enum(["active", "paused"]);
769
+ export type ScheduledTaskStatus = z.infer<typeof ScheduledTaskStatus>;
770
+
771
+ export const ScheduledTaskRunStatus = z.enum(["queued", "dispatched", "failed"]);
772
+ export type ScheduledTaskRunStatus = z.infer<typeof ScheduledTaskRunStatus>;
773
+
774
+ export const ScheduledTaskRunMode = z.enum(["new_session_per_run", "reusable_session"]);
775
+ export type ScheduledTaskRunMode = z.infer<typeof ScheduledTaskRunMode>;
776
+
777
+ export const ScheduledTaskOverlapPolicy = z.enum(["allow_concurrent", "skip", "buffer_one"]);
778
+ export type ScheduledTaskOverlapPolicy = z.infer<typeof ScheduledTaskOverlapPolicy>;
779
+
780
+ export const ScheduledTaskTriggerType = z.enum(["scheduled", "manual"]);
781
+ export type ScheduledTaskTriggerType = z.infer<typeof ScheduledTaskTriggerType>;
782
+
783
+ export const ScheduledTaskScheduleSpec = z.discriminatedUnion("type", [
784
+ z.object({
785
+ type: z.literal("once"),
786
+ runAt: z.string().datetime({ offset: true }),
787
+ timeZone: z.string().min(1).default("UTC"),
788
+ }),
789
+ z.object({
790
+ type: z.literal("interval"),
791
+ everySeconds: z.number().int().positive(),
792
+ startAt: z.string().datetime({ offset: true }).optional(),
793
+ endAt: z.string().datetime({ offset: true }).optional(),
794
+ }),
795
+ z.object({
796
+ type: z.literal("calendar"),
797
+ timeZone: z.string().min(1).default("UTC"),
798
+ hour: z.number().int().min(0).max(23),
799
+ minute: z.number().int().min(0).max(59),
800
+ daysOfWeek: z.array(z.enum(["SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"])).min(1).optional(),
801
+ }),
802
+ ]);
803
+ export type ScheduledTaskScheduleSpec = z.infer<typeof ScheduledTaskScheduleSpec>;
804
+
805
+ export const ScheduledTaskAgentConfig = z.object({
806
+ prompt: z.string().min(1),
807
+ resources: z.array(ResourceRef).default([]),
808
+ tools: z.array(ToolRef).default([]),
809
+ metadata: z.record(z.string(), z.unknown()).default({}),
810
+ model: z.string().min(1).optional(),
811
+ reasoningEffort: ReasoningEffort.optional(),
812
+ sandboxBackend: SandboxBackend.optional(),
813
+ goal: GoalSpec.optional(),
814
+ });
815
+ export type ScheduledTaskAgentConfig = z.infer<typeof ScheduledTaskAgentConfig>;
816
+
817
+ export const ScheduledTask = z.object({
818
+ id: z.string().uuid(),
819
+ accountId: z.string().uuid(),
820
+ workspaceId: z.string().uuid(),
821
+ name: z.string(),
822
+ status: ScheduledTaskStatus,
823
+ schedule: ScheduledTaskScheduleSpec,
824
+ temporalScheduleId: z.string(),
825
+ runMode: ScheduledTaskRunMode,
826
+ overlapPolicy: ScheduledTaskOverlapPolicy,
827
+ agentConfig: ScheduledTaskAgentConfig,
828
+ reusableSessionId: z.string().uuid().nullable(),
829
+ environmentId: z.string().uuid().nullable(),
830
+ metadata: z.record(z.string(), z.unknown()),
831
+ createdAt: z.string(),
832
+ updatedAt: z.string(),
833
+ });
834
+ export type ScheduledTask = z.infer<typeof ScheduledTask>;
835
+
836
+ export const ScheduledTaskRun = z.object({
837
+ id: z.string().uuid(),
838
+ accountId: z.string().uuid(),
839
+ workspaceId: z.string().uuid(),
840
+ taskId: z.string().uuid(),
841
+ status: ScheduledTaskRunStatus,
842
+ triggerType: ScheduledTaskTriggerType,
843
+ scheduledAt: z.string().nullable(),
844
+ firedAt: z.string(),
845
+ sessionId: z.string().uuid().nullable(),
846
+ triggerEventId: z.string().uuid().nullable(),
847
+ error: z.string().nullable(),
848
+ createdAt: z.string(),
849
+ updatedAt: z.string(),
850
+ });
851
+ export type ScheduledTaskRun = z.infer<typeof ScheduledTaskRun>;
852
+
853
+ export const CreateScheduledTaskRequest = z.object({
854
+ name: z.string().min(1),
855
+ schedule: ScheduledTaskScheduleSpec,
856
+ runMode: ScheduledTaskRunMode.default("new_session_per_run"),
857
+ overlapPolicy: ScheduledTaskOverlapPolicy.default("allow_concurrent"),
858
+ agentConfig: ScheduledTaskAgentConfig,
859
+ status: ScheduledTaskStatus.default("active"),
860
+ environmentId: z.string().uuid().nullable().optional(),
861
+ metadata: z.record(z.string(), z.unknown()).default({}),
862
+ });
863
+ export type CreateScheduledTaskRequest = z.infer<typeof CreateScheduledTaskRequest>;
864
+
865
+ export const UpdateScheduledTaskRequest = z.object({
866
+ name: z.string().min(1).optional(),
867
+ schedule: ScheduledTaskScheduleSpec.optional(),
868
+ runMode: ScheduledTaskRunMode.optional(),
869
+ overlapPolicy: ScheduledTaskOverlapPolicy.optional(),
870
+ agentConfig: ScheduledTaskAgentConfig.optional(),
871
+ status: ScheduledTaskStatus.optional(),
872
+ environmentId: z.string().uuid().nullable().optional(),
873
+ metadata: z.record(z.string(), z.unknown()).optional(),
874
+ });
875
+ export type UpdateScheduledTaskRequest = z.infer<typeof UpdateScheduledTaskRequest>;
876
+
877
+ /**
878
+ * Manual-trigger body. `triggerId` is a client-supplied idempotency token: a
879
+ * retried trigger that reuses the same token charges once and starts one run.
880
+ * Omitting it makes each call a distinct trigger (the server mints a token).
881
+ */
882
+ export const TriggerScheduledTaskRequest = z.object({
883
+ triggerId: z.string().min(1).max(128).optional(),
884
+ });
885
+ export type TriggerScheduledTaskRequest = z.infer<typeof TriggerScheduledTaskRequest>;
886
+
887
+ export const CapabilityPackConnectorAuthModel = z.enum([
888
+ "oauth2_authorization_code_pkce",
889
+ "oauth2_authorization_code",
890
+ "api_key",
891
+ "credential_ref",
892
+ ]);
893
+ export type CapabilityPackConnectorAuthModel = z.infer<typeof CapabilityPackConnectorAuthModel>;
894
+
895
+ export const CapabilityPackConnector = z.object({
896
+ id: z.string().min(1),
897
+ name: z.string().min(1),
898
+ category: z.string().min(1),
899
+ authModel: CapabilityPackConnectorAuthModel,
900
+ providers: z.array(z.string().min(1)).default([]),
901
+ scopes: z.array(z.string().min(1)).default([]),
902
+ required: z.boolean().default(false),
903
+ metadata: z.record(z.string(), z.unknown()).default({}),
904
+ });
905
+ export type CapabilityPackConnector = z.infer<typeof CapabilityPackConnector>;
906
+
907
+ export const CapabilityPackKnowledge = z.object({
908
+ type: z.literal("document_base"),
909
+ id: z.string().min(1),
910
+ name: z.string().min(1),
911
+ description: z.string().nullable().default(null),
912
+ required: z.boolean().default(false),
913
+ });
914
+ export type CapabilityPackKnowledge = z.infer<typeof CapabilityPackKnowledge>;
915
+
916
+ export const CapabilityPackScheduledTaskTemplate = z.object({
917
+ id: z.string().min(1),
918
+ name: z.string().min(1),
919
+ description: z.string().min(1),
920
+ defaultSchedule: ScheduledTaskScheduleSpec,
921
+ defaultRunMode: ScheduledTaskRunMode.default("new_session_per_run"),
922
+ defaultOverlapPolicy: ScheduledTaskOverlapPolicy.default("skip"),
923
+ // Optional default agent prompt so registered pack manifests can ship fully
924
+ // instantiable templates; built-in packs may instead build prompts in code.
925
+ prompt: z.string().min(1).optional(),
926
+ });
927
+ export type CapabilityPackScheduledTaskTemplate = z.infer<typeof CapabilityPackScheduledTaskTemplate>;
928
+
929
+ // One file inside a pack skill directory. Paths are workspace-relative POSIX
930
+ // paths inside the skill directory (for example "SKILL.md" or
931
+ // "references/runbook.md"); content is UTF-8 text carried inline in the pack
932
+ // manifest, which is also how registered packs persist it (the manifest JSONB
933
+ // row in workspace_packs is the storage of record for pack skills).
934
+ export const CapabilityPackSkillFile = z.object({
935
+ path: z.string().min(1).max(512).refine(isSafePackSkillRelativePath, {
936
+ message: "skill file path must be a safe relative POSIX path without '..' segments",
937
+ }),
938
+ content: z.string().max(256 * 1024),
939
+ });
940
+ export type CapabilityPackSkillFile = z.infer<typeof CapabilityPackSkillFile>;
941
+
942
+ // A skill delivered by a capability pack. The name doubles as the skill
943
+ // directory under the sandbox skill index (skills/<name>), so it must be a
944
+ // single safe path segment. Every skill must ship a top-level SKILL.md.
945
+ export const CapabilityPackSkill = z.object({
946
+ name: z.string().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, {
947
+ message: "skill name must be a single path segment of letters, digits, '.', '_' or '-'",
948
+ }),
949
+ description: z.string().min(1).max(2048).optional(),
950
+ files: z.array(CapabilityPackSkillFile).min(1).max(64),
951
+ }).superRefine((skill, ctx) => {
952
+ const seen = new Set<string>();
953
+ skill.files.forEach((file, index) => {
954
+ if (seen.has(file.path)) {
955
+ ctx.addIssue({ code: "custom", message: `duplicate skill file path: ${file.path}`, path: ["files", index, "path"] });
956
+ }
957
+ seen.add(file.path);
958
+ });
959
+ if (!skill.files.some((file) => file.path === "SKILL.md")) {
960
+ ctx.addIssue({ code: "custom", message: "skill must include a top-level SKILL.md file", path: ["files"] });
961
+ }
962
+ });
963
+ export type CapabilityPackSkill = z.infer<typeof CapabilityPackSkill>;
964
+
965
+ function isSafePackSkillRelativePath(path: string): boolean {
966
+ if (path.startsWith("/") || path.includes("\\")) {
967
+ return false;
968
+ }
969
+ return path.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
970
+ }
971
+
972
+ export const CapabilityPack = z.object({
973
+ id: z.string().min(1),
974
+ name: z.string().min(1),
975
+ description: z.string().min(1),
976
+ role: z.string().min(1),
977
+ category: z.string().min(1),
978
+ version: z.string().min(1),
979
+ // Container image ref (digest-pinned recommended) the pack's sessions run
980
+ // in. At most one enabled pack per workspace may declare one; with none,
981
+ // sessions use the deployment-wide image settings.
982
+ sandboxImage: z.string().trim().min(1).max(512).optional(),
983
+ // Skills delivered into the sandbox skill index when the pack is enabled.
984
+ skills: z.array(CapabilityPackSkill).max(32).superRefine((skills, ctx) => {
985
+ const seen = new Set<string>();
986
+ skills.forEach((skill, index) => {
987
+ const key = skill.name.toLowerCase();
988
+ if (seen.has(key)) {
989
+ ctx.addIssue({ code: "custom", message: `duplicate pack skill name: ${skill.name}`, path: [index, "name"] });
990
+ }
991
+ seen.add(key);
992
+ });
993
+ }).default([]),
994
+ tools: z.array(ToolRef).default([]),
995
+ connectors: z.array(CapabilityPackConnector).default([]),
996
+ knowledge: z.array(CapabilityPackKnowledge).default([]),
997
+ scheduledTaskTemplates: z.array(CapabilityPackScheduledTaskTemplate).default([]),
998
+ environment: z.object({
999
+ description: z.string().min(1),
1000
+ requiredVariables: z.array(WorkspaceEnvironmentVariableName).default([]),
1001
+ required: z.boolean().default(false),
1002
+ }).optional(),
1003
+ metadata: z.record(z.string(), z.unknown()).default({}),
1004
+ });
1005
+ export type CapabilityPack = z.infer<typeof CapabilityPack>;
1006
+
1007
+ // Registering a pack stores the manifest itself; the request body is a full
1008
+ // CapabilityPack manifest.
1009
+ export const RegisterCapabilityPackRequest = CapabilityPack;
1010
+ export type RegisterCapabilityPackRequest = z.infer<typeof RegisterCapabilityPackRequest>;
1011
+
1012
+ export const WorkspaceRegisteredPack = z.object({
1013
+ accountId: z.string().uuid(),
1014
+ workspaceId: z.string().uuid(),
1015
+ pack: CapabilityPack,
1016
+ createdAt: z.string(),
1017
+ updatedAt: z.string(),
1018
+ });
1019
+ export type WorkspaceRegisteredPack = z.infer<typeof WorkspaceRegisteredPack>;
1020
+
1021
+ export const PackInstallationStatus = z.enum(["active", "disabled"]);
1022
+ export type PackInstallationStatus = z.infer<typeof PackInstallationStatus>;
1023
+
1024
+ export const PackInstallation = z.object({
1025
+ id: z.string().uuid(),
1026
+ accountId: z.string().uuid(),
1027
+ workspaceId: z.string().uuid(),
1028
+ packId: z.string().min(1),
1029
+ status: PackInstallationStatus,
1030
+ metadata: z.record(z.string(), z.unknown()),
1031
+ enabledAt: z.string(),
1032
+ updatedAt: z.string(),
1033
+ });
1034
+ export type PackInstallation = z.infer<typeof PackInstallation>;
1035
+
1036
+ export const EnablePackRequest = z.object({
1037
+ environmentId: z.string().uuid().optional(),
1038
+ metadata: z.record(z.string(), z.unknown()).default({}),
1039
+ });
1040
+ export type EnablePackRequest = z.infer<typeof EnablePackRequest>;
1041
+
1042
+ export const SocialProvider = z.enum([
1043
+ "x",
1044
+ "linkedin",
1045
+ "instagram",
1046
+ "facebook",
1047
+ "tiktok",
1048
+ "youtube",
1049
+ "custom",
1050
+ ]);
1051
+ export type SocialProvider = z.infer<typeof SocialProvider>;
1052
+
1053
+ export const SocialConnectionStatus = z.enum(["connected", "needs_reauth", "disabled"]);
1054
+ export type SocialConnectionStatus = z.infer<typeof SocialConnectionStatus>;
1055
+
1056
+ export const SocialConnection = z.object({
1057
+ id: z.string().uuid(),
1058
+ accountId: z.string().uuid(),
1059
+ workspaceId: z.string().uuid(),
1060
+ provider: SocialProvider,
1061
+ accountHandle: z.string().min(1),
1062
+ accountName: z.string().nullable(),
1063
+ externalAccountId: z.string().nullable(),
1064
+ status: SocialConnectionStatus,
1065
+ scopes: z.array(z.string()),
1066
+ credentialRef: z.string().nullable(),
1067
+ tokenMetadata: z.record(z.string(), z.unknown()),
1068
+ metadata: z.record(z.string(), z.unknown()),
1069
+ createdAt: z.string(),
1070
+ updatedAt: z.string(),
1071
+ });
1072
+ export type SocialConnection = z.infer<typeof SocialConnection>;
1073
+
1074
+ export const CreateSocialConnectionRequest = z.object({
1075
+ provider: SocialProvider,
1076
+ accountHandle: z.string().min(1),
1077
+ accountName: z.string().min(1).optional(),
1078
+ externalAccountId: z.string().min(1).optional(),
1079
+ status: SocialConnectionStatus.default("connected"),
1080
+ scopes: z.array(z.string().min(1)).default([]),
1081
+ credentialRef: z.string().min(1).optional(),
1082
+ tokenMetadata: z.record(z.string(), z.unknown()).default({}),
1083
+ metadata: z.record(z.string(), z.unknown()).default({}),
1084
+ });
1085
+ export type CreateSocialConnectionRequest = z.infer<typeof CreateSocialConnectionRequest>;
1086
+
1087
+ export const SocialPost = z.object({
1088
+ id: z.string().uuid(),
1089
+ accountId: z.string().uuid(),
1090
+ workspaceId: z.string().uuid(),
1091
+ connectionId: z.string().uuid(),
1092
+ provider: SocialProvider,
1093
+ externalPostId: z.string().nullable(),
1094
+ url: z.string().url().nullable(),
1095
+ authorHandle: z.string().nullable(),
1096
+ text: z.string(),
1097
+ publishedAt: z.string(),
1098
+ metrics: z.record(z.string(), z.number()),
1099
+ raw: z.record(z.string(), z.unknown()),
1100
+ createdAt: z.string(),
1101
+ });
1102
+ export type SocialPost = z.infer<typeof SocialPost>;
1103
+
1104
+ export const CreateSocialPostRequest = z.object({
1105
+ connectionId: z.string().uuid(),
1106
+ externalPostId: z.string().min(1).optional(),
1107
+ url: z.string().url().optional(),
1108
+ authorHandle: z.string().min(1).optional(),
1109
+ text: z.string().min(1),
1110
+ publishedAt: z.string().datetime({ offset: true }),
1111
+ metrics: z.record(z.string(), z.number()).default({}),
1112
+ raw: z.record(z.string(), z.unknown()).default({}),
1113
+ });
1114
+ export type CreateSocialPostRequest = z.infer<typeof CreateSocialPostRequest>;
1115
+
1116
+ export const MarketingDailyAnalysisTaskRequest = z.object({
1117
+ name: z.string().min(1).optional(),
1118
+ connectionIds: z.array(z.string().uuid()).default([]),
1119
+ documentBaseIds: z.array(z.string().uuid()).default([]),
1120
+ timeZone: z.string().min(1).default("UTC"),
1121
+ hour: z.number().int().min(0).max(23).default(9),
1122
+ minute: z.number().int().min(0).max(59).default(0),
1123
+ promptInstructions: z.string().min(1).optional(),
1124
+ status: ScheduledTaskStatus.default("active"),
1125
+ runMode: ScheduledTaskRunMode.default("new_session_per_run"),
1126
+ overlapPolicy: ScheduledTaskOverlapPolicy.default("skip"),
1127
+ });
1128
+ export type MarketingDailyAnalysisTaskRequest = z.infer<typeof MarketingDailyAnalysisTaskRequest>;
1129
+
1130
+ export const CapabilityKind = z.enum(["pack", "mcp", "api", "skill", "plugin"]);
1131
+ export type CapabilityKind = z.infer<typeof CapabilityKind>;
1132
+
1133
+ export const CapabilitySource = z.enum(["built_in", "configured", "public_registry", "manual"]);
1134
+ export type CapabilitySource = z.infer<typeof CapabilitySource>;
1135
+
1136
+ export const CapabilityInstallationStatus = z.enum(["active", "disabled"]);
1137
+ export type CapabilityInstallationStatus = z.infer<typeof CapabilityInstallationStatus>;
1138
+
1139
+ export const CapabilityRuntime = z.object({
1140
+ available: z.boolean().default(false),
1141
+ mcpServerId: z.string().min(1).optional(),
1142
+ transport: z.string().min(1).optional(),
1143
+ notes: z.string().nullable().default(null),
1144
+ });
1145
+ export type CapabilityRuntime = z.infer<typeof CapabilityRuntime>;
1146
+
1147
+ export const CapabilityCatalogItem = z.object({
1148
+ id: z.string().min(1),
1149
+ accountId: z.string().uuid().optional(),
1150
+ workspaceId: z.string().uuid().optional(),
1151
+ kind: CapabilityKind,
1152
+ source: CapabilitySource,
1153
+ name: z.string().min(1),
1154
+ description: z.string().nullable().default(null),
1155
+ category: z.string().min(1).default("custom"),
1156
+ tags: z.array(z.string().min(1)).default([]),
1157
+ homepageUrl: z.string().url().nullable().default(null),
1158
+ endpointUrl: z.string().url().nullable().default(null),
1159
+ installUrl: z.string().url().nullable().default(null),
1160
+ authModel: z.string().min(1).nullable().default(null),
1161
+ tools: z.array(ToolRef).default([]),
1162
+ runtime: CapabilityRuntime.default({ available: false, notes: null }),
1163
+ enabled: z.boolean().default(false),
1164
+ enabledReason: z.string().nullable().default(null),
1165
+ metadata: z.record(z.string(), z.unknown()).default({}),
1166
+ createdAt: z.string().optional(),
1167
+ updatedAt: z.string().optional(),
1168
+ });
1169
+ export type CapabilityCatalogItem = z.infer<typeof CapabilityCatalogItem>;
1170
+
1171
+ export const CapabilityInstallation = z.object({
1172
+ id: z.string().uuid(),
1173
+ accountId: z.string().uuid(),
1174
+ workspaceId: z.string().uuid(),
1175
+ capabilityId: z.string().min(1),
1176
+ kind: CapabilityKind,
1177
+ status: CapabilityInstallationStatus,
1178
+ config: z.record(z.string(), z.unknown()),
1179
+ metadata: z.record(z.string(), z.unknown()),
1180
+ enabledAt: z.string(),
1181
+ updatedAt: z.string(),
1182
+ });
1183
+ export type CapabilityInstallation = z.infer<typeof CapabilityInstallation>;
1184
+
1185
+ export const CreateCapabilityCatalogItemRequest = z.object({
1186
+ id: z.string().min(1).optional(),
1187
+ kind: CapabilityKind.exclude(["pack"]),
1188
+ source: CapabilitySource.default("manual"),
1189
+ name: z.string().min(1),
1190
+ description: z.string().min(1).optional(),
1191
+ category: z.string().min(1).default("custom"),
1192
+ tags: z.array(z.string().min(1)).default([]),
1193
+ homepageUrl: z.string().url().optional(),
1194
+ endpointUrl: z.string().url().optional(),
1195
+ installUrl: z.string().url().optional(),
1196
+ authModel: z.string().min(1).optional(),
1197
+ metadata: z.record(z.string(), z.unknown()).default({}),
1198
+ });
1199
+ export type CreateCapabilityCatalogItemRequest = z.infer<typeof CreateCapabilityCatalogItemRequest>;
1200
+
1201
+ export const EnableCapabilityRequest = z.object({
1202
+ config: z.record(z.string(), z.unknown()).default({}),
1203
+ metadata: z.record(z.string(), z.unknown()).default({}),
1204
+ /**
1205
+ * Credential headers for remote MCP capabilities (for example an
1206
+ * Authorization bearer token). Values are encrypted at rest with the
1207
+ * workspace-environments key, injected only into the runtime MCP client,
1208
+ * and never returned by the API — responses expose header names only.
1209
+ */
1210
+ headers: z.record(z.string(), z.string()).default({}),
1211
+ /**
1212
+ * Initial environment attachment for kind=pack capabilities. Mirrors the
1213
+ * dedicated POST /packs/:id/enable body: required to enable an
1214
+ * environment.required pack through the unified capability-enable path,
1215
+ * optional otherwise. Ignored by non-pack capabilities.
1216
+ */
1217
+ environmentId: z.string().uuid().optional(),
1218
+ });
1219
+ export type EnableCapabilityRequest = z.infer<typeof EnableCapabilityRequest>;
1220
+
1221
+ export const CapabilityCatalogResponse = z.object({
1222
+ items: z.array(CapabilityCatalogItem),
1223
+ installations: z.array(CapabilityInstallation),
1224
+ });
1225
+ export type CapabilityCatalogResponse = z.infer<typeof CapabilityCatalogResponse>;
1226
+
1227
+ export const DiscoverMcpCapabilitiesResponse = z.object({
1228
+ items: z.array(CapabilityCatalogItem),
1229
+ source: z.literal("official_mcp_registry"),
1230
+ sourceUrl: z.string().url(),
1231
+ });
1232
+ export type DiscoverMcpCapabilitiesResponse = z.infer<typeof DiscoverMcpCapabilitiesResponse>;
1233
+
1234
+ export const Session = z.object({
1235
+ id: z.string().uuid(),
1236
+ workspaceId: z.string().uuid(),
1237
+ accountId: z.string().uuid(),
1238
+ status: SessionStatus,
1239
+ initialMessage: z.string(),
1240
+ resources: z.array(ResourceRef),
1241
+ tools: z.array(ToolRef),
1242
+ metadata: z.record(z.string(), z.unknown()),
1243
+ model: z.string(),
1244
+ sandboxBackend: SandboxBackend,
1245
+ environmentId: z.string().uuid().nullable(),
1246
+ // Non-default first-party MCP token permissions (manager-style sessions);
1247
+ // null means the fixed worker default set.
1248
+ firstPartyMcpPermissions: z.array(Permission).nullable(),
1249
+ // The manager session that spawned this one via session_create (set only
1250
+ // when the creating grant carried a worker-signed sessionId claim); null for
1251
+ // direct API creates and scheduled-task runs. When set, this session's
1252
+ // terminal-for-now transitions wake the parent.
1253
+ parentSessionId: z.string().uuid().nullable(),
1254
+ // Workspace-scoped CREATE idempotency key the session was created under (the
1255
+ // dedup target collapsing double-submit/retry races to one session); null
1256
+ // when the create carried no key.
1257
+ createIdempotencyKey: z.string().nullable(),
1258
+ temporalWorkflowId: z.string().nullable(),
1259
+ activeTurnId: z.string().uuid().nullable(),
1260
+ // Actual input tokens of the last model call of the most recent turn; the
1261
+ // pre-turn client-side context-compaction trigger reads it as its budget
1262
+ // signal. Null until a turn with usage has completed.
1263
+ lastInputTokens: z.number().int().nonnegative().nullable(),
1264
+ lastSequence: z.number().int().nonnegative(),
1265
+ createdAt: z.string(),
1266
+ updatedAt: z.string(),
1267
+ });
1268
+ export type Session = z.infer<typeof Session>;
1269
+
1270
+ export const SessionEventType = z.enum([
1271
+ "session.created",
1272
+ "session.status.changed",
1273
+ "session.requiresAction",
1274
+ "session.context.compacted",
1275
+ "session.context.cleared",
1276
+ "user.message",
1277
+ "user.interrupt",
1278
+ "user.approvalDecision",
1279
+ "turn.queued",
1280
+ "turn.updated",
1281
+ "turn.started",
1282
+ "turn.completed",
1283
+ "turn.failed",
1284
+ "turn.cancelled",
1285
+ "turn.preempted",
1286
+ "agent.message.delta",
1287
+ "agent.message.completed",
1288
+ "agent.reasoning.delta",
1289
+ "agent.toolCall.created",
1290
+ "agent.toolCall.output",
1291
+ "agent.updated",
1292
+ "sandbox.operation.started",
1293
+ "sandbox.operation.completed",
1294
+ "sandbox.operation.failed",
1295
+ "sandbox.command.output.delta",
1296
+ "artifact.created",
1297
+ "goal.set",
1298
+ "goal.updated",
1299
+ "goal.completed",
1300
+ "goal.paused",
1301
+ "goal.resumed",
1302
+ "goal.continuation",
1303
+ ]);
1304
+ export type SessionEventType = z.infer<typeof SessionEventType>;
1305
+
1306
+ export const SessionEvent = z.object({
1307
+ id: z.string().uuid(),
1308
+ workspaceId: z.string().uuid(),
1309
+ sessionId: z.string().uuid(),
1310
+ sequence: z.number().int().positive(),
1311
+ type: SessionEventType,
1312
+ payload: z.unknown().default({}),
1313
+ occurredAt: z.string(),
1314
+ clientEventId: z.string().min(1).nullable().optional(),
1315
+ turnId: z.string().uuid().nullable().optional(),
1316
+ });
1317
+ export type SessionEvent = z.infer<typeof SessionEvent>;
1318
+
1319
+ export const CreateSessionRequest = z.object({
1320
+ initialMessage: z.string().min(1),
1321
+ resources: z.array(ResourceRef).default([]),
1322
+ tools: z.array(ToolRef).default([]),
1323
+ metadata: z.record(z.string(), z.unknown()).default({}),
1324
+ model: z.string().min(1).optional(),
1325
+ reasoningEffort: ReasoningEffort.optional(),
1326
+ sandboxBackend: SandboxBackend.optional(),
1327
+ // Workspace environment attachment is fixed at session creation; follow-up
1328
+ // user.message events cannot switch or add one.
1329
+ environmentId: z.string().uuid().optional(),
1330
+ goal: GoalSpec.optional(),
1331
+ clientEventId: z.string().min(1).optional(),
1332
+ // Workspace-scoped CREATE idempotency key: collapses concurrent/retried
1333
+ // create calls carrying the same key to a single session (partial unique
1334
+ // index on (workspace_id, create_idempotency_key)). Distinct from
1335
+ // clientEventId, whose uniqueness is per-session and so cannot dedup the
1336
+ // creation of a brand-new session. Absent means no create-dedup (each call
1337
+ // is an independent create).
1338
+ idempotencyKey: z.string().min(1).max(200).optional(),
1339
+ // Permissions the session's first-party MCP token should carry instead of
1340
+ // the fixed worker default — how an operator hands a manager-style session
1341
+ // the orchestration/environment/github tools. Capped at creation: every
1342
+ // requested permission must be held by the creating grant (no escalation).
1343
+ firstPartyMcpPermissions: z.array(Permission).optional(),
1344
+ });
1345
+ export type CreateSessionRequest = z.infer<typeof CreateSessionRequest>;
1346
+
1347
+ export const ClientSessionEvent = z.discriminatedUnion("type", [
1348
+ z.object({
1349
+ type: z.literal("user.message"),
1350
+ clientEventId: z.string().min(1).optional(),
1351
+ payload: z.object({
1352
+ text: z.string().min(1),
1353
+ resources: z.array(ResourceRef).default([]),
1354
+ tools: z.array(ToolRef).default([]),
1355
+ model: z.string().min(1).optional(),
1356
+ reasoningEffort: ReasoningEffort.optional(),
1357
+ }),
1358
+ }),
1359
+ z.object({
1360
+ type: z.literal("user.interrupt"),
1361
+ clientEventId: z.string().min(1).optional(),
1362
+ payload: z.object({ reason: z.string().optional() }).default({}),
1363
+ }),
1364
+ z.object({
1365
+ type: z.literal("user.approvalDecision"),
1366
+ clientEventId: z.string().min(1).optional(),
1367
+ payload: z.object({
1368
+ approvalId: z.string().min(1),
1369
+ decision: z.enum(["approve", "reject"]),
1370
+ message: z.string().optional(),
1371
+ }),
1372
+ }),
1373
+ ]);
1374
+ export type ClientSessionEvent = z.infer<typeof ClientSessionEvent>;
1375
+
1376
+ export const SessionBusMessage = z.object({
1377
+ workspaceId: z.string().uuid(),
1378
+ sessionId: z.string().uuid(),
1379
+ events: z.array(SessionEvent).min(1),
1380
+ });
1381
+ export type SessionBusMessage = z.infer<typeof SessionBusMessage>;
1382
+
1383
+ export const GitHubAppManifestCreate = z.object({
1384
+ appName: z.string().optional(),
1385
+ organization: z.string().optional(),
1386
+ public: z.boolean().default(false),
1387
+ includeCiPermissions: z.boolean().default(true),
1388
+ });
1389
+ export type GitHubAppManifestCreate = z.infer<typeof GitHubAppManifestCreate>;
1390
+
1391
+ export const GitHubRepository = z.object({
1392
+ id: z.number().int(),
1393
+ installationId: z.number().int(),
1394
+ fullName: z.string(),
1395
+ name: z.string(),
1396
+ private: z.boolean(),
1397
+ htmlUrl: z.string(),
1398
+ cloneUrl: z.string(),
1399
+ defaultBranch: z.string(),
1400
+ accountLogin: z.string(),
1401
+ accountType: z.string().nullable(),
1402
+ });
1403
+ export type GitHubRepository = z.infer<typeof GitHubRepository>;
1404
+
1405
+ export const ClientAuthConfig = z.discriminatedUnion("mode", [
1406
+ z.object({
1407
+ mode: z.literal("none"),
1408
+ }),
1409
+ z.object({
1410
+ mode: z.literal("deploymentKey"),
1411
+ headerName: z.literal("x-opengeni-access-key"),
1412
+ }),
1413
+ z.object({
1414
+ mode: z.literal("configuredToken"),
1415
+ headerName: z.literal("authorization"),
1416
+ scheme: z.literal("bearer"),
1417
+ }),
1418
+ z.object({
1419
+ mode: z.literal("managedSession"),
1420
+ session: z.literal("cookie"),
1421
+ }),
1422
+ ]);
1423
+ export type ClientAuthConfig = z.infer<typeof ClientAuthConfig>;
1424
+
1425
+ export const ClientConfig = z.object({
1426
+ deploymentRevision: z.string(),
1427
+ defaultModel: z.string(),
1428
+ allowedModels: z.array(z.string()).min(1),
1429
+ defaultReasoningEffort: ReasoningEffort,
1430
+ allowedReasoningEfforts: z.array(ReasoningEffort).min(1),
1431
+ mcpServers: z.array(z.object({
1432
+ id: z.string(),
1433
+ name: z.string(),
1434
+ })).default([]),
1435
+ fileUploads: z.object({
1436
+ enabled: z.boolean(),
1437
+ maxSizeBytes: z.number().int().positive(),
1438
+ }),
1439
+ productAccessMode: ProductAccessMode,
1440
+ auth: ClientAuthConfig.default({ mode: "none" }),
1441
+ });
1442
+ export type ClientConfig = z.infer<typeof ClientConfig>;
1443
+
1444
+ function base64UrlEncode(value: string): string {
1445
+ return Buffer.from(value, "utf8").toString("base64url");
1446
+ }
1447
+
1448
+ function base64UrlDecode(value: string): string {
1449
+ return Buffer.from(value, "base64url").toString("utf8");
1450
+ }
1451
+
1452
+ async function hmacSha256Base64Url(secret: string, value: string): Promise<string> {
1453
+ const key = await crypto.subtle.importKey(
1454
+ "raw",
1455
+ new TextEncoder().encode(secret),
1456
+ { name: "HMAC", hash: "SHA-256" },
1457
+ false,
1458
+ ["sign"],
1459
+ );
1460
+ const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(value));
1461
+ return Buffer.from(signature).toString("base64url");
1462
+ }
1463
+
1464
+ function constantTimeEqual(actual: string, expected: string): boolean {
1465
+ const actualBytes = new TextEncoder().encode(actual);
1466
+ const expectedBytes = new TextEncoder().encode(expected);
1467
+ if (actualBytes.length !== expectedBytes.length) {
1468
+ return false;
1469
+ }
1470
+ let diff = 0;
1471
+ for (let index = 0; index < actualBytes.length; index += 1) {
1472
+ diff |= actualBytes[index]! ^ expectedBytes[index]!;
1473
+ }
1474
+ return diff === 0;
1475
+ }
1476
+
1477
+ export type HealthResponse = {
1478
+ service: string;
1479
+ environment: string;
1480
+ ok: boolean;
1481
+ };