From 4b97a890fdae50f70bef5ccad7371c3c8a2cd04b Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 25 Apr 2026 09:48:06 +0800 Subject: [PATCH] feat(opencode): add tests for read.ts tool copying based on vision connector presence - Implemented two new test scenarios: one to verify that read.ts is not copied when no vision connector is present, and another to confirm that it is copied when a vision connector is configured. - Updated the runner logic to conditionally copy custom tools into the OpenCode global config directory based on the presence of a vision connector in the configuration. --- agent/sandbox/v2/opencode/runner.go | 23 +++++---- agent/sandbox/v2/opencode/runner_test.go | 66 ++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/agent/sandbox/v2/opencode/runner.go b/agent/sandbox/v2/opencode/runner.go index 4cb69855..7aa7f728 100644 --- a/agent/sandbox/v2/opencode/runner.go +++ b/agent/sandbox/v2/opencode/runner.go @@ -75,15 +75,20 @@ func (r *Runner) Prepare(ctx context.Context, req *types.PrepareRequest) error { } } - // 4. Copy custom tools into OpenCode global config dir ($HOME/.config/opencode/tools/). - // Bun resolves symlinks and breaks module resolution (opencode#11001), so use cp. - // HOME is set to workDir (/workspace) at runtime, so this lands in persistent storage. - steps = append(steps, types.PrepareStep{ - Action: "exec", - Cmd: "mkdir -p $HOME/.config/opencode/tools && for f in /opt/opencode-tools/*.ts; do [ -f \"$f\" ] && cp -f \"$f\" $HOME/.config/opencode/tools/; done", - Once: true, - IgnoreError: true, - }) + // 4. Copy custom tools (e.g. read.ts for vision) into OpenCode global + // config dir ($HOME/.config/opencode/tools/). Only needed when a + // vision connector is configured — the custom read tool overrides the + // built-in read to route image files through the vision API. + if req.Config != nil && req.Config.Runner.Connectors != nil { + if vc, ok := req.Config.Runner.Connectors["vision"]; ok && vc != nil && vc.Connector != "" { + steps = append(steps, types.PrepareStep{ + Action: "exec", + Cmd: "mkdir -p $HOME/.config/opencode/tools && for f in /opt/opencode-tools/*.ts; do [ -f \"$f\" ] && cp -f \"$f\" $HOME/.config/opencode/tools/; done", + Once: true, + IgnoreError: true, + }) + } + } // 5. Generate opencode.json (project config at workspace root) configJSON := buildOpenCodeConfig(req, r.mcpServers) diff --git a/agent/sandbox/v2/opencode/runner_test.go b/agent/sandbox/v2/opencode/runner_test.go index ec8a9a0b..1eaa88be 100644 --- a/agent/sandbox/v2/opencode/runner_test.go +++ b/agent/sandbox/v2/opencode/runner_test.go @@ -103,6 +103,72 @@ func TestOpenCode_Session(t *testing.T) { }) } +// --------------------------------------------------------------------------- +// Scenario 4: No vision connector — read.ts should NOT be copied +// --------------------------------------------------------------------------- + +func TestOpenCode_NoVision_ReadToolNotCopied(t *testing.T) { + sandboxtestutils.Prepare(t) + defer sandboxtestutils.Clean(t) + require.NotNil(t, caller.AgentGetterFunc) + + const assistantID = "tests.sandbox-v2.opencode-oneshot-cli" + agent, err := caller.AgentGetterFunc(assistantID) + require.NoError(t, err) + + chatID := fmt.Sprintf("e2e-novision-%d", time.Now().UnixMilli()) + ctx := agentcontext.New( + context.Background(), + &oauthtypes.AuthorizedInfo{TeamID: "test-team-e2e", UserID: "test-user-e2e"}, + chatID, + ) + + resp := streamAndWait(t, agent, ctx, + `Check if the file $HOME/.config/opencode/tools/read.ts exists. `+ + `Reply with exactly "READ_EXISTS" if it does, or "READ_MISSING" if it does not. Nothing else.`, + defaultTimeout, + ) + + require.NotNil(t, resp.Completion) + content := strings.ToLower(contentString(t, resp)) + t.Logf("NoVision check: %s", content) + assert.Contains(t, content, "read_missing", + "without vision connector, read.ts should NOT be copied") +} + +// --------------------------------------------------------------------------- +// Scenario 5: With vision connector — read.ts SHOULD be copied +// --------------------------------------------------------------------------- + +func TestOpenCode_Vision_ReadToolCopied(t *testing.T) { + sandboxtestutils.Prepare(t) + defer sandboxtestutils.Clean(t) + require.NotNil(t, caller.AgentGetterFunc) + + const assistantID = "tests.sandbox-v2.opencode-vision-cli" + agent, err := caller.AgentGetterFunc(assistantID) + require.NoError(t, err) + + chatID := fmt.Sprintf("e2e-vision-%d", time.Now().UnixMilli()) + ctx := agentcontext.New( + context.Background(), + &oauthtypes.AuthorizedInfo{TeamID: "test-team-e2e", UserID: "test-user-e2e"}, + chatID, + ) + + resp := streamAndWait(t, agent, ctx, + `Check if the file $HOME/.config/opencode/tools/read.ts exists. `+ + `Reply with exactly "READ_EXISTS" if it does, or "READ_MISSING" if it does not. Nothing else.`, + defaultTimeout, + ) + + require.NotNil(t, resp.Completion) + content := strings.ToLower(contentString(t, resp)) + t.Logf("Vision check: %s", content) + assert.Contains(t, content, "read_exists", + "with vision connector, read.ts SHOULD be copied") +} + // --------------------------------------------------------------------------- // Helpers // ---------------------------------------------------------------------------