mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-12 21:02:17 +02:00
fix codex unresponsive
This commit is contained in:
parent
f2b5c6b1ab
commit
e6ff631191
8 changed files with 135 additions and 10 deletions
|
|
@ -103,7 +103,8 @@ export function CodeChat({
|
|||
onOpenDiff: (path: string) => void
|
||||
}) {
|
||||
const {
|
||||
items, liveText, isProcessing, pendingPermission, pendingToolPermissions, pendingAskHumans,
|
||||
items, liveText, isProcessing, compactionStatus, contextUsage,
|
||||
pendingPermission, pendingToolPermissions, pendingAskHumans,
|
||||
loading, send, stop, resolvePermission, respondToToolPermission, respondToAskHuman,
|
||||
} = useCodeChat(session)
|
||||
const [draft, setDraft] = useState('')
|
||||
|
|
@ -111,6 +112,9 @@ export function CodeChat({
|
|||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
const busy = isProcessing || status === 'working' || status === 'needs-you'
|
||||
const contextUsedPercent = contextUsage
|
||||
? Math.min(100, Math.round((contextUsage.used / contextUsage.size) * 100))
|
||||
: null
|
||||
// Attached file PATHS — like dragging a file into the Claude Code CLI, the
|
||||
// agent receives paths and reads the files itself with its own tools.
|
||||
const [attachments, setAttachments] = useState<string[]>([])
|
||||
|
|
@ -188,7 +192,10 @@ export function CodeChat({
|
|||
<Terminal className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-sm font-medium">{session.title}</div>
|
||||
<div className="text-[11px] text-muted-foreground">{AGENT_LABEL[session.agent]} — direct</div>
|
||||
<div className="text-[11px] text-muted-foreground">
|
||||
{AGENT_LABEL[session.agent]} — direct
|
||||
{contextUsedPercent != null ? ` · ${contextUsedPercent}% context used` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -239,9 +246,19 @@ export function CodeChat({
|
|||
/>
|
||||
))}
|
||||
{busy && !pendingPermission && pendingToolPermissions.size === 0 && pendingAskHumans.size === 0 && (
|
||||
<Shimmer className="text-sm">
|
||||
{stopping ? 'Stopping…' : `${AGENT_LABEL[session.agent]} is working…`}
|
||||
</Shimmer>
|
||||
compactionStatus === 'stalled' ? (
|
||||
<div className="text-sm text-amber-600">
|
||||
Context compaction is taking longer than expected. You can stop and retry in a fresh session.
|
||||
</div>
|
||||
) : (
|
||||
<Shimmer className="text-sm">
|
||||
{stopping
|
||||
? 'Stopping…'
|
||||
: compactionStatus === 'running'
|
||||
? 'Compacting context…'
|
||||
: `${AGENT_LABEL[session.agent]} is working…`}
|
||||
</Shimmer>
|
||||
)
|
||||
)}
|
||||
</ConversationContent>
|
||||
<ConversationScrollButton />
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ export interface PendingCodePermission {
|
|||
|
||||
const DIRECT_PREFIX = 'direct-'
|
||||
const STRUCTURAL_EVENTS = new Set(['tool_call', 'tool_call_update', 'plan', 'permission'])
|
||||
const COMPACTION_TITLE = 'Compacting context'
|
||||
const COMPACTION_STALLED_MS = 90_000
|
||||
|
||||
export type CompactionStatus = 'idle' | 'running' | 'stalled'
|
||||
|
||||
function messageText(content: unknown): string {
|
||||
if (typeof content === 'string') return content
|
||||
|
|
@ -62,6 +66,8 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
const [items, setItems] = useState<CodeChatItem[]>([])
|
||||
const [liveText, setLiveText] = useState('')
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
const [compactionStatus, setCompactionStatus] = useState<CompactionStatus>('idle')
|
||||
const [contextUsage, setContextUsage] = useState<{ used: number; size: number } | null>(null)
|
||||
const [pendingPermission, setPendingPermission] = useState<PendingCodePermission | null>(null)
|
||||
// Rowboat-mode copilot gates, same as the main chat: pre-tool-call permission
|
||||
// requests and ask-human questions. Keyed by toolCallId.
|
||||
|
|
@ -69,6 +75,7 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
const [pendingAskHumans, setPendingAskHumans] = useState<Map<string, z.infer<typeof AskHumanRequestEvent>>>(new Map())
|
||||
const [loading, setLoading] = useState(false)
|
||||
const seenMessageIdsRef = useRef<Set<string>>(new Set())
|
||||
const compactionToolIdRef = useRef<string | null>(null)
|
||||
|
||||
const applyCodeRunEvent = useCallback((toolCallId: string, event: CodeRunEvent) => {
|
||||
if (toolCallId.startsWith(DIRECT_PREFIX)) {
|
||||
|
|
@ -99,6 +106,9 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
if (!sessionId) {
|
||||
setItems([])
|
||||
setLiveText('')
|
||||
setCompactionStatus('idle')
|
||||
setContextUsage(null)
|
||||
compactionToolIdRef.current = null
|
||||
setPendingPermission(null)
|
||||
return
|
||||
}
|
||||
|
|
@ -106,6 +116,9 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
setLoading(true)
|
||||
setItems([])
|
||||
setLiveText('')
|
||||
setCompactionStatus('idle')
|
||||
setContextUsage(null)
|
||||
compactionToolIdRef.current = null
|
||||
setPendingPermission(null)
|
||||
setPendingToolPermissions(new Map())
|
||||
setPendingAskHumans(new Map())
|
||||
|
|
@ -217,6 +230,12 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
return () => { cancelled = true }
|
||||
}, [sessionId])
|
||||
|
||||
useEffect(() => {
|
||||
if (compactionStatus !== 'running') return
|
||||
const timer = window.setTimeout(() => setCompactionStatus('stalled'), COMPACTION_STALLED_MS)
|
||||
return () => window.clearTimeout(timer)
|
||||
}, [compactionStatus])
|
||||
|
||||
// Live event stream.
|
||||
useEffect(() => {
|
||||
if (!sessionId) return
|
||||
|
|
@ -230,6 +249,8 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
break
|
||||
case 'run-processing-end':
|
||||
setIsProcessing(false)
|
||||
setCompactionStatus('idle')
|
||||
compactionToolIdRef.current = null
|
||||
setPendingPermission(null)
|
||||
// Anything still streaming that never landed as a message (e.g. the
|
||||
// turn errored) is flushed so the text isn't lost.
|
||||
|
|
@ -247,6 +268,8 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
break
|
||||
case 'run-stopped':
|
||||
setIsProcessing(false)
|
||||
setCompactionStatus('idle')
|
||||
compactionToolIdRef.current = null
|
||||
setPendingPermission(null)
|
||||
setPendingToolPermissions(new Map())
|
||||
setPendingAskHumans(new Map())
|
||||
|
|
@ -348,6 +371,19 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
break
|
||||
case 'code-run-event': {
|
||||
setIsProcessing(true)
|
||||
if (event.event.type === 'usage') {
|
||||
setContextUsage({ used: event.event.used, size: event.event.size })
|
||||
}
|
||||
if (event.event.type === 'tool_call' && event.event.title === COMPACTION_TITLE) {
|
||||
compactionToolIdRef.current = event.event.id ?? null
|
||||
setCompactionStatus('running')
|
||||
}
|
||||
if (event.event.type === 'tool_call_update'
|
||||
&& event.event.id != null
|
||||
&& event.event.id === compactionToolIdRef.current) {
|
||||
compactionToolIdRef.current = null
|
||||
setCompactionStatus('idle')
|
||||
}
|
||||
if (event.event.type === 'message' && event.event.role === 'agent' && event.toolCallId.startsWith(DIRECT_PREFIX)) {
|
||||
const text = event.event.text
|
||||
setLiveText((prev) => prev + text)
|
||||
|
|
@ -460,6 +496,8 @@ export function useCodeChat(session: CodeSession | null) {
|
|||
items,
|
||||
liveText,
|
||||
isProcessing,
|
||||
compactionStatus,
|
||||
contextUsage,
|
||||
pendingPermission,
|
||||
pendingToolPermissions,
|
||||
pendingAskHumans,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
Pencil,
|
||||
Search,
|
||||
ShieldQuestion,
|
||||
Sparkles,
|
||||
Terminal,
|
||||
Trash2,
|
||||
Wrench,
|
||||
|
|
@ -87,7 +88,8 @@ export function reduceEvents(events: CodeRunEvent[]): Row[] {
|
|||
return rows
|
||||
}
|
||||
|
||||
function toolKindIcon(kind?: string) {
|
||||
function toolKindIcon(kind?: string, title?: string) {
|
||||
if (title === 'Compacting context') return <Sparkles className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
switch (kind) {
|
||||
case 'read': return <Eye className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
case 'edit': return <Pencil className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
|
|
@ -137,7 +139,7 @@ export function CodingRunTimeline({
|
|||
{running
|
||||
? <Loader className="size-3.5 shrink-0 animate-spin text-muted-foreground" />
|
||||
: <CheckCircle2 className="size-3.5 shrink-0 text-green-600" />}
|
||||
{toolKindIcon(row.toolKind)}
|
||||
{toolKindIcon(row.toolKind, row.title)}
|
||||
<span className="truncate text-foreground/90">{row.title ?? row.toolKind ?? 'Tool call'}</span>
|
||||
</div>
|
||||
{row.diffs.length > 0 && (
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ function toEvent(update: SessionUpdate): CodeRunEvent {
|
|||
priority: e.priority ?? undefined,
|
||||
})),
|
||||
};
|
||||
case 'usage_update':
|
||||
return { type: 'usage', used: update.used, size: update.size };
|
||||
default:
|
||||
return { type: 'other', sessionUpdate: update.sessionUpdate };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,11 @@ export const CodeRunEvent = z.discriminatedUnion("type", [
|
|||
priority: z.string().optional(),
|
||||
})),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("usage"),
|
||||
used: z.number().nonnegative(),
|
||||
size: z.number().positive(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("permission"),
|
||||
ask: PermissionAsk,
|
||||
|
|
|
|||
57
apps/x/patches/@agentclientprotocol__codex-acp@0.0.44.patch
Normal file
57
apps/x/patches/@agentclientprotocol__codex-acp@0.0.44.patch
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
diff --git a/dist/index.js b/dist/index.js
|
||||
--- a/dist/index.js
|
||||
+++ b/dist/index.js
|
||||
@@ -17678,15 +17678,22 @@
|
||||
case "dynamicToolCall":
|
||||
return await createDynamicToolCallUpdate(event.item);
|
||||
+ case "contextCompaction":
|
||||
+ return {
|
||||
+ sessionUpdate: "tool_call",
|
||||
+ toolCallId: event.item.id,
|
||||
+ kind: "other",
|
||||
+ title: "Compacting context",
|
||||
+ status: "in_progress"
|
||||
+ };
|
||||
case "collabAgentToolCall":
|
||||
case "userMessage":
|
||||
case "hookPrompt":
|
||||
case "agentMessage":
|
||||
case "reasoning":
|
||||
case "webSearch":
|
||||
case "imageView":
|
||||
case "imageGeneration":
|
||||
case "enteredReviewMode":
|
||||
case "exitedReviewMode":
|
||||
- case "contextCompaction":
|
||||
case "plan":
|
||||
return null;
|
||||
@@ -17713,23 +17720,28 @@
|
||||
return this.completeCommandExecutionEvent(event.item);
|
||||
case "reasoning":
|
||||
const summary = event.item.summary[0];
|
||||
if (!summary) return null;
|
||||
return {
|
||||
sessionUpdate: "agent_thought_chunk",
|
||||
content: {
|
||||
type: "text",
|
||||
text: summary
|
||||
}
|
||||
};
|
||||
+ case "contextCompaction":
|
||||
+ return {
|
||||
+ sessionUpdate: "tool_call_update",
|
||||
+ toolCallId: event.item.id,
|
||||
+ status: "completed"
|
||||
+ };
|
||||
case "collabAgentToolCall":
|
||||
case "userMessage":
|
||||
case "hookPrompt":
|
||||
case "agentMessage":
|
||||
case "webSearch":
|
||||
case "imageView":
|
||||
case "imageGeneration":
|
||||
case "enteredReviewMode":
|
||||
case "exitedReviewMode":
|
||||
- case "contextCompaction":
|
||||
case "plan":
|
||||
return null;
|
||||
9
apps/x/pnpm-lock.yaml
generated
9
apps/x/pnpm-lock.yaml
generated
|
|
@ -11,6 +11,9 @@ catalogs:
|
|||
version: 4.1.7
|
||||
|
||||
patchedDependencies:
|
||||
'@agentclientprotocol/codex-acp@0.0.44':
|
||||
hash: 0079b1ab3870451b47fe5ea5ecaca697e17139cff50fd2ee9c296b172aba525e
|
||||
path: patches/@agentclientprotocol__codex-acp@0.0.44.patch
|
||||
'@openai/codex@0.128.0':
|
||||
hash: 9edd926108a95aaa788aa93870fd6b16d70eeccdf5740b503af5d34cc9f25e86
|
||||
path: patches/@openai__codex@0.128.0.patch
|
||||
|
|
@ -57,7 +60,7 @@ importers:
|
|||
version: 0.39.0(@anthropic-ai/sdk@0.100.1(zod@4.2.1))(@modelcontextprotocol/sdk@1.25.1(hono@4.11.3)(zod@4.2.1))
|
||||
'@agentclientprotocol/codex-acp':
|
||||
specifier: ^0.0.44
|
||||
version: 0.0.44(zod@4.2.1)
|
||||
version: 0.0.44(patch_hash=0079b1ab3870451b47fe5ea5ecaca697e17139cff50fd2ee9c296b172aba525e)(zod@4.2.1)
|
||||
'@x/core':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core
|
||||
|
|
@ -414,7 +417,7 @@ importers:
|
|||
version: 0.39.0(@anthropic-ai/sdk@0.100.1(zod@4.2.1))(@modelcontextprotocol/sdk@1.25.1(hono@4.11.3)(zod@4.2.1))
|
||||
'@agentclientprotocol/codex-acp':
|
||||
specifier: ^0.0.44
|
||||
version: 0.0.44(zod@4.2.1)
|
||||
version: 0.0.44(patch_hash=0079b1ab3870451b47fe5ea5ecaca697e17139cff50fd2ee9c296b172aba525e)(zod@4.2.1)
|
||||
'@agentclientprotocol/sdk':
|
||||
specifier: ^0.22.1
|
||||
version: 0.22.1(zod@4.2.1)
|
||||
|
|
@ -8512,7 +8515,7 @@ snapshots:
|
|||
- '@anthropic-ai/sdk'
|
||||
- '@modelcontextprotocol/sdk'
|
||||
|
||||
'@agentclientprotocol/codex-acp@0.0.44(zod@4.2.1)':
|
||||
'@agentclientprotocol/codex-acp@0.0.44(patch_hash=0079b1ab3870451b47fe5ea5ecaca697e17139cff50fd2ee9c296b172aba525e)(zod@4.2.1)':
|
||||
dependencies:
|
||||
'@agentclientprotocol/sdk': 0.21.1(zod@4.2.1)
|
||||
'@openai/codex': 0.128.0(patch_hash=9edd926108a95aaa788aa93870fd6b16d70eeccdf5740b503af5d34cc9f25e86)
|
||||
|
|
|
|||
|
|
@ -24,4 +24,5 @@ onlyBuiltDependencies:
|
|||
- macos-alias
|
||||
- protobufjs
|
||||
patchedDependencies:
|
||||
'@agentclientprotocol/codex-acp@0.0.44': patches/@agentclientprotocol__codex-acp@0.0.44.patch
|
||||
'@openai/codex@0.128.0': patches/@openai__codex@0.128.0.patch
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue