@maudecode/talaria-web 1.16.1 → 1.18.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.
- package/_release.json +8 -8
- package/dist/api/auth-raw.d.ts +1 -1
- package/dist/api/auth-raw.js +4 -4
- package/dist/api/auth-raw.js.map +1 -1
- package/dist/api/automation-raw.d.ts +6 -0
- package/dist/api/automation-raw.js +59 -9
- package/dist/api/automation-raw.js.map +1 -1
- package/dist/api/automation-router.d.ts +163 -10
- package/dist/api/automation-router.js +25 -8
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +700 -15
- package/dist/api/chat-router.js +74 -55
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +47 -2
- package/dist/api/git-router.js +31 -22
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.d.ts +14 -0
- package/dist/api/raw-routes.js +277 -36
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +10 -0
- package/dist/api/router.js +10 -8
- package/dist/api/router.js.map +1 -1
- package/dist/api/sessions-router.d.ts +1710 -73
- package/dist/api/sessions-router.js +164 -10
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +132 -2
- package/dist/api/settings-router.js +15 -5
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.d.ts +16 -3
- package/dist/api/sse-routes.js +194 -67
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-raw.js +12 -25
- package/dist/api/tools-raw.js.map +1 -1
- package/dist/api/tools-router.d.ts +425 -19
- package/dist/api/tools-router.js +157 -26
- package/dist/api/tools-router.js.map +1 -1
- package/dist/app.d.ts +0 -9377
- package/dist/app.js +42 -17
- package/dist/app.js.map +1 -1
- package/dist/auth/gate.js +8 -2
- package/dist/auth/gate.js.map +1 -1
- package/dist/auth/oidc.d.ts +5 -2
- package/dist/auth/oidc.js +8 -7
- package/dist/auth/oidc.js.map +1 -1
- package/dist/bin/talaria-web.js +1 -1
- package/dist/bin/talaria-web.js.map +1 -1
- package/dist/cli/dotenv.js +1 -0
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/config/agent-config.d.ts +2 -0
- package/dist/config/agent-config.js +12 -2
- package/dist/config/agent-config.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/http/context.d.ts +20 -3
- package/dist/http/context.js +41 -8
- package/dist/http/context.js.map +1 -1
- package/dist/http/csp.d.ts +2 -1
- package/dist/http/csp.js +3 -2
- package/dist/http/csp.js.map +1 -1
- package/dist/onboarding.js +4 -0
- package/dist/onboarding.js.map +1 -1
- package/dist/profiles/profiles.d.ts +10 -0
- package/dist/profiles/profiles.js +40 -2
- package/dist/profiles/profiles.js.map +1 -1
- package/dist/providers/catalog.d.ts +101 -8
- package/dist/providers/catalog.js +463 -123
- package/dist/providers/catalog.js.map +1 -1
- package/dist/raster-image.d.ts +7 -0
- package/dist/raster-image.js +267 -0
- package/dist/raster-image.js.map +1 -0
- package/dist/redact.d.ts +1 -0
- package/dist/redact.js +354 -44
- package/dist/redact.js.map +1 -1
- package/dist/runtime.js +111 -27
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +5 -0
- package/dist/server.js.map +1 -1
- package/dist/sessions/anchor.d.ts +2 -0
- package/dist/sessions/anchor.js +8 -3
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/background-tasks.d.ts +2 -9
- package/dist/sessions/background-tasks.js +2 -12
- package/dist/sessions/background-tasks.js.map +1 -1
- package/dist/sessions/claude-code.d.ts +19 -0
- package/dist/sessions/claude-code.js +250 -0
- package/dist/sessions/claude-code.js.map +1 -0
- package/dist/sessions/cli-sessions.d.ts +15 -4
- package/dist/sessions/cli-sessions.js +120 -29
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +61 -9
- package/dist/sessions/completions.js +282 -34
- package/dist/sessions/completions.js.map +1 -1
- package/dist/sessions/compress.d.ts +14 -1
- package/dist/sessions/compress.js +52 -11
- package/dist/sessions/compress.js.map +1 -1
- package/dist/sessions/export.d.ts +5 -0
- package/dist/sessions/export.js +30 -0
- package/dist/sessions/export.js.map +1 -1
- package/dist/sessions/gateway-watcher.d.ts +7 -3
- package/dist/sessions/gateway-watcher.js +31 -19
- package/dist/sessions/gateway-watcher.js.map +1 -1
- package/dist/sessions/handoff.d.ts +32 -0
- package/dist/sessions/handoff.js +114 -0
- package/dist/sessions/handoff.js.map +1 -0
- package/dist/sessions/journal.d.ts +8 -0
- package/dist/sessions/journal.js +44 -10
- package/dist/sessions/journal.js.map +1 -1
- package/dist/sessions/list.d.ts +13 -3
- package/dist/sessions/list.js +46 -15
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +53 -5
- package/dist/sessions/merge.js +219 -23
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/recovery.d.ts +22 -0
- package/dist/sessions/recovery.js +506 -0
- package/dist/sessions/recovery.js.map +1 -0
- package/dist/sessions/relay.d.ts +21 -2
- package/dist/sessions/relay.js +156 -11
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +152 -8
- package/dist/sessions/service.js +640 -72
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +17 -1
- package/dist/sessions/session.js +63 -4
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/state-db-worker.d.ts +1 -0
- package/dist/sessions/state-db-worker.js +33 -0
- package/dist/sessions/state-db-worker.js.map +1 -0
- package/dist/sessions/state-db.d.ts +87 -4
- package/dist/sessions/state-db.js +417 -8
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/store.d.ts +7 -0
- package/dist/sessions/store.js +36 -22
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/streams.d.ts +10 -0
- package/dist/sessions/streams.js +14 -3
- package/dist/sessions/streams.js.map +1 -1
- package/dist/sessions/titles.d.ts +2 -0
- package/dist/sessions/titles.js +4 -0
- package/dist/sessions/titles.js.map +1 -1
- package/dist/sessions/todo.d.ts +6 -1
- package/dist/sessions/todo.js +11 -4
- package/dist/sessions/todo.js.map +1 -1
- package/dist/sessions/tool-display.d.ts +12 -1
- package/dist/sessions/tool-display.js +50 -1
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn-context.d.ts +1 -1
- package/dist/sessions/turn-context.js +11 -7
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +67 -11
- package/dist/sessions/turn.js +413 -111
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sessions/wakeup-pause.d.ts +59 -0
- package/dist/sessions/wakeup-pause.js +181 -0
- package/dist/sessions/wakeup-pause.js.map +1 -0
- package/dist/settings.d.ts +20 -2
- package/dist/settings.js +45 -9
- package/dist/settings.js.map +1 -1
- package/dist/sidecar/fake.d.ts +4 -4
- package/dist/sidecar/fake.js +5 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/spa.js +2 -1
- package/dist/spa.js.map +1 -1
- package/dist/startup.d.ts +13 -5
- package/dist/startup.js +48 -0
- package/dist/startup.js.map +1 -1
- package/dist/test/harness.js +2 -0
- package/dist/test/harness.js.map +1 -1
- package/dist/test/ts-worker-hooks.d.ts +1 -0
- package/dist/test/ts-worker-hooks.js +24 -0
- package/dist/test/ts-worker-hooks.js.map +1 -0
- package/dist/text/diff.d.ts +9 -14
- package/dist/text/diff.js +74 -108
- package/dist/text/diff.js.map +1 -1
- package/dist/tools/crons.js +45 -8
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/dashboard-plugins.d.ts +53 -0
- package/dist/tools/dashboard-plugins.js +188 -0
- package/dist/tools/dashboard-plugins.js.map +1 -0
- package/dist/tools/extensions.d.ts +13 -1
- package/dist/tools/extensions.js +132 -60
- package/dist/tools/extensions.js.map +1 -1
- package/dist/tools/health.d.ts +32 -0
- package/dist/tools/health.js +111 -19
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/insights.d.ts +8 -2
- package/dist/tools/insights.js +18 -2
- package/dist/tools/insights.js.map +1 -1
- package/dist/tools/kanban.d.ts +41 -1
- package/dist/tools/kanban.js +113 -2
- package/dist/tools/kanban.js.map +1 -1
- package/dist/tools/mcp.d.ts +15 -1
- package/dist/tools/mcp.js +99 -5
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/project-os.d.ts +11 -0
- package/dist/tools/project-os.js +267 -0
- package/dist/tools/project-os.js.map +1 -0
- package/dist/tools/skills.d.ts +5 -1
- package/dist/tools/skills.js +5 -3
- package/dist/tools/skills.js.map +1 -1
- package/dist/tools/terminal.d.ts +1 -0
- package/dist/tools/terminal.js +1 -1
- package/dist/tools/terminal.js.map +1 -1
- package/dist/tools/updates.d.ts +33 -8
- package/dist/tools/updates.js +236 -57
- package/dist/tools/updates.js.map +1 -1
- package/dist/tools/wiki.d.ts +68 -0
- package/dist/tools/wiki.js +413 -0
- package/dist/tools/wiki.js.map +1 -0
- package/dist/workspace/escape.d.ts +61 -0
- package/dist/workspace/escape.js +149 -0
- package/dist/workspace/escape.js.map +1 -0
- package/dist/workspace/extract.d.ts +22 -0
- package/dist/workspace/extract.js +308 -0
- package/dist/workspace/extract.js.map +1 -0
- package/dist/workspace/fs.d.ts +19 -3
- package/dist/workspace/fs.js +96 -23
- package/dist/workspace/fs.js.map +1 -1
- package/dist/workspace/git.d.ts +18 -0
- package/dist/workspace/git.js +38 -25
- package/dist/workspace/git.js.map +1 -1
- package/dist/workspace/media.d.ts +4 -4
- package/dist/workspace/media.js +18 -45
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/preview.d.ts +28 -0
- package/dist/workspace/preview.js +110 -0
- package/dist/workspace/preview.js.map +1 -0
- package/dist/workspace/rollback.d.ts +36 -21
- package/dist/workspace/rollback.js +179 -270
- package/dist/workspace/rollback.js.map +1 -1
- package/dist/workspace/unzip.js +4 -1
- package/dist/workspace/unzip.js.map +1 -1
- package/dist/workspace/upload.d.ts +9 -4
- package/dist/workspace/upload.js +9 -3
- package/dist/workspace/upload.js.map +1 -1
- package/dist/workspace/worktrees.d.ts +2 -1
- package/dist/workspace/worktrees.js +14 -15
- package/dist/workspace/worktrees.js.map +1 -1
- package/package.json +4 -2
- package/sidecar/agent_dependency.json +4 -4
- package/sidecar/scripts/replay_sidecar.py +25 -1
- package/sidecar/talaria_sidecar/__init__.py +1 -1
- package/sidecar/talaria_sidecar/__main__.py +7 -1
- package/sidecar/talaria_sidecar/home.py +52 -2
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +39 -5
- package/sidecar/talaria_sidecar/methods/chat.py +431 -43
- package/sidecar/talaria_sidecar/methods/checkpoints.py +89 -0
- package/sidecar/talaria_sidecar/methods/commands.py +96 -2
- package/sidecar/talaria_sidecar/methods/cron.py +39 -8
- package/sidecar/talaria_sidecar/methods/gateway.py +32 -15
- package/sidecar/talaria_sidecar/methods/kanban.py +140 -19
- package/sidecar/talaria_sidecar/methods/mcp.py +1 -1
- package/sidecar/talaria_sidecar/methods/process.py +115 -2
- package/sidecar/talaria_sidecar/methods/profiles.py +84 -8
- package/sidecar/talaria_sidecar/methods/providers.py +27 -8
- package/sidecar/talaria_sidecar/methods/runtime.py +3 -0
- package/sidecar/talaria_sidecar/methods/state_db.py +39 -5
- package/sidecar/talaria_sidecar/methods/text.py +17 -1
- package/sidecar/talaria_sidecar/methods/usage.py +257 -4
- package/sidecar/talaria_sidecar/runtime.py +17 -1
- package/static/dist/FILES.txt +127 -125
- package/static/dist/assets/AppShell-g3bkgnvt.js +1 -0
- package/static/dist/assets/ChatPage-oo7bdan6.js +25 -0
- package/static/dist/assets/Dialog-g355qxhp.js +1 -0
- package/static/dist/assets/ExtensionRoute-i2y9fvjm.js +1 -0
- package/static/dist/assets/HubRoute-lmkrbw47.js +2 -0
- package/static/dist/assets/{Markdown-jqrx5es0.js → Markdown-j4kut4j2.js} +125 -125
- package/static/dist/assets/RightPanel-ohlsdzfl.js +1 -0
- package/static/dist/assets/Select-kddq75qc.js +1 -0
- package/static/dist/assets/SessionListPanel-lu3oy0hw.js +1 -0
- package/static/dist/assets/SettingsLayout-o9bv4mgb.js +1 -0
- package/static/dist/assets/States-eq6fe29m.js +1 -0
- package/static/dist/assets/Toaster-pc247u9f.js +1 -0
- package/static/dist/assets/_app-f8n1i0m1.js +1 -0
- package/static/dist/assets/_app.ext._extensionId-gfbzpoou.js +1 -0
- package/static/dist/assets/_app.index-bzzv80hk.js +1 -0
- package/static/dist/assets/_app.insights-dzixbryi.js +1 -0
- package/static/dist/assets/_app.kanban-c6hep2us.js +1 -0
- package/static/dist/assets/_app.logs-i69qds98.js +1 -0
- package/static/dist/assets/_app.memory-kl4vf0gd.js +1 -0
- package/static/dist/assets/_app.profiles-b8zinmyp.js +1 -0
- package/static/dist/assets/{_app.session._sessionId-hh27wwu2.js → _app.session._sessionId-fn1bhde6.js} +1 -1
- package/static/dist/assets/_app.settings-cg8plwmf.js +1 -0
- package/static/dist/assets/_app.settings._section-bkwbi9zn.js +3 -0
- package/static/dist/assets/_app.skills-cs47sbvs.js +1 -0
- package/static/dist/assets/_app.tasks-dwvk64tc.js +1 -0
- package/static/dist/assets/_app.todos-c8xjm8p3.js +1 -0
- package/static/dist/assets/_app.workspaces-ehvd1gds.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-oity9y4o.js → abnfDiagram-VCTEODGH-c8kdixke.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-6crvnylg.js +1 -0
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-ceaepnyr.js → architectureDiagram-5GKGNRK7-cku06x1p.js} +1 -1
- package/static/dist/assets/{back-eu64w89w.js → back-h7aun7ew.js} +1 -1
- package/static/dist/assets/{blockDiagram-I7D4REHJ-n79st4pj.js → blockDiagram-I7D4REHJ-jzthp1wa.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-mi73akzk.js → c4Diagram-7LVT6UL2-mwr67mt8.js} +1 -1
- package/static/dist/assets/{cancel-mzvkdjg8.js → cancel-fhi0vcnu.js} +1 -1
- package/static/dist/assets/channel-b2zo73z1.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-laat0g1q.js → chunk-4HAMMTFA-gpq1y228.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-0psy1tr2.js → chunk-75Z2AOVW-ktje58c4.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-lw8uox8x.js → chunk-DU6HZSFF-ofajdixh.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-erassib3.js → chunk-F27PBJKO-cmev85ti.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-fz7rx619.js → chunk-GMAD6QVW-htjgt22y.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-gdb0epb2.js → chunk-GVQU2GXP-qndvoxl7.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-ea8t26qw.js → chunk-IMKFNOWR-e5bkd5q6.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-mb1gu9kz.js → chunk-L3NEJ4N5-mj1155r6.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-eovmccvy.js → chunk-OSK3NFVY-e7gkikcw.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-bo55zjo5.js → chunk-P2QGCYS3-f7t54xfm.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-ja7ohe4h.js → chunk-POPQ4Y6H-i927v9oa.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-e30g9nus.js → chunk-PWAF6VOD-m84lmnyt.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-gbb798f7.js → chunk-SHT3W25Y-mi0tlcu9.js} +1 -1
- package/static/dist/assets/{chunk-SVP7TREG-h2iw8urm.js → chunk-SVP7TREG-dnau702j.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-fd2cottz.js → chunk-TICWLB2K-edem1gkg.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-oe99st8x.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-oe99st8x.js +1 -0
- package/static/dist/assets/{copied-h65fc3cx.js → copied-hcplv46p.js} +1 -1
- package/static/dist/assets/copy-magtofyp.js +1 -0
- package/static/dist/assets/{cynefin-OW5HDTMX-lbltyl5j.js → cynefin-OW5HDTMX-siqlulkl.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-lxhsqq4a.js → cynefinDiagram-5FMLGOSQ-i648tah9.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-gn4nnaln.js → dagre-GXQ25YYZ-mwn6uc17.js} +1 -1
- package/static/dist/assets/{diagram-S7CK7UJ4-pa3ij6q7.js → diagram-S7CK7UJ4-l8abxaes.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-zbipyz57.js → diagram-UQ7AKVKN-mekq7zgq.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-d1kbpzgf.js → diagram-VSXAHHWV-fnky6lvc.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-i3ty14xl.js → diagram-VX7I27RA-xe6r5qfw.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-m8xefqa7.js → diagram-Z3DM3KII-nq07dg7k.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-nz7wj05l.js → ebnfDiagram-PWID7BFC-c62ipm3g.js} +1 -1
- package/static/dist/assets/endpoints-klbxod4t.js +64 -0
- package/static/dist/assets/{erDiagram-RLTQ6QDP-fa41ib83.js → erDiagram-RLTQ6QDP-h39kmnzy.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-obfede4u.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-kdpsp1je.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-n7zq01qu.js → ganttDiagram-EL5Y4UJY-ngbsyp37.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-c89emb07.js → gitGraph-4MIJSDKK-fr2i7caa.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-cz7cgr6w.js → gitGraphDiagram-WWUBYQGX-eravdti4.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-d9bapmni.js → highlighted-body-KPVGNVTW-jwpop9p8.js} +1 -1
- package/static/dist/assets/host-i828xuo7.js +1 -0
- package/static/dist/assets/index-eh8zilnv.css +2 -0
- package/static/dist/assets/index-r648f4o1.js +11 -0
- package/static/dist/assets/{info-A6RAGUB7-ndddhhhh.js → info-A6RAGUB7-h11x0dxo.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-ccxigcpf.js → infoDiagram-27XIBGKW-dfh47dw9.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-le0z9vtc.js → ishikawaDiagram-5VMMS53U-i6ypq351.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-ivsmb26o.js → journeyDiagram-3NMN7TZE-lduf1z95.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-h6n2awt0.js → kanban-definition-UXKFOSKX-e18bu4pg.js} +1 -1
- package/static/dist/assets/lazyRouteComponent-e1y4t2i9.js +1 -0
- package/static/dist/assets/{line-dmzlfe0u.js → line-meizqsvn.js} +1 -1
- package/static/dist/assets/link-oot2yn73.js +1 -0
- package/static/dist/assets/login-gu28l9ou.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-mtwrj8fn.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-gwyd5bnq.js → mermaid-parser.core-ntne45i9.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-l68gg5h7.js → mindmap-definition-YA3MSWOX-27b26eok.js} +1 -1
- package/static/dist/assets/onboarding-lkphzu1s.js +1 -0
- package/static/dist/assets/onboarding_model_label-ftpjdwqk.js +1 -0
- package/static/dist/assets/{packet-AYTQ26CC-gpv06065.js → packet-AYTQ26CC-l6jdka8p.js} +1 -1
- package/static/dist/assets/{pegDiagram-XKGWAZYB-e52vs23u.js → pegDiagram-XKGWAZYB-g1er3bxj.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-l6m7nz5s.js → pie-WAS4IAKB-i0ylcrt9.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-b4is36bq.js → pieDiagram-E7YTZNPT-fbz1e55z.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-m8vt2788.js → quadrantDiagram-AXDQQJYC-hhue7d8o.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-nxbrhiov.js → radar-RG4KPBEZ-f9b70aa4.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-222mu9hi.js → railroad-74A4TZTK-lgsuoeda.js} +1 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-nf9d6pdx.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-f7ummkt5.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-oczq5tqs.js +1 -0
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-o8lldfzv.js → railroadDiagram-O6MQD6OU-eqpy9fnz.js} +1 -1
- package/static/dist/assets/refresh-abtu93ke.js +1 -0
- package/static/dist/assets/refresh-cw-hlwb02qv.js +1 -0
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-ovyq38kv.js → requirementDiagram-BXWQKSXE-ogf6puhi.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-ou7lznor.js → sankeyDiagram-P5KCCOFB-fqb8xcjf.js} +1 -1
- package/static/dist/assets/{saved-bazmcup9.js → saved-girnabkf.js} +1 -1
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-ovckdzf2.js → sequenceDiagram-WJ2MYXX4-dlcz6qjz.js} +1 -1
- package/static/dist/assets/{settings_section_extensions_title-ertwwhlx.js → settings_section_extensions_title-h14cwk6f.js} +1 -1
- package/static/dist/assets/share._token-kl0r7ido.js +2 -0
- package/static/dist/assets/sliders-horizontal-n1zm34qq.js +1 -0
- package/static/dist/assets/sse-iwku0sjo.js +1 -0
- package/static/dist/assets/{stateDiagram-D77RDMKH-fcrexkkv.js → stateDiagram-D77RDMKH-fyoznpcs.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-ixhx8r46.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-f7usu8fw.js → swimlanes-42K2YHIH-oefldhfy.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-h9ersk2e.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-gp1hvz9f.js → timeline-definition-24CTP7MA-b1vl2zil.js} +1 -1
- package/static/dist/assets/todoStore-jjr5yr78.js +1 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-gb9gf11u.js → treeView-Q6P3EWNA-f7hcci5o.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-nuvfnvvi.js → treemap-WGGIJYW6-nl85071a.js} +1 -1
- package/static/dist/assets/units-kgxzxfhi.js +1 -0
- package/static/dist/assets/{untitled-htelhc68.js → untitled-cikfjntn.js} +1 -1
- package/static/dist/assets/{useForm-h2t0u14e.js → useForm-kqoqg0k9.js} +1 -1
- package/static/dist/assets/useLocale-o59tbiea.js +1 -0
- package/static/dist/assets/{useParams-dqd64jv7.js → useParams-b4dq6n4q.js} +1 -1
- package/static/dist/assets/{useSearch-j30pvsy7.js → useSearch-ipfmv3he.js} +1 -1
- package/static/dist/assets/{vennDiagram-4TSXK5OY-fzchbzaj.js → vennDiagram-4TSXK5OY-h5hzbk0n.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-m47mhfbh.js → wardley-WFR3VGLG-jftvpm3e.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-odg6ct1z.js → wardleyDiagram-VM6X3IG4-hedh85ge.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-mrhms5um.js → xychartDiagram-S5SC5T6Z-e49523ab.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +2 -2
- package/static/dist/assets/AppShell-ef3bhu32.js +0 -1
- package/static/dist/assets/Button-nx37f4u1.js +0 -1
- package/static/dist/assets/ChatPage-cdcpykcg.js +0 -25
- package/static/dist/assets/Dialog-mec42hvb.js +0 -1
- package/static/dist/assets/HubRoute-ecb65zpr.js +0 -2
- package/static/dist/assets/RightPanel-d1lgvk4s.js +0 -1
- package/static/dist/assets/Select-h4ivl9qf.js +0 -1
- package/static/dist/assets/SessionListPanel-ewib046b.js +0 -1
- package/static/dist/assets/SettingsLayout-lx654cup.js +0 -1
- package/static/dist/assets/States-ehsb6lzo.js +0 -1
- package/static/dist/assets/Toaster-mch1pa3t.js +0 -1
- package/static/dist/assets/_app-m8d961ef.js +0 -1
- package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +0 -1
- package/static/dist/assets/_app.index-dk6zp7e0.js +0 -1
- package/static/dist/assets/_app.insights-jt2fk4i1.js +0 -1
- package/static/dist/assets/_app.kanban-if4pubeu.js +0 -1
- package/static/dist/assets/_app.logs-mfaw9m6g.js +0 -1
- package/static/dist/assets/_app.memory-h3eaarb6.js +0 -1
- package/static/dist/assets/_app.profiles-ienktovs.js +0 -1
- package/static/dist/assets/_app.settings-dpoe0bzf.js +0 -1
- package/static/dist/assets/_app.settings._section-l468ynag.js +0 -3
- package/static/dist/assets/_app.skills-kcbncc8i.js +0 -1
- package/static/dist/assets/_app.tasks-occdrqhw.js +0 -2
- package/static/dist/assets/_app.todos-nnke2rcc.js +0 -1
- package/static/dist/assets/_app.workspaces-cdlatvwj.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +0 -1
- package/static/dist/assets/channel-mbvmf2vv.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +0 -1
- package/static/dist/assets/copy-e6977uwq.js +0 -1
- package/static/dist/assets/endpoints-k7yf25m0.js +0 -64
- package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +0 -1
- package/static/dist/assets/index-h00usru2.js +0 -11
- package/static/dist/assets/index-n73ijkv2.css +0 -2
- package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +0 -1
- package/static/dist/assets/link-budeg5t9.js +0 -1
- package/static/dist/assets/login-kjhasvp8.js +0 -1
- package/static/dist/assets/matchContext-f8oy3lo7.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +0 -1
- package/static/dist/assets/onboarding-cdfrk0pj.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +0 -1
- package/static/dist/assets/refresh-cw-kbcje1uy.js +0 -1
- package/static/dist/assets/search-jen74z4u.js +0 -1
- package/static/dist/assets/share._token-dan9hgvj.js +0 -2
- package/static/dist/assets/sliders-horizontal-f9cb3lh3.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +0 -8
- package/static/dist/assets/todoStore-eqtid4r4.js +0 -1
- package/static/dist/assets/useLocale-hcsb84pj.js +0 -1
- package/static/dist/assets/useNavigate-nwy70ztj.js +0 -1
|
@@ -12,23 +12,31 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
import contextlib
|
|
14
14
|
import contextvars
|
|
15
|
+
import copy
|
|
15
16
|
import hashlib
|
|
16
17
|
import inspect
|
|
17
18
|
import json
|
|
18
19
|
import logging
|
|
20
|
+
import os
|
|
19
21
|
import threading
|
|
20
22
|
import time
|
|
21
23
|
import uuid
|
|
22
24
|
from collections import OrderedDict
|
|
25
|
+
from pathlib import Path
|
|
23
26
|
from typing import Any
|
|
24
27
|
|
|
25
28
|
from ..errors import InvalidParams, RpcError
|
|
26
29
|
from ..home import profile_home_param, scoped_home
|
|
27
30
|
from ..rpc import CallContext
|
|
31
|
+
from .checkpoints import checkpoint_kwargs
|
|
28
32
|
|
|
29
33
|
log = logging.getLogger("talaria_sidecar.chat")
|
|
30
34
|
|
|
31
35
|
_AGENT_CACHE_MAX = 32
|
|
36
|
+
# Shutdown waits this long for evicted agents' memory providers; the server kills the sidecar 5 s after closing stdin.
|
|
37
|
+
_AGENT_RELEASE_TIMEOUT = 4.0
|
|
38
|
+
# Profile deletion waits this long for the profile's memory flushes, under the server's 120 s call timeout.
|
|
39
|
+
_PROFILE_RELEASE_TIMEOUT = 60.0
|
|
32
40
|
_TOOL_RESULT_SNIPPET_MAX = 4000
|
|
33
41
|
# Predecessor ``_TOOL_ARG_CONTENT_KEYS`` (#4928): card content / diff-reconstruction inputs keep the long cap.
|
|
34
42
|
_TOOL_ARG_CONTENT_KEYS = frozenset({"command", "cmd", "script", "code", "patch", "diff", "old_string", "new_string", "content", "path", "file_path"})
|
|
@@ -65,8 +73,16 @@ _RUNS_BY_SESSION: dict[str, str] = {}
|
|
|
65
73
|
_APPROVAL_CB_OWNER: dict[str, str] = {}
|
|
66
74
|
_APPROVAL_CB_LOCK = threading.Lock()
|
|
67
75
|
_RUNS_LOCK = threading.Lock()
|
|
68
|
-
|
|
76
|
+
# session id -> (agent, signature, profile home).
|
|
77
|
+
_AGENT_CACHE: "OrderedDict[str, tuple[Any, str, Any]]" = OrderedDict()
|
|
69
78
|
_AGENT_CACHE_LOCK = threading.Lock()
|
|
79
|
+
# Running release thread -> the profile home it runs under.
|
|
80
|
+
_RELEASES: dict[threading.Thread, Any] = {}
|
|
81
|
+
_RELEASES_LOCK = threading.Lock()
|
|
82
|
+
# Agents inside ``chat.commit_memory`` (guarded by ``_AGENT_CACHE_LOCK``): like a turn, a commit defers their release.
|
|
83
|
+
_COMMITTING: list = []
|
|
84
|
+
# Set (under ``_RUNS_LOCK``) when shutdown drains: no turn is admitted after the drain's snapshot of ``_RUNS``.
|
|
85
|
+
_DRAINING = threading.Event()
|
|
70
86
|
|
|
71
87
|
|
|
72
88
|
def _snippet(raw: Any, limit: int = _TOOL_RESULT_SNIPPET_MAX) -> str:
|
|
@@ -88,6 +104,16 @@ _RAW_RESULT_MAX_KEYS = 64
|
|
|
88
104
|
_RAW_RESULT_OUTCOME_KEYS = ("error", "exit_code", "exitCode", "success")
|
|
89
105
|
|
|
90
106
|
|
|
107
|
+
def _result_diff(raw: Any) -> str | None:
|
|
108
|
+
"""A dict (or JSON-object text) result's string ``diff`` field, whole; the server decides what it shows."""
|
|
109
|
+
try:
|
|
110
|
+
data = raw if isinstance(raw, dict) else json.loads(str(raw or ""))
|
|
111
|
+
except Exception: # noqa: BLE001
|
|
112
|
+
return None
|
|
113
|
+
diff = data.get("diff") if isinstance(data, dict) else None
|
|
114
|
+
return diff if isinstance(diff, str) else None
|
|
115
|
+
|
|
116
|
+
|
|
91
117
|
def _raw_result(raw: Any, limit: int = _TOOL_RESULT_SNIPPET_MAX) -> Any:
|
|
92
118
|
"""The tool result as the server's outcome rule reads it, bounded: a dict (or JSON-object text) keeps its first
|
|
93
119
|
``_RAW_RESULT_MAX_KEYS`` top-level fields plus its outcome fields, scalars as they are, text and non-empty nested
|
|
@@ -232,6 +258,98 @@ def _context_length(agent: Any) -> dict:
|
|
|
232
258
|
return out
|
|
233
259
|
|
|
234
260
|
|
|
261
|
+
# TAL-539: the hard cap on raw tool output kept in a compressed context (predecessor ``_hard_prune_post_compression_*``).
|
|
262
|
+
_TAIL_TOOL_BUDGET_TOKENS = 4096
|
|
263
|
+
_TAIL_TOOL_MIN_SNIPPET_TOKENS = 256
|
|
264
|
+
_ROUGH_TOKEN_CHARS = 4
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _content_text(content: Any) -> str:
|
|
268
|
+
if isinstance(content, list):
|
|
269
|
+
return " ".join(str(p.get("text") or "") for p in content if isinstance(p, dict))
|
|
270
|
+
return str(content or "")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _rough_tokens(text: str) -> int:
|
|
274
|
+
"""The Agent's estimate, which counts CJK and other non-ASCII text as denser; ceil(chars / 4) without it."""
|
|
275
|
+
try:
|
|
276
|
+
from agent.model_metadata import estimate_tokens_rough
|
|
277
|
+
|
|
278
|
+
return int(estimate_tokens_rough(text))
|
|
279
|
+
except Exception: # noqa: BLE001
|
|
280
|
+
return -(-len(text) // _ROUGH_TOKEN_CHARS)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _capped_tool_result(text: str, tokens: int, keep_tokens: int) -> str:
|
|
284
|
+
"""The kept head of an over-budget tool result plus a note on what the model no longer sees."""
|
|
285
|
+
prefix = "[Talaria context budget: omitted "
|
|
286
|
+
suffix = f" (~{tokens} rough tokens) from this tool result; the full output remains in the visible transcript.]"
|
|
287
|
+
if keep_tokens < _TAIL_TOOL_MIN_SNIPPET_TOKENS:
|
|
288
|
+
return f"{prefix}{len(text)} chars{suffix}"
|
|
289
|
+
room = keep_tokens - _rough_tokens(f"{prefix}{len(text)} of {len(text)} chars{suffix}") - 1
|
|
290
|
+
snippet = text[: len(text) * room // tokens]
|
|
291
|
+
# Density varies along the text: shrink until the kept head fits the remaining tokens.
|
|
292
|
+
while snippet and _rough_tokens(snippet) > room:
|
|
293
|
+
snippet = snippet[: len(snippet) * 3 // 4]
|
|
294
|
+
snippet = snippet.rstrip()
|
|
295
|
+
if not snippet:
|
|
296
|
+
return f"{prefix}{len(text)} chars{suffix}"
|
|
297
|
+
return f"{snippet}\n\n{prefix}{len(text) - len(snippet)} of {len(text)} chars{suffix}"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _cap_tool_results(messages: list, compressor: Any) -> list:
|
|
301
|
+
"""Newest tool results spend one shared budget; the first one over it is cut to the remainder, older ones to a note."""
|
|
302
|
+
budget = getattr(compressor, "tail_token_budget", None)
|
|
303
|
+
budget = budget if isinstance(budget, int) and budget > 0 else _TAIL_TOOL_BUDGET_TOKENS
|
|
304
|
+
threshold = getattr(compressor, "threshold_tokens", None)
|
|
305
|
+
if isinstance(threshold, int) and threshold > 0:
|
|
306
|
+
budget = min(budget, max(512, threshold // 2))
|
|
307
|
+
budget = max(512, budget)
|
|
308
|
+
spent = 0
|
|
309
|
+
out = list(messages)
|
|
310
|
+
for i in range(len(out) - 1, -1, -1):
|
|
311
|
+
msg = out[i]
|
|
312
|
+
if msg.get("role") != "tool":
|
|
313
|
+
continue
|
|
314
|
+
text = _content_text(msg.get("content"))
|
|
315
|
+
if not text.strip():
|
|
316
|
+
continue
|
|
317
|
+
tokens = _rough_tokens(text)
|
|
318
|
+
if tokens <= budget - spent:
|
|
319
|
+
spent += tokens
|
|
320
|
+
continue
|
|
321
|
+
out[i] = {**msg, "content": _capped_tool_result(text, tokens, budget - spent)}
|
|
322
|
+
spent = budget
|
|
323
|
+
return out
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _post_compression_context(agent: Any, messages: list, system_message: Any) -> tuple[list, int | None]:
|
|
327
|
+
"""TAL-539: tool output produced after a mid-turn compression never went through the compressor's pruning. Run that
|
|
328
|
+
pruning plus a hard cap on the model context, and estimate the next request from it (predecessor
|
|
329
|
+
``_prune_context_tool_results_after_compression`` / ``_estimate_post_compression_context_tokens``)."""
|
|
330
|
+
compressor = getattr(agent, "context_compressor", None)
|
|
331
|
+
context = messages
|
|
332
|
+
prune = getattr(compressor, "_prune_old_tool_results", None)
|
|
333
|
+
if callable(prune):
|
|
334
|
+
try:
|
|
335
|
+
pruned, count = prune(copy.deepcopy(messages), protect_tail_count=getattr(compressor, "protect_last_n", 20), protect_tail_tokens=getattr(compressor, "tail_token_budget", None))
|
|
336
|
+
if count and isinstance(pruned, list) and len(pruned) == len(messages):
|
|
337
|
+
context = pruned
|
|
338
|
+
except Exception: # noqa: BLE001 - the hard cap below still bounds the context
|
|
339
|
+
log.debug("post-compression tool-result pruning failed", exc_info=True)
|
|
340
|
+
context = _cap_tool_results(context, compressor)
|
|
341
|
+
estimate = None
|
|
342
|
+
try:
|
|
343
|
+
from agent.model_metadata import estimate_request_tokens_rough
|
|
344
|
+
|
|
345
|
+
# The request carries the ephemeral prompt (surface, personality, delivery) after the system message.
|
|
346
|
+
prompts = [p for p in (system_message, getattr(agent, "ephemeral_system_prompt", None)) if isinstance(p, str) and p.strip()]
|
|
347
|
+
estimate = estimate_request_tokens_rough(context, system_prompt="\n\n".join(prompts), tools=getattr(agent, "tools", None) or None)
|
|
348
|
+
except Exception: # noqa: BLE001 - display-only figure
|
|
349
|
+
log.debug("post-compression context estimate failed", exc_info=True)
|
|
350
|
+
return context, estimate if isinstance(estimate, int) and estimate > 0 else None
|
|
351
|
+
|
|
352
|
+
|
|
235
353
|
def _clarify_timeout(params: dict) -> int:
|
|
236
354
|
"""Predecessor ``_clarify_timeout_seconds``: an explicit request value wins, else the Agent's resolver over config."""
|
|
237
355
|
explicit = params.get("clarify_timeout_seconds")
|
|
@@ -379,10 +497,8 @@ def _turn_identity(session_id: str, workspace: str):
|
|
|
379
497
|
not fatal."""
|
|
380
498
|
resets: list = []
|
|
381
499
|
try:
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
except ImportError:
|
|
385
|
-
from tools.approval import reset_current_session_key, set_current_session_key # type: ignore[no-redef]
|
|
500
|
+
from tools.approval_context import reset_current_session_key, set_current_session_key
|
|
501
|
+
|
|
386
502
|
token = set_current_session_key(session_id)
|
|
387
503
|
resets.append(lambda: reset_current_session_key(token))
|
|
388
504
|
except Exception: # noqa: BLE001
|
|
@@ -421,7 +537,7 @@ def _turn_identity(session_id: str, workspace: str):
|
|
|
421
537
|
|
|
422
538
|
|
|
423
539
|
#: Older configs named the CLI composite ``hermes``; the Agent registers it split in two (predecessor
|
|
424
|
-
#: ``_LEGACY_CLI_TOOLSET_ALIASES``).
|
|
540
|
+
#: ``_LEGACY_CLI_TOOLSET_ALIASES``). Agent 0.21.6 expands it itself; delete this once 0.21.6 is the oldest supported.
|
|
425
541
|
_LEGACY_TOOLSET_ALIASES = {"hermes": ("hermes-cli", "hermes-api-server")}
|
|
426
542
|
|
|
427
543
|
|
|
@@ -477,6 +593,30 @@ def _profile_fallback_chain() -> list[dict] | None:
|
|
|
477
593
|
return get_fallback_chain(load_config() or {}) or None
|
|
478
594
|
|
|
479
595
|
|
|
596
|
+
def _main_model_request_overrides(model: str, provider) -> dict | None:
|
|
597
|
+
"""Predecessor ``_main_model_request_overrides``: the profile's ``model.service_tier`` and ``model.extra_body``
|
|
598
|
+
(written by ``/api/model/set``) apply only to turns on that configured main model. Runs under ``scoped_home``."""
|
|
599
|
+
from hermes_cli.config import load_config
|
|
600
|
+
|
|
601
|
+
model_cfg = (load_config() or {}).get("model")
|
|
602
|
+
if not isinstance(model_cfg, dict):
|
|
603
|
+
return None
|
|
604
|
+
main_model = str(model_cfg.get("default") or model_cfg.get("name") or "").strip()
|
|
605
|
+
main_provider = str(model_cfg.get("provider") or "").strip().lower()
|
|
606
|
+
if not main_model or main_model != model.strip():
|
|
607
|
+
return None
|
|
608
|
+
# An unset or ``auto`` main provider resolves at runtime, so the model id alone identifies the main model.
|
|
609
|
+
if main_provider not in ("", "auto") and main_provider != str(provider or "").strip().lower():
|
|
610
|
+
return None
|
|
611
|
+
overrides: dict = {}
|
|
612
|
+
if str(model_cfg.get("service_tier") or "").strip().lower() == "priority":
|
|
613
|
+
overrides["service_tier"] = "priority"
|
|
614
|
+
extra_body = model_cfg.get("extra_body")
|
|
615
|
+
if isinstance(extra_body, dict) and extra_body:
|
|
616
|
+
overrides["extra_body"] = extra_body
|
|
617
|
+
return overrides or None
|
|
618
|
+
|
|
619
|
+
|
|
480
620
|
def _agent_signature(model: str, provider, runtime: dict, toolsets, home: str, kwargs: dict) -> str:
|
|
481
621
|
"""Cache identity of an ``AIAgent``: everything its constructor bound from the resolved runtime, so a rotated key,
|
|
482
622
|
a different API mode, ACP command, or credential pool never reuses an agent built for the old bundle. The key
|
|
@@ -492,6 +632,8 @@ def _agent_signature(model: str, provider, runtime: dict, toolsets, home: str, k
|
|
|
492
632
|
"max_iterations": kwargs.get("max_iterations"), "max_tokens": kwargs.get("max_tokens"),
|
|
493
633
|
# Bound at construction too: a reasoning-effort change from the composer must build a fresh agent.
|
|
494
634
|
"reasoning_config": kwargs.get("reasoning_config"),
|
|
635
|
+
"request_overrides": kwargs.get("request_overrides"),
|
|
636
|
+
"checkpoints": [kwargs.get(name) for name in ("checkpoints_enabled", "checkpoint_max_snapshots", "checkpoint_max_total_size_mb", "checkpoint_max_file_size_mb")],
|
|
495
637
|
# Entries may carry their own ``api_key``.
|
|
496
638
|
"fallback_model": hashlib.sha256(json.dumps(kwargs.get("fallback_model"), sort_keys=True, default=str).encode("utf-8")).hexdigest()[:16],
|
|
497
639
|
}
|
|
@@ -504,19 +646,164 @@ def _evict_idle_agents_locked() -> None:
|
|
|
504
646
|
return
|
|
505
647
|
with _RUNS_LOCK:
|
|
506
648
|
live = {sid for sid, sid_stream in _RUNS_BY_SESSION.items() if (r := _RUNS.get(sid_stream)) is not None and not r.finished.is_set()}
|
|
649
|
+
dropped = []
|
|
507
650
|
for sid in list(_AGENT_CACHE):
|
|
508
651
|
if len(_AGENT_CACHE) <= _AGENT_CACHE_MAX:
|
|
509
652
|
break
|
|
510
653
|
if sid not in live:
|
|
511
|
-
_AGENT_CACHE.pop(sid
|
|
654
|
+
dropped.append(_AGENT_CACHE.pop(sid))
|
|
655
|
+
_release_agents_locked(dropped)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def _release_agent(agent, home) -> threading.Thread:
|
|
659
|
+
"""The gateway's soft eviction on a daemon thread under the owning profile: end the memory session with the
|
|
660
|
+
transcript (``on_session_end``, then provider shutdown), then release the LLM clients (the Codex app-server child,
|
|
661
|
+
httpx pools) while session tool state stays for a resumed session."""
|
|
662
|
+
|
|
663
|
+
def call(name: str, *args) -> bool:
|
|
664
|
+
method = getattr(agent, name, None)
|
|
665
|
+
if not callable(method):
|
|
666
|
+
return True
|
|
667
|
+
try:
|
|
668
|
+
method(*args)
|
|
669
|
+
except Exception: # noqa: BLE001 - one failed phase never skips the next
|
|
670
|
+
log.warning("evicted agent %s() failed", name, exc_info=True)
|
|
671
|
+
return False
|
|
672
|
+
return True
|
|
673
|
+
|
|
674
|
+
def release() -> None:
|
|
675
|
+
released = False
|
|
676
|
+
try:
|
|
677
|
+
with scoped_home(home):
|
|
678
|
+
messages = getattr(agent, "_session_messages", None)
|
|
679
|
+
flushed = call("shutdown_memory_provider", messages if isinstance(messages, list) else None)
|
|
680
|
+
released = True
|
|
681
|
+
thread.failed = not (call("release_clients") and flushed)
|
|
682
|
+
except Exception: # noqa: BLE001 - the profile scope could not be entered
|
|
683
|
+
log.warning("releasing an evicted agent under its profile failed", exc_info=True)
|
|
684
|
+
thread.failed = True
|
|
685
|
+
finally:
|
|
686
|
+
if not released:
|
|
687
|
+
# The memory flush needs the profile; the LLM clients and the Codex child close regardless.
|
|
688
|
+
call("release_clients")
|
|
689
|
+
_release_session_db(getattr(agent, "_talaria_session_db", None))
|
|
690
|
+
with _RELEASES_LOCK:
|
|
691
|
+
_RELEASES.pop(thread, None)
|
|
692
|
+
|
|
693
|
+
thread = threading.Thread(target=release, daemon=True, name="chat-agent-release")
|
|
694
|
+
thread.failed = False # type: ignore[attr-defined] - read by profile deletion
|
|
695
|
+
with _RELEASES_LOCK:
|
|
696
|
+
_RELEASES[thread] = home
|
|
697
|
+
thread.start()
|
|
698
|
+
return thread
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _release_agents_locked(entries) -> list[threading.Thread]:
|
|
702
|
+
"""Release cache entries that just left the cache (caller holds ``_AGENT_CACHE_LOCK``). An agent a turn still holds
|
|
703
|
+
keeps its memory provider and clients; that turn releases it when it ends."""
|
|
704
|
+
with _RUNS_LOCK:
|
|
705
|
+
held = [run.agent for run in _RUNS.values()] + _COMMITTING
|
|
706
|
+
return [_release_agent(agent, home) for agent, _signature, home in entries if not any(agent is other for other in held)]
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def _release_if_dropped_locked(agent, home) -> None:
|
|
710
|
+
"""A holder (turn or commit) is done with ``agent``: release it if it left the cache meanwhile and nothing else holds
|
|
711
|
+
it (caller holds ``_AGENT_CACHE_LOCK``)."""
|
|
712
|
+
if agent is not None and not any(entry[0] is agent for entry in _AGENT_CACHE.values()):
|
|
713
|
+
_release_agents_locked([(agent, None, home)])
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
def _join(threads, deadline: float) -> None:
|
|
717
|
+
for thread in threads:
|
|
718
|
+
thread.join(max(0.0, deadline - time.monotonic()))
|
|
512
719
|
|
|
513
720
|
|
|
514
721
|
def evict_all_agents() -> int:
|
|
515
722
|
"""Drop every cached agent (credentials or environment changed underneath them)."""
|
|
516
723
|
with _AGENT_CACHE_LOCK:
|
|
517
|
-
|
|
724
|
+
entries = list(_AGENT_CACHE.values())
|
|
518
725
|
_AGENT_CACHE.clear()
|
|
519
|
-
|
|
726
|
+
_release_agents_locked(entries)
|
|
727
|
+
return len(entries)
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
def drain_agents(timeout: float = _AGENT_RELEASE_TIMEOUT) -> None:
|
|
731
|
+
"""Shutdown: release every cached agent, stop running turns (like a Stop) so each releases the agent it holds, and
|
|
732
|
+
wait, bounded, for those turns and every release still running."""
|
|
733
|
+
deadline = time.monotonic() + timeout
|
|
734
|
+
evict_all_agents()
|
|
735
|
+
with _RUNS_LOCK:
|
|
736
|
+
_DRAINING.set()
|
|
737
|
+
runs = list(_RUNS.values())
|
|
738
|
+
for run in runs:
|
|
739
|
+
run.cancel.set()
|
|
740
|
+
while time.monotonic() < deadline:
|
|
741
|
+
with _AGENT_CACHE_LOCK, _RUNS_LOCK:
|
|
742
|
+
if not _COMMITTING and not any(_RUNS.get(run.stream_id) is run for run in runs):
|
|
743
|
+
break
|
|
744
|
+
time.sleep(0.05)
|
|
745
|
+
# A turn may have cached its agent after the first eviction.
|
|
746
|
+
evict_all_agents()
|
|
747
|
+
with _RELEASES_LOCK:
|
|
748
|
+
threads = list(_RELEASES)
|
|
749
|
+
_join(threads, deadline)
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def release_profile_agents(home) -> None:
|
|
753
|
+
"""Profile deletion: release the profile's cached agents and wait for every release running under its home. Fails
|
|
754
|
+
rather than let the home go while a memory flush still runs under it."""
|
|
755
|
+
target = os.path.realpath(home)
|
|
756
|
+
with _AGENT_CACHE_LOCK:
|
|
757
|
+
entries = [_AGENT_CACHE.pop(sid) for sid, entry in list(_AGENT_CACHE.items()) if os.path.realpath(entry[2]) == target]
|
|
758
|
+
_release_agents_locked(entries)
|
|
759
|
+
with _RELEASES_LOCK:
|
|
760
|
+
threads = [thread for thread, owner in _RELEASES.items() if os.path.realpath(owner) == target]
|
|
761
|
+
_join(threads, time.monotonic() + _PROFILE_RELEASE_TIMEOUT)
|
|
762
|
+
if any(thread.is_alive() for thread in threads):
|
|
763
|
+
raise RpcError("The profile is still saving memory from its chats. Retry in a moment.")
|
|
764
|
+
if any(thread.failed for thread in threads):
|
|
765
|
+
raise RpcError("Memory from the profile's chats could not be saved; see the sidecar log. Retry to delete the profile anyway.")
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
def _acquire_session_db(home):
|
|
769
|
+
"""TAL-529: the profile's ``state.db`` from the Agent's shared-handle registry, one reference per agent (released with
|
|
770
|
+
it), so the turn's conversation reaches ``hermes sessions``, CLI resume, and ``session_search``. None when it cannot
|
|
771
|
+
open: the turn runs without it, like the predecessor's ``_build_session_db_for_stream``."""
|
|
772
|
+
try:
|
|
773
|
+
from hermes_state_registry import acquire
|
|
774
|
+
|
|
775
|
+
return acquire(Path(home) / "state.db")
|
|
776
|
+
except Exception: # noqa: BLE001
|
|
777
|
+
log.warning("state.db unavailable under %s; this agent's turns will not reach it", home, exc_info=True)
|
|
778
|
+
return None
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
def _release_session_db(db) -> None:
|
|
782
|
+
if db is None:
|
|
783
|
+
return
|
|
784
|
+
try:
|
|
785
|
+
from hermes_state_registry import release_or_close
|
|
786
|
+
|
|
787
|
+
release_or_close(db)
|
|
788
|
+
except Exception: # noqa: BLE001
|
|
789
|
+
log.warning("releasing an agent's state.db handle failed", exc_info=True)
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
def _execution_session_id(db, session_id: str) -> str:
|
|
793
|
+
"""Predecessor ``_resolve_agent_execution_session_id``: a new agent for a session the Agent rotated by compression
|
|
794
|
+
(the old agent went with a sidecar restart or a runtime change) runs on the live tip, with the server's history,
|
|
795
|
+
rather than the closed Web id. The tip qualifies as in the Agent's own adoption: a different, still-open row. Any
|
|
796
|
+
doubt keeps the Web id."""
|
|
797
|
+
try:
|
|
798
|
+
tip = db.get_compression_tip(session_id)
|
|
799
|
+
if tip and tip != session_id:
|
|
800
|
+
row = db.get_session(tip)
|
|
801
|
+
if row and row.get("ended_at") is None:
|
|
802
|
+
log.info("agent for %s runs on its live compression tip %s", session_id, tip)
|
|
803
|
+
return str(tip)
|
|
804
|
+
except Exception: # noqa: BLE001
|
|
805
|
+
log.warning("compression tip lookup failed for %s", session_id, exc_info=True)
|
|
806
|
+
return session_id
|
|
520
807
|
|
|
521
808
|
|
|
522
809
|
def _agent_class():
|
|
@@ -527,6 +814,15 @@ def _agent_class():
|
|
|
527
814
|
return AIAgent
|
|
528
815
|
|
|
529
816
|
|
|
817
|
+
def _max_iterations_summary_request() -> str:
|
|
818
|
+
"""The user row the Agent appends to ask for a summary once its tool budget runs out; the server drops it."""
|
|
819
|
+
try:
|
|
820
|
+
from agent.context_compressor import MAX_ITERATIONS_SUMMARY_REQUEST
|
|
821
|
+
except Exception: # noqa: BLE001
|
|
822
|
+
return ""
|
|
823
|
+
return str(MAX_ITERATIONS_SUMMARY_REQUEST)
|
|
824
|
+
|
|
825
|
+
|
|
530
826
|
def _supported(cls, name: str) -> bool:
|
|
531
827
|
"""Whether ``cls.__init__`` accepts ``name`` (named, or through ``**kwargs``)."""
|
|
532
828
|
try:
|
|
@@ -564,6 +860,36 @@ def _approval_notifier(session_id: str, emit):
|
|
|
564
860
|
return notify
|
|
565
861
|
|
|
566
862
|
|
|
863
|
+
#: The Agent's compression-start lifecycle lines: "Compacting context" (and its heartbeat), pre-API, 413, and too-large
|
|
864
|
+
#: compression. Preflight and idle notices are left out: "Compacting context" follows them once compression proceeds.
|
|
865
|
+
_COMPRESSION_START_MARKERS = ("compacting context", "pre-api compression:", "context too large", "— compressing (", "- compressing (", "compression attempt")
|
|
866
|
+
_COMPRESSION_NOT_STARTED = ("skipping", "defer", "cooldown", "will not start")
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def _is_compression_start(lower: str) -> bool:
|
|
870
|
+
"""Predecessor ``_is_agent_compression_start_status``: only real start notices, never skips or post-compress chatter."""
|
|
871
|
+
if any(marker in lower for marker in _COMPRESSION_NOT_STARTED):
|
|
872
|
+
return False
|
|
873
|
+
if "compressed" in lower and "compressing" not in lower and "compression attempt" not in lower:
|
|
874
|
+
return False
|
|
875
|
+
return any(marker in lower for marker in _COMPRESSION_START_MARKERS)
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
def _status_events(kind, text: str) -> list[str]:
|
|
879
|
+
"""Events for one Agent status line: the compressing card, a fallback warning, and the turn's terminal error."""
|
|
880
|
+
lower = text.lower()
|
|
881
|
+
events = ["terminal_error"] if "non-retryable error" in lower else []
|
|
882
|
+
if str(kind or "").strip().lower() == "lifecycle":
|
|
883
|
+
if _is_compression_start(lower):
|
|
884
|
+
return events + ["compressing"]
|
|
885
|
+
# The Agent's own compression retries ("Compressed N → M messages, retrying...") are not provider fallbacks.
|
|
886
|
+
if "compress" in lower or "context reduced to" in lower:
|
|
887
|
+
return events
|
|
888
|
+
if "fallback" in lower or "rate limit" in lower or ("retry" in lower and "non-retryable" not in lower):
|
|
889
|
+
events.append("warning")
|
|
890
|
+
return events
|
|
891
|
+
|
|
892
|
+
|
|
567
893
|
def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn, one function
|
|
568
894
|
session_id = str(params.get("session_id") or "").strip()
|
|
569
895
|
stream_id = str(params.get("stream_id") or "").strip()
|
|
@@ -581,8 +907,11 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
581
907
|
raise InvalidParams("conversation_history must be a list")
|
|
582
908
|
toolsets = _turn_toolsets(params.get("enabled_toolsets"))
|
|
583
909
|
system_message = params.get("system_message")
|
|
910
|
+
home = profile_home_param(params)
|
|
584
911
|
run = _Run(stream_id, session_id, ctx)
|
|
585
912
|
with _RUNS_LOCK:
|
|
913
|
+
if _DRAINING.is_set():
|
|
914
|
+
raise RpcError("The sidecar is shutting down.", condition="sidecar_unavailable")
|
|
586
915
|
prior = _RUNS.get(_RUNS_BY_SESSION.get(session_id) or "")
|
|
587
916
|
# A previous turn for this session that never returned (a tool ignoring its interrupt) still owns the cached
|
|
588
917
|
# agent; this turn must not share, rebind, or un-interrupt that live instance.
|
|
@@ -591,15 +920,21 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
591
920
|
_RUNS_BY_SESSION[session_id] = stream_id
|
|
592
921
|
raw_emit = ctx.emit
|
|
593
922
|
steer_state = {"last": None}
|
|
923
|
+
usage_state = {"last": None}
|
|
594
924
|
|
|
595
925
|
def emit(event, data=None):
|
|
596
926
|
"""Predecessor ``_webui_steer_events_before``: before content frames, report the Agent's pending steer text
|
|
597
|
-
whenever it changed so the server can mark consumed steers live rather than only after ``done``.
|
|
927
|
+
whenever it changed so the server can mark consumed steers live rather than only after ``done``. TAL-397: the
|
|
928
|
+
Agent's session counters ride the same boundary, so the server meters cost and tokens while the turn runs."""
|
|
598
929
|
if event in ("token", "reasoning", "interim_assistant", "tool", "tool_complete") and run.agent is not None:
|
|
599
930
|
pending = _agent_pending_steer_text(run.agent)
|
|
600
931
|
if pending != steer_state["last"]:
|
|
601
932
|
steer_state["last"] = pending
|
|
602
933
|
raw_emit("steer_pending", {"text": pending})
|
|
934
|
+
usage = _usage(run.agent)
|
|
935
|
+
if usage != usage_state["last"]:
|
|
936
|
+
usage_state["last"] = usage
|
|
937
|
+
raw_emit("usage", usage)
|
|
603
938
|
raw_emit(event, data)
|
|
604
939
|
try:
|
|
605
940
|
_discover_mcp_tools(run)
|
|
@@ -653,6 +988,13 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
653
988
|
call["snippet"] = snippet
|
|
654
989
|
break
|
|
655
990
|
payload = {"event_type": "tool.completed", "name": name, "preview": snippet, "args": _args_snapshot(args), "tid": tid, "raw_result": _raw_result(function_result)}
|
|
991
|
+
# TAL-397: ``raw_result`` caps nested values, so the todo tool's list travels whole for the server's ``todo_state``.
|
|
992
|
+
if name == "todo" and function_result is not None:
|
|
993
|
+
payload["todo_result"] = function_result if isinstance(function_result, str) else json.dumps(function_result, default=str)
|
|
994
|
+
# TAL-448: likewise a result's unified ``diff``, so the server's ``edit_diff`` counts cover the whole change.
|
|
995
|
+
diff = _result_diff(function_result)
|
|
996
|
+
if diff is not None:
|
|
997
|
+
payload["result_diff"] = diff
|
|
656
998
|
cost = _delegation_cost_usd(name, function_result)
|
|
657
999
|
if cost is not None:
|
|
658
1000
|
payload["cost_usd"] = cost
|
|
@@ -671,15 +1013,13 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
671
1013
|
|
|
672
1014
|
def on_status(kind, message):
|
|
673
1015
|
text = str(message or "").strip()
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
elif "non-retryable error" in lower:
|
|
682
|
-
emit("status", {"kind": "terminal_error", "message": text})
|
|
1016
|
+
for event in _status_events(kind, text):
|
|
1017
|
+
if event == "compressing":
|
|
1018
|
+
emit("compressing", {"session_id": session_id, "message": "Compressing context"})
|
|
1019
|
+
elif event == "warning":
|
|
1020
|
+
emit("warning", {"type": "fallback", "message": text})
|
|
1021
|
+
else:
|
|
1022
|
+
emit("status", {"kind": "terminal_error", "message": text})
|
|
683
1023
|
|
|
684
1024
|
def clarify_callback(question, choices, multi_select=False, questions=None):
|
|
685
1025
|
choices_list = [str(c) for c in (choices or [])]
|
|
@@ -759,23 +1099,49 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
759
1099
|
fallback_chain = _profile_fallback_chain()
|
|
760
1100
|
if fallback_chain and _supported(AIAgent, "fallback_model"):
|
|
761
1101
|
kwargs["fallback_model"] = fallback_chain
|
|
1102
|
+
request_overrides = _main_model_request_overrides(resolved_model, resolved_provider)
|
|
1103
|
+
if request_overrides and _supported(AIAgent, "request_overrides"):
|
|
1104
|
+
kwargs["request_overrides"] = request_overrides
|
|
1105
|
+
# TAL-913: the profile's ``checkpoints`` section, as the Agent's own CLI and gateway turns read it.
|
|
1106
|
+
for name, value in checkpoint_kwargs().items():
|
|
1107
|
+
if _supported(AIAgent, name):
|
|
1108
|
+
kwargs[name] = value
|
|
762
1109
|
signature = _agent_signature(resolved_model, resolved_provider, runtime, toolsets, str(params.get("profile_home")), kwargs)
|
|
763
1110
|
agent = None
|
|
1111
|
+
|
|
1112
|
+
def bind(bound) -> None:
|
|
1113
|
+
"""Claim the agent for this run under ``_AGENT_CACHE_LOCK``, so an eviction never releases it mid-turn."""
|
|
1114
|
+
run.prior_messages = getattr(bound, "_session_messages", None)
|
|
1115
|
+
run.agent = bound
|
|
1116
|
+
|
|
764
1117
|
if not session_busy:
|
|
765
1118
|
with _AGENT_CACHE_LOCK:
|
|
766
1119
|
cached = _AGENT_CACHE.get(session_id)
|
|
767
1120
|
if cached and cached[1] == signature:
|
|
768
1121
|
agent = cached[0]
|
|
769
1122
|
_AGENT_CACHE.move_to_end(session_id)
|
|
1123
|
+
bind(agent)
|
|
770
1124
|
if agent is not None:
|
|
771
1125
|
for name in ("stream_delta_callback", "reasoning_callback", "tool_progress_callback", "clarify_callback", "interim_assistant_callback", "tool_start_callback", "tool_complete_callback", "status_callback"):
|
|
772
1126
|
if name in kwargs and hasattr(agent, name):
|
|
773
1127
|
setattr(agent, name, kwargs[name])
|
|
774
1128
|
else:
|
|
775
|
-
|
|
1129
|
+
# A ``/btw`` side question's throwaway session stays out of state.db.
|
|
1130
|
+
session_db = _acquire_session_db(home) if not params.get("ephemeral") and _supported(AIAgent, "session_db") else None
|
|
1131
|
+
if session_db is not None:
|
|
1132
|
+
kwargs["session_db"] = session_db
|
|
1133
|
+
kwargs["session_id"] = _execution_session_id(session_db, session_id)
|
|
1134
|
+
try:
|
|
1135
|
+
agent = AIAgent(**kwargs)
|
|
1136
|
+
except BaseException:
|
|
1137
|
+
_release_session_db(session_db)
|
|
1138
|
+
raise
|
|
1139
|
+
agent._talaria_session_db = session_db # released with the agent (``_release_agent``)
|
|
776
1140
|
with _AGENT_CACHE_LOCK:
|
|
777
|
-
|
|
778
|
-
_AGENT_CACHE.
|
|
1141
|
+
bind(agent)
|
|
1142
|
+
replaced = _AGENT_CACHE.pop(session_id, None)
|
|
1143
|
+
_AGENT_CACHE[session_id] = (agent, signature, home)
|
|
1144
|
+
_release_agents_locked([replaced] if replaced else [])
|
|
779
1145
|
_evict_idle_agents_locked()
|
|
780
1146
|
# Predecessor ``agent.ephemeral_system_prompt``: personality, surface context, progress guidance, and delivery
|
|
781
1147
|
# hints travel as runtime instructions that are never persisted to history.
|
|
@@ -789,8 +1155,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
789
1155
|
with _APPROVAL_CB_LOCK:
|
|
790
1156
|
register_gateway_notify(session_id, approval_cb)
|
|
791
1157
|
_APPROVAL_CB_OWNER[session_id] = stream_id
|
|
792
|
-
|
|
793
|
-
run.agent = agent
|
|
1158
|
+
usage_state["last"] = _usage(agent)
|
|
794
1159
|
compressions_before = int(getattr(getattr(agent, "context_compressor", None), "compression_count", 0) or 0)
|
|
795
1160
|
|
|
796
1161
|
# A cached agent may still carry the interrupt a previous cancel left behind; the Agent keeps a pending
|
|
@@ -830,8 +1195,12 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
830
1195
|
run_params = set()
|
|
831
1196
|
if system_message and ("system_message" in run_params or not run_params):
|
|
832
1197
|
run_kwargs["system_message"] = system_message
|
|
833
|
-
|
|
834
|
-
|
|
1198
|
+
# The server's clean user text (no workspace prefix) is what state.db stores; the Agent keeps the sent bytes beside it.
|
|
1199
|
+
persist = params.get("persist_user_message")
|
|
1200
|
+
if not (isinstance(persist, str) and persist.strip()):
|
|
1201
|
+
persist = user_message if isinstance(user_message, str) else None
|
|
1202
|
+
if "persist_user_message" in run_params and persist is not None:
|
|
1203
|
+
run_kwargs["persist_user_message"] = persist
|
|
835
1204
|
error: str | None = None
|
|
836
1205
|
result: dict = {}
|
|
837
1206
|
try:
|
|
@@ -868,30 +1237,40 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
868
1237
|
error = str(result["final_response"])
|
|
869
1238
|
status = "cancelled" if cancelled else ("error" if failed or (error and not result.get("messages")) else "completed")
|
|
870
1239
|
pending_steer = str(result.get("pending_steer") or "") if isinstance(result, dict) else ""
|
|
1240
|
+
messages = [m for m in (result.get("messages") or []) if isinstance(m, dict)]
|
|
1241
|
+
compressed = compressions_after > compressions_before
|
|
1242
|
+
context_messages, context_estimate = _post_compression_context(agent, messages, system_message) if compressed and messages and status == "completed" else (None, None)
|
|
871
1243
|
return {
|
|
872
1244
|
"status": status,
|
|
873
|
-
"messages":
|
|
1245
|
+
"messages": messages,
|
|
1246
|
+
"context_messages": context_messages,
|
|
1247
|
+
"post_compression_context_tokens_estimate": context_estimate,
|
|
874
1248
|
"final_response": str(result.get("final_response") or ""),
|
|
875
1249
|
"error": error,
|
|
876
1250
|
"failed": failed,
|
|
877
1251
|
"partial": partial,
|
|
878
1252
|
"compression_exhausted": bool(result.get("compression_exhausted")),
|
|
879
|
-
|
|
1253
|
+
# The pinned Agent marks an exhausted tool budget only in ``turn_exit_reason`` (``max_iterations_reached(n/n)``).
|
|
1254
|
+
"tool_limit_reached": bool(result.get("tool_limit_reached") or result.get("max_iterations_reached") or str(result.get("turn_exit_reason") or "").startswith("max_iterations_reached")),
|
|
1255
|
+
"max_iterations_summary_request": _max_iterations_summary_request(),
|
|
880
1256
|
"usage": _usage(agent),
|
|
881
1257
|
"context": _context_length(agent),
|
|
882
1258
|
"model": str(getattr(agent, "model", None) or resolved_model),
|
|
883
1259
|
"provider": str(resolved_provider or ""),
|
|
884
|
-
"compressed":
|
|
1260
|
+
"compressed": compressed,
|
|
885
1261
|
"agent_session_id": str(getattr(agent, "session_id", None) or session_id),
|
|
886
1262
|
"token_sent": token_sent[0],
|
|
887
1263
|
"pending_steer": pending_steer,
|
|
888
1264
|
"live_tool_calls": live_tool_calls,
|
|
889
1265
|
}
|
|
890
1266
|
finally:
|
|
891
|
-
with
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
_RUNS_BY_SESSION.
|
|
1267
|
+
with _AGENT_CACHE_LOCK:
|
|
1268
|
+
with _RUNS_LOCK:
|
|
1269
|
+
_RUNS.pop(stream_id, None)
|
|
1270
|
+
if _RUNS_BY_SESSION.get(session_id) == stream_id:
|
|
1271
|
+
_RUNS_BY_SESSION.pop(session_id, None)
|
|
1272
|
+
# An agent evicted or replaced while this turn held it is released now that the turn is over.
|
|
1273
|
+
_release_if_dropped_locked(run.agent, home)
|
|
895
1274
|
|
|
896
1275
|
|
|
897
1276
|
def _checkpoint_required() -> bool:
|
|
@@ -1122,32 +1501,41 @@ def register(registry) -> None:
|
|
|
1122
1501
|
run = _RUNS.get(_RUNS_BY_SESSION.get(session_id) or "")
|
|
1123
1502
|
if not end_session and run is not None and not run.finished.is_set():
|
|
1124
1503
|
return {"evicted": False}
|
|
1125
|
-
|
|
1504
|
+
entry = _AGENT_CACHE.pop(session_id, None)
|
|
1505
|
+
_release_agents_locked([entry] if entry else [])
|
|
1126
1506
|
if end_session:
|
|
1127
1507
|
try:
|
|
1128
1508
|
from tools.approval import clear_session
|
|
1129
1509
|
clear_session(session_id)
|
|
1130
1510
|
except Exception: # noqa: BLE001
|
|
1131
1511
|
pass
|
|
1132
|
-
return {"evicted":
|
|
1512
|
+
return {"evicted": entry is not None}
|
|
1133
1513
|
|
|
1134
1514
|
@registry.method("chat.commit_memory", requires_agent=False)
|
|
1135
1515
|
def commit_memory_(ctx: CallContext, params: dict) -> dict:
|
|
1136
|
-
"""Predecessor ``commit_session_memory``: flush the cached Agent's memory for a session the user just left.
|
|
1516
|
+
"""Predecessor ``commit_session_memory``: flush the cached Agent's memory for a session the user just left.
|
|
1517
|
+
Providers get the session's transcript and run under its profile's ``scoped_home``."""
|
|
1137
1518
|
session_id = str(params.get("session_id") or "").strip()
|
|
1519
|
+
home = profile_home_param(params)
|
|
1138
1520
|
with _AGENT_CACHE_LOCK:
|
|
1139
1521
|
cached = _AGENT_CACHE.get(session_id)
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1522
|
+
with _RUNS_LOCK:
|
|
1523
|
+
busy = _DRAINING.is_set() or (_RUNS_BY_SESSION.get(session_id) or "") in _RUNS
|
|
1524
|
+
agent = cached[0] if cached else None
|
|
1525
|
+
commit = getattr(agent, "commit_memory_session", None) if agent is not None else None
|
|
1526
|
+
if commit is None or busy:
|
|
1527
|
+
return {"committed": False}
|
|
1528
|
+
_COMMITTING.append(agent)
|
|
1146
1529
|
try:
|
|
1147
|
-
|
|
1530
|
+
with scoped_home(home):
|
|
1531
|
+
commit(list(getattr(agent, "_session_messages", None) or []))
|
|
1148
1532
|
except Exception as exc: # noqa: BLE001
|
|
1149
1533
|
log.warning("commit_memory_session() failed for session %s: %s", session_id, exc)
|
|
1150
1534
|
return {"committed": False}
|
|
1535
|
+
finally:
|
|
1536
|
+
with _AGENT_CACHE_LOCK:
|
|
1537
|
+
_COMMITTING.remove(agent)
|
|
1538
|
+
_release_if_dropped_locked(agent, cached[2])
|
|
1151
1539
|
return {"committed": True}
|
|
1152
1540
|
|
|
1153
1541
|
@registry.method("approval.respond", requires_agent=True)
|