pi-ptc-subagents 1.6.0 → 2.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,9 +5,112 @@ 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
- ## [Unreleased]
8
+ ## [2.0.0] - 2026-10-10
9
9
 
10
- (nothing yet)
10
+ ### Added
11
+
12
+ - **`ptc_run_code` and `ptc_workflow` reach the line where pi's `codemode` orchestrates.** A
13
+ session on the `subagents` surface used to hold `ptc_subagent` and the lifecycle face with no
14
+ program behind them: pi's `codemode` was composing tool calls, and the one capability its
15
+ QuickJS sandbox cannot provide — spawning a process — was unreachable, because `pi.dispatch`,
16
+ background tasks and the frozen six-name environment all live inside the PTC worker. Both tools
17
+ are now registered there at `codemode` reach, which makes them **callable from a `codemode`
18
+ script without being declared to the model**: `AgentSession._isDeclarable` admits `direct` and
19
+ `model-only` only, so the request pi sends still carries exactly one orchestrator and it is
20
+ pi's. Verified end to end on a real pi 1.1.0 session — a `codemode` script enumerates all six
21
+ `ptc_*` tools in `ALL_TOOLS` and successfully calls `await tools.ptc_run_code({ code: … })`,
22
+ while the payload carries only the subagent face
23
+ ([ADR-0025](./docs/adr/0025-extension-surface-is-a-setting.md) §3 as amended).
24
+
25
+ **A pi older than 0.99.0 is unaffected.** `ToolExposure` does not exist there — `grep -r
26
+ exposure` over `pi-coding-agent@0.86.1`'s `dist/` returns nothing, and so does
27
+ `AgentSession._getCallableTools` — so the field is ignored and the tools are declared to the
28
+ model, which is correct: such a pi ships no `codemode`, resolves to `full` anyway, and has
29
+ nothing that could call a `codemode`-reach tool. Measured across 0.86.1 / 0.87.1 / 0.99.0 /
30
+ 0.99.1 / 0.99.2 / 1.0.0 / 1.0.4 / 1.1.0.
31
+
32
+ **Naming either tool in `--tools` or `defaultTools` overrides this.** A tool named there is
33
+ recorded in pi's `toolsAdded`, which the prompt projection filters only against `hidden` and
34
+ never against `codemode` — so it is declared to the model regardless of its exposure. That is
35
+ a user's explicit choice and is left alone; it is recorded here because the exposure field does
36
+ not survive it.
37
+
38
+ ### Changed
39
+
40
+ - **Surface detection asks pi for the loadout instead of reconstructing it.** The third question —
41
+ will `codemode` be **active**, i.e. callable by the model — used to be answered by parsing
42
+ `--tools` / `--exclude-tools` / `--no-tools` / `--no-builtin-tools` out of `process.argv`,
43
+ merging `defaultTools` across the user-scope and project-scope settings files, and replaying pi's
44
+ precedence over the result. That is deleted. At `session_start` — the first moment pi's runtime is
45
+ bound — `getActiveTools()` answers directly, and pi has already applied both the command line and
46
+ its project-trust decision to what it returns. Registration moves to the same event, where pi
47
+ adds a tool from it and refreshes its own registry; the command-line reader, the `defaultTools`
48
+ merge, the precedence replay and the differential test that held them honest all go with it
49
+ ([ADR-0035](./docs/adr/0035-ask-pi-for-the-loadout.md)).
50
+
51
+ **What this fixes.** A project's `defaultTools` was read off disk with no trust check, while pi
52
+ drops project settings for any project it has not been told to trust. An untrusted project could
53
+ therefore name `codemode` and put the session on the `subagents` surface — whose
54
+ `ptc_run_code` and `ptc_workflow` are registered at `codemode` reach, so with no `codemode`
55
+ running they are reachable from nowhere (#131). A trusted project that configures `codemode` that
56
+ way keeps the `subagents` surface, exactly as before.
57
+
58
+ **What does not change.** The MCP auto-enable evidence stays a file-side read, because pi
59
+ activates `codemode` from inside its own `session_start` handler and extension order is not ours
60
+ to choose — measured across pi 0.86.1 → 1.1.0, a read from ours is too early to see it, and the
61
+ existing cross-check catches it when it lands. The **codemode switch** axis also stays as it is;
62
+ it may also be collapsible into the live registry, but that loses the distinction between "not
63
+ shipped" and "not loaded", and it is a separate decision. Behaviour is otherwise unchanged on
64
+ every pi this package supports, verified from 0.86.1 upward under both trust decisions.
65
+
66
+ - **BREAKING: the `surfaceMode` setting is removed, and the surface is detected only.** Which
67
+ model-facing tools this package registers is now a pure function of what pi is — three probes
68
+ (does this pi ship `codemode`, will pi load it, can the model call it), with `full` as the
69
+ fallback for every case a probe cannot answer
70
+ ([ADR-0034](./docs/adr/0034-surface-is-detected-not-set.md)).
71
+
72
+ **What is gone:**
73
+
74
+ - the `surfaceMode` key in `~/.pi/agent/ptc.json` (`off` / `subagents` / `full`);
75
+ - the `off` value — a session that wants none of this package's tools no longer has a way to say
76
+ so here;
77
+ - the `/ptc surface [off|subagents|full]` subcommand. **`/ptc off` is unaffected** — it turns PTC
78
+ _mode_ off, which was never the same thing as the extension surface;
79
+ - the `surfaceModeConflict` notice about a pinned value disagreeing with detection.
80
+
81
+ **Migrating: use pi, not this package.** Run `pi config` and disable this package's extensions
82
+ there — measured on pi 1.1.0 against this package's own `dist/index.js`, a `packages` entry whose
83
+ `extensions` is `[]` or `["!dist/index.js"]` is not loaded at all, while `["+dist/index.js"]` and
84
+ an omitted key are. This is strictly better than any value of a key this package reads, because
85
+ pi honours it **without loading the extension**: none of this package runs, rather than running
86
+ and then deciding to register nothing. In a project-level `.pi/settings.json` write
87
+ `["!dist/index.js"]` rather than `[]` — pi resolves the two scopes with two different functions
88
+ (`package-manager.js:1850`), where an empty project delta means "no change".
89
+
90
+ **Expect one warning on upgrade if you had the key set.** A `ptc.json` carrying `surfaceMode` is
91
+ reported at session start and the key is **not** honoured — deliberately, including for `"off"`,
92
+ which is why this package comes back after being switched off. The warning names the file, quotes
93
+ the value, and points at `pi config`; deleting the key silences it.
94
+
95
+ - **ADR-0025 decision 3 is amended.** `subagents` does register the programming pair now. What
96
+ the decision protected — the model being offered one orchestration surface, not two — is
97
+ unchanged and is now enforced by exposure rather than by an absent tool. `CONTEXT.md`'s
98
+ _orchestration surface_ and _subagent surface_ entries and the README's surface list say so
99
+ too.
100
+
101
+ - **The `codemode` activation probe now reads every CLI flag that can remove the tool.** It reads
102
+ `--exclude-tools` / `-xt` and `--no-tools` / `-nt` in addition to `--tools` / `-t`, and it applies
103
+ them in pi's own order: `-t` decides the list and beats `-nt`, while `-xt` vetoes whatever list
104
+ won. That last one was a real misreading — pi builds the active set as
105
+ `(tools ?? configured).filter(name => !excluded.has(name))` (`sdk.js:148`), so **exclusion beats
106
+ inclusion**, and `-t codemode -xt codemode` was previously read as `active`. The direction was the
107
+ harmful one: under `subagents`, `ptc_run_code` / `ptc_workflow` are at `codemode` reach, so an
108
+ orchestrator believed to be present but absent is an orchestrator nobody can call.
109
+ `--no-builtin-tools` is deliberately still not read: it maps to `noTools: "builtin"` and
110
+ `codemode` is not one of pi's built-in tools, so reading it would be a guess about a flag that
111
+ cannot move it.
112
+
113
+ [2.0.0]: https://github.com/a1121611810/pi-ptc-subagents/compare/v1.6.0...v2.0.0
11
114
 
12
115
  ## [1.6.0] - 2026-10-08
13
116
 
@@ -34,7 +137,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
34
137
  session, and only on a detected surface: if nothing in `settings.json` or a readable `mcp.json`
35
138
  said codemode would be active, the surface defaulted to `full`, and pi's real tool loadout
36
139
  contains `codemode` anyway, the session is now told it is carrying two orchestration surfaces and
37
- which `surfaceMode` key picks one. Checked at session start and again on the first turn, because
140
+ which setting picks one. Checked at session start and again on the first turn, because
38
141
  pi's MCP extension may activate codemode after this package's own `session_start` runs. It is
39
142
  `TUI`-only, like every other notice in this package.
40
143
  - **A named report for an `mcp.json` this package could not read.** A file that is absent is
package/README.md CHANGED
@@ -225,37 +225,40 @@ the session. The mode's rationale and rejected alternatives are in [ADR-0010](./
225
225
  { "defaultMode": false }
226
226
  ```
227
227
 
228
- **Choosing the surface.** `defaultMode` decides whether the session _enters_ PTC mode; `surfaceMode`
229
- decides which model-facing tools this package registers at all ([ADR-0025](./docs/adr/0025-extension-surface-is-a-setting.md)):
228
+ **Choosing the surface.** `defaultMode` decides whether the session _enters_ PTC mode. Which
229
+ model-facing tools this package registers is **detected, not configured** — there is no setting for
230
+ it ([ADR-0034](./docs/adr/0034-surface-is-detected-not-set.md)):
230
231
 
231
- ```
232
- /ptc surface # report the current surface and where it came from
233
- /ptc surface subagents # write the key and reload so it applies now
234
- ```
235
-
236
- ```jsonc
237
- // ~/.pi/agent/ptc.json
238
- { "surfaceMode": "subagents" }
239
- ```
240
-
241
- - `off` — a stock pi session: no tool, no `/ptc` command, no briefing.
242
232
  - `subagents` — `ptc_subagent` plus the three `ptc_task_*` tools, with pi's own `codemode`
243
- doing the orchestration; warns at startup when `codemode` is not in the active tool set.
244
- - `full` — today's set: `ptc_run_code` / `ptc_workflow` plus the three `ptc_task_*` tools.
245
-
246
- The command exists because the alternative is editing JSON by hand and starting a new session: the
247
- surface is read once in the extension factory and pi has no way to unregister a tool, so a change
248
- cannot apply in place. `/ptc surface` writes the key and then performs pi's own `/reload`, which
249
- re-runs every extension factory ([ADR-0030](./docs/adr/0030-surface-switch-reloads.md)). Two things
250
- it will not do silently: a malformed `ptc.json` is reported and left exactly as it was, and an
251
- unchanged value does not reload — a reload replaces every extension instance in the session, so
252
- retyping the value you already have costs you in-flight state for nothing. Switching to
253
- `subagents` or `off` also ends a running PTC mode, which the command says before it does it.
254
-
255
- **The default is detected, and it follows three questions, not one**
233
+ doing the orchestration; warns at startup when `codemode` is not in the active tool set and
234
+ `ptc_run_code` is not declared to the model either (which on a pi below 0.99.0 it is, since
235
+ `exposure` does not exist there and the pair falls back to being model-visible).
236
+ `ptc_run_code` / `ptc_workflow` are registered here too, but at `codemode` reach: a `codemode`
237
+ script can call them, and the model is not shown them. That is what puts `pi.dispatch` and
238
+ background tasks underneath `codemode`, whose sandbox cannot spawn a process itself.
239
+ - `full` — the rest: `ptc_run_code` / `ptc_workflow` plus the three `ptc_task_*` tools. Also the
240
+ answer to every "the probe could not tell" case.
241
+
242
+ **To keep this package out of your sessions, use pi, not this package.** Run `pi config` and
243
+ disable this package's extensions there. Measured on pi 1.1.0 against this package's own
244
+ `dist/index.js`: a `packages` entry whose `extensions` is `[]` or `["!dist/index.js"]` is not
245
+ loaded at all, while `["+dist/index.js"]` and an omitted key are. pi does this **without loading
246
+ the extension**, which no value of a key this package reads could achieve — reading the key
247
+ requires having run the code that reads it.
248
+
249
+ > In the **project-level** `.pi/settings.json`, write `"extensions": ["!dist/index.js"]` rather than
250
+ > `"extensions": []`. The two settings files are resolved by two different functions in pi
251
+ > (`package-manager.js:1850`): the personal-level path reads `[]` as "load nothing", the
252
+ > project-level path reads it as an empty delta, which means "no change".
253
+
254
+ **If you had `surfaceMode` in `ptc.json`, it is no longer read** — including `"off"`, so upgrading
255
+ brings this package back. At session start you get one `warning` naming the file and pointing here.
256
+ Delete the key to silence it.
257
+
258
+ **The surface is detected, and it follows three questions, not one**
256
259
  ([ADR-0026](./docs/adr/0026-surface-default-is-detected.md),
257
260
  [ADR-0027](./docs/adr/0027-codemode-switch-decides-surface.md),
258
- [ADR-0029](./docs/adr/0029-surface-follows-codemode-activation.md)). With no `surfaceMode` key:
261
+ [ADR-0029](./docs/adr/0029-surface-follows-codemode-activation.md)):
259
262
 
260
263
  | does this pi ship `codemode`? | will pi load it? | can the model call it? | surface |
261
264
  | ----------------------------- | ---------------------------------------------- | --------------------------------------------- | ----------- |
@@ -264,15 +267,15 @@ retyping the value you already have costs you in-flight state for nothing. Switc
264
267
  | yes | no (`-builtin:codemode`, or `--no-extensions`) | — | `full` |
265
268
  | no | — | — | `full` |
266
269
 
267
- The third column is the one that decides most sessions, and it is why the default is `full` on a pi
270
+ The third column is the one that decides most sessions, and it is why the answer is `full` on a pi
268
271
  that has never been configured. pi ships `codemode` and loads it by default, but registers it
269
272
  **inactive** (`defaultActive: false`) — it joins the model's tool list only when a loadout names it.
270
273
  Handing orchestration to a tool the model cannot call is the failure this avoids, so `subagents` is
271
274
  chosen only on positive evidence.
272
275
 
273
- Setting the key always wins. A probe that cannot answer falls back to `full` — the safe direction,
274
- since `subagents` as a failure mode would take away the orchestration tool the session was relying
275
- on.
276
+ A probe that cannot answer falls back to `full` — the safe direction, since `subagents` as a
277
+ failure mode would take away the orchestration tool the session was relying on. There is no key
278
+ that overrides this, by design ([ADR-0034](./docs/adr/0034-surface-is-detected-not-set.md)).
276
279
 
277
280
  > **To use the `subagents` surface, put `codemode` in your tool list.** Without that you get
278
281
  > `ptc_run_code` / `ptc_workflow` and no startup warning, which is the correct answer for a session
@@ -284,54 +287,45 @@ on.
284
287
  > ```
285
288
  >
286
289
  > or per launch, `pi --tools read,bash,edit,write,codemode`. A list made only of modifiers starts
287
- > from pi's four defaults, so `{ "defaultTools": ["+codemode"] }` means the same thing. If you would
288
- > rather pin the surface regardless of what pi is doing, set `{ "surfaceMode": "subagents" }` —
289
- > and note that pinning it does not activate `codemode`, so a pinned `subagents` on a session that
290
- > never configured it still warns, by design ([ADR-0025](./docs/adr/0025-extension-surface-is-a-setting.md)
291
- > decision 4).
290
+ > from pi's four defaults, so `{ "defaultTools": ["+codemode"] }` means the same thing.
292
291
 
293
292
  > Turning pi's `codemode` **off** — `"extensions": ["-builtin:codemode"]`, or launching with
294
293
  > `--no-extensions` — brings the PTC surfaces back on its own. Before ADR-0027 it did not: the
295
294
  > detection asked only whether the extension directory exists, so a pi told not to load it still
296
295
  > counted as an orchestrator and you got `ptc_subagent` with nothing to compose with. The switch is
297
296
  > read from the same three places pi reads it — the command line, `<cwd>/.pi/settings.json`, and
298
- > `<agentDir>/settings.json` — in the same order, and the activation probe reads the first two plus
299
- > `--tools`.
300
-
301
- No file, or a value outside that set, falls back to the detected default and says so at startup
302
- rather than half-applying: which tools exist is not something to change on a guess.
303
-
304
- **A detection you cannot see is the failure this design has**, so the result is reported. With no
305
- `surfaceMode` key, the outcome is issued through the TUI notification channel at session start —
306
- how the probe came out and which surface the default therefore is — but only when the probe could
307
- not answer. A pi that ships `codemode`, loads it, and has it in the tool list is the expected case
308
- and says nothing. A second notice is issued when the probe and pi's own tool registry disagree, which
309
- is the case the probe structurally cannot see: it walks the filesystem, so under
310
- `--exclude-tools codemode` it answers `present` for a tool this session does not have (and the
311
- mirror: a restructured `dist` answers `not-found` for one pi plainly registers). A third notice
312
- covers ADR-0027: a settings file that could not be read, and an explicit `surfaceMode` that
313
- disagrees with the table — the pinned value still wins, and the notice only says so. What is **not**
314
- established is that either line actually paints in a real pi TUI: a pty capture at review time
315
- showed neither the notice nor a control marker, and a TUI quits on stdin EOF before a toast
316
- renders, so that is an unmeasured end to end rather than a broken one. No test in this repository
317
- observes a notice through a real TUI. If you are relying on the notice rather than on your own
318
- `surfaceMode` key, verify it once.
297
+ > `<agentDir>/settings.json` — in the same order. The activation probe does **not** read the
298
+ > settings at all: it asks pi for its own tool loadout at session start, by which time pi has
299
+ > already applied the command line _and_ its project-trust decision. That matters for a project
300
+ > you have not approved — its `defaultTools` is not evidence of anything, because pi is not
301
+ > reading it either ([ADR-0035](./docs/adr/0035-ask-pi-for-the-loadout.md)).
302
+
303
+ **A detection you cannot see is the failure this design has**, so the result is reported. The
304
+ outcome is issued through the TUI notification channel at session start — how the probe came out
305
+ and which surface therefore is — but only when the probe could not answer. A pi that ships
306
+ `codemode`, loads it, and has it in the tool list is the expected case and says nothing. A second
307
+ notice is issued when the probe and pi's own tool registry disagree, which is the case the probe
308
+ structurally cannot see: it walks the filesystem, so under `--exclude-tools codemode` it answers
309
+ `present` for a tool this session does not have (and the mirror: a restructured `dist` answers
310
+ `not-found` for one pi plainly registers). A third notice covers ADR-0027: a settings file that
311
+ could not be read. What is **not** established is that either line actually paints in a real pi
312
+ TUI: a pty capture at review time showed neither the notice nor a control marker, and a TUI quits
313
+ on stdin EOF before a toast renders, so that is an unmeasured end to end rather than a broken one.
314
+ No test in this repository observes a notice through a real TUI.
319
315
 
320
316
  **On a `--print` session, none of it prints.** `ui.notify` is the TUI channel; measured across
321
317
  three `--print` runs that each emit one of these notices, stdout and stderr received **0 bytes**
322
318
  each. That makes this page the only channel on which a `--print` user learns why they got the
323
- surface they got — ADR-0025's decision-4 warning shares the gap, and there the answer is the same
324
- one line of JSON: set `surfaceMode` yourself and the detection no longer matters.
319
+ surface they got.
325
320
 
326
321
  **Where it does not run.** Print / JSON / RPC sessions are left exactly as launched, and so is a
327
322
  session started with an explicit tool restriction (`--tools`, `--exclude-tools`,
328
323
  `--no-builtin-tools`, `--no-extensions`) — the extension does not override what you asked for.
329
324
  `--no-extensions` does, however, change the **detected surface**: pi's own `codemode` is a built-in
330
- extension, so turning extensions off means it will not load, and ADR-0027's table resolves the
331
- default to `full` — you keep `ptc_run_code` / `ptc_workflow` rather than a `ptc_subagent` with
332
- nothing to compose with. If you would rather pin the surface regardless, set `"surfaceMode"` in
333
- `ptc.json`. If another extension changes the tool set while the mode is on, the mode yields and
334
- tells you.
325
+ extension, so turning extensions off means it will not load, and ADR-0027's table resolves to
326
+ `full` — you keep `ptc_run_code` / `ptc_workflow` rather than a `ptc_subagent` with
327
+ nothing to compose with. If another extension changes the tool set while the mode is on, the mode
328
+ yields and tells you.
335
329
 
336
330
  **The important consequence:** in a TUI session, bindings come from the loadout recorded _before_
337
331
  the mode narrowed it. That is what keeps `tools.read(…)` working — and it is why a `--tools`
package/dist/index.d.ts CHANGED
@@ -662,14 +662,16 @@ interface DefaultModeConfig {
662
662
  export declare function readDefaultModeConfig(agentDir: string): DefaultModeConfig;
663
663
  /**
664
664
  * Which model-facing tools this package registers, independent of PTC mode (which decides
665
- * which registered tools are *active*). Read from the same agent-dir file as
666
- * {@link readDefaultModeConfig}, beside the `defaultMode` key. ADR-0025.
665
+ * which registered tools are *active*).
667
666
  *
668
- * The order below is the order of increasing responsibility: `off` hands the whole
669
- * orchestration question back to pi, `subagents` keeps only the subagent face and lets pi's
670
- * `codemode` orchestrate, `full` keeps today's set.
667
+ * These are DETECTED values, not settings. The list is two long because detection is the only
668
+ * thing that produces them: `subagents` keeps the subagent face and lets pi's `codemode`
669
+ * orchestrate, `full` keeps the whole set. There was a third, `off`, and a `surfaceMode` key
670
+ * that could pin any of the three (ADR-0025); both are gone, and
671
+ * {@link detectSurfaceMode} is now a pure function of what pi is — see its doc for why the
672
+ * escape hatch the setting provided is not one this package can replace.
671
673
  */
672
- export declare const SURFACE_MODES: readonly ["off", "subagents", "full"];
674
+ declare const SURFACE_MODES: readonly ["subagents", "full"];
673
675
  type SurfaceMode = (typeof SURFACE_MODES)[number];
674
676
  /**
675
677
  * Whether the pi that launched us will actually LOAD its `codemode` extension.
@@ -732,10 +734,14 @@ export declare function readCodemodeSwitch(agentDir: string, cwd: string, argv?:
732
734
  type CodemodeActivation = "active" | "inactive";
733
735
  /**
734
736
  * How the answer was decided, so a test can tell a configured answer from pi's default.
735
- * `"mcp"` is ADR-0033: the loadout mirror said `inactive` and the MCP auto-enable evidence
736
- * said pi will activate `codemode` anyway.
737
+ *
738
+ * `"loadout"` is pi's own live tool set — the answer is read, not reconstructed, so there is no
739
+ * provenance left to name: whatever put `codemode` there is pi's business and not something this
740
+ * package can see. `"mcp"` is ADR-0033: the live loadout said `inactive` and the MCP auto-enable
741
+ * evidence said pi will activate `codemode` anyway, because pi's MCP extension does that from its
742
+ * own `session_start` handler and a read from ours can be too early to see it.
737
743
  */
738
- type CodemodeActivationSource = "cli" | "project" | "user" | "default" | "invalid" | "mcp";
744
+ type CodemodeActivationSource = "loadout" | "invalid" | "mcp";
739
745
  /** The answer plus enough provenance to explain it in a notice. */
740
746
  interface CodemodeActivationResolution {
741
747
  activation: CodemodeActivation;
@@ -749,26 +755,64 @@ interface CodemodeActivationResolution {
749
755
  mcpError?: string;
750
756
  }
751
757
  /**
752
- * Resolve the LOADOUT half of whether `codemode` will be in the model's tool list: the
753
- * command-line allowlist, then the merged `defaultTools`, then pi's own default. This is the
754
- * ADR-0029 mirror; the MCP auto-enable evidence (ADR-0033) is unioned on top by
755
- * {@link applyMcpAutoEnableEvidence}, which {@link readCodemodeActivation} calls.
758
+ * Everything the loadout side of this probe used to need, deleted with the probe.
759
+ *
760
+ * `PI_DEFAULT_TOOL_NAMES`, `isToolModifier`, `stringEntries`, `mergeDefaultTools` and
761
+ * `resolveDefaultTools` were a second implementation of pi's `defaultTools` resolution — the merge
762
+ * direction, the modifier grammar, the "a list of only modifiers starts from pi's four defaults"
763
+ * rule. It existed because the factory had to know the answer before pi would let this package ask,
764
+ * and reimplementing the rule was the only way to know it in advance.
756
765
  *
757
- * The last of those is the load-bearing one and is what makes absence of evidence mean
758
- * `inactive`: pi registers `codemode` inactive, so a session that configured nothing does not get
759
- * it, and delegating orchestration to a tool the model cannot call is the failure this exists to
760
- * prevent.
766
+ * The answer is now read from pi instead, so there is nothing left for these to be right about. A
767
+ * second implementation of a rule the host owns is a defect waiting for the host to change the rule,
768
+ * which is how #131 happened: a project's `defaultTools` was honoured here whether or not pi would
769
+ * ever read it.
761
770
  */
762
- export declare function resolveCodemodeActivation(argv: readonly string[], projectSettings: unknown, userSettings: unknown): CodemodeActivationResolution;
763
771
  /**
764
- * Whether an explicit `surfaceMode` disagrees with what the table above decided.
772
+ * The command-line reader is gone with the reconstruction it existed for.
773
+ *
774
+ * `splitCliToolList`, `PI_VALUE_CONSUMING_FLAGS`, `PI_CONDITIONALLY_CONSUMING_FLAGS`,
775
+ * `piPrintConsumesNext`, `CliToolFlags` and `cliToolFlags` parsed `process.argv` to work out
776
+ * which tools pi would activate: `--tools` as an allowlist, `--exclude-tools` as a denylist,
777
+ * `--no-tools` and `--no-builtin-tools` as the two ways of emptying the list, plus the value-
778
+ * consuming grammar those flags live in, because a value that looks like a flag must not be
779
+ * re-read as one.
780
+ *
781
+ * It was careful — a 294-case differential against pi's own parser found no disagreement once it
782
+ * was made to agree, and every divergence before that had been argued away as pointing in a safe
783
+ * direction. That argument is what this deletion is about rather than the code: it was wrong.
784
+ * Reading the command line correctly is not the same as knowing what pi will do with it, and the
785
+ * settings half had already shown how that goes wrong — #131 is this reader's sibling, with the
786
+ * same shape of mistake on the file side.
787
+ *
788
+ * The reader's replacement is one call, and it is the host's own answer rather than a second
789
+ * opinion on it. See {@link readCodemodeActivation}.
790
+ */
791
+ /**
792
+ * Whether `codemode` is in pi's active tool set, as pi reports it.
793
+ *
794
+ * This is the ADR-0029 loadout mirror with the mirror taken out. It used to resolve the same
795
+ * question by parsing the command line and merging the two `defaultTools` files, replaying pi's
796
+ * precedence over them:
797
+ *
798
+ * 1. `--tools` decides the list, and it beats `--no-tools`.
799
+ * 2. `--exclude-tools` vetoes whichever list won, because the denylist is a `.filter` over that
800
+ * list rather than another source.
801
+ * 3. `--no-tools` and `--no-builtin-tools` with no allowlist empty the list, so `codemode` is not
802
+ * active whatever `defaultTools` says.
803
+ * 4. Otherwise `defaultTools` decides, vetoed by the denylist exactly as in (2).
765
804
  *
766
- * The explicit key always WINS — that is what an override is for — so this exists only to be
767
- * reported. `off` is exempt: it means "I do not want this package's surface at all", which is a
768
- * statement about the package rather than a claim about who orchestrates, and warning about it on
769
- * every session would be crying wolf.
805
+ * All four are still true, and none of them is restated here. pi has already applied them, along
806
+ * with the project-trust decision, by the time an extension can read its loadout at
807
+ * `session_start`; asking it is both shorter and correct in the case that a second implementation
808
+ * cannot be, which is when the two disagree about something pi changed.
809
+ *
810
+ * Absence still means `inactive`, and that is unchanged rather than defaulted: pi registers
811
+ * `codemode` with `defaultActive: false`, so a session that configured nothing does not get it, and
812
+ * delegating orchestration to a tool the model cannot call is the failure this whole probe exists
813
+ * to prevent.
770
814
  */
771
- export declare function surfaceModeConflict(explicit: SurfaceMode | undefined, detected: SurfaceMode): boolean;
815
+ export declare function resolveCodemodeActivation(activeToolNames: readonly string[]): CodemodeActivationResolution;
772
816
  /**
773
817
  * Whether the pi that launched us ships its own `codemode` orchestration tool.
774
818
  *
@@ -798,88 +842,75 @@ interface CodemodePresence {
798
842
  /** How the answer was reached, so a test can tell a measured yes from a failed probe. */
799
843
  how: "found" | "not-found" | "no-entry" | "unresolvable-entry";
800
844
  }
801
- /** Result of reading the surface mode, with enough detail to warn about a broken file. */
802
- interface SurfaceModeConfig {
845
+ /** The detected surface, plus every probe result `session_start` reports on. */
846
+ interface DetectedSurface {
847
+ /** What this package registers. Decided by {@link detectedSurfaceMode}; never configured. */
803
848
  surfaceMode: SurfaceMode;
804
- source: "file" | "default" | "invalid";
805
- error?: string;
806
- /**
807
- * What the probe found, on the paths where the surface was NOT decided by the file. Absent
808
- * when the user set the key: that call never consults the probe (see
809
- * {@link readSurfaceModeConfig}), so there is nothing to report.
810
- */
811
- codemode?: CodemodePresence;
812
- /**
813
- * Whether pi will actually load its own codemode (ADR-0027). Absent for the same reason as
814
- * {@link codemode}: an explicit key short-circuits the probe, so there is nothing to report.
815
- */
816
- codemodeSwitch?: CodemodeSwitchResolution;
817
- /**
818
- * Whether `codemode` will be in the model's tool list (ADR-0029), the third axis of the
819
- * detected table. Absent for the same reason as {@link codemodeSwitch}.
820
- */
821
- codemodeActivation?: CodemodeActivationResolution;
822
- /**
823
- * What the four-case table decided, carried even when an explicit key overrode it — that
824
- * difference is exactly what {@link surfaceModeConflict} reports on.
825
- */
826
- detected?: SurfaceMode;
849
+ /** Whether this pi ships a `codemode` at all. */
850
+ codemode: CodemodePresence;
851
+ /** Whether pi will load it (ADR-0027). */
852
+ codemodeSwitch: CodemodeSwitchResolution;
853
+ /** Whether the model will be able to call it (ADR-0029), the third axis of the table. */
854
+ codemodeActivation: CodemodeActivationResolution;
827
855
  }
828
856
  /**
829
- * Read `surfaceMode` from the agent-dir config file.
857
+ * Decide the surface by asking the pi. A pure function of the three probes and nothing else.
830
858
  *
831
- * A pure function over the filesystem, shaped like {@link readDefaultModeConfig} on purpose:
832
- * an absent file, an absent key, unparseable JSON, a non-object, a wrong-typed value and an
833
- * out-of-set value all resolve to the detected default rather than a hardcoded one, and every
834
- * malformed shape additionally reports `invalid` with a reason. A malformed setting must never
835
- * half-apply - which tools exist is not something to change on a guess.
859
+ * This used to read a `surfaceMode` key first and only fall back to the probes, and the argument
860
+ * for that was an escape hatch: pi has `registerTool` but no `unregisterTool`, so a surface chosen
861
+ * wrongly cannot be corrected once the factory has returned — a session that got the wrong answer
862
+ * keeps it until it restarts. That argument is real, and it is also why the hatch is gone rather
863
+ * than merely narrowed:
836
864
  *
837
- * The `presence` and `codemodeSwitch` arguments are parameters rather than hidden calls, so a test
838
- * can state the pi it is reasoning about instead of depending on the machine it runs on. Omit
839
- * them and the real probes answer, but only on a path that actually needs the answer: they are
840
- * resolved inside the fallback branches rather than in a default parameter, because a default
841
- * parameter is evaluated on EVERY call -- including the ones an explicit `surfaceMode` key
842
- * short-circuits, where the user paid a `realpathSync` plus up to three `statSync` and the
843
- * settings reads below to set one line of JSON and get a constant.
865
+ * - **The wrong answer is now reported rather than prevented.** `session_start` measures the live
866
+ * registry against all three probes and says so when they disagree (ADR-0029), which a pinned
867
+ * value used to suppress.
868
+ * - **The direction that actually hurts is not reachable by a setting anyway.** A pin can only
869
+ * make the surface *more* capable than detection, never less: every way the probes come back
870
+ * wrong (`subagents` chosen for a `codemode` that cannot run) resolves to `full`, and no value
871
+ * the file could hold would have produced a better outcome than the one already chosen.
872
+ * - **Disabling is pi's job now.** The `off` value existed because a package that cannot be
873
+ * switched off is a package that stays loaded forever. `pi config` turns a package's extensions
874
+ * off without loading them — measured, not assumed: on pi 1.1.0 a `packages` entry whose
875
+ * `extensions` is `[]` or `["!dist/index.js"]` does not load the extension at all, while
876
+ * `["+dist/index.js"]` and an absent key both do. That is a channel this package cannot offer
877
+ * for itself, because the switch that uses it is pi's.
844
878
  *
845
- * The probes do NOT save an explicit key their reads, though, and the cost is worth naming exactly
846
- * rather than in the round number it is usually quoted as: `detected()` runs the switch probe AND
847
- * the activation probe, and each reads the SAME two settings files (`readCodemodeSwitch` and
848
- * `readCodemodeActivation` each call `readSettingsObject` twice), so one detected surface costs
849
- * four settings reads over two files, plus the two `mcp.json` reads ADR-0033 added. The
850
- * duplication is pre-existing and deliberate — each probe is copied from pi whole and kept
851
- * self-contained — so the number is recorded, not optimised.
879
+ * The `presence` and `codemodeSwitch` arguments stay parameters rather than hidden calls, so a
880
+ * test can state the pi it is reasoning about instead of depending on the machine it runs on.
881
+ *
882
+ * The probes are resolved here rather than in default parameters, so every call pays for all
883
+ * three — the shortcut that used to exist (`a default parameter is evaluated on EVERY call`)
884
+ * saved real work only when a key short-circuited the probes entirely, and there is no key left.
885
+ * The cost, named rather than rounded: each of the two settings-reading probes reads the SAME two
886
+ * files, so one detection is four settings reads over two files plus the two `mcp.json` reads
887
+ * ADR-0033 added. The duplication is pre-existing and deliberate — each probe is copied from pi
888
+ * whole and kept self-contained — so the number is recorded, not optimised.
852
889
  */
853
- export declare function readSurfaceModeConfig(agentDir: string, presence?: CodemodePresence, codemodeSwitch?: CodemodeSwitchResolution, codemodeActivation?: CodemodeActivationResolution, cwd?: string): SurfaceModeConfig;
854
- /** Outcome of writing `surfaceMode` for `/ptc surface`. */
855
- type SurfaceModeWrite = {
856
- ok: true;
857
- path: string;
858
- /** The value already in the file, or `undefined` when there was none. */
859
- previous: SurfaceMode | undefined;
860
- /** False when the file already said this, in which case NOTHING was written. */
861
- changed: boolean;
862
- } | {
863
- ok: false;
890
+ export declare function detectSurfaceMode(agentDir: string, activeToolNames: readonly string[], presence?: CodemodePresence, codemodeSwitch?: CodemodeSwitchResolution, codemodeActivation?: CodemodeActivationResolution, cwd?: string): DetectedSurface;
891
+ /** A `surfaceMode` left in `ptc.json` by a release that still had the key. */
892
+ interface LegacySurfaceKey {
893
+ /** The path it was found in, or `undefined` when there was no file to look in. */
864
894
  path: string;
865
- error: string;
866
- };
895
+ /** The value that was there, or the JSON rendering when it was not a string. */
896
+ value: unknown;
897
+ }
867
898
  /**
868
- * Write one `surfaceMode` key into the agent-dir config, preserving every other key.
899
+ * Read a `surfaceMode` key that this package no longer acts on, so `session_start` can say so.
869
900
  *
870
- * Three rules, each of which is a way a naive rewrite goes wrong:
901
+ * The key shipped in v1.6.0 and the three values it took are gone, which means a user who set
902
+ * `"off"` to keep this package out of their sessions would find it back on the next upgrade with
903
+ * nothing to explain why. Nothing here restores the behaviour: the returned value is only ever
904
+ * turned into a notice that names the replacement. The alternative — honouring `off` forever as a
905
+ * compatibility shim — is the switch this change exists to delete, and it would leave the package
906
+ * carrying two ways to be disabled, one of which silently does nothing.
871
907
  *
872
- * - **A malformed file is never overwritten.** An unparseable `ptc.json` is a file the user may
873
- * be mid-edit on, and this command is not a licence to replace it with something valid that
874
- * drops whatever was in it. The read side already reports that shape rather than acting on it
875
- * ({@link readSurfaceModeConfig}), and the write side has to agree.
876
- * - **Other keys survive.** `defaultMode` lives in the same file (ADR-0010), and a command that
877
- * wrote `{"surfaceMode": …}` wholesale would silently reset the user's mode preference.
878
- * - **An unchanged value writes nothing.** `/ptc surface full` on a session already at `full`
879
- * should not touch the file's mtime, and — more to the point — should not trigger the reload
880
- * that would follow, since a reload replaces every extension instance for no reason.
908
+ * Returns `undefined` for an absent file, an absent key, and for a file this cannot parse or that
909
+ * is not an object. Those last two are NOT reported here: a `ptc.json` too broken to read has no
910
+ * `surfaceMode` to be stale about, and the parse failure belongs to whoever owns that file
911
+ * (`defaultMode` still lives there, and {@link readDefaultModeConfig} is what reports it).
881
912
  */
882
- export declare function setSurfaceMode(agentDir: string, value: unknown): SurfaceModeWrite;
913
+ export declare function readLegacySurfaceKey(agentDir: string): LegacySurfaceKey | undefined;
883
914
  /** Why the mode declined to turn on. Surfaced in the entry notification / debug logs. */
884
915
  type ModeBlockReason = "not-tui" | "config-off" | "tools-unavailable" | "restricted-session";
885
916
  /** Either the loadout to apply, or the reason the mode stayed off. */
@@ -1541,16 +1572,9 @@ export declare class TurnPools {
1541
1572
  export interface PtcSubagentsOptions {
1542
1573
  /** Use this session-scoped background runtime instead of constructing one. */
1543
1574
  backgroundRuntime?: BackgroundTaskRuntime;
1544
- /**
1545
- * Test seam for ADR-0025's surface mode. When set it wins over the agent-dir `ptc.json`,
1546
- * so a test never reads the developer's real settings -- and the four test files that all
1547
- * build this factory through one stub would otherwise inherit whatever the machine happens
1548
- * to have. Undefined in production, where the file is the only source.
1549
- */
1550
- surfaceMode?: SurfaceMode;
1551
1575
  /**
1552
1576
  * ADR-0026 test seam: what the codemode probe found. Undefined in production, where the
1553
- * probe really runs. A test that exercises the DETECTED default states the pi it
1577
+ * probe really runs. A test that exercises the DETECTED surface states the pi it
1554
1578
  * assumes rather than inheriting whatever process.argv the test runner happens to have,
1555
1579
  * which is how the previous version of that test passed for the wrong reason.
1556
1580
  */
@@ -1562,8 +1586,24 @@ export interface PtcSubagentsOptions {
1562
1586
  * and that is the case this seam exists to state.
1563
1587
  */
1564
1588
  codemodeSwitch?: CodemodeSwitchResolution;
1589
+ /**
1590
+ * ADR-0029 test seam: whether `codemode` will be in the model's tool list. Undefined in
1591
+ * production, where `readCodemodeActivation` reads pi's own active tool set and the MCP
1592
+ * evidence — no settings file and no `argv` on this axis any more (ADR-0035).
1593
+ *
1594
+ * This seam exists because the `surfaceMode` pin it replaced did. That pin was there to keep a
1595
+ * test off the developer's machine: the probe it fed read a settings file out of the developer's
1596
+ * own agent dir, so a test file that pinned nothing decided its own surface by however the
1597
+ * machine running it happened to be configured. Removing the pin without adding this would have
1598
+ * moved that failure back in, silently, to every stub-built factory.
1599
+ *
1600
+ * The machine-dependence is gone — the loadout is handed in rather than read off disk — but the
1601
+ * seam stays, because a test whose subject IS the axis needs to state an answer pi would not
1602
+ * produce on its own (`null` runs the real probe; see `tests/helpers/ptc.ts`).
1603
+ */
1604
+ codemodeActivation?: CodemodeActivationResolution;
1565
1605
  }
1566
1606
  export default function ptcSubagents(pi: ExtensionAPI, options?: PtcSubagentsOptions): void;
1567
1607
  //#endregion
1568
- export type { Binding, BindingContext, BindingTable, CodemodePresence, CodemodeSwitch, CodemodeSwitchResolution, CodemodeSwitchSource, CreateBuiltinBindingsOptions, DefaultModeConfig, ModeBlockReason, ModeEntryDecision, ModeEntryInput, ModeHideStrategy, PersistedModeState, PtcConfig, PtcErrorKind, PtcErrorShape, PtcImage, PtcJsonValue, PtcModeState, PtcRunOutcome, PtcSurface, RunPtcProgramOptions, SurfaceModeConfig, TurnPoolsOptions, WorkerPoolOptions, WorkerPoolStats, WorkerPoolWorkerOptions };
1608
+ export type { Binding, BindingContext, BindingTable, CodemodePresence, CodemodeSwitch, CodemodeSwitchResolution, CodemodeSwitchSource, CreateBuiltinBindingsOptions, DefaultModeConfig, DetectedSurface, LegacySurfaceKey, ModeBlockReason, ModeEntryDecision, ModeEntryInput, ModeHideStrategy, PersistedModeState, PtcConfig, PtcErrorKind, PtcErrorShape, PtcImage, PtcJsonValue, PtcModeState, PtcRunOutcome, PtcSurface, RunPtcProgramOptions, TurnPoolsOptions, WorkerPoolOptions, WorkerPoolStats, WorkerPoolWorkerOptions };
1569
1609
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -31,8 +31,8 @@ import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./protocol-CfOgxz3u.js";
31
31
  `)}function Er(e,t,n=`run_code`,r={}){let i=(typeof e.description==`string`&&e.description.trim().length>0?e.description.trim():e.meta?.name)??``,a=ir(e.code??e.script??``),o=i.length>0?i:a??`(no description)`;return Jn(new er([{left:`└─ ${`${t.fg(`toolTitle`,t.bold(Xn[n]))} ${t.fg(`accent`,o)}`}`}]),{intervalMs:150,isPartial:r.isPartial??!1,theme:t,requestInvalidate:r.requestInvalidate??Dr,state:r.state??{}})}function Dr(){}function Or(e){let t=e.content?.find(e=>e.type===`text`&&(e.text??``).length>0);return nr(t?.text??``)||`failed`}function kr(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 Ar={running:{text:()=>`running`,themeSlot:`muted`},ok:{text:e=>e.durationMs===void 0?`ok`:`ok ${rr(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 jr(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=kr(e.name,e.args),l=Ar[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 Mr(e,t,n,r=!1,i=!1){let a=or(e.details,n),o=e=>a===void 0?e:{...e,right:a};if(t)return[o({left:` ${n.fg(`error`,`failed: ${Or(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(lr(s))return gr(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=ar(s);return c===void 0?[o({left:` ${n.fg(`muted`,`done`)}`})]:[o({left:` ${n.fg(`dim`,`→ ${c}`)}`})]}function Nr(e,t,n,r={}){let i=sr(e.details),a=[...Mr({...e,details:i},t,n,!1,r.isPartial===!0)];return i?.subCalls!==void 0&&a.push(...jr(i.subCalls,n)),new er(a)}function Pr(e,t,n,r,i={}){let a=sr(e.details);if(n){let t=[...Mr({...e,details:a},!0,r)];a?.subCalls!==void 0&&t.push(...jr(a.subCalls,r));let n=H(e.content?.find(e=>e.type===`text`)?.text??Or(e));for(let e of n.split(`
32
32
  `).slice(0,12))t.push({left:` ${r.fg(`error`,e)}`});return new er(t)}let o=[],s=t.code??t.script??``;if(s.length>0){let e=s.split(`
33
33
  `).map(e=>W(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,n=t>0?` …+${t} more phases`:``;o.push({label:`phases`,lines:[`${e.join(` → `)}${n}`]})}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=Mr({...e,details:a},!1,r,l,i.isPartial===!0);c!==void 0&&u.push(...jr(c,r));for(let e=0;e<o.length;e+=1){let t=e===o.length-1;u.push(...cr(o[e]??{label:``,lines:[]},t,r))}return new er(u)}function Fr(e){return{renderCall(t,n,r){return Er(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?Pr(e,r?.args??{},r?.isError===!0,n,{isPartial:i}):Nr(e,r?.isError===!0,n,{isPartial:i})}}}const Ir=["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`); independent calls may overlap under `Promise.all`.",``,"The parallel binding `pi.dispatch` is always available, registered under its literal dot name —",'call it with string indexing, e.g. `await tools["pi.dispatch"]({ agent: "reviewer", task: "..." })`;',"`tools.pi.dispatch` does not exist. It fans work out to child pi agents, and independent","foreground dispatches compose under `Promise.all` exactly like the built-in calls. The run's","actual bound names (this run, not a static list) are on the `ptcBindings` global, so the program",`never has to guess what is bound.`,``,vn,``,"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(`
34
- `),Lr=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 ${I.timeoutMs/1e3} s; anything above ${I.maxTimeoutMs/1e3} s is capped. The deadline bounds the whole run: if it expires, in-flight foreground dispatches spawned by the program are terminated with it.`}))});function Rr(e={}){return g({name:`ptc_run_code`,label:`PTC Run Code`,description:Ir,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:[...Ln],parameters:Lr,async execute(t,n,r,i,a){let o=zn(a),s=U(e.getBindingSourceNames?.()),c=Date.now(),l=e.getPool?.(),u=a.sessionManager?.getSessionDir?.(),d=e.getDispatchDeps?.(),f=Bn({surface:`run_code`,startedAt:c,onUpdate:i}),p;try{p=await hn({code:n.code,surface:`run_code`,cwd:o,bindings:nn({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 Hn(p);return Un({outcome:p,surface:`run_code`,durationMs:Date.now()-c})},...Fr(`run_code`)})}const zr=[`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. The parallel binding `pi.dispatch` is always available under",'its literal dot name — call it with string indexing: `await tools["pi.dispatch"]({ agent, task })`;',"the run's actual bound names are on the `ptcBindings` global. What comes back is the script's","return value, its `log`/`phase` narration and its `console.log` output.",``,vn,``,`Image-bearing tool results inside the script are attached to you after the run — never return`,`image data as the script's value.`].join(`
35
- `),Br=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 Vr(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=Hr(t[e],`args.${e}`,/* @__PURE__ */ new Set);if(n!==void 0)throw TypeError(`ptc_workflow args must be plain JSON: ${n}`)}}}function Hr(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=Hr(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=Hr(i[e],`${n}.${e}`,r);if(t!==void 0)return t}return}finally{r.delete(i)}}function Ur(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 Wr(e={}){return g({name:`ptc_workflow`,label:`PTC Workflow`,description:zr,promptSnippet:`Run a structured TypeScript workflow with named phases, narration (log/phase) and structured concurrency (parallel/pipeline)`,promptGuidelines:[...Ln],parameters:Br,async execute(t,n,r,i,a){Vr(n.args);let o=zn(a),s=U(e.getBindingSourceNames?.()),c=Date.now(),l=e.getPool?.(),u=a.sessionManager?.getSessionDir?.(),d=e.getDispatchDeps?.(),f=Bn({surface:`workflow`,startedAt:c,onUpdate:i}),p;try{p=await hn({code:n.script,surface:`workflow`,cwd:o,bindings:nn({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 Hn(p);let m=n.meta.phases?.map(e=>e.name);return Un({outcome:p,surface:`workflow`,warnings:Ur(p,m),durationMs:Date.now()-c})},...Fr(`workflow`)})}var Gr=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 Kr(e){return typeof e==`object`&&!!e&&e.code===`ENOENT`}var qr=class{#e;constructor(e){if(e.length===0)throw TypeError(`FileOutputStorage: baseDir is required`);this.#e=e}async readOutput(e){try{return await he(this.#t(e),`utf8`)}catch(e){if(Kr(e))return null;throw e}}async writeOutput(e,t){let n=O(this.#e,`tasks`,e);await me(n,{recursive:!0}),await ye(O(n,`output.log`),t,`utf8`)}outputRef(e){return this.#t(e)}#t(e){return O(this.#e,`tasks`,e,`output.log`)}};function Jr(e,t){return Sn(e,`pi-ptc-task-output-`,t===void 0?{}:{fullText:t})}const Yr={running:`accent`,stopping:`warning`,succeeded:`success`,failed:`error`,lost:`dim`,canceled:`muted`},Xr={running:`●`,stopping:`◐`,succeeded:`✓`,failed:`✗`,lost:`◌`,canceled:`⊘`};var q=class{items;constructor(e){this.items=e}invalidate(){}render(e){let t=Math.max(1,Math.floor(e));return this.items.map(e=>Zr(e,t))}};function Zr(e,t){let{left:n,right:r}=e;if(r===void 0)return P(n,t,`…`);let i=F(r);if(i>=t)return P(r,t,`…`);let a=P(n,Math.max(0,t-i-1),`…`),o=Math.max(1,t-i-F(a));return a+` `.repeat(o)+r}function Qr(e,t){return e.length>t?`${e.slice(0,t-1)}…`:e}function $r(e){return Qr((H(e).split(`
34
+ `),Lr=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 ${I.timeoutMs/1e3} s; anything above ${I.maxTimeoutMs/1e3} s is capped. The deadline bounds the whole run: if it expires, in-flight foreground dispatches spawned by the program are terminated with it.`}))});function Rr(e={}){return g({name:`ptc_run_code`,label:`PTC Run Code`,exposure:e.exposure,description:Ir,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:[...Ln],parameters:Lr,async execute(t,n,r,i,a){let o=zn(a),s=U(e.getBindingSourceNames?.()),c=Date.now(),l=e.getPool?.(),u=a.sessionManager?.getSessionDir?.(),d=e.getDispatchDeps?.(),f=Bn({surface:`run_code`,startedAt:c,onUpdate:i}),p;try{p=await hn({code:n.code,surface:`run_code`,cwd:o,bindings:nn({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 Hn(p);return Un({outcome:p,surface:`run_code`,durationMs:Date.now()-c})},...Fr(`run_code`)})}const zr=[`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. The parallel binding `pi.dispatch` is always available under",'its literal dot name — call it with string indexing: `await tools["pi.dispatch"]({ agent, task })`;',"the run's actual bound names are on the `ptcBindings` global. What comes back is the script's","return value, its `log`/`phase` narration and its `console.log` output.",``,vn,``,`Image-bearing tool results inside the script are attached to you after the run — never return`,`image data as the script's value.`].join(`
35
+ `),Br=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 Vr(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=Hr(t[e],`args.${e}`,/* @__PURE__ */ new Set);if(n!==void 0)throw TypeError(`ptc_workflow args must be plain JSON: ${n}`)}}}function Hr(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=Hr(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=Hr(i[e],`${n}.${e}`,r);if(t!==void 0)return t}return}finally{r.delete(i)}}function Ur(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 Wr(e={}){return g({name:`ptc_workflow`,label:`PTC Workflow`,exposure:e.exposure,description:zr,promptSnippet:`Run a structured TypeScript workflow with named phases, narration (log/phase) and structured concurrency (parallel/pipeline)`,promptGuidelines:[...Ln],parameters:Br,async execute(t,n,r,i,a){Vr(n.args);let o=zn(a),s=U(e.getBindingSourceNames?.()),c=Date.now(),l=e.getPool?.(),u=a.sessionManager?.getSessionDir?.(),d=e.getDispatchDeps?.(),f=Bn({surface:`workflow`,startedAt:c,onUpdate:i}),p;try{p=await hn({code:n.script,surface:`workflow`,cwd:o,bindings:nn({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 Hn(p);let m=n.meta.phases?.map(e=>e.name);return Un({outcome:p,surface:`workflow`,warnings:Ur(p,m),durationMs:Date.now()-c})},...Fr(`workflow`)})}var Gr=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 Kr(e){return typeof e==`object`&&!!e&&e.code===`ENOENT`}var qr=class{#e;constructor(e){if(e.length===0)throw TypeError(`FileOutputStorage: baseDir is required`);this.#e=e}async readOutput(e){try{return await he(this.#t(e),`utf8`)}catch(e){if(Kr(e))return null;throw e}}async writeOutput(e,t){let n=O(this.#e,`tasks`,e);await me(n,{recursive:!0}),await ye(O(n,`output.log`),t,`utf8`)}outputRef(e){return this.#t(e)}#t(e){return O(this.#e,`tasks`,e,`output.log`)}};function Jr(e,t){return Sn(e,`pi-ptc-task-output-`,t===void 0?{}:{fullText:t})}const Yr={running:`accent`,stopping:`warning`,succeeded:`success`,failed:`error`,lost:`dim`,canceled:`muted`},Xr={running:`●`,stopping:`◐`,succeeded:`✓`,failed:`✗`,lost:`◌`,canceled:`⊘`};var q=class{items;constructor(e){this.items=e}invalidate(){}render(e){let t=Math.max(1,Math.floor(e));return this.items.map(e=>Zr(e,t))}};function Zr(e,t){let{left:n,right:r}=e;if(r===void 0)return P(n,t,`…`);let i=F(r);if(i>=t)return P(r,t,`…`);let a=P(n,Math.max(0,t-i-1),`…`),o=Math.max(1,t-i-F(a));return a+` `.repeat(o)+r}function Qr(e,t){return e.length>t?`${e.slice(0,t-1)}…`:e}function $r(e){return Qr((H(e).split(`
36
36
  `).find(e=>e.trim().length>0)??``).trim(),120)}function ei(e){return typeof e==`object`&&e?e:{}}function ti(e){return e===`running`||e===`stopping`}function ni(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 ri(e,t){if(ti(e.status))return ni(t-e.createdAt);if(e.durationMs!==void 0)return ni(e.durationMs);if(e.finishedAt!==void 0)return ni(e.finishedAt-e.createdAt)}function ii(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 ai(){}function oi(e,t,n){return new q([{left:`└─ ${n.fg(`toolTitle`,n.bold(e))}${t.length>0?` ${t}`:``}`}])}function si(e,t,n){let r=ei(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 oi(`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 oi(`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=${H(r.reason)}`:``;return oi(`PTC task stop`,e===void 0?n.fg(`muted`,`(unknown task)`):`${n.fg(`accent`,e)}${n.fg(`dim`,t)}`,n)}}}function ci(e,t,n,r){let i=Yr[e.status]??`muted`,a=n.fg(i,Xr[e.status]??`?`),o=Qr(H(e.label),48),s=[`${a} ${n.fg(`text`,o)}`,n.fg(`dim`,H(e.agentName))],c=ri(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 li(e,t){let n=ei(e),r=Array.isArray(n.tasks)?n.tasks:[];if(r.length===0)return ii(new q([{left:` ${t.theme.fg(`muted`,`(no background tasks)`)}`}]),{isLive:!1,state:t.state,requestInvalidate:t.requestInvalidate});let i=r.some(e=>ti(e.status)),a=r.slice(0,32),o=r.length-a.length,s=a.map((e,n)=>ci(e,n===a.length-1&&o===0,t.theme,t.now));return o>0&&s.push({left:` └─ ${t.theme.fg(`dim`,`…+${o} more tasks`)}`}),ii(new q(s),{isLive:t.partial&&i,state:t.state,requestInvalidate:t.requestInvalidate})}function ui(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 di(e,t){return` ${t.fg(`dim`,`full output: `)}${t.fg(`accent`,e)}`}function fi(e,t){let n=ei(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=ui(i,a,t.theme),c=[];if(r.length>0){let e=H(r).split(`
37
37
  `),n=e.slice(0,6);n.forEach((e,n)=>{let r=t.theme.fg(`toolOutput`,Qr(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:di(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:di(o,t.theme),...s===void 0?{}:{right:s}});return new q(c)}function pi(e,t){let n=ei(e),r=n.task;if(typeof r!=`object`||!r)return new q([{left:` ${t.theme.fg(`muted`,`(no task)`)}`}]);let i=Yr[r.status]??`muted`,a=t.theme.fg(i,Xr[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`,H(r.id))}`,t.theme.fg(i,s)];return typeof r.stopReason==`string`&&r.stopReason.length>0&&c.push(t.theme.fg(`dim`,`reason=${H(r.stopReason)}`)),new q([{left:` ${c.join(` `)}`}])}function mi(e,t){let n=e.content?.find(e=>e.type===`text`&&(e.text??``).length>0),r=$r(n?.text??``)||`failed`;return new q([{left:` ${t.fg(`error`,`failed: ${r}`)}`}])}function hi(e,t,n){switch(e){case`task-list`:return li(t.details,n);case`task-output`:return fi(t.details,n);case`task-stop`:return pi(t.details,n)}}function gi(e){return{renderCall(t,n){return si(e,t,n)},renderResult(t,n,r,i){return i?.isError===!0?mi(t,r):hi(e,t,{theme:r,partial:n?.isPartial===!0||i?.isPartial===!0,requestInvalidate:i?.invalidate??ai,state:i?.state??{},now:Date.now()})}}}const _i=[`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). A succeeded task's child report is rendered there, ahead of the prose.`,`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.`],vi=x.Union([x.Literal(`running`),x.Literal(`stopping`),x.Literal(`succeeded`),x.Literal(`failed`),x.Literal(`canceled`),x.Literal(`lost`)]),yi=x.Object({status:x.Optional(x.Array(vi,{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.`}))}),bi=x.Object({tasks:x.Array(x.Object({id:x.String({description:`The task's ULID — the id ptc_task_output and ptc_task_stop take.`}),status:x.String({description:`One of running, stopping, succeeded, failed, canceled, lost (ADR-0022 §2).`}),agent:x.String({description:`The agent the task was dispatched with (the record's agentName).`}),depth:x.Number({description:`0 for a direct child of the session, 1 for a grandchild, and so on (ADR-0016).`}),label:x.String({description:`The label the task was dispatched with.`}),output_bytes:x.Optional(x.Number({description:`Captured output size; absent while the task has flushed no output.`})),error_message:x.Optional(x.String({description:`Failure text; absent unless the record carries an error.`}))}),{description:`One row per task, newest first — the same order as details.tasks.`}),count:x.Number({description:"Number of rows in `tasks`."})});function xi(e){return{id:e.id,status:e.status,agent:e.agentName,depth:e.depth,label:e.label,...e.outputBytes===void 0?{}:{output_bytes:e.outputBytes},...e.errorMessage===void 0?{}:{error_message:e.errorMessage}}}function Si(e){let t=[e.id,e.status,`${e.agentName} depth=${e.depth}`,H(e.label)];return e.outputBytes!==void 0&&t.push(`${e.outputBytes}B`),e.errorMessage!==void 0&&t.push(`error=${H(e.errorMessage)}`),t.join(` `)}function Ci(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(`
38
38
  `),promptSnippet:`List background tasks and their live status (id, status, agent, label, output size)`,promptGuidelines:[..._i],parameters:yi,outputSchema:bi,async execute(t,n){let r=await e.query({...n.status===void 0?{}:{status:n.status},limit:n.limit??100,orderBy:`createdAt-desc`}),i=r.length===0?`(no background tasks)`:r.map(e=>Si(e)).join(`
@@ -45,14 +45,12 @@ import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./protocol-CfOgxz3u.js";
45
45
  `)){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+`: `+Li(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(ae(e),{recursive:!0});let n=e+`.tmp-`+M();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(Ii(e))return[];throw e}}async#c(e){try{return await this.#t.readFile(e,{encoding:`utf8`}),!0}catch(e){if(Ii(e))return!1;throw e}}#l(){return O(this.#e,`tasks`)}#u(){return O(this.#e,`subscriptions`)}#d(){return O(this.#e,`events`)}#f(e){return O(this.#l(),e+`.json`)}#p(e,t){return O(this.#u(),e+`-`+t+`.json`)}#m(e,t){return O(this.#d(),e+`-`+t+`.jsonl`)}};function Bi(e,t){return new zi(e,t)}const Vi=/* @__PURE__ */ new Set([`succeeded`,`failed`,`canceled`,`lost`]),Hi=/* @__PURE__ */ new Set([`running`,`stopping`]),Ui=2**53-1,Wi={info:()=>void 0,warn:e=>{console.warn(`[pi-ptc.background] `+e)}};function J(e){return e instanceof Error?e.message:String(e)}function Gi(e){return e===void 0||e.length===0?`<in-memory>`:e}function Ki(e){let t=e.spawnSource.callerId;return t.length>0?t:e.id}var qi=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()}},Ji=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)}},Yi=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)}},Xi=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]}},Zi=class{registry;outputStorage;pipeline;slots;lifecycle;dispatchDeps;clock;#e;#t;#n;#r;#i;#a=!1;constructor(e){this.clock=e.clock??(()=>Date.now()),this.#e=e.logger??Wi,this.#r=e.owner??{pid:process.pid,bootMs:this.clock()},this.#i=e.isPidAlive??Xe,this.#t=e.createStorage??(e=>e===void 0||e.length===0?new nt:Bi(e)),this.#n=e.createOutputStorage??(e=>e===void 0||e.length===0?new Gr:new qr(e));let t=(e.createInitialStorage??(()=>new nt))(),n=$e(t,{clock:this.clock,logger:this.#e,owner:this.#r,isPidAlive:this.#i}),r=new Pi(t,{now:this.clock,logger:this.#e});this.registry=new qi(n),this.outputStorage=new Ji(new Gr),this.pipeline=new Yi(r),this.lifecycle=new Xi((e.createLifecycle??(()=>new Fe({logger:this.#e})))()),this.slots=new it(e.concurrency??I.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.#o)}#o=(e,t)=>{this.#a||Vi.has(e.status)&&this.pipeline.notifyIdle(t.subscriptionId,e.id).catch(t=>{this.#e.warn(`terminal notification wake for task `+e.id+` failed: `+J(t))})};async bindSession(e,t){let n,r;try{n=this.#t(e),r=this.#n(e)}catch(n){return this.#s(`background runtime: session storage for `+Gi(e)+` is unavailable: `+J(n),t),[]}let i=$e(n,{clock:this.clock,logger:this.#e,owner:this.#r,isPidAlive:this.#i}),a=new Pi(n,{now:this.clock,logger:this.#e});this.registry.setCurrent(i),this.outputStorage.setCurrent(r),this.pipeline.setCurrent(a),this.#a=!1;try{return await i.reconcileLostTasks()}catch(e){return this.#s(`background runtime: startup reconcile failed; stale running tasks were not all marked lost (reconcile is not per-record tolerant yet): `+J(e),t),[]}finally{this.registry.onTransition(this.#o)}}async drainNotifications(e){let t;try{t=await this.registry.query({limit:Ui})}catch(t){return this.#e.warn(`drainNotifications: could not list tasks for subscriber `+e+`: `+J(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+`: `+J(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+`: `+J(e))}}async shutdown(e){if(this.#a)return[];this.#a=!0;let t;try{t=await this.registry.query({limit:Ui})}catch(e){this.#e.warn(`shutdown: could not list tasks: `+J(e)),t=[]}let n=[];for(let r of t)if(Hi.has(r.status)&&Ye(r,this.#r))try{let t=await this.registry.transition({kind:`reconcile-lost`,taskId:r.id,reason:e},{clock:this.clock,callerId:Ki(r),logger:this.#e});n.push(t.record)}catch(e){this.#e.warn(`shutdown: could not mark task `+r.id+` lost: `+J(e))}for(let e of this.lifecycle.liveHandles()){let t=ft(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}#s(e,t){if(this.#e.warn(e),t!==void 0)try{t(e)}catch(e){this.#e.warn(`bindSession reporter threw: `+J(e))}}};function Qi(e={}){return new Zi(e)}const $i=/* @__PURE__ */ new Set([`succeeded`,`failed`,`lost`]);function ea(e){return $i.has(e.status)}function ta(e,t){if(e.length===0)return``;let n=e.map(ra).join(`
46
46
  `);return`${`<bg-task-notifications batch-id="${Y(t.batchId)}" delivered-at-ms="${t.deliveredAtMs}">`}\n${n}\n</bg-task-notifications>`}function na(e,t=102400){let n=[],r=[],i=0,a=t-aa;for(let t of e){let e=ia(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 ra(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="${Y(t.type)}"`,` task-id="${Y(t.taskId)}"`,` subscription-id="${Y(t.subscriptionId)}"`,` status="${Y(t.status)}"`,` label="${Y(n.label)}"`,` agent-name="${Y(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>${Y(n.outputRef)}</output-ref>`),r!==void 0&&r<=2048&&a!==void 0&&o.push(` <output-preview>${Y(a)}</output-preview>`),o.push(` </bg-task-notification>`),o.join(`
47
47
  `)}function Y(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`<`,`&lt;`).replaceAll(`"`,`&quot;`).replaceAll(` `,`&#9;`).replaceAll(`
48
- `,`&#10;`).replaceAll(`\r`,`&#13;`)}function ia(e){return Buffer.byteLength(ra(e),`utf8`)}const aa=Buffer.byteLength(`<bg-task-notifications batch-id="${`0`.repeat(26)}" delivered-at-ms="${`9`.repeat(13)}">\n\n</bg-task-notifications>`,`utf8`),oa=[`ptc_run_code`,`ptc_workflow`],sa=[`read`,`bash`,`edit`,`write`],ca=`ptc-mode`,la=`ptc-mode`,ua=`ptc.json`,da=`builtins-only`;function fa(){return{enabled:!1,base:void 0,ourLoadout:void 0}}function pa(e){let t=O(e,ua),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}`}}const X=[`off`,`subagents`,`full`],ma=`full`,Z=`builtin:codemode`;function Q(e){return e.split(j).join(`/`)}function ha(e){return Q(e.startsWith(`./`)||e.startsWith(`.\\`)?e.slice(2):e)}function ga(e,t,n){let r=Q(k(n,e)),i=ie(e),a=Q(e);return t.some(e=>{let t=Q(e);return be(r,t)||be(i,t)||be(a,t)})}function _a(e,t,n){if(t.length===0)return!1;let r=Q(k(n,e)),i=Q(e);return t.some(e=>{let t=ha(e);return t===r||t===i})}function va(e){let t=[];for(let n of e){if(typeof n!=`string`)continue;let e=n[0];(e===`+`||e===`-`||e===`!`)&&t.push({sign:e,target:n.slice(1)})}return t}function ya(e,t){if(!Array.isArray(e))return;let n;for(let{sign:r,target:i}of va(e)){let e=r!==`-`&&r!==`!`;(r===`+`||r===`-`?_a(Z,[i],t):ga(Z,[i],t))&&(n=e?`enabled`:`disabled`)}return n}function ba(e,t){if(!Array.isArray(e))return;let n=va(e),r=e=>n.filter(t=>t.sign===e).map(e=>e.target),i=r(`!`),a=r(`+`),o=r(`-`),s;return ga(Z,i,t)&&(s=`disabled`),_a(Z,a,t)&&(s=`enabled`),_a(Z,o,t)&&(s=`disabled`),s}function xa(e){for(let t=0;t<e.length;t+=1){let n=e[t];if((n===`-e`||n===`--extension`)&&e[t+1]===Z)return!0}return!1}function Sa(e){let t;try{t=T(e,`utf8`)}catch{return{value:void 0}}try{let n=JSON.parse(t);return typeof n!=`object`||!n||Array.isArray(n)?{value:void 0,error:`${e} must contain a JSON object`}:{value:n}}catch(t){return{value:void 0,error:`${e} is not valid JSON (${t instanceof Error?t.message:String(t)})`}}}function Ca(e,t,n,r={project:process.cwd(),user:process.cwd()}){let i=e.slice(1);if(xa(i))return{switch:`enabled`,source:`cli`};if(i.some(e=>e===`-ne`||e===`--no-extensions`))return{switch:`disabled`,source:`cli`};let a=t?.extensions,o=ya(a,r.project);if(o!==void 0)return{switch:o,source:`project`};let s=n?.extensions,c=ba(s,r.user);return c===void 0?{switch:`absent`,source:`default`}:{switch:c,source:`user`}}function wa(e,t,n=process.argv){let r=Sa(O(t,`.pi`,`settings.json`)),i=Sa(O(e,`settings.json`)),a=Ca(n,r.value,i.value,{project:O(t,`.pi`),user:e}),o=r.error??i.error;return o===void 0?a:{...a,source:`invalid`,error:o}}const Ta=[`read`,`bash`,`edit`,`write`];function Ea(e){return typeof e==`string`&&(e.startsWith(`+`)||e.startsWith(`-`))}function Da(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function Oa(e,t){return t===void 0?e:!Array.isArray(e)||!Array.isArray(t)||!t.every(Ea)?t:[...e,...t]}function ka(e){let t=e.filter(e=>!Ea(e)),n=t.length>0||e.length===0?t:[...Ta];for(let t of e){if(!Ea(t))continue;let e=t.slice(1),r=n.indexOf(e);t.startsWith(`+`)&&r===-1&&e?n.push(e):t.startsWith(`-`)&&r!==-1&&n.splice(r,1)}return n}function Aa(e){for(let t=0;t<e.length;t+=1){if(e[t]!==`--tools`&&e[t]!==`-t`)continue;let n=e[t+1];return n===void 0?void 0:n.split(`,`).map(e=>e.trim()).filter(e=>e.length>0)}}function ja(e,t,n){let r=Aa(e.slice(1));if(r!==void 0)return{activation:r.includes(`codemode`)?`active`:`inactive`,source:`cli`};let i=t?.defaultTools,a=n?.defaultTools,o=Oa(a,i);return o===void 0?{activation:`inactive`,source:`default`}:{activation:ka(Da(o)).includes(`codemode`)?`active`:`inactive`,source:i===void 0?`user`:`project`}}function Ma(e,t,n=process.argv){let r=Sa(O(t,`.pi`,`settings.json`)),i=Sa(O(e,`settings.json`)),a=Ga(ja(n,r.value,i.value),Wa(e,t)),o=r.error??i.error;return o===void 0?a:{...a,source:`invalid`,error:o}}const Na=[`codemode`,`deferred`,`direct`,`hidden`],Pa={"codemode-deferred":`codemode`},Fa=/^[A-Za-z0-9_-]+$/;function Ia(e){return typeof e==`string`?Pa[e]??e:e}function La(e){return typeof e==`string`&&Na.includes(e)}function $(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Ra(e){return $(e)&&Object.values(e).every(e=>typeof e==`string`)}function za(e){return URL.canParse(e)?/^https?:$/.test(new URL(e).protocol):!1}function Ba(e,t){if(!Fa.test(e)||!$(t))return;let n=Ia(t.exposure),r=$(t.toolExposure)?Object.fromEntries(Object.entries(t.toolExposure).map(([e,t])=>[e,Ia(t)])):t.toolExposure;if(n!==void 0&&!La(n)||r!==void 0&&(!$(r)||!Object.values(r).every(e=>La(e)))||t.enabled!==void 0&&typeof t.enabled!=`boolean`||t.description!==void 0&&typeof t.description!=`string`||t.timeout!==void 0&&(typeof t.timeout!=`number`||!(t.timeout>0))||t.type===`sse`)return;let i={enabled:t.enabled,exposure:n,toolExposure:r},a=t.type;if(typeof t.url==`string`&&(a===void 0||a===`http`||a===`streamable-http`))return!za(t.url)||t.headers!==void 0&&!Ra(t.headers)?void 0:i;if(typeof t.command==`string`&&(a===void 0||a===`stdio`))return t.args!==void 0&&!(Array.isArray(t.args)&&t.args.every(e=>typeof e==`string`))||t.env!==void 0&&!Ra(t.env)||t.cwd!==void 0&&typeof t.cwd!=`string`?void 0:i}function Va(e,t){e.error=e.error===void 0?t:`${e.error}; ${t}`}function Ha(e,t,n,r,i){if(t===void 0){n!==void 0&&Va(i,n);return}let a;try{a=JSON.parse(t)}catch(t){Va(i,`${e} is not valid JSON (${t instanceof Error?t.message:String(t)})`);return}if(!$(a)||a.mcpServers!==void 0&&!$(a.mcpServers)){Va(i,`${e} must contain a JSON object with an "mcpServers" object`);return}typeof a.autoEnableCodemode==`boolean`?i.autoEnableCodemode=a.autoEnableCodemode:a.autoEnableCodemode!==void 0&&Va(i,`${e}: autoEnableCodemode must be a boolean`);for(let[e,t]of Object.entries(a.mcpServers??{})){let n=Ba(e,t);if(n===void 0||r===`project`&&$(t)&&`url`in t&&t.auth)continue;let a=`mcp__${e.replace(/-/g,`_`)}`;[...i.servers.entries()].some(([t,n])=>t!==e&&n.namespace===a)||i.servers.set(e,{namespace:a,server:n})}}function Ua(e,t){let n={autoEnableCodemode:void 0,servers:/* @__PURE__ */ new Map,error:void 0};Ha(e.path,e.raw,e.readError,`global`,n),Ha(t.path,t.raw,t.readError,`project`,n);let r=[...n.servers.values()].some(({server:e})=>e.enabled!==!1&&(/* @__PURE__ */ new Set([e.exposure??`codemode`,...Object.values(e.toolExposure??{})])).has(`codemode`));return{autoEnablesCodemode:n.autoEnableCodemode!==!1&&r,...n.error===void 0?{}:{error:n.error}}}function Wa(e,t){let n=e=>{try{return{path:e,raw:T(e,`utf8`)}}catch(t){return t.code===`ENOENT`?{path:e,raw:void 0}:{path:e,raw:void 0,readError:`${e} could not be read (${t instanceof Error?t.message:String(t)})`}}};return Ua(n(O(e,`mcp.json`)),n(O(t,`.pi`,`mcp.json`)))}function Ga(e,t){let n=t.error===void 0?e:{...e,mcpError:t.error};return e.activation===`active`||!t.autoEnablesCodemode?n:{...e,activation:`active`,source:`mcp`,...t.error===void 0?{}:{mcpError:t.error}}}function Ka(e,t,n){return!e.present||t===`disabled`||n!==`active`?ma:`subagents`}function qa(e,t){return e!==void 0&&e!==`off`&&e!==t}function Ja(e=process.argv){let t=e[1];if(t===void 0||t===``)return{present:!1,how:`no-entry`};let n;try{n=E(t)}catch{return{present:!1,how:`unresolvable-entry`}}let r=ae(n);for(let e of Ya){let t=O(r,...e);try{if(ee(t).isDirectory())return{present:!0,how:`found`}}catch{}}return{present:!1,how:`not-found`}}const Ya=[[`..`,`extensions`,`codemode`],[`..`,`..`,`extensions`,`codemode`],[`extensions`,`codemode`]];function Xa(e,t,n,r,i=process.cwd()){let a=O(e,ua),o=()=>{let a=t??Ja(),o=n??wa(e,i),s=r??Ma(e,i);return{surfaceMode:Ka(a,o.switch,s.activation),codemode:a,codemodeSwitch:o,codemodeActivation:s}},s;try{s=T(a,`utf8`)}catch{return{...o(),source:`default`}}let c;try{c=JSON.parse(s)}catch(e){return{surfaceMode:o().surfaceMode,source:`invalid`,error:`${a} is not valid JSON (${e instanceof Error?e.message:String(e)})`}}if(typeof c!=`object`||!c||Array.isArray(c))return{surfaceMode:o().surfaceMode,source:`invalid`,error:`${a} must contain a JSON object`};let l=c.surfaceMode;if(l===void 0)return{...o(),source:`default`};if(typeof l!=`string`)return{surfaceMode:o().surfaceMode,source:`invalid`,error:`${a}: "surfaceMode" must be a string, received ${typeof l}`};if(!X.includes(l))return{surfaceMode:o().surfaceMode,source:`invalid`,error:`${a}: "surfaceMode" must be one of ${X.join(` | `)}, received ${JSON.stringify(l)}`};let u=o();return{surfaceMode:l,source:`file`,detected:u.surfaceMode,codemode:u.codemode,codemodeSwitch:u.codemodeSwitch,codemodeActivation:u.codemodeActivation}}function Za(e,t){let n=O(e,ua);if(typeof t!=`string`||!X.includes(t))return{ok:!1,path:n,error:`surfaceMode must be one of ${X.join(` | `)}`};let r=t,i;try{i=T(n,`utf8`)}catch{i=void 0}let a={};if(i!==void 0){let e;try{e=JSON.parse(i)}catch(e){return{ok:!1,path:n,error:`${n} is not valid JSON (${e instanceof Error?e.message:String(e)}), so it was left alone`}}if(typeof e!=`object`||!e||Array.isArray(e))return{ok:!1,path:n,error:`${n} must contain a JSON object, so it was left alone`};a=e}let o=a.surfaceMode,s=typeof o==`string`?o:void 0;if(s===r)return{ok:!0,path:n,previous:s,changed:!1};try{te(n,JSON.stringify({...a,surfaceMode:r},null,2)+`
49
- `,`utf8`)}catch(e){return{ok:!1,path:n,error:`could not write ${n} (${e instanceof Error?e.message:String(e)})`}}return{ok:!0,path:n,previous:s,changed:!0}}function Qa(e){return[...new Set(e)]}function $a(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 eo(e,t){let n=e=>oa.includes(e);if(t===`all-but-ptc`)return Qa(e.filter(n));let r=new Set(L);return Qa(e.filter(e=>n(e)||!r.has(e)))}function to(e){let{mode:t,defaultMode:n,active:r,manual:i}=e;return t===`tui`?!n&&!i?{enter:!1,reason:`config-off`}:oa.some(e=>r.includes(e))?!i&&sa.filter(e=>!r.includes(e)).length>0?{enter:!1,reason:`restricted-session`}:{enter:!0,base:Qa(r),loadout:eo(r,e.hide??`builtins-only`)}:{enter:!1,reason:`tools-unavailable`}:{enter:!1,reason:`not-tui`}}function no(e,t){return e.enabled&&e.base!==void 0?e.base:t}function ro(e,t){return!e.enabled||e.ourLoadout===void 0?!1:!$a(e.ourLoadout,t)}function io(e,t){let n=t.length>0&&t.every(e=>oa.includes(e));return e?.enabled===!0&&n&&e.base!==void 0?{base:e.base,restoreFirst:!0}:{base:t,restoreFirst:!1}}function ao(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(`
48
+ `,`&#10;`).replaceAll(`\r`,`&#13;`)}function ia(e){return Buffer.byteLength(ra(e),`utf8`)}const aa=Buffer.byteLength(`<bg-task-notifications batch-id="${`0`.repeat(26)}" delivered-at-ms="${`9`.repeat(13)}">\n\n</bg-task-notifications>`,`utf8`),oa=[`ptc_run_code`,`ptc_workflow`],sa=[`read`,`bash`,`edit`,`write`],ca=`ptc-mode`,la=`ptc-mode`,ua=`ptc.json`,da=`builtins-only`;function fa(){return{enabled:!1,base:void 0,ourLoadout:void 0}}function pa(e){let t=O(e,ua),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}`}}const X=`builtin:codemode`;function Z(e){return e.split(j).join(`/`)}function ma(e){return Z(e.startsWith(`./`)||e.startsWith(`.\\`)?e.slice(2):e)}function ha(e,t,n){let r=Z(k(n,e)),i=ie(e),a=Z(e);return t.some(e=>{let t=Z(e);return be(r,t)||be(i,t)||be(a,t)})}function ga(e,t,n){if(t.length===0)return!1;let r=Z(k(n,e)),i=Z(e);return t.some(e=>{let t=ma(e);return t===r||t===i})}function _a(e){let t=[];for(let n of e){if(typeof n!=`string`)continue;let e=n[0];(e===`+`||e===`-`||e===`!`)&&t.push({sign:e,target:n.slice(1)})}return t}function va(e,t){if(!Array.isArray(e))return;let n;for(let{sign:r,target:i}of _a(e)){let e=r!==`-`&&r!==`!`;(r===`+`||r===`-`?ga(X,[i],t):ha(X,[i],t))&&(n=e?`enabled`:`disabled`)}return n}function ya(e,t){if(!Array.isArray(e))return;let n=_a(e),r=e=>n.filter(t=>t.sign===e).map(e=>e.target),i=r(`!`),a=r(`+`),o=r(`-`),s;return ha(X,i,t)&&(s=`disabled`),ga(X,a,t)&&(s=`enabled`),ga(X,o,t)&&(s=`disabled`),s}function ba(e){for(let t=0;t<e.length;t+=1){let n=e[t];if((n===`-e`||n===`--extension`)&&e[t+1]===X)return!0}return!1}function xa(e){let t;try{t=T(e,`utf8`)}catch{return{value:void 0}}try{let n=JSON.parse(t);return typeof n!=`object`||!n||Array.isArray(n)?{value:void 0,error:`${e} must contain a JSON object`}:{value:n}}catch(t){return{value:void 0,error:`${e} is not valid JSON (${t instanceof Error?t.message:String(t)})`}}}function Sa(e,t,n,r={project:process.cwd(),user:process.cwd()}){let i=e.slice(1);if(ba(i))return{switch:`enabled`,source:`cli`};if(i.some(e=>e===`-ne`||e===`--no-extensions`))return{switch:`disabled`,source:`cli`};let a=t?.extensions,o=va(a,r.project);if(o!==void 0)return{switch:o,source:`project`};let s=n?.extensions,c=ya(s,r.user);return c===void 0?{switch:`absent`,source:`default`}:{switch:c,source:`user`}}function Ca(e,t,n=process.argv){let r=xa(O(t,`.pi`,`settings.json`)),i=xa(O(e,`settings.json`)),a=Sa(n,r.value,i.value,{project:O(t,`.pi`),user:e}),o=r.error??i.error;return o===void 0?a:{...a,source:`invalid`,error:o}}function wa(e){return{activation:e.includes(`codemode`)?`active`:`inactive`,source:`loadout`}}function Ta(e,t,n){return Ra(wa(n),La(e,t))}const Ea=[`codemode`,`deferred`,`direct`,`hidden`],Da={"codemode-deferred":`codemode`},Oa=/^[A-Za-z0-9_-]+$/;function ka(e){return typeof e==`string`?Da[e]??e:e}function Aa(e){return typeof e==`string`&&Ea.includes(e)}function Q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function ja(e){return Q(e)&&Object.values(e).every(e=>typeof e==`string`)}function Ma(e){return URL.canParse(e)?/^https?:$/.test(new URL(e).protocol):!1}function Na(e,t){if(!Oa.test(e)||!Q(t))return;let n=ka(t.exposure),r=Q(t.toolExposure)?Object.fromEntries(Object.entries(t.toolExposure).map(([e,t])=>[e,ka(t)])):t.toolExposure;if(n!==void 0&&!Aa(n)||r!==void 0&&(!Q(r)||!Object.values(r).every(e=>Aa(e)))||t.enabled!==void 0&&typeof t.enabled!=`boolean`||t.description!==void 0&&typeof t.description!=`string`||t.timeout!==void 0&&(typeof t.timeout!=`number`||!(t.timeout>0))||t.type===`sse`)return;let i={enabled:t.enabled,exposure:n,toolExposure:r},a=t.type;if(typeof t.url==`string`&&(a===void 0||a===`http`||a===`streamable-http`))return!Ma(t.url)||t.headers!==void 0&&!ja(t.headers)?void 0:i;if(typeof t.command==`string`&&(a===void 0||a===`stdio`))return t.args!==void 0&&!(Array.isArray(t.args)&&t.args.every(e=>typeof e==`string`))||t.env!==void 0&&!ja(t.env)||t.cwd!==void 0&&typeof t.cwd!=`string`?void 0:i}function Pa(e,t){e.error=e.error===void 0?t:`${e.error}; ${t}`}function Fa(e,t,n,r,i){if(t===void 0){n!==void 0&&Pa(i,n);return}let a;try{a=JSON.parse(t)}catch(t){Pa(i,`${e} is not valid JSON (${t instanceof Error?t.message:String(t)})`);return}if(!Q(a)||a.mcpServers!==void 0&&!Q(a.mcpServers)){Pa(i,`${e} must contain a JSON object with an "mcpServers" object`);return}typeof a.autoEnableCodemode==`boolean`?i.autoEnableCodemode=a.autoEnableCodemode:a.autoEnableCodemode!==void 0&&Pa(i,`${e}: autoEnableCodemode must be a boolean`);for(let[e,t]of Object.entries(a.mcpServers??{})){let n=Na(e,t);if(n===void 0||r===`project`&&Q(t)&&`url`in t&&t.auth)continue;let a=`mcp__${e.replace(/-/g,`_`)}`;[...i.servers.entries()].some(([t,n])=>t!==e&&n.namespace===a)||i.servers.set(e,{namespace:a,server:n})}}function Ia(e,t){let n={autoEnableCodemode:void 0,servers:/* @__PURE__ */ new Map,error:void 0};Fa(e.path,e.raw,e.readError,`global`,n),Fa(t.path,t.raw,t.readError,`project`,n);let r=[...n.servers.values()].some(({server:e})=>e.enabled!==!1&&(/* @__PURE__ */ new Set([e.exposure??`codemode`,...Object.values(e.toolExposure??{})])).has(`codemode`));return{autoEnablesCodemode:n.autoEnableCodemode!==!1&&r,...n.error===void 0?{}:{error:n.error}}}function La(e,t){let n=e=>{try{return{path:e,raw:T(e,`utf8`)}}catch(t){return t.code===`ENOENT`?{path:e,raw:void 0}:{path:e,raw:void 0,readError:`${e} could not be read (${t instanceof Error?t.message:String(t)})`}}};return Ia(n(O(e,`mcp.json`)),n(O(t,`.pi`,`mcp.json`)))}function Ra(e,t){let n=t.error===void 0?e:{...e,mcpError:t.error};return e.activation===`active`||!t.autoEnablesCodemode?n:{...e,activation:`active`,source:`mcp`,...t.error===void 0?{}:{mcpError:t.error}}}function za(e,t,n){return!e.present||t===`disabled`||n!==`active`?`full`:`subagents`}function Ba(e=process.argv){let t=e[1];if(t===void 0||t===``)return{present:!1,how:`no-entry`};let n;try{n=E(t)}catch{return{present:!1,how:`unresolvable-entry`}}let r=ae(n);for(let e of Va){let t=O(r,...e);try{if(ee(t).isDirectory())return{present:!0,how:`found`}}catch{}}return{present:!1,how:`not-found`}}const Va=[[`..`,`extensions`,`codemode`],[`..`,`..`,`extensions`,`codemode`],[`extensions`,`codemode`]];function Ha(e,t,n,r,i,a=process.cwd()){let o=n??Ba(),s=r??Ca(e,a),c=i??Ta(e,a,t);return{surfaceMode:za(o,s.switch,c.activation),codemode:o,codemodeSwitch:s,codemodeActivation:c}}function Ua(e){let t=O(e,ua),n;try{n=T(t,`utf8`)}catch{return}let r;try{r=JSON.parse(n)}catch{return}if(typeof r!=`object`||!r||Array.isArray(r))return;let i=r.surfaceMode;return i===void 0?void 0:{path:t,value:i}}function Wa(e){return[...new Set(e)]}function Ga(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 Ka(e,t){let n=e=>oa.includes(e);if(t===`all-but-ptc`)return Wa(e.filter(n));let r=new Set(L);return Wa(e.filter(e=>n(e)||!r.has(e)))}function qa(e){let{mode:t,defaultMode:n,active:r,manual:i}=e;return t===`tui`?!n&&!i?{enter:!1,reason:`config-off`}:oa.some(e=>r.includes(e))?!i&&sa.filter(e=>!r.includes(e)).length>0?{enter:!1,reason:`restricted-session`}:{enter:!0,base:Wa(r),loadout:Ka(r,e.hide??`builtins-only`)}:{enter:!1,reason:`tools-unavailable`}:{enter:!1,reason:`not-tui`}}function Ja(e,t){return e.enabled&&e.base!==void 0?e.base:t}function Ya(e,t){return!e.enabled||e.ourLoadout===void 0?!1:!Ga(e.ourLoadout,t)}function Xa(e,t){let n=t.length>0&&t.every(e=>oa.includes(e));return e?.enabled===!0&&n&&e.base!==void 0?{base:e.base,restoreFirst:!0}:{base:t,restoreFirst:!1}}function Za(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(`
50
49
  `);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(`, `)}, plus the`,'parallel binding `tools["pi.dispatch"](args)` — always bound, and its dot name needs string',"indexing (`tools.pi.dispatch` does not exist). The run's actual bound names are on the","`ptcBindings` global, so a program never has to guess what is bound this run.",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(`
51
- `)}const oo=[`read`,`bash`],so=`Use the read tool to load a skill's file when the task matches its description.`,co=`Use tools.read({ path }) inside a ptc_run_code program to load a skill's file when the task matches its description.`;function lo(e){return!oo.some(t=>e.includes(t))}function uo(e,t=v){let n=t([...e],`read`).trim();return n.length===0?``:n.includes(so)?n.replace(so,co):`${co}\n\n${n}`}const fo=x.Object({task_id:x.Optional(x.String({description:`Id of the background task to poll with ptc_task_output. Present ONLY for a background call; absent once the call has already finished.`})),status:x.String({description:`"background" when this call returned a handle to poll, otherwise the dispatcher's own outcome status ("fulfilled").`}),report_channel:x.String({description:'Which channel delivered the child report: "prompt-json" (a fenced JSON block on the child\'s final message), "tool" (the child called the report tool), "none" (no report arrived — either the child did not comply with the contract, or this is a background handle that has not finished), or "opted-out" (the agent\'s frontmatter set `childReport: false`, so it was never asked). ALWAYS present, so an absent `report` key is never ambiguous — "none" and "opted-out" are different claims about the child.'}),exit_code:x.Optional(x.Number({description:`The child's exit code. Present ONLY for a call that already finished.`})),report:x.Optional(x.Object({summary:x.String({description:`One line, in the child's own words, of what it concluded.`}),findings:x.Array(x.Object({what:x.String({description:`The claim this finding makes.`}),evidence:x.String({description:`The independent thing that supports the claim.`})})),files_touched:x.Array(x.String({description:`A path the child created or modified.`})),usage:x.Object({input:x.Number({description:`Input tokens, counted by the host.`}),output:x.Number({description:`Output tokens, counted by the host.`}),cost:x.Number({description:`Total cost, counted by the host.`}),turns:x.Number({description:`Assistant turns, counted by the host.`})})}))});function po(e){let t=e.taskId;return typeof t==`string`?t:void 0}function mo(e){return g({name:`ptc_subagent`,label:`PTC Subagent`,description:[`Start a fresh pi agent as a child process: it gets its own context, its own model and its own tools, and it cannot see or change this session except through what it returns. Use it for work that would otherwise flood this context: a survey, a second opinion, or a task that can run while you do something else.`].join(`
52
- `),promptSnippet:`Start a fresh pi agent as a child process (foreground or background)`,promptGuidelines:[`Prefer one subagent over many tool calls when the work is a question with an answer rather than a fact you can look up, or when the raw output would be larger than the answer.`],parameters:$t,outputSchema:fo,async execute(t,n){let r=await Xt(n,{callId:0,cwd:n.cwd??e.cwd,depth:e.depth,maxDispatchDepth:e.maxDispatchDepth,...e.parentTaskId===void 0?{}:{parentTaskId:e.parentTaskId}},e.getDispatchDeps?.()??{}),i=po(r),a=i===void 0?{taskId:void 0,status:r.status,exitCode:r.exitCode}:{taskId:i,status:`background`,exitCode:void 0};if(i!==void 0){let e={task_id:i,status:a.status,report_channel:`none`};return{content:[{type:`text`,text:`Started background task `+i}],details:a,structuredContent:e}}if(r.status===`rejected`)throw Error(`ptc_subagent refused the call: `+r.errorMessage);let o=r.report,s=a.exitCode===void 0?{status:a.status,report_channel:r.reportChannel}:o===void 0?{status:a.status,exit_code:a.exitCode,report_channel:r.reportChannel}:{status:a.status,exit_code:a.exitCode,report_channel:r.reportChannel,report:o};return{content:[{type:`text`,text:[Tr(o,r.reportChannel),r.text].filter(e=>e.length>0).join(`
50
+ `)}const Qa=[`read`,`bash`],$a=`Use the read tool to load a skill's file when the task matches its description.`,eo=`Use tools.read({ path }) inside a ptc_run_code program to load a skill's file when the task matches its description.`;function to(e){return!Qa.some(t=>e.includes(t))}function no(e,t=v){let n=t([...e],`read`).trim();return n.length===0?``:n.includes($a)?n.replace($a,eo):`${eo}\n\n${n}`}const ro=x.Object({task_id:x.Optional(x.String({description:`Id of the background task to poll with ptc_task_output. Present ONLY for a background call; absent once the call has already finished.`})),status:x.String({description:`"background" when this call returned a handle to poll, otherwise the dispatcher's own outcome status ("fulfilled").`}),report_channel:x.String({description:'Which channel delivered the child report: "prompt-json" (a fenced JSON block on the child\'s final message), "tool" (the child called the report tool), "none" (no report arrived — either the child did not comply with the contract, or this is a background handle that has not finished), or "opted-out" (the agent\'s frontmatter set `childReport: false`, so it was never asked). ALWAYS present, so an absent `report` key is never ambiguous — "none" and "opted-out" are different claims about the child.'}),exit_code:x.Optional(x.Number({description:`The child's exit code. Present ONLY for a call that already finished.`})),report:x.Optional(x.Object({summary:x.String({description:`One line, in the child's own words, of what it concluded.`}),findings:x.Array(x.Object({what:x.String({description:`The claim this finding makes.`}),evidence:x.String({description:`The independent thing that supports the claim.`})})),files_touched:x.Array(x.String({description:`A path the child created or modified.`})),usage:x.Object({input:x.Number({description:`Input tokens, counted by the host.`}),output:x.Number({description:`Output tokens, counted by the host.`}),cost:x.Number({description:`Total cost, counted by the host.`}),turns:x.Number({description:`Assistant turns, counted by the host.`})})}))});function io(e){let t=e.taskId;return typeof t==`string`?t:void 0}function ao(e){return g({name:`ptc_subagent`,label:`PTC Subagent`,description:[`Start a fresh pi agent as a child process: it gets its own context, its own model and its own tools, and it cannot see or change this session except through what it returns. Use it for work that would otherwise flood this context: a survey, a second opinion, or a task that can run while you do something else.`].join(`
51
+ `),promptSnippet:`Start a fresh pi agent as a child process (foreground or background)`,promptGuidelines:[`Prefer one subagent over many tool calls when the work is a question with an answer rather than a fact you can look up, or when the raw output would be larger than the answer.`],parameters:$t,outputSchema:ro,async execute(t,n){let r=await Xt(n,{callId:0,cwd:n.cwd??e.cwd,depth:e.depth,maxDispatchDepth:e.maxDispatchDepth,...e.parentTaskId===void 0?{}:{parentTaskId:e.parentTaskId}},e.getDispatchDeps?.()??{}),i=io(r),a=i===void 0?{taskId:void 0,status:r.status,exitCode:r.exitCode}:{taskId:i,status:`background`,exitCode:void 0};if(i!==void 0){let e={task_id:i,status:a.status,report_channel:`none`};return{content:[{type:`text`,text:`Started background task `+i}],details:a,structuredContent:e}}if(r.status===`rejected`)throw Error(`ptc_subagent refused the call: `+r.errorMessage);let o=r.report,s=a.exitCode===void 0?{status:a.status,report_channel:r.reportChannel}:o===void 0?{status:a.status,exit_code:a.exitCode,report_channel:r.reportChannel}:{status:a.status,exit_code:a.exitCode,report_channel:r.reportChannel,report:o};return{content:[{type:`text`,text:[Tr(o,r.reportChannel),r.text].filter(e=>e.length>0).join(`
53
52
 
54
- `)}],details:a,structuredContent:s}}})}function ho(e){return e.length<=200?e:`…`+e.slice(-199)}const go=x.Object({summary:x.String({description:`One line, in your own words, of what you concluded.`}),findings:x.Array(x.Object({what:x.String({description:`The claim this finding makes.`}),evidence:x.String({description:`The independent thing that supports the claim — what you read, ran or checked. Not the claim restated.`})})),files_touched:x.Array(x.String({description:`A path you created or modified. Empty if you touched nothing.`}))}),_o=[`Hand the host a child report: what you did in this run, in the shape it can read without parsing English.`,`Call it once, at the end. The exact shape is stated in your instructions, which also tell you what to do if this tool is unavailable to you.`,`Evidence is the point of a finding. Put in the independent thing you read, ran or checked — the claim restated is not evidence, and a finding with none is worse than no finding.`,`files_touched lists only paths you actually created or modified; leave it empty rather than guessing.`,`Your prose answer is kept alongside this report and is never replaced by it, so still answer in words.`,`Do not report usage or token counts — those are measured by the host, and anything you send under them is discarded.`].join(`
55
- `);function vo(e){return g({name:Ie,label:`Child Report`,description:_o,promptSnippet:`Hand the host a child report when you finish`,promptGuidelines:[`Call ptc_child_report once before you finish, even when the answer is "I changed nothing": a child that reports an empty result is a different thing from a child that never reported, and only the first one says so.`],parameters:go,outputSchema:go,defaultActive:e,renderCall(e,t){let n=e,r=typeof n?.summary==`string`?n.summary:``;return new q([{left:t.fg(`toolTitle`,t.bold(`Child report`))+(r===``?``:` `+t.fg(`dim`,ho(r)))}])},renderResult(){return new q([{left:`child report recorded`}])},async execute(e,t){let n={summary:t.summary,findings:t.findings.map(e=>({what:e.what,evidence:e.evidence})),files_touched:[...t.files_touched]};return{content:[{type:`text`,text:`child report recorded (`+String(n.findings.length)+` findings)`}],details:void 0,structuredContent:n}}})}const yo=da;function bo(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: ${U(e).join(`, `)||`(none)`}.`,`Run /ptc off to restore the normal tool set.`].join(`
56
- `)}function xo(e,t={}){let n=fa(),r=t.surfaceMode===void 0?Xa(y(),t.codemode,t.codemodeSwitch):{surfaceMode:t.surfaceMode,source:`file`},i=!1,a=new Mi,o=()=>no(n,e.getActiveTools()),s=Fn(),c=In();if(r.surfaceMode===`off`)return;let l=t.backgroundRuntime??Qi({concurrency:ze({}).dispatchConcurrency}),u=!1,d=e=>{console.warn(`[pi-ptc.background] `+e)},f=t=>{if(t.length!==0){if(u){e.sendMessage({customType:`bg-task-notification`,content:t,display:!1},{deliverAs:`steer`});return}e.sendUserMessage(t)}},p=async e=>{let{items:t,acks:n}=await l.drainNotifications(e),r=t.filter(e=>ea(e.record));for(let e of na(r))f(ta(e,{batchId:De(),deliveredAtMs:l.clock()}));await l.acknowledgeNotifications(n)},m=async()=>{let e=await l.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]},h=async()=>{for(let e of await m())await p(e)},g=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 p(e)};l.pipeline.onIdleWake(e=>{p(e).catch(e=>{d(`notification delivery failed: `+(e instanceof Error?e.message:String(e)))})}),r.surfaceMode===`full`&&(e.registerTool(Rr({getBindingSourceNames:o,getPool:()=>a.get(`run_code`),depth:s,...c===void 0?{}:{parentTaskId:c},getDispatchDeps:()=>l.dispatchDeps})),e.registerTool(Wr({getBindingSourceNames:o,getPool:()=>a.get(`workflow`),depth:s,...c===void 0?{}:{parentTaskId:c},getDispatchDeps:()=>l.dispatchDeps}))),r.surfaceMode===`subagents`&&e.registerTool(mo({cwd:process.cwd(),depth:s,maxDispatchDepth:I.maxDispatchDepth,...c===void 0?{}:{parentTaskId:c},getDispatchDeps:()=>l.dispatchDeps})),e.registerTool(Ci(l.registry)),e.registerTool(Oi(l.registry,l.outputStorage)),e.registerTool(ji(l.registry,l.lifecycle,{clock:l.clock})),e.registerTool(vo(s>0));let _=!1,v=t=>{_||r.source!==`file`&&r.codemodeActivation?.activation===`inactive`&&e.getActiveTools().includes(`codemode`)&&(_=!0,t.ui.notify(`pi-ptc-subagents: pi's codemode is active in this session, but nothing this package reads predicted it — no tool list names it and no readable mcp.json auto-enables it — so the surface defaulted to full, and your model is being offered two orchestration tools. pi activates codemode at runtime in ways no settings file records (its MCP extension is one). Set an explicit "surfaceMode" in ptc.json to pick one.`,`warning`))};e.on(`turn_start`,async(e,t)=>{u=!0,v(t)}),e.on(`agent_settled`,async()=>{u=!1;try{await h()}catch(e){d(`agent_settled delivery failed: `+(e instanceof Error?e.message:String(e)))}}),e.on(`turn_end`,async()=>{let e=a;a=new Mi,await e.drain()});let b=e=>{e.ui.setStatus(la,n.enabled?e.ui.theme.fg(`accent`,`PTC`):void 0)},x=(t,r,a,o)=>{n.enabled=!0,n.base=t,n.ourLoadout=r,e.setActiveTools([...r]),i=!0,e.appendEntry(ca,{enabled:!0,base:[...t]}),b(a),a.ui.notify(o,`info`)},S=(t,r)=>{let a=n.base;n.enabled=!1,n.base=void 0,n.ourLoadout=void 0,i=!1,a!==void 0&&e.setActiveTools([...a]),e.appendEntry(r.customType,{enabled:!1}),b(t),r.announce&&t.ui.notify(`PTC mode off — built-in tools are directly callable again.`,`info`)},C=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}}}},w=async(e,t)=>{let r=y();if(e===``){let e=Xa(r),n=e.detected===void 0||e.detected===e.surfaceMode?``:` (detection would say `+JSON.stringify(e.detected)+`)`;t.ui.notify(`Extension surface: `+JSON.stringify(e.surfaceMode)+`, from `+e.source+n+`. Set it with /ptc surface `+X.join(`|`)+`; the change needs a reload, which this command performs.`,`info`);return}let i=Za(r,e);if(!i.ok){t.ui.notify(`pi-ptc-subagents: `+i.error+`. Nothing was changed.`,`warning`);return}if(!i.changed){t.ui.notify(`The surface is already `+JSON.stringify(e)+` — nothing written, nothing reloaded.`,`info`);return}let a=[`Surface `+(i.previous===void 0?`(unset, so detected)`:JSON.stringify(i.previous))+` → `+JSON.stringify(e)+` in `+i.path+`. Reloading so it takes effect now.`];n.enabled&&e!==`full`&&a.push(`PTC mode was ON and cannot survive this surface — run /ptc on afterwards.`),e===`subagents`&&a.push(`subagents hands orchestration to pi's codemode, so it needs codemode in this session's tool list; if it is not there the startup warning will say so.`),t.ui.notify(a.join(`
57
- `),`info`),await t.reload()};e.registerCommand(`ptc`,{description:`Show or toggle PTC mode, or set the extension surface (/ptc surface off|subagents|full)`,handler:async(t,r)=>{let i=t.trim().toLowerCase();if(i===`surface`||i.startsWith(`surface `)){await w(i.slice(7).trim(),r);return}if(i===`off`){if(!n.enabled){r.ui.notify(`PTC mode is already off.`,`info`);return}S(r,{announce:!0,customType:ca});return}if(i!==`on`){r.ui.notify(n.enabled?`PTC mode is ON — run /ptc off to restore built-in tools.\nBindings: ${U(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=to({mode:r.mode,defaultMode:!0,active:e.getActiveTools(),manual:!0,hide:yo});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}x(a.base,a.loadout,r,bo(a.base,yo))}}),e.on(`session_start`,async(t,n)=>{u=!1;let i=await l.bindSession(n.sessionManager?.getSessionDir?.(),e=>n.ui.notify(e,`warning`));try{await g(i)}catch(e){d(`startup reconcile delivery failed: `+(e instanceof Error?e.message:String(e)))}let a=pa(y());a.error!==void 0&&n.ui.notify(`pi-ptc-subagents: ${a.error}`,`warning`),r.error!==void 0&&r.error!==a.error&&n.ui.notify(`pi-ptc-subagents: ${r.error}`,`warning`);let o=r.codemode;if(o!==void 0&&!o.present&&n.ui.notify(`pi-ptc-subagents: no surfaceMode is set, and the codemode probe reported `+o.how+`, so the surface defaults to full. That is the safe direction, not an error. If this pi really does ship pi's own codemode, set "surfaceMode" in ptc.json -- an explicit key always wins over detection.`,`info`),r.codemodeSwitch?.error!==void 0&&n.ui.notify(`pi-ptc-subagents: ${r.codemodeSwitch.error}`,`warning`),r.codemodeActivation?.mcpError!==void 0&&n.ui.notify(`pi-ptc-subagents: ${r.codemodeActivation.mcpError}`,`warning`),r.source===`file`&&r.detected!==void 0&&qa(r.surfaceMode,r.detected)){let e=r.codemodeSwitch;n.ui.notify(`pi-ptc-subagents: surfaceMode is pinned to `+JSON.stringify(r.surfaceMode)+`, but this pi resolves to `+JSON.stringify(r.detected)+(e===void 0?``:` (pi's codemode is on disk and its switch is `+e.switch+`)`)+`. The pinned value is in force. Change it in ptc.json if that is not what you meant.`,`info`)}if(o!==void 0&&r.source!==`file`){let t=e.getAllTools().some(e=>e.name===`codemode`);!o.present&&t&&n.ui.notify(`pi-ptc-subagents: this session registers pi's codemode, but the probe did not find it (`+o.how+`), so the surface defaulted to full, and your model is being offered two orchestration tools. Set an explicit "surfaceMode" in ptc.json to pick one.`,`warning`),o.present&&!t&&n.ui.notify(`pi-ptc-subagents: the codemode probe found pi's codemode on disk, but this session does not register it (--no-extensions or --exclude-tools codemode), so the surface is `+JSON.stringify(r.surfaceMode)+(r.surfaceMode===`subagents`?` with no orchestrator. Set `+JSON.stringify(`surfaceMode`)+` to `+JSON.stringify(ma)+` in ptc.json to use ptc_run_code instead.`:`.`),`warning`)}r.surfaceMode===`subagents`&&!e.getActiveTools().includes(`codemode`)&&n.ui.notify(`pi-ptc-subagents: surfaceMode is subagents, but codemode is not active in this session, so there is no orchestration tool. Add codemode to your pi tool list (the --tools flag or the default tools setting), or set surfaceMode to "full" to use ptc_run_code instead.`,`warning`),v(n);let s=C(n),{base:c,restoreFirst:f}=io(s,e.getActiveTools());f&&e.setActiveTools([...c]);let p=to({mode:n.mode,defaultMode:a.defaultMode,active:c,manual:!1,hide:yo}),m=s?.enabled??p.enter;if(p.enter&&m){x(c,p.loadout,n,bo(c,yo));return}b(n),!p.enter&&p.reason!==`not-tui`&&s===void 0&&p.reason===`restricted-session`&&n.ui.notify(`PTC mode skipped: this session was launched with an explicit tool restriction.`,`info`)}),e.on(`session_shutdown`,async()=>{u=!1;try{await l.shutdown(`session_ended_while_running`)}catch(e){d(`session shutdown failed: `+(e instanceof Error?e.message:String(e)))}});let T=!1,E=(e,t)=>{if(e===void 0)return;if(!n.enabled||!t){T&&=(delete e.sections.skills,!1);return}let r=uo(e.skills);r.length!==0&&(e.sections.skills=r,T=!0)};e.on(`before_agent_start`,async(t,r)=>{let a=t.systemPromptOptions;if(E(a,lo(e.getActiveTools())),n.enabled){if(ro(n,e.getActiveTools())){S(r,{announce:!1,customType:ca}),E(a,lo(e.getActiveTools())),r.ui.notify(`PTC mode off — another extension changed the active tool set.`,`warning`);return}if(i)return i=!1,{message:{customType:`ptc-mode-briefing`,content:ao(U(n.base),yo),display:!1}}}})}export{L as BUILTIN_BINDING_NAMES,Zt as DEFAULT_BINDING_NAMES,I as DEFAULT_CONFIG,da as DEFAULT_HIDE_STRATEGY,o as HOST_FRAME_KIND,Cn as MAX_LINE_CHARS,sa as MODE_REQUIRED_TOOL_NAMES,n as PTC_ERROR_KIND,i as PTC_LOG_LEVEL,ua as PTC_MODE_CONFIG_FILE,ca as PTC_MODE_ENTRY_TYPE,la as PTC_MODE_STATUS_KEY,oa as PTC_MODE_TOOL_NAMES,co as PTC_SKILL_LOAD_INSTRUCTION,oo as SKILL_READING_TOOL_NAMES,X as SURFACE_MODES,Mi as TurnPools,Le as WORKER_ENV_ALLOW_LIST,t as WORKER_FRAME_KIND,on as WorkerPool,no as bindingSource,ao as buildModeInstruction,uo as buildPtcSkillsSection,nn as createBuiltinBindings,Re as createWorkerEnv,to as decideModeEntry,xo as default,ro as detectExternalLoadoutChange,Be as effectiveTimeoutMs,fa as initialModeState,eo as modeLoadout,Ja as probeCodemodePresence,wa as readCodemodeSwitch,pa as readDefaultModeConfig,Xa as readSurfaceModeConfig,Pn as renderModelValue,io as resolveBaseOnStart,ja as resolveCodemodeActivation,Ca as resolveCodemodeSwitch,ze as resolveConfig,hn as runPtcProgram,$a as sameToolSet,H as sanitizeText,Za as setSurfaceMode,lo as skillsSectionDropped,Tn as stripAnsi,qa as surfaceModeConflict};
53
+ `)}],details:a,structuredContent:s}}})}function oo(e){return e.length<=200?e:`…`+e.slice(-199)}const so=x.Object({summary:x.String({description:`One line, in your own words, of what you concluded.`}),findings:x.Array(x.Object({what:x.String({description:`The claim this finding makes.`}),evidence:x.String({description:`The independent thing that supports the claim — what you read, ran or checked. Not the claim restated.`})})),files_touched:x.Array(x.String({description:`A path you created or modified. Empty if you touched nothing.`}))}),co=[`Hand the host a child report: what you did in this run, in the shape it can read without parsing English.`,`Call it once, at the end. The exact shape is stated in your instructions, which also tell you what to do if this tool is unavailable to you.`,`Evidence is the point of a finding. Put in the independent thing you read, ran or checked — the claim restated is not evidence, and a finding with none is worse than no finding.`,`files_touched lists only paths you actually created or modified; leave it empty rather than guessing.`,`Your prose answer is kept alongside this report and is never replaced by it, so still answer in words.`,`Do not report usage or token counts — those are measured by the host, and anything you send under them is discarded.`].join(`
54
+ `);function lo(e){return g({name:Ie,label:`Child Report`,description:co,promptSnippet:`Hand the host a child report when you finish`,promptGuidelines:[`Call ptc_child_report once before you finish, even when the answer is "I changed nothing": a child that reports an empty result is a different thing from a child that never reported, and only the first one says so.`],parameters:so,outputSchema:so,defaultActive:e,renderCall(e,t){let n=e,r=typeof n?.summary==`string`?n.summary:``;return new q([{left:t.fg(`toolTitle`,t.bold(`Child report`))+(r===``?``:` `+t.fg(`dim`,oo(r)))}])},renderResult(){return new q([{left:`child report recorded`}])},async execute(e,t){let n={summary:t.summary,findings:t.findings.map(e=>({what:e.what,evidence:e.evidence})),files_touched:[...t.files_touched]};return{content:[{type:`text`,text:`child report recorded (`+String(n.findings.length)+` findings)`}],details:void 0,structuredContent:n}}})}const $=da;function uo(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: ${U(e).join(`, `)||`(none)`}.`,`Run /ptc off to restore the normal tool set.`].join(`
55
+ `)}function fo(e,t={}){let n=fa(),r,i=!1,a=new Mi,o=()=>Ja(n,e.getActiveTools()),s=Fn(),c=In(),l=t.backgroundRuntime??Qi({concurrency:ze({}).dispatchConcurrency}),u=!1,d=e=>{console.warn(`[pi-ptc.background] `+e)},f=t=>{if(t.length!==0){if(u){e.sendMessage({customType:`bg-task-notification`,content:t,display:!1},{deliverAs:`steer`});return}e.sendUserMessage(t)}},p=async e=>{let{items:t,acks:n}=await l.drainNotifications(e),r=t.filter(e=>ea(e.record));for(let e of na(r))f(ta(e,{batchId:De(),deliveredAtMs:l.clock()}));await l.acknowledgeNotifications(n)},m=async()=>{let e=await l.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]},h=async()=>{for(let e of await m())await p(e)},g=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 p(e)};l.pipeline.onIdleWake(e=>{p(e).catch(e=>{d(`notification delivery failed: `+(e instanceof Error?e.message:String(e)))})});function _(t){if(t.surfaceMode===`full`||t.surfaceMode===`subagents`){let n=t.surfaceMode===`full`?`direct`:`codemode`;e.registerTool(Rr({getBindingSourceNames:o,getPool:()=>a.get(`run_code`),depth:s,exposure:n,...c===void 0?{}:{parentTaskId:c},getDispatchDeps:()=>l.dispatchDeps})),e.registerTool(Wr({getBindingSourceNames:o,getPool:()=>a.get(`workflow`),depth:s,exposure:n,...c===void 0?{}:{parentTaskId:c},getDispatchDeps:()=>l.dispatchDeps}))}t.surfaceMode===`subagents`&&e.registerTool(ao({cwd:process.cwd(),depth:s,maxDispatchDepth:I.maxDispatchDepth,...c===void 0?{}:{parentTaskId:c},getDispatchDeps:()=>l.dispatchDeps})),e.registerTool(Ci(l.registry)),e.registerTool(Oi(l.registry,l.outputStorage)),e.registerTool(ji(l.registry,l.lifecycle,{clock:l.clock})),e.registerTool(lo(s>0))}let v=!1,b=t=>{v||r.codemodeActivation.activation===`inactive`&&e.getActiveTools().includes(`codemode`)&&(v=!0,t.ui.notify(`pi-ptc-subagents: pi's codemode is active in this session, but nothing this package reads predicted it — no tool list names it and no readable mcp.json auto-enables it — so the surface defaulted to full, and your model is being offered two orchestration tools. pi activates codemode at runtime in ways no settings file records (its MCP extension is one). Run pi with codemode active (-t codemode, or add it to defaultTools) to get the subagents surface instead.`,`warning`))};e.on(`turn_start`,async(e,t)=>{u=!0,b(t)}),e.on(`agent_settled`,async()=>{u=!1;try{await h()}catch(e){d(`agent_settled delivery failed: `+(e instanceof Error?e.message:String(e)))}}),e.on(`turn_end`,async()=>{let e=a;a=new Mi,await e.drain()});let x=e=>{e.ui.setStatus(la,n.enabled?e.ui.theme.fg(`accent`,`PTC`):void 0)},S=(t,r,a,o)=>{n.enabled=!0,n.base=t,n.ourLoadout=r,e.setActiveTools([...r]),i=!0,e.appendEntry(ca,{enabled:!0,base:[...t]}),x(a),a.ui.notify(o,`info`)},C=(t,r)=>{let a=n.base;n.enabled=!1,n.base=void 0,n.ourLoadout=void 0,i=!1,a!==void 0&&e.setActiveTools([...a]),e.appendEntry(r.customType,{enabled:!1}),x(t),r.announce&&t.ui.notify(`PTC mode off — built-in tools are directly callable again.`,`info`)},w=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`,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}C(r,{announce:!0,customType:ca});return}if(i!==`on`){r.ui.notify(n.enabled?`PTC mode is ON — run /ptc off to restore built-in tools.\nBindings: ${U(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=qa({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,uo(a.base,$))}}),e.on(`session_start`,async(n,i)=>{r=Ha(y(),e.getActiveTools(),t.codemode,t.codemodeSwitch,t.codemodeActivation),_(r),u=!1;let a=await l.bindSession(i.sessionManager?.getSessionDir?.(),e=>i.ui.notify(e,`warning`));try{await g(a)}catch(e){d(`startup reconcile delivery failed: `+(e instanceof Error?e.message:String(e)))}let o=pa(y());o.error!==void 0&&i.ui.notify(`pi-ptc-subagents: ${o.error}`,`warning`);let s=Ua(y());s!==void 0&&i.ui.notify(`pi-ptc-subagents: `+s.path+` still has a "surfaceMode" key (`+JSON.stringify(s.value)+"), and it is no longer read: the surface is detected from what pi is, and no key pins it. To keep this package out of your sessions entirely, turn it off in pi -- run `pi config` and disable this package's extensions there (its `extensions` entry set to [] stops pi loading it at all). To silence this notice, delete the key.",`warning`);let c=r.codemode;c.present||i.ui.notify(`pi-ptc-subagents: the codemode probe reported `+c.how+`, so the surface is full. That is the safe direction, not an error.`,`info`),r.codemodeSwitch?.error!==void 0&&i.ui.notify(`pi-ptc-subagents: ${r.codemodeSwitch.error}`,`warning`),r.codemodeActivation?.mcpError!==void 0&&i.ui.notify(`pi-ptc-subagents: ${r.codemodeActivation.mcpError}`,`warning`);{let t=e.getAllTools().some(e=>e.name===`codemode`);!c.present&&t&&i.ui.notify(`pi-ptc-subagents: this session registers pi's codemode, but the probe did not find it (`+c.how+`), so the surface defaulted to full, and your model is being offered two orchestration tools. Enable codemode for real (-t codemode) to get the subagents surface instead.`,`warning`),c.present&&!t&&i.ui.notify(`pi-ptc-subagents: the codemode probe found pi's codemode on disk, but this session does not register it (--no-extensions), so the surface is `+JSON.stringify(r.surfaceMode)+(r.surfaceMode===`subagents`?` with no orchestrator: ptc_run_code and ptc_workflow are reachable only from codemode, and this session has none. Launch with codemode active (pi -t codemode), or turn this package off with pi config.`:`.`),`warning`)}if(r.surfaceMode===`subagents`&&!e.getActiveTools().includes(`codemode`)){let t=e.getActiveTools();t.includes(`ptc_run_code`)||t.includes(`ptc_workflow`)||i.ui.notify(`pi-ptc-subagents: the detected surface is subagents, but codemode is not active in this session, so there is no orchestration tool. Add codemode to your pi tool list (the --tools flag or the default tools setting), or turn this package off with pi config.`,`warning`)}b(i);let f=w(i),{base:p,restoreFirst:m}=Xa(f,e.getActiveTools());m&&e.setActiveTools([...p]);let h=qa({mode:i.mode,defaultMode:o.defaultMode,active:p,manual:!1,hide:$}),v=f?.enabled??h.enter;if(h.enter&&v){S(p,h.loadout,i,uo(p,$));return}x(i),!h.enter&&h.reason!==`not-tui`&&f===void 0&&h.reason===`restricted-session`&&i.ui.notify(`PTC mode skipped: this session was launched with an explicit tool restriction.`,`info`)}),e.on(`session_shutdown`,async()=>{u=!1;try{await l.shutdown(`session_ended_while_running`)}catch(e){d(`session shutdown failed: `+(e instanceof Error?e.message:String(e)))}});let T=!1,E=(e,t)=>{if(e===void 0)return;if(!n.enabled||!t){T&&=(delete e.sections.skills,!1);return}let r=no(e.skills);r.length!==0&&(e.sections.skills=r,T=!0)};e.on(`before_agent_start`,async(t,r)=>{let a=t.systemPromptOptions;if(E(a,to(e.getActiveTools())),n.enabled){if(Ya(n,e.getActiveTools())){C(r,{announce:!1,customType:ca}),E(a,to(e.getActiveTools())),r.ui.notify(`PTC mode off — another extension changed the active tool set.`,`warning`);return}if(i)return i=!1,{message:{customType:`ptc-mode-briefing`,content:Za(U(n.base),$),display:!1}}}})}export{L as BUILTIN_BINDING_NAMES,Zt as DEFAULT_BINDING_NAMES,I as DEFAULT_CONFIG,da as DEFAULT_HIDE_STRATEGY,o as HOST_FRAME_KIND,Cn as MAX_LINE_CHARS,sa as MODE_REQUIRED_TOOL_NAMES,n as PTC_ERROR_KIND,i as PTC_LOG_LEVEL,ua as PTC_MODE_CONFIG_FILE,ca as PTC_MODE_ENTRY_TYPE,la as PTC_MODE_STATUS_KEY,oa as PTC_MODE_TOOL_NAMES,eo as PTC_SKILL_LOAD_INSTRUCTION,Qa as SKILL_READING_TOOL_NAMES,Mi as TurnPools,Le as WORKER_ENV_ALLOW_LIST,t as WORKER_FRAME_KIND,on as WorkerPool,Ja as bindingSource,Za as buildModeInstruction,no as buildPtcSkillsSection,nn as createBuiltinBindings,Re as createWorkerEnv,qa as decideModeEntry,fo as default,Ya as detectExternalLoadoutChange,Ha as detectSurfaceMode,Be as effectiveTimeoutMs,fa as initialModeState,Ka as modeLoadout,Ba as probeCodemodePresence,Ca as readCodemodeSwitch,pa as readDefaultModeConfig,Ua as readLegacySurfaceKey,Pn as renderModelValue,Xa as resolveBaseOnStart,wa as resolveCodemodeActivation,Sa as resolveCodemodeSwitch,ze as resolveConfig,hn as runPtcProgram,Ga as sameToolSet,H as sanitizeText,to as skillsSectionDropped,Tn as stripAnsi};
58
56
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ptc-subagents",
3
- "version": "1.6.0",
3
+ "version": "2.0.0",
4
4
  "description": "DSH PTC mode (ptc_run_code + ptc_workflow) for pi",
5
5
  "keywords": [
6
6
  "pi",