@neta-art/cohub 5.2.0 → 5.3.1

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,5 +1,5 @@
1
- import { A as CronJobsApi, C as ReferencesApi, D as PromptsApi, E as SkillsApi, O as ModelsApi, S as SessionAccessApi, T as PublicAssetsApi, _ as createSessionGenerationStreamClient, a as ReferralsApi, b as createSessionPatchReducer, c as TasksApi, d as SpaceClient, f as SpacesApi, g as SessionGenerationStreamClient, h as buildSpacePath, i as WorksApi, j as ChannelsApi, k as GenerationsApi, l as BoardClient, m as buildSpaceInvitePath, n as createHttpClient, o as UsersApi, p as PublicInviteApi, r as WorkCommerceApi, s as UserApi, t as CohubHttpClient, u as BoardTransactionError, v as parseAssistantMessageCommit, w as SearchApi, x as ensureRealtimeConnected, y as SessionPatchReducer } from "./chunks/http.js";
2
- import { _ as resolveRequestSourceChannel, a as sanitizeAccessToken, c as REQUEST_SOURCE_VIA_MAX_LENGTH, d as isRequestSourceUuid, f as mergeRequestSourceIntoMeta, g as requestSourceToHeaders, h as readRequestSourceFromEnv, i as matchesUnauthorizedErrorToken, l as hasRequestSourceIdentity, m as parseRequestSourceFromHeaders, n as HttpTransport, o as COHUB_SOURCE_HEADER, p as normalizeRequestSource, r as joinApiUrl, s as COHUB_SOURCE_HEADER_NAMES, t as HttpError, u as isRequestSourceEmpty, v as REALTIME_ROOM_EVENT_NAME_PATTERN } from "./chunks/transport.js";
1
+ import { A as parseAssistantMessageCommit, B as ModelsApi, C as SpaceClient, D as buildSpacePath, E as buildSpaceInvitePath, F as ReferencesApi, H as CronJobsApi, I as SearchApi, L as PublicAssetsApi, M as createSessionPatchReducer, N as ensureRealtimeConnected, O as SessionGenerationStreamClient, P as SessionAccessApi, R as SkillsApi, S as BoardTransactionError, T as PublicInviteApi, U as ChannelsApi, V as GenerationsApi, _ as isUiSurfaceMethod, a as ReferralsApi, b as TasksApi, c as UiCommandsApi, d as UI_COMMAND_PAYLOAD_MAX_BYTES, f as UI_COMMAND_PENDING_TTL_SECONDS, g as isTerminalUiCommandStatus, h as UI_COMMAND_VERSION, i as WorksApi, j as SessionPatchReducer, k as createSessionGenerationStreamClient, l as UI_COMMAND_DEFAULT_TIMEOUT_MS, m as UI_COMMAND_TERMINAL_TTL_SECONDS, n as createHttpClient, o as UsersApi, p as UI_COMMAND_SETTLEMENT_GRACE_SECONDS, r as WorkCommerceApi, s as UserApi, t as CohubHttpClient, u as UI_COMMAND_MAX_TIMEOUT_MS, v as parseUiCommand, w as SpacesApi, x as BoardClient, y as parseUiCommandId, z as PromptsApi } from "./chunks/http.js";
2
+ import { _ as requestSourceToHeaders, a as sanitizeAccessToken, 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 REALTIME_ROOM_EVENT_NAME_PATTERN } from "./chunks/transport.js";
3
3
  import { a as resolveApiBaseUrl, c as resolveWebsocketUrl, i as normalizeWebsocketUrl, n as normalizeBaseUrl, o as resolveCohubEnvironment, r as normalizeVoiceInputWebsocketUrl, s as resolveVoiceInputWebsocketUrl, t as COHUB_ENVIRONMENTS } 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";
@@ -389,7 +389,8 @@ z.object({
389
389
  "session",
390
390
  "space",
391
391
  "label",
392
- "room"
392
+ "room",
393
+ "ui"
393
394
  ]),
394
395
  type: z.string(),
395
396
  requestId: z.string().nullable().optional(),
