@north-light/crouter 0.3.241 → 0.3.243

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 (62) hide show
  1. package/dist/api/dto/broker-ops.d.ts +8 -3
  2. package/dist/api/dto/broker.d.ts +4 -2
  3. package/dist/api/dto/inbox.d.ts +1 -0
  4. package/dist/api/dto/nodes.d.ts +4 -0
  5. package/dist/api/dto/review-comments.d.ts +1 -0
  6. package/dist/builtin-memory/04-orchestration-kernel.md +1 -1
  7. package/dist/clients/attach/session/profile-files.js +4 -1
  8. package/dist/clients/attach/viewer.js +1 -1
  9. package/dist/commands/node/create.js +8 -3
  10. package/dist/commands/sys/context/admin/docs-panel.d.ts +17 -7
  11. package/dist/commands/sys/context/admin/docs-panel.js +43 -23
  12. package/dist/commands/sys/context/admin/list-view.d.ts +22 -11
  13. package/dist/commands/sys/context/admin/list-view.js +112 -53
  14. package/dist/commands/sys/context/admin/shell.d.ts +3 -0
  15. package/dist/commands/sys/context/admin/shell.js +11 -2
  16. package/dist/commands/sys/daemon.js +1 -1
  17. package/dist/core/__tests__/child-death-wake.test.js +0 -48
  18. package/dist/core/__tests__/daemon-boot.test.js +0 -8
  19. package/dist/core/__tests__/dead-node-policy-table.test.js +1 -2
  20. package/dist/core/__tests__/integration/deferred-no-wake.test.js +14 -2
  21. package/dist/core/__tests__/lifecycle.test.js +5 -5
  22. package/dist/core/__tests__/relaunch-root.test.js +91 -1
  23. package/dist/core/__tests__/revive-capacity.test.js +36 -1
  24. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +73 -0
  25. package/dist/core/__tests__/seam/dormancy-release.test.js +1 -2
  26. package/dist/core/canvas/canvas.d.ts +6 -5
  27. package/dist/core/canvas/canvas.js +8 -7
  28. package/dist/core/canvas/types.d.ts +6 -0
  29. package/dist/core/review/realize.js +1 -1
  30. package/dist/core/runtime/broker/node-named.d.ts +1 -0
  31. package/dist/core/runtime/host.js +114 -103
  32. package/dist/core/runtime/lifecycle.js +3 -2
  33. package/dist/core/runtime/naming.d.ts +26 -8
  34. package/dist/core/runtime/naming.js +75 -43
  35. package/dist/core/runtime/reset.js +12 -5
  36. package/dist/core/runtime/revive-all.d.ts +4 -11
  37. package/dist/core/runtime/revive-all.js +6 -13
  38. package/dist/core/runtime/warm-pool.js +4 -4
  39. package/dist/daemon/api/__tests__/node-create-description.test.js +1 -0
  40. package/dist/daemon/api/handlers/broker-ops.js +14 -7
  41. package/dist/daemon/api/handlers/feedback-comments.js +1 -1
  42. package/dist/daemon/api/map.js +2 -0
  43. package/dist/daemon/crtrd.js +17 -20
  44. package/dist/daemon/messaging/node-message.d.ts +1 -0
  45. package/dist/daemon/messaging/node-message.js +6 -2
  46. package/dist/daemon/reconcilers/live-obligation.d.ts +1 -2
  47. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +0 -5
  48. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +9 -13
  49. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.js +0 -6
  50. package/dist/daemon/reconcilers/node-lifecycle/terminating.d.ts +0 -2
  51. package/dist/daemon/reconcilers/node-lifecycle/terminating.js +0 -10
  52. package/dist/daemon/reconcilers/node-lifecycle/tick.d.ts +2 -2
  53. package/dist/daemon/reconcilers/node-lifecycle/tick.js +4 -2
  54. package/dist/daemon/reconcilers/storage-maintenance.js +2 -2
  55. package/dist/daemon/review/comment-notify.d.ts +1 -0
  56. package/dist/daemon/review/comment-notify.js +1 -0
  57. package/dist/pi-extensions/broker-local.d.ts +1 -0
  58. package/dist/pi-extensions/broker-local.js +12 -7
  59. package/dist/pi-extensions/canvas-recap.js +1 -0
  60. package/dist/pi-extensions/canvas-stophook.js +2 -0
  61. package/package.json +1 -1
  62. package/runtime.lock.json +2 -2
