@north-light/cli 0.3.533 → 0.3.535

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 (52) hide show
  1. package/dist/api/dto/crons.d.ts +1 -1
  2. package/dist/api/dto/nodes.d.ts +2 -0
  3. package/dist/builtin-memory/crouter-sdk/release-notes.md +6 -1
  4. package/dist/builtin-memory/northlight-apps/providers/README.md +1 -1
  5. package/dist/builtin-memory/northlight-apps/providers/sms.md +1 -1
  6. package/dist/clients/attach/viewer.js +2 -2
  7. package/dist/commands/api-client.d.ts +1 -0
  8. package/dist/commands/api-client.js +1 -1
  9. package/dist/commands/canvas.js +1 -1
  10. package/dist/commands/node/inspect.js +9 -9
  11. package/dist/core/bash-jobs.d.ts +4 -0
  12. package/dist/core/bash-jobs.js +8 -8
  13. package/dist/core/canvas/canvas.d.ts +16 -2
  14. package/dist/core/canvas/canvas.js +20 -20
  15. package/dist/core/canvas/recovery.js +1 -1
  16. package/dist/core/canvas/remote-canvas-source.d.ts +1 -0
  17. package/dist/core/canvas/remote-canvas-source.js +1 -1
  18. package/dist/core/canvas/source.d.ts +4 -0
  19. package/dist/core/canvas/source.js +1 -1
  20. package/dist/core/canvas/tree.d.ts +2 -0
  21. package/dist/core/canvas/tree.js +12 -5
  22. package/dist/core/graph/documents.js +7 -7
  23. package/dist/core/graph/watches.d.ts +0 -2
  24. package/dist/core/graph/watches.js +7 -12
  25. package/dist/core/runs/questions.d.ts +2 -0
  26. package/dist/core/runs/questions.js +1 -1
  27. package/dist/core/runtime/broker/fault-retry.d.ts +6 -0
  28. package/dist/core/runtime/broker/fault-retry.js +1 -1
  29. package/dist/core/runtime/launch.js +2 -2
  30. package/dist/core/worktree.js +8 -8
  31. package/dist/daemon/api/handlers/crons.js +1 -1
  32. package/dist/daemon/api/handlers/docs.js +1 -1
  33. package/dist/daemon/api/handlers/nodes.js +1 -1
  34. package/dist/daemon/api/handlers/objects.js +3 -3
  35. package/dist/daemon/api/handlers/questions.js +1 -1
  36. package/dist/daemon/cron/ended.d.ts +3 -0
  37. package/dist/daemon/cron/ended.js +2 -2
  38. package/dist/daemon/cron/narrowed.d.ts +5 -0
  39. package/dist/daemon/cron/narrowed.js +2 -0
  40. package/dist/daemon/cron/settle.js +1 -1
  41. package/dist/daemon/reconcilers/bash-deadline.d.ts +6 -0
  42. package/dist/daemon/reconcilers/bash-deadline.js +3 -2
  43. package/dist/daemon/runs/observer.d.ts +2 -0
  44. package/dist/daemon/runs/observer.js +2 -2
  45. package/docs/apps/providers/README.md +1 -1
  46. package/docs/apps/providers/sms.md +1 -1
  47. package/docs/sdk/release-notes.md +6 -1
  48. package/package.json +2 -2
  49. package/packages/crouter-identity/package.json +1 -1
  50. package/runtime.lock.json +12 -12
  51. package/dist/core/canvas/nav-model.d.ts +0 -44
  52. package/dist/core/canvas/nav-model.js +0 -1
@@ -23,5 +23,11 @@ export declare class BashDeadlineReconciler {
23
23
  /** Give every backgrounded job dir (job.bg) that has no job object one; the next tick's
24
24
  * `publishExits` then ends it like any other job. */
25
25
  private adoptUnrecorded;
26
+ /** A running job whose process is confirmed gone without job.exit (killed outright, so its
27
+ * supervisor never wrote one) is ended once as killed, with a null exit code, through
28
+ * `finishJob`. job.exit is then written so the file-based listing and stop guard stop counting
29
+ * the job. A job.exit that appears by the re-check is a real exit: `publishExits` reports it.
30
+ * A job being stopped (job.stop) is ended by its killer. */
31
+ private settleVanished;
26
32
  private cancel;
27
33
  }
