@north-light/cli 0.3.504 → 0.3.506

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 (57) hide show
  1. package/dist/api/client.d.ts +5 -1
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/canvas.d.ts +40 -0
  6. package/dist/api/routes.d.ts +2 -0
  7. package/dist/api/routes.js +1 -1
  8. package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
  9. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +3 -3
  10. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +3 -2
  11. package/dist/clients/attach/viewer.js +575 -575
  12. package/dist/clients/dashboard/app.d.ts +7 -0
  13. package/dist/clients/dashboard/app.js +2 -0
  14. package/dist/clients/dashboard/model.d.ts +39 -0
  15. package/dist/clients/dashboard/model.js +1 -0
  16. package/dist/clients/dashboard/render.d.ts +29 -0
  17. package/dist/clients/dashboard/render.js +3 -0
  18. package/dist/commands/canvas-browse.js +1 -1
  19. package/dist/commands/surface/node/focus.js +1 -1
  20. package/dist/commands/surface-dashboard.d.ts +2 -0
  21. package/dist/commands/surface-dashboard.js +3 -0
  22. package/dist/commands/surface.js +1 -1
  23. package/dist/core/canvas/browse/app.d.ts +3 -0
  24. package/dist/core/canvas/browse/app.js +3 -3
  25. package/dist/core/canvas/browse/model.d.ts +5 -1
  26. package/dist/core/canvas/browse/model.js +1 -1
  27. package/dist/core/canvas/browse/render.d.ts +5 -0
  28. package/dist/core/canvas/browse/render.js +3 -3
  29. package/dist/core/canvas/migrations.js +1 -1
  30. package/dist/core/canvas/recovery.d.ts +5 -0
  31. package/dist/core/canvas/recovery.js +2 -2
  32. package/dist/core/human/requests.d.ts +3 -0
  33. package/dist/core/human/requests.js +17 -17
  34. package/dist/core/keybindings/catalog.d.ts +4 -3
  35. package/dist/core/keybindings/catalog.js +1 -1
  36. package/dist/core/llm/run.js +2 -2
  37. package/dist/core/runtime/broker/model-adapter.d.ts +1 -2
  38. package/dist/core/runtime/focus-viewer.d.ts +6 -0
  39. package/dist/core/runtime/focus-viewer.js +1 -1
  40. package/dist/core/runtime/placement-tmux.d.ts +1 -1
  41. package/dist/core/runtime/placement-tmux.js +1 -1
  42. package/dist/core/runtime/tmux-bindings.js +3 -3
  43. package/dist/core/runtime/tmux-driver.d.ts +6 -0
  44. package/dist/core/runtime/tmux-driver.js +5 -5
  45. package/dist/core/subscription-state.d.ts +2 -0
  46. package/dist/core/subscription-state.js +4 -4
  47. package/dist/daemon/api/bridge.js +3 -3
  48. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  49. package/dist/daemon/api/handlers/canvas.js +6 -6
  50. package/dist/daemon/api/handlers/nodes.js +1 -1
  51. package/dist/daemon/api/operations.js +1 -1
  52. package/dist/daemon/api/viewer-presence.d.ts +3 -0
  53. package/dist/daemon/api/viewer-presence.js +1 -1
  54. package/docs/sdk/release-notes.md +8 -0
  55. package/package.json +2 -2
  56. package/packages/crouter-identity/package.json +1 -1
  57. package/runtime.lock.json +12 -12
