@iloveagents/foundry-web-ui 0.3.1 → 0.4.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 +13 -4
- package/dist/components/ag-ui-runtime-provider.d.ts +64 -0
- package/dist/components/ag-ui-runtime-provider.js +71 -0
- package/dist/components/app-brand.d.ts +6 -0
- package/dist/components/app-brand.js +13 -0
- package/dist/components/assistant-chat.d.ts +26 -0
- package/dist/components/assistant-chat.js +93 -0
- package/dist/components/chat-attachments.d.ts +25 -0
- package/dist/components/chat-attachments.js +20 -0
- package/dist/components/chat-bubble.d.ts +1 -0
- package/dist/components/chat-bubble.js +103 -0
- package/dist/components/chat-header.d.ts +2 -0
- package/dist/components/chat-header.js +16 -0
- package/dist/components/client-tool-executor.d.ts +12 -0
- package/dist/components/client-tool-executor.js +108 -0
- package/dist/components/collection-empty-state.d.ts +9 -0
- package/dist/components/collection-empty-state.js +6 -0
- package/dist/components/collection-filter-bar.d.ts +19 -0
- package/dist/components/collection-filter-bar.js +28 -0
- package/dist/components/collection-search-input.d.ts +6 -0
- package/dist/components/collection-search-input.js +8 -0
- package/dist/components/collection-skeleton.d.ts +6 -0
- package/dist/components/collection-skeleton.js +10 -0
- package/dist/components/collection-sort-menu.d.ts +10 -0
- package/dist/components/collection-sort-menu.js +9 -0
- package/dist/components/collection-surface.d.ts +8 -0
- package/dist/components/collection-surface.js +5 -0
- package/dist/components/collection-toolbar.d.ts +9 -0
- package/dist/components/collection-toolbar.js +5 -0
- package/dist/components/collection-view-toggle.d.ts +7 -0
- package/dist/components/collection-view-toggle.js +17 -0
- package/dist/components/composer-submit-bridge.d.ts +1 -0
- package/dist/components/composer-submit-bridge.js +15 -0
- package/dist/components/confirm-dialog.d.ts +11 -0
- package/dist/components/confirm-dialog.js +6 -0
- package/dist/components/confirmation-card.d.ts +14 -0
- package/dist/components/confirmation-card.js +12 -0
- package/dist/components/context-badges.d.ts +16 -0
- package/dist/components/context-badges.js +56 -0
- package/dist/components/context-bar.d.ts +41 -0
- package/dist/components/context-bar.js +123 -0
- package/dist/components/form-dialog.d.ts +12 -0
- package/dist/components/form-dialog.js +13 -0
- package/dist/components/global-selection-popover.d.ts +7 -0
- package/dist/components/global-selection-popover.js +167 -0
- package/dist/components/infinite-scroll-sentinel.d.ts +6 -0
- package/dist/components/infinite-scroll-sentinel.js +25 -0
- package/dist/components/json-viewer.d.ts +10 -0
- package/dist/components/json-viewer.js +80 -0
- package/dist/components/loading-indicator.d.ts +4 -0
- package/dist/components/loading-indicator.js +22 -0
- package/dist/components/markdown-text.d.ts +13 -0
- package/dist/components/markdown-text.js +221 -0
- package/dist/components/name-dialog.d.ts +16 -0
- package/dist/components/name-dialog.js +23 -0
- package/dist/components/selection-popover.d.ts +12 -0
- package/dist/components/selection-popover.js +111 -0
- package/dist/components/show-document-tool-ui.d.ts +1 -0
- package/dist/components/show-document-tool-ui.js +53 -0
- package/dist/components/sidebar.d.ts +4 -0
- package/dist/components/sidebar.js +904 -0
- package/dist/components/surface-card.d.ts +9 -0
- package/dist/components/surface-card.js +33 -0
- package/dist/components/theme-runtime-provider.d.ts +15 -0
- package/dist/components/theme-runtime-provider.js +48 -0
- package/dist/components/theme-toggle.d.ts +2 -0
- package/dist/components/theme-toggle.js +19 -0
- package/dist/components/tool-call-card.d.ts +27 -0
- package/dist/components/tool-call-card.js +13 -0
- package/dist/components/tool-fallback.d.ts +26 -0
- package/dist/components/tool-fallback.js +129 -0
- package/dist/components/tool-panel-layout.d.ts +10 -0
- package/dist/components/tool-panel-layout.js +12 -0
- package/dist/components/tool-panel.d.ts +1 -0
- package/dist/components/tool-panel.js +89 -0
- package/dist/components/tooltip-icon-button.d.ts +7 -0
- package/dist/components/tooltip-icon-button.js +9 -0
- package/dist/components/user-menu.d.ts +2 -0
- package/dist/components/user-menu.js +14 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +83 -0
- package/dist/lib/ag-ui-adapter.d.ts +29 -0
- package/dist/lib/ag-ui-adapter.js +373 -0
- package/dist/lib/app-store.d.ts +115 -0
- package/dist/lib/app-store.js +199 -0
- package/dist/lib/attachment-adapter.d.ts +20 -0
- package/dist/lib/attachment-adapter.js +99 -0
- package/dist/lib/auth-provider.d.ts +16 -0
- package/dist/lib/auth-provider.js +88 -0
- package/dist/lib/chat-bubble-store.d.ts +30 -0
- package/dist/lib/chat-bubble-store.js +23 -0
- package/dist/lib/chat-lifecycle-store.d.ts +10 -0
- package/{src/lib/chat-lifecycle-store.ts → dist/lib/chat-lifecycle-store.js} +3 -13
- package/dist/lib/client-tools.d.ts +11 -0
- package/dist/lib/client-tools.js +274 -0
- package/dist/lib/composer-submit-store.d.ts +12 -0
- package/dist/lib/composer-submit-store.js +17 -0
- package/dist/lib/dev-store.d.ts +38 -0
- package/dist/lib/dev-store.js +32 -0
- package/dist/lib/nav-config.d.ts +159 -0
- package/dist/lib/nav-config.js +162 -0
- package/dist/lib/nav-dnd.d.ts +8 -0
- package/dist/lib/nav-dnd.js +9 -0
- package/dist/lib/nav-store.d.ts +45 -0
- package/dist/lib/nav-store.js +49 -0
- package/dist/lib/selection-context.d.ts +21 -0
- package/dist/lib/selection-context.js +34 -0
- package/dist/lib/sidebar-store.d.ts +23 -0
- package/dist/lib/sidebar-store.js +98 -0
- package/dist/lib/theme-runtime.d.ts +130 -0
- package/dist/lib/theme-runtime.js +479 -0
- package/dist/lib/theme-store.d.ts +15 -0
- package/dist/lib/theme-store.js +53 -0
- package/dist/lib/tool-panel-store.d.ts +48 -0
- package/dist/lib/tool-panel-store.js +69 -0
- package/dist/lib/use-new-conversation.d.ts +8 -0
- package/dist/lib/use-new-conversation.js +62 -0
- package/dist/lib/use-page-tools.d.ts +27 -0
- package/{src/lib/use-page-tools.ts → dist/lib/use-page-tools.js} +11 -13
- package/dist/styles.css +142 -0
- package/dist/ui/collapsible.d.ts +5 -0
- package/{src/ui/collapsible.tsx → dist/ui/collapsible.js} +0 -2
- package/dist/ui/dialog.d.ts +15 -0
- package/dist/ui/dialog.js +25 -0
- package/dist/ui/dropdown-menu.d.ts +7 -0
- package/dist/ui/dropdown-menu.js +19 -0
- package/dist/ui/popover.d.ts +7 -0
- package/dist/ui/popover.js +10 -0
- package/dist/ui/select.d.ts +4 -0
- package/dist/ui/select.js +9 -0
- package/dist/ui/tooltip.d.ts +7 -0
- package/dist/ui/tooltip.js +10 -0
- package/package.json +31 -12
- package/AGENTS.md +0 -59
- package/CHANGELOG.md +0 -211
- package/CLAUDE.md +0 -1
- package/src/__tests__/no-spaces-imports.test.ts +0 -59
- package/src/__tests__/open-core-guards.test.ts +0 -307
- package/src/__tests__/theme-runtime.test.ts +0 -81
- package/src/__tests__/tool-fallback.test.tsx +0 -109
- package/src/components/__tests__/context-bar.test.ts +0 -112
- package/src/components/ag-ui-runtime-provider.tsx +0 -170
- package/src/components/app-brand.tsx +0 -38
- package/src/components/assistant-chat.tsx +0 -461
- package/src/components/chat-attachments.tsx +0 -68
- package/src/components/chat-bubble.tsx +0 -373
- package/src/components/chat-header.tsx +0 -44
- package/src/components/client-tool-executor.tsx +0 -230
- package/src/components/collection-empty-state.tsx +0 -37
- package/src/components/collection-filter-bar.tsx +0 -168
- package/src/components/collection-search-input.tsx +0 -41
- package/src/components/collection-skeleton.tsx +0 -55
- package/src/components/collection-sort-menu.tsx +0 -62
- package/src/components/collection-surface.tsx +0 -46
- package/src/components/collection-toolbar.tsx +0 -33
- package/src/components/collection-view-toggle.tsx +0 -61
- package/src/components/composer-submit-bridge.tsx +0 -19
- package/src/components/confirm-dialog.tsx +0 -49
- package/src/components/confirmation-card.tsx +0 -32
- package/src/components/context-badges.tsx +0 -136
- package/src/components/context-bar.tsx +0 -238
- package/src/components/form-dialog.tsx +0 -56
- package/src/components/global-selection-popover.tsx +0 -266
- package/src/components/infinite-scroll-sentinel.tsx +0 -48
- package/src/components/json-viewer.tsx +0 -232
- package/src/components/loading-indicator.tsx +0 -40
- package/src/components/markdown-text-citations.test.tsx +0 -108
- package/src/components/markdown-text.tsx +0 -508
- package/src/components/name-dialog.tsx +0 -87
- package/src/components/selection-popover.tsx +0 -156
- package/src/components/show-document-tool-ui.tsx +0 -90
- package/src/components/sidebar.test.tsx +0 -611
- package/src/components/sidebar.tsx +0 -1689
- package/src/components/surface-card.tsx +0 -45
- package/src/components/theme-runtime-provider.tsx +0 -93
- package/src/components/theme-toggle.tsx +0 -27
- package/src/components/tool-call-card.tsx +0 -126
- package/src/components/tool-fallback.tsx +0 -169
- package/src/components/tool-panel-layout.tsx +0 -36
- package/src/components/tool-panel.tsx +0 -233
- package/src/components/tooltip-icon-button.tsx +0 -28
- package/src/components/user-menu.tsx +0 -60
- package/src/index.ts +0 -185
- package/src/lib/__tests__/ag-ui-adapter.test.ts +0 -279
- package/src/lib/__tests__/app-store.test.ts +0 -405
- package/src/lib/__tests__/attachment-adapter.test.ts +0 -48
- package/src/lib/__tests__/chat-bubble-store.test.ts +0 -67
- package/src/lib/__tests__/client-tools.test.ts +0 -124
- package/src/lib/__tests__/dev-store.test.ts +0 -78
- package/src/lib/__tests__/nav-config.test.ts +0 -135
- package/src/lib/__tests__/nav-dnd.test.ts +0 -24
- package/src/lib/__tests__/nav-store.test.ts +0 -59
- package/src/lib/__tests__/selection-context.test.ts +0 -114
- package/src/lib/__tests__/sidebar-store.test.ts +0 -144
- package/src/lib/__tests__/theme-store.test.ts +0 -83
- package/src/lib/__tests__/tool-panel-store.test.ts +0 -61
- package/src/lib/__tests__/use-page-context.test.ts +0 -58
- package/src/lib/ag-ui-adapter.ts +0 -426
- package/src/lib/app-store.ts +0 -361
- package/src/lib/attachment-adapter.ts +0 -92
- package/src/lib/auth-provider.tsx +0 -144
- package/src/lib/chat-bubble-store.ts +0 -46
- package/src/lib/client-tools.ts +0 -293
- package/src/lib/composer-submit-store.ts +0 -32
- package/src/lib/dev-store.ts +0 -69
- package/src/lib/nav-config.ts +0 -329
- package/src/lib/nav-dnd.ts +0 -14
- package/src/lib/nav-store.ts +0 -89
- package/src/lib/selection-context.ts +0 -65
- package/src/lib/sidebar-store.ts +0 -113
- package/src/lib/theme-runtime.ts +0 -660
- package/src/lib/theme-store.ts +0 -67
- package/src/lib/tool-panel-store.ts +0 -121
- package/src/lib/use-new-conversation.test.tsx +0 -168
- package/src/lib/use-new-conversation.ts +0 -64
- package/src/ui/dialog.tsx +0 -109
- package/src/ui/dropdown-menu.tsx +0 -75
- package/src/ui/popover.tsx +0 -34
- package/src/ui/select.tsx +0 -15
- package/src/ui/tooltip.tsx +0 -30
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -16
- package/vitest.config.ts +0 -8
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the error-shape sniffing helpers in
|
|
3
|
-
* ``tool-fallback.tsx``. The component itself renders a collapsible
|
|
4
|
-
* card which is awkward to assert from node-land without
|
|
5
|
-
* ``@testing-library/react`` (not a workspace dep); exercising the
|
|
6
|
-
* pure helpers covers the interesting logic. The helpers are
|
|
7
|
-
* exported with an underscore prefix specifically for this use.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { describe, expect, it } from "vitest";
|
|
11
|
-
import {
|
|
12
|
-
_detectErrorResult,
|
|
13
|
-
_extractErrorMessage,
|
|
14
|
-
} from "../components/tool-fallback.tsx";
|
|
15
|
-
|
|
16
|
-
describe("_detectErrorResult", () => {
|
|
17
|
-
it("returns false for null / undefined", () => {
|
|
18
|
-
expect(_detectErrorResult(null)).toBe(false);
|
|
19
|
-
expect(_detectErrorResult(undefined)).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("returns false for normal strings", () => {
|
|
23
|
-
expect(_detectErrorResult("Found 3 documents.")).toBe(false);
|
|
24
|
-
expect(_detectErrorResult("")).toBe(false);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("detects the agent-framework `Error: ...` serialization", () => {
|
|
28
|
-
expect(_detectErrorResult("Error: search backend 500")).toBe(true);
|
|
29
|
-
expect(_detectErrorResult(" Error: with leading whitespace")).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("does NOT false-positive on strings containing the word Error mid-text", () => {
|
|
33
|
-
// This is the failure the anchored regex exists to prevent.
|
|
34
|
-
expect(
|
|
35
|
-
_detectErrorResult("A passage about Error Handling in Python"),
|
|
36
|
-
).toBe(false);
|
|
37
|
-
expect(_detectErrorResult("Error Boundaries in React")).toBe(false);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("detects MCP canonical `{isError: true, content: [...]}`", () => {
|
|
41
|
-
expect(
|
|
42
|
-
_detectErrorResult({
|
|
43
|
-
isError: true,
|
|
44
|
-
content: [{ type: "text", text: "forbidden" }],
|
|
45
|
-
}),
|
|
46
|
-
).toBe(true);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("returns false when isError is false/undefined", () => {
|
|
50
|
-
expect(_detectErrorResult({ isError: false, content: [] })).toBe(false);
|
|
51
|
-
expect(_detectErrorResult({ data: [] })).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("detects structured `{error: 'msg'}`", () => {
|
|
55
|
-
expect(_detectErrorResult({ error: "boom" })).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("does not treat an empty `error` string as an error", () => {
|
|
59
|
-
// Avoids false-positives where a tool echoes `error: ""` in a success
|
|
60
|
-
// envelope. Narrow by design.
|
|
61
|
-
expect(_detectErrorResult({ error: "" })).toBe(false);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("detects `{error: {message: 'msg'}}`", () => {
|
|
65
|
-
expect(_detectErrorResult({ error: { message: "timed out" } })).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe("_extractErrorMessage", () => {
|
|
70
|
-
it("strips the `Error: ` prefix from string errors", () => {
|
|
71
|
-
expect(_extractErrorMessage("Error: search backend 500")).toBe(
|
|
72
|
-
"search backend 500",
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("returns `Execution failed` when the string is just `Error:`", () => {
|
|
77
|
-
expect(_extractErrorMessage("Error:")).toBe("Execution failed");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("returns null for normal strings (so caller falls back)", () => {
|
|
81
|
-
expect(_extractErrorMessage("All good")).toBeNull();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("extracts `error: 'msg'` directly", () => {
|
|
85
|
-
expect(_extractErrorMessage({ error: "timed out" })).toBe("timed out");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("extracts nested `error.message`", () => {
|
|
89
|
-
expect(
|
|
90
|
-
_extractErrorMessage({ error: { message: "invalid token" } }),
|
|
91
|
-
).toBe("invalid token");
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("extracts the first text item from MCP content arrays", () => {
|
|
95
|
-
expect(
|
|
96
|
-
_extractErrorMessage({
|
|
97
|
-
isError: true,
|
|
98
|
-
content: [
|
|
99
|
-
{ type: "text", text: "forbidden" },
|
|
100
|
-
{ type: "text", text: "second" },
|
|
101
|
-
],
|
|
102
|
-
}),
|
|
103
|
-
).toBe("forbidden");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("returns null when no recognisable payload is present", () => {
|
|
107
|
-
expect(_extractErrorMessage({ random: "shape" })).toBeNull();
|
|
108
|
-
});
|
|
109
|
-
});
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for ``getNavigablePath`` — the function powering
|
|
3
|
-
* click-to-navigate on pinned context chips. Two shapes the framework
|
|
4
|
-
* supports: page (uses payload.path) and ref (uses sourcePage as the
|
|
5
|
-
* deep-link URL set by the source module).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, expect, it } from "vitest";
|
|
9
|
-
import { getNavigablePath } from "../context-bar.tsx";
|
|
10
|
-
import type { ContextItem } from "../../lib/app-store.ts";
|
|
11
|
-
|
|
12
|
-
function pageItem(path: string): ContextItem {
|
|
13
|
-
return {
|
|
14
|
-
id: "i-1",
|
|
15
|
-
type: "page",
|
|
16
|
-
label: "Workspace",
|
|
17
|
-
payload: { kind: "page", path, label: "Workspace" },
|
|
18
|
-
sourcePage: path,
|
|
19
|
-
persistence: "persistent",
|
|
20
|
-
createdAt: 0,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function refItem(refId: string, sourcePage: string): ContextItem {
|
|
25
|
-
return {
|
|
26
|
-
id: "i-2",
|
|
27
|
-
type: "ref",
|
|
28
|
-
label: "Field · cell",
|
|
29
|
-
payload: { kind: "ref", refType: "block", refId },
|
|
30
|
-
sourcePage,
|
|
31
|
-
persistence: "persistent",
|
|
32
|
-
createdAt: 0,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
describe("getNavigablePath", () => {
|
|
37
|
-
it("returns the page payload path for page items", () => {
|
|
38
|
-
expect(getNavigablePath(pageItem("/spaces/abc"))).toBe("/spaces/abc");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("returns the ref's sourcePage URL (incl. hash) for ref items", () => {
|
|
42
|
-
// The source module places the deep-link in sourcePage when it
|
|
43
|
-
// creates the ref; the framework surfaces it without knowing
|
|
44
|
-
// anything about the ref shape.
|
|
45
|
-
expect(
|
|
46
|
-
getNavigablePath(refItem("abc:b1", "/spaces/abc#block-b1")),
|
|
47
|
-
).toBe("/spaces/abc#block-b1");
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("returns null when a ref has no sourcePage", () => {
|
|
51
|
-
const item = refItem("abc:b1", "");
|
|
52
|
-
expect(getNavigablePath(item)).toBeNull();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("returns null for selection items (excerpts are not anchors)", () => {
|
|
56
|
-
const item: ContextItem = {
|
|
57
|
-
id: "i-3",
|
|
58
|
-
type: "selection",
|
|
59
|
-
label: "x",
|
|
60
|
-
payload: { kind: "text", text: "x" },
|
|
61
|
-
sourcePage: "/spaces/abc",
|
|
62
|
-
persistence: "ephemeral",
|
|
63
|
-
createdAt: 0,
|
|
64
|
-
};
|
|
65
|
-
expect(getNavigablePath(item)).toBeNull();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("returns null for note items (use text payload — note shares it)", () => {
|
|
69
|
-
// ``ContextPayload`` is a closed union of ``text | page | ref``.
|
|
70
|
-
// Note items reuse the ``text`` payload (the ``type`` field is
|
|
71
|
-
// what distinguishes them from selections).
|
|
72
|
-
const item: ContextItem = {
|
|
73
|
-
id: "i-4",
|
|
74
|
-
type: "note",
|
|
75
|
-
label: "n",
|
|
76
|
-
payload: { kind: "text", text: "n" },
|
|
77
|
-
sourcePage: "/spaces/abc",
|
|
78
|
-
persistence: "ephemeral",
|
|
79
|
-
createdAt: 0,
|
|
80
|
-
};
|
|
81
|
-
expect(getNavigablePath(item)).toBeNull();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("returns null for ref items whose sourcePage isn't a route-like path", () => {
|
|
85
|
-
// Some callers populate ``sourcePage`` with a free-form
|
|
86
|
-
// breadcrumb label rather than a URL. Guard against navigate()
|
|
87
|
-
// pushing a relative/invalid route.
|
|
88
|
-
const item: ContextItem = {
|
|
89
|
-
id: "i-5",
|
|
90
|
-
type: "ref",
|
|
91
|
-
label: "Field · x",
|
|
92
|
-
payload: { kind: "ref", refType: "block", refId: "abc:b1" },
|
|
93
|
-
sourcePage: "Workspace breadcrumb",
|
|
94
|
-
persistence: "persistent",
|
|
95
|
-
createdAt: 0,
|
|
96
|
-
};
|
|
97
|
-
expect(getNavigablePath(item)).toBeNull();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("accepts hash-only sourcePages (same-page deep-link)", () => {
|
|
101
|
-
const item: ContextItem = {
|
|
102
|
-
id: "i-6",
|
|
103
|
-
type: "ref",
|
|
104
|
-
label: "Field · x",
|
|
105
|
-
payload: { kind: "ref", refType: "block", refId: "abc:b1" },
|
|
106
|
-
sourcePage: "#block-b1",
|
|
107
|
-
persistence: "persistent",
|
|
108
|
-
createdAt: 0,
|
|
109
|
-
};
|
|
110
|
-
expect(getNavigablePath(item)).toBe("#block-b1");
|
|
111
|
-
});
|
|
112
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext, useState, useCallback, useMemo } from "react";
|
|
2
|
-
import { AssistantRuntimeProvider, useLocalRuntime } from "@assistant-ui/react";
|
|
3
|
-
import type { ThreadHistoryAdapter } from "@assistant-ui/react";
|
|
4
|
-
import { TooltipProvider } from "../ui/tooltip.tsx";
|
|
5
|
-
import { AGUIAdapterSDK } from "../lib/ag-ui-adapter.ts";
|
|
6
|
-
import { FileAttachmentAdapter } from "../lib/attachment-adapter.ts";
|
|
7
|
-
import { ShowDocumentToolUI } from "./show-document-tool-ui.tsx";
|
|
8
|
-
import { ClientToolExecutor } from "./client-tool-executor.tsx";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Per-mount inputs the host shell passes when it wants a chat
|
|
12
|
-
* conversation history wired to the runtime. Mirrors
|
|
13
|
-
* :type:`ChatConversationFactoryArgs` in ``foundry-web-shell`` —
|
|
14
|
-
* duplicated here (rather than imported) so the web-ui package stays
|
|
15
|
-
* independent of the shell package's dependency graph.
|
|
16
|
-
*/
|
|
17
|
-
export interface AGUIChatConversationFactoryArgs {
|
|
18
|
-
/** Conversation id captured from the URL (resumed chat) or undefined (fresh). */
|
|
19
|
-
urlMatch?: string;
|
|
20
|
-
/** Stable AG-UI adapter thread id — present in both cases. */
|
|
21
|
-
aguiThreadId: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Factory the consumer passes to build a :type:`ThreadHistoryAdapter`
|
|
26
|
-
* once the AG-UI adapter has minted its thread id. Called inside the
|
|
27
|
-
* keyed runtime mount; assistant-ui invokes ``load()``/``append()``
|
|
28
|
-
* via the returned adapter.
|
|
29
|
-
*/
|
|
30
|
-
export type AGUIHistoryAdapterFactory = (
|
|
31
|
-
args: AGUIChatConversationFactoryArgs,
|
|
32
|
-
) => ThreadHistoryAdapter;
|
|
33
|
-
|
|
34
|
-
interface AGUIRuntimeProviderProps {
|
|
35
|
-
children: React.ReactNode;
|
|
36
|
-
/** Custom fetch for agent requests. Handles URL rewriting + auth in production. */
|
|
37
|
-
fetchFn?: typeof fetch;
|
|
38
|
-
/**
|
|
39
|
-
* Optional stable thread id for the AG-UI adapter. When set, the
|
|
40
|
-
* agent's POST body carries this id so server-side persistence
|
|
41
|
-
* (Foundry thread, conversation row) targets the same conversation
|
|
42
|
-
* across reloads / resumes. Changing the id remounts the inner
|
|
43
|
-
* runtime — past messages from a different thread don't bleed
|
|
44
|
-
* into the new one.
|
|
45
|
-
*/
|
|
46
|
-
threadId?: string;
|
|
47
|
-
/**
|
|
48
|
-
* Conversation id captured from the current URL when the user
|
|
49
|
-
* resumed an existing chat (e.g. ``/chat/<id>``), or ``undefined``
|
|
50
|
-
* for a brand-new chat. Distinct from ``threadId``: the host shell
|
|
51
|
-
* MAY set ``threadId`` from a non-URL source (a sticky active
|
|
52
|
-
* conversation, a freshly-minted UUID) so ``threadId`` alone can't
|
|
53
|
-
* tell a history adapter "we're resuming". ``urlMatch`` carries
|
|
54
|
-
* that signal verbatim into ``historyAdapterFactory`` so the
|
|
55
|
-
* adapter can decide whether to load history (resume) or
|
|
56
|
-
* initialise empty (fresh) without inferring from ``threadId``.
|
|
57
|
-
*/
|
|
58
|
-
urlMatch?: string;
|
|
59
|
-
/**
|
|
60
|
-
* Factory that builds the assistant-ui ``ThreadHistoryAdapter`` for
|
|
61
|
-
* this mount. Called once after the AG-UI adapter exists so it can
|
|
62
|
-
* receive both the URL match (when resuming) and the adapter's own
|
|
63
|
-
* thread id (always set — covers fresh chats). Pass alongside
|
|
64
|
-
* ``threadId`` for resumed chats; pass without ``threadId`` to get
|
|
65
|
-
* a fresh-chat runtime that still persists.
|
|
66
|
-
*/
|
|
67
|
-
historyAdapterFactory?: AGUIHistoryAdapterFactory;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
interface AGUIContextValue {
|
|
71
|
-
adapter: AGUIAdapterSDK;
|
|
72
|
-
resetThread: () => void;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const AGUIAdapterContext = createContext<AGUIContextValue | null>(null);
|
|
76
|
-
|
|
77
|
-
export function useAGUIAdapter() {
|
|
78
|
-
const context = useContext(AGUIAdapterContext);
|
|
79
|
-
if (!context) {
|
|
80
|
-
throw new Error("useAGUIAdapter must be used within AGUIRuntimeProvider");
|
|
81
|
-
}
|
|
82
|
-
return context;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function AGUIRuntimeProvider(props: AGUIRuntimeProviderProps) {
|
|
86
|
-
// Splitting render across an outer wrapper + a keyed inner is the
|
|
87
|
-
// canonical React idiom for "rebuild this stateful subtree when X
|
|
88
|
-
// changes". assistant-ui's runtime is created inside useLocalRuntime
|
|
89
|
-
// and tracks its messages internally; swapping threadId or history
|
|
90
|
-
// mid-life would leave stale state. Keying the inner subtree on
|
|
91
|
-
// threadId (+ the manual reset counter) guarantees a clean runtime
|
|
92
|
-
// per conversation, matching the ChatGPT-style "click → fresh chat
|
|
93
|
-
// backed by past messages" UX.
|
|
94
|
-
const [resetKey, setResetKey] = useState(0);
|
|
95
|
-
const resetThread = useCallback(() => setResetKey((p) => p + 1), []);
|
|
96
|
-
|
|
97
|
-
const innerKey = `${props.threadId ?? "fresh"}:${resetKey}`;
|
|
98
|
-
|
|
99
|
-
return (
|
|
100
|
-
<AGUIRuntimeInner key={innerKey} {...props} resetThread={resetThread} />
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function AGUIRuntimeInner({
|
|
105
|
-
children,
|
|
106
|
-
fetchFn,
|
|
107
|
-
threadId,
|
|
108
|
-
urlMatch,
|
|
109
|
-
historyAdapterFactory,
|
|
110
|
-
resetThread,
|
|
111
|
-
}: AGUIRuntimeProviderProps & { resetThread: () => void }) {
|
|
112
|
-
const adapter = useMemo(
|
|
113
|
-
() =>
|
|
114
|
-
new AGUIAdapterSDK(
|
|
115
|
-
"/api/agent",
|
|
116
|
-
// Only pass options if at least one is set, mirroring the prior
|
|
117
|
-
// single-arg semantics for the "fresh chat, no overrides" case.
|
|
118
|
-
fetchFn || threadId ? { fetchFn, threadId } : undefined,
|
|
119
|
-
),
|
|
120
|
-
[fetchFn, threadId],
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const attachmentAdapter = useMemo(() => new FileAttachmentAdapter(), []);
|
|
124
|
-
|
|
125
|
-
// Build the history adapter ONCE per inner mount, after the AG-UI
|
|
126
|
-
// adapter exists so we know its thread id. The keyed-remount in the
|
|
127
|
-
// outer wrapper ensures threadId changes trigger a fresh adapter +
|
|
128
|
-
// fresh history adapter together, so resumed chats hydrate from the
|
|
129
|
-
// right conversation and fresh chats persist under their assistant-
|
|
130
|
-
// ui-minted UUID.
|
|
131
|
-
const historyAdapter = useMemo<ThreadHistoryAdapter | undefined>(() => {
|
|
132
|
-
if (!historyAdapterFactory) return undefined;
|
|
133
|
-
return historyAdapterFactory({
|
|
134
|
-
// ``urlMatch`` is the URL-derived resume signal (undefined for
|
|
135
|
-
// fresh chats). ``aguiThreadId`` is always defined — the AG-UI
|
|
136
|
-
// adapter mints it on construction. The factory uses ``urlMatch``
|
|
137
|
-
// to decide "load history" vs "initialise empty", and
|
|
138
|
-
// ``aguiThreadId`` to identify the row to write into.
|
|
139
|
-
urlMatch,
|
|
140
|
-
aguiThreadId: adapter.threadId,
|
|
141
|
-
});
|
|
142
|
-
// adapter is stable for the life of this inner mount (keyed remount
|
|
143
|
-
// covers the threadId change case), so it's OK to depend on the
|
|
144
|
-
// factory + urlMatch here.
|
|
145
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
146
|
-
}, [historyAdapterFactory, urlMatch]);
|
|
147
|
-
|
|
148
|
-
const runtime = useLocalRuntime(adapter, {
|
|
149
|
-
adapters: {
|
|
150
|
-
attachments: attachmentAdapter,
|
|
151
|
-
// history is optional — when undefined, the runtime acts as a
|
|
152
|
-
// pure in-memory thread (the original behaviour). When provided,
|
|
153
|
-
// assistant-ui calls .load() on mount to seed past messages and
|
|
154
|
-
// .append() after every turn.
|
|
155
|
-
...(historyAdapter ? { history: historyAdapter } : {}),
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
return (
|
|
160
|
-
<AGUIAdapterContext.Provider value={{ adapter, resetThread }}>
|
|
161
|
-
<AssistantRuntimeProvider runtime={runtime}>
|
|
162
|
-
<TooltipProvider>
|
|
163
|
-
<ShowDocumentToolUI />
|
|
164
|
-
<ClientToolExecutor />
|
|
165
|
-
{children}
|
|
166
|
-
</TooltipProvider>
|
|
167
|
-
</AssistantRuntimeProvider>
|
|
168
|
-
</AGUIAdapterContext.Provider>
|
|
169
|
-
);
|
|
170
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Layers3 } from "lucide-react";
|
|
2
|
-
import { cn } from "@iloveagents/foundry-web-primitives";
|
|
3
|
-
import { useThemeRuntime } from "./theme-runtime-provider.tsx";
|
|
4
|
-
|
|
5
|
-
export function AppBrand({
|
|
6
|
-
showName = true,
|
|
7
|
-
className,
|
|
8
|
-
iconClassName,
|
|
9
|
-
labelClassName,
|
|
10
|
-
}: {
|
|
11
|
-
showName?: boolean;
|
|
12
|
-
className?: string;
|
|
13
|
-
iconClassName?: string;
|
|
14
|
-
labelClassName?: string;
|
|
15
|
-
}) {
|
|
16
|
-
const runtime = useThemeRuntime();
|
|
17
|
-
const brandName = runtime.branding.appName || "eomi";
|
|
18
|
-
const logoUrl =
|
|
19
|
-
runtime.mode === "dark"
|
|
20
|
-
? runtime.branding.darkLogoUrl || runtime.branding.logoUrl
|
|
21
|
-
: runtime.branding.logoUrl || runtime.branding.darkLogoUrl;
|
|
22
|
-
const shouldShowName = showName && !logoUrl;
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<span className={cn("flex items-center gap-2", className)}>
|
|
26
|
-
{logoUrl ? (
|
|
27
|
-
<img
|
|
28
|
-
src={logoUrl}
|
|
29
|
-
alt={brandName}
|
|
30
|
-
className={cn("h-5 w-auto max-w-[7rem] shrink-0 object-contain", iconClassName)}
|
|
31
|
-
/>
|
|
32
|
-
) : (
|
|
33
|
-
<Layers3 className={cn("size-5 shrink-0 text-primary", iconClassName)} />
|
|
34
|
-
)}
|
|
35
|
-
{shouldShowName && <span className={cn("truncate", labelClassName)}>{brandName}</span>}
|
|
36
|
-
</span>
|
|
37
|
-
);
|
|
38
|
-
}
|