whale-code 6.5.11 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/chat/ChatApp.js +7 -11
- package/dist/cli/chat/ChatApp.js.map +1 -1
- package/dist/cli/chat/ChatInput.js +7 -3
- package/dist/cli/chat/ChatInput.js.map +1 -1
- package/dist/cli/chat/MessageList.js +5 -6
- package/dist/cli/chat/MessageList.js.map +1 -1
- package/dist/cli/chat/StatusBar.d.ts +2 -2
- package/dist/cli/chat/StatusBar.js +90 -160
- package/dist/cli/chat/StatusBar.js.map +1 -1
- package/dist/cli/chat/components/LiveArea.js +78 -115
- package/dist/cli/chat/components/LiveArea.js.map +1 -1
- package/dist/cli/chat/components/StaticMessages.js +60 -79
- package/dist/cli/chat/components/StaticMessages.js.map +1 -1
- package/dist/cli/chat/hooks/useAgentLoop.js +45 -37
- package/dist/cli/chat/hooks/useAgentLoop.js.map +1 -1
- package/dist/cli/chat/store.d.ts +12 -0
- package/dist/cli/chat/store.js +19 -0
- package/dist/cli/chat/store.js.map +1 -1
- package/dist/cli/services/agent-loop-tools.js +10 -1
- package/dist/cli/services/agent-loop-tools.js.map +1 -1
- package/dist/cli/services/cli-agent-loop.js +3 -2
- package/dist/cli/services/cli-agent-loop.js.map +1 -1
- package/dist/cli/services/config-store.js +4 -3
- package/dist/cli/services/config-store.js.map +1 -1
- package/dist/cli/services/memory-manager.js +2 -2
- package/dist/cli/services/memory-manager.js.map +1 -1
- package/dist/cli/services/permission-modes.js +14 -10
- package/dist/cli/services/permission-modes.js.map +1 -1
- package/dist/cli/services/session-client.js +2 -1
- package/dist/cli/services/session-client.js.map +1 -1
- package/dist/cli/services/session-persistence.js +14 -6
- package/dist/cli/services/session-persistence.js.map +1 -1
- package/dist/cli/shared/SpinnerSlot.js +4 -1
- package/dist/cli/shared/SpinnerSlot.js.map +1 -1
- package/dist/server/handlers/browser-lifecycle.js +10 -0
- package/dist/server/handlers/browser-lifecycle.js.map +1 -1
- package/dist/server/handlers/browser.js +16 -1
- package/dist/server/handlers/browser.js.map +1 -1
- package/dist/server/handlers/campaigns.js +11 -0
- package/dist/server/handlers/campaigns.js.map +1 -1
- package/dist/server/handlers/catalog-products.js +19 -5
- package/dist/server/handlers/catalog-products.js.map +1 -1
- package/dist/server/handlers/catalog.js +42 -8
- package/dist/server/handlers/catalog.js.map +1 -1
- package/dist/server/handlers/clickhouse.js +4 -4
- package/dist/server/handlers/clickhouse.js.map +1 -1
- package/dist/server/handlers/comms-email.js +70 -8
- package/dist/server/handlers/comms-email.js.map +1 -1
- package/dist/server/handlers/comms.js +63 -21
- package/dist/server/handlers/comms.js.map +1 -1
- package/dist/server/handlers/coupons.js +141 -77
- package/dist/server/handlers/coupons.js.map +1 -1
- package/dist/server/handlers/google-ads.js +280 -8
- package/dist/server/handlers/google-ads.js.map +1 -1
- package/dist/server/handlers/remove-bg.d.ts +33 -0
- package/dist/server/handlers/remove-bg.js +698 -44
- package/dist/server/handlers/remove-bg.js.map +1 -1
- package/dist/server/handlers/supply-chain.js +93 -1
- package/dist/server/handlers/supply-chain.js.map +1 -1
- package/dist/server/handlers/workflow-steps-types.d.ts +1 -1
- package/dist/server/handlers/workflow-steps-types.js +7 -1
- package/dist/server/handlers/workflow-steps-types.js.map +1 -1
- package/dist/server/handlers/workflow-steps.js +1 -1
- package/dist/server/handlers/workflow-steps.js.map +1 -1
- package/dist/server/index.js +122 -29
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent-loop-turn.js +33 -3
- package/dist/server/lib/agent-loop-turn.js.map +1 -1
- package/dist/server/lib/agent-loop-types.d.ts +6 -2
- package/dist/server/lib/agent-loop-types.js +14 -2
- package/dist/server/lib/agent-loop-types.js.map +1 -1
- package/dist/server/lib/clickhouse-client.js +4 -2
- package/dist/server/lib/clickhouse-client.js.map +1 -1
- package/dist/server/lib/code-worker.js +4 -1
- package/dist/server/lib/code-worker.js.map +1 -1
- package/dist/server/providers/anthropic.js +103 -33
- package/dist/server/providers/anthropic.js.map +1 -1
- package/dist/server/server-chat.js +2 -2
- package/dist/server/server-chat.js.map +1 -1
- package/dist/server/server-helpers.d.ts +8 -1
- package/dist/server/server-helpers.js +17 -3
- package/dist/server/server-helpers.js.map +1 -1
- package/dist/server/server-persist.js +34 -21
- package/dist/server/server-persist.js.map +1 -1
- package/dist/server/server-rate-limit.d.ts +0 -1
- package/dist/server/server-rate-limit.js +5 -5
- package/dist/server/server-rate-limit.js.map +1 -1
- package/dist/server/server-routes-approvals.js +2 -2
- package/dist/server/server-routes-approvals.js.map +1 -1
- package/dist/server/server-routes-auth.js +2 -2
- package/dist/server/server-routes-auth.js.map +1 -1
- package/dist/server/server-routes-events.js +2 -2
- package/dist/server/server-routes-events.js.map +1 -1
- package/dist/server/server-routes-public.js +4 -4
- package/dist/server/server-routes-public.js.map +1 -1
- package/dist/server/server-routes-webchat.js +3 -3
- package/dist/server/server-routes-webchat.js.map +1 -1
- package/dist/server/server-store-circuit-breaker.js +1 -1
- package/dist/server/server-store-circuit-breaker.js.map +1 -1
- package/dist/server/tool-router.js +7 -4
- package/dist/server/tool-router.js.map +1 -1
- package/dist/server/validation.js +11 -0
- package/dist/server/validation.js.map +1 -1
- package/dist/shared/api-client.js +38 -11
- package/dist/shared/api-client.js.map +1 -1
- package/package.json +12 -10
- package/vendor/ink/build/ink.js +68 -24
- package/vendor/ink/node_modules/react-devtools-core/README.md +152 -0
- package/vendor/ink/node_modules/react-devtools-core/backend.js +1 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/648.chunk.js +2 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/648.chunk.js.map +1 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/backend.js +15691 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/backend.js.map +1 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/importFile.worker.worker.js +2 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/importFile.worker.worker.js.map +1 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/parseSourceAndMetadata.worker.worker.js +14 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/parseSourceAndMetadata.worker.worker.js.map +1 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/standalone.js +2 -0
- package/vendor/ink/node_modules/react-devtools-core/dist/standalone.js.map +1 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/LICENSE +21 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/README.md +495 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/browser.js +8 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/index.js +10 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/buffer-util.js +129 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/constants.js +10 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/event-target.js +184 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/extension.js +223 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/limiter.js +55 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/permessage-deflate.js +518 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/receiver.js +607 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/sender.js +409 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/stream.js +180 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/validation.js +104 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/websocket-server.js +449 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/websocket.js +1197 -0
- package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/package.json +56 -0
- package/vendor/ink/node_modules/react-devtools-core/package.json +38 -0
- package/vendor/ink/node_modules/react-devtools-core/standalone.js +1 -0
- package/dist/cli/__tests__/print-mode-streaming.test.js +0 -270
- package/dist/cli/__tests__/print-mode.basic-output.test.js +0 -230
- package/dist/cli/__tests__/print-mode.session-errors.test.js +0 -252
- package/dist/cli/__tests__/print-mode.test.js +0 -273
- package/dist/cli/__tests__/serve-mode-messages.test.js +0 -338
- package/dist/cli/__tests__/serve-mode.messages.part2.test.js +0 -266
- package/dist/cli/__tests__/serve-mode.messages.test.js +0 -277
- package/dist/cli/__tests__/serve-mode.startup-http.test.js +0 -279
- package/dist/cli/__tests__/serve-mode.test.js +0 -345
- package/dist/cli/chat/NodeManager.d.ts +0 -30
- package/dist/cli/chat/NodeManager.js +0 -66
- package/dist/cli/chat/NodeManager.js.map +0 -1
- package/dist/cli/chat/chat-input-menu-handler.d.ts +0 -32
- package/dist/cli/chat/hooks/slash-imsg-handlers.js +0 -148
- package/dist/cli/chat/hooks/slash-imsg-handlers.js.map +0 -1
- package/dist/cli/chat/hooks/useStreamingReducer.d.ts +0 -66
- package/dist/cli/commands/__tests__/config-cmd.test.js +0 -270
- package/dist/cli/commands/__tests__/doctor.test.js +0 -257
- package/dist/cli/commands/__tests__/imsg-node-bridge.test.js +0 -99
- package/dist/cli/commands/__tests__/imsg-utils.test.js +0 -73
- package/dist/cli/commands/__tests__/init.test.js +0 -214
- package/dist/cli/commands/__tests__/mcp.test.js +0 -287
- package/dist/cli/commands/imsg-watcher-helpers.d.ts +0 -40
- package/dist/cli/commands/imsg-watcher-helpers.js +0 -184
- package/dist/cli/commands/imsg-watcher-helpers.js.map +0 -1
- package/dist/cli/commands/imsg-watcher.d.ts +0 -11
- package/dist/cli/commands/imsg-watcher.js +0 -230
- package/dist/cli/commands/imsg-watcher.js.map +0 -1
- package/dist/cli/services/__tests__/agent-definitions.test.js +0 -153
- package/dist/cli/services/__tests__/agent-events-global.test.js +0 -39
- package/dist/cli/services/__tests__/agent-events.part2.test.js +0 -113
- package/dist/cli/services/__tests__/agent-events.test.js +0 -157
- package/dist/cli/services/__tests__/agent-loop-auth.test.js +0 -392
- package/dist/cli/services/__tests__/agent-loop-budget.test.js +0 -389
- package/dist/cli/services/__tests__/agent-loop-tools-lifecycle.test.js +0 -430
- package/dist/cli/services/__tests__/agent-loop-tools-maxturns.test.js +0 -486
- package/dist/cli/services/__tests__/agent-loop-utils-execution.test.js +0 -528
- package/dist/cli/services/__tests__/agent-loop-utils-helpers.test.js +0 -466
- package/dist/cli/services/__tests__/agent-worker-base-execute.test.js +0 -257
- package/dist/cli/services/__tests__/agent-worker-base-helpers.test.js +0 -198
- package/dist/cli/services/__tests__/agent-worker-base.test.js +0 -278
- package/dist/cli/services/__tests__/auth-service-exports.test.js +0 -41
- package/dist/cli/services/__tests__/auth-service.part2.test.js +0 -169
- package/dist/cli/services/__tests__/auth-service.test.js +0 -242
- package/dist/cli/services/__tests__/background-processes.test.js +0 -282
- package/dist/cli/services/__tests__/claude-md-loader.test.js +0 -134
- package/dist/cli/services/__tests__/config-store.test.js +0 -247
- package/dist/cli/services/__tests__/debug-log.test.js +0 -199
- package/dist/cli/services/__tests__/edge-cases-caching.test.js +0 -174
- package/dist/cli/services/__tests__/edge-cases-compaction-core.test.js +0 -226
- package/dist/cli/services/__tests__/edge-cases-compaction-openai.test.js +0 -152
- package/dist/cli/services/__tests__/edge-cases-compaction-shapes.test.js +0 -53
- package/dist/cli/services/__tests__/edge-cases-compaction-thinking.test.js +0 -226
- package/dist/cli/services/__tests__/edge-cases-compaction.test.js +0 -131
- package/dist/cli/services/__tests__/edge-cases-paths.test.js +0 -86
- package/dist/cli/services/__tests__/error-logger-messages.test.js +0 -81
- package/dist/cli/services/__tests__/error-logger-transport.test.js +0 -119
- package/dist/cli/services/__tests__/error-logger.test.js +0 -264
- package/dist/cli/services/__tests__/file-history.test.js +0 -136
- package/dist/cli/services/__tests__/git-context-cache-reset.test.js +0 -223
- package/dist/cli/services/__tests__/git-context.test.js +0 -241
- package/dist/cli/services/__tests__/interactive-tools-execute.test.js +0 -166
- package/dist/cli/services/__tests__/interactive-tools-plan.test.js +0 -197
- package/dist/cli/services/__tests__/interactive-tools.part2.test.js +0 -168
- package/dist/cli/services/__tests__/interactive-tools.test.js +0 -179
- package/dist/cli/services/__tests__/keybinding-manager.test.js +0 -205
- package/dist/cli/services/__tests__/local-tools-dispatch.test.js +0 -404
- package/dist/cli/services/__tests__/local-tools.test.js +0 -238
- package/dist/cli/services/__tests__/lsp-manager.test.js +0 -364
- package/dist/cli/services/__tests__/mcp-client-connect-disconnect.test.js +0 -310
- package/dist/cli/services/__tests__/mcp-client.test.js +0 -93
- package/dist/cli/services/__tests__/memory-manager.test.js +0 -154
- package/dist/cli/services/__tests__/model-manager-utils.test.js +0 -154
- package/dist/cli/services/__tests__/model-manager.test.js +0 -175
- package/dist/cli/services/__tests__/permission-modes.test.js +0 -222
- package/dist/cli/services/__tests__/ripgrep.test.js +0 -328
- package/dist/cli/services/__tests__/server-tools-execute.test.js +0 -317
- package/dist/cli/services/__tests__/server-tools.test.js +0 -272
- package/dist/cli/services/__tests__/session-persistence.test.js +0 -245
- package/dist/cli/services/__tests__/subagent-basic.test.js +0 -489
- package/dist/cli/services/__tests__/subagent-edge.test.js +0 -545
- package/dist/cli/services/__tests__/subagent-prompts.test.js +0 -558
- package/dist/cli/services/__tests__/subagent-worker-errors.test.js +0 -255
- package/dist/cli/services/__tests__/subagent-worker.test.js +0 -242
- package/dist/cli/services/__tests__/system-prompt.test.js +0 -210
- package/dist/cli/services/__tests__/team-lead-comms-messaging.test.js +0 -250
- package/dist/cli/services/__tests__/team-lead-comms-result.test.js +0 -232
- package/dist/cli/services/__tests__/team-lead-comms-stop.test.js +0 -344
- package/dist/cli/services/__tests__/team-lead-comms.test.js +0 -285
- package/dist/cli/services/__tests__/team-lead-create.test.js +0 -327
- package/dist/cli/services/__tests__/team-lead-run.test.js +0 -318
- package/dist/cli/services/__tests__/team-lead-stop.test.js +0 -199
- package/dist/cli/services/__tests__/team-state-comms.test.js +0 -240
- package/dist/cli/services/__tests__/team-state-core.test.js +0 -230
- package/dist/cli/services/__tests__/team-state-tasks-complete-fail-available.test.js +0 -224
- package/dist/cli/services/__tests__/team-state-tasks.test.js +0 -184
- package/dist/cli/services/__tests__/telemetry-ai-metadata.test.js +0 -116
- package/dist/cli/services/__tests__/telemetry.part2.test.js +0 -195
- package/dist/cli/services/__tests__/telemetry.test.js +0 -176
- package/dist/cli/services/agent-loop-iteration.d.ts +0 -13
- package/dist/cli/services/agent-loop-setup.d.ts +0 -32
- package/dist/cli/services/agent-worker-base-api.d.ts +0 -19
- package/dist/cli/services/agent-worker-base-helpers.d.ts +0 -27
- package/dist/cli/services/agent-worker-base-tools.d.ts +0 -16
- package/dist/cli/services/agent-worker-base-types.d.ts +0 -81
- package/dist/cli/services/background-agents.d.ts +0 -26
- package/dist/cli/services/background-processes-ops.d.ts +0 -24
- package/dist/cli/services/background-tool-defs.d.ts +0 -50
- package/dist/cli/services/config-modules-model.test.js +0 -133
- package/dist/cli/services/config-modules-permission.test.js +0 -85
- package/dist/cli/services/config-modules-permissions.test.js +0 -85
- package/dist/cli/services/config-modules-session.test.js +0 -297
- package/dist/cli/services/format-server-response-columns.test.js +0 -265
- package/dist/cli/services/format-server-response-fallback.test.js +0 -65
- package/dist/cli/services/format-server-response-primitives-basic.test.js +0 -261
- package/dist/cli/services/format-server-response-primitives-nested.test.js +0 -188
- package/dist/cli/services/format-server-response-primitives.test.js +0 -300
- package/dist/cli/services/format-server-response-realworld.test.js +0 -248
- package/dist/cli/services/format-server-response-values.test.js +0 -247
- package/dist/cli/services/hooks-runners.test.js +0 -184
- package/dist/cli/services/hooks.glob-load.test.js +0 -233
- package/dist/cli/services/hooks.run-hooks.test.js +0 -184
- package/dist/cli/services/hooks.test.js +0 -233
- package/dist/cli/services/ink-incremental.d.ts +0 -19
- package/dist/cli/services/ink-incremental.js +0 -59
- package/dist/cli/services/ink-incremental.js.map +0 -1
- package/dist/cli/services/ink-resize-fix.d.ts +0 -18
- package/dist/cli/services/ink-resize-fix.js +0 -76
- package/dist/cli/services/ink-resize-fix.js.map +0 -1
- package/dist/cli/services/ink-sync-output.d.ts +0 -12
- package/dist/cli/services/ink-sync-output.js +0 -16
- package/dist/cli/services/ink-sync-output.js.map +0 -1
- package/dist/cli/services/interactive-tool-defs.d.ts +0 -80
- package/dist/cli/services/local-tools-definitions.d.ts +0 -6
- package/dist/cli/services/local-tools-files.test.js +0 -256
- package/dist/cli/services/local-tools-read-many.d.ts +0 -6
- package/dist/cli/services/model-router.test.js +0 -245
- package/dist/cli/services/rewind-rewindTo.test.js +0 -202
- package/dist/cli/services/rewind.test.js +0 -175
- package/dist/cli/services/sandbox.test.js +0 -198
- package/dist/cli/services/subagent-execution.d.ts +0 -12
- package/dist/cli/services/team-lead-auto.d.ts +0 -11
- package/dist/cli/services/team-lead-execution.d.ts +0 -28
- package/dist/cli/services/teammate-loop.js +0 -557
- package/dist/cli/services/teammate-loop.js.map +0 -1
- package/dist/cli/services/tools/__tests__/agent-tools-tasks-teams.test.js +0 -250
- package/dist/cli/services/tools/__tests__/agent-tools-teams.test.js +0 -200
- package/dist/cli/services/tools/__tests__/agent-tools.test.js +0 -340
- package/dist/cli/services/tools/__tests__/file-ops-cache.test.js +0 -152
- package/dist/cli/services/tools/__tests__/file-ops-notebook.test.js +0 -249
- package/dist/cli/services/tools/__tests__/file-ops-read.test.js +0 -261
- package/dist/cli/services/tools/__tests__/file-ops-write.test.js +0 -292
- package/dist/cli/services/tools/__tests__/search-tools-rg.test.js +0 -92
- package/dist/cli/services/tools/__tests__/search-tools.part2.test.js +0 -174
- package/dist/cli/services/tools/__tests__/search-tools.test.js +0 -227
- package/dist/cli/services/tools/__tests__/shell-exec-allowed-core.test.js +0 -163
- package/dist/cli/services/tools/__tests__/shell-exec-allowed-extended.test.js +0 -220
- package/dist/cli/services/tools/__tests__/shell-exec-allowed.part2.test.js +0 -215
- package/dist/cli/services/tools/__tests__/shell-exec-allowed.test.js +0 -154
- package/dist/cli/services/tools/__tests__/shell-exec-blocked.test.js +0 -132
- package/dist/cli/services/tools/__tests__/shell-exec-execution.test.js +0 -245
- package/dist/cli/services/tools/__tests__/task-manager-create.test.js +0 -110
- package/dist/cli/services/tools/__tests__/task-manager-crud.test.js +0 -339
- package/dist/cli/services/tools/__tests__/task-manager-list-get.test.js +0 -343
- package/dist/cli/services/tools/__tests__/task-manager-query.test.js +0 -346
- package/dist/cli/services/tools/__tests__/task-manager-routing.test.js +0 -58
- package/dist/cli/services/tools/__tests__/task-manager-update.test.js +0 -224
- package/dist/cli/services/tools/__tests__/task-manager.test.js +0 -159
- package/dist/cli/services/tools/__tests__/web-tools-html-search.test.js +0 -227
- package/dist/cli/services/tools/__tests__/web-tools.test.js +0 -285
- package/dist/cli/services/tools/shell-exec.test.js +0 -148
- package/dist/cli/shared/SharedTick.d.ts +0 -10
- package/dist/cli/shared/__tests__/markdown.test.js +0 -188
- package/dist/local-agent/__tests__/connection-disconnect.test.js +0 -201
- package/dist/local-agent/__tests__/connection-lifecycle.test.js +0 -289
- package/dist/local-agent/__tests__/connection-msghandling.test.js +0 -311
- package/dist/local-agent/__tests__/connection-reconnect.test.js +0 -230
- package/dist/local-agent/__tests__/connection-toolexec.test.js +0 -253
- package/dist/local-agent/__tests__/discovery.test.js +0 -328
- package/dist/local-agent/__tests__/executor-background.test.js +0 -219
- package/dist/local-agent/__tests__/executor-exec.test.js +0 -221
- package/dist/local-agent/__tests__/executor-jobs-sessions.test.js +0 -220
- package/dist/local-agent/__tests__/executor-system-info.test.js +0 -133
- package/dist/local-agent/__tests__/executor-systeminfo.test.js +0 -109
- package/dist/local-agent/__tests__/executor.test.js +0 -235
- package/dist/local-agent/__tests__/index.test.js +0 -139
- package/dist/node/__tests__/cli-channels.test.js +0 -293
- package/dist/node/__tests__/cli-config-edge.test.js +0 -154
- package/dist/node/__tests__/cli-config.test.js +0 -215
- package/dist/node/__tests__/config.test.js +0 -292
- package/dist/node/__tests__/runtime-heartbeat.test.js +0 -153
- package/dist/node/__tests__/runtime-lifecycle-init.test.js +0 -263
- package/dist/node/__tests__/runtime-lifecycle-stats.test.js +0 -180
- package/dist/node/__tests__/runtime-lifecycle.test.js +0 -305
- package/dist/node/__tests__/runtime-relay.test.js +0 -341
- package/dist/node/adapters/__tests__/base.test.js +0 -286
- package/dist/node/adapters/__tests__/discord.test.js +0 -284
- package/dist/node/adapters/__tests__/email-send.test.js +0 -295
- package/dist/node/adapters/__tests__/email.inbound-send.test.js +0 -217
- package/dist/node/adapters/__tests__/email.lifecycle.test.js +0 -211
- package/dist/node/adapters/__tests__/email.test.js +0 -290
- package/dist/node/adapters/__tests__/email.webhook-send.test.js +0 -251
- package/dist/node/adapters/__tests__/imessage-filter.test.js +0 -183
- package/dist/node/adapters/__tests__/imessage-lifecycle.test.js +0 -215
- package/dist/node/adapters/__tests__/imessage-send-restart.test.js +0 -227
- package/dist/node/adapters/__tests__/slack.part2.test.js +0 -135
- package/dist/node/adapters/__tests__/slack.test.js +0 -241
- package/dist/node/adapters/__tests__/sms-extras.test.js +0 -108
- package/dist/node/adapters/__tests__/sms-lifecycle.test.js +0 -203
- package/dist/node/adapters/__tests__/sms-messaging.test.js +0 -266
- package/dist/node/adapters/__tests__/sms.part2.test.js +0 -174
- package/dist/node/adapters/__tests__/sms.test.js +0 -253
- package/dist/node/adapters/__tests__/telegram-polling.test.js +0 -256
- package/dist/node/adapters/__tests__/telegram-send.test.js +0 -166
- package/dist/node/adapters/__tests__/webchat-inbound.test.js +0 -188
- package/dist/node/adapters/__tests__/webchat-outbound.test.js +0 -178
- package/dist/node/adapters/__tests__/whatsapp-inbound.test.js +0 -200
- package/dist/node/adapters/__tests__/whatsapp-send.test.js +0 -212
- package/dist/node/adapters/__tests__/whatsapp.test.js +0 -280
- package/dist/server/__tests__/gateway-fast-fail.test.js +0 -160
- package/dist/server/__tests__/local-agent-gateway.test.js +0 -186
- package/dist/server/__tests__/proxy-handlers-delegation.test.js +0 -240
- package/dist/server/__tests__/proxy-handlers-validation.test.js +0 -211
- package/dist/server/__tests__/proxy-handlers.part2.test.js +0 -240
- package/dist/server/__tests__/proxy-handlers.test.js +0 -213
- package/dist/server/__tests__/strip-base64-e2e.test.js +0 -303
- package/dist/server/__tests__/strip-base64.test.js +0 -256
- package/dist/server/__tests__/tool-router-agent-tools.test.js +0 -324
- package/dist/server/__tests__/tool-router-execute-core.test.js +0 -357
- package/dist/server/__tests__/tool-router-execute-permissions.test.js +0 -332
- package/dist/server/__tests__/tool-router-execute.test.js +0 -348
- package/dist/server/__tests__/tool-router-load.test.js +0 -432
- package/dist/server/__tests__/tool-router-permissions.test.js +0 -359
- package/dist/server/__tests__/tool-router-registry-cache.test.js +0 -383
- package/dist/server/__tests__/tool-router-registry-handlers.test.js +0 -272
- package/dist/server/__tests__/tool-router-registry.test.js +0 -331
- package/dist/server/__tests__/validation-inventory.test.js +0 -250
- package/dist/server/__tests__/validation-misc.test.js +0 -243
- package/dist/server/__tests__/validation-supply-chain.test.js +0 -188
- package/dist/server/__tests__/worker.test.js +0 -265
- package/dist/server/handlers/__tests__/conversation-lock.test.js +0 -117
- package/dist/server/handlers/__tests__/e2e/auth-cross-platform-login.e2e.test.js +0 -268
- package/dist/server/handlers/__tests__/e2e/auth-cross-platform-tokens.e2e.test.js +0 -264
- package/dist/server/handlers/__tests__/e2e/email-pipeline-send.e2e.test.js +0 -214
- package/dist/server/handlers/__tests__/e2e/email-pipeline-threads.e2e.test.js +0 -168
- package/dist/server/handlers/__tests__/e2e/error-logging-pipeline-dedup.e2e.test.js +0 -229
- package/dist/server/handlers/__tests__/e2e/error-logging-pipeline.e2e.test.js +0 -239
- package/dist/server/handlers/__tests__/e2e/error-logging-rate-limit.e2e.test.js +0 -150
- package/dist/server/handlers/__tests__/e2e/inventory-sync-guards.e2e.test.js +0 -177
- package/dist/server/handlers/__tests__/e2e/inventory-sync.e2e.test.js +0 -228
- package/dist/server/handlers/__tests__/e2e/inventory-sync.part2.e2e.test.js +0 -188
- package/dist/server/handlers/__tests__/e2e/order-lifecycle-fulfillment.e2e.test.js +0 -295
- package/dist/server/handlers/__tests__/e2e/order-lifecycle.e2e.test.js +0 -277
- package/dist/server/handlers/__tests__/e2e/order-lifecycle.fulfillment.e2e.test.js +0 -307
- package/dist/server/handlers/__tests__/e2e/order-lifecycle.setup.e2e.test.js +0 -177
- package/dist/server/handlers/__tests__/e2e/storefront-checkout-cart.e2e.test.js +0 -255
- package/dist/server/handlers/__tests__/e2e/storefront-checkout-webhook.e2e.test.js +0 -231
- package/dist/server/handlers/__tests__/e2e/workflow-execution-failures.e2e.test.js +0 -235
- package/dist/server/handlers/__tests__/e2e/workflow-execution.e2e.test.js +0 -294
- package/dist/server/handlers/__tests__/e2e/workflow-security.e2e.test.js +0 -311
- package/dist/server/handlers/__tests__/e2e/workflow-security.part2.e2e.test.js +0 -267
- package/dist/server/handlers/__tests__/workflow-cache.test.js +0 -237
- package/dist/server/handlers/analytics-errors-edge.test.js +0 -173
- package/dist/server/handlers/analytics.test.js +0 -280
- package/dist/server/handlers/api-docs-examples-ext.d.ts +0 -9
- package/dist/server/handlers/api-docs-examples-ext.js +0 -278
- package/dist/server/handlers/api-docs-examples-ext.js.map +0 -1
- package/dist/server/handlers/api-docs-examples.d.ts +0 -8
- package/dist/server/handlers/api-docs-examples.js +0 -221
- package/dist/server/handlers/api-docs-examples.js.map +0 -1
- package/dist/server/handlers/api-docs-sections-ext.d.ts +0 -2
- package/dist/server/handlers/api-docs-sections-ext.js +0 -497
- package/dist/server/handlers/api-docs-sections-ext.js.map +0 -1
- package/dist/server/handlers/api-docs-sections.d.ts +0 -21
- package/dist/server/handlers/api-docs-sections.js +0 -293
- package/dist/server/handlers/api-docs-sections.js.map +0 -1
- package/dist/server/handlers/api-keys.part2.test.js +0 -157
- package/dist/server/handlers/api-keys.test.js +0 -161
- package/dist/server/handlers/billing-routes.test.js +0 -123
- package/dist/server/handlers/billing.test.js +0 -215
- package/dist/server/handlers/browser-actions-errors.test.js +0 -94
- package/dist/server/handlers/browser-actions.part2.test.js +0 -190
- package/dist/server/handlers/browser-actions.test.js +0 -190
- package/dist/server/handlers/browser-validation.test.js +0 -257
- package/dist/server/handlers/catalog.test.js +0 -297
- package/dist/server/handlers/comms.test.js +0 -289
- package/dist/server/handlers/creations-advanced-collections.test.js +0 -214
- package/dist/server/handlers/creations-advanced-generate.test.js +0 -142
- package/dist/server/handlers/creations-advanced.test.js +0 -171
- package/dist/server/handlers/creations-collections-preview.test.js +0 -214
- package/dist/server/handlers/creations-crud.test.js +0 -260
- package/dist/server/handlers/creations-mutations.test.js +0 -197
- package/dist/server/handlers/crm.test.js +0 -179
- package/dist/server/handlers/discovery-advertise.test.js +0 -185
- package/dist/server/handlers/discovery-scan.test.js +0 -233
- package/dist/server/handlers/embeddings-embed-search.test.js +0 -196
- package/dist/server/handlers/embeddings-index-delete-stats.test.js +0 -140
- package/dist/server/handlers/embeddings-search.test.js +0 -221
- package/dist/server/handlers/embeddings.test.js +0 -137
- package/dist/server/handlers/enrichment-breach.d.ts +0 -8
- package/dist/server/handlers/enrichment-breach.js +0 -266
- package/dist/server/handlers/enrichment-breach.js.map +0 -1
- package/dist/server/handlers/enrichment-data.d.ts +0 -13
- package/dist/server/handlers/enrichment-data.js +0 -145
- package/dist/server/handlers/enrichment-data.js.map +0 -1
- package/dist/server/handlers/enrichment-mutations.test.js +0 -240
- package/dist/server/handlers/enrichment-queries.test.js +0 -181
- package/dist/server/handlers/enrichment-validation.test.js +0 -177
- package/dist/server/handlers/enrichment-writes.d.ts +0 -16
- package/dist/server/handlers/enrichment-writes.js +0 -226
- package/dist/server/handlers/enrichment-writes.js.map +0 -1
- package/dist/server/handlers/image-gen.test.js +0 -205
- package/dist/server/handlers/inventory.test.js +0 -380
- package/dist/server/handlers/kali-background.test.js +0 -222
- package/dist/server/handlers/kali-errors.test.js +0 -92
- package/dist/server/handlers/kali-validation.test.js +0 -234
- package/dist/server/handlers/llm-providers-actions.test.js +0 -220
- package/dist/server/handlers/llm-providers-anthropic.test.js +0 -239
- package/dist/server/handlers/llm-providers-failover.test.js +0 -232
- package/dist/server/handlers/llm-providers-providers.test.js +0 -300
- package/dist/server/handlers/llm-providers-validation.test.js +0 -239
- package/dist/server/handlers/local-agent-tools.test.js +0 -224
- package/dist/server/handlers/local-agent.test.js +0 -198
- package/dist/server/handlers/local-agent.tools-status.test.js +0 -204
- package/dist/server/handlers/local-agent.validation-exec.test.js +0 -182
- package/dist/server/handlers/meta-ads-audience-rules.test.js +0 -243
- package/dist/server/handlers/meta-ads-audience-targeting.test.js +0 -205
- package/dist/server/handlers/meta-ads-audiences-targeting.test.js +0 -383
- package/dist/server/handlers/meta-ads-crud-ads.test.js +0 -136
- package/dist/server/handlers/meta-ads-crud-campaigns.test.js +0 -189
- package/dist/server/handlers/meta-ads-crud-create.test.js +0 -303
- package/dist/server/handlers/meta-ads-crud-list-update.test.js +0 -259
- package/dist/server/handlers/meta-ads-delete-publish-sync.test.js +0 -282
- package/dist/server/handlers/meta-ads-insights.test.js +0 -80
- package/dist/server/handlers/meta-ads-list-get.test.js +0 -237
- package/dist/server/handlers/meta-ads-publish-delete.test.js +0 -254
- package/dist/server/handlers/meta-ads-publish-helpers.js +0 -117
- package/dist/server/handlers/meta-ads-publish-helpers.js.map +0 -1
- package/dist/server/handlers/meta-ads-publish-sync.test.js +0 -205
- package/dist/server/handlers/meta-ads-publish.test.js +0 -254
- package/dist/server/handlers/meta-ads-sync-insights.test.js +0 -184
- package/dist/server/handlers/meta-ads-update.test.js +0 -117
- package/dist/server/handlers/nodes-channels.test.js +0 -413
- package/dist/server/handlers/nodes-events.test.js +0 -131
- package/dist/server/handlers/nodes-list-delete.test.js +0 -171
- package/dist/server/handlers/nodes-messages-delivery.test.js +0 -208
- package/dist/server/handlers/nodes-messages.test.js +0 -211
- package/dist/server/handlers/nodes-register.test.js +0 -277
- package/dist/server/handlers/nodes.test.js +0 -353
- package/dist/server/handlers/operations.test.js +0 -136
- package/dist/server/handlers/platform-telemetry.test.js +0 -200
- package/dist/server/handlers/platform-websearch.test.js +0 -160
- package/dist/server/handlers/storefront.test.js +0 -329
- package/dist/server/handlers/supply-chain.test.js +0 -347
- package/dist/server/handlers/transcription.test.js +0 -118
- package/dist/server/handlers/video-gen-veo.js +0 -114
- package/dist/server/handlers/video-gen-veo.js.map +0 -1
- package/dist/server/handlers/video-gen.test.js +0 -146
- package/dist/server/handlers/voice.test.js +0 -153
- package/dist/server/handlers/workflow-steps.test.js +0 -330
- package/dist/server/handlers/workflows-extras.test.js +0 -65
- package/dist/server/handlers/workflows.part2.test.js +0 -170
- package/dist/server/handlers/workflows.test.js +0 -281
- package/dist/server/lib/__tests__/batch-client-conversion-jsonl.test.js +0 -171
- package/dist/server/lib/__tests__/batch-client-polling.test.js +0 -292
- package/dist/server/lib/__tests__/batch-client-queue.test.js +0 -270
- package/dist/server/lib/__tests__/clickhouse-buffer.test.js +0 -236
- package/dist/server/lib/__tests__/code-worker-edge-cases.test.js +0 -118
- package/dist/server/lib/__tests__/code-worker-pool-execute.test.js +0 -193
- package/dist/server/lib/__tests__/code-worker-pool-execution.test.js +0 -165
- package/dist/server/lib/__tests__/code-worker-pool-init.test.js +0 -131
- package/dist/server/lib/__tests__/code-worker-pool.test.js +0 -194
- package/dist/server/lib/__tests__/code-worker-sandbox-ops.test.js +0 -123
- package/dist/server/lib/__tests__/code-worker-sandbox.test.js +0 -217
- package/dist/server/lib/__tests__/code-worker.test.js +0 -179
- package/dist/server/lib/__tests__/compaction-service-generate.test.js +0 -229
- package/dist/server/lib/__tests__/compaction-service.test.js +0 -319
- package/dist/server/lib/__tests__/otel.test.js +0 -146
- package/dist/server/lib/__tests__/prompt-sanitizer-validation.test.js +0 -165
- package/dist/server/lib/__tests__/prompt-sanitizer.sanitize.test.js +0 -343
- package/dist/server/lib/__tests__/prompt-sanitizer.test.js +0 -328
- package/dist/server/lib/__tests__/prompt-sanitizer.validate-tool.test.js +0 -145
- package/dist/server/lib/__tests__/provider-capabilities.test.js +0 -263
- package/dist/server/lib/__tests__/provider-failover-routing.test.js +0 -145
- package/dist/server/lib/__tests__/provider-failover-state.test.js +0 -131
- package/dist/server/lib/__tests__/rate-limiter-budgets.test.js +0 -216
- package/dist/server/lib/__tests__/rate-limiter.budgets-tools.test.js +0 -113
- package/dist/server/lib/__tests__/rate-limiter.check-request.test.js +0 -141
- package/dist/server/lib/__tests__/rate-limiter.stats-lifecycle.test.js +0 -135
- package/dist/server/lib/__tests__/rate-limiter.test.js +0 -207
- package/dist/server/lib/__tests__/server-agent-loop-abort-conditions.test.js +0 -544
- package/dist/server/lib/__tests__/server-agent-loop-abort.part2.test.js +0 -504
- package/dist/server/lib/__tests__/server-agent-loop-abort.test.js +0 -396
- package/dist/server/lib/__tests__/server-agent-loop-compaction.test.js +0 -397
- package/dist/server/lib/__tests__/server-agent-loop-failover.test.js +0 -356
- package/dist/server/lib/__tests__/server-agent-loop-features-caching.test.js +0 -519
- package/dist/server/lib/__tests__/server-agent-loop-features-edges.test.js +0 -512
- package/dist/server/lib/__tests__/server-subagent-bailout.test.js +0 -194
- package/dist/server/lib/__tests__/server-subagent-basics.test.js +0 -348
- package/dist/server/lib/__tests__/server-subagent-errors-abort.test.js +0 -319
- package/dist/server/lib/__tests__/server-subagent-errors-progress.test.js +0 -253
- package/dist/server/lib/__tests__/server-subagent-errors.part2.test.js +0 -253
- package/dist/server/lib/__tests__/server-subagent-errors.test.js +0 -319
- package/dist/server/lib/__tests__/session-checkpoint-load.test.js +0 -275
- package/dist/server/lib/__tests__/session-checkpoint-save.test.js +0 -159
- package/dist/server/lib/__tests__/ssrf-guard.test.js +0 -93
- package/dist/server/lib/__tests__/supabase-client.test.js +0 -111
- package/dist/server/lib/__tests__/template-resolver.test.js +0 -317
- package/dist/server/lib/__tests__/utils-timeout.test.js +0 -49
- package/dist/server/lib/__tests__/utils.test.js +0 -322
- package/dist/server/providers/__tests__/anthropic-adapter.test.js +0 -228
- package/dist/server/providers/__tests__/anthropic-betas-toolchoice.test.js +0 -257
- package/dist/server/providers/__tests__/anthropic-errors.test.js +0 -262
- package/dist/server/providers/__tests__/anthropic-stream-core.test.js +0 -275
- package/dist/server/providers/__tests__/anthropic-streaming-betas.test.js +0 -247
- package/dist/server/providers/__tests__/anthropic-streaming-core.test.js +0 -275
- package/dist/server/providers/__tests__/bedrock-config.test.js +0 -177
- package/dist/server/providers/__tests__/bedrock-stream-behavior-streaming.test.js +0 -272
- package/dist/server/providers/__tests__/bedrock-stream-behavior-toolchoice.test.js +0 -214
- package/dist/server/providers/__tests__/bedrock-stream-behavior.part2.test.js +0 -165
- package/dist/server/providers/__tests__/bedrock-stream-behavior.test.js +0 -309
- package/dist/server/providers/__tests__/bedrock-stream-body-credentials.test.js +0 -170
- package/dist/server/providers/__tests__/bedrock-stream-body-extras.test.js +0 -183
- package/dist/server/providers/__tests__/bedrock-stream-body-request.test.js +0 -305
- package/dist/server/providers/__tests__/bedrock-stream-body.part2.test.js +0 -305
- package/dist/server/providers/__tests__/bedrock-stream-body.test.js +0 -175
- package/dist/server/providers/__tests__/bedrock-stream-errors.test.js +0 -165
- package/dist/server/providers/__tests__/gemini-config-methods.test.js +0 -182
- package/dist/server/providers/__tests__/gemini-config-streaming.test.js +0 -257
- package/dist/server/providers/__tests__/gemini-conversion-messages.test.js +0 -247
- package/dist/server/providers/__tests__/gemini-conversion-schema.test.js +0 -365
- package/dist/server/providers/__tests__/gemini-tools-choice.test.js +0 -221
- package/dist/server/providers/__tests__/gemini-tools-fn.test.js +0 -252
- package/dist/server/providers/__tests__/openai-config.test.js +0 -194
- package/dist/server/providers/__tests__/openai-conversion.test.js +0 -276
- package/dist/server/providers/__tests__/openai-messages.test.js +0 -261
- package/dist/server/providers/__tests__/openai-streaming.test.js +0 -394
- package/dist/server/providers/__tests__/openai-tools-cache.test.js +0 -227
- package/dist/server/providers/__tests__/registry.test.js +0 -183
- package/dist/server/providers/__tests__/shared.test.js +0 -297
- package/dist/shared/agent-core-config.test.js +0 -132
- package/dist/shared/agent-core-context-thinking.test.js +0 -293
- package/dist/shared/agent-core-loop-calls.test.js +0 -174
- package/dist/shared/agent-core-loop-detector-bail.test.js +0 -201
- package/dist/shared/agent-core-loop-detector.test.js +0 -195
- package/dist/shared/agent-core-loop-errors.test.js +0 -258
- package/dist/shared/agent-core-pricing.test.js +0 -191
- package/dist/shared/agent-core-sanitize-retry.test.js +0 -129
- package/dist/shared/api-client-build-request.test.js +0 -228
- package/dist/shared/api-client-build-system-caching.test.js +0 -107
- package/dist/shared/api-client-build.test.js +0 -223
- package/dist/shared/api-client-config.d.ts +0 -21
- package/dist/shared/api-client-helpers.d.ts +0 -57
- package/dist/shared/api-client-helpers.test.js +0 -261
- package/dist/shared/api-client-proxy-happy.test.js +0 -255
- package/dist/shared/api-client-proxy-retry.test.js +0 -307
- package/dist/shared/api-client-proxy.d.ts +0 -26
- package/dist/shared/api-client-proxy.test.js +0 -255
- package/dist/shared/api-client-retry.test.js +0 -307
- package/dist/shared/api-client-system-trimming.test.js +0 -261
- package/dist/shared/api-client-trimming.d.ts +0 -36
- package/dist/shared/api-client.test.js +0 -228
- package/dist/shared/compaction-thinking.test.js +0 -315
- package/dist/shared/compaction-trimming.test.js +0 -223
- package/dist/shared/sse-parser-callbacks.test.js +0 -422
- package/dist/shared/sse-parser-collect.test.js +0 -252
- package/dist/shared/sse-parser-e2e.test.js +0 -558
- package/dist/shared/sse-parser-parse.test.js +0 -253
- package/dist/shared/tool-dispatch-advanced-batch-build.test.js +0 -405
- package/dist/shared/tool-dispatch-advanced.test.js +0 -320
- package/dist/shared/tool-dispatch-basic.test.js +0 -278
- package/dist/shared/tool-dispatch-content.d.ts +0 -14
- package/dist/shared/tool-dispatch-parallel.test.js +0 -378
- package/dist/webchat/__tests__/widget-messaging.test.js +0 -323
- package/dist/webchat/__tests__/widget.test.js +0 -273
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.js","names":["Anthropic","sanitizeError","AGENT_DEFAULTS","getCapabilities","registerProvider","jsonResponse","writeSSEHeaders","COMPACTION_TRIGGER_TOKENS","compactionTriggerTokens","COMPACTION_TOTAL_BUDGET","compactionTotalBudget","DEFAULT_OUTPUT_TOKENS","MODEL_MAX_OUTPUT_TOKENS","AnthropicAdapter","name","handleStream","res","config","corsHeaders","model","messages","system","tools","max_tokens","temperature","stream","betas","context_management","thinking","tool_choice","documents","ANTHROPIC_API_KEY","process","env","anthropic","apiKey","apiParams","type","text","cache_control","length","map","t","description","slice","input_schema","undefined","effectiveBetas","includes","push","safeContextMgmt","edits","filtered","filter","e","console","log","response","beta","create","err","error","heartbeat","setInterval","writableEnded","write","event","JSON","stringify","headersSent","clearInterval","end","getThinkingConfig","enabled","budget_tokens","getMaxOutputTokens","agentMax","modelMax","Math","min","getContextManagement","keep","value","supportsCompaction","trigger","pause_after_compaction","instructions","getCompactionConfig","_model","triggerTokens","totalBudget","isNative"],"sources":["../../../src/server/providers/anthropic.ts"],"sourcesContent":["/**\n * Anthropic Provider Adapter — direct Anthropic API passthrough.\n *\n * Simplest adapter: system prompt gets cache_control blocks, tools get\n * { name, description, input_schema }, streaming events pass through as-is.\n *\n * Phase 7.1: Also encapsulates thinking config, context management,\n * output token limits, and compaction config for Anthropic/Claude models.\n */\n\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { sanitizeError, AGENT_DEFAULTS } from \"../../shared/agent-core.js\";\nimport type {\n ProviderAdapter,\n ProviderStreamConfig,\n ProviderCapabilities,\n ThinkingConfigResult,\n ContextManagementConfig,\n CompactionConfig,\n} from \"./types.js\";\nimport { getCapabilities } from \"../lib/provider-capabilities.js\";\nimport { registerProvider } from \"./registry.js\";\nimport { jsonResponse, writeSSEHeaders } from \"./shared.js\";\nimport type http from \"node:http\";\n\n// ============================================================================\n// CONSTANTS — Anthropic-specific model config\n// ============================================================================\n\nconst COMPACTION_TRIGGER_TOKENS = AGENT_DEFAULTS.compactionTriggerTokens;\nconst COMPACTION_TOTAL_BUDGET = AGENT_DEFAULTS.compactionTotalBudget;\nconst DEFAULT_OUTPUT_TOKENS = 16384; // Per-response cap (model max is separate)\n\nconst MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {\n \"claude-opus-4-6\": 128000,\n \"claude-sonnet-4-6\": 64000,\n \"claude-haiku-4-5-20251001\": 64000,\n \"claude-sonnet-4-5-20250929\": 64000,\n \"claude-opus-4-5-20251101\": 64000,\n \"claude-opus-4-1-20250805\": 32768,\n \"claude-sonnet-4-20250514\": 64000,\n \"claude-opus-4-20250514\": 32768,\n \"claude-3-7-sonnet-20250219\": 64000,\n \"claude-3-haiku-20240307\": 4096,\n};\n\n// ============================================================================\n// ADAPTER\n// ============================================================================\n\nexport class AnthropicAdapter implements ProviderAdapter {\n name = \"anthropic\";\n\n async handleStream(\n res: http.ServerResponse,\n config: ProviderStreamConfig,\n corsHeaders: Record<string, string>,\n ): Promise<void> {\n const {\n model, messages, system, tools, max_tokens, temperature,\n stream = true, betas, context_management, thinking, tool_choice, documents,\n } = config;\n\n const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY!;\n const anthropic = new Anthropic({ apiKey: ANTHROPIC_API_KEY });\n\n const apiParams: Record<string, unknown> = { model, max_tokens, messages, stream: true };\n\n if (system) {\n if (typeof system === \"string\") {\n apiParams.system = [{ type: \"text\", text: system, cache_control: { type: \"ephemeral\" } }];\n } else {\n apiParams.system = system;\n }\n }\n if (tools?.length) {\n apiParams.tools = (tools as Array<Record<string, unknown>>).map((t: Record<string, unknown>) => ({\n name: t.name,\n description: typeof t.description === \"string\" ? (t.description as string).slice(0, 4096) : \"\",\n input_schema: t.input_schema,\n }));\n }\n if (temperature !== undefined) apiParams.temperature = temperature;\n\n // Citations API: add beta and documents when source documents are provided\n const effectiveBetas = betas?.length ? [...betas] : [];\n if (documents?.length) {\n if (!effectiveBetas.includes(\"citations-2025-04-15\")) {\n effectiveBetas.push(\"citations-2025-04-15\");\n }\n apiParams.documents = documents;\n }\n if (effectiveBetas.length) apiParams.betas = effectiveBetas;\n\n // Safety: strip clear_thinking_20251015 edits when thinking is not enabled.\n // Anthropic returns 400 if this strategy is present without thinking=enabled/adaptive.\n let safeContextMgmt = context_management;\n if (safeContextMgmt?.edits?.length && !thinking) {\n const filtered = safeContextMgmt.edits.filter((e: any) => e.type !== \"clear_thinking_20251015\");\n if (filtered.length !== safeContextMgmt.edits.length) {\n console.log(`[proxy] Stripped ${safeContextMgmt.edits.length - filtered.length} clear_thinking edit(s) — thinking not enabled`);\n safeContextMgmt = { ...safeContextMgmt, edits: filtered };\n }\n }\n if (safeContextMgmt?.edits?.length) apiParams.context_management = safeContextMgmt;\n if (thinking) apiParams.thinking = thinking;\n\n // Map tool_choice to Anthropic format\n if (tool_choice) {\n if (tool_choice === \"auto\") {\n apiParams.tool_choice = { type: \"auto\" };\n } else if (tool_choice === \"any\") {\n apiParams.tool_choice = { type: \"any\" };\n } else if (tool_choice === \"none\") {\n // \"none\" means no tools — remove tools from request\n delete apiParams.tools;\n } else if (typeof tool_choice === \"object\" && tool_choice.type === \"tool\") {\n apiParams.tool_choice = { type: \"tool\", name: tool_choice.name };\n }\n }\n\n if (!stream) {\n try {\n const response = effectiveBetas.length\n ? await anthropic.beta.messages.create({ ...apiParams, stream: false } as any)\n : await anthropic.messages.create({ ...apiParams, stream: false } as any);\n jsonResponse(res, 200, response, corsHeaders);\n } catch (err) {\n jsonResponse(res, 500, { error: sanitizeError(err) }, corsHeaders);\n }\n return;\n }\n\n // Streaming: defer SSE headers until stream is successfully created\n // so we can return proper HTTP errors if the initial API call fails.\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n\n try {\n const response = effectiveBetas.length\n ? await anthropic.beta.messages.create({ ...apiParams, stream: true } as any)\n : await anthropic.messages.create(apiParams as any);\n\n // Stream created successfully — now safe to commit to SSE\n writeSSEHeaders(res, corsHeaders);\n\n // Heartbeat keeps the connection alive and helps detect dead clients\n heartbeat = setInterval(() => {\n if (!res.writableEnded) res.write(\":ping\\n\\n\");\n }, 15_000);\n\n for await (const event of response as any) {\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n }\n res.write(\"data: [DONE]\\n\\n\");\n } catch (err) {\n if (!res.headersSent) {\n // Haven't started streaming yet — return proper HTTP error\n jsonResponse(res, 502, { error: `anthropic error: ${sanitizeError(err)}` }, corsHeaders);\n } else {\n // Mid-stream failure — send error event so client doesn't hang\n res.write(`data: ${JSON.stringify({ type: \"error\", error: sanitizeError(err) })}\\n\\n`);\n res.write(\"data: [DONE]\\n\\n\");\n }\n } finally {\n if (heartbeat) clearInterval(heartbeat);\n }\n res.end();\n }\n\n // ---- Provider-specific config methods ----\n\n getThinkingConfig(model: string, enabled: boolean): ThinkingConfigResult {\n if (!enabled) {\n return { thinking: { type: \"disabled\" }, beta: \"\" };\n }\n\n if (model.includes(\"opus-4-6\") || model.includes(\"sonnet-4-6\")) {\n return {\n thinking: { type: \"adaptive\" },\n beta: \"adaptive-thinking-2026-01-28\",\n };\n }\n\n // Sonnet 4.5/4.0 / Haiku: fixed budget\n return {\n thinking: { type: \"enabled\", budget_tokens: 10_000 },\n beta: \"interleaved-thinking-2025-05-14\",\n };\n }\n\n getMaxOutputTokens(model: string, agentMax?: number): number {\n const modelMax = MODEL_MAX_OUTPUT_TOKENS[model] ?? DEFAULT_OUTPUT_TOKENS;\n if (agentMax) return Math.min(agentMax, modelMax);\n return Math.min(DEFAULT_OUTPUT_TOKENS, modelMax);\n }\n\n getContextManagement(model: string): ContextManagementConfig {\n const edits: Array<Record<string, unknown>> = [];\n const betas: string[] = [\"context-management-2025-06-27\"];\n\n // Thinking block clearing — must come FIRST in edits array (API requirement).\n edits.push({\n type: \"clear_thinking_20251015\",\n keep: { type: \"thinking_turns\", value: 2 },\n });\n\n // Server-side compaction for models that support compact_20260112.\n const supportsCompaction = model.includes(\"opus-4-6\") || model.includes(\"sonnet-4-6\");\n if (supportsCompaction) {\n edits.push({\n type: \"compact_20260112\",\n trigger: { type: \"input_tokens\", value: COMPACTION_TRIGGER_TOKENS },\n pause_after_compaction: true,\n instructions: \"Summarize the conversation preserving: (1) task goals and constraints, (2) files created/modified with paths, (3) decisions made and rationale, (4) errors encountered and resolutions, (5) exact next steps. Be concise but preserve all state needed to continue work without repeating mistakes.\",\n });\n betas.push(\"compact-2026-01-12\");\n }\n\n edits.push({\n type: \"clear_tool_uses_20250919\",\n trigger: { type: \"input_tokens\", value: 80_000 },\n keep: { type: \"tool_uses\", value: 3 },\n });\n\n return { betas, config: { edits } };\n }\n\n getCompactionConfig(_model: string): CompactionConfig {\n return {\n triggerTokens: COMPACTION_TRIGGER_TOKENS,\n totalBudget: COMPACTION_TOTAL_BUDGET,\n isNative: true,\n };\n }\n\n getCapabilities(): ProviderCapabilities {\n return getCapabilities(\"anthropic\");\n }\n}\n\n// Auto-register on import\nregisterProvider(\"anthropic\", new AnthropicAdapter());\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"anthropic.js","names":["Anthropic","sanitizeError","AGENT_DEFAULTS","isRetryableError","categorizeError","getCapabilities","registerProvider","jsonResponse","writeSSEHeaders","COMPACTION_TRIGGER_TOKENS","compactionTriggerTokens","COMPACTION_TOTAL_BUDGET","compactionTotalBudget","DEFAULT_OUTPUT_TOKENS","MODEL_MAX_OUTPUT_TOKENS","PROXY_MAX_RETRIES","PROXY_BASE_DELAY_MS","PROXY_MAX_DELAY_MS","PROXY_FALLBACK_AFTER","PROXY_MODEL_FALLBACK","proxyRetryDelay","attempt","err","delay","Math","min","pow","category","jitter","random","round","maybeDowngradeModel","apiParams","current","model","fallback","console","warn","AnthropicAdapter","name","handleStream","res","config","corsHeaders","messages","system","tools","max_tokens","temperature","stream","betas","context_management","thinking","tool_choice","documents","ANTHROPIC_API_KEY","process","env","anthropic","apiKey","type","text","cache_control","length","map","t","description","slice","input_schema","undefined","effectiveBetas","includes","push","safeContextMgmt","edits","filtered","filter","e","log","response","beta","create","toFixed","Promise","r","setTimeout","error","heartbeat","streamResponse","end","setInterval","writableEnded","write","event","JSON","stringify","clearInterval","getThinkingConfig","enabled","budget_tokens","getMaxOutputTokens","agentMax","modelMax","getContextManagement","keep","value","supportsCompaction","trigger","pause_after_compaction","instructions","getCompactionConfig","_model","triggerTokens","totalBudget","isNative"],"sources":["../../../src/server/providers/anthropic.ts"],"sourcesContent":["/**\n * Anthropic Provider Adapter — direct Anthropic API passthrough.\n *\n * Simplest adapter: system prompt gets cache_control blocks, tools get\n * { name, description, input_schema }, streaming events pass through as-is.\n *\n * Phase 7.1: Also encapsulates thinking config, context management,\n * output token limits, and compaction config for Anthropic/Claude models.\n */\n\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { sanitizeError, AGENT_DEFAULTS, isRetryableError, categorizeError } from \"../../shared/agent-core.js\";\nimport type {\n ProviderAdapter,\n ProviderStreamConfig,\n ProviderCapabilities,\n ThinkingConfigResult,\n ContextManagementConfig,\n CompactionConfig,\n} from \"./types.js\";\nimport { getCapabilities } from \"../lib/provider-capabilities.js\";\nimport { registerProvider } from \"./registry.js\";\nimport { jsonResponse, writeSSEHeaders } from \"./shared.js\";\nimport type http from \"node:http\";\n\n// ============================================================================\n// CONSTANTS — Anthropic-specific model config\n// ============================================================================\n\nconst COMPACTION_TRIGGER_TOKENS = AGENT_DEFAULTS.compactionTriggerTokens;\nconst COMPACTION_TOTAL_BUDGET = AGENT_DEFAULTS.compactionTotalBudget;\nconst DEFAULT_OUTPUT_TOKENS = 16384; // Per-response cap (model max is separate)\n\nconst MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {\n \"claude-opus-4-6\": 128000,\n \"claude-sonnet-4-6\": 64000,\n \"claude-haiku-4-5-20251001\": 64000,\n \"claude-sonnet-4-5-20250929\": 64000,\n \"claude-opus-4-5-20251101\": 64000,\n \"claude-opus-4-1-20250805\": 32768,\n \"claude-sonnet-4-20250514\": 64000,\n \"claude-opus-4-20250514\": 32768,\n \"claude-3-7-sonnet-20250219\": 64000,\n \"claude-3-haiku-20240307\": 4096,\n};\n\n// ============================================================================\n// PROXY RETRY — retries overloaded/rate-limit errors before giving up\n// ============================================================================\n\nconst PROXY_MAX_RETRIES = 4;\nconst PROXY_BASE_DELAY_MS = 3000;\nconst PROXY_MAX_DELAY_MS = 45000;\nconst PROXY_FALLBACK_AFTER = 2;\n\n// Intra-provider model fallback for proxy (Opus → Sonnet when overloaded)\nconst PROXY_MODEL_FALLBACK: Record<string, string> = {\n \"claude-opus-4-6\": \"claude-sonnet-4-6\",\n \"claude-opus-4-5-20251101\": \"claude-sonnet-4-5-20250929\",\n \"claude-opus-4-20250514\": \"claude-sonnet-4-20250514\",\n \"claude-opus-4-1-20250805\": \"claude-sonnet-4-5-20250929\",\n};\n\nfunction proxyRetryDelay(attempt: number, err: unknown): number {\n let delay = Math.min(PROXY_BASE_DELAY_MS * Math.pow(2, attempt), PROXY_MAX_DELAY_MS);\n const { category } = categorizeError(err);\n if (category === \"PROVIDER_DOWN\" || category === \"RATE_LIMIT\") {\n delay = Math.min(delay * 3, PROXY_MAX_DELAY_MS);\n }\n // Jitter ±25%\n const jitter = delay * 0.25 * (Math.random() * 2 - 1);\n return Math.round(delay + jitter);\n}\n\nfunction maybeDowngradeModel(apiParams: Record<string, unknown>, attempt: number, err: unknown): void {\n const { category } = categorizeError(err);\n if ((category === \"PROVIDER_DOWN\" || category === \"RATE_LIMIT\") && attempt >= PROXY_FALLBACK_AFTER) {\n const current = apiParams.model as string;\n const fallback = PROXY_MODEL_FALLBACK[current];\n if (fallback && current !== fallback) {\n console.warn(`[proxy] Model fallback: ${current} → ${fallback} (overloaded after ${attempt + 1} attempts)`);\n apiParams.model = fallback;\n }\n }\n}\n\n// ============================================================================\n// ADAPTER\n// ============================================================================\n\nexport class AnthropicAdapter implements ProviderAdapter {\n name = \"anthropic\";\n\n async handleStream(\n res: http.ServerResponse,\n config: ProviderStreamConfig,\n corsHeaders: Record<string, string>,\n ): Promise<void> {\n const {\n model, messages, system, tools, max_tokens, temperature,\n stream = true, betas, context_management, thinking, tool_choice, documents,\n } = config;\n\n const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY!;\n const anthropic = new Anthropic({ apiKey: ANTHROPIC_API_KEY });\n\n const apiParams: Record<string, unknown> = { model, max_tokens, messages, stream: true };\n\n if (system) {\n if (typeof system === \"string\") {\n apiParams.system = [{ type: \"text\", text: system, cache_control: { type: \"ephemeral\" } }];\n } else {\n apiParams.system = system;\n }\n }\n if (tools?.length) {\n apiParams.tools = (tools as Array<Record<string, unknown>>).map((t: Record<string, unknown>) => ({\n name: t.name,\n description: typeof t.description === \"string\" ? (t.description as string).slice(0, 4096) : \"\",\n input_schema: t.input_schema,\n }));\n }\n if (temperature !== undefined) apiParams.temperature = temperature;\n\n // Citations API: add beta and documents when source documents are provided\n const effectiveBetas = betas?.length ? [...betas] : [];\n if (documents?.length) {\n if (!effectiveBetas.includes(\"citations-2025-04-15\")) {\n effectiveBetas.push(\"citations-2025-04-15\");\n }\n apiParams.documents = documents;\n }\n if (effectiveBetas.length) apiParams.betas = effectiveBetas;\n\n // Safety: strip clear_thinking_20251015 edits when thinking is not enabled.\n // Anthropic returns 400 if this strategy is present without thinking=enabled/adaptive.\n let safeContextMgmt = context_management;\n if (safeContextMgmt?.edits?.length && !thinking) {\n const filtered = safeContextMgmt.edits.filter((e: any) => e.type !== \"clear_thinking_20251015\");\n if (filtered.length !== safeContextMgmt.edits.length) {\n console.log(`[proxy] Stripped ${safeContextMgmt.edits.length - filtered.length} clear_thinking edit(s) — thinking not enabled`);\n safeContextMgmt = { ...safeContextMgmt, edits: filtered };\n }\n }\n if (safeContextMgmt?.edits?.length) apiParams.context_management = safeContextMgmt;\n if (thinking) apiParams.thinking = thinking;\n\n // Map tool_choice to Anthropic format\n if (tool_choice) {\n if (tool_choice === \"auto\") {\n apiParams.tool_choice = { type: \"auto\" };\n } else if (tool_choice === \"any\") {\n apiParams.tool_choice = { type: \"any\" };\n } else if (tool_choice === \"none\") {\n // \"none\" means no tools — remove tools from request\n delete apiParams.tools;\n } else if (typeof tool_choice === \"object\" && tool_choice.type === \"tool\") {\n apiParams.tool_choice = { type: \"tool\", name: tool_choice.name };\n }\n }\n\n if (!stream) {\n for (let attempt = 0; attempt <= PROXY_MAX_RETRIES; attempt++) {\n try {\n const response = effectiveBetas.length\n ? await anthropic.beta.messages.create({ ...apiParams, stream: false } as any)\n : await anthropic.messages.create({ ...apiParams, stream: false } as any);\n jsonResponse(res, 200, response, corsHeaders);\n return;\n } catch (err) {\n if (attempt < PROXY_MAX_RETRIES && isRetryableError(err)) {\n maybeDowngradeModel(apiParams, attempt, err);\n const delay = proxyRetryDelay(attempt, err);\n console.warn(`[proxy] Attempt ${attempt + 1}/${PROXY_MAX_RETRIES + 1} failed (model=${apiParams.model}) — retrying in ${(delay / 1000).toFixed(1)}s: ${sanitizeError(err)}`);\n await new Promise(r => setTimeout(r, delay));\n continue;\n }\n jsonResponse(res, 500, { error: sanitizeError(err) }, corsHeaders);\n return;\n }\n }\n return;\n }\n\n // Streaming: retry the initial API call with backoff before committing to SSE.\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n let streamResponse: AsyncIterable<unknown> | undefined;\n\n for (let attempt = 0; attempt <= PROXY_MAX_RETRIES; attempt++) {\n try {\n streamResponse = (effectiveBetas.length\n ? await anthropic.beta.messages.create({ ...apiParams, stream: true } as any)\n : await anthropic.messages.create(apiParams as any)) as unknown as AsyncIterable<unknown>;\n break;\n } catch (err) {\n if (attempt < PROXY_MAX_RETRIES && isRetryableError(err)) {\n maybeDowngradeModel(apiParams, attempt, err);\n const delay = proxyRetryDelay(attempt, err);\n console.warn(`[proxy] Stream attempt ${attempt + 1}/${PROXY_MAX_RETRIES + 1} failed (model=${apiParams.model}) — retrying in ${(delay / 1000).toFixed(1)}s: ${sanitizeError(err)}`);\n await new Promise(r => setTimeout(r, delay));\n continue;\n }\n jsonResponse(res, 502, { error: `anthropic error: ${sanitizeError(err)}` }, corsHeaders);\n res.end();\n return;\n }\n }\n\n if (!streamResponse) {\n jsonResponse(res, 502, { error: \"failed to create stream after retries\" }, corsHeaders);\n res.end();\n return;\n }\n\n try {\n // Stream created successfully — now safe to commit to SSE\n writeSSEHeaders(res, corsHeaders);\n\n // Heartbeat keeps the connection alive and helps detect dead clients\n heartbeat = setInterval(() => {\n if (!res.writableEnded) res.write(\":ping\\n\\n\");\n }, 15_000);\n\n for await (const event of streamResponse) {\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n }\n res.write(\"data: [DONE]\\n\\n\");\n } catch (err) {\n // Mid-stream failure — send error event so client doesn't hang\n res.write(`data: ${JSON.stringify({ type: \"error\", error: sanitizeError(err) })}\\n\\n`);\n res.write(\"data: [DONE]\\n\\n\");\n } finally {\n if (heartbeat) clearInterval(heartbeat);\n }\n res.end();\n }\n\n // ---- Provider-specific config methods ----\n\n getThinkingConfig(model: string, enabled: boolean): ThinkingConfigResult {\n if (!enabled) {\n return { thinking: { type: \"disabled\" }, beta: \"\" };\n }\n\n if (model.includes(\"opus-4-6\") || model.includes(\"sonnet-4-6\")) {\n return {\n thinking: { type: \"adaptive\" },\n beta: \"adaptive-thinking-2026-01-28\",\n };\n }\n\n // Sonnet 4.5/4.0 / Haiku: fixed budget\n return {\n thinking: { type: \"enabled\", budget_tokens: 10_000 },\n beta: \"interleaved-thinking-2025-05-14\",\n };\n }\n\n getMaxOutputTokens(model: string, agentMax?: number): number {\n const modelMax = MODEL_MAX_OUTPUT_TOKENS[model] ?? DEFAULT_OUTPUT_TOKENS;\n if (agentMax) return Math.min(agentMax, modelMax);\n return Math.min(DEFAULT_OUTPUT_TOKENS, modelMax);\n }\n\n getContextManagement(model: string): ContextManagementConfig {\n const edits: Array<Record<string, unknown>> = [];\n const betas: string[] = [\"context-management-2025-06-27\"];\n\n // Thinking block clearing — must come FIRST in edits array (API requirement).\n edits.push({\n type: \"clear_thinking_20251015\",\n keep: { type: \"thinking_turns\", value: 2 },\n });\n\n // Server-side compaction for models that support compact_20260112.\n const supportsCompaction = model.includes(\"opus-4-6\") || model.includes(\"sonnet-4-6\");\n if (supportsCompaction) {\n edits.push({\n type: \"compact_20260112\",\n trigger: { type: \"input_tokens\", value: COMPACTION_TRIGGER_TOKENS },\n pause_after_compaction: true,\n instructions: \"Summarize the conversation preserving: (1) task goals and constraints, (2) files created/modified with paths, (3) decisions made and rationale, (4) errors encountered and resolutions, (5) exact next steps. Be concise but preserve all state needed to continue work without repeating mistakes.\",\n });\n betas.push(\"compact-2026-01-12\");\n }\n\n edits.push({\n type: \"clear_tool_uses_20250919\",\n trigger: { type: \"input_tokens\", value: 80_000 },\n keep: { type: \"tool_uses\", value: 3 },\n });\n\n return { betas, config: { edits } };\n }\n\n getCompactionConfig(_model: string): CompactionConfig {\n return {\n triggerTokens: COMPACTION_TRIGGER_TOKENS,\n totalBudget: COMPACTION_TOTAL_BUDGET,\n isNative: true,\n };\n }\n\n getCapabilities(): ProviderCapabilities {\n return getCapabilities(\"anthropic\");\n }\n}\n\n// Auto-register on import\nregisterProvider(\"anthropic\", new AnthropicAdapter());\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,7 +6,7 @@ import { createLogger } from "./lib/logger.js";
|
|
|
6
6
|
import { sanitizeError } from "../shared/agent-core.js";
|
|
7
7
|
import { jsonResponse, sendSSE } from "./server-helpers.js";
|
|
8
8
|
import { resolveAndValidateStoreId } from "./server-store.js";
|
|
9
|
-
import { checkAgentChatRateLimit,
|
|
9
|
+
import { checkAgentChatRateLimit, decrementConcurrent } from "./server-rate-limit.js";
|
|
10
10
|
import { checkCostBudget } from "./server-cost-guard.js";
|
|
11
11
|
import { checkStoreCircuitBreaker, recordStoreOutcome } from "./server-store-circuit-breaker.js";
|
|
12
12
|
import { extractOrCreateTrace } from "./server-trace.js";
|
|
@@ -106,7 +106,7 @@ export async function handleAgentChat(req, res, supabase, body, user, isServiceR
|
|
|
106
106
|
}));
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
// Concurrent count already incremented atomically inside checkAgentChatRateLimit
|
|
110
110
|
let requestSuccess = true;
|
|
111
111
|
try {
|
|
112
112
|
const userId = user?.id || body.userId || "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-chat.js","names":["createLogger","sanitizeError","jsonResponse","sendSSE","resolveAndValidateStoreId","checkAgentChatRateLimit","incrementConcurrent","decrementConcurrent","checkCostBudget","checkStoreCircuitBreaker","recordStoreOutcome","extractOrCreateTrace","SessionManager","log","handleAgentChat","req","res","supabase","body","user","isServiceRole","token","corsHeaders","agentId","message","conversationHistory","source","conversationId","context","attachments","storeId","error","length","resolvedStoreId","userId","data","srStores","from","select","eq","not","limit","store_id","undefined","trace","info","id","traceId","breakerCheck","allowed","costCheck","warning","warn","rateLimitStoreId","rateCheck","writeHead","end","JSON","stringify","requestSuccess","userEmail","email","manager","session","resumeSession","createSession","surface","Connection","traceparent","clientDisconnected","on","result","executeTurn","streaming","onText","text","type","onToolStart","name","input","onToolResult","success","r","onToolProgress","progress","onSubagentProgress","evt","subagentId","subagentEvent","event","toolName","value","usage","input_tokens","tokens","output_tokens","output","cache_creation_tokens","cacheCreation","cache_read_tokens","cacheRead","cost_usd","costUsd","err"],"sources":["../../src/server/server-chat.ts"],"sourcesContent":["// server/server-chat.ts — handleAgentChat SSE handler\n// Delegates to SessionManager for unified session lifecycle.\n// Keeps the exact same function signature for backward compatibility.\n\nimport http from \"node:http\";\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createLogger } from \"./lib/logger.js\";\nimport { sanitizeError } from \"../shared/agent-core.js\";\nimport { jsonResponse, sendSSE } from \"./server-helpers.js\";\nimport { resolveAndValidateStoreId } from \"./server-store.js\";\nimport { checkAgentChatRateLimit, incrementConcurrent, decrementConcurrent } from \"./server-rate-limit.js\";\nimport { checkCostBudget } from \"./server-cost-guard.js\";\nimport { checkStoreCircuitBreaker, recordStoreOutcome } from \"./server-store-circuit-breaker.js\";\nimport { extractOrCreateTrace, type TraceContext } from \"./server-trace.js\";\nimport { SessionManager } from \"./session-manager.js\";\n\nconst log = createLogger(\"server-chat\");\n\n// ============================================================================\n// AGENT CHAT HANDLER (SSE) — delegates to SessionManager\n// ============================================================================\n\nexport async function handleAgentChat(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n supabase: SupabaseClient,\n body: any,\n user: { id: string; email?: string } | null,\n isServiceRole: boolean,\n token: string,\n corsHeaders: Record<string, string>\n): Promise<void> {\n const { agentId, message, conversationHistory, source, conversationId, context, attachments } = body;\n let storeId: string | undefined = body.storeId;\n\n if (!agentId || !message) {\n jsonResponse(res, 400, { error: \"agentId and message required\" }, corsHeaders);\n return;\n }\n\n if (typeof message === \"string\" && message.length > 100_000) {\n jsonResponse(res, 400, { error: \"Message too long (max 100K characters)\" }, corsHeaders);\n return;\n }\n\n const resolvedStoreId = await resolveAndValidateStoreId(\n supabase, storeId, user, isServiceRole, token, body.userId\n );\n\n if (!resolvedStoreId && isServiceRole && body.userId) {\n const { data: srStores } = await supabase\n .from(\"users\").select(\"store_id\")\n .eq(\"auth_user_id\", body.userId).not(\"store_id\", \"is\", null).limit(1);\n if (srStores?.length) storeId = srStores[0].store_id;\n } else {\n storeId = resolvedStoreId || undefined;\n }\n\n if (!storeId && !isServiceRole) {\n jsonResponse(res, 400, { error: \"storeId required for agent chat\" }, corsHeaders);\n return;\n }\n\n // Extract or create W3C trace context from incoming request\n const trace: TraceContext = extractOrCreateTrace(req);\n\n log.info({ storeId: storeId || \"NONE\", source: body.source || \"unknown\", isServiceRole, userId: user?.id || body.userId || \"NONE\", traceId: trace.traceId }, \"agent-chat request\");\n\n // Per-store circuit breaker — reject if store's error rate is too high\n if (storeId) {\n const breakerCheck = checkStoreCircuitBreaker(storeId);\n if (!breakerCheck.allowed) {\n jsonResponse(res, 503, { error: breakerCheck.error }, corsHeaders);\n return;\n }\n }\n\n // DB-backed cost budget check — reject if store/agent budget is exceeded (hard_stop)\n if (storeId) {\n const costCheck = await checkCostBudget(supabase, storeId, agentId);\n if (!costCheck.allowed) {\n jsonResponse(res, 402, { error: costCheck.error }, corsHeaders);\n return;\n }\n if (costCheck.warning) {\n log.warn({ storeId, agentId, warning: costCheck.warning }, \"cost budget warning\");\n }\n }\n\n const rateLimitStoreId = storeId || agentId;\n const rateCheck = checkAgentChatRateLimit(rateLimitStoreId);\n if (!rateCheck.allowed) {\n res.writeHead(429, { \"Content-Type\": \"application/json\", ...corsHeaders });\n res.end(JSON.stringify({ error: rateCheck.error }));\n return;\n }\n incrementConcurrent();\n let requestSuccess = true;\n try {\n\n const userId: string = user?.id || body.userId || \"\";\n const userEmail: string | null = user?.email || body.userEmail || null;\n\n // Delegate to SessionManager\n const manager = new SessionManager(supabase);\n const session = conversationId\n ? await manager.resumeSession(conversationId, \"whale_chat\")\n : await manager.createSession({\n storeId: storeId!,\n agentId,\n surface: \"whale_chat\",\n userId,\n userEmail,\n message,\n });\n\n // SSE headers — include traceparent for distributed tracing\n res.writeHead(200, { \"Content-Type\": \"text/event-stream\", \"Cache-Control\": \"no-cache\", Connection: \"keep-alive\", traceparent: trace.traceparent, ...corsHeaders });\n\n let clientDisconnected = false;\n req.on(\"close\", () => { clientDisconnected = true; });\n\n try {\n const result = await manager.executeTurn(session, {\n message,\n attachments,\n conversationHistory,\n context: context || undefined,\n streaming: true,\n onText: (text) => sendSSE(res, { type: \"text\", text }),\n onToolStart: (name, input?) => { if (input !== undefined) sendSSE(res, { type: \"tool_start\", name, input: input as Record<string, unknown> }); },\n onToolResult: (name, success, r) => sendSSE(res, { type: \"tool_result\", name, success, result: r as any }),\n onToolProgress: (name, progress) => sendSSE(res, { type: \"tool_progress\", name, progress: progress as any }),\n onSubagentProgress: (evt: any) => sendSSE(res, { type: \"subagent\", subagentId: evt.subagentId, subagentEvent: evt.event, name: evt.toolName }),\n clientDisconnected: { get value() { return clientDisconnected; } },\n source: source || \"whale_chat\",\n });\n\n sendSSE(res, {\n type: \"usage\", usage: {\n input_tokens: result.tokens.input, output_tokens: result.tokens.output,\n cache_creation_tokens: result.tokens.cacheCreation, cache_read_tokens: result.tokens.cacheRead,\n cost_usd: result.costUsd,\n },\n });\n\n sendSSE(res, { type: \"done\", conversationId: session.id });\n } catch (err) {\n requestSuccess = false;\n sendSSE(res, { type: \"error\", error: sanitizeError(err) });\n }\n\n res.end();\n\n } finally {\n decrementConcurrent();\n // Record outcome for per-store circuit breaker\n if (storeId) {\n recordStoreOutcome(storeId, requestSuccess);\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"server-chat.js","names":["createLogger","sanitizeError","jsonResponse","sendSSE","resolveAndValidateStoreId","checkAgentChatRateLimit","decrementConcurrent","checkCostBudget","checkStoreCircuitBreaker","recordStoreOutcome","extractOrCreateTrace","SessionManager","log","handleAgentChat","req","res","supabase","body","user","isServiceRole","token","corsHeaders","agentId","message","conversationHistory","source","conversationId","context","attachments","storeId","error","length","resolvedStoreId","userId","data","srStores","from","select","eq","not","limit","store_id","undefined","trace","info","id","traceId","breakerCheck","allowed","costCheck","warning","warn","rateLimitStoreId","rateCheck","writeHead","end","JSON","stringify","requestSuccess","userEmail","email","manager","session","resumeSession","createSession","surface","Connection","traceparent","clientDisconnected","on","result","executeTurn","streaming","onText","text","type","onToolStart","name","input","onToolResult","success","r","onToolProgress","progress","onSubagentProgress","evt","subagentId","subagentEvent","event","toolName","value","usage","input_tokens","tokens","output_tokens","output","cache_creation_tokens","cacheCreation","cache_read_tokens","cacheRead","cost_usd","costUsd","err"],"sources":["../../src/server/server-chat.ts"],"sourcesContent":["// server/server-chat.ts — handleAgentChat SSE handler\n// Delegates to SessionManager for unified session lifecycle.\n// Keeps the exact same function signature for backward compatibility.\n\nimport http from \"node:http\";\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createLogger } from \"./lib/logger.js\";\nimport { sanitizeError } from \"../shared/agent-core.js\";\nimport { jsonResponse, sendSSE } from \"./server-helpers.js\";\nimport { resolveAndValidateStoreId } from \"./server-store.js\";\nimport { checkAgentChatRateLimit, decrementConcurrent } from \"./server-rate-limit.js\";\nimport { checkCostBudget } from \"./server-cost-guard.js\";\nimport { checkStoreCircuitBreaker, recordStoreOutcome } from \"./server-store-circuit-breaker.js\";\nimport { extractOrCreateTrace, type TraceContext } from \"./server-trace.js\";\nimport { SessionManager } from \"./session-manager.js\";\n\nconst log = createLogger(\"server-chat\");\n\n// ============================================================================\n// AGENT CHAT HANDLER (SSE) — delegates to SessionManager\n// ============================================================================\n\nexport async function handleAgentChat(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n supabase: SupabaseClient,\n body: any,\n user: { id: string; email?: string } | null,\n isServiceRole: boolean,\n token: string,\n corsHeaders: Record<string, string>\n): Promise<void> {\n const { agentId, message, conversationHistory, source, conversationId, context, attachments } = body;\n let storeId: string | undefined = body.storeId;\n\n if (!agentId || !message) {\n jsonResponse(res, 400, { error: \"agentId and message required\" }, corsHeaders);\n return;\n }\n\n if (typeof message === \"string\" && message.length > 100_000) {\n jsonResponse(res, 400, { error: \"Message too long (max 100K characters)\" }, corsHeaders);\n return;\n }\n\n const resolvedStoreId = await resolveAndValidateStoreId(\n supabase, storeId, user, isServiceRole, token, body.userId\n );\n\n if (!resolvedStoreId && isServiceRole && body.userId) {\n const { data: srStores } = await supabase\n .from(\"users\").select(\"store_id\")\n .eq(\"auth_user_id\", body.userId).not(\"store_id\", \"is\", null).limit(1);\n if (srStores?.length) storeId = srStores[0].store_id;\n } else {\n storeId = resolvedStoreId || undefined;\n }\n\n if (!storeId && !isServiceRole) {\n jsonResponse(res, 400, { error: \"storeId required for agent chat\" }, corsHeaders);\n return;\n }\n\n // Extract or create W3C trace context from incoming request\n const trace: TraceContext = extractOrCreateTrace(req);\n\n log.info({ storeId: storeId || \"NONE\", source: body.source || \"unknown\", isServiceRole, userId: user?.id || body.userId || \"NONE\", traceId: trace.traceId }, \"agent-chat request\");\n\n // Per-store circuit breaker — reject if store's error rate is too high\n if (storeId) {\n const breakerCheck = checkStoreCircuitBreaker(storeId);\n if (!breakerCheck.allowed) {\n jsonResponse(res, 503, { error: breakerCheck.error }, corsHeaders);\n return;\n }\n }\n\n // DB-backed cost budget check — reject if store/agent budget is exceeded (hard_stop)\n if (storeId) {\n const costCheck = await checkCostBudget(supabase, storeId, agentId);\n if (!costCheck.allowed) {\n jsonResponse(res, 402, { error: costCheck.error }, corsHeaders);\n return;\n }\n if (costCheck.warning) {\n log.warn({ storeId, agentId, warning: costCheck.warning }, \"cost budget warning\");\n }\n }\n\n const rateLimitStoreId = storeId || agentId;\n const rateCheck = checkAgentChatRateLimit(rateLimitStoreId);\n if (!rateCheck.allowed) {\n res.writeHead(429, { \"Content-Type\": \"application/json\", ...corsHeaders });\n res.end(JSON.stringify({ error: rateCheck.error }));\n return;\n }\n // Concurrent count already incremented atomically inside checkAgentChatRateLimit\n let requestSuccess = true;\n try {\n\n const userId: string = user?.id || body.userId || \"\";\n const userEmail: string | null = user?.email || body.userEmail || null;\n\n // Delegate to SessionManager\n const manager = new SessionManager(supabase);\n const session = conversationId\n ? await manager.resumeSession(conversationId, \"whale_chat\")\n : await manager.createSession({\n storeId: storeId!,\n agentId,\n surface: \"whale_chat\",\n userId,\n userEmail,\n message,\n });\n\n // SSE headers — include traceparent for distributed tracing\n res.writeHead(200, { \"Content-Type\": \"text/event-stream\", \"Cache-Control\": \"no-cache\", Connection: \"keep-alive\", traceparent: trace.traceparent, ...corsHeaders });\n\n let clientDisconnected = false;\n req.on(\"close\", () => { clientDisconnected = true; });\n\n try {\n const result = await manager.executeTurn(session, {\n message,\n attachments,\n conversationHistory,\n context: context || undefined,\n streaming: true,\n onText: (text) => sendSSE(res, { type: \"text\", text }),\n onToolStart: (name, input?) => { if (input !== undefined) sendSSE(res, { type: \"tool_start\", name, input: input as Record<string, unknown> }); },\n onToolResult: (name, success, r) => sendSSE(res, { type: \"tool_result\", name, success, result: r as any }),\n onToolProgress: (name, progress) => sendSSE(res, { type: \"tool_progress\", name, progress: progress as any }),\n onSubagentProgress: (evt: any) => sendSSE(res, { type: \"subagent\", subagentId: evt.subagentId, subagentEvent: evt.event, name: evt.toolName }),\n clientDisconnected: { get value() { return clientDisconnected; } },\n source: source || \"whale_chat\",\n });\n\n sendSSE(res, {\n type: \"usage\", usage: {\n input_tokens: result.tokens.input, output_tokens: result.tokens.output,\n cache_creation_tokens: result.tokens.cacheCreation, cache_read_tokens: result.tokens.cacheRead,\n cost_usd: result.costUsd,\n },\n });\n\n sendSSE(res, { type: \"done\", conversationId: session.id });\n } catch (err) {\n requestSuccess = false;\n sendSSE(res, { type: \"error\", error: sanitizeError(err) });\n }\n\n res.end();\n\n } finally {\n decrementConcurrent();\n // Record outcome for per-store circuit breaker\n if (storeId) {\n recordStoreOutcome(storeId, requestSuccess);\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -31,6 +31,13 @@ export interface StreamEvent {
|
|
|
31
31
|
}
|
|
32
32
|
/** Timing-safe secret comparison — hash both to fixed length to avoid leaking secret length */
|
|
33
33
|
export declare function safeCompare(a: string, b: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Extract the real client IP address.
|
|
36
|
+
* Fly.io sets `fly-client-ip` directly. For X-Forwarded-For, the real IP is
|
|
37
|
+
* the LAST value (Fly appends it), so we use .pop() instead of [0] to prevent
|
|
38
|
+
* spoofing via prepended fake IPs.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getClientIp(req: http.IncomingMessage): string;
|
|
34
41
|
export declare function getCorsHeaders(origin?: string): Record<string, string>;
|
|
35
42
|
export declare function jsonResponse(res: http.ServerResponse, status: number, data: unknown, corsHeaders: Record<string, string>): void;
|
|
36
43
|
export declare function sendSSE(res: http.ServerResponse, event: StreamEvent): void;
|
|
@@ -40,7 +47,7 @@ export declare function getAnthropicClient(agent: AgentConfig): Anthropic;
|
|
|
40
47
|
* Uses a linear indexOf scan — safe on strings of any size (no regex stack overflow).
|
|
41
48
|
*/
|
|
42
49
|
export declare function stripLargeBase64Fields(raw: string, keepFrom: number): string;
|
|
43
|
-
export declare function readBody(req: http.IncomingMessage): Promise<string>;
|
|
50
|
+
export declare function readBody(req: http.IncomingMessage, maxBytes?: number): Promise<string>;
|
|
44
51
|
/**
|
|
45
52
|
* Compact conversation history to fit within a total character budget.
|
|
46
53
|
* Only enforces a total budget by walking newest-to-oldest and
|
|
@@ -29,6 +29,20 @@ export function safeCompare(a, b) {
|
|
|
29
29
|
return timingSafeEqual(hashA, hashB);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// CLIENT IP EXTRACTION
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Extract the real client IP address.
|
|
38
|
+
* Fly.io sets `fly-client-ip` directly. For X-Forwarded-For, the real IP is
|
|
39
|
+
* the LAST value (Fly appends it), so we use .pop() instead of [0] to prevent
|
|
40
|
+
* spoofing via prepended fake IPs.
|
|
41
|
+
*/
|
|
42
|
+
export function getClientIp(req) {
|
|
43
|
+
return req.headers["fly-client-ip"]?.toString() || req.headers["x-forwarded-for"]?.toString().split(",").pop()?.trim() || req.socket.remoteAddress || "unknown";
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
// ============================================================================
|
|
33
47
|
// CORS
|
|
34
48
|
// ============================================================================
|
|
@@ -142,8 +156,8 @@ export function stripLargeBase64Fields(raw, keepFrom) {
|
|
|
142
156
|
}
|
|
143
157
|
return result;
|
|
144
158
|
}
|
|
145
|
-
export async function readBody(req) {
|
|
146
|
-
const MAX_BODY =
|
|
159
|
+
export async function readBody(req, maxBytes) {
|
|
160
|
+
const MAX_BODY = maxBytes ?? 10_485_760; // 10MB default (override per-endpoint for large payloads)
|
|
147
161
|
return new Promise((resolve, reject) => {
|
|
148
162
|
const chunks = [];
|
|
149
163
|
let size = 0;
|
|
@@ -153,7 +167,7 @@ export async function readBody(req) {
|
|
|
153
167
|
if (size > MAX_BODY && !rejected) {
|
|
154
168
|
rejected = true;
|
|
155
169
|
req.resume();
|
|
156
|
-
reject(new Error(
|
|
170
|
+
reject(new Error(`Request body too large (max ${Math.round(MAX_BODY / 1_048_576)}MB)`));
|
|
157
171
|
return;
|
|
158
172
|
}
|
|
159
173
|
if (!rejected) chunks.push(chunk);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-helpers.js","names":["timingSafeEqual","createHash","Anthropic","createLogger","log","ANTHROPIC_API_KEY","process","env","ALLOWED_ORIGINS","split","map","s","trim","safeCompare","a","b","hashA","update","digest","hashB","getCorsHeaders","origin","headers","includes","jsonResponse","res","status","data","corsHeaders","writeHead","end","JSON","stringify","sendSSE","event","destroyed","writableEnded","write","getAnthropicClient","agent","key","api_key","apiKey","timeout","stripLargeBase64Fields","raw","keepFrom","MIN_DATA_LEN","result","DATA_MARKER","parts","pos","idx","indexOf","push","slice","length","join","IMG_MARKER","adjustedKeepFrom","Math","min","readBody","req","MAX_BODY","Promise","resolve","reject","chunks","size","rejected","on","chunk","resume","Error","declaredLen","parseInt","error","receivedLen","Buffer","concat","toString","max","info","originalBytes","prunedBytes","byteLength","compactHistory","history","maxHistoryChars","totalChars","compacted","i","msg","msgChars","content","unshift","role","shift"],"sources":["../../src/server/server-helpers.ts"],"sourcesContent":["// server/server-helpers.ts — Shared HTTP utilities for the agent server\n// Extracted from index.ts for modularity.\n\nimport http from \"node:http\";\nimport { timingSafeEqual, createHash } from \"node:crypto\";\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { createLogger } from \"./lib/logger.js\";\nimport type { AgentConfig } from \"./tool-router.js\";\n\nconst log = createLogger(\"server-helpers\");\n\n// ============================================================================\n// CONSTANTS (exported for use by other modules)\n// ============================================================================\n\nconst ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY!;\nconst ALLOWED_ORIGINS = (process.env.ALLOWED_ORIGINS || \"http://localhost:3000,http://127.0.0.1:3000\").split(\",\").map(s => s.trim());\n\n// ============================================================================\n// TYPES (exported)\n// ============================================================================\n\nexport interface ServerContext {\n supabase: import(\"@supabase/supabase-js\").SupabaseClient;\n corsHeaders: Record<string, string>;\n url: URL;\n pathname: string;\n body?: any;\n clientIp?: string;\n}\n\nexport interface StreamEvent {\n type: \"text\" | \"tool_start\" | \"tool_result\" | \"tool_progress\" | \"error\" | \"done\" | \"usage\" | \"subagent\";\n text?: string;\n name?: string;\n input?: Record<string, unknown>;\n result?: unknown;\n success?: boolean;\n error?: string;\n progress?: unknown;\n usage?: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_tokens?: number;\n cache_read_tokens?: number;\n cost_usd?: number;\n };\n conversationId?: string;\n subagentId?: string;\n subagentEvent?: string;\n}\n\n// ============================================================================\n// TIMING-SAFE COMPARISON\n// ============================================================================\n\n/** Timing-safe secret comparison — hash both to fixed length to avoid leaking secret length */\nexport function safeCompare(a: string, b: string): boolean {\n if (!a || !b) return false;\n const hashA = createHash(\"sha256\").update(a).digest();\n const hashB = createHash(\"sha256\").update(b).digest();\n return timingSafeEqual(hashA, hashB);\n}\n\n// ============================================================================\n// CORS\n// ============================================================================\n\nexport function getCorsHeaders(origin?: string): Record<string, string> {\n const headers: Record<string, string> = {\n \"X-Content-Type-Options\": \"nosniff\",\n \"X-Frame-Options\": \"DENY\",\n \"X-XSS-Protection\": \"0\",\n \"Referrer-Policy\": \"strict-origin-when-cross-origin\",\n };\n\n if (ALLOWED_ORIGINS.includes(\"*\")) {\n headers[\"Access-Control-Allow-Origin\"] = \"*\";\n } else if (origin && ALLOWED_ORIGINS.includes(origin)) {\n headers[\"Access-Control-Allow-Origin\"] = origin;\n headers[\"Vary\"] = \"Origin\";\n }\n\n headers[\"Access-Control-Allow-Methods\"] = \"GET, POST, OPTIONS\";\n headers[\"Access-Control-Allow-Headers\"] = \"Content-Type, Authorization, X-Store-Id\";\n return headers;\n}\n\n// ============================================================================\n// JSON & SSE RESPONSE HELPERS\n// ============================================================================\n\nexport function jsonResponse(res: http.ServerResponse, status: number, data: unknown, corsHeaders: Record<string, string>): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\", ...corsHeaders });\n res.end(JSON.stringify(data));\n}\n\nexport function sendSSE(res: http.ServerResponse, event: StreamEvent): void {\n try {\n if (res.destroyed || res.writableEnded) return;\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n } catch { /* client disconnected — benign */ }\n}\n\nexport function getAnthropicClient(agent: AgentConfig): Anthropic {\n const key = agent.api_key || ANTHROPIC_API_KEY;\n return new Anthropic({ apiKey: key, timeout: 15 * 60 * 1000 }); // 15 min for tool-heavy requests\n}\n\n// ============================================================================\n// REQUEST BODY READING\n// ============================================================================\n\n/**\n * Strip large base64 data fields from a JSON string up to `keepFrom` offset.\n * Uses a linear indexOf scan — safe on strings of any size (no regex stack overflow).\n */\nexport function stripLargeBase64Fields(raw: string, keepFrom: number): string {\n const MIN_DATA_LEN = 8_000;\n\n // Pass 1: Strip \"data\":\"<large base64>\" fields (Anthropic image source blocks)\n let result = raw;\n {\n const DATA_MARKER = '\"data\":\"';\n const parts: string[] = [];\n let pos = 0;\n\n while (pos < keepFrom) {\n const idx = result.indexOf(DATA_MARKER, pos);\n if (idx === -1 || idx >= keepFrom) {\n parts.push(result.slice(pos, keepFrom));\n pos = keepFrom;\n break;\n }\n\n parts.push(result.slice(pos, idx + DATA_MARKER.length));\n pos = idx + DATA_MARKER.length;\n\n let end = pos;\n while (end < result.length && result[end] !== '\"') end++;\n\n if (end - pos >= MIN_DATA_LEN && end <= keepFrom) {\n parts.push('\"');\n pos = end + 1;\n }\n }\n\n parts.push(result.slice(keepFrom));\n result = parts.join(\"\");\n }\n\n // Pass 2: Strip __IMAGE__<mime>__<large base64> text markers\n {\n const IMG_MARKER = \"__IMAGE__\";\n const adjustedKeepFrom = Math.min(keepFrom, result.length);\n const parts: string[] = [];\n let pos = 0;\n\n while (pos < adjustedKeepFrom) {\n const idx = result.indexOf(IMG_MARKER, pos);\n if (idx === -1 || idx >= adjustedKeepFrom) {\n parts.push(result.slice(pos, adjustedKeepFrom));\n pos = adjustedKeepFrom;\n break;\n }\n\n parts.push(result.slice(pos, idx));\n\n let end = idx;\n while (end < result.length && result[end] !== '\"') end++;\n\n if (end - idx >= MIN_DATA_LEN && end <= adjustedKeepFrom) {\n parts.push(\"[image pruned]\");\n pos = end;\n } else {\n parts.push(result.slice(idx, idx + IMG_MARKER.length));\n pos = idx + IMG_MARKER.length;\n }\n }\n\n parts.push(result.slice(adjustedKeepFrom));\n result = parts.join(\"\");\n }\n\n return result;\n}\n\nexport async function readBody(req: http.IncomingMessage): Promise<string> {\n const MAX_BODY = 524_288_000; // 500MB\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let rejected = false;\n req.on(\"data\", (chunk: Buffer) => {\n size += chunk.length;\n if (size > MAX_BODY && !rejected) {\n rejected = true;\n req.resume();\n reject(new Error(\"Request body too large (max 500MB)\"));\n return;\n }\n if (!rejected) chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (rejected) return;\n\n const declaredLen = parseInt(req.headers[\"content-length\"] || \"0\", 10);\n if (declaredLen > 0 && size < declaredLen) {\n log.error({ declaredLen, receivedLen: size }, \"Request body truncated during transmission\");\n reject(new Error(`Body truncated: expected ${declaredLen} bytes, got ${size}`));\n return;\n }\n\n let raw = Buffer.concat(chunks).toString(\"utf8\");\n\n if (raw.length > 1_000_000) {\n const keepFrom = Math.max(0, raw.length - 1_048_576);\n raw = stripLargeBase64Fields(raw, keepFrom);\n log.info({ originalBytes: size, prunedBytes: Buffer.byteLength(raw) }, \"Pruned old base64 data from request body\");\n }\n\n resolve(raw);\n });\n req.on(\"error\", reject);\n });\n}\n\n// ============================================================================\n// HISTORY COMPACTION\n// ============================================================================\n\n/**\n * Compact conversation history to fit within a total character budget.\n * Only enforces a total budget by walking newest-to-oldest and\n * dropping the oldest messages that don't fit.\n */\nexport function compactHistory(\n history: Anthropic.MessageParam[],\n maxHistoryChars: number,\n): Anthropic.MessageParam[] {\n if (!history?.length) return [];\n let totalChars = 0;\n const compacted: Anthropic.MessageParam[] = [];\n for (let i = history.length - 1; i >= 0; i--) {\n const msg = history[i];\n const msgChars = JSON.stringify(msg.content).length;\n if (totalChars + msgChars > maxHistoryChars) break;\n totalChars += msgChars;\n compacted.unshift(msg);\n }\n while (compacted.length > 0 && compacted[0].role !== \"user\") compacted.shift();\n return compacted;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"server-helpers.js","names":["timingSafeEqual","createHash","Anthropic","createLogger","log","ANTHROPIC_API_KEY","process","env","ALLOWED_ORIGINS","split","map","s","trim","safeCompare","a","b","hashA","update","digest","hashB","getClientIp","req","headers","toString","pop","socket","remoteAddress","getCorsHeaders","origin","includes","jsonResponse","res","status","data","corsHeaders","writeHead","end","JSON","stringify","sendSSE","event","destroyed","writableEnded","write","getAnthropicClient","agent","key","api_key","apiKey","timeout","stripLargeBase64Fields","raw","keepFrom","MIN_DATA_LEN","result","DATA_MARKER","parts","pos","idx","indexOf","push","slice","length","join","IMG_MARKER","adjustedKeepFrom","Math","min","readBody","maxBytes","MAX_BODY","Promise","resolve","reject","chunks","size","rejected","on","chunk","resume","Error","round","declaredLen","parseInt","error","receivedLen","Buffer","concat","max","info","originalBytes","prunedBytes","byteLength","compactHistory","history","maxHistoryChars","totalChars","compacted","i","msg","msgChars","content","unshift","role","shift"],"sources":["../../src/server/server-helpers.ts"],"sourcesContent":["// server/server-helpers.ts — Shared HTTP utilities for the agent server\n// Extracted from index.ts for modularity.\n\nimport http from \"node:http\";\nimport { timingSafeEqual, createHash } from \"node:crypto\";\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { createLogger } from \"./lib/logger.js\";\nimport type { AgentConfig } from \"./tool-router.js\";\n\nconst log = createLogger(\"server-helpers\");\n\n// ============================================================================\n// CONSTANTS (exported for use by other modules)\n// ============================================================================\n\nconst ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY!;\nconst ALLOWED_ORIGINS = (process.env.ALLOWED_ORIGINS || \"http://localhost:3000,http://127.0.0.1:3000\").split(\",\").map(s => s.trim());\n\n// ============================================================================\n// TYPES (exported)\n// ============================================================================\n\nexport interface ServerContext {\n supabase: import(\"@supabase/supabase-js\").SupabaseClient;\n corsHeaders: Record<string, string>;\n url: URL;\n pathname: string;\n body?: any;\n clientIp?: string;\n}\n\nexport interface StreamEvent {\n type: \"text\" | \"tool_start\" | \"tool_result\" | \"tool_progress\" | \"error\" | \"done\" | \"usage\" | \"subagent\";\n text?: string;\n name?: string;\n input?: Record<string, unknown>;\n result?: unknown;\n success?: boolean;\n error?: string;\n progress?: unknown;\n usage?: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_tokens?: number;\n cache_read_tokens?: number;\n cost_usd?: number;\n };\n conversationId?: string;\n subagentId?: string;\n subagentEvent?: string;\n}\n\n// ============================================================================\n// TIMING-SAFE COMPARISON\n// ============================================================================\n\n/** Timing-safe secret comparison — hash both to fixed length to avoid leaking secret length */\nexport function safeCompare(a: string, b: string): boolean {\n if (!a || !b) return false;\n const hashA = createHash(\"sha256\").update(a).digest();\n const hashB = createHash(\"sha256\").update(b).digest();\n return timingSafeEqual(hashA, hashB);\n}\n\n// ============================================================================\n// CLIENT IP EXTRACTION\n// ============================================================================\n\n/**\n * Extract the real client IP address.\n * Fly.io sets `fly-client-ip` directly. For X-Forwarded-For, the real IP is\n * the LAST value (Fly appends it), so we use .pop() instead of [0] to prevent\n * spoofing via prepended fake IPs.\n */\nexport function getClientIp(req: http.IncomingMessage): string {\n return req.headers[\"fly-client-ip\"]?.toString()\n || req.headers[\"x-forwarded-for\"]?.toString().split(\",\").pop()?.trim()\n || req.socket.remoteAddress || \"unknown\";\n}\n\n// ============================================================================\n// CORS\n// ============================================================================\n\nexport function getCorsHeaders(origin?: string): Record<string, string> {\n const headers: Record<string, string> = {\n \"X-Content-Type-Options\": \"nosniff\",\n \"X-Frame-Options\": \"DENY\",\n \"X-XSS-Protection\": \"0\",\n \"Referrer-Policy\": \"strict-origin-when-cross-origin\",\n };\n\n if (ALLOWED_ORIGINS.includes(\"*\")) {\n headers[\"Access-Control-Allow-Origin\"] = \"*\";\n } else if (origin && ALLOWED_ORIGINS.includes(origin)) {\n headers[\"Access-Control-Allow-Origin\"] = origin;\n headers[\"Vary\"] = \"Origin\";\n }\n\n headers[\"Access-Control-Allow-Methods\"] = \"GET, POST, OPTIONS\";\n headers[\"Access-Control-Allow-Headers\"] = \"Content-Type, Authorization, X-Store-Id\";\n return headers;\n}\n\n// ============================================================================\n// JSON & SSE RESPONSE HELPERS\n// ============================================================================\n\nexport function jsonResponse(res: http.ServerResponse, status: number, data: unknown, corsHeaders: Record<string, string>): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\", ...corsHeaders });\n res.end(JSON.stringify(data));\n}\n\nexport function sendSSE(res: http.ServerResponse, event: StreamEvent): void {\n try {\n if (res.destroyed || res.writableEnded) return;\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n } catch { /* client disconnected — benign */ }\n}\n\nexport function getAnthropicClient(agent: AgentConfig): Anthropic {\n const key = agent.api_key || ANTHROPIC_API_KEY;\n return new Anthropic({ apiKey: key, timeout: 15 * 60 * 1000 }); // 15 min for tool-heavy requests\n}\n\n// ============================================================================\n// REQUEST BODY READING\n// ============================================================================\n\n/**\n * Strip large base64 data fields from a JSON string up to `keepFrom` offset.\n * Uses a linear indexOf scan — safe on strings of any size (no regex stack overflow).\n */\nexport function stripLargeBase64Fields(raw: string, keepFrom: number): string {\n const MIN_DATA_LEN = 8_000;\n\n // Pass 1: Strip \"data\":\"<large base64>\" fields (Anthropic image source blocks)\n let result = raw;\n {\n const DATA_MARKER = '\"data\":\"';\n const parts: string[] = [];\n let pos = 0;\n\n while (pos < keepFrom) {\n const idx = result.indexOf(DATA_MARKER, pos);\n if (idx === -1 || idx >= keepFrom) {\n parts.push(result.slice(pos, keepFrom));\n pos = keepFrom;\n break;\n }\n\n parts.push(result.slice(pos, idx + DATA_MARKER.length));\n pos = idx + DATA_MARKER.length;\n\n let end = pos;\n while (end < result.length && result[end] !== '\"') end++;\n\n if (end - pos >= MIN_DATA_LEN && end <= keepFrom) {\n parts.push('\"');\n pos = end + 1;\n }\n }\n\n parts.push(result.slice(keepFrom));\n result = parts.join(\"\");\n }\n\n // Pass 2: Strip __IMAGE__<mime>__<large base64> text markers\n {\n const IMG_MARKER = \"__IMAGE__\";\n const adjustedKeepFrom = Math.min(keepFrom, result.length);\n const parts: string[] = [];\n let pos = 0;\n\n while (pos < adjustedKeepFrom) {\n const idx = result.indexOf(IMG_MARKER, pos);\n if (idx === -1 || idx >= adjustedKeepFrom) {\n parts.push(result.slice(pos, adjustedKeepFrom));\n pos = adjustedKeepFrom;\n break;\n }\n\n parts.push(result.slice(pos, idx));\n\n let end = idx;\n while (end < result.length && result[end] !== '\"') end++;\n\n if (end - idx >= MIN_DATA_LEN && end <= adjustedKeepFrom) {\n parts.push(\"[image pruned]\");\n pos = end;\n } else {\n parts.push(result.slice(idx, idx + IMG_MARKER.length));\n pos = idx + IMG_MARKER.length;\n }\n }\n\n parts.push(result.slice(adjustedKeepFrom));\n result = parts.join(\"\");\n }\n\n return result;\n}\n\nexport async function readBody(req: http.IncomingMessage, maxBytes?: number): Promise<string> {\n const MAX_BODY = maxBytes ?? 10_485_760; // 10MB default (override per-endpoint for large payloads)\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let rejected = false;\n req.on(\"data\", (chunk: Buffer) => {\n size += chunk.length;\n if (size > MAX_BODY && !rejected) {\n rejected = true;\n req.resume();\n reject(new Error(`Request body too large (max ${Math.round(MAX_BODY / 1_048_576)}MB)`));\n return;\n }\n if (!rejected) chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (rejected) return;\n\n const declaredLen = parseInt(req.headers[\"content-length\"] || \"0\", 10);\n if (declaredLen > 0 && size < declaredLen) {\n log.error({ declaredLen, receivedLen: size }, \"Request body truncated during transmission\");\n reject(new Error(`Body truncated: expected ${declaredLen} bytes, got ${size}`));\n return;\n }\n\n let raw = Buffer.concat(chunks).toString(\"utf8\");\n\n if (raw.length > 1_000_000) {\n const keepFrom = Math.max(0, raw.length - 1_048_576);\n raw = stripLargeBase64Fields(raw, keepFrom);\n log.info({ originalBytes: size, prunedBytes: Buffer.byteLength(raw) }, \"Pruned old base64 data from request body\");\n }\n\n resolve(raw);\n });\n req.on(\"error\", reject);\n });\n}\n\n// ============================================================================\n// HISTORY COMPACTION\n// ============================================================================\n\n/**\n * Compact conversation history to fit within a total character budget.\n * Only enforces a total budget by walking newest-to-oldest and\n * dropping the oldest messages that don't fit.\n */\nexport function compactHistory(\n history: Anthropic.MessageParam[],\n maxHistoryChars: number,\n): Anthropic.MessageParam[] {\n if (!history?.length) return [];\n let totalChars = 0;\n const compacted: Anthropic.MessageParam[] = [];\n for (let i = history.length - 1; i >= 0; i--) {\n const msg = history[i];\n const msgChars = JSON.stringify(msg.content).length;\n if (totalChars + msgChars > maxHistoryChars) break;\n totalChars += msgChars;\n compacted.unshift(msg);\n }\n while (compacted.length > 0 && compacted[0].role !== \"user\") compacted.shift();\n return compacted;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -58,25 +58,33 @@ export async function persistAgentTurn(supabase, agent, opts) {
|
|
|
58
58
|
}, "message persist failed");
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// Update conversation metadata
|
|
61
|
+
// Update conversation metadata (merge with existing to avoid overwriting prior fields)
|
|
62
62
|
try {
|
|
63
|
+
const {
|
|
64
|
+
data: existingConv
|
|
65
|
+
} = await supabase.from("ai_conversations").select("metadata").eq("id", conversationId).maybeSingle();
|
|
66
|
+
const newMeta = {
|
|
67
|
+
agentName: agent.name,
|
|
68
|
+
source,
|
|
69
|
+
model: agentModel,
|
|
70
|
+
lastTurnTokens: result.tokens.input + result.tokens.output,
|
|
71
|
+
lastToolCalls: result.toolCallCount,
|
|
72
|
+
lastDurationMs: chatEndTime - chatStartTime,
|
|
73
|
+
...(senderContext ? {
|
|
74
|
+
channel_type: senderContext.channelType || null,
|
|
75
|
+
channel_id: senderContext.channelId || null,
|
|
76
|
+
channel_name: senderContext.channelName || null,
|
|
77
|
+
sender_id: senderContext.senderId || null,
|
|
78
|
+
customer_id: senderContext.customerId || null,
|
|
79
|
+
customer_name: senderContext.customerName || null
|
|
80
|
+
} : {})
|
|
81
|
+
};
|
|
82
|
+
const mergedMeta = {
|
|
83
|
+
...(existingConv?.metadata || {}),
|
|
84
|
+
...newMeta
|
|
85
|
+
};
|
|
63
86
|
await supabase.from("ai_conversations").update({
|
|
64
|
-
metadata:
|
|
65
|
-
agentName: agent.name,
|
|
66
|
-
source,
|
|
67
|
-
model: agentModel,
|
|
68
|
-
lastTurnTokens: result.tokens.input + result.tokens.output,
|
|
69
|
-
lastToolCalls: result.toolCallCount,
|
|
70
|
-
lastDurationMs: chatEndTime - chatStartTime,
|
|
71
|
-
...(senderContext ? {
|
|
72
|
-
channel_type: senderContext.channelType || null,
|
|
73
|
-
channel_id: senderContext.channelId || null,
|
|
74
|
-
channel_name: senderContext.channelName || null,
|
|
75
|
-
sender_id: senderContext.senderId || null,
|
|
76
|
-
customer_id: senderContext.customerId || null,
|
|
77
|
-
customer_name: senderContext.customerName || null
|
|
78
|
-
} : {})
|
|
79
|
-
}
|
|
87
|
+
metadata: mergedMeta
|
|
80
88
|
}).eq("id", conversationId);
|
|
81
89
|
} catch (err) {
|
|
82
90
|
log.error({
|
|
@@ -236,11 +244,16 @@ export async function persistAgentTurn(supabase, agent, opts) {
|
|
|
236
244
|
log.error({
|
|
237
245
|
err: err2.message
|
|
238
246
|
}, "cost budget update failed after retry");
|
|
247
|
+
const {
|
|
248
|
+
data: existingConv2
|
|
249
|
+
} = await supabase.from("ai_conversations").select("metadata").eq("id", conversationId).maybeSingle();
|
|
250
|
+
const mergedBudgetMeta = {
|
|
251
|
+
...(existingConv2?.metadata || {}),
|
|
252
|
+
budget_sync_failed: true,
|
|
253
|
+
failed_cost_usd: result.costUsd
|
|
254
|
+
};
|
|
239
255
|
await supabase.from("ai_conversations").update({
|
|
240
|
-
metadata:
|
|
241
|
-
budget_sync_failed: true,
|
|
242
|
-
failed_cost_usd: result.costUsd
|
|
243
|
-
}
|
|
256
|
+
metadata: mergedBudgetMeta
|
|
244
257
|
}).eq("id", conversationId).then(() => {});
|
|
245
258
|
}
|
|
246
259
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-persist.js","names":["createLogger","queueSpan","auditRowToSpan","classifyErrorType","getAnthropicClient","extractAndStoreMemories","updateCostBudgets","invalidateCostCache","log","persistAgentTurn","supabase","agent","opts","conversationId","storeId","agentId","agentModel","traceId","message","result","source","chatStartTime","chatEndTime","userId","userEmail","senderContext","from","insert","conversation_id","role","content","type","text","token_count","Math","ceil","length","finalText","is_tool_use","toolCallCount","tool_names","toolsUsed","tokens","input","output","err","error","update","metadata","agentName","name","model","lastTurnTokens","lastToolCalls","lastDurationMs","channel_type","channelType","channel_id","channelId","channel_name","channelName","sender_id","senderId","customer_id","customerId","customer_name","customerName","eq","action","severity","store_id","resource_type","resource_id","request_id","user_id","user_email","service_name","span_kind","status_code","start_time","Date","toISOString","end_time","duration_ms","input_bytes","details","message_preview","substring","agent_id","input_tokens","output_tokens","total_cost","costUsd","trace_id","stop_reason","stopReason","undefined","turn_number","turnCount","response_preview","cacheCreation","cacheRead","turn_count","tool_calls","session_cost_usd","cache_creation_tokens","cache_read_tokens","cache_hit_rate","round","cache_cost_savings_pct","loop_detector_stats","loopDetectorStats","turns","err1","Promise","r","setTimeout","err2","error_type","error_message","user_message_preview","budget_sync_failed","failed_cost_usd","then"],"sources":["../../src/server/server-persist.ts"],"sourcesContent":["// server/server-persist.ts — Agent turn persistence (messages, telemetry, memory, cost)\n// Extracted from server-agent.ts to keep each module under 300 lines.\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createLogger } from \"./lib/logger.js\";\nimport { queueSpan, auditRowToSpan, classifyErrorType } from \"./lib/clickhouse-buffer.js\";\nimport type { AgentConfig } from \"./tool-router.js\";\nimport type { SenderContext } from \"./handlers/nodes.js\";\nimport { getAnthropicClient } from \"./server-helpers.js\";\nimport { extractAndStoreMemories, updateCostBudgets } from \"./server-agent.js\";\nimport { invalidateCostCache } from \"./server-cost-guard.js\";\n\nconst log = createLogger(\"server-persist\");\n\n// ============================================================================\n// PERSIST AGENT TURN — messages, audit, memory, cost\n// ============================================================================\n\n/** Persist everything after an agent turn — messages, audit, memory, cost.\n * Called by both SSE chat and channel paths so nothing is ever missed. */\nexport async function persistAgentTurn(\n supabase: SupabaseClient,\n agent: AgentConfig,\n opts: {\n conversationId: string;\n storeId?: string;\n agentId: string;\n agentModel: string;\n traceId: string;\n message: string;\n result: { finalText: string; toolCallCount: number; toolsUsed: string[]; tokens: { input: number; output: number; cacheCreation?: number; cacheRead?: number }; costUsd: number; turnCount?: number; loopDetectorStats?: any; turns?: Array<{ turn: number; inputTokens: number; outputTokens: number; cacheRead: number; cacheCreation: number; toolsUsed: string[]; costUsd: number }>; stopReason?: string };\n source: string;\n chatStartTime: number;\n chatEndTime: number;\n userId?: string;\n userEmail?: string | null;\n senderContext?: SenderContext;\n },\n): Promise<void> {\n const { conversationId, storeId, agentId, agentModel, traceId, message, result, source, chatStartTime, chatEndTime, userId, userEmail, senderContext } = opts;\n\n // Persist user + assistant messages\n try {\n await supabase.from(\"ai_messages\").insert([\n {\n conversation_id: conversationId, role: \"user\",\n content: [{ type: \"text\", text: message }],\n token_count: Math.ceil(message.length / 4),\n },\n {\n conversation_id: conversationId, role: \"assistant\",\n content: [{ type: \"text\", text: result.finalText || \"\" }],\n is_tool_use: result.toolCallCount > 0,\n tool_names: result.toolsUsed?.length ? result.toolsUsed : null,\n token_count: result.tokens.input + result.tokens.output,\n },\n ]);\n } catch (err) {\n log.error({ err: (err as Error).message }, \"message persist failed\");\n }\n\n // Update conversation metadata\n try {\n await supabase.from(\"ai_conversations\").update({\n metadata: {\n agentName: agent.name,\n source,\n model: agentModel,\n lastTurnTokens: result.tokens.input + result.tokens.output,\n lastToolCalls: result.toolCallCount,\n lastDurationMs: chatEndTime - chatStartTime,\n ...(senderContext ? {\n channel_type: senderContext.channelType || null,\n channel_id: senderContext.channelId || null,\n channel_name: senderContext.channelName || null,\n sender_id: senderContext.senderId || null,\n customer_id: senderContext.customerId || null,\n customer_name: senderContext.customerName || null,\n } : {}),\n },\n }).eq(\"id\", conversationId);\n } catch (err) {\n log.error({ err: (err as Error).message }, \"conversation update failed\");\n }\n\n // Telemetry: user message\n try {\n queueSpan(auditRowToSpan({\n action: \"chat.user_message\", severity: \"info\",\n store_id: storeId || null, resource_type: \"chat_message\",\n resource_id: agentId, request_id: traceId,\n conversation_id: conversationId,\n user_id: userId || null, user_email: userEmail || null,\n source, service_name: \"agent-server\", span_kind: \"INTERNAL\",\n status_code: \"OK\",\n start_time: new Date().toISOString(), end_time: new Date().toISOString(),\n duration_ms: 0,\n input_bytes: typeof message === \"string\" ? message.length : 0,\n details: {\n message_preview: message.substring(0, 200),\n agent_id: agentId, model: agentModel,\n conversation_id: conversationId,\n ...(senderContext ? {\n channel_type: senderContext.channelType || null,\n sender_id: senderContext.senderId || null,\n customer_id: senderContext.customerId || null,\n } : {}),\n },\n }));\n } catch (err) {\n log.error({ err: (err as Error).message }, \"audit user_message failed\");\n }\n\n // Telemetry: assistant response\n try {\n queueSpan(auditRowToSpan({\n action: \"chat.assistant_response\", severity: \"info\",\n store_id: storeId || null, resource_type: \"chat_message\",\n resource_id: agentId, request_id: traceId,\n conversation_id: conversationId,\n duration_ms: chatEndTime - chatStartTime,\n user_id: userId || null, user_email: userEmail || null,\n source,\n input_tokens: result.tokens.input, output_tokens: result.tokens.output,\n total_cost: result.costUsd, model: agentModel,\n trace_id: traceId, span_kind: \"INTERNAL\",\n service_name: \"agent-server\", status_code: \"OK\",\n start_time: new Date(chatStartTime).toISOString(),\n end_time: new Date(chatEndTime).toISOString(),\n stop_reason: result.stopReason || undefined,\n turn_number: result.turnCount || 1,\n details: {\n response_preview: (result.finalText || \"\").substring(0, 500),\n agent_id: agentId, model: agentModel,\n \"gen_ai.request.model\": agentModel,\n \"gen_ai.usage.input_tokens\": result.tokens.input,\n \"gen_ai.usage.output_tokens\": result.tokens.output,\n \"gen_ai.usage.cache_creation_tokens\": result.tokens.cacheCreation || 0,\n \"gen_ai.usage.cache_read_tokens\": result.tokens.cacheRead || 0,\n \"gen_ai.usage.cost\": result.costUsd,\n turn_count: result.turnCount || 1,\n tool_calls: result.toolCallCount,\n tool_names: result.toolsUsed,\n conversation_id: conversationId,\n session_cost_usd: result.costUsd,\n cache_creation_tokens: result.tokens.cacheCreation || 0,\n cache_read_tokens: result.tokens.cacheRead || 0,\n cache_hit_rate: result.tokens.input > 0\n ? Math.round((result.tokens.cacheRead || 0) / ((result.tokens.cacheRead || 0) + result.tokens.input) * 10000) / 100\n : 0,\n cache_cost_savings_pct: result.tokens.input > 0 && (result.tokens.cacheRead || 0) > 0\n ? Math.round((result.tokens.cacheRead || 0) * 0.9 / ((result.tokens.cacheRead || 0) + result.tokens.input) * 10000) / 100\n : 0,\n loop_detector_stats: result.loopDetectorStats || null,\n turns: result.turns || [],\n ...(senderContext ? {\n channel_type: senderContext.channelType || null,\n channel_id: senderContext.channelId || null,\n channel_name: senderContext.channelName || null,\n sender_id: senderContext.senderId || null,\n customer_id: senderContext.customerId || null,\n customer_name: senderContext.customerName || null,\n } : {}),\n },\n }));\n } catch (err) {\n log.error({ err: (err as Error).message }, \"audit assistant_response failed\");\n }\n\n // Memory extraction — awaited with retry\n if (storeId && result.finalText && result.finalText.length > 50) {\n try {\n await extractAndStoreMemories(supabase, getAnthropicClient(agent), agentId, storeId, message, result.finalText);\n } catch (err1: any) {\n try {\n await new Promise(r => setTimeout(r, 2000));\n await extractAndStoreMemories(supabase, getAnthropicClient(agent), agentId, storeId, message, result.finalText);\n } catch (err2: any) {\n log.error({ err: err2.message }, \"memory extract failed after retry\");\n queueSpan(auditRowToSpan({\n action: \"memory.extraction_failed\", severity: \"warning\",\n store_id: storeId || null, resource_type: \"agent_memory\",\n resource_id: agentId, conversation_id: conversationId,\n user_id: userId || null, user_email: userEmail || null,\n error_type: classifyErrorType(err2.message) || undefined,\n service_name: \"agent-server\", span_kind: \"INTERNAL\", status_code: \"ERROR\",\n start_time: new Date().toISOString(), end_time: new Date().toISOString(),\n error_message: err2.message,\n details: { error: err2.message, user_message_preview: message.substring(0, 100) },\n }));\n }\n }\n }\n\n // Cost budget tracking — awaited with retry\n if (storeId && result.costUsd > 0) {\n try {\n await updateCostBudgets(supabase, storeId, agentId, result.costUsd);\n invalidateCostCache(storeId, agentId);\n } catch (err1: any) {\n try {\n await new Promise(r => setTimeout(r, 1000));\n await updateCostBudgets(supabase, storeId, agentId, result.costUsd);\n invalidateCostCache(storeId, agentId);\n } catch (err2: any) {\n log.error({ err: err2.message }, \"cost budget update failed after retry\");\n await supabase.from(\"ai_conversations\").update({\n metadata: { budget_sync_failed: true, failed_cost_usd: result.costUsd },\n }).eq(\"id\", conversationId).then(() => {});\n }\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"server-persist.js","names":["createLogger","queueSpan","auditRowToSpan","classifyErrorType","getAnthropicClient","extractAndStoreMemories","updateCostBudgets","invalidateCostCache","log","persistAgentTurn","supabase","agent","opts","conversationId","storeId","agentId","agentModel","traceId","message","result","source","chatStartTime","chatEndTime","userId","userEmail","senderContext","from","insert","conversation_id","role","content","type","text","token_count","Math","ceil","length","finalText","is_tool_use","toolCallCount","tool_names","toolsUsed","tokens","input","output","err","error","data","existingConv","select","eq","maybeSingle","newMeta","agentName","name","model","lastTurnTokens","lastToolCalls","lastDurationMs","channel_type","channelType","channel_id","channelId","channel_name","channelName","sender_id","senderId","customer_id","customerId","customer_name","customerName","mergedMeta","metadata","update","action","severity","store_id","resource_type","resource_id","request_id","user_id","user_email","service_name","span_kind","status_code","start_time","Date","toISOString","end_time","duration_ms","input_bytes","details","message_preview","substring","agent_id","input_tokens","output_tokens","total_cost","costUsd","trace_id","stop_reason","stopReason","undefined","turn_number","turnCount","response_preview","cacheCreation","cacheRead","turn_count","tool_calls","session_cost_usd","cache_creation_tokens","cache_read_tokens","cache_hit_rate","round","cache_cost_savings_pct","loop_detector_stats","loopDetectorStats","turns","err1","Promise","r","setTimeout","err2","error_type","error_message","user_message_preview","existingConv2","mergedBudgetMeta","budget_sync_failed","failed_cost_usd","then"],"sources":["../../src/server/server-persist.ts"],"sourcesContent":["// server/server-persist.ts — Agent turn persistence (messages, telemetry, memory, cost)\n// Extracted from server-agent.ts to keep each module under 300 lines.\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createLogger } from \"./lib/logger.js\";\nimport { queueSpan, auditRowToSpan, classifyErrorType } from \"./lib/clickhouse-buffer.js\";\nimport type { AgentConfig } from \"./tool-router.js\";\nimport type { SenderContext } from \"./handlers/nodes.js\";\nimport { getAnthropicClient } from \"./server-helpers.js\";\nimport { extractAndStoreMemories, updateCostBudgets } from \"./server-agent.js\";\nimport { invalidateCostCache } from \"./server-cost-guard.js\";\n\nconst log = createLogger(\"server-persist\");\n\n// ============================================================================\n// PERSIST AGENT TURN — messages, audit, memory, cost\n// ============================================================================\n\n/** Persist everything after an agent turn — messages, audit, memory, cost.\n * Called by both SSE chat and channel paths so nothing is ever missed. */\nexport async function persistAgentTurn(\n supabase: SupabaseClient,\n agent: AgentConfig,\n opts: {\n conversationId: string;\n storeId?: string;\n agentId: string;\n agentModel: string;\n traceId: string;\n message: string;\n result: { finalText: string; toolCallCount: number; toolsUsed: string[]; tokens: { input: number; output: number; cacheCreation?: number; cacheRead?: number }; costUsd: number; turnCount?: number; loopDetectorStats?: any; turns?: Array<{ turn: number; inputTokens: number; outputTokens: number; cacheRead: number; cacheCreation: number; toolsUsed: string[]; costUsd: number }>; stopReason?: string };\n source: string;\n chatStartTime: number;\n chatEndTime: number;\n userId?: string;\n userEmail?: string | null;\n senderContext?: SenderContext;\n },\n): Promise<void> {\n const { conversationId, storeId, agentId, agentModel, traceId, message, result, source, chatStartTime, chatEndTime, userId, userEmail, senderContext } = opts;\n\n // Persist user + assistant messages\n try {\n await supabase.from(\"ai_messages\").insert([\n {\n conversation_id: conversationId, role: \"user\",\n content: [{ type: \"text\", text: message }],\n token_count: Math.ceil(message.length / 4),\n },\n {\n conversation_id: conversationId, role: \"assistant\",\n content: [{ type: \"text\", text: result.finalText || \"\" }],\n is_tool_use: result.toolCallCount > 0,\n tool_names: result.toolsUsed?.length ? result.toolsUsed : null,\n token_count: result.tokens.input + result.tokens.output,\n },\n ]);\n } catch (err) {\n log.error({ err: (err as Error).message }, \"message persist failed\");\n }\n\n // Update conversation metadata (merge with existing to avoid overwriting prior fields)\n try {\n const { data: existingConv } = await supabase\n .from(\"ai_conversations\")\n .select(\"metadata\")\n .eq(\"id\", conversationId)\n .maybeSingle();\n const newMeta = {\n agentName: agent.name,\n source,\n model: agentModel,\n lastTurnTokens: result.tokens.input + result.tokens.output,\n lastToolCalls: result.toolCallCount,\n lastDurationMs: chatEndTime - chatStartTime,\n ...(senderContext ? {\n channel_type: senderContext.channelType || null,\n channel_id: senderContext.channelId || null,\n channel_name: senderContext.channelName || null,\n sender_id: senderContext.senderId || null,\n customer_id: senderContext.customerId || null,\n customer_name: senderContext.customerName || null,\n } : {}),\n };\n const mergedMeta = { ...((existingConv?.metadata as Record<string, unknown>) || {}), ...newMeta };\n await supabase.from(\"ai_conversations\").update({\n metadata: mergedMeta,\n }).eq(\"id\", conversationId);\n } catch (err) {\n log.error({ err: (err as Error).message }, \"conversation update failed\");\n }\n\n // Telemetry: user message\n try {\n queueSpan(auditRowToSpan({\n action: \"chat.user_message\", severity: \"info\",\n store_id: storeId || null, resource_type: \"chat_message\",\n resource_id: agentId, request_id: traceId,\n conversation_id: conversationId,\n user_id: userId || null, user_email: userEmail || null,\n source, service_name: \"agent-server\", span_kind: \"INTERNAL\",\n status_code: \"OK\",\n start_time: new Date().toISOString(), end_time: new Date().toISOString(),\n duration_ms: 0,\n input_bytes: typeof message === \"string\" ? message.length : 0,\n details: {\n message_preview: message.substring(0, 200),\n agent_id: agentId, model: agentModel,\n conversation_id: conversationId,\n ...(senderContext ? {\n channel_type: senderContext.channelType || null,\n sender_id: senderContext.senderId || null,\n customer_id: senderContext.customerId || null,\n } : {}),\n },\n }));\n } catch (err) {\n log.error({ err: (err as Error).message }, \"audit user_message failed\");\n }\n\n // Telemetry: assistant response\n try {\n queueSpan(auditRowToSpan({\n action: \"chat.assistant_response\", severity: \"info\",\n store_id: storeId || null, resource_type: \"chat_message\",\n resource_id: agentId, request_id: traceId,\n conversation_id: conversationId,\n duration_ms: chatEndTime - chatStartTime,\n user_id: userId || null, user_email: userEmail || null,\n source,\n input_tokens: result.tokens.input, output_tokens: result.tokens.output,\n total_cost: result.costUsd, model: agentModel,\n trace_id: traceId, span_kind: \"INTERNAL\",\n service_name: \"agent-server\", status_code: \"OK\",\n start_time: new Date(chatStartTime).toISOString(),\n end_time: new Date(chatEndTime).toISOString(),\n stop_reason: result.stopReason || undefined,\n turn_number: result.turnCount || 1,\n details: {\n response_preview: (result.finalText || \"\").substring(0, 500),\n agent_id: agentId, model: agentModel,\n \"gen_ai.request.model\": agentModel,\n \"gen_ai.usage.input_tokens\": result.tokens.input,\n \"gen_ai.usage.output_tokens\": result.tokens.output,\n \"gen_ai.usage.cache_creation_tokens\": result.tokens.cacheCreation || 0,\n \"gen_ai.usage.cache_read_tokens\": result.tokens.cacheRead || 0,\n \"gen_ai.usage.cost\": result.costUsd,\n turn_count: result.turnCount || 1,\n tool_calls: result.toolCallCount,\n tool_names: result.toolsUsed,\n conversation_id: conversationId,\n session_cost_usd: result.costUsd,\n cache_creation_tokens: result.tokens.cacheCreation || 0,\n cache_read_tokens: result.tokens.cacheRead || 0,\n cache_hit_rate: result.tokens.input > 0\n ? Math.round((result.tokens.cacheRead || 0) / ((result.tokens.cacheRead || 0) + result.tokens.input) * 10000) / 100\n : 0,\n cache_cost_savings_pct: result.tokens.input > 0 && (result.tokens.cacheRead || 0) > 0\n ? Math.round((result.tokens.cacheRead || 0) * 0.9 / ((result.tokens.cacheRead || 0) + result.tokens.input) * 10000) / 100\n : 0,\n loop_detector_stats: result.loopDetectorStats || null,\n turns: result.turns || [],\n ...(senderContext ? {\n channel_type: senderContext.channelType || null,\n channel_id: senderContext.channelId || null,\n channel_name: senderContext.channelName || null,\n sender_id: senderContext.senderId || null,\n customer_id: senderContext.customerId || null,\n customer_name: senderContext.customerName || null,\n } : {}),\n },\n }));\n } catch (err) {\n log.error({ err: (err as Error).message }, \"audit assistant_response failed\");\n }\n\n // Memory extraction — awaited with retry\n if (storeId && result.finalText && result.finalText.length > 50) {\n try {\n await extractAndStoreMemories(supabase, getAnthropicClient(agent), agentId, storeId, message, result.finalText);\n } catch (err1: any) {\n try {\n await new Promise(r => setTimeout(r, 2000));\n await extractAndStoreMemories(supabase, getAnthropicClient(agent), agentId, storeId, message, result.finalText);\n } catch (err2: any) {\n log.error({ err: err2.message }, \"memory extract failed after retry\");\n queueSpan(auditRowToSpan({\n action: \"memory.extraction_failed\", severity: \"warning\",\n store_id: storeId || null, resource_type: \"agent_memory\",\n resource_id: agentId, conversation_id: conversationId,\n user_id: userId || null, user_email: userEmail || null,\n error_type: classifyErrorType(err2.message) || undefined,\n service_name: \"agent-server\", span_kind: \"INTERNAL\", status_code: \"ERROR\",\n start_time: new Date().toISOString(), end_time: new Date().toISOString(),\n error_message: err2.message,\n details: { error: err2.message, user_message_preview: message.substring(0, 100) },\n }));\n }\n }\n }\n\n // Cost budget tracking — awaited with retry\n if (storeId && result.costUsd > 0) {\n try {\n await updateCostBudgets(supabase, storeId, agentId, result.costUsd);\n invalidateCostCache(storeId, agentId);\n } catch (err1: any) {\n try {\n await new Promise(r => setTimeout(r, 1000));\n await updateCostBudgets(supabase, storeId, agentId, result.costUsd);\n invalidateCostCache(storeId, agentId);\n } catch (err2: any) {\n log.error({ err: err2.message }, \"cost budget update failed after retry\");\n const { data: existingConv2 } = await supabase\n .from(\"ai_conversations\")\n .select(\"metadata\")\n .eq(\"id\", conversationId)\n .maybeSingle();\n const mergedBudgetMeta = {\n ...((existingConv2?.metadata as Record<string, unknown>) || {}),\n budget_sync_failed: true,\n failed_cost_usd: result.costUsd,\n };\n await supabase.from(\"ai_conversations\").update({\n metadata: mergedBudgetMeta,\n }).eq(\"id\", conversationId).then(() => {});\n }\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -40,7 +40,7 @@ setInterval(() => {
|
|
|
40
40
|
agentChatLimiter.delete(key);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
}, 60_000);
|
|
43
|
+
}, 60_000).unref();
|
|
44
44
|
export function checkAgentChatRateLimit(storeId) {
|
|
45
45
|
if (agentChatConcurrent >= AGENT_CHAT_MAX_CONCURRENT) {
|
|
46
46
|
return {
|
|
@@ -64,14 +64,14 @@ export function checkAgentChatRateLimit(storeId) {
|
|
|
64
64
|
error: `Rate limit exceeded: ${AGENT_CHAT_MAX_PER_MIN}/min for agent chat`
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
// Atomically increment concurrent count when allowed — prevents race between check and increment
|
|
69
|
+
agentChatConcurrent++;
|
|
67
70
|
return {
|
|
68
71
|
allowed: true
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
|
-
export function incrementConcurrent() {
|
|
72
|
-
agentChatConcurrent++;
|
|
73
|
-
}
|
|
74
74
|
export function decrementConcurrent() {
|
|
75
|
-
agentChatConcurrent--;
|
|
75
|
+
if (agentChatConcurrent > 0) agentChatConcurrent--;
|
|
76
76
|
}
|
|
77
77
|
//# sourceMappingURL=server-rate-limit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-rate-limit.js","names":["rateLimiter","sendIpRateLimit","res","ip","headers","result","checkRequest","allowed","writeHead","String","Math","ceil","retryAfterMs","end","JSON","stringify","error","agentChatLimiter","Map","AGENT_CHAT_MAX_PER_MIN","AGENT_CHAT_MAX_CONCURRENT","agentChatConcurrent","setInterval","now","Date","key","entry","windowStart","delete","checkAgentChatRateLimit","storeId","get","count","set","
|
|
1
|
+
{"version":3,"file":"server-rate-limit.js","names":["rateLimiter","sendIpRateLimit","res","ip","headers","result","checkRequest","allowed","writeHead","String","Math","ceil","retryAfterMs","end","JSON","stringify","error","agentChatLimiter","Map","AGENT_CHAT_MAX_PER_MIN","AGENT_CHAT_MAX_CONCURRENT","agentChatConcurrent","setInterval","now","Date","key","entry","windowStart","delete","unref","checkAgentChatRateLimit","storeId","get","count","set","decrementConcurrent"],"sources":["../../src/server/server-rate-limit.ts"],"sourcesContent":["// server/server-rate-limit.ts — Rate limiting helpers for the agent server\n// Extracted from index.ts for modularity.\n\nimport http from \"node:http\";\nimport { rateLimiter } from \"./lib/rate-limiter.js\";\n\n// ============================================================================\n// IP RATE LIMITING\n// ============================================================================\n\n/** Check IP rate limit and send 429 with proper headers if exceeded */\nexport function sendIpRateLimit(res: http.ServerResponse, ip: string, headers: Record<string, string>): boolean {\n const result = rateLimiter.checkRequest(`ip:${ip}`, \"unauthenticated\");\n if (result.allowed) return false;\n res.writeHead(429, {\n \"Retry-After\": String(Math.ceil(result.retryAfterMs / 1000) || 1),\n \"X-RateLimit-Remaining\": \"0\",\n \"Content-Type\": \"application/json\",\n ...headers,\n });\n res.end(JSON.stringify({ error: \"Too many requests\" }));\n return true;\n}\n\n// ============================================================================\n// AGENT CHAT RATE LIMITING — per-store + global concurrent cap\n// ============================================================================\n\nconst agentChatLimiter = new Map<string, { count: number; windowStart: number }>();\nconst AGENT_CHAT_MAX_PER_MIN = 60;\nconst AGENT_CHAT_MAX_CONCURRENT = 10;\nlet agentChatConcurrent = 0;\n\n// Periodically clean up stale entries to prevent unbounded Map growth\nsetInterval(() => {\n const now = Date.now();\n for (const [key, entry] of agentChatLimiter) {\n if (now - entry.windowStart > 120_000) {\n agentChatLimiter.delete(key);\n }\n }\n}, 60_000).unref();\n\nexport function checkAgentChatRateLimit(storeId: string): { allowed: boolean; error?: string } {\n if (agentChatConcurrent >= AGENT_CHAT_MAX_CONCURRENT) {\n return { allowed: false, error: \"Too many concurrent agent sessions. Please wait.\" };\n }\n\n const now = Date.now();\n let entry = agentChatLimiter.get(storeId);\n if (!entry || now - entry.windowStart > 60_000) {\n entry = { count: 0, windowStart: now };\n agentChatLimiter.set(storeId, entry);\n }\n entry.count++;\n if (entry.count > AGENT_CHAT_MAX_PER_MIN) {\n return { allowed: false, error: `Rate limit exceeded: ${AGENT_CHAT_MAX_PER_MIN}/min for agent chat` };\n }\n\n // Atomically increment concurrent count when allowed — prevents race between check and increment\n agentChatConcurrent++;\n return { allowed: true };\n}\n\nexport function decrementConcurrent(): void {\n if (agentChatConcurrent > 0) agentChatConcurrent--;\n}\n"],"mappings":"AAAA;AACA;;AAGA,SAASA,WAAW,QAAQ,uBAAuB;;AAEnD;AACA;AACA;;AAEA;AACA,OAAO,SAASC,eAAeA,CAACC,GAAwB,EAAEC,EAAU,EAAEC,OAA+B,EAAW;EAC9G,MAAMC,MAAM,GAAGL,WAAW,CAACM,YAAY,CAAC,MAAMH,EAAE,EAAE,EAAE,iBAAiB,CAAC;EACtE,IAAIE,MAAM,CAACE,OAAO,EAAE,OAAO,KAAK;EAChCL,GAAG,CAACM,SAAS,CAAC,GAAG,EAAE;IACjB,aAAa,EAAEC,MAAM,CAACC,IAAI,CAACC,IAAI,CAACN,MAAM,CAACO,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACjE,uBAAuB,EAAE,GAAG;IAC5B,cAAc,EAAE,kBAAkB;IAClC,GAAGR;EACL,CAAC,CAAC;EACFF,GAAG,CAACW,GAAG,CAACC,IAAI,CAACC,SAAS,CAAC;IAAEC,KAAK,EAAE;EAAoB,CAAC,CAAC,CAAC;EACvD,OAAO,IAAI;AACb;;AAEA;AACA;AACA;;AAEA,MAAMC,gBAAgB,GAAG,IAAIC,GAAG,CAAiD,CAAC;AAClF,MAAMC,sBAAsB,GAAG,EAAE;AACjC,MAAMC,yBAAyB,GAAG,EAAE;AACpC,IAAIC,mBAAmB,GAAG,CAAC;;AAE3B;AACAC,WAAW,CAAC,MAAM;EAChB,MAAMC,GAAG,GAAGC,IAAI,CAACD,GAAG,CAAC,CAAC;EACtB,KAAK,MAAM,CAACE,GAAG,EAAEC,KAAK,CAAC,IAAIT,gBAAgB,EAAE;IAC3C,IAAIM,GAAG,GAAGG,KAAK,CAACC,WAAW,GAAG,OAAO,EAAE;MACrCV,gBAAgB,CAACW,MAAM,CAACH,GAAG,CAAC;IAC9B;EACF;AACF,CAAC,EAAE,MAAM,CAAC,CAACI,KAAK,CAAC,CAAC;AAElB,OAAO,SAASC,uBAAuBA,CAACC,OAAe,EAAwC;EAC7F,IAAIV,mBAAmB,IAAID,yBAAyB,EAAE;IACpD,OAAO;MAAEb,OAAO,EAAE,KAAK;MAAES,KAAK,EAAE;IAAmD,CAAC;EACtF;EAEA,MAAMO,GAAG,GAAGC,IAAI,CAACD,GAAG,CAAC,CAAC;EACtB,IAAIG,KAAK,GAAGT,gBAAgB,CAACe,GAAG,CAACD,OAAO,CAAC;EACzC,IAAI,CAACL,KAAK,IAAIH,GAAG,GAAGG,KAAK,CAACC,WAAW,GAAG,MAAM,EAAE;IAC9CD,KAAK,GAAG;MAAEO,KAAK,EAAE,CAAC;MAAEN,WAAW,EAAEJ;IAAI,CAAC;IACtCN,gBAAgB,CAACiB,GAAG,CAACH,OAAO,EAAEL,KAAK,CAAC;EACtC;EACAA,KAAK,CAACO,KAAK,EAAE;EACb,IAAIP,KAAK,CAACO,KAAK,GAAGd,sBAAsB,EAAE;IACxC,OAAO;MAAEZ,OAAO,EAAE,KAAK;MAAES,KAAK,EAAE,wBAAwBG,sBAAsB;IAAsB,CAAC;EACvG;;EAEA;EACAE,mBAAmB,EAAE;EACrB,OAAO;IAAEd,OAAO,EAAE;EAAK,CAAC;AAC1B;AAEA,OAAO,SAAS4B,mBAAmBA,CAAA,EAAS;EAC1C,IAAId,mBAAmB,GAAG,CAAC,EAAEA,mBAAmB,EAAE;AACpD","ignoreList":[]}
|