@north-light/crouter 0.3.169 → 0.3.171

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.
Files changed (122) hide show
  1. package/dist/build-root.d.ts +1 -16
  2. package/dist/build-root.js +1 -25
  3. package/dist/builtin-memory/insights/capture.md +66 -0
  4. package/dist/builtin-memory/insights/init.md +45 -0
  5. package/dist/builtin-memory/internal/plugins.md +12 -11
  6. package/dist/cli.js +2 -6
  7. package/dist/clients/attach/__tests__/attach-keybindings.test.js +7 -1
  8. package/dist/clients/attach/__tests__/context-message.test.js +50 -3
  9. package/dist/clients/attach/__tests__/diagram.test.js +1 -1
  10. package/dist/clients/attach/chrome/header.d.ts +10 -0
  11. package/dist/clients/attach/chrome/header.js +35 -0
  12. package/dist/clients/attach/chrome/status-line.d.ts +3 -0
  13. package/dist/clients/attach/chrome/status-line.js +5 -0
  14. package/dist/clients/attach/chrome/widgets.js +7 -18
  15. package/dist/clients/attach/input/controller.d.ts +10 -0
  16. package/dist/clients/attach/input/controller.js +3 -0
  17. package/dist/clients/attach/overlays/help.d.ts +33 -0
  18. package/dist/clients/attach/overlays/help.js +204 -0
  19. package/dist/clients/attach/render/chat-view.d.ts +81 -9
  20. package/dist/clients/attach/render/chat-view.js +434 -90
  21. package/dist/clients/attach/render/context-message.js +3 -3
  22. package/dist/clients/attach/render/diagram.js +2 -2
  23. package/dist/clients/attach/render/measured-container.d.ts +94 -0
  24. package/dist/clients/attach/render/measured-container.js +307 -0
  25. package/dist/clients/attach/render/transcript-copy.d.ts +9 -0
  26. package/dist/clients/attach/render/transcript-copy.js +81 -0
  27. package/dist/clients/attach/render/viewport.d.ts +29 -0
  28. package/dist/clients/attach/render/viewport.js +160 -0
  29. package/dist/clients/attach/session/bindings.d.ts +5 -1
  30. package/dist/clients/attach/session/bindings.js +16 -0
  31. package/dist/clients/attach/session/context.d.ts +5 -3
  32. package/dist/clients/attach/session/frame.d.ts +34 -0
  33. package/dist/clients/attach/session/frame.js +173 -0
  34. package/dist/clients/attach/session/frames.d.ts +2 -0
  35. package/dist/clients/attach/session/frames.js +3 -0
  36. package/dist/clients/attach/session/input-wiring.d.ts +10 -4
  37. package/dist/clients/attach/session/input-wiring.js +13 -13
  38. package/dist/clients/attach/session/keys.d.ts +4 -1
  39. package/dist/clients/attach/session/keys.js +9 -4
  40. package/dist/clients/attach/session/layout.d.ts +10 -8
  41. package/dist/clients/attach/session/layout.js +32 -31
  42. package/dist/clients/attach/session/mouse.d.ts +15 -0
  43. package/dist/clients/attach/session/mouse.js +61 -0
  44. package/dist/clients/attach/slash/dispatch.d.ts +10 -0
  45. package/dist/clients/attach/slash/dispatch.js +35 -2
  46. package/dist/clients/attach/viewer.js +605 -581
  47. package/dist/clients/web/dev-server.d.ts +4 -1
  48. package/dist/clients/web/dev-server.js +8 -19
  49. package/dist/clients/web/web-cmd.js +1 -1
  50. package/dist/commands/human/prompts.js +16 -6
  51. package/dist/commands/pkg/plugin-manage.js +143 -68
  52. package/dist/commands/sys/__tests__/setup-core.test.js +4 -2
  53. package/dist/commands/sys/setup-core.js +21 -16
  54. package/dist/core/__tests__/human-deliver.test.js +62 -2
  55. package/dist/core/clipboard-text.d.ts +8 -0
  56. package/dist/core/clipboard-text.js +18 -0
  57. package/dist/core/command-manifests/registry.d.ts +1 -3
  58. package/dist/core/command-plugins/bundle.d.ts +29 -0
  59. package/dist/core/command-plugins/bundle.js +198 -0
  60. package/dist/core/command-plugins/discovery.d.ts +0 -8
  61. package/dist/core/command-plugins/discovery.js +2 -39
  62. package/dist/core/command-plugins/transport/http-fetch.d.ts +4 -30
  63. package/dist/core/command-plugins/transport/http-fetch.js +17 -95
  64. package/dist/core/command.d.ts +9 -13
  65. package/dist/core/command.js +12 -17
  66. package/dist/core/config.js +2 -1
  67. package/dist/core/keybindings/__tests__/resolve.test.js +4 -0
  68. package/dist/core/keybindings/attach-control.d.ts +3 -0
  69. package/dist/core/keybindings/attach-control.js +1 -0
  70. package/dist/core/keybindings/catalog.d.ts +2 -2
  71. package/dist/core/keybindings/catalog.js +8 -0
  72. package/dist/core/runtime/broker-protocol.d.ts +13 -1
  73. package/dist/core/runtime/broker.js +213 -5
  74. package/dist/core/runtime/canvas-extensions.d.ts +3 -0
  75. package/dist/core/runtime/canvas-extensions.js +3 -0
  76. package/dist/core/runtime/front-door.js +4 -4
  77. package/dist/core/runtime/kickoff.d.ts +1 -5
  78. package/dist/core/runtime/kickoff.js +1 -5
  79. package/dist/core/runtime/naming.d.ts +7 -0
  80. package/dist/core/runtime/naming.js +5 -2
  81. package/dist/core/runtime/node-read.js +15 -0
  82. package/dist/core/runtime/package-health.d.ts +6 -21
  83. package/dist/core/runtime/package-health.js +12 -123
  84. package/dist/core/runtime/stop-guard.d.ts +1 -1
  85. package/dist/core/runtime/stop-guard.js +3 -3
  86. package/dist/core/runtime/tool-group-summary.d.ts +19 -0
  87. package/dist/core/runtime/tool-group-summary.js +117 -0
  88. package/dist/core/termrender/code-doc.d.ts +12 -0
  89. package/dist/core/termrender/code-doc.js +91 -0
  90. package/dist/core/termrender/display.d.ts +12 -0
  91. package/dist/core/termrender/display.js +19 -0
  92. package/dist/core/termrender/termrender.d.ts +73 -0
  93. package/dist/core/termrender/termrender.js +795 -0
  94. package/dist/core/termrender/version.d.ts +1 -0
  95. package/dist/core/termrender/version.js +1 -0
  96. package/dist/core/user-settings.d.ts +14 -0
  97. package/dist/core/user-settings.js +20 -0
  98. package/dist/daemon/crtrd.js +5 -6
  99. package/dist/index.d.ts +2 -0
  100. package/dist/index.js +1 -0
  101. package/dist/pi-extensions/__tests__/canvas-structured-output.test.d.ts +1 -0
  102. package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +63 -0
  103. package/dist/pi-extensions/canvas-stophook.js +2 -2
  104. package/dist/pi-extensions/canvas-structured-output.js +52 -8
  105. package/dist/pi-extensions/summary-tool.d.ts +33 -0
  106. package/dist/pi-extensions/summary-tool.js +62 -0
  107. package/dist/shared/generated-context.d.ts +19 -4
  108. package/dist/shared/generated-context.js +98 -7
  109. package/dist/shared/tool-groups.d.ts +23 -0
  110. package/dist/shared/tool-groups.js +60 -0
  111. package/dist/types.d.ts +11 -0
  112. package/dist/types.js +1 -0
  113. package/dist/web-client/assets/{index-U_NZ66VE.js → index-D7I3gHCL.js} +26 -19
  114. package/dist/web-client/index.html +1 -1
  115. package/dist/web-client/sw.js +1 -1
  116. package/package.json +3 -1
  117. package/runtime.lock.json +62 -3
  118. package/scripts/postinstall.mjs +14 -1
  119. package/dist/core/command-plugins/store.d.ts +0 -16
  120. package/dist/core/command-plugins/store.js +0 -64
  121. package/dist/core/runtime/fault-recovery-nudge.d.ts +0 -3
  122. package/dist/core/runtime/fault-recovery-nudge.js +0 -3
