@xemahq/ui-kernel 0.2.0 → 0.3.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.
Files changed (34) hide show
  1. package/dist/lib/biome-host/errors.d.ts +2 -0
  2. package/dist/lib/biome-host/errors.d.ts.map +1 -0
  3. package/dist/lib/biome-host/errors.js +146 -0
  4. package/dist/lib/biome-host/errors.js.map +1 -0
  5. package/dist/lib/biome-host/host-bridge.d.ts +6 -0
  6. package/dist/lib/biome-host/host-bridge.d.ts.map +1 -1
  7. package/dist/lib/biome-host/host-bridge.js.map +1 -1
  8. package/dist/lib/biome-host/index.d.ts +4 -0
  9. package/dist/lib/biome-host/index.d.ts.map +1 -1
  10. package/dist/lib/biome-host/index.js +4 -0
  11. package/dist/lib/biome-host/index.js.map +1 -1
  12. package/dist/lib/biome-host/realtime-hooks.d.ts +5 -0
  13. package/dist/lib/biome-host/realtime-hooks.d.ts.map +1 -0
  14. package/dist/lib/biome-host/realtime-hooks.js +28 -0
  15. package/dist/lib/biome-host/realtime-hooks.js.map +1 -0
  16. package/dist/lib/biome-host/realtime-port.d.ts +30 -0
  17. package/dist/lib/biome-host/realtime-port.d.ts.map +1 -0
  18. package/dist/lib/biome-host/realtime-port.js +3 -0
  19. package/dist/lib/biome-host/realtime-port.js.map +1 -0
  20. package/dist/lib/biome-host/response-envelope.d.ts +3 -0
  21. package/dist/lib/biome-host/response-envelope.d.ts.map +1 -0
  22. package/dist/lib/biome-host/response-envelope.js +25 -0
  23. package/dist/lib/biome-host/response-envelope.js.map +1 -0
  24. package/dist/ui/chrome/ErrorCard.d.ts.map +1 -1
  25. package/dist/ui/chrome/ErrorCard.js +2 -9
  26. package/dist/ui/chrome/ErrorCard.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/lib/biome-host/errors.ts +220 -0
  29. package/src/lib/biome-host/host-bridge.ts +44 -0
  30. package/src/lib/biome-host/index.ts +4 -0
  31. package/src/lib/biome-host/realtime-hooks.ts +74 -0
  32. package/src/lib/biome-host/realtime-port.ts +109 -0
  33. package/src/lib/biome-host/response-envelope.ts +69 -0
  34. package/src/ui/chrome/ErrorCard.tsx +8 -13
@@ -0,0 +1,2 @@
1
+ export declare function getUserFacingErrorMessage(error: unknown, fallback?: string): string;
2
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/errors.ts"],"names":[],"mappings":"AA0MA,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,OAAO,EACd,QAAQ,SAA4C,GACnD,MAAM,CAcR"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getUserFacingErrorMessage = getUserFacingErrorMessage;
4
+ function collapseWhitespace(value) {
5
+ return value.split(/\s+/).join(' ').trim();
6
+ }
7
+ function getGenericErrorMessage(message, fallback) {
8
+ const normalized = collapseWhitespace(message);
9
+ if (/failed to fetch/i.test(normalized)) {
10
+ return 'Could not reach the service. Check your connection and try again.';
11
+ }
12
+ return normalized || fallback;
13
+ }
14
+ function normalizeValidationMessage(rawMessage) {
15
+ const parts = rawMessage
16
+ .split(',')
17
+ .map((part) => collapseWhitespace(part))
18
+ .filter(Boolean);
19
+ if (parts.length === 0) {
20
+ return 'Some required fields are missing or invalid. Please review your input and try again.';
21
+ }
22
+ const preview = parts.slice(0, 2).join('; ');
23
+ const suffix = parts.length > 2 ? '; and more.' : '.';
24
+ return `Some required fields are missing or invalid (${preview}${suffix})`;
25
+ }
26
+ function isApiClientErrorLike(error) {
27
+ if (!error || typeof error !== 'object') {
28
+ return false;
29
+ }
30
+ const record = error;
31
+ return (record['name'] === 'ApiClientError' &&
32
+ typeof record['statusCode'] === 'number' &&
33
+ typeof record['message'] === 'string');
34
+ }
35
+ function isOrvalClientErrorLike(error) {
36
+ if (!error || typeof error !== 'object') {
37
+ return false;
38
+ }
39
+ const record = error;
40
+ return (record['name'] === 'ClientError' &&
41
+ typeof record['status'] === 'number' &&
42
+ typeof record['message'] === 'string' &&
43
+ Object.hasOwn(record, 'body'));
44
+ }
45
+ function readStringList(value) {
46
+ if (!Array.isArray(value)) {
47
+ return null;
48
+ }
49
+ if (value.some((entry) => typeof entry !== 'string')) {
50
+ return null;
51
+ }
52
+ return value;
53
+ }
54
+ function normalizeWorkflowWalletMissingMessage(envelope) {
55
+ if (envelope.code !== 'WORKFLOW_WALLET_NOT_FOUND') {
56
+ return null;
57
+ }
58
+ const missing = readStringList(envelope.details?.['missing']);
59
+ if (missing === null || missing.length === 0) {
60
+ return 'One or more required wallets are missing for this project.';
61
+ }
62
+ return `Missing wallet${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}. Add ${missing.length > 1 ? 'them' : 'it'} in Project Settings -> Wallets.`;
63
+ }
64
+ function getApiClientErrorMessage(error, fallback) {
65
+ if (error.statusCode === 0) {
66
+ return collapseWhitespace(error.message) || fallback;
67
+ }
68
+ if (error.statusCode === 400) {
69
+ const raw = collapseWhitespace(error.message);
70
+ if (raw.includes(' must ') || raw.includes('regular expression')) {
71
+ return normalizeValidationMessage(raw);
72
+ }
73
+ return raw || 'The request is invalid. Please check your input and try again.';
74
+ }
75
+ if (error.statusCode === 401) {
76
+ return 'Your session expired. Please sign in again.';
77
+ }
78
+ if (error.statusCode === 403) {
79
+ return 'You do not have permission to perform this action.';
80
+ }
81
+ if (error.statusCode === 404) {
82
+ return 'The requested resource was not found.';
83
+ }
84
+ if (error.statusCode >= 500) {
85
+ return 'The service is temporarily unavailable. Please try again in a moment.';
86
+ }
87
+ return collapseWhitespace(error.message) || fallback;
88
+ }
89
+ function getOrvalClientErrorMessage(error, fallback) {
90
+ if (error.status >= 500) {
91
+ return 'The service is temporarily unavailable. Please try again in a moment.';
92
+ }
93
+ if (error.status === 409) {
94
+ const body = error.body;
95
+ if (body && typeof body === 'object') {
96
+ const record = body;
97
+ if (record['error'] === 'session_busy') {
98
+ return 'Your last message is still running. Wait for the agent to finish, then try again.';
99
+ }
100
+ }
101
+ }
102
+ const body = error.body;
103
+ if (body && typeof body === 'object') {
104
+ const record = body;
105
+ const payload = (() => {
106
+ const message = record['message'];
107
+ if (message && typeof message === 'object') {
108
+ return message;
109
+ }
110
+ return {
111
+ code: typeof record['code'] === 'string' ? record['code'] : undefined,
112
+ message: typeof record['message'] === 'string' ? record['message'] : undefined,
113
+ details: record['details'] && typeof record['details'] === 'object'
114
+ ? record['details']
115
+ : undefined,
116
+ };
117
+ })();
118
+ const workflowWalletMessage = normalizeWorkflowWalletMissingMessage(payload);
119
+ if (workflowWalletMessage !== null) {
120
+ return workflowWalletMessage;
121
+ }
122
+ if (typeof payload.message === 'string' && payload.message.trim().length > 0) {
123
+ return collapseWhitespace(payload.message);
124
+ }
125
+ if (typeof record['message'] === 'string' && record['message'].trim().length > 0) {
126
+ return collapseWhitespace(record['message']);
127
+ }
128
+ }
129
+ return getGenericErrorMessage(error.message, fallback);
130
+ }
131
+ function getUserFacingErrorMessage(error, fallback = 'Something went wrong. Please try again.') {
132
+ if (typeof error === 'string') {
133
+ return collapseWhitespace(error) || fallback;
134
+ }
135
+ if (isApiClientErrorLike(error)) {
136
+ return getApiClientErrorMessage(error, fallback);
137
+ }
138
+ if (isOrvalClientErrorLike(error)) {
139
+ return getOrvalClientErrorMessage(error, fallback);
140
+ }
141
+ if (error instanceof Error) {
142
+ return getGenericErrorMessage(error.message, fallback);
143
+ }
144
+ return fallback;
145
+ }
146
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/lib/biome-host/errors.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import type { QueryClient } from '@tanstack/react-query';
2
2
  import { type ReactNode } from 'react';
