From: Jérôme Benoit Date: Fri, 8 May 2026 15:08:22 +0000 (+0200) Subject: fix(sandcastle): patch pi thinking option and replace type indirections X-Git-Tag: cli@v4.7.1~4 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=550e29441d49dc09a8098ad4eed8a67c22d531ea;p=e-mobility-charging-stations-simulator.git fix(sandcastle): patch pi thinking option and replace type indirections Apply pnpm patch for @ai-hero/sandcastle porting PR #584 (pi --thinking flag). Wire the thinking option through agentProvider() and replace Awaited> indirections with direct type imports (Sandbox, SandboxRunResult, RunResult, PiOptions). --- diff --git a/.sandcastle/constants.ts b/.sandcastle/constants.ts index 98a595ee..07ba0382 100644 --- a/.sandcastle/constants.ts +++ b/.sandcastle/constants.ts @@ -7,11 +7,11 @@ export type AgentProviderType = 'opencode' | 'pi' 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' @@ -21,7 +21,7 @@ export const AGENT_ITERATION_BUDGET = 50 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' diff --git a/.sandcastle/refinement-loop.ts b/.sandcastle/refinement-loop.ts index ad546ab5..49f9981e 100644 --- a/.sandcastle/refinement-loop.ts +++ b/.sandcastle/refinement-loop.ts @@ -1,3 +1,5 @@ +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' @@ -459,7 +461,7 @@ async function executeRound ( } // Actor - let actorResult: Awaited> + let actorResult: SandboxRunResult try { actorResult = await sandbox.run({ agent: agentProvider( diff --git a/.sandcastle/task-source.ts b/.sandcastle/task-source.ts index 5452e8b9..36c6bb1b 100644 --- a/.sandcastle/task-source.ts +++ b/.sandcastle/task-source.ts @@ -1,3 +1,5 @@ +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' @@ -95,7 +97,7 @@ export class GithubIssueSource implements TaskSource { for (let attempt = 1; attempt <= this.maxRetries; attempt++) { console.log(`\n=== Planner attempt ${String(attempt)}/${String(this.maxRetries)} ===\n`) - let plan: Awaited> + let plan: RunResult try { plan = await sandcastle.run({ agent: agentProvider(AGENT_PLANNER_MODEL, AGENT_PLANNER_EFFORT), diff --git a/.sandcastle/types.ts b/.sandcastle/types.ts index 6688a97f..90e787bd 100644 --- a/.sandcastle/types.ts +++ b/.sandcastle/types.ts @@ -1,4 +1,4 @@ -import type * as sandcastle from '@ai-hero/sandcastle' +import type { PiOptions, Sandbox } from '@ai-hero/sandcastle' import { z } from 'zod' @@ -67,7 +67,7 @@ export type LoopStatus = 'converged' | 'exhausted' | 'failed' | 'skipped' // 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. */ @@ -77,7 +77,7 @@ export type LoopStrategy = { /** Builds promptArgs for the critic run from task spec and base branch. */ buildCriticArgs: (spec: TaskSpec, baseBranch: string) => Record /** 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. */ @@ -101,7 +101,7 @@ export interface RoundSnapshot { } /** Type alias for a sandcastle sandbox instance. */ -export type SandboxInstance = Awaited> +export type SandboxInstance = Sandbox /** Specification for a task to be implemented. */ export interface TaskSpec { diff --git a/.sandcastle/utils.ts b/.sandcastle/utils.ts index ec05d7c8..efddc89d 100644 --- a/.sandcastle/utils.ts +++ b/.sandcastle/utils.ts @@ -1,4 +1,4 @@ -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' @@ -12,15 +12,15 @@ export const execFileAsync = util.promisify(execFile) /** * 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) } } diff --git a/package.json b/package.json index 0e5d2758..9842f2cd 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,9 @@ "pnpm": { "onlyBuiltDependencies": [ "better-sqlite3" - ] + ], + "patchedDependencies": { + "@ai-hero/sandcastle": "patches/@ai-hero__sandcastle.patch" + } } } diff --git a/patches/@ai-hero__sandcastle.patch b/patches/@ai-hero__sandcastle.patch new file mode 100644 index 00000000..a5784c94 --- /dev/null +++ b/patches/@ai-hero__sandcastle.patch @@ -0,0 +1,30 @@ +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; + } +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, + }; + }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 910000eb..8524dfa6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,11 @@ overrides: 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: .: @@ -92,7 +97,7 @@ 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) @@ -6988,7 +6993,7 @@ snapshots: 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)