@earendil-works/pi-agent-core 0.79.9 → 0.80.1
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/README.md +0 -18
- package/dist/agent-loop.d.ts +1 -1
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +1 -1
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +1 -1
- package/dist/agent.js.map +1 -1
- package/dist/harness/agent-harness.d.ts +2 -2
- package/dist/harness/agent-harness.d.ts.map +1 -1
- package/dist/harness/agent-harness.js +6 -30
- package/dist/harness/agent-harness.js.map +1 -1
- package/dist/harness/compaction/branch-summarization.d.ts +3 -5
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/harness/compaction/branch-summarization.js +2 -3
- package/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/dist/harness/compaction/compaction.d.ts +4 -4
- package/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/dist/harness/compaction/compaction.js +17 -15
- package/dist/harness/compaction/compaction.js.map +1 -1
- package/dist/harness/compaction/utils.d.ts +1 -1
- package/dist/harness/compaction/utils.d.ts.map +1 -1
- package/dist/harness/compaction/utils.js.map +1 -1
- package/dist/harness/messages.d.ts +1 -1
- package/dist/harness/messages.d.ts.map +1 -1
- package/dist/harness/messages.js.map +1 -1
- package/dist/harness/session/session.d.ts +1 -1
- package/dist/harness/session/session.d.ts.map +1 -1
- package/dist/harness/session/session.js +2 -1
- package/dist/harness/session/session.js.map +1 -1
- package/dist/harness/types.d.ts +8 -6
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts +19 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -4
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +1 -1
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +1 -1
- package/dist/proxy.js.map +1 -1
- package/dist/types.d.ts +4 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -6
- package/dist/base.d.ts +0 -20
- package/dist/base.d.ts.map +0 -1
- package/dist/base.js +0 -20
- package/dist/base.js.map +0 -1
package/dist/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAKN,YAAY,GAIZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAoBrE,SAAS,mBAAmB,CAAC,QAAwB,EAAa;IACjE,OAAO,QAAQ,CAAC,MAAM,CACrB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,CACrG,CAAC;AAAA,CACF;AAED,MAAM,WAAW,GAAG;IACnB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,CAAC;IACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;CACpE,CAAC;AAEF,MAAM,aAAa,GAAG;IACrB,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,SAAS;IACf,GAAG,EAAE,SAAS;IACd,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,EAAE;IACX,SAAS,EAAE,KAAK;IAChB,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;IAC1D,aAAa,EAAE,CAAC;IAChB,SAAS,EAAE,CAAC;CACS,CAAC;AASvB,SAAS,uBAAuB,CAC/B,YAAkH,EAC9F;IACpB,IAAI,KAAK,GAAG,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAI,QAAQ,GAAG,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAErD,OAAO;QACN,YAAY,EAAE,YAAY,EAAE,YAAY,IAAI,EAAE;QAC9C,KAAK,EAAE,YAAY,EAAE,KAAK,IAAI,aAAa;QAC3C,aAAa,EAAE,YAAY,EAAE,aAAa,IAAI,KAAK;QACnD,IAAI,KAAK,GAAG;YACX,OAAO,KAAK,CAAC;QAAA,CACb;QACD,IAAI,KAAK,CAAC,SAA2B,EAAE;YACtC,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC;QAAA,CAC1B;QACD,IAAI,QAAQ,GAAG;YACd,OAAO,QAAQ,CAAC;QAAA,CAChB;QACD,IAAI,QAAQ,CAAC,YAA4B,EAAE;YAC1C,QAAQ,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC;QAAA,CAChC;QACD,WAAW,EAAE,KAAK;QAClB,gBAAgB,EAAE,SAAS;QAC3B,gBAAgB,EAAE,IAAI,GAAG,EAAU;QACnC,YAAY,EAAE,SAAS;KACvB,CAAC;AAAA,CACF;AAyBD,MAAM,mBAAmB;IAChB,QAAQ,GAAmB,EAAE,CAAC;IAC/B,IAAI,CAAY;IAEvB,YAAY,IAAe,EAAE;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CACjB;IAED,OAAO,CAAC,OAAqB,EAAQ;QACpC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAAA,CAC5B;IAED,QAAQ,GAAY;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAAA,CAChC;IAED,KAAK,GAAmB;QACvB,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;YACnB,OAAO,OAAO,CAAC;QAChB,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACX,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACvC,OAAO,CAAC,KAAK,CAAC,CAAC;IAAA,CACf;IAED,KAAK,GAAS;QACb,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IAAA,CACnB;CACD;AAQD;;;;;GAKG;AACH,MAAM,OAAO,KAAK;IACT,MAAM,CAAoB;IACjB,SAAS,GAAG,IAAI,GAAG,EAAoE,CAAC;IACxF,aAAa,CAAsB;IACnC,aAAa,CAAsB;IAE7C,YAAY,CAA+D;IAC3E,gBAAgB,CAA+E;IAC/F,QAAQ,CAAW;IACnB,SAAS,CAA0E;IACnF,SAAS,CAAoC;IAC7C,UAAU,CAAqC;IAC/C,cAAc,CAG0B;IACxC,aAAa,CAG0B;IACvC,eAAe,CAE0D;IACxE,SAAS,CAAa;IAC9B,0EAA0E;IACnE,SAAS,CAAU;IAC1B,kFAAkF;IAC3E,eAAe,CAAmB;IACzC,4DAA4D;IACrD,SAAS,CAAY;IAC5B,wDAAwD;IACjD,eAAe,CAAU;IAChC,uFAAuF;IAChF,aAAa,CAAoB;IAExC,YAAY,OAAO,GAAiB,EAAE,EAAE;QACvC,IAAI,CAAC,MAAM,GAAG,uBAAuB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC5D,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,mBAAmB,CAAC;QAChE,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;QACjD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,YAAY,CAAC;QACjD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,aAAa,GAAG,IAAI,mBAAmB,CAAC,OAAO,CAAC,YAAY,IAAI,eAAe,CAAC,CAAC;QACtF,IAAI,CAAC,aAAa,GAAG,IAAI,mBAAmB,CAAC,OAAO,CAAC,YAAY,IAAI,eAAe,CAAC,CAAC;QACtF,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;QAC7C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,UAAU,CAAC;IAAA,CACzD;IAED;;;;;;;;;OASG;IACH,SAAS,CAAC,QAA0E,EAAc;QACjG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAAA,CAC7C;IAED;;;;OAIG;IACH,IAAI,KAAK,GAAe;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC;IAAA,CACnB;IAED,yDAAyD;IACzD,IAAI,YAAY,CAAC,IAAe,EAAE;QACjC,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CAC/B;IAED,IAAI,YAAY,GAAc;QAC7B,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;IAAA,CAC/B;IAED,0DAA0D;IAC1D,IAAI,YAAY,CAAC,IAAe,EAAE;QACjC,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CAC/B;IAED,IAAI,YAAY,GAAc;QAC7B,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;IAAA,CAC/B;IAED,gFAAgF;IAChF,KAAK,CAAC,OAAqB,EAAQ;QAClC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAAA,CACpC;IAED,wEAAwE;IACxE,QAAQ,CAAC,OAAqB,EAAQ;QACrC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAAA,CACpC;IAED,2CAA2C;IAC3C,kBAAkB,GAAS;QAC1B,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;IAAA,CAC3B;IAED,4CAA4C;IAC5C,kBAAkB,GAAS;QAC1B,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;IAAA,CAC3B;IAED,yDAAyD;IACzD,cAAc,GAAS;QACtB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAAA,CAC1B;IAED,sEAAsE;IACtE,iBAAiB,GAAY;QAC5B,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;IAAA,CACtE;IAED,uDAAuD;IACvD,IAAI,MAAM,GAA4B;QACrC,OAAO,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC;IAAA,CAC9C;IAED,+CAA+C;IAC/C,KAAK,GAAS;QACb,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,KAAK,EAAE,CAAC;IAAA,CACxC;IAED;;;;OAIG;IACH,WAAW,GAAkB;QAC5B,OAAO,IAAI,CAAC,SAAS,EAAE,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAAA,CACpD;IAED,kEAAkE;IAClE,KAAK,GAAS;QACb,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,SAAS,CAAC;QACrC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAAA,CAC1B;IAKD,KAAK,CAAC,MAAM,CAAC,KAA6C,EAAE,MAAuB,EAAiB;QACnG,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACd,4GAA4G,CAC5G,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAAA,CACvC;IAED,oGAAoG;IACpG,KAAK,CAAC,QAAQ,GAAkB;QAC/B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;QACxF,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC1E,IAAI,CAAC,WAAW,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,WAAW,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACtC,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;YAClD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChF,OAAO;YACR,CAAC;YAED,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;YACnD,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;gBAC9C,OAAO;YACR,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;IAAA,CAC7B;IAEO,oBAAoB,CAC3B,KAA6C,EAC7C,MAAuB,EACN;QACjB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;QAED,MAAM,OAAO,GAAsC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACnF,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAAA,CAC1D;IAEO,KAAK,CAAC,iBAAiB,CAC9B,QAAwB,EACxB,OAAO,GAA0C,EAAE,EACnC;QAChB,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;YAC7C,MAAM,YAAY,CACjB,QAAQ,EACR,IAAI,CAAC,qBAAqB,EAAE,EAC5B,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAC9B,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EACpC,MAAM,EACN,IAAI,CAAC,QAAQ,CACb,CAAC;QAAA,CACF,CAAC,CAAC;IAAA,CACH;IAEO,KAAK,CAAC,eAAe,GAAkB;QAC9C,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;YAC7C,MAAM,oBAAoB,CACzB,IAAI,CAAC,qBAAqB,EAAE,EAC5B,IAAI,CAAC,gBAAgB,EAAE,EACvB,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EACpC,MAAM,EACN,IAAI,CAAC,QAAQ,CACb,CAAC;QAAA,CACF,CAAC,CAAC;IAAA,CACH;IAEO,qBAAqB,GAAiB;QAC7C,OAAO;YACN,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;YACtC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE;YACtC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE;SAChC,CAAC;IAAA,CACF;IAEO,gBAAgB,CAAC,OAAO,GAA0C,EAAE,EAAmB;QAC9F,IAAI,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,KAAK,IAAI,CAAC;QACvE,OAAO;YACN,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa;YACtF,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;YACzG,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,mBAAmB,EAAE,KAAK,IAAI,EAAE,CAAC;gBAChC,IAAI,uBAAuB,EAAE,CAAC;oBAC7B,uBAAuB,GAAG,KAAK,CAAC;oBAChC,OAAO,EAAE,CAAC;gBACX,CAAC;gBACD,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;YAAA,CAClC;YACD,mBAAmB,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE;SAC3D,CAAC;IAAA,CACF;IAEO,KAAK,CAAC,gBAAgB,CAAC,QAAgD,EAAiB;QAC/F,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,IAAI,cAAc,GAAG,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9C,cAAc,GAAG,OAAO,CAAC;QAAA,CACzB,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC;QAEvE,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,SAAS,CAAC;QAErC,IAAI,CAAC;YACJ,MAAM,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpE,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,SAAS,EAAE,CAAC;QAClB,CAAC;IAAA,CACD;IAEO,KAAK,CAAC,gBAAgB,CAAC,KAAc,EAAE,OAAgB,EAAiB;QAC/E,MAAM,cAAc,GAAG;YACtB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACrC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG;YAC1B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ;YACpC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC3B,KAAK,EAAE,WAAW;YAClB,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;YACzC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YACpE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACE,CAAC;QACzB,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;QAC7E,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;QAC3E,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;QACzF,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;IAAA,CAC5E;IAEO,SAAS,GAAS;QACzB,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;QACjD,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAAA,CAC3B;IAED;;;;;;OAMG;IACK,KAAK,CAAC,aAAa,CAAC,KAAiB,EAAiB;QAC7D,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,eAAe;gBACnB,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC7C,MAAM;YAEP,KAAK,gBAAgB;gBACpB,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC7C,MAAM;YAEP,KAAK,aAAa;gBACjB,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC;gBACzC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACzC,MAAM;YAEP,KAAK,sBAAsB,EAAE,CAAC;gBAC7B,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBAC/D,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACvC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;gBAChD,MAAM;YACP,CAAC;YAED,KAAK,oBAAoB,EAAE,CAAC;gBAC3B,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBAC/D,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAC1C,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;gBAChD,MAAM;YACP,CAAC;YAED,KAAK,UAAU;gBACd,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;oBACtE,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;gBACvD,CAAC;gBACD,MAAM;YAEP,KAAK,WAAW;gBACf,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC;gBACzC,MAAM;QACR,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC;QACtD,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC9D,CAAC;QACD,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACvC,MAAM,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC/B,CAAC;IAAA,CACD;CACD","sourcesContent":["import {\n\ttype ImageContent,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n\ttype TextContent,\n\ttype ThinkingBudgets,\n\ttype Transport,\n} from \"@earendil-works/pi-ai/base\";\nimport { runAgentLoop, runAgentLoopContinue } from \"./agent-loop.ts\";\nimport type {\n\tAfterToolCallContext,\n\tAfterToolCallResult,\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentLoopTurnUpdate,\n\tAgentMessage,\n\tAgentState,\n\tAgentTool,\n\tBeforeToolCallContext,\n\tBeforeToolCallResult,\n\tQueueMode,\n\tStreamFn,\n\tToolExecutionMode,\n} from \"./types.ts\";\n\nexport type { QueueMode } from \"./types.ts\";\n\nfunction defaultConvertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages.filter(\n\t\t(message) => message.role === \"user\" || message.role === \"assistant\" || message.role === \"toolResult\",\n\t);\n}\n\nconst EMPTY_USAGE = {\n\tinput: 0,\n\toutput: 0,\n\tcacheRead: 0,\n\tcacheWrite: 0,\n\ttotalTokens: 0,\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n};\n\nconst DEFAULT_MODEL = {\n\tid: \"unknown\",\n\tname: \"unknown\",\n\tapi: \"unknown\",\n\tprovider: \"unknown\",\n\tbaseUrl: \"\",\n\treasoning: false,\n\tinput: [],\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\tcontextWindow: 0,\n\tmaxTokens: 0,\n} satisfies Model<any>;\n\ntype MutableAgentState = Omit<AgentState, \"isStreaming\" | \"streamingMessage\" | \"pendingToolCalls\" | \"errorMessage\"> & {\n\tisStreaming: boolean;\n\tstreamingMessage?: AgentMessage;\n\tpendingToolCalls: Set<string>;\n\terrorMessage?: string;\n};\n\nfunction createMutableAgentState(\n\tinitialState?: Partial<Omit<AgentState, \"pendingToolCalls\" | \"isStreaming\" | \"streamingMessage\" | \"errorMessage\">>,\n): MutableAgentState {\n\tlet tools = initialState?.tools?.slice() ?? [];\n\tlet messages = initialState?.messages?.slice() ?? [];\n\n\treturn {\n\t\tsystemPrompt: initialState?.systemPrompt ?? \"\",\n\t\tmodel: initialState?.model ?? DEFAULT_MODEL,\n\t\tthinkingLevel: initialState?.thinkingLevel ?? \"off\",\n\t\tget tools() {\n\t\t\treturn tools;\n\t\t},\n\t\tset tools(nextTools: AgentTool<any>[]) {\n\t\t\ttools = nextTools.slice();\n\t\t},\n\t\tget messages() {\n\t\t\treturn messages;\n\t\t},\n\t\tset messages(nextMessages: AgentMessage[]) {\n\t\t\tmessages = nextMessages.slice();\n\t\t},\n\t\tisStreaming: false,\n\t\tstreamingMessage: undefined,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terrorMessage: undefined,\n\t};\n}\n\n/** Options for constructing an {@link Agent}. */\nexport interface AgentOptions {\n\tinitialState?: Partial<Omit<AgentState, \"pendingToolCalls\" | \"isStreaming\" | \"streamingMessage\" | \"errorMessage\">>;\n\tconvertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tstreamFn?: StreamFn;\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tonPayload?: SimpleStreamOptions[\"onPayload\"];\n\tonResponse?: SimpleStreamOptions[\"onResponse\"];\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\tprepareNextTurn?: (\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tsteeringMode?: QueueMode;\n\tfollowUpMode?: QueueMode;\n\tsessionId?: string;\n\tthinkingBudgets?: ThinkingBudgets;\n\ttransport?: Transport;\n\tmaxRetryDelayMs?: number;\n\ttoolExecution?: ToolExecutionMode;\n}\n\nclass PendingMessageQueue {\n\tprivate messages: AgentMessage[] = [];\n\tpublic mode: QueueMode;\n\n\tconstructor(mode: QueueMode) {\n\t\tthis.mode = mode;\n\t}\n\n\tenqueue(message: AgentMessage): void {\n\t\tthis.messages.push(message);\n\t}\n\n\thasItems(): boolean {\n\t\treturn this.messages.length > 0;\n\t}\n\n\tdrain(): AgentMessage[] {\n\t\tif (this.mode === \"all\") {\n\t\t\tconst drained = this.messages.slice();\n\t\t\tthis.messages = [];\n\t\t\treturn drained;\n\t\t}\n\n\t\tconst first = this.messages[0];\n\t\tif (!first) {\n\t\t\treturn [];\n\t\t}\n\t\tthis.messages = this.messages.slice(1);\n\t\treturn [first];\n\t}\n\n\tclear(): void {\n\t\tthis.messages = [];\n\t}\n}\n\ntype ActiveRun = {\n\tpromise: Promise<void>;\n\tresolve: () => void;\n\tabortController: AbortController;\n};\n\n/**\n * Stateful wrapper around the low-level agent loop.\n *\n * `Agent` owns the current transcript, emits lifecycle events, executes tools,\n * and exposes queueing APIs for steering and follow-up messages.\n */\nexport class Agent {\n\tprivate _state: MutableAgentState;\n\tprivate readonly listeners = new Set<(event: AgentEvent, signal: AbortSignal) => Promise<void> | void>();\n\tprivate readonly steeringQueue: PendingMessageQueue;\n\tprivate readonly followUpQueue: PendingMessageQueue;\n\n\tpublic convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\tpublic transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tpublic streamFn: StreamFn;\n\tpublic getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tpublic onPayload?: SimpleStreamOptions[\"onPayload\"];\n\tpublic onResponse?: SimpleStreamOptions[\"onResponse\"];\n\tpublic beforeToolCall?: (\n\t\tcontext: BeforeToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<BeforeToolCallResult | undefined>;\n\tpublic afterToolCall?: (\n\t\tcontext: AfterToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AfterToolCallResult | undefined>;\n\tpublic prepareNextTurn?: (\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tprivate activeRun?: ActiveRun;\n\t/** Session identifier forwarded to providers for cache-aware backends. */\n\tpublic sessionId?: string;\n\t/** Optional per-level thinking token budgets forwarded to the stream function. */\n\tpublic thinkingBudgets?: ThinkingBudgets;\n\t/** Preferred transport forwarded to the stream function. */\n\tpublic transport: Transport;\n\t/** Optional cap for provider-requested retry delays. */\n\tpublic maxRetryDelayMs?: number;\n\t/** Tool execution strategy for assistant messages that contain multiple tool calls. */\n\tpublic toolExecution: ToolExecutionMode;\n\n\tconstructor(options: AgentOptions = {}) {\n\t\tthis._state = createMutableAgentState(options.initialState);\n\t\tthis.convertToLlm = options.convertToLlm ?? defaultConvertToLlm;\n\t\tthis.transformContext = options.transformContext;\n\t\tthis.streamFn = options.streamFn ?? streamSimple;\n\t\tthis.getApiKey = options.getApiKey;\n\t\tthis.onPayload = options.onPayload;\n\t\tthis.onResponse = options.onResponse;\n\t\tthis.beforeToolCall = options.beforeToolCall;\n\t\tthis.afterToolCall = options.afterToolCall;\n\t\tthis.prepareNextTurn = options.prepareNextTurn;\n\t\tthis.steeringQueue = new PendingMessageQueue(options.steeringMode ?? \"one-at-a-time\");\n\t\tthis.followUpQueue = new PendingMessageQueue(options.followUpMode ?? \"one-at-a-time\");\n\t\tthis.sessionId = options.sessionId;\n\t\tthis.thinkingBudgets = options.thinkingBudgets;\n\t\tthis.transport = options.transport ?? \"auto\";\n\t\tthis.maxRetryDelayMs = options.maxRetryDelayMs;\n\t\tthis.toolExecution = options.toolExecution ?? \"parallel\";\n\t}\n\n\t/**\n\t * Subscribe to agent lifecycle events.\n\t *\n\t * Listener promises are awaited in subscription order and are included in\n\t * the current run's settlement. Listeners also receive the active abort\n\t * signal for the current run.\n\t *\n\t * `agent_end` is the final emitted event for a run, but the agent does not\n\t * become idle until all awaited listeners for that event have settled.\n\t */\n\tsubscribe(listener: (event: AgentEvent, signal: AbortSignal) => Promise<void> | void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\t/**\n\t * Current agent state.\n\t *\n\t * Assigning `state.tools` or `state.messages` copies the provided top-level array.\n\t */\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\t/** Controls how queued steering messages are drained. */\n\tset steeringMode(mode: QueueMode) {\n\t\tthis.steeringQueue.mode = mode;\n\t}\n\n\tget steeringMode(): QueueMode {\n\t\treturn this.steeringQueue.mode;\n\t}\n\n\t/** Controls how queued follow-up messages are drained. */\n\tset followUpMode(mode: QueueMode) {\n\t\tthis.followUpQueue.mode = mode;\n\t}\n\n\tget followUpMode(): QueueMode {\n\t\treturn this.followUpQueue.mode;\n\t}\n\n\t/** Queue a message to be injected after the current assistant turn finishes. */\n\tsteer(message: AgentMessage): void {\n\t\tthis.steeringQueue.enqueue(message);\n\t}\n\n\t/** Queue a message to run only after the agent would otherwise stop. */\n\tfollowUp(message: AgentMessage): void {\n\t\tthis.followUpQueue.enqueue(message);\n\t}\n\n\t/** Remove all queued steering messages. */\n\tclearSteeringQueue(): void {\n\t\tthis.steeringQueue.clear();\n\t}\n\n\t/** Remove all queued follow-up messages. */\n\tclearFollowUpQueue(): void {\n\t\tthis.followUpQueue.clear();\n\t}\n\n\t/** Remove all queued steering and follow-up messages. */\n\tclearAllQueues(): void {\n\t\tthis.clearSteeringQueue();\n\t\tthis.clearFollowUpQueue();\n\t}\n\n\t/** Returns true when either queue still contains pending messages. */\n\thasQueuedMessages(): boolean {\n\t\treturn this.steeringQueue.hasItems() || this.followUpQueue.hasItems();\n\t}\n\n\t/** Active abort signal for the current run, if any. */\n\tget signal(): AbortSignal | undefined {\n\t\treturn this.activeRun?.abortController.signal;\n\t}\n\n\t/** Abort the current run, if one is active. */\n\tabort(): void {\n\t\tthis.activeRun?.abortController.abort();\n\t}\n\n\t/**\n\t * Resolve when the current run and all awaited event listeners have finished.\n\t *\n\t * This resolves after `agent_end` listeners settle.\n\t */\n\twaitForIdle(): Promise<void> {\n\t\treturn this.activeRun?.promise ?? Promise.resolve();\n\t}\n\n\t/** Clear transcript state, runtime state, and queued messages. */\n\treset(): void {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.errorMessage = undefined;\n\t\tthis.clearFollowUpQueue();\n\t\tthis.clearSteeringQueue();\n\t}\n\n\t/** Start a new prompt from text, a single message, or a batch of messages. */\n\tasync prompt(message: AgentMessage | AgentMessage[]): Promise<void>;\n\tasync prompt(input: string, images?: ImageContent[]): Promise<void>;\n\tasync prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.\",\n\t\t\t);\n\t\t}\n\t\tconst messages = this.normalizePromptInput(input, images);\n\t\tawait this.runPromptMessages(messages);\n\t}\n\n\t/** Continue from the current transcript. The last message must be a user or tool-result message. */\n\tasync continue(): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\"Agent is already processing. Wait for completion before continuing.\");\n\t\t}\n\n\t\tconst lastMessage = this._state.messages[this._state.messages.length - 1];\n\t\tif (!lastMessage) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\n\t\tif (lastMessage.role === \"assistant\") {\n\t\t\tconst queuedSteering = this.steeringQueue.drain();\n\t\t\tif (queuedSteering.length > 0) {\n\t\t\t\tawait this.runPromptMessages(queuedSteering, { skipInitialSteeringPoll: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst queuedFollowUps = this.followUpQueue.drain();\n\t\t\tif (queuedFollowUps.length > 0) {\n\t\t\t\tawait this.runPromptMessages(queuedFollowUps);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t\t}\n\n\t\tawait this.runContinuation();\n\t}\n\n\tprivate normalizePromptInput(\n\t\tinput: string | AgentMessage | AgentMessage[],\n\t\timages?: ImageContent[],\n\t): AgentMessage[] {\n\t\tif (Array.isArray(input)) {\n\t\t\treturn input;\n\t\t}\n\n\t\tif (typeof input !== \"string\") {\n\t\t\treturn [input];\n\t\t}\n\n\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\tif (images && images.length > 0) {\n\t\t\tcontent.push(...images);\n\t\t}\n\t\treturn [{ role: \"user\", content, timestamp: Date.now() }];\n\t}\n\n\tprivate async runPromptMessages(\n\t\tmessages: AgentMessage[],\n\t\toptions: { skipInitialSteeringPoll?: boolean } = {},\n\t): Promise<void> {\n\t\tawait this.runWithLifecycle(async (signal) => {\n\t\t\tawait runAgentLoop(\n\t\t\t\tmessages,\n\t\t\t\tthis.createContextSnapshot(),\n\t\t\t\tthis.createLoopConfig(options),\n\t\t\t\t(event) => this.processEvents(event),\n\t\t\t\tsignal,\n\t\t\t\tthis.streamFn,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate async runContinuation(): Promise<void> {\n\t\tawait this.runWithLifecycle(async (signal) => {\n\t\t\tawait runAgentLoopContinue(\n\t\t\t\tthis.createContextSnapshot(),\n\t\t\t\tthis.createLoopConfig(),\n\t\t\t\t(event) => this.processEvents(event),\n\t\t\t\tsignal,\n\t\t\t\tthis.streamFn,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate createContextSnapshot(): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\tmessages: this._state.messages.slice(),\n\t\t\ttools: this._state.tools.slice(),\n\t\t};\n\t}\n\n\tprivate createLoopConfig(options: { skipInitialSteeringPoll?: boolean } = {}): AgentLoopConfig {\n\t\tlet skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;\n\t\treturn {\n\t\t\tmodel: this._state.model,\n\t\t\treasoning: this._state.thinkingLevel === \"off\" ? undefined : this._state.thinkingLevel,\n\t\t\tsessionId: this.sessionId,\n\t\t\tonPayload: this.onPayload,\n\t\t\tonResponse: this.onResponse,\n\t\t\ttransport: this.transport,\n\t\t\tthinkingBudgets: this.thinkingBudgets,\n\t\t\tmaxRetryDelayMs: this.maxRetryDelayMs,\n\t\t\ttoolExecution: this.toolExecution,\n\t\t\tbeforeToolCall: this.beforeToolCall,\n\t\t\tafterToolCall: this.afterToolCall,\n\t\t\tprepareNextTurn: this.prepareNextTurn ? async () => await this.prepareNextTurn?.(this.signal) : undefined,\n\t\t\tconvertToLlm: this.convertToLlm,\n\t\t\ttransformContext: this.transformContext,\n\t\t\tgetApiKey: this.getApiKey,\n\t\t\tgetSteeringMessages: async () => {\n\t\t\t\tif (skipInitialSteeringPoll) {\n\t\t\t\t\tskipInitialSteeringPoll = false;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn this.steeringQueue.drain();\n\t\t\t},\n\t\t\tgetFollowUpMessages: async () => this.followUpQueue.drain(),\n\t\t};\n\t}\n\n\tprivate async runWithLifecycle(executor: (signal: AbortSignal) => Promise<void>): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\"Agent is already processing.\");\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\t\tlet resolvePromise = () => {};\n\t\tconst promise = new Promise<void>((resolve) => {\n\t\t\tresolvePromise = resolve;\n\t\t});\n\t\tthis.activeRun = { promise, resolve: resolvePromise, abortController };\n\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.errorMessage = undefined;\n\n\t\ttry {\n\t\t\tawait executor(abortController.signal);\n\t\t} catch (error) {\n\t\t\tawait this.handleRunFailure(error, abortController.signal.aborted);\n\t\t} finally {\n\t\t\tthis.finishRun();\n\t\t}\n\t}\n\n\tprivate async handleRunFailure(error: unknown, aborted: boolean): Promise<void> {\n\t\tconst failureMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\tapi: this._state.model.api,\n\t\t\tprovider: this._state.model.provider,\n\t\t\tmodel: this._state.model.id,\n\t\t\tusage: EMPTY_USAGE,\n\t\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\t\ttimestamp: Date.now(),\n\t\t} satisfies AgentMessage;\n\t\tawait this.processEvents({ type: \"message_start\", message: failureMessage });\n\t\tawait this.processEvents({ type: \"message_end\", message: failureMessage });\n\t\tawait this.processEvents({ type: \"turn_end\", message: failureMessage, toolResults: [] });\n\t\tawait this.processEvents({ type: \"agent_end\", messages: [failureMessage] });\n\t}\n\n\tprivate finishRun(): void {\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis.activeRun?.resolve();\n\t\tthis.activeRun = undefined;\n\t}\n\n\t/**\n\t * Reduce internal state for a loop event, then await listeners.\n\t *\n\t * `agent_end` only means no further loop events will be emitted. The run is\n\t * considered idle later, after all awaited listeners for `agent_end` finish\n\t * and `finishRun()` clears runtime-owned state.\n\t */\n\tprivate async processEvents(event: AgentEvent): Promise<void> {\n\t\tswitch (event.type) {\n\t\t\tcase \"message_start\":\n\t\t\t\tthis._state.streamingMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_update\":\n\t\t\t\tthis._state.streamingMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_end\":\n\t\t\t\tthis._state.streamingMessage = undefined;\n\t\t\t\tthis._state.messages.push(event.message);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool_execution_start\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.add(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"tool_execution_end\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.delete(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"turn_end\":\n\t\t\t\tif (event.message.role === \"assistant\" && event.message.errorMessage) {\n\t\t\t\t\tthis._state.errorMessage = event.message.errorMessage;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis._state.streamingMessage = undefined;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tconst signal = this.activeRun?.abortController.signal;\n\t\tif (!signal) {\n\t\t\tthrow new Error(\"Agent listener invoked outside active run\");\n\t\t}\n\t\tfor (const listener of this.listeners) {\n\t\t\tawait listener(event, signal);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ImageContent,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n\ttype TextContent,\n\ttype ThinkingBudgets,\n\ttype Transport,\n} from \"@earendil-works/pi-ai/compat\";\nimport { runAgentLoop, runAgentLoopContinue } from \"./agent-loop.ts\";\nimport type {\n\tAfterToolCallContext,\n\tAfterToolCallResult,\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentLoopTurnUpdate,\n\tAgentMessage,\n\tAgentState,\n\tAgentTool,\n\tBeforeToolCallContext,\n\tBeforeToolCallResult,\n\tQueueMode,\n\tStreamFn,\n\tToolExecutionMode,\n} from \"./types.ts\";\n\nexport type { QueueMode } from \"./types.ts\";\n\nfunction defaultConvertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages.filter(\n\t\t(message) => message.role === \"user\" || message.role === \"assistant\" || message.role === \"toolResult\",\n\t);\n}\n\nconst EMPTY_USAGE = {\n\tinput: 0,\n\toutput: 0,\n\tcacheRead: 0,\n\tcacheWrite: 0,\n\ttotalTokens: 0,\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n};\n\nconst DEFAULT_MODEL = {\n\tid: \"unknown\",\n\tname: \"unknown\",\n\tapi: \"unknown\",\n\tprovider: \"unknown\",\n\tbaseUrl: \"\",\n\treasoning: false,\n\tinput: [],\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\tcontextWindow: 0,\n\tmaxTokens: 0,\n} satisfies Model<any>;\n\ntype MutableAgentState = Omit<AgentState, \"isStreaming\" | \"streamingMessage\" | \"pendingToolCalls\" | \"errorMessage\"> & {\n\tisStreaming: boolean;\n\tstreamingMessage?: AgentMessage;\n\tpendingToolCalls: Set<string>;\n\terrorMessage?: string;\n};\n\nfunction createMutableAgentState(\n\tinitialState?: Partial<Omit<AgentState, \"pendingToolCalls\" | \"isStreaming\" | \"streamingMessage\" | \"errorMessage\">>,\n): MutableAgentState {\n\tlet tools = initialState?.tools?.slice() ?? [];\n\tlet messages = initialState?.messages?.slice() ?? [];\n\n\treturn {\n\t\tsystemPrompt: initialState?.systemPrompt ?? \"\",\n\t\tmodel: initialState?.model ?? DEFAULT_MODEL,\n\t\tthinkingLevel: initialState?.thinkingLevel ?? \"off\",\n\t\tget tools() {\n\t\t\treturn tools;\n\t\t},\n\t\tset tools(nextTools: AgentTool<any>[]) {\n\t\t\ttools = nextTools.slice();\n\t\t},\n\t\tget messages() {\n\t\t\treturn messages;\n\t\t},\n\t\tset messages(nextMessages: AgentMessage[]) {\n\t\t\tmessages = nextMessages.slice();\n\t\t},\n\t\tisStreaming: false,\n\t\tstreamingMessage: undefined,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terrorMessage: undefined,\n\t};\n}\n\n/** Options for constructing an {@link Agent}. */\nexport interface AgentOptions {\n\tinitialState?: Partial<Omit<AgentState, \"pendingToolCalls\" | \"isStreaming\" | \"streamingMessage\" | \"errorMessage\">>;\n\tconvertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tstreamFn?: StreamFn;\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tonPayload?: SimpleStreamOptions[\"onPayload\"];\n\tonResponse?: SimpleStreamOptions[\"onResponse\"];\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\tprepareNextTurn?: (\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tsteeringMode?: QueueMode;\n\tfollowUpMode?: QueueMode;\n\tsessionId?: string;\n\tthinkingBudgets?: ThinkingBudgets;\n\ttransport?: Transport;\n\tmaxRetryDelayMs?: number;\n\ttoolExecution?: ToolExecutionMode;\n}\n\nclass PendingMessageQueue {\n\tprivate messages: AgentMessage[] = [];\n\tpublic mode: QueueMode;\n\n\tconstructor(mode: QueueMode) {\n\t\tthis.mode = mode;\n\t}\n\n\tenqueue(message: AgentMessage): void {\n\t\tthis.messages.push(message);\n\t}\n\n\thasItems(): boolean {\n\t\treturn this.messages.length > 0;\n\t}\n\n\tdrain(): AgentMessage[] {\n\t\tif (this.mode === \"all\") {\n\t\t\tconst drained = this.messages.slice();\n\t\t\tthis.messages = [];\n\t\t\treturn drained;\n\t\t}\n\n\t\tconst first = this.messages[0];\n\t\tif (!first) {\n\t\t\treturn [];\n\t\t}\n\t\tthis.messages = this.messages.slice(1);\n\t\treturn [first];\n\t}\n\n\tclear(): void {\n\t\tthis.messages = [];\n\t}\n}\n\ntype ActiveRun = {\n\tpromise: Promise<void>;\n\tresolve: () => void;\n\tabortController: AbortController;\n};\n\n/**\n * Stateful wrapper around the low-level agent loop.\n *\n * `Agent` owns the current transcript, emits lifecycle events, executes tools,\n * and exposes queueing APIs for steering and follow-up messages.\n */\nexport class Agent {\n\tprivate _state: MutableAgentState;\n\tprivate readonly listeners = new Set<(event: AgentEvent, signal: AbortSignal) => Promise<void> | void>();\n\tprivate readonly steeringQueue: PendingMessageQueue;\n\tprivate readonly followUpQueue: PendingMessageQueue;\n\n\tpublic convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\tpublic transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tpublic streamFn: StreamFn;\n\tpublic getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tpublic onPayload?: SimpleStreamOptions[\"onPayload\"];\n\tpublic onResponse?: SimpleStreamOptions[\"onResponse\"];\n\tpublic beforeToolCall?: (\n\t\tcontext: BeforeToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<BeforeToolCallResult | undefined>;\n\tpublic afterToolCall?: (\n\t\tcontext: AfterToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AfterToolCallResult | undefined>;\n\tpublic prepareNextTurn?: (\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tprivate activeRun?: ActiveRun;\n\t/** Session identifier forwarded to providers for cache-aware backends. */\n\tpublic sessionId?: string;\n\t/** Optional per-level thinking token budgets forwarded to the stream function. */\n\tpublic thinkingBudgets?: ThinkingBudgets;\n\t/** Preferred transport forwarded to the stream function. */\n\tpublic transport: Transport;\n\t/** Optional cap for provider-requested retry delays. */\n\tpublic maxRetryDelayMs?: number;\n\t/** Tool execution strategy for assistant messages that contain multiple tool calls. */\n\tpublic toolExecution: ToolExecutionMode;\n\n\tconstructor(options: AgentOptions = {}) {\n\t\tthis._state = createMutableAgentState(options.initialState);\n\t\tthis.convertToLlm = options.convertToLlm ?? defaultConvertToLlm;\n\t\tthis.transformContext = options.transformContext;\n\t\tthis.streamFn = options.streamFn ?? streamSimple;\n\t\tthis.getApiKey = options.getApiKey;\n\t\tthis.onPayload = options.onPayload;\n\t\tthis.onResponse = options.onResponse;\n\t\tthis.beforeToolCall = options.beforeToolCall;\n\t\tthis.afterToolCall = options.afterToolCall;\n\t\tthis.prepareNextTurn = options.prepareNextTurn;\n\t\tthis.steeringQueue = new PendingMessageQueue(options.steeringMode ?? \"one-at-a-time\");\n\t\tthis.followUpQueue = new PendingMessageQueue(options.followUpMode ?? \"one-at-a-time\");\n\t\tthis.sessionId = options.sessionId;\n\t\tthis.thinkingBudgets = options.thinkingBudgets;\n\t\tthis.transport = options.transport ?? \"auto\";\n\t\tthis.maxRetryDelayMs = options.maxRetryDelayMs;\n\t\tthis.toolExecution = options.toolExecution ?? \"parallel\";\n\t}\n\n\t/**\n\t * Subscribe to agent lifecycle events.\n\t *\n\t * Listener promises are awaited in subscription order and are included in\n\t * the current run's settlement. Listeners also receive the active abort\n\t * signal for the current run.\n\t *\n\t * `agent_end` is the final emitted event for a run, but the agent does not\n\t * become idle until all awaited listeners for that event have settled.\n\t */\n\tsubscribe(listener: (event: AgentEvent, signal: AbortSignal) => Promise<void> | void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\t/**\n\t * Current agent state.\n\t *\n\t * Assigning `state.tools` or `state.messages` copies the provided top-level array.\n\t */\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\t/** Controls how queued steering messages are drained. */\n\tset steeringMode(mode: QueueMode) {\n\t\tthis.steeringQueue.mode = mode;\n\t}\n\n\tget steeringMode(): QueueMode {\n\t\treturn this.steeringQueue.mode;\n\t}\n\n\t/** Controls how queued follow-up messages are drained. */\n\tset followUpMode(mode: QueueMode) {\n\t\tthis.followUpQueue.mode = mode;\n\t}\n\n\tget followUpMode(): QueueMode {\n\t\treturn this.followUpQueue.mode;\n\t}\n\n\t/** Queue a message to be injected after the current assistant turn finishes. */\n\tsteer(message: AgentMessage): void {\n\t\tthis.steeringQueue.enqueue(message);\n\t}\n\n\t/** Queue a message to run only after the agent would otherwise stop. */\n\tfollowUp(message: AgentMessage): void {\n\t\tthis.followUpQueue.enqueue(message);\n\t}\n\n\t/** Remove all queued steering messages. */\n\tclearSteeringQueue(): void {\n\t\tthis.steeringQueue.clear();\n\t}\n\n\t/** Remove all queued follow-up messages. */\n\tclearFollowUpQueue(): void {\n\t\tthis.followUpQueue.clear();\n\t}\n\n\t/** Remove all queued steering and follow-up messages. */\n\tclearAllQueues(): void {\n\t\tthis.clearSteeringQueue();\n\t\tthis.clearFollowUpQueue();\n\t}\n\n\t/** Returns true when either queue still contains pending messages. */\n\thasQueuedMessages(): boolean {\n\t\treturn this.steeringQueue.hasItems() || this.followUpQueue.hasItems();\n\t}\n\n\t/** Active abort signal for the current run, if any. */\n\tget signal(): AbortSignal | undefined {\n\t\treturn this.activeRun?.abortController.signal;\n\t}\n\n\t/** Abort the current run, if one is active. */\n\tabort(): void {\n\t\tthis.activeRun?.abortController.abort();\n\t}\n\n\t/**\n\t * Resolve when the current run and all awaited event listeners have finished.\n\t *\n\t * This resolves after `agent_end` listeners settle.\n\t */\n\twaitForIdle(): Promise<void> {\n\t\treturn this.activeRun?.promise ?? Promise.resolve();\n\t}\n\n\t/** Clear transcript state, runtime state, and queued messages. */\n\treset(): void {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.errorMessage = undefined;\n\t\tthis.clearFollowUpQueue();\n\t\tthis.clearSteeringQueue();\n\t}\n\n\t/** Start a new prompt from text, a single message, or a batch of messages. */\n\tasync prompt(message: AgentMessage | AgentMessage[]): Promise<void>;\n\tasync prompt(input: string, images?: ImageContent[]): Promise<void>;\n\tasync prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.\",\n\t\t\t);\n\t\t}\n\t\tconst messages = this.normalizePromptInput(input, images);\n\t\tawait this.runPromptMessages(messages);\n\t}\n\n\t/** Continue from the current transcript. The last message must be a user or tool-result message. */\n\tasync continue(): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\"Agent is already processing. Wait for completion before continuing.\");\n\t\t}\n\n\t\tconst lastMessage = this._state.messages[this._state.messages.length - 1];\n\t\tif (!lastMessage) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\n\t\tif (lastMessage.role === \"assistant\") {\n\t\t\tconst queuedSteering = this.steeringQueue.drain();\n\t\t\tif (queuedSteering.length > 0) {\n\t\t\t\tawait this.runPromptMessages(queuedSteering, { skipInitialSteeringPoll: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst queuedFollowUps = this.followUpQueue.drain();\n\t\t\tif (queuedFollowUps.length > 0) {\n\t\t\t\tawait this.runPromptMessages(queuedFollowUps);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t\t}\n\n\t\tawait this.runContinuation();\n\t}\n\n\tprivate normalizePromptInput(\n\t\tinput: string | AgentMessage | AgentMessage[],\n\t\timages?: ImageContent[],\n\t): AgentMessage[] {\n\t\tif (Array.isArray(input)) {\n\t\t\treturn input;\n\t\t}\n\n\t\tif (typeof input !== \"string\") {\n\t\t\treturn [input];\n\t\t}\n\n\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\tif (images && images.length > 0) {\n\t\t\tcontent.push(...images);\n\t\t}\n\t\treturn [{ role: \"user\", content, timestamp: Date.now() }];\n\t}\n\n\tprivate async runPromptMessages(\n\t\tmessages: AgentMessage[],\n\t\toptions: { skipInitialSteeringPoll?: boolean } = {},\n\t): Promise<void> {\n\t\tawait this.runWithLifecycle(async (signal) => {\n\t\t\tawait runAgentLoop(\n\t\t\t\tmessages,\n\t\t\t\tthis.createContextSnapshot(),\n\t\t\t\tthis.createLoopConfig(options),\n\t\t\t\t(event) => this.processEvents(event),\n\t\t\t\tsignal,\n\t\t\t\tthis.streamFn,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate async runContinuation(): Promise<void> {\n\t\tawait this.runWithLifecycle(async (signal) => {\n\t\t\tawait runAgentLoopContinue(\n\t\t\t\tthis.createContextSnapshot(),\n\t\t\t\tthis.createLoopConfig(),\n\t\t\t\t(event) => this.processEvents(event),\n\t\t\t\tsignal,\n\t\t\t\tthis.streamFn,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate createContextSnapshot(): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\tmessages: this._state.messages.slice(),\n\t\t\ttools: this._state.tools.slice(),\n\t\t};\n\t}\n\n\tprivate createLoopConfig(options: { skipInitialSteeringPoll?: boolean } = {}): AgentLoopConfig {\n\t\tlet skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;\n\t\treturn {\n\t\t\tmodel: this._state.model,\n\t\t\treasoning: this._state.thinkingLevel === \"off\" ? undefined : this._state.thinkingLevel,\n\t\t\tsessionId: this.sessionId,\n\t\t\tonPayload: this.onPayload,\n\t\t\tonResponse: this.onResponse,\n\t\t\ttransport: this.transport,\n\t\t\tthinkingBudgets: this.thinkingBudgets,\n\t\t\tmaxRetryDelayMs: this.maxRetryDelayMs,\n\t\t\ttoolExecution: this.toolExecution,\n\t\t\tbeforeToolCall: this.beforeToolCall,\n\t\t\tafterToolCall: this.afterToolCall,\n\t\t\tprepareNextTurn: this.prepareNextTurn ? async () => await this.prepareNextTurn?.(this.signal) : undefined,\n\t\t\tconvertToLlm: this.convertToLlm,\n\t\t\ttransformContext: this.transformContext,\n\t\t\tgetApiKey: this.getApiKey,\n\t\t\tgetSteeringMessages: async () => {\n\t\t\t\tif (skipInitialSteeringPoll) {\n\t\t\t\t\tskipInitialSteeringPoll = false;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn this.steeringQueue.drain();\n\t\t\t},\n\t\t\tgetFollowUpMessages: async () => this.followUpQueue.drain(),\n\t\t};\n\t}\n\n\tprivate async runWithLifecycle(executor: (signal: AbortSignal) => Promise<void>): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\"Agent is already processing.\");\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\t\tlet resolvePromise = () => {};\n\t\tconst promise = new Promise<void>((resolve) => {\n\t\t\tresolvePromise = resolve;\n\t\t});\n\t\tthis.activeRun = { promise, resolve: resolvePromise, abortController };\n\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.errorMessage = undefined;\n\n\t\ttry {\n\t\t\tawait executor(abortController.signal);\n\t\t} catch (error) {\n\t\t\tawait this.handleRunFailure(error, abortController.signal.aborted);\n\t\t} finally {\n\t\t\tthis.finishRun();\n\t\t}\n\t}\n\n\tprivate async handleRunFailure(error: unknown, aborted: boolean): Promise<void> {\n\t\tconst failureMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\tapi: this._state.model.api,\n\t\t\tprovider: this._state.model.provider,\n\t\t\tmodel: this._state.model.id,\n\t\t\tusage: EMPTY_USAGE,\n\t\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\t\ttimestamp: Date.now(),\n\t\t} satisfies AgentMessage;\n\t\tawait this.processEvents({ type: \"message_start\", message: failureMessage });\n\t\tawait this.processEvents({ type: \"message_end\", message: failureMessage });\n\t\tawait this.processEvents({ type: \"turn_end\", message: failureMessage, toolResults: [] });\n\t\tawait this.processEvents({ type: \"agent_end\", messages: [failureMessage] });\n\t}\n\n\tprivate finishRun(): void {\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis.activeRun?.resolve();\n\t\tthis.activeRun = undefined;\n\t}\n\n\t/**\n\t * Reduce internal state for a loop event, then await listeners.\n\t *\n\t * `agent_end` only means no further loop events will be emitted. The run is\n\t * considered idle later, after all awaited listeners for `agent_end` finish\n\t * and `finishRun()` clears runtime-owned state.\n\t */\n\tprivate async processEvents(event: AgentEvent): Promise<void> {\n\t\tswitch (event.type) {\n\t\t\tcase \"message_start\":\n\t\t\t\tthis._state.streamingMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_update\":\n\t\t\t\tthis._state.streamingMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_end\":\n\t\t\t\tthis._state.streamingMessage = undefined;\n\t\t\t\tthis._state.messages.push(event.message);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool_execution_start\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.add(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"tool_execution_end\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.delete(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"turn_end\":\n\t\t\t\tif (event.message.role === \"assistant\" && event.message.errorMessage) {\n\t\t\t\t\tthis._state.errorMessage = event.message.errorMessage;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis._state.streamingMessage = undefined;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tconst signal = this.activeRun?.abortController.signal;\n\t\tif (!signal) {\n\t\t\tthrow new Error(\"Agent listener invoked outside active run\");\n\t\t}\n\t\tfor (const listener of this.listeners) {\n\t\t\tawait listener(event, signal);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AssistantMessage, ImageContent, Model, Models } from "@earendil-works/pi-ai";
|
|
2
2
|
import type { AgentMessage, AgentTool, QueueMode, ThinkingLevel } from "../types.ts";
|
|
3
3
|
import type { AbortResult, AgentHarnessEvent, AgentHarnessEventResultMap, AgentHarnessOptions, AgentHarnessOwnEvent, AgentHarnessResources, AgentHarnessStreamOptions, ExecutionEnv, NavigateTreeResult, PromptTemplate, Skill } from "./types.ts";
|
|
4
4
|
export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate extends PromptTemplate = PromptTemplate, TTool extends AgentTool = AgentTool> {
|
|
5
5
|
readonly env: ExecutionEnv;
|
|
6
6
|
private session;
|
|
7
|
+
readonly models: Models;
|
|
7
8
|
private phase;
|
|
8
9
|
private runAbortController?;
|
|
9
10
|
private runPromise?;
|
|
@@ -12,7 +13,6 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
|
|
|
12
13
|
private thinkingLevel;
|
|
13
14
|
private systemPrompt;
|
|
14
15
|
private streamOptions;
|
|
15
|
-
private getApiKeyAndHeaders?;
|
|
16
16
|
private resources;
|
|
17
17
|
private tools;
|
|
18
18
|
private activeToolNames;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-harness.d.ts","sourceRoot":"","sources":["../../src/harness/agent-harness.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AssistantMessage,\n\ttype ImageContent,\n\ttype Model,\n\tstreamSimple,\n\ttype UserMessage,\n} from \"@earendil-works/pi-ai/base\";\nimport { runAgentLoop } from \"../agent-loop.ts\";\nimport type {\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentTool,\n\tQueueMode,\n\tStreamFn,\n\tThinkingLevel,\n} from \"../types.ts\";\nimport { collectEntriesForBranchSummary, generateBranchSummary } from \"./compaction/branch-summarization.ts\";\nimport { compact, DEFAULT_COMPACTION_SETTINGS, prepareCompaction } from \"./compaction/compaction.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { formatPromptTemplateInvocation } from \"./prompt-templates.ts\";\nimport { formatSkillInvocation } from \"./skills.ts\";\nimport type {\n\tAbortResult,\n\tAgentHarnessEvent,\n\tAgentHarnessEventResultMap,\n\tAgentHarnessOptions,\n\tAgentHarnessOwnEvent,\n\tAgentHarnessPhase,\n\tAgentHarnessResources,\n\tAgentHarnessStreamOptions,\n\tAgentHarnessStreamOptionsPatch,\n\tExecutionEnv,\n\tNavigateTreeResult,\n\tPendingSessionWrite,\n\tPromptTemplate,\n\tSession,\n\tSkill,\n} from \"./types.ts\";\nimport { AgentHarnessError, BranchSummaryError, CompactionError, SessionError, toError } from \"./types.ts\";\n\nfunction createUserMessage(text: string, images?: ImageContent[]): UserMessage {\n\tconst content: Array<{ type: \"text\"; text: string } | ImageContent> = [{ type: \"text\", text }];\n\tif (images) content.push(...images);\n\treturn { role: \"user\", content, timestamp: Date.now() };\n}\n\nfunction createFailureMessage(model: Model<any>, error: unknown, aborted: boolean): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: Date.now(),\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t};\n}\n\nfunction cloneStreamOptions(streamOptions?: AgentHarnessStreamOptions): AgentHarnessStreamOptions {\n\treturn {\n\t\t...streamOptions,\n\t\theaders: streamOptions?.headers ? { ...streamOptions.headers } : undefined,\n\t\tmetadata: streamOptions?.metadata ? { ...streamOptions.metadata } : undefined,\n\t};\n}\n\nfunction mergeHeaders(...headers: Array<Record<string, string> | undefined>): Record<string, string> | undefined {\n\tconst merged: Record<string, string> = {};\n\tlet hasHeaders = false;\n\tfor (const entry of headers) {\n\t\tif (!entry) continue;\n\t\tObject.assign(merged, entry);\n\t\thasHeaders = true;\n\t}\n\treturn hasHeaders ? merged : undefined;\n}\n\nfunction findDuplicateNames(names: string[]): string[] {\n\tconst seen = new Set<string>();\n\tconst duplicates = new Set<string>();\n\tfor (const name of names) {\n\t\tif (seen.has(name)) duplicates.add(name);\n\t\tseen.add(name);\n\t}\n\treturn [...duplicates];\n}\n\nfunction applyStreamOptionsPatch(\n\tbase: AgentHarnessStreamOptions,\n\tpatch?: AgentHarnessStreamOptionsPatch,\n): AgentHarnessStreamOptions {\n\tconst result = cloneStreamOptions(base);\n\tif (!patch) return result;\n\n\tif (Object.hasOwn(patch, \"transport\")) result.transport = patch.transport;\n\tif (Object.hasOwn(patch, \"timeoutMs\")) result.timeoutMs = patch.timeoutMs;\n\tif (Object.hasOwn(patch, \"maxRetries\")) result.maxRetries = patch.maxRetries;\n\tif (Object.hasOwn(patch, \"maxRetryDelayMs\")) result.maxRetryDelayMs = patch.maxRetryDelayMs;\n\tif (Object.hasOwn(patch, \"cacheRetention\")) result.cacheRetention = patch.cacheRetention;\n\n\tif (Object.hasOwn(patch, \"headers\")) {\n\t\tif (patch.headers === undefined) {\n\t\t\tresult.headers = undefined;\n\t\t} else {\n\t\t\tconst headers = { ...(result.headers ?? {}) };\n\t\t\tfor (const [key, value] of Object.entries(patch.headers)) {\n\t\t\t\tif (value === undefined) delete headers[key];\n\t\t\t\telse headers[key] = value;\n\t\t\t}\n\t\t\tresult.headers = Object.keys(headers).length > 0 ? headers : undefined;\n\t\t}\n\t}\n\n\tif (Object.hasOwn(patch, \"metadata\")) {\n\t\tif (patch.metadata === undefined) {\n\t\t\tresult.metadata = undefined;\n\t\t} else {\n\t\t\tconst metadata = { ...(result.metadata ?? {}) };\n\t\t\tfor (const [key, value] of Object.entries(patch.metadata)) {\n\t\t\t\tif (value === undefined) delete metadata[key];\n\t\t\t\telse metadata[key] = value;\n\t\t\t}\n\t\t\tresult.metadata = Object.keys(metadata).length > 0 ? metadata : undefined;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nconst SUBSCRIBER_EVENT_TYPE = \"*\";\n\ntype AgentHarnessHandler = (event: any, signal?: AbortSignal) => Promise<any> | any;\n\nfunction normalizeHarnessError(error: unknown, fallbackCode: AgentHarnessError[\"code\"]): AgentHarnessError {\n\tif (error instanceof AgentHarnessError) return error;\n\tconst cause = toError(error);\n\tif (cause instanceof SessionError) return new AgentHarnessError(\"session\", cause.message, cause);\n\tif (cause instanceof CompactionError) return new AgentHarnessError(\"compaction\", cause.message, cause);\n\tif (cause instanceof BranchSummaryError) return new AgentHarnessError(\"branch_summary\", cause.message, cause);\n\treturn new AgentHarnessError(fallbackCode, cause.message, cause);\n}\n\nfunction normalizeHookError(error: unknown): AgentHarnessError {\n\treturn normalizeHarnessError(error, \"hook\");\n}\n\ninterface AgentHarnessTurnState<\n\tTSkill extends Skill = Skill,\n\tTPromptTemplate extends PromptTemplate = PromptTemplate,\n\tTTool extends AgentTool = AgentTool,\n> {\n\tmessages: AgentMessage[];\n\tresources: AgentHarnessResources<TSkill, TPromptTemplate>;\n\tstreamOptions: AgentHarnessStreamOptions;\n\tsessionId: string;\n\tsystemPrompt: string;\n\tmodel: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\ttools: TTool[];\n\tactiveTools: TTool[];\n}\n\nexport class AgentHarness<\n\tTSkill extends Skill = Skill,\n\tTPromptTemplate extends PromptTemplate = PromptTemplate,\n\tTTool extends AgentTool = AgentTool,\n> {\n\treadonly env: ExecutionEnv;\n\tprivate session: Session;\n\tprivate phase: AgentHarnessPhase = \"idle\";\n\tprivate runAbortController?: AbortController;\n\tprivate runPromise?: Promise<void>;\n\tprivate pendingSessionWrites: PendingSessionWrite[] = [];\n\tprivate model: Model<any>;\n\tprivate thinkingLevel: ThinkingLevel;\n\tprivate systemPrompt: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>[\"systemPrompt\"];\n\tprivate streamOptions: AgentHarnessStreamOptions;\n\tprivate getApiKeyAndHeaders?: AgentHarnessOptions[\"getApiKeyAndHeaders\"];\n\tprivate resources: AgentHarnessResources<TSkill, TPromptTemplate>;\n\tprivate tools = new Map<string, TTool>();\n\tprivate activeToolNames: string[];\n\tprivate steerQueue: UserMessage[] = [];\n\tprivate steeringQueueMode: QueueMode;\n\tprivate followUpQueue: UserMessage[] = [];\n\tprivate followUpQueueMode: QueueMode;\n\tprivate nextTurnQueue: AgentMessage[] = [];\n\tprivate handlers = new Map<string, Set<AgentHarnessHandler>>();\n\n\tconstructor(options: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>) {\n\t\tthis.env = options.env;\n\t\tthis.session = options.session;\n\t\tthis.resources = options.resources ?? {};\n\t\tthis.streamOptions = cloneStreamOptions(options.streamOptions);\n\t\tthis.systemPrompt = options.systemPrompt;\n\t\tthis.getApiKeyAndHeaders = options.getApiKeyAndHeaders;\n\t\tthis.validateUniqueNames(\n\t\t\t(options.tools ?? []).map((tool) => tool.name),\n\t\t\t\"Duplicate tool name(s)\",\n\t\t);\n\t\tfor (const tool of options.tools ?? []) {\n\t\t\tthis.tools.set(tool.name, tool);\n\t\t}\n\t\tthis.model = options.model;\n\t\tthis.thinkingLevel = options.thinkingLevel ?? \"off\";\n\t\tthis.activeToolNames = options.activeToolNames\n\t\t\t? [...options.activeToolNames]\n\t\t\t: (options.tools ?? []).map((tool) => tool.name);\n\t\tthis.validateUniqueNames(this.activeToolNames, \"Duplicate active tool name(s)\");\n\t\tthis.validateToolNames(this.activeToolNames);\n\t\tthis.steeringQueueMode = options.steeringMode ?? \"one-at-a-time\";\n\t\tthis.followUpQueueMode = options.followUpMode ?? \"one-at-a-time\";\n\t}\n\n\tprivate getHandlers(type: string): Set<AgentHarnessHandler> | undefined {\n\t\treturn this.handlers.get(type);\n\t}\n\n\tprivate async emitOwn(event: AgentHarnessOwnEvent<TSkill, TPromptTemplate>, signal?: AbortSignal): Promise<void> {\n\t\tfor (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {\n\t\t\ttry {\n\t\t\t\tawait listener(event, signal);\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async emitAny(event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal): Promise<void> {\n\t\tfor (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {\n\t\t\ttry {\n\t\t\t\tawait listener(event, signal);\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async emitHook<TType extends keyof AgentHarnessEventResultMap>(\n\t\tevent: Extract<AgentHarnessOwnEvent, { type: TType }>,\n\t): Promise<AgentHarnessEventResultMap[TType] | undefined> {\n\t\tconst handlers = this.getHandlers(event.type as TType);\n\t\tif (!handlers || handlers.size === 0) return undefined;\n\t\tlet lastResult: AgentHarnessEventResultMap[TType] | undefined;\n\t\tfor (const handler of handlers) {\n\t\t\ttry {\n\t\t\t\tconst result = await handler(event);\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\tlastResult = result;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t\treturn lastResult;\n\t}\n\n\tprivate async emitBeforeProviderRequest(\n\t\tmodel: Model<any>,\n\t\tsessionId: string,\n\t\tstreamOptions: AgentHarnessStreamOptions,\n\t): Promise<AgentHarnessStreamOptions> {\n\t\tconst handlers = this.getHandlers(\"before_provider_request\");\n\t\tlet current = cloneStreamOptions(streamOptions);\n\t\tif (!handlers || handlers.size === 0) return current;\n\t\tfor (const handler of handlers) {\n\t\t\ttry {\n\t\t\t\tconst result = await handler({\n\t\t\t\t\ttype: \"before_provider_request\",\n\t\t\t\t\tmodel,\n\t\t\t\t\tsessionId,\n\t\t\t\t\tstreamOptions: cloneStreamOptions(current),\n\t\t\t\t});\n\t\t\t\tif (result?.streamOptions) {\n\t\t\t\t\tcurrent = applyStreamOptionsPatch(current, result.streamOptions);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate async emitBeforeProviderPayload(model: Model<any>, payload: unknown): Promise<unknown> {\n\t\tconst handlers = this.getHandlers(\"before_provider_payload\");\n\t\tlet current = payload;\n\t\tif (!handlers || handlers.size === 0) return current;\n\t\tfor (const handler of handlers) {\n\t\t\ttry {\n\t\t\t\tconst result = await handler({ type: \"before_provider_payload\", model, payload: current });\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\tcurrent = result.payload;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate async emitQueueUpdate(): Promise<void> {\n\t\tawait this.emitOwn({\n\t\t\ttype: \"queue_update\",\n\t\t\tsteer: [...this.steerQueue],\n\t\t\tfollowUp: [...this.followUpQueue],\n\t\t\tnextTurn: [...this.nextTurnQueue],\n\t\t});\n\t}\n\n\tprivate startRunPromise(): () => void {\n\t\tlet finish = () => {};\n\t\tthis.runPromise = new Promise<void>((resolve) => {\n\t\t\tfinish = resolve;\n\t\t});\n\t\treturn () => {\n\t\t\tthis.runPromise = undefined;\n\t\t\tfinish();\n\t\t};\n\t}\n\n\tprivate async createTurnState(): Promise<AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>> {\n\t\tconst context = await this.session.buildContext();\n\t\tconst resources = this.getResources();\n\t\tconst sessionMetadata = await this.session.getMetadata();\n\t\tconst tools = [...this.tools.values()];\n\t\tconst activeTools = this.activeToolNames\n\t\t\t.map((name) => this.tools.get(name))\n\t\t\t.filter((tool): tool is TTool => tool !== undefined);\n\t\tlet systemPrompt = \"You are a helpful assistant.\";\n\t\tif (typeof this.systemPrompt === \"string\") {\n\t\t\tsystemPrompt = this.systemPrompt;\n\t\t} else if (this.systemPrompt) {\n\t\t\tsystemPrompt = await this.systemPrompt({\n\t\t\t\tenv: this.env,\n\t\t\t\tsession: this.session,\n\t\t\t\tmodel: this.model,\n\t\t\t\tthinkingLevel: this.thinkingLevel,\n\t\t\t\tactiveTools,\n\t\t\t\tresources,\n\t\t\t});\n\t\t}\n\t\treturn {\n\t\t\tmessages: context.messages,\n\t\t\tresources,\n\t\t\tstreamOptions: cloneStreamOptions(this.streamOptions),\n\t\t\tsessionId: sessionMetadata.id,\n\t\t\tsystemPrompt,\n\t\t\tmodel: this.model,\n\t\t\tthinkingLevel: this.thinkingLevel,\n\t\t\ttools,\n\t\t\tactiveTools,\n\t\t};\n\t}\n\n\tprivate createContext(\n\t\tturnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>,\n\t\tsystemPrompt?: string,\n\t): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: systemPrompt ?? turnState.systemPrompt,\n\t\t\tmessages: turnState.messages.slice(),\n\t\t\ttools: turnState.activeTools.slice(),\n\t\t};\n\t}\n\n\tprivate createStreamFn(getTurnState: () => AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>): StreamFn {\n\t\treturn async (model, context, streamOptions) => {\n\t\t\tconst turnState = getTurnState();\n\t\t\tconst auth = await this.getApiKeyAndHeaders?.(model);\n\t\t\tconst snapshotOptions: AgentHarnessStreamOptions = {\n\t\t\t\t...turnState.streamOptions,\n\t\t\t\theaders: mergeHeaders(turnState.streamOptions.headers, auth?.headers),\n\t\t\t};\n\t\t\tconst requestOptions = await this.emitBeforeProviderRequest(model, turnState.sessionId, snapshotOptions);\n\t\t\treturn streamSimple(model, context, {\n\t\t\t\tcacheRetention: requestOptions.cacheRetention,\n\t\t\t\theaders: requestOptions.headers,\n\t\t\t\tmaxRetries: requestOptions.maxRetries,\n\t\t\t\tmaxRetryDelayMs: requestOptions.maxRetryDelayMs,\n\t\t\t\tmetadata: requestOptions.metadata,\n\t\t\t\tonPayload: async (payload) => await this.emitBeforeProviderPayload(model, payload),\n\t\t\t\tonResponse: async (response) => {\n\t\t\t\t\tconst headers = { ...(response.headers as Record<string, string>) };\n\t\t\t\t\tawait this.emitOwn(\n\t\t\t\t\t\t{ type: \"after_provider_response\", status: response.status, headers },\n\t\t\t\t\t\tstreamOptions?.signal,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\treasoning: streamOptions?.reasoning,\n\t\t\t\tsignal: streamOptions?.signal,\n\t\t\t\tsessionId: turnState.sessionId,\n\t\t\t\ttimeoutMs: requestOptions.timeoutMs,\n\t\t\t\ttransport: requestOptions.transport,\n\t\t\t\tapiKey: auth?.apiKey,\n\t\t\t});\n\t\t};\n\t}\n\n\tprivate async drainQueuedMessages(queue: AgentMessage[], mode: QueueMode): Promise<AgentMessage[]> {\n\t\tconst messages = mode === \"all\" ? queue.splice(0) : queue.splice(0, 1);\n\t\tif (messages.length === 0) return messages;\n\t\ttry {\n\t\t\tawait this.emitQueueUpdate();\n\t\t\treturn messages;\n\t\t} catch (error) {\n\t\t\tqueue.unshift(...messages);\n\t\t\tthrow normalizeHookError(error);\n\t\t}\n\t}\n\n\tprivate createLoopConfig(\n\t\tgetTurnState: () => AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>,\n\t\tsetTurnState: (turnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>) => void,\n\t): AgentLoopConfig {\n\t\tconst turnState = getTurnState();\n\t\treturn {\n\t\t\tmodel: turnState.model,\n\t\t\treasoning: turnState.thinkingLevel === \"off\" ? undefined : turnState.thinkingLevel,\n\t\t\tconvertToLlm,\n\t\t\ttransformContext: async (messages) => {\n\t\t\t\tconst result = await this.emitHook({ type: \"context\", messages: [...messages] });\n\t\t\t\treturn result?.messages ?? messages;\n\t\t\t},\n\t\t\tbeforeToolCall: async ({ toolCall, args }) => {\n\t\t\t\tconst result = await this.emitHook({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\t\ttoolName: toolCall.name,\n\t\t\t\t\tinput: args as Record<string, unknown>,\n\t\t\t\t});\n\t\t\t\treturn result ? { block: result.block, reason: result.reason } : undefined;\n\t\t\t},\n\t\t\tafterToolCall: async ({ toolCall, args, result, isError }) => {\n\t\t\t\tconst patch = await this.emitHook({\n\t\t\t\t\ttype: \"tool_result\",\n\t\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\t\ttoolName: toolCall.name,\n\t\t\t\t\tinput: args as Record<string, unknown>,\n\t\t\t\t\tcontent: result.content,\n\t\t\t\t\tdetails: result.details,\n\t\t\t\t\tisError,\n\t\t\t\t});\n\t\t\t\treturn patch\n\t\t\t\t\t? { content: patch.content, details: patch.details, isError: patch.isError, terminate: patch.terminate }\n\t\t\t\t\t: undefined;\n\t\t\t},\n\t\t\tprepareNextTurn: async () => {\n\t\t\t\tawait this.flushPendingSessionWrites();\n\t\t\t\tconst nextTurnState = await this.createTurnState();\n\t\t\t\tsetTurnState(nextTurnState);\n\t\t\t\treturn {\n\t\t\t\t\tcontext: this.createContext(nextTurnState),\n\t\t\t\t\tmodel: nextTurnState.model,\n\t\t\t\t\tthinkingLevel: nextTurnState.thinkingLevel,\n\t\t\t\t};\n\t\t\t},\n\t\t\tgetSteeringMessages: async () => this.drainQueuedMessages(this.steerQueue, this.steeringQueueMode),\n\t\t\tgetFollowUpMessages: async () => this.drainQueuedMessages(this.followUpQueue, this.followUpQueueMode),\n\t\t};\n\t}\n\n\tprivate validateUniqueNames(names: string[], message: string): void {\n\t\tconst duplicates = findDuplicateNames(names);\n\t\tif (duplicates.length > 0)\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `${message}: ${duplicates.join(\", \")}`);\n\t}\n\n\tprivate validateToolNames(toolNames: string[], tools: Map<string, TTool> = this.tools): void {\n\t\tthis.validateUniqueNames(toolNames, \"Duplicate active tool name(s)\");\n\t\tconst missing = toolNames.filter((name) => !tools.has(name));\n\t\tif (missing.length > 0) throw new AgentHarnessError(\"invalid_argument\", `Unknown tool(s): ${missing.join(\", \")}`);\n\t}\n\n\tprivate async flushPendingSessionWrites(): Promise<void> {\n\t\twhile (this.pendingSessionWrites.length > 0) {\n\t\t\tconst write = this.pendingSessionWrites[0]!;\n\t\t\tif (write.type === \"message\") {\n\t\t\t\tawait this.session.appendMessage(write.message);\n\t\t\t} else if (write.type === \"model_change\") {\n\t\t\t\tawait this.session.appendModelChange(write.provider, write.modelId);\n\t\t\t} else if (write.type === \"thinking_level_change\") {\n\t\t\t\tawait this.session.appendThinkingLevelChange(write.thinkingLevel);\n\t\t\t} else if (write.type === \"active_tools_change\") {\n\t\t\t\tawait this.session.appendActiveToolsChange(write.activeToolNames);\n\t\t\t} else if (write.type === \"custom\") {\n\t\t\t\tawait this.session.appendCustomEntry(write.customType, write.data);\n\t\t\t} else if (write.type === \"custom_message\") {\n\t\t\t\tawait this.session.appendCustomMessageEntry(write.customType, write.content, write.display, write.details);\n\t\t\t} else if (write.type === \"label\") {\n\t\t\t\tawait this.session.appendLabel(write.targetId, write.label);\n\t\t\t} else if (write.type === \"session_info\") {\n\t\t\t\tawait this.session.appendSessionName(write.name ?? \"\");\n\t\t\t} else if (write.type === \"leaf\") {\n\t\t\t\tawait this.session.getStorage().setLeafId(write.targetId);\n\t\t\t}\n\t\t\tthis.pendingSessionWrites.shift();\n\t\t}\n\t}\n\n\tprivate async handleAgentEvent(event: AgentEvent, signal?: AbortSignal): Promise<void> {\n\t\tif (event.type === \"message_end\") {\n\t\t\tawait this.session.appendMessage(event.message);\n\t\t\tawait this.emitAny(event, signal);\n\t\t\treturn;\n\t\t}\n\t\tif (event.type === \"turn_end\") {\n\t\t\tlet eventError: unknown;\n\t\t\ttry {\n\t\t\t\tawait this.emitAny(event, signal);\n\t\t\t} catch (error) {\n\t\t\t\teventError = error;\n\t\t\t}\n\t\t\tconst hadPendingMutations = this.pendingSessionWrites.length > 0;\n\t\t\tawait this.flushPendingSessionWrites();\n\t\t\tif (eventError) throw eventError;\n\t\t\tawait this.emitOwn({ type: \"save_point\", hadPendingMutations });\n\t\t\treturn;\n\t\t}\n\t\tif (event.type === \"agent_end\") {\n\t\t\tawait this.flushPendingSessionWrites();\n\t\t\tthis.phase = \"idle\";\n\t\t\tawait this.emitAny(event, signal);\n\t\t\tawait this.emitOwn({ type: \"settled\", nextTurnCount: this.nextTurnQueue.length }, signal);\n\t\t\treturn;\n\t\t}\n\t\tawait this.emitAny(event, signal);\n\t}\n\n\tprivate async emitRunFailure(\n\t\tmodel: Model<any>,\n\t\terror: unknown,\n\t\taborted: boolean,\n\t\tsignal: AbortSignal,\n\t): Promise<AgentMessage[]> {\n\t\tconst failureMessage = createFailureMessage(model, error, aborted);\n\t\tawait this.handleAgentEvent({ type: \"message_start\", message: failureMessage }, signal);\n\t\tawait this.handleAgentEvent({ type: \"message_end\", message: failureMessage }, signal);\n\t\tawait this.handleAgentEvent({ type: \"turn_end\", message: failureMessage, toolResults: [] }, signal);\n\t\tawait this.handleAgentEvent({ type: \"agent_end\", messages: [failureMessage] }, signal);\n\t\treturn [failureMessage];\n\t}\n\n\tprivate async executeTurn(\n\t\tturnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>,\n\t\ttext: string,\n\t\toptions?: { images?: ImageContent[] },\n\t): Promise<AssistantMessage> {\n\t\tlet activeTurnState = turnState;\n\t\tlet messages: AgentMessage[] = [createUserMessage(text, options?.images)];\n\t\tif (this.nextTurnQueue.length > 0) {\n\t\t\tconst queuedMessages = this.nextTurnQueue.splice(0);\n\t\t\ttry {\n\t\t\t\tawait this.emitQueueUpdate();\n\t\t\t} catch (error) {\n\t\t\t\tthis.nextTurnQueue.unshift(...queuedMessages);\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t\tmessages = [...queuedMessages, messages[0]!];\n\t\t}\n\t\tconst beforeResult = await this.emitHook({\n\t\t\ttype: \"before_agent_start\",\n\t\t\tprompt: text,\n\t\t\timages: options?.images,\n\t\t\tsystemPrompt: turnState.systemPrompt,\n\t\t\tresources: turnState.resources,\n\t\t});\n\t\tif (beforeResult?.messages) messages = [...messages, ...beforeResult.messages];\n\n\t\tconst abortController = new AbortController();\n\t\tconst getTurnState = () => activeTurnState;\n\t\tconst setTurnState = (nextTurnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>) => {\n\t\t\tactiveTurnState = nextTurnState;\n\t\t};\n\t\tthis.runAbortController = abortController;\n\t\tconst runResultPromise = (async () => {\n\t\t\ttry {\n\t\t\t\treturn await runAgentLoop(\n\t\t\t\t\tmessages,\n\t\t\t\t\tthis.createContext(turnState, beforeResult?.systemPrompt),\n\t\t\t\t\tthis.createLoopConfig(getTurnState, setTurnState),\n\t\t\t\t\t(event) => this.handleAgentEvent(event, abortController.signal),\n\t\t\t\t\tabortController.signal,\n\t\t\t\t\tthis.createStreamFn(getTurnState),\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.emitRunFailure(\n\t\t\t\t\t\tactiveTurnState.model,\n\t\t\t\t\t\terror,\n\t\t\t\t\t\tabortController.signal.aborted,\n\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t);\n\t\t\t\t} catch (failureError) {\n\t\t\t\t\tconst cause = new AggregateError(\n\t\t\t\t\t\t[toError(error), toError(failureError)],\n\t\t\t\t\t\t\"Agent run failed and failure reporting failed\",\n\t\t\t\t\t);\n\t\t\t\t\tthrow new AgentHarnessError(\"unknown\", cause.message, cause);\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\t\ttry {\n\t\t\tconst newMessages = await runResultPromise;\n\t\t\tfor (let i = newMessages.length - 1; i >= 0; i--) {\n\t\t\t\tconst message = newMessages[i]!;\n\t\t\t\tif (message.role === \"assistant\") {\n\t\t\t\t\treturn message;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new AgentHarnessError(\"invalid_state\", \"AgentHarness prompt completed without an assistant message\");\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tawait this.flushPendingSessionWrites();\n\t\t\t} finally {\n\t\t\t\tthis.runAbortController = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync prompt(text: string, options?: { images?: ImageContent[] }): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\tconst finishRunPromise = this.startRunPromise();\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\treturn await this.executeTurn(turnState, text, options);\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow normalizeHarnessError(error, \"unknown\");\n\t\t} finally {\n\t\t\tfinishRunPromise();\n\t\t}\n\t}\n\n\tasync skill(name: string, additionalInstructions?: string): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\tconst finishRunPromise = this.startRunPromise();\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\tconst skill = (turnState.resources.skills ?? []).find((candidate) => candidate.name === name);\n\t\t\tif (!skill) throw new AgentHarnessError(\"invalid_argument\", `Unknown skill: ${name}`);\n\t\t\treturn await this.executeTurn(turnState, formatSkillInvocation(skill, additionalInstructions));\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow normalizeHarnessError(error, \"unknown\");\n\t\t} finally {\n\t\t\tfinishRunPromise();\n\t\t}\n\t}\n\n\tasync promptFromTemplate(name: string, args: string[] = []): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\tconst finishRunPromise = this.startRunPromise();\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\tconst template = (turnState.resources.promptTemplates ?? []).find((candidate) => candidate.name === name);\n\t\t\tif (!template) throw new AgentHarnessError(\"invalid_argument\", `Unknown prompt template: ${name}`);\n\t\t\treturn await this.executeTurn(turnState, formatPromptTemplateInvocation(template, args));\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow normalizeHarnessError(error, \"unknown\");\n\t\t} finally {\n\t\t\tfinishRunPromise();\n\t\t}\n\t}\n\n\tasync steer(text: string, options?: { images?: ImageContent[] }): Promise<void> {\n\t\tif (this.phase === \"idle\") throw new AgentHarnessError(\"invalid_state\", \"Cannot steer while idle\");\n\t\tthis.steerQueue.push(createUserMessage(text, options?.images));\n\t\tawait this.emitQueueUpdate();\n\t}\n\n\tasync followUp(text: string, options?: { images?: ImageContent[] }): Promise<void> {\n\t\tif (this.phase === \"idle\") throw new AgentHarnessError(\"invalid_state\", \"Cannot follow up while idle\");\n\t\tthis.followUpQueue.push(createUserMessage(text, options?.images));\n\t\tawait this.emitQueueUpdate();\n\t}\n\n\tasync nextTurn(text: string, options?: { images?: ImageContent[] }): Promise<void> {\n\t\tthis.nextTurnQueue.push(createUserMessage(text, options?.images));\n\t\tawait this.emitQueueUpdate();\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<void> {\n\t\ttry {\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendMessage(message);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"message\", message });\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"session\");\n\t\t}\n\t}\n\n\tasync compact(\n\t\tcustomInstructions?: string,\n\t): Promise<{ summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown }> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"compact() requires idle harness\");\n\t\tthis.phase = \"compaction\";\n\t\ttry {\n\t\t\tconst model = this.model;\n\t\t\tif (!model) throw new AgentHarnessError(\"invalid_state\", \"No model set for compaction\");\n\t\t\tconst auth = await this.getApiKeyAndHeaders?.(model);\n\t\t\tif (!auth) throw new AgentHarnessError(\"auth\", \"No auth available for compaction\");\n\t\t\tconst branchEntries = await this.session.getBranch();\n\t\t\tconst preparationResult = prepareCompaction(branchEntries, DEFAULT_COMPACTION_SETTINGS);\n\t\t\tif (!preparationResult.ok) throw preparationResult.error;\n\t\t\tconst preparation = preparationResult.value;\n\t\t\tif (!preparation) throw new AgentHarnessError(\"compaction\", \"Nothing to compact\");\n\t\t\tconst hookResult = await this.emitHook({\n\t\t\t\ttype: \"session_before_compact\",\n\t\t\t\tpreparation,\n\t\t\t\tbranchEntries,\n\t\t\t\tcustomInstructions,\n\t\t\t\tsignal: new AbortController().signal,\n\t\t\t});\n\t\t\tif (hookResult?.cancel) throw new AgentHarnessError(\"compaction\", \"Compaction cancelled\");\n\t\t\tconst provided = hookResult?.compaction;\n\t\t\tconst compactResult = provided\n\t\t\t\t? { ok: true as const, value: provided }\n\t\t\t\t: await compact(\n\t\t\t\t\t\tpreparation,\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\tauth.apiKey,\n\t\t\t\t\t\tauth.headers,\n\t\t\t\t\t\tcustomInstructions,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tthis.thinkingLevel,\n\t\t\t\t\t);\n\t\t\tif (!compactResult.ok) throw compactResult.error;\n\t\t\tconst result = compactResult.value;\n\t\t\tconst entryId = await this.session.appendCompaction(\n\t\t\t\tresult.summary,\n\t\t\t\tresult.firstKeptEntryId,\n\t\t\t\tresult.tokensBefore,\n\t\t\t\tresult.details,\n\t\t\t\tprovided !== undefined,\n\t\t\t);\n\t\t\tconst entry = await this.session.getEntry(entryId);\n\t\t\tif (entry?.type === \"compaction\") {\n\t\t\t\tawait this.emitOwn({ type: \"session_compact\", compactionEntry: entry, fromHook: provided !== undefined });\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"compaction\");\n\t\t} finally {\n\t\t\tthis.phase = \"idle\";\n\t\t}\n\t}\n\n\tasync navigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<NavigateTreeResult> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"navigateTree() requires idle harness\");\n\t\tthis.phase = \"branch_summary\";\n\t\ttry {\n\t\t\tconst oldLeafId = await this.session.getLeafId();\n\t\t\tif (oldLeafId === targetId) return { cancelled: false };\n\t\t\tconst targetEntry = await this.session.getEntry(targetId);\n\t\t\tif (!targetEntry) throw new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t\tconst { entries, commonAncestorId } = await collectEntriesForBranchSummary(this.session, oldLeafId, targetId);\n\t\t\tconst preparation = {\n\t\t\t\ttargetId,\n\t\t\t\toldLeafId,\n\t\t\t\tcommonAncestorId,\n\t\t\t\tentriesToSummarize: entries,\n\t\t\t\tuserWantsSummary: options?.summarize ?? false,\n\t\t\t\tcustomInstructions: options?.customInstructions,\n\t\t\t\treplaceInstructions: options?.replaceInstructions,\n\t\t\t\tlabel: options?.label,\n\t\t\t};\n\t\t\tconst signal = new AbortController().signal;\n\t\t\tconst hookResult = await this.emitHook({ type: \"session_before_tree\", preparation, signal });\n\t\t\tif (hookResult?.cancel) return { cancelled: true };\n\t\t\tlet summaryEntry: NavigateTreeResult[\"summaryEntry\"];\n\t\t\tlet summaryText: string | undefined = hookResult?.summary?.summary;\n\t\t\tlet summaryDetails: unknown = hookResult?.summary?.details;\n\t\t\tif (!summaryText && options?.summarize && entries.length > 0) {\n\t\t\t\tconst model = this.model;\n\t\t\t\tif (!model) throw new AgentHarnessError(\"invalid_state\", \"No model set for branch summary\");\n\t\t\t\tconst auth = await this.getApiKeyAndHeaders?.(model);\n\t\t\t\tif (!auth) throw new AgentHarnessError(\"auth\", \"No auth available for branch summary\");\n\t\t\t\tconst branchSummary = await generateBranchSummary(entries, {\n\t\t\t\t\tmodel,\n\t\t\t\t\tapiKey: auth.apiKey,\n\t\t\t\t\theaders: auth.headers,\n\t\t\t\t\tsignal: new AbortController().signal,\n\t\t\t\t\tcustomInstructions: hookResult?.customInstructions ?? options?.customInstructions,\n\t\t\t\t\treplaceInstructions: hookResult?.replaceInstructions ?? options?.replaceInstructions,\n\t\t\t\t});\n\t\t\t\tif (!branchSummary.ok) {\n\t\t\t\t\tif (branchSummary.error.code === \"aborted\") return { cancelled: true };\n\t\t\t\t\tthrow new AgentHarnessError(\"branch_summary\", branchSummary.error.message, branchSummary.error);\n\t\t\t\t}\n\t\t\t\tsummaryText = branchSummary.value.summary;\n\t\t\t\tsummaryDetails = {\n\t\t\t\t\treadFiles: branchSummary.value.readFiles,\n\t\t\t\t\tmodifiedFiles: branchSummary.value.modifiedFiles,\n\t\t\t\t};\n\t\t\t}\n\t\t\tlet editorText: string | undefined;\n\t\t\tlet newLeafId: string | null;\n\t\t\tif (targetEntry.type === \"message\" && targetEntry.message.role === \"user\") {\n\t\t\t\tnewLeafId = targetEntry.parentId;\n\t\t\t\tconst content = targetEntry.message.content;\n\t\t\t\teditorText =\n\t\t\t\t\ttypeof content === \"string\"\n\t\t\t\t\t\t? content\n\t\t\t\t\t\t: content\n\t\t\t\t\t\t\t\t.filter((c): c is { readonly type: \"text\"; readonly text: string } => c.type === \"text\")\n\t\t\t\t\t\t\t\t.map((c) => c.text)\n\t\t\t\t\t\t\t\t.join(\"\");\n\t\t\t} else if (targetEntry.type === \"custom_message\") {\n\t\t\t\tnewLeafId = targetEntry.parentId;\n\t\t\t\teditorText =\n\t\t\t\t\ttypeof targetEntry.content === \"string\"\n\t\t\t\t\t\t? targetEntry.content\n\t\t\t\t\t\t: targetEntry.content\n\t\t\t\t\t\t\t\t.filter((c): c is { readonly type: \"text\"; readonly text: string } => c.type === \"text\")\n\t\t\t\t\t\t\t\t.map((c) => c.text)\n\t\t\t\t\t\t\t\t.join(\"\");\n\t\t\t} else {\n\t\t\t\tnewLeafId = targetId;\n\t\t\t}\n\t\t\tconst summaryId = await this.session.moveTo(\n\t\t\t\tnewLeafId,\n\t\t\t\tsummaryText\n\t\t\t\t\t? { summary: summaryText, details: summaryDetails, fromHook: hookResult?.summary !== undefined }\n\t\t\t\t\t: undefined,\n\t\t\t);\n\t\t\tif (summaryId) {\n\t\t\t\tconst entry = await this.session.getEntry(summaryId);\n\t\t\t\tif (entry?.type === \"branch_summary\") summaryEntry = entry;\n\t\t\t}\n\t\t\tawait this.emitOwn({\n\t\t\t\ttype: \"session_tree\",\n\t\t\t\tnewLeafId: await this.session.getLeafId(),\n\t\t\t\toldLeafId,\n\t\t\t\tsummaryEntry,\n\t\t\t\tfromHook: hookResult?.summary !== undefined,\n\t\t\t});\n\t\t\treturn { cancelled: false, editorText, summaryEntry };\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"branch_summary\");\n\t\t} finally {\n\t\t\tthis.phase = \"idle\";\n\t\t}\n\t}\n\n\tgetModel(): Model<any> {\n\t\treturn this.model;\n\t}\n\n\tasync setModel(model: Model<any>): Promise<void> {\n\t\ttry {\n\t\t\tconst previousModel = this.model;\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendModelChange(model.provider, model.id);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"model_change\", provider: model.provider, modelId: model.id });\n\t\t\t}\n\t\t\tthis.model = model;\n\t\t\tawait this.emitOwn({ type: \"model_update\", model, previousModel, source: \"set\" });\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"session\");\n\t\t}\n\t}\n\n\tgetThinkingLevel(): ThinkingLevel {\n\t\treturn this.thinkingLevel;\n\t}\n\n\tasync setThinkingLevel(level: ThinkingLevel): Promise<void> {\n\t\ttry {\n\t\t\tconst previousLevel = this.thinkingLevel;\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendThinkingLevelChange(level);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"thinking_level_change\", thinkingLevel: level });\n\t\t\t}\n\t\t\tthis.thinkingLevel = level;\n\t\t\tawait this.emitOwn({ type: \"thinking_level_update\", level, previousLevel });\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"session\");\n\t\t}\n\t}\n\n\tgetTools(): TTool[] {\n\t\treturn [...this.tools.values()];\n\t}\n\n\tasync setTools(tools: TTool[], activeToolNames?: string[]): Promise<void> {\n\t\ttry {\n\t\t\tthis.validateUniqueNames(\n\t\t\t\ttools.map((tool) => tool.name),\n\t\t\t\t\"Duplicate tool name(s)\",\n\t\t\t);\n\t\t\tconst nextTools = new Map(tools.map((tool) => [tool.name, tool]));\n\t\t\tconst nextActiveToolNames = activeToolNames ? [...activeToolNames] : this.activeToolNames;\n\t\t\tthis.validateToolNames(nextActiveToolNames, nextTools);\n\t\t\tconst previousToolNames = [...this.tools.keys()];\n\t\t\tconst previousActiveToolNames = [...this.activeToolNames];\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendActiveToolsChange(nextActiveToolNames);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"active_tools_change\", activeToolNames: [...nextActiveToolNames] });\n\t\t\t}\n\t\t\tthis.tools = nextTools;\n\t\t\tthis.activeToolNames = [...nextActiveToolNames];\n\t\t\tawait this.emitOwn({\n\t\t\t\ttype: \"tools_update\",\n\t\t\t\ttoolNames: [...this.tools.keys()],\n\t\t\t\tpreviousToolNames,\n\t\t\t\tactiveToolNames: [...this.activeToolNames],\n\t\t\t\tpreviousActiveToolNames,\n\t\t\t\tsource: \"set\",\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"invalid_argument\");\n\t\t}\n\t}\n\n\tgetActiveTools(): TTool[] {\n\t\treturn this.activeToolNames.map((name) => this.tools.get(name)!);\n\t}\n\n\tasync setActiveTools(toolNames: string[]): Promise<void> {\n\t\ttry {\n\t\t\tthis.validateToolNames(toolNames);\n\t\t\tconst previousToolNames = [...this.tools.keys()];\n\t\t\tconst previousActiveToolNames = [...this.activeToolNames];\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendActiveToolsChange(toolNames);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"active_tools_change\", activeToolNames: [...toolNames] });\n\t\t\t}\n\t\t\tthis.activeToolNames = [...toolNames];\n\t\t\tawait this.emitOwn({\n\t\t\t\ttype: \"tools_update\",\n\t\t\t\ttoolNames: [...this.tools.keys()],\n\t\t\t\tpreviousToolNames,\n\t\t\t\tactiveToolNames: [...this.activeToolNames],\n\t\t\t\tpreviousActiveToolNames,\n\t\t\t\tsource: \"set\",\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"invalid_argument\");\n\t\t}\n\t}\n\n\tgetSteeringMode(): QueueMode {\n\t\treturn this.steeringQueueMode;\n\t}\n\n\tasync setSteeringMode(mode: QueueMode): Promise<void> {\n\t\tthis.steeringQueueMode = mode;\n\t}\n\n\tgetFollowUpMode(): QueueMode {\n\t\treturn this.followUpQueueMode;\n\t}\n\n\tasync setFollowUpMode(mode: QueueMode): Promise<void> {\n\t\tthis.followUpQueueMode = mode;\n\t}\n\n\tgetResources(): AgentHarnessResources<TSkill, TPromptTemplate> {\n\t\treturn {\n\t\t\tskills: this.resources.skills?.slice(),\n\t\t\tpromptTemplates: this.resources.promptTemplates?.slice(),\n\t\t};\n\t}\n\n\tasync setResources(resources: AgentHarnessResources<TSkill, TPromptTemplate>): Promise<void> {\n\t\tconst previousResources = this.getResources();\n\t\tthis.resources = {\n\t\t\tskills: resources.skills?.slice(),\n\t\t\tpromptTemplates: resources.promptTemplates?.slice(),\n\t\t};\n\t\tawait this.emitOwn({ type: \"resources_update\", resources: this.getResources(), previousResources });\n\t}\n\n\tgetStreamOptions(): AgentHarnessStreamOptions {\n\t\treturn cloneStreamOptions(this.streamOptions);\n\t}\n\n\tasync setStreamOptions(streamOptions: AgentHarnessStreamOptions): Promise<void> {\n\t\tthis.streamOptions = cloneStreamOptions(streamOptions);\n\t}\n\n\tasync abort(): Promise<AbortResult> {\n\t\tconst clearedSteer = [...this.steerQueue];\n\t\tconst clearedFollowUp = [...this.followUpQueue];\n\t\tthis.steerQueue = [];\n\t\tthis.followUpQueue = [];\n\t\tthis.runAbortController?.abort();\n\t\tconst errors: Error[] = [];\n\t\ttry {\n\t\t\tawait this.emitQueueUpdate();\n\t\t} catch (error) {\n\t\t\terrors.push(toError(error));\n\t\t}\n\t\ttry {\n\t\t\tawait this.waitForIdle();\n\t\t} catch (error) {\n\t\t\terrors.push(toError(error));\n\t\t}\n\t\ttry {\n\t\t\tawait this.emitOwn({ type: \"abort\", clearedSteer, clearedFollowUp });\n\t\t} catch (error) {\n\t\t\terrors.push(toError(error));\n\t\t}\n\t\tif (errors.length > 0) {\n\t\t\tconst cause = errors.length === 1 ? errors[0]! : new AggregateError(errors, \"Abort completed with errors\");\n\t\t\tthrow normalizeHarnessError(cause, \"hook\");\n\t\t}\n\t\treturn { clearedSteer, clearedFollowUp };\n\t}\n\n\tasync waitForIdle(): Promise<void> {\n\t\tawait this.runPromise;\n\t}\n\n\tsubscribe(\n\t\tlistener: (event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal) => Promise<void> | void,\n\t): () => void {\n\t\tlet handlers = this.handlers.get(SUBSCRIBER_EVENT_TYPE);\n\t\tif (!handlers) {\n\t\t\thandlers = new Set();\n\t\t\tthis.handlers.set(SUBSCRIBER_EVENT_TYPE, handlers);\n\t\t}\n\t\thandlers.add(listener as AgentHarnessHandler);\n\t\treturn () => handlers!.delete(listener as AgentHarnessHandler);\n\t}\n\n\ton<TType extends keyof AgentHarnessEventResultMap>(\n\t\ttype: TType,\n\t\thandler: (\n\t\t\tevent: Extract<AgentHarnessOwnEvent, { type: TType }>,\n\t\t) => Promise<AgentHarnessEventResultMap[TType]> | AgentHarnessEventResultMap[TType],\n\t): () => void {\n\t\tlet handlers = this.handlers.get(type);\n\t\tif (!handlers) {\n\t\t\thandlers = new Set();\n\t\t\tthis.handlers.set(type, handlers);\n\t\t}\n\t\thandlers.add(handler as AgentHarnessHandler);\n\t\treturn () => handlers!.delete(handler as AgentHarnessHandler);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent-harness.d.ts","sourceRoot":"","sources":["../../src/harness/agent-harness.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage, ImageContent, Model, Models, UserMessage } from \"@earendil-works/pi-ai\";\nimport { runAgentLoop } from \"../agent-loop.ts\";\nimport type {\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentTool,\n\tQueueMode,\n\tStreamFn,\n\tThinkingLevel,\n} from \"../types.ts\";\nimport { collectEntriesForBranchSummary, generateBranchSummary } from \"./compaction/branch-summarization.ts\";\nimport { compact, DEFAULT_COMPACTION_SETTINGS, prepareCompaction } from \"./compaction/compaction.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { formatPromptTemplateInvocation } from \"./prompt-templates.ts\";\nimport { formatSkillInvocation } from \"./skills.ts\";\nimport type {\n\tAbortResult,\n\tAgentHarnessEvent,\n\tAgentHarnessEventResultMap,\n\tAgentHarnessOptions,\n\tAgentHarnessOwnEvent,\n\tAgentHarnessPhase,\n\tAgentHarnessResources,\n\tAgentHarnessStreamOptions,\n\tAgentHarnessStreamOptionsPatch,\n\tExecutionEnv,\n\tNavigateTreeResult,\n\tPendingSessionWrite,\n\tPromptTemplate,\n\tSession,\n\tSkill,\n} from \"./types.ts\";\nimport { AgentHarnessError, BranchSummaryError, CompactionError, SessionError, toError } from \"./types.ts\";\n\nfunction createUserMessage(text: string, images?: ImageContent[]): UserMessage {\n\tconst content: Array<{ type: \"text\"; text: string } | ImageContent> = [{ type: \"text\", text }];\n\tif (images) content.push(...images);\n\treturn { role: \"user\", content, timestamp: Date.now() };\n}\n\nfunction createFailureMessage(model: Model<any>, error: unknown, aborted: boolean): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: Date.now(),\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t};\n}\n\nfunction cloneStreamOptions(streamOptions?: AgentHarnessStreamOptions): AgentHarnessStreamOptions {\n\treturn {\n\t\t...streamOptions,\n\t\theaders: streamOptions?.headers ? { ...streamOptions.headers } : undefined,\n\t\tmetadata: streamOptions?.metadata ? { ...streamOptions.metadata } : undefined,\n\t};\n}\n\nfunction findDuplicateNames(names: string[]): string[] {\n\tconst seen = new Set<string>();\n\tconst duplicates = new Set<string>();\n\tfor (const name of names) {\n\t\tif (seen.has(name)) duplicates.add(name);\n\t\tseen.add(name);\n\t}\n\treturn [...duplicates];\n}\n\nfunction applyStreamOptionsPatch(\n\tbase: AgentHarnessStreamOptions,\n\tpatch?: AgentHarnessStreamOptionsPatch,\n): AgentHarnessStreamOptions {\n\tconst result = cloneStreamOptions(base);\n\tif (!patch) return result;\n\n\tif (Object.hasOwn(patch, \"transport\")) result.transport = patch.transport;\n\tif (Object.hasOwn(patch, \"timeoutMs\")) result.timeoutMs = patch.timeoutMs;\n\tif (Object.hasOwn(patch, \"maxRetries\")) result.maxRetries = patch.maxRetries;\n\tif (Object.hasOwn(patch, \"maxRetryDelayMs\")) result.maxRetryDelayMs = patch.maxRetryDelayMs;\n\tif (Object.hasOwn(patch, \"cacheRetention\")) result.cacheRetention = patch.cacheRetention;\n\n\tif (Object.hasOwn(patch, \"headers\")) {\n\t\tif (patch.headers === undefined) {\n\t\t\tresult.headers = undefined;\n\t\t} else {\n\t\t\tconst headers = { ...(result.headers ?? {}) };\n\t\t\tfor (const [key, value] of Object.entries(patch.headers)) {\n\t\t\t\tif (value === undefined) delete headers[key];\n\t\t\t\telse headers[key] = value;\n\t\t\t}\n\t\t\tresult.headers = Object.keys(headers).length > 0 ? headers : undefined;\n\t\t}\n\t}\n\n\tif (Object.hasOwn(patch, \"metadata\")) {\n\t\tif (patch.metadata === undefined) {\n\t\t\tresult.metadata = undefined;\n\t\t} else {\n\t\t\tconst metadata = { ...(result.metadata ?? {}) };\n\t\t\tfor (const [key, value] of Object.entries(patch.metadata)) {\n\t\t\t\tif (value === undefined) delete metadata[key];\n\t\t\t\telse metadata[key] = value;\n\t\t\t}\n\t\t\tresult.metadata = Object.keys(metadata).length > 0 ? metadata : undefined;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nconst SUBSCRIBER_EVENT_TYPE = \"*\";\n\ntype AgentHarnessHandler = (event: any, signal?: AbortSignal) => Promise<any> | any;\n\nfunction normalizeHarnessError(error: unknown, fallbackCode: AgentHarnessError[\"code\"]): AgentHarnessError {\n\tif (error instanceof AgentHarnessError) return error;\n\tconst cause = toError(error);\n\tif (cause instanceof SessionError) return new AgentHarnessError(\"session\", cause.message, cause);\n\tif (cause instanceof CompactionError) return new AgentHarnessError(\"compaction\", cause.message, cause);\n\tif (cause instanceof BranchSummaryError) return new AgentHarnessError(\"branch_summary\", cause.message, cause);\n\treturn new AgentHarnessError(fallbackCode, cause.message, cause);\n}\n\nfunction normalizeHookError(error: unknown): AgentHarnessError {\n\treturn normalizeHarnessError(error, \"hook\");\n}\n\ninterface AgentHarnessTurnState<\n\tTSkill extends Skill = Skill,\n\tTPromptTemplate extends PromptTemplate = PromptTemplate,\n\tTTool extends AgentTool = AgentTool,\n> {\n\tmessages: AgentMessage[];\n\tresources: AgentHarnessResources<TSkill, TPromptTemplate>;\n\tstreamOptions: AgentHarnessStreamOptions;\n\tsessionId: string;\n\tsystemPrompt: string;\n\tmodel: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\ttools: TTool[];\n\tactiveTools: TTool[];\n}\n\nexport class AgentHarness<\n\tTSkill extends Skill = Skill,\n\tTPromptTemplate extends PromptTemplate = PromptTemplate,\n\tTTool extends AgentTool = AgentTool,\n> {\n\treadonly env: ExecutionEnv;\n\tprivate session: Session;\n\treadonly models: Models;\n\tprivate phase: AgentHarnessPhase = \"idle\";\n\tprivate runAbortController?: AbortController;\n\tprivate runPromise?: Promise<void>;\n\tprivate pendingSessionWrites: PendingSessionWrite[] = [];\n\tprivate model: Model<any>;\n\tprivate thinkingLevel: ThinkingLevel;\n\tprivate systemPrompt: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>[\"systemPrompt\"];\n\tprivate streamOptions: AgentHarnessStreamOptions;\n\tprivate resources: AgentHarnessResources<TSkill, TPromptTemplate>;\n\tprivate tools = new Map<string, TTool>();\n\tprivate activeToolNames: string[];\n\tprivate steerQueue: UserMessage[] = [];\n\tprivate steeringQueueMode: QueueMode;\n\tprivate followUpQueue: UserMessage[] = [];\n\tprivate followUpQueueMode: QueueMode;\n\tprivate nextTurnQueue: AgentMessage[] = [];\n\tprivate handlers = new Map<string, Set<AgentHarnessHandler>>();\n\n\tconstructor(options: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>) {\n\t\tthis.env = options.env;\n\t\tthis.session = options.session;\n\t\tthis.models = options.models;\n\t\tthis.resources = options.resources ?? {};\n\t\tthis.streamOptions = cloneStreamOptions(options.streamOptions);\n\t\tthis.systemPrompt = options.systemPrompt;\n\t\tthis.validateUniqueNames(\n\t\t\t(options.tools ?? []).map((tool) => tool.name),\n\t\t\t\"Duplicate tool name(s)\",\n\t\t);\n\t\tfor (const tool of options.tools ?? []) {\n\t\t\tthis.tools.set(tool.name, tool);\n\t\t}\n\t\tthis.model = options.model;\n\t\tthis.thinkingLevel = options.thinkingLevel ?? \"off\";\n\t\tthis.activeToolNames = options.activeToolNames\n\t\t\t? [...options.activeToolNames]\n\t\t\t: (options.tools ?? []).map((tool) => tool.name);\n\t\tthis.validateUniqueNames(this.activeToolNames, \"Duplicate active tool name(s)\");\n\t\tthis.validateToolNames(this.activeToolNames);\n\t\tthis.steeringQueueMode = options.steeringMode ?? \"one-at-a-time\";\n\t\tthis.followUpQueueMode = options.followUpMode ?? \"one-at-a-time\";\n\t}\n\n\tprivate getHandlers(type: string): Set<AgentHarnessHandler> | undefined {\n\t\treturn this.handlers.get(type);\n\t}\n\n\tprivate async emitOwn(event: AgentHarnessOwnEvent<TSkill, TPromptTemplate>, signal?: AbortSignal): Promise<void> {\n\t\tfor (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {\n\t\t\ttry {\n\t\t\t\tawait listener(event, signal);\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async emitAny(event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal): Promise<void> {\n\t\tfor (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {\n\t\t\ttry {\n\t\t\t\tawait listener(event, signal);\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async emitHook<TType extends keyof AgentHarnessEventResultMap>(\n\t\tevent: Extract<AgentHarnessOwnEvent, { type: TType }>,\n\t): Promise<AgentHarnessEventResultMap[TType] | undefined> {\n\t\tconst handlers = this.getHandlers(event.type as TType);\n\t\tif (!handlers || handlers.size === 0) return undefined;\n\t\tlet lastResult: AgentHarnessEventResultMap[TType] | undefined;\n\t\tfor (const handler of handlers) {\n\t\t\ttry {\n\t\t\t\tconst result = await handler(event);\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\tlastResult = result;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t\treturn lastResult;\n\t}\n\n\tprivate async emitBeforeProviderRequest(\n\t\tmodel: Model<any>,\n\t\tsessionId: string,\n\t\tstreamOptions: AgentHarnessStreamOptions,\n\t): Promise<AgentHarnessStreamOptions> {\n\t\tconst handlers = this.getHandlers(\"before_provider_request\");\n\t\tlet current = cloneStreamOptions(streamOptions);\n\t\tif (!handlers || handlers.size === 0) return current;\n\t\tfor (const handler of handlers) {\n\t\t\ttry {\n\t\t\t\tconst result = await handler({\n\t\t\t\t\ttype: \"before_provider_request\",\n\t\t\t\t\tmodel,\n\t\t\t\t\tsessionId,\n\t\t\t\t\tstreamOptions: cloneStreamOptions(current),\n\t\t\t\t});\n\t\t\t\tif (result?.streamOptions) {\n\t\t\t\t\tcurrent = applyStreamOptionsPatch(current, result.streamOptions);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate async emitBeforeProviderPayload(model: Model<any>, payload: unknown): Promise<unknown> {\n\t\tconst handlers = this.getHandlers(\"before_provider_payload\");\n\t\tlet current = payload;\n\t\tif (!handlers || handlers.size === 0) return current;\n\t\tfor (const handler of handlers) {\n\t\t\ttry {\n\t\t\t\tconst result = await handler({ type: \"before_provider_payload\", model, payload: current });\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\tcurrent = result.payload;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate async emitQueueUpdate(): Promise<void> {\n\t\tawait this.emitOwn({\n\t\t\ttype: \"queue_update\",\n\t\t\tsteer: [...this.steerQueue],\n\t\t\tfollowUp: [...this.followUpQueue],\n\t\t\tnextTurn: [...this.nextTurnQueue],\n\t\t});\n\t}\n\n\tprivate startRunPromise(): () => void {\n\t\tlet finish = () => {};\n\t\tthis.runPromise = new Promise<void>((resolve) => {\n\t\t\tfinish = resolve;\n\t\t});\n\t\treturn () => {\n\t\t\tthis.runPromise = undefined;\n\t\t\tfinish();\n\t\t};\n\t}\n\n\tprivate async createTurnState(): Promise<AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>> {\n\t\tconst context = await this.session.buildContext();\n\t\tconst resources = this.getResources();\n\t\tconst sessionMetadata = await this.session.getMetadata();\n\t\tconst tools = [...this.tools.values()];\n\t\tconst activeTools = this.activeToolNames\n\t\t\t.map((name) => this.tools.get(name))\n\t\t\t.filter((tool): tool is TTool => tool !== undefined);\n\t\tlet systemPrompt = \"You are a helpful assistant.\";\n\t\tif (typeof this.systemPrompt === \"string\") {\n\t\t\tsystemPrompt = this.systemPrompt;\n\t\t} else if (this.systemPrompt) {\n\t\t\tsystemPrompt = await this.systemPrompt({\n\t\t\t\tenv: this.env,\n\t\t\t\tsession: this.session,\n\t\t\t\tmodel: this.model,\n\t\t\t\tthinkingLevel: this.thinkingLevel,\n\t\t\t\tactiveTools,\n\t\t\t\tresources,\n\t\t\t});\n\t\t}\n\t\treturn {\n\t\t\tmessages: context.messages,\n\t\t\tresources,\n\t\t\tstreamOptions: cloneStreamOptions(this.streamOptions),\n\t\t\tsessionId: sessionMetadata.id,\n\t\t\tsystemPrompt,\n\t\t\tmodel: this.model,\n\t\t\tthinkingLevel: this.thinkingLevel,\n\t\t\ttools,\n\t\t\tactiveTools,\n\t\t};\n\t}\n\n\tprivate createContext(\n\t\tturnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>,\n\t\tsystemPrompt?: string,\n\t): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: systemPrompt ?? turnState.systemPrompt,\n\t\t\tmessages: turnState.messages.slice(),\n\t\t\ttools: turnState.activeTools.slice(),\n\t\t};\n\t}\n\n\tprivate createStreamFn(getTurnState: () => AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>): StreamFn {\n\t\treturn async (model, context, streamOptions) => {\n\t\t\tconst turnState = getTurnState();\n\t\t\tconst snapshotOptions: AgentHarnessStreamOptions = { ...turnState.streamOptions };\n\t\t\tconst requestOptions = await this.emitBeforeProviderRequest(model, turnState.sessionId, snapshotOptions);\n\t\t\treturn this.models.streamSimple(model, context, {\n\t\t\t\tcacheRetention: requestOptions.cacheRetention,\n\t\t\t\theaders: requestOptions.headers,\n\t\t\t\tmaxRetries: requestOptions.maxRetries,\n\t\t\t\tmaxRetryDelayMs: requestOptions.maxRetryDelayMs,\n\t\t\t\tmetadata: requestOptions.metadata,\n\t\t\t\tonPayload: async (payload) => await this.emitBeforeProviderPayload(model, payload),\n\t\t\t\tonResponse: async (response) => {\n\t\t\t\t\tconst headers = { ...(response.headers as Record<string, string>) };\n\t\t\t\t\tawait this.emitOwn(\n\t\t\t\t\t\t{ type: \"after_provider_response\", status: response.status, headers },\n\t\t\t\t\t\tstreamOptions?.signal,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\treasoning: streamOptions?.reasoning,\n\t\t\t\tsignal: streamOptions?.signal,\n\t\t\t\tsessionId: turnState.sessionId,\n\t\t\t\ttimeoutMs: requestOptions.timeoutMs,\n\t\t\t\ttransport: requestOptions.transport,\n\t\t\t});\n\t\t};\n\t}\n\n\tprivate async drainQueuedMessages(queue: AgentMessage[], mode: QueueMode): Promise<AgentMessage[]> {\n\t\tconst messages = mode === \"all\" ? queue.splice(0) : queue.splice(0, 1);\n\t\tif (messages.length === 0) return messages;\n\t\ttry {\n\t\t\tawait this.emitQueueUpdate();\n\t\t\treturn messages;\n\t\t} catch (error) {\n\t\t\tqueue.unshift(...messages);\n\t\t\tthrow normalizeHookError(error);\n\t\t}\n\t}\n\n\tprivate createLoopConfig(\n\t\tgetTurnState: () => AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>,\n\t\tsetTurnState: (turnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>) => void,\n\t): AgentLoopConfig {\n\t\tconst turnState = getTurnState();\n\t\treturn {\n\t\t\tmodel: turnState.model,\n\t\t\treasoning: turnState.thinkingLevel === \"off\" ? undefined : turnState.thinkingLevel,\n\t\t\tconvertToLlm,\n\t\t\ttransformContext: async (messages) => {\n\t\t\t\tconst result = await this.emitHook({ type: \"context\", messages: [...messages] });\n\t\t\t\treturn result?.messages ?? messages;\n\t\t\t},\n\t\t\tbeforeToolCall: async ({ toolCall, args }) => {\n\t\t\t\tconst result = await this.emitHook({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\t\ttoolName: toolCall.name,\n\t\t\t\t\tinput: args as Record<string, unknown>,\n\t\t\t\t});\n\t\t\t\treturn result ? { block: result.block, reason: result.reason } : undefined;\n\t\t\t},\n\t\t\tafterToolCall: async ({ toolCall, args, result, isError }) => {\n\t\t\t\tconst patch = await this.emitHook({\n\t\t\t\t\ttype: \"tool_result\",\n\t\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\t\ttoolName: toolCall.name,\n\t\t\t\t\tinput: args as Record<string, unknown>,\n\t\t\t\t\tcontent: result.content,\n\t\t\t\t\tdetails: result.details,\n\t\t\t\t\tisError,\n\t\t\t\t});\n\t\t\t\treturn patch\n\t\t\t\t\t? { content: patch.content, details: patch.details, isError: patch.isError, terminate: patch.terminate }\n\t\t\t\t\t: undefined;\n\t\t\t},\n\t\t\tprepareNextTurn: async () => {\n\t\t\t\tawait this.flushPendingSessionWrites();\n\t\t\t\tconst nextTurnState = await this.createTurnState();\n\t\t\t\tsetTurnState(nextTurnState);\n\t\t\t\treturn {\n\t\t\t\t\tcontext: this.createContext(nextTurnState),\n\t\t\t\t\tmodel: nextTurnState.model,\n\t\t\t\t\tthinkingLevel: nextTurnState.thinkingLevel,\n\t\t\t\t};\n\t\t\t},\n\t\t\tgetSteeringMessages: async () => this.drainQueuedMessages(this.steerQueue, this.steeringQueueMode),\n\t\t\tgetFollowUpMessages: async () => this.drainQueuedMessages(this.followUpQueue, this.followUpQueueMode),\n\t\t};\n\t}\n\n\tprivate validateUniqueNames(names: string[], message: string): void {\n\t\tconst duplicates = findDuplicateNames(names);\n\t\tif (duplicates.length > 0)\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `${message}: ${duplicates.join(\", \")}`);\n\t}\n\n\tprivate validateToolNames(toolNames: string[], tools: Map<string, TTool> = this.tools): void {\n\t\tthis.validateUniqueNames(toolNames, \"Duplicate active tool name(s)\");\n\t\tconst missing = toolNames.filter((name) => !tools.has(name));\n\t\tif (missing.length > 0) throw new AgentHarnessError(\"invalid_argument\", `Unknown tool(s): ${missing.join(\", \")}`);\n\t}\n\n\tprivate async flushPendingSessionWrites(): Promise<void> {\n\t\twhile (this.pendingSessionWrites.length > 0) {\n\t\t\tconst write = this.pendingSessionWrites[0]!;\n\t\t\tif (write.type === \"message\") {\n\t\t\t\tawait this.session.appendMessage(write.message);\n\t\t\t} else if (write.type === \"model_change\") {\n\t\t\t\tawait this.session.appendModelChange(write.provider, write.modelId);\n\t\t\t} else if (write.type === \"thinking_level_change\") {\n\t\t\t\tawait this.session.appendThinkingLevelChange(write.thinkingLevel);\n\t\t\t} else if (write.type === \"active_tools_change\") {\n\t\t\t\tawait this.session.appendActiveToolsChange(write.activeToolNames);\n\t\t\t} else if (write.type === \"custom\") {\n\t\t\t\tawait this.session.appendCustomEntry(write.customType, write.data);\n\t\t\t} else if (write.type === \"custom_message\") {\n\t\t\t\tawait this.session.appendCustomMessageEntry(write.customType, write.content, write.display, write.details);\n\t\t\t} else if (write.type === \"label\") {\n\t\t\t\tawait this.session.appendLabel(write.targetId, write.label);\n\t\t\t} else if (write.type === \"session_info\") {\n\t\t\t\tawait this.session.appendSessionName(write.name ?? \"\");\n\t\t\t} else if (write.type === \"leaf\") {\n\t\t\t\tawait this.session.getStorage().setLeafId(write.targetId);\n\t\t\t}\n\t\t\tthis.pendingSessionWrites.shift();\n\t\t}\n\t}\n\n\tprivate async handleAgentEvent(event: AgentEvent, signal?: AbortSignal): Promise<void> {\n\t\tif (event.type === \"message_end\") {\n\t\t\tawait this.session.appendMessage(event.message);\n\t\t\tawait this.emitAny(event, signal);\n\t\t\treturn;\n\t\t}\n\t\tif (event.type === \"turn_end\") {\n\t\t\tlet eventError: unknown;\n\t\t\ttry {\n\t\t\t\tawait this.emitAny(event, signal);\n\t\t\t} catch (error) {\n\t\t\t\teventError = error;\n\t\t\t}\n\t\t\tconst hadPendingMutations = this.pendingSessionWrites.length > 0;\n\t\t\tawait this.flushPendingSessionWrites();\n\t\t\tif (eventError) throw eventError;\n\t\t\tawait this.emitOwn({ type: \"save_point\", hadPendingMutations });\n\t\t\treturn;\n\t\t}\n\t\tif (event.type === \"agent_end\") {\n\t\t\tawait this.flushPendingSessionWrites();\n\t\t\tthis.phase = \"idle\";\n\t\t\tawait this.emitAny(event, signal);\n\t\t\tawait this.emitOwn({ type: \"settled\", nextTurnCount: this.nextTurnQueue.length }, signal);\n\t\t\treturn;\n\t\t}\n\t\tawait this.emitAny(event, signal);\n\t}\n\n\tprivate async emitRunFailure(\n\t\tmodel: Model<any>,\n\t\terror: unknown,\n\t\taborted: boolean,\n\t\tsignal: AbortSignal,\n\t): Promise<AgentMessage[]> {\n\t\tconst failureMessage = createFailureMessage(model, error, aborted);\n\t\tawait this.handleAgentEvent({ type: \"message_start\", message: failureMessage }, signal);\n\t\tawait this.handleAgentEvent({ type: \"message_end\", message: failureMessage }, signal);\n\t\tawait this.handleAgentEvent({ type: \"turn_end\", message: failureMessage, toolResults: [] }, signal);\n\t\tawait this.handleAgentEvent({ type: \"agent_end\", messages: [failureMessage] }, signal);\n\t\treturn [failureMessage];\n\t}\n\n\tprivate async executeTurn(\n\t\tturnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>,\n\t\ttext: string,\n\t\toptions?: { images?: ImageContent[] },\n\t): Promise<AssistantMessage> {\n\t\tlet activeTurnState = turnState;\n\t\tlet messages: AgentMessage[] = [createUserMessage(text, options?.images)];\n\t\tif (this.nextTurnQueue.length > 0) {\n\t\t\tconst queuedMessages = this.nextTurnQueue.splice(0);\n\t\t\ttry {\n\t\t\t\tawait this.emitQueueUpdate();\n\t\t\t} catch (error) {\n\t\t\t\tthis.nextTurnQueue.unshift(...queuedMessages);\n\t\t\t\tthrow normalizeHookError(error);\n\t\t\t}\n\t\t\tmessages = [...queuedMessages, messages[0]!];\n\t\t}\n\t\tconst beforeResult = await this.emitHook({\n\t\t\ttype: \"before_agent_start\",\n\t\t\tprompt: text,\n\t\t\timages: options?.images,\n\t\t\tsystemPrompt: turnState.systemPrompt,\n\t\t\tresources: turnState.resources,\n\t\t});\n\t\tif (beforeResult?.messages) messages = [...messages, ...beforeResult.messages];\n\n\t\tconst abortController = new AbortController();\n\t\tconst getTurnState = () => activeTurnState;\n\t\tconst setTurnState = (nextTurnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>) => {\n\t\t\tactiveTurnState = nextTurnState;\n\t\t};\n\t\tthis.runAbortController = abortController;\n\t\tconst runResultPromise = (async () => {\n\t\t\ttry {\n\t\t\t\treturn await runAgentLoop(\n\t\t\t\t\tmessages,\n\t\t\t\t\tthis.createContext(turnState, beforeResult?.systemPrompt),\n\t\t\t\t\tthis.createLoopConfig(getTurnState, setTurnState),\n\t\t\t\t\t(event) => this.handleAgentEvent(event, abortController.signal),\n\t\t\t\t\tabortController.signal,\n\t\t\t\t\tthis.createStreamFn(getTurnState),\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.emitRunFailure(\n\t\t\t\t\t\tactiveTurnState.model,\n\t\t\t\t\t\terror,\n\t\t\t\t\t\tabortController.signal.aborted,\n\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t);\n\t\t\t\t} catch (failureError) {\n\t\t\t\t\tconst cause = new AggregateError(\n\t\t\t\t\t\t[toError(error), toError(failureError)],\n\t\t\t\t\t\t\"Agent run failed and failure reporting failed\",\n\t\t\t\t\t);\n\t\t\t\t\tthrow new AgentHarnessError(\"unknown\", cause.message, cause);\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\t\ttry {\n\t\t\tconst newMessages = await runResultPromise;\n\t\t\tfor (let i = newMessages.length - 1; i >= 0; i--) {\n\t\t\t\tconst message = newMessages[i]!;\n\t\t\t\tif (message.role === \"assistant\") {\n\t\t\t\t\treturn message;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new AgentHarnessError(\"invalid_state\", \"AgentHarness prompt completed without an assistant message\");\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tawait this.flushPendingSessionWrites();\n\t\t\t} finally {\n\t\t\t\tthis.runAbortController = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync prompt(text: string, options?: { images?: ImageContent[] }): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\tconst finishRunPromise = this.startRunPromise();\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\treturn await this.executeTurn(turnState, text, options);\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow normalizeHarnessError(error, \"unknown\");\n\t\t} finally {\n\t\t\tfinishRunPromise();\n\t\t}\n\t}\n\n\tasync skill(name: string, additionalInstructions?: string): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\tconst finishRunPromise = this.startRunPromise();\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\tconst skill = (turnState.resources.skills ?? []).find((candidate) => candidate.name === name);\n\t\t\tif (!skill) throw new AgentHarnessError(\"invalid_argument\", `Unknown skill: ${name}`);\n\t\t\treturn await this.executeTurn(turnState, formatSkillInvocation(skill, additionalInstructions));\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow normalizeHarnessError(error, \"unknown\");\n\t\t} finally {\n\t\t\tfinishRunPromise();\n\t\t}\n\t}\n\n\tasync promptFromTemplate(name: string, args: string[] = []): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\tconst finishRunPromise = this.startRunPromise();\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\tconst template = (turnState.resources.promptTemplates ?? []).find((candidate) => candidate.name === name);\n\t\t\tif (!template) throw new AgentHarnessError(\"invalid_argument\", `Unknown prompt template: ${name}`);\n\t\t\treturn await this.executeTurn(turnState, formatPromptTemplateInvocation(template, args));\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow normalizeHarnessError(error, \"unknown\");\n\t\t} finally {\n\t\t\tfinishRunPromise();\n\t\t}\n\t}\n\n\tasync steer(text: string, options?: { images?: ImageContent[] }): Promise<void> {\n\t\tif (this.phase === \"idle\") throw new AgentHarnessError(\"invalid_state\", \"Cannot steer while idle\");\n\t\tthis.steerQueue.push(createUserMessage(text, options?.images));\n\t\tawait this.emitQueueUpdate();\n\t}\n\n\tasync followUp(text: string, options?: { images?: ImageContent[] }): Promise<void> {\n\t\tif (this.phase === \"idle\") throw new AgentHarnessError(\"invalid_state\", \"Cannot follow up while idle\");\n\t\tthis.followUpQueue.push(createUserMessage(text, options?.images));\n\t\tawait this.emitQueueUpdate();\n\t}\n\n\tasync nextTurn(text: string, options?: { images?: ImageContent[] }): Promise<void> {\n\t\tthis.nextTurnQueue.push(createUserMessage(text, options?.images));\n\t\tawait this.emitQueueUpdate();\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<void> {\n\t\ttry {\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendMessage(message);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"message\", message });\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"session\");\n\t\t}\n\t}\n\n\tasync compact(\n\t\tcustomInstructions?: string,\n\t): Promise<{ summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown }> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"compact() requires idle harness\");\n\t\tthis.phase = \"compaction\";\n\t\ttry {\n\t\t\tconst model = this.model;\n\t\t\tif (!model) throw new AgentHarnessError(\"invalid_state\", \"No model set for compaction\");\n\t\t\tconst branchEntries = await this.session.getBranch();\n\t\t\tconst preparationResult = prepareCompaction(branchEntries, DEFAULT_COMPACTION_SETTINGS);\n\t\t\tif (!preparationResult.ok) throw preparationResult.error;\n\t\t\tconst preparation = preparationResult.value;\n\t\t\tif (!preparation) throw new AgentHarnessError(\"compaction\", \"Nothing to compact\");\n\t\t\tconst hookResult = await this.emitHook({\n\t\t\t\ttype: \"session_before_compact\",\n\t\t\t\tpreparation,\n\t\t\t\tbranchEntries,\n\t\t\t\tcustomInstructions,\n\t\t\t\tsignal: new AbortController().signal,\n\t\t\t});\n\t\t\tif (hookResult?.cancel) throw new AgentHarnessError(\"compaction\", \"Compaction cancelled\");\n\t\t\tconst provided = hookResult?.compaction;\n\t\t\tconst compactResult = provided\n\t\t\t\t? { ok: true as const, value: provided }\n\t\t\t\t: await compact(preparation, this.models, model, customInstructions, undefined, this.thinkingLevel);\n\t\t\tif (!compactResult.ok) throw compactResult.error;\n\t\t\tconst result = compactResult.value;\n\t\t\tconst entryId = await this.session.appendCompaction(\n\t\t\t\tresult.summary,\n\t\t\t\tresult.firstKeptEntryId,\n\t\t\t\tresult.tokensBefore,\n\t\t\t\tresult.details,\n\t\t\t\tprovided !== undefined,\n\t\t\t);\n\t\t\tconst entry = await this.session.getEntry(entryId);\n\t\t\tif (entry?.type === \"compaction\") {\n\t\t\t\tawait this.emitOwn({ type: \"session_compact\", compactionEntry: entry, fromHook: provided !== undefined });\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"compaction\");\n\t\t} finally {\n\t\t\tthis.phase = \"idle\";\n\t\t}\n\t}\n\n\tasync navigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<NavigateTreeResult> {\n\t\tif (this.phase !== \"idle\") throw new AgentHarnessError(\"busy\", \"navigateTree() requires idle harness\");\n\t\tthis.phase = \"branch_summary\";\n\t\ttry {\n\t\t\tconst oldLeafId = await this.session.getLeafId();\n\t\t\tif (oldLeafId === targetId) return { cancelled: false };\n\t\t\tconst targetEntry = await this.session.getEntry(targetId);\n\t\t\tif (!targetEntry) throw new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t\tconst { entries, commonAncestorId } = await collectEntriesForBranchSummary(this.session, oldLeafId, targetId);\n\t\t\tconst preparation = {\n\t\t\t\ttargetId,\n\t\t\t\toldLeafId,\n\t\t\t\tcommonAncestorId,\n\t\t\t\tentriesToSummarize: entries,\n\t\t\t\tuserWantsSummary: options?.summarize ?? false,\n\t\t\t\tcustomInstructions: options?.customInstructions,\n\t\t\t\treplaceInstructions: options?.replaceInstructions,\n\t\t\t\tlabel: options?.label,\n\t\t\t};\n\t\t\tconst signal = new AbortController().signal;\n\t\t\tconst hookResult = await this.emitHook({ type: \"session_before_tree\", preparation, signal });\n\t\t\tif (hookResult?.cancel) return { cancelled: true };\n\t\t\tlet summaryEntry: NavigateTreeResult[\"summaryEntry\"];\n\t\t\tlet summaryText: string | undefined = hookResult?.summary?.summary;\n\t\t\tlet summaryDetails: unknown = hookResult?.summary?.details;\n\t\t\tif (!summaryText && options?.summarize && entries.length > 0) {\n\t\t\t\tconst model = this.model;\n\t\t\t\tif (!model) throw new AgentHarnessError(\"invalid_state\", \"No model set for branch summary\");\n\t\t\t\tconst branchSummary = await generateBranchSummary(entries, {\n\t\t\t\t\tmodels: this.models,\n\t\t\t\t\tmodel,\n\t\t\t\t\tsignal: new AbortController().signal,\n\t\t\t\t\tcustomInstructions: hookResult?.customInstructions ?? options?.customInstructions,\n\t\t\t\t\treplaceInstructions: hookResult?.replaceInstructions ?? options?.replaceInstructions,\n\t\t\t\t});\n\t\t\t\tif (!branchSummary.ok) {\n\t\t\t\t\tif (branchSummary.error.code === \"aborted\") return { cancelled: true };\n\t\t\t\t\tthrow new AgentHarnessError(\"branch_summary\", branchSummary.error.message, branchSummary.error);\n\t\t\t\t}\n\t\t\t\tsummaryText = branchSummary.value.summary;\n\t\t\t\tsummaryDetails = {\n\t\t\t\t\treadFiles: branchSummary.value.readFiles,\n\t\t\t\t\tmodifiedFiles: branchSummary.value.modifiedFiles,\n\t\t\t\t};\n\t\t\t}\n\t\t\tlet editorText: string | undefined;\n\t\t\tlet newLeafId: string | null;\n\t\t\tif (targetEntry.type === \"message\" && targetEntry.message.role === \"user\") {\n\t\t\t\tnewLeafId = targetEntry.parentId;\n\t\t\t\tconst content = targetEntry.message.content;\n\t\t\t\teditorText =\n\t\t\t\t\ttypeof content === \"string\"\n\t\t\t\t\t\t? content\n\t\t\t\t\t\t: content\n\t\t\t\t\t\t\t\t.filter((c): c is { readonly type: \"text\"; readonly text: string } => c.type === \"text\")\n\t\t\t\t\t\t\t\t.map((c) => c.text)\n\t\t\t\t\t\t\t\t.join(\"\");\n\t\t\t} else if (targetEntry.type === \"custom_message\") {\n\t\t\t\tnewLeafId = targetEntry.parentId;\n\t\t\t\teditorText =\n\t\t\t\t\ttypeof targetEntry.content === \"string\"\n\t\t\t\t\t\t? targetEntry.content\n\t\t\t\t\t\t: targetEntry.content\n\t\t\t\t\t\t\t\t.filter((c): c is { readonly type: \"text\"; readonly text: string } => c.type === \"text\")\n\t\t\t\t\t\t\t\t.map((c) => c.text)\n\t\t\t\t\t\t\t\t.join(\"\");\n\t\t\t} else {\n\t\t\t\tnewLeafId = targetId;\n\t\t\t}\n\t\t\tconst summaryId = await this.session.moveTo(\n\t\t\t\tnewLeafId,\n\t\t\t\tsummaryText\n\t\t\t\t\t? { summary: summaryText, details: summaryDetails, fromHook: hookResult?.summary !== undefined }\n\t\t\t\t\t: undefined,\n\t\t\t);\n\t\t\tif (summaryId) {\n\t\t\t\tconst entry = await this.session.getEntry(summaryId);\n\t\t\t\tif (entry?.type === \"branch_summary\") summaryEntry = entry;\n\t\t\t}\n\t\t\tawait this.emitOwn({\n\t\t\t\ttype: \"session_tree\",\n\t\t\t\tnewLeafId: await this.session.getLeafId(),\n\t\t\t\toldLeafId,\n\t\t\t\tsummaryEntry,\n\t\t\t\tfromHook: hookResult?.summary !== undefined,\n\t\t\t});\n\t\t\treturn { cancelled: false, editorText, summaryEntry };\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"branch_summary\");\n\t\t} finally {\n\t\t\tthis.phase = \"idle\";\n\t\t}\n\t}\n\n\tgetModel(): Model<any> {\n\t\treturn this.model;\n\t}\n\n\tasync setModel(model: Model<any>): Promise<void> {\n\t\ttry {\n\t\t\tconst previousModel = this.model;\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendModelChange(model.provider, model.id);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"model_change\", provider: model.provider, modelId: model.id });\n\t\t\t}\n\t\t\tthis.model = model;\n\t\t\tawait this.emitOwn({ type: \"model_update\", model, previousModel, source: \"set\" });\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"session\");\n\t\t}\n\t}\n\n\tgetThinkingLevel(): ThinkingLevel {\n\t\treturn this.thinkingLevel;\n\t}\n\n\tasync setThinkingLevel(level: ThinkingLevel): Promise<void> {\n\t\ttry {\n\t\t\tconst previousLevel = this.thinkingLevel;\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendThinkingLevelChange(level);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"thinking_level_change\", thinkingLevel: level });\n\t\t\t}\n\t\t\tthis.thinkingLevel = level;\n\t\t\tawait this.emitOwn({ type: \"thinking_level_update\", level, previousLevel });\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"session\");\n\t\t}\n\t}\n\n\tgetTools(): TTool[] {\n\t\treturn [...this.tools.values()];\n\t}\n\n\tasync setTools(tools: TTool[], activeToolNames?: string[]): Promise<void> {\n\t\ttry {\n\t\t\tthis.validateUniqueNames(\n\t\t\t\ttools.map((tool) => tool.name),\n\t\t\t\t\"Duplicate tool name(s)\",\n\t\t\t);\n\t\t\tconst nextTools = new Map(tools.map((tool) => [tool.name, tool]));\n\t\t\tconst nextActiveToolNames = activeToolNames ? [...activeToolNames] : this.activeToolNames;\n\t\t\tthis.validateToolNames(nextActiveToolNames, nextTools);\n\t\t\tconst previousToolNames = [...this.tools.keys()];\n\t\t\tconst previousActiveToolNames = [...this.activeToolNames];\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendActiveToolsChange(nextActiveToolNames);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"active_tools_change\", activeToolNames: [...nextActiveToolNames] });\n\t\t\t}\n\t\t\tthis.tools = nextTools;\n\t\t\tthis.activeToolNames = [...nextActiveToolNames];\n\t\t\tawait this.emitOwn({\n\t\t\t\ttype: \"tools_update\",\n\t\t\t\ttoolNames: [...this.tools.keys()],\n\t\t\t\tpreviousToolNames,\n\t\t\t\tactiveToolNames: [...this.activeToolNames],\n\t\t\t\tpreviousActiveToolNames,\n\t\t\t\tsource: \"set\",\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"invalid_argument\");\n\t\t}\n\t}\n\n\tgetActiveTools(): TTool[] {\n\t\treturn this.activeToolNames.map((name) => this.tools.get(name)!);\n\t}\n\n\tasync setActiveTools(toolNames: string[]): Promise<void> {\n\t\ttry {\n\t\t\tthis.validateToolNames(toolNames);\n\t\t\tconst previousToolNames = [...this.tools.keys()];\n\t\t\tconst previousActiveToolNames = [...this.activeToolNames];\n\t\t\tif (this.phase === \"idle\") {\n\t\t\t\tawait this.session.appendActiveToolsChange(toolNames);\n\t\t\t} else {\n\t\t\t\tthis.pendingSessionWrites.push({ type: \"active_tools_change\", activeToolNames: [...toolNames] });\n\t\t\t}\n\t\t\tthis.activeToolNames = [...toolNames];\n\t\t\tawait this.emitOwn({\n\t\t\t\ttype: \"tools_update\",\n\t\t\t\ttoolNames: [...this.tools.keys()],\n\t\t\t\tpreviousToolNames,\n\t\t\t\tactiveToolNames: [...this.activeToolNames],\n\t\t\t\tpreviousActiveToolNames,\n\t\t\t\tsource: \"set\",\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow normalizeHarnessError(error, \"invalid_argument\");\n\t\t}\n\t}\n\n\tgetSteeringMode(): QueueMode {\n\t\treturn this.steeringQueueMode;\n\t}\n\n\tasync setSteeringMode(mode: QueueMode): Promise<void> {\n\t\tthis.steeringQueueMode = mode;\n\t}\n\n\tgetFollowUpMode(): QueueMode {\n\t\treturn this.followUpQueueMode;\n\t}\n\n\tasync setFollowUpMode(mode: QueueMode): Promise<void> {\n\t\tthis.followUpQueueMode = mode;\n\t}\n\n\tgetResources(): AgentHarnessResources<TSkill, TPromptTemplate> {\n\t\treturn {\n\t\t\tskills: this.resources.skills?.slice(),\n\t\t\tpromptTemplates: this.resources.promptTemplates?.slice(),\n\t\t};\n\t}\n\n\tasync setResources(resources: AgentHarnessResources<TSkill, TPromptTemplate>): Promise<void> {\n\t\tconst previousResources = this.getResources();\n\t\tthis.resources = {\n\t\t\tskills: resources.skills?.slice(),\n\t\t\tpromptTemplates: resources.promptTemplates?.slice(),\n\t\t};\n\t\tawait this.emitOwn({ type: \"resources_update\", resources: this.getResources(), previousResources });\n\t}\n\n\tgetStreamOptions(): AgentHarnessStreamOptions {\n\t\treturn cloneStreamOptions(this.streamOptions);\n\t}\n\n\tasync setStreamOptions(streamOptions: AgentHarnessStreamOptions): Promise<void> {\n\t\tthis.streamOptions = cloneStreamOptions(streamOptions);\n\t}\n\n\tasync abort(): Promise<AbortResult> {\n\t\tconst clearedSteer = [...this.steerQueue];\n\t\tconst clearedFollowUp = [...this.followUpQueue];\n\t\tthis.steerQueue = [];\n\t\tthis.followUpQueue = [];\n\t\tthis.runAbortController?.abort();\n\t\tconst errors: Error[] = [];\n\t\ttry {\n\t\t\tawait this.emitQueueUpdate();\n\t\t} catch (error) {\n\t\t\terrors.push(toError(error));\n\t\t}\n\t\ttry {\n\t\t\tawait this.waitForIdle();\n\t\t} catch (error) {\n\t\t\terrors.push(toError(error));\n\t\t}\n\t\ttry {\n\t\t\tawait this.emitOwn({ type: \"abort\", clearedSteer, clearedFollowUp });\n\t\t} catch (error) {\n\t\t\terrors.push(toError(error));\n\t\t}\n\t\tif (errors.length > 0) {\n\t\t\tconst cause = errors.length === 1 ? errors[0]! : new AggregateError(errors, \"Abort completed with errors\");\n\t\t\tthrow normalizeHarnessError(cause, \"hook\");\n\t\t}\n\t\treturn { clearedSteer, clearedFollowUp };\n\t}\n\n\tasync waitForIdle(): Promise<void> {\n\t\tawait this.runPromise;\n\t}\n\n\tsubscribe(\n\t\tlistener: (event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal) => Promise<void> | void,\n\t): () => void {\n\t\tlet handlers = this.handlers.get(SUBSCRIBER_EVENT_TYPE);\n\t\tif (!handlers) {\n\t\t\thandlers = new Set();\n\t\t\tthis.handlers.set(SUBSCRIBER_EVENT_TYPE, handlers);\n\t\t}\n\t\thandlers.add(listener as AgentHarnessHandler);\n\t\treturn () => handlers!.delete(listener as AgentHarnessHandler);\n\t}\n\n\ton<TType extends keyof AgentHarnessEventResultMap>(\n\t\ttype: TType,\n\t\thandler: (\n\t\t\tevent: Extract<AgentHarnessOwnEvent, { type: TType }>,\n\t\t) => Promise<AgentHarnessEventResultMap[TType]> | AgentHarnessEventResultMap[TType],\n\t): () => void {\n\t\tlet handlers = this.handlers.get(type);\n\t\tif (!handlers) {\n\t\t\thandlers = new Set();\n\t\t\tthis.handlers.set(type, handlers);\n\t\t}\n\t\thandlers.add(handler as AgentHarnessHandler);\n\t\treturn () => handlers!.delete(handler as AgentHarnessHandler);\n\t}\n}\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { streamSimple, } from "@earendil-works/pi-ai/base";
|
|
2
1
|
import { runAgentLoop } from "../agent-loop.js";
|
|
3
2
|
import { collectEntriesForBranchSummary, generateBranchSummary } from "./compaction/branch-summarization.js";
|
|
4
3
|
import { compact, DEFAULT_COMPACTION_SETTINGS, prepareCompaction } from "./compaction/compaction.js";
|
|
@@ -39,17 +38,6 @@ function cloneStreamOptions(streamOptions) {
|
|
|
39
38
|
metadata: streamOptions?.metadata ? { ...streamOptions.metadata } : undefined,
|
|
40
39
|
};
|
|
41
40
|
}
|
|
42
|
-
function mergeHeaders(...headers) {
|
|
43
|
-
const merged = {};
|
|
44
|
-
let hasHeaders = false;
|
|
45
|
-
for (const entry of headers) {
|
|
46
|
-
if (!entry)
|
|
47
|
-
continue;
|
|
48
|
-
Object.assign(merged, entry);
|
|
49
|
-
hasHeaders = true;
|
|
50
|
-
}
|
|
51
|
-
return hasHeaders ? merged : undefined;
|
|
52
|
-
}
|
|
53
41
|
function findDuplicateNames(names) {
|
|
54
42
|
const seen = new Set();
|
|
55
43
|
const duplicates = new Set();
|
|
@@ -125,6 +113,7 @@ function normalizeHookError(error) {
|
|
|
125
113
|
export class AgentHarness {
|
|
126
114
|
env;
|
|
127
115
|
session;
|
|
116
|
+
models;
|
|
128
117
|
phase = "idle";
|
|
129
118
|
runAbortController;
|
|
130
119
|
runPromise;
|
|
@@ -133,7 +122,6 @@ export class AgentHarness {
|
|
|
133
122
|
thinkingLevel;
|
|
134
123
|
systemPrompt;
|
|
135
124
|
streamOptions;
|
|
136
|
-
getApiKeyAndHeaders;
|
|
137
125
|
resources;
|
|
138
126
|
tools = new Map();
|
|
139
127
|
activeToolNames;
|
|
@@ -146,10 +134,10 @@ export class AgentHarness {
|
|
|
146
134
|
constructor(options) {
|
|
147
135
|
this.env = options.env;
|
|
148
136
|
this.session = options.session;
|
|
137
|
+
this.models = options.models;
|
|
149
138
|
this.resources = options.resources ?? {};
|
|
150
139
|
this.streamOptions = cloneStreamOptions(options.streamOptions);
|
|
151
140
|
this.systemPrompt = options.systemPrompt;
|
|
152
|
-
this.getApiKeyAndHeaders = options.getApiKeyAndHeaders;
|
|
153
141
|
this.validateUniqueNames((options.tools ?? []).map((tool) => tool.name), "Duplicate tool name(s)");
|
|
154
142
|
for (const tool of options.tools ?? []) {
|
|
155
143
|
this.tools.set(tool.name, tool);
|
|
@@ -308,13 +296,9 @@ export class AgentHarness {
|
|
|
308
296
|
createStreamFn(getTurnState) {
|
|
309
297
|
return async (model, context, streamOptions) => {
|
|
310
298
|
const turnState = getTurnState();
|
|
311
|
-
const
|
|
312
|
-
const snapshotOptions = {
|
|
313
|
-
...turnState.streamOptions,
|
|
314
|
-
headers: mergeHeaders(turnState.streamOptions.headers, auth?.headers),
|
|
315
|
-
};
|
|
299
|
+
const snapshotOptions = { ...turnState.streamOptions };
|
|
316
300
|
const requestOptions = await this.emitBeforeProviderRequest(model, turnState.sessionId, snapshotOptions);
|
|
317
|
-
return streamSimple(model, context, {
|
|
301
|
+
return this.models.streamSimple(model, context, {
|
|
318
302
|
cacheRetention: requestOptions.cacheRetention,
|
|
319
303
|
headers: requestOptions.headers,
|
|
320
304
|
maxRetries: requestOptions.maxRetries,
|
|
@@ -330,7 +314,6 @@ export class AgentHarness {
|
|
|
330
314
|
sessionId: turnState.sessionId,
|
|
331
315
|
timeoutMs: requestOptions.timeoutMs,
|
|
332
316
|
transport: requestOptions.transport,
|
|
333
|
-
apiKey: auth?.apiKey,
|
|
334
317
|
});
|
|
335
318
|
};
|
|
336
319
|
}
|
|
@@ -632,9 +615,6 @@ export class AgentHarness {
|
|
|
632
615
|
const model = this.model;
|
|
633
616
|
if (!model)
|
|
634
617
|
throw new AgentHarnessError("invalid_state", "No model set for compaction");
|
|
635
|
-
const auth = await this.getApiKeyAndHeaders?.(model);
|
|
636
|
-
if (!auth)
|
|
637
|
-
throw new AgentHarnessError("auth", "No auth available for compaction");
|
|
638
618
|
const branchEntries = await this.session.getBranch();
|
|
639
619
|
const preparationResult = prepareCompaction(branchEntries, DEFAULT_COMPACTION_SETTINGS);
|
|
640
620
|
if (!preparationResult.ok)
|
|
@@ -654,7 +634,7 @@ export class AgentHarness {
|
|
|
654
634
|
const provided = hookResult?.compaction;
|
|
655
635
|
const compactResult = provided
|
|
656
636
|
? { ok: true, value: provided }
|
|
657
|
-
: await compact(preparation,
|
|
637
|
+
: await compact(preparation, this.models, model, customInstructions, undefined, this.thinkingLevel);
|
|
658
638
|
if (!compactResult.ok)
|
|
659
639
|
throw compactResult.error;
|
|
660
640
|
const result = compactResult.value;
|
|
@@ -705,13 +685,9 @@ export class AgentHarness {
|
|
|
705
685
|
const model = this.model;
|
|
706
686
|
if (!model)
|
|
707
687
|
throw new AgentHarnessError("invalid_state", "No model set for branch summary");
|
|
708
|
-
const auth = await this.getApiKeyAndHeaders?.(model);
|
|
709
|
-
if (!auth)
|
|
710
|
-
throw new AgentHarnessError("auth", "No auth available for branch summary");
|
|
711
688
|
const branchSummary = await generateBranchSummary(entries, {
|
|
689
|
+
models: this.models,
|
|
712
690
|
model,
|
|
713
|
-
apiKey: auth.apiKey,
|
|
714
|
-
headers: auth.headers,
|
|
715
691
|
signal: new AbortController().signal,
|
|
716
692
|
customInstructions: hookResult?.customInstructions ?? options?.customInstructions,
|
|
717
693
|
replaceInstructions: hookResult?.replaceInstructions ?? options?.replaceInstructions,
|