@@ -62,12 +62,12 @@ Commands:
6262 --dry-run --force --no-interactive
6363 align <name> Recurring check-in to refine an existing agent
6464 Flags: --provider <p> --model <m> --agent-dir <dir>
65- run <name> <prompt> Run one short-lived task for the named agent (embeds pi
66- via its SDK — fresh session, one prompt, exit)
65+ run <name> Run the agent PERSISTENTLY (on-duty) — one warm pi session
66+ that stays up, loading bob.yaml capabilities (discord
67+ gateway, cron). This is what the service unit runs.
68+ run <name> <prompt> Run ONE short-lived task (claude -p style) — minimal +
69+ ephemeral, no gateway. Prints the response, exits.
6770 Flags: --model <m> (--interactive: coming in a later PR)
68- serve <name> Run the agent PERSISTENTLY — one warm pi session that stays
69- up, loading the agent's bob.yaml capabilities (incl. discord).
70- This is what the service unit runs. Flags: --model <m>
7171 install-service <n> Write the agent's service unit (launchd on macOS / systemd
7272 user unit on Linux) so it self-runs. Flags: --bob-bin <abs path> --model <m>
7373 up <name> Load + start the agent's service unit
@@ -167,44 +167,37 @@ async function run(
167167 flags : Record < string , string | boolean > ,
168168) : Promise < number > {
169169 const model = flags . model !== undefined && flags . model !== true ? String ( flags . model ) : undefined ;
170- // PR1 migrated `bob run` to pi's embedded SDK for the non-interactive prompt
171- // path only. The interactive REPL on the SDK lands in a later phase-1 PR.
170+ // The interactive REPL on the SDK lands in a later phase-1 PR.
172171 if ( flags . interactive === true ) {
173172 console . error (
174- "bob run: --interactive is not yet supported on the embedded-SDK path (use a prompt for now)" ,
173+ "bob run: --interactive is not yet supported on the embedded-SDK path (give a task prompt for now)" ,
175174 ) ;
176175 return 2 ;
177176 }
177+ // Lifespan is the only knob (`serve` is retired): a task prompt = a MINIMAL
178+ // one-shot; NO prompt = the agent runs PERSISTENTLY (on-duty).
178179 if ( prompt === undefined ) {
179- console . error ( 'bob run: a prompt is required, e.g. `bob run <name> "summarize my inbox"`' ) ;
180- return 2 ;
180+ // PERSISTENT: one warm pi session that stays up, loading the agent's
181+ // bob.yaml capabilities (discord's inbound gateway, cron, …) and posting
182+ // back via them. This is what the service unit invokes. Blocks until
183+ // SIGTERM/SIGINT — runPersistent disposes the session gracefully (await
184+ // in-flight turn → dispose → exit 0); KeepAlive/Restart relaunches it.
185+ await runPersistent ( { name , model } ) ;
186+ return 0 ;
181187 }
182- // captureStdout collects the assistant's final text into result.stdout.
183- // runAgent itself is silent (it accumulates deltas, never writes to console),
184- // so without this `bob run` printed nothing — print the response here .
188+ // ONE-SHOT TASK (claude -p style) — minimal + ephemeral (no gateway; see
189+ // BOB_PERSISTENT in run.ts). captureStdout collects the assistant's final
190+ // text (runAgent is otherwise silent), so we print the response.
185191 const result = await runAgent ( { name, prompt, model, captureStdout : true } ) ;
186192 if ( result . stdout && result . stdout . trim ( ) . length > 0 ) {
187193 console . log ( result . stdout ) ;
188194 }
189195 return result . exitCode ;
190196}
191197
192- // `bob serve <name>` — run the agent PERSISTENTLY. ONE warm pi AgentSession that
193- // stays up, loading the agent's bob.yaml `capabilities:` (incl. discord, whose
194- // gateway listener feeds inbound messages via pi.sendUserMessage and routes
195- // replies back to the originating channel). This is the entrypoint the launchd
196- // unit runs. Discord is no longer a CLI flag — it's a declared capability.
197- //
198- // Blocks until SIGTERM/SIGINT, which runPersistent handles gracefully (await
199- // in-flight turn → session.dispose() → exit 0). KeepAlive relaunches it.
200- async function serve ( name : string , flags : Record < string , string | boolean > ) : Promise < void > {
201- const model = flags . model !== undefined && flags . model !== true ? String ( flags . model ) : undefined ;
202- await runPersistent ( { name, model } ) ;
203- }
204-
205198// `bob install-service <name>` — write the agent's launchd plist so it self-runs
206199// (KeepAlive + RunAtLoad). Does NOT start it — that's `bob up`. The plist runs
207- // `bob serve <name>`; it embeds NO secrets (the discord token is read from the
200+ // `bob run <name>`; it embeds NO secrets (the discord token is read from the
208201// file path in bob.yaml at runtime).
209202async function installServiceCmd (
210203 name : string ,
@@ -219,7 +212,7 @@ async function installServiceCmd(
219212 const model = flags . model !== undefined && flags . model !== true ? String ( flags . model ) : undefined ;
220213 const { path : written } = await installService ( { name, bobBin, model } ) ;
221214 console . log ( `[bob install-service] wrote ${ written } ` ) ;
222- console . log ( ` runs: ${ bobBin } serve ${ name } ` ) ;
215+ console . log ( ` runs: ${ bobBin } run ${ name } ` ) ;
223216 console . log ( ` next: bob up ${ name } (load + start)` ) ;
224217 if ( bobBin === "bob" ) {
225218 console . error (
@@ -294,13 +287,6 @@ async function main(): Promise<number> {
294287 const prompt = args . positional . slice ( 1 ) . join ( " " ) || undefined ;
295288 return await run ( args . positional [ 0 ] , prompt , args . flags ) ;
296289 }
297- case "serve" :
298- if ( ! args . positional [ 0 ] ) {
299- console . error ( "bob serve: missing <name>" ) ;
300- return 2 ;
301- }
302- await serve ( args . positional [ 0 ] , args . flags ) ;
303- return 0 ;
304290 case "install-service" :
305291 if ( ! args . positional [ 0 ] ) {
306292 console . error ( "bob install-service: missing <name>" ) ;
0 commit comments