@north-light/crouter 0.3.157 → 0.3.158
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/dist/api/client.d.ts +4 -1
- package/dist/api/client.js +5 -0
- package/dist/api/dto/broker.d.ts +1 -1
- package/dist/api/dto/nodes.d.ts +15 -0
- package/dist/api/routes.d.ts +1 -0
- package/dist/api/routes.js +1 -0
- package/dist/builtin-views/chat/core.mjs +6 -51
- package/dist/builtin-views/chat/tui.mjs +6 -14
- package/dist/builtin-views/chat/web.jsx +2 -7
- 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 +9 -1
- package/dist/clients/attach/command.js +5 -5
- package/dist/clients/attach/input/controller.d.ts +23 -8
- package/dist/clients/attach/input/controller.js +59 -29
- package/dist/clients/attach/overlays/dialogs.d.ts +1 -2
- package/dist/clients/attach/overlays/graph.d.ts +4 -1
- package/dist/clients/attach/overlays/graph.js +25 -7
- package/dist/clients/attach/session/context.d.ts +7 -0
- package/dist/clients/attach/session/frames.js +1 -8
- package/dist/clients/attach/session/input-wiring.d.ts +1 -1
- package/dist/clients/attach/session/input-wiring.js +21 -11
- package/dist/clients/attach/session/mode.d.ts +4 -3
- package/dist/clients/attach/session/mode.js +6 -1
- package/dist/clients/attach/session/reconnect.d.ts +3 -3
- package/dist/clients/attach/session/reconnect.js +6 -7
- 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 +13 -1
- package/dist/clients/attach/slash/dispatch.js +65 -17
- package/dist/clients/attach/viewer.js +523 -523
- package/dist/clients/web/web-client/shared/protocol.d.ts +5 -3
- package/dist/commands/node.js +1 -1
- package/dist/core/__tests__/broker-sdk-wiring.test.js +18 -18
- package/dist/core/__tests__/chat-view-reconnect.test.js +44 -23
- package/dist/core/__tests__/full/broker-attach-limits.test.js +60 -36
- package/dist/core/__tests__/full/broker-attach-stream.test.js +4 -4
- package/dist/core/__tests__/full/broker-dialogs.test.js +121 -62
- package/dist/core/__tests__/helpers/broker-clients.js +2 -2
- package/dist/core/__tests__/session-model.test.js +15 -26
- 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 +0 -2
- package/dist/core/runtime/auth-reload.d.ts +4 -4
- package/dist/core/runtime/auth-reload.js +9 -13
- 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 +23 -27
- package/dist/core/runtime/broker-protocol.js +1 -1
- package/dist/core/runtime/broker-request.js +5 -11
- package/dist/core/runtime/broker.d.ts +21 -13
- package/dist/core/runtime/broker.js +151 -186
- package/dist/core/runtime/interactive-deliver.js +4 -5
- package/dist/core/runtime/model-swap.d.ts +2 -3
- package/dist/core/runtime/model-swap.js +3 -4
- package/dist/core/runtime/node-read.d.ts +20 -0
- package/dist/core/runtime/node-read.js +34 -1
- 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 +8 -6
- package/dist/core/session-model/session-state.js +6 -16
- package/dist/daemon/api/handlers/nodes.js +18 -1
- package/dist/daemon/manage.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/pi-extensions/canvas-bash-valve.js +4 -3
- package/dist/web-client/assets/{index-DJhQZoAj.css → index-CpEl9LTS.css} +1 -1
- package/dist/web-client/assets/{index--SsQYcKu.js → index-CsuwzlcQ.js} +19 -19
- package/dist/web-client/index.html +2 -2
- package/dist/web-client/sw.js +1 -1
- package/docs/compat/hearth-crtr-v5.md +175 -0
- package/docs/public-api.md +2 -2
- package/package.json +2 -2
- package/runtime.lock.json +6 -6
- package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-control-preempt.test.js +0 -61
|
@@ -52,6 +52,17 @@ const CANVAS_NAMES = new Set(CANVAS_SLASH_COMMANDS.map((c) => c.name));
|
|
|
52
52
|
* (the in-process GRAPH overlay, already remote-safe — see overlays/graph.ts)
|
|
53
53
|
* and `/view` (a self-contained popup unrelated to `nodeId`) stay available. */
|
|
54
54
|
const REMOTE_UNSAFE_CANVAS_NAMES = new Set(['promote', 'resume-node', 'context', 'node-metadata', 'rename', 'clear', 'mcp']);
|
|
55
|
+
/** Native canvas commands an OBSERVER may not run — the ones that drive the
|
|
56
|
+
* engine or mutate the node, whether by frame or by shelling the crtr CLI:
|
|
57
|
+
* `/clear` sends `new_session`; `/promote` shells `crtr node promote` and then
|
|
58
|
+
* triggers a turn; `/rename` sends `set_session_name` and shells `crtr node
|
|
59
|
+
* config --name`; `/resume-node` opens the navigator whose Enter path focuses
|
|
60
|
+
* (and so revives) a node; `/mcp`'s panel actions ride a `prompt` frame into
|
|
61
|
+
* the engine. Deliberately NOT here — genuinely read-only or viewer-local:
|
|
62
|
+
* `/graph` (in-process overlay), `/context` and `/node-metadata` (read-only
|
|
63
|
+
* inspection popups), `/color` (this pane's tmux border + this viewer's name
|
|
64
|
+
* chip). */
|
|
65
|
+
const OBSERVER_UNSAFE_CANVAS_NAMES = new Set(['clear', 'promote', 'rename', 'resume-node', 'view', 'mcp']);
|
|
55
66
|
/** tmux named colours accepted by `/color` (plus `colourN`/`#rrggbb`, validated
|
|
56
67
|
* by pattern). Mirrors tmux's colour-name table so a typo is rejected with the
|
|
57
68
|
* options rather than silently ignored by tmux. */
|
|
@@ -78,17 +89,53 @@ const THINKING_LEVELS = new Set(Object.keys({
|
|
|
78
89
|
max: true,
|
|
79
90
|
}));
|
|
80
91
|
const BUILTIN_NAMES = new Set(BUILTIN_SLASH_COMMANDS.map((c) => c.name));
|
|
81
|
-
/** Builtin slash commands safe to run
|
|
92
|
+
/** Builtin slash commands safe to run from a READ-ONLY viewer — a REMOTE
|
|
93
|
+
* attach (`--canvas`) or an `--observer` one —
|
|
82
94
|
* everything else in `BUILTIN_SLASH_COMMANDS` either drives the engine
|
|
83
95
|
* (`/model`, `/new`, `/compact`, `/name`, `/resume`, `/fork`, `/tree`,
|
|
84
96
|
* `/settings`, `/share`, `/clone`, `/import`, `/reload`, `/scoped-models`
|
|
85
97
|
* selection) or is viewer-local but operates on the WRONG host for a remote
|
|
86
98
|
* node (`/login`, `/logout` — LOCAL `auth.json` via `defaultAgentDir()`, not
|
|
87
|
-
* the remote broker's
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
|
|
99
|
+
* the remote broker's; for an observer, credentials it may not swap under a
|
|
100
|
+
* running engine). Blocked BEFORE the switch so a read-only viewer never even
|
|
101
|
+
* opens the picker only to have the eventual selection frame swallowed by
|
|
102
|
+
* InputController's `emitCommand` gate (defense-in-depth #2, not the only
|
|
103
|
+
* defense — see input/controller.ts DRIVE_FRAME_TYPES). */
|
|
104
|
+
const READ_ONLY_SAFE_BUILTIN_NAMES = new Set(['copy', 'session', 'changelog', 'hotkeys', 'quit']);
|
|
105
|
+
/** THE capability gate for a read-only viewer, keyed on the two fixed facts a
|
|
106
|
+
* session resolves once (`remote`, `role`) — so the answer for a given command
|
|
107
|
+
* cannot change mid-session. Returns the refusal notice, or `undefined` when
|
|
108
|
+
* this viewer may run the command. Both `dispatchSlashCommand` (so a direct
|
|
109
|
+
* caller is safe) and the InputController's submit path (so a refusal is
|
|
110
|
+
* reported before command dispatch) consult this one function; there is no
|
|
111
|
+
* second policy anywhere. */
|
|
112
|
+
function readOnlyRefusal(name, ctx) {
|
|
113
|
+
const canvas = CANVAS_NAMES.has(name);
|
|
114
|
+
const builtinUnsafe = BUILTIN_NAMES.has(name) && !READ_ONLY_SAFE_BUILTIN_NAMES.has(name);
|
|
115
|
+
if (ctx.remote === true) {
|
|
116
|
+
if (canvas && REMOTE_UNSAFE_CANVAS_NAMES.has(name)) {
|
|
117
|
+
return `/${name} is not available for a remote attach (--canvas) — it acts on this machine's local canvas`;
|
|
118
|
+
}
|
|
119
|
+
if (builtinUnsafe)
|
|
120
|
+
return `/${name} is not available for a remote attach (--canvas) — view-only`;
|
|
121
|
+
}
|
|
122
|
+
if (ctx.remote !== true && (ctx.role ?? 'controller') !== 'controller') {
|
|
123
|
+
if ((canvas && OBSERVER_UNSAFE_CANVAS_NAMES.has(name)) || builtinUnsafe) {
|
|
124
|
+
return `/${name} is not available to a read-only observer attach (--observer) — it would drive this node`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
/** The same gate, entered from raw editor text: the InputController asks BEFORE
|
|
130
|
+
* dispatching so a refused command is reported without reaching command
|
|
131
|
+
* dispatch. Unrecognized (extension) commands answer `undefined` here — they
|
|
132
|
+
* ride the normal `prompt` path, which the controller's own drive gate covers. */
|
|
133
|
+
export function readOnlySlashRefusal(text, ctx) {
|
|
134
|
+
const m = /^\/(\S+)\s*([\s\S]*)$/.exec(text.trim());
|
|
135
|
+
if (!m)
|
|
136
|
+
return undefined;
|
|
137
|
+
return readOnlyRefusal(m[1], ctx);
|
|
138
|
+
}
|
|
92
139
|
/** Every leading-command name this viewer consumes ENTIRELY locally — the
|
|
93
140
|
* message never reaches the engine as prose, whether dispatched or blocked
|
|
94
141
|
* with a notice (a remote-unsafe canvas/builtin command still returns
|
|
@@ -125,13 +172,20 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
125
172
|
}
|
|
126
173
|
// Canvas commands (reimplemented native in the viewer) — handled before the
|
|
127
174
|
// builtin gate since they are neither pi builtins nor engine-extension commands.
|
|
128
|
-
if (CANVAS_NAMES.has(name))
|
|
175
|
+
if (CANVAS_NAMES.has(name)) {
|
|
176
|
+
const refusal = readOnlyRefusal(name, ctx);
|
|
177
|
+
if (refusal !== undefined) {
|
|
178
|
+
ctx.notify(refusal);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
129
181
|
return dispatchCanvasCommand(name, arg, ctx);
|
|
182
|
+
}
|
|
130
183
|
// Unknown to us AND not a builtin → let the engine's extension runner have it.
|
|
131
184
|
if (!BUILTIN_NAMES.has(name))
|
|
132
185
|
return false;
|
|
133
|
-
|
|
134
|
-
|
|
186
|
+
const refusal = readOnlyRefusal(name, ctx);
|
|
187
|
+
if (refusal !== undefined) {
|
|
188
|
+
ctx.notify(refusal);
|
|
135
189
|
return true;
|
|
136
190
|
}
|
|
137
191
|
switch (name) {
|
|
@@ -286,14 +340,8 @@ export function commandAutocompleteItems(commands = BUILTIN_SLASH_COMMANDS, remo
|
|
|
286
340
|
/** Dispatch one of the native canvas commands. Always returns `true` (the
|
|
287
341
|
* command is viewer-owned — never forwarded to the engine as a prompt). */
|
|
288
342
|
function dispatchCanvasCommand(name, arg, ctx) {
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
// NOT EXECUTABLE if typed by hand — a remote node id has no relationship to
|
|
292
|
-
// this machine's local canvas.db (or, worse, collides with one that does).
|
|
293
|
-
if (ctx.remote && REMOTE_UNSAFE_CANVAS_NAMES.has(name)) {
|
|
294
|
-
ctx.notify(`/${name} is not available for a remote attach (--canvas) — it acts on this machine's local canvas`);
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
343
|
+
// The capability gate (`readOnlyRefusal`) already ran in dispatchSlashCommand,
|
|
344
|
+
// so everything reaching here is a command this viewer may run.
|
|
297
345
|
switch (name) {
|
|
298
346
|
case 'clear':
|
|
299
347
|
// Claude-Code ergonomics: `/clear` == `/new` — drop the conversation and
|