Skip to content

Commit 9ad1c66

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
Move Babysit into Create PR
1 parent 2dc74ef commit 9ad1c66

19 files changed

Lines changed: 858 additions & 570 deletions

File tree

.agents/plans/pi-babysit-mode.plan.md

Lines changed: 44 additions & 217 deletions
Large diffs are not rendered by default.

apps/docs/content/docs/en/workflows/blocks/pi.mdx

Lines changed: 55 additions & 65 deletions
Large diffs are not rendered by default.

apps/sim/blocks/blocks/pi.test.ts

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import { describe, expect, it, vi } from 'vitest'
77
// deliberately does not import it — no block imports from `@/executor`. This test is what ties the
88
// two copies together, so adding a provider to one and not the other fails here.
99
vi.mock('@/lib/api-key/byok', () => ({ getBYOKKey: vi.fn(), getApiKeyWithBYOK: vi.fn() }))
10+
vi.mock('@/lib/core/config/env', async (importOriginal) => {
11+
const original = await importOriginal<typeof import('@/lib/core/config/env')>()
12+
return {
13+
...original,
14+
getEnv: vi.fn((key: string) => (key === 'NEXT_PUBLIC_E2B_ENABLED' ? 'true' : undefined)),
15+
isTruthy: vi.fn((value: unknown) => value === 'true'),
16+
}
17+
})
1018

1119
import { evaluateSubBlockCondition } from '@/lib/workflows/subblocks/visibility'
1220
import { PiBlock } from '@/blocks/blocks/pi'
@@ -66,21 +74,50 @@ describe('Pi block search fields', () => {
6674
})
6775
})
6876

69-
describe('Pi Babysit block surface', () => {
70-
it('declares bounded-round inputs and all result outputs', () => {
77+
describe('Pi Create PR Babysit surface', () => {
78+
it('offers exactly Create PR, Review Code, and Local Dev as top-level modes', () => {
79+
const mode = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'mode')
80+
const options =
81+
typeof mode?.options === 'function'
82+
? mode.options()
83+
: (mode?.options as Array<{ id: string }> | undefined)
84+
85+
expect(options?.map(({ id }) => id)).toEqual(['cloud', 'cloud_review', 'local'])
86+
})
87+
88+
it('declares the toggle, required reviewer mentions, advanced rounds, and result outputs', () => {
89+
const toggle = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'babysitMode')
7190
const maxRounds = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'maxRounds')
7291
const mentions = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'reviewMentions')
7392

93+
expect(toggle).toMatchObject({
94+
type: 'switch',
95+
defaultValue: false,
96+
condition: { field: 'mode', value: 'cloud' },
97+
})
7498
expect(maxRounds).toMatchObject({
7599
type: 'short-input',
76100
defaultValue: '3',
77-
condition: { field: 'mode', value: 'babysit' },
101+
mode: 'advanced',
102+
condition: {
103+
field: 'mode',
104+
value: 'cloud',
105+
and: { field: 'babysitMode', value: true },
106+
},
78107
})
79108
expect(mentions).toMatchObject({
80109
type: 'short-input',
81110
defaultValue: '',
82-
mode: 'advanced',
83-
condition: { field: 'mode', value: 'babysit' },
111+
required: {
112+
field: 'mode',
113+
value: 'cloud',
114+
and: { field: 'babysitMode', value: true },
115+
},
116+
condition: {
117+
field: 'mode',
118+
value: 'cloud',
119+
and: { field: 'babysitMode', value: true },
120+
},
84121
})
85122
for (const output of [
86123
'rounds',
@@ -91,20 +128,32 @@ describe('Pi Babysit block surface', () => {
91128
'stopReason',
92129
]) {
93130
expect(PiBlock.outputs[output]).toMatchObject({
94-
condition: { field: 'mode', value: 'babysit' },
131+
condition: {
132+
field: 'mode',
133+
value: 'cloud',
134+
and: { field: 'babysitMode', value: true },
135+
},
95136
})
96137
}
97138
})
98139

99-
it('makes task optional only for Babysit and exposes skills but not tools or memory', () => {
140+
it('requires a task and hides Draft PR while Babysit Mode is enabled', () => {
100141
const task = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'task')
142+
const draft = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'draft')
101143
const skills = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'skills')
102144
const tools = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'tools')
103145
const memory = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'memoryType')
146+
const pullNumber = PiBlock.subBlocks.find((subBlock) => subBlock.id === 'pullNumber')
104147

105-
expect(task?.required).toEqual({ field: 'mode', value: 'babysit', not: true })
106-
expect(evaluateSubBlockCondition(skills?.condition, { mode: 'babysit' })).toBe(true)
107-
expect(evaluateSubBlockCondition(tools?.condition, { mode: 'babysit' })).toBe(false)
108-
expect(evaluateSubBlockCondition(memory?.condition, { mode: 'babysit' })).toBe(false)
148+
expect(task?.required).toBe(true)
149+
expect(evaluateSubBlockCondition(draft?.condition, { mode: 'cloud' })).toBe(true)
150+
expect(evaluateSubBlockCondition(draft?.condition, { mode: 'cloud', babysitMode: true })).toBe(
151+
false
152+
)
153+
expect(evaluateSubBlockCondition(skills?.condition, { mode: 'cloud' })).toBe(true)
154+
expect(evaluateSubBlockCondition(tools?.condition, { mode: 'cloud' })).toBe(false)
155+
expect(evaluateSubBlockCondition(memory?.condition, { mode: 'cloud' })).toBe(true)
156+
expect(evaluateSubBlockCondition(pullNumber?.condition, { mode: 'cloud' })).toBe(false)
157+
expect(evaluateSubBlockCondition(pullNumber?.condition, { mode: 'cloud_review' })).toBe(true)
109158
})
110159
})

