@hachej/boring-agent 0.1.42 → 0.1.44
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-2PUDZ2RL.js → DebugDrawer-RYEBQ6CO.js} +1 -1
- package/dist/{agentPluginEvents-ZoOjcb5J.d.ts → agentPluginEvents-Dgm57gEU.d.ts} +15 -3
- package/dist/chatSubmitPayload-DHqQL2wD.d.ts +48 -0
- package/dist/{chunk-IUJ22EFB.js → chunk-BOIV2I3N.js} +2 -10
- package/dist/{chunk-VQ7VSSSX.js → chunk-HEEJSOFF.js} +8 -2
- package/dist/front/index.d.ts +24 -4
- package/dist/front/index.js +234 -118
- package/dist/front/styles.css +0 -6
- package/dist/piChatEvent-DNKo77Xw.d.ts +306 -0
- package/dist/server/index.d.ts +363 -94
- package/dist/server/index.js +1288 -170
- package/dist/shared/index.d.ts +37 -23
- package/dist/shared/index.js +1 -1
- package/docs/ERROR_CODES.md +4 -0
- package/package.json +2 -2
- package/dist/piChatEvent-Ck1BAE_m.d.ts +0 -323
- package/dist/session-BRovhe0D.d.ts +0 -27
package/dist/front/index.js
CHANGED
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
StopReceiptSchema,
|
|
14
14
|
extractToolUiMetadata,
|
|
15
15
|
sanitizeToolUiMetadata
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-HEEJSOFF.js";
|
|
17
17
|
import {
|
|
18
18
|
DebugDrawer,
|
|
19
19
|
cn,
|
|
20
20
|
copyTextToClipboard
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-BOIV2I3N.js";
|
|
22
22
|
|
|
23
23
|
// src/front/upload/uploadFile.ts
|
|
24
24
|
function readAsDataUrl(file) {
|
|
@@ -30,14 +30,14 @@ function readAsDataUrl(file) {
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
async function uploadFile(file, opts = {}) {
|
|
33
|
-
const { apiBaseUrl = "", workspaceRequestId, directory, sourcePath } = opts;
|
|
33
|
+
const { apiBaseUrl = "", workspaceRequestId, directory, sourcePath, fetch: fetchImpl = globalThis.fetch } = opts;
|
|
34
34
|
const dataUrl = await readAsDataUrl(file);
|
|
35
35
|
const comma = dataUrl.indexOf(",");
|
|
36
36
|
const contentBase64 = comma >= 0 ? dataUrl.slice(comma + 1) : "";
|
|
37
37
|
const headers = { "Content-Type": "application/json" };
|
|
38
38
|
if (workspaceRequestId) headers["x-boring-workspace-id"] = workspaceRequestId;
|
|
39
39
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
40
|
-
const res = await
|
|
40
|
+
const res = await fetchImpl(`${base}/api/v1/files/upload`, {
|
|
41
41
|
method: "POST",
|
|
42
42
|
headers,
|
|
43
43
|
credentials: "include",
|
|
@@ -2230,7 +2230,8 @@ async function resolveAttachmentUrls(files) {
|
|
|
2230
2230
|
return Promise.all(files.map(async (file) => ({
|
|
2231
2231
|
filename: file.filename,
|
|
2232
2232
|
mediaType: file.mediaType,
|
|
2233
|
-
url: file.url.startsWith("blob:") ? await convertBlobUrlToDataUrl(file.url) ?? file.url : file.url
|
|
2233
|
+
url: file.url.startsWith("blob:") ? await convertBlobUrlToDataUrl(file.url) ?? file.url : file.url,
|
|
2234
|
+
...typeof file.path === "string" ? { path: file.path } : {}
|
|
2234
2235
|
})));
|
|
2235
2236
|
}
|
|
2236
2237
|
async function readFileAsText(file) {
|
|
@@ -2255,14 +2256,18 @@ async function createEnrichedSubmitPayload({
|
|
|
2255
2256
|
for (const file of files ?? []) {
|
|
2256
2257
|
const label = file.filename ?? "attachment";
|
|
2257
2258
|
const mime = file.mediaType ?? "application/octet-stream";
|
|
2259
|
+
const workspacePath = typeof file.path === "string" ? file.path : void 0;
|
|
2260
|
+
const pathNote = workspacePath ? `
|
|
2261
|
+
Saved in workspace at: ${workspacePath}
|
|
2262
|
+
Use the workspace file/read tools with this path if you need to inspect it.` : "";
|
|
2258
2263
|
const content = await readFileAsText(file);
|
|
2259
2264
|
if (content !== null) {
|
|
2260
|
-
attachmentSummaries.push(`[attached: ${label} (${mime})]
|
|
2265
|
+
attachmentSummaries.push(`[attached: ${label} (${mime})${pathNote}]
|
|
2261
2266
|
\`\`\`
|
|
2262
2267
|
${content}
|
|
2263
2268
|
\`\`\``);
|
|
2264
2269
|
} else {
|
|
2265
|
-
attachmentSummaries.push(`[attached: ${label} (${mime}, not inlined \u2014 binary)]`);
|
|
2270
|
+
attachmentSummaries.push(`[attached: ${label} (${mime}, not inlined \u2014 binary)${pathNote}]`);
|
|
2266
2271
|
}
|
|
2267
2272
|
}
|
|
2268
2273
|
const mentionNote = mentionedFiles.length > 0 ? `@files: ${mentionedFiles.join(", ")}` : null;
|
|
@@ -2558,10 +2563,8 @@ function writePiComposerThinking(value, options = {}) {
|
|
|
2558
2563
|
function writePiComposerShowThoughts(value, options = {}) {
|
|
2559
2564
|
writeStorage(options, "show-thoughts", value ? "1" : "0");
|
|
2560
2565
|
}
|
|
2561
|
-
function modelOptionsForSelection(options,
|
|
2562
|
-
|
|
2563
|
-
const available = options.filter((model) => model.available);
|
|
2564
|
-
return available.some((model) => model.provider === selected.provider && model.id === selected.id) ? available : [{ ...selected, label: selected.id, available: true }, ...available];
|
|
2566
|
+
function modelOptionsForSelection(options, _selected) {
|
|
2567
|
+
return options.filter((model) => model.available);
|
|
2565
2568
|
}
|
|
2566
2569
|
function readStoredScopedModel(options) {
|
|
2567
2570
|
const userSelected = readStorage(options, "model:user-selected") === "1";
|
|
@@ -3248,7 +3251,7 @@ function selectQueuePreview(state) {
|
|
|
3248
3251
|
}
|
|
3249
3252
|
function selectRuntimeNotices(state) {
|
|
3250
3253
|
const notices = [...state.notices];
|
|
3251
|
-
if (state.connection.state === "reconnecting") {
|
|
3254
|
+
if (state.connection.state === "reconnecting" && hasVisibleReconnectWork(state)) {
|
|
3252
3255
|
notices.push({ id: "connection-reconnecting", level: "warning", text: "Reconnecting to the agent session\u2026" });
|
|
3253
3256
|
}
|
|
3254
3257
|
if (state.retryNotice) {
|
|
@@ -3263,6 +3266,9 @@ function selectRuntimeNotices(state) {
|
|
|
3263
3266
|
}
|
|
3264
3267
|
return notices;
|
|
3265
3268
|
}
|
|
3269
|
+
function hasVisibleReconnectWork(state) {
|
|
3270
|
+
return state.status !== "idle" || state.queue.followUps.length > 0 || Object.keys(state.optimisticOutbox).length > 0 || state.pendingToolCallIds.size > 0;
|
|
3271
|
+
}
|
|
3266
3272
|
function optimisticText(message) {
|
|
3267
3273
|
const text = message.parts.filter((part) => part.type === "text").map((part) => part.text).join("\n\n");
|
|
3268
3274
|
return text;
|
|
@@ -3348,48 +3354,6 @@ function messageCreatedAtMs(message) {
|
|
|
3348
3354
|
return Number.isFinite(timestamp) ? timestamp : void 0;
|
|
3349
3355
|
}
|
|
3350
3356
|
|
|
3351
|
-
// src/front/chat/session/activeSessionStorage.ts
|
|
3352
|
-
var ACTIVE_SESSION_KEY_PREFIX = "boring-agent:v2";
|
|
3353
|
-
var ACTIVE_SESSION_KEY_SUFFIX = "activeSessionId";
|
|
3354
|
-
var DEFAULT_STORAGE_SCOPE2 = "default";
|
|
3355
|
-
function activeSessionStorageKey(storageScope) {
|
|
3356
|
-
const scope = storageScope && storageScope.length > 0 ? storageScope : DEFAULT_STORAGE_SCOPE2;
|
|
3357
|
-
return `${ACTIVE_SESSION_KEY_PREFIX}:${scope}:${ACTIVE_SESSION_KEY_SUFFIX}`;
|
|
3358
|
-
}
|
|
3359
|
-
function readActiveSessionId(options = {}) {
|
|
3360
|
-
const storage = resolveStorage2(options.storage);
|
|
3361
|
-
if (!storage) return void 0;
|
|
3362
|
-
try {
|
|
3363
|
-
return storage.getItem(activeSessionStorageKey(options.storageScope)) ?? void 0;
|
|
3364
|
-
} catch {
|
|
3365
|
-
return void 0;
|
|
3366
|
-
}
|
|
3367
|
-
}
|
|
3368
|
-
function writeActiveSessionId(sessionId, options = {}) {
|
|
3369
|
-
const storage = resolveStorage2(options.storage);
|
|
3370
|
-
if (!storage) return;
|
|
3371
|
-
try {
|
|
3372
|
-
const key = activeSessionStorageKey(options.storageScope);
|
|
3373
|
-
if (sessionId === void 0 || sessionId.length === 0) storage.removeItem(key);
|
|
3374
|
-
else storage.setItem(key, sessionId);
|
|
3375
|
-
} catch {
|
|
3376
|
-
}
|
|
3377
|
-
}
|
|
3378
|
-
function clearActiveSessionId(options = {}) {
|
|
3379
|
-
writeActiveSessionId(void 0, options);
|
|
3380
|
-
}
|
|
3381
|
-
function resolveStorage2(storage) {
|
|
3382
|
-
if (storage) return storage;
|
|
3383
|
-
try {
|
|
3384
|
-
return globalThis.localStorage;
|
|
3385
|
-
} catch {
|
|
3386
|
-
return void 0;
|
|
3387
|
-
}
|
|
3388
|
-
}
|
|
3389
|
-
|
|
3390
|
-
// src/front/chat/session/usePiSessions.ts
|
|
3391
|
-
import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo3, useRef as useRef8, useState as useState10 } from "react";
|
|
3392
|
-
|
|
3393
3357
|
// src/front/chat/pi/piChatAssistantCommit.ts
|
|
3394
3358
|
function commitFinalMessage(state, messageId, final) {
|
|
3395
3359
|
const plan = buildAssistantCommitPlan(state, messageId, final);
|
|
@@ -3747,10 +3711,18 @@ function piChatReducer(state, action) {
|
|
|
3747
3711
|
queue: { followUps: clearQueuedFollowUps(state.queue.followUps, action) },
|
|
3748
3712
|
optimisticOutbox: clearOptimisticFollowUps(state.optimisticOutbox, action)
|
|
3749
3713
|
};
|
|
3750
|
-
case "connection-state":
|
|
3751
|
-
|
|
3714
|
+
case "connection-state": {
|
|
3715
|
+
const nextState = {
|
|
3716
|
+
...state,
|
|
3717
|
+
connection: { ...state.connection, state: action.state }
|
|
3718
|
+
};
|
|
3719
|
+
return action.state === "connected" ? clearProtocolError(nextState) : nextState;
|
|
3720
|
+
}
|
|
3752
3721
|
case "heartbeat":
|
|
3753
|
-
return {
|
|
3722
|
+
return clearProtocolError({
|
|
3723
|
+
...state,
|
|
3724
|
+
connection: { ...state.connection, lastHeartbeatAt: action.now ?? Date.now() }
|
|
3725
|
+
});
|
|
3754
3726
|
case "protocol-error":
|
|
3755
3727
|
return {
|
|
3756
3728
|
...state,
|
|
@@ -3767,6 +3739,14 @@ function piChatReducer(state, action) {
|
|
|
3767
3739
|
return { ...state, notices: state.notices.filter((notice) => notice.id !== action.id) };
|
|
3768
3740
|
}
|
|
3769
3741
|
}
|
|
3742
|
+
function clearProtocolError(state) {
|
|
3743
|
+
if (!state.notices.some((notice) => notice.id === "protocol-error")) return state;
|
|
3744
|
+
return {
|
|
3745
|
+
...state,
|
|
3746
|
+
error: void 0,
|
|
3747
|
+
notices: state.notices.filter((notice) => notice.id !== "protocol-error")
|
|
3748
|
+
};
|
|
3749
|
+
}
|
|
3770
3750
|
function syncCursor(state, cursor) {
|
|
3771
3751
|
if (cursor <= state.lastSeq) return { ...state, hydrated: true, needsResync: void 0 };
|
|
3772
3752
|
return { ...state, lastSeq: cursor, hydrated: true, needsResync: void 0 };
|
|
@@ -3774,7 +3754,9 @@ function syncCursor(state, cursor) {
|
|
|
3774
3754
|
function hydrateFromSnapshot(state, snapshot, options = {}) {
|
|
3775
3755
|
if (snapshot.seq < state.lastSeq && !options.allowSeqRewind) return state;
|
|
3776
3756
|
const queue = { followUps: enrichQueueWithKnownMetadata(snapshot.queue.followUps, state.optimisticOutbox, state.queue.followUps) };
|
|
3777
|
-
const
|
|
3757
|
+
const snapshotMessages = normalizeSnapshotMessages(snapshot.messages);
|
|
3758
|
+
const preserveLocalHistory = shouldPreserveLocalCommittedHistory(state, snapshot, snapshotMessages, options);
|
|
3759
|
+
const committedMessages = preserveLocalHistory ? mergeSnapshotMessagesIntoLocal(state.committedMessages, snapshotMessages) : snapshotMessages;
|
|
3778
3760
|
const serverNonces = /* @__PURE__ */ new Set();
|
|
3779
3761
|
for (const message of committedMessages) {
|
|
3780
3762
|
if (message.clientNonce) serverNonces.add(message.clientNonce);
|
|
@@ -3783,7 +3765,12 @@ function hydrateFromSnapshot(state, snapshot, options = {}) {
|
|
|
3783
3765
|
if (followUp.clientNonce) serverNonces.add(followUp.clientNonce);
|
|
3784
3766
|
}
|
|
3785
3767
|
const nextOutbox = {};
|
|
3786
|
-
|
|
3768
|
+
if (preserveLocalHistory) {
|
|
3769
|
+
for (const message of Object.values(state.optimisticOutbox)) {
|
|
3770
|
+
if (!serverNonces.has(message.clientNonce)) nextOutbox[message.clientNonce] = message;
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
const staleOutbox = preserveLocalHistory ? [] : Object.values(state.optimisticOutbox).filter((message) => !serverNonces.has(message.clientNonce));
|
|
3787
3774
|
const notices = staleOutbox.length > 0 ? upsertNotice(state.notices, {
|
|
3788
3775
|
id: "stale-outbox-cleared",
|
|
3789
3776
|
level: "warning",
|
|
@@ -3810,6 +3797,22 @@ function hydrateFromSnapshot(state, snapshot, options = {}) {
|
|
|
3810
3797
|
needsResync: void 0
|
|
3811
3798
|
};
|
|
3812
3799
|
}
|
|
3800
|
+
function shouldPreserveLocalCommittedHistory(state, snapshot, snapshotMessages, options) {
|
|
3801
|
+
if (options.allowSeqRewind) return false;
|
|
3802
|
+
if (!state.hydrated) return false;
|
|
3803
|
+
if (state.sessionId !== snapshot.sessionId) return false;
|
|
3804
|
+
if (state.committedMessages.length === 0) return false;
|
|
3805
|
+
if (snapshotMessages.length < state.committedMessages.length) return true;
|
|
3806
|
+
for (let index = 0; index < state.committedMessages.length; index += 1) {
|
|
3807
|
+
if (snapshotMessages[index]?.id !== state.committedMessages[index]?.id) return true;
|
|
3808
|
+
}
|
|
3809
|
+
return false;
|
|
3810
|
+
}
|
|
3811
|
+
function mergeSnapshotMessagesIntoLocal(currentMessages, snapshotMessages) {
|
|
3812
|
+
let merged = currentMessages;
|
|
3813
|
+
for (const message of snapshotMessages) merged = replaceOrAppendMessage(merged, message);
|
|
3814
|
+
return merged;
|
|
3815
|
+
}
|
|
3813
3816
|
function applySequencedEvent(state, event) {
|
|
3814
3817
|
if (event.seq <= state.lastSeq) return state;
|
|
3815
3818
|
const expectedSeq = state.lastSeq + 1;
|
|
@@ -4538,6 +4541,7 @@ var RemotePiSession = class {
|
|
|
4538
4541
|
this.store.dispatch({ type: "optimistic-user-message", message: toOptimisticUserMessage(payload) }, { flush: true });
|
|
4539
4542
|
}
|
|
4540
4543
|
if (!this.started) await this.start(this.store.getState().lastSeq);
|
|
4544
|
+
else this.ensureReconnectScheduled();
|
|
4541
4545
|
try {
|
|
4542
4546
|
const receipt = await this.postCommand("/prompt", payload, PromptReceiptSchema);
|
|
4543
4547
|
return receipt;
|
|
@@ -4551,6 +4555,7 @@ var RemotePiSession = class {
|
|
|
4551
4555
|
this.store.dispatch({ type: "optimistic-user-message", message: toOptimisticUserMessage(payload) }, { flush: true });
|
|
4552
4556
|
}
|
|
4553
4557
|
if (!this.started) await this.start(this.store.getState().lastSeq);
|
|
4558
|
+
else this.ensureReconnectScheduled();
|
|
4554
4559
|
try {
|
|
4555
4560
|
const receipt = await this.postCommand("/followup", payload, FollowUpReceiptSchema);
|
|
4556
4561
|
return receipt;
|
|
@@ -4719,7 +4724,9 @@ var RemotePiSession = class {
|
|
|
4719
4724
|
markOpen();
|
|
4720
4725
|
if (!this.isStreamActive(generation, runId)) return;
|
|
4721
4726
|
if (!connectTimedOut && shouldIgnoreStreamClose(error, controller)) return;
|
|
4722
|
-
|
|
4727
|
+
if (!connectTimedOut) {
|
|
4728
|
+
this.dispatchProtocolError(errorMessage(error, "Pi chat event stream disconnected."));
|
|
4729
|
+
}
|
|
4723
4730
|
this.scheduleReconnect(generation);
|
|
4724
4731
|
} finally {
|
|
4725
4732
|
clearConnectTimer();
|
|
@@ -4731,6 +4738,13 @@ var RemotePiSession = class {
|
|
|
4731
4738
|
this.abortEventStream();
|
|
4732
4739
|
void this.hydrateAndConnect(generation, options);
|
|
4733
4740
|
}
|
|
4741
|
+
ensureReconnectScheduled() {
|
|
4742
|
+
if (!this.isGenerationActive(this.generation)) return;
|
|
4743
|
+
const state = this.store.getState();
|
|
4744
|
+
if (state.connection.state === "connected" || state.connection.state === "connecting") return;
|
|
4745
|
+
if (this.reconnectTimer !== void 0) return;
|
|
4746
|
+
void this.hydrateAndConnect(this.generation);
|
|
4747
|
+
}
|
|
4734
4748
|
scheduleReconnect(generation) {
|
|
4735
4749
|
if (!this.isGenerationActive(generation)) return;
|
|
4736
4750
|
this.clearReconnectTimer();
|
|
@@ -4783,7 +4797,7 @@ var RemotePiSession = class {
|
|
|
4783
4797
|
try {
|
|
4784
4798
|
const response = await this.fetchImpl(url, { ...init, signal: controller.signal });
|
|
4785
4799
|
const body = await safeReadJson(response);
|
|
4786
|
-
if (!response.ok) throw new RemotePiSessionHttpError(response.status, routeErrorMessage(body, `HTTP ${response.status}`), body);
|
|
4800
|
+
if (!response.ok) throw new RemotePiSessionHttpError(response.status, routeErrorMessage(body, `HTTP ${response.status}`), body, routeErrorCode(body));
|
|
4787
4801
|
return body;
|
|
4788
4802
|
} catch (error) {
|
|
4789
4803
|
if (timedOut) throw new Error(`Request to ${url} timed out after ${this.requestTimeoutMs}ms.`);
|
|
@@ -4858,15 +4872,22 @@ function createRemotePiSession(options) {
|
|
|
4858
4872
|
return new RemotePiSession(options);
|
|
4859
4873
|
}
|
|
4860
4874
|
var RemotePiSessionHttpError = class extends Error {
|
|
4861
|
-
constructor(status, message, body) {
|
|
4875
|
+
constructor(status, message, body, errorCode) {
|
|
4862
4876
|
super(message);
|
|
4863
4877
|
this.status = status;
|
|
4864
4878
|
this.body = body;
|
|
4879
|
+
this.errorCode = errorCode;
|
|
4865
4880
|
this.name = "RemotePiSessionHttpError";
|
|
4866
4881
|
}
|
|
4867
4882
|
status;
|
|
4868
4883
|
body;
|
|
4884
|
+
errorCode;
|
|
4869
4885
|
};
|
|
4886
|
+
function piChatErrorCode(error) {
|
|
4887
|
+
if (error instanceof RemotePiSessionHttpError) return error.errorCode;
|
|
4888
|
+
const parsed = ErrorCode.safeParse(error?.errorCode);
|
|
4889
|
+
return parsed.success ? parsed.data : void 0;
|
|
4890
|
+
}
|
|
4870
4891
|
function toOptimisticUserMessage(payload) {
|
|
4871
4892
|
const displayText = payload.displayMessage ?? payload.message;
|
|
4872
4893
|
return {
|
|
@@ -4903,6 +4924,13 @@ function routeErrorMessage(body, fallback) {
|
|
|
4903
4924
|
const payload = typeof error === "object" && error !== null ? error : body;
|
|
4904
4925
|
return typeof payload.message === "string" && payload.message ? payload.message : fallback;
|
|
4905
4926
|
}
|
|
4927
|
+
function routeErrorCode(body) {
|
|
4928
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
4929
|
+
const error = body.error;
|
|
4930
|
+
const payload = typeof error === "object" && error !== null ? error : body;
|
|
4931
|
+
const parsed = ErrorCode.safeParse(payload.code);
|
|
4932
|
+
return parsed.success ? parsed.data : void 0;
|
|
4933
|
+
}
|
|
4906
4934
|
function errorMessage(error, fallback) {
|
|
4907
4935
|
return error instanceof Error && error.message ? error.message : fallback;
|
|
4908
4936
|
}
|
|
@@ -4957,7 +4985,47 @@ function clearPageUnloading() {
|
|
|
4957
4985
|
pageUnloading = false;
|
|
4958
4986
|
}
|
|
4959
4987
|
|
|
4988
|
+
// src/front/chat/session/activeSessionStorage.ts
|
|
4989
|
+
var ACTIVE_SESSION_KEY_PREFIX = "boring-agent:v2";
|
|
4990
|
+
var ACTIVE_SESSION_KEY_SUFFIX = "activeSessionId";
|
|
4991
|
+
var DEFAULT_STORAGE_SCOPE2 = "default";
|
|
4992
|
+
function activeSessionStorageKey(storageScope) {
|
|
4993
|
+
const scope = storageScope && storageScope.length > 0 ? storageScope : DEFAULT_STORAGE_SCOPE2;
|
|
4994
|
+
return `${ACTIVE_SESSION_KEY_PREFIX}:${scope}:${ACTIVE_SESSION_KEY_SUFFIX}`;
|
|
4995
|
+
}
|
|
4996
|
+
function readActiveSessionId(options = {}) {
|
|
4997
|
+
const storage = resolveStorage2(options.storage);
|
|
4998
|
+
if (!storage) return void 0;
|
|
4999
|
+
try {
|
|
5000
|
+
return storage.getItem(activeSessionStorageKey(options.storageScope)) ?? void 0;
|
|
5001
|
+
} catch {
|
|
5002
|
+
return void 0;
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
function writeActiveSessionId(sessionId, options = {}) {
|
|
5006
|
+
const storage = resolveStorage2(options.storage);
|
|
5007
|
+
if (!storage) return;
|
|
5008
|
+
try {
|
|
5009
|
+
const key = activeSessionStorageKey(options.storageScope);
|
|
5010
|
+
if (sessionId === void 0 || sessionId.length === 0) storage.removeItem(key);
|
|
5011
|
+
else storage.setItem(key, sessionId);
|
|
5012
|
+
} catch {
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
function clearActiveSessionId(options = {}) {
|
|
5016
|
+
writeActiveSessionId(void 0, options);
|
|
5017
|
+
}
|
|
5018
|
+
function resolveStorage2(storage) {
|
|
5019
|
+
if (storage) return storage;
|
|
5020
|
+
try {
|
|
5021
|
+
return globalThis.localStorage;
|
|
5022
|
+
} catch {
|
|
5023
|
+
return void 0;
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
|
|
4960
5027
|
// src/front/chat/session/usePiSessions.ts
|
|
5028
|
+
import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo3, useRef as useRef8, useState as useState10 } from "react";
|
|
4961
5029
|
var DEFAULT_SESSIONS_API_PATH = "/api/v1/agent/pi-chat/sessions";
|
|
4962
5030
|
var SESSION_PAGE_SIZE = 50;
|
|
4963
5031
|
var DEFAULT_MAX_RETRIES = 60;
|
|
@@ -5637,7 +5705,7 @@ import { memo as memo2 } from "react";
|
|
|
5637
5705
|
import { AlertTriangleIcon, InfoIcon, Loader2Icon, PlugZapIcon, RefreshCwIcon, XIcon as XIcon2 } from "lucide-react";
|
|
5638
5706
|
import { Button as Button8 } from "@hachej/boring-ui-kit";
|
|
5639
5707
|
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5640
|
-
var RuntimeNotices = memo2(({ notices, onDismiss, onAction, className, ...props }) => {
|
|
5708
|
+
var RuntimeNotices = memo2(({ notices, onDismiss, onAction, renderAction, className, ...props }) => {
|
|
5641
5709
|
if (notices.length === 0) return null;
|
|
5642
5710
|
return /* @__PURE__ */ jsx15(
|
|
5643
5711
|
"div",
|
|
@@ -5645,15 +5713,16 @@ var RuntimeNotices = memo2(({ notices, onDismiss, onAction, className, ...props
|
|
|
5645
5713
|
"data-boring-agent-part": "runtime-notices",
|
|
5646
5714
|
className: cn("flex flex-col gap-2 px-4 py-2", className),
|
|
5647
5715
|
...props,
|
|
5648
|
-
children: notices.map((notice) => /* @__PURE__ */ jsx15(RuntimeNoticeRow, { notice, onDismiss, onAction }, notice.id))
|
|
5716
|
+
children: notices.map((notice) => /* @__PURE__ */ jsx15(RuntimeNoticeRow, { notice, onDismiss, onAction, renderAction }, notice.id))
|
|
5649
5717
|
}
|
|
5650
5718
|
);
|
|
5651
5719
|
});
|
|
5652
5720
|
RuntimeNotices.displayName = "RuntimeNotices";
|
|
5653
|
-
function RuntimeNoticeRow({ notice, onDismiss, onAction }) {
|
|
5721
|
+
function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
|
|
5654
5722
|
const kind = inferNoticeKind(notice);
|
|
5655
5723
|
const Icon = iconForNotice(kind, notice.level);
|
|
5656
5724
|
const actionLabel = notice.actionLabel ?? defaultActionLabel(kind);
|
|
5725
|
+
const hostAction = renderAction?.(notice);
|
|
5657
5726
|
return /* @__PURE__ */ jsxs14(
|
|
5658
5727
|
"div",
|
|
5659
5728
|
{
|
|
@@ -5670,6 +5739,7 @@ function RuntimeNoticeRow({ notice, onDismiss, onAction }) {
|
|
|
5670
5739
|
children: [
|
|
5671
5740
|
/* @__PURE__ */ jsx15(Icon, { className: cn(noticeIconClass(notice.level), kind === "retry" && "animate-spin motion-reduce:animate-none"), "aria-hidden": "true" }),
|
|
5672
5741
|
/* @__PURE__ */ jsx15("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx15("p", { className: noticeTextClass(), children: notice.text }) }),
|
|
5742
|
+
hostAction ?? null,
|
|
5673
5743
|
actionLabel && onAction ? /* @__PURE__ */ jsx15(Button8, { type: "button", variant: "ghost", size: "sm", onClick: () => onAction(notice.id), children: actionLabel }) : null,
|
|
5674
5744
|
notice.dismissible && onDismiss ? /* @__PURE__ */ jsx15(
|
|
5675
5745
|
Button8,
|
|
@@ -5710,7 +5780,11 @@ function defaultActionLabel(kind) {
|
|
|
5710
5780
|
|
|
5711
5781
|
// src/front/chat/components/ChatNotices.tsx
|
|
5712
5782
|
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
5713
|
-
function RuntimeNoticeMessages({
|
|
5783
|
+
function RuntimeNoticeMessages({
|
|
5784
|
+
notices,
|
|
5785
|
+
onDismiss,
|
|
5786
|
+
renderAction
|
|
5787
|
+
}) {
|
|
5714
5788
|
const visible = notices.filter(
|
|
5715
5789
|
(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:")
|
|
5716
5790
|
);
|
|
@@ -5720,6 +5794,7 @@ function RuntimeNoticeMessages({ notices, onDismiss }) {
|
|
|
5720
5794
|
{
|
|
5721
5795
|
notices: visible,
|
|
5722
5796
|
onDismiss,
|
|
5797
|
+
renderAction,
|
|
5723
5798
|
className: "mx-auto w-full max-w-3xl px-0 py-0"
|
|
5724
5799
|
}
|
|
5725
5800
|
);
|
|
@@ -6976,6 +7051,7 @@ function PiConversationSurface({
|
|
|
6976
7051
|
toolRenderers,
|
|
6977
7052
|
runtimeNotices,
|
|
6978
7053
|
onDismissNotice,
|
|
7054
|
+
renderNoticeAction,
|
|
6979
7055
|
onScrollToBottomReady,
|
|
6980
7056
|
onSuggestionSubmit,
|
|
6981
7057
|
onRestoreDraft,
|
|
@@ -7016,6 +7092,7 @@ function PiConversationSurface({
|
|
|
7016
7092
|
eyebrow: emptyState?.eyebrow,
|
|
7017
7093
|
title: emptyState?.title,
|
|
7018
7094
|
description: emptyState?.description,
|
|
7095
|
+
footer: emptyState?.footer,
|
|
7019
7096
|
suggestions,
|
|
7020
7097
|
className: emptyHero ? "items-center text-center [&>p]:mx-auto" : void 0,
|
|
7021
7098
|
onSelect: (suggestion) => {
|
|
@@ -7039,7 +7116,7 @@ function PiConversationSurface({
|
|
|
7039
7116
|
},
|
|
7040
7117
|
key
|
|
7041
7118
|
)),
|
|
7042
|
-
/* @__PURE__ */ jsx23(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice })
|
|
7119
|
+
/* @__PURE__ */ jsx23(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
|
|
7043
7120
|
] }),
|
|
7044
7121
|
/* @__PURE__ */ jsx23(ConversationScrollButton, {})
|
|
7045
7122
|
]
|
|
@@ -7257,7 +7334,7 @@ function PluginUpdateStatus({
|
|
|
7257
7334
|
tone: "running",
|
|
7258
7335
|
dataAttribute: "data-boring-plugin-update",
|
|
7259
7336
|
maxWidthClassName,
|
|
7260
|
-
runningContent: "Updating
|
|
7337
|
+
runningContent: "Updating extensions\u2026"
|
|
7261
7338
|
}
|
|
7262
7339
|
);
|
|
7263
7340
|
}
|
|
@@ -7267,7 +7344,7 @@ function PluginUpdateStatus({
|
|
|
7267
7344
|
const frontEvents = state.frontEvents ?? [];
|
|
7268
7345
|
const hasWarningsOrDiagnostics = warnings.length > 0 || diagnostics.length > 0;
|
|
7269
7346
|
const title = state.reloaded ? hasWarningsOrDiagnostics ? "Reload finished with warnings" : "Reload complete" : "Reload queued";
|
|
7270
|
-
const detail = state.reloaded ? hasWarningsOrDiagnostics ? "Some
|
|
7347
|
+
const detail = state.reloaded ? hasWarningsOrDiagnostics ? "Some extension changes need attention." : frontEvents.length > 0 ? `${frontEvents.length} extension module${frontEvents.length === 1 ? "" : "s"} refreshed. Changes are live.` : "Changes are live." : void 0;
|
|
7271
7348
|
return /* @__PURE__ */ jsxs23(
|
|
7272
7349
|
ComposerStatusBanner,
|
|
7273
7350
|
{
|
|
@@ -7293,7 +7370,7 @@ function PluginUpdateStatus({
|
|
|
7293
7370
|
/* @__PURE__ */ jsxs23("span", { children: [
|
|
7294
7371
|
"Reload diagnostics for ",
|
|
7295
7372
|
diagnostics.length,
|
|
7296
|
-
"
|
|
7373
|
+
" extension",
|
|
7297
7374
|
diagnostics.length === 1 ? "" : "s"
|
|
7298
7375
|
] })
|
|
7299
7376
|
] }),
|
|
@@ -7319,7 +7396,7 @@ function PluginUpdateStatus({
|
|
|
7319
7396
|
/* @__PURE__ */ jsxs23("span", { children: [
|
|
7320
7397
|
"Restart needed for ",
|
|
7321
7398
|
warnings.length,
|
|
7322
|
-
"
|
|
7399
|
+
" extension",
|
|
7323
7400
|
warnings.length === 1 ? "" : "s"
|
|
7324
7401
|
] })
|
|
7325
7402
|
] }),
|
|
@@ -7328,7 +7405,7 @@ function PluginUpdateStatus({
|
|
|
7328
7405
|
" \u2014 ",
|
|
7329
7406
|
w.surfaces.join(" + ")
|
|
7330
7407
|
] }, w.id)) }),
|
|
7331
|
-
/* @__PURE__ */ jsx25("p", { className: "mt-1 text-foreground/70", children: "The
|
|
7408
|
+
/* @__PURE__ */ jsx25("p", { className: "mt-1 text-foreground/70", children: "The interface reloaded successfully, but routes and tools were wired at boot. Restart the workspace process to pick up the new code." })
|
|
7332
7409
|
]
|
|
7333
7410
|
}
|
|
7334
7411
|
) : null
|
|
@@ -7342,7 +7419,7 @@ function PluginUpdateStatus({
|
|
|
7342
7419
|
tone: "error",
|
|
7343
7420
|
dataAttribute: "data-boring-plugin-update",
|
|
7344
7421
|
maxWidthClassName,
|
|
7345
|
-
title: "
|
|
7422
|
+
title: "Extension update failed.",
|
|
7346
7423
|
message: state.message,
|
|
7347
7424
|
onRetry,
|
|
7348
7425
|
onDismiss,
|
|
@@ -7563,22 +7640,10 @@ function ThinkingLevelGlyph({ level }) {
|
|
|
7563
7640
|
function modelTriggerLabel(value, options) {
|
|
7564
7641
|
const current = value ? options.find((m) => m.provider === value.provider && m.id === value.id) : void 0;
|
|
7565
7642
|
const rawTriggerLabel = current?.label ?? value?.id;
|
|
7566
|
-
return value ? rawTriggerLabel && current?.label && current.label !== value.id && /[A-Z]/.test(current.label) ? current.label : displayModelLabel(rawTriggerLabel ?? value.id) : "
|
|
7643
|
+
return value ? rawTriggerLabel && current?.label && current.label !== value.id && /[A-Z]/.test(current.label) ? current.label : displayModelLabel(rawTriggerLabel ?? value.id) : "Default model";
|
|
7567
7644
|
}
|
|
7568
|
-
function modelMenuOptions(
|
|
7569
|
-
|
|
7570
|
-
const triggerLabel = modelTriggerLabel(value, options);
|
|
7571
|
-
const availableOptions = options.filter((m) => m.available);
|
|
7572
|
-
const hasCurrentOption = currentKey ? availableOptions.some((m) => encodeModelKey(m) === currentKey) : true;
|
|
7573
|
-
return hasCurrentOption || !value ? availableOptions : [
|
|
7574
|
-
{
|
|
7575
|
-
provider: value.provider,
|
|
7576
|
-
id: value.id,
|
|
7577
|
-
label: triggerLabel,
|
|
7578
|
-
available: true
|
|
7579
|
-
},
|
|
7580
|
-
...availableOptions
|
|
7581
|
-
];
|
|
7645
|
+
function modelMenuOptions(_value, options) {
|
|
7646
|
+
return options.filter((m) => m.available);
|
|
7582
7647
|
}
|
|
7583
7648
|
function groupModelOptions(options) {
|
|
7584
7649
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -7634,6 +7699,7 @@ function ModelPickerMenu({
|
|
|
7634
7699
|
onChange,
|
|
7635
7700
|
options,
|
|
7636
7701
|
disabled,
|
|
7702
|
+
hideDefaultOption = false,
|
|
7637
7703
|
onClose,
|
|
7638
7704
|
className
|
|
7639
7705
|
}) {
|
|
@@ -7641,7 +7707,7 @@ function ModelPickerMenu({
|
|
|
7641
7707
|
const menuOptions = modelMenuOptions(value, options);
|
|
7642
7708
|
const groups = groupModelOptions(menuOptions);
|
|
7643
7709
|
const groupedOptions = [...groups.values()].flat();
|
|
7644
|
-
const keyboardOptions = [null, ...groupedOptions];
|
|
7710
|
+
const keyboardOptions = hideDefaultOption ? groupedOptions : [null, ...groupedOptions];
|
|
7645
7711
|
const selectedIndex = currentKey ? Math.max(0, keyboardOptions.findIndex((option) => option && encodeModelKey(option) === currentKey)) : 0;
|
|
7646
7712
|
const [activeIndex, setActiveIndex] = useState16(selectedIndex);
|
|
7647
7713
|
const activeIndexRef = useRef13(selectedIndex);
|
|
@@ -7678,7 +7744,8 @@ function ModelPickerMenu({
|
|
|
7678
7744
|
window.addEventListener("keydown", handler, { capture: true });
|
|
7679
7745
|
return () => window.removeEventListener("keydown", handler, { capture: true });
|
|
7680
7746
|
}, [activeIndex, disabled, keyboardOptions, onChange, onClose]);
|
|
7681
|
-
const
|
|
7747
|
+
const optionIndexOffset = hideDefaultOption ? 0 : 1;
|
|
7748
|
+
const optionIndexes = new Map(groupedOptions.map((option, index) => [encodeModelKey(option), index + optionIndexOffset]));
|
|
7682
7749
|
return /* @__PURE__ */ jsx27("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "model-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsxs25(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: [
|
|
7683
7750
|
menuOptions.length > 8 && /* @__PURE__ */ jsx27("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx27(
|
|
7684
7751
|
CommandInput,
|
|
@@ -7690,10 +7757,10 @@ function ModelPickerMenu({
|
|
|
7690
7757
|
) }),
|
|
7691
7758
|
/* @__PURE__ */ jsxs25(CommandList, { className: "max-h-[300px] p-0.5", children: [
|
|
7692
7759
|
/* @__PURE__ */ jsx27(CommandEmpty, { className: "py-4 text-center text-[13px] text-muted-foreground", children: "No models found" }),
|
|
7693
|
-
/* @__PURE__ */ jsx27(CommandGroup, { children: /* @__PURE__ */ jsxs25(
|
|
7760
|
+
!hideDefaultOption ? /* @__PURE__ */ jsx27(CommandGroup, { children: /* @__PURE__ */ jsxs25(
|
|
7694
7761
|
CommandItem,
|
|
7695
7762
|
{
|
|
7696
|
-
value: "
|
|
7763
|
+
value: "Default model automatic auto",
|
|
7697
7764
|
onSelect: () => {
|
|
7698
7765
|
if (disabled) return;
|
|
7699
7766
|
onChange(null);
|
|
@@ -7710,11 +7777,11 @@ function ModelPickerMenu({
|
|
|
7710
7777
|
)
|
|
7711
7778
|
}
|
|
7712
7779
|
),
|
|
7713
|
-
/* @__PURE__ */ jsx27("span", { className: "truncate", children: "
|
|
7780
|
+
/* @__PURE__ */ jsx27("span", { className: "truncate", children: "Default model" }),
|
|
7714
7781
|
/* @__PURE__ */ jsx27("span", { className: "ml-auto shrink-0 text-[10px] text-muted-foreground/60", children: "auto" })
|
|
7715
7782
|
]
|
|
7716
7783
|
}
|
|
7717
|
-
) }),
|
|
7784
|
+
) }) : null,
|
|
7718
7785
|
[...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx27(
|
|
7719
7786
|
CommandGroup,
|
|
7720
7787
|
{
|
|
@@ -7979,11 +8046,11 @@ var PromptInput = ({
|
|
|
7979
8046
|
const inputRef = useRef14(null);
|
|
7980
8047
|
const formRef = useRef14(null);
|
|
7981
8048
|
const [items, setItems] = useState17([]);
|
|
7982
|
-
const setFileUrlLocal = useCallback15((id, url, status) => {
|
|
8049
|
+
const setFileUrlLocal = useCallback15((id, url, status, path) => {
|
|
7983
8050
|
setItems((prev) => prev.map((f) => {
|
|
7984
8051
|
if (f.id !== id) return f;
|
|
7985
8052
|
if (f.url !== url && f.url.startsWith("blob:")) URL.revokeObjectURL(f.url);
|
|
7986
|
-
return { ...f, url, status };
|
|
8053
|
+
return { ...f, url, status, ...path ? { path } : {} };
|
|
7987
8054
|
}));
|
|
7988
8055
|
}, []);
|
|
7989
8056
|
const files = usingProvider ? controller.attachments.files : items;
|
|
@@ -8052,7 +8119,7 @@ var PromptInput = ({
|
|
|
8052
8119
|
for (let i = 0; i < entries.length; i++) {
|
|
8053
8120
|
const entry = entries[i];
|
|
8054
8121
|
const file = capped[i];
|
|
8055
|
-
onUploadFile(file).then(({ url }) => setFileUrlLocal(entry.id, url, "ready")).catch(() => setFileUrlLocal(entry.id, entry.url, "error"));
|
|
8122
|
+
onUploadFile(file).then(({ url, path }) => setFileUrlLocal(entry.id, url, "ready", path)).catch(() => setFileUrlLocal(entry.id, entry.url, "error"));
|
|
8056
8123
|
}
|
|
8057
8124
|
}
|
|
8058
8125
|
},
|
|
@@ -8716,6 +8783,8 @@ function PiChatComposerSurface({
|
|
|
8716
8783
|
selectedModel,
|
|
8717
8784
|
modelOptions,
|
|
8718
8785
|
modelControlled,
|
|
8786
|
+
hideDefaultModelOption = false,
|
|
8787
|
+
hideComposerSettings = false,
|
|
8719
8788
|
onModelChange,
|
|
8720
8789
|
onSetModelPickerOpen,
|
|
8721
8790
|
onOpenModelPicker,
|
|
@@ -8733,6 +8802,11 @@ function PiChatComposerSurface({
|
|
|
8733
8802
|
onSubmitMessage,
|
|
8734
8803
|
onStop
|
|
8735
8804
|
}) {
|
|
8805
|
+
const uploadAttachment = useCallback16((file) => uploadFile(file, {
|
|
8806
|
+
apiBaseUrl,
|
|
8807
|
+
workspaceRequestId: requestHeaders?.["x-boring-workspace-id"],
|
|
8808
|
+
fetch: fetch2
|
|
8809
|
+
}), [apiBaseUrl, fetch2, requestHeaders]);
|
|
8736
8810
|
const resizeTextarea = useCallback16((node) => {
|
|
8737
8811
|
if (!node) return;
|
|
8738
8812
|
node.style.height = "auto";
|
|
@@ -8854,6 +8928,7 @@ function PiChatComposerSurface({
|
|
|
8854
8928
|
onChange: onModelChange,
|
|
8855
8929
|
options: modelOptions,
|
|
8856
8930
|
disabled: isStreaming || modelControlled,
|
|
8931
|
+
hideDefaultOption: hideDefaultModelOption,
|
|
8857
8932
|
onClose: () => onSetModelPickerOpen(false)
|
|
8858
8933
|
}
|
|
8859
8934
|
) : null,
|
|
@@ -8891,6 +8966,7 @@ function PiChatComposerSurface({
|
|
|
8891
8966
|
{
|
|
8892
8967
|
"data-boring-state": status,
|
|
8893
8968
|
onSubmit: (message) => onSubmitMessage({ text: message.text, files: message.files }),
|
|
8969
|
+
onUploadFile: uploadAttachment,
|
|
8894
8970
|
multiple: true,
|
|
8895
8971
|
maxFiles: disabled || isStreaming ? 0 : MAX_PROMPT_ATTACHMENTS,
|
|
8896
8972
|
maxFileSize: MAX_PROMPT_ATTACHMENT_BYTES,
|
|
@@ -8969,7 +9045,7 @@ function PiChatComposerSurface({
|
|
|
8969
9045
|
)
|
|
8970
9046
|
}
|
|
8971
9047
|
),
|
|
8972
|
-
/* @__PURE__ */ jsxs30(
|
|
9048
|
+
hideComposerSettings ? null : /* @__PURE__ */ jsxs30(
|
|
8973
9049
|
"div",
|
|
8974
9050
|
{
|
|
8975
9051
|
"data-boring-agent-part": "composer-settings-row",
|
|
@@ -9171,10 +9247,11 @@ function errorMessage2(error, fallback) {
|
|
|
9171
9247
|
|
|
9172
9248
|
// src/front/chat/PiChatPanel.tsx
|
|
9173
9249
|
import { jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
9174
|
-
var DebugDrawer2 = lazy(() => import("../DebugDrawer-
|
|
9250
|
+
var DebugDrawer2 = lazy(() => import("../DebugDrawer-RYEBQ6CO.js").then((m) => ({ default: m.DebugDrawer })));
|
|
9175
9251
|
var EMPTY_COMMANDS = [];
|
|
9176
9252
|
var EMPTY_COMMAND_NAMES = [];
|
|
9177
9253
|
var EMPTY_BLOCKERS = [];
|
|
9254
|
+
var RUN_REJECTED_NOTICE_ID = "run-rejected";
|
|
9178
9255
|
function PiChatPanel({
|
|
9179
9256
|
sessionId,
|
|
9180
9257
|
extraCommands,
|
|
@@ -9201,6 +9278,9 @@ function PiChatPanel({
|
|
|
9201
9278
|
model,
|
|
9202
9279
|
defaultModel,
|
|
9203
9280
|
availableModels,
|
|
9281
|
+
hideDefaultModelOption = false,
|
|
9282
|
+
hideComposerSettings = false,
|
|
9283
|
+
suppressPreSubmitCancelledWarning = false,
|
|
9204
9284
|
thinkingLevel,
|
|
9205
9285
|
thinkingControl = true,
|
|
9206
9286
|
serverResourcesEnabled = true,
|
|
@@ -9222,12 +9302,16 @@ function PiChatPanel({
|
|
|
9222
9302
|
onOpenArtifact,
|
|
9223
9303
|
composerBlockers = EMPTY_BLOCKERS,
|
|
9224
9304
|
onComposerStop,
|
|
9225
|
-
onComposerBlockerAction
|
|
9305
|
+
onComposerBlockerAction,
|
|
9306
|
+
onTurnComplete,
|
|
9307
|
+
renderNoticeAction
|
|
9226
9308
|
}) {
|
|
9227
9309
|
const externalSessionId = sessionId?.trim() || void 0;
|
|
9228
9310
|
const showSessionSidebar = showSessions ?? externalSessionId === void 0;
|
|
9229
9311
|
const onDataRef = useRef16(onData);
|
|
9230
9312
|
onDataRef.current = onData;
|
|
9313
|
+
const onTurnCompleteRef = useRef16(onTurnComplete);
|
|
9314
|
+
onTurnCompleteRef.current = onTurnComplete;
|
|
9231
9315
|
const sessionListRefreshRef = useRef16(void 0);
|
|
9232
9316
|
const requestHeadersKey = useMemo12(() => headersContentKey(requestHeaders), [requestHeaders]);
|
|
9233
9317
|
const normalizedRequestHeaders = useMemo12(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
|
|
@@ -9237,6 +9321,7 @@ function PiChatPanel({
|
|
|
9237
9321
|
onEvent: (event) => {
|
|
9238
9322
|
remoteSessionOptions?.onEvent?.(event);
|
|
9239
9323
|
onDataRef.current?.(event);
|
|
9324
|
+
if (event.type === "agent-end" && !event.willRetry) onTurnCompleteRef.current?.();
|
|
9240
9325
|
if (shouldRefreshSessionListAfterEvent(event)) sessionListRefreshRef.current?.();
|
|
9241
9326
|
}
|
|
9242
9327
|
}), [hydrateMessages, remoteSessionOptions]);
|
|
@@ -9411,7 +9496,7 @@ function PiChatPanel({
|
|
|
9411
9496
|
const largeStateNotice = debug && debugState?.largeStateWarning ? [{
|
|
9412
9497
|
id: "large-state-warning",
|
|
9413
9498
|
level: "warning",
|
|
9414
|
-
text: `Large
|
|
9499
|
+
text: `Large chat state: ${debugState.largeStateWarning.messageCount} messages, approximately ${debugState.largeStateWarning.approxBytes} bytes.`,
|
|
9415
9500
|
dismissible: true
|
|
9416
9501
|
}] : [];
|
|
9417
9502
|
return [...fromState, ...sessionNotice, ...largeStateNotice, ...localNotices].filter((notice) => !dismissedNoticeIds.has(notice.id));
|
|
@@ -9426,10 +9511,19 @@ function PiChatPanel({
|
|
|
9426
9511
|
setDismissedNoticeIds((previous) => new Set(previous).add(id));
|
|
9427
9512
|
setLocalNotices((previous) => previous.filter((notice) => notice.id !== id));
|
|
9428
9513
|
}, []);
|
|
9514
|
+
const dropLocalNotice = useCallback18((id) => {
|
|
9515
|
+
setDismissedNoticeIds((previous) => {
|
|
9516
|
+
if (!previous.has(id)) return previous;
|
|
9517
|
+
const next = new Set(previous);
|
|
9518
|
+
next.delete(id);
|
|
9519
|
+
return next;
|
|
9520
|
+
});
|
|
9521
|
+
setLocalNotices((previous) => previous.filter((notice) => notice.id !== id));
|
|
9522
|
+
}, []);
|
|
9429
9523
|
const createSession = useCallback18(() => {
|
|
9430
9524
|
if (externalSessionId) return;
|
|
9431
9525
|
void sessions.create().catch((error) => {
|
|
9432
|
-
addLocalNotice({ id: "session-create-error", level: "error", text: errorMessage2(error, "Could not create a
|
|
9526
|
+
addLocalNotice({ id: "session-create-error", level: "error", text: errorMessage2(error, "Could not create a chat session."), dismissible: true });
|
|
9433
9527
|
});
|
|
9434
9528
|
}, [addLocalNotice, externalSessionId, sessions.create]);
|
|
9435
9529
|
useEffect21(() => {
|
|
@@ -9439,7 +9533,7 @@ function PiChatPanel({
|
|
|
9439
9533
|
autoCreateInFlightRef.current = true;
|
|
9440
9534
|
void sessions.create().catch((error) => {
|
|
9441
9535
|
autoCreateInFlightRef.current = false;
|
|
9442
|
-
addLocalNotice({ id: "session-auto-create-error", level: "error", text: errorMessage2(error, "Could not create a
|
|
9536
|
+
addLocalNotice({ id: "session-auto-create-error", level: "error", text: errorMessage2(error, "Could not create a chat session."), dismissible: true });
|
|
9443
9537
|
});
|
|
9444
9538
|
}, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessionsError, sessionsLoading]);
|
|
9445
9539
|
useEffect21(() => {
|
|
@@ -9450,7 +9544,7 @@ function PiChatPanel({
|
|
|
9450
9544
|
const deleteSession = useCallback18((sessionId2) => {
|
|
9451
9545
|
if (externalSessionId) return;
|
|
9452
9546
|
void sessions.delete(sessionId2).catch((error) => {
|
|
9453
|
-
addLocalNotice({ id: `session-delete-error:${sessionId2}`, level: "error", text: errorMessage2(error, "Could not delete the
|
|
9547
|
+
addLocalNotice({ id: `session-delete-error:${sessionId2}`, level: "error", text: errorMessage2(error, "Could not delete the chat session."), dismissible: true });
|
|
9454
9548
|
});
|
|
9455
9549
|
}, [addLocalNotice, externalSessionId, sessions.delete]);
|
|
9456
9550
|
const resetSession = useCallback18(() => {
|
|
@@ -9466,7 +9560,7 @@ function PiChatPanel({
|
|
|
9466
9560
|
await sessions.create();
|
|
9467
9561
|
await onSessionReset?.();
|
|
9468
9562
|
})().catch((error) => {
|
|
9469
|
-
addLocalNotice({ id: "session-reset-error", level: "error", text: errorMessage2(error, "Could not reset the
|
|
9563
|
+
addLocalNotice({ id: "session-reset-error", level: "error", text: errorMessage2(error, "Could not reset the chat session."), dismissible: true });
|
|
9470
9564
|
}).finally(() => {
|
|
9471
9565
|
resetInProgressRef.current = false;
|
|
9472
9566
|
});
|
|
@@ -9482,15 +9576,15 @@ function PiChatPanel({
|
|
|
9482
9576
|
const failureMessage = pluginReloadFailureMessage(message);
|
|
9483
9577
|
if (failureMessage) {
|
|
9484
9578
|
setPluginUpdateState({ kind: "error", message: failureMessage });
|
|
9485
|
-
return `
|
|
9579
|
+
return `Extension update failed: ${failureMessage}`;
|
|
9486
9580
|
}
|
|
9487
9581
|
setPluginUpdateState({ kind: "success", reloaded: !/will reload on the next message/i.test(message) });
|
|
9488
9582
|
setServerSkillsRefreshKey((key) => key + 1);
|
|
9489
9583
|
return message;
|
|
9490
9584
|
} catch (error) {
|
|
9491
|
-
const message = errorMessage2(error, "
|
|
9585
|
+
const message = errorMessage2(error, "Extension reload failed.");
|
|
9492
9586
|
setPluginUpdateState({ kind: "error", message });
|
|
9493
|
-
return `
|
|
9587
|
+
return `Extension update failed: ${message}`;
|
|
9494
9588
|
}
|
|
9495
9589
|
}, [reloadAgentPlugins]);
|
|
9496
9590
|
const dismissPluginUpdate = useCallback18(() => setPluginUpdateState(null), []);
|
|
@@ -9639,7 +9733,7 @@ function PiChatPanel({
|
|
|
9639
9733
|
clearMessages: () => addLocalNotice({
|
|
9640
9734
|
id: "clear-not-supported",
|
|
9641
9735
|
level: "info",
|
|
9642
|
-
text: "/clear is not available in
|
|
9736
|
+
text: "/clear is not available in this chat panel.",
|
|
9643
9737
|
dismissible: true
|
|
9644
9738
|
}),
|
|
9645
9739
|
resetSession,
|
|
@@ -9671,6 +9765,7 @@ function PiChatPanel({
|
|
|
9671
9765
|
addLocalNotice({ id: `command:${Date.now()}`, level: "info", text: message, dismissible: true });
|
|
9672
9766
|
},
|
|
9673
9767
|
onWarning: (message) => {
|
|
9768
|
+
if (suppressPreSubmitCancelledWarning && message === "Submit was cancelled before sending.") return;
|
|
9674
9769
|
onComposerWarning?.(message);
|
|
9675
9770
|
addLocalNotice({ id: `composer-warning:${Date.now()}`, level: "warning", text: message, dismissible: true });
|
|
9676
9771
|
},
|
|
@@ -9679,10 +9774,21 @@ function PiChatPanel({
|
|
|
9679
9774
|
onMentionedFilesConsumed?.();
|
|
9680
9775
|
}
|
|
9681
9776
|
});
|
|
9682
|
-
}, [activeChatSessionId, addLocalNotice, clearMentionedFiles, composerBlocked, composerBlockerLabel, effectiveMentionedFiles, markLocalSubmitted, onBeforeSubmit, onCommandResult, onComposerWarning, onMentionedFilesConsumed, openModelPicker, openThinkingPicker, registry, reloadAgentPlugins, resetSession, runPluginUpdate, selectComposerModel, selectComposerThinking, selectedModel, selectedPiSession, selectedThinking, setComposerDraft, submitThinkingControl]);
|
|
9777
|
+
}, [activeChatSessionId, addLocalNotice, clearMentionedFiles, composerBlocked, composerBlockerLabel, effectiveMentionedFiles, markLocalSubmitted, onBeforeSubmit, onCommandResult, onComposerWarning, onMentionedFilesConsumed, openModelPicker, openThinkingPicker, registry, reloadAgentPlugins, resetSession, runPluginUpdate, selectComposerModel, selectComposerThinking, selectedModel, selectedPiSession, selectedThinking, setComposerDraft, submitThinkingControl, suppressPreSubmitCancelledWarning]);
|
|
9778
|
+
const surfaceRunRejected = useCallback18((error) => {
|
|
9779
|
+
const errorCode = piChatErrorCode(error);
|
|
9780
|
+
dropLocalNotice(RUN_REJECTED_NOTICE_ID);
|
|
9781
|
+
addLocalNotice({
|
|
9782
|
+
id: RUN_REJECTED_NOTICE_ID,
|
|
9783
|
+
level: "error",
|
|
9784
|
+
text: errorMessage2(error, "Could not send your message."),
|
|
9785
|
+
dismissible: true,
|
|
9786
|
+
...errorCode ? { errorCode } : {}
|
|
9787
|
+
});
|
|
9788
|
+
}, [addLocalNotice, dropLocalNotice]);
|
|
9683
9789
|
const sendComposerMessage = useCallback18(async ({ text, files, source = "composer" }) => {
|
|
9684
9790
|
if (!policy) {
|
|
9685
|
-
addLocalNotice({ id: "composer-no-session", level: "warning", text: "Create or select a
|
|
9791
|
+
addLocalNotice({ id: "composer-no-session", level: "warning", text: "Create or select a chat session before sending.", dismissible: true });
|
|
9686
9792
|
return false;
|
|
9687
9793
|
}
|
|
9688
9794
|
const submittedDraft = text;
|
|
@@ -9697,6 +9803,9 @@ function PiChatPanel({
|
|
|
9697
9803
|
restoreSubmittedDraft();
|
|
9698
9804
|
return false;
|
|
9699
9805
|
}
|
|
9806
|
+
if (result.type === "prompt" || result.type === "followup") {
|
|
9807
|
+
dropLocalNotice(RUN_REJECTED_NOTICE_ID);
|
|
9808
|
+
}
|
|
9700
9809
|
if (result.type === "prompt" && activeChatSessionId) {
|
|
9701
9810
|
if (shouldHoldLocalSubmitted(selectedPiSession, result.cursor)) markLocalSubmitted(activeChatSessionId);
|
|
9702
9811
|
else clearLocalSubmitted(activeChatSessionId);
|
|
@@ -9705,9 +9814,10 @@ function PiChatPanel({
|
|
|
9705
9814
|
} catch (error) {
|
|
9706
9815
|
clearLocalSubmitted(activeChatSessionId);
|
|
9707
9816
|
restoreSubmittedDraft();
|
|
9708
|
-
|
|
9817
|
+
surfaceRunRejected(error);
|
|
9818
|
+
return false;
|
|
9709
9819
|
}
|
|
9710
|
-
}, [activeChatSessionId,
|
|
9820
|
+
}, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, markLocalSubmitted, policy, selectedPiSession, setComposerDraft, surfaceRunRejected]);
|
|
9711
9821
|
const editQueued = useCallback18(() => {
|
|
9712
9822
|
if (!policy) return;
|
|
9713
9823
|
void policy.editQueued().then((result) => {
|
|
@@ -9719,12 +9829,12 @@ function PiChatPanel({
|
|
|
9719
9829
|
const stop = useCallback18(() => {
|
|
9720
9830
|
onComposerStop?.();
|
|
9721
9831
|
void policy?.stop().catch((error) => {
|
|
9722
|
-
addLocalNotice({ id: "stop-error", level: "error", text: errorMessage2(error, "Could not stop the
|
|
9832
|
+
addLocalNotice({ id: "stop-error", level: "error", text: errorMessage2(error, "Could not stop the chat session."), dismissible: true });
|
|
9723
9833
|
});
|
|
9724
9834
|
}, [addLocalNotice, onComposerStop, policy]);
|
|
9725
9835
|
const interrupt = useCallback18(() => {
|
|
9726
9836
|
void policy?.interrupt().catch((error) => {
|
|
9727
|
-
addLocalNotice({ id: "interrupt-error", level: "error", text: errorMessage2(error, "Could not interrupt the
|
|
9837
|
+
addLocalNotice({ id: "interrupt-error", level: "error", text: errorMessage2(error, "Could not interrupt the chat session."), dismissible: true });
|
|
9728
9838
|
});
|
|
9729
9839
|
}, [addLocalNotice, policy]);
|
|
9730
9840
|
useEffect21(() => {
|
|
@@ -9779,6 +9889,9 @@ function PiChatPanel({
|
|
|
9779
9889
|
}
|
|
9780
9890
|
return;
|
|
9781
9891
|
}
|
|
9892
|
+
if (result.type === "prompt" || result.type === "followup") {
|
|
9893
|
+
dropLocalNotice(RUN_REJECTED_NOTICE_ID);
|
|
9894
|
+
}
|
|
9782
9895
|
if (result.type === "prompt") {
|
|
9783
9896
|
if (shouldHoldLocalSubmitted(selectedPiSession, result.cursor)) markLocalSubmitted(activeSessionId);
|
|
9784
9897
|
else clearLocalSubmitted(activeSessionId);
|
|
@@ -9792,9 +9905,9 @@ function PiChatPanel({
|
|
|
9792
9905
|
clearLocalSubmitted(activeSessionId);
|
|
9793
9906
|
restoreSubmittedDraft();
|
|
9794
9907
|
settlePendingAutoSubmit(activeSessionId);
|
|
9795
|
-
|
|
9908
|
+
surfaceRunRejected(error);
|
|
9796
9909
|
});
|
|
9797
|
-
}, [activeSessionId,
|
|
9910
|
+
}, [activeSessionId, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, dropLocalNotice, initialDraft, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit, surfaceRunRejected]);
|
|
9798
9911
|
useEffect21(() => {
|
|
9799
9912
|
if (workspaceWarmupStatus?.status === "ready") {
|
|
9800
9913
|
clearLocalNotice("workspace-warmup");
|
|
@@ -9896,6 +10009,7 @@ function PiChatPanel({
|
|
|
9896
10009
|
toolRenderers: mergedToolRenderers,
|
|
9897
10010
|
runtimeNotices,
|
|
9898
10011
|
onDismissNotice: clearLocalNotice,
|
|
10012
|
+
renderNoticeAction,
|
|
9899
10013
|
onScrollToBottomReady: (scrollToBottom) => {
|
|
9900
10014
|
scrollToBottomRef.current = scrollToBottom;
|
|
9901
10015
|
},
|
|
@@ -9945,6 +10059,8 @@ function PiChatPanel({
|
|
|
9945
10059
|
selectedModel,
|
|
9946
10060
|
modelOptions,
|
|
9947
10061
|
modelControlled: model !== void 0,
|
|
10062
|
+
hideDefaultModelOption,
|
|
10063
|
+
hideComposerSettings,
|
|
9948
10064
|
onModelChange: modelDiscovery.setModel,
|
|
9949
10065
|
onSetModelPickerOpen: setModelPickerOpen,
|
|
9950
10066
|
onOpenModelPicker: openModelPicker,
|
|
@@ -9966,7 +10082,7 @@ function PiChatPanel({
|
|
|
9966
10082
|
]
|
|
9967
10083
|
}
|
|
9968
10084
|
) }),
|
|
9969
|
-
debug ? /* @__PURE__ */ jsx33(Suspense, { fallback: null, children: /* @__PURE__ */ jsx33("div", { "aria-label": "
|
|
10085
|
+
debug ? /* @__PURE__ */ jsx33(Suspense, { fallback: null, children: /* @__PURE__ */ jsx33("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx33(
|
|
9970
10086
|
DebugDrawer2,
|
|
9971
10087
|
{
|
|
9972
10088
|
apiBaseUrl,
|