@north-light/crouter 0.3.331 → 0.3.333

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 (128) hide show
  1. package/README.md +2 -2
  2. package/dist/api/command-manifest/manifest.js +1 -1
  3. package/dist/build-root.js +1 -1
  4. package/dist/builtin-memory/crouter-concepts/INDEX.md +13 -0
  5. package/dist/builtin-memory/crouter-concepts/README.md +19 -0
  6. package/dist/builtin-memory/crouter-concepts/lifecycle-and-wakes.md +28 -0
  7. package/dist/builtin-memory/crouter-concepts/memory.md +36 -0
  8. package/dist/builtin-memory/crouter-concepts/nodes-and-the-canvas.md +30 -0
  9. package/dist/builtin-memory/crouter-concepts/profiles-kinds-and-modes.md +33 -0
  10. package/dist/builtin-memory/crouter-concepts/scopes-and-trust.md +34 -0
  11. package/dist/builtin-memory/crouter-concepts/why-a-daemon.md +31 -0
  12. package/dist/builtin-memory/crouter-plugin/README.md +10 -13
  13. package/dist/builtin-memory/crouter-plugin/bundles-and-memory.md +2 -4
  14. package/dist/builtin-memory/crouter-plugin/commands.md +4 -7
  15. package/dist/builtin-memory/crouter-plugin/deploying.md +4 -7
  16. package/dist/builtin-memory/crouter-plugin/errors.md +2 -5
  17. package/dist/builtin-memory/crouter-plugin/getting-started.md +3 -6
  18. package/dist/builtin-memory/crouter-plugin/output.md +1 -3
  19. package/dist/builtin-memory/crouter-plugin/parameters.md +3 -4
  20. package/dist/builtin-memory/crouter-sdk/README.md +3 -4
  21. package/dist/builtin-memory/crouter-sdk/bash.md +2 -3
  22. package/dist/builtin-memory/crouter-sdk/client.md +3 -4
  23. package/dist/builtin-memory/crouter-sdk/docker.md +2 -4
  24. package/dist/builtin-memory/crouter-sdk/errors.md +9 -4
  25. package/dist/builtin-memory/crouter-sdk/files.md +2 -3
  26. package/dist/builtin-memory/crouter-sdk/getting-started.md +19 -6
  27. package/dist/builtin-memory/crouter-sdk/memory.md +3 -4
  28. package/dist/builtin-memory/crouter-sdk/migration.md +2 -6
  29. package/dist/builtin-memory/crouter-sdk/nodes.md +3 -3
  30. package/dist/builtin-memory/crouter-sdk/resources.md +2 -4
  31. package/dist/builtin-memory/crouter-sdk/streaming.md +3 -4
  32. package/dist/clients/attach/input/capabilities.js +1 -1
  33. package/dist/clients/attach/overlays/help.js +1 -1
  34. package/dist/clients/attach/slash/dispatch.js +1 -1
  35. package/dist/clients/attach/viewer.js +762 -765
  36. package/dist/commands/sys/branch.js +1 -1
  37. package/dist/commands/sys/connect.js +3 -3
  38. package/dist/commands/sys/setup-core.d.ts +1 -1
  39. package/dist/commands/sys/setup-core.js +4 -4
  40. package/dist/commands/sys/tutorial/branch.d.ts +1 -0
  41. package/dist/commands/sys/tutorial/branch.js +1 -0
  42. package/dist/commands/sys/tutorial/lessons.d.ts +6 -0
  43. package/dist/commands/sys/tutorial/lessons.js +12 -0
  44. package/dist/commands/sys/tutorial/scenario.d.ts +1 -0
  45. package/dist/commands/sys/tutorial/scenario.js +2 -0
  46. package/dist/commands/sys/tutorial/tracks.d.ts +2 -0
  47. package/dist/commands/sys/tutorial/tracks.js +4 -0
  48. package/dist/commands/sys.js +1 -1
  49. package/dist/core/runtime/boot-root.d.ts +7 -0
  50. package/dist/core/runtime/boot-root.js +4 -4
  51. package/dist/core/runtime/first-run-offer.d.ts +11 -0
  52. package/dist/core/runtime/first-run-offer.js +5 -0
  53. package/dist/core/runtime/front-door.js +3 -3
  54. package/dist/core/runtime/spawn.d.ts +3 -1
  55. package/dist/core/runtime/spawn.js +2 -2
  56. package/dist/core/scopes.d.ts +4 -2
  57. package/dist/core/scopes.js +1 -1
  58. package/dist/core/secrets.d.ts +11 -0
  59. package/dist/core/secrets.js +2 -2
  60. package/dist/daemon/api/bridge.d.ts +4 -0
  61. package/dist/daemon/api/bridge.js +2 -2
  62. package/dist/daemon/api/handlers/attach.d.ts +1 -1
  63. package/dist/daemon/api/handlers/attach.js +1 -1
  64. package/dist/daemon/api/handlers/bash.js +1 -1
  65. package/dist/daemon/api/handlers/broker-ops.d.ts +1 -1
  66. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  67. package/dist/daemon/api/handlers/broker-recovery.d.ts +1 -1
  68. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  69. package/dist/daemon/api/handlers/canvas.js +4 -4
  70. package/dist/daemon/api/handlers/crons.d.ts +1 -1
  71. package/dist/daemon/api/handlers/crons.js +1 -1
  72. package/dist/daemon/api/handlers/daemon.d.ts +1 -1
  73. package/dist/daemon/api/handlers/daemon.js +1 -1
  74. package/dist/daemon/api/handlers/files.js +1 -1
  75. package/dist/daemon/api/handlers/focus.d.ts +1 -1
  76. package/dist/daemon/api/handlers/focus.js +1 -1
  77. package/dist/daemon/api/handlers/human-requests.js +1 -1
  78. package/dist/daemon/api/handlers/memory.js +1 -1
  79. package/dist/daemon/api/handlers/messages.d.ts +1 -1
  80. package/dist/daemon/api/handlers/messages.js +2 -2
  81. package/dist/daemon/api/handlers/model-config.d.ts +1 -1
  82. package/dist/daemon/api/handlers/model-config.js +1 -1
  83. package/dist/daemon/api/handlers/modelauth.js +1 -1
  84. package/dist/daemon/api/handlers/nodes.js +1 -1
  85. package/dist/daemon/api/handlers/profiles.js +1 -1
  86. package/dist/daemon/api/handlers/reports.js +1 -1
  87. package/dist/daemon/api/handlers/reviews.js +1 -1
  88. package/dist/daemon/api/handlers/worktree.d.ts +1 -1
  89. package/dist/daemon/api/handlers/worktree.js +1 -1
  90. package/dist/daemon/api/router.d.ts +20 -1
  91. package/dist/daemon/api/router.js +1 -1
  92. package/dist/daemon/api/server.js +1 -11
  93. package/dist/types.js +1 -1
  94. package/docs/cli/README.md +16 -0
  95. package/docs/cli/canvas-and-dashboard.md +56 -0
  96. package/docs/cli/first-session.md +55 -0
  97. package/docs/cli/human-inbox.md +34 -0
  98. package/docs/cli/memory-and-preferences.md +50 -0
  99. package/docs/cli/the-viewer.md +79 -0
  100. package/docs/concepts/README.md +17 -0
  101. package/docs/concepts/lifecycle-and-wakes.md +26 -0
  102. package/docs/concepts/memory.md +34 -0
  103. package/docs/concepts/nodes-and-the-canvas.md +28 -0
  104. package/docs/concepts/profiles-kinds-and-modes.md +31 -0
  105. package/docs/concepts/scopes-and-trust.md +32 -0
  106. package/docs/concepts/why-a-daemon.md +29 -0
  107. package/docs/plugin/README.md +5 -0
  108. package/docs/plugin/bundles-and-memory.md +5 -0
  109. package/docs/plugin/commands.md +5 -0
  110. package/docs/plugin/deploying.md +5 -0
  111. package/docs/plugin/errors.md +5 -0
  112. package/docs/plugin/getting-started.md +5 -0
  113. package/docs/plugin/output.md +5 -0
  114. package/docs/plugin/parameters.md +5 -0
  115. package/docs/sdk/README.md +5 -0
  116. package/docs/sdk/bash.md +5 -0
  117. package/docs/sdk/client.md +6 -1
  118. package/docs/sdk/docker.md +5 -0
  119. package/docs/sdk/errors.md +12 -0
  120. package/docs/sdk/files.md +5 -0
  121. package/docs/sdk/getting-started.md +21 -1
  122. package/docs/sdk/memory.md +5 -0
  123. package/docs/sdk/migration.md +5 -0
  124. package/docs/sdk/nodes.md +6 -1
  125. package/docs/sdk/resources.md +5 -0
  126. package/docs/sdk/streaming.md +5 -0
  127. package/package.json +10 -9
  128. package/runtime.lock.json +6734 -670
