mu-agent 0.1.8 → 0.1.13

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.
Files changed (176) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +34 -30
  3. package/dist/bundle/chunks/anthropic-messages-4PAJY4R6.js +6 -0
  4. package/dist/bundle/chunks/anthropic.js +1 -1
  5. package/dist/bundle/chunks/azure-openai-responses-Y4BBWQ3N.js +2 -0
  6. package/dist/bundle/chunks/bedrock-converse-stream.js +12 -9
  7. package/dist/bundle/chunks/{chunk-NG4PO5FP.js → chunk-3J3HSHLX.js} +128 -110
  8. package/dist/bundle/chunks/{chunk-TRDNDS6A.js → chunk-3V45WARH.js} +6 -3
  9. package/dist/bundle/chunks/{chunk-S3GGRYIO.js → chunk-5AGIVGSV.js} +1 -1
  10. package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
  11. package/dist/bundle/chunks/{chunk-4OBXYPH4.js → chunk-6Z2347ML.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-F5ZABL6Z.js → chunk-7CBJINYJ.js} +2 -2
  13. package/dist/bundle/chunks/{chunk-DRQDKNXA.js → chunk-7DDKXS2Q.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-H3QXYIF7.js → chunk-CF5GWZHW.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-RLZVFGW4.js → chunk-DYTENXHX.js} +29 -92
  16. package/dist/bundle/chunks/{chunk-NTSWBG4X.js → chunk-FLHKNMLQ.js} +1 -1
  17. package/dist/bundle/chunks/chunk-FTJT2X6E.js +2 -0
  18. package/dist/bundle/chunks/{chunk-4L3WN2XY.js → chunk-JXWJOIH4.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-TAOL3JFA.js → chunk-KIDDRTZP.js} +4 -4
  20. package/dist/bundle/chunks/chunk-LHRPMXVI.js +2 -0
  21. package/dist/bundle/chunks/chunk-LMNWTX2L.js +11 -0
  22. package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
  23. package/dist/bundle/chunks/chunk-LT2C56HV.js +130 -0
  24. package/dist/bundle/chunks/{chunk-6CYCI3EJ.js → chunk-LVVZS2NO.js} +1 -1
  25. package/dist/bundle/chunks/chunk-MF3VB62R.js +91 -0
  26. package/dist/bundle/chunks/chunk-NN4BEPYX.js +2 -0
  27. package/dist/bundle/chunks/{chunk-LD2P6SFZ.js → chunk-PMRZO46Y.js} +1 -1
  28. package/dist/bundle/chunks/chunk-R4VPBBDS.js +2 -0
  29. package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
  30. package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
  31. package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
  32. package/dist/bundle/chunks/chunk-URBBMV3M.js +9 -0
  33. package/dist/bundle/chunks/chunk-WCGU75WO.js +11 -0
  34. package/dist/bundle/chunks/chunk-WCJNJQO5.js +9 -0
  35. package/dist/bundle/chunks/chunk-WDURT4ZM.js +2 -0
  36. package/dist/bundle/chunks/{cli-AOVD3SSZ.js → cli-M4YBZZ6Q.js} +4 -4
  37. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-MEZHQVNP.js +2 -0
  38. package/dist/bundle/chunks/codemode-worker.js +117 -13
  39. package/dist/bundle/chunks/easter-egg-3d-VFY3YRQR.js +2 -0
  40. package/dist/bundle/chunks/execute-FKI7Y4XR.js +499 -0
  41. package/dist/bundle/chunks/github-copilot.js +1 -1
  42. package/dist/bundle/chunks/{google-generative-ai-XMFHGQC5.js → google-generative-ai-D2VJQBTK.js} +1 -1
  43. package/dist/bundle/chunks/{google-vertex-4XRRNP74.js → google-vertex-SQG2BO6C.js} +1 -1
  44. package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
  45. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  46. package/dist/bundle/chunks/{jiti-loader-C3NU2SDH.js → jiti-loader-GL4HJFAD.js} +1 -1
  47. package/dist/bundle/chunks/{jiti-static-loader-E6ANQD5T.js → jiti-static-loader-2RCDI6B4.js} +1 -1
  48. package/dist/bundle/chunks/{lib-TEDK3E5I.js → lib-4AVRLBHM.js} +1 -1
  49. package/dist/bundle/chunks/{llama-cpp-classify-XT44PI3C.js → llama-cpp-classify-5YHLY3RC.js} +2 -2
  50. package/dist/bundle/chunks/mistral-conversations-BDARVJTT.js +5 -0
  51. package/dist/bundle/chunks/{multipart-parser-SQ6GDJL7.js → multipart-parser-FMGQ7342.js} +1 -1
  52. package/dist/bundle/chunks/{node-TWVVAGPO.js → node-AUOGOBCN.js} +1 -1
  53. package/dist/bundle/chunks/{node-N5BSDOA5.js → node-KCMANYC2.js} +1 -1
  54. package/dist/bundle/chunks/openai-chatgpt.js +1 -1
  55. package/dist/bundle/chunks/openai-codex-responses-CECGTDYD.js +10 -0
  56. package/dist/bundle/chunks/openai-codex.js +1 -1
  57. package/dist/bundle/chunks/openai-completions-UOA7MELD.js +7 -0
  58. package/dist/bundle/chunks/openai-decisions-HLM372N6.js +5 -0
  59. package/dist/bundle/chunks/openai-responses-TII7ZY5X.js +3 -0
  60. package/dist/bundle/chunks/{openrouter-images-ZVXWCSIM.js → openrouter-images-7T452AS5.js} +1 -1
  61. package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-2UDKLKOO.js} +1 -1
  62. package/dist/bundle/chunks/runtime-IKE5OQIW.js +2 -0
  63. package/dist/bundle/chunks/{src-UK77SYZE.js → src-QKGKDFSU.js} +1 -1
  64. package/dist/bundle/chunks/typesafe-system-one-KNCICMO5.js +2 -0
  65. package/dist/bundle/chunks/virtual-modules-TJPHLTRT.js +2 -0
  66. package/dist/bundle/cli-runtime.js +1 -1
  67. package/dist/bundle/index.js +1 -1
  68. package/dist/bundle/rpc-entry.js +1 -1
  69. package/dist/core/export-html/template.js +21 -16
  70. package/dist/modes/interactive/theme/dark.json +1 -1
  71. package/dist/modes/interactive/theme/light.json +1 -1
  72. package/dist/modes/interactive/theme/theme-schema.d.ts +28 -0
  73. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
  74. package/dist/modes/interactive/theme/theme-schema.js +105 -0
  75. package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
  76. package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
  77. package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
  78. package/dist/modes/interactive/theme/theme-tokens.js +94 -0
  79. package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
  80. package/dist/modes/interactive/theme/theme.d.ts +13 -16
  81. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  82. package/dist/modes/interactive/theme/theme.js +22 -29
  83. package/dist/modes/interactive/theme/theme.js.map +1 -1
  84. package/docs/cli-integration.md +1 -1
  85. package/docs/cli.md +22 -10
  86. package/docs/codemode.md +29 -6
  87. package/docs/compaction.md +5 -5
  88. package/docs/custom-provider.md +53 -1
  89. package/docs/docs.json +8 -0
  90. package/docs/environment-variables.md +3 -1
  91. package/docs/extensions.md +122 -13
  92. package/docs/json.md +3 -3
  93. package/docs/keybindings.md +5 -4
  94. package/docs/llama-cpp.md +9 -2
  95. package/docs/mcp.md +4 -2
  96. package/docs/message-types.md +22 -4
  97. package/docs/models-configuration.md +187 -0
  98. package/docs/models.md +18 -2
  99. package/docs/nvidia-rtx-llama-cpp.md +96 -0
  100. package/docs/providers.md +47 -0
  101. package/docs/sessions.md +1 -1
  102. package/docs/settings.md +9 -6
  103. package/docs/terminal-setup.md +14 -0
  104. package/docs/themes.md +4 -2
  105. package/docs/tui.md +71 -1
  106. package/docs/usage.md +1 -1
  107. package/examples/README.md +0 -3
  108. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  109. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  110. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  111. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  112. package/examples/extensions/gondolin/package-lock.json +2 -2
  113. package/examples/extensions/gondolin/package.json +1 -1
  114. package/examples/extensions/minimal-mode.ts +4 -5
  115. package/examples/extensions/overlay-test.ts +2 -2
  116. package/examples/extensions/plan-mode/index.ts +6 -2
  117. package/examples/extensions/sandbox/package-lock.json +2 -2
  118. package/examples/extensions/sandbox/package.json +1 -1
  119. package/examples/extensions/subagent/index.ts +4 -6
  120. package/examples/extensions/with-deps/package-lock.json +2 -2
  121. package/examples/extensions/with-deps/package.json +1 -1
  122. package/judge/dist/auth.js +91 -35
  123. package/judge/dist/chrome.js +2 -2
  124. package/judge/dist/import.js +852 -175
  125. package/judge/dist/setup.js +240 -153
  126. package/judge/manifest.json +2512 -546
  127. package/judge/mu/chunk-3NQ5BIBI.js +4773 -0
  128. package/judge/mu/chunk-SKGYWHW6.js +51 -0
  129. package/judge/mu/chunk-WQM4MV6L.js +58 -0
  130. package/judge/mu/cloud-code-V7FOGB4J.js +22802 -0
  131. package/judge/mu/dist-75PS4OC4.js +1283 -0
  132. package/judge/{dist/kyrn-judge.js → mu/index.js} +5365 -32809
  133. package/judge/mu/multipart-parser-ZDXVIOOP.js +372 -0
  134. package/judge/mu/src-TVVA5ZK2.js +1409 -0
  135. package/judge/themes/mu-dark.json +80 -0
  136. package/judge/themes/mu-light.json +80 -0
  137. package/kyrn/bin/kyrn-doctor +44 -19
  138. package/kyrn/bin/kyrn-judge-local +43 -14
  139. package/kyrn/bin/kyrn-ledger +16 -1
  140. package/kyrn/bin/mu.mjs +142 -61
  141. package/kyrn/local-judge/server.py +26 -14
  142. package/package.json +8 -6
  143. package/dist/bundle/chunks/anthropic-messages-MI4QHRXW.js +0 -6
  144. package/dist/bundle/chunks/azure-openai-responses-E3QGN4VN.js +0 -2
  145. package/dist/bundle/chunks/chunk-33KL6IIQ.js +0 -20
  146. package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
  147. package/dist/bundle/chunks/chunk-C47CLQZK.js +0 -2
  148. package/dist/bundle/chunks/chunk-HQHD6PZI.js +0 -2
  149. package/dist/bundle/chunks/chunk-KJUDFE6Y.js +0 -11
  150. package/dist/bundle/chunks/chunk-TE3GUV6H.js +0 -9
  151. package/dist/bundle/chunks/chunk-VIV3CWXG.js +0 -2
  152. package/dist/bundle/chunks/chunk-WE4MXUQS.js +0 -11
  153. package/dist/bundle/chunks/chunk-Y2PP32XV.js +0 -2
  154. package/dist/bundle/chunks/chunk-YLY6CKPK.js +0 -132
  155. package/dist/bundle/chunks/chunk-ZE5FMF3V.js +0 -99
  156. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NM3JRRWQ.js +0 -2
  157. package/dist/bundle/chunks/easter-egg-3d-G2LUBEMZ.js +0 -2
  158. package/dist/bundle/chunks/execute-MZVUC6BB.js +0 -388
  159. package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +0 -13
  160. package/dist/bundle/chunks/mistral-conversations-Q22M3IBG.js +0 -5
  161. package/dist/bundle/chunks/openai-codex-responses-4MYQSO5E.js +0 -10
  162. package/dist/bundle/chunks/openai-completions-OSRCWAAA.js +0 -7
  163. package/dist/bundle/chunks/openai-responses-UHF3DHNG.js +0 -3
  164. package/dist/bundle/chunks/runtime-VJMMDLVH.js +0 -2
  165. package/dist/bundle/chunks/typesafe-system-one-PRR5K6CI.js +0 -2
  166. package/dist/bundle/chunks/virtual-modules-RJXJGCPI.js +0 -2
  167. package/dist/modes/interactive/theme/theme-json.d.ts +0 -85
  168. package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
  169. package/dist/modes/interactive/theme/theme-json.js +0 -132
  170. package/dist/modes/interactive/theme/theme-json.js.map +0 -1
  171. package/dist/modes/interactive/theme/theme-schema.json +0 -361
  172. package/examples/plugins/pi-example-plugin/README.md +0 -38
  173. package/examples/plugins/pi-example-plugin/package.json +0 -10
  174. package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
  175. package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
  176. package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
@@ -29,7 +29,59 @@ Prefer a complete provider for new integrations that own more than static endpoi
29
29
 
30
30
  Registering only `baseUrl` or `headers` for an existing provider preserves its built-in models. Supplying `models` in the legacy form replaces that provider's models across chat, image, and classifier operations. An omitted `type` means `"chat"`; image and classifier models require explicit discriminants and implementations keyed by their `api` values through the `images` and `classifiers` fields.
31
31
 
32
- For example, a mixed-operation provider can register non-chat models and their implementations together:
32
+ ### Redirect an existing provider
33
+
34
+ Register only the fields that should change. This keeps the provider's built-in catalog, authentication, API implementation, and compatibility settings:
35
+
36
+ ```typescript
37
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
38
+
39
+ export default function (pi: ExtensionAPI) {
40
+ pi.registerProvider("anthropic", {
41
+ baseUrl: "https://gateway.example.com/anthropic",
42
+ headers: {
43
+ "X-Gateway-Key": "$GATEWAY_API_KEY",
44
+ },
45
+ });
46
+ }
47
+ ```
48
+
49
+ Use this form for a proxy or gateway that preserves the upstream protocol. Do not copy the built-in model list into the extension because doing so turns a small endpoint override into a catalog replacement that can drift.
50
+
51
+ ### Register a compatible endpoint
52
+
53
+ When a service implements a supported protocol but has its own model catalog, register the endpoint and only the models the service actually exposes:
54
+
55
+ ```typescript
56
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
57
+
58
+ export default function (pi: ExtensionAPI) {
59
+ pi.registerProvider("acme", {
60
+ name: "Acme AI",
61
+ baseUrl: "https://api.acme.example/v1",
62
+ apiKey: "$ACME_API_KEY",
63
+ api: "openai-completions",
64
+ models: [
65
+ {
66
+ type: "chat",
67
+ id: "acme-chat",
68
+ name: "Acme Chat",
69
+ reasoning: false,
70
+ input: ["text"],
71
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
72
+ contextWindow: 128000,
73
+ maxTokens: 8192,
74
+ },
75
+ ],
76
+ });
77
+ }
78
+ ```
79
+
80
+ Prefer `models.json` for this case when no executable extension behavior is needed. Use an extension when registration depends on runtime discovery, custom authentication, or code shared with other extension features.
81
+
82
+ ### Register mixed-operation models
83
+
84
+ A mixed-operation provider can register non-chat models and their implementations together:
33
85
 
34
86
  ```typescript
35
87
  pi.registerProvider("media-tools", {
package/docs/docs.json CHANGED
@@ -47,6 +47,10 @@
47
47
  "title": "Run Local Models",
48
48
  "path": "llama-cpp.md"
49
49
  },
50
+ {
51
+ "title": "Run Local Models on NVIDIA RTX",
52
+ "path": "nvidia-rtx-llama-cpp.md"
53
+ },
50
54
  {
51
55
  "title": "Configure Your Terminal",
52
56
  "path": "terminal-setup.md"
@@ -148,6 +152,10 @@
148
152
  "title": "Settings",
149
153
  "path": "settings.md"
150
154
  },
155
+ {
156
+ "title": "Models Configuration",
157
+ "path": "models-configuration.md"
158
+ },
151
159
  {
152
160
  "title": "Environment Variables",
153
161
  "path": "environment-variables.md"
@@ -86,9 +86,11 @@ These variables are read by Pi itself:
86
86
  | `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no` |
87
87
  | `PI_CACHE_RETENTION` | Set to `long` for extended provider prompt caching where supported |
88
88
  | `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` |
89
- | `PI_RADIUS_GATEWAY` | Override the Radius gateway origin used by `/bug` uploads and Radius relay connections |
89
+ | `PI_RADIUS_GATEWAY` | Override the Radius gateway origin used by `/bug` uploads |
90
90
  | `PI_HARDWARE_CURSOR` | Set to `1` to show the hardware cursor; see [Terminal setup](terminal-setup.md) |
91
+ | `PI_CLIPBOARD_OSC52` | Set to `1` to also send copied text to the terminal with OSC 52 after the local clipboard write, as Pi does over SSH. Use it when a remote client reaches Pi without SSH variables, such as a multiplexer attached from another machine or a remote IDE daemon |
91
92
  | `PI_HYPERLINKS` | Override OSC 8 hyperlink detection with `1`, `0`, or `auto` |
93
+ | `PI_PROGRAM_STATUS` | Override OSC 7501 program status detection: `1` always reports, `0` never reports; otherwise Pi reports only after the terminal confirms support. See [Terminal setup](terminal-setup.md#program-status) |
92
94
  | `PI_IMAGE_PROTOCOL` | Override inline image detection with `kitty`, `iterm2`, `none`, or `auto` |
93
95
  | `PI_TRUE_COLOR` | Override truecolor detection with `1`, `0`, or `auto` |
94
96
  | `PI_TUI_ESC_TIMEOUT` | How long to wait after a lone ESC before treating it as Escape, in milliseconds; defaults to `100` over SSH and `10` otherwise. Increase if Alt-key input is misread as Escape |
@@ -51,7 +51,7 @@ Reload replaces the extension runtime, so code after `await ctx.reload()` must n
51
51
 
52
52
  <a id="understand-the-lifecycle"></a>
53
53
 
54
- ## Respect the runtime lifecycle
54
+ ## Understand the runtime lifecycle
55
55
 
56
56
  The factory can be synchronous or asynchronous. Pi waits for an asynchronous factory before startup continues, allowing it to fetch configuration or register providers needed during startup.
57
57
 
@@ -59,12 +59,95 @@ Do not start processes, sockets, watchers, or timers in the factory because some
59
59
  Start long-lived resources from `session_start` or from the command or tool that needs them.
60
60
  Close session-scoped resources from an idempotent `session_shutdown` handler.
61
61
 
62
- A run proceeds from input and `before_agent_start`, through model, message, and tool events, to `agent_end`.
63
- Automatic retries, recovery, compaction, or queued work can continue afterward.
62
+ A normal prompt follows this hook sequence:
63
+
64
+ ```mermaid
65
+ flowchart TD
66
+ Input["input"] --> Before["before_agent_start"]
67
+ Before --> AgentStart["agent_start"]
68
+ AgentStart --> TurnStart["turn_start"]
69
+ TurnStart --> Context["context"]
70
+ Context --> ContextSystem["context_with_system"]
71
+ ContextSystem --> Headers["before_provider_headers"]
72
+ Headers --> Request["before_provider_request"]
73
+ Request --> Response["after_provider_response"]
74
+ Response --> Stream["provider_stream_event and assistant message events"]
75
+ Stream --> ToolDecision{"Assistant requested tools?"}
76
+ ToolDecision -->|yes| ToolStart["tool_execution_start"]
77
+ ToolStart --> ToolCall["tool_call"]
78
+ ToolCall --> Execute["execute and tool_execution_update"]
79
+ Execute --> ToolResult["tool_result"]
80
+ ToolResult --> ToolEnd["tool_execution_end"]
81
+ ToolEnd --> TurnEnd["turn_end"]
82
+ ToolDecision -->|no| TurnEnd
83
+ TurnEnd --> MoreTurns{"Another model turn required?"}
84
+ MoreTurns -->|yes| TurnStart
85
+ MoreTurns -->|no| AgentEnd["agent_end"]
86
+ AgentEnd --> MoreRuns{"Retry, recovery, compaction retry, or queued work?"}
87
+ MoreRuns -->|yes| AgentStart
88
+ MoreRuns -->|no| BeforeSettle["agent_before_settle"]
89
+ BeforeSettle --> Continue{"Continuation requested?"}
90
+ Continue -->|yes| AgentStart
91
+ Continue -->|no| Settled["agent_settled"]
92
+ ```
93
+
94
+ The lifecycle involves these event groups:
95
+
96
+ | Area | Events |
97
+ |---|---|
98
+ | Input preparation | `input`, `before_agent_start` |
99
+ | Agent run | `agent_start`, `agent_end` |
100
+ | Model turn | `turn_start`, `context`, `context_with_system`, `turn_end` |
101
+ | Provider call | `before_provider_headers`, `before_provider_request`, `after_provider_response`, `provider_stream_event` |
102
+ | Message lifecycle | `message_start`, `message_update`, `message_end` |
103
+ | Tool execution | `tool_execution_start`, `tool_call`, `tool_execution_update`, `tool_result`, `tool_execution_end` |
104
+ | Settlement | `agent_before_settle`, `agent_settled` |
105
+
106
+ Pi counts as busy while a run starts, including the compaction check and `before_agent_start`, so messages sent meanwhile go into the starting run. Message lifecycle events also cover user and tool-result messages; the diagram places only the assistant stream where it occurs in each model turn.
107
+
108
+ Tool hooks run for each tool call, and one assistant response can start another turn by returning tool calls. Provider hooks run only when that transport exposes the corresponding stage. See the exported declarations in [`extensions/types.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/extensions/types.ts) for exact event payloads and result types.
109
+
110
+ Session startup and replacement follow a separate lifecycle:
111
+
112
+ ```mermaid
113
+ flowchart TD
114
+ Startup["Process startup"] --> Trust["project_trust when needed"]
115
+ Trust --> SessionStart["session_start"]
116
+ Switch["/new or /resume"] --> BeforeSwitch["session_before_switch"]
117
+ Fork["/fork or /clone"] --> BeforeFork["session_before_fork"]
118
+ BeforeSwitch --> Shutdown["session_shutdown"]
119
+ BeforeFork --> Shutdown
120
+ Reload["reload"] --> Shutdown
121
+ Shutdown --> Replacing{"Replacing the runtime?"}
122
+ Replacing -->|yes| Replacement["load replacement extension runtime"]
123
+ Replacement --> SessionStart
124
+ Replacing -->|no| Stop["process exits"]
125
+ SessionStart --> Discover["resources_discover"]
126
+ ```
127
+
128
+ `project_trust` runs before trusted project resources load and can run again when session replacement enters a new working directory. Retries, recovery, compaction, and queued messages can start another agent run after `agent_end`, so `agent_end` is not a final-idle signal. Reload and session replacement invalidate the old runtime. Code after `await ctx.reload()` still belongs to the old runtime and must not reuse its session-bound state.
129
+
130
+ ### Choose an event by intent
131
+
132
+ | Goal | Event |
133
+ |---|---|
134
+ | Inspect, transform, or consume raw user input | `input` |
135
+ | Change prompt sections, active tools, or add context before a run | `before_agent_start` |
136
+ | Observe one low-level agent run | `agent_start`, `agent_end` |
137
+ | Act after one model response and its tool calls | `turn_end` |
138
+ | Filter conversation messages for one provider request | `context` |
139
+ | Transform the complete transcript, including system messages | `context_with_system` |
140
+ | Change request headers or the serialized provider payload | `before_provider_headers`, `before_provider_request` |
141
+ | Observe provider response metadata or parsed stream events | `after_provider_response`, `provider_stream_event` |
142
+ | Authorize or modify a tool call | `tool_call` |
143
+ | Redact, annotate, or replace a tool result | `tool_result` |
144
+ | Append final entries or request one last continuation | `agent_before_settle` |
145
+ | Run notification work after automatic processing has stopped | `agent_settled` |
146
+ | Initialize or release session-scoped resources | `session_start`, `session_shutdown` |
147
+
64
148
  <a id="agent_start--agent_end--agent_before_settle--agent_settled"></a>
65
149
 
66
- `agent_before_settle` is the final actionable boundary: it can append entries and request one continuation.
67
- `agent_settled` is final and notification-only; use it when an integration needs to know Pi will not continue automatically.
150
+ Use `agent_before_settle` when an extension must append entries or request one final continuation. Use `agent_settled` for notification-only work that should run after Pi has no automatic continuation remaining.
68
151
 
69
152
  <a id="extensionapi-methods"></a>
70
153
 
@@ -100,7 +183,20 @@ Use each event’s declared result type rather than assuming every return value
100
183
 
101
184
  Events cover resource discovery, sessions, agent and message lifecycle, providers, tools, and raw input.
102
185
 
103
- `before_agent_start` exposes both the current prompt and its structured `systemPromptOptions`. Prefer changing prompt sections, selected tools, or guidelines so Pi can append a transcript delta. Returning `systemPrompt`, or setting `forceSystemPrompt`, replaces the whole prompt for that run while the transcript continues recording the structured sections. Providers receive the forced text as their leading system prompt.
186
+ #### Understand handler composition
187
+
188
+ | Pattern | Behavior |
189
+ |---|---|
190
+ | Notification | Return values are ignored. Use the event for observation only. |
191
+ | In-place mutation | Later handlers see changes to values such as `before_provider_headers.headers`, `before_agent_start.systemPromptOptions`, and `tool_call.input`. |
192
+ | Chained transformation | Later handlers receive the value returned by earlier `input`, `context`, `context_with_system`, `message_end`, `tool_result`, and `before_provider_request` handlers. |
193
+ | Gate | A handled input, blocked tool call, cancelled session operation, or handled `user_bash` command stops that operation's normal fallback path. |
194
+ | Actionable boundary | Each `turn_end` or `agent_before_settle` handler sees the entries and continuation decision proposed so far. |
195
+ | Last override | The last `cache_warming_decision` handler that returns an action wins. |
196
+
197
+ Notification handlers should not perform slow work on a streaming event. Transform handlers should preserve fields they do not intentionally own, and gate handlers should fail closed when their safety decision cannot be made.
198
+
199
+ `before_agent_start` exposes both the current prompt and its structured `systemPromptOptions`. `event.messageType` says what started the run: `"user"` for a prompt or `pi.sendUserMessage()`, with `prompt` and `images`; `"custom"` for `pi.sendMessage(..., { triggerTurn: true })`, with the starting `message` and no `prompt`. A returned `message` is added to either kind of run; check `messageType` to skip runs started by custom messages. Prefer changing prompt sections, selected tools, or guidelines so Pi can append a transcript delta. Returning `systemPrompt`, or setting `forceSystemPrompt`, replaces the whole prompt for that run while the transcript continues recording the structured sections. Providers receive the forced text as their leading system prompt.
104
200
 
105
201
  `message_end` can replace a finalized message while preserving its role. `tool_call` can mutate input or block execution. `tool_result` handlers compose, with each handler seeing prior changes.
106
202
 
@@ -159,9 +255,9 @@ See [`hello.ts`](../examples/extensions/hello.ts), [`todo.ts`](../examples/exten
159
255
  - `deferred`: like `codemode`, but codemode tools do not list it; `tool_search` can find and activate it.
160
256
  - `hidden`: registered but unreachable. Re-register a tool with `exposure: "hidden"` to withdraw it, since tools cannot be unregistered.
161
257
 
162
- `namespace: { name, description, instructions }` groups related tools, as MCP servers do. Codemode tools list a namespace under one heading with its `description`. `instructions` holds longer usage guidance; it is not listed, and codemode scripts read it with `describeNamespace(name)`.
258
+ `namespace: { name, aliases, description, instructions }` groups related tools, as MCP servers do. Codemode tools list a namespace under one heading with its `description`. `instructions` holds longer usage guidance; it is not listed, and codemode scripts read it with `describeNamespace(name)`. `aliases` are other names scripts may look the namespace up by, for example the server name `docs` for `mcp__docs`; a name and the same name as a script identifier (`my-docs`, `my_docs`) match alike.
163
259
 
164
- Registering a `direct` or `model-only` tool activates it; the other exposures are not activated on registration. The active set (`pi.getActiveTools()`, `pi.setActiveTools()`) is the set of tools declared to the model. `pi.getAllTools()` reports each tool's `exposure`, `namespace`, and `annotations`.
260
+ Registering a `direct` or `model-only` tool activates it; the other exposures are not activated on registration. The active set (`pi.getActiveTools()`, `pi.setActiveTools()`) is the set of tools declared to the model. `pi.getAllTools()` reports each tool's `exposure`, `namespace`, `annotations`, and `reachesTools`.
165
261
 
166
262
  `annotations` are hints about what a tool does, with the meaning of MCP tool annotations: `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. MCP tools carry the hints their server declares. Missing hints take the MCP defaults: a tool is not read-only, and may be destructive and reach an open world. The hints are not verified, but a permission extension can use them to decide which calls to confirm. This confirms the calls Codex asks approval for:
167
263
 
@@ -177,7 +273,9 @@ pi.on("tool_call", async (event, ctx) => {
177
273
  });
178
274
  ```
179
275
 
180
- A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure and namespace. It returns replacement `descriptions` for declared tools (including its own) and `hiddenDeclarations`: active tools whose declarations requests leave out while they stay active and callable. `codemode` uses only this hook, `exposure`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name.
276
+ A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure, namespace, and prompt guidelines. It returns replacement `descriptions` for declared tools (including its own) and `hiddenDeclarations`: active tools whose declarations requests leave out while they stay active and callable. The default system prompt leaves hidden tools out of its tool list and rules, and names no tool in the skills hint when the file reader is hidden, so the orchestrating tool should present their guidelines itself. `codemode` uses only this hook, `exposure`, `reachesTools`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name.
277
+
278
+ A tool that reaches other tools declares which ones a call may reach with `reachesTools(input)`: `"all"`, or the words of the input that may name them, where a word refers to every tool or namespace whose name it contains. `codemode` returns `"all"` for scripts that search or list tools and the words of the script otherwise; `tool_search` always returns `"all"`. Extensions that register tools late use it from their `tool_call` handler to finish registering the tools a call needs before it runs, as the MCP extension does for servers that are still connecting. The MCP extension also activates `codemode` or `tool_search` for MCP tools only when the tool of that name declares `reachesTools`.
181
279
 
182
280
  ### Activate tools dynamically
183
281
 
@@ -268,7 +366,18 @@ Use `ctx.shutdown()` to request an orderly process shutdown.
268
366
 
269
367
  ## Examples and reference
270
368
 
271
- The checked [extension examples](../examples/extensions/) cover tools, lifecycle events, commands, flags, shortcuts, state, rendering, providers, OAuth, remote execution, and terminal components.
272
- Start with the smallest example matching your integration point.
273
-
274
- Use [Custom Providers](custom-provider.md) for model-service integrations, [Terminal UI](tui.md) for custom components, and [Pi Packages](packages.md) to install or distribute extensions with other resources.
369
+ The checked [extension examples](../examples/extensions/) are the implementation reference. Start with the smallest example matching the behavior you need:
370
+
371
+ | Goal | Start with | Why |
372
+ |---|---|---|
373
+ | Confirm or block tool calls | [`permission-gate.ts`](../examples/extensions/permission-gate.ts) | Combines `tool_call`, mode-aware UI, and fail-closed behavior. |
374
+ | Modify prompt sections for a run | [`prompt-customizer.ts`](../examples/extensions/prompt-customizer.ts) | Uses structured prompt options instead of replacing the complete prompt. |
375
+ | Keep branch-aware tool state | [`todo.ts`](../examples/extensions/todo.ts) | Reconstructs state from persisted entries and renders it in the TUI. |
376
+ | Register tools after startup | [`dynamic-tools.ts`](../examples/extensions/dynamic-tools.ts) | Shares registration logic between lifecycle and command handlers. |
377
+ | Run cancellable model work from a command | [`qna.ts`](../examples/extensions/qna.ts) | Combines nested model calls, cancellation, and custom UI. |
378
+ | Replace the main editor | [`modal-editor.ts`](../examples/extensions/modal-editor.ts) | Extends `CustomEditor` while preserving application controls. |
379
+ | Run finalization at shutdown | [`auto-commit-on-exit.ts`](../examples/extensions/auto-commit-on-exit.ts) | Uses the session shutdown hook for finalization work. |
380
+ | Route built-in tools to another machine | [`ssh.ts`](../examples/extensions/ssh.ts) | Reuses tool operation interfaces instead of recreating tool contracts. |
381
+ | Add OAuth and provider-specific streaming | [`custom-provider-gitlab-duo/`](../examples/extensions/custom-provider-gitlab-duo/) | Shows a complete provider integration while delegating supported protocols. |
382
+
383
+ Use [Custom Providers](custom-provider.md) for model-service integrations, [Terminal UI](tui.md) for custom components, and [Pi Packages](packages.md) to install or distribute extensions with other resources. Use the exported declarations for exact signatures, payloads, and result schemas; this guide focuses on lifecycle choices, composition, and durable implementation patterns.
package/docs/json.md CHANGED
@@ -42,7 +42,7 @@ A basic run produces records like these:
42
42
  {"type":"message_end","message":{"role":"assistant","...":"..."}}
43
43
  {"type":"turn_end","message":{"role":"assistant","...":"..."},"toolResults":[]}
44
44
  {"type":"agent_end","messages":[{"...":"..."}],"willRetry":false}
45
- {"type":"agent_settled"}
45
+ {"type":"agent_settled","aborted":false}
46
46
  ```
47
47
 
48
48
  `agent_end` closes one low-level agent run. Automatic retry, overflow recovery, compaction retry, steering, or follow-up work can still continue. `agent_settled` means Pi has no remaining automatic work for that session-level run.
@@ -53,7 +53,7 @@ A basic run produces records like these:
53
53
  |---|---|---|
54
54
  | `agent_start` | None | A low-level agent run started. |
55
55
  | `agent_end` | `messages`, `willRetry` | That low-level run ended. `messages` contains messages generated by the run. |
56
- | `agent_settled` | None | Pi will not continue automatically through retries, compaction recovery, or queued messages. |
56
+ | `agent_settled` | `aborted` | Pi will not continue automatically through retries, compaction recovery, or queued messages. `aborted` is `true` when the run ended because it was aborted. |
57
57
  | `turn_start` | None | One assistant turn started. |
58
58
  | `turn_end` | `message`, `toolResults` | One assistant response and its resulting tool calls finished. |
59
59
 
@@ -104,7 +104,7 @@ The top-level `usage` is the latest cumulative provider-reported usage for the a
104
104
  |---|---|---|
105
105
  | `tool_execution_start` | `toolCallId`, `toolName`, `args` | Tool execution started. |
106
106
  | `tool_execution_update` | `toolCallId`, `toolName`, `args`, `partialResult` | The tool reported a partial result. |
107
- | `tool_execution_end` | `toolCallId`, `toolName`, `result`, `isError` | Tool execution finished. |
107
+ | `tool_execution_end` | `toolCallId`, `toolName`, `result`, `isError`, `durationMs` | Tool execution finished. `durationMs` is how long the tool's `execute()` took, measured with a monotonic clock; absent when the tool did not run. |
108
108
 
109
109
  Use `toolCallId` to correlate the lifecycle. `partialResult` is the latest partial result supplied by the tool. Whether it replaces or extends an earlier update depends on that tool's result contract.
110
110
 
@@ -12,6 +12,7 @@ Map each action identifier to one key or a list of keys:
12
12
 
13
13
  ```json
14
14
  {
15
+ "$schema": "https://pi.dev/schemas/keybindings.schema.json",
15
16
  "app.session.new": "ctrl+shift+n",
16
17
  "app.session.tree": ["ctrl+shift+t", "alt+shift+t"]
17
18
  }
@@ -57,8 +58,8 @@ Examples: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `super+k`, `ctrl+sup
57
58
  | `tui.editor.cursorRight` | `right`, `ctrl+f` | Move cursor right |
58
59
  | `tui.editor.cursorWordLeft` | `alt+left`, `ctrl+left`, `alt+b` | Move cursor word left |
59
60
  | `tui.editor.cursorWordRight` | `alt+right`, `ctrl+right`, `alt+f` | Move cursor word right |
60
- | `tui.editor.cursorLineStart` | `home`, `ctrl+home`, `ctrl+a` | Move to line start |
61
- | `tui.editor.cursorLineEnd` | `end`, `ctrl+end`, `ctrl+e` | Move to line end |
61
+ | `tui.editor.cursorLineStart` | `home`, `ctrl+a` | Move to line start |
62
+ | `tui.editor.cursorLineEnd` | `end`, `ctrl+e` | Move to line end |
62
63
  | `tui.editor.jumpForward` | `ctrl+]` | Jump forward to character |
63
64
  | `tui.editor.jumpBackward` | `ctrl+alt+]` | Jump backward to character |
64
65
  | `tui.editor.pageUp` | `pageUp`, `ctrl+pageUp` | Scroll up by page |
@@ -113,8 +114,8 @@ In fullscreen mode, these actions control the transcript and take precedence ove
113
114
  | `tui.altScreen.searchNext` | `enter`, `ctrl+g` | Select the next search match while searching |
114
115
  | `tui.altScreen.searchPrevious` | `shift+enter`, `ctrl+shift+g` | Select the previous search match while searching |
115
116
  | `tui.altScreen.searchClose` | `escape` | Close transcript search |
116
- | `tui.altScreen.top` | `home` | Scroll to the beginning of the transcript |
117
- | `tui.altScreen.bottom` | `end` | Scroll to the transcript end and follow new output |
117
+ | `tui.altScreen.top` | `ctrl+home` | Scroll to the beginning of the transcript |
118
+ | `tui.altScreen.bottom` | `ctrl+end` | Scroll to the transcript end and follow new output |
118
119
 
119
120
  ### Application
120
121
 
package/docs/llama-cpp.md CHANGED
@@ -41,7 +41,7 @@ A single-file model can sit directly in the model directory. Put multimodal and
41
41
  └── large-model-Q4_K_M-00003-of-00003.gguf
42
42
  ```
43
43
 
44
- Restart the router after manually adding files. For per-model context sizes and other options, use [llama.cpp model presets](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md#model-presets).
44
+ Restart the router after manually adding files. For per-model context sizes and other options, use [llama.cpp model presets](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md#model-presets). For settings tuned to a specific NVIDIA GPU and model, see [Run Pi with NVIDIA RTX and llama.cpp](nvidia-rtx-llama-cpp.md).
45
45
 
46
46
  ## Configure Pi
47
47
 
@@ -88,7 +88,14 @@ If the router disconnects, `/llama` shows **Retry** and **Close**. Retry reconne
88
88
 
89
89
  ## Classification
90
90
 
91
- Every model listed for chat is also listed as a classifier model with the same ID and the `llama-cpp-classify` API. Classifier models answer typed `choice`, `bool`, and `score` questions about JSON state, like TypeSafe's Jev models. The model reaches them from [`codemode`](cli.md#enable-codemode) scripts, and extensions through `ctx.modelRegistry.classify()`; see [Classifier models](models.md#use-classifier-models).
91
+ Classifier models answer typed `choice`, `bool`, and `score` questions about JSON state, like TypeSafe's Jev models. The model reaches them from [`codemode`](cli.md#enable-codemode) scripts, and extensions through `ctx.modelRegistry.classify()`; see [Classifier models](models.md#use-classifier-models). Pi lists llama.cpp models as classifiers in two ways:
92
+
93
+ - **Decision models** such as [Julia-1, Laya, Kev, lev, and OpenJev](https://huggingface.co/collections/ggml-org/decision-models-6abf80cca3c83f127060a769) answer natively through llama.cpp's `/v1/systemone` endpoint. They appear only as classifiers, with the `typesafe-system-one` API, and not in `/model`.
94
+ - **Chat models** are also listed as classifiers with the same ID and the `llama-cpp-classify` API, which reads answers from next-token probabilities as described below.
95
+
96
+ llama.cpp 0.6.0 and later report decision models in the router's model list: their `architecture.output_modalities` contains `decisions`. The router reads this from the GGUF metadata without loading the model, so Pi recognizes unloaded and sleeping decision models too. Older llama.cpp builds do not report it, and Pi lists their decision models as chat models.
97
+
98
+ ### Chat models as classifiers
92
99
 
93
100
  The model does not generate an answer. Each question becomes one chat prompt: the state, every question of the request, the state again, and then the question with its answers under single-token labels. Labels are letters for a choice (up to 62 options), `Yes`/`No` for a bool, and digits for a score (up to 10 levels). The second copy of the state is read with the questions in view, which improved accuracy on JevBench with small models. Pi reads the probabilities of the labels as the next token and normalizes them. A choice returns every option's probability and a confidence of `(n * peak - 1) / (n - 1)`; a score returns the expected level.
94
101
 
package/docs/mcp.md CHANGED
@@ -225,6 +225,8 @@ Pi activates `codemode` when a server with `codemode` exposure connects. It acti
225
225
 
226
226
  Tools with `codemode` or `deferred` exposure can be reached through either indirect mechanism: codemode scripts can call them, and `tool_search` can load them. Codemode calls do not depend on the active tool set, so they remain available after `/tree`, resume, and fork. Tools loaded by `tool_search` are recorded in the transcript and remain declared on that branch.
227
227
 
228
+ `--tools` does not remove MCP tools unless one of its entries starts with `mcp__`; `pi --tools read,codemode,'mcp__radius__*'` keeps only the tools of `radius`. `--exclude-tools` accepts the same patterns, and `--no-mcp` disables MCP for one run (see [Tools](cli.md#mcp-tools)).
229
+
228
230
  To keep `codemode` active without MCP servers, add `"defaultTools": ["+codemode"]` to [settings](settings.md#tools). To prevent automatic codemode activation, set `"autoEnableCodemode": false` beside `mcpServers`. A project value overrides the user-level value. Pi warns once when neither `codemode` nor `tool_search` is active and non-direct tools cannot be called.
229
231
 
230
232
  Text results over 20 KB reach the model with their middle removed around a `…N chars truncated…` marker. The full text is saved to a temporary file named in the result. Codemode scripts receive the complete result and can reduce it before returning output to the model.
@@ -261,9 +263,9 @@ Changes to enabled state or exposure apply only to the current session. A file-c
261
263
 
262
264
  ### Replace the built-in MCP support
263
265
 
264
- An installed extension that registers `/mcp`, such as `pi-mcp-adapter`, replaces the built-in MCP support for sessions. Pi then does not read `mcp.json` or connect its servers in a session, and `/mcp` belongs to the extension. Remove the extension to restore the built-in behavior. To disable built-in MCP support without a replacement, disable `mcp` under Built-in in `pi config`, or set `"extensions": ["-builtin:mcp"]` in [settings](settings.md#resources).
266
+ An installed extension that registers `/mcp`, such as `pi-mcp-adapter`, replaces the built-in MCP support for sessions. Pi then does not read `mcp.json` or connect its servers in a session, and `/mcp` belongs to the extension. Remove the extension to restore the built-in behavior. To disable built-in MCP support without a replacement, disable `mcp` under Built-in in `pi config`, or set `"extensions": ["-builtin:mcp"]` in [settings](settings.md#resources). `--no-mcp` disables it for one run.
265
267
 
266
- An extension that registers `codemode` or `tool_search` similarly replaces the built-in tool with that name. Shell-level `pi mcp` commands always use the built-in implementation.
268
+ An extension that registers `codemode` or `tool_search` similarly replaces the built-in tool with that name. Pi activates the replacement for MCP servers and waits for the servers its calls need only when it declares [`reachesTools`](extensions.md#tool-exposure). Shell-level `pi mcp` commands always use the built-in implementation.
267
269
 
268
270
  ### Use MCP from the SDK
269
271
 
@@ -100,12 +100,11 @@ interface SystemMessage {
100
100
  sections?: Record<string, string | null>;
101
101
  toolsAdded?: Tool[];
102
102
  toolsRemoved?: ToolReference[];
103
- replace?: boolean;
104
103
  timestamp: number;
105
104
  }
106
105
  ```
107
106
 
108
- The leading system message declares the initial prompt and tools. Later system messages can append instructions, replace or remove named prompt sections, and add or remove tools. Replaying them in order yields the current state. A message with `replace: true` discards the earlier state and establishes a complete new baseline.
107
+ The leading system message declares the initial prompt and tools. Later system messages can append instructions, replace or remove named prompt sections, and add or remove tools. Replaying them in order yields the current state.
109
108
 
110
109
  ### UserMessage
111
110
 
@@ -129,6 +128,7 @@ interface AssistantMessage {
129
128
  responseModel?: string;
130
129
  responseId?: string;
131
130
  providerThinkingLevel?: string;
131
+ thinkingLevel?: ModelThinkingLevel;
132
132
  diagnostics?: AssistantMessageDiagnostic[];
133
133
  usage: Usage;
134
134
  stopReason: "pending" | "stop" | "length" | "toolUse" | "error" | "aborted" | "deferred";
@@ -140,7 +140,7 @@ interface AssistantMessage {
140
140
  }
141
141
  ```
142
142
 
143
- `responseModel` records a concrete provider response model when it differs from the requested model. `responseId`, `providerThinkingLevel`, `diagnostics`, and `rawStopReason` preserve provider or runtime details.
143
+ `responseModel` records a concrete provider response model when it differs from the requested model. `responseId`, `providerThinkingLevel`, `thinkingLevel`, `diagnostics`, and `rawStopReason` preserve provider or runtime details.
144
144
 
145
145
  `"pending"` is used for a partial assistant message while it streams. The completed message in `message_end` has a terminal stop reason, and Pi does not persist `"pending"` assistant messages in session JSONL.
146
146
 
@@ -168,12 +168,30 @@ interface ToolResultMessage<TDetails = any> {
168
168
  content: (TextContent | ImageContent)[];
169
169
  details?: TDetails;
170
170
  usage?: Usage;
171
+ nestedCalls?: NestedToolCalls;
171
172
  isError: boolean;
172
173
  timestamp: number;
173
174
  }
174
175
  ```
175
176
 
176
- `details` is tool-specific. Optional `usage` reports nested model work performed by the tool and contributes to full-session statistics, but it is not part of the main model-call usage.
177
+ `details` is tool-specific. Optional `usage` reports nested model work performed by the tool and contributes to full-session statistics, but it is not part of the main model-call usage. `nestedCalls` records bounded metadata about calls this tool made to other tools:
178
+
179
+ ```typescript
180
+ interface NestedToolCalls {
181
+ calls: NestedToolCallRecord[];
182
+ complete: boolean;
183
+ }
184
+
185
+ interface NestedToolCallRecord {
186
+ id: string;
187
+ name: string;
188
+ arguments?: JsonObject;
189
+ argumentsBytes?: number;
190
+ status: "ok" | "error" | "unfinished";
191
+ durationMs?: number;
192
+ error?: string;
193
+ }
194
+ ```
177
195
 
178
196
  ## Coding-agent messages
179
197