@@ -65,6 +65,7 @@ export interface BrokerExtensionNodeDTO {
65
65
  node_id: string;
66
66
  name: string;
67
67
  description?: string;
68
+ title?: string;
68
69
  icon?: string;
69
70
  kind: string;
70
71
  mode: 'base' | 'orchestrator';
@@ -124,26 +125,30 @@ export interface BrokerExtensionStateDTO {
124
125
  export type BrokerGeneratedNameRequest = {
125
126
  kind: 'initial';
126
127
  description: string;
128
+ title: string;
127
129
  icon: string;
128
130
  } | {
129
131
  kind: 'recap';
130
132
  description: string;
133
+ title: string;
131
134
  icon: string;
132
135
  expected: {
133
136
  name: string;
134
137
  description: string;
138
+ title: string;
135
139
  icon: string;
136
140
  kind: string;
137
141
  };
138
142
  };
139
143
  /** A daemon-selected Pi editor-label directive. No handler calls a broker.
140
- * `description`/`icon` are the values as STORED (an empty icon is left unset),
141
- * so the broker announces what the node now carries rather than what it asked
142
- * for. All three are present exactly when `applied`. */
144
+ * `description`/`title`/`icon` are the values as STORED (an empty title or icon
145
+ * is left unset), so the broker announces what the node now carries rather than
146
+ * what it asked for. All four are present exactly when `applied`. */
143
147
  export interface BrokerGeneratedNameResultDTO {
144
148
  applied: boolean;
145
149
  editorLabel?: string;
146
150
  description?: string;
151
+ title?: string;
147
152
  icon?: string;
148
153
  }
149
154
  export interface BrokerPersonaAckRequest {
@@ -55,12 +55,14 @@ export interface BrokerWelcomeFrame<M = unknown> {
55
55
  * It exists because the name lands SECONDS AFTER the turn that triggered it — a
56
56
  * consumer that settles its own display state on turn-end reads an unnamed node and
57
57
  * has nothing left to re-read on. This frame is the push: `description` is the
58
- * 3-8 word kebab-case handle, `icon` the Nerd Font glyph the namer chose (empty
59
- * when it chose none), and `editorLabel` crtrd's own rendered label for the node.
58
+ * 3-8 word kebab-case handle, `title` the prose sentence a conversation list
59
+ * shows, `icon` the Nerd Font glyph the namer chose (empty when it chose none),
60
+ * and `editorLabel` crtrd's own rendered label for the node.
60
61
  */
61
62
  export interface BrokerNodeNamedFrame {
62
63
  type: 'node_named';
63
64
  description: string;
65
+ title: string;
64
66
  icon: string;
65
67
  editorLabel: string;
66
68
  }
@@ -162,6 +162,7 @@ export interface PageFeedbackDeliveryDTO {
162
162
  status: 'delivered' | 'failed';
163
163
  via?: 'engine' | 'inbox';
164
164
  woke: boolean;
165
+ reason?: 'capacity_frozen';
165
166
  error?: string;
166
167
  }
167
168
  /** Create result: the new comment, the ticket's full feedback state after it,
@@ -166,6 +166,10 @@ export interface NodeFaultDTO {
166
166
  * `GET /v1/nodes/{id}` and by the create/lifecycle actions that yield a node. */
167
167
  export interface NodeDetailDTO extends NodeSummaryDTO {
168
168
  description?: string;
169
+ /** The namer's prose form of `description` — sentence case, punctuation intact
170
+ * (`NodeMeta.title`). What a surface showing this node to a person reads;
171
+ * absent on a node named before titles existed. */
172
+ title?: string;
169
173
  /** The Nerd Font glyph the namer chose for this node's work, when it has one
170
174
  * (`NodeMeta.icon`). Rendered ahead of the label by surfaces that want it. */
171
175
  icon?: string;
@@ -63,6 +63,7 @@ export interface ReviewCommentDeliveryDTO {
63
63
  status: 'delivered' | 'skipped' | 'failed';
64
64
  via?: 'engine' | 'inbox';
65
65
  woke: boolean;
66
+ reason?: 'capacity_frozen';
66
67
  error?: string;
67
68
  }
68
69
  /** Daemon-derived result of one comment mutation. */
@@ -62,7 +62,7 @@ Then advance. Reshape the phases themselves only when reality invalidates the pl
62
62
 
63
63
  ## Delegating
64
64
 
65
- How to write a child's prompt lives on `crtr node new -h`, read at the moment you spawn; this kernel owns the split and the management. Break the goal into units each small enough for one child to finish well in one window, and route findings across children: you are the relay point for everything your children report up, so when a child's task depends on an explore report, design, or spec an earlier child produced, hand that artifact into the new task. When a bounded unit itself contains enough independent work for worthwhile parallelism, create it directly as a sub-orchestrator (`crtr node new --kind <kind> --mode orchestrator`); when it is sequential, assign it to a base child that can yield across windows rather than relying on promotion.
65
+ How to write a child's prompt lives on `crtr node new -h`, read at the moment you spawn; this kernel owns the split and the management. Break the goal into units each small enough for one child to finish well in one window, and route findings across children: you are the relay point for everything your children report up, so when a child's task depends on an explore report, design, or spec an earlier child produced, hand that artifact into the new task. When a bounded unit itself contains enough independent work for worthwhile parallelism, create it directly as a sub-orchestrator (`crtr node new --kind <kind> --mode orchestrator`); when it is sequential, assign it to a base child that can yield across windows rather than relying on promotion. Fanning out many base children of your own kind is your job, not grounds for another coordinator: a sub-orchestrator of your own kind usually just recreates you one window poorer, so reserve sub-orchestrators for a unit of a different kind or a sub-goal genuinely too large for one layer.
66
66
 
67
67
  Every child's task must be a proper subset of your goal. Handing your whole remaining assignment to one child is recursion, not delegation — it recreates you one window poorer, and repeated once per window it builds a chain of managers with no worker. When a unit cannot be split and cannot run in parallel — a sole-writer implementation, one tightly interlocked surface — it is yours: work it hands-on and yield to continue across windows; hands-on work on an unsplittable unit is orchestration done right, not a lost plot.
68
68
 
@@ -171,7 +171,10 @@ export function launchProfileFileSearch(opts) {
171
171
  // horizontally scrolls against the same text — a hidden field would be
172
172
  // unsearchable, and a display slice would silently narrow the query.
173
173
  const yankPath = 'printf \'%s\' {} | { if command -v pbcopy >/dev/null 2>&1; then pbcopy; elif command -v wl-copy >/dev/null 2>&1; then wl-copy; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard; else cat >/dev/null; fi; }; printf \'%s\' {} | tmux load-buffer -w -';
174
- const command = `{ ${scans.map((scan) => `${scan} &`).join(' ')} wait; } > ${shellQuote(candidates)} && ${shellQuote(fzf)} --print0 --scheme=path --hscroll-off=${HSCROLL_OFF} --header ${shellQuote('fuzzy path • ctrl-y yank file path')} --bind ${shellQuote(`ctrl-y:execute-silent(${yankPath})`)} --preview ${shellQuote('head -100 -- {}')} < ${shellQuote(candidates)} > ${shellQuote(selected)}`;
174
+ // `+abort` closes the popup the instant the path is copied — without it the
175
+ // silent yank leaves the popup unchanged and reads as a dead key. Aborting
176
+ // writes no selection, so the caller opens no file and just cleans up.
177
+ const command = `{ ${scans.map((scan) => `${scan} &`).join(' ')} wait; } > ${shellQuote(candidates)} && ${shellQuote(fzf)} --print0 --scheme=path --hscroll-off=${HSCROLL_OFF} --header ${shellQuote('fuzzy path • ctrl-y yank file path')} --bind ${shellQuote(`ctrl-y:execute-silent(${yankPath})+abort`)} --preview ${shellQuote('head -100 -- {}')} < ${shellQuote(candidates)} > ${shellQuote(selected)}`;
175
178
  try {
176
179
  execFile('tmux', ['display-popup', '-E', '-T', ' profile files ', '-w', '80%', '-h', '60%', ...surfaceTmuxStyleArgs(), command], () => {
177
180
  const file = selectionPath(selected);
@@ -2017,7 +2017,7 @@ ${i}`}function OQn(e){try{let t=JSON.parse(e);return Array.isArray(t)?t.flatMap(
2017
2017
  Invocation: \`${f.invocation.replaceAll("`","\\`")}\`
2018
2018
 
2019
2019
  ${f.content}
2020
- `,"utf8");let Z=`if command -v nvim >/dev/null 2>&1; then exec nvim -R -- ${Rm(z)}; else printf 'nvim is required to inspect loaded commands. Install Neovim and retry.\\n'; printf 'Press Enter to close.'; read _; fi`;hhs("tmux",["display-popup","-E","-w","90%","-h","85%",...oF(),Z],()=>ghs(D,{recursive:!0,force:!0}))}}}function EHn(e,t,r){let i=t;return{current:()=>i,refresh:()=>{e.canvasSource.getNode(e.nodeId).then(o=>{r.isTornDown()||o===null||(i=o,r.onChanged())}).catch(()=>{})}}}var Shs={readOption:$4,writeOption:(e,t,r)=>{U4(e,t,r)},stampAsync:c0t,currentPane:()=>process.env.TMUX_PANE??g8()?.pane,ownerId:()=>String(process.pid)};function SHn(e,t,r=Shs){let i,o=r.ownerId(),a=()=>{if(i===void 0||i==="")return!0;let u=r.readOption(i,THn);return u===void 0||u===""?!0:u===o};return{set:()=>{i=r.currentPane(),AHn(i,o,r.writeOption),Chs(e,i,t,r.writeOption),Ihs(i,r.writeOption),Ths(e,i,r.writeOption)},stamp:()=>{e||i===void 0||i===""||r.stampAsync(i)},clear:()=>{a()&&(xhs(e,i,r.writeOption),whs(i,r.writeOption),vhs(e,i,r.writeOption),AHn(i,"",r.writeOption))},pane:()=>i}}var THn="@crtr_viewer_pid";function AHn(e,t,r){if(!(e===void 0||e===""))try{r(e,THn,t)}catch{}}function Ths(e,t,r){if(!(e||t===void 0||t===""))try{r(t,I0e,String(Date.now()))}catch{}}function vhs(e,t,r=U4){if(!(e||t===void 0||t===""))try{r(t,I0e,"")}catch{}}function Chs(e,t,r,i=U4){if(!(e||t===void 0||t===""))try{i(t,"@crtr_node",r)}catch{}}function xhs(e,t,r=U4){if(!(e||t===void 0||t===""))try{r(t,"@crtr_node","")}catch{}}function Ihs(e,t=U4){if(!(e===void 0||e===""))try{t(e,"@crtr_attach","1")}catch{}}function whs(e,t=U4){if(!(e===void 0||e===""))try{t(e,"@crtr_attach","")}catch{}}import{constants as Rhs,accessSync as khs,existsSync as Nhs,mkdtempSync as Phs,readFileSync as Ohs,rmSync as wrt,statSync as Dhs}from"node:fs";import{tmpdir as Mhs}from"node:os";import{delimiter as Lhs,dirname as Fhs,join as kke,resolve as CHn,sep as Bhs}from"node:path";import{execFile as vHn,spawnSync as Uhs}from"node:child_process";function xHn(e,t){let r=new Map(e.map(a=>[a.node_id,a])),i=new Set([t]);for(let a=r.get(t);a?.parent!==null&&a?.parent!==void 0;){let u=a.parent;if(i.has(u))break;i.add(u),a=r.get(u)}let o=[t];for(;o.length>0;){let a=o.pop();for(let u of e)u.parent!==a||i.has(u.node_id)||(i.add(u.node_id),o.push(u.node_id))}return e.filter(a=>i.has(a.node_id)).map(a=>a.cwd)}function IHn(e){try{return Dhs(e).isDirectory()}catch{return!1}}function $hs(e,t){let r=[...t==null?[]:[t],...(process.env.PATH??"").split(Lhs).filter(Boolean).map(i=>kke(i,e))];return[...new Set(r)]}function BJt(e,t){for(let r of $hs(e,t)){try{khs(r,Rhs.X_OK)}catch{continue}if(Uhs(r,["--version"],{stdio:"ignore"}).status===0)return r}return null}function zhs(e){if(e===null||e==="")return{projects:[],memory:null};try{let{profileId:t,manifest:r}=mae(e);return{projects:r.projects.map(i=>i.path),memory:mTr(t)}}catch{return{projects:[],memory:null}}}function UJt(e){let t=new Set,r=[];for(let i of e){let o=CHn(i);t.has(o)||!IHn(o)||(t.add(o),r.push(o))}return r}function jhs(e){return e.filter(t=>!e.some(r=>r!==t&&t.startsWith(r+Bhs)))}function qhs(e){let t=[];for(let r of e){let i=CHn(r);for(;;){let o=kke(i,".crouter","memory");IHn(o)&&t.push(o);let a=Fhs(i);if(a===i)break;i=a}}return UJt(t)}var Ghs=20;function Vhs(e){try{let t=Ohs(e);if(t.length===0)return null;let r=t[t.length-1]===0?t.length-1:t.length;return t.subarray(0,r).toString("utf8")||null}catch{return null}}function wHn(e){let t=BJt("fd",e.fdPath);if(t===null){e.onNotice("fd is required for profile file search");return}let r=BJt("fzf");if(r===null){e.onNotice("fzf is required for profile file search");return}let i=BJt("nvim");if(i===null){e.onNotice("Neovim is required for profile file search");return}let o=zhs(e.profileId),a=UJt([e.cwd,...e.nodeDirs,...o.projects]);if(a.length===0){e.onNotice("No profile file-search directories exist");return}let u=jhs(a),p=UJt([...o.memory===null?[]:[o.memory],...qhs(a)]),f=Phs(kke(Mhs(),"crtr-profile-files-")),_=kke(f,"selection"),m=kke(f,"candidates"),v=`{ ${[...u.map(T=>`${Rm(t)} --type f --hidden --exclude .git --exclude ${Rm("**/.crouter/memory/**")} . ${Rm(T)}`),...p.map(T=>`${Rm(t)} --type f --hidden --exclude .git --no-ignore . ${Rm(T)}`)].map(T=>`${T} &`).join(" ")} wait; } > ${Rm(m)} && ${Rm(r)} --print0 --scheme=path --hscroll-off=${Ghs} --header ${Rm("fuzzy path \u2022 ctrl-y yank file path")} --bind ${Rm("ctrl-y:execute-silent(printf '%s' {} | { if command -v pbcopy >/dev/null 2>&1; then pbcopy; elif command -v wl-copy >/dev/null 2>&1; then wl-copy; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard; else cat >/dev/null; fi; }; printf '%s' {} | tmux load-buffer -w -)")} --preview ${Rm("head -100 -- {}")} < ${Rm(m)} > ${Rm(_)}`;try{vHn("tmux",["display-popup","-E","-T"," profile files ","-w","80%","-h","60%",...oF(),v],()=>{let T=Vhs(_);if(T===null||!Nhs(T)){wrt(f,{recursive:!0,force:!0});return}let C=`exec ${Rm(i)} -- ${Rm(T)}`;try{vHn("tmux",["display-popup","-E","-T"," profile file ","-w","90%","-h","90%",...oF(),C],()=>wrt(f,{recursive:!0,force:!0}))}catch(I){wrt(f,{recursive:!0,force:!0}),e.onNotice(`Could not open profile file: ${I instanceof Error?I.message:String(I)}`)}})}catch(T){wrt(f,{recursive:!0,force:!0}),e.onNotice(`Could not open profile file search: ${T instanceof Error?T.message:String(T)}`)}}var kHn={statuses:{},widgets:{},title:void 0};function NHn(e="observer"){return{role:e,agentDir:void 0,engine:void 0,contextTokens:void 0,queued:[],display:kHn,dialog:null}}function Whs(e){let t=e.snapshot,r=t.display??kHn,i=t.stats.contextUsage;return{role:e.role,agentDir:e.agentDir,engine:t.state,contextTokens:i?.tokens??void 0,queued:Qhs(t),display:{statuses:{...r.statuses},widgets:Object.fromEntries(Object.entries(r.widgets).map(([o,a])=>[o,{lines:[...a.lines],placement:a.placement}])),title:r.title},dialog:e.pending_dialog??null}}function PHn(e,t){switch(t.type){case"welcome":return Whs(t);case"model_changed":return xhe(e,{model:t.model});case"extension_ui_request":switch(t.method){case"setStatus":{let r=e.display.statuses[t.statusKey];if(t.statusText===void 0?r===void 0:r===t.statusText)return e;let i={...e.display.statuses};return delete i[t.statusKey],t.statusText!==void 0&&(i[t.statusKey]=t.statusText),{...e,display:{...e.display,statuses:i}}}case"setWidget":{let r=t.widgetLines===void 0?void 0:{lines:[...t.widgetLines],placement:t.widgetPlacement??"aboveEditor"};if(Jhs(e.display.widgets[t.widgetKey],r))return e;let i={...e.display.widgets};return delete i[t.widgetKey],r!==void 0&&(i[t.widgetKey]=r),{...e,display:{...e.display,widgets:i}}}case"setTitle":return e.display.title===t.title?e:{...e,display:{...e.display,title:t.title}};case"notify":return e;default:return{...e,dialog:t}}case"extension_ui_dismiss":return e.dialog!==null&&e.dialog.id===t.id?{...e,dialog:null}:e;case"message_end":return t.message.role!=="assistant"?e:Hhs(e,t.message.usage.input+t.message.usage.cacheRead+t.message.usage.cacheWrite);case"agent_start":return xhe(e,{isStreaming:!0});case"agent_end":case"agent_settled":return xhe(e,{isStreaming:!1});case"session_info_changed":return xhe(e,{sessionName:t.name});case"thinking_level_changed":return xhe(e,{thinkingLevel:t.level});case"queue_update":{let r=[...t.steering,...t.followUp].map(o=>({text:o}));return{...xhe(e,{pendingMessageCount:r.length}),queued:r}}default:return e}}function Qhs(e){let t=e.queued;if(typeof t!="object"||t===null)return[];let{steering:r,followUp:i}=t;return[...RHn(r),...RHn(i)].map(o=>({text:o}))}function RHn(e){return Array.isArray(e)?e.filter(t=>typeof t=="string"):[]}function xhe(e,t){let r=e.engine;if(r===void 0)return e;let i=!1;for(let[o,a]of Object.entries(t))if(r[o]!==a){i=!0;break}return i?{...e,engine:{...r,...t}}:e}function Hhs(e,t){return e.contextTokens===t?e:{...e,contextTokens:t}}function Jhs(e,t){return e===void 0||t===void 0?e===t:e.placement===t.placement&&e.lines.length===t.lines.length&&e.lines.every((r,i)=>r===t.lines[i])}function OHn(e,t){let r=NHn(e),i=o=>{let a=r;if(a===o)return;let u=a.engine!==o.engine;u&&a.engine!==void 0&&(t.setEngineState(a.engine),t.setEditorTitle(a.engine.sessionName)),u&&t.paintEditorFrame(),a.display.widgets!==o.display.widgets&&t.renderExtensionWidgets(),a.display.title!==o.display.title&&a.display.title!==void 0&&t.setTerminalTitle(a.display.title),a.queued!==o.queued&&t.renderQueued(),(u||a.role!==o.role||a.contextTokens!==o.contextTokens||a.display.statuses!==o.display.statuses)&&t.renderFooter(),u&&t.refreshChrome()};return{state:()=>r,fold:o=>{let a=r;r=PHn(r,o),i(a)},patchLocal:o=>{let a=r.engine;if(a===void 0)return;let u=r;r={...r,engine:{...a,...o}},i(u)},modelThinkingLabel:()=>{let o=r.engine?.model?.id;if(o===void 0)return;let a=r.engine?.thinkingLevel;return a!==void 0&&a!=="off"?`${o} ${a}`:o}}}import{spawn as Yhs}from"node:child_process";import{fileURLToPath as $Jt}from"node:url";var Khs=40,DHn=[$Jt(new URL("./assets/whip-snap.mp3",import.meta.url)),$Jt(new URL("./assets/whip-crack.mp3",import.meta.url)),$Jt(new URL("./assets/whip-06.mp3",import.meta.url))];function Xhs(e){let t=process.platform==="darwin"?"afplay":process.platform==="linux"?"aplay":void 0;if(t!==void 0)try{let r=Yhs(t,[e],{stdio:"ignore",detached:!0});r.on("error",()=>{}),r.unref()}catch{}}var MHn=[["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u28A4\u2840","\x1B[1m\u280B\x1B[22m \u2811\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u2814\u2812\u2826\u2824\u28C4\u28C0","\x1B[1m\u280B\x1B[22m \u2808\u2809\u2813\u2812\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u28E6\x1B[22m","\x1B[1m\u285F\x1B[22m\x1B[1m\u2880\x1B[22m/\u2814\u2812\u2826\u2824\u28C4\u28C0\u2840","\x1B[1m\u28E7\x1B[22m\x1B[1m\u280E\x1B[22m \u2809\u2819\u2812\u2826\u2824\u28C4\u28C0","\x1B[1m\u280F\x1B[22m \u2809\u2819\u2812\u2812\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u28E6\x1B[22m \u2864\u2824\u2824\u28A4\u28C0\u2840","\x1B[1m\u285F\x1B[22m\x1B[1m\u2880\x1B[22m/\u281C \u2809\u2819\u2812\u2826\u2824\u2884\u28C0\u2840","\x1B[1m\u28E7\x1B[22m\x1B[1m\u280E\x1B[22m \u2809\u2809\u2811\u2812\u2826\u2824\u2884\u28C0\u2840","\x1B[1m\u280F\x1B[22m \u2808\u2809\u2811\u2812\u2806",""],["","","","","",""," \u2864\u2824\u2824\u2824\u28C4\u28C0","\x1B[1m\u28F4\x1B[22m\x1B[1m\u28B0\x1B[22m/\u281C \u2808\u2809\u2813\u2826\u28A4\u28C0","\x1B[1m\u28BF\x1B[22m\x1B[1m\u28FC\x1B[22m\x1B[1m\u2847\x1B[22m \u2808\u2811\u2826\u28C4\u2840","\x1B[1m\u28B8\x1B[22m\x1B[1m\u28FF\x1B[22m\x1B[1m\u2803\x1B[22m \u2809\u2813\u2826\u2824\u28C0\u28C0","\x1B[1m\u28B8\x1B[22m\x1B[1m\u28FF\x1B[22m \u2808\u2809\u2809\u2813\u2812\u2806"," \x1B[1m\u281B\x1B[22m"],["","","","",""," \u2864\u2824\u2824\u2824\u28C4\u28C0"," \x1B[1m\u2820\x1B[22m/\u281C \u2808\u2809\u2813\u2826\u28A4\u28C0","\x1B[1m\u28A4\x1B[22m \x1B[1m\u28FF\x1B[22m \u2808\u2819\u2832\u28A4\u2840"," \x1B[1m\u28A3\x1B[22m\x1B[1m\u28BB\x1B[22m\x1B[1m\u2846\x1B[22m \u2808\u2811\u2826\u2884\u2840"," \x1B[1m\u28BB\x1B[22m\x1B[1m\u2847\x1B[22m \u2808\u2811\u2822\u2884\u2840"," \x1B[1m\u2808\x1B[22m\x1B[1m\u281B\x1B[22m \u2808\u2811\u2806",""],["","","",""," \u2864\u2824\u2824\u2824\u28C4\u28C0"," /\u281C \u2808\u2809\u2813\u2826\u28A4\u28C0"," \x1B[1m\u28B8\x1B[22m\x1B[1m\u2840\x1B[22m \u2808\u2819\u2832\u28A4\u2840","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \x1B[1m\u2887\x1B[22m \u2808\u2811\u2826\u2884\u2840"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28BC\x1B[22m\x1B[1m\u2844\x1B[22m \u2808\u2811\u2822\u2884\u2840"," \u2808\u2811\u2806","",""],["","",""," \u28C0\u2860\u2824\u2824\u2824\u2824\u2824\u2824\u28C4\u28C0"," \u28E0\u2814\u281A\u2809 \u2808\u2809\u2813\u2826\u28A4\u28C0"," /\u281C\u2801 \u2808\u2819\u2826\u28C0"," \x1B[1m\u28B8\x1B[22m\x1B[1m\u2840\x1B[22m \u2808\u28A7","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \x1B[1m\u2887\x1B[22m \u2847"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28BC\x1B[22m\x1B[1m\u2844\x1B[22m \u2847"," \u2807","",""],[""," \u2880\u28E0\u2824\u2824\u2824\u2824\u2824\u28C4\u28C0"," \u28E0\u2814\u280A\u2801 \u2808\u2819\u2826\u28C4"," \u2880\u2874\u280B \u2808\u28B3\u2840"," \u28E0\u2814\u280B \u28A7"," /\u281C\u2801 \u284E"," \x1B[1m\u28B8\x1B[22m\x1B[1m\u2840\x1B[22m \u28A0\u2803","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \x1B[1m\u2887\x1B[22m \u281C"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28BC\x1B[22m\x1B[1m\u2844\x1B[22m","","",""],[" \u28C0\u2864\u2824\u28A4\u2840"," \u28C0\u2864\u2814\u280B\u2801 \u28A7"," \u28E0\u2814\u280B\u2801 \u28B8"," \u28E0\u281E\u2801 \u285C"," \u2880\u281C\u2801 \u287C\u2801"," \x1B[1m\u2820\x1B[22m/\u2814\u2801 \u281C\u2801"," \x1B[1m\u28FF\x1B[22m","\x1B[1m\u28A4\x1B[22m\x1B[1m\u2840\x1B[22m \x1B[1m\u28FF\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u28A6\x1B[22m\x1B[1m\u28F9\x1B[22m\x1B[1m\u2847\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u2803\x1B[22m","",""],[" \u28C0\u2864\u2824\u28A4\u2840"," \u28C0\u2864\u2814\u280B\u2801 \u2847"," \u28E0\u28F4\u282F\u2815\u2812\u2812\u2832\u2824\u2824\u2814\u281A\u2801"," \u28A0\u2814\u2812\u2826\u2824\u2814\u28F2\u281F\u2801"," \u281C \u2880\u281C\u2801"," \x1B[1m\u2820\x1B[22m/\u2814\u2801"," \x1B[1m\u28FF\x1B[22m","\x1B[1m\u28A4\x1B[22m\x1B[1m\u2840\x1B[22m \x1B[1m\u28FF\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u28A6\x1B[22m\x1B[1m\u28F9\x1B[22m\x1B[1m\u2847\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u2803\x1B[22m","",""],[" \u28C0\u2864\u2824\u28A4\u2840"," \u28C0\u2864\u2814\u280B\u2801 \u2847"," \u28E0\u28F4\u282F\u2815\u2812\u2812\u2832\u2824\u2824\u2814\u281A\u2801"," \u28A0\u2814\u2812\u2826\u2824\u2814\u28F2\u281F\u2801"," \u281C \u2880\u281C\u2801"," \x1B[1m\u2820\x1B[22m/\u2814\u2801"," \x1B[1m\u28FF\x1B[22m","\x1B[1m\u28A4\x1B[22m\x1B[1m\u2840\x1B[22m \x1B[1m\u28FF\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u28A6\x1B[22m\x1B[1m\u28F9\x1B[22m\x1B[1m\u2847\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u2803\x1B[22m","",""],["","","","","","","","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \u2880\u28E0\u2824\u2814\u2812\u2812\u2812\u2812\u2812\u2812\u2812\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28A4\x1B[22m\x1B[1m\u28C0\x1B[22m\x1B[1m\u28C0\x1B[22m\x1B[1m\u2824\x1B[22m\x1B[1m\u2824\x1B[22m/\u2814\u280A\u2801","","",""],["","","","","",""," \\\\|/","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \u2880\u28E0\u2824\u2814\u2812\u2812\u2812\u2812\u2812\u2812\u2812\u2826\u2824\u2824\u2824\u2824-*-"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28A4\x1B[22m\x1B[1m\u28C0\x1B[22m\x1B[1m\u28C0\x1B[22m\x1B[1m\u2824\x1B[22m\x1B[1m\u2824\x1B[22m/\u2814\u280A\u2801 /|\\\\","","",""],["","","","","","","","\x1B[1m\u28A4\x1B[22m\x1B[1m\u2840\x1B[22m"," \x1B[1m\u2833\x1B[22m\x1B[1m\u2840\x1B[22m \x1B[1m\u28E0\x1B[22m/\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804 ',."," \x1B[1m\u2839\x1B[22m\x1B[1m\u28C4\x1B[22m\x1B[1m\u2864\x1B[22m\x1B[1m\u280B\x1B[22m . ."," \x1B[1m\u2809\x1B[22m",""],["","","","","","","","\x1B[1m\u28F4\x1B[22m","\x1B[1m\u28BB\x1B[22m\x1B[1m\u2846\x1B[22m","\x1B[1m\u28B8\x1B[22m\x1B[1m\u2847\x1B[22m \x1B[1m\u28E0\x1B[22m/\u2814\u2812\u2812\u2826\u2824\u28C4\u28C0"," \x1B[1m\u28FF\x1B[22m\x1B[1m\u28E0\x1B[22m\x1B[1m\u281E\x1B[22m\x1B[1m\u2801\x1B[22m \u2808\u2809\u2813\u2812\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804 . ."," \x1B[1m\u281B\x1B[22m\x1B[1m\u2801\x1B[22m"],["","","","","","","","\x1B[1m\u28E6\x1B[22m","\x1B[1m\u287F\x1B[22m","\x1B[1m\u2847\x1B[22m \x1B[1m\u28E0\x1B[22m/\u28A4\u2840","\x1B[1m\u28E7\x1B[22m\x1B[1m\u281E\x1B[22m\x1B[1m\u2801\x1B[22m \u2809\u2813\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804","\x1B[1m\u2801\x1B[22m"],["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u28A4","\x1B[1m\u280B\x1B[22m \u2808\u2813\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u28A4\u2840","\x1B[1m\u280B\x1B[22m \u2811\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u28A4\u2840","\x1B[1m\u280B\x1B[22m \u2811\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""]],FHn="Go faster",LHn;function BHn(e){let t=e.filter(i=>i!==LHn),r=t[Math.floor(Math.random()*t.length)];return LHn=r,r}var Rrt=class{constructor(t,r){this.tui=t;this.pal=r}tui;pal;frame=-1;nextSound=0;timer;play(){this.stop(),Xhs(DHn[this.nextSound]),this.nextSound=(this.nextSound+1)%DHn.length,this.frame=0,this.tui.requestRender(),this.timer=setInterval(()=>{if(this.frame++,this.frame<MHn.length){this.tui.requestRender();return}this.stop()},Khs)}dispose(){this.stop()}render(t){if(this.frame<0)return[];let r=Math.max(0,t);return MHn[this.frame].map(i=>gl(this.pal.accent(i),r,"",!0))}invalidate(){}stop(){this.timer!==void 0&&clearInterval(this.timer),this.timer=void 0,this.frame=-1,this.tui.requestRender()}};async function X$u(e,t,r){Pte("viewer.connect_start",{node:e});let i=await iHn(e,r);Pte("viewer.socket_connected");let{remote:o,canvasSource:a,meta:u,socket:p}=i,f=nHn(o,t),_=Zhs();Mjn({cwd:u.cwd});let m=Ljn(),g=kjn(),E=rK(),v=E.bindings;await Pjn(g);let T=await Djn(),C=new d7,I=new f7(C),N=new al,k=new gtt,D=new rtt(I,N,{cwd:u.cwd,palette:m,hideThinking:!0,banner:j0r(rHn(u)),inlinePageBlocks:!o,pageOpenHint:()=>Eae(v),onActivityChange:mo=>{k.clear(),mo&&k.addChild(mo)}}),z=D.transcriptSource(),Z=p7n(z),Q=c7n(z,Z.decorate),J=$7n(D,Q,z),ie,ne=vQn({tui:I,pal:m},()=>ie.state()),be=M7n(I,m,Q,Z,{bindings:()=>v,setNotice:mo=>bt(mo)}),Ue=gHn(C,I,N,k,m,g,Q,be.component),{containers:He,editor:$e}=Ue,et=new Rrt(I,m);He.header.addChild(et),He.header.addChild(ne.component);let Nt={nodeId:e,remote:o,role:f,meta:u,canvasSource:a,socket:p,tui:I,pal:m,editor:$e,containers:He},It=cQn(Nt,{syncCursor:()=>wn.sync(),exitRoster:()=>pt.exit(),openTicket:mo=>ot.open(mo),suppressed:()=>ot.active(),setNotice:mo=>bt(mo)});He.header.addChild(It.component);let Fe=tk("user"),We=process.env.CRTR_ATTACH_INITIAL_WHIP==="1";delete process.env.CRTR_ATTACH_INITIAL_WHIP;let mt=$Wn(),tr=zQn(mo=>process.stdout.write(mo),Fe.mouse_mode_default),ar=qQn({frame:()=>Ue.frame.lastRenderedFrame(),width:()=>I.terminal.columns});Ue.frame.setOverlay(mo=>ar.apply(mo));let Br=SQn(Nt,{state:()=>ie.state(),bashMode:()=>Er.bashMode(),asks:()=>xi.asks(),bindings:()=>v,inboxAffordance:!o&&hv(),mouseEnabled:()=>tr.enabled()}),Tr=new yrt({tui:I,mouse:tr,rows:()=>I.terminal.rows,statusNotice:Br.setNotice,formatNotice:mo=>m.warning(`\u26A0 ${mo}`),noticeMs:3e3,capabilities:{remote:o,canFocusNode:!o&&process.env.TMUX_PANE!==void 0},selfPane:process.env.TMUX_PANE}),Ct=ZQn({host:Tr,...o?{}:{scan:()=>{let mo=new Map;for(let sn of oQ())mo.set(sn.dir,sn);for(let sn of ort(e))sn.state!=="pending"||!sn.awaitsResponse||sn.inbox||mo.has(sn.dir)||mo.set(sn.dir,sn);for(let sn of It.pendingTickets())mo.has(sn.dir)||mo.set(sn.dir,sn);return[...mo.values()].sort((sn,qn)=>(qn.kind==="page"?qn.emittedAt:qn.blockedSince).localeCompare(sn.kind==="page"?sn.emittedAt:sn.blockedSince)||sn.id.localeCompare(qn.id))}}}),Dt=o?()=>{}:bet(()=>{D.refreshPageBlocks(),It.refresh()}),ot=EQn(Nt,{syncCursor:()=>wn.sync(),refreshStrip:()=>{It.refresh()},setNotice:mo=>bt(mo),openInbox:mo=>Ct.open(mo),suspendForChild:()=>Tr.suspendForChild()});He.ticketPanel.addChild(ot.component);let _t={toggleInbox:()=>{let mo=ot.currentDir();if(mo!==void 0){ot.close(),Ct.open(mo);return}Ct.toggle()}},bt=(mo,sn)=>{Tr.hasFullPaneSurface()?Tr.notice(mo,sn):Br.setNotice(mo,sn)},it=Br.render,Et=mo=>{bt(J.copy(mo))},Mt=F7n(Q,()=>I.requestRender()),mr={"crtr.attach.scroll.up":()=>{Mt.cancel(),Q.scrollBy(-1)&&I.requestRender()},"crtr.attach.scroll.down":()=>{Mt.cancel(),Q.scrollBy(1)&&I.requestRender()},"crtr.attach.scroll.page-up":()=>{Mt.page("up")},"crtr.attach.scroll.page-down":()=>{Mt.page("down")},"crtr.attach.scroll.live":()=>{Mt.cancel(),Q.goLive(),I.requestRender()},"crtr.attach.mouse.toggle":()=>{let mo=tr.toggle();!mo&&ar.clear()&&I.requestRender(),bt(mo?"Mouse: wheel scrolling on, drag to select and copy":"Mouse: off \u2014 tmux drag-select and copy-mode restored")},"crtr.attach.copy.message":()=>Et("message")},Ze=Q9n(Nt),jt=bHn(Nt,Ze,{setNotice:bt,bindings:()=>v,fdPath:T}),$t=EHn(Nt,i.nodeIdentity,{isTornDown:()=>Mn.isTornDown(),onChanged:()=>{Er.syncContextBadge();let mo=ie.state().engine;mo!==void 0&&Er.setTitle(mo.sessionName),I.requestRender()}});o&&$t.refresh();let Er=fHn(Nt,{nodeIdentity:$t.current,thinkingLevel:()=>ie.state().engine?.thinkingLevel,renderFooter:it,onTextChange:jt.syncCommandPreview});Er.syncContextBadge();let{renderExtensionWidgets:lr,renderQueued:vr}=TQn(Nt,()=>ie.state());ie=OHn(f,{setEngineState:mo=>kn.setState(mo),setEditorTitle:Er.setTitle,paintEditorFrame:Er.paintFrame,renderExtensionWidgets:()=>{ne.render(),lr()},renderQueued:vr,renderFooter:it,setTerminalTitle:mo=>C.setTitle(mo),refreshChrome:()=>xi.refresh()});let ur=mo=>{if(!hv()){bt("Inspector requires tmux");return}bt(`Opening Inspector (${mo})\u2026`);let sn=o7(),Ks=`${sn===void 0?"":`CRTR_HOME=${Rm(sn)} `}crtr surface inspect ${Rm(e)} --section ${mo}`;e_s("tmux",["display-popup","-E","-T",` inspector \xB7 ${mo} `,"-w","90%","-h","85%",...oF(),Ks],()=>{xi.refresh()})},pt=rQn(Nt,{asks:()=>xi.asks(),setNotice:bt,requestChromeRefresh:()=>xi.refresh(),openInspector:ur,syncCursor:()=>wn.sync()}),wn=AQn($e,[pt.active,It.active,ot.active]),Rn=new mrt(I,e,()=>xi.asks(),m,v,a,f,mo=>xi.reportFailure("canvas graph refresh",mo),bt),Bi=new hrt(I,m);Bi.setBindings(v);let xi=CQn(Nt,{isTornDown:()=>Mn.isTornDown(),setNotice:bt,refreshRoster:async()=>{await pt.refresh(),await It.refresh()},refreshGraph:()=>Rn.refresh(),renderFooter:it,renderExtensionWidgets:lr,pollGit:Er.pollGit}),kn=H9n(Nt,g,Ze,{setNotice:bt,agentDir:()=>ie.state().agentDir,openNodeMetadata:()=>ur("overview"),isDormant:()=>Or.isDormant(),copyTarget:Et,toggleToolsExpanded:()=>D.toggleToolsExpanded(),toggleFoldTools:()=>D.toggleFoldSettledTools(),toggleThinking:()=>D.toggleThinking(),toggleGraph:()=>Rn.toggle(),toggleInbox:_t.toggleInbox,toggleHelp:()=>Bi.toggle(),toggleMouse:()=>tr.toggle(),quit:()=>Fn("detach"),setChipColor:Er.setChipColor,onPromptSent:()=>{Fe.whip_mode&&et.play()}}),mi=()=>{if(Tr.hasFullPaneSurface()){bt("Close the active surface before whipping the agent");return}let mo=Fe.whip_message_mode==="go-faster"?FHn:BHn(Fe.whip_messages);kn.sendInterrupting(mo)&&(et.play(),bt(`Whipped agent \u2014 ${mo}`))},ko=()=>{if(ot.active()){bt("Close the open request first (Esc)");return}let mo=D.getTranscriptFilePathsNewestFirst(),sn=qn=>{VWn(Tr,qn,{originNodeId:e,onNotice:bt})};if(Tr.hasFullPaneSurface()){Tr.pickReviewFile({candidates:mo,cwd:u.cwd,title:"Review a file named in this transcript"}).then(qn=>{qn!==null&&sn(qn)});return}kn.openFileReviewPicker(mo,u.cwd,sn)},Nn=()=>{if(o){bt("Profile file search is unavailable on remote canvases");return}if(!hv()){bt("Profile file search requires tmux");return}a.listNodes().then(mo=>{wHn({cwd:u.cwd,nodeDirs:xHn(mo,e),profileId:u.profile_id??null,fdPath:T,onNotice:bt})}).catch(mo=>{let sn=mo instanceof Error?mo.message:String(mo);bt(`Could not load related node directories: ${sn}`)})},fs=FQn(Nt,{foldState:ie.fold,chat:D,setNotice:bt,renderFooter:it,setEditorText:mo=>{$e.setText(mo),I.requestRender()},attachDialog:mo=>kn.attachDialog(mo),dismissDialog:mo=>kn.dismissDialog(mo),refreshNodeIdentity:$t.refresh,setCommands:jt.setCommands,resetMemoryRefs:jt.resetMemoryRefs,reloadBindings:()=>{E=iHt(rK,mo=>{Rn.setBindings(mo),Bi.setBindings(mo)},hv,R3e),v=E.bindings},modelThinkingLabel:ie.modelThinkingLabel}),bs=SHn(o,e),Mn=wQn(Nt,{stopLocal:()=>{Tr.closeAll(),Mt.cancel(),tr.disable(),xi.stop(),ot.close(),Br.dispose(),Rn.close(),Bi.close(),be.close(),et.dispose(),bs.clear(),Ga(),Ua(),zs(),Dt();try{D.dispose()}catch{}},lastSocketError:()=>Or.lastSocketError(),tagPane:bs.pane}),Fn=Mn.teardown,Or=NQn(Nt,{role:f,clientId:_,onFrame:fs,setNotice:bt,isTornDown:()=>Mn.isTornDown(),teardown:Fn,onEnterDormancy:()=>{D.settleRun(),ie.patchLocal({isStreaming:!1})}});i.assertConnected(),bs.set(),Or.install(),Ue.mount(),I.start(),Fe.whip_mode&&We&&et.play(),tr.reassert();let Kn=!1,Vi=mo=>{if(Kn||o)return;let sn=g8();if(sn===null)return;let qn=QQn({file:mo,pane:sn.pane,cwd:u.cwd,nodeId:e,observer:t,beforeRespawn:bs.clear});if(qn==="opened"){Kn=!0;return}qn==="failed"&&bs.set(),qn==="missing"&&bt("File no longer exists"),qn==="failed"&&bt("Could not open file in Neovim")};tr.install(I,{onWheel:(mo,sn)=>{ar.clear()&&I.requestRender(),!Tr.handleWheel(mo,sn)&&(Mt.cancel(),Q.scrollBy(mo==="up"?-sn:sn)&&I.requestRender())},onPrimaryPress:(mo,sn)=>{Tr.hasFullPaneSurface()||(ar.clear()&&I.requestRender(),ar.begin(mo,sn))},onPrimaryDrag:(mo,sn)=>{Tr.hasFullPaneSurface()||ar.extend(mo,sn)&&I.requestRender()},onPrimaryRelease:()=>{let mo=ar.release();mo?.kind==="selection"&&(bt(mo.notice),I.requestRender())}});let zs=I.addInputListener(mo=>{let sn=WQn(mo);if(sn!==void 0)return sn.file===void 0?bt("Could not open file link"):Vi(sn.file),sn.remaining.length>0?{data:sn.remaining}:{consume:!0}}),Ua=I.addInputListener(()=>{ar.clear()&&I.requestRender()}),Ga=BQn(Nt,{bindings:()=>v,detachHint:()=>O7n(v),hasExclusiveSurface:()=>Tr.hasFullPaneSurface(),setPaneFocused:mo=>{mo&&bs.stamp(),wn.setPaneFocused(mo),I.requestRender()},viewportActions:()=>mr,searchHandleKey:be.handleKey,panelHandleKey:ot.handleKey,stripHandleKey:It.handleKey,rosterHandleKey:pt.handleKey,setNotice:bt,detach:()=>Fn("detach"),toggleGraph:()=>Rn.toggle(),toggleHelp:()=>Bi.toggle(),toggleInbox:_t.toggleInbox,toggleInboxStrip:()=>{if(Tr.hasFullPaneSurface()){bt("Close the active surface before opening the request strip");return}ot.active()&&ot.close(),It.toggle()},cycleModelLadder:mo=>kn.cycleLadder(mo),inspectLoadedCommand:jt.inspectLoadedCommand,openFileReview:ko,openProfileFiles:Nn,openSearch:()=>{if(Tr.hasFullPaneSurface()||ot.active()){bt("Close the active surface before searching the transcript");return}be.open()},whip:mi});Mn.installSignalHandlers(),it(),xi.refresh(),vr(),Er.pollGit(),Pte("viewer.first_paint"),process.stdout.write(xQn),Ojn(C.kittyProtocolActive),setImmediate(()=>{iHt(()=>E,()=>{},hv,R3e)}),Pte("viewer.hello_sent"),Or.sendHello(),xi.start(),mt!==void 0&&zWn(Tr,mt),await Mn.done}export{X$u as runAttach};
2020
+ `,"utf8");let Z=`if command -v nvim >/dev/null 2>&1; then exec nvim -R -- ${Rm(z)}; else printf 'nvim is required to inspect loaded commands. Install Neovim and retry.\\n'; printf 'Press Enter to close.'; read _; fi`;hhs("tmux",["display-popup","-E","-w","90%","-h","85%",...oF(),Z],()=>ghs(D,{recursive:!0,force:!0}))}}}function EHn(e,t,r){let i=t;return{current:()=>i,refresh:()=>{e.canvasSource.getNode(e.nodeId).then(o=>{r.isTornDown()||o===null||(i=o,r.onChanged())}).catch(()=>{})}}}var Shs={readOption:$4,writeOption:(e,t,r)=>{U4(e,t,r)},stampAsync:c0t,currentPane:()=>process.env.TMUX_PANE??g8()?.pane,ownerId:()=>String(process.pid)};function SHn(e,t,r=Shs){let i,o=r.ownerId(),a=()=>{if(i===void 0||i==="")return!0;let u=r.readOption(i,THn);return u===void 0||u===""?!0:u===o};return{set:()=>{i=r.currentPane(),AHn(i,o,r.writeOption),Chs(e,i,t,r.writeOption),Ihs(i,r.writeOption),Ths(e,i,r.writeOption)},stamp:()=>{e||i===void 0||i===""||r.stampAsync(i)},clear:()=>{a()&&(xhs(e,i,r.writeOption),whs(i,r.writeOption),vhs(e,i,r.writeOption),AHn(i,"",r.writeOption))},pane:()=>i}}var THn="@crtr_viewer_pid";function AHn(e,t,r){if(!(e===void 0||e===""))try{r(e,THn,t)}catch{}}function Ths(e,t,r){if(!(e||t===void 0||t===""))try{r(t,I0e,String(Date.now()))}catch{}}function vhs(e,t,r=U4){if(!(e||t===void 0||t===""))try{r(t,I0e,"")}catch{}}function Chs(e,t,r,i=U4){if(!(e||t===void 0||t===""))try{i(t,"@crtr_node",r)}catch{}}function xhs(e,t,r=U4){if(!(e||t===void 0||t===""))try{r(t,"@crtr_node","")}catch{}}function Ihs(e,t=U4){if(!(e===void 0||e===""))try{t(e,"@crtr_attach","1")}catch{}}function whs(e,t=U4){if(!(e===void 0||e===""))try{t(e,"@crtr_attach","")}catch{}}import{constants as Rhs,accessSync as khs,existsSync as Nhs,mkdtempSync as Phs,readFileSync as Ohs,rmSync as wrt,statSync as Dhs}from"node:fs";import{tmpdir as Mhs}from"node:os";import{delimiter as Lhs,dirname as Fhs,join as kke,resolve as CHn,sep as Bhs}from"node:path";import{execFile as vHn,spawnSync as Uhs}from"node:child_process";function xHn(e,t){let r=new Map(e.map(a=>[a.node_id,a])),i=new Set([t]);for(let a=r.get(t);a?.parent!==null&&a?.parent!==void 0;){let u=a.parent;if(i.has(u))break;i.add(u),a=r.get(u)}let o=[t];for(;o.length>0;){let a=o.pop();for(let u of e)u.parent!==a||i.has(u.node_id)||(i.add(u.node_id),o.push(u.node_id))}return e.filter(a=>i.has(a.node_id)).map(a=>a.cwd)}function IHn(e){try{return Dhs(e).isDirectory()}catch{return!1}}function $hs(e,t){let r=[...t==null?[]:[t],...(process.env.PATH??"").split(Lhs).filter(Boolean).map(i=>kke(i,e))];return[...new Set(r)]}function BJt(e,t){for(let r of $hs(e,t)){try{khs(r,Rhs.X_OK)}catch{continue}if(Uhs(r,["--version"],{stdio:"ignore"}).status===0)return r}return null}function zhs(e){if(e===null||e==="")return{projects:[],memory:null};try{let{profileId:t,manifest:r}=mae(e);return{projects:r.projects.map(i=>i.path),memory:mTr(t)}}catch{return{projects:[],memory:null}}}function UJt(e){let t=new Set,r=[];for(let i of e){let o=CHn(i);t.has(o)||!IHn(o)||(t.add(o),r.push(o))}return r}function jhs(e){return e.filter(t=>!e.some(r=>r!==t&&t.startsWith(r+Bhs)))}function qhs(e){let t=[];for(let r of e){let i=CHn(r);for(;;){let o=kke(i,".crouter","memory");IHn(o)&&t.push(o);let a=Fhs(i);if(a===i)break;i=a}}return UJt(t)}var Ghs=20;function Vhs(e){try{let t=Ohs(e);if(t.length===0)return null;let r=t[t.length-1]===0?t.length-1:t.length;return t.subarray(0,r).toString("utf8")||null}catch{return null}}function wHn(e){let t=BJt("fd",e.fdPath);if(t===null){e.onNotice("fd is required for profile file search");return}let r=BJt("fzf");if(r===null){e.onNotice("fzf is required for profile file search");return}let i=BJt("nvim");if(i===null){e.onNotice("Neovim is required for profile file search");return}let o=zhs(e.profileId),a=UJt([e.cwd,...e.nodeDirs,...o.projects]);if(a.length===0){e.onNotice("No profile file-search directories exist");return}let u=jhs(a),p=UJt([...o.memory===null?[]:[o.memory],...qhs(a)]),f=Phs(kke(Mhs(),"crtr-profile-files-")),_=kke(f,"selection"),m=kke(f,"candidates"),v=`{ ${[...u.map(T=>`${Rm(t)} --type f --hidden --exclude .git --exclude ${Rm("**/.crouter/memory/**")} . ${Rm(T)}`),...p.map(T=>`${Rm(t)} --type f --hidden --exclude .git --no-ignore . ${Rm(T)}`)].map(T=>`${T} &`).join(" ")} wait; } > ${Rm(m)} && ${Rm(r)} --print0 --scheme=path --hscroll-off=${Ghs} --header ${Rm("fuzzy path \u2022 ctrl-y yank file path")} --bind ${Rm("ctrl-y:execute-silent(printf '%s' {} | { if command -v pbcopy >/dev/null 2>&1; then pbcopy; elif command -v wl-copy >/dev/null 2>&1; then wl-copy; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard; else cat >/dev/null; fi; }; printf '%s' {} | tmux load-buffer -w -)+abort")} --preview ${Rm("head -100 -- {}")} < ${Rm(m)} > ${Rm(_)}`;try{vHn("tmux",["display-popup","-E","-T"," profile files ","-w","80%","-h","60%",...oF(),v],()=>{let T=Vhs(_);if(T===null||!Nhs(T)){wrt(f,{recursive:!0,force:!0});return}let C=`exec ${Rm(i)} -- ${Rm(T)}`;try{vHn("tmux",["display-popup","-E","-T"," profile file ","-w","90%","-h","90%",...oF(),C],()=>wrt(f,{recursive:!0,force:!0}))}catch(I){wrt(f,{recursive:!0,force:!0}),e.onNotice(`Could not open profile file: ${I instanceof Error?I.message:String(I)}`)}})}catch(T){wrt(f,{recursive:!0,force:!0}),e.onNotice(`Could not open profile file search: ${T instanceof Error?T.message:String(T)}`)}}var kHn={statuses:{},widgets:{},title:void 0};function NHn(e="observer"){return{role:e,agentDir:void 0,engine:void 0,contextTokens:void 0,queued:[],display:kHn,dialog:null}}function Whs(e){let t=e.snapshot,r=t.display??kHn,i=t.stats.contextUsage;return{role:e.role,agentDir:e.agentDir,engine:t.state,contextTokens:i?.tokens??void 0,queued:Qhs(t),display:{statuses:{...r.statuses},widgets:Object.fromEntries(Object.entries(r.widgets).map(([o,a])=>[o,{lines:[...a.lines],placement:a.placement}])),title:r.title},dialog:e.pending_dialog??null}}function PHn(e,t){switch(t.type){case"welcome":return Whs(t);case"model_changed":return xhe(e,{model:t.model});case"extension_ui_request":switch(t.method){case"setStatus":{let r=e.display.statuses[t.statusKey];if(t.statusText===void 0?r===void 0:r===t.statusText)return e;let i={...e.display.statuses};return delete i[t.statusKey],t.statusText!==void 0&&(i[t.statusKey]=t.statusText),{...e,display:{...e.display,statuses:i}}}case"setWidget":{let r=t.widgetLines===void 0?void 0:{lines:[...t.widgetLines],placement:t.widgetPlacement??"aboveEditor"};if(Jhs(e.display.widgets[t.widgetKey],r))return e;let i={...e.display.widgets};return delete i[t.widgetKey],r!==void 0&&(i[t.widgetKey]=r),{...e,display:{...e.display,widgets:i}}}case"setTitle":return e.display.title===t.title?e:{...e,display:{...e.display,title:t.title}};case"notify":return e;default:return{...e,dialog:t}}case"extension_ui_dismiss":return e.dialog!==null&&e.dialog.id===t.id?{...e,dialog:null}:e;case"message_end":return t.message.role!=="assistant"?e:Hhs(e,t.message.usage.input+t.message.usage.cacheRead+t.message.usage.cacheWrite);case"agent_start":return xhe(e,{isStreaming:!0});case"agent_end":case"agent_settled":return xhe(e,{isStreaming:!1});case"session_info_changed":return xhe(e,{sessionName:t.name});case"thinking_level_changed":return xhe(e,{thinkingLevel:t.level});case"queue_update":{let r=[...t.steering,...t.followUp].map(o=>({text:o}));return{...xhe(e,{pendingMessageCount:r.length}),queued:r}}default:return e}}function Qhs(e){let t=e.queued;if(typeof t!="object"||t===null)return[];let{steering:r,followUp:i}=t;return[...RHn(r),...RHn(i)].map(o=>({text:o}))}function RHn(e){return Array.isArray(e)?e.filter(t=>typeof t=="string"):[]}function xhe(e,t){let r=e.engine;if(r===void 0)return e;let i=!1;for(let[o,a]of Object.entries(t))if(r[o]!==a){i=!0;break}return i?{...e,engine:{...r,...t}}:e}function Hhs(e,t){return e.contextTokens===t?e:{...e,contextTokens:t}}function Jhs(e,t){return e===void 0||t===void 0?e===t:e.placement===t.placement&&e.lines.length===t.lines.length&&e.lines.every((r,i)=>r===t.lines[i])}function OHn(e,t){let r=NHn(e),i=o=>{let a=r;if(a===o)return;let u=a.engine!==o.engine;u&&a.engine!==void 0&&(t.setEngineState(a.engine),t.setEditorTitle(a.engine.sessionName)),u&&t.paintEditorFrame(),a.display.widgets!==o.display.widgets&&t.renderExtensionWidgets(),a.display.title!==o.display.title&&a.display.title!==void 0&&t.setTerminalTitle(a.display.title),a.queued!==o.queued&&t.renderQueued(),(u||a.role!==o.role||a.contextTokens!==o.contextTokens||a.display.statuses!==o.display.statuses)&&t.renderFooter(),u&&t.refreshChrome()};return{state:()=>r,fold:o=>{let a=r;r=PHn(r,o),i(a)},patchLocal:o=>{let a=r.engine;if(a===void 0)return;let u=r;r={...r,engine:{...a,...o}},i(u)},modelThinkingLabel:()=>{let o=r.engine?.model?.id;if(o===void 0)return;let a=r.engine?.thinkingLevel;return a!==void 0&&a!=="off"?`${o} ${a}`:o}}}import{spawn as Yhs}from"node:child_process";import{fileURLToPath as $Jt}from"node:url";var Khs=40,DHn=[$Jt(new URL("./assets/whip-snap.mp3",import.meta.url)),$Jt(new URL("./assets/whip-crack.mp3",import.meta.url)),$Jt(new URL("./assets/whip-06.mp3",import.meta.url))];function Xhs(e){let t=process.platform==="darwin"?"afplay":process.platform==="linux"?"aplay":void 0;if(t!==void 0)try{let r=Yhs(t,[e],{stdio:"ignore",detached:!0});r.on("error",()=>{}),r.unref()}catch{}}var MHn=[["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u28A4\u2840","\x1B[1m\u280B\x1B[22m \u2811\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u2814\u2812\u2826\u2824\u28C4\u28C0","\x1B[1m\u280B\x1B[22m \u2808\u2809\u2813\u2812\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u28E6\x1B[22m","\x1B[1m\u285F\x1B[22m\x1B[1m\u2880\x1B[22m/\u2814\u2812\u2826\u2824\u28C4\u28C0\u2840","\x1B[1m\u28E7\x1B[22m\x1B[1m\u280E\x1B[22m \u2809\u2819\u2812\u2826\u2824\u28C4\u28C0","\x1B[1m\u280F\x1B[22m \u2809\u2819\u2812\u2812\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u28E6\x1B[22m \u2864\u2824\u2824\u28A4\u28C0\u2840","\x1B[1m\u285F\x1B[22m\x1B[1m\u2880\x1B[22m/\u281C \u2809\u2819\u2812\u2826\u2824\u2884\u28C0\u2840","\x1B[1m\u28E7\x1B[22m\x1B[1m\u280E\x1B[22m \u2809\u2809\u2811\u2812\u2826\u2824\u2884\u28C0\u2840","\x1B[1m\u280F\x1B[22m \u2808\u2809\u2811\u2812\u2806",""],["","","","","",""," \u2864\u2824\u2824\u2824\u28C4\u28C0","\x1B[1m\u28F4\x1B[22m\x1B[1m\u28B0\x1B[22m/\u281C \u2808\u2809\u2813\u2826\u28A4\u28C0","\x1B[1m\u28BF\x1B[22m\x1B[1m\u28FC\x1B[22m\x1B[1m\u2847\x1B[22m \u2808\u2811\u2826\u28C4\u2840","\x1B[1m\u28B8\x1B[22m\x1B[1m\u28FF\x1B[22m\x1B[1m\u2803\x1B[22m \u2809\u2813\u2826\u2824\u28C0\u28C0","\x1B[1m\u28B8\x1B[22m\x1B[1m\u28FF\x1B[22m \u2808\u2809\u2809\u2813\u2812\u2806"," \x1B[1m\u281B\x1B[22m"],["","","","",""," \u2864\u2824\u2824\u2824\u28C4\u28C0"," \x1B[1m\u2820\x1B[22m/\u281C \u2808\u2809\u2813\u2826\u28A4\u28C0","\x1B[1m\u28A4\x1B[22m \x1B[1m\u28FF\x1B[22m \u2808\u2819\u2832\u28A4\u2840"," \x1B[1m\u28A3\x1B[22m\x1B[1m\u28BB\x1B[22m\x1B[1m\u2846\x1B[22m \u2808\u2811\u2826\u2884\u2840"," \x1B[1m\u28BB\x1B[22m\x1B[1m\u2847\x1B[22m \u2808\u2811\u2822\u2884\u2840"," \x1B[1m\u2808\x1B[22m\x1B[1m\u281B\x1B[22m \u2808\u2811\u2806",""],["","","",""," \u2864\u2824\u2824\u2824\u28C4\u28C0"," /\u281C \u2808\u2809\u2813\u2826\u28A4\u28C0"," \x1B[1m\u28B8\x1B[22m\x1B[1m\u2840\x1B[22m \u2808\u2819\u2832\u28A4\u2840","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \x1B[1m\u2887\x1B[22m \u2808\u2811\u2826\u2884\u2840"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28BC\x1B[22m\x1B[1m\u2844\x1B[22m \u2808\u2811\u2822\u2884\u2840"," \u2808\u2811\u2806","",""],["","",""," \u28C0\u2860\u2824\u2824\u2824\u2824\u2824\u2824\u28C4\u28C0"," \u28E0\u2814\u281A\u2809 \u2808\u2809\u2813\u2826\u28A4\u28C0"," /\u281C\u2801 \u2808\u2819\u2826\u28C0"," \x1B[1m\u28B8\x1B[22m\x1B[1m\u2840\x1B[22m \u2808\u28A7","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \x1B[1m\u2887\x1B[22m \u2847"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28BC\x1B[22m\x1B[1m\u2844\x1B[22m \u2847"," \u2807","",""],[""," \u2880\u28E0\u2824\u2824\u2824\u2824\u2824\u28C4\u28C0"," \u28E0\u2814\u280A\u2801 \u2808\u2819\u2826\u28C4"," \u2880\u2874\u280B \u2808\u28B3\u2840"," \u28E0\u2814\u280B \u28A7"," /\u281C\u2801 \u284E"," \x1B[1m\u28B8\x1B[22m\x1B[1m\u2840\x1B[22m \u28A0\u2803","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \x1B[1m\u2887\x1B[22m \u281C"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28BC\x1B[22m\x1B[1m\u2844\x1B[22m","","",""],[" \u28C0\u2864\u2824\u28A4\u2840"," \u28C0\u2864\u2814\u280B\u2801 \u28A7"," \u28E0\u2814\u280B\u2801 \u28B8"," \u28E0\u281E\u2801 \u285C"," \u2880\u281C\u2801 \u287C\u2801"," \x1B[1m\u2820\x1B[22m/\u2814\u2801 \u281C\u2801"," \x1B[1m\u28FF\x1B[22m","\x1B[1m\u28A4\x1B[22m\x1B[1m\u2840\x1B[22m \x1B[1m\u28FF\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u28A6\x1B[22m\x1B[1m\u28F9\x1B[22m\x1B[1m\u2847\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u2803\x1B[22m","",""],[" \u28C0\u2864\u2824\u28A4\u2840"," \u28C0\u2864\u2814\u280B\u2801 \u2847"," \u28E0\u28F4\u282F\u2815\u2812\u2812\u2832\u2824\u2824\u2814\u281A\u2801"," \u28A0\u2814\u2812\u2826\u2824\u2814\u28F2\u281F\u2801"," \u281C \u2880\u281C\u2801"," \x1B[1m\u2820\x1B[22m/\u2814\u2801"," \x1B[1m\u28FF\x1B[22m","\x1B[1m\u28A4\x1B[22m\x1B[1m\u2840\x1B[22m \x1B[1m\u28FF\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u28A6\x1B[22m\x1B[1m\u28F9\x1B[22m\x1B[1m\u2847\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u2803\x1B[22m","",""],[" \u28C0\u2864\u2824\u28A4\u2840"," \u28C0\u2864\u2814\u280B\u2801 \u2847"," \u28E0\u28F4\u282F\u2815\u2812\u2812\u2832\u2824\u2824\u2814\u281A\u2801"," \u28A0\u2814\u2812\u2826\u2824\u2814\u28F2\u281F\u2801"," \u281C \u2880\u281C\u2801"," \x1B[1m\u2820\x1B[22m/\u2814\u2801"," \x1B[1m\u28FF\x1B[22m","\x1B[1m\u28A4\x1B[22m\x1B[1m\u2840\x1B[22m \x1B[1m\u28FF\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u28A6\x1B[22m\x1B[1m\u28F9\x1B[22m\x1B[1m\u2847\x1B[22m"," \x1B[1m\u2819\x1B[22m\x1B[1m\u2803\x1B[22m","",""],["","","","","","","","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \u2880\u28E0\u2824\u2814\u2812\u2812\u2812\u2812\u2812\u2812\u2812\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28A4\x1B[22m\x1B[1m\u28C0\x1B[22m\x1B[1m\u28C0\x1B[22m\x1B[1m\u2824\x1B[22m\x1B[1m\u2824\x1B[22m/\u2814\u280A\u2801","","",""],["","","","","",""," \\\\|/","\x1B[1m\u2824\x1B[22m\x1B[1m\u28C4\x1B[22m \u2880\u28E0\u2824\u2814\u2812\u2812\u2812\u2812\u2812\u2812\u2812\u2826\u2824\u2824\u2824\u2824-*-"," \x1B[1m\u2808\x1B[22m\x1B[1m\u2813\x1B[22m\x1B[1m\u28A4\x1B[22m\x1B[1m\u28C0\x1B[22m\x1B[1m\u28C0\x1B[22m\x1B[1m\u2824\x1B[22m\x1B[1m\u2824\x1B[22m/\u2814\u280A\u2801 /|\\\\","","",""],["","","","","","","","\x1B[1m\u28A4\x1B[22m\x1B[1m\u2840\x1B[22m"," \x1B[1m\u2833\x1B[22m\x1B[1m\u2840\x1B[22m \x1B[1m\u28E0\x1B[22m/\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804 ',."," \x1B[1m\u2839\x1B[22m\x1B[1m\u28C4\x1B[22m\x1B[1m\u2864\x1B[22m\x1B[1m\u280B\x1B[22m . ."," \x1B[1m\u2809\x1B[22m",""],["","","","","","","","\x1B[1m\u28F4\x1B[22m","\x1B[1m\u28BB\x1B[22m\x1B[1m\u2846\x1B[22m","\x1B[1m\u28B8\x1B[22m\x1B[1m\u2847\x1B[22m \x1B[1m\u28E0\x1B[22m/\u2814\u2812\u2812\u2826\u2824\u28C4\u28C0"," \x1B[1m\u28FF\x1B[22m\x1B[1m\u28E0\x1B[22m\x1B[1m\u281E\x1B[22m\x1B[1m\u2801\x1B[22m \u2808\u2809\u2813\u2812\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804 . ."," \x1B[1m\u281B\x1B[22m\x1B[1m\u2801\x1B[22m"],["","","","","","","","\x1B[1m\u28E6\x1B[22m","\x1B[1m\u287F\x1B[22m","\x1B[1m\u2847\x1B[22m \x1B[1m\u28E0\x1B[22m/\u28A4\u2840","\x1B[1m\u28E7\x1B[22m\x1B[1m\u281E\x1B[22m\x1B[1m\u2801\x1B[22m \u2809\u2813\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804","\x1B[1m\u2801\x1B[22m"],["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u28A4","\x1B[1m\u280B\x1B[22m \u2808\u2813\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u28A4\u2840","\x1B[1m\u280B\x1B[22m \u2811\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""],["","","","","","","","\x1B[1m\u2844\x1B[22m","\x1B[1m\u2803\x1B[22m","\x1B[1m\u2880\x1B[22m\x1B[1m\u2860\x1B[22m/\u28A4\u2840","\x1B[1m\u280B\x1B[22m \u2811\u2826\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2824\u2804",""]],FHn="Go faster",LHn;function BHn(e){let t=e.filter(i=>i!==LHn),r=t[Math.floor(Math.random()*t.length)];return LHn=r,r}var Rrt=class{constructor(t,r){this.tui=t;this.pal=r}tui;pal;frame=-1;nextSound=0;timer;play(){this.stop(),Xhs(DHn[this.nextSound]),this.nextSound=(this.nextSound+1)%DHn.length,this.frame=0,this.tui.requestRender(),this.timer=setInterval(()=>{if(this.frame++,this.frame<MHn.length){this.tui.requestRender();return}this.stop()},Khs)}dispose(){this.stop()}render(t){if(this.frame<0)return[];let r=Math.max(0,t);return MHn[this.frame].map(i=>gl(this.pal.accent(i),r,"",!0))}invalidate(){}stop(){this.timer!==void 0&&clearInterval(this.timer),this.timer=void 0,this.frame=-1,this.tui.requestRender()}};async function X$u(e,t,r){Pte("viewer.connect_start",{node:e});let i=await iHn(e,r);Pte("viewer.socket_connected");let{remote:o,canvasSource:a,meta:u,socket:p}=i,f=nHn(o,t),_=Zhs();Mjn({cwd:u.cwd});let m=Ljn(),g=kjn(),E=rK(),v=E.bindings;await Pjn(g);let T=await Djn(),C=new d7,I=new f7(C),N=new al,k=new gtt,D=new rtt(I,N,{cwd:u.cwd,palette:m,hideThinking:!0,banner:j0r(rHn(u)),inlinePageBlocks:!o,pageOpenHint:()=>Eae(v),onActivityChange:mo=>{k.clear(),mo&&k.addChild(mo)}}),z=D.transcriptSource(),Z=p7n(z),Q=c7n(z,Z.decorate),J=$7n(D,Q,z),ie,ne=vQn({tui:I,pal:m},()=>ie.state()),be=M7n(I,m,Q,Z,{bindings:()=>v,setNotice:mo=>bt(mo)}),Ue=gHn(C,I,N,k,m,g,Q,be.component),{containers:He,editor:$e}=Ue,et=new Rrt(I,m);He.header.addChild(et),He.header.addChild(ne.component);let Nt={nodeId:e,remote:o,role:f,meta:u,canvasSource:a,socket:p,tui:I,pal:m,editor:$e,containers:He},It=cQn(Nt,{syncCursor:()=>wn.sync(),exitRoster:()=>pt.exit(),openTicket:mo=>ot.open(mo),suppressed:()=>ot.active(),setNotice:mo=>bt(mo)});He.header.addChild(It.component);let Fe=tk("user"),We=process.env.CRTR_ATTACH_INITIAL_WHIP==="1";delete process.env.CRTR_ATTACH_INITIAL_WHIP;let mt=$Wn(),tr=zQn(mo=>process.stdout.write(mo),Fe.mouse_mode_default),ar=qQn({frame:()=>Ue.frame.lastRenderedFrame(),width:()=>I.terminal.columns});Ue.frame.setOverlay(mo=>ar.apply(mo));let Br=SQn(Nt,{state:()=>ie.state(),bashMode:()=>Er.bashMode(),asks:()=>xi.asks(),bindings:()=>v,inboxAffordance:!o&&hv(),mouseEnabled:()=>tr.enabled()}),Tr=new yrt({tui:I,mouse:tr,rows:()=>I.terminal.rows,statusNotice:Br.setNotice,formatNotice:mo=>m.warning(`\u26A0 ${mo}`),noticeMs:3e3,capabilities:{remote:o,canFocusNode:!o&&process.env.TMUX_PANE!==void 0},selfPane:process.env.TMUX_PANE}),Ct=ZQn({host:Tr,...o?{}:{scan:()=>{let mo=new Map;for(let sn of oQ())mo.set(sn.dir,sn);for(let sn of ort(e))sn.state!=="pending"||!sn.awaitsResponse||sn.inbox||mo.has(sn.dir)||mo.set(sn.dir,sn);for(let sn of It.pendingTickets())mo.has(sn.dir)||mo.set(sn.dir,sn);return[...mo.values()].sort((sn,qn)=>(qn.kind==="page"?qn.emittedAt:qn.blockedSince).localeCompare(sn.kind==="page"?sn.emittedAt:sn.blockedSince)||sn.id.localeCompare(qn.id))}}}),Dt=o?()=>{}:bet(()=>{D.refreshPageBlocks(),It.refresh()}),ot=EQn(Nt,{syncCursor:()=>wn.sync(),refreshStrip:()=>{It.refresh()},setNotice:mo=>bt(mo),openInbox:mo=>Ct.open(mo),suspendForChild:()=>Tr.suspendForChild()});He.ticketPanel.addChild(ot.component);let _t={toggleInbox:()=>{let mo=ot.currentDir();if(mo!==void 0){ot.close(),Ct.open(mo);return}Ct.toggle()}},bt=(mo,sn)=>{Tr.hasFullPaneSurface()?Tr.notice(mo,sn):Br.setNotice(mo,sn)},it=Br.render,Et=mo=>{bt(J.copy(mo))},Mt=F7n(Q,()=>I.requestRender()),mr={"crtr.attach.scroll.up":()=>{Mt.cancel(),Q.scrollBy(-1)&&I.requestRender()},"crtr.attach.scroll.down":()=>{Mt.cancel(),Q.scrollBy(1)&&I.requestRender()},"crtr.attach.scroll.page-up":()=>{Mt.page("up")},"crtr.attach.scroll.page-down":()=>{Mt.page("down")},"crtr.attach.scroll.live":()=>{Mt.cancel(),Q.goLive(),I.requestRender()},"crtr.attach.mouse.toggle":()=>{let mo=tr.toggle();!mo&&ar.clear()&&I.requestRender(),bt(mo?"Mouse: wheel scrolling on, drag to select and copy":"Mouse: off \u2014 tmux drag-select and copy-mode restored")},"crtr.attach.copy.message":()=>Et("message")},Ze=Q9n(Nt),jt=bHn(Nt,Ze,{setNotice:bt,bindings:()=>v,fdPath:T}),$t=EHn(Nt,i.nodeIdentity,{isTornDown:()=>Mn.isTornDown(),onChanged:()=>{Er.syncContextBadge();let mo=ie.state().engine;mo!==void 0&&Er.setTitle(mo.sessionName),I.requestRender()}});o&&$t.refresh();let Er=fHn(Nt,{nodeIdentity:$t.current,thinkingLevel:()=>ie.state().engine?.thinkingLevel,renderFooter:it,onTextChange:jt.syncCommandPreview});Er.syncContextBadge();let{renderExtensionWidgets:lr,renderQueued:vr}=TQn(Nt,()=>ie.state());ie=OHn(f,{setEngineState:mo=>kn.setState(mo),setEditorTitle:Er.setTitle,paintEditorFrame:Er.paintFrame,renderExtensionWidgets:()=>{ne.render(),lr()},renderQueued:vr,renderFooter:it,setTerminalTitle:mo=>C.setTitle(mo),refreshChrome:()=>xi.refresh()});let ur=mo=>{if(!hv()){bt("Inspector requires tmux");return}bt(`Opening Inspector (${mo})\u2026`);let sn=o7(),Ks=`${sn===void 0?"":`CRTR_HOME=${Rm(sn)} `}crtr surface inspect ${Rm(e)} --section ${mo}`;e_s("tmux",["display-popup","-E","-T",` inspector \xB7 ${mo} `,"-w","90%","-h","85%",...oF(),Ks],()=>{xi.refresh()})},pt=rQn(Nt,{asks:()=>xi.asks(),setNotice:bt,requestChromeRefresh:()=>xi.refresh(),openInspector:ur,syncCursor:()=>wn.sync()}),wn=AQn($e,[pt.active,It.active,ot.active]),Rn=new mrt(I,e,()=>xi.asks(),m,v,a,f,mo=>xi.reportFailure("canvas graph refresh",mo),bt),Bi=new hrt(I,m);Bi.setBindings(v);let xi=CQn(Nt,{isTornDown:()=>Mn.isTornDown(),setNotice:bt,refreshRoster:async()=>{await pt.refresh(),await It.refresh()},refreshGraph:()=>Rn.refresh(),renderFooter:it,renderExtensionWidgets:lr,pollGit:Er.pollGit}),kn=H9n(Nt,g,Ze,{setNotice:bt,agentDir:()=>ie.state().agentDir,openNodeMetadata:()=>ur("overview"),isDormant:()=>Or.isDormant(),copyTarget:Et,toggleToolsExpanded:()=>D.toggleToolsExpanded(),toggleFoldTools:()=>D.toggleFoldSettledTools(),toggleThinking:()=>D.toggleThinking(),toggleGraph:()=>Rn.toggle(),toggleInbox:_t.toggleInbox,toggleHelp:()=>Bi.toggle(),toggleMouse:()=>tr.toggle(),quit:()=>Fn("detach"),setChipColor:Er.setChipColor,onPromptSent:()=>{Fe.whip_mode&&et.play()}}),mi=()=>{if(Tr.hasFullPaneSurface()){bt("Close the active surface before whipping the agent");return}let mo=Fe.whip_message_mode==="go-faster"?FHn:BHn(Fe.whip_messages);kn.sendInterrupting(mo)&&(et.play(),bt(`Whipped agent \u2014 ${mo}`))},ko=()=>{if(ot.active()){bt("Close the open request first (Esc)");return}let mo=D.getTranscriptFilePathsNewestFirst(),sn=qn=>{VWn(Tr,qn,{originNodeId:e,onNotice:bt})};if(Tr.hasFullPaneSurface()){Tr.pickReviewFile({candidates:mo,cwd:u.cwd,title:"Review a file named in this transcript"}).then(qn=>{qn!==null&&sn(qn)});return}kn.openFileReviewPicker(mo,u.cwd,sn)},Nn=()=>{if(o){bt("Profile file search is unavailable on remote canvases");return}if(!hv()){bt("Profile file search requires tmux");return}a.listNodes().then(mo=>{wHn({cwd:u.cwd,nodeDirs:xHn(mo,e),profileId:u.profile_id??null,fdPath:T,onNotice:bt})}).catch(mo=>{let sn=mo instanceof Error?mo.message:String(mo);bt(`Could not load related node directories: ${sn}`)})},fs=FQn(Nt,{foldState:ie.fold,chat:D,setNotice:bt,renderFooter:it,setEditorText:mo=>{$e.setText(mo),I.requestRender()},attachDialog:mo=>kn.attachDialog(mo),dismissDialog:mo=>kn.dismissDialog(mo),refreshNodeIdentity:$t.refresh,setCommands:jt.setCommands,resetMemoryRefs:jt.resetMemoryRefs,reloadBindings:()=>{E=iHt(rK,mo=>{Rn.setBindings(mo),Bi.setBindings(mo)},hv,R3e),v=E.bindings},modelThinkingLabel:ie.modelThinkingLabel}),bs=SHn(o,e),Mn=wQn(Nt,{stopLocal:()=>{Tr.closeAll(),Mt.cancel(),tr.disable(),xi.stop(),ot.close(),Br.dispose(),Rn.close(),Bi.close(),be.close(),et.dispose(),bs.clear(),Ga(),Ua(),zs(),Dt();try{D.dispose()}catch{}},lastSocketError:()=>Or.lastSocketError(),tagPane:bs.pane}),Fn=Mn.teardown,Or=NQn(Nt,{role:f,clientId:_,onFrame:fs,setNotice:bt,isTornDown:()=>Mn.isTornDown(),teardown:Fn,onEnterDormancy:()=>{D.settleRun(),ie.patchLocal({isStreaming:!1})}});i.assertConnected(),bs.set(),Or.install(),Ue.mount(),I.start(),Fe.whip_mode&&We&&et.play(),tr.reassert();let Kn=!1,Vi=mo=>{if(Kn||o)return;let sn=g8();if(sn===null)return;let qn=QQn({file:mo,pane:sn.pane,cwd:u.cwd,nodeId:e,observer:t,beforeRespawn:bs.clear});if(qn==="opened"){Kn=!0;return}qn==="failed"&&bs.set(),qn==="missing"&&bt("File no longer exists"),qn==="failed"&&bt("Could not open file in Neovim")};tr.install(I,{onWheel:(mo,sn)=>{ar.clear()&&I.requestRender(),!Tr.handleWheel(mo,sn)&&(Mt.cancel(),Q.scrollBy(mo==="up"?-sn:sn)&&I.requestRender())},onPrimaryPress:(mo,sn)=>{Tr.hasFullPaneSurface()||(ar.clear()&&I.requestRender(),ar.begin(mo,sn))},onPrimaryDrag:(mo,sn)=>{Tr.hasFullPaneSurface()||ar.extend(mo,sn)&&I.requestRender()},onPrimaryRelease:()=>{let mo=ar.release();mo?.kind==="selection"&&(bt(mo.notice),I.requestRender())}});let zs=I.addInputListener(mo=>{let sn=WQn(mo);if(sn!==void 0)return sn.file===void 0?bt("Could not open file link"):Vi(sn.file),sn.remaining.length>0?{data:sn.remaining}:{consume:!0}}),Ua=I.addInputListener(()=>{ar.clear()&&I.requestRender()}),Ga=BQn(Nt,{bindings:()=>v,detachHint:()=>O7n(v),hasExclusiveSurface:()=>Tr.hasFullPaneSurface(),setPaneFocused:mo=>{mo&&bs.stamp(),wn.setPaneFocused(mo),I.requestRender()},viewportActions:()=>mr,searchHandleKey:be.handleKey,panelHandleKey:ot.handleKey,stripHandleKey:It.handleKey,rosterHandleKey:pt.handleKey,setNotice:bt,detach:()=>Fn("detach"),toggleGraph:()=>Rn.toggle(),toggleHelp:()=>Bi.toggle(),toggleInbox:_t.toggleInbox,toggleInboxStrip:()=>{if(Tr.hasFullPaneSurface()){bt("Close the active surface before opening the request strip");return}ot.active()&&ot.close(),It.toggle()},cycleModelLadder:mo=>kn.cycleLadder(mo),inspectLoadedCommand:jt.inspectLoadedCommand,openFileReview:ko,openProfileFiles:Nn,openSearch:()=>{if(Tr.hasFullPaneSurface()||ot.active()){bt("Close the active surface before searching the transcript");return}be.open()},whip:mi});Mn.installSignalHandlers(),it(),xi.refresh(),vr(),Er.pollGit(),Pte("viewer.first_paint"),process.stdout.write(xQn),Ojn(C.kittyProtocolActive),setImmediate(()=>{iHt(()=>E,()=>{},hv,R3e)}),Pte("viewer.hello_sent"),Or.sendHello(),xi.start(),mt!==void 0&&zWn(Tr,mt),await Mn.done}export{X$u as runAttach};
2021
2021
  /*! Bundled license information:
2022
2022
 
2023
2023
  web-streams-polyfill/dist/ponyfill.es2018.js:
@@ -75,7 +75,7 @@ function nodeNewParams() {
75
75
  return [
76
76
  { kind: 'stdin', name: 'prompt', required: false, constraint: 'First user message.' },
77
77
  { kind: 'flag', name: 'kind', type: 'string', required: false, constraint: "Persona kind. Defaults to the profile's default kind; `general` when the profile has none. The <kinds> list below names every top-level installable kind." },
78
- { kind: 'flag', name: 'mode', type: 'enum', choices: ['base', 'orchestrator'], required: false, default: 'base', constraint: 'Persona mode. base for hands-on work; orchestrator when the unit itself needs decomposition across children.' },
78
+ { kind: 'flag', name: 'mode', type: 'enum', choices: ['base', 'orchestrator'], required: false, default: 'base', constraint: 'Persona mode. base for one hands-on unit; orchestrator when the unit itself needs decomposition across children. When you hold several parallel units of one kind, spawn ONE orchestrator child of that kind to own the fan-out rather than spawning the units yourself — unless you are already an orchestrator of that kind, whose job is exactly that fan-out.' },
79
79
  { kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: 'Pin the node to this directory. Defaults to the spawner\u2019s directory for a managed child, and to the directory this command runs in for a root.' },
80
80
  { kind: 'flag', name: 'name', type: 'string', required: false, constraint: 'Display name.' },
81
81
  { kind: 'flag', name: 'parent', type: 'string', required: false, constraint: 'Parent node id. Defaults to the calling node.' },
@@ -147,6 +147,7 @@ function nodeNewOutput() {
147
147
  return [
148
148
  { name: 'node_id', type: 'string', required: true, constraint: 'New node id.' },
149
149
  { name: 'name', type: 'string', required: true, constraint: 'Display name.' },
150
+ { name: 'frozen_at', type: 'string', required: false, constraint: 'Present when a managed child exists but capacity deferred its broker start.' },
150
151
  ...(inTmux()
151
152
  ? [
152
153
  { name: 'window', type: 'string', required: false, constraint: 'tmux window id when a viewer opens.' },
@@ -242,23 +243,27 @@ async function runNodeCreation(input) {
242
243
  placement = openSpawnViewer(detail.node_id, detail.cwd, detail.name);
243
244
  if (root)
244
245
  mark('node_new.viewer_window_opened');
246
+ const frozen = !root && detail.frozen_at !== null;
245
247
  const result = {
246
248
  node_id: detail.node_id,
247
249
  name: detail.name,
250
+ ...(frozen ? { frozen_at: detail.frozen_at } : {}),
248
251
  window: placement.window,
249
252
  session: placement.session,
250
253
  follow_up: root
251
254
  ? (noKickoff
252
255
  ? 'Independent root spawned idle. No kickoff turn ran; hand it off or attach and prompt it. No report wakes you.'
253
256
  : 'Independent root spawned. You are not subscribed, so its finish does not wake you; hand it off and move on.')
254
- : await childFollowUp(parentId),
257
+ : (frozen
258
+ ? 'Child row exists, but no broker started because capacity is full. It is frozen and will launch when a broker slot frees; its final report wakes you then.'
259
+ : await childFollowUp(parentId)),
255
260
  };
256
261
  return result;
257
262
  }
258
263
  export const nodeNew = defineLeaf({
259
264
  name: 'new',
260
265
  description: 'create and start a node immediately',
261
- whenToUse: 'a unit of work is genuinely separable from what you are doing: a bounded subtask you would otherwise grind out inline, several independent units worth running in parallel, a scouting pass over unfamiliar code, a change that wants its own git worktree, or an answer you need in a fixed shape rather than as prose',
266
+ whenToUse: 'a unit of work is genuinely separable from what you are doing: a bounded subtask you would otherwise grind out inline, several independent units worth running in parallel (usually via one orchestrator child of the matching kind rather than a flat fan-out from you), a scouting pass over unfamiliar code, a change that wants its own git worktree, or an answer you need in a fixed shape rather than as prose',
262
267
  tier: 'important',
263
268
  help: {
264
269
  name: 'node new',
@@ -26,27 +26,37 @@ export declare class ContextDocsPanel {
26
26
  private event;
27
27
  private lines;
28
28
  private index;
29
- /** Groups the user opened. Empty means every group is collapsed, which is how
30
- * the page opens: ~25 rows that name the whole corpus. */
29
+ /** Directories the user opened, by full name prefix. Empty means every level
30
+ * is collapsed, which is how the page opens: ~25 rows that name the whole
31
+ * corpus. */
31
32
  private readonly expanded;
32
33
  constructor(onChange: () => void);
33
34
  /** The document the dossier describes: the focused row, or the first member
34
35
  * of the focused group so the dossier is never blank. */
35
36
  get selected(): DocRow | undefined;
36
- /** Documents the current question selects — counted from the group rollups,
37
- * so a fully collapsed list still reports its size. */
37
+ /** Documents the current question selects — counted from the top-level
38
+ * rollups, which partition the corpus, so a fully collapsed list still
39
+ * reports its size and a nested rollup is never counted twice. */
38
40
  get visibleCount(): number;
39
41
  setRows(rows: DocRow[], event: SurfaceEvent, selectedKey?: string): void;
40
42
  setFilters(filters: Filters): void;
41
43
  move(delta: number): void;
42
- /** True when the focused line is a group, so the shell knows whether a
43
- * descend opens a group or the dossier. */
44
+ /** True when the focused line can be opened — a directory header. */
44
45
  get onGroup(): boolean;
46
+ /** True when the focused line IS a document rather than merely standing for
47
+ * one. A directory that is also a document is both, which is why the two
48
+ * gestures split there: space opens it, enter edits it. */
49
+ get onDocument(): boolean;
45
50
  toggleGroup(): void;
46
51
  /** Rebuild the drawn lines, keeping the cursor on the same document (or the
47
- * same group), and clamping when what it was on is gone. */
52
+ * same directory), and clamping when what it was on is gone. A document that
53
+ * owns a directory is drawn as that directory's header, so a held document
54
+ * key matches there too. */
48
55
  private relayout;
49
56
  render(width: number, height: number, focused: boolean, plans: PlanSet): string[];
57
+ /** A directory header. Its stats and cost are the whole subtree's; the matrix
58
+ * is drawn only when the directory is itself a document, which is what marks
59
+ * the one row as both a document and a directory. */
50
60
  private groupLine;
51
61
  private docLine;
52
62
  /** Pad rather than trim: this column sets the length of the divider drawn
@@ -1,7 +1,7 @@
1
- // The middle column: every document the snapshot can see, grouped by the first
2
- // segment of its canonical name and each carrying its own six-event matrix. One
3
- // row is one physical document, so two stores answering to the same canonical
4
- // name stay two rows and the shadowed one is visible.
1
+ // The middle column: every document the snapshot can see, grouped recursively
2
+ // by the directories its canonical name names, and each carrying its own
3
+ // six-event matrix. One row is one physical document, so two stores answering
4
+ // to the same canonical name stay two rows and the shadowed one is visible.
5
5
  import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
6
6
  import { padAnsi, theme, visibleRange } from '../../../../core/tui/panel.js';
7
7
  import { SURFACE_EVENTS } from '../../../../core/substrate/schema.js';
@@ -50,6 +50,9 @@ const COST_WIDTH = 6;
50
50
  /** The glyphs a group header counts its members with — the matrix vocabulary,
51
51
  * so a rung reads the same in both columns. */
52
52
  const RUNG_MARK = { content: '█', preview: '▄', name: '▁' };
53
+ /** Columns one nesting level spends, so a doc sits under its directory's label
54
+ * and the truncator sees the indent as part of the name it is eliding. */
55
+ const INDENT = 2;
53
56
  export class ContextDocsPanel {
54
57
  onChange;
55
58
  rows = [];
@@ -57,8 +60,9 @@ export class ContextDocsPanel {
57
60
  event = 'boot';
58
61
  lines = [];
59
62
  index = 0;
60
- /** Groups the user opened. Empty means every group is collapsed, which is how
61
- * the page opens: ~25 rows that name the whole corpus. */
63
+ /** Directories the user opened, by full name prefix. Empty means every level
64
+ * is collapsed, which is how the page opens: ~25 rows that name the whole
65
+ * corpus. */
62
66
  expanded = new Set();
63
67
  constructor(onChange) {
64
68
  this.onChange = onChange;
@@ -71,10 +75,11 @@ export class ContextDocsPanel {
71
75
  return undefined;
72
76
  return line.kind === 'doc' ? line.row : line.first;
73
77
  }
74
- /** Documents the current question selects — counted from the group rollups,
75
- * so a fully collapsed list still reports its size. */
78
+ /** Documents the current question selects — counted from the top-level
79
+ * rollups, which partition the corpus, so a fully collapsed list still
80
+ * reports its size and a nested rollup is never counted twice. */
76
81
  get visibleCount() {
77
- return this.lines.reduce((count, line) => count + (line.kind === 'group' ? line.docs : 0), 0);
82
+ return this.lines.reduce((count, line) => count + (line.kind === 'group' && line.depth === 0 ? line.docs : 0), 0);
78
83
  }
79
84
  setRows(rows, event, selectedKey) {
80
85
  this.rows = rows;
@@ -95,11 +100,17 @@ export class ContextDocsPanel {
95
100
  this.index = Math.max(0, Math.min(this.lines.length - 1, this.index + delta));
96
101
  this.onChange();
97
102
  }
98
- /** True when the focused line is a group, so the shell knows whether a
99
- * descend opens a group or the dossier. */
103
+ /** True when the focused line can be opened — a directory header. */
100
104
  get onGroup() {
101
105
  return this.lines[this.index]?.kind === 'group';
102
106
  }
107
+ /** True when the focused line IS a document rather than merely standing for
108
+ * one. A directory that is also a document is both, which is why the two
109
+ * gestures split there: space opens it, enter edits it. */
110
+ get onDocument() {
111
+ const line = this.lines[this.index];
112
+ return line !== undefined && (line.kind === 'doc' || line.isDoc);
113
+ }
103
114
  toggleGroup() {
104
115
  const line = this.lines[this.index];
105
116
  if (line?.kind !== 'group')
@@ -112,7 +123,9 @@ export class ContextDocsPanel {
112
123
  this.onChange();
113
124
  }
114
125
  /** Rebuild the drawn lines, keeping the cursor on the same document (or the
115
- * same group), and clamping when what it was on is gone. */
126
+ * same directory), and clamping when what it was on is gone. A document that
127
+ * owns a directory is drawn as that directory's header, so a held document
128
+ * key matches there too. */
116
129
  relayout(selectedKey, groupKey) {
117
130
  const held = selectedKey ?? (this.lines[this.index]?.kind === 'doc' ? this.lines[this.index].key : undefined);
118
131
  this.lines = buildListView(this.rows, this.filters, this.expanded, this.event);
@@ -120,7 +133,7 @@ export class ContextDocsPanel {
120
133
  ? this.lines.findIndex((line) => line.kind === 'group' && line.key === groupKey)
121
134
  : held === undefined
122
135
  ? -1
123
- : this.lines.findIndex((line) => line.kind === 'doc' && line.key === held);
136
+ : this.lines.findIndex((line) => (line.kind === 'doc' && line.key === held) || (line.kind === 'group' && line.isDoc && line.first.key === held));
124
137
  this.index = wanted >= 0 ? wanted : Math.max(0, Math.min(this.index, this.lines.length - 1));
125
138
  }
126
139
  render(width, height, focused, plans) {
@@ -140,7 +153,7 @@ export class ContextDocsPanel {
140
153
  const range = visibleRange(this.lines.length, this.index, capacity);
141
154
  for (let at = range.start; at < range.end; at += 1) {
142
155
  const line = this.lines[at];
143
- const body = line.kind === 'group' ? this.groupLine(line, nameRoom) : this.docLine(line, nameRoom, plans);
156
+ const body = line.kind === 'group' ? this.groupLine(line, nameRoom, plans) : this.docLine(line, nameRoom, plans);
144
157
  out.push(at === this.index && focused
145
158
  ? theme.bg('selectedBg', padAnsi(body, width))
146
159
  : at === this.index
@@ -149,9 +162,12 @@ export class ContextDocsPanel {
149
162
  }
150
163
  return this.fit(out, height);
151
164
  }
152
- groupLine(line, nameRoom) {
165
+ /** A directory header. Its stats and cost are the whole subtree's; the matrix
166
+ * is drawn only when the directory is itself a document, which is what marks
167
+ * the one row as both a document and a directory. */
168
+ groupLine(line, nameRoom, plans) {
153
169
  const marker = line.expanded ? '▾' : '▸';
154
- const label = `${marker} ${theme.bold(line.label)}`;
170
+ const label = `${' '.repeat(line.depth * INDENT)}${marker} ${theme.bold(line.label)}`;
155
171
  const marks = ['content', 'preview', 'name']
156
172
  .filter((rung) => line.rungs[rung] > 0)
157
173
  .map((rung) => `${line.rungs[rung]}${RUNG_MARK[rung]}`)
@@ -159,15 +175,12 @@ export class ContextDocsPanel {
159
175
  const stats = theme.fg('dim', marks === '' ? plural(line.docs, 'doc', 'docs') : `${line.docs} · ${marks}`);
160
176
  const room = Math.max(1, nameRoom - visibleWidth(stats) - 1);
161
177
  const field = `${padAnsi(truncateToWidth(label, room), room)} ${stats}`;
162
- return ` ${padAnsi(field, nameRoom)} ${cost(line.cost)} ${' '.repeat(MATRIX_WIDTH)}`;
178
+ const matrix = line.isDoc ? matrixOf(line.first, plans) : ' '.repeat(MATRIX_WIDTH);
179
+ return ` ${padAnsi(field, nameRoom)} ${cost(line.cost)} ${matrix}`;
163
180
  }
164
181
  docLine(line, nameRoom, plans) {
165
- const matrix = SURFACE_EVENTS.map((event) => {
166
- const state = plans.peek(event) === undefined ? 'pending' : cellStateOf(line.row.records.get(event));
167
- return cellGlyph(line.row.records.get(event), state);
168
- }).join(' ');
169
- const label = truncateToWidth(` ${line.label}`, nameRoom);
170
- return ` ${padAnsi(label, nameRoom)} ${cost(line.cost)} ${matrix}`;
182
+ const label = truncateToWidth(`${' '.repeat(line.depth * INDENT)}${line.label}`, nameRoom);
183
+ return ` ${padAnsi(label, nameRoom)} ${cost(line.cost)} ${matrixOf(line.row, plans)}`;
171
184
  }
172
185
  /** Pad rather than trim: this column sets the length of the divider drawn
173
186
  * beside it. */
@@ -177,6 +190,13 @@ export class ContextDocsPanel {
177
190
  return lines.slice(0, height).map((line) => (visibleWidth(line) === 0 ? '' : line));
178
191
  }
179
192
  }
193
+ /** One document's six events, in the order the heading names them. */
194
+ function matrixOf(row, plans) {
195
+ return SURFACE_EVENTS.map((event) => {
196
+ const state = plans.peek(event) === undefined ? 'pending' : cellStateOf(row.records.get(event));
197
+ return cellGlyph(row.records.get(event), state);
198
+ }).join(' ');
199
+ }
180
200
  /** Right-flushed into the cost column, dim because it is an estimate. */
181
201
  function cost(count) {
182
202
  const text = formatCost(count);
@@ -17,34 +17,45 @@ export interface Filters {
17
17
  query: string;
18
18
  }
19
19
  export declare const NO_FILTERS: Filters;
20
- /** One drawn line. A group carries what its members add up to, so a collapsed
21
- * list still states where the corpus and its cost sit. */
20
+ /** One drawn line. A group carries what its whole subtree adds up to, so a
21
+ * collapsed list still states where the corpus and its cost sit. `depth` is
22
+ * how many name segments sit above the line, which the renderer spends on
23
+ * indent. */
22
24
  export type ListLine = {
23
25
  kind: 'group';
24
26
  key: string;
25
27
  label: string;
28
+ depth: number;
26
29
  docs: number;
27
30
  rungs: Record<Exclude<Rung, 'none'>, number>;
28
31
  cost: number;
29
32
  expanded: boolean;
30
- /** The first member, so the dossier has a document to describe while the
31
- * cursor rests on the header. */
33
+ /** The document the dossier describes while the cursor rests on the
34
+ * header: this directory's own document when it has one, else the first
35
+ * document in its subtree. */
32
36
  first: DocRow;
37
+ /** True when the directory is itself a document — `first` is that
38
+ * document, and the one row is both selectable and expandable. */
39
+ isDoc: boolean;
33
40
  } | {
34
41
  kind: 'doc';
35
42
  key: string;
36
43
  row: DocRow;
37
44
  label: string;
45
+ depth: number;
38
46
  cost: number;
39
47
  };
40
- export declare function groupKeyOf(name: string): string;
41
- /** Every group key the rows can produce, so collapse state can drop the keys a
42
- * re-plan removed instead of accumulating them. */
48
+ /** Every group key the rows can produce — one per proper name prefix, since a
49
+ * prefix exists only because something sits under it — so collapse state can
50
+ * drop the keys a re-plan removed instead of accumulating them. The leading
51
+ * section is a key only when a name with no `/` owns no directory, which is
52
+ * exactly when it is not itself one of those prefixes. */
43
53
  export declare function groupKeysOf(rows: readonly DocRow[]): Set<string>;
44
- /** The lines to draw, in corpus order: the leading section, then each group in
45
- * order of first appearance. A live query force-expands every group holding a
46
- * match without touching `expanded`, so clearing it restores what the user
47
- * collapsed. */
54
+ /** The lines to draw, in corpus order: the leading section, then each directory
55
+ * in order of first appearance, each recursing into its own members only when
56
+ * it is open. A live query force-expands every directory, which is exactly the
57
+ * ancestor chain of every surviving match, without touching `expanded` — so
58
+ * clearing the query restores what the user collapsed. */
48
59
  export declare function buildListView(rows: readonly DocRow[], filters: Filters, expanded: ReadonlySet<string>, event: SurfaceEvent): ListLine[];
49
60
  /** What the whole filtered set costs, split by the rung it arrives at. */
50
61
  export declare function costTotals(rows: readonly DocRow[], event: SurfaceEvent): {