@neta-art/cohub 8.17.0 → 8.19.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,55 @@
1
- import { $ as BoardConnectionSchema, A as DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, B as BoardItemPatchSchema, C as SessionPatchReducer, D as DESKTOP_COMMAND_MAX_TIMEOUT_MS, E as DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, F as parseDesktopCommand, G as parseBoardEffectInput, H as BoardSemanticMutationSchema, I as parseDesktopCommandId, J as BoardCompositionInputSchema, K as BOARD_ANIMATION_CHANNELS, L as resolveOpenSurface, M as DESKTOP_COMMAND_VERSION, N as isDesktopCallMethod, O as DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, P as isTerminalDesktopCommandStatus, Q as parseBoardCompositionInput, R as NAVIGATION_ERROR_MESSAGE_MAX_LENGTH, S as parseAssistantMessageCommit, T as ensureRealtimeConnected, U as BoardEffectInputSchema, V as BoardSemanticCommandSchema, W as BoardEffectSchema, X as BoardProceduralClipSchema, Y as BoardCompositionSchema, Z as BoardTrackSchema, _ as PublicInviteApi, a as DesktopCommandsApi, at as SessionAccessApi, b as SessionGenerationStreamClient, c as ReferralsApi, ct as PublicAssetsApi, d as TasksApi, dt as ModelsApi, et as BOARD_ARROW_STROKE_SIZE, f as BoardClient, ft as GenerationsApi, g as SpacesApi, h as SpaceWebhooksApi, i as WorkCommerceApi, it as DEFAULT_BOARD_RENDER_LIMITS, j as DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, k as DESKTOP_COMMAND_PENDING_TTL_SECONDS, l as UsersApi, lt as SkillsApi, m as SpacePublicFilesApi, mt as ChannelsApi, n as createHttpClient, nt as BOARD_BUILTIN_CLIP_KINDS, o as UiCommandsApi, ot as ReferencesApi, p as SpaceClient, pt as CronJobsApi, q as BOARD_ANIMATION_CHANNEL_CAPABILITIES, r as AppCommerceApi, rt as BOARD_BUILTIN_EFFECT_KINDS, s as AppsApi, st as SearchApi, t as CohubHttpClient, tt as BOARD_BUILTIN_CAPABILITIES, u as UserApi, ut as PromptsApi, v as buildSpaceInvitePath, w as createSessionPatchReducer, x as createSessionGenerationStreamClient, y as buildSpacePath, z as BoardAuthoringItemSchema } from "./chunks/http.js";
1
+ import { $ as BoardEffectInputSchema, A as SkillsApi, B as DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, C as SessionPatchReducer, D as ReferencesApi, E as SessionAccessApi, F as ChannelsApi, G as parseDesktopCommand, H as DESKTOP_COMMAND_VERSION, I as DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, J as NAVIGATION_ERROR_MESSAGE_MAX_LENGTH, K as parseDesktopCommandId, L as DESKTOP_COMMAND_MAX_TIMEOUT_MS, M as ModelsApi, N as GenerationsApi, O as SearchApi, P as CronJobsApi, Q as BoardSemanticMutationSchema, R as DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, S as parseAssistantMessageCommit, T as ensureRealtimeConnected, U as isDesktopCallMethod, V as DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, W as isTerminalDesktopCommandStatus, X as BoardItemPatchSchema, Y as BoardAuthoringItemSchema, Z as BoardSemanticCommandSchema, _ as PublicInviteApi, a as DesktopCommandsApi, at as BoardCompositionSchema, b as SessionGenerationStreamClient, c as ReferralsApi, ct as parseBoardCompositionInput, d as TasksApi, dt as BOARD_BUILTIN_CAPABILITIES, et as BoardEffectSchema, f as BoardClient, ft as BOARD_BUILTIN_CLIP_KINDS, g as SpacesApi, h as SpaceWebhooksApi, i as WorkCommerceApi, it as BoardCompositionInputSchema, j as PromptsApi, k as PublicAssetsApi, l as UsersApi, lt as BoardConnectionSchema, m as SpacePublicFilesApi, mt as DEFAULT_BOARD_RENDER_LIMITS, n as createHttpClient, nt as BOARD_ANIMATION_CHANNELS, o as UiCommandsApi, ot as BoardProceduralClipSchema, p as SpaceClient, pt as BOARD_BUILTIN_EFFECT_KINDS, q as resolveOpenSurface, r as AppCommerceApi, rt as BOARD_ANIMATION_CHANNEL_CAPABILITIES, s as AppsApi, st as BoardTrackSchema, t as CohubHttpClient, tt as parseBoardEffectInput, u as UserApi, ut as BOARD_ARROW_STROKE_SIZE, v as buildSpaceInvitePath, w as createSessionPatchReducer, x as createSessionGenerationStreamClient, y as buildSpacePath, z as DESKTOP_COMMAND_PENDING_TTL_SECONDS } from "./chunks/http.js";
2
2
  import { _ as requestSourceToHeaders, a as sanitizeAccessToken, b as REALTIME_ROOM_EVENT_NAME_PATTERN, c as REQUEST_SOURCE_VIA_MAX_LENGTH, d as isRequestSourceEmpty, f as isRequestSourceUuid, g as readRequestSourceFromEnv, h as parseRequestSourceFromHeaders, i as matchesUnauthorizedErrorToken, l as hasRequestSourceIdentity, m as normalizeRequestSource, n as HttpTransport, o as COHUB_SOURCE_HEADER, p as mergeRequestSourceIntoMeta, r as joinApiUrl, s as COHUB_SOURCE_HEADER_NAMES, t as HttpError, u as isRequestSourceClientId, v as resolveRequestSourceChannel, y as isUuid } from "./chunks/transport.js";
3
3
  import { a as normalizeWebsocketUrl, c as resolveExecutionAppId, d as resolveWebsocketUrl, f as PERMISSIONS, h as scopeListHasPermission, i as normalizeVoiceInputWebsocketUrl, l as resolveExecutionToken, m as isUserLevelPermission, n as getCohubContext, o as resolveApiBaseUrl, p as USER_LEVEL_PERMISSIONS, r as normalizeBaseUrl, s as resolveCohubEnvironment, t as COHUB_ENVIRONMENTS, u as resolveVoiceInputWebsocketUrl } from "./chunks/environment.js";
4
4
  import { a as extractBillingPayload, c as isFeatureNotEntitledError, i as FEATURE_NOT_ENTITLED_ERROR_CODE, l as isHttpErrorCode, n as createWebsocketClient, o as isBillingAccessBlockedCode, r as BILLING_ACCESS_BLOCKED_ERROR_CODE, s as isBillingAccessBlockedError, t as WebsocketClient } from "./chunks/websocket.js";
5
5
  import { VoiceApi, VoiceInputClient, createVoiceInputClient } from "./voice-input.js";
6
6
  import { filterSpacePickerItems, normalizeSpacePickerQuery, orderSpacePickerItems, selectSpacePickerItems } from "./space-picker.js";
7
7
  import { z } from "zod";
8
+ //#region ../protocol/dist/app-authorization.js
9
+ const appAuthorizationTargetSchema = z.discriminatedUnion("kind", [
10
+ z.object({ kind: z.literal("account") }),
11
+ z.object({
12
+ kind: z.literal("space"),
13
+ spaceId: z.string().min(1)
14
+ }),
15
+ z.object({ kind: z.literal("pick-space") })
16
+ ]);
17
+ const appAuthorizationRequestSchema = z.object({
18
+ target: appAuthorizationTargetSchema,
19
+ scopes: z.array(z.string().min(1)).min(1).max(128),
20
+ reason: z.string().max(280).optional(),
21
+ alwaysAsk: z.boolean().optional(),
22
+ fallback: z.enum(["allow", "none"]).default("allow")
23
+ });
24
+ const appAuthorizationGrantSchema = z.object({
25
+ id: z.string().min(1),
26
+ spaceId: z.string().min(1),
27
+ scopes: z.array(z.string()),
28
+ expiresAt: z.string().nullable()
29
+ });
30
+ const appAuthorizationResultSchema = z.discriminatedUnion("status", [
31
+ z.object({
32
+ status: z.literal("granted"),
33
+ requestedTarget: appAuthorizationTargetSchema,
34
+ target: z.discriminatedUnion("kind", [z.object({ kind: z.literal("account") }), z.object({
35
+ kind: z.literal("space"),
36
+ spaceId: z.string().min(1),
37
+ name: z.string().nullable()
38
+ })]),
39
+ resolution: z.enum([
40
+ "requested",
41
+ "selected",
42
+ "fallback"
43
+ ]),
44
+ grant: appAuthorizationGrantSchema
45
+ }),
46
+ z.object({ status: z.literal("cancelled") }),
47
+ z.object({
48
+ status: z.literal("denied"),
49
+ code: z.string()
50
+ })
51
+ ]);
52
+ //#endregion
8
53
  //#region ../protocol/dist/board.js
9
54
  const BOARD_DOCUMENT_KIND = "cohub.board";
10
55
  const BOARD_MANIFEST_KIND = "cohub.board.manifest";
