agent-harness 0.5.9 → 0.7.0
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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +160 -0
- data/lib/agent_harness/providers/aider.rb +361 -4
- data/lib/agent_harness/providers/codex.rb +38 -4
- data/lib/agent_harness/providers/github_copilot.rb +594 -1
- data/lib/agent_harness/providers/kilocode.rb +497 -2
- data/lib/agent_harness/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0a5be3b23b73351341808a0f62fb6e060f226c309ceb508c378ad3d549273e61
|
|
4
|
+
data.tar.gz: aa6f754664cd08deeb36f8a54ec3531249a10c80bbe6561706a59db1191afe4f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d28ce3a2ecc67df9f125c1bb82f51192811fd5cd8394252f9450ec746876d125d2487a65eef6bbf925bdb4438bd74e554213d9ac5aacbbca398357ba32ae1570
|
|
7
|
+
data.tar.gz: 63f514add56b1975e1d1a70f1962f8512a9af6eb4427ce893d4fd1c364bc054824fd5208198d61a6ecab0162cd4d382351bb09bcec7c4c0c7f63c5804ac007cd
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,165 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.7.0](https://github.com/viamin/agent-harness/compare/agent-harness/v0.6.0...agent-harness/v0.7.0) (2026-04-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **copilot:** add JSON output parsing and token extraction ([4f5fc5a](https://github.com/viamin/agent-harness/commit/4f5fc5acd8d45ac8563998a132a0c4878f3b9e0a))
|
|
9
|
+
* **kilocode:** extract token usage from Kilo CLI structured JSON output ([b5384f8](https://github.com/viamin/agent-harness/commit/b5384f8be52431f95d8aa3524a33ceed6bf094eb)), closes [#97](https://github.com/viamin/agent-harness/issues/97)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **copilot:** add nil guard for stdout and improve error string construction ([6a30ce3](https://github.com/viamin/agent-harness/commit/6a30ce342100b27c0b16fc8c2abdce48bbf10ef7))
|
|
15
|
+
* **copilot:** align error ordering with base parser ([0a02d34](https://github.com/viamin/agent-harness/commit/0a02d34cbf07e4f4ecb4d3efc6d69b5b072c6114))
|
|
16
|
+
* **copilot:** align metadata and reply parsing ([e5c3387](https://github.com/viamin/agent-harness/commit/e5c338743dbb5ec8eea5b1a8de5a515f1df7e141))
|
|
17
|
+
* **copilot:** avoid double-counting token aliases ([40e78f3](https://github.com/viamin/agent-harness/commit/40e78f34a6304a5ae21e26c6c291eed773618bea))
|
|
18
|
+
* **copilot:** avoid mixing shutdown token totals ([c4bdfb8](https://github.com/viamin/agent-harness/commit/c4bdfb8fd4c20781ef4621cf421947b14514cb45))
|
|
19
|
+
* **copilot:** drop superseded delta chunks ([769acd6](https://github.com/viamin/agent-harness/commit/769acd6a45f1037f11fc83ea23f5ddeda9aadd17))
|
|
20
|
+
* **copilot:** fall back across malformed token aliases ([9c9f5f8](https://github.com/viamin/agent-harness/commit/9c9f5f8048f63407b6ffc14fb339c26158f74dab))
|
|
21
|
+
* **copilot:** fall back from empty nested message content ([ecd9f49](https://github.com/viamin/agent-harness/commit/ecd9f497e0ef9812f2df363b02679b5842cf668c))
|
|
22
|
+
* **copilot:** fall back from empty shutdown metrics ([0397f1e](https://github.com/viamin/agent-harness/commit/0397f1e5f9442d0d6489c9fe7744d31a0ff48965))
|
|
23
|
+
* **copilot:** fall back from malformed nested message content ([a313487](https://github.com/viamin/agent-harness/commit/a313487f2c1bb9fbf5e7a60e5dc08e7a7079447c))
|
|
24
|
+
* **copilot:** fall back from malformed usage payloads ([733599c](https://github.com/viamin/agent-harness/commit/733599c599cda90873cec823c245b13c81f74ee6))
|
|
25
|
+
* **copilot:** gate json output by cli version ([528d03b](https://github.com/viamin/agent-harness/commit/528d03bed7506996cf9cfd6c4cf54807da254260))
|
|
26
|
+
* **copilot:** guard scalar json events ([13a4131](https://github.com/viamin/agent-harness/commit/13a413157cc159cfc4fd6b7e7ab7fdbb948d07b6))
|
|
27
|
+
* **copilot:** handle JSON event envelopes and camelCase token fields ([e0ee83e](https://github.com/viamin/agent-harness/commit/e0ee83ed73d715d9c67806b5253abab33cce9e19))
|
|
28
|
+
* **copilot:** hash unresolved probe path keys ([ea9aca2](https://github.com/viamin/agent-harness/commit/ea9aca215b000833be18bf035cba6c0bf029615d))
|
|
29
|
+
* **copilot:** hide structured control events from output ([81c108d](https://github.com/viamin/agent-harness/commit/81c108d521e8f72522dc1dc604cccdf46f0d01d4))
|
|
30
|
+
* **copilot:** ignore delta chunks after final reply ([6aef1ca](https://github.com/viamin/agent-harness/commit/6aef1ca4459a5588fc53b70692f95c6eff3b9d88))
|
|
31
|
+
* **copilot:** ignore empty delta chunks ([dca6395](https://github.com/viamin/agent-harness/commit/dca63951866a4dc65b2adca3b834d05a6716f298))
|
|
32
|
+
* **copilot:** ignore failed version probes ([fa6ba35](https://github.com/viamin/agent-harness/commit/fa6ba35e1e36f153f47bccf9c423cea7927176e7))
|
|
33
|
+
* **copilot:** ignore malformed delta content fallback ([0c9211b](https://github.com/viamin/agent-harness/commit/0c9211bb16e713f4b7185c1b9ccda5d065b9d405))
|
|
34
|
+
* **copilot:** ignore malformed token payloads ([0f2f06b](https://github.com/viamin/agent-harness/commit/0f2f06b9a8c0b4244c7b8040e5590cc6e4710143))
|
|
35
|
+
* **copilot:** ignore malformed typed json fallbacks ([0cd5535](https://github.com/viamin/agent-harness/commit/0cd553594aff277ce07abd3d151028ad22b3f597))
|
|
36
|
+
* **copilot:** ignore nested non-assistant fallback text ([799f976](https://github.com/viamin/agent-harness/commit/799f976ef19618acaae1a6e0ecf7faa76b9ff37f))
|
|
37
|
+
* **copilot:** ignore non-assistant top-level messages ([119c854](https://github.com/viamin/agent-harness/commit/119c8540e7db9f1827eac75e73403b638cf8db23))
|
|
38
|
+
* **copilot:** ignore non-assistant top-level token payloads ([23c05b9](https://github.com/viamin/agent-harness/commit/23c05b9dbc6231755783f4a8eefd5099067f9389))
|
|
39
|
+
* **copilot:** ignore partial invalid token aliases ([3344c07](https://github.com/viamin/agent-harness/commit/3344c078d291277b8831ab69fa5d2a40ca95135b))
|
|
40
|
+
* **copilot:** isolate probe cache for PATH overrides ([5fa79a9](https://github.com/viamin/agent-harness/commit/5fa79a91fd90dd33a15d5c6a0c25c2619b8f0ac3))
|
|
41
|
+
* **copilot:** keep delta output on empty final reply ([64fbf68](https://github.com/viamin/agent-harness/commit/64fbf68ffb66b58d7546bc1841c78fec86201acb))
|
|
42
|
+
* **copilot:** keep preflight errors inside base handler ([49c9075](https://github.com/viamin/agent-harness/commit/49c9075d4a1fb9ff42d633723cb5f375e8c2721c))
|
|
43
|
+
* **copilot:** merge partial shutdown token totals ([49d6b2b](https://github.com/viamin/agent-harness/commit/49d6b2b6727297a58e9aa265347c781405012aa0))
|
|
44
|
+
* **copilot:** merge top-level token fallbacks ([826c1f9](https://github.com/viamin/agent-harness/commit/826c1f9fd427b10c40fb34c5b47f4c9fb06f1e64))
|
|
45
|
+
* **copilot:** parse session shutdown token totals ([0148afd](https://github.com/viamin/agent-harness/commit/0148afd2f95312eae687799541545dd8ece185f3))
|
|
46
|
+
* **copilot:** parse streamed delta reply events ([55f553f](https://github.com/viamin/agent-harness/commit/55f553f39dbe729588641b86168f8c36500ec872))
|
|
47
|
+
* **copilot:** prefer final replies and trim probe cache keys ([38dc20e](https://github.com/viamin/agent-harness/commit/38dc20edac2326265c8f9d149394650b70c43e90))
|
|
48
|
+
* **copilot:** prefer final reply over delta chunks ([955654d](https://github.com/viamin/agent-harness/commit/955654db325cc47c8cf0ac992de55e574a45c641))
|
|
49
|
+
* **copilot:** prefer nested assistant message fallback ([15aa6db](https://github.com/viamin/agent-harness/commit/15aa6dbf4bb171eaaa5b85c552008953141ba622))
|
|
50
|
+
* **copilot:** prefer per-turn usage over shutdown totals ([38bd47c](https://github.com/viamin/agent-harness/commit/38bd47c00e5d74ef365a74dc61df9e2fdc5b9c04))
|
|
51
|
+
* **copilot:** prefer populated top-level usage payloads ([8b3aac0](https://github.com/viamin/agent-harness/commit/8b3aac029004f65efe26e8b3fc27f62ac2008dca))
|
|
52
|
+
* **copilot:** preserve blank mixed output lines ([e9830fc](https://github.com/viamin/agent-harness/commit/e9830fcfcceeb7fc67caac658ed16867e1f303d0))
|
|
53
|
+
* **copilot:** preserve empty nested message payloads ([edf27bc](https://github.com/viamin/agent-harness/commit/edf27bcafbfb29dc0f5baf54fcedb8e959e20bba))
|
|
54
|
+
* **copilot:** preserve empty top-level fallback payloads ([1863854](https://github.com/viamin/agent-harness/commit/1863854af8545573a6c9a80cc47297487ee6d2ac))
|
|
55
|
+
* **copilot:** preserve legitimate exit codes in responses ([d1a3cc0](https://github.com/viamin/agent-harness/commit/d1a3cc0f21ea1a45fee0dbf181462efc8b414fc8))
|
|
56
|
+
* **copilot:** preserve literal json stdout ([7d7862c](https://github.com/viamin/agent-harness/commit/7d7862c4da83ccf9bafbbca19b595911923edeed))
|
|
57
|
+
* **copilot:** preserve malformed top-level json output ([f7c5bec](https://github.com/viamin/agent-harness/commit/f7c5becaa01584c53f1327be0afbd2fcebe7f3e8))
|
|
58
|
+
* **copilot:** preserve malformed usage hashes ([0eef69b](https://github.com/viamin/agent-harness/commit/0eef69b470927412c405a774125ca0aa9a58e302))
|
|
59
|
+
* **copilot:** preserve mixed json and text output ([95de0f7](https://github.com/viamin/agent-harness/commit/95de0f72a2bd8a86d33ab0806fb1baf481e52d03))
|
|
60
|
+
* **copilot:** preserve mixed output line boundaries ([b285526](https://github.com/viamin/agent-harness/commit/b28552627192aa613c73c7bf2c8c8afed6811c36))
|
|
61
|
+
* **copilot:** preserve mixed plain-text output ([d277f3a](https://github.com/viamin/agent-harness/commit/d277f3a4ed2799182a2083989a0bdaf9960df16b))
|
|
62
|
+
* **copilot:** preserve non-event typed json output ([27d01c6](https://github.com/viamin/agent-harness/commit/27d01c649cab65ba2da206636f45170af5abbcc9))
|
|
63
|
+
* **copilot:** preserve scalar json stdout ([7c5e74c](https://github.com/viamin/agent-harness/commit/7c5e74cd9aed9de17f572b98faac9c09b8cd9707))
|
|
64
|
+
* **copilot:** preserve unknown typed json output ([942edac](https://github.com/viamin/agent-harness/commit/942edac7bc36d9e26a7c02945f15503ce7faeb73))
|
|
65
|
+
* **copilot:** preserve zero token aliases ([853d251](https://github.com/viamin/agent-harness/commit/853d251869adb3cc36bebc5b6d750a58c70843f7))
|
|
66
|
+
* **copilot:** probe json support per request env ([da94082](https://github.com/viamin/agent-harness/commit/da94082a79a044eb89b470b26eeda29196e7ac95))
|
|
67
|
+
* **copilot:** reject invalid token counts ([106c386](https://github.com/viamin/agent-harness/commit/106c3867d024751e77aa0fc215c651de87230a13))
|
|
68
|
+
* **copilot:** restore reply token fallback ([c9d7182](https://github.com/viamin/agent-harness/commit/c9d71820b0becc80e44a77169c2990be20469be2))
|
|
69
|
+
* **copilot:** restrict token accumulation to usage event types only ([80c979b](https://github.com/viamin/agent-harness/commit/80c979b9952defa05923920a737bb74e1c8885e4))
|
|
70
|
+
* **copilot:** skip blank assistant boundaries ([b9dec9a](https://github.com/viamin/agent-harness/commit/b9dec9af0c6084e1809dc1fb1c50535b987037a1))
|
|
71
|
+
* **copilot:** skip json parsing in legacy mode ([af9ac12](https://github.com/viamin/agent-harness/commit/af9ac1253aa4246c92727bc04f23d6704ec8926e))
|
|
72
|
+
* **copilot:** store probe env per thread ([301f2aa](https://github.com/viamin/agent-harness/commit/301f2aa4c43b183dea8450d21eb7fec799e92ec6))
|
|
73
|
+
* **copilot:** stub json support in parser specs ([96a945e](https://github.com/viamin/agent-harness/commit/96a945ec1572e10231b696008c7514d1acb92c11))
|
|
74
|
+
* **copilot:** sum reply token fallback ([82c5097](https://github.com/viamin/agent-harness/commit/82c5097b73f17e64cd3015c34aa92bfd713c0428))
|
|
75
|
+
* **copilot:** support snake_case delta chunks ([d33ecbc](https://github.com/viamin/agent-harness/commit/d33ecbcc446d895d35f03a7513ad7b16f0f57b1d))
|
|
76
|
+
* **copilot:** support snake_case shutdown metrics ([8c6e93e](https://github.com/viamin/agent-harness/commit/8c6e93e70e0927c95c63811b247291faaceacab0))
|
|
77
|
+
* **copilot:** suppress additional control events ([b14b8bb](https://github.com/viamin/agent-harness/commit/b14b8bbebb2fcc0c975788c1168965bee3281a79))
|
|
78
|
+
* **copilot:** suppress control event namespaces ([7b31bb2](https://github.com/viamin/agent-harness/commit/7b31bb293fb858808e493a23e268d89a590741a0))
|
|
79
|
+
* **copilot:** suppress root control events ([5d4d3ec](https://github.com/viamin/agent-harness/commit/5d4d3ecb80864ad4d20e0da1083ac02d90773fbf))
|
|
80
|
+
* **copilot:** update output_format metadata and add missing parse_response tests ([7fdedde](https://github.com/viamin/agent-harness/commit/7fdeddee3bbe5eca96f29f5613a5c36189695ec4))
|
|
81
|
+
* **kilocode:** aggregate token counts across multiple step_finish events ([23c8c55](https://github.com/viamin/agent-harness/commit/23c8c55def0452de1e0b25765c4f6d1fcd3474d4))
|
|
82
|
+
* **kilocode:** avoid raw ndjson in structured failures ([932e56e](https://github.com/viamin/agent-harness/commit/932e56e06567095750a7bcf15bf5adea065eab44))
|
|
83
|
+
* **kilocode:** clear stale extra usage categories ([2ff7079](https://github.com/viamin/agent-harness/commit/2ff70795a9e3397d5844de92c47a59b49a599426))
|
|
84
|
+
* **kilocode:** count extra result usage tokens ([f65dd3d](https://github.com/viamin/agent-harness/commit/f65dd3d8e4f54174b18233ddd55dba2d3c3fd1f3))
|
|
85
|
+
* **kilocode:** count reasoning and cache step tokens ([c46d089](https://github.com/viamin/agent-harness/commit/c46d089d4d0492534d98736dcdcb1ff82a67d0c1))
|
|
86
|
+
* **kilocode:** fail on structured error events ([48f5378](https://github.com/viamin/agent-harness/commit/48f5378f37cf64af28238c67314a01a3758aab05))
|
|
87
|
+
* **kilocode:** fall back to result text after blank chunks ([7508c58](https://github.com/viamin/agent-harness/commit/7508c58a84f365a680578891dd582a6f13484f1e))
|
|
88
|
+
* **kilocode:** fall back to step token totals when usage is incomplete ([8534691](https://github.com/viamin/agent-harness/commit/8534691ff74ba4efa1ae5d50de329bc53e863bcf))
|
|
89
|
+
* **kilocode:** fall through blank part message aliases ([85db058](https://github.com/viamin/agent-harness/commit/85db05824684cde93755c4d87323bce79e0a1dd7))
|
|
90
|
+
* **kilocode:** fall through blank part text chunks ([417a4c8](https://github.com/viamin/agent-harness/commit/417a4c8005f23c28f5a68a834890614ca49c5dca))
|
|
91
|
+
* **kilocode:** fall through blank text aliases ([63ed6a1](https://github.com/viamin/agent-harness/commit/63ed6a1bb002329ab823a8844e0ac81ef065938b))
|
|
92
|
+
* **kilocode:** guard malformed structured output payloads ([30a59a2](https://github.com/viamin/agent-harness/commit/30a59a21d3b4012cec4dbfa27b5471b9dee329bd))
|
|
93
|
+
* **kilocode:** guard scalar structured error payloads ([26843a9](https://github.com/viamin/agent-harness/commit/26843a90019d3f29f15eb363d098f0bba0f00582))
|
|
94
|
+
* **kilocode:** guard step_finish part.tokens against non-Hash values ([ff2d841](https://github.com/viamin/agent-harness/commit/ff2d8414eb928bd679d9754a71d0849963298a4b))
|
|
95
|
+
* **kilocode:** honor explicit usage totals ([d1e5275](https://github.com/viamin/agent-harness/commit/d1e5275f3515ae40db4b523e0efe2ce8a3d169a1))
|
|
96
|
+
* **kilocode:** honor later explicit total alias updates ([496df42](https://github.com/viamin/agent-harness/commit/496df425a8a5e76e97134dea8b1f74a0067aad1b))
|
|
97
|
+
* **kilocode:** honor synthesized result usage totals ([b0779a7](https://github.com/viamin/agent-harness/commit/b0779a78400c1a3b6aa38610582c5cf96446b28e))
|
|
98
|
+
* **kilocode:** honor valid total fallback aliases ([d0ae122](https://github.com/viamin/agent-harness/commit/d0ae12245be3e606d0919bf78dcc858525c64036))
|
|
99
|
+
* **kilocode:** ignore blank terminal result strings ([ebb34b6](https://github.com/viamin/agent-harness/commit/ebb34b663c7c0a933aedc17efba444263a891b80))
|
|
100
|
+
* **kilocode:** ignore negative token counts ([aee8d1e](https://github.com/viamin/agent-harness/commit/aee8d1e35e438ae25bf3a37571d273d891b89b24))
|
|
101
|
+
* **kilocode:** ignore non-string text payloads ([4a35a6f](https://github.com/viamin/agent-harness/commit/4a35a6fb1555923b3d1555bc895051bbaa7db26c))
|
|
102
|
+
* **kilocode:** ignore scalar json fallback noise ([1f62463](https://github.com/viamin/agent-harness/commit/1f624631477557ae533d605ecf37a5fb40c39544))
|
|
103
|
+
* **kilocode:** ignore usage on non-usage events ([b23ac10](https://github.com/viamin/agent-harness/commit/b23ac10c0d4107e05a50a2dc83204c0088bddbbb))
|
|
104
|
+
* **kilocode:** ignore whitespace-only text alias placeholders ([62bb641](https://github.com/viamin/agent-harness/commit/62bb641a7b7ac5434b2e905c6b297b2abf989020))
|
|
105
|
+
* **kilocode:** ignore whitespace-only text chunks ([699cccc](https://github.com/viamin/agent-harness/commit/699cccc1025b83a3850b7ed2670ff60de39e6adf))
|
|
106
|
+
* **kilocode:** keep last usable structured usage payload ([1b9d9bd](https://github.com/viamin/agent-harness/commit/1b9d9bd7ebbaa0ae9ed6dd7379f9b8c67de19025))
|
|
107
|
+
* **kilocode:** keep stdout diagnostics with structured errors ([76446fc](https://github.com/viamin/agent-harness/commit/76446fc8a95c89c9944b47db794ddb776e29686f))
|
|
108
|
+
* **kilocode:** merge partial structured usage events ([2de37e2](https://github.com/viamin/agent-harness/commit/2de37e246317650f60a0399c2f731f03ecf28ec9))
|
|
109
|
+
* **kilocode:** normalize malformed token counts ([ef4c3dc](https://github.com/viamin/agent-harness/commit/ef4c3dce783e71aae1b60c543da59f64b4efdb6c))
|
|
110
|
+
* **kilocode:** parse hash-shaped structured error aliases ([431f8c4](https://github.com/viamin/agent-harness/commit/431f8c438d7b13bf45d3416e947d3ad34b8b4eca))
|
|
111
|
+
* **kilocode:** parse NDJSON event stream instead of single JSON object ([4e1252f](https://github.com/viamin/agent-harness/commit/4e1252fc384c51d118b43647a7026281927b6794))
|
|
112
|
+
* **kilocode:** parse nested part error messages ([8d49794](https://github.com/viamin/agent-harness/commit/8d49794f79bbe553cfe8f569867508bb80a86805))
|
|
113
|
+
* **kilocode:** parse nested structured error messages ([b9fee6a](https://github.com/viamin/agent-harness/commit/b9fee6a8814045f0c5329bac23b0f0b424b17566))
|
|
114
|
+
* **kilocode:** parse token usage from step_finish.part.tokens ([bbf5a58](https://github.com/viamin/agent-harness/commit/bbf5a58fc6bc631a483a3e71bc0d8b99744e9f7b))
|
|
115
|
+
* **kilocode:** pass legitimate_exit_codes in Response metadata ([dac77c2](https://github.com/viamin/agent-harness/commit/dac77c24711bf717b7d98d847a21d3922889026b))
|
|
116
|
+
* **kilocode:** preserve base error stream ordering ([5281d78](https://github.com/viamin/agent-harness/commit/5281d7875c54cd010364b251069d921c4c5e4838))
|
|
117
|
+
* **kilocode:** preserve extra usage totals without io counts ([b4fb265](https://github.com/viamin/agent-harness/commit/b4fb2657aa56ad42e514a42dd0176742a48fdbc4))
|
|
118
|
+
* **kilocode:** preserve json array fallback output ([16cb566](https://github.com/viamin/agent-harness/commit/16cb5668e1442a9232ca5fcc38d0b196eb673956))
|
|
119
|
+
* **kilocode:** preserve mixed structured failure diagnostics ([5ec3be7](https://github.com/viamin/agent-harness/commit/5ec3be7ff143803c8e9602a1d76b6c7c4c0beb12))
|
|
120
|
+
* **kilocode:** preserve mixed structured success output ([ea64e2e](https://github.com/viamin/agent-harness/commit/ea64e2ea7154c15193ce35c115874bb898dcdc84))
|
|
121
|
+
* **kilocode:** preserve provider token totals ([b62d749](https://github.com/viamin/agent-harness/commit/b62d7499070aa5233c56b7207b6a2aede97a7dfc))
|
|
122
|
+
* **kilocode:** preserve raw mixed stdout spacing ([881c51e](https://github.com/viamin/agent-harness/commit/881c51e9a88705e0d8e28e06cb7fd7381f55feed))
|
|
123
|
+
* **kilocode:** preserve raw output for non-event json ([ce9be0f](https://github.com/viamin/agent-harness/commit/ce9be0f5f8d807dda995202fbe2dc26cddee32b2))
|
|
124
|
+
* **kilocode:** preserve step extras with partial usage ([14eeddb](https://github.com/viamin/agent-harness/commit/14eeddb6ec9e056c8b20ee9251e42a26de260079))
|
|
125
|
+
* **kilocode:** preserve step token totals for partial usage ([5efe2f4](https://github.com/viamin/agent-harness/commit/5efe2f4d3804f52cfae1b6ee63da87d3459a8c0f))
|
|
126
|
+
* **kilocode:** preserve terminal result payload spacing ([977faa7](https://github.com/viamin/agent-harness/commit/977faa7eed454c6070503589cc995f3021d19476))
|
|
127
|
+
* **kilocode:** preserve terminal result text ([282ae35](https://github.com/viamin/agent-harness/commit/282ae35051b715f0210534f31adb87152c75b6b7))
|
|
128
|
+
* **kilocode:** preserve terminal result text across result events ([5652453](https://github.com/viamin/agent-harness/commit/56524531434d9d7c71f7af3acd17d25dbf9656ca))
|
|
129
|
+
* **kilocode:** preserve unreconstructable step totals ([a906ee9](https://github.com/viamin/agent-harness/commit/a906ee951d6dfa5aa74e149f2e41fa214ffad2cf))
|
|
130
|
+
* **kilocode:** preserve unreconstructable totals with result extras ([7585e29](https://github.com/viamin/agent-harness/commit/7585e29d0eb6668f48b6db3ab033272b74d4831f))
|
|
131
|
+
* **kilocode:** preserve whitespace in text alias chunks ([a6592c1](https://github.com/viamin/agent-harness/commit/a6592c12e753a91cdd8f6c0041100c2a0c01d89a))
|
|
132
|
+
* **kilocode:** read terminal result text from hash payloads ([2ac7012](https://github.com/viamin/agent-harness/commit/2ac701278133e2f0fee64a7cb33f854ecff0754b))
|
|
133
|
+
* **kilocode:** recompute totals from updated usage ([aafabfd](https://github.com/viamin/agent-harness/commit/aafabfd972c56e35bcaa36eb92b1658cb6579a6c))
|
|
134
|
+
* **kilocode:** reject fractional token counts ([935a41d](https://github.com/viamin/agent-harness/commit/935a41d327f7d9433368db40da93ab1983e14cbc))
|
|
135
|
+
* **kilocode:** reject non-decimal string token counts ([0be6ddf](https://github.com/viamin/agent-harness/commit/0be6ddf31a306face9b61498d1a77d08a94a02a5))
|
|
136
|
+
* **kilocode:** remove stray binstubs and add missing test coverage ([25287f7](https://github.com/viamin/agent-harness/commit/25287f736c8bd81820553fc5ca2517e1c64f261c))
|
|
137
|
+
* **kilocode:** replace stale partial extra usage fields ([4b89027](https://github.com/viamin/agent-harness/commit/4b8902768620ffb5787e194753b14f6eb7c496d7))
|
|
138
|
+
* **kilocode:** skip scalar JSON lines before reading event fields ([b8fce27](https://github.com/viamin/agent-harness/commit/b8fce27bceb998a6a4b2a3de7ed95f96a2794c3d))
|
|
139
|
+
* **kilocode:** support hash-shaped part text aliases ([778e25c](https://github.com/viamin/agent-harness/commit/778e25c62cf32bafd1c0fb4bae32b2ea13cf1df9))
|
|
140
|
+
* **kilocode:** support nested result message aliases ([e8a5988](https://github.com/viamin/agent-harness/commit/e8a5988e34dfb1afd70eccb551985fa7e273192e))
|
|
141
|
+
* **kilocode:** support result text aliases ([63ccff9](https://github.com/viamin/agent-harness/commit/63ccff9be0c32ffcfd04e3745a441d6d2370eeac))
|
|
142
|
+
* **kilocode:** support scalar part structured errors ([2dd4740](https://github.com/viamin/agent-harness/commit/2dd47408f39340b075815ce64ac02c588fc445de))
|
|
143
|
+
* **kilocode:** support scalar text part payloads ([b9e9013](https://github.com/viamin/agent-harness/commit/b9e90137e6b43fb2eb789857da319221203a7f76))
|
|
144
|
+
* **kilocode:** support text event alias payloads ([4c425ff](https://github.com/viamin/agent-harness/commit/4c425fff93dcd872e2640743cb21da9668f952b7))
|
|
145
|
+
* **kilocode:** support top-level result text aliases ([9ce7c02](https://github.com/viamin/agent-harness/commit/9ce7c0249acaf3b44731271fc8b50d8fbb14cedc))
|
|
146
|
+
* **kilocode:** support top-level structured error text ([cad1d31](https://github.com/viamin/agent-harness/commit/cad1d31007d79b75daecb61b4f1ed8573f9e70a0))
|
|
147
|
+
* **kilocode:** suppress raw ndjson output for structured events ([d7a07d7](https://github.com/viamin/agent-harness/commit/d7a07d70812b40a75f6a09924d1fd609e99a61df))
|
|
148
|
+
* **kilocode:** treat missing usage tokens as unknown ([5e6335f](https://github.com/viamin/agent-harness/commit/5e6335f901e8f197de9c8f4df5727f90fc06ee43))
|
|
149
|
+
* **kilocode:** whitelist structured event types ([d6d8a7d](https://github.com/viamin/agent-harness/commit/d6d8a7d5bf6268050bf2083494360debeec80fab))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
### Improvements
|
|
153
|
+
|
|
154
|
+
* **kilocode:** remove unreachable structured error branch ([3d7ec3d](https://github.com/viamin/agent-harness/commit/3d7ec3d94e7be6b6f2c2c92d0daf0ce0590c4067))
|
|
155
|
+
|
|
156
|
+
## [0.6.0](https://github.com/viamin/agent-harness/compare/agent-harness/v0.5.9...agent-harness/v0.6.0) (2026-04-12)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
### Features
|
|
160
|
+
|
|
161
|
+
* **aider:** extract token usage via --llm-history-file ([0fff343](https://github.com/viamin/agent-harness/commit/0fff343f943d93899d0222b16ffa9832611289ff)), closes [#100](https://github.com/viamin/agent-harness/issues/100)
|
|
162
|
+
|
|
3
163
|
## [0.5.9](https://github.com/viamin/agent-harness/compare/agent-harness/v0.5.8...agent-harness/v0.5.9) (2026-04-12)
|
|
4
164
|
|
|
5
165
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "shellwords"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
3
7
|
module AgentHarness
|
|
4
8
|
module Providers
|
|
5
9
|
# Aider AI coding assistant provider
|
|
@@ -192,29 +196,382 @@ module AgentHarness
|
|
|
192
196
|
["--restore-chat-history", session_id]
|
|
193
197
|
end
|
|
194
198
|
|
|
199
|
+
def send_message(prompt:, **options)
|
|
200
|
+
log_debug("send_message_start", prompt_length: prompt.length, options: options.keys)
|
|
201
|
+
|
|
202
|
+
options = normalize_provider_runtime(options)
|
|
203
|
+
runtime = options[:provider_runtime]
|
|
204
|
+
|
|
205
|
+
options = normalize_mcp_servers(options)
|
|
206
|
+
validate_mcp_servers!(options[:mcp_servers]) if options[:mcp_servers]&.any?
|
|
207
|
+
|
|
208
|
+
llm_history_path = generate_llm_history_path
|
|
209
|
+
command = build_command(prompt, options.merge(llm_history_path: llm_history_path))
|
|
210
|
+
preparation = build_execution_preparation(options)
|
|
211
|
+
timeout = options[:timeout] || @config.timeout || default_timeout
|
|
212
|
+
|
|
213
|
+
start_time = Time.now
|
|
214
|
+
result = execute_with_timeout(
|
|
215
|
+
command,
|
|
216
|
+
timeout: timeout,
|
|
217
|
+
env: build_env(options),
|
|
218
|
+
preparation: preparation,
|
|
219
|
+
**command_execution_options(options)
|
|
220
|
+
)
|
|
221
|
+
duration = Time.now - start_time
|
|
222
|
+
|
|
223
|
+
response = parse_response(result, duration: duration, llm_history_path: llm_history_path)
|
|
224
|
+
if runtime&.model
|
|
225
|
+
response = Response.new(
|
|
226
|
+
output: response.output,
|
|
227
|
+
exit_code: response.exit_code,
|
|
228
|
+
duration: response.duration,
|
|
229
|
+
provider: response.provider,
|
|
230
|
+
model: runtime.model,
|
|
231
|
+
tokens: response.tokens,
|
|
232
|
+
metadata: response.metadata,
|
|
233
|
+
error: response.error
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
track_tokens(response) if response.tokens
|
|
238
|
+
|
|
239
|
+
log_debug("send_message_complete", duration: duration, tokens: response.tokens)
|
|
240
|
+
|
|
241
|
+
response
|
|
242
|
+
rescue McpConfigurationError, McpUnsupportedError, McpTransportUnsupportedError
|
|
243
|
+
raise
|
|
244
|
+
rescue => e
|
|
245
|
+
handle_error(e, prompt: prompt, options: options)
|
|
246
|
+
ensure
|
|
247
|
+
cleanup_llm_history_file!(llm_history_path)
|
|
248
|
+
end
|
|
249
|
+
|
|
195
250
|
protected
|
|
196
251
|
|
|
197
252
|
def build_command(prompt, options)
|
|
198
253
|
cmd = [self.class.binary_name]
|
|
254
|
+
runtime = options[:provider_runtime]
|
|
199
255
|
|
|
200
|
-
# Run in non-interactive mode
|
|
201
256
|
cmd << "--yes"
|
|
202
257
|
|
|
203
|
-
if
|
|
204
|
-
cmd += ["--
|
|
258
|
+
if options[:llm_history_path]
|
|
259
|
+
cmd += ["--llm-history-file", options[:llm_history_path]]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
model = runtime&.model || @config.model
|
|
263
|
+
if model && !model.empty?
|
|
264
|
+
cmd += ["--model", model]
|
|
205
265
|
end
|
|
206
266
|
|
|
207
267
|
if options[:session]
|
|
208
268
|
cmd += session_flags(options[:session])
|
|
209
269
|
end
|
|
210
270
|
|
|
271
|
+
if runtime&.flags&.any?
|
|
272
|
+
validate_runtime_flags!(runtime.flags)
|
|
273
|
+
cmd += runtime.flags
|
|
274
|
+
end
|
|
275
|
+
|
|
211
276
|
cmd += ["--message", prompt]
|
|
212
277
|
|
|
213
278
|
cmd
|
|
214
279
|
end
|
|
215
280
|
|
|
281
|
+
def parse_response(result, duration:, llm_history_path: nil)
|
|
282
|
+
response = super(result, duration: duration)
|
|
283
|
+
tokens = parse_token_usage(result, llm_history_path: llm_history_path)
|
|
284
|
+
|
|
285
|
+
return response unless tokens
|
|
286
|
+
|
|
287
|
+
Response.new(
|
|
288
|
+
output: response.output,
|
|
289
|
+
exit_code: response.exit_code,
|
|
290
|
+
duration: response.duration,
|
|
291
|
+
provider: response.provider,
|
|
292
|
+
model: response.model,
|
|
293
|
+
tokens: tokens,
|
|
294
|
+
metadata: response.metadata,
|
|
295
|
+
error: response.error
|
|
296
|
+
)
|
|
297
|
+
end
|
|
298
|
+
|
|
216
299
|
def default_timeout
|
|
217
|
-
600
|
|
300
|
+
600
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
private
|
|
304
|
+
|
|
305
|
+
TOKEN_COUNT_PATTERN = /\d[\d,]*(?:\.\d+)?[kmb]?/i
|
|
306
|
+
|
|
307
|
+
TOKEN_USAGE_PATTERN =
|
|
308
|
+
/^\s*Tokens:\s*(?<input>#{TOKEN_COUNT_PATTERN})\s+sent(?:,\s*#{TOKEN_COUNT_PATTERN}\s+cache\s+\w+)*,\s*(?<output>#{TOKEN_COUNT_PATTERN})\s+received\.?(?:\s+Cost:\s+.+)?\s*$/i
|
|
309
|
+
FOOTER_COST_PATTERN = /^\s*Cost:\s+.+\s*$/i
|
|
310
|
+
RUN_SHELL_COMMAND_PATTERN = /^\s*Run shell command\?.*$/i
|
|
311
|
+
OUTPUT_STATUS_PATTERN =
|
|
312
|
+
/^\s*(?:Applied edit to|Commit\b|Committing\b|You can use \/undo\b|Added .+ to the chat\.|Removed .+ from the chat\.|Use \/help\b|Create new file\?|Allow edits to\b|Edit the files\?|Run shell command\?).*$/i
|
|
313
|
+
OUTPUT_PATH_PATTERN = /\A(?:\.\.?\/|\/|~\/)[\w.\-\/]+\z/
|
|
314
|
+
OUTPUT_DOTFILE_PATTERN = /\A\.[\w.-]+\z/
|
|
315
|
+
OUTPUT_FILENAME_PATTERN = /\A[\w.-]+\.[A-Za-z][\w.-]*\z/
|
|
316
|
+
COMMON_SHELL_COMMAND_PATTERN =
|
|
317
|
+
/\A(?:git|bundle|ruby|python\d*(?:\.\d+)?|uv|npm|yarn|pnpm|node|bash|sh|zsh|make|rake|rspec|rails|go|pytest|bin\/[\w.-]+|sed|rg|grep|find|ls|cat|cp|mv|rm|mkdir|touch|chmod|chown|docker|kubectl)\z/
|
|
318
|
+
EXECUTOR_LLM_HISTORY_TIMEOUT = 10
|
|
319
|
+
|
|
320
|
+
def generate_llm_history_path
|
|
321
|
+
return "/tmp/aider_llm_history_#{Process.pid}_#{SecureRandom.hex(8)}" if sandboxed_environment?
|
|
322
|
+
|
|
323
|
+
File.join(Dir.tmpdir, "aider_llm_history_#{Process.pid}_#{SecureRandom.hex(8)}")
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def parse_token_usage(result, llm_history_path:)
|
|
327
|
+
# Aider 0.86.x writes --llm-history-file as conversation text, not JSONL.
|
|
328
|
+
# Prefer the request-local history file when it includes a token report,
|
|
329
|
+
# but fall back to captured command output because the usage summary is
|
|
330
|
+
# printed there during normal runs.
|
|
331
|
+
parse_token_usage_text(safe_read_llm_history(llm_history_path), source: :history) ||
|
|
332
|
+
parse_token_usage_text(result.stdout, source: :output) ||
|
|
333
|
+
parse_token_usage_text(result.stderr, source: :output)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def read_llm_history(path)
|
|
337
|
+
return read_executor_llm_history(path) if sandboxed_environment?
|
|
338
|
+
return nil unless path && File.exist?(path) && !File.zero?(path)
|
|
339
|
+
|
|
340
|
+
content = File.read(path)
|
|
341
|
+
return nil if content.strip.empty?
|
|
342
|
+
|
|
343
|
+
content
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def safe_read_llm_history(path)
|
|
347
|
+
read_llm_history(path)
|
|
348
|
+
rescue => e
|
|
349
|
+
log_debug("llm_history_parse_error", error: e.message)
|
|
350
|
+
nil
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def parse_token_usage_text(content, source: :output)
|
|
354
|
+
return nil if content.nil? || content.strip.empty?
|
|
355
|
+
|
|
356
|
+
match = if source == :history
|
|
357
|
+
extract_history_token_usage_match(content)
|
|
358
|
+
else
|
|
359
|
+
extract_output_token_usage_match(content)
|
|
360
|
+
end
|
|
361
|
+
return nil unless match
|
|
362
|
+
|
|
363
|
+
input = parse_token_count(match[:input])
|
|
364
|
+
output = parse_token_count(match[:output])
|
|
365
|
+
|
|
366
|
+
{input: input, output: output, total: input + output}
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def extract_history_token_usage_match(content)
|
|
370
|
+
lines = content.lines
|
|
371
|
+
|
|
372
|
+
lines.each_index.reverse_each do |index|
|
|
373
|
+
match = TOKEN_USAGE_PATTERN.match(lines[index])
|
|
374
|
+
next unless match
|
|
375
|
+
next unless history_token_usage_footer_line?(lines, index)
|
|
376
|
+
|
|
377
|
+
return match
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
nil
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def extract_output_token_usage_match(content)
|
|
384
|
+
lines = content.lines
|
|
385
|
+
|
|
386
|
+
lines.each_index.reverse_each do |index|
|
|
387
|
+
match = TOKEN_USAGE_PATTERN.match(lines[index])
|
|
388
|
+
next unless match
|
|
389
|
+
next unless output_token_usage_footer_line?(lines, index)
|
|
390
|
+
|
|
391
|
+
return match
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
nil
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def history_token_usage_footer_line?(lines, index)
|
|
398
|
+
footer_prefix?(lines, index) && footer_suffix?(lines, index)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def output_token_usage_footer_line?(lines, index)
|
|
402
|
+
footer_prefix?(lines, index) && output_footer_suffix?(lines, index)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def footer_prefix?(lines, index)
|
|
406
|
+
block_start = index
|
|
407
|
+
while block_start.positive? && TOKEN_USAGE_PATTERN.match?(lines[block_start - 1])
|
|
408
|
+
block_start -= 1
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
return false if block_start.zero?
|
|
412
|
+
|
|
413
|
+
lines[block_start - 1].strip.empty?
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def footer_suffix?(lines, index)
|
|
417
|
+
lines[(index + 1)..].to_a.all? do |line|
|
|
418
|
+
stripped = line.strip
|
|
419
|
+
stripped.empty? || TOKEN_USAGE_PATTERN.match?(line) || FOOTER_COST_PATTERN.match?(line)
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def output_footer_suffix?(lines, index)
|
|
424
|
+
suffix_lines = lines[(index + 1)..].to_a
|
|
425
|
+
shell_prompt_index = suffix_lines.index { |line| RUN_SHELL_COMMAND_PATTERN.match?(line) }
|
|
426
|
+
|
|
427
|
+
suffix_lines.each_with_index.all? do |line, line_index|
|
|
428
|
+
stripped = line.strip
|
|
429
|
+
stripped.empty? ||
|
|
430
|
+
TOKEN_USAGE_PATTERN.match?(line) ||
|
|
431
|
+
FOOTER_COST_PATTERN.match?(line) ||
|
|
432
|
+
OUTPUT_STATUS_PATTERN.match?(line) ||
|
|
433
|
+
output_path_footer_line?(stripped) ||
|
|
434
|
+
output_command_footer_line?(line, line_index, shell_prompt_index)
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def output_path_footer_line?(line)
|
|
439
|
+
OUTPUT_PATH_PATTERN.match?(line) ||
|
|
440
|
+
OUTPUT_DOTFILE_PATTERN.match?(line) ||
|
|
441
|
+
OUTPUT_FILENAME_PATTERN.match?(line) ||
|
|
442
|
+
(line.include?("/") && line.match?(/\A[\w.\-\/]+\z/))
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def output_command_footer_line?(line, line_index, shell_prompt_index)
|
|
446
|
+
return false unless shell_prompt_index && line_index < shell_prompt_index
|
|
447
|
+
|
|
448
|
+
stripped = line.strip
|
|
449
|
+
return false if stripped.end_with?(".", "?", "!")
|
|
450
|
+
return false if stripped.empty?
|
|
451
|
+
|
|
452
|
+
tokens = shell_command_footer_tokens(stripped)
|
|
453
|
+
return false if tokens.empty?
|
|
454
|
+
command = tokens.first
|
|
455
|
+
return false unless command_invocation_token?(command)
|
|
456
|
+
return single_token_command_footer?(command) if tokens.length == 1
|
|
457
|
+
return false unless command_line_token?(command, tokens[1..])
|
|
458
|
+
|
|
459
|
+
tokens[1..].all? { |token| command_argument_token?(token) }
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def shell_command_footer_tokens(line)
|
|
463
|
+
Shellwords.shellsplit(line.sub(/\A[$>#]\s*/, ""))
|
|
464
|
+
rescue ArgumentError
|
|
465
|
+
[]
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def command_token?(token)
|
|
469
|
+
token.match?(/\A[a-z0-9_][\w.\/~:-]*\z/) && token.match?(/[a-z]/)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def command_invocation_token?(token)
|
|
473
|
+
command_token?(token) || executable_path_token?(token)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def executable_path_token?(token)
|
|
477
|
+
token.match?(%r{\A(?:\.\.?/|/|~/)[\w.+%:@=-][\w./+%:@~=-]*\z})
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def command_line_token?(token, arguments)
|
|
481
|
+
command_invocation_token?(token) &&
|
|
482
|
+
(COMMON_SHELL_COMMAND_PATTERN.match?(token) ||
|
|
483
|
+
executable_path_token?(token) ||
|
|
484
|
+
command_footer_shell_like_arguments?(arguments))
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def single_token_command_footer?(token)
|
|
488
|
+
COMMON_SHELL_COMMAND_PATTERN.match?(token) || executable_path_token?(token)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def command_footer_shell_like_arguments?(arguments)
|
|
492
|
+
arguments.any? do |argument|
|
|
493
|
+
argument.match?(%r{\A(?:&&|\|\|?|\||[<>]|>>|&>|2>)\z}) ||
|
|
494
|
+
argument.start_with?("-", "./", "../", "/", "~/") ||
|
|
495
|
+
argument.include?("/")
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
def command_argument_token?(token)
|
|
500
|
+
!token.empty? && !token.match?(/[[:cntrl:]]/)
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def parse_token_count(value)
|
|
504
|
+
normalized = value.delete(",").downcase
|
|
505
|
+
multiplier = case normalized[-1]
|
|
506
|
+
when "k" then 1_000
|
|
507
|
+
when "m" then 1_000_000
|
|
508
|
+
when "b" then 1_000_000_000
|
|
509
|
+
else 1
|
|
510
|
+
end
|
|
511
|
+
normalized = normalized[0...-1] if multiplier > 1
|
|
512
|
+
|
|
513
|
+
(normalized.to_f * multiplier).round
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def cleanup_llm_history_file!(path)
|
|
517
|
+
return unless path
|
|
518
|
+
|
|
519
|
+
return cleanup_executor_llm_history_file!(path) if sandboxed_environment?
|
|
520
|
+
|
|
521
|
+
File.delete(path) if File.exist?(path)
|
|
522
|
+
rescue => e
|
|
523
|
+
log_debug("llm_history_cleanup_error", error: e.message)
|
|
524
|
+
nil
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def validate_runtime_flags!(flags)
|
|
528
|
+
invalid_flags = reserved_runtime_flags(flags)
|
|
529
|
+
return if invalid_flags.empty?
|
|
530
|
+
|
|
531
|
+
raise ArgumentError,
|
|
532
|
+
"Aider provider_runtime.flags cannot override provider-managed flags: " \
|
|
533
|
+
"#{invalid_flags.join(", ")}"
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def reserved_runtime_flags(flags)
|
|
537
|
+
flags.each_with_index.filter_map do |flag, index|
|
|
538
|
+
next unless reserved_runtime_flag?(flag)
|
|
539
|
+
|
|
540
|
+
if flag == "--llm-history-file" && flags[index + 1]
|
|
541
|
+
"#{flag} #{flags[index + 1]}"
|
|
542
|
+
else
|
|
543
|
+
flag
|
|
544
|
+
end
|
|
545
|
+
end.uniq
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def reserved_runtime_flag?(flag)
|
|
549
|
+
flag == "--llm-history-file" || flag.start_with?("--llm-history-file=")
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def read_executor_llm_history(path)
|
|
553
|
+
return nil unless path
|
|
554
|
+
|
|
555
|
+
result = @executor.execute(
|
|
556
|
+
["sh", "-lc", "if [ -s #{Shellwords.escape(path)} ]; then cat #{Shellwords.escape(path)}; fi"],
|
|
557
|
+
timeout: EXECUTOR_LLM_HISTORY_TIMEOUT
|
|
558
|
+
)
|
|
559
|
+
return nil unless result.success?
|
|
560
|
+
|
|
561
|
+
content = result.stdout
|
|
562
|
+
return nil if content.to_s.strip.empty?
|
|
563
|
+
|
|
564
|
+
content
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def cleanup_executor_llm_history_file!(path)
|
|
568
|
+
@executor.execute(
|
|
569
|
+
["sh", "-lc", "rm -f -- #{Shellwords.escape(path)}"],
|
|
570
|
+
timeout: EXECUTOR_LLM_HISTORY_TIMEOUT
|
|
571
|
+
)
|
|
572
|
+
rescue => e
|
|
573
|
+
log_debug("llm_history_cleanup_error", error: e.message)
|
|
574
|
+
nil
|
|
218
575
|
end
|
|
219
576
|
end
|
|
220
577
|
end
|
|
@@ -10,6 +10,29 @@ module AgentHarness
|
|
|
10
10
|
class Codex < Base
|
|
11
11
|
SUPPORTED_CLI_VERSION = "0.116.0"
|
|
12
12
|
SUPPORTED_CLI_REQUIREMENT = Gem::Requirement.new(">= #{SUPPORTED_CLI_VERSION}", "< 0.117.0").freeze
|
|
13
|
+
OAUTH_REFRESH_FAILURE_PATTERNS = [
|
|
14
|
+
/refresh_token_reused/i,
|
|
15
|
+
/failed to refresh token\b.*\b401\b/im,
|
|
16
|
+
/failed to refresh token\b.*unauthorized/im,
|
|
17
|
+
/failed to refresh token\b.*\binvalid_client\b/im,
|
|
18
|
+
/failed to refresh token\b.*\binvalid_grant\b/im,
|
|
19
|
+
/failed to refresh token\b.*invalid.*refresh.*token/im,
|
|
20
|
+
/failed to refresh token\b.*refresh.*token.*invalid/im,
|
|
21
|
+
/your access token could not be refreshed because\b.*\b401\b/im,
|
|
22
|
+
/your access token could not be refreshed because\b.*unauthorized/im,
|
|
23
|
+
/your access token could not be refreshed because\b.*\binvalid_client\b/im,
|
|
24
|
+
/your access token could not be refreshed because\b.*\binvalid_grant\b/im,
|
|
25
|
+
/your access token could not be refreshed because\b.*invalid.*refresh.*token/im,
|
|
26
|
+
/your access token could not be refreshed because\b.*refresh.*token.*invalid/im,
|
|
27
|
+
/your access token could not be refreshed because\s+your refresh token .*already (?:been )?used/im,
|
|
28
|
+
/refresh token .*already (?:been )?used/im
|
|
29
|
+
].freeze
|
|
30
|
+
OAUTH_REFRESH_TRANSIENT_PATTERNS = [
|
|
31
|
+
/your access token could not be refreshed because\s+(?:the\s+)?auth(?:entication)? service(?:\s+(?:is|was))?\s+(?:temporarily\s+)?unavailable/im,
|
|
32
|
+
/your access token could not be refreshed because .*connection.*error/im,
|
|
33
|
+
/failed to refresh token\b.*connection.*error/im,
|
|
34
|
+
/failed to refresh token\b.*service(?:\s+(?:is|was))?\s+(?:temporarily\s+)?unavailable/im
|
|
35
|
+
].freeze
|
|
13
36
|
|
|
14
37
|
class << self
|
|
15
38
|
def provider_name
|
|
@@ -171,15 +194,26 @@ module AgentHarness
|
|
|
171
194
|
end
|
|
172
195
|
|
|
173
196
|
def error_patterns
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
197
|
+
{
|
|
198
|
+
rate_limited: COMMON_ERROR_PATTERNS[:rate_limited],
|
|
199
|
+
timeout: [
|
|
200
|
+
/your access token could not be refreshed.*(?:timeout|timed.?out)/im,
|
|
201
|
+
/failed to refresh token\b.*(?:timeout|timed.?out)/im
|
|
202
|
+
],
|
|
203
|
+
transient: COMMON_ERROR_PATTERNS[:transient] + [
|
|
204
|
+
/connection.*reset/i
|
|
205
|
+
] + OAUTH_REFRESH_TRANSIENT_PATTERNS,
|
|
206
|
+
auth_expired: COMMON_ERROR_PATTERNS[:auth_expired] + [
|
|
207
|
+
/\b401\b/,
|
|
208
|
+
/incorrect.*api.*key/i
|
|
209
|
+
] + OAUTH_REFRESH_FAILURE_PATTERNS,
|
|
210
|
+
quota_exceeded: COMMON_ERROR_PATTERNS[:quota_exceeded],
|
|
177
211
|
sandbox_failure: [
|
|
178
212
|
/bwrap.*no permissions/i,
|
|
179
213
|
/no permissions to create a new namespace/i,
|
|
180
214
|
/unprivileged.*namespace/i
|
|
181
215
|
]
|
|
182
|
-
|
|
216
|
+
}
|
|
183
217
|
end
|
|
184
218
|
|
|
185
219
|
def auth_status
|