@astralyn/pi 1.4.9 → 1.4.11
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 +69 -0
- package/dist/bundle/chunks/{chunk-BAGHIP5J.js → chunk-NUQMVNKQ.js} +3 -3
- package/dist/bundle/chunks/{chunk-3PKXUKTB.js → chunk-PUU43PDV.js} +121 -136
- package/dist/bundle/chunks/{chunk-F66AY3WR.js → chunk-TH3QDDPD.js} +1 -1
- package/dist/bundle/chunks/{execute-P22VMM2H.js → execute-VLAIP26K.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-VKMBQTR2.js → virtual-modules-63XNTSVN.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/agent-session.d.ts +34 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +99 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +14 -7
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/isolated-session.d.ts +7 -2
- package/dist/core/isolated-session.d.ts.map +1 -1
- package/dist/core/isolated-session.js +7 -3
- package/dist/core/isolated-session.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +29 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tasks/delivery.d.ts +31 -12
- package/dist/core/tasks/delivery.d.ts.map +1 -1
- package/dist/core/tasks/delivery.js +97 -36
- package/dist/core/tasks/delivery.js.map +1 -1
- package/dist/core/tasks/execution.d.ts +27 -14
- package/dist/core/tasks/execution.d.ts.map +1 -1
- package/dist/core/tasks/execution.js +90 -86
- package/dist/core/tasks/execution.js.map +1 -1
- package/dist/core/tasks/limits.d.ts +7 -11
- package/dist/core/tasks/limits.d.ts.map +1 -1
- package/dist/core/tasks/limits.js +8 -16
- package/dist/core/tasks/limits.js.map +1 -1
- package/dist/core/tasks/output.d.ts +9 -1
- package/dist/core/tasks/output.d.ts.map +1 -1
- package/dist/core/tasks/output.js +23 -10
- package/dist/core/tasks/output.js.map +1 -1
- package/dist/core/tasks/presentation.d.ts +1 -1
- package/dist/core/tasks/presentation.d.ts.map +1 -1
- package/dist/core/tasks/presentation.js +6 -4
- package/dist/core/tasks/presentation.js.map +1 -1
- package/dist/core/tasks/record.d.ts +41 -29
- package/dist/core/tasks/record.d.ts.map +1 -1
- package/dist/core/tasks/record.js +82 -37
- package/dist/core/tasks/record.js.map +1 -1
- package/dist/core/tasks/reminder.js +2 -2
- package/dist/core/tasks/reminder.js.map +1 -1
- package/dist/core/tasks/runtime.d.ts +15 -23
- package/dist/core/tasks/runtime.d.ts.map +1 -1
- package/dist/core/tasks/runtime.js +50 -178
- package/dist/core/tasks/runtime.js.map +1 -1
- package/dist/core/tasks/scheduler.d.ts +3 -1
- package/dist/core/tasks/scheduler.d.ts.map +1 -1
- package/dist/core/tasks/scheduler.js +16 -12
- package/dist/core/tasks/scheduler.js.map +1 -1
- package/dist/core/tasks/session.d.ts +24 -6
- package/dist/core/tasks/session.d.ts.map +1 -1
- package/dist/core/tasks/session.js +70 -22
- package/dist/core/tasks/session.js.map +1 -1
- package/dist/core/tasks/store.d.ts +17 -4
- package/dist/core/tasks/store.d.ts.map +1 -1
- package/dist/core/tasks/store.js +45 -48
- package/dist/core/tasks/store.js.map +1 -1
- package/dist/core/tasks/types.d.ts +64 -93
- package/dist/core/tasks/types.d.ts.map +1 -1
- package/dist/core/tasks/types.js +7 -0
- package/dist/core/tasks/types.js.map +1 -1
- package/dist/core/tools/shell-execution.d.ts.map +1 -1
- package/dist/core/tools/shell-execution.js +2 -1
- package/dist/core/tools/shell-execution.js.map +1 -1
- package/dist/core/tools/shell-tool.d.ts.map +1 -1
- package/dist/core/tools/shell-tool.js +20 -13
- package/dist/core/tools/shell-tool.js.map +1 -1
- package/dist/extensions/agent/activity-lines.d.ts +10 -0
- package/dist/extensions/agent/activity-lines.d.ts.map +1 -0
- package/dist/extensions/agent/activity-lines.js +76 -0
- package/dist/extensions/agent/activity-lines.js.map +1 -0
- package/dist/extensions/agent/activity.d.ts +25 -4
- package/dist/extensions/agent/activity.d.ts.map +1 -1
- package/dist/extensions/agent/activity.js +76 -21
- package/dist/extensions/agent/activity.js.map +1 -1
- package/dist/extensions/agent/artifacts.d.ts +15 -0
- package/dist/extensions/agent/artifacts.d.ts.map +1 -0
- package/dist/extensions/agent/artifacts.js +44 -0
- package/dist/extensions/agent/artifacts.js.map +1 -0
- package/dist/extensions/agent/progress.d.ts +30 -4
- package/dist/extensions/agent/progress.d.ts.map +1 -1
- package/dist/extensions/agent/progress.js +84 -11
- package/dist/extensions/agent/progress.js.map +1 -1
- package/dist/extensions/agent/render.d.ts.map +1 -1
- package/dist/extensions/agent/render.js +8 -24
- package/dist/extensions/agent/render.js.map +1 -1
- package/dist/extensions/agent/report.d.ts +11 -3
- package/dist/extensions/agent/report.d.ts.map +1 -1
- package/dist/extensions/agent/report.js +32 -7
- package/dist/extensions/agent/report.js.map +1 -1
- package/dist/extensions/agent/tool.d.ts.map +1 -1
- package/dist/extensions/agent/tool.js +20 -5
- package/dist/extensions/agent/tool.js.map +1 -1
- package/dist/extensions/agent/types.d.ts +2 -0
- package/dist/extensions/agent/types.d.ts.map +1 -1
- package/dist/extensions/agent/types.js.map +1 -1
- package/dist/extensions/agent/worker.d.ts +7 -0
- package/dist/extensions/agent/worker.d.ts.map +1 -1
- package/dist/extensions/agent/worker.js +9 -3
- package/dist/extensions/agent/worker.js.map +1 -1
- package/dist/extensions/cached-compaction/summary.d.ts +1 -1
- package/dist/extensions/cached-compaction/summary.d.ts.map +1 -1
- package/dist/extensions/cached-compaction/summary.js +13 -16
- package/dist/extensions/cached-compaction/summary.js.map +1 -1
- package/dist/extensions/search/index.d.ts.map +1 -1
- package/dist/extensions/search/index.js +6 -7
- package/dist/extensions/search/index.js.map +1 -1
- package/dist/extensions/tasks/tool/actions.d.ts +1 -2
- package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/actions.js +12 -80
- package/dist/extensions/tasks/tool/actions.js.map +1 -1
- package/dist/extensions/tasks/tool/constants.d.ts +1 -3
- package/dist/extensions/tasks/tool/constants.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/constants.js +1 -3
- package/dist/extensions/tasks/tool/constants.js.map +1 -1
- package/dist/extensions/tasks/tool/schema.d.ts +2 -3
- package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/schema.js +10 -18
- package/dist/extensions/tasks/tool/schema.js.map +1 -1
- package/dist/extensions/tasks/tool/types.d.ts +1 -17
- package/dist/extensions/tasks/tool/types.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/types.js.map +1 -1
- package/dist/extensions/tasks/tool.d.ts +1 -2
- package/dist/extensions/tasks/tool.d.ts.map +1 -1
- package/dist/extensions/tasks/tool.js +5 -6
- package/dist/extensions/tasks/tool.js.map +1 -1
- package/dist/extensions/tasks/ui/completion-render.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/completion-render.js +2 -2
- package/dist/extensions/tasks/ui/completion-render.js.map +1 -1
- package/dist/extensions/tasks/ui/footer.d.ts +37 -0
- package/dist/extensions/tasks/ui/footer.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/footer.js +129 -0
- package/dist/extensions/tasks/ui/footer.js.map +1 -0
- package/dist/extensions/tasks/ui/frame.d.ts +36 -0
- package/dist/extensions/tasks/ui/frame.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/frame.js +33 -0
- package/dist/extensions/tasks/ui/frame.js.map +1 -0
- package/dist/extensions/tasks/ui/help.d.ts +6 -0
- package/dist/extensions/tasks/ui/help.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/help.js +53 -0
- package/dist/extensions/tasks/ui/help.js.map +1 -0
- package/dist/extensions/tasks/ui/inspector.d.ts +4 -3
- package/dist/extensions/tasks/ui/inspector.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/inspector.js +25 -6
- package/dist/extensions/tasks/ui/inspector.js.map +1 -1
- package/dist/extensions/tasks/ui/layout.d.ts +15 -18
- package/dist/extensions/tasks/ui/layout.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/layout.js +162 -255
- package/dist/extensions/tasks/ui/layout.js.map +1 -1
- package/dist/extensions/tasks/ui/list.d.ts +3 -1
- package/dist/extensions/tasks/ui/list.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/list.js +19 -14
- package/dist/extensions/tasks/ui/list.js.map +1 -1
- package/dist/extensions/tasks/ui/manager.d.ts +17 -5
- package/dist/extensions/tasks/ui/manager.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/manager.js +147 -90
- package/dist/extensions/tasks/ui/manager.js.map +1 -1
- package/dist/extensions/tasks/ui/model.d.ts +32 -5
- package/dist/extensions/tasks/ui/model.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/model.js +7 -7
- package/dist/extensions/tasks/ui/model.js.map +1 -1
- package/dist/extensions/tasks/ui/panes.d.ts +40 -0
- package/dist/extensions/tasks/ui/panes.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/panes.js +144 -0
- package/dist/extensions/tasks/ui/panes.js.map +1 -0
- package/dist/extensions/tasks/ui/render.d.ts +2 -16
- package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/render.js +13 -85
- package/dist/extensions/tasks/ui/render.js.map +1 -1
- package/dist/extensions/tasks/ui/viewport.d.ts +12 -4
- package/dist/extensions/tasks/ui/viewport.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/viewport.js +23 -7
- package/dist/extensions/tasks/ui/viewport.js.map +1 -1
- package/dist/extensions/tasks/ui/views.d.ts +12 -6
- package/dist/extensions/tasks/ui/views.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/views.js +39 -32
- package/dist/extensions/tasks/ui/views.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +11 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +43 -17
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +38 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +11 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +48 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +2 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/bundled/extensions/agent.md +39 -7
- package/docs/bundled/extensions/statusline.md +1 -1
- package/docs/bundled/tasks.md +28 -37
- package/docs/bundled/tool-presentation.md +1 -1
- package/docs/extensions.md +15 -13
- package/docs/json.md +3 -0
- package/docs/models-configuration.md +1 -1
- package/docs/models.md +7 -85
- package/docs/rpc.md +14 -0
- package/docs/sdk.md +12 -2
- package/docs/session-format.md +11 -5
- package/docs/sessions.md +1 -1
- package/docs/usage.md +2 -0
- package/package.json +3 -4
- package/dist/modes/interactive/tool-view/tool-execution.d.ts +0 -14
- package/dist/modes/interactive/tool-view/tool-execution.d.ts.map +0 -1
- package/dist/modes/interactive/tool-view/tool-execution.js +0 -13
- package/dist/modes/interactive/tool-view/tool-execution.js.map +0 -1
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/exy.png +0 -0
- package/npm-shrinkwrap.json +0 -4207
package/docs/bundled/tasks.md
CHANGED
|
@@ -10,7 +10,7 @@ Start Bash through the native `bash` tool (the optional native Windows `powershe
|
|
|
10
10
|
{ "command": "npm run build", "timeout": 120, "background": true }
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Omitting `background` keeps the normal foreground wait. A background submission returns an execution reference, not a successful final outcome. Up to eight background executions run at once; a ninth background submission is rejected without starting. Foreground commands are never limited by background work. `bg create` has been removed; its old stored results and background notifications show their saved text in transcripts.
|
|
13
|
+
Omitting `background` keeps the normal foreground wait. A background submission returns an execution reference, not a successful final outcome; an executor that already has its final result when it accepts answers the caller instead. A task is `background` only once its caller has been released: a submission, or a Ctrl+B request made while the executor is still preparing, waits for the executor to accept, and a task answered inline stays foreground, is saved as a foreground record, and never appears in `tasks list`. Up to eight background executions run at once, counting any still waiting for that acceptance; a ninth background submission is rejected without starting. Foreground commands are never limited by background work. `bg create` has been removed; its old stored results and background notifications show their saved text in transcripts.
|
|
14
14
|
|
|
15
15
|
In interactive mode, **Ctrl+B** moves eligible foreground managed tasks to the background, oldest first, while background slots remain; when every slot is taken it says so instead. Once a foreground execution has run for ten seconds and can still move, the statusline shows `Ctrl+B to run in background`, even when no background tasks exist. The hint is hidden while every background slot is taken, background execution is disabled, or no eligible foreground execution remains. It uses the configured shortcut and disappears if that shortcut is disabled. The shortcut works even when `/tasks` owns focus. The same execution continues: no cancellation, restart or timeout reset. The handoff result names the path the work took, because the model cannot see it: a command submitted with `background: true` reports a handoff from an empty log, while one moved mid-run reports that its execution continued unchanged, that the log already holds output, and that a supplied timeout is still measured from command startup. It does not detach ordinary file tools, or user `!` shell commands.
|
|
16
16
|
|
|
@@ -18,6 +18,8 @@ When nothing is eligible, the key is not consumed: it falls through to other bin
|
|
|
18
18
|
|
|
19
19
|
Configure `app.tasks.detach` in `keybindings.json` to change or disable the shortcut. The default `tui.editor.cursorLeft` is now `left`, freeing Ctrl+B. To restore Emacs cursor behavior, disable or rebind detach before assigning Ctrl+B to cursor-left.
|
|
20
20
|
|
|
21
|
+
Background work, submitted or detached, is admitted only while the `tasks` tool is active: without it the model could start work it cannot read or stop. If the bundled `builtin:tasks` extension is disabled or the tool is deactivated, `background: true` is rejected with a message saying that no work was started, the detach key and statusline hint disappear, and foreground execution is unaffected. Work admitted earlier keeps running and its completion is still delivered.
|
|
22
|
+
|
|
21
23
|
Hosts can prohibit background execution with `backgroundAllowed: false`. This restriction survives runtime replacement and does not depend on task kind; foreground execution remains available.
|
|
22
24
|
|
|
23
25
|
## Management tool
|
|
@@ -27,31 +29,18 @@ Hosts can prohibit background execution with `backgroundAllowed: false`. This re
|
|
|
27
29
|
| Action | Parameters | Behavior |
|
|
28
30
|
|---|---|---|
|
|
29
31
|
| `list` | — | Lists active and up to five retained finished records, at most 100 rows; foreground executions are omitted with a count (they deliver inline in the transcript) |
|
|
30
|
-
| `read` | `taskId`, optional `mode`, `bytes` | Reads bounded model-readable output/report while running or after completion; running Agents return a one-line status without Tool calls |
|
|
31
|
-
| `wait` | optional `taskId`, `waitMs`, `sinceBytes` | Waits for one task, or for any task running at call time when `taskId` is omitted |
|
|
32
|
+
| `read` | `taskId`, optional `mode`, `bytes`, `sinceBytes` | Reads bounded model-readable output/report while running or after completion; running Agents return a one-line status without Tool calls |
|
|
32
33
|
| `kill` | `taskId` | Requests cancellation of the selected task |
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```json
|
|
37
|
-
{ "action": "read", "taskId": "<execution-id>", "mode": "tail", "bytes": 8192 }
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Reads default to an 8KB tail. `bytes` clamps between 256 bytes and 48KB; `mode` can be `head` or `tail`. A Bash output path is included when available. Tasks without a log return their bounded published result. Executors can separately attach human-only display text: `/tasks` shows it, while `tasks read`, settled waits, and completion context exclude it. Agent Tool calls use this display channel, so no read-view parameter is needed. Every read and settled wait reports its byte range and where to continue, for example `bytes 92160–100352 of 100352 · next sinceBytes 100352`. Use a returned offset with that task's ID; waitAny does not accept a cross-task offset.
|
|
35
|
+
There is no `wait` action. A background completion is delivered to the model automatically and starts a new turn if the previous one has ended, so the model continues independent work or ends its turn instead of blocking or polling.
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
{ "action": "wait", "taskId": "<execution-id>", "waitMs": 20000, "sinceBytes": 4096 }
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Wait for any task already running at call time by omitting the ID:
|
|
37
|
+
The tool is the only way the model can follow background work, which is why background needs it (see [Start or detach work](#start-or-detach-work)). Use the execution ID returned by the native tool or `tasks list`. An unknown or stale `taskId` fails with the session's current tasks listed inline (active first, then recent finishes), so the next call can use a real ID without a separate list call; an ambiguous prefix lists exactly the tasks it matched.
|
|
47
38
|
|
|
48
39
|
```json
|
|
49
|
-
{ "action": "
|
|
40
|
+
{ "action": "read", "taskId": "<execution-id>", "bytes": 8192 }
|
|
50
41
|
```
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Wait output is limited to 32KB. With `taskId`, it can start after `sinceBytes`; when more than 32KB was written after that offset, the wait returns the newest 32KB and reports how many bytes it skipped. `sinceBytes` is rejected without `taskId` because offsets belong to one task. Interrupted waits do not read output. A settled waitAny reads the winning task and lists initial cohort members still running. Continue independent work instead of sleep-polling or repeatedly reading; wait when the next step genuinely depends on a result.
|
|
43
|
+
Reads default to 8KB. Without `mode`, a report such as an Agent's is read from its start and a log such as a command's from its end; `mode` can be `head` or `tail` and always wins. `details.mode`, and with it the Transcript row, names the end actually read. `bytes` clamps between 256 bytes and 48KB. A Bash output path is included when available. Tasks without a log return their bounded published result. Executors can separately attach a human-only display: `/tasks` shows it in place of the result, while `tasks read` and completion context always use the result. Only a completion's own display survives settlement. Agent Tool calls use this display channel, so no read-view parameter is needed. Every read reports its byte range and where to continue, for example `bytes 92160–100352 of 100352 · next sinceBytes 100352`. Use a returned offset with that task's ID: a head read continues forward from it, while a tail read keeps the newest unread window. Offsets count raw source bytes, so undecodable UTF-8 cannot change the continuation position.
|
|
55
44
|
|
|
56
45
|
```json
|
|
57
46
|
{ "action": "kill", "taskId": "<execution-id>" }
|
|
@@ -59,19 +48,21 @@ Wait output is limited to 32KB. With `taskId`, it can start after `sinceBytes`;
|
|
|
59
48
|
|
|
60
49
|
A cancellation request is not proof that execution has stopped. The task can remain `stopping` during cleanup. Read its later terminal status for the outcome. Partial, failed, cancelled and timeout outcomes are not displayed as successful completion.
|
|
61
50
|
|
|
62
|
-
All model-facing management responses, including listings and error messages, are bounded to 50KB and 2,000 lines overall.
|
|
51
|
+
All model-facing management responses, including listings and error messages, are bounded to 50KB and 2,000 lines overall. Repeated reads do not add usage or restart execution. Completion delivery and usage accounting belong to the host, not panel refreshes.
|
|
63
52
|
|
|
64
53
|
## `/tasks` panel
|
|
65
54
|
|
|
66
|
-
The fullscreen panel has one scrollable list. **Active** groups queued, running and stopping work; **Finished** groups all retained results on the selected branch, including foreground shell results, newest first. Empty groups are hidden. Each task is one row: its status glyph, its source label such as Bash or Agent in an aligned dim column, the title, and right-aligned metadata. The metadata shows elapsed time, the status name for every status except running, completed and failed, whose glyphs are unambiguous, and an ID suffix when two titles are identical.
|
|
55
|
+
The fullscreen panel has one scrollable list. **Active** groups queued, running and stopping work; **Finished** groups all retained results on the selected branch, including foreground shell results, newest first. Empty groups are hidden. Each task is one row: its status glyph, its source label such as Bash or Agent in an aligned dim column, the title, and right-aligned metadata. The metadata shows elapsed time, the status name for every status except running, completed and failed, whose glyphs are unambiguous, and an ID suffix when two titles are identical. The list's top border names the panel and counts what is shown (`Tasks · 2 active · 5 finished`); its bottom border shows the visible range (`1-12 of 40`) only when the list overflows. While a stop question is open, the target row's metadata reads `stop?` in the warning color. Durations omit zero components and separate remaining units (`8s`, `1m`, `1m 5s`, `1h 5m`). The background action is offered only when the selected task can move.
|
|
67
56
|
|
|
68
57
|
Selection follows the task when it finishes; new work does not steal focus or replace browsed output. The list preserves the selected row's screen position where space permits. Reopening selects the newest task started since the panel last closed; when nothing new started, it keeps the remembered selection. The right-hand tab is remembered as well, while the runtime remains available. Runtime replacement resets these preferences, and branch navigation removes results outside the visible branch. History remains bounded by the retention limits below.
|
|
69
58
|
|
|
70
|
-
At 100 columns or more, the task list sits beside an inspector. The inspector's first row
|
|
59
|
+
At 100 columns or more, the task list sits beside an inspector; the list takes 40% of the width, between 34 and 60 columns. Each pane's title is in its top border: the list's `Tasks`, and the inspector's **Output** and **Details** tabs. The inspector's first row is the selected task's title in bold and its second is a meta line, `Bash · Running · Background · 12s`, with the status in its outcome color; a rule joined to the pane's border separates this header from the tab's content. **Output** shows the result directly; **Details** starts with the task ID, then holds what the executor declared — its input such as the full command or prompt (inline when it is one line, as a block when it spans several), facts such as the directory, model or exit code — and the log location. Long values wrap inside their own column, with continuation lines hanging under the value, and unbroken tokens such as paths break at the column width. Each task and tab keeps its reading position for the lifetime of the panel. Tab switches between the two panes, preserving the right-hand tab. Left/Right switch Output/Details only when the inspector has focus.
|
|
60
|
+
|
|
61
|
+
The focused pane has a highlighted border. Task selection uses a persistent row background, and the selected right-hand tab uses an underline; neither changes when focus moves. When the system theme has no background colors, the selected row uses inverse text. There are no focus arrows or extra title colors. Narrow terminals show one pane at a time at full width, with the same Tab navigation, no focus highlight, and the title and meta rows in the inspector. In a narrow terminal the inspector is a drill-down page: Escape returns to the list, and Escape in the list closes the panel; in the wide layout Escape closes the panel from either pane. The footer names the action as `Esc back` or `Esc close`. The minimum supported size is 60 columns by 14 rows; below it, a centered dialog asks for a larger window and covers the screen, because the panel keeps input focus while open.
|
|
71
62
|
|
|
72
|
-
The
|
|
63
|
+
The footer shows context hints on the left and pins the Escape action to the right edge. When the row is too narrow, the lowest-priority hints go first: pane and tab switching, then the arrow hint, open, find, stop and background, with `? keys` kept longest. A message appears there only when an action did not do what was asked, as a warning (`!`) for a no-op or refusal such as a reached background limit or a task that already finished, or as an error (`×`) for an unexpected failure. It replaces the hints and clears on the next key or click, or after about four seconds. A successful stop or background request shows no message: the row and the header already read `Stopping` or the new mode.
|
|
73
64
|
|
|
74
|
-
Every executor is presented the same way, from data it declares: a label such as Bash or Agent, an input, labelled facts, and whether its output is a log or a report.
|
|
65
|
+
Every executor is presented the same way, from data it declares: a label such as Bash or Agent, an input, labelled facts, and whether its output is a log or a report. Only a log streams from its file, with a reading state (following, browsing or finished) and a follow control. Any other output shows what its executor published, updated as soon as it publishes rather than on the log poll: while the task runs, the lines its executor renders for the panel (styled and fitted to the pane, such as a running Agent's colored tool calls with an elapsed time), otherwise its human-readable display when it has one, otherwise the result that models read. A report is never read from an attached file, so a report saved in full still shows its published beginning, with the file listed in Details as **Full report**. A completed report uses the native Markdown renderer. Output follows the tail of a log and of a live report; a settled report opens at its first line, and keeps the position you scrolled to. When a report overflows, its scroll range appears in the pane's bottom border. A running Agent shows its compact tool calls, written as in the transcript (`✓ read README.md · 4ms`), and a finished one shows its final report. A task without an input shows its title in Details. Missing logs fall back to the saved result. Task status, failure reasons and output-read errors remain distinct.
|
|
75
66
|
|
|
76
67
|
| Default key | Action |
|
|
77
68
|
|---|---|
|
|
@@ -79,35 +70,35 @@ Every executor is presented the same way, from data it declares: a label such as
|
|
|
79
70
|
| Left / Right | Output / Details, only while the inspector has focus |
|
|
80
71
|
| Tab / Shift+Tab | Switch between list and inspector, preserving the current tab |
|
|
81
72
|
| Up / Down | Select a task or scroll the focused region |
|
|
82
|
-
| Enter | Open the selected task: focus the inspector,
|
|
73
|
+
| Enter | Open the selected task: focus the inspector, which fills the width in a narrow terminal |
|
|
83
74
|
| Page Up / Page Down | Page the focused list or region |
|
|
84
75
|
| Home / End | Top / bottom of the list or bounded preview |
|
|
85
76
|
| `f` | Follow the latest output, or load the final output after completion, while Output has focus |
|
|
86
77
|
| `b` | Move only the selected eligible foreground task to the background |
|
|
87
|
-
| `k`, then Enter | Request cancellation of the selected whole task. The footer asks `Stop Bash "npm test"?` while the list and inspector stay visible
|
|
78
|
+
| `k`, then Enter | Request cancellation of the selected whole task. The footer asks `Stop Bash "npm test"?` while the list and inspector stay visible and the target row reads `stop?`; Escape cancels. Confirmation has no time limit and closes if its target finishes |
|
|
88
79
|
| `?` | Show configurable controls, grouped into Navigate, View and Act; Up/Down scroll the help |
|
|
89
|
-
| Escape | Close the current search, help or confirmation first; otherwise close the panel |
|
|
80
|
+
| Escape | Close the current search, help or confirmation first; then, in a narrow terminal, return from the inspector to the list; otherwise close the panel |
|
|
90
81
|
| Ctrl+B | Detach **all** eligible foreground executions through the host |
|
|
91
82
|
|
|
92
|
-
Click a pane to focus it, a task row to select it, or a tab label to display it. The mouse wheel scrolls the pane under the pointer without changing keyboard focus or task selection. Footer hints are clickable as well
|
|
83
|
+
Click a pane to focus it, a task row to select it, or a tab label to display it. The mouse wheel scrolls the pane under the pointer without changing keyboard focus or task selection. Footer hints and the Escape action are clickable as well; a click while a message is showing dismisses it. While a stop question owns the footer, only its stop and cancel actions respond. Each mode answers only to its own targets: search to its candidates and cancel, help to its footer, so a click never activates the panel behind them.
|
|
93
84
|
|
|
94
|
-
Shell output initially follows the tail
|
|
85
|
+
Shell output initially follows the tail; the inspector's bottom border shows **Live**. Scrolling up enters **Browsing · f follow**, which holds a bounded output snapshot while status continues updating, and the label itself is clickable. In Output, press `f`, End, or scroll down to the bottom to resume following. If the task finishes while browsing, the border offers `Finished · f load final output`. Only a log has this reading state; a published result is always the current one. The Details bottom border shows its scroll range only when Details overflow. Closing or scrolling never pauses, restarts or cancels execution.
|
|
95
86
|
|
|
96
|
-
The panel polls only selected tail
|
|
87
|
+
The panel polls only the selected attached log's tail once per second, up to 48 KiB per read; navigation and settlement can request an immediate refresh. Progress updates and animation do not add log reads. A truncated tail shows its preview and total byte counts; ordinary complete output omits these statistics. Empty output and expired logs have explicit messages; an unavailable log falls back to the saved result when available. Selecting a task retains its result and log without delaying completion notifications. Closing releases subscriptions, the retention lease and timers.
|
|
97
88
|
|
|
98
89
|
Controls use the `app.tasks.*` bindings: `search`, `previousTab`, `nextTab`, `nextFocus`, `previousFocus`, `top`, `bottom`, `follow`, `kill`, `confirmStop`, `detachSelected`, `detach` and `help`. List paging uses `tui.select.pageUp` / `pageDown`; Details and Output paging use `tui.editor.pageUp` / `pageDown`. All are configurable. The former view and direct-focus bindings have been removed.
|
|
99
90
|
|
|
100
91
|
The `/hotkeys` table lists the task bindings next to the editor ones, and the panel's `?` help lists every control, so both detach scopes are discoverable without reading these docs.
|
|
101
92
|
|
|
102
|
-
The statusline counts only queued, running and stopping background tasks: `1 background task · /tasks to view` or `2 background tasks · /tasks to view`. Foreground tasks and retained results do not contribute to the count; the summary disappears when no active background tasks remain. Finished results remain available in `/tasks`. The independent detach hint can appear alone or after the summary: `1 background task · /tasks to view · Ctrl+B to run in background`. The count uses normal text, `/tasks` and the shortcut use the accent color, and descriptions and separators are muted. The model-facing `tasks list` keeps its background-only scope and foreground omission count.
|
|
93
|
+
The statusline counts only queued, running and stopping background tasks: `1 background task · /tasks to view` or `2 background tasks · /tasks to view`. Foreground tasks and retained results do not contribute to the count; the summary disappears when no active background tasks remain. Finished results remain available in `/tasks`. The independent detach hint can appear alone or after the summary: `1 background task · /tasks to view · Ctrl+B to run in background`. The count uses normal text, `/tasks` and the shortcut use the accent color, and descriptions and separators are muted. The model-facing `tasks list` keeps its background-only scope and foreground omission count. A task whose detach is waiting for its executor to accept still shows as foreground, and is not counted, until the executor accepts.
|
|
103
94
|
|
|
104
95
|
## Completion notifications
|
|
105
96
|
|
|
106
97
|
Interactive `task-completion` messages have a compact collapsed summary: the executor's label, outcome, duration, short ID, and the task title. Failures retain a short reason; log paths, output and worker reports stay out of the collapsed view. In fullscreen mode, left-click the notification to expand or collapse that message independently. The blank spacer above it is not a click target. The configured tool-output expansion binding (default Ctrl+O) remains available for toggling output expansion across the transcript.
|
|
107
98
|
|
|
108
|
-
Expanded notifications separate the input under its own label, such as **Command** or **Prompt** (or **Task** without one), **Details** with the declared facts such as directory and exit code, **Result** or **Error**, a bounded plain-text **Output** preview, **Log** and the full execution ID. A log's saved output keeps its end, where the outcome is; a report's keeps its beginning, where the conclusion is. Both carry an explicit truncation flag. Screen line limits are independent of source truncation.
|
|
99
|
+
Expanded notifications separate the input under its own label, such as **Command** or **Prompt** (or **Task** without one), **Details** with the declared facts such as directory and exit code, **Result** or **Error**, a bounded plain-text **Output** preview, **Log** and the full execution ID. A log's saved output keeps its end, where the outcome is; a report's keeps its beginning, where the conclusion is. Both carry an explicit truncation flag. Screen line limits are independent of source truncation. The card ends with the task ID; it carries no separate closing line. A failed shell task's card states its error once: the output holds only what the command printed.
|
|
109
100
|
|
|
110
|
-
Rendering reads the self-contained terminal snapshot in message `details`, so `/reload`, history eviction, expired logs and restart do not require a live task lookup. The session generates model-facing `content` from the same facts; `details` never enters model context or usage accounting. Replay details retain up to 40 KiB of output. The automatic model-facing notification includes only an 8 KiB tail of a log, plus the log path and truncation guidance for `tasks read`; a report arrives from its beginning, whole when it fits the 48 KiB budget, as its foreground result would. Commands containing `Output:` and literal truncation notices remain ordinary data. See the [snapshot contract](../session-format.md#background-records).
|
|
101
|
+
Rendering reads the self-contained terminal snapshot in message `details`, so `/reload`, history eviction, expired logs and restart do not require a live task lookup. The session generates model-facing `content` from the same facts; `details` never enters model context or usage accounting. Replay details retain up to 40 KiB of output. A shell task's result holds the command's output only; its failure is the separate error, which the notification states once on its `Error:` line instead of repeating it at the end of the output. The automatic model-facing notification includes only an 8 KiB tail of a log, plus the log path and truncation guidance for `tasks read`; a report arrives from its beginning, whole when it fits the 48 KiB budget, as its foreground result would. Commands containing `Output:` and literal truncation notices remain ordinary data. See the [snapshot contract](../session-format.md#background-records).
|
|
111
102
|
|
|
112
103
|
Only the current structured completion format (version 2) is specialized. Older completions, including version-1 shell and report completions, and malformed details receive a bounded plain **Details** view of their saved message; no text reconstruction or migration is performed. Legacy `background-task` notifications show as plain messages. HTML export and `/tree` selector labels continue to display the saved message content.
|
|
113
104
|
|
|
@@ -117,7 +108,7 @@ Background execution belongs to the current session runtime, not a daemon. Paren
|
|
|
117
108
|
|
|
118
109
|
The panel is an observer, not a cleanup engine. Admission, bounded history/output retention, completion delivery and headless exit policy are enforced by the core service and hosting mode.
|
|
119
110
|
|
|
120
|
-
Interactive mode
|
|
111
|
+
Interactive mode and built-in print, JSON, and RPC modes enable Background. Ordinary SDK sessions leave it disabled and reject `background: true`; normal foreground execution remains available. An SDK embedding can explicitly enable it via `session.bindExtensions({ tasksEnabled: true })`, but must own cancellation, bounded draining, result-driven turns, and shutdown. See [SDK Background execution](../sdk.md#background-execution).
|
|
121
112
|
|
|
122
113
|
Managed shell output is collected continuously from startup, including before detach. Background shell output is capped at 20 MiB; crossing the cap fails and stops the command. Foreground output retains its existing uncapped log behavior: detaching a command already over the background budget stops it without deleting the prior bytes. No timeout is supplied by default, and a supplied timeout remains measured from command startup, in seconds, across detach.
|
|
123
114
|
|
|
@@ -125,9 +116,9 @@ Managed logs are ephemeral: they are retained with the runtime record and cleane
|
|
|
125
116
|
|
|
126
117
|
Each settlement appends one version-3 `task-record` custom entry holding the terminal snapshot and its usage. A foreground snapshot omits its output, which the transcript already saves in the tool result, and restoration reads it back from there. The runtime restores version-3 terminal history from the selected branch for programmatic reads and the panel's Finished group, selecting the newest records independently for each history. Older `task-result` records are left in the session file and are not migrated or restored; usage from released sessions still counts. Live execution never resumes after restart, and restoration does not replay accounting or completion events. Restored records drop their log path: logs never outlive the runtime that wrote them. See [session format](../session-format.md#background-records).
|
|
127
118
|
|
|
128
|
-
|
|
119
|
+
Settled history outside the selected branch can be released and restored on return, so it does not fill the new branch's history budget. An undelivered completion is a retention lease held by the session's delivery: its record and output stay protected, even while its branch is hidden, until the model has the completion, a direct SDK consumer acknowledges it, or its runtime closes or is replaced; completions still pending when a runtime is replaced are not delivered by its successor. A bare runtime outside a session holds no such lease, so its settled records are ordinary history. Leases, pins and active reads use a separate allowance within the total runtime retention cap; restoration respects the same cap.
|
|
129
120
|
|
|
130
|
-
Completion delivery follows the same queueing path as interactive input. While the agent is running, a completion is steered into that run, so it lands right after the current tool batch — exactly like a message the user types mid-run, and behind any already queued steering. An idle session starts a completion turn instead. User preflight (input hooks, model and authentication checks, compaction) still holds delivery, so a user message is always the first thing a new run sees. One bounded completion is in flight at a time. Each execution produces one completion. A
|
|
121
|
+
Completion delivery follows the same queueing path as interactive input. While the agent is running, a completion is steered into that run, so it lands right after the current tool batch — exactly like a message the user types mid-run, and behind any already queued steering. An idle session starts a completion turn instead. User preflight (input hooks, model and authentication checks, compaction) still holds delivery, so a user message is always the first thing a new run sees. One bounded completion is in flight at a time. Each execution produces one completion. A direct SDK consumer that handles a result outside model history acknowledges it with `session.acknowledgeTaskDelivery(id)`. Plain reads and panel selection retain output without delaying notifications. Progress and repeated reads do not inject messages or add usage. `agent_settled` still describes the main agent, not the end of all background executions; `session_quiescent` and `session.waitForQuiescence()` mark the point where no task is unsettled and no completion turn remains. Print and JSON mode wait for that point before exiting.
|
|
131
122
|
|
|
132
123
|
After a successful compaction, the next main-agent request includes one bounded, request-local reminder listing background tasks still active at that moment. It warns against duplicate work without creating a turn or entering session history. A successful assistant response consumes it; failed or aborted requests leave it eligible for the next attempt. If no tasks are active, the reminder is cleared instead of being carried forward to unrelated future work.
|
|
133
124
|
|
|
@@ -135,6 +126,6 @@ Failed completion delivery leaves the terminal result available for inspection r
|
|
|
135
126
|
|
|
136
127
|
Queued extension `nextTurn` context accompanies completion turns and stays queued until each message is persisted. A failed or partially persisted turn therefore retries only the context that has not been saved.
|
|
137
128
|
|
|
138
|
-
If an executor ignores cancellation and settles after bounded cleanup has retired its runtime or branch, the old session quarantines its bounded result and reported usage: the latest 32 records remain in memory, and persisted sessions also append `<session-file>.tasks-late.jsonl
|
|
129
|
+
If an executor ignores cancellation and settles after bounded cleanup has retired its runtime or branch, the old session quarantines its bounded result and reported usage: the latest 32 records remain in memory, and persisted sessions also append `<session-file>.tasks-late.jsonl`, which `/resume` deletion removes with the session. These audit records are excluded from active totals and are not automatically reconciled. A completed cleanup grace period is not proof that an uncooperative executor stopped.
|
|
139
130
|
|
|
140
131
|
The previous extension-owned interactive-prompt stall watchdog was not ported. Pi no longer automatically flags a prompt-looking shell tail as `waiting for input` or sends stall remediation notifications. Use non-interactive commands and inspect/stop stalled work manually; legacy stall notifications show as plain saved messages.
|
|
@@ -39,7 +39,7 @@ The `Ctrl+B` background hint is not part of a tool row. Once a foreground manage
|
|
|
39
39
|
|
|
40
40
|
The look is defined in one place, `src/modes/interactive/tool-view/style.ts`: the marker and rail glyphs, their colors, the spacing between blocks, and which tools fold their result. `FramedComponent` in the same file hangs a component's lines off the marker or the rail and derives the gutter width from the glyphs.
|
|
41
41
|
|
|
42
|
-
The upstream `components/tool-execution.ts` owns one call's lifecycle: pending, success and error states, expansion, image placement and Kitty conversion, and renderer disposal. The bundled `tool-presentation` extension attaches a TUI-only shell through the existing tool renderer resolver; `tool-view/shell.ts` maps the visual policy to that shell.
|
|
42
|
+
The upstream `components/tool-execution.ts` owns one call's lifecycle: pending, success and error states, expansion, image placement and Kitty conversion, and renderer disposal. The bundled `tool-presentation` extension attaches a TUI-only shell through the existing tool renderer resolver; `tool-view/shell.ts` maps the visual policy to that shell. Direct constructors can attach the same shell with `withToolShell()` from `tool-view/shell.ts`; there is no second lifecycle implementation.
|
|
43
43
|
|
|
44
44
|
`tool-view/chat.ts` owns the chat container that measures tool blocks for spacing and disposes rows when the chat clears. It reads each row's gap policy: upstream rows keep one blank line, while the distribution shell uses the compact policy above. Extension tools and built-in tools use the same shell unless they explicitly own a self shell.
|
|
45
45
|
|
package/docs/extensions.md
CHANGED
|
@@ -1257,14 +1257,13 @@ Session-bound `TasksContext` supervises native and extension-owned execution. Fo
|
|
|
1257
1257
|
|
|
1258
1258
|
| Method | Contract |
|
|
1259
1259
|
|---|---|
|
|
1260
|
-
| `execute<T>(execution)` | Returns `{ kind: "result", result, status, error? }` or `{ kind: "background", task }` |
|
|
1260
|
+
| `execute<T>(execution)` | Returns `{ kind: "result", result, status, error? }` or `{ kind: "background", task, reason }`, where `reason` is `"submitted"` when the caller asked for `background` and `"detached"` when it was moved while the work ran |
|
|
1261
1261
|
| `list()` / `get(id)` | Bounded, read-only (frozen) retained snapshots; IDs support unique prefixes |
|
|
1262
|
-
| `
|
|
1263
|
-
| `
|
|
1264
|
-
| `waitAny(options?)` | Observes the first task running at call time to settle, with `timeoutMs`, `signal`, `toolCallId`, and `read: { bytes? }`. Returns `{ outcome, waitedMs, task?, read?, running }`; `outcome` adds `no_tasks` for an empty cohort. Later tasks are excluded |
|
|
1262
|
+
| `presentOutput(id, width, theme)` | The running task's live Output lines from its executor's `present` (below), or `undefined` for an unknown, settled or presenter-less task, or when the presenter throws. Hosts that render task Output, such as `/tasks`, call it on every repaint and use the default view when it returns nothing |
|
|
1263
|
+
| `read(id, options?)` | Bounded head, tail, or byte-offset model-readable output; excludes human-only display text. `options`: `mode`, `bytes`, `sinceBytes`. Without `mode`, a `report` is read from its start and a `log` from its end. With `sinceBytes`, `head` continues forward from that offset and `tail` keeps the newest window at or after it. Returned ranges and `endByte` use raw source bytes |
|
|
1265
1264
|
| `kill(id)` | Requests cancellation; `stopping` is not a terminal status |
|
|
1266
|
-
| `detach(id)` / `detachForeground()` | Hands off one task, or eligible foreground tasks oldest first while background slots remain, without restarting them; throws when every background slot is taken |
|
|
1267
|
-
| `canDetach(id)` | Reports whether an execution can move now, including host policy and available background slots; does not change the execution |
|
|
1265
|
+
| `detach(id)` / `detachForeground()` | Hands off one task, or eligible foreground tasks oldest first while background slots remain, without restarting them; throws when every background slot is taken. Returns `false` / `0` while the host cannot supervise background work (see below) |
|
|
1266
|
+
| `canDetach(id)` | Reports whether an execution can move now, including host policy, the `tasks` tool being active, and available background slots; does not change the execution |
|
|
1268
1267
|
| `subscribe(listener)` | Observes task state and progress |
|
|
1269
1268
|
| `retain(id)` | Returns an idempotent release function protecting retained data, without delaying notifications |
|
|
1270
1269
|
|
|
@@ -1273,21 +1272,24 @@ Session-bound `TasksContext` supervises native and extension-owned execution. Fo
|
|
|
1273
1272
|
| Field | Contract |
|
|
1274
1273
|
|---|---|
|
|
1275
1274
|
| `label` | Human source name for the panel and completion card, such as `Bash` or `Agent`; defaults to `kind` |
|
|
1276
|
-
| `output` | `"log"` (default) or `"report"`. A log's outcome is at its end, so a background completion gives the model an 8 KiB tail. A report leads with its conclusion, so it arrives from its beginning, whole when it fits the 48 KiB context budget, as its foreground result would; a completed report renders as Markdown in `/tasks` |
|
|
1275
|
+
| `output` | `"log"` (default) or `"report"`. A log's outcome is at its end, so a background completion gives the model an 8 KiB tail, and `read()` returns its end unless `mode` says otherwise. A report leads with its conclusion, so it arrives from its beginning, whole when it fits the 48 KiB context budget, as its foreground result would, and `read()` returns its start unless `mode` says otherwise; a completed report renders as Markdown in `/tasks` |
|
|
1277
1276
|
| `input` | `{ label, text, truncated, language? }`: what the task was asked to do, such as a command or briefing. Shown in Details and the expanded completion card, highlighted when `language` is set |
|
|
1278
1277
|
| `facts` | Up to 16 labelled `[label, value]` Details rows, such as `["Directory", cwd]` |
|
|
1279
|
-
| `concurrency` | `{ key, limit }`: at most `limit` executions sharing `key` run at once. Later ones wait as `queued` tasks and `run` starts when a slot frees |
|
|
1278
|
+
| `concurrency` | `{ key, limit }`: at most `limit` executions sharing `key` run at once. Lanes belong to one `kind`, so another executor can reuse the key. Later ones wait as `queued` tasks and `run` starts when a slot frees |
|
|
1280
1279
|
| `background`, `signal`, `onUpdate` | Requested handoff, parent cancellation, and foreground progress forwarding |
|
|
1280
|
+
| `present` | `(width, theme)` returning `string[]` or `undefined`: renders the running task's Output in `/tasks` with its own styling, such as colored columns or an elapsed time. Return lines already fitted to `width` (the panel clips any that are wider), or `undefined` for the default view. It runs synchronously on every repaint, so keep it cheap, and it must not return untrusted text without removing terminal sequences. It exists in memory for this execution only: never persisted, not part of any `TaskSnapshot`, dropped at settlement and absent after a restore. A throwing presenter is ignored. `display` stays the plain fallback for other consumers |
|
|
1281
1281
|
|
|
1282
1282
|
`kind` is an executor-owned source label matching `[a-z][a-z0-9-]{0,63}`; the runtime never dispatches on it. Presentation is declared data: the panel and completion card render `label`, `input`, `facts`, and `output` the same way for every executor. Input and facts restore with terminal task history; facts also appear in the completion notification. At most eight background executions, queued ones included, are retained as active at once; foreground executions are not limited by them. Foreground and background results have separate history quotas.
|
|
1283
1283
|
|
|
1284
|
-
A caller can be handed off to the background while its task is queued, before `run` starts, or after `run` calls `accept()`. A queued task becomes `running` when its `run` starts; cancelling it settles it as `cancelled` without starting it.
|
|
1284
|
+
A caller can be handed off to the background while its task is queued, before `run` starts, or after `run` calls `accept()`. A release requested while `run` is still preparing (a `background: true` submission, or a user detach) waits for `accept()`; if `run` settles first, the caller gets the result inline and the task stays `foreground`. `mode` is therefore `background` exactly when the caller was released with a background outcome. A queued task becomes `running` when its `run` starts; cancelling it settles it as `cancelled` without starting it.
|
|
1285
1285
|
|
|
1286
|
-
|
|
1286
|
+
Background work is admitted, submitted or detached, only while the host can supervise it: the session's `tasks` management tool must be active, because the model could otherwise start work it cannot read or stop. A rejected submission starts nothing and its error says so; work admitted earlier keeps running and delivering if the tool is deactivated later.
|
|
1287
1287
|
|
|
1288
|
-
`
|
|
1288
|
+
`run` returns `TaskCompletion<T>`: `{ result, status?, display?, error?, facts?, usage? }`. The original tool result is returned to a foreground caller; task history keeps only its bounded text. Optional `display` is the human-only view that `/tasks` shows in place of the result; `tasks read` and completion context always use `result`. A terminal snapshot carries a display only if this completion supplies one: the last progress display does not survive settlement. Terminal `facts` replace launch facts with the same label, as the shell does with `["Exit", "42"]`. Explicit terminal statuses are `completed`, `partial`, `failed`, `cancelled`, and `timeout`. The host settles usage once, preferring `usage`, then `result.usage`, then the latest published usage; managed foreground results omit usage to avoid billing it twice.
|
|
1289
1289
|
|
|
1290
|
-
|
|
1290
|
+
`TaskControl<T>` exposes `id`, `signal`, `mode`, `onModeChange(listener)`, `accept()`, `publish(update)`, `attachOutput(path, cleanup?)`, and `cancel()`. `publish({ result, display?, usage? })` replaces the bounded model-readable result and optionally the live human-only display and cumulative usage; while it runs `/tasks` shows the display in place of the result, while model reads and completion notifications use only the result. A task with an attached output file is read from that file instead. Call `accept()` after whole-invocation preflight, before allowing a background handoff; `onModeChange` listeners run when the mode becomes `background`. Use the control's signal throughout execution. Parent-call cancellation is disconnected after handoff, while runtime shutdown still cancels the task. Mode subscriptions concern only this task; executors do not need the global registry to observe their own handoff or cancel themselves.
|
|
1291
|
+
|
|
1292
|
+
Completion messages store `{ version: 3, task }` in `details`, a bounded terminal `TaskSnapshot`; only their bounded `content` enters model context. Reads do not acknowledge delivery, and arbitrary tool-result detail fields cannot acknowledge a task. An undelivered completion is a retention lease the session holds on its task: the record and its output stay available until the model has it, and a runtime embedded without a session never holds one, so its settled records are ordinary bounded history.
|
|
1291
1293
|
|
|
1292
1294
|
```typescript
|
|
1293
1295
|
const outcome = await ctx.tasks.execute({
|
|
@@ -1309,7 +1311,7 @@ if (outcome.kind === "result") return outcome.result;
|
|
|
1309
1311
|
return { content: [{ type: "text", text: `Task ${outcome.task.id} continues in the background.` }], details: undefined };
|
|
1310
1312
|
```
|
|
1311
1313
|
|
|
1312
|
-
When Pi attaches the runtime to its durable session lifecycle, `ctx.tasks.session` exposes `stoppedByTreeNavigation(targetId)`, which lists running tasks that navigating to an entry would cancel. Standalone runtimes omit it.
|
|
1314
|
+
When Pi attaches the runtime to its durable session lifecycle, `ctx.tasks.session` exposes `stoppedByTreeNavigation(targetId)`, which lists running tasks that navigating to an entry would cancel. Standalone runtimes omit it.
|
|
1313
1315
|
|
|
1314
1316
|
`TaskRuntime`, `TaskRuntimeOptions`, and the public `Task*`/`TasksContext`/`TasksSessionContext` types are exported from `@astralyn/pi`. Captured capabilities close with their runtime; obtain the replacement from the new session context after reload or session replacement.
|
|
1315
1317
|
|
package/docs/json.md
CHANGED
|
@@ -33,6 +33,7 @@ type AgentSessionEvent =
|
|
|
33
33
|
| Exclude<AgentEvent, { type: "agent_end" }>
|
|
34
34
|
| { type: "agent_end"; messages: AgentMessage[]; willRetry: boolean }
|
|
35
35
|
| { type: "agent_settled"; aborted: boolean }
|
|
36
|
+
| { type: "session_quiescent" }
|
|
36
37
|
| { type: "queue_update"; steering: readonly string[]; followUp: readonly string[] }
|
|
37
38
|
| { type: "compaction_start"; reason: "manual" | "threshold" | "overflow" }
|
|
38
39
|
| { type: "entry_appended"; entry: SessionEntry }
|
|
@@ -49,6 +50,8 @@ type AgentSessionEvent =
|
|
|
49
50
|
```
|
|
50
51
|
```
|
|
51
52
|
|
|
53
|
+
`session_quiescent` means nothing more will happen without new input: no run, compaction, retry, or branch summary, no unsettled Background task, and no Background completion waiting to start a turn. It is emitted once each time the session reaches that state, after the `agent_settled` of the last run. In `--mode json` the stream ends with it: print mode waits for quiescence after every prompt, so the turns that Background completions start are part of the stream, and a line on stderr names the tasks it is waiting for.
|
|
54
|
+
|
|
52
55
|
`queue_update` emits the full pending steering and follow-up queues whenever they change. `compaction_start` and `compaction_end` cover both manual and automatic compaction.
|
|
53
56
|
|
|
54
57
|
Base events retained by `AgentSessionEvent` come from `AgentEvent` in [`@earendil-works/pi-agent-core`](https://www.npmjs.com/package/@earendil-works/pi-agent-core); its `agent_end` member is replaced above:
|
|
@@ -88,7 +88,7 @@ Each `modelOverrides` key is the ID of a built-in or extension model. Unknown ID
|
|
|
88
88
|
|
|
89
89
|
## Input limits
|
|
90
90
|
|
|
91
|
-
See [
|
|
91
|
+
See [Image Input Limits](models.md#image-input-limits).
|
|
92
92
|
|
|
93
93
|
| Field | Type | Default | Description |
|
|
94
94
|
|---|---|---|---|
|
package/docs/models.md
CHANGED
|
@@ -153,18 +153,7 @@ At provider level, `api` is the default for entries in that provider's `models`
|
|
|
153
153
|
|
|
154
154
|
Each key in `providers` is the provider ID used by `/model`, `--provider`, saved credentials, and model references.
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
| --- | --- |
|
|
158
|
-
| `name` | Optional display name for the provider. Defaults to its ID. |
|
|
159
|
-
| `baseUrl` | Default endpoint for this provider's models. A new provider normally needs this for its first custom model; a model may override it. |
|
|
160
|
-
| `api` | Default [API tag](#api-tags) for entries in `models`. |
|
|
161
|
-
| `apiKey` | Optional fallback API-key value; see [Value Resolution](#value-resolution) and [credential resolution](providers.md#resolution-order). |
|
|
162
|
-
| `oauth` | Dynamic OAuth provider type. `"radius"` is the only supported value and requires a Radius gateway `baseUrl`; see [Providers: Radius](providers.md#radius). |
|
|
163
|
-
| `headers` | Request headers for every model on the provider; see [Custom Headers](#custom-headers). |
|
|
164
|
-
| `authHeader` | Adds `Authorization: Bearer <resolved apiKey>` after resolving authentication. |
|
|
165
|
-
| `compat` | Compatibility defaults merged into the provider's models; see [Compatibility Options](#compatibility-options). |
|
|
166
|
-
| `models` | Custom model definitions. On a built-in provider they are added or replaced by `id`; other catalog models remain. |
|
|
167
|
-
| `modelOverrides` | Partial changes for matching final models, including built-in and extension-registered models; see [Per-model Overrides](#per-model-overrides). |
|
|
156
|
+
See [Provider fields](models-configuration.md#providers) for the complete field reference and defaults. `oauth: "radius"` selects the Radius OAuth flow and requires a gateway `baseUrl`; see [Providers: Radius](providers.md#radius).
|
|
168
157
|
|
|
169
158
|
A custom model needs an effective `baseUrl` and `api`. For a new provider, put both on the provider (the usual approach) or on its first model. On a built-in provider, a model that replaces an existing ID can fall back to that catalog model's endpoint and API. `contextWindow` and `maxTokens`, when supplied in a `models` entry, must be positive.
|
|
170
159
|
|
|
@@ -236,24 +225,7 @@ Provider headers apply to every request. A configured model header overrides the
|
|
|
236
225
|
|
|
237
226
|
A `models` entry supplies a complete custom model definition with useful defaults. A model matching a built-in ID replaces that catalog entry rather than partially modifying it; use [`modelOverrides`](#per-model-overrides) for partial changes.
|
|
238
227
|
|
|
239
|
-
|
|
240
|
-
| --- | --- | --- | --- |
|
|
241
|
-
| `id` | Yes | — | Model identifier sent to the API. |
|
|
242
|
-
| `name` | No | `id` | Human-readable label. It is searchable by `--model` patterns and shown as model detail text. |
|
|
243
|
-
| `api` | No | Provider `api` | [API tag](#api-tags) for this model. |
|
|
244
|
-
| `baseUrl` | No | Provider `baseUrl` | Endpoint override for this model. |
|
|
245
|
-
| `reasoning` | No | `false` | Whether the model supports Pi thinking levels. |
|
|
246
|
-
| `thinkingLevelMap` | No | omitted | Maps Pi thinking levels to provider values and declares unsupported levels; see below. |
|
|
247
|
-
| `input` | No | `["text"]` | Supported input kinds: `text` and `image`. |
|
|
248
|
-
| `inputLimits` | No | omitted | Request limits and image preprocessing for this model; see [Image Input Limits](#image-input-limits). |
|
|
249
|
-
| `contextWindow` | No | `128000` | Context window in tokens. |
|
|
250
|
-
| `maxTokens` | No | `16384` | Maximum generated tokens. |
|
|
251
|
-
| `samplingParamsByThinkingLevel` | No | omitted | Per-level sampling overrides; see [Configure sampling by thinking level](#configure-sampling-by-thinking-level). |
|
|
252
|
-
| `samplingParams` | No | omitted | Free-form sampling parameters merged verbatim into every request body; see [Sampling Parameters](#sampling-parameters). |
|
|
253
|
-
| `cost` | No | all rates `0` | Per-million-token rates and optional request-wide price tiers. |
|
|
254
|
-
| `promptCache` | No | omitted | Best-effort prompt cache lifetime in seconds per retention tier; see [Prompt Cache Lifetimes](#prompt-cache-lifetimes). |
|
|
255
|
-
| `headers` | No | omitted | Model-specific request headers; see [Custom Headers](#custom-headers). |
|
|
256
|
-
| `compat` | No | provider `compat` | Compatibility refinements merged with provider defaults. |
|
|
228
|
+
See [Model fields](models-configuration.md#models) for the complete field reference, including defaults and nested cost and input-limit shapes.
|
|
257
229
|
|
|
258
230
|
`/model`, `--list-models`, and the footer show a model's `id`; `name` does not replace it. The model selector and `--model` pattern matching also search `name`.
|
|
259
231
|
|
|
@@ -506,20 +478,7 @@ Set `compat` on a provider for defaults or on a `models`/`modelOverrides` entry
|
|
|
506
478
|
|
|
507
479
|
For `api: "anthropic-messages"`, use these options for Anthropic-compatible endpoints:
|
|
508
480
|
|
|
509
|
-
|
|
510
|
-
| --- | --- |
|
|
511
|
-
| `supportsEagerToolInputStreaming` | Accepts per-tool `eager_input_streaming`. Default: `true`. When `false`, Pi omits it and sends the legacy `fine-grained-tool-streaming-2025-05-14` beta header for tool-enabled requests. |
|
|
512
|
-
| `supportsLongCacheRetention` | Accepts `cache_control.ttl: "1h"` for long cache retention. Default: `true`. |
|
|
513
|
-
| `sendSessionAffinityHeaders` | Sends `x-session-affinity` from the session ID when caching is enabled. Default: `false`; built-in model metadata can enable it for providers that need it. |
|
|
514
|
-
| `supportsCacheControlOnTools` | Accepts Anthropic `cache_control` on tool definitions. Default: `true`. |
|
|
515
|
-
| `supportsTemperature` | Accepts non-default `temperature`. Default: `true`; set `false` for endpoints such as Claude Opus 4.7+ that reject it. |
|
|
516
|
-
| `forceAdaptiveThinking` | Uses adaptive thinking (`thinking.type: "adaptive"` and `output_config.effort`) regardless of model ID. Default: `false`. |
|
|
517
|
-
| `allowEmptySignature` | Replays an empty thinking signature as `signature: ""` instead of converting the thinking block to text. Default: `false`. |
|
|
518
|
-
| `supportsStrictTools` | Accepts strict JSON-schema tool definitions. Default: `false`; capable built-in Anthropic models set it in their metadata. |
|
|
519
|
-
| `supportsMidConvoEffort` | Accepts effort-only system messages and thinking binding controls. Default: `false`. |
|
|
520
|
-
| `supportsMidConvoSystemMessages` | Accepts system-role messages inside the conversation. When `false`, later system messages fold into the top-level system prompt. Default: `false`. |
|
|
521
|
-
| `supportsMidConvoToolChanges` | Accepts mid-conversation `tool_addition` and `tool_removal` blocks. Requires `supportsMidConvoSystemMessages`. Default: `false`. |
|
|
522
|
-
| `allowedFallbackModels` | Models Anthropic accepts in `fallbacks` for server-side refusal fallback, with local pricing metadata for returned fallback responses. Omit `fallbacks` when empty. |
|
|
481
|
+
See [Anthropic Messages compatibility fields](models-configuration.md#anthropic-messages) for the complete field reference and defaults.
|
|
523
482
|
|
|
524
483
|
For example, a proxy that lacks eager streaming but supports long cache retention and adaptive thinking can use:
|
|
525
484
|
|
|
@@ -554,36 +513,9 @@ For example, a proxy that lacks eager streaming but supports long cache retentio
|
|
|
554
513
|
|
|
555
514
|
For `api: "openai-completions"`, Pi auto-detects many defaults from the provider ID and base URL. Set an option only when the endpoint differs from that behavior.
|
|
556
515
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
| `supportsDeveloperRole` | Uses `developer` rather than `system` for reasoning models. Default: URL-detected. |
|
|
561
|
-
| `supportsReasoningEffort` | Accepts `reasoning_effort` when the selected thinking format uses it. Default: URL-detected. |
|
|
562
|
-
| `supportsUsageInStreaming` | Accepts `stream_options: { "include_usage": true }`. Default: `true`. |
|
|
563
|
-
| `supportsFinishReason` | Whether streamed responses include `finish_reason`. When `false`, Pi infers `stop` or `toolUse` when the stream ends. Default: `true`. |
|
|
564
|
-
| `maxTokensField` | Selects `max_completion_tokens` or `max_tokens`. Default: URL-detected. |
|
|
565
|
-
| `requiresToolResultName` | Requires `name` on tool-result messages. Default: URL-detected. |
|
|
566
|
-
| `requiresAssistantAfterToolResult` | Requires an assistant message before a user message following tool results. Default: URL-detected. |
|
|
567
|
-
| `requiresThinkingAsText` | Replays thinking blocks as text delimited by `<thinking>`. Default: URL-detected. |
|
|
568
|
-
| `requiresReasoningContentOnAssistantMessages` | Includes empty `reasoning_content` on replayed assistant messages when reasoning is enabled. Default: URL-detected. |
|
|
569
|
-
| `thinkingFormat` | One of `openai`, `openrouter`, `deepseek`, `together`, `zai`, `qwen`, `chat-template`, `qwen-chat-template`, `string-thinking`, or `ant-ling`. Default: `openai` unless URL detection selects a known provider format. |
|
|
570
|
-
| `chatTemplateKwargs` | `chat_template_kwargs` for `thinkingFormat: "chat-template"`. Values may be strings, numbers, booleans, `null`, or `{ "$var": "thinking.enabled" | "thinking.effort" | "thinking.budget", "omitWhenOff"?: true }`. |
|
|
571
|
-
| `chatTemplateArgs` | `chat_template_args` for `thinkingFormat: "baseten"`. Values may be strings, numbers, booleans, `null`, or `{ "$var": "thinking.enabled" | "thinking.effort" | "thinking.budget", "omitWhenOff"?: true }`. |
|
|
572
|
-
| `thinkingTokenBudgetField` | Top-level request field used to cap reasoning tokens from `thinkingBudgets`, clamped so at least 1024 tokens remain for the answer. Use `thinking_token_budget` for vLLM, `thinking_budget` for Qwen/DashScope/SGLang, or `thinking_budget_tokens` for llama.cpp. Off by default. |
|
|
573
|
-
| `supportsThinkingTokenBudget` | Alias for `thinkingTokenBudgetField: "thinking_token_budget"` (vLLM). Prefer `thinkingTokenBudgetField`. Default: `false`. |
|
|
574
|
-
| `zaiToolStream` | Sends top-level `tool_stream: true` when tools are present. Default: `false`; use only for Z.AI-compatible endpoints that require it. |
|
|
575
|
-
| `cacheControlFormat` | `anthropic` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and final text content when caching is enabled. |
|
|
576
|
-
| `sendSessionAffinityHeaders` | Sends session-affinity headers from the session ID when caching is enabled. Default: `false`. |
|
|
577
|
-
| `sessionAffinityFormat` | `openai` sends `session_id`, `x-client-request-id`, and `x-session-affinity`; `openai-nosession` omits `session_id`; `openrouter` sends `x-session-id`. It does not change `prompt_cache_key`. Default: URL-detected. |
|
|
578
|
-
| `supportsStrictMode` | Accepts strict JSON-schema function tools. Default: URL-detected. |
|
|
579
|
-
| `supportsOpenAIGrammarTools` | Emits OpenAI Lark/regex grammar tools. When `false`, grammar-constrained tools fall back to normal function tools. Default: `false`. |
|
|
580
|
-
| `supportsMidConvoSystemMessages` | Accepts system or developer messages after the conversation has started. When `false`, later system messages fold into the leading system message. Default: `false`. |
|
|
581
|
-
| `supportsMidConvoToolAdditions` | Accepts tools introduced by mid-conversation system messages. Requires `supportsMidConvoSystemMessages`. Default: `false`. |
|
|
582
|
-
| `supportsLongCacheRetention` | Accepts long prompt-cache retention: `prompt_cache_retention: "24h"`, or `cache_control.ttl: "1h"` with `cacheControlFormat: "anthropic"`. Default: `true` except URL-detected incompatible endpoints. |
|
|
583
|
-
| `openRouterRouting` | OpenRouter routing preferences sent unchanged in the request `provider` field. |
|
|
584
|
-
| `vercelGatewayRouting` | Vercel AI Gateway routing preferences (`only`, `order`) sent as `providerOptions.gateway`. |
|
|
585
|
-
|
|
586
|
-
Thinking formats use these request shapes: `openai` sends `reasoning_effort`; `openrouter` sends `reasoning: { effort }`; `deepseek` sends `thinking: { type }` and, when enabled, `reasoning_effort`; `together` sends `reasoning: { enabled }` and optionally `reasoning_effort`; `zai` sends `thinking: { type }`; `qwen` sends `enable_thinking`; `qwen-chat-template` sends `chat_template_kwargs.enable_thinking` and `preserve_thinking`; `string-thinking` sends top-level `thinking` as a string; and `ant-ling` sends `reasoning: { effort }` only for a non-null mapped effort. `chat-template` sends the configured `chatTemplateKwargs`.
|
|
516
|
+
See [OpenAI Chat Completions compatibility fields](models-configuration.md#openai-completions) for the complete field reference and defaults.
|
|
517
|
+
|
|
518
|
+
Thinking formats use these request shapes: `openai` sends `reasoning_effort`; `openrouter` sends `reasoning: { effort }`; `deepseek` sends `thinking: { type }` and, when enabled, `reasoning_effort`; `together` sends `reasoning: { enabled }` and optionally `reasoning_effort`; `baseten` sends the configured `chatTemplateArgs` and optionally `reasoning_effort`; `zai` sends `thinking: { type }`; `qwen` sends `enable_thinking`; `qwen-chat-template` sends `chat_template_kwargs.enable_thinking` and `preserve_thinking`; `string-thinking` sends top-level `thinking` as a string; and `ant-ling` sends `reasoning: { effort }` only for a non-null mapped effort. `chat-template` sends the configured `chatTemplateKwargs`.
|
|
587
519
|
|
|
588
520
|
`thinkingTokenBudgetField` is independent of `thinkingFormat`. Do not enable it on the generated Qwen catalog: those models already send `reasoning_effort`, and DashScope rejects `thinking_budget` together with `reasoning_effort`.
|
|
589
521
|
|
|
@@ -686,17 +618,7 @@ Vercel AI Gateway example:
|
|
|
686
618
|
|
|
687
619
|
### OpenAI Responses
|
|
688
620
|
|
|
689
|
-
For `api: "openai-responses"`,
|
|
690
|
-
|
|
691
|
-
| Field | Description |
|
|
692
|
-
| --- | --- |
|
|
693
|
-
| `supportsDeveloperRole` | Uses `developer` rather than `system` for reasoning models. Default: `true`. |
|
|
694
|
-
| `sessionAffinityFormat` | `openai` sends `session_id` and `x-client-request-id`; `openai-nosession` omits `session_id`; `openrouter` sends `x-session-id`. It does not change `prompt_cache_key`. |
|
|
695
|
-
| `supportsLongCacheRetention` | Accepts long cache retention: `prompt_cache_options.ttl: "30m"` when `supportsExplicitPromptCacheMode` is enabled (including GPT-5.6+ Responses models), otherwise `prompt_cache_retention: "24h"`. Default: `true`. |
|
|
696
|
-
| `supportsStrictMode` | Accepts strict JSON-schema function tools. Default: `false`. |
|
|
697
|
-
| `supportsOpenAIGrammarTools` | Emits OpenAI Lark/regex grammar tools. Default: `false`. |
|
|
698
|
-
| `supportsToolSearch` | Supports client-executed deferred tool search. Default: `false`. |
|
|
699
|
-
| `supportsExplicitPromptCacheMode` | Accepts `prompt_cache_options: { mode: "explicit" }` when cache retention is `none`, which disables implicit prompt caching; long cache retention uses `{ ttl: "30m" }` when `supportsLongCacheRetention` is also enabled. Default: `false`. |
|
|
621
|
+
For `api: "openai-responses"`, see [Responses compatibility fields](models-configuration.md#openai-responses) for the complete field reference and defaults.
|
|
700
622
|
|
|
701
623
|
The public Pi AI model type also exposes the Responses compatibility shape for `azure-openai-responses` and `openai-codex-responses`. The Azure transport uses `supportsDeveloperRole`, `supportsStrictMode`, and `supportsOpenAIGrammarTools`; the Codex transport uses `supportsStrictMode`, `supportsOpenAIGrammarTools`, and `supportsToolSearch`. Other Responses options do not alter those transports.
|
|
702
624
|
|
package/docs/rpc.md
CHANGED
|
@@ -201,6 +201,7 @@ Response:
|
|
|
201
201
|
"thinkingLevel": "medium",
|
|
202
202
|
"isStreaming": false,
|
|
203
203
|
"isCompacting": false,
|
|
204
|
+
"isQuiescent": true,
|
|
204
205
|
"steeringMode": "all",
|
|
205
206
|
"followUpMode": "one-at-a-time",
|
|
206
207
|
"sessionFile": "/path/to/session.jsonl",
|
|
@@ -213,6 +214,8 @@ Response:
|
|
|
213
214
|
}
|
|
214
215
|
```
|
|
215
216
|
|
|
217
|
+
`isQuiescent` is true when no run, compaction, retry, unsettled Background task, or pending Background completion turn remains (see [session_quiescent](#session_quiescent)).
|
|
218
|
+
|
|
216
219
|
The `model` field is a full [Model](#model) object when one is selected, otherwise it is omitted. The `sessionFile` and `sessionName` fields are also omitted when unavailable; `sessionName` is the display name set via `set_session_name`.
|
|
217
220
|
|
|
218
221
|
#### get_messages
|
|
@@ -895,6 +898,7 @@ Events are streamed to stdout as JSON lines during agent operation. Events do no
|
|
|
895
898
|
| `agent_start` | Agent begins processing |
|
|
896
899
|
| `agent_end` | One low-level agent run completes (may still be followed by retry, compaction, or queued continuations) |
|
|
897
900
|
| `agent_settled` | Agent run is fully settled; no automatic retry, compaction retry, or queued continuation remains |
|
|
901
|
+
| `session_quiescent` | Nothing more will happen without new input, including Background tasks and the turns their completions start |
|
|
898
902
|
| `turn_start` | New turn begins |
|
|
899
903
|
| `turn_end` | Turn completes (includes assistant message and tool results) |
|
|
900
904
|
| `message_start` | Message begins |
|
|
@@ -947,6 +951,16 @@ Emitted after the full session-level run settles. At this point Pi will not cont
|
|
|
947
951
|
{"type": "agent_settled", "aborted": false}
|
|
948
952
|
```
|
|
949
953
|
|
|
954
|
+
### session_quiescent
|
|
955
|
+
|
|
956
|
+
Emitted when nothing more will happen without new input: no run, compaction, retry, or branch summary is active, no Background task is unsettled, and no Background completion is waiting to start a turn. A Background completion starts a new run after `agent_settled`, so a client that needs "all work done" waits for this event instead. It is emitted once per transition into that state, after the `agent_settled` of the last run. A completion whose delivery failed does not count, because it is retried only on the next input.
|
|
957
|
+
|
|
958
|
+
```json
|
|
959
|
+
{"type": "session_quiescent"}
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
`RpcClient.waitForQuiescence(timeout?)` resolves on this event, or at once when `get_state` reports `isQuiescent`; `collectEventsUntilQuiescent(timeout?)` collects events until it. `waitForIdle()` and `collectEvents()` still resolve on the first `agent_settled`. Closing stdin shuts the process down immediately, without waiting for Background work.
|
|
963
|
+
|
|
950
964
|
### turn_start / turn_end
|
|
951
965
|
|
|
952
966
|
A turn consists of one assistant response plus any resulting tool calls and results.
|
package/docs/sdk.md
CHANGED
|
@@ -71,6 +71,10 @@ interface AgentSession {
|
|
|
71
71
|
// Send a prompt and wait for completion
|
|
72
72
|
prompt(text: string, options?: PromptOptions): Promise<void>;
|
|
73
73
|
|
|
74
|
+
// Resolve once no run, compaction, retry or Background work remains, including completion turns
|
|
75
|
+
waitForQuiescence(signal?: AbortSignal): Promise<void>;
|
|
76
|
+
readonly isQuiescent: boolean;
|
|
77
|
+
|
|
74
78
|
// Queue messages, optionally with image attachments
|
|
75
79
|
steer(text: string, images?: ImageContent[]): Promise<void>;
|
|
76
80
|
followUp(text: string, images?: ImageContent[]): Promise<void>;
|
|
@@ -246,16 +250,19 @@ Both `steer()` and `followUp()` expand file-based prompt templates but error on
|
|
|
246
250
|
|
|
247
251
|
### Background execution
|
|
248
252
|
|
|
249
|
-
Background is a session-owned capability, not a daemon or a flag on `createAgentSession()`. Interactive mode
|
|
253
|
+
Background is a session-owned capability, not a daemon or a flag on `createAgentSession()`. Interactive mode and the built-in print, JSON, and RPC hosts enable it. Ordinary SDK sessions leave it disabled and reject native shell or extension calls with `background: true`. Normal foreground tools still work, including standalone `createBashTool()` / `createPowerShellTool()` calls without a session context.
|
|
250
254
|
|
|
251
255
|
An embedding can opt in with `await session.bindExtensions({ tasksEnabled: true })`. Bind before prompting; this also binds extensions and emits their session-start lifecycle. When using `AgentSessionRuntime`, rebind the replacement session through the runtime's rebind callback. Setting `mode` or providing a UI alone does not enable Background.
|
|
252
256
|
|
|
257
|
+
Background work is admitted only while the `tasks` management tool is active in the session, so the model that starts work can always read and stop it. The bundled `builtin:tasks` extension provides that tool; a session that disables the extension or deactivates the tool rejects `background: true` and every detach path (the error says no work was started), while work already admitted keeps running and delivering.
|
|
258
|
+
|
|
253
259
|
The host must choose:
|
|
254
260
|
|
|
255
261
|
- **Lifetime and cancellation:** a settled main-agent turn is not the end of background work. `session.abort()` cancels foreground-owned work, not detached work. Use `session.tasks.kill(id)` for a whole execution, or `await session.tasks.shutdown(graceMs?)` to close admission, cancel all owned work and drain within a bounded grace period (default 2 seconds, maximum 60 seconds). A non-cooperating executor can outlive that grace period.
|
|
256
262
|
- **Result-driven turns and cost:** automatic completions join the host's prompt queue, so a running turn receives them as steering and an idle session starts a completion turn. Use the nestable `session.pauseTaskNotifications()` to defer delivery; its returned release function is idempotent. Pausing does not stop execution or its costs. Budget worker activity and completion-triggered turns explicitly.
|
|
257
|
-
- **Observation and delivery:** `session.tasks` exposes the [public supervision methods](extensions.md#ctxtasks). `
|
|
263
|
+
- **Observation and delivery:** `session.tasks` exposes the [public supervision methods](extensions.md#ctxtasks). `list()`, `get()` and `read()` observe; a terminal snapshot alone does not acknowledge model delivery, and there is no blocking wait: subscribe with `session.tasks.subscribe()` or use `waitForQuiescence()` below. A direct SDK consumer that handles a result outside model history can call `session.acknowledgeTaskDelivery(task.id)` after successful consumption, which stops the session from notifying the model about it (an unknown or already delivered ID is ignored). An undelivered completion keeps its task and output retained until it is delivered or acknowledged, or its runtime closes. Failed notification delivery leaves the result available for inspection; after fixing the host failure, call `session.retryTaskNotifications()` explicitly. Pi does not retry a failed delivery forever on a timer.
|
|
258
264
|
- **Shutdown:** `await runtime.dispose()` handles background shutdown and extension lifecycle when using the runtime API. For a directly owned session, drain Background, abort the main agent, then dispose; synchronous `session.dispose()` alone is not an awaited drain.
|
|
265
|
+
- **Waiting for all work:** `prompt()` resolves when its own run settles, before any Background completion turn. `await session.waitForQuiescence(signal?)` resolves once the session is quiescent: no run, compaction, retry or branch summary, no unsettled Background task, and no completion left to deliver (the `session_quiescent` event marks the same moment, once per transition). It uses no timers and rejects when `signal` aborts or the session is disposed. A completion whose delivery failed does not count, since it retries only on the next input; a Background task that never ends keeps the wait open, so pass a signal when that matters.
|
|
259
266
|
|
|
260
267
|
A deliberately single-turn embedding can suppress completion-triggered turns and cancel leftover work on exit:
|
|
261
268
|
|
|
@@ -378,6 +385,9 @@ session.subscribe((event) => {
|
|
|
378
385
|
// Main agent, retries, compaction, and queued continuations have settled
|
|
379
386
|
// Background executions may still be active
|
|
380
387
|
break;
|
|
388
|
+
case "session_quiescent":
|
|
389
|
+
// Also no unsettled Background task and no completion turn still to come
|
|
390
|
+
break;
|
|
381
391
|
|
|
382
392
|
// Turn lifecycle (one LLM response + tool calls)
|
|
383
393
|
case "turn_start":
|