@@ -1205,6 +1250,451 @@ function filterGenerationDeclarationsByPolicy(declarations, policy) {
1205
1250
  });
1206
1251
  }
1207
1252
  //#endregion
1253
+ //#region ../protocol/dist/core/content-schema.js
1254
+ const meta = z.record(z.string(), z.unknown());
1255
+ const contentBlockSchema = z.lazy(() => z.discriminatedUnion("type", [
1256
+ z.object({
1257
+ type: z.literal("text"),
1258
+ text: z.string(),
1259
+ _meta: meta.optional()
1260
+ }),
1261
+ z.object({
1262
+ type: z.literal("thinking"),
1263
+ thinking: z.string(),
1264
+ signature: z.string().optional(),
1265
+ _meta: meta.optional()
1266
+ }),
1267
+ z.object({
1268
+ type: z.literal("image"),
1269
+ source: z.union([z.object({
1270
+ type: z.literal("url"),
1271
+ url: z.string().url()
1272
+ }), z.object({
1273
+ type: z.literal("base64"),
1274
+ media_type: z.string(),
1275
+ data: z.string()
1276
+ })]),
1277
+ _meta: meta.optional()
1278
+ }),
1279
+ z.object({
1280
+ type: z.literal("shell_command"),
1281
+ command: z.string(),
1282
+ rawText: z.string(),
1283
+ _meta: meta.optional()
1284
+ }),
1285
+ z.object({
1286
+ type: z.literal("tool_use"),
1287
+ id: z.string().min(1),
1288
+ name: z.string().min(1),
1289
+ input: z.record(z.string(), z.unknown()),
1290
+ _meta: meta.optional()
1291
+ }),
1292
+ z.object({
1293
+ type: z.literal("tool_result"),
1294
+ tool_use_id: z.string().min(1),
1295
+ content: z.union([z.string(), z.array(contentBlockSchema)]),
1296
+ is_error: z.boolean().optional(),
1297
+ _meta: meta.optional()
1298
+ }),
1299
+ z.object({
1300
+ type: z.literal("system_note"),
1301
+ note_type: z.enum([
1302
+ "session_created",
1303
+ "forked",
1304
+ "compacted",
1305
+ "info"
1306
+ ]),
1307
+ text: z.string(),
1308
+ _meta: meta.optional()
1309
+ })
1310
+ ]));
1311
+ //#endregion
1312
+ //#region ../protocol/dist/runtime/archive.js
1313
+ const RUNTIME_ARCHIVE_SEGMENT_BYTES = 4194304;
1314
+ const digest = z.string().regex(/^[a-f0-9]{64}$/);
1315
+ const runtimeArchiveSegmentSchema = z.object({
1316
+ offset: z.number().int().safe().nonnegative(),
1317
+ sizeBytes: z.number().int().min(1).max(RUNTIME_ARCHIVE_SEGMENT_BYTES),
1318
+ sha256: digest,
1319
+ md5: z.string().regex(/^[a-f0-9]{32}$/)
1320
+ }).strict();
1321
+ const harnessArchiveSchema = z.object({
1322
+ sessionId: z.string().uuid(),
1323
+ turnId: z.string().uuid(),
1324
+ harness: z.enum(["pi", "codex"])
1325
+ }).strict();
1326
+ const harnessArchiveIndexSchema = harnessArchiveSchema.extend({
1327
+ version: z.literal(1),
1328
+ nativeFormat: z.enum(["pi.jsonl", "codex.rollout"]),
1329
+ nativeSessionId: z.string().min(1).max(255),
1330
+ parentTurnId: z.string().uuid().nullable(),
1331
+ sizeBytes: z.number().int().safe().positive(),
1332
+ sha256: digest,
1333
+ segments: z.array(runtimeArchiveSegmentSchema).max(256)
1334
+ }).strict().refine((value) => value.nativeFormat === (value.harness === "pi" ? "pi.jsonl" : "codex.rollout"), "Native format mismatch");
1335
+ function validateArchiveBoundary(index, parent) {
1336
+ if (index.parentTurnId !== (parent?.turnId ?? null)) throw new Error("Archive parent mismatch");
1337
+ if (parent && (parent.sessionId !== index.sessionId || parent.harness !== index.harness || parent.nativeSessionId !== index.nativeSessionId)) throw new Error("Archive identity mismatch");
1338
+ let offset = parent?.sizeBytes ?? 0;
1339
+ for (const segment of index.segments) {
1340
+ if (segment.offset !== offset) throw new Error("Archive segments are not contiguous");
1341
+ offset += segment.sizeBytes;
1342
+ }
1343
+ if (offset !== index.sizeBytes || !parent && !index.segments.length) throw new Error("Archive is incomplete");
1344
+ if (!index.segments.length && parent?.sha256 !== index.sha256) throw new Error("Unchanged archive checksum mismatch");
1345
+ }
1346
+ //#endregion
1347
+ //#region ../protocol/dist/runtime/context.js
1348
+ const createEmptyUsage = () => ({
1349
+ input: 0,
1350
+ output: 0,
1351
+ cacheRead: 0,
1352
+ cacheWrite: 0,
1353
+ totalTokens: 0,
1354
+ cost: {
1355
+ input: 0,
1356
+ output: 0,
1357
+ cacheRead: 0,
1358
+ cacheWrite: 0,
1359
+ total: 0
1360
+ }
1361
+ });
1362
+ /**
1363
+ * Project one block as itself, or drop it. Nothing is ever rewritten into prompt text.
1364
+ * URL images, system notes and unknown blocks have no native representation here; the
1365
+ * durable copy stays in the database and is not faked into the model's input.
1366
+ */
1367
+ function projectBlock(block) {
1368
+ if (block.type === "text") return {
1369
+ type: "text",
1370
+ text: block.text
1371
+ };
1372
+ if (block.type === "image") return block.source.type === "base64" ? {
1373
+ type: "image",
1374
+ data: block.source.data,
1375
+ mimeType: block.source.media_type
1376
+ } : null;
1377
+ if (block.type === "thinking") return {
1378
+ type: "thinking",
1379
+ thinking: block.thinking,
1380
+ ...block.signature ? { thinkingSignature: block.signature } : {}
1381
+ };
1382
+ return null;
1383
+ }
1384
+ const projectContent = (content) => content.map(projectBlock).filter((block) => block !== null);
1385
+ const isCompaction = (message) => message.role === "system" && message.content.some((block) => block.type === "system_note" && block.note_type === "compacted");
1386
+ /** A generation placeholder must never be projected as if it were a finished result. */
1387
+ const isPendingGenerationResult = (message) => message.role === "assistant" && message.meta?.messageKind === "generation_result" && message.meta?.generationStatus !== "completed" && message.meta?.generationStatus !== "failed";
1388
+ function selectRuntimeContextMessages(messages) {
1389
+ let boundary = -1;
1390
+ let latest = -1;
1391
+ messages.forEach((message, index) => {
1392
+ if (!isCompaction(message)) return;
1393
+ const compaction = message.meta?.compaction;
1394
+ const timestamp = Date.parse(compaction?.compactedAt ?? String(message.meta?.createdAt ?? ""));
1395
+ const order = Number.isFinite(timestamp) ? timestamp : index;
1396
+ if (order >= latest) {
1397
+ latest = order;
1398
+ boundary = index;
1399
+ }
1400
+ });
1401
+ const compaction = messages[boundary];
1402
+ if (!compaction) return messages;
1403
+ return [compaction, ...messages.slice(boundary + 1).filter((message) => !isCompaction(message))];
1404
+ }
1405
+ /**
1406
+ * Compile durable, normalized messages into Pi-compatible context without replaying tools.
1407
+ * Representable blocks and tool pairing are preserved verbatim; anything without a native
1408
+ * representation is dropped rather than described in prompt text.
1409
+ */
1410
+ function contextToPiMessages(messages, options = {}) {
1411
+ const result = [];
1412
+ for (const message of selectRuntimeContextMessages(messages)) {
1413
+ if (isPendingGenerationResult(message)) continue;
1414
+ const meta = {
1415
+ ...message.meta,
1416
+ messageId: message.id,
1417
+ turnId: message.turnId
1418
+ };
1419
+ const recordedAt = Date.parse(String(message.meta?.createdAt ?? ""));
1420
+ const timestamp = Number.isFinite(recordedAt) ? recordedAt : 0;
1421
+ if (message.role === "system") continue;
1422
+ if (message.role !== "assistant") {
1423
+ const projected = projectContent(message.content);
1424
+ if (projected.length) result.push({
1425
+ role: "user",
1426
+ content: projected,
1427
+ timestamp,
1428
+ meta
1429
+ });
1430
+ continue;
1431
+ }
1432
+ const calls = new Map(message.content.flatMap((block) => block.type === "tool_use" ? [[block.id, block.name]] : []));
1433
+ const content = message.content.flatMap((block) => {
1434
+ if (block.type === "tool_result") return [];
1435
+ if (block.type === "tool_use") return [{
1436
+ type: "toolCall",
1437
+ id: block.id,
1438
+ name: block.name,
1439
+ arguments: block.input
1440
+ }];
1441
+ const projected = projectBlock(block);
1442
+ return projected ? [projected] : [];
1443
+ });
1444
+ if (content.length) result.push({
1445
+ role: "assistant",
1446
+ content,
1447
+ api: typeof message.meta?.nativeApi === "string" ? message.meta.nativeApi : options.resolveApi?.({
1448
+ provider: message.provider ?? null,
1449
+ model: message.model ?? null
1450
+ }) ?? "history",
1451
+ provider: message.provider ?? options.fallbackProvider ?? "history",
1452
+ model: message.model ?? "history",
1453
+ usage: {
1454
+ ...createEmptyUsage(),
1455
+ ...message.usage
1456
+ },
1457
+ stopReason: message.stopReason === "tool_use" ? "toolUse" : message.stopReason ?? (calls.size ? "toolUse" : "stop"),
1458
+ ...message.errorMessage ? { errorMessage: message.errorMessage } : {},
1459
+ timestamp,
1460
+ meta
1461
+ });
1462
+ for (const block of message.content) {
1463
+ if (block.type !== "tool_result") continue;
1464
+ if (!calls.has(block.tool_use_id)) continue;
1465
+ result.push({
1466
+ role: "toolResult",
1467
+ toolCallId: block.tool_use_id,
1468
+ toolName: calls.get(block.tool_use_id) ?? "tool",
1469
+ content: typeof block.content === "string" ? [{
1470
+ type: "text",
1471
+ text: block.content
1472
+ }] : projectContent(block.content),
1473
+ isError: block.is_error ?? false,
1474
+ timestamp,
1475
+ meta
1476
+ });
1477
+ calls.delete(block.tool_use_id);
1478
+ }
1479
+ }
1480
+ return result;
1481
+ }
1482
+ //#endregion
1483
+ //#region ../protocol/dist/runtime/index.js
1484
+ const RUNTIME_PROTOCOL_VERSION = 1;
1485
+ const RUNTIME_MAX_FRAME_BYTES = 33554432;
1486
+ const RUNTIME_RECOVERY_BATCH_SIZE = 64;
1487
+ z.enum([
1488
+ "cohub",
1489
+ "pi",
1490
+ "codex"
1491
+ ]);
1492
+ const isLocalHarness = (value) => value === "pi" || value === "codex";
1493
+ const resolveHarness = (meta) => {
1494
+ const value = meta && typeof meta === "object" ? meta.harness : null;
1495
+ return isLocalHarness(value) ? value : "cohub";
1496
+ };
1497
+ const runtimeCapabilitiesSchema = z.object({
1498
+ harnesses: z.array(z.enum(["pi", "codex"])).min(1).max(2),
1499
+ models: z.array(z.object({
1500
+ harness: z.enum(["pi", "codex"]),
1501
+ provider: z.string().max(100),
1502
+ id: z.string().min(1).max(255),
1503
+ name: z.string().max(255)
1504
+ })).max(2e3)
1505
+ });
1506
+ const runtimePendingExecutionSchema = z.object({
1507
+ sessionId: z.string().uuid(),
1508
+ turnId: z.string().uuid(),
1509
+ harness: z.enum(["pi", "codex"])
1510
+ }).strict();
1511
+ z.object({
1512
+ connectionId: z.string().uuid(),
1513
+ endpoint: z.url({ protocol: /^wss?$/ }),
1514
+ ownerUserId: z.string().min(1),
1515
+ capabilities: runtimeCapabilitiesSchema
1516
+ });
1517
+ const runtimeReadySchema = z.object({
1518
+ type: z.literal("runtime.ready"),
1519
+ connectionId: z.string().uuid()
1520
+ });
1521
+ z.object({
1522
+ expectedTurnId: z.string().uuid(),
1523
+ revision: z.string().min(1),
1524
+ confirmed: z.literal(true)
1525
+ }).strict();
1526
+ const id = z.string().uuid();
1527
+ const ordinal = z.number().int().min(0).max(1e5);
1528
+ const content = z.array(contentBlockSchema).max(1e5);
1529
+ const count = z.number().finite().nonnegative().optional();
1530
+ const usageSchema = z.object({
1531
+ input: count,
1532
+ output: count,
1533
+ cacheRead: count,
1534
+ cacheWrite: count,
1535
+ totalTokens: count,
1536
+ cost: z.object({
1537
+ input: count,
1538
+ output: count,
1539
+ cacheRead: count,
1540
+ cacheWrite: count,
1541
+ total: count
1542
+ }).nullable().optional()
1543
+ });
1544
+ const runtimeMessageSchema = z.object({
1545
+ ordinal,
1546
+ content,
1547
+ provider: z.string().nullable().optional(),
1548
+ model: z.string().nullable().optional(),
1549
+ usage: usageSchema.nullable().optional(),
1550
+ stopReason: z.string().max(64).nullable().optional(),
1551
+ errorMessage: z.string().max(16384).nullable().optional()
1552
+ });
1553
+ const runtimeEventSchema = z.discriminatedUnion("type", [
1554
+ z.object({
1555
+ type: z.literal("message.start"),
1556
+ ordinal
1557
+ }),
1558
+ z.object({
1559
+ type: z.literal("text.delta"),
1560
+ ordinal,
1561
+ index: ordinal,
1562
+ kind: z.enum(["text", "thinking"]),
1563
+ delta: z.string()
1564
+ }),
1565
+ z.object({
1566
+ type: z.literal("content.replace"),
1567
+ ordinal,
1568
+ content
1569
+ }),
1570
+ z.object({
1571
+ type: z.literal("message.commit"),
1572
+ message: runtimeMessageSchema
1573
+ }),
1574
+ z.object({
1575
+ type: z.literal("turn.end"),
1576
+ message: runtimeMessageSchema,
1577
+ archive: harnessArchiveSchema.nullable().optional(),
1578
+ resume: z.enum([
1579
+ "native",
1580
+ "restored",
1581
+ "handoff",
1582
+ "new"
1583
+ ])
1584
+ }),
1585
+ z.object({
1586
+ type: z.literal("context.required"),
1587
+ pendingTurnIds: z.array(id).max(2).optional(),
1588
+ historyOnly: z.boolean().optional()
1589
+ }),
1590
+ z.object({ type: z.literal("turn.acknowledged") }),
1591
+ z.object({
1592
+ type: z.literal("turn.error"),
1593
+ message: z.string().max(16384),
1594
+ uncertain: z.boolean().optional()
1595
+ })
1596
+ ]);
1597
+ z.discriminatedUnion("type", [
1598
+ z.object({
1599
+ type: z.literal("runtime.hello"),
1600
+ version: z.literal(1),
1601
+ spaceId: id,
1602
+ token: z.string().min(1).max(16384),
1603
+ capabilities: runtimeCapabilitiesSchema
1604
+ }).strict(),
1605
+ z.object({
1606
+ type: z.literal("runtime.recovery"),
1607
+ executions: z.array(runtimePendingExecutionSchema).min(1).max(64)
1608
+ }).strict(),
1609
+ z.object({ type: z.literal("runtime.heartbeat") }),
1610
+ z.object({
1611
+ type: z.literal("runtime.auth"),
1612
+ token: z.string().min(1).max(16384)
1613
+ }),
1614
+ z.object({
1615
+ type: z.literal("runtime.event"),
1616
+ requestId: id,
1617
+ event: runtimeEventSchema
1618
+ })
1619
+ ]);
1620
+ const runtimeContextSchema = z.object({
1621
+ complete: z.boolean().optional(),
1622
+ revision: z.string(),
1623
+ throughTurnId: id.nullable(),
1624
+ messages: z.array(z.object({
1625
+ id: z.string(),
1626
+ turnId: id,
1627
+ role: z.enum([
1628
+ "user",
1629
+ "assistant",
1630
+ "system"
1631
+ ]),
1632
+ content,
1633
+ provider: z.string().nullable().optional(),
1634
+ model: z.string().nullable().optional(),
1635
+ usage: usageSchema.nullable().optional(),
1636
+ stopReason: z.string().nullable().optional(),
1637
+ errorMessage: z.string().nullable().optional(),
1638
+ meta: z.record(z.string(), z.unknown()).nullable().optional()
1639
+ })),
1640
+ archive: harnessArchiveSchema.nullable().optional(),
1641
+ resolvedTurnIds: z.array(id).max(2).optional(),
1642
+ settledTurnIds: z.array(id).max(2).optional()
1643
+ });
1644
+ const runtimeCommandSchema = z.discriminatedUnion("type", [
1645
+ z.object({
1646
+ type: z.literal("turn.recover"),
1647
+ requestId: id,
1648
+ execution: z.object({
1649
+ spaceId: id,
1650
+ sessionId: id,
1651
+ turnId: id,
1652
+ harness: z.enum(["pi", "codex"])
1653
+ })
1654
+ }),
1655
+ z.object({
1656
+ type: z.literal("turn.start"),
1657
+ requestId: id,
1658
+ resumeOnly: z.boolean().optional(),
1659
+ input: z.object({
1660
+ spaceId: id,
1661
+ sessionId: id,
1662
+ turnId: id,
1663
+ userMessageId: id,
1664
+ harness: z.enum(["pi", "codex"]),
1665
+ messages: z.array(z.object({
1666
+ turnId: id,
1667
+ userMessageId: id,
1668
+ userId: z.string().nullable(),
1669
+ content
1670
+ })).min(1).max(64),
1671
+ context: runtimeContextSchema,
1672
+ provider: z.string().nullable().optional(),
1673
+ model: z.string().nullable().optional(),
1674
+ thinkingLevel: z.string().nullable().optional(),
1675
+ accessMode: z.enum(["read_only", "full_access"])
1676
+ }).refine((input) => {
1677
+ const owner = input.messages.at(-1);
1678
+ return owner?.turnId === input.turnId && owner.userMessageId === input.userMessageId && new Set(input.messages.map((message) => message.turnId)).size === input.messages.length && new Set(input.messages.map((message) => message.userMessageId)).size === input.messages.length;
1679
+ }, "Runtime batch owner or message identity mismatch")
1680
+ }),
1681
+ z.object({
1682
+ type: z.literal("session.context"),
1683
+ requestId: id,
1684
+ context: runtimeContextSchema
1685
+ }),
1686
+ z.object({
1687
+ type: z.literal("turn.abort"),
1688
+ requestId: id
1689
+ }),
1690
+ z.object({
1691
+ type: z.literal("turn.ack"),
1692
+ requestId: id,
1693
+ revision: z.string(),
1694
+ turnId: id
1695
+ })
1696
+ ]);
1697
+ //#endregion
1208
1698
  //#region ../protocol/dist/public-identifiers.js
