fix: wait for worktree bootstrap before prompt dispatch; resolve session directory for send/fork (#2708)

This commit is contained in:
Bruno Fantauzzi
2026-08-06 23:47:30 +03:00
committed by GitHub
parent 27c46aa0b9
commit e8caed45c9
4 changed files with 170 additions and 3 deletions
@@ -1,6 +1,6 @@
import express from 'express';
import { createOpencodeClient } from '@opencode-ai/sdk/v2';
import { createWorktree } from '../git/index.js';
import { createWorktree, getWorktreeBootstrapStatus } from '../git/index.js';
import { expandSnippets } from '../opencode/snippets.js';
import { expandCommandGoalObjective, parseScheduledCommandPrompt } from '../scheduled-tasks/runtime.js';
import { buildGoalIntroText, createSessionGoal } from '../session-goal/create.js';
@@ -275,6 +275,31 @@ const resolveRequestedDirectory = async ({ payload, readSettingsFromDiskMigrated
const PROMPT_LANDED_TIMEOUT_MS = 5_000;
const PROMPT_LANDED_POLL_MS = 150;
// createWorktree returns while the worktree is still being populated in the
// background (git reset --hard after a --no-checkout add). Dispatching a
// prompt into a half-populated directory makes opencode's run die with
// UnknownError (agent and config files are not there yet), so wait until the
// bootstrap reaches git-ready (population done) or fails before creating the
// session and dispatching.
const WORKTREE_BOOTSTRAP_TIMEOUT_MS = 60_000;
const WORKTREE_BOOTSTRAP_POLL_MS = 150;
const waitForWorktreeBootstrapReady = async ({ directory }) => {
const deadline = Date.now() + WORKTREE_BOOTSTRAP_TIMEOUT_MS;
for (;;) {
const status = await getWorktreeBootstrapStatus(directory);
if (status?.status === 'failed') {
throw new OpenChamberControlError(`Worktree bootstrap failed: ${status.error || 'unknown error'}`, 500);
}
const phase = status?.phase;
if (status?.status === 'ready' || phase === 'git-ready' || phase === 'setup-ready') return;
if (Date.now() >= deadline) {
throw new OpenChamberControlError('Timed out waiting for the worktree bootstrap', 500);
}
await new Promise((resolve) => setTimeout(resolve, WORKTREE_BOOTSTRAP_POLL_MS));
}
};
const latestUserMessageID = async ({ client, sessionID, directory }) => {
let response;
try {
@@ -579,6 +604,7 @@ export const createOpenChamberSessionService = (dependencies) => {
if (worktreeInput) {
worktree = await createWorktree(resolvedDirectory.directory, worktreeInput);
sessionDirectory = worktree.path;
await waitForWorktreeBootstrapReady({ directory: sessionDirectory });
}
const baseUrl = buildOpenCodeUrl('/', '').replace(/\/$/, '');