@@ -843,6 +844,257 @@ var WorkRealtimeApi = class {
843
844
  }
844
845
  };
845
846
  //#endregion
847
+ //#region ../protocol/dist/work-surface.js
848
+ const WORK_SURFACE_PROTOCOL = "cohub.surface";
849
+ const WORK_SURFACE_READY_TIMEOUT_MS = 1e4;
850
+ const WORK_SURFACE_REQUEST_TIMEOUT_MS = 15e3;
851
+ const WORK_COMPOSER_CHIP_KEY_MAX_LENGTH = 80;
852
+ const WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH = 120;
853
+ const WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES = 32 * 1024;
854
+ const isRecord$1 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
855
+ const isSurfaceEnvelope = (value) => isRecord$1(value) && value.protocol === "cohub.surface" && value.version === 1;
856
+ const parseWorkSurfaceReady = (value) => {
857
+ if (!isSurfaceEnvelope(value) || value.type !== "ready") return null;
858
+ const methods = Array.isArray(value.methods) ? value.methods.filter((method) => typeof method === "string" && Boolean(method)) : [];
859
+ return {
860
+ protocol: WORK_SURFACE_PROTOCOL,
861
+ version: 1,
862
+ type: "ready",
863
+ methods
864
+ };
865
+ };
866
+ const parseWorkSurfaceResponse = (value) => {
867
+ if (!isSurfaceEnvelope(value) || value.type !== "response") return null;
868
+ if (typeof value.requestId !== "string" || !value.requestId) return null;
869
+ const error = isRecord$1(value.error) ? {
870
+ code: typeof value.error.code === "string" && value.error.code ? value.error.code : "surface_error",
871
+ message: typeof value.error.message === "string" ? value.error.message : "Work surface call failed"
872
+ } : void 0;
873
+ return {
874
+ protocol: WORK_SURFACE_PROTOCOL,
875
+ version: 1,
876
+ type: "response",
877
+ requestId: value.requestId,
878
+ ok: value.ok === true,
879
+ ...error ? { error } : {}
880
+ };
881
+ };
882
+ const parseComposerChipKey = (value) => {
883
+ if (typeof value !== "string") return null;
884
+ const key = value.trim();
885
+ if (!key || key.length > 80) return null;
886
+ return key;
887
+ };
888
+ const parseWorkComposerChipSet = (value) => {
889
+ if (!isSurfaceEnvelope(value) || value.type !== "composer.chip.set" || !isRecord$1(value.chip)) return null;
890
+ const key = parseComposerChipKey(value.chip.key);
891
+ if (!key || typeof value.chip.label !== "string" || typeof value.chip.content !== "string") return null;
892
+ const label = value.chip.label.trim();
893
+ if (!label || label.length > 120) return null;
894
+ if (!value.chip.content.trim()) return null;
895
+ if (new TextEncoder().encode(value.chip.content).length > 32768) return null;
896
+ return {
897
+ protocol: WORK_SURFACE_PROTOCOL,
898
+ version: 1,
899
+ type: "composer.chip.set",
900
+ chip: {
901
+ key,
902
+ label,
903
+ content: value.chip.content
904
+ }
905
+ };
906
+ };
907
+ const parseWorkComposerChipClear = (value) => {
908
+ if (!isSurfaceEnvelope(value) || value.type !== "composer.chip.clear") return null;
909
+ const key = parseComposerChipKey(value.key);
910
+ return key ? {
911
+ protocol: WORK_SURFACE_PROTOCOL,
912
+ version: 1,
913
+ type: "composer.chip.clear",
914
+ key
915
+ } : null;
916
+ };
917
+ const parseWorkSurfaceRequest = (value) => {
918
+ if (!isSurfaceEnvelope(value) || value.type !== "request") return null;
919
+ if (typeof value.requestId !== "string" || !value.requestId) return null;
920
+ if (typeof value.method !== "string" || !value.method) return null;
921
+ const commandId = parseUiCommandId(value.commandId);
922
+ if (!commandId) return null;
923
+ return {
924
+ protocol: WORK_SURFACE_PROTOCOL,
925
+ version: 1,
926
+ type: "request",
927
+ requestId: value.requestId,
928
+ method: value.method,
929
+ ...value.input === void 0 ? {} : { input: value.input },
930
+ commandId
931
+ };
932
+ };
933
+ const buildWorkSurfaceReady = (methods) => ({
934
+ protocol: WORK_SURFACE_PROTOCOL,
935
+ version: 1,
936
+ type: "ready",
937
+ methods: [...methods]
938
+ });
939
+ const buildWorkSurfaceRequest = (input) => ({
940
+ protocol: WORK_SURFACE_PROTOCOL,
941
+ version: 1,
942
+ type: "request",
943
+ ...input
944
+ });
945
+ const buildWorkSurfaceResponse = (input) => ({
946
+ protocol: WORK_SURFACE_PROTOCOL,
947
+ version: 1,
948
+ type: "response",
949
+ ...input
950
+ });
951
+ const buildWorkComposerChipSet = (chip) => ({
952
+ protocol: WORK_SURFACE_PROTOCOL,
953
+ version: 1,
954
+ type: "composer.chip.set",
955
+ chip
956
+ });
957
+ const buildWorkComposerChipClear = (key) => ({
958
+ protocol: WORK_SURFACE_PROTOCOL,
959
+ version: 1,
960
+ type: "composer.chip.clear",
961
+ key
962
+ });
963
+ //#endregion
964
+ //#region src/work-surface.ts
965
+ /**
966
+ * Explicit app origins, not a `*.cohub.run` suffix match: Works themselves are
967
+ * served from Cohub subdomains, so a suffix match would let one Work call into
968
+ * another and turn a subdomain takeover into surface access.
969
+ */
970
+ const COHUB_APP_ORIGINS = [
971
+ "https://cohub.run",
972
+ "https://www.cohub.run",
973
+ "https://dev.cohub.run"
974
+ ];
975
+ const isCohubHostOrigin = (origin) => COHUB_APP_ORIGINS.includes(origin);
976
+ const resolveEmbedderOrigin = () => {
977
+ if (typeof window === "undefined" || window.parent === window) return null;
978
+ const ancestor = window.location?.ancestorOrigins?.[0];
979
+ if (typeof ancestor === "string" && ancestor) return ancestor;
980
+ try {
981
+ const referrer = typeof document === "undefined" ? "" : document.referrer;
982
+ return referrer ? new URL(referrer).origin : null;
983
+ } catch {
984
+ return null;
985
+ }
986
+ };
987
+ var WorkSurfaceApi = class {
988
+ handlers = /* @__PURE__ */ new Map();
989
+ listening = false;
990
+ allowedOrigins = null;
991
+ trustedOrigin;
992
+ allowHostOrigins(origins) {
993
+ this.allowedOrigins = origins.map((origin) => origin.trim()).filter(Boolean).map((origin) => {
994
+ try {
995
+ return new URL(origin).origin;
996
+ } catch {
997
+ return "";
998
+ }
999
+ }).filter(Boolean);
1000
+ this.trustedOrigin = void 0;
1001
+ this.announce();
1002
+ }
1003
+ handle(method, handler) {
1004
+ const name = method.trim();
1005
+ if (!name) throw new Error("Work surface method name is required");
1006
+ this.handlers.set(name, handler);
1007
+ this.start();
1008
+ this.announce();
1009
+ return () => {
1010
+ if (this.handlers.get(name) === handler) {
1011
+ this.handlers.delete(name);
1012
+ this.announce();
1013
+ }
1014
+ };
1015
+ }
1016
+ get methods() {
1017
+ return [...this.handlers.keys()];
1018
+ }
1019
+ setComposerChip(chip) {
1020
+ const message = parseWorkComposerChipSet(buildWorkComposerChipSet(chip));
1021
+ if (!message) throw new Error("Invalid Work composer chip");
1022
+ this.post(message);
1023
+ }
1024
+ clearComposerChip(key) {
1025
+ const message = parseWorkComposerChipClear(buildWorkComposerChipClear(key));
1026
+ if (!message) throw new Error("Invalid Work composer chip key");
1027
+ this.post(message);
1028
+ }
1029
+ announce() {
1030
+ this.post(buildWorkSurfaceReady(this.methods));
1031
+ }
1032
+ isTrusted(origin) {
1033
+ if (!origin || origin === "null") return false;
1034
+ if (typeof window !== "undefined" && origin === window.location?.origin) return true;
1035
+ return this.allowedOrigins ? this.allowedOrigins.includes(origin) : isCohubHostOrigin(origin);
1036
+ }
1037
+ resolveTrustedOrigin() {
1038
+ if (this.trustedOrigin !== void 0) return this.trustedOrigin;
1039
+ const embedder = resolveEmbedderOrigin();
1040
+ this.trustedOrigin = embedder && this.isTrusted(embedder) ? embedder : null;
1041
+ return this.trustedOrigin;
1042
+ }
1043
+ start() {
1044
+ if (this.listening || typeof window === "undefined") return;
1045
+ this.listening = true;
1046
+ window.addEventListener("message", this.onMessage);
1047
+ }
1048
+ onMessage = (event) => {
1049
+ if (typeof window === "undefined" || event.source !== window.parent) return;
1050
+ if (!this.isTrusted(event.origin)) return;
1051
+ const request = parseWorkSurfaceRequest(event.data);
1052
+ if (!request) return;
1053
+ const commandId = request.commandId;
1054
+ if (!commandId) return;
1055
+ this.trustedOrigin = event.origin;
1056
+ this.dispatch(request.requestId, request.method, request.input, commandId);
1057
+ };
1058
+ async dispatch(requestId, method, input, commandId) {
1059
+ const handler = this.handlers.get(method);
1060
+ if (!handler) {
1061
+ this.post(buildWorkSurfaceResponse({
1062
+ requestId,
1063
+ ok: false,
1064
+ error: {
1065
+ code: "method_not_found",
1066
+ message: `This Work does not expose "${method}".`
1067
+ }
1068
+ }));
1069
+ return;
1070
+ }
1071
+ try {
1072
+ await handler(input, { commandId });
1073
+ this.post(buildWorkSurfaceResponse({
1074
+ requestId,
1075
+ ok: true
1076
+ }));
1077
+ } catch (error) {
1078
+ this.post(buildWorkSurfaceResponse({
1079
+ requestId,
1080
+ ok: false,
1081
+ error: {
1082
+ code: "handler_failed",
1083
+ message: error instanceof Error ? error.message : String(error)
1084
+ }
1085
+ }));
1086
+ }
1087
+ }
1088
+ post(message) {
1089
+ if (typeof window === "undefined" || window.parent === window) return;
1090
+ const origin = this.resolveTrustedOrigin();
1091
+ if (!origin) return;
1092
+ try {
1093
+ window.parent.postMessage(message, origin);
1094
+ } catch {}
1095
+ }
1096
+ };
1097
+ //#endregion
846
1098
  //#region src/work-runtime.ts