@@ -1,8 +1,7 @@
1
1
  ---
2
2
  kind: knowledge
3
- when-and-why-to-read: When you need `client.files`, this knowledge should be
4
- read because `client.files` reads, writes, and lists host files through the
5
- daemon.
3
+ when-and-why-to-read: When you need Files, this knowledge should be read because
4
+ Read, write, and list host files through the daemon.
6
5
  ---
7
6
 
8
7
  # `client.files`
@@ -1,10 +1,8 @@
1
1
  ---
2
2
  kind: knowledge
3
- when-and-why-to-read: "When you need Getting started, this knowledge should be
4
- read because An agent run needs a crouter daemon (`crtrd`) to run it. The SDK
5
- is a client for that daemon — it never runs an agent itself. There are two
6
- ways to reach one: the unix socket on the machine you are running on, or a TCP
7
- listener with a bearer token."
3
+ when-and-why-to-read: When you need Getting started, this knowledge should be
4
+ read because Connect to a local or remote daemon and run your first agent with
5
+ the SDK.
8
6
  ---
9
7
 
10
8
  # Getting started
@@ -53,7 +51,7 @@ if (outcome.kind === 'result') console.log(outcome.final_report_path);
53
51
 
54
52
  ## 4. A browser or a remote application
55
53
 
56
- A process that is not on the daemon's machine — or a page in a browser, which has no unix sockets at all — reaches the daemon over TCP with a bearer token. The token is the owner credential: whoever holds it can drive the whole surface.
54
+ A process that is not on the daemon's machine — or a page in a browser, which has no unix sockets at all — reaches the daemon over TCP with a bearer token. There are two kinds. The **owner token** is the owner credential: whoever holds it can drive the whole surface. A **scoped token** (`crtr sys connect --scopes`) carries a list of scopes that caps what its holder and every run it creates may do.
57
55
 
58
56
  ### Turn the listener on, once
59
57
 
@@ -72,6 +70,20 @@ $ crtr --json sys connect
72
70
  {"base_url":"http://127.0.0.1:8787","token":"<64-character bearer token>"}
73
71
  ```
74
72
 
73
+ ### A token that holds less than the owner
74
+
75
+ ```bash
76
+ crtr sys connect --scopes ask,memory:read
77
+ ```
78
+
79
+ This mints a new token whose scope list is a ceiling, appends it to the same secrets store, prints it in place of the owner token, and always hands the daemon over (it reads tokens once, at boot). The ceiling is checked on every request that carries the token:
80
+
81
+ - A scope-gated operation the ceiling lacks answers `403 scope_denied`. Creating, reviving, forking, messaging, or yielding a run needs `act`; creating a review or human request needs `ask`; arming or running a cron needs `schedule`; memory reads and writes need `memory:read` and `memory:write`. `bash` and `files` also need `act`: they run code and touch the host directly, which is more than any run does, and `files:<dir>` is recorded, not enforced, so it cannot narrow them.
82
+ - `nodes.create` with `scopes` outside the ceiling answers `403 scope_denied`; `details.scopes` lists the offending scopes. Omit `scopes` and the run gets the ceiling.
83
+ - Owner-only operations — daemon restart, the attach viewer, broker internals, canvas prune, profile pause/resume/delete, model credential install — answer `403 owner_only` to any scoped token.
84
+
85
+ What a scope does **not** do on a developer's machine: there is no OS sandbox. Scopes gate the daemon and the CLI; a run's own bash tool is ungated, and the daemon identifies a calling node by a field the caller declares, so a scope check bounds a well-behaved agent, not a hostile one. `llm`, `files:<dir>`, `net`, and provider scopes are recorded on the run but not enforced. There is no list or revoke verb yet; to retire a scoped token, remove it from the 0600 user secrets store and restart the daemon.
86
+
75
87
  ### Check setup before generating
76
88
 
77
89
  Construct the client from the application's saved connection. On the first visit that value is absent, and `client.auth.status()` returns `'connect'` without a request. Once the user pastes the base URL and token printed by `crtr sys connect`, construct it again and call `status()` to check the selected provider.
@@ -162,6 +174,7 @@ An outcome is returned, never thrown — including a decline and a failure. Only
162
174
 
163
175
  ## Where to go next
164
176
 
177
+ - A complete local page that demonstrates streaming, structured output, and a plugin: [localhost SDK demo](../../examples/localhost-demo)
165
178
  - Every constructor option and environment-variable fallback: [[crouter-sdk/client]]
166
179
  - Checking the daemon connection and selected provider before a run: `client.auth.status()` above
167
180
  - The full create-parameter table and the outcome union: [[crouter-sdk/nodes]]
@@ -1,9 +1,8 @@
1
1
  ---
2
2
  kind: knowledge
3
- when-and-why-to-read: When you need `client.memory`, this knowledge should be
4
- read because Phase 3. `client.memory` reads and writes the memory documents
5
- that shape an agent run. Every call names its target; the daemon never uses
6
- its own working directory or environment to select memory.
3
+ when-and-why-to-read: When you need Memory, this knowledge should be read
4
+ because Read and write memory documents with an explicit target for every
5
+ request.
7
6
  ---
8
7
 
9
8
  # `client.memory`
@@ -1,11 +1,7 @@
1
1
  ---
2
2
  kind: knowledge
3
- when-and-why-to-read: When you need Migration from `generate()` and `local()`,
4
- this knowledge should be read because **This is a hard cut.** `generate()`,
5
- `local()`, and the `Environment` interface are deleted in the same release
6
- that adds `Crouter`. There is no coexistence period, no deprecated re-export,
7
- and no compatibility shim. Both packages are pre-1.0 and the callers are
8
- countable.
3
+ when-and-why-to-read: When you need Migration, this knowledge should be read
4
+ because Replace the removed generate and local APIs with the Crouter client.
9
5
  ---
10
6
 
11
7
  # Migration from `generate()` and `local()`
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  kind: knowledge
3
- when-and-why-to-read: When you need `client.nodes`, this knowledge should be
4
- read because Shipped, except where a row says otherwise.
3
+ when-and-why-to-read: When you need Nodes, this knowledge should be read because
4
+ Create agent runs, wait for outcomes, and parse structured results.
5
5
  ---
6
6
 
7
7
  # `client.nodes`
@@ -64,7 +64,7 @@ Wire fields are `snake_case`. Every `NodeCreateParams` property is optional; `pa
64
64
  | `description` | `string` | Display description. |
65
65
  | `parent` | node id | Graph placement. An external caller leaves this unset. |
66
66
  | `creator` | node id | Graph placement. An external caller leaves this unset. |
67
- | `scopes` | `string[]` | Per-run allow-list. Omit it to inherit every scope. In beta, `ask`, `act`, `schedule`, `memory:read`, and `memory:write` are enforced; `llm`, `files:<dir>`, `net`, provider groups, and peers are recorded because their performers are not available. |
67
+ | `scopes` | `string[]` | Per-run allow-list. Omit it to inherit every scope, or under a scoped token to receive that token's ceiling; a list outside the ceiling answers `403 scope_denied` with the offending scopes in `details.scopes`. In beta, `ask`, `act`, `schedule`, `memory:read`, and `memory:write` are enforced; `llm`, `files:<dir>`, `net`, provider groups, and peers are recorded because their performers are not available. See [[crouter-sdk/getting-started]]. |
68
68
  | `worktree` | `string \| boolean` | Create a managed git worktree for the run. |
69
69
  | `fork_from` | `string` | Start from an existing conversation. |
70
70
  | `no_kickoff` | `boolean` | Create the node without sending the first message. |
@@ -1,10 +1,8 @@
1
1
  ---
2
2
  kind: knowledge
3
3
  when-and-why-to-read: When you need Resource map, this knowledge should be read
4
- because Every namespace exported by the client. Namespaces are camelCase.
5
- Verbs are `create`, `retrieve`, `list`, `update`, `delete`, and `cancel`,
6
- except where the product already has a literal name for the action (`fork`,
7
- `revive`, `promote`, `pause`, `poke`) — in which case the SDK uses that name.
4
+ because Find client namespaces, their daemon routes, and the raw request
5
+ escape hatch.
8
6
  ---
9
7
 
10
8
  # Resource map
@@ -1,9 +1,8 @@
1
1
  ---
2
2
  kind: knowledge
3
- when-and-why-to-read: "When you need Streaming, this knowledge should be read
4
- because Watch a run as it works: assistant text as it is produced, tool calls
5
- as they start and finish, reports as they are pushed, and the settled
6
- outcome."
3
+ when-and-why-to-read: When you need Streaming, this knowledge should be read
4
+ because Follow assistant text, tool activity, reports, and outcomes as an
5
+ agent runs.
7
6
  ---
8
7
 
