| ... | @@ -5881,15 +5881,13 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -5881,15 +5881,13 @@ export default function ChatView(props: ChatViewProps) { |
| 5881 | pendingApprovals.length > 0 || | 5881 | pendingApprovals.length > 0 || |
| 5882 | pendingUserInputs.length > 0 || | 5882 | pendingUserInputs.length > 0 || |
| 5883 | showPlanFollowUpPrompt; | 5883 | showPlanFollowUpPrompt; |
| 5884 | const compactDisabled = compactThreadUnavailable || composerHasUnsentContent; | 5884 | const compactDisabled = compactThreadUnavailable; |
| 5885 | const compactDisabledReason = compactDisabled | 5885 | const compactDisabledReason = compactDisabled |
| 5886 | ? composerHasUnsentContent | 5886 | ? !activeProject |
| 5887 | ? "Send or clear your draft before compacting" | 5887 | ? "Choose a project before compacting" |
| 5888 | : !activeProject | 5888 | : !manualCompactionProviderAvailable |
| 5889 | ? "Choose a project before compacting" | 5889 | ? "Compaction is unavailable for this provider" |
| 5890 | : !manualCompactionProviderAvailable | 5890 | : "Compacting is unavailable right now" |
| 5891 | ? "Compaction is unavailable for this provider" | | |
| 5892 | : "Compacting is unavailable right now" | | |
| 5893 | : null; | 5891 | : null; |
| 5894 | const resumeCompactionBannerItem = useMemo<ComposerBannerStackItem | null>(() => { | 5892 | const resumeCompactionBannerItem = useMemo<ComposerBannerStackItem | null>(() => { |
| 5895 | if ( | 5893 | if ( |
| ... | @@ -6479,6 +6477,78 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -6479,6 +6477,78 @@ export default function ChatView(props: ChatViewProps) { |
| 6479 | ], | 6477 | ], |
| 6480 | ); | 6478 | ); |
| 6481 | | 6479 | |
| | 6480 | const onCompactContext = async () => { |
| | 6481 | if (compactDisabled || !activeThread || !clientSettingsHydrated || sendInFlightRef.current) { |
| | 6482 | return; |
| | 6483 | } |
| | 6484 | const context = composerRef.current?.getSendContext(); |
| | 6485 | if (!context?.providerAvailable) return; |
| | 6486 | |
| | 6487 | // Compaction is a standalone command; the draft and its attachments stay local. |
| | 6488 | const threadId = activeThread.id; |
| | 6489 | const messageId = newMessageId(); |
| | 6490 | const createdAt = new Date().toISOString(); |
| | 6491 | sendInFlightRef.current = true; |
| | 6492 | beginLocalDispatch(); |
| | 6493 | setThreadError(threadId, null); |
| | 6494 | setOptimisticUserMessages((messages) => [ |
| | 6495 | ...messages, |
| | 6496 | { |
| | 6497 | id: messageId, |
| | 6498 | role: "user", |
| | 6499 | text: "/compact", |
| | 6500 | turnId: null, |
| | 6501 | createdAt, |
| | 6502 | updatedAt: createdAt, |
| | 6503 | streaming: false, |
| | 6504 | }, |
| | 6505 | ]); |
| | 6506 | scrollToEnd(); |
| | 6507 | try { |
| | 6508 | const settingsResult = await persistThreadSettingsForNextTurn({ |
| | 6509 | threadId, |
| | 6510 | createdAt, |
| | 6511 | modelSelection: context.selectedModelSelection, |
| | 6512 | ...(localCheckoutBranchMismatch |
| | 6513 | ? { branch: localCheckoutBranchMismatch.currentBranch } |
| | 6514 | : {}), |
| | 6515 | runtimeMode, |
| | 6516 | interactionMode: context.interactionMode, |
| | 6517 | }); |
| | 6518 | const result = |
| | 6519 | settingsResult._tag === "Failure" |
| | 6520 | ? settingsResult |
| | 6521 | : await startThreadTurn({ |
| | 6522 | environmentId, |
| | 6523 | input: { |
| | 6524 | threadId, |
| | 6525 | message: { messageId, role: "user", text: "/compact", attachments: [] }, |
| | 6526 | modelSelection: context.selectedModelSelection, |
| | 6527 | runtimeMode, |
| | 6528 | interactionMode: context.interactionMode, |
| | 6529 | createdAt, |
| | 6530 | }, |
| | 6531 | }); |
| | 6532 | if (result._tag === "Failure") { |
| | 6533 | setOptimisticUserMessages((messages) => |
| | 6534 | messages.filter((message) => message.id !== messageId), |
| | 6535 | ); |
| | 6536 | resetLocalDispatch(); |
| | 6537 | if (!isAtomCommandInterrupted(result)) { |
| | 6538 | const error = squashAtomCommandFailure(result); |
| | 6539 | setThreadError( |
| | 6540 | threadId, |
| | 6541 | error instanceof Error ? error.message : "Failed to compact context.", |
| | 6542 | ); |
| | 6543 | } |
| | 6544 | } else { |
| | 6545 | clearUsageLimitsFor(routeThreadKey); |
| | 6546 | } |
| | 6547 | } finally { |
| | 6548 | sendInFlightRef.current = false; |
| | 6549 | } |
| | 6550 | }; |
| | 6551 | |
| 6482 | const onSend = async ( | 6552 | const onSend = async ( |
| 6483 | e?: { preventDefault: () => void }, | 6553 | e?: { preventDefault: () => void }, |
| 6484 | submissionIntent: ComposerSubmissionIntent = "foreground", | 6554 | submissionIntent: ComposerSubmissionIntent = "foreground", |
| ... | @@ -8575,6 +8645,7 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -8575,6 +8645,7 @@ export default function ChatView(props: ChatViewProps) { |
| 8575 | onPageScrollKeyDown={onComposerPageScrollKeyDown} | 8645 | onPageScrollKeyDown={onComposerPageScrollKeyDown} |
| 8576 | onPageScrollKeyUp={onComposerPageScrollKeyUp} | 8646 | onPageScrollKeyUp={onComposerPageScrollKeyUp} |
| 8577 | onPageScrollRelease={onComposerPageScrollRelease} | 8647 | onPageScrollRelease={onComposerPageScrollRelease} |
| | 8648 | onCompactContext={onCompactContext} |
| 8578 | onSend={onSend} | 8649 | onSend={onSend} |
| 8579 | onInterrupt={onInterrupt} | 8650 | onInterrupt={onInterrupt} |
| 8580 | onImplementPlanInNewThread={onImplementPlanInNewThread} | 8651 | onImplementPlanInNewThread={onImplementPlanInNewThread} |