847
1099
  const isBrowser$1 = () => typeof window !== "undefined" && typeof window.parent !== "undefined";
848
1100
  const hasParent = () => isBrowser$1() && window.parent !== window;
@@ -1240,6 +1492,7 @@ var CohubClient = class {
1240
1492
  references;
1241
1493
  tasks;
1242
1494
  cronJobs;
1495
+ ui;
1243
1496
  invite;
1244
1497
  referrals;
1245
1498
  voice;
@@ -1296,6 +1549,7 @@ var CohubClient = class {
1296
1549
  this.references = new ReferencesApi(this.transport);
1297
1550
  this.tasks = new TasksApi(this.transport);
1298
1551
  this.cronJobs = new CronJobsApi(this.transport);
1552
+ this.ui = new UiCommandsApi(this.transport);
1299
1553
  this.invite = new PublicInviteApi(this.transport);
1300
1554
  this.referrals = new ReferralsApi(this.transport);
1301
1555
  this.works = new WorksApi(this.transport);
@@ -1308,6 +1562,14 @@ var CohubClient = class {
1308
1562
  auth = { request: (input) => this.workRuntime.requestAuthorization(input) };
1309
1563
  work = {
1310
1564
  realtime: null,
1565
+ /** Expose callable methods from inside a published Work. */
1566
+ surface: new WorkSurfaceApi(),
1567
+ composer: {
1568
+ /** Attach or update context from this Work in the Cohub composer. */
1569
+ setChip: (chip) => this.work.surface.setComposerChip(chip),
1570
+ /** Remove context previously attached by this Work. */
1571
+ clearChip: (key) => this.work.surface.clearComposerChip(key)
1572
+ },
1311
1573
  commerce: {
1312
1574
  resolveProducts: async (input) => {
1313
1575
  const context = await this.workRuntime.context();
@@ -1393,6 +1655,140 @@ var CohubClient = class {
1393
1655
  };
1394
1656
  const createCohubClient = (options) => new CohubClient(options);
1395
1657
  //#endregion
1658
+ //#region ../protocol/dist/public-identifiers.js
1659
+ const USERNAME_PATTERN = /^(?!-)(?!.*--)[a-z0-9-]{1,39}(?<!-)$/;
1660
+ const SPACE_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9_-]{0,78}[a-z0-9])?$/;
1661
+ /**
1662
+ * Platform-owned path segments that must not be newly assigned to public
1663
+ * identities. Existing stored values remain readable through parse helpers.
1664
+ */
1665
+ const RESERVED_PLATFORM_PATH_SEGMENTS = Object.freeze([
1666
+ "admin",
1667
+ "api",
1668
+ "assets",
1669
+ "auth",
1670
+ "callback",
1671
+ "changelog",
1672
+ "docs",
1673
+ "explore",
1674
+ "invite",
1675
+ "login",
1676
+ "logout",
1677
+ "new",
1678
+ "org",
1679
+ "pricing",
1680
+ "pwa",
1681
+ "referrals",
1682
+ "sessions",
1683
+ "settings",
1684
+ "spaces",
1685
+ "static",
1686
+ "teams",
1687
+ "trending",
1688
+ "u",
1689
+ "user",
1690
+ "users",
1691
+ "work-auth"
1692
+ ]);
1693
+ new Set(RESERVED_PLATFORM_PATH_SEGMENTS);
1694
+ [...RESERVED_PLATFORM_PATH_SEGMENTS];
1695
+ function parseUsername(value) {
1696
+ if (value === null || value === void 0) return null;
1697
+ const normalized = value.trim().toLowerCase();
1698
+ return USERNAME_PATTERN.test(normalized) ? normalized : null;
1699
+ }
1700
+ function parseSpaceSlug(value) {
1701
+ if (value === null || value === void 0) return null;
1702
+ const normalized = value.trim();
1703
+ return SPACE_SLUG_PATTERN.test(normalized) ? normalized : null;
1704
+ }
1705
+ //#endregion
1706
+ //#region src/work-ref.ts
1707
+ /**
1708
+ * Accepts every way a Work is named across Cohub — id, management URL, public
1709
+ * URL, `cohub://works` URI, or `username/space/work` — and normalizes it.
1710
+ *
1711
+ * Public and mention forms may carry launch state (`?query#hash`); it is kept
1712
+ * separately so it can be forwarded to the Work while the stable identity stays
1713
+ * clean.
1714
+ */
1715
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1716
+ const isWorkId = (value) => UUID_PATTERN.test(value.trim());
1717
+ function decodePart(value) {
1718
+ try {
1719
+ return decodeURIComponent(value).trim();
1720
+ } catch {
1721
+ return "";
1722
+ }
1723
+ }
1724
+ function publicRef(parts) {
1725
+ if (parts.length !== 3) return null;
1726
+ const [usernameRaw = "", spaceSlugRaw = "", workSlugRaw = ""] = parts.map(decodePart);
1727
+ const username = parseUsername(usernameRaw);
1728
+ const spaceSlug = parseSpaceSlug(spaceSlugRaw);
1729
+ const workSlug = parseSpaceSlug(workSlugRaw);
1730
+ return username && spaceSlug && workSlug ? {
1731
+ username,
1732
+ spaceSlug,
1733
+ workSlug
1734
+ } : null;
1735
+ }
1736
+ function launchState(url) {
1737
+ return {
1738
+ ...url.search ? { search: url.search } : {},
1739
+ ...url.hash ? { hash: url.hash } : {}
1740
+ };
1741
+ }
1742
+ function parseUrlRef(value) {
1743
+ let url;
1744
+ try {
1745
+ url = new URL(value);
1746
+ } catch {
1747
+ return null;
1748
+ }
1749
+ const parts = url.pathname.split("/").filter(Boolean);
1750
+ if (url.protocol === "cohub:" && url.hostname === "works") {
1751
+ const ref = publicRef(parts);
1752
+ return ref ? {
1753
+ ...ref,
1754
+ ...launchState(url)
1755
+ } : null;
1756
+ }
1757
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
1758
+ if (parts.length === 4 && parts[0] === "spaces" && UUID_PATTERN.test(parts[1] ?? "") && parts[2] === "works" && UUID_PATTERN.test(parts[3] ?? "")) return { id: parts[3] };
1759
+ if (parts.length === 4 && parts[2] === "w") {
1760
+ const ref = publicRef([
1761
+ parts[0],
1762
+ parts[1],
1763
+ parts[3]
1764
+ ]);
1765
+ return ref ? {
1766
+ ...ref,
1767
+ ...launchState(url)
1768
+ } : null;
1769
+ }
1770
+ return null;
1771
+ }
1772
+ var WorkRefParseError = class extends Error {
1773
+ constructor() {
1774
+ super("Work must be an id, public URL, cohub://works URI, or username/space/work reference");
1775
+ this.name = "WorkRefParseError";
1776
+ }
1777
+ };
1778
+ function parseWorkRef(input) {
1779
+ const value = input.trim();
1780
+ if (UUID_PATTERN.test(value)) return { id: value };
1781
+ const parsedUrl = parseUrlRef(value.includes("://") ? value : value.startsWith("/") ? `https://cohub.invalid${value}` : value);
1782
+ if (parsedUrl) return parsedUrl;
1783
+ const parts = value.split("/").filter(Boolean);
1784
+ const parsedPublic = parts.length === 3 ? publicRef(parts) : null;
1785
+ if (parsedPublic) return parsedPublic;
1786
+ throw new WorkRefParseError();
1787
+ }
1788
+ function formatWorkRef(ref) {
1789
+ return "id" in ref ? ref.id : `${ref.username}/${ref.spaceSlug}/${ref.workSlug}`;
1790
+ }
1791
+ //#endregion
1396
1792
  //#region src/work-grant-cache.ts
1397
1793
  const STORAGE_PREFIX = "cohub:work-grants";
1398
1794
  const CACHE_VERSION = 1;
@@ -2246,42 +2642,6 @@ z.discriminatedUnion("type", [
2246
2642
  })
2247
2643
  ]);
2248
2644
  //#endregion
2249
- //#region ../protocol/dist/public-identifiers.js
2250
- /**
2251
- * Platform-owned path segments that must not be newly assigned to public
2252
- * identities. Existing stored values remain readable through parse helpers.
2253
- */
2254
- const RESERVED_PLATFORM_PATH_SEGMENTS = Object.freeze([
2255
- "admin",
2256
- "api",
2257
- "assets",
2258
- "auth",
2259
- "callback",
2260
- "changelog",
2261
- "docs",
2262
- "explore",
2263
- "invite",
2264
- "login",
2265
- "logout",
2266
- "new",
2267
- "org",
2268
- "pricing",
2269
- "pwa",
2270
- "referrals",
2271
- "sessions",
2272
- "settings",
2273
- "spaces",
2274
- "static",
2275
- "teams",
2276
- "trending",
2277
- "u",
2278
- "user",
2279
- "users",
2280
- "work-auth"
2281
- ]);
2282
- new Set(RESERVED_PLATFORM_PATH_SEGMENTS);
2283
- [...RESERVED_PLATFORM_PATH_SEGMENTS];
2284
- //#endregion
2285
2645
  //#region src/board/animation.ts
2286
2646
  const ZERO_COST = {
2287
2647
  particles: 0,
@@ -2633,4 +2993,4 @@ function createBoardExtensionRegistry(input = {}) {
2633
2993
  return registry;
2634
2994
  }
2635
2995
  //#endregion
2636
- export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BillingApi, BoardClient, BoardExtensionRegistry, BoardPlaybackPolicySchema, BoardTransactionError, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRoom, WorkRuntimeApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, clearGrantedWorkScopes, clip, compileSequence, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceEmpty, isRequestSourceUuid, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline };
2996
+ export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BillingApi, BoardClient, BoardExtensionRegistry, BoardPlaybackPolicySchema, BoardTransactionError, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, UI_COMMAND_DEFAULT_TIMEOUT_MS, UI_COMMAND_MAX_TIMEOUT_MS, UI_COMMAND_PAYLOAD_MAX_BYTES, UI_COMMAND_PENDING_TTL_SECONDS, UI_COMMAND_SETTLEMENT_GRACE_SECONDS, UI_COMMAND_TERMINAL_TTL_SECONDS, UI_COMMAND_VERSION, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES, WORK_COMPOSER_CHIP_KEY_MAX_LENGTH, WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH, WORK_SURFACE_READY_TIMEOUT_MS, WORK_SURFACE_REQUEST_TIMEOUT_MS, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkRuntimeApi, WorkSurfaceApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, buildWorkSurfaceRequest, clearGrantedWorkScopes, clip, compileSequence, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, formatWorkRef, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalUiCommandStatus, isUiSurfaceMethod, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseUiCommand, parseWorkRef, parseWorkSurfaceReady, parseWorkSurfaceResponse, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline };
@@ -5,6 +5,7 @@ const COHUB_SOURCE_HEADER = {
5
5
  session: "X-Cohub-Source-Session",
6
6
  turn: "X-Cohub-Source-Turn",
7
7
  toolCall: "X-Cohub-Source-Tool-Call",
8
+ client: "X-Cohub-Source-Client",
8
9
  via: "X-Cohub-Source-Via"
9
10
  };
10
11
  Object.values(COHUB_SOURCE_HEADER);
@@ -771,6 +771,38 @@ new one. The server keeps the participant ID, updates `room.participantId`, and
771
771
  closes the superseded connection without emitting a leave event. Without this
772
772
  mode, an unclean disconnect can retain its seat lease for up to one minute.
773
773
 
774
+ #### UI command calls that complete later
775
+
776
+ A `preview.show` command with a Surface request stays pending after the Work
777
+ acknowledges it. The host waits for the Work to be mounted and ready, then the
778
+ Work receives the originating `commandId` in the handler context and returns an
779
+ acknowledgement immediately:
780
+
781
+ ```js
782
+ let activeCommandId = null;
783
+
784
+ client.work.surface.handle("image.open", async (input, { commandId }) => {
785
+ if (!commandId) throw new Error("image.open must be called by a UI command");
786
+ activeCommandId = commandId;
787
+ openImageStudio(input);
788
+ return { accepted: true };
789
+ });
790
+
791
+ async function useImage(result) {
792
+ if (!activeCommandId) return;
793
+ await client.ui.reportResult(activeCommandId, {
794
+ status: "applied",
795
+ result,
796
+ error: null,
797
+ });
798
+ activeCommandId = null;
799
+ }
800
+ ```
801
+
802
+ The Work should persist the command id alongside its local/server-backed draft
803
+ so a reload can restore the pending interaction. A Work session may only report a command that targets that same Work. Existing
804
+ `client.work.surface.handle(method, handler)` usage remains unchanged.
805
+
774
806
  ---
775
807
 
776
808
  ## 6. Complete working example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub",
3
- "version": "5.2.0",
3
+ "version": "5.3.1",
4
4
  "description": "Cohub SDK for spaces, sessions, boards, and realtime agent collaboration.",
5
5
  "license": "Apache-2.0",
6
6
  "private": false,