@kolisachint/hoocode-agent 0.4.12 → 0.4.14
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/CHANGELOG.md +11 -0
- package/dist/core/dispatch-evaluator.d.ts +17 -0
- package/dist/core/dispatch-evaluator.d.ts.map +1 -1
- package/dist/core/dispatch-evaluator.js +44 -10
- package/dist/core/dispatch-evaluator.js.map +1 -1
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +7 -2
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/task-store.d.ts +12 -7
- package/dist/core/task-store.d.ts.map +1 -1
- package/dist/core/task-store.js +23 -15
- package/dist/core/task-store.js.map +1 -1
- package/dist/core/token-budget.d.ts.map +1 -1
- package/dist/core/token-budget.js +15 -12
- package/dist/core/token-budget.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +1 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +15 -7
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-panel.js +31 -12
- package/dist/modes/interactive/components/task-panel.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +4 -2
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent-pool.js","sourceRoot":"","sources":["../../src/core/subagent-pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAqB,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA4EhD;;;;;;;;;;GAUG;AACH,MAAM,OAAO,YAAa,SAAQ,YAAY;IAC5B,cAAc,CAAS;IACvB,UAAU,CAAS;IACnB,UAAU,CAAW;IACrB,GAAG,CAAS;IACZ,GAAG,CAAoB;IACvB,kBAAkB,CAAS;IAEpC,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;IACxC,KAAK,GAAuB,EAAE,CAAC;IAC/B,SAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC9C,OAAO,GAAG,IAAI,GAAG,EAAuF,CAAC;IACzG,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IACzC,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC;IAChC,SAAS,CAAoB;IAC7B,QAAQ,GAAG,KAAK,CAAC;IACzB,kFAAkF;IAC1E,WAAW,GAAG,IAAI,GAAG,EAAiC,CAAC;IAC/D,qEAAqE;IAC7D,UAAU,GAAG,IAAI,GAAG,EAAqD,CAAC;IAElF,YAAY,OAA4B,EAAE;QACzC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;QAC3C,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACxC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;QACtC,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAsC,EAAE,EAAE,CAAC;YACxE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC9C,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;QAAA,CAChC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAsC,EAAE,EAAE,CAAC;YACxE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC9C,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;QAAA,CAChC,CAAC,CAAC;IAAA,CACH;IAED,gDAAgD;IACxC,UAAU,CAAC,UAAkB,EAAU;QAC9C,QAAQ,UAAU,EAAE,CAAC;YACpB,KAAK,SAAS,CAAC;YACf,KAAK,QAAQ;gBACZ,OAAO,CAAC,CAAC;YACV,KAAK,KAAK;gBACT,OAAO,CAAC,CAAC;YACV;gBACC,OAAO,CAAC,CAAC;QACX,CAAC;IAAA,CACD;IAED,qDAAqD;IACrD,KAAK,CAAC,IAAsB,EAAQ;QACnC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACnD,CAAC;QACD,IACC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC;YAClD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,EAC/B,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3E,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;IAAA,CACZ;IAED,gCAAgC;IAChC,UAAU,CAAC,OAAe,EAAoE;QAC7F,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,SAAS,CAAC;QAC9C,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC;YAAE,OAAO,QAAQ,CAAC;QACnE,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,SAAS;YAAE,OAAO,SAAS,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC;YAClD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC;YAClD,IAAI,MAAM,CAAC,EAAE;gBAAE,OAAO,MAAM,CAAC;YAC7B,OAAO,QAAQ,CAAC;QACjB,CAAC;QACD,OAAO,QAAQ,CAAC;IAAA,CAChB;IAED,yDAAyD;IACzD,QAAQ,CAAC,OAAe,EAA2B;QAClD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC/B,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAAA,CAC/C,CAAC,CAAC;IAAA,CACH;IAED,6CAA6C;IAC7C,aAAa,GAAW;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IAAA,CACvB;IAED,4CAA4C;IAC5C,YAAY,GAAW;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAAA,CACzB;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,OAAO,GAAoB,EAAE,EAAuB;QAChF,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QACzD,MAAM,SAAS,GAAG,IAAI,iBAAiB,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAE1C,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC;YAC9C,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC1D,CAAC;QAED,MAAM,UAAU,GAAiB,UAAU,IAAK,QAAQ,CAAC,UAA2B,IAAI,SAAS,CAAC;QAClG,MAAM,OAAO,GAAG,YAAY,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACnF,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC9D,MAAM,UAAU,GAAG,QAAQ,CAAC,oBAAoB,CAAC;QAEjD,0EAA0E;QAC1E,gDAAgD;QAChD,MAAM,OAAO,GAAG,oBAAoB,UAAU,WAAW,MAAM,eAAe,UAAU,YAAY,OAAO,EAAE,CAAC;QAC9G,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAErE,MAAM,QAAQ,GAAqB;YAClC,OAAO;YACP,UAAU;YACV,IAAI;YACJ,OAAO;YACP,KAAK;YACL,QAAQ;YACR,GAAG,EAAE,IAAI,CAAC,GAAG;SACb,CAAC;QAEF,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAExC,OAAO;YACN,cAAc,EAAE,KAAK;YACrB,OAAO;YACP,UAAU;YACV,MAAM;YACN,MAAM;YACN,QAAQ;SACR,CAAC;IAAA,CACF;IAED;;;;;OAKG;IACH,KAAK,CAAC,aAAa,CAClB,KAA0D,EAC1D,MAAM,GAAwC,EAAE,EACxB;QACxB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YAC5D,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;QAAA,CACpE,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAAA,CAC7B;IAEO,gBAAgB,CACvB,OAAe,EACf,UAAkB,EAClB,MAAc,EACd,UAAkB,EAClB,IAAY,EACL;QACP,MAAM,GAAG,GAAG;YACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO;YACP,UAAU;YACV,MAAM;YACN,UAAU;YACV,IAAI;SACJ,CAAC;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC;QACrF,IAAI,CAAC;YACJ,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACR,0BAA0B;QAC3B,CAAC;IAAA,CACD;IAEO,eAAe,CAAC,OAAe,EAAE,MAAsB,EAAQ;QACtE,MAAM,MAAM,GAAG;YACd,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,eAAe,EAAE,MAAM,CAAC,eAAe;YACvC,WAAW,EAAE,MAAM,CAAC,WAAW;SAC/B,CAAC;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QAC/E,IAAI,CAAC;YACJ,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACR,0BAA0B;QAC3B,CAAC;IAAA,CACD;IAED,+EAA+E;IAC/E,OAAO,GAAS;QACf,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9B,CAAC;QACF,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAEhB,KAAK,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;YAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAAA,CAC1B;IAED,2DAA2D;IACnD,IAAI,GAAS;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAG,CAAC;YACjC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;IAAA,CACD;IAED,sCAAsC;IAC9B,SAAS,CAAC,IAAsB,EAAY;QACnD,MAAM,IAAI,GAAa,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEzG,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC;gBACJ,MAAM,IAAI,GAAG,IAAI,CAAC,UAA0B,CAAC;gBAC7C,MAAM,YAAY,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;gBACnD,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;gBAC3C,4EAA4E;gBAC5E,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;gBAC/B,IAAI,KAAK,EAAE,CAAC;oBACX,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBACvC,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,0CAA0C;YAC3C,CAAC;QACF,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE;YAClC,CAAC,CAAC,sCAAsC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,aAAa,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1F,CAAC,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAElB,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,kEAAkE;IAC1D,SAAS,CAAC,IAAsB,EAAE,OAAgB,EAAQ;QACjE,wEAAwE;QACxE,+CAA+C;QAC/C,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE;gBACvD,KAAK,EAAE,IAAI,CAAC,YAAY;gBACxB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG;aACzB,CAAC,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,CAAC,IAAuE,EAAE,EAAE,CAAC;gBACxG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;YAAA,CAClC,CAAC,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC;gBAClC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC1C,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBAClC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC9B,CAAC;YAAA,CACD,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QAED,IAAI,IAA8B,CAAC;QACnC,IAAI,CAAC;YACJ,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;gBACnD,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG;gBACzB,uEAAuE;gBACvE,yCAAyC;gBACzC,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,sBAAsB,EAAE,GAAG,EAAE;gBACjD,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;aACjC,CAAC,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACR,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;oBACxB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,KAAK,EAAE,4BAA4B;iBACnC,CAAC,CAAC;gBACH,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE;oBAChC,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,EAAE,EAAE,KAAK;oBACT,MAAM,EAAE,EAAE;oBACV,MAAM,EAAE,EAAE;oBACV,SAAS,EAAE,IAAI;oBACf,KAAK,EAAE,4BAA4B;oBACnC,MAAM,EAAE,QAAQ;iBAChB,CAAC,CAAC;gBACH,IAAI,CAAC,IAAI,EAAE,CAAC;YACb,CAAC;YACD,OAAO;QACR,CAAC;QAED,MAAM,IAAI,GAAiB;YAC1B,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;YAClB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;YACtB,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,kBAAkB;YAC1D,OAAO,EAAE,IAAI;SACb,CAAC;QAEF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAE5D,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC;YAChB,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAE5B,yDAAyD;YACzD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;gBACxB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,SAAS;gBACpC,IAAI,CAAC;oBACJ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;oBAC3D,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;wBAC1B,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAC9C,CAAC;gBACF,CAAC;gBAAC,MAAM,CAAC;oBACR,0BAA0B;gBAC3B,CAAC;YACF,CAAC;QAAA,CACD,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;YACzC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAAA,CAC1B,CAAC,CAAC;QAEH,mBAAmB,CAAC,IAAI,CAAC;aACvB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAChC,MAAM,CAAC,KAAK,EAAE,CAAC;YAEf,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACtD,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAEtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;YAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACrC,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAE3C,iDAAiD;YACjD,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC1D,MAAM,MAAM,GAAmB;oBAC9B,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,EAAE,EAAE,KAAK;oBACT,MAAM;oBACN,MAAM;oBACN,SAAS,EAAE,IAAI;oBACf,MAAM,EAAE,UAAU;iBAClB,CAAC;gBACF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC3C,IAAI,CAAC,IAAI,CAAC,QAAQ,UAAU,EAAE,EAAE;oBAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,QAAQ;oBACR,WAAW;iBACX,CAAC,CAAC;gBACH,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACzC,OAAO;YACR,CAAC;YAED,MAAM,MAAM,GAAmB;gBAC9B,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,EAAE,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,cAAc;gBACjC,MAAM;gBACN,MAAM;gBACN,SAAS,EAAE,IAAI;gBACf,eAAe,EAAE,cAAc;gBAC/B,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ;aAC7D,CAAC;YAEF,IAAI,cAAc,EAAE,CAAC;gBACpB,4DAA4D;gBAC5D,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC9E,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAClD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;gBAChC,IAAI,UAAU,EAAE,CAAC;oBAChB,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,0CAA0C;gBAC7D,CAAC;gBACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC3C,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;oBACtB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,QAAQ;oBACR,WAAW;oBACX,MAAM,EAAE,SAAS;iBACjB,CAAC,CAAC;gBACH,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACzC,OAAO;YACR,CAAC;YAED,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC9E,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;oBACzB,MAAM,CAAC,EAAE,GAAG,KAAK,CAAC;oBAClB,MAAM,CAAC,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC;oBACnC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;oBACzB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBAC3C,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;wBACxB,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,UAAU,EAAE,IAAI,CAAC,UAAU;wBAC3B,QAAQ;wBACR,WAAW;wBACX,KAAK,EAAE,YAAY,CAAC,MAAM;qBAC1B,CAAC,CAAC;oBACH,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBACzC,OAAO;gBACR,CAAC;gBACD,kEAAkE;gBAClE,wCAAwC;gBACxC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;YACjF,CAAC;YAED,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAE3C,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;oBACtB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,QAAQ;oBACR,WAAW;oBACX,MAAM,EAAE,UAAU;iBAClB,CAAC,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;oBACxB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,QAAQ;oBACR,WAAW;oBACX,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,oBAAoB,IAAI,EAAE;iBACjD,CAAC,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAAA,CACzC,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAChC,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;YAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC3B,OAAO;YACR,CAAC;YACD,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAmB;gBAC9B,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,EAAE,EAAE,KAAK;gBACT,MAAM;gBACN,MAAM;gBACN,SAAS,EAAE,IAAI;gBACf,KAAK;gBACL,MAAM,EAAE,QAAQ;aAChB,CAAC;YACF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC3C,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;gBACxB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,QAAQ;gBACR,WAAW;gBACX,KAAK;aACL,CAAC,CAAC;YACH,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAAA,CACzC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE,CAAC;YACd,MAAM,CAAC,kBAAkB,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,CAAC,IAAI,EAAE,CAAC;QAAA,CACZ,CAAC,CAAC;IAAA,CACJ;IAEO,iBAAiB,CAAC,OAAe,EAAE,GAAW,EAAuC;QAC5F,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QAC1E,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QACxC,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACxC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,SAAS,CAAC;QAClB,CAAC;IAAA,CACD;IAEO,aAAa,CAAC,OAAe,EAAE,MAAsB,EAAQ;QACpE,mFAAmF;QACnF,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;aACpE,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;aACzE,IAAI,MAAM,CAAC,EAAE;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;;YACpD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAE5C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,MAAM,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC7B,OAAO;QACR,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAAA,CACpC;CACD","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../config.js\";\nimport { waitForChildProcess } from \"../utils/child-process.js\";\nimport { DispatchEvaluator } from \"./dispatch-evaluator.js\";\nimport { SubagentLifeguard } from \"./lifeguard.js\";\nimport { OutputVerifier } from \"./output-verifier.js\";\nimport { getSubagentSystemPrompt, MODE_TOOLS, type SubagentMode } from \"./subagent.js\";\nimport { TokenBudget } from \"./token-budget.js\";\n\nexport interface SubagentPoolTask {\n\ttask_id: string;\n\tagent_type: SubagentMode | string;\n\ttask: string;\n\tcontext?: string;\n\ttoken_budget?: number;\n\tcwd?: string;\n\tmodel?: string;\n\tprovider?: string;\n}\n\nexport interface SubagentSlot {\n\tpid: number;\n\tagent_type: string;\n\ttask_id: string;\n\tspawned_at: number;\n\ttoken_budget: number;\n\tprocess: ReturnType<typeof spawn>;\n}\n\nexport interface SubagentResult {\n\ttask_id: string;\n\tok: boolean;\n\tstdout: string;\n\tstderr: string;\n\texit_code: number | null;\n\terror?: string;\n\t/** True when the task exceeded its token budget and was hard-stopped. */\n\tbudget_exceeded?: boolean;\n\t/** Terminal status derived from how the task finished. */\n\tstatus?: \"complete\" | \"partial\" | \"failed\" | \"stalled\" | \"timeout\";\n\t/** Parsed result.json content when available (e.g. on partial completion). */\n\tresult_data?: Record<string, unknown>;\n}\n\nexport interface TaskResult {\n\t/** True when the evaluator decided the task is simple enough for inline handling. */\n\thandled_inline: boolean;\n\t/** Present when the task was delegated. */\n\ttask_id?: string;\n\tagent_type?: string;\n\treason?: string;\n\t/** Subagent result when delegated. */\n\tresult?: SubagentResult;\n\t/** Duration in milliseconds when delegated. */\n\tduration?: number;\n}\n\nexport interface DispatchOptions {\n\t/** Skip evaluation and force this agent type (user/explicit override). */\n\tforceAgent?: SubagentMode;\n\t/** Context distilled from the calling agent, passed to the subagent. */\n\tcontext?: string;\n\t/** Model id for the subagent (defaults to the child's configured default). */\n\tmodel?: string;\n\t/** Provider for the subagent. */\n\tprovider?: string;\n}\n\nexport interface SubagentPoolOptions {\n\t/** Path to the hoocode executable (or the runtime, e.g. node, when prefixArgs is set). */\n\texecutable: string;\n\t/** Args inserted before task args (e.g. the CLI entry script for node/tsx). */\n\tprefixArgs?: string[];\n\t/** Maximum concurrent child processes. Defaults to 5. */\n\tmaxConcurrency?: number;\n\t/** Working directory for spawned processes. Defaults to process.cwd(). */\n\tcwd?: string;\n\t/** Environment variables. Defaults to process.env. */\n\tenv?: NodeJS.ProcessEnv;\n\t/** Default token budget per task. Defaults to 0. */\n\tdefaultTokenBudget?: number;\n}\n\n/**\n * Pool for running hoocode subagents as child processes with bounded concurrency,\n * FIFO queuing with priority support, and automatic slot refill.\n *\n * Events:\n * - \"task_done\" – task completed successfully and output was verified\n * - \"task_failed\" – task failed (spawn error, bad exit code, verification failure)\n * - \"task_stalled\" – heartbeat missed for 60s, process was SIGKILLed\n * - \"task_timeout\" – hard timeout exceeded, process was SIGKILLed\n * - \"budget_warning\" – token usage crossed 80% threshold\n */\nexport class SubagentPool extends EventEmitter {\n\tprivate readonly maxConcurrency: number;\n\tprivate readonly executable: string;\n\tprivate readonly prefixArgs: string[];\n\tprivate readonly cwd: string;\n\tprivate readonly env: NodeJS.ProcessEnv;\n\tprivate readonly defaultTokenBudget: number;\n\n\tprivate slots = new Map<string, SubagentSlot>();\n\tprivate queue: SubagentPoolTask[] = [];\n\tprivate completed = new Map<string, SubagentResult>();\n\tprivate waiters = new Map<string, { resolve: (result: SubagentResult) => void; reject: (err: Error) => void }>();\n\tprivate budgets = new Map<string, TokenBudget>();\n\tprivate verifier = new OutputVerifier();\n\tprivate lifeguard: SubagentLifeguard;\n\tprivate disposed = false;\n\t/** Tracks why a task was killed (stalled / timeout) before exit handler fires. */\n\tprivate killReasons = new Map<string, \"stalled\" | \"timeout\">();\n\t/** Persistent terminal status map, survives wait_for consumption. */\n\tprivate taskStatus = new Map<string, \"done\" | \"failed\" | \"stalled\" | \"timeout\">();\n\n\tconstructor(options: SubagentPoolOptions) {\n\t\tsuper();\n\t\tthis.maxConcurrency = options.maxConcurrency ?? 5;\n\t\tthis.executable = options.executable;\n\t\tthis.prefixArgs = options.prefixArgs ?? [];\n\t\tthis.cwd = options.cwd ?? process.cwd();\n\t\tthis.env = options.env ?? process.env;\n\t\tthis.defaultTokenBudget = options.defaultTokenBudget ?? 0;\n\t\tthis.verifier = new OutputVerifier(this.cwd);\n\t\tthis.lifeguard = new SubagentLifeguard(this.cwd);\n\t\tthis.lifeguard.on(\"stalled\", (data: { task_id: string; pid: number }) => {\n\t\t\tthis.killReasons.set(data.task_id, \"stalled\");\n\t\t\tthis.emit(\"task_stalled\", data);\n\t\t});\n\t\tthis.lifeguard.on(\"timeout\", (data: { task_id: string; pid: number }) => {\n\t\t\tthis.killReasons.set(data.task_id, \"timeout\");\n\t\t\tthis.emit(\"task_timeout\", data);\n\t\t});\n\t}\n\n\t/** Priority value: higher numbers run first. */\n\tprivate priorityOf(agent_type: string): number {\n\t\tswitch (agent_type) {\n\t\t\tcase \"explore\":\n\t\t\tcase \"review\":\n\t\t\t\treturn 2;\n\t\t\tcase \"doc\":\n\t\t\t\treturn 0;\n\t\t\tdefault:\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\n\t/** Queue a task. It will run when a slot is free. */\n\tspawn(task: SubagentPoolTask): void {\n\t\tif (this.disposed) {\n\t\t\tthrow new Error(\"SubagentPool has been disposed\");\n\t\t}\n\t\tif (\n\t\t\tthis.slots.has(task.task_id) ||\n\t\t\tthis.queue.some((t) => t.task_id === task.task_id) ||\n\t\t\tthis.completed.has(task.task_id)\n\t\t) {\n\t\t\tthrow new Error(`Duplicate task_id: ${task.task_id}`);\n\t\t}\n\n\t\tconst p = this.priorityOf(task.agent_type);\n\t\tconst idx = this.queue.findIndex((t) => this.priorityOf(t.agent_type) < p);\n\t\tif (idx === -1) {\n\t\t\tthis.queue.push(task);\n\t\t} else {\n\t\t\tthis.queue.splice(idx, 0, task);\n\t\t}\n\t\tthis.pull();\n\t}\n\n\t/** Current status of a task. */\n\tget_status(task_id: string): \"running\" | \"queued\" | \"done\" | \"failed\" | \"stalled\" | \"timeout\" {\n\t\tif (this.slots.has(task_id)) return \"running\";\n\t\tif (this.queue.some((t) => t.task_id === task_id)) return \"queued\";\n\t\tconst persisted = this.taskStatus.get(task_id);\n\t\tif (persisted) return persisted;\n\t\tconst result = this.completed.get(task_id);\n\t\tif (result) {\n\t\t\tif (result.status === \"stalled\") return \"stalled\";\n\t\t\tif (result.status === \"timeout\") return \"timeout\";\n\t\t\tif (result.ok) return \"done\";\n\t\t\treturn \"failed\";\n\t\t}\n\t\treturn \"failed\";\n\t}\n\n\t/** Wait for a task to complete and return its result. */\n\twait_for(task_id: string): Promise<SubagentResult> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(new Error(\"SubagentPool has been disposed\"));\n\t\t}\n\n\t\tconst existing = this.completed.get(task_id);\n\t\tif (existing) {\n\t\t\tthis.completed.delete(task_id);\n\t\t\treturn Promise.resolve(existing);\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.waiters.set(task_id, { resolve, reject });\n\t\t});\n\t}\n\n\t/** Number of currently running subagents. */\n\trunning_count(): number {\n\t\treturn this.slots.size;\n\t}\n\n\t/** Number of tasks waiting in the queue. */\n\tqueued_count(): number {\n\t\treturn this.queue.length;\n\t}\n\n\t/**\n\t * Dispatch a task through the evaluator.\n\t *\n\t * - If `options.forceAgent` is provided, skip evaluation and spawn directly.\n\t * - Otherwise evaluate the task. If it should be handled inline, return\n\t * `{ handled_inline: true }` immediately.\n\t * - If delegating, spawn the subagent, wait for completion, write\n\t * `output.json`, and return the result.\n\t */\n\tasync dispatch(task: string, options: DispatchOptions = {}): Promise<TaskResult> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(new Error(\"SubagentPool has been disposed\"));\n\t\t}\n\n\t\tconst { forceAgent, context, model, provider } = options;\n\t\tconst evaluator = new DispatchEvaluator();\n\t\tconst analysis = evaluator.evaluate(task);\n\n\t\tif (!forceAgent && !analysis.should_delegate) {\n\t\t\treturn { handled_inline: true, reason: analysis.reason };\n\t\t}\n\n\t\tconst agent_type: SubagentMode = forceAgent ?? (analysis.agent_type as SubagentMode) ?? \"explore\";\n\t\tconst task_id = `dispatch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\t\tconst reason = forceAgent ? \"user_override\" : analysis.reason;\n\t\tconst complexity = analysis.estimated_complexity;\n\n\t\t// Pre-dispatch logging. Use stderr: stdout is reserved for the JSON event\n\t\t// stream / TUI render and must not be polluted.\n\t\tconst logLine = `[DISPATCH] agent=${agent_type} reason=${reason} complexity=${complexity} task_id=${task_id}`;\n\t\tconsole.error(logLine);\n\t\tthis.writeDispatchLog(task_id, agent_type, reason, complexity, task);\n\n\t\tconst poolTask: SubagentPoolTask = {\n\t\t\ttask_id,\n\t\t\tagent_type,\n\t\t\ttask,\n\t\t\tcontext,\n\t\t\tmodel,\n\t\t\tprovider,\n\t\t\tcwd: this.cwd,\n\t\t};\n\n\t\tconst startTime = Date.now();\n\t\tthis.spawn(poolTask);\n\t\tconst result = await this.wait_for(task_id);\n\t\tconst duration = Date.now() - startTime;\n\n\t\treturn {\n\t\t\thandled_inline: false,\n\t\t\ttask_id,\n\t\t\tagent_type,\n\t\t\treason,\n\t\t\tresult,\n\t\t\tduration,\n\t\t};\n\t}\n\n\t/**\n\t * Dispatch a batch of subtasks concurrently.\n\t *\n\t * Spawns up to `maxConcurrency` at once; overflow is queued with FIFO.\n\t * Returns aggregated results in the same order as the input.\n\t */\n\tasync dispatchBatch(\n\t\ttasks: Array<{ agent_type: SubagentMode; prompt: string }>,\n\t\tshared: Omit<DispatchOptions, \"forceAgent\"> = {},\n\t): Promise<TaskResult[]> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(new Error(\"SubagentPool has been disposed\"));\n\t\t}\n\n\t\tconst promises = tasks.map(async ({ agent_type, prompt }) => {\n\t\t\treturn this.dispatch(prompt, { ...shared, forceAgent: agent_type });\n\t\t});\n\n\t\treturn Promise.all(promises);\n\t}\n\n\tprivate writeDispatchLog(\n\t\ttask_id: string,\n\t\tagent_type: string,\n\t\treason: string,\n\t\tcomplexity: string,\n\t\ttask: string,\n\t): void {\n\t\tconst log = {\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\ttask_id,\n\t\t\tagent_type,\n\t\t\treason,\n\t\t\tcomplexity,\n\t\t\ttask,\n\t\t};\n\t\tconst path = join(this.cwd, CONFIG_DIR_NAME, \"agents\", task_id, \"dispatch-log.json\");\n\t\ttry {\n\t\t\tmkdirSync(dirname(path), { recursive: true });\n\t\t\twriteFileSync(path, JSON.stringify(log, null, 2));\n\t\t} catch {\n\t\t\t// Best-effort persistence\n\t\t}\n\t}\n\n\tprivate writeOutputJson(task_id: string, result: SubagentResult): void {\n\t\tconst output = {\n\t\t\ttask_id: result.task_id,\n\t\t\tok: result.ok,\n\t\t\texit_code: result.exit_code,\n\t\t\tstatus: result.status,\n\t\t\tstdout: result.stdout,\n\t\t\tstderr: result.stderr,\n\t\t\terror: result.error,\n\t\t\tbudget_exceeded: result.budget_exceeded,\n\t\t\tresult_data: result.result_data,\n\t\t};\n\t\tconst path = join(this.cwd, CONFIG_DIR_NAME, \"agents\", task_id, \"output.json\");\n\t\ttry {\n\t\t\tmkdirSync(dirname(path), { recursive: true });\n\t\t\twriteFileSync(path, JSON.stringify(output, null, 2));\n\t\t} catch {\n\t\t\t// Best-effort persistence\n\t\t}\n\t}\n\n\t/** Kill all running processes, clear the queue, and reject pending waiters. */\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\n\t\tfor (const slot of this.slots.values()) {\n\t\t\tif (!slot.process.killed) {\n\t\t\t\tslot.process.kill(\"SIGTERM\");\n\t\t\t}\n\t\t}\n\t\tthis.slots.clear();\n\t\tthis.queue = [];\n\n\t\tfor (const [task_id, waiter] of this.waiters) {\n\t\t\twaiter.reject(new Error(\"SubagentPool disposed\"));\n\t\t\tthis.waiters.delete(task_id);\n\t\t}\n\t\tthis.completed.clear();\n\t\tfor (const budget of this.budgets.values()) {\n\t\t\tbudget.removeAllListeners();\n\t\t}\n\t\tthis.budgets.clear();\n\t\tthis.killReasons.clear();\n\t\tthis.taskStatus.clear();\n\t\tthis.lifeguard.dispose();\n\t\tthis.removeAllListeners();\n\t}\n\n\t/** Pull tasks from the queue while slots are available. */\n\tprivate pull(): void {\n\t\twhile (this.slots.size < this.maxConcurrency && this.queue.length > 0) {\n\t\t\tconst task = this.queue.shift()!;\n\t\t\tthis.startTask(task, false);\n\t\t}\n\t}\n\n\t/** Build CLI arguments for a task. */\n\tprivate buildArgs(task: SubagentPoolTask): string[] {\n\t\tconst args: string[] = [...this.prefixArgs, \"--mode\", \"json\", \"--no-session\", \"--task-id\", task.task_id];\n\n\t\tif (task.agent_type) {\n\t\t\ttry {\n\t\t\t\tconst mode = task.agent_type as SubagentMode;\n\t\t\t\tconst systemPrompt = getSubagentSystemPrompt(mode);\n\t\t\t\targs.push(\"--system-prompt\", systemPrompt);\n\t\t\t\t// Enforce the per-mode tool allowlist so read-only modes cannot edit/write.\n\t\t\t\tconst tools = MODE_TOOLS[mode];\n\t\t\t\tif (tools) {\n\t\t\t\t\targs.push(\"--tools\", tools.join(\",\"));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unknown mode, skip custom system prompt\n\t\t\t}\n\t\t}\n\n\t\tif (task.model) {\n\t\t\targs.push(\"--model\", task.model);\n\t\t}\n\t\tif (task.provider) {\n\t\t\targs.push(\"--provider\", task.provider);\n\t\t}\n\n\t\tconst prompt = task.context?.trim()\n\t\t\t? `Context from the calling agent:\\n\\n${task.context.trim()}\\n\\nTask: ${task.task.trim()}`\n\t\t\t: `Task: ${task.task.trim()}`;\n\t\targs.push(prompt);\n\n\t\treturn args;\n\t}\n\n\t/** Start a task in a child process, with one retry on failure. */\n\tprivate startTask(task: SubagentPoolTask, isRetry: boolean): void {\n\t\t// Get or create a TokenBudget tracker. On retry, reuse the existing one\n\t\t// so cumulative usage persists across retries.\n\t\tlet budget = this.budgets.get(task.task_id);\n\t\tif (!budget) {\n\t\t\tbudget = new TokenBudget(task.task_id, task.agent_type, {\n\t\t\t\tlimit: task.token_budget,\n\t\t\t\tcwd: task.cwd ?? this.cwd,\n\t\t\t});\n\t\t\tbudget.on(\"budget_warning\", (data: { task_id: string; message: string; used: number; limit: number }) => {\n\t\t\t\tthis.emit(\"budget_warning\", data);\n\t\t\t});\n\t\t\tbudget.on(\"budget_exceeded\", () => {\n\t\t\t\tconst slot = this.slots.get(task.task_id);\n\t\t\t\tif (slot && !slot.process.killed) {\n\t\t\t\t\tslot.process.kill(\"SIGTERM\");\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.budgets.set(task.task_id, budget);\n\t\t}\n\n\t\tlet proc: ReturnType<typeof spawn>;\n\t\ttry {\n\t\t\tproc = spawn(this.executable, this.buildArgs(task), {\n\t\t\t\tcwd: task.cwd ?? this.cwd,\n\t\t\t\t// Mark the child as a subagent so its own DispatchEvaluator refuses to\n\t\t\t\t// spawn further subagents (depth guard).\n\t\t\t\tenv: { ...this.env, HOOCODE_SUBAGENT_DEPTH: \"1\" },\n\t\t\t\tshell: false,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\t\t} catch {\n\t\t\tif (!isRetry) {\n\t\t\t\tthis.startTask(task, true);\n\t\t\t} else {\n\t\t\t\tthis.emit(\"task_failed\", {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\terror: \"Spawn failed synchronously\",\n\t\t\t\t});\n\t\t\t\tthis.resolveWaiter(task.task_id, {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\tok: false,\n\t\t\t\t\tstdout: \"\",\n\t\t\t\t\tstderr: \"\",\n\t\t\t\t\texit_code: null,\n\t\t\t\t\terror: \"Spawn failed synchronously\",\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t});\n\t\t\t\tthis.pull();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst slot: SubagentSlot = {\n\t\t\tpid: proc.pid ?? 0,\n\t\t\tagent_type: task.agent_type,\n\t\t\ttask_id: task.task_id,\n\t\t\tspawned_at: Date.now(),\n\t\t\ttoken_budget: task.token_budget ?? this.defaultTokenBudget,\n\t\t\tprocess: proc,\n\t\t};\n\n\t\tthis.slots.set(task.task_id, slot);\n\t\tthis.lifeguard.monitor(task.task_id, task.agent_type, proc);\n\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\n\t\tproc.stdout?.on(\"data\", (data: Buffer) => {\n\t\t\tconst chunk = data.toString();\n\t\t\tstdout += chunk;\n\t\t\tbudget.processStdout(chunk);\n\n\t\t\t// Heartbeat detection: look for {\"ping\":true} JSON lines\n\t\t\tfor (const raw of chunk.split(\"\\n\")) {\n\t\t\t\tconst line = raw.trim();\n\t\t\t\tif (!line.startsWith(\"{\")) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(line) as Record<string, unknown>;\n\t\t\t\t\tif (parsed.ping === true) {\n\t\t\t\t\t\tthis.lifeguard.recordHeartbeat(task.task_id);\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Not a ping line, ignore\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tproc.stderr?.on(\"data\", (data: Buffer) => {\n\t\t\tstderr += data.toString();\n\t\t});\n\n\t\twaitForChildProcess(proc)\n\t\t\t.then((code) => {\n\t\t\t\tthis.slots.delete(task.task_id);\n\t\t\t\tbudget.flush();\n\n\t\t\t\tconst killReason = this.killReasons.get(task.task_id);\n\t\t\t\tthis.killReasons.delete(task.task_id);\n\n\t\t\t\tconst duration = Date.now() - slot.spawned_at;\n\t\t\t\tconst tokens_used = budget.getUsed();\n\t\t\t\tconst budgetExceeded = budget.isExceeded();\n\n\t\t\t\t// If killed by lifeguard, override exit handling\n\t\t\t\tif (killReason === \"stalled\" || killReason === \"timeout\") {\n\t\t\t\t\tconst result: SubagentResult = {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\tstdout,\n\t\t\t\t\t\tstderr,\n\t\t\t\t\t\texit_code: code,\n\t\t\t\t\t\tstatus: killReason,\n\t\t\t\t\t};\n\t\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\t\t\t\t\tthis.emit(`task_${killReason}`, {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\tduration,\n\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t});\n\t\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst result: SubagentResult = {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\tok: code === 0 && !budgetExceeded,\n\t\t\t\t\tstdout,\n\t\t\t\t\tstderr,\n\t\t\t\t\texit_code: code,\n\t\t\t\t\tbudget_exceeded: budgetExceeded,\n\t\t\t\t\tstatus: code === 0 && !budgetExceeded ? \"complete\" : \"failed\",\n\t\t\t\t};\n\n\t\t\t\tif (budgetExceeded) {\n\t\t\t\t\t// Force-return whatever exists in result.json, mark partial\n\t\t\t\t\tconst resultData = this.tryReadResultJson(task.task_id, task.cwd ?? this.cwd);\n\t\t\t\t\tresult.status = resultData ? \"partial\" : \"failed\";\n\t\t\t\t\tresult.result_data = resultData;\n\t\t\t\t\tif (resultData) {\n\t\t\t\t\t\tresult.ok = true; // partial is considered success with data\n\t\t\t\t\t}\n\t\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\t\t\t\t\tthis.emit(\"task_done\", {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\tduration,\n\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t\tstatus: \"partial\",\n\t\t\t\t\t});\n\t\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (result.ok) {\n\t\t\t\t\tconst verification = this.verifier.verify(task.task_id, task.cwd ?? this.cwd);\n\t\t\t\t\tif (!verification.valid) {\n\t\t\t\t\t\tresult.ok = false;\n\t\t\t\t\t\tresult.error = verification.reason;\n\t\t\t\t\t\tresult.status = \"failed\";\n\t\t\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\t\t\t\t\t\tthis.emit(\"task_failed\", {\n\t\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t\t\terror: verification.reason,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// Attach the verified result.json so callers can read the summary\n\t\t\t\t\t// without parsing the raw event stream.\n\t\t\t\t\tresult.result_data = this.tryReadResultJson(task.task_id, task.cwd ?? this.cwd);\n\t\t\t\t}\n\n\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\n\t\t\t\tif (result.ok) {\n\t\t\t\t\tthis.emit(\"task_done\", {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\tduration,\n\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.emit(\"task_failed\", {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\tduration,\n\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t\terror: result.error ?? `Exited with code ${code}`,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\tthis.slots.delete(task.task_id);\n\t\t\t\tbudget.flush();\n\t\t\t\tconst duration = Date.now() - slot.spawned_at;\n\t\t\t\tconst tokens_used = budget.getUsed();\n\t\t\t\tif (!isRetry) {\n\t\t\t\t\tthis.startTask(task, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst error = err instanceof Error ? err.message : String(err);\n\t\t\t\tconst result: SubagentResult = {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\tok: false,\n\t\t\t\t\tstdout,\n\t\t\t\t\tstderr,\n\t\t\t\t\texit_code: null,\n\t\t\t\t\terror,\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t};\n\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\t\t\t\tthis.emit(\"task_failed\", {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\tduration,\n\t\t\t\t\ttokens_used,\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tbudget.removeAllListeners();\n\t\t\t\tthis.budgets.delete(task.task_id);\n\t\t\t\tthis.pull();\n\t\t\t});\n\t}\n\n\tprivate tryReadResultJson(task_id: string, cwd: string): Record<string, unknown> | undefined {\n\t\tconst path = join(cwd, CONFIG_DIR_NAME, \"agents\", task_id, \"result.json\");\n\t\tif (!existsSync(path)) return undefined;\n\t\ttry {\n\t\t\tconst raw = readFileSync(path, \"utf-8\");\n\t\t\treturn JSON.parse(raw) as Record<string, unknown>;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate resolveWaiter(task_id: string, result: SubagentResult): void {\n\t\t// Persist terminal status for get_status() even after wait_for consumes the result\n\t\tif (result.status === \"stalled\") this.taskStatus.set(task_id, \"stalled\");\n\t\telse if (result.status === \"timeout\") this.taskStatus.set(task_id, \"timeout\");\n\t\telse if (result.ok) this.taskStatus.set(task_id, \"done\");\n\t\telse this.taskStatus.set(task_id, \"failed\");\n\n\t\tconst waiter = this.waiters.get(task_id);\n\t\tif (waiter) {\n\t\t\twaiter.resolve(result);\n\t\t\tthis.waiters.delete(task_id);\n\t\t\treturn;\n\t\t}\n\t\tthis.completed.set(task_id, result);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"subagent-pool.js","sourceRoot":"","sources":["../../src/core/subagent-pool.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../config.js\";\nimport { waitForChildProcess } from \"../utils/child-process.js\";\nimport { DispatchEvaluator } from \"./dispatch-evaluator.js\";\nimport { SubagentLifeguard } from \"./lifeguard.js\";\nimport { OutputVerifier } from \"./output-verifier.js\";\nimport { getSubagentSystemPrompt, MODE_TOOLS, type SubagentMode } from \"./subagent.js\";\nimport { TokenBudget } from \"./token-budget.js\";\n\nexport interface SubagentPoolTask {\n\ttask_id: string;\n\tagent_type: SubagentMode | string;\n\ttask: string;\n\tcontext?: string;\n\ttoken_budget?: number;\n\tcwd?: string;\n\tmodel?: string;\n\tprovider?: string;\n}\n\nexport interface SubagentSlot {\n\tpid: number;\n\tagent_type: string;\n\ttask_id: string;\n\tspawned_at: number;\n\ttoken_budget: number;\n\tprocess: ReturnType<typeof spawn>;\n}\n\nexport interface SubagentResult {\n\ttask_id: string;\n\tok: boolean;\n\tstdout: string;\n\tstderr: string;\n\texit_code: number | null;\n\terror?: string;\n\t/** True when the task exceeded its token budget and was hard-stopped. */\n\tbudget_exceeded?: boolean;\n\t/** Terminal status derived from how the task finished. */\n\tstatus?: \"complete\" | \"partial\" | \"failed\" | \"stalled\" | \"timeout\";\n\t/** Parsed result.json content when available (e.g. on partial completion). */\n\tresult_data?: Record<string, unknown>;\n}\n\nexport interface TaskResult {\n\t/** True when the evaluator decided the task is simple enough for inline handling. */\n\thandled_inline: boolean;\n\t/** Present when the task was delegated. */\n\ttask_id?: string;\n\tagent_type?: string;\n\treason?: string;\n\t/** Subagent result when delegated. */\n\tresult?: SubagentResult;\n\t/** Duration in milliseconds when delegated. */\n\tduration?: number;\n}\n\nexport interface DispatchOptions {\n\t/** Skip evaluation and force this agent type (user/explicit override). */\n\tforceAgent?: SubagentMode;\n\t/** Context distilled from the calling agent, passed to the subagent. */\n\tcontext?: string;\n\t/** Model id for the subagent (defaults to the child's configured default). */\n\tmodel?: string;\n\t/** Provider for the subagent. */\n\tprovider?: string;\n}\n\nexport interface SubagentPoolOptions {\n\t/** Path to the hoocode executable (or the runtime, e.g. node, when prefixArgs is set). */\n\texecutable: string;\n\t/** Args inserted before task args (e.g. the CLI entry script for node/tsx). */\n\tprefixArgs?: string[];\n\t/** Maximum concurrent child processes. Defaults to 5. */\n\tmaxConcurrency?: number;\n\t/** Working directory for spawned processes. Defaults to process.cwd(). */\n\tcwd?: string;\n\t/** Environment variables. Defaults to process.env. */\n\tenv?: NodeJS.ProcessEnv;\n\t/** Default token budget per task. Defaults to 0. */\n\tdefaultTokenBudget?: number;\n}\n\n/**\n * Pool for running hoocode subagents as child processes with bounded concurrency,\n * FIFO queuing with priority support, and automatic slot refill.\n *\n * Events:\n * - \"task_done\" – task completed successfully and output was verified\n * - \"task_failed\" – task failed (spawn error, bad exit code, verification failure)\n * - \"task_stalled\" – heartbeat missed for 60s, process was SIGKILLed\n * - \"task_timeout\" – hard timeout exceeded, process was SIGKILLed\n * - \"budget_warning\" – token usage crossed 80% threshold\n */\nexport class SubagentPool extends EventEmitter {\n\tprivate readonly maxConcurrency: number;\n\tprivate readonly executable: string;\n\tprivate readonly prefixArgs: string[];\n\tprivate readonly cwd: string;\n\tprivate readonly env: NodeJS.ProcessEnv;\n\tprivate readonly defaultTokenBudget: number;\n\n\tprivate slots = new Map<string, SubagentSlot>();\n\tprivate queue: SubagentPoolTask[] = [];\n\tprivate completed = new Map<string, SubagentResult>();\n\tprivate waiters = new Map<string, { resolve: (result: SubagentResult) => void; reject: (err: Error) => void }>();\n\tprivate budgets = new Map<string, TokenBudget>();\n\tprivate verifier = new OutputVerifier();\n\tprivate lifeguard: SubagentLifeguard;\n\tprivate disposed = false;\n\t/** Tracks why a task was killed (stalled / timeout) before exit handler fires. */\n\tprivate killReasons = new Map<string, \"stalled\" | \"timeout\">();\n\t/** Persistent terminal status map, survives wait_for consumption. */\n\tprivate taskStatus = new Map<string, \"done\" | \"failed\" | \"stalled\" | \"timeout\">();\n\n\tconstructor(options: SubagentPoolOptions) {\n\t\tsuper();\n\t\tthis.maxConcurrency = options.maxConcurrency ?? 5;\n\t\tthis.executable = options.executable;\n\t\tthis.prefixArgs = options.prefixArgs ?? [];\n\t\tthis.cwd = options.cwd ?? process.cwd();\n\t\tthis.env = options.env ?? process.env;\n\t\tthis.defaultTokenBudget = options.defaultTokenBudget ?? 0;\n\t\tthis.verifier = new OutputVerifier(this.cwd);\n\t\tthis.lifeguard = new SubagentLifeguard(this.cwd);\n\t\tthis.lifeguard.on(\"stalled\", (data: { task_id: string; pid: number }) => {\n\t\t\tthis.killReasons.set(data.task_id, \"stalled\");\n\t\t\tthis.emit(\"task_stalled\", data);\n\t\t});\n\t\tthis.lifeguard.on(\"timeout\", (data: { task_id: string; pid: number }) => {\n\t\t\tthis.killReasons.set(data.task_id, \"timeout\");\n\t\t\tthis.emit(\"task_timeout\", data);\n\t\t});\n\t}\n\n\t/** Priority value: higher numbers run first. */\n\tprivate priorityOf(agent_type: string): number {\n\t\tswitch (agent_type) {\n\t\t\tcase \"explore\":\n\t\t\tcase \"review\":\n\t\t\t\treturn 2;\n\t\t\tcase \"doc\":\n\t\t\t\treturn 0;\n\t\t\tdefault:\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\n\t/** Queue a task. It will run when a slot is free. */\n\tspawn(task: SubagentPoolTask): void {\n\t\tif (this.disposed) {\n\t\t\tthrow new Error(\"SubagentPool has been disposed\");\n\t\t}\n\t\tif (\n\t\t\tthis.slots.has(task.task_id) ||\n\t\t\tthis.queue.some((t) => t.task_id === task.task_id) ||\n\t\t\tthis.completed.has(task.task_id)\n\t\t) {\n\t\t\tthrow new Error(`Duplicate task_id: ${task.task_id}`);\n\t\t}\n\n\t\tconst p = this.priorityOf(task.agent_type);\n\t\tconst idx = this.queue.findIndex((t) => this.priorityOf(t.agent_type) < p);\n\t\tif (idx === -1) {\n\t\t\tthis.queue.push(task);\n\t\t} else {\n\t\t\tthis.queue.splice(idx, 0, task);\n\t\t}\n\t\tthis.pull();\n\t}\n\n\t/** Current status of a task. */\n\tget_status(task_id: string): \"running\" | \"queued\" | \"done\" | \"failed\" | \"stalled\" | \"timeout\" {\n\t\tif (this.slots.has(task_id)) return \"running\";\n\t\tif (this.queue.some((t) => t.task_id === task_id)) return \"queued\";\n\t\tconst persisted = this.taskStatus.get(task_id);\n\t\tif (persisted) return persisted;\n\t\tconst result = this.completed.get(task_id);\n\t\tif (result) {\n\t\t\tif (result.status === \"stalled\") return \"stalled\";\n\t\t\tif (result.status === \"timeout\") return \"timeout\";\n\t\t\tif (result.ok) return \"done\";\n\t\t\treturn \"failed\";\n\t\t}\n\t\treturn \"failed\";\n\t}\n\n\t/** Wait for a task to complete and return its result. */\n\twait_for(task_id: string): Promise<SubagentResult> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(new Error(\"SubagentPool has been disposed\"));\n\t\t}\n\n\t\tconst existing = this.completed.get(task_id);\n\t\tif (existing) {\n\t\t\tthis.completed.delete(task_id);\n\t\t\treturn Promise.resolve(existing);\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.waiters.set(task_id, { resolve, reject });\n\t\t});\n\t}\n\n\t/** Number of currently running subagents. */\n\trunning_count(): number {\n\t\treturn this.slots.size;\n\t}\n\n\t/** Number of tasks waiting in the queue. */\n\tqueued_count(): number {\n\t\treturn this.queue.length;\n\t}\n\n\t/**\n\t * Dispatch a task through the evaluator.\n\t *\n\t * - If `options.forceAgent` is provided, skip evaluation and spawn directly.\n\t * - Otherwise evaluate the task. If it should be handled inline, return\n\t * `{ handled_inline: true }` immediately.\n\t * - If delegating, spawn the subagent, wait for completion, write\n\t * `output.json`, and return the result.\n\t */\n\tasync dispatch(task: string, options: DispatchOptions = {}): Promise<TaskResult> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(new Error(\"SubagentPool has been disposed\"));\n\t\t}\n\n\t\tconst { forceAgent, context, model, provider } = options;\n\t\tconst evaluator = new DispatchEvaluator();\n\t\tconst analysis = evaluator.evaluate(task);\n\n\t\tif (!forceAgent && !analysis.should_delegate) {\n\t\t\treturn { handled_inline: true, reason: analysis.reason };\n\t\t}\n\n\t\tconst agent_type: SubagentMode = forceAgent ?? (analysis.agent_type as SubagentMode) ?? \"explore\";\n\t\tconst task_id = `dispatch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\t\tconst reason = forceAgent ? \"user_override\" : analysis.reason;\n\t\tconst complexity = analysis.estimated_complexity;\n\n\t\t// Pre-dispatch logging. Use stderr: stdout is reserved for the JSON event\n\t\t// stream / TUI render and must not be polluted.\n\t\tconst logLine = `[DISPATCH] agent=${agent_type} reason=${reason} complexity=${complexity} task_id=${task_id}`;\n\t\tconsole.error(logLine);\n\t\tthis.writeDispatchLog(task_id, agent_type, reason, complexity, task);\n\n\t\tconst poolTask: SubagentPoolTask = {\n\t\t\ttask_id,\n\t\t\tagent_type,\n\t\t\ttask,\n\t\t\tcontext,\n\t\t\tmodel,\n\t\t\tprovider,\n\t\t\tcwd: this.cwd,\n\t\t};\n\n\t\tconst startTime = Date.now();\n\t\tthis.spawn(poolTask);\n\t\tconst result = await this.wait_for(task_id);\n\t\tconst duration = Date.now() - startTime;\n\n\t\treturn {\n\t\t\thandled_inline: false,\n\t\t\ttask_id,\n\t\t\tagent_type,\n\t\t\treason,\n\t\t\tresult,\n\t\t\tduration,\n\t\t};\n\t}\n\n\t/**\n\t * Dispatch a batch of subtasks concurrently.\n\t *\n\t * Spawns up to `maxConcurrency` at once; overflow is queued with FIFO.\n\t * Returns aggregated results in the same order as the input.\n\t */\n\tasync dispatchBatch(\n\t\ttasks: Array<{ agent_type: SubagentMode; prompt: string }>,\n\t\tshared: Omit<DispatchOptions, \"forceAgent\"> = {},\n\t): Promise<TaskResult[]> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(new Error(\"SubagentPool has been disposed\"));\n\t\t}\n\n\t\tconst promises = tasks.map(async ({ agent_type, prompt }) => {\n\t\t\treturn this.dispatch(prompt, { ...shared, forceAgent: agent_type });\n\t\t});\n\n\t\treturn Promise.all(promises);\n\t}\n\n\tprivate writeDispatchLog(\n\t\ttask_id: string,\n\t\tagent_type: string,\n\t\treason: string,\n\t\tcomplexity: string,\n\t\ttask: string,\n\t): void {\n\t\tconst log = {\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\ttask_id,\n\t\t\tagent_type,\n\t\t\treason,\n\t\t\tcomplexity,\n\t\t\ttask,\n\t\t};\n\t\tconst path = join(this.cwd, CONFIG_DIR_NAME, \"agents\", task_id, \"dispatch-log.json\");\n\t\ttry {\n\t\t\tmkdirSync(dirname(path), { recursive: true });\n\t\t\twriteFileSync(path, JSON.stringify(log, null, 2));\n\t\t} catch {\n\t\t\t// Best-effort persistence\n\t\t}\n\t}\n\n\tprivate writeOutputJson(task_id: string, result: SubagentResult): void {\n\t\tconst output = {\n\t\t\ttask_id: result.task_id,\n\t\t\tok: result.ok,\n\t\t\texit_code: result.exit_code,\n\t\t\tstatus: result.status,\n\t\t\tstdout: result.stdout,\n\t\t\tstderr: result.stderr,\n\t\t\terror: result.error,\n\t\t\tbudget_exceeded: result.budget_exceeded,\n\t\t\tresult_data: result.result_data,\n\t\t};\n\t\tconst path = join(this.cwd, CONFIG_DIR_NAME, \"agents\", task_id, \"output.json\");\n\t\ttry {\n\t\t\tmkdirSync(dirname(path), { recursive: true });\n\t\t\twriteFileSync(path, JSON.stringify(output, null, 2));\n\t\t} catch {\n\t\t\t// Best-effort persistence\n\t\t}\n\t}\n\n\t/** Kill all running processes, clear the queue, and reject pending waiters. */\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\n\t\tfor (const slot of this.slots.values()) {\n\t\t\tif (!slot.process.killed) {\n\t\t\t\tslot.process.kill(\"SIGTERM\");\n\t\t\t}\n\t\t}\n\t\tthis.slots.clear();\n\t\tthis.queue = [];\n\n\t\tfor (const [task_id, waiter] of this.waiters) {\n\t\t\twaiter.reject(new Error(\"SubagentPool disposed\"));\n\t\t\tthis.waiters.delete(task_id);\n\t\t}\n\t\tthis.completed.clear();\n\t\tfor (const budget of this.budgets.values()) {\n\t\t\tbudget.removeAllListeners();\n\t\t}\n\t\tthis.budgets.clear();\n\t\tthis.killReasons.clear();\n\t\tthis.taskStatus.clear();\n\t\tthis.lifeguard.dispose();\n\t\tthis.removeAllListeners();\n\t}\n\n\t/** Pull tasks from the queue while slots are available. */\n\tprivate pull(): void {\n\t\twhile (this.slots.size < this.maxConcurrency && this.queue.length > 0) {\n\t\t\tconst task = this.queue.shift()!;\n\t\t\tthis.startTask(task, false);\n\t\t}\n\t}\n\n\t/** Build CLI arguments for a task. */\n\tprivate buildArgs(task: SubagentPoolTask): string[] {\n\t\tconst args: string[] = [...this.prefixArgs, \"--mode\", \"json\", \"--no-session\", \"--task-id\", task.task_id];\n\n\t\tif (task.agent_type) {\n\t\t\ttry {\n\t\t\t\tconst mode = task.agent_type as SubagentMode;\n\t\t\t\tconst systemPrompt = getSubagentSystemPrompt(mode);\n\t\t\t\targs.push(\"--system-prompt\", systemPrompt);\n\t\t\t\t// Enforce the per-mode tool allowlist so read-only modes cannot edit/write.\n\t\t\t\tconst tools = MODE_TOOLS[mode];\n\t\t\t\tif (tools) {\n\t\t\t\t\targs.push(\"--tools\", tools.join(\",\"));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unknown mode, skip custom system prompt\n\t\t\t}\n\t\t}\n\n\t\tif (task.model) {\n\t\t\targs.push(\"--model\", task.model);\n\t\t}\n\t\tif (task.provider) {\n\t\t\targs.push(\"--provider\", task.provider);\n\t\t}\n\n\t\tconst prompt = task.context?.trim()\n\t\t\t? `Context from the calling agent:\\n\\n${task.context.trim()}\\n\\nTask: ${task.task.trim()}`\n\t\t\t: `Task: ${task.task.trim()}`;\n\t\targs.push(prompt);\n\n\t\treturn args;\n\t}\n\n\t/** Start a task in a child process, with one retry on failure. */\n\tprivate startTask(task: SubagentPoolTask, isRetry: boolean): void {\n\t\t// Get or create a TokenBudget tracker. On retry, reuse the existing one\n\t\t// so cumulative usage persists across retries.\n\t\tlet budget = this.budgets.get(task.task_id);\n\t\tif (!budget) {\n\t\t\tbudget = new TokenBudget(task.task_id, task.agent_type, {\n\t\t\t\tlimit: task.token_budget,\n\t\t\t\tcwd: task.cwd ?? this.cwd,\n\t\t\t});\n\t\t\tbudget.on(\"budget_warning\", (data: { task_id: string; message: string; used: number; limit: number }) => {\n\t\t\t\tthis.emit(\"budget_warning\", data);\n\t\t\t});\n\t\t\tbudget.on(\"budget_exceeded\", () => {\n\t\t\t\tconst slot = this.slots.get(task.task_id);\n\t\t\t\tif (slot && !slot.process.killed) {\n\t\t\t\t\tslot.process.kill(\"SIGTERM\");\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.budgets.set(task.task_id, budget);\n\t\t}\n\n\t\tlet proc: ReturnType<typeof spawn>;\n\t\ttry {\n\t\t\tproc = spawn(this.executable, this.buildArgs(task), {\n\t\t\t\tcwd: task.cwd ?? this.cwd,\n\t\t\t\t// Mark the child as a subagent so its own DispatchEvaluator refuses to\n\t\t\t\t// spawn further subagents (depth guard).\n\t\t\t\tenv: { ...this.env, HOOCODE_SUBAGENT_DEPTH: \"1\" },\n\t\t\t\tshell: false,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\t\t} catch {\n\t\t\tif (!isRetry) {\n\t\t\t\tthis.startTask(task, true);\n\t\t\t} else {\n\t\t\t\tthis.emit(\"task_failed\", {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\terror: \"Spawn failed synchronously\",\n\t\t\t\t});\n\t\t\t\tthis.resolveWaiter(task.task_id, {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\tok: false,\n\t\t\t\t\tstdout: \"\",\n\t\t\t\t\tstderr: \"\",\n\t\t\t\t\texit_code: null,\n\t\t\t\t\terror: \"Spawn failed synchronously\",\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t});\n\t\t\t\tthis.pull();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst slot: SubagentSlot = {\n\t\t\tpid: proc.pid ?? 0,\n\t\t\tagent_type: task.agent_type,\n\t\t\ttask_id: task.task_id,\n\t\t\tspawned_at: Date.now(),\n\t\t\ttoken_budget: task.token_budget ?? this.defaultTokenBudget,\n\t\t\tprocess: proc,\n\t\t};\n\n\t\tthis.slots.set(task.task_id, slot);\n\t\tthis.lifeguard.monitor(task.task_id, task.agent_type, proc);\n\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\n\t\tproc.stdout?.on(\"data\", (data: Buffer) => {\n\t\t\tconst chunk = data.toString();\n\t\t\tstdout += chunk;\n\t\t\tbudget.processStdout(chunk);\n\n\t\t\t// Heartbeat detection: look for {\"ping\":true} JSON lines\n\t\t\tfor (const raw of chunk.split(\"\\n\")) {\n\t\t\t\tconst line = raw.trim();\n\t\t\t\tif (!line.startsWith(\"{\")) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(line) as Record<string, unknown>;\n\t\t\t\t\tif (parsed.ping === true) {\n\t\t\t\t\t\tthis.lifeguard.recordHeartbeat(task.task_id);\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Not a ping line, ignore\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tproc.stderr?.on(\"data\", (data: Buffer) => {\n\t\t\tstderr += data.toString();\n\t\t});\n\n\t\twaitForChildProcess(proc)\n\t\t\t.then((code) => {\n\t\t\t\tthis.slots.delete(task.task_id);\n\t\t\t\tbudget.flush();\n\n\t\t\t\tconst killReason = this.killReasons.get(task.task_id);\n\t\t\t\tthis.killReasons.delete(task.task_id);\n\n\t\t\t\tconst duration = Date.now() - slot.spawned_at;\n\t\t\t\tconst tokens_used = budget.getUsed();\n\t\t\t\tconst budgetExceeded = budget.isExceeded();\n\n\t\t\t\t// If killed by lifeguard, override exit handling\n\t\t\t\tif (killReason === \"stalled\" || killReason === \"timeout\") {\n\t\t\t\t\tconst result: SubagentResult = {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\tstdout,\n\t\t\t\t\t\tstderr,\n\t\t\t\t\t\texit_code: code,\n\t\t\t\t\t\tstatus: killReason,\n\t\t\t\t\t};\n\t\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\t\t\t\t\tthis.emit(`task_${killReason}`, {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\tduration,\n\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t});\n\t\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst result: SubagentResult = {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\tok: code === 0 && !budgetExceeded,\n\t\t\t\t\tstdout,\n\t\t\t\t\tstderr,\n\t\t\t\t\texit_code: code,\n\t\t\t\t\tbudget_exceeded: budgetExceeded,\n\t\t\t\t\tstatus: code === 0 && !budgetExceeded ? \"complete\" : \"failed\",\n\t\t\t\t};\n\n\t\t\t\tif (budgetExceeded) {\n\t\t\t\t\t// Force-return whatever exists in result.json, mark partial\n\t\t\t\t\tconst resultData = this.tryReadResultJson(task.task_id, task.cwd ?? this.cwd);\n\t\t\t\t\tresult.status = resultData ? \"partial\" : \"failed\";\n\t\t\t\t\tresult.result_data = resultData;\n\t\t\t\t\tif (resultData) {\n\t\t\t\t\t\tresult.ok = true; // partial is considered success with data\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Hard-stopped before any result.json was written. Surface a clear,\n\t\t\t\t\t\t// actionable reason instead of letting callers report \"unknown error\".\n\t\t\t\t\t\tresult.error = `Token budget exceeded (used ${tokens_used} of ${budget.getLimit()}) before the subagent produced a result. Narrow the task scope or raise the budget for ${task.agent_type} subagents.`;\n\t\t\t\t\t}\n\t\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\t\t\t\t\tthis.emit(resultData ? \"task_done\" : \"task_failed\", {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\tduration,\n\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t\t...(resultData ? { status: \"partial\" } : { error: result.error }),\n\t\t\t\t\t});\n\t\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (result.ok) {\n\t\t\t\t\tconst verification = this.verifier.verify(task.task_id, task.cwd ?? this.cwd);\n\t\t\t\t\tif (!verification.valid) {\n\t\t\t\t\t\tresult.ok = false;\n\t\t\t\t\t\tresult.error = verification.reason;\n\t\t\t\t\t\tresult.status = \"failed\";\n\t\t\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\t\t\t\t\t\tthis.emit(\"task_failed\", {\n\t\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t\t\terror: verification.reason,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// Attach the verified result.json so callers can read the summary\n\t\t\t\t\t// without parsing the raw event stream.\n\t\t\t\t\tresult.result_data = this.tryReadResultJson(task.task_id, task.cwd ?? this.cwd);\n\t\t\t\t}\n\n\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\n\t\t\t\tif (result.ok) {\n\t\t\t\t\tthis.emit(\"task_done\", {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\tduration,\n\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.emit(\"task_failed\", {\n\t\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\t\tduration,\n\t\t\t\t\t\ttokens_used,\n\t\t\t\t\t\terror: result.error ?? `Exited with code ${code}`,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\tthis.slots.delete(task.task_id);\n\t\t\t\tbudget.flush();\n\t\t\t\tconst duration = Date.now() - slot.spawned_at;\n\t\t\t\tconst tokens_used = budget.getUsed();\n\t\t\t\tif (!isRetry) {\n\t\t\t\t\tthis.startTask(task, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst error = err instanceof Error ? err.message : String(err);\n\t\t\t\tconst result: SubagentResult = {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\tok: false,\n\t\t\t\t\tstdout,\n\t\t\t\t\tstderr,\n\t\t\t\t\texit_code: null,\n\t\t\t\t\terror,\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t};\n\t\t\t\tthis.writeOutputJson(task.task_id, result);\n\t\t\t\tthis.emit(\"task_failed\", {\n\t\t\t\t\ttask_id: task.task_id,\n\t\t\t\t\tagent_type: task.agent_type,\n\t\t\t\t\tduration,\n\t\t\t\t\ttokens_used,\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t\tthis.resolveWaiter(task.task_id, result);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tbudget.removeAllListeners();\n\t\t\t\tthis.budgets.delete(task.task_id);\n\t\t\t\tthis.pull();\n\t\t\t});\n\t}\n\n\tprivate tryReadResultJson(task_id: string, cwd: string): Record<string, unknown> | undefined {\n\t\tconst path = join(cwd, CONFIG_DIR_NAME, \"agents\", task_id, \"result.json\");\n\t\tif (!existsSync(path)) return undefined;\n\t\ttry {\n\t\t\tconst raw = readFileSync(path, \"utf-8\");\n\t\t\treturn JSON.parse(raw) as Record<string, unknown>;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate resolveWaiter(task_id: string, result: SubagentResult): void {\n\t\t// Persist terminal status for get_status() even after wait_for consumes the result\n\t\tif (result.status === \"stalled\") this.taskStatus.set(task_id, \"stalled\");\n\t\telse if (result.status === \"timeout\") this.taskStatus.set(task_id, \"timeout\");\n\t\telse if (result.ok) this.taskStatus.set(task_id, \"done\");\n\t\telse this.taskStatus.set(task_id, \"failed\");\n\n\t\tconst waiter = this.waiters.get(task_id);\n\t\tif (waiter) {\n\t\t\twaiter.resolve(result);\n\t\t\tthis.waiters.delete(task_id);\n\t\t\treturn;\n\t\t}\n\t\tthis.completed.set(task_id, result);\n\t}\n}\n"]}
|
|
@@ -37,17 +37,22 @@ declare class TaskStore {
|
|
|
37
37
|
update(id: number, patch: TaskPatch): void;
|
|
38
38
|
remove(id: number): void;
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* Drop finished tasks and restart numbering from #1 once the pane is empty.
|
|
41
41
|
*
|
|
42
|
-
* Called when a new user message arrives: finished
|
|
43
|
-
* (with their final status) for the whole turn and
|
|
44
|
-
* starts the next turn, so
|
|
42
|
+
* Called when a new user message arrives: finished tasks from the previous turn
|
|
43
|
+
* stay visible (with their final status) for the whole turn and are wiped only
|
|
44
|
+
* when the user starts the next turn, so the next turn opens with an empty pane
|
|
45
|
+
* and its first task is #1 again. Active (pending/in_progress) tasks are kept —
|
|
46
|
+
* a follow-up/steer message can arrive while a subagent is still running, and
|
|
47
|
+
* dropping its task here would orphan the live work (its later status update
|
|
48
|
+
* would target a removed id and silently vanish). Numbering only restarts once
|
|
49
|
+
* no active task survives, so ids never collide with a kept task.
|
|
45
50
|
*/
|
|
46
|
-
|
|
51
|
+
reset(): void;
|
|
47
52
|
list(): readonly Task[];
|
|
48
|
-
|
|
49
|
-
/** Clear all tasks. Intended for test isolation only. */
|
|
53
|
+
/** Wipe all tasks and restart numbering. Intended for test isolation only. */
|
|
50
54
|
clear(): void;
|
|
55
|
+
subscribe(listener: Listener): () => void;
|
|
51
56
|
private emit;
|
|
52
57
|
}
|
|
53
58
|
/** Shared, process-wide task store. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/core/task-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvE,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,KAAK,CAAC,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;KACb,CAAC;CACF;AAED,MAAM,WAAW,iBAAiB;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;AAE3F,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAC;AAE3B,cAAM,SAAS;IACd,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAEjD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI,CAa3D;IAED,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CASzC;IAED,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAKvB;IAED
|
|
1
|
+
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/core/task-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvE,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,KAAK,CAAC,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;KACb,CAAC;CACF;AAED,MAAM,WAAW,iBAAiB;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;AAE3F,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAC;AAE3B,cAAM,SAAS;IACd,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAEjD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI,CAa3D;IAED,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CASzC;IAED,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAKvB;IAED;;;;;;;;;;;OAWG;IACH,KAAK,IAAI,IAAI,CAMZ;IAED,IAAI,IAAI,SAAS,IAAI,EAAE,CAEtB;IAED,8EAA8E;IAC9E,KAAK,IAAI,IAAI,CAIZ;IAED,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAKxC;IAED,OAAO,CAAC,IAAI;CAKZ;AAED,uCAAuC;AACvC,eAAO,MAAM,SAAS,WAAkB,CAAC","sourcesContent":["/**\n * Minimal in-process task store.\n *\n * Tracks short-lived tasks (e.g. subagent delegations) so the TUI task panel can\n * display active work. It is a process-level singleton because the tool that\n * creates tasks and the footer that renders them live in the same process and\n * there is no cross-process boundary to cross.\n */\n\nexport type TaskStatus = \"pending\" | \"in_progress\" | \"done\" | \"failed\";\n\nexport interface Task {\n\treadonly id: number;\n\ttitle: string;\n\tstatus: TaskStatus;\n\t/** Subagent mode when this task is owned by a subagent (e.g. \"explore\"). */\n\tsubagentMode?: string;\n\treadonly createdAt: number;\n\tupdatedAt: number;\n\t/** Token and cost usage attributed to this task (e.g. from a subagent session). */\n\tusage?: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheRead: number;\n\t\tcacheWrite: number;\n\t\tcost: number;\n\t};\n}\n\nexport interface CreateTaskOptions {\n\tsubagentMode?: string;\n}\n\nexport type TaskPatch = Partial<Pick<Task, \"title\" | \"status\" | \"subagentMode\" | \"usage\">>;\n\ntype Listener = () => void;\n\nclass TaskStore {\n\tprivate tasks: Task[] = [];\n\tprivate nextId = 1;\n\tprivate readonly listeners = new Set<Listener>();\n\n\tcreate(title: string, options: CreateTaskOptions = {}): Task {\n\t\tconst now = Date.now();\n\t\tconst task: Task = {\n\t\t\tid: this.nextId++,\n\t\t\ttitle: title.trim() || \"(untitled task)\",\n\t\t\tstatus: \"pending\",\n\t\t\tsubagentMode: options.subagentMode,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t\tthis.tasks.push(task);\n\t\tthis.emit();\n\t\treturn task;\n\t}\n\n\tupdate(id: number, patch: TaskPatch): void {\n\t\tconst task = this.tasks.find((t) => t.id === id);\n\t\tif (!task) return;\n\t\tif (patch.title !== undefined) task.title = patch.title;\n\t\tif (patch.status !== undefined) task.status = patch.status;\n\t\tif (patch.subagentMode !== undefined) task.subagentMode = patch.subagentMode;\n\t\tif (patch.usage !== undefined) task.usage = patch.usage;\n\t\ttask.updatedAt = Date.now();\n\t\tthis.emit();\n\t}\n\n\tremove(id: number): void {\n\t\tconst idx = this.tasks.findIndex((t) => t.id === id);\n\t\tif (idx === -1) return;\n\t\tthis.tasks.splice(idx, 1);\n\t\tthis.emit();\n\t}\n\n\t/**\n\t * Drop finished tasks and restart numbering from #1 once the pane is empty.\n\t *\n\t * Called when a new user message arrives: finished tasks from the previous turn\n\t * stay visible (with their final status) for the whole turn and are wiped only\n\t * when the user starts the next turn, so the next turn opens with an empty pane\n\t * and its first task is #1 again. Active (pending/in_progress) tasks are kept —\n\t * a follow-up/steer message can arrive while a subagent is still running, and\n\t * dropping its task here would orphan the live work (its later status update\n\t * would target a removed id and silently vanish). Numbering only restarts once\n\t * no active task survives, so ids never collide with a kept task.\n\t */\n\treset(): void {\n\t\tconst active = this.tasks.filter((t) => t.status === \"pending\" || t.status === \"in_progress\");\n\t\tif (active.length === this.tasks.length && this.nextId === 1) return;\n\t\tthis.tasks = active;\n\t\tif (active.length === 0) this.nextId = 1;\n\t\tthis.emit();\n\t}\n\n\tlist(): readonly Task[] {\n\t\treturn this.tasks;\n\t}\n\n\t/** Wipe all tasks and restart numbering. Intended for test isolation only. */\n\tclear(): void {\n\t\tthis.tasks = [];\n\t\tthis.nextId = 1;\n\t\tthis.emit();\n\t}\n\n\tsubscribe(listener: Listener): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\tprivate emit(): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener();\n\t\t}\n\t}\n}\n\n/** Shared, process-wide task store. */\nexport const taskStore = new TaskStore();\n"]}
|
package/dist/core/task-store.js
CHANGED
|
@@ -47,33 +47,41 @@ class TaskStore {
|
|
|
47
47
|
this.emit();
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
50
|
+
* Drop finished tasks and restart numbering from #1 once the pane is empty.
|
|
51
51
|
*
|
|
52
|
-
* Called when a new user message arrives: finished
|
|
53
|
-
* (with their final status) for the whole turn and
|
|
54
|
-
* starts the next turn, so
|
|
52
|
+
* Called when a new user message arrives: finished tasks from the previous turn
|
|
53
|
+
* stay visible (with their final status) for the whole turn and are wiped only
|
|
54
|
+
* when the user starts the next turn, so the next turn opens with an empty pane
|
|
55
|
+
* and its first task is #1 again. Active (pending/in_progress) tasks are kept —
|
|
56
|
+
* a follow-up/steer message can arrive while a subagent is still running, and
|
|
57
|
+
* dropping its task here would orphan the live work (its later status update
|
|
58
|
+
* would target a removed id and silently vanish). Numbering only restarts once
|
|
59
|
+
* no active task survives, so ids never collide with a kept task.
|
|
55
60
|
*/
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
reset() {
|
|
62
|
+
const active = this.tasks.filter((t) => t.status === "pending" || t.status === "in_progress");
|
|
63
|
+
if (active.length === this.tasks.length && this.nextId === 1)
|
|
64
|
+
return;
|
|
65
|
+
this.tasks = active;
|
|
66
|
+
if (active.length === 0)
|
|
67
|
+
this.nextId = 1;
|
|
68
|
+
this.emit();
|
|
61
69
|
}
|
|
62
70
|
list() {
|
|
63
71
|
return this.tasks;
|
|
64
72
|
}
|
|
73
|
+
/** Wipe all tasks and restart numbering. Intended for test isolation only. */
|
|
74
|
+
clear() {
|
|
75
|
+
this.tasks = [];
|
|
76
|
+
this.nextId = 1;
|
|
77
|
+
this.emit();
|
|
78
|
+
}
|
|
65
79
|
subscribe(listener) {
|
|
66
80
|
this.listeners.add(listener);
|
|
67
81
|
return () => {
|
|
68
82
|
this.listeners.delete(listener);
|
|
69
83
|
};
|
|
70
84
|
}
|
|
71
|
-
/** Clear all tasks. Intended for test isolation only. */
|
|
72
|
-
clear() {
|
|
73
|
-
this.tasks = [];
|
|
74
|
-
this.nextId = 1;
|
|
75
|
-
this.emit();
|
|
76
|
-
}
|
|
77
85
|
emit() {
|
|
78
86
|
for (const listener of this.listeners) {
|
|
79
87
|
listener();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-store.js","sourceRoot":"","sources":["../../src/core/task-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8BH,MAAM,SAAS;IACN,KAAK,GAAW,EAAE,CAAC;IACnB,MAAM,GAAG,CAAC,CAAC;IACF,SAAS,GAAG,IAAI,GAAG,EAAY,CAAC;IAEjD,MAAM,CAAC,KAAa,EAAE,OAAO,GAAsB,EAAE,EAAQ;QAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,GAAS;YAClB,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE;YACjB,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,iBAAiB;YACxC,MAAM,EAAE,SAAS;YACjB,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;SACd,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,MAAM,CAAC,EAAU,EAAE,KAAgB,EAAQ;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACxD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3D,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;YAAE,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;QAC7E,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACxD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;IAAA,CACZ;IAED,MAAM,CAAC,EAAU,EAAQ;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACrD,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;IAAA,CACZ;IAED
|
|
1
|
+
{"version":3,"file":"task-store.js","sourceRoot":"","sources":["../../src/core/task-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Minimal in-process task store.\n *\n * Tracks short-lived tasks (e.g. subagent delegations) so the TUI task panel can\n * display active work. It is a process-level singleton because the tool that\n * creates tasks and the footer that renders them live in the same process and\n * there is no cross-process boundary to cross.\n */\n\nexport type TaskStatus = \"pending\" | \"in_progress\" | \"done\" | \"failed\";\n\nexport interface Task {\n\treadonly id: number;\n\ttitle: string;\n\tstatus: TaskStatus;\n\t/** Subagent mode when this task is owned by a subagent (e.g. \"explore\"). */\n\tsubagentMode?: string;\n\treadonly createdAt: number;\n\tupdatedAt: number;\n\t/** Token and cost usage attributed to this task (e.g. from a subagent session). */\n\tusage?: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheRead: number;\n\t\tcacheWrite: number;\n\t\tcost: number;\n\t};\n}\n\nexport interface CreateTaskOptions {\n\tsubagentMode?: string;\n}\n\nexport type TaskPatch = Partial<Pick<Task, \"title\" | \"status\" | \"subagentMode\" | \"usage\">>;\n\ntype Listener = () => void;\n\nclass TaskStore {\n\tprivate tasks: Task[] = [];\n\tprivate nextId = 1;\n\tprivate readonly listeners = new Set<Listener>();\n\n\tcreate(title: string, options: CreateTaskOptions = {}): Task {\n\t\tconst now = Date.now();\n\t\tconst task: Task = {\n\t\t\tid: this.nextId++,\n\t\t\ttitle: title.trim() || \"(untitled task)\",\n\t\t\tstatus: \"pending\",\n\t\t\tsubagentMode: options.subagentMode,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t\tthis.tasks.push(task);\n\t\tthis.emit();\n\t\treturn task;\n\t}\n\n\tupdate(id: number, patch: TaskPatch): void {\n\t\tconst task = this.tasks.find((t) => t.id === id);\n\t\tif (!task) return;\n\t\tif (patch.title !== undefined) task.title = patch.title;\n\t\tif (patch.status !== undefined) task.status = patch.status;\n\t\tif (patch.subagentMode !== undefined) task.subagentMode = patch.subagentMode;\n\t\tif (patch.usage !== undefined) task.usage = patch.usage;\n\t\ttask.updatedAt = Date.now();\n\t\tthis.emit();\n\t}\n\n\tremove(id: number): void {\n\t\tconst idx = this.tasks.findIndex((t) => t.id === id);\n\t\tif (idx === -1) return;\n\t\tthis.tasks.splice(idx, 1);\n\t\tthis.emit();\n\t}\n\n\t/**\n\t * Drop finished tasks and restart numbering from #1 once the pane is empty.\n\t *\n\t * Called when a new user message arrives: finished tasks from the previous turn\n\t * stay visible (with their final status) for the whole turn and are wiped only\n\t * when the user starts the next turn, so the next turn opens with an empty pane\n\t * and its first task is #1 again. Active (pending/in_progress) tasks are kept —\n\t * a follow-up/steer message can arrive while a subagent is still running, and\n\t * dropping its task here would orphan the live work (its later status update\n\t * would target a removed id and silently vanish). Numbering only restarts once\n\t * no active task survives, so ids never collide with a kept task.\n\t */\n\treset(): void {\n\t\tconst active = this.tasks.filter((t) => t.status === \"pending\" || t.status === \"in_progress\");\n\t\tif (active.length === this.tasks.length && this.nextId === 1) return;\n\t\tthis.tasks = active;\n\t\tif (active.length === 0) this.nextId = 1;\n\t\tthis.emit();\n\t}\n\n\tlist(): readonly Task[] {\n\t\treturn this.tasks;\n\t}\n\n\t/** Wipe all tasks and restart numbering. Intended for test isolation only. */\n\tclear(): void {\n\t\tthis.tasks = [];\n\t\tthis.nextId = 1;\n\t\tthis.emit();\n\t}\n\n\tsubscribe(listener: Listener): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\tprivate emit(): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener();\n\t\t}\n\t}\n}\n\n/** Shared, process-wide task store. */\nexport const taskStore = new TaskStore();\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-budget.d.ts","sourceRoot":"","sources":["../../src/core/token-budget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,MAAM,WAAW,iBAAiB;IACjC,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"token-budget.d.ts","sourceRoot":"","sources":["../../src/core/token-budget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,MAAM,WAAW,iBAAiB;IACjC,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACrB;AAqBD,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;;GAOG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAM;IAE1B,+BAA+B;IAC/B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,kCAAkC;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,YAAY,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAO,EAQ9F;IAED,wDAAwD;IACxD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAYjC;IAED,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAKZ;IAED,sCAAsC;IACtC,OAAO,IAAI,MAAM,CAEhB;IAED,qCAAqC;IACrC,QAAQ,IAAI,MAAM,CAEjB;IAED,qDAAqD;IACrD,QAAQ,IAAI,OAAO,CAElB;IAED,4CAA4C;IAC5C,UAAU,IAAI,OAAO,CAEpB;IAED,4CAA4C;IAC5C,OAAO,IAAI,IAAI,CAkBd;IAED,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,SAAS;CAgDjB","sourcesContent":["import { EventEmitter } from \"node:events\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../config.js\";\n\nexport interface TokenBudgetConfig {\n\t/** Budget limit in tokens. */\n\tlimit: number;\n}\n\nexport interface TokenBudgetState {\n\ttask_id: string;\n\tagent_type: string;\n\tbudget: number;\n\tused: number;\n\twarned: boolean;\n\texceeded: boolean;\n\tlast_updated: number;\n}\n\n/**\n * Default token budgets per agent type (in tokens). Raised from the previous\n * 8k–16k range, which was too low and hard-stopped subagents mid-task.\n * - explore: 35 000 (read-only scanning, tracing deps)\n * - edit: 60 000 (reads, plans, writes, iterates — heaviest)\n * - test: 45 000 (writes tests, runs, debugs failures)\n * - fix: 45 000 (reads, edits, re-runs to verify the fix)\n * - review: 35 000 (read-only audit, security analysis)\n * - doc: 30 000 (reads code, writes docs/comments)\n */\nconst DEFAULT_BUDGETS: Record<string, number> = {\n\texplore: 35000,\n\tedit: 60000,\n\ttest: 45000,\n\tfix: 45000,\n\treview: 35000,\n\tdoc: 30000,\n};\n\n/** Get the default budget for an agent type. */\nexport function getDefaultBudget(agent_type: string): number {\n\treturn DEFAULT_BUDGETS[agent_type] ?? 35000;\n}\n\n/**\n * Tracks cumulative token usage for a single subagent task by parsing\n * newline-delimited JSON events from the subagent's stdout stream.\n *\n * Emits:\n * - \"budget_warning\" when 80% of the budget is consumed\n * - \"budget_exceeded\" when 100% of the budget is consumed\n */\nexport class TokenBudget extends EventEmitter {\n\tprivate readonly task_id: string;\n\tprivate readonly agent_type: string;\n\tprivate readonly limit: number;\n\tprivate readonly cwd: string;\n\n\tprivate used = 0;\n\tprivate warned = false;\n\tprivate exceeded = false;\n\tprivate stdoutBuffer = \"\";\n\n\t/** Warning threshold (80%). */\n\tprivate readonly warningThreshold: number;\n\t/** Hard-stop threshold (100%). */\n\tprivate readonly exceededThreshold: number;\n\n\tconstructor(task_id: string, agent_type: string, options: { limit?: number; cwd?: string } = {}) {\n\t\tsuper();\n\t\tthis.task_id = task_id;\n\t\tthis.agent_type = agent_type;\n\t\tthis.limit = options.limit ?? getDefaultBudget(agent_type);\n\t\tthis.cwd = options.cwd ?? process.cwd();\n\t\tthis.warningThreshold = Math.floor(this.limit * 0.8);\n\t\tthis.exceededThreshold = this.limit;\n\t}\n\n\t/** Process a chunk of stdout data from the subagent. */\n\tprocessStdout(chunk: string): void {\n\t\tthis.stdoutBuffer += chunk;\n\n\t\twhile (true) {\n\t\t\tconst lineEnd = this.stdoutBuffer.indexOf(\"\\n\");\n\t\t\tif (lineEnd === -1) break;\n\t\t\tconst line = this.stdoutBuffer.slice(0, lineEnd).trimEnd();\n\t\t\tthis.stdoutBuffer = this.stdoutBuffer.slice(lineEnd + 1);\n\t\t\tif (line) {\n\t\t\t\tthis.parseLine(line);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Flush any remaining buffered stdout. Call when the stream ends. */\n\tflush(): void {\n\t\tif (this.stdoutBuffer.trim()) {\n\t\t\tthis.parseLine(this.stdoutBuffer.trim());\n\t\t\tthis.stdoutBuffer = \"\";\n\t\t}\n\t}\n\n\t/** Current cumulative token usage. */\n\tgetUsed(): number {\n\t\treturn this.used;\n\t}\n\n\t/** Configured token budget limit. */\n\tgetLimit(): number {\n\t\treturn this.limit;\n\t}\n\n\t/** Whether the budget warning has been triggered. */\n\tisWarned(): boolean {\n\t\treturn this.warned;\n\t}\n\n\t/** Whether the budget has been exceeded. */\n\tisExceeded(): boolean {\n\t\treturn this.exceeded;\n\t}\n\n\t/** Persist current budget state to disk. */\n\tpersist(): void {\n\t\tconst state: TokenBudgetState = {\n\t\t\ttask_id: this.task_id,\n\t\t\tagent_type: this.agent_type,\n\t\t\tbudget: this.limit,\n\t\t\tused: this.used,\n\t\t\twarned: this.warned,\n\t\t\texceeded: this.exceeded,\n\t\t\tlast_updated: Date.now(),\n\t\t};\n\n\t\tconst path = this.budgetPath();\n\t\ttry {\n\t\t\tmkdirSync(dirname(path), { recursive: true });\n\t\t\twriteFileSync(path, JSON.stringify(state, null, 2));\n\t\t} catch {\n\t\t\t// Persistence is best-effort; silently ignore write failures\n\t\t}\n\t}\n\n\tprivate budgetPath(): string {\n\t\treturn join(this.cwd, CONFIG_DIR_NAME, \"agents\", this.task_id, \"budget.json\");\n\t}\n\n\tprivate parseLine(line: string): void {\n\t\tlet event: unknown;\n\t\ttry {\n\t\t\tevent = JSON.parse(line);\n\t\t} catch {\n\t\t\treturn; // Not valid JSON, ignore\n\t\t}\n\n\t\tif (!event || typeof event !== \"object\") return;\n\n\t\t// Look for message_end events with assistant messages that have usage\n\t\tconst typedEvent = event as Record<string, unknown>;\n\t\tif (typedEvent.type !== \"message_end\") return;\n\n\t\tconst message = typedEvent.message as Record<string, unknown> | undefined;\n\t\tif (!message) return;\n\t\tif (message.role !== \"assistant\") return;\n\n\t\tconst usage = message.usage as Record<string, unknown> | undefined;\n\t\tif (!usage) return;\n\n\t\tconst totalTokens = typeof usage.totalTokens === \"number\" ? usage.totalTokens : undefined;\n\t\tif (totalTokens === undefined || totalTokens <= 0) return;\n\n\t\tthis.used += totalTokens;\n\n\t\t// Check thresholds\n\t\tif (!this.warned && this.used >= this.warningThreshold) {\n\t\t\tthis.warned = true;\n\t\t\tthis.emit(\"budget_warning\", {\n\t\t\t\ttask_id: this.task_id,\n\t\t\t\tmessage: \"You are near token limit. Summarize and write result.json now.\",\n\t\t\t\tused: this.used,\n\t\t\t\tlimit: this.limit,\n\t\t\t});\n\t\t}\n\n\t\tif (!this.exceeded && this.used >= this.exceededThreshold) {\n\t\t\tthis.exceeded = true;\n\t\t\tthis.emit(\"budget_exceeded\", {\n\t\t\t\ttask_id: this.task_id,\n\t\t\t\tused: this.used,\n\t\t\t\tlimit: this.limit,\n\t\t\t});\n\t\t}\n\n\t\tthis.persist();\n\t}\n}\n"]}
|
|
@@ -3,23 +3,26 @@ import { mkdirSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { CONFIG_DIR_NAME } from "../config.js";
|
|
5
5
|
/**
|
|
6
|
-
* Default token budgets per agent type (in tokens).
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
6
|
+
* Default token budgets per agent type (in tokens). Raised from the previous
|
|
7
|
+
* 8k–16k range, which was too low and hard-stopped subagents mid-task.
|
|
8
|
+
* - explore: 35 000 (read-only scanning, tracing deps)
|
|
9
|
+
* - edit: 60 000 (reads, plans, writes, iterates — heaviest)
|
|
10
|
+
* - test: 45 000 (writes tests, runs, debugs failures)
|
|
11
|
+
* - fix: 45 000 (reads, edits, re-runs to verify the fix)
|
|
12
|
+
* - review: 35 000 (read-only audit, security analysis)
|
|
13
|
+
* - doc: 30 000 (reads code, writes docs/comments)
|
|
12
14
|
*/
|
|
13
15
|
const DEFAULT_BUDGETS = {
|
|
14
|
-
explore:
|
|
15
|
-
edit:
|
|
16
|
-
test:
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
explore: 35000,
|
|
17
|
+
edit: 60000,
|
|
18
|
+
test: 45000,
|
|
19
|
+
fix: 45000,
|
|
20
|
+
review: 35000,
|
|
21
|
+
doc: 30000,
|
|
19
22
|
};
|
|
20
23
|
/** Get the default budget for an agent type. */
|
|
21
24
|
export function getDefaultBudget(agent_type) {
|
|
22
|
-
return DEFAULT_BUDGETS[agent_type] ??
|
|
25
|
+
return DEFAULT_BUDGETS[agent_type] ?? 35000;
|
|
23
26
|
}
|
|
24
27
|
/**
|
|
25
28
|
* Tracks cumulative token usage for a single subagent task by parsing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-budget.js","sourceRoot":"","sources":["../../src/core/token-budget.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter } from \"node:events\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../config.js\";\n\nexport interface TokenBudgetConfig {\n\t/** Budget limit in tokens. */\n\tlimit: number;\n}\n\nexport interface TokenBudgetState {\n\ttask_id: string;\n\tagent_type: string;\n\tbudget: number;\n\tused: number;\n\twarned: boolean;\n\texceeded: boolean;\n\tlast_updated: number;\n}\n\n/**\n * Default token budgets per agent type (in tokens).\n * - explore: 8 000\n * - edit: 16 000\n * - test: 16 000\n * - review: 12 000\n * - doc: 10 000\n */\nconst DEFAULT_BUDGETS: Record<string, number> = {\n\texplore: 8000,\n\tedit: 16000,\n\ttest: 16000,\n\treview: 12000,\n\tdoc: 10000,\n};\n\n/** Get the default budget for an agent type. */\nexport function getDefaultBudget(agent_type: string): number {\n\treturn DEFAULT_BUDGETS[agent_type] ?? 16000;\n}\n\n/**\n * Tracks cumulative token usage for a single subagent task by parsing\n * newline-delimited JSON events from the subagent's stdout stream.\n *\n * Emits:\n * - \"budget_warning\" when 80% of the budget is consumed\n * - \"budget_exceeded\" when 100% of the budget is consumed\n */\nexport class TokenBudget extends EventEmitter {\n\tprivate readonly task_id: string;\n\tprivate readonly agent_type: string;\n\tprivate readonly limit: number;\n\tprivate readonly cwd: string;\n\n\tprivate used = 0;\n\tprivate warned = false;\n\tprivate exceeded = false;\n\tprivate stdoutBuffer = \"\";\n\n\t/** Warning threshold (80%). */\n\tprivate readonly warningThreshold: number;\n\t/** Hard-stop threshold (100%). */\n\tprivate readonly exceededThreshold: number;\n\n\tconstructor(task_id: string, agent_type: string, options: { limit?: number; cwd?: string } = {}) {\n\t\tsuper();\n\t\tthis.task_id = task_id;\n\t\tthis.agent_type = agent_type;\n\t\tthis.limit = options.limit ?? getDefaultBudget(agent_type);\n\t\tthis.cwd = options.cwd ?? process.cwd();\n\t\tthis.warningThreshold = Math.floor(this.limit * 0.8);\n\t\tthis.exceededThreshold = this.limit;\n\t}\n\n\t/** Process a chunk of stdout data from the subagent. */\n\tprocessStdout(chunk: string): void {\n\t\tthis.stdoutBuffer += chunk;\n\n\t\twhile (true) {\n\t\t\tconst lineEnd = this.stdoutBuffer.indexOf(\"\\n\");\n\t\t\tif (lineEnd === -1) break;\n\t\t\tconst line = this.stdoutBuffer.slice(0, lineEnd).trimEnd();\n\t\t\tthis.stdoutBuffer = this.stdoutBuffer.slice(lineEnd + 1);\n\t\t\tif (line) {\n\t\t\t\tthis.parseLine(line);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Flush any remaining buffered stdout. Call when the stream ends. */\n\tflush(): void {\n\t\tif (this.stdoutBuffer.trim()) {\n\t\t\tthis.parseLine(this.stdoutBuffer.trim());\n\t\t\tthis.stdoutBuffer = \"\";\n\t\t}\n\t}\n\n\t/** Current cumulative token usage. */\n\tgetUsed(): number {\n\t\treturn this.used;\n\t}\n\n\t/** Configured token budget limit. */\n\tgetLimit(): number {\n\t\treturn this.limit;\n\t}\n\n\t/** Whether the budget warning has been triggered. */\n\tisWarned(): boolean {\n\t\treturn this.warned;\n\t}\n\n\t/** Whether the budget has been exceeded. */\n\tisExceeded(): boolean {\n\t\treturn this.exceeded;\n\t}\n\n\t/** Persist current budget state to disk. */\n\tpersist(): void {\n\t\tconst state: TokenBudgetState = {\n\t\t\ttask_id: this.task_id,\n\t\t\tagent_type: this.agent_type,\n\t\t\tbudget: this.limit,\n\t\t\tused: this.used,\n\t\t\twarned: this.warned,\n\t\t\texceeded: this.exceeded,\n\t\t\tlast_updated: Date.now(),\n\t\t};\n\n\t\tconst path = this.budgetPath();\n\t\ttry {\n\t\t\tmkdirSync(dirname(path), { recursive: true });\n\t\t\twriteFileSync(path, JSON.stringify(state, null, 2));\n\t\t} catch {\n\t\t\t// Persistence is best-effort; silently ignore write failures\n\t\t}\n\t}\n\n\tprivate budgetPath(): string {\n\t\treturn join(this.cwd, CONFIG_DIR_NAME, \"agents\", this.task_id, \"budget.json\");\n\t}\n\n\tprivate parseLine(line: string): void {\n\t\tlet event: unknown;\n\t\ttry {\n\t\t\tevent = JSON.parse(line);\n\t\t} catch {\n\t\t\treturn; // Not valid JSON, ignore\n\t\t}\n\n\t\tif (!event || typeof event !== \"object\") return;\n\n\t\t// Look for message_end events with assistant messages that have usage\n\t\tconst typedEvent = event as Record<string, unknown>;\n\t\tif (typedEvent.type !== \"message_end\") return;\n\n\t\tconst message = typedEvent.message as Record<string, unknown> | undefined;\n\t\tif (!message) return;\n\t\tif (message.role !== \"assistant\") return;\n\n\t\tconst usage = message.usage as Record<string, unknown> | undefined;\n\t\tif (!usage) return;\n\n\t\tconst totalTokens = typeof usage.totalTokens === \"number\" ? usage.totalTokens : undefined;\n\t\tif (totalTokens === undefined || totalTokens <= 0) return;\n\n\t\tthis.used += totalTokens;\n\n\t\t// Check thresholds\n\t\tif (!this.warned && this.used >= this.warningThreshold) {\n\t\t\tthis.warned = true;\n\t\t\tthis.emit(\"budget_warning\", {\n\t\t\t\ttask_id: this.task_id,\n\t\t\t\tmessage: \"You are near token limit. Summarize and write result.json now.\",\n\t\t\t\tused: this.used,\n\t\t\t\tlimit: this.limit,\n\t\t\t});\n\t\t}\n\n\t\tif (!this.exceeded && this.used >= this.exceededThreshold) {\n\t\t\tthis.exceeded = true;\n\t\t\tthis.emit(\"budget_exceeded\", {\n\t\t\t\ttask_id: this.task_id,\n\t\t\t\tused: this.used,\n\t\t\t\tlimit: this.limit,\n\t\t\t});\n\t\t}\n\n\t\tthis.persist();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"token-budget.js","sourceRoot":"","sources":["../../src/core/token-budget.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter } from \"node:events\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../config.js\";\n\nexport interface TokenBudgetConfig {\n\t/** Budget limit in tokens. */\n\tlimit: number;\n}\n\nexport interface TokenBudgetState {\n\ttask_id: string;\n\tagent_type: string;\n\tbudget: number;\n\tused: number;\n\twarned: boolean;\n\texceeded: boolean;\n\tlast_updated: number;\n}\n\n/**\n * Default token budgets per agent type (in tokens). Raised from the previous\n * 8k–16k range, which was too low and hard-stopped subagents mid-task.\n * - explore: 35 000 (read-only scanning, tracing deps)\n * - edit: 60 000 (reads, plans, writes, iterates — heaviest)\n * - test: 45 000 (writes tests, runs, debugs failures)\n * - fix: 45 000 (reads, edits, re-runs to verify the fix)\n * - review: 35 000 (read-only audit, security analysis)\n * - doc: 30 000 (reads code, writes docs/comments)\n */\nconst DEFAULT_BUDGETS: Record<string, number> = {\n\texplore: 35000,\n\tedit: 60000,\n\ttest: 45000,\n\tfix: 45000,\n\treview: 35000,\n\tdoc: 30000,\n};\n\n/** Get the default budget for an agent type. */\nexport function getDefaultBudget(agent_type: string): number {\n\treturn DEFAULT_BUDGETS[agent_type] ?? 35000;\n}\n\n/**\n * Tracks cumulative token usage for a single subagent task by parsing\n * newline-delimited JSON events from the subagent's stdout stream.\n *\n * Emits:\n * - \"budget_warning\" when 80% of the budget is consumed\n * - \"budget_exceeded\" when 100% of the budget is consumed\n */\nexport class TokenBudget extends EventEmitter {\n\tprivate readonly task_id: string;\n\tprivate readonly agent_type: string;\n\tprivate readonly limit: number;\n\tprivate readonly cwd: string;\n\n\tprivate used = 0;\n\tprivate warned = false;\n\tprivate exceeded = false;\n\tprivate stdoutBuffer = \"\";\n\n\t/** Warning threshold (80%). */\n\tprivate readonly warningThreshold: number;\n\t/** Hard-stop threshold (100%). */\n\tprivate readonly exceededThreshold: number;\n\n\tconstructor(task_id: string, agent_type: string, options: { limit?: number; cwd?: string } = {}) {\n\t\tsuper();\n\t\tthis.task_id = task_id;\n\t\tthis.agent_type = agent_type;\n\t\tthis.limit = options.limit ?? getDefaultBudget(agent_type);\n\t\tthis.cwd = options.cwd ?? process.cwd();\n\t\tthis.warningThreshold = Math.floor(this.limit * 0.8);\n\t\tthis.exceededThreshold = this.limit;\n\t}\n\n\t/** Process a chunk of stdout data from the subagent. */\n\tprocessStdout(chunk: string): void {\n\t\tthis.stdoutBuffer += chunk;\n\n\t\twhile (true) {\n\t\t\tconst lineEnd = this.stdoutBuffer.indexOf(\"\\n\");\n\t\t\tif (lineEnd === -1) break;\n\t\t\tconst line = this.stdoutBuffer.slice(0, lineEnd).trimEnd();\n\t\t\tthis.stdoutBuffer = this.stdoutBuffer.slice(lineEnd + 1);\n\t\t\tif (line) {\n\t\t\t\tthis.parseLine(line);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Flush any remaining buffered stdout. Call when the stream ends. */\n\tflush(): void {\n\t\tif (this.stdoutBuffer.trim()) {\n\t\t\tthis.parseLine(this.stdoutBuffer.trim());\n\t\t\tthis.stdoutBuffer = \"\";\n\t\t}\n\t}\n\n\t/** Current cumulative token usage. */\n\tgetUsed(): number {\n\t\treturn this.used;\n\t}\n\n\t/** Configured token budget limit. */\n\tgetLimit(): number {\n\t\treturn this.limit;\n\t}\n\n\t/** Whether the budget warning has been triggered. */\n\tisWarned(): boolean {\n\t\treturn this.warned;\n\t}\n\n\t/** Whether the budget has been exceeded. */\n\tisExceeded(): boolean {\n\t\treturn this.exceeded;\n\t}\n\n\t/** Persist current budget state to disk. */\n\tpersist(): void {\n\t\tconst state: TokenBudgetState = {\n\t\t\ttask_id: this.task_id,\n\t\t\tagent_type: this.agent_type,\n\t\t\tbudget: this.limit,\n\t\t\tused: this.used,\n\t\t\twarned: this.warned,\n\t\t\texceeded: this.exceeded,\n\t\t\tlast_updated: Date.now(),\n\t\t};\n\n\t\tconst path = this.budgetPath();\n\t\ttry {\n\t\t\tmkdirSync(dirname(path), { recursive: true });\n\t\t\twriteFileSync(path, JSON.stringify(state, null, 2));\n\t\t} catch {\n\t\t\t// Persistence is best-effort; silently ignore write failures\n\t\t}\n\t}\n\n\tprivate budgetPath(): string {\n\t\treturn join(this.cwd, CONFIG_DIR_NAME, \"agents\", this.task_id, \"budget.json\");\n\t}\n\n\tprivate parseLine(line: string): void {\n\t\tlet event: unknown;\n\t\ttry {\n\t\t\tevent = JSON.parse(line);\n\t\t} catch {\n\t\t\treturn; // Not valid JSON, ignore\n\t\t}\n\n\t\tif (!event || typeof event !== \"object\") return;\n\n\t\t// Look for message_end events with assistant messages that have usage\n\t\tconst typedEvent = event as Record<string, unknown>;\n\t\tif (typedEvent.type !== \"message_end\") return;\n\n\t\tconst message = typedEvent.message as Record<string, unknown> | undefined;\n\t\tif (!message) return;\n\t\tif (message.role !== \"assistant\") return;\n\n\t\tconst usage = message.usage as Record<string, unknown> | undefined;\n\t\tif (!usage) return;\n\n\t\tconst totalTokens = typeof usage.totalTokens === \"number\" ? usage.totalTokens : undefined;\n\t\tif (totalTokens === undefined || totalTokens <= 0) return;\n\n\t\tthis.used += totalTokens;\n\n\t\t// Check thresholds\n\t\tif (!this.warned && this.used >= this.warningThreshold) {\n\t\t\tthis.warned = true;\n\t\t\tthis.emit(\"budget_warning\", {\n\t\t\t\ttask_id: this.task_id,\n\t\t\t\tmessage: \"You are near token limit. Summarize and write result.json now.\",\n\t\t\t\tused: this.used,\n\t\t\t\tlimit: this.limit,\n\t\t\t});\n\t\t}\n\n\t\tif (!this.exceeded && this.used >= this.exceededThreshold) {\n\t\t\tthis.exceeded = true;\n\t\t\tthis.emit(\"budget_exceeded\", {\n\t\t\t\ttask_id: this.task_id,\n\t\t\t\tused: this.used,\n\t\t\t\tlimit: this.limit,\n\t\t\t});\n\t\t}\n\n\t\tthis.persist();\n\t}\n}\n"]}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/** System prompt appendix for the main session when subagent tooling is enabled.
|
|
10
10
|
* Instructs the parent agent on when and how to delegate effectively. */
|
|
11
|
-
export declare const SUBAGENT_MAIN_PROMPT = "You have access to the **subagent** tool. Use it to delegate self-contained tasks to isolated subagent loops that run with their own context and return only their final answer.\n\nAvailable subagent modes:\n- explore: read-only investigation (read, grep, find, ls, bash).\n- edit: make a focused code change (read, edit, write, grep, find, ls, bash).\n- test: run tests and report (read, bash, grep, find, ls).\n- fix: diagnose and fix a failure (read, edit, write, bash, grep, find, ls).\n- review: read-only code review (read, grep, find, ls, bash).\n- doc: write documentation, README, or comments (read, write, edit, grep, find, ls, bash).\n\nWhen to delegate:\n1. The work is self-contained and you only need the final result, not intermediate steps.\n2. You want to investigate or edit something in parallel without losing your current context or reasoning chain.\n3. The task is a discrete unit (explore one module, run one test file, review one PR, fix one isolated bug).\n4. You need to run a long command or test suite and wait for its output without blocking your own reasoning.\n\nGuidelines:\n- Make every task specific and self-contained. The subagent cannot see this conversation.\n- Pass all necessary context (files, constraints, prior findings) via the `context` parameter.\n- Do NOT delegate tasks that require tight back-and-forth with your current reasoning.\n- Do NOT delegate edits to files you are actively reasoning about.\n- The subagent returns ONLY its final answer. Intermediate reasoning, tool calls, and output are hidden from you.\n\
|
|
11
|
+
export declare const SUBAGENT_MAIN_PROMPT = "You have access to the **subagent** tool. Use it to delegate self-contained tasks to isolated subagent loops that run with their own context and return only their final answer.\n\nAvailable subagent modes:\n- explore: read-only investigation (read, grep, find, ls, bash).\n- edit: make a focused code change (read, edit, write, grep, find, ls, bash).\n- test: run tests and report (read, bash, grep, find, ls).\n- fix: diagnose and fix a failure (read, edit, write, bash, grep, find, ls).\n- review: read-only code review (read, grep, find, ls, bash).\n- doc: write documentation, README, or comments (read, write, edit, grep, find, ls, bash).\n\nWhen to delegate:\n1. The work is self-contained and you only need the final result, not intermediate steps.\n2. You want to investigate or edit something in parallel without losing your current context or reasoning chain.\n3. The task is a discrete unit (explore one module, run one test file, review one PR, fix one isolated bug).\n4. You need to run a long command or test suite and wait for its output without blocking your own reasoning.\n\nGuidelines:\n- Make every task specific and self-contained. The subagent cannot see this conversation.\n- Pass all necessary context (files, constraints, prior findings) via the `context` parameter.\n- Do NOT delegate tasks that require tight back-and-forth with your current reasoning.\n- Do NOT delegate edits to files you are actively reasoning about.\n- The subagent returns ONLY its final answer. Intermediate reasoning, tool calls, and output are hidden from you.\n\nChoosing inline vs subagent:\n- Default to handling work inline. Delegate only when one of the \"When to delegate\" cases clearly applies.\n- Handle inline (do NOT delegate): quick lookups, single-file reads, edits under ~50 lines, anything needing tight back-and-forth with your current reasoning, and follow-ups on files you are actively reasoning about.\n- When you do call subagent, a deterministic dispatch evaluator confirms the task is worth delegating and selects the mode for you. Pass the mode you think fits; it is corrected automatically if a better match is detected.\n- Use force=true to bypass evaluation only when you are certain a subagent is required.";
|
|
12
12
|
import type { ToolDefinition } from "../extensions/types.js";
|
|
13
13
|
import type { SubagentMode } from "../subagent.js";
|
|
14
14
|
export interface SubagentToolDetails {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent.d.ts","sourceRoot":"","sources":["../../../src/core/tools/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;0EAC0E;AAC1E,eAAO,MAAM,oBAAoB,82DA0BkD,CAAC;AAKpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAqCnD,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAgBD,sDAAsD;AACtD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED,oFAAoF;AACpF,wBAAgB,4BAA4B,IAAI,cAAc,CAuF7D","sourcesContent":["/**\n * Subagent tool: delegate a focused task to a fresh, isolated agent loop.\n *\n * The tool registers a task in the shared task store (visible in the task panel),\n * runs the subagent to completion, and returns ONLY the subagent's final\n * answer. It is an optional, opt-in tool (enabled via --subagent or the\n * `enableSubagent` setting); see buildSessionOptions in main.ts.\n */\n\n/** System prompt appendix for the main session when subagent tooling is enabled.\n * Instructs the parent agent on when and how to delegate effectively. */\nexport const SUBAGENT_MAIN_PROMPT = `You have access to the **subagent** tool. Use it to delegate self-contained tasks to isolated subagent loops that run with their own context and return only their final answer.\n\nAvailable subagent modes:\n- explore: read-only investigation (read, grep, find, ls, bash).\n- edit: make a focused code change (read, edit, write, grep, find, ls, bash).\n- test: run tests and report (read, bash, grep, find, ls).\n- fix: diagnose and fix a failure (read, edit, write, bash, grep, find, ls).\n- review: read-only code review (read, grep, find, ls, bash).\n- doc: write documentation, README, or comments (read, write, edit, grep, find, ls, bash).\n\nWhen to delegate:\n1. The work is self-contained and you only need the final result, not intermediate steps.\n2. You want to investigate or edit something in parallel without losing your current context or reasoning chain.\n3. The task is a discrete unit (explore one module, run one test file, review one PR, fix one isolated bug).\n4. You need to run a long command or test suite and wait for its output without blocking your own reasoning.\n\nGuidelines:\n- Make every task specific and self-contained. The subagent cannot see this conversation.\n- Pass all necessary context (files, constraints, prior findings) via the \\`context\\` parameter.\n- Do NOT delegate tasks that require tight back-and-forth with your current reasoning.\n- Do NOT delegate edits to files you are actively reasoning about.\n- The subagent returns ONLY its final answer. Intermediate reasoning, tool calls, and output are hidden from you.\n\nDispatch evaluator:\n- The dispatch evaluator determines if a subagent is needed. Do not spawn subagents directly unless the user explicitly requests it.\n- For simple single-file changes (<50 lines, read-only or trivial edit), handle them inline.\n- Use force=true to bypass evaluation when you are certain a subagent is required.`;\n\nimport { Text } from \"@kolisachint/hoocode-tui\";\nimport { type Static, Type } from \"typebox\";\nimport { DispatchEvaluator } from \"../dispatch-evaluator.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { defineTool } from \"../extensions/types.js\";\nimport type { SubagentMode } from \"../subagent.js\";\nimport { getSubagentPool } from \"../subagent-pool-instance.js\";\nimport type { SubagentResultFile } from \"../subagent-result.js\";\nimport { taskStore } from \"../task-store.js\";\n\nconst subagentParams = Type.Object({\n\ttask: Type.String({\n\t\tdescription:\n\t\t\t\"The task to delegate. Make it specific and self-contained; the subagent cannot see this conversation.\",\n\t}),\n\tcontext: Type.String({\n\t\tdescription:\n\t\t\t'Context distilled from the conversation the subagent needs (files, constraints, prior findings). Pass \"\" if none.',\n\t}),\n\tmode: Type.Union(\n\t\t[\n\t\t\tType.Literal(\"explore\"),\n\t\t\tType.Literal(\"edit\"),\n\t\t\tType.Literal(\"test\"),\n\t\t\tType.Literal(\"fix\"),\n\t\t\tType.Literal(\"review\"),\n\t\t\tType.Literal(\"doc\"),\n\t\t],\n\t\t{\n\t\t\tdescription:\n\t\t\t\t\"explore: read-only investigation. edit: make a focused code change. test: run tests and report. fix: diagnose and fix a failure. review: read-only code review. doc: write documentation.\",\n\t\t},\n\t),\n\tforce: Type.Boolean({\n\t\tdescription:\n\t\t\t\"Bypass dispatch evaluation and spawn the subagent directly. Use when you are certain a subagent is required.\",\n\t\tdefault: false,\n\t}),\n});\n\ntype SubagentParams = Static<typeof subagentParams>;\n\nexport interface SubagentToolDetails {\n\tmode: SubagentMode;\n\tok: boolean;\n\terror?: string;\n\ttaskId: number;\n\t/** True when the evaluator handled the task inline instead of delegating. */\n\tinline?: boolean;\n}\n\n/**\n * A short, human-readable task name for the task panel: the first line of the\n * task limited to ~4–8 words so it stays glanceable in the pane. A character cap\n * guards against a single very long word.\n */\nfunction summarize(task: string): string {\n\tconst firstLine = (task.trim().split(\"\\n\")[0] ?? \"\").trim();\n\tif (!firstLine) return \"(task)\";\n\tconst words = firstLine.split(/\\s+/);\n\tlet name = words.length > 8 ? `${words.slice(0, 8).join(\" \")}…` : firstLine;\n\tif (name.length > 60) name = `${name.slice(0, 59)}…`;\n\treturn name;\n}\n\n/** Quick check: should this task go to a subagent? */\nexport function isSubagentRecommended(task: string): boolean {\n\tconst evaluator = new DispatchEvaluator();\n\treturn evaluator.evaluate(task).should_delegate;\n}\n\n/** Create the subagent tool definition. Registered as a customTool when enabled. */\nexport function createSubagentToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof subagentParams, SubagentToolDetails>({\n\t\tname: \"subagent\",\n\t\tlabel: \"Subagent\",\n\t\tdescription: [\n\t\t\t\"Delegate a focused task to a subagent that runs in a fresh, isolated context (it cannot see this conversation).\",\n\t\t\t\"Pass everything it needs via `context`. The subagent returns only its final answer.\",\n\t\t\t\"Modes: explore, edit, test, fix, review, doc.\",\n\t\t\t\"WHEN TO USE: (1) The work is self-contained and you do not need to see intermediate steps — only the final result.\",\n\t\t\t\"(2) You want to investigate or edit something in parallel without losing your current context or reasoning chain.\",\n\t\t\t\"(3) The task is a discrete unit (explore one module, run one test file, review one PR, fix one isolated bug, write docs).\",\n\t\t\t\"(4) You need to run a long command or test suite and wait for its output without blocking your own reasoning.\",\n\t\t\t\"Do NOT use for tasks that require tight back-and-forth with your current reasoning or that change files you are actively reasoning about.\",\n\t\t\t\"Use force=true to bypass dispatch evaluation when you are certain a subagent is required.\",\n\t\t].join(\" \"),\n\t\tpromptSnippet: \"delegate a self-contained task to an isolated subagent (modes: explore/edit/test/fix/review/doc)\",\n\t\tparameters: subagentParams,\n\n\t\tasync execute(_toolCallId, params: SubagentParams, _signal, _onUpdate, ctx) {\n\t\t\tconst forcedMode = params.mode as SubagentMode;\n\n\t\t\t// Dispatch evaluation: a single evaluator/analysis decides inline vs delegate\n\t\t\t// and (when not forced) which mode to use.\n\t\t\tconst evaluator = new DispatchEvaluator();\n\t\t\tconst analysis = evaluator.evaluate(params.task);\n\t\t\tif (!params.force && !analysis.should_delegate) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Task is simple enough for inline handling. Reason: ${analysis.reason}. Use force=true for subagent override.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { mode: forcedMode, ok: true, taskId: 0, inline: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst mode: SubagentMode = params.force ? forcedMode : ((analysis.agent_type as SubagentMode) ?? \"explore\");\n\t\t\tconst summary = summarize(params.task);\n\n\t\t\tconst task = taskStore.create(summary, { subagentMode: mode });\n\t\t\ttaskStore.update(task.id, { status: \"in_progress\" });\n\t\t\ttry {\n\t\t\t\tconst pool = getSubagentPool(ctx.cwd);\n\t\t\t\tconst dispatchResult = await pool.dispatch(params.task, {\n\t\t\t\t\tforceAgent: mode,\n\t\t\t\t\tcontext: params.context,\n\t\t\t\t\tmodel: ctx.model?.id,\n\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t});\n\n\t\t\t\tconst result = dispatchResult.result;\n\t\t\t\tconst resultData = result?.result_data as SubagentResultFile | undefined;\n\t\t\t\tconst usage = resultData?.usage;\n\n\t\t\t\tif (!result || !result.ok) {\n\t\t\t\t\t// Signal failure by throwing: the agent loop derives a tool's error\n\t\t\t\t\t// state from a thrown error, not from a returned flag.\n\t\t\t\t\ttaskStore.update(task.id, { status: \"failed\", usage });\n\t\t\t\t\tthrow new Error(`Subagent (${mode}) failed: ${result?.error ?? \"unknown error\"}`);\n\t\t\t\t}\n\n\t\t\t\t// Leave the task in the store with its final status. It stays visible in\n\t\t\t\t// the task panel until the next user message arrives (retireFinished is\n\t\t\t\t// called when the user starts the next turn).\n\t\t\t\ttaskStore.update(task.id, { status: \"done\", usage });\n\t\t\t\tconst answer = resultData?.summary || \"(subagent returned no output)\";\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: answer }],\n\t\t\t\t\tdetails: { mode, ok: true, taskId: task.id },\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\ttaskStore.update(task.id, { status: \"failed\" });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\n\t\trenderCall(args, theme) {\n\t\t\tconst mode = args.mode ?? \"explore\";\n\t\t\tconst preview = summarize(args.task ?? \"\");\n\t\t\tconst text =\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(\"subagent \")) +\n\t\t\t\ttheme.fg(\"accent\", `[${mode}]`) +\n\t\t\t\ttheme.fg(\"dim\", ` ${preview}`);\n\t\t\treturn new Text(text, 0, 0);\n\t\t},\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"subagent.d.ts","sourceRoot":"","sources":["../../../src/core/tools/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;0EAC0E;AAC1E,eAAO,MAAM,oBAAoB,gsEA2BuD,CAAC;AAKzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAqCnD,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAgBD,sDAAsD;AACtD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED,oFAAoF;AACpF,wBAAgB,4BAA4B,IAAI,cAAc,CA+F7D","sourcesContent":["/**\n * Subagent tool: delegate a focused task to a fresh, isolated agent loop.\n *\n * The tool registers a task in the shared task store (visible in the task panel),\n * runs the subagent to completion, and returns ONLY the subagent's final\n * answer. It is an optional, opt-in tool (enabled via --subagent or the\n * `enableSubagent` setting); see buildSessionOptions in main.ts.\n */\n\n/** System prompt appendix for the main session when subagent tooling is enabled.\n * Instructs the parent agent on when and how to delegate effectively. */\nexport const SUBAGENT_MAIN_PROMPT = `You have access to the **subagent** tool. Use it to delegate self-contained tasks to isolated subagent loops that run with their own context and return only their final answer.\n\nAvailable subagent modes:\n- explore: read-only investigation (read, grep, find, ls, bash).\n- edit: make a focused code change (read, edit, write, grep, find, ls, bash).\n- test: run tests and report (read, bash, grep, find, ls).\n- fix: diagnose and fix a failure (read, edit, write, bash, grep, find, ls).\n- review: read-only code review (read, grep, find, ls, bash).\n- doc: write documentation, README, or comments (read, write, edit, grep, find, ls, bash).\n\nWhen to delegate:\n1. The work is self-contained and you only need the final result, not intermediate steps.\n2. You want to investigate or edit something in parallel without losing your current context or reasoning chain.\n3. The task is a discrete unit (explore one module, run one test file, review one PR, fix one isolated bug).\n4. You need to run a long command or test suite and wait for its output without blocking your own reasoning.\n\nGuidelines:\n- Make every task specific and self-contained. The subagent cannot see this conversation.\n- Pass all necessary context (files, constraints, prior findings) via the \\`context\\` parameter.\n- Do NOT delegate tasks that require tight back-and-forth with your current reasoning.\n- Do NOT delegate edits to files you are actively reasoning about.\n- The subagent returns ONLY its final answer. Intermediate reasoning, tool calls, and output are hidden from you.\n\nChoosing inline vs subagent:\n- Default to handling work inline. Delegate only when one of the \"When to delegate\" cases clearly applies.\n- Handle inline (do NOT delegate): quick lookups, single-file reads, edits under ~50 lines, anything needing tight back-and-forth with your current reasoning, and follow-ups on files you are actively reasoning about.\n- When you do call subagent, a deterministic dispatch evaluator confirms the task is worth delegating and selects the mode for you. Pass the mode you think fits; it is corrected automatically if a better match is detected.\n- Use force=true to bypass evaluation only when you are certain a subagent is required.`;\n\nimport { Text } from \"@kolisachint/hoocode-tui\";\nimport { type Static, Type } from \"typebox\";\nimport { DispatchEvaluator } from \"../dispatch-evaluator.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { defineTool } from \"../extensions/types.js\";\nimport type { SubagentMode } from \"../subagent.js\";\nimport { getSubagentPool } from \"../subagent-pool-instance.js\";\nimport type { SubagentResultFile } from \"../subagent-result.js\";\nimport { taskStore } from \"../task-store.js\";\n\nconst subagentParams = Type.Object({\n\ttask: Type.String({\n\t\tdescription:\n\t\t\t\"The task to delegate. Make it specific and self-contained; the subagent cannot see this conversation.\",\n\t}),\n\tcontext: Type.String({\n\t\tdescription:\n\t\t\t'Context distilled from the conversation the subagent needs (files, constraints, prior findings). Pass \"\" if none.',\n\t}),\n\tmode: Type.Union(\n\t\t[\n\t\t\tType.Literal(\"explore\"),\n\t\t\tType.Literal(\"edit\"),\n\t\t\tType.Literal(\"test\"),\n\t\t\tType.Literal(\"fix\"),\n\t\t\tType.Literal(\"review\"),\n\t\t\tType.Literal(\"doc\"),\n\t\t],\n\t\t{\n\t\t\tdescription:\n\t\t\t\t\"explore: read-only investigation. edit: make a focused code change. test: run tests and report. fix: diagnose and fix a failure. review: read-only code review. doc: write documentation.\",\n\t\t},\n\t),\n\tforce: Type.Boolean({\n\t\tdescription:\n\t\t\t\"Bypass dispatch evaluation and spawn the subagent directly. Use when you are certain a subagent is required.\",\n\t\tdefault: false,\n\t}),\n});\n\ntype SubagentParams = Static<typeof subagentParams>;\n\nexport interface SubagentToolDetails {\n\tmode: SubagentMode;\n\tok: boolean;\n\terror?: string;\n\ttaskId: number;\n\t/** True when the evaluator handled the task inline instead of delegating. */\n\tinline?: boolean;\n}\n\n/**\n * A short, human-readable task name for the task panel: the first line of the\n * task limited to ~4–8 words so it stays glanceable in the pane. A character cap\n * guards against a single very long word.\n */\nfunction summarize(task: string): string {\n\tconst firstLine = (task.trim().split(\"\\n\")[0] ?? \"\").trim();\n\tif (!firstLine) return \"(task)\";\n\tconst words = firstLine.split(/\\s+/);\n\tlet name = words.length > 8 ? `${words.slice(0, 8).join(\" \")}…` : firstLine;\n\tif (name.length > 60) name = `${name.slice(0, 59)}…`;\n\treturn name;\n}\n\n/** Quick check: should this task go to a subagent? */\nexport function isSubagentRecommended(task: string): boolean {\n\tconst evaluator = new DispatchEvaluator();\n\treturn evaluator.evaluate(task).should_delegate;\n}\n\n/** Create the subagent tool definition. Registered as a customTool when enabled. */\nexport function createSubagentToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof subagentParams, SubagentToolDetails>({\n\t\tname: \"subagent\",\n\t\tlabel: \"Subagent\",\n\t\tdescription: [\n\t\t\t\"Delegate a focused task to a subagent that runs in a fresh, isolated context (it cannot see this conversation).\",\n\t\t\t\"Pass everything it needs via `context`. The subagent returns only its final answer.\",\n\t\t\t\"Modes: explore, edit, test, fix, review, doc.\",\n\t\t\t\"WHEN TO USE: (1) The work is self-contained and you do not need to see intermediate steps — only the final result.\",\n\t\t\t\"(2) You want to investigate or edit something in parallel without losing your current context or reasoning chain.\",\n\t\t\t\"(3) The task is a discrete unit (explore one module, run one test file, review one PR, fix one isolated bug, write docs).\",\n\t\t\t\"(4) You need to run a long command or test suite and wait for its output without blocking your own reasoning.\",\n\t\t\t\"Do NOT use for tasks that require tight back-and-forth with your current reasoning or that change files you are actively reasoning about.\",\n\t\t\t\"Prefer handling small, quick, or single-file tasks yourself; delegate only self-contained units of work.\",\n\t\t\t\"The dispatch evaluator confirms whether delegation is warranted and picks the mode; force=true bypasses it when you are certain a subagent is required.\",\n\t\t].join(\" \"),\n\t\tpromptSnippet: \"delegate a self-contained task to an isolated subagent (modes: explore/edit/test/fix/review/doc)\",\n\t\tparameters: subagentParams,\n\n\t\tasync execute(_toolCallId, params: SubagentParams, _signal, _onUpdate, ctx) {\n\t\t\tconst forcedMode = params.mode as SubagentMode;\n\n\t\t\t// Dispatch evaluation: a single evaluator/analysis decides inline vs delegate\n\t\t\t// and (when not forced) which mode to use.\n\t\t\tconst evaluator = new DispatchEvaluator();\n\t\t\tconst analysis = evaluator.evaluate(params.task);\n\t\t\tif (!params.force && !analysis.should_delegate) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Task is simple enough for inline handling. Reason: ${analysis.reason}. Use force=true for subagent override.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { mode: forcedMode, ok: true, taskId: 0, inline: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst mode: SubagentMode = params.force ? forcedMode : ((analysis.agent_type as SubagentMode) ?? \"explore\");\n\t\t\tconst summary = summarize(params.task);\n\n\t\t\tconst task = taskStore.create(summary, { subagentMode: mode });\n\t\t\ttaskStore.update(task.id, { status: \"in_progress\" });\n\t\t\ttry {\n\t\t\t\tconst pool = getSubagentPool(ctx.cwd);\n\t\t\t\tconst dispatchResult = await pool.dispatch(params.task, {\n\t\t\t\t\tforceAgent: mode,\n\t\t\t\t\tcontext: params.context,\n\t\t\t\t\tmodel: ctx.model?.id,\n\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t});\n\n\t\t\t\tconst result = dispatchResult.result;\n\t\t\t\tconst resultData = result?.result_data as SubagentResultFile | undefined;\n\t\t\t\tconst usage = resultData?.usage;\n\n\t\t\t\tif (!result || !result.ok) {\n\t\t\t\t\t// Signal failure by throwing: the agent loop derives a tool's error\n\t\t\t\t\t// state from a thrown error, not from a returned flag.\n\t\t\t\t\ttaskStore.update(task.id, { status: \"failed\", usage });\n\t\t\t\t\tconst reason =\n\t\t\t\t\t\tresult?.error ??\n\t\t\t\t\t\t(result?.budget_exceeded\n\t\t\t\t\t\t\t? \"token budget exceeded before producing a result\"\n\t\t\t\t\t\t\t: result?.status\n\t\t\t\t\t\t\t\t? `subagent ${result.status}`\n\t\t\t\t\t\t\t\t: \"unknown error\");\n\t\t\t\t\tthrow new Error(`Subagent (${mode}) failed: ${reason}`);\n\t\t\t\t}\n\n\t\t\t\t// Leave the task in the store with its final status. It stays visible in\n\t\t\t\t// the task panel until the next user message arrives (taskStore.reset is\n\t\t\t\t// called when the user starts the next turn).\n\t\t\t\ttaskStore.update(task.id, { status: \"done\", usage });\n\t\t\t\tconst answer = resultData?.summary || \"(subagent returned no output)\";\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: answer }],\n\t\t\t\t\tdetails: { mode, ok: true, taskId: task.id },\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\ttaskStore.update(task.id, { status: \"failed\" });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\n\t\trenderCall(args, theme) {\n\t\t\tconst mode = args.mode ?? \"explore\";\n\t\t\tconst preview = summarize(args.task ?? \"\");\n\t\t\tconst text =\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(\"subagent \")) +\n\t\t\t\ttheme.fg(\"accent\", `[${mode}]`) +\n\t\t\t\ttheme.fg(\"dim\", ` ${preview}`);\n\t\t\treturn new Text(text, 0, 0);\n\t\t},\n\t});\n}\n"]}
|