@hachej/boring-agent 0.1.109 → 0.1.110
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/{errors-CEU4iujR.d.ts → admin-DORVQfLn.d.ts} +80 -3
- package/dist/{chunk-GS4ADE34.js → chunk-42D6KAQV.js} +36 -12
- package/dist/{chunk-6OANSUEI.js → chunk-72KT6VWV.js} +59 -2
- package/dist/{chunk-3JFPS2O6.js → chunk-KJIEMSQP.js} +10 -1
- package/dist/{chunk-MVKXLMH3.js → chunk-KN72XWP7.js} +1 -1
- package/dist/{chunk-KDXIXVF6.js → chunk-V3H5A3BR.js} +12410 -5047
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +2 -1
- package/dist/{createHarness-AHWSC36D.js → createHarness-DOLOHA62.js} +2 -2
- package/dist/file-search-BUgMSKZE.d.ts +289 -0
- package/dist/front/index.d.ts +89 -4
- package/dist/front/index.js +508 -47
- package/dist/front/styles.css +55 -55
- package/dist/{harness-Ct0e4RWc.d.ts → harness-qHlMJhZo.d.ts} +1 -1
- package/dist/{piChatEvent-BUWIzw5l.d.ts → piChatEvent-DpE0lIb9.d.ts} +49 -17
- package/dist/{piChatSessionService-nvroRAVk.d.ts → piChatSessionService-CBct-hmz.d.ts} +4 -1
- package/dist/server/agent-host/testing/gatewayConformance.d.ts +7 -6
- package/dist/server/agent-host/testing/gatewayConformance.js +14 -7
- package/dist/server/index.d.ts +890 -56
- package/dist/server/index.js +73 -7
- package/dist/server/worker/index.js +5 -5
- package/dist/{lease-nASWv2tU.d.ts → share-entry-CxS6UJ_L.d.ts} +77 -179
- package/dist/shared/index.d.ts +118 -222
- package/dist/shared/index.js +17 -5
- package/dist/{types-B7-PN5jV.d.ts → types-DcWoQMn8.d.ts} +346 -10
- package/docs/AGENT_GATEWAY_V0.md +22 -2
- package/docs/AGENT_PACKAGES.md +15 -5
- package/docs/ERROR_CODES.md +14 -0
- package/package.json +10 -18
- package/dist/file-search-BGUHnW9o.d.ts +0 -87
package/dist/front/index.js
CHANGED
|
@@ -32,13 +32,13 @@ import {
|
|
|
32
32
|
WORKSPACE_COMMAND_NOTIFY_EVENT,
|
|
33
33
|
extractToolUiMetadata,
|
|
34
34
|
sanitizeToolUiMetadata
|
|
35
|
-
} from "../chunk-
|
|
35
|
+
} from "../chunk-KN72XWP7.js";
|
|
36
36
|
import {
|
|
37
37
|
safeRandomUUID
|
|
38
38
|
} from "../chunk-YGRQ4Q5P.js";
|
|
39
39
|
import {
|
|
40
40
|
ErrorCode
|
|
41
|
-
} from "../chunk-
|
|
41
|
+
} from "../chunk-KJIEMSQP.js";
|
|
42
42
|
|
|
43
43
|
// src/front/upload/uploadFile.ts
|
|
44
44
|
function readAsDataUrl(file) {
|
|
@@ -1313,7 +1313,7 @@ var ToolHeader = ({
|
|
|
1313
1313
|
state,
|
|
1314
1314
|
icon,
|
|
1315
1315
|
toolName,
|
|
1316
|
-
statusLabel,
|
|
1316
|
+
statusLabel: statusLabel2,
|
|
1317
1317
|
...props
|
|
1318
1318
|
}) => {
|
|
1319
1319
|
const derivedName = type === "dynamic-tool" || type === "tool-call" ? toolName : type.split("-").slice(1).join("-");
|
|
@@ -1330,7 +1330,7 @@ var ToolHeader = ({
|
|
|
1330
1330
|
/* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
1331
1331
|
icon ?? /* @__PURE__ */ jsx8(WrenchIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
1332
1332
|
/* @__PURE__ */ jsx8("span", { "data-boring-agent-part": "tool-title", className: "min-w-0 flex-1 truncate text-sm font-medium", children: title ?? derivedName }),
|
|
1333
|
-
getStatusBadge(state,
|
|
1333
|
+
getStatusBadge(state, statusLabel2)
|
|
1334
1334
|
] }),
|
|
1335
1335
|
/* @__PURE__ */ jsx8(
|
|
1336
1336
|
ChevronDownIcon,
|
|
@@ -3914,7 +3914,7 @@ function piChatReducer(state, action) {
|
|
|
3914
3914
|
case "model-confirmed":
|
|
3915
3915
|
return { ...state, currentModel: action.model };
|
|
3916
3916
|
case "event":
|
|
3917
|
-
return
|
|
3917
|
+
return reducePiChatEvent(state, action.event).state;
|
|
3918
3918
|
case "optimistic-user-message":
|
|
3919
3919
|
return {
|
|
3920
3920
|
...state,
|
|
@@ -4045,18 +4045,29 @@ function mergeSnapshotMessagesIntoLocal(currentMessages, snapshotMessages) {
|
|
|
4045
4045
|
for (const message of snapshotMessages) merged = replaceOrAppendMessage(merged, message);
|
|
4046
4046
|
return merged;
|
|
4047
4047
|
}
|
|
4048
|
-
function
|
|
4049
|
-
if (event.seq <= state.lastSeq) return state;
|
|
4048
|
+
function reducePiChatEvent(state, event) {
|
|
4049
|
+
if (event.seq <= state.lastSeq) return { state, accepted: false };
|
|
4050
4050
|
const expectedSeq = state.lastSeq + 1;
|
|
4051
4051
|
if (event.seq > expectedSeq) {
|
|
4052
4052
|
return {
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4053
|
+
state: {
|
|
4054
|
+
...state,
|
|
4055
|
+
connection: { ...state.connection, state: "reconnecting" },
|
|
4056
|
+
needsResync: { expectedSeq, actualSeq: event.seq, lastSeq: state.lastSeq }
|
|
4057
|
+
},
|
|
4058
|
+
accepted: false
|
|
4056
4059
|
};
|
|
4057
4060
|
}
|
|
4058
|
-
const
|
|
4059
|
-
|
|
4061
|
+
const sequencedState = { ...state, lastSeq: event.seq, needsResync: void 0 };
|
|
4062
|
+
if (isRejectedTerminalEvent(sequencedState, event)) {
|
|
4063
|
+
return { state: sequencedState, accepted: false };
|
|
4064
|
+
}
|
|
4065
|
+
return { state: reduceEvent(sequencedState, event), accepted: true };
|
|
4066
|
+
}
|
|
4067
|
+
function isRejectedTerminalEvent(state, event) {
|
|
4068
|
+
if (event.type === "error") return isStaleTurnScopedEvent(state, event.turnId);
|
|
4069
|
+
if (event.type !== "agent-end") return false;
|
|
4070
|
+
return isStaleTurnScopedEvent(state, event.turnId) || isLateNonErrorAgentEndAfterTerminalError(state, event.status);
|
|
4060
4071
|
}
|
|
4061
4072
|
function reduceEvent(state, event) {
|
|
4062
4073
|
switch (event.type) {
|
|
@@ -4065,8 +4076,6 @@ function reduceEvent(state, event) {
|
|
|
4065
4076
|
case "agent-start":
|
|
4066
4077
|
return { ...state, status: "streaming", turnId: event.turnId, error: void 0, streamingPreservedTextPartKeys: void 0 };
|
|
4067
4078
|
case "agent-end":
|
|
4068
|
-
if (isStaleTurnScopedEvent(state, event.turnId)) return state;
|
|
4069
|
-
if (isLateNonErrorAgentEndAfterTerminalError(state, event.status)) return state;
|
|
4070
4079
|
return settleTurn({
|
|
4071
4080
|
...state,
|
|
4072
4081
|
status: event.status === "error" ? "error" : "idle",
|
|
@@ -4116,7 +4125,6 @@ function reduceEvent(state, event) {
|
|
|
4116
4125
|
})
|
|
4117
4126
|
};
|
|
4118
4127
|
case "error":
|
|
4119
|
-
if (isStaleTurnScopedEvent(state, event.turnId)) return state;
|
|
4120
4128
|
return settleTurn({
|
|
4121
4129
|
...state,
|
|
4122
4130
|
status: "error",
|
|
@@ -4523,6 +4531,17 @@ function createPiChatStore(initialState, options = {}) {
|
|
|
4523
4531
|
if (scheduled !== void 0) return;
|
|
4524
4532
|
scheduled = scheduleNotify(notifyNow);
|
|
4525
4533
|
};
|
|
4534
|
+
const publish = (dispatchOptions) => {
|
|
4535
|
+
if (dispatchOptions?.flush) {
|
|
4536
|
+
if (scheduled !== void 0) {
|
|
4537
|
+
cancelNotify(scheduled);
|
|
4538
|
+
scheduled = void 0;
|
|
4539
|
+
}
|
|
4540
|
+
notifyNow();
|
|
4541
|
+
return;
|
|
4542
|
+
}
|
|
4543
|
+
schedule();
|
|
4544
|
+
};
|
|
4526
4545
|
return {
|
|
4527
4546
|
getState() {
|
|
4528
4547
|
return state;
|
|
@@ -4530,15 +4549,14 @@ function createPiChatStore(initialState, options = {}) {
|
|
|
4530
4549
|
dispatch(action, dispatchOptions) {
|
|
4531
4550
|
if (disposed) return;
|
|
4532
4551
|
state = piChatReducer(state, action);
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
schedule();
|
|
4552
|
+
publish(dispatchOptions);
|
|
4553
|
+
},
|
|
4554
|
+
dispatchEvent(event, dispatchOptions) {
|
|
4555
|
+
if (disposed) return false;
|
|
4556
|
+
const reduction = reducePiChatEvent(state, event);
|
|
4557
|
+
state = reduction.state;
|
|
4558
|
+
publish(dispatchOptions);
|
|
4559
|
+
return reduction.accepted;
|
|
4542
4560
|
},
|
|
4543
4561
|
subscribe(listener) {
|
|
4544
4562
|
if (disposed) return () => {
|
|
@@ -5004,8 +5022,8 @@ var RemotePiSession = class {
|
|
|
5004
5022
|
return;
|
|
5005
5023
|
}
|
|
5006
5024
|
this.recordEventType(frame.type);
|
|
5007
|
-
this.
|
|
5008
|
-
this.
|
|
5025
|
+
const accepted = this.store.dispatchEvent(frame);
|
|
5026
|
+
if (accepted && this.isStreamActive(generation, runId)) this.options.onEvent?.(frame);
|
|
5009
5027
|
if (this.store.getState().needsResync && this.isStreamActive(generation, runId)) {
|
|
5010
5028
|
this.gapCount += 1;
|
|
5011
5029
|
this.rehydrateAfterStreamReset(generation);
|
|
@@ -8197,15 +8215,19 @@ var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
8197
8215
|
function isCollapsibleTool(part) {
|
|
8198
8216
|
return part.type === "tool-call" && COLLAPSIBLE_TOOL_NAMES.has(part.toolName);
|
|
8199
8217
|
}
|
|
8218
|
+
var EMPTY_VISIBLE_TOOLS = [];
|
|
8219
|
+
function toolNameOf(part) {
|
|
8220
|
+
return part.toolName;
|
|
8221
|
+
}
|
|
8200
8222
|
function PiTimelineMessage(props) {
|
|
8201
8223
|
return /* @__PURE__ */ jsx24(ChatMessageContributionBoundary, { message: props.message, children: /* @__PURE__ */ jsx24(DefaultPiTimelineMessage, { ...props }) });
|
|
8202
8224
|
}
|
|
8203
|
-
function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate }) {
|
|
8225
|
+
function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate, renderMode = "full", messagesOnlyVisibleTools = EMPTY_VISIBLE_TOOLS, messagesOnlyHideCopyActions = false }) {
|
|
8204
8226
|
const role = message.role;
|
|
8205
8227
|
const isAssistant = role === "assistant";
|
|
8206
8228
|
const textParts = message.parts.filter((part) => part.type === "text");
|
|
8207
8229
|
const fileParts = message.parts.filter((part) => part.type === "file");
|
|
8208
|
-
const finalParts = groupRenderableParts(message);
|
|
8230
|
+
const finalParts = groupRenderableParts(message, renderMode, messagesOnlyVisibleTools);
|
|
8209
8231
|
const attachmentSummaryPaths = role === "user" ? attachmentPathsFromTextParts(textParts) : [];
|
|
8210
8232
|
const openArtifact = useOpenArtifact();
|
|
8211
8233
|
const effectiveMentionCatalog = useMemo13(() => ({
|
|
@@ -8330,7 +8352,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
|
|
|
8330
8352
|
return null;
|
|
8331
8353
|
})
|
|
8332
8354
|
] }),
|
|
8333
|
-
isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx24(
|
|
8355
|
+
isAssistant && !(renderMode === "messages-only" && messagesOnlyHideCopyActions) && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx24(
|
|
8334
8356
|
MessageActionsBar,
|
|
8335
8357
|
{
|
|
8336
8358
|
text: textParts.map((part) => part.text).join("\n\n"),
|
|
@@ -8392,7 +8414,8 @@ function PlainToolCard({ part, renderers }) {
|
|
|
8392
8414
|
}
|
|
8393
8415
|
);
|
|
8394
8416
|
}
|
|
8395
|
-
function groupRenderableParts(message) {
|
|
8417
|
+
function groupRenderableParts(message, renderMode = "full", messagesOnlyVisibleTools = EMPTY_VISIBLE_TOOLS) {
|
|
8418
|
+
const messagesOnly = renderMode === "messages-only";
|
|
8396
8419
|
const grouped = [];
|
|
8397
8420
|
let pendingTools = [];
|
|
8398
8421
|
const flushTools = () => {
|
|
@@ -8404,6 +8427,12 @@ function groupRenderableParts(message) {
|
|
|
8404
8427
|
const key = partKey(message.id, part, index);
|
|
8405
8428
|
if (part.type === "file") return;
|
|
8406
8429
|
if (part.type === "tool-call") {
|
|
8430
|
+
if (messagesOnly) {
|
|
8431
|
+
if (!messagesOnlyVisibleTools.includes(toolNameOf(part))) return;
|
|
8432
|
+
flushTools();
|
|
8433
|
+
grouped.push({ kind: "tool-plain", key, part });
|
|
8434
|
+
return;
|
|
8435
|
+
}
|
|
8407
8436
|
if (isCollapsibleTool(part)) {
|
|
8408
8437
|
pendingTools.push({ part, key });
|
|
8409
8438
|
} else {
|
|
@@ -8414,6 +8443,7 @@ function groupRenderableParts(message) {
|
|
|
8414
8443
|
}
|
|
8415
8444
|
flushTools();
|
|
8416
8445
|
if (part.type === "reasoning") {
|
|
8446
|
+
if (messagesOnly) return;
|
|
8417
8447
|
const previous = grouped[grouped.length - 1];
|
|
8418
8448
|
if (previous?.kind === "reasoning") {
|
|
8419
8449
|
previous.text = `${previous.text}
|
|
@@ -8587,11 +8617,16 @@ function PiConversationSurface({
|
|
|
8587
8617
|
onScrollToBottomReady,
|
|
8588
8618
|
onSuggestionSubmit,
|
|
8589
8619
|
onRestoreDraft,
|
|
8590
|
-
windowResetKey
|
|
8620
|
+
windowResetKey,
|
|
8621
|
+
renderMode = "full",
|
|
8622
|
+
messagesOnlyVisibleTools,
|
|
8623
|
+
messagesOnlyHideCopyActions = false,
|
|
8624
|
+
messagesOnlyHiddenUserPrefixes = []
|
|
8591
8625
|
}) {
|
|
8592
|
-
const
|
|
8626
|
+
const visibleMessages = renderMode === "messages-only" && messagesOnlyHiddenUserPrefixes.length > 0 ? messages.filter((message) => !isHiddenHostPrompt(message, messagesOnlyHiddenUserPrefixes)) : messages;
|
|
8627
|
+
const messageItems = buildMessageRenderItems(visibleMessages);
|
|
8593
8628
|
const total = messageItems.length;
|
|
8594
|
-
const historyEmpty =
|
|
8629
|
+
const historyEmpty = visibleMessages.length === 0;
|
|
8595
8630
|
const terminalError = hasTerminalChatError(runtimeNotices, historyEmpty);
|
|
8596
8631
|
const [visibleCount, setVisibleCount] = useState17(TRANSCRIPT_WINDOW);
|
|
8597
8632
|
useEffect16(() => {
|
|
@@ -8616,8 +8651,8 @@ function PiConversationSurface({
|
|
|
8616
8651
|
chrome ? "max-w-3xl px-6 py-8" : "max-w-[680px] px-4 py-4",
|
|
8617
8652
|
emptyHero && "py-4 text-center"
|
|
8618
8653
|
), children: [
|
|
8619
|
-
|
|
8620
|
-
|
|
8654
|
+
historyEmpty && emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx25(ConversationHistoryLoadingState, {}) : null,
|
|
8655
|
+
historyEmpty && !emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx25(
|
|
8621
8656
|
ChatEmptyState,
|
|
8622
8657
|
{
|
|
8623
8658
|
eyebrow: emptyState?.eyebrow,
|
|
@@ -8645,7 +8680,10 @@ function PiConversationSurface({
|
|
|
8645
8680
|
showThoughts,
|
|
8646
8681
|
toolRenderers,
|
|
8647
8682
|
mentionCatalog,
|
|
8648
|
-
onMentionActivate
|
|
8683
|
+
onMentionActivate,
|
|
8684
|
+
renderMode,
|
|
8685
|
+
messagesOnlyVisibleTools,
|
|
8686
|
+
messagesOnlyHideCopyActions
|
|
8649
8687
|
},
|
|
8650
8688
|
key
|
|
8651
8689
|
)),
|
|
@@ -8730,6 +8768,12 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
|
|
|
8730
8768
|
}
|
|
8731
8769
|
) });
|
|
8732
8770
|
}
|
|
8771
|
+
function isHiddenHostPrompt(message, prefixes) {
|
|
8772
|
+
if (message.role !== "user") return false;
|
|
8773
|
+
return message.parts.some(
|
|
8774
|
+
(part) => part.type === "text" && prefixes.some((prefix) => part.text.startsWith(prefix))
|
|
8775
|
+
);
|
|
8776
|
+
}
|
|
8733
8777
|
function buildMessageRenderItems(messages) {
|
|
8734
8778
|
const assistantTurnCounts = /* @__PURE__ */ new Map();
|
|
8735
8779
|
for (const message of messages) {
|
|
@@ -10455,6 +10499,7 @@ function PiChatComposerSurface({
|
|
|
10455
10499
|
chrome,
|
|
10456
10500
|
pickerPlacement = "above",
|
|
10457
10501
|
isStreaming,
|
|
10502
|
+
composerActivity,
|
|
10458
10503
|
status,
|
|
10459
10504
|
disabled,
|
|
10460
10505
|
submitStatus,
|
|
@@ -10518,6 +10563,8 @@ function PiChatComposerSurface({
|
|
|
10518
10563
|
onStop
|
|
10519
10564
|
}) {
|
|
10520
10565
|
const prefersReducedMotion = useReducedMotion();
|
|
10566
|
+
const customActivity = composerActivity !== void 0;
|
|
10567
|
+
const visibleActivity = customActivity ? composerActivity != null : isStreaming;
|
|
10521
10568
|
const workspaceRequestId = getHeaderValue(requestHeaders, "x-boring-workspace-id");
|
|
10522
10569
|
const composerContributions = useComposerContributions();
|
|
10523
10570
|
const uploadAttachment = useCallback17((file) => uploadFile(file, {
|
|
@@ -10562,10 +10609,10 @@ function PiChatComposerSurface({
|
|
|
10562
10609
|
className: cn(
|
|
10563
10610
|
"mx-auto w-full overflow-hidden transition-[margin,max-height,opacity] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
10564
10611
|
chrome ? "max-w-3xl" : "max-w-[680px]",
|
|
10565
|
-
|
|
10612
|
+
visibleActivity ? "mb-2 max-h-8 opacity-100" : "mb-0 max-h-0 opacity-0"
|
|
10566
10613
|
),
|
|
10567
|
-
"aria-hidden": !
|
|
10568
|
-
children: /* @__PURE__ */ jsxs30(
|
|
10614
|
+
"aria-hidden": !visibleActivity,
|
|
10615
|
+
children: customActivity ? composerActivity : /* @__PURE__ */ jsxs30(
|
|
10569
10616
|
"div",
|
|
10570
10617
|
{
|
|
10571
10618
|
"data-testid": isStreaming ? "chat-working" : void 0,
|
|
@@ -11014,8 +11061,10 @@ function useExternalRemotePiSession({
|
|
|
11014
11061
|
}, [agentTypeId, apiBaseUrl, createRemoteSession, fetch2, remoteSessionOptionsKey, sessionId, stableRequestHeaders, storageScope, workspaceId]);
|
|
11015
11062
|
useEffect22(() => {
|
|
11016
11063
|
if (!session) return;
|
|
11017
|
-
if (enabled) session.
|
|
11018
|
-
else session.
|
|
11064
|
+
if (!enabled) session.suspendStream?.();
|
|
11065
|
+
else if (remoteSessionOptionsRef.current?.autoStart !== false || session.getState().connection.state === "suspended") {
|
|
11066
|
+
session.resumeStream?.();
|
|
11067
|
+
}
|
|
11019
11068
|
}, [enabled, session]);
|
|
11020
11069
|
return session;
|
|
11021
11070
|
}
|
|
@@ -11151,6 +11200,10 @@ function PiChatPanel({
|
|
|
11151
11200
|
fetch: fetch2,
|
|
11152
11201
|
className,
|
|
11153
11202
|
chrome = true,
|
|
11203
|
+
renderMode = "full",
|
|
11204
|
+
messagesOnlyVisibleTools,
|
|
11205
|
+
messagesOnlyHideCopyActions = false,
|
|
11206
|
+
messagesOnlyHiddenUserPrefixes,
|
|
11154
11207
|
debug = false,
|
|
11155
11208
|
showSessions,
|
|
11156
11209
|
hotReloadEnabled = true,
|
|
@@ -11158,6 +11211,7 @@ function PiChatPanel({
|
|
|
11158
11211
|
emptyState,
|
|
11159
11212
|
emptyPlacement = "hero",
|
|
11160
11213
|
composerPlaceholder,
|
|
11214
|
+
composerActivity,
|
|
11161
11215
|
initialDraft,
|
|
11162
11216
|
autoSubmitInitialDraft = false,
|
|
11163
11217
|
onDraftRestored,
|
|
@@ -11327,6 +11381,9 @@ function PiChatPanel({
|
|
|
11327
11381
|
const [resumeQueuedPendingSessionIds, setResumeQueuedPendingSessionIds] = useState22(() => /* @__PURE__ */ new Set());
|
|
11328
11382
|
const [resumeQueuedErrorsBySessionId, setResumeQueuedErrorsBySessionId] = useState22(() => /* @__PURE__ */ new Map());
|
|
11329
11383
|
const resumeQueuedInFlightRef = useRef18(/* @__PURE__ */ new Map());
|
|
11384
|
+
const resumeQueuedOwnerTokenBySessionIdRef = useRef18(/* @__PURE__ */ new Map());
|
|
11385
|
+
const resumeQueuedSessionTokensRef = useRef18(/* @__PURE__ */ new WeakMap());
|
|
11386
|
+
const resumeQueuedSessionTokenSeqRef = useRef18(0);
|
|
11330
11387
|
const initialDraftGuard = useRef18(new InitialDraftAutoSubmitGuard());
|
|
11331
11388
|
const pendingAutoSubmitSettleRef = useRef18(void 0);
|
|
11332
11389
|
const acceptedAutoSubmitSettleRef = useRef18(void 0);
|
|
@@ -11407,13 +11464,30 @@ function PiChatPanel({
|
|
|
11407
11464
|
const isPolicySessionActive = useCallback19(() => selectedPiSessionRef.current === selectedPiSession && activeChatIdentityRef.current === activeChatIdentity, [activeChatIdentity, selectedPiSession]);
|
|
11408
11465
|
const resumeQueuedPending = Boolean(activeChatSessionId && resumeQueuedPendingSessionIds.has(activeChatSessionId));
|
|
11409
11466
|
const resumeQueuedError = activeChatSessionId ? resumeQueuedErrorsBySessionId.get(activeChatSessionId) : void 0;
|
|
11410
|
-
const chatScopeKey = `${externalSessionId ? "external" : "managed"}\0${agentTypeId}\0${storageScope ?? ""}`;
|
|
11411
11467
|
useEffect23(() => {
|
|
11468
|
+
if (!activeChatSessionId || !selectedPiSession) return;
|
|
11469
|
+
let ownerToken = resumeQueuedSessionTokensRef.current.get(selectedPiSession);
|
|
11470
|
+
if (ownerToken === void 0) {
|
|
11471
|
+
ownerToken = ++resumeQueuedSessionTokenSeqRef.current;
|
|
11472
|
+
resumeQueuedSessionTokensRef.current.set(selectedPiSession, ownerToken);
|
|
11473
|
+
}
|
|
11474
|
+
if (resumeQueuedOwnerTokenBySessionIdRef.current.get(activeChatSessionId) === ownerToken) return;
|
|
11475
|
+
resumeQueuedOwnerTokenBySessionIdRef.current.set(activeChatSessionId, ownerToken);
|
|
11412
11476
|
setQueueMutationPending(false);
|
|
11413
|
-
setResumeQueuedPendingSessionIds(
|
|
11414
|
-
|
|
11415
|
-
|
|
11416
|
-
|
|
11477
|
+
setResumeQueuedPendingSessionIds((previous) => {
|
|
11478
|
+
if (!previous.has(activeChatSessionId)) return previous;
|
|
11479
|
+
const next = new Set(previous);
|
|
11480
|
+
next.delete(activeChatSessionId);
|
|
11481
|
+
return next;
|
|
11482
|
+
});
|
|
11483
|
+
setResumeQueuedErrorsBySessionId((previous) => {
|
|
11484
|
+
if (!previous.has(activeChatSessionId)) return previous;
|
|
11485
|
+
const next = new Map(previous);
|
|
11486
|
+
next.delete(activeChatSessionId);
|
|
11487
|
+
return next;
|
|
11488
|
+
});
|
|
11489
|
+
resumeQueuedInFlightRef.current.delete(activeChatSessionId);
|
|
11490
|
+
}, [activeChatSessionId, selectedPiSession]);
|
|
11417
11491
|
const warmupNotice = composerNoticeForWarmup(workspaceWarmupStatus);
|
|
11418
11492
|
const runtimeDependenciesNotice = composerNoticeForRuntimeDependencies(workspaceWarmupStatus);
|
|
11419
11493
|
const workspaceWarmupBlocked = Boolean(warmupNotice);
|
|
@@ -11888,6 +11962,7 @@ function PiChatPanel({
|
|
|
11888
11962
|
const run = policy.resumeQueued();
|
|
11889
11963
|
resumeQueuedInFlightRef.current.set(sessionId2, run);
|
|
11890
11964
|
void run.catch((error) => {
|
|
11965
|
+
if (resumeQueuedInFlightRef.current.get(sessionId2) !== run) return;
|
|
11891
11966
|
setResumeQueuedErrorsBySessionId((previous) => new Map(previous).set(sessionId2, {
|
|
11892
11967
|
id: errorNoticeId,
|
|
11893
11968
|
level: "error",
|
|
@@ -12049,6 +12124,7 @@ function PiChatPanel({
|
|
|
12049
12124
|
chrome,
|
|
12050
12125
|
pickerPlacement: emptyHero ? "above-compact" : "above",
|
|
12051
12126
|
isStreaming,
|
|
12127
|
+
composerActivity,
|
|
12052
12128
|
status,
|
|
12053
12129
|
disabled,
|
|
12054
12130
|
submitStatus,
|
|
@@ -12177,7 +12253,11 @@ function PiChatPanel({
|
|
|
12177
12253
|
},
|
|
12178
12254
|
onSuggestionSubmit: ({ text, files, source }) => sendComposerMessage({ text, files, source }),
|
|
12179
12255
|
onRestoreDraft: setComposerDraft,
|
|
12180
|
-
windowResetKey: activeSessionId
|
|
12256
|
+
windowResetKey: activeSessionId,
|
|
12257
|
+
renderMode,
|
|
12258
|
+
messagesOnlyVisibleTools,
|
|
12259
|
+
messagesOnlyHideCopyActions,
|
|
12260
|
+
messagesOnlyHiddenUserPrefixes
|
|
12181
12261
|
}
|
|
12182
12262
|
),
|
|
12183
12263
|
composerSurface,
|
|
@@ -12350,6 +12430,385 @@ function scopedHeaders(headers, storageScope) {
|
|
|
12350
12430
|
return result;
|
|
12351
12431
|
}
|
|
12352
12432
|
|
|
12433
|
+
// src/front/settings/CredentialSettingsSurface.tsx
|
|
12434
|
+
import { useCallback as useCallback21, useEffect as useEffect25, useMemo as useMemo19, useRef as useRef19, useState as useState24 } from "react";
|
|
12435
|
+
import {
|
|
12436
|
+
Button as Button12,
|
|
12437
|
+
Input as Input2,
|
|
12438
|
+
Label,
|
|
12439
|
+
Notice,
|
|
12440
|
+
SettingsPanel
|
|
12441
|
+
} from "@hachej/boring-ui-kit";
|
|
12442
|
+
import { ExternalLink, KeyRound } from "lucide-react";
|
|
12443
|
+
|
|
12444
|
+
// src/front/settings/credentialSettingsClient.ts
|
|
12445
|
+
var STATES = /* @__PURE__ */ new Set([
|
|
12446
|
+
"active",
|
|
12447
|
+
"disabled",
|
|
12448
|
+
"revoked",
|
|
12449
|
+
"needs_reauth",
|
|
12450
|
+
"intentionally_absent",
|
|
12451
|
+
"instance_fallback_enabled",
|
|
12452
|
+
"not_configured"
|
|
12453
|
+
]);
|
|
12454
|
+
function asRecord3(value) {
|
|
12455
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Credential service returned an invalid response");
|
|
12456
|
+
return value;
|
|
12457
|
+
}
|
|
12458
|
+
function credentialMetadataFromResponse(value) {
|
|
12459
|
+
const input = asRecord3(value);
|
|
12460
|
+
if (typeof input.providerId !== "string" || typeof input.displayName !== "string" || typeof input.credentialType !== "string" || typeof input.state !== "string" || !STATES.has(input.state)) throw new Error("Credential service returned an invalid response");
|
|
12461
|
+
const rawRevocation = input.oauthRevocation && typeof input.oauthRevocation === "object" ? asRecord3(input.oauthRevocation) : void 0;
|
|
12462
|
+
const oauthRevocation = rawRevocation && rawRevocation.localStatus === "revoked" && ["pending", "confirmed"].includes(String(rawRevocation.upstreamStatus)) && typeof rawRevocation.attemptedAt === "string" ? {
|
|
12463
|
+
localStatus: "revoked",
|
|
12464
|
+
upstreamStatus: rawRevocation.upstreamStatus,
|
|
12465
|
+
attemptedAt: rawRevocation.attemptedAt
|
|
12466
|
+
} : void 0;
|
|
12467
|
+
return {
|
|
12468
|
+
providerId: input.providerId,
|
|
12469
|
+
displayName: input.displayName,
|
|
12470
|
+
credentialType: input.credentialType,
|
|
12471
|
+
state: input.state,
|
|
12472
|
+
...typeof input.credentialVersion === "number" ? { credentialVersion: input.credentialVersion } : {},
|
|
12473
|
+
...typeof input.maskedLastFourSuffix === "string" ? { maskedLastFourSuffix: input.maskedLastFourSuffix.slice(-4) } : {},
|
|
12474
|
+
...typeof input.createdAt === "string" ? { createdAt: input.createdAt } : {},
|
|
12475
|
+
...typeof input.updatedAt === "string" ? { updatedAt: input.updatedAt } : {},
|
|
12476
|
+
...oauthRevocation ? { oauthRevocation } : {}
|
|
12477
|
+
};
|
|
12478
|
+
}
|
|
12479
|
+
function oauthFlowFromResponse(value) {
|
|
12480
|
+
const input = asRecord3(value);
|
|
12481
|
+
if (typeof input.flowId !== "string" || input.providerId !== "openai-codex" || !["pending", "succeeded", "failed", "cancelled"].includes(String(input.status)) || !Array.isArray(input.events)) throw new Error("Credential service returned an invalid sign-in response");
|
|
12482
|
+
const events = [];
|
|
12483
|
+
for (const candidate of input.events) {
|
|
12484
|
+
const event = asRecord3(candidate);
|
|
12485
|
+
if (event.type === "progress") events.push({ type: "progress" });
|
|
12486
|
+
if (event.type === "auth_url" && typeof event.url === "string" && event.url.startsWith("https://")) {
|
|
12487
|
+
events.push({ type: "auth_url", url: event.url });
|
|
12488
|
+
}
|
|
12489
|
+
if (event.type === "device_code" && typeof event.userCode === "string" && typeof event.verificationUri === "string" && event.verificationUri.startsWith("https://")) {
|
|
12490
|
+
events.push({
|
|
12491
|
+
type: "device_code",
|
|
12492
|
+
userCode: event.userCode,
|
|
12493
|
+
verificationUri: event.verificationUri,
|
|
12494
|
+
...typeof event.expiresInSeconds === "number" ? { expiresInSeconds: event.expiresInSeconds } : {}
|
|
12495
|
+
});
|
|
12496
|
+
}
|
|
12497
|
+
}
|
|
12498
|
+
let prompt;
|
|
12499
|
+
if (input.prompt && typeof input.prompt === "object") {
|
|
12500
|
+
const rawPrompt = asRecord3(input.prompt);
|
|
12501
|
+
if (typeof rawPrompt.type === "string") {
|
|
12502
|
+
prompt = {
|
|
12503
|
+
type: rawPrompt.type,
|
|
12504
|
+
...Array.isArray(rawPrompt.options) ? {
|
|
12505
|
+
options: rawPrompt.options.flatMap((option) => {
|
|
12506
|
+
const item = asRecord3(option);
|
|
12507
|
+
return typeof item.id === "string" && typeof item.label === "string" ? [{ id: item.id, label: item.label }] : [];
|
|
12508
|
+
})
|
|
12509
|
+
} : {}
|
|
12510
|
+
};
|
|
12511
|
+
}
|
|
12512
|
+
}
|
|
12513
|
+
return {
|
|
12514
|
+
flowId: input.flowId,
|
|
12515
|
+
providerId: "openai-codex",
|
|
12516
|
+
status: input.status,
|
|
12517
|
+
events,
|
|
12518
|
+
...prompt ? { prompt } : {}
|
|
12519
|
+
};
|
|
12520
|
+
}
|
|
12521
|
+
function createCredentialSettingsClient(apiBaseUrl = "", fetchImpl = fetch, requestHeaders) {
|
|
12522
|
+
const base = apiBaseUrl.replace(/\/$/, "");
|
|
12523
|
+
const request = async (path, init) => {
|
|
12524
|
+
const response = await fetchImpl(`${base}${path}`, {
|
|
12525
|
+
credentials: "include",
|
|
12526
|
+
...init,
|
|
12527
|
+
headers: {
|
|
12528
|
+
...requestHeaders,
|
|
12529
|
+
...init?.body ? { "Content-Type": "application/json" } : {},
|
|
12530
|
+
...init?.headers
|
|
12531
|
+
}
|
|
12532
|
+
});
|
|
12533
|
+
if (!response.ok) throw new Error(response.status === 403 ? "Only workspace owners can manage credentials." : "Credential operation failed. Try again.");
|
|
12534
|
+
return response.status === 204 ? void 0 : response.json();
|
|
12535
|
+
};
|
|
12536
|
+
const metadataAction = async (providerId, suffix, method) => credentialMetadataFromResponse(await request(`/api/v1/credentials/${encodeURIComponent(providerId)}${suffix}`, { method }));
|
|
12537
|
+
return {
|
|
12538
|
+
async list() {
|
|
12539
|
+
const payload = asRecord3(await request("/api/v1/credentials"));
|
|
12540
|
+
if (!Array.isArray(payload.credentials)) throw new Error("Credential service returned an invalid response");
|
|
12541
|
+
return payload.credentials.map(credentialMetadataFromResponse);
|
|
12542
|
+
},
|
|
12543
|
+
async putApiKey(providerId, apiKey) {
|
|
12544
|
+
return credentialMetadataFromResponse(await request(`/api/v1/credentials/${encodeURIComponent(providerId)}`, {
|
|
12545
|
+
method: "PUT",
|
|
12546
|
+
body: JSON.stringify({ fields: { "api-key": apiKey } })
|
|
12547
|
+
}));
|
|
12548
|
+
},
|
|
12549
|
+
disable: (providerId) => metadataAction(providerId, "/disable", "POST"),
|
|
12550
|
+
revoke: (providerId) => metadataAction(providerId, "/revoke", "POST"),
|
|
12551
|
+
delete: (providerId) => metadataAction(providerId, "", "DELETE"),
|
|
12552
|
+
async startCodexLogin() {
|
|
12553
|
+
return oauthFlowFromResponse(await request("/api/v1/credentials/openai-codex/oauth", { method: "POST" }));
|
|
12554
|
+
},
|
|
12555
|
+
async getCodexLogin(flowId) {
|
|
12556
|
+
return oauthFlowFromResponse(await request(`/api/v1/credentials/openai-codex/oauth/${encodeURIComponent(flowId)}`));
|
|
12557
|
+
},
|
|
12558
|
+
async respondToCodexLogin(flowId, value) {
|
|
12559
|
+
return oauthFlowFromResponse(await request(`/api/v1/credentials/openai-codex/oauth/${encodeURIComponent(flowId)}/respond`, {
|
|
12560
|
+
method: "POST",
|
|
12561
|
+
body: JSON.stringify({ value })
|
|
12562
|
+
}));
|
|
12563
|
+
},
|
|
12564
|
+
async cancelCodexLogin(flowId) {
|
|
12565
|
+
await request(`/api/v1/credentials/openai-codex/oauth/${encodeURIComponent(flowId)}`, { method: "DELETE" });
|
|
12566
|
+
}
|
|
12567
|
+
};
|
|
12568
|
+
}
|
|
12569
|
+
|
|
12570
|
+
// src/front/settings/CredentialSettingsSurface.tsx
|
|
12571
|
+
import { Fragment as Fragment14, jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
12572
|
+
function statusLabel(credential) {
|
|
12573
|
+
const labels = {
|
|
12574
|
+
active: "Connected",
|
|
12575
|
+
disabled: "Disabled",
|
|
12576
|
+
revoked: "Revoked",
|
|
12577
|
+
needs_reauth: "Sign-in required",
|
|
12578
|
+
intentionally_absent: "Deleted",
|
|
12579
|
+
instance_fallback_enabled: "Instance fallback",
|
|
12580
|
+
not_configured: "Not connected"
|
|
12581
|
+
};
|
|
12582
|
+
return labels[credential.state] ?? "Unavailable";
|
|
12583
|
+
}
|
|
12584
|
+
function updatedLabel(value) {
|
|
12585
|
+
if (!value) return void 0;
|
|
12586
|
+
const date = new Date(value);
|
|
12587
|
+
return Number.isNaN(date.getTime()) ? void 0 : date.toLocaleString();
|
|
12588
|
+
}
|
|
12589
|
+
function CredentialSettingsSurfaceForWorkspace({
|
|
12590
|
+
isWorkspaceOwner,
|
|
12591
|
+
workspaceId,
|
|
12592
|
+
apiBaseUrl = "",
|
|
12593
|
+
client: suppliedClient
|
|
12594
|
+
}) {
|
|
12595
|
+
const client = useMemo19(
|
|
12596
|
+
() => suppliedClient ?? createCredentialSettingsClient(
|
|
12597
|
+
apiBaseUrl,
|
|
12598
|
+
fetch,
|
|
12599
|
+
workspaceId ? { "x-boring-workspace-id": workspaceId } : void 0
|
|
12600
|
+
),
|
|
12601
|
+
[apiBaseUrl, suppliedClient, workspaceId]
|
|
12602
|
+
);
|
|
12603
|
+
const [credentials, setCredentials] = useState24([]);
|
|
12604
|
+
const [loading, setLoading] = useState24(true);
|
|
12605
|
+
const [busyProvider, setBusyProvider] = useState24(null);
|
|
12606
|
+
const [error, setError] = useState24(null);
|
|
12607
|
+
const [success, setSuccess] = useState24(null);
|
|
12608
|
+
const [confirmDelete, setConfirmDelete] = useState24(null);
|
|
12609
|
+
const [oauthFlow, setOauthFlow] = useState24(null);
|
|
12610
|
+
const oauthRequestEpoch = useRef19(0);
|
|
12611
|
+
const load = useCallback21(async () => {
|
|
12612
|
+
setError(null);
|
|
12613
|
+
try {
|
|
12614
|
+
setCredentials(await client.list());
|
|
12615
|
+
} catch (cause) {
|
|
12616
|
+
setError(cause instanceof Error ? cause.message : "Could not load workspace credentials.");
|
|
12617
|
+
} finally {
|
|
12618
|
+
setLoading(false);
|
|
12619
|
+
}
|
|
12620
|
+
}, [client]);
|
|
12621
|
+
useEffect25(() => {
|
|
12622
|
+
if (!isWorkspaceOwner) return;
|
|
12623
|
+
void load();
|
|
12624
|
+
}, [isWorkspaceOwner, load]);
|
|
12625
|
+
useEffect25(() => {
|
|
12626
|
+
if (!oauthFlow || oauthFlow.status !== "pending") return;
|
|
12627
|
+
const epoch = ++oauthRequestEpoch.current;
|
|
12628
|
+
const timer = window.setTimeout(() => {
|
|
12629
|
+
void client.getCodexLogin(oauthFlow.flowId).then((flow) => {
|
|
12630
|
+
if (oauthRequestEpoch.current !== epoch) return;
|
|
12631
|
+
setOauthFlow(flow);
|
|
12632
|
+
if (flow.status === "succeeded") {
|
|
12633
|
+
setSuccess("OpenAI Codex is connected for this workspace. New sessions can use it.");
|
|
12634
|
+
void load();
|
|
12635
|
+
} else if (flow.status === "failed") {
|
|
12636
|
+
setError("OpenAI Codex sign-in did not complete. Try again.");
|
|
12637
|
+
}
|
|
12638
|
+
}).catch(() => {
|
|
12639
|
+
if (oauthRequestEpoch.current === epoch) setError("Could not refresh OpenAI Codex sign-in.");
|
|
12640
|
+
});
|
|
12641
|
+
}, 750);
|
|
12642
|
+
return () => {
|
|
12643
|
+
window.clearTimeout(timer);
|
|
12644
|
+
if (oauthRequestEpoch.current === epoch) oauthRequestEpoch.current += 1;
|
|
12645
|
+
};
|
|
12646
|
+
}, [client, load, oauthFlow]);
|
|
12647
|
+
if (!isWorkspaceOwner) return null;
|
|
12648
|
+
const updateCredential = (next) => {
|
|
12649
|
+
setCredentials((current) => current.map((item) => item.providerId === next.providerId ? next : item));
|
|
12650
|
+
};
|
|
12651
|
+
const submitApiKey = async (event, credential) => {
|
|
12652
|
+
event.preventDefault();
|
|
12653
|
+
if (credential.providerId === "openai-codex" && oauthFlow?.status === "pending") return;
|
|
12654
|
+
const form = event.currentTarget;
|
|
12655
|
+
const apiKey = String(new FormData(form).get("api-key") ?? "");
|
|
12656
|
+
form.reset();
|
|
12657
|
+
if (!apiKey) {
|
|
12658
|
+
setError("Enter an API key before saving.");
|
|
12659
|
+
return;
|
|
12660
|
+
}
|
|
12661
|
+
setError(null);
|
|
12662
|
+
setSuccess(null);
|
|
12663
|
+
setBusyProvider(credential.providerId);
|
|
12664
|
+
try {
|
|
12665
|
+
updateCredential(await client.putApiKey(credential.providerId, apiKey));
|
|
12666
|
+
setSuccess(`${credential.displayName} API key saved. New sessions use this workspace credential.`);
|
|
12667
|
+
} catch (cause) {
|
|
12668
|
+
setError(cause instanceof Error ? cause.message : "Could not save the API key.");
|
|
12669
|
+
} finally {
|
|
12670
|
+
setBusyProvider(null);
|
|
12671
|
+
}
|
|
12672
|
+
};
|
|
12673
|
+
const lifecycle = async (credential, action) => {
|
|
12674
|
+
if (credential.providerId === "openai-codex" && oauthFlow?.status === "pending") return;
|
|
12675
|
+
setError(null);
|
|
12676
|
+
setSuccess(null);
|
|
12677
|
+
setBusyProvider(credential.providerId);
|
|
12678
|
+
try {
|
|
12679
|
+
updateCredential(await client[action](credential.providerId));
|
|
12680
|
+
setSuccess(`${credential.displayName} ${action === "delete" ? "deleted" : `${action}d`}.`);
|
|
12681
|
+
setConfirmDelete(null);
|
|
12682
|
+
} catch (cause) {
|
|
12683
|
+
setError(cause instanceof Error ? cause.message : "Credential operation failed.");
|
|
12684
|
+
} finally {
|
|
12685
|
+
setBusyProvider(null);
|
|
12686
|
+
}
|
|
12687
|
+
};
|
|
12688
|
+
const startCodex = async () => {
|
|
12689
|
+
oauthRequestEpoch.current += 1;
|
|
12690
|
+
setError(null);
|
|
12691
|
+
setSuccess(null);
|
|
12692
|
+
setBusyProvider("openai-codex");
|
|
12693
|
+
try {
|
|
12694
|
+
setOauthFlow(await client.startCodexLogin());
|
|
12695
|
+
} catch (cause) {
|
|
12696
|
+
setError(cause instanceof Error ? cause.message : "Could not start OpenAI Codex sign-in.");
|
|
12697
|
+
} finally {
|
|
12698
|
+
setBusyProvider(null);
|
|
12699
|
+
}
|
|
12700
|
+
};
|
|
12701
|
+
const respondToPrompt = async (event) => {
|
|
12702
|
+
event.preventDefault();
|
|
12703
|
+
if (!oauthFlow) return;
|
|
12704
|
+
const form = event.currentTarget;
|
|
12705
|
+
const value = String(new FormData(form).get("oauth-response") ?? "");
|
|
12706
|
+
form.reset();
|
|
12707
|
+
if (!value) return;
|
|
12708
|
+
try {
|
|
12709
|
+
setOauthFlow(await client.respondToCodexLogin(oauthFlow.flowId, value));
|
|
12710
|
+
} catch {
|
|
12711
|
+
setError("OpenAI Codex could not accept that response. Try again.");
|
|
12712
|
+
}
|
|
12713
|
+
};
|
|
12714
|
+
const cancelCodex = async () => {
|
|
12715
|
+
if (!oauthFlow) return;
|
|
12716
|
+
oauthRequestEpoch.current += 1;
|
|
12717
|
+
setBusyProvider("openai-codex");
|
|
12718
|
+
try {
|
|
12719
|
+
await client.cancelCodexLogin(oauthFlow.flowId);
|
|
12720
|
+
setOauthFlow(null);
|
|
12721
|
+
} catch {
|
|
12722
|
+
setError("Could not cancel OpenAI Codex sign-in. The flow is still pending.");
|
|
12723
|
+
setOauthFlow((flow) => flow ? { ...flow } : flow);
|
|
12724
|
+
} finally {
|
|
12725
|
+
setBusyProvider(null);
|
|
12726
|
+
}
|
|
12727
|
+
};
|
|
12728
|
+
return /* @__PURE__ */ jsx36(
|
|
12729
|
+
SettingsPanel,
|
|
12730
|
+
{
|
|
12731
|
+
id: "credentials",
|
|
12732
|
+
icon: /* @__PURE__ */ jsx36(KeyRound, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
12733
|
+
title: "AI provider credentials",
|
|
12734
|
+
description: "Choose how this workspace funds AI sessions. Keys and tokens are write-only and are never shown again.",
|
|
12735
|
+
children: /* @__PURE__ */ jsxs32("div", { className: "space-y-4", children: [
|
|
12736
|
+
error && /* @__PURE__ */ jsx36(Notice, { role: "alert", tone: "error", description: error }),
|
|
12737
|
+
success && /* @__PURE__ */ jsx36(Notice, { role: "status", tone: "success", description: success }),
|
|
12738
|
+
loading && /* @__PURE__ */ jsx36("p", { className: "text-[13px] text-muted-foreground", children: "Loading providers\u2026" }),
|
|
12739
|
+
!loading && credentials.length === 0 && !error && /* @__PURE__ */ jsx36("p", { className: "text-[13px] text-muted-foreground", children: "No credential providers are available in this deployment." }),
|
|
12740
|
+
/* @__PURE__ */ jsx36("div", { className: "divide-y divide-border/50 rounded-md border border-border/60", children: credentials.map((credential) => {
|
|
12741
|
+
const connected = credential.state === "active";
|
|
12742
|
+
const busy = busyProvider === credential.providerId;
|
|
12743
|
+
const isCodex = credential.providerId === "openai-codex";
|
|
12744
|
+
const codexPending = isCodex && oauthFlow?.status === "pending";
|
|
12745
|
+
return /* @__PURE__ */ jsxs32("section", { "aria-labelledby": `credential-${credential.providerId}`, className: "space-y-3 p-4", children: [
|
|
12746
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [
|
|
12747
|
+
/* @__PURE__ */ jsxs32("div", { children: [
|
|
12748
|
+
/* @__PURE__ */ jsx36("h3", { id: `credential-${credential.providerId}`, className: "text-[13px] font-medium text-foreground", children: credential.displayName }),
|
|
12749
|
+
/* @__PURE__ */ jsxs32("p", { className: "text-[12px] text-muted-foreground", children: [
|
|
12750
|
+
statusLabel(credential),
|
|
12751
|
+
credential.maskedLastFourSuffix ? ` \xB7 ends in ${credential.maskedLastFourSuffix}` : "",
|
|
12752
|
+
credential.credentialVersion ? ` \xB7 v${credential.credentialVersion}` : ""
|
|
12753
|
+
] }),
|
|
12754
|
+
updatedLabel(credential.updatedAt) && /* @__PURE__ */ jsxs32("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
12755
|
+
"Updated ",
|
|
12756
|
+
updatedLabel(credential.updatedAt)
|
|
12757
|
+
] })
|
|
12758
|
+
] }),
|
|
12759
|
+
connected && /* @__PURE__ */ jsx36("span", { className: "rounded-full bg-emerald-500/10 px-2 py-0.5 text-[11px] font-medium text-emerald-700 dark:text-emerald-300", children: "Workspace funded" })
|
|
12760
|
+
] }),
|
|
12761
|
+
!isCodex ? /* @__PURE__ */ jsxs32("form", { className: "flex flex-col gap-2 sm:flex-row sm:items-end", onSubmit: (event) => void submitApiKey(event, credential), children: [
|
|
12762
|
+
/* @__PURE__ */ jsxs32("div", { className: "min-w-0 flex-1 space-y-1.5", children: [
|
|
12763
|
+
/* @__PURE__ */ jsx36(Label, { htmlFor: `api-key-${credential.providerId}`, className: "text-[12px]", children: "API key" }),
|
|
12764
|
+
/* @__PURE__ */ jsx36(Input2, { id: `api-key-${credential.providerId}`, name: "api-key", type: "password", autoComplete: "new-password", className: "h-8 text-[13px]", placeholder: connected ? "Enter a replacement key" : "Paste API key" })
|
|
12765
|
+
] }),
|
|
12766
|
+
/* @__PURE__ */ jsx36(Button12, { type: "submit", size: "sm", disabled: busy || codexPending, children: busy ? "Saving\u2026" : connected ? "Replace key" : "Connect" })
|
|
12767
|
+
] }) : /* @__PURE__ */ jsxs32("div", { className: "space-y-1.5", children: [
|
|
12768
|
+
/* @__PURE__ */ jsx36("p", { className: "text-[12px] font-medium text-foreground", children: "Workspace funding method: OpenAI Codex sign-in" }),
|
|
12769
|
+
/* @__PURE__ */ jsx36(Button12, { type: "button", size: "sm", onClick: () => void startCodex(), disabled: busy || codexPending, children: oauthFlow?.status === "pending" ? "Sign-in pending\u2026" : connected ? "Sign in again" : "Sign in with OpenAI Codex" })
|
|
12770
|
+
] }),
|
|
12771
|
+
isCodex && oauthFlow && /* @__PURE__ */ jsxs32("div", { className: "space-y-3 rounded-md bg-muted/40 p-3", "aria-live": "polite", children: [
|
|
12772
|
+
/* @__PURE__ */ jsxs32("p", { className: "text-[12px] font-medium", children: [
|
|
12773
|
+
"OpenAI Codex sign-in: ",
|
|
12774
|
+
oauthFlow.status
|
|
12775
|
+
] }),
|
|
12776
|
+
oauthFlow.events.map((event, index) => event.type === "auth_url" ? /* @__PURE__ */ jsxs32("a", { href: event.url, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1 text-[12px] underline", children: [
|
|
12777
|
+
"Open authorization page ",
|
|
12778
|
+
/* @__PURE__ */ jsx36(ExternalLink, { className: "h-3 w-3", "aria-hidden": "true" })
|
|
12779
|
+
] }, `${event.type}-${index}`) : event.type === "device_code" ? /* @__PURE__ */ jsxs32("div", { className: "text-[12px]", children: [
|
|
12780
|
+
/* @__PURE__ */ jsxs32("p", { children: [
|
|
12781
|
+
"Enter code ",
|
|
12782
|
+
/* @__PURE__ */ jsx36("strong", { className: "font-mono", children: event.userCode })
|
|
12783
|
+
] }),
|
|
12784
|
+
/* @__PURE__ */ jsx36("a", { href: event.verificationUri, target: "_blank", rel: "noreferrer", className: "underline", children: "Open device verification" })
|
|
12785
|
+
] }, `${event.type}-${index}`) : null),
|
|
12786
|
+
oauthFlow.prompt?.options ? /* @__PURE__ */ jsx36("div", { className: "flex flex-wrap gap-2", children: oauthFlow.prompt.options.map((option) => /* @__PURE__ */ jsx36(Button12, { type: "button", size: "sm", variant: "outline", onClick: () => void client.respondToCodexLogin(oauthFlow.flowId, option.id).then(setOauthFlow), children: option.label }, option.id)) }) : oauthFlow.prompt ? /* @__PURE__ */ jsxs32("form", { onSubmit: (event) => void respondToPrompt(event), className: "flex gap-2", children: [
|
|
12787
|
+
/* @__PURE__ */ jsx36(Label, { htmlFor: "oauth-response", className: "sr-only", children: "Authorization response" }),
|
|
12788
|
+
/* @__PURE__ */ jsx36(Input2, { id: "oauth-response", name: "oauth-response", type: oauthFlow.prompt.type === "password" ? "password" : "text", autoComplete: "off", className: "h-8 text-[13px]", placeholder: "Paste the authorization response" }),
|
|
12789
|
+
/* @__PURE__ */ jsx36(Button12, { type: "submit", size: "sm", children: "Continue" })
|
|
12790
|
+
] }) : null,
|
|
12791
|
+
oauthFlow.status === "pending" && /* @__PURE__ */ jsx36(Button12, { type: "button", size: "sm", variant: "outline", disabled: busy, onClick: () => void cancelCodex(), children: "Cancel sign-in" })
|
|
12792
|
+
] }),
|
|
12793
|
+
credential.state !== "not_configured" && credential.state !== "intentionally_absent" && /* @__PURE__ */ jsxs32("div", { className: "flex flex-wrap gap-2 border-t border-border/40 pt-3", children: [
|
|
12794
|
+
/* @__PURE__ */ jsx36(Button12, { type: "button", size: "sm", variant: "outline", disabled: busy || codexPending || credential.state === "disabled", onClick: () => void lifecycle(credential, "disable"), children: "Disable" }),
|
|
12795
|
+
/* @__PURE__ */ jsx36(Button12, { type: "button", size: "sm", variant: "outline", disabled: busy || codexPending || credential.state === "revoked", onClick: () => void lifecycle(credential, "revoke"), children: "Revoke" }),
|
|
12796
|
+
confirmDelete === credential.providerId ? /* @__PURE__ */ jsxs32(Fragment14, { children: [
|
|
12797
|
+
/* @__PURE__ */ jsx36(Button12, { type: "button", size: "sm", variant: "destructive", disabled: busy || codexPending, onClick: () => void lifecycle(credential, "delete"), children: "Confirm delete" }),
|
|
12798
|
+
/* @__PURE__ */ jsx36(Button12, { type: "button", size: "sm", variant: "outline", onClick: () => setConfirmDelete(null), children: "Cancel" })
|
|
12799
|
+
] }) : /* @__PURE__ */ jsx36(Button12, { type: "button", size: "sm", variant: "destructive", disabled: busy || codexPending, onClick: () => setConfirmDelete(credential.providerId), children: "Delete" })
|
|
12800
|
+
] })
|
|
12801
|
+
] }, credential.providerId);
|
|
12802
|
+
}) }),
|
|
12803
|
+
/* @__PURE__ */ jsx36("p", { className: "text-[12px] leading-5 text-muted-foreground", children: "Changes apply to new sessions. Existing sessions keep the credential they started with." })
|
|
12804
|
+
] })
|
|
12805
|
+
}
|
|
12806
|
+
);
|
|
12807
|
+
}
|
|
12808
|
+
function CredentialSettingsSurface(props) {
|
|
12809
|
+
return /* @__PURE__ */ jsx36(CredentialSettingsSurfaceForWorkspace, { ...props }, props.workspaceId ?? "unscoped");
|
|
12810
|
+
}
|
|
12811
|
+
|
|
12353
12812
|
// src/front/commands.ts
|
|
12354
12813
|
var AGENT_COMMAND_SOURCE = "agent";
|
|
12355
12814
|
function getAgentCommands(options = {}) {
|
|
@@ -12400,6 +12859,7 @@ export {
|
|
|
12400
12859
|
Conversation,
|
|
12401
12860
|
ConversationContent,
|
|
12402
12861
|
ConversationScrollButton,
|
|
12862
|
+
CredentialSettingsSurface,
|
|
12403
12863
|
DebugDrawer,
|
|
12404
12864
|
GatewayResponseError,
|
|
12405
12865
|
Message,
|
|
@@ -12429,6 +12889,7 @@ export {
|
|
|
12429
12889
|
builtinCommands,
|
|
12430
12890
|
cn,
|
|
12431
12891
|
createCommandRegistry,
|
|
12892
|
+
createCredentialSettingsClient,
|
|
12432
12893
|
defaultChatSuggestions,
|
|
12433
12894
|
defaultToolRenderers,
|
|
12434
12895
|
errorResponseCode,
|