@a-t-h-i/bot-lobby 0.6.53 → 0.6.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/prompts/expert.md +17 -0
  4. package/prompts/goal-check.md +44 -0
  5. package/prompts/master.md +70 -32
  6. package/prompts/worker.md +25 -11
  7. package/src/desk/board-client.ts +65 -0
  8. package/src/desk/board.ts +200 -0
  9. package/src/desk/client-extension.ts +31 -1
  10. package/src/desk/ipc.ts +16 -4
  11. package/src/desk/session.ts +129 -1
  12. package/src/execution/agent-runner.ts +27 -132
  13. package/src/execution/delegation.ts +112 -38
  14. package/src/execution/live-run.ts +3 -14
  15. package/src/execution/pi-runner.ts +30 -163
  16. package/src/execution/worker-trees.ts +396 -0
  17. package/src/index.ts +6 -0
  18. package/src/lobby/feed.ts +1 -1
  19. package/src/lobby/host.ts +2 -0
  20. package/src/lobby/planner.ts +2 -2
  21. package/src/lobby/pr-review.ts +3 -3
  22. package/src/lobby/quickfix.ts +4 -3
  23. package/src/lobby/recovery.ts +2 -10
  24. package/src/lobby/runtime.ts +1 -1
  25. package/src/lobby/service.ts +3 -1
  26. package/src/master/goal-check.ts +114 -0
  27. package/src/master/master.ts +60 -24
  28. package/src/master/research.ts +3 -6
  29. package/src/pi/commands.ts +5 -45
  30. package/src/pi/delegation.ts +23 -4
  31. package/src/pi/events.ts +6 -19
  32. package/src/pi/grants.ts +89 -0
  33. package/src/pi/model-settings.ts +2 -4
  34. package/src/pi/model-support.ts +12 -5
  35. package/src/pi/route.ts +1 -1
  36. package/src/pi/run-summary.ts +3 -7
  37. package/src/pi/start-flags.ts +3 -3
  38. package/src/pi/start-task.ts +2 -8
  39. package/src/pi/tools.ts +10 -12
  40. package/src/roles/worker.ts +0 -18
  41. package/src/schemas/configuration.ts +81 -39
  42. package/src/schemas/findings.ts +0 -12
  43. package/src/schemas/task.ts +12 -1
  44. package/src/state/metrics.ts +1 -1
  45. package/src/state/work-clock.ts +140 -0
  46. package/src/state/work-time.ts +4 -7
  47. package/src/webui/api/index.ts +2 -1
  48. package/src/webui/api/settings.ts +6 -18
  49. package/src/webui/dev/fake-service.ts +14 -0
  50. package/src/webui/protocol.ts +23 -0
  51. package/src/workflow/workflow.ts +204 -344
  52. package/webui/dist/assets/{TerminalView-CbRc77C8.js → TerminalView-CIfjkb5H.js} +1 -1
  53. package/webui/dist/assets/index-BkXhtB6A.css +2 -0
  54. package/webui/dist/assets/index-DIM2-FUQ.js +114 -0
  55. package/webui/dist/build.json +10 -6
  56. package/webui/dist/index.html +2 -2
  57. package/src/state/budget.ts +0 -301
  58. package/src/state/step-limits.ts +0 -41
  59. package/webui/dist/assets/index-Cca5uEVs.js +0 -115
  60. package/webui/dist/assets/index-DsVdXmh5.css +0 -2
@@ -8,6 +8,7 @@ import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
8
8
  import { tmpdir } from "node:os";
9
9
  import { basename, join } from "node:path";
10
10
  import { activityWord } from "../pi/activity.ts";
11
+ import { skillFiles } from "../pi/grants.ts";
11
12
  import { shortDuration } from "../text.ts";
12
13
  import { readRelayRequest } from "../ask/relay.ts";
13
14
  import type { AskQuestion, AskResult } from "../ask/types.ts";
