@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,279 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adapter shim regression tests.
|
|
3
|
-
*
|
|
4
|
-
* The shim is a generator-driven assistant-ui `ChatModelAdapter` wrapping
|
|
5
|
-
* `@iloveagents/foundry-agent`'s `AGUIRunner`. The contract under test:
|
|
6
|
-
* - Every intermediate yield carries `status: { type: "running" }`.
|
|
7
|
-
* - The FINAL yield settles the message so the bubble unsticks (the
|
|
8
|
-
* previous adapter left it `running` and it got stuck loading — PR #116):
|
|
9
|
-
* `{ type: "complete", reason: "stop" }` on success, or
|
|
10
|
-
* `{ type: "incomplete", reason: "error", error }` on RUN_ERROR so the
|
|
11
|
-
* UI shows a visible error instead of a silent empty bubble.
|
|
12
|
-
* - `content` is cumulative — every yield holds the full content array,
|
|
13
|
-
* not deltas (assistant-ui v0.12 contract).
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
17
|
-
import type { RunnerEvent } from "@iloveagents/foundry-agent";
|
|
18
|
-
|
|
19
|
-
const appStoreMock = vi.hoisted(() => ({
|
|
20
|
-
agentState: {
|
|
21
|
-
contextItems: [] as Array<Record<string, unknown>>,
|
|
22
|
-
navigation: { contextInstructions: null as string | null },
|
|
23
|
-
},
|
|
24
|
-
consumedMessageIds: [] as string[],
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
const runMock = vi.fn();
|
|
28
|
-
const runnerMock = vi.fn().mockImplementation(() => ({
|
|
29
|
-
threadId: "thread-1",
|
|
30
|
-
state: {},
|
|
31
|
-
run: runMock,
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
|
-
vi.mock("@iloveagents/foundry-agent", () => ({
|
|
35
|
-
AGUIRunner: function AGUIRunnerCtor(...args: unknown[]) {
|
|
36
|
-
return runnerMock(...args);
|
|
37
|
-
},
|
|
38
|
-
clientToolRegistry: {
|
|
39
|
-
getState: () => ({
|
|
40
|
-
isRegistered: (name: string) => name.startsWith("ui_"),
|
|
41
|
-
executeTool: async () => "{}",
|
|
42
|
-
getActiveSchemas: () => [],
|
|
43
|
-
}),
|
|
44
|
-
},
|
|
45
|
-
streamingStatusStore: {
|
|
46
|
-
getState: () => ({ setStreamingStatus: () => {} }),
|
|
47
|
-
},
|
|
48
|
-
}));
|
|
49
|
-
|
|
50
|
-
vi.mock("@iloveagents/foundry-agent/msal", () => ({
|
|
51
|
-
tokenFetch: globalThis.fetch,
|
|
52
|
-
}));
|
|
53
|
-
|
|
54
|
-
vi.mock("../app-store.ts", () => ({
|
|
55
|
-
useAppStore: {
|
|
56
|
-
getState: () => ({
|
|
57
|
-
markThreadActive: () => {},
|
|
58
|
-
getAgentState: () => appStoreMock.agentState,
|
|
59
|
-
consumeContextForMessage: (messageId: string) => {
|
|
60
|
-
appStoreMock.consumedMessageIds.push(messageId);
|
|
61
|
-
},
|
|
62
|
-
}),
|
|
63
|
-
},
|
|
64
|
-
}));
|
|
65
|
-
|
|
66
|
-
vi.mock("../dev-store.ts", () => ({
|
|
67
|
-
useDevStore: {
|
|
68
|
-
getState: () => ({
|
|
69
|
-
captureRequest: () => {},
|
|
70
|
-
addToolCall: () => {},
|
|
71
|
-
updateToolCall: () => {},
|
|
72
|
-
}),
|
|
73
|
-
},
|
|
74
|
-
}));
|
|
75
|
-
|
|
76
|
-
import { AGUIAdapterSDK } from "../ag-ui-adapter.ts";
|
|
77
|
-
|
|
78
|
-
function makeRunInput() {
|
|
79
|
-
return {
|
|
80
|
-
messages: [
|
|
81
|
-
{ id: "u-1", role: "user" as const, content: [{ type: "text" as const, text: "hi" }] },
|
|
82
|
-
],
|
|
83
|
-
abortSignal: undefined as AbortSignal | undefined,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
describe("AGUIAdapterSDK", () => {
|
|
88
|
-
beforeEach(() => {
|
|
89
|
-
runMock.mockReset();
|
|
90
|
-
runnerMock.mockClear();
|
|
91
|
-
appStoreMock.agentState = {
|
|
92
|
-
contextItems: [],
|
|
93
|
-
navigation: { contextInstructions: null },
|
|
94
|
-
};
|
|
95
|
-
appStoreMock.consumedMessageIds = [];
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("yields a terminal complete after a text-only run", async () => {
|
|
99
|
-
runMock.mockImplementation(async function* (): AsyncGenerator<RunnerEvent> {
|
|
100
|
-
yield { type: "turn-started" };
|
|
101
|
-
yield { type: "request-sent", input: {} };
|
|
102
|
-
yield { type: "run-started" };
|
|
103
|
-
yield { type: "text-delta", delta: "hello" };
|
|
104
|
-
yield { type: "text-message-end" };
|
|
105
|
-
yield { type: "run-finished" };
|
|
106
|
-
yield { type: "streaming-status", status: { status: "idle" } };
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const adapter = new AGUIAdapterSDK();
|
|
110
|
-
const out: Array<{ status: unknown; content: unknown }> = [];
|
|
111
|
-
for await (const r of adapter.run(makeRunInput())) {
|
|
112
|
-
out.push({ status: r.status, content: r.content });
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
expect(out.length).toBeGreaterThan(0);
|
|
116
|
-
expect(out[out.length - 1].status).toEqual({ type: "complete", reason: "stop" });
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("yields a terminal complete after a multi-turn client-tool follow-up", async () => {
|
|
120
|
-
// Reproduces the regression from PR #116 review: the previous shim
|
|
121
|
-
// kept yielding `status: running` after the final run-finished because
|
|
122
|
-
// the `needsFollowUp` check stayed true once a client tool resolved.
|
|
123
|
-
runMock.mockImplementation(async function* (): AsyncGenerator<RunnerEvent> {
|
|
124
|
-
// Turn 1 — agent calls client-side ui_navigate, runner executes locally.
|
|
125
|
-
yield { type: "turn-started" };
|
|
126
|
-
yield { type: "request-sent", input: {} };
|
|
127
|
-
yield { type: "run-started" };
|
|
128
|
-
yield { type: "tool-call-start", id: "tc-1", name: "ui_navigate", isClientSide: true };
|
|
129
|
-
yield { type: "tool-call-args", id: "tc-1", delta: '{"path":"/foo"}' };
|
|
130
|
-
yield {
|
|
131
|
-
type: "tool-call-end",
|
|
132
|
-
id: "tc-1",
|
|
133
|
-
args: '{"path":"/foo"}',
|
|
134
|
-
result: { status: "success" },
|
|
135
|
-
};
|
|
136
|
-
yield { type: "run-finished" };
|
|
137
|
-
// Turn 2 — agent acks with text after follow-up.
|
|
138
|
-
yield { type: "turn-started" };
|
|
139
|
-
yield { type: "request-sent", input: {} };
|
|
140
|
-
yield { type: "run-started" };
|
|
141
|
-
yield { type: "text-delta", delta: "Done" };
|
|
142
|
-
yield { type: "text-message-end" };
|
|
143
|
-
yield { type: "run-finished" };
|
|
144
|
-
yield { type: "streaming-status", status: { status: "idle" } };
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const adapter = new AGUIAdapterSDK();
|
|
148
|
-
const out: ChatModelRunResultLike[] = [];
|
|
149
|
-
for await (const r of adapter.run(makeRunInput())) {
|
|
150
|
-
out.push({ status: r.status, content: r.content });
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
expect(out.length).toBeGreaterThan(0);
|
|
154
|
-
|
|
155
|
-
// Every intermediate yield is "running".
|
|
156
|
-
for (const r of out.slice(0, -1)) {
|
|
157
|
-
expect(r.status).toEqual({ type: "running" });
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// The FINAL yield is complete (the regression: previously stuck on running).
|
|
161
|
-
expect(out[out.length - 1].status).toEqual({ type: "complete", reason: "stop" });
|
|
162
|
-
|
|
163
|
-
// Content is cumulative — the final yield carries both the resolved
|
|
164
|
-
// tool-call and the text from turn 2.
|
|
165
|
-
const finalContent = out[out.length - 1].content as Array<{
|
|
166
|
-
type: string;
|
|
167
|
-
toolCallId?: string;
|
|
168
|
-
text?: string;
|
|
169
|
-
}>;
|
|
170
|
-
expect(finalContent.find((p) => p.type === "tool-call")?.toolCallId).toBe("tc-1");
|
|
171
|
-
expect(finalContent.find((p) => p.type === "text")?.text).toBe("Done");
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("settles run-error as incomplete/error so the UI shows it (does not throw)", async () => {
|
|
175
|
-
runMock.mockImplementation(async function* (): AsyncGenerator<RunnerEvent> {
|
|
176
|
-
yield { type: "turn-started" };
|
|
177
|
-
yield { type: "request-sent", input: {} };
|
|
178
|
-
yield { type: "run-started" };
|
|
179
|
-
yield { type: "run-error", message: "boom" };
|
|
180
|
-
yield { type: "streaming-status", status: { status: "idle" } };
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const adapter = new AGUIAdapterSDK();
|
|
184
|
-
const out: ChatModelRunResultLike[] = [];
|
|
185
|
-
// On RUN_ERROR the adapter settles the message as incomplete/error
|
|
186
|
-
// (NOT a silent complete/stop) so assistant-ui's <MessagePrimitive.Error>
|
|
187
|
-
// renders a visible, retryable error. It must still settle via a
|
|
188
|
-
// terminal yield rather than throw — an unhandled throw re-introduces
|
|
189
|
-
// the stuck-running regression (message abandoned mid-run).
|
|
190
|
-
for await (const r of adapter.run(makeRunInput())) {
|
|
191
|
-
out.push({ status: r.status, content: r.content });
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
expect(out[out.length - 1].status).toEqual({
|
|
195
|
-
type: "incomplete",
|
|
196
|
-
reason: "error",
|
|
197
|
-
error: "boom",
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("forwards app context items through the native AG-UI context field", async () => {
|
|
202
|
-
appStoreMock.agentState = {
|
|
203
|
-
navigation: { contextInstructions: null },
|
|
204
|
-
contextItems: [
|
|
205
|
-
{
|
|
206
|
-
type: "ref",
|
|
207
|
-
refType: "block",
|
|
208
|
-
refId: "entity-1:block-2",
|
|
209
|
-
preview: "A selected table cell",
|
|
210
|
-
targetPath: "$.blocks[?(@.id=='block-2')].rows[0].cells[1]",
|
|
211
|
-
sourcePage: "/spaces/entity-1#block-block-2",
|
|
212
|
-
},
|
|
213
|
-
],
|
|
214
|
-
};
|
|
215
|
-
runMock.mockImplementation(async function* (): AsyncGenerator<RunnerEvent> {});
|
|
216
|
-
|
|
217
|
-
const adapter = new AGUIAdapterSDK();
|
|
218
|
-
for await (const _ of adapter.run(makeRunInput())) {
|
|
219
|
-
// Drain the adapter.
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
expect(runMock).toHaveBeenCalledTimes(1);
|
|
223
|
-
expect(runMock.mock.calls[0][0].context).toEqual([
|
|
224
|
-
{
|
|
225
|
-
description:
|
|
226
|
-
"Selected block atom. Use targetPath as the exact edit target when the user asks to modify, rewrite, or transform selected content.",
|
|
227
|
-
value: JSON.stringify(appStoreMock.agentState.contextItems[0]),
|
|
228
|
-
},
|
|
229
|
-
]);
|
|
230
|
-
expect(appStoreMock.consumedMessageIds).toEqual(["u-1"]);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("describes typed JSON ref targets without losing the stable ref handle", async () => {
|
|
234
|
-
appStoreMock.agentState = {
|
|
235
|
-
navigation: { contextInstructions: null },
|
|
236
|
-
contextItems: [
|
|
237
|
-
{
|
|
238
|
-
type: "ref",
|
|
239
|
-
label: "Page Instructions · Test Page",
|
|
240
|
-
refType: "entity-field",
|
|
241
|
-
refId: "entity-1:instructions.agent_instructions",
|
|
242
|
-
preview: "Current: old\nProposed: new",
|
|
243
|
-
targetPath: "$.instructions.agent_instructions",
|
|
244
|
-
target: {
|
|
245
|
-
kind: "json-field",
|
|
246
|
-
path: "$.instructions.agent_instructions",
|
|
247
|
-
label: "Page Instructions",
|
|
248
|
-
scope: "entity-field",
|
|
249
|
-
containerId: "entity-1",
|
|
250
|
-
containerLabel: "Test Page",
|
|
251
|
-
currentValue: "old",
|
|
252
|
-
proposedValue: "new",
|
|
253
|
-
changeIds: ["change-1"],
|
|
254
|
-
},
|
|
255
|
-
sourcePage: "/spaces/entity-1",
|
|
256
|
-
},
|
|
257
|
-
],
|
|
258
|
-
};
|
|
259
|
-
runMock.mockImplementation(async function* (): AsyncGenerator<RunnerEvent> {});
|
|
260
|
-
|
|
261
|
-
const adapter = new AGUIAdapterSDK();
|
|
262
|
-
for await (const _ of adapter.run(makeRunInput())) {
|
|
263
|
-
// Drain the adapter.
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
expect(runMock.mock.calls[0][0].context).toEqual([
|
|
267
|
-
{
|
|
268
|
-
description:
|
|
269
|
-
"Selected entity-field Page Instructions. Use targetPath as the exact JSON edit target when the user asks to modify, rewrite, or transform this reference.",
|
|
270
|
-
value: JSON.stringify(appStoreMock.agentState.contextItems[0]),
|
|
271
|
-
},
|
|
272
|
-
]);
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
interface ChatModelRunResultLike {
|
|
277
|
-
status: unknown;
|
|
278
|
-
content: unknown;
|
|
279
|
-
}
|
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { useAppStore } from "../app-store";
|
|
3
|
-
|
|
4
|
-
const reset = () =>
|
|
5
|
-
useAppStore.setState({
|
|
6
|
-
threadActive: false,
|
|
7
|
-
currentPage: "/chat",
|
|
8
|
-
navContext: {
|
|
9
|
-
group: null,
|
|
10
|
-
groupDescription: null,
|
|
11
|
-
groupMeta: {},
|
|
12
|
-
label: "Chat",
|
|
13
|
-
description: null,
|
|
14
|
-
meta: {},
|
|
15
|
-
contextInstructions: null,
|
|
16
|
-
},
|
|
17
|
-
selectedItemId: null,
|
|
18
|
-
content: {},
|
|
19
|
-
highlights: [],
|
|
20
|
-
contextItems: [],
|
|
21
|
-
sentContext: {},
|
|
22
|
-
});
|
|
23
|
-
const store = () => useAppStore.getState();
|
|
24
|
-
|
|
25
|
-
const ephemeralItem = (label = "hello", sourcePage = "panel") => ({
|
|
26
|
-
type: "selection" as const,
|
|
27
|
-
label,
|
|
28
|
-
payload: { kind: "text" as const, text: label },
|
|
29
|
-
sourcePage,
|
|
30
|
-
persistence: "ephemeral" as const,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const persistentItem = (label = "pinned", sourcePage = "panel") => ({
|
|
34
|
-
type: "selection" as const,
|
|
35
|
-
label,
|
|
36
|
-
payload: { kind: "text" as const, text: label },
|
|
37
|
-
sourcePage,
|
|
38
|
-
persistence: "persistent" as const,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const pageItem = (path = "/workspace", label = "Form") => ({
|
|
42
|
-
type: "page" as const,
|
|
43
|
-
label,
|
|
44
|
-
payload: { kind: "page" as const, path, label },
|
|
45
|
-
sourcePage: path,
|
|
46
|
-
persistence: "persistent" as const,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const refItem = (refId = "entity-1:block-1", preview = "Introduction") => ({
|
|
50
|
-
type: "ref" as const,
|
|
51
|
-
label: preview,
|
|
52
|
-
payload: { kind: "ref" as const, refType: "block", refId, preview },
|
|
53
|
-
sourcePage: "/spaces/entity-1",
|
|
54
|
-
persistence: "persistent" as const,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const scopedRefItem = (
|
|
58
|
-
selectionScope: "block" | "text" | "atom",
|
|
59
|
-
preview = "Introduction",
|
|
60
|
-
targetPath?: string,
|
|
61
|
-
) => ({
|
|
62
|
-
type: "ref" as const,
|
|
63
|
-
label: preview,
|
|
64
|
-
payload: {
|
|
65
|
-
kind: "ref" as const,
|
|
66
|
-
refType: "block",
|
|
67
|
-
refId: "entity-1:block-1",
|
|
68
|
-
preview,
|
|
69
|
-
meta: {
|
|
70
|
-
selectionScope,
|
|
71
|
-
...(targetPath ? { targetPath } : {}),
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
sourcePage: "/spaces/entity-1#block-block-1",
|
|
75
|
-
persistence: selectionScope === "block" ? ("persistent" as const) : ("ephemeral" as const),
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe("app-store", () => {
|
|
79
|
-
beforeEach(reset);
|
|
80
|
-
|
|
81
|
-
describe("addContextItem", () => {
|
|
82
|
-
it("adds an item with generated id and createdAt", () => {
|
|
83
|
-
store().addContextItem(ephemeralItem());
|
|
84
|
-
expect(store().contextItems).toHaveLength(1);
|
|
85
|
-
expect(store().contextItems[0]).toMatchObject({
|
|
86
|
-
label: "hello",
|
|
87
|
-
sourcePage: "panel",
|
|
88
|
-
type: "selection",
|
|
89
|
-
persistence: "ephemeral",
|
|
90
|
-
});
|
|
91
|
-
expect(store().contextItems[0].id).toBeTruthy();
|
|
92
|
-
expect(store().contextItems[0].createdAt).toBeGreaterThan(0);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("deduplicates by label + sourcePage", () => {
|
|
96
|
-
store().addContextItem(ephemeralItem("same", "src"));
|
|
97
|
-
store().addContextItem(ephemeralItem("same", "src"));
|
|
98
|
-
expect(store().contextItems).toHaveLength(1);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("allows same label from different sources", () => {
|
|
102
|
-
store().addContextItem(ephemeralItem("same", "src-a"));
|
|
103
|
-
store().addContextItem(ephemeralItem("same", "src-b"));
|
|
104
|
-
expect(store().contextItems).toHaveLength(2);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("caps at 10 items", () => {
|
|
108
|
-
for (let i = 0; i < 11; i++) {
|
|
109
|
-
store().addContextItem(ephemeralItem(`text-${i}`));
|
|
110
|
-
}
|
|
111
|
-
expect(store().contextItems).toHaveLength(10);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("deduplicates page items by path", () => {
|
|
115
|
-
store().addContextItem(pageItem("/workspace", "Form"));
|
|
116
|
-
store().addContextItem(pageItem("/workspace", "Form copy"));
|
|
117
|
-
expect(store().contextItems).toHaveLength(1);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("allows page items with different paths", () => {
|
|
121
|
-
store().addContextItem(pageItem("/workspace"));
|
|
122
|
-
store().addContextItem(pageItem("/documents"));
|
|
123
|
-
expect(store().contextItems).toHaveLength(2);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("deduplicates ref items by refId", () => {
|
|
127
|
-
store().addContextItem(refItem("e1:b1", "Intro"));
|
|
128
|
-
store().addContextItem(refItem("e1:b1", "Intro copy"));
|
|
129
|
-
expect(store().contextItems).toHaveLength(1);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("allows scoped refs for the same block alongside the durable block ref", () => {
|
|
133
|
-
store().addContextItem(scopedRefItem("block", "Whole block"));
|
|
134
|
-
store().addContextItem(scopedRefItem("text", "Specific phrase"));
|
|
135
|
-
store().addContextItem(
|
|
136
|
-
scopedRefItem("atom", "Table cell", "$.blocks[?(@.id=='block-1')].rows[0].cells[0]"),
|
|
137
|
-
);
|
|
138
|
-
expect(store().contextItems).toHaveLength(3);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("deduplicates scoped refs by their actual scope key", () => {
|
|
142
|
-
store().addContextItem(scopedRefItem("text", "Specific phrase"));
|
|
143
|
-
store().addContextItem(scopedRefItem("text", "Specific phrase"));
|
|
144
|
-
store().addContextItem(
|
|
145
|
-
scopedRefItem("atom", "Table cell", "$.blocks[?(@.id=='block-1')].rows[0].cells[0]"),
|
|
146
|
-
);
|
|
147
|
-
store().addContextItem(
|
|
148
|
-
scopedRefItem("atom", "Updated table cell", "$.blocks[?(@.id=='block-1')].rows[0].cells[0]"),
|
|
149
|
-
);
|
|
150
|
-
expect(store().contextItems).toHaveLength(2);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it("allows ref items with different refIds", () => {
|
|
154
|
-
store().addContextItem(refItem("e1:b1"));
|
|
155
|
-
store().addContextItem(refItem("e1:b2"));
|
|
156
|
-
expect(store().contextItems).toHaveLength(2);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("truncates label longer than 500 chars", () => {
|
|
160
|
-
const longLabel = "a".repeat(600);
|
|
161
|
-
store().addContextItem(ephemeralItem(longLabel));
|
|
162
|
-
expect(store().contextItems[0].label).toHaveLength(503);
|
|
163
|
-
expect(store().contextItems[0].label.endsWith("...")).toBe(true);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe("removeContextItem", () => {
|
|
168
|
-
it("removes by id, leaves others", () => {
|
|
169
|
-
store().addContextItem(ephemeralItem("a"));
|
|
170
|
-
store().addContextItem(ephemeralItem("b"));
|
|
171
|
-
const idToRemove = store().contextItems[0].id;
|
|
172
|
-
store().removeContextItem(idToRemove);
|
|
173
|
-
expect(store().contextItems).toHaveLength(1);
|
|
174
|
-
expect(store().contextItems[0].label).toBe("b");
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe("consumeContextForMessage", () => {
|
|
179
|
-
it("keeps persistent items, removes ephemeral", () => {
|
|
180
|
-
store().addContextItem(persistentItem("pinned"));
|
|
181
|
-
store().addContextItem(ephemeralItem("temp"));
|
|
182
|
-
const snapshot = store().consumeContextForMessage("msg-1");
|
|
183
|
-
|
|
184
|
-
expect(snapshot).toHaveLength(2);
|
|
185
|
-
expect(store().contextItems).toHaveLength(1);
|
|
186
|
-
expect(store().contextItems[0].label).toBe("pinned");
|
|
187
|
-
expect(store().sentContext["msg-1"]).toHaveLength(2);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("returns empty array without side effects when no items", () => {
|
|
191
|
-
const snapshot = store().consumeContextForMessage("msg-1");
|
|
192
|
-
expect(snapshot).toEqual([]);
|
|
193
|
-
expect(store().sentContext).toEqual({});
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
describe("clearAllContext", () => {
|
|
198
|
-
it("resets items and sentContext", () => {
|
|
199
|
-
store().addContextItem(ephemeralItem("a"));
|
|
200
|
-
store().consumeContextForMessage("msg-1");
|
|
201
|
-
store().addContextItem(ephemeralItem("b"));
|
|
202
|
-
store().clearAllContext();
|
|
203
|
-
expect(store().contextItems).toHaveLength(0);
|
|
204
|
-
expect(store().sentContext).toEqual({});
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
describe("promoteContextItem", () => {
|
|
209
|
-
it("changes ephemeral to persistent", () => {
|
|
210
|
-
store().addContextItem(ephemeralItem("temp"));
|
|
211
|
-
const id = store().contextItems[0].id;
|
|
212
|
-
store().promoteContextItem(id);
|
|
213
|
-
expect(store().contextItems[0].persistence).toBe("persistent");
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
describe("navContext", () => {
|
|
218
|
-
it("setNavContext updates navigation context", () => {
|
|
219
|
-
store().setNavContext({
|
|
220
|
-
group: "Contracts",
|
|
221
|
-
groupDescription: "Contract workspace",
|
|
222
|
-
groupMeta: { orgId: "org-1" },
|
|
223
|
-
label: "Acme NDA",
|
|
224
|
-
description: "Review NDA terms",
|
|
225
|
-
meta: { contractId: "123" },
|
|
226
|
-
contextInstructions: null,
|
|
227
|
-
});
|
|
228
|
-
expect(store().navContext.group).toBe("Contracts");
|
|
229
|
-
expect(store().navContext.groupMeta).toEqual({ orgId: "org-1" });
|
|
230
|
-
expect(store().navContext.label).toBe("Acme NDA");
|
|
231
|
-
expect(store().navContext.meta).toEqual({ contractId: "123" });
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe("setCurrentPage", () => {
|
|
236
|
-
it("clears ephemeral context on navigation", () => {
|
|
237
|
-
store().addContextItem(ephemeralItem("temp"));
|
|
238
|
-
store().addContextItem(persistentItem("pinned"));
|
|
239
|
-
store().setCurrentPage("/workspace");
|
|
240
|
-
expect(store().contextItems).toHaveLength(1);
|
|
241
|
-
expect(store().contextItems[0].label).toBe("pinned");
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("does not clear when navigating to the same page", () => {
|
|
245
|
-
store().addContextItem(ephemeralItem("temp"));
|
|
246
|
-
store().setCurrentPage("/chat"); // same as default
|
|
247
|
-
expect(store().contextItems).toHaveLength(1);
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
describe("content", () => {
|
|
252
|
-
it("updates individual fields", () => {
|
|
253
|
-
store().updateContentField("title", "My Draft");
|
|
254
|
-
store().updateContentField("priority", "high");
|
|
255
|
-
expect(store().content).toEqual({ title: "My Draft", priority: "high" });
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("clearContent resets to empty", () => {
|
|
259
|
-
store().updateContentField("title", "test");
|
|
260
|
-
store().clearContent();
|
|
261
|
-
expect(store().content).toEqual({});
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
describe("highlights", () => {
|
|
266
|
-
it("adds and removes highlights", () => {
|
|
267
|
-
store().addHighlight("field-1");
|
|
268
|
-
store().addHighlight("field-2");
|
|
269
|
-
expect(store().highlights).toEqual(["field-1", "field-2"]);
|
|
270
|
-
|
|
271
|
-
store().removeHighlight("field-1");
|
|
272
|
-
expect(store().highlights).toEqual(["field-2"]);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it("deduplicates highlights", () => {
|
|
276
|
-
store().addHighlight("field-1");
|
|
277
|
-
store().addHighlight("field-1");
|
|
278
|
-
expect(store().highlights).toHaveLength(1);
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
describe("getAgentState", () => {
|
|
283
|
-
it("returns correct projection with navigation context", () => {
|
|
284
|
-
store().setCurrentPage("/workspace");
|
|
285
|
-
store().setNavContext({
|
|
286
|
-
group: "Pages",
|
|
287
|
-
groupDescription: null,
|
|
288
|
-
groupMeta: {},
|
|
289
|
-
label: "Workspace",
|
|
290
|
-
description: "Collaborate on drafts",
|
|
291
|
-
meta: {},
|
|
292
|
-
contextInstructions: null,
|
|
293
|
-
});
|
|
294
|
-
store().setSelectedItemId("doc-1");
|
|
295
|
-
store().updateContentField("title", "Test");
|
|
296
|
-
store().addHighlight("field-1");
|
|
297
|
-
store().addContextItem(persistentItem("ctx"));
|
|
298
|
-
|
|
299
|
-
const projection = store().getAgentState();
|
|
300
|
-
expect(projection).toEqual({
|
|
301
|
-
navigation: {
|
|
302
|
-
group: "Pages",
|
|
303
|
-
groupDescription: null,
|
|
304
|
-
groupMeta: {},
|
|
305
|
-
page: "/workspace",
|
|
306
|
-
label: "Workspace",
|
|
307
|
-
description: "Collaborate on drafts",
|
|
308
|
-
meta: {},
|
|
309
|
-
contextInstructions: null,
|
|
310
|
-
},
|
|
311
|
-
selectedItemId: "doc-1",
|
|
312
|
-
content: { title: "Test" },
|
|
313
|
-
highlights: ["field-1"],
|
|
314
|
-
contextItems: [
|
|
315
|
-
{
|
|
316
|
-
type: "selection",
|
|
317
|
-
label: "ctx",
|
|
318
|
-
text: "ctx",
|
|
319
|
-
sourcePage: "panel",
|
|
320
|
-
},
|
|
321
|
-
],
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it("includes navigation metadata", () => {
|
|
326
|
-
store().setNavContext({
|
|
327
|
-
group: "Contracts",
|
|
328
|
-
groupDescription: "Contract workspace",
|
|
329
|
-
groupMeta: { orgId: "org-1" },
|
|
330
|
-
label: "Acme NDA",
|
|
331
|
-
description: "Review NDA",
|
|
332
|
-
meta: { contractId: "123", type: "nda" },
|
|
333
|
-
contextInstructions: null,
|
|
334
|
-
});
|
|
335
|
-
store().setCurrentPage("/contracts/123");
|
|
336
|
-
|
|
337
|
-
const projection = store().getAgentState();
|
|
338
|
-
expect(projection.navigation.meta).toEqual({ contractId: "123", type: "nda" });
|
|
339
|
-
expect(projection.navigation.groupMeta).toEqual({ orgId: "org-1" });
|
|
340
|
-
expect(projection.navigation.group).toBe("Contracts");
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it("compacts reference target values within the documented context limit", () => {
|
|
344
|
-
const longValue = "x".repeat(2_001);
|
|
345
|
-
|
|
346
|
-
store().addContextItem({
|
|
347
|
-
type: "ref",
|
|
348
|
-
label: "Long field",
|
|
349
|
-
sourcePage: "/spaces/entity-1",
|
|
350
|
-
persistence: "ephemeral",
|
|
351
|
-
payload: {
|
|
352
|
-
kind: "ref",
|
|
353
|
-
refType: "entity-field",
|
|
354
|
-
refId: "entity-1",
|
|
355
|
-
target: {
|
|
356
|
-
path: "$.instructions.page",
|
|
357
|
-
label: "Page Instructions",
|
|
358
|
-
currentValue: longValue,
|
|
359
|
-
proposedValue: longValue,
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
const projection = store().getAgentState();
|
|
365
|
-
const target = projection.contextItems[0].target as {
|
|
366
|
-
currentValue: string;
|
|
367
|
-
proposedValue: string;
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
expect(target.currentValue).toHaveLength(2_000);
|
|
371
|
-
expect(target.currentValue.endsWith("...")).toBe(true);
|
|
372
|
-
expect(target.proposedValue).toHaveLength(2_000);
|
|
373
|
-
expect(target.proposedValue.endsWith("...")).toBe(true);
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
describe("resetAll", () => {
|
|
378
|
-
it("resets all state to defaults", () => {
|
|
379
|
-
store().markThreadActive();
|
|
380
|
-
store().setNavContext({
|
|
381
|
-
group: "Contracts",
|
|
382
|
-
groupDescription: "test",
|
|
383
|
-
groupMeta: { orgId: "1" },
|
|
384
|
-
label: "Acme",
|
|
385
|
-
description: "test",
|
|
386
|
-
meta: { id: "1" },
|
|
387
|
-
contextInstructions: null,
|
|
388
|
-
});
|
|
389
|
-
store().setSelectedItemId("doc-1");
|
|
390
|
-
store().updateContentField("title", "test");
|
|
391
|
-
store().addHighlight("field-1");
|
|
392
|
-
store().addContextItem(ephemeralItem("temp"));
|
|
393
|
-
|
|
394
|
-
store().resetAll();
|
|
395
|
-
|
|
396
|
-
expect(store().threadActive).toBe(false);
|
|
397
|
-
expect(store().navContext.group).toBeNull();
|
|
398
|
-
expect(store().navContext.label).toBe("Chat");
|
|
399
|
-
expect(store().selectedItemId).toBeNull();
|
|
400
|
-
expect(store().content).toEqual({});
|
|
401
|
-
expect(store().highlights).toEqual([]);
|
|
402
|
-
expect(store().contextItems).toEqual([]);
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
});
|