Skip to content

Commit 2778d75

Browse files
committed
🤖 Minimize initStateManager passing in tool configuration
Remove initStateManager from ToolConfiguration interface - it's only needed by wrapWithInitWait, not by individual tools. Changes: - Remove workspaceId and initStateManager from ToolConfiguration - Update wrapWithInitWait to accept these as separate parameters - Update getToolsForModel signature to pass them separately - Update all call sites in aiService.ts and ipcMain.ts - Update test helpers to match new config interface This makes the separation clearer: - Tools only receive the config they need (cwd, runtime, secrets, etc.) - Init state management is isolated to the wrapper layer - Non-runtime tools never see init-related fields All tests pass (806 unit, 7 integration).
1 parent f17f092 commit 2778d75

File tree

5 files changed

+42
-33
lines changed

5 files changed

+42
-33
lines changed

src/services/aiService.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -431,14 +431,17 @@ export class AIService extends EventEmitter {
431431

432432
// Get tool names early for mode transition sentinel (stub config, no workspace context needed)
433433
const earlyRuntime = createRuntime({ type: "local", srcBaseDir: process.cwd() });
434-
const earlyAllTools = await getToolsForModel(modelString, {
435-
cwd: process.cwd(),
436-
runtime: earlyRuntime,
437-
runtimeTempDir: os.tmpdir(),
438-
workspaceId: "", // Empty workspace ID for early stub config
439-
initStateManager: this.initStateManager,
440-
secrets: {},
441-
});
434+
const earlyAllTools = await getToolsForModel(
435+
modelString,
436+
{
437+
cwd: process.cwd(),
438+
runtime: earlyRuntime,
439+
runtimeTempDir: os.tmpdir(),
440+
secrets: {},
441+
},
442+
"", // Empty workspace ID for early stub config
443+
this.initStateManager
444+
);
442445
const earlyTools = applyToolPolicy(earlyAllTools, toolPolicy);
443446
const toolNamesForSentinel = Object.keys(earlyTools);
444447

@@ -543,14 +546,17 @@ export class AIService extends EventEmitter {
543546
const runtimeTempDir = await this.streamManager.createTempDirForStream(streamToken, runtime);
544547

545548
// Get model-specific tools with workspace path (correct for local or remote)
546-
const allTools = await getToolsForModel(modelString, {
547-
cwd: workspacePath,
548-
runtime,
549+
const allTools = await getToolsForModel(
550+
modelString,
551+
{
552+
cwd: workspacePath,
553+
runtime,
554+
secrets: secretsToRecord(projectSecrets),
555+
runtimeTempDir,
556+
},
549557
workspaceId,
550-
initStateManager: this.initStateManager,
551-
secrets: secretsToRecord(projectSecrets),
552-
runtimeTempDir,
553-
});
558+
this.initStateManager
559+
);
554560

555561
// Apply tool policy to filter tools (if policy provided)
556562
const tools = applyToolPolicy(allTools, toolPolicy);

src/services/ipcMain.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -924,11 +924,10 @@ export class IpcMain {
924924

925925
// Create bash tool with workspace's cwd and secrets
926926
// All IPC bash calls are from UI (background operations) - use truncate to avoid temp file spam
927+
// No init wait needed - IPC calls are user-initiated, not AI tool use
927928
const bashTool = createBashTool({
928929
cwd: workspacePath, // Bash executes in the workspace directory
929930
runtime,
930-
workspaceId,
931-
initStateManager: this.initStateManager,
932931
secrets: secretsToRecord(projectSecrets),
933932
niceness: options?.niceness,
934933
runtimeTempDir: tempDir.path,

src/services/tools/testHelpers.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ export function createTestToolConfig(
5555
return {
5656
cwd: tempDir,
5757
runtime: new LocalRuntime(tempDir),
58-
workspaceId: "test-workspace",
59-
initStateManager: getTestInitStateManager(),
6058
runtimeTempDir: tempDir,
6159
niceness: options?.niceness,
6260
};

src/services/tools/wrapWithInitWait.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Tool } from "ai";
2-
import type { ToolConfiguration } from "@/utils/tools/tools";
2+
import type { InitStateManager } from "@/services/initStateManager";
33

44
/**
55
* Wraps a tool to wait for workspace initialization before execution.
@@ -11,19 +11,21 @@ import type { ToolConfiguration } from "@/utils/tools/tools";
1111
* Non-runtime tools (propose_plan, todo, web_search) execute immediately.
1212
*
1313
* @param tool The tool to wrap (returned from a tool factory)
14-
* @param config Tool configuration containing initStateManager
14+
* @param workspaceId Workspace ID for init state tracking
15+
* @param initStateManager Init state manager for waiting
1516
* @returns Wrapped tool that waits for init before executing
1617
*/
1718
export function wrapWithInitWait<TParameters, TResult>(
1819
tool: Tool<TParameters, TResult>,
19-
config: ToolConfiguration
20+
workspaceId: string,
21+
initStateManager: InitStateManager
2022
): Tool<TParameters, TResult> {
2123
return {
2224
...tool,
2325
execute: async (args: TParameters, options) => {
2426
// Wait for workspace initialization to complete (no-op if not needed)
2527
// This never throws - tools proceed regardless of init outcome
26-
await config.initStateManager.waitForInit(config.workspaceId);
28+
await initStateManager.waitForInit(workspaceId);
2729

2830
// Execute the actual tool with all arguments
2931
if (!tool.execute) {

src/utils/tools/tools.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ export interface ToolConfiguration {
2020
cwd: string;
2121
/** Runtime environment for executing commands and file operations */
2222
runtime: Runtime;
23-
/** Workspace ID - used to wait for initialization before executing tools */
24-
workspaceId: string;
25-
/** Init state manager - used by tools to wait for async initialization (SSH runtime) */
26-
initStateManager: InitStateManager;
2723
/** Environment secrets to inject (optional) */
2824
secrets?: Record<string, string>;
2925
/** Process niceness level (optional, -20 to 19, lower = higher priority) */
@@ -47,25 +43,33 @@ export type ToolFactory = (config: ToolConfiguration) => Tool;
4743
*
4844
* @param modelString The model string in format "provider:model-id"
4945
* @param config Required configuration for tools
46+
* @param workspaceId Workspace ID for init state tracking (required for runtime tools)
47+
* @param initStateManager Init state manager for runtime tools to wait for initialization
5048
* @returns Promise resolving to record of tools available for the model
5149
*/
5250
export async function getToolsForModel(
5351
modelString: string,
54-
config: ToolConfiguration
52+
config: ToolConfiguration,
53+
workspaceId: string,
54+
initStateManager: InitStateManager
5555
): Promise<Record<string, Tool>> {
5656
const [provider, modelId] = modelString.split(":");
5757

5858
// Runtime-dependent tools need to wait for workspace initialization
5959
// Wrap them to handle init waiting centrally instead of in each tool
6060
const runtimeTools: Record<string, Tool> = {
61-
file_read: wrapWithInitWait(createFileReadTool(config), config),
62-
file_edit_replace_string: wrapWithInitWait(createFileEditReplaceStringTool(config), config),
61+
file_read: wrapWithInitWait(createFileReadTool(config), workspaceId, initStateManager),
62+
file_edit_replace_string: wrapWithInitWait(
63+
createFileEditReplaceStringTool(config),
64+
workspaceId,
65+
initStateManager
66+
),
6367
// DISABLED: file_edit_replace_lines - causes models (particularly GPT-5-Codex)
6468
// to leave repository in broken state due to issues with concurrent file modifications
6569
// and line number miscalculations. Use file_edit_replace_string or file_edit_insert instead.
66-
// file_edit_replace_lines: wrapWithInitWait(createFileEditReplaceLinesTool(config), config),
67-
file_edit_insert: wrapWithInitWait(createFileEditInsertTool(config), config),
68-
bash: wrapWithInitWait(createBashTool(config), config),
70+
// file_edit_replace_lines: wrapWithInitWait(createFileEditReplaceLinesTool(config), workspaceId, initStateManager),
71+
file_edit_insert: wrapWithInitWait(createFileEditInsertTool(config), workspaceId, initStateManager),
72+
bash: wrapWithInitWait(createBashTool(config), workspaceId, initStateManager),
6973
};
7074

7175
// Non-runtime tools execute immediately (no init wait needed)

0 commit comments

Comments
 (0)