@north-light/crouter 0.3.344 → 0.3.346

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.
@@ -306,6 +306,10 @@ An exec leaf direct-spawns its executable (no shell) only on explicit invocation
306
306
 
307
307
  An HTTP leaf is **definition plus HTTP only**: crtr fetches its manifest, renders native help from it, and executes the declared REST mapping. There is no local binary to spawn or sandbox. It carries no exec envelope: a non-streaming 2xx body is the bare result object checked against the declared `output`; a non-2xx body uses `{"error":{"code","message","field?","next?"}}`; an invalid 2xx body or an ill-formed or HTTP-reserved non-2xx error code becomes `cli_protocol_error`, while a missing or malformed non-2xx envelope becomes `cli_http_error`. Declared timeouts, bearer-token handling, and NDJSON stream relay apply.
308
308
 
309
+ ### Context for displayed files
310
+
311
+ When a local command displays file contents, call `crtr sys context expose -h`, then invoke that leaf with `--json` for the displayed files. Accept `content` only when the subprocess exits 0 and stdout parses as an object; otherwise discard its stdout and write the structured error to stderr. Put a returned block in the same prose output as the displayed files. An envelope-producing plugin captures it in a declared prose output field, never ahead of its JSON envelope. HTTP-only services do not gain local file or node access through this feature.
312
+
309
313
  ### Exec protocol
310
314
 
311
315
  For an exec leaf, crtr writes exactly one JSON request to the executable's stdin:
@@ -1,6 +1,6 @@
1
- var N=Object.defineProperty;var d=(t,a)=>N(t,"name",{value:a,configurable:!0});import{envNodeId as M}from"../../shared/env.js";import{localClient as C}from"../../api/node-transport.js";import{defineLeaf as P}from"../../core/command.js";import{memoryExtensionEffectiveCatalog as L,projectEffectiveMemoryExtensions as I}from"../../core/memory/extensions.js";import{ambientMemoryTarget as T,createMemoryDocSnapshot as B,memoryStoresInPrecedence as F,readMemoryDocContent as K,resolveMemoryDocs as O}from"../../core/memory-resolver.js";import{candidateLocation as Y,readDoc as U}from"../../core/memory/service.js";import{expandShellBlocks as W,hasShellBlocks as X,makeNodeShellRunner as $}from"../../core/runtime/shell-expansion.js";import{emptyContextExposureState as z,exposureTarget as v,loadContextExposureState as x,registerDocumentExposure as G,registerExposure as H,saveContextExposureState as E}from"../../core/substrate/injected-store.js";import{dirDedupKey as J,renderDirListing as Q}from"../../core/substrate/listings.js";import{contentReadAttachments as V}from"../../core/substrate/on-read.js";import{renderResult as Z}from"../../core/render.js";import{effectiveDocKind as ee}from"../../core/substrate/schema.js";import{MEMORY_KINDS as te,resolveReadSelector as ne,selectorParam as q}from"./shared.js";async function re(t){if(t===void 0)return null;try{return await C({autostart:!1,coldStartPollWindowMs:0}).nodeSubject(t)}catch{return null}}d(re,"fetchSubject");import{candidateLocation as ve}from"../../core/memory/service.js";const oe=P({name:"read",description:"load a memory document body by name",whenToUse:"a task in front of you matches a stored document and you already know its canonical name \u2014 read it before improvising. Resolution is exact and takes the full canonical name (a project document is `<namespace>/<local name>`); among documents sharing one name the winner is the nearest scope (node > project stack > profile > user > builtin). A directory name is a valid target too: it answers with that directory\u2019s own document, when it has one, together with its immediate listing, and walking directories is the sanctioned browse move. You name the document by its crtr identifier, never a file path \u2014 do not cat or find the markdown off disk. Reach for `crtr memory find` first when you do not yet know which document applies.",help:{name:"memory read",summary:"resolve an exact canonical name to its document body (frontmatter stripped unless --frontmatter), plus the directory listing when that name has members",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical memory name \u2014 a project document is `<namespace>/<local name>`, every other store\u2019s local names are already canonical, where a nested store's namespace includes its path under the repository root. Resolution is exact: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. A name that carries members returns its listing alongside its body; a name that is only a directory returns the listing alone."},{kind:"flag",name:"kind",type:"enum",choices:[...te],required:!1,constraint:"Narrows resolution when the name is ambiguous across kinds."},{kind:"flag",name:"frontmatter",type:"bool",required:!1,constraint:"When present, includes the YAML frontmatter in the returned body. Off by default \u2014 only the body is returned."},q("scope-read"),q("dir",{},"Reads that store alone: the name resolves against its documents only, and the listing shows its members only.")],output:[{name:"name",type:"string",required:!0,constraint:"Resolved canonical document name, or the directory name on a directory read."},{name:"local_name",type:"string",required:!1,constraint:"Store-local identity, before the store namespace composed the canonical name. Absent on a directory read."},{name:"kind",type:"string",required:!1,constraint:"Resolved kind: knowledge or preference. Absent on a directory read."},{name:"scope",type:"string",required:!1,constraint:"Scope the document was resolved from: node, project, profile, user, or builtin. Absent on a directory read."},{name:"path",type:"string",required:!1,constraint:"Absolute path to the document on disk. Revise it with `crtr memory edit`, never by editing this file \u2014 an edit records why the change happened and lands in the doc\u2019s revision history. Absent on a directory read."},{name:"store_root",type:"string",required:!1,constraint:"Absolute memory store root the document loaded from. Absent on a directory read."},{name:"physical_relative_path",type:"string",required:!1,constraint:"Store-relative physical markdown path, ordering prefixes and any `INDEX.md` kept. Absent on a directory read."},{name:"representation",type:"string",required:!1,constraint:"Physical representation of this identity: leaf-file or directory-index. Absent on a directory read."},{name:"candidates",type:"object[]",required:!1,constraint:"Present only when several physical documents share this canonical name: one row per candidate in source order, {winner, scope, store_root, physical_relative_path, path}. The winner is what this read returned; reach a non-winning one with `--dir <its project>`."},{name:"content",type:"string",required:!1,constraint:"Document body. Frontmatter stripped unless --frontmatter is set. A document may embed shell as `!`cmd`` or a ```! fenced block; each runs once per read, in the current working directory, and is replaced by its output. Read `path` off disk when you need the literal unexecuted text. May be preceded by one `<auto-loaded-context>` block carrying the doc\u2019s neighborhood listings and any docs routed to this read. Absent on a directory read."},{name:"listing",type:"string[]",required:!1,constraint:"Present when the name has members \u2014 a directory document returns its body AND this listing, a bare directory returns the listing alone: one `[[name]]: <when-and-why>` line per member doc, one bare `[[name]]` per subdirectory. Follow any line with `crtr memory read <name>`."},{name:"extensions",type:"object",required:!1,constraint:"Effective valid plugin-owned metadata, keyed by plugin namespace then field. Includes enabled declaration defaults without writing them to frontmatter; disabled and unresolved namespaces are omitted. Present only on a document read."},{name:"links",type:"string[]",required:!1,constraint:"Canonical names this document links to via `[[name]]` that resolve in the current corpus \u2014 further reading, loaded only on demand with `crtr memory read <name>`. A directory link returns that directory\u2019s listing. Omitted when the body carries no resolvable links."},{name:"follow_up",type:"string",required:!1,constraint:"Present whenever the read returned a listing or resolvable body links."}],outputKind:"object",effects:["Reads the document. A document that embeds shell (`!`cmd`` or a ```! block) runs those commands once, in the current working directory.","Records a content delivery for this node: a document whose profile project caps automatic boot and workspace-open disclosure below content is still returned whole here, and unsolicited channels stay quiet about it afterwards."]},run:d(async t=>{const a=t.name,c=t.kind,A=t.frontmatter,i=ne({scope:t.scope,dir:t.dir}),l=T();i.store===null&&F(l,i.scope,!0,!0);const r=U({target:l,scope:i.scope,store:i.store,name:a,kind:c,frontmatter:A}),n=M()||void 0;if(r.kind==="directory"){const o=n!==void 0?x(n):null,p=o===null?null:v(o,"transcript"),D=Q(r.listingByName,r.name,p,!1);return n!==void 0&&o!==null&&p!==null&&(H(p,J(r.name),"preview"),E(n,o)),{name:r.name,listing:D,follow_up:"Each line is readable with `crtr memory read <name>` \u2014 a `[[name]]: <line>` entry is a doc, a bare `[[name]]` is a subdirectory whose read browses deeper. Browse the whole inventory with `crtr memory list`."}}const{doc:e,candidates:y,content:m,links:s,listingByName:R}=r,S=ee(e),f=X(m)?await W(m,$({cwd:process.cwd()})):m,w=n!==void 0?x(n):z(),b=v(w,"transcript"),_=await re(n);G(b,e.path,e.body,"content");const h=V(e,{subject:_,target:b,byName:R,memoryTarget:l,listings:"blocks",explicit:!0});n!==void 0&&E(n,w);const j=h.blocks.length===0?f:`<auto-loaded-context>
2
- ${h.blocks.join(`
1
+ var D=Object.defineProperty;var a=(t,i)=>D(t,"name",{value:i,configurable:!0});import{envNodeId as E}from"../../shared/env.js";import{diag as v}from"../../core/io.js";import{defineLeaf as S}from"../../core/command.js";import{memoryExtensionEffectiveCatalog as N,projectEffectiveMemoryExtensions as M}from"../../core/memory/extensions.js";import{ambientMemoryTarget as P,createMemoryDocSnapshot as C,memoryStoresInPrecedence as I,readMemoryDocContent as L,resolveMemoryDocs as B}from"../../core/memory-resolver.js";import{candidateLocation as F,readDoc as T}from"../../core/memory/service.js";import{expandShellBlocks as K,hasShellBlocks as U,makeNodeShellRunner as O}from"../../core/runtime/shell-expansion.js";import{registerDocumentExposure as Y,registerExposure as X}from"../../core/substrate/injected-store.js";import{renderCliContextDelivery as x}from"../../core/substrate/cli-context-delivery.js";import{dirDedupKey as $,renderDirListing as W}from"../../core/substrate/listings.js";import{contentReadAttachments as z}from"../../core/substrate/on-read.js";import{renderResult as G}from"../../core/render.js";import{effectiveDocKind as H}from"../../core/substrate/schema.js";import{MEMORY_KINDS as J,resolveReadSelector as Q,selectorParam as q}from"./shared.js";import{candidateLocation as fe}from"../../core/memory/service.js";const V=S({name:"read",description:"load a memory document body by name",whenToUse:"a task in front of you matches a stored document and you already know its canonical name \u2014 read it before improvising. Resolution is exact and takes the full canonical name (a project document is `<namespace>/<local name>`); among documents sharing one name the winner is the nearest scope (node > project stack > profile > user > builtin). A directory name is a valid target too: it answers with that directory\u2019s own document, when it has one, together with its immediate listing, and walking directories is the sanctioned browse move. You name the document by its crtr identifier, never a file path \u2014 do not cat or find the markdown off disk. Reach for `crtr memory find` first when you do not yet know which document applies.",help:{name:"memory read",summary:"resolve an exact canonical name to its document body (frontmatter stripped unless --frontmatter), plus the directory listing when that name has members",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical memory name \u2014 a project document is `<namespace>/<local name>`, every other store\u2019s local names are already canonical, where a nested store's namespace includes its path under the repository root. Resolution is exact: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. A name that carries members returns its listing alongside its body; a name that is only a directory returns the listing alone."},{kind:"flag",name:"kind",type:"enum",choices:[...J],required:!1,constraint:"Narrows resolution when the name is ambiguous across kinds."},{kind:"flag",name:"frontmatter",type:"bool",required:!1,constraint:"When present, includes the YAML frontmatter in the returned body. Off by default \u2014 only the body is returned."},q("scope-read"),q("dir",{},"Reads that store alone: the name resolves against its documents only, and the listing shows its members only.")],output:[{name:"name",type:"string",required:!0,constraint:"Resolved canonical document name, or the directory name on a directory read."},{name:"local_name",type:"string",required:!1,constraint:"Store-local identity, before the store namespace composed the canonical name. Absent on a directory read."},{name:"kind",type:"string",required:!1,constraint:"Resolved kind: knowledge or preference. Absent on a directory read."},{name:"scope",type:"string",required:!1,constraint:"Scope the document was resolved from: node, project, profile, user, or builtin. Absent on a directory read."},{name:"path",type:"string",required:!1,constraint:"Absolute path to the document on disk. Revise it with `crtr memory edit`, never by editing this file \u2014 an edit records why the change happened and lands in the doc\u2019s revision history. Absent on a directory read."},{name:"store_root",type:"string",required:!1,constraint:"Absolute memory store root the document loaded from. Absent on a directory read."},{name:"physical_relative_path",type:"string",required:!1,constraint:"Store-relative physical markdown path, ordering prefixes and any `INDEX.md` kept. Absent on a directory read."},{name:"representation",type:"string",required:!1,constraint:"Physical representation of this identity: leaf-file or directory-index. Absent on a directory read."},{name:"candidates",type:"object[]",required:!1,constraint:"Present only when several physical documents share this canonical name: one row per candidate in source order, {winner, scope, store_root, physical_relative_path, path}. The winner is what this read returned; reach a non-winning one with `--dir <its project>`."},{name:"content",type:"string",required:!1,constraint:"Document body. Frontmatter stripped unless --frontmatter is set. A document may embed shell as `!`cmd`` or a ```! fenced block; each runs once per read, in the current working directory, and is replaced by its output. Read `path` off disk when you need the literal unexecuted text. May be preceded by one `<auto-loaded-context>` block carrying the doc\u2019s neighborhood listings and any docs routed to this read. Absent on a directory read."},{name:"listing",type:"string[]",required:!1,constraint:"Present when the name has members \u2014 a directory document returns its body AND this listing, a bare directory returns the listing alone: one `[[name]]: <when-and-why>` line per member doc, one bare `[[name]]` per subdirectory. Follow any line with `crtr memory read <name>`."},{name:"extensions",type:"object",required:!1,constraint:"Effective valid plugin-owned metadata, keyed by plugin namespace then field. Includes enabled declaration defaults without writing them to frontmatter; disabled and unresolved namespaces are omitted. Present only on a document read."},{name:"links",type:"string[]",required:!1,constraint:"Canonical names this document links to via `[[name]]` that resolve in the current corpus \u2014 further reading, loaded only on demand with `crtr memory read <name>`. A directory link returns that directory\u2019s listing. Omitted when the body carries no resolvable links."},{name:"follow_up",type:"string",required:!1,constraint:"Present whenever the read returned a listing or resolvable body links."}],outputKind:"object",effects:["Reads the document. A document that embeds shell (`!`cmd`` or a ```! block) runs those commands once, in the current working directory.","Records a content delivery for this node: a document whose profile project caps automatic boot and workspace-open disclosure below content is still returned whole here, and unsolicited channels stay quiet about it afterwards."]},run:a(async t=>{const i=t.name,c=t.kind,A=t.frontmatter,s=Q({scope:t.scope,dir:t.dir}),l=P();s.store===null&&I(l,s.scope,!0,!0);const n=T({target:l,scope:s.scope,store:s.store,name:i,kind:c,frontmatter:A}),f=E()||void 0;if(n.kind==="directory"){const o=await x({nodeId:f,allowUnavailableSubject:!0,render:a((u,r)=>{const p=W(n.listingByName,n.name,r,!1);return X(r,$(n.name),"preview"),{name:n.name,listing:p,follow_up:"Each line is readable with `crtr memory read <name>` \u2014 a `[[name]]: <line>` entry is a doc, a bare `[[name]]` is a subdirectory whose read browses deeper. Browse the whole inventory with `crtr memory list`."}},"render")});return o.diagnostic!==void 0&&v(o.diagnostic),o.result}const{doc:e,candidates:w,content:m,links:d,listingByName:R}=n,_=H(e),b=U(m)?await K(m,O({cwd:process.cwd()})):m,h=await x({nodeId:f,allowUnavailableSubject:!0,render:a((o,u)=>{Y(u,e.path,e.body,"content");const r=z(e,{subject:o,target:u,byName:R,memoryTarget:l,listings:"blocks",explicit:!0}),p=r.blocks.length===0?b:`<auto-loaded-context>
2
+ ${r.blocks.join(`
3
3
  `)}
4
4
  </auto-loaded-context>
5
5
 
6
- ${f}`,u=h.listing,g=u!==void 0&&u.length>0,k=g?s.length>0?"Each `[[name]]` line in `listing` is one of this directory\u2019s immediate members, and the `[[name]]` links in the body are further reading \u2014 follow either with `crtr memory read <name>` only when the task needs that depth.":"Each `[[name]]` line in `listing` is one of this directory\u2019s immediate members, readable with `crtr memory read <name>` \u2014 follow one only when the task needs that depth.":s.length>0?"The `[[name]]` links in the body are further reading \u2014 follow one with `crtr memory read <name>` only when the task needs that depth.":null;return{name:e.name,local_name:e.localName,kind:S,scope:e.scope,path:e.path,store_root:e.root,physical_relative_path:e.physicalRelativePath,representation:e.representation,...y.length>1?{candidates:y.map(o=>Y(o,e))}:{},content:j,...g?{listing:u}:{},extensions:I(e.frontmatter?.extensions,L(e)),...s.length>0?{links:s}:{},...k===null?{}:{follow_up:k}}},"run"),render:d(t=>{const{extensions:a,...c}=t;return Z(c,oe.help)},"render")});export{ve as candidateRow,B as createMemoryDocSnapshot,oe as readLeaf,K as readMemoryDocContent,O as resolveMemoryDocs};
6
+ ${b}`,y=r.listing,g=y!==void 0&&y.length>0,k=g?d.length>0?"Each `[[name]]` line in `listing` is one of this directory\u2019s immediate members, and the `[[name]]` links in the body are further reading \u2014 follow either with `crtr memory read <name>` only when the task needs that depth.":"Each `[[name]]` line in `listing` is one of this directory\u2019s immediate members, readable with `crtr memory read <name>` \u2014 follow one only when the task needs that depth.":d.length>0?"The `[[name]]` links in the body are further reading \u2014 follow one with `crtr memory read <name>` only when the task needs that depth.":null;return{name:e.name,local_name:e.localName,kind:_,scope:e.scope,path:e.path,store_root:e.root,physical_relative_path:e.physicalRelativePath,representation:e.representation,...w.length>1?{candidates:w.map(j=>F(j,e))}:{},content:p,...g?{listing:y}:{},extensions:M(e.frontmatter?.extensions,N(e)),...d.length>0?{links:d}:{},...k===null?{}:{follow_up:k}}},"render")});return h.diagnostic!==void 0&&v(h.diagnostic),h.result},"run"),render:a(t=>{const{extensions:i,...c}=t;return G(c,V.help)},"render")});export{fe as candidateRow,C as createMemoryDocSnapshot,V as readLeaf,L as readMemoryDocContent,B as resolveMemoryDocs};
@@ -1,2 +1 @@
1
- /** Internal read-context endpoint consumed by compiler-aware CLI integrations. */
2
1
  export declare const exposeLeaf: import("../../../core/command.js").LeafDef;
