@hachej/boring-agent 0.1.57 → 0.1.59

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.
@@ -592,6 +592,7 @@ interface PiChatPanelProps<TComposerBlocker extends ComposerBlocker = ComposerBl
592
592
  createRemoteSession?: (options: RemotePiSessionOptions) => RemotePiSession;
593
593
  remoteSessionOptions?: UsePiSessionsOptions['remoteSessionOptions'];
594
594
  hydrateMessages?: boolean;
595
+ allowPromptDuringInitialHydration?: boolean;
595
596
  workspaceWarmupStatus?: ChatPanelWorkspaceWarmupStatus;
596
597
  onSessionReset?: () => void | Promise<void>;
597
598
  onBeforeSubmit?: (draft: string, context: ChatSubmitContext) => false | void | boolean | Promise<false | void | boolean>;
@@ -599,6 +600,10 @@ interface PiChatPanelProps<TComposerBlocker extends ComposerBlocker = ComposerBl
599
600
  onCommandResult?: (message: string) => void;
600
601
  onComposerWarning?: (message: string) => void;
601
602
  onMentionedFilesConsumed?: () => void;
603
+ onPromptSubmitStarted?: (context: {
604
+ sessionId: string;
605
+ clientNonce: string;
606
+ }) => void;
602
607
  onData?: (part: unknown) => void;
603
608
  onOpenArtifact?: (path: string) => void;
604
609
  composerBlockers?: TComposerBlocker[];
@@ -613,7 +618,7 @@ interface PiChatPanelProps<TComposerBlocker extends ComposerBlocker = ComposerBl
613
618
  * knowing what the code means or what the action does. */
614
619
  renderNoticeAction?: (notice: PiChatRuntimeNotice) => ReactNode;
615
620
  }
616
- declare function PiChatPanel<TComposerBlocker extends ComposerBlocker = ComposerBlocker>({ sessionId, extraCommands, apiBaseUrl, workspaceId, storageScope, requestHeaders, storage, fetch, className, chrome, debug, showSessions, hotReloadEnabled, suggestions, emptyState, emptyPlacement, composerPlaceholder, initialDraft, autoSubmitInitialDraft, onDraftRestored, onAutoSubmitInitialDraftAccepted, onAutoSubmitInitialDraftSettled, model, defaultModel, availableModels, hideDefaultModelOption, hideComposerSettings, suppressPreSubmitCancelledWarning, thinkingLevel, thinkingControl, serverResourcesEnabled, mentionedFiles, commands, excludeBuiltinCommands, toolRenderers, createRemoteSession, remoteSessionOptions, hydrateMessages, workspaceWarmupStatus, onSessionReset, onBeforeSubmit, onReloadAgentPlugins, onCommandResult, onComposerWarning, onMentionedFilesConsumed, onData, onOpenArtifact, composerBlockers, onComposerStop, onComposerBlockerAction, onTurnComplete, renderNoticeAction, }: PiChatPanelProps<TComposerBlocker>): react_jsx_runtime.JSX.Element;
621
+ declare function PiChatPanel<TComposerBlocker extends ComposerBlocker = ComposerBlocker>({ sessionId, extraCommands, apiBaseUrl, workspaceId, storageScope, requestHeaders, storage, fetch, className, chrome, debug, showSessions, hotReloadEnabled, suggestions, emptyState, emptyPlacement, composerPlaceholder, initialDraft, autoSubmitInitialDraft, onDraftRestored, onAutoSubmitInitialDraftAccepted, onAutoSubmitInitialDraftSettled, model, defaultModel, availableModels, hideDefaultModelOption, hideComposerSettings, suppressPreSubmitCancelledWarning, thinkingLevel, thinkingControl, serverResourcesEnabled, mentionedFiles, commands, excludeBuiltinCommands, toolRenderers, createRemoteSession, remoteSessionOptions, hydrateMessages, allowPromptDuringInitialHydration, workspaceWarmupStatus, onSessionReset, onBeforeSubmit, onReloadAgentPlugins, onCommandResult, onComposerWarning, onMentionedFilesConsumed, onPromptSubmitStarted, onData, onOpenArtifact, composerBlockers, onComposerStop, onComposerBlockerAction, onTurnComplete, renderNoticeAction, }: PiChatPanelProps<TComposerBlocker>): react_jsx_runtime.JSX.Element;
617
622
 
