comisai 1.0.27 → 1.0.30
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/node_modules/@comis/agent/dist/bootstrap/sections/tool-descriptions.js +62 -8
- package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.js +3 -1
- package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.d.ts +7 -0
- package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +26 -0
- package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.d.ts +21 -0
- package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.js +29 -9
- package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.d.ts +10 -2
- package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.js +15 -9
- package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.d.ts +17 -2
- package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.js +19 -8
- package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +28 -0
- package/node_modules/@comis/agent/dist/executor/executor-response-filter.js +3 -0
- package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +3 -1
- package/node_modules/@comis/agent/dist/executor/phase-filter.d.ts +20 -4
- package/node_modules/@comis/agent/dist/executor/phase-filter.js +62 -19
- package/node_modules/@comis/agent/dist/executor/pi-executor.js +6 -0
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/config-resolver.js +2 -3
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.js +2 -3
- package/node_modules/@comis/agent/dist/executor/ttl-guard.js +2 -3
- package/node_modules/@comis/agent/dist/index.d.ts +4 -2
- package/node_modules/@comis/agent/dist/index.js +3 -2
- package/node_modules/@comis/agent/dist/model/compaction-model-resolver.d.ts +41 -0
- package/node_modules/@comis/agent/dist/model/compaction-model-resolver.js +51 -0
- package/node_modules/@comis/agent/dist/model/model-registry-adapter.js +113 -26
- package/node_modules/@comis/agent/dist/model/model-scanner.d.ts +27 -0
- package/node_modules/@comis/agent/dist/model/model-scanner.js +64 -23
- package/node_modules/@comis/agent/dist/model/operation-model-defaults.d.ts +37 -15
- package/node_modules/@comis/agent/dist/model/operation-model-defaults.js +70 -25
- package/node_modules/@comis/agent/dist/model/operation-model-resolver.d.ts +2 -2
- package/node_modules/@comis/agent/dist/model/operation-model-resolver.js +12 -8
- package/node_modules/@comis/agent/dist/provider/capabilities.d.ts +21 -0
- package/node_modules/@comis/agent/dist/provider/capabilities.js +28 -0
- package/node_modules/@comis/agent/dist/session/orphaned-message-repair.js +61 -1
- package/node_modules/@comis/agent/dist/workspace/templates.js +1 -1
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/dist/shared/channel-manager.d.ts +19 -1
- package/node_modules/@comis/channels/dist/shared/channel-manager.js +59 -3
- package/node_modules/@comis/channels/dist/shared/deliver-to-channel.d.ts +10 -0
- package/node_modules/@comis/channels/dist/shared/deliver-to-channel.js +25 -10
- package/node_modules/@comis/channels/dist/shared/execution-deliver.d.ts +1 -1
- package/node_modules/@comis/channels/dist/shared/execution-deliver.js +1 -1
- package/node_modules/@comis/channels/dist/shared/execution-pipeline.d.ts +8 -0
- package/node_modules/@comis/channels/dist/shared/inbound-gate.js +21 -3
- package/node_modules/@comis/channels/dist/shared/inbound-pipeline.d.ts +8 -0
- package/node_modules/@comis/channels/dist/shared/inbound-route.d.ts +1 -1
- package/node_modules/@comis/channels/dist/shared/inbound-route.js +1 -0
- package/node_modules/@comis/channels/dist/telegram/message-mapper.d.ts +18 -1
- package/node_modules/@comis/channels/dist/telegram/message-mapper.js +95 -1
- package/node_modules/@comis/channels/dist/telegram/telegram-adapter.js +7 -1
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/dist/config/schema-agent.d.ts +15 -3
- package/node_modules/@comis/core/dist/config/schema-agent.js +6 -2
- package/node_modules/@comis/core/dist/config/schema-integrations.d.ts +4 -4
- package/node_modules/@comis/core/dist/config/schema-integrations.js +3 -3
- package/node_modules/@comis/core/dist/config/schema-models.d.ts +4 -2
- package/node_modules/@comis/core/dist/config/schema-models.js +4 -2
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/dist/daemon.js +74 -9
- package/node_modules/@comis/daemon/dist/rpc/agent-handlers.js +40 -9
- package/node_modules/@comis/daemon/dist/rpc/builtin-provider-guard.d.ts +16 -0
- package/node_modules/@comis/daemon/dist/rpc/builtin-provider-guard.js +60 -0
- package/node_modules/@comis/daemon/dist/rpc/config-handlers.js +59 -0
- package/node_modules/@comis/daemon/dist/rpc/credential-resolver.d.ts +17 -0
- package/node_modules/@comis/daemon/dist/rpc/credential-resolver.js +99 -0
- package/node_modules/@comis/daemon/dist/rpc/message-handlers.d.ts +5 -0
- package/node_modules/@comis/daemon/dist/rpc/message-handlers.js +25 -4
- package/node_modules/@comis/daemon/dist/rpc/model-handlers.d.ts +4 -3
- package/node_modules/@comis/daemon/dist/rpc/model-handlers.js +21 -3
- package/node_modules/@comis/daemon/dist/rpc/provider-handlers.js +82 -6
- package/node_modules/@comis/daemon/dist/rpc/rpc-dispatch.d.ts +4 -0
- package/node_modules/@comis/daemon/dist/wiring/inbound-message-id-resolver.d.ts +48 -0
- package/node_modules/@comis/daemon/dist/wiring/inbound-message-id-resolver.js +58 -0
- package/node_modules/@comis/daemon/dist/wiring/restart-continuation.d.ts +10 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-agents.d.ts +18 -6
- package/node_modules/@comis/daemon/dist/wiring/setup-agents.js +98 -46
- package/node_modules/@comis/daemon/dist/wiring/setup-channels.d.ts +13 -1
- package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +2 -1
- package/node_modules/@comis/daemon/dist/wiring/setup-gateway-rpc.js +1 -1
- package/node_modules/@comis/daemon/package.json +2 -2
- package/node_modules/@comis/gateway/package.json +1 -1
- package/node_modules/@comis/infra/package.json +1 -1
- package/node_modules/@comis/memory/package.json +1 -1
- package/node_modules/@comis/scheduler/package.json +1 -1
- package/node_modules/@comis/shared/package.json +1 -1
- package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.d.ts +1 -1
- package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.js +1 -1
- package/node_modules/@comis/skills/dist/bridge/tool-audit.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/exec-tool.d.ts +12 -11
- package/node_modules/@comis/skills/dist/builtin/exec-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/file/apply-patch-tool.d.ts +3 -2
- package/node_modules/@comis/skills/dist/builtin/file/apply-patch-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/file-tools/edit-tool.d.ts +7 -6
- package/node_modules/@comis/skills/dist/builtin/file-tools/edit-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/file-tools/find-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/file-tools/find-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/file-tools/grep-tool.d.ts +16 -15
- package/node_modules/@comis/skills/dist/builtin/file-tools/grep-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/file-tools/ls-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/file-tools/ls-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/file-tools/notebook-edit-tool.d.ts +7 -6
- package/node_modules/@comis/skills/dist/builtin/file-tools/notebook-edit-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/file-tools/read-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/file-tools/read-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/file-tools/write-tool.d.ts +5 -4
- package/node_modules/@comis/skills/dist/builtin/file-tools/write-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/admin-manage-factory.d.ts +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.d.ts +28 -27
- package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/background-tasks-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/background-tasks-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/browser-tool-schema.d.ts +67 -66
- package/node_modules/@comis/skills/dist/builtin/platform/browser-tool-schema.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/channels-manage-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/platform/channels-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/cron-tool.d.ts +20 -19
- package/node_modules/@comis/skills/dist/builtin/platform/cron-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/ctx-expand-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/ctx-expand-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/ctx-inspect-tool.d.ts +3 -2
- package/node_modules/@comis/skills/dist/builtin/platform/ctx-inspect-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/ctx-recall-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/platform/ctx-recall-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/ctx-search-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/platform/ctx-search-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/describe-video-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/describe-video-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/discord-action-tool.d.ts +2 -1
- package/node_modules/@comis/skills/dist/builtin/platform/discord-action-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/extract-document-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/extract-document-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +12 -11
- package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/heartbeat-manage-tool.d.ts +23 -22
- package/node_modules/@comis/skills/dist/builtin/platform/heartbeat-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/image-generate-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/image-generate-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/image-tool.d.ts +8 -7
- package/node_modules/@comis/skills/dist/builtin/platform/image-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/mcp-manage-tool.d.ts +9 -8
- package/node_modules/@comis/skills/dist/builtin/platform/mcp-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/memory-get-tool.d.ts +5 -4
- package/node_modules/@comis/skills/dist/builtin/platform/memory-get-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/memory-manage-tool.d.ts +12 -11
- package/node_modules/@comis/skills/dist/builtin/platform/memory-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/memory-search-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/memory-search-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/memory-store-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/memory-store-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/message-tool.d.ts +32 -31
- package/node_modules/@comis/skills/dist/builtin/platform/message-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/messaging-factory.d.ts +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/models-manage-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/models-manage-tool.js +11 -4
- package/node_modules/@comis/skills/dist/builtin/platform/notify-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/platform/notify-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/obs-query-tool.d.ts +11 -10
- package/node_modules/@comis/skills/dist/builtin/platform/obs-query-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/pipeline-tool.d.ts +37 -36
- package/node_modules/@comis/skills/dist/builtin/platform/pipeline-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/platform-action-tool.d.ts +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/providers-manage-tool.d.ts +21 -20
- package/node_modules/@comis/skills/dist/builtin/platform/providers-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/session-search-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/platform/session-search-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/session-status-tool.d.ts +3 -2
- package/node_modules/@comis/skills/dist/builtin/platform/session-status-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-history-tool.d.ts +5 -4
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-history-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-list-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-list-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-manage-tool.d.ts +5 -4
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-send-tool.d.ts +7 -6
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-send-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-spawn-tool.d.ts +15 -14
- package/node_modules/@comis/skills/dist/builtin/platform/sessions-spawn-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/skills-manage-tool.d.ts +8 -7
- package/node_modules/@comis/skills/dist/builtin/platform/skills-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/slack-action-tool.d.ts +2 -1
- package/node_modules/@comis/skills/dist/builtin/platform/slack-action-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/subagents-tool.d.ts +7 -6
- package/node_modules/@comis/skills/dist/builtin/platform/subagents-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/telegram-action-tool.d.ts +2 -1
- package/node_modules/@comis/skills/dist/builtin/platform/telegram-action-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/tokens-manage-tool.d.ts +5 -4
- package/node_modules/@comis/skills/dist/builtin/platform/tokens-manage-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/transcribe-audio-tool.d.ts +4 -3
- package/node_modules/@comis/skills/dist/builtin/platform/transcribe-audio-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/tts-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/platform/tts-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/unified-context-tool.d.ts +13 -12
- package/node_modules/@comis/skills/dist/builtin/platform/unified-context-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/unified-memory-tool.d.ts +18 -17
- package/node_modules/@comis/skills/dist/builtin/platform/unified-memory-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/unified-session-tool.d.ts +11 -10
- package/node_modules/@comis/skills/dist/builtin/platform/unified-session-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/whatsapp-action-tool.d.ts +2 -1
- package/node_modules/@comis/skills/dist/builtin/platform/whatsapp-action-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/process-tool.d.ts +6 -5
- package/node_modules/@comis/skills/dist/builtin/process-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/web-fetch-tool.d.ts +5 -4
- package/node_modules/@comis/skills/dist/builtin/web-fetch-tool.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/web-search-tool.d.ts +9 -8
- package/node_modules/@comis/skills/dist/builtin/web-search-tool.js +1 -1
- package/node_modules/@comis/skills/package.json +1 -1
- package/node_modules/@comis/web/dist/assets/{agent-detail-DqL6Artv.js → agent-detail-71BSbSfD.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{agent-editor-CNM_h94Y.js → agent-editor-CTSDZhwT.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{agent-list-Dbh-xD_F.js → agent-list-BEhni2ea.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{billing-view-C1DmtyzK.js → billing-view-DVP1IvVs.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{channel-detail-CtCH22N1.js → channel-detail-N_YK74xC.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{channel-list-C7xXn-60.js → channel-list-DRk6ZJaF.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{chat-console-C51pjFwk.js → chat-console-Dm-GtSf9.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{config-editor-BLArYRB7.js → config-editor-CIferYX6.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{context-dag-browser-fuyMinNI.js → context-dag-browser-CL84rXXM.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{context-engine-Bngf2bH0.js → context-engine-B1HOTEZv.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{delivery-view-C80hucxX.js → delivery-view-Y6JKYVFw.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{diagnostics-view-Cl4VbHZ6.js → diagnostics-view-DWV1UQjz.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{ic-chat-message-ByFUoMm6.js → ic-chat-message-DfSERzzg.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{ic-connection-dot-C4nDHgY2.js → ic-connection-dot-CXyhlJup.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{ic-tool-call-Bh5kq-yY.js → ic-tool-call-DNmwTjek.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{index-BBkuC-EU.js → index-CBr0Tm9_.js} +2 -2
- package/node_modules/@comis/web/dist/assets/{mcp-management-DB-phOo7.js → mcp-management-BaH2-vox.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{media-config-CRqZ1ZUH.js → media-config-CZLshJoN.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{media-test-C9vE20Oy.js → media-test-C9NUWgo_.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{memory-inspector-CeqfnxMZ.js → memory-inspector-D_fmTcRN.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{message-center-Daup7Mof.js → message-center-BBFlNCZn.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{models-DLYnEU8E.js → models-BytGLm99.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{observe-view-BTSt_PO5.js → observe-view-VXtHqaqq.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{pipeline-builder-DknfzyLt.js → pipeline-builder-CfXczlfJ.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{pipeline-history-JnHZdeU_.js → pipeline-history-CPmXFnbe.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{pipeline-history-detail-Dg4knsEb.js → pipeline-history-detail-DcueTMs9.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{pipeline-list-AEnibjsp.js → pipeline-list-B-xG5WZh.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{pipeline-monitor-DG7RbIOO.js → pipeline-monitor-pnIOYaSY.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{scheduler-uL1fYKAT.js → scheduler-BtUIFHhA.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{security-C3DywRLH.js → security-C8mWRq2y.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{session-detail-BtqCNWXV.js → session-detail-DgdkO5ka.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{session-list-CJXWa2XT.js → session-list-DcylcfTn.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{setup-wizard-ywn7oJvu.js → setup-wizard-BP5yjsuL.js} +75 -39
- package/node_modules/@comis/web/dist/assets/{skills-DX0KYnWD.js → skills-DXt1bX8Z.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{subagents-B8p5YJEB.js → subagents-C7YbUHXY.js} +1 -1
- package/node_modules/@comis/web/dist/assets/{workspace-manager-CgzNIrw1.js → workspace-manager-DP6pW4wa.js} +1 -1
- package/node_modules/@comis/web/dist/index.html +1 -1
- package/node_modules/@comis/web/package.json +1 -1
- package/npm-shrinkwrap.json +6126 -0
- package/package.json +74 -74
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Built-in provider redundancy guard for providers.create (260501-gyy FIX 2).
|
|
4
|
+
*
|
|
5
|
+
* Rejects providers_manage create attempts that would shadow pi-ai's
|
|
6
|
+
* dynamic catalog with a redundant custom entry. A built-in provider
|
|
7
|
+
* (one in pi-ai's getProviders()) already has its full model list
|
|
8
|
+
* available via the catalog -- custom entries are only legitimate for
|
|
9
|
+
* proxies (different baseUrl) or non-SDK providers. Without this guard,
|
|
10
|
+
* an LLM agent that creates a catalog-shadowing entry with a stale or
|
|
11
|
+
* invented model id sets up downstream 404 / "model not found" failures
|
|
12
|
+
* (production: 2026-05-01 08:53-08:54 trace).
|
|
13
|
+
*
|
|
14
|
+
* Generic by construction: rejection message uses ${providerId} +
|
|
15
|
+
* ${apiKeyName} interpolation only -- no per-provider hardcoded names
|
|
16
|
+
* in the source template (catalog-agnostic). Pinned by source-grep
|
|
17
|
+
* regression test in builtin-provider-guard.test.ts.
|
|
18
|
+
*
|
|
19
|
+
* Mirrors credential-resolver.ts shape (260501-2pz precedent).
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import { getProviders, getModels } from "@mariozechner/pi-ai";
|
|
24
|
+
/**
|
|
25
|
+
* Check whether a providers.create call would land a redundant custom
|
|
26
|
+
* entry that shadows pi-ai's dynamic catalog. Returns ok=true when the
|
|
27
|
+
* call is legitimate (genuine custom provider OR proxy with distinct
|
|
28
|
+
* baseUrl), ok=false with an actionable reason otherwise.
|
|
29
|
+
*/
|
|
30
|
+
export function checkBuiltInProviderRedundancy(providerId, config) {
|
|
31
|
+
const native = new Set(getProviders());
|
|
32
|
+
if (!native.has(providerId)) {
|
|
33
|
+
// Genuine custom provider -- never redundant.
|
|
34
|
+
return { ok: true };
|
|
35
|
+
}
|
|
36
|
+
const catalogBaseUrl = getModels(providerId)[0]?.baseUrl;
|
|
37
|
+
const userBaseUrl = config.baseUrl?.trim();
|
|
38
|
+
if (userBaseUrl && userBaseUrl !== catalogBaseUrl) {
|
|
39
|
+
// Legitimate proxy -- user pointed at a non-catalog URL.
|
|
40
|
+
return { ok: true };
|
|
41
|
+
}
|
|
42
|
+
// Built-in + catalog/absent baseUrl -> redundant.
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
reason: buildRejectionMessage(providerId, config),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function buildRejectionMessage(providerId, config) {
|
|
49
|
+
const apiKeyName = config.apiKeyName ?? "<APIKEY_NAME>";
|
|
50
|
+
const lines = [];
|
|
51
|
+
lines.push(`Cannot create custom provider entry for "${providerId}": this provider is built-in to the pi-ai SDK with its full model catalog available dynamically. Custom entries are only needed for proxies (different baseUrl) or non-SDK providers.`);
|
|
52
|
+
lines.push("");
|
|
53
|
+
lines.push("To use this provider:");
|
|
54
|
+
lines.push(` 1. gateway env_set ${apiKeyName}=<your-key> (find name via gateway env_list)`);
|
|
55
|
+
lines.push(` 2. gateway patch agents.<id>.provider = "${providerId}"`);
|
|
56
|
+
lines.push(` 3. gateway patch agents.<id>.model = "<model-id>" (find available models via models_manage list provider:${providerId})`);
|
|
57
|
+
lines.push("");
|
|
58
|
+
lines.push(`If you intended a custom proxy with a different base URL, supply a distinct provider_id (e.g., "${providerId}-proxy") and your custom baseUrl.`);
|
|
59
|
+
return lines.join("\n");
|
|
60
|
+
}
|
|
@@ -14,6 +14,39 @@ import { stringify as yamlStringify } from "yaml";
|
|
|
14
14
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from "node:fs";
|
|
15
15
|
import { dirname } from "node:path";
|
|
16
16
|
import { z } from "zod";
|
|
17
|
+
import { resolveProviderCredential } from "./credential-resolver.js";
|
|
18
|
+
/**
|
|
19
|
+
* True when the patch key writes a provider/model field.
|
|
20
|
+
* Matches `<id>.provider` or `<id>.model` (leaf only — does not match
|
|
21
|
+
* nested paths like modelFailover.fallbackModels.0.provider, which is
|
|
22
|
+
* deliberately out-of-scope for the credential guard).
|
|
23
|
+
*/
|
|
24
|
+
function isAgentProviderOrModelKey(key) {
|
|
25
|
+
if (!key)
|
|
26
|
+
return false;
|
|
27
|
+
return /(^|\.)(provider|model)$/.test(key);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the provider value the patch is establishing.
|
|
31
|
+
* - For `.provider` patches, the new value IS the provider.
|
|
32
|
+
* - For `.model`-only patches, look up the agent's CURRENT provider
|
|
33
|
+
* (validates that the agent's existing auth chain still resolves —
|
|
34
|
+
* surfaces stale broken configs at patch time rather than at next chat).
|
|
35
|
+
* Returns undefined for paths the guard doesn't validate.
|
|
36
|
+
*/
|
|
37
|
+
function extractTargetProvider(key, newValue, currentConfig) {
|
|
38
|
+
if (key.endsWith(".provider")) {
|
|
39
|
+
return typeof newValue === "string" ? newValue : undefined;
|
|
40
|
+
}
|
|
41
|
+
if (key.endsWith(".model")) {
|
|
42
|
+
const agentId = key.split(".")[0];
|
|
43
|
+
if (!agentId)
|
|
44
|
+
return undefined;
|
|
45
|
+
// eslint-disable-next-line security/detect-object-injection -- agents map is typed Record; agentId from validated key
|
|
46
|
+
return currentConfig.agents?.[agentId]?.provider;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
17
50
|
/**
|
|
18
51
|
* Restore MCP server `env` from existing YAML when the UI patch
|
|
19
52
|
* omits it (because config.read redacted secret values to "[REDACTED]").
|
|
@@ -419,6 +452,32 @@ export function createConfigHandlers(deps) {
|
|
|
419
452
|
: " This setting requires manual operator intervention via config files.";
|
|
420
453
|
throw new Error(`Config path "${key ? `${section}.${key}` : section}" is immutable and cannot be modified at runtime.${suffix}`);
|
|
421
454
|
}
|
|
455
|
+
// Credential guard (260501-2pz): when a patch targets an agent's
|
|
456
|
+
// provider/model field, verify the resulting provider's API key is
|
|
457
|
+
// resolvable from at least one source pi-coding-agent will consult
|
|
458
|
+
// at runtime. Fail-loud here rather than letting an unauthorized
|
|
459
|
+
// provider config persist and explode at the next chat turn.
|
|
460
|
+
// See `.planning/design/daemon-credential-guard/PLAN.md`.
|
|
461
|
+
if (section === "agents" && isAgentProviderOrModelKey(key)) {
|
|
462
|
+
const targetProvider = extractTargetProvider(key, coercedValue, deps.container.config);
|
|
463
|
+
if (targetProvider !== undefined) {
|
|
464
|
+
const resolution = resolveProviderCredential(targetProvider, {
|
|
465
|
+
providerEntries: deps.container.config.providers?.entries ?? {},
|
|
466
|
+
secretManager: deps.container.secretManager,
|
|
467
|
+
});
|
|
468
|
+
if (!resolution.ok) {
|
|
469
|
+
deps.logger.warn({
|
|
470
|
+
method: "config.patch",
|
|
471
|
+
section,
|
|
472
|
+
key,
|
|
473
|
+
targetProvider,
|
|
474
|
+
hint: "Provider credential not resolvable",
|
|
475
|
+
errorKind: "validation",
|
|
476
|
+
}, "Config patch rejected: missing provider credential");
|
|
477
|
+
throw new Error(resolution.reason);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
422
481
|
// Build patch object (use coerced value for the actual data, keep original for audit)
|
|
423
482
|
let patch;
|
|
424
483
|
if (key) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProviderEntry } from "@comis/core";
|
|
2
|
+
export interface CredentialResolverDeps {
|
|
3
|
+
/** Provider-entry map from comis config (providers.entries). */
|
|
4
|
+
providerEntries?: Record<string, ProviderEntry>;
|
|
5
|
+
/** Secret manager backing process.env / ~/.comis/.env. */
|
|
6
|
+
secretManager?: {
|
|
7
|
+
has(key: string): boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface CredentialResolution {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
/** When ok=false: actionable error message ready to throw. */
|
|
13
|
+
reason?: string;
|
|
14
|
+
/** When ok=true: which source resolved. Useful for debug logs. */
|
|
15
|
+
source?: "keyless" | "providers_entry" | "env_canonical";
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveProviderCredential(targetProvider: string, deps: CredentialResolverDeps): CredentialResolution;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Pre-write credential validator for agent provider/model patches.
|
|
4
|
+
*
|
|
5
|
+
* When a patch sets agents.<id>.provider, verify the API key is resolvable
|
|
6
|
+
* from one of the sources pi-coding-agent will consult at runtime. Reject
|
|
7
|
+
* fail-loud with an actionable error if no source resolves.
|
|
8
|
+
*
|
|
9
|
+
* Resolution chain (matches pi-coding-agent runtime semantics):
|
|
10
|
+
* 1. KEYLESS_PROVIDER_TYPES.has(entry.type) — ollama / lm-studio
|
|
11
|
+
* 2. providers.entries.<provider>.apiKeyName → secretManager.has(...)
|
|
12
|
+
* 3. pi-ai's getEnvApiKey(provider) — canonical env + OAuth + ADC + AWS
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import { getEnvApiKey } from "@mariozechner/pi-ai";
|
|
17
|
+
/**
|
|
18
|
+
* Provider types that don't need an API key. Mirrors agent's
|
|
19
|
+
* KEYLESS_PROVIDER_TYPES at model-registry-adapter.ts:60 — extended here to
|
|
20
|
+
* include lm-studio (the agent-side may also extend in a follow-up; the
|
|
21
|
+
* tool guide already documents lm-studio as keyless).
|
|
22
|
+
*/
|
|
23
|
+
const KEYLESS_PROVIDER_TYPES = new Set(["ollama", "lm-studio"]);
|
|
24
|
+
export function resolveProviderCredential(targetProvider, deps) {
|
|
25
|
+
if (!targetProvider || typeof targetProvider !== "string") {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
reason: `Invalid provider value: must be a non-empty string (got ${JSON.stringify(targetProvider)})`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// eslint-disable-next-line security/detect-object-injection -- typed Record<string, ProviderEntry> read; targetProvider validated above
|
|
32
|
+
const entry = deps.providerEntries?.[targetProvider];
|
|
33
|
+
// 1. Keyless types
|
|
34
|
+
if (entry && KEYLESS_PROVIDER_TYPES.has(entry.type)) {
|
|
35
|
+
return { ok: true, source: "keyless" };
|
|
36
|
+
}
|
|
37
|
+
// 2. Source A: providers.entries with secret-manager-resolvable apiKeyName
|
|
38
|
+
if (entry?.apiKeyName && deps.secretManager?.has(entry.apiKeyName)) {
|
|
39
|
+
return { ok: true, source: "providers_entry" };
|
|
40
|
+
}
|
|
41
|
+
// 3. Source B: pi-ai canonical env / OAuth / ADC chain
|
|
42
|
+
if (getEnvApiKey(targetProvider)) {
|
|
43
|
+
return { ok: true, source: "env_canonical" };
|
|
44
|
+
}
|
|
45
|
+
return { ok: false, reason: buildRejectionMessage(targetProvider, entry) };
|
|
46
|
+
}
|
|
47
|
+
function buildRejectionMessage(targetProvider, entry) {
|
|
48
|
+
const lines = [];
|
|
49
|
+
lines.push(`Cannot set agent provider to "${targetProvider}": no API key found.`);
|
|
50
|
+
if (entry?.apiKeyName) {
|
|
51
|
+
lines.push(`The configured providers.entries.${targetProvider}.apiKeyName is "${entry.apiKeyName}", but that name is not in env.`);
|
|
52
|
+
lines.push(`Recovery:`);
|
|
53
|
+
lines.push(` Run gateway({action:"env_set", env_key:"${entry.apiKeyName}", env_value:"<key>"}) to store the key, then retry this patch.`);
|
|
54
|
+
lines.push(` Run gateway({action:"env_list", filter:"${targetProvider.toUpperCase()}*"}) to see what's already configured.`);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const canonical = canonicalEnvKeyHint(targetProvider);
|
|
58
|
+
lines.push(`No providers.entries.${targetProvider} exists, and the canonical env key${canonical ? ` (${canonical})` : ""} is not set.`);
|
|
59
|
+
lines.push(`Recovery options (pick one):`);
|
|
60
|
+
lines.push(canonical
|
|
61
|
+
? ` (a) Run gateway({action:"env_set", env_key:"${canonical}", env_value:"<key>"}) to store the key, then retry this patch.`
|
|
62
|
+
: ` (a) Run gateway({action:"env_list", filter:"${targetProvider.toUpperCase()}*"}) to find the env name, then env_set it.`);
|
|
63
|
+
lines.push(` (b) Run providers_manage({action:"create", provider_id:"${targetProvider}", config:{apiKeyName:"<KEY_NAME>", models:[{id:"<model_id>"}]}}) referencing an apiKeyName that already exists in env.`);
|
|
64
|
+
lines.push(`Always run gateway({action:"env_list", filter:"${targetProvider.toUpperCase()}*"}) FIRST to check before asking the user.`);
|
|
65
|
+
}
|
|
66
|
+
return lines.join("\n");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Best-effort hint at the canonical env key name for a provider, for use in
|
|
70
|
+
* error messages. Returns undefined when pi-ai doesn't have a canonical
|
|
71
|
+
* mapping (custom providers must use providers.entries).
|
|
72
|
+
*
|
|
73
|
+
* Note: this duplicates pi-ai's internal envMap (env-api-keys.js) for
|
|
74
|
+
* messaging only — not used for resolution. The actual check uses
|
|
75
|
+
* getEnvApiKey() which always wins. Acceptable to be slightly out-of-sync
|
|
76
|
+
* with pi-ai upgrades: hint quality only, never load-bearing.
|
|
77
|
+
*/
|
|
78
|
+
function canonicalEnvKeyHint(provider) {
|
|
79
|
+
const knownMap = {
|
|
80
|
+
openai: "OPENAI_API_KEY",
|
|
81
|
+
"azure-openai-responses": "AZURE_OPENAI_API_KEY",
|
|
82
|
+
google: "GEMINI_API_KEY",
|
|
83
|
+
groq: "GROQ_API_KEY",
|
|
84
|
+
cerebras: "CEREBRAS_API_KEY",
|
|
85
|
+
xai: "XAI_API_KEY",
|
|
86
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
87
|
+
"vercel-ai-gateway": "AI_GATEWAY_API_KEY",
|
|
88
|
+
zai: "ZAI_API_KEY",
|
|
89
|
+
mistral: "MISTRAL_API_KEY",
|
|
90
|
+
minimax: "MINIMAX_API_KEY",
|
|
91
|
+
"minimax-cn": "MINIMAX_CN_API_KEY",
|
|
92
|
+
huggingface: "HF_TOKEN",
|
|
93
|
+
opencode: "OPENCODE_API_KEY",
|
|
94
|
+
"kimi-coding": "KIMI_API_KEY",
|
|
95
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
96
|
+
};
|
|
97
|
+
// eslint-disable-next-line security/detect-object-injection -- read of static const map indexed by validated provider string
|
|
98
|
+
return knownMap[provider];
|
|
99
|
+
}
|
|
@@ -31,6 +31,11 @@ export interface MessageHandlerDeps {
|
|
|
31
31
|
onGatewayAttachment?: (channelId: string, marker: string) => void;
|
|
32
32
|
/** Delivery queue for crash-safe persistence */
|
|
33
33
|
deliveryQueue?: import("@comis/core").DeliveryQueuePort;
|
|
34
|
+
/** Resolves daemon NormalizedMessage.id UUIDs to platform-native message
|
|
35
|
+
* ids for delete/edit/react handlers. Optional — when absent, message_id
|
|
36
|
+
* passes through unchanged (which fails on Telegram for inbound UUIDs but
|
|
37
|
+
* works for native ids returned by message.send). */
|
|
38
|
+
inboundMessageIdResolver?: import("../wiring/inbound-message-id-resolver.js").InboundMessageIdResolver;
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
41
|
* Create message and platform-action RPC handlers.
|
|
@@ -14,6 +14,27 @@ import { relative } from "node:path";
|
|
|
14
14
|
import { deliverToChannel, formatForChannel } from "@comis/channels";
|
|
15
15
|
import { resolveAdapter, authorizeChannelAccess } from "../wiring/daemon-utils.js";
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
|
+
// Helpers
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Translate the agent's `message_id` argument to the platform-native id when
|
|
21
|
+
* it matches a recently-received inbound message UUID. Returns the original
|
|
22
|
+
* value when no match (already-native id from message.send, expired UUID,
|
|
23
|
+
* cross-channel mismatch). Always returns a string.
|
|
24
|
+
*/
|
|
25
|
+
function resolveMessageId(resolver, messageId, channelType, channelId) {
|
|
26
|
+
if (!resolver)
|
|
27
|
+
return messageId;
|
|
28
|
+
const record = resolver.resolve(messageId);
|
|
29
|
+
if (!record)
|
|
30
|
+
return messageId;
|
|
31
|
+
if (record.channelType !== channelType)
|
|
32
|
+
return messageId;
|
|
33
|
+
if (record.channelId !== channelId)
|
|
34
|
+
return messageId;
|
|
35
|
+
return record.nativeId;
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
17
38
|
// Capability guard — maps RPC methods to ChannelCapability feature flags.
|
|
18
39
|
// When channelPlugins is provided, unsupported actions are rejected before
|
|
19
40
|
// reaching the adapter, saving a tool call and producing a clear error.
|
|
@@ -79,7 +100,7 @@ export function createMessageHandlers(deps) {
|
|
|
79
100
|
const channelType = params.channel_type;
|
|
80
101
|
const channelId = params.channel_id;
|
|
81
102
|
const text = params.text;
|
|
82
|
-
const messageId = params.message_id;
|
|
103
|
+
const messageId = resolveMessageId(deps.inboundMessageIdResolver, params.message_id, channelType, channelId);
|
|
83
104
|
authorizeChannelAccess(params._originChannelId, channelId, params._trustLevel);
|
|
84
105
|
const adapter = resolveAdapter(channelType, deps.adaptersByType);
|
|
85
106
|
const extra = {
|
|
@@ -103,7 +124,7 @@ export function createMessageHandlers(deps) {
|
|
|
103
124
|
const channelType = params.channel_type;
|
|
104
125
|
assertCapability("message.react", channelType, deps.channelPlugins);
|
|
105
126
|
const channelId = params.channel_id;
|
|
106
|
-
const messageId = params.message_id;
|
|
127
|
+
const messageId = resolveMessageId(deps.inboundMessageIdResolver, params.message_id, channelType, channelId);
|
|
107
128
|
const emoji = params.emoji;
|
|
108
129
|
authorizeChannelAccess(params._originChannelId, channelId, params._trustLevel);
|
|
109
130
|
const adapter = resolveAdapter(channelType, deps.adaptersByType);
|
|
@@ -118,7 +139,7 @@ export function createMessageHandlers(deps) {
|
|
|
118
139
|
const channelType = params.channel_type;
|
|
119
140
|
assertCapability("message.edit", channelType, deps.channelPlugins);
|
|
120
141
|
const channelId = params.channel_id;
|
|
121
|
-
const messageId = params.message_id;
|
|
142
|
+
const messageId = resolveMessageId(deps.inboundMessageIdResolver, params.message_id, channelType, channelId);
|
|
122
143
|
const text = params.text;
|
|
123
144
|
authorizeChannelAccess(params._originChannelId, channelId, params._trustLevel);
|
|
124
145
|
const adapter = resolveAdapter(channelType, deps.adaptersByType);
|
|
@@ -132,7 +153,7 @@ export function createMessageHandlers(deps) {
|
|
|
132
153
|
const channelType = params.channel_type;
|
|
133
154
|
assertCapability("message.delete", channelType, deps.channelPlugins);
|
|
134
155
|
const channelId = params.channel_id;
|
|
135
|
-
const messageId = params.message_id;
|
|
156
|
+
const messageId = resolveMessageId(deps.inboundMessageIdResolver, params.message_id, channelType, channelId);
|
|
136
157
|
authorizeChannelAccess(params._originChannelId, channelId, params._trustLevel);
|
|
137
158
|
const adapter = resolveAdapter(channelType, deps.adaptersByType);
|
|
138
159
|
const result = await adapter.deleteMessage(channelId, messageId);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model management RPC handler module.
|
|
3
3
|
* Handles model catalog query methods:
|
|
4
|
-
* models.list
|
|
5
|
-
* models.test
|
|
6
|
-
*
|
|
4
|
+
* models.list -- List available models (optionally filtered by provider)
|
|
5
|
+
* models.test -- Check provider configuration and catalog status
|
|
6
|
+
* models.list_providers -- Live native pi-ai catalog provider list (Layer 1F)
|
|
7
|
+
* All handlers are read-only -- no approval gate required.
|
|
7
8
|
* @module
|
|
8
9
|
*/
|
|
9
10
|
import type { ModelCatalog } from "@comis/agent";
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Model management RPC handler module.
|
|
4
4
|
* Handles model catalog query methods:
|
|
5
|
-
* models.list
|
|
6
|
-
* models.test
|
|
7
|
-
*
|
|
5
|
+
* models.list -- List available models (optionally filtered by provider)
|
|
6
|
+
* models.test -- Check provider configuration and catalog status
|
|
7
|
+
* models.list_providers -- Live native pi-ai catalog provider list (Layer 1F)
|
|
8
|
+
* All handlers are read-only -- no approval gate required.
|
|
8
9
|
* @module
|
|
9
10
|
*/
|
|
11
|
+
import { getProviders } from "@mariozechner/pi-ai";
|
|
10
12
|
// ---------------------------------------------------------------------------
|
|
11
13
|
// Factory
|
|
12
14
|
// ---------------------------------------------------------------------------
|
|
@@ -62,6 +64,22 @@ export function createModelHandlers(deps) {
|
|
|
62
64
|
};
|
|
63
65
|
},
|
|
64
66
|
// -----------------------------------------------------------------------
|
|
67
|
+
// List native pi-ai catalog providers (Layer 1F -- 260430-vwt)
|
|
68
|
+
//
|
|
69
|
+
// Live self-discovery for the agent: returns the de-duplicated, sorted
|
|
70
|
+
// list of provider names from the pi-ai native catalog. Pairs with the
|
|
71
|
+
// tool-descriptions auto-promote guidance so the agent can verify
|
|
72
|
+
// which provider_id values trigger promotion in providers.create.
|
|
73
|
+
// -----------------------------------------------------------------------
|
|
74
|
+
"models.list_providers": async (params) => {
|
|
75
|
+
const trustLevel = params._trustLevel;
|
|
76
|
+
if (trustLevel !== "admin") {
|
|
77
|
+
throw new Error("Admin access required");
|
|
78
|
+
}
|
|
79
|
+
const providers = [...new Set(getProviders())].sort();
|
|
80
|
+
return { providers, count: providers.length };
|
|
81
|
+
},
|
|
82
|
+
// -----------------------------------------------------------------------
|
|
65
83
|
// Test provider configuration and availability
|
|
66
84
|
// -----------------------------------------------------------------------
|
|
67
85
|
"models.test": async (params) => {
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* @module
|
|
17
17
|
*/
|
|
18
18
|
import { ProviderEntrySchema } from "@comis/core";
|
|
19
|
+
import { getModels, getProviders } from "@mariozechner/pi-ai";
|
|
20
|
+
import { checkBuiltInProviderRedundancy } from "./builtin-provider-guard.js";
|
|
19
21
|
import { persistToConfig } from "./persist-to-config.js";
|
|
20
22
|
import { probeProviderAuth } from "./probe-provider-auth.js";
|
|
21
23
|
// ---------------------------------------------------------------------------
|
|
@@ -68,6 +70,48 @@ function formatReferenceMessage(refs) {
|
|
|
68
70
|
}
|
|
69
71
|
return parts.join("; ");
|
|
70
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Auto-promote a Partial<ProviderEntry> when the `providerId` matches a
|
|
75
|
+
* native pi-ai catalog entry AND the user has not expressed clear intent
|
|
76
|
+
* to deviate from the native shape.
|
|
77
|
+
*
|
|
78
|
+
* The agent's tool guide currently shows `type:"openai"` as the example
|
|
79
|
+
* for any OpenAI-compatible provider. Without promotion, registering
|
|
80
|
+
* "openrouter" by name with that example shape lands a `type:"openai"`
|
|
81
|
+
* entry that bypasses the OpenRouter native catalog (no costs, wrong
|
|
82
|
+
* context window, single explicit model).
|
|
83
|
+
*
|
|
84
|
+
* Promotion fires when:
|
|
85
|
+
* 1. providerId is in the live pi-ai native catalog, AND
|
|
86
|
+
* 2. config.type is missing or set to "openai" (the passthrough sentinel), AND
|
|
87
|
+
* 3. config.baseUrl is missing or matches the native catalog's baseUrl.
|
|
88
|
+
*
|
|
89
|
+
* Conservatism: a user pointing at a custom proxy whose URL differs from
|
|
90
|
+
* the native one keeps their explicit `type:"openai"` shape -- the URL
|
|
91
|
+
* mismatch is the opt-out signal.
|
|
92
|
+
*
|
|
93
|
+
* Logged at INFO with the original_type / promoted_type so operators can
|
|
94
|
+
* see which entries were rewritten on the way in.
|
|
95
|
+
*/
|
|
96
|
+
function normalizeProviderEntry(providerId, config, logger) {
|
|
97
|
+
const native = new Set(getProviders());
|
|
98
|
+
if (!native.has(providerId))
|
|
99
|
+
return config;
|
|
100
|
+
const catalog = getModels(providerId);
|
|
101
|
+
const nativeBaseUrl = catalog[0]?.baseUrl;
|
|
102
|
+
const isPassthroughType = !config.type || config.type === "openai";
|
|
103
|
+
const userBaseUrlMatchesNative = !config.baseUrl || config.baseUrl === nativeBaseUrl;
|
|
104
|
+
if (isPassthroughType && userBaseUrlMatchesNative) {
|
|
105
|
+
logger?.info({
|
|
106
|
+
providerId,
|
|
107
|
+
original_type: config.type,
|
|
108
|
+
promoted_type: providerId,
|
|
109
|
+
hint: "Auto-promoted to native pi-ai catalog provider",
|
|
110
|
+
}, "providers.create: type promoted to native");
|
|
111
|
+
return { ...config, type: providerId };
|
|
112
|
+
}
|
|
113
|
+
return config;
|
|
114
|
+
}
|
|
71
115
|
// ---------------------------------------------------------------------------
|
|
72
116
|
// Factory
|
|
73
117
|
// ---------------------------------------------------------------------------
|
|
@@ -152,7 +196,22 @@ export function createProviderHandlers(deps) {
|
|
|
152
196
|
throw new Error(`Provider already exists: ${providerId}`);
|
|
153
197
|
}
|
|
154
198
|
const config = params.config ?? {};
|
|
155
|
-
|
|
199
|
+
// 260501-gyy: reject redundant catalog-shadowing entries before
|
|
200
|
+
// promotion / probe / persist. A built-in provider with a catalog
|
|
201
|
+
// (or absent) baseUrl is structurally redundant -- pi-ai's dynamic
|
|
202
|
+
// catalog already provides its model list. Production trace
|
|
203
|
+
// 2026-05-01 08:53 showed an LLM agent creating
|
|
204
|
+
// providers.entries.openrouter with an invented model id, leading
|
|
205
|
+
// to a downstream 404.
|
|
206
|
+
const guardResult = checkBuiltInProviderRedundancy(providerId, config);
|
|
207
|
+
if (!guardResult.ok) {
|
|
208
|
+
throw new Error(guardResult.reason);
|
|
209
|
+
}
|
|
210
|
+
// Layer 1C (260430-vwt): auto-promote type to native catalog name
|
|
211
|
+
// when the providerId matches a pi-ai catalog entry AND the user has
|
|
212
|
+
// not opted out via a custom baseUrl.
|
|
213
|
+
const normalizedConfig = normalizeProviderEntry(providerId, config, deps.persistDeps?.logger);
|
|
214
|
+
const parsedConfig = ProviderEntrySchema.parse(normalizedConfig);
|
|
156
215
|
// Probe provider API key before committing config
|
|
157
216
|
if (parsedConfig.apiKeyName && deps.secretManager) {
|
|
158
217
|
const apiKey = deps.secretManager.get(parsedConfig.apiKeyName);
|
|
@@ -164,11 +223,15 @@ export function createProviderHandlers(deps) {
|
|
|
164
223
|
}
|
|
165
224
|
}
|
|
166
225
|
deps.providerEntries[providerId] = parsedConfig;
|
|
167
|
-
// Best-effort persistence to config.yaml
|
|
226
|
+
// Best-effort persistence to config.yaml. Persist the normalized
|
|
227
|
+
// config (post-Layer-1C promotion) so the YAML reflects the
|
|
228
|
+
// promoted type -- otherwise the daemon would re-promote on every
|
|
229
|
+
// restart, or worse, the persisted type:"openai" would override the
|
|
230
|
+
// runtime promoted type on subsequent loads.
|
|
168
231
|
if (deps.persistDeps) {
|
|
169
232
|
const ctx = params._context;
|
|
170
233
|
const persistResult = await persistToConfig(deps.persistDeps, {
|
|
171
|
-
patch: { providers: { entries: { [providerId]:
|
|
234
|
+
patch: { providers: { entries: { [providerId]: normalizedConfig } } },
|
|
172
235
|
actionType: "providers.create",
|
|
173
236
|
entityId: providerId,
|
|
174
237
|
actingUser: ctx?.userId ?? params._agentId,
|
|
@@ -197,12 +260,25 @@ export function createProviderHandlers(deps) {
|
|
|
197
260
|
// Capture user-provided fields BEFORE merge -- persistToConfig does deepMerge internally,
|
|
198
261
|
// so we only persist the user's partial patch (not the fully merged config).
|
|
199
262
|
const userPatch = params.config ? structuredClone(params.config) : {};
|
|
263
|
+
// Layer 1C (260430-vwt): on update, only auto-promote when the user
|
|
264
|
+
// is actively changing the `type` field. If `type` is absent from
|
|
265
|
+
// the patch, the user is editing other fields and we must not
|
|
266
|
+
// rewrite their existing type silently.
|
|
267
|
+
let normalizedPatch = config;
|
|
268
|
+
if (config.type !== undefined) {
|
|
269
|
+
normalizedPatch = normalizeProviderEntry(providerId, config, deps.persistDeps?.logger);
|
|
270
|
+
// Mirror the promotion into both the in-memory merge and the persisted patch
|
|
271
|
+
// so the YAML matches the runtime view.
|
|
272
|
+
if (normalizedPatch.type !== config.type) {
|
|
273
|
+
userPatch.type = normalizedPatch.type;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
200
276
|
// Headers: shallow merge per-key (preserve existing keys, overlay new ones)
|
|
201
|
-
if (
|
|
202
|
-
|
|
277
|
+
if (normalizedPatch.headers && existing.headers) {
|
|
278
|
+
normalizedPatch.headers = { ...existing.headers, ...normalizedPatch.headers };
|
|
203
279
|
}
|
|
204
280
|
// models[] and capabilities: replaced wholesale via spread (no merge needed)
|
|
205
|
-
const merged = { ...existing, ...
|
|
281
|
+
const merged = { ...existing, ...normalizedPatch };
|
|
206
282
|
const parsedConfig = ProviderEntrySchema.parse(merged);
|
|
207
283
|
deps.providerEntries[providerId] = parsedConfig;
|
|
208
284
|
// Best-effort persistence to config.yaml -- persist userPatch NOT merged config
|
|
@@ -77,6 +77,10 @@ export interface RpcDispatchDeps {
|
|
|
77
77
|
};
|
|
78
78
|
};
|
|
79
79
|
adaptersByType: Map<string, ChannelPort>;
|
|
80
|
+
/** Resolves daemon NormalizedMessage.id UUIDs back to platform-native
|
|
81
|
+
* message ids for message.delete/edit/react. Optional for backward compat
|
|
82
|
+
* with daemon configs that disable channel adapters entirely. */
|
|
83
|
+
inboundMessageIdResolver?: import("../wiring/inbound-message-id-resolver.js").InboundMessageIdResolver;
|
|
80
84
|
visionRegistry?: Map<string, VisionProvider>;
|
|
81
85
|
mediaConfig: {
|
|
82
86
|
imageAnalysis: {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves daemon-internal `NormalizedMessage.id` UUIDs to platform-native
|
|
3
|
+
* message ids (e.g. Telegram's integer `message_id`) for inbound messages.
|
|
4
|
+
*
|
|
5
|
+
* The agent's tools see `message_id` as the daemon UUID (from session
|
|
6
|
+
* preamble metadata), but channel adapters need the platform-native id when
|
|
7
|
+
* calling APIs like `bot.api.deleteMessage`. Without translation, those calls
|
|
8
|
+
* fail with `400: Bad Request: message identifier is not specified` (Telegram
|
|
9
|
+
* rejects `Number("e60f9634-...")` → `NaN`).
|
|
10
|
+
*
|
|
11
|
+
* Populated lazily on inbound via `record(msg, channelType)`. The native id
|
|
12
|
+
* is read from `msg.metadata[metaKey]` where metaKey comes from the
|
|
13
|
+
* per-channel `replyToMetaKey` capability. Backed by a TTL cache so stale
|
|
14
|
+
* entries don't accumulate.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import type { NormalizedMessage } from "@comis/core";
|
|
19
|
+
/** A single resolved inbound message record. */
|
|
20
|
+
export interface InboundIdRecord {
|
|
21
|
+
channelType: string;
|
|
22
|
+
channelId: string;
|
|
23
|
+
nativeId: string;
|
|
24
|
+
}
|
|
25
|
+
/** UUID → native message-id resolver for cross-channel inbound messages. */
|
|
26
|
+
export interface InboundMessageIdResolver {
|
|
27
|
+
/** Record an inbound message's UUID → native id mapping. No-op if the
|
|
28
|
+
* channel has no registered metadata key or the metadata is missing. */
|
|
29
|
+
record(msg: NormalizedMessage, channelType: string): void;
|
|
30
|
+
/** Look up native id by daemon UUID. Returns undefined if not found. */
|
|
31
|
+
resolve(uuid: string): InboundIdRecord | undefined;
|
|
32
|
+
}
|
|
33
|
+
/** Options for the resolver factory. */
|
|
34
|
+
export interface InboundMessageIdResolverOpts {
|
|
35
|
+
/** Per-channel metadata key carrying the native id, e.g. {"telegram": "telegramMessageId"}. */
|
|
36
|
+
metaKeyByChannel: Map<string, string>;
|
|
37
|
+
/** Time-to-live in ms for cached entries. Defaults to 1 h. */
|
|
38
|
+
ttlMs?: number;
|
|
39
|
+
/** Max entries before LRU eviction. Defaults to 10_000. */
|
|
40
|
+
maxEntries?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create an inbound-message-id resolver backed by a TTL cache.
|
|
44
|
+
*
|
|
45
|
+
* @param opts Configuration including the per-channel metadata key map.
|
|
46
|
+
* @returns A resolver that records on inbound and resolves UUIDs on lookup.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createInboundMessageIdResolver(opts: InboundMessageIdResolverOpts): InboundMessageIdResolver;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Resolves daemon-internal `NormalizedMessage.id` UUIDs to platform-native
|
|
4
|
+
* message ids (e.g. Telegram's integer `message_id`) for inbound messages.
|
|
5
|
+
*
|
|
6
|
+
* The agent's tools see `message_id` as the daemon UUID (from session
|
|
7
|
+
* preamble metadata), but channel adapters need the platform-native id when
|
|
8
|
+
* calling APIs like `bot.api.deleteMessage`. Without translation, those calls
|
|
9
|
+
* fail with `400: Bad Request: message identifier is not specified` (Telegram
|
|
10
|
+
* rejects `Number("e60f9634-...")` → `NaN`).
|
|
11
|
+
*
|
|
12
|
+
* Populated lazily on inbound via `record(msg, channelType)`. The native id
|
|
13
|
+
* is read from `msg.metadata[metaKey]` where metaKey comes from the
|
|
14
|
+
* per-channel `replyToMetaKey` capability. Backed by a TTL cache so stale
|
|
15
|
+
* entries don't accumulate.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { createTTLCache } from "@comis/shared";
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Factory
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Create an inbound-message-id resolver backed by a TTL cache.
|
|
25
|
+
*
|
|
26
|
+
* @param opts Configuration including the per-channel metadata key map.
|
|
27
|
+
* @returns A resolver that records on inbound and resolves UUIDs on lookup.
|
|
28
|
+
*/
|
|
29
|
+
export function createInboundMessageIdResolver(opts) {
|
|
30
|
+
const ttlMs = opts.ttlMs ?? 60 * 60 * 1000;
|
|
31
|
+
const maxEntries = opts.maxEntries ?? 10_000;
|
|
32
|
+
const cache = createTTLCache({ ttlMs, maxEntries });
|
|
33
|
+
return {
|
|
34
|
+
record(msg, channelType) {
|
|
35
|
+
const metaKey = opts.metaKeyByChannel.get(channelType);
|
|
36
|
+
if (!metaKey)
|
|
37
|
+
return;
|
|
38
|
+
const meta = msg.metadata;
|
|
39
|
+
// metaKey comes from our internal channelCapabilities map (set at adapter
|
|
40
|
+
// bootstrap, not user-controlled), so dynamic access is safe.
|
|
41
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
42
|
+
const raw = meta?.[metaKey];
|
|
43
|
+
if (raw == null)
|
|
44
|
+
return;
|
|
45
|
+
const nativeId = typeof raw === "number"
|
|
46
|
+
? String(raw)
|
|
47
|
+
: typeof raw === "string"
|
|
48
|
+
? raw
|
|
49
|
+
: null;
|
|
50
|
+
if (nativeId == null || nativeId.length === 0)
|
|
51
|
+
return;
|
|
52
|
+
cache.set(msg.id, { channelType, channelId: msg.channelId, nativeId });
|
|
53
|
+
},
|
|
54
|
+
resolve(uuid) {
|
|
55
|
+
return cache.get(uuid);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -16,6 +16,16 @@ export interface ContinuationRecord {
|
|
|
16
16
|
peerId?: string;
|
|
17
17
|
guildId?: string;
|
|
18
18
|
threadId?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Channel-native chat type tag captured at track-time so the synthetic
|
|
21
|
+
* restart message can frame the resumed conversation correctly.
|
|
22
|
+
*
|
|
23
|
+
* For Telegram: `"private"`, `"group"`, `"supergroup"`, or `"channel"`
|
|
24
|
+
* (sourced from `metadata.telegramChatType`). Without this, group sessions
|
|
25
|
+
* are mis-framed as DMs on first turn after restart because the synthetic
|
|
26
|
+
* inbound carries no chat-type metadata.
|
|
27
|
+
*/
|
|
28
|
+
chatType?: string;
|
|
19
29
|
tenantId: string;
|
|
20
30
|
timestamp: number;
|
|
21
31
|
}
|