@@ -1,2 +1,3 @@
1
- var u=Object.defineProperty;var f=(l,t)=>u(l,"name",{value:t,configurable:!0});import{existsSync as n,readFileSync as m,readdirSync as j,statSync as _}from"node:fs";import{join as b}from"node:path";import{bashJobPaths as y,bashJobsDir as w,expiredAutoBackgroundBashJobs as S,stopBackgroundBashJob as x}from"../../core/bash-jobs.js";import{killBashJobObject as N,recordBashJobObject as E}from"../../core/spaces/stop.js";import{withCanvasWrite as h}from"../../core/canvas/db.js";import{finishJob as v}from"../../core/graph/jobs.js";import{getById as p}from"../../core/graph/objects.js";import{isSafeNodeId as c}from"../../core/canvas/paths.js";import{contextDir as A}from"../../core/canvas/node-agent-paths.js";import{emitEvent as s}from"../../core/events/emit.js";import{operationIdContext as d}from"../../core/events/operation-id.js";import{isProfilePaused as g}from"../../core/profiles/manifest.js";const B=5*1e3;class V{static{f(this,"BashDeadlineReconciler")}lastScanAt=Number.NEGATIVE_INFINITY;startedAt=Date.now();async run(t,a){if(await this.publishExits(),!(t-this.lastScanAt<B)){this.lastScanAt=t;for(const e of a.rows){if(e.status==="dead"||g(e.profile_id))continue;const r=await A(e.node_id);let i;try{await this.adoptUnrecorded(e.node_id,r),i=S(r,t)}catch(o){d.fresh(()=>{s({level:"error",event:"bash_deadline.scan_failed",...c(e.node_id)?{node_id:e.node_id}:{},error:o})});continue}for(const o of i)await this.cancel(e.node_id,r,o.jobId)}}}async publishExits(){try{await h(async t=>{const a=await t.prepare(`SELECT j.object, j.job_dir FROM jobs j JOIN objects o ON o.id = j.object
2
- WHERE j.status = 'running' AND o.deleted_at IS NULL`).all();for(const{object:e,job_dir:r}of a){const i=b(r,"job.exit");if(!n(i)||n(b(r,"job.stop")))continue;const o=Number(m(i,"utf8").trim());Number.isSafeInteger(o)&&await v(t,await p(t,e),{exitCode:o})}})}catch(t){d.fresh(()=>s({level:"error",event:"bash_completion.delivery_failed",error:t}))}}async adoptUnrecorded(t,a){let e;try{e=j(w(a))}catch{return}await h(async r=>{for(const i of e){if(await p(r,i)!==null)continue;const o=y(a,i);!n(o.jobBg)||!n(o.cmdSh)||n(o.jobExit)&&_(o.jobExit).mtimeMs<this.startedAt||await E(t,i,{command:m(o.cmdSh,"utf8"),jobDir:o.dir,logPath:o.jobLog})}})}async cancel(t,a,e){(await x(a,e,!1,t)).kind==="stopped"&&await d.fresh(async()=>{try{s({level:"info",event:"bash_deadline.canceled",...c(t)?{node_id:t}:{},fields:{job_id:e}}),await N(e,async()=>null,"deadline")}catch(i){s({level:"error",event:"bash_deadline.notice_failed",...c(t)?{node_id:t}:{},error:i,fields:{job_id:e}})}})}}export{V as BashDeadlineReconciler};
1
+ var y=Object.defineProperty;var p=(h,t)=>y(h,"name",{value:t,configurable:!0});import{existsSync as n,readFileSync as m,readdirSync as _,statSync as w}from"node:fs";import{join as s}from"node:path";import{bashJobPaths as u,bashJobsDir as x,activeBackgroundBashJobs as E,expiredAutoBackgroundBashJobs as S,isBashJobProcessConfirmedGone as B,readJobPgid as N,readJobPgidIdentity as g,stopBackgroundBashJob as v}from"../../core/bash-jobs.js";import{killBashJobObject as J,recordBashJobObject as A}from"../../core/spaces/stop.js";import{createFileBeneath as I}from"../../core/spaces/open-beneath.js";import{withCanvasWrite as d}from"../../core/canvas/db.js";import{finishJob as j}from"../../core/graph/jobs.js";import{getById as l}from"../../core/graph/objects.js";import{isSafeNodeId as f}from"../../core/canvas/paths.js";import{contextDir as P}from"../../core/canvas/node-agent-paths.js";import{emitEvent as c}from"../../core/events/emit.js";import{operationIdContext as b}from"../../core/events/operation-id.js";import{isProfilePaused as k}from"../../core/profiles/manifest.js";const C=5*1e3;class q{static{p(this,"BashDeadlineReconciler")}lastScanAt=Number.NEGATIVE_INFINITY;startedAt=Date.now();async run(t,r){if(await this.publishExits(),!(t-this.lastScanAt<C)){this.lastScanAt=t;for(const e of r.rows){if(e.status==="dead"||k(e.profile_id))continue;const a=await P(e.node_id);let i;try{await this.adoptUnrecorded(e.node_id,a),await this.settleVanished(a),i=S(a,t)}catch(o){b.fresh(()=>{c({level:"error",event:"bash_deadline.scan_failed",...f(e.node_id)?{node_id:e.node_id}:{},error:o})});continue}for(const o of i)await this.cancel(e.node_id,a,o.jobId)}}}async publishExits(){try{await d(async t=>{const r=await t.prepare(`SELECT j.object, j.job_dir FROM jobs j JOIN objects o ON o.id = j.object
2
+ WHERE j.status = 'running' AND o.deleted_at IS NULL`).all();for(const{object:e,job_dir:a}of r){const i=s(a,"job.exit");if(!n(i)||n(s(a,"job.stop")))continue;const o=Number(m(i,"utf8").trim());Number.isSafeInteger(o)&&await j(t,await l(t,e),{exitCode:o})}})}catch(t){b.fresh(()=>c({level:"error",event:"bash_completion.delivery_failed",error:t}))}}async adoptUnrecorded(t,r){let e;try{e=_(x(r))}catch{return}await d(async a=>{for(const i of e){if(await l(a,i)!==null)continue;const o=u(r,i);!n(o.jobBg)||!n(o.cmdSh)||n(o.jobExit)&&w(o.jobExit).mtimeMs<this.startedAt||await A(t,i,{command:m(o.cmdSh,"utf8"),jobDir:o.dir,logPath:o.jobLog})}})}async settleVanished(t){for(const{jobId:r}of E(t)){const e=u(t,r);n(e.jobExit)||n(s(e.dir,"job.stop"))||B(N(t,r),g(t,r))&&await d(async a=>{const i=await l(a,r);if(!(i===null||i.type!=="job")&&!(n(e.jobExit)||n(s(e.dir,"job.stop")))){await j(a,i,{killedBy:null,reason:"process disappeared without an exit record"});try{I(e.root,e.jobExit,`-1
3
+ `)}catch(o){if(o.code!=="EEXIST")throw o}}})}}async cancel(t,r,e){(await v(r,e,!1,t)).kind==="stopped"&&await b.fresh(async()=>{try{c({level:"info",event:"bash_deadline.canceled",...f(t)?{node_id:t}:{},fields:{job_id:e}}),await J(e,async()=>null,"deadline")}catch(i){c({level:"error",event:"bash_deadline.notice_failed",...f(t)?{node_id:t}:{},error:i,fields:{job_id:e}})}})}}export{q as BashDeadlineReconciler};
@@ -8,6 +8,8 @@ export type OpenAssistantBlock = {
8
8
  };
9
9
  /** The newest block of the node's open assistant message; null when no message is open or nothing has streamed yet. */
10
10
  export declare function openAssistantBlock(nodeId: string): OpenAssistantBlock | null;
