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 +106 -3
- package/README.md +58 -64
- package/dist/index.d.ts +143 -103
- package/dist/index.js +8 -10
- package/package.json +1 -1
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
|
-
## [
|
|
8
|
+
## [2.0.0] - 2026-10-10
|
|
9
9
|
|
|
10
|
-
|
|
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
|
|
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
|
|
229
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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))
|
|
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
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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.
|
|
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
|
|
299
|
-
>
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
**A detection you cannot see is the failure this design has**, so the result is reported.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
|
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
|
|
331
|
-
|
|
332
|
-
nothing to compose with. If
|
|
333
|
-
|
|
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*).
|
|
666
|
-
* {@link readDefaultModeConfig}, beside the `defaultMode` key. ADR-0025.
|
|
665
|
+
* which registered tools are *active*).
|
|
667
666
|
*
|
|
668
|
-
*
|
|
669
|
-
*
|
|
670
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
736
|
-
*
|
|
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 = "
|
|
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
|
-
*
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
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
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
767
|
-
*
|
|
768
|
-
*
|
|
769
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
802
|
-
interface
|
|
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
|
-
|
|
805
|
-
|
|
806
|
-
/**
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
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
|
-
*
|
|
857
|
+
* Decide the surface by asking the pi. A pure function of the three probes and nothing else.
|
|
830
858
|
*
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
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
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
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
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
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
|
|
854
|
-
/**
|
|
855
|
-
|
|
856
|
-
|
|
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
|
-
|
|
866
|
-
|
|
895
|
+
/** The value that was there, or the JSON rendering when it was not a string. */
|
|
896
|
+
value: unknown;
|
|
897
|
+
}
|
|
867
898
|
/**
|
|
868
|
-
*
|
|
899
|
+
* Read a `surfaceMode` key that this package no longer acts on, so `session_start` can say so.
|
|
869
900
|
*
|
|
870
|
-
*
|
|
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
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
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
|
|
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
|
|
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,
|
|
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(`&`,`&`).replaceAll(`<`,`<`).replaceAll(`"`,`"`).replaceAll(` `,`	`).replaceAll(`
|
|
48
|
-
`,` `).replaceAll(`\r`,` `)}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
|
|
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
|
+
`,` `).replaceAll(`\r`,` `)}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
|
|
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:
|
|
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
|
|
55
|
-
`);function
|
|
56
|
-
`)}function
|
|
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
|