@@ -1,28 +1,25 @@
1
- // session/layout.ts — the viewer's visual skeleton: the TUI, the nine
2
- // containers it paints into, and the editor.
1
+ // session/layout.ts — the viewer's visual skeleton: its containers, editor,
2
+ // and one full-pane frame.
3
3
  //
4
4
  // Pure construction. Nothing here reads session state, talks to the socket, or
5
5
  // handles a key — a reader chasing behavior can skip this file entirely, and a
6
6
  // reader asking "where does this line get painted?" needs only this one.
7
7
  //
8
- // The render order is the whole point of the file, and it is asserted in ONE
9
- // place (`mount`). Top to bottom: chat · rule · queued · above-editor extension
10
- // widgets · editor · picker panel · below-editor extension widgets · roster ·
11
- // status bar. pi-tui anchors the viewport to the cursor at the BOTTOM, so
12
- // everything except the scrolling chat lives in the bottom-anchored chrome
13
- // stack — a "topmost" badge would scroll off into scrollback the instant the
14
- // transcript exceeded one screen, which is why the mode badge and the two
15
- // canvas panels sit directly above the queued panel rather than at the top.
16
- import { Container, ProcessTerminal, TUI } from '@earendil-works/pi-tui';
8
+ // The render order is asserted in ONE place (`ViewerFrame`'s bottom list).
9
+ // Top to bottom: pinned recap header · virtualized chat · rule · queued ·
10
+ // above-editor extension widgets · editor · picker panel · below-editor
11
+ // extension widgets · roster · status bar. The frame owns the pane rectangle,
12
+ // so every region remains on screen and none can enter terminal scrollback.
13
+ import { Container } from '@earendil-works/pi-tui';
17
14
  import { DynamicBorder, getSelectListTheme } from '@earendil-works/pi-coding-agent';
