Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
4638b26
feat(pi): optional multi-provider web search for the coding agent
Jul 25, 2026
579cda1
fix(pi): drop the banned JSON round-trip from the search parity test
Jul 25, 2026
8493ed4
chore(agents): add reviewed-development skill and the babysit impleme…
Jul 25, 2026
f1cfff6
feat(pi): babysit foundations — shared PR/push extraction, GitHub too…
cursoragent Jul 25, 2026
0899406
fix(tools): correct the rollup CheckRun selection and stop leaking th…
cursoragent Jul 25, 2026
509ce37
test(tools): cover the stripAuthOnRedirect plumbing end to end
cursoragent Jul 25, 2026
40cb5ec
fix(pi): correct the push-hardening claim, reserve finalize time, tig…
cursoragent Jul 25, 2026
1147eaf
fix(pi): reserve both finalize budgets in the Pi command timeout
cursoragent Jul 25, 2026
785d372
docs(pi): stop describing Babysit's digest check in the present tense
cursoragent Jul 25, 2026
6bd708c
fix(pi): scope the sandbox lifetime cap to E2B and harden the job-log…
Jul 25, 2026
11cb574
Add Pi Babysit mode
Jul 25, 2026
bba6bf6
fix(pi): wait on required checks before optional failures
Jul 25, 2026
a38c682
fix(pi): preserve Daytona babysit budget
Jul 25, 2026
dffec58
fix(pi): bound babysit round setup
Jul 25, 2026
770ca81
fix(pi): keep babysit sandboxes active
Jul 25, 2026
5c23136
fix(pi): report babysit round state accurately
Jul 25, 2026
49c1349
fix(pi): classify babysit finalize failures
Jul 25, 2026
dbf510f
fix(pi): preserve babysit partial state
Jul 25, 2026
439dfc8
fix(pi): retain post-push check state
Jul 25, 2026
1835926
fix(pi): retain pending rereview state
Jul 25, 2026
2dc74ef
fix(pi): normalize empty sandbox provider
Jul 25, 2026
9ad1c66
Move Babysit into Create PR
Jul 26, 2026
0218fb7
Fix Babysit wait-only budgeting
Jul 26, 2026
3c0c2fa
Wait for reviews before skipped-thread exit
Jul 26, 2026
d3a5831
Polish Babysit reviewer field spacing
Jul 26, 2026
88b6ca2
Merge remote-tracking branch 'origin/staging' into feature/pi-babysit
icecrasher321 Jul 29, 2026
810844c
Fix duplicated Internet Search section from staging merge
icecrasher321 Jul 29, 2026
8c7d9d2
fix(pi): correct Babysit check, budget, and push-guard accuracy
icecrasher321 Jul 29, 2026
fa8a56a
fix(pi): budget Babysit against the run's real deadline
icecrasher321 Jul 29, 2026
a4aff13
fix(pi): refuse any Git-quoted path before the Babysit push
icecrasher321 Jul 29, 2026
130c76b
fix(pi): harden the Babysit push against a repository the agent controls
icecrasher321 Jul 29, 2026
234693e
fix(pi): correct switch coercion for draft and tidy Babysit reporting
icecrasher321 Jul 29, 2026
7854e40
docs(tools): record why the Babysit GitHub tools are registry-only
icecrasher321 Jul 29, 2026
e768176
docs(pi): document the clean stop reason and Babysit's fixed bounds
icecrasher321 Jul 29, 2026
93b78fb
docs(tools): correct and widen the registry-only note
icecrasher321 Jul 29, 2026
f716fa6
fix(pi): size the sandbox to the run's own execution timeout
icecrasher321 Jul 29, 2026
bc3ff8c
fix(pi): raise the sandbox ceiling to the longest execution we allow
icecrasher321 Jul 29, 2026
1df7836
docs(pi): describe the deadline-sized sandbox, not a fixed hour
icecrasher321 Jul 29, 2026
59327fe
fix(pi): share the sandbox sizing and lift E2B off the base default
icecrasher321 Jul 29, 2026
8a4b28d
chore(pi): remove internal planning files
Jul 29, 2026
9819041
chore(pi): remove generated review commands
Jul 29, 2026
231aa80
fix(pi): align babysit toggle visibility
Jul 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/docs/content/docs/en/integrations/github.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ Fetch PR details including diff and files changed
| ↳ `label` | string | Branch label \(owner:branch\) |
| ↳ `ref` | string | Branch name |
| ↳ `sha` | string | Commit SHA |
| ↳ `repo_full_name` | string | Full name \(owner/repo\) of the branch's repository |
| `base` | object | Branch reference info |
| ↳ `label` | string | Branch label \(owner:branch\) |
| ↳ `ref` | string | Branch name |
| ↳ `sha` | string | Commit SHA |
| ↳ `repo_full_name` | string | Full name \(owner/repo\) of the branch's repository |
| `id` | number | Pull request ID |
| `number` | number | Pull request number |
| `title` | string | PR title |
Expand Down
80 changes: 66 additions & 14 deletions apps/docs/content/docs/en/workflows/blocks/pi.mdx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,16 @@ export function Editor() {
: undefined
}
/>
{showDivider && <FieldDivider subblockMarker />}
{showDivider && (
<FieldDivider
subblockMarker
className={
regularSubBlocks[index + 1]?.hideDividerBefore
? '[&>div]:invisible'
: undefined
}
/>
)}
</div>
)
})}
Expand Down Expand Up @@ -707,7 +716,14 @@ export function Editor() {
}
/>
{index < advancedOnlySubBlocks.length - 1 && (
<FieldDivider subblockMarker />
<FieldDivider
subblockMarker
className={
advancedOnlySubBlocks[index + 1]?.hideDividerBefore
? '[&>div]:invisible'
: undefined
}
/>
)}
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1482,7 +1482,16 @@ function PreviewEditorContent({
subBlockValues={subBlockValues}
disabled={true}
/>
{index < visibleSubBlocks.length - 1 && <FieldDivider subblockMarker />}
{index < visibleSubBlocks.length - 1 && (
<FieldDivider
subblockMarker
className={
visibleSubBlocks[index + 1]?.hideDividerBefore
? '[&>div]:invisible'
: undefined
}
/>
)}
</div>
))}
</div>
Expand Down
102 changes: 102 additions & 0 deletions apps/sim/blocks/blocks/pi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ import { describe, expect, it, vi } from 'vitest'
// deliberately does not import it — no block imports from `@/executor`. This test is what ties the
// two copies together, so adding a provider to one and not the other fails here.
vi.mock('@/lib/api-key/byok', () => ({ getBYOKKey: vi.fn(), getApiKeyWithBYOK: vi.fn() }))
vi.mock('@/lib/core/config/env', async (importOriginal) => {
const original = await importOriginal<typeof import('@/lib/core/config/env')>()
return {
...original,
getEnv: vi.fn((key: string) => (key === 'NEXT_PUBLIC_E2B_ENABLED' ? 'true' : undefined)),
isTruthy: vi.fn((value: unknown) => value === 'true'),
}
})

