Repository navigation
feat: let the agent start workspaces and check their status — #934 on the global-search stack - #941
danyaberezun wants to merge 10 commits into
Conversation
…ver the spawn order
…so a model-less chat reports none chats.ts kept a private copy of the chat start and Continue in workspace an inline one; both reported chat_started with the created session's model, so a chat without a project or a continued chat created without any usable model bucketed pi's placeholder as custom instead of none. Both now start through #934's host/chatSession.ts, the host's only createSession call.
…space_status The workspace tools took every open registry entry as a project, so a chat without a project defaulted to the Chats container: start_workspace failed on listBranches with a git error, every error listed Chats as an open project, and workspace_status reported the container's chats with git lines. The tools now see open repositories only, and a chat without a project must pass `project` — both tools refuse without it and list the open repositories.
…ce that loads an unknown project first addWorkspace drops a workspace.created push for a project whose list was never loaded, so a spawn into a collapsed project opened a record the store did not hold. Continue in workspace already reloaded the list in its own openContinuation. One openWorkspace (chat/openWorkspace.ts) now serves the start_workspace card, Continue's Open and the handoff's source: a known workspace opens under its own project, an unknown one reloads its project's workspace.list first and opens even if that fails.
6faaf21 to
5ebcb63
Compare
| .request("workspace.list", { projectId }) | ||
| .then((rows) => useAppStore.getState().setWorkspaces(projectId, rows)) | ||
| .catch(() => {}) | ||
| .finally(() => activate(projectId)); |
There was a problem hiding this comment.
Non-blocking:
Problem: An unknown workspace is activated unconditionally after loading, allowing an older Open action to override newer navigation.
Failure scenario: Open the new start_workspace card for a project whose workspace list is unloaded, then select another project or chat while workspace.list is pending. When the request settles, this callback switches back to the earlier workspace. This reproduces with the real store.
Suggested fix: Capture a navigation/request stamp before loading and activate only while that intent is still current, invalidating it on later navigation or another Open action.
| promptSnippet: "Start a new workspace with its own chat to work on a task in parallel", | ||
| promptGuidelines: START_GUIDELINES, | ||
| parameters: StartWorkspaceSchema, | ||
| async execute(_toolCallId, params, _signal, _onUpdate, ctx) { |
There was a problem hiding this comment.
Non-blocking:
Problem: start_workspace discards Pi's abort signal, so cancelling preparation does not cancel the pending launch.
Failure scenario: Press Stop while the default-branch prefetch awaits a slow remote. With pinned Pi 1.4.0, Stop waits for this tool; after the fetch finishes, it still creates the workspace and prompts its child, although Stop preceded both operations.
Suggested fix: Forward the signal through WorkspaceToolsHost.start, make preparation abortable, and check cancellation before creation and child startup. Children already launched can remain independent.
Problem
#934 (by rsolmano) gives every chat two always-on tools,
start_workspaceandworkspace_status.With them, "do this in a side workspace" and "run these in parallel" become requests the agent can act
on. #934 was written against
main, before this stack's chats without a project (#887) and Continue inworkspace (#888). On top of them it goes wrong in three places:
start_workspacedefaults to Chats and fails onlistBrancheswitha git error.
Chats (chats)as an open project.workspace_statusreports chats without a project withbase · no PR · diff unknownlines, and thename
Chatmatches all of them.host/chatSession.tsis still copied twice in the stack(
host/chats.ts, Continue'sapplyContinuation). Both copies reportchat_startedascustom(pi's placeholder) instead of
nonefor a chat created without a usable model.workspace.createdpush fora project whose workspace list it never loaded. A spawn into a collapsed project therefore opens a
workspace the store doesn't have. Continue's Open already reloads the list, in its own copy of the same
action.
Eighth PR of the global-search stack; stacked on #939.
Approach
continue_in_workspacenext to the workspace tools);server.ts;ChatActions/ChatTranscript;session.create, which keeps the check for a chat without a project beforestartChatSession.project:"This chat has no project: pass
project. Open projects: …".listProjects()itself. Welcome,project.listand the Chats UI need thecontainer.
workspace_statuswithout arguments in such a chat.One rule for both tools is simpler, and that overview can get large.
chats.tsandapplyContinuationnow start through feat: let the agent start workspaces and check their status #934'sstartChatSession,which is the host's only
createSessioncall.chat_startedfollows the analytics spec everywhere.openWorkspace.chat/openWorkspace.tsserves thestart_workspacecard, Continue's Open andthe handoff's link back to its source.
workspace.list, as Continue already did, and opens evenif that reload fails.
openContinuationis gone.start_workspaceandthe New Workspace dialog use the default branch plus a prefetch.
start_workspace/continue_in_workspaceguidance by intent.Changes
fix(server): start every host-created chat throughstartChatSession.host/chats.tsloses its private copy;applyContinuationloses its inline one.authAttributionregression test: a chat without a project created without a usable modelreports
none. It failed withcustombefore the fix.fix(server): keep the Chats container out ofstart_workspace/workspace_status.host/workspaceTools.ts:resolveProject(ref, fallback | undefined, projects)takes repositoriesonly, and a chat-workspace caller has no fallback.
projectparameter descriptions say it is required in a chat without a project.fix(web): oneopenWorkspacefor every chat card.chat/openWorkspace.tswith its test.ChatActions.openContinuationremoved;WorkspaceChatTarget.sessionIdis optional.projectis required in a chat without a project;openWorkspaceand Continue's cards.Screenshots
Nothing looks different from #934. The
start_workspacecard is #934's, and Continue's cards render asbefore (Open now goes through
openWorkspace). #934's screenshots, seeded transcript, 1280×900:Testing
These checks ran on the final head
6faaf211d:bun run test→ server 1857 pass, 4 skip; web 2009 pass; every other package green.openWorkspacetest leaking a stubbed store action intonavigation/restore.test.ts. It now drives the real store and resets it withgetInitialState().authAttribution→custominstead ofnone;workspaceToolstests → a chat without a project was not refused, andstartWorkspacewent on to cut a worktree from Chats;
openWorkspace→ activated before the list loaded.bun run e2e(full no-agent suite, 8 shards, 651 tests) → all passed.bun run e2e:full e2e/workspace-tools.live.spec.ts e2e/chats-continue.live.spec.ts→ 2 passedwith a real model.
bun run lint(biome, 1794 files);bun run typecheck(22 packages);check:deps,check:boundaries,check:seams,check:spec-surface,check:i18n,check:compiler.rebased tree passed
typecheck, biome, and the affected tests (123 server + 16 web, feat: let the agent start workspaces and check their status #934's ownamong them).
Related issues
Part of the global-search stack (#804 → #805 → #806 → #887 → #888 → #933 → #939 → this). It carries
#934 onto the stack; #934 itself is untouched.
Checklist
bun run lint,bun run typecheck,bun run testbun run e2e, orbun run e2e:fullwhen touching agent behavior)SPEC.md/ top-level specs updated to reflect any boundary, contract, or behavior change