agentx-dev 3.3.0__tar.gz → 3.3.1__tar.gz
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.
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/CHANGELOG.md +644 -566
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/PKG-INFO +33 -1
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/README.md +1095 -1063
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Runner/AgentRun.py +2821 -2683
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Runner/AsyncAgentRun.py +1053 -1008
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Supervisor.py +1937 -1922
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev.egg-info/PKG-INFO +33 -1
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/pyproject.toml +70 -70
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/AGENTX.md +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/CONTRIBUTING.md +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/LICENSE +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/MANIFEST.in +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Agents/Agent.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Agents/__init__.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/AsyncTools.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/AutoSetup.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Cache.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/ChatModel.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Compiler.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Config.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/DefaultTools.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Embeddings.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Evals.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Handoffs.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Loader.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/MCP.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Memory.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Observability.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Planner.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Runner/__init__.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Runner/promptTemplate.yaml +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Session.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Splitters.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Streaming.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/Tools.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/VectorStores/__init__.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/VectorStores/chroma_store.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/VectorStores/pg_store.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/VectorStores/qdrant_store.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/WebTools.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/__init__.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/resources/__init__.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev/resources/promptTemplate.yaml +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev.egg-info/SOURCES.txt +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev.egg-info/dependency_links.txt +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev.egg-info/requires.txt +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/agentx_dev.egg-info/top_level.txt +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/README.md +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/agentic_rag_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/async_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/async_quickstart.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/auto_features_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/caching_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/chatbot_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/complete_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/concurrent_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/concurrent_tool_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/file_agent_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/function_calling_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/mcp_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/mcp_github_triage_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/observability_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/orchestration_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/planner_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/robust_link_scraper.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/supervisor_codebase_analysis_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/supervisor_example.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/sync_quickstart.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/v3_1_1_features_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/v3_1_comprehensive_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/examples/v3_1_features_demo.py +0 -0
- {agentx_dev-3.3.0 → agentx_dev-3.3.1}/setup.cfg +0 -0
|
@@ -1,566 +1,644 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `agentx-dev` are documented here. Format loosely
|
|
4
|
-
follows [Keep a Changelog](https://keepachangelog.com/); versioning is
|
|
5
|
-
[Semver](https://semver.org/).
|
|
6
|
-
|
|
7
|
-
## [3.3.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
###
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
the
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
- **
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
`
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
`
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
`
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
The
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
`
|
|
245
|
-
`
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
`
|
|
301
|
-
|
|
302
|
-
`
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
**
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
-
|
|
461
|
-
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
-
|
|
475
|
-
|
|
476
|
-
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
and
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
- `
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
- `
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
-
|
|
551
|
-
|
|
552
|
-
-
|
|
553
|
-
-
|
|
554
|
-
|
|
555
|
-
-
|
|
556
|
-
|
|
557
|
-
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `agentx-dev` are documented here. Format loosely
|
|
4
|
+
follows [Keep a Changelog](https://keepachangelog.com/); versioning is
|
|
5
|
+
[Semver](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [3.3.1] - 2026-09-09
|
|
8
|
+
|
|
9
|
+
Correctness fixes in the agent loop, found by driving it with a
|
|
10
|
+
scripted model rather than reading it. No API changes; every fix
|
|
11
|
+
replaces a silent wrong answer or a runaway cost with correct
|
|
12
|
+
behaviour. Nine regression tests ship with them
|
|
13
|
+
(`tests/test_agent_loop_flaws.py`).
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Native binding had no loop-level spiral guard.** The repeat breaker
|
|
18
|
+
sat after the native branch's `continue`, so `bind_tools_natively`
|
|
19
|
+
runs never reached it: a model stuck re-issuing one call burned the
|
|
20
|
+
full `max_iterations` (20 LLM turns where text mode stopped at 3) and
|
|
21
|
+
returned the "hit max_iterations" recap quoting the dup-guard's own
|
|
22
|
+
warning text instead of the data.
|
|
23
|
+
|
|
24
|
+
- **`respond` batched with real tool calls dropped them.** A model that
|
|
25
|
+
emitted "do X, and here is my answer" in one turn had X silently
|
|
26
|
+
discarded -- never dispatched, never in `completion.tool_calls`, no
|
|
27
|
+
error. The batch now runs and the answer defers one turn.
|
|
28
|
+
|
|
29
|
+
- **`completion.history` could not be replayed as `chat_history`.** The
|
|
30
|
+
copy filter kept only truthy `{role, content}`, which dropped every
|
|
31
|
+
tool-calling assistant turn (`content=""`), stripped `tool_call_id`
|
|
32
|
+
off `role="tool"` messages, and replayed a stored system prompt on top
|
|
33
|
+
of the fresh one -- so a follow-up call sent two system messages, zero
|
|
34
|
+
assistant turns, and an orphaned tool message providers reject. Fixed
|
|
35
|
+
in both runners.
|
|
36
|
+
|
|
37
|
+
- **A sync `AgentRunner` silently ignored async tools.** `known_tools`
|
|
38
|
+
unioned only the two sync registry tables while the registry accepted,
|
|
39
|
+
listed and prompt-advertised async ones, so calling one fell through
|
|
40
|
+
to implicit-final and returned `action_input` as the answer with an
|
|
41
|
+
empty `tool_calls` list. Now unions all four, matching the async
|
|
42
|
+
runner; the sync dispatcher already returns a clear `ToolError`, so
|
|
43
|
+
the silent wrong answer became an actionable one.
|
|
44
|
+
|
|
45
|
+
- **`AsyncAgentRunner` regressed the function-calling message shape.**
|
|
46
|
+
It appended its FC turn as raw JSON text with no `tool_calls` block
|
|
47
|
+
and never set `_last_function_call_id`, so observations went back as
|
|
48
|
+
`role="user"` -- text-mode shape while in FC mode, losing provider
|
|
49
|
+
correlation and the cached prefix.
|
|
50
|
+
|
|
51
|
+
- **A `BaseException` from a specialist orphaned its siblings.**
|
|
52
|
+
`CancelledError` and `KeyboardInterrupt` are not `Exception`, so
|
|
53
|
+
`_run_subtask`'s handler never saw them; `t.result()` re-raised and
|
|
54
|
+
left sibling tasks running unowned, with in-flight LLM calls still
|
|
55
|
+
billing. The scheduler now owns its tasks in a `try`/`finally`, which
|
|
56
|
+
also covers a consumer that stops iterating `astream` early.
|
|
57
|
+
|
|
58
|
+
- **The tool cache collided on tool name.** It is a process-wide
|
|
59
|
+
singleton keyed on `(tool_name, args)` with no record of which
|
|
60
|
+
implementation ran, so two runners whose tools merely share a name --
|
|
61
|
+
`search`, `fetch`, `query`, routine across Supervisor specialists --
|
|
62
|
+
served each other's results and the second function never ran. Keys
|
|
63
|
+
now fold in the callable's `module.qualname`, so same-name /
|
|
64
|
+
different-implementation misses while genuinely identical tools still
|
|
65
|
+
share, including across processes for the disk-backed `FileCache`.
|
|
66
|
+
|
|
67
|
+
### Docs
|
|
68
|
+
|
|
69
|
+
- Streaming documentation described parameters the API rejects.
|
|
70
|
+
`AsyncAgentRunner.astream()` and `HandoffCoordinator.stream()` take no
|
|
71
|
+
`stream_tokens` (only the sync `AgentRunner.stream()` does), and every
|
|
72
|
+
`text_delta` example was built on a default model where
|
|
73
|
+
`use_function_calling` auto-detects to `True` and yields zero deltas.
|
|
74
|
+
`simple_stream` was documented with the wrong signature entirely.
|
|
75
|
+
Added a table naming exactly which stream methods accept what.
|
|
76
|
+
|
|
77
|
+
### Tests
|
|
78
|
+
|
|
79
|
+
- Repaired 20 stale text-mode tests. `MockModel` defined
|
|
80
|
+
`call_with_tools` unconditionally, so 3.1.7's auto-detect routed every
|
|
81
|
+
text script down the function-calling path and the runner returned
|
|
82
|
+
`""`. The mock now advertises the capability only when scripted for
|
|
83
|
+
it. Suite: 206 passed, 3 skipped.
|
|
84
|
+
|
|
85
|
+
## [3.3.0] - 2026-08-19
|
|
86
|
+
|
|
87
|
+
Dependency DAGs for the Supervisor. Plans declare which steps consume
|
|
88
|
+
which, and the executor derives ordering, parallelism, AND context
|
|
89
|
+
routing from those edges — unifying the old split where sequential
|
|
90
|
+
mode had threading but no parallelism and concurrent mode had
|
|
91
|
+
parallelism but no threading. Design: docs/design/3.3-depends-on-dag.md.
|
|
92
|
+
|
|
93
|
+
### Added
|
|
94
|
+
|
|
95
|
+
- **`depends_on` plan steps.** Every plan step now carries an `id`;
|
|
96
|
+
a step that consumes an earlier step's output lists that id in
|
|
97
|
+
`depends_on`. Sync `Supervisor` executes in stable topological
|
|
98
|
+
order; `AsyncSupervisor` runs a completion-driven scheduler where a
|
|
99
|
+
step starts the MOMENT its dependencies finish (not on wave
|
|
100
|
+
barriers) and independent steps run concurrently. In DAG mode each
|
|
101
|
+
step is threaded ONLY its direct dependencies' results — explicit
|
|
102
|
+
routing instead of "everything prior", which also stops the
|
|
103
|
+
per-entry context budget shrinking as plans grow.
|
|
104
|
+
|
|
105
|
+
- **Plan sanitization that never fails a run.** Missing/duplicate ids
|
|
106
|
+
auto-assigned, unknown dependencies dropped (a planner typo degrades
|
|
107
|
+
to a root step, not a dead run), self-deps dropped, cycles broken
|
|
108
|
+
deterministically (back-edge in plan order), spawn steps cannot be
|
|
109
|
+
depended on. If repairs were needed, the plan is re-requested once
|
|
110
|
+
(`max_plan_retries`, default 1) with the repair warnings appended;
|
|
111
|
+
the sanitized original is kept when the retry is no better.
|
|
112
|
+
|
|
113
|
+
- **Failure cascade + `skipped` flag.** A step whose dependency failed
|
|
114
|
+
(after retries / success-check) is skipped, transitively, with
|
|
115
|
+
`SubtaskResult.skipped=True` and an error naming the failed dep.
|
|
116
|
+
Independent branches keep running; synthesis runs over what
|
|
117
|
+
succeeded. Skipped steps never dispatch — no tokens burned
|
|
118
|
+
downstream of garbage.
|
|
119
|
+
|
|
120
|
+
- **`skip_when` conditional execution.** A step may declare
|
|
121
|
+
`{"step": <direct dep id>, "field": <typed output field>, "is": <value>}`;
|
|
122
|
+
evaluated in Python (no LLM call) against the dependency's 3.2
|
|
123
|
+
structured output, dotted paths supported, strictly FAIL-OPEN (any
|
|
124
|
+
doubt → the step runs). Condition-skips do NOT cascade — dependents
|
|
125
|
+
treat them as empty successes ("retrieval unnecessary" is not
|
|
126
|
+
"answering impossible").
|
|
127
|
+
|
|
128
|
+
- **`Specialist` registry entries.** `agents={}` now also accepts
|
|
129
|
+
`Specialist(description, runner, depends_on=[...names...],
|
|
130
|
+
output_schema=..., when_to_use=...)`. The extras render into the
|
|
131
|
+
planning catalog (`typically after:` / `returns: Schema(fields)` /
|
|
132
|
+
`use when:`) so the planner can write real dependency graphs and
|
|
133
|
+
`skip_when` conditions against actual field names. `depends_on`
|
|
134
|
+
here is a planner HINT, never an execution constraint (the same
|
|
135
|
+
specialist can appear twice in one plan; step-ids disambiguate).
|
|
136
|
+
Classic `(description, runner)` tuples keep working — they are
|
|
137
|
+
wrapped internally, and `Specialist` tuple-unpacks for older code.
|
|
138
|
+
|
|
139
|
+
- **`AsyncSupervisor(max_parallel=N)`.** Caps concurrent sub-tasks for
|
|
140
|
+
rate-limited deployments. `sequential=True` is now sugar for
|
|
141
|
+
`max_parallel=1` with all-prior threading.
|
|
142
|
+
|
|
143
|
+
- **`SubtaskResult.step_id` / `.depends_on` / `.skipped`** and a
|
|
144
|
+
`step_id` field on `dispatch` / `subtask_result` stream events.
|
|
145
|
+
|
|
146
|
+
### Backward compatibility
|
|
147
|
+
|
|
148
|
+
A plan where NO step declares `depends_on` runs with byte-identical
|
|
149
|
+
legacy semantics: sync + async-sequential thread all prior results in
|
|
150
|
+
plan order; async-concurrent runs everything at once with no
|
|
151
|
+
threading. Verified by regression tests against the 3.2 behaviour.
|
|
152
|
+
|
|
153
|
+
## [3.2.0] - 2026-08-13
|
|
154
|
+
|
|
155
|
+
Typed multi-agent pipelines. Specialists can now declare a Pydantic
|
|
156
|
+
output schema once and pass validated instances to each other through
|
|
157
|
+
the Supervisor, instead of downstream agents re-parsing prose.
|
|
158
|
+
|
|
159
|
+
### Added
|
|
160
|
+
|
|
161
|
+
- **`output_schema` on the `AgentRunner` / `AsyncAgentRunner`
|
|
162
|
+
constructor.** Declare the runner's output shape once
|
|
163
|
+
(`AgentRunner(..., output_schema=QueryIntent)`) instead of passing it
|
|
164
|
+
on every call or describing JSON in the prompt. A per-call
|
|
165
|
+
`output_schema=` still wins when both are set. `None` keeps the exact
|
|
166
|
+
pre-3.2 behaviour: no coercion, `completion.output` stays `None`.
|
|
167
|
+
|
|
168
|
+
- **Schema coercion via forced native function calling.** When a schema
|
|
169
|
+
is in play, the final answer is converted by forcing a provider-native
|
|
170
|
+
tool call against the schema (constrained decoding), not by regexing
|
|
171
|
+
JSON out of prose. The ReAct loop itself is untouched: tool selection
|
|
172
|
+
and intermediate reasoning run exactly as before, and the coercion
|
|
173
|
+
happens once, after the loop finishes. Models without a
|
|
174
|
+
`call_with_tools` implementation fall back to the previous text-JSON
|
|
175
|
+
parsing, so custom `BaseChatModel` subclasses keep working.
|
|
176
|
+
`completion.content` keeps the human-readable answer alongside
|
|
177
|
+
`completion.output` in every case.
|
|
178
|
+
|
|
179
|
+
- **`SubtaskResult.output`.** The Supervisor now preserves each
|
|
180
|
+
specialist's validated Pydantic instance next to its `content` text.
|
|
181
|
+
Consumers that only read `content` are unaffected.
|
|
182
|
+
|
|
183
|
+
- **Structured specialist-to-specialist handoff.** When an earlier step
|
|
184
|
+
produced typed output, `_build_augmented_query` serializes it into the
|
|
185
|
+
next specialist's context as a labelled JSON block
|
|
186
|
+
(`STRUCTURED OUTPUT (QueryIntent): {...}`) followed by the summary
|
|
187
|
+
text, so downstream steps parse fields rather than interpreting
|
|
188
|
+
sentences like `INTENT: ... SEARCH_QUERY: ...`.
|
|
189
|
+
|
|
190
|
+
- **`vector_search_tool` pipeline options.** New kwargs:
|
|
191
|
+
`max_text_chars` (default 500; pass `0` for full untruncated passages,
|
|
192
|
+
which a reranker judging evidence actually needs) and
|
|
193
|
+
`structured_output` (default False; when True the tool returns a JSON
|
|
194
|
+
array of `{id, text, vector_score, metadata}` instead of the
|
|
195
|
+
human-formatted list). Defaults preserve existing behaviour byte-for-
|
|
196
|
+
byte.
|
|
197
|
+
|
|
198
|
+
### Fixed
|
|
199
|
+
|
|
200
|
+
- **Supervisor planning prompt contradicted the execution engine.** The
|
|
201
|
+
planner rule said sub-agents "do NOT see previous steps' output" and
|
|
202
|
+
discouraged dependency chains, but the dispatcher has threaded prior
|
|
203
|
+
findings into every step since `_build_augmented_query` shipped.
|
|
204
|
+
The rule now tells the planner that sequential steps receive earlier
|
|
205
|
+
results (structured when available) and that chains like
|
|
206
|
+
intent -> retrieval -> reranking are a good plan shape, while still
|
|
207
|
+
requiring same-specialist steps to merge and banning report-only steps.
|
|
208
|
+
|
|
209
|
+
## [3.1.7] — 2026-07-27
|
|
210
|
+
|
|
211
|
+
### Changed
|
|
212
|
+
|
|
213
|
+
- **`use_function_calling` default flipped to auto-detect** on
|
|
214
|
+
`AgentRunner` / `AsyncAgentRunner`. The parameter's default type is
|
|
215
|
+
now `Optional[bool] = None`; `None` resolves to `True` when the
|
|
216
|
+
model class overrides `BaseChatModel.call_with_tools` (both `GPT`
|
|
217
|
+
and `Claude` do) and to `False` when it doesn't (or when
|
|
218
|
+
`bind_tools_natively=True`). Callers passing `True`/`False`
|
|
219
|
+
explicitly are unaffected. Rationale: text-mode ReAct requires the
|
|
220
|
+
model to emit strict JSON with any long `action_input` string
|
|
221
|
+
properly escaped — a 1200-word markdown draft with unescaped
|
|
222
|
+
newlines or quotes reliably breaks `json.loads` and killed the run.
|
|
223
|
+
Function-calling mode routes the parser through the SDK's typed
|
|
224
|
+
channel so escaping is handled automatically. The historical
|
|
225
|
+
default (`False`) was the fragile option; the new default matches
|
|
226
|
+
what most users actually want.
|
|
227
|
+
|
|
228
|
+
### Fixed
|
|
229
|
+
|
|
230
|
+
- **Malformed parser JSON no longer crashes the run.** When the
|
|
231
|
+
text-mode assistant response failed `json.loads` (typically because
|
|
232
|
+
a long `action_input` string had unescaped `"`, `\n`, or backticks),
|
|
233
|
+
the framework used to raise `JSONDecodeError` and unwind the whole
|
|
234
|
+
invocation. The runner now (1) tries a regex-based salvage that
|
|
235
|
+
extracts `{Thought, action, action_input}` from the raw text
|
|
236
|
+
covering the common "outer envelope valid, inner string broke
|
|
237
|
+
escaping" failure, and (2) if salvage fails, feeds a targeted fix
|
|
238
|
+
hint back to the model (`"your last response was not valid JSON;
|
|
239
|
+
emit …, escape newlines as \n"`) and continues the loop bounded
|
|
240
|
+
by `max_iterations`. Exhaustion returns a clear framework message
|
|
241
|
+
rather than an uncaught exception. Applied to both sync and async
|
|
242
|
+
runners via a shared `_salvage_react_json` helper.
|
|
243
|
+
The salvager's action-name regex is intentionally strict
|
|
244
|
+
(`[A-Za-z_][A-Za-z0-9_.\- ]{0,79}`) so it can't hallucinate an
|
|
245
|
+
"action" out of an unrelated `"key":"value"` pair inside malformed
|
|
246
|
+
JSON.
|
|
247
|
+
|
|
248
|
+
- **Verbose trace in `bind_tools_natively` mode now prints tool
|
|
249
|
+
name + args + response.** Previously native runs showed blank
|
|
250
|
+
`[tool.call.start]` / `[tool.call.complete]` pairs (the
|
|
251
|
+
observability layer fires them without the trace context), so you
|
|
252
|
+
couldn't tell which tool the model actually invoked or what came
|
|
253
|
+
back. The runner now prints `[tool] Invoking '<name>' with args:
|
|
254
|
+
<input>` and `[tool] Response: <preview>` (or `[tool] Error: ...`
|
|
255
|
+
when the dispatch raised) in the post-dispatch loop, matching the
|
|
256
|
+
format text-mode and function-calling mode use. Mirrored to the
|
|
257
|
+
async runner.
|
|
258
|
+
|
|
259
|
+
- **`web_fetch_tool(vector_store=...)` auto-ingests fetched pages into
|
|
260
|
+
a vector store** instead of dumping raw HTML into the model's
|
|
261
|
+
context. Fixes the TPM-limit trap: when a research agent fetches
|
|
262
|
+
four articles in parallel (via ``multi_tool_use.parallel`` or
|
|
263
|
+
native binding), the combined bodies can easily exceed 40k tokens
|
|
264
|
+
and blow past a 30k TPM ceiling on the very next model call.
|
|
265
|
+
New parameters on ``web_fetch_tool``:
|
|
266
|
+
|
|
267
|
+
| Kwarg | Default | Effect |
|
|
268
|
+
|---|---|---|
|
|
269
|
+
| ``vector_store`` | ``None`` | When set, each fetch is HTML-stripped, chunked with ``TextSplitter``, embedded via the store's embeddings, and added with ``{src: url, chunk_index, total_chunks}`` metadata. The tool response becomes a compact summary (URL, byte count, chunk count, 240-char preview) — NOT the raw body. The model then calls ``vector_search`` / ``Rag`` to pull only the passages it needs. |
|
|
270
|
+
| ``chunk_size`` | ``1500`` | Characters per chunk when ``vector_store`` is set. Ignored otherwise. |
|
|
271
|
+
| ``chunk_overlap`` | ``200`` | Overlap between adjacent chunks so a fact spanning a boundary is still retrievable. Ignored otherwise. |
|
|
272
|
+
|
|
273
|
+
Backwards-compatible: the positional ``cache_dir`` signature keeps
|
|
274
|
+
working; `web_fetch_tool()` with no ``vector_store`` returns raw
|
|
275
|
+
body as before. Ingest and cache_dir compose — enable both and get
|
|
276
|
+
disk-cached full bodies AND searchable chunks. HTML stripping is
|
|
277
|
+
minimal and dependency-free (regex-based: script/style blocks
|
|
278
|
+
dropped whole, then tags stripped, whitespace collapsed) so the
|
|
279
|
+
ingest path adds no new install dependency. On JSON/plain-text
|
|
280
|
+
responses the stripper is a near no-op.
|
|
281
|
+
|
|
282
|
+
The observation returned to the model shows topical coverage --
|
|
283
|
+
first, middle, and last chunk previews (up to 3 samples,
|
|
284
|
+
deduplicated for short pages) -- so the model can tell what
|
|
285
|
+
topics the page actually covers, not just the intro paragraph.
|
|
286
|
+
Without this the model would only see the page's opening and
|
|
287
|
+
wouldn't know to query for topics discussed later in the same
|
|
288
|
+
page. Explicit instruction in the observation ("query with
|
|
289
|
+
SPECIFIC keywords from the topics above; do NOT re-fetch; do
|
|
290
|
+
NOT ask for the full body") steers the model toward the RAG path
|
|
291
|
+
on follow-up turns.
|
|
292
|
+
|
|
293
|
+
- **`multi_tool_use.parallel` now reaches its dispatch path.**
|
|
294
|
+
When GPT wanted to batch several tool calls into one turn (fetch N
|
|
295
|
+
URLs concurrently, run M searches at once), it emitted OpenAI's
|
|
296
|
+
synthetic `multi_tool_use.parallel` meta-tool. The registry's
|
|
297
|
+
`_dispatch_multi_parallel` / `_adispatch_multi_parallel` handlers
|
|
298
|
+
already knew how to unpack it, but the runner loop's known-tools
|
|
299
|
+
guardrail rejected the name FIRST as unregistered — dumping the
|
|
300
|
+
raw `{"tool_uses": [...]}` payload into the user-facing "final
|
|
301
|
+
answer" and never invoking any of the nested calls. Added
|
|
302
|
+
`multi_tool_use.parallel` to the recognized action set in both
|
|
303
|
+
sync and async runners so the meta-tool flows through to dispatch
|
|
304
|
+
and the existing unpackers run. Nested calls with the `functions.`
|
|
305
|
+
prefix are normalized before dispatch (same as top-level FC
|
|
306
|
+
calls), so the model can emit either shape.
|
|
307
|
+
|
|
308
|
+
- **`Permissions.full_access` / `read_only` auto-wrap a bare string.**
|
|
309
|
+
Passing `full_access("./workspace")` used to iterate the string
|
|
310
|
+
into 11 single-character "subtrees" (Python's `list("./workspace")`)
|
|
311
|
+
— every path check silently rejected because no real path could
|
|
312
|
+
ever match a `"."` or `"/"` "allowed subtree". The classmethod
|
|
313
|
+
now detects a bare string and treats it as `[allowed_paths]`, so
|
|
314
|
+
`full_access("./workspace")` does the intuitive thing (equivalent
|
|
315
|
+
to `full_access(["./workspace"])` and auto-infers the workspace).
|
|
316
|
+
Same fix on `read_only`. List inputs are unchanged.
|
|
317
|
+
|
|
318
|
+
- **`Permissions.full_access` now accepts (and auto-infers)
|
|
319
|
+
`workspace`.** The classmethod set `allowed_paths` but not
|
|
320
|
+
`workspace`, so short paths like `write_file(path="report.md")`
|
|
321
|
+
resolved to CWD (outside the sandbox) and raised
|
|
322
|
+
`PermissionError: access denied` — a landmine that every caller of
|
|
323
|
+
`Permissions.full_access(["./workspace"])` hit sooner or later.
|
|
324
|
+
New signature: `full_access(allowed_paths, *, workspace=None)`.
|
|
325
|
+
When `workspace` isn't passed AND `allowed_paths` has exactly one
|
|
326
|
+
entry, that path is auto-set as the workspace (the "project-scoped
|
|
327
|
+
agent whose one allowed subtree IS its workspace" case, which is
|
|
328
|
+
99% of use). Two or more paths stay ambiguous and require an
|
|
329
|
+
explicit `workspace=` if short-path resolution is wanted. Pass an
|
|
330
|
+
explicit `workspace=` string to override the auto-choice.
|
|
331
|
+
Backwards-compatible on the positional signature; adds a keyword
|
|
332
|
+
argument that existing callers didn't use.
|
|
333
|
+
|
|
334
|
+
## [3.1.5] — 2026-07-26
|
|
335
|
+
|
|
336
|
+
### Fixed
|
|
337
|
+
|
|
338
|
+
- **Text-mode tool results no longer use `role: "function"`.** In text
|
|
339
|
+
mode (the default — no `use_function_calling`) the runner fed each tool
|
|
340
|
+
observation back to the model as a `role: "function"` message. Newer
|
|
341
|
+
OpenAI models reject that role outright (`400 … 'messages[N].role' does
|
|
342
|
+
not support 'function' with this model`, e.g. gpt-5.x), and Anthropic
|
|
343
|
+
never accepted it — text-mode multi-tool runs on Claude were latently
|
|
344
|
+
broken too; older GPT models simply still tolerated the legacy role.
|
|
345
|
+
Tool observations now go back as a plain `role: "user"` turn framed as
|
|
346
|
+
`Observation: …`, which every provider and model generation accepts and
|
|
347
|
+
which matches the ReAct template's own few-shot convention.
|
|
348
|
+
Function-calling mode is unchanged (native `role: "tool"` +
|
|
349
|
+
`tool_call_id`). The async runner was additionally emitting `function`
|
|
350
|
+
unconditionally (even in FC mode); it now uses the same shared helper.
|
|
351
|
+
|
|
352
|
+
### Added
|
|
353
|
+
|
|
354
|
+
- **`subtask_success_check` on `Supervisor` / `AsyncSupervisor`.** Opt-in
|
|
355
|
+
predicate `(SubtaskResult) -> bool | str` that decides whether a
|
|
356
|
+
*returned* (non-raised) sub-task result is actually acceptable — the
|
|
357
|
+
"ran fine but produced nothing useful" case a plain retry can't catch
|
|
358
|
+
(a scraper that saved 0 links, an extractor that found nothing). Return
|
|
359
|
+
`True` to accept, or `False`/a `str` reason to reject; a rejected
|
|
360
|
+
result is retried like a raised error, with the reason fed back into the
|
|
361
|
+
query, bounded by `max_subtask_retries`. After retries are exhausted the
|
|
362
|
+
last result is returned with its `error` set (content preserved). A
|
|
363
|
+
check that itself raises is treated as "accept" so a buggy predicate
|
|
364
|
+
can't wedge the run. Default `None` keeps the exceptions-only behavior.
|
|
365
|
+
New example `examples/robust_link_scraper.py` wires it together with a
|
|
366
|
+
scraping `system_addendum` (parse relative+absolute hrefs, fall back to
|
|
367
|
+
`sitemap.xml` on JS-rendered sites).
|
|
368
|
+
|
|
369
|
+
## [3.1.4] — 2026-07-26
|
|
370
|
+
|
|
371
|
+
### Fixed
|
|
372
|
+
|
|
373
|
+
- **A malformed-JSON tool argument no longer crashes the whole agent
|
|
374
|
+
run.** When a model emitted a Python snippet or a Windows path as a
|
|
375
|
+
tool-call argument — `re.findall(r'\d+')`, `C:\Users` — the `\d` / `\U`
|
|
376
|
+
are illegal JSON escapes, and the OpenAI adapter's eager
|
|
377
|
+
`json.loads(call.function.arguments)` raised `JSONDecodeError` and
|
|
378
|
+
`raise`d it, unwinding the entire ReAct loop before the agent's own
|
|
379
|
+
retry machinery could act. Under a Supervisor this surfaced as a bare
|
|
380
|
+
`ERROR: Invalid \escape: line 1 column 598` and the sub-task was
|
|
381
|
+
abandoned. Now:
|
|
382
|
+
- `_parse_tool_arguments` repairs the common case (backslashes that
|
|
383
|
+
don't begin a valid JSON escape are doubled), recovering `\d`,
|
|
384
|
+
`\w`, `\s`, etc. with zero extra round-trips. A backslash before a
|
|
385
|
+
valid-escape letter (`\b`, `\n`, …) remains ambiguous and is left
|
|
386
|
+
as the escape — a documented limit.
|
|
387
|
+
- When repair fails, `call_with_tools` returns a dedicated
|
|
388
|
+
`invalid_tool_args` result and the loop feeds the error back as a
|
|
389
|
+
retryable observation ("your arguments weren't valid JSON — escape
|
|
390
|
+
backslashes and resend"), bounded by `max_iterations`, in all
|
|
391
|
+
three modes across both `AgentRunner` and `AsyncAgentRunner`.
|
|
392
|
+
`Claude` was already immune (its tool inputs arrive pre-parsed).
|
|
393
|
+
|
|
394
|
+
- **A tool-call preamble is no longer returned as the final answer.**
|
|
395
|
+
Models routinely end a turn with an announcement instead of an action
|
|
396
|
+
— "I'll look up your recent scores to get a clear view of your
|
|
397
|
+
communication skills. Just a second!" — and every "no tool call
|
|
398
|
+
found" branch in both runners was coded as *this text is the answer,
|
|
399
|
+
break*. The loop terminated on iteration 1 and the caller got a
|
|
400
|
+
promise instead of a result. Three sites per runner were affected:
|
|
401
|
+
the native-binding path (`type != "tool_use"`), the
|
|
402
|
+
`use_function_calling` path (parser unresolved), and the JSON-text
|
|
403
|
+
path (response didn't parse). `max_iterations` never helped, because
|
|
404
|
+
the break happened before any iteration was spent.
|
|
405
|
+
|
|
406
|
+
The runner now feeds the model one corrective nudge — "your last turn
|
|
407
|
+
had no action, so nothing happened; do it, don't announce it" — and
|
|
408
|
+
continues the loop. Verified against both `AgentRunner` and
|
|
409
|
+
`AsyncAgentRunner` in all three modes.
|
|
410
|
+
|
|
411
|
+
### Added
|
|
412
|
+
|
|
413
|
+
- **Proactive "act, don't announce" system-prompt clause.** The reactive
|
|
414
|
+
`text_turn_nudges` fix corrects an agent *after* it narrates instead of
|
|
415
|
+
acting; this clause heads it off. When (and only when) an agent has
|
|
416
|
+
tools, its system prompt now tells it to call the tool rather than
|
|
417
|
+
reply "I'll do X / just a second" and stop — and to report what it DID
|
|
418
|
+
in past tense. Injected in all three modes across both runners; skipped
|
|
419
|
+
for tool-less chat agents, where prose is the correct answer. Sits
|
|
420
|
+
before any `system_addendum` so a caller's role instructions still win.
|
|
421
|
+
|
|
422
|
+
- **`max_subtask_retries` on `Supervisor` / `AsyncSupervisor`** (default
|
|
423
|
+
`1`). A sub-task that raised used to be recorded as an error and the
|
|
424
|
+
Supervisor moved straight to synthesis — no second attempt. Now a
|
|
425
|
+
failed sub-task is re-dispatched up to this many times, with the prior
|
|
426
|
+
error appended to the query so the specialist knows what to fix
|
|
427
|
+
("your previous attempt failed with X — diagnose and try again").
|
|
428
|
+
Bounded and informed: only raised exceptions trigger a retry (a
|
|
429
|
+
sub-task that returns content is accepted as-is, since the Supervisor
|
|
430
|
+
can't tell "terse but correct" from "wrong"), and the error text is
|
|
431
|
+
fed back rather than blindly re-running. Set to `0` for the old
|
|
432
|
+
quit-on-first-failure behavior. Applies in both sequential and
|
|
433
|
+
concurrent async modes.
|
|
434
|
+
|
|
435
|
+
- **`text_turn_nudges` on `AgentRunner` / `AsyncAgentRunner`** (default
|
|
436
|
+
`1`). Caps the re-prompts described above at one extra LLM call per
|
|
437
|
+
run; after the budget is spent the model's text stands as the answer.
|
|
438
|
+
Set to `0` for the previous behavior. Automatically skipped when no
|
|
439
|
+
tools are registered, since a runner with no tools is a plain chat
|
|
440
|
+
call and prose genuinely is the answer there.
|
|
441
|
+
|
|
442
|
+
## [3.1.3] — 2026-07-22
|
|
443
|
+
|
|
444
|
+
Docs-only patch. No code changes since 3.1.2. Users on 3.1.2 don't
|
|
445
|
+
need to upgrade for functionality; upgrade to pick up the improved
|
|
446
|
+
onboarding docs bundled in the sdist.
|
|
447
|
+
|
|
448
|
+
### Documentation
|
|
449
|
+
|
|
450
|
+
- **Tools doc rewritten to answer "how do I actually use these?"**
|
|
451
|
+
Added §0 `How each built-in tool is registered` as the entry
|
|
452
|
+
section. Two registration paths — auto vs manual — laid out in a
|
|
453
|
+
table on the first screen. Six runnable subsections covering every
|
|
454
|
+
combination:
|
|
455
|
+
- §0.1 DefaultTools via `Permissions(...)` (auto)
|
|
456
|
+
- §0.2 WebTools via `tools=[web_search_tool(), web_fetch_tool()]`
|
|
457
|
+
- §0.3 RAG via `TextSplitter` -> `VectorStore.add_documents` ->
|
|
458
|
+
`vector_search_tool(store)`
|
|
459
|
+
- §0.4 Handoffs via `handoff_tool` + `HandoffCoordinator`
|
|
460
|
+
- §0.5 Custom `StructuredTool` from scratch
|
|
461
|
+
- §0.6 Fully-loaded runner combining all of the above
|
|
462
|
+
- §0.7 Rules on name collisions, invisible-denied-capabilities,
|
|
463
|
+
async-tool behavior
|
|
464
|
+
The existing inventory + wrapper / controls / cheat-sheet sections
|
|
465
|
+
are unchanged; they now sit after the "how to use them" primer
|
|
466
|
+
instead of before it.
|
|
467
|
+
|
|
468
|
+
## [3.1.2] — 2026-07-22
|
|
469
|
+
|
|
470
|
+
Patch release. Two independent fixes.
|
|
471
|
+
|
|
472
|
+
### Fixed
|
|
473
|
+
|
|
474
|
+
- **`llm_judge` correctly parses YES/NO across providers.** The judge
|
|
475
|
+
parser was comparing the reply's first word to the literal string
|
|
476
|
+
`"YES"`. GPT-4o answers `"YES,"` (comma-suffixed), which failed the
|
|
477
|
+
equality check and marked every genuine PASS as FAIL. Claude replies
|
|
478
|
+
`"YES"` without punctuation so the bug hid during local development.
|
|
479
|
+
Fixed by matching `\b(YES|NO)\b` (word-boundary regex, case-
|
|
480
|
+
insensitive) at the start of the reply. Handles every real shape:
|
|
481
|
+
`YES`, `YES.`, `YES!`, `YES, exactly right`, `Yes.`, `yes -- reason`.
|
|
482
|
+
Ambiguous replies (`Maybe`, empty string) still fail closed.
|
|
483
|
+
- Regression test `test_llm_judge_parses_various_verdict_shapes`
|
|
484
|
+
covers 8 YES shapes, 5 NO shapes, and 4 ambiguous replies.
|
|
485
|
+
|
|
486
|
+
### Added
|
|
487
|
+
|
|
488
|
+
- **`agentx_dev.Tools` is a one-stop tools namespace.** Users no
|
|
489
|
+
longer need to remember which module each tool lives in:
|
|
490
|
+
|
|
491
|
+
```python
|
|
492
|
+
from agentx_dev.Tools import (
|
|
493
|
+
StandardTool, StructuredTool,
|
|
494
|
+
AsyncStandardTool, AsyncStructuredTool,
|
|
495
|
+
web_search_tool, web_fetch_tool,
|
|
496
|
+
vector_search_tool, handoff_tool,
|
|
497
|
+
DefaultTools, Permissions,
|
|
498
|
+
)
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Both this form and the pre-existing `from agentx_dev import X`
|
|
502
|
+
form coexist. Implementation uses PEP 562 module `__getattr__`
|
|
503
|
+
and `__dir__` so re-exports are lazy (no import cost for modules
|
|
504
|
+
the caller doesn't touch) and show up in IDE autocomplete +
|
|
505
|
+
`dir(agentx_dev.Tools)`.
|
|
506
|
+
|
|
507
|
+
## [3.1.1] — 2026-07-21
|
|
508
|
+
|
|
509
|
+
Second batch of 3.1 features + a full docs + brand pass.
|
|
510
|
+
|
|
511
|
+
### Added
|
|
512
|
+
|
|
513
|
+
**Streaming through orchestration**
|
|
514
|
+
- `Supervisor.stream()` / `AsyncSupervisor.astream()` emit
|
|
515
|
+
`plan_start` / `plan` / `dispatch` / `subtask_result` /
|
|
516
|
+
`synthesize_start` / `final` / `completion` events.
|
|
517
|
+
- `HandoffCoordinator.stream()` / `.astream()` emit `invoke` /
|
|
518
|
+
`completion` / `handoff` / `final` / `result` events per hop.
|
|
519
|
+
- Legacy `.run()` / `.arun()` refactored to consume the streams (no
|
|
520
|
+
code duplication).
|
|
521
|
+
|
|
522
|
+
**Prompt optimization — `Compiled`**
|
|
523
|
+
- New `agentx_dev.Compiler` module.
|
|
524
|
+
- `Compiled(runner_factory, trainset, ...)` iteratively refines a
|
|
525
|
+
runner's `system_addendum` against the eval harness. Half of
|
|
526
|
+
DSPy's power at a tenth of the surface.
|
|
527
|
+
|
|
528
|
+
**Anthropic Batch API**
|
|
529
|
+
- `Claude.batch(requests)` submits many prompts at Anthropic's 50%-off
|
|
530
|
+
batch rate, polls to completion, returns results in submission order.
|
|
531
|
+
- Per-request error dicts on failure; token usage funneled into
|
|
532
|
+
`TokenUsage` so cost tracking stays a single source of truth.
|
|
533
|
+
|
|
534
|
+
**Vector store adapters — `agentx_dev.VectorStores`**
|
|
535
|
+
- `ChromaVectorStore`, `QdrantVectorStore`, `PgVectorStore` — same
|
|
536
|
+
public shape as the in-memory `VectorStore` (`add` / `search` /
|
|
537
|
+
`delete` / `clear` / `__len__` / `embeddings`).
|
|
538
|
+
- `vector_search_tool()` and `SemanticMemory` accept any of them.
|
|
539
|
+
- SDK imports lazy; friendly `ImportError` when the underlying SDK
|
|
540
|
+
is missing.
|
|
541
|
+
|
|
542
|
+
**Trace viewer (`viewer/`)**
|
|
543
|
+
- Self-hosted single-page app that reads `FileHook` JSONL and renders
|
|
544
|
+
a timeline with type/text filters, summary sidebar, JSON drill-down.
|
|
545
|
+
- Works from `file://`, no server required.
|
|
546
|
+
|
|
547
|
+
**Docs site (`host/`)**
|
|
548
|
+
- Full editorial dark-first design system (JetBrains Mono headings,
|
|
549
|
+
Inter body, `#B8FF3E` electric-lime accent).
|
|
550
|
+
- Command palette (`Cmd+K`) with keyboard navigation and live search.
|
|
551
|
+
- Hero code snippet with hand-tinted syntax highlighting.
|
|
552
|
+
- Reading progress bar, breadcrumbs, header anchor links.
|
|
553
|
+
- Sidebar sliding active marker, collapsible groups.
|
|
554
|
+
- Code copy buttons, language labels.
|
|
555
|
+
- Right-rail auto-TOC with `IntersectionObserver` scrollspy.
|
|
556
|
+
- Dark/light theme toggle, persisted.
|
|
557
|
+
- Cache-busted assets so edits land on refresh without hard-reload.
|
|
558
|
+
|
|
559
|
+
**Brand identity (`brand/`)**
|
|
560
|
+
- Full brand kit: 5 SVG assets (`mark`, `mono`, `wordmark`, `logo-full`,
|
|
561
|
+
`app-icon`), `BRAND.md` strategy doc, rendered brand-kit HTML deck.
|
|
562
|
+
- Copy audit dropped "small" (weak) and "LangChain" references from
|
|
563
|
+
all marketing surfaces.
|
|
564
|
+
- Favicon wired into docs + trace viewer.
|
|
565
|
+
|
|
566
|
+
**Test suite (`tests/`)**
|
|
567
|
+
- Restored + expanded pytest suite: 127 tests passing (3 skipped for
|
|
568
|
+
absent optional SDKs).
|
|
569
|
+
- Coverage: parser + all `AgentType` variants, `ToolRegistry`
|
|
570
|
+
(dispatch / dup-guard / circuit-breaker / timeout), Permissions
|
|
571
|
+
(capability gating + sandbox + traversal), budgets (cost / rate /
|
|
572
|
+
retry / non-retryable HTTP), runner loop (streaming + output_schema
|
|
573
|
+
+ chat history), embeddings + `VectorStore` + `SemanticMemory`,
|
|
574
|
+
handoffs (bounded hops + history sanitization), evals harness
|
|
575
|
+
(all assertion helpers + JSON case loaders), vector-store adapter
|
|
576
|
+
shape conformance.
|
|
577
|
+
|
|
578
|
+
**Docs (`docs/`)**
|
|
579
|
+
- Full docs tree (34 pages), including new pages for:
|
|
580
|
+
vector store adapters, prompt optimization, batch API, trace viewer,
|
|
581
|
+
and a **use-cases** landing (13 concrete scenarios with runnable code).
|
|
582
|
+
- Rewrote **Tools** page to enumerate every built-in tool with args,
|
|
583
|
+
return shape, capability flag, and use-case guidance.
|
|
584
|
+
- Rewrote **Agents** page to cover all four orchestration
|
|
585
|
+
architectures (Solo / Supervisor / Handoffs / Compiled) with
|
|
586
|
+
decision trees, worked examples, and cheat sheet.
|
|
587
|
+
- **Agentic RAG chatbot** as use case §13 — multi-query decomposition,
|
|
588
|
+
parallel retrieval, self-critique, citations, user memory.
|
|
589
|
+
|
|
590
|
+
**Examples**
|
|
591
|
+
- `examples/agentic_rag_demo.py` — the runnable version of the
|
|
592
|
+
agentic RAG use case. Auto-seeds a KB if none exists, `--demo` flag
|
|
593
|
+
runs a 3-turn scripted session proving user-notes recall works.
|
|
594
|
+
|
|
595
|
+
**Package**
|
|
596
|
+
- `[chroma]`, `[qdrant]`, `[pgvector]`, `[dev]` extras added.
|
|
597
|
+
- `[anthropic]` bumped to `>=0.36` (Batch API + prompt cache).
|
|
598
|
+
|
|
599
|
+
### Fixed
|
|
600
|
+
|
|
601
|
+
- `AgentRunner._iter_run` in `bind_tools_natively=True` mode uses a
|
|
602
|
+
minimal system prompt instead of the AgentType template so the
|
|
603
|
+
ReAct `action/action_input` scaffold no longer fights the native
|
|
604
|
+
tool interface. Previously produced JSON-blob answers under GPT.
|
|
605
|
+
- `HandoffCoordinator._sanitize_history_for_next_agent` strips tool
|
|
606
|
+
and function role messages between hops so tool_call_ids from a
|
|
607
|
+
previous agent don't leak into the next model's call (OpenAI 400).
|
|
608
|
+
- Docs site marker positioning uses double-`requestAnimationFrame` +
|
|
609
|
+
`document.fonts.ready` so the sidebar accent bar lands on the
|
|
610
|
+
correct row even on a cold font cache.
|
|
611
|
+
- Primary hero CTA color uses `#doc .hero-cta a.primary` selector to
|
|
612
|
+
outrank `#doc a` link styling (previously rendered lime-on-lime
|
|
613
|
+
and was invisible).
|
|
614
|
+
|
|
615
|
+
### Notes
|
|
616
|
+
|
|
617
|
+
- Package version bumped from `3.0.6` to `3.1.1`. The 3.1.0 release
|
|
618
|
+
did not ship publicly — 3.1.1 is the first 3.1-tagged PyPI release
|
|
619
|
+
and includes both batches of features.
|
|
620
|
+
|
|
621
|
+
## [3.1.0] — internal only (commits 52840e7)
|
|
622
|
+
|
|
623
|
+
First batch of 3.1 features. Committed but not released to PyPI.
|
|
624
|
+
Merged into 3.1.1 for the public release.
|
|
625
|
+
|
|
626
|
+
### Added
|
|
627
|
+
- Anthropic prompt caching (`Claude(enable_prompt_cache=True)`).
|
|
628
|
+
- Parallel per-turn tool dispatch in `AgentRunner`
|
|
629
|
+
(`bind_tools_natively=True`, `parallel_tool_workers`).
|
|
630
|
+
- Semantic memory (`SemanticMemory`, embeddings-backed retrieval).
|
|
631
|
+
- RAG core (`Embeddings`, `HashEmbeddings`, `OpenAIEmbeddings`,
|
|
632
|
+
`VectorStore`, `VectorHit`, `vector_search_tool()`).
|
|
633
|
+
- Agent-to-agent handoffs (`HandoffRequest`, `handoff_tool`,
|
|
634
|
+
`HandoffCoordinator`, `HandoffResult`).
|
|
635
|
+
- Evals harness (`EvalCase`, `EvalRunner`, `EvalReport`, 7 assertion
|
|
636
|
+
helpers, JSON case loader, `python -m agentx_dev.Evals run` CLI).
|
|
637
|
+
- `TokenUsage.cache_hit_ratio` property.
|
|
638
|
+
|
|
639
|
+
## [3.0.6] — 2026-03 (baseline)
|
|
640
|
+
|
|
641
|
+
Security hardening baseline (SSRF guard on `web_fetch`, HMAC-signed
|
|
642
|
+
persistent state, scrubbed subprocess env, path sanitizer,
|
|
643
|
+
`permissions.json` mode 0o600, ReDoS guard on `grep`,
|
|
644
|
+
`invoke`/`ainvoke` accept bare strings and message lists).
|