@@ -1,2 +1 @@
1
- var c=Object.defineProperty;var a=(n,e)=>c(n,"name",{value:e,configurable:!0});import{accessSync as l,constants as u,realpathSync as m,statSync as p}from"node:fs";import{resolve as f}from"node:path";import{localClient as h}from"../../../api/node-transport.js";import{defineLeaf as y}from"../../../core/command.js";import{expandTilde as x}from"../../../core/fs-utils.js";import{exposureTarget as b,loadContextExposureState as g,saveContextExposureState as v}from"../../../core/substrate/injected-store.js";import{renderOnReadDocsForSubject as w}from"../../../core/substrate/on-read.js";import{envNodeId as S}from"../../../shared/env.js";function R(n){const e=[],o=new Set;for(const r of n)try{const t=m(f(x(r)));if(l(t,u.R_OK),!p(t).isFile()||o.has(t))continue;o.add(t),e.push(t)}catch{}return e}a(R,"readableRegularFiles");const O=y({name:"expose",tier:"hidden",description:"attach read-routed memory to displayed file content",whenToUse:"an integration has displayed files and needs their read-routed memory",help:{name:"sys context expose",summary:"return read-routed memory for displayed files",params:[{kind:"flag",name:"file",type:"path",required:!0,repeatable:!0,constraint:"A displayed file. Relative paths resolve from the current directory; unreadable or non-file paths contribute nothing."}],output:[{name:"content",type:"string",required:!1,constraint:"Read-routed <auto-loaded-context> blocks, absent when no file matches or CRTR_NODE_ID is unset; empty when node context is unavailable."},{name:"diagnostic",type:"string",required:!1,constraint:"Present when node context is unavailable and no memory was attached."}],outputKind:"object",effects:["Records returned documents in the current node\u2019s transcript exposure ledger."]},run:a(async n=>{const e=S()?.trim();if(e===void 0||e==="")return{};const o=R(n.file);if(o.length===0)return{};let r;try{r=await h({autostart:!1,coldStartPollWindowMs:0}).nodeSubject(e)}catch{return{content:"",diagnostic:"Node context is unavailable; no read-routed memory was attached."}}if(r===null)return{content:"",diagnostic:"Node context is unavailable; no read-routed memory was attached."};const t=g(e),i=b(t,"transcript"),s=o.map(d=>w(r,d,i)).filter(Boolean).join(`
2
- `);return v(e,t),s===""?{}:{content:s}},"run"),render:a(n=>n.content??"","render")});export{O as exposeLeaf};
1
+ var d=Object.defineProperty;var a=(n,e)=>d(n,"name",{value:e,configurable:!0});import{accessSync as l,realpathSync as c,statSync as u,constants as m}from"node:fs";import{resolve as p}from"node:path";import{defineLeaf as f}from"../../../core/command.js";import{diag as h}from"../../../core/io.js";import{expandTilde as y}from"../../../core/fs-utils.js";import{renderAutoLoadedContext as g,renderOnReadDocBlocksForSubject as b}from"../../../core/substrate/on-read.js";import{renderCliContextDelivery as x}from"../../../core/substrate/cli-context-delivery.js";import{envNodeId as w}from"../../../shared/env.js";function R(n){const e=[],r=new Set;for(const o of n)try{const t=c(p(y(o)));if(l(t,m.R_OK),!u(t).isFile()||r.has(t))continue;r.add(t),e.push(t)}catch{}return e}a(R,"readableRegularFiles");const F=f({name:"expose",description:"attach read-routed memory to file content a local command displays",whenToUse:"a local command has successfully rendered file contents and must place the matching Read-routed memory in that same output",help:{name:"sys context expose",summary:"return read-routed memory for files displayed by a command",params:[{kind:"flag",name:"file",type:"path",required:!0,repeatable:!0,constraint:"Files whose contents the caller displays. Relative paths resolve against this process\u2019s cwd; leading ~ expands as for Read. Symlinks resolve to their target. Missing, unreadable, and non-file paths contribute nothing."}],output:[{name:"content",type:"string",required:!1,constraint:"One <auto-loaded-context> block. Absent when nothing new matches or CRTR_NODE_ID is unset. Prose output is the block alone, without a field label or acknowledgement."}],outputKind:"object",effects:["Records the returned memory documents at their matched rungs in this node\u2019s transcript exposure ledger. Inherits node identity from CRTR_NODE_ID; does not start the daemon. The caller must include the returned block in the same output as the displayed files; this is not a preview operation."]},run:a(async n=>{const e=w()?.trim();if(e===void 0||e==="")return{};const r=R(n.file);if(r.length===0)return{};const o=await x({nodeId:e,render:a((t,s)=>{if(t===null)throw new Error("context subject unexpectedly unavailable");return g(r.flatMap(i=>b(t,i,s)))},"render")});return o.diagnostic!==void 0&&h(o.diagnostic),o.result===""?{}:{content:o.result}},"run"),render:a(n=>n.content??"","render")});export{F as exposeLeaf};
@@ -1 +1 @@
1
- import{defineBranch as e}from"../../core/command.js";import{resolveLeaf as o}from"./context/resolve.js";import{docLeaf as a}from"./context/doc.js";import{sysSyspromptLeaf as t}from"./context/sysprompt.js";import{promptReviewLeaf as r}from"./context/prompt-review.js";import{adminLeaf as n}from"./context/admin.js";import{exposeLeaf as s}from"./context/expose.js";const u=e({name:"context",description:"inspect and shape what a node\u2019s memory context surface delivers",whenToUse:"you are asking what a node loads, why a document does or does not reach it, or you want to change the surfaces and caps that decide it",help:{name:"sys context",summary:"inspect and shape the memory context a node\u2019s configuration delivers",model:"A document reaches a node when one of its authored surface entries fires for an event (boot, workspace-open, read, memory-read, command, pre-command), its gates pass, and it wins its canonical name against nearer stores; the profile relationship caps the rung at boot and workspace-open only. resolve answers that forward \u2014 snapshot plus event to the delivered set. doc answers it in reverse \u2014 one document across every event. sysprompt and prompt-review read assembled prompts (a live node\u2019s captured one, and a hypothetical configuration\u2019s). admin is the interactive surface over all of it."},children:[o,a,t,r,n,s]});export{u as contextBranch};
1
+ import{defineBranch as e}from"../../core/command.js";import{resolveLeaf as o}from"./context/resolve.js";import{docLeaf as a}from"./context/doc.js";import{sysSyspromptLeaf as r}from"./context/sysprompt.js";import{promptReviewLeaf as t}from"./context/prompt-review.js";import{adminLeaf as n}from"./context/admin.js";import{exposeLeaf as s}from"./context/expose.js";const l=e({name:"context",description:"inspect and shape what a node\u2019s memory context surface delivers",whenToUse:"you are asking what a node loads, why a document does or does not reach it, or you want to change the surfaces and caps that decide it",help:{name:"sys context",summary:"inspect and shape the memory context a node\u2019s configuration delivers",model:"A document reaches a node when one of its authored surface entries fires for an event (boot, workspace-open, read, memory-read, command, pre-command), its gates pass, and it wins its canonical name against nearer stores; the profile relationship caps the rung at boot and workspace-open only. resolve simulates that delivery from a snapshot and event; expose delivers Read-routed memory for files a local command displays. doc answers in reverse \u2014 one document across every event. sysprompt and prompt-review read assembled prompts (a live node\u2019s captured one, and a hypothetical configuration\u2019s). admin is the interactive surface over all of it."},children:[o,s,a,r,t,n]});export{l as contextBranch};
@@ -0,0 +1,22 @@
1
+ import { type ContextExposureState, type ExposureTarget } from './injected-store.js';
2
+ import type { NodeConfigSubject } from './subject-fields.js';
3
+ export type CliContextDeliveryOutcome = 'stateless' | 'recorded' | 'unrecorded';
4
+ export interface CliContextDelivery<T> {
5
+ result: T;
6
+ outcome: CliContextDeliveryOutcome;
7
+ diagnostic?: string;
8
+ }
9
+ export interface CliContextDeliveryOptions<T> {
10
+ nodeId: string | undefined;
11
+ /** Explicit memory reads remain useful without a daemon: gated attachments
12
+ * skip while ungated ones render, exactly as before this shared boundary. */
13
+ allowUnavailableSubject?: boolean;
14
+ loadSubject?: (nodeId: string) => Promise<NodeConfigSubject | null>;
15
+ loadState?: (nodeId: string) => ContextExposureState;
16
+ saveState?: (nodeId: string, state: ContextExposureState) => void;
17
+ render: (subject: NodeConfigSubject | null, target: ExposureTarget) => T | Promise<T>;
18
+ }
19
+ /** Render one CLI-owned delivery batch. The renderer owns event routing and
20
+ * exposure registration; this boundary owns subject lookup and the one
21
+ * load/clone/render/save ledger transaction shared by CLI callers. */
22
+ export declare function renderCliContextDelivery<T>(opts: CliContextDeliveryOptions<T>): Promise<CliContextDelivery<T>>;
@@ -0,0 +1 @@
1
+ var s=Object.defineProperty;var n=(t,e)=>s(t,"name",{value:e,configurable:!0});import{localClient as f}from"../../api/node-transport.js";import{CrtrError as x}from"../errors.js";import{cloneContextExposureState as m,emptyContextExposureState as S,exposureTarget as c,loadContextExposureState as b,saveContextExposureState as w}from"./injected-store.js";function a(t,e){const r=e instanceof Error?e.message:e===void 0?void 0:String(e);return new x("context_delivery_failed",r===void 0?t:`${t}: ${r}`,void 0,{next:"The displayed file content remains available; retry context delivery in a live node when the runtime is reachable."})}n(a,"deliveryError");async function y(t){return await f({autostart:!1,coldStartPollWindowMs:0}).nodeSubject(t)}n(y,"nodeSubject");async function E(t){const e=t.nodeId?.trim();if(e===void 0||e==="")return{result:await t.render(null,c(S(),"transcript")),outcome:"stateless"};let r;try{r=await(t.loadSubject??y)(e)}catch(o){if(t.allowUnavailableSubject!==!0)throw a(`could not load context subject for node ${e}`,o);r=null}if(r===null&&t.allowUnavailableSubject!==!0)throw a(`could not load context subject for node ${e}`);let d;try{d=(t.loadState??b)(e)}catch(o){throw a(`could not load context exposure state for node ${e}`,o)}const l=m(d),i=await t.render(r,c(l,"transcript"));try{return(t.saveState??w)(e,l),{result:i,outcome:"recorded"}}catch(o){const u=o instanceof Error?o.message:String(o);return{result:i,outcome:"unrecorded",diagnostic:`Context was delivered but not recorded for node ${e}: ${u}`}}}n(E,"renderCliContextDelivery");export{E as renderCliContextDelivery};
@@ -54,8 +54,10 @@ export declare function contentReadAttachments(doc: Pick<SubstrateDoc, 'name' |
54
54
  blocks: string[];
55
55
  listing?: string[];
56
56
  };