apps/sim/blocks/blocks/pi.ts

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,32 @@ const CLOUD_REVIEW: { field: 'mode'; value: 'cloud_review' } = {
4848
field: 'mode',
4949
value: 'cloud_review',
5050
}
51-
const BABYSIT: { field: 'mode'; value: 'babysit' } = { field: 'mode', value: 'babysit' }
52-
const CLOUD_ANY: { field: 'mode'; value: Array<'cloud' | 'cloud_review' | 'babysit'> } = {
51+
const CLOUD_ANY: { field: 'mode'; value: Array<'cloud' | 'cloud_review'> } = {
5352
field: 'mode',
54-
value: ['cloud', 'cloud_review', 'babysit'],
53+
value: ['cloud', 'cloud_review'],
5554
}
56-
const EXISTING_PR: { field: 'mode'; value: Array<'cloud_review' | 'babysit'> } = {
55+
const CLOUD_WITH_BABYSIT: {
56+
field: 'mode'
57+
value: 'cloud'
58+
and: { field: 'babysitMode'; value: true }
59+
} = {
5760
field: 'mode',
58-
value: ['cloud_review', 'babysit'],
61+
value: 'cloud',
62+
and: { field: 'babysitMode', value: true },
63+
}
64+
const CLOUD_WITHOUT_BABYSIT: {
65+
field: 'mode'
66+
value: 'cloud'
67+
and: { field: 'babysitMode'; value: true; not: true }
68+
} = {
69+
field: 'mode',
70+
value: 'cloud',
71+
and: { field: 'babysitMode', value: true, not: true },
5972
}
6073
const LOCAL: { field: 'mode'; value: 'local' } = { field: 'mode', value: 'local' }
61-
const AUTHORING_MODES: { field: 'mode'; value: Array<'cloud' | 'local' | 'babysit'> } = {
74+
const AUTHORING_MODES: { field: 'mode'; value: Array<'cloud' | 'local'> } = {
6275
field: 'mode',
63-
value: ['cloud', 'local', 'babysit'],
76+
value: ['cloud', 'local'],
6477
}
6578
const MEMORY_MODES: { field: 'mode'; value: Array<'cloud' | 'local'> } = {
6679
field: 'mode',
@@ -100,13 +113,13 @@ export const PiBlock: BlockConfig<PiResponse> = {
100113
description: 'Run an autonomous coding agent on a repo',
101114
authMode: AuthMode.ApiKey,
102115
longDescription:
103-
'The Pi Coding Agent runs the Pi harness against a real repository. Create PR opens a new pull request, Review Code posts a structured review, Babysit drives an existing pull request through trusted review threads and required checks in bounded rounds, and Local Dev edits files over SSH. Create PR, Babysit, and Local Dev can reuse skills; only Create PR and Local Dev use conversation memory. Any mode can optionally get one web_search tool backed by your own key.',
116+
'The Pi Coding Agent runs the Pi harness against a real repository. Create PR opens a new pull request and can optionally babysit it through trusted bot reviews and required checks, Review Code posts a structured review, and Local Dev edits files over SSH. Create PR and Local Dev can reuse skills and conversation memory. Any mode can optionally get one web_search tool backed by your own key.',
104117
bestPractices: `
105118
- Use Create PR for hands-off changes against a GitHub repo where a reviewable PR is the deliverable.
119+
- Enable Babysit Mode on Create PR when trusted review bots and required checks should be monitored and fixed in bounded rounds.
106120
- Use Review Code to analyze an existing PR and leave summary + inline review comments.
107-
- Use Babysit to fix and answer trusted review threads and required checks on an existing same-repository PR.
108121
- 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.
109-
- Create PR and Babysit require 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.
122+
- 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.
110123
- Internet Search is off by default and always needs your own key for the selected provider, from the block field or Settings > BYOK. Leave it on None unless the task genuinely needs external information.
111124
`,
112125
category: 'blocks',
@@ -118,7 +131,7 @@ export const PiBlock: BlockConfig<PiResponse> = {
118131
id: 'mode',
119132
title: 'Mode',
120133
type: 'dropdown',
121-
/** Create PR, Review Code, and Babysit require E2B and stay hidden when it is disabled. */
134+
/** Create PR and Review Code require E2B and stay hidden when it is disabled. */
122135
value: () => (isTruthy(getEnv('NEXT_PUBLIC_E2B_ENABLED')) ? 'cloud' : 'local'),
123136
options: () => {
124137
const options = [
@@ -139,11 +152,6 @@ export const PiBlock: BlockConfig<PiResponse> = {
139152
label: 'Review Code',
140153
id: 'cloud_review',
141154
description: 'Reviews an existing PR and posts GitHub review comments',
142-
},
143-
{
144-
label: 'Babysit',
145-
id: 'babysit',
146-
description: 'Fixes review threads and failing checks on an existing PR',
147155
}
148156
)
149157
}
@@ -155,7 +163,7 @@ export const PiBlock: BlockConfig<PiResponse> = {
155163
title: 'Task',
156164
type: 'long-input',
157165
placeholder: 'Describe what the coding agent should do...',
158-
required: { field: 'mode', value: 'babysit', not: true },
166+
required: true,
159167
},
160168
{
161169
id: 'model',
@@ -217,7 +225,7 @@ export const PiBlock: BlockConfig<PiResponse> = {
217225
paramVisibility: 'user-only',
218226
placeholder: 'GitHub personal access token',
219227
tooltip:
220-
'Personal access token used for GitHub access. Create PR needs clone/push/PR permissions; Review Code needs clone + review permissions; Babysit also needs check/Actions reads, thread writes, issue comments, and push access.',
228+
'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.',
221229
required: true,
222230
condition: CLOUD_ANY,
223231
},
@@ -229,6 +237,26 @@ export const PiBlock: BlockConfig<PiResponse> = {
229237
tooltip: 'The branch the pull request is opened against; the repo is cloned from it too.',
230238
condition: CLOUD,
231239
},
240+
{
241+
id: 'babysitMode',
242+
title: 'Babysit Mode',
243+
type: 'switch',
244+
defaultValue: false,
245+
description:
246+
'Create the PR ready for review, request the configured bot reviews, and fix trusted feedback and required checks in bounded rounds.',
247+
condition: CLOUD,
248+
},
249+
{
250+
id: 'reviewMentions',
251+
title: 'Reviewer Mentions',
252+
type: 'short-input',
253+
defaultValue: '',
254+
placeholder: '@greptile, @cursor review',
255+
tooltip:
256+
'Required comma-separated issue comments. Each is posted after PR creation and again after every pushed Babysit fix.',
257+
required: CLOUD_WITH_BABYSIT,
258+
condition: CLOUD_WITH_BABYSIT,
259+
},
232260
{
233261
id: 'branchName',
234262
title: 'Branch Name',
@@ -243,7 +271,7 @@ export const PiBlock: BlockConfig<PiResponse> = {
243271
type: 'switch',
244272
defaultValue: true,
245273
mode: 'advanced',
246-
condition: CLOUD,
274+
condition: CLOUD_WITHOUT_BABYSIT,
247275
},
248276
{
249277
id: 'prTitle',
@@ -267,7 +295,7 @@ export const PiBlock: BlockConfig<PiResponse> = {
267295
type: 'short-input',
268296
placeholder: 'e.g., 42',
269297
required: true,
270-
condition: EXISTING_PR,
298+
condition: CLOUD_REVIEW,
271299
},
272300
{
273301
id: 'reviewEvent',
@@ -289,18 +317,8 @@ export const PiBlock: BlockConfig<PiResponse> = {
289317
defaultValue: '3',
290318
placeholder: '3',
291319
tooltip: 'Maximum number of agent fixing rounds, from 1 to 10.',
292-
condition: BABYSIT,
293-
},
294-
{
295-
id: 'reviewMentions',
296-
title: 'Re-review Mentions',
297-
type: 'short-input',
298-
defaultValue: '',
299-
placeholder: '@greptile, @cursor review',
300-
tooltip:
301-
'Comma-separated issue comments to post after a pushed fix. Leave empty to skip requesting and waiting for re-review.',
302320
mode: 'advanced',
303-
condition: BABYSIT,
321+
condition: CLOUD_WITH_BABYSIT,
304322
},
305323

306324
{
@@ -493,7 +511,7 @@ export const PiBlock: BlockConfig<PiResponse> = {
493511
inputs: {
494512
mode: {
495513
type: 'string',
496-
description: 'Execution mode: Create PR, Review Code, Babysit, or Local Dev',
514+
description: 'Execution mode: Create PR, Review Code, or Local Dev',
497515
},
498516
task: { type: 'string', description: 'Instruction for the coding agent' },
499517
model: { type: 'string', description: 'AI model to use' },
@@ -505,11 +523,19 @@ export const PiBlock: BlockConfig<PiResponse> = {
505523
draft: { type: 'boolean', description: 'Open the PR as a draft (Create PR)' },
506524
prTitle: { type: 'string', description: 'Pull request title (Create PR)' },
507525
prBody: { type: 'string', description: 'Pull request body (Create PR)' },
508-
pullNumber: { type: 'number', description: 'Pull request number (Review Code or Babysit)' },
509-
maxRounds: { type: 'number', description: 'Maximum Babysit fixing rounds (1-10)' },
526+
babysitMode: {
527+
type: 'boolean',
528+
description: 'Create the PR and babysit trusted bot reviews and required checks',
529+
},
530+
pullNumber: { type: 'number', description: 'Pull request number (Review Code)' },
531+
maxRounds: {
532+
type: 'number',
533+
description: 'Maximum Create PR Babysit fixing rounds (1-10)',
534+
},
510535
reviewMentions: {
511536
type: 'string',
512-
description: 'Comma-separated issue comments requesting re-review after a Babysit push',
537+
description:
538+
'Required comma-separated bot review comments posted initially and after Babysit pushes',
513539
},
514540
reviewEvent: {
515541
type: 'string',
@@ -565,32 +591,32 @@ export const PiBlock: BlockConfig<PiResponse> = {
565591
rounds: {
566592
type: 'number',
567593
description: 'Babysit fixing rounds consumed',
568-
condition: BABYSIT,
594+
condition: CLOUD_WITH_BABYSIT,
569595
},
570596
threadsClean: {
571597
type: 'boolean',
572598
description: 'Whether all actionable review threads are resolved',
573-
condition: BABYSIT,
599+
condition: CLOUD_WITH_BABYSIT,
574600
},
575601
checksGreen: {
576602
type: 'boolean',
577603
description: 'Whether required checks are green with none pending',
578-
condition: BABYSIT,
604+
condition: CLOUD_WITH_BABYSIT,
579605
},
580606
threadsResolved: {
581607
type: 'number',
582608
description: 'Review threads resolved by Babysit',
583-
condition: BABYSIT,
609+
condition: CLOUD_WITH_BABYSIT,
584610
},
585611
commitsPushed: {
586612
type: 'number',
587613
description: 'Commits pushed by Babysit',
588-
condition: BABYSIT,
614+
condition: CLOUD_WITH_BABYSIT,
589615
},
590616
stopReason: {
591617
type: 'string',
592618
description: 'Why the Babysit run stopped',
593-
condition: BABYSIT,
619+
condition: CLOUD_WITH_BABYSIT,
594620
},
595621
tokens: { type: 'json', description: 'Token usage statistics' },
596622
cost: { type: 'json', description: 'Cost of the run' },

0 commit comments

Comments
 (0)