Repository navigation
Conversation
added 4 commits
October 21, 2026 21:16
pi's SDK rejects prompts during compaction ("Cannot submit a prompt while
compaction is in progress"), so every submission bounced back to the
composer with an error and the input read as dead. Park the submissions in
the wrapper and flush them FIFO at compaction end, as pi's TUI's
compactionQueuedMessages does. This is the compaction-queueing part of
agegr#812, the focused follow-up its closing comment asked for.
- Parking happens on the SDK's rejection, never before inner.prompt(), so
an extension command typed during compaction still runs: the SDK
executes commands before it refuses.
- A parked prompt keeps its admission's pendingPromptCount outstanding
until delivery or recall, so isRunning(), the idle shutdown and the
reload/clone refusals all see it.
- The flush is the TUI's flushCompactionQueue() in the wrapper: every
delivery carries a streamingBehavior (a plain parked prompt as followUp),
so AgentSession.prompt() queues it into a run still finishing the
compaction or starts one when idle, never "Agent is already processing".
A delivery that started the run emits its prompt_done, one that queued
into another run rides that run's, and a failed delivery goes back to the
queue with prompt_error and waits for Recall or the next compaction end.
- get_state.queuedMessages and every queue_update merge pi's queue with the
parked ones; clear_queue returns both, and Recall puts the text back in
the composer.
- The composer stays editable while compacting: the placeholder says
messages queue, and the status line shows "Compacting... - N queued",
for a manual compaction too (agentRunning alone left it unlabeled).
Tests: lib/rpc-manager-compaction-queue.test.mjs (park, FIFO flush,
recall, merge, failed flush, images), the queued-count case in
chat-phase-label, the editable-composer case in ChatInput. tsc, eslint and
the touched areas' 246 tests pass; the 66 suite failures on this Windows
machine are identical with and without the change.
…mpts out of the transcript
Two bugs from manual testing of the compaction queueing.
1. /compact disabled the composer for the whole compaction. The builtin
slash handler awaited the blocking compact POST, and runBuiltinCommand
holds the composer's <fieldset disabled> until the command resolves: the
input box took no text until the session was switched away and back (a
remount resets builtinCommandPending). The command now completes at once
and runs the compaction detached through handleCompact, the Compact
button's path, which reports itself (Compacting status, Stop compaction,
result banner, error); its result is the silent action: "compact".
handleCompact takes the optional custom instructions and promotes the
session after its reload, as the inline path did.
2. A parked prompt rendered as a sent message. handleSend's optimistic
bubble stayed in the transcript while the message waited in the queue.
A queued submission is now acked { queued: true } (parked, or the SDK's
"queued" disposition) and the client drops the bubble: the queue rows
show it, and the transcript shows it when the flush delivers it.
Tests: hooks/compact-command.test.mjs pins the detached /compact against a
compact POST that never resolves; lib/rpc-manager-compaction-queue.test.mjs
pins the queued ack (and the extension command still answering null).
tsc, eslint and the touched areas' 258 tests pass.
28 test files fixed, test and fixture code only (plus one fixture server).
No product change. Root causes, by cluster:
- 8.3 temp paths: %TEMP% resolves to C:\Users\LINWEI~1\... while git,
the SDK and $HOME report expanded names. Node's realpathSync keeps short
names (only realpathSync.native expands them), so fixtures compared
unequal paths and ancestor walks reached the real ~/.agents/skills from
the scratch dir. Fixture roots now canonicalize with realpathSync.native.
- CRLF checkouts: source-reading tests sliced on ";\n" and regexes ended
at \n before \r, so they extracted nothing. Repo convention
.replace(/\r\n/g, "\n") applied at the read sites.
- chmod on Windows: 0o500/0o400 do not make directories unwritable and
stat reports 0o666 regardless. Unwritable tests now lock the file too
(same refusal path, different syscall); mode assertions keep POSIX.
- ESM absolute paths: hooks fixtures imported a raw G:\... path, which
Node's ESM loader rejects (protocol 'g:'), and drive-letter-case drift
would load a second shim instance. They now take the shim through
jiti.import("react"), the alias route the hooks resolve react by.
- ConPTY: node-pty reports pid 0 until its worker connects and arms a 5s
watchdog; tests now wait for the connection before asserting or ticking
mock timers.
- EBUSY teardown: fixture MCP servers held cwd open while rm ran (Windows
refuses); shutdown is awaited first and removal retries. A fixture's
!touch marker wrote into the repo root (unquoted backslashes in bash -c);
markers are quoted with forward slashes. Grandchild processes of the
MCP fixture now exit with their parent.
- PATH assertions joined with the host delimiter for a case that tests
another platform; PTY and skills-walk tests reached the developer's real
home on Windows (temp under the profile): hermetic .git marker added.
npm test now passes end to end here (2880 tests, 2859 pass, 21 skipped,
0 fail) and exits without --test-force-exit; it used to hang on leaked
fixture processes and fail 66.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pi's SDK refuses prompts while compaction runs ("Cannot submit a prompt while
compaction is in progress"), so nothing typed into a compacting session could reach
the agent. The wrapper now parks those submissions and flushes them when the
compaction ends, as pi's TUI's
compactionQueuedMessagesdoes. This is thecompaction-queueing part of #812, the focused follow-up its closing comment asked for.
Shipped alongside, a pre-existing composer bug this feature cannot ship without: the
/compactbuiltin awaited its blocking compact POST whilerunBuiltinCommandholdsthe composer's
<fieldset disabled>, so the input box took no text for the wholecompaction and came back only after a remount.
inner.prompt(), so an extensioncommand typed during compaction still runs (the SDK executes commands before it refuses).
pendingPromptCountoutstanding until delivery orrecall, so
isRunning(), the idle shutdown and thereload/clonerefusals see it.streamingBehavior, soAgentSession.prompt()queues into arun still finishing the compaction or starts one when idle. It never hits
"Agent is already processing".
get_state.queuedMessagesand everyqueue_updatemerge pi's queue with the parkedones;
clear_queuereturns both, and Recall puts the text back in the composer.reads "Compacting… · N queued" for a manual compaction too, where
agentRunningaloneleft it unlabeled.
Evidence
Before: a prompt sent during compaction bounced with "Cannot submit a prompt while
compaction is in progress." and the text returned to the composer; with
/compactthecomposer was dead for the compaction's duration. Neither queued-message path worked.
After: the message is accepted, appears in the queued-messages panel, and is
delivered FIFO when the compaction ends.
New tests:
Plus the queued-count case in
lib/chat-phase-label.test.mjsand aneditable-while-compacting case in
components/ChatInput.test.mjs.Checks:
tsc --noEmitclean,npm run lintclean,npm testgreen end to end(2880 tests, 2859 pass, 21 skipped, 0 fail).
The branch also carries one separable commit,
test: run the suite green on Windows(28 test files, test and fixture code only): 8.3 temp-path canonicalization, CRLF
normalization at source reads, chmod semantics, ESM absolute-path imports in the hooks
fixtures, ConPTY pid timing, and EBUSY teardown of fixture MCP servers.
npm testused to fail 66 here and hang without
--test-force-exit; it now exits clean. Happy tosplit it into its own PR if you prefer.
Merge Danger
Door: Two-way. A revert restores today's behavior on both paths, and there is no
schema, session-file or extension-API surface change.
Blast Radius: Prompt dispatch while a compaction runs, and the
/compactbuiltincommand. Idle sessions, runs, steer/follow-up queueing outside compaction and other
slash commands are untouched.
the flush drops it. The queue panel and Recall are the user's recourse while it is
parked, and
isRunning()keeps the wrapper alive meanwhile.Recall, and reports
prompt_erroreither way.isPromptRunningreads true while a prompt is parked. Intended ("a prompt is pending"),but it is a new state the status polling can observe.
/compactno longer answers "Command completed"; the compaction's own status line,result banner and error banner report it. Anyone keying on that toast will notice.