11
+ /** Test-only: stop every observer and wait out the polls in flight, so none touches a home the test is about to delete. */
12
+ export declare function closeRunObserversForTests(): Promise<void>;
11
13
  export declare function ensureRunObserver(nodeId: string, runId: string): Promise<void>;
12
14
  /** Observes run nodes with a live broker, plus those whose current outcome
13
15
  * (settled within the event retention window) has no `node.settled` event yet. */
@@ -1,5 +1,5 @@
1
- var k=Object.defineProperty;var r=(s,t)=>k(s,"name",{value:t,configurable:!0});import{randomUUID as y}from"node:crypto";import{openDb as l}from"../../core/canvas/db.js";import{getNode as m}from"../../core/canvas/canvas.js";import{BrokerClient as D,SocketTransport as I}from"../../core/broker-client/client.js";import{emitRunEvent as p,emitRunSettlement as E}from"../../core/runs/events.js";import{cancelOpenQuestions as T}from"../../core/runs/questions.js";import{emitEvent as h}from"../../core/events/emit.js";import{toRunOutcomeDTO as N}from"../api/map.js";import{readOutputResult as R}from"../../core/runtime/structured-output.js";const A=JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),u=new Map,b=250;class g{static{r(this,"RunObserver")}nodeId;runId;client;timer;status;piPid=null;outcomeRevision;openAssistant=!1;assistantStartSequence=0;block=null;thinking="";output="";pendingThinking="";pendingOutput="";flushTimer;closed=!1;polling;frames=Promise.resolve();constructor(t,i,n,e){this.nodeId=t,this.runId=i,this.status=n,this.outcomeRevision=e,this.timer=setInterval(()=>{this.pollOrClose()},250),this.timer.unref(),u.set(t,this)}static async start(t,i){const n=await l(),e=await n.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = ? ORDER BY sequence_number DESC LIMIT 1").get(t,t===i?"node.settled":"child.settled"),o=await n.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = 'node.status.changed' ORDER BY sequence_number DESC LIMIT 1").get(t),d=new g(t,i,o?JSON.parse(o.body_json).status:void 0,e?JSON.parse(e.body_json).outcome.revision:void 0);return await d.poll(),d}async pollOrClose(){try{await this.poll()}catch(t){this.close(),h({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:t})}}poll(){return this.polling===void 0&&(this.polling=this.pollOnce().finally(()=>{this.polling=void 0})),this.polling}async pollOnce(){const t=await m(this.nodeId);if(!t||t.run_id!==this.runId||!await S(this.runId)){this.close();return}this.piPid=t.pi_pid??null;const i=t.pi_pid===null?"dormant":t.status;this.status!==i&&await p(this.runId,this.nodeId,"node.status.changed",{status:i,reason:t.intent??null}),this.status=i;const n=N(t);if(n&&n.revision!==this.outcomeRevision&&Date.now()-Date.parse(n.settled_at)<720*60*6e4){if(this.nodeId===this.runId){const e=await(await l()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ? ORDER BY created DESC").all(this.runId,this.runId);for(const o of e)await w(o.node_id,this.runId),await u.get(o.node_id)?.poll()}if(this.outcomeRevision=n.revision,this.nodeId===this.runId){const e=n.kind==="result"?R(this.nodeId,t.grantee):void 0;await E(this.runId,this.nodeId,"node.settled",{outcome:n,usage:JSON.parse(t.usage_json??A),...e===void 0?{}:{result:e}})}else await E(this.runId,this.nodeId,"child.settled",{child_node_id:this.nodeId,outcome:n});if(t.grantee&&t.grantee!=="app:terminal"){const e=this.nodeId===this.runId?{run_id:this.runId}:{node_id:this.nodeId};T(e,this.nodeId===this.runId?"the run settled":"the asking node was closed").catch(o=>h({level:"error",event:"daemon.runs.question_cancel_failed",node_id:this.nodeId,error:o}))}}t.pi_pid===null?this.close():await this.connect()}get assistantMessageStartSequence(){return this.openAssistant&&this.client&&this.piPid!==null?this.assistantStartSequence:null}get openBlock(){return this.assistantMessageStartSequence===null?null:this.block}async connect(){if(this.client||this.closed)return;const t=new D(this.nodeId,{transport:new I(this.nodeId,{recordsViewerFaults:!1})});this.client=t,t.on("connect",()=>t.send({type:"hello",role:"observer",client_id:y(),attends:!1}));const i=r(async n=>{if(!(this.closed||this.client!==t)){if(n.type==="message_start"&&n.message.role==="assistant"&&(this.discardDeltas(),this.openAssistant=!0,this.block=null,this.thinking="",this.output="",this.assistantStartSequence=(await(await l()).prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(this.runId)).event_cursor),n.type==="message_update"&&n.message.role==="assistant"&&this.openAssistant){const e=n.message.content;if(!Array.isArray(e))return;const o=e.at(-1);o&&typeof o=="object"&&"type"in o&&(o.type==="toolCall"?this.block={kind:"tool_call",name:"name"in o&&typeof o.name=="string"?o.name:""}:(o.type==="text"||o.type==="thinking")&&(this.block={kind:o.type}));const d=e.filter(a=>a?.type==="thinking").map(a=>a.thinking).join(""),c=e.filter(a=>a?.type==="text").map(a=>a.text).join(""),v=d.startsWith(this.thinking)?d.slice(this.thinking.length):d,O=c.startsWith(this.output)?c.slice(this.output.length):c;this.thinking=d,this.output=c,this.pendingThinking+=v,this.pendingOutput+=O,(this.pendingThinking||this.pendingOutput)&&!this.flushTimer&&(this.flushTimer=setTimeout(()=>{this.flushDeltas().catch(a=>h({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:a}))},b),this.flushTimer.unref())}n.type==="message_end"&&n.message.role==="assistant"&&(await this.flushDeltas(),this.openAssistant=!1,this.block=null)}},"handleFrame");t.on("frame",n=>{this.frames=this.frames.then(()=>i(n)).catch(e=>{this.close(),h({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:e})})}),t.on("error",()=>{}),t.on("close",()=>{this.client===t&&(this.client=void 0,this.discardDeltas(),this.openAssistant=!1,this.block=null)}),t.connect()}async flushDeltas(){this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimer=void 0;const t=this.pendingThinking,i=this.pendingOutput;if(this.pendingThinking="",this.pendingOutput="",!this.openAssistant||!t&&!i)return;if(await(await l()).prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND sequence_number >= ? AND type IN ('node.output_text.done', 'node.thinking.done', 'usage') LIMIT 1").get(this.runId,this.nodeId,this.assistantStartSequence)){this.openAssistant=!1;return}t&&await p(this.runId,this.nodeId,"node.thinking.delta",{delta:t}),i&&await p(this.runId,this.nodeId,"node.output_text.delta",{delta:i})}discardDeltas(){this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimer=void 0,this.pendingThinking="",this.pendingOutput=""}close(){this.closed=!0,this.discardDeltas(),clearInterval(this.timer),u.delete(this.nodeId);const t=this.client;this.client=void 0,t?.close()}}function P(s){return u.get(s)?.assistantMessageStartSequence??null}r(P,"openAssistantMessageStartSequence");function Y(s){return u.get(s)?.openBlock??null}r(Y,"openAssistantBlock");async function S(s){return await(await l()).prepare("SELECT 1 FROM nodes WHERE node_id = ? AND run_id = ? AND event_cursor IS NOT NULL").get(s,s)!==void 0}r(S,"runRootExists");const f=new Map;async function w(s,t){const i=f.get(s);if(i)return i;if(u.has(s))return;const n=L(s,t).finally(()=>f.delete(s));return f.set(s,n),n}r(w,"ensureRunObserver");async function L(s,t){if(await S(t)){if(s!==t){const i=await m(s);!await(await l()).prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'child.started' LIMIT 1").get(t,s)&&i&&Date.now()-Date.parse(i.created)<720*60*6e4&&await p(t,s,"child.started",{child_node_id:s,kind:i.kind})}await g.start(s,t)}}r(L,"startRunObserver");async function G(){const s=new Date(Date.now()-2592e6).toISOString(),t=await(await l()).prepare(`SELECT n.node_id, n.run_id, n.pi_pid, COALESCE(n.outcome_revision, 1) AS revision, e.body_json
1
+ var y=Object.defineProperty;var r=(n,t)=>y(n,"name",{value:t,configurable:!0});import{randomUUID as k}from"node:crypto";import{openDb as l}from"../../core/canvas/db.js";import{getNode as g}from"../../core/canvas/canvas.js";import{BrokerClient as D,SocketTransport as T}from"../../core/broker-client/client.js";import{emitRunEvent as p,emitRunSettlement as w}from"../../core/runs/events.js";import{cancelOpenQuestions as I}from"../../core/runs/questions.js";import{emitEvent as c}from"../../core/events/emit.js";import{toRunOutcomeDTO as N}from"../api/map.js";import{readOutputResult as R}from"../../core/runtime/structured-output.js";const b=JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),u=new Map,A=250;class m{static{r(this,"RunObserver")}nodeId;runId;client;timer;status;piPid=null;outcomeRevision;openAssistant=!1;assistantStartSequence=0;block=null;thinking="";output="";pendingThinking="";pendingOutput="";flushTimer;closed=!1;polling;frames=Promise.resolve();constructor(t,i,s,e){this.nodeId=t,this.runId=i,this.status=s,this.outcomeRevision=e,this.timer=setInterval(()=>{this.pollOrClose()},250),this.timer.unref(),u.set(t,this)}static async start(t,i){const s=await l(),e=await s.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = ? ORDER BY sequence_number DESC LIMIT 1").get(t,t===i?"node.settled":"child.settled"),o=await s.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = 'node.status.changed' ORDER BY sequence_number DESC LIMIT 1").get(t),d=new m(t,i,o?JSON.parse(o.body_json).status:void 0,e?JSON.parse(e.body_json).outcome.revision:void 0);return await d.poll(),d}async pollOrClose(){try{await this.poll()}catch(t){this.close(),c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:t})}}poll(){return this.polling===void 0&&(this.polling=this.pollOnce().finally(()=>{this.polling=void 0})),this.polling}async pollOnce(){const t=await g(this.nodeId);if(!t||t.run_id!==this.runId||!await E(this.runId)){this.close();return}this.piPid=t.pi_pid??null;const i=t.pi_pid===null?"dormant":t.status;this.status!==i&&await p(this.runId,this.nodeId,"node.status.changed",{status:i,reason:t.intent??null}),this.status=i;const s=N(t);if(s&&s.revision!==this.outcomeRevision&&Date.now()-Date.parse(s.settled_at)<720*60*6e4){if(this.nodeId===this.runId){const e=await(await l()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ? ORDER BY created DESC").all(this.runId,this.runId);for(const o of e)await v(o.node_id,this.runId),await u.get(o.node_id)?.poll()}if(this.outcomeRevision=s.revision,this.nodeId===this.runId){const e=s.kind==="result"?R(this.nodeId,t.grantee):void 0;await w(this.runId,this.nodeId,"node.settled",{outcome:s,usage:JSON.parse(t.usage_json??b),...e===void 0?{}:{result:e}})}else await w(this.runId,this.nodeId,"child.settled",{child_node_id:this.nodeId,outcome:s});if(t.grantee&&t.grantee!=="app:terminal"){const e=this.nodeId===this.runId?{run_id:this.runId}:{node_id:this.nodeId};I(e,this.nodeId===this.runId?"the run settled":"the asking node was closed").catch(o=>c({level:"error",event:"daemon.runs.question_cancel_failed",node_id:this.nodeId,error:o}))}}t.pi_pid===null?this.close():await this.connect()}async settlePoll(){await this.polling?.catch(()=>{}),await this.frames}get assistantMessageStartSequence(){return this.openAssistant&&this.client&&this.piPid!==null?this.assistantStartSequence:null}get openBlock(){return this.assistantMessageStartSequence===null?null:this.block}async connect(){if(this.client||this.closed)return;const t=new D(this.nodeId,{transport:new T(this.nodeId,{recordsViewerFaults:!1})});this.client=t,t.on("connect",()=>t.send({type:"hello",role:"observer",client_id:k(),attends:!1}));const i=r(async s=>{if(!(this.closed||this.client!==t)){if(s.type==="message_start"&&s.message.role==="assistant"&&(this.discardDeltas(),this.openAssistant=!0,this.block=null,this.thinking="",this.output="",this.assistantStartSequence=(await(await l()).prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(this.runId)).event_cursor),s.type==="message_update"&&s.message.role==="assistant"&&this.openAssistant){const e=s.message.content;if(!Array.isArray(e))return;const o=e.at(-1);o&&typeof o=="object"&&"type"in o&&(o.type==="toolCall"?this.block={kind:"tool_call",name:"name"in o&&typeof o.name=="string"?o.name:""}:(o.type==="text"||o.type==="thinking")&&(this.block={kind:o.type}));const d=e.filter(a=>a?.type==="thinking").map(a=>a.thinking).join(""),h=e.filter(a=>a?.type==="text").map(a=>a.text).join(""),S=d.startsWith(this.thinking)?d.slice(this.thinking.length):d,O=h.startsWith(this.output)?h.slice(this.output.length):h;this.thinking=d,this.output=h,this.pendingThinking+=S,this.pendingOutput+=O,(this.pendingThinking||this.pendingOutput)&&!this.flushTimer&&(this.flushTimer=setTimeout(()=>{this.flushDeltas().catch(a=>c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:a}))},A),this.flushTimer.unref())}s.type==="message_end"&&s.message.role==="assistant"&&(await this.flushDeltas(),this.openAssistant=!1,this.block=null)}},"handleFrame");t.on("frame",s=>{this.frames=this.frames.then(()=>i(s)).catch(e=>{this.close(),c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:e})})}),t.on("error",()=>{}),t.on("close",()=>{this.client===t&&(this.client=void 0,this.discardDeltas(),this.openAssistant=!1,this.block=null)}),t.connect()}async flushDeltas(){this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimer=void 0;const t=this.pendingThinking,i=this.pendingOutput;if(this.pendingThinking="",this.pendingOutput="",!this.openAssistant||!t&&!i)return;if(await(await l()).prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND sequence_number >= ? AND type IN ('node.output_text.done', 'node.thinking.done', 'usage') LIMIT 1").get(this.runId,this.nodeId,this.assistantStartSequence)){this.openAssistant=!1;return}t&&await p(this.runId,this.nodeId,"node.thinking.delta",{delta:t}),i&&await p(this.runId,this.nodeId,"node.output_text.delta",{delta:i})}discardDeltas(){this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimer=void 0,this.pendingThinking="",this.pendingOutput=""}close(){this.closed=!0,this.discardDeltas(),clearInterval(this.timer),u.delete(this.nodeId);const t=this.client;this.client=void 0,t?.close()}}function J(n){return u.get(n)?.assistantMessageStartSequence??null}r(J,"openAssistantMessageStartSequence");function Y(n){return u.get(n)?.openBlock??null}r(Y,"openAssistantBlock");async function G(){const n=[...u.values()];for(const t of n)t.close();await Promise.all(n.map(t=>t.settlePoll())),await Promise.all([...f.values()].map(t=>t.catch(()=>{})))}r(G,"closeRunObserversForTests");async function E(n){return await(await l()).prepare("SELECT 1 FROM nodes WHERE node_id = ? AND run_id = ? AND event_cursor IS NOT NULL").get(n,n)!==void 0}r(E,"runRootExists");const f=new Map;async function v(n,t){const i=f.get(n);if(i)return i;if(u.has(n))return;const s=L(n,t).finally(()=>f.delete(n));return f.set(n,s),s}r(v,"ensureRunObserver");async function L(n,t){if(await E(t)){if(n!==t){const i=await g(n);!await(await l()).prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'child.started' LIMIT 1").get(t,n)&&i&&Date.now()-Date.parse(i.created)<720*60*6e4&&await p(t,n,"child.started",{child_node_id:n,kind:i.kind})}await m.start(n,t)}}r(L,"startRunObserver");async function Q(){const n=new Date(Date.now()-2592e6).toISOString(),t=await(await l()).prepare(`SELECT n.node_id, n.run_id, n.pi_pid, COALESCE(n.outcome_revision, 1) AS revision, e.body_json
2
2
  FROM node_rows n
3
3
  LEFT JOIN run_events e ON n.pi_pid IS NULL AND e.run_id = n.run_id AND e.node_id = n.node_id
4
4
  AND e.type = CASE WHEN n.node_id = n.run_id THEN 'node.settled' ELSE 'child.settled' END
5
- WHERE n.run_id IS NOT NULL AND (n.pi_pid IS NOT NULL OR (n.outcome_at IS NOT NULL AND n.outcome_at > ? AND n.outcome_reason != 'parked'))`).all(s),i=new Map;for(const e of t){const o=i.get(e.node_id)??{node_id:e.node_id,run_id:e.run_id,reported:!1};e.body_json!==null&&JSON.parse(e.body_json).outcome?.revision===e.revision&&(o.reported=!0),i.set(e.node_id,o)}const n=[...i.values()].filter(e=>!e.reported);for(const e of n)try{await w(e.node_id,e.run_id),_.delete(e.node_id)}catch(o){M(e.node_id,o)}}r(G,"pollRunObservers");const _=new Map;function M(s,t){const i=t instanceof Error?t.message:String(t);_.get(s)!==i&&(_.set(s,i),h({level:"error",event:"daemon.runs.observer_failed",node_id:s,error:t}))}r(M,"reportObserverFailure");export{w as ensureRunObserver,Y as openAssistantBlock,P as openAssistantMessageStartSequence,G as pollRunObservers};
5
+ WHERE n.run_id IS NOT NULL AND (n.pi_pid IS NOT NULL OR (n.outcome_at IS NOT NULL AND n.outcome_at > ? AND n.outcome_reason != 'parked'))`).all(n),i=new Map;for(const e of t){const o=i.get(e.node_id)??{node_id:e.node_id,run_id:e.run_id,reported:!1};e.body_json!==null&&JSON.parse(e.body_json).outcome?.revision===e.revision&&(o.reported=!0),i.set(e.node_id,o)}const s=[...i.values()].filter(e=>!e.reported);for(const e of s)try{await v(e.node_id,e.run_id),_.delete(e.node_id)}catch(o){M(e.node_id,o)}}r(Q,"pollRunObservers");const _=new Map;function M(n,t){const i=t instanceof Error?t.message:String(t);_.get(n)!==i&&(_.set(n,i),c({level:"error",event:"daemon.runs.observer_failed",node_id:n,error:t}))}r(M,"reportObserverFailure");export{G as closeRunObserversForTests,v as ensureRunObserver,Y as openAssistantBlock,J as openAssistantMessageStartSequence,Q as pollRunObservers};
@@ -15,7 +15,7 @@ A provider is a service the person's runtime calls for your app. Your app asks t
15
15
  | `context` | Ask about the person from their memory, and look up public profiles | `context:<group>` | [Context](/docs/providers/context) |
16
16
  | `device` | The person's Mac: browser, files, Messages and shell | `device:<group>` | [Device](/docs/providers/device) |
17
17
 
18
- > **Want your app to have its own number and text its users?** The `sms` provider texts only the signed-in person's own phone. For your own number, use Comms: [the Comms guide for coding agents](https://comms.northlight.ai/llms.txt), and the SDK [`@north-light/comms`](https://www.npmjs.com/package/@north-light/comms) on npm.
18
+ > **Want your app to have its own number and text its users?** The `sms` provider texts only the signed-in person's own phone. For your own number, use Comms: [the Comms guide for coding agents](https://comms.northlight.ai/llms.txt), and the SDK [`@north-light/comms`](https://www.npmjs.com/package/@north-light/comms) on npm. Other apps that offer a service to developers are listed under [Services you can build on](/docs/services).
19
19
 
20
20
  ## Where names come from
21
21
 
@@ -5,7 +5,7 @@ description: Text the signed-in person's own verified phone from your app with t
5
5
 
6
6
  # Texting (sms)
7
7
 
8
- > **Want your app to have its own number and text its users?** The `sms` provider cannot: it texts only the signed-in person's own phone. Use Comms instead: [the Comms guide for coding agents](https://comms.northlight.ai/llms.txt) covers fake mode, testers and production, and the SDK is [`@north-light/comms`](https://www.npmjs.com/package/@north-light/comms) on npm.
8
+ > **Want your app to have its own number and text its users?** The `sms` provider cannot: it texts only the signed-in person's own phone. Use Comms instead: [the Comms guide for coding agents](https://comms.northlight.ai/llms.txt) covers fake mode, testers and production, and the SDK is [`@north-light/comms`](https://www.npmjs.com/package/@north-light/comms) on npm. See also [Services you can build on](/docs/services).
9
9
 
10
10
  The `sms` provider sends a text from Northlight's number to the verified phone of the person who signed in to your app. It has no recipient argument, so an app cannot text anyone else.
11
11
 
@@ -7,7 +7,12 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
7
7
 
8
8
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
9
9
 
10
- ## 0.3.533
10
+ ## 0.3.534
11
+
12
+ A cron armed by a caller with no scope limit follows the grant, and a narrowed scope pauses it.
13
+
14
+ - `POST /v1/crons` stores and returns `scopes: null` for a cron armed by a node, cron or app token with no scope limit, where it used to return a copy of the grant's scopes. A cron with `scopes: null` runs under the grant's current scopes at every fire, including scopes the grant gains later. A cron armed with an explicit scope list keeps it and is refused if the grant stops covering any of it.
15
+ - A scheduled fire refused `scope_narrowed` now pauses the cron, recurring ones included (`state: paused` in `GET /v1/crons`), and notifies whoever armed it. Resume it with `POST /v1/crons/:id/resume` once the grant covers the scope again.
11
16
 
12
17
  Three reads take optional narrowing. Additive: no request or response an existing app sends or reads changes.
13
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.533",
3
+ "version": "0.3.535",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -140,7 +140,7 @@
140
140
  },
