@duckmind/dm-windows-x64 0.60.6 → 0.60.9
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +70 -123
- package/extensions/dm-9router-ext/src/index.js +410 -5
- package/extensions/dm-caveman/extensions/caveman.js +283 -12
- package/extensions/dm-cliproxy/index.js +182 -2
- package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
- package/extensions/dm-cliproxy/src/apply.js +228 -1
- package/extensions/dm-cliproxy/src/cache.js +42 -1
- package/extensions/dm-cliproxy/src/commands.js +50 -2
- package/extensions/dm-cliproxy/src/compat.js +81 -1
- package/extensions/dm-cliproxy/src/config.js +192 -2
- package/extensions/dm-cliproxy/src/conflicts.js +46 -1
- package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
- package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
- package/extensions/dm-cliproxy/src/log.js +23 -1
- package/extensions/dm-cliproxy/src/status-quota.js +77 -1
- package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
- package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
- package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
- package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
- package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
- package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
- package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
- package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
- package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
- package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
- package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
- package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
- package/extensions/dm-context/src/context.js +144 -1
- package/extensions/dm-context/src/index.js +339 -7
- package/extensions/dm-context/src/utils.js +9 -1
- package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
- package/extensions/dm-cua/index.js +75 -6
- package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
- package/extensions/dm-cua/src/browser-install.mjs +331 -2
- package/extensions/dm-fff/src/index.js +688 -12
- package/extensions/dm-fff/src/query.js +60 -1
- package/extensions/dm-goal/src/goal.js +894 -23
- package/extensions/dm-image2/index.js +103 -9
- package/extensions/dm-image2/src/image-lib.mjs +1275 -8
- package/extensions/dm-subagents/install.mjs +62 -8
- package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
- package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
- package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
- package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
- package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
- package/extensions/dm-subagents/src/agents/agents.js +1139 -11
- package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
- package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
- package/extensions/dm-subagents/src/agents/identity.js +29 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
- package/extensions/dm-subagents/src/agents/skills.js +614 -6
- package/extensions/dm-subagents/src/extension/config.js +35 -2
- package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
- package/extensions/dm-subagents/src/extension/doctor.js +172 -15
- package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
- package/extensions/dm-subagents/src/extension/index.js +521 -359
- package/extensions/dm-subagents/src/extension/rpc.js +266 -7
- package/extensions/dm-subagents/src/extension/schemas.js +275 -1
- package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
- package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
- package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
- package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
- package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
- package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
- package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
- package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
- package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
- package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
- package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
- package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
- package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
- package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
- package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
- package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
- package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
- package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
- package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
- package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
- package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
- package/extensions/dm-subagents/src/shared/formatters.js +98 -7
- package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
- package/extensions/dm-subagents/src/shared/model-info.js +62 -1
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
- package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
- package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
- package/extensions/dm-subagents/src/shared/settings.js +198 -11
- package/extensions/dm-subagents/src/shared/status-format.js +53 -1
- package/extensions/dm-subagents/src/shared/types.js +184 -6
- package/extensions/dm-subagents/src/shared/utils.js +462 -2
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
- package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
- package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
- package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
- package/extensions/dm-subagents/src/tui/render.js +1542 -4
- package/extensions/dm-usage/index.js +1294 -9
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
- package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
- package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
- package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
- package/extensions/greedysearch-dm/bin/search.mjs +620 -540
- package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
- package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
- package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
- package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
- package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
- package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
- package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
- package/extensions/greedysearch-dm/index.js +123 -23
- package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
- package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
- package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
- package/extensions/greedysearch-dm/src/github.mjs +222 -7
- package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
- package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
- package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
- package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
- package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
- package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
- package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
- package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
- package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
- package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
- package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
- package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
- package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
- package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
- package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
- package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
- package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
- package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
- package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
- package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
- package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
- package/package.json +1 -1
|
@@ -1,14 +1,435 @@
|
|
|
1
|
-
import*as l from"node:fs";import*as d$ from"node:path";import*as x0 from"node:path";import{getMarkdownTheme as G4,keyText as S1}from"@duckmind/dm-coding-agent";import{Container as D1,Markdown as _4,Spacer as K4,Text as R1,visibleWidth as I0}from"@duckmind/dm-tui";import*as U$ from"node:os";import*as q$ from"node:path";var _0="subagent:control-event",K0="subagent:control-intercom";function I$($){return $.trim().replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"unknown"}function F1($){let Z=$?.env??process.env,J=$&&Object.hasOwn($,"getuid")?$.getuid:process.getuid?.bind(process);if(typeof J==="function")return`uid-${J()}`;for(let X of["USERNAME","USER","LOGNAME"]){let f=Z[X];if(f)return`user-${I$(f)}`}let Q=$&&Object.hasOwn($,"userInfo")?$.userInfo:U$.userInfo;try{let X=Q?.().username;if(X)return`user-${I$(X)}`}catch{}let q=Z.USERPROFILE??Z.HOME;if(q)return`home-${I$(q)}`;let U=$&&Object.hasOwn($,"homedir")?$.homedir:U$.homedir;try{let X=U?.();if(X)return`home-${I$(X)}`}catch{}return"shared"}var i=q$.join(U$.tmpdir(),`dm-subagents-${F1()}`),X$=q$.join(i,"async-subagent-results"),C1=q$.join(i,"async-subagent-runs"),E1=q$.join(i,"chain-runs"),c2=q$.join(i,"artifacts"),n$="subagent-async";var M0=250,z0=4;import*as v from"node:fs";import*as A$ from"node:path";var B$=new Map;function u$($){return $ instanceof Error?$.message:String($)}function F0($){return typeof $==="object"&&$!==null&&"code"in $&&$.code==="ENOENT"}function T$($){let Z=A$.join($,"status.json"),J;try{J=v.statSync(Z)}catch(X){if(F0(X))return null;throw Error(`Failed to inspect async status file '${Z}': ${u$(X)}`,{cause:X instanceof Error?X:void 0})}let Q=B$.get(Z);if(Q&&Q.mtime===J.mtimeMs)return Q.status;let q;try{q=v.readFileSync(Z,"utf-8")}catch(X){if(F0(X))return null;throw Error(`Failed to read async status file '${Z}': ${u$(X)}`,{cause:X instanceof Error?X:void 0})}let U;try{U=JSON.parse(q)}catch(X){throw Error(`Failed to parse async status file '${Z}': ${u$(X)}`,{cause:X instanceof Error?X:void 0})}if(B$.set(Z,{mtime:J.mtimeMs,status:U}),B$.size>50){let X=B$.keys().next().value;if(X)B$.delete(X)}return U}var l$=["off","minimal","low","medium","high","xhigh"];function C0($){let Z=$.lastIndexOf(":");if(Z===-1)return{baseModel:$,thinkingSuffix:""};let J=l$.find((Q)=>Q===$.substring(Z+1));if(!J)return{baseModel:$,thinkingSuffix:""};return{baseModel:$.substring(0,Z),thinkingSuffix:`:${J}`}}function N$($){return $<1000?String($):$<1e4?`${($/1000).toFixed(1)}k`:`${Math.round($/1000)}k`}function o$($,Z){let J=$?C0($):void 0,Q=J?.baseModel??$,q=l$.find((X)=>X===Z?.trim()),U=J?.thinkingSuffix?J.thinkingSuffix.slice(1):q;if(Q){let X=Q.lastIndexOf("/");if(X!==-1)Q=Q.slice(X+1)}return[Q,U?`thinking ${U}`:void 0].filter(Boolean).join(" · ")}function p($){if($<1000)return`${$}ms`;if($<60000)return`${($/1000).toFixed(1)}s`;return`${Math.floor($/60000)}m${Math.floor($%60000/1000)}s`}function e($){let Z=process.env.HOME;if(Z&&$.startsWith(Z))return`~${$.slice(Z.length)}`;return $}function T1($,Z,J){if(typeof $!=="object"||$===null)return!1;let{start:Q,count:q,stepIndex:U}=$;return typeof Q==="number"&&typeof q==="number"&&typeof U==="number"&&Number.isInteger(Q)&&Number.isInteger(q)&&Number.isInteger(U)&&Q>=0&&q>0&&U>=0&&U<J&&Q+q<=Z}function a($,Z,J){if(!Array.isArray($))return[];return $.filter((Q)=>T1(Q,Z,J)).sort((Q,q)=>Q.stepIndex-q.stepIndex||Q.start-q.start)}function p$($,Z,J){let Q=0,q=0;for(let U of J){while(q<U.start&&Q<Z){if(q===$)return Q;q++,Q++}if($>=U.start&&$<U.start+U.count)return U.stepIndex;q=U.start+U.count,Q=U.stepIndex+1}while(q<=$&&Q<Z){if(q===$)return Q;q++,Q++}return Math.max(0,Z-1)}function N1($){if($<1000)return"now";if($<60000)return`${Math.floor($/1000)}s`;return`${Math.floor($/60000)}m`}function S$($,Z,J=Date.now()){if($===void 0){if(Z==="needs_attention")return"needs attention";if(Z==="active_long_running")return"active but long-running";return}let Q=N1(Math.max(0,J-$));if(Z==="needs_attention")return`no activity for ${Q}`;if(Z==="active_long_running")return`active but long-running · last activity ${Q} ago`;return Q==="now"?"active now":`active ${Q} ago`}function E0($){return $==="complete"||$==="completed"}function W0($){if($.some((Z)=>Z.status==="running"))return"running";if($.some((Z)=>Z.status==="failed"))return"failed";if($.some((Z)=>Z.status==="paused"))return"paused";if($.length>0&&$.every((Z)=>E0(Z.status)))return"complete";return"pending"}function G$($){return $===1?"1 agent running":`${$} agents running`}function r$($,Z,J={}){let Q=$.filter((H)=>H.status==="running").length,q=$.filter((H)=>E0(H.status)).length,U=$.filter((H)=>H.status==="failed").length,X=$.filter((H)=>H.status==="paused").length,f=[`${q}/${Z} done`];if(J.showRunning!==!1&&Q>0)f.unshift(G$(Q));if(U>0)f.push(`${U} failed`);if(X>0)f.push(`${X} paused`);return f.join(" · ")}function L0($){let Z={total:0,running:0,paused:0,complete:0,failed:0,queued:0};for(let J of $??[]){Z.total++,Z[J.state]++;let Q=L0([...J.children??[],...J.steps?.flatMap((q)=>q.children??[])??[]]);Z.total+=Q.total,Z.running+=Q.running,Z.paused+=Q.paused,Z.complete+=Q.complete,Z.failed+=Q.failed,Z.queued+=Q.queued}return Z}function V$($){let Z=L0($);if(Z.total===0)return;let J=[Z.running>0?`${Z.running} running`:"",Z.paused>0?`${Z.paused} paused`:"",Z.failed>0?`${Z.failed} failed`:"",Z.complete>0?`${Z.complete} complete`:"",Z.queued>0?`${Z.queued} queued`:""].filter(Boolean);return`+${Z.total} nested run${Z.total===1?"":"s"}${J.length?` (${J.join(", ")})`:""}`}function j0(){return S1("app.tools.expand")}function v0(){return`Press ${j0()} for live detail`}function P$(){return process.stdout.columns||120}var y1=new Intl.Segmenter(void 0,{granularity:"grapheme"});function g($,Z){if(I0($)<=Z)return $;let J=Z-1,Q="",q=0,U=[],X=0;while(X<$.length){let f=$.slice(X).match(/^\x1b\[[0-9;]*m/);if(f){let V=f[0];if(Q+=V,V==="\x1B[0m"||V==="\x1B[m")U=[];else U.push(V);X+=V.length;continue}let H=X;while(H<$.length&&!$.slice(H).match(/^\x1b\[[0-9;]*m/))H++;let G=$.slice(X,H);for(let V of y1.segment(G)){let O=V.segment,M=I0(O);if(q+M>J)return Q+U.join("")+"…";Q+=O,q+=M}X=H}return Q+U.join("")+"…"}var T0=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],k1="●";function K$(...$){let Z;for(let J of $){if(J===void 0||!Number.isFinite(J))continue;Z=(Z??0)+Math.trunc(J)}return Z}function Y$($){if($===void 0)return k1;return T0[Math.abs($)%T0.length]}function P1($,Z,J,Q){if(!$.currentTool)return;let q=Math.max(50,Z-20),U=$.currentToolArgs?J||$.currentToolArgs.length<=q?$.currentToolArgs:`${$.currentToolArgs.slice(0,q)}...`:"",X=$.currentToolStartedAt!==void 0&&Q!==void 0?` | ${p(Math.max(0,Q-$.currentToolStartedAt))}`:"";return U?`${$.currentTool}: ${U}${X}`:`${$.currentTool}${X}`}function M$($,Z){if($.lastActivityAt!==void 0&&Z!==void 0)return S$($.lastActivityAt,$.activityState,Z);if($.activityState==="needs_attention")return"needs attention";if($.activityState==="active_long_running")return"active but long-running";if($.lastActivityAt!==void 0)return"active";return}function s($,Z){return $.bold?.(Z)??Z}function w0($,Z){return Z.filter(Boolean).map((J)=>$.fg("dim",J)).join(` ${$.fg("dim","·")} `)}function s$($){return`${N$($)} token`}function c0($){return`${$} tool use${$===1?"":"s"}`}function x$($){return JSON.stringify({asyncDir:$.asyncDir,status:$.status,activityState:$.activityState,lastActivityAt:$.lastActivityAt,currentTool:$.currentTool,currentToolStartedAt:$.currentToolStartedAt,currentPath:$.currentPath,turnCount:$.turnCount,toolCount:$.toolCount,mode:$.mode,agents:$.agents,currentStep:$.currentStep,chainStepCount:$.chainStepCount,parallelGroups:$.parallelGroups,steps:$.steps,nestedChildren:$.nestedChildren,stepsTotal:$.stepsTotal,runningSteps:$.runningSteps,completedSteps:$.completedSteps,activeParallelGroup:$.activeParallelGroup,startedAt:$.startedAt,updatedAt:$.updatedAt,totalTokens:$.totalTokens})}function g0($){let Z=[...new Set($)];if(Z.length===1&&$.length>1)return`${Z[0]} ×${$.length}`;if($.length>3)return`${$.slice(0,2).join(", ")} +${$.length-2} more`;return $.join(", ")}function D$($){if($.mode==="parallel")return"parallel";if($.mode==="chain")return"chain";if($.mode==="single"&&$.agents?.length===1)return $.agents[0];if($.agents?.length)return g0($.agents);return $.mode??"subagent"}function R$($){let Z=[];if($.currentTool&&$.currentToolStartedAt!==void 0&&$.updatedAt!==void 0)Z.push(`${$.currentTool} ${p(Math.max(0,$.updatedAt-$.currentToolStartedAt))}`);else if($.currentTool)Z.push($.currentTool);if($.currentPath)Z.push(e($.currentPath));if($.turnCount!==void 0)Z.push(`${$.turnCount} turns`);if($.toolCount!==void 0)Z.push(`${$.toolCount} tools`);let J=M$($,$.updatedAt);if(J&&Z.length)return`${J} · ${Z.join(" · ")}`;if(J)return J;if(Z.length)return Z.join(" · ");if($.status==="running")return"thinking…";if($.status==="queued")return"queued…";if($.status==="paused")return"Paused";if($.status==="failed")return"Failed";return"Done"}function j$($,Z){return K$(Z,$.index,$.toolCount,$.turnCount,$.tokens?.total,$.lastActivityAt,$.currentToolStartedAt,$.durationMs)}function b0($){let Z;for(let[J,Q]of($??[]).entries())Z=K$(Z,j$(Q,J));return Z}function m0($){return K$($.updatedAt,$.lastActivityAt,$.toolCount,$.turnCount,$.totalTokens?.total,$.currentStep,$.runningSteps,$.completedSteps,b0($.steps))}function t$($){let Z;for(let J of $)Z=K$(Z,m0(J));return Z}function y$($,Z){if($.status==="running")return Z.fg("accent",Y$(m0($)));if($.status==="queued")return Z.fg("muted","◦");if($.status==="complete")return Z.fg("success","✓");if($.status==="paused")return Z.fg("warning","■");return Z.fg("error","✗")}function v$($,Z,J){if($==="running")return Z.fg("accent",Y$(J));if($==="complete"||$==="completed")return Z.fg("success","✓");if($==="failed")return Z.fg("error","✗");if($==="paused")return Z.fg("warning","■");return Z.fg("muted","◦")}function e$($,Z){if($==="running")return Z.fg("accent","running");if($==="complete"||$==="completed")return Z.fg("success","complete");if($==="failed")return Z.fg("error","failed");if($==="paused")return Z.fg("warning","paused");return Z.fg("dim",$)}function x1($,Z){let J=[];if($.currentTool&&$.currentToolStartedAt!==void 0&&Z!==void 0)J.push(`${$.currentTool} ${p(Math.max(0,Z-$.currentToolStartedAt))}`);else if($.currentTool)J.push($.currentTool);if($.currentPath)J.push(e($.currentPath));if($.turnCount!==void 0)J.push(`${$.turnCount} turns`);if($.toolCount!==void 0)J.push(`${$.toolCount} tools`);if($.tokens?.total)J.push(s$($.tokens.total));let Q=M$($,Z);if(Q&&J.length)return`${Q} · ${J.join(" · ")}`;if(Q)return Q;return J.join(" · ")}function h0($,Z,J=!1,Q=P$()){if(!$.steps?.length)return[];let q=$.chainStepCount??$.steps.length,U=[];for(let X of j1(q,$.steps.length,$.parallelGroups)){let f=$.steps.slice(X.start,X.start+X.count);if(X.isParallel){let G=W0(f);U.push(` ${v$(G,Z,b0(f))} Step ${X.stepIndex+1}/${q}: ${s(Z,"parallel group")} ${Z.fg("dim","·")} ${Z.fg("dim",r$(f,X.count))}`);continue}let H=f[0];if(!H){U.push(` ${Z.fg("dim",`◦ Step ${X.stepIndex+1}/${q}: pending`)}`);continue}U.push(...u0($,Z,H,"Step",X.stepIndex+1,q,J,Q))}return U}function N0($,Z,J=!1,Q=P$()){if(!$.steps?.length)return[];if($.mode!=="parallel"&&$.mode!=="chain")return[];if($.mode==="chain"&&!$.activeParallelGroup&&$.parallelGroups?.length)return h0($,Z,J,Q);let q=$.stepsTotal??$.steps.length,U=[];for(let[X,f]of $.steps.entries()){let H=X===$.steps.length-1?"└":"├",G=x1(f,$.updatedAt),V=$.mode==="parallel"||$.activeParallelGroup?"Agent":"Step",O=$0(Z,f.model,f.thinking);U.push(` ${Z.fg("dim",`${H} ${v$(f.status,Z,j$(f,X))} ${V} ${X+1}/${q}: ${f.agent} · ${e$(f.status,Z)}${O}${G?` · ${G}`:""}`)}`);for(let M of _$(f.children,Z,Q,J,$.updatedAt,J?8:1))U.push(` ${M}`)}return U}function j1($,Z,J=[]){let Q=[],q=0;for(let U=0;U<$;U++){let X=J.find((f)=>f.stepIndex===U);if(X){Q.push({stepIndex:U,start:X.start,count:X.count,isParallel:!0}),q=Math.max(q,X.start+X.count);continue}Q.push({stepIndex:U,start:q,count:q<Z?1:0,isParallel:!1}),q++}return Q}function k$($,Z){let J=[],Q=$.stepsTotal??($.agents?.length??1);if($.activeParallelGroup){let q=$.runningSteps??($.status==="running"?1:0),U=$.completedSteps??($.status==="complete"?Q:0);if($.mode==="parallel"){if($.status==="running"&&q>0)J.push(G$(q));if(Q>0)J.push(`${U}/${Q} done`)}else{let f=($.currentStep!==void 0?$.parallelGroups?.find((V)=>$.currentStep>=V.start&&$.currentStep<V.start+V.count):$.parallelGroups?.find((V)=>V.start===0))?.stepIndex??$.currentStep??0,H=$.chainStepCount??Q,G=[`${U}/${Q} done`];if($.status==="running"&&q>0)G.unshift(G$(q));J.push(`step ${f+1}/${H} · parallel group: ${G.join(" · ")}`)}}else if($.currentStep!==void 0)if($.mode==="chain"&&$.parallelGroups?.length){let q=$.chainStepCount??Q;J.push(`step ${p$($.currentStep,q,$.parallelGroups)+1}/${q}`)}else J.push(`step ${$.currentStep+1}/${Q}`);else if(Q>1)J.push(`steps ${Q}`);if($.toolCount!==void 0)J.push(c0($.toolCount));if($.totalTokens?.total)J.push(s$($.totalTokens.total));if($.startedAt!==void 0&&$.updatedAt!==void 0)J.push(p(Math.max(0,$.updatedAt-$.startedAt)));return w0(Z,J)}function d0($,Z){return w0($,[Z.turnCount!==void 0?`${Z.turnCount} turns`:"",Z.toolCount!==void 0?c0(Z.toolCount):"",Z.tokens?.total?s$(Z.tokens.total):"",Z.durationMs!==void 0?p(Z.durationMs):""])}function $0($,Z,J){let Q=o$(Z,J);return Q?$.fg("dim",` (${Q})`):""}function n0($,Z,J,Q){let q=P1($,Z,J,Q);if(q)return q;let U=M$($,Q);if(U)return U;if($.status==="running")return"thinking…";return""}function v1($,Z){if(typeof Z.index!=="number")return;return x0.join($.asyncDir,`output-${Z.index}.log`)}function w1($){if($.agent)return $.agent;if($.agents?.length)return g0($.agents);return $.id}function S0($,Z,J){if($==="running")return Z.fg("accent",Y$(J));if($==="complete"||$==="completed")return Z.fg("success","✓");if($==="failed")return Z.fg("error","✗");if($==="paused")return Z.fg("warning","■");return Z.fg("muted","◦")}function c1($){return K$($.lastUpdate,$.lastActivityAt,$.currentStep,$.toolCount,$.turnCount,$.totalTokens?.total,$.currentToolStartedAt)}function D0($,Z,J){let Q=[];if($.currentTool&&$.currentToolStartedAt!==void 0&&J!==void 0)Q.push(`${$.currentTool} ${p(Math.max(0,J-$.currentToolStartedAt))}`);else if($.currentTool)Q.push($.currentTool);if($.currentPath)Q.push(e($.currentPath));if($.turnCount!==void 0)Q.push(`${$.turnCount} turns`);if($.toolCount!==void 0)Q.push(`${$.toolCount} tools`);let q=M$($,J);if(q&&Q.length)return`${q} · ${Q.join(" · ")}`;if(q)return q;if(Q.length)return Q.join(" · ");if(Z==="running")return"thinking…";if(Z==="queued"||Z==="pending")return"queued…";if(Z==="paused")return"Paused";if(Z==="failed")return"Failed";return"Done"}function _$($,Z,J,Q,q,U=Q?12:1){if(!$?.length||U<=0)return[];if(!Q){let G=V$($);return G?[Z.fg("dim",`↳ ${G}`)]:[]}let X=[],f=2,H=(G,V,O)=>{if(!G?.length||X.length>=U)return;if(V>f){let M=V$(G);if(M&&X.length<U)X.push(Z.fg("dim",`${O}↳ ${M}`));return}for(let M=0;M<G.length;M++){let A=G[M];if(X.length>=U){let S=V$(G.slice(M));if(S)X[X.length-1]=Z.fg("dim",`${O}↳ ${S}`);return}let I=D0(A,A.state,q??A.lastUpdate),N=A.error?` · ${A.error}`:"";if(X.push(Z.fg("dim",`${O}↳ ${S0(A.state,Z,c1(A))} ${w1(A)} · ${A.state} · ${I}${N}`)),V===f){let S=V$([...A.steps?.flatMap((F)=>F.children??[])??[],...A.children??[]]);if(S&&X.length<U)X.push(Z.fg("dim",`${O} ↳ ${S}`));continue}for(let S of A.steps??[]){if(X.length>=U)return;X.push(Z.fg("dim",`${O} ↳ ${S0(S.status,Z)} ${S.agent} · ${S.status} · ${D0(S,S.status,q??A.lastUpdate)}`)),H(S.children,V+1,`${O} `)}H(A.children,V+1,`${O} `)}};return H($,0,""),X.map((G)=>g(G,J))}function u0($,Z,J,Q,q,U,X,f){let H=e$(J.status,Z),G=d0(Z,J),V=$0(Z,J.model,J.thinking),O=[` ${v$(J.status,Z,j$(J,q-1))} ${Q} ${q}/${U}: ${s(Z,J.agent)} ${Z.fg("dim","·")} ${H}${V}${G?` ${Z.fg("dim","·")} ${G}`:""}`],M=n0(J,f,X,$.updatedAt);if(M)O.push(` ${Z.fg("dim",`⎿ ${M}`)}`);for(let A of _$(J.children,Z,f,X,$.updatedAt))O.push(` ${A}`);if(J.status==="running"){if(!X)O.push(` ${Z.fg("accent",v0())}`);let A=v1($,J);if(A)O.push(` ${Z.fg("dim",`output: ${e(A)}`)}`);if(X){let I=M$(J,$.updatedAt);if(I&&I!==M)O.push(` ${Z.fg("accent",I)}`);for(let N of J.recentTools?.slice(-3)??[]){let S=Math.max(40,f-30),F=N.args.length<=S?N.args:`${N.args.slice(0,S)}...`;O.push(` ${Z.fg("dim",`${N.tool}${F?`: ${F}`:""}`)}`)}for(let N of J.recentOutput?.slice(-5)??[])O.push(` ${Z.fg("dim",N)}`)}}return O}function g1($,Z,J,Q){if(!$.steps?.length)return[` ${Z.fg("dim",`⎿ ${R$($)}`)}`,..._$($.nestedChildren,Z,Q,J,$.updatedAt).map((G)=>` ${G}`)];if($.mode==="chain"&&!$.activeParallelGroup&&$.parallelGroups?.length)return h0($,Z,J,Q);let q=$.stepsTotal??$.steps.length,U=$.mode==="parallel"||$.activeParallelGroup?"Agent":"Step",X=[];for(let[G,V]of $.steps.entries())X.push(...u0($,Z,V,U,G+1,q,J,Q));let f=new Set($.steps.flatMap((G)=>G.children?.map((V)=>V.id)??[])),H=$.nestedChildren?.filter((G)=>!f.has(G.id))??[];for(let G of _$(H,Z,Q,J,$.updatedAt))X.push(` ${G}`);return X}function l0($,Z,J,Q){let q=k$($,Z),U=$.mode==="chain"?$.chainStepCount:$.stepsTotal??$.agents?.length??$.steps?.length,X=D$($),f=`async subagent ${X}${U&&U>1?` (${U})`:""}`;return[`${Z.fg("toolTitle",s(Z,f))} ${Z.fg("dim","· background")}`,`${y$($,Z)} ${s(Z,X)}${q?` ${Z.fg("dim","·")} ${q}`:""}`,...g1($,Z,Q,J)].map((H)=>g(H,J))}function b1($,Z,J){let Q=l0($,Z,J,!1);if(Q.length<=10||!$.steps?.length||$.mode!=="parallel"&&!$.activeParallelGroup)return Q;let q=$.stepsTotal??$.steps.length,U=$.mode==="parallel"||$.activeParallelGroup?"Agent":"Step",X=Q.slice(0,2);for(let[f,H]of $.steps.entries()){let G=e$(H.status,Z),V=n0(H,J,!1,$.updatedAt),O=d0(Z,H),M=V?` ${Z.fg("dim","·")} ${Z.fg("dim",V)}`:"",A=$0(Z,H.model,H.thinking);X.push(` ${v$(H.status,Z,j$(H,f))} ${U} ${f+1}/${q}: ${s(Z,H.agent)} ${Z.fg("dim","·")} ${G}${A}${M}${O?` ${Z.fg("dim","·")} ${O}`:""}`);for(let I of _$(H.children,Z,J,!1,$.updatedAt))X.push(` ${I}`)}if($.steps.some((f)=>f.status==="running"))X.push(Z.fg("accent",` ${v0()}`));return X.map((f)=>g(f,J))}var m1=19,j;function o0(){j=void 0}function h1(){let $=process.stdout.rows||30;return Math.max(1,$-m1)}function p0(){return process.stdout.rows||30}function r0(){return process.stdout.columns||120}function d1($){return j?.expanded===$&&j.rows===p0()&&j.columns===r0()}function Z0($){return{running:$.filter((Z)=>Z.status==="running"),queued:$.filter((Z)=>Z.status==="queued"),complete:$.filter((Z)=>Z.status==="complete"),failed:$.filter((Z)=>Z.status==="failed"),paused:$.filter((Z)=>Z.status==="paused")}}function a$($,Z,J){let Q=Z0($),q=Q.running.length>0||Q.queued.length>0,U=Q.running.length>0?Y$(t$(Q.running)):q?"●":"○",X=[];if(Q.running.length>0)X.push(`${Q.running.length}/${$.length} running`);if(Q.queued.length>0)X.push(`${Q.queued.length} queued`);if(Q.failed.length>0)X.push(`${Q.failed.length} failed`);if(Q.paused.length>0)X.push(`${Q.paused.length} paused`);if(!q&&Q.complete.length>0)X.push(`${Q.complete.length}/${$.length} done`);return[g(`${Z.fg(q?"accent":"dim",U)} ${Z.fg(q?"accent":"dim","subagents")} (${X.join(", ")||`${$.length} total`})`,J)]}function R0($){return[...$.filter((Z)=>Z.status==="running"),...$.filter((Z)=>Z.status==="queued"),...$.filter((Z)=>Z.status!=="running"&&Z.status!=="queued")]}function $$($){return $.asyncId}function i$($){return $?.status==="running"||$?.status==="queued"}function n1($,Z,J){if(J<=0)return[];let Q=new Map($.map((X)=>[$$(X),X])),q=[],U=(X)=>{if(q.includes(X)||!Q.has(X))return;q.push(X)};for(let X of Z){if(!i$(Q.get(X)))continue;if(U(X),q.length>=J)return q}for(let X of R0($)){if(!i$(X))continue;let f=$$(X);if(U(f),q.length>=J)break}if(q.length>=J)return q;for(let X of Z){if(i$(Q.get(X)))continue;if(U(X),q.length>=J)return q}for(let X of R0($)){let f=$$(X);if(U(f),q.length>=J)break}return q}function u1($,Z,J){let Q=Z0($),q=Q.running.length>0||Q.queued.length>0,U=Q.running.length>0?Y$(t$(Q.running)):q?"●":"○",X=[];if(Q.running.length>0)X.push(G$(Q.running.length));if(Q.queued.length>0)X.push(`${Q.queued.length} queued`);if(!q){if(Q.failed.length>0)X.push(`${Q.failed.length} failed`);if(Q.paused.length>0)X.push(`${Q.paused.length} paused`);if(Q.complete.length>0)X.push(`${Q.complete.length}/${$.length} done`)}return g(`${Z.fg(q?"accent":"dim",U)} ${Z.fg(q?"accent":"dim","Async agents")} ${Z.fg("dim","·")} ${Z.fg("dim",X.join(", ")||`${$.length} total`)}`,J)}function l1($,Z,J){let Q=k$($,Z),q=R$($),U=$.status==="complete"?"done":$.status,X=[s(Z,D$($)),Z.fg("dim",U),Q,q&&q.toLowerCase()!==U?Z.fg("dim",q):""].filter(Boolean);return g(` ${y$($,Z)} ${X.join(` ${Z.fg("dim","·")} `)}`,J)}function o1($,Z,J){let Q=Z0($),q=[];if(Q.running.length>0)q.push(`${Q.running.length} running`);if(Q.queued.length>0)q.push(`${Q.queued.length} queued`);let U=Q.complete.length+Q.failed.length+Q.paused.length;if(U>0)q.push(`${U} finished`);return g(Z.fg("dim",` +${$.length} more${q.length?` (${q.join(", ")})`:""}`),J)}function y0($,Z,J,Q,q){let U=Math.max(1,Q);if(U===1)return{lines:a$($,Z,J),visibleJobKeys:[]};let X=U-1,f=n1($,q,X),H=new Map($.map((A)=>[$$(A),A])),G=f.map((A)=>H.get(A)).filter((A)=>Boolean(A)),V=$.filter((A)=>!f.includes($$(A)));if(V.length>0&&G.length>=X&&X>0)G=G.slice(0,X-1),f=G.map($$),V=$.filter((A)=>!f.includes($$(A)));let M=[u1($,Z,J),...G.map((A)=>l1(A,Z,J))];if(V.length>0&&M.length<U)M.push(o1(V,Z,J));while(M.length<U)M.push(" ");return{lines:M.slice(0,U),visibleJobKeys:f}}function i0($){return Math.max(10,Math.min(14,Math.floor($*0.35)))}function k0($,Z,J,Q){let q=process.stdout.rows||30,U=Q?Math.max(12,Math.min(24,Math.floor(q*0.55))):i0(q);if($.length<=U)return $;let X=Math.max(1,U-1),f=$.length-X,H=Q?`… ${f} live-detail lines hidden`:`… ${f} lines hidden · ${j0()} expands`;return[...$.slice(0,X),g(Z.fg("dim",H),J)]}function p1($,Z,J,Q,q){if(q)return o0(),k0(Z,J,Q,!0);let U=d1(q),X=p0(),f=r0(),H=h1();if(U&&j?.tier==="single-line")return a$($,J,Q);if(U&&j?.tier==="progressive"&&j.lockedRows!==void 0){let O=y0($,J,Q,j.lockedRows,j.visibleJobKeys);return j.visibleJobKeys=O.visibleJobKeys,O.lines}if(Z.length<=H)return j={expanded:q,rows:X,columns:f,tier:"full",visibleJobKeys:[]},k0(Z,J,Q,!1);if(H<=2)return j={expanded:q,rows:X,columns:f,tier:"single-line",visibleJobKeys:[]},a$($,J,Q);let G=Math.min(H,i0(X)),V=y0($,J,Q,G,[]);return j={expanded:q,rows:X,columns:f,tier:"progressive",lockedRows:G,visibleJobKeys:V.visibleJobKeys},V.lines}function r1($,Z){return(J,Q)=>{let q=P$(),U=Z?P0($,Q,q,!0):$.length===1?b1($[0],Q,q):P0($,Q,q,!1),X=new D1;for(let f of p1($,U,Q,q,Z))X.addChild(new R1(f,1,0));return X}}function P0($,Z,J=P$(),Q=!1){if($.length===0)return[];if($.length===1)return l0($[0],Z,J,Q);let q=$.filter((F)=>F.status==="running"),U=$.filter((F)=>F.status==="queued"),X=$.filter((F)=>F.status!=="running"&&F.status!=="queued"),f=[],H=q.length>0||U.length>0,G=q.length>0?Y$(t$(q)):H?"●":"○";f.push(g(`${Z.fg(H?"accent":"dim",G)} ${Z.fg(H?"accent":"dim","Async agents")} ${Z.fg("dim","· background")}`,J));let V=[],O=0,M=0,A=!1,I=z0;for(let F of q){if(I<=0){O++;continue}let B=k$(F,Z);V.push([`${y$(F,Z)} ${s(Z,D$(F))}${B?` ${Z.fg("dim","·")} ${B}`:""}`,` ${Z.fg("dim",`⎿ ${R$(F)}`)}`,...N0(F,Z,Q,J)]),I--}if(U.length>0&&I>0)V.push([`${Z.fg("muted","◦")} ${Z.fg("dim",`${U.length} queued`)}`]),A=!0,I--;for(let F of X){if(I<=0){M++;continue}let B=k$(F,Z);V.push([`${y$(F,Z)} ${s(Z,D$(F))}${B?` ${Z.fg("dim","·")} ${B}`:""}`,` ${Z.fg("dim",`⎿ ${R$(F)}`)}`,...N0(F,Z,Q,J)]),I--}let N=U.length>0&&!A?U.length:0,S=O+M+N;if(S>0){let F=[];if(O>0)F.push(`${O} running`);if(N>0)F.push(`${N} queued`);if(M>0)F.push(`${M} finished`);V.push([Z.fg("dim",`+${S} more (${F.join(", ")})`)])}for(let F=0;F<V.length;F++){let B=V[F],Y=F===V.length-1,W=Y?"└─":"├─",C=Y?" ":"│ ";f.push(g(`${Z.fg("dim",W)} ${B[0]}`,J));for(let E of B.slice(1))f.push(g(`${Z.fg("dim",C)} ${E}`,J))}return f}function a0($,Z){if(Z.length===0){if(o0(),$.hasUI)$.ui.setWidget(n$,void 0);return}if(!$.hasUI)return;$.ui.setWidget(n$,r1(Z,$.ui.getToolsExpanded?.()??!1))}function i1($){let Z=[];if($.elapsedMs!==void 0)Z.push(`elapsed ${Math.floor(Math.max(0,$.elapsedMs)/1000)}s`);if($.turns!==void 0)Z.push(`${$.turns} turns`);if($.tokens!==void 0)Z.push(`${$.tokens} tokens`);if($.toolCount!==void 0)Z.push(`${$.toolCount} tools`);if($.currentTool)Z.push(`tool ${$.currentTool}${$.currentToolDurationMs!==void 0?` ${Math.floor(Math.max(0,$.currentToolDurationMs)/1000)}s`:""}`);if($.currentPath)Z.push(`path ${$.currentPath}`);return Z.length>0?Z.join(" | "):void 0}function s0($,Z){let J=$.runId;if($.reason==="completion_guard")return[`Subagent failed: ${$.agent}`,`Run: ${J}${$.index!==void 0?` step ${$.index+1}`:""}`,`Signal: ${$.message}`,"Next: read the output artifact or session from the subagent result, then retry with a more explicit implementation prompt or handle the fix directly.",Z?`Run intercom target (may be inactive): ${Z}`:void 0].filter((U)=>Boolean(U)).join(`
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
${f}`:X,V=(($.steps?.length)?$.steps:[{agent:"subagent",status:"running"}]).map((A)=>A.status==="running"||A.status==="pending"?{...A,status:"failed",activityState:void 0,endedAt:A.endedAt??J,durationMs:A.startedAt!==void 0&&A.durationMs===void 0?Math.max(0,J-A.startedAt):A.durationMs,exitCode:A.exitCode??1,error:A.error??H}:A),O={...$,state:"failed",activityState:void 0,lastUpdate:J,endedAt:J,steps:V},M=V[$.currentStep??0]?.agent??V[0]?.agent??"subagent";return{status:O,message:H,result:{id:q,agent:M,mode:$.mode,success:!1,state:"failed",summary:H,results:V.map((A)=>({agent:A.agent,output:A.status==="complete"||A.status==="completed"?"":H,error:A.status==="complete"||A.status==="completed"?void 0:A.error??H,success:A.status==="complete"||A.status==="completed",model:A.model,attemptedModels:A.attemptedModels,modelAttempts:A.modelAttempts,sessionFile:A.sessionFile})),exitCode:1,timestamp:J,durationMs:Math.max(0,J-$.startedAt),asyncDir:Z,sessionId:$.sessionId,sessionFile:$.sessionFile}}}function _1($,Z,J,Q,q){let U=D2(Z,$,Q,q);return f$(J,U.result),f$(P.join($,"status.json"),U.status),W2(P.join($,"events.jsonl"),{type:"subagent.run.repaired_stale",ts:Q,runId:U.status.runId,pid:Z.pid,resultPath:J,message:U.message}),{status:U.status,repaired:!0,resultPath:J,message:U.message}}function K1($){return $==="complete"||$==="failed"||$==="paused"}function*B0($){for(let Z of $??[])yield Z,yield*B0(Z.children),yield*B0(Z.steps?.flatMap((J)=>J.children??[]))}function h$($,Z={}){let J=C$($);for(let Q of B0(J.children)){if(Q.state!=="running"&&Q.state!=="queued")continue;let q=H1($.rootRunId,Q);if(!q)continue;let U=E$(q,{...Z,resultsDir:P.join(Z.resultsDir??X$,"nested",$.rootRunId)}),X=U.status;if(!X)continue;if(!U.repaired&&!K1(X.state))continue;let f=Z.now?.()??Date.now();G1($,{type:K1(X.state)?"subagent.nested.completed":"subagent.nested.updated",ts:f,parentRunId:Q.parentRunId,parentStepIndex:Q.parentStepIndex,child:V1(X,q,{id:Q.id,parentRunId:Q.parentRunId,parentStepIndex:Q.parentStepIndex,depth:Q.depth,path:Q.path,mode:Q.mode,ts:f})})}}function R2($,Z=process.kill){try{return Z($,0),"alive"}catch(J){let Q=typeof J==="object"&&J!==null&&"code"in J?J.code:void 0;if(Q==="ESRCH")return"dead";if(Q==="EPERM")return"unknown";return"unknown"}}function E$($,Z={}){let J=Z.now?.()??Date.now(),Q=L2($),q=!Q&&Z.startedRun?S2($,Z.startedRun,J):void 0,U=Q??q;if(!U)return{status:null,repaired:!1};let X=U.runId||P.basename($),f=P.join(Z.resultsDir??X$,`${X}.json`);if(y.existsSync(f)){let G=U.state==="running"||U.state==="queued"?N2(U,f,J):void 0;if(G)return f$(P.join($,"status.json"),G),{status:G,repaired:!0,resultPath:f,message:"Existing async result file was used to repair stale running status."};return{status:U,repaired:!1,resultPath:f}}if(U.state!=="running"||typeof U.pid!=="number")return{status:Q??null,repaired:!1,resultPath:f};if(!Q){let G=Z.startedRun?.startedAt??U.startedAt;if(J-G<(Z.missingStatusGraceMs??1000))return{status:null,repaired:!1,resultPath:f}}if(R2(U.pid,Z.kill)!=="dead"){let G=Z.staleAlivePidMs??86400000,V=U.lastUpdate??U.startedAt;if(J-V<=G)return{status:Q??null,repaired:!1,resultPath:f};let O=`Async runner process ${U.pid} still has a live PID, but status has not updated for ${J-V}ms. Marked run failed by stale-run reconciliation because PID ownership cannot be verified.`;return _1($,U,f,J,O)}return _1($,U,f,J)}import*as L$ from"node:fs";import*as b from"node:path";function W$($){return $ instanceof Error?$.message:String($)}function A0($){return typeof $==="object"&&$!==null&&"code"in $&&$.code==="ENOENT"}function y2($,Z){let J=b.join($,Z);try{return L$.statSync(J).isDirectory()}catch(Q){if(A0(Q))return!1;throw Error(`Failed to inspect async run path '${J}': ${W$(Q)}`,{cause:Q instanceof Error?Q:void 0})}}function k2($){if(!$)return;try{return L$.statSync($).mtimeMs}catch(Z){if(A0(Z))return;throw Error(`Failed to inspect async output file '${$}': ${W$(Z)}`,{cause:Z instanceof Error?Z:void 0})}}function P2($,Z){if(Z.state!=="running")return{activityState:Z.activityState,lastActivityAt:Z.lastActivityAt};let J=Z.outputFile?b.isAbsolute(Z.outputFile)?Z.outputFile:b.join($,Z.outputFile):void 0,Q=typeof Z.currentStep==="number"?Z.steps?.[Z.currentStep]:void 0;return{activityState:Z.activityState,lastActivityAt:Z.lastActivityAt??k2(J)??Q?.lastActivityAt??Q?.startedAt??Z.startedAt}}function x2($,Z,J=[],Q){if(Z.sessionId!==void 0&&typeof Z.sessionId!=="string")throw Error(`Invalid async status '${b.join($,"status.json")}': sessionId must be a string.`);let{activityState:q,lastActivityAt:U}=P2($,Z),X=Z.steps??[],f=Z.chainStepCount??X.length,H=a(Z.parallelGroups,X.length,f),G=[];if(J.length===0&&Q)try{G=C$(Q)?.children??[]}catch(O){J.push(`Nested status unavailable: ${W$(O)}`)}let V=X.map((O,M)=>{let{activityState:A,lastActivityAt:I}=O;return{index:M,agent:O.agent,...O.label?{label:O.label}:{},...O.phase?{phase:O.phase}:{},...O.outputName?{outputName:O.outputName}:{},...O.structured?{structured:O.structured}:{},status:O.status,...A?{activityState:A}:{},...I?{lastActivityAt:I}:{},...O.currentTool?{currentTool:O.currentTool}:{},...O.currentToolArgs?{currentToolArgs:O.currentToolArgs}:{},...O.currentToolStartedAt?{currentToolStartedAt:O.currentToolStartedAt}:{},...O.currentPath?{currentPath:O.currentPath}:{},...O.recentTools?{recentTools:O.recentTools.map((N)=>({...N}))}:{},...O.recentOutput?{recentOutput:[...O.recentOutput]}:{},...O.turnCount!==void 0?{turnCount:O.turnCount}:{},...O.toolCount!==void 0?{toolCount:O.toolCount}:{},...O.steerCount!==void 0?{steerCount:O.steerCount}:{},...O.lastSteerAt!==void 0?{lastSteerAt:O.lastSteerAt}:{},...O.durationMs!==void 0?{durationMs:O.durationMs}:{},...O.tokens?{tokens:O.tokens}:{},...O.totalCost?{totalCost:O.totalCost}:{},...O.skills?{skills:O.skills}:{},...O.model?{model:O.model}:{},...O.thinking?{thinking:O.thinking}:{},...O.attemptedModels?{attemptedModels:O.attemptedModels}:{},...O.error?{error:O.error}:{},...O.timedOut!==void 0?{timedOut:O.timedOut}:{},...O.turnBudget?{turnBudget:O.turnBudget}:{},...O.turnBudgetExceeded!==void 0?{turnBudgetExceeded:O.turnBudgetExceeded}:{},...O.wrapUpRequested!==void 0?{wrapUpRequested:O.wrapUpRequested}:{},...O.children?.length?{children:O.children}:{}}});return f0(Z.runId||b.basename($),V,G),{id:Z.runId||b.basename($),asyncDir:$,...Z.sessionId?{sessionId:Z.sessionId}:{},state:Z.state,...Z.error?{error:Z.error}:{},activityState:q,lastActivityAt:U,currentTool:Z.currentTool,currentToolStartedAt:Z.currentToolStartedAt,currentPath:Z.currentPath,turnCount:Z.turnCount,toolCount:Z.toolCount,steerCount:Z.steerCount,lastSteerAt:Z.lastSteerAt,mode:Z.mode,cwd:Z.cwd,startedAt:Z.startedAt,lastUpdate:Z.lastUpdate,endedAt:Z.endedAt,...Z.timeoutMs!==void 0?{timeoutMs:Z.timeoutMs}:{},...Z.deadlineAt!==void 0?{deadlineAt:Z.deadlineAt}:{},...Z.timedOut!==void 0?{timedOut:Z.timedOut}:{},...Z.turnBudget?{turnBudget:Z.turnBudget}:{},...Z.turnBudgetExceeded!==void 0?{turnBudgetExceeded:Z.turnBudgetExceeded}:{},...Z.wrapUpRequested!==void 0?{wrapUpRequested:Z.wrapUpRequested}:{},currentStep:Z.currentStep,...Z.chainStepCount!==void 0?{chainStepCount:Z.chainStepCount}:{},...Z.pendingAppends!==void 0?{pendingAppends:Z.pendingAppends}:{},...H.length?{parallelGroups:H}:{},steps:V,...G.length?{nestedChildren:G}:{},...J.length?{nestedWarnings:J}:{},...Z.sessionDir?{sessionDir:Z.sessionDir}:{},...Z.outputFile?{outputFile:Z.outputFile}:{},...Z.totalTokens?{totalTokens:Z.totalTokens}:{},...Z.totalCost?{totalCost:Z.totalCost}:{},...Z.sessionFile?{sessionFile:Z.sessionFile}:{}}}function j2($){let Z=(J)=>{switch(J){case"running":return 0;case"queued":return 1;case"failed":return 2;case"paused":return 2;case"complete":return 3}};return[...$].sort((J,Q)=>{let q=Z(J.state)-Z(Q.state);if(q!==0)return q;let U=J.lastUpdate??J.endedAt??J.startedAt;return(Q.lastUpdate??Q.endedAt??Q.startedAt)-U})}function z1($,Z={}){let J;try{J=L$.readdirSync($).filter((H)=>y2($,H))}catch(H){if(A0(H))return[];throw Error(`Failed to list async runs in '${$}': ${W$(H)}`,{cause:H instanceof Error?H:void 0})}let Q=Z.states?new Set(Z.states):void 0,q=[],U,X=(H)=>{if(!U)U=A1();return U.get(H)};for(let H of J){let G=b.join($,H),O=(Z.reconcile===!1?void 0:E$(G,{resultsDir:Z.resultsDir,kill:Z.kill,now:Z.now}))?.status??T$(G);if(!O)continue;if(Q&&!Q.has(O.state))continue;if(Z.sessionId&&O.sessionId!==Z.sessionId)continue;let M=[],A;try{if(A=X(O.runId||b.basename(G)),A)h$(A,{resultsDir:Z.resultsDir,kill:Z.kill,now:Z.now})}catch(N){M.push(`Nested status unavailable: ${W$(N)}`)}let I=x2(G,O,M,A);q.push(I)}let f=j2(q);return Z.limit!==void 0?f.slice(0,Z.limit):f}var v2=65536,w2=1048576,G0=2097152;function P8($,Z,J,Q={}){let q=Q.completionRetentionMs??1e4,U=Q.pollIntervalMs??M0,X=Q.resultsDir??X$,f=(B,Y=Array.from(Z.asyncJobs.values()))=>{a0(B,Y),B.ui.requestRender?.()},H=(B)=>{try{return l.statSync(d$.join(B,"events.jsonl")).size}catch(Y){if(Y.code==="ENOENT")return 0;throw Y}},G=(B)=>{let Y=a(B.parallelGroups,B.steps.length,B.chainStepCount??B.steps.length),W=B.currentStep!==void 0?Y.find((E)=>B.currentStep>=E.start&&B.currentStep<E.start+E.count):void 0,C=W?B.steps.slice(W.start,W.start+W.count).map((E,R)=>({...E,index:W.start+R})):B.steps.map((E,R)=>({...E,index:R}));return{asyncId:B.id,asyncDir:B.asyncDir,status:B.state,sessionId:B.sessionId,activityState:B.activityState,lastActivityAt:B.lastActivityAt,currentTool:B.currentTool,currentToolStartedAt:B.currentToolStartedAt,currentPath:B.currentPath,turnCount:B.turnCount,toolCount:B.toolCount,mode:B.mode,agents:C.map((E)=>E.agent),currentStep:B.currentStep,chainStepCount:B.chainStepCount,parallelGroups:Y,steps:C,stepsTotal:C.length,runningSteps:C.filter((E)=>E.status==="running").length,completedSteps:C.filter((E)=>E.status==="complete"||E.status==="completed").length,hasParallelGroups:Y.length>0,activeParallelGroup:Boolean(W),startedAt:B.startedAt,updatedAt:B.lastUpdate??B.startedAt,timeoutMs:B.timeoutMs,deadlineAt:B.deadlineAt,timedOut:B.timedOut,turnBudget:B.turnBudget,turnBudgetExceeded:B.turnBudgetExceeded,wrapUpRequested:B.wrapUpRequested,sessionDir:B.sessionDir,outputFile:B.outputFile,totalTokens:B.totalTokens,sessionFile:B.sessionFile,controlEventCursor:H(B.asyncDir),nestedChildren:B.nestedChildren}},V=(B)=>{let Y=Z.cleanupTimers.get(B);if(!Y)return;clearTimeout(Y),Z.cleanupTimers.delete(B)},O=(B)=>{V(B);let Y=setTimeout(()=>{if(Z.cleanupTimers.delete(B),Z.asyncJobs.delete(B),Z.lastUiContext)f(Z.lastUiContext)},q);Z.cleanupTimers.set(B,Y)},M=(B)=>{let Y=d$.join(B.asyncDir,"events.jsonl"),W;try{W=l.openSync(Y,"r")}catch(C){if(C.code==="ENOENT")return;console.error(`Failed to open async control events for '${B.asyncDir}':`,C);return}try{let C=l.fstatSync(W),E=B.controlEventCursor,R=C.size<(E??0)?0:E??0,w=E===void 0&&C.size>G0;if(w)R=C.size-G0;if(C.size<=R)return;let K=Math.min(C.size,R+G0),J$=(h)=>{if(!h.trim())return;let d;try{d=JSON.parse(h)}catch(H$){console.error(`Ignoring malformed async control event in '${Y}':`,H$);return}if(!d||typeof d!=="object"||d.type!=="subagent.control")return;let D=d;if(!D.event||!Array.isArray(D.channels))return;let t={event:D.event,source:"async",asyncDir:B.asyncDir,childIntercomTarget:D.childIntercomTarget,noticeText:D.noticeText??s0(D.event,D.childIntercomTarget)};if(D.channels.includes("event"))$.events.emit(_0,t);if(D.event.type!=="active_long_running"&&D.channels.includes("intercom")&&D.intercom?.to&&D.intercom.message)$.events.emit(K0,{...t,to:D.intercom.to,message:D.intercom.message})},x=R,m=R,c=[],T=0,o=w,V0=(h)=>{if(h.length===0||o)return;if(T+h.length>w2){c=[],T=0,o=!0;return}c.push(h),T+=h.length};while(x<K){let h=Math.min(v2,K-x),d=Buffer.alloc(h),D=l.readSync(W,d,0,h,x);if(D<=0)break;let t=D===d.length?d:d.subarray(0,D),H$=0;for(let Q$=0;Q$<t.length;Q$++){if(t[Q$]!==10)continue;if(V0(t.subarray(H$,Q$)),!o&&T>0)J$(Buffer.concat(c,T).toString("utf-8"));c=[],T=0,o=!1,m=x+Q$+1,H$=Q$+1}if(V0(t.subarray(H$)),x+=D,o)B.controlEventCursor=x}if(m>R)B.controlEventCursor=m;else if(K<C.size||w)B.controlEventCursor=K}catch(C){console.error(`Failed to read async control events for '${B.asyncDir}':`,C)}finally{l.closeSync(W)}},A=()=>{if(Z.poller)return;Z.poller=setInterval(()=>{if(Z.asyncJobs.size===0){if(Z.lastUiContext?.hasUI)f(Z.lastUiContext,[]);if(Z.poller)clearInterval(Z.poller),Z.poller=null;return}let B=!1;for(let Y of Z.asyncJobs.values()){let W=x$(Y),C=!1,E=()=>{try{O0(Y)}catch(w){C=!0,console.error(`Failed to refresh nested async descendants for '${Y.asyncDir}':`,w)}},R=()=>{try{if(Y.nestedRoute)h$(Y.nestedRoute,{resultsDir:X,kill:Q.kill,now:Q.now})}catch(w){C=!0,console.error(`Failed to refresh nested async descendants for '${Y.asyncDir}':`,w)}E()};try{M(Y),R();let K=E$(Y.asyncDir,{resultsDir:X,kill:Q.kill,now:Q.now,startedRun:{runId:Y.asyncId,pid:Y.pid,sessionId:Y.sessionId,mode:Y.mode,agents:Y.agents,chainStepCount:Y.chainStepCount,parallelGroups:Y.parallelGroups,startedAt:Y.startedAt,sessionFile:Y.sessionFile}}).status??T$(Y.asyncDir);if(K){let J$=Y.status;if(Y.status=K.state,Y.status!=="complete"&&Y.status!=="failed"&&Y.status!=="paused")V(Y.asyncId);if(Y.sessionId=K.sessionId??Y.sessionId,Y.activityState=K.activityState,Y.lastActivityAt=K.lastActivityAt??Y.lastActivityAt,Y.currentTool=K.currentTool,Y.currentToolStartedAt=K.currentToolStartedAt,Y.currentPath=K.currentPath,Y.turnCount=K.turnCount??Y.turnCount,Y.toolCount=K.toolCount??Y.toolCount,Y.mode=K.mode,Y.currentStep=K.currentStep??Y.currentStep,Y.chainStepCount=K.chainStepCount??Y.chainStepCount,Y.startedAt=K.startedAt??Y.startedAt,K.lastUpdate!==void 0)Y.updatedAt=K.lastUpdate;if(K.steps?.length){let x=a(K.parallelGroups,K.steps.length,K.chainStepCount??K.steps.length);Y.parallelGroups=x.length?x:Y.parallelGroups,Y.hasParallelGroups=x.length>0||Y.hasParallelGroups;let m=K.currentStep!==void 0?x.find((T)=>K.currentStep>=T.start&&K.currentStep<T.start+T.count):void 0,c=m?K.steps.slice(m.start,m.start+m.count).map((T,o)=>({...T,index:m.start+o})):K.steps.map((T,o)=>({...T,index:o}));if(Y.activeParallelGroup=Boolean(m),Y.agents=c.map((T)=>T.agent),Y.steps=c,E(),Y.stepsTotal=c.length,Y.runningSteps=c.filter((T)=>T.status==="running").length,Y.completedSteps=c.filter((T)=>T.status==="complete"||T.status==="completed").length,K.state==="complete")Y.completedSteps=c.length}if(Y.sessionDir=K.sessionDir??Y.sessionDir,Y.outputFile=K.outputFile??Y.outputFile,Y.totalTokens=K.totalTokens??Y.totalTokens,Y.timeoutMs=K.timeoutMs??Y.timeoutMs,Y.deadlineAt=K.deadlineAt??Y.deadlineAt,Y.timedOut=K.timedOut??Y.timedOut,Y.turnBudget=K.turnBudget??Y.turnBudget,Y.turnBudgetExceeded=K.turnBudgetExceeded??Y.turnBudgetExceeded,Y.wrapUpRequested=K.wrapUpRequested??Y.wrapUpRequested,Y.sessionFile=K.sessionFile??Y.sessionFile,(Y.status==="complete"||Y.status==="failed"||Y.status==="paused")&&!C&&!O$(Y.nestedChildren)&&(J$!==Y.status||!Z.cleanupTimers.has(Y.asyncId)))O(Y.asyncId);if(x$(Y)!==W)B=!0;continue}if(Y.status==="queued")Y.status="running",Y.updatedAt=Date.now()}catch(w){if(Y.status!=="failed")console.error(`Failed to read async status for '${Y.asyncDir}':`,w),Y.status="failed",Y.updatedAt=Date.now();if(!O$(Y.nestedChildren)&&!Z.cleanupTimers.has(Y.asyncId))O(Y.asyncId)}if(x$(Y)!==W)B=!0}if(B&&Z.lastUiContext?.hasUI)f(Z.lastUiContext)},U),Z.poller.unref?.()};return{ensurePoller:A,handleStarted:(B)=>{let Y=B;if(!Y.id)return;if(typeof Z.currentSessionId==="string"&&Y.sessionId!==Z.currentSessionId)return;let W=Date.now(),C=Y.asyncDir??d$.join(J,Y.id),E=Y.agents?.length?Y.agents:Y.chain&&Y.chain.length>0?Y.chain:Y.agent?[Y.agent]:void 0,R=a(Y.parallelGroups,Number.MAX_SAFE_INTEGER,Y.chainStepCount??Number.MAX_SAFE_INTEGER),K=R.find((x)=>x.start===0)?.count,J$=K&&K>0?E?.slice(0,K):E;if(Z.asyncJobs.set(Y.id,{asyncId:Y.id,asyncDir:C,status:"queued",pid:typeof Y.pid==="number"?Y.pid:void 0,...typeof Y.sessionId==="string"?{sessionId:Y.sessionId}:{},mode:Y.mode??(Y.chain?"chain":"single"),agents:J$,chainStepCount:Y.chainStepCount,parallelGroups:R,nestedRoute:Y.nestedRoute,stepsTotal:K??J$?.length,hasParallelGroups:R.length>0,activeParallelGroup:Boolean(K&&K>0),startedAt:W,updatedAt:W,timeoutMs:Y.timeoutMs,deadlineAt:Y.deadlineAt,turnBudget:Y.turnBudget,controlEventCursor:0}),A(),Z.lastUiContext)f(Z.lastUiContext)},handleComplete:(B)=>{let Y=B;if(typeof Z.currentSessionId==="string"&&Y.sessionId!==Z.currentSessionId)return;let W=Y.id;if(!W)return;let C=Z.asyncJobs.get(W),E=!1;if(C){if(C.status=Y.success?"complete":"failed",C.updatedAt=Date.now(),Y.asyncDir)C.asyncDir=Y.asyncDir;try{O0(C)}catch(R){E=!0,console.error(`Failed to refresh nested async descendants for '${C.asyncDir}':`,R)}}if(Z.lastUiContext)f(Z.lastUiContext);if(!E&&!O$(C?.nestedChildren))O(W)},resetJobs:(B)=>{for(let Y of Z.cleanupTimers.values())clearTimeout(Y);if(Z.cleanupTimers.clear(),Z.asyncJobs.clear(),Z.foregroundControls?.clear(),Z.lastForegroundControlId=null,Z.resultFileCoalescer.clear(),B?.hasUI)Z.lastUiContext=B,f(B,[])},restoreActiveJobs:(B)=>{if(B?.hasUI)Z.lastUiContext=B;if(!Z.currentSessionId)return;let Y;try{Y=z1(J,{states:["queued","running"],sessionId:Z.currentSessionId,resultsDir:X,kill:Q.kill,now:Q.now})}catch(W){console.error(`Failed to restore active async jobs from '${J}':`,W);return}for(let W of Y)Z.asyncJobs.set(W.id,G(W));if(Y.length===0)return;if(A(),Z.lastUiContext?.hasUI)f(Z.lastUiContext)}}}export{P8 as createAsyncJobTracker};
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { renderWidget, widgetRenderKey } from "../../tui/render.js";
|
|
4
|
+
import { formatControlNoticeMessage } from "../shared/subagent-control.js";
|
|
5
|
+
import {
|
|
6
|
+
POLL_INTERVAL_MS,
|
|
7
|
+
RESULTS_DIR,
|
|
8
|
+
SUBAGENT_CONTROL_EVENT,
|
|
9
|
+
SUBAGENT_CONTROL_INTERCOM_EVENT
|
|
10
|
+
} from "../../shared/types.js";
|
|
11
|
+
import { readStatus } from "../../shared/utils.js";
|
|
12
|
+
import { normalizeParallelGroups } from "./parallel-groups.js";
|
|
13
|
+
import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-reconciler.js";
|
|
14
|
+
import { hasLiveNestedDescendants, updateAsyncJobNestedProjection } from "../shared/nested-events.js";
|
|
15
|
+
import { listAsyncRuns } from "./async-status.js";
|
|
16
|
+
const CONTROL_EVENT_READ_CHUNK_BYTES = 64 * 1024;
|
|
17
|
+
const MAX_CONTROL_EVENT_LINE_BYTES = 1024 * 1024;
|
|
18
|
+
const CONTROL_EVENT_SCAN_WINDOW_BYTES = 2 * 1024 * 1024;
|
|
19
|
+
export function createAsyncJobTracker(pi, state, asyncDirRoot, options = {}) {
|
|
20
|
+
const completionRetentionMs = options.completionRetentionMs ?? 1e4;
|
|
21
|
+
const pollIntervalMs = options.pollIntervalMs ?? POLL_INTERVAL_MS;
|
|
22
|
+
const resultsDir = options.resultsDir ?? RESULTS_DIR;
|
|
23
|
+
const rerenderWidget = (ctx, jobs = Array.from(state.asyncJobs.values())) => {
|
|
24
|
+
renderWidget(ctx, jobs);
|
|
25
|
+
ctx.ui.requestRender?.();
|
|
26
|
+
};
|
|
27
|
+
const restoredControlEventCursor = (asyncDir) => {
|
|
28
|
+
try {
|
|
29
|
+
return fs.statSync(path.join(asyncDir, "events.jsonl")).size;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error.code === "ENOENT")
|
|
32
|
+
return 0;
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const summaryToJob = (run) => {
|
|
37
|
+
const groups = normalizeParallelGroups(run.parallelGroups, run.steps.length, run.chainStepCount ?? run.steps.length);
|
|
38
|
+
const activeGroup = run.currentStep !== undefined ? groups.find((group) => run.currentStep >= group.start && run.currentStep < group.start + group.count) : undefined;
|
|
39
|
+
const visibleSteps = activeGroup ? run.steps.slice(activeGroup.start, activeGroup.start + activeGroup.count).map((step, index) => ({ ...step, index: activeGroup.start + index })) : run.steps.map((step, index) => ({ ...step, index }));
|
|
40
|
+
return {
|
|
41
|
+
asyncId: run.id,
|
|
42
|
+
asyncDir: run.asyncDir,
|
|
43
|
+
status: run.state,
|
|
44
|
+
sessionId: run.sessionId,
|
|
45
|
+
activityState: run.activityState,
|
|
46
|
+
lastActivityAt: run.lastActivityAt,
|
|
47
|
+
currentTool: run.currentTool,
|
|
48
|
+
currentToolStartedAt: run.currentToolStartedAt,
|
|
49
|
+
currentPath: run.currentPath,
|
|
50
|
+
turnCount: run.turnCount,
|
|
51
|
+
toolCount: run.toolCount,
|
|
52
|
+
mode: run.mode,
|
|
53
|
+
agents: visibleSteps.map((step) => step.agent),
|
|
54
|
+
currentStep: run.currentStep,
|
|
55
|
+
chainStepCount: run.chainStepCount,
|
|
56
|
+
parallelGroups: groups,
|
|
57
|
+
steps: visibleSteps,
|
|
58
|
+
stepsTotal: visibleSteps.length,
|
|
59
|
+
runningSteps: visibleSteps.filter((step) => step.status === "running").length,
|
|
60
|
+
completedSteps: visibleSteps.filter((step) => step.status === "complete" || step.status === "completed").length,
|
|
61
|
+
hasParallelGroups: groups.length > 0,
|
|
62
|
+
activeParallelGroup: Boolean(activeGroup),
|
|
63
|
+
startedAt: run.startedAt,
|
|
64
|
+
updatedAt: run.lastUpdate ?? run.startedAt,
|
|
65
|
+
timeoutMs: run.timeoutMs,
|
|
66
|
+
deadlineAt: run.deadlineAt,
|
|
67
|
+
timedOut: run.timedOut,
|
|
68
|
+
turnBudget: run.turnBudget,
|
|
69
|
+
turnBudgetExceeded: run.turnBudgetExceeded,
|
|
70
|
+
wrapUpRequested: run.wrapUpRequested,
|
|
71
|
+
sessionDir: run.sessionDir,
|
|
72
|
+
outputFile: run.outputFile,
|
|
73
|
+
totalTokens: run.totalTokens,
|
|
74
|
+
sessionFile: run.sessionFile,
|
|
75
|
+
controlEventCursor: restoredControlEventCursor(run.asyncDir),
|
|
76
|
+
nestedChildren: run.nestedChildren
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
const cancelCleanup = (asyncId) => {
|
|
80
|
+
const existingTimer = state.cleanupTimers.get(asyncId);
|
|
81
|
+
if (!existingTimer)
|
|
82
|
+
return;
|
|
83
|
+
clearTimeout(existingTimer);
|
|
84
|
+
state.cleanupTimers.delete(asyncId);
|
|
85
|
+
};
|
|
86
|
+
const scheduleCleanup = (asyncId) => {
|
|
87
|
+
cancelCleanup(asyncId);
|
|
88
|
+
const timer = setTimeout(() => {
|
|
89
|
+
state.cleanupTimers.delete(asyncId);
|
|
90
|
+
state.asyncJobs.delete(asyncId);
|
|
91
|
+
if (state.lastUiContext) {
|
|
92
|
+
rerenderWidget(state.lastUiContext);
|
|
93
|
+
}
|
|
94
|
+
}, completionRetentionMs);
|
|
95
|
+
state.cleanupTimers.set(asyncId, timer);
|
|
96
|
+
};
|
|
97
|
+
const emitNewControlEvents = (job) => {
|
|
98
|
+
const eventsPath = path.join(job.asyncDir, "events.jsonl");
|
|
99
|
+
let fd;
|
|
100
|
+
try {
|
|
101
|
+
fd = fs.openSync(eventsPath, "r");
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error.code === "ENOENT")
|
|
104
|
+
return;
|
|
105
|
+
console.error(`Failed to open async control events for '${job.asyncDir}':`, error);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const stat = fs.fstatSync(fd);
|
|
110
|
+
const savedCursor = job.controlEventCursor;
|
|
111
|
+
let cursor = stat.size < (savedCursor ?? 0) ? 0 : savedCursor ?? 0;
|
|
112
|
+
const startedFromTail = savedCursor === undefined && stat.size > CONTROL_EVENT_SCAN_WINDOW_BYTES;
|
|
113
|
+
if (startedFromTail)
|
|
114
|
+
cursor = stat.size - CONTROL_EVENT_SCAN_WINDOW_BYTES;
|
|
115
|
+
if (stat.size <= cursor)
|
|
116
|
+
return;
|
|
117
|
+
const scanEnd = Math.min(stat.size, cursor + CONTROL_EVENT_SCAN_WINDOW_BYTES);
|
|
118
|
+
const handleLine = (line) => {
|
|
119
|
+
if (!line.trim())
|
|
120
|
+
return;
|
|
121
|
+
let parsed;
|
|
122
|
+
try {
|
|
123
|
+
parsed = JSON.parse(line);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(`Ignoring malformed async control event in '${eventsPath}':`, error);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!parsed || typeof parsed !== "object" || parsed.type !== "subagent.control")
|
|
129
|
+
return;
|
|
130
|
+
const record = parsed;
|
|
131
|
+
if (!record.event || !Array.isArray(record.channels))
|
|
132
|
+
return;
|
|
133
|
+
const payload = {
|
|
134
|
+
event: record.event,
|
|
135
|
+
source: "async",
|
|
136
|
+
asyncDir: job.asyncDir,
|
|
137
|
+
childIntercomTarget: record.childIntercomTarget,
|
|
138
|
+
noticeText: record.noticeText ?? formatControlNoticeMessage(record.event, record.childIntercomTarget)
|
|
139
|
+
};
|
|
140
|
+
if (record.channels.includes("event")) {
|
|
141
|
+
pi.events.emit(SUBAGENT_CONTROL_EVENT, payload);
|
|
142
|
+
}
|
|
143
|
+
if (record.event.type !== "active_long_running" && record.channels.includes("intercom") && record.intercom?.to && record.intercom.message) {
|
|
144
|
+
pi.events.emit(SUBAGENT_CONTROL_INTERCOM_EVENT, {
|
|
145
|
+
...payload,
|
|
146
|
+
to: record.intercom.to,
|
|
147
|
+
message: record.intercom.message
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
let readCursor = cursor;
|
|
152
|
+
let lastCompleteCursor = cursor;
|
|
153
|
+
let lineParts = [];
|
|
154
|
+
let lineBytes = 0;
|
|
155
|
+
let skippingOversizedLine = startedFromTail;
|
|
156
|
+
const appendLineSegment = (segment) => {
|
|
157
|
+
if (segment.length === 0 || skippingOversizedLine)
|
|
158
|
+
return;
|
|
159
|
+
if (lineBytes + segment.length > MAX_CONTROL_EVENT_LINE_BYTES) {
|
|
160
|
+
lineParts = [];
|
|
161
|
+
lineBytes = 0;
|
|
162
|
+
skippingOversizedLine = true;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
lineParts.push(segment);
|
|
166
|
+
lineBytes += segment.length;
|
|
167
|
+
};
|
|
168
|
+
while (readCursor < scanEnd) {
|
|
169
|
+
const toRead = Math.min(CONTROL_EVENT_READ_CHUNK_BYTES, scanEnd - readCursor);
|
|
170
|
+
const buffer = Buffer.alloc(toRead);
|
|
171
|
+
const bytesRead = fs.readSync(fd, buffer, 0, toRead, readCursor);
|
|
172
|
+
if (bytesRead <= 0)
|
|
173
|
+
break;
|
|
174
|
+
const chunk = bytesRead === buffer.length ? buffer : buffer.subarray(0, bytesRead);
|
|
175
|
+
let lineStart = 0;
|
|
176
|
+
for (let index = 0;index < chunk.length; index++) {
|
|
177
|
+
if (chunk[index] !== 10)
|
|
178
|
+
continue;
|
|
179
|
+
appendLineSegment(chunk.subarray(lineStart, index));
|
|
180
|
+
if (!skippingOversizedLine && lineBytes > 0) {
|
|
181
|
+
handleLine(Buffer.concat(lineParts, lineBytes).toString("utf-8"));
|
|
182
|
+
}
|
|
183
|
+
lineParts = [];
|
|
184
|
+
lineBytes = 0;
|
|
185
|
+
skippingOversizedLine = false;
|
|
186
|
+
lastCompleteCursor = readCursor + index + 1;
|
|
187
|
+
lineStart = index + 1;
|
|
188
|
+
}
|
|
189
|
+
appendLineSegment(chunk.subarray(lineStart));
|
|
190
|
+
readCursor += bytesRead;
|
|
191
|
+
if (skippingOversizedLine)
|
|
192
|
+
job.controlEventCursor = readCursor;
|
|
193
|
+
}
|
|
194
|
+
if (lastCompleteCursor > cursor)
|
|
195
|
+
job.controlEventCursor = lastCompleteCursor;
|
|
196
|
+
else if (scanEnd < stat.size || startedFromTail)
|
|
197
|
+
job.controlEventCursor = scanEnd;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error(`Failed to read async control events for '${job.asyncDir}':`, error);
|
|
200
|
+
} finally {
|
|
201
|
+
fs.closeSync(fd);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const ensurePoller = () => {
|
|
205
|
+
if (state.poller)
|
|
206
|
+
return;
|
|
207
|
+
state.poller = setInterval(() => {
|
|
208
|
+
if (state.asyncJobs.size === 0) {
|
|
209
|
+
if (state.lastUiContext?.hasUI)
|
|
210
|
+
rerenderWidget(state.lastUiContext, []);
|
|
211
|
+
if (state.poller) {
|
|
212
|
+
clearInterval(state.poller);
|
|
213
|
+
state.poller = null;
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
let widgetChanged = false;
|
|
218
|
+
for (const job of state.asyncJobs.values()) {
|
|
219
|
+
const widgetStateBefore = widgetRenderKey(job);
|
|
220
|
+
let nestedRefreshFailed = false;
|
|
221
|
+
const refreshNestedProjection = () => {
|
|
222
|
+
try {
|
|
223
|
+
updateAsyncJobNestedProjection(job);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
nestedRefreshFailed = true;
|
|
226
|
+
console.error(`Failed to refresh nested async descendants for '${job.asyncDir}':`, error);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const reconcileNestedDescendants = () => {
|
|
230
|
+
try {
|
|
231
|
+
if (job.nestedRoute)
|
|
232
|
+
reconcileNestedAsyncDescendants(job.nestedRoute, { resultsDir, kill: options.kill, now: options.now });
|
|
233
|
+
} catch (error) {
|
|
234
|
+
nestedRefreshFailed = true;
|
|
235
|
+
console.error(`Failed to refresh nested async descendants for '${job.asyncDir}':`, error);
|
|
236
|
+
}
|
|
237
|
+
refreshNestedProjection();
|
|
238
|
+
};
|
|
239
|
+
try {
|
|
240
|
+
emitNewControlEvents(job);
|
|
241
|
+
reconcileNestedDescendants();
|
|
242
|
+
const reconciliation = reconcileAsyncRun(job.asyncDir, {
|
|
243
|
+
resultsDir,
|
|
244
|
+
kill: options.kill,
|
|
245
|
+
now: options.now,
|
|
246
|
+
startedRun: {
|
|
247
|
+
runId: job.asyncId,
|
|
248
|
+
pid: job.pid,
|
|
249
|
+
sessionId: job.sessionId,
|
|
250
|
+
mode: job.mode,
|
|
251
|
+
agents: job.agents,
|
|
252
|
+
chainStepCount: job.chainStepCount,
|
|
253
|
+
parallelGroups: job.parallelGroups,
|
|
254
|
+
startedAt: job.startedAt,
|
|
255
|
+
sessionFile: job.sessionFile
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
const status = reconciliation.status ?? readStatus(job.asyncDir);
|
|
259
|
+
if (status) {
|
|
260
|
+
const previousStatus = job.status;
|
|
261
|
+
job.status = status.state;
|
|
262
|
+
if (job.status !== "complete" && job.status !== "failed" && job.status !== "paused")
|
|
263
|
+
cancelCleanup(job.asyncId);
|
|
264
|
+
job.sessionId = status.sessionId ?? job.sessionId;
|
|
265
|
+
job.activityState = status.activityState;
|
|
266
|
+
job.lastActivityAt = status.lastActivityAt ?? job.lastActivityAt;
|
|
267
|
+
job.currentTool = status.currentTool;
|
|
268
|
+
job.currentToolStartedAt = status.currentToolStartedAt;
|
|
269
|
+
job.currentPath = status.currentPath;
|
|
270
|
+
job.turnCount = status.turnCount ?? job.turnCount;
|
|
271
|
+
job.toolCount = status.toolCount ?? job.toolCount;
|
|
272
|
+
job.mode = status.mode;
|
|
273
|
+
job.currentStep = status.currentStep ?? job.currentStep;
|
|
274
|
+
job.chainStepCount = status.chainStepCount ?? job.chainStepCount;
|
|
275
|
+
job.startedAt = status.startedAt ?? job.startedAt;
|
|
276
|
+
if (status.lastUpdate !== undefined)
|
|
277
|
+
job.updatedAt = status.lastUpdate;
|
|
278
|
+
if (status.steps?.length) {
|
|
279
|
+
const groups = normalizeParallelGroups(status.parallelGroups, status.steps.length, status.chainStepCount ?? status.steps.length);
|
|
280
|
+
job.parallelGroups = groups.length ? groups : job.parallelGroups;
|
|
281
|
+
job.hasParallelGroups = groups.length > 0 || job.hasParallelGroups;
|
|
282
|
+
const activeGroup = status.currentStep !== undefined ? groups.find((group) => status.currentStep >= group.start && status.currentStep < group.start + group.count) : undefined;
|
|
283
|
+
const visibleSteps = activeGroup ? status.steps.slice(activeGroup.start, activeGroup.start + activeGroup.count).map((step, index) => ({ ...step, index: activeGroup.start + index })) : status.steps.map((step, index) => ({ ...step, index }));
|
|
284
|
+
job.activeParallelGroup = Boolean(activeGroup);
|
|
285
|
+
job.agents = visibleSteps.map((step) => step.agent);
|
|
286
|
+
job.steps = visibleSteps;
|
|
287
|
+
refreshNestedProjection();
|
|
288
|
+
job.stepsTotal = visibleSteps.length;
|
|
289
|
+
job.runningSteps = visibleSteps.filter((step) => step.status === "running").length;
|
|
290
|
+
job.completedSteps = visibleSteps.filter((step) => step.status === "complete" || step.status === "completed").length;
|
|
291
|
+
if (status.state === "complete")
|
|
292
|
+
job.completedSteps = visibleSteps.length;
|
|
293
|
+
}
|
|
294
|
+
job.sessionDir = status.sessionDir ?? job.sessionDir;
|
|
295
|
+
job.outputFile = status.outputFile ?? job.outputFile;
|
|
296
|
+
job.totalTokens = status.totalTokens ?? job.totalTokens;
|
|
297
|
+
job.timeoutMs = status.timeoutMs ?? job.timeoutMs;
|
|
298
|
+
job.deadlineAt = status.deadlineAt ?? job.deadlineAt;
|
|
299
|
+
job.timedOut = status.timedOut ?? job.timedOut;
|
|
300
|
+
job.turnBudget = status.turnBudget ?? job.turnBudget;
|
|
301
|
+
job.turnBudgetExceeded = status.turnBudgetExceeded ?? job.turnBudgetExceeded;
|
|
302
|
+
job.wrapUpRequested = status.wrapUpRequested ?? job.wrapUpRequested;
|
|
303
|
+
job.sessionFile = status.sessionFile ?? job.sessionFile;
|
|
304
|
+
if ((job.status === "complete" || job.status === "failed" || job.status === "paused") && !nestedRefreshFailed && !hasLiveNestedDescendants(job.nestedChildren) && (previousStatus !== job.status || !state.cleanupTimers.has(job.asyncId))) {
|
|
305
|
+
scheduleCleanup(job.asyncId);
|
|
306
|
+
}
|
|
307
|
+
if (widgetRenderKey(job) !== widgetStateBefore)
|
|
308
|
+
widgetChanged = true;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (job.status === "queued") {
|
|
312
|
+
job.status = "running";
|
|
313
|
+
job.updatedAt = Date.now();
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (job.status !== "failed") {
|
|
317
|
+
console.error(`Failed to read async status for '${job.asyncDir}':`, error);
|
|
318
|
+
job.status = "failed";
|
|
319
|
+
job.updatedAt = Date.now();
|
|
320
|
+
}
|
|
321
|
+
if (!hasLiveNestedDescendants(job.nestedChildren) && !state.cleanupTimers.has(job.asyncId)) {
|
|
322
|
+
scheduleCleanup(job.asyncId);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (widgetRenderKey(job) !== widgetStateBefore)
|
|
326
|
+
widgetChanged = true;
|
|
327
|
+
}
|
|
328
|
+
if (widgetChanged && state.lastUiContext?.hasUI)
|
|
329
|
+
rerenderWidget(state.lastUiContext);
|
|
330
|
+
}, pollIntervalMs);
|
|
331
|
+
state.poller.unref?.();
|
|
332
|
+
};
|
|
333
|
+
const handleStarted = (data) => {
|
|
334
|
+
const info = data;
|
|
335
|
+
if (!info.id)
|
|
336
|
+
return;
|
|
337
|
+
if (typeof state.currentSessionId === "string" && info.sessionId !== state.currentSessionId)
|
|
338
|
+
return;
|
|
339
|
+
const now = Date.now();
|
|
340
|
+
const asyncDir = info.asyncDir ?? path.join(asyncDirRoot, info.id);
|
|
341
|
+
const rawAgents = info.agents?.length ? info.agents : info.chain && info.chain.length > 0 ? info.chain : info.agent ? [info.agent] : undefined;
|
|
342
|
+
const validParallelGroups = normalizeParallelGroups(info.parallelGroups, Number.MAX_SAFE_INTEGER, info.chainStepCount ?? Number.MAX_SAFE_INTEGER);
|
|
343
|
+
const firstGroup = validParallelGroups.find((group) => group.start === 0);
|
|
344
|
+
const firstGroupCount = firstGroup?.count;
|
|
345
|
+
const agents = firstGroupCount && firstGroupCount > 0 ? rawAgents?.slice(0, firstGroupCount) : rawAgents;
|
|
346
|
+
state.asyncJobs.set(info.id, {
|
|
347
|
+
asyncId: info.id,
|
|
348
|
+
asyncDir,
|
|
349
|
+
status: "queued",
|
|
350
|
+
pid: typeof info.pid === "number" ? info.pid : undefined,
|
|
351
|
+
...typeof info.sessionId === "string" ? { sessionId: info.sessionId } : {},
|
|
352
|
+
mode: info.mode ?? (info.chain ? "chain" : "single"),
|
|
353
|
+
agents,
|
|
354
|
+
chainStepCount: info.chainStepCount,
|
|
355
|
+
parallelGroups: validParallelGroups,
|
|
356
|
+
nestedRoute: info.nestedRoute,
|
|
357
|
+
stepsTotal: firstGroupCount ?? agents?.length,
|
|
358
|
+
hasParallelGroups: validParallelGroups.length > 0,
|
|
359
|
+
activeParallelGroup: Boolean(firstGroupCount && firstGroupCount > 0),
|
|
360
|
+
startedAt: now,
|
|
361
|
+
updatedAt: now,
|
|
362
|
+
timeoutMs: info.timeoutMs,
|
|
363
|
+
deadlineAt: info.deadlineAt,
|
|
364
|
+
turnBudget: info.turnBudget,
|
|
365
|
+
controlEventCursor: 0
|
|
366
|
+
});
|
|
367
|
+
ensurePoller();
|
|
368
|
+
if (state.lastUiContext) {
|
|
369
|
+
rerenderWidget(state.lastUiContext);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
const handleComplete = (data) => {
|
|
373
|
+
const result = data;
|
|
374
|
+
if (typeof state.currentSessionId === "string" && result.sessionId !== state.currentSessionId)
|
|
375
|
+
return;
|
|
376
|
+
const asyncId = result.id;
|
|
377
|
+
if (!asyncId)
|
|
378
|
+
return;
|
|
379
|
+
const job = state.asyncJobs.get(asyncId);
|
|
380
|
+
let nestedRefreshFailed = false;
|
|
381
|
+
if (job) {
|
|
382
|
+
job.status = result.success ? "complete" : "failed";
|
|
383
|
+
job.updatedAt = Date.now();
|
|
384
|
+
if (result.asyncDir)
|
|
385
|
+
job.asyncDir = result.asyncDir;
|
|
386
|
+
try {
|
|
387
|
+
updateAsyncJobNestedProjection(job);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
nestedRefreshFailed = true;
|
|
390
|
+
console.error(`Failed to refresh nested async descendants for '${job.asyncDir}':`, error);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (state.lastUiContext) {
|
|
394
|
+
rerenderWidget(state.lastUiContext);
|
|
395
|
+
}
|
|
396
|
+
if (!nestedRefreshFailed && !hasLiveNestedDescendants(job?.nestedChildren))
|
|
397
|
+
scheduleCleanup(asyncId);
|
|
398
|
+
};
|
|
399
|
+
const resetJobs = (ctx) => {
|
|
400
|
+
for (const timer of state.cleanupTimers.values()) {
|
|
401
|
+
clearTimeout(timer);
|
|
402
|
+
}
|
|
403
|
+
state.cleanupTimers.clear();
|
|
404
|
+
state.asyncJobs.clear();
|
|
405
|
+
state.foregroundControls?.clear();
|
|
406
|
+
state.lastForegroundControlId = null;
|
|
407
|
+
state.resultFileCoalescer.clear();
|
|
408
|
+
if (ctx?.hasUI) {
|
|
409
|
+
state.lastUiContext = ctx;
|
|
410
|
+
rerenderWidget(ctx, []);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
const restoreActiveJobs = (ctx) => {
|
|
414
|
+
if (ctx?.hasUI)
|
|
415
|
+
state.lastUiContext = ctx;
|
|
416
|
+
if (!state.currentSessionId)
|
|
417
|
+
return;
|
|
418
|
+
let runs;
|
|
419
|
+
try {
|
|
420
|
+
runs = listAsyncRuns(asyncDirRoot, { states: ["queued", "running"], sessionId: state.currentSessionId, resultsDir, kill: options.kill, now: options.now });
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error(`Failed to restore active async jobs from '${asyncDirRoot}':`, error);
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
for (const run of runs) {
|
|
426
|
+
state.asyncJobs.set(run.id, summaryToJob(run));
|
|
427
|
+
}
|
|
428
|
+
if (runs.length === 0)
|
|
429
|
+
return;
|
|
430
|
+
ensurePoller();
|
|
431
|
+
if (state.lastUiContext?.hasUI)
|
|
432
|
+
rerenderWidget(state.lastUiContext);
|
|
433
|
+
};
|
|
434
|
+
return { ensurePoller, handleStarted, handleComplete, resetJobs, restoreActiveJobs };
|
|
435
|
+
}
|