import { evaluateSubBlockCondition } from '@/lib/workflows/subblocks/visibility'
import { PiBlock } from '@/blocks/blocks/pi'
Expand Down Expand Up @@ -75,3 +83,97 @@ describe('Pi block search fields', () => {
expect(PiBlock.inputs.searchApiKey).toBeDefined()
})
})

describe('Pi Create PR Babysit surface', () => {
it('offers exactly Create PR, Review Code, and Local Dev as top-level modes', () => {
const mode = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'mode')
const options =
typeof mode?.options === 'function'
? mode.options()
: (mode?.options as Array<{ id: string }> | undefined)

expect(options?.map(({ id }) => id)).toEqual(['cloud', 'cloud_review', 'local'])
})

it('declares the toggle, required reviewer mentions, advanced rounds, and result outputs', () => {
const toggle = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'babysitMode')
const maxRounds = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'maxRounds')
const mentions = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'reviewMentions')

expect(toggle).toMatchObject({
type: 'switch',
defaultValue: false,
condition: { field: 'mode', value: 'cloud' },
})
expect(maxRounds).toMatchObject({
type: 'short-input',
defaultValue: '3',
mode: 'advanced',
condition: {
field: 'mode',
value: 'cloud',
and: { field: 'babysitMode', value: [true, 'true'] },
},
})
expect(mentions).toMatchObject({
type: 'short-input',
defaultValue: '',
hideDividerBefore: true,
required: {
field: 'mode',
value: 'cloud',
and: { field: 'babysitMode', value: [true, 'true'] },
},
condition: {
field: 'mode',
value: 'cloud',
and: { field: 'babysitMode', value: [true, 'true'] },
},
})
for (const output of [
'rounds',
'threadsClean',
'checksGreen',
'threadsResolved',
'commitsPushed',
'stopReason',
]) {
expect(PiBlock.outputs[output]).toMatchObject({
condition: {
field: 'mode',
value: 'cloud',
and: { field: 'babysitMode', value: [true, 'true'] },
},
})
}
})