57
+ export declare function renderAutoLoadedContext(blocks: readonly string[]): string;
57
58
  /** Surface docs whose `read` entries match a successfully read file, using
58
59
  * the caller-supplied daemon snapshot subject. */
60
+ export declare function renderOnReadDocBlocksForSubject(subject: NodeConfigSubject, readFilePath: string, target?: ExposureTarget, accounting?: DeliveryAccounting): string[];
59
61
  export declare function renderOnReadDocsForSubject(subject: NodeConfigSubject, readFilePath: string, target?: ExposureTarget, accounting?: DeliveryAccounting): string;
60
62
  /** Workspace boot docs using the snapshot's cwd/profile instead of a canvas
61
63
  * lookup. The shared exposure target prevents a later read from repeating a
@@ -1,9 +1,9 @@
1
- var T=Object.defineProperty;var i=(e,n)=>T(e,"name",{value:n,configurable:!0});import{sep as k}from"node:path";import{realpathOrSelf as E}from"../fs-utils.js";import{ambientMemoryTarget as M,listAllMemoryDocs as B,loadMemoryTargetView as P}from"../memory-resolver.js";import{commandEntryMatches as L,owningRootOf as S,preCommandEntryMatches as j}from"./surface-match.js";import{demoteDocumentTranscriptExposure as W,documentExposedAtOrAbove as q,emptyContextExposureState as H,exposedAtOrAbove as I,exposureTarget as K,registerDocumentExposure as V,registerExposure as z}from"./injected-store.js";import{planDelivery as b}from"./plan.js";import{parseSubstrateDoc as G,previewLine as J,rungRank as A}from"./schema.js";import{dirDedupKey as O,docListingDirs as Q,docsByName as $,isDirName as U,renderDirListing as X}from"./listings.js";import{cachedEventCorpusInclusive as Y}from"./session-cache.js";function g(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}i(g,"attr");function C(){try{return Y(B,G)}catch{return[]}}i(C,"resolvedDocs");function Z(e,n){if(n==="none")return null;let r=`kind="${g(e.kind)}" name="${g(e.name)}"`;if(n==="content"){const t=e.body.trim();if(t!=="")return`<memory ${r}>
1
+ var T=Object.defineProperty;var i=(e,n)=>T(e,"name",{value:n,configurable:!0});import{sep as w}from"node:path";import{realpathOrSelf as E}from"../fs-utils.js";import{ambientMemoryTarget as B,listAllMemoryDocs as M,loadMemoryTargetView as P}from"../memory-resolver.js";import{commandEntryMatches as j,owningRootOf as S,preCommandEntryMatches as L}from"./surface-match.js";import{demoteDocumentTranscriptExposure as W,documentExposedAtOrAbove as q,emptyContextExposureState as H,exposedAtOrAbove as I,exposureTarget as K,registerDocumentExposure as V,registerExposure as z}from"./injected-store.js";import{planDelivery as b}from"./plan.js";import{parseSubstrateDoc as G,previewLine as J,rungRank as O}from"./schema.js";import{dirDedupKey as A,docListingDirs as Q,docsByName as F,isDirName as U,renderDirListing as X}from"./listings.js";import{cachedEventCorpusInclusive as Y}from"./session-cache.js";function h(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}i(h,"attr");function C(){try{return Y(M,G)}catch{return[]}}i(C,"resolvedDocs");function Z(e,n){if(n==="none")return null;let r=`kind="${h(e.kind)}" name="${h(e.name)}"`;if(n==="content"){const t=e.body.trim();if(t!=="")return`<memory ${r}>
2
2
  ${t}
3
- </memory>`}else if(n==="preview"){const t=J(e);t!==""&&(r+=` readWhen="${g(t)}"`)}return`<memory ${r} />`}i(Z,"renderDocEnvelope");function l(e){return e.docs.filter(n=>n.winner).map(n=>({doc:n.doc,rung:n.finalRung,source:n.source}))}i(l,"planWinners");function F(e){return $(e.docs.map(({source:n})=>n))}i(F,"planListingCorpus");function _(e,n){return`<memory-listing dir="${g(e)}">
3
+ </memory>`}else if(n==="preview"){const t=J(e);t!==""&&(r+=` readWhen="${h(t)}"`)}return`<memory ${r} />`}i(Z,"renderDocEnvelope");function l(e){return e.docs.filter(n=>n.winner).map(n=>({doc:n.doc,rung:n.finalRung,source:n.source}))}i(l,"planWinners");function R(e){return F(e.docs.map(({source:n})=>n))}i(R,"planListingCorpus");function _(e,n){return`<memory-listing dir="${h(e)}">
4
4
  ${n.join(`
5
5
  `)}
6
- </memory-listing>`}i(_,"listingBlock");function pe(){return{deliveries:new Map}}i(pe,"deliveryAccounting");function N(e,n,r,t,o){if(e===void 0||r==="none")return;const c=E(n.path),a=e.deliveries.get(c);a!==void 0&&A(a.rung)>=A(r)||e.deliveries.set(c,{rung:r,...t===void 0?{}:{cause:t},...o===void 0?{}:{source:o}})}i(N,"accountDelivery");function ee(e,n){const{target:r,byName:t}=n,o=Q(t,e.name),c=o[0]===e.name,a=[];let s;for(const[f,u]of o.entries()){if(!U(t,u))continue;const m=c&&f===0;if(!m&&I(r.state,O(u),"preview"))continue;if(n.listings==="tree"){n.dirs?.push(u);continue}const d=m&&n.explicit===!0,y=X(t,u,r,!d,w=>N(n.accounting,w,"preview","directory listing attached to content delivery",w));y.length!==0&&(z(r,O(u),"preview"),d?s=y:a.push(_(u,y)))}return a.push(...ne(E(e.path),e.name,{...n,deliveryCause:"memory-read companion of content delivery"})),{blocks:a,...s===void 0?{}:{listing:s}}}i(ee,"contentReadAttachments");function p(e,n){const{target:r}=n,t=[];for(const{doc:o,rung:c,source:a}of e){if(q(r.state,o.path,o.body,c))continue;const s=Z(o,c);s!==null&&(V(r,o.path,o.body,c),N(n.accounting,o,c,n.deliveryCause,a),c==="content"&&t.push(...ee(o,{...n,deliveryCause:"memory-read companion of content delivery"}).blocks),t.push(s))}return t}i(p,"renderWinnerBlocks");function D(e,n,r,t){return{subject:e,target:n,byName:F(r),listings:"blocks",accounting:t}}i(D,"eventAttachmentOptions");function h(){return K(H(),"transcript")}i(h,"transientTranscriptTarget");function v(e){return e.length===0?"":`<auto-loaded-context>
6
+ </memory-listing>`}i(_,"listingBlock");function fe(){return{deliveries:new Map}}i(fe,"deliveryAccounting");function $(e,n,r,t,o){if(e===void 0||r==="none")return;const c=E(n.path),a=e.deliveries.get(c);a!==void 0&&O(a.rung)>=O(r)||e.deliveries.set(c,{rung:r,...t===void 0?{}:{cause:t},...o===void 0?{}:{source:o}})}i($,"accountDelivery");function ee(e,n){const{target:r,byName:t}=n,o=Q(t,e.name),c=o[0]===e.name,a=[];let s;for(const[y,u]of o.entries()){if(!U(t,u))continue;const m=c&&y===0;if(!m&&I(r.state,A(u),"preview"))continue;if(n.listings==="tree"){n.dirs?.push(u);continue}const d=m&&n.explicit===!0,g=X(t,u,r,!d,k=>$(n.accounting,k,"preview","directory listing attached to content delivery",k));g.length!==0&&(z(r,A(u),"preview"),d?s=g:a.push(_(u,g)))}return a.push(...re(E(e.path),e.name,{...n,deliveryCause:"memory-read companion of content delivery"})),{blocks:a,...s===void 0?{}:{listing:s}}}i(ee,"contentReadAttachments");function p(e,n){const{target:r}=n,t=[];for(const{doc:o,rung:c,source:a}of e){if(q(r.state,o.path,o.body,c))continue;const s=Z(o,c);s!==null&&(V(r,o.path,o.body,c),$(n.accounting,o,c,n.deliveryCause,a),c==="content"&&t.push(...ee(o,{...n,deliveryCause:"memory-read companion of content delivery"}).blocks),t.push(s))}return t}i(p,"renderWinnerBlocks");function D(e,n,r,t){return{subject:e,target:n,byName:R(r),listings:"blocks",accounting:t}}i(D,"eventAttachmentOptions");function f(){return K(H(),"transcript")}i(f,"transientTranscriptTarget");function x(e){return e.length===0?"":`<auto-loaded-context>
7
7
  ${e.join(`
8
8
  `)}
9
- </auto-loaded-context>`}i(v,"wrapAutoLoaded");function x(e,n,r,t){return t===void 0?b(e,M(),n,r,C()):b(e,t,n,r)}i(x,"eventPlan");function fe(e,n,r=h(),t){const o=x(e,"read",{absoluteFile:n});return v(p(l(o),D(o.subject,r,o,t)))}i(fe,"renderOnReadDocsForSubject");function ye(e,n,r,t=h(),o){const c={cwd:n,profileId:r,nodeId:null};let a,s;try{a=l(b(e,c,"workspace-open")),s=$(P(c,{quiet:!0}).docs)}catch{return""}return a.sort((f,u)=>{const m=S(f.doc)??"",d=S(u.doc)??"";return m.split(k).filter(Boolean).length-d.split(k).filter(Boolean).length||m.localeCompare(d)||f.doc.path.localeCompare(u.doc.path)}),v(p(a,{subject:e,target:t,byName:s,memoryTarget:c,listings:"blocks",accounting:o}))}i(ye,"renderWorkspaceOpenDocsForSubject");function ne(e,n,r){const t=x(r.subject,"memory-read",{resolvedCanonicalName:n,resolvedDocPath:e},r.memoryTarget);return p(l(t),{...r,byName:r.byName??F(t)})}i(ne,"memoryReadDocBlocks");function ge(e,n,r=h(),t){const o=x(e,"command",{command:n});return v(p(l(o),D(e,r,o,t)))}i(ge,"renderOnCommandDocsForSubject");function he(e,n,r=h(),t){const o=x(e,"pre-command",{command:n});return v(p(l(o),D(e,r,o,t)))}i(he,"renderPreCommandDocsForSubject");function re(e){return e.surfaces.some(n=>n.on==="pre-command")}i(re,"carriesPreCommandEntry");function R(e,n){const r=n==="command"?L:j;return C().some(({doc:t})=>t.surfaces.some(o=>r(o,e)))}i(R,"hasCommandSurfaceForCommand");function ve(e){return R(e,"command")}i(ve,"corpusHasCommandSurfaceForCommand");function xe(e){return R(e,"pre-command")}i(xe,"corpusHasPreCommandSurfaceForCommand");function be(e){for(const{doc:n}of C())re(n)&&W(e,n.path,n.body)}i(be,"demotePreCommandExposures");export{N as accountDelivery,ee as contentReadAttachments,ve as corpusHasCommandSurfaceForCommand,xe as corpusHasPreCommandSurfaceForCommand,pe as deliveryAccounting,be as demotePreCommandExposures,ne as memoryReadDocBlocks,ge as renderOnCommandDocsForSubject,fe as renderOnReadDocsForSubject,he as renderPreCommandDocsForSubject,ye as renderWorkspaceOpenDocsForSubject};
9
+ </auto-loaded-context>`}i(x,"renderAutoLoadedContext");function v(e,n,r,t){return t===void 0?b(e,B(),n,r,C()):b(e,t,n,r)}i(v,"eventPlan");function ne(e,n,r=f(),t){const o=v(e,"read",{absoluteFile:n});return p(l(o),D(o.subject,r,o,t))}i(ne,"renderOnReadDocBlocksForSubject");function ye(e,n,r=f(),t){return x(ne(e,n,r,t))}i(ye,"renderOnReadDocsForSubject");function ge(e,n,r,t=f(),o){const c={cwd:n,profileId:r,nodeId:null};let a,s;try{a=l(b(e,c,"workspace-open")),s=F(P(c,{quiet:!0}).docs)}catch{return""}return a.sort((y,u)=>{const m=S(y.doc)??"",d=S(u.doc)??"";return m.split(w).filter(Boolean).length-d.split(w).filter(Boolean).length||m.localeCompare(d)||y.doc.path.localeCompare(u.doc.path)}),x(p(a,{subject:e,target:t,byName:s,memoryTarget:c,listings:"blocks",accounting:o}))}i(ge,"renderWorkspaceOpenDocsForSubject");function re(e,n,r){const t=v(r.subject,"memory-read",{resolvedCanonicalName:n,resolvedDocPath:e},r.memoryTarget);return p(l(t),{...r,byName:r.byName??R(t)})}i(re,"memoryReadDocBlocks");function he(e,n,r=f(),t){const o=v(e,"command",{command:n});return x(p(l(o),D(e,r,o,t)))}i(he,"renderOnCommandDocsForSubject");function xe(e,n,r=f(),t){const o=v(e,"pre-command",{command:n});return x(p(l(o),D(e,r,o,t)))}i(xe,"renderPreCommandDocsForSubject");function te(e){return e.surfaces.some(n=>n.on==="pre-command")}i(te,"carriesPreCommandEntry");function N(e,n){const r=n==="command"?j:L;return C().some(({doc:t})=>t.surfaces.some(o=>r(o,e)))}i(N,"hasCommandSurfaceForCommand");function ve(e){return N(e,"command")}i(ve,"corpusHasCommandSurfaceForCommand");function be(e){return N(e,"pre-command")}i(be,"corpusHasPreCommandSurfaceForCommand");function Ce(e){for(const{doc:n}of C())te(n)&&W(e,n.path,n.body)}i(Ce,"demotePreCommandExposures");export{$ as accountDelivery,ee as contentReadAttachments,ve as corpusHasCommandSurfaceForCommand,be as corpusHasPreCommandSurfaceForCommand,fe as deliveryAccounting,Ce as demotePreCommandExposures,re as memoryReadDocBlocks,x as renderAutoLoadedContext,he as renderOnCommandDocsForSubject,ne as renderOnReadDocBlocksForSubject,ye as renderOnReadDocsForSubject,xe as renderPreCommandDocsForSubject,ge as renderWorkspaceOpenDocsForSubject};
@@ -1,4 +1,4 @@
1
- var H=Object.defineProperty;var p=(a,e)=>H(a,"name",{value:e,configurable:!0});import{statSync as V}from"node:fs";import{join as k}from"node:path";import{homedir as I}from"node:os";import{execFile as K}from"node:child_process";import{promisify as z}from"node:util";import{getNode as S,isWarmSpare as J,subscribersOf as F}from"../../core/canvas/index.js";import{contextDir as Q,isSafeNodeId as u}from"../../core/canvas/paths.js";import{fullName as O}from"../../core/canvas/labels.js";import{recordedPidLiveness as G}from"../../core/canvas/pid.js";import{transition as X}from"../../core/runtime/lifecycle.js";import{readFault as L,readLiveTurn as E,recordFault as Z}from"../../core/canvas/recovery.js";import{nextFaultRetry as ee}from"../../core/runtime/fault-recovery.js";import{fanDoctrineWake as W}from"../../core/runtime/close.js";import{headlessBrokerHost as v}from"../../core/runtime/host.js";import{reloadAuthLive as te}from"../../core/runtime/auth-reload.js";import{emitEvent as f}from"../../core/events/emit.js";import{operationIdContext as C}from"../../core/events/operation-id.js";import{hasUnseenWakeMail as ne,resetStaleExecutionMail as re}from"../../core/canvas/deliveries.js";import{activeBackgroundBashJobs as ie}from"../../core/bash-jobs.js";import{focusOf as se}from"../../core/runtime/placement.js";import{hasAttachedBrokerViewers as ae}from"../../core/runtime/broker/client-registry.js";import{deliverLive as oe}from"../../core/runtime/interactive-deliver.js";import{BrokerUnreachableError as le}from"../../core/runtime/broker-request.js";import{formatCard as de,PARK_SUMMARY_PROMPT as ce}from"../../shared/generated-context.js";import{brokerThresholdsForDaemon as ue}from"../../core/runtime/fleet.js";import{readConfig as fe}from"../../core/config.js";import{scopedStoreExists as pe}from"../../core/scoped-state/db.js";import{providerStore as me}from"../../core/scoped-state/paths.js";import{providerRevision as he}from"../../core/scoped-state/providers.js";import{beginPendingPark as _e,consumePendingPark as y,pendingParkIds as ge,pendingParkStartedAt as ke}from"../park-pending.js";import{shouldParkAfterSummary as ve}from"../park-activity.js";import{applyParkEvent as A,parkEvent as ye}from"./live-obligation.js";import{envNoDaemonAutostart as be,envTestUnattendedParkMs as we,parkSummaryGraceMs as Se,envUnattendedParkMs as Ae}from"../../shared/env.js";const N=z(K);function Ne(a){return E(a)?.busy===!0}p(Ne,"liveBusy");function ht(a){const e=E(a)?.progressAt;if(e==null)return null;const t=Date.parse(e);return Number.isNaN(t)?null:t}p(ht,"liveTurnProgressMs");function U(a,e){return e===null?null:`${a}:${e}`}p(U,"incarnationOf");function R(a){const e=E(a.node_id),t=e?.busy===!0&&e.progressAt!==null?Date.parse(e.progressAt):Number.NaN,s=a.launched_at===null||a.launched_at===void 0?Number.NaN:Date.parse(a.launched_at);return!Number.isFinite(t)||!Number.isFinite(s)||t<s?null:t}p(R,"busySinceForIncarnation");const B=3*6e4,q=2e4,D=20*6e4,Pe=2,Me=5*6e4;function Te(){const a=Ae();if(a!==void 0)return a;const e=fe("user").lifecycle.unattendedParkMs;return be()?we()??e:e}p(Te,"unattendedParkMsForDaemon");function Y(){return Se()}p(Y,"parkSummaryGraceMsForDaemon");function $e(a,e,t,s){return a!==!0||e!=="refresh"||t?"leave":s===null||s<B?"pending":"kill"}p($e,"yieldStallVerdict");function Ee(a){return!a.busy||a.quietForMs===null||a.quietForMs<D?"leave":a.cpuPercent===null||a.hasDescendants!==!1&&a.cpuPercent>Pe?"pending":"wedged"}p(Ee,"wedgeVerdict");function j(a){const e=new Map,t=new Map;for(const s of a.split(`
2
- `)){const i=s.trim();if(i==="")continue;const n=i.split(/\s+/);if(n.length<3)continue;const o=Number(n[0]),r=Number(n[1]),d=Number(n[2]);if(!Number.isFinite(o)||!Number.isFinite(r)||!Number.isFinite(d))continue;e.set(o,d);const c=t.get(r);c===void 0?t.set(r,[o]):c.push(o)}return{byPid:e,childrenOf:t}}p(j,"parsePsTree");function Ce(a,e){const{byPid:t,childrenOf:s}=j(a);if(!t.has(e))return null;let i=0;const n=[e],o=new Set;for(;n.length>0;){const r=n.pop();if(o.has(r))continue;o.add(r),i+=t.get(r)??0;const d=s.get(r);d!==void 0&&n.push(...d)}return i}p(Ce,"sumTreeCpu");function Re(a,e){const{byPid:t,childrenOf:s}=j(a);return t.has(e)?(s.get(e)??[]).length>0:null}p(Re,"treeHasDescendants");function De(){const a=process.env.PI_CODING_AGENT_DIR;return a!==void 0&&a!==""?a==="~"||a.startsWith("~/")?k(I(),a.slice(1)):a:k(I(),".pi","agent")}p(De,"piAgentDir");function xe(a){return[k(a,"auth.json"),k(a,"anthropic-credentials.json"),k(a,"openai-codex-credentials.json")]}p(xe,"piCredentialPaths");class _t{static{p(this,"BrokerSupervisionReconciler")}yieldStalls=new Map;wedgeNotifiedAt=new Map;fatalFaultNotifiedAt=new Map;unattendedSince=new Map;sampleTree;signal;knownCredentialRevision=null;lastOverCapWarnAt=Number.NEGATIVE_INFINITY;constructor(e={}){this.sampleTree=e.readTreeSample??(t=>this.readTreeSample(t)),this.signal=e.signal??((t,s)=>process.kill(t,s))}async runFleetPreamble(e){return await this.handleAuthReload(e),ue()}warnIfSaturated(e){if(e.fleet.size()<e.thresholds.warning||!e.lifecycle.acceptsDetachedWork())return;const t=C.fresh(()=>this.warnOverCapInTmux(e.fleet.size(),e.thresholds,e.frozen));e.lifecycle.registerDetached(t)}async run(e,t){const s=Te();for(const i of t.rows){const n=i.node_id,o=t.fleet.get(n);if(o===void 0||i.status!=="active"&&i.status!=="idle"){this.unattendedSince.delete(n);continue}await C.fresh(async()=>{try{this.handleYieldStall(i,o,t.fleet,e),await this.handleWedgeDetection(i,o,t.fleet,e),this.handleFatalFault(n),this.handleUnattendedParking(n,S(n),o.pid,e,t,s)}catch(r){this.unattendedSince.delete(n),f({level:"error",event:"broker.supervision.failed",...u(n)?{node_id:n}:{fields:{affected_node_id:n}},error:r})}})}for(const i of this.unattendedSince.keys())t.fleet.has(i)||this.unattendedSince.delete(i);for(const i of ge())t.fleet.has(i)||y(i);re(e)}parkResetCondition(e,t,s,i){if(t.pi_session_id==null||t.pi_pid!==s||t.status==="done"||t.status==="canceled"||t.status==="dead"||t.intent!==null||ie(Q(e)).length>0||se(e)!==null||ae(e))return!0;const n=L(e);return n!==null&&n.retry.disposition==="auto"&&n.retry.by==="daemon"&&(n.kind==="connection"||n.kind==="rate-limit"||n.kind==="overloaded")&&(n.retry.nextAt!==void 0||ee(n,i)!==null)?!0:ne(e)}handleUnattendedParking(e,t,s,i,n,o){if(J(e)){this.unattendedSince.delete(e),y(e);return}if(t===null){this.unattendedSince.delete(e),y(e);return}const r=this.parkResetCondition(e,t,s,i),d=ke(e);if(d!==void 0){if(i-d<Y())return;const m=y(e);if(m===void 0||(this.unattendedSince.delete(e),!ve(e,t,m)))return;f({level:"warn",event:"broker.park.summary_expired",...u(e)?{node_id:e}:{},fields:{...u(e)?{}:{affected_node_id:e},grace_ms:Y()}}),A(e,"park"),v.teardown(e);return}if(r||Ne(e)){this.unattendedSince.delete(e);return}const c=this.unattendedSince.get(e);if(c===void 0){this.unattendedSince.set(e,i);return}if(i-c<o)return;this.unattendedSince.delete(e);const l=ye(e,t.lifecycle);if(l==="release"){A(e,l),v.teardown(e);return}if(l==="cancel"){A(e,l),v.teardown(e);return}n.lifecycle.acceptsDetachedWork()&&(_e(e,i),n.lifecycle.registerDetached(this.requestParkSummary(e)))}async requestParkSummary(e){try{const t=await oe(e,de("park",{},ce),{origin:{type:"runtime",kind:"park"}});f({level:"info",event:"broker.park.summary_requested",...u(e)?{node_id:e}:{},fields:{...u(e)?{}:{affected_node_id:e},route:t}})}catch(t){if(y(e)===void 0)return;if(t instanceof le){A(e,"park"),v.teardown(e);return}f({level:"error",event:"broker.park.summary_failed",...u(e)?{node_id:e}:{fields:{affected_node_id:e}},error:t})}}async warnOverCapInTmux(e,t,s){const i=Date.now();if(i-this.lastOverCapWarnAt<Me)return;this.lastOverCapWarnAt=i;const{warning:n,automaticReviveCap:o}=t,r=s.stalled+s.waitingOnMail;f({level:"warn",event:"broker.capacity.warning",fields:{live_count:e,warning_threshold:n,hard_cap:o,frozen_stalled:s.stalled,frozen_waiting_on_mail:s.waitingOnMail}});const d=r===0?"":` \u2014 ${r} frozen (${s.stalled} stalled, ${s.waitingOnMail} waiting on mail)`,c=`\u26A0 crtrd: ${e} live brokers (warning ${n}; hard cap ${o})${d} \u2014 possible broker leak. ps -ax | grep broker-cli.js`;try{const l=await N("tmux",["list-clients","-F","#{client_name}"],{encoding:"utf8",timeout:2e3});if(typeof l.stdout!="string")return;await Promise.allSettled(l.stdout.split(`
3
- `).map(async m=>{if(m!=="")try{await N("tmux",["display-message","-C","-d","10000","-c",m,c],{encoding:"utf8",timeout:2e3})}catch{try{await N("tmux",["display-message","-c",m,c],{encoding:"utf8",timeout:2e3})}catch(_){f({level:"error",event:"broker.capacity.tmux_display_failed",error:_,fields:{client:m}})}}}))}catch(l){f({level:"error",event:"broker.capacity.tmux_list_clients_failed",error:l})}}handleYieldStall(e,t,s,i){const n=e.node_id,o=U(t.pid,e.pi_pid_identity);if(e.intent!=="refresh"||R(e)!==null||o===null){o!==null&&this.yieldStalls.delete(o);return}let r=this.yieldStalls.get(o);if(r===void 0){r={since:i},this.yieldStalls.set(o,r);return}if($e(!0,e.intent,!1,i-r.since)!=="kill")return;const d=S(n);if(!(s.get(n)!==t||d?.pi_pid!==t.pid||d.pi_pid_identity!==e.pi_pid_identity||G(t.pid,e.pi_pid_identity)!=="alive"))if(r.termedAt===void 0){if(!this.signalYieldStall(n,t.pid,"SIGTERM"))return;r.termedAt=i,f({level:"warn",event:"broker.yield_stall.sigterm",...u(n)?{node_id:n}:{},fields:{...u(n)?{}:{affected_node_id:n},pid:t.pid,elapsed_ms:i-r.since,grace_ms:B}})}else i-r.termedAt>=q&&this.signalYieldStall(n,t.pid,"SIGKILL")&&f({level:"warn",event:"broker.yield_stall.sigkill",...u(n)?{node_id:n}:{},fields:{...u(n)?{}:{affected_node_id:n},pid:t.pid,elapsed_ms:i-r.termedAt,grace_ms:q,stall_elapsed_ms:i-r.since}})}signalYieldStall(e,t,s){try{return this.signal(t,s),!0}catch(i){return f({level:"error",event:"broker.yield_stall.signal_failed",...u(e)?{node_id:e}:{},error:i,fields:{...u(e)?{}:{affected_node_id:e},pid:t,signal:s}}),!1}}async readTreeSample(e){try{const{stdout:t}=await N("ps",["-ax","-o","pid=,ppid=,pcpu="],{encoding:"utf8",timeout:2e3});if(typeof t!="string")return null;const s=Ce(t,e),i=Re(t,e);return s===null||i===null?null:{cpuPercent:s,hasDescendants:i}}catch{return null}}async handleWedgeDetection(e,t,s,i){const n=e.node_id,o=U(t.pid,e.pi_pid_identity),r=R(e);if(r===null||o===null){o!==null&&this.wedgeNotifiedAt.delete(o);return}const d=i-r,c=d>=D?await this.sampleTree(t.pid):null,l=S(n),m=l===null?null:R(l);if(s.get(n)!==t||l===null||l.pi_pid!==t.pid||l.pi_pid_identity!==e.pi_pid_identity||m!==r)return;const _=c?.cpuPercent??null,x=c?.hasDescendants??null;if(Ee({busy:!0,quietForMs:d,cpuPercent:_,hasDescendants:x})!=="wedged"||this.wedgeNotifiedAt.get(o)===r)return;this.wedgeNotifiedAt.set(o,r);const b=O(l),w=Math.round((d??D)/6e4),P=x??!0,g=!P&&G(t.pid,e.pi_pid_identity)!=="alive"?"pid_identity_unproven":null;let M=!1,T=!1,h;if(P)h=`Child wedged \u2014 ${b} (${n}) is alive and mid-turn but has shown NO engine progress for ${w}+ minutes and its process is near-idle. It will NOT recover on its own \u2014 read \`crtr memory read wedged-child-on-runaway-bash\` and kill the runaway SUBPROCESS (not this node) so its turn can resume with no lost context.`;else if(g!==null)h=`Child wedged \u2014 ${b} (${n}) is alive and mid-turn but has shown NO engine progress for ${w}+ minutes and it has no subprocess to kill (the engine itself stalled). The daemon did not SIGTERM the broker because its PID identity cannot be confirmed alive.`;else{M=!0;try{this.signal(t.pid,"SIGTERM"),T=!0,h=`Child wedged \u2014 ${b} (${n}) is alive and mid-turn but has shown NO engine progress for ${w}+ minutes and it has no subprocess to kill (the engine itself stalled). The daemon sent SIGTERM to the broker and will resume a cleanly aborted session with a continuation, or start a recovery cycle if the abort cannot settle.`}catch($){f({level:"error",event:"broker.wedge.signal_failed",...u(n)?{node_id:n}:{},error:$,fields:{...u(n)?{}:{affected_node_id:n},pid:t.pid,signal:"SIGTERM"}}),h=`Child wedged \u2014 ${b} (${n}) is alive and mid-turn but has shown NO engine progress for ${w}+ minutes and it has no subprocess to kill (the engine itself stalled). The daemon attempted SIGTERM to the broker, but the signal call failed.`}}f({level:"error",event:"broker.wedge.detected",...u(n)?{node_id:n}:{},fields:{...u(n)?{}:{affected_node_id:n},pid:t.pid,quiet_ms:d,cpu_percent:_,has_descendants:P,kick_attempted:M,kicked:T,...g===null?{}:{kick_suppression_reason:g}}});try{Z(n,{link:"daemon\u2192node",op:"engine wedge",kind:"wedged",retry:{disposition:"manual"},message:h})}catch($){f({level:"error",event:"broker.wedge.fault_record_failed",...u(n)?{node_id:n}:{fields:{affected_node_id:n}},error:$})}W(n,F(n),h,{reason:"child-wedged",child:n,quiet_ms:d,cpu_percent:_,kick_attempted:M,kicked:T,...g===null?{}:{kick_suppression_reason:g}})}handleFatalFault(e){const t=L(e);if(t===null){this.fatalFaultNotifiedAt.delete(e);return}if(t.retry.disposition!=="fatal"||t.link!=="pi\u2192provider"||this.fatalFaultNotifiedAt.get(e)===t.since)return;this.fatalFaultNotifiedAt.set(e,t.since);const s=S(e),i=s!==null?O(s):e,n=t.kind==="auth",o=(t.message.split(`
4
- `)[0]??"").slice(0,200),r=t.providerError,d=r===void 0?"":` Provider: ${r.provider}/${r.model}.`+(r.status===void 0?"":` HTTP status: ${r.status}.`)+(r.errno===void 0?"":` Errno: ${r.errno}.`)+(r.code===void 0?"":` Code: ${r.code}.`)+(r.body===void 0?"":` Body: ${r.body.slice(0,200)}.`),c=t.kind==="context-overflow",l=c?`Child failed \u2014 ${i} (${e}) exceeded its model context window and is now dead. Shrink the task or loaded context, or choose a larger-context model before spawning it again. Error: ${o}`:`Child hit a fatal provider fault \u2014 ${i} (${e}) got a non-retryable ${t.kind} error and will NOT recover on its own; it stays alive, and waking it clears the fault as soon as one turn completes \u2014 a wake that hits the same cause fails again. `+(n?"Its provider credential is dead \u2014 re-authenticate (`/login`, or re-auth crtr); the child recovers on its next wake once the auth reloads. ":"")+`Error: ${o}.${d}`;if(c)try{X(e,"crash",{reason:"context_overflow",outcome:{detail:{schema:"crtr.node-outcome-detail/v1",message:t.message,fault_kind:"context-overflow",error_class:"context_overflow"}}}),v.teardown(e)}catch{}f({level:"error",event:"fault.fatal.fanout",...u(e)?{node_id:e}:{},fields:{...u(e)?{}:{affected_node_id:e},kind:t.kind,link:t.link,terminal_overflow:c}}),W(e,F(e),l,{reason:c?"child-context-overflow":"child-fault-fatal",child:e,kind:t.kind,message:o,...r===void 0?{}:{provider_error:r}})}async handleAuthReload(e){const t=De();let s=0;for(const r of xe(t))try{s=Math.max(s,V(r).mtimeMs)}catch{}let i=0;const n=me(t);pe(n)&&(i=he(t));const o=`${s}:${i}`;if(this.knownCredentialRevision===null){this.knownCredentialRevision=o;return}o!==this.knownCredentialRevision&&(this.knownCredentialRevision=o,await C.fresh(async()=>{try{const r=Array.from(e.entries(),([l])=>l);if(r.length===0)return;const c=(await Promise.allSettled(r.map(async l=>{try{await te(l)}catch(m){throw f({level:"error",event:"auth.reload.broker_failed",...u(l)?{node_id:l}:{fields:{broker_id:l}},error:m}),m}}))).filter(l=>l.status==="fulfilled").length;f({level:"info",event:"auth.reload.completed",outcome:c===r.length?"succeeded":"failed",fields:{broker_count:r.length,succeeded_count:c,failed_count:r.length-c}})}catch(r){f({level:"error",event:"auth.reload.failed",error:r})}}))}}export{_t as BrokerSupervisionReconciler,Pe as WEDGE_CPU_MAX_PERCENT,D as WEDGE_QUIET_MS,B as YIELD_STALL_GRACE_MS,Y as parkSummaryGraceMsForDaemon,Ce as sumTreeCpu,Re as treeHasDescendants,Te as unattendedParkMsForDaemon,Ee as wedgeVerdict,$e as yieldStallVerdict};
1
+ var H=Object.defineProperty;var p=(s,e)=>H(s,"name",{value:e,configurable:!0});import{statSync as V}from"node:fs";import{join as k}from"node:path";import{homedir as I}from"node:os";import{execFile as K}from"node:child_process";import{promisify as z}from"node:util";import{getNode as A,isWarmSpare as J,subscribersOf as x}from"../../core/canvas/index.js";import{contextDir as Q,isSafeNodeId as u}from"../../core/canvas/paths.js";import{fullName as O}from"../../core/canvas/labels.js";import{recordedPidLiveness as G}from"../../core/canvas/pid.js";import{transition as X}from"../../core/runtime/lifecycle.js";import{readFault as L,readLiveTurn as C,recordFault as Z}from"../../core/canvas/recovery.js";import{errorClassFromFault as ee}from"../../core/fault-classifier.js";import{nextFaultRetry as te}from"../../core/runtime/fault-recovery.js";import{fanDoctrineWake as W}from"../../core/runtime/close.js";import{headlessBrokerHost as y}from"../../core/runtime/host.js";import{reloadAuthLive as ne}from"../../core/runtime/auth-reload.js";import{emitEvent as f}from"../../core/events/emit.js";import{operationIdContext as R}from"../../core/events/operation-id.js";import{hasUnseenWakeMail as re,resetStaleExecutionMail as ie}from"../../core/canvas/deliveries.js";import{activeBackgroundBashJobs as ae}from"../../core/bash-jobs.js";import{focusOf as se}from"../../core/runtime/placement.js";import{hasAttachedBrokerViewers as oe}from"../../core/runtime/broker/client-registry.js";import{deliverLive as le}from"../../core/runtime/interactive-deliver.js";import{BrokerUnreachableError as de}from"../../core/runtime/broker-request.js";import{formatCard as ce,PARK_SUMMARY_PROMPT as ue}from"../../shared/generated-context.js";import{brokerThresholdsForDaemon as fe}from"../../core/runtime/fleet.js";import{readConfig as pe}from"../../core/config.js";import{scopedStoreExists as me}from"../../core/scoped-state/db.js";import{providerStore as he}from"../../core/scoped-state/paths.js";import{providerRevision as _e}from"../../core/scoped-state/providers.js";import{beginPendingPark as ge,consumePendingPark as b,pendingParkIds as ve,pendingParkStartedAt as ke}from"../park-pending.js";import{shouldParkAfterSummary as ye}from"../park-activity.js";import{applyParkEvent as N,parkEvent as be}from"./live-obligation.js";import{envNoDaemonAutostart as we,envTestUnattendedParkMs as Se,parkSummaryGraceMs as Ae,envUnattendedParkMs as Ne}from"../../shared/env.js";const P=z(K);function Pe(s){return C(s)?.busy===!0}p(Pe,"liveBusy");function gt(s){const e=C(s)?.progressAt;if(e==null)return null;const t=Date.parse(e);return Number.isNaN(t)?null:t}p(gt,"liveTurnProgressMs");function B(s,e){return e===null?null:`${s}:${e}`}p(B,"incarnationOf");function F(s){const e=C(s.node_id),t=e?.busy===!0&&e.progressAt!==null?Date.parse(e.progressAt):Number.NaN,a=s.launched_at===null||s.launched_at===void 0?Number.NaN:Date.parse(s.launched_at);return!Number.isFinite(t)||!Number.isFinite(a)||t<a?null:t}p(F,"busySinceForIncarnation");const U=3*6e4,q=2e4,D=20*6e4,Me=2,Te=5*6e4;function $e(){const s=Ne();if(s!==void 0)return s;const e=pe("user").lifecycle.unattendedParkMs;return we()?Se()??e:e}p($e,"unattendedParkMsForDaemon");function Y(){return Ae()}p(Y,"parkSummaryGraceMsForDaemon");function Ee(s,e,t,a){return s!==!0||e!=="refresh"||t?"leave":a===null||a<U?"pending":"kill"}p(Ee,"yieldStallVerdict");function Ce(s){return!s.busy||s.quietForMs===null||s.quietForMs<D?"leave":s.cpuPercent===null||s.hasDescendants!==!1&&s.cpuPercent>Me?"pending":"wedged"}p(Ce,"wedgeVerdict");function j(s){const e=new Map,t=new Map;for(const a of s.split(`
2
+ `)){const i=a.trim();if(i==="")continue;const n=i.split(/\s+/);if(n.length<3)continue;const o=Number(n[0]),r=Number(n[1]),d=Number(n[2]);if(!Number.isFinite(o)||!Number.isFinite(r)||!Number.isFinite(d))continue;e.set(o,d);const c=t.get(r);c===void 0?t.set(r,[o]):c.push(o)}return{byPid:e,childrenOf:t}}p(j,"parsePsTree");function Re(s,e){const{byPid:t,childrenOf:a}=j(s);if(!t.has(e))return null;let i=0;const n=[e],o=new Set;for(;n.length>0;){const r=n.pop();if(o.has(r))continue;o.add(r),i+=t.get(r)??0;const d=a.get(r);d!==void 0&&n.push(...d)}return i}p(Re,"sumTreeCpu");function Fe(s,e){const{byPid:t,childrenOf:a}=j(s);return t.has(e)?(a.get(e)??[]).length>0:null}p(Fe,"treeHasDescendants");function De(){const s=process.env.PI_CODING_AGENT_DIR;return s!==void 0&&s!==""?s==="~"||s.startsWith("~/")?k(I(),s.slice(1)):s:k(I(),".pi","agent")}p(De,"piAgentDir");function Ie(s){return[k(s,"auth.json"),k(s,"anthropic-credentials.json"),k(s,"openai-codex-credentials.json")]}p(Ie,"piCredentialPaths");class vt{static{p(this,"BrokerSupervisionReconciler")}yieldStalls=new Map;wedgeNotifiedAt=new Map;fatalFaultNotifiedAt=new Map;unattendedSince=new Map;sampleTree;signal;knownCredentialRevision=null;lastOverCapWarnAt=Number.NEGATIVE_INFINITY;constructor(e={}){this.sampleTree=e.readTreeSample??(t=>this.readTreeSample(t)),this.signal=e.signal??((t,a)=>process.kill(t,a))}async runFleetPreamble(e){return await this.handleAuthReload(e),fe()}warnIfSaturated(e){if(e.fleet.size()<e.thresholds.warning||!e.lifecycle.acceptsDetachedWork())return;const t=R.fresh(()=>this.warnOverCapInTmux(e.fleet.size(),e.thresholds,e.frozen));e.lifecycle.registerDetached(t)}async run(e,t){const a=$e();for(const i of t.rows){const n=i.node_id,o=t.fleet.get(n);if(o===void 0||i.status!=="active"&&i.status!=="idle"){this.unattendedSince.delete(n);continue}await R.fresh(async()=>{try{this.handleYieldStall(i,o,t.fleet,e),await this.handleWedgeDetection(i,o,t.fleet,e),this.handleFatalFault(n),this.handleUnattendedParking(n,A(n),o.pid,e,t,a)}catch(r){this.unattendedSince.delete(n),f({level:"error",event:"broker.supervision.failed",...u(n)?{node_id:n}:{fields:{affected_node_id:n}},error:r})}})}for(const i of this.unattendedSince.keys())t.fleet.has(i)||this.unattendedSince.delete(i);for(const i of ve())t.fleet.has(i)||b(i);ie(e)}parkResetCondition(e,t,a,i){if(t.pi_session_id==null||t.pi_pid!==a||t.status==="done"||t.status==="canceled"||t.status==="dead"||t.intent!==null||ae(Q(e)).length>0||se(e)!==null||oe(e))return!0;const n=L(e);return n!==null&&n.retry.disposition==="auto"&&n.retry.by==="daemon"&&(n.kind==="connection"||n.kind==="rate-limit"||n.kind==="overloaded")&&(n.retry.nextAt!==void 0||te(n,i)!==null)?!0:re(e)}handleUnattendedParking(e,t,a,i,n,o){if(J(e)){this.unattendedSince.delete(e),b(e);return}if(t===null){this.unattendedSince.delete(e),b(e);return}const r=this.parkResetCondition(e,t,a,i),d=ke(e);if(d!==void 0){if(i-d<Y())return;const m=b(e);if(m===void 0||(this.unattendedSince.delete(e),!ye(e,t,m)))return;f({level:"warn",event:"broker.park.summary_expired",...u(e)?{node_id:e}:{},fields:{...u(e)?{}:{affected_node_id:e},grace_ms:Y()}}),N(e,"park"),y.teardown(e);return}if(r||Pe(e)){this.unattendedSince.delete(e);return}const c=this.unattendedSince.get(e);if(c===void 0){this.unattendedSince.set(e,i);return}if(i-c<o)return;this.unattendedSince.delete(e);const l=be(e,t.lifecycle);if(l==="release"){N(e,l),y.teardown(e);return}if(l==="cancel"){N(e,l),y.teardown(e);return}n.lifecycle.acceptsDetachedWork()&&(ge(e,i),n.lifecycle.registerDetached(this.requestParkSummary(e)))}async requestParkSummary(e){try{const t=await le(e,ce("park",{},ue),{origin:{type:"runtime",kind:"park"}});f({level:"info",event:"broker.park.summary_requested",...u(e)?{node_id:e}:{},fields:{...u(e)?{}:{affected_node_id:e},route:t}})}catch(t){if(b(e)===void 0)return;if(t instanceof de){N(e,"park"),y.teardown(e);return}f({level:"error",event:"broker.park.summary_failed",...u(e)?{node_id:e}:{fields:{affected_node_id:e}},error:t})}}async warnOverCapInTmux(e,t,a){const i=Date.now();if(i-this.lastOverCapWarnAt<Te)return;this.lastOverCapWarnAt=i;const{warning:n,automaticReviveCap:o}=t,r=a.stalled+a.waitingOnMail;f({level:"warn",event:"broker.capacity.warning",fields:{live_count:e,warning_threshold:n,hard_cap:o,frozen_stalled:a.stalled,frozen_waiting_on_mail:a.waitingOnMail}});const d=r===0?"":` \u2014 ${r} frozen (${a.stalled} stalled, ${a.waitingOnMail} waiting on mail)`,c=`\u26A0 crtrd: ${e} live brokers (warning ${n}; hard cap ${o})${d} \u2014 possible broker leak. ps -ax | grep broker-cli.js`;try{const l=await P("tmux",["list-clients","-F","#{client_name}"],{encoding:"utf8",timeout:2e3});if(typeof l.stdout!="string")return;await Promise.allSettled(l.stdout.split(`
3
+ `).map(async m=>{if(m!=="")try{await P("tmux",["display-message","-C","-d","10000","-c",m,c],{encoding:"utf8",timeout:2e3})}catch{try{await P("tmux",["display-message","-c",m,c],{encoding:"utf8",timeout:2e3})}catch(h){f({level:"error",event:"broker.capacity.tmux_display_failed",error:h,fields:{client:m}})}}}))}catch(l){f({level:"error",event:"broker.capacity.tmux_list_clients_failed",error:l})}}handleYieldStall(e,t,a,i){const n=e.node_id,o=B(t.pid,e.pi_pid_identity);if(e.intent!=="refresh"||F(e)!==null||o===null){o!==null&&this.yieldStalls.delete(o);return}let r=this.yieldStalls.get(o);if(r===void 0){r={since:i},this.yieldStalls.set(o,r);return}if(Ee(!0,e.intent,!1,i-r.since)!=="kill")return;const d=A(n);if(!(a.get(n)!==t||d?.pi_pid!==t.pid||d.pi_pid_identity!==e.pi_pid_identity||G(t.pid,e.pi_pid_identity)!=="alive"))if(r.termedAt===void 0){if(!this.signalYieldStall(n,t.pid,"SIGTERM"))return;r.termedAt=i,f({level:"warn",event:"broker.yield_stall.sigterm",...u(n)?{node_id:n}:{},fields:{...u(n)?{}:{affected_node_id:n},pid:t.pid,elapsed_ms:i-r.since,grace_ms:U}})}else i-r.termedAt>=q&&this.signalYieldStall(n,t.pid,"SIGKILL")&&f({level:"warn",event:"broker.yield_stall.sigkill",...u(n)?{node_id:n}:{},fields:{...u(n)?{}:{affected_node_id:n},pid:t.pid,elapsed_ms:i-r.termedAt,grace_ms:q,stall_elapsed_ms:i-r.since}})}signalYieldStall(e,t,a){try{return this.signal(t,a),!0}catch(i){return f({level:"error",event:"broker.yield_stall.signal_failed",...u(e)?{node_id:e}:{},error:i,fields:{...u(e)?{}:{affected_node_id:e},pid:t,signal:a}}),!1}}async readTreeSample(e){try{const{stdout:t}=await P("ps",["-ax","-o","pid=,ppid=,pcpu="],{encoding:"utf8",timeout:2e3});if(typeof t!="string")return null;const a=Re(t,e),i=Fe(t,e);return a===null||i===null?null:{cpuPercent:a,hasDescendants:i}}catch{return null}}async handleWedgeDetection(e,t,a,i){const n=e.node_id,o=B(t.pid,e.pi_pid_identity),r=F(e);if(r===null||o===null){o!==null&&this.wedgeNotifiedAt.delete(o);return}const d=i-r,c=d>=D?await this.sampleTree(t.pid):null,l=A(n),m=l===null?null:F(l);if(a.get(n)!==t||l===null||l.pi_pid!==t.pid||l.pi_pid_identity!==e.pi_pid_identity||m!==r)return;const h=c?.cpuPercent??null,_=c?.hasDescendants??null;if(Ce({busy:!0,quietForMs:d,cpuPercent:h,hasDescendants:_})!=="wedged"||this.wedgeNotifiedAt.get(o)===r)return;this.wedgeNotifiedAt.set(o,r);const w=O(l),S=Math.round((d??D)/6e4),M=_??!0,v=!M&&G(t.pid,e.pi_pid_identity)!=="alive"?"pid_identity_unproven":null;let T=!1,$=!1,g;if(M)g=`Child wedged \u2014 ${w} (${n}) is alive and mid-turn but has shown NO engine progress for ${S}+ minutes and its process is near-idle. It will NOT recover on its own \u2014 read \`crtr memory read wedged-child-on-runaway-bash\` and kill the runaway SUBPROCESS (not this node) so its turn can resume with no lost context.`;else if(v!==null)g=`Child wedged \u2014 ${w} (${n}) is alive and mid-turn but has shown NO engine progress for ${S}+ minutes and it has no subprocess to kill (the engine itself stalled). The daemon did not SIGTERM the broker because its PID identity cannot be confirmed alive.`;else{T=!0;try{this.signal(t.pid,"SIGTERM"),$=!0,g=`Child wedged \u2014 ${w} (${n}) is alive and mid-turn but has shown NO engine progress for ${S}+ minutes and it has no subprocess to kill (the engine itself stalled). The daemon sent SIGTERM to the broker and will resume a cleanly aborted session with a continuation, or start a recovery cycle if the abort cannot settle.`}catch(E){f({level:"error",event:"broker.wedge.signal_failed",...u(n)?{node_id:n}:{},error:E,fields:{...u(n)?{}:{affected_node_id:n},pid:t.pid,signal:"SIGTERM"}}),g=`Child wedged \u2014 ${w} (${n}) is alive and mid-turn but has shown NO engine progress for ${S}+ minutes and it has no subprocess to kill (the engine itself stalled). The daemon attempted SIGTERM to the broker, but the signal call failed.`}}f({level:"error",event:"broker.wedge.detected",...u(n)?{node_id:n}:{},fields:{...u(n)?{}:{affected_node_id:n},pid:t.pid,quiet_ms:d,cpu_percent:h,has_descendants:M,kick_attempted:T,kicked:$,...v===null?{}:{kick_suppression_reason:v}}});try{Z(n,{link:"daemon\u2192node",op:"engine wedge",kind:"wedged",retry:{disposition:"manual"},message:g})}catch(E){f({level:"error",event:"broker.wedge.fault_record_failed",...u(n)?{node_id:n}:{fields:{affected_node_id:n}},error:E})}W(n,x(n),g,{reason:"child-wedged",child:n,quiet_ms:d,cpu_percent:h,kick_attempted:T,kicked:$,...v===null?{}:{kick_suppression_reason:v}})}handleFatalFault(e){const t=L(e);if(t===null){this.fatalFaultNotifiedAt.delete(e);return}if(t.retry.disposition!=="fatal"||t.link!=="pi\u2192provider"||this.fatalFaultNotifiedAt.get(e)===t.since)return;const a=A(e),i=a!==null?O(a):e,n=t.kind==="auth",o=(t.message.split(`
4
+ `)[0]??"").slice(0,200),r=t.providerError,d=r===void 0?"":` Provider: ${r.provider}/${r.model}.`+(r.status===void 0?"":` HTTP status: ${r.status}.`)+(r.errno===void 0?"":` Errno: ${r.errno}.`)+(r.code===void 0?"":` Code: ${r.code}.`)+(r.body===void 0?"":` Body: ${r.body.slice(0,200)}.`),c=t.kind==="context-overflow",l=a?.parent==null&&a?.lifecycle==="terminal",m=c||l,h=c?`Child failed \u2014 ${i} (${e}) exceeded its model context window and is now dead. Shrink the task or loaded context, or choose a larger-context model before spawning it again. Error: ${o}`:`Child hit a fatal provider fault \u2014 ${i} (${e}) got a non-retryable ${t.kind} error and will NOT recover on its own; `+(l?"its terminal run has failed. ":"it stays alive, and waking it clears the fault as soon as one turn completes \u2014 a wake that hits the same cause fails again. "+(n?"Its provider credential is dead \u2014 re-authenticate (`/login`, or re-auth crtr); the child recovers on its next wake once the auth reloads. ":""))+`Error: ${o}.${d}`;m&&(X(e,"crash",{reason:c?"context_overflow":"provider_fatal",outcome:{detail:{schema:"crtr.node-outcome-detail/v1",message:t.message,fault_kind:t.kind,error_class:ee(t).class}}}),y.teardown(e).then(_=>{_.confirmed||f({level:"error",event:"fault.fatal.teardown_failed",node_id:e,error:_.error??new Error("Broker teardown not confirmed")})},_=>f({level:"error",event:"fault.fatal.teardown_failed",node_id:e,error:_}))),this.fatalFaultNotifiedAt.set(e,t.since),f({level:"error",event:"fault.fatal.fanout",...u(e)?{node_id:e}:{},fields:{...u(e)?{}:{affected_node_id:e},kind:t.kind,link:t.link,terminal_overflow:c,terminal_provider_fault:l}}),W(e,x(e),h,{reason:c?"child-context-overflow":"child-fault-fatal",child:e,kind:t.kind,message:o,...r===void 0?{}:{provider_error:r}})}async handleAuthReload(e){const t=De();let a=0;for(const r of Ie(t))try{a=Math.max(a,V(r).mtimeMs)}catch{}let i=0;const n=he(t);me(n)&&(i=_e(t));const o=`${a}:${i}`;if(this.knownCredentialRevision===null){this.knownCredentialRevision=o;return}o!==this.knownCredentialRevision&&(this.knownCredentialRevision=o,await R.fresh(async()=>{try{const r=Array.from(e.entries(),([l])=>l);if(r.length===0)return;const c=(await Promise.allSettled(r.map(async l=>{try{await ne(l)}catch(m){throw f({level:"error",event:"auth.reload.broker_failed",...u(l)?{node_id:l}:{fields:{broker_id:l}},error:m}),m}}))).filter(l=>l.status==="fulfilled").length;f({level:"info",event:"auth.reload.completed",outcome:c===r.length?"succeeded":"failed",fields:{broker_count:r.length,succeeded_count:c,failed_count:r.length-c}})}catch(r){f({level:"error",event:"auth.reload.failed",error:r})}}))}}export{vt as BrokerSupervisionReconciler,Me as WEDGE_CPU_MAX_PERCENT,D as WEDGE_QUIET_MS,U as YIELD_STALL_GRACE_MS,Y as parkSummaryGraceMsForDaemon,Re as sumTreeCpu,Fe as treeHasDescendants,$e as unattendedParkMsForDaemon,Ce as wedgeVerdict,Ee as yieldStallVerdict};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.344",
3
+ "version": "0.3.346",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -95,10 +95,10 @@
95
95
  "dev": "tsx --conditions=crtr-src src/cli.ts",
96
96
  "link": "npm link",
97
97
  "prepublishOnly": "npm run build",
98
- "test": "CRTR_NO_DAEMON_AUTOSTART=1 node --conditions=crtr-src --import tsx/esm --import ./scripts/test-env-scrub.mjs --test --test-force-exit --test-concurrency=$(node -p 'Math.max(4, require(`node:os`).availableParallelism() - 1)') --test-timeout=10000 'src/**/__tests__/*.test.ts'",
99
- "test:integration": "npm run build:api && CRTR_NO_DAEMON_AUTOSTART=1 node --conditions=crtr-src --import tsx/esm --import ./scripts/test-env-scrub.mjs --test --test-concurrency=$(node -p 'Math.min(8, Math.max(2, require(`node:os`).availableParallelism() - 1))') --test-timeout=60000 'src/**/__tests__/integration/*.test.ts' 'packages/crouter-sdk/src/**/__tests__/integration/*.test.ts' 'packages/crouter-plugin/src/**/__tests__/integration/*.test.ts'",
98
+ "test": "CRTR_NO_DAEMON_AUTOSTART=1 node --conditions=crtr-src --import tsx/esm --import ./scripts/test-env-scrub.mjs --test --test-force-exit --test-concurrency=$(node -p 'Math.max(4, require(`node:os`).availableParallelism() - 1)') --test-timeout=10000 ${CRTR_TEST_SHARD:+--test-shard=$CRTR_TEST_SHARD} 'src/**/__tests__/*.test.ts'",
99
+ "test:integration": "npm run build:api && CRTR_NO_DAEMON_AUTOSTART=1 node --conditions=crtr-src --import tsx/esm --import ./scripts/test-env-scrub.mjs --test --test-concurrency=$(node -p 'Math.min(8, Math.max(2, require(`node:os`).availableParallelism() - 1))') --test-timeout=60000 ${CRTR_TEST_SHARD:+--test-shard=$CRTR_TEST_SHARD} 'src/**/__tests__/integration/*.test.ts' 'packages/crouter-sdk/src/**/__tests__/integration/*.test.ts' 'packages/crouter-plugin/src/**/__tests__/integration/*.test.ts'",
100
100
  "test:seam": "npm run build && npm run test:seam:built",
101
- "test:seam:built": "CRTR_NO_DAEMON_AUTOSTART=1 node --import ./scripts/test-env-scrub.mjs --test --test-concurrency=$(node -p 'Math.min(4, Math.max(2, require(`node:os`).availableParallelism() - 2))') --test-timeout=180000 'dist/**/__tests__/seam/*.test.js'",
101
+ "test:seam:built": "CRTR_NO_DAEMON_AUTOSTART=1 node --import ./scripts/test-env-scrub.mjs --test --test-concurrency=$(node -p 'Math.min(4, Math.max(2, require(`node:os`).availableParallelism() - 2))') --test-timeout=180000 ${CRTR_TEST_SHARD:+--test-shard=$CRTR_TEST_SHARD} 'dist/**/__tests__/seam/*.test.js'",
102
102
  "test:all": "npm test && npm run test:integration && npm run test:seam"
103
103
  },
104
104
  "overrides": {
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.344",
3
+ "version": "0.3.346",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/crouter",
9
- "version": "0.3.344",
9
+ "version": "0.3.346",
10
10
  "hasInstallScript": true,
11
11
  "license": "GPL-3.0-only",
12
12
  "workspaces": [
@@ -10859,28 +10859,28 @@
10859
10859
  },
10860
10860
  "packages/crouter-api": {
10861
10861
  "name": "@north-light/crouter-api",
10862
- "version": "0.3.344",
10862
+ "version": "0.3.346",
10863
10863
  "license": "GPL-3.0-only"
10864
10864
  },
10865
10865
  "packages/crouter-env-docker": {
10866
10866
  "name": "@north-light/crouter-env-docker",
10867
- "version": "0.3.344",
10867
+ "version": "0.3.346",
10868
10868
  "license": "GPL-3.0-only"
10869
10869
  },
10870
10870
  "packages/crouter-plugin": {
10871
10871
  "name": "@north-light/crouter-plugin",
10872
- "version": "0.3.344",
10872
+ "version": "0.3.346",
10873
10873
  "license": "GPL-3.0-only",
10874
10874
  "dependencies": {
10875
- "@north-light/crouter-api": "^0.3.344"
10875
+ "@north-light/crouter-api": "^0.3.346"
10876
10876
  }
10877
10877
  },
10878
10878
  "packages/crouter-sdk": {
10879
10879
  "name": "@north-light/crouter-sdk",
10880
- "version": "0.3.344",
10880
+ "version": "0.3.346",
10881
10881
  "license": "GPL-3.0-only",
10882
10882
  "dependencies": {
10883
- "@north-light/crouter-api": "^0.3.344"
10883
+ "@north-light/crouter-api": "^0.3.346"
10884
10884
  }
10885
10885
  }
10886
10886
  }