export const AGENT_PROVIDER = 'pi' as AgentProviderType
-export const AGENT_ACTOR_EFFORT = 'high'
+export const AGENT_ACTOR_EFFORT = 'high' as const
export const AGENT_ACTOR_MODEL = 'github-copilot/claude-opus-4.6'
-export const AGENT_CRITIC_EFFORT = 'medium'
+export const AGENT_CRITIC_EFFORT = 'medium' as const
export const AGENT_CRITIC_MODEL = 'github-copilot/gpt-5.4'
export const AGENT_MAX_CRITIC_ROUNDS = 10
-export const AGENT_PLANNER_EFFORT = 'medium'
+export const AGENT_PLANNER_EFFORT = 'medium' as const
export const AGENT_PLANNER_MODEL = 'github-copilot/claude-sonnet-4.6'
+import type { SandboxRunResult } from '@ai-hero/sandcastle'
+
import crypto from 'node:crypto'
import { readFile, realpath } from 'node:fs/promises'
import { join, sep } from 'node:path'
}
// Actor
- let actorResult: Awaited<ReturnType<typeof sandbox.run>>
+ let actorResult: SandboxRunResult
try {
actorResult = await sandbox.run({
agent: agentProvider(
+import type { RunResult } from '@ai-hero/sandcastle'
+
import * as sandcastle from '@ai-hero/sandcastle'
import { docker } from '@ai-hero/sandcastle/sandboxes/docker'
import { z } from 'zod'
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
console.log(`\n=== Planner attempt ${String(attempt)}/${String(this.maxRetries)} ===\n`)
- let plan: Awaited<ReturnType<typeof sandcastle.run>>
+ let plan: RunResult
try {
plan = await sandcastle.run({
agent: agentProvider(AGENT_PLANNER_MODEL, AGENT_PLANNER_EFFORT),
-import type * as sandcastle from '@ai-hero/sandcastle'
+import type { PiOptions, Sandbox } from '@ai-hero/sandcastle'
import { z } from 'zod'
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type LoopStrategy = {
/** Reasoning effort for the actor agent. Defaults to AGENT_ACTOR_EFFORT constant. */
- actorEffort?: string
+ actorEffort?: PiOptions['thinking']
/** Model for the actor agent. Defaults to AGENT_ACTOR_MODEL constant. */
actorModel?: string
/** Path to the actor prompt file. */
/** Builds promptArgs for the critic run from task spec and base branch. */
buildCriticArgs: (spec: TaskSpec, baseBranch: string) => Record<string, string>
/** Reasoning effort for the critic agent. Defaults to AGENT_CRITIC_EFFORT constant. */
- criticEffort?: string
+ criticEffort?: PiOptions['thinking']
/** Model for the critic agent. Defaults to AGENT_CRITIC_MODEL constant. */
criticModel?: string
/** Path to the critic prompt file. */
}
/** Type alias for a sandcastle sandbox instance. */
-export type SandboxInstance = Awaited<ReturnType<typeof sandcastle.createSandbox>>
+export type SandboxInstance = Sandbox
/** Specification for a task to be implemented. */
export interface TaskSpec {
-import type { AgentProvider } from '@ai-hero/sandcastle'
+import type { AgentProvider, PiOptions } from '@ai-hero/sandcastle'
import * as sandcastle from '@ai-hero/sandcastle'
import { execFile } from 'node:child_process'
/**
* Returns a sandcastle agent provider for the given model, selected by AGENT_PROVIDER constant.
* @param model - The model identifier (e.g., 'github-copilot/claude-sonnet-4.6').
- * @param effort - Reasoning effort level passed as `variant` to opencode.
+ * @param effort - Reasoning effort level passed as `variant` to opencode or `thinking` to pi.
* @returns The configured agent provider.
*/
-export function agentProvider (model: string, effort?: string): AgentProvider {
+export function agentProvider (model: string, effort?: PiOptions['thinking']): AgentProvider {
switch (AGENT_PROVIDER) {
case 'opencode':
return sandcastle.opencode(model, effort ? { variant: effort } : undefined)
case 'pi':
- return sandcastle.pi(model)
+ return sandcastle.pi(model, effort ? { thinking: effort } : undefined)
}
}
"pnpm": {
"onlyBuiltDependencies": [
"better-sqlite3"
- ]
+ ],
+ "patchedDependencies": {
+ "@ai-hero/sandcastle": "patches/@ai-hero__sandcastle.patch"
+ }
}
}
--- /dev/null
+diff --git a/dist/AgentProvider.d.ts b/dist/AgentProvider.d.ts
+index 134d7cbea22e9f4525f69de0549f0c2e5c9bac67..c661e939c6e40eefc8f8b2dbbb6ed1154b0e8dd6 100644
+--- a/dist/AgentProvider.d.ts
++++ b/dist/AgentProvider.d.ts
+@@ -48,6 +48,8 @@ export interface AgentProvider {
+ export declare const DEFAULT_MODEL = "claude-opus-4-6";
+ /** Options for the pi agent provider. */
+ export interface PiOptions {
++ /** Reasoning/thinking level for the agent. */
++ readonly thinking?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
+ /** Environment variables injected by this agent provider. */
+ readonly env?: Record<string, string>;
+ }
+diff --git a/dist/AgentProvider.js b/dist/AgentProvider.js
+index 01b533cb129675070c618dc790b74d41259dab0c..f2a72b46e7e01d534027cb1efec81fdbe9761fa8 100644
+--- a/dist/AgentProvider.js
++++ b/dist/AgentProvider.js
+@@ -140,8 +140,11 @@ export const pi = (model, options) => ({
+ env: options?.env ?? {},
+ captureSessions: false,
+ buildPrintCommand({ prompt }) {
++ const thinkingFlag = options?.thinking
++ ? ` --thinking ${options.thinking}`
++ : "";
+ return {
+- command: `pi -p --mode json --no-session --model ${shellEscape(model)}`,
++ command: `pi -p --mode json --no-session${thinkingFlag} --model ${shellEscape(model)}`,
+ stdin: prompt,
+ };
+ },
tough-cookie@<4.1.3: '>=4.1.3'
uuid@<7: '>=7.0.0'
+patchedDependencies:
+ '@ai-hero/sandcastle':
+ hash: 98ac71a9a8d3bc7df881ffb9c3a698ac315e90f9c507c5d8c356868e3e35a4d5
+ path: patches/@ai-hero__sandcastle.patch
+
importers:
.:
devDependencies:
'@ai-hero/sandcastle':
specifier: ^0.5.10
- version: 0.5.10(@effect/cluster@0.57.0(@effect/platform@0.95.0(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/workflow@0.17.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(effect@3.21.0))(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/typeclass@0.39.0(effect@3.21.0))(bufferutil@4.1.0)(utf-8-validate@6.0.6)
+ version: 0.5.10(patch_hash=98ac71a9a8d3bc7df881ffb9c3a698ac315e90f9c507c5d8c356868e3e35a4d5)(@effect/cluster@0.57.0(@effect/platform@0.95.0(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/workflow@0.17.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(effect@3.21.0))(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/typeclass@0.39.0(effect@3.21.0))(bufferutil@4.1.0)(utf-8-validate@6.0.6)
'@commitlint/cli':
specifier: ^20.5.3
version: 20.5.3(@types/node@24.12.3)(conventional-commits-parser@6.4.0)(typescript@6.0.3)
transitivePeerDependencies:
- supports-color
- '@ai-hero/sandcastle@0.5.10(@effect/cluster@0.57.0(@effect/platform@0.95.0(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/workflow@0.17.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(effect@3.21.0))(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/typeclass@0.39.0(effect@3.21.0))(bufferutil@4.1.0)(utf-8-validate@6.0.6)':
+ '@ai-hero/sandcastle@0.5.10(patch_hash=98ac71a9a8d3bc7df881ffb9c3a698ac315e90f9c507c5d8c356868e3e35a4d5)(@effect/cluster@0.57.0(@effect/platform@0.95.0(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/workflow@0.17.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(effect@3.21.0))(effect@3.21.0))(@effect/rpc@0.74.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/platform@0.95.0(effect@3.21.0))(effect@3.21.0))(@effect/typeclass@0.39.0(effect@3.21.0))(bufferutil@4.1.0)(utf-8-validate@6.0.6)':
dependencies:
'@clack/prompts': 1.3.0
'@effect/cli': 0.74.0(@effect/platform@0.95.0(effect@3.21.0))(@effect/printer-ansi@0.48.0(@effect/typeclass@0.39.0(effect@3.21.0))(effect@3.21.0))(@effect/printer@0.48.0(@effect/typeclass@0.39.0(effect@3.21.0))(effect@3.21.0))(effect@3.21.0)