@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
package/docs/README.md CHANGED
@@ -17,6 +17,9 @@ change the binding architecture or describe implemented capabilities.
17
17
 
18
18
  | ID | Record | Type | Status | Related |
19
19
  |---|---|---|---|---|
20
+ | ADR-0047 | [A link a person clicks opens with the system's handler](decisions/ADR-0047-links-open-with-the-os-handler.md) | Decision | Implemented in the Harness, the terminal and the App; acceptance not certified | [10 Server protocol](architecture/10-server-protocol.md), [parity register](parity.md) |
21
+ | ADR-0046 | [Hash-bound plugin proposal review](decisions/ADR-0046-hash-bound-plugin-proposals.md) | Decision | Harness API and model proposal tool implemented; review surfaces pending | [06 Plugins](architecture/06-plugins-and-capabilities.md), [parity register](parity.md) |
22
+ | ADR-0045 | [Release-owned plugin-authoring skill](decisions/ADR-0045-default-plugin-authoring-skill.md) | Decision | Implemented as an on-demand resource | [06 Plugins](architecture/06-plugins-and-capabilities.md) |
20
23
  | PROP-0026 | [Connect structural sketch lines in the terminal display](architecture/proposals/PROP-0026-connected-sketch-display.md) | Proposal | Implemented; working tree and isolated fixtures verified | [ADR-0027](decisions/ADR-0027-connected-sketch-display.md), [11 Terminal](architecture/11-terminal.md), [parity register](parity.md) |
21
24
  | ADR-0027 | [Project structural ASCII sketch lines into connected terminal borders](decisions/ADR-0027-connected-sketch-display.md) | Decision | Accepted; working tree and isolated fixtures verified | [PROP-0026](architecture/proposals/PROP-0026-connected-sketch-display.md), [14 Surfaces and parity](architecture/14-surfaces-and-parity.md) |
22
25
  | FW-0001 | [Agent Modes, starting with Router Agent Mode](future-work.md#fw-0001-agent-modes-starting-with-router-agent-mode) | Future work | Candidate; requires architecture proposal before implementation | [Architecture glossary](architecture/README.md#glossary), [Feature guide](features/README.md#planned-agent-modes) |
@@ -134,7 +134,7 @@ Modules with no member of their own: `variant/` (functions over the variant stor
134
134
  | Letter | The harness owns |
135
135
  |---|---|
136
136
  | **E** Execution | Isolated account folders, the Pi session, the terminal launch spec |
137
- | **T** Tools | History, trajectory, Wiki notes, skill loading, `ask_user`, the run checklist, the connected-apps broker, the tool gate |
137
+ | **T** Tools | History, trajectory, Wiki notes, skill loading, `ask_user`, the run todo list, the connected-apps broker, the tool gate |
138
138
  | **C** Context | The compiler, stable prefix and updates, the versioned variant (c1–c8), skills, on-demand Wiki notes |
139
139
  | **L** Lifecycle | Run state, interrupt, recovery, approvals and questions, subagents, refinement |
140
140
  | **O** Observability | Validated run events saved per run; usage counters |
@@ -50,7 +50,7 @@ harness forwards to the policy and never reads.
50
50
  ## Invariants
51
51
 
52
52
  - **INV-EXT-1** There are seven extension points. Adding, removing or changing the shape of one is
53
- an architecture change (proposal required).
53
+ an architecture change; update this page and affected contracts in the same change.
54
54
  - **INV-EXT-2** Every extension point is optional. The harness runs with none of them supplied.
55
55
  - **INV-EXT-3** An extension point carries data or decisions the application owns. It never
56
56
  carries an application's name or a branch for one application (`INV-BND-3`).
@@ -13,7 +13,7 @@ records everything the run does as validated events.
13
13
  |---|---|---|
14
14
  | `Sessions` | `sessions/sessions.ts` | Starts a turn: `run(input: SessionRunInput)`, `provide(provider)`. Builds the gate. |
15
15
  | `runTurn`, `piSessionFactory`, `loadResources` | `sessions/runner.ts` | Opens the Pi session, loads resources in a fixed order, streams events, settles the run. |
16
- | compaction checkpoint | `sessions/compactionCheckpoint.ts` | Restores the active request's bounded goal, checklist and plan instructions to model context after compaction. |
16
+ | compaction checkpoint | `sessions/compactionCheckpoint.ts` | Restores the active request's bounded goal, todo list and plan instructions to model context after compaction. |
17
17
  | `RunRegistry`, `ActiveRun` | `sessions/registry.ts` | Live runs: `emit`, `requestApproval`, `requestAnswer(s)`, `onInterrupt`, `onSteer`, `finish`; `listen` for observers. |
18
18
  | run store functions | `sessions/runStore.ts` | `saveRun`, `updateRun`, `persistEvent`, `appendEvent`, `readEvents`, `markInterruptedRuns`, `runFamily`, `listRuns`. |
19
19
  | `RunRecord`, `RunEvent`, schemas | `sessions/contracts.ts` | The one contract every session shares. |
@@ -55,7 +55,7 @@ A session that cannot open ends its run as `failed` and returns `nativeSessionId
55
55
  `message.completed`, `message.steered`, `tool.started`, `tool.completed`, `retry.started`,
56
56
  `retry.ended`, `compaction.started`, `compaction.ended`, `approval.requested`,
57
57
  `approval.resolved`, `question.requested`, `question.answered`, `extension.notice`,
58
- `reaction.added`, `agent.message`, `checklist.updated`, `artifact.added`, `file.changed`, `usage.updated`,
58
+ `reaction.added`, `agent.message`, `todo.updated`, `artifact.added`, `file.changed`, `usage.updated`,
59
59
  `account.status`, `status.changed`, `failed`, `completed`. Each has a payload schema in
60
60
  `runEventPayloadSchemas`.
61
61
  - **Workspace root** (`RunRecord.workspaceRoot`) is the canonical root whose filesystem effects
@@ -76,9 +76,10 @@ A session that cannot open ends its run as `failed` and returns `nativeSessionId
76
76
  across alternatives; generation quality is guidance, not a schema guarantee. Old events remain
77
77
  valid without a data rewrite; readers with the old bounds must update before accepting larger
78
78
  sketches. `question.answered` and `QuestionAnswer` are unchanged.
79
- - **Checklist** (PROP-0006): `checklist.updated` carries the whole list the model last wrote with
80
- `checklist_write`, `{ items: { text, status: pending | in_progress | completed }[] }`, at most 50
81
- items. The latest event replaces every earlier one; `checklistOf(events)` reads it.
79
+ - **Todo list** (PROP-0006, renamed by PROP-0045): `todo.updated` carries the whole list the model
80
+ last wrote with `todo_write`, `{ items: { text, status: pending | in_progress | completed }[] }`, at
81
+ most 50 items. The latest event replaces every earlier one; `todoOf(events)` reads it. The
82
+ pre-rename `checklist.updated` is still read so old runs keep their list, and is never written.
82
83
  - **`RunRecord`** fields are listed in `sessions/contracts.ts`. `mode`, `harness`, `sessionType`,
83
84
  `worktreeId`, `attachmentCount`, `exitCode`, `handoffId` and `trigger` belong to the
84
85
  application: stored and returned, and only `mode` and `trigger` are read.
@@ -97,12 +98,24 @@ A session that cannot open ends its run as `failed` and returns `nativeSessionId
97
98
  `results` for the host (`sessions.spawnSubagent` is a compatibility alias). Child runs have
98
99
  `relation: 'subagent'` and `parentRunId`. Nesting defaults to 1, at most 3 (`subagentDepth`).
99
100
  The child's record carries `title`, the short name the parent gave it (PROP-0030).
101
+ `spawn_subagent { continue: <runId> }` (`subagents.spawn { continue }`) continues a child that
102
+ stopped before finishing as a new child run with `continueFrom`. It reopens the first link's
103
+ history folder and keeps the earlier child's session id, profile, title, account, variant version
104
+ and frozen capability set, which must still lie within the parent's; `agent` and `tools` are
105
+ refused with it, and the newest link of a chain is the one continued (ADR-0043). Children an
106
+ earlier reply left stopped are delivered before the next reply's first request, each with a
107
+ note that it can be continued; a stopped child that was continued is reported through its
108
+ continuation. Every run record carries the launch `model` and `effort`, superseded by the model
109
+ the session opened with.
100
110
  - **Agent profiles** (PROP-0029). `agent` names a profile: built-in (`worker`, `explorer`),
101
111
  `<data root>/agents/<name>.md`, `<project>/.xingular/agents/<name>.md`, or a c5 definition of
102
- the parent's variant, later sources winning (`agents/profiles.ts`). A profile narrows the
112
+ the parent's variant, later sources winning (`agents/profiles.ts`). A switched-on package's
113
+ profiles (page 06) then fill names nobody else took; the run resolves them once at start into
114
+ `packageProfiles`, and its children resolve their profiles from that list. A profile narrows the
103
115
  child's tools and plugins (`agents/narrow.ts`), may choose its variant, model and effort, and
104
116
  frames it with a "Your role" section. The child's record carries `profile`; the spawn result
105
- carries `agent` and any `unavailableTools`. A run that may spawn lists every profile in its
117
+ carries `agent` and any `unavailableTools`. `main` is reserved for the main agent's row: no
118
+ profile file or variant definition may take it. A run that may spawn lists every profile in its
106
119
  `subagents` section.
107
120
  - **Plan mode** (`plan: true`). The stable system prompt is unchanged. An attended plan run may
108
121
  include `ask_user` and `spawn_subagent` / `wait_subagents`, subject to the caller's frozen
@@ -122,15 +135,15 @@ A session that cannot open ends its run as `failed` and returns `nativeSessionId
122
135
  The reply ends with one `<proposed_plan>` block.
123
136
  - **Compaction checkpoint** (PROP-0033, ADR-0032). After compaction, an active run's model context
124
137
  receives a bounded, ephemeral checkpoint with the current user request and the latest validated
125
- checklist or applicable plan instructions, when either is available to restore. It is re-added to
138
+ todo list or applicable plan instructions, when either is available to restore. It is re-added to
126
139
  each request while that run continues;
127
140
  its state is cleared when the run settles or the session branch changes. The message changes no
128
141
  tools, permissions, run events or native history. An interrupted run still uses the durable
129
- checklist and normal continuation path.
142
+ todo list and normal continuation path.
130
143
  - **Continuation** (PROP-0007). `continuationOf(root, runId, sessionDir?)` (`runs.continuation`)
131
144
  says whether a stopped run can be continued: `resumable`, a `reason` when not (`not_found`,
132
145
  `active`, `completed`, `no_history`, `history_missing`), the exact native history to reopen,
133
- the pinned account, variant version and capability set, the checklist, unanswered approvals and
146
+ the pinned account, variant version and capability set, the todo list, unanswered approvals and
134
147
  questions, the last tool and whether it finished, and subagents that did not finish.
135
148
  `SessionRunInput.continueFrom` starts a **new** run from it: it reopens that history, records
136
149
  `resumedFromRunId`, pins the same account, variant version and capability set (anything different
@@ -138,7 +151,8 @@ A session that cannot open ends its run as `failed` and returns `nativeSessionId
138
151
  `continuation_unavailable`). If the earlier snapshot includes `ask_user`, an automatic or
139
152
  refinement-triggered continuation fails with `continuation_mismatch` rather than narrowing it.
140
153
  Empty text defaults to "Continue from where you stopped.", and the new run adds
141
- a dynamic `continuation` section (order 26) saying where the earlier run stopped. The next run
154
+ a dynamic `continuation` section (order 26) saying where the earlier run stopped and that its
155
+ unfinished subagents can be continued with `spawn_subagent { continue }`. The next run
142
156
  without `continueFrom` withdraws it. `Recovery.interruptedRunIds` lists the runs `open` marked
143
157
  interrupted.
144
158
  - **Worktrees** (PROP-0016). When `run.worktreeId` names a worktree in the Harness registry
@@ -203,7 +217,9 @@ is approved but remains in progress; no completed cross-surface catalog is impli
203
217
  and never applied or relayed.
204
218
  - **INV-RUN-4** A restarted harness never restarts model work. On `open`, runs left active are
205
219
  marked `interrupted` unless another live process owns them (`owner.json`). Recovery of a
206
- subagent needs the same account, caller, working folder and history branch. Continuing is always
220
+ subagent needs the same account, caller, working folder and history branch; the history branch
221
+ (owner marker and native history id), not the host's session id, ties a parent to an earlier
222
+ one. A stopped subagent is continued only by its parent, through `spawn_subagent { continue }`. Continuing is always
207
223
  a new run with `continueFrom`; it pins the earlier run's account, variant version and capability
208
224
  set, and the earlier run's record is never reopened. The application asks for it, or the earlier
209
225
  run's work-slice policy does when the reply stopped at its budget (PROP-0041); a run started
@@ -225,11 +241,15 @@ is approved but remains in progress; no completed cross-surface catalog is impli
225
241
  plan may include `ask_user`, `spawn_subagent` and `wait_subagents`, subject to the frozen caller
226
242
  capabilities; newly resolved normal, automatic, probe, improver and reflection runs omit
227
243
  `ask_user`. Plan-mode spawning requires a named profile whose effective explicit tool and plugin
228
- allowlists qualify for read-only research. The child inherits plan mode and is narrowed to the
229
- intersection of the parent's frozen capabilities, the profile and the plan research set; it has no
244
+ allowlists qualify for read-only research. The prompt lists only profiles whose intersection with
245
+ the parent's frozen tools and plugin contributions includes a research evidence tool. An omitted
246
+ `tools` override inherits that safe intersection; an explicit override can only narrow it. The
247
+ child inherits plan mode and is narrowed to the intersection of the parent's frozen capabilities,
248
+ the profile and the plan research set; it has no
230
249
  connectors, user questions, writes, commands or nested subagents. The plan run itself may run
231
- Bash only in the OS-enforced read-only scope (PROP-0043, ADR-0038). The gate refuses unsafe calls
232
- before approvals, and the service rechecks before launch. The terminal activates plan-eligible
250
+ Bash only in the OS-enforced read-only scope (PROP-0043, ADR-0038); research children cannot run
251
+ commands. Generic browser actions and connected-app calls remain unavailable in plan mode. The
252
+ gate refuses unsafe calls before approvals, and the service rechecks before launch. The terminal activates plan-eligible
233
253
  tools only for a plan request, then restores its normal set. Provider-cache effects of the tool-list
234
254
  difference are not assumed (page 04, `INV-PRM-8`).
235
255
  - **INV-RUN-9** Subagent results reach the parent at native turn boundaries, at most once
@@ -245,28 +265,39 @@ is approved but remains in progress; no completed cross-surface catalog is impli
245
265
  surface's visible transcript and selected references. It excludes hidden reasoning and attachment
246
266
  bytes, never reads the main native history, and never changes or steers the parent. Its own run
247
267
  record and events use the normal store contract (PROP-0019).
248
- - **INV-RUN-13** A reply is a bounded work slice (PROP-0041). The foundation defaults are 160 tool
249
- calls, 40 minutes and 320 000 new tokens, whichever comes first. The token count covers new input,
250
- cache writes and output, never the cached prefix: a few large reads pass any small cap before
251
- useful work, so the cap is a safety net, not the bound. At the limit the guard ends the slice and
252
- refuses the next tool call with a request for `checkpoint_write`; a handoff call is never refused,
253
- including when it is the first call past the limit, and another tool ends the reply. A reply that ends this way is `interrupted` with terminal reason `slice_complete`, and one
254
- the stall guard ends is `stalled`. The stall guard identifies a call by its name and every
255
- argument, names a repeated call once and ends the reply on the next repeat, and counts afresh in
256
- each reply, so a checklist a reply rewrites as its steps complete is not a repeat. A new run
257
- continues it only up to the policy's slice cap, only
258
- after a slice that changed a file, passed a check or recorded a finished item, and never after a
259
- checkpoint that is `blocked`. A run may turn the guard off with `workSlice.enabled: false`, and a
260
- launch may set `XINGULAR_WORK_SLICE=off` for every run in that process; with it off a reply has no
261
- boundary — no checkpoint, no continuation, no stall notes — and ends only when the model stops or a
262
- person interrupts.
268
+ - **INV-RUN-13** A reply is a bounded work slice (PROP-0041). The foundation defaults are 320 tool
269
+ calls and 60 minutes, whichever comes first, with no new-token limit (PROP-0044). A token cap is
270
+ available with `workSlice.maxNewTokens`, counted on new input, cache writes and output and never on
271
+ the cached prefix: a few large reads pass any small cap before useful work, so the tool calls and
272
+ the clock are the bound and the token count is only a ceiling for whoever asks for one. At the
273
+ limit the guard refuses the next tool call. When `checkpoint_write` is available, it requests a
274
+ checkpoint and leaves handoff calls callable; otherwise a plan-research child must stop with a
275
+ concise partial research report, and the runner derives a checkpoint from run evidence. A
276
+ non-handoff tool call after the refusal ends the reply. A budget-ended reply is `interrupted` with
277
+ terminal reason `slice_complete`; a reply ended by a stall guard is `stalled`.
278
+ The targeted stall guard identifies calls by name and every argument; three identical calls cause
279
+ one refusal, and a repeated trigger ends the reply. A Bash check that fails twice is a trigger. The
280
+ guard counts only successful native `edit`/`write` results (`isError === false`) with a path toward
281
+ the four-edit per-path churn limit; the next edit is refused, and a repeated trigger ends the
282
+ reply. Failed or blocked edits do not count. A successful edit/write clears failed-check counts,
283
+ identical Bash-check repeat counts and the pending nudge, but leaves edit counts and other
284
+ repeated-call counts intact. A passing check clears edit counts. The general 25-call file/check
285
+ no-progress trigger is removed. `todo_write` is bookkeeping: it is exempt from repeated-call
286
+ detection and does not reset loop state, but counts toward the tool-call budget. Third-compaction
287
+ thrash detection remains. Plan-research children without `checkpoint_write` receive the configured
288
+ bound without a new capability and do not auto-continue. A new run continues it only up to the
289
+ policy's slice cap, only after a slice that changed a file, passed a check or recorded a finished
290
+ item, and never after a checkpoint that is `blocked`. A run may turn the guard off with
291
+ `workSlice.enabled: false`, and a launch may set `XINGULAR_WORK_SLICE=off` for every run in that
292
+ process; with it off a reply has no boundary — no checkpoint, no continuation, no stall notes — and
293
+ ends only when the model stops or a person interrupts.
263
294
  - **INV-RUN-14** A run event stream carries `checkpoint` (the handoff `checkpoint_write` records or
264
295
  the harness derives from the run's own evidence when the model wrote none) and `heartbeat`
265
296
  (steps, elapsed time, last tool, compactions, slice, and the call budget). A heartbeat older than
266
297
  five minutes while the owner process lives marks the run `interrupted` with reason `stalled`; a
267
298
  run that never emitted one keeps the 30-minute silence rule. The watchdog never restarts work.
268
299
  - **INV-RUN-12** A post-compaction checkpoint is a bounded context projection of the active request,
269
- the latest validated checklist and applicable plan instructions. It is compiled by
300
+ the latest validated todo list and applicable plan instructions. It is compiled by
270
301
  `prompt/compile.ts`, supplied only to model requests in the active run, and never persists hidden
271
302
  reasoning or changes permissions, stable prompt text, native history or run-event schemas. Its
272
303
  in-memory state is cleared when the run settles or the active session branch changes
@@ -290,16 +321,22 @@ Decisions: [ADR-0002](../decisions/ADR-0002-native-subagent-lifecycle.md) (super
290
321
  [ADR-0022](../decisions/ADR-0022-plan-only-question-tools.md),
291
322
  [ADR-0023](../decisions/ADR-0023-contextual-side-question-runs.md),
292
323
  [ADR-0026](../decisions/ADR-0026-contextual-question-wireframes.md),
293
- [ADR-0033](../decisions/ADR-0033-plan-mode-research-delegation.md).
324
+ [ADR-0033](../decisions/ADR-0033-plan-mode-research-delegation.md),
325
+ [ADR-0039](../decisions/ADR-0039-progress-aware-stall-guards.md).
294
326
 
295
327
  ## Native session names (PROP-0010)
296
328
 
297
329
  `session_name({ name: string })` adds or revises the current Pi session's name in embedded
298
- and terminal execution. The agent names meaningful work early and revises it when the overall
299
- objective changes, including a manual name. Agent-submitted names are imperative sentences
330
+ and terminal execution. The agent normally calls it once before its first other tool call when a
331
+ request needs tools or changes state, and revises it only when the overall objective changes,
332
+ including a manual name. When a top-level attended session starts unnamed, the harness also
333
+ derives a provisional name from a substantive first request before the first model request;
334
+ greetings, thanks and short conversational replies stay unnamed. The model may revise the
335
+ provisional name. Agent-submitted names are imperative sentences
300
336
  of at most six whitespace-separated words; whitespace is normalized, and empty names,
301
337
  terminal escapes, non-whitespace controls and longer names fail without writing. Identical
302
338
  names are a no-op. Pi's native session-info history is the only storage; continuation and
303
339
  reopen preserve it. Each child names only itself. There is no additional model request or
304
- run-event schema. The normal capability selection and host gate apply; `harnessTools: false`
340
+ run-event schema. The normal capability selection and host gate apply; a refused or failed
341
+ provisional name leaves the session unnamed and never fails the run. `harnessTools: false`
305
342
  omits the tool. Plan mode permits it as session metadata maintenance.
@@ -60,9 +60,7 @@ The surface is `PI_SURFACE` (`variant/surface.ts`), the one surface this harness
60
60
  - **INV-FND-2** A variant changes only by committing a new validated version (`commit` →
61
61
  `validate`, `INV-VAR-2`), and a refinement moves a head only when a person adopts
62
62
  (`INV-REF-6`). There is no other door.
63
- - **INV-VAR-1** A variant is the only thing in the harness that shapes an agent's
64
- instructions. The application adds its own sections through `Launch` (page 01); no other
65
- harness code adds instruction text.
63
+ - **INV-VAR-1** Variant instructions are versioned per scope. The application adds its own sections through `Launch` (page 01). The release-owned `plugin-authoring` skill is also available on demand in structured and terminal sessions, including existing variants; its body is not always-on. A variant skill with the same name takes precedence. It is shipped as a package resource and does not mutate pinned versions.
66
64
  - **INV-VAR-2** Every saved version is validated (`commit` → `validate`). An invalid variant is
67
65
  refused with every problem listed.
68
66
  - **INV-VAR-3** A version is never edited. A change is a new version; `restore` moves the head
@@ -16,7 +16,7 @@ keeps provider caching effective across turns.
16
16
  | `render(env, pack)` → `string` | One string for a session that takes a single prompt. |
17
17
  | `sessionLaunch(env, caller, launch, historyFile, variant)` → `{ system, update, pack }` | SDK sessions: stable prefix plus an update with only the dynamic sections that changed since the last delivered snapshot. |
18
18
  | `deliveredSnapshot(sessionFile)` | Reads the last delivered snapshot on the active history branch; a compaction forces a fresh one. |
19
- | `renderCompactionCheckpoint` | Compiles a bounded current-request, checklist and plan-state message for post-compaction model requests. |
19
+ | `renderCompactionCheckpoint` | Compiles a bounded current-request, todo list and plan-state message for post-compaction model requests. |
20
20
  | `saveForSession`, `copyToRun` | Terminal sessions keep the pack they started with and copy it to each run. |
21
21
  | `record`, `load`, `packPath` | The pack each run was given, at `runs/<id>/context.json`. |
22
22
  | `toolGroups`, `fit`, `clip` | The tool manifest by group; cutting text to a budget at a paragraph boundary. |
@@ -28,8 +28,7 @@ A `ContextPack` holds `sections: Section[]` (each with `id`, `title`,
28
28
 
29
29
  ## Section order
30
30
 
31
- `SECTION_ORDER`: `harnessIdentity` 5, `system` 25, `guardrails` 40, `memory` 45, `references`
32
- 50, `subagents` 64, `scripts` 66, `wiki` 80, `tools` 90, `reporting` 95. Application sections
31
+ `SECTION_ORDER`: `harnessIdentity` 5, `system` 25, `sessionState` 30, `guardrails` 40, `memory` 45, `references` 50, `subagents` 64, `scripts` 66, `wiki` 80, `tools` 90, `reporting` 95. Application sections
33
32
  sit among them by their `order`; plan mode uses `plan` at 25. Ties keep insertion order.
34
33
  When a run may spawn, the dynamic `subagents` section lists every profile it can start (built-in,
35
34
  global, project and variant c5 names) and replaces the variant's own list (PROP-0029). Normal-mode
@@ -37,10 +36,19 @@ guidance in that section explains when delegation is worth its overhead, profile
37
36
  self-contained briefs, independent parallel work, and parent ownership of integration. Plan mode
38
37
  lists only effective profiles eligible for read-only research and requires a named profile; keeping
39
38
  this section dynamic leaves the stable system prompt unchanged across modes. The `tools`
40
- section has a Progress paragraph whenever `checklist_write` is available: it asks for a checklist
41
- early and kept current, then requests a final update before the reply. Only fully completed work is
39
+ section has a Progress paragraph whenever `todo_write` is available: it asks for `todo_write` before
40
+ the first other tool call when work has several steps, asks for the list to stay current, then
41
+ requests a final update before the reply. Only fully completed work is
42
42
  marked completed; unfinished or blocked steps remain accurate (PROP-0006).
43
43
 
44
+ `session-state` is a built-in dynamic section at order 30 when a top-level run supplies it. It
45
+ names the current session or says “unnamed”, shows this run's todo (empty at the start) and a
46
+ previous run's list as context, and says whether this is the first turn. It describes only tools
47
+ in the frozen set: the name line appears only with `session_name`, and the todo line only with
48
+ `todo_write`. It never enters the stable prefix. Structured sessions deliver it through
49
+ `sessionLaunch` and the changed-section rule; the terminal appends the same text as a request-time
50
+ custom message.
51
+
44
52
  The fixed `harness-identity` section says `You are an AI agent running in <productName>.` and uses
45
53
  `env.productName` (default `Xingular Agent`). `Launch.standard: { cwd, project?: { name } | null }` requests shared role and working-folder
46
54
  sections from `prompt/standard.ts`; the compiler supplies common connections, file and annotation
@@ -81,7 +89,7 @@ compile ──► pack ──► stable sections ──► system prompt (+ "Con
81
89
  `references` is always resent when present, because it belongs to the current request.
82
90
  - After successful compaction, the foundation checkpoint extension detects the compacted context and
83
91
  uses `renderCompactionCheckpoint` to append one hidden, ephemeral context message to each model
84
- request while the active run continues, when a checklist or applicable plan instructions exist.
92
+ request while the active run continues, when a todo list or applicable plan instructions exist.
85
93
  It contains only the current request and available state. It is not written to native history, and its state is
86
94
  discarded when the run settles or the branch changes.
87
95
  - The session appends the update as a hidden custom message and saves its snapshot in the
@@ -113,8 +121,8 @@ file SecretStore with a host Keychain. See [parity](../parity.md).
113
121
  instructions. SDK sessions use `sessionLaunch`; terminal sessions use `compile` + `render`.
114
122
  - **INV-PRM-2** The system prompt holds only stable text: identity, role, the variant's sections, the
115
123
  harness's reporting guidance, tool guidance and the tool manifest. Changing state never enters it.
116
- - **INV-PRM-3** `references`, `wiki`, and every section the application marks `dynamic` are
117
- delivered as context updates in native history, never in the system prompt.
124
+ - **INV-PRM-3** `session-state`, `references`, `wiki`, and every section the application marks
125
+ `dynamic` are delivered as context updates in native history, never in the system prompt.
118
126
  - **INV-PRM-4** An update carries only changed dynamic sections, so an unchanged turn appends
119
127
  nothing. A compaction on the active branch forces the full snapshot again.
120
128
  - **INV-PRM-5** The pack only describes tools the caller can call (the frozen capability set).
@@ -149,12 +157,15 @@ what this page states is binding. Adopting parts of that draft is an architectur
149
157
  ## Source map
150
158
 
151
159
  `src/prompt/compile.ts`, `src/sessions/sessions.ts` (calls `sessionLaunch`),
152
- `src/terminal/terminals.ts` (calls `compile` + `render`), `src/variant/materialise.ts` (variant
153
- sections), `src/sessions/planMode.ts` (`plan` section).
160
+ `src/terminal/terminals.ts` (calls `compile` + `render`), `src/sessions/sessionState.ts`
161
+ (the native name reader and state shape), `src/variant/materialise.ts` (variant sections),
162
+ `src/sessions/planMode.ts` (`plan` section).
154
163
 
155
164
  ## Session naming guidance (PROP-0010)
156
165
 
157
- The Session tool group describes `session_name` when available: name meaningful work early
158
- with an imperative sentence of at most six words, and revise when the objective changes.
166
+ The Session tool group describes `session_name` when available: call it once before the first
167
+ other tool call when a request needs tools or changes state, with an imperative sentence of at
168
+ most six words, and revise only when the objective changes.
159
169
  This is fixed foundation guidance. The current name stays in native session metadata and
160
- the terminal snapshot; it is never interpolated into the stable system prompt.
170
+ the terminal snapshot; it may appear in the request-scoped `session-state` update, but it is never
171
+ interpolated into the stable system prompt.
@@ -3,7 +3,7 @@
3
3
  ## Purpose
4
4
 
5
5
  A *plugin* is a bundle with a title, a description and contributions: instructions, skills,
6
- commands, tools, connections, code (a Pi extension) and themes. A Pi package is a plugin as it is;
6
+ commands, subagent profiles (`agent`), tools, connections, code (a Pi extension) and themes. A Pi package is a plugin as it is;
7
7
  so are the harness's own parts, the application's parts, a person's registry, the folder every
8
8
  account shares, and what a working folder brings. This part decides what a session loads, who has
9
9
  what switched on, which code may run, and what reaches one caller. A *capability set* is the
@@ -22,6 +22,7 @@ callable result for one run.
22
22
  | `FIRST_PARTY_PLUGINS` | `plugins/firstParty.ts` | `output-trim`, `budget-guard`: inline extensions shipped switched off. `lessons`: switched off; its contribution turns on reflection after a run (page 08, PROP-0008) and loads no Pi code. |
23
23
  | `Hooks` | `plugins/hooks.ts` | Runs a package's hooks file after a write. |
24
24
  | install / trust | `plugins/install.ts`, `plugins/trust.ts` | Install through Pi's package manager with scripts off; trust code by hash. |
25
+ | proposals | `plugins/proposals.ts` | Validate and store inert proposals; preview, reject or accept a hash-bound review. |
25
26
  | inventory / api | `plugins/inventory.ts`, `plugins/api.ts` | `plugins.list`, `plugins.inventory`, attach, trust. |
26
27
  | capability api | `capabilities/api.ts`, `capabilities/types.ts` | `list`, `resolve`, `toolIntersection`, `inventory`, `attach`. |
27
28
  | run snapshot | `capabilities/runSnapshot.ts` | `freezeRun`, `loadRun`, `capabilitySet`. |
@@ -29,8 +30,9 @@ callable result for one run.
29
30
  ## Contracts
30
31
 
31
32
  - **Loading.** `Plugins.loading({ cwd, agentDir, caller, contributions, install? })` returns
32
- `extensionPaths`, `skillPaths`, `promptPaths`, `themePaths`, `builtins` (legacy shape; no Pi CLI built-ins selected), `hooks`,
33
- `found`, `detached`, `projectCode`, `packageCode`. A session opens Pi with its own discovery off
33
+ `extensionPaths`, `skillPaths`, `promptPaths`, `themePaths`, `profilePaths`, `builtins` (legacy shape; no Pi CLI built-ins selected), `hooks`,
34
+ `found`, `detached`, `projectCode`, `packageCode`. `profilePaths` are read by the harness for
35
+ subagent profiles (page 02); Pi is never given them. A session opens Pi with its own discovery off
34
36
  and only these paths; a terminal receives them as `--extension`, `--skill`,
35
37
  `--prompt-template` and `--theme` after `--no-extensions --no-skills --no-prompt-templates
36
38
  --no-themes`. Pi still reads `AGENTS.md` and `SYSTEM.md` itself.
@@ -45,10 +47,39 @@ callable result for one run.
45
47
  - **Attachments.** `config/plugin-attachments.json` holds `global`, `agents`, `projects` (each
46
48
  `{ on, off }`) and `settings` per plugin. The narrowest scope that says something wins: agent,
47
49
  then project, then everywhere. With no entry an item keeps its default.
48
- - **Package metadata.** A Pi package may declare `"harness": { "extends": [...], "hooks": "./hooks.mjs", "commandPrefix": "my-tools" }`.
50
+ - **Package metadata.** A Pi package may declare `"harness": { "extends": [...], "hooks": "./hooks.mjs", "commandPrefix": "my-tools", "agents": "./agents" }`.
51
+ A package without a `package.json` name is named, described and versioned by the first of
52
+ `plugin.json`, `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, so an agent plugin
53
+ written for other harnesses installs as it is.
54
+ - **Package subagent profiles.** An installed package's agents folder (`agents/` unless
55
+ `harness.agents` names another) brings each Markdown file whose frontmatter has a `name` and a
56
+ `description` as an `agent` contribution (at most 50). It is text: it needs no trust and follows
57
+ its switch like a skill. The profile is named by its frontmatter, not its file. Fields only other
58
+ harnesses understand are dropped (`agents/profiles.ts`, `parsePackageProfile`): their tool names
59
+ are translated (`Read`→`read`, `Glob`→`find`, …), a model without a provider is left to the
60
+ parent, `plugins` and `variant` are never taken, and a tool list of which nothing translates
61
+ keeps the child read-only. A package profile never replaces a built-in, person's, project's or
62
+ variant's profile of the same name; the listing reports it as a problem instead.
49
63
  `commandPrefix` is optional; in the terminal, commands otherwise use a normalized package name as
50
64
  their owner prefix. Prefixes use lowercase letters, digits and hyphens, starting with a letter;
51
65
  `plugin` and `skill` are reserved. See page 11 for the command route and collision rules.
66
+ - **Plugin authoring.** `plugin_propose` creates a pending proposal, not an installation. The
67
+ release-owned `plugin-authoring` skill is available on demand in structured and terminal sessions;
68
+ a pinned variant's skill of the same name wins. It adds no trust or permission
69
+ ([ADR-0045](../decisions/ADR-0045-default-plugin-authoring-skill.md)).
70
+ - **Proposal review.** `plugins/proposals.ts` validates a lowercase package name, nonempty description,
71
+ and 1–100 UTF-8 text files (256 KiB each, 1 MiB total). It accepts `README.md`, extensions
72
+ (`.ts`, `.js`, `.mjs`), Markdown skills, prompts and agents, and JSON themes in their respective
73
+ directories; paths must be safe and cannot contain `.git` or `node_modules`. A
74
+ `skills/<name>/SKILL.md` needs matching name and nonempty description frontmatter. The Harness
75
+ generates `package.json`; it is not supplied by the proposal. Preview revalidates the stored data
76
+ and computes SHA-256 over name, description, generated manifest and sorted file entries. Accept
77
+ and reject require the current preview hash and pending status; stale reviews fail.
78
+ Acceptance rechecks after adoption and listing, refuses an existing package name or already-trusted
79
+ identical package code, stages and publishes under `<data root>/plugins/<name>`, clears old
80
+ attachment decisions for that package and explicitly switches it off globally. It grants no code
81
+ trust or permission; reject retains the proposal as rejected. These are trusted caller review
82
+ operations, not model tools. See [ADR-0046](../decisions/ADR-0046-hash-bound-plugin-proposals.md).
52
83
  - **Hooks.** A package declares `"harness": { "extends": [...], "hooks": "./hooks.mjs" }`. Events:
53
84
  `wiki.page.saved`, `history.run.ended`. A hook has 30 seconds; its errors are kept in
54
85
  `plugin-hooks.json`. `ctx.call(connection, tool, args)` reaches only a connected app that is on
@@ -114,6 +145,7 @@ callable result for one run.
114
145
  ```text
115
146
  <data root>/config/plugin-attachments.json (registry-enabled.json read until the first change)
116
147
  <data root>/plugins/<name>/ packages kept with the data; removal moves to trash/plugins/
148
+ <data root>/plugin-proposals/<id>.json proposed text, hash and review status
117
149
  <state dir>/plugin-trust.json trusted code, by hash
118
150
  <state dir>/plugin-registered.json what each extension registered last time
119
151
  <state dir>/plugin-hooks.json hooks and their last run
@@ -134,13 +166,17 @@ metadata or availability:
134
166
  | Foundation | Accounts, sessions and run recording, prompt and variant validation, gate, plugin loading/trust/attachments, resource browser, server and terminal frameworks | Release-owned infrastructure and security contracts |
135
167
  | World | Web search/read tools, browser tools, page annotations | The isolated browser engine and network policy remain Foundation services |
136
168
  | Memory | Wiki, history and trajectory | Storage and run records remain Foundation-owned services |
137
- | Automation | Subagents, ask-user, checklist, lessons and refinement | Run lifecycle, capability narrowing and approval gate remain Foundation-owned |
169
+ | Automation | Subagents, ask-user, todo, lessons and refinement | Run lifecycle, capability narrowing and approval gate remain Foundation-owned |
138
170
  | Integrations | Connected apps/MCP and Hub | Connector credentials and plugin installation remain behind Foundation services |
139
171
  | Operations | Doctor, worktrees and usage | Diagnostics, repository operations and usage analytics remain Harness-managed services |
140
172
 
141
- ## Terminal resource inspection
173
+ ## Terminal plugin and resource inspection
142
174
 
143
- The unified browser joins saved plugin/contribution metadata with actual session skills and tool
144
- registration. A saved on/off state does not prove a resource loaded; origins and owners distinguish
145
- same-name resources. Protected install/remove, settings, attachment and trust actions call the
146
- existing services. No second discovery or enablement mechanism is introduced (PROP-0013).
175
+ The broad `/harness` browser joins saved plugin/contribution metadata with actual session skills and
176
+ tool registration. A saved on/off state does not prove a resource loaded; origins and owners
177
+ distinguish same-name resources. `/plugins` is a focused local manager with Plugins, Trust and
178
+ Packages tabs. It uses the existing plugin services for switching, trust, settings, package install,
179
+ update and removal. `/hub` is a distinct Community catalog surface; it does not load code or add a
180
+ second loader. Hub-owned package removal through `plugins.remove` delegates to Hub so the exact
181
+ receipt and its resources stay consistent. No second discovery or enablement mechanism is
182
+ introduced (PROP-0013, PROP-0047).
@@ -16,9 +16,9 @@ person without letting remembered per-tool grants widen that boundary.
16
16
  | `dispatch` | `tools/dispatch.ts` | One entry point for every History, Wiki and skill call, from a Pi tool, the server or a terminal. |
17
17
  | `harnessTools` | `tools/harnessTools.ts` | The definitions as `ToolSpec`s for one session. |
18
18
  | `askUserTool` (`ask_user`) | `tools/askUser.ts` | Stops an attended plan run and asks up to five related questions with choices and concise 1–2 word labels; absent from newly resolved normal runs. |
19
- | `checklistTool` (`checklist_write`) | `tools/checklist.ts` | Writes the run's whole checklist as a `checklist.updated` event (PROP-0006). Read-only for the gate, so plan mode allows it. Terminal sessions register the same tool; it reaches the run through the `checklist` telemetry call. The prompt's Progress group asks the model to keep it current and write one final update before replying; unfinished work is not automatically marked complete. |
19
+ | `todoTool` (`todo_write`) | `tools/todo.ts` | Writes the run's whole todo list as a `todo.updated` event (PROP-0006, renamed by PROP-0045). Read-only for the gate, so plan mode allows it. Terminal sessions register the same tool; it reaches the run through the `todo` telemetry call. The prompt's Progress group asks the model to call it before the first other tool call when work has several steps, keep it current, and write one final update before replying; unfinished work is not automatically marked complete. The pre-rename `checklist.updated` event is still read, never written. |
20
20
  | `subagentTools` | `tools/spawnSubagent.ts` | `spawn_subagent` (requires a short `title`, PROP-0030) and `wait_subagents`; normal-mode descriptions guide selective, bounded delegation, while plan-mode spawns require a named eligible read-only research profile. |
21
- | `annotationsPart` (`page_notes_list`, `page_notes_read`, `page_notes_mark_fixed`) | `annotations/part.ts` | World tools over the page notes a person shared with the session or agent (PROP-0017). `page_notes_mark_fixed` records the agent's claim that it fixed a note it was sent; it never resolves a note or changes grants (PROP-0018). |
21
+ | `annotationsPart` (`page_notes_list`, `page_notes_read`) | `annotations/part.ts` | Read-only world tools over the page notes a person shared with the session or agent (PROP-0017, PROP-0048). |
22
22
  | `webTools` | `web/tools.ts` | `web_search` (Brave, key from the secret store), `web_fetch` (checked public reads). An application provides them as a provider. |
23
23
  | gate | `sessions/sessions.ts::gate`, `sessions/runner.ts` | `ToolGate { ask, check }`, loaded as the last extension (`xagent-approvals`). |
24
24
  | `Approvals`, `decide`, `grant`, `approvalKey` | `sessions/approvals.ts` | Standing approvals. |
@@ -26,7 +26,7 @@ person without letting remembered per-tool grants widen that boundary.
26
26
  Harness tools: `get_capabilities` (read-only current-run discovery), `history_search`, `history_read`,
27
27
  `trajectory_search`, `trajectory_read`, `wiki_search`, `wiki_read`, `wiki_list`, `wiki_write`
28
28
  (not in the default set), `skill_load`, `ask_user` (attended plan runs and pinned continuations only),
29
- `checklist_write`. Broker:
29
+ `todo_write`, `plugin_propose`. Broker:
30
30
  `mcp_list_tools`, `mcp_call_tool`. Native Pi tools: `read`, `grep`, `find`,
31
31
  `ls`, `edit`, `write`, `bash`.
32
32
 
@@ -45,6 +45,17 @@ when plan mode is active it reports the plan's allowed tool set. `sessions/planM
45
45
  the shared read-only allowlist. Host sessions use this Harness tool rather than defining a duplicate.
46
46
  It reports availability, never permission, and every call remains subject to the gate.
47
47
 
48
+ ## Plugin proposal security
49
+
50
+ `plugin_propose { name, description, files }` is a write-classified Harness tool, subject to the
51
+ normal capability and per-call gate, and creates only a pending proposal in the Harness data root.
52
+ It does not accept, install, enable or trust a plugin. Proposal text is untrusted, including skill
53
+ instructions; preview and human review are needed before acceptance. Acceptance is a separate
54
+ trusted-caller API, bound to the reviewed content hash; it does not execute package code, grant
55
+ trust or bypass the loader, attachment controls, capability restrictions or gate. Text contributions
56
+ can load once enabled without code trust, so review their instructions as well as executable files.
57
+ See page 06 and [ADR-0046](../decisions/ADR-0046-hash-bound-plugin-proposals.md).
58
+
48
59
  ## Native file paths
49
60
 
50
61
  The read-only `read`, `grep`, `find` and `ls` tools accept paths relative to the working folder,
@@ -76,6 +87,18 @@ never replayed after partial effects. Full Permission is the explicit eligible-r
76
87
  Unsupported or unavailable sandbox enforcement fails closed. A side-question run has an empty
77
88
  capability set and no tools (PROP-0019).
78
89
 
90
+ **Windows** has no write-boundary sandbox the harness can use, so the Bash tool treats every
91
+ command there as an outside request: a human-only one-call approval (or an eligible Full
92
+ Permission answer), then the command runs once without a sandbox. Plan-mode Bash is refused,
93
+ because a read-only scope cannot be enforced. Commands run in Git Bash when it is installed
94
+ (`%ProgramFiles%\Git\bin\bash.exe`, then `bash.exe` on `PATH`, never the WSL launcher in
95
+ `System32`), otherwise in PowerShell (`pwsh.exe`, then Windows PowerShell). The tool keeps the name
96
+ `bash`; its description names the shell. Native `edit` and `write` run their atomic helper with
97
+ Node directly, without an OS guard: the target is still canonicalized, authorized, revalidated and
98
+ replaced by rename, and outside targets still need their approval, but a junction or symlink swap
99
+ between the check and the rename is not prevented
100
+ ([ADR-0042](../decisions/ADR-0042-windows-shell-without-sandbox.md)).
101
+
79
102
  ## The gate, per call
80
103
 
81
104
  For native `edit` and `write`, the canonical target check is added to policy scope before approval;
@@ -96,6 +119,7 @@ policy.authorize(call) throws ─► refuse
96
119
  outside native file target ─► human-only one-call approval; eligible Full Permission may answer
97
120
  Bash workspace scope ─► hard-link scan, OS sandbox, run once
98
121
  Bash outside scope ─► pre-execution human-only approval, then run once unconfined
122
+ Bash on Windows (no sandbox) ─► as outside scope; plan-mode Bash refused
99
123
  trigger probe, improver, reflection or unattended ─► refuse anything requiring a person
100
124
  run.requestApproval(...) ─► standing approvals may answer where eligible, else the person
101
125
  denied ─► refuse; never replay a command
@@ -131,6 +155,8 @@ the switch and global tools.
131
155
  Full Permission switch can answer automatically under its existing restrictions. In plan mode, Bash
132
156
  runs only in the read-only scope (PROP-0043): no writes but a discarded temp folder, no network,
133
157
  no signals outside the sandbox, no macOS mach services; Full Permission and `outside` never apply.
158
+ On Windows, which has no sandbox, every Bash call takes the `outside` path and plan-mode Bash is
159
+ refused (ADR-0042).
134
160
  - **INV-GATE-4** Refinement runs (`probe`, `improver`) never ask a person; what would need one is
135
161
  refused, never queued (`INV-REF-4`).
136
162
  - **INV-GATE-5** SDK tools are registered only if their names are in the run's frozen capability
@@ -155,6 +181,7 @@ the switch and global tools.
155
181
  same choice as `!outside <command>`. Full Permission is the only standing boundary bypass and
156
182
  remains subject to host policy, authorization, read-only, plan, unattended, probe, improver and
157
183
  reflection restrictions. The rule does not protect against a peer process running as the same user.
184
+ On Windows the atomic replacement runs without an OS-enforced scope (ADR-0042).
158
185
 
159
186
  ## Data
160
187
 
@@ -164,16 +191,23 @@ the switch and global tools.
164
191
 
165
192
  `src/tools/*.ts`, `src/web/*.ts`, `src/sessions/sessions.ts`, `src/sessions/runner.ts`,
166
193
  `src/sessions/workspaceBoundary.ts`, `src/sessions/initialBash.ts`, `src/core/workspaceBashTool.ts`,
167
- `src/core/workspaceSandbox.ts`, `src/core/workspaceWriteOperations.ts`, `src/sessions/approvals.ts`,
168
- `src/sessions/policy.ts`. Decision: [ADR-0036](../decisions/ADR-0036-workspace-write-operation-boundary.md).
194
+ `src/core/shell.ts`, `src/core/workspaceSandbox.ts`, `src/core/workspaceWriteOperations.ts`,
195
+ `src/sessions/approvals.ts`, `src/sessions/policy.ts`. Decisions:
196
+ [ADR-0036](../decisions/ADR-0036-workspace-write-operation-boundary.md),
197
+ [ADR-0042](../decisions/ADR-0042-windows-shell-without-sandbox.md).
169
198
 
170
199
  ## Session naming (PROP-0010)
171
200
 
172
201
  `session_name` (`tools/sessionName.ts`) binds to the current native session. It maintains
173
202
  only Pi session metadata and is explicitly plan-safe, while still subject to capability
174
- selection and the host policy. The terminal registers the same definition through its Pi
175
- bridge; standalone policy permits naming without approval. See page 02 for validation and
176
- persistence. It is a foundation tool, not mutable variant data.
203
+ selection and the host policy. The prompt asks for it once before the first other tool call when
204
+ a request needs tools or changes state. When a top-level attended session starts unnamed, the
205
+ harness also derives a provisional name from its first substantive request and sets it through
206
+ Pi before the first model request; a host gate refusal or a failed write leaves the session
207
+ unnamed and never fails the run. The model may revise the provisional name. The terminal
208
+ registers the same definition through its Pi bridge; standalone policy permits naming without
209
+ approval. See page 02 for validation and persistence. It is a foundation tool, not mutable
210
+ variant data.
177
211
 
178
212
  ## Diagnostic probes
179
213
 
@@ -181,3 +215,10 @@ persistence. It is a foundation tool, not mutable variant data.
181
215
  intersect current capabilities, and execute tool calls through the existing gate. Calls that
182
216
  require an additional per-call approval report blocked; batch diagnostic consent does not grant
183
217
  standing permission. No raw tool inputs, results, or screenshots enter diagnostic findings.
218
+
219
+ A live check may also run one command the harness wrote itself, when the capability it diagnoses is
220
+ a native Pi tool rather than a harness tool: the shell check runs `printf` through the same
221
+ workspace root, hard-link scan and write-boundary sandbox as a Bash call, and reports only the exit
222
+ status. It passes the gate first, and a diagnostic never widens the boundary — Full Permission,
223
+ an outside-workspace request and a host bypass are all refused for it — so a boundary that stops
224
+ the command before the shell starts is reported as blocked with the boundary's own reason.
@@ -38,7 +38,10 @@ skills, prompts, rules and Pi packages.
38
38
  - **`apps.expose(tools)`** offers the application's own tools through the broker, which is how a
39
39
  terminal session reaches them.
40
40
  - **Hub.** `hub.list`, `get`, `preview`, `install`, `uninstall`, `readDocument`. Pi packages need
41
- an exact npm version or a full Git commit. Documents are saved as inert JSON.
41
+ an exact npm version or a full Git commit. Documents are saved as inert JSON. The `/hub` Catalog
42
+ view filters Community entries for display; the service and other callers retain the full catalog.
43
+ Installed shows receipts from every channel. On page entry, cached/bundled content is shown before
44
+ a remote refresh, and origin, refresh time and failure status remain visible (PROP-0047).
42
45
 
43
46
  ## Invariants
44
47
 
@@ -62,7 +65,10 @@ skills, prompts, rules and Pi packages.
62
65
  nothing of it is stored.
63
66
  - **INV-MCP-4** Hub installation grants no attachment, trust, credential or permission. Writes
64
67
  are serialized; existing registry files are never overwritten; uninstall removes only the exact
65
- receipt's resources and revokes its attachment choices.
68
+ receipt's resources and revokes its attachment choices. Every removal route for a Hub-owned Pi
69
+ package, including `plugins.remove`, goes through Hub's receipt-owning uninstall operation. A
70
+ floating plugin-package update must not change a Hub-owned package; replacement uses an explicit
71
+ exact catalog version (PROP-0047).
66
72
 
67
73
  ## Data
68
74