1209
1699
  const USERNAME_PATTERN = /^(?!-)(?!.*--)[a-z0-9-]{1,39}(?<!-)$/;
1210
1700
  const SPACE_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9_-]{0,78}[a-z0-9])?$/;
@@ -2187,6 +2677,18 @@ function attachAppEmbed(frame, options) {
2187
2677
  }
2188
2678
  //#endregion
2189
2679
  //#region src/app-runtime.ts
2680
+ var AppRuntimeError = class extends Error {
2681
+ code;
2682
+ requestId;
2683
+ retryable;
2684
+ constructor(code, message, requestId) {
2685
+ super(message);
2686
+ this.code = code;
2687
+ this.requestId = requestId;
2688
+ this.name = "AppRuntimeError";
2689
+ this.retryable = code === "host_timeout" || code === "login_timeout" || code === "request_failed";
2690
+ }
2691
+ };
2190
2692
  const isBrowser$1 = () => typeof window !== "undefined" && typeof window.parent !== "undefined";
2191
2693
  const hasParent = () => isBrowser$1() && window.parent !== window;
2192
2694
  const getParentOrigin = () => {
@@ -2258,7 +2760,7 @@ var ParentBridgeTransport = class {
2258
2760
  };
2259
2761
  }
2260
2762
  subscribeContextChanged(listener) {
2261
- if (!hasParent()) return () => {};
2763
+ if (!hasParent() || typeof window.addEventListener !== "function") return () => {};
2262
2764
  this.contextListeners.add(listener);
2263
2765
  if (!this.contextListener) {
2264
2766
  this.contextListener = (event) => {
@@ -2321,7 +2823,7 @@ var ParentBridgeTransport = class {
2321
2823
  cleanup();
2322
2824
  this.trustedParentOrigin = event.origin;
2323
2825
  if (data.type === "cohub.app.error") {
2324
- reject(new Error(data.message));
2826
+ reject(new AppRuntimeError(data.code ?? "request_failed", data.message, requestId));
2325
2827
  return;
2326
2828
  }
2327
2829
  resolve(data);
@@ -2362,6 +2864,7 @@ var PopupBrokerTransport = class {
2362
2864
  if (message.type === "cohub.app.context") return {
2363
2865
  type: "cohub.app.context.result",
2364
2866
  context: {
2867
+ mode: "broker",
2365
2868
  app: {
2366
2869
  id: await this.resolveAppId() ?? "",
2367
2870
  slug: "",
@@ -2397,6 +2900,7 @@ var PopupBrokerTransport = class {
2397
2900
  return;
2398
2901
  }
2399
2902
  let ready = false;
2903
+ let loginPending = false;
2400
2904
  let settled = false;
2401
2905
  let timer = null;
2402
2906
  let closeChecker = null;
@@ -2422,8 +2926,20 @@ var PopupBrokerTransport = class {
2422
2926
  if (event.origin !== this.brokerOrigin) return;
2423
2927
  const data = event.data;
2424
2928
  if (!data) return;
2929
+ if (data.type === "cohub.app.broker.progress" && !loginPending && !ready) {
2930
+ loginPending = true;
2931
+ if (timer) clearTimeout(timer);
2932
+ timer = setTimeout(() => finish(() => reject(new AppRuntimeError("login_timeout", "Login did not complete in time."))), 6e5);
2933
+ return;
2934
+ }
2425
2935
  if (data.type === "cohub.app.broker.ready" && !ready) {
2936
+ if (message.type === "cohub.app.authorize.v2" && data.authorizationVersion !== 2) {
2937
+ finish(() => reject(new AppRuntimeError("unsupported", "This host does not support structured authorization.")));
2938
+ return;
2939
+ }
2426
2940
  ready = true;
2941
+ if (timer) clearTimeout(timer);
2942
+ timer = setTimeout(() => finish(() => reject(new AppRuntimeError("host_timeout", "Authorization timed out."))), timeoutMs);
2427
2943
  try {
2428
2944
  popup.postMessage({
2429
2945
  ...message,
@@ -2437,7 +2953,7 @@ var PopupBrokerTransport = class {
2437
2953
  if (data.requestId !== requestId) return;
2438
2954
  finish(() => {
2439
2955
  if (data.type === "cohub.app.error") {
2440
- reject(new Error(data.message));
2956
+ reject(new AppRuntimeError(data.code ?? "request_failed", data.message, requestId));
2441
2957
  return;
2442
2958
  }
2443
2959
  resolve(data);
@@ -2446,7 +2962,8 @@ var PopupBrokerTransport = class {
2446
2962
  window.addEventListener("message", onMessage);
2447
2963
  closeChecker = setInterval(() => {
2448
2964
  if (popup.closed) finish(() => {
2449
- if (!ready) reject(/* @__PURE__ */ new Error("Authorization window was closed."));
2965
+ if (message.type === "cohub.app.authorize.v2") resolve({ result: { status: "cancelled" } });
2966
+ else if (!ready) reject(/* @__PURE__ */ new Error("Authorization window was closed."));
2450
2967
  else resolve(null);
2451
2968
  });
2452
2969
  }, 500);
@@ -2564,6 +3081,7 @@ var AppRuntimeApi = class {
2564
3081
  }
2565
3082
  constructor(transport = new ParentBridgeTransport(), appId, appIdResolver) {
2566
3083
  this.transport = transport;
3084
+ this.contextUnsubscribe = transport.subscribeContextChanged?.((context) => this.observeContext(context)) ?? null;
2567
3085
  this.appIdResolver = appIdResolver;
2568
3086
  this.diagnosticUnsubscribe = transport.subscribeDiagnostics?.((diagnostic) => {
2569
3087
  console.warn(`[cohub] ${diagnostic.message} (${diagnostic.code})`, diagnostic.detail ?? {});
@@ -2605,6 +3123,10 @@ var AppRuntimeApi = class {
2605
3123
  return this.storageKeysReady;
2606
3124
  }
2607
3125
  readStoredToken() {
3126
+ if (this.transport instanceof PopupBrokerTransport) {
3127
+ this.writeStoredToken(null);
3128
+ return null;
3129
+ }
2608
3130
  if (!this.tokenStorageKey || typeof localStorage === "undefined") return null;
2609
3131
  try {
2610
3132
  return localStorage.getItem(this.tokenStorageKey);
@@ -2613,6 +3135,7 @@ var AppRuntimeApi = class {
2613
3135
  }
2614
3136
  }
2615
3137
  writeStoredToken(token) {
3138
+ if (this.transport instanceof PopupBrokerTransport) token = null;
2616
3139
  if (!this.tokenStorageKey || typeof localStorage === "undefined") return;
2617
3140
  try {
2618
3141
  if (token) localStorage.setItem(this.tokenStorageKey, token);
@@ -2638,11 +3161,30 @@ var AppRuntimeApi = class {
2638
3161
  else localStorage.removeItem(this.grantsStorageKey);
2639
3162
  } catch {}
2640
3163
  }
3164
+ serverGrants = false;
3165
+ viewerIdentity;
3166
+ identityVersion = 0;
3167
+ contextUnsubscribe = null;
3168
+ observeContext(context) {
3169
+ if (context.capabilities?.serverGrants) this.serverGrants = true;
3170
+ if (context.viewer === void 0) return;
3171
+ const viewer = context.viewer?.userUuid ?? null;
3172
+ if (this.viewerIdentity !== void 0 && viewer !== this.viewerIdentity) {
3173
+ this.identityVersion++;
3174
+ this.token = null;
3175
+ this.authorizedGrants = null;
3176
+ this.writeStoredToken(null);
3177
+ this.writeStoredGrants(null);
3178
+ }
3179
+ this.viewerIdentity = viewer;
3180
+ }
2641
3181
  async context() {
2642
- return (await this.transport.request({ type: "cohub.app.context" }, {
3182
+ const response = await this.transport.request({ type: "cohub.app.context" }, {
2643
3183
  timeoutMs: 8e3,
2644
3184
  retryIntervalMs: 250
2645
- }))?.context ?? null;
3185
+ });
3186
+ if (response?.context) this.observeContext(response.context);
3187
+ return response?.context ?? null;
2646
3188
  }
2647
3189
  onContextChanged(listener) {
2648
3190
  return this.transport.subscribeContextChanged?.(listener) ?? (() => {});
@@ -2659,6 +3201,8 @@ var AppRuntimeApi = class {
2659
3201
  const unsubscribe = this.diagnosticUnsubscribe;
2660
3202
  this.diagnosticUnsubscribe = null;
2661
3203
  unsubscribe?.();
3204
+ this.contextUnsubscribe?.();
3205
+ this.contextUnsubscribe = null;
2662
3206
  this.pointerForwarding = false;
2663
3207
  this.pointerPending = null;
2664
3208
  if (this.pointerFrame !== null) {
@@ -2720,12 +3264,13 @@ var AppRuntimeApi = class {
2720
3264
  }
2721
3265
  async getAccessToken(options) {
2722
3266
  await this.ensureStorageKeys();
3267
+ const identityVersion = this.identityVersion;
2723
3268
  if (this.token && !options?.forceRefresh) return this.token;
2724
3269
  if (options?.forceRefresh) {
2725
3270
  this.token = null;
2726
3271
  this.writeStoredToken(null);
2727
3272
  }
2728
- if (options?.forceRefresh && this.authorizedGrants && this.authorizedGrants.length > 0) {
3273
+ if (!this.serverGrants && options?.forceRefresh && this.authorizedGrants && this.authorizedGrants.length > 0) {
2729
3274
  let refreshed = false;
2730
3275
  let next = [];
2731
3276
  for (const grant of this.authorizedGrants) try {
@@ -2734,12 +3279,14 @@ var AppRuntimeApi = class {
2734
3279
  scopes: grant.scopes,
2735
3280
  spaceId: grant.spaceId
2736
3281
  }, { timeoutMs: 12e4 });
3282
+ if (identityVersion !== this.identityVersion) return null;
2737
3283
  const token = response?.token ?? null;
2738
3284
  if (!token) continue;
2739
3285
  this.token = token;
2740
3286
  next = recordConsent(next, responseSpaceId(response?.space?.id) ?? grant.spaceId, grant.scopes);
2741
3287
  refreshed = true;
2742
3288
  } catch {
3289
+ if (identityVersion !== this.identityVersion) return null;
2743
3290
  next = recordConsent(next, grant.spaceId, grant.scopes);
2744
3291
  }
2745
3292
  this.authorizedGrants = next;
@@ -2753,11 +3300,37 @@ var AppRuntimeApi = class {
2753
3300
  type: "cohub.app.token",
2754
3301
  forceRefresh: Boolean(options?.forceRefresh)
2755
3302
  }, { timeoutMs: 2e4 });
3303
+ if (identityVersion !== this.identityVersion) return null;
2756
3304
  this.token = response?.token ?? null;
2757
3305
  this.writeStoredToken(this.token);
2758
3306
  return this.token;
2759
3307
  }
3308
+ /** Structured authorization. Legacy hosts cannot execute this message. */
3309
+ async authorize(input) {
3310
+ const parsed = appAuthorizationRequestSchema.safeParse(input);
3311
+ if (!parsed.success) throw new AppRuntimeError("invalid_request", "Invalid authorization request.");
3312
+ await this.ensureStorageKeys();
3313
+ const context = await this.context();
3314
+ if (context?.mode !== "broker" && context?.capabilities?.authorization !== 2) throw new AppRuntimeError("unsupported", "This host does not support structured authorization.");
3315
+ const identityVersion = this.identityVersion;
3316
+ const response = await this.transport.request({
3317
+ type: "cohub.app.authorize.v2",
3318
+ ...parsed.data
3319
+ }, { timeoutMs: 12e4 });
3320
+ if (identityVersion !== this.identityVersion) throw new AppRuntimeError("session_changed", "The signed-in account changed. Please try again.");
3321
+ if (!response) throw new AppRuntimeError("host_timeout", "Authorization host did not respond. It may not support structured authorization.");
3322
+ const result = appAuthorizationResultSchema.safeParse(response.result);
3323
+ if (!result.success) throw new AppRuntimeError("invalid_response", "Invalid authorization result.");
3324
+ this.serverGrants = true;
3325
+ if (result.data.status === "granted") {
3326
+ if (typeof response.token !== "string" || !response.token) throw new AppRuntimeError("invalid_response", "Authorization token is missing.");
3327
+ this.token = response.token;
3328
+ this.writeStoredToken(this.token);
3329
+ }
3330
+ return result.data;
3331
+ }
2760
3332
  /**
3333
+ * @deprecated Use authorize() to receive the actual target and grant.
2761
3334
  * Requests viewer consent. With an accessible `spaceId` the grant targets
2762
3335
  * that Space; otherwise the host resolves a viewer-controlled Space (the
2763
3336
  * invocation or embedding Space, then the viewer's last picked Space, then
@@ -2788,6 +3361,7 @@ var AppRuntimeApi = class {
2788
3361
  return Boolean(token);
2789
3362
  }
2790
3363
  /**
3364
+ * @deprecated Use authorize() with a pick-space target.
2791
3365
  * Asks the viewer to pick a Space and grant the scopes on it — one consent
2792
3366
  * dialog covers both. Resolves with the picked space so the app knows where
2793
3367
  * it may act; `space` is null when the viewer denied.
@@ -2887,7 +3461,8 @@ function createSlugAppIdResolver(deps) {
2887
3461
  if (typeof doFetch !== "function") return null;
2888
3462
  const response = await doFetch(`${deps.apiBaseUrl}/api/apps/by-slug/${encodeURIComponent(deps.ownerUsername)}/${encodeURIComponent(deps.spaceSlug)}/${encodeURIComponent(deps.appSlug)}`);
2889
3463
  if (!response.ok) throw new Error(`getBySlug failed: ${response.status}`);
2890
- return (await response.json())?.work?.id ?? null;
3464
+ const data = await response.json();
3465
+ return data?.app?.id ?? data?.work?.id ?? null;
2891
3466
  })().catch(() => {
2892
3467
  cached = null;
2893
3468
  return null;
@@ -3027,14 +3602,16 @@ var CohubClient = class {
3027
3602
  this.appRuntime.dispose();
3028
3603
  }
3029
3604
  auth = {
3605
+ authorize: (input) => this.appRuntime.authorize(input),
3030
3606
  /**
3607
+ * @deprecated Use authorize() for the actual target and structured result.
3031
3608
  * Ensure the app holds these scopes. Without an accessible `spaceId` the
3032
3609
  * host targets a viewer-controlled Space (never the app author's home
3033
3610
  * Space) — use `requestSpace` when the app needs to know which Space.
3034
3611
  * Silent when a grant already covers them; `alwaysAsk` forces the dialog.
3035
3612
  */
3036
3613
  request: (input) => this.appRuntime.requestAuthorization(input),
3037
- /** One consent: the viewer picks a Space and grants the scopes on it. `alwaysAsk` re-opens the picker. */
3614
+ /** @deprecated Use authorize({ target: { kind: "pick-space" }, scopes }). */
3038
3615
  requestSpace: (input) => this.appRuntime.requestSpaceAuthorization(input),
3039
3616
  /** One consent: create a viewer-owned Space and grant the scopes on it. `space` is `CreateSpaceInput`. */
3040
3617
  requestCreateSpace: (input) => this.appRuntime.requestCreateSpaceAuthorization(input)
@@ -3393,6 +3970,7 @@ function clearGrantedAppScopes(userUuid, appId, spaceId) {
3393
3970
  }
3394
3971
  //#endregion
3395
3972
  //#region src/app-bridge-core.ts
3973
+ var AppLoginRedirect = class extends Error {};
3396
3974
  function readTokenResponse(value) {
3397
3975
  if (!value || typeof value !== "object") return null;
3398
3976
  const token = value.token;
@@ -3572,18 +4150,95 @@ function createAppBridgeCore(config) {
3572
4150
  const authorizationContext = config.authorizationContext ?? (config.isBackground ? { surface: "background" } : { surface: "page" });
3573
4151
  const onStateChange = config.onStateChange;
3574
4152
  let appToken = null;
4153
+ let tokenViewer;
4154
+ function synchronizeViewer(viewer) {
4155
+ if (tokenViewer !== void 0 && tokenViewer !== viewer) {
4156
+ appToken = null;
4157
+ sessionViewerGrants.clear();
4158
+ authoritativeGrants.clear();
4159
+ }
4160
+ tokenViewer = viewer;
4161
+ }
3575
4162
  /** Consents made through this host, keyed by target space. */
3576
4163
  const sessionViewerGrants = /* @__PURE__ */ new Map();
3577
4164
  let activeInvocation;
3578
4165
  let contextChangeVersion = 0;
3579
4166
  const legacyRequestIds = /* @__PURE__ */ new Set();
4167
+ const authorizationRequests = /* @__PURE__ */ new Map();
4168
+ const authoritativeGrants = /* @__PURE__ */ new Map();
4169
+ function canJoin(pending, next, requestId) {
4170
+ const previous = authorizationRequests.get(pending.requestId);
4171
+ const current = authorizationRequests.get(requestId);
4172
+ return Boolean(previous) === Boolean(current) && previous?.fallback === current?.fallback && isSameConsent(pending, next);
4173
+ }
4174
+ let signInFlight = null;
4175
+ const pendingLoginKey = `cohub:app-login:${app.id}`;
4176
+ function rememberAuthorization(data) {
4177
+ if (data.createSpace) return;
4178
+ try {
4179
+ const structured = typeof data.requestId === "string" ? authorizationRequests.get(data.requestId) : void 0;
4180
+ sessionStorage.setItem(pendingLoginKey, JSON.stringify({
4181
+ at: Date.now(),
4182
+ data: {
4183
+ ...structured ? {
4184
+ ...structured,
4185
+ type: "cohub.app.authorize.v2"
4186
+ } : { type: data.type },
4187
+ requestId: data.requestId,
4188
+ scopes: data.scopes,
4189
+ reason: sanitizeReason(data.reason),
4190
+ spaceId: data.spaceId,
4191
+ selectSpace: data.selectSpace,
4192
+ alwaysAsk: data.alwaysAsk
4193
+ }
4194
+ }));
4195
+ } catch {}
4196
+ }
4197
+ function clearAuthorizationIntent(requestId) {
4198
+ try {
4199
+ const raw = sessionStorage.getItem(pendingLoginKey);
4200
+ if (!raw) return;
4201
+ const saved = JSON.parse(raw);
4202
+ if (isRecord(saved) && isRecord(saved.data) && saved.data.requestId === requestId) sessionStorage.removeItem(pendingLoginKey);
4203
+ } catch {}
4204
+ }
4205
+ async function resumeAuthorization() {
4206
+ if (state.pendingAuth || !await getViewerUuid()) return;
4207
+ try {
4208
+ const raw = sessionStorage.getItem(pendingLoginKey);
4209
+ if (!raw) return;
4210
+ const saved = JSON.parse(raw);
4211
+ if (!isRecord(saved) || typeof saved.at !== "number" || Date.now() - saved.at > 6e5 || !isRecord(saved.data)) {
4212
+ sessionStorage.removeItem(pendingLoginKey);
4213
+ return;
4214
+ }
4215
+ await handleMessage({ data: saved.data });
4216
+ } catch {}
4217
+ }
4218
+ async function startSignIn() {
4219
+ if (state.pendingAuth) rememberAuthorization({
4220
+ ...state.pendingAuth,
4221
+ type: "cohub.app.authorize"
4222
+ });
4223
+ signInFlight ??= config.requestSignIn(typeof location !== "undefined" ? location.pathname + location.search + location.hash : "/").catch((error) => {
4224
+ signInFlight = null;
4225
+ throw error;
4226
+ });
4227
+ await signInFlight;
4228
+ }
3580
4229
  async function getContext() {
3581
4230
  const invocation = activeInvocation !== void 0 ? activeInvocation : config.getInvocation?.() ?? config.invocation;
3582
4231
  const shell = config.getShell?.() ?? config.shell;
3583
4232
  const appScopes = clonePermissionScopes(app.appScopes);
3584
4233
  const viewerUuid = await getViewerUuid();
4234
+ synchronizeViewer(viewerUuid);
3585
4235
  const viewerGrants = mergeViewerGrants(viewerUuid, Array.from(sessionViewerGrants.values()));
3586
4236
  return {
4237
+ capabilities: {
4238
+ authorization: 2,
4239
+ serverGrants: true
4240
+ },
4241
+ mode: authorizationContext.surface === "broker" ? "broker" : "bridge",
3587
4242
  app: {
3588
4243
  id: app.id,
3589
4244
  slug: app.slug,
@@ -3625,13 +4280,63 @@ function createAppBridgeCore(config) {
3625
4280
  };
3626
4281
  }
3627
4282
  function replyForRequest(requestId, payload, complete = false) {
4283
+ const request = authorizationRequests.get(requestId);
4284
+ if (request && payload.type === "cohub.app.error" && [
4285
+ "space_inaccessible",
4286
+ "scope_not_held",
4287
+ "app_not_accessible",
4288
+ "consent_required"
4289
+ ].includes(String(payload.code))) payload = {
4290
+ ...payload,
4291
+ type: "cohub.app.authorize.result",
4292
+ token: null,
4293
+ result: {
4294
+ status: "denied",
4295
+ code: payload.code
4296
+ }
4297
+ };
4298
+ if (request && payload.type === "cohub.app.authorize.result" && !payload.result) {
4299
+ const space = payload.space;
4300
+ const grant = space ? authoritativeGrants.get(space.id) : void 0;
4301
+ let result = { status: "cancelled" };
4302
+ if (payload.token && space && grant) result = {
4303
+ status: "granted",
4304
+ requestedTarget: request.target,
4305
+ target: request.target.kind === "account" ? { kind: "account" } : {
4306
+ kind: "space",
4307
+ spaceId: space.id,
4308
+ name: space.name
4309
+ },
4310
+ resolution: request.target.kind === "pick-space" ? "selected" : request.target.kind === "space" && request.target.spaceId !== space.id ? "fallback" : "requested",
4311
+ grant
4312
+ };
4313
+ else if (payload.token) payload = {
4314
+ type: "cohub.app.error",
4315
+ code: "invalid_response",
4316
+ message: "Missing authoritative grant."
4317
+ };
4318
+ if (payload.type !== "cohub.app.error") payload = {
4319
+ ...payload,
4320
+ result
4321
+ };
4322
+ }
4323
+ if (complete && (payload.type === "cohub.app.authorize.result" || payload.type === "cohub.app.error" && [
4324
+ "invalid_request",
4325
+ "space_inaccessible",
4326
+ "scope_not_held",
4327
+ "app_not_accessible",
4328
+ "consent_required"
4329
+ ].includes(String(payload.code)))) clearAuthorizationIntent(requestId);
3628
4330
  const namespace = legacyRequestIds.has(requestId) ? "work" : "app";
3629
4331
  const type = payload.type;
3630
4332
  reply(requestId, {
3631
4333
  ...payload,
3632
4334
  ...typeof type === "string" && type.startsWith("cohub.app.") ? { type: type.replace("cohub.app.", `cohub.${namespace}.`) } : {}
3633
4335
  });
3634
- if (complete) legacyRequestIds.delete(requestId);
4336
+ if (complete) {
4337
+ legacyRequestIds.delete(requestId);
4338
+ authorizationRequests.delete(requestId);
4339
+ }
3635
4340
  }
3636
4341
  function toLegacyWorkContext(context) {
3637
4342
  const permissions = context.permissions ? {
@@ -3710,12 +4415,11 @@ function createAppBridgeCore(config) {
3710
4415
  return authorizationContext.surface === "background" || authorizationContext.surface === "app" || authorizationContext.surface === "overlay";
3711
4416
  }
3712
4417
  async function ensureBaseToken(forceRefresh = false) {
4418
+ const viewer = await getViewerUuid();
4419
+ synchronizeViewer(viewer);
3713
4420
  if (appToken && !forceRefresh) return appToken;
3714
4421
  const userToken = await getAccessToken({ forceRefresh });
3715
- if (!userToken) {
3716
- await config.requestSignIn(typeof location !== "undefined" ? location.pathname : "/");
3717
- return null;
3718
- }
4422
+ if (!userToken) return null;
3719
4423
  const response = await fetch(`${apiOrigin}/api/apps/${app.id}/session`, {
3720
4424
  method: "POST",
3721
4425
  headers: { Authorization: `Bearer ${userToken}` }
@@ -3723,6 +4427,7 @@ function createAppBridgeCore(config) {
3723
4427
  if (!response.ok) throw new Error("Failed to create app session.");
3724
4428
  const token = readTokenResponse(await response.json());
3725
4429
  if (!token) throw new Error("Invalid app session response.");
4430
+ if (viewer !== await getViewerUuid()) return null;
3726
4431
  appToken = token;
3727
4432
  return appToken;
3728
4433
  }
@@ -3733,11 +4438,14 @@ function createAppBridgeCore(config) {
3733
4438
  * fresh dialog.
3734
4439
  */
3735
4440
  async function authorize(scopes, spaceId, options) {
4441
+ const incremental = Boolean(state.pendingAuth && authorizationRequests.has(state.pendingAuth.requestId));
3736
4442
  const userToken = await getAccessToken(options?.forceRefreshToken ? { forceRefresh: true } : void 0);
3737
4443
  if (!userToken) {
3738
- await config.requestSignIn(typeof location !== "undefined" ? location.pathname : "/");
3739
- throw new Error("Sign in is required to authorize this app.");
4444
+ if (mintedSpace) throw new AppAuthorizationError("Sign in to finish authorization.", 401, "login_required");
4445
+ await startSignIn();
4446
+ throw new AppLoginRedirect();
3740
4447
  }
4448
+ const authorizingViewer = await getViewerUuid();
3741
4449
  const response = await fetch(`${apiOrigin}/api/apps/${app.id}/authorize`, {
3742
4450
  method: "POST",
3743
4451
  signal: timeoutSignal(AUTHORIZE_TIMEOUT_MS),
@@ -3748,13 +4456,18 @@ function createAppBridgeCore(config) {
3748
4456
  body: JSON.stringify({
3749
4457
  scopes,
3750
4458
  ...spaceId ? { spaceId } : {},
3751
- ...options?.silent ? { silent: true } : {}
4459
+ ...options?.silent ? { silent: true } : {},
4460
+ ...incremental ? { scopeMode: "extend" } : {}
3752
4461
  })
3753
4462
  });
3754
4463
  const payload = await response.json().catch(() => null);
3755
4464
  if (!response.ok) throw new AppAuthorizationError(typeof payload?.message === "string" ? payload.message : "Authorization failed.", response.status, typeof payload?.code === "string" ? payload.code : void 0);
4465
+ if (authorizingViewer !== await getViewerUuid()) throw new AppAuthorizationError("The signed-in account changed. Please try again.", 409, "session_changed");
4466
+ synchronizeViewer(authorizingViewer);
3756
4467
  const token = readTokenResponse(payload);
3757
4468
  if (!token) throw new Error("Invalid app authorization response.");
4469
+ const parsedGrant = appAuthorizationGrantSchema.safeParse(payload?.grant);
4470
+ if (parsedGrant.success) authoritativeGrants.set(parsedGrant.data.spaceId, parsedGrant.data);
3758
4471
  const canonicalSpaceId = typeof payload?.grant?.spaceId === "string" && payload.grant.spaceId ? payload.grant.spaceId : spaceId ?? app.spaceId;
3759
4472
  const grantedScopes = sanitizeRequestedScopes(payload?.grant?.scopes);
3760
4473
  const canonicalScopes = grantedScopes.length > 0 ? grantedScopes : clonePermissionScopes(scopes);
@@ -3836,7 +4549,10 @@ function createAppBridgeCore(config) {
3836
4549
  async function listViewerSpaces() {
3837
4550
  const request = async (forceRefresh = false) => {
3838
4551
  const userToken = await getAccessToken({ forceRefresh });
3839
- if (!userToken) return null;
4552
+ if (!userToken) {
4553
+ await startSignIn();
4554
+ throw new AppLoginRedirect();
4555
+ }
3840
4556
  return fetch(`${apiOrigin}/api/spaces`, {
3841
4557
  headers: { Authorization: `Bearer ${userToken}` },
3842
4558
  signal: spaceListSignal()
@@ -3852,7 +4568,8 @@ function createAppBridgeCore(config) {
3852
4568
  const option = toSpaceOption(space);
3853
4569
  return option ? [option] : [];
3854
4570
  });
3855
- } catch {
4571
+ } catch (error) {
4572
+ if (error instanceof AppLoginRedirect) throw error;
3856
4573
  return null;
3857
4574
  }
3858
4575
  }
@@ -4006,8 +4723,38 @@ function createAppBridgeCore(config) {
4006
4723
  return (await response.json()).checkout ?? null;
4007
4724
  }
4008
4725
  async function handleMessage(event) {
4009
- const data = event.data;
4726
+ let data = event.data;
4010
4727
  if (!data?.requestId) return;
4728
+ if (data.type === "cohub.app.authorize.v2") {
4729
+ const parsed = appAuthorizationRequestSchema.safeParse(event.data);
4730
+ if (!parsed.success || parsed.data.scopes.some((scope) => !PERMISSIONS.includes(scope))) {
4731
+ replyForRequest(data.requestId, {
4732
+ type: "cohub.app.error",
4733
+ code: "invalid_request",
4734
+ message: "Invalid authorization request."
4735
+ }, true);
4736
+ return;
4737
+ }
4738
+ const input = parsed.data;
4739
+ if (input.target.kind === "account" && input.scopes.some((scope) => !isUserLevelPermission(scope))) {
4740
+ replyForRequest(data.requestId, {
4741
+ type: "cohub.app.error",
4742
+ code: "invalid_request",
4743
+ message: "Space permissions require a Space target."
4744
+ }, true);
4745
+ return;
4746
+ }
4747
+ authorizationRequests.set(data.requestId, input);
4748
+ data = {
4749
+ requestId: data.requestId,
4750
+ ...input,
4751
+ type: "cohub.app.authorize",
4752
+ scopes: input.scopes,
4753
+ spaceId: input.target.kind === "space" ? input.target.spaceId : void 0,
4754
+ selectSpace: input.target.kind === "pick-space"
4755
+ };
4756
+ }
4757
+ if (!data.requestId) return;
4011
4758
  const isLegacyWork = data.type?.startsWith("cohub.work.") === true;
4012
4759
  if (isLegacyWork) legacyRequestIds.add(data.requestId);
4013
4760
  let reservedAuth = null;
@@ -4018,6 +4765,7 @@ function createAppBridgeCore(config) {
4018
4765
  type: "cohub.app.context.result",
4019
4766
  context: isLegacyWork ? toLegacyWorkContext(context) : context
4020
4767
  }, true);
4768
+ await resumeAuthorization();
4021
4769
  }
4022
4770
  if (data.type === "cohub.app.token" || data.type === "cohub.work.token") {
4023
4771
  const token = await ensureBaseToken(Boolean(data.forceRefresh));
@@ -4125,18 +4873,22 @@ function createAppBridgeCore(config) {
4125
4873
  return;
4126
4874
  }
4127
4875
  if (state.pendingAuth) {
4128
- if (isSameConsent(state.pendingAuth, {
4876
+ if (canJoin(state.pendingAuth, {
4129
4877
  scopes,
4130
4878
  spaceId,
4131
4879
  selectSpace,
4132
4880
  createSpace
4133
- })) {
4881
+ }, data.requestId)) {
4134
4882
  state.pendingAuth.joinedRequestIds = [...state.pendingAuth.joinedRequestIds ?? [], data.requestId];
4135
4883
  return;
4136
4884
  }
4137
4885
  dismissPendingAuth();
4138
4886
  }
4139
4887
  if (createSpace) {
4888
+ if (!await getViewerUuid()) {
4889
+ await startSignIn();
4890
+ return;
4891
+ }
4140
4892
  mintedSpace = null;
4141
4893
  const pendingCreate = {
4142
4894
  requestId: data.requestId,
@@ -4154,7 +4906,7 @@ function createAppBridgeCore(config) {
4154
4906
  notify();
4155
4907
  return;
4156
4908
  }
4157
- if (!selectSpace && !alwaysAsk && allowsOwnerAutoAuthorization() && await isCurrentViewerAppOwner()) try {
4909
+ if (!selectSpace && !authorizationRequests.has(data.requestId) && !alwaysAsk && allowsOwnerAutoAuthorization() && await isCurrentViewerAppOwner()) try {
4158
4910
  const result = await authorizeSilent(scopes, spaceId);
4159
4911
  replyForRequest(data.requestId, authorizeResult(result.token, result.spaceId, result.spaceId === app.spaceId ? app.spaceName ?? null : null), true);
4160
4912
  return;
@@ -4168,12 +4920,12 @@ function createAppBridgeCore(config) {
4168
4920
  }
4169
4921
  }
4170
4922
  if (state.pendingAuth) {
4171
- if (isSameConsent(state.pendingAuth, {
4923
+ if (canJoin(state.pendingAuth, {
4172
4924
  scopes,
4173
4925
  spaceId,
4174
4926
  selectSpace,
4175
4927
  createSpace
4176
- })) {
4928
+ }, data.requestId)) {
4177
4929
  state.pendingAuth.joinedRequestIds = [...state.pendingAuth.joinedRequestIds ?? [], data.requestId];
4178
4930
  return;
4179
4931
  }
@@ -4194,6 +4946,11 @@ function createAppBridgeCore(config) {
4194
4946
  state.authError = null;
4195
4947
  const viewerUuid = await getViewerUuid();
4196
4948
  if (state.pendingAuth !== reserved) return;
4949
+ if (!viewerUuid) {
4950
+ rememberAuthorization(data);
4951
+ await startSignIn();
4952
+ return;
4953
+ }
4197
4954
  const spaceLevel = Boolean(spaceId) || selectSpace || scopes.some((scope) => !isUserLevelPermission(scope));
4198
4955
  const lastPicked = readLastPickedSpace();
4199
4956
  const contextSpaceId = resolveContextSpaceId();
@@ -4208,7 +4965,26 @@ function createAppBridgeCore(config) {
4208
4965
  if (fallback) spaces = [fallback];
4209
4966
  }
4210
4967
  const candidates = spaces ?? null;
4968
+ if (spaceLevel && spaces === null) {
4969
+ reserved.spaces = null;
4970
+ state.authError = "Couldn't load your Spaces. Please try again.";
4971
+ state.authOpen = true;
4972
+ state.canChangeSpace = false;
4973
+ notify();
4974
+ return;
4975
+ }
4211
4976
  const explicitAccessible = Boolean(spaceId) && candidates?.some((space) => space.id === spaceId) === true;
4977
+ const structuredRequest = authorizationRequests.get(data.requestId);
4978
+ if (spaceId && candidates && !explicitAccessible && structuredRequest?.fallback === "none") {
4979
+ replyPendingAuth(reserved, {
4980
+ type: "cohub.app.error",
4981
+ code: "space_inaccessible",
4982
+ message: "The requested Space is unavailable."
4983
+ });
4984
+ resetDialogState();
4985
+ safeNotify();
4986
+ return;
4987
+ }
4212
4988
  const defaultSpaceId = explicitAccessible ? spaceId : resolveDefaultSpaceId(candidates);
4213
4989
  const silentTargets = (!spaceLevel ? [] : [selectSpace ? lastPicked && candidates?.some((space) => space.id === lastPicked) ? lastPicked : null : explicitAccessible ? spaceId : spaceId === void 0 ? defaultSpaceId : null].filter((target) => Boolean(target) && hasGrantedAppScopes(viewerUuid, app.id, scopes, target ?? void 0))).slice(0, MAX_SILENT_SPACE_ATTEMPTS);
4214
4990
  if (!alwaysAsk && await attemptSilentRenewal(reserved, viewerUuid, scopes, silentTargets)) return;
@@ -4230,11 +5006,19 @@ function createAppBridgeCore(config) {
4230
5006
  notify();
4231
5007
  }
4232
5008
  } catch (error) {
5009
+ if (error instanceof AppLoginRedirect) {
5010
+ rememberAuthorization(data);
5011
+ resetDialogState();
5012
+ safeNotify();
5013
+ return;
5014
+ }
4233
5015
  const message = error instanceof Error ? error.message : "Request failed.";
5016
+ const code = error instanceof AppAuthorizationError ? error.code : "request_failed";
4234
5017
  if (reservedAuth && state.pendingAuth === reservedAuth) {
4235
5018
  replyPendingAuth(reservedAuth, {
4236
5019
  type: "cohub.app.error",
4237
- message
5020
+ message,
5021
+ code
4238
5022
  });
4239
5023
  resetDialogState();
4240
5024
  safeNotify();
@@ -4242,7 +5026,8 @@ function createAppBridgeCore(config) {
4242
5026
  }
4243
5027
  replyForRequest(data.requestId, {
4244
5028
  type: "cohub.app.error",
4245
- message
5029
+ message,
5030
+ code
4246
5031
  }, true);
4247
5032
  }
4248
5033
  }
@@ -4262,7 +5047,14 @@ function createAppBridgeCore(config) {
4262
5047
  if (!state.pendingAuth) return;
4263
5048
  replyPendingAuth(state.pendingAuth, {
4264
5049
  type: "cohub.app.authorize.result",
4265
- token: null
5050
+ token: null,
5051
+ ...mintedSpace ? {
5052
+ space: {
5053
+ id: mintedSpace.id,
5054
+ name: mintedSpace.name
5055
+ },
5056
+ stage: mintedSpace.provisioned ? "authorization" : "bootstrap"
5057
+ } : {}
4266
5058
  });
4267
5059
  resetDialogState();
4268
5060
  mintedSpace = null;
@@ -4323,6 +5115,11 @@ function createAppBridgeCore(config) {
4323
5115
  if (!state.pendingAuth || state.authSaving) return;
4324
5116
  const pending = state.pendingAuth;
4325
5117
  let requestedSpaceId = (state.canChangeSpace ? pickedSpaceId : void 0) ?? state.selectedSpaceId ?? void 0;
5118
+ if (pending.spaces === null) {
5119
+ state.authError = "Couldn't load your Spaces. Please try again.";
5120
+ notify();
5121
+ return;
5122
+ }
4326
5123
  if (pending.spaces !== void 0) {
4327
5124
  const candidates = pending.spaces;
4328
5125
  if (!(requestedSpaceId !== void 0 && candidates?.some((space) => space.id === requestedSpaceId) === true)) {
@@ -4360,6 +5157,13 @@ function createAppBridgeCore(config) {
4360
5157
  resetDialogState();
4361
5158
  mintedSpace = null;
4362
5159
  } catch (error) {
5160
+ if (error instanceof AppLoginRedirect) return;
5161
+ if (mintedSpace && error instanceof AppAuthorizationError && error.code === "login_required") {
5162
+ replyPendingAuth(pending, authorizeResult(null, mintedSpace.id, mintedSpace.name));
5163
+ resetDialogState();
5164
+ mintedSpace = null;
5165
+ return;
5166
+ }
4363
5167
  if (error instanceof AppAuthorizationError) emitDiagnostic({
4364
5168
  code: error.code ?? "authorize_failed",
4365
5169
  message: error.message,
@@ -4626,4 +5430,4 @@ function createBoardExtensionRegistry() {
4626
5430
  }
4627
5431
  const BOARD_CHANNELS = BOARD_ANIMATION_CHANNELS;
4628
5432
  //#endregion
4629
- export { APP_COMPOSER_CHIP_CONTENT_MAX_BYTES, APP_COMPOSER_CHIP_KEY_MAX_LENGTH, APP_COMPOSER_CHIP_LABEL_MAX_LENGTH, APP_SURFACE_READY_TIMEOUT_MS, APP_SURFACE_REQUEST_TIMEOUT_MS, AppCommerceApi, AppRealtimeApi, AppRefParseError, AppRoom, AppRuntimeApi, AppRuntimeApi as WorkRuntimeApi, AppSurfaceApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BillingApi, BoardAnimationSpecSchema, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardDealParamsSchema, BoardEffectInputSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, BoardPlaybackPolicySchema, BoardSemanticCommandSchema, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, DESKTOP_COMMAND_MAX_TIMEOUT_MS, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, DESKTOP_COMMAND_PENDING_TTL_SECONDS, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, DESKTOP_COMMAND_VERSION, DesktopCommandsApi, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, PERMISSIONS, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, SpacePublicFilesApi, SpaceWebhooksApi, USER_LEVEL_PERMISSIONS, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, assertGenerationRequestAllowedByPolicy, attachAppEmbed, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, clearGrantedAppScopes, compileComposition, composition, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, filterSpacePickerItems, findGenerationModelPolicy, formatAppRef, formatWorkRef, getAllowedGenerationModelIds, getCohubContext, hasGrantedAppScopes, hasRequestSourceIdentity, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isUserLevelPermission, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeSpacePickerQuery, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, orderSpacePickerItems, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardEffectInput, parseBoardPlaybackPolicy, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseWorkRef, proceduralClip, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveExecutionAppId, resolveExecutionToken, resolveOpenSurface, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, scopeListHasPermission, selectSpacePickerItems, setGrantedAppScopes, track };
5433
+ export { APP_COMPOSER_CHIP_CONTENT_MAX_BYTES, APP_COMPOSER_CHIP_KEY_MAX_LENGTH, APP_COMPOSER_CHIP_LABEL_MAX_LENGTH, APP_SURFACE_READY_TIMEOUT_MS, APP_SURFACE_REQUEST_TIMEOUT_MS, AppCommerceApi, AppRealtimeApi, AppRefParseError, AppRoom, AppRuntimeApi, AppRuntimeApi as WorkRuntimeApi, AppRuntimeError, AppSurfaceApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BillingApi, BoardAnimationSpecSchema, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardDealParamsSchema, BoardEffectInputSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, BoardPlaybackPolicySchema, BoardSemanticCommandSchema, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, DESKTOP_COMMAND_MAX_TIMEOUT_MS, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, DESKTOP_COMMAND_PENDING_TTL_SECONDS, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, DESKTOP_COMMAND_VERSION, DesktopCommandsApi, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, PERMISSIONS, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, RUNTIME_ARCHIVE_SEGMENT_BYTES, RUNTIME_MAX_FRAME_BYTES, RUNTIME_PROTOCOL_VERSION, RUNTIME_RECOVERY_BATCH_SIZE, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, SpacePublicFilesApi, SpaceWebhooksApi, USER_LEVEL_PERMISSIONS, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, assertGenerationRequestAllowedByPolicy, attachAppEmbed, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, clearGrantedAppScopes, compileComposition, composition, contextToPiMessages, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, filterSpacePickerItems, findGenerationModelPolicy, formatAppRef, formatWorkRef, getAllowedGenerationModelIds, getCohubContext, harnessArchiveIndexSchema, hasGrantedAppScopes, hasRequestSourceIdentity, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isLocalHarness, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isUserLevelPermission, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeSpacePickerQuery, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, orderSpacePickerItems, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardEffectInput, parseBoardPlaybackPolicy, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseWorkRef, proceduralClip, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveExecutionAppId, resolveExecutionToken, resolveHarness, resolveOpenSurface, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, runtimeCommandSchema, runtimeEventSchema, runtimeReadySchema, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, scopeListHasPermission, selectRuntimeContextMessages, selectSpacePickerItems, setGrantedAppScopes, track, validateArchiveBoundary };