profclaw 0.0.1-beta.1
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/.env.example +215 -0
- package/CHANGELOG.md +49 -0
- package/CONTRIBUTING.md +119 -0
- package/Dockerfile +107 -0
- package/LICENSE +661 -0
- package/README.md +196 -0
- package/config/agents.yml +55 -0
- package/config/budget.yml +15 -0
- package/config/pricing.yml +46 -0
- package/config/settings.yml +168 -0
- package/dist/adapters/claude-code.d.ts +46 -0
- package/dist/adapters/claude-code.js +215 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.js +4 -0
- package/dist/adapters/ollama.d.ts +45 -0
- package/dist/adapters/ollama.js +277 -0
- package/dist/adapters/openclaw.d.ts +60 -0
- package/dist/adapters/openclaw.js +289 -0
- package/dist/adapters/registry.d.ts +56 -0
- package/dist/adapters/registry.js +163 -0
- package/dist/agents/executor.d.ts +78 -0
- package/dist/agents/executor.js +657 -0
- package/dist/agents/index.d.ts +10 -0
- package/dist/agents/index.js +27 -0
- package/dist/agents/stop-conditions.d.ts +108 -0
- package/dist/agents/stop-conditions.js +299 -0
- package/dist/agents/types.d.ts +185 -0
- package/dist/agents/types.js +14 -0
- package/dist/ai/embedding-service.d.ts +96 -0
- package/dist/ai/embedding-service.js +333 -0
- package/dist/auth/api-tokens.d.ts +71 -0
- package/dist/auth/api-tokens.js +235 -0
- package/dist/auth/auth-service.d.ts +75 -0
- package/dist/auth/auth-service.js +557 -0
- package/dist/auth/device-identity.d.ts +96 -0
- package/dist/auth/device-identity.js +250 -0
- package/dist/auth/github-oauth.d.ts +20 -0
- package/dist/auth/github-oauth.js +59 -0
- package/dist/auth/jira-oauth.d.ts +25 -0
- package/dist/auth/jira-oauth.js +75 -0
- package/dist/auth/linear-oauth.d.ts +21 -0
- package/dist/auth/linear-oauth.js +59 -0
- package/dist/auth/middleware.d.ts +44 -0
- package/dist/auth/middleware.js +190 -0
- package/dist/auth/pairing-codes.d.ts +109 -0
- package/dist/auth/pairing-codes.js +274 -0
- package/dist/auth/password.d.ts +53 -0
- package/dist/auth/password.js +126 -0
- package/dist/auth/qr-pairing.d.ts +36 -0
- package/dist/auth/qr-pairing.js +138 -0
- package/dist/auth/slack-oauth.d.ts +31 -0
- package/dist/auth/slack-oauth.js +80 -0
- package/dist/backup/index.d.ts +50 -0
- package/dist/backup/index.js +184 -0
- package/dist/browser/adapters.d.ts +25 -0
- package/dist/browser/adapters.js +32 -0
- package/dist/browser/cli.d.ts +19 -0
- package/dist/browser/cli.js +277 -0
- package/dist/browser/compression.d.ts +32 -0
- package/dist/browser/compression.js +269 -0
- package/dist/browser/extensions.d.ts +144 -0
- package/dist/browser/extensions.js +251 -0
- package/dist/browser/index.d.ts +37 -0
- package/dist/browser/index.js +43 -0
- package/dist/browser/refs.d.ts +46 -0
- package/dist/browser/refs.js +120 -0
- package/dist/browser/search.d.ts +36 -0
- package/dist/browser/search.js +193 -0
- package/dist/browser/service.d.ts +94 -0
- package/dist/browser/service.js +358 -0
- package/dist/browser/snapshot.d.ts +19 -0
- package/dist/browser/snapshot.js +250 -0
- package/dist/browser/tools.d.ts +259 -0
- package/dist/browser/tools.js +463 -0
- package/dist/browser/types.d.ts +134 -0
- package/dist/browser/types.js +17 -0
- package/dist/chat/agentic-executor.d.ts +108 -0
- package/dist/chat/agentic-executor.js +739 -0
- package/dist/chat/api-key-detector.d.ts +41 -0
- package/dist/chat/api-key-detector.js +290 -0
- package/dist/chat/auto-reply.d.ts +90 -0
- package/dist/chat/auto-reply.js +272 -0
- package/dist/chat/conversations.d.ts +91 -0
- package/dist/chat/conversations.js +297 -0
- package/dist/chat/execution/audit.d.ts +192 -0
- package/dist/chat/execution/audit.js +323 -0
- package/dist/chat/execution/executor.d.ts +56 -0
- package/dist/chat/execution/executor.js +654 -0
- package/dist/chat/execution/guardrails.d.ts +98 -0
- package/dist/chat/execution/guardrails.js +571 -0
- package/dist/chat/execution/index.d.ts +61 -0
- package/dist/chat/execution/index.js +129 -0
- package/dist/chat/execution/model-capability.d.ts +34 -0
- package/dist/chat/execution/model-capability.js +96 -0
- package/dist/chat/execution/process-pool.d.ts +110 -0
- package/dist/chat/execution/process-pool.js +290 -0
- package/dist/chat/execution/pty.d.ts +92 -0
- package/dist/chat/execution/pty.js +285 -0
- package/dist/chat/execution/rate-limiter.d.ts +87 -0
- package/dist/chat/execution/rate-limiter.js +305 -0
- package/dist/chat/execution/registry.d.ts +54 -0
- package/dist/chat/execution/registry.js +220 -0
- package/dist/chat/execution/sandbox.d.ts +109 -0
- package/dist/chat/execution/sandbox.js +684 -0
- package/dist/chat/execution/secrets.d.ts +45 -0
- package/dist/chat/execution/secrets.js +338 -0
- package/dist/chat/execution/security.d.ts +179 -0
- package/dist/chat/execution/security.js +739 -0
- package/dist/chat/execution/self-correction.d.ts +162 -0
- package/dist/chat/execution/self-correction.js +557 -0
- package/dist/chat/execution/session-manager.d.ts +79 -0
- package/dist/chat/execution/session-manager.js +356 -0
- package/dist/chat/execution/session-spawn/config.d.ts +37 -0
- package/dist/chat/execution/session-spawn/config.js +83 -0
- package/dist/chat/execution/session-spawn/index.d.ts +10 -0
- package/dist/chat/execution/session-spawn/index.js +10 -0
- package/dist/chat/execution/session-spawn/manager.d.ts +35 -0
- package/dist/chat/execution/session-spawn/manager.js +418 -0
- package/dist/chat/execution/session-spawn/types.d.ts +117 -0
- package/dist/chat/execution/session-spawn/types.js +16 -0
- package/dist/chat/execution/smart-prompts.d.ts +34 -0
- package/dist/chat/execution/smart-prompts.js +151 -0
- package/dist/chat/execution/stream-results.d.ts +65 -0
- package/dist/chat/execution/stream-results.js +156 -0
- package/dist/chat/execution/tool-router.d.ts +76 -0
- package/dist/chat/execution/tool-router.js +296 -0
- package/dist/chat/execution/tools/agents-list.d.ts +49 -0
- package/dist/chat/execution/tools/agents-list.js +165 -0
- package/dist/chat/execution/tools/browser.d.ts +12 -0
- package/dist/chat/execution/tools/browser.js +14 -0
- package/dist/chat/execution/tools/canvas.d.ts +61 -0
- package/dist/chat/execution/tools/canvas.js +150 -0
- package/dist/chat/execution/tools/complete-task.d.ts +72 -0
- package/dist/chat/execution/tools/complete-task.js +104 -0
- package/dist/chat/execution/tools/cron-tool.d.ts +204 -0
- package/dist/chat/execution/tools/cron-tool.js +680 -0
- package/dist/chat/execution/tools/discord-actions.d.ts +120 -0
- package/dist/chat/execution/tools/discord-actions.js +361 -0
- package/dist/chat/execution/tools/exec.d.ts +43 -0
- package/dist/chat/execution/tools/exec.js +223 -0
- package/dist/chat/execution/tools/file-ops.d.ts +175 -0
- package/dist/chat/execution/tools/file-ops.js +723 -0
- package/dist/chat/execution/tools/git.d.ts +143 -0
- package/dist/chat/execution/tools/git.js +395 -0
- package/dist/chat/execution/tools/github.d.ts +42 -0
- package/dist/chat/execution/tools/github.js +184 -0
- package/dist/chat/execution/tools/glinr-ops.d.ts +202 -0
- package/dist/chat/execution/tools/glinr-ops.js +694 -0
- package/dist/chat/execution/tools/image-analyze.d.ts +29 -0
- package/dist/chat/execution/tools/image-analyze.js +127 -0
- package/dist/chat/execution/tools/index.d.ts +64 -0
- package/dist/chat/execution/tools/index.js +245 -0
- package/dist/chat/execution/tools/integrations.d.ts +109 -0
- package/dist/chat/execution/tools/integrations.js +237 -0
- package/dist/chat/execution/tools/link-understand.d.ts +34 -0
- package/dist/chat/execution/tools/link-understand.js +307 -0
- package/dist/chat/execution/tools/maintenance.d.ts +29 -0
- package/dist/chat/execution/tools/maintenance.js +105 -0
- package/dist/chat/execution/tools/memory-tools.d.ts +76 -0
- package/dist/chat/execution/tools/memory-tools.js +278 -0
- package/dist/chat/execution/tools/openai-image-gen.d.ts +51 -0
- package/dist/chat/execution/tools/openai-image-gen.js +212 -0
- package/dist/chat/execution/tools/profclaw-ops.d.ts +204 -0
- package/dist/chat/execution/tools/profclaw-ops.js +665 -0
- package/dist/chat/execution/tools/session-spawn.d.ts +146 -0
- package/dist/chat/execution/tools/session-spawn.js +544 -0
- package/dist/chat/execution/tools/session-status.d.ts +38 -0
- package/dist/chat/execution/tools/session-status.js +185 -0
- package/dist/chat/execution/tools/sessions-list.d.ts +49 -0
- package/dist/chat/execution/tools/sessions-list.js +153 -0
- package/dist/chat/execution/tools/sessions-send.d.ts +48 -0
- package/dist/chat/execution/tools/sessions-send.js +207 -0
- package/dist/chat/execution/tools/sessions-spawn.d.ts +51 -0
- package/dist/chat/execution/tools/sessions-spawn.js +137 -0
- package/dist/chat/execution/tools/slack-actions.d.ts +90 -0
- package/dist/chat/execution/tools/slack-actions.js +272 -0
- package/dist/chat/execution/tools/subagent-orchestrate.d.ts +66 -0
- package/dist/chat/execution/tools/subagent-orchestrate.js +313 -0
- package/dist/chat/execution/tools/system.d.ts +121 -0
- package/dist/chat/execution/tools/system.js +415 -0
- package/dist/chat/execution/tools/telegram-actions.d.ts +126 -0
- package/dist/chat/execution/tools/telegram-actions.js +339 -0
- package/dist/chat/execution/tools/test-run.d.ts +43 -0
- package/dist/chat/execution/tools/test-run.js +391 -0
- package/dist/chat/execution/tools/tts-speak.d.ts +39 -0
- package/dist/chat/execution/tools/tts-speak.js +199 -0
- package/dist/chat/execution/tools/web-fetch.d.ts +42 -0
- package/dist/chat/execution/tools/web-fetch.js +247 -0
- package/dist/chat/execution/tools/web-search.d.ts +53 -0
- package/dist/chat/execution/tools/web-search.js +130 -0
- package/dist/chat/execution/types.d.ts +337 -0
- package/dist/chat/execution/types.js +8 -0
- package/dist/chat/execution/workflows/built-in.d.ts +10 -0
- package/dist/chat/execution/workflows/built-in.js +249 -0
- package/dist/chat/execution/workflows/executor.d.ts +13 -0
- package/dist/chat/execution/workflows/executor.js +259 -0
- package/dist/chat/execution/workflows/index.d.ts +5 -0
- package/dist/chat/execution/workflows/index.js +5 -0
- package/dist/chat/execution/workflows/trigger.d.ts +15 -0
- package/dist/chat/execution/workflows/trigger.js +111 -0
- package/dist/chat/execution/workflows/types.d.ts +80 -0
- package/dist/chat/execution/workflows/types.js +2 -0
- package/dist/chat/failover/error.d.ts +73 -0
- package/dist/chat/failover/error.js +412 -0
- package/dist/chat/failover/index.d.ts +10 -0
- package/dist/chat/failover/index.js +11 -0
- package/dist/chat/failover/model-fallback.d.ts +97 -0
- package/dist/chat/failover/model-fallback.js +328 -0
- package/dist/chat/failover/types.d.ts +47 -0
- package/dist/chat/failover/types.js +8 -0
- package/dist/chat/format/chunk.d.ts +59 -0
- package/dist/chat/format/chunk.js +253 -0
- package/dist/chat/format/index.d.ts +7 -0
- package/dist/chat/format/index.js +7 -0
- package/dist/chat/format/tool-result-formatter.d.ts +30 -0
- package/dist/chat/format/tool-result-formatter.js +177 -0
- package/dist/chat/group.d.ts +232 -0
- package/dist/chat/group.js +311 -0
- package/dist/chat/index.d.ts +21 -0
- package/dist/chat/index.js +30 -0
- package/dist/chat/memory.d.ts +108 -0
- package/dist/chat/memory.js +343 -0
- package/dist/chat/message-handler.d.ts +38 -0
- package/dist/chat/message-handler.js +274 -0
- package/dist/chat/proactive/index.d.ts +167 -0
- package/dist/chat/proactive/index.js +721 -0
- package/dist/chat/prompt-adapter.d.ts +52 -0
- package/dist/chat/prompt-adapter.js +245 -0
- package/dist/chat/providers/dingtalk/index.d.ts +69 -0
- package/dist/chat/providers/dingtalk/index.js +225 -0
- package/dist/chat/providers/discord/index.d.ts +264 -0
- package/dist/chat/providers/discord/index.js +657 -0
- package/dist/chat/providers/feishu/index.d.ts +97 -0
- package/dist/chat/providers/feishu/index.js +362 -0
- package/dist/chat/providers/googlechat/index.d.ts +269 -0
- package/dist/chat/providers/googlechat/index.js +634 -0
- package/dist/chat/providers/imessage/index.d.ts +61 -0
- package/dist/chat/providers/imessage/index.js +166 -0
- package/dist/chat/providers/index.d.ts +76 -0
- package/dist/chat/providers/index.js +430 -0
- package/dist/chat/providers/irc/index.d.ts +101 -0
- package/dist/chat/providers/irc/index.js +557 -0
- package/dist/chat/providers/line/index.d.ts +113 -0
- package/dist/chat/providers/line/index.js +434 -0
- package/dist/chat/providers/matrix/index.d.ts +161 -0
- package/dist/chat/providers/matrix/index.js +559 -0
- package/dist/chat/providers/mattermost/index.d.ts +106 -0
- package/dist/chat/providers/mattermost/index.js +425 -0
- package/dist/chat/providers/msteams/index.d.ts +198 -0
- package/dist/chat/providers/msteams/index.js +694 -0
- package/dist/chat/providers/nextcloud/index.d.ts +52 -0
- package/dist/chat/providers/nextcloud/index.js +156 -0
- package/dist/chat/providers/nostr/index.d.ts +60 -0
- package/dist/chat/providers/nostr/index.js +200 -0
- package/dist/chat/providers/qq/index.d.ts +57 -0
- package/dist/chat/providers/qq/index.js +260 -0
- package/dist/chat/providers/registry.d.ts +89 -0
- package/dist/chat/providers/registry.js +213 -0
- package/dist/chat/providers/signal/index.d.ts +88 -0
- package/dist/chat/providers/signal/index.js +357 -0
- package/dist/chat/providers/slack/index.d.ts +52 -0
- package/dist/chat/providers/slack/index.js +477 -0
- package/dist/chat/providers/synology/index.d.ts +50 -0
- package/dist/chat/providers/synology/index.js +138 -0
- package/dist/chat/providers/telegram/index.d.ts +148 -0
- package/dist/chat/providers/telegram/index.js +492 -0
- package/dist/chat/providers/tlon/index.d.ts +44 -0
- package/dist/chat/providers/tlon/index.js +243 -0
- package/dist/chat/providers/twitch/index.d.ts +79 -0
- package/dist/chat/providers/twitch/index.js +349 -0
- package/dist/chat/providers/types.d.ts +437 -0
- package/dist/chat/providers/types.js +14 -0
- package/dist/chat/providers/webchat/index.d.ts +38 -0
- package/dist/chat/providers/webchat/index.js +214 -0
- package/dist/chat/providers/wecom/index.d.ts +62 -0
- package/dist/chat/providers/wecom/index.js +228 -0
- package/dist/chat/providers/whatsapp/index.d.ts +147 -0
- package/dist/chat/providers/whatsapp/index.js +396 -0
- package/dist/chat/providers/zalo/index.d.ts +54 -0
- package/dist/chat/providers/zalo/index.js +158 -0
- package/dist/chat/providers/zalo-personal/index.d.ts +53 -0
- package/dist/chat/providers/zalo-personal/index.js +212 -0
- package/dist/chat/skills.d.ts +82 -0
- package/dist/chat/skills.js +410 -0
- package/dist/chat/system-prompts.d.ts +76 -0
- package/dist/chat/system-prompts.js +407 -0
- package/dist/chat/tool-handler.d.ts +57 -0
- package/dist/chat/tool-handler.js +336 -0
- package/dist/chat/tools.d.ts +373 -0
- package/dist/chat/tools.js +512 -0
- package/dist/cli/commands/agent.d.ts +3 -0
- package/dist/cli/commands/agent.js +108 -0
- package/dist/cli/commands/auth.d.ts +12 -0
- package/dist/cli/commands/auth.js +301 -0
- package/dist/cli/commands/browser.d.ts +3 -0
- package/dist/cli/commands/browser.js +138 -0
- package/dist/cli/commands/canvas.d.ts +3 -0
- package/dist/cli/commands/canvas.js +155 -0
- package/dist/cli/commands/channels.d.ts +3 -0
- package/dist/cli/commands/channels.js +167 -0
- package/dist/cli/commands/chat.d.ts +9 -0
- package/dist/cli/commands/chat.js +360 -0
- package/dist/cli/commands/completion.d.ts +3 -0
- package/dist/cli/commands/completion.js +170 -0
- package/dist/cli/commands/config.d.ts +3 -0
- package/dist/cli/commands/config.js +193 -0
- package/dist/cli/commands/cost.d.ts +3 -0
- package/dist/cli/commands/cost.js +134 -0
- package/dist/cli/commands/daemon.d.ts +3 -0
- package/dist/cli/commands/daemon.js +381 -0
- package/dist/cli/commands/devices.d.ts +3 -0
- package/dist/cli/commands/devices.js +150 -0
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.js +295 -0
- package/dist/cli/commands/logs.d.ts +3 -0
- package/dist/cli/commands/logs.js +139 -0
- package/dist/cli/commands/mcp.d.ts +15 -0
- package/dist/cli/commands/mcp.js +157 -0
- package/dist/cli/commands/memory.d.ts +3 -0
- package/dist/cli/commands/memory.js +170 -0
- package/dist/cli/commands/models.d.ts +3 -0
- package/dist/cli/commands/models.js +197 -0
- package/dist/cli/commands/nodes.d.ts +3 -0
- package/dist/cli/commands/nodes.js +193 -0
- package/dist/cli/commands/onboard.d.ts +13 -0
- package/dist/cli/commands/onboard.js +360 -0
- package/dist/cli/commands/plugin.d.ts +15 -0
- package/dist/cli/commands/plugin.js +226 -0
- package/dist/cli/commands/provider.d.ts +16 -0
- package/dist/cli/commands/provider.js +320 -0
- package/dist/cli/commands/security.d.ts +3 -0
- package/dist/cli/commands/security.js +180 -0
- package/dist/cli/commands/serve.d.ts +3 -0
- package/dist/cli/commands/serve.js +179 -0
- package/dist/cli/commands/session.d.ts +3 -0
- package/dist/cli/commands/session.js +172 -0
- package/dist/cli/commands/setup.d.ts +13 -0
- package/dist/cli/commands/setup.js +636 -0
- package/dist/cli/commands/skill.d.ts +15 -0
- package/dist/cli/commands/skill.js +191 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.js +71 -0
- package/dist/cli/commands/summary.d.ts +3 -0
- package/dist/cli/commands/summary.js +160 -0
- package/dist/cli/commands/task.d.ts +3 -0
- package/dist/cli/commands/task.js +170 -0
- package/dist/cli/commands/ticket.d.ts +3 -0
- package/dist/cli/commands/ticket.js +421 -0
- package/dist/cli/commands/tools.d.ts +9 -0
- package/dist/cli/commands/tools.js +508 -0
- package/dist/cli/commands/tui.d.ts +3 -0
- package/dist/cli/commands/tui.js +158 -0
- package/dist/cli/commands/tunnel.d.ts +3 -0
- package/dist/cli/commands/tunnel.js +135 -0
- package/dist/cli/commands/webhooks.d.ts +3 -0
- package/dist/cli/commands/webhooks.js +191 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +127 -0
- package/dist/cli/utils/api.d.ts +17 -0
- package/dist/cli/utils/api.js +63 -0
- package/dist/cli/utils/config.d.ts +31 -0
- package/dist/cli/utils/config.js +81 -0
- package/dist/cli/utils/output.d.ts +58 -0
- package/dist/cli/utils/output.js +148 -0
- package/dist/core/deployment.d.ts +30 -0
- package/dist/core/deployment.js +114 -0
- package/dist/core/sandbox-config.d.ts +63 -0
- package/dist/core/sandbox-config.js +148 -0
- package/dist/costs/budget.d.ts +28 -0
- package/dist/costs/budget.js +57 -0
- package/dist/costs/persistence.d.ts +77 -0
- package/dist/costs/persistence.js +210 -0
- package/dist/costs/pricing.d.ts +16 -0
- package/dist/costs/pricing.js +39 -0
- package/dist/costs/token-tracker.d.ts +34 -0
- package/dist/costs/token-tracker.js +141 -0
- package/dist/cron/heartbeat.d.ts +23 -0
- package/dist/cron/heartbeat.js +109 -0
- package/dist/cron/index.d.ts +22 -0
- package/dist/cron/index.js +37 -0
- package/dist/cron/issue-poller.d.ts +15 -0
- package/dist/cron/issue-poller.js +174 -0
- package/dist/cron/scheduler.d.ts +315 -0
- package/dist/cron/scheduler.js +1008 -0
- package/dist/cron/stale-checker.d.ts +16 -0
- package/dist/cron/stale-checker.js +61 -0
- package/dist/cron/templates.d.ts +107 -0
- package/dist/cron/templates.js +395 -0
- package/dist/diagnostics/exporters.d.ts +21 -0
- package/dist/diagnostics/exporters.js +138 -0
- package/dist/diagnostics/index.d.ts +10 -0
- package/dist/diagnostics/index.js +9 -0
- package/dist/diagnostics/middleware.d.ts +12 -0
- package/dist/diagnostics/middleware.js +58 -0
- package/dist/diagnostics/tracer.d.ts +133 -0
- package/dist/diagnostics/tracer.js +257 -0
- package/dist/discovery/index.d.ts +8 -0
- package/dist/discovery/index.js +7 -0
- package/dist/discovery/mdns.d.ts +40 -0
- package/dist/discovery/mdns.js +249 -0
- package/dist/gateway/index.d.ts +35 -0
- package/dist/gateway/index.js +36 -0
- package/dist/gateway/router.d.ts +110 -0
- package/dist/gateway/router.js +649 -0
- package/dist/gateway/types.d.ts +275 -0
- package/dist/gateway/types.js +2 -0
- package/dist/gateway/workflows.d.ts +37 -0
- package/dist/gateway/workflows.js +748 -0
- package/dist/hooks/agent-webhook.d.ts +277 -0
- package/dist/hooks/agent-webhook.js +197 -0
- package/dist/hooks/index.d.ts +13 -0
- package/dist/hooks/index.js +15 -0
- package/dist/hooks/prompt-submit.d.ts +45 -0
- package/dist/hooks/prompt-submit.js +111 -0
- package/dist/hooks/schemas.d.ts +563 -0
- package/dist/hooks/schemas.js +108 -0
- package/dist/hooks/session-end.d.ts +25 -0
- package/dist/hooks/session-end.js +153 -0
- package/dist/hooks/tool-use.d.ts +41 -0
- package/dist/hooks/tool-use.js +201 -0
- package/dist/hooks/types.d.ts +76 -0
- package/dist/hooks/types.js +7 -0
- package/dist/integrations/cloudflare-tunnel.d.ts +78 -0
- package/dist/integrations/cloudflare-tunnel.js +268 -0
- package/dist/integrations/github-client.d.ts +27 -0
- package/dist/integrations/github-client.js +79 -0
- package/dist/integrations/github-projects.d.ts +97 -0
- package/dist/integrations/github-projects.js +433 -0
- package/dist/integrations/github-ticket-sync.d.ts +82 -0
- package/dist/integrations/github-ticket-sync.js +352 -0
- package/dist/integrations/github.d.ts +11 -0
- package/dist/integrations/github.js +241 -0
- package/dist/integrations/jira-client.d.ts +30 -0
- package/dist/integrations/jira-client.js +67 -0
- package/dist/integrations/jira.d.ts +13 -0
- package/dist/integrations/jira.js +108 -0
- package/dist/integrations/linear-client.d.ts +23 -0
- package/dist/integrations/linear-client.js +52 -0
- package/dist/integrations/linear.d.ts +15 -0
- package/dist/integrations/linear.js +312 -0
- package/dist/integrations/tailscale.d.ts +68 -0
- package/dist/integrations/tailscale.js +173 -0
- package/dist/integrations/web-search.d.ts +112 -0
- package/dist/integrations/web-search.js +287 -0
- package/dist/intelligence/index.d.ts +8 -0
- package/dist/intelligence/index.js +8 -0
- package/dist/intelligence/ollama.d.ts +50 -0
- package/dist/intelligence/ollama.js +213 -0
- package/dist/intelligence/rules.d.ts +39 -0
- package/dist/intelligence/rules.js +308 -0
- package/dist/labels/index.d.ts +93 -0
- package/dist/labels/index.js +248 -0
- package/dist/mcp/browser-tools.d.ts +10 -0
- package/dist/mcp/browser-tools.js +16 -0
- package/dist/mcp/client.d.ts +54 -0
- package/dist/mcp/client.js +153 -0
- package/dist/mcp/index.d.ts +12 -0
- package/dist/mcp/index.js +13 -0
- package/dist/mcp/server.d.ts +40 -0
- package/dist/mcp/server.js +826 -0
- package/dist/mcp/tool-adapter.d.ts +49 -0
- package/dist/mcp/tool-adapter.js +193 -0
- package/dist/memory/experience-store.d.ts +121 -0
- package/dist/memory/experience-store.js +439 -0
- package/dist/memory/index.d.ts +8 -0
- package/dist/memory/index.js +25 -0
- package/dist/memory/memory-service.d.ts +312 -0
- package/dist/memory/memory-service.js +1172 -0
- package/dist/memory/memory-watcher.d.ts +116 -0
- package/dist/memory/memory-watcher.js +239 -0
- package/dist/middleware/logging.d.ts +34 -0
- package/dist/middleware/logging.js +184 -0
- package/dist/middleware/rate-limit.d.ts +29 -0
- package/dist/middleware/rate-limit.js +91 -0
- package/dist/notifications/in-app.d.ts +28 -0
- package/dist/notifications/in-app.js +53 -0
- package/dist/notifications/index.d.ts +3 -0
- package/dist/notifications/index.js +3 -0
- package/dist/notifications/push.d.ts +74 -0
- package/dist/notifications/push.js +175 -0
- package/dist/notifications/slack.d.ts +32 -0
- package/dist/notifications/slack.js +157 -0
- package/dist/plugins/clawhub.d.ts +70 -0
- package/dist/plugins/clawhub.js +260 -0
- package/dist/plugins/loader.d.ts +23 -0
- package/dist/plugins/loader.js +140 -0
- package/dist/plugins/marketplace.d.ts +77 -0
- package/dist/plugins/marketplace.js +275 -0
- package/dist/plugins/registry.d.ts +122 -0
- package/dist/plugins/registry.js +352 -0
- package/dist/plugins/sandbox.d.ts +77 -0
- package/dist/plugins/sandbox.js +287 -0
- package/dist/plugins/scaffolder.d.ts +28 -0
- package/dist/plugins/scaffolder.js +409 -0
- package/dist/plugins/sdk.d.ts +35 -0
- package/dist/plugins/sdk.js +8 -0
- package/dist/plugins/search/brave.d.ts +9 -0
- package/dist/plugins/search/brave.js +157 -0
- package/dist/plugins/search/duckduckgo.d.ts +9 -0
- package/dist/plugins/search/duckduckgo.js +171 -0
- package/dist/plugins/search/index.d.ts +13 -0
- package/dist/plugins/search/index.js +23 -0
- package/dist/plugins/search/searxng.d.ts +9 -0
- package/dist/plugins/search/searxng.js +170 -0
- package/dist/plugins/search/serper.d.ts +9 -0
- package/dist/plugins/search/serper.js +135 -0
- package/dist/plugins/search/tavily.d.ts +9 -0
- package/dist/plugins/search/tavily.js +146 -0
- package/dist/plugins/types.d.ts +174 -0
- package/dist/plugins/types.js +8 -0
- package/dist/projects/index.d.ts +133 -0
- package/dist/projects/index.js +897 -0
- package/dist/projects/types.d.ts +444 -0
- package/dist/projects/types.js +207 -0
- package/dist/providers/adapters/anthropic.d.ts +26 -0
- package/dist/providers/adapters/anthropic.js +306 -0
- package/dist/providers/adapters/base.d.ts +53 -0
- package/dist/providers/adapters/base.js +99 -0
- package/dist/providers/adapters/ollama.d.ts +30 -0
- package/dist/providers/adapters/ollama.js +292 -0
- package/dist/providers/ai-sdk.d.ts +178 -0
- package/dist/providers/ai-sdk.js +1552 -0
- package/dist/providers/core/index.d.ts +8 -0
- package/dist/providers/core/index.js +10 -0
- package/dist/providers/core/models.d.ts +31 -0
- package/dist/providers/core/models.js +949 -0
- package/dist/providers/core/types.d.ts +117 -0
- package/dist/providers/core/types.js +83 -0
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.js +7 -0
- package/dist/providers/registry.d.ts +93 -0
- package/dist/providers/registry.js +228 -0
- package/dist/providers/schema-utils.d.ts +23 -0
- package/dist/providers/schema-utils.js +545 -0
- package/dist/providers/types.d.ts +609 -0
- package/dist/providers/types.js +279 -0
- package/dist/queue/failure-handler.d.ts +93 -0
- package/dist/queue/failure-handler.js +379 -0
- package/dist/queue/index.d.ts +23 -0
- package/dist/queue/index.js +86 -0
- package/dist/queue/memory-queue.d.ts +30 -0
- package/dist/queue/memory-queue.js +429 -0
- package/dist/queue/notifications.d.ts +6 -0
- package/dist/queue/notifications.js +128 -0
- package/dist/queue/task-queue.d.ts +55 -0
- package/dist/queue/task-queue.js +518 -0
- package/dist/queue/webhook-queue.d.ts +47 -0
- package/dist/queue/webhook-queue.js +158 -0
- package/dist/routes/agents.d.ts +4 -0
- package/dist/routes/agents.js +45 -0
- package/dist/routes/auth.d.ts +19 -0
- package/dist/routes/auth.js +492 -0
- package/dist/routes/backup.d.ts +6 -0
- package/dist/routes/backup.js +71 -0
- package/dist/routes/chat.d.ts +10 -0
- package/dist/routes/chat.js +1678 -0
- package/dist/routes/clawhub.d.ts +8 -0
- package/dist/routes/clawhub.js +89 -0
- package/dist/routes/costs.d.ts +4 -0
- package/dist/routes/costs.js +78 -0
- package/dist/routes/cron.d.ts +11 -0
- package/dist/routes/cron.js +708 -0
- package/dist/routes/devices.d.ts +9 -0
- package/dist/routes/devices.js +490 -0
- package/dist/routes/discord.d.ts +15 -0
- package/dist/routes/discord.js +524 -0
- package/dist/routes/dlq.d.ts +4 -0
- package/dist/routes/dlq.js +64 -0
- package/dist/routes/gateway.d.ts +4 -0
- package/dist/routes/gateway.js +253 -0
- package/dist/routes/health.d.ts +29 -0
- package/dist/routes/health.js +296 -0
- package/dist/routes/hooks.d.ts +4 -0
- package/dist/routes/hooks.js +170 -0
- package/dist/routes/import.d.ts +9 -0
- package/dist/routes/import.js +540 -0
- package/dist/routes/index.d.ts +38 -0
- package/dist/routes/index.js +39 -0
- package/dist/routes/integrations.d.ts +10 -0
- package/dist/routes/integrations.js +200 -0
- package/dist/routes/labels.d.ts +8 -0
- package/dist/routes/labels.js +240 -0
- package/dist/routes/mcp.d.ts +14 -0
- package/dist/routes/mcp.js +125 -0
- package/dist/routes/memory.d.ts +10 -0
- package/dist/routes/memory.js +622 -0
- package/dist/routes/notifications.d.ts +9 -0
- package/dist/routes/notifications.js +154 -0
- package/dist/routes/oobe.d.ts +10 -0
- package/dist/routes/oobe.js +405 -0
- package/dist/routes/openapi.d.ts +9 -0
- package/dist/routes/openapi.js +537 -0
- package/dist/routes/plugins.d.ts +8 -0
- package/dist/routes/plugins.js +108 -0
- package/dist/routes/projects.d.ts +8 -0
- package/dist/routes/projects.js +592 -0
- package/dist/routes/push.d.ts +8 -0
- package/dist/routes/push.js +75 -0
- package/dist/routes/search.d.ts +4 -0
- package/dist/routes/search.js +189 -0
- package/dist/routes/security.d.ts +10 -0
- package/dist/routes/security.js +189 -0
- package/dist/routes/settings.d.ts +4 -0
- package/dist/routes/settings.js +119 -0
- package/dist/routes/setup.d.ts +11 -0
- package/dist/routes/setup.js +525 -0
- package/dist/routes/skills.d.ts +13 -0
- package/dist/routes/skills.js +152 -0
- package/dist/routes/slack.d.ts +12 -0
- package/dist/routes/slack.js +999 -0
- package/dist/routes/states.d.ts +8 -0
- package/dist/routes/states.js +123 -0
- package/dist/routes/stats.d.ts +6 -0
- package/dist/routes/stats.js +369 -0
- package/dist/routes/summaries.d.ts +4 -0
- package/dist/routes/summaries.js +344 -0
- package/dist/routes/sync.d.ts +13 -0
- package/dist/routes/sync.js +268 -0
- package/dist/routes/tasks.d.ts +4 -0
- package/dist/routes/tasks.js +473 -0
- package/dist/routes/telegram.d.ts +14 -0
- package/dist/routes/telegram.js +420 -0
- package/dist/routes/tickets.d.ts +4 -0
- package/dist/routes/tickets.js +1090 -0
- package/dist/routes/tokens.d.ts +4 -0
- package/dist/routes/tokens.js +83 -0
- package/dist/routes/tools.d.ts +14 -0
- package/dist/routes/tools.js +790 -0
- package/dist/routes/tunnels.d.ts +8 -0
- package/dist/routes/tunnels.js +145 -0
- package/dist/routes/users.d.ts +15 -0
- package/dist/routes/users.js +998 -0
- package/dist/routes/voice.d.ts +8 -0
- package/dist/routes/voice.js +127 -0
- package/dist/routes/webchat.d.ts +11 -0
- package/dist/routes/webchat.js +173 -0
- package/dist/routes/webhook-dedup.d.ts +11 -0
- package/dist/routes/webhook-dedup.js +49 -0
- package/dist/routes/webhooks.d.ts +4 -0
- package/dist/routes/webhooks.js +71 -0
- package/dist/routes/whatsapp.d.ts +14 -0
- package/dist/routes/whatsapp.js +391 -0
- package/dist/security/audit-scanner.d.ts +32 -0
- package/dist/security/audit-scanner.js +217 -0
- package/dist/security/fs-guard.d.ts +41 -0
- package/dist/security/fs-guard.js +206 -0
- package/dist/security/prompt-guard.d.ts +47 -0
- package/dist/security/prompt-guard.js +198 -0
- package/dist/security/ssrf-guard.d.ts +38 -0
- package/dist/security/ssrf-guard.js +266 -0
- package/dist/security/types.d.ts +92 -0
- package/dist/security/types.js +8 -0
- package/dist/server/route-loader.d.ts +15 -0
- package/dist/server/route-loader.js +135 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +835 -0
- package/dist/settings/index.d.ts +841 -0
- package/dist/settings/index.js +451 -0
- package/dist/skills/compiler.d.ts +83 -0
- package/dist/skills/compiler.js +358 -0
- package/dist/skills/frontmatter.d.ts +33 -0
- package/dist/skills/frontmatter.js +175 -0
- package/dist/skills/index.d.ts +19 -0
- package/dist/skills/index.js +24 -0
- package/dist/skills/installer.d.ts +22 -0
- package/dist/skills/installer.js +220 -0
- package/dist/skills/loader.d.ts +72 -0
- package/dist/skills/loader.js +484 -0
- package/dist/skills/prompt-builder.d.ts +48 -0
- package/dist/skills/prompt-builder.js +135 -0
- package/dist/skills/registry.d.ts +115 -0
- package/dist/skills/registry.js +225 -0
- package/dist/skills/types.d.ts +202 -0
- package/dist/skills/types.js +24 -0
- package/dist/states/index.d.ts +64 -0
- package/dist/states/index.js +142 -0
- package/dist/storage/adapter.d.ts +146 -0
- package/dist/storage/adapter.js +2 -0
- package/dist/storage/index.d.ts +48 -0
- package/dist/storage/index.js +135 -0
- package/dist/storage/libsql.d.ts +99 -0
- package/dist/storage/libsql.js +1873 -0
- package/dist/storage/migrations.d.ts +65 -0
- package/dist/storage/migrations.js +289 -0
- package/dist/storage/schema.d.ts +10185 -0
- package/dist/storage/schema.js +1113 -0
- package/dist/summaries/extractor.d.ts +56 -0
- package/dist/summaries/extractor.js +377 -0
- package/dist/summaries/index.d.ts +13 -0
- package/dist/summaries/index.js +14 -0
- package/dist/summaries/storage.d.ts +51 -0
- package/dist/summaries/storage.js +101 -0
- package/dist/summaries/templates.d.ts +68 -0
- package/dist/summaries/templates.js +245 -0
- package/dist/sync/adapters/base.d.ts +42 -0
- package/dist/sync/adapters/base.js +142 -0
- package/dist/sync/adapters/github.d.ts +45 -0
- package/dist/sync/adapters/github.js +445 -0
- package/dist/sync/adapters/linear.d.ts +40 -0
- package/dist/sync/adapters/linear.js +434 -0
- package/dist/sync/config.d.ts +388 -0
- package/dist/sync/config.js +281 -0
- package/dist/sync/engine.d.ts +112 -0
- package/dist/sync/engine.js +623 -0
- package/dist/sync/index.d.ts +31 -0
- package/dist/sync/index.js +34 -0
- package/dist/sync/integration.d.ts +53 -0
- package/dist/sync/integration.js +231 -0
- package/dist/sync/types.d.ts +249 -0
- package/dist/sync/types.js +30 -0
- package/dist/tickets/index.d.ts +139 -0
- package/dist/tickets/index.js +1406 -0
- package/dist/tickets/types.d.ts +817 -0
- package/dist/tickets/types.js +310 -0
- package/dist/tools/adapters.d.ts +56 -0
- package/dist/tools/adapters.js +183 -0
- package/dist/tools/index.d.ts +66 -0
- package/dist/tools/index.js +105 -0
- package/dist/tools/types.d.ts +117 -0
- package/dist/tools/types.js +20 -0
- package/dist/types/agent.d.ts +101 -0
- package/dist/types/agent.js +2 -0
- package/dist/types/errors.d.ts +150 -0
- package/dist/types/errors.js +338 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/dist/types/summary.d.ts +638 -0
- package/dist/types/summary.js +137 -0
- package/dist/types/task.d.ts +123 -0
- package/dist/types/task.js +46 -0
- package/dist/utils/ai-cache.d.ts +72 -0
- package/dist/utils/ai-cache.js +204 -0
- package/dist/utils/ai-inference.d.ts +57 -0
- package/dist/utils/ai-inference.js +190 -0
- package/dist/utils/ai-responder.d.ts +44 -0
- package/dist/utils/ai-responder.js +272 -0
- package/dist/utils/circuit-breaker.d.ts +139 -0
- package/dist/utils/circuit-breaker.js +281 -0
- package/dist/utils/config-loader.d.ts +12 -0
- package/dist/utils/config-loader.js +44 -0
- package/dist/utils/crypto.d.ts +10 -0
- package/dist/utils/crypto.js +59 -0
- package/dist/utils/env-validator.d.ts +44 -0
- package/dist/utils/env-validator.js +236 -0
- package/dist/utils/file-lock.d.ts +85 -0
- package/dist/utils/file-lock.js +204 -0
- package/dist/utils/logger.d.ts +153 -0
- package/dist/utils/logger.js +282 -0
- package/dist/utils/metrics.d.ts +183 -0
- package/dist/utils/metrics.js +345 -0
- package/dist/utils/security.d.ts +90 -0
- package/dist/utils/security.js +347 -0
- package/dist/voice/index.d.ts +79 -0
- package/dist/voice/index.js +144 -0
- package/dist/voice/stt/whisper.d.ts +13 -0
- package/dist/voice/stt/whisper.js +116 -0
- package/dist/voice/tts/elevenlabs.d.ts +18 -0
- package/dist/voice/tts/elevenlabs.js +178 -0
- package/dist/voice/tts/openai-tts.d.ts +19 -0
- package/dist/voice/tts/openai-tts.js +127 -0
- package/dist/voice/tts/system.d.ts +26 -0
- package/dist/voice/tts/system.js +146 -0
- package/dist/voice/wake-word.d.ts +44 -0
- package/dist/voice/wake-word.js +145 -0
- package/docker-compose.yml +222 -0
- package/package.json +154 -0
- package/profclaw.mjs +24 -0
- package/skills/1password/SKILL.md +155 -0
- package/skills/api-tester/SKILL.md +177 -0
- package/skills/apple-notes/SKILL.md +167 -0
- package/skills/apple-reminders/SKILL.md +178 -0
- package/skills/bear-notes/SKILL.md +166 -0
- package/skills/blogwatcher/SKILL.md +177 -0
- package/skills/camsnap/SKILL.md +130 -0
- package/skills/code-generation/SKILL.md +139 -0
- package/skills/code-review/SKILL.md +116 -0
- package/skills/coding-agent/SKILL.md +184 -0
- package/skills/cron-manager/SKILL.md +141 -0
- package/skills/debug-helper/SKILL.md +134 -0
- package/skills/docker-ops/SKILL.md +246 -0
- package/skills/file-manager/SKILL.md +160 -0
- package/skills/git-workflow/SKILL.md +171 -0
- package/skills/github-issues/SKILL.md +244 -0
- package/skills/gog/SKILL.md +191 -0
- package/skills/goplaces/SKILL.md +156 -0
- package/skills/healthcheck/SKILL.md +270 -0
- package/skills/himalaya/SKILL.md +165 -0
- package/skills/mcp-discovery/SKILL.md +109 -0
- package/skills/memory-manager/SKILL.md +151 -0
- package/skills/model-usage/SKILL.md +177 -0
- package/skills/nano-banana-pro/SKILL.md +214 -0
- package/skills/nano-pdf/SKILL.md +165 -0
- package/skills/notion/SKILL.md +147 -0
- package/skills/obsidian/SKILL.md +163 -0
- package/skills/openai-image-gen/SKILL.md +194 -0
- package/skills/openai-whisper/SKILL.md +98 -0
- package/skills/openai-whisper-api/SKILL.md +130 -0
- package/skills/openhue/SKILL.md +203 -0
- package/skills/oracle/SKILL.md +236 -0
- package/skills/phone-control/SKILL.md +201 -0
- package/skills/profclaw-assistant/SKILL.md +81 -0
- package/skills/profclaw-projects/SKILL.md +68 -0
- package/skills/profclaw-tickets/SKILL.md +91 -0
- package/skills/session-logs/SKILL.md +216 -0
- package/skills/sherpa-onnx-tts/SKILL.md +134 -0
- package/skills/skill-creator/SKILL.md +183 -0
- package/skills/songsee/SKILL.md +170 -0
- package/skills/spotify-player/SKILL.md +254 -0
- package/skills/summarize/SKILL.md +155 -0
- package/skills/system-admin/SKILL.md +179 -0
- package/skills/things-mac/SKILL.md +179 -0
- package/skills/tmux/SKILL.md +170 -0
- package/skills/trello/SKILL.md +154 -0
- package/skills/video-frames/SKILL.md +127 -0
- package/skills/weather/SKILL.md +186 -0
- package/skills/web-research/SKILL.md +128 -0
- package/skills/xurl/SKILL.md +198 -0
- package/ui/README.md +73 -0
- package/ui/dist/assets/ActivityView-CkPgKhwm.js +1 -0
- package/ui/dist/assets/AgentList-C3SVdeEz.js +1 -0
- package/ui/dist/assets/AnalyticsDashboard-DVU--vlP.js +1 -0
- package/ui/dist/assets/AreaChart-B5qMEGWj.js +1 -0
- package/ui/dist/assets/CartesianChart-B50bcUJi.js +36 -0
- package/ui/dist/assets/ChatView-BZFJs7w_.js +10 -0
- package/ui/dist/assets/CostsDashboard-ChfH8BlG.js +1 -0
- package/ui/dist/assets/EstimateSelect-CLpB4NQO.js +1 -0
- package/ui/dist/assets/FloatingChatbot-CBzBY9a0.js +2 -0
- package/ui/dist/assets/InviteCodeManagement-DsbdPa6W.js +2 -0
- package/ui/dist/assets/ProjectDetail-BEM3tFXg.js +2 -0
- package/ui/dist/assets/ProjectDetail-D1-0B2xV.css +1 -0
- package/ui/dist/assets/ProjectIcon-CN1FmFAk.js +1 -0
- package/ui/dist/assets/ProjectList-DSDaNhxQ.js +4 -0
- package/ui/dist/assets/Settings-dPVcqC_d.js +6 -0
- package/ui/dist/assets/StatusIndicator-DA3NAo_b.js +1 -0
- package/ui/dist/assets/SummaryDetail-Bjnf9lT8.js +27 -0
- package/ui/dist/assets/SummaryList-ENwKDBKW.js +1 -0
- package/ui/dist/assets/TaskDetail-DnwG6MzB.js +1 -0
- package/ui/dist/assets/TaskList-D9Ug_HiV.js +1 -0
- package/ui/dist/assets/TicketBoard-CviH1571.js +1 -0
- package/ui/dist/assets/TicketDetail-8Jk8O-33.js +1 -0
- package/ui/dist/assets/TicketList-B-iV4rRA.js +1 -0
- package/ui/dist/assets/UserManagement-CPZ8s7s2.js +2 -0
- package/ui/dist/assets/ViewSwitcher-BUyOOWDI.js +1 -0
- package/ui/dist/assets/alert-dialog-BOmnTkN-.js +7 -0
- package/ui/dist/assets/archive-D5my9zOG.js +1 -0
- package/ui/dist/assets/badge-B4ov81_m.js +1 -0
- package/ui/dist/assets/brain-C53ElFLD.js +1 -0
- package/ui/dist/assets/bug-D0TJoCYn.js +1 -0
- package/ui/dist/assets/calendar-BkWIhKa1.js +1 -0
- package/ui/dist/assets/chevron-up-BRTtndUZ.js +1 -0
- package/ui/dist/assets/circle-x-BN89YhbS.js +1 -0
- package/ui/dist/assets/constants-fc7TFI7u.js +1 -0
- package/ui/dist/assets/cpu-B41x1-vV.js +1 -0
- package/ui/dist/assets/database-D9Cnfmzj.js +1 -0
- package/ui/dist/assets/defineProperty-CoXUr7_6.js +2 -0
- package/ui/dist/assets/dialog-DrWB4EYD.js +1 -0
- package/ui/dist/assets/element-adapter-DL0DQilL.js +4 -0
- package/ui/dist/assets/ellipsis-NuUWekvu.js +1 -0
- package/ui/dist/assets/ellipsis-vertical-BueMfuCw.js +1 -0
- package/ui/dist/assets/file-code-HR9829Jc.js +1 -0
- package/ui/dist/assets/folder-open-C_7oYEqe.js +1 -0
- package/ui/dist/assets/funnel-B5gmNXGf.js +1 -0
- package/ui/dist/assets/git-branch-BRMiJmV3.js +1 -0
- package/ui/dist/assets/git-commit-horizontal-CXOUQeJI.js +1 -0
- package/ui/dist/assets/globe-CCspjvus.js +1 -0
- package/ui/dist/assets/hash-g3102kPC.js +1 -0
- package/ui/dist/assets/history-CNqcBBrV.js +1 -0
- package/ui/dist/assets/index-27mTPKuh.js +1 -0
- package/ui/dist/assets/index-BMx3fH-e.js +1 -0
- package/ui/dist/assets/index-C9jNQXzL.js +1 -0
- package/ui/dist/assets/index-CEZ6R2Uw.js +7 -0
- package/ui/dist/assets/index-DURTouJI.js +1 -0
- package/ui/dist/assets/index-WclMPci1.css +1 -0
- package/ui/dist/assets/index-kb4AJXUB.js +67 -0
- package/ui/dist/assets/layers-CLXiHN6o.js +1 -0
- package/ui/dist/assets/layout-dashboard-B07MFhBs.js +1 -0
- package/ui/dist/assets/lightbulb-B2dgG5ks.js +1 -0
- package/ui/dist/assets/link-2-BxNmr9hL.js +1 -0
- package/ui/dist/assets/markdown-renderer-CjU6hBcW.js +36 -0
- package/ui/dist/assets/message-square-Bf2-CVbt.js +1 -0
- package/ui/dist/assets/pencil-D096Ey62.js +1 -0
- package/ui/dist/assets/play-DfdcSI3x.js +1 -0
- package/ui/dist/assets/popover-BV0QJDQq.js +1 -0
- package/ui/dist/assets/progress-DjPEDJfI.js +1 -0
- package/ui/dist/assets/rocket-CPsTc-5G.js +1 -0
- package/ui/dist/assets/select-LsO16slr.js +1 -0
- package/ui/dist/assets/send-BDYBZDUZ.js +1 -0
- package/ui/dist/assets/server-B3k6noce.js +1 -0
- package/ui/dist/assets/settings-2-Cu4xTjhG.js +1 -0
- package/ui/dist/assets/sheet-BScLnPKX.js +1 -0
- package/ui/dist/assets/shield-check-CQqkwufI.js +1 -0
- package/ui/dist/assets/shield-off-ZFruH9KO.js +1 -0
- package/ui/dist/assets/skeleton-CtkIA_MU.js +1 -0
- package/ui/dist/assets/smartphone-DnoksVkc.js +1 -0
- package/ui/dist/assets/square-uD9ZHMfW.js +1 -0
- package/ui/dist/assets/switch-MQPnwGJK.js +1 -0
- package/ui/dist/assets/table-B8cqlywA.js +1 -0
- package/ui/dist/assets/tag-wEJQRWlV.js +1 -0
- package/ui/dist/assets/textarea-D5LJCOGl.js +1 -0
- package/ui/dist/assets/timer-mbGHegmr.js +1 -0
- package/ui/dist/assets/tooltip-Df1CunSp.js +1 -0
- package/ui/dist/assets/trash-2-B4oAXsX-.js +1 -0
- package/ui/dist/assets/trending-up-BGWxvlmZ.js +1 -0
- package/ui/dist/assets/wand-sparkles-CZy4xvL3.js +1 -0
- package/ui/dist/assets/wrench-ByZkjO6m.js +1 -0
- package/ui/dist/audio-worklets/vad-processor.js +132 -0
- package/ui/dist/brand/profclaw-appicon-192.png +0 -0
- package/ui/dist/brand/profclaw-appicon-512.png +0 -0
- package/ui/dist/brand/profclaw-appicon-shadow.svg +51 -0
- package/ui/dist/brand/profclaw-appicon.svg +50 -0
- package/ui/dist/brand/profclaw-favicon-coral-bg-128.png +0 -0
- package/ui/dist/brand/profclaw-favicon-coral-bg-64.png +0 -0
- package/ui/dist/brand/profclaw-favicon-coral-bg.svg +52 -0
- package/ui/dist/brand/profclaw-favicon-coral-body-128.png +0 -0
- package/ui/dist/brand/profclaw-favicon-coral-body.svg +41 -0
- package/ui/dist/brand/profclaw-favicon-shadow-128.png +0 -0
- package/ui/dist/brand/profclaw-favicon-shadow-64.png +0 -0
- package/ui/dist/brand/profclaw-favicon-shadow.svg +51 -0
- package/ui/dist/brand/profclaw-mark-coral-256.png +0 -0
- package/ui/dist/brand/profclaw-mark-coral-64.png +0 -0
- package/ui/dist/brand/profclaw-mark-coral.svg +31 -0
- package/ui/dist/brand/profclaw-mono-white.svg +18 -0
- package/ui/dist/brand/profclaw-text-dark.svg +18 -0
- package/ui/dist/brand/profclaw-text-light.svg +18 -0
- package/ui/dist/brand/profclaw-wordmark-dark.svg +48 -0
- package/ui/dist/brand/profclaw-wordmark-light.svg +48 -0
- package/ui/dist/brand/safari-pinned-tab.svg +15 -0
- package/ui/dist/favicon/android-chrome-192x192.png +0 -0
- package/ui/dist/favicon/android-chrome-384x384.png +0 -0
- package/ui/dist/favicon/android-chrome-512x512.png +0 -0
- package/ui/dist/favicon/apple-touch-icon.png +0 -0
- package/ui/dist/favicon/browserconfig.xml +9 -0
- package/ui/dist/favicon/favicon-16x16.png +0 -0
- package/ui/dist/favicon/favicon-32x32.png +0 -0
- package/ui/dist/favicon/favicon.ico +0 -0
- package/ui/dist/favicon/mstile-150x150.png +0 -0
- package/ui/dist/favicon/safari-pinned-tab.svg +46 -0
- package/ui/dist/favicon/site.webmanifest +22 -0
- package/ui/dist/index.html +32 -0
- package/ui/dist/manifest.json +49 -0
- package/ui/dist/sw.js +124 -0
- package/ui/dist/vite.svg +1 -0
|
@@ -0,0 +1,1678 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat API Routes
|
|
3
|
+
*
|
|
4
|
+
* REST API for AI chat functionality with profClaw intelligence.
|
|
5
|
+
* Supports multiple providers, conversation history, and context-aware prompts.
|
|
6
|
+
*/
|
|
7
|
+
import { Hono } from "hono";
|
|
8
|
+
import { streamText } from "hono/streaming";
|
|
9
|
+
import { zValidator } from "@hono/zod-validator";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { logger } from "../utils/logger.js";
|
|
13
|
+
const chatRoutes = new Hono();
|
|
14
|
+
let chatRuntimePromise = null;
|
|
15
|
+
let ProviderTypeSchema;
|
|
16
|
+
let aiProvider;
|
|
17
|
+
let MODEL_ALIASES;
|
|
18
|
+
let saveProviderConfig;
|
|
19
|
+
let getClient;
|
|
20
|
+
let CHAT_PRESETS;
|
|
21
|
+
let QUICK_ACTIONS;
|
|
22
|
+
let buildSystemPrompt;
|
|
23
|
+
let createConversation;
|
|
24
|
+
let getConversation;
|
|
25
|
+
let listConversations;
|
|
26
|
+
let deleteConversation;
|
|
27
|
+
let addMessage;
|
|
28
|
+
let getConversationMessages;
|
|
29
|
+
let getRecentConversationsWithPreview;
|
|
30
|
+
let compactMessages;
|
|
31
|
+
let getMemoryStats;
|
|
32
|
+
let needsCompaction;
|
|
33
|
+
let CHAT_SKILLS;
|
|
34
|
+
let MODEL_TIERS;
|
|
35
|
+
let detectIntent;
|
|
36
|
+
let selectModel;
|
|
37
|
+
let createChatToolHandler;
|
|
38
|
+
let getDefaultChatTools;
|
|
39
|
+
let getAllChatTools;
|
|
40
|
+
let getChatToolsForModel;
|
|
41
|
+
let getSessionModel;
|
|
42
|
+
let streamAgenticChat;
|
|
43
|
+
let getGroupChatManager;
|
|
44
|
+
let trackChatUsage;
|
|
45
|
+
async function ensureChatRuntime() {
|
|
46
|
+
if (!chatRuntimePromise) {
|
|
47
|
+
chatRuntimePromise = Promise.all([
|
|
48
|
+
import("../providers/index.js"),
|
|
49
|
+
import("../storage/index.js"),
|
|
50
|
+
import("../chat/index.js"),
|
|
51
|
+
import("../costs/token-tracker.js"),
|
|
52
|
+
])
|
|
53
|
+
.then(([providersModule, storageModule, chatModule, costsModule]) => {
|
|
54
|
+
ProviderTypeSchema = providersModule.ProviderType;
|
|
55
|
+
aiProvider = providersModule.aiProvider;
|
|
56
|
+
MODEL_ALIASES = providersModule.MODEL_ALIASES;
|
|
57
|
+
saveProviderConfig = storageModule.saveProviderConfig;
|
|
58
|
+
getClient = storageModule.getClient;
|
|
59
|
+
CHAT_PRESETS = chatModule.CHAT_PRESETS;
|
|
60
|
+
QUICK_ACTIONS = chatModule.QUICK_ACTIONS;
|
|
61
|
+
buildSystemPrompt = chatModule.buildSystemPrompt;
|
|
62
|
+
createConversation = chatModule.createConversation;
|
|
63
|
+
getConversation = chatModule.getConversation;
|
|
64
|
+
listConversations = chatModule.listConversations;
|
|
65
|
+
deleteConversation = chatModule.deleteConversation;
|
|
66
|
+
addMessage = chatModule.addMessage;
|
|
67
|
+
getConversationMessages = chatModule.getConversationMessages;
|
|
68
|
+
getRecentConversationsWithPreview =
|
|
69
|
+
chatModule.getRecentConversationsWithPreview;
|
|
70
|
+
compactMessages = chatModule.compactMessages;
|
|
71
|
+
getMemoryStats = chatModule.getMemoryStats;
|
|
72
|
+
needsCompaction = chatModule.needsCompaction;
|
|
73
|
+
CHAT_SKILLS = chatModule.CHAT_SKILLS;
|
|
74
|
+
MODEL_TIERS = chatModule.MODEL_TIERS;
|
|
75
|
+
detectIntent = chatModule.detectIntent;
|
|
76
|
+
selectModel = chatModule.selectModel;
|
|
77
|
+
createChatToolHandler = chatModule.createChatToolHandler;
|
|
78
|
+
getDefaultChatTools = chatModule.getDefaultChatTools;
|
|
79
|
+
getAllChatTools = chatModule.getAllChatTools;
|
|
80
|
+
getChatToolsForModel = chatModule.getChatToolsForModel;
|
|
81
|
+
getSessionModel = chatModule.getSessionModel;
|
|
82
|
+
streamAgenticChat = chatModule.streamAgenticChat;
|
|
83
|
+
getGroupChatManager = chatModule.getGroupChatManager;
|
|
84
|
+
trackChatUsage = costsModule.trackChatUsage;
|
|
85
|
+
})
|
|
86
|
+
.catch((error) => {
|
|
87
|
+
chatRuntimePromise = null;
|
|
88
|
+
throw error;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
await chatRuntimePromise;
|
|
92
|
+
}
|
|
93
|
+
function parseProviderType(value) {
|
|
94
|
+
const parsed = ProviderTypeSchema.safeParse(value);
|
|
95
|
+
return parsed.success ? parsed.data : null;
|
|
96
|
+
}
|
|
97
|
+
chatRoutes.use("*", async (c, next) => {
|
|
98
|
+
try {
|
|
99
|
+
await ensureChatRuntime();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
logger.error("[Chat] Failed to initialize runtime", error instanceof Error ? error : undefined);
|
|
103
|
+
return c.json({
|
|
104
|
+
error: error instanceof Error
|
|
105
|
+
? error.message
|
|
106
|
+
: "Chat runtime unavailable",
|
|
107
|
+
}, 500);
|
|
108
|
+
}
|
|
109
|
+
await next();
|
|
110
|
+
});
|
|
111
|
+
// === Schemas ===
|
|
112
|
+
const ChatMessageSchema = z.object({
|
|
113
|
+
role: z.enum(["user", "assistant", "system"]),
|
|
114
|
+
content: z.string(),
|
|
115
|
+
});
|
|
116
|
+
const ChatCompletionSchema = z.object({
|
|
117
|
+
messages: z.array(ChatMessageSchema),
|
|
118
|
+
model: z.string().optional(),
|
|
119
|
+
systemPrompt: z.string().optional(),
|
|
120
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
121
|
+
maxTokens: z.number().positive().optional(),
|
|
122
|
+
stream: z.boolean().optional(),
|
|
123
|
+
// Context linking
|
|
124
|
+
conversationId: z.string().optional(),
|
|
125
|
+
ticketId: z.string().optional(),
|
|
126
|
+
taskId: z.string().optional(),
|
|
127
|
+
});
|
|
128
|
+
const ProviderConfigSchema = z.object({
|
|
129
|
+
type: z.enum([
|
|
130
|
+
"anthropic",
|
|
131
|
+
"openai",
|
|
132
|
+
"azure",
|
|
133
|
+
"google",
|
|
134
|
+
"ollama",
|
|
135
|
+
"openrouter",
|
|
136
|
+
"groq",
|
|
137
|
+
"xai",
|
|
138
|
+
"mistral",
|
|
139
|
+
"cohere",
|
|
140
|
+
"perplexity",
|
|
141
|
+
"deepseek",
|
|
142
|
+
"together",
|
|
143
|
+
"cerebras",
|
|
144
|
+
"fireworks",
|
|
145
|
+
]),
|
|
146
|
+
apiKey: z.string().optional(),
|
|
147
|
+
baseUrl: z.string().optional(),
|
|
148
|
+
// Azure-specific fields
|
|
149
|
+
resourceName: z.string().optional(),
|
|
150
|
+
deploymentName: z.string().optional(),
|
|
151
|
+
apiVersion: z.string().optional(),
|
|
152
|
+
defaultModel: z.string().optional(),
|
|
153
|
+
enabled: z.boolean().optional(),
|
|
154
|
+
});
|
|
155
|
+
// === Routes ===
|
|
156
|
+
/**
|
|
157
|
+
* GET /api/chat/model-capability
|
|
158
|
+
* Returns capability tier and tool access level for a given model ID
|
|
159
|
+
*/
|
|
160
|
+
chatRoutes.get("/model-capability", async (c) => {
|
|
161
|
+
const model = c.req.query("model");
|
|
162
|
+
if (!model) {
|
|
163
|
+
return c.json({ capability: "reasoning", tier: "full", maxSchemaTokens: 20000 });
|
|
164
|
+
}
|
|
165
|
+
const { getModelRouting } = await import("../chat/execution/model-capability.js");
|
|
166
|
+
// Resolve alias to full model ID (e.g. "gemini" -> "gemini-1.5-pro")
|
|
167
|
+
const { MODEL_ALIASES } = await import("../providers/core/models.js");
|
|
168
|
+
const resolved = MODEL_ALIASES[model.toLowerCase()]?.model ?? model;
|
|
169
|
+
return c.json(getModelRouting(resolved));
|
|
170
|
+
});
|
|
171
|
+
/**
|
|
172
|
+
* POST /api/chat/completions
|
|
173
|
+
* Generate a chat completion
|
|
174
|
+
*/
|
|
175
|
+
chatRoutes.post("/completions", zValidator("json", ChatCompletionSchema), async (c) => {
|
|
176
|
+
const body = c.req.valid("json");
|
|
177
|
+
// Convert to ChatMessage format
|
|
178
|
+
const messages = body.messages.map((msg) => ({
|
|
179
|
+
id: randomUUID(),
|
|
180
|
+
role: msg.role,
|
|
181
|
+
content: msg.content,
|
|
182
|
+
timestamp: new Date().toISOString(),
|
|
183
|
+
}));
|
|
184
|
+
// Handle streaming
|
|
185
|
+
if (body.stream) {
|
|
186
|
+
return streamText(c, async (stream) => {
|
|
187
|
+
try {
|
|
188
|
+
const response = await aiProvider.chatStream({
|
|
189
|
+
messages,
|
|
190
|
+
model: body.model,
|
|
191
|
+
systemPrompt: body.systemPrompt,
|
|
192
|
+
temperature: body.temperature,
|
|
193
|
+
maxTokens: body.maxTokens,
|
|
194
|
+
}, (chunk) => {
|
|
195
|
+
// Non-blocking write - fire and forget for speed
|
|
196
|
+
stream
|
|
197
|
+
.write(`data: ${JSON.stringify({ content: chunk })}\n\n`)
|
|
198
|
+
.catch((err) => {
|
|
199
|
+
logger.error("[Chat] Stream write error:", err instanceof Error ? err : undefined);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
// Send final message with usage (this one we await to ensure delivery)
|
|
203
|
+
await stream.write(`data: ${JSON.stringify({
|
|
204
|
+
done: true,
|
|
205
|
+
usage: response.usage,
|
|
206
|
+
finishReason: response.finishReason,
|
|
207
|
+
})}\n\n`);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
logger.error("[Chat] Stream error:", error instanceof Error ? error : undefined);
|
|
211
|
+
await stream.write(`data: ${JSON.stringify({
|
|
212
|
+
error: error instanceof Error ? error.message : "Chat failed",
|
|
213
|
+
})}\n\n`);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
// Non-streaming
|
|
218
|
+
try {
|
|
219
|
+
const response = await aiProvider.chat({
|
|
220
|
+
messages,
|
|
221
|
+
model: body.model,
|
|
222
|
+
systemPrompt: body.systemPrompt,
|
|
223
|
+
temperature: body.temperature,
|
|
224
|
+
maxTokens: body.maxTokens,
|
|
225
|
+
});
|
|
226
|
+
return c.json({
|
|
227
|
+
id: response.id,
|
|
228
|
+
provider: response.provider,
|
|
229
|
+
model: response.model,
|
|
230
|
+
message: {
|
|
231
|
+
role: "assistant",
|
|
232
|
+
content: response.content,
|
|
233
|
+
},
|
|
234
|
+
finishReason: response.finishReason,
|
|
235
|
+
usage: response.usage,
|
|
236
|
+
duration: response.duration,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
logger.error("[Chat] Completion error:", error instanceof Error ? error : undefined);
|
|
241
|
+
return c.json({
|
|
242
|
+
error: error instanceof Error ? error.message : "Chat completion failed",
|
|
243
|
+
}, 500);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
/**
|
|
247
|
+
* GET /api/chat/models
|
|
248
|
+
* List available models
|
|
249
|
+
*/
|
|
250
|
+
chatRoutes.get("/models", async (c) => {
|
|
251
|
+
const provider = c.req.query("provider");
|
|
252
|
+
let models;
|
|
253
|
+
if (provider) {
|
|
254
|
+
const providerType = parseProviderType(provider);
|
|
255
|
+
if (!providerType) {
|
|
256
|
+
return c.json({ error: "Invalid provider" }, 400);
|
|
257
|
+
}
|
|
258
|
+
models = aiProvider.getModelsForProvider(providerType);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
models = aiProvider.getAllModels();
|
|
262
|
+
}
|
|
263
|
+
// Convert MODEL_ALIASES to API format dynamically
|
|
264
|
+
const aliases = Object.entries(MODEL_ALIASES).map(([alias, config]) => ({
|
|
265
|
+
alias,
|
|
266
|
+
provider: config.provider,
|
|
267
|
+
model: config.model,
|
|
268
|
+
}));
|
|
269
|
+
return c.json({
|
|
270
|
+
models,
|
|
271
|
+
aliases,
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
/**
|
|
275
|
+
* GET /api/chat/providers
|
|
276
|
+
* List configured providers and their status
|
|
277
|
+
*/
|
|
278
|
+
chatRoutes.get("/providers", async (c) => {
|
|
279
|
+
const configured = aiProvider.getConfiguredProviders();
|
|
280
|
+
const health = await aiProvider.healthCheck();
|
|
281
|
+
return c.json({
|
|
282
|
+
default: aiProvider.getDefaultProvider(),
|
|
283
|
+
providers: configured.map((p) => {
|
|
284
|
+
const h = health.find((h) => h.provider === p);
|
|
285
|
+
return {
|
|
286
|
+
type: p,
|
|
287
|
+
enabled: true,
|
|
288
|
+
healthy: h?.healthy ?? false,
|
|
289
|
+
message: h?.message,
|
|
290
|
+
latencyMs: h?.latencyMs,
|
|
291
|
+
};
|
|
292
|
+
}),
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
/**
|
|
296
|
+
* POST /api/chat/providers/:type/configure
|
|
297
|
+
* Configure a provider (persists to database)
|
|
298
|
+
*/
|
|
299
|
+
chatRoutes.post("/providers/:type/configure", zValidator("json", ProviderConfigSchema), async (c) => {
|
|
300
|
+
const type = parseProviderType(c.req.param("type"));
|
|
301
|
+
const config = c.req.valid("json");
|
|
302
|
+
if (!type) {
|
|
303
|
+
return c.json({ error: "Invalid provider" }, 400);
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
// Build provider config (include Azure-specific fields)
|
|
307
|
+
const providerConfig = {
|
|
308
|
+
type,
|
|
309
|
+
apiKey: config.apiKey,
|
|
310
|
+
baseUrl: config.baseUrl,
|
|
311
|
+
resourceName: config.resourceName,
|
|
312
|
+
deploymentName: config.deploymentName,
|
|
313
|
+
apiVersion: config.apiVersion,
|
|
314
|
+
defaultModel: config.defaultModel,
|
|
315
|
+
enabled: config.enabled ?? true,
|
|
316
|
+
};
|
|
317
|
+
// Configure in memory
|
|
318
|
+
aiProvider.configure(type, providerConfig);
|
|
319
|
+
// Persist to database (only if we have an API key or baseUrl)
|
|
320
|
+
if (config.apiKey || config.baseUrl) {
|
|
321
|
+
await saveProviderConfig({
|
|
322
|
+
type,
|
|
323
|
+
apiKey: config.apiKey,
|
|
324
|
+
baseUrl: config.baseUrl,
|
|
325
|
+
resourceName: config.resourceName,
|
|
326
|
+
deploymentName: config.deploymentName,
|
|
327
|
+
apiVersion: config.apiVersion,
|
|
328
|
+
defaultModel: config.defaultModel,
|
|
329
|
+
enabled: config.enabled ?? true,
|
|
330
|
+
});
|
|
331
|
+
logger.info(`[Chat] Provider ${type} config saved to database`);
|
|
332
|
+
}
|
|
333
|
+
return c.json({ success: true, message: `${type} configured` });
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
logger.error(`[Chat] Failed to configure provider ${type}:`, error instanceof Error ? error : undefined);
|
|
337
|
+
return c.json({
|
|
338
|
+
error: error instanceof Error ? error.message : "Configuration failed",
|
|
339
|
+
}, 400);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
/**
|
|
343
|
+
* POST /api/chat/providers/:type/health
|
|
344
|
+
* Check provider health
|
|
345
|
+
*/
|
|
346
|
+
chatRoutes.post("/providers/:type/health", async (c) => {
|
|
347
|
+
const type = parseProviderType(c.req.param("type"));
|
|
348
|
+
if (!type) {
|
|
349
|
+
return c.json({ error: "Invalid provider" }, 400);
|
|
350
|
+
}
|
|
351
|
+
const results = await aiProvider.healthCheck(type);
|
|
352
|
+
const result = results[0];
|
|
353
|
+
if (!result) {
|
|
354
|
+
return c.json({ error: "Provider not found" }, 404);
|
|
355
|
+
}
|
|
356
|
+
return c.json(result);
|
|
357
|
+
});
|
|
358
|
+
/**
|
|
359
|
+
* POST /api/chat/providers/default
|
|
360
|
+
* Set default provider
|
|
361
|
+
*/
|
|
362
|
+
chatRoutes.post("/providers/default", zValidator("json", z.object({ provider: ProviderConfigSchema.shape.type })), async (c) => {
|
|
363
|
+
const { provider } = c.req.valid("json");
|
|
364
|
+
try {
|
|
365
|
+
aiProvider.setDefaultProvider(provider);
|
|
366
|
+
return c.json({ success: true, default: provider });
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
return c.json({
|
|
370
|
+
error: error instanceof Error ? error.message : "Failed to set default",
|
|
371
|
+
}, 400);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
/**
|
|
375
|
+
* GET /api/chat/providers/:type/models
|
|
376
|
+
* Fetch available models from a provider (dynamic discovery)
|
|
377
|
+
*/
|
|
378
|
+
chatRoutes.get("/providers/:type/models", async (c) => {
|
|
379
|
+
const type = c.req.param("type");
|
|
380
|
+
try {
|
|
381
|
+
// For Ollama, fetch from the local API
|
|
382
|
+
if (type === "ollama") {
|
|
383
|
+
const baseUrl = process.env.OLLAMA_BASE_URL || "http://localhost:11434";
|
|
384
|
+
const response = await fetch(`${baseUrl}/api/tags`);
|
|
385
|
+
if (!response.ok) {
|
|
386
|
+
throw new Error("Failed to fetch Ollama models");
|
|
387
|
+
}
|
|
388
|
+
const data = (await response.json());
|
|
389
|
+
return c.json({
|
|
390
|
+
provider: type,
|
|
391
|
+
models: (data.models || []).map((m) => ({
|
|
392
|
+
id: m.name,
|
|
393
|
+
name: m.name,
|
|
394
|
+
size: m.size,
|
|
395
|
+
modifiedAt: m.modified_at,
|
|
396
|
+
})),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
// For OpenRouter, fetch from their API
|
|
400
|
+
if (type === "openrouter") {
|
|
401
|
+
const apiKey = process.env.OPENROUTER_API_KEY;
|
|
402
|
+
if (!apiKey) {
|
|
403
|
+
return c.json({ error: "OpenRouter not configured" }, 400);
|
|
404
|
+
}
|
|
405
|
+
const response = await fetch("https://openrouter.ai/api/v1/models", {
|
|
406
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
407
|
+
});
|
|
408
|
+
if (!response.ok) {
|
|
409
|
+
throw new Error("Failed to fetch OpenRouter models");
|
|
410
|
+
}
|
|
411
|
+
const data = (await response.json());
|
|
412
|
+
return c.json({
|
|
413
|
+
provider: type,
|
|
414
|
+
models: (data.data || []).map((m) => ({
|
|
415
|
+
id: m.id,
|
|
416
|
+
name: m.name || m.id,
|
|
417
|
+
pricing: m.pricing,
|
|
418
|
+
})),
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
// For other providers, return static catalog
|
|
422
|
+
const providerType = parseProviderType(type);
|
|
423
|
+
if (!providerType) {
|
|
424
|
+
return c.json({ error: "Invalid provider" }, 400);
|
|
425
|
+
}
|
|
426
|
+
const models = aiProvider.getModelsForProvider(providerType);
|
|
427
|
+
return c.json({ provider: type, models });
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
logger.error(`[Chat] Failed to fetch models for ${type}:`, error instanceof Error ? error : undefined);
|
|
431
|
+
return c.json({
|
|
432
|
+
error: error instanceof Error ? error.message : "Failed to fetch models",
|
|
433
|
+
}, 500);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
/**
|
|
437
|
+
* POST /api/chat/quick
|
|
438
|
+
* Quick chat endpoint for simple requests
|
|
439
|
+
*/
|
|
440
|
+
chatRoutes.post("/quick", zValidator("json", z.object({
|
|
441
|
+
prompt: z.string(),
|
|
442
|
+
model: z.string().optional(),
|
|
443
|
+
systemPrompt: z.string().optional(),
|
|
444
|
+
temperature: z.number().optional(),
|
|
445
|
+
})), async (c) => {
|
|
446
|
+
const { prompt, model, systemPrompt, temperature } = c.req.valid("json");
|
|
447
|
+
try {
|
|
448
|
+
const response = await aiProvider.chat({
|
|
449
|
+
messages: [
|
|
450
|
+
{
|
|
451
|
+
id: randomUUID(),
|
|
452
|
+
role: "user",
|
|
453
|
+
content: prompt,
|
|
454
|
+
timestamp: new Date().toISOString(),
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
model,
|
|
458
|
+
systemPrompt,
|
|
459
|
+
temperature,
|
|
460
|
+
});
|
|
461
|
+
return c.json({
|
|
462
|
+
content: response.content,
|
|
463
|
+
model: response.model,
|
|
464
|
+
provider: response.provider,
|
|
465
|
+
usage: response.usage,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
logger.error("[Chat] Quick chat error:", error instanceof Error ? error : undefined);
|
|
470
|
+
return c.json({
|
|
471
|
+
error: error instanceof Error ? error.message : "Quick chat failed",
|
|
472
|
+
}, 500);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
// === Preset & Context Routes ===
|
|
476
|
+
/**
|
|
477
|
+
* GET /api/chat/presets
|
|
478
|
+
* List available chat presets
|
|
479
|
+
*/
|
|
480
|
+
chatRoutes.get("/presets", async (c) => {
|
|
481
|
+
return c.json({
|
|
482
|
+
presets: CHAT_PRESETS.map((p) => ({
|
|
483
|
+
id: p.id,
|
|
484
|
+
name: p.name,
|
|
485
|
+
description: p.description,
|
|
486
|
+
icon: p.icon,
|
|
487
|
+
examples: p.examples,
|
|
488
|
+
})),
|
|
489
|
+
default: "profclaw-assistant",
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
/**
|
|
493
|
+
* GET /api/chat/quick-actions
|
|
494
|
+
* List quick action suggestions
|
|
495
|
+
*/
|
|
496
|
+
chatRoutes.get("/quick-actions", async (c) => {
|
|
497
|
+
return c.json({ actions: QUICK_ACTIONS });
|
|
498
|
+
});
|
|
499
|
+
// === Skills Routes ===
|
|
500
|
+
/**
|
|
501
|
+
* GET /api/chat/skills
|
|
502
|
+
* List available chat skills
|
|
503
|
+
*/
|
|
504
|
+
chatRoutes.get("/skills", async (c) => {
|
|
505
|
+
return c.json({
|
|
506
|
+
skills: CHAT_SKILLS.map((s) => ({
|
|
507
|
+
id: s.id,
|
|
508
|
+
name: s.name,
|
|
509
|
+
description: s.description,
|
|
510
|
+
icon: s.icon,
|
|
511
|
+
capabilities: s.capabilities,
|
|
512
|
+
preferredModel: s.preferredModel,
|
|
513
|
+
examples: s.examples,
|
|
514
|
+
})),
|
|
515
|
+
modelTiers: MODEL_TIERS.map((t) => ({
|
|
516
|
+
tier: t.tier,
|
|
517
|
+
description: t.description,
|
|
518
|
+
costMultiplier: t.costMultiplier,
|
|
519
|
+
})),
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
/**
|
|
523
|
+
* POST /api/chat/skills/detect
|
|
524
|
+
* Detect intent and match skills for a message
|
|
525
|
+
*/
|
|
526
|
+
chatRoutes.post("/skills/detect", zValidator("json", z.object({
|
|
527
|
+
message: z.string(),
|
|
528
|
+
hasTask: z.boolean().optional(),
|
|
529
|
+
hasTicket: z.boolean().optional(),
|
|
530
|
+
hasCode: z.boolean().optional(),
|
|
531
|
+
})), async (c) => {
|
|
532
|
+
const { message, hasTask, hasTicket, hasCode } = c.req.valid("json");
|
|
533
|
+
const matches = detectIntent(message, { hasTask, hasTicket, hasCode });
|
|
534
|
+
return c.json({
|
|
535
|
+
matches: matches.slice(0, 3).map((m) => ({
|
|
536
|
+
skillId: m.skill.id,
|
|
537
|
+
skillName: m.skill.name,
|
|
538
|
+
confidence: m.confidence,
|
|
539
|
+
matchedPattern: m.matchedPattern,
|
|
540
|
+
extractedVars: m.extractedVars,
|
|
541
|
+
preferredModel: m.skill.preferredModel,
|
|
542
|
+
})),
|
|
543
|
+
recommendedSkill: matches[0]
|
|
544
|
+
? {
|
|
545
|
+
id: matches[0].skill.id,
|
|
546
|
+
name: matches[0].skill.name,
|
|
547
|
+
confidence: matches[0].confidence,
|
|
548
|
+
}
|
|
549
|
+
: null,
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
/**
|
|
553
|
+
* POST /api/chat/skills/route
|
|
554
|
+
* Get recommended model and skill for a message
|
|
555
|
+
*/
|
|
556
|
+
chatRoutes.post("/skills/route", zValidator("json", z.object({
|
|
557
|
+
message: z.string(),
|
|
558
|
+
availableModels: z.array(z.string()).optional(),
|
|
559
|
+
hasTask: z.boolean().optional(),
|
|
560
|
+
hasTicket: z.boolean().optional(),
|
|
561
|
+
hasCode: z.boolean().optional(),
|
|
562
|
+
})), async (c) => {
|
|
563
|
+
const { message, availableModels, hasTask, hasTicket, hasCode } = c.req.valid("json");
|
|
564
|
+
// Get available models from providers if not specified
|
|
565
|
+
let models = availableModels;
|
|
566
|
+
if (!models || models.length === 0) {
|
|
567
|
+
const allModels = aiProvider.getAllModels();
|
|
568
|
+
models = allModels.map((m) => m.id);
|
|
569
|
+
}
|
|
570
|
+
// Detect intent
|
|
571
|
+
const matches = detectIntent(message, { hasTask, hasTicket, hasCode });
|
|
572
|
+
const bestMatch = matches[0];
|
|
573
|
+
if (!bestMatch) {
|
|
574
|
+
return c.json({
|
|
575
|
+
error: "Could not detect intent",
|
|
576
|
+
fallback: { model: models[0], tier: "balanced" },
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
// Select model based on skill and message
|
|
580
|
+
const selection = selectModel(bestMatch, message, models);
|
|
581
|
+
return c.json({
|
|
582
|
+
skill: {
|
|
583
|
+
id: bestMatch.skill.id,
|
|
584
|
+
name: bestMatch.skill.name,
|
|
585
|
+
confidence: bestMatch.confidence,
|
|
586
|
+
},
|
|
587
|
+
model: {
|
|
588
|
+
selected: selection.model,
|
|
589
|
+
tier: selection.tier.tier,
|
|
590
|
+
reason: selection.reason,
|
|
591
|
+
costMultiplier: selection.tier.costMultiplier,
|
|
592
|
+
},
|
|
593
|
+
routing: {
|
|
594
|
+
method: bestMatch.matchedPattern ? "pattern_match" : "fallback",
|
|
595
|
+
pattern: bestMatch.matchedPattern,
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
// === Conversation Routes ===
|
|
600
|
+
/**
|
|
601
|
+
* GET /api/chat/conversations
|
|
602
|
+
* List conversations with optional filtering
|
|
603
|
+
*/
|
|
604
|
+
chatRoutes.get("/conversations", async (c) => {
|
|
605
|
+
const limit = Number(c.req.query("limit")) || 20;
|
|
606
|
+
const offset = Number(c.req.query("offset")) || 0;
|
|
607
|
+
const taskId = c.req.query("taskId");
|
|
608
|
+
const ticketId = c.req.query("ticketId");
|
|
609
|
+
try {
|
|
610
|
+
const result = await listConversations({ limit, offset, taskId, ticketId });
|
|
611
|
+
return c.json(result);
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
logger.error("[Chat] List conversations error:", error instanceof Error ? error : undefined);
|
|
615
|
+
return c.json({ error: "Failed to list conversations" }, 500);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
/**
|
|
619
|
+
* GET /api/chat/conversations/recent
|
|
620
|
+
* Get recent conversations with preview
|
|
621
|
+
*/
|
|
622
|
+
chatRoutes.get("/conversations/recent", async (c) => {
|
|
623
|
+
const limit = Number(c.req.query("limit")) || 10;
|
|
624
|
+
try {
|
|
625
|
+
const conversations = await getRecentConversationsWithPreview(limit);
|
|
626
|
+
return c.json({ conversations });
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
logger.error("[Chat] Recent conversations error:", error instanceof Error ? error : undefined);
|
|
630
|
+
return c.json({ error: "Failed to get recent conversations" }, 500);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
/**
|
|
634
|
+
* POST /api/chat/conversations
|
|
635
|
+
* Create a new conversation
|
|
636
|
+
*/
|
|
637
|
+
chatRoutes.post("/conversations", zValidator("json", z.object({
|
|
638
|
+
title: z.string().optional(),
|
|
639
|
+
presetId: z.string().optional(),
|
|
640
|
+
taskId: z.string().optional(),
|
|
641
|
+
ticketId: z.string().optional(),
|
|
642
|
+
projectId: z.string().optional(),
|
|
643
|
+
})), async (c) => {
|
|
644
|
+
const body = c.req.valid("json");
|
|
645
|
+
try {
|
|
646
|
+
const conversation = await createConversation(body);
|
|
647
|
+
return c.json({ conversation }, 201);
|
|
648
|
+
}
|
|
649
|
+
catch (error) {
|
|
650
|
+
logger.error("[Chat] Create conversation error:", error instanceof Error ? error : undefined);
|
|
651
|
+
return c.json({ error: "Failed to create conversation" }, 500);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
/**
|
|
655
|
+
* GET /api/chat/conversations/:id
|
|
656
|
+
* Get a conversation with messages
|
|
657
|
+
*/
|
|
658
|
+
chatRoutes.get("/conversations/:id", async (c) => {
|
|
659
|
+
const id = c.req.param("id");
|
|
660
|
+
try {
|
|
661
|
+
const conversation = await getConversation(id);
|
|
662
|
+
if (!conversation) {
|
|
663
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
664
|
+
}
|
|
665
|
+
const messages = await getConversationMessages(id);
|
|
666
|
+
return c.json({ conversation, messages });
|
|
667
|
+
}
|
|
668
|
+
catch (error) {
|
|
669
|
+
logger.error("[Chat] Get conversation error:", error instanceof Error ? error : undefined);
|
|
670
|
+
return c.json({ error: "Failed to get conversation" }, 500);
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
/**
|
|
674
|
+
* DELETE /api/chat/conversations/:id
|
|
675
|
+
* Delete a conversation
|
|
676
|
+
*/
|
|
677
|
+
chatRoutes.delete("/conversations/:id", async (c) => {
|
|
678
|
+
const id = c.req.param("id");
|
|
679
|
+
try {
|
|
680
|
+
await deleteConversation(id);
|
|
681
|
+
return c.json({ message: "Conversation deleted" });
|
|
682
|
+
}
|
|
683
|
+
catch (error) {
|
|
684
|
+
logger.error("[Chat] Delete conversation error:", error instanceof Error ? error : undefined);
|
|
685
|
+
return c.json({ error: "Failed to delete conversation" }, 500);
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
/**
|
|
689
|
+
* GET /api/chat/conversations/:id/memory
|
|
690
|
+
* Get memory stats for a conversation
|
|
691
|
+
*/
|
|
692
|
+
chatRoutes.get("/conversations/:id/memory", async (c) => {
|
|
693
|
+
const id = c.req.param("id");
|
|
694
|
+
const model = c.req.query("model");
|
|
695
|
+
try {
|
|
696
|
+
const conversation = await getConversation(id);
|
|
697
|
+
if (!conversation) {
|
|
698
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
699
|
+
}
|
|
700
|
+
const messages = await getConversationMessages(id);
|
|
701
|
+
const stats = getMemoryStats(messages, model);
|
|
702
|
+
return c.json({
|
|
703
|
+
conversationId: id,
|
|
704
|
+
stats,
|
|
705
|
+
recommendation: stats.needsCompaction
|
|
706
|
+
? "Conversation will be automatically compacted on next message"
|
|
707
|
+
: stats.usagePercentage > 50
|
|
708
|
+
? "Approaching context limit, consider starting a new conversation for long discussions"
|
|
709
|
+
: "Memory usage healthy",
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
catch (error) {
|
|
713
|
+
logger.error("[Chat] Memory stats error:", error instanceof Error ? error : undefined);
|
|
714
|
+
return c.json({ error: "Failed to get memory stats" }, 500);
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
/**
|
|
718
|
+
* POST /api/chat/conversations/:id/compact
|
|
719
|
+
* Manually trigger compaction for a conversation
|
|
720
|
+
*/
|
|
721
|
+
chatRoutes.post("/conversations/:id/compact", async (c) => {
|
|
722
|
+
const id = c.req.param("id");
|
|
723
|
+
const model = c.req.query("model");
|
|
724
|
+
try {
|
|
725
|
+
const conversation = await getConversation(id);
|
|
726
|
+
if (!conversation) {
|
|
727
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
728
|
+
}
|
|
729
|
+
const messages = await getConversationMessages(id);
|
|
730
|
+
const result = await compactMessages(messages, model);
|
|
731
|
+
if (!result.wasCompacted) {
|
|
732
|
+
return c.json({
|
|
733
|
+
compacted: false,
|
|
734
|
+
message: "Conversation does not need compaction yet",
|
|
735
|
+
stats: getMemoryStats(messages, model),
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
// Note: In a full implementation, we'd save the compacted messages
|
|
739
|
+
// For now, we just return what the compaction would produce
|
|
740
|
+
return c.json({
|
|
741
|
+
compacted: true,
|
|
742
|
+
originalCount: result.originalCount,
|
|
743
|
+
compactedCount: result.compactedCount,
|
|
744
|
+
tokensReduced: result.tokensReduced,
|
|
745
|
+
summary: result.summary,
|
|
746
|
+
stats: getMemoryStats(result.messages, model),
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
catch (error) {
|
|
750
|
+
logger.error("[Chat] Manual compaction error:", error instanceof Error ? error : undefined);
|
|
751
|
+
return c.json({ error: "Failed to compact conversation" }, 500);
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
/**
|
|
755
|
+
* POST /api/chat/conversations/:id/messages
|
|
756
|
+
* Send a message in a conversation (with context and history)
|
|
757
|
+
*/
|
|
758
|
+
chatRoutes.post("/conversations/:id/messages", zValidator("json", z.object({
|
|
759
|
+
content: z.string(),
|
|
760
|
+
model: z.string().optional(),
|
|
761
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
762
|
+
})), async (c) => {
|
|
763
|
+
const conversationId = c.req.param("id");
|
|
764
|
+
const body = c.req.valid("json");
|
|
765
|
+
try {
|
|
766
|
+
// Get conversation and build context
|
|
767
|
+
const conversation = await getConversation(conversationId);
|
|
768
|
+
if (!conversation) {
|
|
769
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
770
|
+
}
|
|
771
|
+
// Build context from linked entities
|
|
772
|
+
const context = {};
|
|
773
|
+
const client = getClient();
|
|
774
|
+
if (conversation.taskId) {
|
|
775
|
+
const taskResult = await client.execute({
|
|
776
|
+
sql: `SELECT id, description, status, assigned_agent as agent FROM tasks WHERE id = ?`,
|
|
777
|
+
args: [conversation.taskId],
|
|
778
|
+
});
|
|
779
|
+
if (taskResult.rows.length > 0) {
|
|
780
|
+
const t = taskResult.rows[0];
|
|
781
|
+
context.task = {
|
|
782
|
+
id: t.id,
|
|
783
|
+
title: (t.description || "").slice(0, 100),
|
|
784
|
+
description: t.description,
|
|
785
|
+
status: t.status,
|
|
786
|
+
agent: t.agent,
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if (conversation.ticketId) {
|
|
791
|
+
const ticketResult = await client.execute({
|
|
792
|
+
sql: `SELECT id, title, description, status FROM tickets WHERE id = ?`,
|
|
793
|
+
args: [conversation.ticketId],
|
|
794
|
+
});
|
|
795
|
+
if (ticketResult.rows.length > 0) {
|
|
796
|
+
const t = ticketResult.rows[0];
|
|
797
|
+
context.ticket = {
|
|
798
|
+
id: t.id,
|
|
799
|
+
title: t.title,
|
|
800
|
+
description: t.description,
|
|
801
|
+
status: t.status,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
// Get recent activity for context
|
|
806
|
+
const statsResult = await client.execute(`
|
|
807
|
+
SELECT
|
|
808
|
+
(SELECT COUNT(*) FROM tasks WHERE status = 'completed') as completed,
|
|
809
|
+
(SELECT COUNT(*) FROM tasks WHERE status = 'pending') as pending
|
|
810
|
+
`);
|
|
811
|
+
const stats = statsResult.rows[0];
|
|
812
|
+
context.recentActivity = {
|
|
813
|
+
tasksCompleted: Number(stats.completed) || 0,
|
|
814
|
+
tasksPending: Number(stats.pending) || 0,
|
|
815
|
+
activeAgents: [],
|
|
816
|
+
};
|
|
817
|
+
// Add runtime info (model awareness like OpenClaw)
|
|
818
|
+
const sessionOverride = getSessionModel(conversationId);
|
|
819
|
+
// Use the auto-selected default provider when no model specified
|
|
820
|
+
const defaultProvider = aiProvider.getDefaultProvider();
|
|
821
|
+
const resolvedRef = aiProvider.resolveModel(sessionOverride || body.model || defaultProvider);
|
|
822
|
+
context.runtime = {
|
|
823
|
+
model: `${resolvedRef.provider}/${resolvedRef.model}`,
|
|
824
|
+
provider: resolvedRef.provider,
|
|
825
|
+
defaultModel: `${defaultProvider}/${resolvedRef.model}`,
|
|
826
|
+
conversationId,
|
|
827
|
+
sessionOverride,
|
|
828
|
+
};
|
|
829
|
+
// Build system prompt with context
|
|
830
|
+
const systemPrompt = await buildSystemPrompt(conversation.presetId, context);
|
|
831
|
+
// Get existing messages
|
|
832
|
+
const existingMessages = await getConversationMessages(conversationId);
|
|
833
|
+
// Save user message first
|
|
834
|
+
const userMessage = await addMessage({
|
|
835
|
+
conversationId,
|
|
836
|
+
role: "user",
|
|
837
|
+
content: body.content,
|
|
838
|
+
});
|
|
839
|
+
// Add user message to list for compaction check
|
|
840
|
+
const allMessages = [
|
|
841
|
+
...existingMessages,
|
|
842
|
+
{
|
|
843
|
+
id: userMessage.id,
|
|
844
|
+
conversationId,
|
|
845
|
+
role: "user",
|
|
846
|
+
content: body.content,
|
|
847
|
+
createdAt: userMessage.createdAt,
|
|
848
|
+
},
|
|
849
|
+
];
|
|
850
|
+
// Check if compaction is needed and compact if necessary
|
|
851
|
+
let messagesToSend = allMessages;
|
|
852
|
+
let compactionInfo = null;
|
|
853
|
+
if (needsCompaction(allMessages, body.model)) {
|
|
854
|
+
logger.info(`[Chat] Compacting conversation ${conversationId} (${allMessages.length} messages)`);
|
|
855
|
+
const compactionResult = await compactMessages(allMessages, body.model);
|
|
856
|
+
if (compactionResult.wasCompacted) {
|
|
857
|
+
messagesToSend = compactionResult.messages;
|
|
858
|
+
compactionInfo = {
|
|
859
|
+
originalCount: compactionResult.originalCount,
|
|
860
|
+
compactedCount: compactionResult.compactedCount,
|
|
861
|
+
tokensReduced: compactionResult.tokensReduced,
|
|
862
|
+
};
|
|
863
|
+
logger.info(`[Chat] Compaction complete: ${compactionResult.originalCount} -> ${compactionResult.compactedCount} messages, ${compactionResult.tokensReduced} tokens saved`);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
// Build chat messages array from (potentially compacted) messages
|
|
867
|
+
const chatMessages = messagesToSend.map((m) => ({
|
|
868
|
+
id: m.id,
|
|
869
|
+
role: m.role,
|
|
870
|
+
content: m.content,
|
|
871
|
+
timestamp: m.createdAt,
|
|
872
|
+
}));
|
|
873
|
+
// Send to AI
|
|
874
|
+
const response = await aiProvider.chat({
|
|
875
|
+
messages: chatMessages,
|
|
876
|
+
model: body.model,
|
|
877
|
+
systemPrompt,
|
|
878
|
+
temperature: body.temperature,
|
|
879
|
+
});
|
|
880
|
+
// Save assistant response
|
|
881
|
+
const assistantMessage = await addMessage({
|
|
882
|
+
conversationId,
|
|
883
|
+
role: "assistant",
|
|
884
|
+
content: response.content,
|
|
885
|
+
model: response.model,
|
|
886
|
+
provider: response.provider,
|
|
887
|
+
tokenUsage: response.usage
|
|
888
|
+
? {
|
|
889
|
+
prompt: response.usage.promptTokens,
|
|
890
|
+
completion: response.usage.completionTokens,
|
|
891
|
+
total: response.usage.totalTokens,
|
|
892
|
+
}
|
|
893
|
+
: undefined,
|
|
894
|
+
cost: response.usage?.cost,
|
|
895
|
+
});
|
|
896
|
+
return c.json({
|
|
897
|
+
userMessage,
|
|
898
|
+
assistantMessage: {
|
|
899
|
+
...assistantMessage,
|
|
900
|
+
model: response.model,
|
|
901
|
+
provider: response.provider,
|
|
902
|
+
},
|
|
903
|
+
usage: response.usage,
|
|
904
|
+
compaction: compactionInfo,
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
catch (error) {
|
|
908
|
+
logger.error("[Chat] Conversation message error:", error instanceof Error ? error : undefined);
|
|
909
|
+
return c.json({
|
|
910
|
+
error: error instanceof Error ? error.message : "Failed to send message",
|
|
911
|
+
}, 500);
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
/**
|
|
915
|
+
* POST /api/chat/conversations/:id/messages/with-tools
|
|
916
|
+
* Send a message in a conversation with native tool calling enabled
|
|
917
|
+
*/
|
|
918
|
+
chatRoutes.post("/conversations/:id/messages/with-tools", zValidator("json", z.object({
|
|
919
|
+
content: z.string(),
|
|
920
|
+
model: z.string().optional(),
|
|
921
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
922
|
+
enableTools: z.boolean().optional().default(true),
|
|
923
|
+
})), async (c) => {
|
|
924
|
+
const conversationId = c.req.param("id");
|
|
925
|
+
const body = c.req.valid("json");
|
|
926
|
+
try {
|
|
927
|
+
// Get conversation and build context
|
|
928
|
+
const conversation = await getConversation(conversationId);
|
|
929
|
+
if (!conversation) {
|
|
930
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
931
|
+
}
|
|
932
|
+
// Build context from linked entities
|
|
933
|
+
const context = {};
|
|
934
|
+
const client = getClient();
|
|
935
|
+
if (conversation.taskId) {
|
|
936
|
+
const taskResult = await client.execute({
|
|
937
|
+
sql: `SELECT id, description, status, assigned_agent as agent FROM tasks WHERE id = ?`,
|
|
938
|
+
args: [conversation.taskId],
|
|
939
|
+
});
|
|
940
|
+
if (taskResult.rows.length > 0) {
|
|
941
|
+
const t = taskResult.rows[0];
|
|
942
|
+
context.task = {
|
|
943
|
+
id: t.id,
|
|
944
|
+
title: (t.description || "").slice(0, 100),
|
|
945
|
+
description: t.description,
|
|
946
|
+
status: t.status,
|
|
947
|
+
agent: t.agent,
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (conversation.ticketId) {
|
|
952
|
+
const ticketResult = await client.execute({
|
|
953
|
+
sql: `SELECT id, title, description, status FROM tickets WHERE id = ?`,
|
|
954
|
+
args: [conversation.ticketId],
|
|
955
|
+
});
|
|
956
|
+
if (ticketResult.rows.length > 0) {
|
|
957
|
+
const t = ticketResult.rows[0];
|
|
958
|
+
context.ticket = {
|
|
959
|
+
id: t.id,
|
|
960
|
+
title: t.title,
|
|
961
|
+
description: t.description,
|
|
962
|
+
status: t.status,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
// Get recent activity for context
|
|
967
|
+
const statsResult = await client.execute(`
|
|
968
|
+
SELECT
|
|
969
|
+
(SELECT COUNT(*) FROM tasks WHERE status = 'completed') as completed,
|
|
970
|
+
(SELECT COUNT(*) FROM tasks WHERE status = 'pending') as pending
|
|
971
|
+
`);
|
|
972
|
+
const stats = statsResult.rows[0];
|
|
973
|
+
context.recentActivity = {
|
|
974
|
+
tasksCompleted: Number(stats.completed) || 0,
|
|
975
|
+
tasksPending: Number(stats.pending) || 0,
|
|
976
|
+
activeAgents: [],
|
|
977
|
+
};
|
|
978
|
+
// Add runtime info (model awareness like OpenClaw)
|
|
979
|
+
const sessionOverride = getSessionModel(conversationId);
|
|
980
|
+
// Use the auto-selected default provider when no model specified
|
|
981
|
+
const defaultProvider = aiProvider.getDefaultProvider();
|
|
982
|
+
const resolvedRef = aiProvider.resolveModel(sessionOverride || body.model || defaultProvider);
|
|
983
|
+
context.runtime = {
|
|
984
|
+
model: `${resolvedRef.provider}/${resolvedRef.model}`,
|
|
985
|
+
provider: resolvedRef.provider,
|
|
986
|
+
defaultModel: `${defaultProvider}/${resolvedRef.model}`,
|
|
987
|
+
conversationId,
|
|
988
|
+
sessionOverride,
|
|
989
|
+
};
|
|
990
|
+
// Get available tools - model-aware when model ID is known
|
|
991
|
+
const enableTools = body.enableTools ?? true;
|
|
992
|
+
const modelId = resolvedRef.model;
|
|
993
|
+
const tools = enableTools
|
|
994
|
+
? getChatToolsForModel(modelId, { conversationId })
|
|
995
|
+
: [];
|
|
996
|
+
// Build system prompt with context and tool mode
|
|
997
|
+
const systemPrompt = await buildSystemPrompt(conversation.presetId, context, {
|
|
998
|
+
enableTools: enableTools && tools.length > 0,
|
|
999
|
+
});
|
|
1000
|
+
// Get existing messages
|
|
1001
|
+
const existingMessages = await getConversationMessages(conversationId);
|
|
1002
|
+
// Save user message first
|
|
1003
|
+
const userMessage = await addMessage({
|
|
1004
|
+
conversationId,
|
|
1005
|
+
role: "user",
|
|
1006
|
+
content: body.content,
|
|
1007
|
+
});
|
|
1008
|
+
// Add user message to list for API call
|
|
1009
|
+
const allMessages = [
|
|
1010
|
+
...existingMessages,
|
|
1011
|
+
{
|
|
1012
|
+
id: userMessage.id,
|
|
1013
|
+
conversationId,
|
|
1014
|
+
role: "user",
|
|
1015
|
+
content: body.content,
|
|
1016
|
+
createdAt: userMessage.createdAt,
|
|
1017
|
+
},
|
|
1018
|
+
];
|
|
1019
|
+
// Check if compaction is needed
|
|
1020
|
+
let messagesToSend = allMessages;
|
|
1021
|
+
let compactionInfo = null;
|
|
1022
|
+
if (needsCompaction(allMessages, body.model)) {
|
|
1023
|
+
logger.info(`[Chat] Compacting conversation ${conversationId} (${allMessages.length} messages)`);
|
|
1024
|
+
const compactionResult = await compactMessages(allMessages, body.model);
|
|
1025
|
+
if (compactionResult.wasCompacted) {
|
|
1026
|
+
messagesToSend = compactionResult.messages;
|
|
1027
|
+
compactionInfo = {
|
|
1028
|
+
originalCount: compactionResult.originalCount,
|
|
1029
|
+
compactedCount: compactionResult.compactedCount,
|
|
1030
|
+
tokensReduced: compactionResult.tokensReduced,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
// Build chat messages array
|
|
1035
|
+
const chatMessages = messagesToSend.map((m) => ({
|
|
1036
|
+
id: m.id,
|
|
1037
|
+
role: m.role,
|
|
1038
|
+
content: m.content,
|
|
1039
|
+
timestamp: m.createdAt,
|
|
1040
|
+
}));
|
|
1041
|
+
// Create tool handler for this conversation
|
|
1042
|
+
const toolHandler = await createChatToolHandler({
|
|
1043
|
+
conversationId,
|
|
1044
|
+
securityMode: "ask",
|
|
1045
|
+
});
|
|
1046
|
+
// Send to AI with native tool support
|
|
1047
|
+
const response = await aiProvider.chatWithNativeTools({
|
|
1048
|
+
messages: chatMessages,
|
|
1049
|
+
model: body.model,
|
|
1050
|
+
systemPrompt,
|
|
1051
|
+
temperature: body.temperature,
|
|
1052
|
+
tools,
|
|
1053
|
+
onToolCall: async (toolName, args, toolCallId) => {
|
|
1054
|
+
return toolHandler.executeTool(toolName, args, toolCallId);
|
|
1055
|
+
},
|
|
1056
|
+
maxToolRoundtrips: 5,
|
|
1057
|
+
});
|
|
1058
|
+
// Get any pending approvals
|
|
1059
|
+
const pendingApprovals = toolHandler.getPendingApprovals();
|
|
1060
|
+
const inferToolCallStatus = (result) => {
|
|
1061
|
+
if (!result || typeof result !== "object") {
|
|
1062
|
+
return "success";
|
|
1063
|
+
}
|
|
1064
|
+
const record = result;
|
|
1065
|
+
if (record.pending === true) {
|
|
1066
|
+
return "pending";
|
|
1067
|
+
}
|
|
1068
|
+
if (record.success === false || record.error) {
|
|
1069
|
+
return "error";
|
|
1070
|
+
}
|
|
1071
|
+
return "success";
|
|
1072
|
+
};
|
|
1073
|
+
// Build toolCalls array for storage
|
|
1074
|
+
const toolCallsForStorage = response.toolCalls?.map((tc) => {
|
|
1075
|
+
const toolResult = response.toolResults?.find((tr) => tr.toolCallId === tc.id)?.result;
|
|
1076
|
+
return {
|
|
1077
|
+
id: tc.id,
|
|
1078
|
+
name: tc.name,
|
|
1079
|
+
arguments: tc.arguments,
|
|
1080
|
+
result: toolResult,
|
|
1081
|
+
status: inferToolCallStatus(toolResult),
|
|
1082
|
+
};
|
|
1083
|
+
});
|
|
1084
|
+
// Save assistant response with toolCalls
|
|
1085
|
+
const assistantMessage = await addMessage({
|
|
1086
|
+
conversationId,
|
|
1087
|
+
role: "assistant",
|
|
1088
|
+
content: response.content,
|
|
1089
|
+
model: response.model,
|
|
1090
|
+
provider: response.provider,
|
|
1091
|
+
tokenUsage: response.usage
|
|
1092
|
+
? {
|
|
1093
|
+
prompt: response.usage.promptTokens,
|
|
1094
|
+
completion: response.usage.completionTokens,
|
|
1095
|
+
total: response.usage.totalTokens,
|
|
1096
|
+
}
|
|
1097
|
+
: undefined,
|
|
1098
|
+
cost: response.usage?.cost,
|
|
1099
|
+
toolCalls: toolCallsForStorage,
|
|
1100
|
+
});
|
|
1101
|
+
return c.json({
|
|
1102
|
+
userMessage,
|
|
1103
|
+
assistantMessage: {
|
|
1104
|
+
...assistantMessage,
|
|
1105
|
+
model: response.model,
|
|
1106
|
+
provider: response.provider,
|
|
1107
|
+
},
|
|
1108
|
+
usage: response.usage,
|
|
1109
|
+
compaction: compactionInfo,
|
|
1110
|
+
toolCalls: toolCallsForStorage,
|
|
1111
|
+
pendingApprovals: pendingApprovals.length > 0
|
|
1112
|
+
? pendingApprovals.map((a) => ({
|
|
1113
|
+
id: a.id,
|
|
1114
|
+
toolName: a.toolName,
|
|
1115
|
+
params: a.params,
|
|
1116
|
+
securityLevel: "moderate",
|
|
1117
|
+
}))
|
|
1118
|
+
: undefined,
|
|
1119
|
+
// Tool support info for UI warnings
|
|
1120
|
+
toolSupport: response.toolSupport,
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
catch (error) {
|
|
1124
|
+
logger.error("[Chat] Conversation message with tools error:", error instanceof Error ? error : undefined);
|
|
1125
|
+
return c.json({
|
|
1126
|
+
error: error instanceof Error ? error.message : "Failed to send message",
|
|
1127
|
+
}, 500);
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
/**
|
|
1131
|
+
* POST /api/chat/smart
|
|
1132
|
+
* Smart chat with automatic context injection
|
|
1133
|
+
* (Simpler alternative to conversation-based chat)
|
|
1134
|
+
*/
|
|
1135
|
+
chatRoutes.post("/smart", zValidator("json", z.object({
|
|
1136
|
+
messages: z.array(ChatMessageSchema),
|
|
1137
|
+
model: z.string().optional(),
|
|
1138
|
+
presetId: z.string().optional(),
|
|
1139
|
+
taskId: z.string().optional(),
|
|
1140
|
+
ticketId: z.string().optional(),
|
|
1141
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
1142
|
+
})), async (c) => {
|
|
1143
|
+
const body = c.req.valid("json");
|
|
1144
|
+
try {
|
|
1145
|
+
// Build context
|
|
1146
|
+
const context = {};
|
|
1147
|
+
const client = getClient();
|
|
1148
|
+
if (body.taskId) {
|
|
1149
|
+
const taskResult = await client.execute({
|
|
1150
|
+
sql: `SELECT id, description, status, assigned_agent as agent FROM tasks WHERE id = ?`,
|
|
1151
|
+
args: [body.taskId],
|
|
1152
|
+
});
|
|
1153
|
+
if (taskResult.rows.length > 0) {
|
|
1154
|
+
const t = taskResult.rows[0];
|
|
1155
|
+
context.task = {
|
|
1156
|
+
id: t.id,
|
|
1157
|
+
title: (t.description || "").slice(0, 100),
|
|
1158
|
+
description: t.description,
|
|
1159
|
+
status: t.status,
|
|
1160
|
+
agent: t.agent,
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
if (body.ticketId) {
|
|
1165
|
+
const ticketResult = await client.execute({
|
|
1166
|
+
sql: `SELECT id, title, description, status FROM tickets WHERE id = ?`,
|
|
1167
|
+
args: [body.ticketId],
|
|
1168
|
+
});
|
|
1169
|
+
if (ticketResult.rows.length > 0) {
|
|
1170
|
+
const t = ticketResult.rows[0];
|
|
1171
|
+
context.ticket = {
|
|
1172
|
+
id: t.id,
|
|
1173
|
+
title: t.title,
|
|
1174
|
+
description: t.description,
|
|
1175
|
+
status: t.status,
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
// Build system prompt
|
|
1180
|
+
const systemPrompt = await buildSystemPrompt(body.presetId || "profclaw-assistant", context);
|
|
1181
|
+
// Convert messages
|
|
1182
|
+
const messages = body.messages.map((msg) => ({
|
|
1183
|
+
id: randomUUID(),
|
|
1184
|
+
role: msg.role,
|
|
1185
|
+
content: msg.content,
|
|
1186
|
+
timestamp: new Date().toISOString(),
|
|
1187
|
+
}));
|
|
1188
|
+
// Send to AI
|
|
1189
|
+
const response = await aiProvider.chat({
|
|
1190
|
+
messages,
|
|
1191
|
+
model: body.model,
|
|
1192
|
+
systemPrompt,
|
|
1193
|
+
temperature: body.temperature,
|
|
1194
|
+
});
|
|
1195
|
+
return c.json({
|
|
1196
|
+
id: response.id,
|
|
1197
|
+
provider: response.provider,
|
|
1198
|
+
model: response.model,
|
|
1199
|
+
message: {
|
|
1200
|
+
role: "assistant",
|
|
1201
|
+
content: response.content,
|
|
1202
|
+
},
|
|
1203
|
+
finishReason: response.finishReason,
|
|
1204
|
+
usage: response.usage,
|
|
1205
|
+
duration: response.duration,
|
|
1206
|
+
context: {
|
|
1207
|
+
presetId: body.presetId || "profclaw-assistant",
|
|
1208
|
+
taskId: body.taskId,
|
|
1209
|
+
ticketId: body.ticketId,
|
|
1210
|
+
},
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
logger.error("[Chat] Smart chat error:", error instanceof Error ? error : undefined);
|
|
1215
|
+
return c.json({ error: error instanceof Error ? error.message : "Smart chat failed" }, 500);
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
// Chat with Tools
|
|
1219
|
+
/**
|
|
1220
|
+
* POST /api/chat/with-tools
|
|
1221
|
+
* Chat with AI tools enabled (file operations, git, system info, etc.)
|
|
1222
|
+
*/
|
|
1223
|
+
chatRoutes.post("/with-tools", zValidator("json", z.object({
|
|
1224
|
+
messages: z.array(ChatMessageSchema),
|
|
1225
|
+
conversationId: z.string().optional(),
|
|
1226
|
+
model: z.string().optional(),
|
|
1227
|
+
systemPrompt: z.string().optional(),
|
|
1228
|
+
presetId: z.string().optional(),
|
|
1229
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
1230
|
+
enableAllTools: z.boolean().optional(), // Enable all tools vs safe subset
|
|
1231
|
+
securityMode: z
|
|
1232
|
+
.enum(["deny", "sandbox", "allowlist", "ask", "full"])
|
|
1233
|
+
.optional(),
|
|
1234
|
+
workdir: z.string().optional(),
|
|
1235
|
+
})), async (c) => {
|
|
1236
|
+
const body = c.req.valid("json");
|
|
1237
|
+
const conversationId = body.conversationId || randomUUID();
|
|
1238
|
+
try {
|
|
1239
|
+
// Create tool handler with security settings
|
|
1240
|
+
const toolHandler = await createChatToolHandler({
|
|
1241
|
+
conversationId,
|
|
1242
|
+
workdir: body.workdir,
|
|
1243
|
+
securityMode: body.securityMode || "ask",
|
|
1244
|
+
});
|
|
1245
|
+
// Get tools based on user preference
|
|
1246
|
+
const tools = body.enableAllTools
|
|
1247
|
+
? getAllChatTools()
|
|
1248
|
+
: getDefaultChatTools();
|
|
1249
|
+
// Build system prompt with tools enabled (this endpoint always has tools)
|
|
1250
|
+
let systemPrompt = body.systemPrompt || "";
|
|
1251
|
+
if (body.presetId) {
|
|
1252
|
+
systemPrompt =
|
|
1253
|
+
(await buildSystemPrompt(body.presetId, {}, { enableTools: true })) +
|
|
1254
|
+
"\n\n" +
|
|
1255
|
+
systemPrompt;
|
|
1256
|
+
}
|
|
1257
|
+
else if (!systemPrompt) {
|
|
1258
|
+
// Even without preset, add tool-enabled system prompt
|
|
1259
|
+
systemPrompt = await buildSystemPrompt("profclaw-assistant", {}, { enableTools: true });
|
|
1260
|
+
}
|
|
1261
|
+
// Convert messages
|
|
1262
|
+
const messages = body.messages.map((msg) => ({
|
|
1263
|
+
id: randomUUID(),
|
|
1264
|
+
role: msg.role,
|
|
1265
|
+
content: msg.content,
|
|
1266
|
+
timestamp: new Date().toISOString(),
|
|
1267
|
+
}));
|
|
1268
|
+
// Call AI with native tool support
|
|
1269
|
+
const response = await aiProvider.chatWithNativeTools({
|
|
1270
|
+
messages,
|
|
1271
|
+
model: body.model,
|
|
1272
|
+
systemPrompt,
|
|
1273
|
+
temperature: body.temperature,
|
|
1274
|
+
tools,
|
|
1275
|
+
onToolCall: async (toolName, args, toolCallId) => {
|
|
1276
|
+
return toolHandler.executeTool(toolName, args, toolCallId);
|
|
1277
|
+
},
|
|
1278
|
+
maxToolRoundtrips: 5,
|
|
1279
|
+
});
|
|
1280
|
+
// Check for pending approvals
|
|
1281
|
+
const pendingApprovals = toolHandler.getPendingApprovals();
|
|
1282
|
+
return c.json({
|
|
1283
|
+
id: response.id,
|
|
1284
|
+
conversationId,
|
|
1285
|
+
provider: response.provider,
|
|
1286
|
+
model: response.model,
|
|
1287
|
+
message: {
|
|
1288
|
+
role: "assistant",
|
|
1289
|
+
content: response.content,
|
|
1290
|
+
},
|
|
1291
|
+
finishReason: response.finishReason,
|
|
1292
|
+
usage: response.usage,
|
|
1293
|
+
duration: response.duration,
|
|
1294
|
+
// Tool execution info
|
|
1295
|
+
toolCalls: response.toolCalls,
|
|
1296
|
+
toolResults: response.toolResults,
|
|
1297
|
+
pendingApprovals: pendingApprovals.length > 0 ? pendingApprovals : undefined,
|
|
1298
|
+
steps: response.steps,
|
|
1299
|
+
// Tool support info for UI warnings
|
|
1300
|
+
toolSupport: response.toolSupport,
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
catch (error) {
|
|
1304
|
+
logger.error("[Chat] Chat with tools error:", error instanceof Error ? error : undefined);
|
|
1305
|
+
return c.json({
|
|
1306
|
+
error: error instanceof Error ? error.message : "Chat with tools failed",
|
|
1307
|
+
}, 500);
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
/**
|
|
1311
|
+
* POST /api/chat/conversations/:id/messages/agentic
|
|
1312
|
+
* Send a message in agentic mode with SSE streaming for real-time updates.
|
|
1313
|
+
*
|
|
1314
|
+
* This endpoint runs the AI in autonomous mode, executing multiple tools
|
|
1315
|
+
* until the task is complete. Events are streamed via SSE including:
|
|
1316
|
+
* - session:start - Agent session started
|
|
1317
|
+
* - thinking:start/update/end - AI reasoning (if enabled)
|
|
1318
|
+
* - step:start/complete - Step progress
|
|
1319
|
+
* - tool:call - Tool being called
|
|
1320
|
+
* - tool:result - Tool execution result
|
|
1321
|
+
* - summary - Final task summary
|
|
1322
|
+
* - complete - Session complete
|
|
1323
|
+
* - error - Error occurred
|
|
1324
|
+
*/
|
|
1325
|
+
chatRoutes.post("/conversations/:id/messages/agentic", zValidator("json", z.object({
|
|
1326
|
+
content: z.string(),
|
|
1327
|
+
model: z.string().optional(),
|
|
1328
|
+
provider: z.string().optional(),
|
|
1329
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
1330
|
+
showThinking: z.boolean().optional().default(true),
|
|
1331
|
+
maxSteps: z.number().min(1).max(200).optional(),
|
|
1332
|
+
maxBudget: z.number().min(1000).optional(),
|
|
1333
|
+
effort: z.enum(['low', 'medium', 'high', 'max']).optional(),
|
|
1334
|
+
})), async (c) => {
|
|
1335
|
+
const conversationId = c.req.param("id");
|
|
1336
|
+
const body = c.req.valid("json");
|
|
1337
|
+
try {
|
|
1338
|
+
// Get conversation
|
|
1339
|
+
const conversation = await getConversation(conversationId);
|
|
1340
|
+
if (!conversation) {
|
|
1341
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
1342
|
+
}
|
|
1343
|
+
// Build context from linked entities
|
|
1344
|
+
const context = {};
|
|
1345
|
+
const client = getClient();
|
|
1346
|
+
if (conversation.taskId) {
|
|
1347
|
+
const taskResult = await client.execute({
|
|
1348
|
+
sql: `SELECT id, description, status, assigned_agent as agent FROM tasks WHERE id = ?`,
|
|
1349
|
+
args: [conversation.taskId],
|
|
1350
|
+
});
|
|
1351
|
+
if (taskResult.rows.length > 0) {
|
|
1352
|
+
const t = taskResult.rows[0];
|
|
1353
|
+
context.task = {
|
|
1354
|
+
id: t.id,
|
|
1355
|
+
title: (t.description || "").slice(0, 100),
|
|
1356
|
+
description: t.description,
|
|
1357
|
+
status: t.status,
|
|
1358
|
+
agent: t.agent,
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
if (conversation.ticketId) {
|
|
1363
|
+
const ticketResult = await client.execute({
|
|
1364
|
+
sql: `SELECT id, title, description, status FROM tickets WHERE id = ?`,
|
|
1365
|
+
args: [conversation.ticketId],
|
|
1366
|
+
});
|
|
1367
|
+
if (ticketResult.rows.length > 0) {
|
|
1368
|
+
const t = ticketResult.rows[0];
|
|
1369
|
+
context.ticket = {
|
|
1370
|
+
id: t.id,
|
|
1371
|
+
title: t.title,
|
|
1372
|
+
description: t.description,
|
|
1373
|
+
status: t.status,
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
// Add runtime info
|
|
1378
|
+
const sessionOverride = getSessionModel(conversationId);
|
|
1379
|
+
const defaultProvider = aiProvider.getDefaultProvider();
|
|
1380
|
+
const resolvedRef = aiProvider.resolveModel(sessionOverride || body.model || defaultProvider);
|
|
1381
|
+
context.runtime = {
|
|
1382
|
+
model: `${resolvedRef.provider}/${resolvedRef.model}`,
|
|
1383
|
+
provider: resolvedRef.provider,
|
|
1384
|
+
defaultModel: `${defaultProvider}/${resolvedRef.model}`,
|
|
1385
|
+
conversationId,
|
|
1386
|
+
sessionOverride,
|
|
1387
|
+
};
|
|
1388
|
+
// Get tools - agentic mode uses model-aware filtering
|
|
1389
|
+
const tools = getChatToolsForModel(resolvedRef.model, { conversationId, includeAll: true });
|
|
1390
|
+
// Build system prompt with agent mode (uses AGENT_MODE_SUFFIX)
|
|
1391
|
+
const systemPrompt = await buildSystemPrompt(conversation.presetId, context, {
|
|
1392
|
+
agentMode: true,
|
|
1393
|
+
});
|
|
1394
|
+
// Get existing messages
|
|
1395
|
+
const existingMessages = await getConversationMessages(conversationId);
|
|
1396
|
+
// Save user message first
|
|
1397
|
+
const userMessage = await addMessage({
|
|
1398
|
+
conversationId,
|
|
1399
|
+
role: "user",
|
|
1400
|
+
content: body.content,
|
|
1401
|
+
});
|
|
1402
|
+
// Build all messages for compaction check
|
|
1403
|
+
const allMessages = [
|
|
1404
|
+
...existingMessages,
|
|
1405
|
+
{
|
|
1406
|
+
id: userMessage.id,
|
|
1407
|
+
conversationId,
|
|
1408
|
+
role: "user",
|
|
1409
|
+
content: body.content,
|
|
1410
|
+
createdAt: userMessage.createdAt,
|
|
1411
|
+
},
|
|
1412
|
+
];
|
|
1413
|
+
// Apply context pruning if conversation is getting long
|
|
1414
|
+
let messagesToSend = allMessages;
|
|
1415
|
+
let compactionApplied = false;
|
|
1416
|
+
if (needsCompaction(allMessages, body.model)) {
|
|
1417
|
+
logger.info(`[Chat/Agentic] Compacting conversation ${conversationId} (${allMessages.length} messages)`);
|
|
1418
|
+
const compactionResult = await compactMessages(allMessages, body.model);
|
|
1419
|
+
if (compactionResult.wasCompacted) {
|
|
1420
|
+
messagesToSend = compactionResult.messages;
|
|
1421
|
+
compactionApplied = true;
|
|
1422
|
+
logger.info(`[Chat/Agentic] Compaction complete: ${compactionResult.originalCount} -> ${compactionResult.compactedCount} messages, ${compactionResult.tokensReduced} tokens saved`);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
// Build chat messages from (potentially compacted) messages
|
|
1426
|
+
const chatMessages = messagesToSend.map((m) => ({
|
|
1427
|
+
id: m.id,
|
|
1428
|
+
role: m.role,
|
|
1429
|
+
content: m.content,
|
|
1430
|
+
timestamp: m.createdAt,
|
|
1431
|
+
}));
|
|
1432
|
+
// Create tool handler
|
|
1433
|
+
const toolHandler = await createChatToolHandler({
|
|
1434
|
+
conversationId,
|
|
1435
|
+
securityMode: "full", // Full access in agentic mode (tools are pre-approved)
|
|
1436
|
+
});
|
|
1437
|
+
// Set up SSE streaming response
|
|
1438
|
+
c.header("Content-Type", "text/event-stream");
|
|
1439
|
+
c.header("Cache-Control", "no-cache");
|
|
1440
|
+
c.header("Connection", "keep-alive");
|
|
1441
|
+
c.header("X-Accel-Buffering", "no");
|
|
1442
|
+
return streamText(c, async (stream) => {
|
|
1443
|
+
// Overall timeout for the agentic session (3 minutes)
|
|
1444
|
+
const AGENTIC_TIMEOUT_MS = 3 * 60 * 1000;
|
|
1445
|
+
let timedOut = false;
|
|
1446
|
+
const timeoutId = setTimeout(async () => {
|
|
1447
|
+
timedOut = true;
|
|
1448
|
+
logger.warn("[Chat/Agentic] Session timed out", {
|
|
1449
|
+
conversationId,
|
|
1450
|
+
timeoutMs: AGENTIC_TIMEOUT_MS,
|
|
1451
|
+
});
|
|
1452
|
+
try {
|
|
1453
|
+
await stream.write(`data: ${JSON.stringify({
|
|
1454
|
+
type: "error",
|
|
1455
|
+
data: {
|
|
1456
|
+
message: "Agentic session timed out after 3 minutes. The task may be too complex — try breaking it into smaller steps.",
|
|
1457
|
+
code: "TIMEOUT",
|
|
1458
|
+
},
|
|
1459
|
+
timestamp: Date.now(),
|
|
1460
|
+
})}\n\n`);
|
|
1461
|
+
}
|
|
1462
|
+
catch {
|
|
1463
|
+
// Stream may already be closed
|
|
1464
|
+
}
|
|
1465
|
+
}, AGENTIC_TIMEOUT_MS);
|
|
1466
|
+
// Send initial event with user message and compaction info
|
|
1467
|
+
await stream.write(`data: ${JSON.stringify({
|
|
1468
|
+
type: "user_message",
|
|
1469
|
+
data: {
|
|
1470
|
+
id: userMessage.id,
|
|
1471
|
+
content: body.content,
|
|
1472
|
+
compactionApplied,
|
|
1473
|
+
messageCount: messagesToSend.length,
|
|
1474
|
+
},
|
|
1475
|
+
timestamp: Date.now(),
|
|
1476
|
+
})}\n\n`);
|
|
1477
|
+
let lastAssistantContent = "";
|
|
1478
|
+
let totalTokens = 0;
|
|
1479
|
+
let inputTokensTotal;
|
|
1480
|
+
let outputTokensTotal;
|
|
1481
|
+
let finalModel = "";
|
|
1482
|
+
let finalProvider = "";
|
|
1483
|
+
let collectedToolCalls = [];
|
|
1484
|
+
try {
|
|
1485
|
+
// Run the streaming agentic chat
|
|
1486
|
+
for await (const event of streamAgenticChat({
|
|
1487
|
+
conversationId,
|
|
1488
|
+
messages: chatMessages,
|
|
1489
|
+
systemPrompt,
|
|
1490
|
+
model: body.model,
|
|
1491
|
+
provider: body.provider,
|
|
1492
|
+
temperature: body.temperature,
|
|
1493
|
+
toolHandler,
|
|
1494
|
+
tools: tools.map((t) => ({
|
|
1495
|
+
name: t.name,
|
|
1496
|
+
description: t.description,
|
|
1497
|
+
parameters: t.parameters,
|
|
1498
|
+
})),
|
|
1499
|
+
showThinking: body.showThinking,
|
|
1500
|
+
maxSteps: body.maxSteps,
|
|
1501
|
+
maxBudget: body.maxBudget,
|
|
1502
|
+
effort: body.effort,
|
|
1503
|
+
})) {
|
|
1504
|
+
// Check if we've timed out
|
|
1505
|
+
if (timedOut)
|
|
1506
|
+
break;
|
|
1507
|
+
// Stream each event to the client
|
|
1508
|
+
await stream.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
1509
|
+
// Track data for final message save
|
|
1510
|
+
if (event.type === "summary") {
|
|
1511
|
+
const summaryData = event.data;
|
|
1512
|
+
lastAssistantContent = summaryData.summary;
|
|
1513
|
+
}
|
|
1514
|
+
if (event.type === "complete") {
|
|
1515
|
+
const completeData = event.data;
|
|
1516
|
+
totalTokens = completeData.totalTokens;
|
|
1517
|
+
inputTokensTotal = completeData.inputTokens;
|
|
1518
|
+
outputTokensTotal = completeData.outputTokens;
|
|
1519
|
+
finalModel = completeData.model;
|
|
1520
|
+
finalProvider = completeData.provider;
|
|
1521
|
+
collectedToolCalls = completeData.toolCalls || [];
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
// Clear the timeout - we completed normally
|
|
1525
|
+
clearTimeout(timeoutId);
|
|
1526
|
+
// Save assistant response after streaming completes
|
|
1527
|
+
if (lastAssistantContent) {
|
|
1528
|
+
const promptTokens = inputTokensTotal ?? Math.floor(totalTokens * 0.7);
|
|
1529
|
+
const completionTokens = outputTokensTotal ?? (totalTokens - promptTokens);
|
|
1530
|
+
const assistantMessage = await addMessage({
|
|
1531
|
+
conversationId,
|
|
1532
|
+
role: "assistant",
|
|
1533
|
+
content: lastAssistantContent,
|
|
1534
|
+
model: finalModel,
|
|
1535
|
+
provider: finalProvider,
|
|
1536
|
+
tokenUsage: totalTokens > 0
|
|
1537
|
+
? {
|
|
1538
|
+
prompt: promptTokens,
|
|
1539
|
+
completion: completionTokens,
|
|
1540
|
+
total: totalTokens,
|
|
1541
|
+
}
|
|
1542
|
+
: undefined,
|
|
1543
|
+
toolCalls: collectedToolCalls.length > 0 ? collectedToolCalls : undefined,
|
|
1544
|
+
});
|
|
1545
|
+
// Track in-memory usage for cost dashboard
|
|
1546
|
+
if (totalTokens > 0 && finalModel) {
|
|
1547
|
+
trackChatUsage(finalModel, totalTokens, inputTokensTotal, outputTokensTotal);
|
|
1548
|
+
}
|
|
1549
|
+
// Send final saved message event
|
|
1550
|
+
await stream.write(`data: ${JSON.stringify({
|
|
1551
|
+
type: "message_saved",
|
|
1552
|
+
data: { id: assistantMessage.id },
|
|
1553
|
+
timestamp: Date.now(),
|
|
1554
|
+
})}\n\n`);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
catch (error) {
|
|
1558
|
+
clearTimeout(timeoutId);
|
|
1559
|
+
logger.error("[Chat] Agentic streaming error:", error instanceof Error ? error : undefined);
|
|
1560
|
+
await stream.write(`data: ${JSON.stringify({
|
|
1561
|
+
type: "error",
|
|
1562
|
+
data: {
|
|
1563
|
+
message: error instanceof Error
|
|
1564
|
+
? error.message
|
|
1565
|
+
: "Agentic execution failed",
|
|
1566
|
+
},
|
|
1567
|
+
timestamp: Date.now(),
|
|
1568
|
+
})}\n\n`);
|
|
1569
|
+
}
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
catch (error) {
|
|
1573
|
+
logger.error("[Chat] Agentic chat setup error:", error instanceof Error ? error : undefined);
|
|
1574
|
+
return c.json({
|
|
1575
|
+
error: error instanceof Error
|
|
1576
|
+
? error.message
|
|
1577
|
+
: "Failed to start agentic chat",
|
|
1578
|
+
}, 500);
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
/**
|
|
1582
|
+
* GET /api/chat/tools
|
|
1583
|
+
* List available tools for chat
|
|
1584
|
+
*/
|
|
1585
|
+
chatRoutes.get("/tools", async (c) => {
|
|
1586
|
+
const allTools = c.req.query("all") === "true";
|
|
1587
|
+
const tools = allTools ? getAllChatTools() : getDefaultChatTools();
|
|
1588
|
+
return c.json({
|
|
1589
|
+
tools: tools.map((t) => ({
|
|
1590
|
+
name: t.name,
|
|
1591
|
+
description: t.description,
|
|
1592
|
+
})),
|
|
1593
|
+
total: tools.length,
|
|
1594
|
+
mode: allTools ? "all" : "default",
|
|
1595
|
+
});
|
|
1596
|
+
});
|
|
1597
|
+
/**
|
|
1598
|
+
* POST /api/chat/tools/approve
|
|
1599
|
+
* Approve a pending tool execution
|
|
1600
|
+
*/
|
|
1601
|
+
chatRoutes.post("/tools/approve", zValidator("json", z.object({
|
|
1602
|
+
conversationId: z.string(),
|
|
1603
|
+
approvalId: z.string(),
|
|
1604
|
+
decision: z.enum(["allow-once", "allow-always", "deny"]),
|
|
1605
|
+
})), async (c) => {
|
|
1606
|
+
const { conversationId, approvalId, decision } = c.req.valid("json");
|
|
1607
|
+
try {
|
|
1608
|
+
// Create handler for this conversation to access approvals
|
|
1609
|
+
const toolHandler = await createChatToolHandler({ conversationId });
|
|
1610
|
+
const result = await toolHandler.handleApproval(approvalId, decision);
|
|
1611
|
+
if (!result) {
|
|
1612
|
+
return c.json({ error: "Approval not found or expired" }, 404);
|
|
1613
|
+
}
|
|
1614
|
+
return c.json({
|
|
1615
|
+
success: true,
|
|
1616
|
+
result: result.result,
|
|
1617
|
+
decision,
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
catch (error) {
|
|
1621
|
+
logger.error("[Chat] Tool approval error:", error instanceof Error ? error : undefined);
|
|
1622
|
+
return c.json({ error: error instanceof Error ? error.message : "Approval failed" }, 500);
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
// Group Chat Routes
|
|
1626
|
+
/**
|
|
1627
|
+
* GET /api/chat/group/config
|
|
1628
|
+
* Get group chat configuration and channel personalities
|
|
1629
|
+
*/
|
|
1630
|
+
chatRoutes.get("/group/config", (c) => {
|
|
1631
|
+
const manager = getGroupChatManager();
|
|
1632
|
+
return c.json({
|
|
1633
|
+
mentionGating: true,
|
|
1634
|
+
threadingEnabled: true,
|
|
1635
|
+
rateLimiting: true,
|
|
1636
|
+
channelPersonalities: manager.getChannelPersonalities(),
|
|
1637
|
+
});
|
|
1638
|
+
});
|
|
1639
|
+
/**
|
|
1640
|
+
* POST /api/chat/group/personality
|
|
1641
|
+
* Set a channel-specific system prompt
|
|
1642
|
+
*/
|
|
1643
|
+
chatRoutes.post("/group/personality", zValidator("json", z.object({
|
|
1644
|
+
channelId: z.string().min(1),
|
|
1645
|
+
systemPrompt: z.string().min(1),
|
|
1646
|
+
})), async (c) => {
|
|
1647
|
+
const { channelId, systemPrompt } = c.req.valid("json");
|
|
1648
|
+
try {
|
|
1649
|
+
const manager = getGroupChatManager();
|
|
1650
|
+
manager.setChannelPersonality(channelId, systemPrompt);
|
|
1651
|
+
return c.json({ success: true });
|
|
1652
|
+
}
|
|
1653
|
+
catch (error) {
|
|
1654
|
+
logger.error("[Chat/Group] Set personality error:", error instanceof Error ? error : undefined);
|
|
1655
|
+
return c.json({ error: error instanceof Error ? error.message : "Failed to set personality" }, 500);
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
/**
|
|
1659
|
+
* POST /api/chat/group/rate-limit
|
|
1660
|
+
* Configure the per-minute rate limit for a channel
|
|
1661
|
+
*/
|
|
1662
|
+
chatRoutes.post("/group/rate-limit", zValidator("json", z.object({
|
|
1663
|
+
channelId: z.string().min(1),
|
|
1664
|
+
maxPerMinute: z.number().int().positive(),
|
|
1665
|
+
})), async (c) => {
|
|
1666
|
+
const { channelId, maxPerMinute } = c.req.valid("json");
|
|
1667
|
+
try {
|
|
1668
|
+
const manager = getGroupChatManager();
|
|
1669
|
+
manager.setRateLimit(channelId, maxPerMinute);
|
|
1670
|
+
return c.json({ success: true });
|
|
1671
|
+
}
|
|
1672
|
+
catch (error) {
|
|
1673
|
+
logger.error("[Chat/Group] Set rate limit error:", error instanceof Error ? error : undefined);
|
|
1674
|
+
return c.json({ error: error instanceof Error ? error.message : "Failed to set rate limit" }, 500);
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
export { chatRoutes };
|
|
1678
|
+
//# sourceMappingURL=chat.js.map
|