141
141
  "license": "UNLICENSED",
142
142
  "dependencies": {
143
- "@north-light/identity": "0.3.533",
143
+ "@north-light/identity": "0.3.535",
144
144
  "@earendil-works/pi-agent-core": "1.1.0",
145
145
  "@earendil-works/pi-ai": "1.1.0",
146
146
  "@earendil-works/pi-coding-agent": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.533",
3
+ "version": "0.3.535",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.533",
3
+ "version": "0.3.535",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.533",
9
+ "version": "0.3.535",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -24,7 +24,7 @@
24
24
  "@milkdown/preset-gfm": "^7.22.3",
25
25
  "@milkdown/prose": "^7.22.3",
26
26
  "@milkdown/utils": "^7.22.3",
27
- "@north-light/identity": "0.3.533",
27
+ "@north-light/identity": "0.3.535",
28
28
  "cron-parser": "^5.6.0",
29
29
  "esbuild": "^0.27.7",
30
30
  "jose": "^6.2.12",
@@ -12455,17 +12455,17 @@
12455
12455
  },
12456
12456
  "packages/crouter-api": {
12457
12457
  "name": "@north-light/api",
12458
- "version": "0.3.533",
12458
+ "version": "0.3.535",
12459
12459
  "license": "UNLICENSED"
12460
12460
  },
