@agent-native/core 0.217.0 → 0.218.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/dist/automation/index.d.ts +1 -1
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/use-chat-thread-list.d.ts +3 -0
- package/dist/client/use-chat-thread-list.js +23 -7
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +4 -4
- package/dist/secrets/routes.d.ts +10 -10
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/builder-code-starter/template-layer.json +1 -0
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +62 -101
- package/dist/triggers/actions/manage-automation.d.ts +4 -4
- package/docs/content/components.mdx +19 -18
- package/docs/content/sharing.mdx +4 -0
- package/docs/content/toolkit-sharing.mdx +31 -51
- package/migration-manifest.json +1 -1
- package/package.json +3 -3
- package/src/templates/builder-code-starter/template-layer.json +1 -0
- package/src/templates/chat/app/components/layout/Sidebar.tsx +62 -101
- package/dist/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
- package/src/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
|
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
139
139
|
duplicate?: undefined;
|
|
140
140
|
eventId?: undefined;
|
|
141
141
|
} | {
|
|
142
|
+
error?: undefined;
|
|
142
143
|
accepted: boolean;
|
|
143
144
|
duplicate: boolean;
|
|
144
145
|
eventId: string;
|
|
145
|
-
error?: undefined;
|
|
146
146
|
}>;
|
|
@@ -19,7 +19,7 @@ export { AGENT_NATIVE_MCP_APP_HOST_MESSAGE_TYPES, getMcpAppHostContext, initiali
|
|
|
19
19
|
export { buildDynamicAgentSuggestions, dedupeSuggestions, mergeAgentSuggestions, normalizeAgentDynamicSuggestionsConfig, useAgentDynamicSuggestions, useAgentDynamicSuggestionsResult, type AgentDynamicSuggestionContext, type AgentDynamicSuggestionsConfig, type AgentDynamicSuggestionsOption, } from "../dynamic-suggestions.js";
|
|
20
20
|
export { clearChatStorage } from "../chat/storage.js";
|
|
21
21
|
export { isAssistantChatHistoryVersion, coerceAssistantChatHistoryDate, type AssistantChatHistoryDate, type AssistantChatHistoryVersion, } from "../chat/assistant-chat-history-version.js";
|
|
22
|
-
export { ChatThreadListConfigError, useChatThreadList, type UseChatThreadListOptions, type UseChatThreadListResult, } from "../use-chat-thread-list.js";
|
|
22
|
+
export { ChatThreadListConfigError, useChatThreadList, type UseChatThreadListOptions, type UseChatThreadListResult, type ChatThreadListRefreshOutcome, } from "../use-chat-thread-list.js";
|
|
23
23
|
export { ChatThreadListError, fetchChatThreadListPage, notifyChatThreadsUpdated, useChatThreads, type ChatThreadAccessBasis, type ChatThreadListPage, type ChatThreadListRow, type ChatThreadListPageRequest, type ChatThreadOwnership, type ChatThreadVisibility, type ChatThreadScope, type ChatThreadSnapshot, type ChatThreadSummary, type ChatThreadData, type ChatThreadShareLink, type ChatThreadShareState, type UseChatThreadsOptions, } from "../use-chat-threads.js";
|
|
24
24
|
export { type AgentCallProgress, type ContentPart, } from "../sse-event-processor.js";
|
|
25
25
|
export { useRunStuckDetection, useAbortRun, type RunStuckState, type UseRunStuckDetectionOptions, } from "../use-run-stuck-detection.js";
|
|
@@ -16,6 +16,7 @@ export interface UseChatThreadListOptions {
|
|
|
16
16
|
refreshOnThreadUpdates?: boolean;
|
|
17
17
|
apiUrl?: string;
|
|
18
18
|
}
|
|
19
|
+
export type ChatThreadListRefreshOutcome = "success" | "error" | "superseded" | "disabled";
|
|
19
20
|
export interface UseChatThreadListResult {
|
|
20
21
|
threads: ChatThreadListRow[];
|
|
21
22
|
/** True until the first page of the current list settles. */
|
|
@@ -27,6 +28,8 @@ export interface UseChatThreadListResult {
|
|
|
27
28
|
hasMore: boolean;
|
|
28
29
|
loadMore: () => Promise<void>;
|
|
29
30
|
refresh: () => Promise<void>;
|
|
31
|
+
/** Reports whether this invocation applied the complete refreshed window. */
|
|
32
|
+
refreshWithResult: () => Promise<ChatThreadListRefreshOutcome>;
|
|
30
33
|
}
|
|
31
34
|
/** `pageSize` is not an integer between 1 and the server's page cap. */
|
|
32
35
|
export declare class ChatThreadListConfigError extends Error {
|
|
@@ -116,7 +116,7 @@ export function useChatThreadList(options) {
|
|
|
116
116
|
// rows and offset already on screen untouched.
|
|
117
117
|
const replace = useCallback(async (window) => {
|
|
118
118
|
if (!session.active)
|
|
119
|
-
return;
|
|
119
|
+
return "superseded";
|
|
120
120
|
const requestId = ++session.requestId;
|
|
121
121
|
const isCurrent = () => session.active && requestId === session.requestId;
|
|
122
122
|
session.inFlight = "replace";
|
|
@@ -131,7 +131,7 @@ export function useChatThreadList(options) {
|
|
|
131
131
|
limit: Math.min(CHAT_THREADS_MAX_PAGE_SIZE, window - consumed),
|
|
132
132
|
});
|
|
133
133
|
if (!isCurrent())
|
|
134
|
-
return;
|
|
134
|
+
return "superseded";
|
|
135
135
|
for (const thread of page.threads) {
|
|
136
136
|
if (!rows.has(thread.id))
|
|
137
137
|
rows.set(thread.id, thread);
|
|
@@ -142,11 +142,13 @@ export function useChatThreadList(options) {
|
|
|
142
142
|
session.nextOffset = consumed;
|
|
143
143
|
session.hasMore = more;
|
|
144
144
|
update({ threads: [...rows.values()], hasMore: more, error: null });
|
|
145
|
+
return "success";
|
|
145
146
|
}
|
|
146
147
|
catch (failure) {
|
|
147
148
|
if (!isCurrent())
|
|
148
|
-
return;
|
|
149
|
+
return "superseded";
|
|
149
150
|
update({ error: asError(failure) });
|
|
151
|
+
return "error";
|
|
150
152
|
}
|
|
151
153
|
finally {
|
|
152
154
|
if (isCurrent()) {
|
|
@@ -155,13 +157,18 @@ export function useChatThreadList(options) {
|
|
|
155
157
|
}
|
|
156
158
|
}
|
|
157
159
|
}, [request, session, update]);
|
|
158
|
-
const
|
|
160
|
+
const refreshWithResult = useCallback(async () => {
|
|
161
|
+
if (!session.active)
|
|
162
|
+
return "superseded";
|
|
159
163
|
if (!enabled)
|
|
160
|
-
return;
|
|
164
|
+
return "disabled";
|
|
161
165
|
// Reload every row already on screen so a mutation does not collapse a
|
|
162
166
|
// list the person has paged through.
|
|
163
|
-
|
|
167
|
+
return replace(Math.max(pageSize, session.nextOffset));
|
|
164
168
|
}, [enabled, pageSize, replace, session]);
|
|
169
|
+
const refresh = useCallback(async () => {
|
|
170
|
+
await refreshWithResult();
|
|
171
|
+
}, [refreshWithResult]);
|
|
165
172
|
const loadMore = useCallback(async () => {
|
|
166
173
|
if (!session.active || session.inFlight || !session.hasMore)
|
|
167
174
|
return;
|
|
@@ -216,5 +223,14 @@ export function useChatThreadList(options) {
|
|
|
216
223
|
return () => window.removeEventListener(CHAT_THREADS_UPDATED_EVENT, onUpdated);
|
|
217
224
|
}, [enabled, refresh, refreshOnThreadUpdates]);
|
|
218
225
|
const { threads, loading, loadingMore, error, hasMore } = state;
|
|
219
|
-
return {
|
|
226
|
+
return {
|
|
227
|
+
threads,
|
|
228
|
+
loading,
|
|
229
|
+
loadingMore,
|
|
230
|
+
error,
|
|
231
|
+
hasMore,
|
|
232
|
+
loadMore,
|
|
233
|
+
refresh,
|
|
234
|
+
refreshWithResult,
|
|
235
|
+
};
|
|
220
236
|
}
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -16,9 +16,9 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
|
|
|
16
16
|
error: string;
|
|
17
17
|
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
+
error?: undefined;
|
|
19
20
|
liveCopy: "derived" | "not_derived" | "unchanged";
|
|
20
21
|
liveCopyReason?: string;
|
|
21
|
-
error?: undefined;
|
|
22
22
|
ok: boolean;
|
|
23
23
|
} | {
|
|
24
24
|
error?: undefined;
|
|
@@ -13,10 +13,10 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
13
13
|
enabled?: undefined;
|
|
14
14
|
nextRun?: undefined;
|
|
15
15
|
} | {
|
|
16
|
+
deleted?: undefined;
|
|
16
17
|
resourceId: string;
|
|
17
18
|
name: string;
|
|
18
19
|
enabled: boolean;
|
|
19
20
|
nextRun: string;
|
|
20
|
-
deleted?: undefined;
|
|
21
21
|
}>;
|
|
22
22
|
export default _default;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
2
2
|
count: number;
|
|
3
3
|
updated?: undefined;
|
|
4
|
-
ok?: undefined;
|
|
5
4
|
error?: undefined;
|
|
5
|
+
ok?: undefined;
|
|
6
6
|
} | {
|
|
7
7
|
count?: undefined;
|
|
8
8
|
updated: number;
|
|
9
|
-
ok?: undefined;
|
|
10
9
|
error?: undefined;
|
|
10
|
+
ok?: undefined;
|
|
11
11
|
} | {
|
|
12
12
|
count?: undefined;
|
|
13
13
|
updated?: undefined;
|
|
@@ -16,6 +16,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
16
16
|
} | {
|
|
17
17
|
count?: undefined;
|
|
18
18
|
updated?: undefined;
|
|
19
|
-
ok: boolean;
|
|
20
19
|
error?: undefined;
|
|
20
|
+
ok: boolean;
|
|
21
21
|
}>>;
|
|
@@ -33,27 +33,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
33
33
|
summary?: undefined;
|
|
34
34
|
spans?: undefined;
|
|
35
35
|
} | {
|
|
36
|
+
error?: undefined;
|
|
36
37
|
ok?: undefined;
|
|
37
38
|
message?: undefined;
|
|
38
39
|
summary: import("./types.js").TraceSummary;
|
|
39
40
|
spans: import("./types.js").TraceSpan[];
|
|
40
|
-
error?: undefined;
|
|
41
41
|
} | {
|
|
42
|
+
error?: undefined;
|
|
42
43
|
ok?: undefined;
|
|
43
44
|
message?: undefined;
|
|
44
45
|
summary?: undefined;
|
|
45
46
|
spans?: undefined;
|
|
46
47
|
traceMissing: true;
|
|
47
48
|
id: string;
|
|
48
|
-
error?: undefined;
|
|
49
49
|
} | {
|
|
50
|
+
error?: undefined;
|
|
50
51
|
ok?: undefined;
|
|
51
52
|
message?: undefined;
|
|
52
53
|
summary?: undefined;
|
|
53
54
|
spans?: undefined;
|
|
54
55
|
traceMissing?: undefined;
|
|
55
56
|
id: string;
|
|
56
|
-
error?: undefined;
|
|
57
57
|
} | {
|
|
58
58
|
ok?: undefined;
|
|
59
59
|
message?: undefined;
|
|
@@ -61,9 +61,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
61
61
|
spans?: undefined;
|
|
62
62
|
error: any;
|
|
63
63
|
} | {
|
|
64
|
+
error?: undefined;
|
|
64
65
|
message?: undefined;
|
|
65
66
|
summary?: undefined;
|
|
66
67
|
spans?: undefined;
|
|
67
68
|
ok: boolean;
|
|
68
|
-
error?: undefined;
|
|
69
69
|
}>>;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -57,39 +57,39 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
57
57
|
} | {
|
|
58
58
|
error: string;
|
|
59
59
|
errorCode?: undefined;
|
|
60
|
-
status?: undefined;
|
|
61
60
|
ok?: undefined;
|
|
61
|
+
status?: undefined;
|
|
62
62
|
} | {
|
|
63
63
|
error: string;
|
|
64
64
|
errorCode: string;
|
|
65
|
-
status?: undefined;
|
|
66
65
|
ok?: undefined;
|
|
66
|
+
status?: undefined;
|
|
67
67
|
} | {
|
|
68
|
+
error?: undefined;
|
|
68
69
|
errorCode?: undefined;
|
|
69
70
|
ok: boolean;
|
|
70
71
|
status: string;
|
|
71
|
-
error?: undefined;
|
|
72
72
|
} | {
|
|
73
|
+
ok?: undefined;
|
|
73
74
|
error: string;
|
|
74
75
|
removed?: undefined;
|
|
75
|
-
ok?: undefined;
|
|
76
76
|
} | {
|
|
77
|
+
error?: undefined;
|
|
77
78
|
ok: boolean;
|
|
78
79
|
removed: boolean;
|
|
79
|
-
error?: undefined;
|
|
80
80
|
}>>;
|
|
81
81
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
82
|
+
ok?: undefined;
|
|
82
83
|
error: string;
|
|
83
84
|
note?: undefined;
|
|
84
|
-
ok?: undefined;
|
|
85
85
|
} | {
|
|
86
|
+
error?: undefined;
|
|
86
87
|
ok: boolean;
|
|
87
88
|
note?: undefined;
|
|
88
|
-
error?: undefined;
|
|
89
89
|
} | {
|
|
90
|
+
error?: undefined;
|
|
90
91
|
ok: boolean;
|
|
91
92
|
note: string;
|
|
92
|
-
error?: undefined;
|
|
93
93
|
} | {
|
|
94
94
|
note?: undefined;
|
|
95
95
|
ok: boolean;
|
|
@@ -117,11 +117,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
|
|
|
117
117
|
} | {
|
|
118
118
|
error: string;
|
|
119
119
|
} | {
|
|
120
|
+
error?: undefined;
|
|
120
121
|
ok: boolean;
|
|
121
122
|
key: string;
|
|
122
|
-
error?: undefined;
|
|
123
123
|
} | {
|
|
124
|
+
error?: undefined;
|
|
124
125
|
ok: boolean;
|
|
125
126
|
removed: boolean;
|
|
126
|
-
error?: undefined;
|
|
127
127
|
}>>;
|
|
@@ -67,12 +67,12 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
67
67
|
code: any;
|
|
68
68
|
} | {
|
|
69
69
|
errorCode?: undefined;
|
|
70
|
+
error?: undefined;
|
|
70
71
|
code?: undefined;
|
|
71
72
|
ok: boolean;
|
|
72
73
|
key: string;
|
|
73
74
|
baseUrlKey?: string;
|
|
74
75
|
scope: AgentEngineApiKeyScope;
|
|
75
76
|
defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
|
|
76
|
-
error?: undefined;
|
|
77
77
|
}>>;
|
|
78
78
|
export {};
|
|
@@ -16,9 +16,9 @@ export declare function createAgentEngineDisconnectHandler(): import("h3").Event
|
|
|
16
16
|
error: any;
|
|
17
17
|
scope?: undefined;
|
|
18
18
|
} | {
|
|
19
|
-
error?: undefined;
|
|
20
19
|
ok: boolean;
|
|
21
20
|
scope: "org" | "user";
|
|
21
|
+
error?: undefined;
|
|
22
22
|
}>>;
|
|
23
23
|
export interface DefaultModelSelectionRequest {
|
|
24
24
|
engine: string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export declare function createAgentEngineOllamaModelsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
2
|
+
ok?: undefined;
|
|
2
3
|
error: string;
|
|
3
4
|
models?: undefined;
|
|
4
|
-
ok?: undefined;
|
|
5
5
|
} | {
|
|
6
|
+
error?: undefined;
|
|
6
7
|
ok: boolean;
|
|
7
8
|
models: string[];
|
|
8
|
-
error?: undefined;
|
|
9
9
|
}>>;
|
|
@@ -588,8 +588,8 @@ export declare function createPublicRemoteAgentsHandler(discover?: PublicAgentDi
|
|
|
588
588
|
error: string;
|
|
589
589
|
agents?: undefined;
|
|
590
590
|
} | {
|
|
591
|
-
agents: Omit<import("./agent-discovery.js").DiscoveredAgent, "auth" | "kind">[];
|
|
592
591
|
error?: undefined;
|
|
592
|
+
agents: Omit<import("./agent-discovery.js").DiscoveredAgent, "auth" | "kind">[];
|
|
593
593
|
}>>;
|
|
594
594
|
export declare function getBuilderConnectErrorDisposition(error: unknown, connectAttemptId: string | null): "correlated" | "legacy" | null;
|
|
595
595
|
export declare function getBuilderConnectErrorKey(ownerEmail: string, connectAttemptId?: string | null): string;
|
|
@@ -54,8 +54,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
54
54
|
expiresAt?: undefined;
|
|
55
55
|
ttlSeconds?: undefined;
|
|
56
56
|
} | {
|
|
57
|
-
error?: undefined;
|
|
58
57
|
token: string;
|
|
59
58
|
expiresAt: string;
|
|
60
59
|
ttlSeconds: number;
|
|
60
|
+
error?: undefined;
|
|
61
61
|
}>>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appPath } from "@agent-native/core/client/api-path";
|
|
1
2
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
3
|
import {
|
|
3
4
|
navigateWithAgentChatViewTransition,
|
|
@@ -5,14 +6,15 @@ import {
|
|
|
5
6
|
useChatThreads,
|
|
6
7
|
type ChatThreadSummary,
|
|
7
8
|
} from "@agent-native/toolkit/app/chat/agentkit-chat/rail";
|
|
9
|
+
import {
|
|
10
|
+
ownDraftRows,
|
|
11
|
+
OwnershipChatHistoryRail,
|
|
12
|
+
useOwnershipChatHistory,
|
|
13
|
+
type OwnDraftRow,
|
|
14
|
+
} from "@agent-native/toolkit/app/chat/ownership-history";
|
|
8
15
|
import { OrgSwitcher } from "@agent-native/toolkit/app/org";
|
|
9
16
|
import { openCommandMenu } from "@agent-native/toolkit/app/shared";
|
|
10
17
|
import { AgentNativeIcon } from "@agent-native/toolkit/app/shared/AgentNativeIcon";
|
|
11
|
-
import {
|
|
12
|
-
ChatHistoryList,
|
|
13
|
-
type ChatHistoryItem,
|
|
14
|
-
type ChatHistorySection,
|
|
15
|
-
} from "@agent-native/toolkit/chat-history/ChatHistoryList";
|
|
16
18
|
import {
|
|
17
19
|
IconApps,
|
|
18
20
|
IconClock,
|
|
@@ -25,7 +27,7 @@ import {
|
|
|
25
27
|
IconPin,
|
|
26
28
|
IconSearch,
|
|
27
29
|
} from "@tabler/icons-react";
|
|
28
|
-
import { useEffect,
|
|
30
|
+
import { useEffect, useRef, useState } from "react";
|
|
29
31
|
import { Link, useLocation, useNavigate } from "react-router";
|
|
30
32
|
import { toast } from "sonner";
|
|
31
33
|
|
|
@@ -41,7 +43,6 @@ import {
|
|
|
41
43
|
} from "@/components/ui/tooltip";
|
|
42
44
|
import { APP_TITLE } from "@/lib/app-config";
|
|
43
45
|
import { chatThreadPath, HOME_PATH, threadIdFromPath } from "@/lib/chat-paths";
|
|
44
|
-
import { visibleChatThreads } from "@/lib/sidebar-thread-state";
|
|
45
46
|
import { cn } from "@/lib/utils";
|
|
46
47
|
|
|
47
48
|
const CHAT_STORAGE_KEY = "chat";
|
|
@@ -53,11 +54,9 @@ interface SidebarProps {
|
|
|
53
54
|
onCollapsedChange?: (collapsed: boolean) => void;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
function threadUpdatedAt(thread: ChatThreadSummary) {
|
|
57
|
+
function threadUpdatedAt(
|
|
58
|
+
thread: Pick<ChatThreadSummary, "createdAt" | "updatedAt">,
|
|
59
|
+
) {
|
|
61
60
|
return Number.isFinite(thread.updatedAt)
|
|
62
61
|
? thread.updatedAt
|
|
63
62
|
: Number.isFinite(thread.createdAt)
|
|
@@ -65,14 +64,6 @@ function threadUpdatedAt(thread: ChatThreadSummary) {
|
|
|
65
64
|
: 0;
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
function compareRecentThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
|
|
69
|
-
return threadUpdatedAt(b) - threadUpdatedAt(a);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function comparePinnedThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
|
|
73
|
-
return (b.pinnedAt ?? 0) - (a.pinnedAt ?? 0);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
67
|
function formatRelativeTime(timestamp: number) {
|
|
77
68
|
const elapsed = timestamp - Date.now();
|
|
78
69
|
const intervals = [
|
|
@@ -106,7 +97,10 @@ function ThreadHistoryTitle({
|
|
|
106
97
|
title,
|
|
107
98
|
running,
|
|
108
99
|
}: {
|
|
109
|
-
thread:
|
|
100
|
+
thread: OwnDraftRow &
|
|
101
|
+
Partial<
|
|
102
|
+
Pick<ChatThreadSummary, "preview" | "scope" | "source" | "pinnedAt">
|
|
103
|
+
>;
|
|
110
104
|
title: string;
|
|
111
105
|
running: boolean;
|
|
112
106
|
}) {
|
|
@@ -287,7 +281,7 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
287
281
|
pinThread,
|
|
288
282
|
archiveThread,
|
|
289
283
|
renameThread,
|
|
290
|
-
|
|
284
|
+
isNewThread,
|
|
291
285
|
} = useChatThreads(undefined, CHAT_STORAGE_KEY, undefined, {
|
|
292
286
|
autoCreate: false,
|
|
293
287
|
restoreActiveThread: false,
|
|
@@ -295,66 +289,46 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
295
289
|
const { workingThreadIds, observedThreadStarts } =
|
|
296
290
|
useAgentChatRunningThreads();
|
|
297
291
|
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
292
|
+
const localMineRows = ownDraftRows({
|
|
293
|
+
threads,
|
|
294
|
+
isNewThread,
|
|
295
|
+
observedThreadStarts,
|
|
296
|
+
});
|
|
297
|
+
const history = useOwnershipChatHistory({
|
|
298
|
+
getThreadUrl: (threadId) => appPath(chatThreadPath(threadId)),
|
|
299
|
+
activeId:
|
|
300
|
+
threadIdFromPath(location.pathname) ??
|
|
301
|
+
activeThreadId ??
|
|
302
|
+
persistedActiveThreadId(),
|
|
303
|
+
localMineRows,
|
|
304
|
+
pinned: { label: t("chat.pinned") },
|
|
305
|
+
mutations: {
|
|
306
|
+
pin: pinThread,
|
|
307
|
+
rename: renameThread,
|
|
308
|
+
archive: archiveThread,
|
|
309
|
+
},
|
|
310
|
+
onMutationFailed: (kind) => {
|
|
311
|
+
if (kind === "archive") toast.error(t("chat.archiveFailed"));
|
|
312
|
+
if (kind === "rename") toast.error(t("chat.renameFailed"));
|
|
313
|
+
},
|
|
314
|
+
decorateItem: (item, row) => {
|
|
315
|
+
const title = row.title || row.preview || t("chat.untitledChat");
|
|
311
316
|
return {
|
|
312
|
-
|
|
317
|
+
...item,
|
|
313
318
|
title: (
|
|
314
319
|
<ThreadHistoryTitle
|
|
315
|
-
thread={
|
|
320
|
+
thread={row}
|
|
316
321
|
title={title}
|
|
317
|
-
running={workingThreadIds.has(
|
|
322
|
+
running={workingThreadIds.has(row.id)}
|
|
318
323
|
/>
|
|
319
324
|
),
|
|
320
|
-
titleText: title,
|
|
321
|
-
pinned: Boolean(thread.pinnedAt),
|
|
322
325
|
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
.filter((thread) => !thread.pinnedAt)
|
|
330
|
-
.sort(compareRecentThreads)
|
|
331
|
-
.slice(0, 30)
|
|
332
|
-
.map(toItem);
|
|
333
|
-
|
|
334
|
-
return [
|
|
335
|
-
{ id: "pinned", label: t("chat.pinned"), items: pinned },
|
|
336
|
-
{ id: "recents", label: t("chat.recents"), items: recent },
|
|
337
|
-
];
|
|
338
|
-
}, [t, visibleThreads, workingThreadIds]);
|
|
339
|
-
|
|
340
|
-
useEffect(() => {
|
|
341
|
-
const refresh = () => refreshThreads();
|
|
342
|
-
const handleRunning = (event: Event) => {
|
|
343
|
-
const detail = (event as CustomEvent).detail as
|
|
344
|
-
| { isRunning?: unknown }
|
|
345
|
-
| undefined;
|
|
346
|
-
if (typeof detail?.isRunning === "boolean") refreshThreads();
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
window.addEventListener("agent-chat:threads-updated", refresh);
|
|
350
|
-
window.addEventListener("agentNative.chatRunning", handleRunning);
|
|
351
|
-
window.addEventListener("focus", refresh);
|
|
352
|
-
return () => {
|
|
353
|
-
window.removeEventListener("agent-chat:threads-updated", refresh);
|
|
354
|
-
window.removeEventListener("agentNative.chatRunning", handleRunning);
|
|
355
|
-
window.removeEventListener("focus", refresh);
|
|
356
|
-
};
|
|
357
|
-
}, [refreshThreads]);
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
const displayedActiveThreadId =
|
|
329
|
+
threadIdFromPath(location.pathname) ??
|
|
330
|
+
activeThreadId ??
|
|
331
|
+
persistedActiveThreadId();
|
|
358
332
|
|
|
359
333
|
function openThread(threadId: string, options?: { isNew?: boolean }) {
|
|
360
334
|
switchThread(threadId);
|
|
@@ -382,23 +356,6 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
382
356
|
if (threadId) openThread(threadId, { isNew: true });
|
|
383
357
|
}
|
|
384
358
|
|
|
385
|
-
async function handleArchiveThread(threadId: string) {
|
|
386
|
-
const wasActive =
|
|
387
|
-
threadId === activeThreadId || threadId === persistedActiveThreadId();
|
|
388
|
-
const archived = await archiveThread(threadId);
|
|
389
|
-
if (!archived) {
|
|
390
|
-
toast.error(t("chat.archiveFailed"));
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
if (wasActive) await handleNewChat();
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function handleRenameThread(threadId: string, title: string) {
|
|
397
|
-
void renameThread(threadId, title).then((renamed) => {
|
|
398
|
-
if (!renamed) toast.error(t("chat.renameFailed"));
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
|
|
402
359
|
const newChatButton = (
|
|
403
360
|
<button
|
|
404
361
|
type="button"
|
|
@@ -430,17 +387,20 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
430
387
|
return (
|
|
431
388
|
<div className="flex min-h-0 flex-1 flex-col">
|
|
432
389
|
<div className="px-2 pb-3">{newChatButton}</div>
|
|
433
|
-
<
|
|
434
|
-
|
|
390
|
+
<OwnershipChatHistoryRail
|
|
391
|
+
{...history}
|
|
392
|
+
onNewChat={() => void handleNewChat()}
|
|
393
|
+
showNewChat={false}
|
|
435
394
|
activeId={displayedActiveThreadId}
|
|
436
395
|
onSelect={openThread}
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
|
|
396
|
+
onDelete={async (threadId) => {
|
|
397
|
+
const wasActive =
|
|
398
|
+
threadId === activeThreadId ||
|
|
399
|
+
threadId === persistedActiveThreadId();
|
|
400
|
+
const archived = await history.listProps.onDelete?.(threadId);
|
|
401
|
+
if (archived && wasActive) await handleNewChat();
|
|
440
402
|
}}
|
|
441
|
-
onRename={handleRenameThread}
|
|
442
403
|
renameMaxLength={160}
|
|
443
|
-
onDelete={(threadId) => void handleArchiveThread(threadId)}
|
|
444
404
|
labels={{
|
|
445
405
|
options: (item) =>
|
|
446
406
|
t("chat.optionsFor", { title: item.titleText ?? "" }),
|
|
@@ -451,8 +411,9 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
451
411
|
unpin: t("chat.unpinChat"),
|
|
452
412
|
delete: t("chat.archiveChat"),
|
|
453
413
|
}}
|
|
454
|
-
|
|
455
|
-
|
|
414
|
+
railLabels={{
|
|
415
|
+
newChat: t("chat.newChat"),
|
|
416
|
+
}}
|
|
456
417
|
className="chat-sidebar-history min-h-0 flex-1"
|
|
457
418
|
listClassName="pb-4"
|
|
458
419
|
/>
|
|
@@ -67,6 +67,7 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
67
67
|
triggerType: "event" | "manual" | "schedule" | "webhook";
|
|
68
68
|
body: string;
|
|
69
69
|
}, {
|
|
70
|
+
deleted?: undefined;
|
|
70
71
|
created: boolean;
|
|
71
72
|
resourceId: string;
|
|
72
73
|
name: string;
|
|
@@ -87,12 +88,13 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
87
88
|
executionCwd: string;
|
|
88
89
|
mcpTools: string[];
|
|
89
90
|
webhookPath: string;
|
|
90
|
-
deleted?: undefined;
|
|
91
91
|
updated?: undefined;
|
|
92
92
|
changed?: undefined;
|
|
93
93
|
previouslyEnabled?: undefined;
|
|
94
94
|
requestedEnabled?: undefined;
|
|
95
95
|
} | {
|
|
96
|
+
enabled?: undefined;
|
|
97
|
+
nextRun?: undefined;
|
|
96
98
|
created?: undefined;
|
|
97
99
|
deleted: boolean;
|
|
98
100
|
resourceId: string;
|
|
@@ -105,8 +107,6 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
105
107
|
timezone?: undefined;
|
|
106
108
|
condition?: undefined;
|
|
107
109
|
body?: undefined;
|
|
108
|
-
enabled?: undefined;
|
|
109
|
-
nextRun?: undefined;
|
|
110
110
|
createdBy?: undefined;
|
|
111
111
|
model?: undefined;
|
|
112
112
|
reasoningEffort?: undefined;
|
|
@@ -119,8 +119,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
119
119
|
requestedEnabled?: undefined;
|
|
120
120
|
webhookPath?: undefined;
|
|
121
121
|
} | {
|
|
122
|
-
created?: undefined;
|
|
123
122
|
deleted?: undefined;
|
|
123
|
+
created?: undefined;
|
|
124
124
|
updated: boolean;
|
|
125
125
|
resourceId: string;
|
|
126
126
|
name: string;
|
|
@@ -696,24 +696,25 @@ function WorkspaceResources() {
|
|
|
696
696
|
|
|
697
697
|
## Other Public UI {#other-ui}
|
|
698
698
|
|
|
699
|
-
| Area
|
|
700
|
-
|
|
|
701
|
-
| Agent page
|
|
702
|
-
| Blocks UI
|
|
703
|
-
| Canvas
|
|
704
|
-
| Chat history
|
|
705
|
-
|
|
|
706
|
-
|
|
|
707
|
-
|
|
|
708
|
-
| Sharing
|
|
709
|
-
|
|
|
710
|
-
|
|
|
711
|
-
|
|
|
712
|
-
|
|
|
713
|
-
|
|
|
714
|
-
|
|
|
715
|
-
|
|
|
716
|
-
|
|
|
699
|
+
| Area | APIs | Import path |
|
|
700
|
+
| --------------- | ------------------------------------------------------------------------------ | -------------------------------------------------- |
|
|
701
|
+
| Agent page | `<AgentTabsPage>` | `@agent-native/toolkit/app/agent-page` |
|
|
702
|
+
| Blocks UI | `BlockView`, `SchemaBlockEditor`, `BlockRegistryProvider` | `@agent-native/toolkit/app/blocks` |
|
|
703
|
+
| Canvas | `DrawOverlay`, `CanvasCommentPins` | `@agent-native/toolkit/canvas-annotations` |
|
|
704
|
+
| Chat history | `ChatHistoryList`, `ChatHistoryRail` | `@agent-native/toolkit/chat-history` |
|
|
705
|
+
| Owned chat rail | `useOwnershipChatHistory()`, `OwnershipChatHistoryRail` | `@agent-native/toolkit/app/chat/ownership-history` |
|
|
706
|
+
| Data grid | `<DataGrid>` | `@agent-native/toolkit/data-grid` |
|
|
707
|
+
| Dashboard | `DataTable`, `DateRangePicker`, `GenericChartPanel`, `MetricCard`, `StatsCard` | `@agent-native/toolkit/dashboard` |
|
|
708
|
+
| Sharing | `<ShareButton>`, `<ShareDialog>` | `@agent-native/toolkit/app/sharing` |
|
|
709
|
+
| Sharing UI | `<VisibilityBadge>` | `@agent-native/toolkit/sharing` |
|
|
710
|
+
| Notifications | `<NotificationsBell>` | `@agent-native/toolkit/app/notifications` |
|
|
711
|
+
| Progress | `<RunsTray>` | `@agent-native/toolkit/app/progress` |
|
|
712
|
+
| Run registry | Run types and server registry functions | `@agent-native/core/progress` |
|
|
713
|
+
| Onboarding | `useOnboarding()`, onboarding panel hooks | `@agent-native/core/client/onboarding` |
|
|
714
|
+
| Observability | `<ObservabilityDashboard>`, `<ThumbsFeedback>` | `@agent-native/toolkit/app/observability` |
|
|
715
|
+
| Resources UI | `<ResourcesPanel>`, `<ResourceTree>`, `<ResourceEditor>` | `@agent-native/toolkit/app/resources` |
|
|
716
|
+
| Resource hooks | Resource data hooks | `@agent-native/core/client/resources` |
|
|
717
|
+
| Rich editor | `<SharedRichEditor>`, slash commands, block node hooks | `@agent-native/toolkit/editor` |
|
|
717
718
|
|
|
718
719
|
## One-Off Text Completion {#one-off-text-completion}
|
|
719
720
|
|
package/docs/content/sharing.mdx
CHANGED
|
@@ -194,6 +194,10 @@ const shared = useChatThreadList({ ownership: "shared", pageSize: 50 });
|
|
|
194
194
|
|
|
195
195
|
A failed request sets `error` and keeps the rows already loaded. It never looks like an empty list. `<ShareButton>` and `<ShareDialog>` refresh mounted chat lists after they save. If you change a chat's sharing some other way, call `notifyChatThreadsUpdated()` so mounted lists refetch.
|
|
196
196
|
|
|
197
|
+
For a ready-made sidebar that combines owned chats, shared chats, and eligible started drafts, import `useOwnershipChatHistory()` and `OwnershipChatHistoryRail` from `@agent-native/toolkit/app/chat/ownership-history`. The adapter keeps owner and access-basis details on shared rows, pages each section independently, and retains rows and local optimistic changes when a refresh fails. Its mutation callbacks return `Promise<boolean>`, allowing the app to navigate only after a write succeeds. See [Sharing Kit](/docs/toolkit-sharing#shared-chat-history) for an example.
|
|
198
|
+
|
|
199
|
+
An active empty draft remains selectable and runnable while its first save is pending. Until persistence is confirmed, hide pin, rename, and archive actions, and the adapter rejects calls to those callbacks. `ownDraftRows` supplies persistence provenance for owned drafts, while confirmed owned rows from the server list keep their normal actions. For custom `OwnDraftRow` values, set optional `persisted?: boolean` to `true` only for a confirmed persisted server row owned by the current user. Do not infer persistence from message count or active ID. Role, privacy, and whether the active draft is empty remain separate facts.
|
|
200
|
+
|
|
197
201
|
## Same model, agent and UI {#agent-and-ui}
|
|
198
202
|
|
|
199
203
|
The framework auto-mounts these actions in every template — the agent calls them as tools, and the UI calls them through `useActionQuery` / `useActionMutation`:
|
|
@@ -138,66 +138,46 @@ tokens that back agent-readable public surfaces.
|
|
|
138
138
|
|
|
139
139
|
## Shared chat history {#shared-chat-history}
|
|
140
140
|
|
|
141
|
-
`@agent-native/toolkit/chat-history`
|
|
142
|
-
rows and Toolkit renders them, so an app can show My chats and Shared with you
|
|
143
|
-
as separate sections. See [Sharing & Privacy](/docs/sharing#shared-chats) for
|
|
144
|
-
the `ownership` filter and the `ownerEmail` and `accessBasis` fields.
|
|
145
|
-
|
|
146
|
-
`<ChatHistoryRail>` accepts `sections` as well as a flat `items` list. Each
|
|
147
|
-
section previews five rows and has its own **Show all** and **Show less**
|
|
148
|
-
control, loading, error, and empty states, and optional load-more:
|
|
141
|
+
`@agent-native/toolkit/chat-history` provides the presentational rail. For an app sidebar backed by Core chat ownership and lifecycle behavior, use the focused adapter at `@agent-native/toolkit/app/chat/ownership-history`:
|
|
149
142
|
|
|
150
143
|
```tsx
|
|
151
|
-
import {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
144
|
+
import {
|
|
145
|
+
ownDraftRows,
|
|
146
|
+
OwnershipChatHistoryRail,
|
|
147
|
+
useOwnershipChatHistory,
|
|
148
|
+
} from "@agent-native/toolkit/app/chat/ownership-history";
|
|
149
|
+
import { useChatThreads } from "@agent-native/core/client/agent-chat";
|
|
150
|
+
|
|
151
|
+
const { threads, isNewThread, pinThread, renameThread, archiveThread } =
|
|
152
|
+
useChatThreads(undefined, CHAT_STORAGE_KEY, null, { autoCreate: false });
|
|
153
|
+
|
|
154
|
+
const history = useOwnershipChatHistory({
|
|
155
|
+
apiUrl,
|
|
156
|
+
scope: { type: "workspace", id: workspaceId },
|
|
157
|
+
activeId: activeThreadId,
|
|
158
|
+
pinned: true,
|
|
159
|
+
localMineRows: ownDraftRows({ threads, isNewThread }),
|
|
160
|
+
mutations: {
|
|
161
|
+
pin: pinThread,
|
|
162
|
+
rename: renameThread,
|
|
163
|
+
archive: archiveThread,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
156
166
|
|
|
157
|
-
<
|
|
167
|
+
<OwnershipChatHistoryRail
|
|
168
|
+
{...history}
|
|
169
|
+
activeId={activeThreadId}
|
|
158
170
|
onNewChat={startChat}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
showMore: t("showMore"),
|
|
162
|
-
showLess: t("showLess"),
|
|
163
|
-
sections: {
|
|
164
|
-
showAll: t("showAll"),
|
|
165
|
-
showLess: t("showLess"),
|
|
166
|
-
loadMore: t("loadMore"),
|
|
167
|
-
loadingMore: t("loadingMore"),
|
|
168
|
-
retry: t("retry"),
|
|
169
|
-
},
|
|
170
|
-
}}
|
|
171
|
-
sections={[
|
|
172
|
-
{ id: "mine", label: t("myChats"), items: toItems(mine.threads) },
|
|
173
|
-
{
|
|
174
|
-
id: "shared",
|
|
175
|
-
label: t("sharedWithYou"),
|
|
176
|
-
items: toItems(shared.threads),
|
|
177
|
-
hasMore: shared.hasMore,
|
|
178
|
-
loadingMore: shared.loadingMore,
|
|
179
|
-
onLoadMore: shared.loadMore,
|
|
180
|
-
loading: shared.loading,
|
|
181
|
-
error: shared.error ? t("loadFailed") : undefined,
|
|
182
|
-
onRetry: shared.refresh,
|
|
183
|
-
},
|
|
184
|
-
]}
|
|
171
|
+
onSelect={selectThread}
|
|
172
|
+
showNewChat={false}
|
|
185
173
|
/>;
|
|
186
174
|
```
|
|
187
175
|
|
|
188
|
-
|
|
189
|
-
`showAll`, `showLess`, `loadMore`, `loadingMore`, and `retry` as translated text,
|
|
190
|
-
and override them per section with `section.labels` when needed.
|
|
176
|
+
`useOwnershipChatHistory()` requests owned and shared rows from Core before paging. It keeps owner and access basis on shared rows. When pinned grouping is enabled with `pinned: true`, it separates pinned chats from the viewer's other owned chats. Started drafts are not inferred automatically. If the app has local lifecycle data, pass `localMineRows: ownDraftRows({ threads, isNewThread })` to include started drafts proven to belong to the current user. The rail previews five rows in each section, supports Show all and independent paging, and keeps loaded rows visible when a section request fails. Retry preserves local changes until a successful authoritative refresh can reconcile them.
|
|
191
177
|
|
|
192
|
-
|
|
193
|
-
section also offers Load more. A failed load shows above the rows that already
|
|
194
|
-
loaded, so it never reads as an empty section.
|
|
178
|
+
The adapter supplies localized section labels. `railLabels` may override `newChat`, `showMore`, or `showLess` individually. `onNewChat` and `onSelect` are required. Pass `activeId` to both the hook and rail. Mutation callbacks return `Promise<boolean>` so failed writes can roll back without navigating away from the active chat.
|
|
195
179
|
|
|
196
|
-
|
|
197
|
-
`accessLabel`, and `accessBasis` on a `ChatHistoryItem`. The app passes
|
|
198
|
-
translated text, because the Toolkit list ships none. Shared rows should show a
|
|
199
|
-
direct grant over organization access when both apply, which is how Core
|
|
200
|
-
reports `accessBasis`.
|
|
180
|
+
For custom layouts, use `chatThreadToHistoryItem()` to convert a Core row and `ownDraftRows()` to derive eligible started drafts. `ChatHistoryRail` remains available when an app already owns its data and lifecycle logic. See [Sharing & Privacy](/docs/sharing#shared-chats) for ownership filters and access fields.
|
|
201
181
|
|
|
202
182
|
## What's next
|
|
203
183
|
|
package/migration-manifest.json
CHANGED
|
@@ -3330,7 +3330,7 @@
|
|
|
3330
3330
|
"to": "@agent-native/core/client/sharing",
|
|
3331
3331
|
"symbols": {
|
|
3332
3332
|
"VisibilityBadge": {
|
|
3333
|
-
"to": "@agent-native/toolkit/sharing"
|
|
3333
|
+
"to": "@agent-native/toolkit/app/sharing"
|
|
3334
3334
|
},
|
|
3335
3335
|
"ShareButton": {
|
|
3336
3336
|
"to": "@agent-native/toolkit/app/sharing"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.218.0",
|
|
4
4
|
"description": "The agentic application framework for building autonomous agents with intuitive UIs",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -730,8 +730,8 @@
|
|
|
730
730
|
"yaml": "^2.9.0",
|
|
731
731
|
"yjs": "^13.6.33",
|
|
732
732
|
"zod": "^4.3.6",
|
|
733
|
-
"@agent-native/
|
|
734
|
-
"@agent-native/
|
|
733
|
+
"@agent-native/recap-cli": "^0.5.87",
|
|
734
|
+
"@agent-native/agentkit": "^0.218.0"
|
|
735
735
|
},
|
|
736
736
|
"devDependencies": {
|
|
737
737
|
"@ai-sdk/anthropic": "^3.0.71",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appPath } from "@agent-native/core/client/api-path";
|
|
1
2
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
3
|
import {
|
|
3
4
|
navigateWithAgentChatViewTransition,
|
|
@@ -5,14 +6,15 @@ import {
|
|
|
5
6
|
useChatThreads,
|
|
6
7
|
type ChatThreadSummary,
|
|
7
8
|
} from "@agent-native/toolkit/app/chat/agentkit-chat/rail";
|
|
9
|
+
import {
|
|
10
|
+
ownDraftRows,
|
|
11
|
+
OwnershipChatHistoryRail,
|
|
12
|
+
useOwnershipChatHistory,
|
|
13
|
+
type OwnDraftRow,
|
|
14
|
+
} from "@agent-native/toolkit/app/chat/ownership-history";
|
|
8
15
|
import { OrgSwitcher } from "@agent-native/toolkit/app/org";
|
|
9
16
|
import { openCommandMenu } from "@agent-native/toolkit/app/shared";
|
|
10
17
|
import { AgentNativeIcon } from "@agent-native/toolkit/app/shared/AgentNativeIcon";
|
|
11
|
-
import {
|
|
12
|
-
ChatHistoryList,
|
|
13
|
-
type ChatHistoryItem,
|
|
14
|
-
type ChatHistorySection,
|
|
15
|
-
} from "@agent-native/toolkit/chat-history/ChatHistoryList";
|
|
16
18
|
import {
|
|
17
19
|
IconApps,
|
|
18
20
|
IconClock,
|
|
@@ -25,7 +27,7 @@ import {
|
|
|
25
27
|
IconPin,
|
|
26
28
|
IconSearch,
|
|
27
29
|
} from "@tabler/icons-react";
|
|
28
|
-
import { useEffect,
|
|
30
|
+
import { useEffect, useRef, useState } from "react";
|
|
29
31
|
import { Link, useLocation, useNavigate } from "react-router";
|
|
30
32
|
import { toast } from "sonner";
|
|
31
33
|
|
|
@@ -41,7 +43,6 @@ import {
|
|
|
41
43
|
} from "@/components/ui/tooltip";
|
|
42
44
|
import { APP_TITLE } from "@/lib/app-config";
|
|
43
45
|
import { chatThreadPath, HOME_PATH, threadIdFromPath } from "@/lib/chat-paths";
|
|
44
|
-
import { visibleChatThreads } from "@/lib/sidebar-thread-state";
|
|
45
46
|
import { cn } from "@/lib/utils";
|
|
46
47
|
|
|
47
48
|
const CHAT_STORAGE_KEY = "chat";
|
|
@@ -53,11 +54,9 @@ interface SidebarProps {
|
|
|
53
54
|
onCollapsedChange?: (collapsed: boolean) => void;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
function threadUpdatedAt(thread: ChatThreadSummary) {
|
|
57
|
+
function threadUpdatedAt(
|
|
58
|
+
thread: Pick<ChatThreadSummary, "createdAt" | "updatedAt">,
|
|
59
|
+
) {
|
|
61
60
|
return Number.isFinite(thread.updatedAt)
|
|
62
61
|
? thread.updatedAt
|
|
63
62
|
: Number.isFinite(thread.createdAt)
|
|
@@ -65,14 +64,6 @@ function threadUpdatedAt(thread: ChatThreadSummary) {
|
|
|
65
64
|
: 0;
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
function compareRecentThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
|
|
69
|
-
return threadUpdatedAt(b) - threadUpdatedAt(a);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function comparePinnedThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
|
|
73
|
-
return (b.pinnedAt ?? 0) - (a.pinnedAt ?? 0);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
67
|
function formatRelativeTime(timestamp: number) {
|
|
77
68
|
const elapsed = timestamp - Date.now();
|
|
78
69
|
const intervals = [
|
|
@@ -106,7 +97,10 @@ function ThreadHistoryTitle({
|
|
|
106
97
|
title,
|
|
107
98
|
running,
|
|
108
99
|
}: {
|
|
109
|
-
thread:
|
|
100
|
+
thread: OwnDraftRow &
|
|
101
|
+
Partial<
|
|
102
|
+
Pick<ChatThreadSummary, "preview" | "scope" | "source" | "pinnedAt">
|
|
103
|
+
>;
|
|
110
104
|
title: string;
|
|
111
105
|
running: boolean;
|
|
112
106
|
}) {
|
|
@@ -287,7 +281,7 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
287
281
|
pinThread,
|
|
288
282
|
archiveThread,
|
|
289
283
|
renameThread,
|
|
290
|
-
|
|
284
|
+
isNewThread,
|
|
291
285
|
} = useChatThreads(undefined, CHAT_STORAGE_KEY, undefined, {
|
|
292
286
|
autoCreate: false,
|
|
293
287
|
restoreActiveThread: false,
|
|
@@ -295,66 +289,46 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
295
289
|
const { workingThreadIds, observedThreadStarts } =
|
|
296
290
|
useAgentChatRunningThreads();
|
|
297
291
|
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
292
|
+
const localMineRows = ownDraftRows({
|
|
293
|
+
threads,
|
|
294
|
+
isNewThread,
|
|
295
|
+
observedThreadStarts,
|
|
296
|
+
});
|
|
297
|
+
const history = useOwnershipChatHistory({
|
|
298
|
+
getThreadUrl: (threadId) => appPath(chatThreadPath(threadId)),
|
|
299
|
+
activeId:
|
|
300
|
+
threadIdFromPath(location.pathname) ??
|
|
301
|
+
activeThreadId ??
|
|
302
|
+
persistedActiveThreadId(),
|
|
303
|
+
localMineRows,
|
|
304
|
+
pinned: { label: t("chat.pinned") },
|
|
305
|
+
mutations: {
|
|
306
|
+
pin: pinThread,
|
|
307
|
+
rename: renameThread,
|
|
308
|
+
archive: archiveThread,
|
|
309
|
+
},
|
|
310
|
+
onMutationFailed: (kind) => {
|
|
311
|
+
if (kind === "archive") toast.error(t("chat.archiveFailed"));
|
|
312
|
+
if (kind === "rename") toast.error(t("chat.renameFailed"));
|
|
313
|
+
},
|
|
314
|
+
decorateItem: (item, row) => {
|
|
315
|
+
const title = row.title || row.preview || t("chat.untitledChat");
|
|
311
316
|
return {
|
|
312
|
-
|
|
317
|
+
...item,
|
|
313
318
|
title: (
|
|
314
319
|
<ThreadHistoryTitle
|
|
315
|
-
thread={
|
|
320
|
+
thread={row}
|
|
316
321
|
title={title}
|
|
317
|
-
running={workingThreadIds.has(
|
|
322
|
+
running={workingThreadIds.has(row.id)}
|
|
318
323
|
/>
|
|
319
324
|
),
|
|
320
|
-
titleText: title,
|
|
321
|
-
pinned: Boolean(thread.pinnedAt),
|
|
322
325
|
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
.filter((thread) => !thread.pinnedAt)
|
|
330
|
-
.sort(compareRecentThreads)
|
|
331
|
-
.slice(0, 30)
|
|
332
|
-
.map(toItem);
|
|
333
|
-
|
|
334
|
-
return [
|
|
335
|
-
{ id: "pinned", label: t("chat.pinned"), items: pinned },
|
|
336
|
-
{ id: "recents", label: t("chat.recents"), items: recent },
|
|
337
|
-
];
|
|
338
|
-
}, [t, visibleThreads, workingThreadIds]);
|
|
339
|
-
|
|
340
|
-
useEffect(() => {
|
|
341
|
-
const refresh = () => refreshThreads();
|
|
342
|
-
const handleRunning = (event: Event) => {
|
|
343
|
-
const detail = (event as CustomEvent).detail as
|
|
344
|
-
| { isRunning?: unknown }
|
|
345
|
-
| undefined;
|
|
346
|
-
if (typeof detail?.isRunning === "boolean") refreshThreads();
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
window.addEventListener("agent-chat:threads-updated", refresh);
|
|
350
|
-
window.addEventListener("agentNative.chatRunning", handleRunning);
|
|
351
|
-
window.addEventListener("focus", refresh);
|
|
352
|
-
return () => {
|
|
353
|
-
window.removeEventListener("agent-chat:threads-updated", refresh);
|
|
354
|
-
window.removeEventListener("agentNative.chatRunning", handleRunning);
|
|
355
|
-
window.removeEventListener("focus", refresh);
|
|
356
|
-
};
|
|
357
|
-
}, [refreshThreads]);
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
const displayedActiveThreadId =
|
|
329
|
+
threadIdFromPath(location.pathname) ??
|
|
330
|
+
activeThreadId ??
|
|
331
|
+
persistedActiveThreadId();
|
|
358
332
|
|
|
359
333
|
function openThread(threadId: string, options?: { isNew?: boolean }) {
|
|
360
334
|
switchThread(threadId);
|
|
@@ -382,23 +356,6 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
382
356
|
if (threadId) openThread(threadId, { isNew: true });
|
|
383
357
|
}
|
|
384
358
|
|
|
385
|
-
async function handleArchiveThread(threadId: string) {
|
|
386
|
-
const wasActive =
|
|
387
|
-
threadId === activeThreadId || threadId === persistedActiveThreadId();
|
|
388
|
-
const archived = await archiveThread(threadId);
|
|
389
|
-
if (!archived) {
|
|
390
|
-
toast.error(t("chat.archiveFailed"));
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
if (wasActive) await handleNewChat();
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function handleRenameThread(threadId: string, title: string) {
|
|
397
|
-
void renameThread(threadId, title).then((renamed) => {
|
|
398
|
-
if (!renamed) toast.error(t("chat.renameFailed"));
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
|
|
402
359
|
const newChatButton = (
|
|
403
360
|
<button
|
|
404
361
|
type="button"
|
|
@@ -430,17 +387,20 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
430
387
|
return (
|
|
431
388
|
<div className="flex min-h-0 flex-1 flex-col">
|
|
432
389
|
<div className="px-2 pb-3">{newChatButton}</div>
|
|
433
|
-
<
|
|
434
|
-
|
|
390
|
+
<OwnershipChatHistoryRail
|
|
391
|
+
{...history}
|
|
392
|
+
onNewChat={() => void handleNewChat()}
|
|
393
|
+
showNewChat={false}
|
|
435
394
|
activeId={displayedActiveThreadId}
|
|
436
395
|
onSelect={openThread}
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
|
|
396
|
+
onDelete={async (threadId) => {
|
|
397
|
+
const wasActive =
|
|
398
|
+
threadId === activeThreadId ||
|
|
399
|
+
threadId === persistedActiveThreadId();
|
|
400
|
+
const archived = await history.listProps.onDelete?.(threadId);
|
|
401
|
+
if (archived && wasActive) await handleNewChat();
|
|
440
402
|
}}
|
|
441
|
-
onRename={handleRenameThread}
|
|
442
403
|
renameMaxLength={160}
|
|
443
|
-
onDelete={(threadId) => void handleArchiveThread(threadId)}
|
|
444
404
|
labels={{
|
|
445
405
|
options: (item) =>
|
|
446
406
|
t("chat.optionsFor", { title: item.titleText ?? "" }),
|
|
@@ -451,8 +411,9 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
|
|
|
451
411
|
unpin: t("chat.unpinChat"),
|
|
452
412
|
delete: t("chat.archiveChat"),
|
|
453
413
|
}}
|
|
454
|
-
|
|
455
|
-
|
|
414
|
+
railLabels={{
|
|
415
|
+
newChat: t("chat.newChat"),
|
|
416
|
+
}}
|
|
456
417
|
className="chat-sidebar-history min-h-0 flex-1"
|
|
457
418
|
listClassName="pb-4"
|
|
458
419
|
/>
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ChatThreadSummary } from "@agent-native/toolkit/app/chat/agentkit-chat/index";
|
|
2
|
-
|
|
3
|
-
export function visibleChatThreads(
|
|
4
|
-
threads: ChatThreadSummary[],
|
|
5
|
-
observedThreadStarts: ReadonlyMap<string, number>,
|
|
6
|
-
): ChatThreadSummary[] {
|
|
7
|
-
const knownIds = new Set(threads.map((thread) => thread.id));
|
|
8
|
-
const optimistic: ChatThreadSummary[] = [...observedThreadStarts].flatMap(
|
|
9
|
-
([id, startedAt]) =>
|
|
10
|
-
knownIds.has(id)
|
|
11
|
-
? []
|
|
12
|
-
: [
|
|
13
|
-
{
|
|
14
|
-
id,
|
|
15
|
-
title: "",
|
|
16
|
-
preview: "",
|
|
17
|
-
messageCount: 0,
|
|
18
|
-
createdAt: startedAt,
|
|
19
|
-
updatedAt: startedAt,
|
|
20
|
-
scope: null,
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
);
|
|
24
|
-
return [...threads, ...optimistic].filter(
|
|
25
|
-
(thread) =>
|
|
26
|
-
!thread.archivedAt &&
|
|
27
|
-
(thread.messageCount > 0 || observedThreadStarts.has(thread.id)),
|
|
28
|
-
);
|
|
29
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ChatThreadSummary } from "@agent-native/toolkit/app/chat/agentkit-chat/index";
|
|
2
|
-
|
|
3
|
-
export function visibleChatThreads(
|
|
4
|
-
threads: ChatThreadSummary[],
|
|
5
|
-
observedThreadStarts: ReadonlyMap<string, number>,
|
|
6
|
-
): ChatThreadSummary[] {
|
|
7
|
-
const knownIds = new Set(threads.map((thread) => thread.id));
|
|
8
|
-
const optimistic: ChatThreadSummary[] = [...observedThreadStarts].flatMap(
|
|
9
|
-
([id, startedAt]) =>
|
|
10
|
-
knownIds.has(id)
|
|
11
|
-
? []
|
|
12
|
-
: [
|
|
13
|
-
{
|
|
14
|
-
id,
|
|
15
|
-
title: "",
|
|
16
|
-
preview: "",
|
|
17
|
-
messageCount: 0,
|
|
18
|
-
createdAt: startedAt,
|
|
19
|
-
updatedAt: startedAt,
|
|
20
|
-
scope: null,
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
);
|
|
24
|
-
return [...threads, ...optimistic].filter(
|
|
25
|
-
(thread) =>
|
|
26
|
-
!thread.archivedAt &&
|
|
27
|
-
(thread.messageCount > 0 || observedThreadStarts.has(thread.id)),
|
|
28
|
-
);
|
|
29
|
-
}
|