9
8
  # Streaming
@@ -1 +1 @@
1
- var o=Object.defineProperty;var r=(e,s)=>o(e,"name",{value:s,configurable:!0});function l(e){const s=e.remote,t=s?"observer":e.role,a=new Set;return s||(a.add("local-canvas-inspect"),a.add("canvas-inbox")),t==="controller"&&(a.add("engine-drive"),a.add("dequeue"),a.add("wake-dormant"),a.add("auth"),a.add("canvas-resume")),{role:t,remote:s,nodeId:e.nodeId,profileId:e.profileId,permittedOperations:a,permits:r(n=>a.has(n),"permits"),refusal:r((n,i)=>{const c=i===void 0?"":`/${i} `;if(e.isBrokerOffline?.()===!0&&(n==="engine-drive"||n==="dequeue"||n==="auth"))return`${c}needs a live broker; this node is parked \u2014 submit a message to wake it`;if(!a.has(n))return s&&(n==="canvas-resume"||n==="local-canvas-inspect"||n==="canvas-inbox")?`${c}Read-only remote attach \u2014 not available with --canvas because it acts on this machine's local canvas`:s?`${c}Read-only remote attach \u2014 not available with --canvas (view-only)`:`${c}is not available to a read-only observer attach (--observer) \u2014 it would drive this node`},"refusal")}}r(l,"createViewerCapabilities");function u(e){switch(e.type){case"prompt":case"steer":case"follow_up":case"abort":case"bash":case"set_model":case"cycle_model":case"cycle_ladder":case"cycle_thinking":case"set_thinking_level":case"set_auto_retry":case"set_auto_compaction":case"compact":case"new_session":case"switch_session":case"fork":case"set_session_name":case"navigate_tree":case"reload":case"export":case"clone":case"share":case"reload_auth":return"engine-drive";default:return}}r(u,"viewerOperationForFrame");function v(e,s,t){if(t)switch(e){case"resume":return"canvas-resume";case"context":case"node-metadata":return"local-canvas-inspect";case"plugins":return;case"inbox":return"canvas-inbox";case"clear":case"promote":case"rename":case"resume-session":case"mcp":return"engine-drive";default:return}if(s)switch(e){case"copy":case"session":case"changelog":case"hotkeys":case"quit":return;case"login":case"logout":return"auth";default:return"engine-drive"}}r(v,"viewerOperationForSlash");export{l as createViewerCapabilities,u as viewerOperationForFrame,v as viewerOperationForSlash};
1
+ var o=Object.defineProperty;var r=(e,s)=>o(e,"name",{value:s,configurable:!0});function l(e){const s=e.remote,t=s?"observer":e.role,a=new Set;return s||(a.add("local-canvas-inspect"),a.add("canvas-inbox")),t==="controller"&&(a.add("engine-drive"),a.add("dequeue"),a.add("wake-dormant"),a.add("auth"),a.add("canvas-resume")),{role:t,remote:s,nodeId:e.nodeId,profileId:e.profileId,permittedOperations:a,permits:r(n=>a.has(n),"permits"),refusal:r((n,i)=>{const c=i===void 0?"":`/${i} `;if(e.isBrokerOffline?.()===!0&&(n==="engine-drive"||n==="dequeue"||n==="auth"))return`${c}needs a live broker; this node is parked \u2014 submit a message to wake it`;if(!a.has(n))return s&&(n==="canvas-resume"||n==="local-canvas-inspect"||n==="canvas-inbox")?`${c}Read-only remote attach \u2014 not available with --canvas because it acts on this machine's local canvas`:s?`${c}Read-only remote attach \u2014 not available with --canvas (view-only)`:`${c}is not available to a read-only observer attach (--observer) \u2014 it would drive this node`},"refusal")}}r(l,"createViewerCapabilities");function u(e){switch(e.type){case"prompt":case"steer":case"follow_up":case"abort":case"bash":case"set_model":case"cycle_model":case"cycle_ladder":case"cycle_thinking":case"set_thinking_level":case"set_auto_retry":case"set_auto_compaction":case"compact":case"new_session":case"switch_session":case"fork":case"set_session_name":case"navigate_tree":case"reload":case"export":case"clone":case"share":case"reload_auth":return"engine-drive";default:return}}r(u,"viewerOperationForFrame");function v(e,s,t){if(t)switch(e){case"resume":return"canvas-resume";case"context":case"node-metadata":return"local-canvas-inspect";case"plugins":return;case"inbox":return"canvas-inbox";case"clear":case"promote":case"rename":case"resume-session":case"tutorial":case"mcp":return"engine-drive";default:return}if(s)switch(e){case"copy":case"session":case"changelog":case"hotkeys":case"quit":return;case"login":case"logout":return"auth";default:return"engine-drive"}}r(v,"viewerOperationForSlash");export{l as createViewerCapabilities,u as viewerOperationForFrame,v as viewerOperationForSlash};
@@ -1 +1 @@
1
- var w=Object.defineProperty;var d=(g,t)=>w(g,"name",{value:t,configurable:!0});import{matchesKey as h,truncateToWidth as c,visibleWidth as m}from"@earendil-works/pi-tui";import{ATTACH_CONTROL_BINDINGS as x,BINDING_CATALOG as T,formatBinding as $,matchesPiTuiInput as M}from"../../../core/keybindings/index.js";const R=.72,v={anchor:"center",width:"72%",minWidth:48,maxHeight:"72%",margin:1};class C{static{d(this,"HelpOverlay")}tui;pal;handle;bindings;scrollTop=0;lastContentRows=1;constructor(t,e){this.tui=t,this.pal=e}setBindings(t){this.bindings=t,this.handle!==void 0&&this.tui.requestRender()}toggle(){this.handle===void 0?this.open():this.close()}close(){this.handle!==void 0&&(this.handle.hide(),this.handle=void 0,this.tui.requestRender())}isOpen(){return this.handle!==void 0}invalidate(){this.handle!==void 0&&this.tui.requestRender()}render(t){const e=Math.max(1,t);if(e<5)return this.renderNarrow(e);const i=e-4,s=this.body(i),o=Math.max(1,this.tui.terminal.rows),n=Math.max(3,Math.min(Math.floor(o*R),Math.max(3,o-2))),r=Math.max(1,n-2),l=s.length>r,u=l&&r>=3?r-2:1;this.lastContentRows=u,this.scrollTop=Math.max(0,Math.min(this.scrollTop,Math.max(0,s.length-u)));const p=Math.min(s.length,this.scrollTop+u),a=[];for(l&&r>=3&&a.push(this.scrollTop>0?this.pal.muted(`\u2191 ${this.scrollTop} more`):""),a.push(...s.slice(this.scrollTop,p)),l&&r>=3?a.push(p<s.length?this.pal.muted(`\u2193 ${s.length-p} more`):""):l&&r===2&&a.push(p<s.length?this.pal.muted(`\u2193 ${s.length-p} more`):"");a.length<r;)a.push("");const y=this.pal.bold(this.pal.accent("\u2328 attach help")),b=this.pal.muted("esc dismiss \xB7 \u2191\u2193 scroll");return[this.borderRow("\u256D","\u256E",y,e),...a.map(f=>this.boxRow(f,e)),this.borderRow("\u2570","\u256F",b,e)].map(this.pal.surface)}handleInput(t){return h(t,"escape")||h(t,"q")?(this.close(),!0):h(t,"up")||this.matchesBinding("crtr.attach.scroll.up",t)?this.scrollBy(-1):h(t,"down")||this.matchesBinding("crtr.attach.scroll.down",t)?this.scrollBy(1):h(t,"pageUp")||this.matchesBinding("crtr.attach.scroll.page-up",t)?this.scrollBy(-this.pageSize()):h(t,"pageDown")||this.matchesBinding("crtr.attach.scroll.page-down",t)?this.scrollBy(this.pageSize()):!0}open(){this.scrollTop=0,this.handle=this.tui.showOverlay(this,v)}matchesBinding(t,e){return this.bindings!==void 0&&M(this.bindings,t,e,(i,s)=>h(i,s))}scrollBy(t){const e=this.body(Math.max(1,this.tui.terminal.columns-4)).length,i=Math.max(0,e-this.lastContentRows),s=Math.max(0,Math.min(i,this.scrollTop+t));return s!==this.scrollTop&&(this.scrollTop=s,this.tui.requestRender()),!0}pageSize(){return Math.max(1,this.lastContentRows-1)}body(t){return this.lines().flatMap(e=>this.wrap(e.text,t).map(i=>this.style(i,e.style)))}lines(){const t=d((n,r)=>({text:`${this.binding(n)} ${r}`,style:"plain"}),"binding"),e=d(n=>({text:n,style:"heading"}),"heading"),i=d(n=>({text:n,style:"muted"}),"muted"),s={text:"",style:"plain"},o=x.map(({actionId:n})=>{const r=T.find(l=>l.id===n)?.name??n;return t(n,r)});return[e("Scrolling"),t("crtr.attach.scroll.up","scroll one line up"),t("crtr.attach.scroll.down","scroll one line down"),t("crtr.attach.scroll.page-up","scroll one page up"),t("crtr.attach.scroll.page-down","scroll one page down"),t("crtr.attach.scroll.live","return to the live bottom"),i("mouse wheel (when reporting is on)"),s,e("Searching the transcript"),{text:`${this.binding("crtr.attach.search")} search what the transcript is currently showing. Type to jump as you go, enter to browse the matches.`,style:"plain"},{text:`${this.binding("crtr.attach.search.next")} / ${this.binding("crtr.attach.search.previous")} next and previous match \xB7 ${this.binding("crtr.attach.search.edit")} edit the query \xB7 ${this.binding("crtr.attach.search.close")} close. Typing anything else closes search and returns to the editor.`,style:"plain"},{text:"A lowercase query ignores case; any uppercase letter makes it case-sensitive.",style:"plain"},{text:"Folded tool output is not on screen, so it is not searched \u2014 unfold it to include it.",style:"muted"},s,e("Selection and copy"),{text:"Drag with the mouse to select; releasing copies the selection to the clipboard (and the tmux buffer). Any key clears the highlight.",style:"plain"},{text:"prefix + [ tmux copy-mode selects and copies the visible screenful; it works even while mouse reporting is on.",style:"plain"},{text:"Option-drag (iTerm2, Terminal.app) or Shift-drag (Ghostty, Alacritty, xterm) makes a true terminal selection that bypasses mouse reporting.",style:"plain"},{text:"Block-copy commands copy transcript content and are not capped at one screenful.",style:"plain"},s,e("Copy commands"),{text:"/copy last assistant message.",style:"plain"},{text:`/copy-message (${this.binding("crtr.attach.copy.message")}) newest copyable block entirely visible in the viewport; otherwise the newest one that intersects it.`,style:"plain"},{text:"/copy-tool newest tool call's result text.",style:"plain"},{text:"/copy-transcript every copyable block's text, oldest to newest, joined by blank lines.",style:"plain"},s,e("Mouse"),{text:`/mouse or ${this.binding("crtr.attach.mouse.toggle")} toggle mouse reporting \u2014 wheel scrolling and viewer drag-select. Turning it off restores tmux drag-select and copy-mode mouse.`,style:"plain"},s,e("Other attach keys"),...o]}binding(t){return this.bindings===void 0?"Loading\u2026":$(this.bindings,t)}wrap(t,e){if(t==="")return[""];const i=Math.max(1,e),s=t.split(/\s+/),o=[];let n="";for(const r of s){const l=n===""?r:`${n} ${r}`;n!==""&&m(l)>i?(o.push(c(n,i,"",!0)),n=` ${r}`):n=l}return n!==""&&o.push(c(n,i,"",!0)),o}style(t,e){switch(e){case"heading":return this.pal.bold(this.pal.accent(t));case"muted":return this.pal.muted(t);case"plain":return t}}boxRow(t,e){const i=e-4,s=c(t,i,"",!0),o=" ".repeat(Math.max(0,i-m(s)));return`${this.pal.border("\u2502")} ${s}${o} ${this.pal.border("\u2502")}`}borderRow(t,e,i,s){if(s<6)return c(`${this.pal.border(t)}${i}${this.pal.border(e)}`,s,"",!0);const o=s-5,n=c(i,o,"\u2026"),r=Math.max(0,s-5-m(n));return`${this.pal.border(`${t}\u2500`)} ${n} ${this.pal.border("\u2500".repeat(r)+e)}`}renderNarrow(t){const e=c(this.pal.bold(this.pal.accent("help")),t,"",!0);return[this.pal.surface(e)]}}export{C as HelpOverlay};
1
+ var w=Object.defineProperty;var d=(g,t)=>w(g,"name",{value:t,configurable:!0});import{matchesKey as h,truncateToWidth as c,visibleWidth as m}from"@earendil-works/pi-tui";import{ATTACH_CONTROL_BINDINGS as x,BINDING_CATALOG as T,formatBinding as $,matchesPiTuiInput as M}from"../../../core/keybindings/index.js";const R=.72,v={anchor:"center",width:"72%",minWidth:48,maxHeight:"72%",margin:1};class C{static{d(this,"HelpOverlay")}tui;pal;handle;bindings;scrollTop=0;lastContentRows=1;constructor(t,e){this.tui=t,this.pal=e}setBindings(t){this.bindings=t,this.handle!==void 0&&this.tui.requestRender()}toggle(){this.handle===void 0?this.open():this.close()}close(){this.handle!==void 0&&(this.handle.hide(),this.handle=void 0,this.tui.requestRender())}isOpen(){return this.handle!==void 0}invalidate(){this.handle!==void 0&&this.tui.requestRender()}render(t){const e=Math.max(1,t);if(e<5)return this.renderNarrow(e);const i=e-4,s=this.body(i),o=Math.max(1,this.tui.terminal.rows),n=Math.max(3,Math.min(Math.floor(o*R),Math.max(3,o-2))),r=Math.max(1,n-2),l=s.length>r,u=l&&r>=3?r-2:1;this.lastContentRows=u,this.scrollTop=Math.max(0,Math.min(this.scrollTop,Math.max(0,s.length-u)));const p=Math.min(s.length,this.scrollTop+u),a=[];for(l&&r>=3&&a.push(this.scrollTop>0?this.pal.muted(`\u2191 ${this.scrollTop} more`):""),a.push(...s.slice(this.scrollTop,p)),l&&r>=3?a.push(p<s.length?this.pal.muted(`\u2193 ${s.length-p} more`):""):l&&r===2&&a.push(p<s.length?this.pal.muted(`\u2193 ${s.length-p} more`):"");a.length<r;)a.push("");const y=this.pal.bold(this.pal.accent("\u2328 attach help")),b=this.pal.muted("esc dismiss \xB7 \u2191\u2193 scroll");return[this.borderRow("\u256D","\u256E",y,e),...a.map(f=>this.boxRow(f,e)),this.borderRow("\u2570","\u256F",b,e)].map(this.pal.surface)}handleInput(t){return h(t,"escape")||h(t,"q")?(this.close(),!0):h(t,"up")||this.matchesBinding("crtr.attach.scroll.up",t)?this.scrollBy(-1):h(t,"down")||this.matchesBinding("crtr.attach.scroll.down",t)?this.scrollBy(1):h(t,"pageUp")||this.matchesBinding("crtr.attach.scroll.page-up",t)?this.scrollBy(-this.pageSize()):h(t,"pageDown")||this.matchesBinding("crtr.attach.scroll.page-down",t)?this.scrollBy(this.pageSize()):!0}open(){this.scrollTop=0,this.handle=this.tui.showOverlay(this,v)}matchesBinding(t,e){return this.bindings!==void 0&&M(this.bindings,t,e,(i,s)=>h(i,s))}scrollBy(t){const e=this.body(Math.max(1,this.tui.terminal.columns-4)).length,i=Math.max(0,e-this.lastContentRows),s=Math.max(0,Math.min(i,this.scrollTop+t));return s!==this.scrollTop&&(this.scrollTop=s,this.tui.requestRender()),!0}pageSize(){return Math.max(1,this.lastContentRows-1)}body(t){return this.lines().flatMap(e=>this.wrap(e.text,t).map(i=>this.style(i,e.style)))}lines(){const t=d((n,r)=>({text:`${this.binding(n)} ${r}`,style:"plain"}),"binding"),e=d(n=>({text:n,style:"heading"}),"heading"),i=d(n=>({text:n,style:"muted"}),"muted"),s={text:"",style:"plain"},o=x.map(({actionId:n})=>{const r=T.find(l=>l.id===n)?.name??n;return t(n,r)});return[e("Learning crouter"),{text:"/tutorial the guided crouter tutorial \u2014 your agent teaches it, one lesson at a time",style:"plain"},s,e("Scrolling"),t("crtr.attach.scroll.up","scroll one line up"),t("crtr.attach.scroll.down","scroll one line down"),t("crtr.attach.scroll.page-up","scroll one page up"),t("crtr.attach.scroll.page-down","scroll one page down"),t("crtr.attach.scroll.live","return to the live bottom"),i("mouse wheel (when reporting is on)"),s,e("Searching the transcript"),{text:`${this.binding("crtr.attach.search")} search what the transcript is currently showing. Type to jump as you go, enter to browse the matches.`,style:"plain"},{text:`${this.binding("crtr.attach.search.next")} / ${this.binding("crtr.attach.search.previous")} next and previous match \xB7 ${this.binding("crtr.attach.search.edit")} edit the query \xB7 ${this.binding("crtr.attach.search.close")} close. Typing anything else closes search and returns to the editor.`,style:"plain"},{text:"A lowercase query ignores case; any uppercase letter makes it case-sensitive.",style:"plain"},{text:"Folded tool output is not on screen, so it is not searched \u2014 unfold it to include it.",style:"muted"},s,e("Selection and copy"),{text:"Drag with the mouse to select; releasing copies the selection to the clipboard (and the tmux buffer). Any key clears the highlight.",style:"plain"},{text:"prefix + [ tmux copy-mode selects and copies the visible screenful; it works even while mouse reporting is on.",style:"plain"},{text:"Option-drag (iTerm2, Terminal.app) or Shift-drag (Ghostty, Alacritty, xterm) makes a true terminal selection that bypasses mouse reporting.",style:"plain"},{text:"Block-copy commands copy transcript content and are not capped at one screenful.",style:"plain"},s,e("Copy commands"),{text:"/copy last assistant message.",style:"plain"},{text:`/copy-message (${this.binding("crtr.attach.copy.message")}) newest copyable block entirely visible in the viewport; otherwise the newest one that intersects it.`,style:"plain"},{text:"/copy-tool newest tool call's result text.",style:"plain"},{text:"/copy-transcript every copyable block's text, oldest to newest, joined by blank lines.",style:"plain"},s,e("Mouse"),{text:`/mouse or ${this.binding("crtr.attach.mouse.toggle")} toggle mouse reporting \u2014 wheel scrolling and viewer drag-select. Turning it off restores tmux drag-select and copy-mode mouse.`,style:"plain"},s,e("Other attach keys"),...o]}binding(t){return this.bindings===void 0?"Loading\u2026":$(this.bindings,t)}wrap(t,e){if(t==="")return[""];const i=Math.max(1,e),s=t.split(/\s+/),o=[];let n="";for(const r of s){const l=n===""?r:`${n} ${r}`;n!==""&&m(l)>i?(o.push(c(n,i,"",!0)),n=` ${r}`):n=l}return n!==""&&o.push(c(n,i,"",!0)),o}style(t,e){switch(e){case"heading":return this.pal.bold(this.pal.accent(t));case"muted":return this.pal.muted(t);case"plain":return t}}boxRow(t,e){const i=e-4,s=c(t,i,"",!0),o=" ".repeat(Math.max(0,i-m(s)));return`${this.pal.border("\u2502")} ${s}${o} ${this.pal.border("\u2502")}`}borderRow(t,e,i,s){if(s<6)return c(`${this.pal.border(t)}${i}${this.pal.border(e)}`,s,"",!0);const o=s-5,n=c(i,o,"\u2026"),r=Math.max(0,s-5-m(n));return`${this.pal.border(`${t}\u2500`)} ${n} ${this.pal.border("\u2500".repeat(r)+e)}`}renderNarrow(t){const e=c(this.pal.bold(this.pal.accent("help")),t,"",!0);return[this.pal.surface(e)]}}export{C as HelpOverlay};
@@ -1 +1 @@
1
- var E=Object.defineProperty;var i=(o,e)=>E(o,"name",{value:e,configurable:!0});import{execFile as p}from"node:child_process";import{BUILTIN_SLASH_COMMANDS as h}from"../../../core/runtime/pi-vendored.js";import{surfaceTmuxStyleArgs as O}from"../../../core/runtime/surface-bg.js";import{viewerOperationForSlash as _}from"../input/capabilities.js";import{shellQuote as y}from"../../../shared/shell-quote.js";const g=[{name:"clear",description:"Clear the conversation and free context \u2014 a fresh, empty session (like /new)"},{name:"graph",description:"Toggle the canvas GRAPH view (your local subscription tree)"},{name:"inbox",description:"Toggle the human inbox \u2014 pending asks and reviews for you"},{name:"promote",description:"Promote this node to an orchestrator \u2014 /promote, or /promote <kind> to specialize"},{name:"resume",description:"Open the canvas navigator and swap THIS pane onto the chosen node; parked residents wake only on submitted input"},{name:"resume-session",description:"Switch this node onto one of its saved pi sessions \u2014 picker, or /resume-session <session-path>"},{name:"context",description:"Browse this node + its subscriptions\u2019 context dirs in a read-only nvim popup"},{name:"plugins",description:"Open the interactive plugin browser for this machine"},{name:"node-metadata",description:"Inspect this node \u2014 identity, lifecycle, runtime, graph, artifacts, paths, and agent surfaces"},{name:"mcp",description:"MCP servers \u2014 status from the adapter\u2019s config/cache; Enter (or /mcp <server>) connects, reconnects, or re-auths"},{name:"rename",description:"Rename this node everywhere \u2014 node row, viewer window, session chip, tmux pane: /rename <name>"},{name:"color",description:"Color this node \u2014 tmux pane outline + name chip: /color <color> (red, blue, #rrggbb, \u2026) or /color none"},{name:"mouse",description:"Toggle mouse wheel scrolling and drag-select \u2014 off restores tmux drag-select and copy-mode mouse"},{name:"copy-message",description:"Copy the newest message visible in the transcript viewport"},{name:"copy-tool",description:"Copy the newest tool call output"},{name:"copy-transcript",description:"Copy every copyable transcript block"},{name:"fold-tools",description:"View setting \u2014 fold every FINISHED tool call to its single call line (Alt+C \u2192 z \u2192 f)"}],$=new Map([["resume-node","resume"]]),w=new Set([...g.map(o=>o.name),...$.keys()]),U=new Set(["black","red","green","yellow","blue","magenta","cyan","white","brightred","brightgreen","brightyellow","brightblue","brightmagenta","brightcyan","orange","grey","gray","default"]),T=new Set(["trust"]),N=new Set(Object.keys({off:!0,minimal:!0,low:!0,medium:!0,high:!0,xhigh:!0,max:!0})),v=new Set(h.map(o=>o.name));function b(o){return o.capabilities.nodeId}i(b,"nodeIdFor");function H(o){return o.capabilities.profileId}i(H,"profileIdFor");function P(o,e){const n=_(o,v.has(o),w.has(o));return n===void 0?void 0:e.capabilities.refusal(n,o)}i(P,"readOnlyRefusal");function oe(){return new Set([...v,...w,...T])}i(oe,"locallyConsumedCommandNames");function j(o){const e=/^\/(\S+)\s*([\s\S]*)$/.exec(o.trim());return e===null?void 0:{name:e[1],arg:e[2].trim()}}i(j,"parseSlashCommand");function te(o){return o.trimStart().startsWith("/")}i(te,"isSlashCommand");function re(o,e){const n=j(o);if(n===void 0)return!1;const{arg:t}=n,r=$.get(n.name)??n.name;if(T.has(r))return e.notify(`/${r} is not supported in attach (Phase 4)`),!0;if(w.has(r)){const s=P(r,e);return s!==void 0?(e.notify(s),!0):X(r,t,e)}if(!v.has(r))return!1;const u=P(r,e);if(u!==void 0)return e.notify(u),!0;switch(r){case"new":return e.send({type:"new_session"}),!0;case"reload":return e.send({type:"reload"}),!0;case"compact":return e.send({type:"compact",instructions:t||void 0}),!0;case"quit":return e.onQuit?(e.onQuit(),!0):(e.send({type:"bye"}),e.notify("Detaching \u2014 the engine keeps running"),!0);case"name":return t?(e.send({type:"set_session_name",name:t}),!0):f(e,"/name <display name>");case"export":{const{path:s,format:a}=W(t,e.cwd??process.cwd());return e.send({type:"export",path:s,format:a}),e.notify(`Exporting session \u2192 ${s}`),!0}case"import":return t?(e.send({type:"switch_session",path:t}),!0):f(e,"/import <path-to-session.jsonl>");case"model":return t?(e.send({type:"set_model",model:t}),!0):m(e.openModelPicker,e,"model","/model <provider/id>");case"fork":return t?(e.send({type:"fork",entryId:t}),!0):m(e.openForkPicker,e,"fork point","/fork <entryId>");case"tree":return t?(e.send({type:"navigate_tree",targetId:t}),!0):m(e.openTreePicker,e,"tree","/tree <entryId>");case"scoped-models":return e.openScopedModelsPicker?(e.openScopedModelsPicker(),!0):(e.notify("The scoped-models view needs the read-op channel, which this viewer has not wired"),!0);case"settings":return Q(t,e);case"session":return e.notify(Y(e.state)),!0;case"copy":return e.onCopy?(e.onCopy(),!0):(e.notify("/copy is not available in this viewer"),!0);case"login":return e.openLoginPicker?(e.openLoginPicker(),!0):(e.notify("/login is not available in this viewer"),!0);case"logout":return e.openLogoutPicker?(e.openLogoutPicker(),!0):(e.notify("/logout is not available in this viewer"),!0);case"share":return e.send({type:"share"}),e.notify("Sharing session\u2026"),!0;case"clone":return e.send({type:"clone"}),e.notify("Cloning session\u2026"),!0;case"changelog":return e.notify("/changelog: see https://github.com/earendil-works/pi for release notes (no bundled changelog in this build)"),!0;case"hotkeys":return e.onHelp?(e.onHelp(),!0):(e.notify("/hotkeys is not available in this viewer"),!0);default:return e.notify(`/${r} is not available in attach (Phase 4)`),!0}}i(re,"dispatchSlashCommand");function M(o,e){const n=i(a=>{const l=_(a,!1,!0);return l===void 0||e.permits(l)},"permitted"),t=g.filter(a=>n(a.name)),r=new Map(g.map(a=>[a.name,a])),u=o.flatMap(a=>{const l=r.get(a.name);return l===void 0?[a]:n(a.name)?[l]:[]}),s=new Set(o.map(a=>a.name));return[...u,...t.filter(a=>!s.has(a.name))]}i(M,"withCanvasCommands");function ie(o,e){return M(o??h,e).map(n=>({name:n.name,description:n.description}))}i(ie,"slashCommandList");function se(o,e){return M(o??h,e).map(n=>({value:`/${n.name}`,label:`/${n.name}`,description:n.description}))}i(se,"commandAutocompleteItems");function X(o,e,n){switch(o){case"clear":return n.send({type:"new_session"}),!0;case"graph":return n.onGraph?n.onGraph():n.notify("The GRAPH overlay is not wired into the viewer yet"),!0;case"inbox":return n.onInbox?n.onInbox():n.notify("The inbox surface is not wired into the viewer yet"),!0;case"promote":return B(e,n),!0;case"resume":return e?f(n,"/resume \u2014 no argument; use /resume-session <path> to switch pi sessions"):G(n);case"resume-session":return e?(n.send({type:"switch_session",path:e}),!0):m(n.openSessionPicker,n,"session","/resume-session <session-path>");case"mcp":return n.openMcpPicker?n.openMcpPicker(e||void 0):n.notify("The MCP panel is not wired into this viewer"),!0;case"context":return V(n);case"plugins":return q(n);case"node-metadata":return n.onNodeMetadata?n.onNodeMetadata():n.notify("Node metadata is not wired into this viewer"),!0;case"rename":return F(e,n),!0;case"color":return R(e,n);case"fold-tools":return n.onFoldTools?n.onFoldTools():n.notify("The fold-tools view setting is not wired into this viewer"),!0;case"mouse":return n.onToggleMouse?n.onToggleMouse():n.notify("Mouse reporting is not wired into this viewer"),!0;case"copy-message":return n.onCopyTarget?n.onCopyTarget("message"):n.notify("/copy-message is not available in this viewer"),!0;case"copy-tool":return n.onCopyTarget?n.onCopyTarget("tool"):n.notify("/copy-tool is not available in this viewer"),!0;case"copy-transcript":return n.onCopyTarget?n.onCopyTarget("transcript"):n.notify("/copy-transcript is not available in this viewer"),!0;default:return n.notify(`/${o} is not available in attach`),!0}}i(X,"dispatchCanvasCommand");function B(o,e){const n=b(e).trim();if(n===""){e.notify("/promote: no node to promote (viewer has no node id)");return}const t=o.trim().toLowerCase();e.notify(t?`Promoting \u2192 ${t}\u2026`:"Promoting\u2026");const r=["node","promote","--node",n,"--json"];t!==""&&r.push("--kind",t),p("crtr",r,{timeout:15e3,maxBuffer:4*1024*1024},(s,a)=>{let l=null,d=null;try{l=JSON.parse(a)}catch{l=null}if(s&&l===null){const L=typeof s.stdout=="string"?s.stdout:"";try{const C=JSON.parse(L);d=typeof C.message=="string"?C.message:null}catch{d=null}d===null&&(d=s.message)}if(l===null){e.notify(`promote failed: ${d??"unknown error"}`);return}const S=(l.roadmap_path??"").trim();e.notify(`Promoted to ${l.kind??"orchestrator"} orchestrator \u2014 authoring roadmap${S!==""?` (${S})`:""}.`),e.send({type:"prompt",text:"You have just been promoted to an orchestrator. Your new-role guidance is arriving \u2014 read it, author your roadmap, and start delegating."})}).stdin?.end()}i(B,"promoteNode");function F(o,e){const n=b(e).trim();if(n===""){e.notify("/rename: no node to rename (viewer has no node id)");return}if(o===""){e.notify("Usage: /rename <name>");return}e.send({type:"set_session_name",name:o});const t=process.env.TMUX_PANE;process.env.TMUX!==void 0&&t!==void 0&&t!==""&&p("tmux",["select-pane","-t",t,"-T",o],()=>{}),p("crtr",["node","config","--node",n,"--name",o,"--json"],{timeout:15e3,maxBuffer:4*1024*1024},(r,u)=>{if(r){let s=null;try{const a=JSON.parse(u);s=typeof a.message=="string"?a.message:null}catch{s=null}e.notify(`rename failed: ${s??r.message}`);return}e.notify(`Renamed node \u2192 ${o}`)})}i(F,"renameNode");function c(o){return new Promise(e=>{p("tmux",o,{timeout:5e3},(n,t)=>e(n?"":t))})}i(c,"tmuxCall");async function D(o,e,n){if(n){await c(["set-option","-p","-u","-t",o,"@crtr_color"]);return}await c(["set-option","-p","-t",o,"@crtr_color",e]);for(const t of["pane-border-style","pane-active-border-style"]){const r=(await c(["show-options","-wqv","-t",o,t])).trim();if(r.includes("@crtr_color"))continue;let u=r!==""?r:(await c(["show-options","-gqv",t])).trim();u===""&&(u=t==="pane-active-border-style"?"fg=green":"default");const s=`#{?#{!=:#{@crtr_color},},fg=#{@crtr_color},${u.replace(/,/g,"#,")}}`;await c(["set-option","-w","-t",o,t,s])}}i(D,"paintPaneBorder");function R(o,e){const n=o.trim().toLowerCase();if(n==="")return e.notify("Usage: /color <color> \u2014 a name (red, blue, orange\u2026), colourN, or #rrggbb; /color none to clear"),!0;const t=n==="none"||n==="clear";if(!(t||U.has(n)||/^colour\d{1,3}$/.test(n)||/^#[0-9a-f]{6}$/.test(n)))return e.notify(`Invalid color "${n}" \u2014 use a tmux colour name (${["red","green","yellow","blue","magenta","cyan","orange"].join(", ")}, \u2026), colourN, or #rrggbb`),!0;const u=process.env.TMUX_PANE,s=process.env.TMUX!==void 0&&u!==void 0&&u!=="";return s&&D(u,n,t),e.onColor?.(t?null:n),e.notify(t?"Node color cleared":`Node colored ${n}${s?"":" (chip only \u2014 no tmux pane)"}`),!0}i(R,"colorNode");function G(o){const e=process.env.TMUX_PANE;if(process.env.TMUX===void 0||e===void 0||e==="")return o.notify("/resume needs tmux"),!0;const n=y(o.cwd??process.cwd()),t=H(o),r=t===void 0||t===""?"":` --profile-id ${y(t)}`,u=`crtr canvas browse --return-pane ${e} --cwd ${n}${r}`;return k(u,{w:"97%",h:"95%"}),!0}i(G,"resumeNode");function V(o){if(process.env.TMUX===void 0)return o.notify("/context needs tmux"),!0;const e=b(o).trim();return e===""?(o.notify("/context: no node to root on (viewer has no node id)"),!0):(k(`crtr node inspect context --node ${y(e)}`),!0)}i(V,"openContext");function q(o){return process.env.TMUX===void 0?(o.notify("/plugins needs tmux"),!0):(k("crtr pkg browse"),!0)}i(q,"openPlugins");function k(o,e={w:"90%",h:"85%"}){try{p("tmux",["display-popup","-E","-w",e.w,"-h",e.h,...O(),o],()=>{})}catch{}}i(k,"popup");function f(o,e){return o.notify(`Usage: ${e}`),!0}i(f,"usage");function J(o,e,n){return o.notify(`Interactive ${e} picker isn't available over view.sock yet \u2014 use ${n}`),!0}i(J,"pickerHint");function m(o,e,n,t){return o?(o(),!0):J(e,n,t)}i(m,"openOrHint");function Q(o,e){const[n,...t]=o.split(/\s+/),r=t.join(" ").trim();switch(n){case"thinking":return r?N.has(r)?(e.send({type:"set_thinking_level",level:r}),!0):(e.notify(`Invalid thinking level "${r}" \u2014 choose one of: ${[...N].join(", ")}`),!0):f(e,"/settings thinking <level>");case"auto-retry":return e.send({type:"set_auto_retry",enabled:A(r)}),!0;case"auto-compaction":return e.send({type:"set_auto_compaction",enabled:A(r)}),!0;case"":return e.openSettingsPicker?(e.openSettingsPicker(),!0):I(e);default:return I(e)}}i(Q,"handleSettings");function I(o){return o.notify("Settings: /settings thinking <level> | auto-retry on|off | auto-compaction on|off"),!0}i(I,"settingsSummary");function A(o){return/^(on|true|yes|1|enabled?)$/i.test(o.trim())}i(A,"parseBool");function W(o,e){const n=o||`${e}/pi-export-${Date.now()}.html`,t=n.endsWith(".jsonl")?"jsonl":"html";return{path:n,format:t}}i(W,"parseExport");function Y(o){return o?`Session ${o.sessionName?`"${o.sessionName}" `:""}${o.sessionId} \xB7 model ${o.model?.id??"unknown"} \xB7 thinking ${o.thinkingLevel}`:"Session info unavailable"}i(Y,"describeSession");export{g as CANVAS_SLASH_COMMANDS,se as commandAutocompleteItems,re as dispatchSlashCommand,te as isSlashCommand,oe as locallyConsumedCommandNames,ie as slashCommandList};
1
+ var E=Object.defineProperty;var i=(t,e)=>E(t,"name",{value:e,configurable:!0});import{execFile as p}from"node:child_process";import{BUILTIN_SLASH_COMMANDS as h}from"../../../core/runtime/pi-vendored.js";import{surfaceTmuxStyleArgs as O}from"../../../core/runtime/surface-bg.js";import{viewerOperationForSlash as _}from"../input/capabilities.js";import{shellQuote as y}from"../../../shared/shell-quote.js";const g=[{name:"tutorial",description:"The guided crouter tutorial \u2014 your agent teaches it, one lesson at a time"},{name:"clear",description:"Clear the conversation and free context \u2014 a fresh, empty session (like /new)"},{name:"graph",description:"Toggle the canvas GRAPH view (your local subscription tree)"},{name:"inbox",description:"Toggle the human inbox \u2014 pending asks and reviews for you"},{name:"promote",description:"Promote this node to an orchestrator \u2014 /promote, or /promote <kind> to specialize"},{name:"resume",description:"Open the canvas navigator and swap THIS pane onto the chosen node; parked residents wake only on submitted input"},{name:"resume-session",description:"Switch this node onto one of its saved pi sessions \u2014 picker, or /resume-session <session-path>"},{name:"context",description:"Browse this node + its subscriptions\u2019 context dirs in a read-only nvim popup"},{name:"plugins",description:"Open the interactive plugin browser for this machine"},{name:"node-metadata",description:"Inspect this node \u2014 identity, lifecycle, runtime, graph, artifacts, paths, and agent surfaces"},{name:"mcp",description:"MCP servers \u2014 status from the adapter\u2019s config/cache; Enter (or /mcp <server>) connects, reconnects, or re-auths"},{name:"rename",description:"Rename this node everywhere \u2014 node row, viewer window, session chip, tmux pane: /rename <name>"},{name:"color",description:"Color this node \u2014 tmux pane outline + name chip: /color <color> (red, blue, #rrggbb, \u2026) or /color none"},{name:"mouse",description:"Toggle mouse wheel scrolling and drag-select \u2014 off restores tmux drag-select and copy-mode mouse"},{name:"copy-message",description:"Copy the newest message visible in the transcript viewport"},{name:"copy-tool",description:"Copy the newest tool call output"},{name:"copy-transcript",description:"Copy every copyable transcript block"},{name:"fold-tools",description:"View setting \u2014 fold every FINISHED tool call to its single call line (Alt+C \u2192 z \u2192 f)"}],$=new Map([["resume-node","resume"]]),w=new Set([...g.map(t=>t.name),...$.keys()]),U=new Set(["black","red","green","yellow","blue","magenta","cyan","white","brightred","brightgreen","brightyellow","brightblue","brightmagenta","brightcyan","orange","grey","gray","default"]),T=new Set(["trust"]),N=new Set(Object.keys({off:!0,minimal:!0,low:!0,medium:!0,high:!0,xhigh:!0,max:!0})),v=new Set(h.map(t=>t.name));function b(t){return t.capabilities.nodeId}i(b,"nodeIdFor");function H(t){return t.capabilities.profileId}i(H,"profileIdFor");function P(t,e){const n=_(t,v.has(t),w.has(t));return n===void 0?void 0:e.capabilities.refusal(n,t)}i(P,"readOnlyRefusal");function te(){return new Set([...v,...w,...T])}i(te,"locallyConsumedCommandNames");function j(t){const e=/^\/(\S+)\s*([\s\S]*)$/.exec(t.trim());return e===null?void 0:{name:e[1],arg:e[2].trim()}}i(j,"parseSlashCommand");function oe(t){return t.trimStart().startsWith("/")}i(oe,"isSlashCommand");function re(t,e){const n=j(t);if(n===void 0)return!1;const{arg:o}=n,r=$.get(n.name)??n.name;if(T.has(r))return e.notify(`/${r} is not supported in attach (Phase 4)`),!0;if(w.has(r)){const s=P(r,e);return s!==void 0?(e.notify(s),!0):X(r,o,e)}if(!v.has(r))return!1;const u=P(r,e);if(u!==void 0)return e.notify(u),!0;switch(r){case"new":return e.send({type:"new_session"}),!0;case"reload":return e.send({type:"reload"}),!0;case"compact":return e.send({type:"compact",instructions:o||void 0}),!0;case"quit":return e.onQuit?(e.onQuit(),!0):(e.send({type:"bye"}),e.notify("Detaching \u2014 the engine keeps running"),!0);case"name":return o?(e.send({type:"set_session_name",name:o}),!0):m(e,"/name <display name>");case"export":{const{path:s,format:a}=W(o,e.cwd??process.cwd());return e.send({type:"export",path:s,format:a}),e.notify(`Exporting session \u2192 ${s}`),!0}case"import":return o?(e.send({type:"switch_session",path:o}),!0):m(e,"/import <path-to-session.jsonl>");case"model":return o?(e.send({type:"set_model",model:o}),!0):f(e.openModelPicker,e,"model","/model <provider/id>");case"fork":return o?(e.send({type:"fork",entryId:o}),!0):f(e.openForkPicker,e,"fork point","/fork <entryId>");case"tree":return o?(e.send({type:"navigate_tree",targetId:o}),!0):f(e.openTreePicker,e,"tree","/tree <entryId>");case"scoped-models":return e.openScopedModelsPicker?(e.openScopedModelsPicker(),!0):(e.notify("The scoped-models view needs the read-op channel, which this viewer has not wired"),!0);case"settings":return Q(o,e);case"session":return e.notify(Y(e.state)),!0;case"copy":return e.onCopy?(e.onCopy(),!0):(e.notify("/copy is not available in this viewer"),!0);case"login":return e.openLoginPicker?(e.openLoginPicker(),!0):(e.notify("/login is not available in this viewer"),!0);case"logout":return e.openLogoutPicker?(e.openLogoutPicker(),!0):(e.notify("/logout is not available in this viewer"),!0);case"share":return e.send({type:"share"}),e.notify("Sharing session\u2026"),!0;case"clone":return e.send({type:"clone"}),e.notify("Cloning session\u2026"),!0;case"changelog":return e.notify("/changelog: see https://github.com/earendil-works/pi for release notes (no bundled changelog in this build)"),!0;case"hotkeys":return e.onHelp?(e.onHelp(),!0):(e.notify("/hotkeys is not available in this viewer"),!0);default:return e.notify(`/${r} is not available in attach (Phase 4)`),!0}}i(re,"dispatchSlashCommand");function M(t,e){const n=i(a=>{const l=_(a,!1,!0);return l===void 0||e.permits(l)},"permitted"),o=g.filter(a=>n(a.name)),r=new Map(g.map(a=>[a.name,a])),u=t.flatMap(a=>{const l=r.get(a.name);return l===void 0?[a]:n(a.name)?[l]:[]}),s=new Set(t.map(a=>a.name));return[...u,...o.filter(a=>!s.has(a.name))]}i(M,"withCanvasCommands");function ie(t,e){return M(t??h,e).map(n=>({name:n.name,description:n.description}))}i(ie,"slashCommandList");function se(t,e){return M(t??h,e).map(n=>({value:`/${n.name}`,label:`/${n.name}`,description:n.description}))}i(se,"commandAutocompleteItems");function X(t,e,n){switch(t){case"clear":return n.send({type:"new_session"}),!0;case"graph":return n.onGraph?n.onGraph():n.notify("The GRAPH overlay is not wired into the viewer yet"),!0;case"inbox":return n.onInbox?n.onInbox():n.notify("The inbox surface is not wired into the viewer yet"),!0;case"tutorial":return n.send({type:"prompt",text:"The user asked for the crouter tutorial. Run `crtr sys tutorial -h`, pick the track that matches what they want to learn, and teach it one lesson per invocation \u2014 in your own words, letting them try each thing before you move on."}),!0;case"promote":return B(e,n),!0;case"resume":return e?m(n,"/resume \u2014 no argument; use /resume-session <path> to switch pi sessions"):G(n);case"resume-session":return e?(n.send({type:"switch_session",path:e}),!0):f(n.openSessionPicker,n,"session","/resume-session <session-path>");case"mcp":return n.openMcpPicker?n.openMcpPicker(e||void 0):n.notify("The MCP panel is not wired into this viewer"),!0;case"context":return V(n);case"plugins":return q(n);case"node-metadata":return n.onNodeMetadata?n.onNodeMetadata():n.notify("Node metadata is not wired into this viewer"),!0;case"rename":return F(e,n),!0;case"color":return R(e,n);case"fold-tools":return n.onFoldTools?n.onFoldTools():n.notify("The fold-tools view setting is not wired into this viewer"),!0;case"mouse":return n.onToggleMouse?n.onToggleMouse():n.notify("Mouse reporting is not wired into this viewer"),!0;case"copy-message":return n.onCopyTarget?n.onCopyTarget("message"):n.notify("/copy-message is not available in this viewer"),!0;case"copy-tool":return n.onCopyTarget?n.onCopyTarget("tool"):n.notify("/copy-tool is not available in this viewer"),!0;case"copy-transcript":return n.onCopyTarget?n.onCopyTarget("transcript"):n.notify("/copy-transcript is not available in this viewer"),!0;default:return n.notify(`/${t} is not available in attach`),!0}}i(X,"dispatchCanvasCommand");function B(t,e){const n=b(e).trim();if(n===""){e.notify("/promote: no node to promote (viewer has no node id)");return}const o=t.trim().toLowerCase();e.notify(o?`Promoting \u2192 ${o}\u2026`:"Promoting\u2026");const r=["node","promote","--node",n,"--json"];o!==""&&r.push("--kind",o),p("crtr",r,{timeout:15e3,maxBuffer:4*1024*1024},(s,a)=>{let l=null,d=null;try{l=JSON.parse(a)}catch{l=null}if(s&&l===null){const L=typeof s.stdout=="string"?s.stdout:"";try{const C=JSON.parse(L);d=typeof C.message=="string"?C.message:null}catch{d=null}d===null&&(d=s.message)}if(l===null){e.notify(`promote failed: ${d??"unknown error"}`);return}const S=(l.roadmap_path??"").trim();e.notify(`Promoted to ${l.kind??"orchestrator"} orchestrator \u2014 authoring roadmap${S!==""?` (${S})`:""}.`),e.send({type:"prompt",text:"You have just been promoted to an orchestrator. Your new-role guidance is arriving \u2014 read it, author your roadmap, and start delegating."})}).stdin?.end()}i(B,"promoteNode");function F(t,e){const n=b(e).trim();if(n===""){e.notify("/rename: no node to rename (viewer has no node id)");return}if(t===""){e.notify("Usage: /rename <name>");return}e.send({type:"set_session_name",name:t});const o=process.env.TMUX_PANE;process.env.TMUX!==void 0&&o!==void 0&&o!==""&&p("tmux",["select-pane","-t",o,"-T",t],()=>{}),p("crtr",["node","config","--node",n,"--name",t,"--json"],{timeout:15e3,maxBuffer:4*1024*1024},(r,u)=>{if(r){let s=null;try{const a=JSON.parse(u);s=typeof a.message=="string"?a.message:null}catch{s=null}e.notify(`rename failed: ${s??r.message}`);return}e.notify(`Renamed node \u2192 ${t}`)})}i(F,"renameNode");function c(t){return new Promise(e=>{p("tmux",t,{timeout:5e3},(n,o)=>e(n?"":o))})}i(c,"tmuxCall");async function D(t,e,n){if(n){await c(["set-option","-p","-u","-t",t,"@crtr_color"]);return}await c(["set-option","-p","-t",t,"@crtr_color",e]);for(const o of["pane-border-style","pane-active-border-style"]){const r=(await c(["show-options","-wqv","-t",t,o])).trim();if(r.includes("@crtr_color"))continue;let u=r!==""?r:(await c(["show-options","-gqv",o])).trim();u===""&&(u=o==="pane-active-border-style"?"fg=green":"default");const s=`#{?#{!=:#{@crtr_color},},fg=#{@crtr_color},${u.replace(/,/g,"#,")}}`;await c(["set-option","-w","-t",t,o,s])}}i(D,"paintPaneBorder");function R(t,e){const n=t.trim().toLowerCase();if(n==="")return e.notify("Usage: /color <color> \u2014 a name (red, blue, orange\u2026), colourN, or #rrggbb; /color none to clear"),!0;const o=n==="none"||n==="clear";if(!(o||U.has(n)||/^colour\d{1,3}$/.test(n)||/^#[0-9a-f]{6}$/.test(n)))return e.notify(`Invalid color "${n}" \u2014 use a tmux colour name (${["red","green","yellow","blue","magenta","cyan","orange"].join(", ")}, \u2026), colourN, or #rrggbb`),!0;const u=process.env.TMUX_PANE,s=process.env.TMUX!==void 0&&u!==void 0&&u!=="";return s&&D(u,n,o),e.onColor?.(o?null:n),e.notify(o?"Node color cleared":`Node colored ${n}${s?"":" (chip only \u2014 no tmux pane)"}`),!0}i(R,"colorNode");function G(t){const e=process.env.TMUX_PANE;if(process.env.TMUX===void 0||e===void 0||e==="")return t.notify("/resume needs tmux"),!0;const n=y(t.cwd??process.cwd()),o=H(t),r=o===void 0||o===""?"":` --profile-id ${y(o)}`,u=`crtr canvas browse --return-pane ${e} --cwd ${n}${r}`;return k(u,{w:"97%",h:"95%"}),!0}i(G,"resumeNode");function V(t){if(process.env.TMUX===void 0)return t.notify("/context needs tmux"),!0;const e=b(t).trim();return e===""?(t.notify("/context: no node to root on (viewer has no node id)"),!0):(k(`crtr node inspect context --node ${y(e)}`),!0)}i(V,"openContext");function q(t){return process.env.TMUX===void 0?(t.notify("/plugins needs tmux"),!0):(k("crtr pkg browse"),!0)}i(q,"openPlugins");function k(t,e={w:"90%",h:"85%"}){try{p("tmux",["display-popup","-E","-w",e.w,"-h",e.h,...O(),t],()=>{})}catch{}}i(k,"popup");function m(t,e){return t.notify(`Usage: ${e}`),!0}i(m,"usage");function J(t,e,n){return t.notify(`Interactive ${e} picker isn't available over view.sock yet \u2014 use ${n}`),!0}i(J,"pickerHint");function f(t,e,n,o){return t?(t(),!0):J(e,n,o)}i(f,"openOrHint");function Q(t,e){const[n,...o]=t.split(/\s+/),r=o.join(" ").trim();switch(n){case"thinking":return r?N.has(r)?(e.send({type:"set_thinking_level",level:r}),!0):(e.notify(`Invalid thinking level "${r}" \u2014 choose one of: ${[...N].join(", ")}`),!0):m(e,"/settings thinking <level>");case"auto-retry":return e.send({type:"set_auto_retry",enabled:A(r)}),!0;case"auto-compaction":return e.send({type:"set_auto_compaction",enabled:A(r)}),!0;case"":return e.openSettingsPicker?(e.openSettingsPicker(),!0):I(e);default:return I(e)}}i(Q,"handleSettings");function I(t){return t.notify("Settings: /settings thinking <level> | auto-retry on|off | auto-compaction on|off"),!0}i(I,"settingsSummary");function A(t){return/^(on|true|yes|1|enabled?)$/i.test(t.trim())}i(A,"parseBool");function W(t,e){const n=t||`${e}/pi-export-${Date.now()}.html`,o=n.endsWith(".jsonl")?"jsonl":"html";return{path:n,format:o}}i(W,"parseExport");function Y(t){return t?`Session ${t.sessionName?`"${t.sessionName}" `:""}${t.sessionId} \xB7 model ${t.model?.id??"unknown"} \xB7 thinking ${t.thinkingLevel}`:"Session info unavailable"}i(Y,"describeSession");export{g as CANVAS_SLASH_COMMANDS,se as commandAutocompleteItems,re as dispatchSlashCommand,oe as isSlashCommand,te as locallyConsumedCommandNames,ie as slashCommandList};