@xemahq/ui-kernel 0.1.4

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 (208) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +72 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +19 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/biome-host/biome-mode.d.ts +2 -0
  8. package/dist/lib/biome-host/biome-mode.d.ts.map +1 -0
  9. package/dist/lib/biome-host/biome-mode.js +3 -0
  10. package/dist/lib/biome-host/biome-mode.js.map +1 -0
  11. package/dist/lib/biome-host/biome-registry.d.ts +30 -0
  12. package/dist/lib/biome-host/biome-registry.d.ts.map +1 -0
  13. package/dist/lib/biome-host/biome-registry.js +134 -0
  14. package/dist/lib/biome-host/biome-registry.js.map +1 -0
  15. package/dist/lib/biome-host/composition-validation.d.ts +22 -0
  16. package/dist/lib/biome-host/composition-validation.d.ts.map +1 -0
  17. package/dist/lib/biome-host/composition-validation.js +127 -0
  18. package/dist/lib/biome-host/composition-validation.js.map +1 -0
  19. package/dist/lib/biome-host/frontend-biome.d.ts +47 -0
  20. package/dist/lib/biome-host/frontend-biome.d.ts.map +1 -0
  21. package/dist/lib/biome-host/frontend-biome.js +3 -0
  22. package/dist/lib/biome-host/frontend-biome.js.map +1 -0
  23. package/dist/lib/biome-host/host-bridge.d.ts +55 -0
  24. package/dist/lib/biome-host/host-bridge.d.ts.map +1 -0
  25. package/dist/lib/biome-host/host-bridge.js +16 -0
  26. package/dist/lib/biome-host/host-bridge.js.map +1 -0
  27. package/dist/lib/biome-host/host-sources.d.ts +12 -0
  28. package/dist/lib/biome-host/host-sources.d.ts.map +1 -0
  29. package/dist/lib/biome-host/host-sources.js +3 -0
  30. package/dist/lib/biome-host/host-sources.js.map +1 -0
  31. package/dist/lib/biome-host/index.d.ts +10 -0
  32. package/dist/lib/biome-host/index.d.ts.map +1 -0
  33. package/dist/lib/biome-host/index.js +26 -0
  34. package/dist/lib/biome-host/index.js.map +1 -0
  35. package/dist/lib/biome-host/nav.d.ts +17 -0
  36. package/dist/lib/biome-host/nav.d.ts.map +1 -0
  37. package/dist/lib/biome-host/nav.js +52 -0
  38. package/dist/lib/biome-host/nav.js.map +1 -0
  39. package/dist/lib/biome-host/session-contributions.d.ts +143 -0
  40. package/dist/lib/biome-host/session-contributions.d.ts.map +1 -0
  41. package/dist/lib/biome-host/session-contributions.js +3 -0
  42. package/dist/lib/biome-host/session-contributions.js.map +1 -0
  43. package/dist/lib/biome-host/session-profiles.d.ts +18 -0
  44. package/dist/lib/biome-host/session-profiles.d.ts.map +1 -0
  45. package/dist/lib/biome-host/session-profiles.js +39 -0
  46. package/dist/lib/biome-host/session-profiles.js.map +1 -0
  47. package/dist/lib/system-bus/capability-invoke.d.ts +18 -0
  48. package/dist/lib/system-bus/capability-invoke.d.ts.map +1 -0
  49. package/dist/lib/system-bus/capability-invoke.js +3 -0
  50. package/dist/lib/system-bus/capability-invoke.js.map +1 -0
  51. package/dist/lib/system-bus/deeplink.d.ts +33 -0
  52. package/dist/lib/system-bus/deeplink.d.ts.map +1 -0
  53. package/dist/lib/system-bus/deeplink.js +99 -0
  54. package/dist/lib/system-bus/deeplink.js.map +1 -0
  55. package/dist/lib/system-bus/enums.d.ts +27 -0
  56. package/dist/lib/system-bus/enums.d.ts.map +1 -0
  57. package/dist/lib/system-bus/enums.js +35 -0
  58. package/dist/lib/system-bus/enums.js.map +1 -0
  59. package/dist/lib/system-bus/host-ports.d.ts +24 -0
  60. package/dist/lib/system-bus/host-ports.d.ts.map +1 -0
  61. package/dist/lib/system-bus/host-ports.js +3 -0
  62. package/dist/lib/system-bus/host-ports.js.map +1 -0
  63. package/dist/lib/system-bus/index.d.ts +11 -0
  64. package/dist/lib/system-bus/index.d.ts.map +1 -0
  65. package/dist/lib/system-bus/index.js +27 -0
  66. package/dist/lib/system-bus/index.js.map +1 -0
  67. package/dist/lib/system-bus/intent-registry.d.ts +14 -0
  68. package/dist/lib/system-bus/intent-registry.d.ts.map +1 -0
  69. package/dist/lib/system-bus/intent-registry.js +66 -0
  70. package/dist/lib/system-bus/intent-registry.js.map +1 -0
  71. package/dist/lib/system-bus/intents.d.ts +30 -0
  72. package/dist/lib/system-bus/intents.d.ts.map +1 -0
  73. package/dist/lib/system-bus/intents.js +3 -0
  74. package/dist/lib/system-bus/intents.js.map +1 -0
  75. package/dist/lib/system-bus/palette.d.ts +25 -0
  76. package/dist/lib/system-bus/palette.d.ts.map +1 -0
  77. package/dist/lib/system-bus/palette.js +3 -0
  78. package/dist/lib/system-bus/palette.js.map +1 -0
  79. package/dist/lib/system-bus/system-bus-builder.d.ts +10 -0
  80. package/dist/lib/system-bus/system-bus-builder.d.ts.map +1 -0
  81. package/dist/lib/system-bus/system-bus-builder.js +82 -0
  82. package/dist/lib/system-bus/system-bus-builder.js.map +1 -0
  83. package/dist/lib/system-bus/system-bus.d.ts +13 -0
  84. package/dist/lib/system-bus/system-bus.d.ts.map +1 -0
  85. package/dist/lib/system-bus/system-bus.js +3 -0
  86. package/dist/lib/system-bus/system-bus.js.map +1 -0
  87. package/dist/lib/system-bus/windows.d.ts +21 -0
  88. package/dist/lib/system-bus/windows.d.ts.map +1 -0
  89. package/dist/lib/system-bus/windows.js +3 -0
  90. package/dist/lib/system-bus/windows.js.map +1 -0
  91. package/dist/org-db/components/DbResultTable.d.ts +13 -0
  92. package/dist/org-db/components/DbResultTable.d.ts.map +1 -0
  93. package/dist/org-db/components/DbResultTable.js +58 -0
  94. package/dist/org-db/components/DbResultTable.js.map +1 -0
  95. package/dist/org-db/index.d.ts +2 -0
  96. package/dist/org-db/index.d.ts.map +1 -0
  97. package/dist/org-db/index.js +6 -0
  98. package/dist/org-db/index.js.map +1 -0
  99. package/dist/registry/index.d.ts +9 -0
  100. package/dist/registry/index.d.ts.map +1 -0
  101. package/dist/registry/index.js +25 -0
  102. package/dist/registry/index.js.map +1 -0
  103. package/dist/registry/lib/biome-slot.d.ts +7 -0
  104. package/dist/registry/lib/biome-slot.d.ts.map +1 -0
  105. package/dist/registry/lib/biome-slot.js +18 -0
  106. package/dist/registry/lib/biome-slot.js.map +1 -0
  107. package/dist/registry/lib/biomes-enabled-context.d.ts +3 -0
  108. package/dist/registry/lib/biomes-enabled-context.d.ts.map +1 -0
  109. package/dist/registry/lib/biomes-enabled-context.js +11 -0
  110. package/dist/registry/lib/biomes-enabled-context.js.map +1 -0
  111. package/dist/registry/lib/composition-validation-host.d.ts +3 -0
  112. package/dist/registry/lib/composition-validation-host.d.ts.map +1 -0
  113. package/dist/registry/lib/composition-validation-host.js +10 -0
  114. package/dist/registry/lib/composition-validation-host.js.map +1 -0
  115. package/dist/registry/lib/extension-points.d.ts +15 -0
  116. package/dist/registry/lib/extension-points.d.ts.map +1 -0
  117. package/dist/registry/lib/extension-points.js +17 -0
  118. package/dist/registry/lib/extension-points.js.map +1 -0
  119. package/dist/registry/lib/primitives/SessionEventCard.d.ts +14 -0
  120. package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -0
  121. package/dist/registry/lib/primitives/SessionEventCard.js +60 -0
  122. package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -0
  123. package/dist/registry/lib/primitives/SessionEventTimeline.d.ts +13 -0
  124. package/dist/registry/lib/primitives/SessionEventTimeline.d.ts.map +1 -0
  125. package/dist/registry/lib/primitives/SessionEventTimeline.js +35 -0
  126. package/dist/registry/lib/primitives/SessionEventTimeline.js.map +1 -0
  127. package/dist/registry/lib/primitives/SessionMutationBar.d.ts +10 -0
  128. package/dist/registry/lib/primitives/SessionMutationBar.d.ts.map +1 -0
  129. package/dist/registry/lib/primitives/SessionMutationBar.js +37 -0
  130. package/dist/registry/lib/primitives/SessionMutationBar.js.map +1 -0
  131. package/dist/registry/lib/primitives/SessionTabbedDrawer.d.ts +19 -0
  132. package/dist/registry/lib/primitives/SessionTabbedDrawer.d.ts.map +1 -0
  133. package/dist/registry/lib/primitives/SessionTabbedDrawer.js +75 -0
  134. package/dist/registry/lib/primitives/SessionTabbedDrawer.js.map +1 -0
  135. package/dist/registry/lib/primitives/index.d.ts +5 -0
  136. package/dist/registry/lib/primitives/index.d.ts.map +1 -0
  137. package/dist/registry/lib/primitives/index.js +21 -0
  138. package/dist/registry/lib/primitives/index.js.map +1 -0
  139. package/dist/registry/lib/session-context-builder.d.ts +13 -0
  140. package/dist/registry/lib/session-context-builder.d.ts.map +1 -0
  141. package/dist/registry/lib/session-context-builder.js +39 -0
  142. package/dist/registry/lib/session-context-builder.js.map +1 -0
  143. package/dist/registry/lib/session-context-provider.d.ts +10 -0
  144. package/dist/registry/lib/session-context-provider.d.ts.map +1 -0
  145. package/dist/registry/lib/session-context-provider.js +24 -0
  146. package/dist/registry/lib/session-context-provider.js.map +1 -0
  147. package/dist/registry/lib/session-selectors.d.ts +9 -0
  148. package/dist/registry/lib/session-selectors.d.ts.map +1 -0
  149. package/dist/registry/lib/session-selectors.js +149 -0
  150. package/dist/registry/lib/session-selectors.js.map +1 -0
  151. package/dist/session/comments/CommentRail.d.ts +20 -0
  152. package/dist/session/comments/CommentRail.d.ts.map +1 -0
  153. package/dist/session/comments/CommentRail.js +16 -0
  154. package/dist/session/comments/CommentRail.js.map +1 -0
  155. package/dist/session/index.d.ts +4 -0
  156. package/dist/session/index.d.ts.map +1 -0
  157. package/dist/session/index.js +10 -0
  158. package/dist/session/index.js.map +1 -0
  159. package/dist/session/lib/cn.d.ts +3 -0
  160. package/dist/session/lib/cn.d.ts.map +1 -0
  161. package/dist/session/lib/cn.js +9 -0
  162. package/dist/session/lib/cn.js.map +1 -0
  163. package/dist/session/shell/SessionWorkspaceShell.d.ts +16 -0
  164. package/dist/session/shell/SessionWorkspaceShell.d.ts.map +1 -0
  165. package/dist/session/shell/SessionWorkspaceShell.js +15 -0
  166. package/dist/session/shell/SessionWorkspaceShell.js.map +1 -0
  167. package/package.json +84 -0
  168. package/src/index.ts +2 -0
  169. package/src/lib/biome-host/biome-mode.ts +6 -0
  170. package/src/lib/biome-host/biome-registry.ts +245 -0
  171. package/src/lib/biome-host/composition-validation.ts +215 -0
  172. package/src/lib/biome-host/frontend-biome.ts +162 -0
  173. package/src/lib/biome-host/host-bridge.ts +178 -0
  174. package/src/lib/biome-host/host-sources.ts +41 -0
  175. package/src/lib/biome-host/index.ts +23 -0
  176. package/src/lib/biome-host/nav.ts +83 -0
  177. package/src/lib/biome-host/session-contributions.ts +293 -0
  178. package/src/lib/biome-host/session-profiles.ts +99 -0
  179. package/src/lib/system-bus/capability-invoke.ts +92 -0
  180. package/src/lib/system-bus/deeplink.ts +200 -0
  181. package/src/lib/system-bus/enums.ts +86 -0
  182. package/src/lib/system-bus/host-ports.ts +96 -0
  183. package/src/lib/system-bus/index.ts +16 -0
  184. package/src/lib/system-bus/intent-registry.ts +106 -0
  185. package/src/lib/system-bus/intents.ts +109 -0
  186. package/src/lib/system-bus/palette.ts +77 -0
  187. package/src/lib/system-bus/system-bus-builder.ts +157 -0
  188. package/src/lib/system-bus/system-bus.ts +37 -0
  189. package/src/lib/system-bus/windows.ts +51 -0
  190. package/src/org-db/components/DbResultTable.tsx +143 -0
  191. package/src/org-db/index.ts +1 -0
  192. package/src/registry/index.ts +8 -0
  193. package/src/registry/lib/biome-slot.tsx +47 -0
  194. package/src/registry/lib/biomes-enabled-context.ts +20 -0
  195. package/src/registry/lib/composition-validation-host.ts +37 -0
  196. package/src/registry/lib/extension-points.ts +134 -0
  197. package/src/registry/lib/primitives/SessionEventCard.tsx +138 -0
  198. package/src/registry/lib/primitives/SessionEventTimeline.tsx +89 -0
  199. package/src/registry/lib/primitives/SessionMutationBar.tsx +76 -0
  200. package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +155 -0
  201. package/src/registry/lib/primitives/index.ts +18 -0
  202. package/src/registry/lib/session-context-builder.ts +68 -0
  203. package/src/registry/lib/session-context-provider.tsx +50 -0
  204. package/src/registry/lib/session-selectors.ts +231 -0
  205. package/src/session/comments/CommentRail.tsx +164 -0
  206. package/src/session/index.ts +3 -0
  207. package/src/session/lib/cn.ts +11 -0
  208. package/src/session/shell/SessionWorkspaceShell.tsx +141 -0
