@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.
- package/LICENSE +17 -0
- package/README.md +72 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/biome-host/biome-mode.d.ts +2 -0
- package/dist/lib/biome-host/biome-mode.d.ts.map +1 -0
- package/dist/lib/biome-host/biome-mode.js +3 -0
- package/dist/lib/biome-host/biome-mode.js.map +1 -0
- package/dist/lib/biome-host/biome-registry.d.ts +30 -0
- package/dist/lib/biome-host/biome-registry.d.ts.map +1 -0
- package/dist/lib/biome-host/biome-registry.js +134 -0
- package/dist/lib/biome-host/biome-registry.js.map +1 -0
- package/dist/lib/biome-host/composition-validation.d.ts +22 -0
- package/dist/lib/biome-host/composition-validation.d.ts.map +1 -0
- package/dist/lib/biome-host/composition-validation.js +127 -0
- package/dist/lib/biome-host/composition-validation.js.map +1 -0
- package/dist/lib/biome-host/frontend-biome.d.ts +47 -0
- package/dist/lib/biome-host/frontend-biome.d.ts.map +1 -0
- package/dist/lib/biome-host/frontend-biome.js +3 -0
- package/dist/lib/biome-host/frontend-biome.js.map +1 -0
- package/dist/lib/biome-host/host-bridge.d.ts +55 -0
- package/dist/lib/biome-host/host-bridge.d.ts.map +1 -0
- package/dist/lib/biome-host/host-bridge.js +16 -0
- package/dist/lib/biome-host/host-bridge.js.map +1 -0
- package/dist/lib/biome-host/host-sources.d.ts +12 -0
- package/dist/lib/biome-host/host-sources.d.ts.map +1 -0
- package/dist/lib/biome-host/host-sources.js +3 -0
- package/dist/lib/biome-host/host-sources.js.map +1 -0
- package/dist/lib/biome-host/index.d.ts +10 -0
- package/dist/lib/biome-host/index.d.ts.map +1 -0
- package/dist/lib/biome-host/index.js +26 -0
- package/dist/lib/biome-host/index.js.map +1 -0
- package/dist/lib/biome-host/nav.d.ts +17 -0
- package/dist/lib/biome-host/nav.d.ts.map +1 -0
- package/dist/lib/biome-host/nav.js +52 -0
- package/dist/lib/biome-host/nav.js.map +1 -0
- package/dist/lib/biome-host/session-contributions.d.ts +143 -0
- package/dist/lib/biome-host/session-contributions.d.ts.map +1 -0
- package/dist/lib/biome-host/session-contributions.js +3 -0
- package/dist/lib/biome-host/session-contributions.js.map +1 -0
- package/dist/lib/biome-host/session-profiles.d.ts +18 -0
- package/dist/lib/biome-host/session-profiles.d.ts.map +1 -0
- package/dist/lib/biome-host/session-profiles.js +39 -0
- package/dist/lib/biome-host/session-profiles.js.map +1 -0
- package/dist/lib/system-bus/capability-invoke.d.ts +18 -0
- package/dist/lib/system-bus/capability-invoke.d.ts.map +1 -0
- package/dist/lib/system-bus/capability-invoke.js +3 -0
- package/dist/lib/system-bus/capability-invoke.js.map +1 -0
- package/dist/lib/system-bus/deeplink.d.ts +33 -0
- package/dist/lib/system-bus/deeplink.d.ts.map +1 -0
- package/dist/lib/system-bus/deeplink.js +99 -0
- package/dist/lib/system-bus/deeplink.js.map +1 -0
- package/dist/lib/system-bus/enums.d.ts +27 -0
- package/dist/lib/system-bus/enums.d.ts.map +1 -0
- package/dist/lib/system-bus/enums.js +35 -0
- package/dist/lib/system-bus/enums.js.map +1 -0
- package/dist/lib/system-bus/host-ports.d.ts +24 -0
- package/dist/lib/system-bus/host-ports.d.ts.map +1 -0
- package/dist/lib/system-bus/host-ports.js +3 -0
- package/dist/lib/system-bus/host-ports.js.map +1 -0
- package/dist/lib/system-bus/index.d.ts +11 -0
- package/dist/lib/system-bus/index.d.ts.map +1 -0
- package/dist/lib/system-bus/index.js +27 -0
- package/dist/lib/system-bus/index.js.map +1 -0
- package/dist/lib/system-bus/intent-registry.d.ts +14 -0
- package/dist/lib/system-bus/intent-registry.d.ts.map +1 -0
- package/dist/lib/system-bus/intent-registry.js +66 -0
- package/dist/lib/system-bus/intent-registry.js.map +1 -0
- package/dist/lib/system-bus/intents.d.ts +30 -0
- package/dist/lib/system-bus/intents.d.ts.map +1 -0
- package/dist/lib/system-bus/intents.js +3 -0
- package/dist/lib/system-bus/intents.js.map +1 -0
- package/dist/lib/system-bus/palette.d.ts +25 -0
- package/dist/lib/system-bus/palette.d.ts.map +1 -0
- package/dist/lib/system-bus/palette.js +3 -0
- package/dist/lib/system-bus/palette.js.map +1 -0
- package/dist/lib/system-bus/system-bus-builder.d.ts +10 -0
- package/dist/lib/system-bus/system-bus-builder.d.ts.map +1 -0
- package/dist/lib/system-bus/system-bus-builder.js +82 -0
- package/dist/lib/system-bus/system-bus-builder.js.map +1 -0
- package/dist/lib/system-bus/system-bus.d.ts +13 -0
- package/dist/lib/system-bus/system-bus.d.ts.map +1 -0
- package/dist/lib/system-bus/system-bus.js +3 -0
- package/dist/lib/system-bus/system-bus.js.map +1 -0
- package/dist/lib/system-bus/windows.d.ts +21 -0
- package/dist/lib/system-bus/windows.d.ts.map +1 -0
- package/dist/lib/system-bus/windows.js +3 -0
- package/dist/lib/system-bus/windows.js.map +1 -0
- package/dist/org-db/components/DbResultTable.d.ts +13 -0
- package/dist/org-db/components/DbResultTable.d.ts.map +1 -0
- package/dist/org-db/components/DbResultTable.js +58 -0
- package/dist/org-db/components/DbResultTable.js.map +1 -0
- package/dist/org-db/index.d.ts +2 -0
- package/dist/org-db/index.d.ts.map +1 -0
- package/dist/org-db/index.js +6 -0
- package/dist/org-db/index.js.map +1 -0
- package/dist/registry/index.d.ts +9 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +25 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/lib/biome-slot.d.ts +7 -0
- package/dist/registry/lib/biome-slot.d.ts.map +1 -0
- package/dist/registry/lib/biome-slot.js +18 -0
- package/dist/registry/lib/biome-slot.js.map +1 -0
- package/dist/registry/lib/biomes-enabled-context.d.ts +3 -0
- package/dist/registry/lib/biomes-enabled-context.d.ts.map +1 -0
- package/dist/registry/lib/biomes-enabled-context.js +11 -0
- package/dist/registry/lib/biomes-enabled-context.js.map +1 -0
- package/dist/registry/lib/composition-validation-host.d.ts +3 -0
- package/dist/registry/lib/composition-validation-host.d.ts.map +1 -0
- package/dist/registry/lib/composition-validation-host.js +10 -0
- package/dist/registry/lib/composition-validation-host.js.map +1 -0
- package/dist/registry/lib/extension-points.d.ts +15 -0
- package/dist/registry/lib/extension-points.d.ts.map +1 -0
- package/dist/registry/lib/extension-points.js +17 -0
- package/dist/registry/lib/extension-points.js.map +1 -0
- package/dist/registry/lib/primitives/SessionEventCard.d.ts +14 -0
- package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -0
- package/dist/registry/lib/primitives/SessionEventCard.js +60 -0
- package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -0
- package/dist/registry/lib/primitives/SessionEventTimeline.d.ts +13 -0
- package/dist/registry/lib/primitives/SessionEventTimeline.d.ts.map +1 -0
- package/dist/registry/lib/primitives/SessionEventTimeline.js +35 -0
- package/dist/registry/lib/primitives/SessionEventTimeline.js.map +1 -0
- package/dist/registry/lib/primitives/SessionMutationBar.d.ts +10 -0
- package/dist/registry/lib/primitives/SessionMutationBar.d.ts.map +1 -0
- package/dist/registry/lib/primitives/SessionMutationBar.js +37 -0
- package/dist/registry/lib/primitives/SessionMutationBar.js.map +1 -0
- package/dist/registry/lib/primitives/SessionTabbedDrawer.d.ts +19 -0
- package/dist/registry/lib/primitives/SessionTabbedDrawer.d.ts.map +1 -0
- package/dist/registry/lib/primitives/SessionTabbedDrawer.js +75 -0
- package/dist/registry/lib/primitives/SessionTabbedDrawer.js.map +1 -0
- package/dist/registry/lib/primitives/index.d.ts +5 -0
- package/dist/registry/lib/primitives/index.d.ts.map +1 -0
- package/dist/registry/lib/primitives/index.js +21 -0
- package/dist/registry/lib/primitives/index.js.map +1 -0
- package/dist/registry/lib/session-context-builder.d.ts +13 -0
- package/dist/registry/lib/session-context-builder.d.ts.map +1 -0
- package/dist/registry/lib/session-context-builder.js +39 -0
- package/dist/registry/lib/session-context-builder.js.map +1 -0
- package/dist/registry/lib/session-context-provider.d.ts +10 -0
- package/dist/registry/lib/session-context-provider.d.ts.map +1 -0
- package/dist/registry/lib/session-context-provider.js +24 -0
- package/dist/registry/lib/session-context-provider.js.map +1 -0
- package/dist/registry/lib/session-selectors.d.ts +9 -0
- package/dist/registry/lib/session-selectors.d.ts.map +1 -0
- package/dist/registry/lib/session-selectors.js +149 -0
- package/dist/registry/lib/session-selectors.js.map +1 -0
- package/dist/session/comments/CommentRail.d.ts +20 -0
- package/dist/session/comments/CommentRail.d.ts.map +1 -0
- package/dist/session/comments/CommentRail.js +16 -0
- package/dist/session/comments/CommentRail.js.map +1 -0
- package/dist/session/index.d.ts +4 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +10 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/lib/cn.d.ts +3 -0
- package/dist/session/lib/cn.d.ts.map +1 -0
- package/dist/session/lib/cn.js +9 -0
- package/dist/session/lib/cn.js.map +1 -0
- package/dist/session/shell/SessionWorkspaceShell.d.ts +16 -0
- package/dist/session/shell/SessionWorkspaceShell.d.ts.map +1 -0
- package/dist/session/shell/SessionWorkspaceShell.js +15 -0
- package/dist/session/shell/SessionWorkspaceShell.js.map +1 -0
- package/package.json +84 -0
- package/src/index.ts +2 -0
- package/src/lib/biome-host/biome-mode.ts +6 -0
- package/src/lib/biome-host/biome-registry.ts +245 -0
- package/src/lib/biome-host/composition-validation.ts +215 -0
- package/src/lib/biome-host/frontend-biome.ts +162 -0
- package/src/lib/biome-host/host-bridge.ts +178 -0
- package/src/lib/biome-host/host-sources.ts +41 -0
- package/src/lib/biome-host/index.ts +23 -0
- package/src/lib/biome-host/nav.ts +83 -0
- package/src/lib/biome-host/session-contributions.ts +293 -0
- package/src/lib/biome-host/session-profiles.ts +99 -0
- package/src/lib/system-bus/capability-invoke.ts +92 -0
- package/src/lib/system-bus/deeplink.ts +200 -0
- package/src/lib/system-bus/enums.ts +86 -0
- package/src/lib/system-bus/host-ports.ts +96 -0
- package/src/lib/system-bus/index.ts +16 -0
- package/src/lib/system-bus/intent-registry.ts +106 -0
- package/src/lib/system-bus/intents.ts +109 -0
- package/src/lib/system-bus/palette.ts +77 -0
- package/src/lib/system-bus/system-bus-builder.ts +157 -0
- package/src/lib/system-bus/system-bus.ts +37 -0
- package/src/lib/system-bus/windows.ts +51 -0
- package/src/org-db/components/DbResultTable.tsx +143 -0
- package/src/org-db/index.ts +1 -0
- package/src/registry/index.ts +8 -0
- package/src/registry/lib/biome-slot.tsx +47 -0
- package/src/registry/lib/biomes-enabled-context.ts +20 -0
- package/src/registry/lib/composition-validation-host.ts +37 -0
- package/src/registry/lib/extension-points.ts +134 -0
- package/src/registry/lib/primitives/SessionEventCard.tsx +138 -0
- package/src/registry/lib/primitives/SessionEventTimeline.tsx +89 -0
- package/src/registry/lib/primitives/SessionMutationBar.tsx +76 -0
- package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +155 -0
- package/src/registry/lib/primitives/index.ts +18 -0
- package/src/registry/lib/session-context-builder.ts +68 -0
- package/src/registry/lib/session-context-provider.tsx +50 -0
- package/src/registry/lib/session-selectors.ts +231 -0
- package/src/session/comments/CommentRail.tsx +164 -0
- package/src/session/index.ts +3 -0
- package/src/session/lib/cn.ts +11 -0
- package/src/session/shell/SessionWorkspaceShell.tsx +141 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
import { biomeRegistry } from './biome-registry';
|
|
4
|
+
|
|
5
|
+
export interface NavItem {
|
|
6
|
+
readonly key: string;
|
|
7
|
+
readonly label: string;
|
|
8
|
+
readonly icon?: ComponentType<{ className?: string | undefined }> | undefined;
|
|
9
|
+
readonly weight?: number | undefined;
|
|
10
|
+
/** True when this item came from a biome contribution. */
|
|
11
|
+
readonly fromBiome?: boolean | undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface NavSection {
|
|
15
|
+
readonly heading?: string | undefined;
|
|
16
|
+
readonly items: readonly NavItem[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Merge the host's core nav sections with biome nav contributions. Biome
|
|
21
|
+
* items are bucketed into the section whose `heading` matches their
|
|
22
|
+
* `section` field; if no match exists, a synthetic section is appended.
|
|
23
|
+
*
|
|
24
|
+
* Sort is deterministic: by `(weight, key)` — every host renders the same
|
|
25
|
+
* order from the same registry state, so tests stay reproducible.
|
|
26
|
+
*/
|
|
27
|
+
export function mergeNavSections(coreSections: readonly NavSection[]): readonly NavSection[] {
|
|
28
|
+
const sectionMap = new Map<string, NavItem[]>();
|
|
29
|
+
const sectionOrder: string[] = [];
|
|
30
|
+
|
|
31
|
+
for (const section of coreSections) {
|
|
32
|
+
const heading = section.heading ?? '';
|
|
33
|
+
sectionOrder.push(heading);
|
|
34
|
+
sectionMap.set(heading, [...section.items]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const biome of biomeRegistry.list()) {
|
|
38
|
+
for (const navItem of biome.navItems ?? []) {
|
|
39
|
+
const heading = navItem.section ?? '';
|
|
40
|
+
let bucket = sectionMap.get(heading);
|
|
41
|
+
if (!bucket) {
|
|
42
|
+
bucket = [];
|
|
43
|
+
sectionOrder.push(heading);
|
|
44
|
+
sectionMap.set(heading, bucket);
|
|
45
|
+
}
|
|
46
|
+
bucket.push({
|
|
47
|
+
key: navItem.id,
|
|
48
|
+
label: navItem.label,
|
|
49
|
+
icon: navItem.icon,
|
|
50
|
+
weight: navItem.weight,
|
|
51
|
+
fromBiome: true,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return sectionOrder.map((heading) => ({
|
|
57
|
+
heading: heading === '' ? undefined : heading,
|
|
58
|
+
items: (sectionMap.get(heading) ?? []).slice().sort((a, b) => {
|
|
59
|
+
const wa = a.weight ?? 100;
|
|
60
|
+
const wb = b.weight ?? 100;
|
|
61
|
+
if (wa !== wb) return wa - wb;
|
|
62
|
+
return a.key.localeCompare(b.key);
|
|
63
|
+
}),
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Build a `{ navKey: routePath }` map merging core feature-key→route entries
|
|
69
|
+
* with biome nav-item route paths. Biome-contributed entries win over
|
|
70
|
+
* core entries with the same key (intentional — biome authors who pick a
|
|
71
|
+
* conflicting id are signalling override intent).
|
|
72
|
+
*/
|
|
73
|
+
export function mergeFeatureRouteMap(
|
|
74
|
+
coreFeatureRoutes: Readonly<Record<string, string>>,
|
|
75
|
+
): Record<string, string> {
|
|
76
|
+
const out: Record<string, string> = { ...coreFeatureRoutes };
|
|
77
|
+
for (const biome of biomeRegistry.list()) {
|
|
78
|
+
for (const navItem of biome.navItems ?? []) {
|
|
79
|
+
out[navItem.id] = navItem.route;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declarative session contribution types — every UI extension a frontend
|
|
3
|
+
* biome can drop into the agent-session shell. The host shell
|
|
4
|
+
* carries generic primitives; the registries declared here are how
|
|
5
|
+
* biomes (first-party or third-party) hook those primitives into a
|
|
6
|
+
* concrete user experience.
|
|
7
|
+
*
|
|
8
|
+
* Contract shape: each contribution is a small data object the biome
|
|
9
|
+
* adds to its `FrontendBiome.session.*` field. The host's session
|
|
10
|
+
* components call typed selector hooks (`useSessionSlashCommands`,
|
|
11
|
+
* `useSessionDrawerTabs`, …) which walk the biome registry and return
|
|
12
|
+
* the contributions whose `appliesTo` predicate passes for the current
|
|
13
|
+
* `SessionContext`.
|
|
14
|
+
*
|
|
15
|
+
* `appliesTo` is the single gating primitive — biomes use it to scope
|
|
16
|
+
* a contribution to a profile, capability flag, session status, or any
|
|
17
|
+
* derived condition. Without it, every contribution applies everywhere.
|
|
18
|
+
*
|
|
19
|
+
* Naming convention: contribution ids MUST be namespaced as
|
|
20
|
+
* `<biome-id>/<contribution>` so cross-biome collisions surface
|
|
21
|
+
* deterministically at registration time (`RegistryDuplicateKeyError`).
|
|
22
|
+
*/
|
|
23
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
24
|
+
|
|
25
|
+
import type { HostBridge } from './host-bridge';
|
|
26
|
+
import type { SessionProfileDescriptor } from './session-profiles';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Read-only snapshot of the session the contribution is evaluated
|
|
30
|
+
* against. Passed into every `appliesTo` predicate and every render
|
|
31
|
+
* function so contributions can adapt without subscribing to the host's
|
|
32
|
+
* query cache themselves.
|
|
33
|
+
*/
|
|
34
|
+
export interface SessionContext {
|
|
35
|
+
readonly sessionId: string;
|
|
36
|
+
readonly orgId: string;
|
|
37
|
+
readonly projectId: string;
|
|
38
|
+
/**
|
|
39
|
+
* Biome id that owns the session's profile (e.g. `'software-dev'`,
|
|
40
|
+
* `'design-system-builder'`). Biomes use this to filter contributions to
|
|
41
|
+
* sessions they care about.
|
|
42
|
+
*/
|
|
43
|
+
readonly biomeId?: string;
|
|
44
|
+
readonly profileKey?: string;
|
|
45
|
+
readonly status: string;
|
|
46
|
+
/**
|
|
47
|
+
* Capability flags advertised by the session's profile descriptor
|
|
48
|
+
* (e.g. `'hasGit'`, `'hasArtifacts'`, `'supportsBranches'`). Biomes
|
|
49
|
+
* gate against these instead of hard-coding profile keys.
|
|
50
|
+
*/
|
|
51
|
+
readonly capabilities: ReadonlySet<string>;
|
|
52
|
+
/**
|
|
53
|
+
* `true` while the session is in a state that accepts new work
|
|
54
|
+
* (running, waiting, idle); `false` for terminal/paused/failed states.
|
|
55
|
+
* Mirrors the legacy `isActive` boolean the host shell already passes
|
|
56
|
+
* around so contributions can disable destructive actions.
|
|
57
|
+
*/
|
|
58
|
+
readonly isActive: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Stable representation of a session console event passed into activity
|
|
63
|
+
* + tool-call renderers. Mirrors the host's `SessionConsoleEntry` minus
|
|
64
|
+
* host-specific fields — renderers should not depend on host internals.
|
|
65
|
+
*/
|
|
66
|
+
export interface SessionActivityEvent {
|
|
67
|
+
readonly id: string;
|
|
68
|
+
readonly kind: string;
|
|
69
|
+
readonly sequence?: number;
|
|
70
|
+
readonly createdAt: string;
|
|
71
|
+
readonly title: string;
|
|
72
|
+
readonly content?: string;
|
|
73
|
+
readonly detail?: string;
|
|
74
|
+
readonly status?: 'pending' | 'completed' | 'error';
|
|
75
|
+
readonly payload?: Record<string, unknown>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Stable representation of a tool-call event passed into tool-call
|
|
80
|
+
* renderers. `name` is the tool's canonical id (e.g. `'bash_run'`,
|
|
81
|
+
* `'jira.create_issue'`); biomes MAY use namespaced names so their
|
|
82
|
+
* renderers don't collide with other biomes'.
|
|
83
|
+
*/
|
|
84
|
+
export interface SessionToolCallEvent {
|
|
85
|
+
readonly id: string;
|
|
86
|
+
readonly name: string;
|
|
87
|
+
readonly status: 'pending' | 'completed' | 'error';
|
|
88
|
+
readonly startedAt: string;
|
|
89
|
+
readonly completedAt?: string;
|
|
90
|
+
readonly args?: Record<string, unknown>;
|
|
91
|
+
readonly result?: unknown;
|
|
92
|
+
readonly error?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Slash commands ─────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
export interface SlashCommandContext {
|
|
98
|
+
readonly session: SessionContext;
|
|
99
|
+
readonly bridge: HostBridge;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface SlashCommandContribution {
|
|
103
|
+
/** Namespaced id (`<biome-id>/<name>`). */
|
|
104
|
+
readonly id: string;
|
|
105
|
+
/** Command keyword users type after `/` (no slash; e.g. `'commit'`). */
|
|
106
|
+
readonly name: string;
|
|
107
|
+
/** Displayed label in the autocomplete popup. */
|
|
108
|
+
readonly label: string;
|
|
109
|
+
readonly description: string;
|
|
110
|
+
readonly icon?: ComponentType<{ className?: string | undefined }>;
|
|
111
|
+
/** Sort weight inside the autocomplete list (lower first; default 100). */
|
|
112
|
+
readonly weight?: number;
|
|
113
|
+
/** Hide the command unless the session matches. */
|
|
114
|
+
readonly appliesTo?: (ctx: SessionContext) => boolean;
|
|
115
|
+
/** Disable the command when the session is paused/terminal. */
|
|
116
|
+
readonly requiresActiveSession?: boolean;
|
|
117
|
+
/** Invoked when the user selects the command. */
|
|
118
|
+
readonly onSelect: (ctx: SlashCommandContext) => void | Promise<void>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Secondary drawer tabs ─────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
export interface SecondaryDrawerTabContext {
|
|
124
|
+
readonly session: SessionContext;
|
|
125
|
+
readonly bridge: HostBridge;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface SecondaryDrawerTabContribution {
|
|
129
|
+
/** Namespaced id (`<biome-id>/<tab>`). */
|
|
130
|
+
readonly id: string;
|
|
131
|
+
/** Stable short key used by keyboard shortcuts (`'tools'`, `'git'`, …). */
|
|
132
|
+
readonly key: string;
|
|
133
|
+
readonly label: string;
|
|
134
|
+
readonly icon: ComponentType<{ className?: string | undefined }>;
|
|
135
|
+
/** Sort weight (lower first). Default 100. */
|
|
136
|
+
readonly order?: number;
|
|
137
|
+
readonly appliesTo?: (ctx: SessionContext) => boolean;
|
|
138
|
+
readonly render: (ctx: SecondaryDrawerTabContext) => ReactNode;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── Activity renderers ─────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
export interface ActivityRendererContext {
|
|
144
|
+
readonly session: SessionContext;
|
|
145
|
+
readonly event: SessionActivityEvent;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface ActivityRendererContribution {
|
|
149
|
+
/** Namespaced id (`<biome-id>/<renderer>`). */
|
|
150
|
+
readonly id: string;
|
|
151
|
+
/**
|
|
152
|
+
* Activity entry kind this renderer handles. Either an exact
|
|
153
|
+
* `SessionConsoleEntry.kind` value or a namespaced custom kind a
|
|
154
|
+
* biome emits through its own backend events.
|
|
155
|
+
*/
|
|
156
|
+
readonly entryKind: string;
|
|
157
|
+
/** Per-event predicate; defaults to "always handle this kind". */
|
|
158
|
+
readonly appliesTo?: (ctx: ActivityRendererContext) => boolean;
|
|
159
|
+
readonly render: (ctx: ActivityRendererContext) => ReactNode;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ── Tool-call renderers ────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
export interface ToolCallRendererContext {
|
|
165
|
+
readonly session: SessionContext;
|
|
166
|
+
readonly event: SessionToolCallEvent;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface ToolCallRendererContribution {
|
|
170
|
+
/** Namespaced id (`<biome-id>/<renderer>`). */
|
|
171
|
+
readonly id: string;
|
|
172
|
+
/**
|
|
173
|
+
* Either an exact tool name (`'bash_run'`) OR a `'<prefix>.*'` glob
|
|
174
|
+
* matching any tool whose name starts with `<prefix>.` (e.g.
|
|
175
|
+
* `'jira.*'`). Exact matches win over globs; ties broken by `weight`.
|
|
176
|
+
*/
|
|
177
|
+
readonly toolName: string;
|
|
178
|
+
readonly icon?: ComponentType<{ className?: string | undefined }>;
|
|
179
|
+
readonly weight?: number;
|
|
180
|
+
readonly appliesTo?: (ctx: ToolCallRendererContext) => boolean;
|
|
181
|
+
/** Short label shown in the activity timeline row. */
|
|
182
|
+
readonly renderLabel?: (ctx: ToolCallRendererContext) => string;
|
|
183
|
+
/**
|
|
184
|
+
* Expanded card / inline body for the tool call. When omitted, the
|
|
185
|
+
* host falls back to a generic key-value dump of args + result.
|
|
186
|
+
*/
|
|
187
|
+
readonly render?: (ctx: ToolCallRendererContext) => ReactNode;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── Mutation bars ──────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
export interface MutationBarContext {
|
|
193
|
+
readonly session: SessionContext;
|
|
194
|
+
readonly bridge: HostBridge;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Sticky bar rendered between the session header and the chat panel —
|
|
199
|
+
* used today by software-dev for the git branch + PR status row. Generic
|
|
200
|
+
* by design so a Jira biome could add an "Issue: PROJ-123 / In Review"
|
|
201
|
+
* bar, an aws biome could surface a deploy status, etc.
|
|
202
|
+
*/
|
|
203
|
+
export interface MutationBarContribution {
|
|
204
|
+
/** Namespaced id (`<biome-id>/<bar>`). */
|
|
205
|
+
readonly id: string;
|
|
206
|
+
/** Sort weight (lower renders higher). Default 100. */
|
|
207
|
+
readonly order?: number;
|
|
208
|
+
readonly appliesTo?: (ctx: SessionContext) => boolean;
|
|
209
|
+
readonly render: (ctx: MutationBarContext) => ReactNode;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Header chips ───────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
export interface HeaderChipContext {
|
|
215
|
+
readonly session: SessionContext;
|
|
216
|
+
readonly bridge: HostBridge;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Compact chips rendered in the session header row alongside the title
|
|
221
|
+
* (e.g. "Fork of #42", "PR #15", "Catalog entity: …"). Biomes use
|
|
222
|
+
* these for one-glance links to external systems.
|
|
223
|
+
*/
|
|
224
|
+
export interface HeaderChipContribution {
|
|
225
|
+
readonly id: string;
|
|
226
|
+
readonly order?: number;
|
|
227
|
+
readonly appliesTo?: (ctx: SessionContext) => boolean;
|
|
228
|
+
readonly render: (ctx: HeaderChipContext) => ReactNode;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── Attachment classes ─────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
export interface AttachmentPickResult {
|
|
234
|
+
/** Class id (`<biome-id>/<class>`). */
|
|
235
|
+
readonly classId: string;
|
|
236
|
+
/** Stable reference the backend uses to resolve the attachment. */
|
|
237
|
+
readonly ref: string;
|
|
238
|
+
/** Display label shown in the attachments list. */
|
|
239
|
+
readonly label: string;
|
|
240
|
+
/** Optional opaque payload forwarded to the attachment API. */
|
|
241
|
+
readonly payload?: Record<string, unknown>;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export interface AttachmentPickContext {
|
|
245
|
+
readonly session: SessionContext;
|
|
246
|
+
readonly bridge: HostBridge;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* A class of attachable resource a biome contributes. The contribution
|
|
251
|
+
* supplies a label, an icon, and a picker callback that resolves to a
|
|
252
|
+
* concrete attachment ref. The host calls `pickAndAttach` from its
|
|
253
|
+
* attachment dialog when the user selects this class.
|
|
254
|
+
*/
|
|
255
|
+
export interface AttachmentClassContribution {
|
|
256
|
+
readonly id: string;
|
|
257
|
+
readonly label: string;
|
|
258
|
+
readonly description?: string;
|
|
259
|
+
readonly icon?: ComponentType<{ className?: string | undefined }>;
|
|
260
|
+
readonly order?: number;
|
|
261
|
+
readonly appliesTo?: (ctx: SessionContext) => boolean;
|
|
262
|
+
readonly pickAndAttach: (
|
|
263
|
+
ctx: AttachmentPickContext,
|
|
264
|
+
) => Promise<AttachmentPickResult | null>;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ── Aggregate session contributions ────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* All session-level contributions a biome declares on its
|
|
271
|
+
* `FrontendBiome`. Every field is optional; a biome opts in only to
|
|
272
|
+
* the surfaces it cares about. The host walks the registry once per
|
|
273
|
+
* surface and assembles the active contributions for the current
|
|
274
|
+
* session context.
|
|
275
|
+
*/
|
|
276
|
+
export interface SessionContributions {
|
|
277
|
+
readonly slashCommands?: readonly SlashCommandContribution[];
|
|
278
|
+
readonly secondaryDrawerTabs?: readonly SecondaryDrawerTabContribution[];
|
|
279
|
+
readonly activityRenderers?: readonly ActivityRendererContribution[];
|
|
280
|
+
readonly toolCallRenderers?: readonly ToolCallRendererContribution[];
|
|
281
|
+
readonly mutationBars?: readonly MutationBarContribution[];
|
|
282
|
+
readonly headerChips?: readonly HeaderChipContribution[];
|
|
283
|
+
readonly attachmentClasses?: readonly AttachmentClassContribution[];
|
|
284
|
+
/**
|
|
285
|
+
* Profile descriptors this biome owns. The host matches a session's
|
|
286
|
+
* `profileKey` against these and fans out the descriptor's
|
|
287
|
+
* `capabilities` as `SessionContext.capabilities`, which each
|
|
288
|
+
* contribution's `appliesTo` predicate reads. A biome that ships a
|
|
289
|
+
* profile MUST also ship its descriptor here so the capability vocab
|
|
290
|
+
* stays explicit rather than implicit-from-key-naming.
|
|
291
|
+
*/
|
|
292
|
+
readonly profiles?: readonly SessionProfileDescriptor[];
|
|
293
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session profile descriptors — coarse capability flags that gate which
|
|
3
|
+
* UI surfaces a session shows. A profile is owned by exactly one biome
|
|
4
|
+
* (matched by `(biomeId, key)`) and declares a set of namespaced
|
|
5
|
+
* capability strings the host fans out as `SessionContext.capabilities`.
|
|
6
|
+
*
|
|
7
|
+
* Biomes consume capabilities through each contribution's `appliesTo`
|
|
8
|
+
* predicate — the host shell itself never branches on a capability
|
|
9
|
+
* string. This keeps the host neutral; a session that doesn't declare
|
|
10
|
+
* `hasGit` simply gets no git-touching contributions because every git
|
|
11
|
+
* contribution gates on `ctx.capabilities.has('hasGit')`.
|
|
12
|
+
*
|
|
13
|
+
* Capability namespacing: well-known capabilities are documented here as
|
|
14
|
+
* constants so first-party biomes agree on a vocabulary; third-party
|
|
15
|
+
* biomes MUST namespace their own capabilities (`<biome-id>:<flag>`)
|
|
16
|
+
* to avoid collisions.
|
|
17
|
+
*/
|
|
18
|
+
import { useMemo } from 'react';
|
|
19
|
+
|
|
20
|
+
import { biomeRegistry, useBiomeRegistryRevision } from './biome-registry';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Well-known capability flags first-party biomes share. Third-party
|
|
24
|
+
* biomes SHOULD prefer namespacing their own (`<biome-id>:<flag>`).
|
|
25
|
+
*
|
|
26
|
+
* Adding a constant here is a contract-stability statement — it
|
|
27
|
+
* documents a flag enough first-party biomes use that we want a stable
|
|
28
|
+
* spelling. Removing one is a breaking change.
|
|
29
|
+
*/
|
|
30
|
+
export const SessionCapabilities = {
|
|
31
|
+
/** Session backs a git workspace; branches, commits, push, PR. */
|
|
32
|
+
HasGit: 'hasGit',
|
|
33
|
+
/** Session emits workspace file mutations (created/edited/deleted). */
|
|
34
|
+
HasWorkspaceFiles: 'hasWorkspaceFiles',
|
|
35
|
+
/** Session can publish artefacts back to the knowledge base. */
|
|
36
|
+
SupportsKbPush: 'supportsKbPush',
|
|
37
|
+
/** Session can open / render in-workspace artefacts (apps, docs). */
|
|
38
|
+
HasArtifacts: 'hasArtifacts',
|
|
39
|
+
/** Session can fork to a new branch. */
|
|
40
|
+
SupportsForking: 'supportsForking',
|
|
41
|
+
/** Session can share its output (deliverable) to other surfaces. */
|
|
42
|
+
SupportsSharing: 'supportsSharing',
|
|
43
|
+
} as const;
|
|
44
|
+
|
|
45
|
+
export type SessionCapability =
|
|
46
|
+
(typeof SessionCapabilities)[keyof typeof SessionCapabilities];
|
|
47
|
+
|
|
48
|
+
export interface SessionProfileDescriptor {
|
|
49
|
+
/**
|
|
50
|
+
* Stable profile key — matches the backend's `Session.profileKey`
|
|
51
|
+
* (or the session manifest slug for newer profiles).
|
|
52
|
+
*/
|
|
53
|
+
readonly key: string;
|
|
54
|
+
/** Biome that owns this profile. */
|
|
55
|
+
readonly biomeId: string;
|
|
56
|
+
/** Displayed label. */
|
|
57
|
+
readonly label: string;
|
|
58
|
+
readonly description?: string;
|
|
59
|
+
/** Capability flags the host fans out as `SessionContext.capabilities`. */
|
|
60
|
+
readonly capabilities: readonly string[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Hook returning the descriptor that matches a session's `(biomeId,
|
|
65
|
+
* profileKey)`. Falls back to a synthetic descriptor with the well-known
|
|
66
|
+
* core capabilities when no biome owns the profile — that's how
|
|
67
|
+
* sessions started before any biome loaded still get usable defaults
|
|
68
|
+
* (today's behaviour).
|
|
69
|
+
*
|
|
70
|
+
* Resolution order:
|
|
71
|
+
* 1. Exact match on `(biomeId, key)`.
|
|
72
|
+
* 2. Exact match on `key` alone (cross-biome profile name).
|
|
73
|
+
* 3. Synthetic fallback (`unknown` biome, empty capabilities).
|
|
74
|
+
*/
|
|
75
|
+
export function useSessionProfileDescriptor(
|
|
76
|
+
biomeId: string | undefined,
|
|
77
|
+
profileKey: string | undefined,
|
|
78
|
+
): SessionProfileDescriptor {
|
|
79
|
+
const revision = useBiomeRegistryRevision();
|
|
80
|
+
return useMemo(() => {
|
|
81
|
+
const fallback: SessionProfileDescriptor = {
|
|
82
|
+
key: profileKey ?? 'unknown',
|
|
83
|
+
biomeId: biomeId ?? 'unknown',
|
|
84
|
+
label: profileKey ?? 'Unknown profile',
|
|
85
|
+
capabilities: [],
|
|
86
|
+
};
|
|
87
|
+
if (!profileKey) return fallback;
|
|
88
|
+
let crossPluginMatch: SessionProfileDescriptor | undefined;
|
|
89
|
+
for (const biome of biomeRegistry.list()) {
|
|
90
|
+
for (const profile of biome.session?.profiles ?? []) {
|
|
91
|
+
if (profile.key !== profileKey) continue;
|
|
92
|
+
if (biomeId != null && profile.biomeId === biomeId) return profile;
|
|
93
|
+
crossPluginMatch ??= profile;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return crossPluginMatch ?? fallback;
|
|
97
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
98
|
+
}, [revision, biomeId, profileKey]);
|
|
99
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { CapabilityRef } from '@xemahq/kernel-contracts/capability';
|
|
2
|
+
import type { SpaceRef } from '@xemahq/kernel-contracts/space';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Orchestration metadata the SystemBus carries on every
|
|
6
|
+
* `capability.invoke` so the backend capability-router can build its
|
|
7
|
+
* `ExecutionContext` envelope and record who invoked what, from where,
|
|
8
|
+
* in which space, against which environment.
|
|
9
|
+
*
|
|
10
|
+
* **This is pure orchestration data. The SystemBus NEVER inspects it to
|
|
11
|
+
* decide allow/deny.** The backend capability-router is the single
|
|
12
|
+
* authority on auth / tenancy / policy / audit. The SystemBus's only job
|
|
13
|
+
* is to faithfully relay this metadata to the router and pass the result
|
|
14
|
+
* back; it adds no authorization of its own.
|
|
15
|
+
*
|
|
16
|
+
* The fields map 1:1 onto the gateway's per-invocation envelope:
|
|
17
|
+
* - `sourceBiomeId` → `ExecutionContext.biome.id`
|
|
18
|
+
* - `targetCapabilityRef` → `ExecutionContext.capability.ref`
|
|
19
|
+
* - `spaceRef` → `ExecutionContext.space`
|
|
20
|
+
* - `environmentId` → `ExecutionContext.environment.id`
|
|
21
|
+
* - `actorId` → `ExecutionContext.subject` (the host resolves the
|
|
22
|
+
* full SubjectRef from the actor's session).
|
|
23
|
+
*/
|
|
24
|
+
export interface CapabilityInvokeMeta {
|
|
25
|
+
/** Biome that originated the invocation (for provenance + audit). */
|
|
26
|
+
readonly sourceBiomeId: string;
|
|
27
|
+
/** The capability being invoked. Mirrors the `ref` argument; carried
|
|
28
|
+
* explicitly so the metadata is self-describing in audit records. */
|
|
29
|
+
readonly targetCapabilityRef: CapabilityRef;
|
|
30
|
+
/** The space (5/7-tier scope) the invocation runs in. */
|
|
31
|
+
readonly spaceRef: SpaceRef;
|
|
32
|
+
/** Canonical `environment:<slug>` ref of the active execution environment. */
|
|
33
|
+
readonly environmentId: string;
|
|
34
|
+
/** The acting subject's id (user / agent / app). The host expands this
|
|
35
|
+
* into the full `SubjectRef` server-side; the SystemBus only carries
|
|
36
|
+
* the id, never a credential. */
|
|
37
|
+
readonly actorId: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Result of a `capability.invoke`. Mirrors the capability-router's
|
|
42
|
+
* `InvokeMetaToolResponseDto` wire shape (`output? / auditId /
|
|
43
|
+
* obligations`) without importing the generated client — the kernel
|
|
44
|
+
* contract stays framework- and transport-agnostic.
|
|
45
|
+
*
|
|
46
|
+
* `output` is `undefined` on denial / approval-required outcomes; the
|
|
47
|
+
* caller MUST treat its absence explicitly (fetch the structured denial
|
|
48
|
+
* via `auditId`) rather than assume success. `auditId` is ALWAYS
|
|
49
|
+
* populated, including on denial — this is the audit anchor.
|
|
50
|
+
*/
|
|
51
|
+
export interface CapabilityInvokeResult<TOutput = unknown> {
|
|
52
|
+
/** Capability output payload. Omitted on denial / approval-required. */
|
|
53
|
+
readonly output?: TOutput;
|
|
54
|
+
/** Audit entry id. Always populated, including on denial. */
|
|
55
|
+
readonly auditId: string;
|
|
56
|
+
/**
|
|
57
|
+
* Closed-set policy obligations the caller must honour locally
|
|
58
|
+
* (e.g. `redact-secrets`, `max-cost-usd`). Sourced verbatim from the
|
|
59
|
+
* router response. The string union is left open at the contract layer
|
|
60
|
+
* because the obligation enum is owned by `@xemahq/kernel-contracts/policy`;
|
|
61
|
+
* the host maps these to the typed enum when it honours them.
|
|
62
|
+
*/
|
|
63
|
+
readonly obligations: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The `capability.invoke` surface of the SystemBus.
|
|
68
|
+
*
|
|
69
|
+
* `invoke()` routes the call to the host's capability-router client. The
|
|
70
|
+
* backend enforces auth / tenancy / policy / audit; the SystemBus adds
|
|
71
|
+
* NONE of those — it is pure orchestration. A non-2xx transport failure
|
|
72
|
+
* rejects the returned promise (fail-fast); a *policy denial* resolves
|
|
73
|
+
* with `output === undefined` + a populated `auditId` (the router's
|
|
74
|
+
* structured outcome), so denial is observable, never silently swallowed.
|
|
75
|
+
*/
|
|
76
|
+
export interface CapabilityBus {
|
|
77
|
+
/**
|
|
78
|
+
* Invoke a capability through the backend router.
|
|
79
|
+
*
|
|
80
|
+
* @param ref The capability to invoke (`<domain>:<resource>.<verb>@<major>`).
|
|
81
|
+
* @param input The capability input payload (validated server-side
|
|
82
|
+
* against the descriptor's `inputSchema`).
|
|
83
|
+
* @param meta Orchestration metadata for the `ExecutionContext`.
|
|
84
|
+
* The host fills `targetCapabilityRef` from `ref` if the
|
|
85
|
+
* caller omits it; all other fields are required.
|
|
86
|
+
*/
|
|
87
|
+
invoke<TInput = unknown, TOutput = unknown>(
|
|
88
|
+
ref: CapabilityRef,
|
|
89
|
+
input: TInput,
|
|
90
|
+
meta: CapabilityInvokeMeta,
|
|
91
|
+
): Promise<CapabilityInvokeResult<TOutput>>;
|
|
92
|
+
}
|