@@ -0,0 +1,7 @@
1
+ export interface DashboardOptions {
2
+ /** Profile to show; undefined means every profile (no profile toggle). */
3
+ profileId: string | undefined;
4
+ /** The dashboard's own directory — the default for a new conversation. */
5
+ cwd: string;
6
+ }
7
+ export declare function runDashboard(opts: DashboardOptions): Promise<void>;
@@ -0,0 +1,2 @@
1
+ var Q=Object.defineProperty;var d=(s,f)=>Q(s,"name",{value:f,configurable:!0});import{mkdtempSync as V,readFileSync as q,rmSync as E,existsSync as Y}from"node:fs";import{tmpdir as Z}from"node:os";import{join as F}from"node:path";import{cliClient as ee}from"../../commands/api-client.js";import{loadPins as re,savePins as ne}from"../../core/canvas/browse/pins.js";import{detectColorCaps as oe}from"../../core/tui/draw.js";import{getTerminalSize as ie,parseKeypress as te,restoreTerminal as se,setupTerminal as de}from"../../core/tui/terminal.js";import{getPaneOption as le,runShellInBackground as ce,setPaneOption as C,shellQuote as h}from"../../core/runtime/placement-tmux.js";import{matchesTerminalInput as ae,resolveUserKeybindings as fe}from"../../core/keybindings/index.js";import{VIEWER_OWNER_OPTION as S}from"../attach/session/pane-tag.js";import{buildLines as ue,nearestSelectable as M,placements as pe,rootOf as b,stepSelectable as D}from"./model.js";import{listHeight as me,renderDashboard as he}from"./render.js";const ge=2e3,we=250,be=6e3;function P(s){return s.kind==="heading"?null:s.kind==="root"?`root:${s.id}`:`child:${s.rootId}:${s.id}`}d(P,"lineKey");async function Oe(s){const f=process.env.TMUX_PANE;if(f===void 0||f==="")throw new Error("the dashboard runs in a tmux pane (TMUX_PANE is unset)");const m=ee(),R=fe(),j=oe(),c=d((r,n)=>ae(R,r,n),"matches"),k=String(process.pid);C(f,S,k);for(const r of["@crtr_node","@crtr_attach","@crtr_viewer_at"])C(f,r,"");const K=V(F(Z(),"crtr-dashboard-"));let I=!1;const L=d(()=>{if(!I){I=!0;try{se()}catch{}le(f,S)===k&&C(f,S,"");try{E(K,{recursive:!0,force:!0})}catch{}}},"cleanup");process.once("exit",L);for(const r of["SIGTERM","SIGHUP"])process.once(r,()=>{L(),process.exit(0)});const e={rows:[],placed:new Map,pinned:re(),expanded:new Set,lines:[],cursorKey:null,cursor:0,scroll:0,scope:s.profileId===void 0?"all":"profile",profileLabel:s.profileId??"",loaded:!1,help:!1,inputFocused:!1,inputText:"",dirs:[s.cwd],dirIndex:0,dirsLoaded:!1,rename:null,renameTarget:null,confirmClose:null,notice:null,opening:null},x=new Set;let $;const u=d(r=>{e.notice=r,$!==void 0&&clearTimeout($),$=setTimeout(()=>{e.notice=null,l()},be)},"notify"),g=d(()=>{const r=e.rows.filter(t=>!x.has(t.node_id));e.lines=ue(r,e.placed,e.pinned,e.expanded);const n=e.cursorKey===null?-1:e.lines.findIndex(t=>P(t)===e.cursorKey);e.cursor=n>=0?n:M(e.lines,e.cursor),e.cursor<0&&(e.cursor=0);const o=e.lines[e.cursor];e.cursorKey=o===void 0?null:P(o)},"rebuild"),l=d(()=>{if(I)return;const r=ie(),n=me(r.rows)-1;e.cursor<e.scroll&&(e.scroll=e.cursor),e.cursor>=e.scroll+n&&(e.scroll=e.cursor-n+1),e.scroll<0&&(e.scroll=0),process.stdout.write(he({lines:e.lines,cursor:e.cursor,scroll:e.scroll,pinned:e.pinned,scope:e.scope,profileLabel:e.profileLabel,rename:e.rename,inputText:e.inputText,inputFocused:e.inputFocused,chipDir:e.dirs[e.dirIndex]??s.cwd,confirmName:e.confirmClose===null?null:e.rows.find(o=>o.node_id===e.confirmClose)?.name??e.confirmClose,notice:e.notice,opening:e.opening!==null,help:e.help,loaded:e.loaded},r,j))},"paint");let v=!1,y=!1;const p=d(async()=>{if(v){y=!0;return}v=!0;try{const r=await m.liveRoots(e.scope==="profile"&&s.profileId!==void 0?{profile:s.profileId}:{});e.rows=r.rows,e.placed=pe(r.rows,e.placed),e.loaded=!0,g()}catch(r){u(`refresh failed: ${r.message}`)}finally{v=!1}l(),y&&(y=!1,p())},"poll");s.profileId!==void 0&&m.getProfile(s.profileId).then(r=>{e.profileLabel=r.name,l()}).catch(()=>{});const U=d(()=>e.lines[e.cursor],"current"),z=d(r=>{if(e.opening!==null||r.kind==="heading")return;const o=(r.kind==="root"?r.row:e.rows.find(_=>_.node_id===r.rootId)?.children.find(_=>_.node_id===r.id))?.name??r.id,t=r.kind==="root"&&r.row.asks>0?r.row.first_ask_ticket_id:null,i=F(K,`open-${Date.now()}`),T=`${["crtr","surface","node","focus",r.id,"--pane",f,"--in-place","--keep-other-viewers",...t===null?[]:["--open-ticket",t],"--json"].map(h).join(" ")} >${h(`${i}.out`)} 2>${h(`${i}.err`)}; echo $? >${h(`${i}.code`)}; mv ${h(`${i}.code`)} ${h(i)}`;if(!ce(T)){u(`could not open ${o}`),l();return}e.opening={file:i,name:o},l()},"open"),A=d(()=>{const r=e.opening;if(r===null||!Y(r.file))return;const n=d(a=>{try{return q(a,"utf8").trim()}catch{return""}},"read"),o=n(r.file),t=n(`${r.file}.out`),i=n(`${r.file}.err`);for(const a of[r.file,`${r.file}.out`,`${r.file}.err`])E(a,{force:!0});if(e.opening=null,o!=="0")u(i===""?`could not open ${r.name}`:i.split(`
2
+ `)[0]);else{let a=!1;try{a=JSON.parse(t).focused===!0}catch{a=!1}a||u(`could not open ${r.name}`)}l(),p()},"finishOpen"),G=d(async()=>{const r=e.inputText.trim();if(r!==""){try{await m.createNode({root:!0,prompt:r,cwd:e.dirs[e.dirIndex]??s.cwd,...s.profileId===void 0?{}:{profile:s.profileId}}),e.inputText="",e.inputFocused=!1,p()}catch(n){u(`could not start: ${n.message}`)}l()}},"startConversation"),H=d(()=>{e.dirsLoaded||s.profileId===void 0||(e.dirsLoaded=!0,m.getProfile(s.profileId).then(r=>{e.dirs=[s.cwd,...r.projects.map(n=>n.path).filter(n=>n!==s.cwd)],l()}).catch(r=>{u(`could not list directories: ${r.message}`),l()}))},"loadDirs"),O=d(async r=>{e.confirmClose=null,x.add(r),g(),l();try{await m.closeNode(r,{finish:!0})}catch(n){u(`could not close: ${n.message}`)}finally{x.delete(r)}p(),l()},"doClose"),W=d(async()=>{const r=e.renameTarget,n=(e.rename??"").trim();if(e.rename=null,e.renameTarget=null,r!==null&&n!==""){try{await m.patchConfig(r,{name:n})}catch(o){u(`could not rename: ${o.message}`)}p()}l()},"commitRename"),X=d(r=>{e.pinned.has(r)?e.pinned.delete(r):e.pinned.add(r),ne(e.pinned),g()},"togglePin"),w=d(r=>{const n=M(e.lines,r);n<0||(e.cursor=n,e.cursorKey=P(e.lines[n]))},"moveTo"),B=d(r=>[...r].filter(n=>n>=" "&&n!=="\x7F").join(""),"printable"),N=d((r,n,o,t)=>{if(n.backspace)return t(o().slice(0,-1)),!0;if(n.ctrl||n.meta)return!0;const i=B(r);return i!==""&&t(o()+i),!0},"onText"),J=d((r,n)=>{const o={input:r,...n};if(e.help){e.help=!1,l();return}if(e.rename!==null){if(n.escape)e.rename=null,e.renameTarget=null;else if(n.return){W();return}else N(r,n,()=>e.rename??"",i=>{e.rename=i});l();return}if(e.inputFocused){if(c("crtr.dashboard.input-cancel",o))e.inputFocused=!1;else if(c("crtr.dashboard.input-submit",o)){G();return}else c("crtr.dashboard.directory-next",o)?e.dirIndex=(e.dirIndex+1)%e.dirs.length:c("crtr.dashboard.directory-previous",o)?e.dirIndex=(e.dirIndex-1+e.dirs.length)%e.dirs.length:N(r,n,()=>e.inputText,i=>{e.inputText=i});l();return}if(e.confirmClose!==null){const i=e.confirmClose;if(c("crtr.dashboard.confirm-close",o)){O(i);return}c("crtr.dashboard.cancel-close",o)&&(e.confirmClose=null,l());return}const t=U();if(c("crtr.dashboard.down",o))w(D(e.lines,e.cursor,1));else if(c("crtr.dashboard.up",o))w(D(e.lines,e.cursor,-1));else if(c("crtr.dashboard.first",o))w(0);else if(c("crtr.dashboard.last",o))w(e.lines.length-1);else if(c("crtr.dashboard.expand",o))t?.kind==="root"&&t.row.children.length>0&&(e.expanded.add(t.id),g());else if(c("crtr.dashboard.collapse",o)){if(t!==void 0&&t.kind!=="heading"){const i=b(t);e.expanded.delete(i),e.cursorKey=`root:${i}`,g()}}else if(c("crtr.dashboard.open",o)){t!==void 0&&z(t);return}else if(c("crtr.dashboard.pin",o)){const i=t===void 0?null:b(t);i!==null&&X(i)}else if(c("crtr.dashboard.rename",o)){const i=t===void 0?null:b(t);i!==null&&(e.renameTarget=i,e.rename=e.rows.find(a=>a.node_id===i)?.name??"")}else if(c("crtr.dashboard.close",o)){const i=t===void 0?null:b(t),a=i===null?void 0:e.rows.find(T=>T.node_id===i);if(a!==void 0)if(a.running_count>0)e.confirmClose=a.node_id;else{O(a.node_id);return}}else c("crtr.dashboard.profile",o)?s.profileId!==void 0&&(e.scope=e.scope==="profile"?"all":"profile",p()):c("crtr.dashboard.input",o)?(e.inputFocused=!0,H()):c("crtr.dashboard.help",o)&&(e.help=!0);l()},"onKey");de(),l(),p(),setInterval(()=>{p()},ge),setInterval(A,we),process.stdout.on("resize",l),await new Promise(()=>{process.stdin.on("data",r=>{try{const{input:n,key:o}=te(r);J(n,o)}catch(n){u(`internal error: ${n.message}`),l()}})})}d(Oe,"runDashboard");export{Oe as runDashboard};
@@ -0,0 +1,39 @@
1
+ import type { LiveRootChildDTO, LiveRootDTO } from '../../api/dto/canvas.js';
2
+ export type Section = 'waiting' | 'running' | 'idle';
3
+ export declare const SECTION_ORDER: readonly Section[];
4
+ export declare const SECTION_TITLE: Record<Section, string>;
5
+ /** Waiting beats Running beats Idle: a running root with an ask still needs the person. */
6
+ export declare function sectionOf(row: LiveRootDTO): Section;
7
+ /** A root's place in its section: the section it is in and the sort key it took on entering it. */
8
+ export interface Placement {
9
+ section: Section;
10
+ key: string;
11
+ }
12
+ export type Line = {
13
+ kind: 'heading';
14
+ section: Section;
15
+ count: number;
16
+ } | {
17
+ kind: 'root';
18
+ id: string;
19
+ row: LiveRootDTO;
20
+ section: Section;
21
+ expanded: boolean;
22
+ } | {
23
+ kind: 'child';
24
+ id: string;
25
+ rootId: string;
26
+ child: LiveRootChildDTO;
27
+ };
28
+ /** Carry each root's sort key across refreshes: a row keeps its key while it stays in its
29
+ * section, and takes its `last_activity` when it first appears or changes section. */
30
+ export declare function placements(rows: readonly LiveRootDTO[], previous: ReadonlyMap<string, Placement>): Map<string, Placement>;
31
+ /** The flat line list: a heading per non-empty section, pinned rows first inside their section,
32
+ * then newest key first; an expanded root is followed by its children. */
33
+ export declare function buildLines(rows: readonly LiveRootDTO[], placed: ReadonlyMap<string, Placement>, pinned: ReadonlySet<string>, expanded: ReadonlySet<string>): Line[];
34
+ /** Index of the nearest selectable line at or after `from` (then before it); -1 when none. */
35
+ export declare function nearestSelectable(lines: readonly Line[], from: number): number;
36
+ /** Step the cursor over headings: +1 / -1 to the next selectable line, or stay put at an end. */
37
+ export declare function stepSelectable(lines: readonly Line[], from: number, dir: 1 | -1): number;
38
+ /** The root a line acts on for pin, rename and close: a child line acts on its root row. */
39
+ export declare function rootOf(line: Line): string | null;
@@ -0,0 +1 @@
1
+ var l=Object.defineProperty;var c=(n,e)=>l(n,"name",{value:e,configurable:!0});const _=["waiting","running","idle"],x={waiting:"Waiting for you",running:"Running",idle:"Idle"};function p(n){return n.asks>0||n.unread||n.fault!==null?"waiting":n.running_count>0?"running":"idle"}c(p,"sectionOf");function I(n,e){const o=new Map;for(const t of n){const r=p(t),d=e.get(t.node_id);o.set(t.node_id,d!==void 0&&d.section===r?d:{section:r,key:t.last_activity})}return o}c(I,"placements");function O(n,e,o,t){const r=[];for(const d of _){const f=n.filter(i=>e.get(i.node_id)?.section===d);if(f.length!==0){f.sort((i,u)=>{const s=o.has(i.node_id)?0:1,a=o.has(u.node_id)?0:1;if(s!==a)return s-a;const g=e.get(i.node_id).key,h=e.get(u.node_id).key;return g<h?1:g>h||i.node_id<u.node_id?-1:1}),r.push({kind:"heading",section:d,count:f.length});for(const i of f){const u=t.has(i.node_id);if(r.push({kind:"root",id:i.node_id,row:i,section:d,expanded:u}),u)for(const s of i.children)r.push({kind:"child",id:s.node_id,rootId:i.node_id,child:s})}}}return r}c(O,"buildLines");function y(n,e){const o=Math.min(Math.max(0,e),Math.max(0,n.length-1));for(let t=o;t<n.length;t++)if(n[t].kind!=="heading")return t;for(let t=o-1;t>=0;t--)if(n[t].kind!=="heading")return t;return-1}c(y,"nearestSelectable");function S(n,e,o){for(let t=e+o;t>=0&&t<n.length;t+=o)if(n[t].kind!=="heading")return t;return e}c(S,"stepSelectable");function E(n){return n.kind==="root"?n.id:n.kind==="child"?n.rootId:null}c(E,"rootOf");export{_ as SECTION_ORDER,x as SECTION_TITLE,O as buildLines,y as nearestSelectable,I as placements,E as rootOf,p as sectionOf,S as stepSelectable};
@@ -0,0 +1,29 @@
1
+ import { type ColorCaps } from '../../core/tui/draw.js';
2
+ import type { Line } from './model.js';
3
+ export interface DashboardFrameState {
4
+ lines: readonly Line[];
5
+ cursor: number;
6
+ scroll: number;
7
+ pinned: ReadonlySet<string>;
8
+ /** `all` when the profile filter is off. */
9
+ scope: 'profile' | 'all';
10
+ profileLabel: string;
11
+ /** Rename in progress: the typed name. */
12
+ rename: string | null;
13
+ inputText: string;
14
+ inputFocused: boolean;
15
+ /** The directory the new conversation will start in. */
16
+ chipDir: string;
17
+ /** Row being closed while running, awaiting y/n. */
18
+ confirmName: string | null;
19
+ notice: string | null;
20
+ opening: boolean;
21
+ help: boolean;
22
+ loaded: boolean;
23
+ }
24
+ /** Height of the list area: rows minus title, input line and footer. */
25
+ export declare function listHeight(rows: number): number;
26
+ export declare function renderDashboard(state: DashboardFrameState, size: {
27
+ cols: number;
28
+ rows: number;
29
+ }, caps: ColorCaps): string;
@@ -0,0 +1,3 @@
1
+ var R=Object.defineProperty;var g=(t,i)=>R(t,"name",{value:i,configurable:!0});import{assemble as S,clip as c}from"../../core/tui/draw.js";import{resolveNodeVisual as E}from"../../core/canvas/status-glyph.js";import{BOLD as x,DIM as f,ESC as u,RESET as o,REVERSE as _}from"../../core/canvas/nav-render.js";import{SECTION_TITLE as D}from"./model.js";const C=`${u}48;5;236m`,H=[["j k \u2191 \u2193","move"],["g G","first / last"],["l \u2192 / h \u2190","show / hide children"],["Enter","open (the ask first, when one is waiting)"],["p","pin or unpin"],["R","rename"],["x","close (asks first when something is running)"],["P","this profile / all profiles"],["n","new conversation: type, Tab picks a directory, Enter starts it"],["Esc","leave the input"],["?","this help"]];function W(t){return Math.max(1,Math.max(8,t)-3)}g(W,"listHeight");function L(t){const i=Math.max(20,t.cols)-1,n=[`${x}Dashboard \u2014 keys${o}`,""];for(const[h,e]of H)n.push(c(` ${x}${h.padEnd(12)}${o} ${f}${e}${o}`,i+20));for(n.push("",`${f} any key closes${o}`);n.length<Math.max(8,t.rows)-1;)n.push("");return`${u}H${n.map(h=>`${h}${u}K`).join(`\r
2
+ `)}${u}J`}g(L,"renderHelp");function N(t,i,n,h){const e=t.row,s=E(e.status,{streaming:e.running_count>0}),l=e.state_line??e.goal??"",m=[];e.running_count>0&&m.push({text:`\u27F3 ${e.running_count}`,style:{fg:"92"}},{text:" "}),e.asks>0&&m.push({text:`? ${e.asks}`,style:{fg:"93",bold:!0}},{text:" "}),e.fault!==null&&m.push({text:"\u26A0",style:{fg:"33",bold:!0}},{text:" "});const k=m.reduce((w,p)=>w+p.text.length,0),d=`${t.expanded?"\u25BE":"\u25B8"} `,a=Math.min(e.name.length,Math.max(10,Math.floor(n/3))),y=c(e.name,a),r=Math.max(0,n-k-d.length-2-y.length-2-(i?2:0)-1);return{left:[{text:" "},{text:s.glyph,style:{fg:s.color}},{text:" "},{text:d,style:{dim:!0}},...i?[{text:"\u2605 ",style:{fg:"33"}}]:[],{text:y,style:{bold:!0}},{text:" "},{text:c(l.replace(/\s+/g," "),r),style:{dim:!0}}],right:m}}g(N,"rootSpans");function O(t,i){const n=t.child,h=E(n.status,{streaming:n.streaming}),e=[];return n.streaming&&e.push({text:"\u27F3",style:{fg:"92"}},{text:" "}),n.asks>0&&e.push({text:`? ${n.asks}`,style:{fg:"93",bold:!0}},{text:" "}),n.fault!==null&&e.push({text:"\u26A0",style:{fg:"33",bold:!0}},{text:" "}),{left:[{text:" "},{text:h.glyph,style:{fg:h.color}},{text:" "},{text:c(n.name,Math.max(10,i-12))}],right:e}}g(O,"childSpans");function b(t){return t.reduce((i,n)=>i+n.text.length,0)}g(b,"spansWidth");function G(t,i,n){if(t.help)return L(i);const h=Math.max(20,i.cols),e=Math.max(8,i.rows),s=h-1,l=[],m=t.scope==="all"?"all profiles":t.profileLabel,k=t.lines.filter(r=>r.kind==="root").length;l.push(`${x}Dashboard${o} ${f}\u2014 ${m} \xB7 ${k} live${o}`);const d=W(e)-1,a=[];t.lines.length===0&&a.push(`${f} ${t.loaded?"no live conversations \u2014 press n to start one":"loading\u2026"}${o}`);for(let r=t.scroll;r<Math.min(t.lines.length,t.scroll+d);r++){const $=t.lines[r];if($.kind==="heading"){a.push(`${x}${c(`${D[$.section]} (${$.count})`,s)}${o}`);continue}const w=r===t.cursor,p=$.kind==="root"?N($,t.pinned.has($.id),s,n):O($,s),M=w?n.color256?C:_:"",v=Math.max(1,s-b(p.left)-b(p.right)),T=b(p.left)+b(p.right)+1>s?p.left:[...p.left,{text:" ".repeat(v)},...p.right];a.push(S(T,s,n.color,M,w))}for(;a.length<d;)a.push("");l.push(...a);const y=`[${t.chipDir}]`;if(t.rename!==null)l.push(`${u}36mrename \u25B8${o} ${t.rename}\u258E`);else if(t.inputFocused)l.push(c(`${u}36mnew \u25B8${o} ${t.inputText}\u258E ${f}${y}${o}`,s+24));else{const r=t.inputText===""?"n start a conversation":t.inputText;l.push(c(`${f}\u25B8 ${r} ${y}${o}`,s+12))}return t.confirmName!==null?l.push(`${x}${u}93m${c(`\u26A0 ${t.confirmName} is running \u2014 close it (and its subtree) anyway? y / n`,s)}${o}`):t.notice!==null?l.push(`${x}${c(t.opening?`opening\u2026 ${t.notice}`:t.notice,s)}${o}`):t.opening?l.push(`${x}opening\u2026${o}`):t.inputFocused||t.rename!==null?l.push(`${f}${c("\u23CE confirm Esc leave Tab directory",s)}${o}`):l.push(`${f}${c("\u2191\u2193 move \u23CE open \u2192/\u2190 children n new p pin R rename x close P profiles ? help",s)}${o}`),`${u}H${l.map(r=>`${r}${u}K`).join(`\r
3
+ `)}${u}J`}g(G,"renderDashboard");export{W as listHeight,G as renderDashboard};
@@ -1 +1 @@
1
- var d=Object.defineProperty;var t=(e,n)=>d(e,"name",{value:n,configurable:!0});import{envProfileId as c}from"../shared/env.js";import{defineLeaf as l}from"../core/command.js";import{runBrowse as p}from"../core/canvas/browse/app.js";import{ApiCanvasSource as u,cliClient as o}from"./api-client.js";import{getDefaultProfileId as f}from"../core/profiles/default-binding.js";const b=l({name:"browse",description:"open the interactive canvas navigator (tabs/tree/search)",whenToUse:"you want to VIEW and NAVIGATE the whole canvas interactively \u2014 a full-screen TUI with tabs (All/Live/Dormant/Attention/Pinned), an expandable tree (children auto-collapsed; \u2192 to expand), `/` fuzzy search, `f` to pin, `y` to copy a node id, `m` to message and `R` to rename a node in place, and `?` for the full keymap; it opens on Live (active/idle) and Enter resumes the chosen node. Use this to find your way around a large canvas. Use `canvas dashboard` instead for a one-shot ASCII tree you can pipe, and `node inspect list` for a flat machine-readable roster",help:{name:"canvas browse",summary:"interactive full-screen canvas navigator \u2014 tabs, an auto-collapsed tree, and `/` fuzzy search; Enter resumes the chosen node via `crtr surface node focus`. Outside a TTY it prints the static forest and exits",params:[{kind:"flag",name:"return-pane",type:"string",required:!1,constraint:"tmux pane id the chosen node REPLACES on Enter \u2014 that pane is respawned onto its viewer (set by the /resume popup, so resuming swaps the pane you invoked it from). Default: focus into this pane without replacing it."},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"directory used when you toggle the optional cwd filter (c). The navigator opens with All dirs; /resume passes the launching node's cwd. Default: the invocation cwd."},{kind:"flag",name:"profile-id",type:"string",required:!1,constraint:"Exact profile id for the initial profile filter (on by default; P toggles All profiles). Default: the launching node's profile via CRTR_PROFILE_ID, then this cwd's pinned default profile; absent means All profiles."}],output:[],outputKind:"object",effects:["Takes over the terminal in raw mode (alt-screen) until you quit (q/Esc) or pick a node.","On Enter: resumes the selected node via `crtr surface node focus` (reviveNode \u2014 the only sanctioned open).","Read-only on the canvas db; mutates nothing but the chosen node's placement on resume."]},run:t(async e=>{const n=e.cwd??process.cwd(),s=e.profileId??c()??f(n)??void 0,r=new u;await p({returnPane:e.returnPane,cwd:n,profileId:s,source:r,viewedNodeIds:t(async()=>new Set((await o().listFocuses()).map(a=>a.node_id)),"viewedNodeIds"),closeNode:t(a=>o().closeNode(a,process.ppid>0?{finish:!0,caller_pid:process.ppid}:{finish:!0}).then(i=>({closed:i.closed})),"closeNode")})},"run")});export{b as browseLeaf};
1
+ var d=Object.defineProperty;var t=(e,a)=>d(e,"name",{value:a,configurable:!0});import{envProfileId as c}from"../shared/env.js";import{defineLeaf as l}from"../core/command.js";import{runBrowse as p}from"../core/canvas/browse/app.js";import{ApiCanvasSource as u,cliClient as o}from"./api-client.js";import{getDefaultProfileId as f}from"../core/profiles/default-binding.js";const b=l({name:"browse",description:"open the interactive canvas navigator (tabs/tree/search)",whenToUse:"you want to VIEW and NAVIGATE the whole canvas interactively \u2014 a full-screen TUI with tabs (All/Live/Dormant/Attention/Pinned), an expandable tree (children auto-collapsed; \u2192 to expand), `/` fuzzy search, `f` to pin, `y` to copy a node id, `m` to message and `R` to rename a node in place, and `?` for the full keymap; it opens on Live (active/idle) and Enter resumes the chosen node. Use this to find your way around a large canvas. Use `canvas dashboard` instead for a one-shot ASCII tree you can pipe, and `node inspect list` for a flat machine-readable roster",help:{name:"canvas browse",summary:"interactive full-screen canvas navigator \u2014 tabs, an auto-collapsed tree, and `/` fuzzy search; Enter resumes the chosen node via `crtr surface node focus`. Outside a TTY it prints the static forest and exits",params:[{kind:"flag",name:"return-pane",type:"string",required:!1,constraint:"tmux pane id the chosen node REPLACES on Enter \u2014 that pane is respawned onto its viewer (set by the /resume popup, so resuming swaps the pane you invoked it from). Default: focus into this pane without replacing it."},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"directory used when you toggle the optional cwd filter (c). The navigator opens with All dirs; /resume passes the launching node's cwd. Default: the invocation cwd."},{kind:"flag",name:"profile-id",type:"string",required:!1,constraint:"Exact profile id for the initial profile filter (on by default; P toggles All profiles). Default: the launching node's profile via CRTR_PROFILE_ID, then this cwd's pinned default profile; absent means All profiles."}],output:[],outputKind:"object",effects:["Takes over the terminal in raw mode (alt-screen) until you quit (q/Esc) or pick a node.","On Enter: resumes the selected node via `crtr surface node focus` (reviveNode \u2014 the only sanctioned open).","Read-only on the canvas db; mutates nothing but the chosen node's placement on resume."]},run:t(async e=>{const a=e.cwd??process.cwd(),s=e.profileId??c()??f(a)??void 0,r=new u;await p({returnPane:e.returnPane,cwd:a,profileId:s,source:r,viewedNodeIds:t(async()=>new Set((await o().listFocuses()).map(n=>n.node_id)),"viewedNodeIds"),contentSearch:t(n=>o().historySearch(n),"contentSearch"),closeNode:t(n=>o().closeNode(n,process.ppid>0?{finish:!0,caller_pid:process.ppid}:{finish:!0}).then(i=>({closed:i.closed})),"closeNode")})},"run")});export{b as browseLeaf};
@@ -1 +1 @@
1
- var p=Object.defineProperty;var i=(e,n)=>p(e,"name",{value:n,configurable:!0});import{defineLeaf as u}from"../../../core/command.js";import{InputError as l}from"../../../core/io.js";import{focusNode as h}from"../../../core/runtime/focus-viewer.js";import{cliClient as w,rethrowAsCliError as s}from"../../api-client.js";import{currentTmux as f}from"./placement.js";async function d(e,n,a,r=!1,o=!1){const t=await h(e,n,{pane:a,newPane:r,inPlace:o}).catch(c=>s(c));return{focused:t.focused,session:t.session,revived:t.revived,in_place:t.inPlace}}i(d,"focusNodeOrThrow");const x=u({name:"focus",description:"bring a node into view without waking parked or finished work",whenToUse:"you want to bring a specific node into view \u2014 a live node opens beside your pane, while a parked resident opens its saved conversation without waking until input is submitted. FINISHED work \u2014 a terminal worker, or any node that pushed a final report \u2014 opens its saved transcript instead of being put back to work. Use `node lifecycle revive` to deliberately relaunch finished work anyway. Use `surface node cycle` to walk neighbors, and `node message send` to steer without leaving where you are",help:{name:"surface node focus",summary:"bring a node into view \u2014 attach live, open a parked resident without waking it, or inspect finished work as a saved transcript",params:[{kind:"positional",name:"node",required:!0,constraint:"Node id to focus."},{kind:"flag",name:"new-pane",type:"bool",required:!1,constraint:"Always open a NEW viewer SIDE-BY-SIDE with your current pane instead of moving/reusing the node's existing viewer. Two agents on screen at once (F4)."},{kind:"flag",name:"in-place",type:"bool",required:!1,constraint:"Re-exec the --pane pane ONTO this node, REPLACING whatever viewer it showed (a swap in place, not a split beside). Backs the attach viewer's subagent scroll+swap (\u2193 into the reports roster, Enter dives into the child right in your pane). MUST be driven from OUTSIDE that pane's process tree (e.g. `tmux run-shell`) \u2014 respawn-pane -k kills whatever runs in it. Mutually exclusive with --new-pane."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id to focus beside (default: caller TMUX_PANE). Used by the canvas browser popup to focus back into the originating pane."}],output:[{name:"focused",type:"boolean",required:!0,constraint:"True when the node's viewer was brought into view."},{name:"session",type:"string",required:!1,constraint:"The tmux session the viewer lives in."},{name:"revived",type:"boolean",required:!0,constraint:"True when focusing had to recover a non-parked node's broker; false for an already attached viewer, an offline parked resident, or saved-transcript inspection."},{name:"in_place",type:"boolean",required:!0,constraint:"True when an existing viewer was navigated to (no new pane); false when a fresh viewer pane was opened or moved beside you."}],outputKind:"object",effects:["For active/idle nodes: opens/moves the one surface-attach viewer beside the caller, or navigates to its existing viewer.","For an unfinalized parked resident: opens the normal conversation viewer from its saved snapshot without launching a broker or changing lifecycle; submitting input sends the durable human message that wakes it.","For finished work (a terminal node, or any node with a final report): opens a read-only saved transcript popup. It does not launch a broker or change node status.","With --new-pane/--in-place on a conversation viewer: controls whether the viewer splits beside or replaces the caller pane."]},run:i(async e=>{const n=e.node,a=w(),r=await a.getNode(n).catch(t=>s(t,"List nodes with `crtr node inspect list`."));if(r.kind==="human")return d(a,r,"");const o=e.pane??process.env.TMUX_PANE??f()?.pane;if(o===void 0||o==="")throw new l({error:"no_pane",message:`no tmux pane to focus ${n} into \u2014 surfaces need a tmux server, and this call has neither --pane nor a caller pane.`,next:`Run it from inside tmux, pass \`--pane <id>\`, or read the node without a surface: \`crtr node inspect transcript ${n}\`.`});return d(a,r,o,e.newPane===!0,e.inPlace===!0)},"run")});export{x as surfaceNodeFocusLeaf};
1
+ var u=Object.defineProperty;var s=(e,n)=>u(e,"name",{value:n,configurable:!0});import{defineLeaf as h}from"../../../core/command.js";import{InputError as w}from"../../../core/io.js";import{focusNode as f}from"../../../core/runtime/focus-viewer.js";import{cliClient as v,rethrowAsCliError as c}from"../../api-client.js";import{currentTmux as m}from"./placement.js";async function p(e,n,t,a=!1,o=!1,i=!1,d){const r=await f(e,n,{pane:t,newPane:a,inPlace:o,keepOtherViewers:i,...d===void 0?{}:{openTicket:d}}).catch(l=>c(l));return{focused:r.focused,session:r.session,revived:r.revived,in_place:r.inPlace}}s(p,"focusNodeOrThrow");const E=h({name:"focus",description:"bring a node into view without waking parked or finished work",whenToUse:"you want to bring a specific node into view \u2014 a live node opens beside your pane, while a parked resident opens its saved conversation without waking until input is submitted. FINISHED work \u2014 a terminal worker, or any node that pushed a final report \u2014 opens its saved transcript instead of being put back to work. Use `node lifecycle revive` to deliberately relaunch finished work anyway. Use `surface node cycle` to walk neighbors, and `node message send` to steer without leaving where you are",help:{name:"surface node focus",summary:"bring a node into view \u2014 attach live, open a parked resident without waking it, or inspect finished work as a saved transcript",params:[{kind:"positional",name:"node",required:!0,constraint:"Node id to focus."},{kind:"flag",name:"new-pane",type:"bool",required:!1,constraint:"Always open a NEW viewer SIDE-BY-SIDE with your current pane instead of moving/reusing the node's existing viewer. Two agents on screen at once (F4)."},{kind:"flag",name:"in-place",type:"bool",required:!1,constraint:"Re-exec the --pane pane ONTO this node, REPLACING whatever viewer it showed (a swap in place, not a split beside). Backs the attach viewer's subagent scroll+swap (\u2193 into the reports roster, Enter dives into the child right in your pane). MUST be driven from OUTSIDE that pane's process tree (e.g. `tmux run-shell`) \u2014 respawn-pane -k kills whatever runs in it. Mutually exclusive with --new-pane."},{kind:"flag",name:"keep-other-viewers",type:"bool",required:!1,constraint:"With --in-place: leave the node's viewer in another pane open instead of closing it, so the conversation shows in both panes."},{kind:"flag",name:"open-ticket",type:"string",required:!1,constraint:"With --in-place: an inbox ticket id the new viewer opens before it shows the conversation; once the ask is answered or closed the pane shows the conversation. An already-answered ticket shows a notice instead."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id to focus beside (default: caller TMUX_PANE). Used by the canvas browser popup to focus back into the originating pane."}],output:[{name:"focused",type:"boolean",required:!0,constraint:"True when the node's viewer was brought into view."},{name:"session",type:"string",required:!1,constraint:"The tmux session the viewer lives in."},{name:"revived",type:"boolean",required:!0,constraint:"True when focusing had to recover a non-parked node's broker; false for an already attached viewer, an offline parked resident, or saved-transcript inspection."},{name:"in_place",type:"boolean",required:!0,constraint:"True when an existing viewer was navigated to (no new pane); false when a fresh viewer pane was opened or moved beside you."}],outputKind:"object",effects:["For active/idle nodes: opens/moves the one surface-attach viewer beside the caller, or navigates to its existing viewer.","For an unfinalized parked resident: opens the normal conversation viewer from its saved snapshot without launching a broker or changing lifecycle; submitting input sends the durable human message that wakes it.","For finished work (a terminal node, or any node with a final report): opens a read-only saved transcript popup. It does not launch a broker or change node status.","With --new-pane/--in-place on a conversation viewer: controls whether the viewer splits beside or replaces the caller pane."]},run:s(async e=>{const n=e.node,t=v(),a=await t.getNode(n).catch(i=>c(i,"List nodes with `crtr node inspect list`."));if(a.kind==="human")return p(t,a,"");const o=e.pane??process.env.TMUX_PANE??m()?.pane;if(o===void 0||o==="")throw new w({error:"no_pane",message:`no tmux pane to focus ${n} into \u2014 surfaces need a tmux server, and this call has neither --pane nor a caller pane.`,next:`Run it from inside tmux, pass \`--pane <id>\`, or read the node without a surface: \`crtr node inspect transcript ${n}\`.`});return p(t,a,o,e.newPane===!0,e.inPlace===!0,e.keepOtherViewers===!0,e.openTicket)},"run")});export{E as surfaceNodeFocusLeaf};
@@ -0,0 +1,2 @@
1
+ import type { LeafDef } from '../core/command.js';
2
+ export declare const surfaceDashboardLeaf: LeafDef;
@@ -0,0 +1,3 @@
1
+ var E=Object.defineProperty;var s=(t,a)=>E(t,"name",{value:a,configurable:!0});import{defineLeaf as _}from"../core/command.js";import{envProfileId as p}from"../shared/env.js";import{getDefaultProfileId as f}from"../core/profiles/default-binding.js";import{SECTION_ORDER as k,SECTION_TITLE as x,sectionOf as R}from"../clients/dashboard/model.js";import{closeWindow as I,currentTmux as T,displayMessage as v,getPaneOption as P,openNodeWindow as N,paneCurrentCommand as C,paneCurrentPath as D,paneLocation as O,paneStartCommand as U,renameWindow as W,respawnPaneSync as L,shellQuote as S,swapPanes as A,viewerSplitEnv as M,windowOfPane as X}from"../core/runtime/placement-tmux.js";import{cliClient as $}from"./api-client.js";const h="CRTR_DASHBOARD_RUN";async function j(t,a,e){if(t!==void 0&&t!=="")return t;const o=a===void 0?void 0:P(a,"@crtr_node");if(o!==void 0&&o!=="")try{const r=(await $().getNode(o)).profile_id;if(r!=null)return r}catch{}return p()??f(e)??void 0}s(j,"profileFor");async function q(t){const a=await $().liveRoots(t===void 0?{}:{profile:t}),e=[];for(const o of k){const r=a.rows.filter(n=>R(n)===o);if(r.length!==0){e.push(`${x[o]} (${r.length})`);for(const n of r){const i=[n.running_count>0?`\u27F3 ${n.running_count}`:"",n.asks>0?`? ${n.asks}`:"",n.fault!==null?"\u26A0":""].filter(d=>d!=="").join(" ");e.push(` ${n.name} ${n.state_line??n.goal??""}${i===""?"":` ${i}`}`)}}}process.stdout.write(`${e.length===0?"no live conversations":e.join(`
2
+ `)}
3
+ `)}s(q,"printListing");const G=_({name:"dashboard",description:"show the profile\u2019s live root conversations in a tmux pane \u2014 which need the person, which run, which are idle",whenToUse:"the person asks to see all their running conversations in one place, or you are wiring a key to it. It is a screen for the person, not a read: use `canvas dashboard` for a one-shot tree you can pipe",help:{name:"surface dashboard",summary:"put the dashboard in a tmux pane: live root conversations in Waiting for you / Running / Idle, Enter opens one in that pane, n starts a new background conversation. Outside tmux it prints the same listing as text",params:[{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane that shows the dashboard. Default: $TMUX_PANE. A pane running a crouter viewer or a dashboard is respawned; any other pane (a shell, an editor) is swapped for a new window that runs the dashboard, and its program keeps running in the background window."},{kind:"flag",name:"profile-id",type:"string",required:!1,constraint:"Exact profile id to list. Default: the profile of the conversation shown in the pane, then $CRTR_PROFILE_ID, then the pane directory\u2019s pinned default profile; absent means all profiles."}],output:[],outputKind:"object",effects:["Respawns the target pane onto the dashboard (a crouter viewer\u2019s engine keeps running), or opens a new tmux window with the dashboard and swaps it into the pane\u2019s place.","Never kills a pane that is not running a crouter viewer or dashboard.","Inside the dashboard, Enter runs `surface node focus` for the chosen conversation, `n` creates a root conversation, `x` finishes one."]},run:s(async t=>{const a=t.profileId;if(process.env[h]==="1"){delete process.env[h];const{runDashboard:c}=await import("../clients/dashboard/app.js"),y=a??p()??f(process.cwd())??void 0;await c({profileId:y,cwd:process.cwd()});return}if(process.env.TMUX===void 0||process.env.TMUX===""){await q(a??p()??f(process.cwd())??void 0);return}const e=t.pane??process.env.TMUX_PANE??T()?.pane;if(e===void 0||e==="")throw v("crtr: no pane to show the dashboard in"),new Error("no tmux pane to show the dashboard in \u2014 pass --pane <id>");const o=s(c=>{throw v(`crtr: ${c}`,e),new Error(c)},"fail"),r=U(e)?.replace(/^"|"$/g,"");if(r===void 0)return o(`pane ${e} is not reachable`);if(r.startsWith("crtr surface dashboard"))return;const n=D(e)??process.cwd(),i=await j(a,e,n),d=`crtr surface dashboard${i===void 0?"":` --profile-id ${S(i)}`}`,l={...M(),[h]:"1"};if(r.startsWith("crtr surface attach to ")){L({pane:e,cwd:n,env:l,command:d})||o(`tmux could not respawn pane ${e}`);return}const w=O(e);if(w===null)return o(`pane ${e} is not in a tmux window`);const u=N({session:w.session,name:"dashboard",cwd:n,env:l,command:d});if(u===null)return o("tmux could not open a window for the dashboard");const m=C(e),g=A(u.pane,e);if(g!==null)return I(u.window),o(`could not show the dashboard: ${g}`);const b=X(e);b!==null&&m!==void 0&&W(b,m)},"run"),render:s(()=>"","render")});export{G as surfaceDashboardLeaf};
@@ -1 +1 @@
1
- var o=Object.defineProperty;var r=(e,a)=>o(e,"name",{value:a,configurable:!0});import{defineBranch as n}from"../core/command.js";import{registerAttach as t}from"../clients/attach/command.js";import{tmuxSpreadLeaf as c}from"./surface-tmux-spread.js";import{surfaceEditLeaf as s}from"./surface-edit.js";import{registerSurfaceNode as i}from"./surface-node.js";import{surfaceInspectLeaf as m}from"./surface-inspect.js";import{surfaceReloadLeaf as f}from"./surface-reload.js";import{surfaceInboxLeaf as p}from"./surface-inbox.js";function x(){const e=t("surface","important");return n({name:"surface",rootEntry:{concept:"screen-facing surfaces \u2014 node viewers, live broker attachment, and the Inspector",desc:"open and manage screen-facing surfaces",useWhen:"you need a UI or presentation surface rather than a node, document, or graph operation \u2014 node-viewer navigation, live broker attachment, node inspection, tmux placement, or an editor pane"},help:{name:"surface",summary:"open and manage screen-facing surfaces",model:"Choose `node` for navigation among node viewers. The other commands open a broker attachment, node Inspector, subtree spread, reload, or adjacent editor pane."},children:[i(),e,m,f,c,s,p]})}r(x,"registerSurface");export{x as registerSurface};
1
+ var o=Object.defineProperty;var r=(e,a)=>o(e,"name",{value:a,configurable:!0});import{defineBranch as n}from"../core/command.js";import{registerAttach as t}from"../clients/attach/command.js";import{tmuxSpreadLeaf as c}from"./surface-tmux-spread.js";import{surfaceEditLeaf as s}from"./surface-edit.js";import{registerSurfaceNode as m}from"./surface-node.js";import{surfaceInspectLeaf as f}from"./surface-inspect.js";import{surfaceReloadLeaf as i}from"./surface-reload.js";import{surfaceInboxLeaf as p}from"./surface-inbox.js";import{surfaceDashboardLeaf as d}from"./surface-dashboard.js";function w(){const e=t("surface","important");return n({name:"surface",rootEntry:{concept:"screen-facing surfaces \u2014 node viewers, live broker attachment, and the Inspector",desc:"open and manage screen-facing surfaces",useWhen:"you need a UI or presentation surface rather than a node, document, or graph operation \u2014 node-viewer navigation, live broker attachment, node inspection, tmux placement, or an editor pane"},help:{name:"surface",summary:"open and manage screen-facing surfaces",model:"Choose `node` for navigation among node viewers. The other commands open a broker attachment, node Inspector, subtree spread, reload, or adjacent editor pane."},children:[m(),e,f,i,c,s,p,d]})}r(w,"registerSurface");export{w as registerSurface};
@@ -1,4 +1,5 @@
1
1
  import type { CanvasSource } from '../source.js';
2
+ import type { HistorySearchQuery, HistorySearchResultDTO } from '../../../api/dto/canvas.js';
2
3
  import { type BindingId, type BindingResolution, type RawTerminalInput } from '../../keybindings/index.js';
3
4
  declare const BROWSE_NAV_ACTIONS: readonly ["crtr.browse.quit", "crtr.browse.down", "crtr.browse.up", "crtr.browse.first", "crtr.browse.last", "crtr.browse.expand-or-child", "crtr.browse.collapse-or-parent", "crtr.browse.toggle-fold", "crtr.browse.tab-next", "crtr.browse.tab-previous", "crtr.browse.tab.1", "crtr.browse.tab.2", "crtr.browse.tab.3", "crtr.browse.tab.4", "crtr.browse.tab.5", "crtr.browse.close-subtree", "crtr.browse.sort", "crtr.browse.scope", "crtr.browse.profile", "crtr.browse.residents", "crtr.browse.preview", "crtr.browse.report", "crtr.browse.pin", "crtr.browse.search", "crtr.browse.revive-now", "crtr.browse.focus", "crtr.browse.focus-background", "crtr.browse.yank-id"];
4
5
  declare const BROWSE_SEARCH_ACTIONS: readonly ["crtr.browse.search.cancel", "crtr.browse.search.commit", "crtr.browse.search.delete", "crtr.browse.search.quit"];
@@ -22,5 +23,7 @@ export declare function runBrowse(opts: {
22
23
  closeNode: (id: string) => Promise<{
23
24
  closed: string[];
24
25
  }>;
26
+ /** Conversation-content search for `/` (the CLI injects `cliClient().historySearch`). */
27
+ contentSearch: (q: HistorySearchQuery) => Promise<HistorySearchResultDTO>;
25
28
  }): Promise<void>;
26
29
  export {};
@@ -1,3 +1,3 @@
1
- var Te=Object.defineProperty;var c=(a,l)=>Te(a,"name",{value:l,configurable:!0});import{envNodeId as J}from"../../../shared/env.js";import{execFileSync as S}from"node:child_process";import{resolve as Re}from"node:path";import{dashboardRowsAllFromSource as Pe,enrichRowsFromSource as X,loadPreviewFromSource as Be,loadSearchTextFromSource as Z,renderForestFromSource as Ne}from"../render-source.js";import{fullName as qe}from"../labels.js";import{setupTerminal as Me,restoreTerminal as Fe,getTerminalSize as ke,parseKeypress as Ee}from"../../tui/terminal.js";import{buildTree as _e,flatten as Ae,pruneNode as Ke,TABS as x}from"./model.js";import{loadPins as Le,savePins as He}from"./pins.js";import{matchesTerminalInput as ee,resolveUserKeybindings as We}from"../../keybindings/index.js";const $e=["crtr.browse.quit","crtr.browse.down","crtr.browse.up","crtr.browse.first","crtr.browse.last","crtr.browse.expand-or-child","crtr.browse.collapse-or-parent","crtr.browse.toggle-fold","crtr.browse.tab-next","crtr.browse.tab-previous","crtr.browse.tab.1","crtr.browse.tab.2","crtr.browse.tab.3","crtr.browse.tab.4","crtr.browse.tab.5","crtr.browse.close-subtree","crtr.browse.sort","crtr.browse.scope","crtr.browse.profile","crtr.browse.residents","crtr.browse.preview","crtr.browse.report","crtr.browse.pin","crtr.browse.search","crtr.browse.revive-now","crtr.browse.focus","crtr.browse.focus-background","crtr.browse.yank-id"],ze=["crtr.browse.search.cancel","crtr.browse.search.commit","crtr.browse.search.delete","crtr.browse.search.quit"];function re(a,l,m){return l.find(g=>ee(a,g,m))??null}c(re,"firstMatching");function Ve(a,l){return re(a,$e,l)}c(Ve,"resolveBrowseNavAction");function je(a,l){return re(a,ze,l)}c(je,"resolveBrowseSearchAction");const L=["attention","tree","relevance","recency"];import{renderFrame as Ue,detectColorCaps as Ye,headerHeight as De,previewLayout as te,PREVIEW_HEIGHT as Ge}from"./render.js";import{writeClipboardTextEverywhere as Qe}from"../../clipboard-text.js";function Je(a,l,m){const g=Math.max(8,a.rows),y=te(a.cols,m).mode==="bottom"?Ge:0;return Math.max(1,g-De(l)-1-y)}c(Je,"viewportHeight");async function fr(a){const l=a.source,m=We();if(!process.stdin.isTTY){process.stdout.write(await Ne(l)+`
2
- `);return}let g,y,N,H,u,W,q={};await c(async()=>{let r=new Set;if(a.viewedNodeIds!==void 0)try{r=await a.viewedNodeIds()}catch{}y=await(l.browseSnapshot?.()??l.listNodes()),g=(await Pe(l,r,!1,y)).filter(s=>s.kind!=="human"),N=y.filter(s=>s.parent===null&&s.kind!=="human").map(s=>s.node_id);const t=new Map;await Promise.all(y.map(async s=>{t.set(s.node_id,(await l.subscriptionsOf(s.node_id).catch(()=>[])).map(o=>o.node_id))})),H=t,u=_e(g,N,s=>H.get(s)??[]),W=u.nodes.size,q=await Promise.all(N.map(async s=>[s,...await l.view(s)])).then(s=>l.ticketCountsForNodes([...new Set(s.flat())])).catch(()=>({}))},"rebuildSnapshot")();const ne=a.cwd!==void 0&&a.cwd.trim()!==""?Re(a.cwd):null,M=a.profileId,e={tab:"Live",cursor:0,collapsed:new Set([...u.nodes.entries()].filter(([,r])=>r.childIds.length>0).map(([r])=>r)),query:"",search:!1,scrollOffset:0,cwdScope:null,profileScope:M,sort:"attention",preview:!0,showReport:!1,residentsOnly:!0,pendingClose:null,flash:null,pinned:Le(),help:!1,rename:!1,renameBuf:"",renameTarget:null,message:!1,messageBuf:"",messageTarget:null};let w=[];const O=c(r=>u.nodes.get(r)?.row,"rowOf"),se=Ye();let b=!1;const I=c(()=>{if(!b){b=!0;try{Fe()}catch{}}},"cleanup");process.once("exit",I);const F=c((r,t)=>{process.stderr.write(`[crtr browse] ${r}: ${String(t?.message??t)}
3
- `)},"logAsyncFailure"),oe=c(r=>{try{S("crtr",["node","lifecycle","revive",r,"--now"],{stdio:"ignore"})}catch{}},"reviveNow"),ie=c(r=>{I();const t=a.returnPane!==void 0&&a.returnPane!==""?a.returnPane:void 0;try{t!==void 0?S("tmux",["run-shell","-b",`crtr surface node focus ${r} --pane ${t} --in-place >/dev/null 2>&1`],{stdio:"ignore"}):S("crtr",["surface","node","focus",r],{stdio:"inherit"})}catch{}process.exit(0)},"selectAndFocus"),ce=c(r=>{const t=["surface","node","focus",r,...a.returnPane!==void 0&&a.returnPane!==""?["--pane",a.returnPane]:[]];try{S("crtr",t,{stdio:"ignore"})}catch{}},"selectAndFocusKeepOpen"),f=c(r=>{if(w=Ae(u,{collapsed:e.collapsed,tab:e.tab,query:e.query,cwdScope:e.cwdScope,profileScope:e.profileScope,residentsOnly:e.residentsOnly,sort:e.sort,pinned:e.pinned}),r!==void 0){const t=w.findIndex(s=>s.id===r);t>=0&&(e.cursor=t)}e.cursor>w.length-1&&(e.cursor=Math.max(0,w.length-1)),e.cursor<0&&(e.cursor=0)},"recompute"),$=c(async()=>{const r=ke(),t=e.preview&&w.length>0,s=e.search||e.rename||e.message,o=Je(r,s,t);e.cursor<e.scrollOffset&&(e.scrollOffset=e.cursor),e.cursor>=e.scrollOffset+o&&(e.scrollOffset=e.cursor-o+1),e.scrollOffset<0&&(e.scrollOffset=0);const n=e.scrollOffset,d=Math.min(w.length,n+o),p=[];for(let K=n;K<d;K++){const Q=O(w[K].id);Q!==void 0&&p.push(Q)}await X(l,p,q),V(t,te(r.cols,t).mode==="side");const R=Ue({tree:u,visible:w,tab:e.tab,cursor:e.cursor,scrollOffset:e.scrollOffset,query:e.query,search:e.search,totalNodes:W,cwdScope:e.cwdScope,profileScope:e.profileScope,sort:e.sort,preview:e.preview,showReport:e.showReport,residentsOnly:e.residentsOnly,pendingClose:e.pendingClose,flash:e.flash,pinned:e.pinned,help:e.help,rename:e.rename,renameBuf:e.renameBuf,message:e.message,messageBuf:e.messageBuf,readOnly:!1},r,se);process.stdout.write(R)},"flush"),ae=90;let P,k=!1,v={on:!1,transcript:!1};const z=c(r=>r.previewLoaded!==!0||v.transcript&&r.transcriptLoaded!==!0||e.search&&e.query!==""&&r.promptsLoaded!==!0,"previewPending"),V=c((r,t)=>{v={on:r,transcript:t};const s=h()?.id,o=s===void 0?void 0:O(s);!r||o===void 0||!z(o)||(P!==void 0&&clearTimeout(P),P=setTimeout(()=>{P=void 0,le()},ae))},"schedulePreview"),le=c(async()=>{if(k||b)return;const r=h()?.id,t=r===void 0?void 0:O(r);if(!(!v.on||t===void 0||!z(t))){k=!0;try{await Be(l,t,{transcript:v.transcript}),e.search&&e.query!==""&&await Z(l,t)}catch(s){F("preview load failed",s),t.previewLoaded=!0,t.transcriptLoaded=!0}finally{k=!1}b||(h()?.id===r?i():V(v.on,v.transcript))}},"runPreview"),C=c(()=>{I(),process.exit(0)},"quit"),h=c(()=>w[e.cursor],"curRow"),E=c(r=>!e.collapsed.has(r),"isExpanded"),de=c(r=>{const t=[],s=[r],o=new Set;for(;s.length>0;){const n=s.pop();if(!o.has(n)){o.add(n),t.push(n);for(const d of u.nodes.get(n)?.childIds??[])s.push(d)}}return t},"subtreeIds"),fe=c(r=>de(r).some(t=>u.nodes.get(t)?.row.streaming===!0),"anyStreaming"),j=c(async r=>{e.pendingClose=null;let t=[];try{t=(await a.closeNode(r)).closed}catch{return}for(const s of t){const o=await l.getNode(s);if(o===null){Ke(u,s);continue}const n=u.nodes.get(s);n!==void 0&&(n.row.status=o.status,n.row.streaming=!1,n.row.viewed=!1,n.row.hanging=null,n.row.asks=0)}f()},"doClose"),U=c(r=>{const t=x.indexOf(e.tab);e.tab=x[(t+r+x.length)%x.length],e.cursor=0,e.scrollOffset=0,f()},"cycleTab"),ue=c(()=>{const r=h()?.id,t=L.indexOf(e.sort);e.sort=L[(t+1)%L.length],f(r)},"cycleSort"),we=c(()=>{const r=h()?.id;e.cwdScope=e.cwdScope===null?ne:null,f(r)},"toggleScope"),pe=c(()=>{if(M===void 0)return;const r=h()?.id;e.profileScope=e.profileScope===void 0?M:void 0,f(r)},"toggleProfile"),he=c(()=>{const r=h()?.id;e.residentsOnly=!e.residentsOnly,f(r)},"toggleResidents");let _=!1,A=!1,B=!1;const i=c(()=>{B=!0,!A&&(A=!0,(async()=>{try{for(;B&&!b;)B=!1,await $()}finally{A=!1,B&&!b&&i()}})().catch(()=>{I(),process.exit(1)}))},"scheduleFlush"),Y=c(()=>{_||(_=!0,setImmediate(()=>{_=!1,f(),i()}))},"scheduleSearchRepaint"),me=c((r,t)=>{const s=je(m,{input:r,...t});if(s==="crtr.browse.search.cancel"){e.search=!1,e.query="",e.sort="tree",f(),i();return}if(s==="crtr.browse.search.commit"){e.search=!1,f();const n=w.findIndex(d=>d.matched);n>=0&&(e.cursor=n),i();return}if(s==="crtr.browse.search.delete"){e.query=e.query.slice(0,-1),Y();return}if(s==="crtr.browse.search.quit"&&C(),t.ctrl)return;const o=[...r].filter(n=>n>=" ").join("");o.length>0&&(e.query+=o,Y())},"onKeySearch"),be=c(()=>{const r=J();if(!(r===void 0||r===""||!u.nodes.has(r))){for(let t=u.nodes.get(r)?.parentId??null;t!==null;t=u.nodes.get(t)?.parentId??null)e.collapsed.delete(t);f(r)}},"recenterOnSelf"),ge=c(r=>{e.pinned.has(r)?e.pinned.delete(r):e.pinned.add(r),He(e.pinned),f(r)},"togglePin"),ye=c(()=>{const r=e.renameTarget,t=e.renameBuf.trim();if(e.rename=!1,r!==null&&t!==""){try{S("crtr",["node","config",r,"--name",t],{stdio:"ignore"})}catch{}const s=u.nodes.get(r);s!==void 0&&(s.row.handle=t,s.row.name=qe({name:t,kind:s.row.kind,description:s.row.description}))}f(e.renameTarget??void 0),i()},"commitRename"),ve=c(()=>{const r=e.messageTarget,t=e.messageBuf.trim();if(e.message=!1,r!==null&&t!=="")try{S("crtr",["node","message","send","--to",r,t],{stdio:"ignore"})}catch{}i()},"commitMessage"),D=c((r,t,s,o,n,d)=>{if(t.escape){d();return}if(t.return){n();return}if(t.backspace){o(s().slice(0,-1)),i();return}if(t.ctrl){r==="c"&&C();return}const p=[...r].filter(R=>R>=" ").join("");p.length>0&&(o(s()+p),i())},"onKeyText"),Se=c((r,t)=>D(r,t,()=>e.renameBuf,s=>{e.renameBuf=s},ye,()=>{e.rename=!1,i()}),"onKeyRename"),xe=c((r,t)=>D(r,t,()=>e.messageBuf,s=>{e.messageBuf=s},ve,()=>{e.message=!1,i()}),"onKeyMessage"),Oe=c((r,t)=>{if(t.ctrl&&r==="c"){C();return}e.help=!1,i()},"onKeyHelp"),Ie=c((r,t)=>{const s={input:r,...t},o=Ve(m,s);if(e.flash=null,t.ctrl&&o===null)return;if(e.pendingClose!==null&&o==="crtr.browse.quit"&&t.ctrl&&C(),e.pendingClose!==null){if(ee(m,"crtr.browse.confirm-close",s)){const d=e.pendingClose;j(d).then(()=>i()).catch(()=>i());return}e.pendingClose=null,i();return}const n=h();if(o==="crtr.browse.quit"&&C(),o==="crtr.browse.up"){e.cursor=Math.max(0,e.cursor-1),i();return}if(o==="crtr.browse.down"){e.cursor=Math.max(0,Math.min(w.length-1,e.cursor+1)),i();return}if(o==="crtr.browse.first"){e.cursor=0,i();return}if(o==="crtr.browse.last"){e.cursor=Math.max(0,w.length-1),i();return}if(o==="crtr.browse.expand-or-child"){n!==void 0&&n.hasChildren&&(E(n.id)?e.cursor+1<w.length&&w[e.cursor+1].depth>n.depth&&(e.cursor+=1):(e.collapsed.delete(n.id),f(n.id))),i();return}if(o==="crtr.browse.collapse-or-parent"){if(n!==void 0&&n.hasChildren&&E(n.id))e.collapsed.add(n.id),f(n.id);else if(n!==void 0){const d=u.nodes.get(n.id)?.parentId??null;if(d!==null){const p=w.findIndex(R=>R.id===d);p>=0&&(e.cursor=p)}}i();return}if(o==="crtr.browse.toggle-fold"){n!==void 0&&n.hasChildren&&(E(n.id)?e.collapsed.add(n.id):e.collapsed.delete(n.id),f(n.id)),i();return}if(o==="crtr.browse.tab-next"){U(1),i();return}if(o==="crtr.browse.tab-previous"){U(-1),i();return}if(o?.startsWith("crtr.browse.tab.")){const d=Number(o.slice(-1))-1;d<x.length&&(e.tab=x[d],e.cursor=0,e.scrollOffset=0,f()),i();return}if(o==="crtr.browse.close-subtree"){if(n===void 0){i();return}if(fe(n.id)){e.pendingClose=n.id,i();return}j(n.id).then(()=>i()).catch(()=>i());return}if(o==="crtr.browse.sort"){ue(),i();return}if(o==="crtr.browse.scope"){we(),i();return}if(o==="crtr.browse.profile"){pe(),i();return}if(o==="crtr.browse.residents"){he(),i();return}if(o==="crtr.browse.preview"){e.preview=!e.preview,i();return}if(o==="crtr.browse.report"){e.showReport=!e.showReport,i();return}if(r==="?"){e.help=!0,i();return}if(o==="crtr.browse.pin"){n!==void 0&&ge(n.id),i();return}if(r==="."){be(),i();return}if(r==="R"){n!==void 0&&(e.rename=!0,e.renameTarget=n.id,e.renameBuf=O(n.id)?.name??""),i();return}if(r==="m"){n!==void 0&&(e.message=!0,e.messageTarget=n.id,e.messageBuf=""),i();return}if(o==="crtr.browse.search"){Ce(),e.search=!0,e.query="",e.sort="relevance",e.cursor=0,e.scrollOffset=0,f(),i();return}if(o==="crtr.browse.revive-now"){n!==void 0&&O(n.id)?.hanging!=null&&oe(n.id),i();return}if(o==="crtr.browse.yank-id"){if(n!==void 0){const{system:d,tmux:p}=Qe(n.id);e.flash=d||p?`Copied ${n.id}${d?"":" to the tmux buffer"}`:"No clipboard tool available \u2014 nothing copied"}i();return}if(o==="crtr.browse.focus"){n!==void 0&&ie(n.id);return}if(o==="crtr.browse.focus-background"){n!==void 0&&ce(n.id),i();return}},"onKeyNav");f(),w.length===0&&e.residentsOnly&&(e.residentsOnly=!1,f());const T=J();if(T!==void 0&&T!==""&&u.nodes.has(T)){for(let r=u.nodes.get(T)?.parentId??null;r!==null;r=u.nodes.get(r)?.parentId??null)e.collapsed.delete(r);f(T)}Me(),await $();let G=!1;const Ce=c(()=>{if(G)return;G=!0;const r=[...u.nodes.values()].map(n=>n.row);let t=0;const s=30,o=c(async()=>{if(b)return;const n=Math.min(t+s,r.length),d=r.slice(t,n);await X(l,d,q),await Promise.all(d.map(p=>Z(l,p))),t=n,e.search&&e.query!==""&&(f(h()?.id),i()),t<r.length&&setImmediate(()=>{o().catch(p=>F("background warm failed",p))})},"warmChunk");setImmediate(()=>{o().catch(n=>F("background warm failed",n))})},"startWarm");await new Promise(()=>{const r=c(t=>{try{const{input:s,key:o}=Ee(t);e.help?Oe(s,o):e.search?me(s,o):e.rename?Se(s,o):e.message?xe(s,o):Ie(s,o)}catch{I(),process.exit(1)}},"onData");process.stdin.on("data",r),process.stdout.on("resize",i)})}c(fr,"runBrowse");export{Ve as resolveBrowseNavAction,je as resolveBrowseSearchAction,fr as runBrowse};
1
+ var Ae=Object.defineProperty;var c=(a,l)=>Ae(a,"name",{value:l,configurable:!0});import{envNodeId as oe}from"../../../shared/env.js";import{execFileSync as I}from"node:child_process";import{resolve as He}from"node:path";import{dashboardRowsAllFromSource as Ke,enrichRowsFromSource as ie,loadPreviewFromSource as Le,loadSearchTextFromSource as ce,renderForestFromSource as We}from"../render-source.js";import{fullName as $e}from"../labels.js";import{setupTerminal as ze,restoreTerminal as Ue,getTerminalSize as Ve,parseKeypress as je}from"../../tui/terminal.js";import{buildTree as De,flatten as ae,pruneNode as Ye,scoreRow as Ge,TABS as T}from"./model.js";import{loadPins as Qe,savePins as Je}from"./pins.js";import{matchesTerminalInput as de,resolveUserKeybindings as Xe}from"../../keybindings/index.js";const Ze=["crtr.browse.quit","crtr.browse.down","crtr.browse.up","crtr.browse.first","crtr.browse.last","crtr.browse.expand-or-child","crtr.browse.collapse-or-parent","crtr.browse.toggle-fold","crtr.browse.tab-next","crtr.browse.tab-previous","crtr.browse.tab.1","crtr.browse.tab.2","crtr.browse.tab.3","crtr.browse.tab.4","crtr.browse.tab.5","crtr.browse.close-subtree","crtr.browse.sort","crtr.browse.scope","crtr.browse.profile","crtr.browse.residents","crtr.browse.preview","crtr.browse.report","crtr.browse.pin","crtr.browse.search","crtr.browse.revive-now","crtr.browse.focus","crtr.browse.focus-background","crtr.browse.yank-id"],er=["crtr.browse.search.cancel","crtr.browse.search.commit","crtr.browse.search.delete","crtr.browse.search.quit"];function le(a,l,y){return l.find(v=>de(a,v,y))??null}c(le,"firstMatching");function rr(a,l){return le(a,Ze,l)}c(rr,"resolveBrowseNavAction");function tr(a,l){return le(a,er,l)}c(tr,"resolveBrowseSearchAction");const z=["attention","tree","relevance","recency"];import{renderFrame as nr,detectColorCaps as sr,headerHeight as or,previewLayout as fe,PREVIEW_HEIGHT as ir}from"./render.js";import{writeClipboardTextEverywhere as cr}from"../../clipboard-text.js";function ar(a,l,y){const v=Math.max(8,a.rows),S=fe(a.cols,y).mode==="bottom"?ir:0;return Math.max(1,v-or(l)-1-S)}c(ar,"viewportHeight");async function Or(a){const l=a.source,y=Xe();if(!process.stdin.isTTY){process.stdout.write(await We(l)+`
2
+ `);return}let v,S,B,U,u,V,_={};await c(async()=>{let r=new Set;if(a.viewedNodeIds!==void 0)try{r=await a.viewedNodeIds()}catch{}S=await(l.browseSnapshot?.()??l.listNodes()),v=(await Ke(l,r,!1,S)).filter(s=>s.kind!=="human"),B=S.filter(s=>s.parent===null&&s.kind!=="human").map(s=>s.node_id);const t=new Map;await Promise.all(S.map(async s=>{t.set(s.node_id,(await l.subscriptionsOf(s.node_id).catch(()=>[])).map(o=>o.node_id))})),U=t,u=De(v,B,s=>U.get(s)??[]),V=u.nodes.size,_=await Promise.all(B.map(async s=>[s,...await l.view(s)])).then(s=>l.ticketCountsForNodes([...new Set(s.flat())])).catch(()=>({}))},"rebuildSnapshot")();const ue=a.cwd!==void 0&&a.cwd.trim()!==""?He(a.cwd):null,E=a.profileId,e={tab:"Live",cursor:0,collapsed:new Set([...u.nodes.entries()].filter(([,r])=>r.childIds.length>0).map(([r])=>r)),query:"",search:!1,scrollOffset:0,cwdScope:null,profileScope:E,sort:"attention",preview:!0,showReport:!1,residentsOnly:!0,pendingClose:null,flash:null,pinned:Qe(),help:!1,rename:!1,renameBuf:"",renameTarget:null,message:!1,messageBuf:"",messageTarget:null};let p=[],O=new Map,F=!1,m,k=!1;const pe=300,j=3,D=c(()=>{m!==void 0&&clearTimeout(m),m=void 0,O=new Map},"clearContentSearch"),Y=c(async()=>{if(k||!e.search)return;const r=e.query.trim();if(r.length<j)return;const t=ae(u,{collapsed:e.collapsed,tab:e.tab,query:"",cwdScope:e.cwdScope,profileScope:e.profileScope,residentsOnly:e.residentsOnly,sort:"recency",pinned:e.pinned}).map(n=>n.id);if(t.length===0){O=new Map,f(),i();return}k=!0,F=!0,i();let s,o;try{const n=await a.contentSearch({query:r,nodes:t,types:["transcript"],weigh_body:!0,per_node:!0});s=new Map;for(const d of n.hits){const w=d.node_id,b=d.snippet;typeof w=="string"&&typeof b=="string"&&s.set(w,b)}}catch(n){o=`conversation search failed: ${n instanceof Error?n.message:String(n)}`}if(k=!1,F=!1,!g){if(e.query.trim()!==r){e.search&&Y();return}s!==void 0?O=s:e.flash=o??null,f(),i()}},"runContentSearch"),G=c(()=>{if(m!==void 0&&clearTimeout(m),m=void 0,e.query.trim().length<j){O=new Map;return}m=setTimeout(()=>{m=void 0,Y()},pe)},"scheduleContentSearch"),C=c(r=>u.nodes.get(r)?.row,"rowOf"),we=sr();let g=!1;const q=c(()=>{if(!g){g=!0;try{Ue()}catch{}}},"cleanup");process.once("exit",q);const A=c((r,t)=>{process.stderr.write(`[crtr browse] ${r}: ${String(t?.message??t)}
3
+ `)},"logAsyncFailure"),he=c(r=>{try{I("crtr",["node","lifecycle","revive",r,"--now"],{stdio:"ignore"})}catch{}},"reviveNow"),me=c(r=>{q();const t=a.returnPane!==void 0&&a.returnPane!==""?a.returnPane:void 0;try{t!==void 0?I("tmux",["run-shell","-b",`crtr surface node focus ${r} --pane ${t} --in-place >/dev/null 2>&1`],{stdio:"ignore"}):I("crtr",["surface","node","focus",r],{stdio:"inherit"})}catch{}process.exit(0)},"selectAndFocus"),ge=c(r=>{const t=["surface","node","focus",r,...a.returnPane!==void 0&&a.returnPane!==""?["--pane",a.returnPane]:[]];try{I("crtr",t,{stdio:"ignore"})}catch{}},"selectAndFocusKeepOpen"),f=c(r=>{if(p=ae(u,{collapsed:e.collapsed,tab:e.tab,query:e.query,cwdScope:e.cwdScope,profileScope:e.profileScope,residentsOnly:e.residentsOnly,sort:e.sort,pinned:e.pinned,contentHits:O}),r!==void 0){const t=p.findIndex(s=>s.id===r);t>=0&&(e.cursor=t)}e.cursor>p.length-1&&(e.cursor=Math.max(0,p.length-1)),e.cursor<0&&(e.cursor=0)},"recompute"),be=c(()=>{if(e.query==="")return;const r=p[e.cursor],t=r===void 0?void 0:O.get(r.id),s=r===void 0?void 0:C(r.id);if(!(t===void 0||s===void 0||Ge(e.query,s).tier>0))return t},"selectedContentSnippet"),Q=c(async()=>{const r=Ve(),t=e.preview&&p.length>0,s=e.search||e.rename||e.message,o=ar(r,s,t);e.cursor<e.scrollOffset&&(e.scrollOffset=e.cursor),e.cursor>=e.scrollOffset+o&&(e.scrollOffset=e.cursor-o+1),e.scrollOffset<0&&(e.scrollOffset=0);const n=e.scrollOffset,d=Math.min(p.length,n+o),w=[];for(let $=n;$<d;$++){const se=C(p[$].id);se!==void 0&&w.push(se)}await ie(l,w,_),X(t,fe(r.cols,t).mode==="side");const b=nr({tree:u,visible:p,tab:e.tab,cursor:e.cursor,scrollOffset:e.scrollOffset,query:e.query,search:e.search,totalNodes:V,cwdScope:e.cwdScope,profileScope:e.profileScope,sort:e.sort,preview:e.preview,showReport:e.showReport,residentsOnly:e.residentsOnly,pendingClose:e.pendingClose,flash:e.flash,pinned:e.pinned,help:e.help,rename:e.rename,renameBuf:e.renameBuf,message:e.message,messageBuf:e.messageBuf,searchingContent:F,contentSnippet:be(),readOnly:!1},r,we);process.stdout.write(b)},"flush"),ye=90;let M,H=!1,x={on:!1,transcript:!1};const J=c(r=>r.previewLoaded!==!0||x.transcript&&r.transcriptLoaded!==!0||e.search&&e.query!==""&&r.promptsLoaded!==!0,"previewPending"),X=c((r,t)=>{x={on:r,transcript:t};const s=h()?.id,o=s===void 0?void 0:C(s);!r||o===void 0||!J(o)||(M!==void 0&&clearTimeout(M),M=setTimeout(()=>{M=void 0,ve()},ye))},"schedulePreview"),ve=c(async()=>{if(H||g)return;const r=h()?.id,t=r===void 0?void 0:C(r);if(!(!x.on||t===void 0||!J(t))){H=!0;try{await Le(l,t,{transcript:x.transcript}),e.search&&e.query!==""&&await ce(l,t)}catch(s){A("preview load failed",s),t.previewLoaded=!0,t.transcriptLoaded=!0}finally{H=!1}g||(h()?.id===r?i():X(x.on,x.transcript))}},"runPreview"),N=c(()=>{q(),process.exit(0)},"quit"),h=c(()=>p[e.cursor],"curRow"),K=c(r=>!e.collapsed.has(r),"isExpanded"),Se=c(r=>{const t=[],s=[r],o=new Set;for(;s.length>0;){const n=s.pop();if(!o.has(n)){o.add(n),t.push(n);for(const d of u.nodes.get(n)?.childIds??[])s.push(d)}}return t},"subtreeIds"),Oe=c(r=>Se(r).some(t=>u.nodes.get(t)?.row.streaming===!0),"anyStreaming"),Z=c(async r=>{e.pendingClose=null;let t=[];try{t=(await a.closeNode(r)).closed}catch{return}for(const s of t){const o=await l.getNode(s);if(o===null){Ye(u,s);continue}const n=u.nodes.get(s);n!==void 0&&(n.row.status=o.status,n.row.streaming=!1,n.row.viewed=!1,n.row.hanging=null,n.row.asks=0)}f()},"doClose"),ee=c(r=>{const t=T.indexOf(e.tab);e.tab=T[(t+r+T.length)%T.length],e.cursor=0,e.scrollOffset=0,f()},"cycleTab"),Ce=c(()=>{const r=h()?.id,t=z.indexOf(e.sort);e.sort=z[(t+1)%z.length],f(r)},"cycleSort"),xe=c(()=>{const r=h()?.id;e.cwdScope=e.cwdScope===null?ue:null,f(r)},"toggleScope"),Ie=c(()=>{if(E===void 0)return;const r=h()?.id;e.profileScope=e.profileScope===void 0?E:void 0,f(r)},"toggleProfile"),Te=c(()=>{const r=h()?.id;e.residentsOnly=!e.residentsOnly,f(r)},"toggleResidents");let L=!1,W=!1,P=!1;const i=c(()=>{P=!0,!W&&(W=!0,(async()=>{try{for(;P&&!g;)P=!1,await Q()}finally{W=!1,P&&!g&&i()}})().catch(()=>{q(),process.exit(1)}))},"scheduleFlush"),re=c(()=>{L||(L=!0,setImmediate(()=>{L=!1,f(),i()}))},"scheduleSearchRepaint"),qe=c((r,t)=>{const s=tr(y,{input:r,...t});if(s==="crtr.browse.search.cancel"){e.search=!1,e.query="",e.sort="tree",D(),f(),i();return}if(s==="crtr.browse.search.commit"){e.search=!1,f();const n=p.findIndex(d=>d.matched);n>=0&&(e.cursor=n),i();return}if(s==="crtr.browse.search.delete"){e.query=e.query.slice(0,-1),G(),re();return}if(s==="crtr.browse.search.quit"&&N(),t.ctrl)return;const o=[...r].filter(n=>n>=" ").join("");o.length>0&&(e.query+=o,G(),re())},"onKeySearch"),Ne=c(()=>{const r=oe();if(!(r===void 0||r===""||!u.nodes.has(r))){for(let t=u.nodes.get(r)?.parentId??null;t!==null;t=u.nodes.get(t)?.parentId??null)e.collapsed.delete(t);f(r)}},"recenterOnSelf"),Re=c(r=>{e.pinned.has(r)?e.pinned.delete(r):e.pinned.add(r),Je(e.pinned),f(r)},"togglePin"),Me=c(()=>{const r=e.renameTarget,t=e.renameBuf.trim();if(e.rename=!1,r!==null&&t!==""){try{I("crtr",["node","config",r,"--name",t],{stdio:"ignore"})}catch{}const s=u.nodes.get(r);s!==void 0&&(s.row.handle=t,s.row.name=$e({name:t,kind:s.row.kind,description:s.row.description}))}f(e.renameTarget??void 0),i()},"commitRename"),Pe=c(()=>{const r=e.messageTarget,t=e.messageBuf.trim();if(e.message=!1,r!==null&&t!=="")try{I("crtr",["node","message","send","--to",r,t],{stdio:"ignore"})}catch{}i()},"commitMessage"),te=c((r,t,s,o,n,d)=>{if(t.escape){d();return}if(t.return){n();return}if(t.backspace){o(s().slice(0,-1)),i();return}if(t.ctrl){r==="c"&&N();return}const w=[...r].filter(b=>b>=" ").join("");w.length>0&&(o(s()+w),i())},"onKeyText"),Be=c((r,t)=>te(r,t,()=>e.renameBuf,s=>{e.renameBuf=s},Me,()=>{e.rename=!1,i()}),"onKeyRename"),_e=c((r,t)=>te(r,t,()=>e.messageBuf,s=>{e.messageBuf=s},Pe,()=>{e.message=!1,i()}),"onKeyMessage"),Ee=c((r,t)=>{if(t.ctrl&&r==="c"){N();return}e.help=!1,i()},"onKeyHelp"),Fe=c((r,t)=>{const s={input:r,...t},o=rr(y,s);if(e.flash=null,t.ctrl&&o===null)return;if(e.pendingClose!==null&&o==="crtr.browse.quit"&&t.ctrl&&N(),e.pendingClose!==null){if(de(y,"crtr.browse.confirm-close",s)){const d=e.pendingClose;Z(d).then(()=>i()).catch(()=>i());return}e.pendingClose=null,i();return}const n=h();if(o==="crtr.browse.quit"&&N(),o==="crtr.browse.up"){e.cursor=Math.max(0,e.cursor-1),i();return}if(o==="crtr.browse.down"){e.cursor=Math.max(0,Math.min(p.length-1,e.cursor+1)),i();return}if(o==="crtr.browse.first"){e.cursor=0,i();return}if(o==="crtr.browse.last"){e.cursor=Math.max(0,p.length-1),i();return}if(o==="crtr.browse.expand-or-child"){n!==void 0&&n.hasChildren&&(K(n.id)?e.cursor+1<p.length&&p[e.cursor+1].depth>n.depth&&(e.cursor+=1):(e.collapsed.delete(n.id),f(n.id))),i();return}if(o==="crtr.browse.collapse-or-parent"){if(n!==void 0&&n.hasChildren&&K(n.id))e.collapsed.add(n.id),f(n.id);else if(n!==void 0){const d=u.nodes.get(n.id)?.parentId??null;if(d!==null){const w=p.findIndex(b=>b.id===d);w>=0&&(e.cursor=w)}}i();return}if(o==="crtr.browse.toggle-fold"){n!==void 0&&n.hasChildren&&(K(n.id)?e.collapsed.add(n.id):e.collapsed.delete(n.id),f(n.id)),i();return}if(o==="crtr.browse.tab-next"){ee(1),i();return}if(o==="crtr.browse.tab-previous"){ee(-1),i();return}if(o?.startsWith("crtr.browse.tab.")){const d=Number(o.slice(-1))-1;d<T.length&&(e.tab=T[d],e.cursor=0,e.scrollOffset=0,f()),i();return}if(o==="crtr.browse.close-subtree"){if(n===void 0){i();return}if(Oe(n.id)){e.pendingClose=n.id,i();return}Z(n.id).then(()=>i()).catch(()=>i());return}if(o==="crtr.browse.sort"){Ce(),i();return}if(o==="crtr.browse.scope"){xe(),i();return}if(o==="crtr.browse.profile"){Ie(),i();return}if(o==="crtr.browse.residents"){Te(),i();return}if(o==="crtr.browse.preview"){e.preview=!e.preview,i();return}if(o==="crtr.browse.report"){e.showReport=!e.showReport,i();return}if(r==="?"){e.help=!0,i();return}if(o==="crtr.browse.pin"){n!==void 0&&Re(n.id),i();return}if(r==="."){Ne(),i();return}if(r==="R"){n!==void 0&&(e.rename=!0,e.renameTarget=n.id,e.renameBuf=C(n.id)?.name??""),i();return}if(r==="m"){n!==void 0&&(e.message=!0,e.messageTarget=n.id,e.messageBuf=""),i();return}if(o==="crtr.browse.search"){ke(),e.search=!0,e.query="",D(),e.sort="relevance",e.cursor=0,e.scrollOffset=0,f(),i();return}if(o==="crtr.browse.revive-now"){n!==void 0&&C(n.id)?.hanging!=null&&he(n.id),i();return}if(o==="crtr.browse.yank-id"){if(n!==void 0){const{system:d,tmux:w}=cr(n.id);e.flash=d||w?`Copied ${n.id}${d?"":" to the tmux buffer"}`:"No clipboard tool available \u2014 nothing copied"}i();return}if(o==="crtr.browse.focus"){n!==void 0&&me(n.id);return}if(o==="crtr.browse.focus-background"){n!==void 0&&ge(n.id),i();return}},"onKeyNav");f(),p.length===0&&e.residentsOnly&&(e.residentsOnly=!1,f());const R=oe();if(R!==void 0&&R!==""&&u.nodes.has(R)){for(let r=u.nodes.get(R)?.parentId??null;r!==null;r=u.nodes.get(r)?.parentId??null)e.collapsed.delete(r);f(R)}ze(),await Q();let ne=!1;const ke=c(()=>{if(ne)return;ne=!0;const r=[...u.nodes.values()].map(n=>n.row);let t=0;const s=30,o=c(async()=>{if(g)return;const n=Math.min(t+s,r.length),d=r.slice(t,n);await ie(l,d,_),await Promise.all(d.map(w=>ce(l,w))),t=n,e.search&&e.query!==""&&(f(h()?.id),i()),t<r.length&&setImmediate(()=>{o().catch(w=>A("background warm failed",w))})},"warmChunk");setImmediate(()=>{o().catch(n=>A("background warm failed",n))})},"startWarm");await new Promise(()=>{const r=c(t=>{try{const{input:s,key:o}=je(t);e.help?Ee(s,o):e.search?qe(s,o):e.rename?Be(s,o):e.message?_e(s,o):Fe(s,o)}catch{q(),process.exit(1)}},"onData");process.stdin.on("data",r),process.stdout.on("resize",i)})}c(Or,"runBrowse");export{rr as resolveBrowseNavAction,tr as resolveBrowseSearchAction,Or as runBrowse};
@@ -81,7 +81,7 @@ export declare function previewSnippet(query: string, text: string, width: numbe
81
81
  export declare function promptText(row: DashboardRow): string;
82
82
  /** Does this row match the live query? Empty query matches everything; a
83
83
  * non-empty query uses the same fields and match tiers as relevance ranking. */
84
- export declare function queryMatch(query: string, row: DashboardRow): boolean;
84
+ export declare function queryMatch(query: string, row: DashboardRow, contentHits?: ReadonlyMap<string, string>): boolean;
85
85
  export type MatchTier = 0 | 1 | 2 | 3;
86
86
  export interface FieldMatch {
87
87
  /** 3 = bounded word/phrase, 2 = contiguous partial, 1 = subsequence. */
@@ -148,6 +148,10 @@ export interface FlattenOpts {
148
148
  /** User-starred node ids (local, view-only). Gates the Pinned tab, and floats
149
149
  * pinned rows to the top of every FLAT sort mode. undefined = none pinned. */
150
150
  pinned?: Set<string>;
151
+ /** Conversation-content search hits for the live query: node id → matching
152
+ * snippet. A row in this map matches the query even when its name and prompts
153
+ * do not; such a row scores tier 0, so it ranks below every name/prompt match. */
154
+ contentHits?: ReadonlyMap<string, string>;
151
155
  }
152
156
  /** Is this row inside the active cwd scope? No scope (null/undefined) = All dirs. */
153
157
  export declare function cwdMatch(scope: string | null | undefined, row: DashboardRow): boolean;
@@ -1 +1 @@
1
- var W=Object.defineProperty;var r=(t,e)=>W(t,"name",{value:e,configurable:!0});import{isFinishedWork as z,isLiveStatus as V,statusRank as Y}from"../node-order.js";import{matchIndices as j}from"../../tui/fuzzy.js";const dt=["All","Live","Dormant","Attention","Pinned"],at=["tree","attention","relevance","recency"];function q(t,e,o=!1){switch(t){case"All":return!0;case"Live":return V(e.status)||!z({lifecycle:e.lifecycle,finalized:e.finalized===!0});case"Dormant":return e.status==="done"||e.status==="dead"||e.status==="canceled";case"Attention":return e.asks>0||e.hanging!=null;case"Pinned":return o}}r(q,"tabPredicate");function lt(t,e,o){const c=new Map(t.map(n=>[n.node_id,n])),i=new Map,s=[],d=new Set,f=r(n=>{const g=c.get(n);return g!==void 0?Y(g.status):99},"rankOf"),u=r((n,g,p)=>{if(d.has(n))return;const M=c.get(n);if(M===void 0)return;d.add(n);const I=o(n).filter(S=>c.has(S)&&!d.has(S));i.set(n,{row:M,depth:g,parentId:p,childIds:I});for(const S of I)u(S,g+1,n)},"walk"),m=[...e].filter(n=>c.has(n)).sort((n,g)=>f(n)-f(g));for(const n of m)d.has(n)||(s.push(n),u(n,0,null));const h=t.map(n=>n.node_id).filter(n=>!d.has(n)).sort((n,g)=>f(n)-f(g));for(const n of h)d.has(n)||(s.push(n),u(n,0,null));return{roots:s,nodes:i}}r(lt,"buildTree");function ft(t,e){const o=t.nodes.get(e);if(o===void 0)return;const c=o.parentId,i=o.childIds.filter(s=>t.nodes.has(s));for(const s of i){const d=t.nodes.get(s);d!==void 0&&(d.parentId=c)}if(c!==null){const s=t.nodes.get(c);if(s!==void 0){const d=s.childIds.indexOf(e);d>=0&&s.childIds.splice(d,1,...i)}}else{const s=t.roots.indexOf(e);s>=0&&t.roots.splice(s,1,...i)}t.nodes.delete(e)}r(ft,"pruneNode");const D=12;function G(t,e){if(t==="")return new Set;const o=e.toLowerCase().indexOf(t.toLowerCase());if(o<0)return new Set;const c=new Set;for(let i=o;i<o+t.length;i++)c.add(i);return c}r(G,"highlightIndices");function K(t,e,o){const c=[];if(e<=0||o<=0)return c;const i=t.length;let s=0;for(;s<i&&c.length<o;){const d=s;let f=s,u=s;for(;u<i;){let h=u;for(;h<i&&t[h]===" ";)h++;let n=h;for(;n<i&&t[n]!==" ";)n++;if(h===n)break;if(n-d<=e||f===d)f=n,u=n;else break}let m=t.slice(d,f);m.length>e&&(m=m.slice(0,e)),c.push({text:m,start:d}),s=f,s<i&&t[s]===" "&&s++}return c}r(K,"wrapTracked");function ut(t,e,o,c){const i=e.replace(/\s+/g," ").trim();if(i===""||o<=0||c<=0)return[];const s=t===""?-1:i.toLowerCase().indexOf(t.toLowerCase());let d=0;if(s>D){let p=s-D;for(;p<s&&i[p]!==" ";)p++;d=p<s?p+1:s}const f=(d>0?"\u2026 ":"")+i.slice(d),u=G(t,f),m=K(f,o,c),h=m.map(({text:p,start:M})=>{const I=new Set;for(const S of u)S>=M&&S<M+p.length&&I.add(S-M);return{text:p,hi:I}}),n=m[m.length-1],g=h[h.length-1];if(n!==void 0&&g!==void 0&&n.start+n.text.length<f.length){const p=Math.max(0,Math.min(g.text.length,o-1)),M=g.text.slice(0,p),I=new Set;for(const S of g.hi)S<p&&I.add(S);h[h.length-1]={text:`${M}\u2026`,hi:I}}return h}r(ut,"previewSnippet");function X(t){return t.slice(0,8)}r(X,"shortId");function $(t){return t.prompts??t.goal??""}r($,"promptText");function J(t,e){return t===""||F(t,e).tier>0}r(J,"queryMatch");const R={tier:0,quality:0},Q=/[\p{L}\p{N}]/u;function A(t){return t!==void 0&&Q.test(t)}r(A,"isWordChar");function U(t,e){if(t===""||e==="")return R;const o=t.toLowerCase(),c=e.toLowerCase(),i=c.indexOf(o);if(i>=0){for(let u=i;u>=0;u=c.indexOf(o,u+1)){const m=u+o.length,h=!A(o[0])||!A(c[u-1]),n=!A(o[o.length-1])||!A(c[m]);if(h&&n)return{tier:3,quality:c===o?1:.95}}const f=Math.min(.2,i/400);return{tier:2,quality:(i===0?1:.8)-f}}const s=[...j(o,c)];if(s.length===0)return R;const d=s[s.length-1]-s[0]+1;return{tier:1,quality:o.length/d}}r(U,"fieldMatch");const T={handle:4,prompt:4,kind:2,description:1.5,id:1};function Z(t){const e=t.handle??t.name;return e===t.kind?"":e}r(Z,"explicitHandle");function F(t,e){if(t==="")return{tier:0,strength:0};const c=[{text:Z(e),weight:T.handle,minimumTier:1},{text:$(e),weight:T.prompt,minimumTier:2},{text:e.kind,weight:T.kind,minimumTier:1},{text:e.description??"",weight:T.description,minimumTier:1},{text:X(e.node_id),weight:T.id,minimumTier:1}].map(({text:d,weight:f,minimumTier:u})=>{const m=U(t,d);return{match:m.tier>=u?m:R,weight:f}}),i=Math.max(...c.map(({match:d})=>d.tier)),s=c.reduce((d,{match:f,weight:u})=>d+(f.tier===i?f.quality*u:0),0);return{tier:i,strength:s}}r(F,"scoreRow");function _(t){const e=t.mtimeMs;if(typeof e=="number"&&Number.isFinite(e))return e;const o=Date.parse(t.created);return Number.isFinite(o)?o:0}r(_,"attentionMtime");const tt=.1,et=10080*60*1e3;function P(t,e){const o=Math.max(0,e-_(t));return 1+tt/(1+o/et)}r(P,"relevanceFreshness");function H(t){const e=t.viewed===!0,o=t.streaming===!0;return e&&o?0:e?1:o?2:t.status==="active"||t.status==="idle"?3:4}r(H,"attentionTier");function nt(t,e){return t==null||e.cwd===t}r(nt,"cwdMatch");function ot(t,e){return t===void 0||e.profile_id===t}r(ot,"profileMatch");function st(t,e){return t!==!0||e.lifecycle!=="terminal"}r(st,"lifecycleMatch");function ht(t,e){const{collapsed:o,tab:c,query:i,cwdScope:s,profileScope:d,residentsOnly:f,sort:u="tree",pinned:m}=e,h=r(a=>m?.has(a)===!0,"isPinned"),n=new Set,g=new Set;for(const[a,w]of t.nodes)q(c,w.row,h(a))&&ot(d,w.row)&&nt(s,w.row)&&J(i,w.row)&&(g.add(a),st(f,w.row)&&n.add(a));if(u!=="tree"){const a=[...n],w=r(l=>t.nodes.get(l)?.row.created??"","createdOf"),k=r((l,x)=>w(x).localeCompare(w(l)),"byRecency"),E=r(l=>h(l)?0:1,"pinRank");if(u==="attention"){const l=r(x=>t.nodes.get(x).row,"rowOf");a.sort((x,O)=>E(x)-E(O)||H(l(x))-H(l(O))||_(l(O))-_(l(x))||k(x,O))}else if(u==="recency"||i==="")a.sort((l,x)=>E(l)-E(x)||k(l,x));else{const l=new Map,x=r(C=>t.nodes.get(C).row,"rowOf"),O=Date.now();for(const C of a)l.set(C,F(i,x(C)));a.sort((C,L)=>{const y=l.get(C),N=l.get(L),b=y.strength*P(x(C),O),B=N.strength*P(x(L),O);return E(C)-E(L)||N.tier-y.tier||B-b||_(x(L))-_(x(C))||k(C,L)})}return a.map(l=>({id:l,depth:0,hasChildren:!1,collapsed:!1,matched:!0}))}const p=new Set;for(const a of n){let w=t.nodes.get(a)?.parentId??null;for(;w!==null&&!p.has(w);)p.add(w),w=t.nodes.get(w)?.parentId??null}const M=r(a=>n.has(a)||p.has(a),"included"),I=r(a=>i!==""&&p.has(a)?!0:!o.has(a),"isExpanded"),S=[],v=r((a,w)=>{const k=t.nodes.get(a);if(k===void 0)return;const E=M(a)||f===!0&&w&&g.has(a);if(E){const l=k.childIds.length>0;S.push({id:a,depth:k.depth,hasChildren:l,collapsed:l&&!I(a),matched:n.has(a)})}if(I(a))for(const l of k.childIds)v(l,E)},"walk");for(const a of t.roots)v(a,!1);return S}r(ht,"flatten");export{at as SORTS,dt as TABS,_ as attentionMtime,H as attentionTier,lt as buildTree,nt as cwdMatch,U as fieldMatch,ht as flatten,G as highlightIndices,st as lifecycleMatch,ut as previewSnippet,ot as profileMatch,$ as promptText,ft as pruneNode,J as queryMatch,P as relevanceFreshness,F as scoreRow,q as tabPredicate};
1
+ var z=Object.defineProperty;var r=(t,e)=>z(t,"name",{value:e,configurable:!0});import{isFinishedWork as V,isLiveStatus as Y,statusRank as j}from"../node-order.js";import{matchIndices as q}from"../../tui/fuzzy.js";const at=["All","Live","Dormant","Attention","Pinned"],lt=["tree","attention","relevance","recency"];function G(t,e,o=!1){switch(t){case"All":return!0;case"Live":return Y(e.status)||!V({lifecycle:e.lifecycle,finalized:e.finalized===!0});case"Dormant":return e.status==="done"||e.status==="dead"||e.status==="canceled";case"Attention":return e.asks>0||e.hanging!=null;case"Pinned":return o}}r(G,"tabPredicate");function ft(t,e,o){const c=new Map(t.map(n=>[n.node_id,n])),i=new Map,s=[],d=new Set,f=r(n=>{const h=c.get(n);return h!==void 0?j(h.status):99},"rankOf"),u=r((n,h,x)=>{if(d.has(n))return;const I=c.get(n);if(I===void 0)return;d.add(n);const M=o(n).filter(S=>c.has(S)&&!d.has(S));i.set(n,{row:I,depth:h,parentId:x,childIds:M});for(const S of M)u(S,h+1,n)},"walk"),p=[...e].filter(n=>c.has(n)).sort((n,h)=>f(n)-f(h));for(const n of p)d.has(n)||(s.push(n),u(n,0,null));const m=t.map(n=>n.node_id).filter(n=>!d.has(n)).sort((n,h)=>f(n)-f(h));for(const n of m)d.has(n)||(s.push(n),u(n,0,null));return{roots:s,nodes:i}}r(ft,"buildTree");function ut(t,e){const o=t.nodes.get(e);if(o===void 0)return;const c=o.parentId,i=o.childIds.filter(s=>t.nodes.has(s));for(const s of i){const d=t.nodes.get(s);d!==void 0&&(d.parentId=c)}if(c!==null){const s=t.nodes.get(c);if(s!==void 0){const d=s.childIds.indexOf(e);d>=0&&s.childIds.splice(d,1,...i)}}else{const s=t.roots.indexOf(e);s>=0&&t.roots.splice(s,1,...i)}t.nodes.delete(e)}r(ut,"pruneNode");const F=12;function K(t,e){if(t==="")return new Set;const o=e.toLowerCase().indexOf(t.toLowerCase());if(o<0)return new Set;const c=new Set;for(let i=o;i<o+t.length;i++)c.add(i);return c}r(K,"highlightIndices");function X(t,e,o){const c=[];if(e<=0||o<=0)return c;const i=t.length;let s=0;for(;s<i&&c.length<o;){const d=s;let f=s,u=s;for(;u<i;){let m=u;for(;m<i&&t[m]===" ";)m++;let n=m;for(;n<i&&t[n]!==" ";)n++;if(m===n)break;if(n-d<=e||f===d)f=n,u=n;else break}let p=t.slice(d,f);p.length>e&&(p=p.slice(0,e)),c.push({text:p,start:d}),s=f,s<i&&t[s]===" "&&s++}return c}r(X,"wrapTracked");function ht(t,e,o,c){const i=e.replace(/\s+/g," ").trim();if(i===""||o<=0||c<=0)return[];const s=t===""?-1:i.toLowerCase().indexOf(t.toLowerCase());let d=0;if(s>F){let x=s-F;for(;x<s&&i[x]!==" ";)x++;d=x<s?x+1:s}const f=(d>0?"\u2026 ":"")+i.slice(d),u=K(t,f),p=X(f,o,c),m=p.map(({text:x,start:I})=>{const M=new Set;for(const S of u)S>=I&&S<I+x.length&&M.add(S-I);return{text:x,hi:M}}),n=p[p.length-1],h=m[m.length-1];if(n!==void 0&&h!==void 0&&n.start+n.text.length<f.length){const x=Math.max(0,Math.min(h.text.length,o-1)),I=h.text.slice(0,x),M=new Set;for(const S of h.hi)S<x&&M.add(S);m[m.length-1]={text:`${I}\u2026`,hi:M}}return m}r(ht,"previewSnippet");function $(t){return t.slice(0,8)}r($,"shortId");function J(t){return t.prompts??t.goal??""}r(J,"promptText");function Q(t,e,o){return t===""||H(t,e).tier>0||o?.has(e.node_id)===!0}r(Q,"queryMatch");const R={tier:0,quality:0},U=/[\p{L}\p{N}]/u;function A(t){return t!==void 0&&U.test(t)}r(A,"isWordChar");function Z(t,e){if(t===""||e==="")return R;const o=t.toLowerCase(),c=e.toLowerCase(),i=c.indexOf(o);if(i>=0){for(let u=i;u>=0;u=c.indexOf(o,u+1)){const p=u+o.length,m=!A(o[0])||!A(c[u-1]),n=!A(o[o.length-1])||!A(c[p]);if(m&&n)return{tier:3,quality:c===o?1:.95}}const f=Math.min(.2,i/400);return{tier:2,quality:(i===0?1:.8)-f}}const s=[...q(o,c)];if(s.length===0)return R;const d=s[s.length-1]-s[0]+1;return{tier:1,quality:o.length/d}}r(Z,"fieldMatch");const L={handle:4,prompt:4,kind:2,description:1.5,id:1};function tt(t){const e=t.handle??t.name;return e===t.kind?"":e}r(tt,"explicitHandle");function H(t,e){if(t==="")return{tier:0,strength:0};const c=[{text:tt(e),weight:L.handle,minimumTier:1},{text:J(e),weight:L.prompt,minimumTier:2},{text:e.kind,weight:L.kind,minimumTier:1},{text:e.description??"",weight:L.description,minimumTier:1},{text:$(e.node_id),weight:L.id,minimumTier:1}].map(({text:d,weight:f,minimumTier:u})=>{const p=Z(t,d);return{match:p.tier>=u?p:R,weight:f}}),i=Math.max(...c.map(({match:d})=>d.tier)),s=c.reduce((d,{match:f,weight:u})=>d+(f.tier===i?f.quality*u:0),0);return{tier:i,strength:s}}r(H,"scoreRow");function T(t){const e=t.mtimeMs;if(typeof e=="number"&&Number.isFinite(e))return e;const o=Date.parse(t.created);return Number.isFinite(o)?o:0}r(T,"attentionMtime");const et=.1,nt=10080*60*1e3;function P(t,e){const o=Math.max(0,e-T(t));return 1+et/(1+o/nt)}r(P,"relevanceFreshness");function b(t){const e=t.viewed===!0,o=t.streaming===!0;return e&&o?0:e?1:o?2:t.status==="active"||t.status==="idle"?3:4}r(b,"attentionTier");function ot(t,e){return t==null||e.cwd===t}r(ot,"cwdMatch");function st(t,e){return t===void 0||e.profile_id===t}r(st,"profileMatch");function it(t,e){return t!==!0||e.lifecycle!=="terminal"}r(it,"lifecycleMatch");function pt(t,e){const{collapsed:o,tab:c,query:i,cwdScope:s,profileScope:d,residentsOnly:f,sort:u="tree",pinned:p,contentHits:m}=e,n=r(a=>p?.has(a)===!0,"isPinned"),h=new Set,x=new Set;for(const[a,w]of t.nodes)G(c,w.row,n(a))&&st(d,w.row)&&ot(s,w.row)&&Q(i,w.row,m)&&(x.add(a),it(f,w.row)&&h.add(a));if(u!=="tree"){const a=[...h],w=r(l=>t.nodes.get(l)?.row.created??"","createdOf"),k=r((l,g)=>w(g).localeCompare(w(l)),"byRecency"),E=r(l=>n(l)?0:1,"pinRank");if(u==="attention"){const l=r(g=>t.nodes.get(g).row,"rowOf");a.sort((g,O)=>E(g)-E(O)||b(l(g))-b(l(O))||T(l(O))-T(l(g))||k(g,O))}else if(u==="recency"||i==="")a.sort((l,g)=>E(l)-E(g)||k(l,g));else{const l=new Map,g=r(C=>t.nodes.get(C).row,"rowOf"),O=Date.now();for(const C of a)l.set(C,H(i,g(C)));a.sort((C,_)=>{const N=l.get(C),D=l.get(_),B=N.strength*P(g(C),O),W=D.strength*P(g(_),O);return E(C)-E(_)||D.tier-N.tier||W-B||T(g(_))-T(g(C))||k(C,_)})}return a.map(l=>({id:l,depth:0,hasChildren:!1,collapsed:!1,matched:!0}))}const I=new Set;for(const a of h){let w=t.nodes.get(a)?.parentId??null;for(;w!==null&&!I.has(w);)I.add(w),w=t.nodes.get(w)?.parentId??null}const M=r(a=>h.has(a)||I.has(a),"included"),S=r(a=>i!==""&&I.has(a)?!0:!o.has(a),"isExpanded"),v=[],y=r((a,w)=>{const k=t.nodes.get(a);if(k===void 0)return;const E=M(a)||f===!0&&w&&x.has(a);if(E){const l=k.childIds.length>0;v.push({id:a,depth:k.depth,hasChildren:l,collapsed:l&&!S(a),matched:h.has(a)})}if(S(a))for(const l of k.childIds)y(l,E)},"walk");for(const a of t.roots)y(a,!1);return v}r(pt,"flatten");export{lt as SORTS,at as TABS,T as attentionMtime,b as attentionTier,ft as buildTree,ot as cwdMatch,Z as fieldMatch,pt as flatten,K as highlightIndices,it as lifecycleMatch,ht as previewSnippet,st as profileMatch,J as promptText,ut as pruneNode,Q as queryMatch,P as relevanceFreshness,H as scoreRow,G as tabPredicate};
@@ -40,6 +40,11 @@ export interface RenderState {
40
40
  * briefing. Ignored while a search query is live (the transcript owns the panel)
41
41
  * and for a node that has pushed nothing. */
42
42
  showReport?: boolean;
43
+ /** The conversation-content search snippet for the selected row, set only when
44
+ * the row matched on content and nothing in its name or prompts. */
45
+ contentSnippet?: string;
46
+ /** A conversation-content search is in flight: footer shows it. */
47
+ searchingContent?: boolean;
43
48
  /** Lifecycle filter: when true, `terminal` (one-shot worker) nodes are hidden
44
49
  * — surfaced as a status-line cue. */
45
50
  residentsOnly: boolean;
@@ -1,3 +1,3 @@
1
- var Nt=Object.defineProperty;var f=(t,n)=>Nt(t,"name",{value:n,configurable:!0});import{STATUS_GLYPH as rt,STATUS_COLOR as It,isFrozenRow as J,resolveNodeVisual as X,faultSummary as lt}from"../status-glyph.js";import{TABS as Pt,promptText as Ht,previewSnippet as q}from"./model.js";import{matchIndices as Gt}from"../../tui/fuzzy.js";import{clip as _,assemble as w,detectColorCaps as Dt}from"../../tui/draw.js";import{renderMarkdownSpans as Q}from"../../tui/markdown.js";const Bt=2,Ft=2,ct=8,it=ct+2,Ut=100,ut=46,jt=110,Yt=.55,at=40,dt=" \u2502 ";function Vt(t,n){const e=Math.max(20,t)-1;if(!n)return{mode:"none",panelW:0,rowsW:e};if(t<Ut)return{mode:"bottom",panelW:0,rowsW:e};const s=[...dt].length;let r=Math.min(jt,Math.max(ut,Math.round(e*Yt)));return e-r-s<at&&(r=e-s-at),r<ut?{mode:"bottom",panelW:0,rowsW:e}:{mode:"side",panelW:r,rowsW:e-r-s}}f(Vt,"previewLayout");function Me(t){return 5+(t?1:0)}f(Me,"headerHeight");const ft=10,$t=22,ht=6,pt=5,gt=16,W=" ",M="\x1B[",$=`${M}0m`,U=`${M}7m`,y=`${M}2m`,C=`${M}1m`,Kt=`${M}48;5;236m`,mt="33",zt="31",H="36",N="90",xt="93",I="96",bt="35";function Z(t){return t<=0?"0k":`${Math.floor(t/1e3)}k`}f(Z,"fmtCtx");function yt(t){if(t>=1e5)return zt;if(t>=5e4)return mt}f(yt,"ctxColorCode");function G(t,n){const e=Date.parse(t);if(Number.isNaN(e))return"";const s=Math.max(0,Math.floor((n-e)/1e3));if(s<60)return`${s}s`;const r=Math.floor(s/60);if(r<60)return`${r}m`;const o=Math.floor(r/60);if(o<24)return`${o}h`;const u=Math.floor(o/24);if(u<7)return`${u}d`;const i=Math.floor(u/7);return i<5?`${i}w`:`${Math.floor(u/30)}mo`}f(G,"relAge");function j(t){const n=t.replace(/\/+$/,"").split("/");return n[n.length-1]||t}f(j,"baseDir");const vt=new WeakMap;function Jt(t){const n=vt.get(t);if(n!==void 0)return n;const e={active:0,idle:0,done:0,dead:0,canceled:0};for(const r of t.nodes.values())e[r.row.status]++;const s=[];for(const r of["active","idle","done","dead","canceled"])e[r]>0&&s.push({status:r,count:e[r]});return vt.set(t,s),s}f(Jt,"statusCounts");function Xt(t,n){return Pt.map(e=>e===t?n?`${C}${M}${H}m[ ${e} ]${$}`:`${U}[ ${e} ]${$}`:n?`${y} ${e} ${$}`:` ${e} `).join("")}f(Xt,"tabBar");const qt=new Set;function Qt(t,n,e){const s=n===""?qt:Gt(n,t);if(s.size===0)return[{text:t,style:{dim:e.dim,bold:e.bold}}];const r=[];let o="",u=!1;const i=f(()=>{o!==""&&(u?r.push({text:o,style:{fg:I,bold:!0}}):r.push({text:o,style:{dim:e.dim,bold:e.bold}}),o="")},"flush");for(let l=0;l<t.length;l++){const c=s.has(l);c!==u&&(i(),u=c),o+=t[l]}return i(),r}f(Qt,"nameSpans");function L(t){let n=0;for(const e of t)n+=[...e.text].length;return n}f(L,"spansWidth");function Zt(t){const n=X(t.status,{streaming:t.streaming===!0,frozen:J(t),hanging:t.hanging??null}),e=n.word??{active:"active",idle:"idle",done:"done",dead:"dead",canceled:"cancel"}[t.status]??t.status,s=n.color,r=`${n.glyph} ${e}`,o=Math.max(1,ft-[...r].length);return[{text:r,style:{fg:s,bold:n.bold===!0}},{text:" ".repeat(o)}]}f(Zt,"statusRail");function te(t,n,e){const s=yt(t.ctx_tokens),r=[{text:Et(`${t.kind}/${t.mode}`,$t),style:{fg:N}},{text:W},{text:Z(t.ctx_tokens).padStart(ht),style:{fg:s,dim:s===void 0}},{text:W},{text:G(t.created,n).padStart(pt),style:{dim:!0}}];return e&&r.push({text:W},{text:Et(`~${j(t.cwd)}`,gt),style:{fg:N}}),r}f(te,"metaCluster");function Et(t,n){return _(t,n).padEnd(n)}f(Et,"clipPad");function tt(t,n,e,s,r,o){const u=L(t),i=L(n);if(n.length===0||u+i+1>e)return w(t,e,s,r,o);const l=e-u-i;return w([...t,{text:" ".repeat(l)},...n],e,s,r,o)}f(tt,"assembleRow");function ee(t,n,e,s,r,o,u,i,l,c=!0,h=!1){const a=n.nodes.get(t.id);if(a===void 0)return"";const d=a.row,b=" ".repeat(t.depth),m=t.hasChildren?t.collapsed?"\u25B8":"\u25BE":" ",E=d.status==="done"||d.status==="dead"||d.status==="canceled",v=d.viewed===!0?{text:"\uF06E ",style:{fg:I,bold:!0}}:{text:" "},p=[];d.asks>0&&p.push({text:` \u2691${d.asks}`,style:{fg:xt,bold:!0}}),d.hanging!=null&&p.push({text:` \xB7 ${lt(d.hanging,i)}`,style:{fg:mt,dim:!0}});const x=l?[{text:"\u2605 ",style:{fg:bt,bold:!0}}]:[],T=Zt(d),R=c?te(d,i,u):[],A=`${b}${m} `,D=L([v])+L(T)+[...A].length+L(x)+L(p),B=L(R),K=Math.max(4,e-D-B-1),z={dim:!s&&E,bold:s},O=[v,...T,{text:A,style:{dim:!0}},...x,...Qt(_(d.name,K),r,z),...p];let k="",P=h;return s?(k=o.color256?Kt:U,P=!0):t.matched||(k=y),tt(O,R,e,o.color,k,P)}f(ee,"rowLine");function ne(t,n,e,s=!0,r=!1){const o=[{text:" "},{text:"STATUS".padEnd(ft),style:{dim:!0,bold:!0}},{text:"NAME",style:{dim:!0,bold:!0}}];if(!s)return w(o,t,e.color,"",r);const u=[{text:"KIND/MODE".padEnd($t),style:{dim:!0,bold:!0}},{text:W},{text:"CTX".padStart(ht),style:{dim:!0,bold:!0}},{text:W},{text:"AGE".padStart(pt),style:{dim:!0,bold:!0}}];return n&&u.push({text:W},{text:"PROJECT".padEnd(gt),style:{dim:!0,bold:!0}}),tt(o,u,t,e.color,"",!1)}f(ne,"columnHeaderLine");function oe(t){const n=f(o=>`${y}${o}${$}`,"dim"),e=t.profileScope===void 0?"all profiles":"current",s=t.cwdScope===null?"all dirs":j(t.cwdScope),r=[`${n("profile")} ${e}`,`${n("scope")} ${s}`,`${n("sort")} ${t.sort}`,`${n("lifecycle")} ${t.residentsOnly?"residents":"all"}`];return!t.search&&t.query!==""&&r.push(`${n("filter")} ${t.query}`),r.join(n(" \xB7 "))}f(oe,"statusLine");function Mt(t,n,e,s){if(t.hi.size===0)return w([{text:t.text,style:s}],n,e.color,"",!1);const r=[];let o="",u=!1;const i=f(()=>{o!==""&&(r.push(u?{text:o,style:{fg:I,bold:!0}}:{text:o,style:s}),o="")},"flush");for(let l=0;l<t.text.length;l++){const c=t.hi.has(l);c!==u&&(i(),u=c),o+=t.text[l]}return i(),w(r,n,e.color,"",!1)}f(Mt,"snippetLine");function se(t,n){const e=t?Bt:0,s=n?Ft:0,r=Math.max(2,ct-e-s),o=Math.ceil(r/2);return{recap:e,ask:o,reply:r-o,report:s}}f(se,"previewAllocation");function _t(t,n,e,s,r,o,u){if(s<=0)return[];const i=[],l=o.color?`${y}${t}${$}`:t,c=Math.max(1,r-2);if(u===""){const a=Q(n,c,s,!1);if(a.length===0)i.push(`${l}${y}${_(e,c)}${$}`);else for(const d of a)i.push(`${i.length===0?l:" "}${w(d,c,o.color,"",!1)}`);for(;i.length<s;)i.push("");return i}const h=q(u,n,c,s);if(h.length===0)i.push(`${l}${y}${_(e,c)}${$}`);else for(const a of h)i.push(`${i.length===0?l:" "}${Mt(a,c,o)}`);for(;i.length<s;)i.push("");return i}f(_t,"markedBlock");function Tt(t,n,e,s){const r=`${t.padEnd(6)}`,o=_(n,Math.max(1,e-r.length));return s.color?`${y}${r}${$}${C}${o}${$}`:`${r}${o}`}f(Tt,"recapLine");function re(t,n,e,s,r){const o=[`${y}${"\u2500".repeat(n)}${$}`];if(t===void 0){for(;o.length<it;)o.push("");return o}const u=X(t.status,{streaming:t.streaming===!0,frozen:J(t),hanging:t.hanging??null}),i=e.color?`${M}${u.color}m${u.glyph}${$}`:u.glyph,l=[`${t.status} ${t.kind}/${t.mode}`,j(t.cwd),G(t.created,s),`ctx ${Z(t.ctx_tokens)}`];t.asks>0&&l.push(`\u2691${t.asks}`),t.hanging!=null&&l.push(lt(t.hanging,s)),t.streaming===!0&&l.push("\u27F3"),t.viewed===!0&&l.push("\uF06E viewing");const c=_(l.filter(d=>d!=="").join(" \xB7 "),Math.max(0,n-2));o.push(e.color?`${i} ${y}${c}${$}`:`${i} ${c}`);const h=se(t.recap!==void 0,t.report!==void 0);t.recap!==void 0&&(o.push(Tt("Goal",t.recap.goal===""?"\u2014":t.recap.goal,n,e)),o.push(Tt("State",t.recap.state===""?"\u2014":t.recap.state,n,e)));const a=r!==""?Ht(t):t.goal??"";if(o.push(..._t("\u276F ",a,"(no spawn prompt)",h.ask,n,e,r)),o.push(..._t("\u21A9 ",t.lastAssistant??"","(no reply yet)",h.reply,n,e,"")),t.report!==void 0){const d=G(t.report.ts,s),b=`${d===""?"":`${d} `}${t.report.kind}`,m=t.report.heading===""?t.report.body:t.report.heading,E=e.color?`${y}\u25A3 ${$}`:"\u25A3 ",v=Math.max(1,n-2),p=e.color?`${M}${H}m${b}${$}${y} \xB7 ${$}`:`${b} \xB7 `;o.push(`${E}${p}${_(m,Math.max(1,v-b.length-3))}`);for(let x=1;x<h.report;x++){const T=t.report.heading===""?"":t.report.body,R=q("",T,v,h.report-1)[x-1];o.push(R===void 0?"":` ${y}${_(R.text,v)}${$}`)}}return o}f(re,"previewPanel");function Y(t,n,e,s){const r=s===void 0?"":`${s.text} `,o=Math.max(0,n-4-t.length-r.length),u=s===void 0?"":e.color?`${M}${s.fg}m${s.text}${$}${y} `:`${s.text} `;return`${y}\u2500\u2500 ${C}${t}${$}${y} ${u}${"\u2500".repeat(o)}${$}`}f(Y,"sectionRule");function et(t,n,e,s,r,o,u,i,l,c){if(o<=0)return[];const h=[...t].length,a=Math.max(1,u-h),d=f((m,E)=>w([{text:m,style:E}],h,i.color,"",!0),"mk");if(l===""){const m=Q(s,a,o,!0,c);return m.length===0?r===void 0?[]:[`${d(t,e)}${y}${_(r,a)}${$}`]:m.map((E,v)=>`${d(v===0?t:n,e)}${w(E,a,i.color,"",!1)}`)}const b=q(l,s,a,o);return b.length===0?r===void 0?[]:[`${d(t,e)}${y}${_(r,a)}${$}`]:b.map((m,E)=>`${d(E===0?t:n,e)}${Mt(m,a,i,c)}`)}f(et,"blockLines");const le=400,ce=1,nt=14,wt={fg:I,bold:!0,reverse:!0};function ie(t){let n="";for(const e of t)n+=e.text;return n}f(ie,"spansText");function ue(t,n){if(n==="")return{spans:t,hits:0};const e=ie(t),s=e.toLowerCase(),r=n.toLowerCase();if(s.indexOf(r)<0)return{spans:t,hits:0};const o=new Uint8Array(e.length);let u=0;for(let c=s.indexOf(r);c>=0;c=s.indexOf(r,c+r.length)){u++;for(let h=c;h<c+r.length;h++)o[h]=1}const i=[];let l=0;for(const c of t){let h="",a=o[l]===1;for(let d=0;d<c.text.length;d++){const b=o[l]===1;b!==a&&(h!==""&&i.push({text:h,style:a?wt:c.style}),h="",a=b),h+=c.text[d],l++}h!==""&&i.push({text:h,style:a?wt:c.style})}return{spans:i,hits:u}}f(ue,"highlightSpans");const V="\u258C ";function ot(t,n,e,s,r){if(e<2)return{lines:[],hits:0,truncated:!0};const o=t.role==="user",u=o?{fg:I,bold:!0}:{fg:N},i=Math.max(1,s-V.length),l=f(()=>w([{text:V,style:u}],V.length,r.color,"",!0),"bar"),c=[w([{text:V,style:u},{text:o?"YOU":"AGENT",style:{...u,bold:!0}}],s,r.color,"",!1)],a=Q(t.text,i,le,!1,o?{}:{dim:!0}).map(x=>ue(x,n)),d=a.reduce((x,T)=>x+T.hits,0),b=e-1;let m=0;a.length>b&&d>0&&(m=Math.max(0,a.findIndex(x=>x.hits>0)-ce),m=Math.min(m,a.length-b));const E=a.slice(m,m+b);for(const x of E)c.push(`${l()}${w(x.spans,i,r.color,"",!1)}`);const v=m>0,p=m+E.length<a.length;return v&&E.length>1&&(c[1]=`${l()}${y}\u22EF${$}`),p&&E.length>(v?2:1)&&(c[c.length-1]=`${l()}${y}\u22EF${$}`),{lines:c,hits:d,truncated:v||p}}f(ot,"turnBlock");function Rt(t,n,e){return w([{text:` \u22EF ${t} ${t===1?"turn":"turns"}`,style:{dim:!0}}],n,e.color,"",!1)}f(Rt,"elisionLine");function St(t,n){return n!==""&&t.text.toLowerCase().indexOf(n.toLowerCase())>=0}f(St,"turnMatches");function ae(t,n){return t.reduce((e,s)=>e+(St(s,n)?1:0),0)}f(ae,"transcriptHitTurns");function At(t,n,e,s,r){if(s<=0||t.length===0)return[];const o=n===""?[]:t.map((l,c)=>St(l,n)?c:-1).filter(l=>l>=0);if(o.length>0){const l=[];let c=-1;for(const a of o){const d=l.length===0?0:1,b=s-l.length-d;if(b<3)break;const{lines:m}=ot(t[a],n,Math.min(b,nt),e,r);if(m.length===0)break;const E=c<0?a:a-c-1,v=E>0&&l.length+d+m.length+1<=s;d>0&&l.push(""),v&&l.push(Rt(E,e,r)),l.push(...m),c=a}let h=c;for(let a=c+1;a<t.length;a++){const d=s-l.length-1;if(d<3)break;const{lines:b,truncated:m}=ot(t[a],n,Math.min(d,nt),e,r);if(b.length===0||m)break;l.push("",...b),h=a}return h>=0&&h<t.length-1&&l.length<s&&l.push(Rt(t.length-1-h,e,r)),l.slice(0,s)}const u=[];let i=0;for(let l=t.length-1;l>=0;l--){const c=u.length===0,h=c?0:1,a=s-i-h;if(a<3)break;const{lines:d,truncated:b}=ot(t[l],"",Math.min(a,nt),e,r);if(d.length===0||b&&!c)break;h>0&&u.push([""]),u.push(d),i+=d.length+h}return u.reverse().flat().slice(-s)}f(At,"transcriptLines");function kt(t){return t.transcriptLoaded===!0||t.previewLoaded===!0?`${y}(no conversation yet)${$}`:`${y}(loading\u2026)${$}`}f(kt,"emptyBodyNote");function Ct(t){const n=t.transcript;if(n!==void 0&&n.length>0)return n;const e=[];return t.goal!==void 0&&t.goal!==""&&e.push({role:"user",text:t.goal}),t.lastAssistant!==void 0&&t.lastAssistant!==""&&e.push({role:"assistant",text:t.lastAssistant}),e}f(Ct,"panelTurns");const de=4;function fe(t,n,e,s){const r=yt(t.ctx_tokens),o=f(()=>({text:" \xB7 ",style:{dim:!0}}),"dot"),u=[{text:`${t.kind}/${t.mode}`,style:{fg:N}},o(),{text:`~${j(t.cwd)}`,style:{fg:N}},o(),{text:G(t.created,e),style:{dim:!0}},o(),{text:`ctx ${Z(t.ctx_tokens)}`,style:{fg:r,dim:r===void 0}}];return w(u,n,s.color,"",!1)}f(fe,"panelFactsLine");function $e(t,n,e,s){if(t===void 0)return w([{text:"PREVIEW",style:{dim:!0,bold:!0}}],n,e.color,"",!1);const r=[];s&&r.push({text:"\u2605 ",style:{fg:bt,bold:!0}});const o=t.node_id.slice(0,8);return r.push({text:_(t.name,Math.max(4,n-o.length-2-L(r))),style:{bold:!0}}),tt(r,[{text:o,style:{dim:!0}}],n,e.color,"",!1)}f($e,"panelTitleLine");function he(t,n,e,s,r,o,u){if(e<=0)return[];if(t===void 0)return[`${y}(no selection)${$}`];if(o!==""){const p=Ct(t),x=ae(p,o),T=[Y("TRANSCRIPT",n,s,x>0?{text:`${x} ${x===1?"turn":"turns"}`,fg:I}:{text:"matched on name",fg:N})],R=At(p,o,n,e-1,s);return T.push(...R.length===0?[kt(t)]:R),T.slice(0,e)}const i=t.report,l=f(()=>({fg:X(t.status,{streaming:t.streaming===!0,frozen:J(t),hanging:t.hanging??null}).color}),"accentOf");if(u&&i!==void 0){const p=G(i.ts,r),x=[Y("REPORT",n,s,{text:`${p===""?"":`${p} \xB7 `}${i.kind}`,fg:H})],T=i.heading===""?i.body:i.heading;return x.push(...et("\u25A3 "," ",{fg:H,bold:!0},T,void 0,3,n,s,"",{bold:!0})),i.heading!==""&&i.body!==""&&(x.push(""),x.push(...et(" "," ",void 0,i.body,void 0,e-x.length,n,s,""))),x.slice(0,e)}const c=[fe(t,n,r,s),""],h=l(),a=f((p,x)=>[Y(p,n,s),...et("\u258C ","\u258C ",h,x,void 0,de,n,s,"",{bold:!0})],"briefing"),d=t.recap?.goal??"";d!==""&&c.push(...a("GOAL",d),"");const b=t.recap?.state??"",m=b===""?[]:[...a("STATE",b)],E=m.length===0?0:m.length+1,v=e-c.length-E-1;if(v>=2){c.push(Y("CHAT",n,s));const p=At(Ct(t),"",n,v,s);c.push(...p.length===0?[kt(t)]:p)}if(m.length>0){for(;c.length<e-m.length;)c.push("");c.push(...m)}return c.slice(0,e)}f(he,"sidePanelBody");const pe=[["Move","\u2191/\u2193 j/k","up / down"],["","g / G","top / bottom"],["",".","jump to this node (recenter)"],["Tree","\u2192/\u2190 h/l","expand / collapse"],["","space","toggle fold"],["Tabs","Tab / 1-5","All \xB7 Live \xB7 Dormant \xB7 Attention \xB7 Pinned"],["Find","/","fuzzy search (name \xB7 kind \xB7 id \xB7 prompts)"],["Pin","p","star / unstar \u2014 floats to top, Pinned tab"],["Open","\u23CE o","open conversation (closes navigator)"],["","O","open, keep browsing (background)"],["Act","y","copy the node id to the clipboard"],["","m","message the node (no resume)"],["","R","rename the node"],["","x","close-out node + subtree"],["","K","revive now (a hanging node)"],["View","s","cycle sort (attention/tree/relevance/recency)"],["","c","toggle cwd scope"],["","P","toggle profile scope"],["","r","toggle residents-only"],["","v","toggle preview (goal, chat, state)"],["","V","in the preview: swap chat for the newest report"],["Quit","q / Esc",""]];function ge(t,n){const e=Math.max(20,t.cols),s=Math.max(8,t.rows),r=e-1,o=[];o.push(`${C}Canvas navigator \u2014 keys${$}`),o.push("");const u=f(l=>`${y}${l}${$}`,"dim");for(const[l,c,h]of pe){const a=l.padEnd(6),d=c.padEnd(12),b=` ${u(a)}${C}${d}${$}${h===""?"":` ${u(h)}`}`;o.push(_(b,r))}for(o.push(""),o.push(u(" ? or any key to close"));o.length<s-1;)o.push("");const i=o.map(l=>`${l}${M}K`).join(`\r
2
- `);return`${M}H${i}${M}J`}f(ge,"renderHelp");function _e(t,n,e={color:!1,color256:!1}){if(t.help===!0)return ge(n,e);const s=Math.max(20,n.cols),r=Math.max(8,n.rows),o=s-1,u=Date.now(),i=t.search||t.rename===!0||t.message===!0,l=[],c=`${C}Canvas${$} \u2014 ${t.totalNodes} nodes`,h=`Canvas \u2014 ${t.totalNodes} nodes`.length,a=Jt(t.tree),d=a.map(g=>`${rt[g.status]}${g.count}`).join(" "),b=e.color?a.map(g=>`${M}${It[g.status]}m${rt[g.status]}${$}${y}${g.count}${$}`).join(" "):`${y}${d}${$}`,m=o-h-d.length;if(l.push(m>1?`${c}${" ".repeat(m)}${b}`:c),l.push(Xt(t.tab,e.color)),l.push(oe(t)),i){const g=t.rename===!0?"rename \u25B8":t.message===!0?"msg \u25B8":"/",S=t.rename===!0?t.renameBuf??"":t.message===!0?t.messageBuf??"":t.query,F=e.color?`${M}${H}m${g}${$}`:g;l.push(`${F} ${S}\u258E`)}const E=t.preview&&t.visible.length>0,v=Vt(s,E),p=v.mode==="side",x=p?v.rowsW:o,T=`${y}${dt}${$}`,R=t.visible[t.cursor],A=R!==void 0?t.tree.nodes.get(R.id)?.row:void 0,D=t.cwdScope===null&&!p,B=ne(x,D,e,!p,p);l.push(p?`${B}${T}${$e(A,v.panelW,e,R!==void 0&&t.pinned.has(R.id))}`:B),l.push(p?`${y}${"\u2500".repeat(x)}\u2500\u253C\u2500${"\u2500".repeat(v.panelW)}${$}`:`${y}${"\u2500".repeat(o)}${$}`);const K=l.length,z=v.mode==="bottom"?it:0,O=Math.max(1,r-K-1-z),k=t.scrollOffset,P=Math.min(t.visible.length,k+O),Lt=p?he(A,v.panelW,O,e,u,t.query,t.showReport===!0):[],st=f((g,S)=>p?`${g}${T}${Lt[S]??""}`:g,"compose"),Ot=p?" ".repeat(x):"";if(t.visible.length===0){l.push(`${y} (no nodes match this view)${$}`);for(let g=1;g<O;g++)l.push("")}else{for(let g=k;g<P;g++){const S=t.visible[g];l.push(st(ee(S,t.tree,x,g===t.cursor,t.query,e,D,u,t.pinned.has(S.id),!p,p),g-k))}for(let g=P-k;g<O;g++)l.push(st(Ot,g))}if(v.mode==="bottom")for(const g of re(A,o,e,u,t.query))l.push(g);if(t.pendingClose!==null&&t.pendingClose!==void 0){const g=t.tree.nodes.get(t.pendingClose),F=`\u26A0 ${g!==void 0?g.row.name:t.pendingClose} is \u27F3 \u2014 close it (and its subtree) anyway? y / n`;l.push(e.color?`${C}${M}${xt}m${_(F,o)}${$}`:`${U}${_(F,o)}${$}`)}else if(t.flash!==null&&t.flash!==void 0&&t.flash!=="")l.push(e.color?`${C}${_(t.flash,o)}${$}`:`${U}${_(t.flash,o)}${$}`);else{const g=A?.hanging!=null?"K revive now ":"",S=i?"\u23CE commit Esc cancel \u232B delete":t.readOnly===!0?"\u2191\u2193 move \u2192/\u2190 tree / search p pin v preview Tab tabs ? more q quit \u2014 read-only remote canvas":`${g}${A?.report!==void 0&&p&&t.query===""?t.showReport===!0?"V chat ":"V report ":""}\u2191\u2193 move \u23CE open O bg / search p pin v preview m msg x close ? more q quit`;l.push(`${y}${_(S,o)}${$}`)}const Wt=l.map(g=>`${g}${M}K`).join(`\r
3
- `);return`${M}H${Wt}${M}J`}f(_e,"renderFrame");export{ct as PREVIEW_BODY,it as PREVIEW_HEIGHT,Bt as RECAP_LINES,Ft as REPORT_LINES,Dt as detectColorCaps,Me as headerHeight,ue as highlightSpans,se as previewAllocation,Vt as previewLayout,_e as renderFrame,ae as transcriptHitTurns};
1
+ var Nt=Object.defineProperty;var d=(t,n)=>Nt(t,"name",{value:n,configurable:!0});import{STATUS_GLYPH as lt,STATUS_COLOR as It,isFrozenRow as X,resolveNodeVisual as q,faultSummary as ct}from"../status-glyph.js";import{TABS as Pt,promptText as Ht,previewSnippet as U}from"./model.js";import{matchIndices as Gt}from"../../tui/fuzzy.js";import{clip as T,assemble as R,detectColorCaps as Dt}from"../../tui/draw.js";import{renderMarkdownSpans as Q}from"../../tui/markdown.js";const Bt=2,Ft=2,it=8,ut=it+2,Ut=100,at=46,jt=110,Yt=.55,dt=40,ft=" \u2502 ";function Vt(t,n){const e=Math.max(20,t)-1;if(!n)return{mode:"none",panelW:0,rowsW:e};if(t<Ut)return{mode:"bottom",panelW:0,rowsW:e};const o=[...ft].length;let s=Math.min(jt,Math.max(at,Math.round(e*Yt)));return e-s-o<dt&&(s=e-o-dt),s<at?{mode:"bottom",panelW:0,rowsW:e}:{mode:"side",panelW:s,rowsW:e-s-o}}d(Vt,"previewLayout");function Ee(t){return 5+(t?1:0)}d(Ee,"headerHeight");const $t=10,ht=22,pt=6,gt=5,mt=16,I=" ",_="\x1B[",h=`${_}0m`,j=`${_}7m`,x=`${_}2m`,C=`${_}1m`,Kt=`${_}48;5;236m`,xt="33",zt="31",D="36",P="90",bt="93",H="96",yt="35";function Z(t){return t<=0?"0k":`${Math.floor(t/1e3)}k`}d(Z,"fmtCtx");function vt(t){if(t>=1e5)return zt;if(t>=5e4)return xt}d(vt,"ctxColorCode");function B(t,n){const e=Date.parse(t);if(Number.isNaN(e))return"";const o=Math.max(0,Math.floor((n-e)/1e3));if(o<60)return`${o}s`;const s=Math.floor(o/60);if(s<60)return`${s}m`;const r=Math.floor(s/60);if(r<24)return`${r}h`;const c=Math.floor(r/24);if(c<7)return`${c}d`;const u=Math.floor(c/7);return u<5?`${u}w`:`${Math.floor(c/30)}mo`}d(B,"relAge");function Y(t){const n=t.replace(/\/+$/,"").split("/");return n[n.length-1]||t}d(Y,"baseDir");const Mt=new WeakMap;function Jt(t){const n=Mt.get(t);if(n!==void 0)return n;const e={active:0,idle:0,done:0,dead:0,canceled:0};for(const s of t.nodes.values())e[s.row.status]++;const o=[];for(const s of["active","idle","done","dead","canceled"])e[s]>0&&o.push({status:s,count:e[s]});return Mt.set(t,o),o}d(Jt,"statusCounts");function Xt(t,n){return Pt.map(e=>e===t?n?`${C}${_}${D}m[ ${e} ]${h}`:`${j}[ ${e} ]${h}`:n?`${x} ${e} ${h}`:` ${e} `).join("")}d(Xt,"tabBar");const qt=new Set;function Qt(t,n,e){const o=n===""?qt:Gt(n,t);if(o.size===0)return[{text:t,style:{dim:e.dim,bold:e.bold}}];const s=[];let r="",c=!1;const u=d(()=>{r!==""&&(c?s.push({text:r,style:{fg:H,bold:!0}}):s.push({text:r,style:{dim:e.dim,bold:e.bold}}),r="")},"flush");for(let l=0;l<t.length;l++){const i=o.has(l);i!==c&&(u(),c=i),r+=t[l]}return u(),s}d(Qt,"nameSpans");function L(t){let n=0;for(const e of t)n+=[...e.text].length;return n}d(L,"spansWidth");function Zt(t){const n=q(t.status,{streaming:t.streaming===!0,frozen:X(t),hanging:t.hanging??null}),e=n.word??{active:"active",idle:"idle",done:"done",dead:"dead",canceled:"cancel"}[t.status]??t.status,o=n.color,s=`${n.glyph} ${e}`,r=Math.max(1,$t-[...s].length);return[{text:s,style:{fg:o,bold:n.bold===!0}},{text:" ".repeat(r)}]}d(Zt,"statusRail");function te(t,n,e){const o=vt(t.ctx_tokens),s=[{text:Et(`${t.kind}/${t.mode}`,ht),style:{fg:P}},{text:I},{text:Z(t.ctx_tokens).padStart(pt),style:{fg:o,dim:o===void 0}},{text:I},{text:B(t.created,n).padStart(gt),style:{dim:!0}}];return e&&s.push({text:I},{text:Et(`~${Y(t.cwd)}`,mt),style:{fg:P}}),s}d(te,"metaCluster");function Et(t,n){return T(t,n).padEnd(n)}d(Et,"clipPad");function tt(t,n,e,o,s,r){const c=L(t),u=L(n);if(n.length===0||c+u+1>e)return R(t,e,o,s,r);const l=e-c-u;return R([...t,{text:" ".repeat(l)},...n],e,o,s,r)}d(tt,"assembleRow");function ee(t,n,e,o,s,r,c,u,l,i=!0,$=!1){const a=n.nodes.get(t.id);if(a===void 0)return"";const f=a.row,m=" ".repeat(t.depth),g=t.hasChildren?t.collapsed?"\u25B8":"\u25BE":" ",v=f.status==="done"||f.status==="dead"||f.status==="canceled",E=f.viewed===!0?{text:"\uF06E ",style:{fg:H,bold:!0}}:{text:" "},y=[];f.asks>0&&y.push({text:` \u2691${f.asks}`,style:{fg:bt,bold:!0}}),f.hanging!=null&&y.push({text:` \xB7 ${ct(f.hanging,u)}`,style:{fg:xt,dim:!0}});const b=l?[{text:"\u2605 ",style:{fg:yt,bold:!0}}]:[],M=Zt(f),S=i?te(f,u,c):[],w=`${m}${g} `,O=L([E])+L(M)+[...w].length+L(b)+L(y),W=L(S),z=Math.max(4,e-O-W-1),J={dim:!o&&v,bold:o},N=[E,...M,{text:w,style:{dim:!0}},...b,...Qt(T(f.name,z),s,J),...y];let k="",G=$;return o?(k=r.color256?Kt:j,G=!0):t.matched||(k=x),tt(N,S,e,r.color,k,G)}d(ee,"rowLine");function ne(t,n,e,o=!0,s=!1){const r=[{text:" "},{text:"STATUS".padEnd($t),style:{dim:!0,bold:!0}},{text:"NAME",style:{dim:!0,bold:!0}}];if(!o)return R(r,t,e.color,"",s);const c=[{text:"KIND/MODE".padEnd(ht),style:{dim:!0,bold:!0}},{text:I},{text:"CTX".padStart(pt),style:{dim:!0,bold:!0}},{text:I},{text:"AGE".padStart(gt),style:{dim:!0,bold:!0}}];return n&&c.push({text:I},{text:"PROJECT".padEnd(mt),style:{dim:!0,bold:!0}}),tt(r,c,t,e.color,"",!1)}d(ne,"columnHeaderLine");function oe(t){const n=d(r=>`${x}${r}${h}`,"dim"),e=t.profileScope===void 0?"all profiles":"current",o=t.cwdScope===null?"all dirs":Y(t.cwdScope),s=[`${n("profile")} ${e}`,`${n("scope")} ${o}`,`${n("sort")} ${t.sort}`,`${n("lifecycle")} ${t.residentsOnly?"residents":"all"}`];return!t.search&&t.query!==""&&s.push(`${n("filter")} ${t.query}`),s.join(n(" \xB7 "))}d(oe,"statusLine");function et(t,n,e,o){if(t.hi.size===0)return R([{text:t.text,style:o}],n,e.color,"",!1);const s=[];let r="",c=!1;const u=d(()=>{r!==""&&(s.push(c?{text:r,style:{fg:H,bold:!0}}:{text:r,style:o}),r="")},"flush");for(let l=0;l<t.text.length;l++){const i=t.hi.has(l);i!==c&&(u(),c=i),r+=t.text[l]}return u(),R(s,n,e.color,"",!1)}d(et,"snippetLine");function se(t,n){const e=t?Bt:0,o=n?Ft:0,s=Math.max(2,it-e-o),r=Math.ceil(s/2);return{recap:e,ask:r,reply:s-r,report:o}}d(se,"previewAllocation");function _t(t,n,e,o,s,r,c){if(o<=0)return[];const u=[],l=r.color?`${x}${t}${h}`:t,i=Math.max(1,s-2);if(c===""){const a=Q(n,i,o,!1);if(a.length===0)u.push(`${l}${x}${T(e,i)}${h}`);else for(const f of a)u.push(`${u.length===0?l:" "}${R(f,i,r.color,"",!1)}`);for(;u.length<o;)u.push("");return u}const $=U(c,n,i,o);if($.length===0)u.push(`${l}${x}${T(e,i)}${h}`);else for(const a of $)u.push(`${u.length===0?l:" "}${et(a,i,r)}`);for(;u.length<o;)u.push("");return u}d(_t,"markedBlock");function Tt(t,n,e,o){const s=`${t.padEnd(6)}`,r=T(n,Math.max(1,e-s.length));return o.color?`${x}${s}${h}${C}${r}${h}`:`${s}${r}`}d(Tt,"recapLine");function re(t,n,e,o,s,r){const c=[`${x}${"\u2500".repeat(n)}${h}`];if(t===void 0){for(;c.length<ut;)c.push("");return c}const u=q(t.status,{streaming:t.streaming===!0,frozen:X(t),hanging:t.hanging??null}),l=e.color?`${_}${u.color}m${u.glyph}${h}`:u.glyph,i=[`${t.status} ${t.kind}/${t.mode}`,Y(t.cwd),B(t.created,o),`ctx ${Z(t.ctx_tokens)}`];t.asks>0&&i.push(`\u2691${t.asks}`),t.hanging!=null&&i.push(ct(t.hanging,o)),t.streaming===!0&&i.push("\u27F3"),t.viewed===!0&&i.push("\uF06E viewing");const $=T(i.filter(m=>m!=="").join(" \xB7 "),Math.max(0,n-2));c.push(e.color?`${l} ${x}${$}${h}`:`${l} ${$}`);const a=se(t.recap!==void 0,t.report!==void 0);t.recap!==void 0&&(c.push(Tt("Goal",t.recap.goal===""?"\u2014":t.recap.goal,n,e)),c.push(Tt("State",t.recap.state===""?"\u2014":t.recap.state,n,e)));const f=s!==""?r??Ht(t):t.goal??"";if(c.push(..._t("\u276F ",f,"(no spawn prompt)",a.ask,n,e,s)),c.push(..._t("\u21A9 ",t.lastAssistant??"","(no reply yet)",a.reply,n,e,"")),t.report!==void 0){const m=B(t.report.ts,o),g=`${m===""?"":`${m} `}${t.report.kind}`,v=t.report.heading===""?t.report.body:t.report.heading,E=e.color?`${x}\u25A3 ${h}`:"\u25A3 ",y=Math.max(1,n-2),b=e.color?`${_}${D}m${g}${h}${x} \xB7 ${h}`:`${g} \xB7 `;c.push(`${E}${b}${T(v,Math.max(1,y-g.length-3))}`);for(let M=1;M<a.report;M++){const S=t.report.heading===""?"":t.report.body,w=U("",S,y,a.report-1)[M-1];c.push(w===void 0?"":` ${x}${T(w.text,y)}${h}`)}}return c}d(re,"previewPanel");function V(t,n,e,o){const s=o===void 0?"":`${o.text} `,r=Math.max(0,n-4-t.length-s.length),c=o===void 0?"":e.color?`${_}${o.fg}m${o.text}${h}${x} `:`${o.text} `;return`${x}\u2500\u2500 ${C}${t}${h}${x} ${c}${"\u2500".repeat(r)}${h}`}d(V,"sectionRule");function nt(t,n,e,o,s,r,c,u,l,i){if(r<=0)return[];const $=[...t].length,a=Math.max(1,c-$),f=d((g,v)=>R([{text:g,style:v}],$,u.color,"",!0),"mk");if(l===""){const g=Q(o,a,r,!0,i);return g.length===0?s===void 0?[]:[`${f(t,e)}${x}${T(s,a)}${h}`]:g.map((v,E)=>`${f(E===0?t:n,e)}${R(v,a,u.color,"",!1)}`)}const m=U(l,o,a,r);return m.length===0?s===void 0?[]:[`${f(t,e)}${x}${T(s,a)}${h}`]:m.map((g,v)=>`${f(v===0?t:n,e)}${et(g,a,u,i)}`)}d(nt,"blockLines");const le=400,ce=1,ot=14,St={fg:H,bold:!0,reverse:!0};function ie(t){let n="";for(const e of t)n+=e.text;return n}d(ie,"spansText");function ue(t,n){if(n==="")return{spans:t,hits:0};const e=ie(t),o=e.toLowerCase(),s=n.toLowerCase();if(o.indexOf(s)<0)return{spans:t,hits:0};const r=new Uint8Array(e.length);let c=0;for(let i=o.indexOf(s);i>=0;i=o.indexOf(s,i+s.length)){c++;for(let $=i;$<i+s.length;$++)r[$]=1}const u=[];let l=0;for(const i of t){let $="",a=r[l]===1;for(let f=0;f<i.text.length;f++){const m=r[l]===1;m!==a&&($!==""&&u.push({text:$,style:a?St:i.style}),$="",a=m),$+=i.text[f],l++}$!==""&&u.push({text:$,style:a?St:i.style})}return{spans:u,hits:c}}d(ue,"highlightSpans");const K="\u258C ";function st(t,n,e,o,s){if(e<2)return{lines:[],hits:0,truncated:!0};const r=t.role==="user",c=r?{fg:H,bold:!0}:{fg:P},u=Math.max(1,o-K.length),l=d(()=>R([{text:K,style:c}],K.length,s.color,"",!0),"bar"),i=[R([{text:K,style:c},{text:r?"YOU":"AGENT",style:{...c,bold:!0}}],o,s.color,"",!1)],a=Q(t.text,u,le,!1,r?{}:{dim:!0}).map(b=>ue(b,n)),f=a.reduce((b,M)=>b+M.hits,0),m=e-1;let g=0;a.length>m&&f>0&&(g=Math.max(0,a.findIndex(b=>b.hits>0)-ce),g=Math.min(g,a.length-m));const v=a.slice(g,g+m);for(const b of v)i.push(`${l()}${R(b.spans,u,s.color,"",!1)}`);const E=g>0,y=g+v.length<a.length;return E&&v.length>1&&(i[1]=`${l()}${x}\u22EF${h}`),y&&v.length>(E?2:1)&&(i[i.length-1]=`${l()}${x}\u22EF${h}`),{lines:i,hits:f,truncated:E||y}}d(st,"turnBlock");function wt(t,n,e){return R([{text:` \u22EF ${t} ${t===1?"turn":"turns"}`,style:{dim:!0}}],n,e.color,"",!1)}d(wt,"elisionLine");function Rt(t,n){return n!==""&&t.text.toLowerCase().indexOf(n.toLowerCase())>=0}d(Rt,"turnMatches");function ae(t,n){return t.reduce((e,o)=>e+(Rt(o,n)?1:0),0)}d(ae,"transcriptHitTurns");function At(t,n,e,o,s){if(o<=0||t.length===0)return[];const r=n===""?[]:t.map((l,i)=>Rt(l,n)?i:-1).filter(l=>l>=0);if(r.length>0){const l=[];let i=-1;for(const a of r){const f=l.length===0?0:1,m=o-l.length-f;if(m<3)break;const{lines:g}=st(t[a],n,Math.min(m,ot),e,s);if(g.length===0)break;const v=i<0?a:a-i-1,E=v>0&&l.length+f+g.length+1<=o;f>0&&l.push(""),E&&l.push(wt(v,e,s)),l.push(...g),i=a}let $=i;for(let a=i+1;a<t.length;a++){const f=o-l.length-1;if(f<3)break;const{lines:m,truncated:g}=st(t[a],n,Math.min(f,ot),e,s);if(m.length===0||g)break;l.push("",...m),$=a}return $>=0&&$<t.length-1&&l.length<o&&l.push(wt(t.length-1-$,e,s)),l.slice(0,o)}const c=[];let u=0;for(let l=t.length-1;l>=0;l--){const i=c.length===0,$=i?0:1,a=o-u-$;if(a<3)break;const{lines:f,truncated:m}=st(t[l],"",Math.min(a,ot),e,s);if(f.length===0||m&&!i)break;$>0&&c.push([""]),c.push(f),u+=f.length+$}return c.reverse().flat().slice(-o)}d(At,"transcriptLines");function kt(t){return t.transcriptLoaded===!0||t.previewLoaded===!0?`${x}(no conversation yet)${h}`:`${x}(loading\u2026)${h}`}d(kt,"emptyBodyNote");function Ct(t){const n=t.transcript;if(n!==void 0&&n.length>0)return n;const e=[];return t.goal!==void 0&&t.goal!==""&&e.push({role:"user",text:t.goal}),t.lastAssistant!==void 0&&t.lastAssistant!==""&&e.push({role:"assistant",text:t.lastAssistant}),e}d(Ct,"panelTurns");const de=4;function fe(t,n,e,o){const s=vt(t.ctx_tokens),r=d(()=>({text:" \xB7 ",style:{dim:!0}}),"dot"),c=[{text:`${t.kind}/${t.mode}`,style:{fg:P}},r(),{text:`~${Y(t.cwd)}`,style:{fg:P}},r(),{text:B(t.created,e),style:{dim:!0}},r(),{text:`ctx ${Z(t.ctx_tokens)}`,style:{fg:s,dim:s===void 0}}];return R(c,n,o.color,"",!1)}d(fe,"panelFactsLine");function $e(t,n,e,o){if(t===void 0)return R([{text:"PREVIEW",style:{dim:!0,bold:!0}}],n,e.color,"",!1);const s=[];o&&s.push({text:"\u2605 ",style:{fg:yt,bold:!0}});const r=t.node_id.slice(0,8);return s.push({text:T(t.name,Math.max(4,n-r.length-2-L(s))),style:{bold:!0}}),tt(s,[{text:r,style:{dim:!0}}],n,e.color,"",!1)}d($e,"panelTitleLine");function he(t,n,e,o,s,r,c,u){if(e<=0)return[];if(t===void 0)return[`${x}(no selection)${h}`];if(r!==""){const b=Ct(t),M=ae(b,r),S=M===0&&u!==void 0,w=[V("TRANSCRIPT",n,o,M>0?{text:`${M} ${M===1?"turn":"turns"}`,fg:H}:{text:S?"matched in conversation":"matched on name",fg:P})],O=S?U(r,u,Math.max(1,n-2),e-1).map(W=>` ${et(W,Math.max(1,n-2),o)}`):At(b,r,n,e-1,o);return w.push(...O.length===0?[kt(t)]:O),w.slice(0,e)}const l=t.report,i=d(()=>({fg:q(t.status,{streaming:t.streaming===!0,frozen:X(t),hanging:t.hanging??null}).color}),"accentOf");if(c&&l!==void 0){const b=B(l.ts,s),M=[V("REPORT",n,o,{text:`${b===""?"":`${b} \xB7 `}${l.kind}`,fg:D})],S=l.heading===""?l.body:l.heading;return M.push(...nt("\u25A3 "," ",{fg:D,bold:!0},S,void 0,3,n,o,"",{bold:!0})),l.heading!==""&&l.body!==""&&(M.push(""),M.push(...nt(" "," ",void 0,l.body,void 0,e-M.length,n,o,""))),M.slice(0,e)}const $=[fe(t,n,s,o),""],a=i(),f=d((b,M)=>[V(b,n,o),...nt("\u258C ","\u258C ",a,M,void 0,de,n,o,"",{bold:!0})],"briefing"),m=t.recap?.goal??"";m!==""&&$.push(...f("GOAL",m),"");const g=t.recap?.state??"",v=g===""?[]:[...f("STATE",g)],E=v.length===0?0:v.length+1,y=e-$.length-E-1;if(y>=2){$.push(V("CHAT",n,o));const b=At(Ct(t),"",n,y,o);$.push(...b.length===0?[kt(t)]:b)}if(v.length>0){for(;$.length<e-v.length;)$.push("");$.push(...v)}return $.slice(0,e)}d(he,"sidePanelBody");const pe=[["Move","\u2191/\u2193 j/k","up / down"],["","g / G","top / bottom"],["",".","jump to this node (recenter)"],["Tree","\u2192/\u2190 h/l","expand / collapse"],["","space","toggle fold"],["Tabs","Tab / 1-5","All \xB7 Live \xB7 Dormant \xB7 Attention \xB7 Pinned"],["Find","/","fuzzy search (name \xB7 kind \xB7 id \xB7 prompts)"],["Pin","p","star / unstar \u2014 floats to top, Pinned tab"],["Open","\u23CE o","open conversation (closes navigator)"],["","O","open, keep browsing (background)"],["Act","y","copy the node id to the clipboard"],["","m","message the node (no resume)"],["","R","rename the node"],["","x","close-out node + subtree"],["","K","revive now (a hanging node)"],["View","s","cycle sort (attention/tree/relevance/recency)"],["","c","toggle cwd scope"],["","P","toggle profile scope"],["","r","toggle residents-only"],["","v","toggle preview (goal, chat, state)"],["","V","in the preview: swap chat for the newest report"],["Quit","q / Esc",""]];function ge(t,n){const e=Math.max(20,t.cols),o=Math.max(8,t.rows),s=e-1,r=[];r.push(`${C}Canvas navigator \u2014 keys${h}`),r.push("");const c=d(l=>`${x}${l}${h}`,"dim");for(const[l,i,$]of pe){const a=l.padEnd(6),f=i.padEnd(12),m=` ${c(a)}${C}${f}${h}${$===""?"":` ${c($)}`}`;r.push(T(m,s))}for(r.push(""),r.push(c(" ? or any key to close"));r.length<o-1;)r.push("");const u=r.map(l=>`${l}${_}K`).join(`\r
2
+ `);return`${_}H${u}${_}J`}d(ge,"renderHelp");function _e(t,n,e={color:!1,color256:!1}){if(t.help===!0)return ge(n,e);const o=Math.max(20,n.cols),s=Math.max(8,n.rows),r=o-1,c=Date.now(),u=t.search||t.rename===!0||t.message===!0,l=[],i=`${C}Canvas${h} \u2014 ${t.totalNodes} nodes`,$=`Canvas \u2014 ${t.totalNodes} nodes`.length,a=Jt(t.tree),f=a.map(p=>`${lt[p.status]}${p.count}`).join(" "),m=e.color?a.map(p=>`${_}${It[p.status]}m${lt[p.status]}${h}${x}${p.count}${h}`).join(" "):`${x}${f}${h}`,g=r-$-f.length;if(l.push(g>1?`${i}${" ".repeat(g)}${m}`:i),l.push(Xt(t.tab,e.color)),l.push(oe(t)),u){const p=t.rename===!0?"rename \u25B8":t.message===!0?"msg \u25B8":"/",A=t.rename===!0?t.renameBuf??"":t.message===!0?t.messageBuf??"":t.query,F=e.color?`${_}${D}m${p}${h}`:p;l.push(`${F} ${A}\u258E`)}const v=t.preview&&t.visible.length>0,E=Vt(o,v),y=E.mode==="side",b=y?E.rowsW:r,M=`${x}${ft}${h}`,S=t.visible[t.cursor],w=S!==void 0?t.tree.nodes.get(S.id)?.row:void 0,O=t.cwdScope===null&&!y,W=ne(b,O,e,!y,y);l.push(y?`${W}${M}${$e(w,E.panelW,e,S!==void 0&&t.pinned.has(S.id))}`:W),l.push(y?`${x}${"\u2500".repeat(b)}\u2500\u253C\u2500${"\u2500".repeat(E.panelW)}${h}`:`${x}${"\u2500".repeat(r)}${h}`);const z=l.length,J=E.mode==="bottom"?ut:0,N=Math.max(1,s-z-1-J),k=t.scrollOffset,G=Math.min(t.visible.length,k+N),Lt=y?he(w,E.panelW,N,e,c,t.query,t.showReport===!0,t.contentSnippet):[],rt=d((p,A)=>y?`${p}${M}${Lt[A]??""}`:p,"compose"),Ot=y?" ".repeat(b):"";if(t.visible.length===0){l.push(`${x} (no nodes match this view)${h}`);for(let p=1;p<N;p++)l.push("")}else{for(let p=k;p<G;p++){const A=t.visible[p];l.push(rt(ee(A,t.tree,b,p===t.cursor,t.query,e,O,c,t.pinned.has(A.id),!y,y),p-k))}for(let p=G-k;p<N;p++)l.push(rt(Ot,p))}if(E.mode==="bottom")for(const p of re(w,r,e,c,t.query,t.contentSnippet))l.push(p);if(t.pendingClose!==null&&t.pendingClose!==void 0){const p=t.tree.nodes.get(t.pendingClose),F=`\u26A0 ${p!==void 0?p.row.name:t.pendingClose} is \u27F3 \u2014 close it (and its subtree) anyway? y / n`;l.push(e.color?`${C}${_}${bt}m${T(F,r)}${h}`:`${j}${T(F,r)}${h}`)}else if(t.flash!==null&&t.flash!==void 0&&t.flash!=="")l.push(e.color?`${C}${T(t.flash,r)}${h}`:`${j}${T(t.flash,r)}${h}`);else{const p=w?.hanging!=null?"K revive now ":"",A=u?`\u23CE commit Esc cancel \u232B delete${t.searchingContent===!0?" searching conversations\u2026":""}`:t.readOnly===!0?"\u2191\u2193 move \u2192/\u2190 tree / search p pin v preview Tab tabs ? more q quit \u2014 read-only remote canvas":`${p}${w?.report!==void 0&&y&&t.query===""?t.showReport===!0?"V chat ":"V report ":""}\u2191\u2193 move \u23CE open O bg / search p pin v preview m msg x close ? more q quit`;l.push(`${x}${T(A,r)}${h}`)}const Wt=l.map(p=>`${p}${_}K`).join(`\r
3
+ `);return`${_}H${Wt}${_}J`}d(_e,"renderFrame");export{it as PREVIEW_BODY,ut as PREVIEW_HEIGHT,Bt as RECAP_LINES,Ft as REPORT_LINES,Dt as detectColorCaps,Ee as headerHeight,ue as highlightSpans,se as previewAllocation,Vt as previewLayout,_e as renderFrame,ae as transcriptHitTurns};
@@ -1046,4 +1046,4 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
1046
1046
  CREATE UNIQUE INDEX objects_named ON objects(owner, name)
1047
1047
  WHERE deleted_at IS NULL AND home_machine IS NULL AND (type = 'document' OR instr(type, '.') > 0);
1048
1048
  CREATE UNIQUE INDEX objects_handle ON objects(type, name) WHERE home_machine IS NULL AND type IN ('person','app','repo','profile');
1049
- `),e=>e.exec("ALTER TABLE grants ADD COLUMN inference_endpoint_json TEXT")];function B(e,t,n){const o=e.prepare("PRAGMA foreign_keys").get().foreign_keys;e.exec("PRAGMA foreign_keys = OFF;");try{e.exec("BEGIN IMMEDIATE");try{W(e)===t&&(n(),e.exec(`PRAGMA user_version = ${t+1}`)),e.exec("COMMIT")}catch(T){try{e.exec("ROLLBACK")}catch{}throw T}}finally{e.exec(`PRAGMA foreign_keys = ${o};`)}}E(B,"selfTransactionalMigration");const bt=new Set([3,61,63,66]);function W(e){return e.prepare("PRAGMA user_version").get().user_version}E(W,"currentUserVersion");function se(e,t){F[t](e),e.exec(`PRAGMA user_version = ${t+1}`)}E(se,"applyMigration");function on(e){const t=W(e);if(t>F.length)throw new Error(`unsupported canvas schema version ${t} (latest supported ${F.length})`);if(t===F.length)return;const n=e.prepare("PRAGMA busy_timeout").get().timeout;e.exec("PRAGMA busy_timeout = 60000;");try{Bt(e)}finally{e.exec(`PRAGMA busy_timeout = ${n};`)}}E(on,"migrate");function Bt(e){for(;;){const t=W(e);if(t>=F.length)return;if(bt.has(t)){if(e.isTransaction)throw new Error(`canvas migration v${t+1} requires autocommit`);F[t](e);continue}if(e.isTransaction){se(e,t);continue}e.exec("BEGIN IMMEDIATE");try{W(e)===t&&se(e,t),e.exec("COMMIT")}catch(n){try{e.exec("ROLLBACK")}catch{}throw n}}}E(Bt,"runPendingMigrations");export{F as MIGRATIONS,on as migrate};
1049
+ `),e=>e.exec("ALTER TABLE grants ADD COLUMN inference_endpoint_json TEXT"),e=>e.exec("ALTER TABLE nodes ADD COLUMN reply_at TEXT; ALTER TABLE nodes ADD COLUMN read_at TEXT;")];function B(e,t,n){const o=e.prepare("PRAGMA foreign_keys").get().foreign_keys;e.exec("PRAGMA foreign_keys = OFF;");try{e.exec("BEGIN IMMEDIATE");try{W(e)===t&&(n(),e.exec(`PRAGMA user_version = ${t+1}`)),e.exec("COMMIT")}catch(T){try{e.exec("ROLLBACK")}catch{}throw T}}finally{e.exec(`PRAGMA foreign_keys = ${o};`)}}E(B,"selfTransactionalMigration");const bt=new Set([3,61,63,66]);function W(e){return e.prepare("PRAGMA user_version").get().user_version}E(W,"currentUserVersion");function se(e,t){F[t](e),e.exec(`PRAGMA user_version = ${t+1}`)}E(se,"applyMigration");function on(e){const t=W(e);if(t>F.length)throw new Error(`unsupported canvas schema version ${t} (latest supported ${F.length})`);if(t===F.length)return;const n=e.prepare("PRAGMA busy_timeout").get().timeout;e.exec("PRAGMA busy_timeout = 60000;");try{Bt(e)}finally{e.exec(`PRAGMA busy_timeout = ${n};`)}}E(on,"migrate");function Bt(e){for(;;){const t=W(e);if(t>=F.length)return;if(bt.has(t)){if(e.isTransaction)throw new Error(`canvas migration v${t+1} requires autocommit`);F[t](e);continue}if(e.isTransaction){se(e,t);continue}e.exec("BEGIN IMMEDIATE");try{W(e)===t&&se(e,t),e.exec("COMMIT")}catch(n){try{e.exec("ROLLBACK")}catch{}throw n}}}E(Bt,"runPendingMigrations");export{F as MIGRATIONS,on as migrate};
@@ -34,9 +34,14 @@ export declare function openTurn(executionId: string, _opts: {
34
34
  reason: 'agent-start' | 'prompt-unsettled';
35
35
  }): boolean;
36
36
  export declare function recordTurnProgress(executionId: string): boolean;
37
+ /** Closes the open turn. A `settled` close also stamps the node's `reply_at` (the dashboard's unread marker) in the same
38
+ * write, and `read_at` too when a viewer was shown the conversation while the turn ended. A close that changes no row writes neither. */
37
39
  export declare function closeTurn(executionId: string, opts: {
38
40
  endReason: 'settled' | 'teardown-aborted';
41
+ viewerPresent?: boolean;
39
42
  }): boolean;
43
+ /** Stamps `read_at`: a viewer has just been shown this conversation. */
44
+ export declare function markNodeRead(nodeId: string): boolean;
40
45
  export declare function recordTransportPoisonVerdict(executionId: string, verdict: TransportPoisonVerdict): boolean;
41
46
  export declare function readTransportPoisonVerdict(nodeId: string): TransportPoisonVerdict | null;
42
47
  export declare function readLiveTurn(nodeId: string): {