pi-ptc-subagents 0.1.3 → 1.0.0

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.0] - 2026-09-24
9
+
10
+ ### Added
11
+
12
+ - **Background dispatch: PTC programs can fan out to long-lived children.**
13
+ `pi.dispatch(...)` gains an opt-in `{ background: true }` path: instead of
14
+ awaiting the child, the binding returns a `DispatchHandle`
15
+ (`{ taskId, label, status: "running" }`) immediately, and a detached pump
16
+ drives a session-level `TaskRecord` through
17
+ `running / stopping / succeeded / failed / canceled / lost`, past the end of
18
+ the program and of the turn. The handle is a frozen spawn-time projection;
19
+ live state comes from three model-facing tools that stay on when PTC mode is
20
+ off (`/ptc off` only blocks new spawns):
21
+ - **`ptc_task_list`** — list this session's tasks (`status?`, `limit?`,
22
+ default 100, newest first);
23
+ - **`ptc_task_output`** — read a task's captured output (`taskId`,
24
+ `sinceBytes?`), tail-truncated to pi's 50 KB / 2000-line contract
25
+ (ADR-0015) with the full text written to a temp file;
26
+ - **`ptc_task_stop`** — ask a running task to stop (`taskId`, `reason?`,
27
+ default `"model stop"`), moving it `running -> stopping -> canceled`.
28
+
29
+ Lifecycle changes arrive as user-role `<bg-task-notification>` events under a
30
+ `<bg-task-notifications>` batch, cursor-delivered per subscriber with a
31
+ 2048-byte inline-preview ceiling. Background tasks count against the existing
32
+ `dispatchConcurrency` (8) for their whole lifetime and share the
33
+ `maxDispatchDepth` (3) recursion bound. Existing foreground `pi.dispatch`
34
+ keeps its `DispatchResult` shape unchanged. ADR-0022.
35
+
8
36
  ## [0.1.3] - 2026-09-23
9
37
 
10
38
  ### Added
package/README.md CHANGED
@@ -32,6 +32,9 @@ required — install it and the extension is on for the next pi startup.
32
32
  - `ptc_workflow` — structured variant with `meta` + plain-JSON `args`, plus the
33
33
  workflow helpers (`log`, `phase`, `parallel`, `pipeline`). There is no
34
34
  `agent()` helper on either surface.
