@north-light/crouter 0.3.276 → 0.3.278
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/clients/attach/input/capabilities.js +2 -1
- package/dist/clients/attach/slash/dispatch.js +42 -19
- package/dist/clients/attach/viewer.js +505 -505
- package/dist/commands/canvas-browse.js +2 -2
- package/dist/core/__tests__/integration/command-plugins.test.js +6 -6
- package/dist/core/canvas/browse/app.d.ts +1 -1
- package/dist/core/canvas/browse/app.js +32 -7
- package/dist/core/canvas/browse/render.d.ts +17 -2
- package/dist/core/canvas/browse/render.js +342 -242
- package/dist/core/canvas/render-source.d.ts +26 -0
- package/dist/core/canvas/render-source.js +73 -0
- package/dist/core/command-manifests/manifest.d.ts +4 -2
- package/dist/core/command-plugins/help-addenda.d.ts +12 -3
- package/dist/core/command-plugins/help-addenda.js +17 -4
- package/dist/core/command.js +2 -2
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +1 -0
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/daemon/reconcilers/live-obligation.d.ts +3 -2
- package/dist/daemon/reconcilers/live-obligation.js +4 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.js +2 -2
- package/package.json +3 -1
- package/runtime.lock.json +2 -2
|
@@ -74,7 +74,7 @@ export function viewerOperationForFrame(frame) {
|
|
|
74
74
|
export function viewerOperationForSlash(name, builtin, canvas) {
|
|
75
75
|
if (canvas) {
|
|
76
76
|
switch (name) {
|
|
77
|
-
case 'resume
|
|
77
|
+
case 'resume': return 'canvas-resume';
|
|
78
78
|
case 'context':
|
|
79
79
|
case 'node-metadata': return 'local-canvas-inspect';
|
|
80
80
|
// `/plugins` configures the local machine, not this node; observers and
|
|
@@ -84,6 +84,7 @@ export function viewerOperationForSlash(name, builtin, canvas) {
|
|
|
84
84
|
case 'clear':
|
|
85
85
|
case 'promote':
|
|
86
86
|
case 'rename':
|
|
87
|
+
case 'resume-session':
|
|
87
88
|
case 'mcp': return 'engine-drive';
|
|
88
89
|
default: return undefined;
|
|
89
90
|
}
|
|
@@ -38,7 +38,8 @@ export const CANVAS_SLASH_COMMANDS = [
|
|
|
38
38
|
{ name: 'graph', description: 'Toggle the canvas GRAPH view (your local subscription tree)' },
|
|
39
39
|
{ name: 'inbox', description: 'Toggle the human inbox — pending asks and reviews for you' },
|
|
40
40
|
{ name: 'promote', description: 'Promote this node to an orchestrator — /promote, or /promote <kind> to specialize' },
|
|
41
|
-
{ name: 'resume
|
|
41
|
+
{ name: 'resume', description: 'Open the canvas navigator and swap THIS pane onto the chosen node; parked residents wake only on submitted input' },
|
|
42
|
+
{ name: 'resume-session', description: 'Switch this node onto one of its saved pi sessions — picker, or /resume-session <session-path>' },
|
|
42
43
|
{ name: 'context', description: 'Browse this node + its subscriptions’ context dirs in a read-only nvim popup' },
|
|
43
44
|
{ name: 'plugins', description: 'Open the interactive plugin browser for this machine' },
|
|
44
45
|
{ name: 'node-metadata', description: 'Inspect this node — identity, lifecycle, runtime, graph, artifacts, paths, and agent surfaces' },
|
|
@@ -51,7 +52,11 @@ export const CANVAS_SLASH_COMMANDS = [
|
|
|
51
52
|
{ name: 'copy-transcript', description: 'Copy every copyable transcript block' },
|
|
52
53
|
{ name: 'fold-tools', description: 'View setting — fold every FINISHED tool call to its single call line (Alt+C → z → f)' },
|
|
53
54
|
];
|
|
54
|
-
|
|
55
|
+
/** Prior names still dispatched natively but kept OUT of autocomplete, so
|
|
56
|
+
* muscle memory keeps working while the palette shows one canonical entry.
|
|
57
|
+
* Normalized to the canonical name before any gate or dispatch runs. */
|
|
58
|
+
const CANVAS_ALIASES = new Map([['resume-node', 'resume']]);
|
|
59
|
+
const CANVAS_NAMES = new Set([...CANVAS_SLASH_COMMANDS.map((c) => c.name), ...CANVAS_ALIASES.keys()]);
|
|
55
60
|
/** tmux named colours accepted by `/color` (plus `colourN`/`#rrggbb`, validated
|
|
56
61
|
* by pattern). Mirrors tmux's colour-name table so a typo is rejected with the
|
|
57
62
|
* options rather than silently ignored by tmux. */
|
|
@@ -119,7 +124,8 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
119
124
|
const command = parseSlashCommand(text);
|
|
120
125
|
if (command === undefined)
|
|
121
126
|
return false;
|
|
122
|
-
const {
|
|
127
|
+
const { arg } = command;
|
|
128
|
+
const name = CANVAS_ALIASES.get(command.name) ?? command.name;
|
|
123
129
|
if (SCOPED_OUT.has(name)) {
|
|
124
130
|
ctx.notify(`/${name} is not supported in attach (Phase 4)`);
|
|
125
131
|
return true;
|
|
@@ -189,11 +195,6 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
189
195
|
return openOrHint(ctx.openModelPicker, ctx, 'model', '/model <provider/id>');
|
|
190
196
|
ctx.send({ type: 'set_model', model: arg });
|
|
191
197
|
return true;
|
|
192
|
-
case 'resume':
|
|
193
|
-
if (!arg)
|
|
194
|
-
return openOrHint(ctx.openSessionPicker, ctx, 'session', '/resume <session-path>');
|
|
195
|
-
ctx.send({ type: 'switch_session', path: arg });
|
|
196
|
-
return true;
|
|
197
198
|
case 'fork':
|
|
198
199
|
if (!arg)
|
|
199
200
|
return openOrHint(ctx.openForkPicker, ctx, 'fork point', '/fork <entryId>');
|
|
@@ -277,12 +278,24 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
277
278
|
* omitted rather than offered-then-blocked, so autocomplete never advertises a
|
|
278
279
|
* command `dispatchCanvasCommand` would refuse to run. */
|
|
279
280
|
function withCanvasCommands(commands, capabilities) {
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
const operation = viewerOperationForSlash(command.name, false, true);
|
|
281
|
+
const permitted = (name) => {
|
|
282
|
+
const operation = viewerOperationForSlash(name, false, true);
|
|
283
283
|
return operation === undefined || capabilities.permits(operation);
|
|
284
|
+
};
|
|
285
|
+
const offerable = CANVAS_SLASH_COMMANDS.filter((command) => permitted(command.name));
|
|
286
|
+
// A canvas command that REPURPOSES a builtin name (`/resume`) keeps that name's
|
|
287
|
+
// slot in the incoming list but carries the canvas description and the canvas
|
|
288
|
+
// capability gate — so autocomplete never advertises pi's meaning for a command
|
|
289
|
+
// this viewer redefines, and never offers one it would refuse to run.
|
|
290
|
+
const canvasByName = new Map(CANVAS_SLASH_COMMANDS.map((command) => [command.name, command]));
|
|
291
|
+
const merged = commands.flatMap((command) => {
|
|
292
|
+
const canvas = canvasByName.get(command.name);
|
|
293
|
+
if (canvas === undefined)
|
|
294
|
+
return [command];
|
|
295
|
+
return permitted(command.name) ? [canvas] : [];
|
|
284
296
|
});
|
|
285
|
-
|
|
297
|
+
const seen = new Set(commands.map((c) => c.name));
|
|
298
|
+
return [...merged, ...offerable.filter((command) => !seen.has(command.name))];
|
|
286
299
|
}
|
|
287
300
|
/** Build slash-command autocomplete entries from the merged command list (the
|
|
288
301
|
* broker's `get_commands` result, which T7 may inject) — defaults to the
|
|
@@ -326,10 +339,19 @@ function dispatchCanvasCommand(name, arg, ctx) {
|
|
|
326
339
|
case 'promote':
|
|
327
340
|
promoteNode(arg, ctx);
|
|
328
341
|
return true;
|
|
329
|
-
case 'resume
|
|
330
|
-
// Open the full-screen canvas navigator as a popup; on Enter it
|
|
331
|
-
//
|
|
342
|
+
case 'resume':
|
|
343
|
+
// Open the full-screen canvas navigator as a popup; on Enter it swaps THIS
|
|
344
|
+
// pane onto the chosen node via `crtr surface node focus --in-place`.
|
|
345
|
+
if (arg)
|
|
346
|
+
return usage(ctx, '/resume — no argument; use /resume-session <path> to switch pi sessions');
|
|
332
347
|
return resumeNode(ctx);
|
|
348
|
+
case 'resume-session':
|
|
349
|
+
// The pi session picker: rebind THIS node onto a different saved session
|
|
350
|
+
// file (recovering a conversation the node cycled away from).
|
|
351
|
+
if (!arg)
|
|
352
|
+
return openOrHint(ctx.openSessionPicker, ctx, 'session', '/resume-session <session-path>');
|
|
353
|
+
ctx.send({ type: 'switch_session', path: arg });
|
|
354
|
+
return true;
|
|
333
355
|
case 'mcp':
|
|
334
356
|
// MCP server status panel — viewer-local reads of the adapter's on-disk
|
|
335
357
|
// config sources/cache (see overlays/mcp.ts); the actions (connect /
|
|
@@ -556,13 +578,14 @@ function colorNode(arg, ctx) {
|
|
|
556
578
|
ctx.notify(clear ? 'Node color cleared' : `Node colored ${value}${inTmux ? '' : ' (chip only — no tmux pane)'}`);
|
|
557
579
|
return true;
|
|
558
580
|
}
|
|
559
|
-
/** `/resume
|
|
560
|
-
*
|
|
561
|
-
*
|
|
581
|
+
/** `/resume`. Opens `crtr canvas browse` as a tmux popup, initially scoped to
|
|
582
|
+
* this node's profile (with all dirs visible); on Enter the navigator respawns
|
|
583
|
+
* THIS pane onto the chosen node (`crtr surface node focus --pane --in-place`),
|
|
584
|
+
* so resuming replaces the viewer you invoked it from. */
|
|
562
585
|
function resumeNode(ctx) {
|
|
563
586
|
const origPane = process.env['TMUX_PANE'];
|
|
564
587
|
if (process.env['TMUX'] === undefined || origPane === undefined || origPane === '') {
|
|
565
|
-
ctx.notify('/resume
|
|
588
|
+
ctx.notify('/resume needs tmux');
|
|
566
589
|
return true;
|
|
567
590
|
}
|
|
568
591
|
const cwd = shellQuote(ctx.cwd ?? process.cwd());
|