Skip to content

feat: add prompt param to POST /api/sandboxes#231

Merged
sweetmantech merged 1 commit intotestfrom
sweetmantech/myc-4278-api-post-apisandboxes-prompt-param
Feb 18, 2026
Merged

feat: add prompt param to POST /api/sandboxes#231
sweetmantech merged 1 commit intotestfrom
sweetmantech/myc-4278-api-post-apisandboxes-prompt-param

Conversation

@sweetmantech
Copy link
Contributor

@sweetmantech sweetmantech commented Feb 18, 2026

Summary

  • Adds optional prompt field to sandboxBodySchema in validateSandboxBody.ts
  • Validates that command and prompt cannot be used together (returns 400)
  • In processCreateSandbox, converts prompt to command: "opencode", args: ["run", prompt]
  • Adds tests for validation (prompt accepted, command+prompt rejected, empty prompt rejected) and domain logic (prompt converts to opencode command)

Test plan

  • pnpm test lib/sandbox/__tests__/validateSandboxBody.test.ts — 8 tests pass
  • pnpm test lib/sandbox/__tests__/processCreateSandbox.test.ts — 8 tests pass
  • Lint passes on changed files

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Sandbox creation now accepts an optional prompt field
    • Prompts are automatically converted to run commands
    • Added validation preventing both prompt and command from being specified simultaneously

Add optional `prompt` field that converts to `opencode run "<prompt>"`
command. Validates that command and prompt cannot be used together.
Includes tests for validation and domain logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Contributor

vercel bot commented Feb 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-api Ready Ready Preview Feb 18, 2026 1:15pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

Adds optional prompt field to sandbox creation workflow. When provided, translates it to command: "opencode" with args: ["run", prompt]. Updates type definitions and validation schema with mutual exclusivity constraint between command and prompt parameters.

Changes

Cohort / File(s) Summary
Sandbox Command Processing
lib/sandbox/processCreateSandbox.ts
Extended ProcessCreateSandboxInput type to include optional prompt field. Added conditional logic to translate prompt into opencode command invocation when present.
Sandbox Validation
lib/sandbox/validateSandboxBody.ts
Added optional prompt field (string, min length 1) to sandboxBodySchema. Introduced refinement to enforce mutual exclusivity: both command and prompt cannot be specified simultaneously.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Possibly related PRs

Poem

✨ A prompt now whispers, where commands once spoke,
Translating intention to opencode's stroke,
One field or the other, never both at play,
Clean validation guides the sandbox way! 🎭

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning Code violates Clean Code and SOLID principles: silently discards user input without validation, splits validation responsibility between layers, and uses semantically incorrect error field names. Add schema refinement to reject args+prompt conflicts, distinguish error types in handlers, and consolidate validation logic to maintain SRP with comprehensive test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sweetmantech/myc-4278-api-post-apisandboxes-prompt-param

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
lib/sandbox/processCreateSandbox.ts (1)

7-10: Consider exporting ProcessCreateSandboxInput for MCP tool callers.

The JSDoc on line 15 explicitly notes this function is consumed by both the HTTP handler and the run_sandbox_command MCP tool. Keeping the input type unexported means external callers must reconstruct or any-cast the parameter shape. Exporting it costs nothing and improves type safety at all call sites.