it('requires a task and hides Draft PR while Babysit Mode is enabled', () => {
const task = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'task')
const draft = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'draft')
const skills = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'skills')
const tools = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'tools')
const memory = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'memoryType')
const pullNumber = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'pullNumber')

expect(task?.required).toBe(true)
expect(evaluateSubBlockCondition(draft?.condition, { mode: 'cloud' })).toBe(true)
expect(evaluateSubBlockCondition(draft?.condition, { mode: 'cloud', babysitMode: true })).toBe(
false
)
expect(
evaluateSubBlockCondition(draft?.condition, { mode: 'cloud', babysitMode: 'true' })
).toBe(false)
expect(
evaluateSubBlockCondition(
PiBlock.subBlocks.find((subBlock) => subBlock.id === 'reviewMentions')?.condition,
{ mode: 'cloud', babysitMode: 'true' }
)
).toBe(true)
expect(evaluateSubBlockCondition(skills?.condition, { mode: 'cloud' })).toBe(true)
expect(evaluateSubBlockCondition(tools?.condition, { mode: 'cloud' })).toBe(false)
expect(evaluateSubBlockCondition(memory?.condition, { mode: 'cloud' })).toBe(true)
expect(evaluateSubBlockCondition(pullNumber?.condition, { mode: 'cloud' })).toBe(false)
expect(evaluateSubBlockCondition(pullNumber?.condition, { mode: 'cloud_review' })).toBe(true)
})
})
118 changes: 112 additions & 6 deletions apps/sim/blocks/blocks/pi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ interface PiResponse extends ToolResponse {
branch?: string
reviewUrl?: string
commentsPosted?: number
rounds?: number
threadsClean?: boolean
checksGreen?: boolean
threadsResolved?: number
commitsPushed?: number
stopReason?: string
tokens?: {
input?: number
output?: number
Expand Down Expand Up @@ -46,6 +52,31 @@ const CLOUD_ANY: { field: 'mode'; value: Array<'cloud' | 'cloud_review'> } = {
field: 'mode',
value: ['cloud', 'cloud_review'],
}
const BABYSIT_ENABLED_VALUES: Array<true | 'true'> = [true, 'true']
const CLOUD_WITH_BABYSIT: {
field: 'mode'
value: 'cloud'
and: { field: 'babysitMode'; value: Array<true | 'true'> }
} = {
field: 'mode',
value: 'cloud',
and: { field: 'babysitMode', value: BABYSIT_ENABLED_VALUES },
}
function getCloudWithoutBabysitCondition(values?: Record<string, unknown>): {
field: 'mode'
value: 'cloud'
and: { field: 'babysitMode'; value: true | 'true'; not: true }
} {
return {
field: 'mode',
value: 'cloud',
and: {
field: 'babysitMode',
value: values?.babysitMode === 'true' ? 'true' : true,
not: true,
},
}
}
Comment thread
BillLeoutsakosvl346 marked this conversation as resolved.
const LOCAL: { field: 'mode'; value: 'local' } = { field: 'mode', value: 'local' }
const AUTHORING_MODES: { field: 'mode'; value: Array<'cloud' | 'local'> } = {
field: 'mode',
Expand Down Expand Up @@ -85,9 +116,10 @@ export const PiBlock: BlockConfig<PiResponse> = {
description: 'Run an autonomous coding agent on a repo',
authMode: AuthMode.ApiKey,
longDescription:
'The Pi Coding Agent runs the Pi harness against a real repository. Create PR spins up an isolated sandbox, clones a GitHub repo, edits with native shell + git, and opens a pull request. Review Code checks out a pinned PR snapshot with read-only tools and posts a structured review with optional inline comments. Local Dev edits files on your own machine over SSH. Create PR and Local Dev can reuse skills and multi-turn memory; Review Code runs without either because PR contents are untrusted. Any mode can optionally get one web_search tool backed by your own Exa, Serper, Parallel AI, or Firecrawl key; the agent writes its own queries, so repository content may reach the provider, and results are untrusted third-party data.',
'The Pi Coding Agent runs the Pi harness against a real repository. Create PR spins up an isolated sandbox, clones a GitHub repo, edits with native shell + git, and opens a pull request; Babysit Mode then keeps that pull request under watch, fixing trusted bot review threads and failing required checks in bounded rounds. Review Code checks out a pinned PR snapshot with read-only tools and posts a structured review with optional inline comments. Local Dev edits files on your own machine over SSH. Create PR and Local Dev can reuse skills and multi-turn memory; Review Code runs without either because PR contents are untrusted. Any mode can optionally get one web_search tool backed by your own Exa, Serper, Parallel AI, or Firecrawl key; the agent writes its own queries, so repository content may reach the provider, and results are untrusted third-party data.',
bestPractices: `
- Use Create PR for hands-off changes against a GitHub repo where a reviewable PR is the deliverable.
- Enable Babysit Mode on Create PR when trusted review bots and required checks should be monitored and fixed in bounded rounds.
- Use Review Code to analyze an existing PR and leave summary + inline review comments.
- Use Local Dev to edit a repo on your own machine; expose the machine on a public hostname/tunnel so Sim can reach it over SSH.
- Create PR requires your own provider API key because the model runs in the sandbox. Review Code keeps the model key in Sim and can use either BYOK or a hosted key.
Expand Down Expand Up @@ -204,7 +236,7 @@ export const PiBlock: BlockConfig<PiResponse> = {
paramVisibility: 'user-only',
placeholder: 'GitHub personal access token',
tooltip:
'Personal access token used for GitHub access. Create PR needs clone/push/PR permissions; Review Code needs clone + review permissions.',
'Personal access token used for GitHub access. Create PR needs clone/push/PR permissions; with Babysit Mode it also needs check/Actions reads, thread writes, and issue comments. Review Code needs clone + review permissions.',
required: true,
condition: CLOUD_ANY,
},
Expand All @@ -216,6 +248,27 @@ export const PiBlock: BlockConfig<PiResponse> = {
tooltip: 'The branch the pull request is opened against; the repo is cloned from it too.',
condition: CLOUD,
},
{
id: 'babysitMode',
title: 'Babysit Mode',
type: 'switch',
defaultValue: false,
description:
'Create the PR ready for review, request the configured bot reviews, and fix trusted feedback and required checks in bounded rounds.',
condition: CLOUD,
},
{
id: 'reviewMentions',
title: 'Reviewer Mentions',
type: 'short-input',
defaultValue: '',
placeholder: '@greptile, @cursor review',
tooltip:
'Required comma-separated issue comments. Each is posted after PR creation and again after every pushed Babysit fix.',
hideDividerBefore: true,
required: CLOUD_WITH_BABYSIT,
condition: CLOUD_WITH_BABYSIT,
},
{
id: 'branchName',
title: 'Branch Name',
Expand All @@ -230,7 +283,7 @@ export const PiBlock: BlockConfig<PiResponse> = {
type: 'switch',
defaultValue: true,
mode: 'advanced',
condition: CLOUD,
condition: getCloudWithoutBabysitCondition,
},
{
id: 'prTitle',
Expand Down Expand Up @@ -269,6 +322,16 @@ export const PiBlock: BlockConfig<PiResponse> = {
'How GitHub records the submitted review. Comment is neutral; Request changes marks the pull request as changes requested.',
condition: CLOUD_REVIEW,
},
{
id: 'maxRounds',
title: 'Maximum Rounds',
type: 'short-input',
defaultValue: '3',
placeholder: '3',
tooltip: 'Maximum number of agent fixing rounds, from 1 to 10.',
mode: 'advanced',
condition: CLOUD_WITH_BABYSIT,
},

{
id: 'host',
Expand Down Expand Up @@ -464,15 +527,28 @@ export const PiBlock: BlockConfig<PiResponse> = {
},
task: { type: 'string', description: 'Instruction for the coding agent' },
model: { type: 'string', description: 'AI model to use' },
owner: { type: 'string', description: 'GitHub repository owner (Create PR and Review Code)' },
repo: { type: 'string', description: 'GitHub repository name (Create PR and Review Code)' },
githubToken: { type: 'string', description: 'GitHub token (Create PR and Review Code)' },
owner: { type: 'string', description: 'GitHub repository owner' },
repo: { type: 'string', description: 'GitHub repository name' },
githubToken: { type: 'string', description: 'GitHub token' },
baseBranch: { type: 'string', description: 'Base branch for the PR (Create PR)' },
branchName: { type: 'string', description: 'Branch to create (Create PR)' },
draft: { type: 'boolean', description: 'Open the PR as a draft (Create PR)' },
prTitle: { type: 'string', description: 'Pull request title (Create PR)' },
prBody: { type: 'string', description: 'Pull request body (Create PR)' },
babysitMode: {
type: 'boolean',
description: 'Create the PR and babysit trusted bot reviews and required checks',
},
pullNumber: { type: 'number', description: 'Pull request number (Review Code)' },
maxRounds: {
type: 'number',
description: 'Maximum Create PR Babysit fixing rounds (1-10)',
},
reviewMentions: {
type: 'string',
description:
'Required comma-separated bot review comments posted initially and after Babysit pushes',
},
reviewEvent: {
type: 'string',
description: 'GitHub review event: COMMENT or REQUEST_CHANGES',
Expand Down Expand Up @@ -524,6 +600,36 @@ export const PiBlock: BlockConfig<PiResponse> = {
description: 'Number of inline review comments posted',
condition: CLOUD_REVIEW,
},
rounds: {
type: 'number',
description: 'Babysit fixing rounds consumed',
condition: CLOUD_WITH_BABYSIT,
},
threadsClean: {
type: 'boolean',
description: 'Whether all actionable review threads are resolved',
condition: CLOUD_WITH_BABYSIT,
},
checksGreen: {
type: 'boolean',
description: 'Whether required checks are green with none pending',
condition: CLOUD_WITH_BABYSIT,
},
threadsResolved: {
type: 'number',
description: 'Review threads resolved by Babysit',
condition: CLOUD_WITH_BABYSIT,
},
commitsPushed: {
type: 'number',
description: 'Commits pushed by Babysit',
condition: CLOUD_WITH_BABYSIT,
},
stopReason: {
type: 'string',
description: 'Why the Babysit run stopped',
condition: CLOUD_WITH_BABYSIT,
},
tokens: { type: 'json', description: 'Token usage statistics' },
cost: { type: 'json', description: 'Cost of the run' },
providerTiming: { type: 'json', description: 'Provider timing information' },
Expand Down
1 change: 1 addition & 0 deletions apps/sim/blocks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ export interface SubBlockConfig {
connectionDroppable?: boolean
hidden?: boolean
hideFromPreview?: boolean // Hide this subblock from the workflow block preview
hideDividerBefore?: boolean // Visually group this field with the preceding visible subblock
showWhenEnvSet?: string // Show this subblock only when the named NEXT_PUBLIC_ env var is truthy
hideWhenHosted?: boolean // Hide this subblock when running on hosted sim
hideWhenEnvSet?: string // Hide this subblock when the named NEXT_PUBLIC_ env var is truthy
Expand Down
Loading
Loading