@hachej/boring-agent 0.1.95 → 0.1.97
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-2EMAKBVR.js → chunk-CXMM24NW.js} +9039 -7788
- package/dist/{chunk-OFQ7J6SI.js → chunk-LSO2AO6Q.js} +57 -1
- 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 +86 -16
- package/dist/front/index.js +839 -343
- package/dist/front/styles.css +1171 -2129
- 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 +572 -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) {
|
|
@@ -2012,14 +2018,16 @@ function MentionPicker({
|
|
|
2012
2018
|
setActiveIdx(0);
|
|
2013
2019
|
const ctrl = new AbortController();
|
|
2014
2020
|
const runFetch = fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
2015
|
-
const headers =
|
|
2021
|
+
const headers = withStorageScope(requestHeaders, storageScope);
|
|
2016
2022
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
2017
2023
|
const t = setTimeout(() => {
|
|
2018
2024
|
runFetch(`${base}/api/v1/files/search?q=${encodeURIComponent(mentionSearchGlob(mention.query))}&limit=8`, {
|
|
2019
2025
|
signal: ctrl.signal,
|
|
2020
2026
|
...headers ? { headers } : {}
|
|
2021
2027
|
}).then((r) => r.ok ? r.json() : null).then((data) => {
|
|
2022
|
-
if (data?.
|
|
2028
|
+
if (data?.resources) {
|
|
2029
|
+
setResults(data.resources.filter((resource) => resource.filesystem === "user").map((resource) => resource.path).slice(0, 8));
|
|
2030
|
+
}
|
|
2023
2031
|
}).catch(() => {
|
|
2024
2032
|
});
|
|
2025
2033
|
}, 120);
|
|
@@ -2070,15 +2078,6 @@ function detectMention(value, cursorPos) {
|
|
|
2070
2078
|
const atIdx = before.lastIndexOf("@");
|
|
2071
2079
|
return { query: match[2], anchorStart: atIdx, anchorEnd: cursorPos };
|
|
2072
2080
|
}
|
|
2073
|
-
function scopedHeaders(requestHeaders, storageScope) {
|
|
2074
|
-
const headers = {};
|
|
2075
|
-
for (const [key, value] of Object.entries(requestHeaders ?? {})) {
|
|
2076
|
-
if (value !== void 0) headers[key] = value;
|
|
2077
|
-
}
|
|
2078
|
-
const hasStorageScope = Object.keys(headers).some((key) => key.toLowerCase() === "x-boring-storage-scope");
|
|
2079
|
-
if (storageScope && !hasStorageScope) headers["x-boring-storage-scope"] = storageScope;
|
|
2080
|
-
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
2081
|
-
}
|
|
2082
2081
|
|
|
2083
2082
|
// src/front/useComposerPickers.ts
|
|
2084
2083
|
function setTextareaValue2(textarea, value) {
|
|
@@ -2599,6 +2598,7 @@ function resolveStorage(storage) {
|
|
|
2599
2598
|
|
|
2600
2599
|
// src/front/hooks/useChatModelSelection.ts
|
|
2601
2600
|
function useChatModelSelection({
|
|
2601
|
+
agentTypeId,
|
|
2602
2602
|
apiBaseUrl,
|
|
2603
2603
|
defaultModel,
|
|
2604
2604
|
fetch: fetchImpl,
|
|
@@ -2620,14 +2620,16 @@ function useChatModelSelection({
|
|
|
2620
2620
|
userSelectedModelRef.current = userSelectedModel;
|
|
2621
2621
|
}, [userSelectedModel]);
|
|
2622
2622
|
const setModel = useCallback6((next) => {
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
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 });
|
|
2627
2628
|
}, [storage, storageScope]);
|
|
2628
2629
|
const discoveryKey = useMemo2(
|
|
2629
2630
|
() => JSON.stringify({
|
|
2630
2631
|
apiBaseUrl: apiBaseUrl ?? "",
|
|
2632
|
+
agentTypeId,
|
|
2631
2633
|
headers: Object.entries(requestHeaders ?? {}).sort(([a], [b]) => a.localeCompare(b)),
|
|
2632
2634
|
storageScope: storageScope ?? ""
|
|
2633
2635
|
}),
|
|
@@ -2661,8 +2663,8 @@ function useChatModelSelection({
|
|
|
2661
2663
|
let aborted = false;
|
|
2662
2664
|
setLoaded(false);
|
|
2663
2665
|
const nextFetch = fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
2664
|
-
nextFetch(agentResourceUrl(apiBaseUrl,
|
|
2665
|
-
headers:
|
|
2666
|
+
nextFetch(agentResourceUrl(apiBaseUrl, `/api/v1/agents/${encodeURIComponent(agentTypeId)}/models`), {
|
|
2667
|
+
headers: withStorageScope(requestHeaders, storageScope)
|
|
2666
2668
|
}).then((res) => res.ok ? res.json() : null).then((payload) => {
|
|
2667
2669
|
if (aborted) return;
|
|
2668
2670
|
if (!payload?.models) {
|
|
@@ -2702,7 +2704,7 @@ function useChatModelSelection({
|
|
|
2702
2704
|
return () => {
|
|
2703
2705
|
aborted = true;
|
|
2704
2706
|
};
|
|
2705
|
-
}, [apiBaseUrl, discoveryKey, enabled, fetchImpl, requestHeaders, storage, storageScope]);
|
|
2707
|
+
}, [agentTypeId, apiBaseUrl, discoveryKey, enabled, fetchImpl, requestHeaders, storage, storageScope]);
|
|
2706
2708
|
useEffect5(() => {
|
|
2707
2709
|
const onChange = (event) => {
|
|
2708
2710
|
const next = parseModelSelection(event.detail);
|
|
@@ -2716,17 +2718,6 @@ function useChatModelSelection({
|
|
|
2716
2718
|
const currentModel = currentDiscoveryLoaded ? model : null;
|
|
2717
2719
|
return { availableModels: currentAvailableModels, loaded: currentDiscoveryLoaded, model: currentModel, setModel };
|
|
2718
2720
|
}
|
|
2719
|
-
function agentResourceUrl(apiBaseUrl, path) {
|
|
2720
|
-
const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
2721
|
-
return `${base}${path}`;
|
|
2722
|
-
}
|
|
2723
|
-
function scopedHeaders2(headers, storageScope) {
|
|
2724
|
-
if (!headers && !storageScope) return void 0;
|
|
2725
|
-
const result = { ...headers ?? {} };
|
|
2726
|
-
const hasStorageScope = Object.keys(result).some((key) => key.toLowerCase() === "x-boring-storage-scope");
|
|
2727
|
-
if (storageScope && !hasStorageScope) result["x-boring-storage-scope"] = storageScope;
|
|
2728
|
-
return result;
|
|
2729
|
-
}
|
|
2730
2721
|
|
|
2731
2722
|
// src/front/hooks/useServerCommands.ts
|
|
2732
2723
|
import { useEffect as useEffect6, useRef as useRef7, useState as useState8 } from "react";
|
|
@@ -2735,21 +2726,21 @@ function serverCommandErrorMessage(body, fallback) {
|
|
|
2735
2726
|
if (body.error && typeof body.error === "object" && typeof body.error.message === "string") return body.error.message;
|
|
2736
2727
|
return fallback;
|
|
2737
2728
|
}
|
|
2738
|
-
function toSlashCommand(command,
|
|
2729
|
+
function toSlashCommand(command, identity, isCurrentIdentity, apiBaseUrl, requestHeaders, fetchImpl) {
|
|
2739
2730
|
return {
|
|
2740
2731
|
name: command.name,
|
|
2741
2732
|
description: command.description ?? "",
|
|
2742
2733
|
source: command.source,
|
|
2743
2734
|
...command.sourcePlugin ? { sourcePlugin: command.sourcePlugin } : {},
|
|
2744
2735
|
handler: async (args) => {
|
|
2736
|
+
if (!isCurrentIdentity(identity.key)) return;
|
|
2745
2737
|
const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
2746
|
-
const
|
|
2747
|
-
const url = `${base}/api/v1/agent/commands/execute?${params.toString()}`;
|
|
2738
|
+
const url = `${base}/api/v1/agents/${encodeURIComponent(identity.agentTypeId)}/commands/execute`;
|
|
2748
2739
|
try {
|
|
2749
2740
|
const res = await fetchImpl(url, {
|
|
2750
2741
|
method: "POST",
|
|
2751
2742
|
headers: { ...requestHeaders ?? {}, "content-type": "application/json" },
|
|
2752
|
-
body: JSON.stringify({ name: command.name, args })
|
|
2743
|
+
body: JSON.stringify({ requestId: createRequestId("command"), sessionId: identity.sessionId, name: command.name, args })
|
|
2753
2744
|
});
|
|
2754
2745
|
if (!res.ok) {
|
|
2755
2746
|
const body = await res.json().catch(() => ({}));
|
|
@@ -2770,6 +2761,7 @@ function toSlashCommand(command, getSessionId, apiBaseUrl, requestHeaders, fetch
|
|
|
2770
2761
|
};
|
|
2771
2762
|
}
|
|
2772
2763
|
function useServerCommands({
|
|
2764
|
+
agentTypeId,
|
|
2773
2765
|
registry,
|
|
2774
2766
|
requestHeaders,
|
|
2775
2767
|
sessionId,
|
|
@@ -2780,41 +2772,62 @@ function useServerCommands({
|
|
|
2780
2772
|
refreshKey = 0
|
|
2781
2773
|
}) {
|
|
2782
2774
|
const [stamp, setStamp] = useState8(0);
|
|
2783
|
-
const
|
|
2784
|
-
const
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
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
|
+
}, []);
|
|
2788
2785
|
useEffect6(() => {
|
|
2789
2786
|
const clearRegistered = () => {
|
|
2790
|
-
|
|
2791
|
-
for (const name of
|
|
2792
|
-
|
|
2793
|
-
|
|
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;
|
|
2794
2792
|
};
|
|
2795
|
-
if (
|
|
2793
|
+
if (registrationsRef.current.registry !== registry) {
|
|
2794
|
+
if (clearRegistered()) setStamp((n) => n + 1);
|
|
2795
|
+
}
|
|
2796
|
+
if (!enabled || !canonicalSessionId || !identityKey) {
|
|
2796
2797
|
if (clearRegistered()) setStamp((n) => n + 1);
|
|
2797
2798
|
return;
|
|
2798
2799
|
}
|
|
2800
|
+
if (registrationsRef.current.identity !== identityKey) {
|
|
2801
|
+
if (clearRegistered()) setStamp((n) => n + 1);
|
|
2802
|
+
}
|
|
2799
2803
|
let aborted = false;
|
|
2800
2804
|
const nextFetch = fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
2801
2805
|
const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
2802
|
-
const url = `${base}/api/v1/
|
|
2803
|
-
const headers =
|
|
2806
|
+
const url = `${base}/api/v1/agents/${encodeURIComponent(agentTypeId)}/commands?sessionId=${encodeURIComponent(canonicalSessionId)}`;
|
|
2807
|
+
const headers = withStorageScope(requestHeaders, storageScope);
|
|
2804
2808
|
nextFetch(url, { headers }).then(async (res) => {
|
|
2805
2809
|
if (!res.ok) throw new Error(`commands request failed (${res.status})`);
|
|
2806
2810
|
return await res.json();
|
|
2807
2811
|
}).then((payload) => {
|
|
2808
2812
|
if (aborted) return;
|
|
2809
2813
|
const removed = clearRegistered();
|
|
2814
|
+
const registeredNames = /* @__PURE__ */ new Set();
|
|
2810
2815
|
let added = false;
|
|
2811
2816
|
for (const serverCommand of payload.commands ?? []) {
|
|
2812
|
-
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
|
+
);
|
|
2813
2825
|
if (registry.get(command.name)) continue;
|
|
2814
2826
|
registry.register(command);
|
|
2815
|
-
|
|
2827
|
+
registeredNames.add(command.name);
|
|
2816
2828
|
added = true;
|
|
2817
2829
|
}
|
|
2830
|
+
registrationsRef.current = { registry, identity: identityKey, names: registeredNames };
|
|
2818
2831
|
if (removed || added) setStamp((n) => n + 1);
|
|
2819
2832
|
}).catch(() => {
|
|
2820
2833
|
if (aborted) return;
|
|
@@ -2823,16 +2836,9 @@ function useServerCommands({
|
|
|
2823
2836
|
return () => {
|
|
2824
2837
|
aborted = true;
|
|
2825
2838
|
};
|
|
2826
|
-
}, [apiBaseUrl, enabled, fetchImpl, refreshKey, requestHeaders, registry, storageScope]);
|
|
2839
|
+
}, [agentTypeId, apiBaseUrl, canonicalSessionId, enabled, fetchImpl, identityKey, refreshKey, requestHeaders, registry, storageScope]);
|
|
2827
2840
|
return stamp;
|
|
2828
2841
|
}
|
|
2829
|
-
function scopedHeaders3(headers, storageScope) {
|
|
2830
|
-
if (!headers && !storageScope) return void 0;
|
|
2831
|
-
const result = { ...headers ?? {} };
|
|
2832
|
-
const hasScope = Object.keys(result).some((k) => k.toLowerCase() === "x-boring-storage-scope");
|
|
2833
|
-
if (storageScope && !hasScope) result["x-boring-storage-scope"] = storageScope;
|
|
2834
|
-
return result;
|
|
2835
|
-
}
|
|
2836
2842
|
|
|
2837
2843
|
// src/front/hooks/useAttachmentNotice.ts
|
|
2838
2844
|
import { useEffect as useEffect7, useState as useState9 } from "react";
|
|
@@ -3804,7 +3810,8 @@ function syncCursor(state, cursor) {
|
|
|
3804
3810
|
return { ...state, lastSeq: cursor, hydrated: true, needsResync: void 0 };
|
|
3805
3811
|
}
|
|
3806
3812
|
function hydrateFromSnapshot(state, snapshot, options = {}) {
|
|
3807
|
-
|
|
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;
|
|
3808
3815
|
const queue = { followUps: enrichQueueWithKnownMetadata(snapshot.queue.followUps, state.optimisticOutbox, state.queue.followUps) };
|
|
3809
3816
|
const snapshotMessages = normalizeSnapshotMessages(snapshot.messages);
|
|
3810
3817
|
const preserveLocalHistory = shouldPreserveLocalCommittedHistory(state, snapshot, snapshotMessages, options);
|
|
@@ -4467,11 +4474,12 @@ function parsePiChatReplayRangeError(status, body) {
|
|
|
4467
4474
|
}
|
|
4468
4475
|
function buildPiChatEventsUrl({
|
|
4469
4476
|
apiBaseUrl = "",
|
|
4477
|
+
agentTypeId,
|
|
4470
4478
|
sessionId,
|
|
4471
4479
|
cursor
|
|
4472
4480
|
}) {
|
|
4473
4481
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
4474
|
-
return `${base}/api/v1/
|
|
4482
|
+
return `${base}/api/v1/agents/${encodeURIComponent(agentTypeId)}/sessions/${encodeURIComponent(sessionId)}/events?cursor=${encodeURIComponent(String(cursor))}`;
|
|
4475
4483
|
}
|
|
4476
4484
|
function calculateJitteredBackoffDelayMs({
|
|
4477
4485
|
attempt,
|
|
@@ -4512,6 +4520,7 @@ var SUPPORTED_PROTOCOL_VERSION = 1;
|
|
|
4512
4520
|
var DEFAULT_RECONNECT_BASE_MS = 1e3;
|
|
4513
4521
|
var DEFAULT_RECONNECT_MAX_MS = 3e4;
|
|
4514
4522
|
var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
|
|
4523
|
+
var DEFAULT_COMMAND_TIMEOUT_MS = 6e4;
|
|
4515
4524
|
var DEFAULT_LARGE_STATE_WARNING_BYTES = 5 * 1024 * 1024;
|
|
4516
4525
|
var DEFAULT_LARGE_STATE_WARNING_MESSAGES = 300;
|
|
4517
4526
|
var EVENT_TYPE_RING_LIMIT = 20;
|
|
@@ -4528,6 +4537,7 @@ var RemotePiSession = class {
|
|
|
4528
4537
|
this.setTimeoutFn = options.setTimeoutFn ?? globalThis.setTimeout;
|
|
4529
4538
|
this.clearTimeoutFn = options.clearTimeoutFn ?? globalThis.clearTimeout;
|
|
4530
4539
|
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
4540
|
+
this.commandTimeoutMs = options.commandTimeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;
|
|
4531
4541
|
this.store = createPiChatStore(createInitialPiChatState({
|
|
4532
4542
|
sessionId: options.sessionId,
|
|
4533
4543
|
workspaceId: options.workspaceId,
|
|
@@ -4544,10 +4554,12 @@ var RemotePiSession = class {
|
|
|
4544
4554
|
setTimeoutFn;
|
|
4545
4555
|
clearTimeoutFn;
|
|
4546
4556
|
requestTimeoutMs;
|
|
4557
|
+
commandTimeoutMs;
|
|
4547
4558
|
generation = 0;
|
|
4548
4559
|
streamRunId = 0;
|
|
4549
4560
|
reconnectAttempt = 0;
|
|
4550
4561
|
started = false;
|
|
4562
|
+
suspended = false;
|
|
4551
4563
|
disposed = false;
|
|
4552
4564
|
streamAbortController;
|
|
4553
4565
|
reconnectTimer;
|
|
@@ -4581,6 +4593,7 @@ var RemotePiSession = class {
|
|
|
4581
4593
|
streamingMessageCount: state.streamingMessage ? 1 : 0
|
|
4582
4594
|
},
|
|
4583
4595
|
disposed: this.disposed,
|
|
4596
|
+
suspended: this.suspended,
|
|
4584
4597
|
generation: this.generation,
|
|
4585
4598
|
streamRunId: this.streamRunId,
|
|
4586
4599
|
reconnectAttempt: this.reconnectAttempt,
|
|
@@ -4592,7 +4605,7 @@ var RemotePiSession = class {
|
|
|
4592
4605
|
return this.store.subscribe(listener);
|
|
4593
4606
|
}
|
|
4594
4607
|
start(cursor) {
|
|
4595
|
-
if (this.disposed || this.started) return Promise.resolve();
|
|
4608
|
+
if (this.disposed || this.suspended || this.started) return Promise.resolve();
|
|
4596
4609
|
this.started = true;
|
|
4597
4610
|
const generation = this.generation;
|
|
4598
4611
|
if (cursor === void 0) {
|
|
@@ -4607,8 +4620,8 @@ var RemotePiSession = class {
|
|
|
4607
4620
|
if (!this.disposed) {
|
|
4608
4621
|
this.store.dispatch({ type: "optimistic-user-message", message: toOptimisticUserMessage(payload) }, { flush: true });
|
|
4609
4622
|
}
|
|
4610
|
-
if (!this.started) await this.start(this.store.getState().lastSeq);
|
|
4611
|
-
else this.ensureReconnectScheduled();
|
|
4623
|
+
if (!this.started && !this.suspended) await this.start(this.store.getState().lastSeq);
|
|
4624
|
+
else if (!this.suspended) this.ensureReconnectScheduled();
|
|
4612
4625
|
try {
|
|
4613
4626
|
const receipt = await this.postCommand("/prompt", payload, PromptReceiptSchema);
|
|
4614
4627
|
return receipt;
|
|
@@ -4621,8 +4634,8 @@ var RemotePiSession = class {
|
|
|
4621
4634
|
if (!this.disposed) {
|
|
4622
4635
|
this.store.dispatch({ type: "optimistic-user-message", message: toOptimisticUserMessage(payload) }, { flush: true });
|
|
4623
4636
|
}
|
|
4624
|
-
if (!this.started) await this.start(this.store.getState().lastSeq);
|
|
4625
|
-
else this.ensureReconnectScheduled();
|
|
4637
|
+
if (!this.started && !this.suspended) await this.start(this.store.getState().lastSeq);
|
|
4638
|
+
else if (!this.suspended) this.ensureReconnectScheduled();
|
|
4626
4639
|
try {
|
|
4627
4640
|
const receipt = await this.postCommand("/followup", payload, FollowUpReceiptSchema);
|
|
4628
4641
|
return receipt;
|
|
@@ -4648,6 +4661,28 @@ var RemotePiSession = class {
|
|
|
4648
4661
|
}
|
|
4649
4662
|
return receipt;
|
|
4650
4663
|
}
|
|
4664
|
+
suspendStream() {
|
|
4665
|
+
if (this.disposed || this.suspended) return;
|
|
4666
|
+
this.suspended = true;
|
|
4667
|
+
this.started = false;
|
|
4668
|
+
this.streamRunId += 1;
|
|
4669
|
+
this.clearReconnectTimer();
|
|
4670
|
+
this.abortEventStream();
|
|
4671
|
+
this.store.dispatch({ type: "connection-state", state: "suspended" }, { flush: true });
|
|
4672
|
+
}
|
|
4673
|
+
resumeStream() {
|
|
4674
|
+
if (this.disposed || !this.suspended && this.started) return;
|
|
4675
|
+
this.suspended = false;
|
|
4676
|
+
this.started = true;
|
|
4677
|
+
const generation = this.generation;
|
|
4678
|
+
const state = this.store.getState();
|
|
4679
|
+
if (!state.hydrated) {
|
|
4680
|
+
void this.hydrateAndConnect(generation);
|
|
4681
|
+
return;
|
|
4682
|
+
}
|
|
4683
|
+
this.store.dispatch({ type: "connection-state", state: "connecting" }, { flush: true });
|
|
4684
|
+
void this.connectEvents(state.lastSeq, generation);
|
|
4685
|
+
}
|
|
4651
4686
|
dispose() {
|
|
4652
4687
|
if (this.disposed) return;
|
|
4653
4688
|
this.disposed = true;
|
|
@@ -4661,33 +4696,34 @@ var RemotePiSession = class {
|
|
|
4661
4696
|
this.store.dispose();
|
|
4662
4697
|
}
|
|
4663
4698
|
async hydrateAndConnect(generation, options = {}) {
|
|
4664
|
-
|
|
4699
|
+
const hydrationRunId = this.streamRunId;
|
|
4700
|
+
if (!this.isHydrationActive(generation, hydrationRunId)) return;
|
|
4665
4701
|
this.clearReconnectTimer();
|
|
4666
4702
|
this.abortEventStream();
|
|
4667
4703
|
this.store.dispatch({ type: "connection-state", state: this.store.getState().hydrated ? "reconnecting" : "connecting" }, { flush: true });
|
|
4668
4704
|
try {
|
|
4669
4705
|
const headers = await this.requestHeaders();
|
|
4670
|
-
if (!this.
|
|
4706
|
+
if (!this.isHydrationActive(generation, hydrationRunId)) return;
|
|
4671
4707
|
const responseBody = await this.fetchJson(this.stateUrl(), { method: "GET", headers });
|
|
4672
|
-
if (!this.
|
|
4673
|
-
const raw =
|
|
4708
|
+
if (!this.isHydrationActive(generation, hydrationRunId)) return;
|
|
4709
|
+
const raw = addressedSnapshot(responseBody);
|
|
4674
4710
|
this.recordLargeStateWarning(raw);
|
|
4675
4711
|
if (readProtocolVersion(raw) !== SUPPORTED_PROTOCOL_VERSION) {
|
|
4676
4712
|
this.dispatchProtocolError(`Unsupported Pi chat protocol version: ${String(readProtocolVersion(raw) ?? "missing")}`);
|
|
4677
4713
|
return;
|
|
4678
4714
|
}
|
|
4679
4715
|
const snapshot = PiChatSnapshotSchema.parse(raw);
|
|
4680
|
-
if (!this.
|
|
4716
|
+
if (!this.isHydrationActive(generation, hydrationRunId)) return;
|
|
4681
4717
|
this.store.dispatch({ type: "hydrate", snapshot, allowSeqRewind: options?.allowSeqRewind }, { flush: true });
|
|
4682
4718
|
this.connectEvents(snapshot.seq, generation);
|
|
4683
4719
|
} catch (error) {
|
|
4684
|
-
if (!this.
|
|
4720
|
+
if (!this.isHydrationActive(generation, hydrationRunId) || isAbortError(error)) return;
|
|
4685
4721
|
this.dispatchProtocolError(errorMessage(error, "Failed to hydrate Pi chat session state."));
|
|
4686
4722
|
this.scheduleReconnect(generation);
|
|
4687
4723
|
}
|
|
4688
4724
|
}
|
|
4689
4725
|
connectEvents(cursor, generation) {
|
|
4690
|
-
if (!this.
|
|
4726
|
+
if (!this.isStreamLifecycleActive(generation)) return Promise.resolve();
|
|
4691
4727
|
this.clearReconnectTimer();
|
|
4692
4728
|
this.abortEventStream();
|
|
4693
4729
|
const runId = ++this.streamRunId;
|
|
@@ -4801,20 +4837,20 @@ var RemotePiSession = class {
|
|
|
4801
4837
|
}
|
|
4802
4838
|
}
|
|
4803
4839
|
rehydrateAfterStreamReset(generation, options = {}) {
|
|
4804
|
-
if (!this.
|
|
4840
|
+
if (!this.isStreamLifecycleActive(generation)) return;
|
|
4805
4841
|
this.streamRunId += 1;
|
|
4806
4842
|
this.abortEventStream();
|
|
4807
4843
|
void this.hydrateAndConnect(generation, options);
|
|
4808
4844
|
}
|
|
4809
4845
|
ensureReconnectScheduled() {
|
|
4810
|
-
if (!this.
|
|
4846
|
+
if (!this.isStreamLifecycleActive(this.generation)) return;
|
|
4811
4847
|
const state = this.store.getState();
|
|
4812
4848
|
if (state.connection.state === "connected" || state.connection.state === "connecting") return;
|
|
4813
4849
|
if (this.reconnectTimer !== void 0) return;
|
|
4814
4850
|
void this.hydrateAndConnect(this.generation);
|
|
4815
4851
|
}
|
|
4816
4852
|
scheduleReconnect(generation) {
|
|
4817
|
-
if (!this.
|
|
4853
|
+
if (!this.isStreamLifecycleActive(generation)) return;
|
|
4818
4854
|
this.clearReconnectTimer();
|
|
4819
4855
|
this.store.dispatch({ type: "connection-state", state: "reconnecting" }, { flush: true });
|
|
4820
4856
|
const attempt = this.reconnectAttempt++;
|
|
@@ -4828,7 +4864,7 @@ var RemotePiSession = class {
|
|
|
4828
4864
|
clearTimeoutFn: this.clearTimeoutFn,
|
|
4829
4865
|
reconnect: () => {
|
|
4830
4866
|
this.reconnectTimer = void 0;
|
|
4831
|
-
if (!this.
|
|
4867
|
+
if (!this.isStreamLifecycleActive(generation)) return;
|
|
4832
4868
|
const state = this.store.getState();
|
|
4833
4869
|
if (state.hydrated) this.connectEvents(state.lastSeq, generation);
|
|
4834
4870
|
else void this.hydrateAndConnect(generation);
|
|
@@ -4844,14 +4880,14 @@ var RemotePiSession = class {
|
|
|
4844
4880
|
method: "POST",
|
|
4845
4881
|
headers: { ...headers, "Content-Type": "application/json" },
|
|
4846
4882
|
body: JSON.stringify(this.addressedCommandPayload(path, payload))
|
|
4847
|
-
});
|
|
4883
|
+
}, this.commandTimeoutMs);
|
|
4848
4884
|
if (!this.isGenerationActive(generation)) {
|
|
4849
4885
|
throw abortError("Remote Pi session disposed before command receipt.");
|
|
4850
4886
|
}
|
|
4851
4887
|
return schema.parse(this.addressedCommandReceipt(path, raw));
|
|
4852
4888
|
}
|
|
4853
4889
|
addressedCommandReceipt(path, receipt) {
|
|
4854
|
-
if (
|
|
4890
|
+
if (path !== "/followup" || typeof receipt !== "object" || receipt === null) {
|
|
4855
4891
|
return receipt;
|
|
4856
4892
|
}
|
|
4857
4893
|
const record = receipt;
|
|
@@ -4861,21 +4897,21 @@ var RemotePiSession = class {
|
|
|
4861
4897
|
if (this.disposed) return;
|
|
4862
4898
|
this.store.dispatch({ type: "remove-optimistic-user-message", clientNonce }, { flush: true });
|
|
4863
4899
|
}
|
|
4864
|
-
async fetchJson(url, init) {
|
|
4900
|
+
async fetchJson(url, init, timeoutMs = this.requestTimeoutMs) {
|
|
4865
4901
|
const controller = new AbortController();
|
|
4866
4902
|
this.fetchControllers.add(controller);
|
|
4867
4903
|
let timedOut = false;
|
|
4868
4904
|
const timer = globalThis.setTimeout(() => {
|
|
4869
4905
|
timedOut = true;
|
|
4870
4906
|
controller.abort();
|
|
4871
|
-
},
|
|
4907
|
+
}, timeoutMs);
|
|
4872
4908
|
try {
|
|
4873
4909
|
const response = await this.fetchImpl(url, { ...init, signal: controller.signal });
|
|
4874
4910
|
const body = await safeReadJson(response);
|
|
4875
4911
|
if (!response.ok) throw new RemotePiSessionHttpError(response.status, routeErrorMessage(body, `HTTP ${response.status}`), body, routeErrorCode(body));
|
|
4876
4912
|
return body;
|
|
4877
4913
|
} catch (error) {
|
|
4878
|
-
if (timedOut) throw new Error(`Request to ${url} timed out after ${
|
|
4914
|
+
if (timedOut) throw new Error(`Request to ${url} timed out after ${timeoutMs}ms.`);
|
|
4879
4915
|
throw error;
|
|
4880
4916
|
} finally {
|
|
4881
4917
|
globalThis.clearTimeout(timer);
|
|
@@ -4897,19 +4933,18 @@ var RemotePiSession = class {
|
|
|
4897
4933
|
return this.sessionUrl("/state");
|
|
4898
4934
|
}
|
|
4899
4935
|
eventsUrl(cursor) {
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4936
|
+
return buildPiChatEventsUrl({
|
|
4937
|
+
apiBaseUrl: this.apiBaseUrl,
|
|
4938
|
+
agentTypeId: this.options.agentTypeId,
|
|
4939
|
+
sessionId: this.options.sessionId,
|
|
4940
|
+
cursor
|
|
4941
|
+
});
|
|
4904
4942
|
}
|
|
4905
4943
|
sessionUrl(path) {
|
|
4906
|
-
|
|
4907
|
-
return `${this.apiBaseUrl}/api/v1/agents/${encodeURIComponent(this.options.agentTypeId)}/sessions/${encodeURIComponent(this.options.sessionId)}${path}`;
|
|
4908
|
-
}
|
|
4909
|
-
return `${this.apiBaseUrl}/api/v1/agent/pi-chat/${encodeURIComponent(this.options.sessionId)}${path}`;
|
|
4944
|
+
return `${this.apiBaseUrl}/api/v1/agents/${encodeURIComponent(this.options.agentTypeId)}/sessions/${encodeURIComponent(this.options.sessionId)}${path}`;
|
|
4910
4945
|
}
|
|
4911
4946
|
addressedCommandPayload(path, payload) {
|
|
4912
|
-
if (
|
|
4947
|
+
if (typeof payload !== "object" || payload === null) return payload;
|
|
4913
4948
|
const record = payload;
|
|
4914
4949
|
if (typeof record.requestId === "string" && record.requestId.length > 0) return payload;
|
|
4915
4950
|
if (path === "/prompt" && typeof record.clientNonce === "string" && typeof record.message === "string") {
|
|
@@ -4972,8 +5007,14 @@ var RemotePiSession = class {
|
|
|
4972
5007
|
isGenerationActive(generation) {
|
|
4973
5008
|
return !this.disposed && generation === this.generation;
|
|
4974
5009
|
}
|
|
5010
|
+
isStreamLifecycleActive(generation) {
|
|
5011
|
+
return !this.suspended && this.isGenerationActive(generation);
|
|
5012
|
+
}
|
|
5013
|
+
isHydrationActive(generation, runId) {
|
|
5014
|
+
return this.isStreamLifecycleActive(generation) && runId === this.streamRunId;
|
|
5015
|
+
}
|
|
4975
5016
|
isStreamActive(generation, runId) {
|
|
4976
|
-
return this.
|
|
5017
|
+
return this.isStreamLifecycleActive(generation) && runId === this.streamRunId;
|
|
4977
5018
|
}
|
|
4978
5019
|
};
|
|
4979
5020
|
function addressedSnapshot(value) {
|
|
@@ -5100,13 +5141,23 @@ function clearPageUnloading() {
|
|
|
5100
5141
|
pageUnloading = false;
|
|
5101
5142
|
}
|
|
5102
5143
|
|
|
5103
|
-
// src/front/chat/session/
|
|
5144
|
+
// src/front/chat/session/sessionSelectionStorage.ts
|
|
5104
5145
|
var ACTIVE_SESSION_KEY_PREFIX = "boring-agent:v2";
|
|
5105
5146
|
var ACTIVE_SESSION_KEY_SUFFIX = "activeSessionId";
|
|
5147
|
+
var BOOT_RESUME_SESSION_KEY_SUFFIX = "bootResumeSessionId";
|
|
5106
5148
|
var DEFAULT_STORAGE_SCOPE2 = "default";
|
|
5107
5149
|
function activeSessionStorageKey(storageScope) {
|
|
5108
|
-
|
|
5109
|
-
|
|
5150
|
+
return scopedSessionStorageKey(storageScope, ACTIVE_SESSION_KEY_SUFFIX);
|
|
5151
|
+
}
|
|
5152
|
+
function bootResumeSessionStorageKey(source) {
|
|
5153
|
+
const scope = typeof source === "object" && source !== null ? encodeURIComponent(JSON.stringify([
|
|
5154
|
+
source.apiBaseUrl?.replace(/\/$/, "") ?? "",
|
|
5155
|
+
source.sessionsApiPath,
|
|
5156
|
+
source.agentTypeId ?? "",
|
|
5157
|
+
source.workspaceId ?? "",
|
|
5158
|
+
source.storageScope ?? DEFAULT_STORAGE_SCOPE2
|
|
5159
|
+
])) : source;
|
|
5160
|
+
return scopedSessionStorageKey(scope, BOOT_RESUME_SESSION_KEY_SUFFIX);
|
|
5110
5161
|
}
|
|
5111
5162
|
function readActiveSessionId(options = {}) {
|
|
5112
5163
|
const storage = resolveStorage2(options.storage);
|
|
@@ -5127,8 +5178,28 @@ function writeActiveSessionId(sessionId, options = {}) {
|
|
|
5127
5178
|
} catch {
|
|
5128
5179
|
}
|
|
5129
5180
|
}
|
|
5130
|
-
function
|
|
5131
|
-
|
|
5181
|
+
function readBootResumeSessionId(options = {}) {
|
|
5182
|
+
const storage = resolveBootStorage(options.storage);
|
|
5183
|
+
if (!storage) return void 0;
|
|
5184
|
+
try {
|
|
5185
|
+
return storage.getItem(bootResumeSessionStorageKey(options.bootResumeSource ?? options.storageScope)) ?? void 0;
|
|
5186
|
+
} catch {
|
|
5187
|
+
return void 0;
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5190
|
+
function writeBootResumeSessionId(sessionId, options = {}) {
|
|
5191
|
+
const storage = resolveBootStorage(options.storage);
|
|
5192
|
+
if (!storage) return;
|
|
5193
|
+
try {
|
|
5194
|
+
const key = bootResumeSessionStorageKey(options.bootResumeSource ?? options.storageScope);
|
|
5195
|
+
if (sessionId === void 0 || sessionId.length === 0) storage.removeItem(key);
|
|
5196
|
+
else storage.setItem(key, sessionId);
|
|
5197
|
+
} catch {
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
5200
|
+
function scopedSessionStorageKey(storageScope, suffix) {
|
|
5201
|
+
const scope = storageScope && storageScope.length > 0 ? storageScope : DEFAULT_STORAGE_SCOPE2;
|
|
5202
|
+
return `${ACTIVE_SESSION_KEY_PREFIX}:${scope}:${suffix}`;
|
|
5132
5203
|
}
|
|
5133
5204
|
function resolveStorage2(storage) {
|
|
5134
5205
|
if (storage) return storage;
|
|
@@ -5138,29 +5209,43 @@ function resolveStorage2(storage) {
|
|
|
5138
5209
|
return void 0;
|
|
5139
5210
|
}
|
|
5140
5211
|
}
|
|
5212
|
+
function resolveBootStorage(storage) {
|
|
5213
|
+
if (storage) return storage;
|
|
5214
|
+
try {
|
|
5215
|
+
return globalThis.sessionStorage;
|
|
5216
|
+
} catch {
|
|
5217
|
+
return void 0;
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5141
5220
|
|
|
5142
5221
|
// src/front/chat/session/usePiSessions.ts
|
|
5143
|
-
import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef8, useState as useState10 } from "react";
|
|
5144
|
-
var DEFAULT_SESSIONS_API_PATH = "/api/v1/agent/pi-chat/sessions";
|
|
5222
|
+
import { useCallback as useCallback7, useEffect as useEffect8, useLayoutEffect, useMemo as useMemo4, useRef as useRef8, useState as useState10 } from "react";
|
|
5145
5223
|
var SESSION_PAGE_SIZE = 50;
|
|
5146
5224
|
var DEFAULT_MAX_RETRIES = 60;
|
|
5147
5225
|
var DEFAULT_RETRY_BASE_MS = 250;
|
|
5148
5226
|
var DEFAULT_RETRY_MAX_MS = 2e3;
|
|
5227
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect8 : useLayoutEffect;
|
|
5149
5228
|
var SessionsPreparingError = class extends Error {
|
|
5150
5229
|
constructor() {
|
|
5151
5230
|
super("Agent runtime is still preparing");
|
|
5152
5231
|
this.name = "SessionsPreparingError";
|
|
5153
5232
|
}
|
|
5154
5233
|
};
|
|
5234
|
+
var StaleSessionsSourceError = class extends Error {
|
|
5235
|
+
constructor() {
|
|
5236
|
+
super("Pi sessions source is stale");
|
|
5237
|
+
this.name = "StaleSessionsSourceError";
|
|
5238
|
+
}
|
|
5239
|
+
};
|
|
5155
5240
|
function isNetworkFetchError(error) {
|
|
5156
5241
|
return error instanceof TypeError;
|
|
5157
5242
|
}
|
|
5158
|
-
function usePiSessions(options
|
|
5243
|
+
function usePiSessions(options) {
|
|
5159
5244
|
const enabled = options.enabled ?? true;
|
|
5160
5245
|
const apiBaseUrl = options.apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
5161
|
-
const
|
|
5162
|
-
const sessionsApiPath = options.sessionsApiPath ?? (addressed ? `/api/v1/agents/${encodeURIComponent(options.agentTypeId)}/sessions` : DEFAULT_SESSIONS_API_PATH);
|
|
5246
|
+
const sessionsApiPath = options.sessionsApiPath ?? `/api/v1/agents/${encodeURIComponent(options.agentTypeId)}/sessions`;
|
|
5163
5247
|
const storageScope = options.storageScope ?? "default";
|
|
5248
|
+
const activeSessionStorageScope = options.activeSessionStorageScope ?? storageScope;
|
|
5164
5249
|
const fetchImpl = useMemo4(() => options.fetch ?? globalThis.fetch.bind(globalThis), [options.fetch]);
|
|
5165
5250
|
const createRemoteSession = options.createRemoteSession ?? createRemotePiSession;
|
|
5166
5251
|
const connectActiveSession = options.connectActiveSession ?? true;
|
|
@@ -5168,12 +5253,30 @@ function usePiSessions(options = {}) {
|
|
|
5168
5253
|
const retryBaseMs = options.retry?.baseMs ?? DEFAULT_RETRY_BASE_MS;
|
|
5169
5254
|
const retryMaxMs = options.retry?.maxMs ?? DEFAULT_RETRY_MAX_MS;
|
|
5170
5255
|
const headersKey = useMemo4(() => headersScopeKey(options.requestHeaders, storageScope), [options.requestHeaders, storageScope]);
|
|
5171
|
-
const normalizedHeaders = useMemo4(() =>
|
|
5172
|
-
const requestScopeKey = useMemo4(
|
|
5173
|
-
|
|
5256
|
+
const normalizedHeaders = useMemo4(() => withStorageScope(options.requestHeaders, storageScope) ?? {}, [headersKey, storageScope]);
|
|
5257
|
+
const requestScopeKey = useMemo4(
|
|
5258
|
+
() => requestScopeIdentity(apiBaseUrl, sessionsApiPath, options.agentTypeId, storageScope, headersKey, options.workspaceId, options.sourceIdentity),
|
|
5259
|
+
[apiBaseUrl, headersKey, options.agentTypeId, options.sourceIdentity, options.workspaceId, sessionsApiPath, storageScope]
|
|
5260
|
+
);
|
|
5261
|
+
const dataSourceKey = useMemo4(
|
|
5262
|
+
() => dataSourceIdentity(apiBaseUrl, sessionsApiPath, options.agentTypeId, storageScope, options.workspaceId, options.sourceIdentity),
|
|
5263
|
+
[apiBaseUrl, options.agentTypeId, options.sourceIdentity, options.workspaceId, sessionsApiPath, storageScope]
|
|
5264
|
+
);
|
|
5265
|
+
const bootResumeSource = useMemo4(() => ({
|
|
5266
|
+
apiBaseUrl,
|
|
5267
|
+
sessionsApiPath,
|
|
5268
|
+
agentTypeId: options.agentTypeId,
|
|
5269
|
+
workspaceId: options.workspaceId,
|
|
5270
|
+
storageScope
|
|
5271
|
+
}), [apiBaseUrl, options.agentTypeId, options.workspaceId, sessionsApiPath, storageScope]);
|
|
5174
5272
|
const [sessions, setSessions] = useState10([]);
|
|
5175
5273
|
const [dataStorageScope, setDataStorageScope] = useState10(storageScope);
|
|
5176
|
-
const [activeSessionId, setActiveSessionId] = useState10(
|
|
5274
|
+
const [activeSessionId, setActiveSessionId] = useState10(void 0);
|
|
5275
|
+
const [resumeSessionState, setResumeSessionState] = useState10(() => ({
|
|
5276
|
+
dataSourceKey,
|
|
5277
|
+
id: readBootResumeSessionId({ bootResumeSource, storage: options.bootResumeStorage })
|
|
5278
|
+
}));
|
|
5279
|
+
const resumeSessionId = resumeSessionState.dataSourceKey === dataSourceKey ? resumeSessionState.id : void 0;
|
|
5177
5280
|
const [activePiSession, setActivePiSession] = useState10(void 0);
|
|
5178
5281
|
const [loading, setLoading] = useState10(enabled);
|
|
5179
5282
|
const [loadingMore, setLoadingMore] = useState10(false);
|
|
@@ -5190,17 +5293,30 @@ function usePiSessions(options = {}) {
|
|
|
5190
5293
|
const loadMoreRequestSeqRef = useRef8(0);
|
|
5191
5294
|
const loadMoreInFlightRef = useRef8(false);
|
|
5192
5295
|
const pendingCreatedRef = useRef8(/* @__PURE__ */ new Map());
|
|
5296
|
+
const pendingDeletedRef = useRef8(/* @__PURE__ */ new Set());
|
|
5193
5297
|
const pendingCreatedScopeRef = useRef8(requestScopeKey);
|
|
5194
5298
|
const dataStorageScopeRef = useRef8(storageScope);
|
|
5195
5299
|
const loadedDataSourceRef = useRef8(dataSourceKey);
|
|
5196
|
-
const
|
|
5197
|
-
|
|
5300
|
+
const committedRequestScopeRef = useRef8(requestScopeKey);
|
|
5301
|
+
useIsomorphicLayoutEffect(() => {
|
|
5302
|
+
committedRequestScopeRef.current = requestScopeKey;
|
|
5303
|
+
}, [requestScopeKey]);
|
|
5304
|
+
const sourceIsCurrent = useCallback7(
|
|
5305
|
+
(scope) => mountedRef.current && committedRequestScopeRef.current === scope,
|
|
5306
|
+
[]
|
|
5307
|
+
);
|
|
5198
5308
|
const remoteSessionOptionsRef = useRef8(options.remoteSessionOptions);
|
|
5199
5309
|
remoteSessionOptionsRef.current = options.remoteSessionOptions;
|
|
5200
5310
|
const remoteSessionOptionsKey = useMemo4(
|
|
5201
5311
|
() => remoteSessionOptionsIdentity(options.remoteSessionOptions),
|
|
5202
5312
|
[options.remoteSessionOptions]
|
|
5203
5313
|
);
|
|
5314
|
+
useIsomorphicLayoutEffect(() => {
|
|
5315
|
+
setResumeSessionState((current) => current.dataSourceKey === dataSourceKey ? current : {
|
|
5316
|
+
dataSourceKey,
|
|
5317
|
+
id: readBootResumeSessionId({ bootResumeSource, storage: options.bootResumeStorage })
|
|
5318
|
+
});
|
|
5319
|
+
}, [bootResumeSource, dataSourceKey, options.bootResumeStorage]);
|
|
5204
5320
|
useEffect8(() => {
|
|
5205
5321
|
sessionsRef.current = sessions;
|
|
5206
5322
|
}, [sessions]);
|
|
@@ -5213,48 +5329,66 @@ function usePiSessions(options = {}) {
|
|
|
5213
5329
|
const activeSessionKnown = Boolean(activeSessionId && sessions.some((session) => session.id === activeSessionId));
|
|
5214
5330
|
const requestHeaders = useCallback7(() => normalizedHeaders, [normalizedHeaders]);
|
|
5215
5331
|
const sessionsUrl = useCallback7((suffix = "") => `${apiBaseUrl}${sessionsApiPath}${suffix}`, [apiBaseUrl, sessionsApiPath]);
|
|
5216
|
-
const sessionsListUrl = useCallback7((
|
|
5217
|
-
const query = new URLSearchParams();
|
|
5218
|
-
if (
|
|
5219
|
-
query.set("limit", String(SESSION_PAGE_SIZE));
|
|
5220
|
-
if (cursor) query.set("cursor", cursor);
|
|
5221
|
-
} else {
|
|
5222
|
-
if (offset > 0) {
|
|
5223
|
-
query.set("limit", String(SESSION_PAGE_SIZE));
|
|
5224
|
-
query.set("offset", String(offset));
|
|
5225
|
-
}
|
|
5226
|
-
if (offset <= 0 && includeId) query.set("activeSessionId", includeId);
|
|
5227
|
-
}
|
|
5228
|
-
if (query.size === 0) return sessionsUrl();
|
|
5332
|
+
const sessionsListUrl = useCallback7((cursor) => {
|
|
5333
|
+
const query = new URLSearchParams({ limit: String(SESSION_PAGE_SIZE) });
|
|
5334
|
+
if (cursor) query.set("cursor", cursor);
|
|
5229
5335
|
return sessionsUrl(`?${query.toString()}`);
|
|
5230
|
-
}, [
|
|
5336
|
+
}, [sessionsUrl]);
|
|
5231
5337
|
const persistActive = useCallback7((id) => {
|
|
5232
|
-
writeActiveSessionId(id, { storageScope, storage: options.storage });
|
|
5233
|
-
}, [options.storage
|
|
5338
|
+
writeActiveSessionId(id, { storageScope: activeSessionStorageScope, storage: options.storage });
|
|
5339
|
+
}, [activeSessionStorageScope, options.storage]);
|
|
5340
|
+
const persistBootResume = useCallback7((id) => {
|
|
5341
|
+
writeBootResumeSessionId(id, { bootResumeSource, storage: options.bootResumeStorage });
|
|
5342
|
+
setResumeSessionState({ dataSourceKey, id });
|
|
5343
|
+
}, [bootResumeSource, dataSourceKey, options.bootResumeStorage]);
|
|
5344
|
+
const rememberConfirmedEmptySession = useCallback7((id) => {
|
|
5345
|
+
writeBootResumeSessionId(id, { bootResumeSource, storage: options.bootResumeStorage });
|
|
5346
|
+
setResumeSessionState({ dataSourceKey, id: void 0 });
|
|
5347
|
+
}, [bootResumeSource, dataSourceKey, options.bootResumeStorage]);
|
|
5234
5348
|
const ensurePendingScope = useCallback7(() => {
|
|
5235
5349
|
if (pendingCreatedScopeRef.current === requestScopeKey) return;
|
|
5236
5350
|
pendingCreatedScopeRef.current = requestScopeKey;
|
|
5237
5351
|
pendingCreatedRef.current.clear();
|
|
5352
|
+
pendingDeletedRef.current.clear();
|
|
5238
5353
|
}, [requestScopeKey]);
|
|
5239
5354
|
const preferredSessionId = useCallback7(() => {
|
|
5240
|
-
const persisted = options.initialActiveSessionId ?? readActiveSessionId({
|
|
5355
|
+
const persisted = options.initialActiveSessionId ?? readActiveSessionId({
|
|
5356
|
+
storageScope: activeSessionStorageScope,
|
|
5357
|
+
storage: options.storage
|
|
5358
|
+
});
|
|
5241
5359
|
if (loadedDataSourceRef.current === dataSourceKey) return activeSessionIdRef.current ?? persisted;
|
|
5242
5360
|
if (dataStorageScopeRef.current !== storageScope) return persisted;
|
|
5243
5361
|
return void 0;
|
|
5244
|
-
}, [dataSourceKey, options.initialActiveSessionId, options.storage, storageScope]);
|
|
5362
|
+
}, [activeSessionStorageScope, dataSourceKey, options.initialActiveSessionId, options.storage, storageScope]);
|
|
5245
5363
|
const applySessions = useCallback7((data, applyOptions = {}) => {
|
|
5246
5364
|
ensurePendingScope();
|
|
5247
5365
|
const replacingScope = loadedDataSourceRef.current !== dataSourceKey;
|
|
5248
5366
|
const requestedActiveId = preferredSessionId();
|
|
5249
5367
|
const replacingScopePreferred = replacingScope ? requestedActiveId : void 0;
|
|
5250
5368
|
const pendingCreated = pendingCreatedRef.current;
|
|
5369
|
+
const pendingDeleted = pendingDeletedRef.current;
|
|
5370
|
+
const deletedIds = new Set(pendingDeleted);
|
|
5251
5371
|
for (const session of data) pendingCreated.delete(session.id);
|
|
5252
|
-
const
|
|
5253
|
-
|
|
5372
|
+
const filteredData = data.filter((session) => !deletedIds.has(session.id));
|
|
5373
|
+
if (applyOptions.nextCursor === void 0) {
|
|
5374
|
+
const returnedIds = new Set(data.map((session) => session.id));
|
|
5375
|
+
for (const deletedId of pendingDeleted) {
|
|
5376
|
+
if (!returnedIds.has(deletedId)) pendingDeleted.delete(deletedId);
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5379
|
+
const canonicalCount = canonicalPageCount(filteredData);
|
|
5380
|
+
const pageMayHaveMore = applyOptions.nextCursor !== void 0;
|
|
5254
5381
|
const wasExhaustedBeyondFirstPage = applyOptions.background && !hasMoreRef.current && canonicalLoadedCountRef.current >= canonicalCount;
|
|
5255
|
-
const requestedActiveReturned = Boolean(requestedActiveId &&
|
|
5382
|
+
const requestedActiveReturned = Boolean(requestedActiveId && filteredData.some((session) => session.id === requestedActiveId));
|
|
5256
5383
|
const current = applyOptions.background && pageMayHaveMore ? sessionsRef.current.filter((session) => !requestedActiveId || requestedActiveReturned || session.id !== requestedActiveId) : [];
|
|
5257
|
-
const merged = mergeSessions(Array.from(pendingCreated.values()),
|
|
5384
|
+
const merged = mergeSessions(Array.from(pendingCreated.values()), filteredData, current.filter((session) => !deletedIds.has(session.id)));
|
|
5385
|
+
const rememberedEmptyId = readBootResumeSessionId({
|
|
5386
|
+
bootResumeSource,
|
|
5387
|
+
storage: options.bootResumeStorage
|
|
5388
|
+
});
|
|
5389
|
+
if (rememberedEmptyId && filteredData.some((session) => session.id === rememberedEmptyId)) {
|
|
5390
|
+
persistBootResume(void 0);
|
|
5391
|
+
}
|
|
5258
5392
|
const nextHasMore = pageMayHaveMore && !wasExhaustedBeyondFirstPage;
|
|
5259
5393
|
canonicalLoadedCountRef.current = applyOptions.background ? Math.max(canonicalLoadedCountRef.current, canonicalCount) : canonicalCount;
|
|
5260
5394
|
loadedDataSourceRef.current = dataSourceKey;
|
|
@@ -5269,10 +5403,12 @@ function usePiSessions(options = {}) {
|
|
|
5269
5403
|
persistActive(next);
|
|
5270
5404
|
return next;
|
|
5271
5405
|
});
|
|
5272
|
-
}, [
|
|
5406
|
+
}, [bootResumeSource, dataSourceKey, ensurePendingScope, options.bootResumeStorage, persistActive, persistBootResume, preferredSessionId, storageScope]);
|
|
5273
5407
|
const refresh = useCallback7(async (refreshOptions = {}) => {
|
|
5408
|
+
const scope = requestScopeKey;
|
|
5409
|
+
if (mountedRef.current && !sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5274
5410
|
const version = ++refreshVersionRef.current;
|
|
5275
|
-
const isCurrent = () =>
|
|
5411
|
+
const isCurrent = () => sourceIsCurrent(scope) && version === refreshVersionRef.current;
|
|
5276
5412
|
clearRetryTimer(retryTimerRef);
|
|
5277
5413
|
const background = refreshOptions.background === true;
|
|
5278
5414
|
if (!enabled) {
|
|
@@ -5299,7 +5435,7 @@ function usePiSessions(options = {}) {
|
|
|
5299
5435
|
let page;
|
|
5300
5436
|
for (let attempt = 0; ; attempt += 1) {
|
|
5301
5437
|
try {
|
|
5302
|
-
page = await fetchSessionList(fetchImpl, sessionsListUrl(
|
|
5438
|
+
page = await fetchSessionList(fetchImpl, sessionsListUrl(), requestHeaders());
|
|
5303
5439
|
break;
|
|
5304
5440
|
} catch (err) {
|
|
5305
5441
|
const transient = err instanceof SessionsPreparingError || isNetworkFetchError(err);
|
|
@@ -5321,7 +5457,7 @@ function usePiSessions(options = {}) {
|
|
|
5321
5457
|
setLoading(false);
|
|
5322
5458
|
if (refreshOptions.throwOnError) throw error2;
|
|
5323
5459
|
}
|
|
5324
|
-
}, [
|
|
5460
|
+
}, [applySessions, enabled, fetchImpl, persistActive, preferredSessionId, requestHeaders, requestScopeKey, retryBaseMs, retryMaxMs, retryMaxRetries, sessionsListUrl, sourceIsCurrent]);
|
|
5325
5461
|
useEffect8(() => {
|
|
5326
5462
|
mountedRef.current = true;
|
|
5327
5463
|
void refresh();
|
|
@@ -5337,14 +5473,13 @@ function usePiSessions(options = {}) {
|
|
|
5337
5473
|
loadMoreInFlightRef.current = true;
|
|
5338
5474
|
const version = refreshVersionRef.current;
|
|
5339
5475
|
const scope = requestScopeKey;
|
|
5340
|
-
const offset = canonicalLoadedCountRef.current;
|
|
5341
5476
|
setLoadingMore(true);
|
|
5342
5477
|
try {
|
|
5343
|
-
const page = await fetchSessionList(fetchImpl, sessionsListUrl(
|
|
5344
|
-
const data = page.sessions;
|
|
5345
|
-
if (
|
|
5346
|
-
const merged = mergeSessions(sessionsRef.current, data);
|
|
5347
|
-
const nextHasMore =
|
|
5478
|
+
const page = await fetchSessionList(fetchImpl, sessionsListUrl(nextCursorRef.current), requestHeaders());
|
|
5479
|
+
const data = page.sessions.filter((session) => !pendingDeletedRef.current.has(session.id));
|
|
5480
|
+
if (requestSeq !== loadMoreRequestSeqRef.current || version !== refreshVersionRef.current || !sourceIsCurrent(scope)) return;
|
|
5481
|
+
const merged = mergeSessions(sessionsRef.current.filter((session) => !pendingDeletedRef.current.has(session.id)), data);
|
|
5482
|
+
const nextHasMore = page.nextCursor !== void 0;
|
|
5348
5483
|
canonicalLoadedCountRef.current += data.length;
|
|
5349
5484
|
nextCursorRef.current = page.nextCursor;
|
|
5350
5485
|
setSessions(merged);
|
|
@@ -5357,16 +5492,16 @@ function usePiSessions(options = {}) {
|
|
|
5357
5492
|
return next;
|
|
5358
5493
|
});
|
|
5359
5494
|
} catch (err) {
|
|
5360
|
-
if (
|
|
5495
|
+
if (requestSeq === loadMoreRequestSeqRef.current && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
|
|
5361
5496
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
5362
5497
|
}
|
|
5363
5498
|
} finally {
|
|
5364
5499
|
if (requestSeq === loadMoreRequestSeqRef.current) loadMoreInFlightRef.current = false;
|
|
5365
|
-
if (
|
|
5500
|
+
if (requestSeq === loadMoreRequestSeqRef.current && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
|
|
5366
5501
|
setLoadingMore(false);
|
|
5367
5502
|
}
|
|
5368
5503
|
}
|
|
5369
|
-
}, [
|
|
5504
|
+
}, [enabled, fetchImpl, hasMore, loading, loadingMore, persistActive, requestHeaders, requestScopeKey, sessionsListUrl, sourceIsCurrent]);
|
|
5370
5505
|
useEffect8(() => {
|
|
5371
5506
|
if (!enabled || !connectActiveSession || !activeSessionId || !activeSessionKnown) {
|
|
5372
5507
|
setActivePiSession(void 0);
|
|
@@ -5388,6 +5523,8 @@ function usePiSessions(options = {}) {
|
|
|
5388
5523
|
};
|
|
5389
5524
|
}, [activeSessionId, activeSessionKnown, apiBaseUrl, connectActiveSession, createRemoteSession, enabled, fetchImpl, remoteSessionOptionsKey, options.agentTypeId, options.workspaceId, requestHeaders, storageScope]);
|
|
5390
5525
|
const create = useCallback7(async (init) => {
|
|
5526
|
+
const scope = requestScopeKey;
|
|
5527
|
+
if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5391
5528
|
if (!enabled) throw new Error("Pi sessions are disabled");
|
|
5392
5529
|
const response = await fetchImpl(sessionsUrl(), {
|
|
5393
5530
|
method: "POST",
|
|
@@ -5400,8 +5537,10 @@ function usePiSessions(options = {}) {
|
|
|
5400
5537
|
throw err;
|
|
5401
5538
|
}
|
|
5402
5539
|
const body = await response.json();
|
|
5403
|
-
const session =
|
|
5540
|
+
const session = addressedCreatedSession(body, options.agentTypeId, init?.title);
|
|
5541
|
+
if (!sourceIsCurrent(scope)) return session;
|
|
5404
5542
|
ensurePendingScope();
|
|
5543
|
+
rememberConfirmedEmptySession(session.id);
|
|
5405
5544
|
pendingCreatedRef.current.set(session.id, session);
|
|
5406
5545
|
setDataStorageScope(storageScope);
|
|
5407
5546
|
setSessions((previous) => mergeSessions([session], previous));
|
|
@@ -5409,17 +5548,43 @@ function usePiSessions(options = {}) {
|
|
|
5409
5548
|
persistActive(session.id);
|
|
5410
5549
|
void refresh();
|
|
5411
5550
|
return session;
|
|
5412
|
-
}, [
|
|
5551
|
+
}, [enabled, ensurePendingScope, fetchImpl, persistActive, refresh, rememberConfirmedEmptySession, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
|
|
5413
5552
|
const switchSession = useCallback7((id) => {
|
|
5553
|
+
if (!sourceIsCurrent(requestScopeKey)) return;
|
|
5414
5554
|
const known = sessionsRef.current.some((session) => session.id === id);
|
|
5415
5555
|
const next = known ? id : sessionsRef.current[0]?.id;
|
|
5416
5556
|
setActiveSessionId(next);
|
|
5417
5557
|
persistActive(next);
|
|
5418
|
-
}, [persistActive]);
|
|
5558
|
+
}, [persistActive, requestScopeKey, sourceIsCurrent]);
|
|
5559
|
+
const rename = useCallback7(async (id, title) => {
|
|
5560
|
+
const scope = requestScopeKey;
|
|
5561
|
+
if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5562
|
+
if (!enabled) throw new Error("Pi sessions are disabled");
|
|
5563
|
+
const response = await fetchImpl(sessionsUrl(`/${encodeURIComponent(id)}/rename`), {
|
|
5564
|
+
method: "POST",
|
|
5565
|
+
headers: { ...requestHeaders(), "Content-Type": "application/json" },
|
|
5566
|
+
body: JSON.stringify({ requestId: createRequestId("rename"), title })
|
|
5567
|
+
});
|
|
5568
|
+
if (!response.ok) throw new Error(`Failed to rename session: ${response.status}`);
|
|
5569
|
+
const renamed = toAddressedSessionSummary(await response.json());
|
|
5570
|
+
if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5571
|
+
ensurePendingScope();
|
|
5572
|
+
if (pendingCreatedRef.current.has(id)) pendingCreatedRef.current.set(id, renamed);
|
|
5573
|
+
setSessions((current) => current.map((session) => session.id === id ? renamed : session));
|
|
5574
|
+
return renamed;
|
|
5575
|
+
}, [enabled, ensurePendingScope, fetchImpl, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent]);
|
|
5419
5576
|
const deleteSession = useCallback7(async (id) => {
|
|
5577
|
+
const scope = requestScopeKey;
|
|
5578
|
+
if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
|
|
5420
5579
|
if (!enabled) throw new Error("Pi sessions are disabled");
|
|
5421
5580
|
ensurePendingScope();
|
|
5581
|
+
if (readBootResumeSessionId({ bootResumeSource, storage: options.bootResumeStorage }) === id) {
|
|
5582
|
+
persistBootResume(void 0);
|
|
5583
|
+
}
|
|
5584
|
+
const deletedSession = sessionsRef.current.find((session) => session.id === id);
|
|
5585
|
+
const deletedSessionWasActive = activeSessionIdRef.current === id;
|
|
5422
5586
|
pendingCreatedRef.current.delete(id);
|
|
5587
|
+
pendingDeletedRef.current.add(id);
|
|
5423
5588
|
setDataStorageScope(storageScope);
|
|
5424
5589
|
setSessions((previous) => previous.filter((session) => session.id !== id));
|
|
5425
5590
|
setActiveSessionId((previous) => {
|
|
@@ -5436,18 +5601,29 @@ function usePiSessions(options = {}) {
|
|
|
5436
5601
|
if (!response.ok && response.status !== 404) throw new Error(`Failed to delete session: ${response.status}`);
|
|
5437
5602
|
} catch (err) {
|
|
5438
5603
|
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
5439
|
-
|
|
5440
|
-
|
|
5604
|
+
if (sourceIsCurrent(scope)) {
|
|
5605
|
+
pendingDeletedRef.current.delete(id);
|
|
5606
|
+
if (deletedSession) {
|
|
5607
|
+
setSessions((previous) => mergeSessions([deletedSession], previous));
|
|
5608
|
+
if (deletedSessionWasActive) {
|
|
5609
|
+
setActiveSessionId(id);
|
|
5610
|
+
persistActive(id);
|
|
5611
|
+
}
|
|
5612
|
+
}
|
|
5613
|
+
setError(error2);
|
|
5614
|
+
void refresh();
|
|
5615
|
+
}
|
|
5441
5616
|
throw error2;
|
|
5442
5617
|
}
|
|
5443
|
-
void refresh();
|
|
5444
|
-
}, [enabled, ensurePendingScope, fetchImpl, persistActive, refresh, requestHeaders, sessionsUrl, storageScope]);
|
|
5618
|
+
if (sourceIsCurrent(scope)) void refresh();
|
|
5619
|
+
}, [bootResumeSource, enabled, ensurePendingScope, fetchImpl, options.bootResumeStorage, persistActive, persistBootResume, refresh, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
|
|
5445
5620
|
const reset = useCallback7(() => {
|
|
5621
|
+
if (!sourceIsCurrent(requestScopeKey)) return;
|
|
5446
5622
|
pendingCreatedRef.current.clear();
|
|
5623
|
+
pendingDeletedRef.current.clear();
|
|
5447
5624
|
loadMoreRequestSeqRef.current += 1;
|
|
5448
5625
|
loadMoreInFlightRef.current = false;
|
|
5449
5626
|
canonicalLoadedCountRef.current = canonicalPageCount(sessionsRef.current);
|
|
5450
|
-
nextCursorRef.current = void 0;
|
|
5451
5627
|
loadedDataSourceRef.current = dataSourceKey;
|
|
5452
5628
|
dataStorageScopeRef.current = storageScope;
|
|
5453
5629
|
setDataStorageScope(storageScope);
|
|
@@ -5455,36 +5631,35 @@ function usePiSessions(options = {}) {
|
|
|
5455
5631
|
setActivePiSession(void 0);
|
|
5456
5632
|
setLoadingMore(false);
|
|
5457
5633
|
persistActive(void 0);
|
|
5458
|
-
}, [dataSourceKey, persistActive, storageScope]);
|
|
5634
|
+
}, [dataSourceKey, persistActive, requestScopeKey, sourceIsCurrent, storageScope]);
|
|
5459
5635
|
const visibleActiveSessionId = enabled ? activeSessionId : void 0;
|
|
5460
5636
|
const activeSession = enabled ? sessions.find((session) => session.id === visibleActiveSessionId) : void 0;
|
|
5461
5637
|
return {
|
|
5462
5638
|
sessions,
|
|
5463
5639
|
activeSession,
|
|
5464
5640
|
activeSessionId: visibleActiveSessionId,
|
|
5641
|
+
resumeSessionId,
|
|
5465
5642
|
activePiSession: visibleActiveSessionId ? activePiSession : void 0,
|
|
5466
5643
|
dataStorageScope,
|
|
5644
|
+
sourceIdentity: enabled && loadedDataSourceRef.current === dataSourceKey ? options.sourceIdentity : void 0,
|
|
5467
5645
|
loading: enabled ? loading : false,
|
|
5468
5646
|
loadingMore,
|
|
5469
5647
|
hasMore: enabled ? hasMore : false,
|
|
5470
5648
|
error,
|
|
5471
5649
|
refresh,
|
|
5472
5650
|
create,
|
|
5651
|
+
rename,
|
|
5473
5652
|
switch: switchSession,
|
|
5474
5653
|
delete: deleteSession,
|
|
5475
5654
|
loadMore,
|
|
5476
5655
|
reset
|
|
5477
5656
|
};
|
|
5478
5657
|
}
|
|
5479
|
-
async function fetchSessionList(fetchImpl, url, headers
|
|
5658
|
+
async function fetchSessionList(fetchImpl, url, headers) {
|
|
5480
5659
|
const response = await fetchImpl(url, Object.keys(headers).length > 0 ? { headers } : void 0);
|
|
5481
5660
|
if (response.status === 503) throw new SessionsPreparingError();
|
|
5482
5661
|
if (!response.ok) throw new Error(`Failed to load sessions: ${response.status}`);
|
|
5483
5662
|
const body = await response.json();
|
|
5484
|
-
if (!addressed) {
|
|
5485
|
-
if (!Array.isArray(body)) throw new Error("Failed to load sessions: invalid response");
|
|
5486
|
-
return { sessions: body.map(toSessionSummary) };
|
|
5487
|
-
}
|
|
5488
5663
|
if (typeof body !== "object" || body === null || !Array.isArray(body.sessions)) {
|
|
5489
5664
|
throw new Error("Failed to load sessions: invalid response");
|
|
5490
5665
|
}
|
|
@@ -5494,19 +5669,6 @@ async function fetchSessionList(fetchImpl, url, headers, addressed) {
|
|
|
5494
5669
|
...typeof page.nextCursor === "string" ? { nextCursor: page.nextCursor } : {}
|
|
5495
5670
|
};
|
|
5496
5671
|
}
|
|
5497
|
-
function toSessionSummary(value) {
|
|
5498
|
-
if (typeof value !== "object" || value === null) throw new Error("invalid session summary");
|
|
5499
|
-
const record = value;
|
|
5500
|
-
if (typeof record.id !== "string" || !record.id) throw new Error("invalid session id");
|
|
5501
|
-
const now = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
5502
|
-
return {
|
|
5503
|
-
id: record.id,
|
|
5504
|
-
title: typeof record.title === "string" ? record.title : "Untitled",
|
|
5505
|
-
createdAt: typeof record.createdAt === "string" ? record.createdAt : now,
|
|
5506
|
-
updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : now,
|
|
5507
|
-
turnCount: typeof record.turnCount === "number" ? record.turnCount : 0
|
|
5508
|
-
};
|
|
5509
|
-
}
|
|
5510
5672
|
function toAddressedSessionSummary(value) {
|
|
5511
5673
|
if (typeof value !== "object" || value === null) throw new Error("invalid addressed session summary");
|
|
5512
5674
|
const record = value;
|
|
@@ -5516,21 +5678,29 @@ function toAddressedSessionSummary(value) {
|
|
|
5516
5678
|
}
|
|
5517
5679
|
const createdAt = typeof record.createdAt === "number" ? new Date(record.createdAt).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
5518
5680
|
const updatedAt = typeof record.updatedAt === "number" ? new Date(record.updatedAt).toISOString() : createdAt;
|
|
5681
|
+
const agentTypeId = ref.agentTypeId;
|
|
5682
|
+
const status = record.status;
|
|
5683
|
+
const addressedStatus = status === "idle" || status === "running" || status === "aborting" || status === "error" ? status : void 0;
|
|
5519
5684
|
return {
|
|
5520
5685
|
id: ref.sessionId,
|
|
5521
5686
|
title: typeof record.title === "string" ? record.title : "Untitled",
|
|
5522
5687
|
createdAt,
|
|
5523
5688
|
updatedAt,
|
|
5524
|
-
turnCount: 0
|
|
5689
|
+
turnCount: typeof record.turnCount === "number" ? record.turnCount : 0,
|
|
5690
|
+
...typeof agentTypeId === "string" ? { agentTypeId } : {},
|
|
5691
|
+
...typeof record.nativeSessionId === "string" ? { nativeSessionId: record.nativeSessionId } : {},
|
|
5692
|
+
...typeof record.hasAssistantReply === "boolean" ? { hasAssistantReply: record.hasAssistantReply } : {},
|
|
5693
|
+
...addressedStatus ? { status: addressedStatus } : {}
|
|
5525
5694
|
};
|
|
5526
5695
|
}
|
|
5527
|
-
function addressedCreatedSession(value, title) {
|
|
5528
|
-
if (typeof value !== "object" || value === null || typeof value.sessionId !== "string") {
|
|
5696
|
+
function addressedCreatedSession(value, expectedAgentTypeId, title) {
|
|
5697
|
+
if (typeof value !== "object" || value === null || typeof value.sessionId !== "string" || value.agentTypeId !== expectedAgentTypeId) {
|
|
5529
5698
|
throw new Error("invalid addressed session ref");
|
|
5530
5699
|
}
|
|
5531
5700
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5532
5701
|
return {
|
|
5533
5702
|
id: value.sessionId,
|
|
5703
|
+
agentTypeId: expectedAgentTypeId,
|
|
5534
5704
|
title: title ?? "Untitled",
|
|
5535
5705
|
createdAt: now,
|
|
5536
5706
|
updatedAt: now,
|
|
@@ -5586,31 +5756,25 @@ function mergeSessions(...lists) {
|
|
|
5586
5756
|
}
|
|
5587
5757
|
return merged;
|
|
5588
5758
|
}
|
|
5589
|
-
function buildRequestHeaders(headers, storageScope) {
|
|
5590
|
-
const result = {};
|
|
5591
|
-
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
5592
|
-
if (typeof value === "string") result[key] = value;
|
|
5593
|
-
}
|
|
5594
|
-
if (storageScope && !hasHeader2(result, "x-boring-storage-scope")) result["x-boring-storage-scope"] = storageScope;
|
|
5595
|
-
return result;
|
|
5596
|
-
}
|
|
5597
5759
|
function headersScopeKey(headers, storageScope) {
|
|
5598
5760
|
return JSON.stringify({ storageScope, headers: Object.entries(headers ?? {}).sort(([a], [b]) => a.localeCompare(b)) });
|
|
5599
5761
|
}
|
|
5600
|
-
function requestScopeIdentity(apiBaseUrl, sessionsApiPath, storageScope, headersKey) {
|
|
5762
|
+
function requestScopeIdentity(apiBaseUrl, sessionsApiPath, agentTypeId, storageScope, headersKey, workspaceId, sourceIdentity) {
|
|
5601
5763
|
return `${apiBaseUrl}
|
|
5602
5764
|
${sessionsApiPath}
|
|
5765
|
+
${agentTypeId}
|
|
5603
5766
|
${storageScope}
|
|
5604
|
-
${headersKey}
|
|
5767
|
+
${headersKey}
|
|
5768
|
+
${workspaceId ?? ""}
|
|
5769
|
+
${sourceIdentity ?? ""}`;
|
|
5605
5770
|
}
|
|
5606
|
-
function dataSourceIdentity(apiBaseUrl, sessionsApiPath, storageScope) {
|
|
5771
|
+
function dataSourceIdentity(apiBaseUrl, sessionsApiPath, agentTypeId, storageScope, workspaceId, sourceIdentity) {
|
|
5607
5772
|
return `${apiBaseUrl}
|
|
5608
5773
|
${sessionsApiPath}
|
|
5609
|
-
${
|
|
5610
|
-
}
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
return Object.keys(headers).some((key) => key.toLowerCase() === lower);
|
|
5774
|
+
${agentTypeId}
|
|
5775
|
+
${storageScope}
|
|
5776
|
+
${workspaceId ?? ""}
|
|
5777
|
+
${sourceIdentity ?? ""}`;
|
|
5614
5778
|
}
|
|
5615
5779
|
function retryDelayMs(attempt, retry) {
|
|
5616
5780
|
const base = retry.baseMs ?? DEFAULT_RETRY_BASE_MS;
|
|
@@ -5991,8 +6155,8 @@ function searchPiSessions(sessions, query, options = {}) {
|
|
|
5991
6155
|
}
|
|
5992
6156
|
|
|
5993
6157
|
// src/front/chat/components/PiConversationSurface.tsx
|
|
5994
|
-
import {
|
|
5995
|
-
import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect, useRef as useRef11, useState as useState15 } from "react";
|
|
6158
|
+
import { Skeleton } from "@hachej/boring-ui-kit";
|
|
6159
|
+
import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect as useLayoutEffect2, useRef as useRef11, useState as useState15 } from "react";
|
|
5996
6160
|
import { useStickToBottomContext as useStickToBottomContext2 } from "use-stick-to-bottom";
|
|
5997
6161
|
|
|
5998
6162
|
// src/front/primitives/conversation.tsx
|
|
@@ -6053,7 +6217,7 @@ var ConversationScrollButton = ({
|
|
|
6053
6217
|
Button7,
|
|
6054
6218
|
{
|
|
6055
6219
|
className: cn(
|
|
6056
|
-
"absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full dark:bg-background dark:hover:bg-muted",
|
|
6220
|
+
"boring-agent-scroll-latest absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full dark:bg-background dark:hover:bg-muted",
|
|
6057
6221
|
className
|
|
6058
6222
|
),
|
|
6059
6223
|
onClick: handleScrollToBottom,
|
|
@@ -6098,8 +6262,27 @@ function noticeTextClass(className) {
|
|
|
6098
6262
|
import { memo as memo2 } from "react";
|
|
6099
6263
|
import { AlertTriangleIcon, InfoIcon, Loader2Icon, PlugZapIcon, RefreshCwIcon, XIcon as XIcon2 } from "lucide-react";
|
|
6100
6264
|
import { Button as Button8 } from "@hachej/boring-ui-kit";
|
|
6101
|
-
|
|
6102
|
-
|
|
6265
|
+
|
|
6266
|
+
// src/front/chat/components/terminalChatErrors.ts
|
|
6267
|
+
var TERMINAL_CHAT_ERROR_IDS = /* @__PURE__ */ new Set(["chat-error", "session-navigation-error"]);
|
|
6268
|
+
function isTerminalChatErrorId(id) {
|
|
6269
|
+
return TERMINAL_CHAT_ERROR_IDS.has(id);
|
|
6270
|
+
}
|
|
6271
|
+
function isTerminalChatErrorNotice(id, historyEmpty) {
|
|
6272
|
+
return historyEmpty && isTerminalChatErrorId(id);
|
|
6273
|
+
}
|
|
6274
|
+
function hasTerminalChatError(notices, historyEmpty) {
|
|
6275
|
+
return historyEmpty && notices.some((notice) => isTerminalChatErrorId(notice.id));
|
|
6276
|
+
}
|
|
6277
|
+
var COMPETING_NOISE_NOTICE_IDS = /* @__PURE__ */ new Set(["connection-reconnecting", "auto-retry"]);
|
|
6278
|
+
function filterCompetingNoiseNotices(notices, historyEmpty) {
|
|
6279
|
+
if (!hasTerminalChatError(notices, historyEmpty)) return notices;
|
|
6280
|
+
return notices.filter((notice) => !COMPETING_NOISE_NOTICE_IDS.has(notice.id));
|
|
6281
|
+
}
|
|
6282
|
+
|
|
6283
|
+
// src/front/chat/components/RuntimeNotices.tsx
|
|
6284
|
+
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
6285
|
+
var RuntimeNotices = memo2(({ notices, onDismiss, onAction, renderAction, historyEmpty, className, ...props }) => {
|
|
6103
6286
|
if (notices.length === 0) return null;
|
|
6104
6287
|
return /* @__PURE__ */ jsx16(
|
|
6105
6288
|
"div",
|
|
@@ -6107,16 +6290,29 @@ var RuntimeNotices = memo2(({ notices, onDismiss, onAction, renderAction, classN
|
|
|
6107
6290
|
"data-boring-agent-part": "runtime-notices",
|
|
6108
6291
|
className: cn("flex flex-col gap-2 px-4 py-2", className),
|
|
6109
6292
|
...props,
|
|
6110
|
-
children: notices.map((notice) => /* @__PURE__ */ jsx16(
|
|
6293
|
+
children: notices.map((notice) => /* @__PURE__ */ jsx16(
|
|
6294
|
+
RuntimeNoticeRow,
|
|
6295
|
+
{
|
|
6296
|
+
notice,
|
|
6297
|
+
onDismiss,
|
|
6298
|
+
onAction,
|
|
6299
|
+
renderAction,
|
|
6300
|
+
historyEmpty: historyEmpty ?? false
|
|
6301
|
+
},
|
|
6302
|
+
notice.id
|
|
6303
|
+
))
|
|
6111
6304
|
}
|
|
6112
6305
|
);
|
|
6113
6306
|
});
|
|
6114
6307
|
RuntimeNotices.displayName = "RuntimeNotices";
|
|
6115
|
-
function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
|
|
6308
|
+
function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction, historyEmpty }) {
|
|
6116
6309
|
const kind = inferNoticeKind(notice);
|
|
6117
6310
|
const Icon = iconForNotice(kind, notice.level);
|
|
6118
6311
|
const actionLabel = notice.actionLabel ?? defaultActionLabel(kind);
|
|
6119
6312
|
const hostAction = renderAction?.(notice);
|
|
6313
|
+
const isTerminalError = isTerminalChatErrorNotice(notice.id, historyEmpty);
|
|
6314
|
+
const explicitAction = actionLabel && onAction ? /* @__PURE__ */ jsx16(Button8, { type: "button", variant: "ghost", size: "sm", onClick: () => onAction(notice.id), children: actionLabel }) : null;
|
|
6315
|
+
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);
|
|
6120
6316
|
return /* @__PURE__ */ jsxs14(
|
|
6121
6317
|
"div",
|
|
6122
6318
|
{
|
|
@@ -6132,9 +6328,15 @@ function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
|
|
|
6132
6328
|
),
|
|
6133
6329
|
children: [
|
|
6134
6330
|
/* @__PURE__ */ jsx16(Icon, { className: cn(noticeIconClass(notice.level), kind === "retry" && "animate-spin motion-reduce:animate-none"), "aria-hidden": "true" }),
|
|
6135
|
-
/* @__PURE__ */ jsx16("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */
|
|
6136
|
-
|
|
6137
|
-
|
|
6331
|
+
/* @__PURE__ */ jsx16("div", { className: "min-w-0 flex-1", children: isTerminalError ? /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
6332
|
+
/* @__PURE__ */ jsx16("p", { className: "text-sm font-semibold text-foreground", children: "Chat history unavailable" }),
|
|
6333
|
+
/* @__PURE__ */ jsx16("p", { className: noticeTextClass("mt-0.5"), children: "The saved conversation could not be loaded. Reload the workspace to try again." }),
|
|
6334
|
+
/* @__PURE__ */ jsxs14("details", { className: "mt-2 text-xs text-muted-foreground", children: [
|
|
6335
|
+
/* @__PURE__ */ jsx16("summary", { className: "cursor-pointer select-none", children: "Error details" }),
|
|
6336
|
+
/* @__PURE__ */ jsx16("p", { className: "mt-1 break-words", children: notice.text })
|
|
6337
|
+
] })
|
|
6338
|
+
] }) : /* @__PURE__ */ jsx16("p", { className: noticeTextClass(), children: notice.text }) }),
|
|
6339
|
+
primaryAction,
|
|
6138
6340
|
notice.dismissible && onDismiss ? /* @__PURE__ */ jsx16(
|
|
6139
6341
|
Button8,
|
|
6140
6342
|
{
|
|
@@ -6177,7 +6379,8 @@ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
|
6177
6379
|
function RuntimeNoticeMessages({
|
|
6178
6380
|
notices,
|
|
6179
6381
|
onDismiss,
|
|
6180
|
-
renderAction
|
|
6382
|
+
renderAction,
|
|
6383
|
+
historyEmpty
|
|
6181
6384
|
}) {
|
|
6182
6385
|
const visible = notices.filter(
|
|
6183
6386
|
(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:")
|
|
@@ -6189,6 +6392,7 @@ function RuntimeNoticeMessages({
|
|
|
6189
6392
|
notices: visible,
|
|
6190
6393
|
onDismiss,
|
|
6191
6394
|
renderAction,
|
|
6395
|
+
historyEmpty,
|
|
6192
6396
|
className: "mx-auto w-full max-w-3xl px-0 py-0"
|
|
6193
6397
|
}
|
|
6194
6398
|
);
|
|
@@ -6650,11 +6854,11 @@ function extractCodeBlockText(children) {
|
|
|
6650
6854
|
};
|
|
6651
6855
|
return walk(children);
|
|
6652
6856
|
}
|
|
6653
|
-
var MarkdownPre = ({ children }) => {
|
|
6857
|
+
var MarkdownPre = ({ children, filename }) => {
|
|
6654
6858
|
const code3 = extractCodeBlockText(children).replace(/\n$/, "");
|
|
6655
6859
|
const language = extractCodeBlockLanguage(children);
|
|
6656
6860
|
return /* @__PURE__ */ jsx19(CodeBlock2, { code: code3, language: language ?? "text", className: "my-3", children: /* @__PURE__ */ jsxs17(CodeBlockHeader, { children: [
|
|
6657
|
-
/* @__PURE__ */ jsx19(CodeBlockTitle, { children: /* @__PURE__ */ jsx19(CodeBlockFilename, { children: language ?? "text" }) }),
|
|
6861
|
+
/* @__PURE__ */ jsx19(CodeBlockTitle, { children: /* @__PURE__ */ jsx19(CodeBlockFilename, { children: filename ?? language ?? "text" }) }),
|
|
6658
6862
|
/* @__PURE__ */ jsx19(CodeBlockCopyButton, {})
|
|
6659
6863
|
] }) });
|
|
6660
6864
|
};
|
|
@@ -6684,7 +6888,7 @@ var markdownComponents = {
|
|
|
6684
6888
|
code: MarkdownCode
|
|
6685
6889
|
};
|
|
6686
6890
|
var MessageResponse = memo3(
|
|
6687
|
-
({ className, shikiTheme, components, ...props }) => /* @__PURE__ */ jsx19(
|
|
6891
|
+
({ className, shikiTheme, components, codeFilename, ...props }) => /* @__PURE__ */ jsx19(
|
|
6688
6892
|
Streamdown,
|
|
6689
6893
|
{
|
|
6690
6894
|
className: cn(
|
|
@@ -6693,11 +6897,15 @@ var MessageResponse = memo3(
|
|
|
6693
6897
|
),
|
|
6694
6898
|
plugins: streamdownPlugins,
|
|
6695
6899
|
shikiTheme: shikiTheme ?? DEFAULT_SHIKI_THEME,
|
|
6696
|
-
components: {
|
|
6900
|
+
components: {
|
|
6901
|
+
...markdownComponents,
|
|
6902
|
+
...components ?? {},
|
|
6903
|
+
...codeFilename ? { pre: (props2) => /* @__PURE__ */ jsx19(MarkdownPre, { ...props2, filename: codeFilename }) } : {}
|
|
6904
|
+
},
|
|
6697
6905
|
...props
|
|
6698
6906
|
}
|
|
6699
6907
|
),
|
|
6700
|
-
(prevProps, nextProps) => prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating && nextProps.components === prevProps.components
|
|
6908
|
+
(prevProps, nextProps) => prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating && nextProps.components === prevProps.components && prevProps.codeFilename === nextProps.codeFilename
|
|
6701
6909
|
);
|
|
6702
6910
|
MessageResponse.displayName = "MessageResponse";
|
|
6703
6911
|
|
|
@@ -6778,7 +6986,7 @@ var ShimmerComponent = ({
|
|
|
6778
6986
|
var Shimmer = memo4(ShimmerComponent);
|
|
6779
6987
|
|
|
6780
6988
|
// src/front/primitives/reasoning.tsx
|
|
6781
|
-
import { Fragment as
|
|
6989
|
+
import { Fragment as Fragment8, jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
6782
6990
|
var ReasoningContext = createContext6(null);
|
|
6783
6991
|
var useReasoning = () => {
|
|
6784
6992
|
const context = useContext6(ReasoningContext);
|
|
@@ -6891,7 +7099,7 @@ var ReasoningTrigger = memo5(
|
|
|
6891
7099
|
className
|
|
6892
7100
|
),
|
|
6893
7101
|
...props,
|
|
6894
|
-
children: children ?? /* @__PURE__ */ jsxs18(
|
|
7102
|
+
children: children ?? /* @__PURE__ */ jsxs18(Fragment8, { children: [
|
|
6895
7103
|
/* @__PURE__ */ jsx21(BrainIcon, { className: "size-3" }),
|
|
6896
7104
|
getThinkingMessage(isStreaming, duration),
|
|
6897
7105
|
/* @__PURE__ */ jsx21(
|
|
@@ -7053,6 +7261,10 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
|
|
|
7053
7261
|
return () => window.clearInterval(interval);
|
|
7054
7262
|
}, [isRunning]);
|
|
7055
7263
|
if (toolParts.length === 0) return null;
|
|
7264
|
+
const inlineToolKeys = new Set(toolParts.flatMap(({ part, key }) => {
|
|
7265
|
+
const { renderer } = resolveToolRendererForPart(part, mergedToolRenderers);
|
|
7266
|
+
return renderer.presentation === "inline" ? [key] : [];
|
|
7267
|
+
}));
|
|
7056
7268
|
return /* @__PURE__ */ jsxs19(
|
|
7057
7269
|
Collapsible3,
|
|
7058
7270
|
{
|
|
@@ -7061,6 +7273,11 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
|
|
|
7061
7273
|
className: "not-prose my-1.5",
|
|
7062
7274
|
"data-boring-agent-tool-state": visualState,
|
|
7063
7275
|
children: [
|
|
7276
|
+
toolParts.map(({ part, key }) => {
|
|
7277
|
+
if (!inlineToolKeys.has(key)) return null;
|
|
7278
|
+
const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(part, mergedToolRenderers);
|
|
7279
|
+
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);
|
|
7280
|
+
}),
|
|
7064
7281
|
/* @__PURE__ */ jsxs19(
|
|
7065
7282
|
CollapsibleTrigger4,
|
|
7066
7283
|
{
|
|
@@ -7106,7 +7323,10 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
|
|
|
7106
7323
|
children: [
|
|
7107
7324
|
/* @__PURE__ */ jsx22("div", { className: "absolute left-[5px] top-0 bottom-2 w-px bg-border/35" }),
|
|
7108
7325
|
/* @__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 }) => {
|
|
7109
|
-
const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(
|
|
7326
|
+
const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(
|
|
7327
|
+
part,
|
|
7328
|
+
inlineToolKeys.has(key) ? {} : mergedToolRenderers
|
|
7329
|
+
);
|
|
7110
7330
|
return /* @__PURE__ */ jsx22(
|
|
7111
7331
|
"div",
|
|
7112
7332
|
{
|
|
@@ -7158,7 +7378,7 @@ function ChatMessageContributionBoundary({
|
|
|
7158
7378
|
|
|
7159
7379
|
// src/front/chat/components/MessageMentions.tsx
|
|
7160
7380
|
import { Children, cloneElement, isValidElement as isValidElement2 } from "react";
|
|
7161
|
-
import { Fragment as
|
|
7381
|
+
import { Fragment as Fragment9, jsx as jsx24 } from "react/jsx-runtime";
|
|
7162
7382
|
var TERMINAL_BOUNDARY = String.raw`(?=$|\s|[.,;!?)}\]"'”’]+(?=$|\s))`;
|
|
7163
7383
|
var PREFIX_BOUNDARY = String.raw`(^|[\s(\[{"'“‘])`;
|
|
7164
7384
|
function escapeRegExp2(value) {
|
|
@@ -7316,8 +7536,8 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
|
|
|
7316
7536
|
const content = Children.toArray(children).filter((child) => child !== null && child !== "");
|
|
7317
7537
|
if (content.length === 1 && isValidElement2(content[0])) {
|
|
7318
7538
|
const child = content[0];
|
|
7319
|
-
if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx24(
|
|
7320
|
-
if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx24(
|
|
7539
|
+
if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx24(Fragment9, { children });
|
|
7540
|
+
if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx24(Fragment9, { children });
|
|
7321
7541
|
}
|
|
7322
7542
|
return /* @__PURE__ */ jsx24("p", { ...props, children: decorate(children) });
|
|
7323
7543
|
};
|
|
@@ -7403,12 +7623,13 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
7403
7623
|
mentionActivateRef.current?.(mention);
|
|
7404
7624
|
}, [openArtifact]);
|
|
7405
7625
|
const hasMentions = effectiveMentionCatalog.files || effectiveMentionCatalog.commands.length > 0 || effectiveMentionCatalog.skills.length > 0;
|
|
7406
|
-
const
|
|
7626
|
+
const messageIsStreaming = isAssistant && isLast && (isStreaming || message.status === "streaming");
|
|
7627
|
+
const mentionsEnabled = Boolean(isAssistant && !messageIsStreaming && hasMentions);
|
|
7407
7628
|
const mentionMarkdownComponents = useMemo12(
|
|
7408
7629
|
() => mentionsEnabled ? createMessageMentionMarkdownComponents(effectiveMentionCatalog, activateCurrentMention) : void 0,
|
|
7409
7630
|
[activateCurrentMention, effectiveMentionCatalog, mentionSignature, mentionsEnabled]
|
|
7410
7631
|
);
|
|
7411
|
-
const shouldReserveStreamingActions =
|
|
7632
|
+
const shouldReserveStreamingActions = messageIsStreaming;
|
|
7412
7633
|
return /* @__PURE__ */ jsxs20(
|
|
7413
7634
|
Message,
|
|
7414
7635
|
{
|
|
@@ -7467,7 +7688,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
7467
7688
|
})
|
|
7468
7689
|
}
|
|
7469
7690
|
) : null,
|
|
7470
|
-
finalParts.map((item) => {
|
|
7691
|
+
finalParts.map((item, index) => {
|
|
7471
7692
|
if (item.kind === "reasoning") {
|
|
7472
7693
|
return /* @__PURE__ */ jsx25(
|
|
7473
7694
|
TimelineReasoningPart,
|
|
@@ -7491,6 +7712,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
7491
7712
|
MessageResponse,
|
|
7492
7713
|
{
|
|
7493
7714
|
components: mentionMarkdownComponents,
|
|
7715
|
+
codeFilename: codeFilenameForPart(finalParts, index),
|
|
7494
7716
|
className: "boring-agent-markdown max-w-none",
|
|
7495
7717
|
children: text
|
|
7496
7718
|
},
|
|
@@ -7507,7 +7729,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
7507
7729
|
MessageActionsBar,
|
|
7508
7730
|
{
|
|
7509
7731
|
text: textParts.map((part) => part.text).join("\n\n"),
|
|
7510
|
-
visible: !
|
|
7732
|
+
visible: !messageIsStreaming
|
|
7511
7733
|
}
|
|
7512
7734
|
) : null
|
|
7513
7735
|
]
|
|
@@ -7542,7 +7764,7 @@ function TimelineReasoningPart({ item, showThoughts }) {
|
|
|
7542
7764
|
ReasoningTrigger,
|
|
7543
7765
|
{
|
|
7544
7766
|
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",
|
|
7545
|
-
getThinkingMessage: (
|
|
7767
|
+
getThinkingMessage: () => /* @__PURE__ */ jsx25("span", { children: "Reasoning" }),
|
|
7546
7768
|
onClick: toggleManualOpen
|
|
7547
7769
|
}
|
|
7548
7770
|
),
|
|
@@ -7603,6 +7825,15 @@ ${part.text}`;
|
|
|
7603
7825
|
flushTools();
|
|
7604
7826
|
return grouped;
|
|
7605
7827
|
}
|
|
7828
|
+
function codeFilenameForPart(parts, index) {
|
|
7829
|
+
const precedingPart = parts[index - 1];
|
|
7830
|
+
if (!precedingPart || precedingPart.kind !== "tool-plain") return void 0;
|
|
7831
|
+
if (precedingPart.part.toolName !== "write" && precedingPart.part.toolName !== "edit") return void 0;
|
|
7832
|
+
const input = precedingPart.part.input;
|
|
7833
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return void 0;
|
|
7834
|
+
const path = input.path;
|
|
7835
|
+
return typeof path === "string" && path.trim() ? path : void 0;
|
|
7836
|
+
}
|
|
7606
7837
|
function NoticeBubble({ level, text }) {
|
|
7607
7838
|
return /* @__PURE__ */ jsx25(
|
|
7608
7839
|
"div",
|
|
@@ -7683,10 +7914,10 @@ function MessageActionsBar({
|
|
|
7683
7914
|
}
|
|
7684
7915
|
};
|
|
7685
7916
|
const iconActionBtnClass = cn(
|
|
7686
|
-
"inline-flex
|
|
7687
|
-
"text-muted-foreground/
|
|
7688
|
-
"hover:bg-foreground/[0.04] hover:text-muted-foreground
|
|
7689
|
-
"focus-visible:outline-none focus-visible:ring-
|
|
7917
|
+
"boring-agent-message-action inline-flex size-8 items-center justify-center rounded-[var(--radius-sm)]",
|
|
7918
|
+
"text-muted-foreground/70 transition-colors duration-150",
|
|
7919
|
+
"hover:bg-foreground/[0.04] hover:text-muted-foreground",
|
|
7920
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)]/40"
|
|
7690
7921
|
);
|
|
7691
7922
|
const hiddenActionProps = visible ? {} : { tabIndex: -1 };
|
|
7692
7923
|
return /* @__PURE__ */ jsx25(
|
|
@@ -7694,7 +7925,7 @@ function MessageActionsBar({
|
|
|
7694
7925
|
{
|
|
7695
7926
|
"aria-hidden": !visible,
|
|
7696
7927
|
className: cn(
|
|
7697
|
-
"flex min-h-
|
|
7928
|
+
"flex min-h-8 items-center gap-1 -mt-1 text-xs text-muted-foreground transition-opacity duration-200",
|
|
7698
7929
|
visible ? "opacity-100" : "pointer-events-none opacity-0"
|
|
7699
7930
|
),
|
|
7700
7931
|
children: /* @__PURE__ */ jsx25(
|
|
@@ -7755,6 +7986,8 @@ function PiConversationSurface({
|
|
|
7755
7986
|
}) {
|
|
7756
7987
|
const messageItems = buildMessageRenderItems(messages);
|
|
7757
7988
|
const total = messageItems.length;
|
|
7989
|
+
const historyEmpty = messages.length === 0;
|
|
7990
|
+
const terminalError = hasTerminalChatError(runtimeNotices, historyEmpty);
|
|
7758
7991
|
const [visibleCount, setVisibleCount] = useState15(TRANSCRIPT_WINDOW);
|
|
7759
7992
|
useEffect14(() => {
|
|
7760
7993
|
setVisibleCount(TRANSCRIPT_WINDOW);
|
|
@@ -7778,11 +8011,8 @@ function PiConversationSurface({
|
|
|
7778
8011
|
chrome ? "max-w-3xl px-6 py-8" : "max-w-[680px] px-4 py-4",
|
|
7779
8012
|
emptyHero && "py-4 text-center"
|
|
7780
8013
|
), children: [
|
|
7781
|
-
messages.length === 0 && emptyStateHydrating ? /* @__PURE__ */
|
|
7782
|
-
|
|
7783
|
-
"Loading chat history\u2026"
|
|
7784
|
-
] }) : null,
|
|
7785
|
-
messages.length === 0 && !emptyStateHydrating ? /* @__PURE__ */ jsx26(
|
|
8014
|
+
messages.length === 0 && emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx26(ConversationHistoryLoadingState, {}) : null,
|
|
8015
|
+
messages.length === 0 && !emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx26(
|
|
7786
8016
|
ChatEmptyState,
|
|
7787
8017
|
{
|
|
7788
8018
|
eyebrow: emptyState?.eyebrow,
|
|
@@ -7814,13 +8044,45 @@ function PiConversationSurface({
|
|
|
7814
8044
|
},
|
|
7815
8045
|
key
|
|
7816
8046
|
)),
|
|
7817
|
-
/* @__PURE__ */ jsx26(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
|
|
8047
|
+
/* @__PURE__ */ jsx26(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction, historyEmpty })
|
|
7818
8048
|
] }),
|
|
7819
8049
|
/* @__PURE__ */ jsx26(ConversationScrollButton, {})
|
|
7820
8050
|
]
|
|
7821
8051
|
}
|
|
7822
8052
|
);
|
|
7823
8053
|
}
|
|
8054
|
+
function ConversationHistoryLoadingState() {
|
|
8055
|
+
const skeletonClassName = "motion-reduce:animate-none";
|
|
8056
|
+
return /* @__PURE__ */ jsxs21(
|
|
8057
|
+
"div",
|
|
8058
|
+
{
|
|
8059
|
+
role: "status",
|
|
8060
|
+
"aria-label": "Loading chat history",
|
|
8061
|
+
"aria-busy": "true",
|
|
8062
|
+
"data-boring-agent-part": "chat-history-loading",
|
|
8063
|
+
className: "flex flex-col gap-7 py-4",
|
|
8064
|
+
children: [
|
|
8065
|
+
/* @__PURE__ */ jsxs21("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
|
|
8066
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
|
|
8067
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex min-w-0 flex-1 flex-col gap-2.5", children: [
|
|
8068
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-24 rounded-sm", skeletonClassName) }),
|
|
8069
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-full rounded-sm", skeletonClassName) }),
|
|
8070
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-4/5 rounded-sm", skeletonClassName) })
|
|
8071
|
+
] })
|
|
8072
|
+
] }),
|
|
8073
|
+
/* @__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) }) }),
|
|
8074
|
+
/* @__PURE__ */ jsxs21("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
|
|
8075
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
|
|
8076
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex min-w-0 flex-1 flex-col gap-3", children: [
|
|
8077
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-20 rounded-sm", skeletonClassName) }),
|
|
8078
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-20 w-full rounded-lg", skeletonClassName) }),
|
|
8079
|
+
/* @__PURE__ */ jsx26(Skeleton, { className: cn("h-3 w-3/5 rounded-sm", skeletonClassName) })
|
|
8080
|
+
] })
|
|
8081
|
+
] })
|
|
8082
|
+
]
|
|
8083
|
+
}
|
|
8084
|
+
);
|
|
8085
|
+
}
|
|
7824
8086
|
function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
|
|
7825
8087
|
const { scrollRef } = useStickToBottomContext2();
|
|
7826
8088
|
const pendingAnchor = useRef11(null);
|
|
@@ -7839,7 +8101,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
|
|
|
7839
8101
|
el.addEventListener("scroll", onScroll, { passive: true });
|
|
7840
8102
|
return () => el.removeEventListener("scroll", onScroll);
|
|
7841
8103
|
}, [scrollRef, onLoadOlder]);
|
|
7842
|
-
|
|
8104
|
+
useLayoutEffect2(() => {
|
|
7843
8105
|
const el = scrollRef.current;
|
|
7844
8106
|
if (!el) return;
|
|
7845
8107
|
if (pendingAnchor.current != null) {
|
|
@@ -7853,7 +8115,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
|
|
|
7853
8115
|
{
|
|
7854
8116
|
type: "button",
|
|
7855
8117
|
onClick: onLoadOlder,
|
|
7856
|
-
className: "rounded-full border border-border/60 px-3 py-1 text-
|
|
8118
|
+
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",
|
|
7857
8119
|
children: [
|
|
7858
8120
|
"Load ",
|
|
7859
8121
|
olderCount,
|
|
@@ -7885,8 +8147,8 @@ function buildMessageRenderItems(messages) {
|
|
|
7885
8147
|
}
|
|
7886
8148
|
|
|
7887
8149
|
// src/front/chat/components/PiChatComposerSurface.tsx
|
|
7888
|
-
import { useCallback as useCallback16, useLayoutEffect as
|
|
7889
|
-
import { motion as motion2 } from "motion/react";
|
|
8150
|
+
import { useCallback as useCallback16, useLayoutEffect as useLayoutEffect3 } from "react";
|
|
8151
|
+
import { motion as motion2, useReducedMotion } from "motion/react";
|
|
7890
8152
|
|
|
7891
8153
|
// src/front/composer/PluginUpdateStatus.tsx
|
|
7892
8154
|
import { useEffect as useEffect15, useRef as useRef12 } from "react";
|
|
@@ -8129,7 +8391,7 @@ function PluginUpdateStatus({
|
|
|
8129
8391
|
|
|
8130
8392
|
// src/front/composer/CommandRunStatus.tsx
|
|
8131
8393
|
import { useEffect as useEffect16, useRef as useRef13 } from "react";
|
|
8132
|
-
import { Fragment as
|
|
8394
|
+
import { Fragment as Fragment10, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
8133
8395
|
function CommandRunStatus({
|
|
8134
8396
|
state,
|
|
8135
8397
|
onDismiss,
|
|
@@ -8154,7 +8416,7 @@ function CommandRunStatus({
|
|
|
8154
8416
|
tone: "running",
|
|
8155
8417
|
dataAttribute: "data-boring-command-run",
|
|
8156
8418
|
maxWidthClassName,
|
|
8157
|
-
runningContent: state.command ? /* @__PURE__ */ jsxs24(
|
|
8419
|
+
runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
|
|
8158
8420
|
" Running ",
|
|
8159
8421
|
/* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
|
|
8160
8422
|
"/",
|
|
@@ -8172,7 +8434,7 @@ function CommandRunStatus({
|
|
|
8172
8434
|
tone: "success",
|
|
8173
8435
|
dataAttribute: "data-boring-command-run",
|
|
8174
8436
|
maxWidthClassName,
|
|
8175
|
-
title: state.command ? /* @__PURE__ */ jsxs24(
|
|
8437
|
+
title: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
|
|
8176
8438
|
" Ran ",
|
|
8177
8439
|
/* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
|
|
8178
8440
|
"/",
|
|
@@ -8192,7 +8454,7 @@ function CommandRunStatus({
|
|
|
8192
8454
|
tone: "error",
|
|
8193
8455
|
dataAttribute: "data-boring-command-run",
|
|
8194
8456
|
maxWidthClassName,
|
|
8195
|
-
title: state.command ? /* @__PURE__ */ jsxs24(
|
|
8457
|
+
title: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
|
|
8196
8458
|
" ",
|
|
8197
8459
|
/* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
|
|
8198
8460
|
"/",
|
|
@@ -8242,7 +8504,7 @@ function displayProviderLabel(provider) {
|
|
|
8242
8504
|
}
|
|
8243
8505
|
|
|
8244
8506
|
// src/front/chatPanelComposerControls.tsx
|
|
8245
|
-
import { Fragment as
|
|
8507
|
+
import { Fragment as Fragment11, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
8246
8508
|
var composerActionClass = cn(
|
|
8247
8509
|
"inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
|
|
8248
8510
|
"text-muted-foreground shadow-none transition-colors motion-reduce:transition-none",
|
|
@@ -8384,14 +8646,14 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
|
|
|
8384
8646
|
className
|
|
8385
8647
|
),
|
|
8386
8648
|
...props,
|
|
8387
|
-
children: trigger === "slash" ? /* @__PURE__ */ jsxs25(
|
|
8649
|
+
children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
|
|
8388
8650
|
/* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "Model \xB7 " }),
|
|
8389
8651
|
/* @__PURE__ */ jsx30("span", { className: "min-w-0 truncate text-foreground", children: triggerLabel }),
|
|
8390
8652
|
value ? /* @__PURE__ */ jsxs25("span", { className: "shrink-0 text-muted-foreground", children: [
|
|
8391
8653
|
" \xB7 ",
|
|
8392
8654
|
displayProviderLabel(value.provider)
|
|
8393
8655
|
] }) : null
|
|
8394
|
-
] }) : /* @__PURE__ */ jsxs25(
|
|
8656
|
+
] }) : /* @__PURE__ */ jsxs25(Fragment11, { children: [
|
|
8395
8657
|
/* @__PURE__ */ jsx30("span", { className: "min-w-0 truncate", children: triggerDisplay }),
|
|
8396
8658
|
/* @__PURE__ */ jsx30(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
|
|
8397
8659
|
] })
|
|
@@ -8680,10 +8942,10 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
|
|
|
8680
8942
|
...props,
|
|
8681
8943
|
children: [
|
|
8682
8944
|
trigger === "button" ? /* @__PURE__ */ jsx30(ThinkingLevelGlyph, { level: value }) : null,
|
|
8683
|
-
trigger === "slash" ? /* @__PURE__ */ jsxs25(
|
|
8945
|
+
trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
|
|
8684
8946
|
/* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "Thinking \xB7 " }),
|
|
8685
8947
|
/* @__PURE__ */ jsx30("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
|
|
8686
|
-
] }) : /* @__PURE__ */ jsxs25(
|
|
8948
|
+
] }) : /* @__PURE__ */ jsxs25(Fragment11, { children: [
|
|
8687
8949
|
/* @__PURE__ */ jsx30("span", { children: THINKING_LEVEL_LABELS[value] }),
|
|
8688
8950
|
/* @__PURE__ */ jsx30(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
|
|
8689
8951
|
] })
|
|
@@ -8847,7 +9109,7 @@ var PromptInputFooter = ({
|
|
|
8847
9109
|
);
|
|
8848
9110
|
|
|
8849
9111
|
// src/front/primitives/prompt-input.tsx
|
|
8850
|
-
import { Fragment as
|
|
9112
|
+
import { Fragment as Fragment12, jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
8851
9113
|
var PromptInput = ({
|
|
8852
9114
|
className,
|
|
8853
9115
|
accept,
|
|
@@ -9190,7 +9452,7 @@ var PromptInput = ({
|
|
|
9190
9452
|
},
|
|
9191
9453
|
[usingProvider, controller, files, onSubmit, clear]
|
|
9192
9454
|
);
|
|
9193
|
-
const inner = /* @__PURE__ */ jsxs27(
|
|
9455
|
+
const inner = /* @__PURE__ */ jsxs27(Fragment12, { children: [
|
|
9194
9456
|
/* @__PURE__ */ jsx32(
|
|
9195
9457
|
Input,
|
|
9196
9458
|
{
|
|
@@ -9359,7 +9621,7 @@ var PromptInputSubmit = ({
|
|
|
9359
9621
|
|
|
9360
9622
|
// src/front/primitives/slash-command-picker.tsx
|
|
9361
9623
|
import { useEffect as useEffect19, useMemo as useMemo14, useRef as useRef16, useState as useState18 } from "react";
|
|
9362
|
-
import { Fragment as
|
|
9624
|
+
import { Fragment as Fragment13, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
9363
9625
|
var ALL_PLUGINS = "__all__";
|
|
9364
9626
|
function commandGroup(cmd) {
|
|
9365
9627
|
if (cmd.source === "skill") return "skills";
|
|
@@ -9471,7 +9733,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
|
|
|
9471
9733
|
"/",
|
|
9472
9734
|
cmd.name
|
|
9473
9735
|
] }),
|
|
9474
|
-
cmd.source === "skill" ? /* @__PURE__ */ jsxs28(
|
|
9736
|
+
cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment13, { children: [
|
|
9475
9737
|
/* @__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" }),
|
|
9476
9738
|
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
|
|
9477
9739
|
] }) : cmd.sourcePlugin ? (
|
|
@@ -9488,7 +9750,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
|
|
|
9488
9750
|
}
|
|
9489
9751
|
|
|
9490
9752
|
// src/front/chat/components/ComposerAttachments.tsx
|
|
9491
|
-
import { AlertCircleIcon as AlertCircleIcon2, Loader2 as
|
|
9753
|
+
import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader22, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
|
|
9492
9754
|
import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
|
|
9493
9755
|
import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
9494
9756
|
function AttachmentButton({ disabled, className }) {
|
|
@@ -9503,7 +9765,7 @@ function AttachmentButton({ disabled, className }) {
|
|
|
9503
9765
|
onClick: () => {
|
|
9504
9766
|
if (!disabled) attachments.openFileDialog();
|
|
9505
9767
|
},
|
|
9506
|
-
className: cn(composerActionClass, "!h-10 !w-10", className),
|
|
9768
|
+
className: cn(composerActionClass, "composer-attachment-button !h-10 !w-10", className),
|
|
9507
9769
|
"aria-label": "Attach files",
|
|
9508
9770
|
title: disabled ? "Attachments are available when the composer is ready." : "Attach files",
|
|
9509
9771
|
children: /* @__PURE__ */ jsx34(PaperclipIcon2, { className: "h-4 w-4", strokeWidth: 1.75 })
|
|
@@ -9526,18 +9788,18 @@ function AttachmentsList() {
|
|
|
9526
9788
|
data: file,
|
|
9527
9789
|
onRemove: () => attachments.remove(file.id),
|
|
9528
9790
|
className: cn(
|
|
9529
|
-
"!h-9 !gap-2 !rounded-full !border-input/80 !bg-muted/40 !pl-1 !pr-2",
|
|
9791
|
+
"composer-attachment-chip !h-9 !gap-2 !rounded-full !border-input/80 !bg-muted/40 !pl-1 !pr-2",
|
|
9530
9792
|
"transition-colors hover:!bg-muted/70 hover:!text-foreground",
|
|
9531
9793
|
file.status === "error" && "!border-destructive/50 !bg-destructive/10"
|
|
9532
9794
|
),
|
|
9533
9795
|
children: [
|
|
9534
9796
|
/* @__PURE__ */ jsxs29("div", { className: "relative shrink-0", children: [
|
|
9535
9797
|
/* @__PURE__ */ jsx34(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
|
|
9536
|
-
file.status === "uploading" ? /* @__PURE__ */ jsx34("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx34(
|
|
9798
|
+
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,
|
|
9537
9799
|
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
|
|
9538
9800
|
] }),
|
|
9539
9801
|
/* @__PURE__ */ jsx34(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
|
|
9540
|
-
/* @__PURE__ */ jsx34(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
|
|
9802
|
+
/* @__PURE__ */ jsx34(AttachmentRemove, { className: "composer-attachment-remove !size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
|
|
9541
9803
|
]
|
|
9542
9804
|
},
|
|
9543
9805
|
file.id
|
|
@@ -9629,6 +9891,7 @@ function PiChatComposerSurface({
|
|
|
9629
9891
|
onSubmitMessage,
|
|
9630
9892
|
onStop
|
|
9631
9893
|
}) {
|
|
9894
|
+
const prefersReducedMotion = useReducedMotion();
|
|
9632
9895
|
const workspaceRequestId = getHeaderValue(requestHeaders, "x-boring-workspace-id");
|
|
9633
9896
|
const composerContributions = useComposerContributions();
|
|
9634
9897
|
const uploadAttachment = useCallback16((file) => uploadFile(file, {
|
|
@@ -9658,7 +9921,7 @@ function PiChatComposerSurface({
|
|
|
9658
9921
|
rail?.style.setProperty("--composer-input-group-height", `${inputGroupHeight}px`);
|
|
9659
9922
|
rail?.setAttribute("data-composer-multiline", inputGroupHeight > COMPOSER_INPUT_GROUP_MIN_HEIGHT ? "true" : "false");
|
|
9660
9923
|
}, []);
|
|
9661
|
-
|
|
9924
|
+
useLayoutEffect3(() => {
|
|
9662
9925
|
resizeTextarea(textareaRef.current);
|
|
9663
9926
|
}, [draft, resizeTextarea, textareaRef]);
|
|
9664
9927
|
return /* @__PURE__ */ jsxs30("div", { className: cn(
|
|
@@ -9681,15 +9944,15 @@ function PiChatComposerSurface({
|
|
|
9681
9944
|
"data-testid": isStreaming ? "chat-working" : void 0,
|
|
9682
9945
|
role: isStreaming ? "status" : void 0,
|
|
9683
9946
|
"aria-live": isStreaming ? "polite" : void 0,
|
|
9684
|
-
className: "inline-flex items-center gap-2 rounded-full border border-border/50 bg-background/85 px-2.5 py-1 text-[12px] text-
|
|
9947
|
+
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",
|
|
9685
9948
|
children: [
|
|
9686
9949
|
/* @__PURE__ */ jsx35(
|
|
9687
9950
|
motion2.span,
|
|
9688
9951
|
{
|
|
9689
9952
|
"aria-hidden": "true",
|
|
9690
9953
|
className: "inline-block size-1.5 rounded-full bg-[color:var(--accent)]",
|
|
9691
|
-
animate: { opacity: [0.35, 1, 0.35] },
|
|
9692
|
-
transition: { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
|
|
9954
|
+
animate: prefersReducedMotion ? { opacity: 1 } : { opacity: [0.35, 1, 0.35] },
|
|
9955
|
+
transition: prefersReducedMotion ? { duration: 0 } : { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
|
|
9693
9956
|
}
|
|
9694
9957
|
),
|
|
9695
9958
|
/* @__PURE__ */ jsx35("span", { children: "Working\u2026" })
|
|
@@ -9881,7 +10144,7 @@ function PiChatComposerSurface({
|
|
|
9881
10144
|
onStop,
|
|
9882
10145
|
disabled: submitDisabled,
|
|
9883
10146
|
className: cn(
|
|
9884
|
-
"h-8 w-8 shrink-0 rounded-full",
|
|
10147
|
+
"composer-submit-control h-8 w-8 shrink-0 rounded-full",
|
|
9885
10148
|
"bg-foreground text-background",
|
|
9886
10149
|
"transition-all duration-150 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
9887
10150
|
"hover:bg-foreground/90 hover:shadow-[0_0_0_3px_oklch(from_var(--foreground)_l_c_h/0.12)] hover:scale-[1.04]",
|
|
@@ -9920,6 +10183,7 @@ function PiChatComposerSurface({
|
|
|
9920
10183
|
disabled: isStreaming || modelControlled,
|
|
9921
10184
|
trigger: "slash",
|
|
9922
10185
|
open: modelPickerOpen,
|
|
10186
|
+
className: "composer-settings-trigger",
|
|
9923
10187
|
onClick: () => {
|
|
9924
10188
|
if (modelPickerOpen) {
|
|
9925
10189
|
onSetModelPickerOpen(false);
|
|
@@ -9936,6 +10200,7 @@ function PiChatComposerSurface({
|
|
|
9936
10200
|
disabled: isStreaming || thinkingControlled,
|
|
9937
10201
|
trigger: "slash",
|
|
9938
10202
|
open: thinkingPickerOpen,
|
|
10203
|
+
className: "composer-settings-trigger",
|
|
9939
10204
|
onClick: () => {
|
|
9940
10205
|
if (thinkingPickerOpen) {
|
|
9941
10206
|
onSetThinkingPickerOpen(false);
|
|
@@ -9958,86 +10223,6 @@ function getHeaderValue(headers, name) {
|
|
|
9958
10223
|
|
|
9959
10224
|
// src/front/chat/piChatPanelHooks.ts
|
|
9960
10225
|
import { useCallback as useCallback17, useEffect as useEffect20, useMemo as useMemo15, useRef as useRef17, useState as useState19, useSyncExternalStore } from "react";
|
|
9961
|
-
function useExternalRemotePiSession({
|
|
9962
|
-
sessionId,
|
|
9963
|
-
agentTypeId,
|
|
9964
|
-
workspaceId,
|
|
9965
|
-
storageScope,
|
|
9966
|
-
apiBaseUrl,
|
|
9967
|
-
requestHeaders,
|
|
9968
|
-
fetch: fetch2,
|
|
9969
|
-
createRemoteSession,
|
|
9970
|
-
remoteSessionOptions
|
|
9971
|
-
}) {
|
|
9972
|
-
const [session, setSession] = useState19();
|
|
9973
|
-
const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
|
|
9974
|
-
remoteSessionOptionsRef.current = remoteSessionOptions;
|
|
9975
|
-
const remoteSessionOptionsKey = useMemo15(
|
|
9976
|
-
() => remoteSessionOptionsIdentity2(remoteSessionOptions),
|
|
9977
|
-
[remoteSessionOptions]
|
|
9978
|
-
);
|
|
9979
|
-
useEffect20(() => {
|
|
9980
|
-
if (!sessionId) {
|
|
9981
|
-
setSession(void 0);
|
|
9982
|
-
return;
|
|
9983
|
-
}
|
|
9984
|
-
const next = (createRemoteSession ?? createRemotePiSession)({
|
|
9985
|
-
...remoteSessionOptionsRef.current,
|
|
9986
|
-
sessionId,
|
|
9987
|
-
agentTypeId,
|
|
9988
|
-
workspaceId,
|
|
9989
|
-
storageScope,
|
|
9990
|
-
apiBaseUrl,
|
|
9991
|
-
headers: requestHeaders,
|
|
9992
|
-
fetch: fetch2
|
|
9993
|
-
});
|
|
9994
|
-
setSession(next);
|
|
9995
|
-
return () => next.dispose();
|
|
9996
|
-
}, [agentTypeId, apiBaseUrl, createRemoteSession, fetch2, remoteSessionOptionsKey, requestHeaders, sessionId, storageScope, workspaceId]);
|
|
9997
|
-
return session;
|
|
9998
|
-
}
|
|
9999
|
-
var remoteSessionOptionObjectIds2 = /* @__PURE__ */ new WeakMap();
|
|
10000
|
-
var remoteSessionOptionObjectSeq2 = 0;
|
|
10001
|
-
function remoteSessionOptionObjectIdentity2(value) {
|
|
10002
|
-
if (typeof value !== "object" && typeof value !== "function" || value === null) return void 0;
|
|
10003
|
-
const object = value;
|
|
10004
|
-
let id = remoteSessionOptionObjectIds2.get(object);
|
|
10005
|
-
if (!id) {
|
|
10006
|
-
id = ++remoteSessionOptionObjectSeq2;
|
|
10007
|
-
remoteSessionOptionObjectIds2.set(object, id);
|
|
10008
|
-
}
|
|
10009
|
-
return String(id);
|
|
10010
|
-
}
|
|
10011
|
-
function remoteSessionOptionsIdentity2(options) {
|
|
10012
|
-
if (!options) return "{}";
|
|
10013
|
-
return JSON.stringify({
|
|
10014
|
-
autoStart: options.autoStart,
|
|
10015
|
-
requestTimeoutMs: options.requestTimeoutMs,
|
|
10016
|
-
onEvent: remoteSessionOptionObjectIdentity2(options.onEvent),
|
|
10017
|
-
storeOptions: remoteSessionOptionObjectIdentity2(options.storeOptions),
|
|
10018
|
-
setTimeoutFn: remoteSessionOptionObjectIdentity2(options.setTimeoutFn),
|
|
10019
|
-
clearTimeoutFn: remoteSessionOptionObjectIdentity2(options.clearTimeoutFn),
|
|
10020
|
-
reconnect: options.reconnect ? {
|
|
10021
|
-
baseMs: options.reconnect.baseMs,
|
|
10022
|
-
maxMs: options.reconnect.maxMs,
|
|
10023
|
-
jitterRatio: options.reconnect.jitterRatio,
|
|
10024
|
-
random: remoteSessionOptionObjectIdentity2(options.reconnect.random)
|
|
10025
|
-
} : void 0,
|
|
10026
|
-
debug: options.debug ? {
|
|
10027
|
-
largeStateWarningBytes: options.debug.largeStateWarningBytes,
|
|
10028
|
-
largeStateWarningMessages: options.debug.largeStateWarningMessages,
|
|
10029
|
-
onWarning: remoteSessionOptionObjectIdentity2(options.debug.onWarning)
|
|
10030
|
-
} : void 0
|
|
10031
|
-
});
|
|
10032
|
-
}
|
|
10033
|
-
function useRemotePiSessionState(session) {
|
|
10034
|
-
return useSyncExternalStore(
|
|
10035
|
-
useCallback17((listener) => session?.subscribe(listener) ?? (() => {
|
|
10036
|
-
}), [session]),
|
|
10037
|
-
useCallback17(() => session?.getState(), [session]),
|
|
10038
|
-
useCallback17(() => session?.getState(), [session])
|
|
10039
|
-
);
|
|
10040
|
-
}
|
|
10041
10226
|
|
|
10042
10227
|
// src/front/chat/piChatPanelUtils.ts
|
|
10043
10228
|
function headersContentKey(headers) {
|
|
@@ -10144,9 +10329,103 @@ function errorMessage2(error, fallback) {
|
|
|
10144
10329
|
return error instanceof Error && error.message ? error.message : fallback;
|
|
10145
10330
|
}
|
|
10146
10331
|
|
|
10332
|
+
// src/front/chat/piChatPanelHooks.ts
|
|
10333
|
+
function useExternalRemotePiSession({
|
|
10334
|
+
sessionId,
|
|
10335
|
+
agentTypeId,
|
|
10336
|
+
workspaceId,
|
|
10337
|
+
storageScope,
|
|
10338
|
+
apiBaseUrl,
|
|
10339
|
+
requestHeaders,
|
|
10340
|
+
fetch: fetch2,
|
|
10341
|
+
createRemoteSession,
|
|
10342
|
+
remoteSessionOptions,
|
|
10343
|
+
enabled = true
|
|
10344
|
+
}) {
|
|
10345
|
+
const [session, setSession] = useState19();
|
|
10346
|
+
const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
|
|
10347
|
+
remoteSessionOptionsRef.current = remoteSessionOptions;
|
|
10348
|
+
const requestHeadersKey = headersContentKey(requestHeaders);
|
|
10349
|
+
const stableRequestHeaders = useMemo15(
|
|
10350
|
+
() => normalizedHeadersFromContentKey(requestHeadersKey),
|
|
10351
|
+
[requestHeadersKey]
|
|
10352
|
+
);
|
|
10353
|
+
const remoteSessionOptionsKey = useMemo15(
|
|
10354
|
+
() => remoteSessionOptionsIdentity2(remoteSessionOptions),
|
|
10355
|
+
[remoteSessionOptions]
|
|
10356
|
+
);
|
|
10357
|
+
useEffect20(() => {
|
|
10358
|
+
if (!sessionId) {
|
|
10359
|
+
setSession(void 0);
|
|
10360
|
+
return;
|
|
10361
|
+
}
|
|
10362
|
+
const next = (createRemoteSession ?? createRemotePiSession)({
|
|
10363
|
+
...remoteSessionOptionsRef.current,
|
|
10364
|
+
autoStart: false,
|
|
10365
|
+
sessionId,
|
|
10366
|
+
agentTypeId,
|
|
10367
|
+
workspaceId,
|
|
10368
|
+
storageScope,
|
|
10369
|
+
apiBaseUrl,
|
|
10370
|
+
headers: stableRequestHeaders,
|
|
10371
|
+
fetch: fetch2
|
|
10372
|
+
});
|
|
10373
|
+
setSession(next);
|
|
10374
|
+
return () => next.dispose();
|
|
10375
|
+
}, [agentTypeId, apiBaseUrl, createRemoteSession, fetch2, remoteSessionOptionsKey, sessionId, stableRequestHeaders, storageScope, workspaceId]);
|
|
10376
|
+
useEffect20(() => {
|
|
10377
|
+
if (!session) return;
|
|
10378
|
+
if (enabled) session.resumeStream?.();
|
|
10379
|
+
else session.suspendStream?.();
|
|
10380
|
+
}, [enabled, session]);
|
|
10381
|
+
return session;
|
|
10382
|
+
}
|
|
10383
|
+
var remoteSessionOptionObjectIds2 = /* @__PURE__ */ new WeakMap();
|
|
10384
|
+
var remoteSessionOptionObjectSeq2 = 0;
|
|
10385
|
+
function remoteSessionOptionObjectIdentity2(value) {
|
|
10386
|
+
if (typeof value !== "object" && typeof value !== "function" || value === null) return void 0;
|
|
10387
|
+
const object = value;
|
|
10388
|
+
let id = remoteSessionOptionObjectIds2.get(object);
|
|
10389
|
+
if (!id) {
|
|
10390
|
+
id = ++remoteSessionOptionObjectSeq2;
|
|
10391
|
+
remoteSessionOptionObjectIds2.set(object, id);
|
|
10392
|
+
}
|
|
10393
|
+
return String(id);
|
|
10394
|
+
}
|
|
10395
|
+
function remoteSessionOptionsIdentity2(options) {
|
|
10396
|
+
if (!options) return "{}";
|
|
10397
|
+
return JSON.stringify({
|
|
10398
|
+
autoStart: options.autoStart,
|
|
10399
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
10400
|
+
onEvent: remoteSessionOptionObjectIdentity2(options.onEvent),
|
|
10401
|
+
storeOptions: remoteSessionOptionObjectIdentity2(options.storeOptions),
|
|
10402
|
+
setTimeoutFn: remoteSessionOptionObjectIdentity2(options.setTimeoutFn),
|
|
10403
|
+
clearTimeoutFn: remoteSessionOptionObjectIdentity2(options.clearTimeoutFn),
|
|
10404
|
+
reconnect: options.reconnect ? {
|
|
10405
|
+
baseMs: options.reconnect.baseMs,
|
|
10406
|
+
maxMs: options.reconnect.maxMs,
|
|
10407
|
+
jitterRatio: options.reconnect.jitterRatio,
|
|
10408
|
+
random: remoteSessionOptionObjectIdentity2(options.reconnect.random)
|
|
10409
|
+
} : void 0,
|
|
10410
|
+
debug: options.debug ? {
|
|
10411
|
+
largeStateWarningBytes: options.debug.largeStateWarningBytes,
|
|
10412
|
+
largeStateWarningMessages: options.debug.largeStateWarningMessages,
|
|
10413
|
+
onWarning: remoteSessionOptionObjectIdentity2(options.debug.onWarning)
|
|
10414
|
+
} : void 0
|
|
10415
|
+
});
|
|
10416
|
+
}
|
|
10417
|
+
function useRemotePiSessionState(session) {
|
|
10418
|
+
return useSyncExternalStore(
|
|
10419
|
+
useCallback17((listener) => session?.subscribe(listener) ?? (() => {
|
|
10420
|
+
}), [session]),
|
|
10421
|
+
useCallback17(() => session?.getState(), [session]),
|
|
10422
|
+
useCallback17(() => session?.getState(), [session])
|
|
10423
|
+
);
|
|
10424
|
+
}
|
|
10425
|
+
|
|
10147
10426
|
// src/front/chat/PiChatPanel.tsx
|
|
10148
10427
|
import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
10149
|
-
var DebugDrawer2 = lazy(() => import("../DebugDrawer-
|
|
10428
|
+
var DebugDrawer2 = lazy(() => import("../DebugDrawer-2TO5SE2D.js").then((m) => ({ default: m.DebugDrawer })));
|
|
10150
10429
|
var EMPTY_COMMANDS = [];
|
|
10151
10430
|
var EMPTY_COMMAND_NAMES = [];
|
|
10152
10431
|
var EMPTY_BLOCKERS = [];
|
|
@@ -10282,6 +10561,7 @@ function PiChatPanel({
|
|
|
10282
10561
|
const selectedSessionPending = Boolean(activeSessionId && !selectedChatState);
|
|
10283
10562
|
const modelDiscoveryEnabled = serverResourcesEnabled && availableModels === void 0;
|
|
10284
10563
|
const modelDiscovery = useChatModelSelection({
|
|
10564
|
+
agentTypeId,
|
|
10285
10565
|
apiBaseUrl,
|
|
10286
10566
|
defaultModel,
|
|
10287
10567
|
fetch: fetch2,
|
|
@@ -10385,6 +10665,7 @@ function PiChatPanel({
|
|
|
10385
10665
|
return next;
|
|
10386
10666
|
}, [apiBaseUrl, commands, contributedCommands, excludeBuiltinCommands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
|
|
10387
10667
|
const commandsStamp = useServerCommands({
|
|
10668
|
+
agentTypeId,
|
|
10388
10669
|
registry,
|
|
10389
10670
|
requestHeaders: normalizedRequestHeaders,
|
|
10390
10671
|
sessionId: activeSessionId ?? "default",
|
|
@@ -10414,7 +10695,8 @@ function PiChatPanel({
|
|
|
10414
10695
|
const queuePreview = selectedChatState ? selectQueuePreview(selectedChatState) : [];
|
|
10415
10696
|
const messages = canonicalMessages;
|
|
10416
10697
|
const userHistory = useMemo16(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
|
|
10417
|
-
const
|
|
10698
|
+
const awaitingHydration = Boolean(hydrateMessages && selectedChatState && !selectedChatState.hydrated);
|
|
10699
|
+
const emptyStateHydrating = statusForState(selectedChatState, sessionsLoading || chatStatePending || selectedSessionPending) === "hydrating" || awaitingHydration;
|
|
10418
10700
|
const emptyHero = emptyPlacement === "hero" && messages.length === 0 && queuePreview.length === 0 && !emptyStateHydrating;
|
|
10419
10701
|
const debugState = selectedPiSession?.getDebugState();
|
|
10420
10702
|
const composerBlocked = workspaceWarmupBlocked || activeBlockers.length > 0;
|
|
@@ -10430,8 +10712,9 @@ function PiChatPanel({
|
|
|
10430
10712
|
text: `Large chat state: ${debugState.largeStateWarning.messageCount} messages, approximately ${debugState.largeStateWarning.approxBytes} bytes.`,
|
|
10431
10713
|
dismissible: true
|
|
10432
10714
|
}] : [];
|
|
10433
|
-
|
|
10434
|
-
|
|
10715
|
+
const combined = [...fromState, ...sessionNotice, ...largeStateNotice, ...localNotices].filter((notice) => !dismissedNoticeIds.has(notice.id));
|
|
10716
|
+
return filterCompetingNoiseNotices(combined, messages.length === 0);
|
|
10717
|
+
}, [debug, debugState?.largeStateWarning, dismissedNoticeIds, localNotices, messages.length, selectedChatState, sessionsError]);
|
|
10435
10718
|
const addLocalNotice = useCallback18((notice) => {
|
|
10436
10719
|
setLocalNotices((previous) => {
|
|
10437
10720
|
const next = previous.filter((candidate) => candidate.id !== notice.id);
|
|
@@ -10462,11 +10745,11 @@ function PiChatPanel({
|
|
|
10462
10745
|
if (resetInProgressRef.current) return;
|
|
10463
10746
|
if (autoCreateInFlightRef.current) return;
|
|
10464
10747
|
autoCreateInFlightRef.current = true;
|
|
10465
|
-
void sessions.create().catch((error) => {
|
|
10748
|
+
void sessions.create({ resumeSessionId: sessions.resumeSessionId }).catch((error) => {
|
|
10466
10749
|
autoCreateInFlightRef.current = false;
|
|
10467
10750
|
addLocalNotice({ id: "session-auto-create-error", level: "error", text: errorMessage2(error, "Could not create a chat session."), dismissible: true });
|
|
10468
10751
|
});
|
|
10469
|
-
}, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessionsError, sessionsLoading]);
|
|
10752
|
+
}, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessions.resumeSessionId, sessionsError, sessionsLoading]);
|
|
10470
10753
|
useEffect21(() => {
|
|
10471
10754
|
if (externalSessionId || sessionsError || activeSessionId || sessionList.length > 0) {
|
|
10472
10755
|
autoCreateInFlightRef.current = false;
|
|
@@ -10914,14 +11197,18 @@ function PiChatPanel({
|
|
|
10914
11197
|
}, [isStreaming]);
|
|
10915
11198
|
useEffect21(() => {
|
|
10916
11199
|
if (typeof window === "undefined" || !activeChatSessionId) return;
|
|
10917
|
-
window.dispatchEvent(new CustomEvent("boring:chat-session-status", {
|
|
11200
|
+
const emitStatus = () => window.dispatchEvent(new CustomEvent("boring:chat-session-status", {
|
|
10918
11201
|
detail: {
|
|
11202
|
+
...workspaceId ? { workspaceId } : {},
|
|
10919
11203
|
sessionId: activeChatSessionId,
|
|
10920
11204
|
...agentTypeId ? { agentTypeId } : {},
|
|
10921
11205
|
working: isStreaming
|
|
10922
11206
|
}
|
|
10923
11207
|
}));
|
|
10924
|
-
|
|
11208
|
+
emitStatus();
|
|
11209
|
+
window.addEventListener("boring:chat-session-status-request", emitStatus);
|
|
11210
|
+
return () => window.removeEventListener("boring:chat-session-status-request", emitStatus);
|
|
11211
|
+
}, [activeChatSessionId, agentTypeId, isStreaming, workspaceId]);
|
|
10925
11212
|
const onTextareaKeyDown = useCallback18((event) => {
|
|
10926
11213
|
if (event.key === "Escape" && isStreaming) {
|
|
10927
11214
|
if (event.defaultPrevented || mentionState !== null || slashQuery !== null) {
|
|
@@ -11011,6 +11298,7 @@ function PiChatPanel({
|
|
|
11011
11298
|
{
|
|
11012
11299
|
"data-boring-agent": "",
|
|
11013
11300
|
"data-boring-agent-part": "chat",
|
|
11301
|
+
"data-agent-type-id": agentTypeId,
|
|
11014
11302
|
"data-pi-chat-session-id": activeSessionId,
|
|
11015
11303
|
"data-pi-chat-connection": debugState?.connection ?? "disconnected",
|
|
11016
11304
|
"data-pi-chat-last-seq": debugState?.lastSeq ?? 0,
|
|
@@ -11089,6 +11377,7 @@ function PiChatPanel({
|
|
|
11089
11377
|
debug ? /* @__PURE__ */ jsx36(Suspense, { fallback: null, children: /* @__PURE__ */ jsx36("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx36(
|
|
11090
11378
|
DebugDrawer2,
|
|
11091
11379
|
{
|
|
11380
|
+
agentTypeId,
|
|
11092
11381
|
apiBaseUrl,
|
|
11093
11382
|
fetch: fetch2,
|
|
11094
11383
|
sessionId: activeSessionId ?? activeChatSessionId ?? "unknown",
|
|
@@ -11105,6 +11394,207 @@ function PiChatPanel({
|
|
|
11105
11394
|
) });
|
|
11106
11395
|
}
|
|
11107
11396
|
|
|
11397
|
+
// src/front/chat/useAddressedAgentSelection.ts
|
|
11398
|
+
import { useCallback as useCallback19, useEffect as useEffect22, useMemo as useMemo17, useState as useState21 } from "react";
|
|
11399
|
+
|
|
11400
|
+
// src/front/chat/gatewayResponseError.ts
|
|
11401
|
+
var RUNTIME_SCOPE_MISMATCH_MESSAGE = "This chat belongs to a previous runtime configuration and can no longer be changed.";
|
|
11402
|
+
var GatewayResponseError = class extends Error {
|
|
11403
|
+
constructor(status, message, code3, details, body, operation) {
|
|
11404
|
+
super(message);
|
|
11405
|
+
this.status = status;
|
|
11406
|
+
this.code = code3;
|
|
11407
|
+
this.details = details;
|
|
11408
|
+
this.body = body;
|
|
11409
|
+
this.operation = operation;
|
|
11410
|
+
this.name = "GatewayResponseError";
|
|
11411
|
+
this.errorCode = code3;
|
|
11412
|
+
}
|
|
11413
|
+
status;
|
|
11414
|
+
code;
|
|
11415
|
+
details;
|
|
11416
|
+
body;
|
|
11417
|
+
operation;
|
|
11418
|
+
errorCode;
|
|
11419
|
+
};
|
|
11420
|
+
async function gatewayResponseError(response, fallbackMessage, operation) {
|
|
11421
|
+
const body = await safeReadJson2(response);
|
|
11422
|
+
return gatewayResponseErrorFromBody(response.status, body, fallbackMessage, operation);
|
|
11423
|
+
}
|
|
11424
|
+
function gatewayResponseErrorFromBody(status, body, fallbackMessage, operation) {
|
|
11425
|
+
const payload = errorPayload(body);
|
|
11426
|
+
const code3 = gatewayResponseErrorCode(payload?.code);
|
|
11427
|
+
const serverMessage = typeof payload?.message === "string" && payload.message.trim().length > 0 ? payload.message : fallbackMessage;
|
|
11428
|
+
const message = code3 === AgentGatewayErrorCode.AGENT_SESSION_RUNTIME_SCOPE_MISMATCH ? RUNTIME_SCOPE_MISMATCH_MESSAGE : serverMessage;
|
|
11429
|
+
const details = isJsonValue(payload?.details) ? payload.details : void 0;
|
|
11430
|
+
return new GatewayResponseError(status, message, code3, details, body, operation);
|
|
11431
|
+
}
|
|
11432
|
+
function gatewayResponseErrorCode(value) {
|
|
11433
|
+
const sharedCode = ErrorCode.safeParse(value);
|
|
11434
|
+
if (sharedCode.success) return sharedCode.data;
|
|
11435
|
+
return typeof value === "string" && AGENT_GATEWAY_ERROR_CODES.includes(value) ? value : void 0;
|
|
11436
|
+
}
|
|
11437
|
+
function errorResponseCode(error) {
|
|
11438
|
+
if (error instanceof GatewayResponseError) return error.code;
|
|
11439
|
+
const record = typeof error === "object" && error !== null ? error : void 0;
|
|
11440
|
+
return gatewayResponseErrorCode(record?.code ?? record?.errorCode);
|
|
11441
|
+
}
|
|
11442
|
+
function isRuntimeScopeMismatchError(error) {
|
|
11443
|
+
return errorResponseCode(error) === AgentGatewayErrorCode.AGENT_SESSION_RUNTIME_SCOPE_MISMATCH;
|
|
11444
|
+
}
|
|
11445
|
+
async function safeReadJson2(response) {
|
|
11446
|
+
const text = await response.text();
|
|
11447
|
+
if (!text) return void 0;
|
|
11448
|
+
try {
|
|
11449
|
+
return JSON.parse(text);
|
|
11450
|
+
} catch {
|
|
11451
|
+
return void 0;
|
|
11452
|
+
}
|
|
11453
|
+
}
|
|
11454
|
+
function errorPayload(body) {
|
|
11455
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
11456
|
+
const record = body;
|
|
11457
|
+
return typeof record.error === "object" && record.error !== null ? record.error : record;
|
|
11458
|
+
}
|
|
11459
|
+
function isJsonValue(value) {
|
|
11460
|
+
if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
|
|
11461
|
+
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
11462
|
+
return typeof value === "object" && value !== null && Object.values(value).every(isJsonValue);
|
|
11463
|
+
}
|
|
11464
|
+
|
|
11465
|
+
// src/front/chat/useAddressedAgentSelection.ts
|
|
11466
|
+
var inFlightDiscoveries = /* @__PURE__ */ new WeakMap();
|
|
11467
|
+
function useAddressedAgentSelection({
|
|
11468
|
+
apiBaseUrl,
|
|
11469
|
+
requestHeaders,
|
|
11470
|
+
storageScope,
|
|
11471
|
+
fetch: fetch2,
|
|
11472
|
+
enabled = false
|
|
11473
|
+
} = {}) {
|
|
11474
|
+
const normalizedBaseUrl = apiBaseUrl?.replace(/\/$/, "") ?? "";
|
|
11475
|
+
const headersKey = useMemo17(
|
|
11476
|
+
() => JSON.stringify({
|
|
11477
|
+
storageScope: storageScope ?? "",
|
|
11478
|
+
headers: Object.entries(requestHeaders ?? {}).sort(([a], [b]) => a.localeCompare(b))
|
|
11479
|
+
}),
|
|
11480
|
+
[requestHeaders, storageScope]
|
|
11481
|
+
);
|
|
11482
|
+
const discoveryKey = `${normalizedBaseUrl}
|
|
11483
|
+
${headersKey}`;
|
|
11484
|
+
const fetchImpl = fetch2 ?? globalThis.fetch;
|
|
11485
|
+
const [state, setState] = useState21(() => ({
|
|
11486
|
+
discoveryKey: enabled ? "" : discoveryKey,
|
|
11487
|
+
agents: [],
|
|
11488
|
+
selectedAgentTypeId: void 0,
|
|
11489
|
+
loading: enabled,
|
|
11490
|
+
error: void 0
|
|
11491
|
+
}));
|
|
11492
|
+
useEffect22(() => {
|
|
11493
|
+
if (!enabled) {
|
|
11494
|
+
setState({
|
|
11495
|
+
discoveryKey,
|
|
11496
|
+
agents: [],
|
|
11497
|
+
selectedAgentTypeId: void 0,
|
|
11498
|
+
loading: false,
|
|
11499
|
+
error: void 0
|
|
11500
|
+
});
|
|
11501
|
+
return;
|
|
11502
|
+
}
|
|
11503
|
+
let cancelled = false;
|
|
11504
|
+
setState((previous) => ({
|
|
11505
|
+
discoveryKey,
|
|
11506
|
+
agents: previous.discoveryKey === discoveryKey ? previous.agents : [],
|
|
11507
|
+
selectedAgentTypeId: previous.discoveryKey === discoveryKey ? previous.selectedAgentTypeId : void 0,
|
|
11508
|
+
loading: true,
|
|
11509
|
+
error: void 0
|
|
11510
|
+
}));
|
|
11511
|
+
void discoverAgents(fetchImpl, discoveryKey, `${normalizedBaseUrl}/api/v1/agents`, {
|
|
11512
|
+
headers: scopedHeaders(requestHeaders, storageScope)
|
|
11513
|
+
}).then((agents2) => {
|
|
11514
|
+
if (cancelled) return;
|
|
11515
|
+
setState((previous) => ({
|
|
11516
|
+
discoveryKey,
|
|
11517
|
+
agents: agents2,
|
|
11518
|
+
selectedAgentTypeId: agents2.some((agent) => agent.agentTypeId === previous.selectedAgentTypeId) ? previous.selectedAgentTypeId : agents2[0]?.agentTypeId,
|
|
11519
|
+
loading: false,
|
|
11520
|
+
error: void 0
|
|
11521
|
+
}));
|
|
11522
|
+
}).catch((error) => {
|
|
11523
|
+
if (cancelled) return;
|
|
11524
|
+
setState({
|
|
11525
|
+
discoveryKey,
|
|
11526
|
+
agents: [],
|
|
11527
|
+
selectedAgentTypeId: void 0,
|
|
11528
|
+
loading: false,
|
|
11529
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
11530
|
+
});
|
|
11531
|
+
});
|
|
11532
|
+
return () => {
|
|
11533
|
+
cancelled = true;
|
|
11534
|
+
};
|
|
11535
|
+
}, [discoveryKey, enabled, fetchImpl, normalizedBaseUrl]);
|
|
11536
|
+
const current = state.discoveryKey === discoveryKey;
|
|
11537
|
+
const agents = enabled && current ? state.agents : [];
|
|
11538
|
+
const selectedAgentTypeId = enabled && current ? state.selectedAgentTypeId : void 0;
|
|
11539
|
+
const selectAgentTypeId = useCallback19((agentTypeId) => {
|
|
11540
|
+
setState((previous) => {
|
|
11541
|
+
if (previous.discoveryKey !== discoveryKey) return previous;
|
|
11542
|
+
if (!previous.agents.some((agent) => agent.agentTypeId === agentTypeId)) return previous;
|
|
11543
|
+
return { ...previous, selectedAgentTypeId: agentTypeId };
|
|
11544
|
+
});
|
|
11545
|
+
}, [discoveryKey]);
|
|
11546
|
+
return {
|
|
11547
|
+
agents,
|
|
11548
|
+
selectedAgentTypeId,
|
|
11549
|
+
loading: enabled && (!current || state.loading),
|
|
11550
|
+
error: enabled && current ? state.error : void 0,
|
|
11551
|
+
selectAgentTypeId
|
|
11552
|
+
};
|
|
11553
|
+
}
|
|
11554
|
+
function discoverAgents(fetchImpl, discoveryKey, url, init) {
|
|
11555
|
+
let requests = inFlightDiscoveries.get(fetchImpl);
|
|
11556
|
+
if (!requests) {
|
|
11557
|
+
requests = /* @__PURE__ */ new Map();
|
|
11558
|
+
inFlightDiscoveries.set(fetchImpl, requests);
|
|
11559
|
+
}
|
|
11560
|
+
const existing = requests.get(discoveryKey);
|
|
11561
|
+
if (existing) return existing;
|
|
11562
|
+
const request = fetchImpl(url, init).then(async (response) => {
|
|
11563
|
+
if (!response.ok) throw await gatewayResponseError(response, "Failed to load agents.", "load agents");
|
|
11564
|
+
return parseAgentOptions(await response.json());
|
|
11565
|
+
});
|
|
11566
|
+
requests.set(discoveryKey, request);
|
|
11567
|
+
void request.finally(() => {
|
|
11568
|
+
if (requests?.get(discoveryKey) === request) requests.delete(discoveryKey);
|
|
11569
|
+
}).catch(() => void 0);
|
|
11570
|
+
return request;
|
|
11571
|
+
}
|
|
11572
|
+
function parseAgentOptions(value) {
|
|
11573
|
+
if (!Array.isArray(value)) throw new Error("Failed to load agents: invalid response");
|
|
11574
|
+
return value.map((item) => {
|
|
11575
|
+
if (typeof item !== "object" || item === null) throw new Error("Failed to load agents: invalid response");
|
|
11576
|
+
const record = item;
|
|
11577
|
+
if (typeof record.agentTypeId !== "string" || record.agentTypeId.length === 0) {
|
|
11578
|
+
throw new Error("Failed to load agents: invalid response");
|
|
11579
|
+
}
|
|
11580
|
+
return {
|
|
11581
|
+
agentTypeId: record.agentTypeId,
|
|
11582
|
+
label: typeof record.label === "string" && record.label.length > 0 ? record.label : record.agentTypeId,
|
|
11583
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
11584
|
+
...Array.isArray(record.pluginIds) ? { pluginIds: record.pluginIds.filter((pluginId) => typeof pluginId === "string" && pluginId.length > 0) } : {}
|
|
11585
|
+
};
|
|
11586
|
+
});
|
|
11587
|
+
}
|
|
11588
|
+
function scopedHeaders(headers, storageScope) {
|
|
11589
|
+
const result = {};
|
|
11590
|
+
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
11591
|
+
if (typeof value === "string") result[key] = value;
|
|
11592
|
+
}
|
|
11593
|
+
const hasStorageScope = Object.keys(result).some((key) => key.toLowerCase() === "x-boring-storage-scope");
|
|
11594
|
+
if (storageScope && !hasStorageScope) result["x-boring-storage-scope"] = storageScope;
|
|
11595
|
+
return result;
|
|
11596
|
+
}
|
|
11597
|
+
|
|
11108
11598
|
// src/front/commands.ts
|
|
11109
11599
|
var AGENT_COMMAND_SOURCE = "agent";
|
|
11110
11600
|
function getAgentCommands(options = {}) {
|
|
@@ -11150,6 +11640,7 @@ export {
|
|
|
11150
11640
|
ConversationContent,
|
|
11151
11641
|
ConversationScrollButton,
|
|
11152
11642
|
DebugDrawer,
|
|
11643
|
+
GatewayResponseError,
|
|
11153
11644
|
Message,
|
|
11154
11645
|
MessageAction,
|
|
11155
11646
|
MessageActions,
|
|
@@ -11165,6 +11656,7 @@ export {
|
|
|
11165
11656
|
PromptInputFooter,
|
|
11166
11657
|
PromptInputSubmit,
|
|
11167
11658
|
PromptInputTextarea,
|
|
11659
|
+
RUNTIME_SCOPE_MISMATCH_MESSAGE,
|
|
11168
11660
|
Reasoning,
|
|
11169
11661
|
ReasoningContent,
|
|
11170
11662
|
ReasoningTrigger,
|
|
@@ -11175,12 +11667,15 @@ export {
|
|
|
11175
11667
|
ToolHeader,
|
|
11176
11668
|
activeSessionStorageKey,
|
|
11177
11669
|
builtinCommands,
|
|
11178
|
-
clearActiveSessionId,
|
|
11179
11670
|
cn,
|
|
11180
11671
|
createCommandRegistry,
|
|
11181
11672
|
defaultChatSuggestions,
|
|
11182
11673
|
defaultToolRenderers,
|
|
11674
|
+
errorResponseCode,
|
|
11675
|
+
gatewayResponseError,
|
|
11676
|
+
gatewayResponseErrorFromBody,
|
|
11183
11677
|
getAgentCommands,
|
|
11678
|
+
isRuntimeScopeMismatchError,
|
|
11184
11679
|
matchPiSessionSearch,
|
|
11185
11680
|
mergeShadcnToolRenderers,
|
|
11186
11681
|
mergeToolRenderers,
|
|
@@ -11190,6 +11685,7 @@ export {
|
|
|
11190
11685
|
resolveToolRenderer,
|
|
11191
11686
|
searchPiSessions,
|
|
11192
11687
|
uploadFile,
|
|
11688
|
+
useAddressedAgentSelection,
|
|
11193
11689
|
useOpenArtifact,
|
|
11194
11690
|
usePiSessions,
|
|
11195
11691
|
writeActiveSessionId
|