♻️ Proposed change
-type ProcessCreateSandboxInput = Pick<
+export type ProcessCreateSandboxInput = Pick<
   SandboxBody,
   "accountId" | "command" | "args" | "cwd" | "prompt"
 >;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/sandbox/processCreateSandbox.ts` around lines 7 - 10, The type
ProcessCreateSandboxInput is currently unexported which forces external callers
like the MCP tool to reconstruct or cast the shape; export the type so callers
can reuse it safely. Update the declaration of ProcessCreateSandboxInput to be
exported (export type ProcessCreateSandboxInput = ...) and ensure any
imports/exports where run_sandbox_command or the HTTP handler live reference
this exported type; keep the exact property pick (accountId, command, args, cwd,
prompt) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/sandbox/validateSandboxBody.ts`:
- Around line 8-18: The schema's refine currently only rejects command+prompt
but allows args+prompt which are dropped downstream; update
sandboxBodySchema.refine predicate to reject any payload where prompt is present
with either command or a non-empty args (e.g. change refine to: data =>
!(data.prompt && (data.command || (data.args && data.args.length > 0)))), and
update the refine message/path accordingly (e.g. message "Cannot specify prompt
with command or args" and path ["prompt"] or ["args"] as appropriate) so callers
get validation errors when supplying args alongside prompt.
- Around line 15-17: The refinement in validateSandboxBody that uses
.refine(..., { message: "Cannot specify both command and prompt", path:
["prompt"] }) incorrectly maps to the response shape's missing_fields; change
the refinement to emit a distinct conflict indicator (e.g., set path to
["conflicting_fields"] or attach a custom issue/code like "conflict") and update
the error-to-response mapping logic that currently populates missing_fields to
instead populate a new conflicting_fields array when that conflict issue/code is
present (include both "command" and "prompt" or at least the conflicting field
names). This keeps the message text, but ensures the response shape
distinguishes conflicts from missing fields and uses identifiers in
validateSandboxBody, the .refine call, and the error-to-response mapping logic
to locate and update the code.

---

Nitpick comments:
In `@lib/sandbox/processCreateSandbox.ts`:
- Around line 7-10: The type ProcessCreateSandboxInput is currently unexported
which forces external callers like the MCP tool to reconstruct or cast the
shape; export the type so callers can reuse it safely. Update the declaration of
ProcessCreateSandboxInput to be exported (export type ProcessCreateSandboxInput
= ...) and ensure any imports/exports where run_sandbox_command or the HTTP
handler live reference this exported type; keep the exact property pick
(accountId, command, args, cwd, prompt) unchanged.

Comment on lines +8 to +18
export const sandboxBodySchema = z
.object({
command: z.string().min(1, "command cannot be empty").optional(),
args: z.array(z.string()).optional(),
cwd: z.string().optional(),
prompt: z.string().min(1, "prompt cannot be empty").optional(),
})
.refine(data => !(data.command && data.prompt), {
message: "Cannot specify both command and prompt",
path: ["prompt"],
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

args + prompt combination passes validation but is silently dropped downstream.

The refinement only guards against command + prompt. A request body of { prompt: "...", args: ["extra"] } passes validation cleanly, but args is silently discarded in processCreateSandbox.ts (line 27: const args = prompt ? ["run", prompt] : input.args). A caller who passes args alongside prompt gets no feedback that their args were ignored.

Extend the refinement to also reject args + prompt:

🛡️ Proposed fix — also block args + prompt together
  .refine(data => !(data.command && data.prompt), {
    message: "Cannot specify both command and prompt",
    path: ["prompt"],
- });
+ })
+ .refine(data => !(data.args && data.prompt), {
+   message: "Cannot specify both args and prompt",
+   path: ["args"],
+ });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const sandboxBodySchema = z
.object({
command: z.string().min(1, "command cannot be empty").optional(),
args: z.array(z.string()).optional(),
cwd: z.string().optional(),
prompt: z.string().min(1, "prompt cannot be empty").optional(),
})
.refine(data => !(data.command && data.prompt), {
message: "Cannot specify both command and prompt",
path: ["prompt"],
});
export const sandboxBodySchema = z
.object({
command: z.string().min(1, "command cannot be empty").optional(),
args: z.array(z.string()).optional(),
cwd: z.string().optional(),
prompt: z.string().min(1, "prompt cannot be empty").optional(),
})
.refine(data => !(data.command && data.prompt), {
message: "Cannot specify both command and prompt",
path: ["prompt"],
})
.refine(data => !(data.args && data.prompt), {
message: "Cannot specify both args and prompt",
path: ["args"],
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/sandbox/validateSandboxBody.ts` around lines 8 - 18, The schema's refine
currently only rejects command+prompt but allows args+prompt which are dropped
downstream; update sandboxBodySchema.refine predicate to reject any payload
where prompt is present with either command or a non-empty args (e.g. change
refine to: data => !(data.prompt && (data.command || (data.args &&
data.args.length > 0)))), and update the refine message/path accordingly (e.g.
message "Cannot specify prompt with command or args" and path ["prompt"] or
["args"] as appropriate) so callers get validation errors when supplying args
alongside prompt.

Comment on lines +15 to +17
.refine(data => !(data.command && data.prompt), {
message: "Cannot specify both command and prompt",
path: ["prompt"],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

missing_fields is semantically incorrect for this refinement error path.

When this refinement fires, line 44 responds with { missing_fields: ["prompt"], error: "Cannot specify both command and prompt" }. The prompt field is not missing — it's conflicting. API consumers receiving missing_fields: ["prompt"] will be misled into thinking they need to add prompt, not remove one of the two conflicting params.

This is a pre-existing naming issue in the error response shape, but the new refinement makes it actively user-hostile because the field literally is present.

💡 Suggested fix — differentiate conflict errors from missing-field errors
  if (!result.success) {
    const firstError = result.error.issues[0];
+   const isConflict = firstError.code === "custom";
    return NextResponse.json(
      {
        status: "error",
-       missing_fields: firstError.path,
+       ...(isConflict
+         ? { conflicting_fields: firstError.path }
+         : { missing_fields: firstError.path }),
        error: firstError.message,
      },
      {
        status: 400,
        headers: getCorsHeaders(),
      },
    );
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/sandbox/validateSandboxBody.ts` around lines 15 - 17, The refinement in
validateSandboxBody that uses .refine(..., { message: "Cannot specify both
command and prompt", path: ["prompt"] }) incorrectly maps to the response
shape's missing_fields; change the refinement to emit a distinct conflict
indicator (e.g., set path to ["conflicting_fields"] or attach a custom
issue/code like "conflict") and update the error-to-response mapping logic that
currently populates missing_fields to instead populate a new conflicting_fields
array when that conflict issue/code is present (include both "command" and
"prompt" or at least the conflicting field names). This keeps the message text,
but ensures the response shape distinguishes conflicts from missing fields and
uses identifiers in validateSandboxBody, the .refine call, and the
error-to-response mapping logic to locate and update the code.

@sweetmantech sweetmantech merged commit b96839b into test Feb 18, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments