@xingular/harness 1.2.0 → 1.3.1-unstable.1

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 (186) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +26 -17
  3. package/builtin/skills/plugin-authoring/SKILL.md +34 -0
  4. package/dist/cli.js +7362 -6239
  5. package/dist/sdk/agents/builtin.js +3 -3
  6. package/dist/sdk/agents/planResearch.d.ts +23 -1
  7. package/dist/sdk/agents/planResearch.js +71 -8
  8. package/dist/sdk/agents/profiles.d.ts +16 -4
  9. package/dist/sdk/agents/profiles.js +75 -12
  10. package/dist/sdk/annotations/bridge.d.ts +2 -0
  11. package/dist/sdk/annotations/bridge.js +4 -0
  12. package/dist/sdk/annotations/capture.d.ts +44 -2
  13. package/dist/sdk/annotations/capture.js +104 -3
  14. package/dist/sdk/annotations/delivery.d.ts +24 -17
  15. package/dist/sdk/annotations/delivery.js +82 -49
  16. package/dist/sdk/annotations/inspect.js +7 -6
  17. package/dist/sdk/annotations/pageScript.js +4 -4
  18. package/dist/sdk/annotations/part.d.ts +0 -2
  19. package/dist/sdk/annotations/part.js +4 -33
  20. package/dist/sdk/annotations/queue.d.ts +2 -2
  21. package/dist/sdk/annotations/queue.js +12 -7
  22. package/dist/sdk/annotations/serverMethods.js +11 -7
  23. package/dist/sdk/annotations/store.d.ts +13 -21
  24. package/dist/sdk/annotations/store.js +47 -37
  25. package/dist/sdk/capabilities/runSnapshot.js +10 -4
  26. package/dist/sdk/commands/list.js +2 -0
  27. package/dist/sdk/commands/registry.d.ts +11 -3
  28. package/dist/sdk/commands/registry.js +3 -2
  29. package/dist/sdk/core/links.d.ts +51 -0
  30. package/dist/sdk/core/links.js +155 -0
  31. package/dist/sdk/core/shell.d.ts +20 -0
  32. package/dist/sdk/core/shell.js +73 -0
  33. package/dist/sdk/core/workspaceBashTool.d.ts +17 -1
  34. package/dist/sdk/core/workspaceBashTool.js +16 -2
  35. package/dist/sdk/core/workspaceSandbox.d.ts +10 -0
  36. package/dist/sdk/core/workspaceSandbox.js +52 -12
  37. package/dist/sdk/core/workspaceWriteOperations.d.ts +11 -0
  38. package/dist/sdk/core/workspaceWriteOperations.js +26 -8
  39. package/dist/sdk/doctor/checks.js +71 -12
  40. package/dist/sdk/harness.d.ts +5 -0
  41. package/dist/sdk/harness.js +11 -6
  42. package/dist/sdk/hub/hub.js +5 -3
  43. package/dist/sdk/index.d.ts +3 -2
  44. package/dist/sdk/index.js +2 -1
  45. package/dist/sdk/plugins/builtin.js +14 -9
  46. package/dist/sdk/plugins/builtinRegistry.d.ts +3 -1
  47. package/dist/sdk/plugins/builtinRegistry.js +9 -3
  48. package/dist/sdk/plugins/discover.d.ts +7 -1
  49. package/dist/sdk/plugins/discover.js +41 -2
  50. package/dist/sdk/plugins/identity.js +1 -1
  51. package/dist/sdk/plugins/install.d.ts +6 -2
  52. package/dist/sdk/plugins/install.js +29 -5
  53. package/dist/sdk/plugins/inventory.js +3 -3
  54. package/dist/sdk/plugins/manifest.d.ts +2 -0
  55. package/dist/sdk/plugins/manifest.js +21 -4
  56. package/dist/sdk/plugins/plugins.d.ts +2 -0
  57. package/dist/sdk/plugins/plugins.js +1 -0
  58. package/dist/sdk/plugins/proposals.d.ts +17 -0
  59. package/dist/sdk/plugins/proposals.js +147 -0
  60. package/dist/sdk/plugins/types.d.ts +1 -1
  61. package/dist/sdk/plugins/types.js +1 -1
  62. package/dist/sdk/prompt/compile.d.ts +13 -4
  63. package/dist/sdk/prompt/compile.js +46 -20
  64. package/dist/sdk/prompt/standard.d.ts +1 -1
  65. package/dist/sdk/prompt/standard.js +1 -1
  66. package/dist/sdk/server/methods.js +14 -2
  67. package/dist/sdk/sessions/compactionCheckpoint.d.ts +3 -3
  68. package/dist/sdk/sessions/compactionCheckpoint.js +12 -12
  69. package/dist/sdk/sessions/continuation.d.ts +4 -2
  70. package/dist/sdk/sessions/continuation.js +7 -6
  71. package/dist/sdk/sessions/contracts.d.ts +22 -4
  72. package/dist/sdk/sessions/contracts.js +12 -7
  73. package/dist/sdk/sessions/planMode.js +2 -2
  74. package/dist/sdk/sessions/runStore.js +3 -1
  75. package/dist/sdk/sessions/runner.js +2 -2
  76. package/dist/sdk/sessions/sessionState.d.ts +13 -0
  77. package/dist/sdk/sessions/sessionState.js +31 -0
  78. package/dist/sdk/sessions/sessions.d.ts +7 -0
  79. package/dist/sdk/sessions/sessions.js +73 -15
  80. package/dist/sdk/sessions/subagentService.d.ts +15 -0
  81. package/dist/sdk/sessions/subagentService.js +195 -55
  82. package/dist/sdk/sessions/subagents.d.ts +10 -1
  83. package/dist/sdk/sessions/subagents.js +4 -0
  84. package/dist/sdk/sessions/workSlice.d.ts +8 -5
  85. package/dist/sdk/sessions/workSlice.js +77 -45
  86. package/dist/sdk/terminal/controller.d.ts +20 -0
  87. package/dist/sdk/terminal/controller.js +43 -18
  88. package/dist/sdk/terminal/opentui/ansi.d.ts +25 -0
  89. package/dist/sdk/terminal/opentui/ansi.js +192 -0
  90. package/dist/sdk/terminal/opentui/model.d.ts +163 -0
  91. package/dist/sdk/terminal/opentui/model.js +482 -0
  92. package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +3 -3
  93. package/dist/sdk/terminal/opentui/subagentsWidget.js +7 -5
  94. package/dist/sdk/terminal/opentui/theme.d.ts +2 -0
  95. package/dist/sdk/terminal/opentui/theme.js +2 -0
  96. package/dist/sdk/terminal/piExtension.js +98 -15
  97. package/dist/sdk/terminal/subagentMonitor.d.ts +17 -4
  98. package/dist/sdk/terminal/subagentMonitor.js +29 -8
  99. package/dist/sdk/terminal/terminals.d.ts +16 -0
  100. package/dist/sdk/terminal/terminals.js +130 -26
  101. package/dist/sdk/terminal/trajectory.d.ts +6 -1
  102. package/dist/sdk/terminal/trajectory.js +8 -0
  103. package/dist/sdk/terminal/tui/annotateCommand.d.ts +5 -1
  104. package/dist/sdk/terminal/tui/annotateCommand.js +21 -7
  105. package/dist/sdk/terminal/tui/annotateSend.d.ts +30 -0
  106. package/dist/sdk/terminal/tui/annotateSend.js +40 -0
  107. package/dist/sdk/terminal/tui/chrome.d.ts +4 -0
  108. package/dist/sdk/terminal/tui/chrome.js +13 -0
  109. package/dist/sdk/terminal/tui/commandPages.d.ts +1 -1
  110. package/dist/sdk/terminal/tui/commandPages.js +16 -11
  111. package/dist/sdk/terminal/tui/commands.d.ts +4 -0
  112. package/dist/sdk/terminal/tui/commands.js +12 -2
  113. package/dist/sdk/terminal/tui/dialogs.js +1 -0
  114. package/dist/sdk/terminal/tui/pageOverlay.d.ts +3 -0
  115. package/dist/sdk/terminal/tui/pageOverlay.js +25 -7
  116. package/dist/sdk/terminal/tui/pages/agentsPage.js +5 -2
  117. package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +70 -16
  118. package/dist/sdk/terminal/tui/pages/annotatePage.js +349 -115
  119. package/dist/sdk/terminal/tui/pages/browserPage.js +5 -4
  120. package/dist/sdk/terminal/tui/pages/hubPage.d.ts +14 -1
  121. package/dist/sdk/terminal/tui/pages/hubPage.js +192 -28
  122. package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +14 -1
  123. package/dist/sdk/terminal/tui/pages/pluginsPage.js +295 -25
  124. package/dist/sdk/terminal/tui/pages/subagentsPage.js +8 -5
  125. package/dist/sdk/terminal/tui/pages/usagePage.d.ts +4 -2
  126. package/dist/sdk/terminal/tui/pages/usagePage.js +39 -12
  127. package/dist/sdk/terminal/tui/pluginCommands.d.ts +1 -1
  128. package/dist/sdk/terminal/tui/pluginCommands.js +13 -3
  129. package/dist/sdk/tools/defaultSkills.d.ts +4 -0
  130. package/dist/sdk/tools/defaultSkills.js +20 -0
  131. package/dist/sdk/tools/definitions.d.ts +3 -1
  132. package/dist/sdk/tools/definitions.js +17 -1
  133. package/dist/sdk/tools/dispatch.js +12 -0
  134. package/dist/sdk/tools/harnessTools.js +1 -1
  135. package/dist/sdk/tools/sessionName.d.ts +9 -0
  136. package/dist/sdk/tools/sessionName.js +58 -11
  137. package/dist/sdk/tools/spawnSubagent.js +9 -7
  138. package/dist/sdk/tools/todo.d.ts +15 -0
  139. package/dist/sdk/tools/todo.js +64 -0
  140. package/dist/sdk/world/world.d.ts +0 -2
  141. package/dist/terminal-opentui.js +3331 -1584
  142. package/dist/terminal-pi.js +421 -133
  143. package/docs/README.md +3 -0
  144. package/docs/architecture/00-overview.md +1 -1
  145. package/docs/architecture/01-extension-points.md +1 -1
  146. package/docs/architecture/02-sessions-and-runs.md +73 -36
  147. package/docs/architecture/03-variants.md +1 -3
  148. package/docs/architecture/04-prompt.md +24 -13
  149. package/docs/architecture/06-plugins-and-capabilities.md +46 -10
  150. package/docs/architecture/07-tools-and-gate.md +49 -8
  151. package/docs/architecture/09-accounts-and-connectors.md +8 -2
  152. package/docs/architecture/10-server-protocol.md +68 -16
  153. package/docs/architecture/11-terminal.md +94 -45
  154. package/docs/architecture/12-storage.md +1 -0
  155. package/docs/architecture/13-world-tools.md +8 -6
  156. package/docs/architecture/14-surfaces-and-parity.md +4 -1
  157. package/docs/architecture/README.md +20 -18
  158. package/docs/architecture/proposals/PROP-0006-run-checklist.md +2 -0
  159. package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +6 -2
  160. package/docs/architecture/proposals/PROP-0041-bounded-work-slices.md +32 -21
  161. package/docs/architecture/proposals/PROP-0044-work-slice-defaults.md +93 -0
  162. package/docs/architecture/proposals/PROP-0045-rename-checklist-to-todo.md +132 -0
  163. package/docs/architecture/proposals/PROP-0046-progress-aware-stall-guards.md +82 -0
  164. package/docs/architecture/proposals/PROP-0047-plugins-hub-separation.md +122 -0
  165. package/docs/architecture/proposals/PROP-0048-simple-annotate.md +85 -0
  166. package/docs/architecture/proposals/README.md +11 -6
  167. package/docs/decisions/ADR-0039-progress-aware-stall-guards.md +39 -0
  168. package/docs/decisions/ADR-0040-architecture-change-workflow.md +31 -0
  169. package/docs/decisions/ADR-0041-plugins-hub-separation.md +41 -0
  170. package/docs/decisions/ADR-0042-windows-shell-without-sandbox.md +40 -0
  171. package/docs/decisions/ADR-0043-parents-continue-stopped-subagents.md +49 -0
  172. package/docs/decisions/ADR-0044-annotations-pending-or-sent.md +24 -0
  173. package/docs/decisions/ADR-0045-default-plugin-authoring-skill.md +9 -0
  174. package/docs/decisions/ADR-0046-hash-bound-plugin-proposals.md +20 -0
  175. package/docs/decisions/ADR-0047-links-open-with-the-os-handler.md +47 -0
  176. package/docs/doctor.md +14 -1
  177. package/docs/parity.md +16 -13
  178. package/docs/releases/1.3.0.md +21 -0
  179. package/docs/releases/1.3.1-unstable.1.md +8 -0
  180. package/docs/releases/README.md +9 -6
  181. package/docs/terminal-tui.md +127 -65
  182. package/package.json +1 -1
  183. package/dist/sdk/terminal/tui/pages/notesPage.d.ts +0 -75
  184. package/dist/sdk/terminal/tui/pages/notesPage.js +0 -201
  185. package/dist/sdk/tools/checklist.d.ts +0 -9
  186. package/dist/sdk/tools/checklist.js +0 -55
@@ -1,6 +1,6 @@
1
1
  # Harness terminal frontend
2
2
 
3
- Status: current terminal behavior, checked against `@xingular/harness` 1.2.0. Last updated: 2026-11-19.
3
+ Status: current terminal behavior, checked against `@xingular/harness` 1.3.0. Last updated: 2026-11-19.
4
4
  Decision: [ADR-0001](decisions/ADR-0001-owned-terminal.md),
5
5
  [ADR-0006](decisions/ADR-0006-opentui-only-terminal.md),
6
6
  [ADR-0009](decisions/ADR-0009-xharness-command.md),
@@ -93,9 +93,9 @@ The frontend is verified with a real isolated PTY and a loopback provider. Run
93
93
  `python3 scripts/verify-terminal.py --welcome-only --sizes 40x20,80x24,120x40` to check the empty
94
94
  welcome, product label, accent colors and `/help` transition. Run
95
95
  `python3 scripts/verify-terminal.py --composer-only` for recognized slash-command colors in the composer
96
- and a user bubble, unknown-command and argument colors, explicit-newline and soft-wrap checks of both
97
- prompt gutters, and normal/plan/Bash mode exclusivity. Run it with `--sizes 40x20,80x24,120x40` for
98
- compact through wide layouts. Use `--dialog-paste-only` to verify bracketed paste into a centered
96
+ and a user bubble, enabled-skill colors in the composer, unknown-token and argument colors,
97
+ explicit-newline and soft-wrap checks of both prompt gutters, and normal/plan/Bash mode exclusivity.
98
+ Run it with `--sizes 40x20,80x24,120x40` for compact through wide layouts. Use `--dialog-paste-only` to verify bracketed paste into a centered
99
99
  input dialog, `--file-completion-only` for filesystem completion and insertion,
100
100
  `--history-only` for active-session history across `/new` and `/session`, `--resume-only` for the full-screen searchable, unnumbered two-line saved-session picker, `--help-only` for `/help` replacing the welcome screen with grouped two-column output that wraps without per-command gaps, `--questions-only` for multi-select marker glyphs, recommended choices,
101
101
  selected-row and Continue/Submit colors, `--jump-latest-only` for scroll-away badge visibility and click-to-latest (screen-level click assertions use optional `pyte`), and choice-backed “Something else” answers, or `--thinking-only` for `/thinking` visibility toggles. The full suite covers a complete turn, `read`, `bash` approval, `ask_user_question` approval and picker, plugin overlay/widget/status
@@ -124,16 +124,24 @@ and a searchable `/subagents` page. The rows poll a private monitor route once p
124
124
  during a turn and make a final read at settlement. They sit one blank row below the status line, with
125
125
  no heading, and each reads: the status mark, the agent profile name in its colour (`subagent` for a
126
126
  copy of the parent), the title the parent gave the child (PROP-0030), and `n of m tasks` from the
127
- child's own checklist. At most three show, active first, then `+N more · /subagents`. The
127
+ child's own todo list. At most three show, active first, then `+N more · /subagents`. The
128
128
  `/subagents` page uses a colored profile and normal-color title on the first line, then muted account,
129
129
  effective model, effort and cumulative usage metadata on the second. One blank row separates each
130
130
  two-line entry. The page also shows status, elapsed time and the latest tool name. Runtime metadata
131
131
  stays in the private terminal projection.
132
132
  Usage contains safe token/cache/cost/context totals, not per-request payloads or reasoning summaries.
133
133
  The projection is read-only, flat across nested descendants, and scoped to the latest parent run in
134
- this terminal session; finished rows remain visible until the next parent run starts. It is cleared
135
- on session replacement or terminal disposal. It never includes tool arguments (beyond the title),
136
- output or checklist item text.
134
+ this terminal session; finished rows remain visible until the next parent run starts. The page also
135
+ pins a `main` row for the parent run itself, with the same details; the rows under the status line
136
+ leave it out. Reopening a conversation (a new launch, `/resume`, or a switch) rebuilds the rows from
137
+ the branch's latest parent run and its saved child runs, with their final status, until the next run
138
+ starts. A switch to a conversation without one clears them, as does disposal. It never includes tool
139
+ arguments (beyond the title), output or todo item text.
140
+
141
+ An interrupted or failed child keeps its saved conversation. The next reply is told which children
142
+ stopped before it answers, and the parent continues one with `spawn_subagent { continue: <runId> }`,
143
+ which reopens that child's own history with its profile and tools (ADR-0043). Esc therefore does
144
+ not lose a child's progress.
137
145
 
138
146
  `/agents` opens the Agents page (PROP-0029): built-in, global, project and variant profiles with
139
147
  their tools, plugins, variant, model and instructions. Enter on a row edits a person's own profile
@@ -215,7 +223,7 @@ rule; then the work that followed. The summary is shown by default. Clicking eit
215
223
  shows it, and Ctrl+O shows every summary again. On a narrow screen the rule drops the click hint, then
216
224
  the token count. Earlier compactions in the same session keep their own rules and summary in place.
217
225
  Notices stay after the message they followed. The model sees the summary and recent messages plus a
218
- bounded, hidden checkpoint of the active request, checklist and plan instructions when available;
226
+ bounded, hidden checkpoint of the active request, todo list and plan instructions when available;
219
227
  the checkpoint is regenerated for each post-compaction request until the run settles. The visible
220
228
  transcript remains a presentation of the full history, not the model context.
221
229
  Transcript entry ids follow positions in the displayed message list, so when a rebuilt list moves a
@@ -260,23 +268,36 @@ restores the terminal's alternate-screen state when the application exits.
260
268
 
261
269
  Assistant replies, plan-card bodies and Markdown image-result text share one style map. Heading
262
270
  levels use the Harness palette; links are blue and underlined, and only their labels appear in the
263
- transcript. Hovering a supported link changes the terminal pointer to a hand and shows a `Click to
264
- open link` hint in the status bar. A primary click opens it with the system browser; only valid
265
- HTTP and HTTPS destinations are accepted, and the launcher receives the URL as an argument without
266
- a shell. OSC 8 metadata is emitted where available, but link activation does not depend on terminal
267
- OSC 8 support. Inline and fenced code use a subtle panel fill with syntax colors. Emphasis, quotes,
268
- list markers, tables, dividers and task-list markers use the existing titanium palette. Images
269
- display their alt text and are not fetched or rendered as image files.
270
-
271
- ## Checklist block
272
-
273
- `checklist_write` is registered in terminal sessions (PAR-001). Each call renders in the
274
- transcript as a checklist block: a status mark, `Checklist · n of m done`, then one line per item
275
- with `✓` (done, success colour), `●` (in progress, warning yellow, bold) or `○` (pending, dim).
276
- Each row reserves a one-cell status column and one-cell gap; item text and wrapped lines share the
277
- same starting column. A call whose arguments are not a checklist, or that failed, stays an ordinary
278
- tool row. The prompt's Progress group asks the model to keep the list current and write a final
279
- update before replying; unfinished work remains unfinished rather than being marked complete.
271
+ transcript. Hovering a link changes the terminal pointer to a hand and recolours the link's own
272
+ text (`theme.linkHover`); the status line says nothing about it. The gesture that opens a link is
273
+ the one the platform uses: **Ctrl+click** where the terminal reports the modifier, and on macOS the
274
+ **primary click**, because ⌘ is not part of the terminal mouse protocol (a macOS terminal opens an
275
+ OSC 8 link itself on ⌘+click). Either way the link opens with the operating system's handler for
276
+ its kind: a web address in the browser, `mailto:` in the mail client, a file in its default
277
+ application, a folder in Finder. A click that opens a link leaves the composer focused. `core/links.ts` decides what
278
+ may open (ADR-0047): a relative path resolves against the session's folder, and an application
279
+ bundle, an installer, a launchable script or an executable file is refused with a notice instead of
280
+ being run. The launcher receives the address as one argument without a shell. OSC 8 metadata is
281
+ emitted where available, but link activation does not depend on terminal OSC 8 support; a terminal
282
+ that opens OSC 8 links itself acts on its own modifier, so the Harness keeps the primary click.
283
+ Inline and fenced code use a subtle panel fill with syntax colors. Emphasis, quotes, list markers,
284
+ tables, dividers and task-list markers use the existing titanium palette. Images display their alt
285
+ text and are not fetched or rendered as image files.
286
+
287
+ ## Todo block
288
+
289
+ `todo_write` is registered in terminal sessions (PAR-001). Each call renders in the transcript as
290
+ a todo block: a status mark, `Todo · n of m done`, then one line per item with `✓` (done, success
291
+ colour), `●` (in progress, warning yellow, bold) or `○` (pending, dim). Each row reserves a one-cell
292
+ status column and one-cell gap; item text and wrapped lines share the same starting column. A call
293
+ whose arguments are not a todo list, or that failed, stays an ordinary tool row. The prompt's
294
+ Progress group asks the model to keep the list current and write a final update before replying;
295
+ unfinished work remains unfinished rather than being marked complete.
296
+
297
+ `/todo` (alias `/checklist`) appends a snapshot of the current list: the same header and rows, with
298
+ no status mark on the header, because it is not a tool call. It reads the latest non-side-question
299
+ run of this terminal's native session and makes no model request; an empty list is a one-line
300
+ notice.
280
301
 
281
302
  ## Blocks and pages
282
303
 
