whale-code 6.5.10 → 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/commands/doctor.js +1 -6
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/services/agent-loop-tools.js +11 -2
- package/dist/cli/services/agent-loop-tools.js.map +1 -1
- package/dist/cli/services/agent-loop.js +1 -1
- package/dist/cli/services/agent-loop.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.d.ts +8 -10
- package/dist/cli/services/config-store.js +14 -13
- 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/setup/SetupApp.d.ts +2 -2
- package/dist/cli/setup/SetupApp.js +91 -254
- package/dist/cli/setup/SetupApp.js.map +1 -1
- package/dist/cli/shared/SpinnerSlot.js +4 -1
- package/dist/cli/shared/SpinnerSlot.js.map +1 -1
- package/dist/cli/status/StatusApp.js +3 -3
- package/dist/cli/status/StatusApp.js.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.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/setup.js +5 -25
- package/dist/setup.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
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Unified path for streaming and non-streaming API calls.
|
|
4
4
|
|
|
5
|
-
import { isRetryableError, sanitizeError } from "../../shared/agent-core.js";
|
|
5
|
+
import { categorizeError, isRetryableError, sanitizeError } from "../../shared/agent-core.js";
|
|
6
6
|
import { processStreamWithCallbacks } from "../../shared/sse-parser.js";
|
|
7
7
|
import { providerFailover } from "./provider-failover.js";
|
|
8
8
|
import { createLogger } from "./logger.js";
|
|
9
|
-
import { MAX_RETRIES, RETRY_BASE_DELAY_MS } from "./agent-loop-types.js";
|
|
9
|
+
import { MAX_RETRIES, RETRY_BASE_DELAY_MS, RETRY_MAX_DELAY_MS, RETRY_OVERLOADED_MULTIPLIER, MODEL_FALLBACK_AFTER_ATTEMPT, MODEL_FALLBACK_MAP } from "./agent-loop-types.js";
|
|
10
10
|
const log = createLogger("agent-loop");
|
|
11
11
|
|
|
12
12
|
// ============================================================================
|
|
@@ -84,11 +84,41 @@ export async function executeTurn(cfg, callbacks, streaming) {
|
|
|
84
84
|
} catch (err) {
|
|
85
85
|
providerFailover.recordFailure(cfg.activeProvider);
|
|
86
86
|
if (attempt < MAX_RETRIES && isRetryableError(err)) {
|
|
87
|
-
const
|
|
87
|
+
const {
|
|
88
|
+
category
|
|
89
|
+
} = categorizeError(err);
|
|
90
|
+
let delay = Math.min(RETRY_BASE_DELAY_MS * Math.pow(2, attempt), RETRY_MAX_DELAY_MS);
|
|
91
|
+
|
|
92
|
+
// Overloaded/rate-limited errors need much longer backoff
|
|
93
|
+
if (category === "PROVIDER_DOWN" || category === "RATE_LIMIT") {
|
|
94
|
+
delay = Math.min(delay * RETRY_OVERLOADED_MULTIPLIER, RETRY_MAX_DELAY_MS);
|
|
95
|
+
|
|
96
|
+
// Model fallback: after N failed attempts on an overloaded model,
|
|
97
|
+
// downgrade to a lower-tier model (e.g. Opus → Sonnet) for remaining retries.
|
|
98
|
+
// Sonnet has much more capacity and is almost never overloaded.
|
|
99
|
+
if (attempt >= MODEL_FALLBACK_AFTER_ATTEMPT) {
|
|
100
|
+
const fallbackModel = MODEL_FALLBACK_MAP[baseParams.model];
|
|
101
|
+
if (fallbackModel && baseParams.model !== fallbackModel) {
|
|
102
|
+
log.warn({
|
|
103
|
+
from: baseParams.model,
|
|
104
|
+
to: fallbackModel,
|
|
105
|
+
attempt
|
|
106
|
+
}, "model fallback: overloaded, downgrading");
|
|
107
|
+
baseParams.model = fallbackModel;
|
|
108
|
+
// Reset delay — fallback model is likely available immediately
|
|
109
|
+
delay = Math.min(RETRY_BASE_DELAY_MS, delay);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Add jitter (±25%) to prevent thundering herd
|
|
115
|
+
const jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
116
|
+
delay = Math.round(delay + jitter);
|
|
88
117
|
log.warn({
|
|
89
118
|
attempt: attempt + 1,
|
|
90
119
|
maxRetries: MAX_RETRIES,
|
|
91
120
|
delayMs: delay,
|
|
121
|
+
category,
|
|
92
122
|
err: sanitizeError(err)
|
|
93
123
|
}, "retrying API call");
|
|
94
124
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-loop-turn.js","names":["isRetryableError","sanitizeError","processStreamWithCallbacks","providerFailover","createLogger","MAX_RETRIES","RETRY_BASE_DELAY_MS","log","executeTurn","cfg","callbacks","streaming","betas","ctxMgmt","thinkingCfg","beta","push","documents","length","includes","baseParams","model","activeModel","max_tokens","maxTokens","temperature","thinking","type","system","omitTools","tools","finalToolDefs","anthropicToolChoice","tool_choice","messages","finalMessages","context_management","config","attempt","result","stream","anthropic","create","streamResult","text","toolUseBlocks","compactionContent","stopReason","citations","usage","inputTokens","outputTokens","cacheReadTokens","cacheCreationTokens","response","extractNonStreamingResult","recordSuccess","activeProvider","err","recordFailure","delay","Math","pow","warn","maxRetries","delayMs","Promise","resolve","setTimeout","Error","executeStreamingTurn","executeNonStreamingTurn","block","content","onText","id","name","input","onToolStart","citeBlock","citation","cited_text","document_index","start_char_index","end_char_index","document_title","onCitation","stop_reason","input_tokens","output_tokens","cache_read_input_tokens","cache_creation_input_tokens"],"sources":["../../../src/server/lib/agent-loop-turn.ts"],"sourcesContent":["// agent-loop-turn.ts — Per-turn API call + response processing for the server agent loop.\n//\n// Unified path for streaming and non-streaming API calls.\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type {\n BetaTextBlockParam,\n BetaMessageParam,\n BetaToolUnion,\n BetaMessage,\n MessageCreateParamsStreaming,\n MessageCreateParamsNonStreaming,\n} from \"@anthropic-ai/sdk/resources/beta/messages/messages\";\n\nimport { isRetryableError, sanitizeError } from \"../../shared/agent-core.js\";\nimport { processStreamWithCallbacks } from \"../../shared/sse-parser.js\";\nimport type { BetaStreamEvent } from \"../../shared/anthropic-types.js\";\nimport type { CitationBlock } from \"../../shared/types.js\";\nimport { providerFailover } from \"./provider-failover.js\";\nimport { createLogger } from \"./logger.js\";\n\nimport { MAX_RETRIES, RETRY_BASE_DELAY_MS, type TurnMetrics } from \"./agent-loop-types.js\";\n\nconst log = createLogger(\"agent-loop\");\n\n// ============================================================================\n// Shared turn-level types\n// ============================================================================\n\nexport interface TurnConfig {\n anthropic: Anthropic;\n activeModel: string;\n activeProvider: string;\n maxTokens: number;\n temperature: number;\n system: BetaTextBlockParam[];\n finalToolDefs: object[];\n finalMessages: Anthropic.MessageParam[];\n anthropicToolChoice?: Record<string, unknown>;\n omitTools: boolean;\n ctxMgmt: { betas: string[]; config: unknown };\n thinkingCfg: { thinking: Record<string, unknown>; beta?: string };\n documents?: any[];\n}\n\nexport interface TurnResult {\n text: string;\n toolUseBlocks: Array<{ id: string; name: string; input: Record<string, unknown> }>;\n compactionContent: string | null;\n stopReason: string;\n citations: CitationBlock[];\n usage: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n };\n}\n\nexport interface TurnCallbacks {\n onText?: (text: string) => void;\n onToolStart?: (name: string, input?: Record<string, unknown>) => void;\n onCitation?: (citation: CitationBlock) => void;\n}\n\n// ============================================================================\n// UNIFIED TURN EXECUTION — streaming or non-streaming via single path\n// ============================================================================\n\n/**\n * Execute a single API turn with retry, provider failover, and response normalization.\n * Handles both streaming and non-streaming modes through a unified code path.\n */\nexport async function executeTurn(\n cfg: TurnConfig,\n callbacks: TurnCallbacks,\n streaming: boolean,\n): Promise<TurnResult> {\n // Build betas list (shared between both modes)\n const betas = [...cfg.ctxMgmt.betas];\n if (cfg.thinkingCfg.beta) betas.push(cfg.thinkingCfg.beta);\n if (cfg.documents?.length && !betas.includes(\"citations-2025-04-15\")) {\n betas.push(\"citations-2025-04-15\");\n }\n\n // Build shared API params\n const baseParams = {\n model: cfg.activeModel,\n max_tokens: cfg.maxTokens,\n temperature: cfg.thinkingCfg.thinking.type !== \"disabled\" ? 1 : cfg.temperature,\n system: cfg.system,\n ...(cfg.omitTools ? {} : { tools: cfg.finalToolDefs as unknown as BetaToolUnion[] }),\n ...(cfg.anthropicToolChoice && !cfg.omitTools ? { tool_choice: cfg.anthropicToolChoice } : {}),\n messages: cfg.finalMessages as unknown as BetaMessageParam[],\n thinking: cfg.thinkingCfg.thinking,\n betas,\n context_management: cfg.ctxMgmt.config,\n ...(cfg.documents?.length ? { documents: cfg.documents } : {}),\n };\n\n // Retry loop (shared between both modes)\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n let result: TurnResult;\n\n if (streaming) {\n const stream = await cfg.anthropic.beta.messages.create({\n ...baseParams,\n stream: true,\n } as unknown as MessageCreateParamsStreaming);\n\n const streamResult = await processStreamWithCallbacks(\n stream as unknown as AsyncIterable<BetaStreamEvent>,\n callbacks,\n );\n\n result = {\n text: streamResult.text,\n toolUseBlocks: streamResult.toolUseBlocks,\n compactionContent: streamResult.compactionContent,\n stopReason: streamResult.stopReason || \"end_turn\",\n citations: streamResult.citations,\n usage: {\n inputTokens: streamResult.usage.inputTokens,\n outputTokens: streamResult.usage.outputTokens,\n cacheReadTokens: streamResult.usage.cacheReadTokens,\n cacheCreationTokens: streamResult.usage.cacheCreationTokens,\n },\n };\n } else {\n const response = await cfg.anthropic.beta.messages.create({\n ...baseParams,\n } as unknown as MessageCreateParamsNonStreaming) as BetaMessage;\n\n result = extractNonStreamingResult(response, callbacks);\n }\n\n providerFailover.recordSuccess(cfg.activeProvider);\n return result;\n } catch (err) {\n providerFailover.recordFailure(cfg.activeProvider);\n if (attempt < MAX_RETRIES && isRetryableError(err)) {\n const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);\n log.warn({ attempt: attempt + 1, maxRetries: MAX_RETRIES, delayMs: delay, err: sanitizeError(err) }, \"retrying API call\");\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n throw err;\n }\n }\n\n throw new Error(\"Failed to get response after retries\");\n}\n\n// ============================================================================\n// Backward-compatible wrappers (used by existing callers)\n// ============================================================================\n\nexport async function executeStreamingTurn(cfg: TurnConfig, callbacks: TurnCallbacks): Promise<TurnResult> {\n return executeTurn(cfg, callbacks, true);\n}\n\nexport async function executeNonStreamingTurn(cfg: TurnConfig, callbacks: TurnCallbacks): Promise<TurnResult> {\n return executeTurn(cfg, callbacks, false);\n}\n\n// ============================================================================\n// Non-streaming response extraction (private helper)\n// ============================================================================\n\nfunction extractNonStreamingResult(response: BetaMessage, callbacks: TurnCallbacks): TurnResult {\n let text = \"\";\n let compactionContent: string | null = null;\n const toolUseBlocks: Array<{ id: string; name: string; input: Record<string, unknown> }> = [];\n const citations: CitationBlock[] = [];\n\n for (const block of response.content) {\n if (block.type === \"text\") {\n text += block.text;\n callbacks.onText?.(block.text);\n } else if (block.type === \"tool_use\") {\n toolUseBlocks.push({\n id: block.id,\n name: block.name,\n input: block.input as Record<string, unknown>,\n });\n callbacks.onToolStart?.(block.name, block.input as Record<string, unknown>);\n } else if ((block as any).type === \"cite\") {\n const citeBlock = block as any;\n const citation: CitationBlock = {\n type: \"cite\",\n cited_text: citeBlock.cited_text ?? \"\",\n document_index: citeBlock.document_index ?? 0,\n start_char_index: citeBlock.start_char_index ?? 0,\n end_char_index: citeBlock.end_char_index ?? 0,\n ...(citeBlock.document_title ? { document_title: citeBlock.document_title } : {}),\n };\n citations.push(citation);\n callbacks.onCitation?.(citation);\n } else if ((block as any).type === \"compaction\") {\n compactionContent = (block as any).content || \"\";\n }\n }\n\n return {\n text,\n toolUseBlocks,\n compactionContent,\n stopReason: response.stop_reason || \"end_turn\",\n citations,\n usage: {\n inputTokens: response.usage?.input_tokens || 0,\n outputTokens: response.usage?.output_tokens || 0,\n cacheReadTokens: response.usage?.cache_read_input_tokens ?? 0,\n cacheCreationTokens: response.usage?.cache_creation_input_tokens ?? 0,\n },\n };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"agent-loop-turn.js","names":["categorizeError","isRetryableError","sanitizeError","processStreamWithCallbacks","providerFailover","createLogger","MAX_RETRIES","RETRY_BASE_DELAY_MS","RETRY_MAX_DELAY_MS","RETRY_OVERLOADED_MULTIPLIER","MODEL_FALLBACK_AFTER_ATTEMPT","MODEL_FALLBACK_MAP","log","executeTurn","cfg","callbacks","streaming","betas","ctxMgmt","thinkingCfg","beta","push","documents","length","includes","baseParams","model","activeModel","max_tokens","maxTokens","temperature","thinking","type","system","omitTools","tools","finalToolDefs","anthropicToolChoice","tool_choice","messages","finalMessages","context_management","config","attempt","result","stream","anthropic","create","streamResult","text","toolUseBlocks","compactionContent","stopReason","citations","usage","inputTokens","outputTokens","cacheReadTokens","cacheCreationTokens","response","extractNonStreamingResult","recordSuccess","activeProvider","err","recordFailure","category","delay","Math","min","pow","fallbackModel","warn","from","to","jitter","random","round","maxRetries","delayMs","Promise","resolve","setTimeout","Error","executeStreamingTurn","executeNonStreamingTurn","block","content","onText","id","name","input","onToolStart","citeBlock","citation","cited_text","document_index","start_char_index","end_char_index","document_title","onCitation","stop_reason","input_tokens","output_tokens","cache_read_input_tokens","cache_creation_input_tokens"],"sources":["../../../src/server/lib/agent-loop-turn.ts"],"sourcesContent":["// agent-loop-turn.ts — Per-turn API call + response processing for the server agent loop.\n//\n// Unified path for streaming and non-streaming API calls.\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type {\n BetaTextBlockParam,\n BetaMessageParam,\n BetaToolUnion,\n BetaMessage,\n MessageCreateParamsStreaming,\n MessageCreateParamsNonStreaming,\n} from \"@anthropic-ai/sdk/resources/beta/messages/messages\";\n\nimport { categorizeError, isRetryableError, sanitizeError } from \"../../shared/agent-core.js\";\nimport { processStreamWithCallbacks } from \"../../shared/sse-parser.js\";\nimport type { BetaStreamEvent } from \"../../shared/anthropic-types.js\";\nimport type { CitationBlock } from \"../../shared/types.js\";\nimport { providerFailover } from \"./provider-failover.js\";\nimport { createLogger } from \"./logger.js\";\n\nimport { MAX_RETRIES, RETRY_BASE_DELAY_MS, RETRY_MAX_DELAY_MS, RETRY_OVERLOADED_MULTIPLIER, MODEL_FALLBACK_AFTER_ATTEMPT, MODEL_FALLBACK_MAP, type TurnMetrics } from \"./agent-loop-types.js\";\n\nconst log = createLogger(\"agent-loop\");\n\n// ============================================================================\n// Shared turn-level types\n// ============================================================================\n\nexport interface TurnConfig {\n anthropic: Anthropic;\n activeModel: string;\n activeProvider: string;\n maxTokens: number;\n temperature: number;\n system: BetaTextBlockParam[];\n finalToolDefs: object[];\n finalMessages: Anthropic.MessageParam[];\n anthropicToolChoice?: Record<string, unknown>;\n omitTools: boolean;\n ctxMgmt: { betas: string[]; config: unknown };\n thinkingCfg: { thinking: Record<string, unknown>; beta?: string };\n documents?: any[];\n}\n\nexport interface TurnResult {\n text: string;\n toolUseBlocks: Array<{ id: string; name: string; input: Record<string, unknown> }>;\n compactionContent: string | null;\n stopReason: string;\n citations: CitationBlock[];\n usage: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n };\n}\n\nexport interface TurnCallbacks {\n onText?: (text: string) => void;\n onToolStart?: (name: string, input?: Record<string, unknown>) => void;\n onCitation?: (citation: CitationBlock) => void;\n}\n\n// ============================================================================\n// UNIFIED TURN EXECUTION — streaming or non-streaming via single path\n// ============================================================================\n\n/**\n * Execute a single API turn with retry, provider failover, and response normalization.\n * Handles both streaming and non-streaming modes through a unified code path.\n */\nexport async function executeTurn(\n cfg: TurnConfig,\n callbacks: TurnCallbacks,\n streaming: boolean,\n): Promise<TurnResult> {\n // Build betas list (shared between both modes)\n const betas = [...cfg.ctxMgmt.betas];\n if (cfg.thinkingCfg.beta) betas.push(cfg.thinkingCfg.beta);\n if (cfg.documents?.length && !betas.includes(\"citations-2025-04-15\")) {\n betas.push(\"citations-2025-04-15\");\n }\n\n // Build shared API params\n const baseParams = {\n model: cfg.activeModel,\n max_tokens: cfg.maxTokens,\n temperature: cfg.thinkingCfg.thinking.type !== \"disabled\" ? 1 : cfg.temperature,\n system: cfg.system,\n ...(cfg.omitTools ? {} : { tools: cfg.finalToolDefs as unknown as BetaToolUnion[] }),\n ...(cfg.anthropicToolChoice && !cfg.omitTools ? { tool_choice: cfg.anthropicToolChoice } : {}),\n messages: cfg.finalMessages as unknown as BetaMessageParam[],\n thinking: cfg.thinkingCfg.thinking,\n betas,\n context_management: cfg.ctxMgmt.config,\n ...(cfg.documents?.length ? { documents: cfg.documents } : {}),\n };\n\n // Retry loop (shared between both modes)\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n let result: TurnResult;\n\n if (streaming) {\n const stream = await cfg.anthropic.beta.messages.create({\n ...baseParams,\n stream: true,\n } as unknown as MessageCreateParamsStreaming);\n\n const streamResult = await processStreamWithCallbacks(\n stream as unknown as AsyncIterable<BetaStreamEvent>,\n callbacks,\n );\n\n result = {\n text: streamResult.text,\n toolUseBlocks: streamResult.toolUseBlocks,\n compactionContent: streamResult.compactionContent,\n stopReason: streamResult.stopReason || \"end_turn\",\n citations: streamResult.citations,\n usage: {\n inputTokens: streamResult.usage.inputTokens,\n outputTokens: streamResult.usage.outputTokens,\n cacheReadTokens: streamResult.usage.cacheReadTokens,\n cacheCreationTokens: streamResult.usage.cacheCreationTokens,\n },\n };\n } else {\n const response = await cfg.anthropic.beta.messages.create({\n ...baseParams,\n } as unknown as MessageCreateParamsNonStreaming) as BetaMessage;\n\n result = extractNonStreamingResult(response, callbacks);\n }\n\n providerFailover.recordSuccess(cfg.activeProvider);\n return result;\n } catch (err) {\n providerFailover.recordFailure(cfg.activeProvider);\n if (attempt < MAX_RETRIES && isRetryableError(err)) {\n const { category } = categorizeError(err);\n let delay = Math.min(RETRY_BASE_DELAY_MS * Math.pow(2, attempt), RETRY_MAX_DELAY_MS);\n\n // Overloaded/rate-limited errors need much longer backoff\n if (category === \"PROVIDER_DOWN\" || category === \"RATE_LIMIT\") {\n delay = Math.min(delay * RETRY_OVERLOADED_MULTIPLIER, RETRY_MAX_DELAY_MS);\n\n // Model fallback: after N failed attempts on an overloaded model,\n // downgrade to a lower-tier model (e.g. Opus → Sonnet) for remaining retries.\n // Sonnet has much more capacity and is almost never overloaded.\n if (attempt >= MODEL_FALLBACK_AFTER_ATTEMPT) {\n const fallbackModel = MODEL_FALLBACK_MAP[baseParams.model];\n if (fallbackModel && baseParams.model !== fallbackModel) {\n log.warn({ from: baseParams.model, to: fallbackModel, attempt }, \"model fallback: overloaded, downgrading\");\n baseParams.model = fallbackModel;\n // Reset delay — fallback model is likely available immediately\n delay = Math.min(RETRY_BASE_DELAY_MS, delay);\n }\n }\n }\n\n // Add jitter (±25%) to prevent thundering herd\n const jitter = delay * 0.25 * (Math.random() * 2 - 1);\n delay = Math.round(delay + jitter);\n\n log.warn({ attempt: attempt + 1, maxRetries: MAX_RETRIES, delayMs: delay, category, err: sanitizeError(err) }, \"retrying API call\");\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n throw err;\n }\n }\n\n throw new Error(\"Failed to get response after retries\");\n}\n\n// ============================================================================\n// Backward-compatible wrappers (used by existing callers)\n// ============================================================================\n\nexport async function executeStreamingTurn(cfg: TurnConfig, callbacks: TurnCallbacks): Promise<TurnResult> {\n return executeTurn(cfg, callbacks, true);\n}\n\nexport async function executeNonStreamingTurn(cfg: TurnConfig, callbacks: TurnCallbacks): Promise<TurnResult> {\n return executeTurn(cfg, callbacks, false);\n}\n\n// ============================================================================\n// Non-streaming response extraction (private helper)\n// ============================================================================\n\nfunction extractNonStreamingResult(response: BetaMessage, callbacks: TurnCallbacks): TurnResult {\n let text = \"\";\n let compactionContent: string | null = null;\n const toolUseBlocks: Array<{ id: string; name: string; input: Record<string, unknown> }> = [];\n const citations: CitationBlock[] = [];\n\n for (const block of response.content) {\n if (block.type === \"text\") {\n text += block.text;\n callbacks.onText?.(block.text);\n } else if (block.type === \"tool_use\") {\n toolUseBlocks.push({\n id: block.id,\n name: block.name,\n input: block.input as Record<string, unknown>,\n });\n callbacks.onToolStart?.(block.name, block.input as Record<string, unknown>);\n } else if ((block as any).type === \"cite\") {\n const citeBlock = block as any;\n const citation: CitationBlock = {\n type: \"cite\",\n cited_text: citeBlock.cited_text ?? \"\",\n document_index: citeBlock.document_index ?? 0,\n start_char_index: citeBlock.start_char_index ?? 0,\n end_char_index: citeBlock.end_char_index ?? 0,\n ...(citeBlock.document_title ? { document_title: citeBlock.document_title } : {}),\n };\n citations.push(citation);\n callbacks.onCitation?.(citation);\n } else if ((block as any).type === \"compaction\") {\n compactionContent = (block as any).content || \"\";\n }\n }\n\n return {\n text,\n toolUseBlocks,\n compactionContent,\n stopReason: response.stop_reason || \"end_turn\",\n citations,\n usage: {\n inputTokens: response.usage?.input_tokens || 0,\n outputTokens: response.usage?.output_tokens || 0,\n cacheReadTokens: response.usage?.cache_read_input_tokens ?? 0,\n cacheCreationTokens: response.usage?.cache_creation_input_tokens ?? 0,\n },\n };\n}\n"],"mappings":"AAAA;AACA;AACA;;AAYA,SAASA,eAAe,EAAEC,gBAAgB,EAAEC,aAAa,QAAQ,4BAA4B;AAC7F,SAASC,0BAA0B,QAAQ,4BAA4B;AAGvE,SAASC,gBAAgB,QAAQ,wBAAwB;AACzD,SAASC,YAAY,QAAQ,aAAa;AAE1C,SAASC,WAAW,EAAEC,mBAAmB,EAAEC,kBAAkB,EAAEC,2BAA2B,EAAEC,4BAA4B,EAAEC,kBAAkB,QAA0B,uBAAuB;AAE7L,MAAMC,GAAG,GAAGP,YAAY,CAAC,YAAY,CAAC;;AAEtC;AACA;AACA;;AAsCA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,OAAO,eAAeQ,WAAWA,CAC/BC,GAAe,EACfC,SAAwB,EACxBC,SAAkB,EACG;EACrB;EACA,MAAMC,KAAK,GAAG,CAAC,GAAGH,GAAG,CAACI,OAAO,CAACD,KAAK,CAAC;EACpC,IAAIH,GAAG,CAACK,WAAW,CAACC,IAAI,EAAEH,KAAK,CAACI,IAAI,CAACP,GAAG,CAACK,WAAW,CAACC,IAAI,CAAC;EAC1D,IAAIN,GAAG,CAACQ,SAAS,EAAEC,MAAM,IAAI,CAACN,KAAK,CAACO,QAAQ,CAAC,sBAAsB,CAAC,EAAE;IACpEP,KAAK,CAACI,IAAI,CAAC,sBAAsB,CAAC;EACpC;;EAEA;EACA,MAAMI,UAAU,GAAG;IACjBC,KAAK,EAAEZ,GAAG,CAACa,WAAW;IACtBC,UAAU,EAAEd,GAAG,CAACe,SAAS;IACzBC,WAAW,EAAEhB,GAAG,CAACK,WAAW,CAACY,QAAQ,CAACC,IAAI,KAAK,UAAU,GAAG,CAAC,GAAGlB,GAAG,CAACgB,WAAW;IAC/EG,MAAM,EAAEnB,GAAG,CAACmB,MAAM;IAClB,IAAInB,GAAG,CAACoB,SAAS,GAAG,CAAC,CAAC,GAAG;MAAEC,KAAK,EAAErB,GAAG,CAACsB;IAA4C,CAAC,CAAC;IACpF,IAAItB,GAAG,CAACuB,mBAAmB,IAAI,CAACvB,GAAG,CAACoB,SAAS,GAAG;MAAEI,WAAW,EAAExB,GAAG,CAACuB;IAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9FE,QAAQ,EAAEzB,GAAG,CAAC0B,aAA8C;IAC5DT,QAAQ,EAAEjB,GAAG,CAACK,WAAW,CAACY,QAAQ;IAClCd,KAAK;IACLwB,kBAAkB,EAAE3B,GAAG,CAACI,OAAO,CAACwB,MAAM;IACtC,IAAI5B,GAAG,CAACQ,SAAS,EAAEC,MAAM,GAAG;MAAED,SAAS,EAAER,GAAG,CAACQ;IAAU,CAAC,GAAG,CAAC,CAAC;EAC/D,CAAC;;EAED;EACA,KAAK,IAAIqB,OAAO,GAAG,CAAC,EAAEA,OAAO,IAAIrC,WAAW,EAAEqC,OAAO,EAAE,EAAE;IACvD,IAAI;MACF,IAAIC,MAAkB;MAEtB,IAAI5B,SAAS,EAAE;QACb,MAAM6B,MAAM,GAAG,MAAM/B,GAAG,CAACgC,SAAS,CAAC1B,IAAI,CAACmB,QAAQ,CAACQ,MAAM,CAAC;UACtD,GAAGtB,UAAU;UACboB,MAAM,EAAE;QACV,CAA4C,CAAC;QAE7C,MAAMG,YAAY,GAAG,MAAM7C,0BAA0B,CACnD0C,MAAM,EACN9B,SACF,CAAC;QAED6B,MAAM,GAAG;UACPK,IAAI,EAAED,YAAY,CAACC,IAAI;UACvBC,aAAa,EAAEF,YAAY,CAACE,aAAa;UACzCC,iBAAiB,EAAEH,YAAY,CAACG,iBAAiB;UACjDC,UAAU,EAAEJ,YAAY,CAACI,UAAU,IAAI,UAAU;UACjDC,SAAS,EAAEL,YAAY,CAACK,SAAS;UACjCC,KAAK,EAAE;YACLC,WAAW,EAAEP,YAAY,CAACM,KAAK,CAACC,WAAW;YAC3CC,YAAY,EAAER,YAAY,CAACM,KAAK,CAACE,YAAY;YAC7CC,eAAe,EAAET,YAAY,CAACM,KAAK,CAACG,eAAe;YACnDC,mBAAmB,EAAEV,YAAY,CAACM,KAAK,CAACI;UAC1C;QACF,CAAC;MACH,CAAC,MAAM;QACL,MAAMC,QAAQ,GAAG,MAAM7C,GAAG,CAACgC,SAAS,CAAC1B,IAAI,CAACmB,QAAQ,CAACQ,MAAM,CAAC;UACxD,GAAGtB;QACL,CAA+C,CAAgB;QAE/DmB,MAAM,GAAGgB,yBAAyB,CAACD,QAAQ,EAAE5C,SAAS,CAAC;MACzD;MAEAX,gBAAgB,CAACyD,aAAa,CAAC/C,GAAG,CAACgD,cAAc,CAAC;MAClD,OAAOlB,MAAM;IACf,CAAC,CAAC,OAAOmB,GAAG,EAAE;MACZ3D,gBAAgB,CAAC4D,aAAa,CAAClD,GAAG,CAACgD,cAAc,CAAC;MAClD,IAAInB,OAAO,GAAGrC,WAAW,IAAIL,gBAAgB,CAAC8D,GAAG,CAAC,EAAE;QAClD,MAAM;UAAEE;QAAS,CAAC,GAAGjE,eAAe,CAAC+D,GAAG,CAAC;QACzC,IAAIG,KAAK,GAAGC,IAAI,CAACC,GAAG,CAAC7D,mBAAmB,GAAG4D,IAAI,CAACE,GAAG,CAAC,CAAC,EAAE1B,OAAO,CAAC,EAAEnC,kBAAkB,CAAC;;QAEpF;QACA,IAAIyD,QAAQ,KAAK,eAAe,IAAIA,QAAQ,KAAK,YAAY,EAAE;UAC7DC,KAAK,GAAGC,IAAI,CAACC,GAAG,CAACF,KAAK,GAAGzD,2BAA2B,EAAED,kBAAkB,CAAC;;UAEzE;UACA;UACA;UACA,IAAImC,OAAO,IAAIjC,4BAA4B,EAAE;YAC3C,MAAM4D,aAAa,GAAG3D,kBAAkB,CAACc,UAAU,CAACC,KAAK,CAAC;YAC1D,IAAI4C,aAAa,IAAI7C,UAAU,CAACC,KAAK,KAAK4C,aAAa,EAAE;cACvD1D,GAAG,CAAC2D,IAAI,CAAC;gBAAEC,IAAI,EAAE/C,UAAU,CAACC,KAAK;gBAAE+C,EAAE,EAAEH,aAAa;gBAAE3B;cAAQ,CAAC,EAAE,yCAAyC,CAAC;cAC3GlB,UAAU,CAACC,KAAK,GAAG4C,aAAa;cAChC;cACAJ,KAAK,GAAGC,IAAI,CAACC,GAAG,CAAC7D,mBAAmB,EAAE2D,KAAK,CAAC;YAC9C;UACF;QACF;;QAEA;QACA,MAAMQ,MAAM,GAAGR,KAAK,GAAG,IAAI,IAAIC,IAAI,CAACQ,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrDT,KAAK,GAAGC,IAAI,CAACS,KAAK,CAACV,KAAK,GAAGQ,MAAM,CAAC;QAElC9D,GAAG,CAAC2D,IAAI,CAAC;UAAE5B,OAAO,EAAEA,OAAO,GAAG,CAAC;UAAEkC,UAAU,EAAEvE,WAAW;UAAEwE,OAAO,EAAEZ,KAAK;UAAED,QAAQ;UAAEF,GAAG,EAAE7D,aAAa,CAAC6D,GAAG;QAAE,CAAC,EAAE,mBAAmB,CAAC;QACnI,MAAM,IAAIgB,OAAO,CAAEC,OAAO,IAAKC,UAAU,CAACD,OAAO,EAAEd,KAAK,CAAC,CAAC;QAC1D;MACF;MACA,MAAMH,GAAG;IACX;EACF;EAEA,MAAM,IAAImB,KAAK,CAAC,sCAAsC,CAAC;AACzD;;AAEA;AACA;AACA;;AAEA,OAAO,eAAeC,oBAAoBA,CAACrE,GAAe,EAAEC,SAAwB,EAAuB;EACzG,OAAOF,WAAW,CAACC,GAAG,EAAEC,SAAS,EAAE,IAAI,CAAC;AAC1C;AAEA,OAAO,eAAeqE,uBAAuBA,CAACtE,GAAe,EAAEC,SAAwB,EAAuB;EAC5G,OAAOF,WAAW,CAACC,GAAG,EAAEC,SAAS,EAAE,KAAK,CAAC;AAC3C;;AAEA;AACA;AACA;;AAEA,SAAS6C,yBAAyBA,CAACD,QAAqB,EAAE5C,SAAwB,EAAc;EAC9F,IAAIkC,IAAI,GAAG,EAAE;EACb,IAAIE,iBAAgC,GAAG,IAAI;EAC3C,MAAMD,aAAkF,GAAG,EAAE;EAC7F,MAAMG,SAA0B,GAAG,EAAE;EAErC,KAAK,MAAMgC,KAAK,IAAI1B,QAAQ,CAAC2B,OAAO,EAAE;IACpC,IAAID,KAAK,CAACrD,IAAI,KAAK,MAAM,EAAE;MACzBiB,IAAI,IAAIoC,KAAK,CAACpC,IAAI;MAClBlC,SAAS,CAACwE,MAAM,GAAGF,KAAK,CAACpC,IAAI,CAAC;IAChC,CAAC,MAAM,IAAIoC,KAAK,CAACrD,IAAI,KAAK,UAAU,EAAE;MACpCkB,aAAa,CAAC7B,IAAI,CAAC;QACjBmE,EAAE,EAAEH,KAAK,CAACG,EAAE;QACZC,IAAI,EAAEJ,KAAK,CAACI,IAAI;QAChBC,KAAK,EAAEL,KAAK,CAACK;MACf,CAAC,CAAC;MACF3E,SAAS,CAAC4E,WAAW,GAAGN,KAAK,CAACI,IAAI,EAAEJ,KAAK,CAACK,KAAgC,CAAC;IAC7E,CAAC,MAAM,IAAKL,KAAK,CAASrD,IAAI,KAAK,MAAM,EAAE;MACzC,MAAM4D,SAAS,GAAGP,KAAY;MAC9B,MAAMQ,QAAuB,GAAG;QAC9B7D,IAAI,EAAE,MAAM;QACZ8D,UAAU,EAAEF,SAAS,CAACE,UAAU,IAAI,EAAE;QACtCC,cAAc,EAAEH,SAAS,CAACG,cAAc,IAAI,CAAC;QAC7CC,gBAAgB,EAAEJ,SAAS,CAACI,gBAAgB,IAAI,CAAC;QACjDC,cAAc,EAAEL,SAAS,CAACK,cAAc,IAAI,CAAC;QAC7C,IAAIL,SAAS,CAACM,cAAc,GAAG;UAAEA,cAAc,EAAEN,SAAS,CAACM;QAAe,CAAC,GAAG,CAAC,CAAC;MAClF,CAAC;MACD7C,SAAS,CAAChC,IAAI,CAACwE,QAAQ,CAAC;MACxB9E,SAAS,CAACoF,UAAU,GAAGN,QAAQ,CAAC;IAClC,CAAC,MAAM,IAAKR,KAAK,CAASrD,IAAI,KAAK,YAAY,EAAE;MAC/CmB,iBAAiB,GAAIkC,KAAK,CAASC,OAAO,IAAI,EAAE;IAClD;EACF;EAEA,OAAO;IACLrC,IAAI;IACJC,aAAa;IACbC,iBAAiB;IACjBC,UAAU,EAAEO,QAAQ,CAACyC,WAAW,IAAI,UAAU;IAC9C/C,SAAS;IACTC,KAAK,EAAE;MACLC,WAAW,EAAEI,QAAQ,CAACL,KAAK,EAAE+C,YAAY,IAAI,CAAC;MAC9C7C,YAAY,EAAEG,QAAQ,CAACL,KAAK,EAAEgD,aAAa,IAAI,CAAC;MAChD7C,eAAe,EAAEE,QAAQ,CAACL,KAAK,EAAEiD,uBAAuB,IAAI,CAAC;MAC7D7C,mBAAmB,EAAEC,QAAQ,CAACL,KAAK,EAAEkD,2BAA2B,IAAI;IACtE;EACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -105,9 +105,13 @@ export interface ServerAgentLoopResult {
|
|
|
105
105
|
}
|
|
106
106
|
export type { SubagentProgressCallback, SubagentProgressEvent };
|
|
107
107
|
export type { ToolChoice };
|
|
108
|
-
export declare const MAX_RETRIES =
|
|
109
|
-
export declare const RETRY_BASE_DELAY_MS =
|
|
108
|
+
export declare const MAX_RETRIES = 5;
|
|
109
|
+
export declare const RETRY_BASE_DELAY_MS = 2000;
|
|
110
|
+
export declare const RETRY_MAX_DELAY_MS = 60000;
|
|
111
|
+
export declare const RETRY_OVERLOADED_MULTIPLIER = 3;
|
|
112
|
+
export declare const MODEL_FALLBACK_AFTER_ATTEMPT = 2;
|
|
110
113
|
export declare const DEFAULT_MAX_CONCURRENT_TOOLS = 7;
|
|
114
|
+
export declare const MODEL_FALLBACK_MAP: Record<string, string>;
|
|
111
115
|
/**
|
|
112
116
|
* Map ToolChoice to Anthropic API `tool_choice` format.
|
|
113
117
|
* Returns undefined if tools should be omitted entirely.
|
|
@@ -10,10 +10,22 @@
|
|
|
10
10
|
// CONSTANTS
|
|
11
11
|
// ============================================================================
|
|
12
12
|
|
|
13
|
-
export const MAX_RETRIES =
|
|
14
|
-
export const RETRY_BASE_DELAY_MS =
|
|
13
|
+
export const MAX_RETRIES = 5;
|
|
14
|
+
export const RETRY_BASE_DELAY_MS = 2000;
|
|
15
|
+
export const RETRY_MAX_DELAY_MS = 60000;
|
|
16
|
+
export const RETRY_OVERLOADED_MULTIPLIER = 3;
|
|
17
|
+
export const MODEL_FALLBACK_AFTER_ATTEMPT = 2;
|
|
15
18
|
export const DEFAULT_MAX_CONCURRENT_TOOLS = 7;
|
|
16
19
|
|
|
20
|
+
// Intra-provider model fallback — when a model is overloaded, try a lower-tier model
|
|
21
|
+
// from the same provider instead of retrying the same overloaded endpoint.
|
|
22
|
+
export const MODEL_FALLBACK_MAP = {
|
|
23
|
+
"claude-opus-4-6": "claude-sonnet-4-6",
|
|
24
|
+
"claude-opus-4-5-20251101": "claude-sonnet-4-5-20250929",
|
|
25
|
+
"claude-opus-4-20250514": "claude-sonnet-4-20250514",
|
|
26
|
+
"claude-opus-4-1-20250805": "claude-sonnet-4-5-20250929"
|
|
27
|
+
};
|
|
28
|
+
|
|
17
29
|
// ============================================================================
|
|
18
30
|
// TOOL CHOICE MAPPING — convert ToolChoice to provider-specific format
|
|
19
31
|
// ============================================================================
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-loop-types.js","names":["MAX_RETRIES","RETRY_BASE_DELAY_MS","DEFAULT_MAX_CONCURRENT_TOOLS","mapToolChoiceForAnthropic","tc","toolChoice","type","omitTools","name"],"sources":["../../../src/server/lib/agent-loop-types.ts"],"sourcesContent":["// agent-loop-types.ts — Shared types, constants, and helpers for the server agent loop.\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type {\n ContextManagementOverrides,\n LoopDetectorConfig,\n ToolChoice,\n} from \"../../shared/agent-core.js\";\nimport type { CitationBlock } from \"../../shared/types.js\";\nimport type { ToolProgressCallback } from \"../tool-router.js\";\nimport type {\n SubagentProgressCallback,\n SubagentProgressEvent,\n} from \"./server-subagent.js\";\nimport type { RequestCapabilityRequirements } from \"./provider-capabilities.js\";\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface ToolDef {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\nexport type ToolResult = { success: boolean; data?: unknown; error?: string };\n\nexport interface ServerAgentLoopOptions {\n // Core\n anthropic: Anthropic;\n supabase: SupabaseClient;\n model: string;\n systemPrompt: string;\n messages: Anthropic.MessageParam[];\n tools: ToolDef[];\n\n // Behavior\n maxTurns: number;\n temperature: number;\n maxTokens?: number;\n /** @deprecated — Anthropic context_management handles limits. Ignored. */\n maxToolResultChars?: number;\n\n // Context (audit + subagent)\n storeId?: string;\n traceId?: string;\n userId?: string | null;\n userEmail?: string | null;\n source?: string;\n conversationId?: string;\n agentId?: string;\n\n // Tool execution — injected, avoids circular deps\n executeTool: (\n toolName: string,\n args: Record<string, unknown>,\n sourceOverride?: string,\n onToolProgress?: ToolProgressCallback,\n ) => Promise<ToolResult>;\n\n // Tool progress streaming (e.g. kali exec stdout/stderr)\n onToolProgress?: ToolProgressCallback;\n\n // Lazy tool loading — extended tools available via discover_tools\n extendedTools?: ToolDef[];\n\n // Tool choice control\n toolChoice?: ToolChoice; // Default: \"auto\" (smart resolution per turn)\n\n // Feature flags\n enableDelegation?: boolean; // Default: true (auto-inject for Opus)\n enablePromptCaching?: boolean; // Default: true\n enableStreaming?: boolean; // Default: true\n enableModelRouting?: boolean; // Default: true (controls routeModel on turn 1)\n maxConcurrentTools?: number; // Default: 7\n\n // Citations — source documents for Anthropic Citations API (opt-in)\n documents?: any[];\n\n // Callbacks (all optional)\n onText?: (text: string) => void;\n onToolStart?: (name: string, input?: Record<string, unknown>) => void;\n onToolResult?: (name: string, success: boolean, result: unknown) => void;\n onCitation?: (citation: CitationBlock) => void;\n onSubagentProgress?: SubagentProgressCallback;\n\n // Subagent overrides (from parent's context_config)\n subagentMaxTokens?: number; // Default: 8192\n subagentMaxTurns?: number; // Default: 6\n subagentTemperature?: number; // Default: 0.3\n\n // Budget control\n maxCostUsd?: number; // Default: DEFAULT_SESSION_COST_BUDGET_USD ($5)\n\n // Abort control\n clientDisconnected?: { value: boolean };\n startedAt?: number;\n maxDurationMs?: number;\n\n // Capability-aware routing — when set, failover skips providers that lack these capabilities\n requiredCapabilities?: RequestCapabilityRequirements;\n\n // Context management overrides from ai_agent_config.context_config JSONB\n contextOverrides?: ContextManagementOverrides;\n\n // Loop detector thresholds from ai_agent_config.context_config JSONB\n loopDetectorConfig?: Partial<LoopDetectorConfig>;\n /** Max errors from a single parallel batch before aborting remaining calls */\n batchErrorLimit?: number;\n}\n\nexport interface TurnMetrics {\n turn: number;\n inputTokens: number;\n outputTokens: number;\n cacheRead: number;\n cacheCreation: number;\n toolsUsed: string[];\n costUsd: number;\n /** Set when a failover occurred this turn */\n failover?: {\n originalProvider: string;\n activeProvider: string;\n model: string;\n };\n}\n\nexport interface ServerAgentLoopResult {\n finalText: string;\n allTextResponses: string[];\n turnCount: number;\n toolCallCount: number;\n toolsUsed: string[];\n /** Citations collected from all turns (only populated when documents are provided) */\n citations: CitationBlock[];\n tokens: {\n input: number;\n output: number;\n cacheCreation: number;\n cacheRead: number;\n };\n costUsd: number;\n loopDetectorStats: {\n totalErrors: number;\n failedStrategies: number;\n consecutiveFailedTurns: number;\n };\n turns: TurnMetrics[];\n /** Final stop reason from the last API response (end_turn, tool_use, max_tokens) */\n stopReason: string;\n}\n\n// Re-export for consumers\nexport type { SubagentProgressCallback, SubagentProgressEvent };\nexport type { ToolChoice };\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nexport const MAX_RETRIES =
|
|
1
|
+
{"version":3,"file":"agent-loop-types.js","names":["MAX_RETRIES","RETRY_BASE_DELAY_MS","RETRY_MAX_DELAY_MS","RETRY_OVERLOADED_MULTIPLIER","MODEL_FALLBACK_AFTER_ATTEMPT","DEFAULT_MAX_CONCURRENT_TOOLS","MODEL_FALLBACK_MAP","mapToolChoiceForAnthropic","tc","toolChoice","type","omitTools","name"],"sources":["../../../src/server/lib/agent-loop-types.ts"],"sourcesContent":["// agent-loop-types.ts — Shared types, constants, and helpers for the server agent loop.\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type {\n ContextManagementOverrides,\n LoopDetectorConfig,\n ToolChoice,\n} from \"../../shared/agent-core.js\";\nimport type { CitationBlock } from \"../../shared/types.js\";\nimport type { ToolProgressCallback } from \"../tool-router.js\";\nimport type {\n SubagentProgressCallback,\n SubagentProgressEvent,\n} from \"./server-subagent.js\";\nimport type { RequestCapabilityRequirements } from \"./provider-capabilities.js\";\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface ToolDef {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\nexport type ToolResult = { success: boolean; data?: unknown; error?: string };\n\nexport interface ServerAgentLoopOptions {\n // Core\n anthropic: Anthropic;\n supabase: SupabaseClient;\n model: string;\n systemPrompt: string;\n messages: Anthropic.MessageParam[];\n tools: ToolDef[];\n\n // Behavior\n maxTurns: number;\n temperature: number;\n maxTokens?: number;\n /** @deprecated — Anthropic context_management handles limits. Ignored. */\n maxToolResultChars?: number;\n\n // Context (audit + subagent)\n storeId?: string;\n traceId?: string;\n userId?: string | null;\n userEmail?: string | null;\n source?: string;\n conversationId?: string;\n agentId?: string;\n\n // Tool execution — injected, avoids circular deps\n executeTool: (\n toolName: string,\n args: Record<string, unknown>,\n sourceOverride?: string,\n onToolProgress?: ToolProgressCallback,\n ) => Promise<ToolResult>;\n\n // Tool progress streaming (e.g. kali exec stdout/stderr)\n onToolProgress?: ToolProgressCallback;\n\n // Lazy tool loading — extended tools available via discover_tools\n extendedTools?: ToolDef[];\n\n // Tool choice control\n toolChoice?: ToolChoice; // Default: \"auto\" (smart resolution per turn)\n\n // Feature flags\n enableDelegation?: boolean; // Default: true (auto-inject for Opus)\n enablePromptCaching?: boolean; // Default: true\n enableStreaming?: boolean; // Default: true\n enableModelRouting?: boolean; // Default: true (controls routeModel on turn 1)\n maxConcurrentTools?: number; // Default: 7\n\n // Citations — source documents for Anthropic Citations API (opt-in)\n documents?: any[];\n\n // Callbacks (all optional)\n onText?: (text: string) => void;\n onToolStart?: (name: string, input?: Record<string, unknown>) => void;\n onToolResult?: (name: string, success: boolean, result: unknown) => void;\n onCitation?: (citation: CitationBlock) => void;\n onSubagentProgress?: SubagentProgressCallback;\n\n // Subagent overrides (from parent's context_config)\n subagentMaxTokens?: number; // Default: 8192\n subagentMaxTurns?: number; // Default: 6\n subagentTemperature?: number; // Default: 0.3\n\n // Budget control\n maxCostUsd?: number; // Default: DEFAULT_SESSION_COST_BUDGET_USD ($5)\n\n // Abort control\n clientDisconnected?: { value: boolean };\n startedAt?: number;\n maxDurationMs?: number;\n\n // Capability-aware routing — when set, failover skips providers that lack these capabilities\n requiredCapabilities?: RequestCapabilityRequirements;\n\n // Context management overrides from ai_agent_config.context_config JSONB\n contextOverrides?: ContextManagementOverrides;\n\n // Loop detector thresholds from ai_agent_config.context_config JSONB\n loopDetectorConfig?: Partial<LoopDetectorConfig>;\n /** Max errors from a single parallel batch before aborting remaining calls */\n batchErrorLimit?: number;\n}\n\nexport interface TurnMetrics {\n turn: number;\n inputTokens: number;\n outputTokens: number;\n cacheRead: number;\n cacheCreation: number;\n toolsUsed: string[];\n costUsd: number;\n /** Set when a failover occurred this turn */\n failover?: {\n originalProvider: string;\n activeProvider: string;\n model: string;\n };\n}\n\nexport interface ServerAgentLoopResult {\n finalText: string;\n allTextResponses: string[];\n turnCount: number;\n toolCallCount: number;\n toolsUsed: string[];\n /** Citations collected from all turns (only populated when documents are provided) */\n citations: CitationBlock[];\n tokens: {\n input: number;\n output: number;\n cacheCreation: number;\n cacheRead: number;\n };\n costUsd: number;\n loopDetectorStats: {\n totalErrors: number;\n failedStrategies: number;\n consecutiveFailedTurns: number;\n };\n turns: TurnMetrics[];\n /** Final stop reason from the last API response (end_turn, tool_use, max_tokens) */\n stopReason: string;\n}\n\n// Re-export for consumers\nexport type { SubagentProgressCallback, SubagentProgressEvent };\nexport type { ToolChoice };\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nexport const MAX_RETRIES = 5;\nexport const RETRY_BASE_DELAY_MS = 2000;\nexport const RETRY_MAX_DELAY_MS = 60000;\nexport const RETRY_OVERLOADED_MULTIPLIER = 3;\nexport const MODEL_FALLBACK_AFTER_ATTEMPT = 2;\nexport const DEFAULT_MAX_CONCURRENT_TOOLS = 7;\n\n// Intra-provider model fallback — when a model is overloaded, try a lower-tier model\n// from the same provider instead of retrying the same overloaded endpoint.\nexport const MODEL_FALLBACK_MAP: 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\n// ============================================================================\n// TOOL CHOICE MAPPING — convert ToolChoice to provider-specific format\n// ============================================================================\n\n/**\n * Map ToolChoice to Anthropic API `tool_choice` format.\n * Returns undefined if tools should be omitted entirely.\n */\nexport function mapToolChoiceForAnthropic(\n tc: ToolChoice,\n): { toolChoice?: Record<string, unknown>; omitTools?: boolean } {\n if (tc === \"auto\") return { toolChoice: { type: \"auto\" } };\n if (tc === \"any\") return { toolChoice: { type: \"any\" } };\n if (tc === \"none\") return { omitTools: true };\n if (typeof tc === \"object\" && tc.type === \"tool\") {\n return { toolChoice: { type: \"tool\", name: tc.name } };\n }\n return { toolChoice: { type: \"auto\" } };\n}\n"],"mappings":"AAAA;;AAiBA;AACA;AACA;;AAuIA;;AAIA;AACA;AACA;;AAEA,OAAO,MAAMA,WAAW,GAAG,CAAC;AAC5B,OAAO,MAAMC,mBAAmB,GAAG,IAAI;AACvC,OAAO,MAAMC,kBAAkB,GAAG,KAAK;AACvC,OAAO,MAAMC,2BAA2B,GAAG,CAAC;AAC5C,OAAO,MAAMC,4BAA4B,GAAG,CAAC;AAC7C,OAAO,MAAMC,4BAA4B,GAAG,CAAC;;AAE7C;AACA;AACA,OAAO,MAAMC,kBAA0C,GAAG;EACxD,iBAAiB,EAAE,mBAAmB;EACtC,0BAA0B,EAAE,4BAA4B;EACxD,wBAAwB,EAAE,0BAA0B;EACpD,0BAA0B,EAAE;AAC9B,CAAC;;AAED;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,yBAAyBA,CACvCC,EAAc,EACiD;EAC/D,IAAIA,EAAE,KAAK,MAAM,EAAE,OAAO;IAAEC,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAO;EAAE,CAAC;EAC1D,IAAIF,EAAE,KAAK,KAAK,EAAE,OAAO;IAAEC,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAM;EAAE,CAAC;EACxD,IAAIF,EAAE,KAAK,MAAM,EAAE,OAAO;IAAEG,SAAS,EAAE;EAAK,CAAC;EAC7C,IAAI,OAAOH,EAAE,KAAK,QAAQ,IAAIA,EAAE,CAACE,IAAI,KAAK,MAAM,EAAE;IAChD,OAAO;MAAED,UAAU,EAAE;QAAEC,IAAI,EAAE,MAAM;QAAEE,IAAI,EAAEJ,EAAE,CAACI;MAAK;IAAE,CAAC;EACxD;EACA,OAAO;IAAEH,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAO;EAAE,CAAC;AACzC","ignoreList":[]}
|
|
@@ -57,7 +57,8 @@ class ClickHouseClient {
|
|
|
57
57
|
...this.authHeaders,
|
|
58
58
|
"Content-Type": "application/json"
|
|
59
59
|
},
|
|
60
|
-
body
|
|
60
|
+
body,
|
|
61
|
+
signal: AbortSignal.timeout(10_000)
|
|
61
62
|
});
|
|
62
63
|
if (!response.ok) {
|
|
63
64
|
const text = await response.text();
|
|
@@ -80,7 +81,8 @@ class ClickHouseClient {
|
|
|
80
81
|
...this.authHeaders,
|
|
81
82
|
"Content-Type": "text/plain"
|
|
82
83
|
},
|
|
83
|
-
body: sql
|
|
84
|
+
body: sql,
|
|
85
|
+
signal: AbortSignal.timeout(10_000)
|
|
84
86
|
});
|
|
85
87
|
if (!response.ok) {
|
|
86
88
|
const text = await response.text();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clickhouse-client.js","names":["ClickHouseClient","constructor","host","process","env","CLICKHOUSE_HOST","user","CLICKHOUSE_USER","password","CLICKHOUSE_PASSWORD","database","CLICKHOUSE_DATABASE","enabled","CLICKHOUSE_ENABLED","isEnabled","baseUrl","authHeaders","credentials","Buffer","from","toString","Authorization","insert","table","rows","length","body","map","row","JSON","stringify","join","query","response","fetch","encodeURIComponent","method","headers","ok","text","console","error","status","slice","err","message","sql","trim","split","line","parse","queryOne","_client","getClickHouseClient","insertSpan","span","client","catch","insertError"],"sources":["../../../src/server/lib/clickhouse-client.ts"],"sourcesContent":["/**\n * ClickHouse HTTP Client for Node.js MCP Server\n *\n * Ported from the Deno edge function client (supabase/functions/_shared/clickhouse.ts).\n * Uses the ClickHouse HTTP interface with JSONEachRow format.\n *\n * Environment variables:\n * CLICKHOUSE_HOST — e.g. \"xxx.us-east-1.aws.clickhouse.cloud\"\n * CLICKHOUSE_USER — default \"default\"\n * CLICKHOUSE_PASSWORD — from ClickHouse Cloud console\n * CLICKHOUSE_DATABASE — default \"default\"\n * CLICKHOUSE_ENABLED — \"true\" to enable (graceful no-op otherwise)\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ClickHouseSpan {\n span_id: string;\n trace_id: string;\n parent_span_id?: string;\n store_id?: string;\n service_name: string;\n operation_name?: string;\n span_kind?: string;\n source?: string;\n started_at: string; // ISO 8601\n ended_at: string;\n duration_ms: number;\n status_code?: string;\n severity?: string;\n http_status?: number;\n http_method?: string;\n http_path?: string;\n model_name?: string;\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n token_cost_usd?: number;\n agent_id?: string;\n conversation_id?: string;\n tool_name?: string;\n error_message?: string;\n attributes?: string; // JSON string\n events?: string; // JSON string\n request_id?: string;\n user_id?: string;\n environment?: string;\n // Enriched columns (003_enrich_spans migration)\n user_email?: string;\n error_type?: string;\n retryable?: number; // UInt8: 0 or 1\n resource_type?: string;\n cache_read_tokens?: number;\n cache_creation_tokens?: number;\n input_bytes?: number;\n output_bytes?: number;\n stop_reason?: string;\n turn_number?: number;\n parent_conversation_id?: string;\n}\n\nexport interface ClickHouseError {\n fingerprint: string;\n severity?: string;\n error_type: string;\n error_message: string;\n error_code?: string;\n stack_trace?: string;\n source_file?: string;\n source_line?: number;\n source_function?: string;\n platform?: string;\n service_name: string;\n service_version?: string;\n environment?: string;\n store_id?: string;\n user_id?: string;\n user_email?: string;\n trace_id?: string;\n span_id?: string;\n request_id?: string;\n tags?: string; // JSON string\n extra?: string; // JSON string\n breadcrumbs?: string; // JSON string\n device_info?: string; // JSON string\n runtime_info?: string; // JSON string\n occurred_at: string; // ISO 8601\n}\n\n// ============================================================================\n// Client\n// ============================================================================\n\nclass ClickHouseClient {\n private host: string;\n private user: string;\n private password: string;\n private database: string;\n private enabled: boolean;\n\n constructor() {\n this.host = process.env.CLICKHOUSE_HOST || \"\";\n this.user = process.env.CLICKHOUSE_USER || \"default\";\n this.password = process.env.CLICKHOUSE_PASSWORD || \"\";\n this.database = process.env.CLICKHOUSE_DATABASE || \"default\";\n this.enabled = process.env.CLICKHOUSE_ENABLED === \"true\";\n }\n\n get isEnabled(): boolean {\n return this.enabled && !!this.host;\n }\n\n private get baseUrl(): string {\n return `https://${this.host}:8443`;\n }\n\n private get authHeaders(): Record<string, string> {\n const credentials = Buffer.from(`${this.user}:${this.password}`).toString(\"base64\");\n return {\n Authorization: `Basic ${credentials}`,\n \"X-ClickHouse-Database\": this.database,\n };\n }\n\n /**\n * Insert rows into a ClickHouse table using JSONEachRow format.\n * Fire-and-forget — logs errors but never throws.\n */\n async insert(table: string, rows: Record<string, unknown>[]): Promise<void> {\n if (!this.isEnabled || rows.length === 0) return;\n\n const body = rows.map((row) => JSON.stringify(row)).join(\"\\n\");\n const query = `INSERT INTO ${table} FORMAT JSONEachRow`;\n\n try {\n const response = await fetch(\n `${this.baseUrl}/?query=${encodeURIComponent(query)}`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeaders,\n \"Content-Type\": \"application/json\",\n },\n body,\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.error(`[clickhouse] insert ${table} failed (${response.status}): ${text.slice(0, 200)}`);\n }\n } catch (err) {\n console.error(`[clickhouse] insert ${table} error: ${(err as Error).message}`);\n }\n }\n\n /**\n * Execute a SELECT query and return parsed JSON rows.\n */\n async query<T = Record<string, unknown>>(sql: string): Promise<T[]> {\n if (!this.isEnabled) return [];\n\n try {\n const response = await fetch(\n `${this.baseUrl}/?default_format=JSONEachRow`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeaders,\n \"Content-Type\": \"text/plain\",\n },\n body: sql,\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.error(`[clickhouse] query failed (${response.status}): ${text.slice(0, 200)}`);\n return [];\n }\n\n const text = await response.text();\n if (!text.trim()) return [];\n\n return text\n .trim()\n .split(\"\\n\")\n .map((line) => JSON.parse(line) as T);\n } catch (err) {\n console.error(`[clickhouse] query error: ${(err as Error).message}`);\n return [];\n }\n }\n\n /**\n * Execute a query and return a single aggregated result (e.g. JSONB_BUILD_OBJECT).\n * Useful for analytics RPCs that return one row with a complex JSON object.\n */\n async queryOne<T = Record<string, unknown>>(sql: string): Promise<T | null> {\n const rows = await this.query<T>(sql);\n return rows.length > 0 ? rows[0] : null;\n }\n}\n\n// Singleton — lazy init on first access\nlet _client: ClickHouseClient | null = null;\n\nexport function getClickHouseClient(): ClickHouseClient {\n if (!_client) {\n _client = new ClickHouseClient();\n }\n return _client;\n}\n\n// ============================================================================\n// Convenience: Insert a span\n// ============================================================================\n\nexport function insertSpan(span: ClickHouseSpan): void {\n const client = getClickHouseClient();\n if (!client.isEnabled) return;\n\n client.insert(\"ai_spans\", [span as unknown as Record<string, unknown>]).catch((err) => {\n console.error(`[clickhouse] insertSpan failed: ${(err as Error).message}`);\n });\n}\n\n// ============================================================================\n// Convenience: Insert an error event\n// ============================================================================\n\nexport function insertError(error: ClickHouseError): void {\n const client = getClickHouseClient();\n if (!client.isEnabled) return;\n\n client.insert(\"error_events\", [error as unknown as Record<string, unknown>]).catch((err) => {\n console.error(`[clickhouse] insertError failed: ${(err as Error).message}`);\n });\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"clickhouse-client.js","names":["ClickHouseClient","constructor","host","process","env","CLICKHOUSE_HOST","user","CLICKHOUSE_USER","password","CLICKHOUSE_PASSWORD","database","CLICKHOUSE_DATABASE","enabled","CLICKHOUSE_ENABLED","isEnabled","baseUrl","authHeaders","credentials","Buffer","from","toString","Authorization","insert","table","rows","length","body","map","row","JSON","stringify","join","query","response","fetch","encodeURIComponent","method","headers","signal","AbortSignal","timeout","ok","text","console","error","status","slice","err","message","sql","trim","split","line","parse","queryOne","_client","getClickHouseClient","insertSpan","span","client","catch","insertError"],"sources":["../../../src/server/lib/clickhouse-client.ts"],"sourcesContent":["/**\n * ClickHouse HTTP Client for Node.js MCP Server\n *\n * Ported from the Deno edge function client (supabase/functions/_shared/clickhouse.ts).\n * Uses the ClickHouse HTTP interface with JSONEachRow format.\n *\n * Environment variables:\n * CLICKHOUSE_HOST — e.g. \"xxx.us-east-1.aws.clickhouse.cloud\"\n * CLICKHOUSE_USER — default \"default\"\n * CLICKHOUSE_PASSWORD — from ClickHouse Cloud console\n * CLICKHOUSE_DATABASE — default \"default\"\n * CLICKHOUSE_ENABLED — \"true\" to enable (graceful no-op otherwise)\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ClickHouseSpan {\n span_id: string;\n trace_id: string;\n parent_span_id?: string;\n store_id?: string;\n service_name: string;\n operation_name?: string;\n span_kind?: string;\n source?: string;\n started_at: string; // ISO 8601\n ended_at: string;\n duration_ms: number;\n status_code?: string;\n severity?: string;\n http_status?: number;\n http_method?: string;\n http_path?: string;\n model_name?: string;\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n token_cost_usd?: number;\n agent_id?: string;\n conversation_id?: string;\n tool_name?: string;\n error_message?: string;\n attributes?: string; // JSON string\n events?: string; // JSON string\n request_id?: string;\n user_id?: string;\n environment?: string;\n // Enriched columns (003_enrich_spans migration)\n user_email?: string;\n error_type?: string;\n retryable?: number; // UInt8: 0 or 1\n resource_type?: string;\n cache_read_tokens?: number;\n cache_creation_tokens?: number;\n input_bytes?: number;\n output_bytes?: number;\n stop_reason?: string;\n turn_number?: number;\n parent_conversation_id?: string;\n}\n\nexport interface ClickHouseError {\n fingerprint: string;\n severity?: string;\n error_type: string;\n error_message: string;\n error_code?: string;\n stack_trace?: string;\n source_file?: string;\n source_line?: number;\n source_function?: string;\n platform?: string;\n service_name: string;\n service_version?: string;\n environment?: string;\n store_id?: string;\n user_id?: string;\n user_email?: string;\n trace_id?: string;\n span_id?: string;\n request_id?: string;\n tags?: string; // JSON string\n extra?: string; // JSON string\n breadcrumbs?: string; // JSON string\n device_info?: string; // JSON string\n runtime_info?: string; // JSON string\n occurred_at: string; // ISO 8601\n}\n\n// ============================================================================\n// Client\n// ============================================================================\n\nclass ClickHouseClient {\n private host: string;\n private user: string;\n private password: string;\n private database: string;\n private enabled: boolean;\n\n constructor() {\n this.host = process.env.CLICKHOUSE_HOST || \"\";\n this.user = process.env.CLICKHOUSE_USER || \"default\";\n this.password = process.env.CLICKHOUSE_PASSWORD || \"\";\n this.database = process.env.CLICKHOUSE_DATABASE || \"default\";\n this.enabled = process.env.CLICKHOUSE_ENABLED === \"true\";\n }\n\n get isEnabled(): boolean {\n return this.enabled && !!this.host;\n }\n\n private get baseUrl(): string {\n return `https://${this.host}:8443`;\n }\n\n private get authHeaders(): Record<string, string> {\n const credentials = Buffer.from(`${this.user}:${this.password}`).toString(\"base64\");\n return {\n Authorization: `Basic ${credentials}`,\n \"X-ClickHouse-Database\": this.database,\n };\n }\n\n /**\n * Insert rows into a ClickHouse table using JSONEachRow format.\n * Fire-and-forget — logs errors but never throws.\n */\n async insert(table: string, rows: Record<string, unknown>[]): Promise<void> {\n if (!this.isEnabled || rows.length === 0) return;\n\n const body = rows.map((row) => JSON.stringify(row)).join(\"\\n\");\n const query = `INSERT INTO ${table} FORMAT JSONEachRow`;\n\n try {\n const response = await fetch(\n `${this.baseUrl}/?query=${encodeURIComponent(query)}`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeaders,\n \"Content-Type\": \"application/json\",\n },\n body,\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.error(`[clickhouse] insert ${table} failed (${response.status}): ${text.slice(0, 200)}`);\n }\n } catch (err) {\n console.error(`[clickhouse] insert ${table} error: ${(err as Error).message}`);\n }\n }\n\n /**\n * Execute a SELECT query and return parsed JSON rows.\n */\n async query<T = Record<string, unknown>>(sql: string): Promise<T[]> {\n if (!this.isEnabled) return [];\n\n try {\n const response = await fetch(\n `${this.baseUrl}/?default_format=JSONEachRow`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeaders,\n \"Content-Type\": \"text/plain\",\n },\n body: sql,\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.error(`[clickhouse] query failed (${response.status}): ${text.slice(0, 200)}`);\n return [];\n }\n\n const text = await response.text();\n if (!text.trim()) return [];\n\n return text\n .trim()\n .split(\"\\n\")\n .map((line) => JSON.parse(line) as T);\n } catch (err) {\n console.error(`[clickhouse] query error: ${(err as Error).message}`);\n return [];\n }\n }\n\n /**\n * Execute a query and return a single aggregated result (e.g. JSONB_BUILD_OBJECT).\n * Useful for analytics RPCs that return one row with a complex JSON object.\n */\n async queryOne<T = Record<string, unknown>>(sql: string): Promise<T | null> {\n const rows = await this.query<T>(sql);\n return rows.length > 0 ? rows[0] : null;\n }\n}\n\n// Singleton — lazy init on first access\nlet _client: ClickHouseClient | null = null;\n\nexport function getClickHouseClient(): ClickHouseClient {\n if (!_client) {\n _client = new ClickHouseClient();\n }\n return _client;\n}\n\n// ============================================================================\n// Convenience: Insert a span\n// ============================================================================\n\nexport function insertSpan(span: ClickHouseSpan): void {\n const client = getClickHouseClient();\n if (!client.isEnabled) return;\n\n client.insert(\"ai_spans\", [span as unknown as Record<string, unknown>]).catch((err) => {\n console.error(`[clickhouse] insertSpan failed: ${(err as Error).message}`);\n });\n}\n\n// ============================================================================\n// Convenience: Insert an error event\n// ============================================================================\n\nexport function insertError(error: ClickHouseError): void {\n const client = getClickHouseClient();\n if (!client.isEnabled) return;\n\n client.insert(\"error_events\", [error as unknown as Record<string, unknown>]).catch((err) => {\n console.error(`[clickhouse] insertError failed: ${(err as Error).message}`);\n });\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -55,7 +55,10 @@ if (process.send) {
|
|
|
55
55
|
const logs = [];
|
|
56
56
|
|
|
57
57
|
// Validate code for dangerous patterns (defense-in-depth — sandbox isolation is primary defense)
|
|
58
|
-
const dangerousPatterns = [/constructor\s*\[/i, /constructor\s*\(/i, /\.constructor/i, /__proto__/i, /prototype\s*\[/i, /\bprocess\b/, /\brequire\b/, /\bimport\b/, /\bglobalThis\b/, /\bglobal\b/, /\bFunction\b/, /\beval\b/, /\bReflect\b/, /\bProxy\b/, /\bSymbol\b/, /\bWeakRef\b/]
|
|
58
|
+
const dangerousPatterns = [/constructor\s*\[/i, /constructor\s*\(/i, /\.constructor/i, /\["constructor"\]/i, /\['constructor'\]/i, /\[`constructor`\]/i, /__proto__/i, /prototype\s*\[/i, /\bprocess\b/, /\brequire\b/, /\bimport\b/, /\bglobalThis\b/, /\bglobal\b/, /\bFunction\b/, /\beval\b/, /\bReflect\b/, /\bProxy\b/, /\bSymbol\b/, /\bWeakRef\b/, /\[\s*['"`]__proto__['"`]\s*\]/i, /\[\s*['"`]prototype['"`]\s*\]/i, /\bthis\s*\[/,
|
|
59
|
+
// bracket notation on this
|
|
60
|
+
/\barguments\b/ // arguments object can leak caller
|
|
61
|
+
];
|
|
59
62
|
for (const pattern of dangerousPatterns) {
|
|
60
63
|
if (pattern.test(msg.code)) {
|
|
61
64
|
const result = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-worker.js","names":["runInNewContext","randomUUID","safeFunction","fn","safe","args","apply","Object","defineProperty","value","undefined","writable","configurable","freeze","safeNamespace","source","keys","ns","create","key","val","process","send","on","msg","type","logs","dangerousPatterns","pattern","test","code","result","success","error","sandbox","steps","JSON","parse","stringify","context","trigger","input","output","logFn","push","map","String","join","consoleObj","log","console","mathKeys","Math","dateNs","now","Date","parseInt","parseFloat","isNaN","isFinite","encodeURIComponent","decodeURIComponent","urlNs","u","URL","href","protocol","host","hostname","port","pathname","search","hash","origin","searchParams","fromEntries","format","parts","bufferNs","from","Buffer","alloc","size","cryptoNs","crypto","require","global","globalThis","Function","eval","setTimeout","setInterval","Reflect","Proxy","Symbol","WeakRef","timeout","timeoutMs","filename","breakOnSigint","microtaskMode","workerResult","err","message"],"sources":["../../../src/server/lib/code-worker.ts"],"sourcesContent":["// server/lib/code-worker.ts — Isolated code execution worker\n// Runs as a child process via fork(). Receives code + context via IPC,\n// executes in a fresh vm context, returns result. Crash here cannot take down the server.\n\nimport { runInNewContext } from \"node:vm\";\nimport { randomUUID } from \"node:crypto\";\n\ninterface WorkerMessage {\n type: \"execute\";\n code: string;\n context: {\n steps: Record<string, unknown>;\n trigger: Record<string, unknown>;\n input?: unknown;\n };\n timeoutMs: number;\n}\n\ninterface WorkerResult {\n type: \"result\";\n success: boolean;\n output?: unknown;\n logs?: string[];\n error?: string;\n}\n\n/**\n * Create a safe wrapper function that cannot be used to reach the host Function constructor.\n * Wraps a host function in a proxy whose prototype chain is severed — .constructor returns\n * undefined instead of the host Function constructor.\n */\nfunction safeFunction<T extends (...args: any[]) => any>(fn: T): T {\n // Create a wrapper function whose .constructor and .prototype are severed.\n // We can't make a callable null-prototype object directly, so we override them on a regular function.\n const safe = function (this: unknown, ...args: unknown[]) {\n return fn.apply(this, args);\n };\n Object.defineProperty(safe, \"constructor\", { value: undefined, writable: false, configurable: false });\n Object.defineProperty(safe, \"prototype\", { value: undefined, writable: false, configurable: false });\n // Freeze to prevent re-assignment of constructor\n Object.freeze(safe);\n return safe as unknown as T;\n}\n\n/**\n * Create a safe namespace object (like Math, JSON, Buffer) whose properties\n * cannot reach the host Function constructor through any prototype chain.\n * All function-valued properties are wrapped via safeFunction().\n */\nfunction safeNamespace(source: Record<string, unknown>, keys: string[]): Readonly<Record<string, unknown>> {\n const ns = Object.create(null) as Record<string, unknown>;\n for (const key of keys) {\n const val = source[key];\n if (typeof val === \"function\") {\n ns[key] = safeFunction(val as (...args: any[]) => any);\n } else {\n ns[key] = val;\n }\n }\n return Object.freeze(ns);\n}\n\n// Only run when forked as a child process\nif (process.send) {\n process.on(\"message\", (msg: WorkerMessage) => {\n if (msg.type !== \"execute\") return;\n\n const logs: string[] = [];\n\n // Validate code for dangerous patterns (defense-in-depth — sandbox isolation is primary defense)\n const dangerousPatterns = [\n /constructor\\s*\\[/i,\n /constructor\\s*\\(/i,\n /\\.constructor/i,\n /__proto__/i,\n /prototype\\s*\\[/i,\n /\\bprocess\\b/,\n /\\brequire\\b/,\n /\\bimport\\b/,\n /\\bglobalThis\\b/,\n /\\bglobal\\b/,\n /\\bFunction\\b/,\n /\\beval\\b/,\n /\\bReflect\\b/,\n /\\bProxy\\b/,\n /\\bSymbol\\b/,\n /\\bWeakRef\\b/,\n ];\n for (const pattern of dangerousPatterns) {\n if (pattern.test(msg.code)) {\n const result: WorkerResult = {\n type: \"result\",\n success: false,\n error: `Code contains blocked pattern: ${pattern.source}`,\n };\n process.send!(result);\n return;\n }\n }\n\n // Build hardened sandbox — NO host constructors or direct function references.\n // vm.runInNewContext provides its own Array, Object, String, Number, Boolean in the\n // sandbox context. We do NOT pass host realm constructors, which would allow\n // escaping via .constructor.constructor('return process')().\n const sandbox = Object.create(null) as Record<string, unknown>;\n sandbox.steps = JSON.parse(JSON.stringify(msg.context.steps));\n sandbox.trigger = JSON.parse(JSON.stringify(msg.context.trigger));\n sandbox.input = msg.context.input != null ? JSON.parse(JSON.stringify(msg.context.input)) : undefined;\n sandbox.output = undefined;\n\n // console — safe: null-prototype object with wrapped log function\n const logFn = safeFunction((...args: unknown[]) => logs.push(args.map(String).join(\" \")));\n const consoleObj = Object.create(null) as Record<string, unknown>;\n consoleObj.log = logFn;\n sandbox.console = Object.freeze(consoleObj);\n\n // JSON — safe namespace with only parse/stringify\n sandbox.JSON = safeNamespace(JSON as unknown as Record<string, unknown>, [\"parse\", \"stringify\"]);\n\n // Math — safe namespace (all methods wrapped, numeric constants preserved)\n const mathKeys = [\n \"abs\", \"ceil\", \"floor\", \"round\", \"max\", \"min\", \"pow\", \"sqrt\", \"log\", \"log2\", \"log10\",\n \"random\", \"sign\", \"trunc\", \"cbrt\", \"hypot\", \"clz32\", \"imul\", \"fround\",\n \"sin\", \"cos\", \"tan\", \"asin\", \"acos\", \"atan\", \"atan2\", \"sinh\", \"cosh\", \"tanh\",\n \"PI\", \"E\", \"LN2\", \"LN10\", \"LOG2E\", \"LOG10E\", \"SQRT2\", \"SQRT1_2\",\n ];\n sandbox.Math = safeNamespace(Math as unknown as Record<string, unknown>, mathKeys);\n\n // Date — only expose Date.now() as a safe function, not the Date constructor itself.\n // The Date constructor is a host function whose .constructor leads to Function.\n const dateNs = Object.create(null) as Record<string, unknown>;\n dateNs.now = safeFunction(Date.now);\n sandbox.Date = Object.freeze(dateNs);\n\n // Utility functions — each wrapped to sever .constructor chain\n sandbox.parseInt = safeFunction(parseInt);\n sandbox.parseFloat = safeFunction(parseFloat);\n sandbox.isNaN = safeFunction(isNaN);\n sandbox.isFinite = safeFunction(isFinite);\n sandbox.encodeURIComponent = safeFunction(encodeURIComponent);\n sandbox.decodeURIComponent = safeFunction(decodeURIComponent);\n\n // URL — safe namespace with only parse method (no constructor exposure)\n const urlNs = Object.create(null) as Record<string, unknown>;\n urlNs.parse = safeFunction((input: string) => {\n try {\n const u = new URL(input);\n return { href: u.href, protocol: u.protocol, host: u.host, hostname: u.hostname,\n port: u.port, pathname: u.pathname, search: u.search, hash: u.hash,\n origin: u.origin, searchParams: Object.fromEntries(u.searchParams) };\n } catch { return null; }\n });\n urlNs.format = safeFunction((parts: Record<string, string>) => {\n try { return new URL(`${parts.protocol || \"https:\"}//${parts.host || parts.hostname || \"\"}${parts.pathname || \"\"}${parts.search || \"\"}`).href; }\n catch { return null; }\n });\n sandbox.URL = Object.freeze(urlNs);\n\n // Buffer — safe namespace with wrapped from/alloc\n const bufferNs = Object.create(null) as Record<string, unknown>;\n bufferNs.from = safeFunction((...args: unknown[]) => Buffer.from(args[0] as any, args[1] as any));\n bufferNs.alloc = safeFunction((size: number) => Buffer.alloc(size));\n sandbox.Buffer = Object.freeze(bufferNs);\n\n // crypto — safe namespace with wrapped randomUUID\n const cryptoNs = Object.create(null) as Record<string, unknown>;\n cryptoNs.randomUUID = safeFunction(randomUUID);\n sandbox.crypto = Object.freeze(cryptoNs);\n\n // DO NOT expose host constructors: Array, Object, String, Number, Boolean.\n // vm.runInNewContext creates its own versions of these in the sandbox context.\n // Passing host constructors would allow: [].constructor.constructor('return process')()\n\n // Block dangerous globals explicitly\n sandbox.process = undefined;\n sandbox.require = undefined;\n sandbox.global = undefined;\n sandbox.globalThis = undefined;\n sandbox.Function = undefined;\n sandbox.eval = undefined;\n sandbox.setTimeout = undefined;\n sandbox.setInterval = undefined;\n sandbox.Reflect = undefined;\n sandbox.Proxy = undefined;\n sandbox.Symbol = undefined;\n sandbox.WeakRef = undefined;\n\n try {\n const result = runInNewContext(msg.code, sandbox, {\n timeout: msg.timeoutMs,\n filename: \"workflow-code-step\",\n breakOnSigint: true,\n microtaskMode: \"afterEvaluate\",\n });\n\n const workerResult: WorkerResult = {\n type: \"result\",\n success: true,\n output: { result: sandbox.output ?? result, logs },\n };\n process.send!(workerResult);\n } catch (err: any) {\n const workerResult: WorkerResult = {\n type: \"result\",\n success: false,\n error: err.code === \"ERR_SCRIPT_EXECUTION_TIMEOUT\"\n ? `Code execution timed out after ${msg.timeoutMs}ms`\n : `Code error: ${err.message}`,\n logs,\n };\n process.send!(workerResult);\n }\n });\n\n // Signal ready\n process.send({ type: \"ready\" });\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"code-worker.js","names":["runInNewContext","randomUUID","safeFunction","fn","safe","args","apply","Object","defineProperty","value","undefined","writable","configurable","freeze","safeNamespace","source","keys","ns","create","key","val","process","send","on","msg","type","logs","dangerousPatterns","pattern","test","code","result","success","error","sandbox","steps","JSON","parse","stringify","context","trigger","input","output","logFn","push","map","String","join","consoleObj","log","console","mathKeys","Math","dateNs","now","Date","parseInt","parseFloat","isNaN","isFinite","encodeURIComponent","decodeURIComponent","urlNs","u","URL","href","protocol","host","hostname","port","pathname","search","hash","origin","searchParams","fromEntries","format","parts","bufferNs","from","Buffer","alloc","size","cryptoNs","crypto","require","global","globalThis","Function","eval","setTimeout","setInterval","Reflect","Proxy","Symbol","WeakRef","timeout","timeoutMs","filename","breakOnSigint","microtaskMode","workerResult","err","message"],"sources":["../../../src/server/lib/code-worker.ts"],"sourcesContent":["// server/lib/code-worker.ts — Isolated code execution worker\n// Runs as a child process via fork(). Receives code + context via IPC,\n// executes in a fresh vm context, returns result. Crash here cannot take down the server.\n\nimport { runInNewContext } from \"node:vm\";\nimport { randomUUID } from \"node:crypto\";\n\ninterface WorkerMessage {\n type: \"execute\";\n code: string;\n context: {\n steps: Record<string, unknown>;\n trigger: Record<string, unknown>;\n input?: unknown;\n };\n timeoutMs: number;\n}\n\ninterface WorkerResult {\n type: \"result\";\n success: boolean;\n output?: unknown;\n logs?: string[];\n error?: string;\n}\n\n/**\n * Create a safe wrapper function that cannot be used to reach the host Function constructor.\n * Wraps a host function in a proxy whose prototype chain is severed — .constructor returns\n * undefined instead of the host Function constructor.\n */\nfunction safeFunction<T extends (...args: any[]) => any>(fn: T): T {\n // Create a wrapper function whose .constructor and .prototype are severed.\n // We can't make a callable null-prototype object directly, so we override them on a regular function.\n const safe = function (this: unknown, ...args: unknown[]) {\n return fn.apply(this, args);\n };\n Object.defineProperty(safe, \"constructor\", { value: undefined, writable: false, configurable: false });\n Object.defineProperty(safe, \"prototype\", { value: undefined, writable: false, configurable: false });\n // Freeze to prevent re-assignment of constructor\n Object.freeze(safe);\n return safe as unknown as T;\n}\n\n/**\n * Create a safe namespace object (like Math, JSON, Buffer) whose properties\n * cannot reach the host Function constructor through any prototype chain.\n * All function-valued properties are wrapped via safeFunction().\n */\nfunction safeNamespace(source: Record<string, unknown>, keys: string[]): Readonly<Record<string, unknown>> {\n const ns = Object.create(null) as Record<string, unknown>;\n for (const key of keys) {\n const val = source[key];\n if (typeof val === \"function\") {\n ns[key] = safeFunction(val as (...args: any[]) => any);\n } else {\n ns[key] = val;\n }\n }\n return Object.freeze(ns);\n}\n\n// Only run when forked as a child process\nif (process.send) {\n process.on(\"message\", (msg: WorkerMessage) => {\n if (msg.type !== \"execute\") return;\n\n const logs: string[] = [];\n\n // Validate code for dangerous patterns (defense-in-depth — sandbox isolation is primary defense)\n const dangerousPatterns = [\n /constructor\\s*\\[/i,\n /constructor\\s*\\(/i,\n /\\.constructor/i,\n /\\[\"constructor\"\\]/i,\n /\\['constructor'\\]/i,\n /\\[`constructor`\\]/i,\n /__proto__/i,\n /prototype\\s*\\[/i,\n /\\bprocess\\b/,\n /\\brequire\\b/,\n /\\bimport\\b/,\n /\\bglobalThis\\b/,\n /\\bglobal\\b/,\n /\\bFunction\\b/,\n /\\beval\\b/,\n /\\bReflect\\b/,\n /\\bProxy\\b/,\n /\\bSymbol\\b/,\n /\\bWeakRef\\b/,\n /\\[\\s*['\"`]__proto__['\"`]\\s*\\]/i,\n /\\[\\s*['\"`]prototype['\"`]\\s*\\]/i,\n /\\bthis\\s*\\[/, // bracket notation on this\n /\\barguments\\b/, // arguments object can leak caller\n ];\n for (const pattern of dangerousPatterns) {\n if (pattern.test(msg.code)) {\n const result: WorkerResult = {\n type: \"result\",\n success: false,\n error: `Code contains blocked pattern: ${pattern.source}`,\n };\n process.send!(result);\n return;\n }\n }\n\n // Build hardened sandbox — NO host constructors or direct function references.\n // vm.runInNewContext provides its own Array, Object, String, Number, Boolean in the\n // sandbox context. We do NOT pass host realm constructors, which would allow\n // escaping via .constructor.constructor('return process')().\n const sandbox = Object.create(null) as Record<string, unknown>;\n sandbox.steps = JSON.parse(JSON.stringify(msg.context.steps));\n sandbox.trigger = JSON.parse(JSON.stringify(msg.context.trigger));\n sandbox.input = msg.context.input != null ? JSON.parse(JSON.stringify(msg.context.input)) : undefined;\n sandbox.output = undefined;\n\n // console — safe: null-prototype object with wrapped log function\n const logFn = safeFunction((...args: unknown[]) => logs.push(args.map(String).join(\" \")));\n const consoleObj = Object.create(null) as Record<string, unknown>;\n consoleObj.log = logFn;\n sandbox.console = Object.freeze(consoleObj);\n\n // JSON — safe namespace with only parse/stringify\n sandbox.JSON = safeNamespace(JSON as unknown as Record<string, unknown>, [\"parse\", \"stringify\"]);\n\n // Math — safe namespace (all methods wrapped, numeric constants preserved)\n const mathKeys = [\n \"abs\", \"ceil\", \"floor\", \"round\", \"max\", \"min\", \"pow\", \"sqrt\", \"log\", \"log2\", \"log10\",\n \"random\", \"sign\", \"trunc\", \"cbrt\", \"hypot\", \"clz32\", \"imul\", \"fround\",\n \"sin\", \"cos\", \"tan\", \"asin\", \"acos\", \"atan\", \"atan2\", \"sinh\", \"cosh\", \"tanh\",\n \"PI\", \"E\", \"LN2\", \"LN10\", \"LOG2E\", \"LOG10E\", \"SQRT2\", \"SQRT1_2\",\n ];\n sandbox.Math = safeNamespace(Math as unknown as Record<string, unknown>, mathKeys);\n\n // Date — only expose Date.now() as a safe function, not the Date constructor itself.\n // The Date constructor is a host function whose .constructor leads to Function.\n const dateNs = Object.create(null) as Record<string, unknown>;\n dateNs.now = safeFunction(Date.now);\n sandbox.Date = Object.freeze(dateNs);\n\n // Utility functions — each wrapped to sever .constructor chain\n sandbox.parseInt = safeFunction(parseInt);\n sandbox.parseFloat = safeFunction(parseFloat);\n sandbox.isNaN = safeFunction(isNaN);\n sandbox.isFinite = safeFunction(isFinite);\n sandbox.encodeURIComponent = safeFunction(encodeURIComponent);\n sandbox.decodeURIComponent = safeFunction(decodeURIComponent);\n\n // URL — safe namespace with only parse method (no constructor exposure)\n const urlNs = Object.create(null) as Record<string, unknown>;\n urlNs.parse = safeFunction((input: string) => {\n try {\n const u = new URL(input);\n return { href: u.href, protocol: u.protocol, host: u.host, hostname: u.hostname,\n port: u.port, pathname: u.pathname, search: u.search, hash: u.hash,\n origin: u.origin, searchParams: Object.fromEntries(u.searchParams) };\n } catch { return null; }\n });\n urlNs.format = safeFunction((parts: Record<string, string>) => {\n try { return new URL(`${parts.protocol || \"https:\"}//${parts.host || parts.hostname || \"\"}${parts.pathname || \"\"}${parts.search || \"\"}`).href; }\n catch { return null; }\n });\n sandbox.URL = Object.freeze(urlNs);\n\n // Buffer — safe namespace with wrapped from/alloc\n const bufferNs = Object.create(null) as Record<string, unknown>;\n bufferNs.from = safeFunction((...args: unknown[]) => Buffer.from(args[0] as any, args[1] as any));\n bufferNs.alloc = safeFunction((size: number) => Buffer.alloc(size));\n sandbox.Buffer = Object.freeze(bufferNs);\n\n // crypto — safe namespace with wrapped randomUUID\n const cryptoNs = Object.create(null) as Record<string, unknown>;\n cryptoNs.randomUUID = safeFunction(randomUUID);\n sandbox.crypto = Object.freeze(cryptoNs);\n\n // DO NOT expose host constructors: Array, Object, String, Number, Boolean.\n // vm.runInNewContext creates its own versions of these in the sandbox context.\n // Passing host constructors would allow: [].constructor.constructor('return process')()\n\n // Block dangerous globals explicitly\n sandbox.process = undefined;\n sandbox.require = undefined;\n sandbox.global = undefined;\n sandbox.globalThis = undefined;\n sandbox.Function = undefined;\n sandbox.eval = undefined;\n sandbox.setTimeout = undefined;\n sandbox.setInterval = undefined;\n sandbox.Reflect = undefined;\n sandbox.Proxy = undefined;\n sandbox.Symbol = undefined;\n sandbox.WeakRef = undefined;\n\n try {\n const result = runInNewContext(msg.code, sandbox, {\n timeout: msg.timeoutMs,\n filename: \"workflow-code-step\",\n breakOnSigint: true,\n microtaskMode: \"afterEvaluate\",\n });\n\n const workerResult: WorkerResult = {\n type: \"result\",\n success: true,\n output: { result: sandbox.output ?? result, logs },\n };\n process.send!(workerResult);\n } catch (err: any) {\n const workerResult: WorkerResult = {\n type: \"result\",\n success: false,\n error: err.code === \"ERR_SCRIPT_EXECUTION_TIMEOUT\"\n ? `Code execution timed out after ${msg.timeoutMs}ms`\n : `Code error: ${err.message}`,\n logs,\n };\n process.send!(workerResult);\n }\n });\n\n // Signal ready\n process.send({ type: \"ready\" });\n}\n"],"mappings":"AAAA;AACA;AACA;;AAEA,SAASA,eAAe,QAAQ,SAAS;AACzC,SAASC,UAAU,QAAQ,aAAa;AAqBxC;AACA;AACA;AACA;AACA;AACA,SAASC,YAAYA,CAAoCC,EAAK,EAAK;EACjE;EACA;EACA,MAAMC,IAAI,GAAG,SAAAA,CAAyB,GAAGC,IAAe,EAAE;IACxD,OAAOF,EAAE,CAACG,KAAK,CAAC,IAAI,EAAED,IAAI,CAAC;EAC7B,CAAC;EACDE,MAAM,CAACC,cAAc,CAACJ,IAAI,EAAE,aAAa,EAAE;IAAEK,KAAK,EAAEC,SAAS;IAAEC,QAAQ,EAAE,KAAK;IAAEC,YAAY,EAAE;EAAM,CAAC,CAAC;EACtGL,MAAM,CAACC,cAAc,CAACJ,IAAI,EAAE,WAAW,EAAE;IAAEK,KAAK,EAAEC,SAAS;IAAEC,QAAQ,EAAE,KAAK;IAAEC,YAAY,EAAE;EAAM,CAAC,CAAC;EACpG;EACAL,MAAM,CAACM,MAAM,CAACT,IAAI,CAAC;EACnB,OAAOA,IAAI;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASU,aAAaA,CAACC,MAA+B,EAAEC,IAAc,EAAqC;EACzG,MAAMC,EAAE,GAAGV,MAAM,CAACW,MAAM,CAAC,IAAI,CAA4B;EACzD,KAAK,MAAMC,GAAG,IAAIH,IAAI,EAAE;IACtB,MAAMI,GAAG,GAAGL,MAAM,CAACI,GAAG,CAAC;IACvB,IAAI,OAAOC,GAAG,KAAK,UAAU,EAAE;MAC7BH,EAAE,CAACE,GAAG,CAAC,GAAGjB,YAAY,CAACkB,GAA8B,CAAC;IACxD,CAAC,MAAM;MACLH,EAAE,CAACE,GAAG,CAAC,GAAGC,GAAG;IACf;EACF;EACA,OAAOb,MAAM,CAACM,MAAM,CAACI,EAAE,CAAC;AAC1B;;AAEA;AACA,IAAII,OAAO,CAACC,IAAI,EAAE;EAChBD,OAAO,CAACE,EAAE,CAAC,SAAS,EAAGC,GAAkB,IAAK;IAC5C,IAAIA,GAAG,CAACC,IAAI,KAAK,SAAS,EAAE;IAE5B,MAAMC,IAAc,GAAG,EAAE;;IAEzB;IACA,MAAMC,iBAAiB,GAAG,CACxB,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,UAAU,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,aAAa,EACb,gCAAgC,EAChC,gCAAgC,EAChC,aAAa;IAAQ;IACrB,eAAe,CAAO;IAAA,CACvB;IACD,KAAK,MAAMC,OAAO,IAAID,iBAAiB,EAAE;MACvC,IAAIC,OAAO,CAACC,IAAI,CAACL,GAAG,CAACM,IAAI,CAAC,EAAE;QAC1B,MAAMC,MAAoB,GAAG;UAC3BN,IAAI,EAAE,QAAQ;UACdO,OAAO,EAAE,KAAK;UACdC,KAAK,EAAE,kCAAkCL,OAAO,CAACb,MAAM;QACzD,CAAC;QACDM,OAAO,CAACC,IAAI,CAAES,MAAM,CAAC;QACrB;MACF;IACF;;IAEA;IACA;IACA;IACA;IACA,MAAMG,OAAO,GAAG3B,MAAM,CAACW,MAAM,CAAC,IAAI,CAA4B;IAC9DgB,OAAO,CAACC,KAAK,GAAGC,IAAI,CAACC,KAAK,CAACD,IAAI,CAACE,SAAS,CAACd,GAAG,CAACe,OAAO,CAACJ,KAAK,CAAC,CAAC;IAC7DD,OAAO,CAACM,OAAO,GAAGJ,IAAI,CAACC,KAAK,CAACD,IAAI,CAACE,SAAS,CAACd,GAAG,CAACe,OAAO,CAACC,OAAO,CAAC,CAAC;IACjEN,OAAO,CAACO,KAAK,GAAGjB,GAAG,CAACe,OAAO,CAACE,KAAK,IAAI,IAAI,GAAGL,IAAI,CAACC,KAAK,CAACD,IAAI,CAACE,SAAS,CAACd,GAAG,CAACe,OAAO,CAACE,KAAK,CAAC,CAAC,GAAG/B,SAAS;IACrGwB,OAAO,CAACQ,MAAM,GAAGhC,SAAS;;IAE1B;IACA,MAAMiC,KAAK,GAAGzC,YAAY,CAAC,CAAC,GAAGG,IAAe,KAAKqB,IAAI,CAACkB,IAAI,CAACvC,IAAI,CAACwC,GAAG,CAACC,MAAM,CAAC,CAACC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzF,MAAMC,UAAU,GAAGzC,MAAM,CAACW,MAAM,CAAC,IAAI,CAA4B;IACjE8B,UAAU,CAACC,GAAG,GAAGN,KAAK;IACtBT,OAAO,CAACgB,OAAO,GAAG3C,MAAM,CAACM,MAAM,CAACmC,UAAU,CAAC;;IAE3C;IACAd,OAAO,CAACE,IAAI,GAAGtB,aAAa,CAACsB,IAAI,EAAwC,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;;IAEhG;IACA,MAAMe,QAAQ,GAAG,CACf,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EACpF,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EACrE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC5E,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,CAChE;IACDjB,OAAO,CAACkB,IAAI,GAAGtC,aAAa,CAACsC,IAAI,EAAwCD,QAAQ,CAAC;;IAElF;IACA;IACA,MAAME,MAAM,GAAG9C,MAAM,CAACW,MAAM,CAAC,IAAI,CAA4B;IAC7DmC,MAAM,CAACC,GAAG,GAAGpD,YAAY,CAACqD,IAAI,CAACD,GAAG,CAAC;IACnCpB,OAAO,CAACqB,IAAI,GAAGhD,MAAM,CAACM,MAAM,CAACwC,MAAM,CAAC;;IAEpC;IACAnB,OAAO,CAACsB,QAAQ,GAAGtD,YAAY,CAACsD,QAAQ,CAAC;IACzCtB,OAAO,CAACuB,UAAU,GAAGvD,YAAY,CAACuD,UAAU,CAAC;IAC7CvB,OAAO,CAACwB,KAAK,GAAGxD,YAAY,CAACwD,KAAK,CAAC;IACnCxB,OAAO,CAACyB,QAAQ,GAAGzD,YAAY,CAACyD,QAAQ,CAAC;IACzCzB,OAAO,CAAC0B,kBAAkB,GAAG1D,YAAY,CAAC0D,kBAAkB,CAAC;IAC7D1B,OAAO,CAAC2B,kBAAkB,GAAG3D,YAAY,CAAC2D,kBAAkB,CAAC;;IAE7D;IACA,MAAMC,KAAK,GAAGvD,MAAM,CAACW,MAAM,CAAC,IAAI,CAA4B;IAC5D4C,KAAK,CAACzB,KAAK,GAAGnC,YAAY,CAAEuC,KAAa,IAAK;MAC5C,IAAI;QACF,MAAMsB,CAAC,GAAG,IAAIC,GAAG,CAACvB,KAAK,CAAC;QACxB,OAAO;UAAEwB,IAAI,EAAEF,CAAC,CAACE,IAAI;UAAEC,QAAQ,EAAEH,CAAC,CAACG,QAAQ;UAAEC,IAAI,EAAEJ,CAAC,CAACI,IAAI;UAAEC,QAAQ,EAAEL,CAAC,CAACK,QAAQ;UACtEC,IAAI,EAAEN,CAAC,CAACM,IAAI;UAAEC,QAAQ,EAAEP,CAAC,CAACO,QAAQ;UAAEC,MAAM,EAAER,CAAC,CAACQ,MAAM;UAAEC,IAAI,EAAET,CAAC,CAACS,IAAI;UAClEC,MAAM,EAAEV,CAAC,CAACU,MAAM;UAAEC,YAAY,EAAEnE,MAAM,CAACoE,WAAW,CAACZ,CAAC,CAACW,YAAY;QAAE,CAAC;MAC/E,CAAC,CAAC,MAAM;QAAE,OAAO,IAAI;MAAE;IACzB,CAAC,CAAC;IACFZ,KAAK,CAACc,MAAM,GAAG1E,YAAY,CAAE2E,KAA6B,IAAK;MAC7D,IAAI;QAAE,OAAO,IAAIb,GAAG,CAAC,GAAGa,KAAK,CAACX,QAAQ,IAAI,QAAQ,KAAKW,KAAK,CAACV,IAAI,IAAIU,KAAK,CAACT,QAAQ,IAAI,EAAE,GAAGS,KAAK,CAACP,QAAQ,IAAI,EAAE,GAAGO,KAAK,CAACN,MAAM,IAAI,EAAE,EAAE,CAAC,CAACN,IAAI;MAAE,CAAC,CAChJ,MAAM;QAAE,OAAO,IAAI;MAAE;IACvB,CAAC,CAAC;IACF/B,OAAO,CAAC8B,GAAG,GAAGzD,MAAM,CAACM,MAAM,CAACiD,KAAK,CAAC;;IAElC;IACA,MAAMgB,QAAQ,GAAGvE,MAAM,CAACW,MAAM,CAAC,IAAI,CAA4B;IAC/D4D,QAAQ,CAACC,IAAI,GAAG7E,YAAY,CAAC,CAAC,GAAGG,IAAe,KAAK2E,MAAM,CAACD,IAAI,CAAC1E,IAAI,CAAC,CAAC,CAAC,EAASA,IAAI,CAAC,CAAC,CAAQ,CAAC,CAAC;IACjGyE,QAAQ,CAACG,KAAK,GAAG/E,YAAY,CAAEgF,IAAY,IAAKF,MAAM,CAACC,KAAK,CAACC,IAAI,CAAC,CAAC;IACnEhD,OAAO,CAAC8C,MAAM,GAAGzE,MAAM,CAACM,MAAM,CAACiE,QAAQ,CAAC;;IAExC;IACA,MAAMK,QAAQ,GAAG5E,MAAM,CAACW,MAAM,CAAC,IAAI,CAA4B;IAC/DiE,QAAQ,CAAClF,UAAU,GAAGC,YAAY,CAACD,UAAU,CAAC;IAC9CiC,OAAO,CAACkD,MAAM,GAAG7E,MAAM,CAACM,MAAM,CAACsE,QAAQ,CAAC;;IAExC;IACA;IACA;;IAEA;IACAjD,OAAO,CAACb,OAAO,GAAGX,SAAS;IAC3BwB,OAAO,CAACmD,OAAO,GAAG3E,SAAS;IAC3BwB,OAAO,CAACoD,MAAM,GAAG5E,SAAS;IAC1BwB,OAAO,CAACqD,UAAU,GAAG7E,SAAS;IAC9BwB,OAAO,CAACsD,QAAQ,GAAG9E,SAAS;IAC5BwB,OAAO,CAACuD,IAAI,GAAG/E,SAAS;IACxBwB,OAAO,CAACwD,UAAU,GAAGhF,SAAS;IAC9BwB,OAAO,CAACyD,WAAW,GAAGjF,SAAS;IAC/BwB,OAAO,CAAC0D,OAAO,GAAGlF,SAAS;IAC3BwB,OAAO,CAAC2D,KAAK,GAAGnF,SAAS;IACzBwB,OAAO,CAAC4D,MAAM,GAAGpF,SAAS;IAC1BwB,OAAO,CAAC6D,OAAO,GAAGrF,SAAS;IAE3B,IAAI;MACF,MAAMqB,MAAM,GAAG/B,eAAe,CAACwB,GAAG,CAACM,IAAI,EAAEI,OAAO,EAAE;QAChD8D,OAAO,EAAExE,GAAG,CAACyE,SAAS;QACtBC,QAAQ,EAAE,oBAAoB;QAC9BC,aAAa,EAAE,IAAI;QACnBC,aAAa,EAAE;MACjB,CAAC,CAAC;MAEF,MAAMC,YAA0B,GAAG;QACjC5E,IAAI,EAAE,QAAQ;QACdO,OAAO,EAAE,IAAI;QACbU,MAAM,EAAE;UAAEX,MAAM,EAAEG,OAAO,CAACQ,MAAM,IAAIX,MAAM;UAAEL;QAAK;MACnD,CAAC;MACDL,OAAO,CAACC,IAAI,CAAE+E,YAAY,CAAC;IAC7B,CAAC,CAAC,OAAOC,GAAQ,EAAE;MACjB,MAAMD,YAA0B,GAAG;QACjC5E,IAAI,EAAE,QAAQ;QACdO,OAAO,EAAE,KAAK;QACdC,KAAK,EAAEqE,GAAG,CAACxE,IAAI,KAAK,8BAA8B,GAC9C,kCAAkCN,GAAG,CAACyE,SAAS,IAAI,GACnD,eAAeK,GAAG,CAACC,OAAO,EAAE;QAChC7E;MACF,CAAC;MACDL,OAAO,CAACC,IAAI,CAAE+E,YAAY,CAAC;IAC7B;EACF,CAAC,CAAC;;EAEF;EACAhF,OAAO,CAACC,IAAI,CAAC;IAAEG,IAAI,EAAE;EAAQ,CAAC,CAAC;AACjC","ignoreList":[]}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import Anthropic from "@anthropic-ai/sdk";
|
|
12
|
-
import { sanitizeError, AGENT_DEFAULTS } from "../../shared/agent-core.js";
|
|
12
|
+
import { sanitizeError, AGENT_DEFAULTS, isRetryableError, categorizeError } from "../../shared/agent-core.js";
|
|
13
13
|
import { getCapabilities } from "../lib/provider-capabilities.js";
|
|
14
14
|
import { registerProvider } from "./registry.js";
|
|
15
15
|
import { jsonResponse, writeSSEHeaders } from "./shared.js";
|
|
@@ -34,6 +34,48 @@ const MODEL_MAX_OUTPUT_TOKENS = {
|
|
|
34
34
|
"claude-3-haiku-20240307": 4096
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// PROXY RETRY — retries overloaded/rate-limit errors before giving up
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
const PROXY_MAX_RETRIES = 4;
|
|
42
|
+
const PROXY_BASE_DELAY_MS = 3000;
|
|
43
|
+
const PROXY_MAX_DELAY_MS = 45000;
|
|
44
|
+
const PROXY_FALLBACK_AFTER = 2;
|
|
45
|
+
|
|
46
|
+
// Intra-provider model fallback for proxy (Opus → Sonnet when overloaded)
|
|
47
|
+
const PROXY_MODEL_FALLBACK = {
|
|
48
|
+
"claude-opus-4-6": "claude-sonnet-4-6",
|
|
49
|
+
"claude-opus-4-5-20251101": "claude-sonnet-4-5-20250929",
|
|
50
|
+
"claude-opus-4-20250514": "claude-sonnet-4-20250514",
|
|
51
|
+
"claude-opus-4-1-20250805": "claude-sonnet-4-5-20250929"
|
|
52
|
+
};
|
|
53
|
+
function proxyRetryDelay(attempt, err) {
|
|
54
|
+
let delay = Math.min(PROXY_BASE_DELAY_MS * Math.pow(2, attempt), PROXY_MAX_DELAY_MS);
|
|
55
|
+
const {
|
|
56
|
+
category
|
|
57
|
+
} = categorizeError(err);
|
|
58
|
+
if (category === "PROVIDER_DOWN" || category === "RATE_LIMIT") {
|
|
59
|
+
delay = Math.min(delay * 3, PROXY_MAX_DELAY_MS);
|
|
60
|
+
}
|
|
61
|
+
// Jitter ±25%
|
|
62
|
+
const jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
63
|
+
return Math.round(delay + jitter);
|
|
64
|
+
}
|
|
65
|
+
function maybeDowngradeModel(apiParams, attempt, err) {
|
|
66
|
+
const {
|
|
67
|
+
category
|
|
68
|
+
} = categorizeError(err);
|
|
69
|
+
if ((category === "PROVIDER_DOWN" || category === "RATE_LIMIT") && attempt >= PROXY_FALLBACK_AFTER) {
|
|
70
|
+
const current = apiParams.model;
|
|
71
|
+
const fallback = PROXY_MODEL_FALLBACK[current];
|
|
72
|
+
if (fallback && current !== fallback) {
|
|
73
|
+
console.warn(`[proxy] Model fallback: ${current} → ${fallback} (overloaded after ${attempt + 1} attempts)`);
|
|
74
|
+
apiParams.model = fallback;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
37
79
|
// ============================================================================
|
|
38
80
|
// ADAPTER
|
|
39
81
|
// ============================================================================
|
|
@@ -134,32 +176,67 @@ export class AnthropicAdapter {
|
|
|
134
176
|
}
|
|
135
177
|
}
|
|
136
178
|
if (!stream) {
|
|
179
|
+
for (let attempt = 0; attempt <= PROXY_MAX_RETRIES; attempt++) {
|
|
180
|
+
try {
|
|
181
|
+
const response = effectiveBetas.length ? await anthropic.beta.messages.create({
|
|
182
|
+
...apiParams,
|
|
183
|
+
stream: false
|
|
184
|
+
}) : await anthropic.messages.create({
|
|
185
|
+
...apiParams,
|
|
186
|
+
stream: false
|
|
187
|
+
});
|
|
188
|
+
jsonResponse(res, 200, response, corsHeaders);
|
|
189
|
+
return;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (attempt < PROXY_MAX_RETRIES && isRetryableError(err)) {
|
|
192
|
+
maybeDowngradeModel(apiParams, attempt, err);
|
|
193
|
+
const delay = proxyRetryDelay(attempt, err);
|
|
194
|
+
console.warn(`[proxy] Attempt ${attempt + 1}/${PROXY_MAX_RETRIES + 1} failed (model=${apiParams.model}) — retrying in ${(delay / 1000).toFixed(1)}s: ${sanitizeError(err)}`);
|
|
195
|
+
await new Promise(r => setTimeout(r, delay));
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
jsonResponse(res, 500, {
|
|
199
|
+
error: sanitizeError(err)
|
|
200
|
+
}, corsHeaders);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Streaming: retry the initial API call with backoff before committing to SSE.
|
|
208
|
+
let heartbeat;
|
|
209
|
+
let streamResponse;
|
|
210
|
+
for (let attempt = 0; attempt <= PROXY_MAX_RETRIES; attempt++) {
|
|
137
211
|
try {
|
|
138
|
-
|
|
212
|
+
streamResponse = effectiveBetas.length ? await anthropic.beta.messages.create({
|
|
139
213
|
...apiParams,
|
|
140
|
-
stream:
|
|
141
|
-
}) : await anthropic.messages.create(
|
|
142
|
-
|
|
143
|
-
stream: false
|
|
144
|
-
});
|
|
145
|
-
jsonResponse(res, 200, response, corsHeaders);
|
|
214
|
+
stream: true
|
|
215
|
+
}) : await anthropic.messages.create(apiParams);
|
|
216
|
+
break;
|
|
146
217
|
} catch (err) {
|
|
147
|
-
|
|
148
|
-
|
|
218
|
+
if (attempt < PROXY_MAX_RETRIES && isRetryableError(err)) {
|
|
219
|
+
maybeDowngradeModel(apiParams, attempt, err);
|
|
220
|
+
const delay = proxyRetryDelay(attempt, err);
|
|
221
|
+
console.warn(`[proxy] Stream attempt ${attempt + 1}/${PROXY_MAX_RETRIES + 1} failed (model=${apiParams.model}) — retrying in ${(delay / 1000).toFixed(1)}s: ${sanitizeError(err)}`);
|
|
222
|
+
await new Promise(r => setTimeout(r, delay));
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
jsonResponse(res, 502, {
|
|
226
|
+
error: `anthropic error: ${sanitizeError(err)}`
|
|
149
227
|
}, corsHeaders);
|
|
228
|
+
res.end();
|
|
229
|
+
return;
|
|
150
230
|
}
|
|
231
|
+
}
|
|
232
|
+
if (!streamResponse) {
|
|
233
|
+
jsonResponse(res, 502, {
|
|
234
|
+
error: "failed to create stream after retries"
|
|
235
|
+
}, corsHeaders);
|
|
236
|
+
res.end();
|
|
151
237
|
return;
|
|
152
238
|
}
|
|
153
|
-
|
|
154
|
-
// Streaming: defer SSE headers until stream is successfully created
|
|
155
|
-
// so we can return proper HTTP errors if the initial API call fails.
|
|
156
|
-
let heartbeat;
|
|
157
239
|
try {
|
|
158
|
-
const response = effectiveBetas.length ? await anthropic.beta.messages.create({
|
|
159
|
-
...apiParams,
|
|
160
|
-
stream: true
|
|
161
|
-
}) : await anthropic.messages.create(apiParams);
|
|
162
|
-
|
|
163
240
|
// Stream created successfully — now safe to commit to SSE
|
|
164
241
|
writeSSEHeaders(res, corsHeaders);
|
|
165
242
|
|
|
@@ -167,24 +244,17 @@ export class AnthropicAdapter {
|
|
|
167
244
|
heartbeat = setInterval(() => {
|
|
168
245
|
if (!res.writableEnded) res.write(":ping\n\n");
|
|
169
246
|
}, 15_000);
|
|
170
|
-
for await (const event of
|
|
247
|
+
for await (const event of streamResponse) {
|
|
171
248
|
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
172
249
|
}
|
|
173
250
|
res.write("data: [DONE]\n\n");
|
|
174
251
|
} catch (err) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// Mid-stream failure — send error event so client doesn't hang
|
|
182
|
-
res.write(`data: ${JSON.stringify({
|
|
183
|
-
type: "error",
|
|
184
|
-
error: sanitizeError(err)
|
|
185
|
-
})}\n\n`);
|
|
186
|
-
res.write("data: [DONE]\n\n");
|
|
187
|
-
}
|
|
252
|
+
// Mid-stream failure — send error event so client doesn't hang
|
|
253
|
+
res.write(`data: ${JSON.stringify({
|
|
254
|
+
type: "error",
|
|
255
|
+
error: sanitizeError(err)
|
|
256
|
+
})}\n\n`);
|
|
257
|
+
res.write("data: [DONE]\n\n");
|
|
188
258
|
} finally {
|
|
189
259
|
if (heartbeat) clearInterval(heartbeat);
|
|
190
260
|
}
|