@north-light/crouter 0.3.154 → 0.3.157
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/README.md +2 -1
- package/dist/api/client.d.ts +1 -4
- package/dist/api/client.js +0 -5
- package/dist/api/dto/broker.d.ts +1 -1
- package/dist/api/dto/nodes.d.ts +0 -15
- package/dist/api/routes.d.ts +0 -1
- package/dist/api/routes.js +0 -1
- package/dist/builtin-views/chat/core.mjs +51 -6
- package/dist/builtin-views/chat/tui.mjs +14 -6
- package/dist/builtin-views/chat/web.jsx +7 -2
- package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.js +1 -1
- package/dist/clients/attach/chrome/roster.d.ts +1 -1
- package/dist/clients/attach/chrome/roster.js +1 -9
- package/dist/clients/attach/command.js +5 -5
- package/dist/clients/attach/input/controller.d.ts +8 -23
- package/dist/clients/attach/input/controller.js +29 -59
- package/dist/clients/attach/overlays/dialogs.d.ts +2 -1
- package/dist/clients/attach/overlays/graph.d.ts +1 -4
- package/dist/clients/attach/overlays/graph.js +7 -25
- package/dist/clients/attach/session/context.d.ts +0 -7
- package/dist/clients/attach/session/frames.js +8 -1
- package/dist/clients/attach/session/input-wiring.d.ts +1 -1
- package/dist/clients/attach/session/input-wiring.js +11 -21
- package/dist/clients/attach/session/mode.d.ts +3 -4
- package/dist/clients/attach/session/mode.js +1 -6
- package/dist/clients/attach/session/reconnect.d.ts +3 -3
- package/dist/clients/attach/session/reconnect.js +7 -6
- package/dist/clients/attach/session/state-sync.d.ts +1 -1
- package/dist/clients/attach/session/state-sync.js +2 -2
- package/dist/clients/attach/slash/dispatch.d.ts +1 -13
- package/dist/clients/attach/slash/dispatch.js +17 -65
- package/dist/clients/attach/viewer.js +523 -523
- package/dist/clients/web/web-client/shared/protocol.d.ts +3 -5
- package/dist/core/__tests__/broker-sdk-wiring.test.js +18 -18
- package/dist/core/__tests__/chat-view-reconnect.test.js +23 -44
- package/dist/core/__tests__/full/broker-attach-limits.test.js +36 -60
- package/dist/core/__tests__/full/broker-attach-stream.test.js +4 -4
- package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-control-preempt.test.js +61 -0
- package/dist/core/__tests__/full/broker-dialogs.test.js +62 -121
- package/dist/core/__tests__/helpers/broker-clients.js +2 -2
- package/dist/core/__tests__/session-model.test.js +26 -15
- package/dist/core/keybindings/__tests__/resolve.test.js +1 -1
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +2 -0
- package/dist/core/runtime/auth-reload.d.ts +4 -4
- package/dist/core/runtime/auth-reload.js +13 -9
- package/dist/core/runtime/boot-root.d.ts +2 -2
- package/dist/core/runtime/boot-root.js +7 -7
- package/dist/core/runtime/broker-protocol.d.ts +27 -23
- package/dist/core/runtime/broker-protocol.js +1 -1
- package/dist/core/runtime/broker-request.js +11 -5
- package/dist/core/runtime/broker.d.ts +13 -21
- package/dist/core/runtime/broker.js +186 -151
- package/dist/core/runtime/interactive-deliver.js +5 -4
- package/dist/core/runtime/model-swap.d.ts +3 -2
- package/dist/core/runtime/model-swap.js +4 -3
- package/dist/core/runtime/node-read.d.ts +0 -20
- package/dist/core/runtime/node-read.js +1 -34
- package/dist/core/runtime/resume-root.d.ts +1 -1
- package/dist/core/runtime/resume-root.js +6 -6
- package/dist/core/runtime/spawn.js +3 -3
- package/dist/core/session-model/session-state.d.ts +6 -8
- package/dist/core/session-model/session-state.js +16 -6
- package/dist/daemon/api/handlers/nodes.js +1 -18
- package/dist/daemon/manage.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/web-client/assets/index--SsQYcKu.js +79 -0
- package/dist/web-client/assets/{index-CpEl9LTS.css → index-DJhQZoAj.css} +1 -1
- package/dist/web-client/index.html +2 -2
- package/dist/web-client/sw.js +1 -1
- package/docs/compat/hearth-crtr-v1.md +1 -1
- package/docs/compat/hearth-crtr-v2.md +1 -1
- package/docs/compat/hearth-crtr-v3.md +1 -1
- package/docs/compat/hearth-crtr-v4.md +3 -1
- package/docs/public-api.md +2 -2
- package/package.json +4 -4
- package/runtime.lock.json +2 -2
- package/dist/web-client/assets/index-BpyZGBhI.js +0 -79
- package/docs/compat/hearth-crtr-v5.md +0 -175
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
// two-channel inbox path had (send rode the watcher's ~1.8s inject; stop rode a
|
|
8
8
|
// direct abort frame; no shared ordering).
|
|
9
9
|
//
|
|
10
|
-
// Both ride the shared one-shot
|
|
11
|
-
// connect → `hello`
|
|
12
|
-
//
|
|
13
|
-
//
|
|
10
|
+
// Both ride the shared one-shot controller round-trip (broker-request.ts):
|
|
11
|
+
// connect → `hello` + `request_control` (preempting any attached viewer for one
|
|
12
|
+
// frame) → send the correlated frame → await its ack. A connection failure
|
|
13
|
+
// BEFORE establishment surfaces as `BrokerUnreachableError` so the caller can
|
|
14
|
+
// fall back to the durable path.
|
|
14
15
|
import { oneShotControllerRequest } from './broker-request.js';
|
|
15
16
|
/** Deliver `text` to a LIVE node's engine on its serialized frame loop. The
|
|
16
17
|
* broker routes it itself (idle → prompt, streaming → steer) and acks once
|
|
@@ -3,8 +3,9 @@ import { type NodeMeta } from '../canvas/index.js';
|
|
|
3
3
|
* AND its view.sock exists. Both are required — a dead pid with a stale socket,
|
|
4
4
|
* or a live pid mid-boot before it binds the socket, is NOT a live broker. */
|
|
5
5
|
export declare function isBrokerLive(meta: NodeMeta): boolean;
|
|
6
|
-
/** Swap a LIVE node's model over its view.sock. Connects
|
|
7
|
-
*
|
|
6
|
+
/** Swap a LIVE node's model over its view.sock. Connects, claims controller
|
|
7
|
+
* (hello + request_control, so an attached viewer never blocks the swap), sends
|
|
8
|
+
* `set_model`, and resolves with the broker-resolved `provider/id` on its ack —
|
|
8
9
|
* read back from the row the broker just persisted (model_changed.model carries
|
|
9
10
|
* only the bare id). Rejects with the broker's message on an `error` frame (e.g.
|
|
10
11
|
* `no model matching '<spec>' in the registry`), on connect failure, or timeout.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// it survives revives. Two paths, chosen on broker liveness:
|
|
4
4
|
//
|
|
5
5
|
// LIVE — the node's broker is up (recorded pi_pid alive AND view.sock
|
|
6
|
-
// present): connect to view.sock
|
|
6
|
+
// present): connect to view.sock, claim controller, send `set_model`,
|
|
7
7
|
// await the ack. The broker's broadcastModelChanged already persists
|
|
8
8
|
// the choice via persistModelChoice (model_override + launch.model), so
|
|
9
9
|
// this path NEVER writes the recipe itself — it reads back the resolved
|
|
@@ -23,8 +23,9 @@ import { oneShotControllerRequest } from './broker-request.js';
|
|
|
23
23
|
export function isBrokerLive(meta) {
|
|
24
24
|
return isPidAlive(meta.pi_pid) && existsSync(viewSocketPath(meta.node_id));
|
|
25
25
|
}
|
|
26
|
-
/** Swap a LIVE node's model over its view.sock. Connects
|
|
27
|
-
*
|
|
26
|
+
/** Swap a LIVE node's model over its view.sock. Connects, claims controller
|
|
27
|
+
* (hello + request_control, so an attached viewer never blocks the swap), sends
|
|
28
|
+
* `set_model`, and resolves with the broker-resolved `provider/id` on its ack —
|
|
28
29
|
* read back from the row the broker just persisted (model_changed.model carries
|
|
29
30
|
* only the bare id). Rejects with the broker's message on an `error` frame (e.g.
|
|
30
31
|
* `no model matching '<spec>' in the registry`), on connect failure, or timeout.
|
|
@@ -1,24 +1,4 @@
|
|
|
1
1
|
import type { BrokerSnapshot } from './broker-protocol.js';
|
|
2
|
-
/** The node's conversation exactly as it ran: the raw session `.jsonl` bytes
|
|
3
|
-
* plus the assembled system prompt. Nothing here is interpreted — this is the
|
|
4
|
-
* faithful record (every session-tree entry, every cycle, every branch, every
|
|
5
|
-
* custom role), as opposed to `readNodeSnapshot`'s cycle-flattened
|
|
6
|
-
* `AgentMessage[]` reconstruction. */
|
|
7
|
-
export interface NodeSessionRead {
|
|
8
|
-
nodeId: string;
|
|
9
|
-
/** Absolute path of the session file the bytes came from. */
|
|
10
|
-
sessionFile: string;
|
|
11
|
-
/** Verbatim contents of the session `.jsonl`. */
|
|
12
|
-
sessionJsonl: string;
|
|
13
|
-
/** The assembled system prompt as last captured, or `null` if never written. */
|
|
14
|
-
systemPrompt: string | null;
|
|
15
|
-
}
|
|
16
|
-
/** Read a node's raw conversation record off disk — no broker, no attach, no
|
|
17
|
-
* mapping. The system prompt is NOT part of the session file (pi assembles it
|
|
18
|
-
* per boot), so it is read from the sidecar the broker captures on every
|
|
19
|
-
* rebind (`<jobDir>/system-prompt.md`), falling back to the one `launch.ts`
|
|
20
|
-
* writes at spawn (`<nodeDir>/system-prompt.md`). */
|
|
21
|
-
export declare function readNodeSession(nodeId: string): NodeSessionRead;
|
|
22
2
|
export interface NodeSnapshotRead {
|
|
23
3
|
nodeId: string;
|
|
24
4
|
snapshot: BrokerSnapshot;
|
|
@@ -12,7 +12,7 @@ import { tmpdir } from 'node:os';
|
|
|
12
12
|
import { basename, join } from 'node:path';
|
|
13
13
|
import { InputError } from '../io.js';
|
|
14
14
|
import { fullName, getNode } from '../canvas/index.js';
|
|
15
|
-
import {
|
|
15
|
+
import { sessionPtrPath } from '../canvas/paths.js';
|
|
16
16
|
import { SessionManager } from './broker-sdk.js';
|
|
17
17
|
import { piAgentDir } from './package-health.js';
|
|
18
18
|
import { BUILTIN_SLASH_COMMANDS } from './pi-vendored.js';
|
|
@@ -98,39 +98,6 @@ function statsFromMessages(messages, sessionId, sessionFile) {
|
|
|
98
98
|
stats.tokens.total = stats.tokens.input + stats.tokens.output + stats.tokens.cacheRead + stats.tokens.cacheWrite;
|
|
99
99
|
return stats;
|
|
100
100
|
}
|
|
101
|
-
/** Read a node's raw conversation record off disk — no broker, no attach, no
|
|
102
|
-
* mapping. The system prompt is NOT part of the session file (pi assembles it
|
|
103
|
-
* per boot), so it is read from the sidecar the broker captures on every
|
|
104
|
-
* rebind (`<jobDir>/system-prompt.md`), falling back to the one `launch.ts`
|
|
105
|
-
* writes at spawn (`<nodeDir>/system-prompt.md`). */
|
|
106
|
-
export function readNodeSession(nodeId) {
|
|
107
|
-
const node = getNode(nodeId);
|
|
108
|
-
if (node === null) {
|
|
109
|
-
throw new InputError({ error: 'not_found', message: `no node: ${nodeId}`, field: 'id', next: 'List nodes with `crtr node inspect list`.' });
|
|
110
|
-
}
|
|
111
|
-
const sessionFile = resolveSessionFile(nodeId, node.pi_session_file);
|
|
112
|
-
if (sessionFile === null) {
|
|
113
|
-
throw new InputError({ error: 'no_session', message: `node has no readable session file: ${nodeId}`, field: 'id', next: 'Choose a node with captured conversation history.' });
|
|
114
|
-
}
|
|
115
|
-
return {
|
|
116
|
-
nodeId,
|
|
117
|
-
sessionFile,
|
|
118
|
-
// A live broker appends to this file; a torn read would truncate the last
|
|
119
|
-
// line, so the caller gets whatever was complete at read time (the same
|
|
120
|
-
// at-rest guarantee `readNodeSnapshot`'s copy has).
|
|
121
|
-
sessionJsonl: readFileSync(sessionFile, 'utf8'),
|
|
122
|
-
systemPrompt: readIfPresent(join(jobDir(nodeId), 'system-prompt.md'))
|
|
123
|
-
?? readIfPresent(join(nodeDir(nodeId), 'system-prompt.md')),
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
function readIfPresent(path) {
|
|
127
|
-
try {
|
|
128
|
-
return readFileSync(path, 'utf8');
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
101
|
/** Reconstruct a persisted session without launching its broker. Backs the
|
|
135
102
|
* machine-readable snapshot endpoint and the human transcript render. */
|
|
136
103
|
export async function readNodeSnapshot(nodeId) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Resolve a front-door resume: pick a target root (MRU or via picker), then
|
|
2
|
-
*
|
|
2
|
+
* take over this terminal to view it. On success it execs the viewer inline and
|
|
3
3
|
* never returns. Otherwise reports why it did not: `'empty'` (no resumable root
|
|
4
4
|
* here → caller boots a fresh one) or `'aborted'` (the picker was dismissed →
|
|
5
5
|
* caller exits without surprise-booting). */
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
//
|
|
9
9
|
// A "session" in the Claude-Code sense is a crtr ROOT node: a durable pi
|
|
10
10
|
// conversation pinned to a cwd that never dies, only goes dormant. So resuming
|
|
11
|
-
// is reviving the last root here and making THIS pane its
|
|
11
|
+
// is reviving the last root here and making THIS pane its controller-viewer —
|
|
12
12
|
// mirroring the front-door boot tail (register the pane as the viewer focus,
|
|
13
13
|
// then exec `crtr surface attach` inline). cwd is the key so it never teleports
|
|
14
14
|
// across projects.
|
|
@@ -50,7 +50,7 @@ async function listResumableRoots(cwd) {
|
|
|
50
50
|
.sort((a, b) => (a.created < b.created ? 1 : a.created > b.created ? -1 : 0)); // created DESC = MRU
|
|
51
51
|
}
|
|
52
52
|
/** Resolve a front-door resume: pick a target root (MRU or via picker), then
|
|
53
|
-
*
|
|
53
|
+
* take over this terminal to view it. On success it execs the viewer inline and
|
|
54
54
|
* never returns. Otherwise reports why it did not: `'empty'` (no resumable root
|
|
55
55
|
* here → caller boots a fresh one) or `'aborted'` (the picker was dismissed →
|
|
56
56
|
* caller exits without surprise-booting). */
|
|
@@ -70,8 +70,8 @@ export async function resumeRoot(cwd, pick) {
|
|
|
70
70
|
await attachInline(target.node_id);
|
|
71
71
|
return 'aborted'; // unreachable — attachInline execs and exits
|
|
72
72
|
}
|
|
73
|
-
/** Register THIS pane as the target root's
|
|
74
|
-
*
|
|
73
|
+
/** Register THIS pane as the target root's one viewer, then exec `crtr surface
|
|
74
|
+
* attach` inline so this terminal becomes the controller-viewer. The attach's
|
|
75
75
|
* own ensureAttach revives the dormant broker + waits for its view socket
|
|
76
76
|
* server-side, so this does NOT revive in-process. Mirrors the front-door boot
|
|
77
77
|
* tail. Does not return. */
|
|
@@ -83,10 +83,10 @@ async function attachInline(nodeId) {
|
|
|
83
83
|
installTmuxBindings();
|
|
84
84
|
}
|
|
85
85
|
catch { /* best-effort */ }
|
|
86
|
-
// Make THIS pane the node's
|
|
86
|
+
// Make THIS pane the node's one viewer. A dormant root's prior focus row
|
|
87
87
|
// points at a stale/detached pane (the front door keeps the row across a
|
|
88
88
|
// clean detach), so drop it and claim the current pane — the exact --new-pane
|
|
89
|
-
// discipline of registerViewerFocus,
|
|
89
|
+
// discipline of registerViewerFocus (UNIQUE(node_id): one viewer per node),
|
|
90
90
|
// routed through crtrd since it is a canvas write.
|
|
91
91
|
const here = currentTmux();
|
|
92
92
|
if (here !== null) {
|
|
@@ -302,8 +302,8 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
302
302
|
}
|
|
303
303
|
// (The three scoped long-term memory stores are seeded for EVERY node at birth
|
|
304
304
|
// in spawnNode — no orchestrator-gated seeding needed here.)
|
|
305
|
-
// An ATTENDED --root spawned from inside tmux opens its
|
|
306
|
-
//
|
|
305
|
+
// An ATTENDED --root spawned from inside tmux opens its one viewer in the
|
|
306
|
+
// CALLER'S CURRENT session (below), so it appears where the spawner is
|
|
307
307
|
// working; a managed child is a headless broker with NO viewer at all, and
|
|
308
308
|
// so is an unattended terminal root (nobody asked to watch it).
|
|
309
309
|
const here = attendedRoot ? currentTmux() : null;
|
|
@@ -318,7 +318,7 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
318
318
|
// Broker is the only host: launch the detached broker ENGINE. A managed child
|
|
319
319
|
// gets NO viewer — spawning it returns once the engine is launched and a human
|
|
320
320
|
// opens a viewer on demand via `crtr surface node focus` / surface attach. A --root gets
|
|
321
|
-
//
|
|
321
|
+
// one foreground viewer below.
|
|
322
322
|
const placed = launchBrokerOrCrash(meta.node_id, inv, {
|
|
323
323
|
cwd: meta.cwd,
|
|
324
324
|
name: fullName(meta),
|
|
@@ -12,10 +12,10 @@ export interface DisplayState {
|
|
|
12
12
|
title: string | undefined;
|
|
13
13
|
}
|
|
14
14
|
export interface SessionState {
|
|
15
|
-
/** This viewer's
|
|
16
|
-
* means writable and `observer` means read-only; the broker never reassigns
|
|
17
|
-
* it, so nothing after `welcome` can change this field. */
|
|
15
|
+
/** This viewer's broker-assigned role. */
|
|
18
16
|
role: ClientRole;
|
|
17
|
+
/** The client_id currently holding control, or null when nobody does. */
|
|
18
|
+
controllerId: string | null;
|
|
19
19
|
/** The broker process's pi agent dir, from `welcome` — lets a viewer point an
|
|
20
20
|
* AuthStorage/ModelRegistry at the SAME auth.json. */
|
|
21
21
|
agentDir: string | undefined;
|
|
@@ -29,10 +29,8 @@ export interface SessionState {
|
|
|
29
29
|
* this rather than leaving a dead broker's rows on screen. */
|
|
30
30
|
queued: string[];
|
|
31
31
|
display: DisplayState;
|
|
32
|
-
/** The blocking dialog awaiting an answer, or null. Only a
|
|
33
|
-
*
|
|
34
|
-
* answer wins and the broker dismisses the rest by id.
|
|
35
|
-
* `welcome.pending_dialog` carries one in flight at attach time, and
|
|
32
|
+
/** The blocking dialog awaiting an answer, or null. Only a controller is ever
|
|
33
|
+
* sent one; `welcome.pending_dialog` carries one in flight at attach time, and
|
|
36
34
|
* a welcome is a COMPLETE catch-up, so a welcome naming none clears it.
|
|
37
35
|
* (The terminal viewer does not read this field: its overlay is owned by
|
|
38
36
|
* `InputController`, which holds a live TUI handle rather than data. The field
|
|
@@ -48,4 +46,4 @@ export declare function initialSessionState(role?: ClientRole): SessionState;
|
|
|
48
46
|
export declare function applySnapshot(frame: WelcomeFrame): SessionState;
|
|
49
47
|
/** Fold one broker frame into the session state. Pure and total: a frame that
|
|
50
48
|
* carries no session state returns `state` UNCHANGED (same reference). */
|
|
51
|
-
export declare function reduce(state: SessionState, frame: BrokerToClient): SessionState;
|
|
49
|
+
export declare function reduce(state: SessionState, frame: BrokerToClient, clientId: string): SessionState;
|
|
@@ -11,10 +11,9 @@
|
|
|
11
11
|
// SESSION state = anything derived SOLELY from what the broker sent.
|
|
12
12
|
// VIEWER state = anything derived from local input or this terminal/tab.
|
|
13
13
|
//
|
|
14
|
-
// So `role` belongs here even though it differs per viewer (
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// correlation id this viewer minted, and the transcript
|
|
14
|
+
// So `role` belongs here even though it differs per viewer (it is BROKER-assigned
|
|
15
|
+
// — "identical across viewers" would get it wrong), while a transient notice, a
|
|
16
|
+
// connection phase, a correlation id this viewer minted, and the transcript
|
|
18
17
|
// RENDERING all stay with the viewer. The transcript itself is deliberately out
|
|
19
18
|
// of scope: the terminal accumulates into a scrollback and the web replaces
|
|
20
19
|
// wholesale, and that difference is legitimate.
|
|
@@ -27,6 +26,7 @@ const EMPTY_DISPLAY = { statuses: {}, widgets: {}, title: undefined };
|
|
|
27
26
|
export function initialSessionState(role = 'observer') {
|
|
28
27
|
return {
|
|
29
28
|
role,
|
|
29
|
+
controllerId: null,
|
|
30
30
|
agentDir: undefined,
|
|
31
31
|
engine: undefined,
|
|
32
32
|
contextTokens: undefined,
|
|
@@ -48,6 +48,7 @@ export function applySnapshot(frame) {
|
|
|
48
48
|
const contextUsage = snapshot.stats.contextUsage;
|
|
49
49
|
return {
|
|
50
50
|
role: frame.role,
|
|
51
|
+
controllerId: frame.controller_id,
|
|
51
52
|
agentDir: frame.agentDir,
|
|
52
53
|
engine: snapshot.state,
|
|
53
54
|
contextTokens: contextUsage?.tokens ?? undefined,
|
|
@@ -62,16 +63,25 @@ export function applySnapshot(frame) {
|
|
|
62
63
|
}
|
|
63
64
|
/** Fold one broker frame into the session state. Pure and total: a frame that
|
|
64
65
|
* carries no session state returns `state` UNCHANGED (same reference). */
|
|
65
|
-
export function reduce(state, frame) {
|
|
66
|
+
export function reduce(state, frame, clientId) {
|
|
66
67
|
switch (frame.type) {
|
|
67
68
|
case 'welcome':
|
|
68
69
|
return applySnapshot(frame);
|
|
70
|
+
case 'control_changed': {
|
|
71
|
+
if (state.controllerId === frame.controller_id)
|
|
72
|
+
return state;
|
|
73
|
+
return {
|
|
74
|
+
...state,
|
|
75
|
+
controllerId: frame.controller_id,
|
|
76
|
+
role: frame.controller_id === clientId ? 'controller' : 'observer',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
69
79
|
case 'model_changed':
|
|
70
80
|
return patchEngine(state, { model: frame.model });
|
|
71
81
|
case 'extension_ui_request': {
|
|
72
82
|
// pi's uniform envelope: the three display METHODS are levels this model
|
|
73
83
|
// retains; every other method is a dialog (a new one supersedes any
|
|
74
|
-
// showing one).
|
|
84
|
+
// showing one — a control handoff re-routes it).
|
|
75
85
|
switch (frame.method) {
|
|
76
86
|
case 'setStatus': {
|
|
77
87
|
const current = state.display.statuses[frame.statusKey];
|
|
@@ -13,7 +13,7 @@ import { childrenOf, subscribersOf, subscriptionsOf, setMessageWait, updateNode
|
|
|
13
13
|
import { subtreeIds } from '../../../core/canvas/nav-model.js';
|
|
14
14
|
import { contextDir, reportsDir } from '../../../core/canvas/paths.js';
|
|
15
15
|
import { nodeArtifacts } from '../../../core/canvas/history.js';
|
|
16
|
-
import {
|
|
16
|
+
import { readNodeSnapshot, transcriptMarkdown } from '../../../core/runtime/node-read.js';
|
|
17
17
|
import { reviveAll } from '../../../core/runtime/revive-all.js';
|
|
18
18
|
import { promote, requestYield } from '../../../core/runtime/promote.js';
|
|
19
19
|
import { recycleNode } from '../../../core/runtime/recycle.js';
|
|
@@ -233,22 +233,6 @@ async function handleSnapshot(ctx) {
|
|
|
233
233
|
};
|
|
234
234
|
return { status: 200, body };
|
|
235
235
|
}
|
|
236
|
-
/** The faithful export read: raw `.jsonl` bytes + assembled system prompt.
|
|
237
|
-
* `/snapshot` reconstructs a cycle-flattened `AgentMessage[]` for renderers and
|
|
238
|
-
* drops what a renderer cannot show; this drops nothing. */
|
|
239
|
-
function handleSession(ctx) {
|
|
240
|
-
const id = ctx.params['id'];
|
|
241
|
-
requireMeta(id);
|
|
242
|
-
const read = readNodeSession(id);
|
|
243
|
-
const body = {
|
|
244
|
-
node_id: id,
|
|
245
|
-
session_file: read.sessionFile,
|
|
246
|
-
session_jsonl: read.sessionJsonl,
|
|
247
|
-
system_prompt: read.systemPrompt,
|
|
248
|
-
captured_at: nowIso(),
|
|
249
|
-
};
|
|
250
|
-
return { status: 200, body };
|
|
251
|
-
}
|
|
252
236
|
async function handleTranscript(ctx) {
|
|
253
237
|
const id = ctx.params['id'];
|
|
254
238
|
requireMeta(id);
|
|
@@ -523,7 +507,6 @@ export const nodeRoutes = [
|
|
|
523
507
|
{ method: 'POST', pattern: '/v1/nodes/revive-all', handler: () => handleReviveAll() },
|
|
524
508
|
{ method: 'GET', pattern: '/v1/nodes/:id', handler: handleDetail },
|
|
525
509
|
{ method: 'GET', pattern: '/v1/nodes/:id/snapshot', handler: handleSnapshot },
|
|
526
|
-
{ method: 'GET', pattern: '/v1/nodes/:id/session', handler: handleSession },
|
|
527
510
|
{ method: 'GET', pattern: '/v1/nodes/:id/transcript', handler: handleTranscript },
|
|
528
511
|
{ method: 'GET', pattern: '/v1/nodes/:id/context', handler: handleContext },
|
|
529
512
|
{ method: 'GET', pattern: '/v1/nodes/:id/artifacts', handler: handleArtifacts },
|
package/dist/daemon/manage.js
CHANGED
|
@@ -50,8 +50,8 @@ export const DAEMON_ENV_STRIP_KEYS = [
|
|
|
50
50
|
// reads `currentTmux()` for a --root's foreground viewer placement; a stale
|
|
51
51
|
// inherited $TMUX makes that resolve to an arbitrary "current" pane, so a
|
|
52
52
|
// front-door root (created through crtrd) opens a SECOND viewer window and
|
|
53
|
-
// focuses it — stealing
|
|
54
|
-
// user's pane.
|
|
53
|
+
// focuses it — stealing the controller role from the front door's own inline
|
|
54
|
+
// attach, which is then demoted to a read-only observer in the user's pane.
|
|
55
55
|
// Strip them so the daemon is truly paneless and leaves root-viewer placement
|
|
56
56
|
// to the front door. Explicit `-t <target>` tmux ops (focus/window mgmt) are
|
|
57
57
|
// unaffected — the tmux CLI reaches the server via its default socket.
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { readTelemetry, readContextTokens } from './core/canvas/telemetry.js';
|
|
|
14
14
|
export type { Telemetry } from './core/canvas/telemetry.js';
|
|
15
15
|
export { BrokerClient, BrokerUnavailableError } from './core/broker-client/index.js';
|
|
16
16
|
export { encodeFrame, FrameDecoder, FrameOverflowError, CLIENT_READ_CAPS, BROKER_READ_CAPS, } from './core/runtime/broker-protocol.js';
|
|
17
|
-
export type { FrameDecoderCaps, BrokerSnapshot, ClientRole, WelcomeFrame, AckFrame, ErrorFrame, ExtensionUIRequestFrame, BrokerToClient, HelloFrame, PromptFrame, SteerFrame, FollowUpFrame, AbortFrame, ByeFrame, ShutdownFrame, SetModelFrame, CycleModelFrame, SetThinkingLevelFrame, SetAutoRetryFrame, SetAutoCompactionFrame, CompactFrame, NewSessionFrame, SwitchSessionFrame, ForkFrame, SetSessionNameFrame, GetCommandsFrame, NavigateTreeFrame, ReloadFrame, ExportFrame, ClientToBroker, RpcExtensionUIRequest, RpcExtensionUIResponse, ExtensionUIResponseFrame, } from './core/runtime/broker-protocol.js';
|
|
17
|
+
export type { FrameDecoderCaps, BrokerSnapshot, ClientRole, WelcomeFrame, ControlChangedFrame, AckFrame, ErrorFrame, ExtensionUIRequestFrame, BrokerToClient, HelloFrame, PromptFrame, SteerFrame, FollowUpFrame, AbortFrame, RequestControlFrame, ReleaseControlFrame, ByeFrame, ShutdownFrame, SetModelFrame, CycleModelFrame, SetThinkingLevelFrame, SetAutoRetryFrame, SetAutoCompactionFrame, CompactFrame, NewSessionFrame, SwitchSessionFrame, ForkFrame, SetSessionNameFrame, GetCommandsFrame, NavigateTreeFrame, ReloadFrame, ExportFrame, ClientToBroker, RpcExtensionUIRequest, RpcExtensionUIResponse, ExtensionUIResponseFrame, } from './core/runtime/broker-protocol.js';
|
|
18
18
|
export type { SessionStats } from '@earendil-works/pi-coding-agent';
|
|
19
19
|
export { general } from './core/errors.js';
|
|
20
20
|
export type { CrtrError } from './core/errors.js';
|