@hachej/boring-agent 0.1.94 → 0.1.96
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/DebugDrawer-2TO5SE2D.js +7 -0
- package/dist/{chunk-NITBKFG6.js → chunk-3OEDBRKY.js} +93 -58
- package/dist/{chunk-BOIV2I3N.js → chunk-46HJK2ES.js} +31 -16
- package/dist/{chunk-AQBXNPMD.js → chunk-4IQ4HHBG.js} +4 -0
- package/dist/{chunk-OFQ7J6SI.js → chunk-LSO2AO6Q.js} +57 -1
- package/dist/{chunk-N4AATPH7.js → chunk-NSE2RHU3.js} +8973 -7731
- package/dist/{chunk-2GBHYJVC.js → chunk-VXF5DWS6.js} +34 -0
- package/dist/{chunk-2TMS2RU2.js → chunk-WC5KBGP2.js} +518 -165
- package/dist/chunk-YGRQ4Q5P.js +20 -0
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +604 -7
- package/dist/{createHarness-UCD74XXK.js → createHarness-PW3A2MRB.js} +3 -3
- package/dist/{errors-G6T6S7PQ.d.ts → errors-cNC4HMpV.d.ts} +14 -7
- package/dist/eval/index.d.ts +5 -3
- package/dist/eval/index.js +31 -23
- package/dist/file-search-QxnanNjO.d.ts +87 -0
- package/dist/front/index.d.ts +87 -17
- package/dist/front/index.js +1232 -690
- package/dist/front/styles.css +1228 -2167
- package/dist/{harness-Cc48o6uS.d.ts → harness-DS8jLKIx.d.ts} +4 -2
- package/dist/{lease-CusB-uLg.d.ts → lease-D8WC6pQl.d.ts} +10 -89
- package/dist/{middleware-CzW8n7B7.d.ts → middleware-Cq_cYHjJ.d.ts} +1 -1
- package/dist/piChatEvent-B3ehh9V0.d.ts +384 -0
- package/dist/{piChatSessionService-C0KbcGw9.d.ts → piChatSessionService-xnv8rcdh.d.ts} +1 -1
- package/dist/{sandbox-DAD8cWQR.d.ts → sandbox-KQiEdz4W.d.ts} +12 -1
- package/dist/server/agent-host/testing/compositionRouteProof.d.ts +8 -0
- package/dist/server/agent-host/testing/compositionRouteProof.js +78 -0
- package/dist/server/agent-host/testing/gatewayConformance.d.ts +6 -6
- package/dist/server/agent-host/testing/gatewayConformance.js +17 -9
- package/dist/server/index.d.ts +570 -370
- package/dist/server/index.js +69 -17
- package/dist/server/worker/index.d.ts +2 -2
- package/dist/server/worker/index.js +9 -9
- package/dist/shared/index.d.ts +212 -56
- package/dist/shared/index.js +43 -7
- package/dist/{types-C-Eroi__.d.ts → types-D_SD-7_l.d.ts} +304 -87
- package/docs/AGENT_GATEWAY_V0.md +6 -3
- package/docs/AGENT_PACKAGES.md +80 -0
- package/docs/ERROR_CODES.md +58 -0
- package/docs/README.md +1 -0
- package/docs/runtime.md +19 -2
- package/package.json +34 -29
- package/dist/DebugDrawer-RYEBQ6CO.js +0 -6
- package/dist/chunk-EOMY2HDT.js +0 -619
- package/dist/piChatEvent-BsMp3MSo.d.ts +0 -368
package/dist/front/index.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DebugDrawer,
|
|
3
|
+
agentResourceUrl,
|
|
3
4
|
cn,
|
|
4
|
-
copyTextToClipboard
|
|
5
|
-
|
|
5
|
+
copyTextToClipboard,
|
|
6
|
+
createRequestId,
|
|
7
|
+
withStorageScope
|
|
8
|
+
} from "../chunk-46HJK2ES.js";
|
|
6
9
|
import {
|
|
10
|
+
AGENT_GATEWAY_ERROR_CODES,
|
|
11
|
+
AgentGatewayErrorCode,
|
|
7
12
|
CommandReceiptSchema,
|
|
8
13
|
FollowUpReceiptSchema,
|
|
9
14
|
PiChatSnapshotSchema,
|
|
@@ -15,10 +20,11 @@ import {
|
|
|
15
20
|
WORKSPACE_COMMAND_NOTIFY_EVENT,
|
|
16
21
|
extractToolUiMetadata,
|
|
17
22
|
sanitizeToolUiMetadata
|
|
18
|
-
} from "../chunk-
|
|
23
|
+
} from "../chunk-LSO2AO6Q.js";
|
|
24
|
+
import "../chunk-YGRQ4Q5P.js";
|
|
19
25
|
import {
|
|
20
26
|
ErrorCode
|
|
21
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-VXF5DWS6.js";
|
|
22
28
|
|
|
23
29
|
// src/front/upload/uploadFile.ts
|
|
24
30
|
function readAsDataUrl(file) {
|
|
@@ -140,12 +146,73 @@ var defaultChatSuggestions = [
|
|
|
140
146
|
prompt: "Help me refactor a function \u2014 clean it up without changing behavior."
|
|
141
147
|
}
|
|
142
148
|
];
|
|
149
|
+
function ChatSuggestionGrid({ suggestions, onSelect, className }) {
|
|
150
|
+
if (suggestions.length === 0) return null;
|
|
151
|
+
return /* @__PURE__ */ jsx2(
|
|
152
|
+
"div",
|
|
153
|
+
{
|
|
154
|
+
"data-boring-agent-part": "suggestion-grid",
|
|
155
|
+
style: {
|
|
156
|
+
["--chat-empty-card-hover"]: "var(--accent-soft)",
|
|
157
|
+
["--chat-empty-accent"]: "var(--accent)"
|
|
158
|
+
},
|
|
159
|
+
className: cn(
|
|
160
|
+
"grid w-full grid-cols-1 gap-px overflow-hidden rounded-xl bg-border/70 ring-1 ring-border/70 @[480px]:grid-cols-2",
|
|
161
|
+
className
|
|
162
|
+
),
|
|
163
|
+
children: suggestions.map((suggestion) => {
|
|
164
|
+
const Icon = suggestion.icon;
|
|
165
|
+
return /* @__PURE__ */ jsxs(
|
|
166
|
+
Button,
|
|
167
|
+
{
|
|
168
|
+
type: "button",
|
|
169
|
+
variant: "ghost",
|
|
170
|
+
"data-boring-agent-part": "suggestion-card",
|
|
171
|
+
onClick: () => {
|
|
172
|
+
if (suggestion.onSelect) {
|
|
173
|
+
suggestion.onSelect();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
onSelect?.(suggestion);
|
|
177
|
+
},
|
|
178
|
+
className: "group h-auto justify-start gap-3 rounded-none bg-background px-4 py-3.5 text-left hover:bg-[color:var(--chat-empty-card-hover)] focus-visible:bg-[color:var(--chat-empty-card-hover)]",
|
|
179
|
+
children: [
|
|
180
|
+
Icon && /* @__PURE__ */ jsx2(
|
|
181
|
+
Icon,
|
|
182
|
+
{
|
|
183
|
+
className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground transition-colors group-hover:text-[color:var(--chat-empty-accent)]",
|
|
184
|
+
strokeWidth: 1.75
|
|
185
|
+
}
|
|
186
|
+
),
|
|
187
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
188
|
+
/* @__PURE__ */ jsx2("span", { className: "text-[13px] font-medium text-foreground", children: suggestion.label }),
|
|
189
|
+
suggestion.hint && /* @__PURE__ */ jsx2("span", { className: "text-[12px] text-muted-foreground", children: suggestion.hint })
|
|
190
|
+
] }),
|
|
191
|
+
/* @__PURE__ */ jsx2(
|
|
192
|
+
ArrowUpRight,
|
|
193
|
+
{
|
|
194
|
+
className: cn(
|
|
195
|
+
"mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground opacity-0 transition-all duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
196
|
+
"group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:opacity-100 group-hover:text-[color:var(--chat-empty-accent)]"
|
|
197
|
+
),
|
|
198
|
+
strokeWidth: 2
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
suggestion.label
|
|
204
|
+
);
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
}
|
|
143
209
|
function ChatEmptyState({
|
|
144
210
|
eyebrow = "New session",
|
|
145
|
-
title = "What
|
|
211
|
+
title = "What should we work on?",
|
|
146
212
|
description = "Ask a question, open a file from the workbench, or start from a template.",
|
|
147
213
|
suggestions = defaultChatSuggestions,
|
|
148
214
|
onSelect,
|
|
215
|
+
hero = false,
|
|
149
216
|
footer,
|
|
150
217
|
className
|
|
151
218
|
}) {
|
|
@@ -164,65 +231,20 @@ function ChatEmptyState({
|
|
|
164
231
|
// @container lets the hero scale to the pane it lives in, not the
|
|
165
232
|
// viewport — split chat panes get the compact variant.
|
|
166
233
|
"@container mx-auto flex w-full max-w-[640px] flex-col px-2 pt-12 pb-4",
|
|
234
|
+
hero && "items-center py-0 text-center",
|
|
167
235
|
className
|
|
168
236
|
),
|
|
169
237
|
children: [
|
|
170
|
-
eyebrow && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[10.5px] font-medium uppercase tracking-[0.16em] text-[color:var(--chat-empty-eyebrow-color)]", children: [
|
|
238
|
+
!hero && eyebrow && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[10.5px] font-medium uppercase tracking-[0.16em] text-[color:var(--chat-empty-eyebrow-color)]", children: [
|
|
171
239
|
/* @__PURE__ */ jsx2("span", { className: "inline-block h-px w-4 bg-[color:var(--chat-empty-accent)]", "aria-hidden": "true" }),
|
|
172
240
|
eyebrow
|
|
173
241
|
] }),
|
|
174
|
-
title && /* @__PURE__ */ jsx2("h3", { className:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
className: "mt-8 grid w-full grid-cols-1 gap-px overflow-hidden rounded-xl bg-border/70 ring-1 ring-border/70 @[480px]:grid-cols-2",
|
|
181
|
-
children: suggestions.map((suggestion) => {
|
|
182
|
-
const Icon = suggestion.icon;
|
|
183
|
-
return /* @__PURE__ */ jsxs(
|
|
184
|
-
Button,
|
|
185
|
-
{
|
|
186
|
-
type: "button",
|
|
187
|
-
variant: "ghost",
|
|
188
|
-
"data-boring-agent-part": "suggestion-card",
|
|
189
|
-
onClick: () => {
|
|
190
|
-
if (suggestion.onSelect) {
|
|
191
|
-
suggestion.onSelect();
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
onSelect?.(suggestion);
|
|
195
|
-
},
|
|
196
|
-
className: "group h-auto justify-start gap-3 rounded-none bg-background px-4 py-3.5 text-left hover:bg-[color:var(--chat-empty-card-hover)] focus-visible:bg-[color:var(--chat-empty-card-hover)]",
|
|
197
|
-
children: [
|
|
198
|
-
Icon && /* @__PURE__ */ jsx2(
|
|
199
|
-
Icon,
|
|
200
|
-
{
|
|
201
|
-
className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground transition-colors group-hover:text-[color:var(--chat-empty-accent)]",
|
|
202
|
-
strokeWidth: 1.75
|
|
203
|
-
}
|
|
204
|
-
),
|
|
205
|
-
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
206
|
-
/* @__PURE__ */ jsx2("span", { className: "text-[13px] font-medium text-foreground", children: suggestion.label }),
|
|
207
|
-
suggestion.hint && /* @__PURE__ */ jsx2("span", { className: "text-[12px] text-muted-foreground", children: suggestion.hint })
|
|
208
|
-
] }),
|
|
209
|
-
/* @__PURE__ */ jsx2(
|
|
210
|
-
ArrowUpRight,
|
|
211
|
-
{
|
|
212
|
-
className: cn(
|
|
213
|
-
"mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground opacity-0 transition-all duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
214
|
-
"group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:opacity-100 group-hover:text-[color:var(--chat-empty-accent)]"
|
|
215
|
-
),
|
|
216
|
-
strokeWidth: 2
|
|
217
|
-
}
|
|
218
|
-
)
|
|
219
|
-
]
|
|
220
|
-
},
|
|
221
|
-
suggestion.label
|
|
222
|
-
);
|
|
223
|
-
})
|
|
224
|
-
}
|
|
225
|
-
),
|
|
242
|
+
title && /* @__PURE__ */ jsx2("h3", { className: cn(
|
|
243
|
+
"text-[24px] font-medium leading-[1.05] tracking-[-0.02em] text-[color:var(--chat-empty-title-color)] @[480px]:text-[34px]",
|
|
244
|
+
hero ? "m-0" : "mt-3"
|
|
245
|
+
), children: title }),
|
|
246
|
+
!hero && description && /* @__PURE__ */ jsx2("p", { className: "mt-3 hidden max-w-[440px] text-[14px] leading-relaxed text-[color:var(--chat-empty-description-color)] @[420px]:block", children: description }),
|
|
247
|
+
/* @__PURE__ */ jsx2(ChatSuggestionGrid, { suggestions, onSelect, className: hero ? "mt-4" : "mt-8" }),
|
|
226
248
|
footer
|
|
227
249
|
]
|
|
228
250
|
}
|
|
@@ -1996,14 +2018,16 @@ function MentionPicker({
|
|
|
1996
2018
|
setActiveIdx(0);
|
|
1997
2019
|
const ctrl = new AbortController();
|
|
1998
2020
|
const runFetch = fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
1999
|
-
const headers =
|
|
2021
|
+
const headers = withStorageScope(requestHeaders, storageScope);
|
|
2000
2022
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
2001
2023
|
const t = setTimeout(() => {
|
|
2002
2024
|
runFetch(`${base}/api/v1/files/search?q=${encodeURIComponent(mentionSearchGlob(mention.query))}&limit=8`, {
|
|
2003
2025
|
signal: ctrl.signal,
|
|
2004
2026
|
...headers ? { headers } : {}
|
|
2005
2027
|
}).then((r) => r.ok ? r.json() : null).then((data) => {
|
|
2006
|
-
if (data?.
|
|
2028
|
+
if (data?.resources) {
|
|
2029
|
+
setResults(data.resources.filter((resource) => resource.filesystem === "user").map((resource) => resource.path).slice(0, 8));
|
|
2030
|
+
}
|
|
2007
2031
|
}).catch(() => {
|
|
2008
2032
|
});
|
|
2009
2033
|
}, 120);
|
|
@@ -2054,15 +2078,6 @@ function detectMention(value, cursorPos) {
|
|
|
2054
2078
|
const atIdx = before.lastIndexOf("@");
|
|
2055
2079
|
return { query: match[2], anchorStart: atIdx, anchorEnd: cursorPos };
|
|
2056
2080
|
}
|
|
2057
|
-
function scopedHeaders(requestHeaders, storageScope) {
|
|
2058
|
-
const headers = {};
|
|
2059
|
-
for (const [key, value] of Object.entries(requestHeaders ?? {})) {
|
|
2060
|
-
if (value !== void 0) headers[key] = value;
|
|
2061
|
-
}
|
|
2062
|
-
const hasStorageScope = Object.keys(headers).some((key) => key.toLowerCase() === "x-boring-storage-scope");
|
|
2063
|
-
if (storageScope && !hasStorageScope) headers["x-boring-storage-scope"] = storageScope;
|
|
2064
|
-
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
2065
|
-
}
|
|
2066
2081
|
|
|
2067
2082
|
// src/front/useComposerPickers.ts
|
|
2068
2083
|
function setTextareaValue2(textarea, value) {
|
|
@@ -2583,6 +2598,7 @@ function resolveStorage(storage) {
|
|
|
2583
2598
|
|
|
2584
2599
|
// src/front/hooks/useChatModelSelection.ts
|
|
2585
2600
|
function useChatModelSelection({
|
|
2601
|
+
agentTypeId,
|
|
2586
2602
|
apiBaseUrl,
|
|
2587
2603
|
defaultModel,
|
|
2588
2604
|
fetch: fetchImpl,
|
|
@@ -2604,14 +2620,16 @@ function useChatModelSelection({
|
|
|
2604
2620
|
userSelectedModelRef.current = userSelectedModel;
|
|
2605
2621
|
}, [userSelectedModel]);
|
|
2606
2622
|
const setModel = useCallback6((next) => {
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2623
|
+
const normalized = next === null ? null : parseModelSelection(next);
|
|
2624
|
+
userSelectedModelRef.current = normalized !== null;
|
|
2625
|
+
setUserSelectedModel(normalized !== null);
|
|
2626
|
+
setModelState(normalized);
|
|
2627
|
+
writePiComposerModelSelection(normalized, { storageScope, storage });
|
|
2611
2628
|
}, [storage, storageScope]);
|
|
2612
2629
|
const discoveryKey = useMemo2(
|
|
2613
2630
|
() => JSON.stringify({
|
|
2614
2631
|
apiBaseUrl: apiBaseUrl ?? "",
|
|
2632
|
+
agentTypeId,
|
|
2615
2633
|
headers: Object.entries(requestHeaders ?? {}).sort(([a], [b]) => a.localeCompare(b)),
|
|
2616
2634
|
storageScope: storageScope ?? ""
|
|
2617
2635
|
}),
|
|
@@ -2645,8 +2663,8 @@ function useChatModelSelection({
|
|
|
2645
2663
|
let aborted = false;
|
|
2646
2664
|
setLoaded(false);
|
|
2647
2665
|
const nextFetch = fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
2648
|
-
nextFetch(agentResourceUrl(apiBaseUrl,
|
|
2649
|
-
headers:
|
|
2666
|
+
nextFetch(agentResourceUrl(apiBaseUrl, `/api/v1/agents/${encodeURIComponent(agentTypeId)}/models`), {
|
|
2667
|
+
headers: withStorageScope(requestHeaders, storageScope)
|
|
2650
2668
|
}).then((res) => res.ok ? res.json() : null).then((payload) => {
|
|
2651
2669
|
if (aborted) return;
|
|
2652
2670
|
if (!payload?.models) {
|
|
@@ -2686,7 +2704,7 @@ function useChatModelSelection({
|
|
|
2686
2704
|
return () => {
|
|
2687
2705
|
aborted = true;
|
|
2688
2706
|
};
|
|
2689
|
-
}, [apiBaseUrl, discoveryKey, enabled, fetchImpl, requestHeaders, storage, storageScope]);
|
|
2707
|
+
}, [agentTypeId, apiBaseUrl, discoveryKey, enabled, fetchImpl, requestHeaders, storage, storageScope]);
|
|
2690
2708
|
useEffect5(() => {
|
|
2691
2709
|
const onChange = (event) => {
|
|
2692
2710
|
const next = parseModelSelection(event.detail);
|
|
@@ -2700,17 +2718,6 @@ function useChatModelSelection({
|
|
|
2700
2718
|
const currentModel = currentDiscoveryLoaded ? model : null;
|
|
2701
2719
|
return { availableModels: currentAvailableModels, loaded: currentDiscoveryLoaded, model: currentModel, setModel };
|
|
2702
2720
|
}
|
|
2703
|
-
function agentResourceUrl(apiBaseUrl, path) {
|
|
2704
|
-
const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
2705
|
-
return `${base}${path}`;
|
|
2706
|
-
}
|
|
2707
|
-
function scopedHeaders2(headers, storageScope) {
|
|
2708
|
-
if (!headers && !storageScope) return void 0;
|
|
2709
|
-
const result = { ...headers ?? {} };
|
|
2710
|
-
const hasStorageScope = Object.keys(result).some((key) => key.toLowerCase() === "x-boring-storage-scope");
|
|
2711
|
-
if (storageScope && !hasStorageScope) result["x-boring-storage-scope"] = storageScope;
|
|
2712
|
-
return result;
|
|
2713
|
-
}
|
|
2714
2721
|
|
|
2715
2722
|
// src/front/hooks/useServerCommands.ts
|
|
2716
2723
|
import { useEffect as useEffect6, useRef as useRef7, useState as useState8 } from "react";
|
|
@@ -2719,21 +2726,21 @@ function serverCommandErrorMessage(body, fallback) {
|
|
|
2719
2726
|
if (body.error && typeof body.error === "object" && typeof body.error.message === "string") return body.error.message;
|
|
2720
2727
|
return fallback;
|
|
2721
2728
|
}
|
|
2722
|
-
function toSlashCommand(command,
|
|
2729
|
+
function toSlashCommand(command, identity, isCurrentIdentity, apiBaseUrl, requestHeaders, fetchImpl) {
|
|
2723
2730
|
return {
|
|
2724
2731
|
name: command.name,
|
|
2725
2732
|
description: command.description ?? "",
|
|
2726
2733
|
source: command.source,
|
|
2727
2734
|
...command.sourcePlugin ? { sourcePlugin: command.sourcePlugin } : {},
|
|
2728
2735
|
handler: async (args) => {
|
|
2736
|
+
if (!isCurrentIdentity(identity.key)) return;
|
|
2729
2737
|
const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
2730
|
-
const
|
|
2731
|
-
const url = `${base}/api/v1/agent/commands/execute?${params.toString()}`;
|
|
2738
|
+
const url = `${base}/api/v1/agents/${encodeURIComponent(identity.agentTypeId)}/commands/execute`;
|
|
2732
2739
|
try {
|
|
2733
2740
|
const res = await fetchImpl(url, {
|
|
2734
2741
|
method: "POST",
|
|
2735
2742
|
headers: { ...requestHeaders ?? {}, "content-type": "application/json" },
|
|
2736
|
-
body: JSON.stringify({ name: command.name, args })
|
|
2743
|
+
body: JSON.stringify({ requestId: createRequestId("command"), sessionId: identity.sessionId, name: command.name, args })
|
|
2737
2744
|
});
|
|
2738
2745
|
if (!res.ok) {
|
|
2739
2746
|
const body = await res.json().catch(() => ({}));
|
|
@@ -2754,6 +2761,7 @@ function toSlashCommand(command, getSessionId, apiBaseUrl, requestHeaders, fetch
|
|
|
2754
2761
|
};
|
|
2755
2762
|
}
|
|
2756
2763
|
function useServerCommands({
|
|
2764
|
+
agentTypeId,
|
|
2757
2765
|
registry,
|
|
2758
2766
|
requestHeaders,
|
|
2759
2767
|
sessionId,
|
|
@@ -2764,41 +2772,62 @@ function useServerCommands({
|
|
|
2764
2772
|
refreshKey = 0
|
|
2765
2773
|
}) {
|
|
2766
2774
|
const [stamp, setStamp] = useState8(0);
|
|
2767
|
-
const
|
|
2768
|
-
const
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2775
|
+
const registrationsRef = useRef7({ registry, identity: void 0, names: /* @__PURE__ */ new Set() });
|
|
2776
|
+
const canonicalSessionId = sessionId.trim() || void 0;
|
|
2777
|
+
const identityKey = canonicalSessionId ? `${agentTypeId}\0${canonicalSessionId}` : void 0;
|
|
2778
|
+
const identityKeyRef = useRef7(identityKey);
|
|
2779
|
+
identityKeyRef.current = identityKey;
|
|
2780
|
+
useEffect6(() => () => {
|
|
2781
|
+
const registrations = registrationsRef.current;
|
|
2782
|
+
for (const name of registrations.names) registrations.registry.unregister(name);
|
|
2783
|
+
registrationsRef.current = { registry: registrations.registry, identity: void 0, names: /* @__PURE__ */ new Set() };
|
|
2784
|
+
}, []);
|
|
2772
2785
|
useEffect6(() => {
|
|
2773
2786
|
const clearRegistered = () => {
|
|
2774
|
-
|
|
2775
|
-
for (const name of
|
|
2776
|
-
|
|
2777
|
-
|
|
2787
|
+
const registrations = registrationsRef.current;
|
|
2788
|
+
for (const name of registrations.names) registrations.registry.unregister(name);
|
|
2789
|
+
const changed = registrations.names.size > 0;
|
|
2790
|
+
registrationsRef.current = { registry, identity: void 0, names: /* @__PURE__ */ new Set() };
|
|
2791
|
+
return changed;
|
|
2778
2792
|
};
|
|
2779
|
-
if (
|
|
2793
|
+
if (registrationsRef.current.registry !== registry) {
|
|
2794
|
+
if (clearRegistered()) setStamp((n) => n + 1);
|
|
2795
|
+
}
|
|
2796
|
+
if (!enabled || !canonicalSessionId || !identityKey) {
|
|
2780
2797
|
if (clearRegistered()) setStamp((n) => n + 1);
|
|
2781
2798
|
return;
|
|
2782
2799
|
}
|
|
2800
|
+
if (registrationsRef.current.identity !== identityKey) {
|
|
2801
|
+
if (clearRegistered()) setStamp((n) => n + 1);
|
|
2802
|
+
}
|
|
2783
2803
|
let aborted = false;
|
|
2784
2804
|
const nextFetch = fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
2785
2805
|
const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
2786
|
-
const url = `${base}/api/v1/
|
|
2787
|
-
const headers =
|
|
2806
|
+
const url = `${base}/api/v1/agents/${encodeURIComponent(agentTypeId)}/commands?sessionId=${encodeURIComponent(canonicalSessionId)}`;
|
|
2807
|
+
const headers = withStorageScope(requestHeaders, storageScope);
|
|
2788
2808
|
nextFetch(url, { headers }).then(async (res) => {
|
|
2789
2809
|
if (!res.ok) throw new Error(`commands request failed (${res.status})`);
|
|
2790
2810
|
return await res.json();
|
|
2791
2811
|
}).then((payload) => {
|
|
2792
2812
|
if (aborted) return;
|
|
2793
2813
|
const removed = clearRegistered();
|
|
2814
|
+
const registeredNames = /* @__PURE__ */ new Set();
|
|
2794
2815
|
let added = false;
|
|
2795
2816
|
for (const serverCommand of payload.commands ?? []) {
|
|
2796
|
-
const command = toSlashCommand(
|
|
2817
|
+
const command = toSlashCommand(
|
|
2818
|
+
serverCommand,
|
|
2819
|
+
{ key: identityKey, sessionId: canonicalSessionId, agentTypeId },
|
|
2820
|
+
(key) => identityKeyRef.current === key,
|
|
2821
|
+
apiBaseUrl,
|
|
2822
|
+
headers,
|
|
2823
|
+
nextFetch
|
|
2824
|
+
);
|
|
2797
2825
|
if (registry.get(command.name)) continue;
|
|
2798
2826
|
registry.register(command);
|
|
2799
|
-
|
|
2827
|
+
registeredNames.add(command.name);
|
|
2800
2828
|
added = true;
|
|
2801
2829
|
}
|
|
2830
|
+
registrationsRef.current = { registry, identity: identityKey, names: registeredNames };
|
|
2802
2831
|
if (removed || added) setStamp((n) => n + 1);
|
|
2803
2832
|
}).catch(() => {
|
|
2804
2833
|
if (aborted) return;
|
|
@@ -2807,16 +2836,9 @@ function useServerCommands({
|
|
|
2807
2836
|
return () => {
|
|
2808
2837
|
aborted = true;
|
|
2809
2838
|
};
|
|
2810
|
-
}, [apiBaseUrl, enabled, fetchImpl, refreshKey, requestHeaders, registry, storageScope]);
|
|
2839
|
+
}, [agentTypeId, apiBaseUrl, canonicalSessionId, enabled, fetchImpl, identityKey, refreshKey, requestHeaders, registry, storageScope]);
|
|
2811
2840
|
return stamp;
|
|
2812
2841
|
}
|
|
2813
|
-
function scopedHeaders3(headers, storageScope) {
|
|
2814
|
-
if (!headers && !storageScope) return void 0;
|
|
2815
|
-
const result = { ...headers ?? {} };
|
|
2816
|
-
const hasScope = Object.keys(result).some((k) => k.toLowerCase() === "x-boring-storage-scope");
|
|
2817
|
-
if (storageScope && !hasScope) result["x-boring-storage-scope"] = storageScope;
|
|
2818
|
-
return result;
|
|
2819
|
-
}
|
|
2820
2842
|
|
|
2821
2843
|
// src/front/hooks/useAttachmentNotice.ts
|
|
2822
2844
|
import { useEffect as useEffect7, useState as useState9 } from "react";
|
|
@@ -3788,7 +3810,8 @@ function syncCursor(state, cursor) {
|
|
|
3788
3810
|
return { ...state, lastSeq: cursor, hydrated: true, needsResync: void 0 };
|
|
3789
3811
|
}
|
|
3790
3812
|
function hydrateFromSnapshot(state, snapshot, options = {}) {
|
|
3791
|
-
|
|
3813
|
+
const hasUnhydratedLiveEvents = state.turnId !== void 0 || state.streamingMessage !== void 0 || state.committedMessages.length > 0 || state.status === "streaming" || state.status === "aborting";
|
|
3814
|
+
if (snapshot.seq < state.lastSeq && !options.allowSeqRewind && (state.hydrated || hasUnhydratedLiveEvents)) return state;
|
|
3792
3815
|
const queue = { followUps: enrichQueueWithKnownMetadata(snapshot.queue.followUps, state.optimisticOutbox, state.queue.followUps) };
|
|
3793
3816
|
const snapshotMessages = normalizeSnapshotMessages(snapshot.messages);
|
|
3794
3817
|
const preserveLocalHistory = shouldPreserveLocalCommittedHistory(state, snapshot, snapshotMessages, options);
|
|
@@ -4451,11 +4474,12 @@ function parsePiChatReplayRangeError(status, body) {
|
|
|
4451
4474
|
}
|
|
4452
4475
|
function buildPiChatEventsUrl({
|
|
4453
4476
|
apiBaseUrl = "",
|
|
4477
|
+
agentTypeId,
|
|
4454
4478
|
sessionId,
|
|
4455
4479
|
cursor
|
|
4456
4480
|
}) {
|
|
4457
4481
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
4458
|
-
return `${base}/api/v1/
|
|
4482
|
+
return `${base}/api/v1/agents/${encodeURIComponent(agentTypeId)}/sessions/${encodeURIComponent(sessionId)}/events?cursor=${encodeURIComponent(String(cursor))}`;
|
|
4459
4483
|
}
|
|
4460
4484
|
function calculateJitteredBackoffDelayMs({
|
|
4461
4485
|
attempt,
|
|
@@ -4532,6 +4556,7 @@ var RemotePiSession = class {
|
|
|
4532
4556
|
streamRunId = 0;
|
|
4533
4557
|
reconnectAttempt = 0;
|
|
4534
4558
|
started = false;
|
|
4559
|
+
suspended = false;
|
|
4535
4560
|
disposed = false;
|
|
4536
4561
|
streamAbortController;
|
|
4537
4562
|
reconnectTimer;
|
|
@@ -4565,6 +4590,7 @@ var RemotePiSession = class {
|
|
|
4565
4590
|
streamingMessageCount: state.streamingMessage ? 1 : 0
|
|
4566
4591
|
},
|
|
4567
4592
|
disposed: this.disposed,
|
|
4593
|
+
suspended: this.suspended,
|
|
4568
4594
|
generation: this.generation,
|
|
4569
4595
|
streamRunId: this.streamRunId,
|
|
4570
4596
|
reconnectAttempt: this.reconnectAttempt,
|
|
@@ -4576,7 +4602,7 @@ var RemotePiSession = class {
|
|
|
4576
4602
|
return this.store.subscribe(listener);
|
|
4577
4603
|
}
|
|
4578
4604
|
start(cursor) {
|
|
4579
|
-
if (this.disposed || this.started) return Promise.resolve();
|
|
4605
|
+
if (this.disposed || this.suspended || this.started) return Promise.resolve();
|
|
4580
4606
|
this.started = true;
|
|
4581
4607
|
const generation = this.generation;
|
|
4582
4608
|
if (cursor === void 0) {
|
|
@@ -4591,8 +4617,8 @@ var RemotePiSession = class {
|
|
|
4591
4617
|
if (!this.disposed) {
|
|
4592
4618
|
this.store.dispatch({ type: "optimistic-user-message", message: toOptimisticUserMessage(payload) }, { flush: true });
|
|
4593
4619
|
}
|
|
4594
|
-
if (!this.started) await this.start(this.store.getState().lastSeq);
|
|
4595
|
-
else this.ensureReconnectScheduled();
|
|
4620
|
+
if (!this.started && !this.suspended) await this.start(this.store.getState().lastSeq);
|
|
4621
|
+
else if (!this.suspended) this.ensureReconnectScheduled();
|
|
4596
4622
|
try {
|
|
4597
4623
|
const receipt = await this.postCommand("/prompt", payload, PromptReceiptSchema);
|
|
4598
4624
|
return receipt;
|
|
@@ -4605,8 +4631,8 @@ var RemotePiSession = class {
|
|
|
4605
4631
|
if (!this.disposed) {
|
|
4606
4632
|
this.store.dispatch({ type: "optimistic-user-message", message: toOptimisticUserMessage(payload) }, { flush: true });
|
|
4607
4633
|
}
|
|
4608
|
-
if (!this.started) await this.start(this.store.getState().lastSeq);
|
|
4609
|
-
else this.ensureReconnectScheduled();
|
|
4634
|
+
if (!this.started && !this.suspended) await this.start(this.store.getState().lastSeq);
|
|
4635
|
+
else if (!this.suspended) this.ensureReconnectScheduled();
|
|
4610
4636
|
try {
|
|
4611
4637
|
const receipt = await this.postCommand("/followup", payload, FollowUpReceiptSchema);
|
|
4612
4638
|
return receipt;
|
|
@@ -4632,6 +4658,28 @@ var RemotePiSession = class {
|
|
|
4632
4658
|
}
|
|
4633
4659
|
return receipt;
|
|
4634
4660
|
}
|
|
4661
|
+
suspendStream() {
|
|
4662
|
+
if (this.disposed || this.suspended) return;
|
|
4663
|
+
this.suspended = true;
|
|
4664
|
+
this.started = false;
|
|
4665
|
+
this.streamRunId += 1;
|
|
4666
|
+
this.clearReconnectTimer();
|
|
4667
|
+
this.abortEventStream();
|
|
4668
|
+
this.store.dispatch({ type: "connection-state", state: "suspended" }, { flush: true });
|
|
4669
|
+
}
|
|
4670
|
+
resumeStream() {
|
|
4671
|
+
if (this.disposed || !this.suspended && this.started) return;
|
|
4672
|
+
this.suspended = false;
|
|
4673
|
+
this.started = true;
|
|
4674
|
+
const generation = this.generation;
|
|
4675
|
+
const state = this.store.getState();
|
|
4676
|
+
if (!state.hydrated) {
|
|
4677
|
+
void this.hydrateAndConnect(generation);
|
|
4678
|
+
return;
|
|
4679
|
+
}
|
|
4680
|
+
this.store.dispatch({ type: "connection-state", state: "connecting" }, { flush: true });
|
|
4681
|
+
void this.connectEvents(state.lastSeq, generation);
|
|
4682
|
+
}
|
|
4635
4683
|
dispose() {
|
|
4636
4684
|
if (this.disposed) return;
|
|
4637
4685
|
this.disposed = true;
|
|
@@ -4645,33 +4693,34 @@ var RemotePiSession = class {
|
|
|
4645
4693
|
this.store.dispose();
|
|
4646
4694
|
}
|
|
4647
4695
|
async hydrateAndConnect(generation, options = {}) {
|
|
4648
|
-
|
|
4696
|
+
const hydrationRunId = this.streamRunId;
|
|
4697
|
+
if (!this.isHydrationActive(generation, hydrationRunId)) return;
|
|
4649
4698
|
this.clearReconnectTimer();
|
|
4650
4699
|
this.abortEventStream();
|
|
4651
4700
|
this.store.dispatch({ type: "connection-state", state: this.store.getState().hydrated ? "reconnecting" : "connecting" }, { flush: true });
|
|
4652
4701
|
try {
|
|
4653
4702
|
const headers = await this.requestHeaders();
|
|
4654
|
-
if (!this.
|
|
4703
|
+
if (!this.isHydrationActive(generation, hydrationRunId)) return;
|
|
4655
4704
|
const responseBody = await this.fetchJson(this.stateUrl(), { method: "GET", headers });
|
|
4656
|
-
if (!this.
|
|
4657
|
-
const raw =
|
|
4705
|
+
if (!this.isHydrationActive(generation, hydrationRunId)) return;
|
|
4706
|
+
const raw = addressedSnapshot(responseBody);
|
|
4658
4707
|
this.recordLargeStateWarning(raw);
|
|
4659
4708
|
if (readProtocolVersion(raw) !== SUPPORTED_PROTOCOL_VERSION) {
|
|
4660
4709
|
this.dispatchProtocolError(`Unsupported Pi chat protocol version: ${String(readProtocolVersion(raw) ?? "missing")}`);
|
|
4661
4710
|
return;
|
|
4662
4711
|
}
|
|
4663
4712
|
const snapshot = PiChatSnapshotSchema.parse(raw);
|
|
4664
|
-
if (!this.
|
|
4713
|
+
if (!this.isHydrationActive(generation, hydrationRunId)) return;
|
|
4665
4714
|
this.store.dispatch({ type: "hydrate", snapshot, allowSeqRewind: options?.allowSeqRewind }, { flush: true });
|
|
4666
4715
|
this.connectEvents(snapshot.seq, generation);
|
|
4667
4716
|
} catch (error) {
|
|
4668
|
-
if (!this.
|
|
4717
|
+
if (!this.isHydrationActive(generation, hydrationRunId) || isAbortError(error)) return;
|
|
4669
4718
|
this.dispatchProtocolError(errorMessage(error, "Failed to hydrate Pi chat session state."));
|
|
4670
4719
|
this.scheduleReconnect(generation);
|
|
4671
4720
|
}
|
|
4672
4721
|
}
|
|
4673
4722
|
connectEvents(cursor, generation) {
|
|
4674
|
-
if (!this.
|
|
4723
|
+
if (!this.isStreamLifecycleActive(generation)) return Promise.resolve();
|
|
4675
4724
|
this.clearReconnectTimer();
|
|
4676
4725
|
this.abortEventStream();
|
|
4677
4726
|
const runId = ++this.streamRunId;
|
|
@@ -4785,20 +4834,20 @@ var RemotePiSession = class {
|
|
|
4785
4834
|
}
|
|
4786
4835
|
}
|
|
4787
4836
|
rehydrateAfterStreamReset(generation, options = {}) {
|
|
4788
|
-
if (!this.
|
|
4837
|
+
if (!this.isStreamLifecycleActive(generation)) return;
|
|
4789
4838
|
this.streamRunId += 1;
|
|
4790
4839
|
this.abortEventStream();
|
|
4791
4840
|
void this.hydrateAndConnect(generation, options);
|
|
4792
4841
|
}
|
|
4793
4842
|
ensureReconnectScheduled() {
|
|
4794
|
-
if (!this.
|
|
4843
|
+
if (!this.isStreamLifecycleActive(this.generation)) return;
|
|
4795
4844
|
const state = this.store.getState();
|
|
4796
4845
|
if (state.connection.state === "connected" || state.connection.state === "connecting") return;
|
|
4797
4846
|
if (this.reconnectTimer !== void 0) return;
|
|
4798
4847
|
void this.hydrateAndConnect(this.generation);
|
|
4799
4848
|
}
|
|
4800
4849
|
scheduleReconnect(generation) {
|
|
4801
|
-
if (!this.
|
|
4850
|
+
if (!this.isStreamLifecycleActive(generation)) return;
|
|
4802
4851
|
this.clearReconnectTimer();
|
|
4803
4852
|
this.store.dispatch({ type: "connection-state", state: "reconnecting" }, { flush: true });
|
|
4804
4853
|
const attempt = this.reconnectAttempt++;
|
|
@@ -4812,7 +4861,7 @@ var RemotePiSession = class {
|
|
|
4812
4861
|
clearTimeoutFn: this.clearTimeoutFn,
|
|
4813
4862
|
reconnect: () => {
|
|
4814
4863
|
this.reconnectTimer = void 0;
|
|
4815
|
-
if (!this.
|
|
4864
|
+
if (!this.isStreamLifecycleActive(generation)) return;
|
|
4816
4865
|
const state = this.store.getState();
|
|
4817
4866
|
if (state.hydrated) this.connectEvents(state.lastSeq, generation);
|
|
4818
4867
|
else void this.hydrateAndConnect(generation);
|
|
@@ -4835,7 +4884,7 @@ var RemotePiSession = class {
|
|
|
4835
4884
|
return schema.parse(this.addressedCommandReceipt(path, raw));
|
|
4836
4885
|
}
|
|
4837
4886
|
addressedCommandReceipt(path, receipt) {
|
|
4838
|
-
if (
|
|
4887
|
+
if (path !== "/followup" || typeof receipt !== "object" || receipt === null) {
|
|
4839
4888
|
return receipt;
|
|
4840
4889
|
}
|
|
4841
4890
|
const record = receipt;
|
|
@@ -4881,19 +4930,18 @@ var RemotePiSession = class {
|
|
|
4881
4930
|
return this.sessionUrl("/state");
|
|
4882
4931
|
}
|
|
4883
4932
|
eventsUrl(cursor) {
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4933
|
+
return buildPiChatEventsUrl({
|
|
4934
|
+
apiBaseUrl: this.apiBaseUrl,
|
|
4935
|
+
agentTypeId: this.options.agentTypeId,
|
|
4936
|
+
sessionId: this.options.sessionId,
|
|
4937
|
+
cursor
|
|
4938
|
+
});
|
|
4888
4939
|
}
|
|
4889
4940
|
sessionUrl(path) {
|
|
4890
|
-
|
|
4891
|
-
return `${this.apiBaseUrl}/api/v1/agents/${encodeURIComponent(this.options.agentTypeId)}/sessions/${encodeURIComponent(this.options.sessionId)}${path}`;
|
|
4892
|
-
}
|
|
4893
|
-
return `${this.apiBaseUrl}/api/v1/agent/pi-chat/${encodeURIComponent(this.options.sessionId)}${path}`;
|
|
4941
|
+
return `${this.apiBaseUrl}/api/v1/agents/${encodeURIComponent(this.options.agentTypeId)}/sessions/${encodeURIComponent(this.options.sessionId)}${path}`;
|
|
4894
4942
|
}
|
|
4895
4943
|
addressedCommandPayload(path, payload) {
|
|
4896
|
-
if (
|
|
4944
|
+
if (typeof payload !== "object" || payload === null) return payload;
|
|
4897
4945
|
const record = payload;
|
|
4898
4946
|
if (typeof record.requestId === "string" && record.requestId.length > 0) return payload;
|
|
4899
4947
|
if (path === "/prompt" && typeof record.clientNonce === "string" && typeof record.message === "string") {
|
|
@@ -4956,8 +5004,14 @@ var RemotePiSession = class {
|
|
|
4956
5004
|
isGenerationActive(generation) {
|
|
4957
5005
|
return !this.disposed && generation === this.generation;
|
|
4958
5006
|
}
|
|
5007
|
+
isStreamLifecycleActive(generation) {
|
|
5008
|
+
return !this.suspended && this.isGenerationActive(generation);
|
|
5009
|
+
}
|
|
5010
|
+
isHydrationActive(generation, runId) {
|
|
5011
|
+
return this.isStreamLifecycleActive(generation) && runId === this.streamRunId;
|
|
5012
|
+
}
|
|
4959
5013
|
isStreamActive(generation, runId) {
|
|
4960
|
-
return this.
|
|
5014
|
+
return this.isStreamLifecycleActive(generation) && runId === this.streamRunId;
|
|
4961
5015
|
}
|
|
4962
5016
|
};
|
|
4963
5017
|
function addressedSnapshot(value) {
|
|
@@ -5084,13 +5138,23 @@ function clearPageUnloading() {
|
|
|
5084
5138
|
pageUnloading = false;
|
|
5085
5139
|
}
|
|
5086
5140
|
|
|
5087
|
-
// src/front/chat/session/
|
|
5141
|
+
// src/front/chat/session/sessionSelectionStorage.ts
|
|
5088
5142
|
var ACTIVE_SESSION_KEY_PREFIX = "boring-agent:v2";
|
|
5089
5143
|
var ACTIVE_SESSION_KEY_SUFFIX = "activeSessionId";
|
|
5144
|
+
var BOOT_RESUME_SESSION_KEY_SUFFIX = "bootResumeSessionId";
|
|
5090
5145
|
var DEFAULT_STORAGE_SCOPE2 = "default";
|
|
5091
5146
|
function activeSessionStorageKey(storageScope) {
|
|
5092
|
-
|
|
5093
|
-
|
|
5147
|
+
return scopedSessionStorageKey(storageScope, ACTIVE_SESSION_KEY_SUFFIX);
|
|
5148
|
+
}
|
|
5149
|
+
function bootResumeSessionStorageKey(source) {
|
|
5150
|
+
const scope = typeof source === "object" && source !== null ? encodeURIComponent(JSON.stringify([
|
|
5151
|
+
source.apiBaseUrl?.replace(/\/$/, "") ?? "",
|
|
5152
|
+
source.sessionsApiPath,
|
|
5153
|
+
source.agentTypeId ?? "",
|
|
5154
|
+
source.workspaceId ?? "",
|
|
5155
|
+
source.storageScope ?? DEFAULT_STORAGE_SCOPE2
|
|
5156
|
+
])) : source;
|
|
5157
|
+
return scopedSessionStorageKey(scope, BOOT_RESUME_SESSION_KEY_SUFFIX);
|
|
5094
5158
|
}
|
|
5095
5159
|
function readActiveSessionId(options = {}) {
|
|
5096
5160
|
const storage = resolveStorage2(options.storage);
|
|
@@ -5111,8 +5175,28 @@ function writeActiveSessionId(sessionId, options = {}) {
|
|
|
5111
5175
|
} catch {
|
|
5112
5176
|
}
|
|
5113
5177
|
}
|
|
5114
|
-
function
|
|
5115
|
-
|
|
5178
|
+
function readBootResumeSessionId(options = {}) {
|
|
5179
|
+
const storage = resolveBootStorage(options.storage);
|
|
5180
|
+
if (!storage) return void 0;
|
|
5181
|
+
try {
|
|
5182
|
+
return storage.getItem(bootResumeSessionStorageKey(options.bootResumeSource ?? options.storageScope)) ?? void 0;
|
|
5183
|
+
} catch {
|
|
5184
|
+
return void 0;
|
|
5185
|
+
}
|
|
5186
|
+
}
|
|
5187
|
+
function writeBootResumeSessionId(sessionId, options = {}) {
|
|
5188
|
+
const storage = resolveBootStorage(options.storage);
|
|
5189
|
+
if (!storage) return;
|
|
5190
|
+
try {
|
|
5191
|
+
const key = bootResumeSessionStorageKey(options.bootResumeSource ?? options.storageScope);
|
|
5192
|
+
if (sessionId === void 0 || sessionId.length === 0) storage.removeItem(key);
|
|
5193
|
+
else storage.setItem(key, sessionId);
|
|
5194
|
+
} catch {
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
function scopedSessionStorageKey(storageScope, suffix) {
|
|
5198
|
+
const scope = storageScope && storageScope.length > 0 ? storageScope : DEFAULT_STORAGE_SCOPE2;
|
|
5199
|
+
return `${ACTIVE_SESSION_KEY_PREFIX}:${scope}:${suffix}`;
|
|
5116
5200
|
}
|
|
5117
5201
|
function resolveStorage2(storage) {
|
|
5118
5202
|
if (storage) return storage;
|
|
@@ -5122,29 +5206,43 @@ function resolveStorage2(storage) {
|
|
|
5122
5206
|
return void 0;
|
|
5123
5207
|
}
|
|
5124
5208
|
}
|
|
5209
|
+
function resolveBootStorage(storage) {
|
|
5210
|
+
if (storage) return storage;
|
|
5211
|
+
try {
|
|
5212
|
+
return globalThis.sessionStorage;
|
|
5213
|
+
} catch {
|
|
5214
|
+
return void 0;
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5125
5217
|
|
|
5126
5218
|
// src/front/chat/session/usePiSessions.ts
|
|
5127
|
-
import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef8, useState as useState10 } from "react";
|
|
5128
|
-
var DEFAULT_SESSIONS_API_PATH = "/api/v1/agent/pi-chat/sessions";
|
|
5219
|
+
import { useCallback as useCallback7, useEffect as useEffect8, useLayoutEffect, useMemo as useMemo4, useRef as useRef8, useState as useState10 } from "react";
|
|
5129
5220
|
var SESSION_PAGE_SIZE = 50;
|
|
5130
5221
|
var DEFAULT_MAX_RETRIES = 60;
|
|
5131
5222
|
var DEFAULT_RETRY_BASE_MS = 250;
|
|
5132
5223
|
var DEFAULT_RETRY_MAX_MS = 2e3;
|
|
5224
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect8 : useLayoutEffect;
|
|
5133
5225
|
var SessionsPreparingError = class extends Error {
|
|
5134
5226
|
constructor() {
|
|
5135
5227
|
super("Agent runtime is still preparing");
|
|
5136
5228
|
this.name = "SessionsPreparingError";
|
|
5137
5229
|
}
|
|
5138
5230
|
};
|
|
5231
|
+
var StaleSessionsSourceError = class extends Error {
|
|
5232
|
+
constructor() {
|
|
5233
|
+
super("Pi sessions source is stale");
|
|
5234
|
+
this.name = "StaleSessionsSourceError";
|
|
5235
|
+
}
|
|
5236
|
+
};
|
|
5139
5237
|
function isNetworkFetchError(error) {
|
|
5140
5238
|
return error instanceof TypeError;
|
|
5141
5239
|
}
|
|
5142
|
-
function usePiSessions(options
|
|
5240
|
+
function usePiSessions(options) {
|
|
5143
5241
|
const enabled = options.enabled ?? true;
|
|
5144
5242
|
const apiBaseUrl = options.apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
5145
|
-
const
|
|
5146
|
-
const sessionsApiPath = options.sessionsApiPath ?? (addressed ? `/api/v1/agents/${encodeURIComponent(options.agentTypeId)}/sessions` : DEFAULT_SESSIONS_API_PATH);
|
|
5243
|
+
const sessionsApiPath = options.sessionsApiPath ?? `/api/v1/agents/${encodeURIComponent(options.agentTypeId)}/sessions`;
|
|
5147
5244
|
const storageScope = options.storageScope ?? "default";
|
|
5245
|
+
const activeSessionStorageScope = options.activeSessionStorageScope ?? storageScope;
|
|
5148
5246
|
const fetchImpl = useMemo4(() => options.fetch ?? globalThis.fetch.bind(globalThis), [options.fetch]);
|
|
5149
5247
|
const createRemoteSession = options.createRemoteSession ?? createRemotePiSession;
|
|
5150
5248
|
const connectActiveSession = options.connectActiveSession ?? true;
|
|
@@ -5152,12 +5250,30 @@ function usePiSessions(options = {}) {
|
|
|
5152
5250
|
const retryBaseMs = options.retry?.baseMs ?? DEFAULT_RETRY_BASE_MS;
|
|
5153
5251
|
const retryMaxMs = options.retry?.maxMs ?? DEFAULT_RETRY_MAX_MS;
|
|
5154
5252
|
const headersKey = useMemo4(() => headersScopeKey(options.requestHeaders, storageScope), [options.requestHeaders, storageScope]);
|
|
5155
|
-
const normalizedHeaders = useMemo4(() =>
|
|
5156
|
-
const requestScopeKey = useMemo4(
|
|
5157
|
-
|
|
5253
|
+
const normalizedHeaders = useMemo4(() => withStorageScope(options.requestHeaders, storageScope) ?? {}, [headersKey, storageScope]);
|
|
5254
|
+
const requestScopeKey = useMemo4(
|
|
5255
|
+
() => requestScopeIdentity(apiBaseUrl, sessionsApiPath, options.agentTypeId, storageScope, headersKey, options.workspaceId, options.sourceIdentity),
|
|
5256
|
+
[apiBaseUrl, headersKey, options.agentTypeId, options.sourceIdentity, options.workspaceId, sessionsApiPath, storageScope]
|
|
5257
|
+
);
|
|
5258
|
+
const dataSourceKey = useMemo4(
|
|
5259
|
+
() => dataSourceIdentity(apiBaseUrl, sessionsApiPath, options.agentTypeId, storageScope, options.workspaceId, options.sourceIdentity),
|
|
5260
|
+
[apiBaseUrl, options.agentTypeId, options.sourceIdentity, options.workspaceId, sessionsApiPath, storageScope]
|
|
5261
|
+
);
|
|
5262
|
+
const bootResumeSource = useMemo4(() => ({
|
|
5263
|
+
apiBaseUrl,
|
|
5264
|
+
sessionsApiPath,
|
|
5265
|
+
agentTypeId: options.agentTypeId,
|
|
5266
|
+
workspaceId: options.workspaceId,
|
|
5267
|
+
storageScope
|
|
5268
|
+
}), [apiBaseUrl, options.agentTypeId, options.workspaceId, sessionsApiPath, storageScope]);
|
|
5158
5269
|
const [sessions, setSessions] = useState10([]);
|
|
5159
5270
|
const [dataStorageScope, setDataStorageScope] = useState10(storageScope);
|
|
5160
|
-
const [activeSessionId, setActiveSessionId] = useState10(
|
|
5271
|
+
const [activeSessionId, setActiveSessionId] = useState10(void 0);
|
|
5272
|
+
const [resumeSessionState, setResumeSessionState] = useState10(() => ({
|
|
5273
|
+
dataSourceKey,
|
|
5274
|
+
id: readBootResumeSessionId({ bootResumeSource, storage: options.bootResumeStorage })
|
|
5275
|
+
}));
|
|
5276
|
+
const resumeSessionId = resumeSessionState.dataSourceKey === dataSourceKey ? resumeSessionState.id : void 0;
|
|
5161
5277
|
const [activePiSession, setActivePiSession] = useState10(void 0);
|
|
5162
5278
|
const [loading, setLoading] = useState10(enabled);
|
|
5163
5279
|
const [loadingMore, setLoadingMore] = useState10(false);
|
|
@@ -5174,17 +5290,30 @@ function usePiSessions(options = {}) {
|
|
|
5174
5290
|
const loadMoreRequestSeqRef = useRef8(0);
|
|
5175
5291
|
const loadMoreInFlightRef = useRef8(false);
|
|
5176
5292
|
const pendingCreatedRef = useRef8(/* @__PURE__ */ new Map());
|
|
5293
|
+
const pendingDeletedRef = useRef8(/* @__PURE__ */ new Set());
|
|
5177
5294
|
const pendingCreatedScopeRef = useRef8(requestScopeKey);
|
|
5178
5295
|
const dataStorageScopeRef = useRef8(storageScope);
|
|
5179
5296
|
const loadedDataSourceRef = useRef8(dataSourceKey);
|
|
5180
|
-
const
|
|
5181
|
-
|
|
5297
|
+
const committedRequestScopeRef = useRef8(requestScopeKey);
|
|
5298
|
+
useIsomorphicLayoutEffect(() => {
|
|
5299
|
+
committedRequestScopeRef.current = requestScopeKey;
|
|
5300
|
+
}, [requestScopeKey]);
|
|
5301
|
+
const sourceIsCurrent = useCallback7(
|
|
5302
|
+
(scope) => mountedRef.current && committedRequestScopeRef.current === scope,
|
|
5303
|
+
[]
|
|
5304
|
+
);
|
|
5182
5305
|
const remoteSessionOptionsRef = useRef8(options.remoteSessionOptions);
|
|
5183
5306
|
remoteSessionOptionsRef.current = options.remoteSessionOptions;
|
|
5184
5307
|
const remoteSessionOptionsKey = useMemo4(
|
|
5185
5308
|
() => remoteSessionOptionsIdentity(options.remoteSessionOptions),
|
|
5186
5309
|
[options.remoteSessionOptions]
|
|
5187
5310
|
);
|
|
5311
|
+
useIsomorphicLayoutEffect(() => {
|
|
5312
|
+
setResumeSessionState((current) => current.dataSourceKey === dataSourceKey ? current : {
|
|
5313
|
+
dataSourceKey,
|
|
5314
|
+
id: readBootResumeSessionId({ bootResumeSource, storage: options.bootResumeStorage })
|
|
5315
|
+
});
|
|
5316
|
+
}, [bootResumeSource, dataSourceKey, options.bootResumeStorage]);
|
|
5188
5317
|
useEffect8(() => {
|
|
5189
5318
|
sessionsRef.current = sessions;
|
|
5190
5319
|
}, [sessions]);
|
|
@@ -5197,48 +5326,66 @@ function usePiSessions(options = {}) {
|
|
|
5197
5326
|
const activeSessionKnown = Boolean(activeSessionId && sessions.some((session) => session.id === activeSessionId));
|
|
5198
5327
|
const requestHeaders = useCallback7(() => normalizedHeaders, [normalizedHeaders]);
|
|
5199
5328
|
const sessionsUrl = useCallback7((suffix = "") => `${apiBaseUrl}${sessionsApiPath}${suffix}`, [apiBaseUrl, sessionsApiPath]);
|
|
5200
|
-
const sessionsListUrl = useCallback7((
|
|
5201
|
-
const query = new URLSearchParams();
|
|
5202
|
-
if (
|
|
5203
|
-
query.set("limit", String(SESSION_PAGE_SIZE));
|
|
5204
|
-
if (cursor) query.set("cursor", cursor);
|
|
5205
|
-
} else {
|
|
5206
|
-
if (offset > 0) {
|
|
5207
|
-
query.set("limit", String(SESSION_PAGE_SIZE));
|
|
5208
|
-
query.set("offset", String(offset));
|
|
5209
|
-
}
|
|
5210
|
-
if (offset <= 0 && includeId) query.set("activeSessionId", includeId);
|
|
5211
|
-
}
|
|
5212
|
-
if (query.size === 0) return sessionsUrl();
|
|
5329
|
+
const sessionsListUrl = useCallback7((cursor) => {
|
|
5330
|
+
const query = new URLSearchParams({ limit: String(SESSION_PAGE_SIZE) });
|
|
5331
|
+
if (cursor) query.set("cursor", cursor);
|
|
5213
5332
|
return sessionsUrl(`?${query.toString()}`);
|
|
5214
|
-
}, [
|
|
5333
|
+
}, [sessionsUrl]);
|
|
5215
5334
|
const persistActive = useCallback7((id) => {
|
|
5216
|
-
writeActiveSessionId(id, { storageScope, storage: options.storage });
|
|
5217
|
-
}, [options.storage
|
|
5335
|
+
writeActiveSessionId(id, { storageScope: activeSessionStorageScope, storage: options.storage });
|
|
5336
|
+
}, [activeSessionStorageScope, options.storage]);
|
|
5337
|
+
const persistBootResume = useCallback7((id) => {
|
|
5338
|
+
writeBootResumeSessionId(id, { bootResumeSource, storage: options.bootResumeStorage });
|
|
5339
|
+
setResumeSessionState({ dataSourceKey, id });
|
|
5340
|
+
}, [bootResumeSource, dataSourceKey, options.bootResumeStorage]);
|
|
5341
|
+
const rememberConfirmedEmptySession = useCallback7((id) => {
|
|
5342
|
+
writeBootResumeSessionId(id, { bootResumeSource, storage: options.bootResumeStorage });
|
|
5343
|
+
setResumeSessionState({ dataSourceKey, id: void 0 });
|
|
5344
|
+
}, [bootResumeSource, dataSourceKey, options.bootResumeStorage]);
|
|
5218
5345
|
const ensurePendingScope = useCallback7(() => {
|
|
5219
5346
|
if (pendingCreatedScopeRef.current === requestScopeKey) return;
|
|
5220
5347
|
pendingCreatedScopeRef.current = requestScopeKey;
|
|
5221
5348
|
pendingCreatedRef.current.clear();
|
|
5349
|
+
pendingDeletedRef.current.clear();
|
|
5222
5350
|
}, [requestScopeKey]);
|
|
5223
5351
|
const preferredSessionId = useCallback7(() => {
|
|
5224
|
-
const persisted = options.initialActiveSessionId ?? readActiveSessionId({
|
|
5352
|
+
const persisted = options.initialActiveSessionId ?? readActiveSessionId({
|
|
5353
|
+
storageScope: activeSessionStorageScope,
|
|
5354
|
+
storage: options.storage
|
|
5355
|
+
});
|
|
5225
5356
|
if (loadedDataSourceRef.current === dataSourceKey) return activeSessionIdRef.current ?? persisted;
|
|
5226
5357
|
if (dataStorageScopeRef.current !== storageScope) return persisted;
|
|
5227
5358
|
return void 0;
|
|
5228
|
-
}, [dataSourceKey, options.initialActiveSessionId, options.storage, storageScope]);
|
|
5359
|
+
}, [activeSessionStorageScope, dataSourceKey, options.initialActiveSessionId, options.storage, storageScope]);
|
|
5229
5360
|
const applySessions = useCallback7((data, applyOptions = {}) => {
|
|
5230
5361
|
ensurePendingScope();
|
|
5231
5362
|
const replacingScope = loadedDataSourceRef.current !== dataSourceKey;
|
|
5232
5363
|
const requestedActiveId = preferredSessionId();
|
|
5233
5364
|
const replacingScopePreferred = replacingScope ? requestedActiveId : void 0;
|
|
5234
5365
|
const pendingCreated = pendingCreatedRef.current;
|
|
5366
|
+
const pendingDeleted = pendingDeletedRef.current;
|
|
5367
|
+
const deletedIds = new Set(pendingDeleted);
|
|
5235
5368
|
for (const session of data) pendingCreated.delete(session.id);
|
|
5236
|
-
const
|
|
5237
|
-
|
|
5369
|
+
const filteredData = data.filter((session) => !deletedIds.has(session.id));
|
|
5370
|
+
if (applyOptions.nextCursor === void 0) {
|
|
5371
|
+
const returnedIds = new Set(data.map((session) => session.id));
|
|
5372
|
+
for (const deletedId of pendingDeleted) {
|
|
5373
|
+
if (!returnedIds.has(deletedId)) pendingDeleted.delete(deletedId);
|
|
5374
|
+
}
|
|
5375
|
+
}
|
|
5376
|
+
const canonicalCount = canonicalPageCount(filteredData);
|
|
5377
|
+
const pageMayHaveMore = applyOptions.nextCursor !== void 0;
|
|
5238
5378
|
const wasExhaustedBeyondFirstPage = applyOptions.background && !hasMoreRef.current && canonicalLoadedCountRef.current >= canonicalCount;
|
|
5239
|
-
const requestedActiveReturned = Boolean(requestedActiveId &&
|
|
5379
|
+
const requestedActiveReturned = Boolean(requestedActiveId && filteredData.some((session) => session.id === requestedActiveId));
|
|
5240
5380
|
const current = applyOptions.background && pageMayHaveMore ? sessionsRef.current.filter((session) => !requestedActiveId || requestedActiveReturned || session.id !== requestedActiveId) : [];
|
|
5241
|
-
const merged = mergeSessions(Array.from(pendingCreated.values()),
|
|
5381
|
+
const merged = mergeSessions(Array.from(pendingCreated.values()), filteredData, current.filter((session) => !deletedIds.has(session.id)));
|
|
5382
|
+
const rememberedEmptyId = readBootResumeSessionId({
|
|
5383
|
+
bootResumeSource,
|
|
5384
|
+
storage: options.bootResumeStorage
|
|
5385
|
+
});
|
|
5386
|
+
if (rememberedEmptyId && filteredData.some((session) => session.id === rememberedEmptyId)) {
|
|
5387
|
+
persistBootResume(void 0);
|
|
5388
|
+
}
|
|
5242
5389
|
const nextHasMore = pageMayHaveMore && !wasExhaustedBeyondFirstPage;
|
|
5243
5390
|
canonicalLoadedCountRef.current = applyOptions.background ? Math.max(canonicalLoadedCountRef.current, canonicalCount) : canonicalCount;
|
|
5244
5391
|
loadedDataSourceRef.current = dataSourceKey;
|
|
@@ -5253,10 +5400,12 @@ function usePiSessions(options = {}) {
|
|
|
5253
5400
|
persistActive(next);
|
|
5254
5401
|
return next;
|
|
5255
5402
|
});
|
|
5256
|
-
}, [
|
|
5403
|
+
}, [bootResumeSource, dataSourceKey, ensurePendingScope, options.bootResumeStorage, persistActive, persistBootResume, preferredSessionId, storageScope]);
|
|
5257
5404
|
const refresh = useCallback7(async (refreshOptions = {}) => {
|
|
5405
|
+
const scope = requestScopeKey;
|
|
5406
|
+
if (mountedRef.current && !sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5258
5407
|
const version = ++refreshVersionRef.current;
|
|
5259
|
-
const isCurrent = () =>
|
|
5408
|
+
const isCurrent = () => sourceIsCurrent(scope) && version === refreshVersionRef.current;
|
|
5260
5409
|
clearRetryTimer(retryTimerRef);
|
|
5261
5410
|
const background = refreshOptions.background === true;
|
|
5262
5411
|
if (!enabled) {
|
|
@@ -5283,7 +5432,7 @@ function usePiSessions(options = {}) {
|
|
|
5283
5432
|
let page;
|
|
5284
5433
|
for (let attempt = 0; ; attempt += 1) {
|
|
5285
5434
|
try {
|
|
5286
|
-
page = await fetchSessionList(fetchImpl, sessionsListUrl(
|
|
5435
|
+
page = await fetchSessionList(fetchImpl, sessionsListUrl(), requestHeaders());
|
|
5287
5436
|
break;
|
|
5288
5437
|
} catch (err) {
|
|
5289
5438
|
const transient = err instanceof SessionsPreparingError || isNetworkFetchError(err);
|
|
@@ -5305,7 +5454,7 @@ function usePiSessions(options = {}) {
|
|
|
5305
5454
|
setLoading(false);
|
|
5306
5455
|
if (refreshOptions.throwOnError) throw error2;
|
|
5307
5456
|
}
|
|
5308
|
-
}, [
|
|
5457
|
+
}, [applySessions, enabled, fetchImpl, persistActive, preferredSessionId, requestHeaders, requestScopeKey, retryBaseMs, retryMaxMs, retryMaxRetries, sessionsListUrl, sourceIsCurrent]);
|
|
5309
5458
|
useEffect8(() => {
|
|
5310
5459
|
mountedRef.current = true;
|
|
5311
5460
|
void refresh();
|
|
@@ -5321,14 +5470,13 @@ function usePiSessions(options = {}) {
|
|
|
5321
5470
|
loadMoreInFlightRef.current = true;
|
|
5322
5471
|
const version = refreshVersionRef.current;
|
|
5323
5472
|
const scope = requestScopeKey;
|
|
5324
|
-
const offset = canonicalLoadedCountRef.current;
|
|
5325
5473
|
setLoadingMore(true);
|
|
5326
5474
|
try {
|
|
5327
|
-
const page = await fetchSessionList(fetchImpl, sessionsListUrl(
|
|
5328
|
-
const data = page.sessions;
|
|
5329
|
-
if (
|
|
5330
|
-
const merged = mergeSessions(sessionsRef.current, data);
|
|
5331
|
-
const nextHasMore =
|
|
5475
|
+
const page = await fetchSessionList(fetchImpl, sessionsListUrl(nextCursorRef.current), requestHeaders());
|
|
5476
|
+
const data = page.sessions.filter((session) => !pendingDeletedRef.current.has(session.id));
|
|
5477
|
+
if (requestSeq !== loadMoreRequestSeqRef.current || version !== refreshVersionRef.current || !sourceIsCurrent(scope)) return;
|
|
5478
|
+
const merged = mergeSessions(sessionsRef.current.filter((session) => !pendingDeletedRef.current.has(session.id)), data);
|
|
5479
|
+
const nextHasMore = page.nextCursor !== void 0;
|
|
5332
5480
|
canonicalLoadedCountRef.current += data.length;
|
|
5333
5481
|
nextCursorRef.current = page.nextCursor;
|
|
5334
5482
|
setSessions(merged);
|
|
@@ -5341,16 +5489,16 @@ function usePiSessions(options = {}) {
|
|
|
5341
5489
|
return next;
|
|
5342
5490
|
});
|
|
5343
5491
|
} catch (err) {
|
|
5344
|
-
if (
|
|
5492
|
+
if (requestSeq === loadMoreRequestSeqRef.current && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
|
|
5345
5493
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
5346
5494
|
}
|
|
5347
5495
|
} finally {
|
|
5348
5496
|
if (requestSeq === loadMoreRequestSeqRef.current) loadMoreInFlightRef.current = false;
|
|
5349
|
-
if (
|
|
5497
|
+
if (requestSeq === loadMoreRequestSeqRef.current && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
|
|
5350
5498
|
setLoadingMore(false);
|
|
5351
5499
|
}
|
|
5352
5500
|
}
|
|
5353
|
-
}, [
|
|
5501
|
+
}, [enabled, fetchImpl, hasMore, loading, loadingMore, persistActive, requestHeaders, requestScopeKey, sessionsListUrl, sourceIsCurrent]);
|
|
5354
5502
|
useEffect8(() => {
|
|
5355
5503
|
if (!enabled || !connectActiveSession || !activeSessionId || !activeSessionKnown) {
|
|
5356
5504
|
setActivePiSession(void 0);
|
|
@@ -5372,6 +5520,8 @@ function usePiSessions(options = {}) {
|
|
|
5372
5520
|
};
|
|
5373
5521
|
}, [activeSessionId, activeSessionKnown, apiBaseUrl, connectActiveSession, createRemoteSession, enabled, fetchImpl, remoteSessionOptionsKey, options.agentTypeId, options.workspaceId, requestHeaders, storageScope]);
|
|
5374
5522
|
const create = useCallback7(async (init) => {
|
|
5523
|
+
const scope = requestScopeKey;
|
|
5524
|
+
if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5375
5525
|
if (!enabled) throw new Error("Pi sessions are disabled");
|
|
5376
5526
|
const response = await fetchImpl(sessionsUrl(), {
|
|
5377
5527
|
method: "POST",
|
|
@@ -5384,8 +5534,10 @@ function usePiSessions(options = {}) {
|
|
|
5384
5534
|
throw err;
|
|
5385
5535
|
}
|
|
5386
5536
|
const body = await response.json();
|
|
5387
|
-
const session =
|
|
5537
|
+
const session = addressedCreatedSession(body, options.agentTypeId, init?.title);
|
|
5538
|
+
if (!sourceIsCurrent(scope)) return session;
|
|
5388
5539
|
ensurePendingScope();
|
|
5540
|
+
rememberConfirmedEmptySession(session.id);
|
|
5389
5541
|
pendingCreatedRef.current.set(session.id, session);
|
|
5390
5542
|
setDataStorageScope(storageScope);
|
|
5391
5543
|
setSessions((previous) => mergeSessions([session], previous));
|
|
@@ -5393,17 +5545,43 @@ function usePiSessions(options = {}) {
|
|
|
5393
5545
|
persistActive(session.id);
|
|
5394
5546
|
void refresh();
|
|
5395
5547
|
return session;
|
|
5396
|
-
}, [
|
|
5548
|
+
}, [enabled, ensurePendingScope, fetchImpl, persistActive, refresh, rememberConfirmedEmptySession, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
|
|
5397
5549
|
const switchSession = useCallback7((id) => {
|
|
5550
|
+
if (!sourceIsCurrent(requestScopeKey)) return;
|
|
5398
5551
|
const known = sessionsRef.current.some((session) => session.id === id);
|
|
5399
5552
|
const next = known ? id : sessionsRef.current[0]?.id;
|
|
5400
5553
|
setActiveSessionId(next);
|
|
5401
5554
|
persistActive(next);
|
|
5402
|
-
}, [persistActive]);
|
|
5555
|
+
}, [persistActive, requestScopeKey, sourceIsCurrent]);
|
|
5556
|
+
const rename = useCallback7(async (id, title) => {
|
|
5557
|
+
const scope = requestScopeKey;
|
|
5558
|
+
if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5559
|
+
if (!enabled) throw new Error("Pi sessions are disabled");
|
|
5560
|
+
const response = await fetchImpl(sessionsUrl(`/${encodeURIComponent(id)}/rename`), {
|
|
5561
|
+
method: "POST",
|
|
5562
|
+
headers: { ...requestHeaders(), "Content-Type": "application/json" },
|
|
5563
|
+
body: JSON.stringify({ requestId: createRequestId("rename"), title })
|
|
5564
|
+
});
|
|
5565
|
+
if (!response.ok) throw new Error(`Failed to rename session: ${response.status}`);
|
|
5566
|
+
const renamed = toAddressedSessionSummary(await response.json());
|
|
5567
|
+
if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5568
|
+
ensurePendingScope();
|
|
5569
|
+
if (pendingCreatedRef.current.has(id)) pendingCreatedRef.current.set(id, renamed);
|
|
5570
|
+
setSessions((current) => current.map((session) => session.id === id ? renamed : session));
|
|
5571
|
+
return renamed;
|
|
5572
|
+
}, [enabled, ensurePendingScope, fetchImpl, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent]);
|
|
5403
5573
|
const deleteSession = useCallback7(async (id) => {
|
|
5574
|
+
const scope = requestScopeKey;
|
|
5575
|
+
if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5404
5576
|
if (!enabled) throw new Error("Pi sessions are disabled");
|
|
5405
5577
|
ensurePendingScope();
|
|
5578
|
+
if (readBootResumeSessionId({ bootResumeSource, storage: options.bootResumeStorage }) === id) {
|
|
5579
|
+
persistBootResume(void 0);
|
|
5580
|
+
}
|
|
5581
|
+
const deletedSession = sessionsRef.current.find((session) => session.id === id);
|
|
5582
|
+
const deletedSessionWasActive = activeSessionIdRef.current === id;
|
|
5406
5583
|
pendingCreatedRef.current.delete(id);
|
|
5584
|
+
pendingDeletedRef.current.add(id);
|
|
5407
5585
|
setDataStorageScope(storageScope);
|
|
5408
5586
|
setSessions((previous) => previous.filter((session) => session.id !== id));
|
|
5409
5587
|
setActiveSessionId((previous) => {
|
|
@@ -5420,18 +5598,29 @@ function usePiSessions(options = {}) {
|
|
|
5420
5598
|
if (!response.ok && response.status !== 404) throw new Error(`Failed to delete session: ${response.status}`);
|
|
5421
5599
|
} catch (err) {
|
|
5422
5600
|
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
5423
|
-
|
|
5424
|
-
|
|
5601
|
+
if (sourceIsCurrent(scope)) {
|
|
5602
|
+
pendingDeletedRef.current.delete(id);
|
|
5603
|
+
if (deletedSession) {
|
|
5604
|
+
setSessions((previous) => mergeSessions([deletedSession], previous));
|
|
5605
|
+
if (deletedSessionWasActive) {
|
|
5606
|
+
setActiveSessionId(id);
|
|
5607
|
+
persistActive(id);
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
setError(error2);
|
|
5611
|
+
void refresh();
|
|
5612
|
+
}
|
|
5425
5613
|
throw error2;
|
|
5426
5614
|
}
|
|
5427
|
-
void refresh();
|
|
5428
|
-
}, [enabled, ensurePendingScope, fetchImpl, persistActive, refresh, requestHeaders, sessionsUrl, storageScope]);
|
|
5615
|
+
if (sourceIsCurrent(scope)) void refresh();
|
|
5616
|
+
}, [bootResumeSource, enabled, ensurePendingScope, fetchImpl, options.bootResumeStorage, persistActive, persistBootResume, refresh, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
|
|
5429
5617
|
const reset = useCallback7(() => {
|
|
5618
|
+
if (!sourceIsCurrent(requestScopeKey)) return;
|
|
5430
5619
|
pendingCreatedRef.current.clear();
|
|
5620
|
+
pendingDeletedRef.current.clear();
|
|
5431
5621
|
loadMoreRequestSeqRef.current += 1;
|
|
5432
5622
|
loadMoreInFlightRef.current = false;
|
|
5433
5623
|
canonicalLoadedCountRef.current = canonicalPageCount(sessionsRef.current);
|
|
5434
|
-
nextCursorRef.current = void 0;
|
|
5435
5624
|
loadedDataSourceRef.current = dataSourceKey;
|
|
5436
5625
|
dataStorageScopeRef.current = storageScope;
|
|
5437
5626
|
setDataStorageScope(storageScope);
|
|
@@ -5439,36 +5628,35 @@ function usePiSessions(options = {}) {
|
|
|
5439
5628
|
setActivePiSession(void 0);
|
|
5440
5629
|
setLoadingMore(false);
|
|
5441
5630
|
persistActive(void 0);
|
|
5442
|
-
}, [dataSourceKey, persistActive, storageScope]);
|
|
5631
|
+
}, [dataSourceKey, persistActive, requestScopeKey, sourceIsCurrent, storageScope]);
|
|
5443
5632
|
const visibleActiveSessionId = enabled ? activeSessionId : void 0;
|
|
5444
5633
|
const activeSession = enabled ? sessions.find((session) => session.id === visibleActiveSessionId) : void 0;
|
|
5445
5634
|
return {
|
|
5446
5635
|
sessions,
|
|
5447
5636
|
activeSession,
|
|
5448
5637
|
activeSessionId: visibleActiveSessionId,
|
|
5638
|
+
resumeSessionId,
|
|
5449
5639
|
activePiSession: visibleActiveSessionId ? activePiSession : void 0,
|
|
5450
5640
|
dataStorageScope,
|
|
5641
|
+
sourceIdentity: enabled && loadedDataSourceRef.current === dataSourceKey ? options.sourceIdentity : void 0,
|
|
5451
5642
|
loading: enabled ? loading : false,
|
|
5452
5643
|
loadingMore,
|
|
5453
5644
|
hasMore: enabled ? hasMore : false,
|
|
5454
5645
|
error,
|
|
5455
5646
|
refresh,
|
|
5456
5647
|
create,
|
|
5648
|
+
rename,
|
|
5457
5649
|
switch: switchSession,
|
|
5458
5650
|
delete: deleteSession,
|
|
5459
5651
|
loadMore,
|
|
5460
5652
|
reset
|
|
5461
5653
|
};
|
|
5462
5654
|
}
|
|
5463
|
-
async function fetchSessionList(fetchImpl, url, headers
|
|
5655
|
+
async function fetchSessionList(fetchImpl, url, headers) {
|
|
5464
5656
|
const response = await fetchImpl(url, Object.keys(headers).length > 0 ? { headers } : void 0);
|
|
5465
5657
|
if (response.status === 503) throw new SessionsPreparingError();
|
|
5466
5658
|
if (!response.ok) throw new Error(`Failed to load sessions: ${response.status}`);
|
|
5467
5659
|
const body = await response.json();
|
|
5468
|
-
if (!addressed) {
|
|
5469
|
-
if (!Array.isArray(body)) throw new Error("Failed to load sessions: invalid response");
|
|
5470
|
-
return { sessions: body.map(toSessionSummary) };
|
|
5471
|
-
}
|
|
5472
5660
|
if (typeof body !== "object" || body === null || !Array.isArray(body.sessions)) {
|
|
5473
5661
|
throw new Error("Failed to load sessions: invalid response");
|
|
5474
5662
|
}
|
|
@@ -5478,19 +5666,6 @@ async function fetchSessionList(fetchImpl, url, headers, addressed) {
|
|
|
5478
5666
|
...typeof page.nextCursor === "string" ? { nextCursor: page.nextCursor } : {}
|
|
5479
5667
|
};
|
|
5480
5668
|
}
|
|
5481
|
-
function toSessionSummary(value) {
|
|
5482
|
-
if (typeof value !== "object" || value === null) throw new Error("invalid session summary");
|
|
5483
|
-
const record = value;
|
|
5484
|
-
if (typeof record.id !== "string" || !record.id) throw new Error("invalid session id");
|
|
5485
|
-
const now = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
5486
|
-
return {
|
|
5487
|
-
id: record.id,
|
|
5488
|
-
title: typeof record.title === "string" ? record.title : "Untitled",
|
|
5489
|
-
createdAt: typeof record.createdAt === "string" ? record.createdAt : now,
|
|
5490
|
-
updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : now,
|
|
5491
|
-
turnCount: typeof record.turnCount === "number" ? record.turnCount : 0
|
|
5492
|
-
};
|
|
5493
|
-
}
|
|
5494
5669
|
function toAddressedSessionSummary(value) {
|
|
5495
5670
|
if (typeof value !== "object" || value === null) throw new Error("invalid addressed session summary");
|
|
5496
5671
|
const record = value;
|
|
@@ -5500,21 +5675,29 @@ function toAddressedSessionSummary(value) {
|
|
|
5500
5675
|
}
|
|
5501
5676
|
const createdAt = typeof record.createdAt === "number" ? new Date(record.createdAt).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
5502
5677
|
const updatedAt = typeof record.updatedAt === "number" ? new Date(record.updatedAt).toISOString() : createdAt;
|
|
5678
|
+
const agentTypeId = ref.agentTypeId;
|
|
5679
|
+
const status = record.status;
|
|
5680
|
+
const addressedStatus = status === "idle" || status === "running" || status === "aborting" || status === "error" ? status : void 0;
|
|
5503
5681
|
return {
|
|
5504
5682
|
id: ref.sessionId,
|
|
5505
5683
|
title: typeof record.title === "string" ? record.title : "Untitled",
|
|
5506
5684
|
createdAt,
|
|
5507
5685
|
updatedAt,
|
|
5508
|
-
turnCount: 0
|
|
5686
|
+
turnCount: typeof record.turnCount === "number" ? record.turnCount : 0,
|
|
5687
|
+
...typeof agentTypeId === "string" ? { agentTypeId } : {},
|
|
5688
|
+
...typeof record.nativeSessionId === "string" ? { nativeSessionId: record.nativeSessionId } : {},
|
|
5689
|
+
...typeof record.hasAssistantReply === "boolean" ? { hasAssistantReply: record.hasAssistantReply } : {},
|
|
5690
|
+
...addressedStatus ? { status: addressedStatus } : {}
|
|
5509
5691
|
};
|
|
5510
5692
|
}
|
|
5511
|
-
function addressedCreatedSession(value, title) {
|
|
5512
|
-
if (typeof value !== "object" || value === null || typeof value.sessionId !== "string") {
|
|
5693
|
+
function addressedCreatedSession(value, expectedAgentTypeId, title) {
|
|
5694
|
+
if (typeof value !== "object" || value === null || typeof value.sessionId !== "string" || value.agentTypeId !== expectedAgentTypeId) {
|
|
5513
5695
|
throw new Error("invalid addressed session ref");
|
|
5514
5696
|
}
|
|
5515
5697
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5516
5698
|
return {
|
|
5517
5699
|
id: value.sessionId,
|
|
5700
|
+
agentTypeId: expectedAgentTypeId,
|
|
5518
5701
|
title: title ?? "Untitled",
|
|
5519
5702
|
createdAt: now,
|
|
5520
5703
|
updatedAt: now,
|
|
@@ -5570,31 +5753,25 @@ function mergeSessions(...lists) {
|
|
|
5570
5753
|
}
|
|
5571
5754
|
return merged;
|
|
5572
5755
|
}
|
|
5573
|
-
function buildRequestHeaders(headers, storageScope) {
|
|
5574
|
-
const result = {};
|
|
5575
|
-
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
5576
|
-
if (typeof value === "string") result[key] = value;
|
|
5577
|
-
}
|
|
5578
|
-
if (storageScope && !hasHeader2(result, "x-boring-storage-scope")) result["x-boring-storage-scope"] = storageScope;
|
|
5579
|
-
return result;
|
|
5580
|
-
}
|
|
5581
5756
|
function headersScopeKey(headers, storageScope) {
|
|
5582
5757
|
return JSON.stringify({ storageScope, headers: Object.entries(headers ?? {}).sort(([a], [b]) => a.localeCompare(b)) });
|
|
5583
5758
|
}
|
|
5584
|
-
function requestScopeIdentity(apiBaseUrl, sessionsApiPath, storageScope, headersKey) {
|
|
5759
|
+
function requestScopeIdentity(apiBaseUrl, sessionsApiPath, agentTypeId, storageScope, headersKey, workspaceId, sourceIdentity) {
|
|
5585
5760
|
return `${apiBaseUrl}
|
|
5586
5761
|
${sessionsApiPath}
|
|
5762
|
+
${agentTypeId}
|
|
5587
5763
|
${storageScope}
|
|
5588
|
-
${headersKey}
|
|
5764
|
+
${headersKey}
|
|
5765
|
+
${workspaceId ?? ""}
|
|
5766
|
+
${sourceIdentity ?? ""}`;
|
|
5589
5767
|
}
|
|
5590
|
-
function dataSourceIdentity(apiBaseUrl, sessionsApiPath, storageScope) {
|
|
5768
|
+
function dataSourceIdentity(apiBaseUrl, sessionsApiPath, agentTypeId, storageScope, workspaceId, sourceIdentity) {
|
|
5591
5769
|
return `${apiBaseUrl}
|
|
5592
5770
|
${sessionsApiPath}
|
|
5593
|
-
${
|
|
5594
|
-
}
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
return Object.keys(headers).some((key) => key.toLowerCase() === lower);
|
|
5771
|
+
${agentTypeId}
|
|
5772
|
+
${storageScope}
|
|
5773
|
+
${workspaceId ?? ""}
|
|
5774
|
+
${sourceIdentity ?? ""}`;
|
|
5598
5775
|
}
|
|
5599
5776
|
function retryDelayMs(attempt, retry) {
|
|
5600
5777
|
const base = retry.baseMs ?? DEFAULT_RETRY_BASE_MS;
|
|
@@ -5975,8 +6152,8 @@ function searchPiSessions(sessions, query, options = {}) {
|
|
|
5975
6152
|
}
|
|
5976
6153
|
|
|
5977
6154
|
// src/front/chat/components/PiConversationSurface.tsx
|
|
5978
|
-
import {
|
|
5979
|
-
import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect, useRef as useRef11, useState as useState15 } from "react";
|
|
6155
|
+
import { Skeleton } from "@hachej/boring-ui-kit";
|
|
6156
|
+
import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect as useLayoutEffect2, useRef as useRef11, useState as useState15 } from "react";
|
|
5980
6157
|
import { useStickToBottomContext as useStickToBottomContext2 } from "use-stick-to-bottom";
|
|
5981
6158
|
|
|
5982
6159
|
// src/front/primitives/conversation.tsx
|
|
@@ -6037,7 +6214,7 @@ var ConversationScrollButton = ({
|
|
|
6037
6214
|
Button7,
|
|
6038
6215
|
{
|
|
6039
6216
|
className: cn(
|
|
6040
|
-
"absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full dark:bg-background dark:hover:bg-muted",
|
|
6217
|
+
"boring-agent-scroll-latest absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full dark:bg-background dark:hover:bg-muted",
|
|
6041
6218
|
className
|
|
6042
6219
|
),
|
|
6043
6220
|
onClick: handleScrollToBottom,
|
|
@@ -6082,8 +6259,27 @@ function noticeTextClass(className) {
|
|
|
6082
6259
|
import { memo as memo2 } from "react";
|
|
6083
6260
|
import { AlertTriangleIcon, InfoIcon, Loader2Icon, PlugZapIcon, RefreshCwIcon, XIcon as XIcon2 } from "lucide-react";
|
|
6084
6261
|
import { Button as Button8 } from "@hachej/boring-ui-kit";
|
|
6085
|
-
|
|
6086
|
-
|
|
6262
|
+
|
|
6263
|
+
// src/front/chat/components/terminalChatErrors.ts
|
|
6264
|
+
var TERMINAL_CHAT_ERROR_IDS = /* @__PURE__ */ new Set(["chat-error", "session-navigation-error"]);
|
|
6265
|
+
function isTerminalChatErrorId(id) {
|
|
6266
|
+
return TERMINAL_CHAT_ERROR_IDS.has(id);
|
|
6267
|
+
}
|
|
6268
|
+
function isTerminalChatErrorNotice(id, historyEmpty) {
|
|
6269
|
+
return historyEmpty && isTerminalChatErrorId(id);
|
|
6270
|
+
}
|
|
6271
|
+
function hasTerminalChatError(notices, historyEmpty) {
|
|
6272
|
+
return historyEmpty && notices.some((notice) => isTerminalChatErrorId(notice.id));
|
|
6273
|
+
}
|
|
6274
|
+
var COMPETING_NOISE_NOTICE_IDS = /* @__PURE__ */ new Set(["connection-reconnecting", "auto-retry"]);
|
|
6275
|
+
function filterCompetingNoiseNotices(notices, historyEmpty) {
|
|
6276
|
+
if (!hasTerminalChatError(notices, historyEmpty)) return notices;
|
|
6277
|
+
return notices.filter((notice) => !COMPETING_NOISE_NOTICE_IDS.has(notice.id));
|
|
6278
|
+
}
|
|
6279
|
+
|
|
6280
|
+
// src/front/chat/components/RuntimeNotices.tsx
|
|
6281
|
+
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
6282
|
+
var RuntimeNotices = memo2(({ notices, onDismiss, onAction, renderAction, historyEmpty, className, ...props }) => {
|
|
6087
6283
|
if (notices.length === 0) return null;
|
|
6088
6284
|
return /* @__PURE__ */ jsx16(
|
|
6089
6285
|
"div",
|
|
@@ -6091,16 +6287,29 @@ var RuntimeNotices = memo2(({ notices, onDismiss, onAction, renderAction, classN
|
|
|
6091
6287
|
"data-boring-agent-part": "runtime-notices",
|
|
6092
6288
|
className: cn("flex flex-col gap-2 px-4 py-2", className),
|
|
6093
6289
|
...props,
|
|
6094
|
-
children: notices.map((notice) => /* @__PURE__ */ jsx16(
|
|
6290
|
+
children: notices.map((notice) => /* @__PURE__ */ jsx16(
|
|
6291
|
+
RuntimeNoticeRow,
|
|
6292
|
+
{
|
|
6293
|
+
notice,
|
|
6294
|
+
onDismiss,
|
|
6295
|
+
onAction,
|
|
6296
|
+
renderAction,
|
|
6297
|
+
historyEmpty: historyEmpty ?? false
|
|
6298
|
+
},
|
|
6299
|
+
notice.id
|
|
6300
|
+
))
|
|
6095
6301
|
}
|
|
6096
6302
|
);
|
|
6097
6303
|
});
|
|
6098
6304
|
RuntimeNotices.displayName = "RuntimeNotices";
|
|
6099
|
-
function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
|
|
6305
|
+
function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction, historyEmpty }) {
|
|
6100
6306
|
const kind = inferNoticeKind(notice);
|
|
6101
6307
|
const Icon = iconForNotice(kind, notice.level);
|
|
6102
6308
|
const actionLabel = notice.actionLabel ?? defaultActionLabel(kind);
|
|
6103
6309
|
const hostAction = renderAction?.(notice);
|
|
6310
|
+
const isTerminalError = isTerminalChatErrorNotice(notice.id, historyEmpty);
|
|
6311
|
+
const explicitAction = actionLabel && onAction ? /* @__PURE__ */ jsx16(Button8, { type: "button", variant: "ghost", size: "sm", onClick: () => onAction(notice.id), children: actionLabel }) : null;
|
|
6312
|
+
const primaryAction = hostAction ?? explicitAction ?? (isTerminalError ? /* @__PURE__ */ jsx16(Button8, { type: "button", variant: "outline", size: "sm", className: "shrink-0", onClick: () => window.location.reload(), children: "Reload workspace" }) : null);
|
|
6104
6313
|
return /* @__PURE__ */ jsxs14(
|
|
6105
6314
|
"div",
|
|
6106
6315
|
{
|
|
@@ -6116,9 +6325,15 @@ function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
|
|
|
6116
6325
|
),
|
|
6117
6326
|
children: [
|
|
6118
6327
|
/* @__PURE__ */ jsx16(Icon, { className: cn(noticeIconClass(notice.level), kind === "retry" && "animate-spin motion-reduce:animate-none"), "aria-hidden": "true" }),
|
|
6119
|
-
/* @__PURE__ */ jsx16("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */
|
|
6120
|
-
|
|
6121
|
-
|
|
6328
|
+
/* @__PURE__ */ jsx16("div", { className: "min-w-0 flex-1", children: isTerminalError ? /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
6329
|
+
/* @__PURE__ */ jsx16("p", { className: "text-sm font-semibold text-foreground", children: "Chat history unavailable" }),
|
|
6330
|
+
/* @__PURE__ */ jsx16("p", { className: noticeTextClass("mt-0.5"), children: "The saved conversation could not be loaded. Reload the workspace to try again." }),
|
|
6331
|
+
/* @__PURE__ */ jsxs14("details", { className: "mt-2 text-xs text-muted-foreground", children: [
|
|
6332
|
+
/* @__PURE__ */ jsx16("summary", { className: "cursor-pointer select-none", children: "Error details" }),
|
|
6333
|
+
/* @__PURE__ */ jsx16("p", { className: "mt-1 break-words", children: notice.text })
|
|
6334
|
+
] })
|
|
6335
|
+
] }) : /* @__PURE__ */ jsx16("p", { className: noticeTextClass(), children: notice.text }) }),
|
|
6336
|
+
primaryAction,
|
|
6122
6337
|
notice.dismissible && onDismiss ? /* @__PURE__ */ jsx16(
|
|
6123
6338
|
Button8,
|
|
6124
6339
|
{
|
|
@@ -6161,7 +6376,8 @@ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
|
6161
6376
|
function RuntimeNoticeMessages({
|
|
6162
6377
|
notices,
|
|
6163
6378
|
onDismiss,
|
|
6164
|
-
renderAction
|
|
6379
|
+
renderAction,
|
|
6380
|
+
historyEmpty
|
|
6165
6381
|
}) {
|
|
6166
6382
|
const visible = notices.filter(
|
|
6167
6383
|
(notice) => notice.level === "error" || notice.level === "warning" || notice.id === "connection-reconnecting" || notice.id === "auto-retry" || notice.id === "large-state-warning" || notice.id.startsWith("command:") || notice.id.startsWith("composer-warning:")
|
|
@@ -6173,6 +6389,7 @@ function RuntimeNoticeMessages({
|
|
|
6173
6389
|
notices: visible,
|
|
6174
6390
|
onDismiss,
|
|
6175
6391
|
renderAction,
|
|
6392
|
+
historyEmpty,
|
|
6176
6393
|
className: "mx-auto w-full max-w-3xl px-0 py-0"
|
|
6177
6394
|
}
|
|
6178
6395
|
);
|
|
@@ -6634,11 +6851,11 @@ function extractCodeBlockText(children) {
|
|
|
6634
6851
|
};
|
|
6635
6852
|
return walk(children);
|
|
6636
6853
|
}
|
|
6637
|
-
var MarkdownPre = ({ children }) => {
|
|
6854
|
+
var MarkdownPre = ({ children, filename }) => {
|
|
6638
6855
|
const code3 = extractCodeBlockText(children).replace(/\n$/, "");
|
|
6639
6856
|
const language = extractCodeBlockLanguage(children);
|
|
6640
6857
|
return /* @__PURE__ */ jsx19(CodeBlock2, { code: code3, language: language ?? "text", className: "my-3", children: /* @__PURE__ */ jsxs17(CodeBlockHeader, { children: [
|
|
6641
|
-
/* @__PURE__ */ jsx19(CodeBlockTitle, { children: /* @__PURE__ */ jsx19(CodeBlockFilename, { children: language ?? "text" }) }),
|
|
6858
|
+
/* @__PURE__ */ jsx19(CodeBlockTitle, { children: /* @__PURE__ */ jsx19(CodeBlockFilename, { children: filename ?? language ?? "text" }) }),
|
|
6642
6859
|
/* @__PURE__ */ jsx19(CodeBlockCopyButton, {})
|
|
6643
6860
|
] }) });
|
|
6644
6861
|
};
|
|
@@ -6668,7 +6885,7 @@ var markdownComponents = {
|
|
|
6668
6885
|
code: MarkdownCode
|
|
6669
6886
|
};
|
|
6670
6887
|
var MessageResponse = memo3(
|
|
6671
|
-
({ className, shikiTheme, components, ...props }) => /* @__PURE__ */ jsx19(
|
|
6888
|
+
({ className, shikiTheme, components, codeFilename, ...props }) => /* @__PURE__ */ jsx19(
|
|
6672
6889
|
Streamdown,
|
|
6673
6890
|
{
|
|
6674
6891
|
className: cn(
|
|
@@ -6677,11 +6894,15 @@ var MessageResponse = memo3(
|
|
|
6677
6894
|
),
|
|
6678
6895
|
plugins: streamdownPlugins,
|
|
6679
6896
|
shikiTheme: shikiTheme ?? DEFAULT_SHIKI_THEME,
|
|
6680
|
-
components: {
|
|
6897
|
+
components: {
|
|
6898
|
+
...markdownComponents,
|
|
6899
|
+
...components ?? {},
|
|
6900
|
+
...codeFilename ? { pre: (props2) => /* @__PURE__ */ jsx19(MarkdownPre, { ...props2, filename: codeFilename }) } : {}
|
|
6901
|
+
},
|
|
6681
6902
|
...props
|
|
6682
6903
|
}
|
|
6683
6904
|
),
|
|
6684
|
-
(prevProps, nextProps) => prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating && nextProps.components === prevProps.components
|
|
6905
|
+
(prevProps, nextProps) => prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating && nextProps.components === prevProps.components && prevProps.codeFilename === nextProps.codeFilename
|
|
6685
6906
|
);
|
|
6686
6907
|
MessageResponse.displayName = "MessageResponse";
|
|
6687
6908
|
|
|
@@ -6762,7 +6983,7 @@ var ShimmerComponent = ({
|
|
|
6762
6983
|
var Shimmer = memo4(ShimmerComponent);
|
|
6763
6984
|
|
|
6764
6985
|
// src/front/primitives/reasoning.tsx
|
|
6765
|
-
import { Fragment as
|
|
6986
|
+
import { Fragment as Fragment8, jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
6766
6987
|
var ReasoningContext = createContext6(null);
|
|
6767
6988
|
var useReasoning = () => {
|
|
6768
6989
|
const context = useContext6(ReasoningContext);
|
|
@@ -6875,7 +7096,7 @@ var ReasoningTrigger = memo5(
|
|
|
6875
7096
|
className
|
|
6876
7097
|
),
|
|
6877
7098
|
...props,
|
|
6878
|
-
children: children ?? /* @__PURE__ */ jsxs18(
|
|
7099
|
+
children: children ?? /* @__PURE__ */ jsxs18(Fragment8, { children: [
|
|
6879
7100
|
/* @__PURE__ */ jsx21(BrainIcon, { className: "size-3" }),
|
|
6880
7101
|
getThinkingMessage(isStreaming, duration),
|
|
6881
7102
|
/* @__PURE__ */ jsx21(
|
|
@@ -7037,6 +7258,10 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
|
|
|
7037
7258
|
return () => window.clearInterval(interval);
|
|
7038
7259
|
}, [isRunning]);
|
|
7039
7260
|
if (toolParts.length === 0) return null;
|
|
7261
|
+
const inlineToolKeys = new Set(toolParts.flatMap(({ part, key }) => {
|
|
7262
|
+
const { renderer } = resolveToolRendererForPart(part, mergedToolRenderers);
|
|
7263
|
+
return renderer.presentation === "inline" ? [key] : [];
|
|
7264
|
+
}));
|
|
7040
7265
|
return /* @__PURE__ */ jsxs19(
|
|
7041
7266
|
Collapsible3,
|
|
7042
7267
|
{
|
|
@@ -7045,6 +7270,11 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
|
|
|
7045
7270
|
className: "not-prose my-1.5",
|
|
7046
7271
|
"data-boring-agent-tool-state": visualState,
|
|
7047
7272
|
children: [
|
|
7273
|
+
toolParts.map(({ part, key }) => {
|
|
7274
|
+
if (!inlineToolKeys.has(key)) return null;
|
|
7275
|
+
const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(part, mergedToolRenderers);
|
|
7276
|
+
return /* @__PURE__ */ jsx22("div", { "data-boring-agent-part": "inline-tool-renderer", "data-tool-call-id": resolvedPart.toolCallId, "data-tool-renderer-key": resolution.key, children: renderer(resolvedPart) }, key);
|
|
7277
|
+
}),
|
|
7048
7278
|
/* @__PURE__ */ jsxs19(
|
|
7049
7279
|
CollapsibleTrigger4,
|
|
7050
7280
|
{
|
|
@@ -7090,7 +7320,10 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
|
|
|
7090
7320
|
children: [
|
|
7091
7321
|
/* @__PURE__ */ jsx22("div", { className: "absolute left-[5px] top-0 bottom-2 w-px bg-border/35" }),
|
|
7092
7322
|
/* @__PURE__ */ jsx22("div", { className: "flex min-w-0 flex-col gap-1.5 [&_[data-boring-agent-part=tool-card]]:!my-0", children: toolParts.map(({ part, key }) => {
|
|
7093
|
-
const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(
|
|
7323
|
+
const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(
|
|
7324
|
+
part,
|
|
7325
|
+
inlineToolKeys.has(key) ? {} : mergedToolRenderers
|
|
7326
|
+
);
|
|
7094
7327
|
return /* @__PURE__ */ jsx22(
|
|
7095
7328
|
"div",
|
|
7096
7329
|
{
|
|
@@ -7142,7 +7375,7 @@ function ChatMessageContributionBoundary({
|
|
|
7142
7375
|
|
|
7143
7376
|
// src/front/chat/components/MessageMentions.tsx
|
|
7144
7377
|
import { Children, cloneElement, isValidElement as isValidElement2 } from "react";
|
|
7145
|
-
import { Fragment as
|
|
7378
|
+
import { Fragment as Fragment9, jsx as jsx24 } from "react/jsx-runtime";
|
|
7146
7379
|
var TERMINAL_BOUNDARY = String.raw`(?=$|\s|[.,;!?)}\]"'”’]+(?=$|\s))`;
|
|
7147
7380
|
var PREFIX_BOUNDARY = String.raw`(^|[\s(\[{"'“‘])`;
|
|
7148
7381
|
function escapeRegExp2(value) {
|
|
@@ -7300,8 +7533,8 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
|
|
|
7300
7533
|
const content = Children.toArray(children).filter((child) => child !== null && child !== "");
|
|
7301
7534
|
if (content.length === 1 && isValidElement2(content[0])) {
|
|
7302
7535
|
const child = content[0];
|
|
7303
|
-
if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx24(
|
|
7304
|
-
if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx24(
|
|
7536
|
+
if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx24(Fragment9, { children });
|
|
7537
|
+
if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx24(Fragment9, { children });
|
|
7305
7538
|
}
|
|
7306
7539
|
return /* @__PURE__ */ jsx24("p", { ...props, children: decorate(children) });
|
|
7307
7540
|
};
|
|
@@ -7387,12 +7620,13 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
7387
7620
|
mentionActivateRef.current?.(mention);
|
|
7388
7621
|
}, [openArtifact]);
|
|
7389
7622
|
const hasMentions = effectiveMentionCatalog.files || effectiveMentionCatalog.commands.length > 0 || effectiveMentionCatalog.skills.length > 0;
|
|
7390
|
-
const
|
|
7623
|
+
const messageIsStreaming = isAssistant && isLast && (isStreaming || message.status === "streaming");
|
|
7624
|
+
const mentionsEnabled = Boolean(isAssistant && !messageIsStreaming && hasMentions);
|
|
7391
7625
|
const mentionMarkdownComponents = useMemo12(
|
|
7392
7626
|
() => mentionsEnabled ? createMessageMentionMarkdownComponents(effectiveMentionCatalog, activateCurrentMention) : void 0,
|
|
7393
7627
|
[activateCurrentMention, effectiveMentionCatalog, mentionSignature, mentionsEnabled]
|
|
7394
7628
|
);
|
|
7395
|
-
const shouldReserveStreamingActions =
|
|
7629
|
+
const shouldReserveStreamingActions = messageIsStreaming;
|
|
7396
7630
|
return /* @__PURE__ */ jsxs20(
|
|
7397
7631
|
Message,
|
|
7398
7632
|
{
|
|
@@ -7451,7 +7685,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
7451
7685
|
})
|
|
7452
7686
|
}
|
|
7453
7687
|
) : null,
|
|
7454
|
-
finalParts.map((item) => {
|
|
7688
|
+
finalParts.map((item, index) => {
|
|
7455
7689
|
if (item.kind === "reasoning") {
|
|
7456
7690
|
return /* @__PURE__ */ jsx25(
|
|
7457
7691
|
TimelineReasoningPart,
|
|
@@ -7475,6 +7709,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
7475
7709
|
MessageResponse,
|
|
7476
7710
|
{
|
|
7477
7711
|
components: mentionMarkdownComponents,
|
|
7712
|
+
codeFilename: codeFilenameForPart(finalParts, index),
|
|
7478
7713
|
className: "boring-agent-markdown max-w-none",
|
|
7479
7714
|
children: text
|
|
7480
7715
|
},
|
|
@@ -7491,7 +7726,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
7491
7726
|
MessageActionsBar,
|
|
7492
7727
|
{
|
|
7493
7728
|
text: textParts.map((part) => part.text).join("\n\n"),
|
|
7494
|
-
visible: !
|
|
7729
|
+
visible: !messageIsStreaming
|
|
7495
7730
|
}
|
|
7496
7731
|
) : null
|
|
7497
7732
|
]
|
|
@@ -7526,7 +7761,7 @@ function TimelineReasoningPart({ item, showThoughts }) {
|
|
|
7526
7761
|
ReasoningTrigger,
|
|
7527
7762
|
{
|
|
7528
7763
|
className: "mb-1 w-fit rounded-[var(--radius-sm)] px-0 py-0 !text-xs !font-normal !text-muted-foreground/75 hover:bg-transparent hover:!text-muted-foreground/75 [&_svg]:!text-muted-foreground/75",
|
|
7529
|
-
getThinkingMessage: (
|
|
7764
|
+
getThinkingMessage: () => /* @__PURE__ */ jsx25("span", { children: "Reasoning" }),
|
|
7530
7765
|
onClick: toggleManualOpen
|
|
7531
7766
|
}
|
|
7532
7767
|
),
|
|
@@ -7587,6 +7822,15 @@ ${part.text}`;
|
|
|
7587
7822
|
flushTools();
|
|
7588
7823
|
return grouped;
|
|
7589
7824
|
}
|
|
7825
|
+
function codeFilenameForPart(parts, index) {
|
|
7826
|
+
const precedingPart = parts[index - 1];
|
|
7827
|
+
if (!precedingPart || precedingPart.kind !== "tool-plain") return void 0;
|
|
7828
|
+
if (precedingPart.part.toolName !== "write" && precedingPart.part.toolName !== "edit") return void 0;
|
|
7829
|
+
const input = precedingPart.part.input;
|
|
7830
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return void 0;
|
|
7831
|
+
const path = input.path;
|
|
7832
|
+
return typeof path === "string" && path.trim() ? path : void 0;
|
|
7833
|
+
}
|
|
7590
7834
|
function NoticeBubble({ level, text }) {
|
|
7591
7835
|
return /* @__PURE__ */ jsx25(
|
|
7592
7836
|
"div",
|
|
@@ -7667,10 +7911,10 @@ function MessageActionsBar({
|
|
|
7667
7911
|
}
|
|
7668
7912
|
};
|
|
7669
7913
|
const iconActionBtnClass = cn(
|
|
7670
|
-
"inline-flex
|
|
7671
|
-
"text-muted-foreground/
|
|
7672
|
-
"hover:bg-foreground/[0.04] hover:text-muted-foreground
|
|
7673
|
-
"focus-visible:outline-none focus-visible:ring-
|
|
7914
|
+
"boring-agent-message-action inline-flex size-8 items-center justify-center rounded-[var(--radius-sm)]",
|
|
7915
|
+
"text-muted-foreground/70 transition-colors duration-150",
|
|
7916
|
+
"hover:bg-foreground/[0.04] hover:text-muted-foreground",
|
|
7917
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)]/40"
|
|
7674
7918
|
);
|
|
7675
7919
|
const hiddenActionProps = visible ? {} : { tabIndex: -1 };
|
|
7676
7920
|
return /* @__PURE__ */ jsx25(
|
|
@@ -7678,7 +7922,7 @@ function MessageActionsBar({
|
|
|
7678
7922
|
{
|
|
7679
7923
|
"aria-hidden": !visible,
|
|
7680
7924
|
className: cn(
|
|
7681
|
-
"flex min-h-
|
|
7925
|
+
"flex min-h-8 items-center gap-1 -mt-1 text-xs text-muted-foreground transition-opacity duration-200",
|
|
7682
7926
|
visible ? "opacity-100" : "pointer-events-none opacity-0"
|
|
7683
7927
|
),
|
|
7684
7928
|
children: /* @__PURE__ */ jsx25(
|
|
@@ -7739,6 +7983,8 @@ function PiConversationSurface({
|
|
|
7739
7983
|
}) {
|
|
7740
7984
|
const messageItems = buildMessageRenderItems(messages);
|
|
7741
7985
|
const total = messageItems.length;
|
|
7986
|
+
const historyEmpty = messages.length === 0;
|
|
7987
|
+
const terminalError = hasTerminalChatError(runtimeNotices, historyEmpty);
|
|
7742
7988
|
const [visibleCount, setVisibleCount] = useState15(TRANSCRIPT_WINDOW);
|
|
7743
7989
|
useEffect14(() => {
|
|
7744
7990
|
setVisibleCount(TRANSCRIPT_WINDOW);
|
|
@@ -7752,7 +7998,7 @@ function PiConversationSurface({
|
|
|
7752
7998
|
return /* @__PURE__ */ jsxs21(
|
|
7753
7999
|
Conversation,
|
|
7754
8000
|
{
|
|
7755
|
-
className: emptyHero ? "max-h-[45vh]
|
|
8001
|
+
className: emptyHero ? cn("flex-none", runtimeNotices.length > 0 ? "max-h-[45vh]" : "max-h-[20vh]") : "flex-1",
|
|
7756
8002
|
"aria-label": "Agent conversation",
|
|
7757
8003
|
"aria-live": "polite",
|
|
7758
8004
|
onScrollToBottomReady,
|
|
@@ -7762,19 +8008,16 @@ function PiConversationSurface({
|
|
|
7762
8008
|
chrome ? "max-w-3xl px-6 py-8" : "max-w-[680px] px-4 py-4",
|
|
7763
8009
|
emptyHero && "py-4 text-center"
|
|
7764
8010
|
), children: [
|
|
7765
|
-
messages.length === 0 && emptyStateHydrating ? /* @__PURE__ */
|
|
7766
|
-
|
|
7767
|
-
"Loading chat history\u2026"
|
|
7768
|
-
] }) : null,
|
|
7769
|
-
messages.length === 0 && !emptyStateHydrating ? /* @__PURE__ */ jsx26(
|
|
8011
|
+
messages.length === 0 && emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx26(ConversationHistoryLoadingState, {}) : null,
|
|
8012
|
+
messages.length === 0 && !emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx26(
|
|
7770
8013
|
ChatEmptyState,
|
|
7771
8014
|
{
|
|
7772
8015
|
eyebrow: emptyState?.eyebrow,
|
|
7773
8016
|
title: emptyState?.title,
|
|
7774
8017
|
description: emptyState?.description,
|
|
7775
|
-
footer: emptyState?.footer,
|
|
7776
|
-
suggestions,
|
|
7777
|
-
|
|
8018
|
+
footer: emptyHero ? void 0 : emptyState?.footer,
|
|
8019
|
+
suggestions: emptyHero ? [] : suggestions,
|
|
8020
|
+
hero: emptyHero,
|
|
7778
8021
|
onSelect: (suggestion) => {
|
|
7779
8022
|
const text = suggestion.prompt ?? suggestion.label;
|
|
7780
8023
|
if (!text.trim()) return;
|
|
@@ -7798,13 +8041,45 @@ function PiConversationSurface({
|
|
|
7798
8041
|
},
|
|
7799
8042
|
key
|
|
7800
8043
|
)),
|
|
7801
|
-
/* @__PURE__ */ jsx26(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
|
|
8044
|
+
/* @__PURE__ */ jsx26(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction, historyEmpty })
|
|
7802
8045
|
] }),
|
|
7803
8046
|
/* @__PURE__ */ jsx26(ConversationScrollButton, {})
|
|
7804
8047
|
]
|
|
7805
8048
|
}
|
|
7806
8049
|
);
|
|
7807
8050
|
}
|
|
8051
|
+
function ConversationHistoryLoadingState() {
|
|
8052
|
+
const skeletonClassName = "motion-reduce:animate-none";
|
|
8053
|
+
return /* @__PURE__ */ jsxs21(
|
|
8054
|
+
"div",
|
|
8055
|
+
{
|
|
8056
|
+
role: "status",
|
|
8057
|
+
"aria-label": "Loading chat history",
|
|
8058
|
+
"aria-busy": "true",
|
|
8059
|
+
"data-boring-agent-part": "chat-history-loading",
|
|
8060
|
+
className: "flex flex-col gap-7 py-4",
|
|
8061
|
+
children: [
|
|
8062
|
+
/* @__PURE__ */ jsxs21("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
|
|
8063
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
|
|
8064
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex min-w-0 flex-1 flex-col gap-2.5", children: [
|
|
8065
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-24 rounded-sm", skeletonClassName) }),
|
|
8066
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-full rounded-sm", skeletonClassName) }),
|
|
8067
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-4/5 rounded-sm", skeletonClassName) })
|
|
8068
|
+
] })
|
|
8069
|
+
] }),
|
|
8070
|
+
/* @__PURE__ */ jsx26("div", { "aria-hidden": "true", className: "flex justify-end", children: /* @__PURE__ */ jsx26(Skeleton, { className: cn("h-11 w-[min(72%,30rem)] rounded-xl", skeletonClassName) }) }),
|
|
8071
|
+
/* @__PURE__ */ jsxs21("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
|
|
8072
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
|
|
8073
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex min-w-0 flex-1 flex-col gap-3", children: [
|
|
8074
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-20 rounded-sm", skeletonClassName) }),
|
|
8075
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-20 w-full rounded-lg", skeletonClassName) }),
|
|
8076
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-3/5 rounded-sm", skeletonClassName) })
|
|
8077
|
+
] })
|
|
8078
|
+
] })
|
|
8079
|
+
]
|
|
8080
|
+
}
|
|
8081
|
+
);
|
|
8082
|
+
}
|
|
7808
8083
|
function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
|
|
7809
8084
|
const { scrollRef } = useStickToBottomContext2();
|
|
7810
8085
|
const pendingAnchor = useRef11(null);
|
|
@@ -7823,7 +8098,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
|
|
|
7823
8098
|
el.addEventListener("scroll", onScroll, { passive: true });
|
|
7824
8099
|
return () => el.removeEventListener("scroll", onScroll);
|
|
7825
8100
|
}, [scrollRef, onLoadOlder]);
|
|
7826
|
-
|
|
8101
|
+
useLayoutEffect2(() => {
|
|
7827
8102
|
const el = scrollRef.current;
|
|
7828
8103
|
if (!el) return;
|
|
7829
8104
|
if (pendingAnchor.current != null) {
|
|
@@ -7837,7 +8112,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
|
|
|
7837
8112
|
{
|
|
7838
8113
|
type: "button",
|
|
7839
8114
|
onClick: onLoadOlder,
|
|
7840
|
-
className: "rounded-full border border-border/60 px-3 py-1 text-
|
|
8115
|
+
className: "boring-agent-history-action min-h-8 rounded-full border border-border/60 px-3 py-1.5 text-xs text-muted-foreground transition-colors hover:text-foreground",
|
|
7841
8116
|
children: [
|
|
7842
8117
|
"Load ",
|
|
7843
8118
|
olderCount,
|
|
@@ -7869,8 +8144,8 @@ function buildMessageRenderItems(messages) {
|
|
|
7869
8144
|
}
|
|
7870
8145
|
|
|
7871
8146
|
// src/front/chat/components/PiChatComposerSurface.tsx
|
|
7872
|
-
import { useCallback as useCallback16, useLayoutEffect as
|
|
7873
|
-
import { motion as motion2 } from "motion/react";
|
|
8147
|
+
import { useCallback as useCallback16, useLayoutEffect as useLayoutEffect3 } from "react";
|
|
8148
|
+
import { motion as motion2, useReducedMotion } from "motion/react";
|
|
7874
8149
|
|
|
7875
8150
|
// src/front/composer/PluginUpdateStatus.tsx
|
|
7876
8151
|
import { useEffect as useEffect15, useRef as useRef12 } from "react";
|
|
@@ -8113,7 +8388,7 @@ function PluginUpdateStatus({
|
|
|
8113
8388
|
|
|
8114
8389
|
// src/front/composer/CommandRunStatus.tsx
|
|
8115
8390
|
import { useEffect as useEffect16, useRef as useRef13 } from "react";
|
|
8116
|
-
import { Fragment as
|
|
8391
|
+
import { Fragment as Fragment10, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
8117
8392
|
function CommandRunStatus({
|
|
8118
8393
|
state,
|
|
8119
8394
|
onDismiss,
|
|
@@ -8138,7 +8413,7 @@ function CommandRunStatus({
|
|
|
8138
8413
|
tone: "running",
|
|
8139
8414
|
dataAttribute: "data-boring-command-run",
|
|
8140
8415
|
maxWidthClassName,
|
|
8141
|
-
runningContent: state.command ? /* @__PURE__ */ jsxs24(
|
|
8416
|
+
runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
|
|
8142
8417
|
" Running ",
|
|
8143
8418
|
/* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
|
|
8144
8419
|
"/",
|
|
@@ -8156,7 +8431,7 @@ function CommandRunStatus({
|
|
|
8156
8431
|
tone: "success",
|
|
8157
8432
|
dataAttribute: "data-boring-command-run",
|
|
8158
8433
|
maxWidthClassName,
|
|
8159
|
-
title: state.command ? /* @__PURE__ */ jsxs24(
|
|
8434
|
+
title: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
|
|
8160
8435
|
" Ran ",
|
|
8161
8436
|
/* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
|
|
8162
8437
|
"/",
|
|
@@ -8176,7 +8451,7 @@ function CommandRunStatus({
|
|
|
8176
8451
|
tone: "error",
|
|
8177
8452
|
dataAttribute: "data-boring-command-run",
|
|
8178
8453
|
maxWidthClassName,
|
|
8179
|
-
title: state.command ? /* @__PURE__ */ jsxs24(
|
|
8454
|
+
title: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
|
|
8180
8455
|
" ",
|
|
8181
8456
|
/* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
|
|
8182
8457
|
"/",
|
|
@@ -8226,12 +8501,12 @@ function displayProviderLabel(provider) {
|
|
|
8226
8501
|
}
|
|
8227
8502
|
|
|
8228
8503
|
// src/front/chatPanelComposerControls.tsx
|
|
8229
|
-
import { Fragment as
|
|
8504
|
+
import { Fragment as Fragment11, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
8230
8505
|
var composerActionClass = cn(
|
|
8231
8506
|
"inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
|
|
8232
|
-
"text-muted-foreground shadow-none transition",
|
|
8507
|
+
"text-muted-foreground shadow-none transition-colors motion-reduce:transition-none",
|
|
8233
8508
|
"hover:bg-muted/60 hover:text-foreground",
|
|
8234
|
-
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring
|
|
8509
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover",
|
|
8235
8510
|
"disabled:pointer-events-none disabled:opacity-50"
|
|
8236
8511
|
);
|
|
8237
8512
|
var THINKING_LEVEL_LABELS = {
|
|
@@ -8241,10 +8516,10 @@ var THINKING_LEVEL_LABELS = {
|
|
|
8241
8516
|
high: "High"
|
|
8242
8517
|
};
|
|
8243
8518
|
var THINKING_LEVEL_STATUS_LABELS = {
|
|
8244
|
-
off: "
|
|
8245
|
-
low: "
|
|
8246
|
-
medium: "
|
|
8247
|
-
high: "
|
|
8519
|
+
off: "Off",
|
|
8520
|
+
low: "Low",
|
|
8521
|
+
medium: "Medium",
|
|
8522
|
+
high: "High"
|
|
8248
8523
|
};
|
|
8249
8524
|
var THINKING_LEVEL_DETAILS = {
|
|
8250
8525
|
off: "No extra reasoning",
|
|
@@ -8254,7 +8529,7 @@ var THINKING_LEVEL_DETAILS = {
|
|
|
8254
8529
|
};
|
|
8255
8530
|
var selectorTriggerClass = cn(
|
|
8256
8531
|
composerActionClass,
|
|
8257
|
-
"h-
|
|
8532
|
+
"h-8 rounded-lg border border-border/60 bg-transparent px-2 text-xs font-medium text-muted-foreground",
|
|
8258
8533
|
"hover:border-border/80 hover:bg-muted/55 hover:text-foreground"
|
|
8259
8534
|
);
|
|
8260
8535
|
var selectorContentClass = cn(
|
|
@@ -8262,12 +8537,12 @@ var selectorContentClass = cn(
|
|
|
8262
8537
|
"bg-[color:var(--popover)] p-1 text-[color:var(--popover-foreground)] shadow-xl"
|
|
8263
8538
|
);
|
|
8264
8539
|
var composerPickerMenuClass = cn(
|
|
8265
|
-
"
|
|
8266
|
-
"bg-[color:var(--popover)] text-[color:var(--popover-foreground)] shadow-
|
|
8540
|
+
"w-full overflow-hidden rounded-xl border border-border",
|
|
8541
|
+
"bg-[color:var(--popover)] text-[color:var(--popover-foreground)] shadow-xl"
|
|
8267
8542
|
);
|
|
8268
8543
|
function selectorItemClass(selected) {
|
|
8269
8544
|
return cn(
|
|
8270
|
-
"flex items-center gap-2 rounded-md px-2 py-
|
|
8545
|
+
"flex min-h-10 items-center gap-2 rounded-md px-2.5 py-2 text-sm text-muted-foreground",
|
|
8271
8546
|
"data-[selected=true]:bg-[color:oklch(from_var(--accent)_l_c_h/0.15)] data-[selected=true]:text-foreground",
|
|
8272
8547
|
selected && "bg-foreground/[0.06] text-foreground"
|
|
8273
8548
|
);
|
|
@@ -8322,7 +8597,8 @@ function ThinkingLevelGlyph({ level }) {
|
|
|
8322
8597
|
function modelTriggerLabel(value, options, emptyLabel = "Default model") {
|
|
8323
8598
|
const current = value ? options.find((m) => m.provider === value.provider && m.id === value.id) : void 0;
|
|
8324
8599
|
const rawTriggerLabel = current?.label ?? value?.id;
|
|
8325
|
-
|
|
8600
|
+
const label = value ? rawTriggerLabel && current?.label && current.label !== value.id && /[A-Z]/.test(current.label) ? current.label : displayModelLabel(rawTriggerLabel ?? value.id) : emptyLabel;
|
|
8601
|
+
return value ? label.replace(/\s+\([^)]*\)\s*$/, "") : label;
|
|
8326
8602
|
}
|
|
8327
8603
|
function modelMenuOptions(_value, options) {
|
|
8328
8604
|
return options.filter((m) => m.available);
|
|
@@ -8361,16 +8637,20 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
|
|
|
8361
8637
|
title: trigger === "slash" ? `Open model picker. Current model: ${triggerDisplay}` : void 0,
|
|
8362
8638
|
onClick,
|
|
8363
8639
|
className: cn(
|
|
8364
|
-
trigger === "slash" ? "max-w-[min(
|
|
8640
|
+
trigger === "slash" ? "h-8 max-w-[min(68vw,420px)] cursor-pointer overflow-hidden text-ellipsis whitespace-nowrap rounded-[var(--radius-md)] px-2 text-xs transition-colors hover:bg-muted/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none" : selectorTriggerClass,
|
|
8365
8641
|
trigger === "slash" ? "w-auto" : "w-auto max-w-[min(52vw,260px)] gap-1",
|
|
8366
8642
|
open && (trigger === "slash" ? "bg-muted/45" : "border-border/80 bg-muted/55 text-foreground"),
|
|
8367
8643
|
className
|
|
8368
8644
|
),
|
|
8369
8645
|
...props,
|
|
8370
|
-
children: trigger === "slash" ? /* @__PURE__ */ jsxs25(
|
|
8371
|
-
/* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "
|
|
8372
|
-
/* @__PURE__ */ jsx30("span", { className: "text-foreground", children:
|
|
8373
|
-
|
|
8646
|
+
children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
|
|
8647
|
+
/* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "Model \xB7 " }),
|
|
8648
|
+
/* @__PURE__ */ jsx30("span", { className: "min-w-0 truncate text-foreground", children: triggerLabel }),
|
|
8649
|
+
value ? /* @__PURE__ */ jsxs25("span", { className: "shrink-0 text-muted-foreground", children: [
|
|
8650
|
+
" \xB7 ",
|
|
8651
|
+
displayProviderLabel(value.provider)
|
|
8652
|
+
] }) : null
|
|
8653
|
+
] }) : /* @__PURE__ */ jsxs25(Fragment11, { children: [
|
|
8374
8654
|
/* @__PURE__ */ jsx30("span", { className: "min-w-0 truncate", children: triggerDisplay }),
|
|
8375
8655
|
/* @__PURE__ */ jsx30(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
|
|
8376
8656
|
] })
|
|
@@ -8435,11 +8715,11 @@ function ModelPickerMenu({
|
|
|
8435
8715
|
{
|
|
8436
8716
|
placeholder: "Search models\u2026",
|
|
8437
8717
|
autoFocus: true,
|
|
8438
|
-
className: "h-8 w-full border-0 bg-transparent text-
|
|
8718
|
+
className: "h-8 w-full border-0 bg-transparent text-sm outline-none focus:ring-0"
|
|
8439
8719
|
}
|
|
8440
8720
|
) }),
|
|
8441
8721
|
/* @__PURE__ */ jsxs25(CommandList, { className: "max-h-[300px] p-0.5", children: [
|
|
8442
|
-
/* @__PURE__ */ jsx30(CommandEmpty, { className: "py-4 text-center text-
|
|
8722
|
+
/* @__PURE__ */ jsx30(CommandEmpty, { className: "py-4 text-center text-sm text-muted-foreground", children: "No models found" }),
|
|
8443
8723
|
!hideDefaultOption ? /* @__PURE__ */ jsx30(CommandGroup, { children: /* @__PURE__ */ jsxs25(
|
|
8444
8724
|
CommandItem,
|
|
8445
8725
|
{
|
|
@@ -8461,7 +8741,7 @@ function ModelPickerMenu({
|
|
|
8461
8741
|
}
|
|
8462
8742
|
),
|
|
8463
8743
|
/* @__PURE__ */ jsx30("span", { className: "truncate", children: "Default model" }),
|
|
8464
|
-
/* @__PURE__ */ jsx30("span", { className: "ml-auto shrink-0 text-
|
|
8744
|
+
/* @__PURE__ */ jsx30("span", { className: "ml-auto shrink-0 text-xs text-muted-foreground", children: "Auto" })
|
|
8465
8745
|
]
|
|
8466
8746
|
}
|
|
8467
8747
|
) }) : null,
|
|
@@ -8469,7 +8749,7 @@ function ModelPickerMenu({
|
|
|
8469
8749
|
CommandGroup,
|
|
8470
8750
|
{
|
|
8471
8751
|
heading: displayProviderLabel(provider),
|
|
8472
|
-
className: "[&_[cmdk-group-heading]]:px-2.5 [&_[cmdk-group-heading]]:py-
|
|
8752
|
+
className: "[&_[cmdk-group-heading]]:px-2.5 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-[0.08em] [&_[cmdk-group-heading]]:text-muted-foreground",
|
|
8473
8753
|
children: list.map((m) => {
|
|
8474
8754
|
const key = encodeModelKey(m);
|
|
8475
8755
|
const label = m.label || displayModelLabel(m.id);
|
|
@@ -8495,7 +8775,7 @@ function ModelPickerMenu({
|
|
|
8495
8775
|
}
|
|
8496
8776
|
),
|
|
8497
8777
|
/* @__PURE__ */ jsx30("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
|
|
8498
|
-
/* @__PURE__ */ jsx30("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-
|
|
8778
|
+
/* @__PURE__ */ jsx30("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-xs text-muted-foreground", children: m.id })
|
|
8499
8779
|
]
|
|
8500
8780
|
},
|
|
8501
8781
|
key
|
|
@@ -8646,7 +8926,7 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
|
|
|
8646
8926
|
"data-boring-state": disabled ? "disabled" : void 0,
|
|
8647
8927
|
disabled,
|
|
8648
8928
|
className: cn(
|
|
8649
|
-
trigger === "slash" ? "cursor-pointer rounded-[var(--radius-md)] px-
|
|
8929
|
+
trigger === "slash" ? "h-8 cursor-pointer rounded-[var(--radius-md)] px-2 text-xs transition-colors hover:bg-muted/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none" : selectorTriggerClass,
|
|
8650
8930
|
trigger === "button" && "gap-1.5",
|
|
8651
8931
|
trigger === "button" && value !== "off" && !open && "border-[color:oklch(from_var(--accent)_l_c_h/0.35)] bg-[color:oklch(from_var(--accent)_l_c_h/0.08)] text-foreground",
|
|
8652
8932
|
open && (trigger === "slash" ? "bg-muted/45" : "border-border/80 bg-muted/55 text-foreground"),
|
|
@@ -8659,10 +8939,10 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
|
|
|
8659
8939
|
...props,
|
|
8660
8940
|
children: [
|
|
8661
8941
|
trigger === "button" ? /* @__PURE__ */ jsx30(ThinkingLevelGlyph, { level: value }) : null,
|
|
8662
|
-
trigger === "slash" ? /* @__PURE__ */ jsxs25(
|
|
8663
|
-
/* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "
|
|
8942
|
+
trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
|
|
8943
|
+
/* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "Thinking \xB7 " }),
|
|
8664
8944
|
/* @__PURE__ */ jsx30("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
|
|
8665
|
-
] }) : /* @__PURE__ */ jsxs25(
|
|
8945
|
+
] }) : /* @__PURE__ */ jsxs25(Fragment11, { children: [
|
|
8666
8946
|
/* @__PURE__ */ jsx30("span", { children: THINKING_LEVEL_LABELS[value] }),
|
|
8667
8947
|
/* @__PURE__ */ jsx30(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
|
|
8668
8948
|
] })
|
|
@@ -8735,7 +9015,7 @@ function ThinkingPickerMenu({
|
|
|
8735
9015
|
),
|
|
8736
9016
|
/* @__PURE__ */ jsx30(ThinkingLevelGlyph, { level }),
|
|
8737
9017
|
/* @__PURE__ */ jsx30("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
|
|
8738
|
-
/* @__PURE__ */ jsx30("span", { className: "ml-auto text-
|
|
9018
|
+
/* @__PURE__ */ jsx30("span", { className: "ml-auto text-xs text-muted-foreground", children: THINKING_LEVEL_DETAILS[level] })
|
|
8739
9019
|
]
|
|
8740
9020
|
},
|
|
8741
9021
|
level
|
|
@@ -8826,7 +9106,7 @@ var PromptInputFooter = ({
|
|
|
8826
9106
|
);
|
|
8827
9107
|
|
|
8828
9108
|
// src/front/primitives/prompt-input.tsx
|
|
8829
|
-
import { Fragment as
|
|
9109
|
+
import { Fragment as Fragment12, jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
8830
9110
|
var PromptInput = ({
|
|
8831
9111
|
className,
|
|
8832
9112
|
accept,
|
|
@@ -9169,7 +9449,7 @@ var PromptInput = ({
|
|
|
9169
9449
|
},
|
|
9170
9450
|
[usingProvider, controller, files, onSubmit, clear]
|
|
9171
9451
|
);
|
|
9172
|
-
const inner = /* @__PURE__ */ jsxs27(
|
|
9452
|
+
const inner = /* @__PURE__ */ jsxs27(Fragment12, { children: [
|
|
9173
9453
|
/* @__PURE__ */ jsx32(
|
|
9174
9454
|
Input,
|
|
9175
9455
|
{
|
|
@@ -9338,7 +9618,7 @@ var PromptInputSubmit = ({
|
|
|
9338
9618
|
|
|
9339
9619
|
// src/front/primitives/slash-command-picker.tsx
|
|
9340
9620
|
import { useEffect as useEffect19, useMemo as useMemo14, useRef as useRef16, useState as useState18 } from "react";
|
|
9341
|
-
import { Fragment as
|
|
9621
|
+
import { Fragment as Fragment13, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
9342
9622
|
var ALL_PLUGINS = "__all__";
|
|
9343
9623
|
function commandGroup(cmd) {
|
|
9344
9624
|
if (cmd.source === "skill") return "skills";
|
|
@@ -9450,7 +9730,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
|
|
|
9450
9730
|
"/",
|
|
9451
9731
|
cmd.name
|
|
9452
9732
|
] }),
|
|
9453
|
-
cmd.source === "skill" ? /* @__PURE__ */ jsxs28(
|
|
9733
|
+
cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment13, { children: [
|
|
9454
9734
|
/* @__PURE__ */ jsx33("span", { className: "rounded-sm bg-accent/15 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-accent-foreground", children: "skill" }),
|
|
9455
9735
|
cmd.sourcePlugin ? /* @__PURE__ */ jsx33("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin }) : null
|
|
9456
9736
|
] }) : cmd.sourcePlugin ? (
|
|
@@ -9467,7 +9747,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
|
|
|
9467
9747
|
}
|
|
9468
9748
|
|
|
9469
9749
|
// src/front/chat/components/ComposerAttachments.tsx
|
|
9470
|
-
import { AlertCircleIcon as AlertCircleIcon2, Loader2 as
|
|
9750
|
+
import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader22, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
|
|
9471
9751
|
import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
|
|
9472
9752
|
import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
9473
9753
|
function AttachmentButton({ disabled, className }) {
|
|
@@ -9482,10 +9762,10 @@ function AttachmentButton({ disabled, className }) {
|
|
|
9482
9762
|
onClick: () => {
|
|
9483
9763
|
if (!disabled) attachments.openFileDialog();
|
|
9484
9764
|
},
|
|
9485
|
-
className: cn(composerActionClass, "w-
|
|
9765
|
+
className: cn(composerActionClass, "composer-attachment-button !h-10 !w-10", className),
|
|
9486
9766
|
"aria-label": "Attach files",
|
|
9487
9767
|
title: disabled ? "Attachments are available when the composer is ready." : "Attach files",
|
|
9488
|
-
children: /* @__PURE__ */ jsx34(PaperclipIcon2, { className: "h-
|
|
9768
|
+
children: /* @__PURE__ */ jsx34(PaperclipIcon2, { className: "h-4 w-4", strokeWidth: 1.75 })
|
|
9489
9769
|
}
|
|
9490
9770
|
);
|
|
9491
9771
|
}
|
|
@@ -9505,18 +9785,18 @@ function AttachmentsList() {
|
|
|
9505
9785
|
data: file,
|
|
9506
9786
|
onRemove: () => attachments.remove(file.id),
|
|
9507
9787
|
className: cn(
|
|
9508
|
-
"!h-9 !gap-2 !rounded-full !border-input/80 !bg-muted/40 !pl-1 !pr-2",
|
|
9788
|
+
"composer-attachment-chip !h-9 !gap-2 !rounded-full !border-input/80 !bg-muted/40 !pl-1 !pr-2",
|
|
9509
9789
|
"transition-colors hover:!bg-muted/70 hover:!text-foreground",
|
|
9510
9790
|
file.status === "error" && "!border-destructive/50 !bg-destructive/10"
|
|
9511
9791
|
),
|
|
9512
9792
|
children: [
|
|
9513
9793
|
/* @__PURE__ */ jsxs29("div", { className: "relative shrink-0", children: [
|
|
9514
9794
|
/* @__PURE__ */ jsx34(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
|
|
9515
|
-
file.status === "uploading" ? /* @__PURE__ */ jsx34("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx34(
|
|
9795
|
+
file.status === "uploading" ? /* @__PURE__ */ jsx34("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx34(Loader22, { className: "size-3.5 animate-spin text-muted-foreground" }) }) : null,
|
|
9516
9796
|
file.status === "error" ? /* @__PURE__ */ jsx34("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-destructive/20", children: /* @__PURE__ */ jsx34(AlertCircleIcon2, { className: "size-3.5 text-destructive" }) }) : null
|
|
9517
9797
|
] }),
|
|
9518
9798
|
/* @__PURE__ */ jsx34(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
|
|
9519
|
-
/* @__PURE__ */ jsx34(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
|
|
9799
|
+
/* @__PURE__ */ jsx34(AttachmentRemove, { className: "composer-attachment-remove !size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
|
|
9520
9800
|
]
|
|
9521
9801
|
},
|
|
9522
9802
|
file.id
|
|
@@ -9550,6 +9830,7 @@ function hasSoftWrappedLine(node, style) {
|
|
|
9550
9830
|
}
|
|
9551
9831
|
function PiChatComposerSurface({
|
|
9552
9832
|
chrome,
|
|
9833
|
+
pickerPlacement = "above",
|
|
9553
9834
|
isStreaming,
|
|
9554
9835
|
status,
|
|
9555
9836
|
disabled,
|
|
@@ -9607,6 +9888,7 @@ function PiChatComposerSurface({
|
|
|
9607
9888
|
onSubmitMessage,
|
|
9608
9889
|
onStop
|
|
9609
9890
|
}) {
|
|
9891
|
+
const prefersReducedMotion = useReducedMotion();
|
|
9610
9892
|
const workspaceRequestId = getHeaderValue(requestHeaders, "x-boring-workspace-id");
|
|
9611
9893
|
const composerContributions = useComposerContributions();
|
|
9612
9894
|
const uploadAttachment = useCallback16((file) => uploadFile(file, {
|
|
@@ -9636,10 +9918,13 @@ function PiChatComposerSurface({
|
|
|
9636
9918
|
rail?.style.setProperty("--composer-input-group-height", `${inputGroupHeight}px`);
|
|
9637
9919
|
rail?.setAttribute("data-composer-multiline", inputGroupHeight > COMPOSER_INPUT_GROUP_MIN_HEIGHT ? "true" : "false");
|
|
9638
9920
|
}, []);
|
|
9639
|
-
|
|
9921
|
+
useLayoutEffect3(() => {
|
|
9640
9922
|
resizeTextarea(textareaRef.current);
|
|
9641
9923
|
}, [draft, resizeTextarea, textareaRef]);
|
|
9642
|
-
return /* @__PURE__ */ jsxs30("div", { className: cn(
|
|
9924
|
+
return /* @__PURE__ */ jsxs30("div", { className: cn(
|
|
9925
|
+
"relative z-20",
|
|
9926
|
+
chrome ? "pb-[calc(1rem+env(safe-area-inset-bottom))] pl-[calc(1rem+env(safe-area-inset-left))] pr-[calc(1rem+env(safe-area-inset-right))] pt-2 sm:pb-[calc(1.25rem+env(safe-area-inset-bottom))] sm:pl-[calc(1.5rem+env(safe-area-inset-left))] sm:pr-[calc(1.5rem+env(safe-area-inset-right))]" : "pb-[calc(0.5rem+env(safe-area-inset-bottom))] pl-[calc(0.75rem+env(safe-area-inset-left))] pr-[calc(0.75rem+env(safe-area-inset-right))] pt-1"
|
|
9927
|
+
), children: [
|
|
9643
9928
|
/* @__PURE__ */ jsx35(
|
|
9644
9929
|
"div",
|
|
9645
9930
|
{
|
|
@@ -9656,15 +9941,15 @@ function PiChatComposerSurface({
|
|
|
9656
9941
|
"data-testid": isStreaming ? "chat-working" : void 0,
|
|
9657
9942
|
role: isStreaming ? "status" : void 0,
|
|
9658
9943
|
"aria-live": isStreaming ? "polite" : void 0,
|
|
9659
|
-
className: "inline-flex items-center gap-2 rounded-full border border-border/50 bg-background/85 px-2.5 py-1 text-[12px] text-
|
|
9944
|
+
className: "inline-flex items-center gap-2 rounded-full border border-border/50 bg-background/85 px-2.5 py-1 text-[12px] text-foreground shadow-sm backdrop-blur",
|
|
9660
9945
|
children: [
|
|
9661
9946
|
/* @__PURE__ */ jsx35(
|
|
9662
9947
|
motion2.span,
|
|
9663
9948
|
{
|
|
9664
9949
|
"aria-hidden": "true",
|
|
9665
9950
|
className: "inline-block size-1.5 rounded-full bg-[color:var(--accent)]",
|
|
9666
|
-
animate: { opacity: [0.35, 1, 0.35] },
|
|
9667
|
-
transition: { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
|
|
9951
|
+
animate: prefersReducedMotion ? { opacity: 1 } : { opacity: [0.35, 1, 0.35] },
|
|
9952
|
+
transition: prefersReducedMotion ? { duration: 0 } : { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
|
|
9668
9953
|
}
|
|
9669
9954
|
),
|
|
9670
9955
|
/* @__PURE__ */ jsx35("span", { children: "Working\u2026" })
|
|
@@ -9707,219 +9992,225 @@ function PiChatComposerSurface({
|
|
|
9707
9992
|
children: attachmentNotice
|
|
9708
9993
|
}
|
|
9709
9994
|
) : null,
|
|
9710
|
-
/* @__PURE__ */ jsxs30("div", { className: cn("mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9995
|
+
/* @__PURE__ */ jsxs30("div", { className: cn("@container group/composer relative mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
|
|
9996
|
+
/* @__PURE__ */ jsxs30("div", { className: cn(
|
|
9997
|
+
"absolute inset-x-0 z-30",
|
|
9998
|
+
pickerPlacement === "above-compact" ? "bottom-[calc(100%+4px)] [&_[cmdk-list]]:!max-h-[200px]" : "bottom-[calc(100%+4px)]"
|
|
9999
|
+
), children: [
|
|
10000
|
+
mentionState ? /* @__PURE__ */ jsx35("div", { className: "mr-px ml-[43px]", children: /* @__PURE__ */ jsx35(
|
|
10001
|
+
MentionPicker,
|
|
10002
|
+
{
|
|
10003
|
+
mention: mentionState,
|
|
10004
|
+
apiBaseUrl,
|
|
10005
|
+
fetch: fetch2,
|
|
10006
|
+
requestHeaders,
|
|
10007
|
+
storageScope,
|
|
10008
|
+
onSelect: onSelectMention,
|
|
10009
|
+
onDismiss: onDismissMention
|
|
10010
|
+
}
|
|
10011
|
+
) }) : null,
|
|
10012
|
+
slashQuery !== null ? /* @__PURE__ */ jsx35("div", { className: "mr-px ml-[43px]", children: /* @__PURE__ */ jsx35(
|
|
10013
|
+
SlashCommandPicker,
|
|
10014
|
+
{
|
|
10015
|
+
query: slashQuery,
|
|
10016
|
+
commands,
|
|
10017
|
+
onSelect: onSelectSlashCommand,
|
|
10018
|
+
onDismiss: onDismissSlash
|
|
10019
|
+
}
|
|
10020
|
+
) }) : null,
|
|
10021
|
+
mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx35("div", { className: "mr-px mb-1 ml-[43px]", children: /* @__PURE__ */ jsx35(
|
|
10022
|
+
ModelPickerMenu,
|
|
10023
|
+
{
|
|
10024
|
+
value: selectedModel,
|
|
10025
|
+
onChange: onModelChange,
|
|
10026
|
+
options: modelOptions,
|
|
10027
|
+
disabled: isStreaming || modelControlled,
|
|
10028
|
+
hideDefaultOption: hideDefaultModelOption,
|
|
10029
|
+
onClose: () => onSetModelPickerOpen(false)
|
|
10030
|
+
}
|
|
10031
|
+
) }) : null,
|
|
10032
|
+
mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx35("div", { className: "mr-px mb-1 ml-[43px]", children: /* @__PURE__ */ jsx35(
|
|
10033
|
+
ThinkingPickerMenu,
|
|
10034
|
+
{
|
|
10035
|
+
value: selectedThinking,
|
|
10036
|
+
onChange: onThinkingChange,
|
|
10037
|
+
disabled: isStreaming || thinkingControlled,
|
|
10038
|
+
onClose: () => onSetThinkingPickerOpen(false)
|
|
10039
|
+
}
|
|
10040
|
+
) }) : null
|
|
10041
|
+
] }),
|
|
10042
|
+
composerContributions.map(({ id, Top }) => Top ? /* @__PURE__ */ jsx35(
|
|
10043
|
+
"div",
|
|
9725
10044
|
{
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
}
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
10045
|
+
"data-boring-agent-part": "composer-contribution-top",
|
|
10046
|
+
"data-boring-agent-contribution-id": id,
|
|
10047
|
+
className: cn("mx-auto mb-2 w-full empty:hidden", chrome ? "max-w-3xl" : "max-w-[680px]"),
|
|
10048
|
+
children: /* @__PURE__ */ jsx35(Top, {})
|
|
10049
|
+
},
|
|
10050
|
+
id
|
|
10051
|
+
) : null),
|
|
10052
|
+
/* @__PURE__ */ jsx35(
|
|
10053
|
+
"div",
|
|
9734
10054
|
{
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
10055
|
+
"data-boring-agent-part": "composer-rail",
|
|
10056
|
+
"data-composer-multiline": "false",
|
|
10057
|
+
style: { "--composer-input-group-height": `${COMPOSER_INPUT_GROUP_MIN_HEIGHT}px` },
|
|
10058
|
+
className: cn(
|
|
10059
|
+
"relative mx-auto w-full overflow-visible rounded-xl",
|
|
10060
|
+
chrome ? "max-w-3xl bg-transparent shadow-[0_1px_2px_-1px_oklch(0_0_0/0.06),0_6px_18px_-12px_oklch(0_0_0/0.12),inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.7)] focus-within:shadow-[0_1px_3px_-1px_oklch(0_0_0/0.08),0_10px_28px_-14px_oklch(0_0_0/0.16),inset_0_0_0_1px_oklch(from_var(--accent)_l_c_h/0.45)]" : "max-w-[680px] bg-transparent shadow-[inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.7)] focus-within:shadow-[inset_0_0_0_1px_oklch(from_var(--accent)_l_c_h/0.45)]",
|
|
10061
|
+
"transition-shadow duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
10062
|
+
"[&_[data-slot=input-group]]:!h-auto [&_[data-slot=input-group]]:!min-h-[var(--composer-input-group-height)]",
|
|
10063
|
+
"[&_[data-slot=input-group]]:!flex-col [&_[data-slot=input-group]]:!items-stretch [&_[data-slot=input-group]]:!overflow-hidden",
|
|
10064
|
+
"[&_[data-slot=input-group]]:!border-0 [&_[data-slot=input-group]]:!rounded-xl",
|
|
10065
|
+
"[&_[data-slot=input-group]]:!bg-transparent [&_[data-slot=input-group]]:!shadow-none",
|
|
10066
|
+
"[&_[data-slot=input-group]]:dark:!bg-transparent [&_[data-slot=input-group]]:!ring-0",
|
|
10067
|
+
"[&_[data-slot=input-group]]:has-[:focus]:!ring-0",
|
|
10068
|
+
"[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:self-end",
|
|
10069
|
+
"[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:mb-2"
|
|
10070
|
+
),
|
|
10071
|
+
children: /* @__PURE__ */ jsxs30(
|
|
10072
|
+
PromptInput,
|
|
10073
|
+
{
|
|
10074
|
+
"data-boring-state": status,
|
|
10075
|
+
onSubmit: (message) => onSubmitMessage({ text: message.text, files: message.files }),
|
|
10076
|
+
onUploadFile: uploadAttachment,
|
|
10077
|
+
multiple: true,
|
|
10078
|
+
maxFiles: disabled || isStreaming ? 0 : MAX_PROMPT_ATTACHMENTS,
|
|
10079
|
+
maxFileSize: MAX_PROMPT_ATTACHMENT_BYTES,
|
|
10080
|
+
onError: (err) => {
|
|
10081
|
+
if (err.code === "max_files") onAttachmentNotice(`Up to ${MAX_PROMPT_ATTACHMENTS} attachments per message.`);
|
|
10082
|
+
else if (err.code === "max_file_size") onAttachmentNotice("Files must be under 4 MB each.");
|
|
10083
|
+
else if (err.code === "accept") onAttachmentNotice("That file type isn't supported here.");
|
|
10084
|
+
else onAttachmentNotice(err.message || "Attachment rejected.");
|
|
10085
|
+
},
|
|
10086
|
+
children: [
|
|
10087
|
+
/* @__PURE__ */ jsx35(AttachmentsList, {}),
|
|
10088
|
+
/* @__PURE__ */ jsxs30(
|
|
10089
|
+
"div",
|
|
10090
|
+
{
|
|
10091
|
+
"data-boring-agent-part": "composer-input-row",
|
|
10092
|
+
className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
|
|
10093
|
+
children: [
|
|
10094
|
+
/* @__PURE__ */ jsx35("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx35(AttachmentButton, { disabled: disabled || isStreaming }) }),
|
|
10095
|
+
/* @__PURE__ */ jsx35(
|
|
10096
|
+
PromptInputTextarea,
|
|
10097
|
+
{
|
|
10098
|
+
value: draft,
|
|
10099
|
+
placeholder: composerBlocked ? workspaceWarmupBlocked ? composerBlockerLabel : "" : composerPlaceholder ?? "Ask anything\u2026",
|
|
10100
|
+
disabled,
|
|
10101
|
+
readOnly: composerBlocked,
|
|
10102
|
+
"aria-label": "Agent prompt",
|
|
10103
|
+
ref: (node) => {
|
|
10104
|
+
textareaRef.current = node;
|
|
10105
|
+
resizeTextarea(node);
|
|
10106
|
+
},
|
|
10107
|
+
onChange: onTextareaChange,
|
|
10108
|
+
onKeyDown: onTextareaKeyDown,
|
|
10109
|
+
style: { fieldSizing: "fixed" },
|
|
10110
|
+
className: cn(
|
|
10111
|
+
"min-w-0 flex-1 !min-h-10 !max-h-40 resize-none overflow-hidden border-0 bg-transparent shadow-none",
|
|
10112
|
+
"[field-sizing:fixed]",
|
|
10113
|
+
"px-2 py-2 text-sm leading-6 text-foreground",
|
|
10114
|
+
"placeholder:text-muted-foreground/60",
|
|
10115
|
+
"focus-visible:ring-0 focus-visible:ring-offset-0"
|
|
10116
|
+
)
|
|
10117
|
+
}
|
|
10118
|
+
),
|
|
10119
|
+
/* @__PURE__ */ jsx35(
|
|
10120
|
+
PromptInputFooter,
|
|
10121
|
+
{
|
|
10122
|
+
align: "inline-end",
|
|
10123
|
+
"data-boring-agent-part": "composer-submit-addon",
|
|
10124
|
+
className: "!order-none !w-auto shrink-0 self-center justify-between border-0 bg-transparent !px-2 !py-0",
|
|
10125
|
+
children: /* @__PURE__ */ jsxs30("div", { className: "ml-auto flex items-center gap-1.5", children: [
|
|
10126
|
+
composerContributions.map(({ id, Action }) => Action ? /* @__PURE__ */ jsx35(
|
|
10127
|
+
"span",
|
|
10128
|
+
{
|
|
10129
|
+
"data-boring-agent-part": "composer-contribution-action",
|
|
10130
|
+
"data-boring-agent-contribution-id": id,
|
|
10131
|
+
className: "contents",
|
|
10132
|
+
children: /* @__PURE__ */ jsx35(Action, { updateDraft })
|
|
10133
|
+
},
|
|
10134
|
+
id
|
|
10135
|
+
) : null),
|
|
10136
|
+
/* @__PURE__ */ jsx35(
|
|
10137
|
+
PromptInputSubmit,
|
|
10138
|
+
{
|
|
10139
|
+
"data-boring-agent-part": "composer-submit",
|
|
10140
|
+
status: submitStatus,
|
|
10141
|
+
onStop,
|
|
10142
|
+
disabled: submitDisabled,
|
|
10143
|
+
className: cn(
|
|
10144
|
+
"composer-submit-control h-8 w-8 shrink-0 rounded-full",
|
|
10145
|
+
"bg-foreground text-background",
|
|
10146
|
+
"transition-all duration-150 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
10147
|
+
"hover:bg-foreground/90 hover:shadow-[0_0_0_3px_oklch(from_var(--foreground)_l_c_h/0.12)] hover:scale-[1.04]",
|
|
10148
|
+
"active:scale-[0.93] active:brightness-95",
|
|
10149
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20",
|
|
10150
|
+
"disabled:pointer-events-none disabled:opacity-40",
|
|
10151
|
+
"[&>svg]:size-3.5"
|
|
10152
|
+
)
|
|
10153
|
+
}
|
|
10154
|
+
)
|
|
10155
|
+
] })
|
|
10156
|
+
}
|
|
10157
|
+
)
|
|
10158
|
+
]
|
|
10159
|
+
}
|
|
10160
|
+
)
|
|
10161
|
+
]
|
|
10162
|
+
}
|
|
10163
|
+
)
|
|
9741
10164
|
}
|
|
9742
|
-
)
|
|
9743
|
-
|
|
9744
|
-
|
|
10165
|
+
),
|
|
10166
|
+
hideComposerSettings ? null : /* @__PURE__ */ jsx35(
|
|
10167
|
+
"div",
|
|
9745
10168
|
{
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
"data-composer-multiline": "false",
|
|
9768
|
-
style: { "--composer-input-group-height": `${COMPOSER_INPUT_GROUP_MIN_HEIGHT}px` },
|
|
9769
|
-
className: cn(
|
|
9770
|
-
"relative mx-auto w-full overflow-visible rounded-[28px]",
|
|
9771
|
-
chrome ? "max-w-3xl bg-transparent shadow-[0_1px_2px_-1px_oklch(0_0_0/0.06),0_6px_18px_-12px_oklch(0_0_0/0.12),inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.7)] focus-within:shadow-[0_1px_3px_-1px_oklch(0_0_0/0.08),0_10px_28px_-14px_oklch(0_0_0/0.16),inset_0_0_0_1px_oklch(from_var(--accent)_l_c_h/0.45)]" : "max-w-[680px] bg-transparent shadow-[inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.7)] focus-within:shadow-[inset_0_0_0_1px_oklch(from_var(--accent)_l_c_h/0.45)]",
|
|
9772
|
-
"transition-shadow duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
9773
|
-
"[&_[data-slot=input-group]]:!h-auto [&_[data-slot=input-group]]:!min-h-[var(--composer-input-group-height)]",
|
|
9774
|
-
"[&_[data-slot=input-group]]:!flex-col [&_[data-slot=input-group]]:!items-stretch [&_[data-slot=input-group]]:!overflow-hidden",
|
|
9775
|
-
"[&_[data-slot=input-group]]:!border-0 [&_[data-slot=input-group]]:!rounded-[28px]",
|
|
9776
|
-
"[&_[data-slot=input-group]]:!bg-transparent [&_[data-slot=input-group]]:!shadow-none",
|
|
9777
|
-
"[&_[data-slot=input-group]]:dark:!bg-transparent [&_[data-slot=input-group]]:!ring-0",
|
|
9778
|
-
"[&_[data-slot=input-group]]:has-[:focus]:!ring-0",
|
|
9779
|
-
"[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:self-end",
|
|
9780
|
-
"[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:mb-2"
|
|
9781
|
-
),
|
|
9782
|
-
children: /* @__PURE__ */ jsxs30(
|
|
9783
|
-
PromptInput,
|
|
9784
|
-
{
|
|
9785
|
-
"data-boring-state": status,
|
|
9786
|
-
onSubmit: (message) => onSubmitMessage({ text: message.text, files: message.files }),
|
|
9787
|
-
onUploadFile: uploadAttachment,
|
|
9788
|
-
multiple: true,
|
|
9789
|
-
maxFiles: disabled || isStreaming ? 0 : MAX_PROMPT_ATTACHMENTS,
|
|
9790
|
-
maxFileSize: MAX_PROMPT_ATTACHMENT_BYTES,
|
|
9791
|
-
onError: (err) => {
|
|
9792
|
-
if (err.code === "max_files") onAttachmentNotice(`Up to ${MAX_PROMPT_ATTACHMENTS} attachments per message.`);
|
|
9793
|
-
else if (err.code === "max_file_size") onAttachmentNotice("Files must be under 4 MB each.");
|
|
9794
|
-
else if (err.code === "accept") onAttachmentNotice("That file type isn't supported here.");
|
|
9795
|
-
else onAttachmentNotice(err.message || "Attachment rejected.");
|
|
9796
|
-
},
|
|
9797
|
-
children: [
|
|
9798
|
-
/* @__PURE__ */ jsx35(AttachmentsList, {}),
|
|
9799
|
-
/* @__PURE__ */ jsxs30(
|
|
9800
|
-
"div",
|
|
9801
|
-
{
|
|
9802
|
-
"data-boring-agent-part": "composer-input-row",
|
|
9803
|
-
className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
|
|
9804
|
-
children: [
|
|
9805
|
-
/* @__PURE__ */ jsx35("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx35(AttachmentButton, { disabled: disabled || isStreaming }) }),
|
|
9806
|
-
/* @__PURE__ */ jsx35(
|
|
9807
|
-
PromptInputTextarea,
|
|
9808
|
-
{
|
|
9809
|
-
value: draft,
|
|
9810
|
-
placeholder: composerBlocked ? workspaceWarmupBlocked ? composerBlockerLabel : "" : composerPlaceholder ?? "Ask anything\u2026",
|
|
9811
|
-
disabled,
|
|
9812
|
-
readOnly: composerBlocked,
|
|
9813
|
-
"aria-label": "Agent prompt",
|
|
9814
|
-
ref: (node) => {
|
|
9815
|
-
textareaRef.current = node;
|
|
9816
|
-
resizeTextarea(node);
|
|
9817
|
-
},
|
|
9818
|
-
onChange: onTextareaChange,
|
|
9819
|
-
onKeyDown: onTextareaKeyDown,
|
|
9820
|
-
style: { fieldSizing: "fixed" },
|
|
9821
|
-
className: cn(
|
|
9822
|
-
"min-w-0 flex-1 !min-h-10 !max-h-40 resize-none overflow-hidden border-0 bg-transparent shadow-none",
|
|
9823
|
-
"[field-sizing:fixed]",
|
|
9824
|
-
"px-2 py-2 text-[13px] leading-6",
|
|
9825
|
-
"placeholder:text-muted-foreground/45",
|
|
9826
|
-
"focus-visible:ring-0 focus-visible:ring-offset-0"
|
|
9827
|
-
)
|
|
9828
|
-
}
|
|
9829
|
-
),
|
|
9830
|
-
/* @__PURE__ */ jsx35(
|
|
9831
|
-
PromptInputFooter,
|
|
9832
|
-
{
|
|
9833
|
-
align: "inline-end",
|
|
9834
|
-
"data-boring-agent-part": "composer-submit-addon",
|
|
9835
|
-
className: "!order-none !w-auto shrink-0 self-center justify-between border-0 bg-transparent !px-2 !py-0",
|
|
9836
|
-
children: /* @__PURE__ */ jsxs30("div", { className: "ml-auto flex items-center gap-1.5", children: [
|
|
9837
|
-
composerContributions.map(({ id, Action }) => Action ? /* @__PURE__ */ jsx35(
|
|
9838
|
-
"span",
|
|
9839
|
-
{
|
|
9840
|
-
"data-boring-agent-part": "composer-contribution-action",
|
|
9841
|
-
"data-boring-agent-contribution-id": id,
|
|
9842
|
-
className: "contents",
|
|
9843
|
-
children: /* @__PURE__ */ jsx35(Action, { updateDraft })
|
|
9844
|
-
},
|
|
9845
|
-
id
|
|
9846
|
-
) : null),
|
|
9847
|
-
/* @__PURE__ */ jsx35(
|
|
9848
|
-
PromptInputSubmit,
|
|
9849
|
-
{
|
|
9850
|
-
"data-boring-agent-part": "composer-submit",
|
|
9851
|
-
status: submitStatus,
|
|
9852
|
-
onStop,
|
|
9853
|
-
disabled: submitDisabled,
|
|
9854
|
-
className: cn(
|
|
9855
|
-
"h-8 w-8 shrink-0 rounded-full",
|
|
9856
|
-
"bg-foreground",
|
|
9857
|
-
"text-background",
|
|
9858
|
-
"transition-all duration-150 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
9859
|
-
"hover:bg-foreground/90 hover:shadow-[0_0_0_3px_oklch(from_var(--foreground)_l_c_h/0.12)] hover:scale-[1.04]",
|
|
9860
|
-
"active:scale-[0.93] active:brightness-95",
|
|
9861
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20",
|
|
9862
|
-
"disabled:pointer-events-none disabled:opacity-40",
|
|
9863
|
-
"[&>svg]:size-3.5"
|
|
9864
|
-
)
|
|
9865
|
-
}
|
|
9866
|
-
)
|
|
9867
|
-
] })
|
|
9868
|
-
}
|
|
9869
|
-
)
|
|
9870
|
-
]
|
|
10169
|
+
"data-boring-agent-part": "composer-settings-row",
|
|
10170
|
+
className: cn(
|
|
10171
|
+
"mx-auto mt-2 grid min-h-8 w-full grid-cols-[minmax(0,1fr)_auto] items-center text-xs text-muted-foreground",
|
|
10172
|
+
chrome ? "max-w-3xl" : "max-w-[680px]"
|
|
10173
|
+
),
|
|
10174
|
+
children: /* @__PURE__ */ jsxs30("div", { className: "flex min-w-0 items-center justify-center gap-1", children: [
|
|
10175
|
+
/* @__PURE__ */ jsx35(
|
|
10176
|
+
ModelSelectTrigger,
|
|
10177
|
+
{
|
|
10178
|
+
value: selectedModel,
|
|
10179
|
+
options: modelOptions,
|
|
10180
|
+
disabled: isStreaming || modelControlled,
|
|
10181
|
+
trigger: "slash",
|
|
10182
|
+
open: modelPickerOpen,
|
|
10183
|
+
className: "composer-settings-trigger",
|
|
10184
|
+
onClick: () => {
|
|
10185
|
+
if (modelPickerOpen) {
|
|
10186
|
+
onSetModelPickerOpen(false);
|
|
10187
|
+
return;
|
|
10188
|
+
}
|
|
10189
|
+
void onOpenModelPicker();
|
|
9871
10190
|
}
|
|
9872
|
-
)
|
|
9873
|
-
]
|
|
9874
|
-
}
|
|
9875
|
-
)
|
|
9876
|
-
}
|
|
9877
|
-
),
|
|
9878
|
-
hideComposerSettings ? null : /* @__PURE__ */ jsxs30(
|
|
9879
|
-
"div",
|
|
9880
|
-
{
|
|
9881
|
-
"data-boring-agent-part": "composer-settings-row",
|
|
9882
|
-
className: cn(
|
|
9883
|
-
"mx-auto mt-1.5 flex w-full items-center justify-center gap-1.5 text-[10.5px] text-muted-foreground/45",
|
|
9884
|
-
chrome ? "max-w-3xl" : "max-w-[680px]"
|
|
9885
|
-
),
|
|
9886
|
-
children: [
|
|
9887
|
-
/* @__PURE__ */ jsx35(
|
|
9888
|
-
ModelSelectTrigger,
|
|
9889
|
-
{
|
|
9890
|
-
value: selectedModel,
|
|
9891
|
-
options: modelOptions,
|
|
9892
|
-
disabled: isStreaming || modelControlled,
|
|
9893
|
-
trigger: "slash",
|
|
9894
|
-
open: modelPickerOpen,
|
|
9895
|
-
onClick: () => {
|
|
9896
|
-
if (modelPickerOpen) {
|
|
9897
|
-
onSetModelPickerOpen(false);
|
|
9898
|
-
return;
|
|
9899
|
-
}
|
|
9900
|
-
void onOpenModelPicker();
|
|
9901
10191
|
}
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
10192
|
+
),
|
|
10193
|
+
thinkingControl ? /* @__PURE__ */ jsx35(
|
|
10194
|
+
ThinkingSelectTrigger,
|
|
10195
|
+
{
|
|
10196
|
+
value: selectedThinking,
|
|
10197
|
+
disabled: isStreaming || thinkingControlled,
|
|
10198
|
+
trigger: "slash",
|
|
10199
|
+
open: thinkingPickerOpen,
|
|
10200
|
+
className: "composer-settings-trigger",
|
|
10201
|
+
onClick: () => {
|
|
10202
|
+
if (thinkingPickerOpen) {
|
|
10203
|
+
onSetThinkingPickerOpen(false);
|
|
10204
|
+
return;
|
|
10205
|
+
}
|
|
10206
|
+
void onOpenThinkingPicker();
|
|
9915
10207
|
}
|
|
9916
|
-
void onOpenThinkingPicker();
|
|
9917
10208
|
}
|
|
9918
|
-
|
|
9919
|
-
)
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
)
|
|
10209
|
+
) : null
|
|
10210
|
+
] })
|
|
10211
|
+
}
|
|
10212
|
+
)
|
|
10213
|
+
] })
|
|
9923
10214
|
] });
|
|
9924
10215
|
}
|
|
9925
10216
|
function getHeaderValue(headers, name) {
|
|
@@ -9929,86 +10220,6 @@ function getHeaderValue(headers, name) {
|
|
|
9929
10220
|
|
|
9930
10221
|
// src/front/chat/piChatPanelHooks.ts
|
|
9931
10222
|
import { useCallback as useCallback17, useEffect as useEffect20, useMemo as useMemo15, useRef as useRef17, useState as useState19, useSyncExternalStore } from "react";
|
|
9932
|
-
function useExternalRemotePiSession({
|
|
9933
|
-
sessionId,
|
|
9934
|
-
agentTypeId,
|
|
9935
|
-
workspaceId,
|
|
9936
|
-
storageScope,
|
|
9937
|
-
apiBaseUrl,
|
|
9938
|
-
requestHeaders,
|
|
9939
|
-
fetch: fetch2,
|
|
9940
|
-
createRemoteSession,
|
|
9941
|
-
remoteSessionOptions
|
|
9942
|
-
}) {
|
|
9943
|
-
const [session, setSession] = useState19();
|
|
9944
|
-
const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
|
|
9945
|
-
remoteSessionOptionsRef.current = remoteSessionOptions;
|
|
9946
|
-
const remoteSessionOptionsKey = useMemo15(
|
|
9947
|
-
() => remoteSessionOptionsIdentity2(remoteSessionOptions),
|
|
9948
|
-
[remoteSessionOptions]
|
|
9949
|
-
);
|
|
9950
|
-
useEffect20(() => {
|
|
9951
|
-
if (!sessionId) {
|
|
9952
|
-
setSession(void 0);
|
|
9953
|
-
return;
|
|
9954
|
-
}
|
|
9955
|
-
const next = (createRemoteSession ?? createRemotePiSession)({
|
|
9956
|
-
...remoteSessionOptionsRef.current,
|
|
9957
|
-
sessionId,
|
|
9958
|
-
agentTypeId,
|
|
9959
|
-
workspaceId,
|
|
9960
|
-
storageScope,
|
|
9961
|
-
apiBaseUrl,
|
|
9962
|
-
headers: requestHeaders,
|
|
9963
|
-
fetch: fetch2
|
|
9964
|
-
});
|
|
9965
|
-
setSession(next);
|
|
9966
|
-
return () => next.dispose();
|
|
9967
|
-
}, [agentTypeId, apiBaseUrl, createRemoteSession, fetch2, remoteSessionOptionsKey, requestHeaders, sessionId, storageScope, workspaceId]);
|
|
9968
|
-
return session;
|
|
9969
|
-
}
|
|
9970
|
-
var remoteSessionOptionObjectIds2 = /* @__PURE__ */ new WeakMap();
|
|
9971
|
-
var remoteSessionOptionObjectSeq2 = 0;
|
|
9972
|
-
function remoteSessionOptionObjectIdentity2(value) {
|
|
9973
|
-
if (typeof value !== "object" && typeof value !== "function" || value === null) return void 0;
|
|
9974
|
-
const object = value;
|
|
9975
|
-
let id = remoteSessionOptionObjectIds2.get(object);
|
|
9976
|
-
if (!id) {
|
|
9977
|
-
id = ++remoteSessionOptionObjectSeq2;
|
|
9978
|
-
remoteSessionOptionObjectIds2.set(object, id);
|
|
9979
|
-
}
|
|
9980
|
-
return String(id);
|
|
9981
|
-
}
|
|
9982
|
-
function remoteSessionOptionsIdentity2(options) {
|
|
9983
|
-
if (!options) return "{}";
|
|
9984
|
-
return JSON.stringify({
|
|
9985
|
-
autoStart: options.autoStart,
|
|
9986
|
-
requestTimeoutMs: options.requestTimeoutMs,
|
|
9987
|
-
onEvent: remoteSessionOptionObjectIdentity2(options.onEvent),
|
|
9988
|
-
storeOptions: remoteSessionOptionObjectIdentity2(options.storeOptions),
|
|
9989
|
-
setTimeoutFn: remoteSessionOptionObjectIdentity2(options.setTimeoutFn),
|
|
9990
|
-
clearTimeoutFn: remoteSessionOptionObjectIdentity2(options.clearTimeoutFn),
|
|
9991
|
-
reconnect: options.reconnect ? {
|
|
9992
|
-
baseMs: options.reconnect.baseMs,
|
|
9993
|
-
maxMs: options.reconnect.maxMs,
|
|
9994
|
-
jitterRatio: options.reconnect.jitterRatio,
|
|
9995
|
-
random: remoteSessionOptionObjectIdentity2(options.reconnect.random)
|
|
9996
|
-
} : void 0,
|
|
9997
|
-
debug: options.debug ? {
|
|
9998
|
-
largeStateWarningBytes: options.debug.largeStateWarningBytes,
|
|
9999
|
-
largeStateWarningMessages: options.debug.largeStateWarningMessages,
|
|
10000
|
-
onWarning: remoteSessionOptionObjectIdentity2(options.debug.onWarning)
|
|
10001
|
-
} : void 0
|
|
10002
|
-
});
|
|
10003
|
-
}
|
|
10004
|
-
function useRemotePiSessionState(session) {
|
|
10005
|
-
return useSyncExternalStore(
|
|
10006
|
-
useCallback17((listener) => session?.subscribe(listener) ?? (() => {
|
|
10007
|
-
}), [session]),
|
|
10008
|
-
useCallback17(() => session?.getState(), [session]),
|
|
10009
|
-
useCallback17(() => session?.getState(), [session])
|
|
10010
|
-
);
|
|
10011
|
-
}
|
|
10012
10223
|
|
|
10013
10224
|
// src/front/chat/piChatPanelUtils.ts
|
|
10014
10225
|
function headersContentKey(headers) {
|
|
@@ -10115,9 +10326,103 @@ function errorMessage2(error, fallback) {
|
|
|
10115
10326
|
return error instanceof Error && error.message ? error.message : fallback;
|
|
10116
10327
|
}
|
|
10117
10328
|
|
|
10329
|
+
// src/front/chat/piChatPanelHooks.ts
|
|
10330
|
+
function useExternalRemotePiSession({
|
|
10331
|
+
sessionId,
|
|
10332
|
+
agentTypeId,
|
|
10333
|
+
workspaceId,
|
|
10334
|
+
storageScope,
|
|
10335
|
+
apiBaseUrl,
|
|
10336
|
+
requestHeaders,
|
|
10337
|
+
fetch: fetch2,
|
|
10338
|
+
createRemoteSession,
|
|
10339
|
+
remoteSessionOptions,
|
|
10340
|
+
enabled = true
|
|
10341
|
+
}) {
|
|
10342
|
+
const [session, setSession] = useState19();
|
|
10343
|
+
const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
|
|
10344
|
+
remoteSessionOptionsRef.current = remoteSessionOptions;
|
|
10345
|
+
const requestHeadersKey = headersContentKey(requestHeaders);
|
|
10346
|
+
const stableRequestHeaders = useMemo15(
|
|
10347
|
+
() => normalizedHeadersFromContentKey(requestHeadersKey),
|
|
10348
|
+
[requestHeadersKey]
|
|
10349
|
+
);
|
|
10350
|
+
const remoteSessionOptionsKey = useMemo15(
|
|
10351
|
+
() => remoteSessionOptionsIdentity2(remoteSessionOptions),
|
|
10352
|
+
[remoteSessionOptions]
|
|
10353
|
+
);
|
|
10354
|
+
useEffect20(() => {
|
|
10355
|
+
if (!sessionId) {
|
|
10356
|
+
setSession(void 0);
|
|
10357
|
+
return;
|
|
10358
|
+
}
|
|
10359
|
+
const next = (createRemoteSession ?? createRemotePiSession)({
|
|
10360
|
+
...remoteSessionOptionsRef.current,
|
|
10361
|
+
autoStart: false,
|
|
10362
|
+
sessionId,
|
|
10363
|
+
agentTypeId,
|
|
10364
|
+
workspaceId,
|
|
10365
|
+
storageScope,
|
|
10366
|
+
apiBaseUrl,
|
|
10367
|
+
headers: stableRequestHeaders,
|
|
10368
|
+
fetch: fetch2
|
|
10369
|
+
});
|
|
10370
|
+
setSession(next);
|
|
10371
|
+
return () => next.dispose();
|
|
10372
|
+
}, [agentTypeId, apiBaseUrl, createRemoteSession, fetch2, remoteSessionOptionsKey, sessionId, stableRequestHeaders, storageScope, workspaceId]);
|
|
10373
|
+
useEffect20(() => {
|
|
10374
|
+
if (!session) return;
|
|
10375
|
+
if (enabled) session.resumeStream?.();
|
|
10376
|
+
else session.suspendStream?.();
|
|
10377
|
+
}, [enabled, session]);
|
|
10378
|
+
return session;
|
|
10379
|
+
}
|
|
10380
|
+
var remoteSessionOptionObjectIds2 = /* @__PURE__ */ new WeakMap();
|
|
10381
|
+
var remoteSessionOptionObjectSeq2 = 0;
|
|
10382
|
+
function remoteSessionOptionObjectIdentity2(value) {
|
|
10383
|
+
if (typeof value !== "object" && typeof value !== "function" || value === null) return void 0;
|
|
10384
|
+
const object = value;
|
|
10385
|
+
let id = remoteSessionOptionObjectIds2.get(object);
|
|
10386
|
+
if (!id) {
|
|
10387
|
+
id = ++remoteSessionOptionObjectSeq2;
|
|
10388
|
+
remoteSessionOptionObjectIds2.set(object, id);
|
|
10389
|
+
}
|
|
10390
|
+
return String(id);
|
|
10391
|
+
}
|
|
10392
|
+
function remoteSessionOptionsIdentity2(options) {
|
|
10393
|
+
if (!options) return "{}";
|
|
10394
|
+
return JSON.stringify({
|
|
10395
|
+
autoStart: options.autoStart,
|
|
10396
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
10397
|
+
onEvent: remoteSessionOptionObjectIdentity2(options.onEvent),
|
|
10398
|
+
storeOptions: remoteSessionOptionObjectIdentity2(options.storeOptions),
|
|
10399
|
+
setTimeoutFn: remoteSessionOptionObjectIdentity2(options.setTimeoutFn),
|
|
10400
|
+
clearTimeoutFn: remoteSessionOptionObjectIdentity2(options.clearTimeoutFn),
|
|
10401
|
+
reconnect: options.reconnect ? {
|
|
10402
|
+
baseMs: options.reconnect.baseMs,
|
|
10403
|
+
maxMs: options.reconnect.maxMs,
|
|
10404
|
+
jitterRatio: options.reconnect.jitterRatio,
|
|
10405
|
+
random: remoteSessionOptionObjectIdentity2(options.reconnect.random)
|
|
10406
|
+
} : void 0,
|
|
10407
|
+
debug: options.debug ? {
|
|
10408
|
+
largeStateWarningBytes: options.debug.largeStateWarningBytes,
|
|
10409
|
+
largeStateWarningMessages: options.debug.largeStateWarningMessages,
|
|
10410
|
+
onWarning: remoteSessionOptionObjectIdentity2(options.debug.onWarning)
|
|
10411
|
+
} : void 0
|
|
10412
|
+
});
|
|
10413
|
+
}
|
|
10414
|
+
function useRemotePiSessionState(session) {
|
|
10415
|
+
return useSyncExternalStore(
|
|
10416
|
+
useCallback17((listener) => session?.subscribe(listener) ?? (() => {
|
|
10417
|
+
}), [session]),
|
|
10418
|
+
useCallback17(() => session?.getState(), [session]),
|
|
10419
|
+
useCallback17(() => session?.getState(), [session])
|
|
10420
|
+
);
|
|
10421
|
+
}
|
|
10422
|
+
|
|
10118
10423
|
// src/front/chat/PiChatPanel.tsx
|
|
10119
10424
|
import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
10120
|
-
var DebugDrawer2 = lazy(() => import("../DebugDrawer-
|
|
10425
|
+
var DebugDrawer2 = lazy(() => import("../DebugDrawer-2TO5SE2D.js").then((m) => ({ default: m.DebugDrawer })));
|
|
10121
10426
|
var EMPTY_COMMANDS = [];
|
|
10122
10427
|
var EMPTY_COMMAND_NAMES = [];
|
|
10123
10428
|
var EMPTY_BLOCKERS = [];
|
|
@@ -10146,7 +10451,7 @@ function PiChatPanel({
|
|
|
10146
10451
|
hotReloadEnabled = true,
|
|
10147
10452
|
suggestions = defaultChatSuggestions,
|
|
10148
10453
|
emptyState,
|
|
10149
|
-
emptyPlacement = "
|
|
10454
|
+
emptyPlacement = "hero",
|
|
10150
10455
|
composerPlaceholder,
|
|
10151
10456
|
initialDraft,
|
|
10152
10457
|
autoSubmitInitialDraft = false,
|
|
@@ -10253,6 +10558,7 @@ function PiChatPanel({
|
|
|
10253
10558
|
const selectedSessionPending = Boolean(activeSessionId && !selectedChatState);
|
|
10254
10559
|
const modelDiscoveryEnabled = serverResourcesEnabled && availableModels === void 0;
|
|
10255
10560
|
const modelDiscovery = useChatModelSelection({
|
|
10561
|
+
agentTypeId,
|
|
10256
10562
|
apiBaseUrl,
|
|
10257
10563
|
defaultModel,
|
|
10258
10564
|
fetch: fetch2,
|
|
@@ -10356,6 +10662,7 @@ function PiChatPanel({
|
|
|
10356
10662
|
return next;
|
|
10357
10663
|
}, [apiBaseUrl, commands, contributedCommands, excludeBuiltinCommands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
|
|
10358
10664
|
const commandsStamp = useServerCommands({
|
|
10665
|
+
agentTypeId,
|
|
10359
10666
|
registry,
|
|
10360
10667
|
requestHeaders: normalizedRequestHeaders,
|
|
10361
10668
|
sessionId: activeSessionId ?? "default",
|
|
@@ -10385,7 +10692,8 @@ function PiChatPanel({
|
|
|
10385
10692
|
const queuePreview = selectedChatState ? selectQueuePreview(selectedChatState) : [];
|
|
10386
10693
|
const messages = canonicalMessages;
|
|
10387
10694
|
const userHistory = useMemo16(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
|
|
10388
|
-
const
|
|
10695
|
+
const awaitingHydration = Boolean(hydrateMessages && selectedChatState && !selectedChatState.hydrated);
|
|
10696
|
+
const emptyStateHydrating = statusForState(selectedChatState, sessionsLoading || chatStatePending || selectedSessionPending) === "hydrating" || awaitingHydration;
|
|
10389
10697
|
const emptyHero = emptyPlacement === "hero" && messages.length === 0 && queuePreview.length === 0 && !emptyStateHydrating;
|
|
10390
10698
|
const debugState = selectedPiSession?.getDebugState();
|
|
10391
10699
|
const composerBlocked = workspaceWarmupBlocked || activeBlockers.length > 0;
|
|
@@ -10401,8 +10709,9 @@ function PiChatPanel({
|
|
|
10401
10709
|
text: `Large chat state: ${debugState.largeStateWarning.messageCount} messages, approximately ${debugState.largeStateWarning.approxBytes} bytes.`,
|
|
10402
10710
|
dismissible: true
|
|
10403
10711
|
}] : [];
|
|
10404
|
-
|
|
10405
|
-
|
|
10712
|
+
const combined = [...fromState, ...sessionNotice, ...largeStateNotice, ...localNotices].filter((notice) => !dismissedNoticeIds.has(notice.id));
|
|
10713
|
+
return filterCompetingNoiseNotices(combined, messages.length === 0);
|
|
10714
|
+
}, [debug, debugState?.largeStateWarning, dismissedNoticeIds, localNotices, messages.length, selectedChatState, sessionsError]);
|
|
10406
10715
|
const addLocalNotice = useCallback18((notice) => {
|
|
10407
10716
|
setLocalNotices((previous) => {
|
|
10408
10717
|
const next = previous.filter((candidate) => candidate.id !== notice.id);
|
|
@@ -10433,11 +10742,11 @@ function PiChatPanel({
|
|
|
10433
10742
|
if (resetInProgressRef.current) return;
|
|
10434
10743
|
if (autoCreateInFlightRef.current) return;
|
|
10435
10744
|
autoCreateInFlightRef.current = true;
|
|
10436
|
-
void sessions.create().catch((error) => {
|
|
10745
|
+
void sessions.create({ resumeSessionId: sessions.resumeSessionId }).catch((error) => {
|
|
10437
10746
|
autoCreateInFlightRef.current = false;
|
|
10438
10747
|
addLocalNotice({ id: "session-auto-create-error", level: "error", text: errorMessage2(error, "Could not create a chat session."), dismissible: true });
|
|
10439
10748
|
});
|
|
10440
|
-
}, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessionsError, sessionsLoading]);
|
|
10749
|
+
}, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessions.resumeSessionId, sessionsError, sessionsLoading]);
|
|
10441
10750
|
useEffect21(() => {
|
|
10442
10751
|
if (externalSessionId || sessionsError || activeSessionId || sessionList.length > 0) {
|
|
10443
10752
|
autoCreateInFlightRef.current = false;
|
|
@@ -10885,14 +11194,18 @@ function PiChatPanel({
|
|
|
10885
11194
|
}, [isStreaming]);
|
|
10886
11195
|
useEffect21(() => {
|
|
10887
11196
|
if (typeof window === "undefined" || !activeChatSessionId) return;
|
|
10888
|
-
window.dispatchEvent(new CustomEvent("boring:chat-session-status", {
|
|
11197
|
+
const emitStatus = () => window.dispatchEvent(new CustomEvent("boring:chat-session-status", {
|
|
10889
11198
|
detail: {
|
|
11199
|
+
...workspaceId ? { workspaceId } : {},
|
|
10890
11200
|
sessionId: activeChatSessionId,
|
|
10891
11201
|
...agentTypeId ? { agentTypeId } : {},
|
|
10892
11202
|
working: isStreaming
|
|
10893
11203
|
}
|
|
10894
11204
|
}));
|
|
10895
|
-
|
|
11205
|
+
emitStatus();
|
|
11206
|
+
window.addEventListener("boring:chat-session-status-request", emitStatus);
|
|
11207
|
+
return () => window.removeEventListener("boring:chat-session-status-request", emitStatus);
|
|
11208
|
+
}, [activeChatSessionId, agentTypeId, isStreaming, workspaceId]);
|
|
10896
11209
|
const onTextareaKeyDown = useCallback18((event) => {
|
|
10897
11210
|
if (event.key === "Escape" && isStreaming) {
|
|
10898
11211
|
if (event.defaultPrevented || mentionState !== null || slashQuery !== null) {
|
|
@@ -10905,11 +11218,84 @@ function PiChatPanel({
|
|
|
10905
11218
|
}
|
|
10906
11219
|
handleComposerKeyDown(event);
|
|
10907
11220
|
}, [handleComposerKeyDown, interrupt, isStreaming, mentionState, slashQuery]);
|
|
11221
|
+
const selectEmptySuggestion = useCallback18((suggestion) => {
|
|
11222
|
+
const text = suggestion.prompt ?? suggestion.label;
|
|
11223
|
+
if (!text.trim()) return;
|
|
11224
|
+
void sendComposerMessage({ text, files: [], source: "suggestion" }).then((result) => {
|
|
11225
|
+
if (result === false) setComposerDraft(text);
|
|
11226
|
+
});
|
|
11227
|
+
}, [sendComposerMessage, setComposerDraft]);
|
|
11228
|
+
const composerSurface = /* @__PURE__ */ jsx36(
|
|
11229
|
+
PiChatComposerSurface,
|
|
11230
|
+
{
|
|
11231
|
+
chrome,
|
|
11232
|
+
pickerPlacement: emptyHero ? "above-compact" : "above",
|
|
11233
|
+
isStreaming,
|
|
11234
|
+
status,
|
|
11235
|
+
disabled,
|
|
11236
|
+
submitStatus,
|
|
11237
|
+
submitDisabled,
|
|
11238
|
+
composerBlocked,
|
|
11239
|
+
composerBlockerLabel,
|
|
11240
|
+
composerPlaceholder,
|
|
11241
|
+
composerStatusNotice,
|
|
11242
|
+
workspaceWarmupBlocked,
|
|
11243
|
+
primaryComposerBlocker,
|
|
11244
|
+
onComposerBlockerAction,
|
|
11245
|
+
queuePreview,
|
|
11246
|
+
onEditQueued: editQueued,
|
|
11247
|
+
hotReloadEnabled,
|
|
11248
|
+
pluginUpdateState,
|
|
11249
|
+
onDismissPluginUpdate: dismissPluginUpdate,
|
|
11250
|
+
onRunPluginUpdate: runPluginUpdate,
|
|
11251
|
+
commandNotifyState,
|
|
11252
|
+
onDismissCommandNotify: dismissCommandNotify,
|
|
11253
|
+
attachmentNotice,
|
|
11254
|
+
onAttachmentNotice: setAttachmentNotice,
|
|
11255
|
+
mentionState,
|
|
11256
|
+
slashQuery,
|
|
11257
|
+
apiBaseUrl,
|
|
11258
|
+
fetch: fetch2,
|
|
11259
|
+
requestHeaders: normalizedRequestHeaders,
|
|
11260
|
+
storageScope,
|
|
11261
|
+
onSelectMention: selectMention,
|
|
11262
|
+
onDismissMention: dismissMention,
|
|
11263
|
+
commands: allCommands,
|
|
11264
|
+
onSelectSlashCommand: selectSlashCommand,
|
|
11265
|
+
onDismissSlash: dismissSlash,
|
|
11266
|
+
modelPickerOpen,
|
|
11267
|
+
selectedModel,
|
|
11268
|
+
modelOptions,
|
|
11269
|
+
modelControlled: model !== void 0,
|
|
11270
|
+
hideDefaultModelOption,
|
|
11271
|
+
hideComposerSettings,
|
|
11272
|
+
onModelChange: modelDiscovery.setModel,
|
|
11273
|
+
onSetModelPickerOpen: setModelPickerOpen,
|
|
11274
|
+
onOpenModelPicker: openModelPicker,
|
|
11275
|
+
thinkingPickerOpen,
|
|
11276
|
+
selectedThinking,
|
|
11277
|
+
thinkingControl,
|
|
11278
|
+
thinkingControlled: thinkingLevel !== void 0,
|
|
11279
|
+
onThinkingChange: setStoredThinkingLevel,
|
|
11280
|
+
onSetThinkingPickerOpen: setThinkingPickerOpen,
|
|
11281
|
+
onOpenThinkingPicker: openThinkingPicker,
|
|
11282
|
+
draft,
|
|
11283
|
+
textareaRef,
|
|
11284
|
+
onTextareaChange,
|
|
11285
|
+
onTextareaKeyDown,
|
|
11286
|
+
updateDraft: (update, options) => {
|
|
11287
|
+
setComposerDraft(update(draftRef.current), options?.focus ?? true);
|
|
11288
|
+
},
|
|
11289
|
+
onSubmitMessage: ({ text, files }) => sendComposerMessage({ text, files }),
|
|
11290
|
+
onStop: stop
|
|
11291
|
+
}
|
|
11292
|
+
);
|
|
10908
11293
|
return /* @__PURE__ */ jsx36(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
|
|
10909
11294
|
"div",
|
|
10910
11295
|
{
|
|
10911
11296
|
"data-boring-agent": "",
|
|
10912
11297
|
"data-boring-agent-part": "chat",
|
|
11298
|
+
"data-agent-type-id": agentTypeId,
|
|
10913
11299
|
"data-pi-chat-session-id": activeSessionId,
|
|
10914
11300
|
"data-pi-chat-connection": debugState?.connection ?? "disconnected",
|
|
10915
11301
|
"data-pi-chat-last-seq": debugState?.lastSeq ?? 0,
|
|
@@ -10940,8 +11326,8 @@ function PiChatPanel({
|
|
|
10940
11326
|
"div",
|
|
10941
11327
|
{
|
|
10942
11328
|
className: cn(
|
|
10943
|
-
"flex h-full min-h-0 flex-col
|
|
10944
|
-
emptyHero
|
|
11329
|
+
"flex h-full min-h-0 flex-col",
|
|
11330
|
+
emptyHero ? "justify-center overflow-y-auto py-6" : "overflow-hidden",
|
|
10945
11331
|
chrome && "mx-3 my-3 rounded-xl bg-[color:var(--surface-chat)] shadow-[0_1px_0_oklch(0_0_0/0.02),0_1px_2px_-1px_oklch(0_0_0/0.04),inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.6)]"
|
|
10946
11332
|
),
|
|
10947
11333
|
children: [
|
|
@@ -10970,76 +11356,25 @@ function PiChatPanel({
|
|
|
10970
11356
|
windowResetKey: activeSessionId
|
|
10971
11357
|
}
|
|
10972
11358
|
),
|
|
10973
|
-
|
|
10974
|
-
|
|
11359
|
+
composerSurface,
|
|
11360
|
+
emptyHero ? /* @__PURE__ */ jsxs31(
|
|
11361
|
+
"div",
|
|
10975
11362
|
{
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
composerBlocked,
|
|
10983
|
-
composerBlockerLabel,
|
|
10984
|
-
composerPlaceholder,
|
|
10985
|
-
composerStatusNotice,
|
|
10986
|
-
workspaceWarmupBlocked,
|
|
10987
|
-
primaryComposerBlocker,
|
|
10988
|
-
onComposerBlockerAction,
|
|
10989
|
-
queuePreview,
|
|
10990
|
-
onEditQueued: editQueued,
|
|
10991
|
-
hotReloadEnabled,
|
|
10992
|
-
pluginUpdateState,
|
|
10993
|
-
onDismissPluginUpdate: dismissPluginUpdate,
|
|
10994
|
-
onRunPluginUpdate: runPluginUpdate,
|
|
10995
|
-
commandNotifyState,
|
|
10996
|
-
onDismissCommandNotify: dismissCommandNotify,
|
|
10997
|
-
attachmentNotice,
|
|
10998
|
-
onAttachmentNotice: setAttachmentNotice,
|
|
10999
|
-
mentionState,
|
|
11000
|
-
slashQuery,
|
|
11001
|
-
apiBaseUrl,
|
|
11002
|
-
fetch: fetch2,
|
|
11003
|
-
requestHeaders: normalizedRequestHeaders,
|
|
11004
|
-
storageScope,
|
|
11005
|
-
onSelectMention: selectMention,
|
|
11006
|
-
onDismissMention: dismissMention,
|
|
11007
|
-
commands: allCommands,
|
|
11008
|
-
onSelectSlashCommand: selectSlashCommand,
|
|
11009
|
-
onDismissSlash: dismissSlash,
|
|
11010
|
-
modelPickerOpen,
|
|
11011
|
-
selectedModel,
|
|
11012
|
-
modelOptions,
|
|
11013
|
-
modelControlled: model !== void 0,
|
|
11014
|
-
hideDefaultModelOption,
|
|
11015
|
-
hideComposerSettings,
|
|
11016
|
-
onModelChange: modelDiscovery.setModel,
|
|
11017
|
-
onSetModelPickerOpen: setModelPickerOpen,
|
|
11018
|
-
onOpenModelPicker: openModelPicker,
|
|
11019
|
-
thinkingPickerOpen,
|
|
11020
|
-
selectedThinking,
|
|
11021
|
-
thinkingControl,
|
|
11022
|
-
thinkingControlled: thinkingLevel !== void 0,
|
|
11023
|
-
onThinkingChange: setStoredThinkingLevel,
|
|
11024
|
-
onSetThinkingPickerOpen: setThinkingPickerOpen,
|
|
11025
|
-
onOpenThinkingPicker: openThinkingPicker,
|
|
11026
|
-
draft,
|
|
11027
|
-
textareaRef,
|
|
11028
|
-
onTextareaChange,
|
|
11029
|
-
onTextareaKeyDown,
|
|
11030
|
-
updateDraft: (update, options) => {
|
|
11031
|
-
setComposerDraft(update(draftRef.current), options?.focus ?? true);
|
|
11032
|
-
},
|
|
11033
|
-
onSubmitMessage: ({ text, files }) => sendComposerMessage({ text, files }),
|
|
11034
|
-
onStop: stop
|
|
11363
|
+
"data-boring-agent-part": "empty-hero-suggestions",
|
|
11364
|
+
className: "@container mx-auto w-full max-w-[640px] px-2 pb-4",
|
|
11365
|
+
children: [
|
|
11366
|
+
/* @__PURE__ */ jsx36(ChatSuggestionGrid, { suggestions, onSelect: selectEmptySuggestion, className: "mt-4" }),
|
|
11367
|
+
emptyState?.footer
|
|
11368
|
+
]
|
|
11035
11369
|
}
|
|
11036
|
-
)
|
|
11370
|
+
) : null
|
|
11037
11371
|
]
|
|
11038
11372
|
}
|
|
11039
11373
|
) }),
|
|
11040
11374
|
debug ? /* @__PURE__ */ jsx36(Suspense, { fallback: null, children: /* @__PURE__ */ jsx36("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx36(
|
|
11041
11375
|
DebugDrawer2,
|
|
11042
11376
|
{
|
|
11377
|
+
agentTypeId,
|
|
11043
11378
|
apiBaseUrl,
|
|
11044
11379
|
fetch: fetch2,
|
|
11045
11380
|
sessionId: activeSessionId ?? activeChatSessionId ?? "unknown",
|
|
@@ -11056,6 +11391,207 @@ function PiChatPanel({
|
|
|
11056
11391
|
) });
|
|
11057
11392
|
}
|
|
11058
11393
|
|
|
11394
|
+
// src/front/chat/useAddressedAgentSelection.ts
|
|
11395
|
+
import { useCallback as useCallback19, useEffect as useEffect22, useMemo as useMemo17, useState as useState21 } from "react";
|
|
11396
|
+
|
|
11397
|
+
// src/front/chat/gatewayResponseError.ts
|
|
11398
|
+
var RUNTIME_SCOPE_MISMATCH_MESSAGE = "This chat belongs to a previous runtime configuration and can no longer be changed.";
|
|
11399
|
+
var GatewayResponseError = class extends Error {
|
|
11400
|
+
constructor(status, message, code3, details, body, operation) {
|
|
11401
|
+
super(message);
|
|
11402
|
+
this.status = status;
|
|
11403
|
+
this.code = code3;
|
|
11404
|
+
this.details = details;
|
|
11405
|
+
this.body = body;
|
|
11406
|
+
this.operation = operation;
|
|
11407
|
+
this.name = "GatewayResponseError";
|
|
11408
|
+
this.errorCode = code3;
|
|
11409
|
+
}
|
|
11410
|
+
status;
|
|
11411
|
+
code;
|
|
11412
|
+
details;
|
|
11413
|
+
body;
|
|
11414
|
+
operation;
|
|
11415
|
+
errorCode;
|
|
11416
|
+
};
|
|
11417
|
+
async function gatewayResponseError(response, fallbackMessage, operation) {
|
|
11418
|
+
const body = await safeReadJson2(response);
|
|
11419
|
+
return gatewayResponseErrorFromBody(response.status, body, fallbackMessage, operation);
|
|
11420
|
+
}
|
|
11421
|
+
function gatewayResponseErrorFromBody(status, body, fallbackMessage, operation) {
|
|
11422
|
+
const payload = errorPayload(body);
|
|
11423
|
+
const code3 = gatewayResponseErrorCode(payload?.code);
|
|
11424
|
+
const serverMessage = typeof payload?.message === "string" && payload.message.trim().length > 0 ? payload.message : fallbackMessage;
|
|
11425
|
+
const message = code3 === AgentGatewayErrorCode.AGENT_SESSION_RUNTIME_SCOPE_MISMATCH ? RUNTIME_SCOPE_MISMATCH_MESSAGE : serverMessage;
|
|
11426
|
+
const details = isJsonValue(payload?.details) ? payload.details : void 0;
|
|
11427
|
+
return new GatewayResponseError(status, message, code3, details, body, operation);
|
|
11428
|
+
}
|
|
11429
|
+
function gatewayResponseErrorCode(value) {
|
|
11430
|
+
const sharedCode = ErrorCode.safeParse(value);
|
|
11431
|
+
if (sharedCode.success) return sharedCode.data;
|
|
11432
|
+
return typeof value === "string" && AGENT_GATEWAY_ERROR_CODES.includes(value) ? value : void 0;
|
|
11433
|
+
}
|
|
11434
|
+
function errorResponseCode(error) {
|
|
11435
|
+
if (error instanceof GatewayResponseError) return error.code;
|
|
11436
|
+
const record = typeof error === "object" && error !== null ? error : void 0;
|
|
11437
|
+
return gatewayResponseErrorCode(record?.code ?? record?.errorCode);
|
|
11438
|
+
}
|
|
11439
|
+
function isRuntimeScopeMismatchError(error) {
|
|
11440
|
+
return errorResponseCode(error) === AgentGatewayErrorCode.AGENT_SESSION_RUNTIME_SCOPE_MISMATCH;
|
|
11441
|
+
}
|
|
11442
|
+
async function safeReadJson2(response) {
|
|
11443
|
+
const text = await response.text();
|
|
11444
|
+
if (!text) return void 0;
|
|
11445
|
+
try {
|
|
11446
|
+
return JSON.parse(text);
|
|
11447
|
+
} catch {
|
|
11448
|
+
return void 0;
|
|
11449
|
+
}
|
|
11450
|
+
}
|
|
11451
|
+
function errorPayload(body) {
|
|
11452
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
11453
|
+
const record = body;
|
|
11454
|
+
return typeof record.error === "object" && record.error !== null ? record.error : record;
|
|
11455
|
+
}
|
|
11456
|
+
function isJsonValue(value) {
|
|
11457
|
+
if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
|
|
11458
|
+
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
11459
|
+
return typeof value === "object" && value !== null && Object.values(value).every(isJsonValue);
|
|
11460
|
+
}
|
|
11461
|
+
|
|
11462
|
+
// src/front/chat/useAddressedAgentSelection.ts
|
|
11463
|
+
var inFlightDiscoveries = /* @__PURE__ */ new WeakMap();
|
|
11464
|
+
function useAddressedAgentSelection({
|
|
11465
|
+
apiBaseUrl,
|
|
11466
|
+
requestHeaders,
|
|
11467
|
+
storageScope,
|
|
11468
|
+
fetch: fetch2,
|
|
11469
|
+
enabled = false
|
|
11470
|
+
} = {}) {
|
|
11471
|
+
const normalizedBaseUrl = apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
11472
|
+
const headersKey = useMemo17(
|
|
11473
|
+
() => JSON.stringify({
|
|
11474
|
+
storageScope: storageScope ?? "",
|
|
11475
|
+
headers: Object.entries(requestHeaders ?? {}).sort(([a], [b]) => a.localeCompare(b))
|
|
11476
|
+
}),
|
|
11477
|
+
[requestHeaders, storageScope]
|
|
11478
|
+
);
|
|
11479
|
+
const discoveryKey = `${normalizedBaseUrl}
|
|
11480
|
+
${headersKey}`;
|
|
11481
|
+
const fetchImpl = fetch2 ?? globalThis.fetch;
|
|
11482
|
+
const [state, setState] = useState21(() => ({
|
|
11483
|
+
discoveryKey: enabled ? "" : discoveryKey,
|
|
11484
|
+
agents: [],
|
|
11485
|
+
selectedAgentTypeId: void 0,
|
|
11486
|
+
loading: enabled,
|
|
11487
|
+
error: void 0
|
|
11488
|
+
}));
|
|
11489
|
+
useEffect22(() => {
|
|
11490
|
+
if (!enabled) {
|
|
11491
|
+
setState({
|
|
11492
|
+
discoveryKey,
|
|
11493
|
+
agents: [],
|
|
11494
|
+
selectedAgentTypeId: void 0,
|
|
11495
|
+
loading: false,
|
|
11496
|
+
error: void 0
|
|
11497
|
+
});
|
|
11498
|
+
return;
|
|
11499
|
+
}
|
|
11500
|
+
let cancelled = false;
|
|
11501
|
+
setState((previous) => ({
|
|
11502
|
+
discoveryKey,
|
|
11503
|
+
agents: previous.discoveryKey === discoveryKey ? previous.agents : [],
|
|
11504
|
+
selectedAgentTypeId: previous.discoveryKey === discoveryKey ? previous.selectedAgentTypeId : void 0,
|
|
11505
|
+
loading: true,
|
|
11506
|
+
error: void 0
|
|
11507
|
+
}));
|
|
11508
|
+
void discoverAgents(fetchImpl, discoveryKey, `${normalizedBaseUrl}/api/v1/agents`, {
|
|
11509
|
+
headers: scopedHeaders(requestHeaders, storageScope)
|
|
11510
|
+
}).then((agents2) => {
|
|
11511
|
+
if (cancelled) return;
|
|
11512
|
+
setState((previous) => ({
|
|
11513
|
+
discoveryKey,
|
|
11514
|
+
agents: agents2,
|
|
11515
|
+
selectedAgentTypeId: agents2.some((agent) => agent.agentTypeId === previous.selectedAgentTypeId) ? previous.selectedAgentTypeId : agents2[0]?.agentTypeId,
|
|
11516
|
+
loading: false,
|
|
11517
|
+
error: void 0
|
|
11518
|
+
}));
|
|
11519
|
+
}).catch((error) => {
|
|
11520
|
+
if (cancelled) return;
|
|
11521
|
+
setState({
|
|
11522
|
+
discoveryKey,
|
|
11523
|
+
agents: [],
|
|
11524
|
+
selectedAgentTypeId: void 0,
|
|
11525
|
+
loading: false,
|
|
11526
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
11527
|
+
});
|
|
11528
|
+
});
|
|
11529
|
+
return () => {
|
|
11530
|
+
cancelled = true;
|
|
11531
|
+
};
|
|
11532
|
+
}, [discoveryKey, enabled, fetchImpl, normalizedBaseUrl]);
|
|
11533
|
+
const current = state.discoveryKey === discoveryKey;
|
|
11534
|
+
const agents = enabled && current ? state.agents : [];
|
|
11535
|
+
const selectedAgentTypeId = enabled && current ? state.selectedAgentTypeId : void 0;
|
|
11536
|
+
const selectAgentTypeId = useCallback19((agentTypeId) => {
|
|
11537
|
+
setState((previous) => {
|
|
11538
|
+
if (previous.discoveryKey !== discoveryKey) return previous;
|
|
11539
|
+
if (!previous.agents.some((agent) => agent.agentTypeId === agentTypeId)) return previous;
|
|
11540
|
+
return { ...previous, selectedAgentTypeId: agentTypeId };
|
|
11541
|
+
});
|
|
11542
|
+
}, [discoveryKey]);
|
|
11543
|
+
return {
|
|
11544
|
+
agents,
|
|
11545
|
+
selectedAgentTypeId,
|
|
11546
|
+
loading: enabled && (!current || state.loading),
|
|
11547
|
+
error: enabled && current ? state.error : void 0,
|
|
11548
|
+
selectAgentTypeId
|
|
11549
|
+
};
|
|
11550
|
+
}
|
|
11551
|
+
function discoverAgents(fetchImpl, discoveryKey, url, init) {
|
|
11552
|
+
let requests = inFlightDiscoveries.get(fetchImpl);
|
|
11553
|
+
if (!requests) {
|
|
11554
|
+
requests = /* @__PURE__ */ new Map();
|
|
11555
|
+
inFlightDiscoveries.set(fetchImpl, requests);
|
|
11556
|
+
}
|
|
11557
|
+
const existing = requests.get(discoveryKey);
|
|
11558
|
+
if (existing) return existing;
|
|
11559
|
+
const request = fetchImpl(url, init).then(async (response) => {
|
|
11560
|
+
if (!response.ok) throw await gatewayResponseError(response, "Failed to load agents.", "load agents");
|
|
11561
|
+
return parseAgentOptions(await response.json());
|
|
11562
|
+
});
|
|
11563
|
+
requests.set(discoveryKey, request);
|
|
11564
|
+
void request.finally(() => {
|
|
11565
|
+
if (requests?.get(discoveryKey) === request) requests.delete(discoveryKey);
|
|
11566
|
+
}).catch(() => void 0);
|
|
11567
|
+
return request;
|
|
11568
|
+
}
|
|
11569
|
+
function parseAgentOptions(value) {
|
|
11570
|
+
if (!Array.isArray(value)) throw new Error("Failed to load agents: invalid response");
|
|
11571
|
+
return value.map((item) => {
|
|
11572
|
+
if (typeof item !== "object" || item === null) throw new Error("Failed to load agents: invalid response");
|
|
11573
|
+
const record = item;
|
|
11574
|
+
if (typeof record.agentTypeId !== "string" || record.agentTypeId.length === 0) {
|
|
11575
|
+
throw new Error("Failed to load agents: invalid response");
|
|
11576
|
+
}
|
|
11577
|
+
return {
|
|
11578
|
+
agentTypeId: record.agentTypeId,
|
|
11579
|
+
label: typeof record.label === "string" && record.label.length > 0 ? record.label : record.agentTypeId,
|
|
11580
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
11581
|
+
...Array.isArray(record.pluginIds) ? { pluginIds: record.pluginIds.filter((pluginId) => typeof pluginId === "string" && pluginId.length > 0) } : {}
|
|
11582
|
+
};
|
|
11583
|
+
});
|
|
11584
|
+
}
|
|
11585
|
+
function scopedHeaders(headers, storageScope) {
|
|
11586
|
+
const result = {};
|
|
11587
|
+
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
11588
|
+
if (typeof value === "string") result[key] = value;
|
|
11589
|
+
}
|
|
11590
|
+
const hasStorageScope = Object.keys(result).some((key) => key.toLowerCase() === "x-boring-storage-scope");
|
|
11591
|
+
if (storageScope && !hasStorageScope) result["x-boring-storage-scope"] = storageScope;
|
|
11592
|
+
return result;
|
|
11593
|
+
}
|
|
11594
|
+
|
|
11059
11595
|
// src/front/commands.ts
|
|
11060
11596
|
var AGENT_COMMAND_SOURCE = "agent";
|
|
11061
11597
|
function getAgentCommands(options = {}) {
|
|
@@ -11101,6 +11637,7 @@ export {
|
|
|
11101
11637
|
ConversationContent,
|
|
11102
11638
|
ConversationScrollButton,
|
|
11103
11639
|
DebugDrawer,
|
|
11640
|
+
GatewayResponseError,
|
|
11104
11641
|
Message,
|
|
11105
11642
|
MessageAction,
|
|
11106
11643
|
MessageActions,
|
|
@@ -11116,6 +11653,7 @@ export {
|
|
|
11116
11653
|
PromptInputFooter,
|
|
11117
11654
|
PromptInputSubmit,
|
|
11118
11655
|
PromptInputTextarea,
|
|
11656
|
+
RUNTIME_SCOPE_MISMATCH_MESSAGE,
|
|
11119
11657
|
Reasoning,
|
|
11120
11658
|
ReasoningContent,
|
|
11121
11659
|
ReasoningTrigger,
|
|
@@ -11126,12 +11664,15 @@ export {
|
|
|
11126
11664
|
ToolHeader,
|
|
11127
11665
|
activeSessionStorageKey,
|
|
11128
11666
|
builtinCommands,
|
|
11129
|
-
clearActiveSessionId,
|
|
11130
11667
|
cn,
|
|
11131
11668
|
createCommandRegistry,
|
|
11132
11669
|
defaultChatSuggestions,
|
|
11133
11670
|
defaultToolRenderers,
|
|
11671
|
+
errorResponseCode,
|
|
11672
|
+
gatewayResponseError,
|
|
11673
|
+
gatewayResponseErrorFromBody,
|
|
11134
11674
|
getAgentCommands,
|
|
11675
|
+
isRuntimeScopeMismatchError,
|
|
11135
11676
|
matchPiSessionSearch,
|
|
11136
11677
|
mergeShadcnToolRenderers,
|
|
11137
11678
|
mergeToolRenderers,
|
|
@@ -11141,6 +11682,7 @@ export {
|
|
|
11141
11682
|
resolveToolRenderer,
|
|
11142
11683
|
searchPiSessions,
|
|
11143
11684
|
uploadFile,
|
|
11685
|
+
useAddressedAgentSelection,
|
|
11144
11686
|
useOpenArtifact,
|
|
11145
11687
|
usePiSessions,
|
|
11146
11688
|
writeActiveSessionId
|