agim-cli 1.2.45 → 1.2.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +340 -0
- package/README.md +40 -6
- package/README.zh-CN.md +40 -6
- package/dist/cli-ui/config-wizard.js +16 -2
- package/dist/cli-ui/config-wizard.js.map +1 -1
- package/dist/cli-ui/diag.d.ts +29 -0
- package/dist/cli-ui/diag.d.ts.map +1 -0
- package/dist/cli-ui/diag.js +135 -0
- package/dist/cli-ui/diag.js.map +1 -0
- package/dist/cli-ui/i18n.d.ts +38 -0
- package/dist/cli-ui/i18n.d.ts.map +1 -1
- package/dist/cli-ui/i18n.js +101 -0
- package/dist/cli-ui/i18n.js.map +1 -1
- package/dist/cli-ui/setup-llm.d.ts +30 -0
- package/dist/cli-ui/setup-llm.d.ts.map +1 -0
- package/dist/cli-ui/setup-llm.js +460 -0
- package/dist/cli-ui/setup-llm.js.map +1 -0
- package/dist/cli-ui/setup-mcp.d.ts +19 -0
- package/dist/cli-ui/setup-mcp.d.ts.map +1 -0
- package/dist/cli-ui/setup-mcp.js +308 -0
- package/dist/cli-ui/setup-mcp.js.map +1 -0
- package/dist/cli-ui/token-menu.js +2 -2
- package/dist/cli-ui/token-menu.js.map +1 -1
- package/dist/cli.js +121 -22
- package/dist/cli.js.map +1 -1
- package/dist/core/access-token.d.ts +16 -5
- package/dist/core/access-token.d.ts.map +1 -1
- package/dist/core/access-token.js +82 -13
- package/dist/core/access-token.js.map +1 -1
- package/dist/core/agent-cwd.d.ts.map +1 -1
- package/dist/core/agent-cwd.js +74 -0
- package/dist/core/agent-cwd.js.map +1 -1
- package/dist/core/approval-bus.d.ts +22 -0
- package/dist/core/approval-bus.d.ts.map +1 -1
- package/dist/core/approval-bus.js +93 -0
- package/dist/core/approval-bus.js.map +1 -1
- package/dist/core/ask-user-router.d.ts +24 -0
- package/dist/core/ask-user-router.d.ts.map +1 -0
- package/dist/core/ask-user-router.js +84 -0
- package/dist/core/ask-user-router.js.map +1 -0
- package/dist/core/ask-user-rpc.d.ts +80 -0
- package/dist/core/ask-user-rpc.d.ts.map +1 -0
- package/dist/core/ask-user-rpc.js +388 -0
- package/dist/core/ask-user-rpc.js.map +1 -0
- package/dist/core/audit-log.d.ts +1 -1
- package/dist/core/audit-log.d.ts.map +1 -1
- package/dist/core/audit-log.js.map +1 -1
- package/dist/core/commands/builtin.d.ts.map +1 -1
- package/dist/core/commands/builtin.js +21 -5
- package/dist/core/commands/builtin.js.map +1 -1
- package/dist/core/commands/goal.d.ts +3 -0
- package/dist/core/commands/goal.d.ts.map +1 -0
- package/dist/core/commands/goal.js +240 -0
- package/dist/core/commands/goal.js.map +1 -0
- package/dist/core/commands/heartbeat.d.ts +3 -0
- package/dist/core/commands/heartbeat.d.ts.map +1 -0
- package/dist/core/commands/heartbeat.js +219 -0
- package/dist/core/commands/heartbeat.js.map +1 -0
- package/dist/core/commands/router-compare.d.ts +3 -0
- package/dist/core/commands/router-compare.d.ts.map +1 -0
- package/dist/core/commands/router-compare.js +81 -0
- package/dist/core/commands/router-compare.js.map +1 -0
- package/dist/core/commands/router.d.ts.map +1 -1
- package/dist/core/commands/router.js +9 -1
- package/dist/core/commands/router.js.map +1 -1
- package/dist/core/commands/skill.d.ts +3 -0
- package/dist/core/commands/skill.d.ts.map +1 -0
- package/dist/core/commands/skill.js +91 -0
- package/dist/core/commands/skill.js.map +1 -0
- package/dist/core/config-schema.d.ts +28 -0
- package/dist/core/config-schema.d.ts.map +1 -1
- package/dist/core/config-schema.js +124 -0
- package/dist/core/config-schema.js.map +1 -1
- package/dist/core/goals.d.ts +81 -0
- package/dist/core/goals.d.ts.map +1 -0
- package/dist/core/goals.js +361 -0
- package/dist/core/goals.js.map +1 -0
- package/dist/core/heartbeat-store.d.ts +90 -0
- package/dist/core/heartbeat-store.d.ts.map +1 -0
- package/dist/core/heartbeat-store.js +302 -0
- package/dist/core/heartbeat-store.js.map +1 -0
- package/dist/core/heartbeat.d.ts +35 -0
- package/dist/core/heartbeat.d.ts.map +1 -0
- package/dist/core/heartbeat.js +332 -0
- package/dist/core/heartbeat.js.map +1 -0
- package/dist/core/intent-llm.d.ts +9 -0
- package/dist/core/intent-llm.d.ts.map +1 -1
- package/dist/core/intent-llm.js +77 -32
- package/dist/core/intent-llm.js.map +1 -1
- package/dist/core/llm/agent-loop.d.ts +112 -0
- package/dist/core/llm/agent-loop.d.ts.map +1 -0
- package/dist/core/llm/agent-loop.js +313 -0
- package/dist/core/llm/agent-loop.js.map +1 -0
- package/dist/core/llm/anthropic-provider.d.ts +53 -0
- package/dist/core/llm/anthropic-provider.d.ts.map +1 -0
- package/dist/core/llm/anthropic-provider.js +339 -0
- package/dist/core/llm/anthropic-provider.js.map +1 -0
- package/dist/core/llm/auto-compact.d.ts +35 -0
- package/dist/core/llm/auto-compact.d.ts.map +1 -0
- package/dist/core/llm/auto-compact.js +149 -0
- package/dist/core/llm/auto-compact.js.map +1 -0
- package/dist/core/llm/builtin-dispatcher.d.ts +21 -0
- package/dist/core/llm/builtin-dispatcher.d.ts.map +1 -0
- package/dist/core/llm/builtin-dispatcher.js +196 -0
- package/dist/core/llm/builtin-dispatcher.js.map +1 -0
- package/dist/core/llm/imhub-dispatcher.d.ts +27 -0
- package/dist/core/llm/imhub-dispatcher.d.ts.map +1 -0
- package/dist/core/llm/imhub-dispatcher.js +308 -0
- package/dist/core/llm/imhub-dispatcher.js.map +1 -0
- package/dist/core/llm/index.d.ts +11 -0
- package/dist/core/llm/index.d.ts.map +1 -0
- package/dist/core/llm/index.js +16 -0
- package/dist/core/llm/index.js.map +1 -0
- package/dist/core/llm/introspection.d.ts +73 -0
- package/dist/core/llm/introspection.d.ts.map +1 -0
- package/dist/core/llm/introspection.js +90 -0
- package/dist/core/llm/introspection.js.map +1 -0
- package/dist/core/llm/mcp-client.d.ts +80 -0
- package/dist/core/llm/mcp-client.d.ts.map +1 -0
- package/dist/core/llm/mcp-client.js +270 -0
- package/dist/core/llm/mcp-client.js.map +1 -0
- package/dist/core/llm/mcp-registry.d.ts +57 -0
- package/dist/core/llm/mcp-registry.d.ts.map +1 -0
- package/dist/core/llm/mcp-registry.js +155 -0
- package/dist/core/llm/mcp-registry.js.map +1 -0
- package/dist/core/llm/openai-compat-provider.d.ts +65 -0
- package/dist/core/llm/openai-compat-provider.d.ts.map +1 -0
- package/dist/core/llm/openai-compat-provider.js +451 -0
- package/dist/core/llm/openai-compat-provider.js.map +1 -0
- package/dist/core/llm/policy-approval-gate.d.ts +30 -0
- package/dist/core/llm/policy-approval-gate.d.ts.map +1 -0
- package/dist/core/llm/policy-approval-gate.js +86 -0
- package/dist/core/llm/policy-approval-gate.js.map +1 -0
- package/dist/core/llm/provider-base.d.ts +302 -0
- package/dist/core/llm/provider-base.d.ts.map +1 -0
- package/dist/core/llm/provider-base.js +222 -0
- package/dist/core/llm/provider-base.js.map +1 -0
- package/dist/core/llm/registry.d.ts +44 -0
- package/dist/core/llm/registry.d.ts.map +1 -0
- package/dist/core/llm/registry.js +160 -0
- package/dist/core/llm/registry.js.map +1 -0
- package/dist/core/llm/secrets.d.ts +46 -0
- package/dist/core/llm/secrets.d.ts.map +1 -0
- package/dist/core/llm/secrets.js +157 -0
- package/dist/core/llm/secrets.js.map +1 -0
- package/dist/core/llm/tool-dispatcher.d.ts +37 -0
- package/dist/core/llm/tool-dispatcher.d.ts.map +1 -0
- package/dist/core/llm/tool-dispatcher.js +85 -0
- package/dist/core/llm/tool-dispatcher.js.map +1 -0
- package/dist/core/memory-consolidate.d.ts.map +1 -1
- package/dist/core/memory-consolidate.js +38 -23
- package/dist/core/memory-consolidate.js.map +1 -1
- package/dist/core/memory-distill.d.ts.map +1 -1
- package/dist/core/memory-distill.js +51 -25
- package/dist/core/memory-distill.js.map +1 -1
- package/dist/core/memory-rpc.d.ts.map +1 -1
- package/dist/core/memory-rpc.js +6 -0
- package/dist/core/memory-rpc.js.map +1 -1
- package/dist/core/message-sink.d.ts.map +1 -1
- package/dist/core/message-sink.js +76 -0
- package/dist/core/message-sink.js.map +1 -1
- package/dist/core/notification-evaluator.d.ts +49 -0
- package/dist/core/notification-evaluator.d.ts.map +1 -0
- package/dist/core/notification-evaluator.js +232 -0
- package/dist/core/notification-evaluator.js.map +1 -0
- package/dist/core/onboarding.d.ts.map +1 -1
- package/dist/core/onboarding.js +10 -0
- package/dist/core/onboarding.js.map +1 -1
- package/dist/core/persona.d.ts +10 -0
- package/dist/core/persona.d.ts.map +1 -1
- package/dist/core/persona.js +28 -14
- package/dist/core/persona.js.map +1 -1
- package/dist/core/push-rpc.d.ts +8 -0
- package/dist/core/push-rpc.d.ts.map +1 -1
- package/dist/core/push-rpc.js +41 -1
- package/dist/core/push-rpc.js.map +1 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +11 -0
- package/dist/core/registry.js.map +1 -1
- package/dist/core/render-router.d.ts.map +1 -1
- package/dist/core/render-router.js +23 -6
- package/dist/core/render-router.js.map +1 -1
- package/dist/core/router-compare.d.ts +76 -0
- package/dist/core/router-compare.d.ts.map +1 -0
- package/dist/core/router-compare.js +253 -0
- package/dist/core/router-compare.js.map +1 -0
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +25 -1
- package/dist/core/router.js.map +1 -1
- package/dist/core/skills/builtin/agim-self/SKILL.md +81 -0
- package/dist/core/skills/loader.d.ts +120 -0
- package/dist/core/skills/loader.d.ts.map +1 -0
- package/dist/core/skills/loader.js +577 -0
- package/dist/core/skills/loader.js.map +1 -0
- package/dist/core/skills-rpc.d.ts +44 -0
- package/dist/core/skills-rpc.d.ts.map +1 -0
- package/dist/core/skills-rpc.js +71 -0
- package/dist/core/skills-rpc.js.map +1 -0
- package/dist/core/types.d.ts +35 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/util/format-age.d.ts +20 -0
- package/dist/core/util/format-age.d.ts.map +1 -0
- package/dist/core/util/format-age.js +86 -0
- package/dist/core/util/format-age.js.map +1 -0
- package/dist/plugins/agents/acp/acp-adapter.d.ts +3 -0
- package/dist/plugins/agents/acp/acp-adapter.d.ts.map +1 -1
- package/dist/plugins/agents/acp/acp-adapter.js +12 -0
- package/dist/plugins/agents/acp/acp-adapter.js.map +1 -1
- package/dist/plugins/agents/antigravity/index.d.ts +1 -0
- package/dist/plugins/agents/antigravity/index.d.ts.map +1 -1
- package/dist/plugins/agents/antigravity/index.js +1 -0
- package/dist/plugins/agents/antigravity/index.js.map +1 -1
- package/dist/plugins/agents/claude-code/index.d.ts +1 -0
- package/dist/plugins/agents/claude-code/index.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/index.js +1 -0
- package/dist/plugins/agents/claude-code/index.js.map +1 -1
- package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts +22 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/mcp-approval-server.js +157 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.js.map +1 -1
- package/dist/plugins/agents/codex/index.d.ts +1 -0
- package/dist/plugins/agents/codex/index.d.ts.map +1 -1
- package/dist/plugins/agents/codex/index.js +1 -0
- package/dist/plugins/agents/codex/index.js.map +1 -1
- package/dist/plugins/agents/cursor/ensure-mcp-config.d.ts +43 -0
- package/dist/plugins/agents/cursor/ensure-mcp-config.d.ts.map +1 -0
- package/dist/plugins/agents/cursor/ensure-mcp-config.js +99 -0
- package/dist/plugins/agents/cursor/ensure-mcp-config.js.map +1 -0
- package/dist/plugins/agents/cursor/index.d.ts +28 -0
- package/dist/plugins/agents/cursor/index.d.ts.map +1 -0
- package/dist/plugins/agents/cursor/index.js +290 -0
- package/dist/plugins/agents/cursor/index.js.map +1 -0
- package/dist/plugins/agents/native/index.d.ts +34 -0
- package/dist/plugins/agents/native/index.d.ts.map +1 -0
- package/dist/plugins/agents/native/index.js +598 -0
- package/dist/plugins/agents/native/index.js.map +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts.map +1 -1
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js.map +1 -1
- package/dist/plugins/messengers/wechat/ilink-adapter.d.ts +4 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.d.ts.map +1 -1
- package/dist/plugins/messengers/wechat/ilink-adapter.js +26 -1
- package/dist/plugins/messengers/wechat/ilink-adapter.js.map +1 -1
- package/dist/web/agim-skills-api.d.ts +4 -0
- package/dist/web/agim-skills-api.d.ts.map +1 -0
- package/dist/web/agim-skills-api.js +150 -0
- package/dist/web/agim-skills-api.js.map +1 -0
- package/dist/web/background-tasks-api.d.ts +7 -0
- package/dist/web/background-tasks-api.d.ts.map +1 -0
- package/dist/web/background-tasks-api.js +225 -0
- package/dist/web/background-tasks-api.js.map +1 -0
- package/dist/web/llm-api.d.ts +8 -0
- package/dist/web/llm-api.d.ts.map +1 -0
- package/dist/web/llm-api.js +300 -0
- package/dist/web/llm-api.js.map +1 -0
- package/dist/web/mcp-api.d.ts +8 -0
- package/dist/web/mcp-api.d.ts.map +1 -0
- package/dist/web/mcp-api.js +205 -0
- package/dist/web/mcp-api.js.map +1 -0
- package/dist/web/public/assets/{a2a-Cdl8iY0e.js → a2a-06bnQicA.js} +3 -3
- package/dist/web/public/assets/{a2a-Cdl8iY0e.js.map → a2a-06bnQicA.js.map} +1 -1
- package/dist/web/public/assets/{activity-DXahuK1C.js → activity-Bhc6Esfy.js} +2 -2
- package/dist/web/public/assets/{activity-DXahuK1C.js.map → activity-Bhc6Esfy.js.map} +1 -1
- package/dist/web/public/assets/{admins-C8fJsuHC.js → admins-BVpnOw3o.js} +3 -3
- package/dist/web/public/assets/{admins-C8fJsuHC.js.map → admins-BVpnOw3o.js.map} +1 -1
- package/dist/web/public/assets/agents-Cn7D3UMz.js +7 -0
- package/dist/web/public/assets/agents-Cn7D3UMz.js.map +1 -0
- package/dist/web/public/assets/{approvals-CpyL90ef.js → approvals-BJiLyoLf.js} +3 -3
- package/dist/web/public/assets/{approvals-CpyL90ef.js.map → approvals-BJiLyoLf.js.map} +1 -1
- package/dist/web/public/assets/asks-C7gKbna1.js +7 -0
- package/dist/web/public/assets/asks-C7gKbna1.js.map +1 -0
- package/dist/web/public/assets/{audit-DDqpiC3z.js → audit-DgWZD8l5.js} +2 -2
- package/dist/web/public/assets/{audit-DDqpiC3z.js.map → audit-DgWZD8l5.js.map} +1 -1
- package/dist/web/public/assets/bell-pWZT2wlM.js +7 -0
- package/dist/web/public/assets/bell-pWZT2wlM.js.map +1 -0
- package/dist/web/public/assets/{bgjobs-CAFV-I4I.js → bgjobs-DkAJdzHf.js} +2 -2
- package/dist/web/public/assets/{bgjobs-CAFV-I4I.js.map → bgjobs-DkAJdzHf.js.map} +1 -1
- package/dist/web/public/assets/{brain-CJyZe3Oa.js → brain-B0mM-ro_.js} +2 -2
- package/dist/web/public/assets/{brain-CJyZe3Oa.js.map → brain-B0mM-ro_.js.map} +1 -1
- package/dist/web/public/assets/{briefcase-DcLbQF2I.js → briefcase-CUQlj33_.js} +2 -2
- package/dist/web/public/assets/{briefcase-DcLbQF2I.js.map → briefcase-CUQlj33_.js.map} +1 -1
- package/dist/web/public/assets/{chevron-right-BhKELKvM.js → chevron-right-CJuDP_fx.js} +2 -2
- package/dist/web/public/assets/{chevron-right-BhKELKvM.js.map → chevron-right-CJuDP_fx.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-D1qST9RS.js → circle-check-CYse8LRx.js} +2 -2
- package/dist/web/public/assets/{circle-check-D1qST9RS.js.map → circle-check-CYse8LRx.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-big-EKe3o9y1.js → circle-check-big-9NpJLA1Z.js} +2 -2
- package/dist/web/public/assets/{circle-check-big-EKe3o9y1.js.map → circle-check-big-9NpJLA1Z.js.map} +1 -1
- package/dist/web/public/assets/{circle-x-DtePUX5x.js → circle-x-BxZ24SVG.js} +2 -2
- package/dist/web/public/assets/{circle-x-DtePUX5x.js.map → circle-x-BxZ24SVG.js.map} +1 -1
- package/dist/web/public/assets/{confirm-dialog-CCfCf6BG.js → confirm-dialog-CmHeZ7ei.js} +2 -2
- package/dist/web/public/assets/{confirm-dialog-CCfCf6BG.js.map → confirm-dialog-CmHeZ7ei.js.map} +1 -1
- package/dist/web/public/assets/{data-table-BRLUxacD.js → data-table-tu4BIahS.js} +5 -5
- package/dist/web/public/assets/{data-table-BRLUxacD.js.map → data-table-tu4BIahS.js.map} +1 -1
- package/dist/web/public/assets/{dialog-By4YA8bm.js → dialog-D8QAkLhI.js} +3 -3
- package/dist/web/public/assets/{dialog-By4YA8bm.js.map → dialog-D8QAkLhI.js.map} +1 -1
- package/dist/web/public/assets/{download-CiTB2sCh.js → download-DBS0ZPTh.js} +2 -2
- package/dist/web/public/assets/{download-CiTB2sCh.js.map → download-DBS0ZPTh.js.map} +1 -1
- package/dist/web/public/assets/{email-H-b2AExg.js → email-DgbAEEvZ.js} +3 -3
- package/dist/web/public/assets/{email-H-b2AExg.js.map → email-DgbAEEvZ.js.map} +1 -1
- package/dist/web/public/assets/{empty-state-DZJTUlvz.js → empty-state-B1S_EYNJ.js} +2 -2
- package/dist/web/public/assets/{empty-state-DZJTUlvz.js.map → empty-state-B1S_EYNJ.js.map} +1 -1
- package/dist/web/public/assets/{external-link-CfU6Rttn.js → external-link-Dkmwlv6p.js} +2 -2
- package/dist/web/public/assets/{external-link-CfU6Rttn.js.map → external-link-Dkmwlv6p.js.map} +1 -1
- package/dist/web/public/assets/{eye-BvWcd0pJ.js → eye-Ch0DLviZ.js} +4 -4
- package/dist/web/public/assets/{eye-BvWcd0pJ.js.map → eye-Ch0DLviZ.js.map} +1 -1
- package/dist/web/public/assets/{facts-BRDbQLSI.js → facts-C3YV1Mkp.js} +2 -2
- package/dist/web/public/assets/{facts-BRDbQLSI.js.map → facts-C3YV1Mkp.js.map} +1 -1
- package/dist/web/public/assets/goals-DEt5gGry.js +17 -0
- package/dist/web/public/assets/goals-DEt5gGry.js.map +1 -0
- package/dist/web/public/assets/{health-DJPFOiKw.js → health-DuB9uNiZ.js} +2 -2
- package/dist/web/public/assets/{health-DJPFOiKw.js.map → health-DuB9uNiZ.js.map} +1 -1
- package/dist/web/public/assets/heart-pulse-CD8xNAGs.js +7 -0
- package/dist/web/public/assets/heart-pulse-CD8xNAGs.js.map +1 -0
- package/dist/web/public/assets/heartbeat-DbR8VwmG.js +7 -0
- package/dist/web/public/assets/heartbeat-DbR8VwmG.js.map +1 -0
- package/dist/web/public/assets/hot-izk3vGQB.js +17 -0
- package/dist/web/public/assets/hot-izk3vGQB.js.map +1 -0
- package/dist/web/public/assets/index-B7QRVy9N.css +1 -0
- package/dist/web/public/assets/index-BY1vfCja.js +166 -0
- package/dist/web/public/assets/index-BY1vfCja.js.map +1 -0
- package/dist/web/public/assets/installed-BXB3-YIy.js +31 -0
- package/dist/web/public/assets/installed-BXB3-YIy.js.map +1 -0
- package/dist/web/public/assets/{jobs-DdWaBgOh.js → jobs-DkbM4mIQ.js} +2 -2
- package/dist/web/public/assets/{jobs-DdWaBgOh.js.map → jobs-DkbM4mIQ.js.map} +1 -1
- package/dist/web/public/assets/{layout-Dr12u0W_.js → layout-5ePj6Bek.js} +2 -2
- package/dist/web/public/assets/{layout-Dr12u0W_.js.map → layout-5ePj6Bek.js.map} +1 -1
- package/dist/web/public/assets/layout-B5Dzy_Zc.js +2 -0
- package/dist/web/public/assets/layout-B5Dzy_Zc.js.map +1 -0
- package/dist/web/public/assets/layout-BZpbNQFb.js +2 -0
- package/dist/web/public/assets/layout-BZpbNQFb.js.map +1 -0
- package/dist/web/public/assets/layout-DY9Xq34u.js +2 -0
- package/dist/web/public/assets/layout-DY9Xq34u.js.map +1 -0
- package/dist/web/public/assets/{layout-BybpkTy0.js → layout-DxzBaeTv.js} +2 -2
- package/dist/web/public/assets/{layout-BybpkTy0.js.map → layout-DxzBaeTv.js.map} +1 -1
- package/dist/web/public/assets/llm-Dg4W7eRM.js +7 -0
- package/dist/web/public/assets/llm-Dg4W7eRM.js.map +1 -0
- package/dist/web/public/assets/{loader-circle-BiR4Xs-3.js → loader-circle-pGNYdPoE.js} +2 -2
- package/dist/web/public/assets/{loader-circle-BiR4Xs-3.js.map → loader-circle-pGNYdPoE.js.map} +1 -1
- package/dist/web/public/assets/{map-pin-B8hea8yW.js → map-pin-D1UX9mg7.js} +2 -2
- package/dist/web/public/assets/{map-pin-B8hea8yW.js.map → map-pin-D1UX9mg7.js.map} +1 -1
- package/dist/web/public/assets/mcp-uoOzu4_J.js +7 -0
- package/dist/web/public/assets/mcp-uoOzu4_J.js.map +1 -0
- package/dist/web/public/assets/{memos-C0_I1Uzt.js → memos-CAWASHg5.js} +2 -2
- package/dist/web/public/assets/{memos-C0_I1Uzt.js.map → memos-CAWASHg5.js.map} +1 -1
- package/dist/web/public/assets/{messengers-DuS5_JlE.js → messengers-B1-mZms8.js} +3 -3
- package/dist/web/public/assets/{messengers-DuS5_JlE.js.map → messengers-B1-mZms8.js.map} +1 -1
- package/dist/web/public/assets/native-agent-UuVvvzsW.js +7 -0
- package/dist/web/public/assets/native-agent-UuVvvzsW.js.map +1 -0
- package/dist/web/public/assets/{network-CpkocMHV.js → network-DpjSgAwW.js} +2 -2
- package/dist/web/public/assets/{network-CpkocMHV.js.map → network-DpjSgAwW.js.map} +1 -1
- package/dist/web/public/assets/{outbox-mGqTV2Ex.js → outbox-DeAgiXkY.js} +3 -3
- package/dist/web/public/assets/{outbox-mGqTV2Ex.js.map → outbox-DeAgiXkY.js.map} +1 -1
- package/dist/web/public/assets/{pagination-BAYvy11u.js → pagination-5GD_he3L.js} +3 -3
- package/dist/web/public/assets/{pagination-BAYvy11u.js.map → pagination-5GD_he3L.js.map} +1 -1
- package/dist/web/public/assets/{persona-CM0TLPJa.js → persona-BhkDCmeB.js} +2 -2
- package/dist/web/public/assets/{persona-CM0TLPJa.js.map → persona-BhkDCmeB.js.map} +1 -1
- package/dist/web/public/assets/{play-DMgMpe7N.js → play-B7o6cJ8W.js} +2 -2
- package/dist/web/public/assets/{play-DMgMpe7N.js.map → play-B7o6cJ8W.js.map} +1 -1
- package/dist/web/public/assets/plus-C5d4C6Zp.js +7 -0
- package/dist/web/public/assets/plus-C5d4C6Zp.js.map +1 -0
- package/dist/web/public/assets/policy-Ct_sKm3Y.js +2 -0
- package/dist/web/public/assets/{policy-Ch-xirrA.js.map → policy-Ct_sKm3Y.js.map} +1 -1
- package/dist/web/public/assets/{refresh-ccw-D8mc4hxU.js → refresh-ccw-BlXMN3UX.js} +2 -2
- package/dist/web/public/assets/{refresh-ccw-D8mc4hxU.js.map → refresh-ccw-BlXMN3UX.js.map} +1 -1
- package/dist/web/public/assets/{reminders-Dbe7Rc4h.js → reminders-gb1Y0S3t.js} +4 -9
- package/dist/web/public/assets/reminders-gb1Y0S3t.js.map +1 -0
- package/dist/web/public/assets/{save-1aALBaZc.js → save-DAFuApnY.js} +2 -2
- package/dist/web/public/assets/{save-1aALBaZc.js.map → save-DAFuApnY.js.map} +1 -1
- package/dist/web/public/assets/{schedules-BBQ2wcnS.js → schedules-CtFumT2B.js} +3 -3
- package/dist/web/public/assets/{schedules-BBQ2wcnS.js.map → schedules-CtFumT2B.js.map} +1 -1
- package/dist/web/public/assets/{search-DDYjsJ7-.js → search-BWuZE0YD.js} +2 -2
- package/dist/web/public/assets/{search-DDYjsJ7-.js.map → search-BWuZE0YD.js.map} +1 -1
- package/dist/web/public/assets/{service-DMp11TKF.js → service-lah7RWEc.js} +3 -3
- package/dist/web/public/assets/{service-DMp11TKF.js.map → service-lah7RWEc.js.map} +1 -1
- package/dist/web/public/assets/{status-badge-Cvj81JKi.js → status-badge-BgmFa_8z.js} +2 -2
- package/dist/web/public/assets/{status-badge-Cvj81JKi.js.map → status-badge-BgmFa_8z.js.map} +1 -1
- package/dist/web/public/assets/{subtasks-BC3Pf7OZ.js → subtasks-CgqrOGhl.js} +3 -3
- package/dist/web/public/assets/{subtasks-BC3Pf7OZ.js.map → subtasks-CgqrOGhl.js.map} +1 -1
- package/dist/web/public/assets/{table-B6nK6k3L.js → table-BhDzIBvB.js} +2 -2
- package/dist/web/public/assets/{table-B6nK6k3L.js.map → table-BhDzIBvB.js.map} +1 -1
- package/dist/web/public/assets/{topn-BwFh2jKv.js → topn-DXgTCdM_.js} +3 -3
- package/dist/web/public/assets/{topn-BwFh2jKv.js.map → topn-DXgTCdM_.js.map} +1 -1
- package/dist/web/public/assets/{trash-2-CqpMjbu5.js → trash-2-i4F_Rzh3.js} +3 -3
- package/dist/web/public/assets/{trash-2-CqpMjbu5.js.map → trash-2-i4F_Rzh3.js.map} +1 -1
- package/dist/web/public/assets/use-background-tasks-Cq2D7JQJ.js +2 -0
- package/dist/web/public/assets/use-background-tasks-Cq2D7JQJ.js.map +1 -0
- package/dist/web/public/assets/use-llm-admin-BzOVXSjz.js +2 -0
- package/dist/web/public/assets/use-llm-admin-BzOVXSjz.js.map +1 -0
- package/dist/web/public/assets/{use-memory-jxZdt-RL.js → use-memory-B_RQWQyG.js} +2 -2
- package/dist/web/public/assets/{use-memory-jxZdt-RL.js.map → use-memory-B_RQWQyG.js.map} +1 -1
- package/dist/web/public/assets/{use-observability-9TDgLO9y.js → use-observability-CCTN-a71.js} +2 -2
- package/dist/web/public/assets/{use-observability-9TDgLO9y.js.map → use-observability-CCTN-a71.js.map} +1 -1
- package/dist/web/public/assets/{use-settings-DFB_c2pr.js → use-settings-D4EdDLiK.js} +2 -2
- package/dist/web/public/assets/{use-settings-DFB_c2pr.js.map → use-settings-D4EdDLiK.js.map} +1 -1
- package/dist/web/public/assets/{use-workspace-C-Eg6yWa.js → use-workspace-BhTEp_QH.js} +2 -2
- package/dist/web/public/assets/{use-workspace-C-Eg6yWa.js.map → use-workspace-BhTEp_QH.js.map} +1 -1
- package/dist/web/public/assets/useQuery-DmoWKLw3.js +2 -0
- package/dist/web/public/assets/{useQuery-rLmHxFp4.js.map → useQuery-DmoWKLw3.js.map} +1 -1
- package/dist/web/public/assets/{vector-D9UqmLi6.js → vector-By-cheMu.js} +2 -2
- package/dist/web/public/assets/{vector-D9UqmLi6.js.map → vector-By-cheMu.js.map} +1 -1
- package/dist/web/public/assets/{viewer-_OpObeWr.js → viewer-CJNrz2iw.js} +3 -3
- package/dist/web/public/assets/{viewer-_OpObeWr.js.map → viewer-CJNrz2iw.js.map} +1 -1
- package/dist/web/public/assets/{workspace-DSHRdspq.js → workspace-DvYlIAhu.js} +4 -4
- package/dist/web/public/assets/{workspace-DSHRdspq.js.map → workspace-DvYlIAhu.js.map} +1 -1
- package/dist/web/public/assets/{workspaces-CsBdhbXz.js → workspaces-2pn73O-K.js} +3 -3
- package/dist/web/public/assets/{workspaces-CsBdhbXz.js.map → workspaces-2pn73O-K.js.map} +1 -1
- package/dist/web/public/assets/{x-D1re-vBd.js → x-5Z_de5zo.js} +2 -2
- package/dist/web/public/assets/{x-D1re-vBd.js.map → x-5Z_de5zo.js.map} +1 -1
- package/dist/web/public/index.html +2 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +45 -0
- package/dist/web/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/web/public/assets/agents-lrzFDGgX.js +0 -12
- package/dist/web/public/assets/agents-lrzFDGgX.js.map +0 -1
- package/dist/web/public/assets/hot-CFGHGkqL.js +0 -17
- package/dist/web/public/assets/hot-CFGHGkqL.js.map +0 -1
- package/dist/web/public/assets/index-C4hk1i67.js +0 -166
- package/dist/web/public/assets/index-C4hk1i67.js.map +0 -1
- package/dist/web/public/assets/index-Cvacw7Jg.css +0 -1
- package/dist/web/public/assets/installed-UONWfKHS.js +0 -7
- package/dist/web/public/assets/installed-UONWfKHS.js.map +0 -1
- package/dist/web/public/assets/layout-C8_IQRGM.js +0 -2
- package/dist/web/public/assets/layout-C8_IQRGM.js.map +0 -1
- package/dist/web/public/assets/layout-CxRHLqvi.js +0 -2
- package/dist/web/public/assets/layout-CxRHLqvi.js.map +0 -1
- package/dist/web/public/assets/layout-LC5zjw1h.js +0 -2
- package/dist/web/public/assets/layout-LC5zjw1h.js.map +0 -1
- package/dist/web/public/assets/policy-Ch-xirrA.js +0 -2
- package/dist/web/public/assets/reminders-Dbe7Rc4h.js.map +0 -1
- package/dist/web/public/assets/use-skills-OZKB5x-S.js +0 -2
- package/dist/web/public/assets/use-skills-OZKB5x-S.js.map +0 -1
- package/dist/web/public/assets/useQuery-rLmHxFp4.js +0 -2
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// LLM registry — role-based provider lookup, Stage 1.
|
|
2
|
+
//
|
|
3
|
+
// Callers don't know (or care) which concrete backend handles their
|
|
4
|
+
// request; they ask the registry for a *role* ('cheap' for distill /
|
|
5
|
+
// intent-llm, 'evaluator' for future heartbeat) and the registry hands
|
|
6
|
+
// back a configured LlmProvider — or null when nothing is configured,
|
|
7
|
+
// in which case the caller falls back to its CLI agent path.
|
|
8
|
+
//
|
|
9
|
+
// Lifecycle:
|
|
10
|
+
// - `loadFromConfig(cfg)` is called once at startup from cli.ts; it
|
|
11
|
+
// instantiates one provider per `llmBackends` entry, filters out
|
|
12
|
+
// unconfigured backends (no secret), and builds the role index.
|
|
13
|
+
// - `reload()` re-reads the config + secrets from disk (used by web
|
|
14
|
+
// "Test connection" and future hot-config reload). Tests use it to
|
|
15
|
+
// swap out fixtures without restarting bun.
|
|
16
|
+
// - `getProvider(role)` is the hot path; returns null fast.
|
|
17
|
+
//
|
|
18
|
+
// The registry is intentionally a module-level singleton — the same
|
|
19
|
+
// pattern as `core/registry.ts` (agents). Module-level state is
|
|
20
|
+
// acceptable here because every Stage-1 caller is server-side and
|
|
21
|
+
// agim's process model is single-instance.
|
|
22
|
+
import { logger as rootLogger } from '../logger.js';
|
|
23
|
+
import { OpenAICompatProvider } from './openai-compat-provider.js';
|
|
24
|
+
import { AnthropicProvider } from './anthropic-provider.js';
|
|
25
|
+
import { resolveSecret, loadSecretsFile } from './secrets.js';
|
|
26
|
+
const log = rootLogger.child({ component: 'llm-registry' });
|
|
27
|
+
let state = freshState();
|
|
28
|
+
function freshState() {
|
|
29
|
+
return {
|
|
30
|
+
providers: new Map(),
|
|
31
|
+
roles: new Map(),
|
|
32
|
+
unconfigured: new Set(),
|
|
33
|
+
duplicates: new Set(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build the registry from a validated config. Idempotent — repeated
|
|
38
|
+
* calls fully replace the previous state. Logs (info / warn) the load
|
|
39
|
+
* outcome so operators see what landed at startup.
|
|
40
|
+
*/
|
|
41
|
+
export function loadFromConfig(cfg) {
|
|
42
|
+
const next = freshState();
|
|
43
|
+
const backends = Array.isArray(cfg.llmBackends)
|
|
44
|
+
? cfg.llmBackends
|
|
45
|
+
: [];
|
|
46
|
+
// Pre-load secrets file once so each provider construction doesn't
|
|
47
|
+
// re-read it from disk.
|
|
48
|
+
const secrets = loadSecretsFile();
|
|
49
|
+
for (const raw of backends) {
|
|
50
|
+
const cfg = raw;
|
|
51
|
+
if (!cfg || typeof cfg !== 'object' || typeof cfg.name !== 'string')
|
|
52
|
+
continue;
|
|
53
|
+
if (cfg.enabled === false) {
|
|
54
|
+
log.debug({ event: 'llm-registry.skip_disabled', name: cfg.name });
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (next.providers.has(cfg.name)) {
|
|
58
|
+
next.duplicates.add(cfg.name);
|
|
59
|
+
log.warn({ event: 'llm-registry.duplicate_name', name: cfg.name }, `Duplicate llmBackends entry "${cfg.name}"; last wins`);
|
|
60
|
+
}
|
|
61
|
+
const apiKey = resolveSecret(cfg.name, secrets);
|
|
62
|
+
const provider = buildProvider(cfg, apiKey);
|
|
63
|
+
if (!provider)
|
|
64
|
+
continue;
|
|
65
|
+
if (!provider.isConfigured()) {
|
|
66
|
+
next.unconfigured.add(cfg.name);
|
|
67
|
+
log.info({ event: 'llm-registry.unconfigured', name: cfg.name }, `LLM backend "${cfg.name}" has no secret; skipped (caller will fall back to CLI agent)`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
next.providers.set(cfg.name, provider);
|
|
71
|
+
}
|
|
72
|
+
const roleMap = cfg.llmRoles;
|
|
73
|
+
if (roleMap && typeof roleMap === 'object') {
|
|
74
|
+
for (const [role, backendName] of Object.entries(roleMap)) {
|
|
75
|
+
if (typeof backendName !== 'string' || !backendName)
|
|
76
|
+
continue;
|
|
77
|
+
next.roles.set(role, backendName);
|
|
78
|
+
if (!next.providers.has(backendName)) {
|
|
79
|
+
log.warn({ event: 'llm-registry.role_dangling', role, backend: backendName }, `llmRoles.${role} points at "${backendName}" which has no configured backend`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
state = next;
|
|
84
|
+
log.info({
|
|
85
|
+
event: 'llm-registry.loaded',
|
|
86
|
+
backends: state.providers.size,
|
|
87
|
+
roles: state.roles.size,
|
|
88
|
+
unconfigured: state.unconfigured.size,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get the provider configured for `role`, or null when no usable
|
|
93
|
+
* backend is wired. Callers should NEVER throw on null — that's how the
|
|
94
|
+
* registry tells them "fall back to your CLI path".
|
|
95
|
+
*/
|
|
96
|
+
export function getProvider(role) {
|
|
97
|
+
const backend = state.roles.get(role);
|
|
98
|
+
if (!backend)
|
|
99
|
+
return null;
|
|
100
|
+
return state.providers.get(backend) ?? null;
|
|
101
|
+
}
|
|
102
|
+
/** Look up a provider directly by backend name (mostly for the web
|
|
103
|
+
* "Test connection" button and unit tests). */
|
|
104
|
+
export function getProviderByName(name) {
|
|
105
|
+
return state.providers.get(name) ?? null;
|
|
106
|
+
}
|
|
107
|
+
/** List all configured backends — handy for the web UI roster. */
|
|
108
|
+
export function listProviders() {
|
|
109
|
+
return Array.from(state.providers.values()).map(p => ({
|
|
110
|
+
name: p.name,
|
|
111
|
+
providerType: p.providerType,
|
|
112
|
+
model: p.model,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
/** Test/diagnostic: clear the registry. Used by bun tests so module
|
|
116
|
+
* state doesn't leak across cases. */
|
|
117
|
+
export function _resetRegistry() {
|
|
118
|
+
state = freshState();
|
|
119
|
+
}
|
|
120
|
+
/** Diagnostic snapshot for the cost dashboard's "LLM backends" tile. */
|
|
121
|
+
export function describeRegistry() {
|
|
122
|
+
return {
|
|
123
|
+
backends: listProviders(),
|
|
124
|
+
roles: Object.fromEntries(state.roles),
|
|
125
|
+
unconfigured: Array.from(state.unconfigured).sort(),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function buildProvider(cfg, apiKey) {
|
|
129
|
+
switch (cfg.provider) {
|
|
130
|
+
case 'openai-compat':
|
|
131
|
+
return new OpenAICompatProvider({
|
|
132
|
+
name: cfg.name,
|
|
133
|
+
model: cfg.model,
|
|
134
|
+
apiKey,
|
|
135
|
+
baseUrl: cfg.baseUrl,
|
|
136
|
+
providerLabel: cfg.label,
|
|
137
|
+
extraHeaders: cfg.extraHeaders,
|
|
138
|
+
defaultTemperature: cfg.temperature,
|
|
139
|
+
defaultMaxTokens: cfg.maxTokens,
|
|
140
|
+
retryOnTransient: cfg.retryOnTransient,
|
|
141
|
+
});
|
|
142
|
+
case 'anthropic':
|
|
143
|
+
return new AnthropicProvider({
|
|
144
|
+
name: cfg.name,
|
|
145
|
+
model: cfg.model,
|
|
146
|
+
apiKey,
|
|
147
|
+
baseUrl: cfg.baseUrl,
|
|
148
|
+
providerLabel: cfg.label,
|
|
149
|
+
extraHeaders: cfg.extraHeaders,
|
|
150
|
+
defaultTemperature: cfg.temperature,
|
|
151
|
+
defaultMaxTokens: cfg.maxTokens,
|
|
152
|
+
retryOnTransient: cfg.retryOnTransient,
|
|
153
|
+
});
|
|
154
|
+
default:
|
|
155
|
+
// Future: ollama-native, bedrock, vertex.
|
|
156
|
+
log.warn({ event: 'llm-registry.unknown_provider', name: cfg.name, provider: cfg.provider }, `Unknown LLM provider "${cfg.provider}" for backend "${cfg.name}"; skipped`);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/llm/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const LLM_SECRETS_PATH: string;
|
|
2
|
+
interface SecretsFile {
|
|
3
|
+
version: number;
|
|
4
|
+
keys: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
/** Convert a backend name to the env var suffix. Same rule as the rest
|
|
7
|
+
* of the codebase: non-alphanumeric → underscore, uppercased. So
|
|
8
|
+
* "deepseek-cheap" → "AGIM_LLM_SECRET_DEEPSEEK_CHEAP". */
|
|
9
|
+
export declare function envKeyForBackend(name: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Read the on-disk secrets file (or return an empty SecretsFile if missing / corrupt).
|
|
12
|
+
* Logs a warning on corrupt files so operators notice — we deliberately
|
|
13
|
+
* don't throw because callers want the provider to gracefully report
|
|
14
|
+
* "not configured" rather than crash agim at startup.
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadSecretsFile(): SecretsFile;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a single secret for a backend, applying env-var precedence.
|
|
19
|
+
* Returns undefined when nothing is configured — providers downstream
|
|
20
|
+
* use this to flip `isConfigured()` off.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveSecret(backendName: string, fileSecrets?: SecretsFile): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Persist a secret. Idempotent — writes the entire file with 0600
|
|
25
|
+
* perms. Used by the CLI wizard (`agim config llm add`) once we land
|
|
26
|
+
* Stage 4; Stage 1 ships the helper so tests can exercise it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function saveSecret(backendName: string, apiKey: string): void;
|
|
29
|
+
/** Remove a secret entirely. Safe to call on missing names. */
|
|
30
|
+
export declare function removeSecret(backendName: string): void;
|
|
31
|
+
/** Test helper: list configured backend names (no values). */
|
|
32
|
+
export declare function listConfiguredBackends(): string[];
|
|
33
|
+
/**
|
|
34
|
+
* One-shot health check used by the CLI wizard + web "test backend"
|
|
35
|
+
* button. Returns a short reason string when something looks off so the
|
|
36
|
+
* UI doesn't have to guess. The string is operator-facing; keep it
|
|
37
|
+
* succinct and actionable.
|
|
38
|
+
*/
|
|
39
|
+
export declare function describeSecretSource(backendName: string): {
|
|
40
|
+
source: 'env' | 'file' | 'none';
|
|
41
|
+
envKey: string;
|
|
42
|
+
filePath: string;
|
|
43
|
+
perms?: string;
|
|
44
|
+
};
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../../src/core/llm/secrets.ts"],"names":[],"mappings":"AA8BA,eAAO,MAAM,gBAAgB,QAAsC,CAAA;AAEnE,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC7B;AASD;;2DAE2D;AAC3D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAmB7C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAQhG;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAMpE;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAKtD;AAED,8DAA8D;AAC9D,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAoBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG;IACzD,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAaA"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// LLM secrets store — `~/.agim/llm-secrets.json` with 0600 perms.
|
|
2
|
+
//
|
|
3
|
+
// Why a separate file (not the main config):
|
|
4
|
+
// - config.json is committed/backed up in many user setups; api keys
|
|
5
|
+
// belong somewhere we can chmod 0600 without changing user habits.
|
|
6
|
+
// - Aligns with the same pattern used for `~/.agim/acp-token` and the
|
|
7
|
+
// web token — secrets live next to their public config but stay in
|
|
8
|
+
// their own file.
|
|
9
|
+
//
|
|
10
|
+
// File format (intentionally tiny):
|
|
11
|
+
// {
|
|
12
|
+
// "version": 1,
|
|
13
|
+
// "keys": { "<backend-name>": "<api-key>" }
|
|
14
|
+
// }
|
|
15
|
+
//
|
|
16
|
+
// Lookup precedence (callers go through `resolveSecret`):
|
|
17
|
+
// 1. process.env `AGIM_LLM_SECRET_<UPPER_BACKEND_NAME>` — wins.
|
|
18
|
+
// Lets systemd unit files / CI / cloud agents inject without
|
|
19
|
+
// touching the on-disk file. Hyphens in the backend name are
|
|
20
|
+
// converted to underscores for the env var name.
|
|
21
|
+
// 2. ~/.agim/llm-secrets.json `keys.<backend-name>`.
|
|
22
|
+
// 3. undefined (provider will report isConfigured()=false).
|
|
23
|
+
import { existsSync, readFileSync, writeFileSync, chmodSync, mkdirSync, statSync } from 'node:fs';
|
|
24
|
+
import { dirname, join } from 'node:path';
|
|
25
|
+
import { AGIM_HOME } from '../agim-paths.js';
|
|
26
|
+
import { logger as rootLogger } from '../logger.js';
|
|
27
|
+
const log = rootLogger.child({ component: 'llm-secrets' });
|
|
28
|
+
export const LLM_SECRETS_PATH = join(AGIM_HOME, 'llm-secrets.json');
|
|
29
|
+
function emptyFile() {
|
|
30
|
+
// Fresh object every call. Returning a shared `EMPTY` const would let
|
|
31
|
+
// a caller's mutation (e.g. saveSecret stamping the keys map) leak
|
|
32
|
+
// back into the next loadSecretsFile() call. Cheap to allocate.
|
|
33
|
+
return { version: 1, keys: {} };
|
|
34
|
+
}
|
|
35
|
+
/** Convert a backend name to the env var suffix. Same rule as the rest
|
|
36
|
+
* of the codebase: non-alphanumeric → underscore, uppercased. So
|
|
37
|
+
* "deepseek-cheap" → "AGIM_LLM_SECRET_DEEPSEEK_CHEAP". */
|
|
38
|
+
export function envKeyForBackend(name) {
|
|
39
|
+
const safe = name.replace(/[^A-Za-z0-9]+/g, '_').toUpperCase();
|
|
40
|
+
return `AGIM_LLM_SECRET_${safe}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read the on-disk secrets file (or return an empty SecretsFile if missing / corrupt).
|
|
44
|
+
* Logs a warning on corrupt files so operators notice — we deliberately
|
|
45
|
+
* don't throw because callers want the provider to gracefully report
|
|
46
|
+
* "not configured" rather than crash agim at startup.
|
|
47
|
+
*/
|
|
48
|
+
export function loadSecretsFile() {
|
|
49
|
+
try {
|
|
50
|
+
if (!existsSync(LLM_SECRETS_PATH))
|
|
51
|
+
return emptyFile();
|
|
52
|
+
const raw = readFileSync(LLM_SECRETS_PATH, 'utf-8');
|
|
53
|
+
const parsed = JSON.parse(raw);
|
|
54
|
+
if (!parsed || typeof parsed !== 'object' || !parsed.keys || typeof parsed.keys !== 'object') {
|
|
55
|
+
log.warn({ event: 'llm-secrets.malformed', path: LLM_SECRETS_PATH });
|
|
56
|
+
return emptyFile();
|
|
57
|
+
}
|
|
58
|
+
// Coerce keys to strings; ignore non-string values silently.
|
|
59
|
+
const clean = {};
|
|
60
|
+
for (const [k, v] of Object.entries(parsed.keys)) {
|
|
61
|
+
if (typeof v === 'string' && v.length > 0)
|
|
62
|
+
clean[k] = v;
|
|
63
|
+
}
|
|
64
|
+
return { version: parsed.version === 1 ? 1 : 1, keys: clean };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
log.warn({ event: 'llm-secrets.read_failed', err: String(err) });
|
|
68
|
+
return emptyFile();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a single secret for a backend, applying env-var precedence.
|
|
73
|
+
* Returns undefined when nothing is configured — providers downstream
|
|
74
|
+
* use this to flip `isConfigured()` off.
|
|
75
|
+
*/
|
|
76
|
+
export function resolveSecret(backendName, fileSecrets) {
|
|
77
|
+
if (!backendName)
|
|
78
|
+
return undefined;
|
|
79
|
+
const envKey = envKeyForBackend(backendName);
|
|
80
|
+
const envVal = process.env[envKey];
|
|
81
|
+
if (envVal && envVal.length > 0)
|
|
82
|
+
return envVal;
|
|
83
|
+
const file = fileSecrets ?? loadSecretsFile();
|
|
84
|
+
const v = file.keys[backendName];
|
|
85
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Persist a secret. Idempotent — writes the entire file with 0600
|
|
89
|
+
* perms. Used by the CLI wizard (`agim config llm add`) once we land
|
|
90
|
+
* Stage 4; Stage 1 ships the helper so tests can exercise it.
|
|
91
|
+
*/
|
|
92
|
+
export function saveSecret(backendName, apiKey) {
|
|
93
|
+
if (!backendName)
|
|
94
|
+
throw new Error('saveSecret: backendName is required');
|
|
95
|
+
if (!apiKey)
|
|
96
|
+
throw new Error('saveSecret: apiKey is required (use removeSecret to delete)');
|
|
97
|
+
const file = loadSecretsFile();
|
|
98
|
+
file.keys[backendName] = apiKey;
|
|
99
|
+
_writeFile(file);
|
|
100
|
+
}
|
|
101
|
+
/** Remove a secret entirely. Safe to call on missing names. */
|
|
102
|
+
export function removeSecret(backendName) {
|
|
103
|
+
const file = loadSecretsFile();
|
|
104
|
+
if (!(backendName in file.keys))
|
|
105
|
+
return;
|
|
106
|
+
delete file.keys[backendName];
|
|
107
|
+
_writeFile(file);
|
|
108
|
+
}
|
|
109
|
+
/** Test helper: list configured backend names (no values). */
|
|
110
|
+
export function listConfiguredBackends() {
|
|
111
|
+
return Object.keys(loadSecretsFile().keys).sort();
|
|
112
|
+
}
|
|
113
|
+
function _writeFile(file) {
|
|
114
|
+
// Ensure the parent directory exists; AGIM_HOME might still be a fresh
|
|
115
|
+
// install where nothing else has written yet.
|
|
116
|
+
const dir = dirname(LLM_SECRETS_PATH);
|
|
117
|
+
try {
|
|
118
|
+
if (!existsSync(dir))
|
|
119
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Best-effort; if we can't even mkdir, the writeFile below will
|
|
123
|
+
// throw a clearer error.
|
|
124
|
+
}
|
|
125
|
+
const payload = `${JSON.stringify(file, null, 2)}\n`;
|
|
126
|
+
writeFileSync(LLM_SECRETS_PATH, payload, { encoding: 'utf-8', mode: 0o600 });
|
|
127
|
+
// writeFileSync's mode is advisory on some filesystems; chmodSync is
|
|
128
|
+
// the belt-and-suspenders that matches how the bootstrap token writer
|
|
129
|
+
// does it elsewhere in the codebase.
|
|
130
|
+
try {
|
|
131
|
+
chmodSync(LLM_SECRETS_PATH, 0o600);
|
|
132
|
+
}
|
|
133
|
+
catch { /* ignore */ }
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* One-shot health check used by the CLI wizard + web "test backend"
|
|
137
|
+
* button. Returns a short reason string when something looks off so the
|
|
138
|
+
* UI doesn't have to guess. The string is operator-facing; keep it
|
|
139
|
+
* succinct and actionable.
|
|
140
|
+
*/
|
|
141
|
+
export function describeSecretSource(backendName) {
|
|
142
|
+
const envKey = envKeyForBackend(backendName);
|
|
143
|
+
if (process.env[envKey])
|
|
144
|
+
return { source: 'env', envKey, filePath: LLM_SECRETS_PATH };
|
|
145
|
+
const file = loadSecretsFile();
|
|
146
|
+
if (file.keys[backendName]) {
|
|
147
|
+
let perms;
|
|
148
|
+
try {
|
|
149
|
+
const st = statSync(LLM_SECRETS_PATH);
|
|
150
|
+
perms = `0${(st.mode & 0o777).toString(8)}`;
|
|
151
|
+
}
|
|
152
|
+
catch { /* ignore */ }
|
|
153
|
+
return { source: 'file', envKey, filePath: LLM_SECRETS_PATH, perms };
|
|
154
|
+
}
|
|
155
|
+
return { source: 'none', envKey, filePath: LLM_SECRETS_PATH };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../../src/core/llm/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ToolCallRequest } from './provider-base.js';
|
|
2
|
+
/** Outcome of executing one tool call. Mirrors mcp-client.ToolCallResult
|
|
3
|
+
* so dispatchers can pass it through unchanged. */
|
|
4
|
+
export interface DispatcherResult {
|
|
5
|
+
text: string;
|
|
6
|
+
isError: boolean;
|
|
7
|
+
/** Optional dispatcher tag for audit / debug — e.g. 'mcp:filesystem'
|
|
8
|
+
* or 'builtin'. Lets the agent loop's audit row distinguish where
|
|
9
|
+
* the cost is going. Default empty string. */
|
|
10
|
+
source?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Async function the agent loop invokes once per tool call. Signal is
|
|
14
|
+
* the loop's overall AbortSignal — dispatchers should honor it to
|
|
15
|
+
* abort in-flight tool work on /stop or timeout.
|
|
16
|
+
*/
|
|
17
|
+
export type ToolDispatcher = (call: ToolCallRequest, signal: AbortSignal) => Promise<DispatcherResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Build the default dispatcher: every call name beginning with `mcp_`
|
|
20
|
+
* is routed to the MCP registry; everything else gets a graceful
|
|
21
|
+
* "tool unknown" error. The agent loop's job is to surface that error
|
|
22
|
+
* back to the model so it can choose a different tool or give up.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildMcpDispatcher(): ToolDispatcher;
|
|
25
|
+
/**
|
|
26
|
+
* Stack dispatchers: the first one that returns a non-"unknown" result
|
|
27
|
+
* wins. Use this in sub-PR #4 to put built-in tools above MCP, so
|
|
28
|
+
* `echo` / `time` etc. resolve locally and don't accidentally hit a
|
|
29
|
+
* server's same-named tool.
|
|
30
|
+
*
|
|
31
|
+
* Implementation note: we identify "unknown" by checking `source ===
|
|
32
|
+
* 'unknown'`. Dispatchers signal "I don't handle this" by returning
|
|
33
|
+
* isError=true with source='unknown'; everything else (including
|
|
34
|
+
* domain-level tool errors) is considered handled.
|
|
35
|
+
*/
|
|
36
|
+
export declare function combineDispatchers(...dispatchers: ToolDispatcher[]): ToolDispatcher;
|
|
37
|
+
//# sourceMappingURL=tool-dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-dispatcher.d.ts","sourceRoot":"","sources":["../../../src/core/llm/tool-dispatcher.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAGzD;oDACoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;IAChB;;mDAE+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAC3B,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,WAAW,KAChB,OAAO,CAAC,gBAAgB,CAAC,CAAA;AAE9B;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,cAAc,CAgBnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,WAAW,EAAE,cAAc,EAAE,GAAG,cAAc,CAmBnF"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// tool-dispatcher — routes a ToolCallRequest to the right executor
|
|
2
|
+
// (Stage 2 sub-PR #3).
|
|
3
|
+
//
|
|
4
|
+
// The agent loop doesn't want to know WHERE each tool lives. It just
|
|
5
|
+
// produces a ToolCallRequest and asks the dispatcher to run it. This
|
|
6
|
+
// keeps the loop testable (stub dispatcher) and lets us add new tool
|
|
7
|
+
// sources (built-in agim tools, sub-agent delegate, remote ACP tools,
|
|
8
|
+
// …) later without touching the loop.
|
|
9
|
+
//
|
|
10
|
+
// Stage 2 sub-PR #3 ships only:
|
|
11
|
+
// - the dispatcher interface (ToolDispatcher)
|
|
12
|
+
// - DispatcherResult shape (text + isError; matches mcp-client)
|
|
13
|
+
// - one concrete implementation: `buildMcpDispatcher()` that routes
|
|
14
|
+
// every call to mcp-registry. Names not in the MCP roster return
|
|
15
|
+
// an isError result rather than throwing.
|
|
16
|
+
// - a `combineDispatchers(...)` helper for future stages that want
|
|
17
|
+
// to layer built-in tools above MCP.
|
|
18
|
+
//
|
|
19
|
+
// Out of scope here (covered by sub-PR #4):
|
|
20
|
+
// - built-in tool functions (echo / time / web_search) — they're an
|
|
21
|
+
// orthogonal feature; once the loop is stable, adding them is one
|
|
22
|
+
// more dispatcher implementation.
|
|
23
|
+
import { callToolByName as callMcpToolByName, getOwningServer as getMcpOwningServer } from './mcp-registry.js';
|
|
24
|
+
/**
|
|
25
|
+
* Build the default dispatcher: every call name beginning with `mcp_`
|
|
26
|
+
* is routed to the MCP registry; everything else gets a graceful
|
|
27
|
+
* "tool unknown" error. The agent loop's job is to surface that error
|
|
28
|
+
* back to the model so it can choose a different tool or give up.
|
|
29
|
+
*/
|
|
30
|
+
export function buildMcpDispatcher() {
|
|
31
|
+
return async (call, signal) => {
|
|
32
|
+
if (!call.name.startsWith('mcp_')) {
|
|
33
|
+
return {
|
|
34
|
+
text: `unknown tool "${call.name}" (dispatcher only handles mcp_*; no built-in fallback registered)`,
|
|
35
|
+
isError: true,
|
|
36
|
+
source: 'unknown',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const r = await callMcpToolByName(call.name, call.arguments, { signal });
|
|
40
|
+
return {
|
|
41
|
+
text: r.text,
|
|
42
|
+
isError: r.isError,
|
|
43
|
+
source: tagFor(call.name),
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Stack dispatchers: the first one that returns a non-"unknown" result
|
|
49
|
+
* wins. Use this in sub-PR #4 to put built-in tools above MCP, so
|
|
50
|
+
* `echo` / `time` etc. resolve locally and don't accidentally hit a
|
|
51
|
+
* server's same-named tool.
|
|
52
|
+
*
|
|
53
|
+
* Implementation note: we identify "unknown" by checking `source ===
|
|
54
|
+
* 'unknown'`. Dispatchers signal "I don't handle this" by returning
|
|
55
|
+
* isError=true with source='unknown'; everything else (including
|
|
56
|
+
* domain-level tool errors) is considered handled.
|
|
57
|
+
*/
|
|
58
|
+
export function combineDispatchers(...dispatchers) {
|
|
59
|
+
return async (call, signal) => {
|
|
60
|
+
let lastUnknown = null;
|
|
61
|
+
for (const d of dispatchers) {
|
|
62
|
+
const r = await d(call, signal);
|
|
63
|
+
if (r.source === 'unknown') {
|
|
64
|
+
lastUnknown = r;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
return r;
|
|
68
|
+
}
|
|
69
|
+
// No dispatcher took it. Surface the last "unknown" message; if
|
|
70
|
+
// somehow nothing returned, synthesise one.
|
|
71
|
+
return lastUnknown ?? {
|
|
72
|
+
text: `unknown tool "${call.name}" (no dispatcher handled it)`,
|
|
73
|
+
isError: true,
|
|
74
|
+
source: 'unknown',
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Map a namespaced MCP tool name back to its owning server for the
|
|
79
|
+
* audit `source` tag. Falls back to 'mcp:?' if the registry can't
|
|
80
|
+
* identify the owner (shouldn't happen for valid calls). */
|
|
81
|
+
function tagFor(callName) {
|
|
82
|
+
const server = getMcpOwningServer(callName);
|
|
83
|
+
return server ? `mcp:${server}` : 'mcp:?';
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=tool-dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-dispatcher.js","sourceRoot":"","sources":["../../../src/core/llm/tool-dispatcher.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,uBAAuB;AACvB,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,qEAAqE;AACrE,sEAAsE;AACtE,sCAAsC;AACtC,EAAE;AACF,gCAAgC;AAChC,gDAAgD;AAChD,kEAAkE;AAClE,sEAAsE;AACtE,qEAAqE;AACrE,8CAA8C;AAC9C,qEAAqE;AACrE,yCAAyC;AACzC,EAAE;AACF,4CAA4C;AAC5C,sEAAsE;AACtE,sEAAsE;AACtE,sCAAsC;AAGtC,OAAO,EAAE,cAAc,IAAI,iBAAiB,EAAE,eAAe,IAAI,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAuB9G;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;QAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,OAAO;gBACL,IAAI,EAAE,iBAAiB,IAAI,CAAC,IAAI,oEAAoE;gBACpG,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,SAAS;aAClB,CAAA;QACH,CAAC;QACD,MAAM,CAAC,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;QACxE,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;SAC1B,CAAA;IACH,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAG,WAA6B;IACjE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;QAC5B,IAAI,WAAW,GAA4B,IAAI,CAAA;QAC/C,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;YAC/B,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC3B,WAAW,GAAG,CAAC,CAAA;gBACf,SAAQ;YACV,CAAC;YACD,OAAO,CAAC,CAAA;QACV,CAAC;QACD,gEAAgE;QAChE,4CAA4C;QAC5C,OAAO,WAAW,IAAI;YACpB,IAAI,EAAE,iBAAiB,IAAI,CAAC,IAAI,8BAA8B;YAC9D,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,SAAS;SAClB,CAAA;IACH,CAAC,CAAA;AACH,CAAC;AAED;;6DAE6D;AAC7D,SAAS,MAAM,CAAC,QAAgB;IAC9B,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAA;IAC3C,OAAO,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAA;AAC3C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-consolidate.d.ts","sourceRoot":"","sources":["../../src/core/memory-consolidate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory-consolidate.d.ts","sourceRoot":"","sources":["../../src/core/memory-consolidate.ts"],"names":[],"mappings":"AAgNA;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAkBxF;AAKD,8EAA8E;AAC9E,wBAAgB,uBAAuB,IAAI,IAAI,CAgB9C;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAK7C"}
|
|
@@ -22,6 +22,7 @@ import { logger as rootLogger } from './logger.js';
|
|
|
22
22
|
import { registry } from './registry.js';
|
|
23
23
|
import { listRecentFacts, upsertPersona, } from './memory.js';
|
|
24
24
|
import { isMemoryEnabled } from './persona.js';
|
|
25
|
+
import { tryIntrospect } from './llm/index.js';
|
|
25
26
|
const log = rootLogger.child({ component: 'memory-consolidate' });
|
|
26
27
|
const MAX_FACTS_PER_USER = 80; // cap input size to keep prompt cheap
|
|
27
28
|
const PERSONA_TARGET_CHARS = 600; // ~300 tokens give or take
|
|
@@ -65,7 +66,7 @@ function buildConsolidationPrompt(facts) {
|
|
|
65
66
|
'- 矛盾事实保留最新的,标"(目前)"',
|
|
66
67
|
'- 优先 category=profile / preference / goal,category=history 仅写最新',
|
|
67
68
|
'- 用清晰的小段落或要点列出,不要散文式叙述',
|
|
68
|
-
|
|
69
|
+
`- 严格 ≤ ${PERSONA_TARGET_CHARS} 字符`,
|
|
69
70
|
'- 输出**纯文本摘要,不要 markdown 围栏,不要任何解释**',
|
|
70
71
|
'',
|
|
71
72
|
'事实列表:',
|
|
@@ -78,30 +79,44 @@ async function consolidateOneUser(userKey, agentName) {
|
|
|
78
79
|
const facts = listRecentFacts(userKey, MAX_FACTS_PER_USER);
|
|
79
80
|
if (facts.length === 0)
|
|
80
81
|
return false;
|
|
81
|
-
const agent = registry.getAgent(agentName);
|
|
82
|
-
if (!agent) {
|
|
83
|
-
log.warn({ event: 'memory.consolidate.agent_missing', agentName, userKey });
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
82
|
const prompt = buildConsolidationPrompt(facts);
|
|
87
83
|
let raw = '';
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
break; // safety cap
|
|
100
|
-
}
|
|
84
|
+
// D+ Stage 1: prefer a configured native LLM backend ('cheap' role).
|
|
85
|
+
// Falls back to the user's recent CLI agent on no-backend or failure
|
|
86
|
+
// so historical behaviour is preserved when llmBackends is empty.
|
|
87
|
+
const nativeResult = await tryIntrospect({
|
|
88
|
+
role: 'cheap',
|
|
89
|
+
prompt,
|
|
90
|
+
timeoutMs: PROMPT_TIMEOUT_MS,
|
|
91
|
+
temperature: 0.2,
|
|
92
|
+
});
|
|
93
|
+
if (nativeResult) {
|
|
94
|
+
raw = nativeResult.text;
|
|
101
95
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
else {
|
|
97
|
+
const agent = registry.getAgent(agentName);
|
|
98
|
+
if (!agent) {
|
|
99
|
+
log.warn({ event: 'memory.consolidate.agent_missing', agentName, userKey });
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const gen = agent.sendPrompt(`memory-consolidate-${Date.now()}`, prompt, [], {
|
|
104
|
+
platform: 'memory-consolidate',
|
|
105
|
+
userId: userKey,
|
|
106
|
+
});
|
|
107
|
+
const deadline = Date.now() + PROMPT_TIMEOUT_MS;
|
|
108
|
+
for await (const chunk of gen) {
|
|
109
|
+
raw += chunk;
|
|
110
|
+
if (Date.now() > deadline)
|
|
111
|
+
break;
|
|
112
|
+
if (raw.length > PERSONA_TARGET_CHARS * 4)
|
|
113
|
+
break; // safety cap
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
log.warn({ event: 'memory.consolidate.agent_failed', userKey, agentName, err: String(err) });
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
105
120
|
}
|
|
106
121
|
let summary = raw.trim();
|
|
107
122
|
if (!summary)
|
|
@@ -112,7 +127,7 @@ async function consolidateOneUser(userKey, agentName) {
|
|
|
112
127
|
summary = fence[1].trim();
|
|
113
128
|
if (summary.length > PERSONA_TARGET_CHARS * 2) {
|
|
114
129
|
// Hard truncate at 2x target to avoid runaway summaries.
|
|
115
|
-
summary = summary.slice(0, PERSONA_TARGET_CHARS * 2)
|
|
130
|
+
summary = `${summary.slice(0, PERSONA_TARGET_CHARS * 2)}…`;
|
|
116
131
|
}
|
|
117
132
|
const ok = upsertPersona(userKey, summary);
|
|
118
133
|
if (ok) {
|