618
623
  interface DebugDrawerProps {
619
624
  apiBaseUrl?: string;
@@ -2309,7 +2309,10 @@ var PiFollowUpQueueController = class {
2309
2309
  return this.block("empty", "Enter a message before sending.");
2310
2310
  }
2311
2311
  const state = this.session.getState();
2312
- if (state.status === "hydrating") {
2312
+ if (state.status === "hydrating" && !(this.options.allowPromptDuringInitialHydration === true && canPromptDuringInitialHydration(state))) {
2313
+ return this.block("hydrating", "The agent session is still hydrating.");
2314
+ }
2315
+ if (state.status === "idle" && hasPendingOptimisticPromptInEmptySession(state)) {
2313
2316
  return this.block("hydrating", "The agent session is still hydrating.");
2314
2317
  }
2315
2318
  if (isPiChatBusy(state.status)) {
@@ -2379,6 +2382,12 @@ function createPiFollowUpQueueController(session, options) {
2379
2382
  function isPiChatBusy(status) {
2380
2383
  return status === "submitted" || status === "streaming" || status === "aborting";
2381
2384
  }
2385
+ function canPromptDuringInitialHydration(state) {
2386
+ return !state.hydrated && state.history.messageCount === 0 && state.committedMessages.length === 0 && state.queue.followUps.length === 0 && Object.keys(state.optimisticOutbox).length === 0 && !state.streamingMessage;
2387
+ }
2388
+ function hasPendingOptimisticPromptInEmptySession(state) {
2389
+ return state.hydrated && state.history.messageCount === 0 && state.committedMessages.length === 0 && Object.values(state.optimisticOutbox).some((message) => message.clientSeq === void 0);
2390
+ }
2382
2391
  function nextFollowUpClientSeq(state, floor = 1) {
2383
2392
  let maxSeq = floor - 1;
2384
2393
  for (const queued of state.queue.followUps) {
@@ -3776,13 +3785,15 @@ function hydrateFromSnapshot(state, snapshot, options = {}) {
3776
3785
  for (const followUp of queue.followUps) {
3777
3786
  if (followUp.clientNonce) serverNonces.add(followUp.clientNonce);
3778
3787
  }
3788
+ const preserveInitialOptimisticOutbox = shouldPreserveInitialOptimisticOutbox(state, snapshot, snapshotMessages);
3789
+ const preserveOutbox = preserveLocalHistory || preserveInitialOptimisticOutbox;
3779
3790
  const nextOutbox = {};
3780
- if (preserveLocalHistory) {
3791
+ if (preserveOutbox) {
3781
3792
  for (const message of Object.values(state.optimisticOutbox)) {
3782
3793
  if (!serverNonces.has(message.clientNonce)) nextOutbox[message.clientNonce] = message;
3783
3794
  }
3784
3795
  }
3785
- const staleOutbox = preserveLocalHistory ? [] : Object.values(state.optimisticOutbox).filter((message) => !serverNonces.has(message.clientNonce));
3796
+ const staleOutbox = preserveOutbox ? [] : Object.values(state.optimisticOutbox).filter((message) => !serverNonces.has(message.clientNonce));
3786
3797
  const notices = staleOutbox.length > 0 ? upsertNotice(state.notices, {
3787
3798
  id: "stale-outbox-cleared",
3788
3799
  level: "warning",
@@ -3809,6 +3820,9 @@ function hydrateFromSnapshot(state, snapshot, options = {}) {
3809
3820
  needsResync: void 0
3810
3821
  };
3811
3822
  }
3823
+ function shouldPreserveInitialOptimisticOutbox(state, snapshot, snapshotMessages) {
3824
+ return !state.hydrated && Object.keys(state.optimisticOutbox).length > 0 && snapshotMessages.length === 0 && snapshot.queue.followUps.length === 0;
3825
+ }
3812
3826
  function shouldPreserveLocalCommittedHistory(state, snapshot, snapshotMessages, options) {
3813
3827
  if (options.allowSeqRewind) return false;
3814
3828
  if (!state.hydrated) return false;
@@ -9455,6 +9469,7 @@ function PiChatPanel({
9455
9469
  createRemoteSession,
9456
9470
  remoteSessionOptions,
9457
9471
  hydrateMessages = true,
9472
+ allowPromptDuringInitialHydration = false,
9458
9473
  workspaceWarmupStatus,
9459
9474
  onSessionReset,
9460
9475
  onBeforeSubmit,
@@ -9462,6 +9477,7 @@ function PiChatPanel({
9462
9477
  onCommandResult,
9463
9478
  onComposerWarning,
9464
9479
  onMentionedFilesConsumed,
9480
+ onPromptSubmitStarted,
9465
9481
  onData,
9466
9482
  onOpenArtifact,
9467
9483
  composerBlockers = EMPTY_BLOCKERS,
@@ -9920,6 +9936,7 @@ function PiChatPanel({
9920
9936
  mentionedFiles: effectiveMentionedFiles,
9921
9937
  getDraft: () => draftRef.current,
9922
9938
  onDraftChange: setComposerDraft,
9939
+ allowPromptDuringInitialHydration,
9923
9940
  onPromptSubmitStarted: () => {
9924
9941
  markLocalSubmitted(activeChatSessionId);
9925
9942
  },
@@ -9941,7 +9958,7 @@ function PiChatPanel({
9941
9958
  onMentionedFilesConsumed?.();
9942
9959
  }
9943
9960
  });
9944
- }, [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]);
9961
+ }, [activeChatSessionId, addLocalNotice, allowPromptDuringInitialHydration, clearMentionedFiles, composerBlocked, composerBlockerLabel, effectiveMentionedFiles, markLocalSubmitted, onBeforeSubmit, onCommandResult, onComposerWarning, onMentionedFilesConsumed, onPromptSubmitStarted, openModelPicker, openThinkingPicker, registry, reloadAgentPlugins, resetSession, runPluginUpdate, selectComposerModel, selectComposerThinking, selectedModel, selectedPiSession, selectedThinking, setComposerDraft, submitThinkingControl, suppressPreSubmitCancelledWarning]);
9945
9962
  const surfaceRunRejected = useCallback18((error) => {
9946
9963
  const errorCode = piChatErrorCode(error);
9947
9964
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
@@ -9974,6 +9991,7 @@ function PiChatPanel({
9974
9991
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
9975
9992
  }
9976
9993
  if (result.type === "prompt" && activeChatSessionId) {
9994
+ onPromptSubmitStarted?.({ sessionId: activeChatSessionId, clientNonce: result.clientNonce });
9977
9995
  if (shouldHoldLocalSubmitted(selectedPiSession, result.cursor)) markLocalSubmitted(activeChatSessionId);
9978
9996
  else clearLocalSubmitted(activeChatSessionId);
9979
9997
  }
@@ -9984,7 +10002,7 @@ function PiChatPanel({
9984
10002
  surfaceRunRejected(error);
9985
10003
  return false;
9986
10004
  }
9987
- }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, markLocalSubmitted, policy, selectedPiSession, setComposerDraft, surfaceRunRejected]);
10005
+ }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, markLocalSubmitted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, surfaceRunRejected]);
9988
10006
  const editQueued = useCallback18(() => {
9989
10007
  if (!policy) return;
9990
10008
  void policy.editQueued().then((result) => {
@@ -10061,6 +10079,7 @@ function PiChatPanel({
10061
10079
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
10062
10080
  }
10063
10081
  if (result.type === "prompt") {
10082
+ onPromptSubmitStarted?.({ sessionId: activeSessionId, clientNonce: result.clientNonce });
10064
10083
  if (shouldHoldLocalSubmitted(selectedPiSession, result.cursor)) markLocalSubmitted(activeSessionId);
10065
10084
  else clearLocalSubmitted(activeSessionId);
10066
10085
  }
@@ -10075,15 +10094,18 @@ function PiChatPanel({
10075
10094
  settlePendingAutoSubmit(activeSessionId);
10076
10095
  surfaceRunRejected(error);
10077
10096
  });
10078
- }, [activeSessionId, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, dropLocalNotice, initialDraft, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit, surfaceRunRejected]);
10097
+ }, [activeSessionId, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, dropLocalNotice, initialDraft, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit, surfaceRunRejected]);
10079
10098
  useEffect21(() => {
10080
10099
  if (workspaceWarmupStatus?.status === "ready") {
10081
10100
  clearLocalNotice("workspace-warmup");
10082
10101
  }
10083
10102
  }, [clearLocalNotice, workspaceWarmupStatus?.status]);
10103
+ const initialHydrationPromptAllowed = Boolean(
10104
+ allowPromptDuringInitialHydration && selectedChatState && selectedChatState.status === "hydrating" && !selectedChatState.hydrated && selectedChatState.history.messageCount === 0 && selectedChatState.committedMessages.length === 0 && selectedChatState.queue.followUps.length === 0 && Object.keys(selectedChatState.optimisticOutbox).length === 0 && !selectedChatState.streamingMessage
10105
+ );
10084
10106
  const disabled = !policy || sessionsLoading || composerBlocked;
10085
10107
  const isStreaming = isPiBusyStatus(status);
10086
- const submitStatus = toPromptSubmitStatus(status);
10108
+ const submitStatus = initialHydrationPromptAllowed ? "ready" : toPromptSubmitStatus(status);
10087
10109
  const submitDisabled = !policy || sessionsLoading || composerBlocked && !isStreaming;
10088
10110
  const mergedToolRenderers = useMemo13(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
10089
10111
  const debugMessages = useMemo13(() => messages.map(toDebugUiMessage), [messages]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-agent",
3
- "version": "0.1.57",
3
+ "version": "0.1.59",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Pane-embeddable coding agent. Ships direct/local/vercel-sandbox execution modes behind one interface.",
@@ -74,7 +74,7 @@
74
74
  "use-stick-to-bottom": "^1.1.3",
75
75
  "yaml": "^2.8.3",
76
76
  "zod": "^3.25.76",
77
- "@hachej/boring-ui-kit": "0.1.57"
77
+ "@hachej/boring-ui-kit": "0.1.59"
78
78
  },
79
79
  "devDependencies": {
80
80
  "@antithesishq/bombadil": "0.5.0",