12461
12461
  "packages/crouter-env-docker": {
12462
12462
  "name": "@north-light/env-docker",
12463
- "version": "0.3.533",
12463
+ "version": "0.3.535",
12464
12464
  "license": "UNLICENSED"
12465
12465
  },
12466
12466
  "packages/crouter-identity": {
12467
12467
  "name": "@north-light/identity",
12468
- "version": "0.3.533",
12468
+ "version": "0.3.535",
12469
12469
  "license": "UNLICENSED",
12470
12470
  "dependencies": {
12471
12471
  "jose": "^6.2.1"
@@ -12473,20 +12473,20 @@
12473
12473
  },
12474
12474
  "packages/crouter-plugin": {
12475
12475
  "name": "@north-light/plugin",
12476
- "version": "0.3.533",
12476
+ "version": "0.3.535",
12477
12477
  "license": "UNLICENSED",
12478
12478
  "dependencies": {
12479
- "@north-light/api": "0.3.533",
12480
- "@north-light/identity": "0.3.533"
12479
+ "@north-light/api": "0.3.535",
12480
+ "@north-light/identity": "0.3.535"
12481
12481
  }
12482
12482
  },
12483
12483
  "packages/crouter-sdk": {
12484
12484
  "name": "@north-light/sdk",
12485
- "version": "0.3.533",
12485
+ "version": "0.3.535",
12486
12486
  "license": "UNLICENSED",
12487
12487
  "dependencies": {
12488
- "@north-light/api": "0.3.533",
12489
- "@north-light/identity": "0.3.533",
12488
+ "@north-light/api": "0.3.535",
12489
+ "@north-light/identity": "0.3.535",
12490
12490
  "jose": "^6.2.1"
12491
12491
  },
12492
12492
  "bin": {
@@ -1,44 +0,0 @@
1
- import { type FlatRow, type FoldState, type SubtreeActivity } from './nav-render.js';
2
- import type { NodeMeta } from './index.js';
3
- export { PI_MAX_WIDGET_LINES, VIEWPORT_FALLBACK_ROWS, ESC, RESET, BOLD, DIM, REVERSE, BG_ATTACHED, GREEN, RED, YELLOW, CYAN, GRAY, nodeGlyph, visibleWidth, truncate, fillWidth, fillBar, fmtTokens, tokensCell, activityCell, cycleBadge, navLabel, shortId, askBadge, liveBelowBadge, isAttached, hasBrokerViewers, fetchAsksMap, graphWidgetBudget, GRAPH_HINT, type FlatRow, type FoldState, type SubtreeActivity, } from './nav-render.js';
4
- /** First manager (by created) — the UP step for the ancestry spine. */
5
- export declare function managerOf(id: string): Promise<string | undefined>;
6
- /** A kind:'human' node is a control-plane ASK (a page on the human's
7
- * screen), NOT a pi conversation — it has no session, so focusing/reviving it
8
- * boots a confused blank "you have been revived" pi. Its pending-ask signal
9
- * already rides the ⚑ badge on the ASKING node (attention.ts follows the
10
- * bridge node's parent relation), so the row carries no signal of its
11
- * own. Drop it from every navigable list (the tree, BASE reports, child counts,
12
- * subtree expansion) so it can never be selected. */
13
- export declare function isHumanAsk(id: string): Promise<boolean>;
14
- /** A node's direct children that are navigable conversations — human-ask nodes
15
- * dropped. The one place the nav chrome enumerates children. */
16
- export declare function convoChildIds(id: string): Promise<string[]>;
17
- /** Live reports (active|idle) of a node — the DOWN set in BASE. */
18
- export declare function liveReports(id: string): Promise<string[]>;
19
- /** Direct navigable children — used for the ⤳ badge (human-ask nodes excluded,
20
- * so the count matches what the tree actually shows). */
21
- export declare function childCount(id: string): Promise<number>;
22
- /** Climb first-manager edges from `self` to the ancestry root (cycle-guarded). */
23
- export declare function climbRoot(self: string): Promise<string>;
24
- /** Space-joined ids of a node's subtree (cursor-relative {subtree} var). */
25
- export declare function subtreeIds(root: string): Promise<string[]>;
26
- /** ⤳M direct-children badge — the single child-count signal, shown on any row
27
- * with navigable children (folded or not). In GRAPH the ▸ caret separately marks
28
- * a collapsed row, so this stays a pure count and never doubles as a fold badge. */
29
- export declare function childBadge(node: NodeMeta | null): Promise<string>;
30
- /** Direct children, live-first — the sibling order used both when flattening
31
- * the tree and when stepping into a subtree (`l`). Array.sort is stable, so
32
- * equal-status siblings keep their creation order. */
33
- export declare function sortedChildIds(id: string): Promise<string[]>;
34
- export declare function computeSubtreeActivity(root: string, self: string): Promise<SubtreeActivity>;
35
- /** Fold policy alone — thin wrapper over computeSubtreeActivity for the keypress
36
- * path (buildGraphModel without a precomputed set). The render path computes the
37
- * activity once and threads `expand` in, so it never recomputes here. */
38
- export declare function computeDefaultExpanded(root: string, self: string): Promise<Set<string>>;
39
- export declare function buildGraphModel(self: string, folds: FoldState, expand?: ReadonlySet<string>): Promise<FlatRow[]>;
40
- /** Render one GRAPH row. CURSOR (selected) → reverse-video bar; an ATTACHED
41
- * (human-watched) node → a coloured background bar; SELF → bold name. The
42
- * cursor outranks the attached tint when both land on the same row. The
43
- * overlay-aware glyph alone carries the live signal (status / ⟳ / ⚠). */
44
- export declare function renderGraphRow(r: FlatRow, isCursor: boolean, focused: ReadonlySet<string>, activeBelow: ReadonlyMap<string, number>, asks: Record<string, number>): Promise<string>;
@@ -1 +0,0 @@
1
- var S=Object.defineProperty;var n=(t,a)=>S(t,"name",{value:a,configurable:!0});import{getNode as v,subscribersOf as R,subscriptionsOf as I}from"./index.js";import{faultSummary as C}from"./status-glyph.js";import{statusRank as _}from"./node-order.js";import{nodeGlyph as G,navLabel as O,shortId as T,tokensCell as D,activityCell as W,cycleBadge as L,askBadge as M,liveBelowBadge as N,truncate as H,fillBar as k,fillWidth as B,isAttached as K,BOLD as P,DIM as E,RESET as p,REVERSE as V,BG_ATTACHED as Y}from"./nav-render.js";import{filterAsync as b,mapAsync as F}from"../async-array.js";import{PI_MAX_WIDGET_LINES as wt,VIEWPORT_FALLBACK_ROWS as pt,ESC as yt,RESET as $t,BOLD as mt,DIM as vt,REVERSE as Et,BG_ATTACHED as xt,GREEN as gt,RED as kt,YELLOW as Bt,CYAN as bt,GRAY as At,nodeGlyph as St,visibleWidth as Rt,truncate as It,fillWidth as Ct,fillBar as _t,fmtTokens as Gt,tokensCell as Ot,activityCell as Tt,cycleBadge as Dt,navLabel as Wt,shortId as Lt,askBadge as Mt,liveBelowBadge as Nt,isAttached as Ht,hasBrokerViewers as Kt,fetchAsksMap as Pt,graphWidgetBudget as Vt,GRAPH_HINT as Yt}from"./nav-render.js";async function q(t){try{return await I(t)}catch{return[]}}n(q,"subsOf");async function X(t){try{return await R(t)}catch{return[]}}n(X,"mgrsOf");async function j(t){return(await X(t))[0]?.node_id}n(j,"managerOf");async function z(t){return(await v(t))?.kind==="human"}n(z,"isHumanAsk");async function y(t){return await b((await q(t)).map(a=>a.node_id),async a=>!await z(a))}n(y,"convoChildIds");async function it(t){return await b(await y(t),async a=>{const s=(await v(a))?.status;return s==="active"||s==="idle"})}n(it,"liveReports");async function J(t){return(await y(t)).length}n(J,"childCount");async function Q(t){let a=t;const s=new Set([a]);for(;;){const o=await j(a);if(o===void 0||s.has(o))break;s.add(o),a=o}return a}n(Q,"climbRoot");async function lt(t){const a=[],s=new Set([t]),o=await y(t);for(;o.length>0;){const i=o.shift();if(!s.has(i)){s.add(i),a.push(i);for(const l of await y(i))s.has(l)||o.push(l)}}return a}n(lt,"subtreeIds");async function U(t){if(t===null)return"";const a=await J(t.node_id);return a>0?` ${E}\u2933${a}${p}`:""}n(U,"childBadge");async function Z(t){return(await F(await y(t),async a=>({cid:a,rank:_((await v(a))?.status)}))).sort((a,s)=>a.rank-s.rank).map(({cid:a})=>a)}n(Z,"sortedChildIds");async function tt(t,a){const s=new Set,o=new Map,i=new Set,l=n(async e=>{const u=(await v(e))?.status==="active";if(i.has(e))return{active:u?1:0,reveal:u||e===a};i.add(e);let d=0,c=!1;for(const h of await y(e)){const f=await l(h);d+=f.active,f.reveal&&(c=!0)}return o.set(e,d),c&&s.add(e),{active:d+(u?1:0),reveal:c||u||e===a}},"visit");return await l(t),{expand:s,activeBelow:o}}n(tt,"computeSubtreeActivity");async function at(t,a){return(await tt(t,a)).expand}n(at,"computeDefaultExpanded");async function ut(t,a,s){const o=await Q(t),i=s??await at(o,t),l=n(c=>a.userExpanded.has(c)?!1:a.userCollapsed.has(c)?!0:!i.has(c),"isFolded"),e=[],u=new Set,d=n(async(c,h,f,$)=>{if(u.has(c)){const r=f?"":$?"\u2514\u2500 ":"\u251C\u2500 ";e.push({id:c,hasKids:!1,isSelf:c===t,branch:h+r,cycle:!0,collapsed:!1});return}u.add(c);const w=await Z(c),m=l(c),x=f?"":$?"\u2514\u2500 ":"\u251C\u2500 ";if(e.push({id:c,hasKids:w.length>0,isSelf:c===t,branch:h+x,cycle:!1,collapsed:m}),m)return;const g=f?"":h+($?" ":"\u2502 ");for(let r=0;r<w.length;r++)await d(w[r],g,!1,r===w.length-1)},"walk");return await d(o,"",!0,!0),e}n(ut,"buildGraphModel");async function dt(t,a,s,o,i){const l=n((r,A)=>a?k(r,B(),V):A?k(r,B(),Y):H(r),"wrap");if(t.cycle){const r=`${t.branch} ${E}\u21BA ${T(t.id)}${p}`;return l(r,!1)}const e=await v(t.id),u=G(e),d=O(e,t.id),c=t.isSelf?`${P}${d}${p}`:d,h=`${E}${e?.kind??""}${p}`,f=`${E}${D(e)}${p}`,$=t.hasKids&&t.collapsed,w=!a&&$?`${E}\u25B8${p} `:" ",m=e?.fault??null,x=m!==null?` \xB7 ${C(m)}`:"",g=`${t.branch}${w}${u} ${c} ${h} ${f}${L(e)}${await U(e)}${N(e,o)}${M(t.id,i)}${W(e)}${x}`;return l(g,K(t.id,e,s))}n(dt,"renderGraphRow");export{xt as BG_ATTACHED,mt as BOLD,bt as CYAN,vt as DIM,yt as ESC,Yt as GRAPH_HINT,At as GRAY,gt as GREEN,wt as PI_MAX_WIDGET_LINES,kt as RED,$t as RESET,Et as REVERSE,pt as VIEWPORT_FALLBACK_ROWS,Bt as YELLOW,Tt as activityCell,Mt as askBadge,ut as buildGraphModel,U as childBadge,J as childCount,Q as climbRoot,at as computeDefaultExpanded,tt as computeSubtreeActivity,y as convoChildIds,Dt as cycleBadge,Pt as fetchAsksMap,_t as fillBar,Ct as fillWidth,Gt as fmtTokens,Vt as graphWidgetBudget,Kt as hasBrokerViewers,Ht as isAttached,z as isHumanAsk,Nt as liveBelowBadge,it as liveReports,j as managerOf,Wt as navLabel,St as nodeGlyph,dt as renderGraphRow,Lt as shortId,Z as sortedChildIds,lt as subtreeIds,Ot as tokensCell,It as truncate,Rt as visibleWidth};