@north-light/cli 0.3.556 → 0.3.558
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.
- package/dist/clients/attach/viewer.js +172 -172
- package/dist/commands/node/bash.js +2 -2
- package/dist/core/bash-job-supervisor.d.ts +1 -1
- package/dist/core/bash-job-supervisor.js +3 -3
- package/dist/core/bash-jobs.d.ts +26 -0
- package/dist/core/bash-jobs.js +10 -8
- package/dist/core/graph/kinds/job.js +2 -2
- package/dist/core/runtime/broker/bash-job-launch.js +1 -1
- package/dist/core/runtime/broker/fault-retry.d.ts +10 -3
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/promote.d.ts +2 -2
- package/dist/core/runtime/promote.js +1 -1
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/reconcilers/bash-deadline.d.ts +8 -0
- package/dist/daemon/reconcilers/bash-deadline.js +3 -3
- package/dist/shared/env.d.ts +4 -0
- package/dist/shared/env.js +1 -1
- package/dist/shared/inbox-entry-body.js +7 -7
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var
|
|
1
|
+
var $=Object.defineProperty;var i=(e,a)=>$(e,"name",{value:a,configurable:!0});import{envNodeId as _}from"../../shared/env.js";import{defineBranch as T,defineLeaf as c}from"../../core/command.js";import{InputError as r}from"../../core/io.js";import{ExitCode as I}from"../../types.js";import{existsSync as u,mkdirSync as q,rmSync as D,writeFileSync as f}from"node:fs";import{activeBackgroundBashJobs as E,backgroundRunningBashJobs as R,bashJobCommandLine as N,bashJobPaths as w,clearJobDeadline as B,formatBashElapsed as y,isSafeBashJobId as j,jobStartedAtMs as S,markJobQuietOk as k,newBashJobId as M,readJobPgid as L,tailJobLog as U,writeJobDeadlineMs as A}from"../../core/bash-jobs.js";import{parseDurationMs as J}from"../../core/wake.js";import{cliClient as O,getNodeOrNull as b}from"../api-client.js";import{PREVIEW_RESULT_PATH_ENV as P}from"../../core/preview-result-path.js";import{nodeInPane as C}from"../surface/node/placement.js";function m(e,a){const n=e.pane??process.env.TMUX_PANE;let o=e.node;if((o===void 0||o==="")&&(o=C(n)),(o===void 0||o==="")&&(o=_()),o===void 0||o==="")throw new r({error:"no_node",message:a,next:"Run from inside a node, pass --node <id>, or --pane <pane>."});return{id:o,pane:n}}i(m,"resolveBashTarget");const K=c({name:"run",description:"start a shell command as a background job and return at once",whenToUse:"a command needs longer than one tool call \u2014 a test lane, a build, a soak \u2014 and you should be woken with its exit code instead of waiting, backgrounding with `&`, or polling a log",tier:"normal",help:{name:"node bash run",summary:"start one shell command as a background job owned by the calling node and return before it finishes",params:[{kind:"positional",name:"command",required:!0,constraint:"The shell command, run by bash from the current directory with this process\u2019s environment. The same refusals apply as for a bash tool call, judged on this command."},{kind:"flag",name:"quiet-ok",type:"bool",required:!1,constraint:"Pass for a job that legitimately sits idle \u2014 `gh run watch`, `sleep`, waiting on a lock, a `docker build` whose work runs outside the process tree \u2014 so it gets no silence messages. Omit it for anything that should be making progress."}],output:[{name:"job_id",type:"string",required:!0,constraint:"The job object\u2019s id; read or watch it with `crtr canvas`, stop it with `node bash kill`."},{name:"log_path",type:"string",required:!0,constraint:"File receiving the command\u2019s combined stdout and stderr, capped at 10 MiB."}],outputKind:"object",effects:["Works only inside a node\u2019s bash tool call; the job belongs to the calling node.","Starts the command detached from this call, so it keeps running after the call and the turn end. A `timeout` around this leaf bounds only the launch, not the job.","Records the job as a canvas object; when the command exits, its watchers (the calling node by default) get an urgent `exited` push with the exit code and log path.","Sets no cancel deadline: the job runs to completion unless stopped with `node bash kill`.","A job whose processes use almost no CPU and write to no open file for 15 minutes messages the calling node about it, again at 30 and 60 minutes, then hourly; activity restarts the schedule and nothing is stopped. `--quiet-ok` turns these messages off.","Fails, with the job id and log path, if the job could not be recorded; the command is then running but will not message the node when it exits."]},run:i(async e=>{const a=e.command,n=_(),o=process.env.CRTR_CONTEXT_DIR;if(n===void 0||n===""||o===void 0||o==="")throw new r({error:"no_node",message:"`node bash run` starts a job for the calling node and works only inside a node\u2019s bash tool",next:"Run it from a node\u2019s bash tool call."});const s=process.cwd(),{bashCommandRefusal:d}=await import("../../core/bash-guard.js"),l=d(a,s);if(l!==null)throw new r({error:"refused",message:l,next:"Run a different command."});const t=w(o,M());q(t.dir,{recursive:!0}),f(t.cmdSh,a),f(t.jobLog,""),f(t.jobBg,""),e.quietOk===!0&&k(t);const{launchBashJobSupervisor:h}=await import("../../core/runtime/broker/bash-job-launch.js"),{[P]:g,...x}=process.env;try{await h(n,t,o,s,x)}catch(p){throw D(t.dir,{recursive:!0,force:!0}),p}const{recordBackgroundedJob:v}=await import("../../core/runtime/broker/daemon-ops.js");try{await v(n,t.jobId,{command:a,job_dir:t.dir,log_path:t.jobLog})}catch(p){throw new r({error:"job_not_recorded",message:`job ${t.jobId} is running but could not be recorded (${p.message}); it will NOT message you when it exits. Log: ${t.jobLog}`,next:`Watch it with \`tail -f ${t.jobLog}\`, or stop it with \`crtr node bash kill ${t.jobId}\`.`},I.GENERAL)}return{job_id:t.jobId,log_path:t.jobLog}},"run"),render:i(e=>`[backgrounded as job ${e.job_id}. It will interrupt you with a message when it exits. Watch: tail -f ${e.log_path} ; stop: crtr node bash kill ${e.job_id}]`,"render")}),X=c({name:"background",description:"hand the focused node\u2019s running bash command to its background job system",whenToUse:"the tmux prefix menu invokes this when a foreground bash command should stop blocking the agent immediately but keep running as a file-backed background job",tier:"hidden",help:{name:"node bash background",summary:"hand every running foreground bash command for one node to its file-backed background job system",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node whose running bash command to background. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE. The prefix menu passes this for you."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The target node."},{name:"jobs",type:"object[]",required:!0,constraint:"Per handed-off job {job_id, elapsed_ms} \u2014 the foreground elapsed time in milliseconds at the moment of handoff."}],outputKind:"object",effects:["Signals every foreground bash command currently running for the node to return from its tool call immediately while its detached supervisor keeps the command running.","Each handed-off command retains its log under the node context jobs directory and becomes a job object; when it exits, the job\u2019s watchers (its node by default) get an urgent `exited` push carrying the exit code and log path."]},run:i(async e=>{const{id:a}=m(e,"no node found for the bash handoff"),n=await b(a);if(n===null)throw new r({error:"not_found",message:`no node: ${a}`,next:"List nodes with `crtr node inspect list`."});const o=R(n.paths.context_dir),s=Date.now(),d=o.map(l=>({job_id:l.jobId,elapsed_ms:s-(S(l.jobId)??s)}));return{node_id:a,jobs:d}},"run"),render:i(e=>{const a=e.jobs;if(a.length===0)return`No foreground bash command is running for ${e.node_id}.`;const n=Math.max(...a.map(o=>o.elapsed_ms));return`Handed ${a.length} bash command(s) to the background job system for ${e.node_id} \xB7 ran ${y(n)} in the foreground.`},"render")}),W=c({name:"list",description:"list a node\u2019s live background bash jobs",whenToUse:"you need the ids, commands, and ages of your jobs that are still running",help:{name:"node bash list",summary:"read every still-running background bash job owned by one node",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node whose background jobs to list. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."},{kind:"flag",name:"tail",type:"int",required:!1,default:0,constraint:"Include this many trailing log lines per job (0 = none)."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The target node."},{name:"jobs",type:"object[]",required:!0,constraint:"Oldest-first {job_id, command, purpose, started_at, elapsed_ms, deadline_at, log_path, pgid, log_tail} for every live background job; purpose is a human-readable label or null; deadline_at is the auto-cancel time or null for a job that runs to completion."}],outputKind:"object",effects:["Reads the node context jobs directory. Changes nothing."]},run:i(async e=>{const{id:a}=m(e,"no node found for the bash job list"),n=await b(a);if(n===null)throw new r({error:"not_found",message:`no node: ${a}`,next:"List nodes with `crtr node inspect list`."});const o=Date.now(),s=Math.max(0,Math.trunc(Number(e.tail??0))),d=n.paths.context_dir,l=E(d).map(t=>({job_id:t.jobId,command:t.command,purpose:t.purpose,started_at:new Date(t.startedAtMs).toISOString(),elapsed_ms:o-t.startedAtMs,deadline_at:t.deadlineAtMs===null?null:new Date(t.deadlineAtMs).toISOString(),log_path:t.logPath,pgid:L(d,t.jobId)??null,log_tail:U(t.logPath,s)}));return{node_id:n.node_id,jobs:l}},"run"),render:i(e=>{const a=e.jobs;return a.length===0?`No background bash jobs for ${e.node_id}.`:a.map(n=>`${n.job_id} \xB7 ${y(n.elapsed_ms)} \xB7 ${N(n.command)}${n.purpose===null?"":`
|
|
2
2
|
purpose: ${n.purpose}`}${n.deadline_at===null?"":`
|
|
3
3
|
cancels at: ${n.deadline_at}`}
|
|
4
4
|
log: ${n.log_path}`).join(`
|
|
5
|
-
`)},"render")}),G=
|
|
5
|
+
`)},"render")}),G=c({name:"kill",description:"stop one of a node\u2019s background bash jobs",whenToUse:"a job you started is no longer wanted or is stuck, and it should stop now",help:{name:"node bash kill",summary:"best-effort stop a background bash job and tell the owning node it was canceled",params:[{kind:"positional",name:"job",required:!0,constraint:"Job id, as reported by node bash list."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node owning the job. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The owning node."},{name:"signaled",type:"boolean",required:!0,constraint:"True when at least one observed supervisor-tree process received SIGTERM."}],outputKind:"object",effects:["Snapshots the supervisor\u2019s observable process tree, sends SIGTERM then SIGKILL to those processes, and cannot stop a descendant that detached and reparented before the snapshot.","Records the job\u2019s exit so it stops showing as live, and ends its job object as killed; the job\u2019s watchers (its node by default) get an urgent `killed` push."]},run:i(async e=>{const{id:a}=m(e,"no node found for the bash job"),n=await b(a);if(n===null)throw new r({error:"not_found",message:`no node: ${a}`,next:"List nodes with `crtr node inspect list`."});const o=e.job;if(!j(o))throw new r({error:"invalid_job_id",message:`invalid background job id: ${o}`,next:"Use a job id reported by `crtr node bash list`."});const s=await O().stopBashJob(n.node_id,o);return{node_id:n.node_id,signaled:s.signaled}},"run"),render:i(e=>`Canceled a background bash job for ${e.node_id}${e.signaled===!0?"":" (no observed job process was still running)"}.`,"render")}),F=c({name:"quiet",description:"mark a running background bash job as an expected wait so it sends no silence messages",whenToUse:"a silence message arrived for a job that is meant to sit idle",help:{name:"node bash quiet",summary:"stop silence messages for one of a node\u2019s live background bash jobs",params:[{kind:"positional",name:"job",required:!0,constraint:"Job id, as reported by node bash list or the silence message."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node owning the job. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The owning node."}],outputKind:"object",effects:["Writes a marker in the job\u2019s directory; the daemon sends no further silence messages for it. Signals nothing."]},run:i(async e=>{const{id:a}=m(e,"no node found for the bash job"),n=await b(a);if(n===null)throw new r({error:"not_found",message:`no node: ${a}`,next:"List nodes with `crtr node inspect list`."});const o=e.job;if(!j(o))throw new r({error:"invalid_job_id",message:`invalid background job id: ${o}`,next:"Use a job id reported by `crtr node bash list`."});const s=w(n.paths.context_dir,o);if(!u(s.jobBg)||u(s.jobExit))throw new r({error:"not_running",message:`background job ${o} for ${n.node_id} is not running`,next:"List live jobs with `crtr node bash list`."});return k(s),{node_id:n.node_id}},"run"),render:i(e=>`Background job for ${e.node_id} will send no more silence messages.`,"render")}),H=c({name:"extend",description:"claim an auto-backgrounded bash job so the daemon does not cancel it at its deadline",whenToUse:"a command that was moved to a job automatically is worth keeping past its cancel deadline \u2014 give it more time with --for, or let it run to completion with --keep",help:{name:"node bash extend",summary:"move or clear the auto-cancel deadline on one of a node\u2019s live background bash jobs",params:[{kind:"positional",name:"job",required:!0,constraint:"Job id, as reported by node bash list or the auto-background notice."},{kind:"flag",name:"for",type:"string",required:!1,constraint:"New window from now before the job is canceled, as a duration (e.g. 90s, 10m, 1h30m). Mutually exclusive with --keep; give exactly one."},{kind:"flag",name:"keep",type:"bool",required:!1,constraint:"Remove the deadline entirely so the job runs to completion. Mutually exclusive with --for; give exactly one."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node owning the job. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The owning node."},{name:"deadline_at",type:"string",required:!1,constraint:"The job\u2019s new auto-cancel time (ISO), or absent when --keep cleared the deadline so the job runs to completion."}],outputKind:"object",effects:["Rewrites or removes the job\u2019s deadline file. The daemon\u2019s deadline lane reads it on its next scan. Signals nothing."]},run:i(async e=>{const{id:a}=m(e,"no node found for the bash job"),n=await b(a);if(n===null)throw new r({error:"not_found",message:`no node: ${a}`,next:"List nodes with `crtr node inspect list`."});const o=e.job;if(!j(o))throw new r({error:"invalid_job_id",message:`invalid background job id: ${o}`,next:"Use a job id reported by `crtr node bash list`."});const s=e.for,d=e.keep===!0;if(d===(s!==void 0&&s!==""))throw new r({error:"invalid_args",message:"give exactly one of --for <duration> or --keep",next:"Extend by a window with --for 10m, or drop the deadline with --keep."});const l=n.paths.context_dir,t=w(l,o);if(!u(t.dir))throw new r({error:"not_found",message:`no background job ${o} for ${n.node_id}`,next:"List live jobs with `crtr node bash list`."});if(!u(t.jobBg)||u(t.jobExit))throw new r({error:"not_running",message:`background job ${o} for ${n.node_id} is no longer running`,next:`It finished or was stopped, so there is no deadline to change. Log: ${t.jobLog}`});if(d)return B(t),{node_id:n.node_id};const h=J(s);if(h===null||h<=0)throw new r({error:"invalid_duration",message:`not a duration: ${s}`,next:"Use a value like 90s, 10m, or 1h30m."});const g=Date.now()+h;return A(t,g),{node_id:n.node_id,deadline_at:new Date(g).toISOString()}},"run"),render:i(e=>e.deadline_at===void 0?`Cleared the deadline \u2014 the background job will run to completion for ${e.node_id}.`:`Background job for ${e.node_id} will be canceled at ${e.deadline_at} unless extended again.`,"render")}),re=T({name:"bash",description:"start, list, stop, and extend your background bash jobs",whenToUse:"a command needs longer than one tool call \u2014 start it as a job and be woken with its exit code instead of using `&`, `nohup`, or a cron that polls a log \u2014 or you need to list, stop, or extend the deadline of a job you already have",help:{name:"node bash",summary:"start, list, stop, and extend background bash jobs owned by a node",model:"Every bash call runs under a detached supervisor with its output in a log file, so a command that outlives its call keeps running as a job: a canvas object its node owns, readable and watchable with `crtr canvas`. `run` starts a job on purpose and it is never auto-canceled. A job that shows no activity for 15 minutes messages its node, and is never stopped for it. A foreground command still running after 5 minutes is moved to a job automatically, with a cancel deadline unless `extend` claims it."},children:[K,X,W,G,F,H]});export{re as nodeBash};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const MAX_BACKGROUND_BASH_LOG_BYTES: number;
|
|
2
2
|
export declare const BACKGROUND_BASH_LOG_TRUNCATION_MARKER = "\n[background bash job output truncated at 10 MiB; remaining output discarded]\n";
|
|
3
|
-
/** File-backed command supervisor. It writes the exit code to job.exit and, for a foreground
|
|
3
|
+
/** File-backed command supervisor. Beside the job it runs the activity sampler. It writes the exit code to job.exit and, for a foreground
|
|
4
4
|
* command, claims job.done; the daemon reads job.exit of a backgrounded job and publishes `exited`. */
|
|
5
5
|
export declare const BASH_JOB_SUPERVISOR_SCRIPT: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const c=10485760,t=`
|
|
2
2
|
[background bash job output truncated at 10 MiB; remaining output discarded]
|
|
3
|
-
`,e=['const fs = require("node:fs");',"const cap = 10485760;",`const marker = Buffer.from(${JSON.stringify(
|
|
4
|
-
`);export{
|
|
3
|
+
`,e=['const fs = require("node:fs");',"const cap = 10485760;",`const marker = Buffer.from(${JSON.stringify(t)});`,"const prefixCap = cap - marker.length;",'const fd = fs.openSync(process.argv[1], "w");',"let written = 0;","let truncated = false;",'process.stdin.on("data", (chunk) => {'," if (truncated) return;"," const available = prefixCap - written;"," if (available > 0) {"," const prefix = chunk.subarray(0, available);"," fs.writeSync(fd, prefix);"," written += prefix.length;"," if (prefix.length === chunk.length) return;"," }"," fs.writeSync(fd, marker);"," truncated = true;","});",'process.stdin.on("end", () => fs.closeSync(fd));'].join(" "),s=['const fs = require("node:fs"), cp = require("node:child_process");',"const rootPid = Number(process.argv[1]), out = process.argv[3];","let logPath = process.argv[2];","try { logPath = fs.realpathSync(logPath); } catch {}","const interval = Number(process.env.CRTR_BASH_ACTIVITY_SAMPLE_MS) || 60000;",'const linux = fs.existsSync("/proc/self/stat");',"function processes() {"," const procs = new Map();"," if (linux) {",' for (const e of fs.readdirSync("/proc")) {'," if (!/^[0-9]+$/.test(e)) continue;"," try {",' const s = fs.readFileSync("/proc/" + e + "/stat", "utf8");',' const r = s.slice(s.lastIndexOf(")") + 2).split(" ");'," procs.set(Number(e), { ppid: Number(r[1]), cpu: (Number(r[11]) + Number(r[12])) / 100 });"," } catch {}"," }"," return procs;"," }",' const ps = cp.execFileSync("ps", ["-ax", "-o", "pid=,ppid=,time="], { encoding: "utf8", timeout: 5000 });',' for (const line of ps.split("\\n")) {'," const f = line.trim().split(/\\s+/);"," if (f.length < 3) continue;",' const [days, clock] = f[2].includes("-") ? f[2].split("-") : ["0", f[2]];',' const cpu = clock.split(":").reduce((t, x) => t * 60 + Number(x), 0) + Number(days) * 86400;'," if (Number.isFinite(cpu)) procs.set(Number(f[0]), { ppid: Number(f[1]), cpu });"," }"," return procs;","}","function tree(procs) {"," const cpus = new Map(), stack = [rootPid];"," while (stack.length > 0) {"," const pid = stack.pop();"," if (pid === process.pid || cpus.has(pid) || !procs.has(pid)) continue;"," cpus.set(pid, procs.get(pid).cpu);"," for (const [child, p] of procs) if (p.ppid === pid) stack.push(child);"," }"," return cpus;","}","function writableFiles(pids) {"," const files = new Set();"," if (linux) {"," for (const pid of pids) {"," let fds = [];",' try { fds = fs.readdirSync("/proc/" + pid + "/fd"); } catch {}'," for (const fd of fds) {"," try {",' const info = fs.readFileSync("/proc/" + pid + "/fdinfo/" + fd, "utf8").match(/flags:\\s*([0-7]+)/);'," if (info === null || (parseInt(info[1], 8) & 3) === 0) continue;",' files.add(fs.readlinkSync("/proc/" + pid + "/fd/" + fd));'," } catch {}"," }"," }"," return files;"," }",' const r = cp.spawnSync("lsof", ["-nP", "-w", "-F", "pfatn", "-p", pids.join(",")], { encoding: "utf8", timeout: 10000 });',' if (r.error || typeof r.stdout !== "string") return files;',' let access = "", type = "";',' for (const line of r.stdout.split("\\n")) {'," const key = line[0], value = line.slice(1);",' if (key === "f") { access = ""; type = ""; }',' else if (key === "a") access = value;',' else if (key === "t") type = value;',' else if (key === "n" && (access === "w" || access === "u") && type === "REG") files.add(value);'," }"," return files;","}","let lastActivity = Date.now(), prevAt = lastActivity, prevCpu = new Map(), watched = [logPath];","try { prevCpu = tree(processes()); } catch {}","function record() {",' fs.writeFileSync(out + ".tmp", lastActivity + " " + prevAt + "\\n" + watched.join("\\n") + "\\n");',' fs.renameSync(out + ".tmp", out);',"}","function sample() {",' try { process.kill(rootPid, 0); } catch (e) { if (e.code !== "EPERM") process.exit(0); }'," const at = Date.now();"," const cpus = tree(processes());"," let used = 0;"," for (const [pid, cpu] of cpus) used += Math.max(0, cpu - (prevCpu.get(pid) || 0));"," const paths = new Set([logPath]);"," let changed = false;"," for (const file of writableFiles([...cpus.keys()])) {"," try {"," const st = fs.statSync(file);"," if (!st.isFile()) continue;"," paths.add(fs.realpathSync(file));"," if (st.mtimeMs > prevAt) changed = true;"," } catch {}"," }"," try { if (fs.statSync(logPath).mtimeMs > prevAt) changed = true; } catch {}"," if (at - prevAt > 3 * interval) changed = true;"," if (changed || used > 0.02 * (at - prevAt) / 1000) lastActivity = at;"," watched = [...paths];"," prevAt = at;"," prevCpu = cpus;"," record();","}","record();","setInterval(() => { try { sample(); } catch {} }, interval);"].join(" "),n=['"$8" -e '+JSON.stringify(s)+' "$$" "$2" "$9" &',"sampler=$!","n=$(command -v nice)",'${n:+"$n" -n "$7"} "$6" "$1" 2>&1 | "$8" -e '+JSON.stringify(e)+' "$2"',"status=${PIPESTATUS[0]}",'kill "$sampler" 2>/dev/null','echo "$status" > "$3"','mv "$4" "$5" 2>/dev/null || true'].join(`
|
|
4
|
+
`);export{t as BACKGROUND_BASH_LOG_TRUNCATION_MARKER,n as BASH_JOB_SUPERVISOR_SCRIPT,c as MAX_BACKGROUND_BASH_LOG_BYTES};
|
package/dist/core/bash-jobs.d.ts
CHANGED
|
@@ -25,6 +25,13 @@ export interface BashJobPaths {
|
|
|
25
25
|
* subject to the daemon's claim-or-cancel deadline. Absent for a deliberate
|
|
26
26
|
* handoff (tmux menu, broker termination), which runs to completion. */
|
|
27
27
|
jobDeadline: string;
|
|
28
|
+
/** Written beside the job by its supervisor's activity sampler: when the job last
|
|
29
|
+
* showed life and which files it was watched through (see `readJobActivity`). */
|
|
30
|
+
jobActivity: string;
|
|
31
|
+
/** Present when the job's owner declared that it legitimately sits idle: it gets no silence messages. */
|
|
32
|
+
jobQuietOk: string;
|
|
33
|
+
/** The daemon's record of the last silence message sent: `<last-activity-ms> <threshold-ms>`. */
|
|
34
|
+
jobSilence: string;
|
|
28
35
|
}
|
|
29
36
|
/** A purpose is display text, never shell input. Keep the file-backed control
|
|
30
37
|
* plane to one bounded, visible line so old or hand-written job directories
|
|
@@ -78,6 +85,25 @@ export declare function writeJobDeadlineMs(paths: BashJobPaths, deadlineMs: numb
|
|
|
78
85
|
/** Drop a job's cancel deadline so it runs to completion — the node has claimed
|
|
79
86
|
* it. Best-effort: a job with no deadline file is already in this state. */
|
|
80
87
|
export declare function clearJobDeadline(paths: BashJobPaths): void;
|
|
88
|
+
/** What the job's activity sampler last recorded: when the job last showed life (its process tree
|
|
89
|
+
* used CPU, or a regular file one of its processes holds open for writing changed), when that was
|
|
90
|
+
* sampled, and the files it was watched through. undefined for a job with no readable record. */
|
|
91
|
+
export declare function readJobActivity(contextDir: string, jobId: string): {
|
|
92
|
+
lastActivityMs: number;
|
|
93
|
+
sampledAtMs: number;
|
|
94
|
+
files: string[];
|
|
95
|
+
} | undefined;
|
|
96
|
+
/** Whether the owner declared the job an expected wait, so it gets no silence messages. */
|
|
97
|
+
export declare function isJobQuietOk(contextDir: string, jobId: string): boolean;
|
|
98
|
+
/** Declare a job an expected wait (`node bash run --quiet-ok`, `node bash quiet`). */
|
|
99
|
+
export declare function markJobQuietOk(paths: BashJobPaths): void;
|
|
100
|
+
/** The silence message the daemon last sent for a job: the last-activity time it was sent for and
|
|
101
|
+
* the threshold it announced. undefined when none was sent. */
|
|
102
|
+
export declare function readJobSilence(contextDir: string, jobId: string): {
|
|
103
|
+
lastActivityMs: number;
|
|
104
|
+
thresholdMs: number;
|
|
105
|
+
} | undefined;
|
|
106
|
+
export declare function writeJobSilence(paths: BashJobPaths, lastActivityMs: number, thresholdMs: number): void;
|
|
81
107
|
/** Launcher jobs have a root-owned bwrap pid, not a process group; direct
|
|
82
108
|
* jobs keep their detached supervisor process group. */
|
|
83
109
|
export declare function isBashJobProcessGroupAlive(pgid: number, identity?: string): boolean;
|
package/dist/core/bash-jobs.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
var $=Object.defineProperty;var o=(e
|
|
2
|
-
`)}o(
|
|
3
|
-
`)}o(
|
|
4
|
-
`)
|
|
5
|
-
`)}
|
|
6
|
-
`)
|
|
7
|
-
`)
|
|
8
|
-
`)
|
|
1
|
+
var $=Object.defineProperty;var o=(t,e)=>$(t,"name",{value:e,configurable:!0});import{randomBytes as R}from"node:crypto";import{closeSync as y,openSync as B,readSync as S,statSync as v}from"node:fs";import{dirname as m,join as c}from"node:path";import{captureTeardownSnapshot as O,isAnyPidAlive as T,isPidAlive as M,killProcessTreePids as x}from"./canvas/pid.js";import{launcherRequest as F}from"../launcher/client.js";import{LaunchRefused as _}from"../launcher/spec.js";import{createFileBeneath as A,existsBeneath as I,readdirBeneath as C,readFileBeneath as Q,renameBeneath as z,unlinkBeneath as G,writeFileBeneath as g}from"./spaces/open-beneath.js";const H=280;function X(t){if(typeof t!="string"||/[\p{Cc}\p{Cf}]/u.test(t))return null;const e=t.trim();return e===""?null:Buffer.byteLength(e,"utf8")<=H?e:null}o(X,"normalizeBashJobPurpose");function j(t){return c(t,"jobs")}o(j,"bashJobsDir");function b(t,e){return Q(m(t),e).toString("utf8")}o(b,"readJobText");function J(t){return C(m(t),j(t)).map(e=>e.name)}o(J,"jobIds");function a(t,e){return I(t.root,e)}o(a,"has");function k(t,e){try{G(t,e)}catch(n){if(n.code!=="ENOENT")throw n}}o(k,"unlinkIfPresent");function ct(t){return typeof t=="string"&&t!==""&&t!=="."&&t!==".."&&/^[A-Za-z0-9._~-]+$/u.test(t)&&Buffer.byteLength(t,"utf8")<=128}o(ct,"isSafeBashJobId");function f(t,e){const n=c(j(t),e);return{jobId:e,root:m(t),dir:n,cmdSh:c(n,"cmd.sh"),jobLog:c(n,"job.log"),jobExit:c(n,"job.exit"),jobRun:c(n,"job.run"),jobBg:c(n,"job.bg"),jobDone:c(n,"job.done"),jobPgid:c(n,"job.pgid"),jobPgidIdentity:c(n,"job.pgid-identity"),jobPurpose:c(n,"purpose"),jobDeadline:c(n,"job.deadline"),jobActivity:c(n,"job.activity"),jobQuietOk:c(n,"job.quiet-ok"),jobSilence:c(n,"job.silence")}}o(f,"bashJobPaths");function q(t,e){try{const n=Number(b(t,f(t,e).jobPgid).trim());return Number.isInteger(n)&&n>1?n:void 0}catch{return}}o(q,"readJobPgid");function U(t,e){try{const n=b(t,f(t,e).jobPgidIdentity).trim();return n===""?void 0:n}catch{return}}o(U,"readJobPgidIdentity");function W(t,e){try{return X(b(t,f(t,e).jobPurpose))}catch{return null}}o(W,"readJobPurpose");const P=10,K=19;function N(t){return c(j(t),"niceness")}o(N,"bashNicenessPath");function at(t){try{const e=Number(b(t,N(t)).trim());return!Number.isInteger(e)||e<0||e>K?P:e}catch{return P}}o(at,"readBashNiceness");function ft(t,e){g(m(t),N(t),`${Math.trunc(e)}
|
|
2
|
+
`)}o(ft,"writeBashNiceness");function Y(t,e){try{const n=Number(b(t,f(t,e).jobDeadline).trim());return Number.isFinite(n)?n:void 0}catch{return}}o(Y,"readJobDeadlineMs");function Z(t,e){g(t.root,t.jobDeadline,`${Math.trunc(e)}
|
|
3
|
+
`)}o(Z,"writeJobDeadlineMs");function dt(t){try{k(t.root,t.jobDeadline)}catch{}}o(dt,"clearJobDeadline");function lt(t,e){try{const[n,...r]=b(t,f(t,e).jobActivity).split(`
|
|
4
|
+
`),[s,i]=(n??"").split(" ").map(Number);return!Number.isFinite(s)||!Number.isFinite(i)?void 0:{lastActivityMs:s,sampledAtMs:i,files:r.filter(u=>u!=="")}}catch{return}}o(lt,"readJobActivity");function bt(t,e){return a(f(t,e),f(t,e).jobQuietOk)}o(bt,"isJobQuietOk");function ht(t){g(t.root,t.jobQuietOk,"")}o(ht,"markJobQuietOk");function pt(t,e){try{const[n,r]=b(t,f(t,e).jobSilence).trim().split(" ").map(Number);return Number.isFinite(n)&&Number.isFinite(r)?{lastActivityMs:n,thresholdMs:r}:void 0}catch{return}}o(pt,"readJobSilence");function mt(t,e,n){g(t.root,t.jobSilence,`${Math.trunc(e)} ${Math.trunc(n)}
|
|
5
|
+
`)}o(mt,"writeJobSilence");function gt(t,e){if(e===`launcher:${t}`)return M(t);try{return process.kill(-t,0),!0}catch(n){return n.code==="EPERM"}}o(gt,"isBashJobProcessGroupAlive");function jt(t,e){if(t===void 0||e===void 0)return!1;if(e===`launcher:${t}`)return!M(t);try{return process.kill(-t,0),!1}catch(n){return n.code==="ESRCH"}}o(jt,"isBashJobProcessConfirmedGone");async function V(t,e){const n=O(t,e);if(n.reused||n.tree.length===0)return!1;const r=[...n.tree].reverse(),s=T(r);return x(r,"SIGTERM",n.identities),await new Promise(i=>setTimeout(i,400)),x(r,"SIGKILL",n.identities),s}o(V,"cancelBashJobProcessTree");async function yt(t,e,n=!1,r,s){const i=f(t,e);if(!a(i,i.dir))return{kind:"missing"};if(!a(i,i.jobBg)&&!(n&&a(i,i.jobRun))||a(i,i.jobExit))return{kind:"retired",logPath:i.jobLog};const u=q(t,e),d=U(t,e);if(u===void 0||d===void 0)return{kind:"not-stoppable",logPath:i.jobLog};const h=c(i.dir,"job.stop");try{A(i.root,h,s===void 0?"":`${s}
|
|
6
|
+
`)}catch(l){if(l.code==="EEXIST")return{kind:"retired",logPath:i.jobLog};throw l}try{let l;if(d===`launcher:${u}`){if(r===void 0)throw new Error("launcher job stop requires its owning node");try{if(!("ok"in await F({operation:"stop",pid:u,nodeId:r})))throw new Error("launcher did not stop job");l=!0}catch(p){if(!(p instanceof _&&p.refusal==="invalid_spec"))throw p;l=!1}}else l=await V(u,d);try{A(i.root,i.jobExit,`143
|
|
7
|
+
`)}catch(p){if(p.code!=="EEXIST")throw p}return{kind:"stopped",signaled:l,pgid:u,logPath:i.jobLog}}catch(l){throw k(i.root,h),l}}o(yt,"stopBackgroundBashJob");function Bt(t,e){if(e<=0)return[];const n=64*1024;let r;try{const s=v(t).size,i=Math.max(0,s-n),u=Buffer.alloc(Math.min(s,n));r=B(t,"r"),S(r,u,0,u.length,i);const d=u.toString("utf8"),h=(i>0?d.slice(d.indexOf(`
|
|
8
|
+
`)+1):d).split(`
|
|
9
|
+
`);return h[h.length-1]===""&&h.pop(),h.slice(-e)}catch{return[]}finally{r!==void 0&&y(r)}}o(Bt,"tailJobLog");function St(t){let e;try{e=B(t,"r");const n=Buffer.alloc(64*1024);let r=0,s=0;for(;;){const i=S(e,n,0,n.length,null);if(i===0)break;for(let u=0;u<i;u+=1)n[u]===10&&(r+=1);s=n[i-1]}return s!==0&&s!==10?r+1:r}catch{return 0}finally{e!==void 0&&y(e)}}o(St,"countJobLogLines");function Mt(){return`${Date.now()}-${R(4).toString("hex")}`}o(Mt,"newBashJobId");function E(t){const e=t.indexOf("-");if(e<=0||e===t.length-1)return;const n=Number(t.slice(0,e));return Number.isFinite(n)?n:void 0}o(E,"jobStartedAtMs");function xt(t){const e=Math.max(0,Math.floor(t/1e3));if(e<60)return`${e}s`;const n=Math.floor(e/60),r=e%60;if(n<60)return`${n}m ${String(r).padStart(2,"0")}s`;const s=Math.floor(n/60),i=n%60;return`${s}h ${String(i).padStart(2,"0")}m`}o(xt,"formatBashElapsed");function At(t){return(t.split(`
|
|
10
|
+
`).map(n=>n.trim()).find(n=>n!=="")??"").replace(/\s+/g," ")}o(At,"bashJobCommandLine");function w(t,e=!1){let n;try{n=J(t)}catch{return[]}const r=[];for(const s of n){const i=E(s);if(i===void 0)continue;const u=f(t,s);let d;try{if(!a(u,u.jobBg)&&!(e&&a(u,u.jobRun))||a(u,u.jobExit))continue;d=b(t,u.cmdSh)}catch{continue}r.push({jobId:s,command:d,purpose:W(t,s),startedAtMs:i,logPath:u.jobLog,deadlineAtMs:Y(t,s)??null})}return r.sort((s,i)=>s.startedAtMs-i.startedAtMs)}o(w,"activeBackgroundBashJobs");function Jt(t,e){return w(t).filter(n=>n.deadlineAtMs!==null&&e>=n.deadlineAtMs)}o(Jt,"expiredAutoBackgroundBashJobs");function kt(t,e){if(!Number.isFinite(e)||e<=0)return 0;let n=0;for(const r of w(t))if(r.deadlineAtMs!==null)try{Z(f(t,r.jobId),r.deadlineAtMs+e),n+=1}catch{}return n}o(kt,"shiftAutoBackgroundBashDeadlines");function D(t){try{return z(t.root,t.jobRun,t.jobBg),"backgrounded"}catch(e){if(e.code!=="ENOENT")throw e;return a(t,t.jobBg)?"already-backgrounded":a(t,t.jobDone)?"finished":"missing"}}o(D,"backgroundBashJob");function L(t){let e;try{e=J(t)}catch{return[]}return e.flatMap(n=>{const r=f(t,n);try{return a(r,r.cmdSh)&&a(r,r.jobLog)&&a(r,r.jobRun)?[r]:[]}catch{return[]}})}o(L,"runningBashJobs");function Pt(t){let e;for(const n of L(t)){const r=E(n.jobId);r!==void 0&&(e===void 0||r<e)&&(e=r)}return e}o(Pt,"oldestRunningBashJobStartedAtMs");function Nt(t){const e=[];for(const n of L(t))D(n)==="backgrounded"&&e.push(n);return e}o(Nt,"backgroundRunningBashJobs");export{P as DEFAULT_BASH_NICENESS,K as MAX_BASH_NICENESS,w as activeBackgroundBashJobs,D as backgroundBashJob,Nt as backgroundRunningBashJobs,At as bashJobCommandLine,f as bashJobPaths,j as bashJobsDir,N as bashNicenessPath,V as cancelBashJobProcessTree,dt as clearJobDeadline,St as countJobLogLines,Jt as expiredAutoBackgroundBashJobs,xt as formatBashElapsed,jt as isBashJobProcessConfirmedGone,gt as isBashJobProcessGroupAlive,bt as isJobQuietOk,ct as isSafeBashJobId,E as jobStartedAtMs,ht as markJobQuietOk,Mt as newBashJobId,X as normalizeBashJobPurpose,Pt as oldestRunningBashJobStartedAtMs,at as readBashNiceness,lt as readJobActivity,Y as readJobDeadlineMs,q as readJobPgid,U as readJobPgidIdentity,W as readJobPurpose,pt as readJobSilence,kt as shiftAutoBackgroundBashDeadlines,yt as stopBackgroundBashJob,Bt as tailJobLog,ft as writeBashNiceness,Z as writeJobDeadlineMs,mt as writeJobSilence};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jobRow as d}from"../jobs.js";import{objectDTO as c}from"./dto.js";import{withBase as l}from"./kind.js";import{recordLiveRead as p}from"./live-read.js";import{attr as r,header as w}from"./render.js";const u={name:"job",addressable:"id",events:l({exited:{level:7},killed:{level:7}}),async read(a,i,e,s){const o=await d(a,e);if(!o)throw new Error(`job object ${e.id} has no jobs row`);await p(a,i,e,s.reposFn);const
|
|
2
|
-
`);return{data:t,text:
|
|
1
|
+
import{jobRow as d}from"../jobs.js";import{objectDTO as c}from"./dto.js";import{withBase as l}from"./kind.js";import{recordLiveRead as p}from"./live-read.js";import{attr as r,header as w}from"./render.js";const u={name:"job",addressable:"id",events:l({exited:{level:7},killed:{level:7},silent:{level:7}}),async read(a,i,e,s){const o=await d(a,e);if(!o)throw new Error(`job object ${e.id} has no jobs row`);await p(a,i,e,s.reposFn);const n=await c(a,e),t={command:o.command,status:o.status,exit_code:o.exit_code,log_path:o.log_path},m=[w("object",n,r("status",t.status)+r("exit_code",t.exit_code)),`Command: ${t.command}`,`Log: ${t.log_path}`].join(`
|
|
2
|
+
`);return{data:t,text:m}}};export{u as jobKind};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var p=Object.defineProperty;var m=(e,o)=>p(e,"name",{value:o,configurable:!0});import{spawn as S}from"node:child_process";import{writeFileSync as a}from"node:fs";import{BASH_JOB_SUPERVISOR_SCRIPT as l}from"../../bash-job-supervisor.js";import{readBashNiceness as j}from"../../bash-jobs.js";import{capturePidIdentities as g}from"../../canvas/pid.js";import{freshChildNodeExecPath as h}from"../branded-host.js";import{resolveAdmittedHostCommand as B}from"../spawn-env.js";import{startBrokerBashJob as y}from"./daemon-ops.js";const I=process.platform==="linux";async function J(e,o,f,n,d){const b=await h();if(I){const{pid:t}=await y(e,{job_id:o.jobId,cwd:n,env:d});return{pgid:t,identity:`launcher:${t}`}}const s=B("bash",d,n),r=S(s,["-c",l,"--",o.cmdSh,o.jobLog,o.jobExit,o.jobRun,o.jobDone,s,String(j(f)),b,o.jobActivity],{cwd:n,detached:!0,stdio:"ignore",env:d}),i=await new Promise((t,u)=>{r.once("error",u),r.pid!==void 0&&t(r.pid)});r.unref();const c=g([i])?.get(i);if(c===void 0){try{process.kill(-i,"SIGKILL")}catch{}throw new Error(`bash job ${o.jobId} started without a process identity`)}return a(o.jobPgid,String(i)),a(o.jobPgidIdentity,c),{pgid:i,identity:c}}m(J,"launchBashJobSupervisor");export{I as BASH_JOBS_USE_LAUNCHER,J as launchBashJobSupervisor};
|
|
@@ -104,8 +104,11 @@ export declare class FaultRetry {
|
|
|
104
104
|
* in production (session-file identity and the new-cycle guard already rule
|
|
105
105
|
* it out below), so it is treated the same as "no progress" rather than
|
|
106
106
|
* blocking the ladder. Returns 'retry-armed' when the timer now owns the
|
|
107
|
-
* first turn, '
|
|
108
|
-
|
|
107
|
+
* first turn, 'fatal' when the episode had already used its whole retry
|
|
108
|
+
* budget and was settled as a fatal fault (no turn runs; the caller's
|
|
109
|
+
* default kickoff must not), 'none' when the caller's default kickoff should
|
|
110
|
+
* proceed. */
|
|
111
|
+
recoverOnStartup(): Promise<'retry-armed' | 'fatal' | 'none'>;
|
|
109
112
|
/** True iff the branch segment strictly after `anchorEntryId` (root→leaf,
|
|
110
113
|
* anchor exclusive) contains an assistant message with non-empty content or
|
|
111
114
|
* a tool result — the two shapes a runtime card and a `content: []`
|
|
@@ -113,7 +116,11 @@ export declare class FaultRetry {
|
|
|
113
116
|
* branch cannot be distinguished from "no progress" here; callers treat it
|
|
114
117
|
* as no progress rather than as a positive finding. */
|
|
115
118
|
private hasProgressPastAnchor;
|
|
116
|
-
|
|
119
|
+
/** Arms the retry timer when one is owed. Reports what now owns the episode:
|
|
120
|
+
* 'armed' (a timer is armed), 'exhausted' (the episode had no retries left
|
|
121
|
+
* and was settled as a fatal fault), or 'none' (nothing is armed). */
|
|
122
|
+
schedule(): Promise<'armed' | 'exhausted' | 'none'>;
|
|
123
|
+
private settleExhausted;
|
|
117
124
|
/** A run node's retry would land past its turn's retry window: complete the
|
|
118
125
|
* turn with its error now and end the episode, so no recovery turn answers a
|
|
119
126
|
* turn the app already saw complete. The app's next message is a fresh turn
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var ue=Object.defineProperty;var m=(i,e)=>ue(i,"name",{value:e,configurable:!0});import{envModelIntent as j,envNodeCwd as Y,envProfileId as q}from"../../../shared/env.js";import{isContextOverflow as V}from"@earendil-works/pi-ai";import{classify as C}from"../../fault-classifier.js";import{isDaemonTransportApiError as X}from"../../../api/errors.js";import{isModelNotFoundError as fe,isProviderUnconfiguredError as pe}from"../fault.js";import{brokerRecovery as he,mutateBrokerFault as J,mutateBrokerProviderRetry as k}from"./daemon-ops.js";import{extractCoolingDeadline as me}from"../managed-provider-cooling.js";import{extractProviderFailureDetails as ge}from"../../provider-failure.js";import{expandModelCandidates as z,modelRequestFromConfig as H,probeRouteAvailability as ye}from"../../model-routes.js";import{operationIdContext as K}from"../../events/operation-id.js";import{emitEvent as F}from"../../events/emit.js";import{CONNECTION_FAULT_RECOVERY_BODY as ve,PROVIDER_FAULT_RECOVERY_BODY as we,RUN_CONNECTION_FAULT_RETRY_BODY as Fe,formatCard as Q,formatModelFallbackRecovery as be}from"../../../shared/generated-context.js";import{markFatalOnExhaust as O,nextFaultRetry as P,policyFor as Z}from"../fault-recovery.js";import{parseModelSpec as ee}from"../model-selection.js";import{endedByAbort as te}from"../session-cycles.js";import{resolveProviderCandidates as ke}from"../../subscription-state.js";import{announceMessageOrigin as ne}from"../stamp/channel.js";import{LLM_REFUSAL_DIAGNOSTIC as _e,refusalRetryAt as Ie}from"./model-adapter.js";import{isSafetyRefusal as re}from"../../llm/refusal.js";const Ee=2147483647,ie=3e4;function Re(i,e,t){const n=Date.parse(i);return!Number.isFinite(n)||t+e-n>ie}m(Re,"runTurnRetryExpired");class _ extends Error{static{m(this,"StaleOutcome")}}const Se=1e3,Ae=3e4;function Me(i,e){return i!=="connection"?we:e?Fe:ve}m(Me,"retryCardBody");function I(i){return{link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,since:i.since,operation_id:i.operation_id,...i.anchor_entry_id===void 0?{}:{anchorEntryId:i.anchor_entry_id},...i.provider_error===void 0?{}:{providerError:i.provider_error}}}m(I,"policyFault");function se(i){return i instanceof Error?i.message:String(i)}m(se,"errorText");function oe(i){return X(i)||C("pi\u2192provider",i).disposition==="auto"}m(oe,"isTransientRejection");function A(i){return{link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,...i.operation_id===void 0?{}:{operation_id:i.operation_id},...i.since===void 0?{}:{since:i.since},...i.anchorEntryId===void 0?{}:{anchor_entry_id:i.anchorEntryId},...i.providerError===void 0?{}:{provider_error:i.providerError}}}m(A,"faultInput");function ae(i,e){const t=m(n=>`${n.providerId}/${n.modelId}`===e.modelSpec,"onLiveModel");return(i.find(n=>t(n)&&n.thinkingLevel===e.thinkingLevel)??i.find(t))?.rank}m(ae,"liveRouteRank");function de(i){const e=C("pi\u2192provider",i),t=i.diagnostics?.some(n=>n.type===_e)??!1;return{classified:e,disposition:t?"fatal":e.disposition}}m(de,"settledProviderFault");function Qe(i,e){if(i.stopReason!=="error"||te(i)||re(i)||V(i,e))return!1;const{classified:t,disposition:n}=de(i);return n==="auto"&&(t.kind==="connection"||t.kind==="rate-limit"||t.kind==="overloaded")}m(Qe,"willDaemonRetry");const xe=/does not support assistant message prefill|conversation must end with a user message/i;function Te(i,e){return e&&xe.test(i)}m(Te,"isPrefillRejectionDuringEpisode");class Ze{static{m(this,"FaultRetry")}deps;timer=null;failedRetryRoutes=new Set;notFoundModels=new Set;providerFallbackInFlight=null;notFoundFallbackInFlight=null;stagedCapacityFailure=null;agentStarts=0;constructor(e){this.deps=e}clearTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}reset(){this.clearTimer(),this.deps.turnEnds?.abandon(),this.providerFallbackInFlight=null,this.notFoundFallbackInFlight=null,this.stagedCapacityFailure=null}abandonTurnEnd(){this.deps.turnEnds?.abandon()}state(){return he(this.deps.nodeId)}async recordFault(e){return(await J(this.deps.nodeId,{op:"record",input:e})).fault}async clearFault(e,t=!1){await J(this.deps.nodeId,{op:"clear",...e===void 0?{}:{link:e},...t?{preserve_episode:!0}:{}})}sessionId(e){const t=e.sessionManager.getSessionId?.();return typeof t=="string"&&t!==""?t:null}async clearEpisode(e){const t=this.sessionId(e);t!==null&&await k(this.deps.nodeId,{op:"clear",session_id:t})}onEvent(e){e.type==="agent_start"&&this.agentStarts++,this.maybeSwitchProviderForRetry(e),this.maybeFallbackOnUnusableModel(e)}isActiveAutoFault(e){return e===null||e.retry.disposition!=="auto"||e.retry.by!=="daemon"?!1:e.kind==="connection"||e.kind==="rate-limit"||e.kind==="overloaded"}async publishSettledProviderFault(e){const t={agentEnd:e.stagedProviderAgentEnd,watchdogAbort:e.stagedWatchdogAbort,refreshAbort:e.stagedRefreshAbort,overflowFailure:e.stagedOverflowFailure,capacityFailure:this.stagedCapacityFailure?.generation===e?this.stagedCapacityFailure:null};this.stagedCapacityFailure=null,e.stagedProviderAgentEnd=null,e.stagedWatchdogAbort=!1,e.stagedRefreshAbort=!1,e.stagedOverflowFailure=null;const n=this.agentStarts,r=m(()=>{if(this.deps.installedGeneration()!==e||this.agentStarts!==n)throw new _},"assertCurrent");try{await this.retryWhileDaemonUnreachable("settled turn fault write",()=>this.recordStagedOutcome(e,t,r),r)}catch(s){if(!(s instanceof _))throw s}}async retryWhileDaemonUnreachable(e,t,n){let r=Se;for(;;){try{await t();return}catch(s){if(!X(s))throw s;F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`${e} waiting for the daemon: ${s.message}`}})}await new Promise(s=>setTimeout(s,r)),r=Math.min(r*2,Ae),n()}}async recordStagedOutcome(e,t,n){const{agentEnd:r,watchdogAbort:s,refreshAbort:a,overflowFailure:l,capacityFailure:c}=t,o=e.session,f=m(async b=>{n(),await this.clearEpisode(b)},"clearEpisode"),g=m(async b=>{n(),await this.recordFault(b)},"recordFault"),p=m(async(b,ce)=>{n(),await this.recordPendingProviderFault(b,ce)},"recordPendingProviderFault"),y=await this.state(),u=y.fault,h=y.episode,E=this.sessionId(o),w=this.isActiveAutoFault(u)&&u.link==="pi\u2192provider"?u:h?.state==="admitted"&&h.session_id===E&&this.isActiveAutoFault(h.fault)?h.fault:null,v=w===null?null:{since:w.since,anchorEntryId:w.anchor_entry_id,attempt:w.retry.attempt},d=[...Array.isArray(r?.messages)?r.messages:[]].reverse().find(b=>typeof b=="object"&&b!==null&&b.role==="assistant");if(c!==null||l!==null){await f(o),await g({link:"pi\u2192provider",op:"context overflow recovery",kind:"context-overflow",retry:{disposition:"fatal"},message:c?.errorMessage??l.errorMessage,anchor_entry_id:o.sessionManager.getLeafId?.()??void 0});return}if(s&&d?.stopReason==="aborted"){await p(o,{link:"pi\u2192provider",op:"generation turn",kind:"connection",retry:{disposition:"auto",by:"daemon",...v?.attempt!==void 0?{attempt:v.attempt}:{}},message:typeof d.errorMessage=="string"?d.errorMessage:"engine error (no errorMessage recorded)",...v!==null?{since:v.since}:{},anchor_entry_id:this.retryAnchor(o,v?.anchorEntryId)});return}if(d?.stopReason!=="error"){d!==void 0&&await f(o);return}if(a){await f(o);return}if(re(d)){await f(o);return}const M=typeof d.errorMessage=="string"?d.errorMessage:"engine error (no errorMessage recorded)";if(te({stopReason:"error",errorMessage:M})){await f(o);return}const R=V(d,o.model?.contextWindow),x=de(d),D=v!==null&&x.disposition!=="auto"&&Te(M,!0),le=D?{kind:"connection",disposition:"auto"}:x.classified,S=D?"auto":x.disposition,L=me(d),N=Ie(d),G=ge(d),U=typeof d.provider=="string"&&d.provider!==""?d.provider:o.model?.provider,W=typeof d.model=="string"&&d.model!==""?d.model:o.model?.id,B=G===void 0||U===void 0||W===void 0?void 0:{provider:U,model:W,...G};R&&await f(o);const T={link:"pi\u2192provider",op:"generation turn",kind:R?"context-overflow":le.kind,retry:R?{disposition:"fatal"}:S==="auto"?{disposition:"auto",by:"daemon",...L!==void 0?{nextAt:new Date(L).toISOString()}:{},...v?.attempt!==void 0?{attempt:v.attempt}:{}}:{disposition:S,...N!==void 0?{nextAt:new Date(N).toISOString()}:{}},message:M,...!R&&S==="auto"&&v!==null?{since:v.since}:{},anchor_entry_id:!R&&S==="auto"?this.retryAnchor(o,v?.anchorEntryId):o.sessionManager.getLeafId?.()??void 0,...B===void 0?{}:{provider_error:B}};T.retry.disposition==="auto"?await p(o,T):(await f(o),await g(T))}retryAnchor(e,t){const n=e.sessionManager.getLeafId?.()??void 0;return t!==void 0&&!this.hasProgressPastAnchor(e,t)?t:n}async recoverOnStartup(){const e=this.deps.installedGeneration();if(e===void 0)return"none";const t=(await this.state()).episode,n=this.sessionId(e.session);if(this.deps.cfg.newCycle===!0)return t!==null&&n!==null&&t.session_id===n&&await k(this.deps.nodeId,{op:"invalidate",session_id:n}),"none";if(t===null||n===null||t.session_id!==n)return"none";if(t.state==="admitted")if(t.fault.anchor_entry_id!==void 0&&!this.hasProgressPastAnchor(e.session,t.fault.anchor_entry_id)){if(!(await k(this.deps.nodeId,{op:"reopen",session_id:n})).applied)return"none"}else return await this.clearEpisode(e.session),"none";else if(t.state!=="pending")return"none";return await this.recordFault(A(I(t.fault))),await this.schedule(),"retry-armed"}hasProgressPastAnchor(e,t){const n=e.sessionManager.getBranch(),r=n.findIndex(a=>a.id===t);return(r===-1?n:n.slice(r+1)).some(a=>{if(a.type!=="message")return!1;const{message:l}=a;return l.role==="toolResult"?!0:l.role==="assistant"&&Array.isArray(l.content)&&l.content.length>0})}async schedule(){const e=this.deps.installedGeneration(),t=this.deps.currentSession();if(t.isStreaming||this.timer!==null)return;if(e===void 0){this.abandonTurnEnd();return}const n=await this.state(),r=n.fault;if(!this.isActiveAutoFault(r)||this.sessionId(t)!==null&&!this.pendingEpisodeMatches(t,r,n)){this.clearTimer(),this.abandonTurnEnd();return}const s=Date.now(),a=this.faultRetryDelayMs(r,s);if(a!==null&&this.deps.turnEnds?.holding()===!0&&Re(r.since,a,s)){await this.endRunTurnOnFault(t,r,a);return}a!==null?this.clearAndArmFaultRetry(a,e):this.abandonTurnEnd()}async endRunTurnOnFault(e,t,n){this.clearTimer(),this.abandonTurnEnd(),F({level:"info",event:"broker.runtime.diagnostic",fields:{message:`run turn completed on provider fault: ${t.kind} retry in ${n}ms is past the ${ie}ms turn window (since ${t.since}, attempt ${t.retry.attempt??0})`}}),await this.clearEpisode(e),await this.clearFault("pi\u2192provider")}async rearmIfStranded(){if(this.timer!==null||this.deps.installedGeneration()===void 0)return;const e=this.deps.currentSession();if(e.isStreaming)return;const t=await this.state();!this.isActiveAutoFault(t.fault)||!this.pendingEpisodeMatches(e,t.fault,t)||await this.schedule()}maybeSwitchProviderForRetry(e){if(e.type!=="auto_retry_start")return;const t=C("pi\u2192provider",e.errorMessage??"").kind;if(t!=="rate-limit"&&t!=="connection"||this.providerFallbackInFlight!==null||this.deps.cfg.modelExact===!0)return;const n=this.deps.installedGeneration();if(n===void 0)return;const r=this.deps.currentSession(),s=this.deps.currentServices(),a=this.deps.formatModelSpec(r.model,r.thinkingLevel);if(a===null)return;const l=H(this.deps.cfg.model,j(),!1);if(l===void 0)return;const c=z(l,Y()??this.deps.cfg.cwd,q()),o=ee(a),f=ae(c,o),g=f===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(c.slice(f+1),this.deps.registryOf(s),r.getContextUsage()?.tokens,u=>!this.failedRetryRoutes.has(u.routeId));if(g.route===void 0){g.capacityExhausted&&(this.stageCapacityFailure(n,r),queueMicrotask(()=>{this.deps.installedGeneration()===n&&this.deps.currentSession()===r&&r.abortRetry()}));return}const p=g.route,y=(async()=>{const u=this.deps.registryOf(s).find(p.providerId,p.modelId);if(!u||(await r.setModel(u),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r))return;const h=`${p.providerId}/${p.modelId}`===o.modelSpec?o.thinkingLevel:p.thinkingLevel;r.setThinkingLevel(h??"off"),this.failedRetryRoutes.add(p.routeId),await this.deps.broadcastModelChanged(!1)})().catch(u=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`provider route fallback failed: ${u instanceof Error?u.message:String(u)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.providerFallbackInFlight=null)});this.providerFallbackInFlight=y}maybeFallbackOnUnusableModel(e){if(e.type!=="message_end"||e.message.role!=="assistant"||e.message.stopReason!=="error")return;const t=pe(e.message.errorMessage??"");if(!t&&!fe(e.message.errorMessage??"")||this.deps.cfg.modelExact===!0||this.notFoundFallbackInFlight!==null)return;const n=this.deps.installedGeneration();if(n===void 0)return;const r=this.deps.currentSession(),s=this.deps.currentServices(),a=this.deps.formatModelSpec(r.model,r.thinkingLevel);if(a===null)return;const l=ee(a);this.notFoundModels.add(l.modelSpec);const c=H(this.deps.cfg.model,j(),!1),o=c?z(c,Y()??this.deps.cfg.cwd,q()):[],f=ae(o,l),g=f===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(o.slice(f+1),this.deps.registryOf(s),r.getContextUsage()?.tokens,h=>!this.notFoundModels.has(`${h.providerId}/${h.modelId}`));if(g.route===void 0){g.capacityExhausted&&this.stageCapacityFailure(n,r);return}const p=g.route,y=`${p.providerId}/${p.modelId}${p.thinkingLevel?`:${p.thinkingLevel}`:""}`,u=(async()=>{const h=this.deps.registryOf(s).find(p.providerId,p.modelId);if(h===void 0){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback skipped: ${y} is not registered`}});return}if(await r.setModel(h),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r)return;r.setThinkingLevel(p.thinkingLevel??"off"),await this.deps.broadcastModelChanged();const E=this.deps.formatModelSpec(r.model,r.thinkingLevel)??y;if(F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback: ${a} unavailable \u2192 ${E}; re-driving turn`}}),await r.agent.waitForIdle(),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r)return;t&&await this.clearFault("pi\u2192provider");const w=Q("recovery",{},be(a,E,t?"credential":"not-found"));ne(w,{type:"runtime",kind:"recovery"}),await this.deps.turnAdmission.admit(v=>K.fresh(()=>r.prompt(w,{preflightResult:v})))})().catch(h=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback failed: ${h instanceof Error?h.message:String(h)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.notFoundFallbackInFlight=null)});this.notFoundFallbackInFlight=u}nextEligibleFallbackRoute(e,t,n,r){let s=0,a=0;for(const l of e){if(!r(l)||ke([{...l,...ye(l,t),automaticFallback:!0}]).candidates.length===0)continue;const c=t.find(l.providerId,l.modelId);if(c!==void 0){if(s++,this.targetCannotFitContext(c.contextWindow,n)){a++;continue}return{route:l,capacityExhausted:!1}}}return{route:void 0,capacityExhausted:s>0&&s===a}}targetCannotFitContext(e,t){return Number.isFinite(e)&&Number.isFinite(t)&&e<t}stageCapacityFailure(e,t){const n=t.getContextUsage()?.tokens;this.stagedCapacityFailure={generation:e,errorMessage:Number.isFinite(n)?`All available fallback models have context windows smaller than the current conversation (${n} tokens).`:"All available fallback models have context windows smaller than the current conversation."}}async recordPendingProviderFault(e,t){const n=this.sessionId(e);if(n===null){await this.recordFault(t);return}await k(this.deps.nodeId,{op:"record-pending",session_id:n,input:t})}pendingEpisodeMatches(e,t,n){const r=this.sessionId(e),s=n.episode;return r!==null&&s!==null&&s.state==="pending"&&s.session_id===r&&s.fault.fault_id===t.fault_id}faultRetryDelayMs(e,t){if(e.retry.nextAt!==void 0){const n=Date.parse(e.retry.nextAt)-t;return Number.isFinite(n)?Math.max(0,n):null}return P(I(e),t)?.delayMs??null}clearAndArmFaultRetry(e,t){if(this.deps.installedGeneration()!==t)return;this.clearTimer();const n=Math.min(Math.max(0,e),Ee),r=Math.max(0,e-n);this.timer=setTimeout(()=>{if(this.timer=null,this.deps.installedGeneration()===t){if(r>0){this.clearAndArmFaultRetry(r,t);return}this.runFaultRetry().catch(s=>{this.abandonTurnEnd(),F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry fire failed: ${s instanceof Error?s.message:String(s)}`}})})}},n),typeof this.timer.unref=="function"&&this.timer.unref()}async runFaultRetry(){await this.attemptFaultRetry()===void 0&&this.abandonTurnEnd()}async attemptFaultRetry(){const e=this.deps.installedGeneration();if(e===void 0)return;const t=e.session;if(t.isStreaming)return;const n=(await this.state()).fault;if(!this.isActiveAutoFault(n))return;let r=n;if(n.kind==="connection"&&this.deps.turnEnds===void 0){const y=await this.deps.retryProbe();if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming)return;const u=(await this.state()).fault;if(!this.isActiveAutoFault(u)||u.since!==n.since)return;if(!y)return this.clearAndArmFaultRetry(Z(u.kind).baseDelayMs,e),"rearmed";r=u}const s=this.deps.turnEnds!==void 0,a=r.retry.attempt??0,l=Z(r.kind);if(a>=l.max){await this.recordFault(A(O(I(r)))),await this.clearEpisode(t);return}const c=P(I(r),Date.now());if(c===null){await this.recordFault(A(O(I(r)))),await this.clearEpisode(t);return}if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t)return;let o,f;const g=await this.deps.turnAdmission.admit(async y=>{const u=(await this.state()).fault;if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||!this.isActiveAutoFault(u)||u.since!==r.since){y();return}const h=this.sessionId(t);if(h===null){y();return}if(!(await k(this.deps.nodeId,{op:"admit",session_id:h,fault_id:u.fault_id,retry:c.fault.retry})).applied){y();return}const w=r.anchor_entry_id;if(w!==void 0&&(t.sessionManager.getLeafId?.()??null)!==w&&!this.hasProgressPastAnchor(t,w))try{const d=await t.navigateTree(w);if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t){y();return}d.cancelled||this.deps.reWelcomeAll()}catch(d){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`fault retry rewind skipped: ${d instanceof Error?d.message:String(d)}`}})}const v=Q("recovery",s?{attempt:c.attempt}:{attempt:c.attempt,max:c.max},Me(r.kind,s));ne(v,{type:"runtime",kind:"recovery"}),this.deps.turnEnds?.beginRecovery();const $=this.agentStarts;try{await K.fresh(()=>t.prompt(v,{preflightResult:y}))}catch(d){o={error:d,faultId:u.fault_id,startsBefore:$},oe(d)||(f={code:"model_error",message:`recovery prompt rejected: ${se(d)}`});return}finally{this.deps.turnEnds?.recoveryReturned(f)}return"prompted"});if(o===void 0)return g;const p=await this.settleRejectedRecoveryPrompt(e,t,o);if(p==="stale")throw o.error;return p==="rearmed"?"rearmed":void 0}async settleRejectedRecoveryPrompt(e,t,n){const r=this.sessionId(t);if(r===null)return"stale";const s=m(()=>{if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||this.agentStarts!==n.startsBefore)throw new _},"assertCurrent"),a=se(n.error),l=oe(n.error);try{let c;return await this.retryWhileDaemonUnreachable("rejected recovery prompt settlement",async()=>{s();const{episode:o}=await this.state();if(s(),o===null||o.state!=="admitted"||o.session_id!==r||o.fault.fault_id!==n.faultId)throw new _;const f=I(o.fault),g=l&&this.deps.turnEnds===void 0?P(f,Date.now()):null,p=g===null?O(l?f:{...f,kind:"other",message:`recovery prompt rejected: ${a}`}):{...f,retry:{...f.retry,nextAt:g.nextAt}};if(s(),!(await k(this.deps.nodeId,{op:"settle-rejected",session_id:r,fault_id:n.faultId,reopen:g!==null,input:A(p)})).applied)throw new _;c=g?.nextAt},s),c===void 0?(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected, ending the retry episode: ${a}`}}),"ended"):(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected before any turn started, retrying at ${c}: ${a}`}}),await this.schedule(),"rearmed")}catch(c){if(!(c instanceof _))throw c;return"stale"}}}export{Ze as FaultRetry,ie as RUN_TURN_RETRY_WINDOW_MS,Te as isPrefillRejectionDuringEpisode,Me as retryCardBody,Re as runTurnRetryExpired,de as settledProviderFault,Qe as willDaemonRetry};
|
|
1
|
+
var ue=Object.defineProperty;var m=(i,e)=>ue(i,"name",{value:e,configurable:!0});import{envModelIntent as B,envNodeCwd as j,envProfileId as Y}from"../../../shared/env.js";import{isContextOverflow as q}from"@earendil-works/pi-ai";import{classify as T}from"../../fault-classifier.js";import{isDaemonTransportApiError as V}from"../../../api/errors.js";import{isModelNotFoundError as fe,isProviderUnconfiguredError as pe}from"../fault.js";import{brokerRecovery as he,mutateBrokerFault as X,mutateBrokerProviderRetry as k}from"./daemon-ops.js";import{extractCoolingDeadline as me}from"../managed-provider-cooling.js";import{extractProviderFailureDetails as ge}from"../../provider-failure.js";import{expandModelCandidates as J,modelRequestFromConfig as z,probeRouteAvailability as ye}from"../../model-routes.js";import{operationIdContext as H}from"../../events/operation-id.js";import{emitEvent as F}from"../../events/emit.js";import{CONNECTION_FAULT_RECOVERY_BODY as ve,PROVIDER_FAULT_RECOVERY_BODY as we,RUN_CONNECTION_FAULT_RETRY_BODY as Fe,formatCard as K,formatModelFallbackRecovery as be}from"../../../shared/generated-context.js";import{markFatalOnExhaust as Q,nextFaultRetry as C,policyFor as Z}from"../fault-recovery.js";import{parseModelSpec as ee}from"../model-selection.js";import{endedByAbort as te}from"../session-cycles.js";import{resolveProviderCandidates as ke}from"../../subscription-state.js";import{announceMessageOrigin as ne}from"../stamp/channel.js";import{LLM_REFUSAL_DIAGNOSTIC as _e,refusalRetryAt as Ie}from"./model-adapter.js";import{isSafetyRefusal as re}from"../../llm/refusal.js";const Ee=2147483647,ie=3e4;function Re(i,e,t){const n=Date.parse(i);return!Number.isFinite(n)||t+e-n>ie}m(Re,"runTurnRetryExpired");class _ extends Error{static{m(this,"StaleOutcome")}}const Se=1e3,Ae=3e4;function Me(i,e){return i!=="connection"?we:e?Fe:ve}m(Me,"retryCardBody");function R(i){return{link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,since:i.since,operation_id:i.operation_id,...i.anchor_entry_id===void 0?{}:{anchorEntryId:i.anchor_entry_id},...i.provider_error===void 0?{}:{providerError:i.provider_error}}}m(R,"policyFault");function se(i){return i instanceof Error?i.message:String(i)}m(se,"errorText");function oe(i){return V(i)||T("pi\u2192provider",i).disposition==="auto"}m(oe,"isTransientRejection");function O(i){return{link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,...i.operation_id===void 0?{}:{operation_id:i.operation_id},...i.since===void 0?{}:{since:i.since},...i.anchorEntryId===void 0?{}:{anchor_entry_id:i.anchorEntryId},...i.providerError===void 0?{}:{provider_error:i.providerError}}}m(O,"faultInput");function ae(i,e){const t=m(n=>`${n.providerId}/${n.modelId}`===e.modelSpec,"onLiveModel");return(i.find(n=>t(n)&&n.thinkingLevel===e.thinkingLevel)??i.find(t))?.rank}m(ae,"liveRouteRank");function de(i){const e=T("pi\u2192provider",i),t=i.diagnostics?.some(n=>n.type===_e)??!1;return{classified:e,disposition:t?"fatal":e.disposition}}m(de,"settledProviderFault");function Qe(i,e){if(i.stopReason!=="error"||te(i)||re(i)||q(i,e))return!1;const{classified:t,disposition:n}=de(i);return n==="auto"&&(t.kind==="connection"||t.kind==="rate-limit"||t.kind==="overloaded")}m(Qe,"willDaemonRetry");const xe=/does not support assistant message prefill|conversation must end with a user message/i;function Te(i,e){return e&&xe.test(i)}m(Te,"isPrefillRejectionDuringEpisode");class Ze{static{m(this,"FaultRetry")}deps;timer=null;failedRetryRoutes=new Set;notFoundModels=new Set;providerFallbackInFlight=null;notFoundFallbackInFlight=null;stagedCapacityFailure=null;agentStarts=0;constructor(e){this.deps=e}clearTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}reset(){this.clearTimer(),this.deps.turnEnds?.abandon(),this.providerFallbackInFlight=null,this.notFoundFallbackInFlight=null,this.stagedCapacityFailure=null}abandonTurnEnd(){this.deps.turnEnds?.abandon()}state(){return he(this.deps.nodeId)}async recordFault(e){return(await X(this.deps.nodeId,{op:"record",input:e})).fault}async clearFault(e,t=!1){await X(this.deps.nodeId,{op:"clear",...e===void 0?{}:{link:e},...t?{preserve_episode:!0}:{}})}sessionId(e){const t=e.sessionManager.getSessionId?.();return typeof t=="string"&&t!==""?t:null}async clearEpisode(e){const t=this.sessionId(e);t!==null&&await k(this.deps.nodeId,{op:"clear",session_id:t})}onEvent(e){e.type==="agent_start"&&this.agentStarts++,this.maybeSwitchProviderForRetry(e),this.maybeFallbackOnUnusableModel(e)}isActiveAutoFault(e){return e===null||e.retry.disposition!=="auto"||e.retry.by!=="daemon"?!1:e.kind==="connection"||e.kind==="rate-limit"||e.kind==="overloaded"}async publishSettledProviderFault(e){const t={agentEnd:e.stagedProviderAgentEnd,watchdogAbort:e.stagedWatchdogAbort,refreshAbort:e.stagedRefreshAbort,overflowFailure:e.stagedOverflowFailure,capacityFailure:this.stagedCapacityFailure?.generation===e?this.stagedCapacityFailure:null};this.stagedCapacityFailure=null,e.stagedProviderAgentEnd=null,e.stagedWatchdogAbort=!1,e.stagedRefreshAbort=!1,e.stagedOverflowFailure=null;const n=this.agentStarts,r=m(()=>{if(this.deps.installedGeneration()!==e||this.agentStarts!==n)throw new _},"assertCurrent");try{await this.retryWhileDaemonUnreachable("settled turn fault write",()=>this.recordStagedOutcome(e,t,r),r)}catch(s){if(!(s instanceof _))throw s}}async retryWhileDaemonUnreachable(e,t,n){let r=Se;for(;;){try{await t();return}catch(s){if(!V(s))throw s;F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`${e} waiting for the daemon: ${s.message}`}})}await new Promise(s=>setTimeout(s,r)),r=Math.min(r*2,Ae),n()}}async recordStagedOutcome(e,t,n){const{agentEnd:r,watchdogAbort:s,refreshAbort:a,overflowFailure:l,capacityFailure:c}=t,o=e.session,f=m(async b=>{n(),await this.clearEpisode(b)},"clearEpisode"),g=m(async b=>{n(),await this.recordFault(b)},"recordFault"),p=m(async(b,ce)=>{n(),await this.recordPendingProviderFault(b,ce)},"recordPendingProviderFault"),y=await this.state(),u=y.fault,h=y.episode,I=this.sessionId(o),w=this.isActiveAutoFault(u)&&u.link==="pi\u2192provider"?u:h?.state==="admitted"&&h.session_id===I&&this.isActiveAutoFault(h.fault)?h.fault:null,v=w===null?null:{since:w.since,anchorEntryId:w.anchor_entry_id,attempt:w.retry.attempt},d=[...Array.isArray(r?.messages)?r.messages:[]].reverse().find(b=>typeof b=="object"&&b!==null&&b.role==="assistant");if(c!==null||l!==null){await f(o),await g({link:"pi\u2192provider",op:"context overflow recovery",kind:"context-overflow",retry:{disposition:"fatal"},message:c?.errorMessage??l.errorMessage,anchor_entry_id:o.sessionManager.getLeafId?.()??void 0});return}if(s&&d?.stopReason==="aborted"){await p(o,{link:"pi\u2192provider",op:"generation turn",kind:"connection",retry:{disposition:"auto",by:"daemon",...v?.attempt!==void 0?{attempt:v.attempt}:{}},message:typeof d.errorMessage=="string"?d.errorMessage:"engine error (no errorMessage recorded)",...v!==null?{since:v.since}:{},anchor_entry_id:this.retryAnchor(o,v?.anchorEntryId)});return}if(d?.stopReason!=="error"){d!==void 0&&await f(o);return}if(a){await f(o);return}if(re(d)){await f(o);return}const A=typeof d.errorMessage=="string"?d.errorMessage:"engine error (no errorMessage recorded)";if(te({stopReason:"error",errorMessage:A})){await f(o);return}const E=q(d,o.model?.contextWindow),M=de(d),$=v!==null&&M.disposition!=="auto"&&Te(A,!0),le=$?{kind:"connection",disposition:"auto"}:M.classified,S=$?"auto":M.disposition,D=me(d),L=Ie(d),N=ge(d),G=typeof d.provider=="string"&&d.provider!==""?d.provider:o.model?.provider,U=typeof d.model=="string"&&d.model!==""?d.model:o.model?.id,W=N===void 0||G===void 0||U===void 0?void 0:{provider:G,model:U,...N};E&&await f(o);const x={link:"pi\u2192provider",op:"generation turn",kind:E?"context-overflow":le.kind,retry:E?{disposition:"fatal"}:S==="auto"?{disposition:"auto",by:"daemon",...D!==void 0?{nextAt:new Date(D).toISOString()}:{},...v?.attempt!==void 0?{attempt:v.attempt}:{}}:{disposition:S,...L!==void 0?{nextAt:new Date(L).toISOString()}:{}},message:A,...!E&&S==="auto"&&v!==null?{since:v.since}:{},anchor_entry_id:!E&&S==="auto"?this.retryAnchor(o,v?.anchorEntryId):o.sessionManager.getLeafId?.()??void 0,...W===void 0?{}:{provider_error:W}};x.retry.disposition==="auto"?await p(o,x):(await f(o),await g(x))}retryAnchor(e,t){const n=e.sessionManager.getLeafId?.()??void 0;return t!==void 0&&!this.hasProgressPastAnchor(e,t)?t:n}async recoverOnStartup(){const e=this.deps.installedGeneration();if(e===void 0)return"none";const t=(await this.state()).episode,n=this.sessionId(e.session);if(this.deps.cfg.newCycle===!0)return t!==null&&n!==null&&t.session_id===n&&await k(this.deps.nodeId,{op:"invalidate",session_id:n}),"none";if(t===null||n===null||t.session_id!==n)return"none";if(t.state==="admitted")if(t.fault.anchor_entry_id!==void 0&&!this.hasProgressPastAnchor(e.session,t.fault.anchor_entry_id)){if(!(await k(this.deps.nodeId,{op:"reopen",session_id:n})).applied)return"none"}else return await this.clearEpisode(e.session),"none";else if(t.state!=="pending")return"none";await this.recordFault(O(R(t.fault)));const r=await this.schedule();return r==="armed"?"retry-armed":r==="exhausted"?"fatal":"none"}hasProgressPastAnchor(e,t){const n=e.sessionManager.getBranch(),r=n.findIndex(a=>a.id===t);return(r===-1?n:n.slice(r+1)).some(a=>{if(a.type!=="message")return!1;const{message:l}=a;return l.role==="toolResult"?!0:l.role==="assistant"&&Array.isArray(l.content)&&l.content.length>0})}async schedule(){const e=this.deps.installedGeneration(),t=this.deps.currentSession();if(t.isStreaming||this.timer!==null)return this.timer!==null?"armed":"none";if(e===void 0)return this.abandonTurnEnd(),"none";const n=await this.state(),r=n.fault;if(!this.isActiveAutoFault(r)||this.sessionId(t)!==null&&!this.pendingEpisodeMatches(t,r,n))return this.clearTimer(),this.abandonTurnEnd(),"none";const s=Date.now(),a=this.faultRetryDelayMs(r,s);return a!==null&&this.deps.turnEnds?.holding()===!0&&Re(r.since,a,s)?(await this.endRunTurnOnFault(t,r,a),"none"):a!==null?(this.clearAndArmFaultRetry(a,e),this.timer!==null?"armed":"none"):(this.abandonTurnEnd(),r.retry.nextAt!==void 0||this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming?"none":(await this.settleExhausted(t,r),"exhausted"))}async settleExhausted(e,t){await this.recordFault(O(Q(R(t)))),await this.clearEpisode(e)}async endRunTurnOnFault(e,t,n){this.clearTimer(),this.abandonTurnEnd(),F({level:"info",event:"broker.runtime.diagnostic",fields:{message:`run turn completed on provider fault: ${t.kind} retry in ${n}ms is past the ${ie}ms turn window (since ${t.since}, attempt ${t.retry.attempt??0})`}}),await this.clearEpisode(e),await this.clearFault("pi\u2192provider")}async rearmIfStranded(){if(this.timer!==null||this.deps.installedGeneration()===void 0)return;const e=this.deps.currentSession();if(e.isStreaming)return;const t=await this.state();!this.isActiveAutoFault(t.fault)||!this.pendingEpisodeMatches(e,t.fault,t)||await this.schedule()}maybeSwitchProviderForRetry(e){if(e.type!=="auto_retry_start")return;const t=T("pi\u2192provider",e.errorMessage??"").kind;if(t!=="rate-limit"&&t!=="connection"||this.providerFallbackInFlight!==null||this.deps.cfg.modelExact===!0)return;const n=this.deps.installedGeneration();if(n===void 0)return;const r=this.deps.currentSession(),s=this.deps.currentServices(),a=this.deps.formatModelSpec(r.model,r.thinkingLevel);if(a===null)return;const l=z(this.deps.cfg.model,B(),!1);if(l===void 0)return;const c=J(l,j()??this.deps.cfg.cwd,Y()),o=ee(a),f=ae(c,o),g=f===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(c.slice(f+1),this.deps.registryOf(s),r.getContextUsage()?.tokens,u=>!this.failedRetryRoutes.has(u.routeId));if(g.route===void 0){g.capacityExhausted&&(this.stageCapacityFailure(n,r),queueMicrotask(()=>{this.deps.installedGeneration()===n&&this.deps.currentSession()===r&&r.abortRetry()}));return}const p=g.route,y=(async()=>{const u=this.deps.registryOf(s).find(p.providerId,p.modelId);if(!u||(await r.setModel(u),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r))return;const h=`${p.providerId}/${p.modelId}`===o.modelSpec?o.thinkingLevel:p.thinkingLevel;r.setThinkingLevel(h??"off"),this.failedRetryRoutes.add(p.routeId),await this.deps.broadcastModelChanged(!1)})().catch(u=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`provider route fallback failed: ${u instanceof Error?u.message:String(u)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.providerFallbackInFlight=null)});this.providerFallbackInFlight=y}maybeFallbackOnUnusableModel(e){if(e.type!=="message_end"||e.message.role!=="assistant"||e.message.stopReason!=="error")return;const t=pe(e.message.errorMessage??"");if(!t&&!fe(e.message.errorMessage??"")||this.deps.cfg.modelExact===!0||this.notFoundFallbackInFlight!==null)return;const n=this.deps.installedGeneration();if(n===void 0)return;const r=this.deps.currentSession(),s=this.deps.currentServices(),a=this.deps.formatModelSpec(r.model,r.thinkingLevel);if(a===null)return;const l=ee(a);this.notFoundModels.add(l.modelSpec);const c=z(this.deps.cfg.model,B(),!1),o=c?J(c,j()??this.deps.cfg.cwd,Y()):[],f=ae(o,l),g=f===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(o.slice(f+1),this.deps.registryOf(s),r.getContextUsage()?.tokens,h=>!this.notFoundModels.has(`${h.providerId}/${h.modelId}`));if(g.route===void 0){g.capacityExhausted&&this.stageCapacityFailure(n,r);return}const p=g.route,y=`${p.providerId}/${p.modelId}${p.thinkingLevel?`:${p.thinkingLevel}`:""}`,u=(async()=>{const h=this.deps.registryOf(s).find(p.providerId,p.modelId);if(h===void 0){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback skipped: ${y} is not registered`}});return}if(await r.setModel(h),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r)return;r.setThinkingLevel(p.thinkingLevel??"off"),await this.deps.broadcastModelChanged();const I=this.deps.formatModelSpec(r.model,r.thinkingLevel)??y;if(F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback: ${a} unavailable \u2192 ${I}; re-driving turn`}}),await r.agent.waitForIdle(),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r)return;t&&await this.clearFault("pi\u2192provider");const w=K("recovery",{},be(a,I,t?"credential":"not-found"));ne(w,{type:"runtime",kind:"recovery"}),await this.deps.turnAdmission.admit(v=>H.fresh(()=>r.prompt(w,{preflightResult:v})))})().catch(h=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback failed: ${h instanceof Error?h.message:String(h)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.notFoundFallbackInFlight=null)});this.notFoundFallbackInFlight=u}nextEligibleFallbackRoute(e,t,n,r){let s=0,a=0;for(const l of e){if(!r(l)||ke([{...l,...ye(l,t),automaticFallback:!0}]).candidates.length===0)continue;const c=t.find(l.providerId,l.modelId);if(c!==void 0){if(s++,this.targetCannotFitContext(c.contextWindow,n)){a++;continue}return{route:l,capacityExhausted:!1}}}return{route:void 0,capacityExhausted:s>0&&s===a}}targetCannotFitContext(e,t){return Number.isFinite(e)&&Number.isFinite(t)&&e<t}stageCapacityFailure(e,t){const n=t.getContextUsage()?.tokens;this.stagedCapacityFailure={generation:e,errorMessage:Number.isFinite(n)?`All available fallback models have context windows smaller than the current conversation (${n} tokens).`:"All available fallback models have context windows smaller than the current conversation."}}async recordPendingProviderFault(e,t){const n=this.sessionId(e);if(n===null){await this.recordFault(t);return}await k(this.deps.nodeId,{op:"record-pending",session_id:n,input:t})}pendingEpisodeMatches(e,t,n){const r=this.sessionId(e),s=n.episode;return r!==null&&s!==null&&s.state==="pending"&&s.session_id===r&&s.fault.fault_id===t.fault_id}faultRetryDelayMs(e,t){if(e.retry.nextAt!==void 0){const n=Date.parse(e.retry.nextAt)-t;return Number.isFinite(n)?Math.max(0,n):null}return C(R(e),t)?.delayMs??null}clearAndArmFaultRetry(e,t){if(this.deps.installedGeneration()!==t)return;this.clearTimer();const n=Math.min(Math.max(0,e),Ee),r=Math.max(0,e-n);this.timer=setTimeout(()=>{if(this.timer=null,this.deps.installedGeneration()===t){if(r>0){this.clearAndArmFaultRetry(r,t);return}this.runFaultRetry().catch(s=>{this.abandonTurnEnd(),F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry fire failed: ${s instanceof Error?s.message:String(s)}`}})})}},n),typeof this.timer.unref=="function"&&this.timer.unref()}async runFaultRetry(){await this.attemptFaultRetry()===void 0&&this.abandonTurnEnd()}async attemptFaultRetry(){const e=this.deps.installedGeneration();if(e===void 0)return;const t=e.session;if(t.isStreaming)return;const n=(await this.state()).fault;if(!this.isActiveAutoFault(n))return;let r=n;if(n.kind==="connection"&&this.deps.turnEnds===void 0){const y=await this.deps.retryProbe();if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming)return;const u=(await this.state()).fault;if(!this.isActiveAutoFault(u)||u.since!==n.since)return;if(!y)return this.clearAndArmFaultRetry(Z(u.kind).baseDelayMs,e),"rearmed";r=u}const s=this.deps.turnEnds!==void 0,a=r.retry.attempt??0,l=Z(r.kind),c=a>=l.max?null:C(R(r),Date.now());if(c===null){await this.settleExhausted(t,r);return}if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t)return;let o,f;const g=await this.deps.turnAdmission.admit(async y=>{const u=(await this.state()).fault;if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||!this.isActiveAutoFault(u)||u.since!==r.since){y();return}const h=this.sessionId(t);if(h===null){y();return}if(!(await k(this.deps.nodeId,{op:"admit",session_id:h,fault_id:u.fault_id,retry:c.fault.retry})).applied){y();return}const w=r.anchor_entry_id;if(w!==void 0&&(t.sessionManager.getLeafId?.()??null)!==w&&!this.hasProgressPastAnchor(t,w))try{const d=await t.navigateTree(w);if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t){y();return}d.cancelled||this.deps.reWelcomeAll()}catch(d){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`fault retry rewind skipped: ${d instanceof Error?d.message:String(d)}`}})}const v=K("recovery",s?{attempt:c.attempt}:{attempt:c.attempt,max:c.max},Me(r.kind,s));ne(v,{type:"runtime",kind:"recovery"}),this.deps.turnEnds?.beginRecovery();const P=this.agentStarts;try{await H.fresh(()=>t.prompt(v,{preflightResult:y}))}catch(d){o={error:d,faultId:u.fault_id,startsBefore:P},oe(d)||(f={code:"model_error",message:`recovery prompt rejected: ${se(d)}`});return}finally{this.deps.turnEnds?.recoveryReturned(f)}return"prompted"});if(o===void 0)return g;const p=await this.settleRejectedRecoveryPrompt(e,t,o);if(p==="stale")throw o.error;return p==="rearmed"?"rearmed":void 0}async settleRejectedRecoveryPrompt(e,t,n){const r=this.sessionId(t);if(r===null)return"stale";const s=m(()=>{if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||this.agentStarts!==n.startsBefore)throw new _},"assertCurrent"),a=se(n.error),l=oe(n.error);try{let c;return await this.retryWhileDaemonUnreachable("rejected recovery prompt settlement",async()=>{s();const{episode:o}=await this.state();if(s(),o===null||o.state!=="admitted"||o.session_id!==r||o.fault.fault_id!==n.faultId)throw new _;const f=R(o.fault),g=l&&this.deps.turnEnds===void 0?C(f,Date.now()):null,p=g===null?Q(l?f:{...f,kind:"other",message:`recovery prompt rejected: ${a}`}):{...f,retry:{...f.retry,nextAt:g.nextAt}};if(s(),!(await k(this.deps.nodeId,{op:"settle-rejected",session_id:r,fault_id:n.faultId,reopen:g!==null,input:O(p)})).applied)throw new _;c=g?.nextAt},s),c===void 0?(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected, ending the retry episode: ${a}`}}),"ended"):(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected before any turn started, retrying at ${c}: ${a}`}}),await this.schedule(),"rearmed")}catch(c){if(!(c instanceof _))throw c;return"stale"}}}export{Ze as FaultRetry,ie as RUN_TURN_RETRY_WINDOW_MS,Te as isPrefillRejectionDuringEpisode,Me as retryCardBody,Re as runTurnRetryExpired,de as settledProviderFault,Qe as willDaemonRetry};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Pe=Object.defineProperty;var n=(t,o)=>Pe(t,"name",{value:o,configurable:!0});import{envInboxBetweenTurns as Oe,envModelIntent as Ie,envNodeCwd as De,envNodeId as Ne,envProfileId as Fe,envScopes as Le,isSandboxedNode as $e,scopeAllowed as Be}from"../../shared/env.js";import{createServer as Ue}from"node:net";import{existsSync as ye,readFileSync as Ge,unlinkSync as be}from"node:fs";import{join as Te}from"node:path";import{randomUUID as M}from"node:crypto";import{mark as Ke}from"../timing.js";import{SUBMIT_RESULT_TOOL as je}from"./structured-output.js";import{installSessionManagerStorePatch as We}from"../conversation-store/index.js";import{getAgentDir as Y,ModelRegistry as qe,ModelRuntime as Ve}from"@earendil-works/pi-coding-agent";import{nodeRunDir as Xe,viewSocketPath as He}from"../canvas/paths.js";import{bindBrokerEventSource as Ye}from"../events/source.js";import{emitEvent as T,flushBrokerLog as ze}from"../events/emit.js";import{operationIdContext as Je}from"../events/operation-id.js";import{findMissingPackagePaths as Qe}from"./package-health.js";import{installBrokerConfigSnapshot as Ze}from"../config.js";import{FRONT_DOOR_ENV as en}from"./front-door.js";import{CANVAS_EXTENSIONS as re,piInvocationToSdkConfig as nn}from"./launch.js";import{assertEngineVersion as tn,loadBrokerEngine as rn}from"./broker-sdk.js";import{initCrtrTheme as on}from"./crtr-theme.js";import{resolveBundledPiPackageDir as sn}from"./pi-cli.js";import{captureSessionChatCapabilities as an}from"./command-surface.js";import{InMemoryCredentialStore as dn}from"@earendil-works/pi-ai";import{configureBrokerModelRuntime as ln}from"./broker/model-adapter.js";import{mutateBrokerFault as z,recordBrokerTurn as Re}from"./broker/daemon-ops.js";import{modelRequestFromConfig as cn,expandModelCandidates as mn,parseModelSpec as un,unregisteredConcreteModel as fn}from"../model-routes.js";import{KNOWN_STREAM_WAIT_UI as pn}from"./stream-watchdog.js";import{probeOnline as gn}from"./connectivity.js";import{CRTR_CYCLE_CUSTOM_TYPE as vn,cycleAwareMessages as hn,endedByAbort as wn}from"./session-cycles.js";import{turnErrorOf as Sn}from"../llm/refusal.js";import{projectTranscript as _n}from"./session-visibility.js";import{currentTurnVisibility as yn}from"./turn-visibility.js";import{isUnknownModel as oe}from"./model-selection.js";import{BrokerClientRegistry as bn}from"./broker/client-registry.js";import{ToolGroupTracker as Tn}from"./broker/tool-groups.js";import{TurnReplyTracker as Rn}from"./broker/turn-replies.js";import{onNodeNamed as kn}from"./broker/node-named.js";import{FaultRetry as En,willDaemonRetry as Cn}from"./broker/fault-retry.js";import{RunTurnEnds as Mn}from"./broker/run-turn-end.js";import{createTurnAdmissionGate as xn}from"./broker/turn-admission.js";import{BROKER_IDLE_TURN_START as An,BROKER_RUN_TURN_BUSY as Pn}from"./broker/held-deferred-inbox.js";import{prependRunDeferredMail as On,settleRunDeferredMail as In}from"./broker/run-deferred-mail.js";import{createRunTurnQueue as Dn}from"./broker/run-turn-queue.js";import{EventProjection as Nn}from"./broker/event-projection.js";import{createFrameDispatchContext as Fn,handleFrame as Ln}from"./broker/frame-dispatch.js";import{hydratePersistedAdvertisedCommandMessages as $n,installAdvertisedCommandInvocationContract as Bn}from"./advertised-command-invocation.js";import{RebindDriver as Un}from"./broker/rebind.js";import{beginRuntimeTeardown as Gn}from"./broker/extension-abort.js";import{receiveNodeToken as Kn}from"./broker/node-token.js";import{serveCliRelay as jn}from"./broker/cli-relay.js";import{setTurnCauseSource as Wn}from"./broker/turn-cause.js";import{decodeKickoffOrigin as qn,KICKOFF_ORIGIN_ENV as Vn}from"./stamp/protocol.js";import{announceMessageOrigin as Xn}from"./stamp/channel.js";import{brokerExtensionState as Hn,commitBrokerModel as Yn}from"./broker/daemon-ops.js";import{composerNames as zn}from"./broker/frame-memory-refs.js";import{FrameOverflowError as Jn,normalizeSnapshotTail as Qn}from"./broker-protocol.js";import{ReviewBoundaryNavigationError as pr,REVIEW_BOUNDARY_NAVIGATION_CODE as gr}from"./broker/frame-replies.js";function se(t){return new qe(t.modelRuntime)}n(se,"registryOf");function B(t,o){if(t==null||oe(t))return null;const i=o!==void 0&&o!==""&&o!=="off"?`:${o}`:"";return`${t.provider}/${t.id}${i}`}n(B,"formatModelSpec");function Zn(t,o){const i=B(t,void 0);return i===null?null:`${i}:${o===void 0||o===""?"off":o}`}n(Zn,"formatExactModelSpec");const et=12e4;let J=null;import{brokerSignalClient as Q}from"./broker/signal-stream.js";function nt(){Q().stop();const t=J;if(J=null,t!==null)try{t.dispose()}catch{}}n(nt,"disposeActiveSession");function tt(t,o){const i=Be(o,"crtr:act")?void 0:["bash"],u=t!==void 0&&t.length>0?[...new Set([...t,je])]:t===void 0?void 0:[...t];return{...u!==void 0?{tools:u}:{},...i!==void 0?{excludeTools:i}:{}}}n(tt,"brokerToolOptions");async function mr(t,o=process.hrtime.bigint(),i=new Date().toISOString()){Ye(t);const u=await Kn(),s=await jn(t,u);process.env.CRTR_API_SOCKET=s.path,Je.fresh(()=>{T({level:"info",event:"broker.startup.cli_entry",ts:i,duration_ms:0,fields:{runtime_version:process.version,engine_version:null,model:null}}),T({level:"info",event:"broker.startup.module_loaded",duration_ms:Number(process.hrtime.bigint()-o)/1e6,fields:{runtime_version:process.version,engine_version:null,model:null}})});const a=new Set;let l=null;const d=n((e,r=null)=>{a.has(e)||(a.add(e),Ke(e,{duration_ms:Number(process.hrtime.bigint()-o)/1e6}),T({level:"info",event:e,duration_ms:Number(process.hrtime.bigint()-o)/1e6,fields:{runtime_version:process.version,engine_version:l,model:r}}))},"emitStartupMilestone");process.env[en]="1",process.env.CRTR_BROKER_PID=String(process.pid),delete process.env.TMUX_PANE,delete process.env.TMUX,We(t);const p=await rn();tn(p.VERSION),l=p.VERSION;const S=Xe(t),k=Te(S,"broker-launch.json"),_=JSON.parse(Ge(k,"utf8"));if($e()){if(!_.configSnapshot)throw new Error("broker launch is missing its config snapshot");Ze(_.configSnapshot)}const f=nn(_);f.modelExact===!0?process.env.CRTR_MODEL_EXACT="1":delete process.env.CRTR_MODEL_EXACT;const y=await zn(t);let b,x;const V=n(e=>{b!==void 0?b(e):x??=e},"onSignal");process.on("SIGTERM",()=>V("SIGTERM")),process.on("SIGINT",()=>V("SIGINT"));let{session:U,services:A,resuming:ae,runtime:G}=await it(p,f);d("broker.startup.session_built");const E=(await Hn(t)).node.review_binding?.review_id,R=Oe();J=U;const D=gn;try{process.env.PI_PACKAGE_DIR=sn(),on(A.settingsManager.getTheme(),Y(),!1)}catch(e){T({level:"error",event:"broker.theme.initialization_failed",error:e})}try{for(const e of Qe(Y()))T({level:"warn",event:"broker.startup.warning",fields:{stage:"package_health",source:e.source,resolved_path:e.resolved}})}catch{}const K=He(t),g=new bn(t),X=new Map,O=new Tn(t,e=>g.broadcast(e)),j=new Rn(t,e=>g.broadcast(e));kn(e=>g.broadcast({type:"node_named",...e}));let N=!1,de=!1,H,le,m,W;const Z=new Mn;let F,ce=n(()=>{},"notifyTurnAccepted"),me=n(()=>{},"resetFrameState");const ee=xn();let ue=n(()=>0,"pendingPromptDispatches"),fe;const L=Dn({runNode:R,session:n(()=>m.session(),"session"),isReplacing:n(()=>m.isReplacing(),"isReplacing"),pendingRebind:n(()=>m.pendingRebind(),"pendingRebind"),isAgentRunActive:n(()=>m.isAgentRunActive(),"isAgentRunActive"),pendingDispatches:n(()=>ue(),"pendingDispatches"),turnAdmission:ee,prependDeferred:n(e=>On(t,e),"prependDeferred"),onError:n(e=>T({level:"error",event:"broker.inbox.idle_prompt_failed",error:e}),"onError")});Wn(()=>m.session());const pe=L.drain;f.firstPrompt!==void 0&&f.firstPrompt!==""&&L.holdForKickoff(),globalThis[An]=L.start,globalThis[Pn]=L.busy,m=new Un({nodeId:t,cfg:f,session:U,services:A,runtime:G,registry:g,toolGroups:O,turnReplies:j,transcriptMessages:n(e=>ie(e,E)?.map(({message:r})=>r)??e.sessionManager.buildSessionContext().messages,"transcriptMessages"),uiContext:n(()=>le,"uiContext"),faultRetry:n(()=>W,"faultRetry"),projection:n(()=>F,"projection"),setActiveSession:n(e=>{if(J=e,fe?.(),R){let r="stop",c,v=!1;fe=e.subscribe(h=>{if(h.type==="agent_start"&&(r="stop",c=void 0,v=!1,Z.started()),h.type==="agent_end"){const w=[...h.messages??[]].reverse().find($=>$.role==="assistant");w&&(r=w.stopReason,c=wn(w)?void 0:Sn(w),v=w.role==="assistant"&&Cn(w,e.model?.contextWindow))}h.type==="agent_settled"&&(Z.settled(e.sessionManager,{stop_reason:r,...c?{error:c}:{}},v),In(t,e).catch(w=>{T({level:"error",event:"broker.run_deferred_mail.settle_failed",error:w})}),queueMicrotask(()=>{m.session()===e&&pe()}))})}},"setActiveSession"),disposeAndExit:n((e,r)=>q(e,r),"disposeAndExit"),onSessionRebind:n(()=>{me()},"onSessionRebind"),onReplacementIdle:n(()=>{R&&pe()},"onReplacementIdle"),reWelcomeAll:n(()=>ne(),"reWelcomeAll")});const ge=n(async(e,r=!1)=>{const c=m.session(),v=B(c.model,c.thinkingLevel);if(v===null)return null;const h=await Yn(t,{spec:v,...e!==void 0?{intent:e}:{},userSelected:r});return r&&(f.model=h.modelOverride,e!==void 0?(process.env.CRTR_MODEL_INTENT=`${e.family}/${e.strength}`,delete process.env.CRTR_MODEL_EXACT,f.modelExact=void 0):(delete process.env.CRTR_MODEL_INTENT,process.env.CRTR_MODEL_EXACT="1",f.modelExact=!0)),h.modelOverride},"persistModelChoice"),ve=n(async(e=!0,r,c=!1)=>{const v=m.session(),h=B(v.model,v.thinkingLevel),w=e?await ge(r,c):h;g.broadcast({type:"model_changed",model:oe(v.model)?void 0:v.model,spec:w})},"broadcastModelChanged");W=new En({nodeId:t,cfg:f,retryProbe:D,currentSession:n(()=>m.session(),"currentSession"),currentServices:n(()=>m.services(),"currentServices"),installedGeneration:n(()=>m.installedGeneration(),"installedGeneration"),registryOf:se,formatModelSpec:B,broadcastModelChanged:ve,turnAdmission:ee,reWelcomeAll:n(()=>ne(),"reWelcomeAll"),...R?{turnEnds:Z}:{}});const he=n(()=>{W.rearmIfStranded().catch(e=>{T({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry re-arm failed: ${e instanceof Error?e.message:String(e)}`}})})},"rearmStrandedRetry");Q().subscribe("resync",he),Q().subscribe("fault-retry",he),F=new Nn({registry:g,faultRetry:W,installedGeneration:n(()=>m.installedGeneration(),"installedGeneration"),currentSession:n(()=>m.session(),"currentSession"),notifyTurnAccepted:n(()=>ce(),"notifyTurnAccepted"),emitStartupMilestone:d,formatModelSpec:B});const Ee=n(e=>{const r=m.session(),c=ie(r,E),v=Qn(e.snapshotTail),h=c===void 0||v===void 0?c:c.slice(-v),w=h?.map(({message:I})=>I)??r.sessionManager.buildSessionContext().messages,$=O.summaries(),_e=h!==void 0&&v!==void 0?rt(w):void 0,Ae=_e===void 0?$:Object.fromEntries(Object.entries($).filter(([I])=>_e.has(I)));return{messages:w,...h===void 0?{}:{messageIds:h.map(({id:I})=>I)},messageVisibility:h?.map(({visibility:I})=>I)??w.map(()=>"visible"),turnVisibility:yn(),stats:r.getSessionStats(),state:{model:oe(r.model)?void 0:r.model,thinkingLevel:r.thinkingLevel,isStreaming:r.isStreaming,isCompacting:r.isCompacting,steeringMode:r.steeringMode,followUpMode:r.followUpMode,sessionFile:r.sessionFile,sessionId:r.sessionId,sessionName:r.sessionName,autoCompactionEnabled:r.autoCompactionEnabled,messageCount:r.messages.length,pendingMessageCount:r.pendingMessageCount},display:{...g.displaySnapshot()},toolGroupSummaries:Ae,turnReplies:j.snapshot(),workingActivity:F.currentWorkingActivity(),queued:{steering:[...r.getSteeringMessages()],followUp:[...r.getFollowUpMessages()]}}},"buildSnapshot"),we=n(e=>{const r=e.role==="controller"?X.values().next().value:void 0;g.sendFrame(e,{type:"welcome",snapshot:Ee(e),role:e.role,pending_dialog:r!==void 0?r.request:null,agentDir:Y()},!0)},"sendWelcome"),Ce=n(e=>{const r=[...X.values()];for(let c=1;c<r.length;c++)g.sendFrame(e,r[c].request)},"replayExtraPendingDialogsTo"),ne=n(()=>{try{const e=m.session();j.seed(ie(e,E)?.map(({message:r})=>r)??e.sessionManager.buildSessionContext().messages)}catch(e){T({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`turn reply reseed failed: ${String(e)}`}})}g.forEachHelloed(we)},"reWelcomeAll"),Se=n(async e=>{if(!N){N=!0,Q().stop(),O.persistPendingSummary(),F.dispose(),nt();try{H?.close()}catch{}s.close(),g.dispose();try{ye(K)&&be(K)}catch{}g.clearAttachState(),await ze(),process.exit(e)}},"completeExit"),q=n((e,r=0)=>{if(N)return;const c=e==="shutdown-hook"||e==="shutdown-after-settle";if(c&&O.finishOpenToolGroup(!0),c&&(O.hasSummaryTasks()||j.hasTasks())){if(de)return;de=!0,Promise.allSettled([O.waitForSummaryTasks(),j.waitForTasks()]).finally(()=>Se(r));return}Se(r)},"disposeAndExit");le=at({writable:n(()=>g.writableClients(),"writable"),forward:n((e,r)=>g.sendFrame(e,r),"forward"),pending:X,broadcast:n(e=>g.broadcastUi(e),"broadcast"),onKnownStreamWait:n(e=>F.knownStreamWait(e),"onKnownStreamWait")}),m.setRebindSession(),await m.enqueueRebind(U,A),d("broker.startup.extensions_bound");const C=Fn({nodeId:t,registry:g,toolGroups:O,projection:F,rebind:m,boundaryReviewId:E,pendingDialogs:X,sendWelcome:we,replayExtraPendingDialogsTo:Ce,reWelcomeAll:ne,disposeAndExit:q,persistModelChoice:ge,broadcastModelChanged:ve,registryOf:se,formatModelSpec:B,formatExactModelSpec:Zn,turnAdmission:ee,memoryInventory:y,runNode:R,onKickoffAdmitted:L.releaseKickoff});ce=C.notifyTurnAccepted,me=C.resetForSession,ue=C.pendingPromptDispatches;const Me=2500;let te=!1;const xe=n(async e=>{if(N||te)return;if(te=!0,m.isAgentRunActive()||m.session().isStreaming||C.pendingPromptDispatches()>0){const c=Date.now()+Me;for(;C.pendingPromptDispatches()>0&&!m.session().isStreaming&&Date.now()<c;)await new Promise(v=>setTimeout(v,25));if(m.session().isStreaming){const v=c-Date.now();if(v>0){let h;try{Gn(),await Promise.race([m.session().abort().then(()=>!0,()=>!1),new Promise($=>{h=setTimeout(()=>$(!1),v)})])&&await Re(t,{event:"closed",end_reason:"teardown-aborted"})}finally{h!==void 0&&clearTimeout(h)}}}else(C.pendingPromptDispatches()>0||m.isAgentRunActive())&&await Re(t,{event:"interrupted"})}q(e)},"abortAndExit");if(b=n(e=>{xe(e)},"signalTeardown"),x!==void 0){b(x);return}try{ye(K)&&be(K)}catch{}H=Ue(e=>{const r=g.create(e);e.on("data",v=>{let h;try{h=r.decoder.push(v)}catch(w){w instanceof Jn?(g.sendFrame(r,{type:"error",code:"frame_overflow",message:w.message}),g.dropClient(r,`frame overflow: ${w.message}`)):g.dropClient(r,`decoder error: ${String(w)}`);return}for(const w of h)try{Ln(C,r,w)}catch{}});const c=n(()=>{g.removeClient(r)},"drop");e.on("close",c),e.on("error",()=>{})}),H.on("error",e=>{N||(T({level:"error",event:"broker.socket.failed",error:e}),q("socket-error",1))}),H.listen(K,async()=>{Promise.all([z(t,{op:"clear",link:"viewer\u2194broker"}),z(t,{op:"clear",link:"relay\u2194broker"})]).catch(r=>T({level:"error",event:"broker.daemon.operation.failed",node_id:t,error:r,fields:{operation:"broker_socket_bound_fault_clear"}}));let e;try{e=await W.recoverOnStartup()}catch(r){T({level:"error",event:"broker.daemon.operation.failed",node_id:t,error:r,fields:{operation:"recovery_startup"}}),q("recovery-startup-failed",1);return}if(!(N||te))if(e!=="retry-armed"&&f.firstPrompt!==void 0&&f.firstPrompt!==""){const r=qn(process.env[Vn]);r!==null&&Xn(f.firstPrompt,r);const c=process.env.CRTR_KICKOFF_MESSAGE_IDS;C.kickoff(f.firstPrompt,c?JSON.parse(c):[])}else L.releaseKickoff()}),g.persistAttachState()}n(mr,"runBroker");function rt(t){const o=new Set;for(const i of t)if(!(i.role!=="assistant"||!Array.isArray(i.content)))for(const u of i.content)u!==null&&typeof u=="object"&&u.type==="toolCall"&&typeof u.id=="string"&&o.add(u.id);return o}n(rt,"toolCallIds");function ie(t,o){const i=t.sessionManager,u=hn(i);return u!==void 0&&$n(u.map(({message:s})=>s)),u===void 0?void 0:_n(u,{boundaryReviewId:o,sessionEntries:i.getEntries?.()})}n(ie,"snapshotMessages");function ke(){const t=Ne();if(t===void 0||t==="")throw new Error("broker launch is missing CRTR_NODE_ID");return t}n(ke,"currentBrokerNodeId");async function ot(t,o){const i=cn(o.model,Ie(),o.modelExact===!0);if(i===void 0)return;const u=se(t),s={cwd:De()??o.cwd,profileId:Fe()},a=fn(i,u,s);if(a!==void 0)throw await z(ke(),{op:"record",input:{link:"crtr\u2192pi",op:"model resolution at launch",kind:"model-not-found",retry:{disposition:"fatal"},message:`model '${a.request.spec}' is not registered in the serving model registry`}}),new Error(`[broker] FATAL: model '${a.request.spec}' is not registered in the serving model registry`);const l=mn(i,s.cwd,s.profileId),d=un(i.spec);for(const p of l){const S=u.find(p.providerId,p.modelId);if(S===void 0)continue;const _=d?.providerId===p.providerId&&d.modelId===p.modelId?d.thinkingLevel??(p.thinkingLevel===void 0?void 0:"off"):p.thinkingLevel;return{model:S,thinkingLevel:_}}if(o.modelExact===!0){const p=l.map(S=>`'${S.providerId}/${S.modelId}' unregistered`).join("; ");throw await z(ke(),{op:"record",input:{link:"crtr\u2192pi",op:"model resolution at launch",kind:"model-not-found",retry:{disposition:"fatal"},message:`exact-consult model '${o.model}' is unavailable \u2014 refusing to substitute (${p})`}}),new Error(`[broker] FATAL: exact-consult model '${o.model}' is unavailable`)}T({level:"warn",event:"broker.model.none_viable",fields:{rejected_candidates:l.map(p=>({provider:p.providerId,model:p.modelId,reason:"unregistered"})).slice(0,16)}})}n(ot,"resolveLaunchModel");function st(t){let o=!0;return{runtime:new Proxy(t,{get(u,s,a){return o&&s==="getAvailableSnapshot"?()=>[]:o&&s==="getAvailable"?async()=>[]:o&&s==="getModel"?()=>{}:Reflect.get(u,s,a)}}),reveal:n(()=>{o=!1},"reveal")}}n(st,"modelLessRegistry");async function it(t,o){const i=Y(),u=n(async _=>{const f=await Ve.create({credentials:new dn,modelsPath:Te(_.agentDir,"models.json")});ln(f);const y=await t.createAgentSessionServices({cwd:_.cwd,agentDir:_.agentDir,modelRuntime:f,resourceLoaderOptions:{additionalExtensionPaths:o.extensionPaths,appendSystemPrompt:o.appendSystemPromptPath!==void 0?[o.appendSystemPromptPath]:void 0,noContextFiles:!0,extensionsOverride:n(P=>({...P,extensions:P.extensions.filter(E=>!/[/\\]provider-rotation\.(?:ts|js)$/.test(E.path))}),"extensionsOverride")}}),b=y.resourceLoader.getExtensions();if(o.extensionPaths.some(P=>re.includes(P))){const P=new Set(b.extensions.map(R=>R.path)),E=re.filter(R=>!P.has(R));if(E.length>0){const R=b.errors.filter(D=>re.includes(D.path)).map(D=>`${D.path}: ${D.error}`).join("; ");throw new Error(`[broker] FATAL: mandatory canvas extensions failed to load: ${E.join(", ")}${R===""?"":` (${R})`}`)}}y.settingsManager.getRetryEnabled=()=>!1;const x=await ot(y,o),V=x?.model,U=x?.thinkingLevel,A=x===void 0?st(y.modelRuntime):void 0,ae=A!==void 0?{...y,modelRuntime:A.runtime}:y,G=await t.createAgentSessionFromServices({services:ae,sessionManager:_.sessionManager,model:V,thinkingLevel:U,...tt(o.tools,Le()),sessionStartEvent:_.sessionStartEvent});return Bn(G.session),an(G.session),A?.reveal(),{...G,services:y}},"buildForManager"),s=o.resumeSessionPath,a=o.forkFrom!==void 0&&o.forkFrom!=="";if(!a&&(s===void 0||s==="")&&o.resumeSessionId!==void 0&&o.resumeSessionId!=="")throw new Error(`[broker] resume requires a session .jsonl PATH; got only a bare id '${o.resumeSessionId}' (pi_session_file was never captured for this node)`);const l=!a&&s!==void 0&&s!=="",d=l&&o.newCycle===!0,p=l&&!d;let S;if(a)S=await t.SessionManager.forkFrom(o.forkFrom,o.cwd);else if(p||d){if(S=await t.SessionManager.open(s),d){const _=S.getLeafId();S.resetLeaf(),S.appendCustomEntry(vn,{cycle:Number(process.env.CRTR_CYCLES),fromLeaf:_})}}else S=await t.SessionManager.create(o.cwd);if(t.createAgentSessionRuntime!==void 0){const _=n(async y=>{const b=await u({cwd:y.cwd,agentDir:y.agentDir,sessionManager:y.sessionManager,sessionStartEvent:y.sessionStartEvent});return{...b,diagnostics:b.services.diagnostics??[]}},"factory"),f=await t.createAgentSessionRuntime(_,{cwd:o.cwd,agentDir:i,sessionManager:S});return{session:f.session,services:f.services,resuming:p,runtime:f}}const k=await u({cwd:o.cwd,agentDir:i,sessionManager:S});return{session:k.session,services:k.services,resuming:p,runtime:void 0}}n(it,"buildBrokerSession");function at(t){const o=n((s,a,l,d)=>{if(d?.signal?.aborted)return Promise.resolve(s);const p=t.writable();return p.length===0?Promise.resolve(s):new Promise(S=>{let k;const _=n(()=>{k!==void 0&&clearTimeout(k),d?.signal?.removeEventListener("abort",f),t.pending.delete(a.id)},"cleanup"),f=n(()=>{_(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),S(s)},"onAbort");d?.signal?.addEventListener("abort",f,{once:!0});const y=d?.timeout!==void 0?d.timeout:et;k=setTimeout(()=>{_(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),S(s)},y),typeof k.unref=="function"&&k.unref(),t.pending.set(a.id,{request:a,resolve:n(b=>{_(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),S(l(b))},"resolve")});for(const b of p)t.forward(b,a)})},"dialogPromise"),i=n(()=>{},"noop");return{[pn]:t.onKnownStreamWait??(s=>()=>{}),get theme(){const s=globalThis[Symbol.for("@earendil-works/pi-coding-agent:theme")];if(s==null)throw new Error("Theme not initialized. Call initTheme() first.");return s},select:n((s,a,l)=>o(void 0,{type:"extension_ui_request",id:M(),method:"select",title:s,options:a,timeout:l?.timeout},d=>"cancelled"in d&&d.cancelled?void 0:"value"in d?d.value:void 0,l),"select"),confirm:n((s,a,l)=>o(!1,{type:"extension_ui_request",id:M(),method:"confirm",title:s,message:a,timeout:l?.timeout},d=>"cancelled"in d&&d.cancelled?!1:"confirmed"in d?d.confirmed:!1,l),"confirm"),input:n((s,a,l)=>o(void 0,{type:"extension_ui_request",id:M(),method:"input",title:s,placeholder:a,timeout:l?.timeout},d=>"cancelled"in d&&d.cancelled?void 0:"value"in d?d.value:void 0,l),"input"),editor:n((s,a)=>o(void 0,{type:"extension_ui_request",id:M(),method:"editor",title:s,prefill:a},l=>"cancelled"in l&&l.cancelled?void 0:"value"in l?l.value:void 0),"editor"),setStatus:n((s,a)=>t.broadcast({type:"extension_ui_request",id:M(),method:"setStatus",statusKey:s,statusText:a}),"setStatus"),setWidget:n((s,a,l)=>{typeof a!="function"&&t.broadcast({type:"extension_ui_request",id:M(),method:"setWidget",widgetKey:s,widgetLines:a,widgetPlacement:l?.placement})},"setWidget"),setTitle:n(s=>t.broadcast({type:"extension_ui_request",id:M(),method:"setTitle",title:s}),"setTitle"),notify:n((s,a)=>{const l=a==="warn"?"warning":a;t.broadcast({type:"extension_ui_request",id:M(),method:"notify",message:s,notifyType:l})},"notify"),onTerminalInput:n(()=>i,"onTerminalInput"),setWorkingMessage:i,setWorkingVisible:i,setWorkingIndicator:i,setHiddenThinkingLabel:i,setFooter:i,setHeader:i,custom:n(()=>Promise.resolve(void 0),"custom"),pasteToEditor:i,setEditorText:i,getEditorText:n(()=>"","getEditorText"),addAutocompleteProvider:i,setEditorComponent:i,getEditorComponent:n(()=>{},"getEditorComponent"),getAllThemes:n(()=>[],"getAllThemes"),getTheme:n(()=>{},"getTheme"),setTheme:i,getToolsExpanded:n(()=>!1,"getToolsExpanded"),setToolsExpanded:i}}n(at,"makeBrokerUiContext");export{gr as REVIEW_BOUNDARY_NAVIGATION_CODE,pr as ReviewBoundaryNavigationError,tt as brokerToolOptions,it as buildBrokerSession,nt as disposeActiveSession,at as makeBrokerUiContext,mr as runBroker,ie as snapshotMessages};
|
|
1
|
+
var Pe=Object.defineProperty;var n=(t,r)=>Pe(t,"name",{value:r,configurable:!0});import{envInboxBetweenTurns as Oe,envModelIntent as Ie,envNodeCwd as De,envNodeId as Ne,envProfileId as Fe,envScopes as Le,isSandboxedNode as $e,scopeAllowed as Be}from"../../shared/env.js";import{createServer as Ue}from"node:net";import{existsSync as be,readFileSync as Ge,unlinkSync as ye}from"node:fs";import{join as Te}from"node:path";import{randomUUID as M}from"node:crypto";import{mark as Ke}from"../timing.js";import{SUBMIT_RESULT_TOOL as je}from"./structured-output.js";import{installSessionManagerStorePatch as We}from"../conversation-store/index.js";import{getAgentDir as Y,ModelRegistry as qe,ModelRuntime as Ve}from"@earendil-works/pi-coding-agent";import{nodeRunDir as Xe,viewSocketPath as He}from"../canvas/paths.js";import{bindBrokerEventSource as Ye}from"../events/source.js";import{emitEvent as T,flushBrokerLog as ze}from"../events/emit.js";import{operationIdContext as Je}from"../events/operation-id.js";import{findMissingPackagePaths as Qe}from"./package-health.js";import{installBrokerConfigSnapshot as Ze}from"../config.js";import{FRONT_DOOR_ENV as en}from"./front-door.js";import{CANVAS_EXTENSIONS as oe,piInvocationToSdkConfig as nn}from"./launch.js";import{assertEngineVersion as tn,loadBrokerEngine as on}from"./broker-sdk.js";import{initCrtrTheme as rn}from"./crtr-theme.js";import{resolveBundledPiPackageDir as sn}from"./pi-cli.js";import{captureSessionChatCapabilities as an}from"./command-surface.js";import{InMemoryCredentialStore as dn}from"@earendil-works/pi-ai";import{configureBrokerModelRuntime as ln}from"./broker/model-adapter.js";import{mutateBrokerFault as z,recordBrokerTurn as Re}from"./broker/daemon-ops.js";import{modelRequestFromConfig as cn,expandModelCandidates as mn,parseModelSpec as un,unregisteredConcreteModel as fn}from"../model-routes.js";import{KNOWN_STREAM_WAIT_UI as pn}from"./stream-watchdog.js";import{probeOnline as gn}from"./connectivity.js";import{CRTR_CYCLE_CUSTOM_TYPE as vn,cycleAwareMessages as hn,endedByAbort as wn}from"./session-cycles.js";import{turnErrorOf as Sn}from"../llm/refusal.js";import{projectTranscript as _n}from"./session-visibility.js";import{currentTurnVisibility as bn}from"./turn-visibility.js";import{isUnknownModel as re}from"./model-selection.js";import{BrokerClientRegistry as yn}from"./broker/client-registry.js";import{ToolGroupTracker as Tn}from"./broker/tool-groups.js";import{TurnReplyTracker as Rn}from"./broker/turn-replies.js";import{onNodeNamed as kn}from"./broker/node-named.js";import{FaultRetry as En,willDaemonRetry as Cn}from"./broker/fault-retry.js";import{RunTurnEnds as Mn}from"./broker/run-turn-end.js";import{createTurnAdmissionGate as xn}from"./broker/turn-admission.js";import{BROKER_IDLE_TURN_START as An,BROKER_RUN_TURN_BUSY as Pn}from"./broker/held-deferred-inbox.js";import{prependRunDeferredMail as On,settleRunDeferredMail as In}from"./broker/run-deferred-mail.js";import{createRunTurnQueue as Dn}from"./broker/run-turn-queue.js";import{EventProjection as Nn}from"./broker/event-projection.js";import{createFrameDispatchContext as Fn,handleFrame as Ln}from"./broker/frame-dispatch.js";import{hydratePersistedAdvertisedCommandMessages as $n,installAdvertisedCommandInvocationContract as Bn}from"./advertised-command-invocation.js";import{RebindDriver as Un}from"./broker/rebind.js";import{beginRuntimeTeardown as Gn}from"./broker/extension-abort.js";import{receiveNodeToken as Kn}from"./broker/node-token.js";import{serveCliRelay as jn}from"./broker/cli-relay.js";import{setTurnCauseSource as Wn}from"./broker/turn-cause.js";import{decodeKickoffOrigin as qn,KICKOFF_ORIGIN_ENV as Vn}from"./stamp/protocol.js";import{announceMessageOrigin as Xn}from"./stamp/channel.js";import{brokerExtensionState as Hn,commitBrokerModel as Yn}from"./broker/daemon-ops.js";import{composerNames as zn}from"./broker/frame-memory-refs.js";import{FrameOverflowError as Jn,normalizeSnapshotTail as Qn}from"./broker-protocol.js";import{ReviewBoundaryNavigationError as go,REVIEW_BOUNDARY_NAVIGATION_CODE as vo}from"./broker/frame-replies.js";function se(t){return new qe(t.modelRuntime)}n(se,"registryOf");function B(t,r){if(t==null||re(t))return null;const i=r!==void 0&&r!==""&&r!=="off"?`:${r}`:"";return`${t.provider}/${t.id}${i}`}n(B,"formatModelSpec");function Zn(t,r){const i=B(t,void 0);return i===null?null:`${i}:${r===void 0||r===""?"off":r}`}n(Zn,"formatExactModelSpec");const et=12e4;let J=null;import{brokerSignalClient as Q}from"./broker/signal-stream.js";function nt(){Q().stop();const t=J;if(J=null,t!==null)try{t.dispose()}catch{}}n(nt,"disposeActiveSession");function tt(t,r){const i=Be(r,"crtr:act")?void 0:["bash"],u=t!==void 0&&t.length>0?[...new Set([...t,je])]:t===void 0?void 0:[...t];return{...u!==void 0?{tools:u}:{},...i!==void 0?{excludeTools:i}:{}}}n(tt,"brokerToolOptions");async function uo(t,r=process.hrtime.bigint(),i=new Date().toISOString()){Ye(t);const u=await Kn(),s=await jn(t,u);process.env.CRTR_API_SOCKET=s.path,Je.fresh(()=>{T({level:"info",event:"broker.startup.cli_entry",ts:i,duration_ms:0,fields:{runtime_version:process.version,engine_version:null,model:null}}),T({level:"info",event:"broker.startup.module_loaded",duration_ms:Number(process.hrtime.bigint()-r)/1e6,fields:{runtime_version:process.version,engine_version:null,model:null}})});const a=new Set;let l=null;const d=n((e,o=null)=>{a.has(e)||(a.add(e),Ke(e,{duration_ms:Number(process.hrtime.bigint()-r)/1e6}),T({level:"info",event:e,duration_ms:Number(process.hrtime.bigint()-r)/1e6,fields:{runtime_version:process.version,engine_version:l,model:o}}))},"emitStartupMilestone");process.env[en]="1",process.env.CRTR_BROKER_PID=String(process.pid),delete process.env.TMUX_PANE,delete process.env.TMUX,We(t);const p=await on();tn(p.VERSION),l=p.VERSION;const S=Xe(t),k=Te(S,"broker-launch.json"),_=JSON.parse(Ge(k,"utf8"));if($e()){if(!_.configSnapshot)throw new Error("broker launch is missing its config snapshot");Ze(_.configSnapshot)}const f=nn(_);f.modelExact===!0?process.env.CRTR_MODEL_EXACT="1":delete process.env.CRTR_MODEL_EXACT;const b=await zn(t);let y,x;const V=n(e=>{y!==void 0?y(e):x??=e},"onSignal");process.on("SIGTERM",()=>V("SIGTERM")),process.on("SIGINT",()=>V("SIGINT"));let{session:U,services:A,resuming:ae,runtime:G}=await it(p,f);d("broker.startup.session_built");const E=(await Hn(t)).node.review_binding?.review_id,R=Oe();J=U;const D=gn;try{process.env.PI_PACKAGE_DIR=sn(),rn(A.settingsManager.getTheme(),Y(),!1)}catch(e){T({level:"error",event:"broker.theme.initialization_failed",error:e})}try{for(const e of Qe(Y()))T({level:"warn",event:"broker.startup.warning",fields:{stage:"package_health",source:e.source,resolved_path:e.resolved}})}catch{}const K=He(t),g=new yn(t),X=new Map,O=new Tn(t,e=>g.broadcast(e)),j=new Rn(t,e=>g.broadcast(e));kn(e=>g.broadcast({type:"node_named",...e}));let N=!1,de=!1,H,le,m,W;const Z=new Mn;let F,ce=n(()=>{},"notifyTurnAccepted"),me=n(()=>{},"resetFrameState");const ee=xn();let ue=n(()=>0,"pendingPromptDispatches"),fe;const L=Dn({runNode:R,session:n(()=>m.session(),"session"),isReplacing:n(()=>m.isReplacing(),"isReplacing"),pendingRebind:n(()=>m.pendingRebind(),"pendingRebind"),isAgentRunActive:n(()=>m.isAgentRunActive(),"isAgentRunActive"),pendingDispatches:n(()=>ue(),"pendingDispatches"),turnAdmission:ee,prependDeferred:n(e=>On(t,e),"prependDeferred"),onError:n(e=>T({level:"error",event:"broker.inbox.idle_prompt_failed",error:e}),"onError")});Wn(()=>m.session());const pe=L.drain;f.firstPrompt!==void 0&&f.firstPrompt!==""&&L.holdForKickoff(),globalThis[An]=L.start,globalThis[Pn]=L.busy,m=new Un({nodeId:t,cfg:f,session:U,services:A,runtime:G,registry:g,toolGroups:O,turnReplies:j,transcriptMessages:n(e=>ie(e,E)?.map(({message:o})=>o)??e.sessionManager.buildSessionContext().messages,"transcriptMessages"),uiContext:n(()=>le,"uiContext"),faultRetry:n(()=>W,"faultRetry"),projection:n(()=>F,"projection"),setActiveSession:n(e=>{if(J=e,fe?.(),R){let o="stop",c,v=!1;fe=e.subscribe(h=>{if(h.type==="agent_start"&&(o="stop",c=void 0,v=!1,Z.started()),h.type==="agent_end"){const w=[...h.messages??[]].reverse().find($=>$.role==="assistant");w&&(o=w.stopReason,c=wn(w)?void 0:Sn(w),v=w.role==="assistant"&&Cn(w,e.model?.contextWindow))}h.type==="agent_settled"&&(Z.settled(e.sessionManager,{stop_reason:o,...c?{error:c}:{}},v),In(t,e).catch(w=>{T({level:"error",event:"broker.run_deferred_mail.settle_failed",error:w})}),queueMicrotask(()=>{m.session()===e&&pe()}))})}},"setActiveSession"),disposeAndExit:n((e,o)=>q(e,o),"disposeAndExit"),onSessionRebind:n(()=>{me()},"onSessionRebind"),onReplacementIdle:n(()=>{R&&pe()},"onReplacementIdle"),reWelcomeAll:n(()=>ne(),"reWelcomeAll")});const ge=n(async(e,o=!1)=>{const c=m.session(),v=B(c.model,c.thinkingLevel);if(v===null)return null;const h=await Yn(t,{spec:v,...e!==void 0?{intent:e}:{},userSelected:o});return o&&(f.model=h.modelOverride,e!==void 0?(process.env.CRTR_MODEL_INTENT=`${e.family}/${e.strength}`,delete process.env.CRTR_MODEL_EXACT,f.modelExact=void 0):(delete process.env.CRTR_MODEL_INTENT,process.env.CRTR_MODEL_EXACT="1",f.modelExact=!0)),h.modelOverride},"persistModelChoice"),ve=n(async(e=!0,o,c=!1)=>{const v=m.session(),h=B(v.model,v.thinkingLevel),w=e?await ge(o,c):h;g.broadcast({type:"model_changed",model:re(v.model)?void 0:v.model,spec:w})},"broadcastModelChanged");W=new En({nodeId:t,cfg:f,retryProbe:D,currentSession:n(()=>m.session(),"currentSession"),currentServices:n(()=>m.services(),"currentServices"),installedGeneration:n(()=>m.installedGeneration(),"installedGeneration"),registryOf:se,formatModelSpec:B,broadcastModelChanged:ve,turnAdmission:ee,reWelcomeAll:n(()=>ne(),"reWelcomeAll"),...R?{turnEnds:Z}:{}});const he=n(()=>{W.rearmIfStranded().catch(e=>{T({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry re-arm failed: ${e instanceof Error?e.message:String(e)}`}})})},"rearmStrandedRetry");Q().subscribe("resync",he),Q().subscribe("fault-retry",he),F=new Nn({registry:g,faultRetry:W,installedGeneration:n(()=>m.installedGeneration(),"installedGeneration"),currentSession:n(()=>m.session(),"currentSession"),notifyTurnAccepted:n(()=>ce(),"notifyTurnAccepted"),emitStartupMilestone:d,formatModelSpec:B});const Ee=n(e=>{const o=m.session(),c=ie(o,E),v=Qn(e.snapshotTail),h=c===void 0||v===void 0?c:c.slice(-v),w=h?.map(({message:I})=>I)??o.sessionManager.buildSessionContext().messages,$=O.summaries(),_e=h!==void 0&&v!==void 0?ot(w):void 0,Ae=_e===void 0?$:Object.fromEntries(Object.entries($).filter(([I])=>_e.has(I)));return{messages:w,...h===void 0?{}:{messageIds:h.map(({id:I})=>I)},messageVisibility:h?.map(({visibility:I})=>I)??w.map(()=>"visible"),turnVisibility:bn(),stats:o.getSessionStats(),state:{model:re(o.model)?void 0:o.model,thinkingLevel:o.thinkingLevel,isStreaming:o.isStreaming,isCompacting:o.isCompacting,steeringMode:o.steeringMode,followUpMode:o.followUpMode,sessionFile:o.sessionFile,sessionId:o.sessionId,sessionName:o.sessionName,autoCompactionEnabled:o.autoCompactionEnabled,messageCount:o.messages.length,pendingMessageCount:o.pendingMessageCount},display:{...g.displaySnapshot()},toolGroupSummaries:Ae,turnReplies:j.snapshot(),workingActivity:F.currentWorkingActivity(),queued:{steering:[...o.getSteeringMessages()],followUp:[...o.getFollowUpMessages()]}}},"buildSnapshot"),we=n(e=>{const o=e.role==="controller"?X.values().next().value:void 0;g.sendFrame(e,{type:"welcome",snapshot:Ee(e),role:e.role,pending_dialog:o!==void 0?o.request:null,agentDir:Y()},!0)},"sendWelcome"),Ce=n(e=>{const o=[...X.values()];for(let c=1;c<o.length;c++)g.sendFrame(e,o[c].request)},"replayExtraPendingDialogsTo"),ne=n(()=>{try{const e=m.session();j.seed(ie(e,E)?.map(({message:o})=>o)??e.sessionManager.buildSessionContext().messages)}catch(e){T({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`turn reply reseed failed: ${String(e)}`}})}g.forEachHelloed(we)},"reWelcomeAll"),Se=n(async e=>{if(!N){N=!0,Q().stop(),O.persistPendingSummary(),F.dispose(),nt();try{H?.close()}catch{}s.close(),g.dispose();try{be(K)&&ye(K)}catch{}g.clearAttachState(),await ze(),process.exit(e)}},"completeExit"),q=n((e,o=0)=>{if(N)return;const c=e==="shutdown-hook"||e==="shutdown-after-settle";if(c&&O.finishOpenToolGroup(!0),c&&(O.hasSummaryTasks()||j.hasTasks())){if(de)return;de=!0,Promise.allSettled([O.waitForSummaryTasks(),j.waitForTasks()]).finally(()=>Se(o));return}Se(o)},"disposeAndExit");le=at({writable:n(()=>g.writableClients(),"writable"),forward:n((e,o)=>g.sendFrame(e,o),"forward"),pending:X,broadcast:n(e=>g.broadcastUi(e),"broadcast"),onKnownStreamWait:n(e=>F.knownStreamWait(e),"onKnownStreamWait")}),m.setRebindSession(),await m.enqueueRebind(U,A),d("broker.startup.extensions_bound");const C=Fn({nodeId:t,registry:g,toolGroups:O,projection:F,rebind:m,boundaryReviewId:E,pendingDialogs:X,sendWelcome:we,replayExtraPendingDialogsTo:Ce,reWelcomeAll:ne,disposeAndExit:q,persistModelChoice:ge,broadcastModelChanged:ve,registryOf:se,formatModelSpec:B,formatExactModelSpec:Zn,turnAdmission:ee,memoryInventory:b,runNode:R,onKickoffAdmitted:L.releaseKickoff});ce=C.notifyTurnAccepted,me=C.resetForSession,ue=C.pendingPromptDispatches;const Me=2500;let te=!1;const xe=n(async e=>{if(N||te)return;if(te=!0,m.isAgentRunActive()||m.session().isStreaming||C.pendingPromptDispatches()>0){const c=Date.now()+Me;for(;C.pendingPromptDispatches()>0&&!m.session().isStreaming&&Date.now()<c;)await new Promise(v=>setTimeout(v,25));if(m.session().isStreaming){const v=c-Date.now();if(v>0){let h;try{Gn(),await Promise.race([m.session().abort().then(()=>!0,()=>!1),new Promise($=>{h=setTimeout(()=>$(!1),v)})])&&await Re(t,{event:"closed",end_reason:"teardown-aborted"})}finally{h!==void 0&&clearTimeout(h)}}}else(C.pendingPromptDispatches()>0||m.isAgentRunActive())&&await Re(t,{event:"interrupted"})}q(e)},"abortAndExit");if(y=n(e=>{xe(e)},"signalTeardown"),x!==void 0){y(x);return}try{be(K)&&ye(K)}catch{}H=Ue(e=>{const o=g.create(e);e.on("data",v=>{let h;try{h=o.decoder.push(v)}catch(w){w instanceof Jn?(g.sendFrame(o,{type:"error",code:"frame_overflow",message:w.message}),g.dropClient(o,`frame overflow: ${w.message}`)):g.dropClient(o,`decoder error: ${String(w)}`);return}for(const w of h)try{Ln(C,o,w)}catch{}});const c=n(()=>{g.removeClient(o)},"drop");e.on("close",c),e.on("error",()=>{})}),H.on("error",e=>{N||(T({level:"error",event:"broker.socket.failed",error:e}),q("socket-error",1))}),H.listen(K,async()=>{Promise.all([z(t,{op:"clear",link:"viewer\u2194broker"}),z(t,{op:"clear",link:"relay\u2194broker"})]).catch(o=>T({level:"error",event:"broker.daemon.operation.failed",node_id:t,error:o,fields:{operation:"broker_socket_bound_fault_clear"}}));let e;try{e=await W.recoverOnStartup()}catch(o){T({level:"error",event:"broker.daemon.operation.failed",node_id:t,error:o,fields:{operation:"recovery_startup"}}),q("recovery-startup-failed",1);return}if(!(N||te))if(e==="none"&&f.firstPrompt!==void 0&&f.firstPrompt!==""){const o=qn(process.env[Vn]);o!==null&&Xn(f.firstPrompt,o);const c=process.env.CRTR_KICKOFF_MESSAGE_IDS;C.kickoff(f.firstPrompt,c?JSON.parse(c):[])}else L.releaseKickoff()}),g.persistAttachState()}n(uo,"runBroker");function ot(t){const r=new Set;for(const i of t)if(!(i.role!=="assistant"||!Array.isArray(i.content)))for(const u of i.content)u!==null&&typeof u=="object"&&u.type==="toolCall"&&typeof u.id=="string"&&r.add(u.id);return r}n(ot,"toolCallIds");function ie(t,r){const i=t.sessionManager,u=hn(i);return u!==void 0&&$n(u.map(({message:s})=>s)),u===void 0?void 0:_n(u,{boundaryReviewId:r,sessionEntries:i.getEntries?.()})}n(ie,"snapshotMessages");function ke(){const t=Ne();if(t===void 0||t==="")throw new Error("broker launch is missing CRTR_NODE_ID");return t}n(ke,"currentBrokerNodeId");async function rt(t,r){const i=cn(r.model,Ie(),r.modelExact===!0);if(i===void 0)return;const u=se(t),s={cwd:De()??r.cwd,profileId:Fe()},a=fn(i,u,s);if(a!==void 0)throw await z(ke(),{op:"record",input:{link:"crtr\u2192pi",op:"model resolution at launch",kind:"model-not-found",retry:{disposition:"fatal"},message:`model '${a.request.spec}' is not registered in the serving model registry`}}),new Error(`[broker] FATAL: model '${a.request.spec}' is not registered in the serving model registry`);const l=mn(i,s.cwd,s.profileId),d=un(i.spec);for(const p of l){const S=u.find(p.providerId,p.modelId);if(S===void 0)continue;const _=d?.providerId===p.providerId&&d.modelId===p.modelId?d.thinkingLevel??(p.thinkingLevel===void 0?void 0:"off"):p.thinkingLevel;return{model:S,thinkingLevel:_}}if(r.modelExact===!0){const p=l.map(S=>`'${S.providerId}/${S.modelId}' unregistered`).join("; ");throw await z(ke(),{op:"record",input:{link:"crtr\u2192pi",op:"model resolution at launch",kind:"model-not-found",retry:{disposition:"fatal"},message:`exact-consult model '${r.model}' is unavailable \u2014 refusing to substitute (${p})`}}),new Error(`[broker] FATAL: exact-consult model '${r.model}' is unavailable`)}T({level:"warn",event:"broker.model.none_viable",fields:{rejected_candidates:l.map(p=>({provider:p.providerId,model:p.modelId,reason:"unregistered"})).slice(0,16)}})}n(rt,"resolveLaunchModel");function st(t){let r=!0;return{runtime:new Proxy(t,{get(u,s,a){return r&&s==="getAvailableSnapshot"?()=>[]:r&&s==="getAvailable"?async()=>[]:r&&s==="getModel"?()=>{}:Reflect.get(u,s,a)}}),reveal:n(()=>{r=!1},"reveal")}}n(st,"modelLessRegistry");async function it(t,r){const i=Y(),u=n(async _=>{const f=await Ve.create({credentials:new dn,modelsPath:Te(_.agentDir,"models.json")});ln(f);const b=await t.createAgentSessionServices({cwd:_.cwd,agentDir:_.agentDir,modelRuntime:f,resourceLoaderOptions:{additionalExtensionPaths:r.extensionPaths,appendSystemPrompt:r.appendSystemPromptPath!==void 0?[r.appendSystemPromptPath]:void 0,noContextFiles:!0,extensionsOverride:n(P=>({...P,extensions:P.extensions.filter(E=>!/[/\\]provider-rotation\.(?:ts|js)$/.test(E.path))}),"extensionsOverride")}}),y=b.resourceLoader.getExtensions();if(r.extensionPaths.some(P=>oe.includes(P))){const P=new Set(y.extensions.map(R=>R.path)),E=oe.filter(R=>!P.has(R));if(E.length>0){const R=y.errors.filter(D=>oe.includes(D.path)).map(D=>`${D.path}: ${D.error}`).join("; ");throw new Error(`[broker] FATAL: mandatory canvas extensions failed to load: ${E.join(", ")}${R===""?"":` (${R})`}`)}}b.settingsManager.getRetryEnabled=()=>!1;const x=await rt(b,r),V=x?.model,U=x?.thinkingLevel,A=x===void 0?st(b.modelRuntime):void 0,ae=A!==void 0?{...b,modelRuntime:A.runtime}:b,G=await t.createAgentSessionFromServices({services:ae,sessionManager:_.sessionManager,model:V,thinkingLevel:U,...tt(r.tools,Le()),sessionStartEvent:_.sessionStartEvent});return Bn(G.session),an(G.session),A?.reveal(),{...G,services:b}},"buildForManager"),s=r.resumeSessionPath,a=r.forkFrom!==void 0&&r.forkFrom!=="";if(!a&&(s===void 0||s==="")&&r.resumeSessionId!==void 0&&r.resumeSessionId!=="")throw new Error(`[broker] resume requires a session .jsonl PATH; got only a bare id '${r.resumeSessionId}' (pi_session_file was never captured for this node)`);const l=!a&&s!==void 0&&s!=="",d=l&&r.newCycle===!0,p=l&&!d;let S;if(a)S=await t.SessionManager.forkFrom(r.forkFrom,r.cwd);else if(p||d){if(S=await t.SessionManager.open(s),d){const _=S.getLeafId();S.resetLeaf(),S.appendCustomEntry(vn,{cycle:Number(process.env.CRTR_CYCLES),fromLeaf:_})}}else S=await t.SessionManager.create(r.cwd);if(t.createAgentSessionRuntime!==void 0){const _=n(async b=>{const y=await u({cwd:b.cwd,agentDir:b.agentDir,sessionManager:b.sessionManager,sessionStartEvent:b.sessionStartEvent});return{...y,diagnostics:y.services.diagnostics??[]}},"factory"),f=await t.createAgentSessionRuntime(_,{cwd:r.cwd,agentDir:i,sessionManager:S});return{session:f.session,services:f.services,resuming:p,runtime:f}}const k=await u({cwd:r.cwd,agentDir:i,sessionManager:S});return{session:k.session,services:k.services,resuming:p,runtime:void 0}}n(it,"buildBrokerSession");function at(t){const r=n((s,a,l,d)=>{if(d?.signal?.aborted)return Promise.resolve(s);const p=t.writable();return p.length===0?Promise.resolve(s):new Promise(S=>{let k;const _=n(()=>{k!==void 0&&clearTimeout(k),d?.signal?.removeEventListener("abort",f),t.pending.delete(a.id)},"cleanup"),f=n(()=>{_(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),S(s)},"onAbort");d?.signal?.addEventListener("abort",f,{once:!0});const b=d?.timeout!==void 0?d.timeout:et;k=setTimeout(()=>{_(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),S(s)},b),typeof k.unref=="function"&&k.unref(),t.pending.set(a.id,{request:a,resolve:n(y=>{_(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),S(l(y))},"resolve")});for(const y of p)t.forward(y,a)})},"dialogPromise"),i=n(()=>{},"noop");return{[pn]:t.onKnownStreamWait??(s=>()=>{}),get theme(){const s=globalThis[Symbol.for("@earendil-works/pi-coding-agent:theme")];if(s==null)throw new Error("Theme not initialized. Call initTheme() first.");return s},select:n((s,a,l)=>r(void 0,{type:"extension_ui_request",id:M(),method:"select",title:s,options:a,timeout:l?.timeout},d=>"cancelled"in d&&d.cancelled?void 0:"value"in d?d.value:void 0,l),"select"),confirm:n((s,a,l)=>r(!1,{type:"extension_ui_request",id:M(),method:"confirm",title:s,message:a,timeout:l?.timeout},d=>"cancelled"in d&&d.cancelled?!1:"confirmed"in d?d.confirmed:!1,l),"confirm"),input:n((s,a,l)=>r(void 0,{type:"extension_ui_request",id:M(),method:"input",title:s,placeholder:a,timeout:l?.timeout},d=>"cancelled"in d&&d.cancelled?void 0:"value"in d?d.value:void 0,l),"input"),editor:n((s,a)=>r(void 0,{type:"extension_ui_request",id:M(),method:"editor",title:s,prefill:a},l=>"cancelled"in l&&l.cancelled?void 0:"value"in l?l.value:void 0),"editor"),setStatus:n((s,a)=>t.broadcast({type:"extension_ui_request",id:M(),method:"setStatus",statusKey:s,statusText:a}),"setStatus"),setWidget:n((s,a,l)=>{typeof a!="function"&&t.broadcast({type:"extension_ui_request",id:M(),method:"setWidget",widgetKey:s,widgetLines:a,widgetPlacement:l?.placement})},"setWidget"),setTitle:n(s=>t.broadcast({type:"extension_ui_request",id:M(),method:"setTitle",title:s}),"setTitle"),notify:n((s,a)=>{const l=a==="warn"?"warning":a;t.broadcast({type:"extension_ui_request",id:M(),method:"notify",message:s,notifyType:l})},"notify"),onTerminalInput:n(()=>i,"onTerminalInput"),setWorkingMessage:i,setWorkingVisible:i,setWorkingIndicator:i,setHiddenThinkingLabel:i,setFooter:i,setHeader:i,custom:n(()=>Promise.resolve(void 0),"custom"),pasteToEditor:i,setEditorText:i,getEditorText:n(()=>"","getEditorText"),addAutocompleteProvider:i,setEditorComponent:i,getEditorComponent:n(()=>{},"getEditorComponent"),getAllThemes:n(()=>[],"getAllThemes"),getTheme:n(()=>{},"getTheme"),setTheme:i,getToolsExpanded:n(()=>!1,"getToolsExpanded"),setToolsExpanded:i}}n(at,"makeBrokerUiContext");export{vo as REVIEW_BOUNDARY_NAVIGATION_CODE,go as ReviewBoundaryNavigationError,tt as brokerToolOptions,it as buildBrokerSession,nt as disposeActiveSession,at as makeBrokerUiContext,uo as runBroker,ie as snapshotMessages};
|
|
@@ -22,9 +22,9 @@ type ReshapeOptions = {
|
|
|
22
22
|
profileId?: string | null;
|
|
23
23
|
};
|
|
24
24
|
/** Synchronous reshape for CLI callers outside the daemon. */
|
|
25
|
-
export declare function reshapeNode(nodeId: string, opts?: ReshapeOptions, beforePersist?: () => void): Promise<NodeMeta>;
|
|
25
|
+
export declare function reshapeNode(nodeId: string, opts?: ReshapeOptions, beforePersist?: () => void | Promise<void>): Promise<NodeMeta>;
|
|
26
26
|
/** Daemon-safe reshape: network-backed config resolution happens off its event loop. */
|
|
27
|
-
export declare function reshapeNodeAsync(nodeId: string, opts?: ReshapeOptions, beforePersist?: () => void): Promise<NodeMeta>;
|
|
27
|
+
export declare function reshapeNodeAsync(nodeId: string, opts?: ReshapeOptions, beforePersist?: () => void | Promise<void>): Promise<NodeMeta>;
|
|
28
28
|
/** Promote a node to an orchestrator (mode→orchestrator), optionally
|
|
29
29
|
* specializing its kind (e.g. a `general` worker becoming a
|
|
30
30
|
* `developer.orchestrator`) and optionally also making it resident. Idempotent:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var g=Object.defineProperty;var r=(n,e)=>g(n,"name",{value:e,configurable:!0});import{getNode as o,updateNode as x}from"../canvas/index.js";import{transition as E}from"./lifecycle.js";import{ensureDaemon as R}from"../../daemon/manage.js";import{buildLaunchSpec as $,buildLaunchSpecAsync as A}from"./launch.js";import{ensureRoadmap as m,hasRoadmap as c,roadmapRef as u}from"./roadmap.js";import{readGoal as f,goalPath as w}from"./kickoff.js";async function k(n,e){const i=await o(n);if(i===null)throw new Error(`unknown node: ${n}`);const a=e.kind??i.kind,l=e.mode??i.mode,d=e.lifecycle??i.lifecycle,t=e.profileId===void 0?i.profile_id??null:e.profileId;return{node:i,kind:a,mode:l,lifecycle:d,profileId:t,launchOpts:{lifecycle:d,hasManager:i.parent!==null,model:e.model??i.model_override??void 0,modelOverride:e.model!==void 0,modelIntent:e.model===void 0?i.launch?.modelIntent:void 0,modelExact:e.model===void 0?i.launch?.modelExact:void 0,cwd:i.cwd,profileId:t}}}r(k,"reshapeInputs");async function s(n,e,i,a,l){l?.();const d=i.node.name===i.node.kind&&i.kind!==i.node.kind;return await x(n,{kind:i.kind,mode:i.mode,lifecycle:i.lifecycle,profile_id:i.profileId,launch:a,...d?{name:i.kind}:{},...e.model!==void 0?{model_override:a?.model??e.model}:{}})}r(s,"persistReshape");async function h(n,e={},i){const a=await k(n,e);return await s(n,e,a,$(a.kind,a.mode,a.launchOpts).launch,i)}r(h,"reshapeNode");async function y(n,e={},i){const a=await k(n,e),{launch:l}=await A(a.kind,a.mode,a.launchOpts);return await s(n,e,a,l,i)}r(y,"reshapeNodeAsync");async function N(n,e={}){const i=await o(n);if(i===null)throw new Error(`unknown node: ${n}`);let a=!1;return{meta:await h(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{},mode:"orchestrator",lifecycle:e.resident===!0?"resident":i.lifecycle},async()=>{if(!await c(n)){const d=await f(n);a=await m(n,d!==null&&d.trim()!==""?{goal:d.trim()}:{})}}),roadmapWritten:a,roadmapRef:u(n),goalPath:await w(n)}}r(N,"promote");async function O(n,e={}){const i=await o(n);if(i===null)throw new Error(`unknown node: ${n}`);let a=!1;return{meta:await y(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{},mode:"orchestrator",lifecycle:e.resident===!0?"resident":i.lifecycle},async()=>{if(!await c(n)){const d=await f(n);a=await m(n,d!==null&&d.trim()!==""?{goal:d.trim()}:{})}}),roadmapWritten:a,roadmapRef:u(n),goalPath:await w(n)}}r(O,"promoteAsync");async function Y(n,e={},i={}){if(await o(n)===null)throw new Error(`unknown node: ${n}`);return e.promote===!0?await O(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{}}):(e.kind!==void 0||e.model!==void 0)&&await y(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{}}),{...await _(n,{},i),promoted:e.promote===!0}}r(Y,"requestYieldAsync");async function _(n,e={},i={}){const a=i.ensure??R;if(await o(n)===null)throw new Error(`unknown node: ${n}`);const d=e.kind!==void 0||e.model!==void 0;let t=!1;return e.promote===!0?(await N(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{}}),t=!0):d&&await h(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{}}),a(),await E(n,"yield"),{meta:await o(n),promoted:t,willRefresh:!0}}r(_,"requestYield");export{N as promote,O as promoteAsync,_ as requestYield,Y as requestYieldAsync,h as reshapeNode,y as reshapeNodeAsync};
|
|
1
|
+
var g=Object.defineProperty;var r=(n,e)=>g(n,"name",{value:e,configurable:!0});import{getNode as o,updateNode as x}from"../canvas/index.js";import{transition as E}from"./lifecycle.js";import{ensureDaemon as R}from"../../daemon/manage.js";import{buildLaunchSpec as $,buildLaunchSpecAsync as A}from"./launch.js";import{ensureRoadmap as m,hasRoadmap as c,roadmapRef as u}from"./roadmap.js";import{readGoal as f,goalPath as w}from"./kickoff.js";async function k(n,e){const i=await o(n);if(i===null)throw new Error(`unknown node: ${n}`);const a=e.kind??i.kind,l=e.mode??i.mode,d=e.lifecycle??i.lifecycle,t=e.profileId===void 0?i.profile_id??null:e.profileId;return{node:i,kind:a,mode:l,lifecycle:d,profileId:t,launchOpts:{lifecycle:d,hasManager:i.parent!==null,model:e.model??i.model_override??void 0,modelOverride:e.model!==void 0,modelIntent:e.model===void 0?i.launch?.modelIntent:void 0,modelExact:e.model===void 0?i.launch?.modelExact:void 0,cwd:i.cwd,profileId:t}}}r(k,"reshapeInputs");async function s(n,e,i,a,l){await l?.();const d=i.node.name===i.node.kind&&i.kind!==i.node.kind;return await x(n,{kind:i.kind,mode:i.mode,lifecycle:i.lifecycle,profile_id:i.profileId,launch:a,...d?{name:i.kind}:{},...e.model!==void 0?{model_override:a?.model??e.model}:{}})}r(s,"persistReshape");async function h(n,e={},i){const a=await k(n,e);return await s(n,e,a,$(a.kind,a.mode,a.launchOpts).launch,i)}r(h,"reshapeNode");async function y(n,e={},i){const a=await k(n,e),{launch:l}=await A(a.kind,a.mode,a.launchOpts);return await s(n,e,a,l,i)}r(y,"reshapeNodeAsync");async function N(n,e={}){const i=await o(n);if(i===null)throw new Error(`unknown node: ${n}`);let a=!1;return{meta:await h(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{},mode:"orchestrator",lifecycle:e.resident===!0?"resident":i.lifecycle},async()=>{if(!await c(n)){const d=await f(n);a=await m(n,d!==null&&d.trim()!==""?{goal:d.trim()}:{})}}),roadmapWritten:a,roadmapRef:u(n),goalPath:await w(n)}}r(N,"promote");async function O(n,e={}){const i=await o(n);if(i===null)throw new Error(`unknown node: ${n}`);let a=!1;return{meta:await y(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{},mode:"orchestrator",lifecycle:e.resident===!0?"resident":i.lifecycle},async()=>{if(!await c(n)){const d=await f(n);a=await m(n,d!==null&&d.trim()!==""?{goal:d.trim()}:{})}}),roadmapWritten:a,roadmapRef:u(n),goalPath:await w(n)}}r(O,"promoteAsync");async function Y(n,e={},i={}){if(await o(n)===null)throw new Error(`unknown node: ${n}`);return e.promote===!0?await O(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{}}):(e.kind!==void 0||e.model!==void 0)&&await y(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{}}),{...await _(n,{},i),promoted:e.promote===!0}}r(Y,"requestYieldAsync");async function _(n,e={},i={}){const a=i.ensure??R;if(await o(n)===null)throw new Error(`unknown node: ${n}`);const d=e.kind!==void 0||e.model!==void 0;let t=!1;return e.promote===!0?(await N(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{}}),t=!0):d&&await h(n,{...e.kind!==void 0?{kind:e.kind}:{},...e.model!==void 0?{model:e.model}:{}}),a(),await E(n,"yield"),{meta:await o(n),promoted:t,willRefresh:!0}}r(_,"requestYield");export{N as promote,O as promoteAsync,_ as requestYield,Y as requestYieldAsync,h as reshapeNode,y as reshapeNodeAsync};
|