@xingular/harness 1.3.1-unstable.2 → 1.3.1-unstable.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +1 -1
- package/dist/cli.js +1217 -701
- package/dist/sdk/computer/screenshot.d.ts +32 -0
- package/dist/sdk/computer/screenshot.js +35 -0
- package/dist/sdk/context/attribution.d.ts +18 -0
- package/dist/sdk/context/attribution.js +295 -0
- package/dist/sdk/context/types.d.ts +94 -0
- package/dist/sdk/context/types.js +10 -0
- package/dist/sdk/index.d.ts +2 -1
- package/dist/sdk/index.js +2 -1
- package/dist/sdk/plugins/builtin.js +1 -0
- package/dist/sdk/prompt/compile.d.ts +42 -2
- package/dist/sdk/prompt/compile.js +87 -15
- package/dist/sdk/server/methods.js +3 -0
- package/dist/sdk/sessions/contracts.d.ts +4 -0
- package/dist/sdk/sessions/contracts.js +2 -0
- package/dist/sdk/sessions/runner.d.ts +10 -0
- package/dist/sdk/sessions/runner.js +17 -0
- package/dist/sdk/sessions/subagentService.d.ts +11 -1
- package/dist/sdk/sessions/subagentService.js +120 -38
- package/dist/sdk/sessions/subagents.d.ts +31 -0
- package/dist/sdk/sessions/subagents.js +45 -0
- package/dist/sdk/terminal/client.d.ts +1 -1
- package/dist/sdk/terminal/client.js +13 -6
- package/dist/sdk/terminal/control.d.ts +1 -1
- package/dist/sdk/terminal/control.js +2 -2
- package/dist/sdk/terminal/controller.d.ts +9 -0
- package/dist/sdk/terminal/controller.js +48 -11
- package/dist/sdk/terminal/piExtension.js +35 -1
- package/dist/sdk/terminal/terminals.d.ts +2 -0
- package/dist/sdk/terminal/terminals.js +11 -0
- package/dist/sdk/terminal/tui/commands.d.ts +1 -2
- package/dist/sdk/terminal/tui/commands.js +1 -4
- package/dist/sdk/terminal/tui/dialogs.d.ts +3 -2
- package/dist/sdk/terminal/tui/dialogs.js +4 -3
- package/dist/sdk/terminal/tui/keybindings.d.ts +1 -1
- package/dist/sdk/terminal/tui/keybindings.js +1 -1
- package/dist/sdk/terminal/tui/modeChoices.d.ts +3 -2
- package/dist/sdk/terminal/tui/modeChoices.js +4 -4
- package/dist/sdk/terminal/tui/mouseReport.d.ts +30 -0
- package/dist/sdk/terminal/tui/mouseReport.js +80 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.js +3 -2
- package/dist/sdk/terminal/tui/pages/browserPage.d.ts +1 -1
- package/dist/sdk/terminal/tui/pages/contextPage.d.ts +54 -0
- package/dist/sdk/terminal/tui/pages/contextPage.js +393 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +5 -4
- package/dist/sdk/terminal/tui/runtime.js +8 -2
- package/dist/sdk/terminal/tui/search.js +3 -2
- package/dist/sdk/terminal/tui/skillInvocation.d.ts +21 -2
- package/dist/sdk/terminal/tui/skillInvocation.js +31 -11
- package/dist/sdk/tools/defaultSkills.d.ts +19 -0
- package/dist/sdk/tools/defaultSkills.js +27 -1
- package/dist/sdk/tools/dispatch.js +8 -9
- package/dist/sdk/tools/spawnSubagent.d.ts +1 -1
- package/dist/sdk/tools/spawnSubagent.js +4 -2
- package/dist/terminal-opentui.js +570 -240
- package/dist/terminal-pi.js +142 -28
- package/docs/README.md +1 -0
- package/docs/architecture/02-sessions-and-runs.md +9 -0
- package/docs/architecture/04-prompt.md +13 -4
- package/docs/architecture/07-tools-and-gate.md +1 -1
- package/docs/architecture/11-terminal.md +12 -2
- package/docs/architecture/13-world-tools.md +17 -3
- package/docs/architecture/14-surfaces-and-parity.md +3 -3
- package/docs/architecture/README.md +1 -0
- package/docs/architecture/proposals/PROP-0049-context-attribution.md +382 -0
- package/docs/architecture/proposals/README.md +1 -0
- package/docs/decisions/ADR-0048-required-skills-from-a-message.md +42 -0
- package/docs/decisions/ADR-0050-approved-window-screenshots.md +19 -0
- package/docs/decisions/ADR-0051-terminal-decision-wait.md +10 -0
- package/docs/decisions/ADR-0052-per-profile-subagent-budgets.md +25 -0
- package/docs/parity.md +6 -6
- package/docs/releases/1.3.1-unstable.3.md +54 -0
- package/docs/terminal-tui.md +28 -15
- package/package.json +1 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: PROP-0049
|
|
3
|
+
title: Context attribution as a built-in capability
|
|
4
|
+
status: Proposed
|
|
5
|
+
created: 2026-10-28
|
|
6
|
+
updated: 2026-10-28
|
|
7
|
+
affects: [INV-PRM-6, INV-SRV-1, INV-PAR-1, INV-PAR-2, INV-RUN-7, 04-prompt.md, 10-server-protocol.md, 11-terminal.md, 14-surfaces-and-parity.md, parity.md]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# PROP-0049: Context attribution as a built-in capability
|
|
11
|
+
|
|
12
|
+
## Problem
|
|
13
|
+
|
|
14
|
+
A person can see that a run is expensive but not where the context goes. The harness already
|
|
15
|
+
records every fact needed to answer it, and no surface shows the answer:
|
|
16
|
+
|
|
17
|
+
- `runs/<id>/context.json` holds the pack the run was given: each section's `title`, `size`
|
|
18
|
+
(characters), `budget`, `cut` and `sources`, plus `toolGroups` and the variant fingerprint
|
|
19
|
+
(`src/prompt/compile.ts`).
|
|
20
|
+
- `runs/<id>/events.jsonl` holds `context.snapshot` — the verbatim `systemPrompt`, the
|
|
21
|
+
`contextUpdate`, and every tool's `{name, description, parameters}` — and `usage.updated`, whose
|
|
22
|
+
`contextTokens` is the provider-measured size of the latest request and `contextWindow` the
|
|
23
|
+
model's limit (`src/sessions/contracts.ts`, emitted by `src/sessions/runner.ts`).
|
|
24
|
+
- `Pi` computes a live `{ tokens, contextWindow, percent }` for the session
|
|
25
|
+
(`getContextUsage()`), and its estimator is `ceil(chars / 4)`
|
|
26
|
+
(`node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js`).
|
|
27
|
+
|
|
28
|
+
What is missing is the attribution: a row per contributor, tokens, share of the window, and a
|
|
29
|
+
stack that reconciles to the measured total. The App shows a reply's context pack as a collapsed
|
|
30
|
+
`<details>` list of character counts (`App/src/modes/shared/ContextPackView.tsx`), and a single
|
|
31
|
+
`Meter` for "context at the end" (`App/src/modes/shared/SessionContextInspector.tsx`). The terminal
|
|
32
|
+
declares `model.contextWindow` on its status band and never renders it
|
|
33
|
+
(`src/terminal/tui/status.ts`). `usage.summary` carries no context fields at all
|
|
34
|
+
(`src/usage/types.ts`).
|
|
35
|
+
|
|
36
|
+
Attribution is something a person uses, so it belongs in the Harness with a terminal surface
|
|
37
|
+
before an App screen (`INV-PAR-1`, `INV-PAR-2`).
|
|
38
|
+
|
|
39
|
+
## What other harnesses show
|
|
40
|
+
|
|
41
|
+
Evidence is from the checkout in `external/` for open repositories, and from official docs for
|
|
42
|
+
Claude Code, Codex CLI and Gemini CLI. "Third-party" marks a claim read from a user report rather
|
|
43
|
+
than a vendor document.
|
|
44
|
+
|
|
45
|
+
| Harness | Where it shows | Rows / categories | % of window | Visual | Measured vs estimated | Thresholds | Actions from the view |
|
|
46
|
+
|---|---|---|---|---|---|---|---|
|
|
47
|
+
| **Claude Code** | `/context` command in the transcript; ring in the desktop panel; scriptable status line | `System prompt`, `System tools`, `MCP tools`, `Custom agents` (with source), `Memory files`, `Skills`, `Messages`; plus `Autocompact buffer` and `Free space` | Per row and in the header `598.5k / 1M (60%)` | "Colored grid" (docs); a table of tokens + % in the panel (**third-party**, [issue #94436](https://github.com/anthropics/claude-code/issues/94436)) | Estimated per row; header from measured | Ring blue → yellow → red; over-limit banner naming `/compact` | Text suggestions only ("trim CLAUDE.md · 49k per request"); `/usage` can apply some |
|
|
48
|
+
| **Pi** (the engine under this harness) | TUI footer segment | One aggregate: `45.3%/200K (auto)` plus `↑in ↓out R<cache> W<cache> CH<hit>% $cost` (`dist/modes/interactive/components/footer.js:133-200`) | Yes, one decimal | Text, coloured | `getContextUsage()` = measured usage anchored, tail estimated at chars/4 | `>70` warning, `>90` error; `?/window` when unknown | `/compact` |
|
|
49
|
+
| **opencode** | Sidebar block + prompt footer (`external/opencode/packages/tui/src/feature-plugins/sidebar/context.tsx:20-51`, `prompt/footer.tsx:38-55`) | One aggregate: `N tokens`, `N% used`, `$cost` | Rounded integer | Plain text, no bar, no colour | Last provider usage only (`util/session.ts:46-61`) | None | `<leader>c` / `/compact`; auto-compact reserves 15% or 16k |
|
|
50
|
+
| **oh-my-pi (omp)** | Status-line segment system (`external/omp/packages/tui/src/status-line/footer.ts:171-260`) | One aggregate: `45.3%/200K`, per-direction counters, billing | One decimal | Coloured text, segment priorities | Provider-anchored, tail estimated; strict/approximate/upperbound tokenizer modes | Percent **or** absolute tokens, whichever first: 50%/150k, 70%/270k, 90%/500k (`chrome/context-thresholds.ts:6-11`) | `/compact` modes, `/shake` (drop heavy tool results), `/handoff` |
|
|
51
|
+
| **DeepSeek Harness** | Web chat statistics dock; per-turn disclosure (`external/deepseek-harness/packages/client/ui-chat`) | `systemTokens`, `toolsTokens`, `messageTokens` — exactly three (`packages/llm/token-meter/README.md`) | Separate `contextPressure.pressureTokens`; the division is client-side | Not confirmed | Explicit: provider usage reused only on an identical request envelope, else estimated; rows are "approximate composition, never a total" | Compaction at `min(W×0.8, W−O−65536)` | `/compact`; tool-result pruning |
|
|
52
|
+
| **Kimi CLI** | Status bar, right-aligned (`external/kimi-cli/src/kimi_cli/ui/shell/prompt.py:2251`, `soul/__init__.py:80`) | One aggregate: `context: 42.0% (4.2k/10.0k)` | Percent first | Plain text | Last provider usage (lags one request) | Ratio 85% **or** 50k reserve (`soul/compaction.py:59`) | `/compact`, `/clear` |
|
|
53
|
+
| **Orca** | 16px SVG ring in the composer row, click/hover opens a popover (`external/orca/src/.../NativeChatContextUsageRing.tsx`) | Passes the underlying CLI's own categories through verbatim, with a `deferred` flag for on-demand context | Row % only, one decimal; ring clamps, stored value does not | Ring + `Progress` bar in the popover | Three states: `report`, `estimate`, `unknown`; window held separately from used | `CRITICAL_PERCENTAGE = 90` turns the ring destructive | None — read-only |
|
|
54
|
+
| **Codex CLI** | `/status` prints configuration and "remaining context capacity"; `/statusline` picks footer fields (`developers.openai.com/codex/cli/slash-commands`) | No breakdown | Remaining capacity | Configurable footer items: model / context / limits / git / tokens / session | Not documented | Not documented | `/compact` |
|
|
55
|
+
| **Gemini CLI** | `/stats` full-screen report (`external`-free; `docs/reference/commands.md:438-450`) | `session` (duration, tool calls), `model` (token counts, quota), `tools` (per-tool) | Not in the documented output | Text tables | Not documented | Not documented | `/compress` |
|
|
56
|
+
|
|
57
|
+
Distinctive ideas worth keeping, by harness: **Claude Code** — a stack closed by `Autocompact
|
|
58
|
+
buffer` + `Free space`, and the remedy named inside the view. **Pi** — `?` when the window is
|
|
59
|
+
unknown rather than `0%`. **opencode** — one `formatContextUsage` helper shared by every surface, so
|
|
60
|
+
they cannot disagree. **omp** — warn on percent *or* absolute tokens (small windows warn at the
|
|
61
|
+
same absolute size) and a declared drop order for narrow terminals. **DeepSeek** — three separate
|
|
62
|
+
projections (billing, occupancy, composition), never mixed into one number. **Kimi** — percent
|
|
63
|
+
first, `used/total` in parentheses, readable at 30 columns. **Orca** — show the provider's own
|
|
64
|
+
category names instead of inventing a taxonomy, and mark deferred context as deferred. **Codex** —
|
|
65
|
+
the person chooses which fields the always-on strip carries.
|
|
66
|
+
|
|
67
|
+
## Affected rules
|
|
68
|
+
|
|
69
|
+
- **INV-PRM-6** Each run's pack is recorded beside the run; failing to record it never fails the
|
|
70
|
+
session. Attribution reads that record and adds per-section rendered sizes to it. Recording
|
|
71
|
+
stays best-effort.
|
|
72
|
+
- **INV-SRV-1** `src/server/methods.ts` is the contract for every non-Node host. `context.attribution`
|
|
73
|
+
is additive: no existing method, param or result shape changes.
|
|
74
|
+
- **INV-RUN-7** Token accounting keeps its meaning. `usage.updated.contextTokens` stays the
|
|
75
|
+
provider's figure for the latest request; attribution never rewrites it, and marks every row it
|
|
76
|
+
derived as estimated.
|
|
77
|
+
- **INV-PAR-1 / INV-PAR-2** The capability lands in the Harness with a terminal surface, and the App
|
|
78
|
+
presents the same rows from the same method.
|
|
79
|
+
- **INV-PRM-1** The prompt is still assembled only in `src/prompt/compile.ts`. Attribution measures
|
|
80
|
+
what that compiler produced; it never assembles text.
|
|
81
|
+
|
|
82
|
+
## Proposal
|
|
83
|
+
|
|
84
|
+
### 1. The measurement model
|
|
85
|
+
|
|
86
|
+
Three facts are read, and nothing new is written per request:
|
|
87
|
+
|
|
88
|
+
| Fact | Source | Kind |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `systemPrompt`, `contextUpdate`, `tools[]` | `context.snapshot` event | recorded verbatim |
|
|
91
|
+
| `contextTokens`, `contextWindow` | latest `usage.updated` event | **measured** by the provider |
|
|
92
|
+
| sections, `toolGroups`, variant, `delivery` | `runs/<id>/context.json` | recorded at compile time |
|
|
93
|
+
|
|
94
|
+
One constant, `CHARS_PER_TOKEN = 4`, the same ratio Pi, opencode, DeepSeek and Kimi use, so a row
|
|
95
|
+
in this view and the terminal's status band never disagree by more than rounding.
|
|
96
|
+
|
|
97
|
+
`compile.render()` records the rendered size of each section (including the `## Title` framing and
|
|
98
|
+
the gaps between sections) and, inside a section, the characters contributed by each `Source`. That
|
|
99
|
+
is the only change to recorded data: it removes the need to re-parse a prompt later, which cannot
|
|
100
|
+
be done safely because a variant section's body may itself contain `## ` headings.
|
|
101
|
+
|
|
102
|
+
Rows are built in this order, and the last one is a subtraction rather than an estimate:
|
|
103
|
+
|
|
104
|
+
1. **System prompt sections** — each `Section`'s rendered characters ÷ 4, attributed to the group
|
|
105
|
+
that owns it.
|
|
106
|
+
2. **Tool schemas** — `JSON.stringify({name, description, parameters})` ÷ 4 for each recorded tool,
|
|
107
|
+
grouped by the pack's `toolGroups`; a tool in no group becomes `Other tools`.
|
|
108
|
+
3. **This request's update** — the recorded `contextUpdate` ÷ 4.
|
|
109
|
+
4. **Conversation** — `contextTokens − tokens(1..3)`, floored at zero. When the subtraction would go
|
|
110
|
+
negative the prefix rows are scaled down and the view says the prefix estimate exceeded the
|
|
111
|
+
measured total; it never shows a negative row.
|
|
112
|
+
5. **Compaction reserve** — Pi's `settings.compaction.reserveTokens` (default 16 384) when
|
|
113
|
+
auto-compaction is on.
|
|
114
|
+
6. **Free space** — `contextWindow − contextTokens − reserve`, so the stack sums to the window with the
|
|
115
|
+
reserve shown as its own row (the reserve is headroom the next request may not use, not free space).
|
|
116
|
+
|
|
117
|
+
The rows therefore always add up to the window, and the number in the header is always the
|
|
118
|
+
provider's. A run with no `usage.updated` yet shows estimated rows and marks the header
|
|
119
|
+
`estimate only`; a session whose window is unknown shows `/ ?` like Pi, never `0%`.
|
|
120
|
+
|
|
121
|
+
### 2. The row taxonomy: foundation, variant, application, conversation
|
|
122
|
+
|
|
123
|
+
This is where the harness can say something no other harness can. Rows group by *who owns the
|
|
124
|
+
text*, and variant rows say whether they cost every request:
|
|
125
|
+
|
|
126
|
+
| Group | Members | Owner |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| **Foundation** | Harness identity, tool guidance, tool schemas (History, Wiki, Native, World, Progress, Session, Annotations), Wiki pointer, session state, references, reporting rules | released harness |
|
|
129
|
+
| **Variant c1–c8** | Instructions (c1), Guardrails (c2), Memory (c3), Tool guidance (c4), subagent index (c5), skills index (c6), scripts index (c7) | `variant/`, refinable |
|
|
130
|
+
| **Your application** | Identity, role, working folder, skills, plugin descriptions, connected apps, annotations | the host's sections and tool groups |
|
|
131
|
+
| **Conversation** | Messages and tool results (measured residual), context updates | the run |
|
|
132
|
+
| **Reserved and free** | Compaction reserve, free space | Pi, the model |
|
|
133
|
+
|
|
134
|
+
Two facts make the variant rows actionable: `alwaysOnMax` caps always-on variant text at 24 000
|
|
135
|
+
characters (`src/variant/surface.ts`), and only components with `reach: 'always'` occupy the prompt
|
|
136
|
+
on every request. The view therefore shows a **budget meter per variant row** (`3.6K of 24K`) and a
|
|
137
|
+
separate **on demand** list — skills, subagents, scripts — which are not in this request but will
|
|
138
|
+
cost about *n* tokens when loaded. Claude Code shows a `Skills` row without that distinction; Orca
|
|
139
|
+
has the `deferred` flag but no per-row cost. Showing both is the improvement.
|
|
140
|
+
|
|
141
|
+
The foundation/variant split also gives the view its one obvious action: a variant row deeper than
|
|
142
|
+
the cap, or plainly oversized, links to the Harness screen where it is edited or refined.
|
|
143
|
+
|
|
144
|
+
### 3. Surfaces
|
|
145
|
+
|
|
146
|
+
**Harness (reference terminal).** A `/context` full-screen page built like `/usage`:
|
|
147
|
+
`src/terminal/tui/pages/contextPage.ts`, registered in `commandPages.ts` as a command page, reading
|
|
148
|
+
`context.attribution`. It also fills the gap in `src/terminal/tui/status.ts`, whose band declares
|
|
149
|
+
`model.contextWindow` and never paints it: the band gains `45.3%/200K` in the same place Pi puts it.
|
|
150
|
+
|
|
151
|
+
**Server.** One additive method:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
context.attribution { runId }
|
|
155
|
+
→ {
|
|
156
|
+
runId, model, provider, window, used, share, // numbers the provider reported
|
|
157
|
+
source: 'measured' | 'estimate', // which of the above is trusted
|
|
158
|
+
compaction: { enabled, reserveTokens, thresholdTokens } | null,
|
|
159
|
+
rows: [{ id, title, group, tokens, chars, share, always, estimated, cut?,
|
|
160
|
+
budget?, sources?, tools?, note? }],
|
|
161
|
+
onDemand: [{ id, title, tokens, items }],
|
|
162
|
+
totals: { foundation, variant, application, conversation, reserved },
|
|
163
|
+
recordedAt
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`context.runPack` keeps returning the text; the App already calls it from
|
|
168
|
+
`App/src/bridge/commands.ts:642`, and the new page uses it to show a row's actual prompt slice.
|
|
169
|
+
|
|
170
|
+
**App.** A full-width page, not a sidebar tab — the sidebar is ~360–420 px wide, and a 6-column
|
|
171
|
+
accounting sheet needs the main pane:
|
|
172
|
+
|
|
173
|
+
- `ContextAttributionPage` in the main pane, opened from (a) the Context view's header, (b) a
|
|
174
|
+
compact meter in the composer footer, (c) the command palette. It uses the same host pattern as
|
|
175
|
+
agent configuration: a header strip with a close control
|
|
176
|
+
(`App/src/modes/solo/SoloWorkspace.tsx:263-265`).
|
|
177
|
+
- The compact meter reuses `Meter` (`variant="ring"`) from `@xingular/ui` and opens the page.
|
|
178
|
+
- The stack and its legend reuse `SegmentedBar` from `@xingular/ui/ui/charts`, whose `Segment` type
|
|
179
|
+
is already `{ key, label, value, color, display, share, title, recorded }` — exactly this page.
|
|
180
|
+
- A cache strip uses `CacheUsageBadge` and `formatTokens` from
|
|
181
|
+
`App/src/modes/shared/usage/usageModel.ts`.
|
|
182
|
+
|
|
183
|
+
### 4. Sketches
|
|
184
|
+
|
|
185
|
+
A rendered version of these sketches, in the dark theme with the shared tokens, is
|
|
186
|
+
[`outputs/context-attribution-sketch.html`](../../../../outputs/context-attribution-sketch.html)
|
|
187
|
+
(open it in a browser; four tabs, switchable). All three options use one dataset, and the rows add
|
|
188
|
+
to the provider's measured total.
|
|
189
|
+
|
|
190
|
+
**Option A — accounting sheet (Claude Code's shape, this harness's rows).** Recommended.
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
┌─ Context ────────────────────────────────────────────────────────────────────────────────┐
|
|
194
|
+
│ claude-sonnet-4-6 · 1M window · run r-8f21 auto-compact at 983.6K [x] │
|
|
195
|
+
│ │
|
|
196
|
+
│ 183.4K / 1M · 18.3% measured · provider-reported │
|
|
197
|
+
│ ████████▓▓▓▓▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
|
198
|
+
│ ├ foundation 13.1K ┤├ variant 18.4K ┤├ app 9.8K ┤├ conversation 142.1K ┤ │
|
|
199
|
+
│ │
|
|
200
|
+
│ ALWAYS IN CONTEXT TOKENS SHARE OF BUDGET │
|
|
201
|
+
│ v Foundation · harness 13.1K 1.3% │
|
|
202
|
+
│ Harness identity 29 0.0% 29 / 1.0K chars │
|
|
203
|
+
│ Tool guidance 1.9K 0.2% 1.9K / 8.0K chars │
|
|
204
|
+
│ Tool schemas · 34 tools 9.4K 0.9% │
|
|
205
|
+
│ History 3 · Wiki 6 · Native 6 · World 5 · Connections 7 · Other 7 │
|
|
206
|
+
│ Wiki pointer 1.1K 0.1% 1.1K / 2.5K chars │
|
|
207
|
+
│ Reporting rules 712 0.1% 712 / 2.0K chars │
|
|
208
|
+
│ Session state · references 0 0.0% no update this request │
|
|
209
|
+
│ v Variant · c3 · 9a1f 18.4K 1.8% │
|
|
210
|
+
│ Instructions (c1) 8.2K 0.8% 8.2K / 24K chars │
|
|
211
|
+
│ Guardrails (c2) 4.1K 0.4% │
|
|
212
|
+
│ Memory (c3) 3.6K 0.4% │
|
|
213
|
+
│ Tool guidance (c4) 2.5K 0.3% │
|
|
214
|
+
│ v Your application 9.8K 1.0% │
|
|
215
|
+
│ Identity · Role · Working folder 2.4K 0.2% │
|
|
216
|
+
│ Skills index · 12 skills 1.1K 0.1% │
|
|
217
|
+
│ Connected apps · 7 tools 6.3K 0.6% │
|
|
218
|
+
│ v Conversation 142.1K 14.2% measured by subtraction │
|
|
219
|
+
│ Messages and tool results 142.1K 14.2% │
|
|
220
|
+
│ │
|
|
221
|
+
│ RESERVED AND FREE │
|
|
222
|
+
│ Compaction reserve 16.4K 1.6% Pi, auto-compaction │
|
|
223
|
+
│ Free space 800.2K 80.0% │
|
|
224
|
+
│ │
|
|
225
|
+
│ ON DEMAND · not in this request when loaded │
|
|
226
|
+
│ Skills c6 · 12 available ~ 4.2K per skill │
|
|
227
|
+
│ Subagents c5 · 3 available own context each │
|
|
228
|
+
│ │
|
|
229
|
+
│ Rows are estimated from recorded character counts (÷ 4) and reconciled to │
|
|
230
|
+
│ the provider measured 183.4K; Conversation is the remainder, so the rows add │
|
|
231
|
+
│ to the whole. │
|
|
232
|
+
└──────────────────────────────────────────────────────────────────────────────────────────┘
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Option B — ledger plus detail pane.** The same rows on the left; selecting one shows what it is
|
|
236
|
+
on the right: the prompt text, the section's sources and revision, and the actions that apply.
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
┌───────────────────────────────────────────────┬──────────────────────────────────────────┐
|
|
240
|
+
│ CONTEXT ██████████░░░░░░░░░░ 18.3% │ Instructions (c1) │
|
|
241
|
+
│ 183.4K of 1M · sonnet-4-6 · 34 tools │ ───────────────────────────────────── │
|
|
242
|
+
│ │ 8.2K tokens · 0.8% of the window │
|
|
243
|
+
│FOUNDATION 13.1K 1.3%│ always in context · 8.2K / 24K chars │
|
|
244
|
+
│ Harness identity 29 0.0%│ variant c3 · fingerprint 9a1f │
|
|
245
|
+
│ Tool schemas 9.4K 0.9%│ source systemprompt.md │
|
|
246
|
+
│ Tool guidance 1.9K 0.2%│ +----------------------------------+ │
|
|
247
|
+
│ Wiki pointer 1.1K 0.1%│ | You are the person's assistant, | │
|
|
248
|
+
│ Reporting 712 0.1%│ | working with them directly in | │
|
|
249
|
+
│VARIANT · c3 18.4K 1.8%│ | this session: research, writing | │
|
|
250
|
+
│ Instructions c1 8.2K 0.8%│ | and coding in its working folder | │
|
|
251
|
+
│ Guardrails c2 4.1K 0.4%│ +----------------------------------+ │
|
|
252
|
+
│ Memory c3 3.6K 0.4%│ [Open in Harness] [Refine this] │
|
|
253
|
+
│ Tool guidance c4 2.5K 0.3%│ │
|
|
254
|
+
│YOUR APPLICATION 9.8K 1.0%│ │
|
|
255
|
+
│CONVERSATION 142.1K 14.2%│ │
|
|
256
|
+
│RESERVED 16.4K 1.6%│ │
|
|
257
|
+
│FREE 800.2K 80.0%│ │
|
|
258
|
+
└───────────────────────────────────────────────┴──────────────────────────────────────────┘
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Option C — growth over the session.** A second tab for the same page: the stack at each request,
|
|
262
|
+
so "what grew" is visible. Compaction markers come from `compaction.ended` (`tokensBefore`,
|
|
263
|
+
`tokensAfter`), which the run already records.
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
┌ Growth ───────────────────────────────────────────────────────────────────────────────────┐
|
|
267
|
+
│ 1M ┤│
|
|
268
|
+
│ │ ╭── 44.2K│
|
|
269
|
+
│ 20% ┤ ╭──╮ ╭──╮ ╭───╮ ╭────╮ ╭─────╯│
|
|
270
|
+
│ │ ╭──╮ │ │ │ │ │ │ │ │ │ │
|
|
271
|
+
│ 0% ┼──────┴──┴────┴──┴───┴──┴───┴───┴───┴────┴───┴─────┬─────────────── requests →│
|
|
272
|
+
│ r1 r2 r3 r4 r5 r6 r7 r8 ⧗ compacted 112K → 31K│
|
|
273
|
+
│ │
|
|
274
|
+
│ foundation ▓ variant ▒ application ░ conversation █ each segment a group│
|
|
275
|
+
└──────────────────────────────────────────────────────────────────────────────────────────┘
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**The compact meter** — one always-visible readout, everywhere, opening the page:
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
App composer footer Terminal status band
|
|
282
|
+
┌────────────────────────────────────┐ ~/project (main) · account · sonnet-4-6
|
|
283
|
+
│ ▸ model sonnet-4-6 ◔ 18% ↑ ⏎ │ ↑12.1k ↓3.4k R84.9k 18.3%/1M
|
|
284
|
+
└────────────────────────────────────┘ └─ click / /context ─────────────────
|
|
285
|
+
ring colour: 18% data-1 · 80% warning · 95% destructive
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**The terminal page** (`/context`), for reference:
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
Context sonnet-4-6 · 1M 18.3% used · measured
|
|
292
|
+
████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
293
|
+
foundation 13.1K 1.3% ██
|
|
294
|
+
variant 18.4K 1.8% █
|
|
295
|
+
your app 9.8K 1.0% ▌
|
|
296
|
+
conversation 142.1K 14.2% ██████
|
|
297
|
+
reserved 16.4K 1.6% █
|
|
298
|
+
free 800.2K 80.0% (dim)
|
|
299
|
+
j/k move · enter open · v variant · c compact · r refresh · q close
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### 5. Honesty rules the view must keep
|
|
303
|
+
|
|
304
|
+
Taken from the mistakes of the harnesses above, and from `INV-RUN-7`:
|
|
305
|
+
|
|
306
|
+
1. The header number is the provider's, or the word `estimate`.
|
|
307
|
+
2. `Conversation` is a subtraction, never a guess, and is labelled `measured by subtraction`.
|
|
308
|
+
3. Free space is `window − measured − reserve`, never `window − listed rows` — the Claude Code #94436
|
|
309
|
+
report is exactly that bug.
|
|
310
|
+
4. A row that could not be measured is `not recorded`, not `0`.
|
|
311
|
+
5. An unknown window is `/?`, never `0%`.
|
|
312
|
+
6. Rows are `approx` in the copy, and the page states the ratio once.
|
|
313
|
+
7. No number is recomputed from a provider's figure in a way that changes its meaning; a scaled row
|
|
314
|
+
is marked scaled.
|
|
315
|
+
|
|
316
|
+
### 6. What this proposal does not do
|
|
317
|
+
|
|
318
|
+
- No tokenizer. `chars / 4` is the house constant; a real tokenizer would need the provider's
|
|
319
|
+
encoding per model and would still not reconcile with the measured total.
|
|
320
|
+
- No new storage. Everything is read from files the harness already writes, plus the rendered sizes
|
|
321
|
+
added to the pack (`INV-PRM-6`, `INV-MEM-5` untouched).
|
|
322
|
+
- No new writer for run records or the ledger.
|
|
323
|
+
- No cost attribution per row in the first surface. Cost per request exists (`pricing.ts`) and can
|
|
324
|
+
be added as a column later without changing the model.
|
|
325
|
+
|
|
326
|
+
## Alternatives
|
|
327
|
+
|
|
328
|
+
- **Do nothing.** The App already shows a pack's character counts and one meter. The gap is the
|
|
329
|
+
share of the window per contributor, the free space, and the variant's always-on cost — the three
|
|
330
|
+
questions a person actually asks.
|
|
331
|
+
- **A tab in the existing sidebar Context view.** Rejected: the sidebar is too narrow for a six
|
|
332
|
+
column table, and the request is explicitly a full-width page.
|
|
333
|
+
- **Client-side computation in the App.** Rejected by `INV-PAR-1`: the terminal needs the same rows,
|
|
334
|
+
and two implementations of the subtraction rule would drift.
|
|
335
|
+
- **Reuse `usage.summary`.** Rejected: that is a period aggregate over the ledger. Attribution is
|
|
336
|
+
one request's composition, and `UsageTotals` has no context fields.
|
|
337
|
+
- **Full tokenizer.** Rejected: see above; it would also make the view disagree with Pi's own
|
|
338
|
+
footer, which is the number a person already sees.
|
|
339
|
+
|
|
340
|
+
## Consequences
|
|
341
|
+
|
|
342
|
+
- **Applications.** One new method, additive. A host that shows the page reads
|
|
343
|
+
`context.attribution`; a host that does not is unaffected.
|
|
344
|
+
- **Stored data.** `runs/<id>/context.json` gains a `rendered` size per section (and per source).
|
|
345
|
+
Older packs lack it; the view falls back to `size` and marks the section `approximate`.
|
|
346
|
+
- **Protocol.** One new method name and result shape; no `PROTOCOL_VERSION` change.
|
|
347
|
+
- **Spec.** `04-prompt.md` gains the recorded rendered sizes; `11-terminal.md` gains `/context`;
|
|
348
|
+
`14-surfaces-and-parity.md` and `parity.md` gain the row.
|
|
349
|
+
|
|
350
|
+
## Migration
|
|
351
|
+
|
|
352
|
+
Nothing to migrate. Packs recorded before this change stay readable; the only difference is the
|
|
353
|
+
`approximate` marker on rows whose rendered size was not recorded. The capability can be removed by
|
|
354
|
+
deleting the page, the method and the extra pack field; no data depends on it.
|
|
355
|
+
|
|
356
|
+
## Checks
|
|
357
|
+
|
|
358
|
+
- Harness: `npm run typecheck`, `npm test`, `npm run prove`.
|
|
359
|
+
- New unit tests: the reconciliation (rows sum to `contextTokens`), the negative-prefix case, a pack
|
|
360
|
+
with no `usage.updated`, a pack with no `delivery`, and a run with unknown `contextWindow`.
|
|
361
|
+
- Terminal: a page test in the style of `usagePage.test.ts`.
|
|
362
|
+
- App: `npm run typecheck`, `npm test`, `npm run check:layers`, `npm run check:ui`, and
|
|
363
|
+
`cd src-tauri && cargo test`.
|
|
364
|
+
- A visual check of both surfaces against one real run, comparing the header figure with the
|
|
365
|
+
terminal status band and with Pi's own footer.
|
|
366
|
+
|
|
367
|
+
## Open questions
|
|
368
|
+
|
|
369
|
+
1. **Placement.** Full-width page in the main pane (recommended), a new workspace view tab that
|
|
370
|
+
renders full width, or a tab beside `Usage` in the sidebar? The sketches assume the first.
|
|
371
|
+
2. **Shape.** Option A (accounting sheet), A plus Option B's detail pane on wide windows, or all
|
|
372
|
+
three as tabs of one page?
|
|
373
|
+
3. **Rows.** Group by owner (foundation / variant / application / conversation), as proposed, or by
|
|
374
|
+
kind (`System prompt`, `Tools`, `Skills`, `Memory`, `Messages`) as Claude Code does? Owner
|
|
375
|
+
grouping is what makes the variant actionable; kind grouping is what a person coming from Claude
|
|
376
|
+
Code expects.
|
|
377
|
+
4. **Cost per row.** Show `cost per request` beside tokens from the first version, or keep the view
|
|
378
|
+
token-only?
|
|
379
|
+
5. **Live updates.** Refresh on every `usage.updated` while a reply streams, or on open and on
|
|
380
|
+
`completed` only?
|
|
381
|
+
6. **The compact meter.** Add the ring to the App composer footer, the terminal status band, or
|
|
382
|
+
both? Pi's footer already shows `45.3%/200K` and this harness's band shows neither.
|
|
@@ -78,6 +78,7 @@ it to the index and update its status as the work progresses.
|
|
|
78
78
|
| [PROP-0046](PROP-0046-progress-aware-stall-guards.md) | Progress-aware stall guards and bounded plan research children | Implemented; ADR-0039 | INV-RUN-13, 02-sessions-and-runs.md, PROP-0041, terminal-tui.md, ADR-0039 | 2026-10-27 |
|
|
79
79
|
| [PROP-0047](PROP-0047-plugins-hub-separation.md) | Separate local plugin management from Community Hub discovery | Implemented; ADR-0041 | INV-MCP-4, INV-TUI-2/4, INV-PAR-1/2, 06, 09, 10, 11, 14, terminal-tui.md, parity.md | 2026-10-27 |
|
|
80
80
|
| [PROP-0048](PROP-0048-simple-annotate.md) | One annotate page, two actions, and easy app attach | Implemented; ADR-0044 | PROP-0018, 07, 10, 11, 13, terminal-tui.md, parity.md | 2026-10-27 |
|
|
81
|
+
| [PROP-0049](PROP-0049-context-attribution.md) | Context attribution: what fills the context window, by foundation, variant and application | Proposed | INV-PRM-6, INV-SRV-1, INV-PAR-1/2, INV-RUN-7, 04, 10, 11, 14, parity.md | 2026-10-28 |
|
|
81
82
|
|
|
82
83
|
Numbers are four digits, start at `0001`, and are never reused.
|
|
83
84
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ADR-0048
|
|
3
|
+
title: A skill named anywhere in a message is required for that request
|
|
4
|
+
status: Accepted
|
|
5
|
+
created: 2026-11-20
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# ADR-0048: A skill named anywhere in a message is required for that request
|
|
9
|
+
|
|
10
|
+
## Decision
|
|
11
|
+
|
|
12
|
+
- `$name` is a skill invocation at any word start in a message, not only as its first token. The
|
|
13
|
+
composer's palette opens at the caret and replaces only the token being typed; the rest of the
|
|
14
|
+
draft and its caret stay in place.
|
|
15
|
+
- Every `$name` that names a skill the session can load makes that skill required for the request.
|
|
16
|
+
The named skills travel as `Launch.requiredSkills`, and the prompt compiler renders them as the
|
|
17
|
+
dynamic `required-skills` section: the skill's text from the pinned variant, the directive to
|
|
18
|
+
follow it, and an honest line for a name the variant does not have. The shipped
|
|
19
|
+
`plugin-authoring` skill fills in when the variant has none.
|
|
20
|
+
- The section belongs to the request. `sessionLaunch` resends it like `references`, even when it is
|
|
21
|
+
identical to the previous snapshot.
|
|
22
|
+
- The terminal builds the same text with `requiredSkillsText` from Pi's loaded skills and appends it
|
|
23
|
+
as a hidden `xingular-required-skills` custom message. It no longer rewrites a leading `$name` to
|
|
24
|
+
`/skill:name`; the person's message reaches the model unchanged and the requirement is enforced at
|
|
25
|
+
every position.
|
|
26
|
+
- A required skill changes no tool and no permission: the run's callable set and the gate are
|
|
27
|
+
untouched. It is a prompt-level requirement, not a capability narrowing.
|
|
28
|
+
|
|
29
|
+
## Rationale
|
|
30
|
+
|
|
31
|
+
Pi expands only a leading `/skill:name`, so a mid-message skill was plain text and had no effect.
|
|
32
|
+
Moving the requirement into the request-scoped prompt text makes the same promise at every position,
|
|
33
|
+
keeps the stable prefix (and therefore the provider cache) intact, and gives the terminal and a host
|
|
34
|
+
one shared wording through `requiredSkillsText`.
|
|
35
|
+
|
|
36
|
+
## Consequences
|
|
37
|
+
|
|
38
|
+
A leading `$name` no longer depends on Pi's `/skill:name` expansion. The skill's text is delivered
|
|
39
|
+
once per request that names it, so repeating the same skill in several turns repeats its text in
|
|
40
|
+
native history; the model sees the most recent copy. The App's composer already inserts `$` chips
|
|
41
|
+
anywhere; its reference path now sends skills as `requiredSkills` rather than as pinned notes.
|
|
42
|
+
Skills that a variant does not contain are reported as unavailable rather than silently dropped.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ADR-0050: Approved-window screenshot contract
|
|
2
|
+
|
|
3
|
+
## Decision
|
|
4
|
+
|
|
5
|
+
A host may supply `computer_screenshot` through the existing tool-provider extension point using
|
|
6
|
+
the Harness contract in `src/computer/screenshot.ts`. The backend must capture one currently
|
|
7
|
+
approved window and exclude the desktop, other windows and overlays, including overlapping ones.
|
|
8
|
+
If it cannot guarantee that scope it refuses without returning pixels; it never silently captures a
|
|
9
|
+
display instead. The adapter is opt-in, goes through the ordinary tool gate, and does not make
|
|
10
|
+
capture plan-mode safe. A host that also offers coordinate-based interactions interprets their
|
|
11
|
+
coordinates relative to the returned image pixels.
|
|
12
|
+
|
|
13
|
+
## Consequences
|
|
14
|
+
|
|
15
|
+
Browser screenshots remain a separate capability with their own tab isolation. The contract cannot
|
|
16
|
+
verify native backend isolation; each host must implement and test it. The existing macOS display
|
|
17
|
+
capture has not adopted this contract and is not evidence of approved-window isolation. The
|
|
18
|
+
standalone terminal has no such backend. The contract adds no automatic provider registration or
|
|
19
|
+
new extension point.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# ADR-0051: Revisioned terminal decision waits
|
|
2
|
+
|
|
3
|
+
The terminal needs timely approval and question updates while avoiding a control request every
|
|
4
|
+
100 ms in every idle session. The controller owns the pending inbox, so it exposes a private,
|
|
5
|
+
control-key-protected revisioned snapshot with a bounded wait. Every inbox mutation wakes waiters;
|
|
6
|
+
the frontend resynchronizes on reconnect. The existing immediate snapshot remains available.
|
|
7
|
+
|
|
8
|
+
The wait uses a separate socket request, so it does not block other terminal control calls.
|
|
9
|
+
Waits time out after 20 seconds and are woken on controller disposal. Client shutdown cancels its
|
|
10
|
+
request. No persisted state or public server protocol changes.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# ADR-0052: Per-profile and total subagent budgets
|
|
2
|
+
|
|
3
|
+
A run could start any number of children: `maxDepth` bounded nesting only. Run `90906235`
|
|
4
|
+
("Implement the plan: Safe screenshot capability", 2026-10-28) started 11 children in nine
|
|
5
|
+
minutes for a five-step plan — three spawns before the parent had read a file, five children that
|
|
6
|
+
returned no change, and two tasks retried as fresh children. The shape of delegation was left to
|
|
7
|
+
the prompt.
|
|
8
|
+
|
|
9
|
+
A run now starts only a bounded number of distinct child conversations. Foundation defaults: four
|
|
10
|
+
children in total, two `worker`, three `explorer`, one of any other named profile and one with no
|
|
11
|
+
profile. A host overrides them through `SessionRunInput.subagents` (`maxChildren`, `maxByProfile`,
|
|
12
|
+
`maxOther`, `maxUnprofiled`), so the terminal and the App receive the same defaults; every value
|
|
13
|
+
is an integer clamped to 0–50.
|
|
14
|
+
|
|
15
|
+
`Subagents.spawn` counts the run's recorded children in `subagents.json`, the children of the
|
|
16
|
+
parent runs it was continued from (`resumedFromRunId`), and slots reserved by concurrent calls. It
|
|
17
|
+
deduplicates a continuation to its first link, so continuing a stopped child uses no new slot,
|
|
18
|
+
while a child whose startup failed still counts. A refused spawn returns an `invalid_request`
|
|
19
|
+
tool result telling the model to do the work itself; it creates no run. `continue` bypasses the
|
|
20
|
+
check because it is the same child.
|
|
21
|
+
|
|
22
|
+
The budget is per run, not per conversation, so a long chat is not starved. It lives in the
|
|
23
|
+
foundation because it bounds what every application built on the harness may spend, and changing
|
|
24
|
+
it needs a release. A host that legitimately needs more children raises the limits for its
|
|
25
|
+
session; the spawn tool's description states the effective numbers.
|
package/docs/parity.md
CHANGED
|
@@ -30,19 +30,19 @@ implemented is not a parity claim; acceptance is tracked per command.
|
|
|
30
30
|
| Approvals and standing approvals | `sessions/approvals.ts` | `approvals.*` | decision dock, `/permissions` | approvals dock, settings | parity | — |
|
|
31
31
|
| Opening a link | `core/links.ts` | `links.open`; `HarnessOptions.linkOpener` (ADR-0047) | hover shows the hand pointer and recolours the link, Ctrl+click (the primary click on macOS, whose ⌘ the mouse protocol cannot report) opens web, `mailto:`, file and folder targets with the system handler, a refused link posts a notice | ⌘-click on macOS and Ctrl+click elsewhere opens with the system handler, a plain click keeps the sidebar and falls back to the system for what it cannot show, `a:hover` uses `--link-hover` | implemented; acceptance not certified | — |
|
|
32
32
|
| Questions (`ask_user`) | `tools/askUser.ts` | shared `ask_user` tool | plan-mode dock: strict aligned columns, bounded body scroll, 80×24 source sketch viewport; connected structural projection with high-confidence boundary normalization (PROP-0026, PROP-0028) | plan-mode form accepts the same 80×24 ASCII source contract with literal preview and accessible overflow; no typography change required | parity; terminal projection verified in isolated fixtures | — |
|
|
33
|
-
| Subagents | `sessions/subagentService.ts` | `spawn_subagent` (requires `title`, PROP-0030; `continue` resumes a stopped child, ADR-0043), `subagents.*`; plan mode allows only named eligible read-only research profiles | `/subagents` page with the `main` row pinned first, and live rows under the status line (mark, profile, parent-set title, `n of m tasks`), both rebuilt from saved runs when a conversation reopens; plan-mode research delegation uses the same Harness gate | reply trajectory; plan-mode research delegation uses the same Harness gate | parity | — |
|
|
33
|
+
| Subagents | `sessions/subagentService.ts` | `spawn_subagent` (requires `title`, PROP-0030; `continue` resumes a stopped child, ADR-0043), `subagents.*`; plan mode allows only named eligible read-only research profiles; per-profile and total child budgets enforced in `Subagents.spawn` (ADR-0052), with the same refusal result in both surfaces | `/subagents` page with the `main` row pinned first, and live rows under the status line (mark, profile, parent-set title, `n of m tasks`), both rebuilt from saved runs when a conversation reopens; plan-mode research delegation uses the same Harness gate | reply trajectory; plan-mode research delegation uses the same Harness gate | parity | — |
|
|
34
34
|
| Agent profiles | `agents/profiles.ts` | `spawn_subagent { agent }`, `agents.list/get/create/remove`; terminal `terminal.agents.*`; package profiles through `Loading.profilePaths` | `/agents` page: browse, create, edit in `$EDITOR`, copy, delete; package profiles listed under "From a plugin" and switched on `/plugins` | Solo `/agents` lists profiles; profile details and management are not wired | partial | PAR-010 |
|
|
35
35
|
| Run todo list | `tools/todo.ts` | `todo_write`, `runs.todo` (`runs.checklist` deprecated); terminal `todo` telemetry | registered in terminal sessions and their subagents; each write shows as a todo block in the transcript; `/todo` shows the current list; a subagent row shows its `n of m tasks` | `/todo` shows the current list on demand; each write is not rendered inline | terminal done; App shows it on demand | PAR-001 |
|
|
36
36
|
| Continuation of a stopped run | `sessions/continuation.ts` | `continueFrom`, `runs.continuation` | not offered (`/resume` reopens history only) | not offered | planned | PAR-003 |
|
|
37
37
|
| Bounded replies and checkpoints (PROP-0041) | `sessions/workSlice.ts`, `tools/checkpoint.ts`, `sessions/runner.ts` | `checkpoint_write`, `workSlice`, `runs.continuation.checkpoint`, `checkpoint`/`heartbeat` events | guard on every terminal reply with the shared tool; the handoff is recorded on the run; no checkpoint card or slice control yet | not wired | implemented; terminal card, App presentation and `/slices` open | PAR-004 |
|
|
38
38
|
| Accounts | `accounts/` | `accounts.*` | `/accounts` | Accounts | parity | — |
|
|
39
39
|
| Models and effort | `accounts/choices.ts` | — | `/model`, `/effort` | composer pickers | parity | — |
|
|
40
|
-
| Skills | `plugins/`, `tools/dispatch.ts` | `skill_load` | `$name
|
|
40
|
+
| Skills | `plugins/`, `tools/dispatch.ts`, `prompt/compile.ts` | `skill_load`; `Launch.requiredSkills` | `$name` at any word start (required for that request), `/skill:name`, browser Skills tab | composer `$` chip with `requiredSkills`; Capability | implemented; acceptance not recertified | — |
|
|
41
41
|
| Plugins and Hub | `plugins/`, `hub/` | `plugins.*`, `hub.*` | full-screen `/plugins` local manager; separate full-screen `/hub` Community catalog with bounded registry/document Preview (no Pi-package Preview) | local Plugins manager; Community Hub with payload Preview for non-package entries | parity | — |
|
|
42
42
|
| Plugin authoring and proposal review | `plugins/proposals.ts`, `tools/defaultSkills.ts` | `plugin_propose`, `plugins.proposals.*` | `/plugins` Proposals tab: preview, accept or reject | structured model tool and skill; review UI not implemented | partial; App review pending | PAR-017 |
|
|
43
43
|
| Connected apps (MCP) | `mcp/` | `connections.*`, broker tools | resource browser Connections tab | Connections | parity | — |
|
|
44
44
|
| Web search and page reading | `web/` | `web_search`, `web_fetch` | world tools | world tools | parity | — |
|
|
45
|
-
| Isolated browser | `browser/` | shared `browser_*` schemas | isolated world tools, headed on request, `/settings` Browser tab | native WebKit: navigate, tabs, visible-text read and page scroll without refs; unsupported operations refuse explicitly | partial; native backend gap | PAR-015 |
|
|
45
|
+
| Isolated browser | `browser/` | shared `browser_*` schemas | isolated world tools, headed on request, `/settings` Browser tab | native WebKit: navigate, tabs, visible-text read, page screenshot and page scroll without refs; unsupported operations refuse explicitly; live screenshot acceptance pending | partial; native backend gap | PAR-015 |
|
|
46
46
|
| Web annotations | `annotations/` (PROP-0017, PROP-0018, PROP-0038, PROP-0048) | `annotations.*`, `annotations.capture.*`, `page_notes_*` | `/annotate:` page (Annotate and All annotations tabs; DevTools app list and launch), `/annotate:notes`, `/annotate:submit`, `/annotate:clear` | website tab Annotate panel (inspector, queue to the focused thread, app windows through `annotations.capture.*`) | App behind PROP-0048 (queue methods removed); native WKWebView gestures unaccepted | PAR-011 |
|
|
47
47
|
| Managed worktrees | `worktrees/` (PROP-0016) | `worktrees.*` | `--worktree`, `/worktree`, `xagent worktree` | Solo `/worktree` lists records only; Solo starts sessions in the project folder and has no worktree level | partial | PAR-012 |
|
|
48
48
|
| Wiki notes (model tools) | `wiki/`, `tools/` | `wiki_*` | `Terminals.ownTools` registers Wiki-read tools through the session socket; `xagent-wiki` remains a person-facing shell utility | structured Harness tools | implemented on both; acceptance not recertified | PAR-002: acceptance not recertified |
|
|
@@ -56,7 +56,7 @@ implemented is not a parity claim; acceptance is tracked per command.
|
|
|
56
56
|
| Provider limits | `usage/limits/` (PROP-0042) | `usage.limits` | `/usage:limit` (current account), `/usage:accounts` (all accounts) | not wired | terminal implemented; host planned | PAR-016 |
|
|
57
57
|
| Session catalog (sessions per folder) | `sessions/` saved-history controls; shared catalog not implemented | `sessions.*` controls; no cross-surface catalog method | `/session` selects native Pi histories in the current folder | `/session` reads `bridge.agent.threads(SOLO_ID)`, maps each thread to its project folder, and filters by current effective account; `all` broadens only this App list | planned; catalogs differ | PAR-008 |
|
|
58
58
|
| Folder choice and recent folders | project/worktree selection remains surface-specific | launch `cwd`, `--worktree`, `/worktree` | starts in launch folder; explicit worktree commands | Solo sidebar is Project › Session; new sessions use the project's folder, with no folder/worktree level | planned; no shared recent-folder picker | PAR-009 |
|
|
59
|
-
| Computer control |
|
|
59
|
+
| Computer control | `computer/screenshot.ts` defines an opt-in approved-window screenshot contract; other tools remain host-owned | `computer_*` (host tools) | no computer screenshot backend | macOS bridge (host-backed); its display capture has not adopted the approved-window contract | partial; screenshot contract not integrated | PAR-007 |
|
|
60
60
|
| Shared files vault | — | `files_*` (host tools) | none | Files | host only | PAR-007 |
|
|
61
61
|
|
|
62
62
|
## Core slash-command surfaces (PROP-0040)
|
|
@@ -125,7 +125,7 @@ or live-model sketch quality.
|
|
|
125
125
|
| PAR-004 | Refinement and adoption only in the App | Variant tab actions: start, candidates, adopt, trust. |
|
|
126
126
|
| PAR-005 | Wiki browsing only in the App | A `/wiki` page over `wiki.*`. |
|
|
127
127
|
| PAR-006 | Acceptance of terminal first-party hooks | Source gap resolved: enabled hooks are returned by protected `firstParty.list` and loaded by `terminal/piExtension.ts`. Acceptance has not been recertified in this documentation pass. |
|
|
128
|
-
| PAR-007 | Host tools without a Harness
|
|
128
|
+
| PAR-007 | Host tools without a complete Harness surface | The opt-in screenshot contract exists, but terminal availability reporting and a conforming host backend are not wired. The current macOS host captures a display, not an isolated approved window. The remaining computer controls are host-owned; the files vault becomes a Harness capability or is dropped. |
|
|
129
129
|
| PAR-008 | Separate session catalogs | Proposal approved; shared catalog is not implemented. Terminal `/session` selects native Pi history in the current folder. App `/session` reads Solo `agent.threads`, maps threads through Solo project groups, and filters to the current effective account and folder; `/session all` broadens only within that App catalog. It cannot open an arbitrary native session path. No cross-surface catalog or visibility guarantee exists. |
|
|
130
130
|
| PAR-009 | Shared folder/recent-project choice | Terminal starts from its launch `cwd` and can switch through its worktree surface. App Solo uses Project › Session and starts new sessions in the selected project's folder; it has no folder/worktree level or shared recent-folder picker. |
|
|
131
131
|
| PAR-010 | Agent profile management in Solo | App Solo `/agents` can list profiles. Although the renderer allowlist includes `agents.get`, Solo does not wrap it; `agents.create/remove` are not exposed there. Profile details and management actions are unavailable, unlike terminal `/agents`. |
|
|
@@ -134,4 +134,4 @@ or live-model sketch quality.
|
|
|
134
134
|
| PAR-013 | Expanded question wireframes and terminal layout | Closed by [PROP-0025 verification](evidence/terminal-question-wireframes/README.md): four-size production PTY acceptance covers aligned compact columns, body scrolling, pinned controls, Shift+arrow sketch panning and live resizing. Host contracts accept 80×24 literal ASCII sketches (1,943 bytes); six isolated browser tests include exact-boundary content, accessible overflow and unchanged selection/answers. This is controlled fixture acceptance, not live-provider or native Tauri evidence. |
|
|
135
135
|
| PAR-014 | Connected terminal sketch display | Closed by [PROP-0026 verification](evidence/terminal-connected-sketches/README.md) and extended by [PROP-0028 verification](evidence/terminal-wireframe-normalization/README.md): production PTY fixtures cover connected corners/tees, title-bearing rules, missing and whitespace-shifted side edges, internal table widths, frame-attached dividers, resize/panning and unchanged raw persistence/answers. Harness and host checks passed. Host literal rendering remains permitted; no host implementation change was required. This is fixture verification, not an every-font or live-model guarantee. |
|
|
136
136
|
| PAR-016 | Usage analytics and provider limits in the App | The terminal has `/usage:analysis` and `/usage:limit` over `usage.*`. The App presents the same capability: an analytics view and a per-account provider limit view. |
|
|
137
|
-
| PAR-015 | Native WebKit browser-tool coverage | The shared browser schema is wider than the native backend. Native WebKit supports navigation, tabs, visible-text reads and scrolling without refs. Accessibility snapshots, snapshot-ref interactions (including click/type/select/ref-scroll),
|
|
137
|
+
| PAR-015 | Native WebKit browser-tool coverage | The shared browser schema is wider than the native backend. Native WebKit supports navigation, tabs, visible-text reads, page screenshots and scrolling without refs; live screenshot acceptance is pending. Accessibility snapshots, snapshot-ref interactions (including click/type/select/ref-scroll/ref-screenshot), waits and keyboard presses return an explicit `browser_unsupported` refusal before effects; there is no automatic switch to the isolated Harness browser. See page 13 and the native backend's `surfaces` note. |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Xingular Harness 1.3.1-unstable.3
|
|
2
|
+
|
|
3
|
+
Unstable preview of the current Harness work. Install with `npm install @xingular/harness@unstable`.
|
|
4
|
+
|
|
5
|
+
- Name a skill anywhere in a message: `$name` at any word start makes that skill required for the
|
|
6
|
+
request (ADR-0048). `Launch.requiredSkills` carries it, and the compiler renders a
|
|
7
|
+
`required-skills` section at order 52 from the pinned variant through `readSkillFile`, bounded at
|
|
8
|
+
12,000 characters. An unavailable skill is named honestly instead of invented, and the built-in
|
|
9
|
+
`plugin-authoring` skill stands in when the variant ships no skills. The section belongs to the
|
|
10
|
+
request, so an identical one is delivered again rather than deduplicated. The terminal builds the
|
|
11
|
+
same text with `requiredSkillsText` and appends it as a hidden `xingular-required-skills` message,
|
|
12
|
+
so the person's own wording reaches the model unchanged. In the composer the `$` palette opens at
|
|
13
|
+
the caret and inserts there, keeping the rest of the draft.
|
|
14
|
+
- Bound delegation per run and per profile (ADR-0052). A run now starts at most four distinct child
|
|
15
|
+
conversations: two `worker`, three `explorer`, one of any other named profile and one with no
|
|
16
|
+
profile. `SessionRunInput.subagents` / the SDK's `Subagents` options override them with
|
|
17
|
+
`maxChildren`, `maxByProfile`, `maxOther` and `maxUnprofiled` (clamped to 0–50), and the model's
|
|
18
|
+
`spawn_subagent` description states the budget it has left. `Subagents.spawn` refuses a child past
|
|
19
|
+
a limit with `invalid_request` and starts no run; continuing a stopped child costs no slot, and a
|
|
20
|
+
work-slice continuation keeps the parent's budget.
|
|
21
|
+
- Read what a request was made of. `context.attribution` returns one row per contributor with its
|
|
22
|
+
estimated tokens and share of the window, reconciled to the provider's measured total, and one
|
|
23
|
+
row's text on request; the terminal reaches it through `terminal.context`. The compiler now
|
|
24
|
+
records where its characters went: `renderSized` returns each section's size and the framing that
|
|
25
|
+
belongs to no section, `Delivery.sizes` stores them on the recorded pack, and `Source.chars`
|
|
26
|
+
attributes a variant's tool guidance inside the tools section. `context.snapshot` also records the
|
|
27
|
+
compaction headroom the run was held to (`compaction: { enabled, reserveTokens }`) from Pi's own
|
|
28
|
+
settings. The terminal page over this data arrives in a later preview; no `/context` command is
|
|
29
|
+
registered yet.
|
|
30
|
+
- Add an opt-in approved-window screenshot contract (ADR-0050). `src/computer/screenshot.ts` defines
|
|
31
|
+
`computer_screenshot` for a host-supplied backend: it captures exactly one currently approved
|
|
32
|
+
window, excludes the desktop, other windows and overlapping overlays, and refuses with
|
|
33
|
+
`COMPUTER_SCREENSHOT_UNAVAILABLE` rather than returning display pixels. It goes through the
|
|
34
|
+
ordinary tool gate, is not plan-mode safe, and adds no extension point. The standalone terminal
|
|
35
|
+
has no backend, and the existing macOS host capture has not adopted this contract.
|
|
36
|
+
- Deliver approvals and questions promptly without polling: the control-key-protected
|
|
37
|
+
`terminal.pending.wait` returns as soon as the pending revision changes and otherwise waits at most
|
|
38
|
+
20 s, waking on additions, removals and disposal (ADR-0051). `terminal.pending` is unchanged and
|
|
39
|
+
the server protocol is untouched.
|
|
40
|
+
- Swap the OpenTUI send keys back. `Return` sends, or queues a follow-up behind a running reply, and
|
|
41
|
+
`Cmd+Return`, `Ctrl+Return` or `Alt+Return` steers that reply. The `app.message.followUp` action is
|
|
42
|
+
`app.message.steer` again, so a `keybindings.json` entry under the newer name needs renaming.
|
|
43
|
+
- Keep a stray mouse report out of input. A report split across the input parser's timeout no longer
|
|
44
|
+
types its `<button;column;row>` tail into the composer or a single-line field, so a scroll cannot
|
|
45
|
+
become a run's prompt or a filter; typing and pastes are untouched and `/trajectory` no longer
|
|
46
|
+
prints a recorded report.
|
|
47
|
+
|
|
48
|
+
## Upgrading
|
|
49
|
+
|
|
50
|
+
- A leading `$name` is no longer rewritten to Pi's `/skill:name`. An embedding host sends required
|
|
51
|
+
skills as `Launch.requiredSkills`, and the message text reaches the model as written.
|
|
52
|
+
- Runs that spawned children freely now stop at the defaults above; set `subagents` explicitly to
|
|
53
|
+
change them.
|
|
54
|
+
- A `keybindings.json` entry named `app.message.followUp` is now `app.message.steer`.
|