@aitne/daemon 0.1.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/LICENSE +21 -0
- package/dist/adapters/composite-dashboard-stream.d.ts +42 -0
- package/dist/adapters/composite-dashboard-stream.d.ts.map +1 -0
- package/dist/adapters/composite-dashboard-stream.js +49 -0
- package/dist/adapters/composite-dashboard-stream.js.map +1 -0
- package/dist/adapters/dashboard-adapter.d.ts +104 -0
- package/dist/adapters/dashboard-adapter.d.ts.map +1 -0
- package/dist/adapters/dashboard-adapter.js +216 -0
- package/dist/adapters/dashboard-adapter.js.map +1 -0
- package/dist/adapters/discord.d.ts +77 -0
- package/dist/adapters/discord.d.ts.map +1 -0
- package/dist/adapters/discord.js +339 -0
- package/dist/adapters/discord.js.map +1 -0
- package/dist/adapters/docs-qa-adapter.d.ts +123 -0
- package/dist/adapters/docs-qa-adapter.d.ts.map +1 -0
- package/dist/adapters/docs-qa-adapter.js +218 -0
- package/dist/adapters/docs-qa-adapter.js.map +1 -0
- package/dist/adapters/message-hub.d.ts +70 -0
- package/dist/adapters/message-hub.d.ts.map +1 -0
- package/dist/adapters/message-hub.js +359 -0
- package/dist/adapters/message-hub.js.map +1 -0
- package/dist/adapters/notification-manager.d.ts +99 -0
- package/dist/adapters/notification-manager.d.ts.map +1 -0
- package/dist/adapters/notification-manager.js +498 -0
- package/dist/adapters/notification-manager.js.map +1 -0
- package/dist/adapters/outbound-text.d.ts +28 -0
- package/dist/adapters/outbound-text.d.ts.map +1 -0
- package/dist/adapters/outbound-text.js +58 -0
- package/dist/adapters/outbound-text.js.map +1 -0
- package/dist/adapters/slack-adapter.d.ts +82 -0
- package/dist/adapters/slack-adapter.d.ts.map +1 -0
- package/dist/adapters/slack-adapter.js +359 -0
- package/dist/adapters/slack-adapter.js.map +1 -0
- package/dist/adapters/telegram-adapter.d.ts +107 -0
- package/dist/adapters/telegram-adapter.d.ts.map +1 -0
- package/dist/adapters/telegram-adapter.js +477 -0
- package/dist/adapters/telegram-adapter.js.map +1 -0
- package/dist/adapters/types.d.ts +92 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/adapters/whatsapp-adapter.d.ts +213 -0
- package/dist/adapters/whatsapp-adapter.d.ts.map +1 -0
- package/dist/adapters/whatsapp-adapter.js +1216 -0
- package/dist/adapters/whatsapp-adapter.js.map +1 -0
- package/dist/api/chat-binding-query.d.ts +36 -0
- package/dist/api/chat-binding-query.d.ts.map +1 -0
- package/dist/api/chat-binding-query.js +63 -0
- package/dist/api/chat-binding-query.js.map +1 -0
- package/dist/api/chat-session-resume.d.ts +12 -0
- package/dist/api/chat-session-resume.d.ts.map +1 -0
- package/dist/api/chat-session-resume.js +21 -0
- package/dist/api/chat-session-resume.js.map +1 -0
- package/dist/api/delegated-proxy-helper.d.ts +33 -0
- package/dist/api/delegated-proxy-helper.d.ts.map +1 -0
- package/dist/api/delegated-proxy-helper.js +54 -0
- package/dist/api/delegated-proxy-helper.js.map +1 -0
- package/dist/api/directory-picker.d.ts +38 -0
- package/dist/api/directory-picker.d.ts.map +1 -0
- package/dist/api/directory-picker.js +278 -0
- package/dist/api/directory-picker.js.map +1 -0
- package/dist/api/env-writer.d.ts +25 -0
- package/dist/api/env-writer.d.ts.map +1 -0
- package/dist/api/env-writer.js +421 -0
- package/dist/api/env-writer.js.map +1 -0
- package/dist/api/integration-route-gate.d.ts +60 -0
- package/dist/api/integration-route-gate.d.ts.map +1 -0
- package/dist/api/integration-route-gate.js +83 -0
- package/dist/api/integration-route-gate.js.map +1 -0
- package/dist/api/json-body.d.ts +29 -0
- package/dist/api/json-body.d.ts.map +1 -0
- package/dist/api/json-body.js +87 -0
- package/dist/api/json-body.js.map +1 -0
- package/dist/api/routes/activity-sources.d.ts +20 -0
- package/dist/api/routes/activity-sources.d.ts.map +1 -0
- package/dist/api/routes/activity-sources.js +18 -0
- package/dist/api/routes/activity-sources.js.map +1 -0
- package/dist/api/routes/agent.d.ts +4 -0
- package/dist/api/routes/agent.d.ts.map +1 -0
- package/dist/api/routes/agent.js +619 -0
- package/dist/api/routes/agent.js.map +1 -0
- package/dist/api/routes/apple-calendar.d.ts +31 -0
- package/dist/api/routes/apple-calendar.d.ts.map +1 -0
- package/dist/api/routes/apple-calendar.js +310 -0
- package/dist/api/routes/apple-calendar.js.map +1 -0
- package/dist/api/routes/attachments.d.ts +36 -0
- package/dist/api/routes/attachments.d.ts.map +1 -0
- package/dist/api/routes/attachments.js +305 -0
- package/dist/api/routes/attachments.js.map +1 -0
- package/dist/api/routes/backends.d.ts +4 -0
- package/dist/api/routes/backends.d.ts.map +1 -0
- package/dist/api/routes/backends.js +1132 -0
- package/dist/api/routes/backends.js.map +1 -0
- package/dist/api/routes/books.d.ts +63 -0
- package/dist/api/routes/books.d.ts.map +1 -0
- package/dist/api/routes/books.js +467 -0
- package/dist/api/routes/books.js.map +1 -0
- package/dist/api/routes/calendar.d.ts +36 -0
- package/dist/api/routes/calendar.d.ts.map +1 -0
- package/dist/api/routes/calendar.js +351 -0
- package/dist/api/routes/calendar.js.map +1 -0
- package/dist/api/routes/commands.d.ts +4 -0
- package/dist/api/routes/commands.d.ts.map +1 -0
- package/dist/api/routes/commands.js +251 -0
- package/dist/api/routes/commands.js.map +1 -0
- package/dist/api/routes/context.d.ts +57 -0
- package/dist/api/routes/context.d.ts.map +1 -0
- package/dist/api/routes/context.js +1765 -0
- package/dist/api/routes/context.js.map +1 -0
- package/dist/api/routes/dashboard.d.ts +29 -0
- package/dist/api/routes/dashboard.d.ts.map +1 -0
- package/dist/api/routes/dashboard.js +2062 -0
- package/dist/api/routes/dashboard.js.map +1 -0
- package/dist/api/routes/delegated-sync.d.ts +4 -0
- package/dist/api/routes/delegated-sync.d.ts.map +1 -0
- package/dist/api/routes/delegated-sync.js +192 -0
- package/dist/api/routes/delegated-sync.js.map +1 -0
- package/dist/api/routes/delegated.d.ts +42 -0
- package/dist/api/routes/delegated.d.ts.map +1 -0
- package/dist/api/routes/delegated.js +250 -0
- package/dist/api/routes/delegated.js.map +1 -0
- package/dist/api/routes/docs.d.ts +34 -0
- package/dist/api/routes/docs.d.ts.map +1 -0
- package/dist/api/routes/docs.js +580 -0
- package/dist/api/routes/docs.js.map +1 -0
- package/dist/api/routes/entities.d.ts +9 -0
- package/dist/api/routes/entities.d.ts.map +1 -0
- package/dist/api/routes/entities.js +176 -0
- package/dist/api/routes/entities.js.map +1 -0
- package/dist/api/routes/git-accounts.d.ts +23 -0
- package/dist/api/routes/git-accounts.d.ts.map +1 -0
- package/dist/api/routes/git-accounts.js +227 -0
- package/dist/api/routes/git-accounts.js.map +1 -0
- package/dist/api/routes/git-templates.d.ts +50 -0
- package/dist/api/routes/git-templates.d.ts.map +1 -0
- package/dist/api/routes/git-templates.js +276 -0
- package/dist/api/routes/git-templates.js.map +1 -0
- package/dist/api/routes/git.d.ts +34 -0
- package/dist/api/routes/git.d.ts.map +1 -0
- package/dist/api/routes/git.js +126 -0
- package/dist/api/routes/git.js.map +1 -0
- package/dist/api/routes/github.d.ts +34 -0
- package/dist/api/routes/github.d.ts.map +1 -0
- package/dist/api/routes/github.js +465 -0
- package/dist/api/routes/github.js.map +1 -0
- package/dist/api/routes/health.d.ts +4 -0
- package/dist/api/routes/health.d.ts.map +1 -0
- package/dist/api/routes/health.js +257 -0
- package/dist/api/routes/health.js.map +1 -0
- package/dist/api/routes/integrations-reconcile.d.ts +33 -0
- package/dist/api/routes/integrations-reconcile.d.ts.map +1 -0
- package/dist/api/routes/integrations-reconcile.js +463 -0
- package/dist/api/routes/integrations-reconcile.js.map +1 -0
- package/dist/api/routes/integrations.d.ts +19 -0
- package/dist/api/routes/integrations.d.ts.map +1 -0
- package/dist/api/routes/integrations.js +1384 -0
- package/dist/api/routes/integrations.js.map +1 -0
- package/dist/api/routes/knowledge.d.ts +4 -0
- package/dist/api/routes/knowledge.d.ts.map +1 -0
- package/dist/api/routes/knowledge.js +224 -0
- package/dist/api/routes/knowledge.js.map +1 -0
- package/dist/api/routes/mail.d.ts +39 -0
- package/dist/api/routes/mail.d.ts.map +1 -0
- package/dist/api/routes/mail.js +1406 -0
- package/dist/api/routes/mail.js.map +1 -0
- package/dist/api/routes/managed-tasks.d.ts +48 -0
- package/dist/api/routes/managed-tasks.d.ts.map +1 -0
- package/dist/api/routes/managed-tasks.js +844 -0
- package/dist/api/routes/managed-tasks.js.map +1 -0
- package/dist/api/routes/mcp.d.ts +50 -0
- package/dist/api/routes/mcp.d.ts.map +1 -0
- package/dist/api/routes/mcp.js +470 -0
- package/dist/api/routes/mcp.js.map +1 -0
- package/dist/api/routes/metrics.d.ts +13 -0
- package/dist/api/routes/metrics.d.ts.map +1 -0
- package/dist/api/routes/metrics.js +117 -0
- package/dist/api/routes/metrics.js.map +1 -0
- package/dist/api/routes/notion.d.ts +35 -0
- package/dist/api/routes/notion.d.ts.map +1 -0
- package/dist/api/routes/notion.js +442 -0
- package/dist/api/routes/notion.js.map +1 -0
- package/dist/api/routes/observations.d.ts +4 -0
- package/dist/api/routes/observations.d.ts.map +1 -0
- package/dist/api/routes/observations.js +177 -0
- package/dist/api/routes/observations.js.map +1 -0
- package/dist/api/routes/obsidian.d.ts +16 -0
- package/dist/api/routes/obsidian.d.ts.map +1 -0
- package/dist/api/routes/obsidian.js +321 -0
- package/dist/api/routes/obsidian.js.map +1 -0
- package/dist/api/routes/profile-questions.d.ts +17 -0
- package/dist/api/routes/profile-questions.d.ts.map +1 -0
- package/dist/api/routes/profile-questions.js +115 -0
- package/dist/api/routes/profile-questions.js.map +1 -0
- package/dist/api/routes/receipts.d.ts +4 -0
- package/dist/api/routes/receipts.d.ts.map +1 -0
- package/dist/api/routes/receipts.js +155 -0
- package/dist/api/routes/receipts.js.map +1 -0
- package/dist/api/routes/recurring-schedules.d.ts +4 -0
- package/dist/api/routes/recurring-schedules.d.ts.map +1 -0
- package/dist/api/routes/recurring-schedules.js +137 -0
- package/dist/api/routes/recurring-schedules.js.map +1 -0
- package/dist/api/routes/repositories.d.ts +40 -0
- package/dist/api/routes/repositories.d.ts.map +1 -0
- package/dist/api/routes/repositories.js +857 -0
- package/dist/api/routes/repositories.js.map +1 -0
- package/dist/api/routes/setup-migrate.d.ts +74 -0
- package/dist/api/routes/setup-migrate.d.ts.map +1 -0
- package/dist/api/routes/setup-migrate.js +944 -0
- package/dist/api/routes/setup-migrate.js.map +1 -0
- package/dist/api/routes/setup.d.ts +4 -0
- package/dist/api/routes/setup.d.ts.map +1 -0
- package/dist/api/routes/setup.js +443 -0
- package/dist/api/routes/setup.js.map +1 -0
- package/dist/api/routes/skill-curation.d.ts +5 -0
- package/dist/api/routes/skill-curation.d.ts.map +1 -0
- package/dist/api/routes/skill-curation.js +728 -0
- package/dist/api/routes/skill-curation.js.map +1 -0
- package/dist/api/routes/skills.d.ts +52 -0
- package/dist/api/routes/skills.d.ts.map +1 -0
- package/dist/api/routes/skills.js +429 -0
- package/dist/api/routes/skills.js.map +1 -0
- package/dist/api/routes/sot-bindings.d.ts +20 -0
- package/dist/api/routes/sot-bindings.d.ts.map +1 -0
- package/dist/api/routes/sot-bindings.js +163 -0
- package/dist/api/routes/sot-bindings.js.map +1 -0
- package/dist/api/routes/sse.d.ts +86 -0
- package/dist/api/routes/sse.d.ts.map +1 -0
- package/dist/api/routes/sse.js +378 -0
- package/dist/api/routes/sse.js.map +1 -0
- package/dist/api/routes/system.d.ts +4 -0
- package/dist/api/routes/system.d.ts.map +1 -0
- package/dist/api/routes/system.js +207 -0
- package/dist/api/routes/system.js.map +1 -0
- package/dist/api/routes/task-flows.d.ts +30 -0
- package/dist/api/routes/task-flows.d.ts.map +1 -0
- package/dist/api/routes/task-flows.js +155 -0
- package/dist/api/routes/task-flows.js.map +1 -0
- package/dist/api/routes/travel-bookings.d.ts +4 -0
- package/dist/api/routes/travel-bookings.d.ts.map +1 -0
- package/dist/api/routes/travel-bookings.js +142 -0
- package/dist/api/routes/travel-bookings.js.map +1 -0
- package/dist/api/routes/travel-time.d.ts +8 -0
- package/dist/api/routes/travel-time.d.ts.map +1 -0
- package/dist/api/routes/travel-time.js +87 -0
- package/dist/api/routes/travel-time.js.map +1 -0
- package/dist/api/routes/triggers.d.ts +4 -0
- package/dist/api/routes/triggers.d.ts.map +1 -0
- package/dist/api/routes/triggers.js +101 -0
- package/dist/api/routes/triggers.js.map +1 -0
- package/dist/api/routes/voice.d.ts +48 -0
- package/dist/api/routes/voice.d.ts.map +1 -0
- package/dist/api/routes/voice.js +232 -0
- package/dist/api/routes/voice.js.map +1 -0
- package/dist/api/server.d.ts +428 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +558 -0
- package/dist/api/server.js.map +1 -0
- package/dist/config.d.ts +136 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +699 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-core.d.ts +517 -0
- package/dist/core/agent-core.d.ts.map +1 -0
- package/dist/core/agent-core.js +102 -0
- package/dist/core/agent-core.js.map +1 -0
- package/dist/core/alerts.d.ts +86 -0
- package/dist/core/alerts.d.ts.map +1 -0
- package/dist/core/alerts.js +304 -0
- package/dist/core/alerts.js.map +1 -0
- package/dist/core/atomic-write.d.ts +51 -0
- package/dist/core/atomic-write.d.ts.map +1 -0
- package/dist/core/atomic-write.js +135 -0
- package/dist/core/atomic-write.js.map +1 -0
- package/dist/core/backends/api-key-probe.d.ts +40 -0
- package/dist/core/backends/api-key-probe.d.ts.map +1 -0
- package/dist/core/backends/api-key-probe.js +116 -0
- package/dist/core/backends/api-key-probe.js.map +1 -0
- package/dist/core/backends/auth-health-monitor.d.ts +373 -0
- package/dist/core/backends/auth-health-monitor.d.ts.map +1 -0
- package/dist/core/backends/auth-health-monitor.js +950 -0
- package/dist/core/backends/auth-health-monitor.js.map +1 -0
- package/dist/core/backends/auth-recovery.d.ts +263 -0
- package/dist/core/backends/auth-recovery.d.ts.map +1 -0
- package/dist/core/backends/auth-recovery.js +1086 -0
- package/dist/core/backends/auth-recovery.js.map +1 -0
- package/dist/core/backends/auth-telemetry.d.ts +81 -0
- package/dist/core/backends/auth-telemetry.d.ts.map +1 -0
- package/dist/core/backends/auth-telemetry.js +108 -0
- package/dist/core/backends/auth-telemetry.js.map +1 -0
- package/dist/core/backends/backend-router.d.ts +272 -0
- package/dist/core/backends/backend-router.d.ts.map +1 -0
- package/dist/core/backends/backend-router.js +759 -0
- package/dist/core/backends/backend-router.js.map +1 -0
- package/dist/core/backends/claude-code-core.d.ts +299 -0
- package/dist/core/backends/claude-code-core.d.ts.map +1 -0
- package/dist/core/backends/claude-code-core.js +2541 -0
- package/dist/core/backends/claude-code-core.js.map +1 -0
- package/dist/core/backends/claude-credentials-store.d.ts +83 -0
- package/dist/core/backends/claude-credentials-store.d.ts.map +1 -0
- package/dist/core/backends/claude-credentials-store.js +243 -0
- package/dist/core/backends/claude-credentials-store.js.map +1 -0
- package/dist/core/backends/cli-utils.d.ts +95 -0
- package/dist/core/backends/cli-utils.d.ts.map +1 -0
- package/dist/core/backends/cli-utils.js +464 -0
- package/dist/core/backends/cli-utils.js.map +1 -0
- package/dist/core/backends/codex-core.d.ts +127 -0
- package/dist/core/backends/codex-core.d.ts.map +1 -0
- package/dist/core/backends/codex-core.js +1693 -0
- package/dist/core/backends/codex-core.js.map +1 -0
- package/dist/core/backends/gemini-cli-core.d.ts +367 -0
- package/dist/core/backends/gemini-cli-core.d.ts.map +1 -0
- package/dist/core/backends/gemini-cli-core.js +2331 -0
- package/dist/core/backends/gemini-cli-core.js.map +1 -0
- package/dist/core/backends/idle-watchdog.d.ts +77 -0
- package/dist/core/backends/idle-watchdog.d.ts.map +1 -0
- package/dist/core/backends/idle-watchdog.js +94 -0
- package/dist/core/backends/idle-watchdog.js.map +1 -0
- package/dist/core/backends/install-methods.d.ts +93 -0
- package/dist/core/backends/install-methods.d.ts.map +1 -0
- package/dist/core/backends/install-methods.js +267 -0
- package/dist/core/backends/install-methods.js.map +1 -0
- package/dist/core/backends/model-registry.d.ts +58 -0
- package/dist/core/backends/model-registry.d.ts.map +1 -0
- package/dist/core/backends/model-registry.js +539 -0
- package/dist/core/backends/model-registry.js.map +1 -0
- package/dist/core/backends/plan-presets.d.ts +123 -0
- package/dist/core/backends/plan-presets.d.ts.map +1 -0
- package/dist/core/backends/plan-presets.js +235 -0
- package/dist/core/backends/plan-presets.js.map +1 -0
- package/dist/core/backends/price-fetcher.d.ts +48 -0
- package/dist/core/backends/price-fetcher.d.ts.map +1 -0
- package/dist/core/backends/price-fetcher.js +248 -0
- package/dist/core/backends/price-fetcher.js.map +1 -0
- package/dist/core/backends/process-config-cascade.d.ts +68 -0
- package/dist/core/backends/process-config-cascade.d.ts.map +1 -0
- package/dist/core/backends/process-config-cascade.js +173 -0
- package/dist/core/backends/process-config-cascade.js.map +1 -0
- package/dist/core/backends/prompt-utils.d.ts +6 -0
- package/dist/core/backends/prompt-utils.d.ts.map +1 -0
- package/dist/core/backends/prompt-utils.js +80 -0
- package/dist/core/backends/prompt-utils.js.map +1 -0
- package/dist/core/backends/proxy-model-registry.d.ts +110 -0
- package/dist/core/backends/proxy-model-registry.d.ts.map +1 -0
- package/dist/core/backends/proxy-model-registry.js +195 -0
- package/dist/core/backends/proxy-model-registry.js.map +1 -0
- package/dist/core/backends/silent-api-error-detector.d.ts +31 -0
- package/dist/core/backends/silent-api-error-detector.d.ts.map +1 -0
- package/dist/core/backends/silent-api-error-detector.js +44 -0
- package/dist/core/backends/silent-api-error-detector.js.map +1 -0
- package/dist/core/bang-commands/commands-cost.d.ts +13 -0
- package/dist/core/bang-commands/commands-cost.d.ts.map +1 -0
- package/dist/core/bang-commands/commands-cost.js +91 -0
- package/dist/core/bang-commands/commands-cost.js.map +1 -0
- package/dist/core/bang-commands/commands-report.d.ts +18 -0
- package/dist/core/bang-commands/commands-report.d.ts.map +1 -0
- package/dist/core/bang-commands/commands-report.js +105 -0
- package/dist/core/bang-commands/commands-report.js.map +1 -0
- package/dist/core/bang-commands/commands-stop-start.d.ts +4 -0
- package/dist/core/bang-commands/commands-stop-start.d.ts.map +1 -0
- package/dist/core/bang-commands/commands-stop-start.js +88 -0
- package/dist/core/bang-commands/commands-stop-start.js.map +1 -0
- package/dist/core/bang-commands/format-utils.d.ts +34 -0
- package/dist/core/bang-commands/format-utils.d.ts.map +1 -0
- package/dist/core/bang-commands/format-utils.js +118 -0
- package/dist/core/bang-commands/format-utils.js.map +1 -0
- package/dist/core/bang-commands/index.d.ts +20 -0
- package/dist/core/bang-commands/index.d.ts.map +1 -0
- package/dist/core/bang-commands/index.js +31 -0
- package/dist/core/bang-commands/index.js.map +1 -0
- package/dist/core/bang-commands/registry.d.ts +72 -0
- package/dist/core/bang-commands/registry.d.ts.map +1 -0
- package/dist/core/bang-commands/registry.js +174 -0
- package/dist/core/bang-commands/registry.js.map +1 -0
- package/dist/core/bang-commands/user-commands.d.ts +86 -0
- package/dist/core/bang-commands/user-commands.d.ts.map +1 -0
- package/dist/core/bang-commands/user-commands.js +212 -0
- package/dist/core/bang-commands/user-commands.js.map +1 -0
- package/dist/core/channel-timeline.d.ts +28 -0
- package/dist/core/channel-timeline.d.ts.map +1 -0
- package/dist/core/channel-timeline.js +117 -0
- package/dist/core/channel-timeline.js.map +1 -0
- package/dist/core/character-block.d.ts +37 -0
- package/dist/core/character-block.d.ts.map +1 -0
- package/dist/core/character-block.js +162 -0
- package/dist/core/character-block.js.map +1 -0
- package/dist/core/context/activity-sources.d.ts +37 -0
- package/dist/core/context/activity-sources.d.ts.map +1 -0
- package/dist/core/context/activity-sources.js +69 -0
- package/dist/core/context/activity-sources.js.map +1 -0
- package/dist/core/context/activity-view-reconciler.d.ts +110 -0
- package/dist/core/context/activity-view-reconciler.d.ts.map +1 -0
- package/dist/core/context/activity-view-reconciler.js +252 -0
- package/dist/core/context/activity-view-reconciler.js.map +1 -0
- package/dist/core/context/activity-view-runner.d.ts +38 -0
- package/dist/core/context/activity-view-runner.d.ts.map +1 -0
- package/dist/core/context/activity-view-runner.js +402 -0
- package/dist/core/context/activity-view-runner.js.map +1 -0
- package/dist/core/context/default-schedules-reconciler.d.ts +85 -0
- package/dist/core/context/default-schedules-reconciler.d.ts.map +1 -0
- package/dist/core/context/default-schedules-reconciler.js +153 -0
- package/dist/core/context/default-schedules-reconciler.js.map +1 -0
- package/dist/core/context/default-schedules-runner.d.ts +40 -0
- package/dist/core/context/default-schedules-runner.d.ts.map +1 -0
- package/dist/core/context/default-schedules-runner.js +233 -0
- package/dist/core/context/default-schedules-runner.js.map +1 -0
- package/dist/core/context/domain-index-reconciler.d.ts +81 -0
- package/dist/core/context/domain-index-reconciler.d.ts.map +1 -0
- package/dist/core/context/domain-index-reconciler.js +199 -0
- package/dist/core/context/domain-index-reconciler.js.map +1 -0
- package/dist/core/context/domain-index-runner.d.ts +35 -0
- package/dist/core/context/domain-index-runner.d.ts.map +1 -0
- package/dist/core/context/domain-index-runner.js +223 -0
- package/dist/core/context/domain-index-runner.js.map +1 -0
- package/dist/core/context/entity-mirror.d.ts +227 -0
- package/dist/core/context/entity-mirror.d.ts.map +1 -0
- package/dist/core/context/entity-mirror.js +629 -0
- package/dist/core/context/entity-mirror.js.map +1 -0
- package/dist/core/context/entity-source-rename.d.ts +61 -0
- package/dist/core/context/entity-source-rename.d.ts.map +1 -0
- package/dist/core/context/entity-source-rename.js +237 -0
- package/dist/core/context/entity-source-rename.js.map +1 -0
- package/dist/core/context/index-reconciler.d.ts +61 -0
- package/dist/core/context/index-reconciler.d.ts.map +1 -0
- package/dist/core/context/index-reconciler.js +329 -0
- package/dist/core/context/index-reconciler.js.map +1 -0
- package/dist/core/context/policy-index-reconciler.d.ts +102 -0
- package/dist/core/context/policy-index-reconciler.d.ts.map +1 -0
- package/dist/core/context/policy-index-reconciler.js +202 -0
- package/dist/core/context/policy-index-reconciler.js.map +1 -0
- package/dist/core/context/policy-index-runner.d.ts +66 -0
- package/dist/core/context/policy-index-runner.d.ts.map +1 -0
- package/dist/core/context/policy-index-runner.js +406 -0
- package/dist/core/context/policy-index-runner.js.map +1 -0
- package/dist/core/context/reconciler-runner.d.ts +44 -0
- package/dist/core/context/reconciler-runner.d.ts.map +1 -0
- package/dist/core/context/reconciler-runner.js +273 -0
- package/dist/core/context/reconciler-runner.js.map +1 -0
- package/dist/core/context-builder.d.ts +115 -0
- package/dist/core/context-builder.d.ts.map +1 -0
- package/dist/core/context-builder.js +1148 -0
- package/dist/core/context-builder.js.map +1 -0
- package/dist/core/context-frontmatter-backfill.d.ts +33 -0
- package/dist/core/context-frontmatter-backfill.d.ts.map +1 -0
- package/dist/core/context-frontmatter-backfill.js +111 -0
- package/dist/core/context-frontmatter-backfill.js.map +1 -0
- package/dist/core/context-frontmatter.d.ts +13 -0
- package/dist/core/context-frontmatter.d.ts.map +1 -0
- package/dist/core/context-frontmatter.js +325 -0
- package/dist/core/context-frontmatter.js.map +1 -0
- package/dist/core/context-health.d.ts +51 -0
- package/dist/core/context-health.d.ts.map +1 -0
- package/dist/core/context-health.js +304 -0
- package/dist/core/context-health.js.map +1 -0
- package/dist/core/context-paths.d.ts +183 -0
- package/dist/core/context-paths.d.ts.map +1 -0
- package/dist/core/context-paths.js +241 -0
- package/dist/core/context-paths.js.map +1 -0
- package/dist/core/context-staleness.d.ts +45 -0
- package/dist/core/context-staleness.d.ts.map +1 -0
- package/dist/core/context-staleness.js +88 -0
- package/dist/core/context-staleness.js.map +1 -0
- package/dist/core/custom-routine-scheduler.d.ts +151 -0
- package/dist/core/custom-routine-scheduler.d.ts.map +1 -0
- package/dist/core/custom-routine-scheduler.js +335 -0
- package/dist/core/custom-routine-scheduler.js.map +1 -0
- package/dist/core/daemon-api-cli.d.ts +33 -0
- package/dist/core/daemon-api-cli.d.ts.map +1 -0
- package/dist/core/daemon-api-cli.js +614 -0
- package/dist/core/daemon-api-cli.js.map +1 -0
- package/dist/core/dashboard-session-cleanup.d.ts +39 -0
- package/dist/core/dashboard-session-cleanup.d.ts.map +1 -0
- package/dist/core/dashboard-session-cleanup.js +108 -0
- package/dist/core/dashboard-session-cleanup.js.map +1 -0
- package/dist/core/dashboard-session-controls.d.ts +41 -0
- package/dist/core/dashboard-session-controls.d.ts.map +1 -0
- package/dist/core/dashboard-session-controls.js +154 -0
- package/dist/core/dashboard-session-controls.js.map +1 -0
- package/dist/core/delegated-connector-health.d.ts +63 -0
- package/dist/core/delegated-connector-health.d.ts.map +1 -0
- package/dist/core/delegated-connector-health.js +157 -0
- package/dist/core/delegated-connector-health.js.map +1 -0
- package/dist/core/dispatcher.d.ts +999 -0
- package/dist/core/dispatcher.d.ts.map +1 -0
- package/dist/core/dispatcher.js +4378 -0
- package/dist/core/dispatcher.js.map +1 -0
- package/dist/core/dm-freshness-metrics.d.ts +73 -0
- package/dist/core/dm-freshness-metrics.d.ts.map +1 -0
- package/dist/core/dm-freshness-metrics.js +138 -0
- package/dist/core/dm-freshness-metrics.js.map +1 -0
- package/dist/core/docs/citation-validator.d.ts +73 -0
- package/dist/core/docs/citation-validator.d.ts.map +1 -0
- package/dist/core/docs/citation-validator.js +195 -0
- package/dist/core/docs/citation-validator.js.map +1 -0
- package/dist/core/docs/extract-terms.d.ts +78 -0
- package/dist/core/docs/extract-terms.d.ts.map +1 -0
- package/dist/core/docs/extract-terms.js +147 -0
- package/dist/core/docs/extract-terms.js.map +1 -0
- package/dist/core/docs/indexer.d.ts +104 -0
- package/dist/core/docs/indexer.d.ts.map +1 -0
- package/dist/core/docs/indexer.js +340 -0
- package/dist/core/docs/indexer.js.map +1 -0
- package/dist/core/drift-effects.d.ts +30 -0
- package/dist/core/drift-effects.d.ts.map +1 -0
- package/dist/core/drift-effects.js +384 -0
- package/dist/core/drift-effects.js.map +1 -0
- package/dist/core/event-bus.d.ts +56 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +135 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/git-project-docs.d.ts +77 -0
- package/dist/core/git-project-docs.d.ts.map +1 -0
- package/dist/core/git-project-docs.js +439 -0
- package/dist/core/git-project-docs.js.map +1 -0
- package/dist/core/health-monitor.d.ts +57 -0
- package/dist/core/health-monitor.d.ts.map +1 -0
- package/dist/core/health-monitor.js +137 -0
- package/dist/core/health-monitor.js.map +1 -0
- package/dist/core/heartbeat.d.ts +26 -0
- package/dist/core/heartbeat.d.ts.map +1 -0
- package/dist/core/heartbeat.js +48 -0
- package/dist/core/heartbeat.js.map +1 -0
- package/dist/core/integration-health.d.ts +49 -0
- package/dist/core/integration-health.d.ts.map +1 -0
- package/dist/core/integration-health.js +89 -0
- package/dist/core/integration-health.js.map +1 -0
- package/dist/core/integration-lifecycle.d.ts +79 -0
- package/dist/core/integration-lifecycle.d.ts.map +1 -0
- package/dist/core/integration-lifecycle.js +153 -0
- package/dist/core/integration-lifecycle.js.map +1 -0
- package/dist/core/integration-main-backend.d.ts +36 -0
- package/dist/core/integration-main-backend.d.ts.map +1 -0
- package/dist/core/integration-main-backend.js +59 -0
- package/dist/core/integration-main-backend.js.map +1 -0
- package/dist/core/integration-probe.d.ts +98 -0
- package/dist/core/integration-probe.d.ts.map +1 -0
- package/dist/core/integration-probe.js +152 -0
- package/dist/core/integration-probe.js.map +1 -0
- package/dist/core/management-md-write-lock.d.ts +68 -0
- package/dist/core/management-md-write-lock.d.ts.map +1 -0
- package/dist/core/management-md-write-lock.js +93 -0
- package/dist/core/management-md-write-lock.js.map +1 -0
- package/dist/core/management-md.d.ts +186 -0
- package/dist/core/management-md.d.ts.map +1 -0
- package/dist/core/management-md.js +652 -0
- package/dist/core/management-md.js.map +1 -0
- package/dist/core/management-registry.d.ts +245 -0
- package/dist/core/management-registry.d.ts.map +1 -0
- package/dist/core/management-registry.js +906 -0
- package/dist/core/management-registry.js.map +1 -0
- package/dist/core/management-telemetry.d.ts +100 -0
- package/dist/core/management-telemetry.d.ts.map +1 -0
- package/dist/core/management-telemetry.js +156 -0
- package/dist/core/management-telemetry.js.map +1 -0
- package/dist/core/message-recorder.d.ts +38 -0
- package/dist/core/message-recorder.d.ts.map +1 -0
- package/dist/core/message-recorder.js +88 -0
- package/dist/core/message-recorder.js.map +1 -0
- package/dist/core/metrics.d.ts +338 -0
- package/dist/core/metrics.d.ts.map +1 -0
- package/dist/core/metrics.js +747 -0
- package/dist/core/metrics.js.map +1 -0
- package/dist/core/migration-backup.d.ts +218 -0
- package/dist/core/migration-backup.d.ts.map +1 -0
- package/dist/core/migration-backup.js +934 -0
- package/dist/core/migration-backup.js.map +1 -0
- package/dist/core/overview-write-lock.d.ts +48 -0
- package/dist/core/overview-write-lock.d.ts.map +1 -0
- package/dist/core/overview-write-lock.js +56 -0
- package/dist/core/overview-write-lock.js.map +1 -0
- package/dist/core/path-compat.d.ts +22 -0
- package/dist/core/path-compat.d.ts.map +1 -0
- package/dist/core/path-compat.js +67 -0
- package/dist/core/path-compat.js.map +1 -0
- package/dist/core/path-rewrite.d.ts +58 -0
- package/dist/core/path-rewrite.d.ts.map +1 -0
- package/dist/core/path-rewrite.js +141 -0
- package/dist/core/path-rewrite.js.map +1 -0
- package/dist/core/policy-files.d.ts +108 -0
- package/dist/core/policy-files.d.ts.map +1 -0
- package/dist/core/policy-files.js +198 -0
- package/dist/core/policy-files.js.map +1 -0
- package/dist/core/profile-questions/seed.d.ts +44 -0
- package/dist/core/profile-questions/seed.d.ts.map +1 -0
- package/dist/core/profile-questions/seed.js +173 -0
- package/dist/core/profile-questions/seed.js.map +1 -0
- package/dist/core/profile-questions/slot-filled.d.ts +51 -0
- package/dist/core/profile-questions/slot-filled.d.ts.map +1 -0
- package/dist/core/profile-questions/slot-filled.js +118 -0
- package/dist/core/profile-questions/slot-filled.js.map +1 -0
- package/dist/core/prompts.d.ts +111 -0
- package/dist/core/prompts.d.ts.map +1 -0
- package/dist/core/prompts.js +267 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/quiet-hours-sync.d.ts +15 -0
- package/dist/core/quiet-hours-sync.d.ts.map +1 -0
- package/dist/core/quiet-hours-sync.js +51 -0
- package/dist/core/quiet-hours-sync.js.map +1 -0
- package/dist/core/read-sensitive-token-manager.d.ts +19 -0
- package/dist/core/read-sensitive-token-manager.d.ts.map +1 -0
- package/dist/core/read-sensitive-token-manager.js +29 -0
- package/dist/core/read-sensitive-token-manager.js.map +1 -0
- package/dist/core/recurrence.d.ts +24 -0
- package/dist/core/recurrence.d.ts.map +1 -0
- package/dist/core/recurrence.js +162 -0
- package/dist/core/recurrence.js.map +1 -0
- package/dist/core/reinstall.d.ts +107 -0
- package/dist/core/reinstall.d.ts.map +1 -0
- package/dist/core/reinstall.js +163 -0
- package/dist/core/reinstall.js.map +1 -0
- package/dist/core/release-assets.d.ts +106 -0
- package/dist/core/release-assets.d.ts.map +1 -0
- package/dist/core/release-assets.js +434 -0
- package/dist/core/release-assets.js.map +1 -0
- package/dist/core/repository-management-docs.d.ts +216 -0
- package/dist/core/repository-management-docs.d.ts.map +1 -0
- package/dist/core/repository-management-docs.js +855 -0
- package/dist/core/repository-management-docs.js.map +1 -0
- package/dist/core/retention.d.ts +164 -0
- package/dist/core/retention.d.ts.map +1 -0
- package/dist/core/retention.js +1008 -0
- package/dist/core/retention.js.map +1 -0
- package/dist/core/review-context.d.ts +48 -0
- package/dist/core/review-context.d.ts.map +1 -0
- package/dist/core/review-context.js +282 -0
- package/dist/core/review-context.js.map +1 -0
- package/dist/core/roadmap-horizon.d.ts +48 -0
- package/dist/core/roadmap-horizon.d.ts.map +1 -0
- package/dist/core/roadmap-horizon.js +213 -0
- package/dist/core/roadmap-horizon.js.map +1 -0
- package/dist/core/roadmap-ids.d.ts +57 -0
- package/dist/core/roadmap-ids.d.ts.map +1 -0
- package/dist/core/roadmap-ids.js +118 -0
- package/dist/core/roadmap-ids.js.map +1 -0
- package/dist/core/roadmap-merge.d.ts +7 -0
- package/dist/core/roadmap-merge.d.ts.map +1 -0
- package/dist/core/roadmap-merge.js +187 -0
- package/dist/core/roadmap-merge.js.map +1 -0
- package/dist/core/roadmap-refresh-triggers.d.ts +32 -0
- package/dist/core/roadmap-refresh-triggers.d.ts.map +1 -0
- package/dist/core/roadmap-refresh-triggers.js +51 -0
- package/dist/core/roadmap-refresh-triggers.js.map +1 -0
- package/dist/core/roadmap-truncate.d.ts +49 -0
- package/dist/core/roadmap-truncate.d.ts.map +1 -0
- package/dist/core/roadmap-truncate.js +152 -0
- package/dist/core/roadmap-truncate.js.map +1 -0
- package/dist/core/roadmap-validate.d.ts +31 -0
- package/dist/core/roadmap-validate.d.ts.map +1 -0
- package/dist/core/roadmap-validate.js +403 -0
- package/dist/core/roadmap-validate.js.map +1 -0
- package/dist/core/roadmap-write-lock.d.ts +53 -0
- package/dist/core/roadmap-write-lock.d.ts.map +1 -0
- package/dist/core/roadmap-write-lock.js +59 -0
- package/dist/core/roadmap-write-lock.js.map +1 -0
- package/dist/core/schedule-insert-helper.d.ts +46 -0
- package/dist/core/schedule-insert-helper.d.ts.map +1 -0
- package/dist/core/schedule-insert-helper.js +52 -0
- package/dist/core/schedule-insert-helper.js.map +1 -0
- package/dist/core/schedule-maintenance.d.ts +22 -0
- package/dist/core/schedule-maintenance.d.ts.map +1 -0
- package/dist/core/schedule-maintenance.js +57 -0
- package/dist/core/schedule-maintenance.js.map +1 -0
- package/dist/core/scheduler.d.ts +208 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +896 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/semaphore.d.ts +13 -0
- package/dist/core/semaphore.d.ts.map +1 -0
- package/dist/core/semaphore.js +31 -0
- package/dist/core/semaphore.js.map +1 -0
- package/dist/core/session-gate.d.ts +37 -0
- package/dist/core/session-gate.d.ts.map +1 -0
- package/dist/core/session-gate.js +69 -0
- package/dist/core/session-gate.js.map +1 -0
- package/dist/core/session-manager.d.ts +252 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +716 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/signal-detector.d.ts +97 -0
- package/dist/core/signal-detector.d.ts.map +1 -0
- package/dist/core/signal-detector.js +215 -0
- package/dist/core/signal-detector.js.map +1 -0
- package/dist/core/skeleton.d.ts +83 -0
- package/dist/core/skeleton.d.ts.map +1 -0
- package/dist/core/skeleton.js +255 -0
- package/dist/core/skeleton.js.map +1 -0
- package/dist/core/skill-curation/apply-proposal.d.ts +71 -0
- package/dist/core/skill-curation/apply-proposal.d.ts.map +1 -0
- package/dist/core/skill-curation/apply-proposal.js +175 -0
- package/dist/core/skill-curation/apply-proposal.js.map +1 -0
- package/dist/core/skill-curation/auto-revert.d.ts +43 -0
- package/dist/core/skill-curation/auto-revert.d.ts.map +1 -0
- package/dist/core/skill-curation/auto-revert.js +155 -0
- package/dist/core/skill-curation/auto-revert.js.map +1 -0
- package/dist/core/skill-curation/classify-diff.d.ts +27 -0
- package/dist/core/skill-curation/classify-diff.d.ts.map +1 -0
- package/dist/core/skill-curation/classify-diff.js +0 -0
- package/dist/core/skill-curation/classify-diff.js.map +1 -0
- package/dist/core/skill-curation/declarations.d.ts +32 -0
- package/dist/core/skill-curation/declarations.d.ts.map +1 -0
- package/dist/core/skill-curation/declarations.js +171 -0
- package/dist/core/skill-curation/declarations.js.map +1 -0
- package/dist/core/skill-curation/knowledge-map.d.ts +26 -0
- package/dist/core/skill-curation/knowledge-map.d.ts.map +1 -0
- package/dist/core/skill-curation/knowledge-map.js +154 -0
- package/dist/core/skill-curation/knowledge-map.js.map +1 -0
- package/dist/core/skill-curation/orphan-overlay.d.ts +35 -0
- package/dist/core/skill-curation/orphan-overlay.d.ts.map +1 -0
- package/dist/core/skill-curation/orphan-overlay.js +167 -0
- package/dist/core/skill-curation/orphan-overlay.js.map +1 -0
- package/dist/core/skill-curation/overlay-store.d.ts +41 -0
- package/dist/core/skill-curation/overlay-store.d.ts.map +1 -0
- package/dist/core/skill-curation/overlay-store.js +143 -0
- package/dist/core/skill-curation/overlay-store.js.map +1 -0
- package/dist/core/skill-curation/render/convention-notes.d.ts +4 -0
- package/dist/core/skill-curation/render/convention-notes.d.ts.map +1 -0
- package/dist/core/skill-curation/render/convention-notes.js +13 -0
- package/dist/core/skill-curation/render/convention-notes.js.map +1 -0
- package/dist/core/skill-curation/render/cross-references.d.ts +4 -0
- package/dist/core/skill-curation/render/cross-references.d.ts.map +1 -0
- package/dist/core/skill-curation/render/cross-references.js +10 -0
- package/dist/core/skill-curation/render/cross-references.js.map +1 -0
- package/dist/core/skill-curation/render/frontmatter-schema.d.ts +4 -0
- package/dist/core/skill-curation/render/frontmatter-schema.d.ts.map +1 -0
- package/dist/core/skill-curation/render/frontmatter-schema.js +25 -0
- package/dist/core/skill-curation/render/frontmatter-schema.js.map +1 -0
- package/dist/core/skill-curation/render/index.d.ts +5 -0
- package/dist/core/skill-curation/render/index.d.ts.map +1 -0
- package/dist/core/skill-curation/render/index.js +42 -0
- package/dist/core/skill-curation/render/index.js.map +1 -0
- package/dist/core/skill-curation/render/knowledge-layout.d.ts +4 -0
- package/dist/core/skill-curation/render/knowledge-layout.d.ts.map +1 -0
- package/dist/core/skill-curation/render/knowledge-layout.js +36 -0
- package/dist/core/skill-curation/render/knowledge-layout.js.map +1 -0
- package/dist/core/skill-curation/render/routing-table.d.ts +4 -0
- package/dist/core/skill-curation/render/routing-table.d.ts.map +1 -0
- package/dist/core/skill-curation/render/routing-table.js +37 -0
- package/dist/core/skill-curation/render/routing-table.js.map +1 -0
- package/dist/core/skill-curation/render/search-recipes.d.ts +4 -0
- package/dist/core/skill-curation/render/search-recipes.d.ts.map +1 -0
- package/dist/core/skill-curation/render/search-recipes.js +39 -0
- package/dist/core/skill-curation/render/search-recipes.js.map +1 -0
- package/dist/core/skill-curation/run-token.d.ts +27 -0
- package/dist/core/skill-curation/run-token.d.ts.map +1 -0
- package/dist/core/skill-curation/run-token.js +81 -0
- package/dist/core/skill-curation/run-token.js.map +1 -0
- package/dist/core/skill-curation/signals.d.ts +49 -0
- package/dist/core/skill-curation/signals.d.ts.map +1 -0
- package/dist/core/skill-curation/signals.js +149 -0
- package/dist/core/skill-curation/signals.js.map +1 -0
- package/dist/core/skill-curation/smoke-test.d.ts +39 -0
- package/dist/core/skill-curation/smoke-test.d.ts.map +1 -0
- package/dist/core/skill-curation/smoke-test.js +313 -0
- package/dist/core/skill-curation/smoke-test.js.map +1 -0
- package/dist/core/skill-curation/splicer.d.ts +16 -0
- package/dist/core/skill-curation/splicer.d.ts.map +1 -0
- package/dist/core/skill-curation/splicer.js +78 -0
- package/dist/core/skill-curation/splicer.js.map +1 -0
- package/dist/core/skill-curation/workdir.d.ts +40 -0
- package/dist/core/skill-curation/workdir.d.ts.map +1 -0
- package/dist/core/skill-curation/workdir.js +242 -0
- package/dist/core/skill-curation/workdir.js.map +1 -0
- package/dist/core/skills-compiler.d.ts +391 -0
- package/dist/core/skills-compiler.d.ts.map +1 -0
- package/dist/core/skills-compiler.js +1271 -0
- package/dist/core/skills-compiler.js.map +1 -0
- package/dist/core/skills-manifest.d.ts +8 -0
- package/dist/core/skills-manifest.d.ts.map +1 -0
- package/dist/core/skills-manifest.js +408 -0
- package/dist/core/skills-manifest.js.map +1 -0
- package/dist/core/system-reset.d.ts +268 -0
- package/dist/core/system-reset.d.ts.map +1 -0
- package/dist/core/system-reset.js +816 -0
- package/dist/core/system-reset.js.map +1 -0
- package/dist/core/template-store.d.ts +170 -0
- package/dist/core/template-store.d.ts.map +1 -0
- package/dist/core/template-store.js +388 -0
- package/dist/core/template-store.js.map +1 -0
- package/dist/core/template-versions.d.ts +95 -0
- package/dist/core/template-versions.d.ts.map +1 -0
- package/dist/core/template-versions.js +175 -0
- package/dist/core/template-versions.js.map +1 -0
- package/dist/core/today-agent-plan.d.ts +33 -0
- package/dist/core/today-agent-plan.d.ts.map +1 -0
- package/dist/core/today-agent-plan.js +120 -0
- package/dist/core/today-agent-plan.js.map +1 -0
- package/dist/core/today-direct-writer.d.ts +62 -0
- package/dist/core/today-direct-writer.d.ts.map +1 -0
- package/dist/core/today-direct-writer.js +132 -0
- package/dist/core/today-direct-writer.js.map +1 -0
- package/dist/core/today-write-lock.d.ts +89 -0
- package/dist/core/today-write-lock.d.ts.map +1 -0
- package/dist/core/today-write-lock.js +154 -0
- package/dist/core/today-write-lock.js.map +1 -0
- package/dist/core/trigger-dispatch.d.ts +31 -0
- package/dist/core/trigger-dispatch.d.ts.map +1 -0
- package/dist/core/trigger-dispatch.js +100 -0
- package/dist/core/trigger-dispatch.js.map +1 -0
- package/dist/core/trigger-evaluator.d.ts +59 -0
- package/dist/core/trigger-evaluator.d.ts.map +1 -0
- package/dist/core/trigger-evaluator.js +243 -0
- package/dist/core/trigger-evaluator.js.map +1 -0
- package/dist/core/workdir.d.ts +241 -0
- package/dist/core/workdir.d.ts.map +1 -0
- package/dist/core/workdir.js +565 -0
- package/dist/core/workdir.js.map +1 -0
- package/dist/db/automation-triggers.d.ts +90 -0
- package/dist/db/automation-triggers.d.ts.map +1 -0
- package/dist/db/automation-triggers.js +199 -0
- package/dist/db/automation-triggers.js.map +1 -0
- package/dist/db/client.d.ts +6 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +47 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/entities-store.d.ts +92 -0
- package/dist/db/entities-store.d.ts.map +1 -0
- package/dist/db/entities-store.js +180 -0
- package/dist/db/entities-store.js.map +1 -0
- package/dist/db/hourly-check-signals.d.ts +78 -0
- package/dist/db/hourly-check-signals.d.ts.map +1 -0
- package/dist/db/hourly-check-signals.js +289 -0
- package/dist/db/hourly-check-signals.js.map +1 -0
- package/dist/db/integration-probe-store.d.ts +27 -0
- package/dist/db/integration-probe-store.d.ts.map +1 -0
- package/dist/db/integration-probe-store.js +75 -0
- package/dist/db/integration-probe-store.js.map +1 -0
- package/dist/db/integrations-store.d.ts +19 -0
- package/dist/db/integrations-store.d.ts.map +1 -0
- package/dist/db/integrations-store.js +85 -0
- package/dist/db/integrations-store.js.map +1 -0
- package/dist/db/managed-tasks-store.d.ts +130 -0
- package/dist/db/managed-tasks-store.d.ts.map +1 -0
- package/dist/db/managed-tasks-store.js +238 -0
- package/dist/db/managed-tasks-store.js.map +1 -0
- package/dist/db/management-parse-failures-store.d.ts +45 -0
- package/dist/db/management-parse-failures-store.d.ts.map +1 -0
- package/dist/db/management-parse-failures-store.js +36 -0
- package/dist/db/management-parse-failures-store.js.map +1 -0
- package/dist/db/observations.d.ts +145 -0
- package/dist/db/observations.d.ts.map +1 -0
- package/dist/db/observations.js +287 -0
- package/dist/db/observations.js.map +1 -0
- package/dist/db/recurring-schedules.d.ts +70 -0
- package/dist/db/recurring-schedules.d.ts.map +1 -0
- package/dist/db/recurring-schedules.js +213 -0
- package/dist/db/recurring-schedules.js.map +1 -0
- package/dist/db/repositories-store.d.ts +296 -0
- package/dist/db/repositories-store.d.ts.map +1 -0
- package/dist/db/repositories-store.js +754 -0
- package/dist/db/repositories-store.js.map +1 -0
- package/dist/db/runtime-state.d.ts +61 -0
- package/dist/db/runtime-state.d.ts.map +1 -0
- package/dist/db/runtime-state.js +104 -0
- package/dist/db/runtime-state.js.map +1 -0
- package/dist/db/schema.d.ts +4 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +1338 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/sot-bindings-store.d.ts +41 -0
- package/dist/db/sot-bindings-store.d.ts.map +1 -0
- package/dist/db/sot-bindings-store.js +64 -0
- package/dist/db/sot-bindings-store.js.map +1 -0
- package/dist/db/test-schemas.d.ts +23 -0
- package/dist/db/test-schemas.d.ts.map +1 -0
- package/dist/db/test-schemas.js +111 -0
- package/dist/db/test-schemas.js.map +1 -0
- package/dist/db/voice-transcripts-store.d.ts +28 -0
- package/dist/db/voice-transcripts-store.d.ts.map +1 -0
- package/dist/db/voice-transcripts-store.js +43 -0
- package/dist/db/voice-transcripts-store.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2913 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +7 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +32 -0
- package/dist/init.js.map +1 -0
- package/dist/log-buffer.d.ts +71 -0
- package/dist/log-buffer.d.ts.map +1 -0
- package/dist/log-buffer.js +201 -0
- package/dist/log-buffer.js.map +1 -0
- package/dist/logging.d.ts +5 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +130 -0
- package/dist/logging.js.map +1 -0
- package/dist/management-rules.d.ts +2 -0
- package/dist/management-rules.d.ts.map +1 -0
- package/dist/management-rules.js +62 -0
- package/dist/management-rules.js.map +1 -0
- package/dist/messaging/constants.d.ts +33 -0
- package/dist/messaging/constants.d.ts.map +1 -0
- package/dist/messaging/constants.js +52 -0
- package/dist/messaging/constants.js.map +1 -0
- package/dist/messaging/magic-phrase.d.ts +16 -0
- package/dist/messaging/magic-phrase.d.ts.map +1 -0
- package/dist/messaging/magic-phrase.js +103 -0
- package/dist/messaging/magic-phrase.js.map +1 -0
- package/dist/messaging/owner-channels.d.ts +20 -0
- package/dist/messaging/owner-channels.d.ts.map +1 -0
- package/dist/messaging/owner-channels.js +41 -0
- package/dist/messaging/owner-channels.js.map +1 -0
- package/dist/observers/calendar-poller.d.ts +51 -0
- package/dist/observers/calendar-poller.d.ts.map +1 -0
- package/dist/observers/calendar-poller.js +128 -0
- package/dist/observers/calendar-poller.js.map +1 -0
- package/dist/observers/context-index-reconciler-observer.d.ts +72 -0
- package/dist/observers/context-index-reconciler-observer.d.ts.map +1 -0
- package/dist/observers/context-index-reconciler-observer.js +253 -0
- package/dist/observers/context-index-reconciler-observer.js.map +1 -0
- package/dist/observers/delegated-probe-observer.d.ts +83 -0
- package/dist/observers/delegated-probe-observer.d.ts.map +1 -0
- package/dist/observers/delegated-probe-observer.js +237 -0
- package/dist/observers/delegated-probe-observer.js.map +1 -0
- package/dist/observers/delegated-sync-worker.d.ts +375 -0
- package/dist/observers/delegated-sync-worker.d.ts.map +1 -0
- package/dist/observers/delegated-sync-worker.js +1087 -0
- package/dist/observers/delegated-sync-worker.js.map +1 -0
- package/dist/observers/entity-mirror-observer.d.ts +55 -0
- package/dist/observers/entity-mirror-observer.d.ts.map +1 -0
- package/dist/observers/entity-mirror-observer.js +73 -0
- package/dist/observers/entity-mirror-observer.js.map +1 -0
- package/dist/observers/git-delegated-cron.d.ts +41 -0
- package/dist/observers/git-delegated-cron.d.ts.map +1 -0
- package/dist/observers/git-delegated-cron.js +159 -0
- package/dist/observers/git-delegated-cron.js.map +1 -0
- package/dist/observers/git-event-classifier.d.ts +52 -0
- package/dist/observers/git-event-classifier.d.ts.map +1 -0
- package/dist/observers/git-event-classifier.js +70 -0
- package/dist/observers/git-event-classifier.js.map +1 -0
- package/dist/observers/git-watcher.d.ts +162 -0
- package/dist/observers/git-watcher.d.ts.map +1 -0
- package/dist/observers/git-watcher.js +768 -0
- package/dist/observers/git-watcher.js.map +1 -0
- package/dist/observers/github-poller-classifier.d.ts +101 -0
- package/dist/observers/github-poller-classifier.d.ts.map +1 -0
- package/dist/observers/github-poller-classifier.js +199 -0
- package/dist/observers/github-poller-classifier.js.map +1 -0
- package/dist/observers/github-poller.d.ts +291 -0
- package/dist/observers/github-poller.d.ts.map +1 -0
- package/dist/observers/github-poller.js +609 -0
- package/dist/observers/github-poller.js.map +1 -0
- package/dist/observers/imminent-event-scheduler.d.ts +34 -0
- package/dist/observers/imminent-event-scheduler.d.ts.map +1 -0
- package/dist/observers/imminent-event-scheduler.js +125 -0
- package/dist/observers/imminent-event-scheduler.js.map +1 -0
- package/dist/observers/mail-poller.d.ts +133 -0
- package/dist/observers/mail-poller.d.ts.map +1 -0
- package/dist/observers/mail-poller.js +563 -0
- package/dist/observers/mail-poller.js.map +1 -0
- package/dist/observers/mail-reconciliation.d.ts +87 -0
- package/dist/observers/mail-reconciliation.d.ts.map +1 -0
- package/dist/observers/mail-reconciliation.js +241 -0
- package/dist/observers/mail-reconciliation.js.map +1 -0
- package/dist/observers/manager.d.ts +67 -0
- package/dist/observers/manager.d.ts.map +1 -0
- package/dist/observers/manager.js +136 -0
- package/dist/observers/manager.js.map +1 -0
- package/dist/observers/notion-poller.d.ts +43 -0
- package/dist/observers/notion-poller.d.ts.map +1 -0
- package/dist/observers/notion-poller.js +184 -0
- package/dist/observers/notion-poller.js.map +1 -0
- package/dist/observers/observation-summarizer/index.d.ts +13 -0
- package/dist/observers/observation-summarizer/index.d.ts.map +1 -0
- package/dist/observers/observation-summarizer/index.js +13 -0
- package/dist/observers/observation-summarizer/index.js.map +1 -0
- package/dist/observers/observation-summarizer/pre-filter.d.ts +62 -0
- package/dist/observers/observation-summarizer/pre-filter.d.ts.map +1 -0
- package/dist/observers/observation-summarizer/pre-filter.js +189 -0
- package/dist/observers/observation-summarizer/pre-filter.js.map +1 -0
- package/dist/observers/observation-summarizer/response-parser.d.ts +30 -0
- package/dist/observers/observation-summarizer/response-parser.d.ts.map +1 -0
- package/dist/observers/observation-summarizer/response-parser.js +106 -0
- package/dist/observers/observation-summarizer/response-parser.js.map +1 -0
- package/dist/observers/observation-summarizer/summarizer-client.d.ts +83 -0
- package/dist/observers/observation-summarizer/summarizer-client.d.ts.map +1 -0
- package/dist/observers/observation-summarizer/summarizer-client.js +185 -0
- package/dist/observers/observation-summarizer/summarizer-client.js.map +1 -0
- package/dist/observers/observation-summarizer/summarizer-prompts.d.ts +51 -0
- package/dist/observers/observation-summarizer/summarizer-prompts.d.ts.map +1 -0
- package/dist/observers/observation-summarizer/summarizer-prompts.js +286 -0
- package/dist/observers/observation-summarizer/summarizer-prompts.js.map +1 -0
- package/dist/observers/observation-summarizer/worker.d.ts +106 -0
- package/dist/observers/observation-summarizer/worker.d.ts.map +1 -0
- package/dist/observers/observation-summarizer/worker.js +311 -0
- package/dist/observers/observation-summarizer/worker.js.map +1 -0
- package/dist/observers/obsidian-watcher.d.ts +90 -0
- package/dist/observers/obsidian-watcher.d.ts.map +1 -0
- package/dist/observers/obsidian-watcher.js +166 -0
- package/dist/observers/obsidian-watcher.js.map +1 -0
- package/dist/observers/primary-vault-watcher.d.ts +73 -0
- package/dist/observers/primary-vault-watcher.d.ts.map +1 -0
- package/dist/observers/primary-vault-watcher.js +115 -0
- package/dist/observers/primary-vault-watcher.js.map +1 -0
- package/dist/observers/repository-management-cron.d.ts +70 -0
- package/dist/observers/repository-management-cron.d.ts.map +1 -0
- package/dist/observers/repository-management-cron.js +166 -0
- package/dist/observers/repository-management-cron.js.map +1 -0
- package/dist/observers/skill-curation-walker.d.ts +33 -0
- package/dist/observers/skill-curation-walker.d.ts.map +1 -0
- package/dist/observers/skill-curation-walker.js +216 -0
- package/dist/observers/skill-curation-walker.js.map +1 -0
- package/dist/safety/absolute-block-audit.d.ts +22 -0
- package/dist/safety/absolute-block-audit.d.ts.map +1 -0
- package/dist/safety/absolute-block-audit.js +32 -0
- package/dist/safety/absolute-block-audit.js.map +1 -0
- package/dist/safety/agent-write-tracker.d.ts +42 -0
- package/dist/safety/agent-write-tracker.d.ts.map +1 -0
- package/dist/safety/agent-write-tracker.js +82 -0
- package/dist/safety/agent-write-tracker.js.map +1 -0
- package/dist/safety/always-disallowed.d.ts +66 -0
- package/dist/safety/always-disallowed.d.ts.map +1 -0
- package/dist/safety/always-disallowed.js +347 -0
- package/dist/safety/always-disallowed.js.map +1 -0
- package/dist/safety/audit.d.ts +118 -0
- package/dist/safety/audit.d.ts.map +1 -0
- package/dist/safety/audit.js +324 -0
- package/dist/safety/audit.js.map +1 -0
- package/dist/safety/integration-write-tracker.d.ts +58 -0
- package/dist/safety/integration-write-tracker.d.ts.map +1 -0
- package/dist/safety/integration-write-tracker.js +41 -0
- package/dist/safety/integration-write-tracker.js.map +1 -0
- package/dist/safety/risk-classifier.d.ts +65 -0
- package/dist/safety/risk-classifier.d.ts.map +1 -0
- package/dist/safety/risk-classifier.js +763 -0
- package/dist/safety/risk-classifier.js.map +1 -0
- package/dist/scheduler/hourly-check-gate.d.ts +73 -0
- package/dist/scheduler/hourly-check-gate.d.ts.map +1 -0
- package/dist/scheduler/hourly-check-gate.js +128 -0
- package/dist/scheduler/hourly-check-gate.js.map +1 -0
- package/dist/secrets/backend-api-key-env.d.ts +104 -0
- package/dist/secrets/backend-api-key-env.d.ts.map +1 -0
- package/dist/secrets/backend-api-key-env.js +197 -0
- package/dist/secrets/backend-api-key-env.js.map +1 -0
- package/dist/secrets/codex-home-materializer.d.ts +35 -0
- package/dist/secrets/codex-home-materializer.d.ts.map +1 -0
- package/dist/secrets/codex-home-materializer.js +76 -0
- package/dist/secrets/codex-home-materializer.js.map +1 -0
- package/dist/secrets/encrypted-blob-store.d.ts +20 -0
- package/dist/secrets/encrypted-blob-store.d.ts.map +1 -0
- package/dist/secrets/encrypted-blob-store.js +80 -0
- package/dist/secrets/encrypted-blob-store.js.map +1 -0
- package/dist/secrets/platform-secret-store.d.ts +17 -0
- package/dist/secrets/platform-secret-store.d.ts.map +1 -0
- package/dist/secrets/platform-secret-store.js +37 -0
- package/dist/secrets/platform-secret-store.js.map +1 -0
- package/dist/secrets/redaction.d.ts +2 -0
- package/dist/secrets/redaction.d.ts.map +1 -0
- package/dist/secrets/redaction.js +2 -0
- package/dist/secrets/redaction.js.map +1 -0
- package/dist/secrets/secret-broker.d.ts +61 -0
- package/dist/secrets/secret-broker.d.ts.map +1 -0
- package/dist/secrets/secret-broker.js +160 -0
- package/dist/secrets/secret-broker.js.map +1 -0
- package/dist/secrets/secret-names.d.ts +34 -0
- package/dist/secrets/secret-names.d.ts.map +1 -0
- package/dist/secrets/secret-names.js +39 -0
- package/dist/secrets/secret-names.js.map +1 -0
- package/dist/secrets/secret-store.d.ts +8 -0
- package/dist/secrets/secret-store.d.ts.map +1 -0
- package/dist/secrets/secret-store.js +2 -0
- package/dist/secrets/secret-store.js.map +1 -0
- package/dist/secrets/types.d.ts +7 -0
- package/dist/secrets/types.d.ts.map +1 -0
- package/dist/secrets/types.js +2 -0
- package/dist/secrets/types.js.map +1 -0
- package/dist/services/apple-calendar/caldav-client.d.ts +48 -0
- package/dist/services/apple-calendar/caldav-client.d.ts.map +1 -0
- package/dist/services/apple-calendar/caldav-client.js +86 -0
- package/dist/services/apple-calendar/caldav-client.js.map +1 -0
- package/dist/services/apple-calendar/caldav-codec.d.ts +67 -0
- package/dist/services/apple-calendar/caldav-codec.d.ts.map +1 -0
- package/dist/services/apple-calendar/caldav-codec.js +341 -0
- package/dist/services/apple-calendar/caldav-codec.js.map +1 -0
- package/dist/services/apple-calendar/index.d.ts +3 -0
- package/dist/services/apple-calendar/index.d.ts.map +1 -0
- package/dist/services/apple-calendar/index.js +2 -0
- package/dist/services/apple-calendar/index.js.map +1 -0
- package/dist/services/apple-calendar/service.d.ts +75 -0
- package/dist/services/apple-calendar/service.d.ts.map +1 -0
- package/dist/services/apple-calendar/service.js +374 -0
- package/dist/services/apple-calendar/service.js.map +1 -0
- package/dist/services/apple-calendar/types.d.ts +78 -0
- package/dist/services/apple-calendar/types.d.ts.map +1 -0
- package/dist/services/apple-calendar/types.js +17 -0
- package/dist/services/apple-calendar/types.js.map +1 -0
- package/dist/services/attachments/hardlink.d.ts +11 -0
- package/dist/services/attachments/hardlink.d.ts.map +1 -0
- package/dist/services/attachments/hardlink.js +56 -0
- package/dist/services/attachments/hardlink.js.map +1 -0
- package/dist/services/attachments/sanitize.d.ts +21 -0
- package/dist/services/attachments/sanitize.d.ts.map +1 -0
- package/dist/services/attachments/sanitize.js +128 -0
- package/dist/services/attachments/sanitize.js.map +1 -0
- package/dist/services/attachments/store.d.ts +146 -0
- package/dist/services/attachments/store.d.ts.map +1 -0
- package/dist/services/attachments/store.js +477 -0
- package/dist/services/attachments/store.js.map +1 -0
- package/dist/services/calendar/outlook/graph-calendar-client.d.ts +114 -0
- package/dist/services/calendar/outlook/graph-calendar-client.d.ts.map +1 -0
- package/dist/services/calendar/outlook/graph-calendar-client.js +146 -0
- package/dist/services/calendar/outlook/graph-calendar-client.js.map +1 -0
- package/dist/services/calendar.d.ts +115 -0
- package/dist/services/calendar.d.ts.map +1 -0
- package/dist/services/calendar.js +281 -0
- package/dist/services/calendar.js.map +1 -0
- package/dist/services/delegated-backend-invoker.d.ts +414 -0
- package/dist/services/delegated-backend-invoker.d.ts.map +1 -0
- package/dist/services/delegated-backend-invoker.js +2372 -0
- package/dist/services/delegated-backend-invoker.js.map +1 -0
- package/dist/services/delegated-proxy-config.d.ts +93 -0
- package/dist/services/delegated-proxy-config.d.ts.map +1 -0
- package/dist/services/delegated-proxy-config.js +98 -0
- package/dist/services/delegated-proxy-config.js.map +1 -0
- package/dist/services/delegated-task-result-cache.d.ts +176 -0
- package/dist/services/delegated-task-result-cache.d.ts.map +1 -0
- package/dist/services/delegated-task-result-cache.js +0 -0
- package/dist/services/delegated-task-result-cache.js.map +1 -0
- package/dist/services/delegated-task-runtime.d.ts +346 -0
- package/dist/services/delegated-task-runtime.d.ts.map +1 -0
- package/dist/services/delegated-task-runtime.js +589 -0
- package/dist/services/delegated-task-runtime.js.map +1 -0
- package/dist/services/delegated-task-session-pool.d.ts +182 -0
- package/dist/services/delegated-task-session-pool.d.ts.map +1 -0
- package/dist/services/delegated-task-session-pool.js +292 -0
- package/dist/services/delegated-task-session-pool.js.map +1 -0
- package/dist/services/delegated-tool-runtime.d.ts +50 -0
- package/dist/services/delegated-tool-runtime.d.ts.map +1 -0
- package/dist/services/delegated-tool-runtime.js +120 -0
- package/dist/services/delegated-tool-runtime.js.map +1 -0
- package/dist/services/fts5.d.ts +40 -0
- package/dist/services/fts5.d.ts.map +1 -0
- package/dist/services/fts5.js +54 -0
- package/dist/services/fts5.js.map +1 -0
- package/dist/services/git-account-registry.d.ts +164 -0
- package/dist/services/git-account-registry.d.ts.map +1 -0
- package/dist/services/git-account-registry.js +297 -0
- package/dist/services/git-account-registry.js.map +1 -0
- package/dist/services/github.d.ts +49 -0
- package/dist/services/github.d.ts.map +1 -0
- package/dist/services/github.js +123 -0
- package/dist/services/github.js.map +1 -0
- package/dist/services/gmail-classifier.d.ts +62 -0
- package/dist/services/gmail-classifier.d.ts.map +1 -0
- package/dist/services/gmail-classifier.js +221 -0
- package/dist/services/gmail-classifier.js.map +1 -0
- package/dist/services/gmail.d.ts +192 -0
- package/dist/services/gmail.d.ts.map +1 -0
- package/dist/services/gmail.js +678 -0
- package/dist/services/gmail.js.map +1 -0
- package/dist/services/google-auth.d.ts +16 -0
- package/dist/services/google-auth.d.ts.map +1 -0
- package/dist/services/google-auth.js +37 -0
- package/dist/services/google-auth.js.map +1 -0
- package/dist/services/google-maps.d.ts +35 -0
- package/dist/services/google-maps.d.ts.map +1 -0
- package/dist/services/google-maps.js +82 -0
- package/dist/services/google-maps.js.map +1 -0
- package/dist/services/integrations/extract-write-item-id.d.ts +64 -0
- package/dist/services/integrations/extract-write-item-id.d.ts.map +1 -0
- package/dist/services/integrations/extract-write-item-id.js +188 -0
- package/dist/services/integrations/extract-write-item-id.js.map +1 -0
- package/dist/services/integrations/reconcile.d.ts +136 -0
- package/dist/services/integrations/reconcile.d.ts.map +1 -0
- package/dist/services/integrations/reconcile.js +218 -0
- package/dist/services/integrations/reconcile.js.map +1 -0
- package/dist/services/integrations/snapshot-partitions.d.ts +40 -0
- package/dist/services/integrations/snapshot-partitions.d.ts.map +1 -0
- package/dist/services/integrations/snapshot-partitions.js +113 -0
- package/dist/services/integrations/snapshot-partitions.js.map +1 -0
- package/dist/services/journal/render.d.ts +15 -0
- package/dist/services/journal/render.d.ts.map +1 -0
- package/dist/services/journal/render.js +17 -0
- package/dist/services/journal/render.js.map +1 -0
- package/dist/services/journal/writer.d.ts +26 -0
- package/dist/services/journal/writer.d.ts.map +1 -0
- package/dist/services/journal/writer.js +50 -0
- package/dist/services/journal/writer.js.map +1 -0
- package/dist/services/mail/account-registry.d.ts +208 -0
- package/dist/services/mail/account-registry.d.ts.map +1 -0
- package/dist/services/mail/account-registry.js +554 -0
- package/dist/services/mail/account-registry.js.map +1 -0
- package/dist/services/mail/gmail/auth-failure-classifier.d.ts +24 -0
- package/dist/services/mail/gmail/auth-failure-classifier.d.ts.map +1 -0
- package/dist/services/mail/gmail/auth-failure-classifier.js +67 -0
- package/dist/services/mail/gmail/auth-failure-classifier.js.map +1 -0
- package/dist/services/mail/gmail/gmail-provider.d.ts +58 -0
- package/dist/services/mail/gmail/gmail-provider.d.ts.map +1 -0
- package/dist/services/mail/gmail/gmail-provider.js +434 -0
- package/dist/services/mail/gmail/gmail-provider.js.map +1 -0
- package/dist/services/mail/gmail/legacy-row.d.ts +24 -0
- package/dist/services/mail/gmail/legacy-row.d.ts.map +1 -0
- package/dist/services/mail/gmail/legacy-row.js +71 -0
- package/dist/services/mail/gmail/legacy-row.js.map +1 -0
- package/dist/services/mail/gmail/poll-cursor.d.ts +12 -0
- package/dist/services/mail/gmail/poll-cursor.d.ts.map +1 -0
- package/dist/services/mail/gmail/poll-cursor.js +32 -0
- package/dist/services/mail/gmail/poll-cursor.js.map +1 -0
- package/dist/services/mail/html-to-plaintext.d.ts +27 -0
- package/dist/services/mail/html-to-plaintext.d.ts.map +1 -0
- package/dist/services/mail/html-to-plaintext.js +163 -0
- package/dist/services/mail/html-to-plaintext.js.map +1 -0
- package/dist/services/mail/imap/app-password.d.ts +27 -0
- package/dist/services/mail/imap/app-password.d.ts.map +1 -0
- package/dist/services/mail/imap/app-password.js +86 -0
- package/dist/services/mail/imap/app-password.js.map +1 -0
- package/dist/services/mail/imap/auth-failure-classifier.d.ts +21 -0
- package/dist/services/mail/imap/auth-failure-classifier.d.ts.map +1 -0
- package/dist/services/mail/imap/auth-failure-classifier.js +54 -0
- package/dist/services/mail/imap/auth-failure-classifier.js.map +1 -0
- package/dist/services/mail/imap/capabilities.d.ts +30 -0
- package/dist/services/mail/imap/capabilities.d.ts.map +1 -0
- package/dist/services/mail/imap/capabilities.js +70 -0
- package/dist/services/mail/imap/capabilities.js.map +1 -0
- package/dist/services/mail/imap/client.d.ts +15 -0
- package/dist/services/mail/imap/client.d.ts.map +1 -0
- package/dist/services/mail/imap/client.js +60 -0
- package/dist/services/mail/imap/client.js.map +1 -0
- package/dist/services/mail/imap/cursor.d.ts +19 -0
- package/dist/services/mail/imap/cursor.d.ts.map +1 -0
- package/dist/services/mail/imap/cursor.js +47 -0
- package/dist/services/mail/imap/cursor.js.map +1 -0
- package/dist/services/mail/imap/folder-resolver.d.ts +24 -0
- package/dist/services/mail/imap/folder-resolver.d.ts.map +1 -0
- package/dist/services/mail/imap/folder-resolver.js +58 -0
- package/dist/services/mail/imap/folder-resolver.js.map +1 -0
- package/dist/services/mail/imap/icloud-provider.d.ts +5 -0
- package/dist/services/mail/imap/icloud-provider.d.ts.map +1 -0
- package/dist/services/mail/imap/icloud-provider.js +5 -0
- package/dist/services/mail/imap/icloud-provider.js.map +1 -0
- package/dist/services/mail/imap/imap-provider-base.d.ts +173 -0
- package/dist/services/mail/imap/imap-provider-base.d.ts.map +1 -0
- package/dist/services/mail/imap/imap-provider-base.js +1004 -0
- package/dist/services/mail/imap/imap-provider-base.js.map +1 -0
- package/dist/services/mail/imap/query-translator.d.ts +13 -0
- package/dist/services/mail/imap/query-translator.d.ts.map +1 -0
- package/dist/services/mail/imap/query-translator.js +114 -0
- package/dist/services/mail/imap/query-translator.js.map +1 -0
- package/dist/services/mail/imap/reconcile-planner.d.ts +56 -0
- package/dist/services/mail/imap/reconcile-planner.d.ts.map +1 -0
- package/dist/services/mail/imap/reconcile-planner.js +52 -0
- package/dist/services/mail/imap/reconcile-planner.js.map +1 -0
- package/dist/services/mail/imap/reply-mime.d.ts +24 -0
- package/dist/services/mail/imap/reply-mime.d.ts.map +1 -0
- package/dist/services/mail/imap/reply-mime.js +77 -0
- package/dist/services/mail/imap/reply-mime.js.map +1 -0
- package/dist/services/mail/imap/yahoo-provider.d.ts +5 -0
- package/dist/services/mail/imap/yahoo-provider.d.ts.map +1 -0
- package/dist/services/mail/imap/yahoo-provider.js +5 -0
- package/dist/services/mail/imap/yahoo-provider.js.map +1 -0
- package/dist/services/mail/mail-search.d.ts +35 -0
- package/dist/services/mail/mail-search.d.ts.map +1 -0
- package/dist/services/mail/mail-search.js +59 -0
- package/dist/services/mail/mail-search.js.map +1 -0
- package/dist/services/mail/outlook/auth-failure-classifier.d.ts +38 -0
- package/dist/services/mail/outlook/auth-failure-classifier.d.ts.map +1 -0
- package/dist/services/mail/outlook/auth-failure-classifier.js +91 -0
- package/dist/services/mail/outlook/auth-failure-classifier.js.map +1 -0
- package/dist/services/mail/outlook/client-config.d.ts +34 -0
- package/dist/services/mail/outlook/client-config.d.ts.map +1 -0
- package/dist/services/mail/outlook/client-config.js +58 -0
- package/dist/services/mail/outlook/client-config.js.map +1 -0
- package/dist/services/mail/outlook/delta-cursor.d.ts +66 -0
- package/dist/services/mail/outlook/delta-cursor.d.ts.map +1 -0
- package/dist/services/mail/outlook/delta-cursor.js +85 -0
- package/dist/services/mail/outlook/delta-cursor.js.map +1 -0
- package/dist/services/mail/outlook/graph-client.d.ts +98 -0
- package/dist/services/mail/outlook/graph-client.d.ts.map +1 -0
- package/dist/services/mail/outlook/graph-client.js +198 -0
- package/dist/services/mail/outlook/graph-client.js.map +1 -0
- package/dist/services/mail/outlook/msal-app-factory.d.ts +20 -0
- package/dist/services/mail/outlook/msal-app-factory.d.ts.map +1 -0
- package/dist/services/mail/outlook/msal-app-factory.js +62 -0
- package/dist/services/mail/outlook/msal-app-factory.js.map +1 -0
- package/dist/services/mail/outlook/msal-cache-plugin.d.ts +19 -0
- package/dist/services/mail/outlook/msal-cache-plugin.d.ts.map +1 -0
- package/dist/services/mail/outlook/msal-cache-plugin.js +30 -0
- package/dist/services/mail/outlook/msal-cache-plugin.js.map +1 -0
- package/dist/services/mail/outlook/oauth-device-code.d.ts +26 -0
- package/dist/services/mail/outlook/oauth-device-code.d.ts.map +1 -0
- package/dist/services/mail/outlook/oauth-device-code.js +32 -0
- package/dist/services/mail/outlook/oauth-device-code.js.map +1 -0
- package/dist/services/mail/outlook/oauth-loopback.d.ts +41 -0
- package/dist/services/mail/outlook/oauth-loopback.d.ts.map +1 -0
- package/dist/services/mail/outlook/oauth-loopback.js +223 -0
- package/dist/services/mail/outlook/oauth-loopback.js.map +1 -0
- package/dist/services/mail/outlook/outlook-provider.d.ts +100 -0
- package/dist/services/mail/outlook/outlook-provider.d.ts.map +1 -0
- package/dist/services/mail/outlook/outlook-provider.js +619 -0
- package/dist/services/mail/outlook/outlook-provider.js.map +1 -0
- package/dist/services/mail/outlook/query-translator.d.ts +10 -0
- package/dist/services/mail/outlook/query-translator.d.ts.map +1 -0
- package/dist/services/mail/outlook/query-translator.js +103 -0
- package/dist/services/mail/outlook/query-translator.js.map +1 -0
- package/dist/services/mail/provider.d.ts +267 -0
- package/dist/services/mail/provider.d.ts.map +1 -0
- package/dist/services/mail/provider.js +34 -0
- package/dist/services/mail/provider.js.map +1 -0
- package/dist/services/mail/query-utils.d.ts +13 -0
- package/dist/services/mail/query-utils.d.ts.map +1 -0
- package/dist/services/mail/query-utils.js +18 -0
- package/dist/services/mail/query-utils.js.map +1 -0
- package/dist/services/mail-classifier.d.ts +25 -0
- package/dist/services/mail-classifier.d.ts.map +1 -0
- package/dist/services/mail-classifier.js +52 -0
- package/dist/services/mail-classifier.js.map +1 -0
- package/dist/services/mail-ingestion.d.ts +139 -0
- package/dist/services/mail-ingestion.d.ts.map +1 -0
- package/dist/services/mail-ingestion.js +223 -0
- package/dist/services/mail-ingestion.js.map +1 -0
- package/dist/services/mcp/auto-probe.d.ts +76 -0
- package/dist/services/mcp/auto-probe.d.ts.map +1 -0
- package/dist/services/mcp/auto-probe.js +147 -0
- package/dist/services/mcp/auto-probe.js.map +1 -0
- package/dist/services/mcp/generators/claude.d.ts +18 -0
- package/dist/services/mcp/generators/claude.d.ts.map +1 -0
- package/dist/services/mcp/generators/claude.js +90 -0
- package/dist/services/mcp/generators/claude.js.map +1 -0
- package/dist/services/mcp/generators/codex.d.ts +22 -0
- package/dist/services/mcp/generators/codex.d.ts.map +1 -0
- package/dist/services/mcp/generators/codex.js +102 -0
- package/dist/services/mcp/generators/codex.js.map +1 -0
- package/dist/services/mcp/generators/gemini.d.ts +20 -0
- package/dist/services/mcp/generators/gemini.d.ts.map +1 -0
- package/dist/services/mcp/generators/gemini.js +97 -0
- package/dist/services/mcp/generators/gemini.js.map +1 -0
- package/dist/services/mcp/generators/index.d.ts +20 -0
- package/dist/services/mcp/generators/index.d.ts.map +1 -0
- package/dist/services/mcp/generators/index.js +29 -0
- package/dist/services/mcp/generators/index.js.map +1 -0
- package/dist/services/mcp/generators/types.d.ts +47 -0
- package/dist/services/mcp/generators/types.d.ts.map +1 -0
- package/dist/services/mcp/generators/types.js +40 -0
- package/dist/services/mcp/generators/types.js.map +1 -0
- package/dist/services/mcp/probe.d.ts +31 -0
- package/dist/services/mcp/probe.d.ts.map +1 -0
- package/dist/services/mcp/probe.js +437 -0
- package/dist/services/mcp/probe.js.map +1 -0
- package/dist/services/mcp/registry.d.ts +84 -0
- package/dist/services/mcp/registry.d.ts.map +1 -0
- package/dist/services/mcp/registry.js +387 -0
- package/dist/services/mcp/registry.js.map +1 -0
- package/dist/services/mcp/risk.d.ts +82 -0
- package/dist/services/mcp/risk.d.ts.map +1 -0
- package/dist/services/mcp/risk.js +126 -0
- package/dist/services/mcp/risk.js.map +1 -0
- package/dist/services/mcp/session-materializer.d.ts +123 -0
- package/dist/services/mcp/session-materializer.d.ts.map +1 -0
- package/dist/services/mcp/session-materializer.js +361 -0
- package/dist/services/mcp/session-materializer.js.map +1 -0
- package/dist/services/mcp/tool-audit.d.ts +53 -0
- package/dist/services/mcp/tool-audit.d.ts.map +1 -0
- package/dist/services/mcp/tool-audit.js +74 -0
- package/dist/services/mcp/tool-audit.js.map +1 -0
- package/dist/services/mcp/types.d.ts +88 -0
- package/dist/services/mcp/types.d.ts.map +1 -0
- package/dist/services/mcp/types.js +94 -0
- package/dist/services/mcp/types.js.map +1 -0
- package/dist/services/notion.d.ts +134 -0
- package/dist/services/notion.d.ts.map +1 -0
- package/dist/services/notion.js +350 -0
- package/dist/services/notion.js.map +1 -0
- package/dist/services/obsidian.d.ts +116 -0
- package/dist/services/obsidian.d.ts.map +1 -0
- package/dist/services/obsidian.js +305 -0
- package/dist/services/obsidian.js.map +1 -0
- package/dist/services/service-registry.d.ts +31 -0
- package/dist/services/service-registry.d.ts.map +1 -0
- package/dist/services/service-registry.js +15 -0
- package/dist/services/service-registry.js.map +1 -0
- package/dist/services/voice/transcriber-impl.d.ts +15 -0
- package/dist/services/voice/transcriber-impl.d.ts.map +1 -0
- package/dist/services/voice/transcriber-impl.js +129 -0
- package/dist/services/voice/transcriber-impl.js.map +1 -0
- package/dist/services/voice/transcriber.d.ts +117 -0
- package/dist/services/voice/transcriber.d.ts.map +1 -0
- package/dist/services/voice/transcriber.js +201 -0
- package/dist/services/voice/transcriber.js.map +1 -0
- package/dist/settings/runtime-settings.d.ts +232 -0
- package/dist/settings/runtime-settings.d.ts.map +1 -0
- package/dist/settings/runtime-settings.js +769 -0
- package/dist/settings/runtime-settings.js.map +1 -0
- package/dist/settings/settings-store.d.ts +13 -0
- package/dist/settings/settings-store.d.ts.map +1 -0
- package/dist/settings/settings-store.js +87 -0
- package/dist/settings/settings-store.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,2541 @@
|
|
|
1
|
+
import { query, } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import { collectSessionDeniedTools, INTEGRATION_DESCRIPTORS, INTEGRATION_KEYS, isAutonomousProcessKey, matchRunAllowedToolPattern, } from "@aitne/shared";
|
|
3
|
+
import { readIntegrations } from "../../db/integrations-store.js";
|
|
4
|
+
import { buildExecutionPrompt, buildSummaryPrompt, } from "./prompt-utils.js";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { resolve as resolvePath, isAbsolute } from "node:path";
|
|
7
|
+
import { getContextDir } from "../../config.js";
|
|
8
|
+
import { materializeMcpForSession } from "../../services/mcp/session-materializer.js";
|
|
9
|
+
import { parseMcpToolName } from "../../services/mcp/risk.js";
|
|
10
|
+
import { logMcpToolCall, updateMcpToolCallResult } from "../../services/mcp/tool-audit.js";
|
|
11
|
+
import { BackendQuotaError, BackendDecisiveFailure, DelegatedProxyTimeoutError, classifyAbortReason, } from "../agent-core.js";
|
|
12
|
+
import { IdleWatchdog } from "./idle-watchdog.js";
|
|
13
|
+
import { DELEGATED_PROXY_DEFAULTS } from "../../services/delegated-proxy-config.js";
|
|
14
|
+
import { buildDelegatedToolPrompt, emptyCost, flattenToolResultContent, tryParseToolResult, withDurationMs, } from "../../services/delegated-tool-runtime.js";
|
|
15
|
+
import { createSessionWorkdir, cleanupSessionWorkdir } from "../workdir.js";
|
|
16
|
+
import { buildDaemonApiCliEnv } from "../daemon-api-cli.js";
|
|
17
|
+
import { createLogger } from "../../logging.js";
|
|
18
|
+
import { DEFAULT_CLAUDE_HIGH_MODEL, DEFAULT_CLAUDE_MEDIUM_MODEL, findRegisteredModel, getModelsForBackend, } from "./model-registry.js";
|
|
19
|
+
import { readClaudeCredentials } from "./claude-credentials-store.js";
|
|
20
|
+
import { ALWAYS_DISALLOWED_TOOLS, classifyAbsoluteBlock, } from "../../safety/always-disallowed.js";
|
|
21
|
+
import { recordAbsoluteBlockAudit } from "../../safety/absolute-block-audit.js";
|
|
22
|
+
import { isPathInsideOrEqual, shellPathForms, } from "../path-compat.js";
|
|
23
|
+
import { CliPathCache, isPlausibleAnthropicApiKey } from "./cli-utils.js";
|
|
24
|
+
import { probeApiKeyServerSide } from "./api-key-probe.js";
|
|
25
|
+
import { extractSilentApiErrors, logSilentApiErrors, } from "./silent-api-error-detector.js";
|
|
26
|
+
const logger = createLogger("claude-code-core");
|
|
27
|
+
/**
|
|
28
|
+
* Detect which cloud-provider mode the Claude Code SDK is currently
|
|
29
|
+
* configured for, based on the documented `CLAUDE_CODE_USE_*` env flags.
|
|
30
|
+
* Returns null when running in the default direct-API-key / OAuth mode.
|
|
31
|
+
*
|
|
32
|
+
* Required-env spec follows the official Claude Code docs (verified
|
|
33
|
+
* 2026-05):
|
|
34
|
+
* - **Bedrock** — only `AWS_REGION` is required by Claude Code itself
|
|
35
|
+
* (the docs explicitly call this out). AWS credentials flow through
|
|
36
|
+
* the SDK's default credential chain, which can come from any of
|
|
37
|
+
* AWS_ACCESS_KEY_ID/SECRET, AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE, or
|
|
38
|
+
* `~/.aws/`. Requiring access-key + secret here would falsely fail
|
|
39
|
+
* bearer-token / profile setups.
|
|
40
|
+
* - **Vertex** — `ANTHROPIC_VERTEX_PROJECT_ID` + `CLOUD_ML_REGION`.
|
|
41
|
+
* GCP credentials flow through ADC, which can be `gcloud auth
|
|
42
|
+
* application-default login` or a `GOOGLE_APPLICATION_CREDENTIALS`
|
|
43
|
+
* file path; we don't require either at this layer.
|
|
44
|
+
* - **Foundry** — `ANTHROPIC_FOUNDRY_RESOURCE` OR
|
|
45
|
+
* `ANTHROPIC_FOUNDRY_BASE_URL`. The API key is optional; without it,
|
|
46
|
+
* Claude Code uses the Azure DefaultAzureCredential chain.
|
|
47
|
+
*
|
|
48
|
+
* The SDK reads these flags itself; the daemon only inspects them so
|
|
49
|
+
* the auth probes (`checkAuth` / `checkAuthDetailed`) can return the
|
|
50
|
+
* right `method` and skip the Anthropic-API probe (which would 401
|
|
51
|
+
* against a Bedrock / Vertex / Foundry deployment).
|
|
52
|
+
*/
|
|
53
|
+
function detectCloudProviderEnv() {
|
|
54
|
+
if (process.env.CLAUDE_CODE_USE_BEDROCK?.trim() === "1") {
|
|
55
|
+
const missing = [];
|
|
56
|
+
if (!process.env.AWS_REGION?.trim())
|
|
57
|
+
missing.push("AWS_REGION");
|
|
58
|
+
return {
|
|
59
|
+
method: "bedrock",
|
|
60
|
+
flagEnvVar: "CLAUDE_CODE_USE_BEDROCK",
|
|
61
|
+
label: "Amazon Bedrock",
|
|
62
|
+
missing,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (process.env.CLAUDE_CODE_USE_VERTEX?.trim() === "1") {
|
|
66
|
+
const required = [
|
|
67
|
+
"ANTHROPIC_VERTEX_PROJECT_ID",
|
|
68
|
+
"CLOUD_ML_REGION",
|
|
69
|
+
];
|
|
70
|
+
return {
|
|
71
|
+
method: "vertex",
|
|
72
|
+
flagEnvVar: "CLAUDE_CODE_USE_VERTEX",
|
|
73
|
+
label: "Google Vertex AI",
|
|
74
|
+
missing: required.filter((name) => !process.env[name]?.trim()),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (process.env.CLAUDE_CODE_USE_FOUNDRY?.trim() === "1") {
|
|
78
|
+
const hasResource = Boolean(process.env.ANTHROPIC_FOUNDRY_RESOURCE?.trim());
|
|
79
|
+
const hasBaseUrl = Boolean(process.env.ANTHROPIC_FOUNDRY_BASE_URL?.trim());
|
|
80
|
+
return {
|
|
81
|
+
method: "foundry",
|
|
82
|
+
flagEnvVar: "CLAUDE_CODE_USE_FOUNDRY",
|
|
83
|
+
label: "Microsoft Foundry",
|
|
84
|
+
missing: hasResource || hasBaseUrl
|
|
85
|
+
? []
|
|
86
|
+
: ["ANTHROPIC_FOUNDRY_RESOURCE or ANTHROPIC_FOUNDRY_BASE_URL"],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
// Probe data is derived from `INTEGRATION_DESCRIPTORS.backendConnectors.claude`
|
|
92
|
+
// at module init, so adding a new delegated integration only requires the
|
|
93
|
+
// registry update — the prompt, prefix list, and tool-name regex follow
|
|
94
|
+
// automatically. Before this was registry-driven, every new integration
|
|
95
|
+
// silently broke its own probe (`present` permanently false) until someone
|
|
96
|
+
// remembered to edit four constants in lockstep. See
|
|
97
|
+
// `docs/design/17-delegated-mode-v2.md` §7.1.
|
|
98
|
+
//
|
|
99
|
+
// Tool names span two character classes:
|
|
100
|
+
// - Gmail / Calendar: `search_threads`, `list_events` (snake_case only)
|
|
101
|
+
// - Notion: `notion-search`, `notion-create-pages` (kebab-case)
|
|
102
|
+
// The trailing `[A-Za-z0-9_-]+` class covers both. Namespace strings are
|
|
103
|
+
// regex-escaped because Codex (`._`) and Gemini (`.`) namespaces contain
|
|
104
|
+
// metacharacters.
|
|
105
|
+
function buildClaudeProbeData() {
|
|
106
|
+
const meta = [];
|
|
107
|
+
for (const key of INTEGRATION_KEYS) {
|
|
108
|
+
const descriptor = INTEGRATION_DESCRIPTORS[key];
|
|
109
|
+
const connector = descriptor.backendConnectors.claude;
|
|
110
|
+
if (!connector)
|
|
111
|
+
continue;
|
|
112
|
+
const seen = new Set();
|
|
113
|
+
for (const tools of Object.values(connector.capabilityTools)) {
|
|
114
|
+
for (const t of tools)
|
|
115
|
+
seen.add(t);
|
|
116
|
+
}
|
|
117
|
+
meta.push({
|
|
118
|
+
displayName: descriptor.displayName,
|
|
119
|
+
toolNamespace: connector.toolNamespace,
|
|
120
|
+
requiredCapabilities: connector.requiredCapabilities,
|
|
121
|
+
capabilityToolNames: Array.from(seen),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const prefixes = meta.map((m) => m.toolNamespace);
|
|
125
|
+
const lines = ["Use `ToolSearch` only."];
|
|
126
|
+
for (const m of meta) {
|
|
127
|
+
// Keyword query: display name + required capability words split on
|
|
128
|
+
// `_` / `-`. Mirrors the pre-registry-driven prompts (Gmail used
|
|
129
|
+
// `'Gmail search read draft label'`, Calendar used `'Google Calendar
|
|
130
|
+
// list get create update delete event'`) — both are display name +
|
|
131
|
+
// requiredCapabilities expanded into word tokens. Using the
|
|
132
|
+
// capability set (semantic) rather than every capability tool name
|
|
133
|
+
// (mechanical) keeps ToolSearch's token-overlap ranking sharp; a
|
|
134
|
+
// bag-of-tool-name-fragments query dilutes the signal across
|
|
135
|
+
// ~15-30 tokens per integration and demotes the actually-relevant
|
|
136
|
+
// hits.
|
|
137
|
+
const queryWords = [
|
|
138
|
+
m.displayName,
|
|
139
|
+
...m.requiredCapabilities.flatMap((c) => c.split(/[-_]/)),
|
|
140
|
+
]
|
|
141
|
+
.join(" ")
|
|
142
|
+
.replace(/\s+/g, " ")
|
|
143
|
+
.trim();
|
|
144
|
+
lines.push(`Search for ${m.displayName} connector tools with query '${queryWords}' and max_results 20.`);
|
|
145
|
+
}
|
|
146
|
+
lines.push("Do not call any of the searched MCP tools.");
|
|
147
|
+
lines.push(`After the searches, print every full tool name returned that starts with one of: ${prefixes
|
|
148
|
+
.map((p) => `'${p}'`)
|
|
149
|
+
.join(", ")}.`);
|
|
150
|
+
// Per-integration "must include" hints: ToolSearch caps results at
|
|
151
|
+
// max_results, so lower-ranked tools (e.g. Gmail's label_* family) can
|
|
152
|
+
// be missed by keyword search. Listing them explicitly nudges the agent
|
|
153
|
+
// to print them when they do appear in any of the searches above.
|
|
154
|
+
for (const m of meta) {
|
|
155
|
+
if (m.capabilityToolNames.length === 0)
|
|
156
|
+
continue;
|
|
157
|
+
const fullNames = m.capabilityToolNames
|
|
158
|
+
.map((n) => m.toolNamespace + n)
|
|
159
|
+
.join(", ");
|
|
160
|
+
lines.push(`Include these ${m.displayName} tools if present: ${fullNames}.`);
|
|
161
|
+
}
|
|
162
|
+
lines.push("One tool name per line. No markdown fences. No explanation. If no such tools are available, print NONE.");
|
|
163
|
+
// Defense: an empty prefix list would compile to `\b(?:)[A-Za-z0-9_-]+\b`,
|
|
164
|
+
// which matches any word — every connector probe would falsely "succeed".
|
|
165
|
+
// Realistically `INTEGRATION_DESCRIPTORS` is non-empty, but the guard
|
|
166
|
+
// keeps a future registry rollback from corrupting probe semantics.
|
|
167
|
+
const escapedPrefixes = prefixes.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
168
|
+
const regex = escapedPrefixes.length === 0
|
|
169
|
+
? /(?!)/g
|
|
170
|
+
: new RegExp(`\\b(?:${escapedPrefixes.join("|")})[A-Za-z0-9_-]+\\b`, "g");
|
|
171
|
+
return { prompt: lines.join(" "), prefixes, regex };
|
|
172
|
+
}
|
|
173
|
+
const CLAUDE_PROBE_DATA = buildClaudeProbeData();
|
|
174
|
+
export const CLAUDE_PROBE_TOOLS_PROMPT = CLAUDE_PROBE_DATA.prompt;
|
|
175
|
+
const CLAUDE_PROBE_TOOL_PREFIXES = CLAUDE_PROBE_DATA.prefixes;
|
|
176
|
+
const CLAUDE_CONNECTOR_TOOL_RE = CLAUDE_PROBE_DATA.regex;
|
|
177
|
+
/**
|
|
178
|
+
* The built-in Claude Code tool that loads schemas for deferred MCP tools.
|
|
179
|
+
* When a session inherits many MCP servers from the user's global config,
|
|
180
|
+
* the CLI defers a portion of the tool schemas; the model must call
|
|
181
|
+
* `ToolSearch` to bring a specific tool's schema into the working set
|
|
182
|
+
* before invoking it. The proxy explicitly allows it (see runDelegatedTool)
|
|
183
|
+
* and the stream parser excludes it from `wrongToolName` capture so a
|
|
184
|
+
* partial-trace failure (`ToolSearch` + max_turns before the connector
|
|
185
|
+
* call) classifies as `no_tool_call` rather than the misleading
|
|
186
|
+
* `wrong_tool=ToolSearch`.
|
|
187
|
+
*/
|
|
188
|
+
const DEFERRED_TOOL_DISCOVERY_TOOL_NAME = "ToolSearch";
|
|
189
|
+
/**
|
|
190
|
+
* Registry-driven allowlist entries for integrations currently delegated to
|
|
191
|
+
* Claude. Under `permissionMode: "dontAsk"`, any tool not in the SDK's
|
|
192
|
+
* `allowedTools` is silently denied — so a delegated Gmail / Calendar
|
|
193
|
+
* integration whose skill instructs the agent to call
|
|
194
|
+
* `mcp__claude_ai_Gmail__search_threads` will fail with "permission denied"
|
|
195
|
+
* unless that exact tool name is pre-authorized here.
|
|
196
|
+
*
|
|
197
|
+
* Pure over `(integrations)` — callers pass the record read from
|
|
198
|
+
* `db/integrations-store.ts#readIntegrations`. The SDK's MCP tool matcher is
|
|
199
|
+
* literal (`mcp__<server>__<tool>` — see `services/mcp/risk.ts`), so we
|
|
200
|
+
* enumerate every capability tool the registry declares rather than using a
|
|
201
|
+
* wildcard. This guarantees the allowlist only widens by what the registry
|
|
202
|
+
* explicitly advertises; adding a new connector tool demands a registry
|
|
203
|
+
* update first.
|
|
204
|
+
*
|
|
205
|
+
* Only integrations whose `delegatedBackend === "claude"` contribute — a
|
|
206
|
+
* Codex-delegated Gmail integration must not widen Claude's surface.
|
|
207
|
+
*/
|
|
208
|
+
export function computeDelegatedClaudeTools(integrations) {
|
|
209
|
+
const out = new Set();
|
|
210
|
+
for (const key of INTEGRATION_KEYS) {
|
|
211
|
+
const state = integrations[key];
|
|
212
|
+
if (!state || state.mode !== "delegated")
|
|
213
|
+
continue;
|
|
214
|
+
if (state.delegatedBackend !== "claude")
|
|
215
|
+
continue;
|
|
216
|
+
const connector = INTEGRATION_DESCRIPTORS[key].backendConnectors.claude;
|
|
217
|
+
if (!connector)
|
|
218
|
+
continue;
|
|
219
|
+
for (const toolNames of Object.values(connector.capabilityTools)) {
|
|
220
|
+
for (const toolName of toolNames) {
|
|
221
|
+
out.add(connector.toolNamespace + toolName);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return Array.from(out);
|
|
226
|
+
}
|
|
227
|
+
export class AgentTimeoutError extends Error {
|
|
228
|
+
timeoutMs;
|
|
229
|
+
constructor(timeoutMs) {
|
|
230
|
+
super(`Agent execution exceeded timeout of ${timeoutMs}ms`);
|
|
231
|
+
this.timeoutMs = timeoutMs;
|
|
232
|
+
this.name = "AgentTimeoutError";
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
export function isClaudeCodeQuotaError(error) {
|
|
236
|
+
if (!(error instanceof Error)) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
const maybeRecord = error;
|
|
240
|
+
const message = error.message.toLowerCase();
|
|
241
|
+
const code = typeof maybeRecord.code === "string" ? maybeRecord.code.toLowerCase() : "";
|
|
242
|
+
const type = typeof maybeRecord.type === "string" ? maybeRecord.type.toLowerCase() : "";
|
|
243
|
+
if (maybeRecord.status === 429) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
if (code.includes("rate") || code.includes("quota")) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
if (type.includes("rate") || type.includes("quota")) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
return /rate.?limit|quota|too many requests|you['']?\s*ve hit your limit/.test(message);
|
|
253
|
+
}
|
|
254
|
+
export function isClaudeCodeMaxBudgetError(error) {
|
|
255
|
+
const message = error instanceof Error
|
|
256
|
+
? error.message
|
|
257
|
+
: typeof error === "string"
|
|
258
|
+
? error
|
|
259
|
+
: "";
|
|
260
|
+
const code = typeof error === "object" && error !== null && typeof error.code === "string"
|
|
261
|
+
? error.code
|
|
262
|
+
: "";
|
|
263
|
+
const type = typeof error === "object" && error !== null && typeof error.type === "string"
|
|
264
|
+
? error.type
|
|
265
|
+
: "";
|
|
266
|
+
return /max(?:imum)? budget|max_budget_usd|budget limit|per-turn budget/i.test(`${message} ${code} ${type}`);
|
|
267
|
+
}
|
|
268
|
+
export function extractClaudeCodeQuotaResetHint(error) {
|
|
269
|
+
if (!(error instanceof Error)) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
const match = /resets?\s+(\d{1,2})(?::(\d{2}))?\s*(am|pm)\s*(?:\(([^)]+)\))?/i.exec(error.message);
|
|
273
|
+
if (!match) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
const rawHour = Number(match[1]);
|
|
277
|
+
const meridiem = match[3].toLowerCase();
|
|
278
|
+
let hour = rawHour % 12;
|
|
279
|
+
if (meridiem === "pm") {
|
|
280
|
+
hour += 12;
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
hour,
|
|
284
|
+
minute: match[2] ? Number(match[2]) : 0,
|
|
285
|
+
timeZone: match[4]?.trim() || undefined,
|
|
286
|
+
rawLabel: error.message.slice(match.index).replace(/^resets?\s+/i, "").trim(),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* ClaudeCodeCore intentionally does NOT run a pre-flight `checkAuth()`
|
|
291
|
+
* gate inside `execute()` / `runTurn()`. Codex and Gemini each call
|
|
292
|
+
* `await this.checkAuth()` at the top of `runTurn()` as a cheap way
|
|
293
|
+
* to surface an early `BackendDecisiveFailure("auth")` before paying
|
|
294
|
+
* the latency of spawning the CLI subprocess; Claude uses the
|
|
295
|
+
* `@anthropic-ai/claude-agent-sdk` stream consumer instead, and the
|
|
296
|
+
* SDK's first HTTP round-trip already returns a decisive 401 on its
|
|
297
|
+
* own. A pre-flight here would duplicate work (two credential reads
|
|
298
|
+
* per execute) and isn't needed to prevent accidental token use —
|
|
299
|
+
* the reactive path in `BackendRouter` catches the SDK's 401, maps
|
|
300
|
+
* it to `BackendDecisiveFailure("auth")`, and calls
|
|
301
|
+
* `recordReactiveAuthFailure` exactly as if a pre-flight had run.
|
|
302
|
+
*
|
|
303
|
+
* This asymmetry is deliberate and matched by the corresponding
|
|
304
|
+
* explanatory comments at `codex-core.ts` / `gemini-cli-core.ts`'s
|
|
305
|
+
* `runTurn` pre-flight and at `IAgentCore.checkAuth` in
|
|
306
|
+
* `agent-core.ts`. If you add a new backend, decide the pre-flight
|
|
307
|
+
* question based on the CLI / SDK's own startup cost, not on
|
|
308
|
+
* pattern-matching against one of the existing three.
|
|
309
|
+
*/
|
|
310
|
+
export class ClaudeCodeCore {
|
|
311
|
+
config;
|
|
312
|
+
writeTracker;
|
|
313
|
+
backendId = "claude";
|
|
314
|
+
static RETRY_DELAY_MS = 5 * 60 * 1000;
|
|
315
|
+
static MAX_RETRIES = 1;
|
|
316
|
+
// Lazily re-resolved with a 60 s TTL so CLI install/uninstall is
|
|
317
|
+
// detected without a daemon restart (roadmap §9.4). Constructor does an
|
|
318
|
+
// eager PATH scan so the first checkAuth() call has no extra latency.
|
|
319
|
+
cliPathCache;
|
|
320
|
+
/** Legacy shared read token injected into the Claude subprocess env. */
|
|
321
|
+
readToken;
|
|
322
|
+
/** Scoped token manager preferred over the legacy shared read token. */
|
|
323
|
+
readTokenManager;
|
|
324
|
+
/** B-003 Phase 3 — DB + blob store for per-session MCP materialization. */
|
|
325
|
+
mcpContext;
|
|
326
|
+
/** Transparent getter — all existing `this.cliPath` references keep working. */
|
|
327
|
+
get cliPath() {
|
|
328
|
+
return this.cliPathCache.get();
|
|
329
|
+
}
|
|
330
|
+
constructor(config,
|
|
331
|
+
/**
|
|
332
|
+
* Shared AgentWriteTracker. When present, the Write/Edit PreToolUse hook
|
|
333
|
+
* pre-marks vault-scoped writes so the ObsidianWatcher attributes the
|
|
334
|
+
* resulting chokidar event to `actor='agent'` instead of `'user'`. Without
|
|
335
|
+
* this wiring, the hourly_check dispatcher would re-discover the agent's
|
|
336
|
+
* own vault writes every cycle and loop.
|
|
337
|
+
*/
|
|
338
|
+
writeTracker) {
|
|
339
|
+
this.config = config;
|
|
340
|
+
this.writeTracker = writeTracker;
|
|
341
|
+
this.warnOnMissingCriticalTools();
|
|
342
|
+
this.cliPathCache = new CliPathCache("claude");
|
|
343
|
+
}
|
|
344
|
+
/** Set the per-daemon-boot read token for subprocess-local daemon API auth. */
|
|
345
|
+
setReadToken(token) {
|
|
346
|
+
this.readToken = token;
|
|
347
|
+
}
|
|
348
|
+
setReadTokenManager(manager) {
|
|
349
|
+
this.readTokenManager = manager;
|
|
350
|
+
}
|
|
351
|
+
setMcpContext(context) {
|
|
352
|
+
this.mcpContext = context;
|
|
353
|
+
logger.info({ backendId: this.backendId }, "MCP context wired — delegated-integration allowlist resolution enabled");
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Load the per-session MCP materialization. When the core hasn't been
|
|
357
|
+
* wired with a context (tests, startup ordering) we return an empty
|
|
358
|
+
* result rather than throwing — equivalent to "no enabled servers".
|
|
359
|
+
*/
|
|
360
|
+
async materializeMcp(sessionDir, processKey) {
|
|
361
|
+
if (!this.mcpContext) {
|
|
362
|
+
return {
|
|
363
|
+
servers: [],
|
|
364
|
+
env: {},
|
|
365
|
+
configPath: null,
|
|
366
|
+
claudeMcpServers: null,
|
|
367
|
+
disallowedTools: [],
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
// Allow mode bypasses the approve-tier MCP strip that autonomous
|
|
371
|
+
// process keys normally trigger — "strong permission mode" means every
|
|
372
|
+
// MCP is reachable, including routines that would otherwise have
|
|
373
|
+
// `slack_send_message` et al stripped.
|
|
374
|
+
const allowMode = this.config.claudeExecutionPermissionMode === "allow";
|
|
375
|
+
const autonomous = !allowMode && (processKey ? isAutonomousProcessKey(processKey) : false);
|
|
376
|
+
return materializeMcpForSession({
|
|
377
|
+
db: this.mcpContext.db,
|
|
378
|
+
blobStore: this.mcpContext.blobStore,
|
|
379
|
+
sessionDir,
|
|
380
|
+
backendId: this.backendId,
|
|
381
|
+
autonomous,
|
|
382
|
+
contextDir: getContextDir(this.config),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Tools the reactive DM path depends on. If `allowedToolsOverride` is set
|
|
387
|
+
* but drops any of these, sessions will silently deny them under
|
|
388
|
+
* `permissionMode: "dontAsk"` and the owner will see a misleading
|
|
389
|
+
* "restricted" error. See BUG-DM-BACKEND-PERMISSIONS.md §9 (Fix 1a).
|
|
390
|
+
*/
|
|
391
|
+
static CRITICAL_OVERRIDE_TOOLS = ["Skill", "Bash(jq *)"];
|
|
392
|
+
/**
|
|
393
|
+
* Pure computation: which of the CRITICAL_OVERRIDE_TOOLS are missing from
|
|
394
|
+
* the current `allowedToolsOverride`. Returns `[]` when the override is
|
|
395
|
+
* unset (the default allowlist already contains all critical tools).
|
|
396
|
+
*/
|
|
397
|
+
getMissingCriticalOverrideTools() {
|
|
398
|
+
const override = this.config.allowedToolsOverride;
|
|
399
|
+
if (!override)
|
|
400
|
+
return [];
|
|
401
|
+
return ClaudeCodeCore.CRITICAL_OVERRIDE_TOOLS.filter((tool) => !override.includes(tool));
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* `allowedToolsOverride` REPLACES the default allowlist (it does not merge).
|
|
405
|
+
* Emit a one-shot warning at construction so a mis-configuration surfaces
|
|
406
|
+
* in the daemon log instead of a confusing DM reply.
|
|
407
|
+
*/
|
|
408
|
+
warnOnMissingCriticalTools() {
|
|
409
|
+
// In allow mode neither the override nor the default allowlist applies —
|
|
410
|
+
// the SDK runs under bypassPermissions, so a missing Skill / Bash(jq *)
|
|
411
|
+
// entry in the override is cosmetic. Suppress the warning.
|
|
412
|
+
if (this.config.claudeExecutionPermissionMode === "allow")
|
|
413
|
+
return;
|
|
414
|
+
const missing = this.getMissingCriticalOverrideTools();
|
|
415
|
+
if (missing.length === 0)
|
|
416
|
+
return;
|
|
417
|
+
logger.warn({
|
|
418
|
+
missing,
|
|
419
|
+
overrideSize: this.config.allowedToolsOverride?.length ?? 0,
|
|
420
|
+
}, "allowedToolsOverride is set but missing critical tools. " +
|
|
421
|
+
"The reactive DM path calls user skills and jq pipelines; without these " +
|
|
422
|
+
"the session will deny them with 'dontAsk' and respond to the owner with " +
|
|
423
|
+
"a misleading 'restricted' message. Either append the missing entries to " +
|
|
424
|
+
"the override, or clear the override to fall back to the default allowlist.");
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Translate the Aitne advisor config into the SDK `options.settings`
|
|
428
|
+
* shape. `advisorModel` is a field on `Settings`, not on `Options` — the
|
|
429
|
+
* query() parameter must be passed inside a `settings` object.
|
|
430
|
+
*
|
|
431
|
+
* Returns an empty object when advisor is disabled so we don't clobber any
|
|
432
|
+
* settings source that would otherwise load.
|
|
433
|
+
*/
|
|
434
|
+
buildAdvisorSettings() {
|
|
435
|
+
if (!this.config.advisorEnabled || !this.config.advisorModel) {
|
|
436
|
+
return {};
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
settings: {
|
|
440
|
+
advisorModel: this.config.advisorModel,
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
buildSystemPrompt() {
|
|
445
|
+
// Character is NOT appended here — Phase 2 of the Character feature
|
|
446
|
+
// (see docs/design/15-character.md §15.4.3) moved the injection into
|
|
447
|
+
// the rendered CLAUDE.md so Claude / Codex / Gemini see a byte-
|
|
448
|
+
// identical block. The remaining append is the WhatsApp-prefix
|
|
449
|
+
// operational note, which is byte-stable per-session and therefore
|
|
450
|
+
// cache-friendly.
|
|
451
|
+
const appendParts = [
|
|
452
|
+
"WhatsApp outbound messages are prefixed by the daemon. Do not add that prefix yourself unless the user explicitly asks.",
|
|
453
|
+
];
|
|
454
|
+
return {
|
|
455
|
+
type: "preset",
|
|
456
|
+
preset: "claude_code",
|
|
457
|
+
append: appendParts.join("\n"),
|
|
458
|
+
// Strip per-session dynamic sections (cwd, auto-memory path, git
|
|
459
|
+
// status) from the cached system prompt prefix. The content is
|
|
460
|
+
// re-injected as the first user message so the model still sees it,
|
|
461
|
+
// but the system prompt becomes byte-identical across sessions —
|
|
462
|
+
// enabling inter-session prompt cache hits within the 5-minute TTL.
|
|
463
|
+
// Biggest wins: back-to-back routines (morning_routine + roadmap_refresh
|
|
464
|
+
// + post-morning catchups) and retry chains where the second session
|
|
465
|
+
// starts within minutes of the first.
|
|
466
|
+
//
|
|
467
|
+
// Limitation: the SDK still reads CLAUDE.md and .claude/skills/ from
|
|
468
|
+
// cwd at session init. Different event types use different profiles
|
|
469
|
+
// (routine.md vs conversational.md) and skill subsets, so the file
|
|
470
|
+
// layer differs across event types → no cross-event-type cache hit.
|
|
471
|
+
// This flag only helps same-type back-to-back sessions.
|
|
472
|
+
excludeDynamicSections: true,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Expand CLI-style aliases ("opus", "sonnet") to their current canonical
|
|
477
|
+
* API IDs. Unrecognised strings pass through unchanged so custom or
|
|
478
|
+
* fine-tuned model IDs reach the SDK verbatim.
|
|
479
|
+
*
|
|
480
|
+
* NOTE: `"opus"` now resolves to Opus 4.7 (previously 4.6). Legacy
|
|
481
|
+
* `agent_schedule.model = 'opus'` rows will silently route to 4.7 on the
|
|
482
|
+
* next dispatch — intentional, since the alias means "latest Opus".
|
|
483
|
+
*/
|
|
484
|
+
resolveActualModelId(modelId) {
|
|
485
|
+
if (modelId === "opus") {
|
|
486
|
+
return DEFAULT_CLAUDE_HIGH_MODEL;
|
|
487
|
+
}
|
|
488
|
+
if (modelId === "sonnet") {
|
|
489
|
+
return DEFAULT_CLAUDE_MEDIUM_MODEL;
|
|
490
|
+
}
|
|
491
|
+
return modelId;
|
|
492
|
+
}
|
|
493
|
+
async execute(params, streamCallbacks) {
|
|
494
|
+
return await this.runWithRetry(() => this.executeOnce(params, streamCallbacks), { eventType: params.event.type, modelId: params.modelId });
|
|
495
|
+
}
|
|
496
|
+
async executeOnce(params, streamCallbacks) {
|
|
497
|
+
const { prompt, context, event, modelId, maxTurns, maxBudgetUsd, sessionDir, persistSession = false, conversationHistory, webSearchEnabled = false, } = params;
|
|
498
|
+
const fullPrompt = buildExecutionPrompt(prompt, context, event, conversationHistory);
|
|
499
|
+
const startMs = Date.now();
|
|
500
|
+
const actualModelId = this.resolveActualModelId(modelId);
|
|
501
|
+
// If caller provided a persistent sessionDir, use it (non-DM Opus sessions
|
|
502
|
+
// that may be resumed later). Otherwise create a disposable temp dir.
|
|
503
|
+
// The disposable path also receives user skills so agent DMs that don't
|
|
504
|
+
// take the persistent-workdir path can still discover user-authored skills.
|
|
505
|
+
const useSessionDir = sessionDir ?? createSessionWorkdir(this.config.workspaceDir, event.type, `${this.config.dataDir}/skills`, {
|
|
506
|
+
backendId: this.backendId,
|
|
507
|
+
processKey: params.processKey,
|
|
508
|
+
character: this.config.character,
|
|
509
|
+
});
|
|
510
|
+
const isOwnedTempDir = !sessionDir;
|
|
511
|
+
const daemonReadToken = this.readTokenManager?.issue(useSessionDir) ?? this.readToken;
|
|
512
|
+
const mcp = await this.materializeMcp(useSessionDir, params.processKey);
|
|
513
|
+
const delegatedTools = this.getDelegatedClaudeTools();
|
|
514
|
+
const sessionDeniedTools = this.getSessionDeniedTools();
|
|
515
|
+
logger.info({
|
|
516
|
+
eventType: event.type,
|
|
517
|
+
model: actualModelId,
|
|
518
|
+
maxTurns,
|
|
519
|
+
promptLen: fullPrompt.length,
|
|
520
|
+
mcpServers: mcp.servers.map((s) => s.id),
|
|
521
|
+
delegatedToolCount: delegatedTools.length,
|
|
522
|
+
sessionDeniedToolCount: sessionDeniedTools.length,
|
|
523
|
+
}, "Agent execute started");
|
|
524
|
+
try {
|
|
525
|
+
const allowMode = this.config.claudeExecutionPermissionMode === "allow";
|
|
526
|
+
// P22 §3.4 step 4 — when the dispatcher pins a per-execute
|
|
527
|
+
// `allowedToolsOverride`, suspend Allow mode for this run: the
|
|
528
|
+
// optimizer agent must NOT receive `bypassPermissions`, regardless
|
|
529
|
+
// of the operator's per-backend Execution Mode setting. We swap
|
|
530
|
+
// back to strict `dontAsk` + the explicit allowedTools list. The
|
|
531
|
+
// ALWAYS_DISALLOWED_TOOLS layer still applies.
|
|
532
|
+
const optimizerClampActive = Array.isArray(params.allowedToolsOverride) && params.allowedToolsOverride.length > 0;
|
|
533
|
+
const stream = query({
|
|
534
|
+
prompt: fullPrompt,
|
|
535
|
+
options: {
|
|
536
|
+
model: actualModelId,
|
|
537
|
+
maxTurns,
|
|
538
|
+
maxBudgetUsd,
|
|
539
|
+
effort: actualModelId.includes("opus") ? "high" : "medium",
|
|
540
|
+
cwd: useSessionDir,
|
|
541
|
+
env: {
|
|
542
|
+
...buildDaemonApiCliEnv(useSessionDir, this.config.apiPort, {
|
|
543
|
+
readToken: daemonReadToken,
|
|
544
|
+
sessionBackend: "claude",
|
|
545
|
+
sessionId: params.sessionDbId,
|
|
546
|
+
eventCorrelationId: event.correlationId,
|
|
547
|
+
}),
|
|
548
|
+
...mcp.env,
|
|
549
|
+
...(params.turnToken ? { PA_TURN_TOKEN: params.turnToken } : {}),
|
|
550
|
+
},
|
|
551
|
+
systemPrompt: this.buildSystemPrompt(),
|
|
552
|
+
...(optimizerClampActive
|
|
553
|
+
? {
|
|
554
|
+
permissionMode: "dontAsk",
|
|
555
|
+
allowedTools: [...params.allowedToolsOverride],
|
|
556
|
+
disallowedTools: [
|
|
557
|
+
...ALWAYS_DISALLOWED_TOOLS,
|
|
558
|
+
...this.config.disallowedTools,
|
|
559
|
+
...mcp.disallowedTools,
|
|
560
|
+
...sessionDeniedTools,
|
|
561
|
+
],
|
|
562
|
+
}
|
|
563
|
+
: allowMode
|
|
564
|
+
? {
|
|
565
|
+
permissionMode: "bypassPermissions",
|
|
566
|
+
allowDangerouslySkipPermissions: true,
|
|
567
|
+
disallowedTools: [
|
|
568
|
+
...ALWAYS_DISALLOWED_TOOLS,
|
|
569
|
+
...mcp.disallowedTools,
|
|
570
|
+
...sessionDeniedTools,
|
|
571
|
+
],
|
|
572
|
+
}
|
|
573
|
+
: {
|
|
574
|
+
permissionMode: "dontAsk",
|
|
575
|
+
allowedTools: this.getAllowedTools(webSearchEnabled, delegatedTools),
|
|
576
|
+
disallowedTools: [
|
|
577
|
+
...ALWAYS_DISALLOWED_TOOLS,
|
|
578
|
+
...this.config.disallowedTools,
|
|
579
|
+
...mcp.disallowedTools,
|
|
580
|
+
...sessionDeniedTools,
|
|
581
|
+
],
|
|
582
|
+
}),
|
|
583
|
+
...(mcp.claudeMcpServers
|
|
584
|
+
? {
|
|
585
|
+
mcpServers: mcp.claudeMcpServers,
|
|
586
|
+
}
|
|
587
|
+
: {}),
|
|
588
|
+
hooks: this.getSecurityHooks(allowMode),
|
|
589
|
+
persistSession,
|
|
590
|
+
includePartialMessages: !!streamCallbacks,
|
|
591
|
+
...this.buildAdvisorSettings(),
|
|
592
|
+
},
|
|
593
|
+
});
|
|
594
|
+
const result = await this.withTimeout(stream, () => this.consumeStream(stream, actualModelId, startMs, streamCallbacks, event.type), this.config.executeTimeoutMinutes);
|
|
595
|
+
logger.info({ eventType: event.type, model: actualModelId, durationMs: result.durationMs, costUsd: result.costUsd, numTurns: result.numTurns, isError: result.isError }, "Agent execute completed");
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
598
|
+
catch (err) {
|
|
599
|
+
logger.error({ err, eventType: event.type, model: actualModelId, durationMs: Date.now() - startMs }, "Agent execute failed");
|
|
600
|
+
throw err;
|
|
601
|
+
}
|
|
602
|
+
finally {
|
|
603
|
+
if (isOwnedTempDir) {
|
|
604
|
+
this.readTokenManager?.revoke(useSessionDir);
|
|
605
|
+
}
|
|
606
|
+
// Only clean up temp dirs we created; caller-owned dirs have their own lifecycle
|
|
607
|
+
if (isOwnedTempDir) {
|
|
608
|
+
cleanupSessionWorkdir(useSessionDir);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async executeResume(params, streamCallbacks) {
|
|
613
|
+
return await this.runWithRetry(() => this.executeResumeOnce(params, streamCallbacks), { sessionId: params.sessionId, modelId: params.modelId });
|
|
614
|
+
}
|
|
615
|
+
async executeResumeOnce(params, streamCallbacks) {
|
|
616
|
+
const { sessionId, message, modelId, sessionDir, webSearchEnabled = false } = params;
|
|
617
|
+
const startMs = Date.now();
|
|
618
|
+
const actualModelId = this.resolveActualModelId(modelId);
|
|
619
|
+
const isOpusTier = actualModelId.includes("opus");
|
|
620
|
+
const maxTurns = params.maxTurns ?? (isOpusTier ? 300 : 50);
|
|
621
|
+
const maxBudgetUsd = params.maxBudgetUsd ?? (isOpusTier ? 5.0 : 1.0);
|
|
622
|
+
if (!sessionDir) {
|
|
623
|
+
throw new Error("sessionDir is required for executeResume — SDK stores session history per-cwd");
|
|
624
|
+
}
|
|
625
|
+
const daemonReadToken = this.readTokenManager?.issue(sessionDir) ?? this.readToken;
|
|
626
|
+
// Resume is always a reactive DM continuation (owner in the loop), so the
|
|
627
|
+
// approve-tier MCP strip stays off even for otherwise-autonomous process
|
|
628
|
+
// keys. `materializeMcp` short-circuits on no context / no servers.
|
|
629
|
+
const mcp = await this.materializeMcp(sessionDir, undefined);
|
|
630
|
+
const delegatedTools = this.getDelegatedClaudeTools();
|
|
631
|
+
const sessionDeniedTools = this.getSessionDeniedTools();
|
|
632
|
+
logger.info({
|
|
633
|
+
sessionId,
|
|
634
|
+
model: actualModelId,
|
|
635
|
+
maxTurns,
|
|
636
|
+
mcpServers: mcp.servers.map((s) => s.id),
|
|
637
|
+
delegatedToolCount: delegatedTools.length,
|
|
638
|
+
sessionDeniedToolCount: sessionDeniedTools.length,
|
|
639
|
+
}, "Agent resume started");
|
|
640
|
+
// Use the same cwd as the original execute() so the SDK can find
|
|
641
|
+
// the session history (stored per-cwd in ~/.claude/projects/).
|
|
642
|
+
const allowMode = this.config.claudeExecutionPermissionMode === "allow";
|
|
643
|
+
const stream = query({
|
|
644
|
+
prompt: message,
|
|
645
|
+
options: {
|
|
646
|
+
resume: sessionId,
|
|
647
|
+
maxTurns,
|
|
648
|
+
maxBudgetUsd,
|
|
649
|
+
cwd: sessionDir,
|
|
650
|
+
env: {
|
|
651
|
+
...buildDaemonApiCliEnv(sessionDir, this.config.apiPort, {
|
|
652
|
+
readToken: daemonReadToken,
|
|
653
|
+
sessionBackend: "claude",
|
|
654
|
+
sessionId: params.sessionDbId,
|
|
655
|
+
eventCorrelationId: params.eventCorrelationId,
|
|
656
|
+
}),
|
|
657
|
+
...mcp.env,
|
|
658
|
+
...(params.turnToken ? { PA_TURN_TOKEN: params.turnToken } : {}),
|
|
659
|
+
},
|
|
660
|
+
systemPrompt: this.buildSystemPrompt(),
|
|
661
|
+
...(allowMode
|
|
662
|
+
? {
|
|
663
|
+
permissionMode: "bypassPermissions",
|
|
664
|
+
allowDangerouslySkipPermissions: true,
|
|
665
|
+
disallowedTools: [
|
|
666
|
+
...ALWAYS_DISALLOWED_TOOLS,
|
|
667
|
+
...mcp.disallowedTools,
|
|
668
|
+
...sessionDeniedTools,
|
|
669
|
+
],
|
|
670
|
+
}
|
|
671
|
+
: {
|
|
672
|
+
permissionMode: "dontAsk",
|
|
673
|
+
allowedTools: this.getAllowedTools(webSearchEnabled, delegatedTools),
|
|
674
|
+
disallowedTools: [
|
|
675
|
+
...ALWAYS_DISALLOWED_TOOLS,
|
|
676
|
+
...this.config.disallowedTools,
|
|
677
|
+
...mcp.disallowedTools,
|
|
678
|
+
...sessionDeniedTools,
|
|
679
|
+
],
|
|
680
|
+
}),
|
|
681
|
+
...(mcp.claudeMcpServers
|
|
682
|
+
? { mcpServers: mcp.claudeMcpServers }
|
|
683
|
+
: {}),
|
|
684
|
+
hooks: this.getSecurityHooks(allowMode),
|
|
685
|
+
includePartialMessages: !!streamCallbacks,
|
|
686
|
+
...this.buildAdvisorSettings(),
|
|
687
|
+
},
|
|
688
|
+
});
|
|
689
|
+
return await this.withTimeout(stream, () => this.consumeStream(stream, actualModelId, startMs, streamCallbacks, "message.received"), this.config.executeTimeoutMinutes);
|
|
690
|
+
}
|
|
691
|
+
async runWithRetry(fn, context) {
|
|
692
|
+
let lastError;
|
|
693
|
+
for (let attempt = 0; attempt <= ClaudeCodeCore.MAX_RETRIES; attempt++) {
|
|
694
|
+
try {
|
|
695
|
+
return await fn();
|
|
696
|
+
}
|
|
697
|
+
catch (error) {
|
|
698
|
+
lastError = error;
|
|
699
|
+
if (attempt < ClaudeCodeCore.MAX_RETRIES &&
|
|
700
|
+
this.isRetryableExecutionError(error)) {
|
|
701
|
+
logger.warn({
|
|
702
|
+
...context,
|
|
703
|
+
attempt: attempt + 1,
|
|
704
|
+
maxRetries: ClaudeCodeCore.MAX_RETRIES,
|
|
705
|
+
error: this.getErrorMessage(error),
|
|
706
|
+
}, "Retrying Claude backend after transient failure");
|
|
707
|
+
await this.sleep(ClaudeCodeCore.RETRY_DELAY_MS);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
throw this.classifyExecutionError(error);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
throw this.classifyExecutionError(lastError);
|
|
714
|
+
}
|
|
715
|
+
isRetryableExecutionError(error) {
|
|
716
|
+
if (error instanceof BackendQuotaError ||
|
|
717
|
+
error instanceof BackendDecisiveFailure) {
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
if (error instanceof AgentTimeoutError) {
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
if (isClaudeCodeQuotaError(error)) {
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
const status = this.getErrorStatus(error);
|
|
727
|
+
if (typeof status === "number" && status >= 500) {
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
const code = this.getErrorCode(error)?.toUpperCase();
|
|
731
|
+
if (code &&
|
|
732
|
+
["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "EPIPE", "ECONNABORTED", "ENOTFOUND"].includes(code)) {
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
return /network error|fetch failed|socket hang up|connection reset|timed out/i.test(this.getErrorMessage(error));
|
|
736
|
+
}
|
|
737
|
+
/** Visible for testing. */
|
|
738
|
+
classifyExecutionError(error) {
|
|
739
|
+
if (error instanceof BackendQuotaError ||
|
|
740
|
+
error instanceof BackendDecisiveFailure) {
|
|
741
|
+
return error;
|
|
742
|
+
}
|
|
743
|
+
const quotaError = this.toBackendQuotaError(error);
|
|
744
|
+
if (quotaError) {
|
|
745
|
+
return quotaError;
|
|
746
|
+
}
|
|
747
|
+
if (error instanceof AgentTimeoutError) {
|
|
748
|
+
return new BackendDecisiveFailure(this.backendId, "timeout", error);
|
|
749
|
+
}
|
|
750
|
+
if (this.isAuthError(error)) {
|
|
751
|
+
return new BackendDecisiveFailure(this.backendId, "auth", error);
|
|
752
|
+
}
|
|
753
|
+
return new BackendDecisiveFailure(this.backendId, "other_non_retryable", error);
|
|
754
|
+
}
|
|
755
|
+
toBackendQuotaError(error) {
|
|
756
|
+
if (isClaudeCodeMaxBudgetError(error)) {
|
|
757
|
+
return new BackendQuotaError(this.backendId, "max_budget_usd", null, this.getErrorMessage(error));
|
|
758
|
+
}
|
|
759
|
+
if (!isClaudeCodeQuotaError(error)) {
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
762
|
+
const hint = extractClaudeCodeQuotaResetHint(error);
|
|
763
|
+
return new BackendQuotaError(this.backendId, this.getErrorCode(error) ?? "rate_limited", hint ? this.toBackendQuotaResetHint(hint) : null, this.getErrorMessage(error));
|
|
764
|
+
}
|
|
765
|
+
toBackendQuotaResetHint(hint) {
|
|
766
|
+
return {
|
|
767
|
+
hour: hint.hour,
|
|
768
|
+
minute: hint.minute,
|
|
769
|
+
timeZone: hint.timeZone,
|
|
770
|
+
rawLabel: hint.rawLabel,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
isAuthError(error) {
|
|
774
|
+
const status = this.getErrorStatus(error);
|
|
775
|
+
if (status === 401 || status === 403) {
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
const code = this.getErrorCode(error)?.toLowerCase() ?? "";
|
|
779
|
+
const type = this.getErrorType(error)?.toLowerCase() ?? "";
|
|
780
|
+
if (code.includes("auth") ||
|
|
781
|
+
code.includes("forbidden") ||
|
|
782
|
+
code.includes("unauthorized") ||
|
|
783
|
+
type.includes("auth") ||
|
|
784
|
+
type.includes("forbidden") ||
|
|
785
|
+
type.includes("unauthorized")) {
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
return /unauthorized|forbidden|authentication|invalid api key|login required/i.test(this.getErrorMessage(error));
|
|
789
|
+
}
|
|
790
|
+
getErrorStatus(error) {
|
|
791
|
+
return typeof error === "object" && error !== null && "status" in error
|
|
792
|
+
? error.status
|
|
793
|
+
: undefined;
|
|
794
|
+
}
|
|
795
|
+
getErrorCode(error) {
|
|
796
|
+
return typeof error === "object" && error !== null && typeof error.code === "string"
|
|
797
|
+
? error.code
|
|
798
|
+
: undefined;
|
|
799
|
+
}
|
|
800
|
+
getErrorType(error) {
|
|
801
|
+
return typeof error === "object" && error !== null && typeof error.type === "string"
|
|
802
|
+
? error.type
|
|
803
|
+
: undefined;
|
|
804
|
+
}
|
|
805
|
+
getErrorMessage(error) {
|
|
806
|
+
if (error instanceof Error) {
|
|
807
|
+
return error.message;
|
|
808
|
+
}
|
|
809
|
+
if (typeof error === "string") {
|
|
810
|
+
return error;
|
|
811
|
+
}
|
|
812
|
+
return "Claude backend execution failed";
|
|
813
|
+
}
|
|
814
|
+
async sleep(ms) {
|
|
815
|
+
await new Promise((resolve) => {
|
|
816
|
+
const timer = setTimeout(resolve, ms);
|
|
817
|
+
timer.unref?.();
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
async summarize(conversationText) {
|
|
821
|
+
const prompt = buildSummaryPrompt(conversationText);
|
|
822
|
+
const startMs = Date.now();
|
|
823
|
+
const sessionDir = createSessionWorkdir(this.config.workspaceDir, "message.received", undefined, { backendId: this.backendId, character: this.config.character });
|
|
824
|
+
const daemonReadToken = this.readTokenManager?.issue(sessionDir) ?? this.readToken;
|
|
825
|
+
try {
|
|
826
|
+
// Light-tier model for cheap conversation summarization. Tracks the
|
|
827
|
+
// canonical default in MODEL_REGISTRY so a Sonnet generation bump
|
|
828
|
+
// propagates here for free, mirroring how Codex/Gemini do it via
|
|
829
|
+
// their own `pickSummaryModel()` helpers.
|
|
830
|
+
const summaryModel = DEFAULT_CLAUDE_MEDIUM_MODEL;
|
|
831
|
+
const stream = query({
|
|
832
|
+
prompt,
|
|
833
|
+
options: {
|
|
834
|
+
model: summaryModel,
|
|
835
|
+
maxTurns: 1,
|
|
836
|
+
maxBudgetUsd: 0.25,
|
|
837
|
+
cwd: sessionDir,
|
|
838
|
+
env: buildDaemonApiCliEnv(sessionDir, this.config.apiPort, { readToken: daemonReadToken, sessionBackend: "claude" }),
|
|
839
|
+
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
840
|
+
permissionMode: "dontAsk",
|
|
841
|
+
allowedTools: [],
|
|
842
|
+
},
|
|
843
|
+
});
|
|
844
|
+
const result = await this.withTimeout(stream, () => this.consumeStream(stream, summaryModel, startMs), this.config.executeTimeoutMinutes);
|
|
845
|
+
return result.output || "";
|
|
846
|
+
}
|
|
847
|
+
finally {
|
|
848
|
+
this.readTokenManager?.revoke(sessionDir);
|
|
849
|
+
cleanupSessionWorkdir(sessionDir);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
async checkAuth() {
|
|
853
|
+
// Presence check only — this is the cheap gate used from the reactive
|
|
854
|
+
// execute path. Detailed probe lives in checkAuthDetailed().
|
|
855
|
+
const cloud = detectCloudProviderEnv();
|
|
856
|
+
if (cloud) {
|
|
857
|
+
if (cloud.missing.length > 0) {
|
|
858
|
+
return {
|
|
859
|
+
ok: false,
|
|
860
|
+
reason: `${cloud.flagEnvVar}=1 but missing required env vars: ${cloud.missing.join(", ")}`,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
return { ok: true, method: cloud.method };
|
|
864
|
+
}
|
|
865
|
+
const rawApiKey = process.env.ANTHROPIC_API_KEY?.trim();
|
|
866
|
+
if (rawApiKey) {
|
|
867
|
+
if (!isPlausibleAnthropicApiKey(rawApiKey)) {
|
|
868
|
+
return {
|
|
869
|
+
ok: false,
|
|
870
|
+
reason: "ANTHROPIC_API_KEY is set but does not look like an Anthropic key (expected `sk-ant-…`).",
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
return { ok: true, method: "api_key" };
|
|
874
|
+
}
|
|
875
|
+
if (!this.cliPath) {
|
|
876
|
+
return {
|
|
877
|
+
ok: false,
|
|
878
|
+
reason: "Claude Code CLI is not installed or not on PATH. Run `npm install -g @anthropic-ai/claude-code`.",
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
return { ok: true, method: "cli_login" };
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Detailed auth probe used by AuthHealthMonitor and the dashboard setup
|
|
885
|
+
* wizard. Two modes:
|
|
886
|
+
* - **API key** (`ANTHROPIC_API_KEY`): format check + server-side probe
|
|
887
|
+
* via `probeApiKeyServerSide("anthropic", ...)` (roadmap §9.1).
|
|
888
|
+
* Throws on network/timeout so `checkAll()` records `probe_network_error`.
|
|
889
|
+
* - **CLI login**: reads `~/.claude/credentials.json` for `refreshToken`.
|
|
890
|
+
* Never writes to the Keychain or credentials file — refresh is left
|
|
891
|
+
* to the CLI (Phase 0 confirmed rotating refresh_tokens; daemon-driven
|
|
892
|
+
* refresh would race and corrupt state).
|
|
893
|
+
*/
|
|
894
|
+
async checkAuthDetailed() {
|
|
895
|
+
const cloud = detectCloudProviderEnv();
|
|
896
|
+
if (cloud) {
|
|
897
|
+
if (cloud.missing.length > 0) {
|
|
898
|
+
return {
|
|
899
|
+
ok: false,
|
|
900
|
+
status: "missing",
|
|
901
|
+
method: cloud.method,
|
|
902
|
+
detail: `${cloud.flagEnvVar}=1 but missing: ${cloud.missing.join(", ")}`,
|
|
903
|
+
recoveryCommand: `Set the missing env vars or unset ${cloud.flagEnvVar}`,
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
// Real auth happens inside the SDK against AWS / GCP / Azure. The
|
|
907
|
+
// daemon does not run a server-side probe for cloud providers — the
|
|
908
|
+
// first execution will surface any credential failure. Mark the
|
|
909
|
+
// status as `ok` here so the dashboard reports "Configured (cloud)";
|
|
910
|
+
// AuthHealthMonitor still re-runs this check hourly so a malformed
|
|
911
|
+
// env (env vars cleared after launch) flips the cache to `missing`.
|
|
912
|
+
return {
|
|
913
|
+
ok: true,
|
|
914
|
+
status: "ok",
|
|
915
|
+
method: cloud.method,
|
|
916
|
+
detail: `Configured via ${cloud.label} — runtime auth verified by Claude Code SDK`,
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
const rawApiKey = process.env.ANTHROPIC_API_KEY?.trim();
|
|
920
|
+
if (rawApiKey) {
|
|
921
|
+
if (!isPlausibleAnthropicApiKey(rawApiKey)) {
|
|
922
|
+
return {
|
|
923
|
+
ok: false,
|
|
924
|
+
status: "expired",
|
|
925
|
+
method: "api_key",
|
|
926
|
+
detail: "ANTHROPIC_API_KEY does not match Anthropic key format (expected `sk-ant-…`).",
|
|
927
|
+
recoveryCommand: "Unset ANTHROPIC_API_KEY or replace it with a valid Anthropic API key",
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
// Format is plausible — attempt a server-side probe to detect
|
|
931
|
+
// revoked keys within 1 hourly cycle (roadmap §9.1). On network
|
|
932
|
+
// failure, the probe throws and the caller (checkAll or check-auth
|
|
933
|
+
// route) records `probe_network_error` without flipping DB cache.
|
|
934
|
+
const probe = await probeApiKeyServerSide("anthropic", rawApiKey);
|
|
935
|
+
return {
|
|
936
|
+
ok: probe.ok,
|
|
937
|
+
status: probe.ok ? "ok" : "expired",
|
|
938
|
+
method: "api_key",
|
|
939
|
+
detail: probe.detail,
|
|
940
|
+
...(!probe.ok && {
|
|
941
|
+
recoveryCommand: "Unset ANTHROPIC_API_KEY or replace it with a valid Anthropic API key",
|
|
942
|
+
}),
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
if (!this.cliPath) {
|
|
946
|
+
return {
|
|
947
|
+
ok: false,
|
|
948
|
+
status: "missing",
|
|
949
|
+
method: "cli_login",
|
|
950
|
+
detail: "Claude Code CLI not found on PATH",
|
|
951
|
+
recoveryCommand: "npm install -g @anthropic-ai/claude-code",
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
const bundle = await readClaudeCredentials();
|
|
955
|
+
if (!bundle) {
|
|
956
|
+
return {
|
|
957
|
+
ok: false,
|
|
958
|
+
status: "expired",
|
|
959
|
+
method: "cli_login",
|
|
960
|
+
detail: "No Claude credentials found",
|
|
961
|
+
recoveryCommand: "claude auth login",
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
if (!bundle.refreshToken) {
|
|
965
|
+
return {
|
|
966
|
+
ok: false,
|
|
967
|
+
status: "expired",
|
|
968
|
+
method: "cli_login",
|
|
969
|
+
detail: "Credentials lack refresh_token — run `claude auth login`",
|
|
970
|
+
recoveryCommand: "claude auth login",
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
return { ok: true, status: "ok", method: "oauth" };
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Phase 5 §4.11 live probe. Claude Code 2.1+ may defer large MCP tool
|
|
977
|
+
* manifests behind `ToolSearch`, so the old zero-turn `system.init.tools`
|
|
978
|
+
* capture is no longer sufficient for hosted claude.ai connectors like
|
|
979
|
+
* Gmail and Google Calendar. Run a tightly-scoped turn that can only use
|
|
980
|
+
* the read-only ToolSearch catalog, then extract connector names from
|
|
981
|
+
* both returned `tool_reference` blocks and the final printed answer.
|
|
982
|
+
*/
|
|
983
|
+
async probeTools() {
|
|
984
|
+
const sessionDir = createSessionWorkdir(this.config.workspaceDir, "message.received", undefined, { backendId: this.backendId, character: this.config.character });
|
|
985
|
+
const daemonReadToken = this.readTokenManager?.issue(sessionDir) ?? this.readToken;
|
|
986
|
+
try {
|
|
987
|
+
const stream = query({
|
|
988
|
+
prompt: CLAUDE_PROBE_TOOLS_PROMPT,
|
|
989
|
+
options: {
|
|
990
|
+
model: DEFAULT_CLAUDE_MEDIUM_MODEL,
|
|
991
|
+
maxTurns: 3,
|
|
992
|
+
maxBudgetUsd: 0.25,
|
|
993
|
+
cwd: sessionDir,
|
|
994
|
+
env: buildDaemonApiCliEnv(sessionDir, this.config.apiPort, { readToken: daemonReadToken, sessionBackend: "claude" }),
|
|
995
|
+
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
996
|
+
permissionMode: "dontAsk",
|
|
997
|
+
allowedTools: ["ToolSearch"],
|
|
998
|
+
},
|
|
999
|
+
});
|
|
1000
|
+
const tools = await new Promise((resolve, reject) => {
|
|
1001
|
+
const timer = setTimeout(() => {
|
|
1002
|
+
void (async () => {
|
|
1003
|
+
try {
|
|
1004
|
+
await stream.return?.(undefined);
|
|
1005
|
+
}
|
|
1006
|
+
catch {
|
|
1007
|
+
/* stream already closed */
|
|
1008
|
+
}
|
|
1009
|
+
})();
|
|
1010
|
+
reject(new Error("probeTools: timeout waiting for ToolSearch probe"));
|
|
1011
|
+
}, 60_000);
|
|
1012
|
+
timer.unref?.();
|
|
1013
|
+
void (async () => {
|
|
1014
|
+
const collected = [];
|
|
1015
|
+
let terminalError = null;
|
|
1016
|
+
try {
|
|
1017
|
+
for await (const message of stream) {
|
|
1018
|
+
collected.push(...extractClaudeProbeTools(message));
|
|
1019
|
+
if (message.type === "result") {
|
|
1020
|
+
const result = message;
|
|
1021
|
+
if (result.is_error) {
|
|
1022
|
+
terminalError = describeClaudeProbeResultError(result);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
clearTimeout(timer);
|
|
1027
|
+
const deduped = Array.from(new Set(collected));
|
|
1028
|
+
if (terminalError && deduped.length === 0) {
|
|
1029
|
+
reject(new Error(`probeTools: ${terminalError}`));
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
resolve(deduped);
|
|
1033
|
+
}
|
|
1034
|
+
catch (err) {
|
|
1035
|
+
clearTimeout(timer);
|
|
1036
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
1037
|
+
}
|
|
1038
|
+
})();
|
|
1039
|
+
});
|
|
1040
|
+
logger.info({ toolCount: tools.length }, "Live probe collected tool manifest");
|
|
1041
|
+
return tools;
|
|
1042
|
+
}
|
|
1043
|
+
finally {
|
|
1044
|
+
this.readTokenManager?.revoke(sessionDir);
|
|
1045
|
+
cleanupSessionWorkdir(sessionDir);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
listModels() {
|
|
1049
|
+
const defaultMediumModel = DEFAULT_CLAUDE_MEDIUM_MODEL;
|
|
1050
|
+
const defaultHighModel = DEFAULT_CLAUDE_HIGH_MODEL;
|
|
1051
|
+
const configuredModels = [
|
|
1052
|
+
{
|
|
1053
|
+
...(findRegisteredModel(this.backendId, defaultMediumModel) ?? {
|
|
1054
|
+
backendId: this.backendId,
|
|
1055
|
+
modelId: defaultMediumModel,
|
|
1056
|
+
label: defaultMediumModel,
|
|
1057
|
+
tier: "medium",
|
|
1058
|
+
available: true,
|
|
1059
|
+
}),
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
...(findRegisteredModel(this.backendId, defaultHighModel) ?? {
|
|
1063
|
+
backendId: this.backendId,
|
|
1064
|
+
modelId: defaultHighModel,
|
|
1065
|
+
label: defaultHighModel,
|
|
1066
|
+
tier: "high",
|
|
1067
|
+
available: true,
|
|
1068
|
+
}),
|
|
1069
|
+
},
|
|
1070
|
+
];
|
|
1071
|
+
const models = [...configuredModels, ...getModelsForBackend(this.backendId)];
|
|
1072
|
+
return models.filter((model, index, list) => list.findIndex((candidate) => candidate.modelId === model.modelId) === index);
|
|
1073
|
+
}
|
|
1074
|
+
async consumeStream(stream, model, startMs, streamCallbacks, eventType) {
|
|
1075
|
+
let output = "";
|
|
1076
|
+
let streamedOutput = "";
|
|
1077
|
+
let sessionId = null;
|
|
1078
|
+
let costUsd = 0;
|
|
1079
|
+
let usage = {
|
|
1080
|
+
inputTokens: 0,
|
|
1081
|
+
outputTokens: 0,
|
|
1082
|
+
cacheCreationInputTokens: 0,
|
|
1083
|
+
cacheReadInputTokens: 0,
|
|
1084
|
+
};
|
|
1085
|
+
const modelUsage = {};
|
|
1086
|
+
let numTurns = 0;
|
|
1087
|
+
let durationApiMs = 0;
|
|
1088
|
+
let isError = false;
|
|
1089
|
+
let stopReason = null;
|
|
1090
|
+
// Context-update tracking (Phase 6 + H2 refinement).
|
|
1091
|
+
const contextUpdateCalls = new Set();
|
|
1092
|
+
const failedContextUpdates = new Set();
|
|
1093
|
+
// Advisor tool invocation count — incremented each time the SDK stream
|
|
1094
|
+
// emits a `server_tool_use` content block with `name: "advisor"`. This is
|
|
1095
|
+
// the `advisor_20260301` Anthropic-hosted tool. See `docs/advisor.md`.
|
|
1096
|
+
let advisorCallCount = 0;
|
|
1097
|
+
// B-003 Phase 4.4 — MCP tool result matching.
|
|
1098
|
+
// Maps tool_use_id → { rowId, startMs } so that when the SDK delivers the
|
|
1099
|
+
// matching tool_result block we can backfill ok/error/duration_ms.
|
|
1100
|
+
// startMs is set when the assistant message containing the tool_use block is
|
|
1101
|
+
// processed. The resulting duration_ms therefore covers SDK dispatch latency
|
|
1102
|
+
// (model → executor → result delivery) rather than pure tool execution time.
|
|
1103
|
+
const mcpPendingResults = new Map();
|
|
1104
|
+
try {
|
|
1105
|
+
for await (const message of stream) {
|
|
1106
|
+
if (message.type === "system" && message.subtype === "init") {
|
|
1107
|
+
const sysMsg = message;
|
|
1108
|
+
sessionId = sysMsg.session_id;
|
|
1109
|
+
logger.debug({ sessionId, model: sysMsg.model }, "Session initialized");
|
|
1110
|
+
}
|
|
1111
|
+
else if (message.type === "stream_event") {
|
|
1112
|
+
// Forward streaming text deltas to the caller (e.g., dashboard SSE)
|
|
1113
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- SDK stream_event lacks typed .event
|
|
1114
|
+
const event = message.event;
|
|
1115
|
+
if (event?.type === "content_block_delta" && event?.delta?.type === "text_delta") {
|
|
1116
|
+
streamedOutput += event.delta.text;
|
|
1117
|
+
streamCallbacks?.onText?.(event.delta.text);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
else if (message.type === "assistant") {
|
|
1121
|
+
// Track Bash tool_use blocks that hit the context API + server-side
|
|
1122
|
+
// advisor tool invocations.
|
|
1123
|
+
const assistantMsg = message;
|
|
1124
|
+
const blocks = assistantMsg.message?.content;
|
|
1125
|
+
if (Array.isArray(blocks)) {
|
|
1126
|
+
for (const block of blocks) {
|
|
1127
|
+
if (!block || typeof block !== "object")
|
|
1128
|
+
continue;
|
|
1129
|
+
const blockType = block.type;
|
|
1130
|
+
const blockName = block.name;
|
|
1131
|
+
if (blockType === "tool_use" && blockName === "Bash") {
|
|
1132
|
+
const toolUseId = block.id;
|
|
1133
|
+
const cmd = (block.input?.command ?? "");
|
|
1134
|
+
if (typeof toolUseId === "string" &&
|
|
1135
|
+
typeof cmd === "string" &&
|
|
1136
|
+
ClaudeCodeCore.isContextUpdateCommand(cmd)) {
|
|
1137
|
+
contextUpdateCalls.add(toolUseId);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
else if (blockType === "tool_use" &&
|
|
1141
|
+
typeof blockName === "string" &&
|
|
1142
|
+
blockName.startsWith("mcp__")) {
|
|
1143
|
+
// B-003 Phase 4.4 — persist MCP tool call to `mcp_tool_calls`.
|
|
1144
|
+
// Capture tool_use_id + start time so we can match the result
|
|
1145
|
+
// block that arrives later in a `user` message.
|
|
1146
|
+
const parsed = parseMcpToolName(blockName);
|
|
1147
|
+
if (parsed) {
|
|
1148
|
+
logger.debug({
|
|
1149
|
+
serverId: parsed.serverId,
|
|
1150
|
+
toolName: parsed.toolName,
|
|
1151
|
+
sessionId,
|
|
1152
|
+
eventType,
|
|
1153
|
+
}, "mcp.tool_call");
|
|
1154
|
+
if (this.mcpContext?.db) {
|
|
1155
|
+
try {
|
|
1156
|
+
const rowId = logMcpToolCall(this.mcpContext.db, {
|
|
1157
|
+
serverId: parsed.serverId,
|
|
1158
|
+
toolName: parsed.toolName,
|
|
1159
|
+
eventType,
|
|
1160
|
+
sessionId: sessionId ?? undefined,
|
|
1161
|
+
});
|
|
1162
|
+
const toolUseId = block.id;
|
|
1163
|
+
if (typeof toolUseId === "string") {
|
|
1164
|
+
mcpPendingResults.set(toolUseId, { rowId, startMs: Date.now() });
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
catch (err) {
|
|
1168
|
+
logger.warn({ err, serverId: parsed.serverId }, "mcp.tool_call audit insert failed");
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
else if (blockType === "server_tool_use" &&
|
|
1174
|
+
blockName === "advisor") {
|
|
1175
|
+
// The advisor_20260301 server-side tool. The SDK emits one
|
|
1176
|
+
// server_tool_use per invocation; we count them here.
|
|
1177
|
+
advisorCallCount += 1;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
else if (message.type === "user") {
|
|
1183
|
+
// The SDK emits the tool_result that feeds back to the model
|
|
1184
|
+
// as a 'user' message whose content array contains
|
|
1185
|
+
// tool_result blocks. Check is_error on each result matched
|
|
1186
|
+
// against our pending context tool_use ids, and scan the
|
|
1187
|
+
// merged tool output for PA_API_ERROR markers emitted by the
|
|
1188
|
+
// daemon API wrappers.
|
|
1189
|
+
const userMsg = message;
|
|
1190
|
+
const content = userMsg.message?.content;
|
|
1191
|
+
if (Array.isArray(content)) {
|
|
1192
|
+
for (const block of content) {
|
|
1193
|
+
if (block &&
|
|
1194
|
+
block.type === "tool_result") {
|
|
1195
|
+
const toolUseId = block.tool_use_id;
|
|
1196
|
+
const isError = block.is_error === true;
|
|
1197
|
+
if (typeof toolUseId === "string") {
|
|
1198
|
+
// Context-update error tracking.
|
|
1199
|
+
if (contextUpdateCalls.has(toolUseId) && isError) {
|
|
1200
|
+
failedContextUpdates.add(toolUseId);
|
|
1201
|
+
}
|
|
1202
|
+
// B-003 Phase 4.4 — backfill ok/error/duration_ms for MCP tool calls.
|
|
1203
|
+
const pending = mcpPendingResults.get(toolUseId);
|
|
1204
|
+
if (pending && this.mcpContext?.db) {
|
|
1205
|
+
const ok = !isError;
|
|
1206
|
+
const errorText = isError
|
|
1207
|
+
? flattenToolResultContent(block.content).slice(0, 1000) || null
|
|
1208
|
+
: null;
|
|
1209
|
+
const durationMs = Date.now() - pending.startMs;
|
|
1210
|
+
try {
|
|
1211
|
+
updateMcpToolCallResult(this.mcpContext.db, pending.rowId, ok, errorText, durationMs);
|
|
1212
|
+
}
|
|
1213
|
+
catch (err) {
|
|
1214
|
+
logger.warn({ err, rowId: pending.rowId }, "mcp.tool_call result update failed");
|
|
1215
|
+
}
|
|
1216
|
+
mcpPendingResults.delete(toolUseId);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
const resultText = flattenToolResultContent(block.content);
|
|
1220
|
+
const apiErrors = extractSilentApiErrors(resultText);
|
|
1221
|
+
if (apiErrors.length > 0) {
|
|
1222
|
+
logSilentApiErrors(logger, apiErrors, {
|
|
1223
|
+
backendId: this.backendId,
|
|
1224
|
+
sessionId,
|
|
1225
|
+
eventType,
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
else if (message.type === "result") {
|
|
1233
|
+
const r = message;
|
|
1234
|
+
if (r.subtype === "success") {
|
|
1235
|
+
const resultOutput = typeof r.result === "string" ? r.result : "";
|
|
1236
|
+
// Claude SDK can stream assistant text and still report an empty
|
|
1237
|
+
// final result when the turn ends after a tool-only message.
|
|
1238
|
+
output =
|
|
1239
|
+
resultOutput.trim().length > 0
|
|
1240
|
+
? resultOutput
|
|
1241
|
+
: streamedOutput.trim().length > 0
|
|
1242
|
+
? streamedOutput
|
|
1243
|
+
: resultOutput;
|
|
1244
|
+
}
|
|
1245
|
+
sessionId = r.session_id;
|
|
1246
|
+
costUsd = r.total_cost_usd;
|
|
1247
|
+
usage = {
|
|
1248
|
+
inputTokens: r.usage.input_tokens,
|
|
1249
|
+
outputTokens: r.usage.output_tokens,
|
|
1250
|
+
cacheCreationInputTokens: r.usage.cache_creation_input_tokens ?? 0,
|
|
1251
|
+
cacheReadInputTokens: r.usage.cache_read_input_tokens ?? 0,
|
|
1252
|
+
};
|
|
1253
|
+
// Convert SDK modelUsage to our format
|
|
1254
|
+
for (const [modelName, mu] of Object.entries(r.modelUsage)) {
|
|
1255
|
+
modelUsage[modelName] = {
|
|
1256
|
+
inputTokens: mu.inputTokens,
|
|
1257
|
+
outputTokens: mu.outputTokens,
|
|
1258
|
+
costUsd: mu.costUSD,
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
numTurns = r.num_turns;
|
|
1262
|
+
durationApiMs = r.duration_api_ms;
|
|
1263
|
+
isError = r.is_error;
|
|
1264
|
+
stopReason = r.stop_reason;
|
|
1265
|
+
if (r.subtype !== "success") {
|
|
1266
|
+
logger.warn({ subtype: r.subtype, errors: "errors" in r ? r.errors : [] }, "Agent session ended with error");
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
finally {
|
|
1272
|
+
// Always signal stream completion — even on error — so the client's
|
|
1273
|
+
// streaming state is reset (prevents stuck "streaming = true").
|
|
1274
|
+
streamCallbacks?.onEnd?.();
|
|
1275
|
+
}
|
|
1276
|
+
const contextUpdated = contextUpdateCalls.size > failedContextUpdates.size;
|
|
1277
|
+
return {
|
|
1278
|
+
output,
|
|
1279
|
+
sessionId,
|
|
1280
|
+
backendId: this.backendId,
|
|
1281
|
+
modelId: model,
|
|
1282
|
+
costSource: "sdk",
|
|
1283
|
+
costUsd,
|
|
1284
|
+
usage,
|
|
1285
|
+
modelUsage,
|
|
1286
|
+
numTurns,
|
|
1287
|
+
durationMs: Date.now() - startMs,
|
|
1288
|
+
durationApiMs,
|
|
1289
|
+
model,
|
|
1290
|
+
isError,
|
|
1291
|
+
stopReason,
|
|
1292
|
+
contextUpdated,
|
|
1293
|
+
advisorCallCount,
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Race `fn()` against an `executeTimeoutMinutes` wall-clock timer.
|
|
1298
|
+
*
|
|
1299
|
+
* On timeout we must terminate the underlying SDK stream too — otherwise
|
|
1300
|
+
* `Promise.race` just rejects while `query()` keeps running in the
|
|
1301
|
+
* background, which in retry scenarios would produce two concurrent
|
|
1302
|
+
* Claude Code sessions for the same event and double quota consumption.
|
|
1303
|
+
*/
|
|
1304
|
+
async withTimeout(stream, fn, timeoutMinutes) {
|
|
1305
|
+
const timeoutMs = timeoutMinutes * 60 * 1000;
|
|
1306
|
+
let timer;
|
|
1307
|
+
let timedOut = false;
|
|
1308
|
+
const fnPromise = fn();
|
|
1309
|
+
// Attach a no-op catch so post-timeout rejections from fn() don't
|
|
1310
|
+
// surface as unhandledRejection once Promise.race has already resolved.
|
|
1311
|
+
fnPromise.catch(() => undefined);
|
|
1312
|
+
try {
|
|
1313
|
+
return await Promise.race([
|
|
1314
|
+
fnPromise,
|
|
1315
|
+
new Promise((_, reject) => {
|
|
1316
|
+
timer = setTimeout(() => {
|
|
1317
|
+
timedOut = true;
|
|
1318
|
+
reject(new AgentTimeoutError(timeoutMs));
|
|
1319
|
+
// Cancel the SDK stream asynchronously.
|
|
1320
|
+
void (async () => {
|
|
1321
|
+
try {
|
|
1322
|
+
const iterable = stream;
|
|
1323
|
+
const iterator = typeof iterable[Symbol.asyncIterator] === "function"
|
|
1324
|
+
? iterable[Symbol.asyncIterator]()
|
|
1325
|
+
: null;
|
|
1326
|
+
await iterator?.return?.(undefined);
|
|
1327
|
+
}
|
|
1328
|
+
catch {
|
|
1329
|
+
// ignore — cancellation is best-effort
|
|
1330
|
+
}
|
|
1331
|
+
})();
|
|
1332
|
+
}, timeoutMs);
|
|
1333
|
+
timer.unref?.();
|
|
1334
|
+
}),
|
|
1335
|
+
]);
|
|
1336
|
+
}
|
|
1337
|
+
finally {
|
|
1338
|
+
if (timer) {
|
|
1339
|
+
clearTimeout(timer);
|
|
1340
|
+
}
|
|
1341
|
+
void timedOut; // reference to avoid unused-variable if optimizer drops it
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Detect whether a Bash command invokes a write against the Context
|
|
1346
|
+
* File API (`PUT|PATCH /api/context/*` via curl).
|
|
1347
|
+
*/
|
|
1348
|
+
static isContextUpdateCommand(command) {
|
|
1349
|
+
if (!/\bcurl\b/.test(command))
|
|
1350
|
+
return false;
|
|
1351
|
+
if (!/\/api\/context\//.test(command))
|
|
1352
|
+
return false;
|
|
1353
|
+
const segments = command.split(/[;&|\n]+/);
|
|
1354
|
+
for (const seg of segments) {
|
|
1355
|
+
if (!/\bcurl\b/.test(seg))
|
|
1356
|
+
continue;
|
|
1357
|
+
if (!/\/api\/context\//.test(seg))
|
|
1358
|
+
continue;
|
|
1359
|
+
if (/(?:-X|--request)\s+(?:PUT|PATCH)\b/.test(seg)) {
|
|
1360
|
+
return true;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1365
|
+
// Template resolution and event data extraction are shared with Codex/Gemini
|
|
1366
|
+
// backends via prompt-utils.ts (buildExecutionPrompt, extractEventData).
|
|
1367
|
+
/**
|
|
1368
|
+
* Allowed tools whitelist for dontAsk permission mode.
|
|
1369
|
+
*
|
|
1370
|
+
* `delegatedTools` is UNION'd onto the returned list — even when
|
|
1371
|
+
* `allowedToolsOverride` is set. This is a deliberate deviation from the
|
|
1372
|
+
* override's otherwise-absolute "replace everything" contract (see
|
|
1373
|
+
* `CRITICAL_OVERRIDE_TOOLS` at line 293, which warns but does not union).
|
|
1374
|
+
* Rationale: delegated mode is a runtime-configurable axis orthogonal to
|
|
1375
|
+
* the dashboard's tool-customization override. If a user set the override
|
|
1376
|
+
* before flipping an integration to delegated, silently dropping the
|
|
1377
|
+
* registry-declared connector tools would break mail/calendar with a
|
|
1378
|
+
* misleading "permission denied" DM. Union semantics keep the override's
|
|
1379
|
+
* curation intent while letting delegated mode widen the surface to
|
|
1380
|
+
* whatever the registry already advertised.
|
|
1381
|
+
*/
|
|
1382
|
+
getAllowedTools(webSearchEnabled, delegatedTools = []) {
|
|
1383
|
+
const base = this.config.allowedToolsOverride ?? [
|
|
1384
|
+
"Read",
|
|
1385
|
+
"Glob",
|
|
1386
|
+
"Grep",
|
|
1387
|
+
"Write",
|
|
1388
|
+
"Edit",
|
|
1389
|
+
"Skill", // user skills (external-services, obsidian-*, observations, ...)
|
|
1390
|
+
"Bash(curl *)", // curl broadly allowed; hooks restrict to localhost
|
|
1391
|
+
"Bash(git *)", // Git operations
|
|
1392
|
+
"Bash(jq *)", // safe JSON post-processor for curl pipelines
|
|
1393
|
+
];
|
|
1394
|
+
const merged = new Set(base);
|
|
1395
|
+
if (!this.config.allowedToolsOverride && webSearchEnabled) {
|
|
1396
|
+
merged.add("WebSearch");
|
|
1397
|
+
}
|
|
1398
|
+
for (const tool of delegatedTools)
|
|
1399
|
+
merged.add(tool);
|
|
1400
|
+
return Array.from(merged);
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Thin wrapper around the pure `computeDelegatedClaudeTools` helper that
|
|
1404
|
+
* pulls the integrations record from the wired mcp context. Returns `[]`
|
|
1405
|
+
* when the context is not yet wired (tests, startup ordering) — which
|
|
1406
|
+
* matches the pre-fix behavior for sessions that ran before the daemon
|
|
1407
|
+
* finished composing. A one-shot warning is emitted in `setMcpContext`
|
|
1408
|
+
* confirming wiring.
|
|
1409
|
+
*/
|
|
1410
|
+
getDelegatedClaudeTools() {
|
|
1411
|
+
if (!this.mcpContext)
|
|
1412
|
+
return [];
|
|
1413
|
+
try {
|
|
1414
|
+
const integrations = readIntegrations(this.mcpContext.db);
|
|
1415
|
+
return computeDelegatedClaudeTools(integrations);
|
|
1416
|
+
}
|
|
1417
|
+
catch (err) {
|
|
1418
|
+
logger.warn({ err }, "Failed to read integrations for delegated-tool allowlist — proceeding without delegated tools");
|
|
1419
|
+
return [];
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* DELEGATED-MODE-V2-DESIGN.md §4.3.3 — same-backend deny enforcement at
|
|
1424
|
+
* the SDK boundary. For every integration whose `delegatedBackend === "claude"`,
|
|
1425
|
+
* expand `state.deniedTools` against the connector's known tools and emit
|
|
1426
|
+
* the namespaced names (`mcp__claude_ai_<X>__<tool>`). The SDK refuses any
|
|
1427
|
+
* tool listed in `disallowedTools` regardless of `allowedTools` — hard
|
|
1428
|
+
* enforcement.
|
|
1429
|
+
*
|
|
1430
|
+
* Returns `[]` when context isn't wired (tests / pre-startup) and on read
|
|
1431
|
+
* failures, matching the conservative pattern used by
|
|
1432
|
+
* `getDelegatedClaudeTools`.
|
|
1433
|
+
*/
|
|
1434
|
+
getSessionDeniedTools() {
|
|
1435
|
+
if (!this.mcpContext)
|
|
1436
|
+
return [];
|
|
1437
|
+
try {
|
|
1438
|
+
const integrations = readIntegrations(this.mcpContext.db);
|
|
1439
|
+
const map = collectSessionDeniedTools(integrations, "claude");
|
|
1440
|
+
const out = [];
|
|
1441
|
+
for (const names of map.values()) {
|
|
1442
|
+
for (const n of names)
|
|
1443
|
+
out.push(n);
|
|
1444
|
+
}
|
|
1445
|
+
return out;
|
|
1446
|
+
}
|
|
1447
|
+
catch (err) {
|
|
1448
|
+
logger.warn({ err }, "Failed to read integrations for same-backend denied-tools — proceeding without per-integration deny");
|
|
1449
|
+
return [];
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Security hooks:
|
|
1454
|
+
* 1. Bash(curl *) — restrict to localhost Daemon API, block connection-override flags. (strict only)
|
|
1455
|
+
* 2. Bash(jq *) — block file-access flags and the `env` filter (process env exfiltration). (strict only)
|
|
1456
|
+
* 3. Write/Edit — block writes into the session helper dir and context dir, mark vault writes.
|
|
1457
|
+
*
|
|
1458
|
+
* In allow mode the curl and jq hooks are dropped, but the Write/Edit hook
|
|
1459
|
+
* stays: the context-dir chokepoint exists for memory integrity (today-write
|
|
1460
|
+
* lock, md_file_snapshots, CONTEXT_WRITE_PERMISSIONS), not permissions.
|
|
1461
|
+
*/
|
|
1462
|
+
getSecurityHooks(allowMode = false) {
|
|
1463
|
+
const bashCurlHook = async (input) => {
|
|
1464
|
+
const toolInput = input.tool_input;
|
|
1465
|
+
const cmd = toolInput?.command ?? "";
|
|
1466
|
+
if (/\bcurl\b/.test(cmd)) {
|
|
1467
|
+
const urls = cmd.match(/https?:\/\/[^\s'"]+/g) ?? [];
|
|
1468
|
+
if (urls.length === 0) {
|
|
1469
|
+
return {
|
|
1470
|
+
decision: "block",
|
|
1471
|
+
reason: "curl command must contain an explicit localhost URL",
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
for (const url of urls) {
|
|
1475
|
+
try {
|
|
1476
|
+
const parsed = new URL(url);
|
|
1477
|
+
if (parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1") {
|
|
1478
|
+
return {
|
|
1479
|
+
decision: "block",
|
|
1480
|
+
reason: `curl target not allowed: ${url} (host: ${parsed.hostname})`,
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
const effectivePort = parsed.port || (parsed.protocol === "https:" ? "443" : "80");
|
|
1484
|
+
if (effectivePort !== String(this.config.apiPort)) {
|
|
1485
|
+
return {
|
|
1486
|
+
decision: "block",
|
|
1487
|
+
reason: `curl target port not allowed: ${effectivePort}`,
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
catch {
|
|
1492
|
+
return {
|
|
1493
|
+
decision: "block",
|
|
1494
|
+
reason: `curl target URL is malformed: ${url}`,
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
if (/--connect-to|--resolve|--config\b|(?:^|\s)-[a-zA-Z]*K|--proxy\b|(?:^|\s)-[a-zA-Z]*x|--socks/.test(cmd)) {
|
|
1499
|
+
return {
|
|
1500
|
+
decision: "block",
|
|
1501
|
+
reason: "curl connection override flags not allowed (--connect-to, --resolve, --config, --proxy)",
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
return { continue: true };
|
|
1506
|
+
};
|
|
1507
|
+
const bashJqHook = async (input) => {
|
|
1508
|
+
const toolInput = input.tool_input;
|
|
1509
|
+
const cmd = toolInput?.command ?? "";
|
|
1510
|
+
if (!/\bjq\b/.test(cmd))
|
|
1511
|
+
return { continue: true };
|
|
1512
|
+
// Narrow to THIS jq invocation's own args (up to the next pipe / chain op)
|
|
1513
|
+
// so that later pipeline stages are not inspected by the jq rules.
|
|
1514
|
+
//
|
|
1515
|
+
// Known approximation: `[^|;&]*` does not respect shell quoting, so a
|
|
1516
|
+
// jq filter with a `|` INSIDE a quoted expression (e.g. `jq 'env | keys'`)
|
|
1517
|
+
// will truncate `jqPart` at the first `|` regardless of whether that `|`
|
|
1518
|
+
// is a jq pipe inside quotes or an actual shell pipeline break. This is
|
|
1519
|
+
// intentionally conservative on the safe side: the env-filter check
|
|
1520
|
+
// below still fires on the truncated left half (`jq 'env `), so attack
|
|
1521
|
+
// payloads are still blocked. The downside is slightly reduced precision
|
|
1522
|
+
// on benign expressions containing the jq `|` operator — those get
|
|
1523
|
+
// scanned only up to the first pipe, not their full extent.
|
|
1524
|
+
const jqMatch = cmd.match(/\bjq\b([^|;&]*)/);
|
|
1525
|
+
if (!jqMatch)
|
|
1526
|
+
return { continue: true };
|
|
1527
|
+
const jqPart = jqMatch[0];
|
|
1528
|
+
// (a) Block file-access flags — --slurpfile / --rawfile read arbitrary
|
|
1529
|
+
// files, which would bypass the Read deny list (~/.ssh/**, .env, etc.).
|
|
1530
|
+
if (/(?:^|\s)--slurpfile\b/.test(jqPart) || /(?:^|\s)--rawfile\b/.test(jqPart)) {
|
|
1531
|
+
return {
|
|
1532
|
+
decision: "block",
|
|
1533
|
+
reason: "jq --slurpfile and --rawfile are not allowed " +
|
|
1534
|
+
"(would bypass Read(.env) / Read(~/.ssh/**) disallow rules).",
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
// (b) Block module loading — -L <dir> + import can load filter code from
|
|
1538
|
+
// the filesystem, effectively RCE inside the jq process.
|
|
1539
|
+
if (/(?:^|\s)-L(?:\s|=|$)/.test(jqPart)) {
|
|
1540
|
+
return {
|
|
1541
|
+
decision: "block",
|
|
1542
|
+
reason: "jq -L (module load path) is not allowed.",
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
// (c) Block the `env` filter. `jq env`, `jq -n env`, `jq 'env.FOO'`,
|
|
1546
|
+
// `jq '. , env'` all dump the daemon's process.env to stdout. Process.env
|
|
1547
|
+
// on this daemon is expected to be clean (secrets live in the keychain),
|
|
1548
|
+
// but defense-in-depth: if OPENAI_API_KEY or similar is ever exported at
|
|
1549
|
+
// launch, the env filter is the shortest exfil path.
|
|
1550
|
+
//
|
|
1551
|
+
// Heuristic: match bare `env` NOT preceded by a field-access dot or word
|
|
1552
|
+
// char, and NOT followed by a word char. This matches jq's env filter
|
|
1553
|
+
// (`env`, `env.HOME`, `(env)`, `env|keys`) while leaving field access
|
|
1554
|
+
// like `.env`, `.env_var`, `.data.environments` untouched.
|
|
1555
|
+
if (/(?:^|[^\w.])env(?!\w)/.test(jqPart)) {
|
|
1556
|
+
return {
|
|
1557
|
+
decision: "block",
|
|
1558
|
+
reason: "jq env filter is not allowed — it dumps the daemon process " +
|
|
1559
|
+
"environment, which is a known exfiltration vector for any " +
|
|
1560
|
+
"secrets loaded via .env at startup.",
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
return { continue: true };
|
|
1564
|
+
};
|
|
1565
|
+
/**
|
|
1566
|
+
* Block any Bash command that references the context-directory path.
|
|
1567
|
+
*
|
|
1568
|
+
* Rationale: the daemon API is the ONLY sanctioned write channel for
|
|
1569
|
+
* context files — it enforces today-write-lock, md_file_snapshots,
|
|
1570
|
+
* CONTEXT_WRITE_PERMISSIONS, and onPromptContextChanged. In strict mode,
|
|
1571
|
+
* the allowlist (Bash narrowed to curl/git/jq) + fileWriteHook keeps
|
|
1572
|
+
* this chokepoint intact. In allow mode Bash is unrestricted, so an
|
|
1573
|
+
* agent could bypass via `echo > today.md`, `tee`, `python -c 'open…'`,
|
|
1574
|
+
* `git log … > context/…`, etc.
|
|
1575
|
+
*
|
|
1576
|
+
* This hook runs in BOTH modes (defense-in-depth). The heuristic is
|
|
1577
|
+
* deliberately conservative: we block the command if its raw text
|
|
1578
|
+
* contains any of the well-known path representations of the context
|
|
1579
|
+
* dir. This has false positives for read-only uses (`cat context/*.md`),
|
|
1580
|
+
* but the agent always has the Read tool and the daemon GET endpoint
|
|
1581
|
+
* available, so a blocked shell-read is an error message, not a
|
|
1582
|
+
* capability loss. Obfuscation beyond the handled variants (dynamic
|
|
1583
|
+
* path construction inside a subshell) is out of scope — the threat
|
|
1584
|
+
* model is "the model picks a shell workaround", not adversarial
|
|
1585
|
+
* prompt injection.
|
|
1586
|
+
*/
|
|
1587
|
+
const bashContextWriteHook = async (input) => {
|
|
1588
|
+
const toolInput = input.tool_input;
|
|
1589
|
+
const cmd = toolInput?.command ?? "";
|
|
1590
|
+
if (typeof cmd !== "string" || cmd.length === 0)
|
|
1591
|
+
return { continue: true };
|
|
1592
|
+
const absContextDir = resolvePath(getContextDir(this.config));
|
|
1593
|
+
const home = homedir();
|
|
1594
|
+
const pathForms = shellPathForms(absContextDir, home);
|
|
1595
|
+
for (const form of pathForms) {
|
|
1596
|
+
if (cmd.includes(form)) {
|
|
1597
|
+
return {
|
|
1598
|
+
decision: "block",
|
|
1599
|
+
reason: `Bash commands that reference the context directory (${absContextDir}) are ` +
|
|
1600
|
+
`not allowed. Use the daemon API: ` +
|
|
1601
|
+
`GET/PUT/PATCH http://localhost:${this.config.apiPort}/api/context/<path>. ` +
|
|
1602
|
+
`The API enforces today-write-lock, md_file_snapshots, CONTEXT_WRITE_PERMISSIONS, ` +
|
|
1603
|
+
`and onPromptContextChanged — bypassing it via shell redirects or script ` +
|
|
1604
|
+
`engines leaves the memory layer inconsistent. Matched path form: ${form}.`,
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return { continue: true };
|
|
1609
|
+
};
|
|
1610
|
+
const fileWriteHook = async (input) => {
|
|
1611
|
+
const hookInput = input;
|
|
1612
|
+
const toolInput = hookInput.tool_input;
|
|
1613
|
+
const rawFilePath = toolInput?.file_path;
|
|
1614
|
+
if (typeof rawFilePath !== "string" || rawFilePath.length === 0) {
|
|
1615
|
+
return { continue: true };
|
|
1616
|
+
}
|
|
1617
|
+
const filePath = rawFilePath;
|
|
1618
|
+
const cwd = hookInput.cwd;
|
|
1619
|
+
if (!cwd && !isAbsolute(filePath))
|
|
1620
|
+
return { continue: true };
|
|
1621
|
+
const absFile = resolvePath(cwd ?? "/", filePath);
|
|
1622
|
+
// (a) Block writes into the session-local helper dir. The `curl` shim in
|
|
1623
|
+
// `.pa/bin/` carries daemon-auth env at execution time; letting the model
|
|
1624
|
+
// rewrite it would turn the helper into a secret exfiltration vector.
|
|
1625
|
+
const absHelperDir = resolvePath(cwd ?? "/", ".pa");
|
|
1626
|
+
const withinHelperDir = isPathInsideOrEqual(absHelperDir, absFile);
|
|
1627
|
+
if (withinHelperDir) {
|
|
1628
|
+
return {
|
|
1629
|
+
decision: "block",
|
|
1630
|
+
reason: "Direct Write/Edit to .pa is forbidden. " +
|
|
1631
|
+
"Session helper binaries are managed by the daemon.",
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
// (b) Block writes into the context dir.
|
|
1635
|
+
const contextDir = getContextDir(this.config);
|
|
1636
|
+
const absContextDir = resolvePath(contextDir);
|
|
1637
|
+
const withinContext = isPathInsideOrEqual(absContextDir, absFile);
|
|
1638
|
+
if (withinContext) {
|
|
1639
|
+
return {
|
|
1640
|
+
decision: "block",
|
|
1641
|
+
reason: `Direct Write/Edit to context dir is forbidden. ` +
|
|
1642
|
+
`Use the daemon API instead: ` +
|
|
1643
|
+
`PUT http://localhost:${this.config.apiPort}/api/context/<path> (full replace) or ` +
|
|
1644
|
+
`PATCH http://localhost:${this.config.apiPort}/api/context/<path> (section op). ` +
|
|
1645
|
+
`The API enforces CONTEXT_WRITE_PERMISSIONS, morningRoutineLock, md_file_snapshots, ` +
|
|
1646
|
+
`onPromptContextChanged, and expectedMtime concurrency. Path: ${absFile}`,
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
// (c) Mark vault-scoped writes for observer attribution.
|
|
1650
|
+
// Targets the EXTERNAL Obsidian vault; the ObsidianWatcher observer
|
|
1651
|
+
// watches that path and would otherwise misattribute agent writes
|
|
1652
|
+
// as user writes.
|
|
1653
|
+
if (!this.writeTracker)
|
|
1654
|
+
return { continue: true };
|
|
1655
|
+
const vaultPath = this.config.externalObsidianVaultPath;
|
|
1656
|
+
if (!vaultPath)
|
|
1657
|
+
return { continue: true };
|
|
1658
|
+
const absVault = resolvePath(vaultPath);
|
|
1659
|
+
const withinVault = isPathInsideOrEqual(absVault, absFile);
|
|
1660
|
+
if (!withinVault)
|
|
1661
|
+
return { continue: true };
|
|
1662
|
+
this.writeTracker.markWriting(absFile);
|
|
1663
|
+
logger.debug({ filePath: absFile }, "vault write pre-marked for observer attribution");
|
|
1664
|
+
return { continue: true };
|
|
1665
|
+
};
|
|
1666
|
+
// EXECUTION-MODE-DESIGN.md §6 — absolute-block audit hook. Runs ahead
|
|
1667
|
+
// of every other Bash/Read/Write/Edit hook in both modes. The SDK-level
|
|
1668
|
+
// `disallowedTools` rejection is the authoritative block; this hook is
|
|
1669
|
+
// redundant defense-in-depth that also writes the `blocked_absolute`
|
|
1670
|
+
// audit row so the owner can see the layer is active.
|
|
1671
|
+
const makeAbsoluteBlockHook = (toolName, argField) => async (input) => {
|
|
1672
|
+
const toolInput = input.tool_input;
|
|
1673
|
+
const raw = toolInput?.[argField];
|
|
1674
|
+
if (typeof raw !== "string")
|
|
1675
|
+
return { continue: true };
|
|
1676
|
+
const match = classifyAbsoluteBlock(toolName, raw);
|
|
1677
|
+
if (!match)
|
|
1678
|
+
return { continue: true };
|
|
1679
|
+
recordAbsoluteBlockAudit({
|
|
1680
|
+
db: this.mcpContext?.db,
|
|
1681
|
+
backend: "claude",
|
|
1682
|
+
mode: this.config.claudeExecutionPermissionMode,
|
|
1683
|
+
match,
|
|
1684
|
+
toolName,
|
|
1685
|
+
});
|
|
1686
|
+
return {
|
|
1687
|
+
decision: "block",
|
|
1688
|
+
reason: `Absolute-block layer denied this ${toolName} call ` +
|
|
1689
|
+
`(category: ${match.category}). This rule holds in both Safe ` +
|
|
1690
|
+
`and Allow modes — see EXECUTION-MODE-DESIGN.md §6.`,
|
|
1691
|
+
};
|
|
1692
|
+
};
|
|
1693
|
+
const bashAbsoluteBlockHook = makeAbsoluteBlockHook("Bash", "command");
|
|
1694
|
+
const readAbsoluteBlockHook = makeAbsoluteBlockHook("Read", "file_path");
|
|
1695
|
+
const writeAbsoluteBlockHook = makeAbsoluteBlockHook("Write", "file_path");
|
|
1696
|
+
const editAbsoluteBlockHook = makeAbsoluteBlockHook("Edit", "file_path");
|
|
1697
|
+
// The context-write hook is always attached to Bash — it is the only
|
|
1698
|
+
// guarantee that the daemon-API chokepoint for memory files survives
|
|
1699
|
+
// allow mode (where curl/jq restrictions are dropped and Bash can
|
|
1700
|
+
// otherwise redirect into context/*.md freely).
|
|
1701
|
+
//
|
|
1702
|
+
// The absolute-block audit hook is appended LAST on every matcher
|
|
1703
|
+
// (§6.3). Appended rather than prepended so existing per-index hook
|
|
1704
|
+
// tests keep pointing at the same functions; semantically it is a
|
|
1705
|
+
// fallback defense whose practical effect is duplicating the SDK's
|
|
1706
|
+
// `disallowedTools` rejection into an `agent_actions` row.
|
|
1707
|
+
return {
|
|
1708
|
+
PreToolUse: [
|
|
1709
|
+
{
|
|
1710
|
+
matcher: "Bash",
|
|
1711
|
+
hooks: allowMode
|
|
1712
|
+
? [bashContextWriteHook, bashAbsoluteBlockHook]
|
|
1713
|
+
: [
|
|
1714
|
+
bashCurlHook,
|
|
1715
|
+
bashJqHook,
|
|
1716
|
+
bashContextWriteHook,
|
|
1717
|
+
bashAbsoluteBlockHook,
|
|
1718
|
+
],
|
|
1719
|
+
},
|
|
1720
|
+
{ matcher: "Write", hooks: [fileWriteHook, writeAbsoluteBlockHook] },
|
|
1721
|
+
{ matcher: "Edit", hooks: [fileWriteHook, editAbsoluteBlockHook] },
|
|
1722
|
+
{ matcher: "Read", hooks: [readAbsoluteBlockHook] },
|
|
1723
|
+
],
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
/**
|
|
1727
|
+
* Delegated proxy invocation — Claude SDK path.
|
|
1728
|
+
*
|
|
1729
|
+
* Spawns a one-shot SDK `query()` constrained to a single `allowedTools`
|
|
1730
|
+
* entry (the requested connector tool), parses the structured stream
|
|
1731
|
+
* for the matching `tool_use` → `tool_result` pair, and returns the raw
|
|
1732
|
+
* tool result. The model's surrounding text is discarded — the route
|
|
1733
|
+
* handler only sees the connector's structured payload.
|
|
1734
|
+
*
|
|
1735
|
+
* Error classes (DelegatedToolErrorClass):
|
|
1736
|
+
* - `wrong_tool` — model called a tool we did not request (anti-prompt-injection).
|
|
1737
|
+
* - `no_tool_call` — model never invoked the tool within `maxTurns`.
|
|
1738
|
+
* - `tool_error` — connector returned `is_error: true`.
|
|
1739
|
+
* - `parse_error` — stream produced no terminal `result` event.
|
|
1740
|
+
* - `auth_error` — SDK reported `authentication_failed`.
|
|
1741
|
+
* - `timeout` — invoker's wall-clock signal aborted the stream.
|
|
1742
|
+
* - `subprocess_crashed` — exception thrown out of the iterator.
|
|
1743
|
+
*
|
|
1744
|
+
* Cost is captured from the terminal `SDKResultMessage` regardless of
|
|
1745
|
+
* subtype so the invoker can attribute partial spend on the failure
|
|
1746
|
+
* paths (no_tool_call, wrong_tool, tool_error).
|
|
1747
|
+
*/
|
|
1748
|
+
async runDelegatedTool(params) {
|
|
1749
|
+
const startMs = Date.now();
|
|
1750
|
+
const { toolName, toolArgs, modelId, maxTurns, maxBudgetUsd, sessionDir } = params;
|
|
1751
|
+
const prompt = buildDelegatedToolPrompt(toolName, toolArgs);
|
|
1752
|
+
const daemonReadToken = this.readTokenManager?.issue(sessionDir) ?? this.readToken;
|
|
1753
|
+
let stream = null;
|
|
1754
|
+
const aborted = { value: false };
|
|
1755
|
+
// `abortReason` carries the reason that caused `aborted.value=true`
|
|
1756
|
+
// so the post-loop classifier can map idle-watchdog aborts to
|
|
1757
|
+
// `errorClass="timeout"`. Falls back to the caller's signal reason
|
|
1758
|
+
// when only the caller initiated the abort.
|
|
1759
|
+
let abortReason = null;
|
|
1760
|
+
const closeStream = () => {
|
|
1761
|
+
void (async () => {
|
|
1762
|
+
try {
|
|
1763
|
+
await stream?.return?.(undefined);
|
|
1764
|
+
}
|
|
1765
|
+
catch {
|
|
1766
|
+
/* stream already closed */
|
|
1767
|
+
}
|
|
1768
|
+
})();
|
|
1769
|
+
};
|
|
1770
|
+
const onAbort = () => {
|
|
1771
|
+
aborted.value = true;
|
|
1772
|
+
abortReason = params.abortSignal?.reason ?? null;
|
|
1773
|
+
closeStream();
|
|
1774
|
+
};
|
|
1775
|
+
if (params.abortSignal) {
|
|
1776
|
+
if (params.abortSignal.aborted) {
|
|
1777
|
+
aborted.value = true;
|
|
1778
|
+
abortReason = params.abortSignal.reason ?? null;
|
|
1779
|
+
}
|
|
1780
|
+
else {
|
|
1781
|
+
params.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
// Idle watchdog. Claude SDK runs in-process; cold-start is
|
|
1785
|
+
// negligible (no MCP/CLI load) so the typical first message lands
|
|
1786
|
+
// within 1-3 s. A 30 s idle threshold catches a stuck SDK iterator
|
|
1787
|
+
// (network stall, server-side hang) without false-tripping a slow
|
|
1788
|
+
// tool. On trip we close the stream the same way `onAbort` does and
|
|
1789
|
+
// record the trip in `abortReason` so the classifier returns
|
|
1790
|
+
// `errorClass="timeout"` (uniform with CLI backends).
|
|
1791
|
+
const idleTimeoutMs = DELEGATED_PROXY_DEFAULTS.idleTimeoutMsByBackend.claude
|
|
1792
|
+
?? DELEGATED_PROXY_DEFAULTS.idleTimeoutMs;
|
|
1793
|
+
const idleWatchdog = new IdleWatchdog({
|
|
1794
|
+
idleTimeoutMs,
|
|
1795
|
+
onTimeout: (idleMs) => {
|
|
1796
|
+
if (aborted.value)
|
|
1797
|
+
return;
|
|
1798
|
+
aborted.value = true;
|
|
1799
|
+
abortReason = new DelegatedProxyTimeoutError(`claude SDK stream idle for ${idleMs}ms (limit ${idleTimeoutMs}ms)`);
|
|
1800
|
+
logger.warn({ idleMs, idleTimeoutMs, toolName }, "claude delegated proxy idle watchdog tripped");
|
|
1801
|
+
closeStream();
|
|
1802
|
+
},
|
|
1803
|
+
});
|
|
1804
|
+
try {
|
|
1805
|
+
stream = query({
|
|
1806
|
+
prompt,
|
|
1807
|
+
options: {
|
|
1808
|
+
model: modelId,
|
|
1809
|
+
maxTurns,
|
|
1810
|
+
maxBudgetUsd,
|
|
1811
|
+
cwd: sessionDir,
|
|
1812
|
+
env: buildDaemonApiCliEnv(sessionDir, this.config.apiPort, { readToken: daemonReadToken, sessionBackend: "claude" }),
|
|
1813
|
+
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
1814
|
+
permissionMode: "dontAsk",
|
|
1815
|
+
// The connector tool must be pre-authorized — Claude SDK with
|
|
1816
|
+
// permissionMode="dontAsk" silently denies anything not in
|
|
1817
|
+
// allowedTools.
|
|
1818
|
+
//
|
|
1819
|
+
// ToolSearch is Claude Code's deferred-tool discovery mechanism:
|
|
1820
|
+
// when many MCP servers are registered in the user's global
|
|
1821
|
+
// config (~/.claude.json: Notion, Gmail, GCal, Drive, Figma,
|
|
1822
|
+
// Canva, Hugging Face, …) the CLI ships only a working set of
|
|
1823
|
+
// tool schemas and defers the rest. To call a deferred tool, the
|
|
1824
|
+
// model must first call ToolSearch to load its schema. Without
|
|
1825
|
+
// ToolSearch allowed, the proxy's first turn was wasted on a
|
|
1826
|
+
// denied ToolSearch call (audit log 2026-04-29: 1 Notion failure
|
|
1827
|
+
// logged as `wrong_tool=ToolSearch`, 5 logged as
|
|
1828
|
+
// `subprocess_crashed: Reached maximum number of turns (2)` —
|
|
1829
|
+
// the model retried other approaches and exhausted the budget).
|
|
1830
|
+
//
|
|
1831
|
+
// Allowing ToolSearch is safe: allowedTools enforcement still
|
|
1832
|
+
// gates which tools can be CALLED, and ToolSearch only loads
|
|
1833
|
+
// schemas into context. The proxy parser below also skips
|
|
1834
|
+
// ToolSearch when capturing `wrongToolName` so a ToolSearch+
|
|
1835
|
+
// partial-result trace classifies as `no_tool_call` rather than
|
|
1836
|
+
// misleading `wrong_tool=ToolSearch`.
|
|
1837
|
+
//
|
|
1838
|
+
// TODO(future): a cleaner architectural fix is to materialize a
|
|
1839
|
+
// session-local `.mcp.json` containing only the relevant
|
|
1840
|
+
// connector's MCP server and pass `strictMcpConfig: true` —
|
|
1841
|
+
// that prevents deferral entirely (one MCP server → schemas fit
|
|
1842
|
+
// in the working set). Punted because it requires extracting
|
|
1843
|
+
// server configs from the user's global file per integration.
|
|
1844
|
+
allowedTools: [toolName, DEFERRED_TOOL_DISCOVERY_TOOL_NAME],
|
|
1845
|
+
// Defense-in-depth: even with allowedTools restricted to a tight
|
|
1846
|
+
// set, keep the absolute-block layer (rm -rf, sudo, secret file
|
|
1847
|
+
// reads) merged so a future relaxation of allowedTools can't
|
|
1848
|
+
// accidentally drop these guarantees.
|
|
1849
|
+
disallowedTools: [...ALWAYS_DISALLOWED_TOOLS],
|
|
1850
|
+
// Adaptive thinking is the SDK default for thinking-capable
|
|
1851
|
+
// models (Haiku 4.5+ / Sonnet 4.6+). Per Anthropic's docs
|
|
1852
|
+
// thinking happens within a single API call so it does not
|
|
1853
|
+
// typically burn an extra turn — but for a proxy that issues
|
|
1854
|
+
// one named tool call with explicit args, thinking adds latency
|
|
1855
|
+
// and tokens for no benefit. The proxy.md profile says "no
|
|
1856
|
+
// narration"; disabling thinking aligns runtime behavior with
|
|
1857
|
+
// that intent.
|
|
1858
|
+
thinking: { type: "disabled" },
|
|
1859
|
+
},
|
|
1860
|
+
});
|
|
1861
|
+
let capturedToolUseId = null;
|
|
1862
|
+
let capturedToolResult = undefined;
|
|
1863
|
+
let capturedToolErrorMessage = null;
|
|
1864
|
+
let wrongToolName = null;
|
|
1865
|
+
let cost = emptyCost();
|
|
1866
|
+
let terminalSubtype = null;
|
|
1867
|
+
let terminalIsError = false;
|
|
1868
|
+
let terminalErrors = [];
|
|
1869
|
+
try {
|
|
1870
|
+
idleWatchdog.start();
|
|
1871
|
+
for await (const message of stream) {
|
|
1872
|
+
idleWatchdog.beat();
|
|
1873
|
+
if (aborted.value) {
|
|
1874
|
+
break;
|
|
1875
|
+
}
|
|
1876
|
+
if (message.type === "assistant") {
|
|
1877
|
+
const assistantMsg = message;
|
|
1878
|
+
const blocks = assistantMsg.message?.content;
|
|
1879
|
+
if (!Array.isArray(blocks))
|
|
1880
|
+
continue;
|
|
1881
|
+
for (const block of blocks) {
|
|
1882
|
+
if (!block || typeof block !== "object")
|
|
1883
|
+
continue;
|
|
1884
|
+
const blockType = block.type;
|
|
1885
|
+
if (blockType !== "tool_use")
|
|
1886
|
+
continue;
|
|
1887
|
+
const blockName = block.name;
|
|
1888
|
+
const blockId = block.id;
|
|
1889
|
+
if (typeof blockName !== "string" || typeof blockId !== "string") {
|
|
1890
|
+
continue;
|
|
1891
|
+
}
|
|
1892
|
+
if (blockName === toolName) {
|
|
1893
|
+
if (capturedToolUseId === null) {
|
|
1894
|
+
capturedToolUseId = blockId;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
else if (blockName === DEFERRED_TOOL_DISCOVERY_TOOL_NAME) {
|
|
1898
|
+
// Expected intermediate step for loading the connector's
|
|
1899
|
+
// deferred MCP schema — not a violation. Do not capture as
|
|
1900
|
+
// wrongToolName so a partial trace (ToolSearch + max_turns
|
|
1901
|
+
// before the connector call) classifies as `no_tool_call`
|
|
1902
|
+
// instead of misleading `wrong_tool=ToolSearch`.
|
|
1903
|
+
}
|
|
1904
|
+
else if (wrongToolName === null) {
|
|
1905
|
+
wrongToolName = blockName;
|
|
1906
|
+
// Early abort: bound the wall-clock spend on a wrong_tool
|
|
1907
|
+
// failure to ~5s. Set `aborted` so the next loop iteration
|
|
1908
|
+
// breaks; close the SDK stream so any pending tool_use
|
|
1909
|
+
// doesn't continue. The post-loop classifier checks
|
|
1910
|
+
// `wrongToolName` BEFORE the abort branch, so the failure
|
|
1911
|
+
// is correctly attributed.
|
|
1912
|
+
aborted.value = true;
|
|
1913
|
+
closeStream();
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
else if (message.type === "user") {
|
|
1918
|
+
const userMsg = message;
|
|
1919
|
+
const content = userMsg.message?.content;
|
|
1920
|
+
if (!Array.isArray(content))
|
|
1921
|
+
continue;
|
|
1922
|
+
for (const block of content) {
|
|
1923
|
+
if (!block || typeof block !== "object")
|
|
1924
|
+
continue;
|
|
1925
|
+
if (block.type !== "tool_result")
|
|
1926
|
+
continue;
|
|
1927
|
+
const tuid = block.tool_use_id;
|
|
1928
|
+
if (tuid !== capturedToolUseId)
|
|
1929
|
+
continue;
|
|
1930
|
+
const isToolError = block.is_error === true;
|
|
1931
|
+
const rawContent = block.content;
|
|
1932
|
+
const flat = flattenToolResultContent(rawContent);
|
|
1933
|
+
if (isToolError) {
|
|
1934
|
+
capturedToolErrorMessage =
|
|
1935
|
+
flat.trim().length > 0 ? flat : "tool returned is_error";
|
|
1936
|
+
}
|
|
1937
|
+
else if (capturedToolResult === undefined) {
|
|
1938
|
+
capturedToolResult = tryParseToolResult(flat);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
else if (message.type === "result") {
|
|
1943
|
+
const r = message;
|
|
1944
|
+
terminalSubtype = r.subtype;
|
|
1945
|
+
terminalIsError = r.is_error;
|
|
1946
|
+
cost = {
|
|
1947
|
+
tokensInput: r.usage.input_tokens ?? 0,
|
|
1948
|
+
tokensOutput: r.usage.output_tokens ?? 0,
|
|
1949
|
+
cacheCreationTokens: r.usage.cache_creation_input_tokens ?? 0,
|
|
1950
|
+
cacheReadTokens: r.usage.cache_read_input_tokens ?? 0,
|
|
1951
|
+
costUsd: r.total_cost_usd ?? 0,
|
|
1952
|
+
durationMs: r.duration_ms ?? Date.now() - startMs,
|
|
1953
|
+
numTurns: r.num_turns ?? 0,
|
|
1954
|
+
};
|
|
1955
|
+
if (r.subtype !== "success" && "errors" in r && Array.isArray(r.errors)) {
|
|
1956
|
+
terminalErrors = r.errors;
|
|
1957
|
+
}
|
|
1958
|
+
// The result message is terminal per SDK semantics. Break out
|
|
1959
|
+
// before the next iterator step. When `r.is_error` is true, the
|
|
1960
|
+
// SDK's transport sets `lastErrorResultText` and throws on the
|
|
1961
|
+
// next `readMessages` iteration — wrapping it as
|
|
1962
|
+
// `Error("Claude Code returned an error result: <text>")`. That
|
|
1963
|
+
// throw would land in the outer catch and misclassify as
|
|
1964
|
+
// `subprocess_crashed`, discarding the captured cost. Audit log
|
|
1965
|
+
// (2026-04-29) showed 5 such failures with num_turns=0,
|
|
1966
|
+
// tokens=0, masking that this was actually `error_max_turns`.
|
|
1967
|
+
// Breaking here lets the post-loop classifier run with the
|
|
1968
|
+
// captured terminalSubtype, terminalErrors, wrongToolName, and
|
|
1969
|
+
// cost intact.
|
|
1970
|
+
break;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
finally {
|
|
1975
|
+
idleWatchdog.stop();
|
|
1976
|
+
try {
|
|
1977
|
+
await stream?.return?.(undefined);
|
|
1978
|
+
}
|
|
1979
|
+
catch {
|
|
1980
|
+
/* stream already closed */
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
cost = withDurationMs(cost, startMs);
|
|
1984
|
+
// wrong_tool check hoisted above the abort branch because the
|
|
1985
|
+
// early-abort path sets `aborted.value` AND `wrongToolName`.
|
|
1986
|
+
// Without this ordering the failure would surface as `cancelled`
|
|
1987
|
+
// instead of the actual upstream cause. The `abortReason` field
|
|
1988
|
+
// distinguishes idle-watchdog aborts (errorClass="timeout") from
|
|
1989
|
+
// caller-initiated cancels (errorClass="cancelled" unless the
|
|
1990
|
+
// caller's reason was itself a `DelegatedProxyTimeoutError`).
|
|
1991
|
+
if (wrongToolName !== null) {
|
|
1992
|
+
return {
|
|
1993
|
+
ok: false,
|
|
1994
|
+
errorClass: "wrong_tool",
|
|
1995
|
+
message: `model called '${wrongToolName}' instead of requested '${toolName}'`,
|
|
1996
|
+
cost,
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
if (aborted.value) {
|
|
2000
|
+
const reason = abortReason ?? params.abortSignal?.reason;
|
|
2001
|
+
const errorClass = classifyAbortReason(reason);
|
|
2002
|
+
const idleAbort = reason instanceof DelegatedProxyTimeoutError
|
|
2003
|
+
&& /idle/.test(reason.message);
|
|
2004
|
+
return {
|
|
2005
|
+
ok: false,
|
|
2006
|
+
errorClass,
|
|
2007
|
+
message: errorClass === "timeout"
|
|
2008
|
+
? (idleAbort
|
|
2009
|
+
? `delegated proxy stream went idle (no claude SDK events for ${idleTimeoutMs}ms)`
|
|
2010
|
+
: "delegated proxy timed out (wall-clock)")
|
|
2011
|
+
: "delegated proxy cancelled by caller",
|
|
2012
|
+
cost,
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
if (capturedToolResult !== undefined) {
|
|
2016
|
+
return { ok: true, toolResult: capturedToolResult, cost };
|
|
2017
|
+
}
|
|
2018
|
+
if (capturedToolErrorMessage !== null) {
|
|
2019
|
+
return {
|
|
2020
|
+
ok: false,
|
|
2021
|
+
errorClass: "tool_error",
|
|
2022
|
+
message: capturedToolErrorMessage,
|
|
2023
|
+
cost,
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
// Map specific terminal subtypes before falling through to
|
|
2027
|
+
// no_tool_call. The model can fail for auth or budget reasons
|
|
2028
|
+
// before ever emitting a tool_use block.
|
|
2029
|
+
if (terminalSubtype === "error_during_execution" && terminalErrors.length > 0) {
|
|
2030
|
+
const joined = terminalErrors.join("; ");
|
|
2031
|
+
if (/auth|unauthorized|authentication_failed|invalid api key/i.test(joined)) {
|
|
2032
|
+
return {
|
|
2033
|
+
ok: false,
|
|
2034
|
+
errorClass: "auth_error",
|
|
2035
|
+
message: joined,
|
|
2036
|
+
cost,
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
return {
|
|
2040
|
+
ok: false,
|
|
2041
|
+
errorClass: "tool_error",
|
|
2042
|
+
message: joined,
|
|
2043
|
+
cost,
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
if (terminalSubtype === null && !terminalIsError) {
|
|
2047
|
+
// Stream ended before any terminal `result` arrived — abnormal
|
|
2048
|
+
// termination not classified as an abort. Treat as parse_error
|
|
2049
|
+
// so the route handler can surface the bug rather than retrying.
|
|
2050
|
+
return {
|
|
2051
|
+
ok: false,
|
|
2052
|
+
errorClass: "parse_error",
|
|
2053
|
+
message: "Claude SDK stream ended without a terminal result message",
|
|
2054
|
+
cost,
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
return {
|
|
2058
|
+
ok: false,
|
|
2059
|
+
errorClass: "no_tool_call",
|
|
2060
|
+
message: `model did not invoke '${toolName}' within ${maxTurns} turns (subtype=${terminalSubtype ?? "unknown"})`,
|
|
2061
|
+
cost,
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
catch (err) {
|
|
2065
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2066
|
+
const cost = withDurationMs(emptyCost(), startMs);
|
|
2067
|
+
// Map auth-shape exceptions before the catch-all subprocess_crashed.
|
|
2068
|
+
if (/authentication_failed|unauthorized|invalid api key|sk-ant-/i.test(message)) {
|
|
2069
|
+
return { ok: false, errorClass: "auth_error", message, cost };
|
|
2070
|
+
}
|
|
2071
|
+
if (aborted.value) {
|
|
2072
|
+
return {
|
|
2073
|
+
ok: false,
|
|
2074
|
+
errorClass: classifyAbortReason(abortReason ?? params.abortSignal?.reason),
|
|
2075
|
+
message,
|
|
2076
|
+
cost,
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
return { ok: false, errorClass: "subprocess_crashed", message, cost };
|
|
2080
|
+
}
|
|
2081
|
+
finally {
|
|
2082
|
+
params.abortSignal?.removeEventListener("abort", onAbort);
|
|
2083
|
+
this.readTokenManager?.revoke(sessionDir);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* DELEGATED-TASK-MODE-DESIGN.md §9.1 — Claude SDK task mode. The
|
|
2088
|
+
* subprocess plans + executes 1..N MCP calls within `allowedTools` and
|
|
2089
|
+
* emits a final assistant message that the runtime helper validates
|
|
2090
|
+
* against the caller's `outputSchema`.
|
|
2091
|
+
*
|
|
2092
|
+
* Stream parsing differences from `runDelegatedTool`:
|
|
2093
|
+
* - We accept multiple `tool_use` blocks (counted against `maxToolCalls`).
|
|
2094
|
+
* - We track per-tool durations to feed `onToolStep`.
|
|
2095
|
+
* - We capture the *final* assistant text (after the last tool turn)
|
|
2096
|
+
* as the validation target, not a single tool's `tool_result`.
|
|
2097
|
+
*
|
|
2098
|
+
* Safety:
|
|
2099
|
+
* - `allowedTools` already excludes the destructive set when
|
|
2100
|
+
* `allowDestructive: false`; the SDK will not surface those tools.
|
|
2101
|
+
* - `disallowedTools` is the absolute-block layer + the destructive
|
|
2102
|
+
* set as defense-in-depth (so a future relaxation of allowedTools
|
|
2103
|
+
* can't accidentally widen the surface).
|
|
2104
|
+
*
|
|
2105
|
+
* The §6.2 "no retry after write" rule is enforced at the invoker
|
|
2106
|
+
* layer; this method just signals via `writeClassToolFired` whether
|
|
2107
|
+
* any destructive tool ran during the task.
|
|
2108
|
+
*/
|
|
2109
|
+
async runDelegatedTask(params) {
|
|
2110
|
+
const startMs = Date.now();
|
|
2111
|
+
const { systemPrompt, allowedTools, destructiveTools, writeClassTools, modelId, maxToolCalls, maxBudgetUsd, sessionDir, onToolStep, } = params;
|
|
2112
|
+
const daemonReadToken = this.readTokenManager?.issue(sessionDir) ?? this.readToken;
|
|
2113
|
+
const trace = [];
|
|
2114
|
+
// §6.2 / §7.4 — match against the *write-class* set (destructive ∪
|
|
2115
|
+
// reversible writes), not just destructive. Otherwise reversible
|
|
2116
|
+
// write tools like `create_draft` slip past the retry guard and the
|
|
2117
|
+
// single retry creates a duplicate side effect.
|
|
2118
|
+
//
|
|
2119
|
+
// Phase 1 (`/exec`) entries are fully-qualified exact names — the
|
|
2120
|
+
// exact-equality fast path inside `matchRunAllowedToolPattern` covers
|
|
2121
|
+
// them at one comparison. Phase 2 (`/api/delegated/run`) may pass
|
|
2122
|
+
// `*`-suffixed glob patterns derived from the caller's allowedTools
|
|
2123
|
+
// (DELEGATED-TASK-MODE-DESIGN.md §4.2); the shared helper handles both.
|
|
2124
|
+
const writeClassMatcher = (name) => writeClassTools.some((pattern) => matchRunAllowedToolPattern(pattern, name));
|
|
2125
|
+
let writeClassToolFired = false;
|
|
2126
|
+
// DELEGATED-TASK-MODE-DESIGN.md §13 Phase 3.1 — Claude SDK
|
|
2127
|
+
// structured-output. When the invoker passed `structuredOutputEnabled:
|
|
2128
|
+
// true` AND a `wrappedSchema`, configure `outputFormat` so the SDK
|
|
2129
|
+
// validates the model's final emission against the schema (with its
|
|
2130
|
+
// own internal retries) and surfaces it on `SDKResultSuccess.structured_output`.
|
|
2131
|
+
// We still capture the assistant text as a fallback — if the SDK
|
|
2132
|
+
// returns success without `structured_output` (older subtype, future
|
|
2133
|
+
// shape change, kill-switch flips off mid-call), the existing text
|
|
2134
|
+
// path takes over.
|
|
2135
|
+
const useStructuredOutput = params.structuredOutputEnabled === true
|
|
2136
|
+
&& !!params.wrappedSchema;
|
|
2137
|
+
let capturedStructured;
|
|
2138
|
+
let sawStructuredOutputRetryError = false;
|
|
2139
|
+
let stream = null;
|
|
2140
|
+
const aborted = { value: false };
|
|
2141
|
+
const onAbort = () => {
|
|
2142
|
+
aborted.value = true;
|
|
2143
|
+
void (async () => {
|
|
2144
|
+
try {
|
|
2145
|
+
await stream?.return?.(undefined);
|
|
2146
|
+
}
|
|
2147
|
+
catch {
|
|
2148
|
+
/* stream already closed */
|
|
2149
|
+
}
|
|
2150
|
+
})();
|
|
2151
|
+
};
|
|
2152
|
+
if (params.abortSignal) {
|
|
2153
|
+
if (params.abortSignal.aborted) {
|
|
2154
|
+
aborted.value = true;
|
|
2155
|
+
}
|
|
2156
|
+
else {
|
|
2157
|
+
params.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
const pendingByUseId = new Map();
|
|
2161
|
+
let toolCallCount = 0;
|
|
2162
|
+
let loopAborted = false;
|
|
2163
|
+
let assistantTextChunks = [];
|
|
2164
|
+
/** The "final" assistant message is the most recent assistant message
|
|
2165
|
+
* that contained NO `tool_use` block. The SDK emits one assistant
|
|
2166
|
+
* message per turn; the planning turns mix text + tool_use, the
|
|
2167
|
+
* closing turn is text-only. */
|
|
2168
|
+
let lastAssistantTextOnlyChunks = [];
|
|
2169
|
+
try {
|
|
2170
|
+
stream = query({
|
|
2171
|
+
prompt: systemPrompt,
|
|
2172
|
+
options: {
|
|
2173
|
+
model: modelId,
|
|
2174
|
+
maxTurns: Math.max(2, maxToolCalls + 1),
|
|
2175
|
+
maxBudgetUsd,
|
|
2176
|
+
cwd: sessionDir,
|
|
2177
|
+
env: buildDaemonApiCliEnv(sessionDir, this.config.apiPort, { readToken: daemonReadToken, sessionBackend: "claude" }),
|
|
2178
|
+
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
2179
|
+
permissionMode: "dontAsk",
|
|
2180
|
+
allowedTools: [...allowedTools],
|
|
2181
|
+
// Defense-in-depth: absolute-block layer + destructive denies.
|
|
2182
|
+
// Destructive entries are redundant with the allowedTools
|
|
2183
|
+
// subtraction (when allowDestructive=false) but kept so a
|
|
2184
|
+
// future allowedTools widening doesn't drop the guarantee.
|
|
2185
|
+
disallowedTools: [
|
|
2186
|
+
...ALWAYS_DISALLOWED_TOOLS,
|
|
2187
|
+
...(params.allowDestructive ? [] : destructiveTools),
|
|
2188
|
+
],
|
|
2189
|
+
// §13 Phase 3.1 — bind the wrapped schema (user schema OR
|
|
2190
|
+
// confirmation envelope OR error envelope) to SDK 0.2.98's
|
|
2191
|
+
// `outputFormat`. Result message carries `structured_output`
|
|
2192
|
+
// which we read below. Off when the kill switch is false or
|
|
2193
|
+
// the invoker omitted the wrapped schema.
|
|
2194
|
+
...(useStructuredOutput && params.wrappedSchema
|
|
2195
|
+
? {
|
|
2196
|
+
outputFormat: {
|
|
2197
|
+
type: "json_schema",
|
|
2198
|
+
schema: params.wrappedSchema,
|
|
2199
|
+
},
|
|
2200
|
+
}
|
|
2201
|
+
: {}),
|
|
2202
|
+
},
|
|
2203
|
+
});
|
|
2204
|
+
let cost = emptyCost();
|
|
2205
|
+
try {
|
|
2206
|
+
for await (const message of stream) {
|
|
2207
|
+
if (aborted.value || loopAborted)
|
|
2208
|
+
break;
|
|
2209
|
+
if (message.type === "assistant") {
|
|
2210
|
+
const assistantMsg = message;
|
|
2211
|
+
const blocks = assistantMsg.message?.content;
|
|
2212
|
+
if (!Array.isArray(blocks))
|
|
2213
|
+
continue;
|
|
2214
|
+
const textChunks = [];
|
|
2215
|
+
let sawToolUse = false;
|
|
2216
|
+
for (const block of blocks) {
|
|
2217
|
+
if (!block || typeof block !== "object")
|
|
2218
|
+
continue;
|
|
2219
|
+
const blockType = block.type;
|
|
2220
|
+
if (blockType === "text") {
|
|
2221
|
+
const text = block.text;
|
|
2222
|
+
if (typeof text === "string")
|
|
2223
|
+
textChunks.push(text);
|
|
2224
|
+
continue;
|
|
2225
|
+
}
|
|
2226
|
+
if (blockType !== "tool_use")
|
|
2227
|
+
continue;
|
|
2228
|
+
sawToolUse = true;
|
|
2229
|
+
const blockName = block.name;
|
|
2230
|
+
const blockId = block.id;
|
|
2231
|
+
const blockArgs = block.input;
|
|
2232
|
+
if (typeof blockName !== "string" || typeof blockId !== "string") {
|
|
2233
|
+
continue;
|
|
2234
|
+
}
|
|
2235
|
+
toolCallCount += 1;
|
|
2236
|
+
if (toolCallCount > maxToolCalls) {
|
|
2237
|
+
// §7.5 — once the cap is exceeded, abort. The next
|
|
2238
|
+
// tool_use is treated as overrun.
|
|
2239
|
+
loopAborted = true;
|
|
2240
|
+
aborted.value = true;
|
|
2241
|
+
try {
|
|
2242
|
+
await stream?.return?.(undefined);
|
|
2243
|
+
}
|
|
2244
|
+
catch {
|
|
2245
|
+
/* already closed */
|
|
2246
|
+
}
|
|
2247
|
+
break;
|
|
2248
|
+
}
|
|
2249
|
+
if (writeClassMatcher(blockName)) {
|
|
2250
|
+
writeClassToolFired = true;
|
|
2251
|
+
}
|
|
2252
|
+
pendingByUseId.set(blockId, {
|
|
2253
|
+
name: blockName,
|
|
2254
|
+
args: blockArgs,
|
|
2255
|
+
startedAt: Date.now(),
|
|
2256
|
+
});
|
|
2257
|
+
}
|
|
2258
|
+
assistantTextChunks = assistantTextChunks.concat(textChunks);
|
|
2259
|
+
if (!sawToolUse && textChunks.length > 0) {
|
|
2260
|
+
lastAssistantTextOnlyChunks = textChunks;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
else if (message.type === "user") {
|
|
2264
|
+
const userMsg = message;
|
|
2265
|
+
const content = userMsg.message?.content;
|
|
2266
|
+
if (!Array.isArray(content))
|
|
2267
|
+
continue;
|
|
2268
|
+
for (const block of content) {
|
|
2269
|
+
if (!block || typeof block !== "object")
|
|
2270
|
+
continue;
|
|
2271
|
+
if (block.type !== "tool_result")
|
|
2272
|
+
continue;
|
|
2273
|
+
const tuid = block.tool_use_id;
|
|
2274
|
+
if (typeof tuid !== "string")
|
|
2275
|
+
continue;
|
|
2276
|
+
const pending = pendingByUseId.get(tuid);
|
|
2277
|
+
if (!pending)
|
|
2278
|
+
continue;
|
|
2279
|
+
pendingByUseId.delete(tuid);
|
|
2280
|
+
const isToolError = block.is_error === true;
|
|
2281
|
+
// `tool_result` content is either a string or an array of
|
|
2282
|
+
// content blocks (typically a single `{type:"text", text}`).
|
|
2283
|
+
// The MCP SDK wraps connector JSON responses by serializing
|
|
2284
|
+
// to that text body, so the response-shape walker
|
|
2285
|
+
// downstream wants the parsed object. Pull the first text
|
|
2286
|
+
// block, JSON-parse when possible, fallback to the raw
|
|
2287
|
+
// string so the field is always populated for ok steps.
|
|
2288
|
+
let parsedToolResult;
|
|
2289
|
+
const blockContent = block.content;
|
|
2290
|
+
if (typeof blockContent === "string") {
|
|
2291
|
+
try {
|
|
2292
|
+
parsedToolResult = JSON.parse(blockContent);
|
|
2293
|
+
}
|
|
2294
|
+
catch {
|
|
2295
|
+
parsedToolResult = blockContent;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
else if (Array.isArray(blockContent)) {
|
|
2299
|
+
const firstText = blockContent.find((b) => !!b
|
|
2300
|
+
&& typeof b === "object"
|
|
2301
|
+
&& b.type === "text"
|
|
2302
|
+
&& typeof b.text === "string");
|
|
2303
|
+
if (firstText) {
|
|
2304
|
+
try {
|
|
2305
|
+
parsedToolResult = JSON.parse(firstText.text);
|
|
2306
|
+
}
|
|
2307
|
+
catch {
|
|
2308
|
+
parsedToolResult = firstText.text;
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
else {
|
|
2312
|
+
parsedToolResult = blockContent;
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
const step = {
|
|
2316
|
+
toolName: pending.name,
|
|
2317
|
+
toolArgs: pending.args,
|
|
2318
|
+
durationMs: Date.now() - pending.startedAt,
|
|
2319
|
+
status: isToolError ? "error" : "ok",
|
|
2320
|
+
costUsd: null,
|
|
2321
|
+
tokensInput: null,
|
|
2322
|
+
tokensOutput: null,
|
|
2323
|
+
toolResult: parsedToolResult,
|
|
2324
|
+
};
|
|
2325
|
+
trace.push(step);
|
|
2326
|
+
onToolStep?.(step);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
else if (message.type === "result") {
|
|
2330
|
+
const r = message;
|
|
2331
|
+
cost = {
|
|
2332
|
+
tokensInput: r.usage.input_tokens ?? 0,
|
|
2333
|
+
tokensOutput: r.usage.output_tokens ?? 0,
|
|
2334
|
+
cacheCreationTokens: r.usage.cache_creation_input_tokens ?? 0,
|
|
2335
|
+
cacheReadTokens: r.usage.cache_read_input_tokens ?? 0,
|
|
2336
|
+
costUsd: r.total_cost_usd ?? 0,
|
|
2337
|
+
durationMs: r.duration_ms ?? Date.now() - startMs,
|
|
2338
|
+
numTurns: r.num_turns ?? 0,
|
|
2339
|
+
};
|
|
2340
|
+
// §13 Phase 3.1 — capture structured output when present, and
|
|
2341
|
+
// map the SDK's structured-output-retry-exhausted subtype to
|
|
2342
|
+
// a parse_error so the invoker classifies it consistently
|
|
2343
|
+
// with the text-extract path.
|
|
2344
|
+
if (r.subtype === "success") {
|
|
2345
|
+
const success = r;
|
|
2346
|
+
if (success.structured_output !== undefined) {
|
|
2347
|
+
capturedStructured = success.structured_output;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
else if (r.subtype === "error_max_structured_output_retries") {
|
|
2351
|
+
sawStructuredOutputRetryError = true;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
finally {
|
|
2357
|
+
try {
|
|
2358
|
+
await stream?.return?.(undefined);
|
|
2359
|
+
}
|
|
2360
|
+
catch {
|
|
2361
|
+
/* already closed */
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
cost = withDurationMs(cost, startMs);
|
|
2365
|
+
if (loopAborted) {
|
|
2366
|
+
return {
|
|
2367
|
+
ok: false,
|
|
2368
|
+
errorClass: "loop_aborted",
|
|
2369
|
+
message: `subprocess exceeded maxToolCalls=${maxToolCalls}`,
|
|
2370
|
+
cost,
|
|
2371
|
+
trace,
|
|
2372
|
+
writeClassToolFired,
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
if (aborted.value) {
|
|
2376
|
+
const errorClass = classifyAbortReason(params.abortSignal?.reason);
|
|
2377
|
+
return {
|
|
2378
|
+
ok: false,
|
|
2379
|
+
errorClass,
|
|
2380
|
+
message: errorClass === "timeout"
|
|
2381
|
+
? "delegated task timed out (wall-clock)"
|
|
2382
|
+
: "delegated task cancelled by caller",
|
|
2383
|
+
cost,
|
|
2384
|
+
trace,
|
|
2385
|
+
writeClassToolFired,
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
const finalText = lastAssistantTextOnlyChunks.length > 0
|
|
2389
|
+
? lastAssistantTextOnlyChunks.join("\n").trim()
|
|
2390
|
+
: assistantTextChunks.join("\n").trim();
|
|
2391
|
+
// §13 Phase 3.1 — `error_max_structured_output_retries` typically
|
|
2392
|
+
// fires when the model wanted to emit a §7.2 confirmation envelope
|
|
2393
|
+
// or §5.1 error envelope, neither of which satisfies the user's
|
|
2394
|
+
// narrow schema. The assistant text emissions captured during those
|
|
2395
|
+
// retries land in `assistantTextChunks` / `lastAssistantTextOnlyChunks`,
|
|
2396
|
+
// so the invoker's text-extract chain can route them via
|
|
2397
|
+
// `detectConfirmationEnvelope` / `detectErrorEnvelope`. Only return
|
|
2398
|
+
// `parse_error` if there is also no usable text — otherwise fall
|
|
2399
|
+
// through to the text-emission path (no `structuredOutput` field
|
|
2400
|
+
// set, so the invoker uses `rawAssistantText`).
|
|
2401
|
+
if (sawStructuredOutputRetryError
|
|
2402
|
+
&& capturedStructured === undefined
|
|
2403
|
+
&& finalText.length === 0) {
|
|
2404
|
+
return {
|
|
2405
|
+
ok: false,
|
|
2406
|
+
errorClass: "parse_error",
|
|
2407
|
+
message: "Claude SDK exhausted structured-output retries and emitted no text fallback.",
|
|
2408
|
+
cost,
|
|
2409
|
+
trace,
|
|
2410
|
+
writeClassToolFired,
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
// §13 Phase 3.1 — when the SDK supplied `structured_output`, that
|
|
2414
|
+
// is the validated final emission; the assistant text may be empty
|
|
2415
|
+
// (the SDK consumes the JSON internally on success). Skip the
|
|
2416
|
+
// empty-text parse_error guard in that case.
|
|
2417
|
+
if (capturedStructured === undefined && finalText.length === 0) {
|
|
2418
|
+
return {
|
|
2419
|
+
ok: false,
|
|
2420
|
+
errorClass: "parse_error",
|
|
2421
|
+
message: "Claude SDK stream ended without a text-only assistant turn",
|
|
2422
|
+
cost,
|
|
2423
|
+
trace,
|
|
2424
|
+
writeClassToolFired,
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
return {
|
|
2428
|
+
ok: true,
|
|
2429
|
+
// When structured output is present, `rawAssistantText` is purely
|
|
2430
|
+
// a fallback; the invoker prefers `structuredOutput`. Carry both
|
|
2431
|
+
// so a future kill-switch flip mid-restart still sees an
|
|
2432
|
+
// extractable text emission.
|
|
2433
|
+
rawAssistantText: finalText,
|
|
2434
|
+
cost,
|
|
2435
|
+
trace,
|
|
2436
|
+
writeClassToolFired,
|
|
2437
|
+
...(capturedStructured !== undefined
|
|
2438
|
+
? { structuredOutput: capturedStructured }
|
|
2439
|
+
: {}),
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
2442
|
+
catch (err) {
|
|
2443
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2444
|
+
const cost = withDurationMs(emptyCost(), startMs);
|
|
2445
|
+
if (/authentication_failed|unauthorized|invalid api key|sk-ant-/i.test(message)) {
|
|
2446
|
+
return {
|
|
2447
|
+
ok: false,
|
|
2448
|
+
errorClass: "auth_error",
|
|
2449
|
+
message,
|
|
2450
|
+
cost,
|
|
2451
|
+
trace,
|
|
2452
|
+
writeClassToolFired,
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
if (aborted.value) {
|
|
2456
|
+
return {
|
|
2457
|
+
ok: false,
|
|
2458
|
+
errorClass: classifyAbortReason(params.abortSignal?.reason),
|
|
2459
|
+
message,
|
|
2460
|
+
cost,
|
|
2461
|
+
trace,
|
|
2462
|
+
writeClassToolFired,
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
return {
|
|
2466
|
+
ok: false,
|
|
2467
|
+
errorClass: "subprocess_crashed",
|
|
2468
|
+
message,
|
|
2469
|
+
cost,
|
|
2470
|
+
trace,
|
|
2471
|
+
writeClassToolFired,
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
finally {
|
|
2475
|
+
params.abortSignal?.removeEventListener("abort", onAbort);
|
|
2476
|
+
this.readTokenManager?.revoke(sessionDir);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
function extractClaudeProbeTools(message) {
|
|
2481
|
+
if (!message || typeof message !== "object")
|
|
2482
|
+
return [];
|
|
2483
|
+
const out = [];
|
|
2484
|
+
const record = message;
|
|
2485
|
+
if (record.type === "system" && record.subtype === "init" && Array.isArray(record.tools)) {
|
|
2486
|
+
addClaudeProbeTools(record.tools, out);
|
|
2487
|
+
}
|
|
2488
|
+
if (record.type === "assistant" || record.type === "user") {
|
|
2489
|
+
addClaudeProbeTools(record.message, out);
|
|
2490
|
+
}
|
|
2491
|
+
if (record.type === "result") {
|
|
2492
|
+
addClaudeProbeTools(record.result, out);
|
|
2493
|
+
}
|
|
2494
|
+
return out;
|
|
2495
|
+
}
|
|
2496
|
+
function addClaudeProbeTools(value, out, depth = 0) {
|
|
2497
|
+
if (depth > 8 || value === null || value === undefined)
|
|
2498
|
+
return;
|
|
2499
|
+
if (typeof value === "string") {
|
|
2500
|
+
for (const match of value.matchAll(CLAUDE_CONNECTOR_TOOL_RE)) {
|
|
2501
|
+
out.push(match[0]);
|
|
2502
|
+
}
|
|
2503
|
+
if (isClaudeProbeToolName(value))
|
|
2504
|
+
out.push(value);
|
|
2505
|
+
return;
|
|
2506
|
+
}
|
|
2507
|
+
if (Array.isArray(value)) {
|
|
2508
|
+
for (const item of value)
|
|
2509
|
+
addClaudeProbeTools(item, out, depth + 1);
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2512
|
+
if (typeof value !== "object")
|
|
2513
|
+
return;
|
|
2514
|
+
const record = value;
|
|
2515
|
+
addClaudeProbeTools(record.tool_name, out, depth + 1);
|
|
2516
|
+
addClaudeProbeTools(record.text, out, depth + 1);
|
|
2517
|
+
addClaudeProbeTools(record.content, out, depth + 1);
|
|
2518
|
+
addClaudeProbeTools(record.message, out, depth + 1);
|
|
2519
|
+
addClaudeProbeTools(record.result, out, depth + 1);
|
|
2520
|
+
addClaudeProbeTools(record.tools, out, depth + 1);
|
|
2521
|
+
}
|
|
2522
|
+
function isClaudeProbeToolName(value) {
|
|
2523
|
+
return CLAUDE_PROBE_TOOL_PREFIXES.some((prefix) => {
|
|
2524
|
+
if (!value.startsWith(prefix))
|
|
2525
|
+
return false;
|
|
2526
|
+
// Hyphen is part of the alphabet for kebab-case connectors (Notion's
|
|
2527
|
+
// `notion-search` etc.). Snake-case connectors keep working because
|
|
2528
|
+
// `_` is still in the class.
|
|
2529
|
+
return /^[A-Za-z0-9_-]+$/.test(value.slice(prefix.length));
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
function describeClaudeProbeResultError(result) {
|
|
2533
|
+
if ("result" in result && typeof result.result === "string" && result.result.trim()) {
|
|
2534
|
+
return result.result.trim();
|
|
2535
|
+
}
|
|
2536
|
+
if ("errors" in result && Array.isArray(result.errors) && result.errors.length > 0) {
|
|
2537
|
+
return result.errors.join("; ");
|
|
2538
|
+
}
|
|
2539
|
+
return result.subtype;
|
|
2540
|
+
}
|
|
2541
|
+
//# sourceMappingURL=claude-code-core.js.map
|