@nextclaw/ui 0.12.24 → 0.12.26
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/CHANGELOG.md +136 -29
- package/dist/assets/api-DGD9_Bg4.js +15 -0
- package/dist/assets/app-manager-provider-oYdeYPSv.js +1 -0
- package/dist/assets/{book-open-DDlN5MvX.js → book-open-BcnAiKde.js} +1 -1
- package/dist/assets/channels-list-page-HgLgrEg4.js +8 -0
- package/dist/assets/chat-page-DAKMFDrS.js +1 -0
- package/dist/assets/config-split-page-CcrEUtwu.js +1 -0
- package/dist/assets/cpu-DPPwMzoC.js +3 -0
- package/dist/assets/{createLucideIcon-BLMK3QUd.js → createLucideIcon-DzY6wN61.js} +1 -1
- package/dist/assets/desktop-DVUbOWbR.js +3 -0
- package/dist/assets/desktop-update-config-CP8dFYXK.js +1 -0
- package/dist/assets/{dialog-C3D7Be0p.js → dialog-BKo0RItd.js} +1 -1
- package/dist/assets/{dist-CPlbUgwU.js → dist-CFiwgaLs.js} +1 -1
- package/dist/assets/doc-browser-CAhfnm0D.js +1 -0
- package/dist/assets/{doc-browser-context-BJuMaI3o.js → doc-browser-context-FukQHvyo.js} +1 -1
- package/dist/assets/doc-browser-p9DDNPWB.js +1 -0
- package/dist/assets/doc-browser-rZIQIjuw.js +1 -0
- package/dist/assets/download-CMM8po31.js +1 -0
- package/dist/assets/{es2015-xqN1slyW.js → es2015-BhznEEyJ.js} +1 -1
- package/dist/assets/{external-link-DwfSfTLB.js → external-link-CpEvG65F.js} +1 -1
- package/dist/assets/i18n-D1144VAA.js +1 -0
- package/dist/assets/index-Cuwst6cc.js +100 -0
- package/dist/assets/index-dlcqieQ0.css +1 -0
- package/dist/assets/{key-round-CJ5gDAAG.js → key-round-DUq47t0P.js} +1 -1
- package/dist/assets/marketplace-page-BeFbwxR-.js +105 -0
- package/dist/assets/marketplace-page-CR4xq-TM.js +1 -0
- package/dist/assets/mcp-marketplace-page-DlRrSCj3.js +1 -0
- package/dist/assets/mcp-marketplace-page-DwnaLNTx.js +40 -0
- package/dist/assets/model-config-L2l6YAlQ.js +1 -0
- package/dist/assets/{notice-card-BFDbKQDA.js → notice-card-Dr6xCwva.js} +1 -1
- package/dist/assets/play-AqrNslHI.js +1 -0
- package/dist/assets/plus-B-YHtTNC.js +1 -0
- package/dist/assets/{popover-B86Dbfhf.js → popover-BDFNiLlg.js} +1 -1
- package/dist/assets/provider-scoped-model-input-BMTp4BEH.js +1 -0
- package/dist/assets/providers-list-DYAEunOp.js +1 -0
- package/dist/assets/refresh-cw-CrbD8EkT.js +1 -0
- package/dist/assets/remote-Dr3jcfWP.js +1 -0
- package/dist/assets/{rotate-cw-BZ2JObNs.js → rotate-cw-BN9yjccP.js} +1 -1
- package/dist/assets/runtime-config-page-BdeU8PEK.js +1 -0
- package/dist/assets/{save-euRxl8pI.js → save-CO_4qf6b.js} +1 -1
- package/dist/assets/{search-CLd7m0M7.js → search-CRtQwr-h.js} +1 -1
- package/dist/assets/search-config-CQUhd5RU.js +1 -0
- package/dist/assets/secrets-config-D-NWlW9q.js +3 -0
- package/dist/assets/{select-CJ0wbo3D.js → select-BUTwE_lC.js} +1 -1
- package/dist/assets/{setting-row-D1Yygqp7.js → setting-row-BavcnXw1.js} +1 -1
- package/dist/assets/settings-MWL2SMyk.js +1 -0
- package/dist/assets/{sparkles-DVfeSVJQ.js → sparkles-BmgOD4nY.js} +1 -1
- package/dist/assets/{status-dot-ChvPCib9.js → status-dot-l3kPFdq_.js} +1 -1
- package/dist/assets/{tabs-custom-Hia_ong0.js → tabs-custom-D48zdZoc.js} +1 -1
- package/dist/assets/{tag-chip-FrkmkT8r.js → tag-chip-Dm2Lqnpu.js} +1 -1
- package/dist/assets/use-config-Cyv5IuSt.js +1 -0
- package/dist/assets/use-infinite-scroll-loader-CFVdPpNv.js +1 -0
- package/dist/assets/x-BeyYA_h6.js +1 -0
- package/dist/index.html +29 -40
- package/package.json +9 -9
- package/src/app/components/layout/sidebar.layout.test.tsx +2 -4
- package/src/app/components/theme-provider.tsx +1 -0
- package/src/app/configs/app-navigation.config.ts +0 -6
- package/src/app/index.tsx +4 -7
- package/src/features/agents/components/agents-page.test.tsx +25 -15
- package/src/features/agents/components/agents-page.tsx +133 -172
- package/src/features/channels/components/config/channel-form.test.tsx +1 -0
- package/src/features/channels/components/config/channel-form.tsx +4 -3
- package/src/features/channels/components/config/weixin-channel-auth-section.test.tsx +38 -1
- package/src/features/channels/components/config/weixin-channel-auth-section.tsx +137 -40
- package/src/features/channels/index.ts +1 -1
- package/src/features/channels/utils/channel-form-fields.utils.test.ts +26 -0
- package/src/features/channels/utils/channel-form-fields.utils.ts +32 -18
- package/src/features/chat/components/chat-session-workspace-panel-nav.tsx +23 -4
- package/src/features/chat/components/chat-session-workspace-panel.tsx +53 -35
- package/src/features/chat/components/chat-sidebar-session-item.tsx +16 -12
- package/src/features/chat/components/conversation/chat-conversation-header.test.tsx +74 -0
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +8 -2
- package/src/features/chat/components/conversation/chat-conversation-panel.test.tsx +262 -114
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +210 -174
- package/src/features/chat/components/conversation/chat-input-bar.container.tsx +11 -1
- package/src/features/chat/components/conversation/session-header/chat-session-header-actions.test.tsx +24 -0
- package/src/features/chat/components/conversation/session-header/chat-session-header-actions.tsx +27 -6
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +174 -0
- package/src/features/chat/components/layout/chat-sidebar.test.tsx +45 -8
- package/src/features/chat/components/layout/chat-sidebar.tsx +29 -46
- package/src/features/chat/components/providers/chat-presenter.provider.tsx +4 -0
- package/src/features/chat/components/workspace/session-cron-job-content.tsx +103 -0
- package/src/features/chat/hooks/use-ncp-agent-runtime.test.tsx +153 -80
- package/src/features/chat/hooks/use-ncp-chat-page-data.test.tsx +70 -0
- package/src/features/chat/hooks/use-ncp-chat-page-data.ts +1 -1
- package/src/features/chat/hooks/use-ncp-child-session-tabs-view.ts +2 -8
- package/src/features/chat/hooks/use-ncp-session-list-view.ts +1 -2
- package/src/features/chat/managers/chat-session-list.manager.test.ts +7 -9
- package/src/features/chat/managers/chat-session-list.manager.ts +5 -10
- package/src/features/chat/managers/ncp-chat-input.manager.test.ts +20 -2
- package/src/features/chat/managers/ncp-chat-input.manager.ts +18 -0
- package/src/features/chat/managers/ncp-chat-presenter.manager.ts +7 -0
- package/src/features/chat/managers/ncp-chat-thread.manager.test.ts +52 -1
- package/src/features/chat/managers/ncp-chat-thread.manager.ts +21 -0
- package/src/features/chat/pages/ncp-chat-page.tsx +9 -5
- package/src/features/chat/stores/chat-input.store.ts +3 -1
- package/src/features/chat/stores/chat-session-list.store.ts +0 -2
- package/src/features/chat/stores/chat-thread.store.ts +4 -0
- package/src/features/chat/utils/chat-session-display.utils.test.ts +83 -1
- package/src/features/chat/utils/chat-session-display.utils.ts +73 -0
- package/src/features/chat/utils/ncp-chat-input-availability.utils.test.ts +1 -0
- package/src/features/chat/utils/ncp-session-adapter.utils.test.ts +22 -0
- package/src/features/chat/utils/ncp-session-adapter.utils.ts +32 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-curated-scene-route.test.tsx +235 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.config.ts +162 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.tsx +355 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-shelf-card.tsx +118 -0
- package/src/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer.ts +201 -0
- package/src/features/marketplace/components/detail-doc/marketplace-detail-doc.test.ts +40 -0
- package/src/features/marketplace/components/marketplace-catalog-grid.tsx +114 -0
- package/src/features/marketplace/components/marketplace-detail-doc.ts +73 -24
- package/src/features/marketplace/components/marketplace-item-icon.tsx +45 -0
- package/src/features/marketplace/components/marketplace-list-card.tsx +177 -93
- package/src/features/marketplace/components/marketplace-page-detail.test.tsx +9 -2
- package/src/features/marketplace/components/marketplace-page-parts.tsx +1 -1
- package/src/features/marketplace/components/marketplace-page.test.tsx +25 -6
- package/src/features/marketplace/components/marketplace-page.tsx +154 -132
- package/src/features/marketplace/hooks/use-marketplace-curated-scene-route.ts +97 -0
- package/src/features/marketplace/hooks/use-marketplace.ts +59 -3
- package/src/features/system-status/components/config/runtime-agent-list-card.tsx +4 -8
- package/src/features/system-status/components/config/runtime-binding-list-card.tsx +5 -7
- package/src/features/system-status/components/config/runtime-config-editor.tsx +1 -19
- package/src/features/system-status/components/config/runtime-entry-list-card.tsx +10 -11
- package/src/features/system-status/components/config/runtime-settings-card.tsx +15 -23
- package/src/features/system-status/components/runtime-control-card.test.tsx +8 -6
- package/src/features/system-status/components/runtime-control-card.tsx +7 -6
- package/src/features/system-status/pages/runtime-config-page.test.tsx +19 -9
- package/src/features/system-status/pages/runtime-config-page.tsx +2 -3
- package/src/features/system-status/utils/runtime-config-agent.utils.ts +4 -4
- package/src/features/system-status/utils/system-status.utils.ts +31 -6
- package/src/index.css +8 -0
- package/src/platforms/desktop/components/desktop-app-shell.test.tsx +68 -0
- package/src/platforms/desktop/components/desktop-app-shell.tsx +46 -18
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +30 -0
- package/src/platforms/desktop/index.ts +6 -0
- package/src/platforms/desktop/types/desktop-update.types.ts +3 -0
- package/src/platforms/desktop/utils/desktop-host.utils.ts +56 -0
- package/src/shared/components/common/brand-header.tsx +36 -16
- package/src/shared/components/config/provider-form-support.ts +2 -22
- package/src/shared/components/cron-config.tsx +12 -58
- package/src/shared/components/doc-browser/doc-browser.tsx +4 -4
- package/src/shared/components/ui/select.tsx +19 -7
- package/src/shared/lib/api/channel-auth.types.ts +1 -0
- package/src/shared/lib/api/ncp-session.types.ts +9 -0
- package/src/shared/lib/api/types.ts +12 -1
- package/src/shared/lib/api/utils/marketplace.utils.ts +7 -1
- package/src/shared/lib/cron/cron-job-view.utils.ts +59 -0
- package/src/shared/lib/cron/index.ts +1 -0
- package/src/shared/lib/i18n/{channel-auth.ts → channel-auth.constants.ts} +31 -0
- package/src/shared/lib/i18n/chat-labels.utils.ts +3 -2
- package/src/shared/lib/i18n/index.ts +20 -59
- package/src/shared/lib/i18n/{runtime-control.ts → runtime-control-labels.utils.ts} +30 -1
- package/src/shared/lib/provider-models/index.test.ts +39 -0
- package/src/shared/lib/provider-models/index.ts +1 -3
- package/src/shared/lib/ui-document-title/index.ts +0 -1
- package/tsconfig.json +1 -0
- package/vite.config.ts +1 -1
- package/vitest.config.ts +1 -1
- package/dist/assets/api-D2xRKmZd.js +0 -15
- package/dist/assets/app-manager-provider-CNaZboG4.js +0 -1
- package/dist/assets/app-navigation.config-Ihhrrt--.js +0 -1
- package/dist/assets/channels-list-page-p26lgxLk.js +0 -8
- package/dist/assets/chat-Dkh2qtuz.js +0 -61
- package/dist/assets/chat-page-DoTmE2wx.js +0 -1
- package/dist/assets/chunk-JZWAC4HX-Kydj4yEz.js +0 -3
- package/dist/assets/config-split-page-DIOCjj2Q.js +0 -1
- package/dist/assets/desktop-update-config-DlpzDfKM.js +0 -1
- package/dist/assets/doc-browser-C8FM5fC0.js +0 -1
- package/dist/assets/doc-browser-RJUOL_GO.js +0 -1
- package/dist/assets/doc-browser-p82AdNO-.js +0 -1
- package/dist/assets/folder-CeJKPx5P.js +0 -1
- package/dist/assets/hash-BqxRTZW5.js +0 -1
- package/dist/assets/i18n-DnTGDIRw.js +0 -1
- package/dist/assets/index-D8MKmXtO.css +0 -1
- package/dist/assets/index-pBvbJ5Mt.js +0 -2
- package/dist/assets/loader-circle-fd-vQKtW.js +0 -1
- package/dist/assets/logo-badge-KAe-7d8c.js +0 -1
- package/dist/assets/logos-C4sYP1Vl.js +0 -1
- package/dist/assets/marketplace-page-Cql0kDi-.js +0 -1
- package/dist/assets/marketplace-page-m4P5g_Ht.js +0 -49
- package/dist/assets/mcp-marketplace-page-9WVKl1m1.js +0 -1
- package/dist/assets/mcp-marketplace-page-ByzBQZcx.js +0 -40
- package/dist/assets/message-square-z_osm9c0.js +0 -1
- package/dist/assets/model-config-Dbr_0APb.js +0 -1
- package/dist/assets/play-Dv6Nr1Ew.js +0 -1
- package/dist/assets/plus-D8eKFY7h.js +0 -1
- package/dist/assets/provider-scoped-model-input-DFm6N2f7.js +0 -1
- package/dist/assets/providers-list-BJcLOjun.js +0 -1
- package/dist/assets/refresh-ccw-ByVwmnN_.js +0 -1
- package/dist/assets/refresh-cw-PcqoYB3K.js +0 -1
- package/dist/assets/remote-BOxo9iwd.js +0 -1
- package/dist/assets/runtime-config-page-CjLhnbSl.js +0 -1
- package/dist/assets/search-config-J4Htco-P.js +0 -1
- package/dist/assets/secrets-config-CUdERjco.js +0 -3
- package/dist/assets/sessions-config-page-DpK991fs.js +0 -2
- package/dist/assets/settings-drbWqzA4.js +0 -1
- package/dist/assets/skeleton-BK1SOSRA.js +0 -1
- package/dist/assets/theme-provider-0hxjiPc_.js +0 -2
- package/dist/assets/tooltip-Cj4yA0gH.js +0 -1
- package/dist/assets/trash-2-CBsHCfqq.js +0 -1
- package/dist/assets/use-config-38Ur-89i.js +0 -1
- package/dist/assets/use-confirm-dialog-DPQThaeU.js +0 -1
- package/dist/assets/use-infinite-scroll-loader-5Gf1xQi7.js +0 -1
- package/dist/assets/use-viewport-layout-D1XzKeip.js +0 -1
- package/dist/assets/x-CM-XDMpk.js +0 -1
- package/src/features/chat/components/config/sessions-config-detail-pane.tsx +0 -244
- package/src/features/chat/pages/sessions-config-page.test.tsx +0 -152
- package/src/features/chat/pages/sessions-config-page.tsx +0 -192
- /package/dist/assets/{config-hints-MogHYQ8G.js → config-hints-BNfpOL4J.js} +0 -0
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
type NcpEndpointEvent,
|
|
6
6
|
type NcpEndpointManifest,
|
|
7
7
|
type NcpEndpointSubscriber,
|
|
8
|
+
type NcpMessage,
|
|
9
|
+
type NcpStreamRequestPayload,
|
|
8
10
|
NcpEventType,
|
|
9
11
|
} from "@nextclaw/ncp";
|
|
10
12
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
@@ -31,9 +33,57 @@ class DeferredSendClient implements NcpAgentClientEndpoint {
|
|
|
31
33
|
readonly stream = vi.fn(async () => {});
|
|
32
34
|
readonly abort = vi.fn(async () => {});
|
|
33
35
|
private listeners = new Set<NcpEndpointSubscriber>();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.
|
|
36
|
+
|
|
37
|
+
emit = async (event: NcpEndpointEvent): Promise<void> => {
|
|
38
|
+
this.publish(event);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
subscribe = (listener: NcpEndpointSubscriber): (() => void) => {
|
|
42
|
+
this.listeners.add(listener);
|
|
43
|
+
return () => {
|
|
44
|
+
this.listeners.delete(listener);
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
send = vi.fn(async (envelope: NcpAgentSendEnvelope) => ({
|
|
49
|
+
sessionId: "session-created",
|
|
50
|
+
userMessageId: envelope.message.id,
|
|
51
|
+
assistantMessageId: "assistant-1",
|
|
52
|
+
runId: "run-1",
|
|
53
|
+
...(envelope.correlationId ? { correlationId: envelope.correlationId } : {}),
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
private publish = (event: NcpEndpointEvent): void => {
|
|
57
|
+
for (const listener of this.listeners) {
|
|
58
|
+
listener(event);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class ExistingSessionLiveClient implements NcpAgentClientEndpoint {
|
|
64
|
+
readonly manifest: NcpEndpointManifest = {
|
|
65
|
+
endpointKind: "agent",
|
|
66
|
+
endpointId: "existing-session-live-client",
|
|
67
|
+
version: "0.1.0",
|
|
68
|
+
supportsStreaming: true,
|
|
69
|
+
supportsAbort: true,
|
|
70
|
+
supportsProactiveMessages: false,
|
|
71
|
+
supportsLiveSessionStream: true,
|
|
72
|
+
supportedPartTypes: ["text"],
|
|
73
|
+
expectedLatency: "seconds",
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
readonly start = vi.fn(async () => {});
|
|
77
|
+
readonly abort = vi.fn(async () => {});
|
|
78
|
+
private listeners = new Set<NcpEndpointSubscriber>();
|
|
79
|
+
private liveStreamActive = false;
|
|
80
|
+
|
|
81
|
+
stop = vi.fn(async () => {
|
|
82
|
+
this.liveStreamActive = false;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
stream = vi.fn(async (_payload: NcpStreamRequestPayload) => {
|
|
86
|
+
this.liveStreamActive = true;
|
|
37
87
|
});
|
|
38
88
|
|
|
39
89
|
emit = async (event: NcpEndpointEvent): Promise<void> => {
|
|
@@ -47,78 +97,59 @@ class DeferredSendClient implements NcpAgentClientEndpoint {
|
|
|
47
97
|
};
|
|
48
98
|
};
|
|
49
99
|
|
|
50
|
-
send = vi.fn(async (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
role: "user",
|
|
59
|
-
status: "final",
|
|
60
|
-
parts: [{ type: "text", text: "hello" }],
|
|
61
|
-
timestamp: now,
|
|
100
|
+
send = vi.fn(async (envelope: NcpAgentSendEnvelope) => {
|
|
101
|
+
const events: NcpEndpointEvent[] = [
|
|
102
|
+
{
|
|
103
|
+
type: NcpEventType.RunStarted,
|
|
104
|
+
payload: {
|
|
105
|
+
sessionId: "session-existing",
|
|
106
|
+
messageId: "assistant-1",
|
|
107
|
+
runId: "run-1",
|
|
62
108
|
},
|
|
63
109
|
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
runId: "run-1",
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
await this.completionGate;
|
|
74
|
-
this.publish({
|
|
75
|
-
type: NcpEventType.MessageTextStart,
|
|
76
|
-
payload: {
|
|
77
|
-
sessionId: "session-created",
|
|
78
|
-
messageId: "assistant-1",
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
this.publish({
|
|
82
|
-
type: NcpEventType.MessageTextDelta,
|
|
83
|
-
payload: {
|
|
84
|
-
sessionId: "session-created",
|
|
85
|
-
messageId: "assistant-1",
|
|
86
|
-
delta: "done",
|
|
110
|
+
{
|
|
111
|
+
type: NcpEventType.MessageTextStart,
|
|
112
|
+
payload: {
|
|
113
|
+
sessionId: "session-existing",
|
|
114
|
+
messageId: "assistant-1",
|
|
115
|
+
},
|
|
87
116
|
},
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
117
|
+
{
|
|
118
|
+
type: NcpEventType.MessageTextDelta,
|
|
119
|
+
payload: {
|
|
120
|
+
sessionId: "session-existing",
|
|
121
|
+
messageId: "assistant-1",
|
|
122
|
+
delta: "done",
|
|
123
|
+
},
|
|
94
124
|
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
message: {
|
|
101
|
-
id: "assistant-1",
|
|
102
|
-
sessionId: "session-created",
|
|
103
|
-
role: "assistant",
|
|
104
|
-
status: "final",
|
|
105
|
-
parts: [{ type: "text", text: "done" }],
|
|
106
|
-
timestamp: now,
|
|
125
|
+
{
|
|
126
|
+
type: NcpEventType.MessageTextEnd,
|
|
127
|
+
payload: {
|
|
128
|
+
sessionId: "session-existing",
|
|
129
|
+
messageId: "assistant-1",
|
|
107
130
|
},
|
|
108
131
|
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
132
|
+
{
|
|
133
|
+
type: NcpEventType.RunFinished,
|
|
134
|
+
payload: {
|
|
135
|
+
sessionId: "session-existing",
|
|
136
|
+
runId: "run-1",
|
|
137
|
+
},
|
|
115
138
|
},
|
|
116
|
-
|
|
117
|
-
});
|
|
139
|
+
];
|
|
118
140
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
141
|
+
if (this.liveStreamActive) {
|
|
142
|
+
for (const event of events) {
|
|
143
|
+
this.publish(event);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
sessionId: "session-existing",
|
|
148
|
+
userMessageId: envelope.message.id,
|
|
149
|
+
assistantMessageId: "assistant-1",
|
|
150
|
+
runId: "run-1",
|
|
151
|
+
};
|
|
152
|
+
});
|
|
122
153
|
|
|
123
154
|
private publish = (event: NcpEndpointEvent): void => {
|
|
124
155
|
for (const listener of this.listeners) {
|
|
@@ -127,12 +158,20 @@ class DeferredSendClient implements NcpAgentClientEndpoint {
|
|
|
127
158
|
};
|
|
128
159
|
}
|
|
129
160
|
|
|
161
|
+
function readAssistantText(messages: readonly NcpMessage[]): string {
|
|
162
|
+
const assistant = messages.find((message) => message.role === "assistant");
|
|
163
|
+
return assistant?.parts
|
|
164
|
+
.filter((part) => part.type === "text")
|
|
165
|
+
.map((part) => part.text ?? "")
|
|
166
|
+
.join("") ?? "";
|
|
167
|
+
}
|
|
168
|
+
|
|
130
169
|
describe("useNcpAgentRuntime", () => {
|
|
131
170
|
beforeEach(() => {
|
|
132
171
|
vi.clearAllMocks();
|
|
133
172
|
});
|
|
134
173
|
|
|
135
|
-
it("
|
|
174
|
+
it("returns a command handle when a new root chat materializes a session id", async () => {
|
|
136
175
|
const client = new DeferredSendClient();
|
|
137
176
|
const manager = new DefaultNcpAgentConversationStateManager();
|
|
138
177
|
const envelope: NcpAgentSendEnvelope = {
|
|
@@ -150,30 +189,64 @@ describe("useNcpAgentRuntime", () => {
|
|
|
150
189
|
{ initialProps: { sessionId: undefined as string | undefined } },
|
|
151
190
|
);
|
|
152
191
|
|
|
153
|
-
let
|
|
154
|
-
act(() => {
|
|
155
|
-
|
|
192
|
+
let handle: Awaited<ReturnType<typeof result.current.send>> | null = null;
|
|
193
|
+
await act(async () => {
|
|
194
|
+
handle = await result.current.send(envelope);
|
|
156
195
|
});
|
|
157
196
|
|
|
158
|
-
|
|
159
|
-
|
|
197
|
+
expect(handle).toEqual({
|
|
198
|
+
sessionId: "session-created",
|
|
199
|
+
userMessageId: "user-1",
|
|
200
|
+
assistantMessageId: "assistant-1",
|
|
201
|
+
runId: "run-1",
|
|
160
202
|
});
|
|
203
|
+
expect(result.current.visibleMessages).toEqual([]);
|
|
204
|
+
expect(client.stop).not.toHaveBeenCalled();
|
|
161
205
|
|
|
162
206
|
rerender({ sessionId: "session-created" });
|
|
207
|
+
});
|
|
163
208
|
|
|
164
|
-
|
|
209
|
+
it("aborts by session id even before a hydrated active run reaches local state", async () => {
|
|
210
|
+
const client = new DeferredSendClient();
|
|
211
|
+
const manager = new DefaultNcpAgentConversationStateManager();
|
|
212
|
+
const { result } = renderHook(() =>
|
|
213
|
+
useNcpAgentRuntime({ sessionId: "session-running", client, manager: manager as never }),
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
await act(async () => {
|
|
217
|
+
await result.current.abort();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(client.abort).toHaveBeenCalledWith({ sessionId: "session-running" });
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("uses the hydrated live stream as the only event source while sending to an existing session", async () => {
|
|
224
|
+
const client = new ExistingSessionLiveClient();
|
|
225
|
+
const manager = new DefaultNcpAgentConversationStateManager();
|
|
226
|
+
await client.stream({ sessionId: "session-existing" });
|
|
227
|
+
const { result } = renderHook(() =>
|
|
228
|
+
useNcpAgentRuntime({ sessionId: "session-existing", client, manager: manager as never }),
|
|
229
|
+
);
|
|
165
230
|
|
|
166
231
|
await act(async () => {
|
|
167
|
-
|
|
168
|
-
|
|
232
|
+
await result.current.send({
|
|
233
|
+
sessionId: "session-existing",
|
|
234
|
+
message: {
|
|
235
|
+
id: "user-1",
|
|
236
|
+
sessionId: "session-existing",
|
|
237
|
+
role: "user",
|
|
238
|
+
status: "final",
|
|
239
|
+
parts: [{ type: "text", text: "hello" }],
|
|
240
|
+
timestamp: now,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
169
243
|
});
|
|
170
244
|
|
|
171
245
|
await waitFor(() => {
|
|
172
246
|
expect(result.current.snapshot.activeRun).toBeNull();
|
|
247
|
+
expect(readAssistantText(result.current.visibleMessages)).toBe("done");
|
|
173
248
|
});
|
|
174
|
-
expect(
|
|
175
|
-
|
|
176
|
-
"assistant-1",
|
|
177
|
-
]);
|
|
249
|
+
expect(client.stop).not.toHaveBeenCalled();
|
|
250
|
+
expect(client.stream).toHaveBeenCalledTimes(1);
|
|
178
251
|
});
|
|
179
252
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { useNcpChatPageData } from './use-ncp-chat-page-data';
|
|
4
|
+
|
|
5
|
+
const useNcpSessionSkillsMock = vi.fn();
|
|
6
|
+
|
|
7
|
+
vi.mock('@/shared/hooks/use-config', () => ({
|
|
8
|
+
useConfig: () => ({
|
|
9
|
+
data: {
|
|
10
|
+
agents: { defaults: {} },
|
|
11
|
+
providers: {}
|
|
12
|
+
},
|
|
13
|
+
isFetched: true,
|
|
14
|
+
isSuccess: true
|
|
15
|
+
}),
|
|
16
|
+
useConfigMeta: () => ({
|
|
17
|
+
data: { providers: [] },
|
|
18
|
+
isFetched: true,
|
|
19
|
+
isSuccess: true
|
|
20
|
+
}),
|
|
21
|
+
useNcpSessions: () => ({
|
|
22
|
+
data: { sessions: [] }
|
|
23
|
+
}),
|
|
24
|
+
useNcpSessionSkills: (params: unknown) => useNcpSessionSkillsMock(params)
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
vi.mock('./use-ncp-chat-session-types', () => ({
|
|
28
|
+
useNcpChatSessionTypes: () => ({
|
|
29
|
+
data: {
|
|
30
|
+
defaultType: 'native',
|
|
31
|
+
options: [{ value: 'native', label: 'Native' }]
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
function renderPageData(params: { sessionKey: string | null }) {
|
|
37
|
+
return renderHook(() =>
|
|
38
|
+
useNcpChatPageData({
|
|
39
|
+
sessionKey: params.sessionKey,
|
|
40
|
+
query: '',
|
|
41
|
+
currentSelectedModel: '',
|
|
42
|
+
pendingSessionType: '',
|
|
43
|
+
setPendingSessionType: vi.fn(),
|
|
44
|
+
setSelectedModel: vi.fn(),
|
|
45
|
+
setSelectedThinkingLevel: vi.fn()
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe('useNcpChatPageData skills query', () => {
|
|
51
|
+
it('loads draft-session skills before a new chat materializes', () => {
|
|
52
|
+
useNcpSessionSkillsMock.mockReturnValue({ data: { records: [] }, isLoading: false });
|
|
53
|
+
|
|
54
|
+
renderPageData({ sessionKey: null });
|
|
55
|
+
|
|
56
|
+
expect(useNcpSessionSkillsMock).toHaveBeenCalledWith({
|
|
57
|
+
sessionId: 'draft-session'
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('loads real session skills after materialization', () => {
|
|
62
|
+
useNcpSessionSkillsMock.mockReturnValue({ data: { records: [] }, isLoading: false });
|
|
63
|
+
|
|
64
|
+
renderPageData({ sessionKey: 'session-1' });
|
|
65
|
+
|
|
66
|
+
expect(useNcpSessionSkillsMock).toHaveBeenCalledWith({
|
|
67
|
+
sessionId: 'session-1'
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -158,7 +158,7 @@ export function useNcpChatPageData(params: UseNcpChatPageDataParams) {
|
|
|
158
158
|
const sessionsQuery = useNcpSessions({ limit: 200 });
|
|
159
159
|
const sessionTypesQuery = useNcpChatSessionTypes();
|
|
160
160
|
const sessionSkillsQuery = useNcpSessionSkills({
|
|
161
|
-
sessionId: sessionKey
|
|
161
|
+
sessionId: sessionKey?.trim() || 'draft-session',
|
|
162
162
|
...(Object.prototype.hasOwnProperty.call(params, 'projectRootOverride')
|
|
163
163
|
? { projectRoot: projectRootOverride ?? null }
|
|
164
164
|
: {})
|
|
@@ -49,16 +49,10 @@ export function useNcpChildSessionTabsView(
|
|
|
49
49
|
return new Map(sessions.map((session) => [session.key, session]));
|
|
50
50
|
}, [summaries]);
|
|
51
51
|
|
|
52
|
-
const summaryByKey = useMemo(
|
|
53
|
-
() => new Map(summaries.map((summary) => [summary.sessionId, summary])),
|
|
54
|
-
[summaries],
|
|
55
|
-
);
|
|
56
|
-
|
|
57
52
|
return useMemo(
|
|
58
53
|
() =>
|
|
59
54
|
tabs.map((tab) => {
|
|
60
55
|
const session = sessionByKey.get(tab.sessionKey) ?? null;
|
|
61
|
-
const summary = summaryByKey.get(tab.sessionKey) ?? null;
|
|
62
56
|
const agentId = tab.agentId?.trim() || session?.agentId || null;
|
|
63
57
|
return {
|
|
64
58
|
sessionKey: tab.sessionKey,
|
|
@@ -68,7 +62,7 @@ export function useNcpChildSessionTabsView(
|
|
|
68
62
|
updatedAt: session?.updatedAt ?? null,
|
|
69
63
|
lastMessageAt: session?.lastMessageAt ?? null,
|
|
70
64
|
readAt: session?.readAt ?? null,
|
|
71
|
-
runStatus:
|
|
65
|
+
runStatus: session?.status === "running" ? "running" : undefined,
|
|
72
66
|
sessionTypeLabel: session?.sessionType
|
|
73
67
|
? resolveSessionTypeLabel(session.sessionType)
|
|
74
68
|
: null,
|
|
@@ -77,6 +71,6 @@ export function useNcpChildSessionTabsView(
|
|
|
77
71
|
projectRoot: session?.projectRoot?.trim() || null,
|
|
78
72
|
};
|
|
79
73
|
}),
|
|
80
|
-
[sessionByKey,
|
|
74
|
+
[sessionByKey, tabs],
|
|
81
75
|
);
|
|
82
76
|
}
|
|
@@ -32,11 +32,10 @@ export function useNcpSessionListView(params: { limit?: number } = {}) {
|
|
|
32
32
|
shouldShowSessionInSidebar,
|
|
33
33
|
);
|
|
34
34
|
const filteredSessions = filterSessionsByQuery(sessions, query);
|
|
35
|
-
const summaryBySessionId = new Map(summaries.map((summary) => [summary.sessionId, summary]));
|
|
36
35
|
|
|
37
36
|
return filteredSessions.map((session) => ({
|
|
38
37
|
session,
|
|
39
|
-
runStatus:
|
|
38
|
+
runStatus: session.status === 'running' ? 'running' : undefined
|
|
40
39
|
}));
|
|
41
40
|
}, [query, sessionsQuery.data?.sessions]);
|
|
42
41
|
|
|
@@ -35,7 +35,6 @@ describe('ChatSessionListManager', () => {
|
|
|
35
35
|
snapshot: {
|
|
36
36
|
...useChatSessionListStore.getState().snapshot,
|
|
37
37
|
selectedSessionKey: 'session-1',
|
|
38
|
-
draftSessionKey: 'draft-root-1',
|
|
39
38
|
listMode: 'time-first'
|
|
40
39
|
}
|
|
41
40
|
});
|
|
@@ -65,8 +64,8 @@ describe('ChatSessionListManager', () => {
|
|
|
65
64
|
expect(streamActionsManager.resetStreamState).toHaveBeenCalledTimes(1);
|
|
66
65
|
expect(uiManager.goToChatRoot).toHaveBeenCalledTimes(1);
|
|
67
66
|
expect(useChatSessionListStore.getState().snapshot.selectedSessionKey).toBeNull();
|
|
68
|
-
expect(useChatSessionListStore.getState().snapshot.draftSessionKey).toBeNull();
|
|
69
67
|
expect(useChatThreadStore.getState().snapshot.sessionKey).toBeNull();
|
|
68
|
+
expect(useChatThreadStore.getState().snapshot.hasSubmittedDraftMessage).toBe(false);
|
|
70
69
|
expect(useChatInputStore.getState().snapshot.pendingSessionType).toBe('codex');
|
|
71
70
|
expect(useChatInputStore.getState().snapshot.pendingProjectRoot).toBeNull();
|
|
72
71
|
expect(useChatInputStore.getState().snapshot.pendingProjectRootSessionKey).toBeNull();
|
|
@@ -89,8 +88,8 @@ describe('ChatSessionListManager', () => {
|
|
|
89
88
|
expect(uiManager.goToChatRoot).toHaveBeenCalledTimes(1);
|
|
90
89
|
expect(useChatSessionListStore.getState().snapshot.selectedAgentId).toBe('researcher');
|
|
91
90
|
expect(useChatSessionListStore.getState().snapshot.selectedSessionKey).toBeNull();
|
|
92
|
-
expect(useChatSessionListStore.getState().snapshot.draftSessionKey).toBeNull();
|
|
93
91
|
expect(useChatThreadStore.getState().snapshot.sessionKey).toBeNull();
|
|
92
|
+
expect(useChatThreadStore.getState().snapshot.hasSubmittedDraftMessage).toBe(false);
|
|
94
93
|
expect(useChatInputStore.getState().snapshot.pendingSessionType).toBe('codex');
|
|
95
94
|
expect(useChatInputStore.getState().snapshot.pendingProjectRoot).toBeNull();
|
|
96
95
|
expect(useChatInputStore.getState().snapshot.pendingProjectRootSessionKey).toBeNull();
|
|
@@ -117,8 +116,7 @@ describe('ChatSessionListManager', () => {
|
|
|
117
116
|
useChatSessionListStore.setState({
|
|
118
117
|
snapshot: {
|
|
119
118
|
...useChatSessionListStore.getState().snapshot,
|
|
120
|
-
selectedSessionKey: null
|
|
121
|
-
draftSessionKey: 'draft-root-2'
|
|
119
|
+
selectedSessionKey: null
|
|
122
120
|
}
|
|
123
121
|
});
|
|
124
122
|
const uiManager = {
|
|
@@ -137,6 +135,7 @@ describe('ChatSessionListManager', () => {
|
|
|
137
135
|
expect(uiManager.goToChatRoot).not.toHaveBeenCalled();
|
|
138
136
|
expect(uiManager.goToSession).not.toHaveBeenCalled();
|
|
139
137
|
expect(useChatSessionListStore.getState().snapshot.selectedSessionKey).toBeNull();
|
|
138
|
+
expect(useChatThreadStore.getState().snapshot.hasSubmittedDraftMessage).toBe(true);
|
|
140
139
|
});
|
|
141
140
|
|
|
142
141
|
it('does not eagerly replace the old selected session before the route finishes switching', () => {
|
|
@@ -225,12 +224,11 @@ describe('ChatSessionListManager', () => {
|
|
|
225
224
|
expect(mocks.updateNcpSession).not.toHaveBeenCalled();
|
|
226
225
|
});
|
|
227
226
|
|
|
228
|
-
it('routes to the backend-materialized root session
|
|
227
|
+
it('routes to the backend-materialized root session without duplicating route-owned selection state', () => {
|
|
229
228
|
useChatSessionListStore.setState({
|
|
230
229
|
snapshot: {
|
|
231
230
|
...useChatSessionListStore.getState().snapshot,
|
|
232
231
|
selectedSessionKey: null,
|
|
233
|
-
draftSessionKey: 'draft-root-2',
|
|
234
232
|
}
|
|
235
233
|
});
|
|
236
234
|
const uiManager = {
|
|
@@ -246,8 +244,8 @@ describe('ChatSessionListManager', () => {
|
|
|
246
244
|
manager.ensureDraftSession('native');
|
|
247
245
|
manager.materializeRootSessionRoute('ncp-materialized-session');
|
|
248
246
|
|
|
249
|
-
expect(useChatSessionListStore.getState().snapshot.selectedSessionKey).
|
|
250
|
-
expect(useChatThreadStore.getState().snapshot.sessionKey).
|
|
247
|
+
expect(useChatSessionListStore.getState().snapshot.selectedSessionKey).toBeNull();
|
|
248
|
+
expect(useChatThreadStore.getState().snapshot.sessionKey).toBeNull();
|
|
251
249
|
expect(uiManager.goToSession).toHaveBeenCalledWith('ncp-materialized-session', { replace: true });
|
|
252
250
|
});
|
|
253
251
|
});
|
|
@@ -12,7 +12,7 @@ export class ChatSessionListManager {
|
|
|
12
12
|
private streamActionsManager: ChatStreamActionsManager
|
|
13
13
|
) {}
|
|
14
14
|
|
|
15
|
-
private syncDraftThreadState = () => {
|
|
15
|
+
private syncDraftThreadState = (hasSubmittedDraftMessage = false) => {
|
|
16
16
|
useChatThreadStore.getState().setSnapshot({
|
|
17
17
|
sessionKey: null,
|
|
18
18
|
sessionDisplayName: undefined,
|
|
@@ -21,9 +21,11 @@ export class ChatSessionListManager {
|
|
|
21
21
|
messages: [],
|
|
22
22
|
isSending: false,
|
|
23
23
|
isAwaitingAssistantOutput: false,
|
|
24
|
+
hasSubmittedDraftMessage,
|
|
24
25
|
parentSessionKey: null,
|
|
25
26
|
parentSessionLabel: null,
|
|
26
27
|
workspacePanelParentKey: null,
|
|
28
|
+
activeWorkspacePanelKind: null,
|
|
27
29
|
childSessionTabs: [],
|
|
28
30
|
activeChildSessionKey: null,
|
|
29
31
|
activeWorkspaceFileKey: null,
|
|
@@ -108,7 +110,6 @@ export class ChatSessionListManager {
|
|
|
108
110
|
this.streamActionsManager.resetStreamState();
|
|
109
111
|
useChatSessionListStore.getState().setSnapshot({
|
|
110
112
|
selectedSessionKey: null,
|
|
111
|
-
draftSessionKey: null
|
|
112
113
|
});
|
|
113
114
|
this.syncDraftThreadState();
|
|
114
115
|
useChatInputStore.getState().setSnapshot({
|
|
@@ -134,7 +135,7 @@ export class ChatSessionListManager {
|
|
|
134
135
|
typeof sessionType === 'string' && sessionType.trim().length > 0
|
|
135
136
|
? sessionType.trim()
|
|
136
137
|
: null;
|
|
137
|
-
this.syncDraftThreadState();
|
|
138
|
+
this.syncDraftThreadState(true);
|
|
138
139
|
if (normalizedSessionType) {
|
|
139
140
|
useChatInputStore.getState().setSnapshot({ pendingSessionType: normalizedSessionType });
|
|
140
141
|
}
|
|
@@ -149,19 +150,13 @@ export class ChatSessionListManager {
|
|
|
149
150
|
if (!this.uiManager.isAtChatRoot()) {
|
|
150
151
|
return;
|
|
151
152
|
}
|
|
152
|
-
useChatSessionListStore.getState().setSnapshot({
|
|
153
|
-
selectedSessionKey: normalizedSessionKey,
|
|
154
|
-
draftSessionKey: null,
|
|
155
|
-
});
|
|
156
|
-
useChatThreadStore.getState().setSnapshot({
|
|
157
|
-
sessionKey: normalizedSessionKey,
|
|
158
|
-
});
|
|
159
153
|
this.uiManager.goToSession(normalizedSessionKey, { replace: true });
|
|
160
154
|
};
|
|
161
155
|
|
|
162
156
|
selectSession = (sessionKey: string) => {
|
|
163
157
|
useChatThreadStore.getState().setSnapshot({
|
|
164
158
|
workspacePanelParentKey: null,
|
|
159
|
+
activeWorkspacePanelKind: null,
|
|
165
160
|
activeChildSessionKey: null,
|
|
166
161
|
activeWorkspaceFileKey: null,
|
|
167
162
|
});
|
|
@@ -15,6 +15,7 @@ describe('NcpChatInputManager', () => {
|
|
|
15
15
|
composerNodes: [createChatComposerTextNode('hello from current thread')],
|
|
16
16
|
attachments: [],
|
|
17
17
|
selectedSkills: [],
|
|
18
|
+
composerFocusRequest: null,
|
|
18
19
|
selectedSessionType: 'native',
|
|
19
20
|
selectedModel: 'gpt-5',
|
|
20
21
|
selectedThinkingLevel: null,
|
|
@@ -58,7 +59,6 @@ describe('NcpChatInputManager', () => {
|
|
|
58
59
|
snapshot: {
|
|
59
60
|
...useChatSessionListStore.getState().snapshot,
|
|
60
61
|
selectedSessionKey: 'stale-selected-session',
|
|
61
|
-
draftSessionKey: 'draft-root-session',
|
|
62
62
|
selectedAgentId: 'main',
|
|
63
63
|
},
|
|
64
64
|
});
|
|
@@ -109,7 +109,6 @@ describe('NcpChatInputManager', () => {
|
|
|
109
109
|
snapshot: {
|
|
110
110
|
...useChatSessionListStore.getState().snapshot,
|
|
111
111
|
selectedSessionKey: null,
|
|
112
|
-
draftSessionKey: null,
|
|
113
112
|
},
|
|
114
113
|
});
|
|
115
114
|
const streamActionsManager = {
|
|
@@ -211,4 +210,23 @@ describe('NcpChatInputManager', () => {
|
|
|
211
210
|
expect(streamActionsManager.sendMessage).toHaveBeenCalledTimes(1);
|
|
212
211
|
expect(sessionListManager.materializeRootSessionRoute).not.toHaveBeenCalled();
|
|
213
212
|
});
|
|
213
|
+
|
|
214
|
+
it('creates and consumes one-shot composer focus requests', () => {
|
|
215
|
+
const manager = new NcpChatInputManager(
|
|
216
|
+
{} as ConstructorParameters<typeof NcpChatInputManager>[0],
|
|
217
|
+
{} as ConstructorParameters<typeof NcpChatInputManager>[1],
|
|
218
|
+
{} as ConstructorParameters<typeof NcpChatInputManager>[2],
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
manager.requestComposerFocusAtEnd();
|
|
222
|
+
|
|
223
|
+
const request = useChatInputStore.getState().snapshot.composerFocusRequest;
|
|
224
|
+
expect(request).toEqual({ id: 1, placement: 'end' });
|
|
225
|
+
|
|
226
|
+
manager.consumeComposerFocusRequest(999);
|
|
227
|
+
expect(useChatInputStore.getState().snapshot.composerFocusRequest).toEqual(request);
|
|
228
|
+
|
|
229
|
+
manager.consumeComposerFocusRequest(request!.id);
|
|
230
|
+
expect(useChatInputStore.getState().snapshot.composerFocusRequest).toBeNull();
|
|
231
|
+
});
|
|
214
232
|
});
|
|
@@ -132,6 +132,24 @@ export class NcpChatInputManager {
|
|
|
132
132
|
this.syncComposerSnapshot(createChatComposerNodesFromDraft(value));
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
+
requestComposerFocusAtEnd = () => {
|
|
136
|
+
const currentRequest = useChatInputStore.getState().snapshot.composerFocusRequest;
|
|
137
|
+
useChatInputStore.getState().setSnapshot({
|
|
138
|
+
composerFocusRequest: {
|
|
139
|
+
id: (currentRequest?.id ?? 0) + 1,
|
|
140
|
+
placement: 'end',
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
consumeComposerFocusRequest = (requestId: number) => {
|
|
146
|
+
const currentRequest = useChatInputStore.getState().snapshot.composerFocusRequest;
|
|
147
|
+
if (currentRequest?.id !== requestId) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
useChatInputStore.getState().setSnapshot({ composerFocusRequest: null });
|
|
151
|
+
};
|
|
152
|
+
|
|
135
153
|
setComposerNodes = (next: SetStateAction<ChatComposerNode[]>) => {
|
|
136
154
|
const prev = useChatInputStore.getState().snapshot.composerNodes;
|
|
137
155
|
const value = this.resolveUpdateValue(prev, next);
|
|
@@ -18,4 +18,11 @@ export class NcpChatPresenter {
|
|
|
18
18
|
this.chatSessionListManager,
|
|
19
19
|
this.chatStreamActionsManager
|
|
20
20
|
);
|
|
21
|
+
|
|
22
|
+
startAgentCreationDraft = (prompt: string) => {
|
|
23
|
+
this.chatSessionListManager.createSession();
|
|
24
|
+
this.chatSessionListManager.setSelectedAgentId('main');
|
|
25
|
+
this.chatInputManager.setDraft(prompt);
|
|
26
|
+
this.chatInputManager.requestComposerFocusAtEnd();
|
|
27
|
+
};
|
|
21
28
|
}
|