@beeuni/sdk 0.1.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.
- package/README.md +53 -92
- package/dist/beeuni.esm.min.js +36 -34
- package/dist/beeuni.global.js +36 -34
- package/dist/browser/cache/idb-sync-cache.js +15 -5
- package/dist/browser/session-sync/server-transcript-mirror.d.ts +7 -1
- package/dist/browser/session-sync/server-transcript-mirror.js +30 -24
- package/dist/browser/session-sync/session-sync-registry.d.ts +3 -1
- package/dist/browser/session-sync/session-sync-registry.js +48 -3
- package/dist/browser/stores/sync-store/keep-unchanged.d.ts +4 -0
- package/dist/browser/stores/sync-store/keep-unchanged.js +34 -0
- package/dist/browser/stores/sync-store.d.ts +7 -0
- package/dist/browser/stores/sync-store.js +122 -35
- package/dist/core/attachments/prompt-attachments.d.ts +4 -0
- package/dist/core/attachments/prompt-attachments.js +34 -0
- package/dist/core/capture/rules.d.ts +17 -0
- package/dist/core/capture/rules.js +27 -0
- package/dist/core/client/beeuni.d.ts +99 -34
- package/dist/core/client/beeuni.js +5 -11
- package/dist/core/client/capture.d.ts +105 -0
- package/dist/core/client/capture.js +48 -0
- package/dist/core/client/facade-catalog.d.ts +0 -4
- package/dist/core/client/facade-catalog.js +1 -5
- package/dist/core/client/facade-identity.d.ts +1 -0
- package/dist/core/client/facade-identity.js +1 -0
- package/dist/core/client/project-access-resources.d.ts +3 -1
- package/dist/core/client/project-access-resources.js +1 -1
- package/dist/core/client/project-connections.d.ts +2 -2
- package/dist/core/client/project-connections.js +2 -2
- package/dist/core/client/project-operations-admin.d.ts +4 -4
- package/dist/core/client/project-operations-review.d.ts +2 -3
- package/dist/core/client/project-operations-review.js +1 -0
- package/dist/core/client/project-operations-workspace.d.ts +4 -4
- package/dist/core/client/project-operations-workspace.js +1 -1
- package/dist/core/client/project-operations.d.ts +9 -19
- package/dist/core/client/project-operations.js +2 -6
- package/dist/core/client/project-platform-security.d.ts +1 -1
- package/dist/core/client/project-platform-settings.d.ts +1 -1
- package/dist/core/client/session-context.d.ts +2 -0
- package/dist/core/client/session-handle.d.ts +13 -0
- package/dist/core/client/session-lifecycle-resources.d.ts +11 -0
- package/dist/core/client/session-lifecycle-resources.js +4 -0
- package/dist/core/client/session-lifecycle-security.d.ts +2 -0
- package/dist/core/client/session-lifecycle-security.js +1 -0
- package/dist/core/client/session-readiness.d.ts +2 -0
- package/dist/core/client/session-readiness.js +20 -1
- package/dist/core/http/api-client.d.ts +1 -7
- package/dist/core/http/api-client.js +2 -37
- package/dist/core/http/auth.d.ts +2 -4
- package/dist/core/http/auth.js +2 -8
- package/dist/core/http/config.d.ts +2 -0
- package/dist/core/http/response-error.js +26 -11
- package/dist/core/http/transport.js +6 -1
- package/dist/core/models/composer-model.d.ts +14 -0
- package/dist/core/models/composer-model.js +21 -1
- package/dist/core/rest/platform-client/capture.d.ts +363 -0
- package/dist/core/rest/platform-client/capture.js +116 -0
- package/dist/core/rest/platform-client/client-policy.d.ts +8 -0
- package/dist/core/rest/platform-client/client-policy.js +30 -0
- package/dist/core/rest/platform-client/index.d.ts +4 -4
- package/dist/core/rest/platform-client/index.js +4 -4
- package/dist/core/rest/platform-client/lifecycle.js +18 -12
- package/dist/core/rest/platform-client/shared.d.ts +5 -0
- package/dist/core/rest/platform-client/shared.js +18 -0
- package/dist/core/rest/platform-client/types.d.ts +1 -1
- package/dist/core/rest/projects-client/account-secret-resources.d.ts +2 -1
- package/dist/core/rest/projects-client/accounts.d.ts +34 -0
- package/dist/core/rest/projects-client/accounts.js +10 -0
- package/dist/core/rest/projects-client/admin-providers.d.ts +0 -61
- package/dist/core/rest/projects-client/admin-providers.js +0 -6
- package/dist/core/rest/projects-client/agent-config.d.ts +3 -0
- package/dist/core/rest/projects-client/apps-fleet.d.ts +5 -1
- package/dist/core/rest/projects-client/apps-fleet.js +2 -2
- package/dist/core/rest/projects-client/apps.d.ts +11 -1
- package/dist/core/rest/projects-client/change-requests.d.ts +2 -0
- package/dist/core/rest/projects-client/change-requests.js +2 -0
- package/dist/core/rest/projects-client/connectors.d.ts +13 -55
- package/dist/core/rest/projects-client/connectors.js +5 -29
- package/dist/core/rest/projects-client/files.d.ts +3 -0
- package/dist/core/rest/projects-client/files.js +4 -0
- package/dist/core/rest/projects-client/gateway.d.ts +5 -0
- package/dist/core/rest/projects-client/git-backend.d.ts +1 -10
- package/dist/core/rest/projects-client/git-backend.js +0 -10
- package/dist/core/rest/projects-client/iam-actions.d.ts +76 -0
- package/dist/core/rest/projects-client/iam-actions.js +101 -0
- package/dist/core/rest/projects-client/iam-sso.d.ts +0 -10
- package/dist/core/rest/projects-client/iam-sso.js +0 -3
- package/dist/core/rest/projects-client/iam.d.ts +1 -0
- package/dist/core/rest/projects-client/iam.js +1 -0
- package/dist/core/rest/projects-client/index.d.ts +1 -4
- package/dist/core/rest/projects-client/index.js +1 -4
- package/dist/core/rest/projects-client/notifications.d.ts +1 -1
- package/dist/core/rest/projects-client/notifications.js +4 -0
- package/dist/core/rest/projects-client/projects.d.ts +4 -2
- package/dist/core/rest/projects-client/projects.js +4 -2
- package/dist/core/rest/projects-client/review-items.d.ts +2 -0
- package/dist/core/rest/projects-client/review-items.js +4 -0
- package/dist/core/rest/projects-client/review.d.ts +17 -3
- package/dist/core/rest/projects-client/review.js +11 -0
- package/dist/core/rest/projects-client/sandbox.d.ts +8 -9
- package/dist/core/rest/projects-client/secrets.d.ts +3 -0
- package/dist/core/rest/projects-client/session-attachments.js +14 -1
- package/dist/core/rest/projects-client/session-sandbox.d.ts +1 -1
- package/dist/core/rest/projects-client/sessions.d.ts +31 -4
- package/dist/core/rest/projects-client/sessions.js +34 -4
- package/dist/core/runtime/runtime-rest-client.js +1 -1
- package/dist/core/runtime/runtime-types.d.ts +1 -0
- package/dist/core/session/control-stream.d.ts +1 -0
- package/dist/core/session/control-stream.js +18 -1
- package/dist/core/session/open-bundle.d.ts +4 -4
- package/dist/core/session/open-bundle.js +29 -46
- package/dist/core/session/turn-end-watch.d.ts +47 -0
- package/dist/core/session/turn-end-watch.js +141 -0
- package/dist/core/session-log/guard.d.ts +6 -0
- package/dist/core/session-log/guard.js +109 -0
- package/dist/core/session-log/index.d.ts +7 -0
- package/dist/core/session-log/index.js +5 -0
- package/dist/core/session-log/saved-log.d.ts +10 -0
- package/dist/core/session-log/saved-log.js +25 -0
- package/dist/core/session-log/source.d.ts +25 -0
- package/dist/core/session-log/source.js +30 -0
- package/dist/core/session-log/store.d.ts +21 -0
- package/dist/core/session-log/store.js +126 -0
- package/dist/core/session-log/tool-call.d.ts +11 -0
- package/dist/core/session-log/tool-call.js +65 -0
- package/dist/core/session-log/turns/blocks.d.ts +60 -0
- package/dist/core/session-log/turns/blocks.js +124 -0
- package/dist/core/session-log/turns/history.d.ts +32 -0
- package/dist/core/session-log/turns/history.js +99 -0
- package/dist/core/session-log/turns/index.d.ts +4 -0
- package/dist/core/session-log/turns/index.js +4 -0
- package/dist/core/session-log/turns/metrics.d.ts +21 -0
- package/dist/core/session-log/turns/metrics.js +29 -0
- package/dist/core/session-log/turns/turn.d.ts +55 -0
- package/dist/core/session-log/turns/turn.js +279 -0
- package/dist/core/session-log/turns/v1-parts.d.ts +8 -0
- package/dist/core/session-log/turns/v1-parts.js +43 -0
- package/dist/core/session-log/types.d.ts +264 -0
- package/dist/core/session-log/types.js +6 -0
- package/dist/core/session-log/v1-view.d.ts +13 -0
- package/dist/core/session-log/v1-view.js +215 -0
- package/dist/core/session-log/v1.d.ts +31 -0
- package/dist/core/session-log/v1.js +473 -0
- package/dist/core/session-sync/copy-store-types.d.ts +19 -0
- package/dist/core/session-sync/copy-store-types.js +1 -0
- package/dist/core/session-sync/saved-copy-store.js +25 -4
- package/dist/core/session-sync/saved-transcript.d.ts +15 -1
- package/dist/core/session-sync/saved-transcript.js +55 -0
- package/dist/core/session-sync/session-sync-controller.js +3 -0
- package/dist/core/stream/event-stream.js +2 -2
- package/dist/core/turns/classify.d.ts +2 -1
- package/dist/core/turns/classify.js +7 -3
- package/dist/core/turns/edit-blocks.d.ts +1 -0
- package/dist/core/turns/edit-blocks.js +15 -0
- package/dist/core/turns/parts.d.ts +3 -2
- package/dist/core/turns/parts.js +4 -2
- package/dist/core/turns/segments/burst-summary.d.ts +1 -0
- package/dist/core/turns/segments/burst-summary.js +24 -0
- package/dist/core/turns/segments/group-steps.d.ts +4 -3
- package/dist/core/turns/segments/group-steps.js +9 -6
- package/dist/core/turns/segments/index.d.ts +1 -0
- package/dist/core/turns/segments/index.js +1 -0
- package/dist/core/turns/segments/narration.d.ts +5 -4
- package/dist/core/turns/segments/narration.js +12 -3
- package/dist/core/turns/segments/session-activity-groups.d.ts +2 -1
- package/dist/core/turns/segments/session-activity-groups.js +3 -1
- package/dist/core/turns/segments/step-label.d.ts +2 -1
- package/dist/core/turns/segments/step-label.js +3 -1
- package/dist/core/turns/segments/work-split.d.ts +10 -0
- package/dist/core/turns/segments/work-split.js +40 -0
- package/dist/core/turns/segments/working-turn.d.ts +4 -2
- package/dist/core/turns/segments/working-turn.js +9 -8
- package/dist/core/turns/state.d.ts +2 -7
- package/dist/core/turns/state.js +28 -134
- package/dist/core/turns/tool-kind.d.ts +1 -1
- package/dist/core/turns/tool-kind.js +3 -2
- package/dist/core/turns/tool-registry.js +1 -0
- package/dist/core/turns/tools/tool-meta.d.ts +3 -2
- package/dist/core/turns/tools/tool-meta.js +5 -2
- package/dist/core/turns/tools/tool-outcome.d.ts +2 -1
- package/dist/core/turns/tools/tool-outcome.js +3 -1
- package/dist/core/turns/types.d.ts +1 -1
- package/dist/core/turns/view-model.js +1 -13
- package/dist/core/warm-session-pool.d.ts +29 -0
- package/dist/core/warm-session-pool.js +127 -0
- package/dist/genui/catalog.d.ts +28 -0
- package/dist/genui/catalog.js +396 -0
- package/dist/genui/expansion.d.ts +3 -0
- package/dist/genui/expansion.js +295 -0
- package/dist/genui/fence-entry.d.ts +2 -0
- package/dist/genui/fence-entry.js +1 -0
- package/dist/genui/fence.d.ts +21 -0
- package/dist/genui/fence.js +138 -0
- package/dist/genui/index.d.ts +6 -0
- package/dist/genui/index.js +6 -0
- package/dist/genui/markdown.d.ts +12 -0
- package/dist/genui/markdown.js +104 -0
- package/dist/genui/parse.d.ts +6 -0
- package/dist/genui/parse.js +75 -0
- package/dist/genui/prompt.d.ts +2 -0
- package/dist/genui/prompt.js +206 -0
- package/dist/genui/react/default-components.d.ts +2 -0
- package/dist/genui/react/default-components.js +72 -0
- package/dist/genui/react/default-components.test.d.ts +1 -0
- package/dist/genui/react/default-components.test.js +94 -0
- package/dist/genui/react/genui-block-disabled.test.d.ts +1 -0
- package/dist/genui/react/genui-block-disabled.test.js +50 -0
- package/dist/genui/react/genui-block.d.ts +28 -0
- package/dist/genui/react/genui-block.js +91 -0
- package/dist/genui/react/genui-block.test.d.ts +1 -0
- package/dist/genui/react/genui-block.test.js +205 -0
- package/dist/genui/react/index.d.ts +2 -0
- package/dist/genui/react/index.js +2 -0
- package/dist/genui/share.d.ts +5 -0
- package/dist/genui/share.js +30 -0
- package/dist/genui/test-fixtures.d.ts +2 -0
- package/dist/genui/test-fixtures.js +16 -0
- package/dist/genui/types.d.ts +29 -0
- package/dist/genui/types.js +1 -0
- package/dist/genui/urls.d.ts +1 -0
- package/dist/genui/urls.js +18 -0
- package/dist/genui/validate.d.ts +9 -0
- package/dist/genui/validate.js +116 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/internal/connector-catalog.d.ts +48 -0
- package/dist/internal/connector-catalog.js +27 -0
- package/dist/internal/host-configuration.d.ts +22 -0
- package/dist/internal/host-configuration.js +28 -0
- package/dist/internal/platform-admin-react.d.ts +12 -0
- package/dist/internal/platform-admin-react.js +13 -0
- package/dist/internal/platform-admin.d.ts +9 -0
- package/dist/internal/platform-admin.js +9 -0
- package/dist/platform/ui.d.ts +8 -2
- package/dist/platform/ui.js +19 -4
- package/dist/react/chat/use-chat-turns.d.ts +1 -1
- package/dist/react/human-presence.d.ts +9 -0
- package/dist/react/human-presence.js +12 -0
- package/dist/react/index.d.ts +4 -13
- package/dist/react/index.js +4 -13
- package/dist/react/opencode.d.ts +1 -0
- package/dist/react/opencode.js +1 -0
- package/dist/react/query-contracts.d.ts +6 -0
- package/dist/react/query-contracts.js +6 -0
- package/dist/react/query-keys.d.ts +17 -2
- package/dist/react/query-keys.js +24 -2
- package/dist/react/reset-identity-state.js +2 -0
- package/dist/react/session-cache-write.d.ts +1 -0
- package/dist/react/session-cache-write.js +20 -0
- package/dist/react/session-transcript-subscription.js +3 -1
- package/dist/react/sign-in-with-beeuni.d.ts +6 -6
- package/dist/react/turn-end-handoff.d.ts +13 -0
- package/dist/react/turn-end-handoff.js +101 -0
- package/dist/react/use-admin-providers.d.ts +3 -6
- package/dist/react/use-admin-providers.js +1 -11
- package/dist/react/use-capture.d.ts +47 -0
- package/dist/react/use-capture.js +173 -0
- package/dist/react/use-feature-flag.js +1 -0
- package/dist/react/use-opencode-events/handle-event.d.ts +1 -0
- package/dist/react/use-opencode-events/handle-event.js +5 -0
- package/dist/react/use-opencode-events/handle-interaction-event.js +8 -3
- package/dist/react/use-opencode-events/handle-session-event.js +13 -8
- package/dist/react/use-opencode-events/handler-context.d.ts +1 -0
- package/dist/react/use-opencode-events/helpers.d.ts +1 -0
- package/dist/react/use-opencode-events/helpers.js +11 -1
- package/dist/react/use-opencode-events/index.js +1 -0
- package/dist/react/use-opencode-local.d.ts +5 -1
- package/dist/react/use-opencode-local.js +15 -2
- package/dist/react/use-opencode-sessions/commands.d.ts +11 -1
- package/dist/react/use-opencode-sessions/commands.js +21 -1
- package/dist/react/use-opencode-sessions/permissions.js +28 -1
- package/dist/react/use-project-sessions.d.ts +1 -1
- package/dist/react/use-project-sessions.js +6 -5
- package/dist/react/use-prompt-attachments.js +1 -0
- package/dist/react/use-review-summary.d.ts +13 -0
- package/dist/react/use-review-summary.js +32 -0
- package/dist/react/use-runtime-reconnect.js +1 -1
- package/dist/react/use-session-list-state.d.ts +6 -0
- package/dist/react/use-session-list-state.js +33 -0
- package/dist/react/use-session-log.d.ts +16 -0
- package/dist/react/use-session-log.js +143 -0
- package/dist/react/use-session-messages.d.ts +2 -1
- package/dist/react/use-session-messages.js +15 -14
- package/dist/react/use-session-stream.js +12 -1
- package/dist/react/use-session-sync.d.ts +2 -2
- package/dist/react/use-session-sync.js +10 -8
- package/dist/react/use-session-transcript-history.js +2 -12
- package/dist/react/use-session-working.d.ts +2 -0
- package/dist/react/use-session-working.js +10 -0
- package/dist/react/use-session.d.ts +6 -0
- package/dist/react/use-session.js +88 -6
- package/package.json +62 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { genuiVersionFromClassName, genuiVersionOf, separateGenuiClosers, splitGenui } from './fence.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { GenuiSegment } from './types.js';
|
|
2
|
+
export declare function genuiVersionOf(tag: string): number | null;
|
|
3
|
+
export declare function genuiVersionFromClassName(className: string | undefined): number | null;
|
|
4
|
+
export declare function fenceOpener(line: string): {
|
|
5
|
+
prefix: string;
|
|
6
|
+
marker: string;
|
|
7
|
+
version: number | null;
|
|
8
|
+
} | null;
|
|
9
|
+
export declare function isTopLevel(prefix: string): boolean;
|
|
10
|
+
export declare function closesFence(line: string, marker: string): boolean;
|
|
11
|
+
export interface ContainerFence {
|
|
12
|
+
prefix: string;
|
|
13
|
+
first: string;
|
|
14
|
+
marker: string;
|
|
15
|
+
nested: boolean;
|
|
16
|
+
version: number | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function containerFenceOpener(line: string): ContainerFence | null;
|
|
19
|
+
export declare function fenceBodyLine(line: string, fence: ContainerFence): string | null;
|
|
20
|
+
export declare function separateGenuiClosers(text: string): string;
|
|
21
|
+
export declare function splitGenui(text: string): GenuiSegment[];
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const V1_TAGS = new Set(['openui', 'openui-lang']);
|
|
2
|
+
const FENCE_START = /^([ \t>]*)(`{3,}|~{3,})/;
|
|
3
|
+
const INFO_TAG = /^[ \t]*([^\s`]*)/;
|
|
4
|
+
const CLOSE_FENCE = /^ {0,3}(`{3,}|~{3,})\s*$/;
|
|
5
|
+
const TOP_LEVEL = /^ {0,3}$/;
|
|
6
|
+
export function genuiVersionOf(tag) {
|
|
7
|
+
const lang = tag.trim().toLowerCase();
|
|
8
|
+
if (V1_TAGS.has(lang))
|
|
9
|
+
return 1;
|
|
10
|
+
const match = /^openui-v(\d{1,3})$/.exec(lang);
|
|
11
|
+
return match ? Number(match[1]) : null;
|
|
12
|
+
}
|
|
13
|
+
export function genuiVersionFromClassName(className) {
|
|
14
|
+
const match = /(?:^|\s)language-([\w-]+)/.exec(className ?? '');
|
|
15
|
+
return match?.[1] ? genuiVersionOf(match[1]) : null;
|
|
16
|
+
}
|
|
17
|
+
export function fenceOpener(line) {
|
|
18
|
+
const match = FENCE_START.exec(line);
|
|
19
|
+
if (!match)
|
|
20
|
+
return null;
|
|
21
|
+
const info = line.slice(match[0].length);
|
|
22
|
+
if (info.includes('`'))
|
|
23
|
+
return null;
|
|
24
|
+
return { prefix: match[1], marker: match[2], version: genuiVersionOf(INFO_TAG.exec(info)[1]) };
|
|
25
|
+
}
|
|
26
|
+
export function isTopLevel(prefix) {
|
|
27
|
+
return TOP_LEVEL.test(prefix);
|
|
28
|
+
}
|
|
29
|
+
export function closesFence(line, marker) {
|
|
30
|
+
const close = CLOSE_FENCE.exec(line);
|
|
31
|
+
return close !== null && close[1][0] === marker[0] && close[1].length >= marker.length;
|
|
32
|
+
}
|
|
33
|
+
const LIST_FENCE = /^([ \t>]*)([-*+]|\d{1,9}[.)])([ \t]+)(?=`{3}|~{3})/;
|
|
34
|
+
const LEAD = /^[ \t>]*/;
|
|
35
|
+
const quoteDepth = (prefix) => prefix.split('>').length - 1;
|
|
36
|
+
export function containerFenceOpener(line) {
|
|
37
|
+
const item = LIST_FENCE.exec(line);
|
|
38
|
+
const open = fenceOpener(item ? line.slice(item[0].length) : line);
|
|
39
|
+
if (!open)
|
|
40
|
+
return null;
|
|
41
|
+
const prefix = item ? item[1] + ' '.repeat(item[2].length + item[3].length) : open.prefix;
|
|
42
|
+
return { marker: open.marker, version: open.version, prefix, first: item ? item[0] : prefix, nested: item !== null || !isTopLevel(open.prefix) };
|
|
43
|
+
}
|
|
44
|
+
export function fenceBodyLine(line, fence) {
|
|
45
|
+
if (!fence.nested)
|
|
46
|
+
return line;
|
|
47
|
+
if (line.startsWith(fence.prefix))
|
|
48
|
+
return line.slice(fence.prefix.length);
|
|
49
|
+
const lead = LEAD.exec(line)[0];
|
|
50
|
+
if (fence.prefix.includes('>') && quoteDepth(lead) === quoteDepth(fence.prefix))
|
|
51
|
+
return line.slice(lead.length);
|
|
52
|
+
return line.trim() === '' || line.trimEnd() === fence.prefix.trimEnd() ? '' : null;
|
|
53
|
+
}
|
|
54
|
+
function gluedCloserAt(body, marker) {
|
|
55
|
+
let end = body.length;
|
|
56
|
+
while (end > 0 && (body[end - 1] === ' ' || body[end - 1] === '\t'))
|
|
57
|
+
end--;
|
|
58
|
+
let start = end;
|
|
59
|
+
while (start > 0 && body[start - 1] === marker[0])
|
|
60
|
+
start--;
|
|
61
|
+
const before = body[start - 1];
|
|
62
|
+
return end - start >= marker.length && (before === ')' || before === ']' || before === '"') ? start : -1;
|
|
63
|
+
}
|
|
64
|
+
export function separateGenuiClosers(text) {
|
|
65
|
+
if (!/openui/i.test(text))
|
|
66
|
+
return text;
|
|
67
|
+
const lines = text.split('\n');
|
|
68
|
+
let fence = null;
|
|
69
|
+
let changed = false;
|
|
70
|
+
for (let i = 0; i < lines.length; i++) {
|
|
71
|
+
const line = lines[i];
|
|
72
|
+
if (!fence) {
|
|
73
|
+
fence = containerFenceOpener(line);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const body = fenceBodyLine(line, fence);
|
|
77
|
+
if (body === null) {
|
|
78
|
+
fence = null;
|
|
79
|
+
i--;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (closesFence(body, fence.marker)) {
|
|
83
|
+
fence = null;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const at = fence.version === null ? -1 : gluedCloserAt(body, fence.marker);
|
|
87
|
+
if (at < 0)
|
|
88
|
+
continue;
|
|
89
|
+
const cut = line.length - body.length + at;
|
|
90
|
+
lines[i] = `${line.slice(0, cut)}\n${fence.prefix}${line.slice(cut).trimEnd()}`;
|
|
91
|
+
changed = true;
|
|
92
|
+
fence = null;
|
|
93
|
+
}
|
|
94
|
+
return changed ? lines.join('\n') : text;
|
|
95
|
+
}
|
|
96
|
+
export function splitGenui(text) {
|
|
97
|
+
const segments = [];
|
|
98
|
+
const lines = text.split('\n');
|
|
99
|
+
let markdown = [];
|
|
100
|
+
let fence = null;
|
|
101
|
+
const flushMarkdown = () => {
|
|
102
|
+
if (markdown.length > 0)
|
|
103
|
+
segments.push({ kind: 'markdown', text: markdown.join('\n') });
|
|
104
|
+
markdown = [];
|
|
105
|
+
};
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (!fence) {
|
|
108
|
+
const open = fenceOpener(line);
|
|
109
|
+
if (!open || !isTopLevel(open.prefix)) {
|
|
110
|
+
markdown.push(line);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
fence = { marker: open.marker, version: open.version, body: [] };
|
|
114
|
+
if (open.version === null)
|
|
115
|
+
markdown.push(line);
|
|
116
|
+
else
|
|
117
|
+
flushMarkdown();
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (!closesFence(line, fence.marker)) {
|
|
121
|
+
if (fence.version === null)
|
|
122
|
+
markdown.push(line);
|
|
123
|
+
else
|
|
124
|
+
fence.body.push(line);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (fence.version === null)
|
|
128
|
+
markdown.push(line);
|
|
129
|
+
else
|
|
130
|
+
segments.push({ kind: 'genui', code: fence.body.join('\n'), version: fence.version, closed: true });
|
|
131
|
+
fence = null;
|
|
132
|
+
}
|
|
133
|
+
if (fence && fence.version !== null) {
|
|
134
|
+
segments.push({ kind: 'genui', code: fence.body.join('\n'), version: fence.version, closed: false });
|
|
135
|
+
}
|
|
136
|
+
flushMarkdown();
|
|
137
|
+
return segments;
|
|
138
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { genuiVersionFromClassName, genuiVersionOf, separateGenuiClosers, splitGenui } from './fence.js';
|
|
2
|
+
export { GENUI_CUT_OFF_NOTE, GENUI_UNSUPPORTED_NOTE, genuiA11yText, genuiBlockToMarkdown, genuiNodeToMarkdown, genuiToMarkdown, } from './markdown.js';
|
|
3
|
+
export { createGenuiParser, parseGenui, type GenuiParser } from './parse.js';
|
|
4
|
+
export { buildGenuiPrompt, GENUI_PROMPT_VERSION } from './prompt.js';
|
|
5
|
+
export { GENUI_SCHEMA_VERSION, type GenuiIssue, type GenuiIssueCode, type GenuiNode, type GenuiParseResult, type GenuiSegment, } from './types.js';
|
|
6
|
+
export { safeUrl } from './urls.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { genuiVersionFromClassName, genuiVersionOf, separateGenuiClosers, splitGenui } from './fence.js';
|
|
2
|
+
export { GENUI_CUT_OFF_NOTE, GENUI_UNSUPPORTED_NOTE, genuiA11yText, genuiBlockToMarkdown, genuiNodeToMarkdown, genuiToMarkdown, } from './markdown.js';
|
|
3
|
+
export { createGenuiParser, parseGenui } from './parse.js';
|
|
4
|
+
export { buildGenuiPrompt, GENUI_PROMPT_VERSION } from './prompt.js';
|
|
5
|
+
export { GENUI_SCHEMA_VERSION, } from './types.js';
|
|
6
|
+
export { safeUrl } from './urls.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type GenuiNode, type GenuiParseResult } from './types.js';
|
|
2
|
+
export declare const GENUI_UNSUPPORTED_NOTE = "This content needs a newer version of Beeuni.";
|
|
3
|
+
export declare const GENUI_CUT_OFF_NOTE = "Response was cut off.";
|
|
4
|
+
export declare function genuiNodeToMarkdown(node: GenuiNode): string;
|
|
5
|
+
export declare function genuiA11yText(node: GenuiNode): string | null;
|
|
6
|
+
export declare function genuiResultToMarkdown(result: GenuiParseResult): string;
|
|
7
|
+
export declare function genuiBlockToMarkdown(code: string, version?: number, options?: {
|
|
8
|
+
streaming?: boolean;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function genuiToMarkdown(text: string, options?: {
|
|
11
|
+
streaming?: boolean;
|
|
12
|
+
}): string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { GENUI_SPECS } from './catalog.js';
|
|
2
|
+
import { closesFence, containerFenceOpener, fenceBodyLine, separateGenuiClosers, splitGenui } from './fence.js';
|
|
3
|
+
import { createGenuiParser } from './parse.js';
|
|
4
|
+
import { GENUI_SCHEMA_VERSION } from './types.js';
|
|
5
|
+
export const GENUI_UNSUPPORTED_NOTE = 'This content needs a newer version of Beeuni.';
|
|
6
|
+
export const GENUI_CUT_OFF_NOTE = 'Response was cut off.';
|
|
7
|
+
const UNAVAILABLE_NOTE = 'This content could not be displayed.';
|
|
8
|
+
export function genuiNodeToMarkdown(node) {
|
|
9
|
+
const spec = GENUI_SPECS[node.type];
|
|
10
|
+
return spec && !node.partial ? spec.toMarkdown(node.props, genuiNodeToMarkdown) : '';
|
|
11
|
+
}
|
|
12
|
+
export function genuiA11yText(node) {
|
|
13
|
+
return GENUI_SPECS[node.type]?.a11y?.(node.props) ?? null;
|
|
14
|
+
}
|
|
15
|
+
export function genuiResultToMarkdown(result) {
|
|
16
|
+
try {
|
|
17
|
+
const body = result.root ? genuiNodeToMarkdown(result.root) : '';
|
|
18
|
+
if (result.streaming)
|
|
19
|
+
return body;
|
|
20
|
+
if (result.issues.some((issue) => issue.code === 'cut-off'))
|
|
21
|
+
return [body, `*${GENUI_CUT_OFF_NOTE}*`].filter(Boolean).join('\n\n');
|
|
22
|
+
return result.root ? body : `*${UNAVAILABLE_NOTE}*`;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function genuiBlockToMarkdown(code, version = GENUI_SCHEMA_VERSION, options = {}) {
|
|
29
|
+
if (version !== GENUI_SCHEMA_VERSION)
|
|
30
|
+
return `*${GENUI_UNSUPPORTED_NOTE}*`;
|
|
31
|
+
return genuiResultToMarkdown(createGenuiParser(version).update(code, options.streaming ?? false));
|
|
32
|
+
}
|
|
33
|
+
function convertNestedBlocks(text, streaming) {
|
|
34
|
+
const lines = text.split('\n');
|
|
35
|
+
const out = [];
|
|
36
|
+
let fence = null;
|
|
37
|
+
let changed = false;
|
|
38
|
+
const finish = (unclosed) => {
|
|
39
|
+
if (fence?.body) {
|
|
40
|
+
changed = true;
|
|
41
|
+
const markdown = genuiBlockToMarkdown(fence.body.join('\n'), fence.version, { streaming: streaming && unclosed });
|
|
42
|
+
const { prefix, first } = fence;
|
|
43
|
+
if (markdown) {
|
|
44
|
+
markdown.split('\n').forEach((line, index) => out.push(index === 0 ? first + line : line ? prefix + line : prefix.trimEnd()));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
fence = null;
|
|
48
|
+
};
|
|
49
|
+
for (let i = 0; i < lines.length; i++) {
|
|
50
|
+
const line = lines[i];
|
|
51
|
+
if (!fence) {
|
|
52
|
+
const open = containerFenceOpener(line);
|
|
53
|
+
if (open) {
|
|
54
|
+
fence = { ...open, body: open.nested && open.version !== null ? [] : null };
|
|
55
|
+
if (fence.body)
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
out.push(line);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const inside = fenceBodyLine(line, fence);
|
|
62
|
+
if (inside === null) {
|
|
63
|
+
finish(false);
|
|
64
|
+
i--;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (closesFence(inside, fence.marker)) {
|
|
68
|
+
if (!fence.body)
|
|
69
|
+
out.push(line);
|
|
70
|
+
finish(false);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (fence.body)
|
|
74
|
+
fence.body.push(inside);
|
|
75
|
+
else
|
|
76
|
+
out.push(line);
|
|
77
|
+
}
|
|
78
|
+
finish(true);
|
|
79
|
+
return changed ? out.join('\n') : text;
|
|
80
|
+
}
|
|
81
|
+
function trimNewlines(text) {
|
|
82
|
+
let start = 0;
|
|
83
|
+
let end = text.length;
|
|
84
|
+
while (start < end && text[start] === '\n')
|
|
85
|
+
start++;
|
|
86
|
+
while (end > start && text[end - 1] === '\n')
|
|
87
|
+
end--;
|
|
88
|
+
return text.slice(start, end);
|
|
89
|
+
}
|
|
90
|
+
export function genuiToMarkdown(text, options = {}) {
|
|
91
|
+
if (!/openui/i.test(text))
|
|
92
|
+
return text;
|
|
93
|
+
const streaming = options.streaming ?? false;
|
|
94
|
+
const flat = convertNestedBlocks(separateGenuiClosers(text), streaming);
|
|
95
|
+
const segments = splitGenui(flat);
|
|
96
|
+
if (!segments.some((segment) => segment.kind === 'genui'))
|
|
97
|
+
return flat;
|
|
98
|
+
return segments
|
|
99
|
+
.map((segment) => segment.kind === 'markdown'
|
|
100
|
+
? trimNewlines(segment.text)
|
|
101
|
+
: genuiBlockToMarkdown(segment.code, segment.version, { streaming: streaming && !segment.closed }))
|
|
102
|
+
.filter((part) => part.trim().length > 0)
|
|
103
|
+
.join('\n\n');
|
|
104
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type GenuiParseResult } from './types.js';
|
|
2
|
+
export interface GenuiParser {
|
|
3
|
+
update(code: string, streaming: boolean): GenuiParseResult;
|
|
4
|
+
}
|
|
5
|
+
export declare function createGenuiParser(version?: number): GenuiParser;
|
|
6
|
+
export declare function parseGenui(code: string, version?: number): GenuiParseResult;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createStreamingParser } from '@openuidev/lang-core';
|
|
2
|
+
import { GENUI_LIBRARY } from './catalog.js';
|
|
3
|
+
import { expansionIssue, pendingStatementId } from './expansion.js';
|
|
4
|
+
import { shareNodes } from './share.js';
|
|
5
|
+
import { GENUI_SCHEMA_VERSION } from './types.js';
|
|
6
|
+
import { sanitizeTree } from './validate.js';
|
|
7
|
+
let librarySchema = null;
|
|
8
|
+
const schema = () => (librarySchema ??= GENUI_LIBRARY.toJSONSchema());
|
|
9
|
+
const fromLangCore = (error) => ({
|
|
10
|
+
code: error.code === 'unknown-component' ? 'unknown-component' : 'schema',
|
|
11
|
+
component: error.component,
|
|
12
|
+
statementId: error.statementId,
|
|
13
|
+
message: error.message,
|
|
14
|
+
});
|
|
15
|
+
const MAX_ISSUES = 50;
|
|
16
|
+
export function createGenuiParser(version = GENUI_SCHEMA_VERSION) {
|
|
17
|
+
if (version !== GENUI_SCHEMA_VERSION) {
|
|
18
|
+
const unsupported = {
|
|
19
|
+
root: null,
|
|
20
|
+
pending: [],
|
|
21
|
+
issues: [{ code: 'version', message: `Block version ${version}; this client renders ${GENUI_SCHEMA_VERSION}` }],
|
|
22
|
+
streaming: false,
|
|
23
|
+
};
|
|
24
|
+
return { update: () => unsupported };
|
|
25
|
+
}
|
|
26
|
+
let parser = createStreamingParser(schema(), 'Stack');
|
|
27
|
+
let last = null;
|
|
28
|
+
let previousNodes = new Map();
|
|
29
|
+
let lastGood = { root: null, pending: [] };
|
|
30
|
+
const rejected = (issue, streaming) => streaming ? { ...lastGood, issues: [issue], streaming } : { root: null, pending: [], issues: [issue], streaming };
|
|
31
|
+
const parse = (code, streaming) => {
|
|
32
|
+
let raw;
|
|
33
|
+
let sanitized;
|
|
34
|
+
try {
|
|
35
|
+
const issue = expansionIssue(code);
|
|
36
|
+
if (issue)
|
|
37
|
+
return rejected(issue, streaming);
|
|
38
|
+
raw = parser.set(code);
|
|
39
|
+
sanitized = sanitizeTree(raw.root, { streaming, unfinished: raw.meta.incomplete ? pendingStatementId(code) : null });
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
parser = createStreamingParser(schema(), 'Stack');
|
|
43
|
+
previousNodes = new Map();
|
|
44
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
45
|
+
return rejected({ code: 'parse-failed', message: message.slice(0, 200) }, streaming);
|
|
46
|
+
}
|
|
47
|
+
const shared = shareNodes(sanitized.root, previousNodes);
|
|
48
|
+
previousNodes = shared.byId;
|
|
49
|
+
lastGood = { root: shared.root, pending: raw.meta.unresolved };
|
|
50
|
+
const issues = [...raw.meta.errors.map(fromLangCore), ...sanitized.issues];
|
|
51
|
+
const unsupported = raw.queryStatements.length + raw.mutationStatements.length + Object.keys(raw.stateDeclarations).length;
|
|
52
|
+
if (unsupported > 0) {
|
|
53
|
+
issues.push({ code: 'unsupported-statement', message: `${unsupported} Query/Mutation/state statement(s) ignored` });
|
|
54
|
+
}
|
|
55
|
+
if (!streaming && !shared.root)
|
|
56
|
+
issues.push({ code: 'no-root', message: 'The block has no valid root block' });
|
|
57
|
+
const capped = issues.slice(0, MAX_ISSUES);
|
|
58
|
+
const cutOff = issues.find((issue) => issue.code === 'cut-off');
|
|
59
|
+
if (cutOff && !capped.includes(cutOff))
|
|
60
|
+
capped.push(cutOff);
|
|
61
|
+
return { root: shared.root, pending: raw.meta.unresolved, issues: capped, streaming };
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
update(code, streaming) {
|
|
65
|
+
if (last && last.code === code && last.streaming === streaming)
|
|
66
|
+
return last.result;
|
|
67
|
+
const result = parse(code, streaming);
|
|
68
|
+
last = { code, streaming, result };
|
|
69
|
+
return result;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function parseGenui(code, version = GENUI_SCHEMA_VERSION) {
|
|
74
|
+
return createGenuiParser(version).update(code, false);
|
|
75
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { GENUI_LIBRARY } from './catalog.js';
|
|
2
|
+
const PREAMBLE = `# Generative UI
|
|
3
|
+
|
|
4
|
+
The Beeuni chat renders \`openui\` code blocks inside your markdown reply as rich UI: step rows with photos,
|
|
5
|
+
option cards, checklists, image galleries, maps, tables, comparisons, stat rows, and charts.
|
|
6
|
+
|
|
7
|
+
## The shape of a visual answer
|
|
8
|
+
|
|
9
|
+
1. Open with 1 or 2 sentences that answer the question or give your recommendation.
|
|
10
|
+
2. Then one section per part of the answer: a \`##\` heading (numbered when there are 3 or more), 1 to 3 short
|
|
11
|
+
sentences, then the one block that shows the section. A short numbered list of exact actions may follow it.
|
|
12
|
+
Requirements, specs, and rules (size, format, deadline) are bullets with a bold label (**Photo size:** 35 × 45 mm),
|
|
13
|
+
never a Checklist. Words the user should say or send go in a \`>\` quote.
|
|
14
|
+
3. End with one closing sentence or a short "## My recommendation", then **Official source:** [page](url) when one exists.
|
|
15
|
+
After it, ask one short question only when one missing detail would change your recommendation, about that one detail.
|
|
16
|
+
Ask nothing when the answer is complete or the user asked for plain text.
|
|
17
|
+
|
|
18
|
+
One block per section, one job per block. A guide, a trip, or a how-to has 4 to 10 blocks. A short data answer
|
|
19
|
+
is one sentence and one block. Never put the whole answer in one block, and never put explanations inside a block.
|
|
20
|
+
|
|
21
|
+
Answer in this reply; never ask first. When a missing detail (dates, budget) changes the answer, assume the common case,
|
|
22
|
+
say so in one sentence, and give the whole answer. Never assume what the request already says (a weekend is both days).
|
|
23
|
+
Never reply with only questions, and never put questions in a Checklist.
|
|
24
|
+
|
|
25
|
+
Before you write: list the sections and pick each block. First run ONE web_search batch for the routes, times,
|
|
26
|
+
prices, and rules you will show; then ONE image_search for the photos, and the coordinates with bash (see Rules).
|
|
27
|
+
|
|
28
|
+
## Pick the block from the content
|
|
29
|
+
|
|
30
|
+
Products, places, food, and parts the user will buy, visit, eat, or handle always show their photos.
|
|
31
|
+
|
|
32
|
+
- a journey or route, leg by leg (home → station → airport) → Steps, one Step per leg, each with a photo
|
|
33
|
+
- a process the user does in the real world (check in, apply for a visa, fix a tire, cook a dish) → Steps, one Step per action
|
|
34
|
+
- things to pack or gather (a packing list, documents to collect) → Checklist
|
|
35
|
+
- what a correct result looks like (a valid passport photo, the part to buy) → one Image with text beside it
|
|
36
|
+
- 2 to 8 options to choose between (routes, flights, products, cafés, hotels) → a Stack of Cards, each with its photo.
|
|
37
|
+
Choosing how to do something (taxi or train, which flight) is always Cards, never Compare
|
|
38
|
+
- the user asks for a spec-by-spec table or pros and cons of 2 to 4 named options → Compare, with the winner set when you recommend one
|
|
39
|
+
- a ranked list of things with no photo (ideas, strategies) → RankedList. A product or place shortlist is Cards, never a RankedList
|
|
40
|
+
- 2 or more named places the user goes to or picks from (stations, hotels, sights) → Map; add Cards or Steps for them only when they carry photos or facts the pins do not
|
|
41
|
+
- what a place or a stage looks like → a gallery: a Stack of 3 Images
|
|
42
|
+
- a schedule or timeline, or other rows and columns → Table
|
|
43
|
+
- 2 to 4 headline numbers → StatRow. One number across items → BarChart. A value over time → LineChart. Shares of a whole or survey answers → PieChart
|
|
44
|
+
- one warning the user must not miss → Callout
|
|
45
|
+
- optional detail a reader may skip → Accordion. Views the user asked to switch between → Tabs
|
|
46
|
+
|
|
47
|
+
Never put the main steps or options in an Accordion or Tabs. Never write them as "### Step 1" headings or a long
|
|
48
|
+
numbered list: use Steps. 4 or more real-world actions in order are Steps, not prose or a numbered list. Never write a markdown table for data.
|
|
49
|
+
|
|
50
|
+
## Stay in prose
|
|
51
|
+
|
|
52
|
+
Write no block for chat, a fact under about 3 sentences, an explanation of how something works, a one-line opinion with no options to compare,
|
|
53
|
+
translating or rewriting text, and code. Code and terminal commands stay in ordinary code fences, so a software
|
|
54
|
+
install or setup guide is prose with code fences, at any length. When the user asks for plain text, markdown, JSON, or no UI, write exactly that format and no block.`;
|
|
55
|
+
const SYNTAX = `## Syntax
|
|
56
|
+
|
|
57
|
+
1. One statement per line: \`name = Component(arg1, arg2, ...)\`. Strings use double quotes.
|
|
58
|
+
2. Write \`root\` first. root is the one component the block shows. Use a Stack only for 2 or more components in one block.
|
|
59
|
+
3. Arguments are positional only: Stat("Users", "900"), never Stat(label: "Users", value: "900") and never Stat("Users", "900", unit="k"). To set a later argument, write null for each one you skip.
|
|
60
|
+
4. Every name except root is used by another statement. An unused name does not render.
|
|
61
|
+
5. Put the closing \`\`\` on its own line, never right after the last statement.
|
|
62
|
+
|
|
63
|
+
\`\`\`openui
|
|
64
|
+
root = Steps([a, b, c])
|
|
65
|
+
a = Step("Title", "What to do.")
|
|
66
|
+
b = Step("Title", "What to do.")
|
|
67
|
+
c = Step("Title", "What to do.")
|
|
68
|
+
\`\`\``;
|
|
69
|
+
const RULES = [
|
|
70
|
+
'Children of Stack, Tab, and AccordionItem are component references, never plain strings. Prose goes between blocks, never inside a Callout or a Card.',
|
|
71
|
+
'Respect argument order and limits: Map(markers, source, zoom?, route?). Step(title, text?, image?, meta?). Image(src, alt, caption?, text?). Checklist(title, items). Stat delta and Badge label are at most 24 characters. Checklist items and Table caption are at most 120 characters. Table takes exactly columns, rows, caption?.',
|
|
72
|
+
'Write at least one sentence of prose before the first block and between two blocks, except when the second block continues the first. The prose never repeats the block.',
|
|
73
|
+
'Show the same data once: never a chart and a table, or two charts, of the same numbers.',
|
|
74
|
+
'Images, links, numbers, prices, times, and coordinates come only from tool results, files, or the user. Never invent them. Charts and maps always fill source with the origin in 2 to 5 words (web_search, the billing export), never "provided" or "supplied". Mention a fact the tool results lack only when the user must act on it, once, as an instruction.',
|
|
75
|
+
'Get every photo for the reply in ONE image_search call before you write: one query per Step, Card, and Image, joined with |||, and num_results 3. For each item, use the first fitting result of its own query. A Step photo may show the kind of place or action (a check-in hall). Without image_search, use a web_search result image whose description matches. Never fetch pages for images. Never use one photo for two items: leave the image empty rather than use a wrong one.',
|
|
76
|
+
"Every Step and every Card in a list carries its photo, in the item's own image argument, never in a separate gallery under the items.",
|
|
77
|
+
'In an option Card, subtitle is the route, kind, or travel time line (City A → Hub City → City B, 60 to 90 min), price the cost, body 1 or 2 sentences, href its official or booking page, and Badge("My first choice", "good") marks your pick among 2 or more options: a single item, a day plan, or a schedule has no pick badge. A hotel, product, or place Card also carries its rating and reviews from a tool result. Leave price empty when no tool result gives it. A score out of 10 goes in subtitle with its review count (Old Town · Booking 9.3/10, 2,345 reviews), never halved and never a Badge; then leave rating and reviews empty.',
|
|
78
|
+
'Set a Stack of Cards to list when the text matters more than the picture (routes, flights, a shortlist, logos), and to grid when the picture is the choice (hotels, places, food): Stack([a, b], "col", "list").',
|
|
79
|
+
'A Step title is under 60 characters: the leg (Home → Central Station) or the action (Check in your bag). Its text is 1 to 3 short sentences, each on its own line with \\n. meta is a short time or cost (about 35 min, 5 USD), never an instruction. A fare or timetable time needs a tool result; a cooking or task time may come from know-how.',
|
|
80
|
+
'Steps holds 3 to 8 Steps, one leg or action each. Never pack several legs or actions into one Step. A single action is a sentence of prose, not Steps. A trip guide opens with a Steps of every leg, door to arrival, one short line per leg; the actions inside a leg go only in that leg\'s own section. A trip guide also maps the airports and stations it names.',
|
|
81
|
+
'Checklist items are short things the reader ticks off (Valid passport), each under 80 characters, 3 to 15 of them. Next steps, recommendations, swaps, tips, and questions stay prose or a short numbered list of at most 5 items, one sentence each, never a Checklist, RankedList, Callout, or Cards.',
|
|
82
|
+
'A gallery is a Stack of Images and nothing else; 3 read best, as one row. Put the name and a one-line note in each caption, and no text. Never put images in a RankedList or in Cards only to show them.',
|
|
83
|
+
"A Marker's value is the number the user asked about (a rating, a price, a population), never a word (no 'Luxury'): otherwise leave it empty.",
|
|
84
|
+
'To map places you know only by name or address, look them all up in ONE bash call, one request per second: printf "%s\\n" "Place One, City" "Place Two, City" | while IFS= read -r q; do curl -sG -A "Beeuni agent (https://beeuni.com)" https://nominatim.openstreetmap.org/search -d format=jsonv2 -d limit=1 --data-urlencode "q=$q"; echo; sleep 1; done. Keep a place only when its display_name names it; otherwise look it up once more, in a second bash call, with its street address from your web_search results, and keep that result when its display_name has the same street and house number. Write the Map with source "OpenStreetMap Nominatim". If fewer than 2 places are found, give links instead of a Map.',
|
|
85
|
+
'When the user asks to see an answer as cards, a list, a chart, a table, or a map, write the block in this reply. Do not call the show tool, build a page, or write a file unless they ask you to build or save one.',
|
|
86
|
+
'Write openui blocks only in your chat reply. Never put them in messages you send to Slack, Teams, email, or files.',
|
|
87
|
+
];
|
|
88
|
+
const EXAMPLES = [
|
|
89
|
+
`Example: a first-time travel guide. Every name, number, and URL is a placeholder: real ones come from your tool results.
|
|
90
|
+
|
|
91
|
+
After one web_search batch for the facts and one bash call for the coordinates, one call for every photo:
|
|
92
|
+
image_search(query: "Airport One Terminal 2 ||| plane takeoff runway ||| Hub Airport transfer area ||| Airport Two arrivals ||| City A taxi rank ||| City A train platform ||| Carrier One aircraft ||| Carrier Two aircraft ||| airport check-in ||| airport security ||| airport gate ||| Airport Two passport control", num_results: 3)
|
|
93
|
+
|
|
94
|
+
Then the reply:
|
|
95
|
+
|
|
96
|
+
Since this is your first flight, here is the whole trip from your door in City A to City B, with one connection at Hub City.
|
|
97
|
+
|
|
98
|
+
## 1. Your journey at a glance
|
|
99
|
+
|
|
100
|
+
Here is every leg, door to arrival.
|
|
101
|
+
|
|
102
|
+
\`\`\`openui
|
|
103
|
+
root = Steps([s1, s2, s3, s4])
|
|
104
|
+
s1 = Step("Home → Airport One", "Take a taxi to Terminal 2.", "https://example.com/photos/terminal-two.jpg", "60 to 90 min")
|
|
105
|
+
s2 = Step("Airport One → Hub Airport", "Your first flight, on Carrier One.", "https://example.com/photos/takeoff.jpg")
|
|
106
|
+
s3 = Step("Hub Airport", "Change planes inside the international transfer area.", "https://example.com/photos/hub-transfer.jpg")
|
|
107
|
+
s4 = Step("Airport Two", "Land in City B.", "https://example.com/photos/arrivals.jpg")
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
## 2. How to reach Airport One
|
|
111
|
+
|
|
112
|
+
Airport One is 25 km east of Central Station.
|
|
113
|
+
|
|
114
|
+
\`\`\`openui
|
|
115
|
+
root = Map([m1, m2], "OpenStreetMap Nominatim", 11)
|
|
116
|
+
m1 = Marker(10.01, 20.01, "Central Station", "Main entrance")
|
|
117
|
+
m2 = Marker(10.05, 20.24, "Airport One", "Terminal 2")
|
|
118
|
+
\`\`\`
|
|
119
|
+
|
|
120
|
+
You have two good choices. With two suitcases, take the taxi.
|
|
121
|
+
|
|
122
|
+
\`\`\`openui
|
|
123
|
+
root = Stack([taxi, train], "col", "list")
|
|
124
|
+
taxi = Card("Option A: taxi", "No changes and easiest with big bags. Book it the evening before.", "Home → Terminal 2, 60 to 90 min", "https://example.com/photos/taxi.jpg", null, [pick], null, null, "about 40 USD")
|
|
125
|
+
pick = Badge("Best for a first flight", "good")
|
|
126
|
+
train = Card("Option B: train", "Cheaper, but you carry your bags through Central Station.", "Central Station → Terminal 2, 70 to 100 min", "https://example.com/photos/train-platform.jpg", null, null, null, null, "about 5 USD")
|
|
127
|
+
\`\`\`
|
|
128
|
+
|
|
129
|
+
## 3. Which flight to book
|
|
130
|
+
|
|
131
|
+
Book both flights on one ticket, with a 2 to 4 hour connection, so your bag goes straight through.
|
|
132
|
+
|
|
133
|
+
\`\`\`openui
|
|
134
|
+
root = Stack([one, two], "col", "list")
|
|
135
|
+
one = Card("Carrier One", "One ticket and one short connection.", "City A → Hub City → City B", "https://example.com/photos/carrier-one.jpg", "https://example.com/carrier-one", [first])
|
|
136
|
+
first = Badge("My first choice", "good")
|
|
137
|
+
two = Card("Carrier Two", "A good alternative through Other Hub.", "City A → Other Hub → City B", "https://example.com/photos/carrier-two.jpg", "https://example.com/carrier-two")
|
|
138
|
+
\`\`\`
|
|
139
|
+
|
|
140
|
+
## 4. Inside Airport One
|
|
141
|
+
|
|
142
|
+
Arrive 3 hours before departure. This is the order you go through.
|
|
143
|
+
|
|
144
|
+
\`\`\`openui
|
|
145
|
+
root = Steps([c1, c2, c3])
|
|
146
|
+
c1 = Step("Check in your bag", "Find your airline's counter on the screens.\\nShow your passport and booking.", "https://example.com/photos/check-in.jpg")
|
|
147
|
+
c2 = Step("Immigration and security", "Show your passport and visa.\\nPut your laptop and liquids in the trays.", "https://example.com/photos/security.jpg", "about 30 min")
|
|
148
|
+
c3 = Step("Find your gate", "Your boarding pass shows the gate.\\nCheck the screens: gates can change.", "https://example.com/photos/gate.jpg")
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
## 5. After you land in City B
|
|
152
|
+
|
|
153
|
+
Passport control comes first.
|
|
154
|
+
|
|
155
|
+
\`\`\`openui
|
|
156
|
+
root = Image("https://example.com/photos/passport-control.jpg", "Passport control hall", null, "Join the line for foreign passports. Have your visa and hotel address ready.")
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
1. Follow the signs to passport control.
|
|
160
|
+
2. Collect your bag and walk through customs.
|
|
161
|
+
3. Take an official taxi or the airport bus to the centre.
|
|
162
|
+
|
|
163
|
+
## 6. What to pack
|
|
164
|
+
|
|
165
|
+
Keep these in your cabin bag.
|
|
166
|
+
|
|
167
|
+
\`\`\`openui
|
|
168
|
+
root = Checklist("Your travel checklist", ["Valid passport", "Visa or entry permit", "Flight booking and boarding passes", "Bank card and some cash", "Phone, charger, and power bank in your cabin bag"])
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
## 7. Departure day
|
|
172
|
+
|
|
173
|
+
For a 07:00 flight, plan the morning like this:
|
|
174
|
+
|
|
175
|
+
\`\`\`openui
|
|
176
|
+
root = Table(["Time", "What to do"], [["03:00", "Leave home by taxi"], ["04:00 to 06:00", "Check in, immigration, and security"], ["07:00", "Departure"]], "Example times, not your booking")
|
|
177
|
+
\`\`\`
|
|
178
|
+
|
|
179
|
+
My pick for a first trip: the taxi to Airport One, and Carrier One on one ticket through Hub City.`,
|
|
180
|
+
`Example: a short data answer (placeholders).
|
|
181
|
+
|
|
182
|
+
Revenue grew every quarter, by 50 USD in total.
|
|
183
|
+
|
|
184
|
+
\`\`\`openui
|
|
185
|
+
root = BarChart(["Q1", "Q2", "Q3"], [revenue], "billing export from the tool result", "USD")
|
|
186
|
+
revenue = Series("Revenue", [120, 150, 170])
|
|
187
|
+
\`\`\``,
|
|
188
|
+
];
|
|
189
|
+
export function buildGenuiPrompt() {
|
|
190
|
+
const signatures = Object.values(GENUI_LIBRARY.toSpec().components).map((c) => `${c.signature} — ${c.description}`);
|
|
191
|
+
return [
|
|
192
|
+
PREAMBLE,
|
|
193
|
+
SYNTAX,
|
|
194
|
+
`## Components\n\nArguments marked with ? are optional.\n\n${signatures.join('\n')}`,
|
|
195
|
+
`## Rules\n\n${RULES.map((rule) => `- ${rule}`).join('\n')}`,
|
|
196
|
+
`## Examples\n\n${EXAMPLES.join('\n\n')}`,
|
|
197
|
+
].join('\n\n');
|
|
198
|
+
}
|
|
199
|
+
export const GENUI_PROMPT_VERSION = (() => {
|
|
200
|
+
let hash = 0x811c9dc5;
|
|
201
|
+
for (const char of buildGenuiPrompt()) {
|
|
202
|
+
hash ^= char.codePointAt(0);
|
|
203
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
204
|
+
}
|
|
205
|
+
return hash.toString(16).padStart(8, '0');
|
|
206
|
+
})();
|