18
15
  import { TitledEditor } from '../input/titled-editor.js';
19
- /** Build the TUI, its containers, and the editor. No behavior is wired here;
20
- * the orchestrator owns every callback. */
21
- export function createLayout(pal, km) {
22
- const procTerminal = new ProcessTerminal();
23
- const tui = new TUI(procTerminal);
16
+ import { ViewerFrame } from './frame.js';
17
+ /** Build the layout around the transcript viewport. The viewer creates the
18
+ * terminal and chat first because the viewport measures that chat source. */
19
+ export function createLayout(procTerminal, tui, chat, pal, km, viewport) {
24
20
  const containers = {
25
- chat: new Container(),
21
+ chat,
22
+ header: new Container(),
26
23
  chrome: new Container(),
27
24
  aboveWidgets: new Container(),
28
25
  reports: new Container(),
@@ -31,26 +28,30 @@ export function createLayout(pal, km) {
31
28
  belowWidgets: new Container(),
32
29
  footer: new Container(),
33
30
  };
34
- // The themed rule dividing the scrolling chat from the fixed chrome below it.
35
- // `renderExtensionWidgets` re-derives this container's contents on every
36
- // widget change (the passive recap replaces the rule), so this is the seed,
37
- // not the sole writer.
31
+ // The themed rule dividing the transcript from the fixed cockpit below.
32
+ // `renderExtensionWidgets` re-derives this container on every widget change.
38
33
  containers.chrome.addChild(new DynamicBorder(pal.border));
39
34
  const editorTheme = { borderColor: pal.border, selectList: getSelectListTheme() };
40
35
  // TitledEditor paints the session name into its top border (solid-background
41
36
  // chip) and tracks a thinking-level border color, both set from live state.
42
37
  const editor = new TitledEditor(tui, editorTheme, km, { paddingX: 1 });
38
+ const frame = new ViewerFrame(procTerminal, {
39
+ header: containers.header,
40
+ viewport,
41
+ bottom: [
42
+ { slot: 'chrome', component: containers.chrome },
43
+ { slot: 'queued', component: containers.queued },
44
+ { slot: 'aboveWidgets', component: containers.aboveWidgets },
45
+ { slot: 'editor', component: editor },
46
+ { slot: 'pickerPanel', component: containers.pickerPanel },
47
+ { slot: 'belowWidgets', component: containers.belowWidgets },
48
+ { slot: 'reports', component: containers.reports },
49
+ { slot: 'footer', component: containers.footer },
50
+ ],
51
+ });
43
52
  const mount = () => {
44
- tui.addChild(containers.chat);
45
- tui.addChild(containers.chrome);
46
- tui.addChild(containers.queued);
47
- tui.addChild(containers.aboveWidgets);
48
- tui.addChild(editor);
49
- tui.addChild(containers.pickerPanel);
50
- tui.addChild(containers.belowWidgets);
51
- tui.addChild(containers.reports);
52
- tui.addChild(containers.footer);
53
+ tui.addChild(frame);
53
54
  tui.setFocus(editor);
54
55
  };
55
- return { procTerminal, tui, containers, editor, mount };
56
+ return { procTerminal, tui, containers, editor, frame, mount };
56
57
  }
@@ -0,0 +1,15 @@
1
+ import type { TUI } from '@earendil-works/pi-tui';
2
+ /** Lines moved for one terminal wheel notch. */
3
+ export declare const WHEEL_LINES = 3;
4
+ export interface MouseReporting {
5
+ /** Reporting currently on. */
6
+ enabled(): boolean;
7
+ /** Flip, write the DECSET/DECRST, return the new state. */
8
+ toggle(): boolean;
9
+ /** Turn off unconditionally — teardown and process exit. */
10
+ disable(): void;
11
+ /** Install the SGR listener; returns the remover. */
12
+ install(tui: TUI, onWheel: (direction: 'up' | 'down', lines: number) => void): () => void;
13
+ }
14
+ /** Create per-viewer mouse reporting, seeded from tmux's current mouse option. */
15
+ export declare function createMouseReporting(write: (data: string) => void): MouseReporting;
@@ -0,0 +1,61 @@
1
+ // session/mouse.ts — terminal mouse reporting for the attach viewport.
2
+ //
3
+ // Mouse reporting is enabled only when tmux already has mouse on. Outside tmux
4
+ // we leave native terminal selection alone; inside, every mouse sequence is
5
+ // consumed so clicks and drags can never become editor input.
6
+ import { spawnSync } from 'node:child_process';
7
+ import { inTmux } from '../../../core/runtime/placement-tmux.js';
8
+ const MOUSE_REPORTING_ON = '\x1b[?1002h\x1b[?1006h';
9
+ const MOUSE_REPORTING_OFF = '\x1b[?1002l\x1b[?1006l';
10
+ /** Lines moved for one terminal wheel notch. */
11
+ export const WHEEL_LINES = 3;
12
+ function detectMouseDefault() {
13
+ if (!inTmux())
14
+ return false;
15
+ const result = spawnSync('tmux', ['show-options', '-gv', 'mouse'], { encoding: 'utf8' });
16
+ return result.status === 0 && result.stdout?.trim() === 'on';
17
+ }
18
+ /** Create per-viewer mouse reporting, seeded from tmux's current mouse option. */
19
+ export function createMouseReporting(write) {
20
+ let isEnabled = detectMouseDefault();
21
+ if (isEnabled)
22
+ write(MOUSE_REPORTING_ON);
23
+ const disable = () => {
24
+ isEnabled = false;
25
+ try {
26
+ write(MOUSE_REPORTING_OFF);
27
+ }
28
+ catch {
29
+ // Terminal restoration is best-effort, including during process exit.
30
+ }
31
+ };
32
+ process.once('exit', disable);
33
+ return {
34
+ enabled: () => isEnabled,
35
+ toggle: () => {
36
+ isEnabled = !isEnabled;
37
+ write(isEnabled ? MOUSE_REPORTING_ON : MOUSE_REPORTING_OFF);
38
+ return isEnabled;
39
+ },
40
+ disable,
41
+ install: (tui, onWheel) => tui.addInputListener((data) => {
42
+ let consumed = false;
43
+ const withoutSgr = data.replace(/\x1b\[<(\d+);\d+;\d+[Mm]/g, (_match, buttons) => {
44
+ consumed = true;
45
+ const button = Number(buttons) & ~(4 | 8 | 16);
46
+ if (button === 64)
47
+ onWheel('up', WHEEL_LINES);
48
+ if (button === 65)
49
+ onWheel('down', WHEEL_LINES);
50
+ return '';
51
+ });
52
+ const withoutMouse = withoutSgr.replace(/\x1b\[M[\s\S]{3}/g, () => {
53
+ consumed = true;
54
+ return '';
55
+ });
56
+ if (!consumed)
57
+ return undefined;
58
+ return withoutMouse.length > 0 ? { data: withoutMouse } : { consume: true };
59
+ }),
60
+ };
61
+ }
@@ -1,4 +1,5 @@
1
1
  import type { AutocompleteItem, SlashCommand } from '@earendil-works/pi-tui';
2
+ import type { CopyTarget } from '../render/transcript-copy.js';
2
3
  import type { BrokerSnapshot, ClientRole, ClientToBroker } from '../../../core/runtime/broker-protocol.js';
3
4
  /** Everything a slash handler needs: the frame sink, a notice sink, the latest
4
5
  * engine state (for read-only commands like `/session`), and the cwd (for the
@@ -38,6 +39,8 @@ export interface SlashContext {
38
39
  * registers `/graph` as a stub that calls this hook. Unit C/Q owns the overlay
39
40
  * itself and populates this; when absent `/graph` notifies it isn't wired yet. */
40
41
  onGraph?: () => void;
42
+ /** Toggle the in-process Help overlay from `/hotkeys`. */
43
+ onHelp?: () => void;
41
44
  /** Detach this viewer (`/quit`). A viewer quit DETACHES — it tears down the
42
45
  * local TUI/socket and leaves the shared engine running. Wired by viewer.ts to
43
46
  * `teardown('detach')`; without it `/quit` cannot actually exit (just sending
@@ -47,6 +50,8 @@ export interface SlashContext {
47
50
  /** Copy the last assistant message to the clipboard (`/copy`). Wired by
48
51
  * viewer.ts (it owns the ChatView that holds the text + the clipboard writer). */
49
52
  onCopy?: () => void;
53
+ /** Copy one viewer-local transcript target. */
54
+ onCopyTarget?: (target: CopyTarget) => void;
50
55
  /** OPTIONAL native-picker openers — the InputController fills these from its
51
56
  * read-op request channel. When absent (channel unwired) the value-picker
52
57
  * builtins fall back to the text-arg notice. */
@@ -75,6 +80,11 @@ export interface SlashContext {
75
80
  * viewer-local render state (ChatView owns it), so it is safe for an observer
76
81
  * and for a remote attach. Absent → the command notifies it is unwired. */
77
82
  onFoldTools?: () => void;
83
+ /** Toggle mouse wheel scrolling reporting on/off. Viewer-local render state
84
+ * (MouseReporting owns the reporting state and the hardware toggle), so it is
85
+ * safe for an observer and for a remote attach. Absent → the command notifies
86
+ * it is unwired. */
87
+ onToggleMouse?: () => void;
78
88
  }
79
89
  /** Canvas slash-commands — implemented NATIVE in the viewer, the ONLY live host:
80
90
  * the broker binds mode:'rpc', so broker pi-extensions never run their
@@ -42,6 +42,10 @@ export const CANVAS_SLASH_COMMANDS = [
42
42
  { name: 'mcp', description: 'MCP servers — status from the adapter’s config/cache; Enter (or /mcp <server>) connects, reconnects, or re-auths' },
43
43
  { name: 'rename', description: 'Rename this node everywhere — node row, viewer window, session chip, tmux pane: /rename <name>' },
44
44
  { name: 'color', description: 'Color this node — tmux pane outline + name chip: /color <color> (red, blue, #rrggbb, …) or /color none' },
45
+ { name: 'mouse', description: 'Toggle mouse wheel scrolling — off restores tmux drag-select and copy-mode mouse' },
46
+ { name: 'copy-message', description: 'Copy the newest message visible in the transcript viewport' },
47
+ { name: 'copy-tool', description: 'Copy the newest tool call output' },
48
+ { name: 'copy-transcript', description: 'Copy every copyable transcript block' },
45
49
  { name: 'fold-tools', description: 'View setting — fold every FINISHED tool call to its single call line (Alt+C → w → f)' },
46
50
  ];
47
51
  const CANVAS_NAMES = new Set(CANVAS_SLASH_COMMANDS.map((c) => c.name));
@@ -301,10 +305,14 @@ export function dispatchSlashCommand(text, ctx) {
301
305
  case 'changelog':
302
306
  ctx.notify('/changelog: see https://github.com/earendil-works/pi for release notes (no bundled changelog in this build)');
303
307
  return true;
304
- // --- recognized builtins with no Phase-4 wire path --------------------
305
308
  case 'hotkeys':
306
- ctx.notify(`/${name} is not available in attach (Phase 4)`);
309
+ if (ctx.onHelp) {
310
+ ctx.onHelp();
311
+ return true;
312
+ }
313
+ ctx.notify('/hotkeys is not available in this viewer');
307
314
  return true;
315
+ // --- recognized builtins with no Phase-4 wire path --------------------
308
316
  default:
309
317
  // A builtin we listed but didn't special-case: treat as unsupported
310
318
  // rather than leak it to the LLM as literal text.
@@ -402,6 +410,31 @@ function dispatchCanvasCommand(name, arg, ctx) {
402
410
  else
403
411
  ctx.notify('The fold-tools view setting is not wired into this viewer');
404
412
  return true;
413
+ case 'mouse':
414
+ // Viewer-local render state only — no frame, no CLI, no node mutation.
415
+ if (ctx.onToggleMouse)
416
+ ctx.onToggleMouse();
417
+ else
418
+ ctx.notify('Mouse reporting is not wired into this viewer');
419
+ return true;
420
+ case 'copy-message':
421
+ if (ctx.onCopyTarget)
422
+ ctx.onCopyTarget('message');
423
+ else
424
+ ctx.notify('/copy-message is not available in this viewer');
425
+ return true;
426
+ case 'copy-tool':
427
+ if (ctx.onCopyTarget)
428
+ ctx.onCopyTarget('tool');
429
+ else
430
+ ctx.notify('/copy-tool is not available in this viewer');
431
+ return true;
432
+ case 'copy-transcript':
433
+ if (ctx.onCopyTarget)
434
+ ctx.onCopyTarget('transcript');
435
+ else
436
+ ctx.notify('/copy-transcript is not available in this viewer');
437
+ return true;
405
438
  default:
406
439
  ctx.notify(`/${name} is not available in attach`);
407
440
  return true;