@@ -301,20 +322,36 @@ frontends draw that field from the same muted-blue selection palette (`selection
301
322
  explicit high-contrast text on it instead of the accent colour, so a choice reads the same in every
302
323
  list. Rules use the theme's `warning` and `dim` colors, so a custom theme restyles them.
303
324
 
304
- The Harness resource browser (PROP-0013) unifies Overview, Foundation, Variant, Skills, Plugins,
305
- Connections, Tools and Library. `/harness` opens Overview; `/skills`, `/plugins`, `/connections`
306
- (`/mcp`) and `/tools` open sections. `/hub` selects Discover and `/components` selects Library.
307
- The existing Accounts, Permissions, Settings, Trajectory and Log pages retain their own tabs.
325
+ The broad Harness resource browser (PROP-0013) unifies Overview, Foundation, Variant, Skills,
326
+ Plugins, Connections, Tools and Library. `/harness` opens Overview; `/skills`, `/connections`
327
+ (`/mcp`), `/tools` and `/components` open sections. `/plugins` is a separate
328
+ full-screen local manager with Plugins, Trust and Packages tabs; `/hub` is a separate
329
+ full-screen Community catalog with Catalog and Installed tabs. Both keep a searchable
330
+ inventory beside selected-item details on wide terminals, stacking details below the list
331
+ on compact terminals. The existing Accounts, Permissions, Settings, Trajectory and Log
332
+ pages retain their own tabs.
308
333
 
309
334
  `/settings` includes a Browser tab with the device-local **Private address access** setting. It is
310
335
  off by default. Enabling it asks for confirmation and allows agent browsing and annotation capture
311
336
  to reach localhost and private networks.
312
337
 
313
- Resource sections share Installed/Discover and source filters. Installed includes locally present
314
- resources; current-session availability comes only from the runtime loader and active tool registry.
315
- Discover uses the existing Hub with All/Community/Official filtering and an explicit remote refresh.
316
- No matching community entries is an empty result, never replaced with built-in resources.
317
- The view preserves search across source switches and restores section selection on return.
338
+ Within the broad `/harness` browser, resource sections share Installed/Discover and source filters.
339
+ Installed includes locally present resources; current-session availability comes only from the runtime
340
+ loader and active tool registry. Discover uses the unfiltered Hub service with All/Community/Official
341
+ filters and an explicit refresh. No matching Community entries is an empty result, never replaced with
342
+ built-in resources. The view preserves search across source switches and restores section selection.
343
+
344
+ The dedicated `/hub` Catalog shows Community entries across supported types only. Its Installed tab
345
+ shows receipts from every channel with the channel labeled. On each page entry, cached/bundled entries
346
+ appear first, then the remote catalog refreshes; the status keeps the origin, last-refresh time and any
347
+ stale/error feedback visible. Manual refresh remains available. Catalog installs are pinned to the
348
+ selected version, and Hub uninstalls remove only receipt-owned resources. Registry/document entries
349
+ offer a bounded payload Preview; Pi packages have no JSON payload Preview.
350
+
351
+ The `/plugins` manager keeps local switching, code trust and settings separate from public discovery.
352
+ Its Packages tab adds local package installation, update and removal. Hub-owned packages remain pinned
353
+ to exact catalog versions; removal goes through Hub receipt cleanup, and updating requires an explicit
354
+ Hub version replacement.
318
355
 
319
356
  The browser fills the terminal (PROP-0014), with Overview, Foundation, Variant, Skills, Plugins,
320
357
  Connections, Tools and Library as persistent top tabs. The section dropdown and sidebar are removed.
@@ -329,8 +366,8 @@ Left/Right changes the focused tab or control; Enter on the tab strip returns to
329
366
  Ctrl+F focuses search. Escape returns from content to actions, returns to the list, clears search,
330
367
  then closes. Mouse clicks select tabs directly, activate resource actions and focus filters;
331
368
  wheel scrolling targets the list or inspector under the pointer. Other command pages keep their
332
- centered presentation; `/settings` and `/accounts` use full-screen page layouts. Closing the browser preserves
333
- the composer draft and attachments.
369
+ centered presentation; `/settings`, `/accounts`, `/plugins` and `/hub` use full-screen page layouts.
370
+ Closing the browser preserves the composer draft and attachments.
334
371
 
335
372
  Foundation explains fixed runtime ownership; it is read-only. Variant shows c1–c8 with the
336
373
  prepared version and current head separately. It previews pinned files from the preparation
@@ -359,24 +396,34 @@ skill import rejects symbolic links and limits bytes, files and traversal. See
359
396
  [PROP-0013](architecture/proposals/PROP-0013-resource-browser.md) and
360
397
  [ADR-0015](decisions/ADR-0015-resource-browser.md).
361
398
 
362
- ## Annotate pane (PROP-0018)
399
+ ## Annotate page (PROP-0018, PROP-0048)
363
400
 
364
- `/annotate:` opens the full-screen capture pane (equivalent to `/annotate:annotate`) beside the window being inspected. Add a page address with `/annotate:annotate [url]`. The target window is one of:
401
+ `/annotate:` opens the full-screen annotate page beside the window being inspected. It has two tabs:
402
+ **Annotate** and **All annotations** (`/annotate:notes` opens on the second). Add a page address with
403
+ `/annotate: [url]`; without one, the page returns to the window that is still open, with its URL and
404
+ the inspector on, and asks for an address only when no window is open. The target window is one of:
365
405
  - the visible capture browser
366
- - an app's own windows attached over its DevTools port (`--app`)
406
+ - an app's own windows over its DevTools port (`--app [port]`, or `a` in the page)
367
407
  - app windows that loaded the page bridge script (`--bridge`)
368
408
 
369
- The header shows the target and window and whether the inspector is on. The list has three groups: Queue, Sent and fixed, and Saved. While the inspector is on, the page polls for the next popover comment every 400 ms, shows the result as a notice, and refreshes the list.
409
+ The header shows the target and window and whether the inspector is on. Click a component in the
410
+ window and choose **Annotate** or **Annotate & send** (Cmd/Ctrl+Enter). Every annotation is shared
411
+ with this session; the list groups them as Unsent and Sent. While the inspector is on, the page polls
412
+ for the next popover comment every 400 ms, shows the result as a notice, and refreshes the list.
370
413
 
371
414
  | Key | Action |
372
415
  |---|---|
373
416
  | `i` | inspector on/off |
374
- | `s` | send the queue |
375
- | `x` | unqueue the selected note |
417
+ | `s` | send every unsent annotation (page-origin ones are confirmed once first) |
418
+ | `o` | type a page address |
419
+ | `a` | apps with a DevTools port: pick one, type a port, start an app with one, or use the bridge |
376
420
  | `t` | choose a window |
377
- | `Enter` | the note menu |
421
+ | `Enter` | the annotation menu |
422
+ | `Tab` | switch tab |
378
423
 
379
- The page has no search row, so these letters are free for actions. `/annotate:notes` opens the searchable Web annotations list.
424
+ The Annotate tab has no search row, so these letters are free for actions. `/annotate:submit <request>`
425
+ sends the unsent annotations with your request after them; `/annotate:clear` resolves the unsent ones
426
+ and stops sharing them.
380
427
 
381
428
  ## Plan mode and thinking keys
382
429
 
@@ -384,16 +431,21 @@ Use `/plan <brief>` for one read-only planning request. A plan waits outside Pi'
384
431
  queue until the active response settles, then runs by itself. Later messages remain behind it, even
385
432
  when Pi's follow-up mode is `all`. During that run the session narrows to read-only inspection
386
433
  (`read`, `grep`, `find`, `ls`), web search/page reading, questions (`ask_user`, `ask_user_question`)
387
- and recall (history and Wiki reads, `memory_facts`). It may also delegate bounded research to a
388
- named profile whose effective tool and plugin allowlists qualify for research; the child inherits
389
- plan mode and receives only the intersection of the parent's frozen capabilities, profile and
390
- plan-research set. Children cannot write, run commands, ask the user, use connectors or spawn more
391
- children. Normal mode omits both question tools from the active set; the plan request enables
392
- registered questions and research delegation temporarily and restores the normal set when it settles,
393
- including on failure or interruption. Providers may mark individual calls as safe for research; every
394
- other call is blocked before approval. Edits, writes, shell commands, memory writes and connected-app
395
- tools remain unavailable to the parent and child. The plan instructions arrive as a request-scoped
396
- prompt section, which the next ordinary request withdraws.
434
+ and recall (history and Wiki reads, `memory_facts`). The parent may also run Bash in the
435
+ OS-enforced read-only sandbox: no network and no writes except a discarded per-command temporary
436
+ directory (PROP-0043). It may delegate bounded research to a named profile whose effective tool and
437
+ plugin allowlists qualify for research; the child inherits plan mode and receives only the
438
+ intersection of the parent's frozen capabilities, profile and plan-research set. Children cannot
439
+ write, run commands, ask the user, use connectors or spawn more children; they remain bounded by the
440
+ configured work-slice policy without gaining `checkpoint_write`, and the Harness records an
441
+ evidence-derived checkpoint if the child stops at a boundary. Normal mode omits both question tools
442
+ from the active set; the plan request enables registered questions and research delegation
443
+ temporarily and restores the normal set when it settles, including on failure or interruption.
444
+ Providers may mark individual calls as safe for research; every other call is blocked before
445
+ approval. Edits, writes, memory writes and connected-app tools remain unavailable to parent and
446
+ child; generic browser controls are also blocked. Sandboxed read-only Bash is the parent's only
447
+ plan-mode shell exception; children cannot run commands. The plan instructions arrive as a
448
+ request-scoped prompt section, which the next ordinary request withdraws.
397
449
 
398
450
  Each mode keeps its own model and effort for the account. Every selection the person makes (the model
399
451
  picker, `/effort`, Shift+Up/Down, Ctrl+P) is saved against the mode it was made in, so Shift+Tab switches to that
@@ -699,12 +751,16 @@ normal-color title and a muted second line with relative activity time, `<projec
699
751
  file size separated by ` · `. One blank line separates each session. Enter resumes the selected
700
752
  session; account and folder scope checks
701
753
  remain enforced. Supplying an explicit path continues directly without opening the picker.
702
- The separate `$` palette completes enabled skill invocations; `/skills` always browses skills. `/help`
703
- renders the command list as one transcript entry, grouped under Core, Run, Model, Session, Inspect,
704
- Harness, Plugins and Prompts; command names and descriptions wrap in aligned columns, with no
705
- blank rows between choices. `/plugins` opens the browser with protected code trust and plugin settings actions. Settings use
706
- `plugins.configure`; on/off settings and bounded whole-number settings keep their existing ranges.
707
- Saved settings are read when the plugin next uses them and do not require a restart. The standalone
754
+ The `$` palette completes enabled skill invocations using the same two-column palette as slash
755
+ commands; `/skills` always browses skills. Choosing a skill inserts `$name ` without submitting.
756
+ A recognized leading `$name` uses the blue accent in the composer, while its arguments, unavailable
757
+ names and ordinary currency text keep the normal color. `/help` renders the command list as one
758
+ transcript entry, grouped under Core, Run, Model, Session, Inspect, Harness, Plugins and Prompts;
759
+ command names and descriptions wrap in aligned columns, with no blank rows between choices. `/plugins`
760
+ opens the focused local manager; its Packages tab installs, updates and removes packages with protected
761
+ Harness operations. Settings use `plugins.configure`; on/off settings and bounded whole-number settings
762
+ keep their existing ranges. Saved settings are read when the plugin next uses them and do not require a
763
+ restart. The standalone
708
764
  terminal no longer has separate `/login`, `/trust`, `/bug`, `/changelog`, `/share`, `/scoped-mode` or
709
765
  `/import` commands. `/btw <question>` (or `/side <question>`) starts a separate Harness run with the
710
766
  main run's effective instructions, visible conversation and redacted public tool inputs/results.
@@ -738,8 +794,8 @@ of feature-plugin command metadata: `/doctor`, `/worktree`, `/trajectory`, `/sub
738
794
  `/history` (also `/tree`) continues to navigate session branches. `/mcp` and `/connections` open the
739
795
  Connections browser. Feature descriptors and their services remain registered; only these terminal
740
796
  routes move out of the plugin command registry. Other plugin-owned commands continue to use
741
- `/<owner-prefix>:<command>`. The annotations plugin uses `/annotate:annotate` and `/annotate:notes`;
742
- `/annotate:` opens its default capture pane. Pi package extensions use their normalized package name
797
+ `/<owner-prefix>:<command>`. The annotations plugin uses `/annotate:notes`, `/annotate:submit` and
798
+ `/annotate:clear`; `/annotate:` opens its annotate page (a root-only command with no named route). Pi package extensions use their normalized package name
743
799
  as owner prefix unless `harness.commandPrefix` overrides it; loose extensions use their normalized
744
800
  extension name. A plugin prefix collision or a duplicate command within one owner is rejected with
745
801
  owner paths in the diagnostic. The former owner-qualified and `/plugin:<command>` routes of promoted
@@ -894,12 +950,18 @@ inherit that retained ledger.
894
950
 
895
951
  ## Agent-managed session names
896
952
 
897
- Agents can use `session_name({ name: string })` to name meaningful work early and revise
898
- its name when the objective changes. Names submitted by agents are short imperative
953
+ Agents can use `session_name({ name: string })` to name work before their first other tool call
954
+ when a request needs tools or changes state, and revise the name only when the objective changes.
955
+ Names submitted by agents are short imperative
899
956
  sentences with at most six words. The agent may revise a manual name; `/name` remains
900
957
  available and manual names have no new word limit. Pi stores the name in native history,
901
958
  so resuming the same conversation restores it without another model request.
902
959
 
960
+ A substantive first turn also gets a provisional name through the terminal policy when the
961
+ session is unnamed; a refused or failed name leaves it unnamed. Each turn appends a hidden
962
+ `xingular-session-state` message with the current name and this run's empty todo, with the
963
+ previous run's list as context; the message is request-only and does not enter the stable pack.
964
+
903
965
  The working line shows `Working (2m, 50 tok/s) – Add web browser integrations`; the name
904
966
  also stays on the completion line. Unnamed sessions have no separator, and narrow terminals
905
967
  truncate trailing text. Renaming refreshes the line through Pi's existing metadata event.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xingular/harness",
3
- "version": "1.2.0",
3
+ "version": "1.3.1-unstable.1",
4
4
  "type": "module",
5
5
  "description": "Xingular Harness — a Pi agent harness built on recursive harnessing (Harness²): versioned eight-component variants, refinement from evidence, multi-account sessions, a compiled prompt, project notes, tools, and MCP",
6
6
  "repository": {
@@ -1,75 +0,0 @@
1
- /**
2
- * The Web annotations page (`/annotate:notes`, `/annotate:annotate [url]`): comments a person keeps about web pages. A new annotation
3
- * is captured in a visible browser window: the whole page, text the person selects, or an element
4
- * they click. An annotation is private until it is shared with this session, whose agent can then read it
5
- * with `page_notes_read` (PROP-0017).
6
- */
7
- import type { Theme } from '@earendil-works/pi-coding-agent';
8
- import type { MenuCall, MenuUi } from "../../pluginMenu.js";
9
- import type { TabInfo } from "../chrome.js";
10
- import { type ListRow } from "../list.js";
11
- import type { TerminalMouseInput } from "../mouse.js";
12
- import type { TerminalPage } from "../page.js";
13
- import type { Note } from "../../../annotations/store.js";
14
- export interface NotesPageOptions {
15
- theme(): Theme;
16
- call: MenuCall;
17
- ui: MenuUi & {
18
- input(title: string, placeholder?: string): Promise<string | undefined>;
19
- };
20
- changed(): void;
21
- /** Start a new annotation as soon as the page opens (`/annotate:annotate [url]`), on this address when given. */
22
- start?: {
23
- url?: string;
24
- };
25
- }
26
- type Row = {
27
- kind: 'new';
28
- } | {
29
- kind: 'note';
30
- note: Note;
31
- };
32
- export declare const NOTE_KINDS: {
33
- readonly page: "The whole page";
34
- readonly text: "Text I select in the window";
35
- readonly element: "An element I click in the window";
36
- };
37
- export declare const NOTE_ACTIONS: {
38
- readonly reveal: "Show it in the browser window";
39
- readonly edit: "Edit the annotation";
40
- readonly resolve: "Mark resolved";
41
- readonly reopen: "Reopen";
42
- readonly share: "Let this session's agent read it";
43
- readonly unshare: "Stop sharing it with this session";
44
- readonly delete: "Delete";
45
- };
46
- export declare class NotesPage implements TerminalPage {
47
- private readonly options;
48
- presentation: "centered";
49
- private notes;
50
- private sessionId;
51
- private error;
52
- private busy;
53
- private disposed;
54
- private readonly list;
55
- constructor(options: NotesPageOptions);
56
- get focused(): boolean;
57
- set focused(value: boolean);
58
- activate(): void;
59
- dispose(): void;
60
- tabs(): readonly TabInfo[];
61
- activeTab(): string;
62
- hint(): string;
63
- reload(): Promise<void>;
64
- private shared;
65
- rows(): ListRow<Row>[];
66
- private attempt;
67
- activateRow(row: Row): Promise<void>;
68
- /** Opens the capture window, takes what the person points at, and saves their annotation. */
69
- newNote(startUrl?: string): Promise<void>;
70
- private noteMenu;
71
- handleMouse(event: TerminalMouseInput): import("@earendil-works/pi-tui").TuiMouseEventResult | undefined;
72
- handleInput(data: string): boolean;
73
- render(width: number, rows: number): string[];
74
- }
75
- export {};
@@ -1,201 +0,0 @@
1
- import { truncateToWidth } from '@earendil-works/pi-tui';
2
- import { FilterList } from "../list.js";
3
- const TABS = [{ id: 'notes', title: 'Web annotations' }];
4
- export const NOTE_KINDS = { page: 'The whole page', text: 'Text I select in the window', element: 'An element I click in the window' };
5
- export const NOTE_ACTIONS = {
6
- reveal: 'Show it in the browser window',
7
- edit: 'Edit the annotation',
8
- resolve: 'Mark resolved',
9
- reopen: 'Reopen',
10
- share: 'Let this session\'s agent read it',
11
- unshare: 'Stop sharing it with this session',
12
- delete: 'Delete'
13
- };
14
- const clip = (text, size) => (text.length > size ? `${text.slice(0, size - 1)}…` : text);
15
- function describeTarget(target) {
16
- if (target.kind === 'text')
17
- return `“${clip(target.exact.replace(/\s+/g, ' '), 60)}”`;
18
- if (target.kind === 'element')
19
- return `<${target.tag}> ${clip(target.name || target.text, 50)}`;
20
- return 'whole page';
21
- }
22
- export class NotesPage {
23
- options;
24
- presentation = 'centered';
25
- notes;
26
- sessionId = null;
27
- error;
28
- busy = false;
29
- disposed = false;
30
- list;
31
- constructor(options) {
32
- this.options = options;
33
- this.list = new FilterList({
34
- theme: options.theme, search: 'always', noun: ['annotation', 'annotations'], empty: 'No web annotations yet',
35
- onActivate: row => { if (row.data)
36
- void this.activateRow(row.data); }
37
- });
38
- }
39
- get focused() { return this.list.focused; }
40
- set focused(value) { this.list.focused = value; }
41
- activate() {
42
- void this.reload().then(() => {
43
- if (this.options.start)
44
- void this.newNote(this.options.start.url);
45
- });
46
- }
47
- dispose() { this.disposed = true; }
48
- tabs() { return TABS; }
49
- activeTab() { return 'notes'; }
50
- hint() { return 'Enter choose · type to search · Esc close'; }
51
- async reload() {
52
- try {
53
- const result = await this.options.call('terminal.annotations.list');
54
- this.notes = result.notes;
55
- this.sessionId = result.sessionId;
56
- this.error = undefined;
57
- }
58
- catch (error) {
59
- this.error = error instanceof Error ? error.message : String(error);
60
- }
61
- if (this.disposed)
62
- return;
63
- this.list.setRows(this.rows());
64
- this.options.changed();
65
- }
66
- shared(note) {
67
- return note.grants.some(grant => grant.principal.sessionId && grant.principal.sessionId === this.sessionId);
68
- }
69
- rows() {
70
- const rows = [{ id: 'new', label: 'New web annotation…', group: 'Actions', detail: 'opens a browser window', tone: 'accent', data: { kind: 'new' } }];
71
- for (const note of this.notes ?? []) {
72
- rows.push({
73
- id: `note:${note.id}`, label: clip(note.body.replace(/\s+/g, ' '), 60), group: 'Annotations',
74
- detail: `${describeTarget(note.target)} · ${note.status}${this.shared(note) ? ' · shared here' : ''}`,
75
- tone: note.status === 'resolved' ? 'dim' : this.shared(note) ? 'success' : 'muted',
76
- searchText: `${note.body} ${note.source.url} ${note.source.title} ${note.tags.join(' ')}`,
77
- data: { kind: 'note', note }
78
- });
79
- }
80
- return rows;
81
- }
82
- async attempt(work) {
83
- if (this.busy)
84
- return;
85
- this.busy = true;
86
- try {
87
- const message = await work();
88
- if (message)
89
- this.options.ui.notify(message, 'info');
90
- }
91
- catch (error) {
92
- this.options.ui.notify(error instanceof Error ? error.message : String(error), 'error');
93
- }
94
- finally {
95
- this.busy = false;
96
- }
97
- await this.reload();
98
- }
99
- async activateRow(row) {
100
- if (row.kind === 'new')
101
- return this.newNote();
102
- return this.noteMenu(row.note);
103
- }
104
- /** Opens the capture window, takes what the person points at, and saves their annotation. */
105
- newNote(startUrl) {
106
- const { call, ui } = this.options;
107
- return this.attempt(async () => {
108
- const url = startUrl ?? await ui.input('Page address', 'https://');
109
- if (!url)
110
- return;
111
- await call('terminal.annotations.open', { url });
112
- const kind = await ui.select('What does the annotation point at?', Object.values(NOTE_KINDS));
113
- if (!kind)
114
- return;
115
- let envelope;
116
- if (kind === NOTE_KINDS.page) {
117
- const identity = await call('terminal.annotations.identity');
118
- envelope = { ...identity, target: { kind: 'page' } };
119
- }
120
- else {
121
- const element = kind === NOTE_KINDS.element;
122
- const { captureId } = await call('terminal.annotations.begin', { kind: element ? 'element' : 'text' });
123
- const ready = await ui.confirm(element ? 'Click the element in the browser window' : 'Select the text in the browser window', 'When it is selected, choose Yes to capture it. Choose No to cancel.');
124
- if (!ready) {
125
- await call('terminal.annotations.cancel', { captureId });
126
- return;
127
- }
128
- envelope = await call('terminal.annotations.capture', { captureId });
129
- if (!envelope)
130
- return 'Nothing was selected in the browser window, so no annotation was made.';
131
- }
132
- const body = await ui.input('Comment', 'What should change or be remembered?');
133
- if (!body?.trim())
134
- return 'An annotation needs a comment; nothing was saved.';
135
- const tags = (await ui.input('Tags (optional, comma separated)', '') ?? '').split(',').map(tag => tag.trim()).filter(Boolean);
136
- const share = await ui.confirm('Share it with this session?', 'This session\'s agent can then read the annotation with page_notes_read. You can stop sharing it later.');
137
- await call('terminal.annotations.create', {
138
- source: { url: envelope.url, title: envelope.title, capturedAt: new Date().toISOString(), surface: 'terminal', representationHint: envelope.documentId },
139
- target: envelope.target, body, tags, share
140
- });
141
- return share ? 'Annotation saved and shared with this session.' : 'Annotation saved. It stays private until you share it.';
142
- });
143
- }
144
- async noteMenu(note) {
145
- const { call, ui } = this.options;
146
- const choices = [
147
- NOTE_ACTIONS.reveal,
148
- NOTE_ACTIONS.edit,
149
- note.status === 'open' ? NOTE_ACTIONS.resolve : NOTE_ACTIONS.reopen,
150
- this.shared(note) ? NOTE_ACTIONS.unshare : NOTE_ACTIONS.share,
151
- NOTE_ACTIONS.delete
152
- ];
153
- const choice = await ui.select(`${clip(note.source.title || note.source.url, 60)} · ${describeTarget(note.target)}`, choices);
154
- const id = note.id;
155
- if (choice === NOTE_ACTIONS.reveal)
156
- return this.attempt(async () => {
157
- const result = await call('terminal.annotations.reveal', { id });
158
- return result.state === 'matched' ? 'Marked in the browser window.' : `The target is ${result.state}${result.reason ? `: ${result.reason}` : ''}.`;
159
- });
160
- if (choice === NOTE_ACTIONS.edit)
161
- return this.attempt(async () => {
162
- const body = await ui.input('Note', note.body);
163
- if (!body?.trim() || body === note.body)
164
- return;
165
- await call('terminal.annotations.update', { id, expectedVersion: note.version, patch: { body } });
166
- return 'Annotation saved.';
167
- });
168
- if (choice === NOTE_ACTIONS.resolve || choice === NOTE_ACTIONS.reopen)
169
- return this.attempt(async () => {
170
- await call('terminal.annotations.update', { id, expectedVersion: note.version, patch: { status: choice === NOTE_ACTIONS.resolve ? 'resolved' : 'open' } });
171
- });
172
- if (choice === NOTE_ACTIONS.share || choice === NOTE_ACTIONS.unshare)
173
- return this.attempt(async () => {
174
- await call(choice === NOTE_ACTIONS.share ? 'terminal.annotations.share' : 'terminal.annotations.unshare', { id });
175
- return choice === NOTE_ACTIONS.share ? 'This session\'s agent can read the annotation.' : 'The annotation is no longer shared with this session.';
176
- });
177
- if (choice === NOTE_ACTIONS.delete)
178
- return this.attempt(async () => {
179
- if (!await ui.confirm('Delete this annotation?', 'It cannot be restored.'))
180
- return;
181
- await call('terminal.annotations.delete', { id, expectedVersion: note.version });
182
- return 'Annotation deleted.';
183
- });
184
- }
185
- handleMouse(event) {
186
- return this.list.handleMouse(event);
187
- }
188
- handleInput(data) {
189
- return this.list.handleInput(data);
190
- }
191
- render(width, rows) {
192
- const theme = this.options.theme();
193
- if (width < 1 || rows < 1)
194
- return [];
195
- if (this.error)
196
- return [theme.fg('error', `Could not read web annotations: ${this.error}`)];
197
- if (!this.notes)
198
- return [theme.fg('dim', 'Reading web annotations…')];
199
- return this.list.render(width, rows).slice(0, rows).map(line => truncateToWidth(line, width, ''));
200
- }
201
- }
@@ -1,9 +0,0 @@
1
- import { type ChecklistItem, type RunEvent } from "../sessions/contracts.js";
2
- import type { ActiveRun } from "../sessions/registry.js";
3
- import type { ToolSpec } from "./types.js";
4
- export declare const CHECKLIST_WRITE = "checklist_write";
5
- /** The checklist as the model reads it back. */
6
- export declare function describeChecklist(items: readonly ChecklistItem[]): string;
7
- /** The latest checklist a run wrote, or an empty list. */
8
- export declare function checklistOf(events: readonly RunEvent[]): ChecklistItem[];
9
- export declare function checklistTool(run: Pick<ActiveRun, 'emit'>): ToolSpec;