@hachej/boring-agent 0.1.108 → 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.
Files changed (30) hide show
  1. package/dist/{errors-CEU4iujR.d.ts → admin-DORVQfLn.d.ts} +80 -3
  2. package/dist/{chunk-GS4ADE34.js → chunk-42D6KAQV.js} +36 -12
  3. package/dist/{chunk-6OANSUEI.js → chunk-72KT6VWV.js} +59 -2
  4. package/dist/{chunk-3JFPS2O6.js → chunk-KJIEMSQP.js} +10 -1
  5. package/dist/{chunk-MVKXLMH3.js → chunk-KN72XWP7.js} +1 -1
  6. package/dist/{chunk-KDXIXVF6.js → chunk-V3H5A3BR.js} +12410 -5047
  7. package/dist/core/index.d.ts +5 -5
  8. package/dist/core/index.js +2 -1
  9. package/dist/{createHarness-AHWSC36D.js → createHarness-DOLOHA62.js} +2 -2
  10. package/dist/file-search-BUgMSKZE.d.ts +289 -0
  11. package/dist/front/index.d.ts +89 -4
  12. package/dist/front/index.js +508 -47
  13. package/dist/front/styles.css +55 -55
  14. package/dist/{harness-Ct0e4RWc.d.ts → harness-qHlMJhZo.d.ts} +1 -1
  15. package/dist/{piChatEvent-BUWIzw5l.d.ts → piChatEvent-DpE0lIb9.d.ts} +49 -17
  16. package/dist/{piChatSessionService-nvroRAVk.d.ts → piChatSessionService-CBct-hmz.d.ts} +4 -1
  17. package/dist/server/agent-host/testing/gatewayConformance.d.ts +7 -6
  18. package/dist/server/agent-host/testing/gatewayConformance.js +14 -7
  19. package/dist/server/index.d.ts +890 -56
  20. package/dist/server/index.js +73 -7
  21. package/dist/server/worker/index.js +5 -5
  22. package/dist/{lease-nASWv2tU.d.ts → share-entry-CxS6UJ_L.d.ts} +77 -179
  23. package/dist/shared/index.d.ts +118 -222
  24. package/dist/shared/index.js +17 -5
  25. package/dist/{types-B7-PN5jV.d.ts → types-DcWoQMn8.d.ts} +346 -10
  26. package/docs/AGENT_GATEWAY_V0.md +22 -2
  27. package/docs/AGENT_PACKAGES.md +15 -5
  28. package/docs/ERROR_CODES.md +14 -0
  29. package/package.json +10 -18
  30. package/dist/file-search-BGUHnW9o.d.ts +0 -87
@@ -32,13 +32,13 @@ import {
32
32
  WORKSPACE_COMMAND_NOTIFY_EVENT,
33
33
  extractToolUiMetadata,
34
34
  sanitizeToolUiMetadata
35
- } from "../chunk-MVKXLMH3.js";
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-3JFPS2O6.js";
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, statusLabel)
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 applySequencedEvent(state, action.event);
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 applySequencedEvent(state, event) {
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
- ...state,
4054
- connection: { ...state.connection, state: "reconnecting" },
4055
- needsResync: { expectedSeq, actualSeq: event.seq, lastSeq: state.lastSeq }
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 next = reduceEvent({ ...state, lastSeq: event.seq, needsResync: void 0 }, event);
4059
- return next;
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
- if (dispatchOptions?.flush) {
4534
- if (scheduled !== void 0) {
4535
- cancelNotify(scheduled);
4536
- scheduled = void 0;
4537
- }
4538
- notifyNow();
4539
- return;
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.options.onEvent?.(frame);
5008
- this.store.dispatch({ type: "event", event: frame });
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 messageItems = buildMessageRenderItems(messages);
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 = messages.length === 0;
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
- messages.length === 0 && emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx25(ConversationHistoryLoadingState, {}) : null,
8620
- messages.length === 0 && !emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx25(
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
- isStreaming ? "mb-2 max-h-8 opacity-100" : "mb-0 max-h-0 opacity-0"
10612
+ visibleActivity ? "mb-2 max-h-8 opacity-100" : "mb-0 max-h-0 opacity-0"
10566
10613
  ),
10567
- "aria-hidden": !isStreaming,
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.resumeStream?.();
11018
- else session.suspendStream?.();
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(/* @__PURE__ */ new Set());
11414
- setResumeQueuedErrorsBySessionId(/* @__PURE__ */ new Map());
11415
- resumeQueuedInFlightRef.current.clear();
11416
- }, [chatScopeKey]);
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,