@@ -29,7 +30,9 @@ export interface PiRunOptions {
29
30
  effortProfile?: RunProfileLike;
30
31
  agent?: { runId: string; taskId: string; domain: Domain; role: Role; depth?: number };
31
32
  mcpTools?: string[];
32
- delegationQuota?: { used: number };
33
+ delegationQuota?: { used: number; consults?: number };
34
+ /** The model and thinking ask_expert consults on, resolved by the oracle (its session model when settings name none). */
35
+ expert?: { model?: string; thinking: string };
33
36
  onChildRun?: (run: AgentRun) => void;
34
37
  cwd: string;
35
38
  task: string;
@@ -37,7 +40,8 @@ export interface PiRunOptions {
37
40
  tools?: readonly string[];
38
41
  model?: string;
39
42
  thinking?: string;
40
- timeoutMs: number;
43
+ /** Names of the skills this agent may load; absent keeps pi's own discovery. */
44
+ skills?: string[];
41
45
  signal?: AbortSignal;
42
46
  /** Reports the current one-word tool activity while the agent works. */
43
47
  onActivity?: (activity: string) => void;
@@ -51,52 +55,21 @@ export interface PiRunOptions {
51
55
  stallTimeoutMs?: number;
52
56
  /** Silence allowed while one tool call is in flight (tests, builds); 0 disables. */
53
57
  toolStallTimeoutMs?: number;
54
- /** Ask the agent to wrap up and report at this elapsed time; 0 disables. */
55
- wrapUpAtMs?: number;
56
58
  /** Receives the steering handle once the process is running. */
57
59
  onStart?: (handle: RunHandle) => void;
58
- /** Under a task time budget: when the agent is warned, when its time is up, and who decides on more (see ProcessRunOptions). */
59
- time?: TimeLimit;
60
60
  /** Answers the agent's relayed questions; without it they are cancelled like any other dialog. */
61
61
  onAsk?: RelayAsk;
62
62
  }
63
63
 
64
- /**
65
- * A run's allotted time under a task budget. At `headsUpAtMs` the agent is
66
- * told how much is left; at `upAtMs` it is steered to stop and report where it
67
- * left off. Once that report is in (its turn ended), `onTimeUp` decides: more
68
- * ms keeps the same process going with its context (a follow-up prompt), 0
69
- * ends it. The hard deadline (`timeoutMs`) and the stall watchdog wait while
70
- * the decision is pending, and move out with any time granted.
71
- */
72
- export interface TimeLimit {
73
- /** Reporting time is included in the allowance, including extensions. */
74
- hardLimit?: boolean;
75
- upAtMs: number;
76
- headsUpAtMs?: number;
77
- headsUpMessage?: string;
78
- upMessage: string;
79
- onTimeUp?: (report: string) => Promise<{ extraMs: number; message?: string }>;
80
- /** Time the agent gets to write its report after `upAtMs` before it is stopped. */
81
- graceMs: number;
82
- }
83
-
84
64
  export interface ProcessOutcome {
85
65
  exitCode: number;
86
66
  stdout: string;
87
67
  stderr: string;
88
68
  killed: boolean;
89
- timedOut: boolean;
90
69
  /** Killed by the watchdog after going silent. */
91
70
  stalled?: boolean;
92
- /** The agent was asked to wrap up before its deadline. */
93
- wrappedUp?: boolean;
94
71
  /** The RPC host rejected the prompt itself. */
95
72
  protocolError?: string;
96
- /** Stopped at its allotted time and not given more: its report says where it left off. */
97
- timeUp?: boolean;
98
- /** Time granted past its allotment, in ms. */
99
- extendedMs?: number;
100
73
  }
101
74
 
102
75
  /** Streaming agent events forwarded as they arrive on stdout. */
@@ -111,14 +84,9 @@ export type PiStreamEvent =
111
84
  | { type: "compaction" }
112
85
  | { type: "compaction_end"; error?: string }
113
86
  | { type: "usage"; input: number; output: number; cost: number; model?: string }
114
- | { type: "wrap_up" }
115
- /** Its allotted time is up: it was asked to stop and report where it left off. */
116
- | { type: "time_up" }
117
- /** It was given `ms` more and carries on. */
118
- | { type: "extended"; ms: number }
119
87
  /** One finished thinking block, bounded to `MAX_THOUGHT_CHARS`. */
120
88
  | { type: "thought"; text: string }
121
- /** It asked the user (relayed); its clocks wait until the answers are back. */
89
+ /** It asked the user (relayed); the stall watchdog waits until the answers are back. */
122
90
  | { type: "asking"; questions: number }
123
91
  /** The answers went back after `waitedMs` of waiting on the user. */
124
92
  | { type: "answered"; waitedMs: number }
@@ -130,7 +98,6 @@ export const MAX_THOUGHT_CHARS = 1500;
130
98
  export interface ProcessRunOptions {
131
99
  cwd: string;
132
100
  signal?: AbortSignal;
133
- timeoutMs: number;
134
101
  /** The task message sent to the agent over RPC stdin. */
135
102
  prompt?: string;
136
103
  /** Optional sink for streaming agent events. */
@@ -138,12 +105,7 @@ export interface ProcessRunOptions {
138
105
  env?: Record<string, string>;
139
106
  stallTimeoutMs?: number;
140
107
  toolStallTimeoutMs?: number;
141
- wrapUpAtMs?: number;
142
- wrapUpMessage?: string;
143
108
  onStart?: (handle: RunHandle) => void;
144
- /** Grace between the deadline abort and the hard kill. */
145
- graceMs?: number;
146
- time?: TimeLimit;
147
109
  onAsk?: RelayAsk;
148
110
  }
149
111
 
@@ -160,9 +122,6 @@ export interface PiRunResult {
160
122
  usage: { input: number; output: number; cost: number; turns: number };
161
123
  model?: string;
162
124
  stalled?: boolean;
163
- wrappedUp?: boolean;
164
- timeUp?: boolean;
165
- extendedMs?: number;
166
125
  }
167
126
 
168
127
  export interface ParsedStream {
@@ -177,17 +136,8 @@ const MAX_STREAM_CHARS = 5_000_000;
177
136
  const HEARTBEAT_MS = 2000;
178
137
  const SETTLE_FALLBACK_MS = 3000;
179
138
  const EXIT_DRAIN_MS = 1500;
180
- const DEADLINE_GRACE_MS = 5000;
181
139
  const DIALOG_METHODS = new Set(["select", "confirm", "input", "editor"]);
182
140
 
183
- /** The steering message sent at the wrap-up point. */
184
- export const WRAP_UP_MESSAGE = [
185
- "bot-lobby: you are close to your time limit.",
186
- "Stop exploring now. Finish or revert any half-done edit so every file is consistent,",
187
- "then write your final report in the required output format.",
188
- "List anything unfinished under Blockers or Notes.",
189
- ].join(" ");
190
-
191
141
  /** RPC protocol lines the process host reacts to (never forwarded to the caller). */
192
142
  export interface ControlEvent {
193
143
  type: string;
@@ -366,10 +316,15 @@ export function finishedThought(line: string): string | undefined {
366
316
  export const MCP_RESOURCE_TOOLS: readonly string[] = ["list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource"];
367
317
 
368
318
  /** Build the `pi` argv for one isolated, headless RPC agent run; the task goes over stdin. */
369
- export function buildPiArgs(options: Omit<PiRunOptions, "task"> & { systemPromptFile?: string }): string[] {
319
+ export function buildPiArgs(options: Omit<PiRunOptions, "task"> & { systemPromptFile?: string; skillFiles?: string[] }): string[] {
370
320
  const args = ["--mode", "rpc", "--no-session", "--no-prompt-templates", "--no-themes", "--extension", fileURLToPath(new URL("../index.ts", import.meta.url))];
371
321
  if (options.model && options.model !== "inherit") args.push("--model", options.model);
372
322
  if (options.thinking) args.push("--thinking", options.thinking);
323
+ // A skill grant replaces pi's own discovery with exactly the granted skills.
324
+ if (options.skillFiles) {
325
+ args.push("--no-skills");
326
+ for (const file of options.skillFiles) args.push("--skill", file);
327
+ }
373
328
  if (options.tools && options.tools.length > 0) {
374
329
  // pi keeps every MCP tool under --tools unless an entry names one, so name the granted ones (and the resource
375
330
  // tools their servers need), or run without MCP at all when nothing is granted.
@@ -502,19 +457,11 @@ function rpcInput(proc: ChildProcess): { send(command: object): void; close(): v
502
457
 
503
458
  interface RunState {
504
459
  killed: boolean;
505
- timedOut: boolean;
506
460
  stalled: boolean;
507
- wrappedUp: boolean;
508
461
  settled: boolean;
509
462
  protocolError?: string;
510
- /** Told its time is up; its next turn end waits on the decision. */
511
- timeUp: boolean;
512
- headsUp: boolean;
513
- /** Waiting on the decision: the agent is idle, so the deadline and the stall watchdog wait too. */
514
- deciding: boolean;
515
- /** Relayed questions waiting on the user: every clock and the watchdog wait. */
463
+ /** Relayed questions waiting on the user: the watchdog waits. */
516
464
  asking: number;
517
- extendedMs: number;
518
465
  }
519
466
 
520
467
  /** Smallest positive limit, used to pick a watchdog cadence that can see it. */
@@ -526,11 +473,11 @@ function watchInterval(...limits: Array<number | undefined>): number {
526
473
 
527
474
  /**
528
475
  * Default runner: spawn pi in RPC mode, send the task over stdin, and watch it.
529
- * The child leads its own process group so a stall, deadline or cancel takes
530
- * its grandchildren (dev servers, watch-mode tests) with it, and the run ends
531
- * on process exit rather than waiting for a pipe a grandchild may hold open.
532
- * Near the deadline the agent is steered to wrap up and report; at the
533
- * deadline it is aborted; after prolonged silence it is killed as stalled.
476
+ * The child leads its own process group so a stall or cancel takes its
477
+ * grandchildren (dev servers, watch-mode tests) with it, and the run ends on
478
+ * process exit rather than waiting for a pipe a grandchild may hold open.
479
+ * There is no time limit: a run ends when the agent finishes, is cancelled,
480
+ * or goes silent for so long it is killed as stalled.
534
481
  */
535
482
  export function spawnPiProcess(args: string[], options: ProcessRunOptions): Promise<ProcessOutcome> {
536
483
  return new Promise((resolve) => {
@@ -545,25 +492,16 @@ export function spawnPiProcess(args: string[], options: ProcessRunOptions): Prom
545
492
  windowsHide: true,
546
493
  stdio: ["pipe", "pipe", "pipe"],
547
494
  });
548
- const state: RunState = { killed: false, timedOut: false, stalled: false, wrappedUp: false, settled: false, timeUp: false, headsUp: false, deciding: false, asking: 0, extendedMs: 0 };
495
+ const state: RunState = { killed: false, stalled: false, settled: false, asking: 0 };
549
496
  const input = rpcInput(proc);
550
- const started = Date.now();
551
- const time = options.time;
552
- // The allotted time counts from here; a grant restarts it.
553
- let clockStart = started;
554
- let upAtMs = time?.upAtMs ?? 0;
555
- let headsUpAtMs = time?.headsUpAtMs ?? 0;
556
- let lastOutput = started;
497
+ let lastOutput = Date.now();
557
498
  let quietGraceUntil = 0;
558
499
  let toolsInFlight = 0;
559
500
  let stderr = "";
560
501
  let exitCode: number | undefined;
561
502
  let settleTimer: ReturnType<typeof setTimeout> | undefined;
562
503
  let done = false;
563
- // Time spent waiting on the user's answers is not the agent's: its limits move out by it.
564
- let deadlineAt = started + options.timeoutMs;
565
504
  let askStarted = 0;
566
- let pausedMs = 0;
567
505
  const asks = new AbortController();
568
506
 
569
507
  const onEvent = (event: PiStreamEvent) => {
@@ -572,51 +510,18 @@ export function spawnPiProcess(args: string[], options: ProcessRunOptions): Prom
572
510
  if (event.type === "retry") quietGraceUntil = Date.now() + event.delayMs;
573
511
  options.onEvent?.(event);
574
512
  };
575
- // A turn that ended after the time ran out waits on the decision; any other ends the run.
513
+ // The run ends once its turn is over.
576
514
  let turnDone = false;
577
515
  const turnOver = () => {
578
516
  if (turnDone) return;
579
517
  turnDone = true;
580
518
  clearTimeout(settleTimer);
581
519
  settleTimer = undefined;
582
- if (state.timeUp && time?.onTimeUp && !done && !state.killed) decide(time.onTimeUp);
583
- else input.close();
584
- };
585
- const decide = (onTimeUp: NonNullable<TimeLimit["onTimeUp"]>) => {
586
- state.deciding = true;
587
- clearTimeout(deadline);
588
- const report = parsePiStream(collector.peek()).text;
589
- onTimeUp(report).then(({ extraMs, message }) => {
590
- if (done || state.killed) return;
591
- state.deciding = false;
592
- if (extraMs <= 0) return input.close();
593
- // Granted: the same agent carries on where it stopped, its context intact.
594
- state.timeUp = false;
595
- state.headsUp = false;
596
- state.settled = false;
597
- state.extendedMs += extraMs;
598
- turnDone = false;
599
- clockStart = Date.now();
600
- lastOutput = clockStart;
601
- upAtMs = time?.hardLimit ? Math.max(1, extraMs - Math.min(time.graceMs, Math.floor(extraMs / 4))) : extraMs;
602
- // The user just named the time it has: no heads-up quoting the old allotment.
603
- headsUpAtMs = 0;
604
- const nextLimit = extraMs + (time?.hardLimit ? 0 : time?.graceMs ?? 0);
605
- deadlineAt = Date.now() + nextLimit;
606
- deadline = setTimeout(onDeadline, nextLimit);
607
- input.send({ type: "prompt", message: message ?? "You have more time: carry on from where you left off and finish." });
608
- options.onEvent?.({ type: "extended", ms: extraMs });
609
- }, () => {
610
- state.deciding = false;
611
- input.close();
612
- });
520
+ input.close();
613
521
  };
614
522
  const relay = (id: string | undefined, questions: AskQuestion[], ask: RelayAsk) => {
615
523
  state.asking += 1;
616
- if (state.asking === 1) {
617
- askStarted = Date.now();
618
- clearTimeout(deadline);
619
- }
524
+ if (state.asking === 1) askStarted = Date.now();
620
525
  options.onEvent?.({ type: "asking", questions: questions.length });
621
526
  ask(questions, asks.signal)
622
527
  .catch((): AskResult => ({ answers: [], cancelled: true }))
@@ -626,11 +531,7 @@ export function spawnPiProcess(args: string[], options: ProcessRunOptions): Prom
626
531
  if (state.asking === 0) {
627
532
  const now = Date.now();
628
533
  const waited = now - askStarted;
629
- pausedMs += waited;
630
- deadlineAt += waited;
631
- clockStart += waited;
632
534
  lastOutput = now;
633
- if (!state.deciding) deadline = setTimeout(onDeadline, Math.max(0, deadlineAt - now));
634
535
  options.onEvent?.({ type: "answered", waitedMs: waited });
635
536
  }
636
537
  input.send({ type: "extension_ui_response", id, value: JSON.stringify(result) });
@@ -673,7 +574,6 @@ export function spawnPiProcess(args: string[], options: ProcessRunOptions): Prom
673
574
  const finish = (code: number) => {
674
575
  if (done) return;
675
576
  done = true;
676
- clearTimeout(deadline);
677
577
  clearTimeout(settleTimer);
678
578
  clearInterval(watch);
679
579
  // Questions still open when the run ends are put away.
@@ -682,9 +582,8 @@ export function spawnPiProcess(args: string[], options: ProcessRunOptions): Prom
682
582
  input.close();
683
583
  // Reap anything the agent left running in its group.
684
584
  if (POSIX) signalGroup(proc, "SIGTERM");
685
- const { killed, timedOut, stalled, wrappedUp, protocolError } = state;
686
- const timeUp = state.timeUp && !killed;
687
- resolve({ exitCode: code, stdout: collector.finish(), stderr, killed, timedOut, stalled, wrappedUp, protocolError, ...(timeUp ? { timeUp } : {}), ...(state.extendedMs > 0 ? { extendedMs: state.extendedMs } : {}) });
585
+ const { killed, stalled, protocolError } = state;
586
+ resolve({ exitCode: code, stdout: collector.finish(), stderr, killed, stalled, protocolError });
688
587
  };
689
588
  const onAbort = () => {
690
589
  state.killed = true;
@@ -692,31 +591,9 @@ export function spawnPiProcess(args: string[], options: ProcessRunOptions): Prom
692
591
  input.close();
693
592
  terminate(proc, 3000);
694
593
  };
695
- const onDeadline = () => {
696
- state.killed = true;
697
- state.timedOut = true;
698
- input.send({ type: "abort" });
699
- input.close();
700
- setTimeout(() => terminate(proc), options.graceMs ?? DEADLINE_GRACE_MS).unref();
701
- };
702
- let deadline = setTimeout(onDeadline, options.timeoutMs);
703
594
  const watch = setInterval(() => {
704
- if (state.killed || done || state.deciding || state.asking > 0) return;
595
+ if (state.killed || done || state.asking > 0) return;
705
596
  const now = Date.now();
706
- if (!state.wrappedUp && !state.settled && options.wrapUpAtMs && options.wrapUpAtMs > 0 && now - started - pausedMs >= options.wrapUpAtMs) {
707
- state.wrappedUp = true;
708
- input.send({ type: "steer", message: options.wrapUpMessage ?? WRAP_UP_MESSAGE });
709
- options.onEvent?.({ type: "wrap_up" });
710
- }
711
- if (time && !state.headsUp && !state.timeUp && !turnDone && headsUpAtMs > 0 && now - clockStart >= headsUpAtMs) {
712
- state.headsUp = true;
713
- if (time.headsUpMessage) input.send({ type: "steer", message: time.headsUpMessage });
714
- }
715
- if (time && !state.timeUp && !turnDone && upAtMs > 0 && now - clockStart >= upAtMs) {
716
- state.timeUp = true;
717
- input.send({ type: "steer", message: time.upMessage });
718
- options.onEvent?.({ type: "time_up" });
719
- }
720
597
  const limit = toolsInFlight > 0 ? options.toolStallTimeoutMs : options.stallTimeoutMs;
721
598
  if (limit && limit > 0 && now - Math.max(lastOutput, quietGraceUntil) >= limit) {
722
599
  state.killed = true;
@@ -724,7 +601,7 @@ export function spawnPiProcess(args: string[], options: ProcessRunOptions): Prom
724
601
  input.close();
725
602
  terminate(proc, 3000);
726
603
  }
727
- }, watchInterval(options.stallTimeoutMs, options.toolStallTimeoutMs, options.wrapUpAtMs, time?.upAtMs, time?.headsUpAtMs));
604
+ }, watchInterval(options.stallTimeoutMs, options.toolStallTimeoutMs));
728
605
  watch.unref();
729
606
 
730
607
  proc.on("exit", (code, signal) => {
@@ -745,15 +622,11 @@ export function spawnPiProcess(args: string[], options: ProcessRunOptions): Prom
745
622
  }
746
623
 
747
624
  function toResult(parsed: ParsedStream, outcome: ProcessOutcome, aborted: boolean, options: PiRunOptions): PiRunResult {
748
- const base = { output: parsed.text, usage: parsed.usage, model: parsed.model, wrappedUp: outcome.wrappedUp === true, ...(outcome.timeUp ? { timeUp: true } : {}), ...(outcome.extendedMs ? { extendedMs: outcome.extendedMs } : {}) };
625
+ const base = { output: parsed.text, usage: parsed.usage, model: parsed.model };
749
626
  if (outcome.stalled) {
750
627
  const quiet = shortDuration(options.stallTimeoutMs ?? 0);
751
628
  return { ...base, status: "timeout", stalled: true, error: `stalled: no output for ${quiet}` };
752
629
  }
753
- if (outcome.timedOut) {
754
- const note = outcome.wrappedUp ? " after a wrap-up request" : "";
755
- return { ...base, status: "timeout", error: `hit the ${shortDuration(options.timeoutMs)} time limit${note}` };
756
- }
757
630
  if (outcome.killed || aborted || parsed.stopReason === "aborted") {
758
631
  return { ...base, status: "cancelled", error: "agent was cancelled" };
759
632
  }
@@ -784,23 +657,17 @@ export async function runPiAgent(given: PiRunOptions, run: ProcessRunner = spawn
784
657
  try {
785
658
  prompt = writePromptFile([options.systemPrompt, host.note].filter(Boolean).join("\n\n"));
786
659
  if (options.signal?.aborted) return { status: "cancelled", output: "", usage: { input: 0, output: 0, cost: 0, turns: 0 } };
787
- const args = buildPiArgs({ ...options, tools: host.tools, mcpTools: host.mcpTools, systemPromptFile: prompt.file });
660
+ const args = buildPiArgs({ ...options, tools: host.tools, mcpTools: host.mcpTools, systemPromptFile: prompt.file, ...(options.skills ? { skillFiles: skillFiles(options.skills, options.cwd) } : {}) });
788
661
  const outcome = await run(args, {
789
662
  cwd: options.cwd,
790
663
  signal: options.signal,
791
- timeoutMs: options.timeoutMs,
792
664
  prompt: `Task: ${options.task}`,
793
665
  env: host.env,
794
666
  stallTimeoutMs: options.stallTimeoutMs,
795
667
  toolStallTimeoutMs: options.toolStallTimeoutMs,
796
- wrapUpAtMs: options.wrapUpAtMs,
797
- ...(options.time ? { time: options.time } : {}),
798
668
  ...(options.onAsk ? { onAsk: options.onAsk } : {}),
799
669
  onStart: host.onStart,
800
670
  onEvent: (event) => {
801
- if (event.type === "asking") host.pause?.();
802
- if (event.type === "answered") host.extend?.(event.waitedMs);
803
- if (event.type === "extended") host.extend?.(event.ms, true);
804
671
  if (event.type === "tool_execution_start") options.onActivity?.(activityWord(event.toolName));
805
672
  options.onEvent?.(event);
806
673
  },