3
3
  import type { CapabilityPort } from '../capabilities';
4
4
  import type { SystemBus } from '../system-bus';
5
+ import type { RealtimeSource } from './realtime-port';
5
6
  export interface HostBridgeNavigation {
6
7
  push(path: string): void;
7
8
  replace(path: string): void;
@@ -29,6 +30,9 @@ export interface HostBridgeToast {
29
30
  export interface HostBridgeRequestContext {
30
31
  readonly correlationId: string;
31
32
  }
33
+ export interface HostBridgeErrors {
34
+ getUserFacingErrorMessage(error: unknown, fallback?: string): string;
35
+ }
32
36
  export interface PageBackTarget {
33
37
  readonly to: string;
34
38
  readonly label?: string;
@@ -54,6 +58,8 @@ export interface HostBridge {
54
58
  readonly pageMeta: HostBridgePageMeta;
55
59
  readonly system: SystemBus;
56
60
  readonly capabilities: CapabilityPort;
61
+ readonly realtime?: RealtimeSource;
62
+ readonly errors?: HostBridgeErrors;
57
63
  }
58
64
  export declare const HostBridgeContext: import("react").Context<HostBridge | null>;
59
65
  export declare function useHostBridge(): HostBridge;
@@ -1 +1 @@
1
- {"version":3,"file":"host-bridge.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/host-bridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"host-bridge.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/host-bridge.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"host-bridge.js","sourceRoot":"","sources":["../../../src/lib/biome-host/host-bridge.ts"],"names":[],"mappings":";;;AAqNA,sCASC;AA5ND,iCAAkE;AA2MrD,QAAA,iBAAiB,GAAG,IAAA,qBAAa,EAAoB,IAAI,CAAC,CAAC;AACxE,yBAAiB,CAAC,WAAW,GAAG,mBAAmB,CAAC;AAOpD,SAAgB,aAAa;IAC3B,MAAM,MAAM,GAAG,IAAA,kBAAU,EAAC,yBAAiB,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,gFAAgF;YAC9E,0EAA0E,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"host-bridge.js","sourceRoot":"","sources":["../../../src/lib/biome-host/host-bridge.ts"],"names":[],"mappings":";;;AAiQA,sCASC;AAxQD,iCAAkE;AAuPrD,QAAA,iBAAiB,GAAG,IAAA,qBAAa,EAAoB,IAAI,CAAC,CAAC;AACxE,yBAAiB,CAAC,WAAW,GAAG,mBAAmB,CAAC;AAOpD,SAAgB,aAAa;IAC3B,MAAM,MAAM,GAAG,IAAA,kBAAU,EAAC,yBAAiB,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,gFAAgF;YAC9E,0EAA0E,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -12,4 +12,8 @@ export * from './host-sources';
12
12
  export * from './nav';
13
13
  export * from './biome-mode';
14
14
  export * from './agent-validation';
15
+ export * from './realtime-port';
16
+ export * from './realtime-hooks';
17
+ export * from './errors';
18
+ export * from './response-envelope';
15
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/index.ts"],"names":[],"mappings":"AAcA,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/index.ts"],"names":[],"mappings":"AAcA,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC"}
@@ -28,4 +28,8 @@ __exportStar(require("./host-sources"), exports);
28
28
  __exportStar(require("./nav"), exports);
29
29
  __exportStar(require("./biome-mode"), exports);
30
30
  __exportStar(require("./agent-validation"), exports);
31
+ __exportStar(require("./realtime-port"), exports);
32
+ __exportStar(require("./realtime-hooks"), exports);
33
+ __exportStar(require("./errors"), exports);
34
+ __exportStar(require("./response-envelope"), exports);
31
35
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/biome-host/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAcA,mDAAiC;AACjC,gDAA8B;AAC9B,qDAAmC;AACnC,gDAA8B;AAC9B,uDAAqC;AACrC,mDAAiC;AACjC,8DAA4C;AAC5C,mDAAiC;AACjC,0DAAwC;AACxC,qDAAmC;AACnC,iDAA+B;AAC/B,wCAAsB;AACtB,+CAA6B;AAC7B,qDAAmC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/biome-host/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAcA,mDAAiC;AACjC,gDAA8B;AAC9B,qDAAmC;AACnC,gDAA8B;AAC9B,uDAAqC;AACrC,mDAAiC;AACjC,8DAA4C;AAC5C,mDAAiC;AACjC,0DAAwC;AACxC,qDAAmC;AACnC,iDAA+B;AAC/B,wCAAsB;AACtB,+CAA6B;AAC7B,qDAAmC;AACnC,kDAAgC;AAChC,mDAAiC;AACjC,2CAAyB;AACzB,sDAAoC"}
@@ -0,0 +1,5 @@
1
+ import type { RealtimeCloudEvent, RealtimeConnectionState, RealtimeScope } from './realtime-port';
2
+ export declare function useCloudEvent(eventType: string, handler: (event: RealtimeCloudEvent) => void): void;
3
+ export declare function useRealtimeStatus(): RealtimeConnectionState;
4
+ export declare function useEventScope(scope: RealtimeScope): void;
5
+ //# sourceMappingURL=realtime-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-hooks.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/realtime-hooks.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,kBAAkB,EAClB,uBAAuB,EACvB,aAAa,EAEd,MAAM,iBAAiB,CAAC;AAiCzB,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAC3C,IAAI,CAGN;AAGD,wBAAgB,iBAAiB,IAAI,uBAAuB,CAG3D;AAOD,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAGxD"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useCloudEvent = useCloudEvent;
4
+ exports.useRealtimeStatus = useRealtimeStatus;
5
+ exports.useEventScope = useEventScope;
6
+ const host_bridge_1 = require("./host-bridge");
7
+ function requireRealtime(realtime) {
8
+ if (!realtime) {
9
+ throw new Error('useCloudEvent/useRealtimeStatus/useEventScope require a realtime ' +
10
+ 'transport, but the host has not wired `bridge.realtime`. The host ' +
11
+ 'must provide a RealtimeSource (e.g. backed by @xemahq/realtime-client) ' +
12
+ 'on the HostBridge to use kernel realtime hooks.');
13
+ }
14
+ return realtime;
15
+ }
16
+ function useCloudEvent(eventType, handler) {
17
+ const bridge = (0, host_bridge_1.useHostBridge)();
18
+ requireRealtime(bridge.realtime).useCloudEvent(eventType, handler);
19
+ }
20
+ function useRealtimeStatus() {
21
+ const bridge = (0, host_bridge_1.useHostBridge)();
22
+ return requireRealtime(bridge.realtime).useRealtimeStatus();
23
+ }
24
+ function useEventScope(scope) {
25
+ const bridge = (0, host_bridge_1.useHostBridge)();
26
+ requireRealtime(bridge.realtime).useEventScope(scope);
27
+ }
28
+ //# sourceMappingURL=realtime-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-hooks.js","sourceRoot":"","sources":["../../../src/lib/biome-host/realtime-hooks.ts"],"names":[],"mappings":";;AAmDA,sCAMC;AAGD,8CAGC;AAOD,sCAGC;AA7DD,+CAA8C;AAc9C,SAAS,eAAe,CAAC,QAAoC;IAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,mEAAmE;YACjE,oEAAoE;YACpE,yEAAyE;YACzE,iDAAiD,CACpD,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAeD,SAAgB,aAAa,CAC3B,SAAiB,EACjB,OAA4C;IAE5C,MAAM,MAAM,GAAG,IAAA,2BAAa,GAAE,CAAC;IAC/B,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AACrE,CAAC;AAGD,SAAgB,iBAAiB;IAC/B,MAAM,MAAM,GAAG,IAAA,2BAAa,GAAE,CAAC;IAC/B,OAAO,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,iBAAiB,EAAE,CAAC;AAC9D,CAAC;AAOD,SAAgB,aAAa,CAAC,KAAoB;IAChD,MAAM,MAAM,GAAG,IAAA,2BAAa,GAAE,CAAC;IAC/B,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,30 @@
1
+ export interface RealtimeCloudEvent {
2
+ id?: string;
3
+ type: string;
4
+ source?: string;
5
+ subject?: string;
6
+ data?: unknown;
7
+ ehorgid: string;
8
+ ehprojectid?: string;
9
+ ehuserid?: string;
10
+ ehglobalseq?: string;
11
+ ehorgseq?: string;
12
+ ehprojectseq?: string;
13
+ ehsessionseq?: string;
14
+ __hint?: unknown;
15
+ }
16
+ export type RealtimeStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'closed';
17
+ export interface RealtimeConnectionState {
18
+ readonly status: RealtimeStatus;
19
+ readonly connectionId?: string;
20
+ }
21
+ export interface RealtimeScope {
22
+ readonly kind: 'project' | 'session';
23
+ readonly id: string;
24
+ }
25
+ export interface RealtimeSource {
26
+ useCloudEvent(eventType: string, handler: (event: RealtimeCloudEvent) => void): void;
27
+ useRealtimeStatus(): RealtimeConnectionState;
28
+ useEventScope(scope: RealtimeScope): void;
29
+ }
30
+ //# sourceMappingURL=realtime-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-port.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/realtime-port.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,kBAAkB;IAEjC,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,IAAI,EAAE,MAAM,CAAC;IAEb,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,OAAO,EAAE,MAAM,CAAC;IAEhB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAGD,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,YAAY,GACZ,WAAW,GACX,cAAc,GACd,QAAQ,CAAC;AAGb,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAEhC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAOD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAWD,MAAM,WAAW,cAAc;IAO7B,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI,CAAC;IAErF,iBAAiB,IAAI,uBAAuB,CAAC;IAM7C,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC3C"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=realtime-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-port.js","sourceRoot":"","sources":["../../../src/lib/biome-host/realtime-port.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export declare function unwrapList<T>(value: unknown): T[];
2
+ export declare function unwrapData<T>(value: unknown): T;
3
+ //# sourceMappingURL=response-envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-envelope.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/response-envelope.ts"],"names":[],"mappings":"AA+BA,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,CAYjD;AAeD,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAU/C"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unwrapList = unwrapList;
4
+ exports.unwrapData = unwrapData;
5
+ function unwrapList(value) {
6
+ if (Array.isArray(value)) {
7
+ return value;
8
+ }
9
+ if (value !== null &&
10
+ typeof value === 'object' &&
11
+ Array.isArray(value.data)) {
12
+ return value.data;
13
+ }
14
+ return [];
15
+ }
16
+ function unwrapData(value) {
17
+ if (value !== null &&
18
+ typeof value === 'object' &&
19
+ 'data' in value &&
20
+ !('pagination' in value)) {
21
+ return value.data;
22
+ }
23
+ return value;
24
+ }
25
+ //# sourceMappingURL=response-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-envelope.js","sourceRoot":"","sources":["../../../src/lib/biome-host/response-envelope.ts"],"names":[],"mappings":";;AA+BA,gCAYC;AAeD,gCAUC;AArCD,SAAgB,UAAU,CAAI,KAAc;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAY,CAAC;IACtB,CAAC;IACD,IACE,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,OAAO,CAAE,KAA4B,CAAC,IAAI,CAAC,EACjD,CAAC;QACD,OAAQ,KAAuB,CAAC,IAAI,CAAC;IACvC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAeD,SAAgB,UAAU,CAAI,KAAc;IAC1C,IACE,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,MAAM,IAAK,KAAiC;QAC5C,CAAC,CAAC,YAAY,IAAK,KAAiC,CAAC,EACrD,CAAC;QACD,OAAQ,KAAqB,CAAC,IAAI,CAAC;IACrC,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorCard.d.ts","sourceRoot":"","sources":["../../../src/ui/chrome/ErrorCard.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;AAYjF,UAAU,cAAc;IACtB,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAMrB,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,KAAK,EACL,KAA8B,EAC9B,OAAO,EACP,WAAgC,EAChC,eAA0D,GAC3D,EAAE,cAAc,+BAyBhB"}
1
+ {"version":3,"file":"ErrorCard.d.ts","sourceRoot":"","sources":["../../../src/ui/chrome/ErrorCard.tsx"],"names":[],"mappings":"AAaA,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;AAKjF,UAAU,cAAc;IACtB,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAMrB,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,KAAK,EACL,KAA8B,EAC9B,OAAO,EACP,WAAgC,EAChC,eAA0D,GAC3D,EAAE,cAAc,+BAyBhB"}
@@ -3,17 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = ErrorCard;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const lucide_react_1 = require("lucide-react");
6
+ const errors_1 = require("../../lib/biome-host/errors");
6
7
  const button_1 = require("../primitives/button");
7
8
  const card_1 = require("../primitives/card");
8
- const defaultFormatError = (error, fallback) => {
9
- if (typeof error === 'string') {
10
- return error || fallback;
11
- }
12
- if (error instanceof Error && error.message) {
13
- return error.message;
14
- }
15
- return fallback;
16
- };
9
+ const defaultFormatError = (error, fallback) => (0, errors_1.getUserFacingErrorMessage)(error, fallback);
17
10
  function ErrorCard({ error, title = 'Something went wrong', onRetry, formatError = defaultFormatError, fallbackMessage = 'Unable to load this content right now.', }) {
18
11
  const message = formatError(error, fallbackMessage);
19
12
  return ((0, jsx_runtime_1.jsx)(card_1.Card, { className: "border-destructive/25 bg-destructive/[0.03] rounded-xl", children: (0, jsx_runtime_1.jsxs)(card_1.CardContent, { className: "flex flex-col items-center py-14 text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "h-14 w-14 rounded-2xl bg-destructive/10 flex items-center justify-center mb-4", children: (0, jsx_runtime_1.jsx)(lucide_react_1.AlertCircle, { className: "h-6 w-6 text-destructive" }) }), (0, jsx_runtime_1.jsx)("p", { className: "text-subtitle font-semibold text-ink", children: title }), (0, jsx_runtime_1.jsx)("p", { className: "text-body-1 text-ink-3 mt-2 max-w-md leading-relaxed", children: message }), onRetry && ((0, jsx_runtime_1.jsxs)(button_1.Button, { size: "sm", variant: "outline", onClick: onRetry, className: "mt-5 gap-2 h-10 border-destructive/20 text-destructive hover:bg-destructive/5", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "h-4 w-4" }), "Retry"] }))] }) }));
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorCard.js","sourceRoot":"","sources":["../../../src/ui/chrome/ErrorCard.tsx"],"names":[],"mappings":";;AAoCA,4BA+BC;;AAnED,+CAAsD;AAEtD,iDAA8C;AAC9C,6CAAuD;AAUvD,MAAM,kBAAkB,GAA0B,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;IACpE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,IAAI,QAAQ,CAAC;IAC3B,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAeF,SAAwB,SAAS,CAAC,EAChC,KAAK,EACL,KAAK,GAAG,sBAAsB,EAC9B,OAAO,EACP,WAAW,GAAG,kBAAkB,EAChC,eAAe,GAAG,wCAAwC,GAC3C;IACf,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;IAEpD,OAAO,CACL,uBAAC,WAAI,IAAC,SAAS,EAAC,wDAAwD,YACtE,wBAAC,kBAAW,IAAC,SAAS,EAAC,8CAA8C,aACnE,gCAAK,SAAS,EAAC,+EAA+E,YAC5F,uBAAC,0BAAW,IAAC,SAAS,EAAC,0BAA0B,GAAG,GAChD,EACN,8BAAG,SAAS,EAAC,sCAAsC,YAAE,KAAK,GAAK,EAC/D,8BAAG,SAAS,EAAC,sDAAsD,YAAE,OAAO,GAAK,EAChF,OAAO,IAAI,CACV,wBAAC,eAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,SAAS,EACjB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAC,+EAA+E,aAEzF,uBAAC,wBAAS,IAAC,SAAS,EAAC,SAAS,GAAG,aAE1B,CACV,IACW,GACT,CACR,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ErrorCard.js","sourceRoot":"","sources":["../../../src/ui/chrome/ErrorCard.tsx"],"names":[],"mappings":";;AA+BA,4BA+BC;;AA9DD,+CAAsD;AAEtD,wDAAwE;AACxE,iDAA8C;AAC9C,6CAAuD;AAWvD,MAAM,kBAAkB,GAA0B,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CACpE,IAAA,kCAAyB,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAe7C,SAAwB,SAAS,CAAC,EAChC,KAAK,EACL,KAAK,GAAG,sBAAsB,EAC9B,OAAO,EACP,WAAW,GAAG,kBAAkB,EAChC,eAAe,GAAG,wCAAwC,GAC3C;IACf,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;IAEpD,OAAO,CACL,uBAAC,WAAI,IAAC,SAAS,EAAC,wDAAwD,YACtE,wBAAC,kBAAW,IAAC,SAAS,EAAC,8CAA8C,aACnE,gCAAK,SAAS,EAAC,+EAA+E,YAC5F,uBAAC,0BAAW,IAAC,SAAS,EAAC,0BAA0B,GAAG,GAChD,EACN,8BAAG,SAAS,EAAC,sCAAsC,YAAE,KAAK,GAAK,EAC/D,8BAAG,SAAS,EAAC,sDAAsD,YAAE,OAAO,GAAK,EAChF,OAAO,IAAI,CACV,wBAAC,eAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,SAAS,EACjB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAC,+EAA+E,aAEzF,uBAAC,wBAAS,IAAC,SAAS,EAAC,SAAS,GAAG,aAE1B,CACV,IACW,GACT,CACR,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/ui-kernel",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Host-framework-agnostic UI kernel for the Xema OS. Defines the SystemBus orchestration contract (capability.invoke, cross-biome intents, command palette, xema:// deeplinks, window manager) AND the biome-host contract surface (FrontendBiome/FrontendBiomeFactory, HostBridge, the singleton biomeRegistry, session contributions) that every frontend biome composes against. No Vite, Next.js, or React-Router — React itself IS allowed as the shared component model (the contracts traffic in ReactNode/ComponentType and a React context). Concrete host adapters (router/auth/toast wiring) live in separate packages that consume this kernel. The SystemBus is pure orchestration: it never authorizes, the backend capability-router enforces all policy.",
5
5
  "keywords": [
6
6
  "xema",
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Canonical user-facing error decoder — ONE implementation biomes, the host
3
+ * shell, and the kernel's {@link ErrorCard} default all share.
4
+ *
5
+ * Biomes were each re-implementing "pull a human message off an error"
6
+ * (envelope shapes, status-code copy, "failed to fetch"). This is the single
7
+ * host-agnostic decoder. It is a PURE function: no fetch, no i18n, no config,
8
+ * no `instanceof` against host classes. It recognises the two error shapes the
9
+ * Xema frontend produces by their structural signature:
10
+ *
11
+ * - the host's `ApiClientError` (`name === 'ApiClientError'`, `statusCode`,
12
+ * `message`, optional `errorType`), thrown by the hand-rolled HTTP client;
13
+ * - Orval's generated `ClientError` (`name === 'ClientError'`, `status`,
14
+ * `message`, `body`), thrown by the generated clients. Its `body` carries
15
+ * the backend `{ message, code, details }` envelope (and the workflow
16
+ * wallet-missing / `session_busy` special cases).
17
+ *
18
+ * Hosts that previously kept their own `getUserFacingErrorMessage` should
19
+ * re-export THIS one (or delete theirs and import it) so there is a single
20
+ * decoder. {@link ErrorCard} defaults to it, so biomes get envelope-aware copy
21
+ * without wiring anything.
22
+ */
23
+
24
+ function collapseWhitespace(value: string): string {
25
+ return value.split(/\s+/).join(' ').trim();
26
+ }
27
+
28
+ function getGenericErrorMessage(message: string, fallback: string): string {
29
+ const normalized = collapseWhitespace(message);
30
+ if (/failed to fetch/i.test(normalized)) {
31
+ return 'Could not reach the service. Check your connection and try again.';
32
+ }
33
+ return normalized || fallback;
34
+ }
35
+
36
+ function normalizeValidationMessage(rawMessage: string): string {
37
+ const parts = rawMessage
38
+ .split(',')
39
+ .map((part) => collapseWhitespace(part))
40
+ .filter(Boolean);
41
+
42
+ if (parts.length === 0) {
43
+ return 'Some required fields are missing or invalid. Please review your input and try again.';
44
+ }
45
+
46
+ const preview = parts.slice(0, 2).join('; ');
47
+ const suffix = parts.length > 2 ? '; and more.' : '.';
48
+ return `Some required fields are missing or invalid (${preview}${suffix})`;
49
+ }
50
+
51
+ /** Structural signature of the host's `ApiClientError`. */
52
+ interface ApiClientErrorLike {
53
+ readonly name: string;
54
+ readonly statusCode: number;
55
+ readonly message: string;
56
+ readonly errorType?: string;
57
+ }
58
+
59
+ function isApiClientErrorLike(error: unknown): error is ApiClientErrorLike {
60
+ if (!error || typeof error !== 'object') {
61
+ return false;
62
+ }
63
+ const record = error as Record<string, unknown>;
64
+ return (
65
+ record['name'] === 'ApiClientError' &&
66
+ typeof record['statusCode'] === 'number' &&
67
+ typeof record['message'] === 'string'
68
+ );
69
+ }
70
+
71
+ /** Structural signature of an Orval-generated `ClientError`. */
72
+ interface OrvalClientErrorLike {
73
+ readonly name: string;
74
+ readonly status: number;
75
+ readonly message: string;
76
+ readonly body: unknown;
77
+ }
78
+
79
+ function isOrvalClientErrorLike(error: unknown): error is OrvalClientErrorLike {
80
+ if (!error || typeof error !== 'object') {
81
+ return false;
82
+ }
83
+ const record = error as Record<string, unknown>;
84
+ return (
85
+ record['name'] === 'ClientError' &&
86
+ typeof record['status'] === 'number' &&
87
+ typeof record['message'] === 'string' &&
88
+ Object.hasOwn(record, 'body')
89
+ );
90
+ }
91
+
92
+ interface WorkflowErrorEnvelope {
93
+ readonly code?: string;
94
+ readonly message?: string;
95
+ readonly details?: Record<string, unknown>;
96
+ }
97
+
98
+ function readStringList(value: unknown): string[] | null {
99
+ if (!Array.isArray(value)) {
100
+ return null;
101
+ }
102
+ if (value.some((entry) => typeof entry !== 'string')) {
103
+ return null;
104
+ }
105
+ return value;
106
+ }
107
+
108
+ function normalizeWorkflowWalletMissingMessage(envelope: WorkflowErrorEnvelope): string | null {
109
+ if (envelope.code !== 'WORKFLOW_WALLET_NOT_FOUND') {
110
+ return null;
111
+ }
112
+ const missing = readStringList(envelope.details?.['missing']);
113
+ if (missing === null || missing.length === 0) {
114
+ return 'One or more required wallets are missing for this project.';
115
+ }
116
+ return `Missing wallet${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}. Add ${
117
+ missing.length > 1 ? 'them' : 'it'
118
+ } in Project Settings -> Wallets.`;
119
+ }
120
+
121
+ function getApiClientErrorMessage(error: ApiClientErrorLike, fallback: string): string {
122
+ if (error.statusCode === 0) {
123
+ return collapseWhitespace(error.message) || fallback;
124
+ }
125
+ if (error.statusCode === 400) {
126
+ const raw = collapseWhitespace(error.message);
127
+ if (raw.includes(' must ') || raw.includes('regular expression')) {
128
+ return normalizeValidationMessage(raw);
129
+ }
130
+ return raw || 'The request is invalid. Please check your input and try again.';
131
+ }
132
+ if (error.statusCode === 401) {
133
+ return 'Your session expired. Please sign in again.';
134
+ }
135
+ if (error.statusCode === 403) {
136
+ return 'You do not have permission to perform this action.';
137
+ }
138
+ if (error.statusCode === 404) {
139
+ return 'The requested resource was not found.';
140
+ }
141
+ if (error.statusCode >= 500) {
142
+ return 'The service is temporarily unavailable. Please try again in a moment.';
143
+ }
144
+ return collapseWhitespace(error.message) || fallback;
145
+ }
146
+
147
+ function getOrvalClientErrorMessage(error: OrvalClientErrorLike, fallback: string): string {
148
+ if (error.status >= 500) {
149
+ return 'The service is temporarily unavailable. Please try again in a moment.';
150
+ }
151
+ // session-api refuses mutations while a prompt turn is in flight; the 409
152
+ // body is `{ error: 'session_busy', retryAfterTurnDone: true }`. Surface a
153
+ // clear retry hint instead of the generic "Conflict" copy.
154
+ if (error.status === 409) {
155
+ const body = error.body;
156
+ if (body && typeof body === 'object') {
157
+ const record = body as Record<string, unknown>;
158
+ if (record['error'] === 'session_busy') {
159
+ return 'Your last message is still running. Wait for the agent to finish, then try again.';
160
+ }
161
+ }
162
+ }
163
+ const body = error.body;
164
+ if (body && typeof body === 'object') {
165
+ const record = body as Record<string, unknown>;
166
+ const payload: WorkflowErrorEnvelope = (() => {
167
+ const message = record['message'];
168
+ if (message && typeof message === 'object') {
169
+ return message as WorkflowErrorEnvelope;
170
+ }
171
+ return {
172
+ code: typeof record['code'] === 'string' ? record['code'] : undefined,
173
+ message: typeof record['message'] === 'string' ? record['message'] : undefined,
174
+ details:
175
+ record['details'] && typeof record['details'] === 'object'
176
+ ? (record['details'] as Record<string, unknown>)
177
+ : undefined,
178
+ };
179
+ })();
180
+
181
+ const workflowWalletMessage = normalizeWorkflowWalletMissingMessage(payload);
182
+ if (workflowWalletMessage !== null) {
183
+ return workflowWalletMessage;
184
+ }
185
+ if (typeof payload.message === 'string' && payload.message.trim().length > 0) {
186
+ return collapseWhitespace(payload.message);
187
+ }
188
+ if (typeof record['message'] === 'string' && record['message'].trim().length > 0) {
189
+ return collapseWhitespace(record['message']);
190
+ }
191
+ }
192
+ return getGenericErrorMessage(error.message, fallback);
193
+ }
194
+
195
+ /**
196
+ * Decode any thrown value into a human, user-facing message. The single decoder
197
+ * for biomes, the host shell, and {@link ErrorCard}'s default.
198
+ *
199
+ * @param error Any thrown value (string, `Error`, `ApiClientError`, Orval
200
+ * `ClientError`, or unknown).
201
+ * @param fallback Copy returned when nothing better can be extracted.
202
+ */
203
+ export function getUserFacingErrorMessage(
204
+ error: unknown,
205
+ fallback = 'Something went wrong. Please try again.',
206
+ ): string {
207
+ if (typeof error === 'string') {
208
+ return collapseWhitespace(error) || fallback;
209
+ }
210
+ if (isApiClientErrorLike(error)) {
211
+ return getApiClientErrorMessage(error, fallback);
212
+ }
213
+ if (isOrvalClientErrorLike(error)) {
214
+ return getOrvalClientErrorMessage(error, fallback);
215
+ }
216
+ if (error instanceof Error) {
217
+ return getGenericErrorMessage(error.message, fallback);
218
+ }
219
+ return fallback;
220
+ }
@@ -5,6 +5,8 @@ import { createContext, useContext, type ReactNode } from 'react';
5
5
  import type { CapabilityPort } from '../capabilities';
6
6
  import type { SystemBus } from '../system-bus';
7
7
 
8
+ import type { RealtimeSource } from './realtime-port';
9
+
8
10
  /**
9
11
  * `HostBridge` is the host-agnostic abstraction every frontend biome
10
12
  * receives at registration time. It decouples biome code from any
@@ -113,6 +115,26 @@ export interface HostBridgeRequestContext {
113
115
  readonly correlationId: string;
114
116
  }
115
117
 
118
+ /**
119
+ * Error-decoding surface — the canonical "turn a thrown value into a
120
+ * user-facing message" decoder, exposed on the bridge so biomes use ONE
121
+ * implementation instead of each re-deriving envelope/status-code copy.
122
+ *
123
+ * The decoder is a PURE function (no host context), so the kernel also ships it
124
+ * directly as {@link getUserFacingErrorMessage} and {@link ErrorCard} defaults
125
+ * to it. It is ALSO hung off the bridge so a host can override it with a
126
+ * shell-specific decoder (extra error codes, i18n) without every biome wiring a
127
+ * prop — the bridge value is the single override point.
128
+ */
129
+ export interface HostBridgeErrors {
130
+ /**
131
+ * Decode any thrown value into a user-facing message. `fallback` is returned
132
+ * when nothing better can be extracted. Defaults (in the kernel default
133
+ * bridge wiring) to {@link getUserFacingErrorMessage}.
134
+ */
135
+ getUserFacingErrorMessage(error: unknown, fallback?: string): string;
136
+ }
137
+
116
138
  /**
117
139
  * Where the topbar's leading back-button takes you. Pages that have a
118
140
  * meaningful "parent context" register this; pages without one omit it
@@ -201,6 +223,28 @@ export interface HostBridge {
201
223
  * `<CapabilityProvider>` + `useCapability()` (see `../capabilities`).
202
224
  */
203
225
  readonly capabilities: CapabilityPort;
226
+ /**
227
+ * Realtime (CloudEvents over SSE) port. The HOST injects a concrete
228
+ * {@link RealtimeSource} backed by its realtime transport
229
+ * (`@xemahq/realtime-client`); biomes reach it through the kernel
230
+ * `useCloudEvent` / `useRealtimeStatus` / `useEventScope` hooks so biome code
231
+ * never imports the SSE client directly — same decoupling as `navigation`.
232
+ *
233
+ * OPTIONAL: a host without a realtime transport simply omits it; the kernel
234
+ * `useCloudEvent`/`useRealtimeStatus`/`useEventScope` hooks fail fast with an
235
+ * actionable error if a biome uses them before the host wires `bridge.realtime`.
236
+ */
237
+ readonly realtime?: RealtimeSource;
238
+ /**
239
+ * Error-decoding surface — the canonical user-facing error decoder. Defaults
240
+ * (in the kernel default bridge wiring) to {@link getUserFacingErrorMessage};
241
+ * a host MAY override it. Biomes + {@link ErrorCard} read ONE decoder instead
242
+ * of each re-deriving message extraction.
243
+ *
244
+ * OPTIONAL: when omitted, consumers fall back to the kernel default
245
+ * {@link getUserFacingErrorMessage} (which ErrorCard already uses directly).
246
+ */
247
+ readonly errors?: HostBridgeErrors;
204
248
  }
205
249
 
206
250
  export const HostBridgeContext = createContext<HostBridge | null>(null);
@@ -26,3 +26,7 @@ export * from './host-sources';
26
26
  export * from './nav';
27
27
  export * from './biome-mode';
28
28
  export * from './agent-validation';
29
+ export * from './realtime-port';
30
+ export * from './realtime-hooks';
31
+ export * from './errors';
32
+ export * from './response-envelope';
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Kernel-side realtime hooks. Biomes call these INSTEAD of importing the host's
3
+ * realtime client (`@xemahq/realtime-client`) directly, so biome code stays
4
+ * decoupled from the SSE transport — the same decoupling `useHostBridge`
5
+ * already provides for navigation / auth / capabilities.
6
+ *
7
+ * Each hook reads the host-injected {@link RealtimeSource} off
8
+ * `bridge.realtime` and delegates straight through. The host owns the transport
9
+ * (connection, token refresh, org header, scope refcounting); the kernel owns
10
+ * only the contract + this thin delegation.
11
+ */
12
+
13
+ import { useHostBridge } from './host-bridge';
14
+ import type {
15
+ RealtimeCloudEvent,
16
+ RealtimeConnectionState,
17
+ RealtimeScope,
18
+ RealtimeSource,
19
+ } from './realtime-port';
20
+
21
+ /**
22
+ * Resolve the host-injected realtime transport, failing fast with an actionable
23
+ * message if the host has not wired `bridge.realtime`. `bridge.realtime` is an
24
+ * optional port (a host without a realtime transport omits it), so the kernel
25
+ * hooks must not silently no-op — a biome that calls them expects live events.
26
+ */
27
+ function requireRealtime(realtime: RealtimeSource | undefined): RealtimeSource {
28
+ if (!realtime) {
29
+ throw new Error(
30
+ 'useCloudEvent/useRealtimeStatus/useEventScope require a realtime ' +
31
+ 'transport, but the host has not wired `bridge.realtime`. The host ' +
32
+ 'must provide a RealtimeSource (e.g. backed by @xemahq/realtime-client) ' +
33
+ 'on the HostBridge to use kernel realtime hooks.',
34
+ );
35
+ }
36
+ return realtime;
37
+ }
38
+
39
+ /**
40
+ * Subscribe to delivered CloudEvents of a given `type` for the lifetime of the
41
+ * calling component. Pass a stable `handler` (e.g. via `useCallback`) — the
42
+ * host re-subscribes when the handler identity changes. Narrow `event.data`
43
+ * inside the handler; the kernel does not parse payloads.
44
+ *
45
+ * @example
46
+ * useCloudEvent('session.lifecycle.changed', (event) => {
47
+ * const data = event.data as { sessionId?: string } | undefined;
48
+ * if (data?.sessionId !== sessionId) return;
49
+ * refetch();
50
+ * });
51
+ */
52
+ export function useCloudEvent(
53
+ eventType: string,
54
+ handler: (event: RealtimeCloudEvent) => void,
55
+ ): void {
56
+ const bridge = useHostBridge();
57
+ requireRealtime(bridge.realtime).useCloudEvent(eventType, handler);
58
+ }
59
+
60
+ /** Coarse realtime connection state for a status indicator. */
61
+ export function useRealtimeStatus(): RealtimeConnectionState {
62
+ const bridge = useHostBridge();
63
+ return requireRealtime(bridge.realtime).useRealtimeStatus();
64
+ }
65
+
66
+ /**
67
+ * Subscribe the connection to a project/session {@link RealtimeScope} while the
68
+ * calling component is mounted. Scopes are reference-counted by the host, so
69
+ * multiple components asking for the same scope share one server subscription.
70
+ */
71
+ export function useEventScope(scope: RealtimeScope): void {
72
+ const bridge = useHostBridge();
73
+ requireRealtime(bridge.realtime).useEventScope(scope);
74
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Host-agnostic realtime (CloudEvents over SSE) port.
3
+ *
4
+ * Frontend biomes need to subscribe to server-pushed CloudEvents (session
5
+ * lifecycle, tool calls, notifications, …). The transport — the unified SSE
6
+ * connection, the Keycloak-minted bearer, the active-org header, cross-tab
7
+ * leader election, server-side scope subscriptions — is HOST-owned (today
8
+ * `@xemahq/realtime-client` wrapped by the host's `RealtimeProvider`). Biomes
9
+ * must NOT import that client or `EventSource` directly, exactly as they must
10
+ * not import `next/navigation` or `sonner`.
11
+ *
12
+ * Instead the host injects a concrete {@link RealtimeSource} when it builds the
13
+ * {@link HostBridge}; it is hung off `bridge.realtime`. Biomes reach it through
14
+ * the {@link useCloudEvent} / {@link useRealtimeStatus} / {@link useEventScope}
15
+ * kernel hooks — the same decoupling pattern as `bridge.navigation` /
16
+ * `bridge.capabilities`.
17
+ *
18
+ * Framework-agnostic: pure interface contracts, no React, no SSE, no fetch.
19
+ */
20
+
21
+ /**
22
+ * The subset of the CloudEvents-shape envelope a biome handler receives.
23
+ *
24
+ * Locally typed in the kernel (deliberately NOT imported from
25
+ * `@xemahq/realtime-client` / `@xemahq/events`) so `@xemahq/ui-kernel` stays
26
+ * transport-free and host-agnostic. The host's realtime client emits an object
27
+ * that is structurally assignable to this — value-identical to the client's
28
+ * `DeliveredEvent`, kept here as the kernel's stable contract.
29
+ */
30
+ export interface RealtimeCloudEvent {
31
+ /** CloudEvents `id` — unique per delivered event, when the server stamps it. */
32
+ id?: string;
33
+ /** CloudEvents `type`, e.g. `session.lifecycle.changed`. The subscription key. */
34
+ type: string;
35
+ /** CloudEvents `source`, e.g. `/services/activity-feed-api` or `/biomes/<id>`. */
36
+ source?: string;
37
+ /** CloudEvents `subject`, e.g. `session/<session-id>`. */
38
+ subject?: string;
39
+ /** CloudEvents `data` payload. Biomes narrow this themselves. */
40
+ data?: unknown;
41
+ /** Org-id header echoed onto every delivered envelope. */
42
+ ehorgid: string;
43
+ /** Project-id header, when the event is project-scoped. */
44
+ ehprojectid?: string;
45
+ /** User-id header, when the event is user-scoped. */
46
+ ehuserid?: string;
47
+ /** Global delivery cursor. */
48
+ ehglobalseq?: string;
49
+ /** Per-org delivery cursor. */
50
+ ehorgseq?: string;
51
+ /** Per-project delivery cursor. */
52
+ ehprojectseq?: string;
53
+ /** Per-session delivery cursor. */
54
+ ehsessionseq?: string;
55
+ /** Frontend-only invalidation hint carried alongside the envelope. */
56
+ __hint?: unknown;
57
+ }
58
+
59
+ /** Coarse realtime connection status for status indicators. */
60
+ export type RealtimeStatus =
61
+ | 'idle'
62
+ | 'connecting'
63
+ | 'connected'
64
+ | 'reconnecting'
65
+ | 'closed';
66
+
67
+ /** Coarse connection state surfaced to status UIs. */
68
+ export interface RealtimeConnectionState {
69
+ readonly status: RealtimeStatus;
70
+ /** Latest connectionId returned by the upstream `CONNECTED` frame. */
71
+ readonly connectionId?: string;
72
+ }
73
+
74
+ /**
75
+ * A server-side scope a connection can subscribe to so it only receives the
76
+ * events for one project or session, rather than the full org firehose. The
77
+ * host's transport reference-counts identical scopes across mounts.
78
+ */
79
+ export interface RealtimeScope {
80
+ readonly kind: 'project' | 'session';
81
+ readonly id: string;
82
+ }
83
+
84
+ /**
85
+ * Host-implemented realtime source. The host wires each method against its
86
+ * realtime transport (`@xemahq/realtime-client`'s `RealtimeContext`) and passes
87
+ * it in when building the {@link HostBridge}.
88
+ *
89
+ * Every method is a React hook (it reads transport context / state), so it
90
+ * MUST be called from a biome component's render — the kernel `useCloudEvent` /
91
+ * `useRealtimeStatus` / `useEventScope` hooks delegate straight through.
92
+ */
93
+ export interface RealtimeSource {
94
+ /**
95
+ * Subscribe `handler` to every delivered CloudEvent whose `type` matches
96
+ * `eventType`, for the lifetime of the calling component. Per-`data` filtering
97
+ * (e.g. by `sessionId`) is the handler's responsibility. Mirrors the host
98
+ * client's `useCloudEvent`.
99
+ */
100
+ useCloudEvent(eventType: string, handler: (event: RealtimeCloudEvent) => void): void;
101
+ /** Coarse connection state for a status indicator. */
102
+ useRealtimeStatus(): RealtimeConnectionState;
103
+ /**
104
+ * Subscribe the connection's unified SSE to a project/session scope while the
105
+ * calling component is mounted; auto-unsubscribes on unmount. Server-side
106
+ * subscriptions are reference-counted by the host transport.
107
+ */
108
+ useEventScope(scope: RealtimeScope): void;
109
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Frontend mirror of the backend `ResponseEnvelopeInterceptor`.
3
+ *
4
+ * Every 2xx body the platform returns is wrapped as `{ data: T }`, or
5
+ * `{ data: T[], pagination }` for a paginated list (single source of truth:
6
+ * `@xemahq/platform-common`'s `ResponseEnvelopeInterceptor`). Orval's
7
+ * `custom-fetch` mutators usually peel that envelope at the HTTP boundary, but
8
+ * spec drift (a controller typed `T[]` that emits `{ data }`), public clients
9
+ * that DON'T peel, and transient partial responses leave a `.data`-wrapped or
10
+ * mis-shaped value where biome code expects a bare value/array — and a raw
11
+ * `.map` on it white-screens the route.
12
+ *
13
+ * These are the ONE shared, pure unwrap helpers — no host context, no fetch.
14
+ * Biomes (and the host shell's own `unwrap-list.ts`) should adopt these instead
15
+ * of re-implementing the guard. This is the shared helper only; adopters are
16
+ * migrated incrementally, not in a blanket rewrite.
17
+ */
18
+
19
+ /**
20
+ * Resilient list unwrap for DISPLAY surfaces — normalises a React-Query `.data`
21
+ * to an array so the page renders its empty/error state instead of crashing:
22
+ *
23
+ * - already an array → returned as-is
24
+ * - `{ data: T[] }` envelope → `value.data`
25
+ * - anything else → `[]`
26
+ *
27
+ * Deliberately NON-throwing: the query's own `isError`/`isLoading` flags drive
28
+ * the visible error/empty UI; this only guards the render path. For
29
+ * mutation/single-resource flows where an unexpected shape is a real bug, use
30
+ * {@link unwrapData} (fail-fast) instead.
31
+ */
32
+ export function unwrapList<T>(value: unknown): T[] {
33
+ if (Array.isArray(value)) {
34
+ return value as T[];
35
+ }
36
+ if (
37
+ value !== null &&
38
+ typeof value === 'object' &&
39
+ Array.isArray((value as { data?: unknown }).data)
40
+ ) {
41
+ return (value as { data: T[] }).data;
42
+ }
43
+ return [];
44
+ }
45
+
46
+ /**
47
+ * Fail-fast single-resource unwrap. Returns `value.data` when the value is a
48
+ * `{ data }` envelope; returns the value unchanged when it is already the
49
+ * unwrapped resource (an Orval client that peeled the envelope at the boundary).
50
+ *
51
+ * Use this on mutation / single-resource flows where the response SHOULD be a
52
+ * concrete object — unlike {@link unwrapList}, it does not silently coerce, so
53
+ * a genuinely-unexpected shape surfaces as the value rather than being masked.
54
+ *
55
+ * A paginated envelope (`{ data, pagination }`) is returned INTACT — callers
56
+ * that need both fields read them off the returned object; peeling only `data`
57
+ * would drop the pagination cursor.
58
+ */
59
+ export function unwrapData<T>(value: unknown): T {
60
+ if (
61
+ value !== null &&
62
+ typeof value === 'object' &&
63
+ 'data' in (value as Record<string, unknown>) &&
64
+ !('pagination' in (value as Record<string, unknown>))
65
+ ) {
66
+ return (value as { data: T }).data;
67
+ }
68
+ return value as T;
69
+ }
@@ -1,25 +1,20 @@
1
1
  import { AlertCircle, RefreshCw } from 'lucide-react';
2
2
 
3
+ import { getUserFacingErrorMessage } from '../../lib/biome-host/errors';
3
4
  import { Button } from '../primitives/button';
4
5
  import { Card, CardContent } from '../primitives/card';
5
6
 
6
7
  /**
7
- * Host-agnostic error formatter. The host shell owns rich error decoding
8
- * (Orval envelopes, workflow error codes, …); pass its formatter via
9
- * `formatError` to preserve that behaviour. The default extracts a plain
10
- * message and is safe in any consumer.
8
+ * Host-agnostic error formatter. The default is the canonical, envelope-aware
9
+ * {@link getUserFacingErrorMessage} decoder biomes get rich error copy with
10
+ * zero wiring. Pass `formatError` only to override with a host-specific decoder
11
+ * (extra codes, i18n); the canonical default already handles Orval envelopes,
12
+ * `ApiClientError`, workflow error codes, and "failed to fetch".
11
13
  */
12
14
  export type ErrorMessageFormatter = (error: unknown, fallback: string) => string;
13
15
 
14
- const defaultFormatError: ErrorMessageFormatter = (error, fallback) => {
15
- if (typeof error === 'string') {
16
- return error || fallback;
17
- }
18
- if (error instanceof Error && error.message) {
19
- return error.message;
20
- }
21
- return fallback;
22
- };
16
+ const defaultFormatError: ErrorMessageFormatter = (error, fallback) =>
17
+ getUserFacingErrorMessage(error, fallback);
23
18
 
24
19
  interface ErrorCardProps {
25
20
  error: Error | string | unknown;