@elizaos/app-core 2.0.0-alpha.13 → 2.0.0-alpha.15
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/.turbo/turbo-build.log +0 -1
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +26 -12
- package/dist/api/client.d.ts +3 -3
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +24 -13
- package/dist/bridge/plugin-bridge.d.ts.map +1 -1
- package/dist/components/AvatarLoader.d.ts +3 -1
- package/dist/components/AvatarLoader.d.ts.map +1 -1
- package/dist/components/AvatarLoader.js +4 -1
- package/dist/components/BscTradePanel.d.ts +1 -1
- package/dist/components/BscTradePanel.d.ts.map +1 -1
- package/dist/components/CharacterView.d.ts.map +1 -1
- package/dist/components/CharacterView.js +50 -15
- package/dist/components/ChatView.js +1 -1
- package/dist/components/ConfigPageView.d.ts.map +1 -1
- package/dist/components/ConfigPageView.js +9 -8
- package/dist/components/ConversationsSidebar.js +1 -1
- package/dist/components/CustomActionEditor.js +1 -1
- package/dist/components/FineTuningView.d.ts.map +1 -1
- package/dist/components/FineTuningView.js +2 -2
- package/dist/components/GlobalEmoteOverlay.d.ts.map +1 -1
- package/dist/components/GlobalEmoteOverlay.js +1 -1
- package/dist/components/HeartbeatsView.js +1 -1
- package/dist/components/LoadingScreen.d.ts.map +1 -1
- package/dist/components/LoadingScreen.js +38 -7
- package/dist/components/OnboardingWizard.js +1 -1
- package/dist/components/PluginsView.d.ts.map +1 -1
- package/dist/components/PluginsView.js +4 -1
- package/dist/components/ShellOverlays.js +1 -1
- package/dist/components/VoiceConfigView.d.ts.map +1 -1
- package/dist/components/VoiceConfigView.js +4 -2
- package/dist/components/avatar/VrmEngine.d.ts +2 -0
- package/dist/components/avatar/VrmEngine.d.ts.map +1 -1
- package/dist/components/avatar/VrmEngine.js +14 -6
- package/dist/components/companion/CompanionSceneHost.d.ts +1 -1
- package/dist/components/companion/CompanionSceneHost.d.ts.map +1 -1
- package/dist/components/companion/CompanionSceneHost.js +1 -1
- package/dist/components/index.d.ts +10 -10
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +10 -10
- package/dist/components/inventory/TokensTable.js +2 -2
- package/dist/components/onboarding/IdentityStep.d.ts.map +1 -1
- package/dist/components/onboarding/IdentityStep.js +1 -1
- package/dist/components/shared/ShellHeaderControls.js +1 -1
- package/dist/config/config-field.d.ts.map +1 -1
- package/dist/config/config-field.js +7 -8
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/hooks/useVoiceChat.d.ts.map +1 -1
- package/dist/hooks/useVoiceChat.js +3 -1
- package/dist/i18n/locales/en.json +1192 -1192
- package/dist/i18n/locales/es.json +1192 -1192
- package/dist/i18n/locales/ko.json +1192 -1192
- package/dist/i18n/locales/pt.json +1192 -1192
- package/dist/i18n/locales/zh-CN.json +1192 -1192
- package/dist/package.json +181 -0
- package/dist/platform/lifo.d.ts.map +1 -1
- package/dist/platform/lifo.js +4 -1
- package/dist/state/AppContext.d.ts.map +1 -1
- package/dist/state/AppContext.js +23 -6
- package/dist/state/internal.d.ts +1 -1
- package/dist/state/internal.d.ts.map +1 -1
- package/dist/state/internal.js +1 -1
- package/dist/state/parsers.d.ts.map +1 -1
- package/dist/state/parsers.js +3 -2
- package/dist/state/persistence.js +1 -1
- package/dist/styles/anime.css +6324 -0
- package/dist/styles/base.css +196 -0
- package/dist/styles/onboarding-game.css +738 -0
- package/dist/styles/styles.css +2087 -0
- package/dist/styles/xterm.css +241 -0
- package/package.json +4 -4
- package/src/App.tsx +35 -14
- package/src/ambient.d.ts +5 -5
- package/src/api/client.ts +36 -23
- package/src/bridge/plugin-bridge.ts +1 -1
- package/src/components/AvatarLoader.tsx +6 -0
- package/src/components/BscTradePanel.tsx +1 -1
- package/src/components/CharacterView.tsx +536 -367
- package/src/components/ChatView.tsx +3 -3
- package/src/components/ConfigPageView.tsx +9 -8
- package/src/components/ConversationsSidebar.tsx +1 -1
- package/src/components/CustomActionEditor.tsx +6 -6
- package/src/components/FineTuningView.tsx +6 -3
- package/src/components/GlobalEmoteOverlay.tsx +1 -4
- package/src/components/HeartbeatsView.tsx +1 -1
- package/src/components/InventoryView.tsx +2 -2
- package/src/components/LoadingScreen.tsx +39 -6
- package/src/components/OnboardingWizard.tsx +1 -1
- package/src/components/PluginsView.tsx +6 -0
- package/src/components/ShellOverlays.tsx +1 -1
- package/src/components/VoiceConfigView.tsx +4 -5
- package/src/components/avatar/VrmEngine.ts +25 -7
- package/src/components/companion/CompanionSceneHost.tsx +5 -1
- package/src/components/index.ts +10 -10
- package/src/components/inventory/TokensTable.tsx +2 -2
- package/src/components/onboarding/IdentityStep.tsx +9 -13
- package/src/components/shared/ShellHeaderControls.tsx +1 -1
- package/src/config/config-field.tsx +7 -8
- package/src/config/index.ts +3 -3
- package/src/hooks/useVoiceChat.ts +5 -3
- package/src/platform/lifo.ts +14 -4
- package/src/state/AppContext.tsx +24 -1
- package/src/state/internal.ts +6 -6
- package/src/state/parsers.ts +4 -3
- package/src/state/persistence.ts +1 -1
- package/test/app/MessageContent.test.tsx +42 -0
- package/test/app/bug-report-modal.test.tsx +3 -3
- package/test/app/chat-view.test.tsx +3 -3
- package/test/app/cloud-login-lock.test.ts +3 -2
- package/test/app/custom-actions-smoke.test.ts +3 -3
- package/test/app/onboarding-language.test.tsx +3 -3
- package/test/app/pages-navigation-smoke.e2e.test.ts +13 -8
- package/test/app/plugin-bridge.test.ts +1 -1
- package/test/app/provider-dropdown-default.test.tsx +2 -4
- package/test/app/restart-banner.test.tsx +3 -3
- package/test/app/shell-mode-switching.e2e.test.ts +6 -6
- package/test/app/shell-mode-tab-memory.test.tsx +1 -1
- package/test/app/startup-chat.e2e.test.ts +3 -3
- package/test/app/triggers-view.e2e.test.ts +3 -2
- package/test/app/wallet-api-save-lock.test.ts +2 -1
- package/test/utils/assistant-text.test.ts +64 -0
- package/test/utils/streaming-text.test.ts +89 -0
package/src/platform/lifo.ts
CHANGED
|
@@ -77,7 +77,12 @@ function parseLocationParams(locationLike: LocationLike): {
|
|
|
77
77
|
export function isLifoPopoutValue(value: string | null | undefined): boolean {
|
|
78
78
|
if (typeof value !== "string") return false;
|
|
79
79
|
const normalized = value.trim().toLowerCase();
|
|
80
|
-
return
|
|
80
|
+
return (
|
|
81
|
+
normalized === "" ||
|
|
82
|
+
normalized === "1" ||
|
|
83
|
+
normalized === "true" ||
|
|
84
|
+
normalized === "lifo"
|
|
85
|
+
);
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
export function getPopoutValueFromLocation(
|
|
@@ -87,7 +92,9 @@ export function getPopoutValueFromLocation(
|
|
|
87
92
|
return search.get("popout") ?? hash.get("popout");
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
export function isLifoPopoutModeAtLocation(
|
|
95
|
+
export function isLifoPopoutModeAtLocation(
|
|
96
|
+
locationLike: LocationLike,
|
|
97
|
+
): boolean {
|
|
91
98
|
return isLifoPopoutValue(getPopoutValueFromLocation(locationLike));
|
|
92
99
|
}
|
|
93
100
|
|
|
@@ -121,7 +128,9 @@ export function generateLifoSessionId(): string {
|
|
|
121
128
|
bytes[index] = Math.floor(Math.random() * 256);
|
|
122
129
|
}
|
|
123
130
|
}
|
|
124
|
-
return Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join(
|
|
131
|
+
return Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join(
|
|
132
|
+
"",
|
|
133
|
+
);
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
export function getLifoSyncChannelName(sessionId: string | null): string {
|
|
@@ -205,7 +214,8 @@ export async function createLifoRuntime(
|
|
|
205
214
|
options?.onStdout?.("Command dispatched to sandbox runtime.\n");
|
|
206
215
|
return { exitCode: 0 };
|
|
207
216
|
} catch (error) {
|
|
208
|
-
const message =
|
|
217
|
+
const message =
|
|
218
|
+
error instanceof Error ? error.message : String(error);
|
|
209
219
|
options?.onStderr?.(`${message}\n`);
|
|
210
220
|
throw error;
|
|
211
221
|
}
|
package/src/state/AppContext.tsx
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Children access state and actions through the useApp() hook.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { OnboardingConnection } from "@elizaos/autonomous/contracts/onboarding";
|
|
7
8
|
import {
|
|
8
9
|
type ReactNode,
|
|
9
10
|
useCallback,
|
|
@@ -12,7 +13,6 @@ import {
|
|
|
12
13
|
useRef,
|
|
13
14
|
useState,
|
|
14
15
|
} from "react";
|
|
15
|
-
import type { OnboardingConnection } from "@elizaos/autonomous/contracts/onboarding";
|
|
16
16
|
import { prepareDraftForSave } from "../actions/character";
|
|
17
17
|
import {
|
|
18
18
|
type AgentStartupDiagnostics,
|
|
@@ -926,6 +926,8 @@ export function AppProvider({ children }: { children: ReactNode }) {
|
|
|
926
926
|
|
|
927
927
|
// --- Refs for timers ---
|
|
928
928
|
const actionNoticeTimer = useRef<number | null>(null);
|
|
929
|
+
/** Session-scoped set of notice texts that have been shown with once=true. */
|
|
930
|
+
const shownOnceNotices = useRef<Set<string>>(new Set());
|
|
929
931
|
const elizaCloudPollInterval = useRef<number | null>(null);
|
|
930
932
|
const elizaCloudLoginPollTimer = useRef<number | null>(null);
|
|
931
933
|
const prevAgentStateRef = useRef<string | null>(null);
|
|
@@ -989,7 +991,10 @@ export function AppProvider({ children }: { children: ReactNode }) {
|
|
|
989
991
|
text: string,
|
|
990
992
|
tone: "info" | "success" | "error" = "info",
|
|
991
993
|
ttlMs = 2800,
|
|
994
|
+
once = false,
|
|
992
995
|
) => {
|
|
996
|
+
if (once && shownOnceNotices.current.has(text)) return;
|
|
997
|
+
if (once) shownOnceNotices.current.add(text);
|
|
993
998
|
setActionNoticeState({ tone, text });
|
|
994
999
|
if (actionNoticeTimer.current != null) {
|
|
995
1000
|
window.clearTimeout(actionNoticeTimer.current);
|
|
@@ -3092,6 +3097,24 @@ export function AppProvider({ children }: { children: ReactNode }) {
|
|
|
3092
3097
|
conversationMessagesRef.current.length > 0
|
|
3093
3098
|
)
|
|
3094
3099
|
return;
|
|
3100
|
+
|
|
3101
|
+
// Clean up empty conversations: if the previous conversation has only
|
|
3102
|
+
// system/greeting messages and no user messages, delete it silently.
|
|
3103
|
+
const prevId = activeConversationId;
|
|
3104
|
+
if (prevId && prevId !== id) {
|
|
3105
|
+
const prevMessages = conversationMessagesRef.current;
|
|
3106
|
+
const hasUserMessage = prevMessages.some((m) => m.role === "user");
|
|
3107
|
+
if (!hasUserMessage && prevMessages.length <= 1) {
|
|
3108
|
+
void client.deleteConversation(prevId).catch(() => {});
|
|
3109
|
+
setConversations((prev) => prev.filter((c) => c.id !== prevId));
|
|
3110
|
+
setUnreadConversations((prev) => {
|
|
3111
|
+
const next = new Set(prev);
|
|
3112
|
+
next.delete(prevId);
|
|
3113
|
+
return next;
|
|
3114
|
+
});
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3095
3118
|
const previousActive = activeConversationId;
|
|
3096
3119
|
setActiveConversationId(id);
|
|
3097
3120
|
activeConversationIdRef.current = id;
|
package/src/state/internal.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export {
|
|
2
|
+
deriveOnboardingResumeConnection,
|
|
3
|
+
deriveOnboardingResumeFields,
|
|
4
|
+
hasPartialOnboardingConnectionConfig,
|
|
5
|
+
inferOnboardingResumeStep,
|
|
6
|
+
} from "./onboarding-resume";
|
|
1
7
|
export {
|
|
2
8
|
asApiLikeError,
|
|
3
9
|
computeStreamingDelta,
|
|
@@ -15,12 +21,6 @@ export {
|
|
|
15
21
|
parseStreamEventEnvelopeEvent,
|
|
16
22
|
shouldApplyFinalStreamText,
|
|
17
23
|
} from "./parsers";
|
|
18
|
-
export {
|
|
19
|
-
deriveOnboardingResumeConnection,
|
|
20
|
-
deriveOnboardingResumeFields,
|
|
21
|
-
hasPartialOnboardingConnectionConfig,
|
|
22
|
-
inferOnboardingResumeStep,
|
|
23
|
-
} from "./onboarding-resume";
|
|
24
24
|
export {
|
|
25
25
|
applyUiTheme,
|
|
26
26
|
clearPersistedOnboardingStep,
|
package/src/state/parsers.ts
CHANGED
|
@@ -171,11 +171,12 @@ function normalizeSlashCommandName(name: string): string {
|
|
|
171
171
|
|
|
172
172
|
// A simple utility to split command arguments, equivalent to chat-commands splitCommandArgs
|
|
173
173
|
function splitCommandArgs(text: string): string[] {
|
|
174
|
-
const parts = [];
|
|
174
|
+
const parts: string[] = [];
|
|
175
175
|
const regex = /[^\s"']+|"([^"]*)"|'([^']*)'/g;
|
|
176
|
-
let match;
|
|
177
|
-
while (
|
|
176
|
+
let match: RegExpExecArray | null = regex.exec(text);
|
|
177
|
+
while (match !== null) {
|
|
178
178
|
parts.push(match[1] || match[2] || match[0]);
|
|
179
|
+
match = regex.exec(text);
|
|
179
180
|
}
|
|
180
181
|
return parts;
|
|
181
182
|
}
|
package/src/state/persistence.ts
CHANGED
|
@@ -289,7 +289,7 @@ export function loadActiveConversationId(): string | null {
|
|
|
289
289
|
|
|
290
290
|
export function saveActiveConversationId(value: string | null): void {
|
|
291
291
|
try {
|
|
292
|
-
if (value
|
|
292
|
+
if (value?.trim()) {
|
|
293
293
|
localStorage.setItem(ACTIVE_CONVERSATION_ID_KEY, value);
|
|
294
294
|
return;
|
|
295
295
|
}
|
|
@@ -324,3 +324,45 @@ describe("MessageContent — XML action stripping", () => {
|
|
|
324
324
|
expect(rendered).toBe("**Saturday**: Sunny and warm.");
|
|
325
325
|
});
|
|
326
326
|
});
|
|
327
|
+
|
|
328
|
+
describe("XML tag stripping during streaming", () => {
|
|
329
|
+
it("strips <actions> blocks completely", () => {
|
|
330
|
+
const output = renderText(
|
|
331
|
+
'Hello world<actions><action name="test">do thing</action></actions>',
|
|
332
|
+
);
|
|
333
|
+
expect(output).toContain("Hello world");
|
|
334
|
+
expect(output).not.toContain("do thing");
|
|
335
|
+
expect(output).not.toContain("<actions>");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("strips <think> blocks completely", () => {
|
|
339
|
+
const output = renderText(
|
|
340
|
+
"<think>internal reasoning about the query</think>Hello there!",
|
|
341
|
+
);
|
|
342
|
+
expect(output).toContain("Hello there!");
|
|
343
|
+
expect(output).not.toContain("internal reasoning");
|
|
344
|
+
expect(output).not.toContain("<think>");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it("extracts text from <response><text> wrapper", () => {
|
|
348
|
+
const output = renderText(
|
|
349
|
+
"<response><text>Hello world</text></response>",
|
|
350
|
+
);
|
|
351
|
+
expect(output).toContain("Hello world");
|
|
352
|
+
expect(output).not.toContain("<response>");
|
|
353
|
+
expect(output).not.toContain("<text>");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("handles partial <response><text> during streaming (no closing tag yet)", () => {
|
|
357
|
+
const output = renderText("<response><text>Hello wor");
|
|
358
|
+
expect(output).toContain("Hello wor");
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("does not leak <actions> tag name as visible text", () => {
|
|
362
|
+
const output = renderText(
|
|
363
|
+
'Answer here<actions><action name="save">data</action></actions> done',
|
|
364
|
+
);
|
|
365
|
+
expect(output).not.toContain("actions");
|
|
366
|
+
expect(output).not.toContain("save");
|
|
367
|
+
});
|
|
368
|
+
});
|
|
@@ -14,9 +14,9 @@ const { mockUseBugReport, mockClient } = vi.hoisted(() => ({
|
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
16
|
vi.mock("@elizaos/app-core/hooks", async () => {
|
|
17
|
-
const actual = await vi.importActual<
|
|
18
|
-
"@elizaos/app-core/hooks"
|
|
19
|
-
);
|
|
17
|
+
const actual = await vi.importActual<
|
|
18
|
+
typeof import("@elizaos/app-core/hooks")
|
|
19
|
+
>("@elizaos/app-core/hooks");
|
|
20
20
|
return {
|
|
21
21
|
...actual,
|
|
22
22
|
useBugReport: () => mockUseBugReport(),
|
|
@@ -73,9 +73,9 @@ vi.mock("@elizaos/app-core/platform", () => ({
|
|
|
73
73
|
}));
|
|
74
74
|
|
|
75
75
|
vi.mock("@elizaos/app-core/hooks", async () => {
|
|
76
|
-
const actual = await vi.importActual<
|
|
77
|
-
"@elizaos/app-core/hooks"
|
|
78
|
-
);
|
|
76
|
+
const actual = await vi.importActual<
|
|
77
|
+
typeof import("@elizaos/app-core/hooks")
|
|
78
|
+
>("@elizaos/app-core/hooks");
|
|
79
79
|
return {
|
|
80
80
|
...actual,
|
|
81
81
|
useVoiceChat: (...args: unknown[]) => mockUseVoiceChat(...args),
|
|
@@ -74,6 +74,7 @@ vi.mock("@elizaos/app-core/api", () => ({
|
|
|
74
74
|
SkillScanReportSummary: {},
|
|
75
75
|
}));
|
|
76
76
|
|
|
77
|
+
import type { AppState } from "@elizaos/app-core/state";
|
|
77
78
|
import { AppProvider, useApp } from "@elizaos/app-core/state";
|
|
78
79
|
|
|
79
80
|
function createDeferred<T>() {
|
|
@@ -99,8 +100,8 @@ function Probe(props: { onReady: (api: ProbeApi) => void }) {
|
|
|
99
100
|
|
|
100
101
|
useEffect(() => {
|
|
101
102
|
onReady({
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
setState: (key, value) =>
|
|
104
|
+
app.setState(key as keyof AppState, value as AppState[keyof AppState]),
|
|
104
105
|
handleOnboardingNext: app.handleOnboardingNext,
|
|
105
106
|
handleOnboardingBack: app.handleOnboardingBack,
|
|
106
107
|
handleCloudLogin: app.handleCloudLogin,
|
|
@@ -29,9 +29,9 @@ vi.mock("@elizaos/app-core/platform", () => ({
|
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
31
|
vi.mock("@elizaos/app-core/hooks", async () => {
|
|
32
|
-
const actual = await vi.importActual<
|
|
33
|
-
"@elizaos/app-core/hooks"
|
|
34
|
-
);
|
|
32
|
+
const actual = await vi.importActual<
|
|
33
|
+
typeof import("@elizaos/app-core/hooks")
|
|
34
|
+
>("@elizaos/app-core/hooks");
|
|
35
35
|
return {
|
|
36
36
|
...actual,
|
|
37
37
|
useVoiceChat: () => mockUseVoiceChat(),
|
|
@@ -7,9 +7,9 @@ const { mockUseApp } = vi.hoisted(() => ({
|
|
|
7
7
|
}));
|
|
8
8
|
|
|
9
9
|
vi.mock("@elizaos/app-core/state", async () => {
|
|
10
|
-
const actual = await vi.importActual<
|
|
11
|
-
"@elizaos/app-core/state"
|
|
12
|
-
);
|
|
10
|
+
const actual = await vi.importActual<
|
|
11
|
+
typeof import("@elizaos/app-core/state")
|
|
12
|
+
>("@elizaos/app-core/state");
|
|
13
13
|
return {
|
|
14
14
|
...actual,
|
|
15
15
|
useApp: () => mockUseApp(),
|
|
@@ -35,9 +35,9 @@ const { companionOverlayTabs, mockUseApp, noop } = vi.hoisted(() => ({
|
|
|
35
35
|
}));
|
|
36
36
|
|
|
37
37
|
vi.mock("@elizaos/app-core/state", async () => {
|
|
38
|
-
const actual = await vi.importActual<
|
|
39
|
-
"@elizaos/app-core/state"
|
|
40
|
-
);
|
|
38
|
+
const actual = await vi.importActual<
|
|
39
|
+
typeof import("@elizaos/app-core/state")
|
|
40
|
+
>("@elizaos/app-core/state");
|
|
41
41
|
return {
|
|
42
42
|
...actual,
|
|
43
43
|
useApp: () => mockUseApp(),
|
|
@@ -269,9 +269,9 @@ vi.mock("../../../packages/app-core/src/components/LifoSandboxView", () => ({
|
|
|
269
269
|
React.createElement("section", null, "LifoSandboxView Ready"),
|
|
270
270
|
}));
|
|
271
271
|
vi.mock("@elizaos/app-core/hooks", async () => {
|
|
272
|
-
const actual = await vi.importActual<
|
|
273
|
-
"@elizaos/app-core/hooks"
|
|
274
|
-
);
|
|
272
|
+
const actual = await vi.importActual<
|
|
273
|
+
typeof import("@elizaos/app-core/hooks")
|
|
274
|
+
>("@elizaos/app-core/hooks");
|
|
275
275
|
return {
|
|
276
276
|
...actual,
|
|
277
277
|
useContextMenu: () => ({
|
|
@@ -396,7 +396,9 @@ describe("pages navigation smoke (e2e)", () => {
|
|
|
396
396
|
conversations: [],
|
|
397
397
|
elizaCloudCredits: null,
|
|
398
398
|
uiShellMode: "companion",
|
|
399
|
-
setUiShellMode: vi.fn()
|
|
399
|
+
setUiShellMode: vi.fn((mode: "native" | "companion") => {
|
|
400
|
+
state.uiShellMode = mode;
|
|
401
|
+
}),
|
|
400
402
|
uiLanguage: "en",
|
|
401
403
|
agentStatus: { state: "running", agentName: "Milady" },
|
|
402
404
|
loadDropStatus: vi.fn(),
|
|
@@ -587,7 +589,9 @@ describe("pages navigation smoke (e2e)", () => {
|
|
|
587
589
|
conversations: [],
|
|
588
590
|
elizaCloudCredits: null,
|
|
589
591
|
uiShellMode: "companion",
|
|
590
|
-
setUiShellMode: vi.fn()
|
|
592
|
+
setUiShellMode: vi.fn((mode: "native" | "companion") => {
|
|
593
|
+
state.uiShellMode = mode;
|
|
594
|
+
}),
|
|
591
595
|
uiLanguage: "en",
|
|
592
596
|
agentStatus: { state: "running", agentName: "Milady" },
|
|
593
597
|
loadDropStatus: vi.fn(),
|
|
@@ -600,6 +604,7 @@ describe("pages navigation smoke (e2e)", () => {
|
|
|
600
604
|
setActionNotice: vi.fn(),
|
|
601
605
|
setTab: (tab: Tab) => {
|
|
602
606
|
state.tab = tab;
|
|
607
|
+
state.uiShellMode = shellModeForTab(tab);
|
|
603
608
|
},
|
|
604
609
|
};
|
|
605
610
|
Object.assign(state, entry.patch);
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Tests for plugin-bridge — capabilities detection and feature flags on web platform.
|
|
4
4
|
*/
|
|
5
|
-
import { describe, expect, it, vi } from "vitest";
|
|
6
5
|
|
|
7
6
|
import {
|
|
8
7
|
getPluginCapabilities,
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
isWeb,
|
|
16
15
|
platform,
|
|
17
16
|
} from "@elizaos/app-core/bridge";
|
|
17
|
+
import { describe, expect, it, vi } from "vitest";
|
|
18
18
|
|
|
19
19
|
describe("plugin-bridge", () => {
|
|
20
20
|
// -- Platform --
|
|
@@ -85,8 +85,7 @@ function defaultProps() {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function getSelectValue(tree: ReactTestRenderer): string | undefined {
|
|
88
|
-
|
|
89
|
-
const root = (tree as any).root;
|
|
88
|
+
const root = tree.root;
|
|
90
89
|
const selects = root.findAll(
|
|
91
90
|
(node: ReactTestInstance) =>
|
|
92
91
|
node.type === "select" && node.props.value !== undefined,
|
|
@@ -95,8 +94,7 @@ function getSelectValue(tree: ReactTestRenderer): string | undefined {
|
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
function getSelect(tree: ReactTestRenderer): ReactTestInstance {
|
|
98
|
-
|
|
99
|
-
const root = (tree as any).root;
|
|
97
|
+
const root = tree.root;
|
|
100
98
|
return root.find(
|
|
101
99
|
(node: ReactTestInstance) =>
|
|
102
100
|
node.type === "select" && node.props.value !== undefined,
|
|
@@ -14,9 +14,9 @@ interface RestartBannerContextStub {
|
|
|
14
14
|
const mockUseApp = vi.fn<() => RestartBannerContextStub>();
|
|
15
15
|
|
|
16
16
|
vi.mock("@elizaos/app-core/state", async () => {
|
|
17
|
-
const actual = await vi.importActual<
|
|
18
|
-
"@elizaos/app-core/state"
|
|
19
|
-
);
|
|
17
|
+
const actual = await vi.importActual<
|
|
18
|
+
typeof import("@elizaos/app-core/state")
|
|
19
|
+
>("@elizaos/app-core/state");
|
|
20
20
|
return {
|
|
21
21
|
...actual,
|
|
22
22
|
useApp: () => mockUseApp(),
|
|
@@ -49,9 +49,9 @@ vi.mock("@elizaos/app-core/platform", async () => {
|
|
|
49
49
|
/* ── Mock every leaf component ────────────────────────────────────── */
|
|
50
50
|
|
|
51
51
|
vi.mock("@elizaos/app-core/state", async () => {
|
|
52
|
-
const actual = await vi.importActual<
|
|
53
|
-
"@elizaos/app-core/state"
|
|
54
|
-
);
|
|
52
|
+
const actual = await vi.importActual<
|
|
53
|
+
typeof import("@elizaos/app-core/state")
|
|
54
|
+
>("@elizaos/app-core/state");
|
|
55
55
|
return {
|
|
56
56
|
...actual,
|
|
57
57
|
useApp: () => mockUseApp(),
|
|
@@ -308,9 +308,9 @@ vi.mock("../../../packages/app-core/src/components/LifoSandboxView", () => ({
|
|
|
308
308
|
}));
|
|
309
309
|
|
|
310
310
|
vi.mock("@elizaos/app-core/hooks", async () => {
|
|
311
|
-
const actual = await vi.importActual<
|
|
312
|
-
"@elizaos/app-core/hooks"
|
|
313
|
-
);
|
|
311
|
+
const actual = await vi.importActual<
|
|
312
|
+
typeof import("@elizaos/app-core/hooks")
|
|
313
|
+
>("@elizaos/app-core/hooks");
|
|
314
314
|
return {
|
|
315
315
|
...actual,
|
|
316
316
|
useContextMenu: () => ({
|
|
@@ -11,9 +11,9 @@ const { mockUseApp } = vi.hoisted(() => ({
|
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
13
|
vi.mock("@elizaos/app-core/state", async () => {
|
|
14
|
-
const actual = await vi.importActual<
|
|
15
|
-
"@elizaos/app-core/state"
|
|
16
|
-
);
|
|
14
|
+
const actual = await vi.importActual<
|
|
15
|
+
typeof import("@elizaos/app-core/state")
|
|
16
|
+
>("@elizaos/app-core/state");
|
|
17
17
|
return {
|
|
18
18
|
...actual,
|
|
19
19
|
useApp: () => mockUseApp(),
|
|
@@ -475,8 +475,9 @@ describe("TriggersView UI E2E", () => {
|
|
|
475
475
|
runtimeHarness = createTriggerRuntimeHarness();
|
|
476
476
|
server = await startApiServerFn({
|
|
477
477
|
port: 0,
|
|
478
|
-
|
|
479
|
-
|
|
478
|
+
runtime: runtimeHarness.runtime as Parameters<
|
|
479
|
+
typeof startApiServerFn
|
|
480
|
+
>[0]["runtime"],
|
|
480
481
|
});
|
|
481
482
|
});
|
|
482
483
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import type { WalletConfigUpdateRequest } from "@elizaos/autonomous/contracts/wallet";
|
|
2
4
|
import React, { useEffect } from "react";
|
|
3
5
|
import TestRenderer, { act } from "react-test-renderer";
|
|
4
6
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import type { WalletConfigUpdateRequest } from "@elizaos/autonomous/contracts/wallet";
|
|
6
7
|
|
|
7
8
|
const { mockClient } = vi.hoisted(() => ({
|
|
8
9
|
mockClient: {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { stripAssistantStageDirections } from "../../src/utils/assistant-text";
|
|
3
|
+
|
|
4
|
+
describe("stripAssistantStageDirections", () => {
|
|
5
|
+
it("strips asterisk-wrapped stage directions", () => {
|
|
6
|
+
const result = stripAssistantStageDirections("Hello *smiles warmly* there");
|
|
7
|
+
expect(result).not.toContain("smiles warmly");
|
|
8
|
+
expect(result).toContain("Hello");
|
|
9
|
+
expect(result).toContain("there");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("strips underscore-wrapped stage directions", () => {
|
|
13
|
+
const result = stripAssistantStageDirections("Hello _waves happily_ there");
|
|
14
|
+
expect(result).not.toContain("waves happily");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("preserves asterisk content that is NOT a stage direction", () => {
|
|
18
|
+
const result = stripAssistantStageDirections("Use *bold text* for emphasis");
|
|
19
|
+
expect(result).toContain("bold text");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("preserves underscore content that is NOT a stage direction", () => {
|
|
23
|
+
const result = stripAssistantStageDirections("Use _italic text_ for emphasis");
|
|
24
|
+
expect(result).toContain("italic text");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("handles empty input", () => {
|
|
28
|
+
expect(stripAssistantStageDirections("")).toBe("");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("handles text with no stage directions", () => {
|
|
32
|
+
expect(stripAssistantStageDirections("Just plain text")).toBe(
|
|
33
|
+
"Just plain text",
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("handles multiple stage directions in one message", () => {
|
|
38
|
+
const result = stripAssistantStageDirections(
|
|
39
|
+
"*nods* I agree. *smiles* That sounds right.",
|
|
40
|
+
);
|
|
41
|
+
expect(result).not.toContain("nods");
|
|
42
|
+
expect(result).not.toContain("smiles");
|
|
43
|
+
expect(result).toContain("I agree.");
|
|
44
|
+
expect(result).toContain("That sounds right.");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("handles stage direction at start of text", () => {
|
|
48
|
+
const result = stripAssistantStageDirections("*laughs* That's funny!");
|
|
49
|
+
expect(result).not.toContain("laughs");
|
|
50
|
+
expect(result).toContain("That's funny!");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("handles stage direction at end of text", () => {
|
|
54
|
+
const result = stripAssistantStageDirections("Goodbye! *waves*");
|
|
55
|
+
expect(result).not.toContain("waves");
|
|
56
|
+
expect(result).toContain("Goodbye!");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("does not strip across newlines (asterisk pattern is non-greedy single line)", () => {
|
|
60
|
+
const input = "*smiles\nacross lines*";
|
|
61
|
+
const result = stripAssistantStageDirections(input);
|
|
62
|
+
expect(result).toContain("smiles");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
mergeStreamingText,
|
|
4
|
+
computeStreamingDelta,
|
|
5
|
+
} from "../../src/utils/streaming-text";
|
|
6
|
+
|
|
7
|
+
describe("mergeStreamingText", () => {
|
|
8
|
+
it("returns incoming when it starts with existing (cumulative snapshot)", () => {
|
|
9
|
+
expect(mergeStreamingText("Hello", "Hello world")).toBe("Hello world");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("returns incoming when existing is empty", () => {
|
|
13
|
+
expect(mergeStreamingText("", "Hello")).toBe("Hello");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("returns existing when incoming is empty", () => {
|
|
17
|
+
expect(mergeStreamingText("Hello", "")).toBe("Hello");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("returns existing when incoming equals existing", () => {
|
|
21
|
+
expect(mergeStreamingText("Hello", "Hello")).toBe("Hello");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("keeps existing when incoming is a prefix (regressive snapshot)", () => {
|
|
25
|
+
expect(mergeStreamingText("Hello world", "Hello")).toBe("Hello world");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("appends new portion when incoming overlaps existing suffix", () => {
|
|
29
|
+
expect(mergeStreamingText("Hello wor", "world")).toBe("Hello world");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("appends single character even when it matches last char (repeated chars)", () => {
|
|
33
|
+
expect(mergeStreamingText("Hel", "l")).toBe("Hell");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("builds up character by character correctly", () => {
|
|
37
|
+
let text = "";
|
|
38
|
+
for (const char of "Hello") {
|
|
39
|
+
text = mergeStreamingText(text, char);
|
|
40
|
+
}
|
|
41
|
+
expect(text).toBe("Hello");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("drops suffix-only resend larger than 1 character", () => {
|
|
45
|
+
expect(mergeStreamingText("Hello world", "world")).toBe("Hello world");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("replaces when incoming shares prefix >=8 chars but differs", () => {
|
|
49
|
+
expect(
|
|
50
|
+
mergeStreamingText(
|
|
51
|
+
"The quick brown fox jumps",
|
|
52
|
+
"The quick brown fox leaps",
|
|
53
|
+
),
|
|
54
|
+
).toBe("The quick brown fox leaps");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("does not false-positive on short messages with natural overlap", () => {
|
|
58
|
+
expect(mergeStreamingText("Hi", "Hi there")).toBe("Hi there");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("returns incoming when it contains existing as substring", () => {
|
|
62
|
+
expect(mergeStreamingText("Hello", "prefix Hello suffix")).toBe(
|
|
63
|
+
"prefix Hello suffix",
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("concatenates when no overlap detected and not a snapshot", () => {
|
|
68
|
+
expect(mergeStreamingText("abc", "xyz")).toBe("abcxyz");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("computeStreamingDelta", () => {
|
|
73
|
+
it("returns empty string when merge result equals existing", () => {
|
|
74
|
+
expect(computeStreamingDelta("Hello world", "Hello")).toBe("");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns only the new portion for cumulative snapshots", () => {
|
|
78
|
+
expect(computeStreamingDelta("Hello", "Hello world")).toBe(" world");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("returns incoming for snapshot replacements", () => {
|
|
82
|
+
expect(
|
|
83
|
+
computeStreamingDelta(
|
|
84
|
+
"The quick brown fox jumps",
|
|
85
|
+
"The quick brown fox leaps",
|
|
86
|
+
),
|
|
87
|
+
).toBe("The quick brown fox leaps");
|
|
88
|
+
});
|
|
89
|
+
});
|