35
+ - `ptc_task_list` / `ptc_task_output` / `ptc_task_stop` — manage background
36
+ dispatches (see [Background dispatch](#background-dispatch)). They stay
37
+ available when PTC mode is off.
35
38
 
36
39
  Long output follows pi's own truncation contract ([ADR-0015](./docs/adr/0015-pi-truncation-contract.md)):
37
40
 
@@ -43,9 +46,11 @@ the next program can `tools.read`; the collapsed row then shows `truncated` in i
43
46
 
44
47
  PTC programs can spawn a fresh `pi` subprocess per call via the **`pi.dispatch(...)`** binding ([ADR-0016](./docs/adr/0016-ptc-dispatch-binding.md)). Use it to fan out to a specialist agent — the child subprocess loads the named agent's markdown from `~/.pi/agent/agents/<name>.md` (or `.pi/agents/<name>.md` for project-scope agents), runs that agent's tool set and system prompt in isolation, and returns a structured result.
45
48
 
49
+ Bindings are reached through the one `tools` table — there is no `pi` global in the worker — so the binding named `pi.dispatch` is called as `tools["pi.dispatch"]({ … })` with a single object argument.
50
+
46
51
  ```ts
47
52
  // inside a ptc_run_code program
48
- const result = await pi.dispatch({
53
+ const result = await tools["pi.dispatch"]({
49
54
  agent: "scout",
50
55
  task: "find all auth code in src/",
51
56
  cwd: process.cwd(),
@@ -62,8 +67,8 @@ const result = await pi.dispatch({
62
67
  ```ts
63
68
  const [read, scoutA, scoutB] = await Promise.all([
64
69
  tools.read({ path: "package.json" }),
65
- pi.dispatch({ agent: "scout", task: "review auth" }),
66
- pi.dispatch({ agent: "scout", task: "review db" }),
70
+ tools["pi.dispatch"]({ agent: "scout", task: "review auth" }),
71
+ tools["pi.dispatch"]({ agent: "scout", task: "review db" }),
67
72
  ]);
68
73
  ```
69
74
 
@@ -78,11 +83,36 @@ const [read, scoutA, scoutB] = await Promise.all([
78
83
  ```ts
79
84
  // in a hypothetical runner that wants to keep reads-only:
80
85
  createBuiltinBindings({ cwd: "/abs/path", names: ["read", "grep"] });
81
- // `pi.dispatch` is NOT in the resulting table.
86
+ // `pi.dispatch` is NOT in the resulting `tools` table.
82
87
  ```
83
88
 
84
89
  **Not a subagent.** The term _subagent_ is overloaded in this field (DSH's `subagent` is a different thing; pi's `examples/extensions/subagent/` extension is also a different thing). pi-ptc uses _parallel binding_ and _concurrent tool call_ throughout; see `CONTEXT.md` for the canonical terms.
85
90
 
91
+ ### Background dispatch
92
+
93
+ Foreground `pi.dispatch` blocks the program until the child exits. Pass `background: true` to spawn the child and return immediately with a `DispatchHandle` ([ADR-0022](./docs/adr/0022-background-dispatch.md)); the child outlives both the program and the turn:
94
+
95
+ ```ts
96
+ // inside a ptc_run_code program — bindings are reached as tools["<name>"]
97
+ const handle = await tools["pi.dispatch"]({
98
+ agent: "scout",
99
+ task: "audit the auth code",
100
+ background: true,
101
+ label: "auth audit", // defaults to task.slice(0, 64)
102
+ });
103
+ // handle: { taskId: "01J…", label: "auth audit", status: "running" }
104
+ ```
105
+
106
+ (The binding's name is `pi.dispatch`; a program reaches it as `tools["pi.dispatch"]`.)
107
+
108
+ A detached pump drives the task's lifecycle (`running` -> `succeeded` / `failed` / `canceled` / `lost`), and the model observes it with three always-on tools — they are not part of the PTC-mode loadout, so `/ptc off` (which only blocks new spawns) does not remove them:
109
+
110
+ - `ptc_task_list({ status?, limit? })` — list this session's tasks, newest first (default limit 100).
111
+ - `ptc_task_output({ taskId, sinceBytes? })` — read a task's captured output, tail-truncated to pi's 50 KB / 2000-line contract (ADR-0015).
112
+ - `ptc_task_stop({ taskId, reason? })` — ask a running task to stop.
113
+
114
+ Background tasks count against the same `dispatchConcurrency` (default 8) for their whole lifetime and share the `maxDispatchDepth` (default 3) recursion bound. A pre-spawn refusal (depth or concurrency cap, unknown agent) still comes back as the familiar `DispatchResult` with `status: "rejected"`. Full guide: [`docs/usage/bgdispatch.md`](./docs/usage/bgdispatch.md).
115
+
86
116
  ## TUI rendering
87
117
 
88
118
  Both tools register custom `renderCall` / `renderResult` hooks, so a PTC run reads as a program
package/dist/index.d.ts CHANGED
@@ -1,5 +1,466 @@
1
- import { ExtensionAPI, Skill } from "@earendil-works/pi-coding-agent";
1
+ import { ExtensionAPI, Skill, truncateTail } from "@earendil-works/pi-coding-agent";
2
+ import "@earendil-works/pi-ai";
2
3
  import { MessagePort, Worker } from "node:worker_threads";
4
+ //#region src/runtime/ulid.d.ts
5
+ /**
6
+ * Lexically-sortable identifier (26-char Crockford base32). Brand-only: the runtime value is a
7
+ * plain `string`, the brand is a compile-time distinction that keeps a taskId from being
8
+ * passed into a cursor slot.
9
+ */
10
+ type ULID = string & {
11
+ readonly __brand: "ULID";
12
+ };
13
+ //#endregion
14
+ //#region src/runtime/child-process-lifecycle.d.ts
15
+ /**
16
+ * One line of the child's stdout, parsed as JSON. The child emits the events described
17
+ * in examples/extensions/subagent/index.ts (message_end, tool_result_end, error).
18
+ * Anything unparseable is dropped: the line is one event the host cannot interpret,
19
+ * and the run continues.
20
+ */
21
+ interface ParsedAgentEvent {
22
+ type: string;
23
+ message?: {
24
+ role?: string;
25
+ content?: ReadonlyArray<{
26
+ type?: string;
27
+ text?: string;
28
+ }>;
29
+ usage?: {
30
+ input?: number;
31
+ output?: number;
32
+ cacheRead?: number;
33
+ cacheWrite?: number;
34
+ cost?: {
35
+ total?: number;
36
+ };
37
+ };
38
+ model?: string;
39
+ stopReason?: string;
40
+ errorMessage?: string;
41
+ };
42
+ message_text?: string;
43
+ }
44
+ /**
45
+ * Opaque per-handle state owned by the adapter that produced it. The dispatch code
46
+ * never reads `opaque` directly; it only hands the handle back to adapter methods
47
+ * (`kill`, `events`, `exit`, `stderr`).
48
+ */
49
+ interface ChildHandle {
50
+ readonly id: ULID;
51
+ readonly opaque: unknown;
52
+ }
53
+ /**
54
+ * Spawn options shared by both adapters. `promptFile` / `sessionDir` / `sessionId` /
55
+ * `sessionName` are the R1 fields the background dispatch consumes (ADR-0022 §1/R1). The
56
+ * foreground `pi.dispatch` path passes only `cwd` / `env` / `signal` / `promptFile`.
57
+ */
58
+ interface ChildSpawnOptions {
59
+ cwd: string;
60
+ env?: NodeJS.ProcessEnv;
61
+ signal?: AbortSignal;
62
+ promptFile: string;
63
+ sessionDir?: string;
64
+ sessionId?: string;
65
+ sessionName?: string;
66
+ }
67
+ /** Process-exit shape the `close` event on `node:child_process.ChildProcess` produces. */
68
+ interface ChildExitValue {
69
+ code: number | null;
70
+ signal: NodeJS.Signals | null;
71
+ }
72
+ /**
73
+ * The lifecycle contract. Implementations:
74
+ * - `RealChildProcessLifecycle` - wraps `node:child_process.spawn` (production).
75
+ * - `MockChildProcessLifecycle` - in-memory queue + exit signal (tests).
76
+ */
77
+ interface ChildProcessLifecycle {
78
+ /**
79
+ * Launch one child process. The first element of `argv` is the executable; the rest
80
+ * are forwarded as command-line arguments. Returns an opaque handle the caller uses
81
+ * to talk to the same child via the other methods.
82
+ *
83
+ * `opts.env` is the full environment for the child; the caller is responsible for
84
+ * merging anything the child needs (e.g. `PI_PTC_DEPTH`) on top of `process.env`.
85
+ */
86
+ spawn(argv: readonly string[], opts: ChildSpawnOptions): ChildHandle;
87
+ /**
88
+ * Send `signal` to the child iff it is still attached. Absorbs "process already gone"
89
+ * throws so callers can fire-and-forget on cancel. Mirrors `safeKill` below.
90
+ */
91
+ kill(handle: ChildHandle, signal: "SIGTERM" | "SIGKILL"): void;
92
+ /**
93
+ * Async iterator over the child's stdout events. Yields one `ParsedAgentEvent` per
94
+ * JSONL line. The iterator ends after the child closes; callers should `await` it
95
+ * fully before reading `exit()`.
96
+ */
97
+ events(handle: ChildHandle): AsyncIterable<ParsedAgentEvent>;
98
+ /**
99
+ * Resolve when the child closes, with `(code, signal)` matching `node:child_process`'s
100
+ * `close` event. A clean exit is `(0, null)`; a signal kill is `(null, <signal>)`;
101
+ * a spawn failure surfaces as `(null, null)` once the error event has propagated.
102
+ */
103
+ exit(handle: ChildHandle): Promise<ChildExitValue>;
104
+ /**
105
+ * Resolve with the accumulated stderr text once the child has closed. Returns `""`
106
+ * when the child wrote nothing. Spawn failures append a `[spawn-error] ...` marker
107
+ * (same convention as the pre-BG-03 dispatch code) so callers can distinguish a
108
+ * failed spawn from a clean non-zero exit.
109
+ */
110
+ stderr(handle: ChildHandle): Promise<string>;
111
+ }
112
+ //#endregion
113
+ //#region src/runtime/task-storage.d.ts
114
+ /**
115
+ * The 6-state TaskRecord status (ADR-0022 §2). `queued` is deliberately absent in v1
116
+ * (spawn-or-reject, no in-task queue; cap=8 rejects above the limit immediately).
117
+ */
118
+ type TaskStatus = "running" | "stopping" | "succeeded" | "failed" | "canceled" | "lost";
119
+ /** Origin of the spawn — `ptc-program` from a PTC run, `ptc-batch` from a future batch entry. */
120
+ interface TaskSpawnSource {
121
+ kind: "ptc-program" | "ptc-batch";
122
+ /** Caller id (PTC run id for `ptc-program`; batch id for `ptc-batch`). */
123
+ callerId: string;
124
+ }
125
+ /**
126
+ * Session-level row tracking the lifecycle of one background child. 21 fields, exact shape from
127
+ * ADR-0022 §3. Optional fields are absent on records still `running` (e.g. `finishedAt` /
128
+ * `exitCode` / `outputRef` are written at transition time, not at spawn).
129
+ */
130
+ interface TaskRecord {
131
+ id: ULID;
132
+ label: string;
133
+ agentName: string;
134
+ /** 0 = parent's direct, 1 = grandchild, etc. (ADR-0016 recursive section). */
135
+ depth: number;
136
+ status: TaskStatus;
137
+ /** ms epoch. */
138
+ createdAt: number;
139
+ startedAt: number;
140
+ finishedAt?: number;
141
+ durationMs?: number;
142
+ /** Last state transition ms epoch; used for cursor ordering of events. */
143
+ transitionAt: number;
144
+ /** `<sessionDir>/tasks/<id>/output.log` once the child has flushed any output. */
145
+ outputRef?: string;
146
+ outputBytes?: number;
147
+ /** ≤ 2 KB inline preview (Map+preview, ADR-0022 §3). */
148
+ outputPreview?: string;
149
+ stopReason?: string;
150
+ errorMessage?: string;
151
+ exitCode?: number;
152
+ spawnSource: TaskSpawnSource;
153
+ parentTaskId?: ULID;
154
+ /** `<sessionDir>/tasks/<id>.pi-*` session file (R1). */
155
+ sessionFile?: string;
156
+ }
157
+ /**
158
+ * Per-subscriber cursor for one TaskRecord (ADR-0022 §5). Cursor is per-subscriber (not per-task)
159
+ * because fork semantics differ from single-session semantics: a forked branch observes events
160
+ * from `max(parent, child)` cursor onwards, not from task creation.
161
+ */
162
+ interface Subscription {
163
+ subscriberId: ULID;
164
+ taskId: ULID;
165
+ /** Monotonic ULID; advances on every event delivered. */
166
+ cursor: ULID;
167
+ status: "active" | "closed";
168
+ /** ms epoch. */
169
+ createdAt: number;
170
+ }
171
+ /**
172
+ * Append-only event in a subscription buffer. The `eventId` IS the cursor: monotonic ULID, the
173
+ * `loadEvents(since)` filter slices `[strictly-after since, ...]`. Schema fields beyond the
174
+ * cursor carry enough state for the renderer to emit `<bg-task-notification>` (ADR-0022 §7)
175
+ * without re-reading the TaskRecord.
176
+ */
177
+ interface TaskEvent {
178
+ /** Cursor / event id; the loadEvents(since) filter. */
179
+ eventId: ULID;
180
+ subscriptionId: ULID;
181
+ taskId: ULID;
182
+ /**
183
+ * Emit key per ADR-0022 §2 + §8, e.g. `task:<id>:running`, `task:<id>:->canceled`,
184
+ * `task:<id>:->lost`. Storage layer treats this as an opaque string.
185
+ */
186
+ type: string;
187
+ status: TaskStatus;
188
+ /** ms epoch; matches `TaskRecord.transitionAt` at the moment of emission. */
189
+ transitionAtMs: number;
190
+ outputBytes?: number;
191
+ /** ≤ 2 KB inline preview (only present when `outputBytes <= 2048`). */
192
+ outputPreview?: string;
193
+ }
194
+ //#endregion
195
+ //#region src/runtime/task-registry.d.ts
196
+ /** Minimal structured logger seam (ADR-0022 implementation spec §12). */
197
+ interface RegistryLogger {
198
+ info(msg: string): void;
199
+ warn(msg: string): void;
200
+ }
201
+ /**
202
+ * Thin, frozen, spawn-time projection of a TaskRecord (ADR-0022 §4). The program carries
203
+ * this; the registry stores the TaskRecord. The handle is *not* updated on transition.
204
+ */
205
+ interface DispatchHandle {
206
+ taskId: ULID;
207
+ label: string;
208
+ status: "running";
209
+ }
210
+ /**
211
+ * Per-call dependencies for one registry command. `callerId` is also the subscriber id
212
+ * (ADR-0022 §5, "Subscriber == owner"); `clock` must be the injected time source.
213
+ */
214
+ interface TransitionContext {
215
+ clock: () => number;
216
+ logger?: RegistryLogger;
217
+ callerId: string;
218
+ }
219
+ /**
220
+ * The three `lost` reasons ADR-0022 §8 keeps distinct in the TaskRecord's
221
+ * `errorMessage` field for auditability.
222
+ */
223
+ type LostReason = "session_ended_while_running" | "user_killed_via_esc" | "lost_on_session_restart";
224
+ /** All state mutations the TaskRegistry accepts (single-terminal-writer invariant, this file). */
225
+ type TaskCommand = {
226
+ kind: "spawn";
227
+ parentTaskId?: ULID;
228
+ handle: DispatchHandle;
229
+ record: Omit<TaskRecord, "id" | "status" | "createdAt" | "transitionAt">;
230
+ } | {
231
+ kind: "transition";
232
+ taskId: ULID;
233
+ to: TaskStatus;
234
+ reason?: string;
235
+ stopReason?: string;
236
+ errorMessage?: string;
237
+ exitCode?: number;
238
+ /**
239
+ * ADR-0022 §3: the child's captured-output projection written on the terminal
240
+ * transition. The BG-04 pump drains stdout, persists it through `OutputStorage`, and
241
+ * passes these three so `ptc_task_output` can find it.
242
+ */
243
+ outputRef?: string;
244
+ outputBytes?: number;
245
+ outputPreview?: string;
246
+ } | {
247
+ kind: "stop";
248
+ taskId: ULID;
249
+ reason: string;
250
+ } | {
251
+ kind: "reconcile-lost";
252
+ taskId: ULID;
253
+ reason: LostReason;
254
+ } | {
255
+ kind: "resolve-exit";
256
+ taskId: ULID;
257
+ exitCode: number;
258
+ outputRef?: string;
259
+ outputBytes?: number;
260
+ outputPreview?: string;
261
+ };
262
+ /** Outcome of one successful command: the persisted record, emitted events, and cursor. */
263
+ interface TransitionResult {
264
+ record: TaskRecord;
265
+ events: TaskEvent[];
266
+ /** The event id of the last emitted event; equal to the subscription's new cursor. */
267
+ cursor: ULID;
268
+ /**
269
+ * The status the record held *before* this command applied. Absent on `spawn` (there is no
270
+ * prior state); present on every transition/stop/resolve so a caller can report the source
271
+ * state without a second, racing read (ADR-0022 §8 late-arrival stop).
272
+ */
273
+ fromStatus?: TaskStatus;
274
+ }
275
+ /**
276
+ * In-process transition observer (issue #68 §1). Fired synchronously by
277
+ * {@link TaskRegistry.transition} **after** the new record and its event have been persisted,
278
+ * with the post-transition record and the emitted event. Observers are best-effort: a throwing
279
+ * observer is warned about and never corrupts the transition. Registration returns an
280
+ * idempotent unsubscribe.
281
+ */
282
+ type TaskTransitionObserver = (record: TaskRecord, event: TaskEvent) => void;
283
+ /** Read-side filter (ADR-0022 §3). `limit` defaults to 100; `orderBy` defaults to desc. */
284
+ interface TaskQuery {
285
+ status?: TaskStatus[];
286
+ label?: string;
287
+ limit?: number;
288
+ orderBy?: "createdAt-asc" | "createdAt-desc";
289
+ }
290
+ /** The five-method lifecycle surface from the BG-02 brief. */
291
+ interface TaskRegistry {
292
+ transition(command: TaskCommand, ctx: TransitionContext): Promise<TransitionResult>;
293
+ query(view: TaskQuery): Promise<TaskRecord[]>;
294
+ /**
295
+ * Load one TaskRecord by id; `null` when the id is unknown. The O(1) complement of `query`,
296
+ * used by the background pump to see whether a stop was requested before it writes the
297
+ * terminal transition (ADR-0022 §8).
298
+ */
299
+ get(taskId: ULID): Promise<TaskRecord | null>;
300
+ /**
301
+ * Subscribe to every successfully persisted state write (spawn included). Returns an
302
+ * idempotent unsubscribe. Observers run synchronously, in-process; see
303
+ * {@link TaskTransitionObserver} for the best-effort contract and single-writer ordering.
304
+ */
305
+ onTransition(observer: TaskTransitionObserver): () => void;
306
+ reconcileLostTasks(): Promise<TaskRecord[]>;
307
+ }
308
+ //#endregion
309
+ //#region src/runtime/output-storage.d.ts
310
+ /**
311
+ * The persistence seam for one task's captured output.
312
+ *
313
+ * `readOutput` returning `null` and `writeOutput` accepting any string are the whole contract;
314
+ * ordering, framing and truncation are the caller's job.
315
+ */
316
+ interface OutputStorage {
317
+ /** Load the task's raw output; `null` when nothing has been written for that id. */
318
+ readOutput(taskId: ULID): Promise<string | null>;
319
+ /** Persist (insert-or-replace) the task's raw output. */
320
+ writeOutput(taskId: ULID, content: string): Promise<void>;
321
+ /**
322
+ * The stable path/ref the dispatcher records on `TaskRecord.outputRef`. It exists even before
323
+ * the first `writeOutput` (the ref is the record's address, not the file's existence).
324
+ */
325
+ outputRef(taskId: ULID): string;
326
+ }
327
+ //#endregion
328
+ //#region src/runtime/dispatch.d.ts
329
+ /**
330
+ * Per-run in-flight `pi.dispatch` counter (ADR-0016 §2 + ADR-0022 §9). The dispatcher's
331
+ * foreground gate owns the canonical counter today; this class is the small exported seam
332
+ * ADR-0022 §9 asks for so the background branch can hold a slot for a child's whole
333
+ * lifetime and release it only on the terminal transition — a background task keeps
334
+ * counting against `dispatchConcurrency` after `dispatch()` has already returned. The
335
+ * dispatcher can adopt this type without a behaviour change.
336
+ */
337
+ declare class DispatchSlotCounter {
338
+ #private;
339
+ readonly limit: number;
340
+ constructor(limit: number);
341
+ /** Number of slots currently held. */
342
+ get active(): number;
343
+ /** Reserve one in-flight slot (optionally keyed by a per-task holder token); `false` means the cap is reached (hard reject, no queue). */
344
+ tryAcquire(holder?: string): boolean;
345
+ /**
346
+ * Release one slot; per-token idempotent (a late release for the same task is a no-op) and
347
+ * never negative. An anonymous release only frees an anonymous reservation.
348
+ */
349
+ release(holder?: string): void;
350
+ }
351
+ /**
352
+ * Injected dependencies for the background branch. The foreground path ignores this object
353
+ * entirely (it uses the module-level {@link DISPATCH_LIFECYCLE}); every field is optional so
354
+ * existing zero-argument call sites keep working. Tests pass an in-memory registry and a
355
+ * mock lifecycle so no real `pi` process is ever spawned.
356
+ */
357
+ interface DispatchDeps {
358
+ /** Session-level TaskRegistry (ADR-0022 §3). Defaults to a lazy in-memory registry. */
359
+ taskRegistry?: TaskRegistry;
360
+ /** Child lifecycle for the background spawn. Defaults to the shared real adapter. */
361
+ lifecycle?: ChildProcessLifecycle;
362
+ /** Per-run in-flight counter shared with the dispatcher's foreground gate (ADR-0022 §9). */
363
+ slots?: DispatchSlotCounter;
364
+ /** Time source for TaskRecord stamps. Defaults to `Date.now`. */
365
+ clock?: () => number;
366
+ /** Logger for the background pump's failure path. Defaults to a `console.warn` logger. */
367
+ logger?: RegistryLogger;
368
+ /**
369
+ * ADR-0022 §3: where the pump persists a task's drained stdout. When present, the terminal
370
+ * transition records `outputRef` / `outputBytes` / `outputPreview` so `ptc_task_output`
371
+ * can dereference the bytes (BG-07).
372
+ */
373
+ outputStorage?: OutputStorage;
374
+ }
375
+ //#endregion
376
+ //#region src/runtime/notification-pipeline.d.ts
377
+ /** Idle-wake callback: one subscriber's pending events, delivered in cursor order. */
378
+ type IdleWakeHandler = (subscriberId: ULID, events: TaskEvent[]) => void;
379
+ /**
380
+ * The five-method pipeline surface (Ticket BG-05). The concrete class adds the
381
+ * dispatcher-facing `notifyIdle` wake trigger on top of this interface.
382
+ */
383
+ interface NotificationPipeline {
384
+ /** Create (or return the existing) subscription for one (subscriberId, taskId) pair. */
385
+ subscribe(subscriberId: ULID, taskId: ULID, since?: ULID): Promise<Subscription>;
386
+ /** Read pending events strictly newer than the cursor, WITHOUT advancing the cursor. */
387
+ drainPending(subscriberId: ULID, taskId: ULID): Promise<TaskEvent[]>;
388
+ /** Advance the cursor to `untilCursor`; never moves backwards (idempotent). */
389
+ acknowledgeEvents(subscriberId: ULID, taskId: ULID, untilCursor: ULID): Promise<void>;
390
+ /** Register an idle-wake handler; handlers fire in registration order. */
391
+ onIdleWake(handler: IdleWakeHandler): void;
392
+ /** Count events strictly newer than the cursor. */
393
+ pendingCount(subscriberId: ULID, taskId: ULID): Promise<number>;
394
+ }
395
+ //#endregion
396
+ //#region src/runtime/task-notification.d.ts
397
+ /**
398
+ * One drained event joined to the TaskRecord it describes. The join is required: the event
399
+ * lacks `label` / `agentName` / `depth` / `durationMs` / `outputRef` (see the module
400
+ * note above), all of which the ADR-0022 §7 child element must carry.
401
+ */
402
+ interface TaskNotificationItem {
403
+ event: TaskEvent;
404
+ record: TaskRecord;
405
+ }
406
+ //#endregion
407
+ //#region src/runtime/background-runtime.d.ts
408
+ /** Best-effort reporter for a bind failure; the index notifies the user from it. */
409
+ type BackgroundBindReporter = (message: string) => void;
410
+ /** One cursor to advance after a drained batch has actually been delivered. */
411
+ interface NotificationAck {
412
+ subscriberId: ULID;
413
+ taskId: ULID;
414
+ cursor: ULID;
415
+ /**
416
+ * The concrete session pipeline this drain READ from, carried so the ack lands on the same
417
+ * session even if a rebind happens between the drain and the send (P1). Without it the ack goes
418
+ * through the stable proxy's *current* delegate and targets a session that never knew the
419
+ * subscription, leaving the original cursor unadvanced and re-delivering the batch.
420
+ */
421
+ sink: NotificationPipeline;
422
+ }
423
+ /**
424
+ * One drain: the events to render plus the cursors the caller acknowledges after a successful
425
+ * send. The split is what lets a failed send leave the cursor unadvanced (ADR-0022 §5/§6).
426
+ */
427
+ interface NotificationDrain {
428
+ items: TaskNotificationItem[];
429
+ acks: readonly NotificationAck[];
430
+ }
431
+ /**
432
+ * The holder. `registry` / `outputStorage` / `pipeline` are stable objects safe to capture
433
+ * before any session exists; `bindSession` swaps the session delegate underneath them.
434
+ */
435
+ interface BackgroundTaskRuntime {
436
+ readonly registry: TaskRegistry;
437
+ readonly outputStorage: OutputStorage;
438
+ readonly pipeline: NotificationPipeline;
439
+ readonly slots: DispatchSlotCounter;
440
+ readonly lifecycle: ChildProcessLifecycle;
441
+ /** Stable deps the two PTC tools hand to `runPtcProgram` (ADR-0022 §9). */
442
+ readonly dispatchDeps: DispatchDeps;
443
+ /** Session clock (`Date.now` unless injected); passed to `ptc_task_stop`. */
444
+ readonly clock: () => number;
445
+ /**
446
+ * (Re)bind to a session: build the session storage/registry/pipeline, swap the delegates, then
447
+ * reconcile. Returns the records marked `lost`. A bind failure (storage construction or the
448
+ * reconcile sweep) is reported via `reporter` and the logger, and leaves the previous delegate
449
+ * usable; it never rejects and never partially swaps.
450
+ */
451
+ bindSession(sessionDir: string | undefined, reporter?: BackgroundBindReporter): Promise<TaskRecord[]>;
452
+ /** Drain undelivered events for a subscriber as `{event, record}` pairs ready for the renderer. */
453
+ drainNotifications(subscriberId: ULID): Promise<NotificationDrain>;
454
+ /**
455
+ * Advance the subscription cursor for a drained batch. The caller invokes this ONLY after the
456
+ * batch's message was actually delivered (ADR-0022 §5/§6); a failed send leaves the cursor
457
+ * where it was so the next drain re-delivers the event.
458
+ */
459
+ acknowledgeNotifications(acks: readonly NotificationAck[]): Promise<void>;
460
+ /** Mark every non-terminal task lost (given reason) and release resources. */
461
+ shutdown(reason: LostReason): Promise<TaskRecord[]>;
462
+ }
463
+ //#endregion
3
464
  //#region src/mode/ptc-mode.d.ts
4
465
  /** The two surfaces this package exposes. `/ptc on` needs at least one of them active. */
5
466
  export declare const PTC_MODE_TOOL_NAMES: readonly string[];
@@ -410,6 +871,29 @@ interface BindingContext {
410
871
  depth: number;
411
872
  /** Maximum allowed depth; passed through to `pi.dispatch` for the depth check. */
412
873
  maxDispatchDepth: number;
874
+ /**
875
+ * ADR-0022 §5: the TaskRecord owner / subscription subscriber. The dispatcher threads the
876
+ * run id here so a background task's events are addressed to the spawning run.
877
+ */
878
+ callerId?: string;
879
+ /**
880
+ * ADR-0022 R1: the session dir a background child persists into. Threaded from the
881
+ * dispatcher (see `RunPtcProgramOptions.sessionDir`); when no dir is available the spawn
882
+ * keeps the foreground no-session shape.
883
+ */
884
+ sessionDir?: string;
885
+ /**
886
+ * ADR-0022 §3/reopen R-m12: this process's own background task id, when this run is inside a
887
+ * background child. Threaded from the dispatcher (see `RunPtcProgramOptions.parentTaskId`)
888
+ * and forwarded to `pi.dispatch` so a nested spawn records `TaskRecord.parentTaskId`.
889
+ */
890
+ parentTaskId?: ULID;
891
+ /**
892
+ * ADR-0022 §9: per-run dispatch dependencies. The dispatcher supplies the run's shared
893
+ * `DispatchSlotCounter` here; a host may also pass session-level deps (registry / lifecycle /
894
+ * output storage) so the binding's `dispatch()` call shares them.
895
+ */
896
+ dispatchDeps?: DispatchDeps;
413
897
  }
414
898
  interface Binding {
415
899
  readonly name: string;
@@ -540,8 +1024,28 @@ interface RunPtcProgramOptions {
540
1024
  * to 0.
541
1025
  */
542
1026
  depth?: number;
1027
+ /**
1028
+ * ADR-0022 §3/reopen R-m12: this process's own background task id, when the run is inside a
1029
+ * child spawned by `pi.dispatch({ background: true })`. The entrypoint reads it from
1030
+ * `PI_PTC_TASK_ID` and threads it to the binding context so a nested spawn records
1031
+ * `TaskRecord.parentTaskId`. Absent for a top-level session.
1032
+ */
1033
+ parentTaskId?: ULID;
543
1034
  /** Identifier carried to the worker; generated when omitted. */
544
1035
  runId?: string;
1036
+ /**
1037
+ * ADR-0022 R1: the session dir background children persist into, when the host has one.
1038
+ * Threaded to the binding context so `pi.dispatch({ background: true })` can stamp the R1
1039
+ * session flags; absent means the background spawn keeps the foreground no-session shape.
1040
+ */
1041
+ sessionDir?: string;
1042
+ /**
1043
+ * ADR-0022 §3/§9: session-level dispatch dependencies (TaskRegistry / OutputStorage /
1044
+ * lifecycle / clock / logger). The dispatcher merges its per-run `DispatchSlotCounter`
1045
+ * into this bag before handing it to the `pi.dispatch` binding, so background tasks count
1046
+ * against this run's `dispatchConcurrency` for their whole lifetime.
1047
+ */
1048
+ dispatchDeps?: DispatchDeps;
545
1049
  /**
546
1050
  * Optional worker pool (ADR-0017). Absent = spawn a fresh worker and terminate it
547
1051
  * at run end (the original cold-start path). Present = the dispatcher acquires a
@@ -642,7 +1146,15 @@ export declare class TurnPools {
642
1146
  }
643
1147
  //#endregion
644
1148
  //#region src/index.d.ts
645
- export default function ptcSubagents(pi: ExtensionAPI): void;
1149
+ /**
1150
+ * Optional seams for the extension factory. Production calls `ptcSubagents(pi)`; tests may inject
1151
+ * a pre-built background runtime (with a mock child lifecycle) so no real `pi` process is spawned.
1152
+ */
1153
+ export interface PtcSubagentsOptions {
1154
+ /** Use this session-scoped background runtime instead of constructing one. */
1155
+ backgroundRuntime?: BackgroundTaskRuntime;
1156
+ }
1157
+ export default function ptcSubagents(pi: ExtensionAPI, options?: PtcSubagentsOptions): void;
646
1158
  //#endregion
647
1159
  export type { Binding, BindingContext, BindingTable, CreateBuiltinBindingsOptions, DefaultModeConfig, ModeBlockReason, ModeEntryDecision, ModeEntryInput, ModeHideStrategy, PersistedModeState, PtcConfig, PtcErrorKind, PtcErrorShape, PtcImage, PtcJsonValue, PtcModeState, PtcRunOutcome, PtcSurface, RunPtcProgramOptions, TurnPoolsOptions, WorkerPoolOptions, WorkerPoolStats, WorkerPoolWorkerOptions };
648
1160
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,32 +1,43 @@
1
- import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./protocol-CfOgxz3u.js";import{DEFAULT_MAX_BYTES as s,DEFAULT_MAX_LINES as c,createBashTool as l,createEditTool as u,createFindTool as d,createGrepTool as f,createLsTool as p,createReadTool as m,createWriteTool as h,defineTool as g,formatSize as _,formatSkillsForPrompt as v,getAgentDir as y,truncateTail as b}from"@earendil-works/pi-coding-agent";import{Type as x}from"typebox";import{validateToolArguments as S}from"@earendil-works/pi-ai";import{spawn as ee}from"node:child_process";import*as C from"node:fs";import{existsSync as w,readFileSync as T,writeFileSync as E}from"node:fs";import*as D from"node:os";import{tmpdir as te}from"node:os";import*as O from"node:path";import{dirname as k,join as A,resolve as ne}from"node:path";import{randomUUID as j}from"node:crypto";import{MessageChannel as re,Worker as M}from"node:worker_threads";import N from"node:diagnostics_channel";import{fileURLToPath as P,pathToFileURL as ie}from"node:url";import{truncateToWidth as ae,visibleWidth as F}from"@earendil-works/pi-tui";function oe(e,t,n){let r=[`<pi-ptc-context depth="`+t+`" max-depth="`+n+`">`,`You are a PTC run at depth `+t+` (root is depth 0). You may write PTC programs and you may`,`call pi.dispatch(...) to spawn further children, but each level costs a fresh pi subprocess.`,`The remaining depth budget is `+n+` - `+t+`. Beyond it, pi.dispatch rejects with`,`{ status: "rejected", errorMessage: "dispatch depth limit reached" }.`,`</pi-ptc-context>`].join(`
1
+ import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./protocol-CfOgxz3u.js";import{DEFAULT_MAX_BYTES as s,DEFAULT_MAX_LINES as c,createBashTool as l,createEditTool as u,createFindTool as d,createGrepTool as f,createLsTool as p,createReadTool as m,createWriteTool as h,defineTool as g,formatSize as _,formatSkillsForPrompt as v,getAgentDir as y,truncateTail as b}from"@earendil-works/pi-coding-agent";import{Type as x}from"typebox";import{validateToolArguments as S}from"@earendil-works/pi-ai";import*as C from"node:fs";import{existsSync as w,readFileSync as T,writeFileSync as ee}from"node:fs";import*as E from"node:os";import{tmpdir as te}from"node:os";import*as D from"node:path";import{dirname as O,join as k,resolve as A}from"node:path";import{spawn as ne}from"node:child_process";import{randomBytes as re,randomUUID as j}from"node:crypto";import{ok as M}from"node:assert/strict";import{MessageChannel as ie,Worker as N}from"node:worker_threads";import P from"node:diagnostics_channel";import{fileURLToPath as ae,pathToFileURL as F}from"node:url";import{truncateToWidth as I,visibleWidth as L}from"@earendil-works/pi-tui";import{appendFile as oe,mkdir as se,readFile as R,readdir as ce,rename as le,unlink as ue,writeFile as z}from"node:fs/promises";const de=`0123456789ABCDEFGHJKMNPQRSTVWXYZ`;function fe(e){let t=``,n=Math.max(0,Math.floor(e));for(let e=0;e<10;e++)t=(de[n%32]??`0`)+t,n=Math.floor(n/32);return t}function pe(e){let t=``,n=0,r=0;for(let i=0;i<e.length&&t.length<16;i++)for(n=n<<8|(e[i]??0),r+=8;r>=5&&t.length<16;)r-=5,t+=de[n>>>r&31];return t.padEnd(16,`0`)}function me(e){let t=new Uint8Array(e);for(let e=t.length-1;e>=0;e--){if((t[e]??0)<255)return t[e]=(t[e]??0)+1,t;t[e]=0}return t}function he(e){let t=e.randomBytes??(e=>new Uint8Array(re(e))),n=-1,r=/* @__PURE__ */ new Uint8Array(10);return{next(){let i=Math.max(0,Math.floor(e.now())),a=i>n?i:n,o=a===n?me(r):t(10);return n=a,r=o,fe(a)+pe(o)}}}const ge=he({now:()=>Date.now()});function _e(){return ge.next()}function ve(e){let t=e.trim();if(t.length===0)return null;try{return JSON.parse(t)}catch{return null}}function ye(e,t){if(e.pid===void 0)return!1;try{return e.kill(t),!0}catch{return!1}}function be(e,t){if(e.eventWaiters.length>0){let n=e.eventWaiters.shift();n&&n(t);return}e.events.push(t)}function xe(e,t){if(e.exited)return;e.exited=!0,e.exitValue=t;for(let n of e.exitWaiters)n(t);e.exitWaiters=[];let n=e.eventWaiters;e.eventWaiters=[];for(let e of n)e(null);for(let t of e.stderrWaiters)t(e.stderr);e.stderrWaiters=[]}function Se(e,t){let n=t.sessionDir===void 0?[...e]:e.filter(e=>e!==`--no-session`);return t.sessionDir!==void 0&&(n.push(`--session-dir`,t.sessionDir),t.sessionId!==void 0&&n.push(`--session-id`,t.sessionId),t.sessionName!==void 0&&n.push(`--name`,t.sessionName)),n}const Ce={warn:e=>{console.warn(`[pi-ptc.lifecycle] `+e)}};function we(e){return e.length<=200?e:e.slice(0,200)+`…`}var Te=class{#e;constructor(e={}){this.#e=e.logger??Ce}spawn(e,t){if(e.length===0)throw TypeError(`RealChildProcessLifecycle.spawn: argv must include the command`);let n=Se(e,t),r=n[0],i=n.slice(1),a=ne(r,i,{cwd:t.cwd,shell:!1,stdio:[`ignore`,`pipe`,`pipe`],env:t.env}),o={proc:a,events:[],eventWaiters:[],exited:!1,exitValue:null,stderrWaiters:[],exitWaiters:[],stderr:``,lineBuffer:``},s={id:_e(),opaque:o},c=a.stdout;c&&c.on(`data`,e=>{o.lineBuffer+=e.toString(`utf-8`);let t=o.lineBuffer.indexOf(`
2
+ `);for(;t>=0;){let e=o.lineBuffer.slice(0,t);o.lineBuffer=o.lineBuffer.slice(t+1);let n=ve(e);n?be(o,n):e.trim().length>0&&this.#t(s,e),t=o.lineBuffer.indexOf(`
3
+ `)}});let l=a.stderr;return l&&l.on(`data`,e=>{o.stderr+=e.toString(`utf-8`)}),a.on(`close`,(e,t)=>{let n=o.lineBuffer.trim();if(n.length>0){let e=ve(n);e?be(o,e):this.#t(s,n),o.lineBuffer=``}xe(o,{code:e,signal:t})}),a.on(`error`,e=>{o.stderr+=`[spawn-error] `+e.message+`
4
+ `,o.exited||xe(o,{code:null,signal:null})}),s}kill(e,t){let n=e.opaque;ye(n.proc,t)}events(e){let t=e.opaque;return{async*[Symbol.asyncIterator](){for(;;){if(t.events.length>0){let e=t.events.shift();e!==void 0&&(yield e);continue}if(t.exited)return;let e=await new Promise(e=>{t.eventWaiters.push(e)});if(e===null)return;yield e}}}}exit(e){let t=e.opaque;return t.exited&&t.exitValue?Promise.resolve(t.exitValue):new Promise(e=>{t.exitWaiters.push(e)})}stderr(e){let t=e.opaque;return t.exited?Promise.resolve(t.stderr):new Promise(e=>{t.stderrWaiters.push(e)})}#t(e,t){this.#e.warn(`child ${e.id}: dropped a non-JSON stdout line (${t.length} chars): `+we(t))}};const B=Object.freeze({timeoutMs:12e4,maxTimeoutMs:6e5,maxOutputBytes:67108864,maxMessageBytes:134217728,maxPendingCalls:128,maxParallelSubCalls:10,dispatchConcurrency:8,maxDispatchDepth:3,maxItemsPerCall:4096,graceMs:3e3,maxOldGenerationSizeMb:512,maxYoungGenerationSizeMb:64,poolSize:4,poolAcquireTimeoutMs:3e4,drainGraceMs:5e3}),Ee=Object.freeze([`PATH`,`PATHEXT`,`SYSTEMROOT`,`WINDIR`,`TEMP`,`TMP`]);function De(e=process.env){let t={};for(let n of Ee){let r=e[n];typeof r==`string`&&r.length>0&&(t[n]=r)}return t}function Oe(e={}){let t={...B};for(let n of Object.keys(B)){let r=e[n];if(r!==void 0){if(typeof r!=`number`||!Number.isFinite(r)||r<=0)throw TypeError(`invalid PTC config override for ${n}: ${String(r)}`);t[n]=r}}return t}function ke(e,t=B){return e===void 0||!Number.isFinite(e)||e<=0?t.timeoutMs:Math.min(e,t.maxTimeoutMs)}const Ae={running:[`stopping`,`succeeded`,`failed`,`canceled`,`lost`],stopping:[`succeeded`,`failed`,`canceled`],succeeded:[],failed:[],canceled:[],lost:[]},je=/* @__PURE__ */ new Set([`succeeded`,`failed`,`canceled`,`lost`]),Me=/* @__PURE__ */ new Set([`running`,`stopping`]);function Ne(e){return e===`running`||e===`stopping`||e===`succeeded`||e===`failed`||e===`canceled`||e===`lost`}function Pe(e){return typeof e==`string`&&e.length>0}const Fe=/* @__PURE__ */ new Set([`session_ended_while_running`,`user_killed_via_esc`,`lost_on_session_restart`]);function Ie(e){return typeof e==`string`&&Fe.has(e)}function Le(e,t){let n=`task:${e}`;return t===`running`||t===`stopping`?`${n}:${t}`:`${n}:->${t}`}var Re=class{#e;#t;#n;#r;#i=/* @__PURE__ */ new Set;#a=Promise.resolve();constructor(e,t){this.#e=e,this.#t=t.clock,this.#n=t.logger,this.#r=he({now:()=>this.#t()})}async transition(e,t){let n=await this.#p(()=>this.#o(e,t)),r=n.events[n.events.length-1];return r!==void 0&&this.#m(n.record,r,t.logger),n}onTransition(e){this.#i.add(e);let t=!0;return()=>{t&&(t=!1,this.#i.delete(e))}}async#o(e,t){switch(e.kind){case`spawn`:return this.#s(e,t);case`transition`:return this.#c(e,t);case`stop`:return this.#l(e,t);case`reconcile-lost`:return this.#d(e,t);case`resolve-exit`:return this.#u(e,t)}throw Error(`transition: unknown command kind ${String(e.kind)}`)}async get(e){return Pe(e)?await this.#e.loadTask(e):null}async query(e){let t=e.limit??100;if(!Number.isInteger(t)||t<0)throw Error(`query: limit must be a non-negative integer, got ${String(e.limit)}`);let n=e.orderBy??`createdAt-desc`,r=e.label===void 0?void 0:{label:e.label},i=[];for await(let t of this.#e.listTasks(r))(e.status===void 0||e.status.includes(t.status))&&i.push(t);return i.sort((e,t)=>n===`createdAt-asc`?e.createdAt-t.createdAt:t.createdAt-e.createdAt),i.slice(0,t)}async reconcileLostTasks(){let e=[];for await(let t of this.#e.listTasks())Me.has(t.status)&&e.push(t);let t=[];for(let n of e){let e=await this.transition({kind:`reconcile-lost`,taskId:n.id,reason:`lost_on_session_restart`},{clock:this.#t,callerId:this.#y(n),logger:this.#n});t.push(e.record)}return this.#n?.info(`reconcileLostTasks: marked ${t.length} task(s) lost`),t}async#s(e,t){let n=Math.max(0,Math.floor(t.clock())),r=e.handle;if(r==null)throw Error(`spawn: handle is required`);if(r.status!==`running`)throw Error(`spawn: handle.status must be "running", got ${String(r.status)}`);let i=Pe(r.taskId),a=i?r.taskId:this.#r.next();if(await this.#e.loadTask(a)!==null)throw Error(`spawn: task ${a} already exists (duplicate spawn)`);let o={...e.record,id:a,label:e.record.label.length>0?e.record.label:r.label,status:`running`,createdAt:n,startedAt:n,finishedAt:void 0,durationMs:void 0,transitionAt:n,parentTaskId:e.parentTaskId??e.record.parentTaskId};i||(r.taskId=a);let s=t.callerId,c=this.#v(a,s,`running`,n,o);return await this.#e.saveSubscription({subscriberId:s,taskId:a,cursor:c.eventId,status:`active`,createdAt:n}),await this.#e.appendEvents(s,[c]),await this.#e.saveTask(o),(t.logger??this.#n)?.info(`spawn: task ${a} (${o.label}) running for subscriber ${s}`),{record:o,events:[c],cursor:c.eventId}}async#c(e,t){if(!Ne(e.to))throw Error(`transition: unknown target state ${String(e.to)}`);let n=await this.#g(e.taskId);if(this.#h(n,e.to),e.to===`lost`&&!Ie(e.reason))throw Error(`transition: to "lost" requires a LostReason (session_ended_while_running | user_killed_via_esc | lost_on_session_restart)`+(e.reason===void 0?``:`, got ${String(e.reason)}`));return this.#f(n,e.to,t,{reason:e.reason,stopReason:e.stopReason,errorMessage:e.errorMessage,exitCode:e.exitCode,outputRef:e.outputRef,outputBytes:e.outputBytes,outputPreview:e.outputPreview})}async#l(e,t){let n=await this.#g(e.taskId);return n.status===`stopping`?{record:n,events:[],cursor:(await this.#e.loadSubscription(t.callerId,n.id))?.cursor??n.id,fromStatus:n.status}:(this.#h(n,`stopping`),this.#f(n,`stopping`,t,{stopReason:e.reason}))}async#u(e,t){let n=await this.#g(e.taskId);if(n.status!==`running`&&n.status!==`stopping`)throw Error(`resolve-exit: task ${n.id} is terminal (${n.status}); the single terminal writer refuses to overwrite it`);let r=n.status===`stopping`?`canceled`:e.exitCode===0?`succeeded`:`failed`;return this.#h(n,r),this.#f(n,r,t,{exitCode:e.exitCode,outputRef:e.outputRef,outputBytes:e.outputBytes,outputPreview:e.outputPreview})}async#d(e,t){let n=await this.#g(e.taskId);if(!Me.has(n.status))throw Error(`reconcile-lost: task ${n.id} is terminal (${n.status}); only running/stopping tasks can be reconciled`);return this.#f(n,`lost`,t,{errorMessage:e.reason})}async#f(e,t,n,r){let i=Math.max(0,Math.floor(n.clock())),a={...e,status:t,transitionAt:i};je.has(t)&&(a.finishedAt=i,a.durationMs=i-e.startedAt),r.stopReason!==void 0&&(a.stopReason=r.stopReason),r.errorMessage!==void 0&&(a.errorMessage=r.errorMessage),r.exitCode!==void 0&&(a.exitCode=r.exitCode),r.outputRef!==void 0&&(a.outputRef=r.outputRef),r.outputBytes!==void 0&&(a.outputBytes=r.outputBytes),r.outputPreview!==void 0&&(a.outputPreview=r.outputPreview),r.reason!==void 0&&((t===`stopping`||t===`canceled`)&&a.stopReason===void 0&&(a.stopReason=r.reason),(t===`failed`||t===`lost`)&&a.errorMessage===void 0&&(a.errorMessage=r.reason));let o=n.callerId,s=this.#v(e.id,o,t,i,a);return await this.#_(o,e.id,i,s.eventId),await this.#e.appendEvents(o,[s]),await this.#e.saveTask(a),(n.logger??this.#n)?.info(`transition: task ${e.id} ${e.status} -> ${t} (subscriber ${o})`),{record:a,events:[s],cursor:s.eventId,fromStatus:e.status}}async#p(e){let t=this.#a.then(e,e);return this.#a=t.then(()=>void 0,()=>void 0),await t}#m(e,t,n){for(let r of this.#i)try{r(e,t)}catch(t){let r=t instanceof Error?t.message:String(t),i=`transition observer threw for task ${e.id}: ${r}`,a=n??this.#n;a===void 0?console.warn(`[task-registry] `+i):a.warn(i)}}#h(e,t){let n=Ae[e.status];if(!n.includes(t)){let r=n.length===0?`<terminal>`:n.join(`, `);throw Error(`illegal transition for task ${e.id}: ${e.status} -> ${t} (allowed from ${e.status}: ${r})`)}}async#g(e){if(!Pe(e))throw Error(`transition: taskId is required`);let t=await this.#e.loadTask(e);if(t===null)throw Error(`transition: unknown taskId ${e}`);return t}async#_(e,t,n,r){await this.#e.loadSubscription(e,t)===null&&(await this.#e.saveSubscription({subscriberId:e,taskId:t,cursor:r,status:`active`,createdAt:n}),this.#n?.warn(`subscription: auto-created for subscriber ${e} on task ${t} at cursor ${r}`))}#v(e,t,n,r,i){let a=i.outputBytes,o=a!==void 0&&a<=2048?i.outputPreview:void 0;return{eventId:this.#r.next(),subscriptionId:t,taskId:e,type:Le(e,n),status:n,transitionAtMs:r,outputBytes:a,outputPreview:o}}#y(e){let t=e.spawnSource.callerId;return t.length>0?t:e.id}};function ze(e,t){return new Re(e,t)}function Be(e,t){return e.eventId<t.eventId?-1:+(e.eventId>t.eventId)}function Ve(e){for(let t=1;t<e.length;t++)if(Be(e[t-1],e[t])>0)return!1;return!0}var He=class e{#e;#t;#n;#r;constructor(){this.#e=/* @__PURE__ */ new Map,this.#t=/* @__PURE__ */ new Map,this.#n=/* @__PURE__ */ new Set,this.#r=/* @__PURE__ */ new Map}static#i(e,t){return`${e}-${t}`}#a(e){return this.#n.has(e)}async loadTask(e){let t=this.#e.get(e);return t===void 0?null:structuredClone(t)}async saveTask(e){M(e,`saveTask: record is required`),this.#e.set(e.id,structuredClone(e))}async deleteTask(e){if(!this.#e.has(e))throw Error(`deleteTask: unknown taskId ${e}`);this.#e.delete(e)}async*listTasks(e){for(let t of this.#e.values())(e?.status===void 0||t.status===e.status)&&(e?.label===void 0||t.label===e.label)&&(yield structuredClone(t))}async loadSubscription(t,n){let r=this.#t.get(e.#i(t,n));return r===void 0?null:structuredClone(r)}async saveSubscription(t){M(t,`saveSubscription: sub is required`);let n=e.#i(t.subscriberId,t.taskId);this.#t.set(n,structuredClone(t)),this.#n.add(t.subscriberId)}async appendEvents(e,t){if(!this.#a(e))throw Error(`appendEvents: unknown subscriptionId ${e}`);if(t.length===0)return;let n=this.#r.get(e)??[],r=Ve(t)?[...t]:[...t].sort(Be),i=n.length===0?void 0:n[n.length-1]?.eventId,a=r[0]?.eventId;if(i===void 0||a!==void 0&&i<=a){for(let e of r)n.push(structuredClone(e));this.#r.set(e,n);return}let o=[...n,...r.map(e=>structuredClone(e))];o.sort(Be),this.#r.set(e,o)}async*loadEvents(e,t){if(!this.#a(e))throw Error(`loadEvents: unknown subscriptionId ${e}`);let n=this.#r.get(e)??[],r=0;if(t!==void 0){let e=0,i=n.length;for(;e<i;){let r=e+i>>>1;(n[r]?.eventId??``)<=t?e=r+1:i=r}r=e}for(let e=r;e<n.length;e++)yield structuredClone(n[e])}};const V=new Te;var Ue=class{limit;#e;#t=/* @__PURE__ */ new Set;constructor(e){if(!Number.isInteger(e)||e<0)throw TypeError(`DispatchSlotCounter: limit must be a non-negative integer, got ${String(e)}`);this.limit=e,this.#e=0}get active(){return this.#e}tryAcquire(e){return e!==void 0&&this.#t.has(e)||this.#e>=this.limit?!1:(e!==void 0&&this.#t.add(e),this.#e+=1,!0)}release(e){if(e!==void 0){if(!this.#t.delete(e))return;--this.#e;return}this.#e>this.#t.size&&--this.#e}};const We=new Ue(B.dispatchConcurrency);let Ge;function Ke(e,t){return e.taskRegistry===void 0?(Ge??=new Re(new He,{clock:t}),Ge):e.taskRegistry}const qe={info:()=>void 0,warn:e=>{console.warn(`[pi.dispatch] `+e)}};function Je(e,t,n){let r=[`<pi-ptc-context depth="`+t+`" max-depth="`+n+`">`,`You are a PTC run at depth `+t+` (root is depth 0). You may write PTC programs and you may`,`call pi.dispatch(...) to spawn further children, but each level costs a fresh pi subprocess.`,`The remaining depth budget is `+n+` - `+t+`. Beyond it, pi.dispatch rejects with`,`{ status: "rejected", errorMessage: "dispatch depth limit reached" }.`,`</pi-ptc-context>`].join(`
2
5
  `);return e.length===0?r:e+`
3
6
 
4
- `+r}function se(){return{text:``,status:`rejected`,started:!1,agentName:`unknown`,durationMs:0,exitCode:-1,errorMessage:`dispatch depth limit reached`}}function ce(){return{text:``,status:`rejected`,started:!1,agentName:`unknown`,durationMs:0,exitCode:-1,errorMessage:`dispatch concurrency limit reached`}}function le(e){return e.aborted?{status:`rejected`,errorMessage:`dispatch cancelled`}:e.exitCode===0&&e.finalText.length>0?{status:`fulfilled`}:e.exitCode===0?{status:`rejected`,errorMessage:`dispatch produced no final text`}:{status:`rejected`}}function ue(e,t){if(e.pid===void 0)return!1;try{return e.kill(t),!0}catch{return!1}}function I(e,t){let n=e.match(RegExp(`^`+t+`:\\s*(.+)$`,`m`));if(!n)return;let r=(n[1]??``).trim();if(r.length>=2){let e=r[0],t=r[r.length-1];(e===`"`&&t===`"`||e===`'`&&t===`'`)&&(r=r.slice(1,-1))}return r.length===0?void 0:r}function de(e,t){let n=e.match(RegExp(`^`+t+`:\\s*(.+)$`,`m`));if(!n)return;let r=(n[1]??``).trim();r.startsWith(`[`)&&r.endsWith(`]`)&&(r=r.slice(1,-1));let i=r.split(`,`).map(e=>e.trim()).filter(e=>e.length>0);return i.length===0?void 0:i}function fe(e){let t=e.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!t)return null;let n=t[1]??``,r=t[2]??``,i=I(n,`name`);if(!i)return null;let a=I(n,`description`)??``,o=I(n,`model`)??``,s=de(n,`tools`),c={name:i,systemPrompt:r.trim()};return a!==void 0&&(c.description=a),o!==void 0&&(c.model=o),s!==void 0&&(c.tools=s),c}function pe(e,t,n){let r=O.join(D.homedir(),`.pi`,`agent`,`agents`),i=O.join(t,`.pi`,`agents`),a=[];(n===`user`||n===`both`)&&a.push({path:O.join(r,e+`.md`),source:`user`}),(n===`project`||n===`both`)&&a.push({path:O.join(i,e+`.md`),source:`project`});for(let t of a){let n;try{n=C.readFileSync(t.path,`utf-8`)}catch{continue}let r=fe(n);if(r&&r.name===e){let e={name:r.name,source:t.source,systemPrompt:r.systemPrompt};return r.model!==void 0&&(e.model=r.model),r.tools!==void 0&&(e.tools=r.tools),e}}return null}function me(e,t,n){let r=[`--mode`,`json`,`-p`,`--no-session`],i=e.model??t.model;i&&r.push(`--model`,i);let a=e.thinkingLevel??`off`;return r.push(`--thinking`,a),t.tools&&t.tools.length>0&&r.push(`--tools`,t.tools.join(`,`)),r.push(`--append-system-prompt`,n),r.push(`Task: `+e.task),r}function he(e){let t=e.trim();if(t.length===0)return null;try{return JSON.parse(t)}catch{return null}}async function ge(e,t){let n=await C.promises.mkdtemp(O.join(D.tmpdir(),`pi-dispatch-`)),r=e.replace(/[^\w.-]+/g,`_`),i=O.join(n,`prompt-`+r+`.md`);return await C.promises.writeFile(i,t,{encoding:`utf-8`,mode:384}),{dir:n,filePath:i}}function _e(e){try{C.unlinkSync(e.filePath)}catch{}try{C.rmdirSync(e.dir)}catch{}}async function ve(e,t){let n=t.depth+1;if(n>t.maxDispatchDepth)return se();let r=e.cwd??t.cwd,i=e.agentScope??`user`,a=Date.now(),o=pe(e.agent,r,i);if(!o)return{text:``,status:`rejected`,started:!1,agentName:e.agent,durationMs:Date.now()-a,exitCode:1,errorMessage:`unknown agent: `+e.agent+` (agentScope=`+i+`, cwd=`+r+`)`};let s=oe(o.systemPrompt,n,t.maxDispatchDepth),c=await ge(o.name,s),l=me(e,o,c.filePath);return await new Promise(e=>{let i=``,s=``,u=``,d,f=-1,p=!1,m,h,g=!1,_=(n,r,i=!0)=>{if(p)return;p=!0,m&&clearTimeout(m),t.signal&&t.signal.removeEventListener(`abort`,v),_e(c);let l={text:u,status:n,started:i,agentName:o.name,durationMs:Date.now()-a,exitCode:f};d&&(l.usage=d),s.length>0&&(l.stderr=s),r&&(l.errorMessage=r),e(l)},v=()=>{!h||h.killed||p||(g=!0,ue(h,`SIGTERM`),m=setTimeout(()=>{h&&!h.killed&&ue(h,`SIGKILL`)},5e3))};try{h=ee(`pi`,l,{cwd:r,shell:!1,stdio:[`ignore`,`pipe`,`pipe`],env:{...process.env,PI_PTC_DEPTH:String(n)}})}catch(e){_(`rejected`,`failed to spawn pi: `+(e instanceof Error?e.message:String(e)),!1);return}t.signal&&(t.signal.aborted?v():t.signal.addEventListener(`abort`,v,{once:!0}));let y=h.stdout;if(!y)return;y.on(`data`,e=>{i+=e.toString(`utf-8`);let t=i.indexOf(`
5
- `);for(;t>=0;){let e=i.slice(0,t);i=i.slice(t+1);let n=he(e);if(!n){t=i.indexOf(`
6
- `);continue}if(n.type===`message_end`&&n.message&&n.message.role===`assistant`){let e=n.message;if(e.usage){let t=d??{input:0,output:0,cacheRead:0,cacheWrite:0,cost:0,turns:0};t.input+=e.usage.input??0,t.output+=e.usage.output??0,t.cacheRead+=e.usage.cacheRead??0,t.cacheWrite+=e.usage.cacheWrite??0,t.cost+=e.usage.cost?.total??0,t.turns+=1,d=t}if(Array.isArray(e.content))for(let t of e.content)t&&t.type===`text`&&typeof t.text==`string`&&(u=t.text)}t=i.indexOf(`
7
- `)}});let b=h.stderr;b&&(b.on(`data`,e=>{s+=e.toString(`utf-8`)}),h.on(`close`,e=>{f=e??-1;let{status:t,errorMessage:n}=le({exitCode:f,finalText:u,aborted:g});_(t,n)}),h.on(`error`,e=>{s+=`[spawn-error] `+e.message+`
8
- `,_(`rejected`,`failed to spawn pi: `+e.message,!1)}))})}const L=[`read`,`bash`,`edit`,`write`,`grep`,`find`,`ls`],ye=`pi.dispatch`,be=L,xe={read:m,bash:l,edit:u,write:h,grep:f,find:d,ls:p};function Se(e){let t=e.names??be,n=e.includeDispatch??e.names===void 0,r=/* @__PURE__ */ new Map;for(let n of t){let t=xe[n];if(!t)throw TypeError(`unknown PTC binding "${n}"; known bindings: ${L.join(`, `)}`);let i=t(e.cwd);r.set(n,{name:n,execute:async(e,t)=>{let n=`ptc:${t.callId}`,r={type:`toolCall`,id:n,name:i.name,arguments:e},a=S(i,r),o=await i.execute(n,a,t.signal,void 0);return{content:o.content,details:o.details===void 0?null:o.details}}})}return n&&r.set(ye,{name:ye,execute:async(t,n)=>ve(t,{signal:n.signal,callId:n.callId,cwd:e.cwd,depth:n.depth,maxDispatchDepth:n.maxDispatchDepth})}),r}const R=Object.freeze({timeoutMs:12e4,maxTimeoutMs:6e5,maxOutputBytes:67108864,maxMessageBytes:134217728,maxPendingCalls:128,maxParallelSubCalls:10,dispatchConcurrency:8,maxDispatchDepth:3,maxItemsPerCall:4096,graceMs:3e3,maxOldGenerationSizeMb:512,maxYoungGenerationSizeMb:64,poolSize:4,poolAcquireTimeoutMs:3e4,drainGraceMs:5e3}),Ce=Object.freeze([`PATH`,`PATHEXT`,`SYSTEMROOT`,`WINDIR`,`TEMP`,`TMP`]);function we(e=process.env){let t={};for(let n of Ce){let r=e[n];typeof r==`string`&&r.length>0&&(t[n]=r)}return t}function Te(e={}){let t={...R};for(let n of Object.keys(R)){let r=e[n];if(r!==void 0){if(typeof r!=`number`||!Number.isFinite(r)||r<=0)throw TypeError(`invalid PTC config override for ${n}: ${String(r)}`);t[n]=r}}return t}function Ee(e,t=R){return e===void 0||!Number.isFinite(e)||e<=0?t.timeoutMs:Math.min(e,t.maxTimeoutMs)}function De(){let e=/* @__PURE__ */ new Map;return{recordStart(t,n,r){if(e.has(t))throw Error(`duplicate sub-call callId ${t}`);e.set(t,{callId:t,name:n,args:r,status:`running`,startMs:Date.now()})},recordEnd(t,n,r){let i=e.get(t);if(i===void 0)throw Error(`recordEnd on unknown callId ${t}`);return i.status=n,i.endMs=Date.now(),i.durationMs=i.endMs-i.startMs,n===`rejected`&&(i.durationMs=0),r?.errorMessage!==void 0&&(i.errorMessage=r.errorMessage),r?.resultSummary!==void 0&&(i.resultSummary=r.resultSummary),i},snapshot(){return[...e.values()].map(e=>({...e}))}}}function Oe(e){return{name:`ptc-${e.surface}`,env:e.env,workerData:e.runId===void 0?{env:e.env}:{runId:e.runId,env:e.env},resourceLimits:{maxOldGenerationSizeMb:e.limits.maxOldGenerationSizeMb,maxYoungGenerationSizeMb:e.limits.maxYoungGenerationSizeMb}}}const z={acquireLatency:N.channel(`ptc:pool:acquire-latency`),resetTime:N.channel(`ptc:worker:reset-time`),imageBytes:N.channel(`ptc:image:hoist-bytes`)};function B(e,t){e.publish(t)}var ke=class{#e;#t;#n;#r;#i;#a=[];#o=/* @__PURE__ */ new Set;#s=[];#c=/* @__PURE__ */ new WeakMap;#l=0;#u=0;#d=!1;constructor(e){if(typeof e.buildWorkerUrl!=`function`)throw TypeError(`WorkerPool: buildWorkerUrl must be a function`);if(e.size!==void 0&&(typeof e.size!=`number`||!Number.isFinite(e.size)||e.size<=0))throw TypeError(`WorkerPool: size must be a positive finite number, received ${String(e.size)}`);if(e.acquireTimeoutMs!==void 0&&(typeof e.acquireTimeoutMs!=`number`||!Number.isFinite(e.acquireTimeoutMs)||e.acquireTimeoutMs<=0))throw TypeError(`WorkerPool: acquireTimeoutMs must be a positive finite number, received ${String(e.acquireTimeoutMs)}`);if(e.drainGraceMs!==void 0&&(typeof e.drainGraceMs!=`number`||!Number.isFinite(e.drainGraceMs)||e.drainGraceMs<=0))throw TypeError(`WorkerPool: drainGraceMs must be a positive finite number, received ${String(e.drainGraceMs)}`);this.#e=e.buildWorkerUrl,this.#t=e.size??4,this.#n=e.acquireTimeoutMs??3e4,this.#r=e.drainGraceMs??5e3,this.#i=e.workerOptions}async acquire(){if(this.#d)throw Error(`pool has been drained and can no longer serve workers`);this.#l+=1;let e=Date.now(),t=this.#a.shift();for(;t!==void 0&&!this.#p(t);)t=this.#a.shift();if(t!==void 0)return this.#o.add(t),t.ref(),B(z.acquireLatency,{poolSize:this.#t,waiters:this.#s.length,durationMs:Date.now()-e}),t;if(this.#o.size<this.#t){let t=this.#f();return this.#o.add(t),B(z.acquireLatency,{poolSize:this.#t,waiters:this.#s.length,durationMs:Date.now()-e}),t}return this.#u+=1,await new Promise((t,n)=>{let r=setTimeout(()=>{let e=this.#s.findIndex(e=>e.timer===r);e>=0&&this.#s.splice(e,1),n(/* @__PURE__ */ Error(`pool acquire timed out after ${this.#n} ms`))},this.#n);this.#s.push({resolve:t,reject:n,timer:r,startedAt:e})})}release(e){if(!this.#o.delete(e)||!this.#p(e))return;this.#c.set(e,Date.now());let t=this.#s.shift();if(t){clearTimeout(t.timer),this.#o.add(e),B(z.acquireLatency,{poolSize:this.#t,waiters:this.#s.length,durationMs:Date.now()-t.startedAt}),t.resolve(e);return}this.#a.push(e),e.unref()}lastSettledAt(e){return this.#c.get(e)}async drain(){for(;this.#s.length>0;){let e=this.#s.shift();if(!e)break;clearTimeout(e.timer),e.reject(/* @__PURE__ */ Error(`pool drained before acquire could be satisfied`))}let e=Date.now()+this.#r;for(;this.#o.size>0&&Date.now()<e;)await new Promise(e=>setTimeout(e,5));let t=[...this.#a,...this.#o];this.#a.length=0,this.#o.clear(),await Promise.all(t.map(e=>e.terminate().catch(()=>void 0))),this.#d=!0}stats(){return{resident:this.#o.size+this.#a.length,inFlight:this.#o.size,waiters:this.#s.length,totalAcquires:this.#l,poolExhaustions:this.#u}}#f(){let e=new M(this.#e(),this.#i);return e.once(`exit`,()=>{let t=this.#a.indexOf(e);if(t>=0){this.#a.splice(t,1);return}this.#o.delete(e)}),e}#p(e){return e.threadId!==-1}};function Ae(e){B(z.imageBytes,{byteLength:e})}function je(e){B(z.resetTime,{durationMs:e})}function Me(){let e=k(P(import.meta.url)),t=[ne(e,`worker.js`),ne(e,`worker-entry.ts`)];for(let e of t)if(w(e))return ie(e);throw Error(`worker entry not found; tried: ${t.join(`, `)}`)}function Ne(e){if(typeof e!=`object`||!e)return;let t=e;if(t.status!==`rejected`)return;let n=typeof t.errorMessage==`string`?t.errorMessage:t.started===!1?`dispatch refused`:`dispatch failed`;return{kind:t.started===!1?`refused`:`failed`,message:n}}function V(e){return e instanceof Error?e.message:String(e)}function Pe(e){if(typeof e!=`object`||!e)return;let t=e.content;if(Array.isArray(t)&&t.length>0){let e=t[0];if(typeof e==`object`&&e){let t=e.text;if(typeof t==`string`&&t.length>0){let e=t.indexOf(`
9
- `);return e===-1?t:t.slice(0,e)}}}}function Fe(e){return e instanceof ArrayBuffer||ArrayBuffer.isView(e)}function Ie(e,t){let n=/* @__PURE__ */ new WeakSet,r=e=>{if(e!=null){if(e instanceof ArrayBuffer){t(e);return}if(ArrayBuffer.isView(e)){t(e);return}if(typeof e==`object`&&!n.has(e)){if(n.add(e),Array.isArray(e)){for(let t of e)r(t);return}for(let t of Object.keys(e))r(e[t])}}};r(e)}function Le(e){try{let t=Buffer.byteLength(JSON.stringify(e,(e,t)=>Fe(t)?null:t)??``,`utf8`),n=0;return Ie(e,e=>{n+=e.byteLength}),t+n}catch{return 1/0}}async function Re(e){let i=Te(e.config),s=we(),c=e.runId??j(),l=Ee(e.timeoutMs,i),u=[],d=[],f=[];if(e.signal?.aborted)return{logs:u,narrations:d,phases:f,error:{kind:n.abort,message:`run cancelled before start`}};let p;try{p=e.pool?await e.pool.acquire():new M(Me(),Oe({surface:e.surface,env:s,limits:i,runId:c}))}catch(e){return{logs:u,narrations:d,phases:f,error:{kind:n.workerExit,message:`pool acquire failed: ${V(e)}`}}}return await new Promise(s=>{let m=new re,h=m.port1,g=m.port2,_=new AbortController,v=!1,y=De(),b=()=>{v||e.onSubCallChange?.(()=>y.snapshot())},x={recordStart(e,t,n){y.recordStart(e,t,n),b()},recordEnd(e,t,n){let r=y.recordEnd(e,t,n);return b(),r},snapshot:()=>y.snapshot()},S,ee=!1,C=0,w=0,T=0,E=0,D=[],te=e.depth??0,O,k,A=[],ne=e=>{let t=[],n=e?.content;if(!Array.isArray(n))return t;for(let e of n){if(typeof e!=`object`||!e)continue;let n=e;if(n.type!==`image`)continue;let r;typeof n.data==`string`&&n.data.length>0?r=n.data:n.bytes instanceof ArrayBuffer&&n.bytes.byteLength>0&&(r=Buffer.from(n.bytes).toString(`base64`)),r!==void 0&&t.push({data:r,mimeType:typeof n.mimeType==`string`?n.mimeType:`application/octet-stream`})}return t},j=e=>e===`timeout`?`run timed out after ${l} ms`:`run cancelled`,M=e=>e===`timeout`?n.timeout:n.abort,N=t=>{if(v)return;v=!0,O&&clearTimeout(O),k&&clearTimeout(k),e.signal?.removeEventListener(`abort`,pe),_.abort(),h.close(),e.pool?e.pool.release(p):p.terminate();let n=x.snapshot();s(n.length>0?{...t,subCalls:n}:t)},P=(e,t,n)=>{N({logs:u,narrations:d,phases:f,error:n===void 0?{kind:e,message:t}:{kind:e,message:t,stack:n}})},ie=e=>{if(!v){if(!r(e)){P(n.protocol,`internal error: malformed host frame`);return}h.postMessage(e)}},ae=()=>{let t={kind:o.init,runId:c,surface:e.surface,code:e.code,bindings:[...e.bindings.keys()],bindingCandidates:[...L],maxItemsPerCall:i.maxItemsPerCall,maxPendingCalls:i.maxPendingCalls,...e.args===void 0?{}:{args:e.args}};if(!r(t)){P(n.protocol,`internal error: malformed init frame`);return}let a=Le(t);if(a>i.maxMessageBytes){P(n.protocol,`init frame of ${a} bytes exceeds maxMessageBytes (${i.maxMessageBytes})`);return}ie(t)},F=(e,t)=>(w+=e,w<=i.maxOutputBytes||(P(n.outputLimit,`${t} exceeded the output budget: logs + result reached ${w} bytes (maxOutputBytes=${i.maxOutputBytes}); ${u.length} log line(s) retained`),!1)),oe=async()=>{if(E<i.maxParallelSubCalls){E+=1;return}await new Promise(e=>{D.push(e)})},se=()=>{let e=D.shift();if(e){e();return}--E},le=e=>{if(x.recordStart(e.callId,e.tool,e.args),C+=1,C>i.maxPendingCalls){x.recordEnd(e.callId,`error`,{errorMessage:`more than maxPendingCalls (${i.maxPendingCalls}) binding calls in flight`}),P(n.protocol,`more than maxPendingCalls (${i.maxPendingCalls}) binding calls in flight`);return}ue(e).finally(()=>{--C})},ue=async t=>{let r=e.bindings.get(t.tool);if(!r){x.recordEnd(t.callId,`error`,{errorMessage:`no binding named "${t.tool}" in this run; available bindings: `+([...e.bindings.keys()].join(`, `)||`(none)`)}),I({kind:o.callResult,callId:t.callId,tool:t.tool,ok:!1,message:`no binding named "${t.tool}" in this run; available bindings: `+([...e.bindings.keys()].join(`, `)||`(none)`)});return}let a=t.tool===ye;if(a){if(T>=i.dispatchConcurrency){x.recordEnd(t.callId,`rejected`,{errorMessage:`dispatch concurrency limit reached`}),I({kind:o.callResult,callId:t.callId,tool:t.tool,ok:!0,value:ce()});return}T+=1}else if(await oe(),v){x.recordEnd(t.callId,`cancelled`,{errorMessage:`run settled before binding started`}),se();return}try{let e=await r.execute(t.args,{signal:_.signal,callId:t.callId,depth:te,maxDispatchDepth:i.maxDispatchDepth}),s=ne(e),c={kind:o.callResult,callId:t.callId,tool:t.tool,ok:!0,value:e},l=Le(c);if(l>i.maxMessageBytes){x.recordEnd(t.callId,`error`,{errorMessage:`callResult frame of ${l} bytes exceeds maxMessageBytes (${i.maxMessageBytes})`}),P(n.protocol,`callResult frame of ${l} bytes exceeds maxMessageBytes (${i.maxMessageBytes})`);return}if(I(c))for(let e of s)A.push(e),Ae(Buffer.byteLength(e.data,`base64`));if(_.signal.aborted)x.recordEnd(t.callId,`cancelled`,{errorMessage:`binding aborted by run cancellation`});else{let n=a?Ne(e):void 0;n===void 0?x.recordEnd(t.callId,`ok`,{resultSummary:Pe(e)}):x.recordEnd(t.callId,n.kind===`refused`?`rejected`:`error`,{errorMessage:n.message})}}catch(e){_.signal.aborted?x.recordEnd(t.callId,`cancelled`,{errorMessage:V(e)}):x.recordEnd(t.callId,`error`,{errorMessage:V(e)}),I({kind:o.callResult,callId:t.callId,tool:t.tool,ok:!1,message:V(e)})}finally{a?--T:se()}},I=e=>{if(v)return!1;try{return h.postMessage(e),!0}catch(t){try{h.postMessage({kind:o.callResult,callId:e.callId,tool:e.tool,ok:!1,message:`binding result could not be transferred: ${V(t)}`})}catch{P(n.protocol,`binding result is not transferable`)}return!1}},de=e=>S?{logs:u,narrations:d,phases:f,error:{kind:M(S),message:j(S)}}:e,fe=r=>{if(v)return;if(!a(r)){P(n.protocol,`malformed worker frame`);return}let o=r.kind===t.ready?0:Le(r);if(!Number.isFinite(o)){P(n.protocol,`worker frame could not be serialized for accounting`);return}if(o>i.maxMessageBytes){P(n.protocol,`control frame of ${o} bytes exceeds maxMessageBytes (${i.maxMessageBytes})`);return}switch(r.kind){case t.ready:if(e.pool){let t=e.pool.lastSettledAt(p);t!==void 0&&je(Date.now()-t)}if(!ee&&(ee=!0,S)){me(S);return}ae();return;case t.call:le(r);return;case t.log:F(o,`console output`)&&u.push(r.text);return;case t.narration:F(o,`workflow narration`)&&d.push(r.message);return;case t.phase:F(o,`workflow phase`)&&f.push(r.title);return;case t.result:{if(!F(o,`completion value`))return;let e={logs:u,narrations:d,phases:f,...A.length>0?{images:A}:{}};N(de(r.value===void 0?e:{...e,value:r.value}));return}case t.error:N(de({logs:u,narrations:d,phases:f,error:r.error}));return;default:P(n.protocol,`unknown worker frame`)}};function pe(){he(`abort`)}let me=e=>{k&&clearTimeout(k),k=setTimeout(()=>{P(M(e),j(e))},i.graceMs)};function he(e){if(!v){if(S){P(M(S),j(S));return}S=e,_.abort(),ie({kind:o.cancel,reason:e}),me(e)}}p.on(`error`,e=>{P(n.workerExit,`worker failed: ${V(e)}`)}),p.on(`exit`,e=>{P(n.workerExit,`worker exited with code ${e} before the run completed`)}),h.on(`message`,fe),h.on(`messageerror`,()=>{P(n.protocol,`worker sent a frame that could not be deserialized`)}),e.signal?.addEventListener(`abort`,pe,{once:!0}),O=setTimeout(()=>{he(`timeout`)},l);let ge={kind:o.connect,port:g};r(ge)?p.postMessage(ge,[g]):P(n.protocol,`internal error: malformed connect frame`)})}const ze=200,Be=new RegExp([`(?:\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\u005C|\\u009C))`,`[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]`].join(`|`),`g`);function Ve(e){return!e.includes(`\x1B`)&&!e.includes(`›`)?e:e.replace(Be,``)}function H(e){let t=Ve(e).replace(/\r/g,``);if(t.length===0)return t;let n=``;for(let e of t){let t=e.codePointAt(0);if(t!==void 0){if(t===10||t===9){n+=e;continue}t<=31||t>=65529&&t<=65531||(n+=e)}}return n}const He=/^[A-Za-z_$][A-Za-z0-9_$]*$/;function U(e){return He.test(e)?e:JSON.stringify(e)}function Ue(e){if(e===null)return`null`;if(typeof e==`number`||typeof e==`boolean`)return String(e);if(typeof e==`string`){let t=H(e);return t.includes(`
10
- `)?void 0:JSON.stringify(t)}if(Array.isArray(e)){let t=[];for(let n of e){let e=Ue(n);if(e===void 0)return;t.push(e)}return`[${t.join(`, `)}]`}let t=Object.keys(e);if(t.length===0)return`{}`;let n=[];for(let r of t){let t=Ue(e[r]??null);if(t===void 0)return;n.push(`${U(r)}: ${t}`)}return`{${n.join(`, `)}}`}function We(e){let t=Ue(e);if(t!==void 0)return t.length<=100?t:void 0}function Ge(e){return e===null?`null`:typeof e==`string`?H(e):String(e)}function Ke(e,t){if(typeof e!=`object`||!e)return[`${t}${Ge(e)}`];if(Array.isArray(e)){if(e.length===0)return[`${t}[]`];let n=[`${t}[`];for(let r of e){let e=We(r);if(e!==void 0){n.push(`${t} ${e}`);continue}n.push(...qe(r,t+` `))}return n.push(`${t}]`),n}let n=Object.keys(e);if(n.length===0)return[`${t}{}`];let r=[`${t}{`],i=t+` `;for(let t of n){let n=e[t]??null,a=We(n);if(a!==void 0){r.push(`${i}${U(t)}: ${a}`);continue}if(typeof n==`string`){r.push(`${i}${U(t)}:`);for(let e of H(n).split(`
11
- `))r.push(e.length===0?``:`${i} ${e}`);continue}let o=Ke(n,i);r.push(`${i}${U(t)}: ${(o[0]??``).trimStart()}`.trimEnd(),...o.slice(1))}return r.push(`${t}}`),r}function qe(e,t){return typeof e==`string`?H(e).split(`
12
- `):typeof e!=`object`||!e?[String(e)]:Ke(e,t)}function Je(e){return e.split(`
7
+ `+r}function Ye(){return{text:``,status:`rejected`,started:!1,agentName:`unknown`,durationMs:0,exitCode:-1,errorMessage:`dispatch depth limit reached`}}const Xe=`dispatch concurrency limit reached`;function Ze(){return{text:``,status:`rejected`,started:!1,agentName:`unknown`,durationMs:0,exitCode:-1,errorMessage:Xe}}function Qe(e,t,n={}){e.kill(t,`SIGTERM`);let r=setTimeout(()=>{n.isDone?.()!==!0&&e.kill(t,`SIGKILL`)},5e3);return n.unref===!0&&r.unref(),()=>clearTimeout(r)}function $e(){return{...Ye(),errorMessage:`dispatch depth limit reached; next_step: call ptc_task_list to inspect the in-flight background tasks`}}function et(){return{...Ze(),errorMessage:`dispatch concurrency limit reached; next_step: call ptc_task_list to inspect running tasks and ptc_task_stop to free a slot`}}function tt(e){return e.aborted?{status:`rejected`,errorMessage:`dispatch cancelled`}:e.exitCode===0&&e.finalText.length>0?{status:`fulfilled`}:e.exitCode===0?{status:`rejected`,errorMessage:`dispatch produced no final text`}:{status:`rejected`}}function nt(e,t){let n=e.match(RegExp(`^`+t+`:\\s*(.+)$`,`m`));if(!n)return;let r=(n[1]??``).trim();if(r.length>=2){let e=r[0],t=r[r.length-1];(e===`"`&&t===`"`||e===`'`&&t===`'`)&&(r=r.slice(1,-1))}return r.length===0?void 0:r}function rt(e,t){let n=e.match(RegExp(`^`+t+`:\\s*(.+)$`,`m`));if(!n)return;let r=(n[1]??``).trim();r.startsWith(`[`)&&r.endsWith(`]`)&&(r=r.slice(1,-1));let i=r.split(`,`).map(e=>e.trim()).filter(e=>e.length>0);return i.length===0?void 0:i}function it(e){let t=e.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!t)return null;let n=t[1]??``,r=t[2]??``,i=nt(n,`name`);if(!i)return null;let a=nt(n,`description`)??``,o=nt(n,`model`)??``,s=rt(n,`tools`),c={name:i,systemPrompt:r.trim()};return a!==void 0&&(c.description=a),o!==void 0&&(c.model=o),s!==void 0&&(c.tools=s),c}function at(e,t,n){let r=D.join(E.homedir(),`.pi`,`agent`,`agents`),i=D.join(t,`.pi`,`agents`),a=[];(n===`user`||n===`both`)&&a.push({path:D.join(r,e+`.md`),source:`user`}),(n===`project`||n===`both`)&&a.push({path:D.join(i,e+`.md`),source:`project`});for(let t of a){let n;try{n=C.readFileSync(t.path,`utf-8`)}catch{continue}let r=it(n);if(r&&r.name===e){let e={name:r.name,source:t.source,systemPrompt:r.systemPrompt};return r.model!==void 0&&(e.model=r.model),r.tools!==void 0&&(e.tools=r.tools),e}}return null}function ot(e,t,n){let r=[`--mode`,`json`,`-p`,`--no-session`],i=e.model??t.model;i&&r.push(`--model`,i);let a=e.thinkingLevel??`off`;return r.push(`--thinking`,a),t.tools&&t.tools.length>0&&r.push(`--tools`,t.tools.join(`,`)),r.push(`--append-system-prompt`,n),r.push(`Task: `+e.task),r}async function st(e,t){let n=await C.promises.mkdtemp(D.join(E.tmpdir(),`pi-dispatch-`)),r=e.replace(/[^\w.-]+/g,`_`),i=D.join(n,`prompt-`+r+`.md`);return await C.promises.writeFile(i,t,{encoding:`utf-8`,mode:384}),{dir:n,filePath:i}}function ct(e){try{C.unlinkSync(e.filePath)}catch{}try{C.rmdirSync(e.dir)}catch{}}function lt(e){if(e.type!==`message_end`||e.message?.role!==`assistant`)return;let t=e.message.content;if(!Array.isArray(t))return;let n;for(let e of t)e&&e.type===`text`&&typeof e.text==`string`&&(n=e.text);return n}async function ut(e,t,n){let r=t.depth+1;if(r>t.maxDispatchDepth)return $e();let i=_e(),a=n.slots??We;if(!a.tryAcquire(i))return et();let o=n.clock??(()=>Date.now()),s=n.logger??qe,c=e.cwd??t.cwd,l=e.agentScope??`user`,u=n.lifecycle??V,d=t.callerId??`dispatch:`+String(t.callId),f=Ke(n,o),p=n.outputStorage,m,h;try{let n=at(e.agent,c,l);if(!n)return a.release(i),{text:``,status:`rejected`,started:!1,agentName:e.agent,durationMs:0,exitCode:1,errorMessage:`unknown agent: `+e.agent+` (agentScope=`+l+`, cwd=`+c+`)`};let g=e.label??e.task.slice(0,64),_=Je(n.systemPrompt,r,t.maxDispatchDepth),v=await st(n.name,_);h=v;let y=ot(e,n,v.filePath),b={cwd:c,env:{...process.env,PI_PTC_DEPTH:String(r),PI_PTC_TASK_ID:i},promptFile:v.filePath};t.sessionDir!==void 0&&(b.sessionDir=t.sessionDir,b.sessionId=i,b.sessionName=`bgdispatch:`+i),m=u.spawn([`pi`,...y],b);let x=m,S={kind:`ptc-program`,callerId:d};await f.transition({kind:`spawn`,handle:{taskId:i,label:g,status:`running`},record:{label:g,agentName:n.name,depth:r,startedAt:Math.max(0,Math.floor(o())),finishedAt:void 0,durationMs:void 0,outputRef:void 0,outputBytes:void 0,outputPreview:void 0,stopReason:void 0,errorMessage:void 0,exitCode:void 0,spawnSource:S,parentTaskId:t.parentTaskId}},{clock:o,callerId:d,logger:s});let C=!1,w,T=f.onTransition((e,t)=>{e.id===i&&t.status===`stopping`&&(C||w!==void 0||(w=Qe(u,x,{isDone:()=>C,unref:!0})))});return(async()=>{let e=``;try{for await(let t of u.events(x)){let n=lt(t);n!==void 0&&(e=n)}C=!0,w?.();let t=(await u.exit(x)).code??-1,n=Buffer.byteLength(e,`utf8`),r=n<=2048?e:void 0,a;if(p!==void 0)try{await p.writeOutput(i,e),a=p.outputRef(i)}catch(e){let t=e instanceof Error?e.message:String(e);s.warn(`background dispatch output persistence for task `+i+` failed: `+t)}await f.transition({kind:`resolve-exit`,taskId:i,exitCode:t,outputRef:a,outputBytes:n,outputPreview:r},{clock:o,callerId:d,logger:s})}catch(t){let n=t instanceof Error?t.message:String(t);s.warn(`background dispatch pump for task `+i+` failed after `+String(e.length)+` chars of buffered output: `+n)}finally{T(),w?.(),ct(v),a.release(i)}})(),{taskId:i,label:g,status:`running`}}catch(t){let n=t instanceof Error?t.message:String(t);return m!==void 0&&Qe(u,m,{unref:!0}),h!==void 0&&ct(h),a.release(i),s.warn(`background dispatch failed for agent `+e.agent+`: `+n),{text:``,status:`rejected`,started:m!==void 0,agentName:e.agent,durationMs:0,exitCode:-1,errorMessage:`background dispatch failed: `+n}}}async function dt(e,t,n={}){if(e.background===!0)return await ut(e,t,n);let r=t.depth+1;if(r>t.maxDispatchDepth)return Ye();let i=e.cwd??t.cwd,a=e.agentScope??`user`,o=Date.now(),s=at(e.agent,i,a);if(!s)return{text:``,status:`rejected`,started:!1,agentName:e.agent,durationMs:Date.now()-o,exitCode:1,errorMessage:`unknown agent: `+e.agent+` (agentScope=`+a+`, cwd=`+i+`)`};let c=Je(s.systemPrompt,r,t.maxDispatchDepth),l=await st(s.name,c),u=ot(e,s,l.filePath);return await new Promise(e=>{let n=``,a,c=-1,d=``,f=!1,p,m,h=!1,g=(r,i,u=!0)=>{if(f)return;f=!0,p?.(),t.signal&&t.signal.removeEventListener(`abort`,_),ct(l);let m={text:n,status:r,started:u,agentName:s.name,durationMs:Date.now()-o,exitCode:c};a&&(m.usage=a),d.length>0&&(m.stderr=d),i&&(m.errorMessage=i),e(m)},_=()=>{m&&!f&&(h=!0,p=Qe(V,m,{isDone:()=>f}))};try{m=V.spawn([`pi`,...u],{cwd:i,env:{...process.env,PI_PTC_DEPTH:String(r)},promptFile:l.filePath})}catch(e){g(`rejected`,`failed to spawn pi: `+(e instanceof Error?e.message:String(e)),!1);return}t.signal&&(t.signal.aborted?_():t.signal.addEventListener(`abort`,_,{once:!0})),(async()=>{if(!m)return;let e=m;try{for await(let t of V.events(e))if(t.type===`message_end`&&t.message&&t.message.role===`assistant`){let e=t.message;if(e.usage){let t=a??{input:0,output:0,cacheRead:0,cacheWrite:0,cost:0,turns:0};t.input+=e.usage.input??0,t.output+=e.usage.output??0,t.cacheRead+=e.usage.cacheRead??0,t.cacheWrite+=e.usage.cacheWrite??0,t.cost+=e.usage.cost?.total??0,t.turns+=1,a=t}if(Array.isArray(e.content))for(let t of e.content)t&&t.type===`text`&&typeof t.text==`string`&&(n=t.text)}let t=await V.exit(e);c=t.code??-1,d=await V.stderr(e);let r=d.match(/^\[spawn-error\] (.+)$/m);if(r&&t.code===null&&t.signal===null){g(`rejected`,`failed to spawn pi: `+r[1],!1);return}let{status:i,errorMessage:o}=tt({exitCode:c,finalText:n,aborted:h});g(i,o)}catch(e){let t=e instanceof Error?e.message:String(e);g(`rejected`,`dispatch internal error: `+t)}})()})}const H=[`read`,`bash`,`edit`,`write`,`grep`,`find`,`ls`],ft=`pi.dispatch`,pt=H,mt={read:m,bash:l,edit:u,write:h,grep:f,find:d,ls:p};function ht(e){let t=e.names??pt,n=e.includeDispatch??e.names===void 0,r=/* @__PURE__ */ new Map;for(let n of t){let t=mt[n];if(!t)throw TypeError(`unknown PTC binding "${n}"; known bindings: ${H.join(`, `)}`);let i=t(e.cwd);r.set(n,{name:n,execute:async(e,t)=>{let n=`ptc:${t.callId}`,r={type:`toolCall`,id:n,name:i.name,arguments:e},a=S(i,r),o=await i.execute(n,a,t.signal,void 0);return{content:o.content,details:o.details===void 0?null:o.details}}})}return n&&r.set(ft,{name:ft,execute:async(t,n)=>dt(t,{signal:n.signal,callId:n.callId,cwd:e.cwd,depth:n.depth,maxDispatchDepth:n.maxDispatchDepth,...n.callerId===void 0?{}:{callerId:n.callerId},...n.sessionDir===void 0?{}:{sessionDir:n.sessionDir},...n.parentTaskId===void 0?{}:{parentTaskId:n.parentTaskId}},n.dispatchDeps)}),r}function gt(){let e=/* @__PURE__ */ new Map;return{recordStart(t,n,r){if(e.has(t))throw Error(`duplicate sub-call callId ${t}`);e.set(t,{callId:t,name:n,args:r,status:`running`,startMs:Date.now()})},recordEnd(t,n,r){let i=e.get(t);if(i===void 0)throw Error(`recordEnd on unknown callId ${t}`);return i.status=n,i.endMs=Date.now(),i.durationMs=i.endMs-i.startMs,n===`rejected`&&(i.durationMs=0),r?.errorMessage!==void 0&&(i.errorMessage=r.errorMessage),r?.resultSummary!==void 0&&(i.resultSummary=r.resultSummary),i},snapshot(){return[...e.values()].map(e=>({...e}))}}}function _t(e){return{name:`ptc-${e.surface}`,env:e.env,workerData:e.runId===void 0?{env:e.env}:{runId:e.runId,env:e.env},resourceLimits:{maxOldGenerationSizeMb:e.limits.maxOldGenerationSizeMb,maxYoungGenerationSizeMb:e.limits.maxYoungGenerationSizeMb}}}const U={acquireLatency:P.channel(`ptc:pool:acquire-latency`),resetTime:P.channel(`ptc:worker:reset-time`),imageBytes:P.channel(`ptc:image:hoist-bytes`)};function W(e,t){e.publish(t)}var vt=class{#e;#t;#n;#r;#i;#a=[];#o=/* @__PURE__ */ new Set;#s=[];#c=/* @__PURE__ */ new WeakMap;#l=0;#u=0;#d=!1;constructor(e){if(typeof e.buildWorkerUrl!=`function`)throw TypeError(`WorkerPool: buildWorkerUrl must be a function`);if(e.size!==void 0&&(typeof e.size!=`number`||!Number.isFinite(e.size)||e.size<=0))throw TypeError(`WorkerPool: size must be a positive finite number, received ${String(e.size)}`);if(e.acquireTimeoutMs!==void 0&&(typeof e.acquireTimeoutMs!=`number`||!Number.isFinite(e.acquireTimeoutMs)||e.acquireTimeoutMs<=0))throw TypeError(`WorkerPool: acquireTimeoutMs must be a positive finite number, received ${String(e.acquireTimeoutMs)}`);if(e.drainGraceMs!==void 0&&(typeof e.drainGraceMs!=`number`||!Number.isFinite(e.drainGraceMs)||e.drainGraceMs<=0))throw TypeError(`WorkerPool: drainGraceMs must be a positive finite number, received ${String(e.drainGraceMs)}`);this.#e=e.buildWorkerUrl,this.#t=e.size??4,this.#n=e.acquireTimeoutMs??3e4,this.#r=e.drainGraceMs??5e3,this.#i=e.workerOptions}async acquire(){if(this.#d)throw Error(`pool has been drained and can no longer serve workers`);this.#l+=1;let e=Date.now(),t=this.#a.shift();for(;t!==void 0&&!this.#p(t);)t=this.#a.shift();if(t!==void 0)return this.#o.add(t),t.ref(),W(U.acquireLatency,{poolSize:this.#t,waiters:this.#s.length,durationMs:Date.now()-e}),t;if(this.#o.size<this.#t){let t=this.#f();return this.#o.add(t),W(U.acquireLatency,{poolSize:this.#t,waiters:this.#s.length,durationMs:Date.now()-e}),t}return this.#u+=1,await new Promise((t,n)=>{let r=setTimeout(()=>{let e=this.#s.findIndex(e=>e.timer===r);e>=0&&this.#s.splice(e,1),n(/* @__PURE__ */ Error(`pool acquire timed out after ${this.#n} ms`))},this.#n);this.#s.push({resolve:t,reject:n,timer:r,startedAt:e})})}release(e){if(!this.#o.delete(e)||!this.#p(e))return;this.#c.set(e,Date.now());let t=this.#s.shift();if(t){clearTimeout(t.timer),this.#o.add(e),W(U.acquireLatency,{poolSize:this.#t,waiters:this.#s.length,durationMs:Date.now()-t.startedAt}),t.resolve(e);return}this.#a.push(e),e.unref()}lastSettledAt(e){return this.#c.get(e)}async drain(){for(;this.#s.length>0;){let e=this.#s.shift();if(!e)break;clearTimeout(e.timer),e.reject(/* @__PURE__ */ Error(`pool drained before acquire could be satisfied`))}let e=Date.now()+this.#r;for(;this.#o.size>0&&Date.now()<e;)await new Promise(e=>setTimeout(e,5));let t=[...this.#a,...this.#o];this.#a.length=0,this.#o.clear(),await Promise.all(t.map(e=>e.terminate().catch(()=>void 0))),this.#d=!0}stats(){return{resident:this.#o.size+this.#a.length,inFlight:this.#o.size,waiters:this.#s.length,totalAcquires:this.#l,poolExhaustions:this.#u}}#f(){let e=new N(this.#e(),this.#i);return e.once(`exit`,()=>{let t=this.#a.indexOf(e);if(t>=0){this.#a.splice(t,1);return}this.#o.delete(e)}),e}#p(e){return e.threadId!==-1}};function yt(e){W(U.imageBytes,{byteLength:e})}function bt(e){W(U.resetTime,{durationMs:e})}function xt(){let e=O(ae(import.meta.url)),t=[A(e,`worker.js`),A(e,`worker-entry.ts`)];for(let e of t)if(w(e))return F(e);throw Error(`worker entry not found; tried: ${t.join(`, `)}`)}function St(e){if(typeof e!=`object`||!e)return;let t=e;if(t.status!==`rejected`)return;let n=typeof t.errorMessage==`string`?t.errorMessage:t.started===!1?`dispatch refused`:`dispatch failed`;return{kind:t.started===!1?`refused`:`failed`,message:n}}function Ct(e){return typeof e==`object`&&!!e&&e.background===!0}function G(e){return e instanceof Error?e.message:String(e)}function wt(e){if(typeof e!=`object`||!e)return;let t=e.content;if(Array.isArray(t)&&t.length>0){let e=t[0];if(typeof e==`object`&&e){let t=e.text;if(typeof t==`string`&&t.length>0){let e=t.indexOf(`
8
+ `);return e===-1?t:t.slice(0,e)}}}}function Tt(e){return e instanceof ArrayBuffer||ArrayBuffer.isView(e)}function Et(e,t){let n=/* @__PURE__ */ new WeakSet,r=e=>{if(e!=null){if(e instanceof ArrayBuffer){t(e);return}if(ArrayBuffer.isView(e)){t(e);return}if(typeof e==`object`&&!n.has(e)){if(n.add(e),Array.isArray(e)){for(let t of e)r(t);return}for(let t of Object.keys(e))r(e[t])}}};r(e)}function Dt(e){try{let t=Buffer.byteLength(JSON.stringify(e,(e,t)=>Tt(t)?null:t)??``,`utf8`),n=0;return Et(e,e=>{n+=e.byteLength}),t+n}catch{return 1/0}}async function Ot(e){let i=Oe(e.config),s=De(),c=e.runId??j(),l=ke(e.timeoutMs,i),u=[],d=[],f=[];if(e.signal?.aborted)return{logs:u,narrations:d,phases:f,error:{kind:n.abort,message:`run cancelled before start`}};let p;try{p=e.pool?await e.pool.acquire():new N(xt(),_t({surface:e.surface,env:s,limits:i,runId:c}))}catch(e){return{logs:u,narrations:d,phases:f,error:{kind:n.workerExit,message:`pool acquire failed: ${G(e)}`}}}return await new Promise(s=>{let m=new ie,h=m.port1,g=m.port2,_=new AbortController,v=!1,y=gt(),b=()=>{v||e.onSubCallChange?.(()=>y.snapshot())},x={recordStart(e,t,n){y.recordStart(e,t,n),b()},recordEnd(e,t,n){let r=y.recordEnd(e,t,n);return b(),r},snapshot:()=>y.snapshot()},S,C=!1,w=0,T=0,ee=new Ue(i.dispatchConcurrency),E=0,te=[],D=e.depth??0,O,k,A=[],ne=e=>{let t=[],n=e?.content;if(!Array.isArray(n))return t;for(let e of n){if(typeof e!=`object`||!e)continue;let n=e;if(n.type!==`image`)continue;let r;typeof n.data==`string`&&n.data.length>0?r=n.data:n.bytes instanceof ArrayBuffer&&n.bytes.byteLength>0&&(r=Buffer.from(n.bytes).toString(`base64`)),r!==void 0&&t.push({data:r,mimeType:typeof n.mimeType==`string`?n.mimeType:`application/octet-stream`})}return t},re=e=>e===`timeout`?`run timed out after ${l} ms`:`run cancelled`,j=e=>e===`timeout`?n.timeout:n.abort,M=t=>{if(v)return;v=!0,O&&clearTimeout(O),k&&clearTimeout(k),e.signal?.removeEventListener(`abort`,ue),_.abort(),h.close(),e.pool?e.pool.release(p):p.terminate();let n=x.snapshot();s(n.length>0?{...t,subCalls:n}:t)},N=(e,t,n)=>{M({logs:u,narrations:d,phases:f,error:n===void 0?{kind:e,message:t}:{kind:e,message:t,stack:n}})},P=e=>{if(!v){if(!r(e)){N(n.protocol,`internal error: malformed host frame`);return}h.postMessage(e)}},ae=()=>{let t={kind:o.init,runId:c,surface:e.surface,code:e.code,bindings:[...e.bindings.keys()],bindingCandidates:[...H],maxItemsPerCall:i.maxItemsPerCall,maxPendingCalls:i.maxPendingCalls,...e.args===void 0?{}:{args:e.args}};if(!r(t)){N(n.protocol,`internal error: malformed init frame`);return}let a=Dt(t);if(a>i.maxMessageBytes){N(n.protocol,`init frame of ${a} bytes exceeds maxMessageBytes (${i.maxMessageBytes})`);return}P(t)},F=(e,t)=>(T+=e,T<=i.maxOutputBytes||(N(n.outputLimit,`${t} exceeded the output budget: logs + result reached ${T} bytes (maxOutputBytes=${i.maxOutputBytes}); ${u.length} log line(s) retained`),!1)),I=async()=>{if(E<i.maxParallelSubCalls){E+=1;return}await new Promise(e=>{te.push(e)})},L=()=>{let e=te.shift();if(e){e();return}--E},oe=e=>{if(x.recordStart(e.callId,e.tool,e.args),w+=1,w>i.maxPendingCalls){x.recordEnd(e.callId,`error`,{errorMessage:`more than maxPendingCalls (${i.maxPendingCalls}) binding calls in flight`}),N(n.protocol,`more than maxPendingCalls (${i.maxPendingCalls}) binding calls in flight`);return}se(e).finally(()=>{--w})},se=async t=>{let r=e.bindings.get(t.tool);if(!r){x.recordEnd(t.callId,`error`,{errorMessage:`no binding named "${t.tool}" in this run; available bindings: `+([...e.bindings.keys()].join(`, `)||`(none)`)}),R({kind:o.callResult,callId:t.callId,tool:t.tool,ok:!1,message:`no binding named "${t.tool}" in this run; available bindings: `+([...e.bindings.keys()].join(`, `)||`(none)`)});return}let a=t.tool===ft,s=a&&Ct(t.args);if(a){if(!s&&!ee.tryAcquire()){x.recordEnd(t.callId,`rejected`,{errorMessage:Xe}),R({kind:o.callResult,callId:t.callId,tool:t.tool,ok:!0,value:Ze()});return}}else if(await I(),v){x.recordEnd(t.callId,`cancelled`,{errorMessage:`run settled before binding started`}),L();return}try{let s=await r.execute(t.args,{signal:_.signal,callId:t.callId,depth:D,maxDispatchDepth:i.maxDispatchDepth,callerId:c,...e.parentTaskId===void 0?{}:{parentTaskId:e.parentTaskId},...e.sessionDir===void 0?{}:{sessionDir:e.sessionDir},dispatchDeps:{...e.dispatchDeps,slots:e.dispatchDeps?.slots??ee}}),l=ne(s),u={kind:o.callResult,callId:t.callId,tool:t.tool,ok:!0,value:s},d=Dt(u);if(d>i.maxMessageBytes){x.recordEnd(t.callId,`error`,{errorMessage:`callResult frame of ${d} bytes exceeds maxMessageBytes (${i.maxMessageBytes})`}),N(n.protocol,`callResult frame of ${d} bytes exceeds maxMessageBytes (${i.maxMessageBytes})`);return}if(R(u))for(let e of l)A.push(e),yt(Buffer.byteLength(e.data,`base64`));if(_.signal.aborted)x.recordEnd(t.callId,`cancelled`,{errorMessage:`binding aborted by run cancellation`});else{let e=a?St(s):void 0;e===void 0?x.recordEnd(t.callId,`ok`,{resultSummary:wt(s)}):x.recordEnd(t.callId,e.kind===`refused`?`rejected`:`error`,{errorMessage:e.message})}}catch(e){_.signal.aborted?x.recordEnd(t.callId,`cancelled`,{errorMessage:G(e)}):x.recordEnd(t.callId,`error`,{errorMessage:G(e)}),R({kind:o.callResult,callId:t.callId,tool:t.tool,ok:!1,message:G(e)})}finally{a?s||ee.release():L()}},R=e=>{if(v)return!1;try{return h.postMessage(e),!0}catch(t){try{h.postMessage({kind:o.callResult,callId:e.callId,tool:e.tool,ok:!1,message:`binding result could not be transferred: ${G(t)}`})}catch{N(n.protocol,`binding result is not transferable`)}return!1}},ce=e=>S?{logs:u,narrations:d,phases:f,error:{kind:j(S),message:re(S)}}:e,le=r=>{if(v)return;if(!a(r)){N(n.protocol,`malformed worker frame`);return}let o=r.kind===t.ready?0:Dt(r);if(!Number.isFinite(o)){N(n.protocol,`worker frame could not be serialized for accounting`);return}if(o>i.maxMessageBytes){N(n.protocol,`control frame of ${o} bytes exceeds maxMessageBytes (${i.maxMessageBytes})`);return}switch(r.kind){case t.ready:if(e.pool){let t=e.pool.lastSettledAt(p);t!==void 0&&bt(Date.now()-t)}if(!C&&(C=!0,S)){z(S);return}ae();return;case t.call:oe(r);return;case t.log:F(o,`console output`)&&u.push(r.text);return;case t.narration:F(o,`workflow narration`)&&d.push(r.message);return;case t.phase:F(o,`workflow phase`)&&f.push(r.title);return;case t.result:{if(!F(o,`completion value`))return;let e={logs:u,narrations:d,phases:f,...A.length>0?{images:A}:{}};M(ce(r.value===void 0?e:{...e,value:r.value}));return}case t.error:M(ce({logs:u,narrations:d,phases:f,error:r.error}));return;default:N(n.protocol,`unknown worker frame`)}};function ue(){de(`abort`)}let z=e=>{k&&clearTimeout(k),k=setTimeout(()=>{N(j(e),re(e))},i.graceMs)};function de(e){if(!v){if(S){N(j(S),re(S));return}S=e,_.abort(),P({kind:o.cancel,reason:e}),z(e)}}p.on(`error`,e=>{N(n.workerExit,`worker failed: ${G(e)}`)}),p.on(`exit`,e=>{N(n.workerExit,`worker exited with code ${e} before the run completed`)}),h.on(`message`,le),h.on(`messageerror`,()=>{N(n.protocol,`worker sent a frame that could not be deserialized`)}),e.signal?.addEventListener(`abort`,ue,{once:!0}),O=setTimeout(()=>{de(`timeout`)},l);let fe={kind:o.connect,port:g};r(fe)?p.postMessage(fe,[g]):N(n.protocol,`internal error: malformed connect frame`)})}const kt=`pi-ptc-output-`;function At(e,t=kt){let n=k(te(),`${t}${j()}.txt`);return ee(n,e,`utf8`),n}function jt(e,t){let n=e.totalLines-e.outputLines+1,r=e.totalLines;return e.lastLinePartial?`[Showing last ${_(e.outputBytes)} of line ${r}. Full output: ${t}]`:e.truncatedBy===`lines`?`[Showing lines ${n}-${r} of ${e.totalLines}. Full output: ${t}]`:`[Showing lines ${n}-${r} of ${e.totalLines} (${_(s)} limit). Full output: ${t}]`}function Mt(e,t=kt,n={}){let r=b(e,{maxLines:c,maxBytes:s});if(!r.truncated)return{text:e,truncated:!1};let i=At(n.fullText??e,t);return{text:`${r.content}\n\n${jt(r,i)}`,truncated:!0,fullPath:i}}const Nt=200,Pt=new RegExp([`(?:\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\u005C|\\u009C))`,`[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]`].join(`|`),`g`);function Ft(e){return!e.includes(`\x1B`)&&!e.includes(`›`)?e:e.replace(Pt,``)}function K(e){let t=Ft(e).replace(/\r/g,``);if(t.length===0)return t;let n=``;for(let e of t){let t=e.codePointAt(0);if(t!==void 0){if(t===10||t===9){n+=e;continue}t<=31||t>=65529&&t<=65531||(n+=e)}}return n}const It=/^[A-Za-z_$][A-Za-z0-9_$]*$/;function Lt(e){return It.test(e)?e:JSON.stringify(e)}function Rt(e){if(e===null)return`null`;if(typeof e==`number`||typeof e==`boolean`)return String(e);if(typeof e==`string`){let t=K(e);return t.includes(`
9
+ `)?void 0:JSON.stringify(t)}if(Array.isArray(e)){let t=[];for(let n of e){let e=Rt(n);if(e===void 0)return;t.push(e)}return`[${t.join(`, `)}]`}let t=Object.keys(e);if(t.length===0)return`{}`;let n=[];for(let r of t){let t=Rt(e[r]??null);if(t===void 0)return;n.push(`${Lt(r)}: ${t}`)}return`{${n.join(`, `)}}`}function zt(e){let t=Rt(e);if(t!==void 0)return t.length<=100?t:void 0}function Bt(e){return e===null?`null`:typeof e==`string`?K(e):String(e)}function Vt(e,t){if(typeof e!=`object`||!e)return[`${t}${Bt(e)}`];if(Array.isArray(e)){if(e.length===0)return[`${t}[]`];let n=[`${t}[`];for(let r of e){let e=zt(r);if(e!==void 0){n.push(`${t} ${e}`);continue}n.push(...Ht(r,t+` `))}return n.push(`${t}]`),n}let n=Object.keys(e);if(n.length===0)return[`${t}{}`];let r=[`${t}{`],i=t+` `;for(let t of n){let n=e[t]??null,a=zt(n);if(a!==void 0){r.push(`${i}${Lt(t)}: ${a}`);continue}if(typeof n==`string`){r.push(`${i}${Lt(t)}:`);for(let e of K(n).split(`
10
+ `))r.push(e.length===0?``:`${i} ${e}`);continue}let o=Vt(n,i);r.push(`${i}${Lt(t)}: ${(o[0]??``).trimStart()}`.trimEnd(),...o.slice(1))}return r.push(`${t}}`),r}function Ht(e,t){return typeof e==`string`?K(e).split(`
11
+ `):typeof e!=`object`||!e?[String(e)]:Vt(e,t)}function Ut(e){return e.split(`
13
12
  `).map(e=>e.length>200?`${e.slice(0,199)}…`:e).join(`
14
- `)}function Ye(e){if(typeof e==`string`)return Je(H(e));if(typeof e!=`object`||!e)return String(e);let t=We(e);return Je((t===void 0?qe(e,``):[t]).join(`
15
- `))}function Xe(e=process.env){let t=e.PI_PTC_DEPTH;return t===void 0||!/^\d+$/.test(t)?0:Number.parseInt(t,10)}function W(e){if(e===void 0)return L;let t=new Set(e);return L.filter(e=>t.has(e))}const Ze=["Image-bearing tool results inside a program (a `tools.read` on a PNG, say) are attached to you after the run — never return image data as the completion value.",`Use ptc_run_code when a task needs several tool calls whose intermediate output you do not need to see — gather it inside the program and return only the final value.`,`Use ptc_workflow instead of ptc_run_code when the work has named phases and the user benefits from seeing progress narration.`],Qe={run_code:`ptc_run_code`,workflow:`ptc_workflow`};function $e(e){let t=e?.cwd;return typeof t==`string`&&t.length>0?t:process.cwd()}function et(e){let{surface:t,startedAt:n,onUpdate:r}=e;if(r===void 0)return{update:()=>{},cancel:()=>{}};let i=0,a,o,s=()=>{if(o===void 0)return;let e=o;o=void 0,i=Date.now(),r({content:[],details:{surface:t,logs:[],narrations:[],phases:[],warnings:[],durationMs:Date.now()-n,imageCount:0,subCalls:e()}})};return{update(e){o=e;let t=100-(Date.now()-i);if(t<=0){a!==void 0&&(clearTimeout(a),a=void 0),s();return}a??=setTimeout(()=>{a=void 0,s()},t)},cancel(){a!==void 0&&(clearTimeout(a),a=void 0),o=void 0}}}function tt(e){return[...e.phases.map(e=>`[phase] ${H(e)}`),...e.narrations.map(e=>`[log] ${H(e)}`),...e.logs.map(e=>H(e))]}function nt(e){let t=e.error;if(t===void 0)return/* @__PURE__ */ Error(`internal error: a successful PTC run was rendered as a failure`);let n=tt(e),r=n.length===0?``:`\nCaptured output:\n${n.join(`
16
- `)}`,i=Error(H(`code run failed (${t.kind}): ${t.message}${r}`));return i.name=`CodeRunFailedError`,i}function rt(e){let t=A(te(),`pi-ptc-output-${j()}.txt`);return E(t,e,`utf8`),t}function it(e,t){let n=e.totalLines-e.outputLines+1,r=e.totalLines;return e.lastLinePartial?`[Showing last ${_(e.outputBytes)} of line ${r}. Full output: ${t}]`:e.truncatedBy===`lines`?`[Showing lines ${n}-${r} of ${e.totalLines}. Full output: ${t}]`:`[Showing lines ${n}-${r} of ${e.totalLines} (${_(s)} limit). Full output: ${t}]`}function at(e){let{outcome:t,surface:n}=e,r=[...e.warnings??[]],i=t.images??[],a=[];t.phases.length>0&&a.push(`Phases: ${t.phases.map(e=>H(e)).join(` → `)}`),t.narrations.length>0&&a.push(t.narrations.map(e=>H(e)).join(`
17
- `)),t.logs.length>0&&a.push(t.logs.map(e=>H(e)).join(`
18
- `)),t.value!==void 0&&a.push(Ye(t.value));for(let e of r)a.push(`Warning: ${H(e)}`);let o=a.length>0?a.join(`
19
- `):`(${Qe[n]} completed with no output)`,l=b(o,{maxLines:c,maxBytes:s}),u,d=l.content;return l.truncated&&(u=rt(o),d+=`\n\n${it(l,u)}`),{content:[{type:`text`,text:d},...i.map(e=>({type:`image`,data:e.data,mimeType:e.mimeType}))],details:{surface:n,logs:[...t.logs],narrations:[...t.narrations],phases:[...t.phases],warnings:r,...t.value===void 0?{}:{result:t.value},durationMs:e.durationMs,imageCount:i.length,...u===void 0?{}:{fullOutputPath:u},...t.subCalls===void 0?{}:{subCalls:t.subCalls}}}}const ot=/^(.*?)(└─ |├─ | )(?:PTC(?: workflow)?)([ ].*)$/;function st(e){return e.replace(/\x1b\[[0-9;]*m|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[\?[0-9;]*[a-zA-Z]/g,``)}function ct(e,t){if(e.charCodeAt(t)!==27)return t;let n=e.charCodeAt(t+1);if(n===93){let n=t+2;for(;n<e.length;){let t=e.charCodeAt(n);if(t===7)return n+1;if(t===27&&e.charCodeAt(n+1)===92)return n+2;n+=1}return n}if(n===91){let n=t+2;for(;n<e.length;){let t=e.charCodeAt(n);if(t>=64&&t<=126)return n+1;n+=1}return n}return t+2}function lt(e,t){let n=0,r=0;for(;r<t&&n<e.length;)e.charCodeAt(n)===27?n=ct(e,n):(n+=1,r+=1);return n}function ut(e,t){let n=t.intervalMs??150,{state:r}=t;t.isPartial?(r.startedAt??=Date.now(),r.interval??=setInterval(()=>t.requestInvalidate(),n)):(r.interval!==void 0&&(clearInterval(r.interval),r.interval=void 0),r.startedAt=void 0);let i=e.render.bind(e),a=e;return a.render=e=>{let a=i(e);return r.startedAt===void 0?a:dt(a,Math.floor((Date.now()-r.startedAt)/n),t.theme)},a.dispose=()=>{r.interval!==void 0&&(clearInterval(r.interval),r.interval=void 0)},e}function dt(e,t,n){return e.map(e=>{let r=st(e),i=ot.exec(r);if(i===null)return e;let a=(i[3]??``).slice(1);if(a.length===0)return e;let o=t%(a.length+1),s=i[0].length-a.length,c=lt(e,s),l=lt(e,s+a.length),u=o>=a.length?n.fg(`dim`,a):`${o>0?n.fg(`dim`,a.slice(0,o)):``}${n.fg(`accent`,a.slice(o,o+1))}${n.fg(`dim`,a.slice(o+1))}`;return`${e.slice(0,c)}${u}${e.slice(l)}`})}const ft={run_code:`PTC`,workflow:`PTC workflow`};var G=class{items;constructor(e){this.items=e}invalidate(){}render(e){let t=Math.max(1,Math.floor(e));return this.items.map(e=>pt(e,t))}};function pt(e,t){let{left:n,right:r}=e;if(r===void 0)return ae(n,t,`…`);let i=F(r);if(i>=t)return ae(r,t,`…`);let a=ae(n,Math.max(0,t-i-1),`…`),o=Math.max(1,t-i-F(a));return a+` `.repeat(o)+r}function K(e,t){return e.length>t?`${e.slice(0,t-1)}…`:e}function mt(e){return K((H(e).split(`
20
- `).find(e=>e.trim().length>0)??``).trim(),120)}function ht(e){return e<1e3?`${e}ms`:`${(e/1e3).toFixed(2).replace(/\.?0+$/,``)}s`}function q(e,t){return`${e} ${t}${e===1?``:`s`}`}function gt(e){for(let t of e.split(`
21
- `)){let e=t.trim();if(e.length!==0&&!e.startsWith(`//`)&&!e.startsWith(`/*`)&&!e.startsWith(`*`))return K(e.replace(/\s*\/\/.*$/,``),120)}}function _t(e){if(e===void 0)return;if(typeof e==`string`){let t=H(e).split(`
22
- `),n=t.find(e=>e.trim().length>0)?.trim()??``,r=t.length>1?` (+${t.length-1} lines)`:``;return n.length===0&&r.length===0?`""`:K(n,60)+r}if(typeof e!=`object`||!e)return String(e);let t=Ye(e);if(!t.includes(`
23
- `)&&t.length<=60)return t;if(Array.isArray(e))return`Array(${e.length})`;let n=Object.keys(e);return n.length===0?`{}`:`{${n.slice(0,4).join(`, `)}${n.length>4?`, +${n.length-4}`:``}}`}function vt(e,t){if(e===void 0)return;let n=[],r=e.logs.length+e.narrations.length;if(r>0&&n.push(t.fg(`dim`,q(r,`output line`))),e.imageCount>0&&n.push(t.fg(`toolOutput`,q(e.imageCount,`image`))),e.surface===`workflow`&&e.phases.length>0&&n.push(t.fg(`dim`,q(e.phases.length,`phase`))),e.warnings.length>0&&n.push(t.fg(`warning`,q(e.warnings.length,`warning`))),e.fullOutputPath!==void 0&&n.push(t.fg(`warning`,`truncated`)),e.durationMs>0&&n.push(t.fg(`dim`,ht(e.durationMs))),n.length!==0)return`${t.fg(`dim`,`•`)} ${n.join(t.fg(`dim`,` · `))}`}function yt(e){if(e!==void 0)return{surface:e.surface??`run_code`,logs:e.logs??[],narrations:e.narrations??[],phases:e.phases??[],warnings:e.warnings??[],durationMs:e.durationMs??0,imageCount:e.imageCount??0,...e.result===void 0?{}:{result:e.result},...e.fullOutputPath===void 0?{}:{fullOutputPath:e.fullOutputPath},...e.subCalls===void 0?{}:{subCalls:e.subCalls}}}function bt(e,t,n){let r=` `+(t?`└─ `:`├─ `),i=` `+(t?` `:`│ `),a=` `.repeat(8),o=[],s=e.lines[0]??``;o.push({left:`${r}${e.label.padEnd(8)}${n.fg(`dim`,s)}`});for(let t=1;t<e.lines.length;t++)o.push({left:`${i}${a}${n.fg(`dim`,e.lines[t]??``)}`});return e.more!==void 0&&e.more>0&&o.push({left:`${i}${a}…+${e.more} more lines`}),o}function xt(e){return typeof e!=`object`||!e?!1:Array.isArray(e)?e.length>0:Object.keys(e).length>0}const St=/^[A-Za-z_$][A-Za-z0-9_$]*$/;function Ct(e){return St.test(e)?e:JSON.stringify(e)}function J(e,t){if(e===null)return`null`;if(typeof e==`boolean`||typeof e==`number`)return String(e);if(typeof e==`string`){let n=H(e);if(n.length===0)return`""`;if(n.includes(`
24
- `)){let e=K(n.replace(/\n/g,` `),Math.max(2,t-5));return JSON.stringify(e)+`…`}return n.length+2<=t?JSON.stringify(n):JSON.stringify(K(n,Math.max(2,t-5)))+`…`}return`?`}function wt(e){return typeof e!=`object`||!e}function Tt(e,t){if(t<8)return;if(Array.isArray(e)){if(e.length===0)return`[]`;if(e.length>5||!e.every(wt))return;let n=`[`+e.map(e=>J(e,Math.max(2,Math.floor(t/4)))).join(`, `)+`]`;return n.length<=t?n:void 0}if(typeof e!=`object`||!e)return;let n=Object.keys(e);if(n.length===0)return`{}`;if(n.length>5||!Object.values(e).every(wt))return;let r=`{`+n.map(n=>{let r=e[n]??null;return Ct(n)+`: `+J(r,Math.max(2,Math.floor(t/4)))}).join(`, `)+`}`;return r.length<=t?r:void 0}function Y(e,t,n,r,i,a,o,s,c){let l=n+(r?`└─ `:`├─ `),u=n+(r?` `:`│ `);if(s>=i){c.push(X(l+t+`…`,o));return}if(Array.isArray(e)&&e.length===0){c.push(X(l+t+`[]`,o));return}if(!Array.isArray(e)&&typeof e==`object`&&e&&Object.keys(e).length===0){c.push(X(l+t+`{}`,o));return}if(typeof e!=`object`||!e){let n=Math.max(0,o-F(l)-t.length);c.push(X(l+t+J(e,n),o));return}let d=Tt(e,Math.max(0,o-F(l)-t.length));if(d!==void 0){c.push(X(l+t+d,o));return}let f=Array.isArray(e),p=f?e.length:Object.keys(e).length,m;m=f?`Array(`+p+`)`:p>a?`{`+a+` of `+p+`}`:`{`+p+` keys}`,c.push(X(l+t+m,o));let h=Math.min(a,p);if(f)for(let t=0;t<h;t+=1){let n=t===h-1&&p<=h;Y(e[t]??null,`[`+t+`] `,u,n,i,a,o,s+1,c)}else{let t=Object.keys(e);for(let n=0;n<h;n+=1){let r=n===h-1&&p<=h,l=t[n];Y(e[l]??null,Ct(l)+`: `,u,r,i,a,o,s+1,c)}}if(p>h){let e=f?`items`:`keys`;c.push(X(u+`└─ …+`+(p-h)+` more `+e,o))}}function X(e,t){if(F(e)<=t)return e;let n=e.match(/^([\s│├└─]*)/),r=n&&n[1]?n[1]:``,i=F(r),a=Math.max(0,t-i-1),o=``,s=0;for(let t of e.slice(r.length)){let e=F(t);if(s+e>a)break;o+=t,s+=e}return r+o+`…`}function Et(e,t={}){let n=t.maxDepth??4,r=t.maxChildren??6,i=t.maxLineChars??120,a=t.moreAfter===!0;if(!xt(e))return typeof e==`object`&&e?[Array.isArray(e)?`[]`:`{}`]:[J(e,i)];let o=[];if(Array.isArray(e)){let t=e.length,s=Math.min(r,t);for(let c=0;c<s;c+=1){let l=!a&&c===s-1&&t<=s;Y(e[c]??null,`[`+c+`] `,``,l,n,r,i,0,o)}return t>s&&o.push((a?`├─ `:`└─ `)+`…+`+(t-s)+` more items`),o}let s=Object.keys(e),c=s.length,l=Math.min(r,c);for(let t=0;t<l;t+=1){let u=!a&&t===l-1&&c<=l,d=s[t];Y(e[d]??null,Ct(d)+`: `,``,u,n,r,i,0,o)}return c>l&&o.push((a?`├─ `:`└─ `)+`…+`+(c-l)+` more keys`),o}function Dt(e,t,n=`run_code`,r={}){let i=(typeof e.description==`string`&&e.description.trim().length>0?e.description.trim():e.meta?.name)??``,a=gt(e.code??e.script??``),o=i.length>0?i:a??`(no description)`;return ut(new G([{left:`└─ ${`${t.fg(`toolTitle`,t.bold(ft[n]))} ${t.fg(`accent`,o)}`}`}]),{intervalMs:150,isPartial:r.isPartial??!1,theme:t,requestInvalidate:r.requestInvalidate??Ot,state:r.state??{}})}function Ot(){}function kt(e){let t=e.content?.find(e=>e.type===`text`&&(e.text??``).length>0);return mt(t?.text??``)||`failed`}function At(e,t){if(e===`pi.dispatch`&&typeof t==`object`&&t&&`agent`in t)return`→ ${String(t.agent)}`;if(typeof t==`object`&&t){let n=t,r=e=>typeof n[e]==`string`?n[e]:void 0,i=e===`read`?r(`path`):e===`bash`?r(`command`):e===`grep`||e===`find`?r(`pattern`):e===`ls`||e===`edit`||e===`write`?r(`path`):void 0;if(typeof i==`string`)return i}try{let e=JSON.stringify(t).replace(/\n/g,` `);return e.length>40?`${e.slice(0,39)}…`:e}catch{return String(t).slice(0,40)}}const jt={running:{text:()=>`running`,themeSlot:`muted`},ok:{text:e=>e.durationMs===void 0?`ok`:`ok ${ht(e.durationMs)}`,themeSlot:`accent`},error:{text:e=>`failed: ${e.errorMessage??`error`}`,themeSlot:`error`},cancelled:{text:()=>`cancelled`,themeSlot:`muted`},rejected:{text:e=>`rejected: ${e.errorMessage??`concurrency`}`,themeSlot:`warning`}};function Mt(e,t){if(e.length===0)return[];let n=e.slice(0,32),r=e.length>32?e.length-32:0,i=[];return n.forEach((e,a)=>{let o=a===n.length-1&&r===0?`└─ `:`├─ `,s=e.name.length>=8?`${e.name} `:e.name.padEnd(8),c=At(e.name,e.args),l=jt[e.status];i.push({left:` ${o}${t.fg(`toolTitle`,s)}${t.fg(l.themeSlot,l.text(e))}${t.fg(`dim`,` `+c)}`})}),r>0&&i.push({left:` └─ …+${r} more calls`}),i}function Nt(e,t,n,r=!1,i=!1){let a=vt(e.details,n),o=e=>a===void 0?e:{...e,right:a};if(t)return[o({left:` ${n.fg(`error`,`failed: ${kt(e)}`)}`})];if(i&&e.details?.result===void 0)return[];let s=e.details?.result;if(s===void 0)return[o({left:` ${n.fg(`muted`,`done`)}`})];if(xt(s))return Et(s,{maxDepth:4,maxChildren:6,maxLineChars:120,moreAfter:r}).map((e,t)=>{let n={left:` ${e}`};return t===0&&a!==void 0?{...n,right:a}:n});let c=_t(s);return c===void 0?[o({left:` ${n.fg(`muted`,`done`)}`})]:[o({left:` ${n.fg(`dim`,`→ ${c}`)}`})]}function Pt(e,t,n,r={}){let i=yt(e.details),a=[...Nt({...e,details:i},t,n,!1,r.isPartial===!0)];return i?.subCalls!==void 0&&a.push(...Mt(i.subCalls,n)),new G(a)}function Ft(e,t,n,r,i={}){let a=yt(e.details);if(n){let t=[...Nt({...e,details:a},!0,r)];a?.subCalls!==void 0&&t.push(...Mt(a.subCalls,r));let n=H(e.content?.find(e=>e.type===`text`)?.text??kt(e));for(let e of n.split(`
25
- `).slice(0,12))t.push({left:` ${r.fg(`error`,e)}`});return new G(t)}let o=[],s=t.code??t.script??``;if(s.length>0){let e=s.split(`
26
- `).map(e=>K(e,120)),t=e.slice(0,3);o.push({label:`code`,lines:t,...e.length>t.length?{more:e.length-t.length}:{}})}if(a!==void 0&&a.surface===`workflow`&&a.phases.length>0){let e=a.phases.slice(0,8),t=a.phases.length>e.length?` …`:``;o.push({label:`phases`,lines:[`${e.join(` → `)}${t}`]})}if(a!==void 0&&a.narrations.length>0){let e=a.narrations.slice(0,12);o.push({label:`log`,lines:e.map(e=>H(e)),...a.narrations.length>e.length?{more:a.narrations.length-e.length}:{}})}if(a!==void 0&&a.logs.length>0){let e=a.logs.slice(0,12);o.push({label:`out`,lines:e.map(e=>H(e)),...a.logs.length>e.length?{more:a.logs.length-e.length}:{}})}if(a!==void 0&&a.warnings.length>0){let e=a.warnings.slice(0,4);o.push({label:`warn`,lines:e.map(e=>H(e)),...a.warnings.length>e.length?{more:a.warnings.length-e.length}:{}})}if(a!==void 0&&a.imageCount>0){let e=a.imageCount===1?`1 image attached`:`${a.imageCount} images attached`;o.push({label:`image`,lines:[e]})}let c=a?.subCalls,l=c!==void 0&&c.length>0||o.length>0,u=Nt({...e,details:a},!1,r,l,i.isPartial===!0);c!==void 0&&u.push(...Mt(c,r));for(let e=0;e<o.length;e+=1){let t=e===o.length-1;u.push(...bt(o[e]??{label:``,lines:[]},t,r))}return new G(u)}function It(e){return{renderCall(t,n,r){return Dt(t,n,e,{...r?.invalidate===void 0?{}:{requestInvalidate:r.invalidate},...r?.isPartial===void 0?{}:{isPartial:r.isPartial},...r?.state===void 0?{}:{state:r.state}})},renderResult(e,t,n,r){let i=t.isPartial===!0;return t.expanded===!0?Ft(e,r?.args??{},r?.isError===!0,n,{isPartial:i}):Pt(e,r?.isError===!0,n,{isPartial:i})}}}const Lt=["Run a TypeScript program that composes pi's tools in one shot. Required arguments: `code` —","the body of an async function (top-level `return` and `await` work; type annotations are","advisory, the code runs type-stripped) — and `description`, a 5-10 word summary of what the",`program does.`,``,"Inside the program, call this session's enabled built-in tools as `tools.<name>(args)` — e.g.",'`await tools.read({ path: "src/index.ts" })` or `await tools.bash({ command: "npm test" })`.',"The bound names mirror the session's active tools (a default session has `read`, `bash`, `edit`,","`write`); calling a name that is not bound rejects with an error the program can catch, and","independent calls may overlap under `Promise.all`.",``,"Only the program's return value and its `console.log` output come back. This surface has no","helpers: `log` / `phase` / `parallel` / `pipeline` exist only in `ptc_workflow`.",``,"Image-bearing tool results inside the program (a `tools.read` on a PNG, say) are attached to you",`after the run, so never return image data as the completion value — that only spends your context`,`on base64.`].join(`
27
- `),Rt=x.Object({code:x.String({description:`The program: the body of an async TypeScript function.`}),description:x.String({description:`Clear, concise description of what this program does in active voice, 5-10 words (shown in the UI).`}),timeoutMs:x.Optional(x.Number({description:`Elapsed deadline in milliseconds for the whole run, including tool calls. Omit it (or pass 0) for the default of ${R.timeoutMs/1e3} s; anything above ${R.maxTimeoutMs/1e3} s is capped.`}))});function zt(e={}){return g({name:`ptc_run_code`,label:`PTC Run Code`,description:Lt,promptSnippet:`Run a TypeScript program that composes pi's tools in one shot (tools.<name>(args)); only its return value and console.log come back`,promptGuidelines:[...Ze],parameters:Rt,async execute(t,n,r,i,a){let o=$e(a),s=W(e.getBindingSourceNames?.()),c=Date.now(),l=e.getPool?.(),u=et({surface:`run_code`,startedAt:c,onUpdate:i}),d;try{d=await Re({code:n.code,surface:`run_code`,cwd:o,bindings:Se({cwd:o,names:s,includeDispatch:!0}),...e.depth===void 0?{}:{depth:e.depth},...n.timeoutMs===void 0?{}:{timeoutMs:n.timeoutMs},...r===void 0?{}:{signal:r},...e.config===void 0?{}:{config:e.config},...l===void 0?{}:{pool:l},onSubCallChange:e=>u.update(e)})}finally{u.cancel()}if(d.error!==void 0)throw nt(d);return at({outcome:d,surface:`run_code`,durationMs:Date.now()-c})},...It(`run_code`)})}const Bt=[`Run a structured TypeScript workflow: a named plan that reports phases and narration as it`,"goes. `script` is the body of an async function, `meta` names it (`{ name, description, phases? }`,","where `phases` is the ordered plan), and optional `args` is plain-JSON input bound to the","program's `args` global.",``,"Helpers inside the script: `log(message)` narrates progress; `phase(title)` frames the workflow","(titles outside `meta.phases` are reported as warnings); `parallel(thunks)` runs thunks","concurrently, a failed item becoming `null`; `pipeline(items, ...stages)` chains stages per item","with the same per-item `null` on failure. There is no `agent()` helper.",``,"Tools are reachable as `tools.<name>(args)` exactly as in `ptc_run_code`, and the bound names",`mirror the session's enabled tools. What comes back is the script's return value, its`,"`log`/`phase` narration and its `console.log` output.",``,`Image-bearing tool results inside the script are attached to you after the run — never return`,`image data as the script's value.`].join(`
28
- `),Vt=x.Object({meta:x.Object({name:x.String({description:`Short workflow name, shown in the UI.`}),description:x.String({description:`What this workflow does, in active voice.`}),phases:x.Optional(x.Array(x.Object({name:x.String({description:`Phase title, matched against phase(title) calls.`})}),{description:`Ordered plan of the workflow's phases. phase() titles outside this list are reported as warnings.`}))},{description:`Workflow metadata: what this run is and which phases it declares.`}),script:x.String({description:`The program: the body of an async TypeScript function.`}),args:x.Optional(x.Record(x.String(),x.Unknown(),{description:"Plain-JSON input bound to the program's `args` global. No functions, symbols, undefined values or cycles."}))});function Ht(t){if(t!==void 0){if(typeof t!=`object`||!t||Array.isArray(t))throw TypeError(`ptc_workflow args must be a plain JSON object, received ${e(t)}`);for(let e of Object.keys(t)){let n=Ut(t[e],`args.${e}`,/* @__PURE__ */ new Set);if(n!==void 0)throw TypeError(`ptc_workflow args must be plain JSON: ${n}`)}}}function Ut(t,n,r){if(t===null||typeof t==`string`||typeof t==`boolean`)return;if(t===void 0)return`${n} is undefined`;if(typeof t==`number`)return Number.isFinite(t)?void 0:`${n} is ${String(t)}, which is not representable as JSON`;if(typeof t!=`object`)return`${n} is ${e(t)}`;let i=t;if(r.has(i))return`${n} is a circular reference`;r.add(i);try{if(Array.isArray(i)){for(let e=0;e<i.length;e+=1){let t=Ut(i[e],`${n}[${e}]`,r);if(t!==void 0)return t}return}let e=Object.getPrototypeOf(i);if(e!==Object.prototype&&e!==null){let e=i.constructor?.name;return`${n} is a ${typeof e==`string`&&e.length>0?e:`non-plain object`}, not plain JSON`}for(let e of Object.keys(i)){let t=Ut(i[e],`${n}.${e}`,r);if(t!==void 0)return t}return}finally{r.delete(i)}}function Wt(e,t){if(t===void 0||t.length===0)return[];let n=new Set(t),r=/* @__PURE__ */ new Set,i=[];for(let t of e.phases)n.has(t)||r.has(t)||(r.add(t),i.push(`phase "${t}" is not listed in meta.phases (declared: ${[...n].join(`, `)})`));return i}function Gt(e={}){return g({name:`ptc_workflow`,label:`PTC Workflow`,description:Bt,promptSnippet:`Run a structured TypeScript workflow with named phases, narration (log/phase) and structured concurrency (parallel/pipeline)`,promptGuidelines:[...Ze],parameters:Vt,async execute(t,n,r,i,a){Ht(n.args);let o=$e(a),s=W(e.getBindingSourceNames?.()),c=Date.now(),l=e.getPool?.(),u=et({surface:`workflow`,startedAt:c,onUpdate:i}),d;try{d=await Re({code:n.script,surface:`workflow`,cwd:o,bindings:Se({cwd:o,names:s,includeDispatch:!0}),...n.args===void 0?{}:{args:n.args},...e.depth===void 0?{}:{depth:e.depth},...r===void 0?{}:{signal:r},...e.config===void 0?{}:{config:e.config},...l===void 0?{}:{pool:l},onSubCallChange:e=>u.update(e)})}finally{u.cancel()}if(d.error!==void 0)throw nt(d);let f=n.meta.phases?.map(e=>e.name);return at({outcome:d,surface:`workflow`,warnings:Wt(d,f),durationMs:Date.now()-c})},...It(`workflow`)})}var Kt=class{#e;#t=/* @__PURE__ */ new Map;constructor(e={}){this.#e=Te(e.config)}get(e){let t=this.#t.get(e);if(t!==void 0)return t;let n=we(),r=new ke({buildWorkerUrl:Me,size:this.#e.poolSize,acquireTimeoutMs:this.#e.poolAcquireTimeoutMs,drainGraceMs:this.#e.drainGraceMs,workerOptions:Oe({surface:e,env:n,limits:this.#e})});return this.#t.set(e,r),r}async drain(){let e=[...this.#t.values()];this.#t.clear(),await Promise.all(e.map(e=>e.drain()))}};const Z=[`ptc_run_code`,`ptc_workflow`],qt=[`read`,`bash`,`edit`,`write`],Q=`ptc-mode`,Jt=`ptc-mode`,Yt=`ptc.json`,Xt=`builtins-only`;function Zt(){return{enabled:!1,base:void 0,ourLoadout:void 0}}function Qt(e){let t=A(e,Yt),n;try{n=T(t,`utf8`)}catch{return{defaultMode:!0,source:`default`}}let r;try{r=JSON.parse(n)}catch(e){return{defaultMode:!0,source:`invalid`,error:`${t} is not valid JSON (${e instanceof Error?e.message:String(e)})`}}if(typeof r!=`object`||!r||Array.isArray(r))return{defaultMode:!0,source:`invalid`,error:`${t} must contain a JSON object`};let i=r.defaultMode;return i===void 0?{defaultMode:!0,source:`default`}:typeof i==`boolean`?{defaultMode:i,source:`file`}:{defaultMode:!0,source:`invalid`,error:`${t}: "defaultMode" must be a boolean, received ${typeof i}`}}function $t(e){return[...new Set(e)]}function en(e,t){if(e.length!==t.length)return!1;let n=[...e].sort(),r=[...t].sort();return n.every((e,t)=>e===r[t])}function tn(e,t){let n=e=>Z.includes(e);if(t===`all-but-ptc`)return $t(e.filter(n));let r=new Set(L);return $t(e.filter(e=>n(e)||!r.has(e)))}function nn(e){let{mode:t,defaultMode:n,active:r,manual:i}=e;return t===`tui`?!n&&!i?{enter:!1,reason:`config-off`}:Z.some(e=>r.includes(e))?!i&&qt.filter(e=>!r.includes(e)).length>0?{enter:!1,reason:`restricted-session`}:{enter:!0,base:$t(r),loadout:tn(r,e.hide??`builtins-only`)}:{enter:!1,reason:`tools-unavailable`}:{enter:!1,reason:`not-tui`}}function rn(e,t){return e.enabled&&e.base!==void 0?e.base:t}function an(e,t){return!e.enabled||e.ourLoadout===void 0?!1:!en(e.ourLoadout,t)}function on(e,t){let n=t.length>0&&t.every(e=>Z.includes(e));return e?.enabled===!0&&n&&e.base!==void 0?{base:e.base,restoreFirst:!0}:{base:t,restoreFirst:!1}}function sn(e,t){let n=t===`all-but-ptc`?`Every built-in tool is hidden from you, and so are other extensions' tools.`:`The built-in tools (read, bash, edit, write, grep, find, ls) are hidden from you.`,r=e.map(e=>`tools.${e}(args)`),i=e.slice(0,2).map(e=>e===`read`?`await tools.read({ path: "src/index.ts" })`:e===`bash`?`await tools.bash({ command: "pnpm test" })`:`await tools.${e}({ /* … */ })`).join(`
13
+ `)}function Wt(e){if(typeof e==`string`)return Ut(K(e));if(typeof e!=`object`||!e)return String(e);let t=zt(e);return Ut((t===void 0?Ht(e,``):[t]).join(`
14
+ `))}function Gt(e=process.env){let t=e.PI_PTC_DEPTH;return t===void 0||!/^\d+$/.test(t)?0:Number.parseInt(t,10)}function Kt(e=process.env){let t=e.PI_PTC_TASK_ID;if(t!==void 0&&/^[0-9A-HJKMNP-TV-Z]{26}$/.test(t))return t}function q(e){if(e===void 0)return H;let t=new Set(e);return H.filter(e=>t.has(e))}const qt=["Image-bearing tool results inside a program (a `tools.read` on a PNG, say) are attached to you after the run — never return image data as the completion value.",`Use ptc_run_code when a task needs several tool calls whose intermediate output you do not need to see — gather it inside the program and return only the final value.`,`Use ptc_workflow instead of ptc_run_code when the work has named phases and the user benefits from seeing progress narration.`],Jt={run_code:`ptc_run_code`,workflow:`ptc_workflow`};function Yt(e){let t=e?.cwd;return typeof t==`string`&&t.length>0?t:process.cwd()}function Xt(e){let{surface:t,startedAt:n,onUpdate:r}=e;if(r===void 0)return{update:()=>{},cancel:()=>{}};let i=0,a,o,s=()=>{if(o===void 0)return;let e=o;o=void 0,i=Date.now(),r({content:[],details:{surface:t,logs:[],narrations:[],phases:[],warnings:[],durationMs:Date.now()-n,imageCount:0,subCalls:e()}})};return{update(e){o=e;let t=100-(Date.now()-i);if(t<=0){a!==void 0&&(clearTimeout(a),a=void 0),s();return}a??=setTimeout(()=>{a=void 0,s()},t)},cancel(){a!==void 0&&(clearTimeout(a),a=void 0),o=void 0}}}function Zt(e){return[...e.phases.map(e=>`[phase] ${K(e)}`),...e.narrations.map(e=>`[log] ${K(e)}`),...e.logs.map(e=>K(e))]}function Qt(e){let t=e.error;if(t===void 0)return/* @__PURE__ */ Error(`internal error: a successful PTC run was rendered as a failure`);let n=Zt(e),r=n.length===0?``:`\nCaptured output:\n${n.join(`
15
+ `)}`,i=Error(K(`code run failed (${t.kind}): ${t.message}${r}`));return i.name=`CodeRunFailedError`,i}function $t(e){let{outcome:t,surface:n}=e,r=[...e.warnings??[]],i=t.images??[],a=[];t.phases.length>0&&a.push(`Phases: ${t.phases.map(e=>K(e)).join(` → `)}`),t.narrations.length>0&&a.push(t.narrations.map(e=>K(e)).join(`
16
+ `)),t.logs.length>0&&a.push(t.logs.map(e=>K(e)).join(`
17
+ `)),t.value!==void 0&&a.push(Wt(t.value));for(let e of r)a.push(`Warning: ${K(e)}`);let o=Mt(a.length>0?a.join(`
18
+ `):`(${Jt[n]} completed with no output)`),s=o.text,c=o.truncated?o.fullPath:void 0;return{content:[{type:`text`,text:s},...i.map(e=>({type:`image`,data:e.data,mimeType:e.mimeType}))],details:{surface:n,logs:[...t.logs],narrations:[...t.narrations],phases:[...t.phases],warnings:r,...t.value===void 0?{}:{result:t.value},durationMs:e.durationMs,imageCount:i.length,...c===void 0?{}:{fullOutputPath:c},...t.subCalls===void 0?{}:{subCalls:t.subCalls}}}}const en=/^(.*?)(└─ |├─ | )(?:PTC(?: workflow)?)([ ].*)$/;function tn(e){return e.replace(/\x1b\[[0-9;]*m|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[\?[0-9;]*[a-zA-Z]/g,``)}function nn(e,t){if(e.charCodeAt(t)!==27)return t;let n=e.charCodeAt(t+1);if(n===93){let n=t+2;for(;n<e.length;){let t=e.charCodeAt(n);if(t===7)return n+1;if(t===27&&e.charCodeAt(n+1)===92)return n+2;n+=1}return n}if(n===91){let n=t+2;for(;n<e.length;){let t=e.charCodeAt(n);if(t>=64&&t<=126)return n+1;n+=1}return n}return t+2}function rn(e,t){let n=0,r=0;for(;r<t&&n<e.length;)e.charCodeAt(n)===27?n=nn(e,n):(n+=1,r+=1);return n}function an(e,t){let n=t.intervalMs??150,{state:r}=t;t.isPartial?(r.startedAt??=Date.now(),r.interval??=setInterval(()=>t.requestInvalidate(),n)):(r.interval!==void 0&&(clearInterval(r.interval),r.interval=void 0),r.startedAt=void 0);let i=e.render.bind(e),a=e;return a.render=e=>{let a=i(e);return r.startedAt===void 0?a:on(a,Math.floor((Date.now()-r.startedAt)/n),t.theme)},a.dispose=()=>{r.interval!==void 0&&(clearInterval(r.interval),r.interval=void 0)},e}function on(e,t,n){return e.map(e=>{let r=tn(e),i=en.exec(r);if(i===null)return e;let a=(i[3]??``).slice(1);if(a.length===0)return e;let o=t%(a.length+1),s=i[0].length-a.length,c=rn(e,s),l=rn(e,s+a.length),u=o>=a.length?n.fg(`dim`,a):`${o>0?n.fg(`dim`,a.slice(0,o)):``}${n.fg(`accent`,a.slice(o,o+1))}${n.fg(`dim`,a.slice(o+1))}`;return`${e.slice(0,c)}${u}${e.slice(l)}`})}const sn={run_code:`PTC`,workflow:`PTC workflow`};var cn=class{items;constructor(e){this.items=e}invalidate(){}render(e){let t=Math.max(1,Math.floor(e));return this.items.map(e=>ln(e,t))}};function ln(e,t){let{left:n,right:r}=e;if(r===void 0)return I(n,t,`…`);let i=L(r);if(i>=t)return I(r,t,`…`);let a=I(n,Math.max(0,t-i-1),`…`),o=Math.max(1,t-i-L(a));return a+` `.repeat(o)+r}function J(e,t){return e.length>t?`${e.slice(0,t-1)}…`:e}function un(e){return J((K(e).split(`
19
+ `).find(e=>e.trim().length>0)??``).trim(),120)}function dn(e){return e<1e3?`${e}ms`:`${(e/1e3).toFixed(2).replace(/\.?0+$/,``)}s`}function fn(e,t){return`${e} ${t}${e===1?``:`s`}`}function pn(e){for(let t of e.split(`
20
+ `)){let e=t.trim();if(e.length!==0&&!e.startsWith(`//`)&&!e.startsWith(`/*`)&&!e.startsWith(`*`))return J(e.replace(/\s*\/\/.*$/,``),120)}}function mn(e){if(e===void 0)return;if(typeof e==`string`){let t=K(e).split(`
21
+ `),n=t.find(e=>e.trim().length>0)?.trim()??``,r=t.length>1?` (+${t.length-1} lines)`:``;return n.length===0&&r.length===0?`""`:J(n,60)+r}if(typeof e!=`object`||!e)return String(e);let t=Wt(e);if(!t.includes(`
22
+ `)&&t.length<=60)return t;if(Array.isArray(e))return`Array(${e.length})`;let n=Object.keys(e);return n.length===0?`{}`:`{${n.slice(0,4).join(`, `)}${n.length>4?`, +${n.length-4}`:``}}`}function hn(e,t){if(e===void 0)return;let n=[],r=e.logs.length+e.narrations.length;if(r>0&&n.push(t.fg(`dim`,fn(r,`output line`))),e.imageCount>0&&n.push(t.fg(`toolOutput`,fn(e.imageCount,`image`))),e.surface===`workflow`&&e.phases.length>0&&n.push(t.fg(`dim`,fn(e.phases.length,`phase`))),e.warnings.length>0&&n.push(t.fg(`warning`,fn(e.warnings.length,`warning`))),e.fullOutputPath!==void 0&&n.push(t.fg(`warning`,`truncated`)),e.durationMs>0&&n.push(t.fg(`dim`,dn(e.durationMs))),n.length!==0)return`${t.fg(`dim`,`•`)} ${n.join(t.fg(`dim`,` · `))}`}function gn(e){if(e!==void 0)return{surface:e.surface??`run_code`,logs:e.logs??[],narrations:e.narrations??[],phases:e.phases??[],warnings:e.warnings??[],durationMs:e.durationMs??0,imageCount:e.imageCount??0,...e.result===void 0?{}:{result:e.result},...e.fullOutputPath===void 0?{}:{fullOutputPath:e.fullOutputPath},...e.subCalls===void 0?{}:{subCalls:e.subCalls}}}function _n(e,t,n){let r=` `+(t?`└─ `:`├─ `),i=` `+(t?` `:`│ `),a=` `.repeat(8),o=[],s=e.lines[0]??``;o.push({left:`${r}${e.label.padEnd(8)}${n.fg(`dim`,s)}`});for(let t=1;t<e.lines.length;t++)o.push({left:`${i}${a}${n.fg(`dim`,e.lines[t]??``)}`});return e.more!==void 0&&e.more>0&&o.push({left:`${i}${a}…+${e.more} more lines`}),o}function vn(e){return typeof e!=`object`||!e?!1:Array.isArray(e)?e.length>0:Object.keys(e).length>0}const yn=/^[A-Za-z_$][A-Za-z0-9_$]*$/;function bn(e){return yn.test(e)?e:JSON.stringify(e)}function xn(e,t){if(e===null)return`null`;if(typeof e==`boolean`||typeof e==`number`)return String(e);if(typeof e==`string`){let n=K(e);if(n.length===0)return`""`;if(n.includes(`
23
+ `)){let e=J(n.replace(/\n/g,` `),Math.max(2,t-5));return JSON.stringify(e)+`…`}return n.length+2<=t?JSON.stringify(n):JSON.stringify(J(n,Math.max(2,t-5)))+`…`}return`?`}function Sn(e){return typeof e!=`object`||!e}function Cn(e,t){if(t<8)return;if(Array.isArray(e)){if(e.length===0)return`[]`;if(e.length>5||!e.every(Sn))return;let n=`[`+e.map(e=>xn(e,Math.max(2,Math.floor(t/4)))).join(`, `)+`]`;return n.length<=t?n:void 0}if(typeof e!=`object`||!e)return;let n=Object.keys(e);if(n.length===0)return`{}`;if(n.length>5||!Object.values(e).every(Sn))return;let r=`{`+n.map(n=>{let r=e[n]??null;return bn(n)+`: `+xn(r,Math.max(2,Math.floor(t/4)))}).join(`, `)+`}`;return r.length<=t?r:void 0}function wn(e,t,n,r,i,a,o,s,c){let l=n+(r?`└─ `:`├─ `),u=n+(r?` `:`│ `);if(s>=i){c.push(Y(l+t+`…`,o));return}if(Array.isArray(e)&&e.length===0){c.push(Y(l+t+`[]`,o));return}if(!Array.isArray(e)&&typeof e==`object`&&e&&Object.keys(e).length===0){c.push(Y(l+t+`{}`,o));return}if(typeof e!=`object`||!e){let n=Math.max(0,o-L(l)-t.length);c.push(Y(l+t+xn(e,n),o));return}let d=Cn(e,Math.max(0,o-L(l)-t.length));if(d!==void 0){c.push(Y(l+t+d,o));return}let f=Array.isArray(e),p=f?e.length:Object.keys(e).length,m;m=f?`Array(`+p+`)`:p>a?`{`+a+` of `+p+`}`:`{`+p+` keys}`,c.push(Y(l+t+m,o));let h=Math.min(a,p);if(f)for(let t=0;t<h;t+=1){let n=t===h-1&&p<=h;wn(e[t]??null,`[`+t+`] `,u,n,i,a,o,s+1,c)}else{let t=Object.keys(e);for(let n=0;n<h;n+=1){let r=n===h-1&&p<=h,l=t[n];wn(e[l]??null,bn(l)+`: `,u,r,i,a,o,s+1,c)}}if(p>h){let e=f?`items`:`keys`;c.push(Y(u+`└─ …+`+(p-h)+` more `+e,o))}}function Y(e,t){if(L(e)<=t)return e;let n=e.match(/^([\s│├└─]*)/),r=n&&n[1]?n[1]:``,i=L(r),a=Math.max(0,t-i-1),o=``,s=0;for(let t of e.slice(r.length)){let e=L(t);if(s+e>a)break;o+=t,s+=e}return r+o+`…`}function Tn(e,t={}){let n=t.maxDepth??4,r=t.maxChildren??6,i=t.maxLineChars??120,a=t.moreAfter===!0;if(!vn(e))return typeof e==`object`&&e?[Array.isArray(e)?`[]`:`{}`]:[xn(e,i)];let o=[];if(Array.isArray(e)){let t=e.length,s=Math.min(r,t);for(let c=0;c<s;c+=1){let l=!a&&c===s-1&&t<=s;wn(e[c]??null,`[`+c+`] `,``,l,n,r,i,0,o)}return t>s&&o.push((a?`├─ `:`└─ `)+`…+`+(t-s)+` more items`),o}let s=Object.keys(e),c=s.length,l=Math.min(r,c);for(let t=0;t<l;t+=1){let u=!a&&t===l-1&&c<=l,d=s[t];wn(e[d]??null,bn(d)+`: `,``,u,n,r,i,0,o)}return c>l&&o.push((a?`├─ `:`└─ `)+`…+`+(c-l)+` more keys`),o}function En(e,t,n=`run_code`,r={}){let i=(typeof e.description==`string`&&e.description.trim().length>0?e.description.trim():e.meta?.name)??``,a=pn(e.code??e.script??``),o=i.length>0?i:a??`(no description)`;return an(new cn([{left:`└─ ${`${t.fg(`toolTitle`,t.bold(sn[n]))} ${t.fg(`accent`,o)}`}`}]),{intervalMs:150,isPartial:r.isPartial??!1,theme:t,requestInvalidate:r.requestInvalidate??Dn,state:r.state??{}})}function Dn(){}function On(e){let t=e.content?.find(e=>e.type===`text`&&(e.text??``).length>0);return un(t?.text??``)||`failed`}function kn(e,t){if(e===`pi.dispatch`&&typeof t==`object`&&t&&`agent`in t)return`→ ${String(t.agent)}`;if(typeof t==`object`&&t){let n=t,r=e=>typeof n[e]==`string`?n[e]:void 0,i=e===`read`?r(`path`):e===`bash`?r(`command`):e===`grep`||e===`find`?r(`pattern`):e===`ls`||e===`edit`||e===`write`?r(`path`):void 0;if(typeof i==`string`)return i}try{let e=JSON.stringify(t).replace(/\n/g,` `);return e.length>40?`${e.slice(0,39)}…`:e}catch{return String(t).slice(0,40)}}const An={running:{text:()=>`running`,themeSlot:`muted`},ok:{text:e=>e.durationMs===void 0?`ok`:`ok ${dn(e.durationMs)}`,themeSlot:`accent`},error:{text:e=>`failed: ${e.errorMessage??`error`}`,themeSlot:`error`},cancelled:{text:()=>`cancelled`,themeSlot:`muted`},rejected:{text:e=>`rejected: ${e.errorMessage??`concurrency`}`,themeSlot:`warning`}};function jn(e,t){if(e.length===0)return[];let n=e.slice(0,32),r=e.length>32?e.length-32:0,i=[];return n.forEach((e,a)=>{let o=a===n.length-1&&r===0?`└─ `:`├─ `,s=e.name.length>=8?`${e.name} `:e.name.padEnd(8),c=kn(e.name,e.args),l=An[e.status];i.push({left:` ${o}${t.fg(`toolTitle`,s)}${t.fg(l.themeSlot,l.text(e))}${t.fg(`dim`,` `+c)}`})}),r>0&&i.push({left:` └─ …+${r} more calls`}),i}function Mn(e,t,n,r=!1,i=!1){let a=hn(e.details,n),o=e=>a===void 0?e:{...e,right:a};if(t)return[o({left:` ${n.fg(`error`,`failed: ${On(e)}`)}`})];if(i&&e.details?.result===void 0)return[];let s=e.details?.result;if(s===void 0)return[o({left:` ${n.fg(`muted`,`done`)}`})];if(vn(s))return Tn(s,{maxDepth:4,maxChildren:6,maxLineChars:120,moreAfter:r}).map((e,t)=>{let n={left:` ${e}`};return t===0&&a!==void 0?{...n,right:a}:n});let c=mn(s);return c===void 0?[o({left:` ${n.fg(`muted`,`done`)}`})]:[o({left:` ${n.fg(`dim`,`→ ${c}`)}`})]}function Nn(e,t,n,r={}){let i=gn(e.details),a=[...Mn({...e,details:i},t,n,!1,r.isPartial===!0)];return i?.subCalls!==void 0&&a.push(...jn(i.subCalls,n)),new cn(a)}function Pn(e,t,n,r,i={}){let a=gn(e.details);if(n){let t=[...Mn({...e,details:a},!0,r)];a?.subCalls!==void 0&&t.push(...jn(a.subCalls,r));let n=K(e.content?.find(e=>e.type===`text`)?.text??On(e));for(let e of n.split(`
24
+ `).slice(0,12))t.push({left:` ${r.fg(`error`,e)}`});return new cn(t)}let o=[],s=t.code??t.script??``;if(s.length>0){let e=s.split(`
25
+ `).map(e=>J(e,120)),t=e.slice(0,3);o.push({label:`code`,lines:t,...e.length>t.length?{more:e.length-t.length}:{}})}if(a!==void 0&&a.surface===`workflow`&&a.phases.length>0){let e=a.phases.slice(0,8),t=a.phases.length>e.length?` …`:``;o.push({label:`phases`,lines:[`${e.join(` → `)}${t}`]})}if(a!==void 0&&a.narrations.length>0){let e=a.narrations.slice(0,12);o.push({label:`log`,lines:e.map(e=>K(e)),...a.narrations.length>e.length?{more:a.narrations.length-e.length}:{}})}if(a!==void 0&&a.logs.length>0){let e=a.logs.slice(0,12);o.push({label:`out`,lines:e.map(e=>K(e)),...a.logs.length>e.length?{more:a.logs.length-e.length}:{}})}if(a!==void 0&&a.warnings.length>0){let e=a.warnings.slice(0,4);o.push({label:`warn`,lines:e.map(e=>K(e)),...a.warnings.length>e.length?{more:a.warnings.length-e.length}:{}})}if(a!==void 0&&a.imageCount>0){let e=a.imageCount===1?`1 image attached`:`${a.imageCount} images attached`;o.push({label:`image`,lines:[e]})}let c=a?.subCalls,l=c!==void 0&&c.length>0||o.length>0,u=Mn({...e,details:a},!1,r,l,i.isPartial===!0);c!==void 0&&u.push(...jn(c,r));for(let e=0;e<o.length;e+=1){let t=e===o.length-1;u.push(..._n(o[e]??{label:``,lines:[]},t,r))}return new cn(u)}function Fn(e){return{renderCall(t,n,r){return En(t,n,e,{...r?.invalidate===void 0?{}:{requestInvalidate:r.invalidate},...r?.isPartial===void 0?{}:{isPartial:r.isPartial},...r?.state===void 0?{}:{state:r.state}})},renderResult(e,t,n,r){let i=t.isPartial===!0;return t.expanded===!0?Pn(e,r?.args??{},r?.isError===!0,n,{isPartial:i}):Nn(e,r?.isError===!0,n,{isPartial:i})}}}const In=["Run a TypeScript program that composes pi's tools in one shot. Required arguments: `code` —","the body of an async function (top-level `return` and `await` work; type annotations are","advisory, the code runs type-stripped) — and `description`, a 5-10 word summary of what the",`program does.`,``,"Inside the program, call this session's enabled built-in tools as `tools.<name>(args)` — e.g.",'`await tools.read({ path: "src/index.ts" })` or `await tools.bash({ command: "npm test" })`.',"The bound names mirror the session's active tools (a default session has `read`, `bash`, `edit`,","`write`); calling a name that is not bound rejects with an error the program can catch, and","independent calls may overlap under `Promise.all`.",``,"Only the program's return value and its `console.log` output come back. This surface has no","helpers: `log` / `phase` / `parallel` / `pipeline` exist only in `ptc_workflow`.",``,"Image-bearing tool results inside the program (a `tools.read` on a PNG, say) are attached to you",`after the run, so never return image data as the completion value — that only spends your context`,`on base64.`].join(`
26
+ `),Ln=x.Object({code:x.String({description:`The program: the body of an async TypeScript function.`}),description:x.String({description:`Clear, concise description of what this program does in active voice, 5-10 words (shown in the UI).`}),timeoutMs:x.Optional(x.Number({description:`Elapsed deadline in milliseconds for the whole run, including tool calls. Omit it (or pass 0) for the default of ${B.timeoutMs/1e3} s; anything above ${B.maxTimeoutMs/1e3} s is capped.`}))});function Rn(e={}){return g({name:`ptc_run_code`,label:`PTC Run Code`,description:In,promptSnippet:`Run a TypeScript program that composes pi's tools in one shot (tools.<name>(args)); only its return value and console.log come back`,promptGuidelines:[...qt],parameters:Ln,async execute(t,n,r,i,a){let o=Yt(a),s=q(e.getBindingSourceNames?.()),c=Date.now(),l=e.getPool?.(),u=a.sessionManager?.getSessionDir?.(),d=e.getDispatchDeps?.(),f=Xt({surface:`run_code`,startedAt:c,onUpdate:i}),p;try{p=await Ot({code:n.code,surface:`run_code`,cwd:o,bindings:ht({cwd:o,names:s,includeDispatch:!0}),...u===void 0?{}:{sessionDir:u},...d===void 0?{}:{dispatchDeps:d},...e.depth===void 0?{}:{depth:e.depth},...e.parentTaskId===void 0?{}:{parentTaskId:e.parentTaskId},...n.timeoutMs===void 0?{}:{timeoutMs:n.timeoutMs},...r===void 0?{}:{signal:r},...e.config===void 0?{}:{config:e.config},...l===void 0?{}:{pool:l},onSubCallChange:e=>f.update(e)})}finally{f.cancel()}if(p.error!==void 0)throw Qt(p);return $t({outcome:p,surface:`run_code`,durationMs:Date.now()-c})},...Fn(`run_code`)})}const zn=[`Run a structured TypeScript workflow: a named plan that reports phases and narration as it`,"goes. `script` is the body of an async function, `meta` names it (`{ name, description, phases? }`,","where `phases` is the ordered plan), and optional `args` is plain-JSON input bound to the","program's `args` global.",``,"Helpers inside the script: `log(message)` narrates progress; `phase(title)` frames the workflow","(titles outside `meta.phases` are reported as warnings); `parallel(thunks)` runs thunks","concurrently, a failed item becoming `null`; `pipeline(items, ...stages)` chains stages per item","with the same per-item `null` on failure. There is no `agent()` helper.",``,"Tools are reachable as `tools.<name>(args)` exactly as in `ptc_run_code`, and the bound names",`mirror the session's enabled tools. What comes back is the script's return value, its`,"`log`/`phase` narration and its `console.log` output.",``,`Image-bearing tool results inside the script are attached to you after the run — never return`,`image data as the script's value.`].join(`
27
+ `),Bn=x.Object({meta:x.Object({name:x.String({description:`Short workflow name, shown in the UI.`}),description:x.String({description:`What this workflow does, in active voice.`}),phases:x.Optional(x.Array(x.Object({name:x.String({description:`Phase title, matched against phase(title) calls.`})}),{description:`Ordered plan of the workflow's phases. phase() titles outside this list are reported as warnings.`}))},{description:`Workflow metadata: what this run is and which phases it declares.`}),script:x.String({description:`The program: the body of an async TypeScript function.`}),args:x.Optional(x.Record(x.String(),x.Unknown(),{description:"Plain-JSON input bound to the program's `args` global. No functions, symbols, undefined values or cycles."}))});function Vn(t){if(t!==void 0){if(typeof t!=`object`||!t||Array.isArray(t))throw TypeError(`ptc_workflow args must be a plain JSON object, received ${e(t)}`);for(let e of Object.keys(t)){let n=Hn(t[e],`args.${e}`,/* @__PURE__ */ new Set);if(n!==void 0)throw TypeError(`ptc_workflow args must be plain JSON: ${n}`)}}}function Hn(t,n,r){if(t===null||typeof t==`string`||typeof t==`boolean`)return;if(t===void 0)return`${n} is undefined`;if(typeof t==`number`)return Number.isFinite(t)?void 0:`${n} is ${String(t)}, which is not representable as JSON`;if(typeof t!=`object`)return`${n} is ${e(t)}`;let i=t;if(r.has(i))return`${n} is a circular reference`;r.add(i);try{if(Array.isArray(i)){for(let e=0;e<i.length;e+=1){let t=Hn(i[e],`${n}[${e}]`,r);if(t!==void 0)return t}return}let e=Object.getPrototypeOf(i);if(e!==Object.prototype&&e!==null){let e=i.constructor?.name;return`${n} is a ${typeof e==`string`&&e.length>0?e:`non-plain object`}, not plain JSON`}for(let e of Object.keys(i)){let t=Hn(i[e],`${n}.${e}`,r);if(t!==void 0)return t}return}finally{r.delete(i)}}function Un(e,t){if(t===void 0||t.length===0)return[];let n=new Set(t),r=/* @__PURE__ */ new Set,i=[];for(let t of e.phases)n.has(t)||r.has(t)||(r.add(t),i.push(`phase "${t}" is not listed in meta.phases (declared: ${[...n].join(`, `)})`));return i}function Wn(e={}){return g({name:`ptc_workflow`,label:`PTC Workflow`,description:zn,promptSnippet:`Run a structured TypeScript workflow with named phases, narration (log/phase) and structured concurrency (parallel/pipeline)`,promptGuidelines:[...qt],parameters:Bn,async execute(t,n,r,i,a){Vn(n.args);let o=Yt(a),s=q(e.getBindingSourceNames?.()),c=Date.now(),l=e.getPool?.(),u=a.sessionManager?.getSessionDir?.(),d=e.getDispatchDeps?.(),f=Xt({surface:`workflow`,startedAt:c,onUpdate:i}),p;try{p=await Ot({code:n.script,surface:`workflow`,cwd:o,bindings:ht({cwd:o,names:s,includeDispatch:!0}),...u===void 0?{}:{sessionDir:u},...d===void 0?{}:{dispatchDeps:d},...n.args===void 0?{}:{args:n.args},...e.depth===void 0?{}:{depth:e.depth},...e.parentTaskId===void 0?{}:{parentTaskId:e.parentTaskId},...r===void 0?{}:{signal:r},...e.config===void 0?{}:{config:e.config},...l===void 0?{}:{pool:l},onSubCallChange:e=>f.update(e)})}finally{f.cancel()}if(p.error!==void 0)throw Qt(p);let m=n.meta.phases?.map(e=>e.name);return $t({outcome:p,surface:`workflow`,warnings:Un(p,m),durationMs:Date.now()-c})},...Fn(`workflow`)})}var Gn=class{#e;constructor(e){this.#e=new Map(e??[])}async readOutput(e){let t=this.#e.get(e);return t===void 0?null:t}async writeOutput(e,t){this.#e.set(e,t)}outputRef(e){return`memory:tasks/${e}/output.log`}};function Kn(e){return typeof e==`object`&&!!e&&e.code===`ENOENT`}var qn=class{#e;constructor(e){if(e.length===0)throw TypeError(`FileOutputStorage: baseDir is required`);this.#e=e}async readOutput(e){try{return await R(this.#t(e),`utf8`)}catch(e){if(Kn(e))return null;throw e}}async writeOutput(e,t){let n=k(this.#e,`tasks`,e);await se(n,{recursive:!0}),await z(k(n,`output.log`),t,`utf8`)}outputRef(e){return this.#t(e)}#t(e){return k(this.#e,`tasks`,e,`output.log`)}};function Jn(e,t){return Mt(e,`pi-ptc-task-output-`,t===void 0?{}:{fullText:t})}const Yn={running:`accent`,stopping:`warning`,succeeded:`success`,failed:`error`,lost:`dim`,canceled:`muted`},Xn={running:`●`,stopping:`◐`,succeeded:`✓`,failed:`✗`,lost:`◌`,canceled:`⊘`};var X=class{items;constructor(e){this.items=e}invalidate(){}render(e){let t=Math.max(1,Math.floor(e));return this.items.map(e=>Zn(e,t))}};function Zn(e,t){let{left:n,right:r}=e;if(r===void 0)return I(n,t,`…`);let i=L(r);if(i>=t)return I(r,t,`…`);let a=I(n,Math.max(0,t-i-1),`…`),o=Math.max(1,t-i-L(a));return a+` `.repeat(o)+r}function Qn(e,t){return e.length>t?`${e.slice(0,t-1)}…`:e}function $n(e){return Qn((K(e).split(`
28
+ `).find(e=>e.trim().length>0)??``).trim(),120)}function er(e){return typeof e==`object`&&e?e:{}}function tr(e){return e===`running`||e===`stopping`}function nr(e){let t=Math.max(0,Math.floor(e));if(t<1e3)return`${t}ms`;let n=Math.floor(t/1e3);if(n<60)return`${n}s`;let r=Math.floor(n/60),i=n%60;return r<60?`${r}m${i}s`:`${Math.floor(r/60)}h${String(r%60).padStart(2,`0`)}m`}function rr(e,t){if(tr(e.status))return nr(t-e.createdAt);if(e.durationMs!==void 0)return nr(e.durationMs);if(e.finishedAt!==void 0)return nr(e.finishedAt-e.createdAt)}function ir(e,t){let{state:n}=t;t.isLive?n.interval??=setInterval(()=>t.requestInvalidate(),1e3):n.interval!==void 0&&(clearInterval(n.interval),n.interval=void 0);let r=e;return r.dispose=()=>{n.interval!==void 0&&(clearInterval(n.interval),n.interval=void 0)},e}function ar(){}function or(e,t,n){return new X([{left:`└─ ${n.fg(`toolTitle`,n.bold(e))}${t.length>0?` ${t}`:``}`}])}function sr(e,t,n){let r=er(t);switch(e){case`task-list`:{let e=Array.isArray(r.status)?r.status:void 0,t=e!==void 0&&e.length>0?e.join(`,`):`all`,i=typeof r.limit==`number`?` · limit=${r.limit}`:``;return or(`PTC task list`,n.fg(`dim`,`status=${t}${i}`),n)}case`task-output`:{let e=typeof r.taskId==`string`&&r.taskId.length>0?r.taskId:void 0,t=typeof r.sinceBytes==`number`?` · since=${r.sinceBytes}B`:``;return or(`PTC task output`,e===void 0?n.fg(`muted`,`(unknown task)`):`${n.fg(`accent`,e)}${n.fg(`dim`,t)}`,n)}case`task-stop`:{let e=typeof r.taskId==`string`&&r.taskId.length>0?r.taskId:void 0,t=typeof r.reason==`string`&&r.reason.length>0?` · reason=${K(r.reason)}`:``;return or(`PTC task stop`,e===void 0?n.fg(`muted`,`(unknown task)`):`${n.fg(`accent`,e)}${n.fg(`dim`,t)}`,n)}}}function cr(e,t,n,r){let i=Yn[e.status]??`muted`,a=n.fg(i,Xn[e.status]??`?`),o=Qn(K(e.label),48),s=[`${a} ${n.fg(`text`,o)}`,n.fg(`dim`,K(e.agentName))],c=rr(e,r);return c!==void 0&&s.push(n.fg(`dim`,c)),e.outputBytes!==void 0&&s.push(n.fg(`dim`,`${e.outputBytes}B`)),{left:` ${t?`└─ `:`├─ `}${s.join(n.fg(`dim`,` · `))}`}}function lr(e,t){let n=er(e),r=Array.isArray(n.tasks)?n.tasks:[];if(r.length===0)return ir(new X([{left:` ${t.theme.fg(`muted`,`(no background tasks)`)}`}]),{isLive:!1,state:t.state,requestInvalidate:t.requestInvalidate});let i=r.some(e=>tr(e.status)),a=r.slice(0,32),o=r.length-a.length,s=a.map((e,n)=>cr(e,n===a.length-1&&o===0,t.theme,t.now));return o>0&&s.push({left:` └─ ${t.theme.fg(`dim`,`…+${o} more tasks`)}`}),ir(new X(s),{isLive:t.partial&&i,state:t.state,requestInvalidate:t.requestInvalidate})}function ur(e,t,n){let r=[];if(e!==void 0&&r.push(n.fg(`dim`,`${e}B`)),t&&r.push(n.fg(`warning`,`truncated`)),r.length!==0)return`${n.fg(`dim`,`•`)} ${r.join(n.fg(`dim`,` · `))}`}function dr(e,t){return` ${t.fg(`dim`,`full output: `)}${t.fg(`accent`,e)}`}function fr(e,t){let n=er(e),r=typeof n.output==`string`?n.output:``,i=typeof n.outputBytes==`number`?n.outputBytes:void 0,a=n.outputTruncated===!0,o=typeof n.outputFullPath==`string`&&n.outputFullPath.length>0?n.outputFullPath:void 0,s=ur(i,a,t.theme),c=[];if(r.length>0){let e=K(r).split(`
29
+ `),n=e.slice(0,6);n.forEach((e,n)=>{let r=t.theme.fg(`toolOutput`,Qn(e,160));c.push(n===0&&s!==void 0?{left:` ${r}`,right:s}:{left:` ${r}`})}),e.length>n.length&&c.push({left:` ${t.theme.fg(`dim`,`…+${e.length-n.length} more lines`)}`}),a&&o!==void 0&&c.push({left:dr(o,t.theme)})}else o===void 0?c.push({left:` ${t.theme.fg(`muted`,`(no output yet)`)}`,...s===void 0?{}:{right:s}}):c.push({left:dr(o,t.theme),...s===void 0?{}:{right:s}});return new X(c)}function pr(e,t){let n=er(e),r=n.task;if(typeof r!=`object`||!r)return new X([{left:` ${t.theme.fg(`muted`,`(no task)`)}`}]);let i=Yn[r.status]??`muted`,a=t.theme.fg(i,Xn[r.status]??`?`),o=typeof n.fromStatus==`string`?n.fromStatus:void 0,s=o===void 0?r.status===`stopping`?`running → stopping`:`→ ${String(r.status)}`:`${o} → ${r.status}`,c=[`${a} ${t.theme.fg(`text`,K(r.id))}`,t.theme.fg(i,s)];return typeof r.stopReason==`string`&&r.stopReason.length>0&&c.push(t.theme.fg(`dim`,`reason=${K(r.stopReason)}`)),new X([{left:` ${c.join(` `)}`}])}function mr(e,t){let n=e.content?.find(e=>e.type===`text`&&(e.text??``).length>0),r=$n(n?.text??``)||`failed`;return new X([{left:` ${t.fg(`error`,`failed: ${r}`)}`}])}function hr(e,t,n){switch(e){case`task-list`:return lr(t.details,n);case`task-output`:return fr(t.details,n);case`task-stop`:return pr(t.details,n)}}function gr(e){return{renderCall(t,n){return sr(e,t,n)},renderResult(t,n,r,i){return i?.isError===!0?mr(t,r):hr(e,t,{theme:r,partial:n?.isPartial===!0||i?.isPartial===!0,requestInvalidate:i?.invalidate??ar,state:i?.state??{},now:Date.now()})}}}const _r=[`Use ptc_task_list to see the background tasks this session dispatched and their live status.`,`Use ptc_task_output to read a task's captured output (it is tail-truncated; the footer names the full-output file).`,`Use ptc_task_stop to ask a running task to stop; the dispatcher delivers the actual signal, so the task transitions through stopping to canceled.`,`These background-task tools stay callable when PTC mode is off — /ptc off only blocks new dispatches.`],vr=x.Union([x.Literal(`running`),x.Literal(`stopping`),x.Literal(`succeeded`),x.Literal(`failed`),x.Literal(`canceled`),x.Literal(`lost`)]),yr=x.Object({status:x.Optional(x.Array(vr,{description:`Only return tasks in these states. Omit for all six states (running, stopping, succeeded, failed, canceled, lost).`})),limit:x.Optional(x.Number({description:`Maximum number of tasks to return, newest first. Defaults to 100.`}))});function br(e){let t=[e.id,e.status,`${e.agentName} depth=${e.depth}`,K(e.label)];return e.outputBytes!==void 0&&t.push(`${e.outputBytes}B`),e.errorMessage!==void 0&&t.push(`error=${K(e.errorMessage)}`),t.join(` `)}function xr(e){return g({name:`ptc_task_list`,label:`PTC Task List`,description:[`List the background tasks this session has dispatched (from pi.dispatch({ background: true })).`,`Returns each task's id, status, agent, depth, label and output size, newest first.`,`Filter with status (e.g. ["running"]); limit caps the row count (default 100).`].join(`
30
+ `),promptSnippet:`List background tasks and their live status (id, status, agent, label, output size)`,promptGuidelines:[..._r],parameters:yr,async execute(t,n){let r=await e.query({...n.status===void 0?{}:{status:n.status},limit:n.limit??100,orderBy:`createdAt-desc`});return{content:[{type:`text`,text:r.length===0?`(no background tasks)`:r.map(e=>br(e)).join(`
31
+ `)}],details:{tasks:r,count:r.length}}},...gr(`task-list`)})}async function Sr(e,t,n){let r=await e.get(t);if(r===null)throw Error(`${n}: unknown taskId ${t}`);return r}const Cr=x.Object({taskId:x.String({description:`The background task's id (as returned by ptc_task_list or the dispatch handle).`}),sinceBytes:x.Optional(x.Number({description:`Skip this many bytes of the stored output before returning it. Use for paging larger logs; defaults to 0.`}))});function wr(e,t){return g({name:`ptc_task_output`,label:`PTC Task Output`,description:[`Read a background task's captured output.`,`The text is tail-truncated to pi's ADR-0015 limits (50 KB / 2000 lines); when anything is cut, outputFullPath names a file with the complete output.`,`Pass sinceBytes to skip a prefix of the stored output when paging.`].join(`
32
+ `),promptSnippet:`Read a background task's output (tail-truncated, with the full-output file path)`,promptGuidelines:[..._r],parameters:Cr,async execute(n,r){let i=r.taskId,a=await Sr(e,i,`ptc_task_output`),o=await t.readOutput(i)??``,s=Buffer.byteLength(o,`utf8`),c=r.sinceBytes??0;if(!Number.isInteger(c)||c<0)throw Error(`ptc_task_output: sinceBytes must be a non-negative integer, got ${String(r.sinceBytes)}`);if(c>s)throw Error(`ptc_task_output: sinceBytes ${c} exceeds the ${s}-byte output of task ${i}`);let l=Buffer.from(o,`utf8`);if(c<s&&((l[c]??0)&192)==128)throw Error(`ptc_task_output: sinceBytes ${c} splits a UTF-8 character in task ${i}; use a byte offset on a character boundary`);let u=Jn(c===0?o:l.subarray(c).toString(`utf8`),o),d=s<=2048?o:void 0,f={taskId:i,output:u.text,outputBytes:s,...d===void 0?{}:{outputPreview:d},outputTruncated:u.truncated,...u.fullPath===void 0?{}:{outputFullPath:u.fullPath}};return{content:[{type:`text`,text:f.output.length>0?f.output:`(no output yet; task ${i} is ${a.status})`}],details:f}},...gr(`task-output`)})}const Tr=x.Object({taskId:x.String({description:`The running task's id (as returned by ptc_task_list or the dispatch handle).`}),reason:x.Optional(x.String({description:`Why the task is being stopped; recorded as the task's stopReason. Defaults to "model stop".`}))});function Er(e,t,n={}){if(t==null)throw TypeError(`createPtcTaskStopTool: lifecycle is required`);let r=n.clock??(()=>Date.now());return g({name:`ptc_task_stop`,label:`PTC Task Stop`,description:[`Ask a running background task to stop.`,`The task moves to "stopping"; the dispatcher sends the signal and records the terminal "canceled" state when the child exits.`,`The call is idempotent when the task is already stopping, and errors when the task is already terminal.`].join(`
33
+ `),promptSnippet:`Ask a background task to stop (dispatcher delivers the signal)`,promptGuidelines:[..._r],parameters:Tr,async execute(t,n){let i=n.taskId,a=await Sr(e,i,`ptc_task_stop`),o=a.spawnSource.callerId.length>0?a.spawnSource.callerId:`ptc_task_stop`,{record:s,fromStatus:c}=await e.transition({kind:`stop`,taskId:i,reason:n.reason??`model stop`},{clock:r,callerId:o}),l=s.stopReason===void 0?``:` reason=${K(s.stopReason)}`;return{content:[{type:`text`,text:`${s.id} ${s.status}${l}`}],details:{task:s,fromStatus:c??a.status}}},...gr(`task-stop`)})}var Dr=class{#e;#t=/* @__PURE__ */ new Map;constructor(e={}){this.#e=Oe(e.config)}get(e){let t=this.#t.get(e);if(t!==void 0)return t;let n=De(),r=new vt({buildWorkerUrl:xt,size:this.#e.poolSize,acquireTimeoutMs:this.#e.poolAcquireTimeoutMs,drainGraceMs:this.#e.drainGraceMs,workerOptions:_t({surface:e,env:n,limits:this.#e})});return this.#t.set(e,r),r}async drain(){let e=[...this.#t.values()];this.#t.clear(),await Promise.all(e.map(e=>e.drain()))}};const Or={warn:e=>{console.warn(`[pi-ptc.notifications] `+e)}};var kr=class{#e;#t;#n;#r;#i;constructor(e,t={}){this.#e=e,this.#t=t.now??Date.now,this.#n=t.logger??Or,this.#r=[],this.#i=/* @__PURE__ */ new Map}async subscribe(e,t,n){let r=await this.#e.loadSubscription(e,t);if(r!==null)return r;let i={subscriberId:e,taskId:t,cursor:n??`00000000000000000000`,status:`active`,createdAt:this.#t()};return await this.#e.saveSubscription(i),i}async drainPending(e,t){let n=await this.#o(e,t,`drainPending`);return this.#s(n)}async acknowledgeEvents(e,t,n){await this.#a(e,t,async()=>{let r=await this.#o(e,t,`acknowledgeEvents`);n<=r.cursor||await this.#e.saveSubscription({...r,cursor:n})})}#a(e,t,n){let r=`${e}-${t}`,i=(this.#i.get(r)??Promise.resolve()).then(n),a=i.then(()=>void 0,()=>void 0);return this.#i.set(r,a),a.then(()=>{this.#i.get(r)===a&&this.#i.delete(r)}),i}onIdleWake(e){this.#r.push(e)}async pendingCount(e,t){let n=await this.#o(e,t,`pendingCount`);return(await this.#s(n)).length}async notifyIdle(e,t){let n=await this.drainPending(e,t);if(n.length===0)return n;let r=this.#r.slice();for(let i of r)try{i(e,n)}catch(n){let r=n instanceof Error?n.message:String(n);this.#n.warn(`notifyIdle: idle-wake handler for subscriber ${e} / task ${t} threw: ${r}`)}return n}async#o(e,t,n){let r=await this.#e.loadSubscription(e,t);if(r===null)throw Error(`${n}: unknown subscription for subscriber ${e} / task ${t}`);return r}async#s(e){let t=[];for await(let n of this.#e.loadEvents(e.subscriberId,e.cursor===`00000000000000000000`?void 0:e.cursor))n.taskId===e.taskId&&t.push(n);return t}};const Ar={mkdir:(e,t)=>se(e,t),readFile:(e,t)=>R(e,t),writeFile:(e,t,n)=>z(e,t,n),appendFile:(e,t,n)=>oe(e,t,n),rename:(e,t)=>le(e,t),unlink:e=>ue(e),readdir:e=>ce(e)};function jr(e){return typeof e==`object`&&!!e&&e.code===`ENOENT`}function Mr(e){return e instanceof Error?e.message:String(e)}function Nr(e,t){return e.eventId<t.eventId?-1:+(e.eventId>t.eventId)}var Pr=class{#e;#t;constructor(e,t={}){if(e.length===0)throw TypeError(`FileTaskStorage: baseDir is required`);this.#e=e,this.#t=t.fs??Ar}async loadTask(e){return await this.#r(this.#f(e))}async saveTask(e){M(e,`saveTask: record is required`),await this.#o(this.#f(e.id),e)}async deleteTask(e){let t=this.#f(e);if(!await this.#c(t))throw Error(`deleteTask: unknown taskId `+e);await this.#t.unlink(t)}async*listTasks(e){let t=this.#l();for(let n of(await this.#s(t)).sort()){if(!n.endsWith(`.json`))continue;let r=await this.#r(k(t,n));r!==null&&(e?.status===void 0||r.status===e.status)&&(e?.label===void 0||r.label===e.label)&&(yield r)}}async loadSubscription(e,t){return await this.#r(this.#p(e,t))}async saveSubscription(e){M(e,`saveSubscription: sub is required`),await this.#o(this.#p(e.subscriberId,e.taskId),e)}async appendEvents(e,t){if(t.length===0){if((await this.#n(e)).length===0)throw Error(`appendEvents: unknown subscriptionId `+e);return}let n=/* @__PURE__ */ new Map;for(let e of t){let t=n.get(e.taskId);t===void 0?n.set(e.taskId,[e]):t.push(e)}let r;for(let t of n.keys())if(await this.loadSubscription(e,t)===null){r=t;break}if(r!==void 0)throw(await this.#n(e)).length===0?Error(`appendEvents: unknown subscriptionId `+e):Error(`appendEvents: no subscription for subscriber `+e+` on task `+r);await this.#t.mkdir(this.#d(),{recursive:!0});for(let[t,r]of n){let n=(r.every((e,t)=>t===0||Nr(r[t-1],e)<=0)?r:[...r].sort(Nr)).map(e=>JSON.stringify(e)).join(`
34
+ `)+`
35
+ `;await this.#t.appendFile(this.#m(e,t),n,{encoding:`utf8`})}}async*loadEvents(e,t){let n=await this.#n(e);if(n.length===0)throw Error(`loadEvents: unknown subscriptionId `+e);let r=[];for(let t of n)r.push(...await this.#a(this.#m(e,t.taskId)));r.sort(Nr);for(let e of r)t!==void 0&&e.eventId<=t||(yield e)}async#n(e){let t=this.#u(),n=[];for(let r of await this.#s(t)){if(!r.endsWith(`.json`))continue;let i=await this.#r(k(t,r));i!==null&&i.subscriberId===e&&n.push(i)}return n}async#r(e){let t;try{t=await this.#t.readFile(e,{encoding:`utf8`})}catch(e){if(jr(e))return null;throw e}return this.#i(e,t)}#i(e,t){let n;try{n=JSON.parse(t)}catch(t){throw Error(`FileTaskStorage: corrupt JSON in `+e+`: `+Mr(t))}if(typeof n!=`object`||!n||Array.isArray(n))throw Error(`FileTaskStorage: corrupt JSON in `+e+`: expected a JSON object`);return n}async#a(e){let t;try{t=await this.#t.readFile(e,{encoding:`utf8`})}catch(e){if(jr(e))return[];throw e}let n=[];for(let r of t.split(`
36
+ `)){let t=r.trim();if(t.length===0)continue;let i;try{i=JSON.parse(t)}catch(t){throw Error(`FileTaskStorage: corrupt event JSON in `+e+`: `+Mr(t))}if(typeof i!=`object`||!i||Array.isArray(i))throw Error(`FileTaskStorage: corrupt event JSON in `+e+`: expected a JSON object`);n.push(i)}return n}async#o(e,t){await this.#t.mkdir(O(e),{recursive:!0});let n=e+`.tmp-`+j();await this.#t.writeFile(n,JSON.stringify(t,null,2),{encoding:`utf8`}),await this.#t.rename(n,e)}async#s(e){try{return await this.#t.readdir(e)}catch(e){if(jr(e))return[];throw e}}async#c(e){try{return await this.#t.readFile(e,{encoding:`utf8`}),!0}catch(e){if(jr(e))return!1;throw e}}#l(){return k(this.#e,`tasks`)}#u(){return k(this.#e,`subscriptions`)}#d(){return k(this.#e,`events`)}#f(e){return k(this.#l(),e+`.json`)}#p(e,t){return k(this.#u(),e+`-`+t+`.json`)}#m(e,t){return k(this.#d(),e+`-`+t+`.jsonl`)}};function Fr(e,t){return new Pr(e,t)}const Ir=/* @__PURE__ */ new Set([`succeeded`,`failed`,`canceled`,`lost`]),Lr=/* @__PURE__ */ new Set([`running`,`stopping`]),Rr=2**53-1,zr={info:()=>void 0,warn:e=>{console.warn(`[pi-ptc.background] `+e)}};function Z(e){return e instanceof Error?e.message:String(e)}function Br(e){return e===void 0||e.length===0?`<in-memory>`:e}function Vr(e){let t=e.spawnSource.callerId;return t.length>0?t:e.id}var Hr=class{#e;#t=/* @__PURE__ */ new Map;constructor(e){this.#e=e}setCurrent(e){this.#e=e}async transition(e,t){let n=e.kind===`spawn`?this.#e:this.#t.get(e.taskId)??this.#e,r=await n.transition(e,t);return e.kind===`spawn`&&this.#t.set(r.record.id,n),r}query(e){return this.#e.query(e)}get(e){return this.#e.get(e)}onTransition(e){return this.#e.onTransition(e)}reconcileLostTasks(){return this.#e.reconcileLostTasks()}},Ur=class{#e;constructor(e){this.#e=e}setCurrent(e){this.#e=e}readOutput(e){return this.#e.readOutput(e)}writeOutput(e,t){return this.#e.writeOutput(e,t)}outputRef(e){return this.#e.outputRef(e)}},Wr=class{#e;#t=[];constructor(e){this.#e=e}setCurrent(e){this.#e=e;for(let t of this.#t)e.onIdleWake(t)}current(){return this.#e}subscribe(e,t,n){return this.#e.subscribe(e,t,n)}drainPending(e,t){return this.#e.drainPending(e,t)}acknowledgeEvents(e,t,n){return this.#e.acknowledgeEvents(e,t,n)}onIdleWake(e){this.#t.push(e),this.#e.onIdleWake(e)}pendingCount(e,t){return this.#e.pendingCount(e,t)}notifyIdle(e,t){return this.#e.notifyIdle(e,t)}},Gr=class{#e;#t=/* @__PURE__ */ new Set;#n=/* @__PURE__ */ new Map;constructor(e){this.#e=e}isLive(e){return this.#t.has(e)}trackReapCancel(e,t){this.#n.set(e,t)}spawn(e,t){let n=this.#e.spawn(e,t);return this.#t.add(n),n}kill(e,t){this.#e.kill(e,t)}events(e){return this.#e.events(e)}async exit(e){try{return await this.#e.exit(e)}finally{this.#t.delete(e);let t=this.#n.get(e);t!==void 0&&(this.#n.delete(e),t())}}stderr(e){return this.#e.stderr(e)}liveHandles(){return[...this.#t]}},Kr=class{registry;outputStorage;pipeline;slots;lifecycle;dispatchDeps;clock;#e;#t;#n;#r=!1;constructor(e){this.clock=e.clock??(()=>Date.now()),this.#e=e.logger??zr,this.#t=e.createStorage??(e=>e===void 0||e.length===0?new He:Fr(e)),this.#n=e.createOutputStorage??(e=>e===void 0||e.length===0?new Gn:new qn(e));let t=new He,n=ze(t,{clock:this.clock,logger:this.#e}),r=new kr(t,{now:this.clock,logger:this.#e});this.registry=new Hr(n),this.outputStorage=new Ur(new Gn),this.pipeline=new Wr(r),this.lifecycle=new Gr((e.createLifecycle??(()=>new Te({logger:this.#e})))()),this.slots=new Ue(e.concurrency??B.dispatchConcurrency),this.dispatchDeps={taskRegistry:this.registry,lifecycle:this.lifecycle,slots:this.slots,outputStorage:this.outputStorage,clock:this.clock,logger:this.#e},this.registry.onTransition(this.#i)}#i=(e,t)=>{this.#r||Ir.has(e.status)&&this.pipeline.notifyIdle(t.subscriptionId,e.id).catch(t=>{this.#e.warn(`terminal notification wake for task `+e.id+` failed: `+Z(t))})};async bindSession(e,t){let n,r;try{n=this.#t(e),r=this.#n(e)}catch(n){return this.#a(`background runtime: session storage for `+Br(e)+` is unavailable: `+Z(n),t),[]}let i=ze(n,{clock:this.clock,logger:this.#e}),a=new kr(n,{now:this.clock,logger:this.#e});this.registry.setCurrent(i),this.outputStorage.setCurrent(r),this.pipeline.setCurrent(a),this.#r=!1;try{return await i.reconcileLostTasks()}catch(e){return this.#a(`background runtime: startup reconcile failed; stale running tasks were not all marked lost (reconcile is not per-record tolerant yet): `+Z(e),t),[]}finally{this.registry.onTransition(this.#i)}}async drainNotifications(e){let t;try{t=await this.registry.query({limit:Rr})}catch(t){return this.#e.warn(`drainNotifications: could not list tasks for subscriber `+e+`: `+Z(t)),{items:[],acks:[]}}let n=[],r=[],i=this.pipeline.current();for(let a of t){if(a.spawnSource.callerId!==e)continue;let t;try{t=await i.drainPending(e,a.id)}catch(t){this.#e.warn(`drainNotifications: task `+a.id+` for subscriber `+e+`: `+Z(t));continue}for(let e of t)n.push({event:e,record:a});let o=t[t.length-1];o!==void 0&&r.push({subscriberId:e,taskId:a.id,cursor:o.eventId,sink:i})}return{items:n,acks:r}}async acknowledgeNotifications(e){for(let t of e)try{await t.sink.acknowledgeEvents(t.subscriberId,t.taskId,t.cursor)}catch(e){this.#e.warn(`acknowledgeNotifications: could not acknowledge task `+t.taskId+`: `+Z(e))}}async shutdown(e){if(this.#r)return[];this.#r=!0;let t;try{t=await this.registry.query({limit:Rr})}catch(e){this.#e.warn(`shutdown: could not list tasks: `+Z(e)),t=[]}let n=[];for(let r of t)if(Lr.has(r.status))try{let t=await this.registry.transition({kind:`reconcile-lost`,taskId:r.id,reason:e},{clock:this.clock,callerId:Vr(r),logger:this.#e});n.push(t.record)}catch(e){this.#e.warn(`shutdown: could not mark task `+r.id+` lost: `+Z(e))}for(let e of this.lifecycle.liveHandles()){let t=Qe(this.lifecycle,e,{isDone:()=>!this.lifecycle.isLive(e),unref:!0});this.lifecycle.trackReapCancel(e,t)}for(let e of n)this.slots.release(e.id);return n}#a(e,t){if(this.#e.warn(e),t!==void 0)try{t(e)}catch(e){this.#e.warn(`bindSession reporter threw: `+Z(e))}}};function qr(e={}){return new Kr(e)}const Jr=/* @__PURE__ */ new Set([`succeeded`,`failed`,`lost`]);function Yr(e){return Jr.has(e.status)}function Xr(e,t){if(e.length===0)return``;let n=e.map(Qr).join(`
37
+ `);return`${`<bg-task-notifications batch-id="${Q(t.batchId)}" delivered-at-ms="${t.deliveredAtMs}">`}\n${n}\n</bg-task-notifications>`}function Zr(e,t=102400){let n=[],r=[],i=0,a=t-ei;for(let t of e){let e=$r(t);r.length>0&&i+1+e>a&&(n.push(r),r=[],i=0),r.length>0&&(i+=1),r.push(t),i+=e}return r.length>0&&n.push(r),n}function Qr(e){let{event:t,record:n}=e,r=t.outputBytes??n.outputBytes,i=r??0,a=t.outputPreview??n.outputPreview,o=[` <bg-task-notification`,` id="${Q(t.type)}"`,` task-id="${Q(t.taskId)}"`,` subscription-id="${Q(t.subscriptionId)}"`,` status="${Q(t.status)}"`,` label="${Q(n.label)}"`,` agent-name="${Q(n.agentName)}"`,` depth="${n.depth}"`,` duration-ms="${n.durationMs??0}"`,` transition-at-ms="${t.transitionAtMs}">`,` <output-bytes>${i}</output-bytes>`];return n.outputRef!==void 0&&o.push(` <output-ref>${Q(n.outputRef)}</output-ref>`),r!==void 0&&r<=2048&&a!==void 0&&o.push(` <output-preview>${Q(a)}</output-preview>`),o.push(` </bg-task-notification>`),o.join(`
38
+ `)}function Q(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`<`,`&lt;`).replaceAll(`"`,`&quot;`).replaceAll(` `,`&#9;`).replaceAll(`
39
+ `,`&#10;`).replaceAll(`\r`,`&#13;`)}function $r(e){return Buffer.byteLength(Qr(e),`utf8`)}const ei=Buffer.byteLength(`<bg-task-notifications batch-id="${`0`.repeat(26)}" delivered-at-ms="${`9`.repeat(13)}">\n\n</bg-task-notifications>`,`utf8`),ti=[`ptc_run_code`,`ptc_workflow`],ni=[`read`,`bash`,`edit`,`write`],ri=`ptc-mode`,ii=`ptc-mode`,ai=`ptc.json`,oi=`builtins-only`;function si(){return{enabled:!1,base:void 0,ourLoadout:void 0}}function ci(e){let t=k(e,ai),n;try{n=T(t,`utf8`)}catch{return{defaultMode:!0,source:`default`}}let r;try{r=JSON.parse(n)}catch(e){return{defaultMode:!0,source:`invalid`,error:`${t} is not valid JSON (${e instanceof Error?e.message:String(e)})`}}if(typeof r!=`object`||!r||Array.isArray(r))return{defaultMode:!0,source:`invalid`,error:`${t} must contain a JSON object`};let i=r.defaultMode;return i===void 0?{defaultMode:!0,source:`default`}:typeof i==`boolean`?{defaultMode:i,source:`file`}:{defaultMode:!0,source:`invalid`,error:`${t}: "defaultMode" must be a boolean, received ${typeof i}`}}function li(e){return[...new Set(e)]}function ui(e,t){if(e.length!==t.length)return!1;let n=[...e].sort(),r=[...t].sort();return n.every((e,t)=>e===r[t])}function di(e,t){let n=e=>ti.includes(e);if(t===`all-but-ptc`)return li(e.filter(n));let r=new Set(H);return li(e.filter(e=>n(e)||!r.has(e)))}function fi(e){let{mode:t,defaultMode:n,active:r,manual:i}=e;return t===`tui`?!n&&!i?{enter:!1,reason:`config-off`}:ti.some(e=>r.includes(e))?!i&&ni.filter(e=>!r.includes(e)).length>0?{enter:!1,reason:`restricted-session`}:{enter:!0,base:li(r),loadout:di(r,e.hide??`builtins-only`)}:{enter:!1,reason:`tools-unavailable`}:{enter:!1,reason:`not-tui`}}function pi(e,t){return e.enabled&&e.base!==void 0?e.base:t}function mi(e,t){return!e.enabled||e.ourLoadout===void 0?!1:!ui(e.ourLoadout,t)}function hi(e,t){let n=t.length>0&&t.every(e=>ti.includes(e));return e?.enabled===!0&&n&&e.base!==void 0?{base:e.base,restoreFirst:!0}:{base:t,restoreFirst:!1}}function gi(e,t){let n=t===`all-but-ptc`?`Every built-in tool is hidden from you, and so are other extensions' tools.`:`The built-in tools (read, bash, edit, write, grep, find, ls) are hidden from you.`,r=e.map(e=>`tools.${e}(args)`),i=e.slice(0,2).map(e=>e===`read`?`await tools.read({ path: "src/index.ts" })`:e===`bash`?`await tools.bash({ command: "pnpm test" })`:`await tools.${e}({ /* … */ })`).join(`
29
40
  `);return[`[PTC MODE ACTIVE]`,`This session runs in PTC mode. `+n,``,`Work by writing one TypeScript program per task and running it:`,` - ptc_run_code for a plain program (its return value and console.log come back to you)`,` - ptc_workflow when the task has named phases and you want narration (log / phase /`,` parallel / pipeline are available there)`,``,`The program body is an async function. Available inside it: ${r.join(`, `)}.`,i.length>0?`Examples:\n${i}`:``,``,`Batch a task's steps into ONE program instead of issuing many tool calls — gathering`,`intermediate output yourself and returning only what matters is the point of this mode.`,`Image-bearing tool results inside a program are attached to you after the run — never return`,`base64 image data as your program's value.`,`If the PTC surfaces cannot express what you need, say so and the user can run /ptc off.`].filter(e=>e.length>0).join(`
30
- `)}const cn=[`read`,`bash`],ln=`Use the read tool to load a skill's file when the task matches its description.`,un=`Use tools.read({ path }) inside a ptc_run_code program to load a skill's file when the task matches its description.`;function dn(e){return!cn.some(t=>e.includes(t))}function fn(e,t=v){let n=t([...e],`read`).trim();return n.length===0?``:n.includes(ln)?n.replace(ln,un):`${un}\n\n${n}`}const $=Xt;function pn(e,t){return[`PTC mode on — ${t===`all-but-ptc`?`all other tools`:`built-in tools (now reachable only as tools.<name> inside a program)`} hidden.`,`Bindings available inside a program: ${W(e).join(`, `)||`(none)`}.`,`Run /ptc off to restore the normal tool set.`].join(`
31
- `)}function mn(e){let t=Zt(),n=!1,r=new Kt,i=()=>rn(t,e.getActiveTools()),a=Xe();e.registerTool(zt({getBindingSourceNames:i,getPool:()=>r.get(`run_code`),depth:a})),e.registerTool(Gt({getBindingSourceNames:i,getPool:()=>r.get(`workflow`),depth:a})),e.on(`turn_end`,async()=>{let e=r;r=new Kt,await e.drain()});let o=e=>{e.ui.setStatus(Jt,t.enabled?e.ui.theme.fg(`accent`,`PTC`):void 0)},s=(r,i,a,s)=>{t.enabled=!0,t.base=r,t.ourLoadout=i,e.setActiveTools([...i]),n=!0,e.appendEntry(Q,{enabled:!0,base:[...r]}),o(a),a.ui.notify(s,`info`)},c=(r,i)=>{let a=t.base;t.enabled=!1,t.base=void 0,t.ourLoadout=void 0,n=!1,a!==void 0&&e.setActiveTools([...a]),e.appendEntry(i.customType,{enabled:!1}),o(r),i.announce&&r.ui.notify(`PTC mode off — built-in tools are directly callable again.`,`info`)},l=e=>{let t=e.sessionManager.getEntries();for(let e=t.length-1;e>=0;--e){let n=t[e];if(n?.type!==`custom`||n.customType!==`ptc-mode`)continue;let r=n.data,i=Array.isArray(r?.base)?r.base.filter(e=>typeof e==`string`):void 0;return{enabled:r?.enabled===!0,...i===void 0?{}:{base:i}}}};e.registerCommand(`ptc`,{description:`Show or toggle PTC mode (built-in tools reachable only from inside a PTC program)`,handler:async(n,r)=>{let i=n.trim().toLowerCase();if(i===`off`){if(!t.enabled){r.ui.notify(`PTC mode is already off.`,`info`);return}c(r,{announce:!0,customType:Q});return}if(i!==`on`){r.ui.notify(t.enabled?`PTC mode is ON — run /ptc off to restore built-in tools.\nBindings: ${W(t.base).join(`, `)||`(none)`}`:`PTC mode is OFF — run /ptc on to enter it.`,`info`);return}if(t.enabled){r.ui.notify(`PTC mode is already on.`,`info`);return}let a=nn({mode:r.mode,defaultMode:!0,active:e.getActiveTools(),manual:!0,hide:$});if(!a.enter){r.ui.notify(a.reason===`tools-unavailable`?`Cannot enter PTC mode: ptc_run_code / ptc_workflow are not active in this session.`:`Cannot enter PTC mode in this run mode.`,`warning`);return}s(a.base,a.loadout,r,pn(a.base,$))}}),e.on(`session_start`,async(t,n)=>{let r=Qt(y());r.error!==void 0&&n.ui.notify(`pi-ptc-subagents: ${r.error}`,`warning`);let i=l(n),{base:a,restoreFirst:c}=on(i,e.getActiveTools());c&&e.setActiveTools([...a]);let u=nn({mode:n.mode,defaultMode:r.defaultMode,active:a,manual:!1,hide:$}),d=i?.enabled??u.enter;if(u.enter&&d){s(a,u.loadout,n,pn(a,$));return}o(n),!u.enter&&u.reason!==`not-tui`&&i===void 0&&u.reason===`restricted-session`&&n.ui.notify(`PTC mode skipped: this session was launched with an explicit tool restriction.`,`info`)});let u=!1,d=(e,n)=>{if(e===void 0)return;if(!t.enabled||!n){u&&=(delete e.sections.skills,!1);return}let r=fn(e.skills);r.length!==0&&(e.sections.skills=r,u=!0)};e.on(`before_agent_start`,async(r,i)=>{let a=r.systemPromptOptions;if(d(a,dn(e.getActiveTools())),t.enabled){if(an(t,e.getActiveTools())){c(i,{announce:!1,customType:Q}),d(a,dn(e.getActiveTools())),i.ui.notify(`PTC mode off — another extension changed the active tool set.`,`warning`);return}if(n)return n=!1,{message:{customType:`ptc-mode-briefing`,content:sn(W(t.base),$),display:!1}}}})}export{L as BUILTIN_BINDING_NAMES,be as DEFAULT_BINDING_NAMES,R as DEFAULT_CONFIG,Xt as DEFAULT_HIDE_STRATEGY,o as HOST_FRAME_KIND,ze as MAX_LINE_CHARS,qt as MODE_REQUIRED_TOOL_NAMES,n as PTC_ERROR_KIND,i as PTC_LOG_LEVEL,Yt as PTC_MODE_CONFIG_FILE,Q as PTC_MODE_ENTRY_TYPE,Jt as PTC_MODE_STATUS_KEY,Z as PTC_MODE_TOOL_NAMES,un as PTC_SKILL_LOAD_INSTRUCTION,cn as SKILL_READING_TOOL_NAMES,Kt as TurnPools,Ce as WORKER_ENV_ALLOW_LIST,t as WORKER_FRAME_KIND,ke as WorkerPool,rn as bindingSource,sn as buildModeInstruction,fn as buildPtcSkillsSection,Se as createBuiltinBindings,we as createWorkerEnv,nn as decideModeEntry,mn as default,an as detectExternalLoadoutChange,Ee as effectiveTimeoutMs,Zt as initialModeState,tn as modeLoadout,Qt as readDefaultModeConfig,Ye as renderModelValue,on as resolveBaseOnStart,Te as resolveConfig,Re as runPtcProgram,en as sameToolSet,H as sanitizeText,dn as skillsSectionDropped,Ve as stripAnsi};
41
+ `)}const _i=[`read`,`bash`],vi=`Use the read tool to load a skill's file when the task matches its description.`,yi=`Use tools.read({ path }) inside a ptc_run_code program to load a skill's file when the task matches its description.`;function bi(e){return!_i.some(t=>e.includes(t))}function xi(e,t=v){let n=t([...e],`read`).trim();return n.length===0?``:n.includes(vi)?n.replace(vi,yi):`${yi}\n\n${n}`}const $=oi;function Si(e,t){return[`PTC mode on — ${t===`all-but-ptc`?`all other tools`:`built-in tools (now reachable only as tools.<name> inside a program)`} hidden.`,`Bindings available inside a program: ${q(e).join(`, `)||`(none)`}.`,`Run /ptc off to restore the normal tool set.`].join(`
42
+ `)}function Ci(e,t={}){let n=si(),r=!1,i=new Dr,a=()=>pi(n,e.getActiveTools()),o=Gt(),s=Kt(),c=t.backgroundRuntime??qr(),l=!1,u=e=>{console.warn(`[pi-ptc.background] `+e)},d=t=>{if(t.length!==0){if(l){e.sendMessage({customType:`bg-task-notification`,content:t,display:!1},{deliverAs:`steer`});return}e.sendUserMessage(t)}},f=async e=>{let{items:t,acks:n}=await c.drainNotifications(e),r=t.filter(e=>Yr(e.record));for(let e of Zr(r))d(Xr(e,{batchId:_e(),deliveredAtMs:c.clock()}));await c.acknowledgeNotifications(n)},p=async()=>{let e=await c.registry.query({limit:2**53-1}),t=/* @__PURE__ */ new Set;for(let n of e){let e=n.spawnSource.callerId;e.length>0&&t.add(e)}return[...t]},m=async()=>{for(let e of await p())await f(e)},h=async e=>{let t=/* @__PURE__ */ new Set;for(let n of e){let e=n.spawnSource.callerId;e.length>0&&t.add(e)}for(let e of t)await f(e)};c.pipeline.onIdleWake(e=>{f(e).catch(e=>{u(`notification delivery failed: `+(e instanceof Error?e.message:String(e)))})}),e.registerTool(Rn({getBindingSourceNames:a,getPool:()=>i.get(`run_code`),depth:o,...s===void 0?{}:{parentTaskId:s},getDispatchDeps:()=>c.dispatchDeps})),e.registerTool(Wn({getBindingSourceNames:a,getPool:()=>i.get(`workflow`),depth:o,...s===void 0?{}:{parentTaskId:s},getDispatchDeps:()=>c.dispatchDeps})),e.registerTool(xr(c.registry)),e.registerTool(wr(c.registry,c.outputStorage)),e.registerTool(Er(c.registry,c.lifecycle,{clock:c.clock})),e.on(`turn_start`,async()=>{l=!0}),e.on(`agent_settled`,async()=>{l=!1;try{await m()}catch(e){u(`agent_settled delivery failed: `+(e instanceof Error?e.message:String(e)))}}),e.on(`turn_end`,async()=>{let e=i;i=new Dr,await e.drain()});let g=e=>{e.ui.setStatus(ii,n.enabled?e.ui.theme.fg(`accent`,`PTC`):void 0)},_=(t,i,a,o)=>{n.enabled=!0,n.base=t,n.ourLoadout=i,e.setActiveTools([...i]),r=!0,e.appendEntry(ri,{enabled:!0,base:[...t]}),g(a),a.ui.notify(o,`info`)},v=(t,i)=>{let a=n.base;n.enabled=!1,n.base=void 0,n.ourLoadout=void 0,r=!1,a!==void 0&&e.setActiveTools([...a]),e.appendEntry(i.customType,{enabled:!1}),g(t),i.announce&&t.ui.notify(`PTC mode off — built-in tools are directly callable again.`,`info`)},b=e=>{let t=e.sessionManager.getEntries();for(let e=t.length-1;e>=0;--e){let n=t[e];if(n?.type!==`custom`||n.customType!==`ptc-mode`)continue;let r=n.data,i=Array.isArray(r?.base)?r.base.filter(e=>typeof e==`string`):void 0;return{enabled:r?.enabled===!0,...i===void 0?{}:{base:i}}}};e.registerCommand(`ptc`,{description:`Show or toggle PTC mode (built-in tools reachable only from inside a PTC program)`,handler:async(t,r)=>{let i=t.trim().toLowerCase();if(i===`off`){if(!n.enabled){r.ui.notify(`PTC mode is already off.`,`info`);return}v(r,{announce:!0,customType:ri});return}if(i!==`on`){r.ui.notify(n.enabled?`PTC mode is ON — run /ptc off to restore built-in tools.\nBindings: ${q(n.base).join(`, `)||`(none)`}`:`PTC mode is OFF — run /ptc on to enter it.`,`info`);return}if(n.enabled){r.ui.notify(`PTC mode is already on.`,`info`);return}let a=fi({mode:r.mode,defaultMode:!0,active:e.getActiveTools(),manual:!0,hide:$});if(!a.enter){r.ui.notify(a.reason===`tools-unavailable`?`Cannot enter PTC mode: ptc_run_code / ptc_workflow are not active in this session.`:`Cannot enter PTC mode in this run mode.`,`warning`);return}_(a.base,a.loadout,r,Si(a.base,$))}}),e.on(`session_start`,async(t,n)=>{l=!1;let r=await c.bindSession(n.sessionManager?.getSessionDir?.(),e=>n.ui.notify(e,`warning`));try{await h(r)}catch(e){u(`startup reconcile delivery failed: `+(e instanceof Error?e.message:String(e)))}let i=ci(y());i.error!==void 0&&n.ui.notify(`pi-ptc-subagents: ${i.error}`,`warning`);let a=b(n),{base:o,restoreFirst:s}=hi(a,e.getActiveTools());s&&e.setActiveTools([...o]);let d=fi({mode:n.mode,defaultMode:i.defaultMode,active:o,manual:!1,hide:$}),f=a?.enabled??d.enter;if(d.enter&&f){_(o,d.loadout,n,Si(o,$));return}g(n),!d.enter&&d.reason!==`not-tui`&&a===void 0&&d.reason===`restricted-session`&&n.ui.notify(`PTC mode skipped: this session was launched with an explicit tool restriction.`,`info`)}),e.on(`session_shutdown`,async()=>{l=!1;try{await c.shutdown(`session_ended_while_running`)}catch(e){u(`session shutdown failed: `+(e instanceof Error?e.message:String(e)))}});let x=!1,S=(e,t)=>{if(e===void 0)return;if(!n.enabled||!t){x&&=(delete e.sections.skills,!1);return}let r=xi(e.skills);r.length!==0&&(e.sections.skills=r,x=!0)};e.on(`before_agent_start`,async(t,i)=>{let a=t.systemPromptOptions;if(S(a,bi(e.getActiveTools())),n.enabled){if(mi(n,e.getActiveTools())){v(i,{announce:!1,customType:ri}),S(a,bi(e.getActiveTools())),i.ui.notify(`PTC mode off — another extension changed the active tool set.`,`warning`);return}if(r)return r=!1,{message:{customType:`ptc-mode-briefing`,content:gi(q(n.base),$),display:!1}}}})}export{H as BUILTIN_BINDING_NAMES,pt as DEFAULT_BINDING_NAMES,B as DEFAULT_CONFIG,oi as DEFAULT_HIDE_STRATEGY,o as HOST_FRAME_KIND,Nt as MAX_LINE_CHARS,ni as MODE_REQUIRED_TOOL_NAMES,n as PTC_ERROR_KIND,i as PTC_LOG_LEVEL,ai as PTC_MODE_CONFIG_FILE,ri as PTC_MODE_ENTRY_TYPE,ii as PTC_MODE_STATUS_KEY,ti as PTC_MODE_TOOL_NAMES,yi as PTC_SKILL_LOAD_INSTRUCTION,_i as SKILL_READING_TOOL_NAMES,Dr as TurnPools,Ee as WORKER_ENV_ALLOW_LIST,t as WORKER_FRAME_KIND,vt as WorkerPool,pi as bindingSource,gi as buildModeInstruction,xi as buildPtcSkillsSection,ht as createBuiltinBindings,De as createWorkerEnv,fi as decideModeEntry,Ci as default,mi as detectExternalLoadoutChange,ke as effectiveTimeoutMs,si as initialModeState,di as modeLoadout,ci as readDefaultModeConfig,Wt as renderModelValue,hi as resolveBaseOnStart,Oe as resolveConfig,Ot as runPtcProgram,ui as sameToolSet,K as sanitizeText,bi as skillsSectionDropped,Ft as stripAnsi};
32
43
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ptc-subagents",
3
- "version": "0.1.3",
3
+ "version": "1.0.0",
4
4
  "description": "DSH PTC mode (ptc_run_code + ptc_workflow) for pi",
5
5
  "keywords": [
6
6
  "pi",