pi-ptc-subagents 3.4.0 → 5.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 +171 -0
- package/README.md +44 -2
- package/dist/index.d.ts +153 -2
- package/dist/index.js +54 -52
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,177 @@ 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
|
+
## [5.0.0] - 2026-10-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **A dispatch reports the model its child actually ran.** The model used to be resolved inside the
|
|
13
|
+
child and never came back: neither `pi.dispatch`'s `DispatchResult` nor a background
|
|
14
|
+
`TaskRecord` said which provider or model served the call, so a wrong-model delegation could
|
|
15
|
+
only be inferred from a failure after the fact. Both now carry an optional observed-model pair —
|
|
16
|
+
observed by the host from the child's own assistant turns (last turn wins), not taken from the
|
|
17
|
+
request — and the model-facing surfaces state the same fact as a `model: provider/id` line on
|
|
18
|
+
`ptc_subagent` and `ptc_task_output` and as a `model=` fact in a refusal. A transcript that
|
|
19
|
+
carries only a model id reports the id alone; the absence of both means no observed assistant turn
|
|
20
|
+
named the pair — no turn, a supported pi that does not report it, or an old record — not "the
|
|
21
|
+
model is unknown". A background record gains the pair at the same terminal close that writes
|
|
22
|
+
`usage`, so a failed or canceled close that named a model still states it. The pair is named
|
|
23
|
+
`observedProvider` / `observedModel` (ADR-0045): the result's `model` input is the requested
|
|
24
|
+
model, and two different facts no longer share a name. Records written before the rename are
|
|
25
|
+
still read (the field rename itself is breaking for program-visible result shapes — see the
|
|
26
|
+
rename entry under Changed). The rendered value is stripped of ANSI escape sequences and
|
|
27
|
+
control characters and head-capped at 200 characters before it enters model-facing text,
|
|
28
|
+
because the pair is untrusted child wire data. See
|
|
29
|
+
ADR-0044, ADR-0016 §"The child's model", and ADR-0022 §3 as amended.
|
|
30
|
+
- **The `ptc_subagent` call row states the model a generalist dispatch will inherit.** When the
|
|
31
|
+
call names no model and no agent (a generalist child, where a definition cannot pin the tier),
|
|
32
|
+
the request row says `model=inherit` and its expanded view names the provider/id the caller's
|
|
33
|
+
session is on; a named agent gets no such claim, and a pinned `model` is stated as before. This
|
|
34
|
+
is a request fact captured at the call's first render, so a later model switch does not change
|
|
35
|
+
an already-rendered row — and it is not the observed model, which still appears only after the
|
|
36
|
+
child ran. ADR-0044.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **BREAKING — the observed-model fields are named `observedProvider` / `observedModel`.**
|
|
41
|
+
`DispatchResult.provider` / `model` and `TaskRecord.provider` / `model` carried the observed
|
|
42
|
+
model, while the input's `model` is the requested model; the shared name made two different
|
|
43
|
+
facts look comparable. The output fields are renamed, the input keeps its name, the child wire
|
|
44
|
+
and the model-facing `model:` text are unchanged, and a record written before the rename is
|
|
45
|
+
normalized on read so its observation is not lost. No aliases: the old result keys are removed,
|
|
46
|
+
which is why this is breaking for program-visible result shapes. See ADR-0045.
|
|
47
|
+
- **BREAKING — a dispatch that names no model now runs on the caller session's current model, not
|
|
48
|
+
pi's global default.** `pi.dispatch` and `ptc_subagent` resolve the child's model first match
|
|
49
|
+
wins: the dispatch's `model` argument, then the agent definition's frontmatter `model:`, then
|
|
50
|
+
the calling session's **current** model, then pi's configured default. A session that switched
|
|
51
|
+
provider or model mid-conversation now carries that switch into its next dispatched child — the
|
|
52
|
+
background-dispatch guide previously said the opposite, and that statement is what this changes.
|
|
53
|
+
Resolution is a snapshot at spawn: a model switched after the child starts does not change it, a
|
|
54
|
+
nested dispatch inherits the child's own session model, and re-running the dispatch is how a
|
|
55
|
+
caller picks up a new model. Tiers 1–2 are unchanged, so pinning the model at the call site or in
|
|
56
|
+
the definition is the explicit opt-out; no config key and no `model: "inherit"` sentinel is
|
|
57
|
+
added. A direct library caller with no context model, an older pi, or a session started without a
|
|
58
|
+
model all fall through to pi's default exactly as before — the session read is structural, so
|
|
59
|
+
the declared 0.86.0 floor gains no new pi type dependency. See ADR-0044 for the decision and its
|
|
60
|
+
rejected alternatives.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- **A failed foreground dispatch no longer loses the child's own failure.** The child is a pi
|
|
65
|
+
subprocess: a rate limit or a model error can make it exit 0 after writing
|
|
66
|
+
`stopReason: "error"` and an `errorMessage` on its assistant turn. The foreground loop
|
|
67
|
+
discarded that text and reported the generic `dispatch produced no final text`, while the
|
|
68
|
+
background registry already recorded the child's reason (issue #70's rule). The close decision now
|
|
69
|
+
returns the child's reported error verbatim, using the background registry's rule — the child's
|
|
70
|
+
own error outranks the exit-code and truncation inference — with cancellation still first:
|
|
71
|
+
cancelled, then exit-0-with-text (fulfilled), then the child's error, then exit-0-without-text,
|
|
72
|
+
then truncation. It logs one warning on that newly diagnosed path only. `stderr` is no longer
|
|
73
|
+
described as guaranteed; `errorMessage` is the structured failure channel. This is
|
|
74
|
+
foreground/background parity: one verdict and one sentence for one failure. See ADR-0044
|
|
75
|
+
Decision 3 and ADR-0016 §Decision 3.
|
|
76
|
+
- **`ptc_subagent refused the call: undefined` is gone.** A rejected dispatch without an
|
|
77
|
+
`errorMessage` used to render the literal string `undefined`, telling the model nothing the
|
|
78
|
+
dispatcher knew. The refusal now states the cause plus bounded facts the result already carries —
|
|
79
|
+
whether a child ran or the harness declined to start, the exit code, turns, report channel, and
|
|
80
|
+
the observed model when there is one — tail-bounded to 800 characters so a provider's actionable
|
|
81
|
+
status code survives the cut. See ADR-0044 Decision 3.
|
|
82
|
+
|
|
83
|
+
## [4.0.0] - 2026-10-19
|
|
84
|
+
|
|
85
|
+
### Added
|
|
86
|
+
|
|
87
|
+
- **The `ptc_subagent` call row shows the dispatch, not its argument dump.** The tool had no
|
|
88
|
+
renderer, so pi fell back to its raw argument serialisation — one enormous
|
|
89
|
+
`ptc_subagent task="…\\n\\n…"` line of JSON-escaped prose (2026-10-19 field report). It now
|
|
90
|
+
registers a `renderCall`: collapsed it reads as a heading (agent, foreground/background and
|
|
91
|
+
read-only flags, and a pinned `model` when one was passed) with one task preview line and
|
|
92
|
+
`… 还有 N 行`; `ctrl+e` expands `label` / `why` rows and the task block — the first task line
|
|
93
|
+
plus up to the existing `MAX_LOG_LINES_EXPANDED` continuation lines, so no new cap is
|
|
94
|
+
introduced. Follows ADR-0013's "no escaped JSON in the call row" rule.
|
|
95
|
+
- **`whyEnabled`, a default-on switch for the dispatch `why` block.** `~/.pi/agent/ptc.json` takes a
|
|
96
|
+
boolean `whyEnabled`; when it is `false`, a `why` passed to `pi.dispatch` / `ptc_subagent` is
|
|
97
|
+
dropped instead of delivered, and both the foreground result and the background record say that a
|
|
98
|
+
suppression happened. **The switch fails on:** a non-boolean value, an unparseable file, or a file
|
|
99
|
+
that is not a JSON object all leave the feature ON — a broken value must never remove it. A broken
|
|
100
|
+
_key_ is reported once at `session_start` with the path, the key and the type that arrived; a broken
|
|
101
|
+
_file_ is already reported by the default-mode reader, so one typo produces one notice. The
|
|
102
|
+
`why` argument stays on the schema either way, so a running program is never rejected for passing
|
|
103
|
+
an argument whose value the switch drops. `DispatchResult.whySuppressed` and
|
|
104
|
+
`TaskRecord.whySuppressed` record **that a reason was withheld, never what it was** — a background
|
|
105
|
+
dispatch returns a handle rather than a result, so without the record it would have no way at all
|
|
106
|
+
to say so. See ADR-0043.
|
|
107
|
+
- **`/ptc why on|off`**, a session-scoped override for the switch above. It records one session entry
|
|
108
|
+
and never writes `ptc.json` — the difference is "about this conversation" against "about every
|
|
109
|
+
future one". A resumed session restores the last record; a new one starts from the file. Bare
|
|
110
|
+
`/ptc why` reports the value _and_ which of the two is in force, and an argument that is neither
|
|
111
|
+
`on` nor `off` warns and changes nothing. See ADR-0043.
|
|
112
|
+
- **A succeeded background task's completion notification now states its report channel.** The
|
|
113
|
+
`<bg-task-notification>` for a `succeeded` record gains a `<report-channel>` child —
|
|
114
|
+
`tool`, `prompt-json`, `none` or `opted-out`, the same **total** field ADR-0032 defines —
|
|
115
|
+
rendered between `<error-message>` and `<output-ref>`. Before this, a model that wanted to
|
|
116
|
+
know whether a finished child had honoured the report contract had to poll `ptc_task_output`;
|
|
117
|
+
the push it already receives now answers it, once per finished child. The element is absent from
|
|
118
|
+
every other event because only a `succeeded` close declares a channel: a running task has not
|
|
119
|
+
reported _yet_, and a `failed` / `lost` / `canceled` close has no report claim to state
|
|
120
|
+
(`none` and `opted-out` are claims about a finished child, not the absence of one). See
|
|
121
|
+
ADR-0022 §7 as amended (2026-10-19).
|
|
122
|
+
|
|
123
|
+
### Changed
|
|
124
|
+
|
|
125
|
+
- **BREAKING — a dispatch's `goal` is now `why`.** `pi.dispatch` and `ptc_subagent` took an
|
|
126
|
+
optional `goal` since 3.4.0; that argument is now `why`, and the child's block is now
|
|
127
|
+
`<pi-ptc-why>` instead of `<pi-ptc-goal>`. A PTC program that passes `goal` must pass `why`.
|
|
128
|
+
The block's **wording is unchanged** — it still reads "This goal is context for doing it well" —
|
|
129
|
+
because that sentence was held constant across ADR-0042's 180 runs and renaming the tag does not
|
|
130
|
+
re-test the contrast; rewriting the sentence would change what was held still. See ADR-0043.
|
|
131
|
+
The rename is not cosmetic: in the agent ecosystem `goal` names an objective or a loop, a thing
|
|
132
|
+
worked toward over time, which is not what this argument holds — the reason one slice was cut out.
|
|
133
|
+
`background` was the other candidate and was unavailable, because the dispatch schema already
|
|
134
|
+
carries a `background` argument meaning "detach the child". No compatibility alias is kept: the
|
|
135
|
+
only consumer is a model reading the live schema each session, and a second name for one value
|
|
136
|
+
would cost exactly the comprehension this change buys back.
|
|
137
|
+
- **The background-dispatch guide now says which model a dispatched child runs, and that it is
|
|
138
|
+
not the caller's.** The child is a fresh `pi` subprocess, so its model resolves — first match
|
|
139
|
+
wins — as the dispatch's `model` argument, then the named agent definition's frontmatter
|
|
140
|
+
`model:`, then pi's own global default; the calling session's current model (and therefore a
|
|
141
|
+
provider switched to mid-session) is **not** inherited. Recorded after the 2026-10-19 field
|
|
142
|
+
report: the parent had switched provider, and the child it dispatched afterwards ran on pi's
|
|
143
|
+
default provider and hit a 429. No behaviour change — the resolution order is what the code
|
|
144
|
+
already did, and the guide states it so the failure is also preventable. See the background
|
|
145
|
+
dispatch guide, `docs/usage/bgdispatch.md`.
|
|
146
|
+
- **The TUI task panel now shows why a background task failed.** The `task-output` panel prints
|
|
147
|
+
the record's failure reason as an `error: …` line first in the output block — ahead of the
|
|
148
|
+
preview or the empty-output placeholder — and the `task-list` panel appends one to each
|
|
149
|
+
`failed` / `lost` row whose record carries an `errorMessage`, so a child that died before
|
|
150
|
+
writing output stays legible in the panel instead of as an empty preview, without a
|
|
151
|
+
`ptc_task_output` call. A `lost` record carries the sweep's reason
|
|
152
|
+
(`session_ended_while_running` / `lost_on_session_restart`) and gets the row too; a
|
|
153
|
+
`canceled` task carries no reason and does not. Both rows reuse the panel's existing
|
|
154
|
+
`MAX_ERROR_CHARS` bound (120, first non-blank line), so no new cap or number is added, and
|
|
155
|
+
neither annotation counts against the panel's row caps — see ADR-0022 §11. A settled
|
|
156
|
+
failure's empty-output placeholder also stops claiming `(no output yet)`: with a reason on the
|
|
157
|
+
record it reads `(no output)`, while a page with no recorded reason keeps the `yet` (this
|
|
158
|
+
payload carries no status to tell a live task from an unlabelled close).
|
|
159
|
+
|
|
160
|
+
### Fixed
|
|
161
|
+
|
|
162
|
+
- **A background task that failed with an empty log now says why — on the notification, in
|
|
163
|
+
`ptc_task_output`, and in the host log.** A 2026-10-19 field report: a child was rate-limited
|
|
164
|
+
(429) before writing anything, its output log was 0 bytes, the `TaskRecord` carried the reason
|
|
165
|
+
in `errorMessage`, and the parent agent received only `status="failed"` — it read the empty
|
|
166
|
+
log as an infrastructure fault and could not act on the real one. Three surfaces now carry the
|
|
167
|
+
cause. A `<bg-task-notification>` whose record carries an `errorMessage` gains an optional
|
|
168
|
+
`<error-message>` (head-truncated to at most 1024 code points, an ellipsis marking the cut, and
|
|
169
|
+
XML-escaped; absent when the record has no such text), so the push that announces the failure
|
|
170
|
+
states it. `ptc_task_output`
|
|
171
|
+
renders the reason as an `error=…` line ahead of a failed/lost task's body and exposes the
|
|
172
|
+
record's `errorMessage` on the codemode projection as `error_message` (and on `details` as
|
|
173
|
+
`errorMessage`), so the pull after the push reads the same reason. The background pump logs a `logger.warn` with the
|
|
174
|
+
task id and the failure reason, so the host operator no longer sees only an empty child log. The
|
|
175
|
+
terminal empty-output fallback also stops saying "yet": a terminal task reads
|
|
176
|
+
`(no output; task <id> is <status>)`, while `running` / `stopping` keep
|
|
177
|
+
`(no output yet; …)`. See ADR-0022 §7 as amended (2026-10-19).
|
|
178
|
+
|
|
8
179
|
## [3.4.0] - 2026-10-19
|
|
9
180
|
|
|
10
181
|
### Added
|
package/README.md
CHANGED
|
@@ -79,11 +79,12 @@ const result = await tools["pi.dispatch"]({
|
|
|
79
79
|
// result.status: "fulfilled" | "rejected" (the binding never throws)
|
|
80
80
|
// result.text: final assistant text
|
|
81
81
|
// result.usage: { input, output, cacheRead, cacheWrite, cost, turns }
|
|
82
|
+
// result.observedProvider?, result.observedModel?: the provider/model the child actually ran
|
|
82
83
|
// result.exitCode, result.durationMs, result.stderr?, result.errorMessage?
|
|
83
84
|
```
|
|
84
85
|
|
|
85
86
|
**Fan out in parallel** with the rest of PTC's tools — dispatch is a binding, not a
|
|
86
|
-
model-
|
|
87
|
+
model-facing lifecycle tool, so the dispatcher handles concurrency the same way it does for
|
|
87
88
|
any other tool call:
|
|
88
89
|
|
|
89
90
|
```ts
|
|
@@ -116,6 +117,32 @@ at 20 findings with the withheld count stated in-band.
|
|
|
116
117
|
- `signal` — when the parent run is cancelled, every in-flight child gets `SIGTERM` then
|
|
117
118
|
`SIGKILL` after a 5-second grace window.
|
|
118
119
|
|
|
120
|
+
### The child's model
|
|
121
|
+
|
|
122
|
+
A dispatched child is a fresh `pi` subprocess and resolves its own model, first match wins:
|
|
123
|
+
|
|
124
|
+
1. the dispatch's `model` argument;
|
|
125
|
+
2. the named agent definition's frontmatter `model:`;
|
|
126
|
+
3. **the caller session's current model** — read from the pi session that spawns the child;
|
|
127
|
+
4. pi's configured default.
|
|
128
|
+
|
|
129
|
+
Tier 3 is the behaviour change of [ADR-0044](./docs/adr/0044-a-dispatch-inherits-the-session-model.md):
|
|
130
|
+
a session that switched provider or model mid-conversation carries that switch into the children it
|
|
131
|
+
dispatches, instead of silently running them on pi's global default. Resolution happens once, at
|
|
132
|
+
spawn — a model switched afterwards does not change a child that is already running, and a nested
|
|
133
|
+
dispatch inherits the child's own session model. Pin the model at the call site or in the agent
|
|
134
|
+
definition to opt out; there is no config key for it, and a direct library caller (or a pi that
|
|
135
|
+
exposes no session model) falls back to pi's default exactly as before.
|
|
136
|
+
|
|
137
|
+
The result says which model the child actually ran: `observedProvider` / `observedModel` on a foreground
|
|
138
|
+
`DispatchResult`, the same optional pair on a background task record (which `ptc_task_output`
|
|
139
|
+
renders as a line), and a `model: provider/id` line in `ptc_subagent`'s text. Absence means no
|
|
140
|
+
observed assistant turn named the pair — no turn, a supported pi that does not report it, or an
|
|
141
|
+
old record — never "unknown model". When the child reported its own failure, a
|
|
142
|
+
rejected dispatch now states that error verbatim as `errorMessage` instead of the generic
|
|
143
|
+
`dispatch produced no final text`, so the foreground failure path matches the background one. Full
|
|
144
|
+
guide: [`docs/usage/bgdispatch.md`](./docs/usage/bgdispatch.md#which-model-the-child-runs).
|
|
145
|
+
|
|
119
146
|
### Background dispatch
|
|
120
147
|
|
|
121
148
|
Foreground `pi.dispatch` blocks the program until the child exits. Pass `background: true` to
|
|
@@ -169,6 +196,12 @@ per row, with the withheld amount stated in-band. A scalar is one line (`→ 47`
|
|
|
169
196
|
bounds ([ADR-0012](./docs/adr/0012-model-facing-result-text.md)): a compact value under 100
|
|
170
197
|
characters stays on one line, every line caps at 200. Nothing is ever printed as escaped JSON.
|
|
171
198
|
|
|
199
|
+
The `ptc_subagent` call row follows the same rule: it reads as a heading — agent, state flags, and
|
|
200
|
+
a pinned model when the caller set one — plus one task preview line and a withheld count, and
|
|
201
|
+
`ctrl+e` expands the `label` / `why` rows and the task block (the first task line plus up to
|
|
202
|
+
`MAX_LOG_LINES_EXPANDED` continuation lines). A dispatch's long prose is never dumped as its
|
|
203
|
+
JSON-escaped argument string.
|
|
204
|
+
|
|
172
205
|
## Images
|
|
173
206
|
|
|
174
207
|
An image read _inside_ a program — `await tools.read({ path: "shot.png" })` — is attached to
|
|
@@ -186,11 +219,20 @@ and reaches `read` / `bash` / `edit` / `write` / `grep` / `find` / `ls` through
|
|
|
186
219
|
…) stay directly callable. Turn it off for one session with `/ptc off` (also `/ptc on`, `/ptc`
|
|
187
220
|
for status), permanently with `{ "defaultMode": false }` in `~/.pi/agent/ptc.json`.
|
|
188
221
|
|
|
222
|
+
A dispatch may also carry the caller's **reason** for the slice: an optional `why` on
|
|
223
|
+
`pi.dispatch` / `ptc_subagent`, which reaches the child as its own `<pi-ptc-why>` block after the
|
|
224
|
+
host's policy block, and never widens the task. It is on by default. Turn it off for one session
|
|
225
|
+
with `/ptc why off` (`/ptc why` reports the current value and whether it came from the session or
|
|
226
|
+
the file), or permanently with `{ "whyEnabled": false }` in `~/.pi/agent/ptc.json`. A value that is
|
|
227
|
+
not a boolean leaves the block **on** and says so at startup — a default-on switch that could turn
|
|
228
|
+
itself off on a typo is the wrong direction. A `why` that was dropped is reported on the dispatch
|
|
229
|
+
result (`whySuppressed`), never silently ([ADR-0043](./docs/adr/0043-a-why-switch-and-a-name.md)).
|
|
230
|
+
|
|
189
231
|
Which model-facing tools this package registers is **detected, not configured** — there is no
|
|
190
232
|
setting for it ([ADR-0034](./docs/adr/0034-surface-is-detected-not-set.md)). The short version:
|
|
191
233
|
a pi whose `codemode` is loaded _and_ callable by the model gets the `subagents` surface
|
|
192
234
|
(`ptc_subagent` on top; the program pair underneath at `codemode` reach); every other pi gets
|
|
193
|
-
`full` (`ptc_run_code` / `ptc_workflow` model-
|
|
235
|
+
`full` (`ptc_run_code` / `ptc_workflow` model-facing). To use the `subagents` surface, put
|
|
194
236
|
`codemode` in your tool list (`pi --tools read,bash,edit,write,codemode`, or `"defaultTools":
|
|
195
237
|
["read", "bash", "edit", "write", "+codemode"]`). To keep this package out of your sessions
|
|
196
238
|
entirely, run `pi config` and disable its extensions there.
|
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,14 @@ interface ParsedAgentEvent {
|
|
|
37
37
|
};
|
|
38
38
|
};
|
|
39
39
|
model?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Provider that served this assistant turn. pi >= 1.0.0 puts the resolved provider on the
|
|
42
|
+
* assistant message, so a host can report which model actually ran the child (dispatches
|
|
43
|
+
* otherwise run on pi's configured default, which the caller never sees). Optional and
|
|
44
|
+
* structurally read -- the 0.86.0 floor's type surface may not declare it, and an absent
|
|
45
|
+
* field must mean "not observed" rather than a parse failure.
|
|
46
|
+
*/
|
|
47
|
+
provider?: string;
|
|
40
48
|
stopReason?: string;
|
|
41
49
|
errorMessage?: string;
|
|
42
50
|
};
|
|
@@ -218,11 +226,13 @@ interface TaskSpawnSource {
|
|
|
218
226
|
* Session-level row tracking the lifecycle of one background child. 20 pre-ownership fields since
|
|
219
227
|
* the 2026-10-16 #199 amendment (19 ADR-0022 §3 originals — its "21 fields" heading is an
|
|
220
228
|
* authoring miscount — plus `usage`), two optional ADR-0023 owner-identity fields, two ADR-0032
|
|
221
|
-
* report fields
|
|
229
|
+
* report fields, ADR-0041's `readOnlyTier`, ADR-0043's `whySuppressed` and the host-observed
|
|
230
|
+
* `observedProvider` / `observedModel` pair (ADR-0045); 28 today, persisted as-is. Optional fields are
|
|
222
231
|
* absent on records still `running` (e.g. `finishedAt` / `exitCode` / `outputRef` are written at
|
|
223
232
|
* transition time, not at spawn) and `ownerPid` / `ownerBootMs` are absent on pre-upgrade
|
|
224
233
|
* legacy records. The `report` / `reportChannel` pair is absent unless the record reached
|
|
225
|
-
* `succeeded` (ADR-0032); `usage`
|
|
234
|
+
* `succeeded` (ADR-0032); `usage` and the observed-model pair are written on every terminal
|
|
235
|
+
* close once a child started.
|
|
226
236
|
*/
|
|
227
237
|
interface TaskRecord {
|
|
228
238
|
id: ULID;
|
|
@@ -291,6 +301,21 @@ interface TaskRecord {
|
|
|
291
301
|
* tier was asked for, which is the same claim the foreground result's absent field makes.
|
|
292
302
|
*/
|
|
293
303
|
readOnlyTier?: ReadOnlyTier;
|
|
304
|
+
/**
|
|
305
|
+
* ADR-0043: `true` when the caller passed a `why` and this session's switch dropped it, so the
|
|
306
|
+
* child's prompt carried no `<pi-ptc-why>` block. Written at SPAWN, alongside `readOnlyTier`,
|
|
307
|
+
* because it too is a fact about the request rather than about the child's output.
|
|
308
|
+
*
|
|
309
|
+
* **The dropped value is deliberately not here.** ADR-0042 declined to persist `why` on the
|
|
310
|
+
* TaskRecord because the value is a caller-held fact, not a host-measured one. That reasoning
|
|
311
|
+
* reaches the value and stops at the event: "a suppression happened" is observed by the host and
|
|
312
|
+
* is the only thing a background caller can otherwise not learn, since a background dispatch
|
|
313
|
+
* returns a handle and has no result to put it on.
|
|
314
|
+
*
|
|
315
|
+
* Absent means no `why` was passed, which is the same claim the foreground
|
|
316
|
+
* `DispatchResult.whySuppressed`'s absence makes — the two fronts state one fact, not two.
|
|
317
|
+
*/
|
|
318
|
+
whySuppressed?: true;
|
|
294
319
|
/**
|
|
295
320
|
* Host-observed token usage for this child (ADR-0032's counter, moved to its own carrier).
|
|
296
321
|
* Written on every terminal close for any child the host actually started — independent of
|
|
@@ -300,6 +325,27 @@ interface TaskRecord {
|
|
|
300
325
|
* Absent on records written before this field existed, and on children that never started.
|
|
301
326
|
*/
|
|
302
327
|
usage?: DispatchUsage;
|
|
328
|
+
/**
|
|
329
|
+
* The provider that actually served this child, observed by the pump off the child's assistant
|
|
330
|
+
* `message_end` events (last turn wins). Persisted at the terminal close, on every outcome the
|
|
331
|
+
* close can produce -- a failed or canceled child still ran a model, and that is the fact the
|
|
332
|
+
* model-observability fix exists to state. Named `observedProvider` because the input's `model`
|
|
333
|
+
* is the requested model, not this one (ADR-0045).
|
|
334
|
+
*
|
|
335
|
+
* ABSENT means no assistant turn NAMED a provider -- not "the child ran on an unknown
|
|
336
|
+
* provider". It covers a child with no observed assistant turn, a supported older pi that
|
|
337
|
+
* does not report the pair on assistant messages (the wire field arrives with pi 1.0.0; the
|
|
338
|
+
* declared floor is 0.86.0), and a record written before these fields existed. A background
|
|
339
|
+
* dispatch that names no model runs on pi's configured default; `ptc_task_output` projects
|
|
340
|
+
* this pair back so a caller can finally see which default that was.
|
|
341
|
+
*/
|
|
342
|
+
observedProvider?: string;
|
|
343
|
+
/**
|
|
344
|
+
* The model id the child actually ran, observed like {@link observedProvider}. Present even
|
|
345
|
+
* when `observedProvider` is absent: a transcript may carry a model without a provider, and a
|
|
346
|
+
* half-observation is still an observation.
|
|
347
|
+
*/
|
|
348
|
+
observedModel?: string;
|
|
303
349
|
}
|
|
304
350
|
/**
|
|
305
351
|
* Per-subscriber cursor for one TaskRecord (ADR-0022 §5). Cursor is per-subscriber (not per-task)
|
|
@@ -410,6 +456,9 @@ type TaskCommand = {
|
|
|
410
456
|
report?: ChildReport;
|
|
411
457
|
reportChannel: ChildReportChannel;
|
|
412
458
|
usage?: DispatchUsage;
|
|
459
|
+
/** ADR-0045: the observed model names; a legacy on-disk record is normalized on read. */
|
|
460
|
+
observedProvider?: string;
|
|
461
|
+
observedModel?: string;
|
|
413
462
|
};
|
|
414
463
|
/** Outcome of one successful command: the persisted record, emitted events, and cursor. */
|
|
415
464
|
interface TransitionResult {
|
|
@@ -477,6 +526,26 @@ interface OutputStorage {
|
|
|
477
526
|
outputRef(taskId: ULID): string;
|
|
478
527
|
}
|
|
479
528
|
//#endregion
|
|
529
|
+
//#region src/runtime/session-model.d.ts
|
|
530
|
+
/**
|
|
531
|
+
* The caller session's current model, as a dispatch can learn it.
|
|
532
|
+
*
|
|
533
|
+
* ADR-0044: a dispatch that names no model at the call site and whose agent markdown names
|
|
534
|
+
* none inherits the model the caller's session is currently running. That fact lives on pi's
|
|
535
|
+
* tool-execution context (`ctx.model`) rather than in this package, so this module is the one
|
|
536
|
+
* structural reader of it -- kept separate so 0.86.0's type surface (which may not declare the
|
|
537
|
+
* field) cannot turn the read into a compile error on the declared floor.
|
|
538
|
+
*
|
|
539
|
+
* The value is a SNAPSHOT taken when the dispatch starts: a model switch after the spawn does
|
|
540
|
+
* not retro-change a child that is already running, and a nested dispatch inherits the child's
|
|
541
|
+
* own context model (which is what the child runs with, not the grandparent's).
|
|
542
|
+
*/
|
|
543
|
+
/** One provider + model id pair, e.g. `deepseek` + `deepseek-flash`. */
|
|
544
|
+
interface SessionModel {
|
|
545
|
+
readonly provider: string;
|
|
546
|
+
readonly id: string;
|
|
547
|
+
}
|
|
548
|
+
//#endregion
|
|
480
549
|
//#region src/runtime/dispatch.d.ts
|
|
481
550
|
/**
|
|
482
551
|
* Per-run in-flight `pi.dispatch` counter (ADR-0016 §2 + ADR-0022 §9). The dispatcher's
|
|
@@ -524,6 +593,23 @@ interface DispatchDeps {
|
|
|
524
593
|
* can dereference the bytes (BG-07).
|
|
525
594
|
*/
|
|
526
595
|
outputStorage?: OutputStorage;
|
|
596
|
+
/**
|
|
597
|
+
* ADR-0043: whether the caller may hand the child a `why`. Absent means ON, which is this
|
|
598
|
+
* package's default and the state ADR-0042's measurement was taken in — so every existing
|
|
599
|
+
* zero-argument call site keeps the behaviour it had before the switch existed.
|
|
600
|
+
*
|
|
601
|
+
* The FILE is read ONCE, at extension load, and carried here rather than consulted per call for
|
|
602
|
+
* the reason #73 already records for `optInBindings`: a switch read per call would let the third
|
|
603
|
+
* dispatch of a run see a different answer from the first, with nothing in the transcript
|
|
604
|
+
* recording that it changed.
|
|
605
|
+
*
|
|
606
|
+
* "Read once" is about this file, not about the value being frozen for the session — `/ptc why`
|
|
607
|
+
* overwrites this exact field mid-session by design (spec D8, ADR-0043), and the `whyConfig`
|
|
608
|
+
* JSDoc in `index.ts` carries that half. What keeps the two together is that the write is
|
|
609
|
+
* recorded: `/ptc why` appends a session entry, so a program that saw the value change knows
|
|
610
|
+
* from the transcript that a user changed it rather than finding it had moved underneath a run.
|
|
611
|
+
*/
|
|
612
|
+
whyEnabled?: boolean;
|
|
527
613
|
}
|
|
528
614
|
/**
|
|
529
615
|
* ADR-0041: the two read-only tiers. `strict` narrows what the child can be handed;
|
|
@@ -710,6 +796,13 @@ interface BindingContext {
|
|
|
710
796
|
* output storage) so the binding's `dispatch()` call shares them.
|
|
711
797
|
*/
|
|
712
798
|
dispatchDeps?: DispatchDeps;
|
|
799
|
+
/**
|
|
800
|
+
* ADR-0044: the caller session's current model, read by the host off pi's tool-execution
|
|
801
|
+
* context. The `pi.dispatch` binding forwards it into the `DispatchContext` so a dispatch
|
|
802
|
+
* that names no model explicitly inherits the session's. Absent for direct library callers
|
|
803
|
+
* and for a pi too old to pass `ctx.model`: both keep the pre-ADR-0044 behaviour.
|
|
804
|
+
*/
|
|
805
|
+
sessionModel?: SessionModel;
|
|
713
806
|
}
|
|
714
807
|
interface Binding {
|
|
715
808
|
readonly name: string;
|
|
@@ -819,6 +912,43 @@ interface DefaultModeConfig {
|
|
|
819
912
|
* on** — the caller should surface the problem rather than silently changing behavior.
|
|
820
913
|
*/
|
|
821
914
|
export declare function readDefaultModeConfig(agentDir: string): DefaultModeConfig;
|
|
915
|
+
/** Result of reading the `why` switch, with enough detail to warn about a broken file. */
|
|
916
|
+
interface WhyEnabledConfig {
|
|
917
|
+
whyEnabled: boolean;
|
|
918
|
+
source: "file" | "default" | "invalid";
|
|
919
|
+
error?: string;
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Read `whyEnabled` from the agent-dir config file — ADR-0043's switch for the dispatch caller's
|
|
923
|
+
* reason.
|
|
924
|
+
*
|
|
925
|
+
* The three outcomes are borrowed from {@link readDefaultModeConfig} rather than invented, because
|
|
926
|
+
* this package already decided what a broken boolean in this file means and a second answer for the
|
|
927
|
+
* same file would be a second failure vocabulary:
|
|
928
|
+
*
|
|
929
|
+
* - Absent file, absent key → **on**. This package is default-on (CONTEXT.md), and ADR-0042's
|
|
930
|
+
* measurement is why the block exists at all.
|
|
931
|
+
* - A non-boolean value, or a file that is not a JSON object, or unparseable JSON → reported as
|
|
932
|
+
* `invalid` **and still resolved to on**.
|
|
933
|
+
*
|
|
934
|
+
* That last line is the one worth stating plainly: a broken value must never be able to take the
|
|
935
|
+
* feature away. A switch whose parse failure silently removes a block the user never asked to
|
|
936
|
+
* remove is worse than one that ignores the typo and says so — the failure here is LOUD (a
|
|
937
|
+
* `session_start` warning naming the path and the key) and the behaviour is UNCHANGED.
|
|
938
|
+
*
|
|
939
|
+
* **A file too broken to parse is not this function's to report**, and an earlier draft got that
|
|
940
|
+
* wrong in a way `tests/extension.test.ts` caught: this reader raised its own "not valid JSON" and
|
|
941
|
+
* `readDefaultModeConfig` raised the identical sentence for the same file, so one typo produced
|
|
942
|
+
* two notices. Two identical warnings train a user to ignore warnings, which defeats the loudness
|
|
943
|
+
* the previous paragraph is buying. The file's parse belongs to the reader that owns
|
|
944
|
+
* `defaultMode`; this one reports only what is wrong with the `whyEnabled` KEY, exactly as
|
|
945
|
+
* `readOptInBindingsConfig` draws the same boundary for `optInBindings`.
|
|
946
|
+
*
|
|
947
|
+
* What this key does NOT do: it stays off the model-facing schema either way. The parameter keeps
|
|
948
|
+
* its place in `DISPATCH_PARAMETERS` so a running program is never rejected for passing an argument
|
|
949
|
+
* whose value this switch decides to drop.
|
|
950
|
+
*/
|
|
951
|
+
export declare function readWhyEnabledConfig(agentDir: string): WhyEnabledConfig;
|
|
822
952
|
/** Result of reading the opt-in binding config, with enough detail to warn about a broken file. */
|
|
823
953
|
interface OptInBindingsConfig {
|
|
824
954
|
optInBindings: readonly BuiltinBindingName[];
|
|
@@ -1597,6 +1727,14 @@ interface RunPtcProgramOptions {
|
|
|
1597
1727
|
* against this run's `dispatchConcurrency` for their whole lifetime.
|
|
1598
1728
|
*/
|
|
1599
1729
|
dispatchDeps?: DispatchDeps;
|
|
1730
|
+
/**
|
|
1731
|
+
* ADR-0044: the caller session's current model, read by the entrypoint off pi's
|
|
1732
|
+
* tool-execution context. Threaded to every binding call's context so `pi.dispatch` can
|
|
1733
|
+
* inherit it when the call site and the agent markdown both name no model. Absent means
|
|
1734
|
+
* no inheritance -- exactly the pre-ADR-0044 behaviour, which is what every direct library
|
|
1735
|
+
* caller and every pi too old to pass `ctx.model` gets.
|
|
1736
|
+
*/
|
|
1737
|
+
sessionModel?: SessionModel;
|
|
1600
1738
|
/**
|
|
1601
1739
|
* Optional worker pool (ADR-0017). Absent = spawn a fresh worker and terminate it
|
|
1602
1740
|
* at run end (the original cold-start path). Present = the dispatcher acquires a
|
|
@@ -1736,6 +1874,19 @@ export interface PtcSubagentsOptions {
|
|
|
1736
1874
|
codemodeActivation?: CodemodeActivationResolution;
|
|
1737
1875
|
}
|
|
1738
1876
|
export default function ptcSubagents(pi: ExtensionAPI, options?: PtcSubagentsOptions): void;
|
|
1877
|
+
/**
|
|
1878
|
+
* `customType` for this session's `why` record (`pi.appendEntry`).
|
|
1879
|
+
*
|
|
1880
|
+
* Separate from `PTC_MODE_ENTRY_TYPE` and it has to be: both readers scan the SAME entry list
|
|
1881
|
+
* backwards for their own type, so a shared one would let `/ptc why off` restore as a mode record
|
|
1882
|
+
* on the next `session_start` -- and `/ptc off` restore as a `why` decision. Two entry types, two
|
|
1883
|
+
* independent switches, and neither recoverable as the other.
|
|
1884
|
+
*
|
|
1885
|
+
* Declared here rather than beside `PTC_MODE_ENTRY_TYPE` in `src/mode/ptc-mode.ts` because that
|
|
1886
|
+
* module is PTC MODE's state machine; this switch has nothing to say about the loadout, and
|
|
1887
|
+
* everything it touches lives in this file.
|
|
1888
|
+
*/
|
|
1889
|
+
export declare const PTC_WHY_ENTRY_TYPE = "ptc-why";
|
|
1739
1890
|
//#endregion
|
|
1740
1891
|
export type { Binding, BindingContext, BindingTable, CodemodePresence, CodemodeSwitch, CodemodeSwitchResolution, CodemodeSwitchSource, CreateBuiltinBindingsOptions, DefaultModeConfig, DetectedSurface, LegacySurfaceKey, ModeBlockReason, ModeEntryDecision, ModeEntryInput, ModeHideStrategy, OptInBindingName, OptInBindingsConfig, PersistedModeState, PtcConfig, PtcErrorKind, PtcErrorShape, PtcImage, PtcJsonValue, PtcModeState, PtcRunOutcome, PtcSurface, RunPtcProgramOptions, TurnPoolsOptions, WorkerPoolOptions, WorkerPoolStats, WorkerPoolWorkerOptions };
|
|
1741
1892
|
//# sourceMappingURL=index.d.ts.map
|