@xingular/harness 0.1.6 → 2.0.0
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 +25 -0
- package/NOTICE.md +38 -0
- package/README.md +139 -28
- package/dist/cli.js +9598 -2201
- package/dist/terminal-pi.js +808 -16
- package/docs/README.md +15 -0
- package/docs/decisions/ADR-0001-owned-terminal.md +87 -0
- package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
- package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +68 -0
- package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +49 -0
- package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
- package/docs/terminal-tui.md +344 -0
- package/package.json +11 -4
- package/src/accounts/accounts.test.ts +197 -1
- package/src/accounts/accounts.ts +266 -28
- package/src/cli.ts +7 -3
- package/src/core/bus.test.ts +22 -0
- package/src/core/bus.ts +17 -2
- package/src/harness.ts +27 -4
- package/src/hub/catalog.json +123 -0
- package/src/hub/cli.ts +59 -0
- package/src/hub/hub.test.ts +205 -0
- package/src/hub/hub.ts +256 -0
- package/src/hub/schema.ts +81 -0
- package/src/index.ts +12 -2
- package/src/mcp/connections.ts +39 -12
- package/src/mcp/service.test.ts +49 -3
- package/src/mcp/service.ts +14 -3
- package/src/mcp/types.ts +10 -0
- package/src/memory/api.ts +16 -2
- package/src/memory/blocks.ts +3 -1
- package/src/memory/caller.ts +13 -1
- package/src/memory/curation.test.ts +206 -0
- package/src/memory/curation.ts +64 -0
- package/src/memory/dispatch.ts +5 -0
- package/src/memory/maintenance.test.ts +17 -1
- package/src/memory/maintenance.ts +367 -18
- package/src/memory/operations.ts +12 -0
- package/src/memory/proposals.ts +16 -8
- package/src/memory/recall.test.ts +11 -0
- package/src/memory/recall.ts +11 -3
- package/src/memory/trajectory.test.ts +178 -0
- package/src/memory/trajectory.ts +220 -0
- package/src/memory/wiki.ts +2 -1
- package/src/memory/wikiIndex.ts +3 -2
- package/src/plugins/builtin.ts +4 -2
- package/src/plugins/catalogue.test.ts +1 -1
- package/src/server/methods.ts +43 -2
- package/src/server/server.test.ts +20 -1
- package/src/sessions/approvals.test.ts +49 -0
- package/src/sessions/approvals.ts +26 -4
- package/src/sessions/contracts.ts +29 -3
- package/src/sessions/conversation.test.ts +83 -0
- package/src/sessions/lineageJoin.ts +1 -1
- package/src/sessions/policy.ts +4 -0
- package/src/sessions/questionBatches.test.ts +110 -0
- package/src/sessions/registry.ts +145 -27
- package/src/sessions/runStore.ts +14 -0
- package/src/sessions/runner.ts +45 -4
- package/src/sessions/sessions.test.ts +137 -4
- package/src/sessions/sessions.ts +51 -15
- package/src/sessions/subagentService.test.ts +159 -0
- package/src/sessions/subagentService.ts +236 -0
- package/src/sessions/subagents.ts +97 -0
- package/src/skills/registry.ts +16 -10
- package/src/terminal/accountsMenu.test.ts +225 -0
- package/src/terminal/accountsMenu.ts +382 -0
- package/src/terminal/control.test.ts +23 -0
- package/src/terminal/control.ts +23 -0
- package/src/terminal/controller.ts +134 -7
- package/src/terminal/hubMenu.ts +24 -0
- package/src/terminal/launch.test.ts +5 -14
- package/src/terminal/launch.ts +2 -42
- package/src/terminal/piExtension.lifecycle.test.ts +55 -0
- package/src/terminal/piExtension.ts +91 -6
- package/src/terminal/socket.test.ts +23 -0
- package/src/terminal/socket.ts +55 -20
- package/src/terminal/standalone.test.ts +37 -0
- package/src/terminal/standalone.ts +28 -7
- package/src/terminal/terminal.test.ts +220 -16
- package/src/terminal/terminals.ts +234 -16
- package/src/terminal/trajectory.test.ts +66 -0
- package/src/terminal/trajectory.ts +44 -0
- package/src/terminal/tui/application.test.ts +290 -0
- package/src/terminal/tui/application.ts +653 -0
- package/src/terminal/tui/commands.test.ts +278 -0
- package/src/terminal/tui/commands.ts +443 -0
- package/src/terminal/tui/dialogs.ts +301 -0
- package/src/terminal/tui/editor.test.ts +113 -0
- package/src/terminal/tui/editor.ts +109 -0
- package/src/terminal/tui/keybindings.test.ts +30 -0
- package/src/terminal/tui/keybindings.ts +59 -0
- package/src/terminal/tui/piAdapter.ts +62 -0
- package/src/terminal/tui/planMode.test.ts +10 -0
- package/src/terminal/tui/planMode.ts +12 -0
- package/src/terminal/tui/presentation.test.ts +79 -0
- package/src/terminal/tui/questions.test.ts +41 -0
- package/src/terminal/tui/questions.ts +47 -0
- package/src/terminal/tui/runtime.socket.test.ts +178 -0
- package/src/terminal/tui/runtime.test.ts +225 -0
- package/src/terminal/tui/runtime.ts +403 -0
- package/src/terminal/tui/screen.test.ts +143 -0
- package/src/terminal/tui/screen.ts +121 -0
- package/src/terminal/tui/sessionUi.ts +24 -0
- package/src/terminal/tui/settings.test.ts +150 -0
- package/src/terminal/tui/settings.ts +188 -0
- package/src/terminal/tui/skillInvocation.test.ts +71 -0
- package/src/terminal/tui/skillInvocation.ts +51 -0
- package/src/terminal/tui/status.test.ts +48 -0
- package/src/terminal/tui/status.ts +82 -0
- package/src/terminal/tui/theme.ts +185 -0
- package/src/terminal/tui/tools.test.ts +145 -0
- package/src/terminal/tui/tools.ts +142 -0
- package/src/terminal/tui/trajectoryPanel.test.ts +99 -0
- package/src/terminal/tui/trajectoryPanel.ts +226 -0
- package/src/terminal/tui/transcript.test.ts +39 -0
- package/src/terminal/tui/transcript.ts +135 -0
- package/src/terminal/tui/ui.test.ts +313 -0
- package/src/terminal/tui/ui.ts +166 -0
- package/src/terminal/tui/welcome.ts +102 -0
- package/src/terminal/tui/workingStatus.test.ts +44 -0
- package/src/terminal/tui/workingStatus.ts +238 -0
- package/src/tools/askUser.test.ts +69 -0
- package/src/tools/askUser.ts +31 -17
- package/src/tools/definitions.ts +42 -1
- package/src/tools/maintenance.ts +29 -0
- package/src/tools/memoryTools.ts +1 -1
- package/src/tools/registryWrite.test.ts +101 -0
- package/src/tools/registryWrite.ts +182 -0
- package/src/tools/spawnSubagent.ts +47 -0
- package/src/tools/types.ts +2 -0
package/docs/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Harness documentation
|
|
2
|
+
|
|
3
|
+
Current implementation: [Harness terminal frontend](terminal-tui.md). The package-level
|
|
4
|
+
[README](../README.md) describes the embedding API, accounts, plugins, memory and storage.
|
|
5
|
+
|
|
6
|
+
| ID | Record | Type | Status | Related |
|
|
7
|
+
|---|---|---|---|---|
|
|
8
|
+
| ADR-0004 | [Fullscreen terminal layout with a fixed dock](decisions/ADR-0004-fullscreen-terminal-layout.md) | Decision | Accepted; working tree implementation | ADR-0001, [Terminal architecture and evidence](terminal-tui.md) |
|
|
9
|
+
| ADR-0003 | [Shared asynchronous subagents with native-history delivery receipts](decisions/ADR-0003-shared-asynchronous-subagents.md) | Decision | Accepted; working tree implementation | ADR-0002, [Embedding API](../README.md) |
|
|
10
|
+
| ADR-0002 | [Native subagent lifecycle in Harness sessions](decisions/ADR-0002-native-subagent-lifecycle.md) | Decision | Superseded by ADR-0003 | [Embedding API](../README.md) |
|
|
11
|
+
| ADR-0001 | [Own the terminal frontend and keep Pi as the session runtime](decisions/ADR-0001-owned-terminal.md) | Decision | Accepted; working tree implementation | [Terminal architecture and evidence](terminal-tui.md) |
|
|
12
|
+
|
|
13
|
+
Living reference pages describe checked source behavior. Accepted decisions record the choice;
|
|
14
|
+
they do not establish publication or release. Evidence references on the terminal page identify
|
|
15
|
+
the inspected upstream artifacts and bound the verification claims.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ADR-0001
|
|
3
|
+
title: Own the terminal frontend and keep Pi as the session runtime
|
|
4
|
+
status: accepted
|
|
5
|
+
created: 2026-10-17
|
|
6
|
+
updated: 2026-10-17
|
|
7
|
+
related: []
|
|
8
|
+
supersedes: null
|
|
9
|
+
superseded_by: null
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ADR-0001 — Own the terminal frontend and keep Pi as the session runtime
|
|
13
|
+
|
|
14
|
+
## Context and decision drivers
|
|
15
|
+
|
|
16
|
+
The requested terminal must reproduce the supported Oh My Pi presentation using capabilities
|
|
17
|
+
Harness already has. An extension can replace a header, footer or editor, but Pi's
|
|
18
|
+
`InteractiveMode` still owns transcript layout, command routing, focus and screen lifecycle.
|
|
19
|
+
That arrangement cannot give Harness full control over those surfaces.
|
|
20
|
+
|
|
21
|
+
The existing terminal boundary already provides isolated accounts, a compiled prompt, trusted
|
|
22
|
+
resource selection, a session socket and controller, a final tool gate and one writer for run
|
|
23
|
+
records. Retaining that boundary avoids a second authority for authentication or run state.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
Own the terminal application in `src/terminal/tui/` and run a persistent Pi SDK
|
|
28
|
+
`AgentSessionRuntime` inside the prepared terminal child. Harness owns composition, editor and
|
|
29
|
+
input routing, dialogs, overlays and built-in command dispatch. Pi owns model/tool execution
|
|
30
|
+
and native history. The frontend binds the public extension UI contract in `tui` mode and may
|
|
31
|
+
reuse public rich content components.
|
|
32
|
+
|
|
33
|
+
Keep `TerminalAssets` and `Terminals.prepare/send/state/input/dispose` compatible. Preserve the
|
|
34
|
+
socket/controller bridge and its final gate. Keep the private control claim in process memory
|
|
35
|
+
across native session replacement, revalidate prepared resource trust before every load, and
|
|
36
|
+
scope native resume/import to the terminal's original working folder. Put unavoidable Pi
|
|
37
|
+
implementation imports in one adapter checked against the pinned 1.2.0 version.
|
|
38
|
+
|
|
39
|
+
Use the shipped OMP 18.11.3 artifacts as the visual reference, with Harness identity and existing
|
|
40
|
+
commands. [The living reference](../terminal-tui.md) records artifact digests, evidence and
|
|
41
|
+
verification limits. [NOTICE.md](../../NOTICE.md) preserves the upstream MIT license.
|
|
42
|
+
|
|
43
|
+
## Alternatives and rejection reasons
|
|
44
|
+
|
|
45
|
+
- **Only install a presentation extension:** leaves the default interactive frontend in control
|
|
46
|
+
of transcript geometry, built-in commands and terminal lifecycle, contrary to the requested ownership.
|
|
47
|
+
- **Call `sessions.run` for each prompt:** recreates and disposes the runtime per turn, losing
|
|
48
|
+
persistent plugin UI/queue state and overlapping terminal controller run accounting.
|
|
49
|
+
- **Replace the execution stack with OMP:** changes tools, model/provider behavior and product
|
|
50
|
+
capabilities beyond the requested presentation migration.
|
|
51
|
+
- **Fork Pi's complete interactive mode:** introduces a large second implementation of session
|
|
52
|
+
and terminal behavior, making upgrades and account/governance boundaries harder to review.
|
|
53
|
+
|
|
54
|
+
## Consequences and risks
|
|
55
|
+
|
|
56
|
+
Harness must maintain its own command routing, focus, keybindings and extension UI lifecycle.
|
|
57
|
+
The small internal adapter is an explicit upgrade constraint. Native session format and
|
|
58
|
+
account storage stay compatible; cross-folder navigation requires a freshly prepared scope.
|
|
59
|
+
Changes to trusted plugin code require review and reopening the terminal.
|
|
60
|
+
|
|
61
|
+
This record accepts the architectural choice. Implementation exists in the working tree;
|
|
62
|
+
publication and exhaustive visual/provider/platform parity are not implied. The living page
|
|
63
|
+
records passed focused tests and the pending controlled PTY verification result.
|
|
64
|
+
|
|
65
|
+
## Implementation and evidence
|
|
66
|
+
|
|
67
|
+
- [`TerminalApplication`](../../src/terminal/tui/application.ts) owns the screen and input lifecycle.
|
|
68
|
+
- [`runtime.ts`](../../src/terminal/tui/runtime.ts) owns the persistent SDK adapter and scoped navigation.
|
|
69
|
+
- [`piAdapter.ts`](../../src/terminal/tui/piAdapter.ts) contains pinned compatibility imports.
|
|
70
|
+
- [`piExtension.ts`](../../src/terminal/piExtension.ts) retains gate/telemetry integration.
|
|
71
|
+
- [`runtime.socket.test.ts`](../../src/terminal/tui/runtime.socket.test.ts) checks the real isolated SDK/socket boundary without a provider request.
|
|
72
|
+
- [Reference identities, retained REA evidence and verification limits](../terminal-tui.md#verification-and-remaining-limits).
|
|
73
|
+
|
|
74
|
+
## Verification follow-up — 2026-10-17
|
|
75
|
+
|
|
76
|
+
The earlier pending-PTY statement above records the state when this decision was written.
|
|
77
|
+
The [living reference](../terminal-tui.md#verification-and-remaining-limits) now records passed
|
|
78
|
+
production PTY scenarios at all three planned sizes, 419 Harness tests, a standalone proof and
|
|
79
|
+
portable OMP/Harness comparison records. The implementation also scopes retained plugin UI
|
|
80
|
+
callbacks and transcript components to a native resource generation, provides frozen runtime
|
|
81
|
+
state, and submits grouped question answers through the existing registry atomically.
|
|
82
|
+
|
|
83
|
+
The personal Codex live attempt reached the provider but was rejected because its usage limit
|
|
84
|
+
had been reached. The isolated credentials were deleted, the source sign-in remained unchanged,
|
|
85
|
+
and terminal restoration passed; see the [live evidence](../evidence/terminal-2026-10-17-followup/live-codex/README.md).
|
|
86
|
+
Successful live streaming/tool acceptance and App renderer acceptance remain open. This evidence
|
|
87
|
+
does not establish a released package or exhaustive platform/visual parity.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ADR-0002
|
|
3
|
+
title: Native subagent lifecycle in Harness sessions
|
|
4
|
+
status: superseded
|
|
5
|
+
superseded_by: ADR-0003
|
|
6
|
+
created: 2026-10-18
|
|
7
|
+
updated: 2026-10-18
|
|
8
|
+
---
|
|
9
|
+
# ADR-0002 — Native subagent lifecycle in Harness sessions
|
|
10
|
+
|
|
11
|
+
Superseded by [ADR-0003](ADR-0003-shared-asynchronous-subagents.md), which expands the native
|
|
12
|
+
service to asynchronous execution, reliable delivery, and terminal parity. The earlier decision
|
|
13
|
+
and its rationale are retained below.
|
|
14
|
+
|
|
15
|
+
## Context
|
|
16
|
+
|
|
17
|
+
Subagents already used Harness sessions, capability snapshots and run lineage, but their tool
|
|
18
|
+
owned launching and polled run files for completion. A host inferred ownership from session
|
|
19
|
+
names, coupling it to an internal identifier format.
|
|
20
|
+
|
|
21
|
+
## Decision
|
|
22
|
+
|
|
23
|
+
`Sessions.spawnSubagent` owns child creation. A live parent's captured launch supplies caller,
|
|
24
|
+
account, model selection, folder, policy context, prompt and capability ceiling. The host or
|
|
25
|
+
model supplies only a complete brief and an optional tool subset. Current capability availability
|
|
26
|
+
and the captured nesting limit further restrict each child. Empty tool subsets stay empty;
|
|
27
|
+
spawning permission is never added back after narrowing. Children start with fresh history,
|
|
28
|
+
without inherited image attachments or request preamble. Existing tool gating applies.
|
|
29
|
+
|
|
30
|
+
The model tool is an adapter to the same native API exposed over RPC. `RunRegistry.wait` resolves
|
|
31
|
+
on settlement, including failed startup and interruption, without polling or per-call listeners.
|
|
32
|
+
The Harness Subagents plugin attributes the model-visible tool. Opt-in remains a session option;
|
|
33
|
+
`{}` selects the Harness default depth of one. Application organization delegation remains a host
|
|
34
|
+
concern. Consumers resolve ownership from run lineage rather than parsing session IDs.
|
|
35
|
+
|
|
36
|
+
## Alternatives and consequences
|
|
37
|
+
|
|
38
|
+
Keeping orchestration in the tool would leave hosts without the same lifecycle contract.
|
|
39
|
+
Moving application organizations into Harness would violate the embedding boundary. A new
|
|
40
|
+
background job manager would duplicate existing run tracking, approvals and interruption.
|
|
41
|
+
The selected change reuses these layers and preserves the synchronous model tool behavior.
|
|
42
|
+
The native API starts children only for live parents in the same Harness instance; it does not
|
|
43
|
+
resume a stopped parent's launch or implement cross-process child management.
|
|
44
|
+
|
|
45
|
+
## Evidence
|
|
46
|
+
|
|
47
|
+
Regression coverage is in `src/sessions/sessions.test.ts` and `src/server/server.test.ts`.
|
|
48
|
+
Application host and run-family tests cover nested lineage with opaque session IDs. Verification
|
|
49
|
+
results belong to the implementation handoff; no release or live-provider behavior is claimed.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ADR-0003
|
|
3
|
+
title: Shared asynchronous subagents with native-history delivery receipts
|
|
4
|
+
status: accepted
|
|
5
|
+
created: 2026-10-18
|
|
6
|
+
updated: 2026-10-18
|
|
7
|
+
supersedes: ADR-0002
|
|
8
|
+
---
|
|
9
|
+
# ADR-0003 — Shared asynchronous subagents with native-history delivery receipts
|
|
10
|
+
|
|
11
|
+
## Context and decision
|
|
12
|
+
|
|
13
|
+
The owner selected reliable delivery, asynchronous spawn/wait, and one SDK/terminal/App API after
|
|
14
|
+
studying OMP. They chose attached children joined before the parent finishes, async spawning by
|
|
15
|
+
default, availability for every interactive caller within its capability ceiling, and recovery
|
|
16
|
+
only when the user explicitly resumes. This supersedes the blocking tool contract in ADR-0002.
|
|
17
|
+
|
|
18
|
+
`harness.subagents` owns spawning, selective waits and results. SDK and terminal adapters register
|
|
19
|
+
live, frozen parent context with it; no model argument supplies caller, account or replacement
|
|
20
|
+
launch metadata. The terminal adapter uses its private control key. Children use normal Harness
|
|
21
|
+
sessions, isolated accounts, fresh history, existing gates and capability intersection. The old
|
|
22
|
+
`sessions.spawnSubagent` entry point forwards to the service for compatibility.
|
|
23
|
+
|
|
24
|
+
A parent reserves each child in `runs/<parent>/subagents.json` through the run store before startup.
|
|
25
|
+
A native branch owner marker is persisted before a real parent accepts spawns. Results are delivered
|
|
26
|
+
at turn boundaries or through explicit blocking tools. Native messages and tool results carry typed
|
|
27
|
+
receipts; only receipts on the selected branch acknowledge delivery. Constructing a delivery draft
|
|
28
|
+
never acknowledges it. Large batches acknowledge only the included children. The final settlement
|
|
29
|
+
hook joins children and injects remaining results; an adapter verification guard refuses successful
|
|
30
|
+
completion if persisted native history still lacks a result. The stable system prefix is unchanged.
|
|
31
|
+
|
|
32
|
+
On explicit resume, matching owner markers, caller/account/folder/native identity and run lineage
|
|
33
|
+
restore undelivered results. A startup reservation that never opened becomes an interrupted run.
|
|
34
|
+
Existing startup recovery interrupts orphaned active runs. Neither recovery path restarts model
|
|
35
|
+
work. Cancellation settles approvals and questions before waiting for SDK abort, preventing blocked
|
|
36
|
+
child interactions from deadlocking parent shutdown.
|
|
37
|
+
|
|
38
|
+
Interactive sessions enable the tools by default, `null` disables them, and explicit capability
|
|
39
|
+
sets can exclude them. Unattended callers remain off unless the host explicitly enables them.
|
|
40
|
+
Default nesting remains one, with a maximum of three. There is no new scheduler, reusable-worker
|
|
41
|
+
lifecycle, model routing, workspace isolation system, concurrency cap or family budget here.
|
|
42
|
+
|
|
43
|
+
## Alternatives and consequences
|
|
44
|
+
|
|
45
|
+
A tool-owned launcher leaves terminal and SDK behavior divergent. An in-memory delivery flag loses
|
|
46
|
+
results across restart; searching prose for run IDs confuses mentions with delivery. Native-history
|
|
47
|
+
receipts identify persisted context without adding a second acknowledgement store. Run records
|
|
48
|
+
remain the outcome source of truth, while the parent file preserves startup reservations.
|
|
49
|
+
|
|
50
|
+
The App owns organization delegation and resolves native ancestry for thread routing, tools,
|
|
51
|
+
isolation and connections. Its existing work-budget accounting includes native descendants. Harness
|
|
52
|
+
learns no organizational role or application concept. Native completion records execution outcome;
|
|
53
|
+
it does not certify the child's findings or artifacts.
|
|
54
|
+
|
|
55
|
+
## Evidence and limits
|
|
56
|
+
|
|
57
|
+
`subagentService.test.ts` covers selective waits, foreign handles, cancellation, bounded batches and
|
|
58
|
+
recovery windows. Real scripted Pi tests in `conversation.test.ts` cover async delivery, inline
|
|
59
|
+
receipts and interruption during a child question. `runtime.socket.test.ts` exercises the real
|
|
60
|
+
terminal socket, private controls, native execution and child decision routing. RPC and App host
|
|
61
|
+
regressions cover protocol shapes and ancestry. Tests use disposable storage without external
|
|
62
|
+
model requests. These are working-tree changes, not a publication or live-provider acceptance claim.
|
|
63
|
+
|
|
64
|
+
Verification on 2026-10-18: Harness typecheck, 455 tests (two skipped), unchanged golden fixtures,
|
|
65
|
+
build and standalone proof passed. App typecheck, layer/UI checks, sidecar build, 30 sidecar tests
|
|
66
|
+
and 217 Rust tests passed (one ignored). The renderer suite passed 491 tests and retained its
|
|
67
|
+
pre-existing `PendingRequestDock.test.ts` heading-text assertion failure. No live account or
|
|
68
|
+
user-data-root testing was performed.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ADR-0004
|
|
3
|
+
title: Fullscreen terminal layout with a fixed dock
|
|
4
|
+
status: accepted
|
|
5
|
+
created: 2026-10-18
|
|
6
|
+
updated: 2026-10-18
|
|
7
|
+
supersedes: none
|
|
8
|
+
---
|
|
9
|
+
# ADR-0004 — Fullscreen terminal layout with a fixed dock
|
|
10
|
+
|
|
11
|
+
## Context and decision
|
|
12
|
+
|
|
13
|
+
The owned terminal drew its transcript and composer as one flowing list on the main screen. With a
|
|
14
|
+
short transcript the composer sat under the last line and moved down as the transcript grew. Harness
|
|
15
|
+
also kept its own scroll offset, mouse regions and paging, which duplicated the viewport Pi already
|
|
16
|
+
provides in fullscreen.
|
|
17
|
+
|
|
18
|
+
Harness now uses Pi's fullscreen renderer by default. The welcome and transcript form a scroll view
|
|
19
|
+
that fills the terminal; the dock (working line, queue and notices, widgets, editor and status band,
|
|
20
|
+
footer) sits fixed below it. Pi's `tuiMode`, `fullscreenScrollbar`, `fullscreenWheelScrollLines`,
|
|
21
|
+
`fullscreenCopyOnSelect` and `fullscreenExitOutput` settings are read from the account and honoured.
|
|
22
|
+
`tuiMode: "regular"` keeps the flowing layout on the main screen. When fullscreen ends with
|
|
23
|
+
`fullscreenExitOutput: "transcript"`, the rendered conversation is copied into the normal scrollback.
|
|
24
|
+
|
|
25
|
+
## Consequences
|
|
26
|
+
|
|
27
|
+
- While fullscreen is running, the transcript is not in the terminal's scrollback. Mouse capture is
|
|
28
|
+
on, so terminal drag-selection gives way to the application's selection, which copies on release.
|
|
29
|
+
These are Pi's defaults and remain changeable in settings.
|
|
30
|
+
- Harness no longer owns scroll state, wheel handling or paging. Those come from Pi's viewport and its
|
|
31
|
+
`tui.altScreen.*` bindings, and the keyboard list shows the scroll keys only in fullscreen.
|
|
32
|
+
- Regular mode has no Harness-managed scroll position; scrolling there belongs to the terminal.
|
|
33
|
+
- When the screen is short, a long notice shows its first rows and scrolls for the rest. The prompt
|
|
34
|
+
and status are never clipped; the head of a notice stays readable, so the newest line of a long
|
|
35
|
+
list may need a scroll.
|
|
36
|
+
- Only `transcript` exit output is implemented. Other values write nothing on exit.
|
|
37
|
+
|
|
38
|
+
## Alternatives considered
|
|
39
|
+
|
|
40
|
+
Padding the main-screen output to the terminal height also keeps the composer on the last rows for a
|
|
41
|
+
short transcript. It keeps native scrollback, but Harness would still own its scrolling and mouse
|
|
42
|
+
handling, and the composer would scroll away with the terminal's scrollback once the transcript
|
|
43
|
+
overflowed. That was rejected because it keeps the duplicated code this change removes.
|
|
44
|
+
|
|
45
|
+
## Evidence
|
|
46
|
+
|
|
47
|
+
The layout is covered by `src/terminal/tui/screen.test.ts`: the dock stays on the last rows as the
|
|
48
|
+
transcript grows and scrolls, regular mode flows in order, and the exit copy is written with its styles
|
|
49
|
+
reset. The PTY verification of the built CLI is recorded in [the terminal reference](../terminal-tui.md).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ADR-0005
|
|
3
|
+
title: Application-provided OAuth clients for connected apps
|
|
4
|
+
status: accepted
|
|
5
|
+
created: 2026-10-18
|
|
6
|
+
updated: 2026-10-18
|
|
7
|
+
supersedes: none
|
|
8
|
+
---
|
|
9
|
+
# ADR-0005 — Application-provided OAuth clients for connected apps
|
|
10
|
+
|
|
11
|
+
## Context and decision
|
|
12
|
+
|
|
13
|
+
Every remote connected app so far authenticated one of two ways: the server registers this app
|
|
14
|
+
dynamically (RFC 7591), or the person registers a client with the provider and pastes its ID and
|
|
15
|
+
secret into Connections. Google's Workspace MCP servers support neither in practice: they have no
|
|
16
|
+
dynamic registration, and requiring every person to create a Google Cloud project before connecting
|
|
17
|
+
Gmail is not a product.
|
|
18
|
+
|
|
19
|
+
A connection's `oauthClient` now has a third value, `app`: **the application supplies the client**.
|
|
20
|
+
The host does so through one extension point on the harness:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
export interface AppOAuthClients {
|
|
24
|
+
ids(): string[]
|
|
25
|
+
get(catalogId: string): Promise<{ clientId: string; clientSecret?: string } | null>
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`HarnessOptions.oauthClients` carries it. Only a connection whose `catalogId` appears in `ids()` can
|
|
30
|
+
use it — a hand-typed connection, a Hub connector recipe, and a plugin template cannot, because none
|
|
31
|
+
of them can name a client the application actually holds. `connections.appClients` reports the ids so
|
|
32
|
+
an application can offer only the connectors it can actually sign in for.
|
|
33
|
+
|
|
34
|
+
The client is resolved inside `Connections.oauthLoad` and placed in the OAuth state as
|
|
35
|
+
`staticClient`, exactly where a user-registered client goes. `oauthSave` already strips
|
|
36
|
+
`staticClient`, so the client is never written to `connections.json` or the secret store; it is
|
|
37
|
+
re-resolved on each load. Refresh tokens continue to be stored per connection.
|
|
38
|
+
|
|
39
|
+
## Consequences
|
|
40
|
+
|
|
41
|
+
- The harness learns no product concept: it knows a catalog entry id, a client, and a sign-in. Which
|
|
42
|
+
entries an application can serve is entirely the host's answer.
|
|
43
|
+
- A build without a client is a first-class state, not an error: `ids()` is empty, `signIn` refuses
|
|
44
|
+
with "This connector has no app sign-in in this build", and the application hides those catalog
|
|
45
|
+
entries instead of offering a dead end.
|
|
46
|
+
- A client secret in a distributed desktop build is extractable, so it is treated as public. The real
|
|
47
|
+
controls stay Google's consent screen, the requested scopes, and the tool approval gate.
|
|
48
|
+
- One client can serve several catalog entries (five Google servers, one client). Each entry is still
|
|
49
|
+
its own connection with its own tokens, so signing in to one does not authorize another.
|
|
50
|
+
- The per-user path is untouched: `oauthClient: 'user'` still stores a person's own client in
|
|
51
|
+
Keychain, and it remains the only route for providers or accounts an application cannot pre-register
|
|
52
|
+
for.
|
|
53
|
+
- Google's Developer Preview terms, app verification and CASA assessment for restricted scopes are
|
|
54
|
+
outside this decision; they decide who can use the shipped client, not how it is delivered.
|