@@ -0,0 +1,200 @@
1
+ import { DeeplinkTargetKind } from './enums';
2
+
3
+ /**
4
+ * Typed parse/resolve of the SystemBus `xema://` deeplink scheme.
5
+ *
6
+ * The deeplink scheme addresses *places in the running shell*. It is a
7
+ * distinct, narrower grammar from the kernel's `XemaObjectRef`
8
+ * (`xema://<scope>/<kind>/<slug>`): a deeplink's first segment is a
9
+ * {@link DeeplinkTargetKind} discriminator, not a scope path. The two
10
+ * schemes share the `xema://` scheme prefix but never the same body —
11
+ * `parseDeeplink` rejects anything whose head is not a known target kind,
12
+ * so an `XemaObjectRef` is only valid as a deeplink when wrapped as
13
+ * `xema://object/<XemaObjectRef-without-scheme>`.
14
+ *
15
+ * Grammar:
16
+ * xema://biome/<biomeId>/<route...>?<ctx> → BiomeRoute
17
+ * xema://object/<scope>/<kind>/<slug>[@<ver>] → Object
18
+ * xema://capability/<capability-ref>?<input> → Capability
19
+ * xema://window/<windowId> → Window
20
+ *
21
+ * Every parse is fail-fast: an unknown target kind, a missing required
22
+ * segment, or a malformed query is a {@link DeeplinkParseError}, never a
23
+ * silent fallback.
24
+ */
25
+
26
+ const DEEPLINK_SCHEME = 'xema://';
27
+
28
+ /** Structured, fail-fast error raised by `parseDeeplink`. */
29
+ export class DeeplinkParseError extends Error {
30
+ public readonly code = 'DEEPLINK_INVALID';
31
+ public readonly raw: string;
32
+ public readonly reason: string;
33
+
34
+ public constructor(raw: string, reason: string) {
35
+ super(`Invalid deeplink "${raw}": ${reason}`);
36
+ this.name = 'DeeplinkParseError';
37
+ this.raw = raw;
38
+ this.reason = reason;
39
+ }
40
+ }
41
+
42
+ /** A parsed `xema://biome/...` deeplink. */
43
+ export interface BiomeRouteDeeplink {
44
+ readonly kind: DeeplinkTargetKind.BiomeRoute;
45
+ readonly biomeId: string;
46
+ /** The route path within the biome, with a leading slash. */
47
+ readonly route: string;
48
+ /** Parsed `?ctx` query params, empty when absent. */
49
+ readonly ctx: Readonly<Record<string, string>>;
50
+ }
51
+
52
+ /** A parsed `xema://object/...` deeplink. The body is the scheme-less
53
+ * `XemaObjectRef` (e.g. `system/capability/kb.page.read@1`). */
54
+ export interface ObjectDeeplink {
55
+ readonly kind: DeeplinkTargetKind.Object;
56
+ /** Full `XemaObjectRef` (`xema://` re-prefixed) for handing to the
57
+ * kernel object resolver. */
58
+ readonly objectRef: `xema://${string}`;
59
+ }
60
+
61
+ /** A parsed `xema://capability/...` deeplink. */
62
+ export interface CapabilityDeeplink {
63
+ readonly kind: DeeplinkTargetKind.Capability;
64
+ /** The raw capability ref string (validated by the kernel
65
+ * `parseCapabilityRef` when the host launches it). */
66
+ readonly capabilityRef: string;
67
+ /** Parsed `?...` query params used as launch input, empty when absent. */
68
+ readonly input: Readonly<Record<string, string>>;
69
+ }
70
+
71
+ /** A parsed `xema://window/...` deeplink. */
72
+ export interface WindowDeeplink {
73
+ readonly kind: DeeplinkTargetKind.Window;
74
+ readonly windowId: string;
75
+ }
76
+
77
+ /** Discriminated union of every deeplink target. */
78
+ export type ParsedDeeplink =
79
+ | BiomeRouteDeeplink
80
+ | ObjectDeeplink
81
+ | CapabilityDeeplink
82
+ | WindowDeeplink;
83
+
84
+ function parseQuery(search: string): Record<string, string> {
85
+ const out: Record<string, string> = {};
86
+ if (search.length === 0) return out;
87
+ for (const pair of search.split('&')) {
88
+ if (pair.length === 0) continue;
89
+ const eq = pair.indexOf('=');
90
+ if (eq === -1) {
91
+ out[decodeURIComponent(pair)] = '';
92
+ } else {
93
+ out[decodeURIComponent(pair.slice(0, eq))] = decodeURIComponent(
94
+ pair.slice(eq + 1),
95
+ );
96
+ }
97
+ }
98
+ return out;
99
+ }
100
+
101
+ /**
102
+ * Parse a `xema://` deeplink string into a typed, discriminated
103
+ * {@link ParsedDeeplink}. Throws {@link DeeplinkParseError} on any
104
+ * malformed input — never returns null, never coerces.
105
+ */
106
+ export function parseDeeplink(raw: string): ParsedDeeplink {
107
+ if (typeof raw !== 'string' || !raw.startsWith(DEEPLINK_SCHEME)) {
108
+ throw new DeeplinkParseError(String(raw), `must start with "${DEEPLINK_SCHEME}"`);
109
+ }
110
+ const afterScheme = raw.slice(DEEPLINK_SCHEME.length);
111
+ if (afterScheme.length === 0) {
112
+ throw new DeeplinkParseError(raw, 'empty body after scheme');
113
+ }
114
+
115
+ const qIndex = afterScheme.indexOf('?');
116
+ const path = qIndex === -1 ? afterScheme : afterScheme.slice(0, qIndex);
117
+ const search = qIndex === -1 ? '' : afterScheme.slice(qIndex + 1);
118
+ const segments = path.split('/').filter((s) => s.length > 0);
119
+
120
+ const head = segments[0];
121
+ if (head === undefined) {
122
+ throw new DeeplinkParseError(raw, 'missing target-kind head segment');
123
+ }
124
+
125
+ switch (head) {
126
+ case DeeplinkTargetKind.BiomeRoute: {
127
+ const biomeId = segments[1];
128
+ if (biomeId === undefined) {
129
+ throw new DeeplinkParseError(raw, 'biome deeplink missing <biomeId>');
130
+ }
131
+ const routeSegments = segments.slice(2);
132
+ const route = `/${routeSegments.join('/')}`;
133
+ return {
134
+ kind: DeeplinkTargetKind.BiomeRoute,
135
+ biomeId,
136
+ route,
137
+ ctx: parseQuery(search),
138
+ };
139
+ }
140
+ case DeeplinkTargetKind.Object: {
141
+ const body = segments.slice(1).join('/');
142
+ if (body.length === 0) {
143
+ throw new DeeplinkParseError(raw, 'object deeplink missing <XemaObjectRef>');
144
+ }
145
+ return {
146
+ kind: DeeplinkTargetKind.Object,
147
+ objectRef: `${DEEPLINK_SCHEME}${body}` as `xema://${string}`,
148
+ };
149
+ }
150
+ case DeeplinkTargetKind.Capability: {
151
+ const capabilityRef = segments.slice(1).join('/');
152
+ if (capabilityRef.length === 0) {
153
+ throw new DeeplinkParseError(
154
+ raw,
155
+ 'capability deeplink missing <capability-ref>',
156
+ );
157
+ }
158
+ return {
159
+ kind: DeeplinkTargetKind.Capability,
160
+ capabilityRef,
161
+ input: parseQuery(search),
162
+ };
163
+ }
164
+ case DeeplinkTargetKind.Window: {
165
+ const windowId = segments[1];
166
+ if (windowId === undefined) {
167
+ throw new DeeplinkParseError(raw, 'window deeplink missing <windowId>');
168
+ }
169
+ return { kind: DeeplinkTargetKind.Window, windowId };
170
+ }
171
+ default:
172
+ throw new DeeplinkParseError(
173
+ raw,
174
+ `unknown deeplink target kind "${head}" (expected one of: ${Object.values(
175
+ DeeplinkTargetKind,
176
+ ).join(', ')})`,
177
+ );
178
+ }
179
+ }
180
+
181
+ /**
182
+ * The `deeplink` surface of the SystemBus. `parse` is pure
183
+ * (framework-agnostic) and re-exported from the runtime as
184
+ * {@link parseDeeplink}. `resolve` navigates the running shell to the
185
+ * parsed target; the host supplies the concrete navigation. Resolution
186
+ * is orchestration only — for a capability target the actual auth check
187
+ * happens when `capability.invoke` reaches the backend router.
188
+ */
189
+ export interface DeeplinkBus {
190
+ /** Parse a `xema://` deeplink string. Throws on malformed input. */
191
+ parse(raw: string): ParsedDeeplink;
192
+ /**
193
+ * Navigate the running shell to the deeplink target. The host
194
+ * implements the actual navigation (biome route push, object detail
195
+ * open, capability launch, window focus). Rejects if the target cannot
196
+ * be resolved (e.g. unknown biome / window) — fail-fast, no silent
197
+ * no-op.
198
+ */
199
+ resolve(target: string | ParsedDeeplink): Promise<void>;
200
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Closed sets for the `SystemBus` contract (Phase 6b).
3
+ *
4
+ * Every closed domain the SystemBus exposes is a real TypeScript enum so
5
+ * the contract is stable across hosts and serialises predictably. The
6
+ * `ObjectKind` domain deliberately REUSES the kernel's
7
+ * {@link XemaObjectKind} rather than re-declaring a parallel set — see
8
+ * `intents.ts`. The enums below describe SystemBus-specific closed
9
+ * domains that have no kernel equivalent.
10
+ */
11
+
12
+ /**
13
+ * The kind of target a `xema://` deeplink addresses. The deeplink scheme
14
+ * is intentionally narrower than the full `XemaObjectRef` grammar — a
15
+ * deeplink resolves to a *place in the running shell* (a biome route, an
16
+ * object detail page, a capability launcher, a window), not to every
17
+ * addressable object. New target kinds are added here by one-line kernel
18
+ * change; an unknown target kind is a fail-fast parse error.
19
+ */
20
+ export enum DeeplinkTargetKind {
21
+ /** `xema://biome/<biomeId>/<route>?<ctx>` — navigate to a biome route. */
22
+ BiomeRoute = 'biome-route',
23
+ /** `xema://object/<xema-object-ref>` — open an object's detail surface. */
24
+ Object = 'object',
25
+ /** `xema://capability/<capability-ref>?<input>` — launch a capability. */
26
+ Capability = 'capability',
27
+ /** `xema://window/<windowId>` — focus an already-open window. */
28
+ Window = 'window',
29
+ }
30
+
31
+ /**
32
+ * Window operations the host window manager exposes through
33
+ * `SystemBus.windows`. Closed set — a host that cannot honour an op
34
+ * (e.g. a single-window mobile shell) fails fast rather than silently
35
+ * degrading; the SystemBus never invents a partial window model.
36
+ */
37
+ export enum WindowOp {
38
+ Open = 'open',
39
+ Focus = 'focus',
40
+ Close = 'close',
41
+ Arrange = 'arrange',
42
+ }
43
+
44
+ /**
45
+ * Layout arrangements a host may apply to its open windows when
46
+ * `WindowOp.Arrange` is requested. Closed set so the contract is stable
47
+ * across desktop / tiling / tabbed shells.
48
+ */
49
+ export enum WindowArrangement {
50
+ /** Tile all open windows in a grid. */
51
+ Tile = 'tile',
52
+ /** Stack windows with the focused one on top. */
53
+ Stack = 'stack',
54
+ /** Cascade windows with a fixed offset. */
55
+ Cascade = 'cascade',
56
+ /** Maximise the focused window, hide the rest. */
57
+ Maximize = 'maximize',
58
+ }
59
+
60
+ /**
61
+ * The kind of action an intent provides. An intent contributes either a
62
+ * cross-biome *capability* launch ("Send to Slack" → a capability ref)
63
+ * or an in-shell *deeplink* navigation ("Open with Document Buddy" → a
64
+ * biome route). Closed set — these are the only two ways one biome can
65
+ * hand an object to another without importing it.
66
+ */
67
+ export enum ActionRefKind {
68
+ /** Resolves to a `CapabilityRef` invoked through `capability.invoke`. */
69
+ Capability = 'capability',
70
+ /** Resolves to a `xema://` deeplink navigated through `deeplink`. */
71
+ Deeplink = 'deeplink',
72
+ }
73
+
74
+ /**
75
+ * The source that contributed a palette entry. The palette is the union
76
+ * of (a) every capability the actor may invoke (sourced from
77
+ * `capability-registry-api`) and (b) every registered cross-biome intent
78
+ * action. This enum tags each entry's provenance so the UI can group /
79
+ * filter and so the host knows which subsystem resolves the entry.
80
+ */
81
+ export enum PaletteEntrySource {
82
+ /** Backed by a capability descriptor from `capability-registry-api`. */
83
+ Capability = 'capability',
84
+ /** Backed by a registered intent (`intents.register`). */
85
+ Intent = 'intent',
86
+ }
@@ -0,0 +1,96 @@
1
+ import type { CapabilityRef } from '@xemahq/kernel-contracts/capability';
2
+ import type {
3
+ CapabilityInvokeMeta,
4
+ CapabilityInvokeResult,
5
+ } from './capability-invoke';
6
+ import type { PaletteEntry, PaletteQuery } from './palette';
7
+ import type {
8
+ OpenWindowRequest,
9
+ WindowId,
10
+ WindowState,
11
+ } from './windows';
12
+ import type { WindowArrangement } from './enums';
13
+
14
+ /**
15
+ * Framework-agnostic host ports the concrete host implements so the
16
+ * SystemBus sub-buses can be constructed by the framework-agnostic
17
+ * `buildSystemBus` assembler (shipped by this kernel).
18
+ *
19
+ * These ports carry NO React, NO router, NO transport client — they are
20
+ * the seam between "the SystemBus contract" (kernel) and "this host's
21
+ * real backend + shell" (the Next.js App Router host). The assembler wires a
22
+ * `SystemBus` out of `IntentRegistry` + `parseDeeplink` (both already in
23
+ * the kernel) PLUS these three host-supplied ports. A missing port is a
24
+ * wiring bug and MUST fail fast at bridge construction — never a silent
25
+ * no-op.
26
+ *
27
+ * None of these ports authorize. `CapabilityInvoker` relays to the
28
+ * backend capability-router (the single authority); `PaletteSource`
29
+ * indexes an already-actor-scoped registry; `WindowManager` is pure
30
+ * shell orchestration; `DeeplinkResolver` navigates. Policy lives in the
31
+ * backend, full stop.
32
+ */
33
+
34
+ /**
35
+ * Port the host implements to relay a `capability.invoke` to the backend
36
+ * capability-router. The adapter wraps this into `SystemBus.capability`.
37
+ *
38
+ * The host implementation POSTs the router's invoke endpoint with the
39
+ * Gate-E `ExecutionContext` built from {@link CapabilityInvokeMeta}. It
40
+ * MUST surface a policy denial as the typed result (`output === undefined`
41
+ * + populated `auditId`) and MUST reject on transport failure (fail-fast).
42
+ * It adds NO authorization of its own.
43
+ */
44
+ export interface CapabilityInvoker {
45
+ invoke<TInput = unknown, TOutput = unknown>(
46
+ ref: CapabilityRef,
47
+ input: TInput,
48
+ meta: CapabilityInvokeMeta,
49
+ ): Promise<CapabilityInvokeResult<TOutput>>;
50
+ }
51
+
52
+ /**
53
+ * Port the host implements to source the capability half of the command
54
+ * palette from `capability-registry-api` (already actor-scoped
55
+ * server-side). The intent half is sourced by the adapter from the
56
+ * SystemBus `IntentRegistry`; the adapter merges both behind
57
+ * `SystemBus.palette`.
58
+ *
59
+ * `queryCapabilities` returns ONLY capability-backed entries; the adapter
60
+ * unions them with intent entries and applies the host ranker.
61
+ * `subscribe` fires when the capability source refreshes so a palette
62
+ * overlay can re-query without polling. Returns an unsubscribe fn.
63
+ */
64
+ export interface PaletteSource {
65
+ queryCapabilities(query: PaletteQuery): Promise<readonly PaletteEntry[]>;
66
+ subscribe(listener: () => void): () => void;
67
+ }
68
+
69
+ /**
70
+ * Port the host implements to realise the window plane. The adapter
71
+ * exposes it verbatim as `SystemBus.windows`. Every op maps to a closed
72
+ * {@link WindowArrangement}/`WindowOp`; an op a host cannot honour MUST
73
+ * reject fail-fast rather than silently no-op.
74
+ */
75
+ export interface WindowManager {
76
+ open(request: OpenWindowRequest): Promise<WindowId>;
77
+ focus(id: WindowId): Promise<void>;
78
+ close(id: WindowId): Promise<void>;
79
+ arrange(arrangement: WindowArrangement): Promise<void>;
80
+ list(): readonly WindowState[];
81
+ subscribe(listener: () => void): () => void;
82
+ }
83
+
84
+ /**
85
+ * Port the host implements to navigate the running shell to a parsed
86
+ * deeplink target. The adapter combines kernel `parseDeeplink` (pure)
87
+ * with this host-supplied `resolve` to build `SystemBus.deeplink`.
88
+ *
89
+ * `resolve` receives the ALREADY-PARSED, validated target (the adapter
90
+ * parses first, so a malformed deeplink fails fast before the host sees
91
+ * it). It MUST reject when the target cannot be resolved (unknown biome /
92
+ * window) — fail-fast, never a silent no-op.
93
+ */
94
+ export interface DeeplinkResolver {
95
+ resolve(target: import('./deeplink').ParsedDeeplink): Promise<void>;
96
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * SystemBus — the framework-agnostic OS-orchestration contract plane
3
+ * (Phase 6b). Pure types + a small framework-agnostic runtime
4
+ * ({@link IntentRegistry} + {@link parseDeeplink}). No React, no Vite, no
5
+ * Next.js, no React-Router, no transport client.
6
+ */
7
+ export * from './enums';
8
+ export * from './capability-invoke';
9
+ export * from './intents';
10
+ export * from './intent-registry';
11
+ export * from './deeplink';
12
+ export * from './palette';
13
+ export * from './windows';
14
+ export * from './host-ports';
15
+ export * from './system-bus';
16
+ export * from './system-bus-builder';
@@ -0,0 +1,106 @@
1
+ import {
2
+ parseXemaObjectRef,
3
+ type XemaObjectKind,
4
+ type XemaObjectRef,
5
+ } from '@xemahq/kernel-contracts/object';
6
+ import { ActionRefKind } from './enums';
7
+ import type {
8
+ ActionRef,
9
+ IntentBus,
10
+ IntentRegistration,
11
+ ResolvedIntent,
12
+ } from './intents';
13
+
14
+ const VALID_ACTION_KINDS: ReadonlySet<ActionRefKind> = new Set([
15
+ ActionRefKind.Capability,
16
+ ActionRefKind.Deeplink,
17
+ ]);
18
+
19
+ function assertValidRegistration(registration: IntentRegistration): void {
20
+ if (!registration.biomeId) {
21
+ throw new Error('[xema-ui-kernel] IntentRegistration.biomeId is required.');
22
+ }
23
+ for (const action of registration.provides) {
24
+ if (!VALID_ACTION_KINDS.has(action.kind)) {
25
+ throw new Error(
26
+ `[xema-ui-kernel] IntentRegistration from "${registration.biomeId}" ` +
27
+ `has an ActionRef with unknown kind "${(action as ActionRef).kind}". ` +
28
+ `Expected one of: ${[...VALID_ACTION_KINDS].join(', ')}.`,
29
+ );
30
+ }
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Framework-agnostic, in-memory implementation of {@link IntentBus}.
36
+ *
37
+ * No React, no router — a plain registry a host can instantiate once and
38
+ * expose through `SystemBus.intents`. Registration order is preserved so
39
+ * `resolve` returns actions deterministically (first-registered first).
40
+ * Re-registering the same `biomeId` REPLACES its prior contribution while
41
+ * keeping its original ordering slot (HMR / re-bootstrap safe).
42
+ *
43
+ * Pure orchestration: it indexes and resolves contributions and performs
44
+ * no authorization. Whether the actor may run a resolved action is decided
45
+ * when the action fires.
46
+ */
47
+ export class IntentRegistry implements IntentBus {
48
+ /** Insertion-ordered map: biomeId → its registration. */
49
+ private readonly registrations = new Map<string, IntentRegistration>();
50
+ private readonly listeners = new Set<() => void>();
51
+
52
+ register(registration: IntentRegistration): void {
53
+ assertValidRegistration(registration);
54
+ // Map preserves first-insertion order; deleting then setting on an
55
+ // update would move it to the end, so only delete when truly replacing.
56
+ this.registrations.set(registration.biomeId, registration);
57
+ this.notify();
58
+ }
59
+
60
+ unregister(biomeId: string): boolean {
61
+ const removed = this.registrations.delete(biomeId);
62
+ if (removed) this.notify();
63
+ return removed;
64
+ }
65
+
66
+ resolve(kind: XemaObjectKind): readonly ResolvedIntent[] {
67
+ const out: ResolvedIntent[] = [];
68
+ for (const registration of this.registrations.values()) {
69
+ if (!registration.handles.includes(kind)) continue;
70
+ for (const action of registration.provides) {
71
+ out.push({ biomeId: registration.biomeId, action });
72
+ }
73
+ }
74
+ return out;
75
+ }
76
+
77
+ resolveForObject(ref: XemaObjectRef): readonly ResolvedIntent[] {
78
+ // Fail-fast on a malformed ref rather than returning an empty menu —
79
+ // a bad ref is a wiring bug, not "no handlers".
80
+ const { kind } = parseXemaObjectRef(ref);
81
+ return this.resolve(kind);
82
+ }
83
+
84
+ /**
85
+ * Insertion-ordered snapshot of every current registration. The command
86
+ * palette needs every provided action regardless of object kind (unlike
87
+ * `resolve`, which filters by a single kind), so it reads this snapshot
88
+ * and flattens `provides`. Returns a defensive shallow copy — callers
89
+ * MUST NOT mutate the registry through it.
90
+ */
91
+ snapshot(): readonly IntentRegistration[] {
92
+ return [...this.registrations.values()];
93
+ }
94
+
95
+ /** Subscribe to registry changes; returns an unsubscribe fn. */
96
+ subscribe(listener: () => void): () => void {
97
+ this.listeners.add(listener);
98
+ return () => {
99
+ this.listeners.delete(listener);
100
+ };
101
+ }
102
+
103
+ private notify(): void {
104
+ for (const listener of this.listeners) listener();
105
+ }
106
+ }
@@ -0,0 +1,109 @@
1
+ import type { CapabilityRef } from '@xemahq/kernel-contracts/capability';
2
+ import type { XemaObjectKind, XemaObjectRef } from '@xemahq/kernel-contracts/object';
3
+ import { ActionRefKind } from './enums';
4
+
5
+ /**
6
+ * Cross-biome intent registry — the "Open with… / Send to…" plane.
7
+ *
8
+ * A biome registers the object kinds it can *handle* and the actions it
9
+ * *provides*. When the user invokes "Open with… / Send to…" on an object,
10
+ * `intents.resolve(kind)` returns every action any biome registered for
11
+ * that kind. This is how one biome hands an object to another WITHOUT
12
+ * importing it — the macOS "Open With" / Share-Sheet analogy from the
13
+ * plan-of-record (§12.3).
14
+ *
15
+ * The registry is pure orchestration: it indexes contributions and
16
+ * resolves them. It performs NO authorization — whether the actor may
17
+ * actually run a resolved action is decided when the action fires
18
+ * (`capability.invoke` → backend router for capability actions; the host
19
+ * navigation guard for deeplink actions).
20
+ */
21
+
22
+ /**
23
+ * A resolvable cross-biome action. Discriminated on {@link ActionRefKind}
24
+ * so a single resolved list can mix capability launches and deeplink
25
+ * navigations. Closed via the enum — there is no third way to hand an
26
+ * object across biomes.
27
+ */
28
+ export type ActionRef =
29
+ | {
30
+ readonly kind: ActionRefKind.Capability;
31
+ /** Stable id, unique within the providing biome. */
32
+ readonly id: string;
33
+ /** Human label rendered in the "Send to…" menu. */
34
+ readonly label: string;
35
+ /** The capability the action invokes. */
36
+ readonly capabilityRef: CapabilityRef;
37
+ }
38
+ | {
39
+ readonly kind: ActionRefKind.Deeplink;
40
+ /** Stable id, unique within the providing biome. */
41
+ readonly id: string;
42
+ /** Human label rendered in the "Open with…" menu. */
43
+ readonly label: string;
44
+ /**
45
+ * Deeplink template the action navigates to. The host substitutes
46
+ * the selected object's ref into the template before navigating
47
+ * (e.g. `xema://biome/document-buddy/edit?object={ref}`).
48
+ */
49
+ readonly deeplinkTemplate: string;
50
+ };
51
+
52
+ /**
53
+ * One biome's intent contribution. `handles` is the closed set of object
54
+ * kinds this biome can receive; `provides` is the set of actions it
55
+ * offers for those kinds. Both reference the kernel's {@link XemaObjectKind}
56
+ * so the contract never drifts from the canonical object universe.
57
+ */
58
+ export interface IntentRegistration {
59
+ /** Biome that owns this registration (provenance + dedup key). */
60
+ readonly biomeId: string;
61
+ /** Object kinds this biome can be a target of ("Open with…"). */
62
+ readonly handles: readonly XemaObjectKind[];
63
+ /** Actions this biome provides for the handled kinds. */
64
+ readonly provides: readonly ActionRef[];
65
+ }
66
+
67
+ /**
68
+ * A resolved intent action for a specific object kind — an {@link ActionRef}
69
+ * tagged with the biome that provides it. Returned by `intents.resolve`.
70
+ */
71
+ export interface ResolvedIntent {
72
+ readonly biomeId: string;
73
+ readonly action: ActionRef;
74
+ }
75
+
76
+ /**
77
+ * The `intents` surface of the SystemBus. Framework-agnostic runtime
78
+ * registry — no React, no router. Hosts drive it at biome bootstrap
79
+ * (`register`) and at "Open with…" menu build time (`resolve`).
80
+ */
81
+ export interface IntentBus {
82
+ /**
83
+ * Register a biome's intent contribution. Idempotent per `biomeId`:
84
+ * re-registering the same biome REPLACES its prior contribution (HMR /
85
+ * re-bootstrap safe). Fail-fast on a malformed registration (e.g. an
86
+ * `ActionRef` whose `kind` is outside {@link ActionRefKind}).
87
+ */
88
+ register(registration: IntentRegistration): void;
89
+
90
+ /**
91
+ * Remove a biome's intent contribution (admin disable / unregister).
92
+ * Returns `true` if a registration was removed.
93
+ */
94
+ unregister(biomeId: string): boolean;
95
+
96
+ /**
97
+ * Resolve every action any biome registered for `kind`, in stable
98
+ * registration order. Returns an empty array when no biome handles the
99
+ * kind — the caller renders an empty "Open with…" menu, never an error.
100
+ */
101
+ resolve(kind: XemaObjectKind): readonly ResolvedIntent[];
102
+
103
+ /**
104
+ * Resolve actions for a concrete object ref. Convenience over
105
+ * `resolve(kind)` that parses the kind out of the `XemaObjectRef` and
106
+ * is the entry point the "Open with…" menu uses on a selected object.
107
+ */
108
+ resolveForObject(ref: XemaObjectRef): readonly ResolvedIntent[];
109
+ }
@@ -0,0 +1,77 @@
1
+ import type { CapabilityRef } from '@xemahq/kernel-contracts/capability';
2
+ import { PaletteEntrySource } from './enums';
3
+
4
+ /**
5
+ * Command-palette plane of the SystemBus.
6
+ *
7
+ * The palette is the UNION of two sources (tagged by
8
+ * {@link PaletteEntrySource}):
9
+ * 1. every capability the actor may invoke — sourced from
10
+ * `capability-registry-api` (the host's capability-registry client);
11
+ * 2. every registered cross-biome intent action — sourced from the
12
+ * SystemBus `intents` registry.
13
+ *
14
+ * The palette performs NO authorization. The capability source is already
15
+ * actor-scoped server-side (the registry returns only capabilities the
16
+ * subject can see); selecting an entry routes through `capability.invoke`,
17
+ * where the backend router makes the final allow/deny decision. The
18
+ * palette is pure presentation orchestration.
19
+ */
20
+
21
+ /** A palette entry backed by a capability descriptor. */
22
+ export interface CapabilityPaletteEntry {
23
+ readonly source: PaletteEntrySource.Capability;
24
+ /** Stable id (the capability ref doubles as the id). */
25
+ readonly id: string;
26
+ /** Human label rendered in the palette row. */
27
+ readonly label: string;
28
+ /** Optional one-line description from the capability descriptor. */
29
+ readonly summary?: string;
30
+ /** The capability the entry invokes when selected. */
31
+ readonly capabilityRef: CapabilityRef;
32
+ }
33
+
34
+ /** A palette entry backed by a registered cross-biome intent. */
35
+ export interface IntentPaletteEntry {
36
+ readonly source: PaletteEntrySource.Intent;
37
+ /** Stable id, unique across registered intents. */
38
+ readonly id: string;
39
+ /** Human label rendered in the palette row. */
40
+ readonly label: string;
41
+ /** Biome that provides the intent action. */
42
+ readonly biomeId: string;
43
+ }
44
+
45
+ /** Discriminated union of palette entries. */
46
+ export type PaletteEntry = CapabilityPaletteEntry | IntentPaletteEntry;
47
+
48
+ /**
49
+ * Query against the palette. `text` is matched by the host's own ranker
50
+ * (the contract does not prescribe a fuzzy algorithm). `sources` narrows
51
+ * to a subset of {@link PaletteEntrySource}; omitted means "all sources".
52
+ */
53
+ export interface PaletteQuery {
54
+ readonly text: string;
55
+ readonly sources?: readonly PaletteEntrySource[];
56
+ }
57
+
58
+ /**
59
+ * The `palette` surface of the SystemBus. Framework-agnostic — no React.
60
+ * The host wires a capability-registry client + the SystemBus intent
61
+ * registry behind `query`. `subscribe` lets a palette UI re-render when
62
+ * either source changes (a biome registers an intent, the registry
63
+ * refreshes).
64
+ */
65
+ export interface PaletteBus {
66
+ /**
67
+ * Resolve palette entries matching `query`, ranked by the host. Async
68
+ * because the capability source may hit `capability-registry-api`.
69
+ */
70
+ query(query: PaletteQuery): Promise<readonly PaletteEntry[]>;
71
+ /**
72
+ * Subscribe to palette-source changes. Returns an unsubscribe fn. Fires
73
+ * when a biome registers/unregisters an intent or the capability source
74
+ * is refreshed — so a palette overlay can re-query without polling.
75
+ */
76
+ subscribe(listener: () => void): () => void;
77
+ }