@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,2331 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve as resolvePath } from "node:path";
|
|
4
|
+
import { APP_NAME, collectSessionDeniedTools, getAgentDayDateStr, INTEGRATION_DESCRIPTORS, INTEGRATION_KEYS, isAutonomousProcessKey, isPlausibleGeminiApiKey, matchRunAllowedToolPattern, } from "@aitne/shared";
|
|
5
|
+
import { readIntegrations } from "../../db/integrations-store.js";
|
|
6
|
+
import { getContextDir } from "../../config.js";
|
|
7
|
+
import { cleanupSessionWorkdir, createSessionWorkdir } from "../workdir.js";
|
|
8
|
+
import { BackendDecisiveFailure, BackendQuotaError, DelegatedProxyTimeoutError, classifyAbortReason, } from "../agent-core.js";
|
|
9
|
+
import { IdleWatchdog } from "./idle-watchdog.js";
|
|
10
|
+
import { buildDelegatedToolPrompt, emptyCost, tryParseToolResult, withDurationMs, } from "../../services/delegated-tool-runtime.js";
|
|
11
|
+
import { DELEGATED_PROXY_DEFAULTS } from "../../services/delegated-proxy-config.js";
|
|
12
|
+
import { materializeMcpForSession } from "../../services/mcp/session-materializer.js";
|
|
13
|
+
import { parseMcpToolName } from "../../services/mcp/risk.js";
|
|
14
|
+
import { logMcpToolCall } from "../../services/mcp/tool-audit.js";
|
|
15
|
+
import { buildDaemonApiCliEnv } from "../daemon-api-cli.js";
|
|
16
|
+
import { ALWAYS_DISALLOWED_TOOLS } from "../../safety/always-disallowed.js";
|
|
17
|
+
import { CliPathCache, parseJsonLine, runLineCommand, } from "./cli-utils.js";
|
|
18
|
+
import { isPathInsideOrEqual, jsonStringPathForms, shellPathForms, } from "../path-compat.js";
|
|
19
|
+
import { probeApiKeyServerSide } from "./api-key-probe.js";
|
|
20
|
+
import { extractSilentApiErrors, logSilentApiErrors, } from "./silent-api-error-detector.js";
|
|
21
|
+
import { estimateTextInputTokens, findRegisteredModel, getModelsForBackend, latestLiteFor, } from "./model-registry.js";
|
|
22
|
+
import { PriceFetcher } from "./price-fetcher.js";
|
|
23
|
+
import { buildExecutionPrompt, buildSummaryPrompt } from "./prompt-utils.js";
|
|
24
|
+
import { createLogger } from "../../logging.js";
|
|
25
|
+
import { readRuntimeState, writeRuntimeState } from "../../db/runtime-state.js";
|
|
26
|
+
const logger = createLogger("gemini-cli-core");
|
|
27
|
+
/**
|
|
28
|
+
* runtime_state key for the Gemini per-agent-day request counter.
|
|
29
|
+
* Gemini meters per-day model requests, and `maxTurns` alone cannot
|
|
30
|
+
* bound daily consumption — so this counter applies a conservative
|
|
31
|
+
* fixed ceiling that matches the free Gemini API tier (500 requests/
|
|
32
|
+
* day × 0.9 = 450 requests/day). Operators on higher-quota Gemini
|
|
33
|
+
* accounts can widen via the dashboard config.
|
|
34
|
+
*/
|
|
35
|
+
const GEMINI_REQUESTS_STATE_KEY = "gemini_requests_today";
|
|
36
|
+
/**
|
|
37
|
+
* Conservative default. Replaces the legacy plan-driven ceilings — the
|
|
38
|
+
* daemon does not ask the operator which Gemini account tier they
|
|
39
|
+
* hold (Aitne is intended to run on `GEMINI_API_KEY` / `GOOGLE_API_KEY`,
|
|
40
|
+
* with the CLI's local auth as a fallback), so we cannot infer a
|
|
41
|
+
* tier-specific quota and assume the free-tier published cap. When an
|
|
42
|
+
* API key is configured, the upstream Google quota is the real bound
|
|
43
|
+
* and this counter just stops the daemon from blowing past 450/day in
|
|
44
|
+
* the worst case.
|
|
45
|
+
*/
|
|
46
|
+
const GEMINI_DAILY_REQUEST_CEILING = 450;
|
|
47
|
+
/** Policy file name written into each session workdir. */
|
|
48
|
+
const ADMIN_POLICY_FILENAME = ".pa-admin-policy.toml";
|
|
49
|
+
const EMPTY_USAGE = {
|
|
50
|
+
inputTokens: 0,
|
|
51
|
+
outputTokens: 0,
|
|
52
|
+
cacheCreationInputTokens: 0,
|
|
53
|
+
cacheReadInputTokens: 0,
|
|
54
|
+
};
|
|
55
|
+
export class GeminiCliCore {
|
|
56
|
+
config;
|
|
57
|
+
writeTracker;
|
|
58
|
+
priceFetcher;
|
|
59
|
+
db;
|
|
60
|
+
backendId = "gemini";
|
|
61
|
+
// Lazily re-resolved with a 60 s TTL — see ClaudeCodeCore for rationale (§9.4).
|
|
62
|
+
cliPathCache;
|
|
63
|
+
/** Legacy shared read token injected into the Gemini subprocess env. */
|
|
64
|
+
readToken;
|
|
65
|
+
/** Scoped token manager preferred over the legacy shared read token. */
|
|
66
|
+
readTokenManager;
|
|
67
|
+
get cliPath() {
|
|
68
|
+
return this.cliPathCache.get();
|
|
69
|
+
}
|
|
70
|
+
constructor(config,
|
|
71
|
+
/**
|
|
72
|
+
* Shared AgentWriteTracker. When present, vault-scoped writes detected in
|
|
73
|
+
* Gemini's tool_use stream are pre-marked so the ObsidianWatcher attributes
|
|
74
|
+
* the chokidar event to `actor='agent'` instead of `'user'`.
|
|
75
|
+
*/
|
|
76
|
+
writeTracker, priceFetcher = new PriceFetcher(config.dataDir),
|
|
77
|
+
/**
|
|
78
|
+
* DB handle for the per-agent-day Gemini request counter. Optional so
|
|
79
|
+
* unit tests that construct the core without a database get
|
|
80
|
+
* graceful degradation: no quota enforcement and no counter writes.
|
|
81
|
+
* Production always supplies this.
|
|
82
|
+
*/
|
|
83
|
+
db = null) {
|
|
84
|
+
this.config = config;
|
|
85
|
+
this.writeTracker = writeTracker;
|
|
86
|
+
this.priceFetcher = priceFetcher;
|
|
87
|
+
this.db = db;
|
|
88
|
+
this.cliPathCache = new CliPathCache("gemini");
|
|
89
|
+
}
|
|
90
|
+
/** Set the per-daemon-boot read token for subprocess-local daemon API auth. */
|
|
91
|
+
setReadToken(token) {
|
|
92
|
+
this.readToken = token;
|
|
93
|
+
}
|
|
94
|
+
setReadTokenManager(manager) {
|
|
95
|
+
this.readTokenManager = manager;
|
|
96
|
+
}
|
|
97
|
+
mcpContext;
|
|
98
|
+
setMcpContext(context) {
|
|
99
|
+
this.mcpContext = context;
|
|
100
|
+
}
|
|
101
|
+
async materializeMcp(sessionDir, processKey) {
|
|
102
|
+
if (!this.mcpContext) {
|
|
103
|
+
return {
|
|
104
|
+
servers: [],
|
|
105
|
+
env: {},
|
|
106
|
+
configPath: null,
|
|
107
|
+
claudeMcpServers: null,
|
|
108
|
+
disallowedTools: [],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Allow mode bypasses the approve-tier MCP strip (see claude-code-core
|
|
112
|
+
// for the rationale — "strong permission mode" enables every MCP tool,
|
|
113
|
+
// autonomous routines included).
|
|
114
|
+
const allowMode = this.config.geminiExecutionPermissionMode === "allow";
|
|
115
|
+
const autonomous = !allowMode && (processKey ? isAutonomousProcessKey(processKey) : false);
|
|
116
|
+
return materializeMcpForSession({
|
|
117
|
+
db: this.mcpContext.db,
|
|
118
|
+
blobStore: this.mcpContext.blobStore,
|
|
119
|
+
sessionDir,
|
|
120
|
+
backendId: this.backendId,
|
|
121
|
+
autonomous,
|
|
122
|
+
contextDir: getContextDir(this.config),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async execute(params, streamCallbacks) {
|
|
126
|
+
return await this.runTurn({
|
|
127
|
+
prompt: appendGeminiAttachmentTokens(buildExecutionPrompt(params.prompt, params.context, params.event, params.conversationHistory), params.stagedAttachments),
|
|
128
|
+
modelId: params.modelId,
|
|
129
|
+
eventType: params.event.type,
|
|
130
|
+
processKey: params.processKey,
|
|
131
|
+
sessionDir: params.sessionDir,
|
|
132
|
+
maxBudgetUsd: params.maxBudgetUsd,
|
|
133
|
+
webSearchEnabled: params.webSearchEnabled,
|
|
134
|
+
turnToken: params.turnToken,
|
|
135
|
+
sessionDbId: params.sessionDbId,
|
|
136
|
+
eventCorrelationId: params.event.correlationId,
|
|
137
|
+
}, streamCallbacks);
|
|
138
|
+
}
|
|
139
|
+
async executeResume(params, streamCallbacks) {
|
|
140
|
+
return await this.runTurn({
|
|
141
|
+
prompt: appendGeminiAttachmentTokens(params.message, params.stagedAttachments),
|
|
142
|
+
modelId: params.modelId,
|
|
143
|
+
eventType: "message.received",
|
|
144
|
+
sessionDir: params.sessionDir,
|
|
145
|
+
resumeSessionId: params.sessionId,
|
|
146
|
+
maxBudgetUsd: params.maxBudgetUsd,
|
|
147
|
+
webSearchEnabled: params.webSearchEnabled,
|
|
148
|
+
turnToken: params.turnToken,
|
|
149
|
+
sessionDbId: params.sessionDbId,
|
|
150
|
+
eventCorrelationId: params.eventCorrelationId,
|
|
151
|
+
}, streamCallbacks);
|
|
152
|
+
}
|
|
153
|
+
async summarize(conversationText) {
|
|
154
|
+
const result = await this.runTurn({
|
|
155
|
+
prompt: buildSummaryPrompt(conversationText),
|
|
156
|
+
modelId: this.pickSummaryModel(),
|
|
157
|
+
eventType: "message.received",
|
|
158
|
+
});
|
|
159
|
+
return result.output;
|
|
160
|
+
}
|
|
161
|
+
async checkAuth() {
|
|
162
|
+
if (!this.cliPath) {
|
|
163
|
+
return { ok: false, reason: "Gemini CLI is not installed or not on PATH." };
|
|
164
|
+
}
|
|
165
|
+
const rawApiKey = process.env.GEMINI_API_KEY?.trim() || process.env.GOOGLE_API_KEY?.trim();
|
|
166
|
+
if (rawApiKey) {
|
|
167
|
+
if (!isPlausibleGeminiApiKey(rawApiKey)) {
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
reason: "GEMINI_API_KEY / GOOGLE_API_KEY is set but does not look like a Google API key (expected `AIza…`, 39 chars).",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return { ok: true, method: "api_key" };
|
|
174
|
+
}
|
|
175
|
+
if (process.env.GOOGLE_APPLICATION_CREDENTIALS?.trim()
|
|
176
|
+
&& process.env.GOOGLE_CLOUD_PROJECT?.trim()) {
|
|
177
|
+
return { ok: true, method: "vertex" };
|
|
178
|
+
}
|
|
179
|
+
if (existsSync(join(homedir(), ".gemini", "oauth_creds.json"))
|
|
180
|
+
|| existsSync(join(homedir(), ".gemini", "google_accounts.json"))) {
|
|
181
|
+
return { ok: true, method: "oauth" };
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
ok: false,
|
|
185
|
+
reason: "Gemini is not authenticated. Configure OAuth, Vertex, or GEMINI_API_KEY.",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Detailed auth probe. Three modes:
|
|
190
|
+
* - **API key** (`GEMINI_API_KEY` / `GOOGLE_API_KEY`): format check +
|
|
191
|
+
* server-side probe via `probeApiKeyServerSide("google", ...)`
|
|
192
|
+
* (roadmap §9.1). Throws on network/timeout.
|
|
193
|
+
* - **Vertex** (`GOOGLE_APPLICATION_CREDENTIALS` + `GOOGLE_CLOUD_PROJECT`):
|
|
194
|
+
* env-var presence check only.
|
|
195
|
+
* - **OAuth** (`~/.gemini/oauth_creds.json`): presence check on
|
|
196
|
+
* `refresh_token` field + grace window. Does NOT validate the
|
|
197
|
+
* refresh_token server-side; real failures surface via the reactive
|
|
198
|
+
* path during `execute()`.
|
|
199
|
+
*/
|
|
200
|
+
async checkAuthDetailed() {
|
|
201
|
+
if (!this.cliPath) {
|
|
202
|
+
return {
|
|
203
|
+
ok: false,
|
|
204
|
+
status: "missing",
|
|
205
|
+
method: "cli_login",
|
|
206
|
+
detail: "Gemini CLI not found on PATH",
|
|
207
|
+
recoveryCommand: "npm install -g @google/gemini-cli",
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const rawApiKey = process.env.GEMINI_API_KEY?.trim() || process.env.GOOGLE_API_KEY?.trim();
|
|
211
|
+
if (rawApiKey) {
|
|
212
|
+
if (!isPlausibleGeminiApiKey(rawApiKey)) {
|
|
213
|
+
return {
|
|
214
|
+
ok: false,
|
|
215
|
+
status: "expired",
|
|
216
|
+
method: "api_key",
|
|
217
|
+
detail: "GEMINI_API_KEY / GOOGLE_API_KEY does not match Google API key format (`AIza…`, 39 chars).",
|
|
218
|
+
recoveryCommand: "Unset the env var or replace it with a valid Google API key",
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// Format is plausible — attempt a server-side probe to detect
|
|
222
|
+
// revoked keys within 1 hourly cycle (roadmap §9.1).
|
|
223
|
+
const probe = await probeApiKeyServerSide("google", rawApiKey);
|
|
224
|
+
return {
|
|
225
|
+
ok: probe.ok,
|
|
226
|
+
status: probe.ok ? "ok" : "expired",
|
|
227
|
+
method: "api_key",
|
|
228
|
+
detail: probe.detail,
|
|
229
|
+
...(!probe.ok && {
|
|
230
|
+
recoveryCommand: "Unset the env var or replace it with a valid Google API key",
|
|
231
|
+
}),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (process.env.GOOGLE_APPLICATION_CREDENTIALS?.trim()
|
|
235
|
+
&& process.env.GOOGLE_CLOUD_PROJECT?.trim()) {
|
|
236
|
+
return { ok: true, status: "ok", method: "vertex" };
|
|
237
|
+
}
|
|
238
|
+
const oauthPath = join(homedir(), ".gemini", "oauth_creds.json");
|
|
239
|
+
if (existsSync(oauthPath)) {
|
|
240
|
+
try {
|
|
241
|
+
const raw = readFileSync(oauthPath, "utf-8");
|
|
242
|
+
const creds = JSON.parse(raw);
|
|
243
|
+
if (typeof creds.refresh_token === "string" && creds.refresh_token.length > 0) {
|
|
244
|
+
return { ok: true, status: "ok", method: "oauth" };
|
|
245
|
+
}
|
|
246
|
+
// refresh_token missing — treat recently-modified files as ok
|
|
247
|
+
// because the Gemini CLI has a known upstream bug where the field
|
|
248
|
+
// drops out mid-session (observed in 0.x CLI versions). Without
|
|
249
|
+
// this grace, every Gemini user would false-trip to "expired"
|
|
250
|
+
// until they re-authenticated. The grace window is tunable via
|
|
251
|
+
// `PA_GEMINI_OAUTH_GRACE_HOURS`; set to 0 to disable the grace
|
|
252
|
+
// entirely once the upstream bug is fixed.
|
|
253
|
+
const graceHours = parseGraceHours(process.env.PA_GEMINI_OAUTH_GRACE_HOURS, 24);
|
|
254
|
+
const hoursSinceModified = (Date.now() - statSync(oauthPath).mtimeMs) / 3_600_000;
|
|
255
|
+
if (graceHours > 0 && hoursSinceModified < graceHours) {
|
|
256
|
+
logger.warn({ graceHours, hoursSinceModified }, "Gemini oauth_creds.json missing refresh_token — applying grace window");
|
|
257
|
+
return {
|
|
258
|
+
ok: true,
|
|
259
|
+
status: "ok",
|
|
260
|
+
method: "oauth",
|
|
261
|
+
detail: `refresh_token missing but file was updated ${hoursSinceModified.toFixed(1)}h ago (< ${graceHours}h grace)`,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
status: "expired",
|
|
267
|
+
method: "oauth",
|
|
268
|
+
detail: "refresh_token missing from oauth_creds.json",
|
|
269
|
+
recoveryCommand: "gemini → Sign in with Google",
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
logger.warn({ err }, "Failed to read Gemini oauth_creds.json");
|
|
274
|
+
return {
|
|
275
|
+
ok: false,
|
|
276
|
+
status: "expired",
|
|
277
|
+
method: "oauth",
|
|
278
|
+
detail: err instanceof Error ? err.message : "Failed to parse oauth_creds.json",
|
|
279
|
+
recoveryCommand: "gemini → Sign in with Google",
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (existsSync(join(homedir(), ".gemini", "gemini-credentials.json"))) {
|
|
284
|
+
return {
|
|
285
|
+
ok: true,
|
|
286
|
+
status: "ok",
|
|
287
|
+
method: "oauth",
|
|
288
|
+
detail: "Encrypted storage detected — CLI handles validation",
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (existsSync(join(homedir(), ".gemini", "google_accounts.json"))) {
|
|
292
|
+
return { ok: true, status: "ok", method: "oauth" };
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
ok: false,
|
|
296
|
+
status: "expired",
|
|
297
|
+
method: "oauth",
|
|
298
|
+
detail: "No Gemini OAuth credentials found",
|
|
299
|
+
recoveryCommand: "gemini → Sign in with Google",
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
listModels() {
|
|
303
|
+
return getModelsForBackend(this.backendId);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Phase 5 §4.11 live probe for Gemini. Gemini CLI exposes no first-party
|
|
307
|
+
* tool-enumeration API and its MCP namespace (`mcp_<server>_<tool>`,
|
|
308
|
+
* single-underscore — confirmed via stream-event probe 2026-04-26)
|
|
309
|
+
* differs from Claude / Codex (`mcp__<server>__<tool>`).
|
|
310
|
+
*
|
|
311
|
+
* Strategy: scan the host for registered MCP servers (the
|
|
312
|
+
* `gemini-extension.json` `mcpServers` block under each subdir of
|
|
313
|
+
* `~/.gemini/extensions/` plus the `~/.gemini/settings.json`
|
|
314
|
+
* `mcpServers` block), then for every registry-declared Gemini
|
|
315
|
+
* connector whose namespace points at a detected server, synthesize
|
|
316
|
+
* the expected fully-qualified tool names from `capabilityTools`. The
|
|
317
|
+
* synthetic list is what `evaluateProbe` matches against the
|
|
318
|
+
* descriptor.
|
|
319
|
+
*
|
|
320
|
+
* Trade-off: this is a "presence-only" probe — it verifies the server
|
|
321
|
+
* is registered with Gemini, not that the agent's OAuth / tool-call
|
|
322
|
+
* actually works. Connector auth failures surface when the agent first
|
|
323
|
+
* invokes the tool. Codex / Claude run live `tool_search` queries
|
|
324
|
+
* because their MCP catalog is queryable from the SDK; Gemini does not
|
|
325
|
+
* expose that surface, and asking the model to enumerate via prompt
|
|
326
|
+
* proved unreliable (whitespace-only responses, see chat history).
|
|
327
|
+
*
|
|
328
|
+
* Two distinct outcomes:
|
|
329
|
+
* - Server NOT detected → returns `[]`. `evaluateProbe` matches
|
|
330
|
+
* against the connector's expected tools, finds none, reports all
|
|
331
|
+
* required capabilities missing — dashboard correctly shows
|
|
332
|
+
* "Gemini's <X> connector is not installed."
|
|
333
|
+
* - Server detected → returns the registry's claimed tool list for
|
|
334
|
+
* that server. `evaluateProbe` matches every entry exactly (since
|
|
335
|
+
* the list IS the registry's expectation), so all caps mark
|
|
336
|
+
* present. The dashboard shows "all features available." This is
|
|
337
|
+
* accurate for `google-workspace` (whose tool names were verified
|
|
338
|
+
* from `~/.gemini/extensions/google-workspace/dist/index.js` —
|
|
339
|
+
* `registerTool("gmail.*", ...)` etc. — at registry-write time)
|
|
340
|
+
* and is a guess for `notion` (registry assumes `notion-search`,
|
|
341
|
+
* `notion-fetch`, etc.; actual hosted-MCP names may diverge —
|
|
342
|
+
* surfaced via the install card's namespace caveat). Runtime
|
|
343
|
+
* `wrong_tool` errors are the failure mode for the latter.
|
|
344
|
+
*/
|
|
345
|
+
async probeTools() {
|
|
346
|
+
const detectedServers = new Set();
|
|
347
|
+
// Extension-installed MCP servers (e.g. google-workspace ships
|
|
348
|
+
// gmail.* and calendar.* via this path).
|
|
349
|
+
const extDir = join(homedir(), ".gemini", "extensions");
|
|
350
|
+
if (existsSync(extDir)) {
|
|
351
|
+
try {
|
|
352
|
+
const subdirs = readdirSync(extDir, { withFileTypes: true });
|
|
353
|
+
for (const ent of subdirs) {
|
|
354
|
+
if (!ent.isDirectory())
|
|
355
|
+
continue;
|
|
356
|
+
const manifestPath = join(extDir, ent.name, "gemini-extension.json");
|
|
357
|
+
if (!existsSync(manifestPath))
|
|
358
|
+
continue;
|
|
359
|
+
collectMcpServerNames(manifestPath, detectedServers);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
logger.warn({ err, extDir }, "failed to scan Gemini extensions");
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// User-added MCP servers (`gemini mcp add <name> ...`). Notion's
|
|
367
|
+
// hosted MCP server is typically registered here under the literal
|
|
368
|
+
// name `notion` — that's the assumption the registry's Notion
|
|
369
|
+
// descriptor encodes.
|
|
370
|
+
const settingsPath = join(homedir(), ".gemini", "settings.json");
|
|
371
|
+
if (existsSync(settingsPath)) {
|
|
372
|
+
collectMcpServerNames(settingsPath, detectedServers);
|
|
373
|
+
}
|
|
374
|
+
const tools = [];
|
|
375
|
+
for (const integrationKey of INTEGRATION_KEYS) {
|
|
376
|
+
const connector = INTEGRATION_DESCRIPTORS[integrationKey].backendConnectors.gemini;
|
|
377
|
+
if (!connector)
|
|
378
|
+
continue;
|
|
379
|
+
const serverName = extractGeminiServerName(connector.toolNamespace);
|
|
380
|
+
if (!serverName || !detectedServers.has(serverName))
|
|
381
|
+
continue;
|
|
382
|
+
for (const toolList of Object.values(connector.capabilityTools)) {
|
|
383
|
+
for (const t of toolList) {
|
|
384
|
+
tools.push(connector.toolNamespace + t);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const deduped = Array.from(new Set(tools));
|
|
389
|
+
logger.info({
|
|
390
|
+
detectedServers: [...detectedServers],
|
|
391
|
+
toolCount: deduped.length,
|
|
392
|
+
}, "Gemini probe collected tool manifest via host MCP scan");
|
|
393
|
+
return deduped;
|
|
394
|
+
}
|
|
395
|
+
async runTurn(params, streamCallbacks) {
|
|
396
|
+
// Pre-flight auth gate — intentionally absent in ClaudeCodeCore.
|
|
397
|
+
// Same reasoning as Codex: spawning the Gemini CLI subprocess has
|
|
398
|
+
// non-trivial TTFB, so reading `oauth_creds.json` (or the API key
|
|
399
|
+
// format) once here is cheaper than discovering the failure after
|
|
400
|
+
// the subprocess has already booted and begun streaming. Claude
|
|
401
|
+
// leans on the Agent SDK's own 401 instead, which is why it has
|
|
402
|
+
// no equivalent pre-flight. See the class-level comment on
|
|
403
|
+
// `ClaudeCodeCore` for the full rationale.
|
|
404
|
+
const auth = await this.checkAuth();
|
|
405
|
+
if (!auth.ok) {
|
|
406
|
+
logger.warn({ reason: auth.reason }, "Gemini auth check failed");
|
|
407
|
+
throw new BackendDecisiveFailure(this.backendId, "auth", new Error(auth.reason));
|
|
408
|
+
}
|
|
409
|
+
// Daily-request quota gate. Reads `backend_global_defaults.gemini_plan`
|
|
410
|
+
// → `PlanPreset.dailyRequestCeiling` and compares against the per-agent-day
|
|
411
|
+
// counter in runtime_state. Pre-flight refusal so we don't waste a CLI
|
|
412
|
+
// subprocess on a request we know is over quota. See
|
|
413
|
+
// `docs/design/09-safety-cost.md` §9.4.8 for the full contract.
|
|
414
|
+
const today = getAgentDayDateStr(this.config.timezone, this.config.dayBoundaryHour);
|
|
415
|
+
const ceiling = this.resolveDailyRequestCeiling();
|
|
416
|
+
if (ceiling !== null) {
|
|
417
|
+
const currentCount = this.readRequestsCount(today);
|
|
418
|
+
if (currentCount >= ceiling) {
|
|
419
|
+
logger.warn({ currentCount, ceiling, date: today }, "Gemini daily-request ceiling hit — refusing execution");
|
|
420
|
+
throw new BackendQuotaError(this.backendId, "daily_ceiling", null, `Gemini daily-request ceiling reached (${currentCount}/${ceiling}) — resets at the next agent-day boundary.`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
this.assertPromptWithinMaxBudget(params.prompt, params.maxBudgetUsd, params.modelId);
|
|
424
|
+
const startMs = Date.now();
|
|
425
|
+
const sessionDir = params.sessionDir ?? createSessionWorkdir(this.config.workspaceDir, params.eventType, `${this.config.dataDir}/skills`, {
|
|
426
|
+
backendId: this.backendId,
|
|
427
|
+
processKey: params.processKey,
|
|
428
|
+
character: this.config.character,
|
|
429
|
+
});
|
|
430
|
+
const ownsSessionDir = !params.sessionDir;
|
|
431
|
+
const daemonReadToken = this.readTokenManager?.issue(sessionDir) ?? this.readToken;
|
|
432
|
+
// Write admin policy to session workdir. The admin tier overrides the
|
|
433
|
+
// --approval-mode yolo grants, so this policy is the ONLY mechanism on
|
|
434
|
+
// Gemini for preserving non-negotiable invariants — chiefly that writes
|
|
435
|
+
// to the context directory must go through the daemon API.
|
|
436
|
+
//
|
|
437
|
+
// Strict mode: full whitelist policy (catch-all deny + explicit allows).
|
|
438
|
+
// Allow mode: minimal policy with NO catch-all — only denies context-dir
|
|
439
|
+
// writes and sensitive-path reads; everything else falls through to yolo.
|
|
440
|
+
const allowMode = this.config.geminiExecutionPermissionMode === "allow";
|
|
441
|
+
const policyPath = join(sessionDir, ADMIN_POLICY_FILENAME);
|
|
442
|
+
writeFileSync(policyPath, allowMode
|
|
443
|
+
? this.generateAllowModeMinimalPolicy()
|
|
444
|
+
: this.generateAdminPolicy({ webSearchEnabled: params.webSearchEnabled }), "utf-8");
|
|
445
|
+
const mcp = await this.materializeMcp(sessionDir, params.processKey);
|
|
446
|
+
logger.info({ eventType: params.eventType, model: params.modelId, promptLen: params.prompt.length, mcpServers: mcp.servers.map((s) => s.id) }, "Gemini execute started");
|
|
447
|
+
let assistantDelta = "";
|
|
448
|
+
let finalAssistantMessage = "";
|
|
449
|
+
let sessionId = params.resumeSessionId ?? null;
|
|
450
|
+
let lastError = null;
|
|
451
|
+
let resultStatus = "error";
|
|
452
|
+
let stats = null;
|
|
453
|
+
let streamed = false;
|
|
454
|
+
const deferStreamingUntilBudgetCheck = params.maxBudgetUsd !== undefined;
|
|
455
|
+
// Accumulate raw subprocess text across the run so we can scan once for
|
|
456
|
+
// `PA_API_ERROR` markers the pa-api / curl wrappers emit on HTTP errors.
|
|
457
|
+
// Gemini's stream schema doesn't expose a dedicated tool-output field, so
|
|
458
|
+
// we fall back to the raw stdout/stderr lines (which include both the
|
|
459
|
+
// JSONL itself and any uncaptured subprocess stderr).
|
|
460
|
+
const apiOutputBuffer = [];
|
|
461
|
+
try {
|
|
462
|
+
const runResult = await runLineCommand({
|
|
463
|
+
command: "gemini",
|
|
464
|
+
args: this.buildArgs(params, policyPath),
|
|
465
|
+
cwd: sessionDir,
|
|
466
|
+
env: {
|
|
467
|
+
...buildDaemonApiCliEnv(sessionDir, this.config.apiPort, {
|
|
468
|
+
readToken: daemonReadToken,
|
|
469
|
+
sessionBackend: "gemini",
|
|
470
|
+
sessionId: params.sessionDbId,
|
|
471
|
+
eventCorrelationId: params.eventCorrelationId,
|
|
472
|
+
}),
|
|
473
|
+
...mcp.env,
|
|
474
|
+
...(params.turnToken ? { PA_TURN_TOKEN: params.turnToken } : {}),
|
|
475
|
+
},
|
|
476
|
+
timeoutMs: this.config.executeTimeoutMinutes * 60 * 1000,
|
|
477
|
+
onStdoutLine: (line) => {
|
|
478
|
+
apiOutputBuffer.push(line);
|
|
479
|
+
const event = parseJsonLine(line);
|
|
480
|
+
if (!event?.type) {
|
|
481
|
+
if (isLikelyGeminiFailure(line)) {
|
|
482
|
+
lastError = line.trim();
|
|
483
|
+
}
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (event.type === "init" && typeof event.session_id === "string") {
|
|
487
|
+
sessionId = event.session_id;
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
// Pre-mark vault writes for observer attribution (same as Claude Code hook).
|
|
491
|
+
if (this.writeTracker && event.type === "tool_use") {
|
|
492
|
+
this.trackVaultWrite(event);
|
|
493
|
+
}
|
|
494
|
+
// B-003 Phase 4.4 — persist MCP tool call to `mcp_tool_calls`.
|
|
495
|
+
// Gemini emits `mcp_<server>_<tool>` (single underscore — see
|
|
496
|
+
// parseMcpToolName); host-installed Gemini MCPs (like
|
|
497
|
+
// `google-workspace` from extensions, or user-added `notion`)
|
|
498
|
+
// produce `serverId` values that DON'T appear in the daemon's
|
|
499
|
+
// `mcp_servers` table. The schema has no FK so writes succeed,
|
|
500
|
+
// but the dashboard's per-server activity panel (keyed by a
|
|
501
|
+
// matching server-card) won't surface them. The rows persist
|
|
502
|
+
// for any future "all MCP activity" view; for now they're a
|
|
503
|
+
// forensic-only audit trail.
|
|
504
|
+
if (event.type === "tool_use" && typeof event.tool_name === "string") {
|
|
505
|
+
const parsed = parseMcpToolName(event.tool_name);
|
|
506
|
+
if (parsed) {
|
|
507
|
+
logger.debug({
|
|
508
|
+
serverId: parsed.serverId,
|
|
509
|
+
toolName: parsed.toolName,
|
|
510
|
+
sessionId,
|
|
511
|
+
eventType: params.eventType,
|
|
512
|
+
}, "mcp.tool_call");
|
|
513
|
+
if (this.mcpContext?.db) {
|
|
514
|
+
try {
|
|
515
|
+
logMcpToolCall(this.mcpContext.db, {
|
|
516
|
+
serverId: parsed.serverId,
|
|
517
|
+
toolName: parsed.toolName,
|
|
518
|
+
eventType: params.eventType,
|
|
519
|
+
sessionId: sessionId ?? undefined,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
logger.warn({ err, serverId: parsed.serverId }, "mcp.tool_call audit insert failed");
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (event.type === "message" && event.role === "assistant") {
|
|
529
|
+
const content = typeof event.content === "string" ? event.content : "";
|
|
530
|
+
if (!content) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (event.delta) {
|
|
534
|
+
assistantDelta += content;
|
|
535
|
+
if (!deferStreamingUntilBudgetCheck) {
|
|
536
|
+
streamCallbacks?.onText?.(content);
|
|
537
|
+
streamed = true;
|
|
538
|
+
}
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
finalAssistantMessage = content;
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (event.type === "result") {
|
|
545
|
+
const payload = event.result ?? event;
|
|
546
|
+
resultStatus = payload.status ?? resultStatus;
|
|
547
|
+
stats = payload.stats ?? stats;
|
|
548
|
+
lastError = payload.error ?? lastError;
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
onStderrLine: (line) => {
|
|
552
|
+
apiOutputBuffer.push(line);
|
|
553
|
+
if (isLikelyGeminiFailure(line)) {
|
|
554
|
+
lastError = line.trim();
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
const apiErrors = extractSilentApiErrors(apiOutputBuffer.join("\n"));
|
|
559
|
+
if (apiErrors.length > 0) {
|
|
560
|
+
logSilentApiErrors(logger, apiErrors, {
|
|
561
|
+
backendId: this.backendId,
|
|
562
|
+
sessionId,
|
|
563
|
+
eventType: params.eventType,
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
if (runResult.timedOut) {
|
|
567
|
+
const err = new BackendDecisiveFailure(this.backendId, "timeout", new Error(`Gemini execution exceeded timeout of ${this.config.executeTimeoutMinutes} minutes`));
|
|
568
|
+
logger.error({ err, eventType: params.eventType, model: params.modelId, durationMs: Date.now() - startMs }, "Gemini execute timed out");
|
|
569
|
+
throw err;
|
|
570
|
+
}
|
|
571
|
+
const outputSource = finalAssistantMessage.trim().length > 0
|
|
572
|
+
? finalAssistantMessage
|
|
573
|
+
: assistantDelta;
|
|
574
|
+
const output = outputSource.trim();
|
|
575
|
+
if (resultStatus !== "success" || runResult.exitCode !== 0) {
|
|
576
|
+
const failureText = lastError
|
|
577
|
+
?? firstFailureLine(runResult.stdoutLines)
|
|
578
|
+
?? firstFailureLine(runResult.stderrLines)
|
|
579
|
+
?? "Gemini execution did not complete successfully.";
|
|
580
|
+
const classified = this.classifyFailure(failureText);
|
|
581
|
+
logger.error({ err: classified, eventType: params.eventType, model: params.modelId, exitCode: runResult.exitCode, durationMs: Date.now() - startMs }, "Gemini execute failed");
|
|
582
|
+
throw classified;
|
|
583
|
+
}
|
|
584
|
+
const normalizedUsage = normalizeGeminiUsage(stats);
|
|
585
|
+
const { modelUsage, costSource } = buildGeminiModelUsage(this.priceFetcher, stats);
|
|
586
|
+
const actualModelId = resolveActualGeminiModel(params.modelId, modelUsage);
|
|
587
|
+
const durationApiMs = stats?.duration_ms ?? Date.now() - startMs;
|
|
588
|
+
const estimatedCost = this.priceFetcher.estimateUsageCost({
|
|
589
|
+
backendId: this.backendId,
|
|
590
|
+
modelId: actualModelId,
|
|
591
|
+
usage: normalizedUsage,
|
|
592
|
+
fallbackModel: findRegisteredModel(this.backendId, actualModelId),
|
|
593
|
+
});
|
|
594
|
+
const costUsd = Object.values(modelUsage)
|
|
595
|
+
.reduce((total, usage) => total + usage.costUsd, 0)
|
|
596
|
+
|| estimatedCost.costUsd;
|
|
597
|
+
this.assertWithinMaxBudget(costUsd, params.maxBudgetUsd, actualModelId);
|
|
598
|
+
if (output && !streamed) {
|
|
599
|
+
streamCallbacks?.onText?.(output);
|
|
600
|
+
}
|
|
601
|
+
const durationMs = Date.now() - startMs;
|
|
602
|
+
logger.info({ eventType: params.eventType, model: actualModelId, durationMs, costUsd }, "Gemini execute completed");
|
|
603
|
+
// Bump the per-agent-day request counter on successful completion.
|
|
604
|
+
// We increment per-runTurn rather than per-JSONL-event because
|
|
605
|
+
// Gemini's streaming CLI may emit only delta messages without a
|
|
606
|
+
// final non-delta aggregate — counting on events made the ceiling
|
|
607
|
+
// enforcement silently break in streaming-only mode. Per-runTurn is
|
|
608
|
+
// protocol-agnostic and guaranteed to fire once per successful
|
|
609
|
+
// execution. Tool-fanout turns still count as one request, which
|
|
610
|
+
// undercounts real API consumption; that is an acceptable
|
|
611
|
+
// approximation for a safety-net ceiling (the tight 900 / 1350 /
|
|
612
|
+
// 1800 plan ceilings absorb the slack).
|
|
613
|
+
this.incrementRequestsCount(today);
|
|
614
|
+
return {
|
|
615
|
+
output,
|
|
616
|
+
sessionId,
|
|
617
|
+
backendId: this.backendId,
|
|
618
|
+
modelId: actualModelId,
|
|
619
|
+
costSource: Object.keys(modelUsage).length > 0 ? costSource : estimatedCost.costSource,
|
|
620
|
+
costUsd,
|
|
621
|
+
usage: normalizedUsage,
|
|
622
|
+
modelUsage,
|
|
623
|
+
numTurns: 1,
|
|
624
|
+
durationMs,
|
|
625
|
+
durationApiMs,
|
|
626
|
+
model: actualModelId,
|
|
627
|
+
isError: false,
|
|
628
|
+
stopReason: resultStatus,
|
|
629
|
+
contextUpdated: false,
|
|
630
|
+
// advisorCallCount omitted — non-Anthropic backends never populate
|
|
631
|
+
// this field, consumers treat undefined as 0.
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
finally {
|
|
635
|
+
if (ownsSessionDir) {
|
|
636
|
+
this.readTokenManager?.revoke(sessionDir);
|
|
637
|
+
}
|
|
638
|
+
streamCallbacks?.onEnd?.();
|
|
639
|
+
if (ownsSessionDir) {
|
|
640
|
+
cleanupSessionWorkdir(sessionDir);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
buildArgs(params, policyPath) {
|
|
645
|
+
// The admin policy always applies (strict or allow) so context-dir and
|
|
646
|
+
// sensitive-path guardrails remain enforced. `--sandbox` is the container
|
|
647
|
+
// sandbox toggle (Docker/Podman) — only strict mode runs inside it.
|
|
648
|
+
// `--skip-trust` bypasses the workspace-trust prompt: session workdirs
|
|
649
|
+
// under PA_DATA_DIR are created fresh by the daemon and can't realistically
|
|
650
|
+
// be trusted interactively; the admin policy is the actual safety surface.
|
|
651
|
+
const allowMode = this.config.geminiExecutionPermissionMode === "allow";
|
|
652
|
+
return [
|
|
653
|
+
...(params.resumeSessionId ? ["--resume", params.resumeSessionId] : []),
|
|
654
|
+
"--prompt",
|
|
655
|
+
params.prompt,
|
|
656
|
+
"--model",
|
|
657
|
+
params.modelId,
|
|
658
|
+
"--approval-mode",
|
|
659
|
+
"yolo",
|
|
660
|
+
"--skip-trust",
|
|
661
|
+
...(allowMode ? [] : ["--sandbox"]),
|
|
662
|
+
"--admin-policy",
|
|
663
|
+
policyPath,
|
|
664
|
+
"--output-format",
|
|
665
|
+
"stream-json",
|
|
666
|
+
];
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Generate a TOML admin policy that restricts Gemini CLI tools to match
|
|
670
|
+
* the same security posture as Claude Code (allowedTools + security hooks).
|
|
671
|
+
*
|
|
672
|
+
* Admin policies are tier 5 (highest priority) and override --approval-mode yolo.
|
|
673
|
+
*
|
|
674
|
+
* All regex patterns use TOML literal strings ('...') to avoid escape-sequence
|
|
675
|
+
* conflicts — TOML basic strings ("...") treat `\b` as backspace and reject
|
|
676
|
+
* unknown escapes like `\.` / `\s`, which are valid regex metacharacters.
|
|
677
|
+
*
|
|
678
|
+
* ── Audit note: DM reactive-path allowlist bug (BUG-DM-BACKEND-PERMISSIONS) ──
|
|
679
|
+
*
|
|
680
|
+
* This Gemini policy does NOT need the Skill / Bash(jq *) additions that
|
|
681
|
+
* Claude Code needed for the same bug. Reasons verified 2026-04-11:
|
|
682
|
+
*
|
|
683
|
+
* 1. Gemini CLI has no first-class "Skill" tool. User skills are loaded
|
|
684
|
+
* as files in the session workdir (`${dataDir}/skills/`), and the
|
|
685
|
+
* agent reads them via `read_file` — which is already allowed at
|
|
686
|
+
* priority 500 below. There is no tool invocation to deny.
|
|
687
|
+
*
|
|
688
|
+
* 2. The priority-950 rule below denies any shell command that contains
|
|
689
|
+
* a pipe (`|`), semicolon (`;`), `&&`, or subshell operator. That
|
|
690
|
+
* means `curl ... | jq ...` is ALREADY denied on this backend by
|
|
691
|
+
* design — adding `jq` to an allowlist would have no effect while the
|
|
692
|
+
* pipe-chaining deny rule is in force. Gemini's workflow for JSON
|
|
693
|
+
* post-processing is: `curl -o /tmp/out.json ...`, then `read_file`,
|
|
694
|
+
* then parse in-model. That workflow does not need `jq`.
|
|
695
|
+
*
|
|
696
|
+
* If `message.dm` is ever re-routed to Gemini by default, re-audit: the
|
|
697
|
+
* task-flow prompts may reference `Skill(...)` syntax the agent cannot
|
|
698
|
+
* invoke on this backend. That would be a prompt/task-flow issue, not an
|
|
699
|
+
* allowlist issue.
|
|
700
|
+
*/
|
|
701
|
+
generateAdminPolicy(options) {
|
|
702
|
+
const webSearchEnabled = options?.webSearchEnabled ?? false;
|
|
703
|
+
const port = this.config.apiPort;
|
|
704
|
+
const contextDir = resolvePath(getContextDir(this.config));
|
|
705
|
+
// Escape regex special chars in the path for use inside TOML literal strings.
|
|
706
|
+
// Literal strings don't process escapes, so single backslash is fine.
|
|
707
|
+
const escapedContextDir = contextDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
708
|
+
// Build sensitive-path argsPattern (regex alternation).
|
|
709
|
+
const sensitivePathPatterns = [
|
|
710
|
+
"\\.ssh/",
|
|
711
|
+
"\\.gnupg/",
|
|
712
|
+
"\\.aws/",
|
|
713
|
+
"Library/Keychains/",
|
|
714
|
+
"\\.personal-agent/backups/",
|
|
715
|
+
"\\.personal-agent/whatsapp/auth/",
|
|
716
|
+
// `\"` (closing JSON string quote) is a required terminator because
|
|
717
|
+
// Gemini matches argsPattern against the JSON-stringified args object
|
|
718
|
+
// like `{"file_path":".env"}` — without `\"` the pattern silently
|
|
719
|
+
// misses root `.env` reads (after `.env` comes `"` in the JSON, which
|
|
720
|
+
// matches none of $ / . / /).
|
|
721
|
+
'\\.env($|\\.|/|\\")',
|
|
722
|
+
];
|
|
723
|
+
const sensitivePathRegex = sensitivePathPatterns.join("|");
|
|
724
|
+
const sessionHelperPathRegex = '(^|/)\\.pa/';
|
|
725
|
+
// Translate config.disallowedTools into additional deny rules.
|
|
726
|
+
// Claude Code format: "Bash(rm -rf *)", "Read(~/.ssh/**)" etc.
|
|
727
|
+
const extraDenyRules = this.buildDisallowedToolRules();
|
|
728
|
+
// DELEGATED-MODE-V2-DESIGN.md §4.3.3 — same-backend integration deny
|
|
729
|
+
// rules. For each integration whose `delegatedBackend === "gemini"`,
|
|
730
|
+
// emit one literal-toolName deny rule per concrete (glob-expanded)
|
|
731
|
+
// tool name. Priority sits between user-disallowedTools (935) and the
|
|
732
|
+
// absolute-block layer (1000) — same enforcement weight as user-driven
|
|
733
|
+
// disallowedTools by intent, but distinct so logs/audits can attribute.
|
|
734
|
+
const sessionDenyRules = this.buildSessionDeniedToolRules();
|
|
735
|
+
// EXECUTION-MODE-DESIGN.md §6 absolute-block layer — applied in both
|
|
736
|
+
// strict and allow mode at priority 999 (the in-tier ceiling enforced
|
|
737
|
+
// by Gemini CLI's PolicyFileSchema; admin tier still outranks user /
|
|
738
|
+
// workspace / default tiers regardless of in-tier priority).
|
|
739
|
+
const absoluteBlockRules = this.buildAbsoluteBlockRules();
|
|
740
|
+
return `# ${APP_NAME} admin policy for Gemini CLI (auto-generated)
|
|
741
|
+
# Admin tier — overrides approval-mode yolo
|
|
742
|
+
|
|
743
|
+
# ══════════════════════════════════════════════════
|
|
744
|
+
# Catch-all: deny everything not explicitly allowed.
|
|
745
|
+
# This creates whitelist semantics matching Claude Code's allowedTools.
|
|
746
|
+
# Every allowed tool must have a higher-priority allow rule below.
|
|
747
|
+
# ══════════════════════════════════════════════════
|
|
748
|
+
[[rule]]
|
|
749
|
+
toolName = "*"
|
|
750
|
+
decision = "deny"
|
|
751
|
+
denyMessage = "This tool is not permitted in daemon mode."
|
|
752
|
+
priority = 1
|
|
753
|
+
|
|
754
|
+
${this.buildWebAccessRules(webSearchEnabled)}
|
|
755
|
+
|
|
756
|
+
# ── Shell: restrict to curl localhost:${port} and git ──
|
|
757
|
+
|
|
758
|
+
# Deny any shell command containing a non-localhost HTTP URL.
|
|
759
|
+
# This catches curl piping/chaining bypasses like:
|
|
760
|
+
# curl http://localhost:8321/... ; curl http://evil.com
|
|
761
|
+
# Negative lookahead ensures only localhost:{port} URLs are permitted.
|
|
762
|
+
[[rule]]
|
|
763
|
+
toolName = "run_shell_command"
|
|
764
|
+
commandRegex = 'https?://(?!(localhost|127\\.0\\.0\\.1):${port}[/\\s?#])'
|
|
765
|
+
decision = "deny"
|
|
766
|
+
denyMessage = "HTTP requests to non-localhost targets are forbidden. Use the daemon API."
|
|
767
|
+
priority = 960
|
|
768
|
+
|
|
769
|
+
# Deny curl with connection-override flags (bypass prevention, matches Claude Code hook)
|
|
770
|
+
[[rule]]
|
|
771
|
+
toolName = "run_shell_command"
|
|
772
|
+
commandRegex = '\\bcurl\\b.*(--connect-to|--resolve|--config(\\b|=)|\\s-K|--proxy(\\b|=)|\\s-x|--socks)'
|
|
773
|
+
decision = "deny"
|
|
774
|
+
denyMessage = "curl connection override flags are not allowed (--connect-to, --resolve, --config, --proxy)."
|
|
775
|
+
priority = 955
|
|
776
|
+
|
|
777
|
+
# Deny curl with command chaining operators (pipe, semicolon, &&, ||, subshell, backtick)
|
|
778
|
+
[[rule]]
|
|
779
|
+
toolName = "run_shell_command"
|
|
780
|
+
commandRegex = '\\bcurl\\b.*(;|\\|\\||&&|\\||\\$\\(|\x60)'
|
|
781
|
+
decision = "deny"
|
|
782
|
+
denyMessage = "curl with command chaining is not allowed. Use separate tool calls."
|
|
783
|
+
priority = 950
|
|
784
|
+
|
|
785
|
+
# Allow curl targeting localhost:${port}
|
|
786
|
+
[[rule]]
|
|
787
|
+
toolName = "run_shell_command"
|
|
788
|
+
commandRegex = '\\bcurl\\b.*https?://(localhost|127\\.0\\.0\\.1):${port}(/|\\s|$|\\?)'
|
|
789
|
+
decision = "allow"
|
|
790
|
+
priority = 900
|
|
791
|
+
|
|
792
|
+
# Deny any other curl command
|
|
793
|
+
[[rule]]
|
|
794
|
+
toolName = "run_shell_command"
|
|
795
|
+
commandRegex = '\\bcurl\\b'
|
|
796
|
+
decision = "deny"
|
|
797
|
+
denyMessage = "curl is restricted to localhost:${port}. Use the daemon API for all HTTP requests."
|
|
798
|
+
priority = 850
|
|
799
|
+
|
|
800
|
+
# Deny dangerous git operations (matches Claude Code's disallowedTools)
|
|
801
|
+
[[rule]]
|
|
802
|
+
toolName = "run_shell_command"
|
|
803
|
+
commandRegex = '\\bgit\\s+push\\s+(-f|--force)'
|
|
804
|
+
decision = "deny"
|
|
805
|
+
denyMessage = "git push --force is not allowed."
|
|
806
|
+
priority = 940
|
|
807
|
+
|
|
808
|
+
[[rule]]
|
|
809
|
+
toolName = "run_shell_command"
|
|
810
|
+
commandRegex = '\\bgit\\s+reset\\s+--hard'
|
|
811
|
+
decision = "deny"
|
|
812
|
+
denyMessage = "git reset --hard is not allowed."
|
|
813
|
+
priority = 940
|
|
814
|
+
|
|
815
|
+
[[rule]]
|
|
816
|
+
toolName = "run_shell_command"
|
|
817
|
+
commandRegex = '\\bgit\\s+clean\\b'
|
|
818
|
+
decision = "deny"
|
|
819
|
+
denyMessage = "git clean is not allowed."
|
|
820
|
+
priority = 940
|
|
821
|
+
|
|
822
|
+
# Allow other git commands
|
|
823
|
+
[[rule]]
|
|
824
|
+
toolName = "run_shell_command"
|
|
825
|
+
commandPrefix = "git "
|
|
826
|
+
decision = "allow"
|
|
827
|
+
priority = 800
|
|
828
|
+
|
|
829
|
+
[[rule]]
|
|
830
|
+
toolName = "run_shell_command"
|
|
831
|
+
commandPrefix = "git"
|
|
832
|
+
decision = "allow"
|
|
833
|
+
priority = 800
|
|
834
|
+
|
|
835
|
+
# Deny keychain access (secrets use -A flag; block agent from reading them)
|
|
836
|
+
[[rule]]
|
|
837
|
+
toolName = "run_shell_command"
|
|
838
|
+
commandPrefix = "security "
|
|
839
|
+
decision = "deny"
|
|
840
|
+
denyMessage = "Keychain access is not allowed in daemon mode."
|
|
841
|
+
priority = 950
|
|
842
|
+
|
|
843
|
+
# Deny dangerous shell commands
|
|
844
|
+
[[rule]]
|
|
845
|
+
toolName = "run_shell_command"
|
|
846
|
+
commandPrefix = "rm -rf"
|
|
847
|
+
decision = "deny"
|
|
848
|
+
priority = 940
|
|
849
|
+
|
|
850
|
+
[[rule]]
|
|
851
|
+
toolName = "run_shell_command"
|
|
852
|
+
commandPrefix = "rm -r"
|
|
853
|
+
decision = "deny"
|
|
854
|
+
priority = 940
|
|
855
|
+
|
|
856
|
+
[[rule]]
|
|
857
|
+
toolName = "run_shell_command"
|
|
858
|
+
commandPrefix = "sudo "
|
|
859
|
+
decision = "deny"
|
|
860
|
+
priority = 940
|
|
861
|
+
|
|
862
|
+
[[rule]]
|
|
863
|
+
toolName = "run_shell_command"
|
|
864
|
+
commandPrefix = "su "
|
|
865
|
+
decision = "deny"
|
|
866
|
+
priority = 940
|
|
867
|
+
|
|
868
|
+
[[rule]]
|
|
869
|
+
toolName = "run_shell_command"
|
|
870
|
+
commandPrefix = "chmod "
|
|
871
|
+
decision = "deny"
|
|
872
|
+
priority = 940
|
|
873
|
+
|
|
874
|
+
[[rule]]
|
|
875
|
+
toolName = "run_shell_command"
|
|
876
|
+
commandPrefix = "chown "
|
|
877
|
+
decision = "deny"
|
|
878
|
+
priority = 940
|
|
879
|
+
|
|
880
|
+
# Deny all other shell commands (only curl-to-localhost and git are allowed)
|
|
881
|
+
[[rule]]
|
|
882
|
+
toolName = "run_shell_command"
|
|
883
|
+
decision = "deny"
|
|
884
|
+
denyMessage = "Only 'curl localhost:${port}' and 'git' commands are allowed in daemon mode."
|
|
885
|
+
priority = 100
|
|
886
|
+
|
|
887
|
+
# ── File operations ──
|
|
888
|
+
|
|
889
|
+
# Deny writes to context directory (must use daemon API)
|
|
890
|
+
[[rule]]
|
|
891
|
+
toolName = ["write_file", "replace"]
|
|
892
|
+
argsPattern = '${escapedContextDir}'
|
|
893
|
+
decision = "deny"
|
|
894
|
+
denyMessage = "Direct writes to context directory are forbidden. Use PUT/PATCH http://localhost:${port}/api/context/*"
|
|
895
|
+
priority = 960
|
|
896
|
+
|
|
897
|
+
# Deny writes to the session helper directory. The daemon manages .pa/bin/*
|
|
898
|
+
# wrappers; allowing edits would let the agent rewrite the curl shim that
|
|
899
|
+
# receives daemon-auth env at execution time.
|
|
900
|
+
[[rule]]
|
|
901
|
+
toolName = ["write_file", "replace"]
|
|
902
|
+
argsPattern = '${sessionHelperPathRegex}'
|
|
903
|
+
decision = "deny"
|
|
904
|
+
denyMessage = "Direct writes to .pa are forbidden. Session helper binaries are daemon-managed."
|
|
905
|
+
priority = 958
|
|
906
|
+
|
|
907
|
+
# Deny reads/writes to sensitive paths
|
|
908
|
+
[[rule]]
|
|
909
|
+
toolName = ["read_file", "read_many_files", "write_file", "replace"]
|
|
910
|
+
argsPattern = '${sensitivePathRegex}'
|
|
911
|
+
decision = "deny"
|
|
912
|
+
denyMessage = "Access to sensitive paths (.ssh, .aws, .gnupg, Keychains, .env, backups) is forbidden."
|
|
913
|
+
priority = 955
|
|
914
|
+
|
|
915
|
+
# Allow standard file operations
|
|
916
|
+
[[rule]]
|
|
917
|
+
toolName = ["read_file", "read_many_files", "glob", "grep_search", "list_directory"]
|
|
918
|
+
decision = "allow"
|
|
919
|
+
priority = 500
|
|
920
|
+
|
|
921
|
+
[[rule]]
|
|
922
|
+
toolName = ["write_file", "replace"]
|
|
923
|
+
decision = "allow"
|
|
924
|
+
priority = 500
|
|
925
|
+
|
|
926
|
+
# ── Other tools ──
|
|
927
|
+
[[rule]]
|
|
928
|
+
toolName = ["ask_user", "write_todos", "save_memory"]
|
|
929
|
+
decision = "allow"
|
|
930
|
+
priority = 500
|
|
931
|
+
|
|
932
|
+
# ── Subagent delegation (invoke_agent) ──
|
|
933
|
+
# Block Gemini CLI's built-in subagent dispatcher. The daemon's stream
|
|
934
|
+
# parsers (runTurn + runDelegatedTool) only observe the parent agent's
|
|
935
|
+
# tool_use events — subagent-internal MCP calls don't surface, so the
|
|
936
|
+
# anti-prompt-injection guard would mis-classify the turn as wrong_tool.
|
|
937
|
+
# Subagents also paraphrase tool output instead of returning the raw
|
|
938
|
+
# connector envelope, and per-integration deniedTools / cost accounting
|
|
939
|
+
# don't cross the subagent boundary. Bundled policies/agents.toml allows
|
|
940
|
+
# invoke_agent at default-tier priority 50; admin tier wins cross-tier
|
|
941
|
+
# so this rule's priority only orders it inside the admin policy, not
|
|
942
|
+
# against the bundled allow.
|
|
943
|
+
#
|
|
944
|
+
# Hard-coded by tool name — if a future Gemini CLI ships a renamed or
|
|
945
|
+
# additional delegation builtin, this rule will not catch it. Watch for
|
|
946
|
+
# new tool-name constants alongside AGENT_TOOL_NAME in the bundled CLI.
|
|
947
|
+
[[rule]]
|
|
948
|
+
toolName = "invoke_agent"
|
|
949
|
+
decision = "deny"
|
|
950
|
+
denyMessage = "Subagent delegation via invoke_agent is not allowed in daemon-managed sessions. Call the requested tool directly."
|
|
951
|
+
priority = 950
|
|
952
|
+
${absoluteBlockRules}${extraDenyRules}${sessionDenyRules}`;
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Minimal admin policy for allow mode. No catch-all deny — every tool not
|
|
956
|
+
* matched by a rule below falls through to the `--approval-mode yolo`
|
|
957
|
+
* grants, which is what "all commands/MCPs/skills enabled" requires.
|
|
958
|
+
*
|
|
959
|
+
* Only two invariants are preserved:
|
|
960
|
+
* 1. Context directory writes must go through the daemon API. This is
|
|
961
|
+
* memory-layer integrity (today-write-lock, md_file_snapshots,
|
|
962
|
+
* CONTEXT_WRITE_PERMISSIONS, onPromptContextChanged) — orthogonal to
|
|
963
|
+
* tool permissions. Denied for both `write_file`/`replace` directly
|
|
964
|
+
* and for `run_shell_command` whose argv contains the context path.
|
|
965
|
+
* 2. Reads of keychain / SSH / GPG / AWS / .env / daemon secrets /
|
|
966
|
+
* WhatsApp auth / backups stay denied. These aren't tool policy
|
|
967
|
+
* either — they're the exfiltration surface, and enabling them
|
|
968
|
+
* serves no agent workflow on a single-owner device.
|
|
969
|
+
*
|
|
970
|
+
* Everything else (arbitrary `rm`, `curl` to any host, `chmod`, `sudo`,
|
|
971
|
+
* web search / fetch, etc.) is permitted because allow mode is meant to
|
|
972
|
+
* be the "strong permission mode" the user asked for.
|
|
973
|
+
*/
|
|
974
|
+
generateAllowModeMinimalPolicy() {
|
|
975
|
+
const port = this.config.apiPort;
|
|
976
|
+
const contextDir = resolvePath(getContextDir(this.config));
|
|
977
|
+
// Escape regex metacharacters — TOML literal strings don't process escapes,
|
|
978
|
+
// so a single backslash reaches the regex engine unchanged.
|
|
979
|
+
const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
980
|
+
// Match any shell-level path form that points at the context dir:
|
|
981
|
+
// - absolute: /Users/.../context
|
|
982
|
+
// - home-tilde: ~/... (shell expands; raw command text contains "~")
|
|
983
|
+
// - $HOME / ${HOME} env-var interpolation
|
|
984
|
+
// Without these alternations, `tee ~/.personal-agent/context/today.md`
|
|
985
|
+
// slips through the context-write deny rule.
|
|
986
|
+
const home = homedir();
|
|
987
|
+
const contextPathForms = shellPathForms(contextDir, home);
|
|
988
|
+
const contextPathRegex = contextPathForms.map(escapeRegex).join("|");
|
|
989
|
+
const contextArgsRegex = jsonStringPathForms(contextPathForms)
|
|
990
|
+
.map(escapeRegex)
|
|
991
|
+
.join("|");
|
|
992
|
+
const sensitivePathPatterns = [
|
|
993
|
+
"\\.ssh[\\\\/]",
|
|
994
|
+
"\\.gnupg[\\\\/]",
|
|
995
|
+
"\\.aws[\\\\/]",
|
|
996
|
+
"Library[\\\\/]Keychains[\\\\/]",
|
|
997
|
+
"\\.personal-agent[\\\\/]backups[\\\\/]",
|
|
998
|
+
"\\.personal-agent[\\\\/]whatsapp[\\\\/]auth[\\\\/]",
|
|
999
|
+
"\\.personal-agent[\\\\/]secrets[\\\\/]",
|
|
1000
|
+
// `\"` (closing JSON string quote) is a required terminator because
|
|
1001
|
+
// Gemini matches argsPattern against the JSON-stringified args object
|
|
1002
|
+
// like `{"file_path":".env"}` — without `\"` the pattern silently
|
|
1003
|
+
// misses root `.env` reads (after `.env` comes `"` in the JSON, which
|
|
1004
|
+
// matches none of $ / . / /).
|
|
1005
|
+
'\\.env($|\\.|[\\\\/]|\\")',
|
|
1006
|
+
];
|
|
1007
|
+
const sensitivePathRegex = sensitivePathPatterns.join("|");
|
|
1008
|
+
return `# ${APP_NAME} admin policy for Gemini CLI — Allow mode (minimal)
|
|
1009
|
+
# Admin tier — overrides approval-mode yolo only for the rules below.
|
|
1010
|
+
# No catch-all deny: every tool not matched here inherits yolo's auto-approve.
|
|
1011
|
+
|
|
1012
|
+
# ── Context directory writes must go through the daemon API ──
|
|
1013
|
+
# Memory-layer integrity invariants (today-write-lock, md_file_snapshots,
|
|
1014
|
+
# CONTEXT_WRITE_PERMISSIONS, onPromptContextChanged) cannot be enforced on
|
|
1015
|
+
# shell-level writes; so we deny any shell command or direct tool that
|
|
1016
|
+
# targets the context dir.
|
|
1017
|
+
|
|
1018
|
+
[[rule]]
|
|
1019
|
+
toolName = ["write_file", "replace"]
|
|
1020
|
+
argsPattern = '${contextArgsRegex}'
|
|
1021
|
+
decision = "deny"
|
|
1022
|
+
denyMessage = "Direct writes to the context directory are forbidden even in Allow mode. Use PUT/PATCH http://localhost:${port}/api/context/* so today-write-lock, md_file_snapshots, and CONTEXT_WRITE_PERMISSIONS stay enforced."
|
|
1023
|
+
priority = 999
|
|
1024
|
+
|
|
1025
|
+
[[rule]]
|
|
1026
|
+
toolName = "run_shell_command"
|
|
1027
|
+
commandRegex = '${contextPathRegex}'
|
|
1028
|
+
decision = "deny"
|
|
1029
|
+
denyMessage = "Shell commands that reference the context directory are forbidden — writing via redirects / tee / sed -i / script engines bypasses daemon-API guarantees. Use http://localhost:${port}/api/context/*. Absolute, ~, and HOME-env-var forms are all matched."
|
|
1030
|
+
priority = 999
|
|
1031
|
+
|
|
1032
|
+
# ── Sensitive-path reads ──
|
|
1033
|
+
# Secrets, keys, and daemon backups stay read-blocked. Writes to these
|
|
1034
|
+
# paths are also blocked so the agent cannot plant credentials or tamper
|
|
1035
|
+
# with keychain-adjacent directories.
|
|
1036
|
+
|
|
1037
|
+
[[rule]]
|
|
1038
|
+
toolName = ["read_file", "read_many_files", "write_file", "replace"]
|
|
1039
|
+
argsPattern = '${sensitivePathRegex}'
|
|
1040
|
+
decision = "deny"
|
|
1041
|
+
denyMessage = "Access to sensitive paths (.ssh, .aws, .gnupg, Keychains, .env, daemon secrets, backups, WhatsApp auth) is forbidden even in Allow mode."
|
|
1042
|
+
priority = 998
|
|
1043
|
+
|
|
1044
|
+
# ── Subagent delegation (invoke_agent) ──
|
|
1045
|
+
# Same rationale as the strict-mode policy: daemon stream parsers don't
|
|
1046
|
+
# see subagent-internal tool calls, subagents paraphrase results, and
|
|
1047
|
+
# per-integration deniedTools / cost accounting don't cross the subagent
|
|
1048
|
+
# boundary. Allow mode has no catch-all "*" deny, so this explicit rule
|
|
1049
|
+
# is the only thing keeping bundled policies/agents.toml (default tier,
|
|
1050
|
+
# priority 50) from re-allowing invoke_agent.
|
|
1051
|
+
[[rule]]
|
|
1052
|
+
toolName = "invoke_agent"
|
|
1053
|
+
decision = "deny"
|
|
1054
|
+
denyMessage = "Subagent delegation via invoke_agent is not allowed in daemon-managed sessions. Call the requested tool directly."
|
|
1055
|
+
priority = 950
|
|
1056
|
+
${this.buildAbsoluteBlockRules()}${this.buildSessionDeniedToolRules()}`;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* EXECUTION-MODE-DESIGN.md §6.2 — absolute-block rules applied in BOTH
|
|
1060
|
+
* strict and allow mode. Priority is pinned at 999 — the in-tier
|
|
1061
|
+
* ceiling enforced by Gemini CLI's `PolicyFileSchema` (`priority` is
|
|
1062
|
+
* `.int().min(0).max(999)`; values >= 1000 fail with "Schema validation
|
|
1063
|
+
* failed" and the entire policy file is dropped, which would also drop
|
|
1064
|
+
* every other guardrail in this admin policy).
|
|
1065
|
+
*
|
|
1066
|
+
* Cross-tier precedence is unaffected: the daemon registers this file
|
|
1067
|
+
* via `--admin-policy <path>`, which puts it at `ADMIN_POLICY_TIER (=5)`,
|
|
1068
|
+
* strictly above user/workspace/default. Within the admin tier, 999 is
|
|
1069
|
+
* the maximum so absolute-block still wins against every other rule
|
|
1070
|
+
* here. The two existing 998/999 deny rules in allow mode are also
|
|
1071
|
+
* `decision = "deny"`, so a tie at 999 collapses to the same outcome
|
|
1072
|
+
* (deny) — no semantic conflict.
|
|
1073
|
+
*
|
|
1074
|
+
* Highest-priority ALLOW rule across both modes is `curl localhost`
|
|
1075
|
+
* at 900, well below 999, so absolute-block continues to override any
|
|
1076
|
+
* allow currently emitted.
|
|
1077
|
+
*/
|
|
1078
|
+
buildAbsoluteBlockRules() {
|
|
1079
|
+
return this.convertToolListToTomlRules(ALWAYS_DISALLOWED_TOOLS, {
|
|
1080
|
+
priority: 999,
|
|
1081
|
+
denyPrefix: "Blocked by absolute-block layer: ",
|
|
1082
|
+
sectionHeader: "Absolute-block layer (EXECUTION-MODE-DESIGN.md §6)",
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Convert config.disallowedTools entries into TOML deny rules.
|
|
1087
|
+
*
|
|
1088
|
+
* Claude Code format entries like "Bash(rm -rf *)" or "Read(~/.ssh/**)"
|
|
1089
|
+
* are translated into Gemini-native policy rules.
|
|
1090
|
+
*/
|
|
1091
|
+
buildDisallowedToolRules() {
|
|
1092
|
+
return this.convertToolListToTomlRules(this.config.disallowedTools ?? [], {
|
|
1093
|
+
priority: 935,
|
|
1094
|
+
denyPrefix: "Blocked by disallowedTools: ",
|
|
1095
|
+
sectionHeader: "User-configured disallowedTools",
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* DELEGATED-MODE-V2-DESIGN.md §4.3.3 — emit deny rules for every
|
|
1100
|
+
* concrete (glob-expanded) MCP tool name carried by an integration's
|
|
1101
|
+
* `deniedTools` whose `delegatedBackend === "gemini"`. Tools are
|
|
1102
|
+
* matched by literal name (e.g.
|
|
1103
|
+
* `mcp__codex_apps__google_calendar._delete_event`) at priority 936
|
|
1104
|
+
* — one tick above the user-disallowedTools block (935) so a deny
|
|
1105
|
+
* here cannot be overridden by anything below.
|
|
1106
|
+
*
|
|
1107
|
+
* Empty when no integration is in same-backend mode for Gemini.
|
|
1108
|
+
*/
|
|
1109
|
+
buildSessionDeniedToolRules() {
|
|
1110
|
+
if (!this.mcpContext)
|
|
1111
|
+
return "";
|
|
1112
|
+
let denied = [];
|
|
1113
|
+
try {
|
|
1114
|
+
const integrations = readIntegrations(this.mcpContext.db);
|
|
1115
|
+
const map = collectSessionDeniedTools(integrations, "gemini");
|
|
1116
|
+
const flat = [];
|
|
1117
|
+
for (const names of map.values())
|
|
1118
|
+
flat.push(...names);
|
|
1119
|
+
denied = flat;
|
|
1120
|
+
}
|
|
1121
|
+
catch (err) {
|
|
1122
|
+
logger.warn({ err }, "Failed to read integrations for same-backend denied-tools — proceeding without per-integration deny");
|
|
1123
|
+
return "";
|
|
1124
|
+
}
|
|
1125
|
+
if (denied.length === 0)
|
|
1126
|
+
return "";
|
|
1127
|
+
const rules = [
|
|
1128
|
+
"",
|
|
1129
|
+
"",
|
|
1130
|
+
"# ══════════════════════════════════════════════════",
|
|
1131
|
+
"# Same-backend deniedTools (DELEGATED-MODE-V2-DESIGN.md §4.3.3)",
|
|
1132
|
+
"# ══════════════════════════════════════════════════",
|
|
1133
|
+
];
|
|
1134
|
+
for (const tool of denied) {
|
|
1135
|
+
rules.push(`
|
|
1136
|
+
[[rule]]
|
|
1137
|
+
toolName = "${this.escapeTomlBasicString(tool)}"
|
|
1138
|
+
decision = "deny"
|
|
1139
|
+
denyMessage = "Tool '${this.escapeTomlBasicString(tool)}' is denied by the user's integration deniedTools setting."
|
|
1140
|
+
priority = 936`);
|
|
1141
|
+
}
|
|
1142
|
+
return rules.join("\n");
|
|
1143
|
+
}
|
|
1144
|
+
convertToolListToTomlRules(disallowed, opts) {
|
|
1145
|
+
const rules = [];
|
|
1146
|
+
for (const entry of disallowed) {
|
|
1147
|
+
const bashMatch = /^Bash\((.+)\)$/.exec(entry);
|
|
1148
|
+
if (bashMatch) {
|
|
1149
|
+
// "Bash(rm -rf *)" → deny run_shell_command with commandPrefix
|
|
1150
|
+
const cmdPattern = bashMatch[1].replace(/\s*\*$/, "").trim();
|
|
1151
|
+
if (cmdPattern) {
|
|
1152
|
+
rules.push(`
|
|
1153
|
+
[[rule]]
|
|
1154
|
+
toolName = "run_shell_command"
|
|
1155
|
+
commandPrefix = "${this.escapeTomlBasicString(cmdPattern)}"
|
|
1156
|
+
decision = "deny"
|
|
1157
|
+
denyMessage = "${this.escapeTomlBasicString(opts.denyPrefix)}${this.escapeTomlBasicString(entry)}"
|
|
1158
|
+
priority = ${opts.priority}`);
|
|
1159
|
+
}
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
const fileMatch = /^(Read|Write|Edit)\((.+)\)$/.exec(entry);
|
|
1163
|
+
if (fileMatch) {
|
|
1164
|
+
const [, toolType, pathGlob] = fileMatch;
|
|
1165
|
+
// Convert glob to a simple regex substring for argsPattern.
|
|
1166
|
+
// "~/.ssh/**" → ".ssh/"
|
|
1167
|
+
const pathFragment = pathGlob
|
|
1168
|
+
.replace(/^~\//, "")
|
|
1169
|
+
.replace(/\*+$/g, "")
|
|
1170
|
+
.replace(/\\/g, "/");
|
|
1171
|
+
if (!pathFragment)
|
|
1172
|
+
continue;
|
|
1173
|
+
const toolNames = [];
|
|
1174
|
+
if (toolType === "Read")
|
|
1175
|
+
toolNames.push("read_file", "read_many_files");
|
|
1176
|
+
if (toolType === "Write" || toolType === "Edit")
|
|
1177
|
+
toolNames.push("write_file", "replace");
|
|
1178
|
+
if (toolNames.length > 0) {
|
|
1179
|
+
const toolNameValue = toolNames.length === 1
|
|
1180
|
+
? `"${toolNames[0]}"`
|
|
1181
|
+
: `[${toolNames.map((t) => `"${t}"`).join(", ")}]`;
|
|
1182
|
+
rules.push(`
|
|
1183
|
+
[[rule]]
|
|
1184
|
+
toolName = ${toolNameValue}
|
|
1185
|
+
argsPattern = '${pathFragment}'
|
|
1186
|
+
decision = "deny"
|
|
1187
|
+
denyMessage = "${this.escapeTomlBasicString(opts.denyPrefix)}${this.escapeTomlBasicString(entry)}"
|
|
1188
|
+
priority = ${opts.priority}`);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
if (rules.length === 0)
|
|
1193
|
+
return "";
|
|
1194
|
+
return `
|
|
1195
|
+
# ── ${opts.sectionHeader} ──
|
|
1196
|
+
${rules.join("\n")}
|
|
1197
|
+
`;
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Build the web-access TOML rules section.
|
|
1201
|
+
*
|
|
1202
|
+
* When disabled (default): deny both google_web_search and web_fetch at
|
|
1203
|
+
* priority 999 (overrides any lower-priority allow).
|
|
1204
|
+
*
|
|
1205
|
+
* When enabled: allow google_web_search only. web_fetch remains denied
|
|
1206
|
+
* because it can fetch arbitrary URLs, creating a data-exfiltration
|
|
1207
|
+
* vector that contradicts the curl-to-localhost shell restriction.
|
|
1208
|
+
*/
|
|
1209
|
+
buildWebAccessRules(webSearchEnabled) {
|
|
1210
|
+
if (webSearchEnabled) {
|
|
1211
|
+
return `# ── Web access: PARTIAL ALLOW ──
|
|
1212
|
+
# google_web_search enabled via dashboard settings.
|
|
1213
|
+
# web_fetch remains denied (arbitrary URL fetch = exfiltration risk).
|
|
1214
|
+
|
|
1215
|
+
[[rule]]
|
|
1216
|
+
toolName = "google_web_search"
|
|
1217
|
+
decision = "allow"
|
|
1218
|
+
priority = 500
|
|
1219
|
+
|
|
1220
|
+
[[rule]]
|
|
1221
|
+
toolName = "web_fetch"
|
|
1222
|
+
decision = "deny"
|
|
1223
|
+
denyMessage = "Web fetch is not allowed. Only google_web_search is permitted when web search is enabled."
|
|
1224
|
+
priority = 999`;
|
|
1225
|
+
}
|
|
1226
|
+
return `# ── Web access: DENY ──
|
|
1227
|
+
# Block internet search and URL fetching (parity with Claude Code's allowedTools whitelist)
|
|
1228
|
+
|
|
1229
|
+
[[rule]]
|
|
1230
|
+
toolName = "google_web_search"
|
|
1231
|
+
decision = "deny"
|
|
1232
|
+
denyMessage = "Web search is not allowed in daemon mode. Use the daemon API for external data."
|
|
1233
|
+
priority = 999
|
|
1234
|
+
|
|
1235
|
+
[[rule]]
|
|
1236
|
+
toolName = "web_fetch"
|
|
1237
|
+
decision = "deny"
|
|
1238
|
+
denyMessage = "Web fetch is not allowed in daemon mode. Use the daemon API for external data."
|
|
1239
|
+
priority = 999`;
|
|
1240
|
+
}
|
|
1241
|
+
/** Escape characters for TOML basic string values (double-quoted). */
|
|
1242
|
+
escapeTomlBasicString(value) {
|
|
1243
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Pre-mark vault-scoped file writes from Gemini's tool_use events
|
|
1247
|
+
* so the ObsidianWatcher attributes them to actor='agent'.
|
|
1248
|
+
*/
|
|
1249
|
+
trackVaultWrite(event) {
|
|
1250
|
+
if (!this.writeTracker)
|
|
1251
|
+
return;
|
|
1252
|
+
const vaultPath = this.config.externalObsidianVaultPath;
|
|
1253
|
+
if (!vaultPath)
|
|
1254
|
+
return;
|
|
1255
|
+
if (event.tool_name !== "write_file" && event.tool_name !== "replace")
|
|
1256
|
+
return;
|
|
1257
|
+
const filePath = event.args?.file_path;
|
|
1258
|
+
if (typeof filePath !== "string" || !filePath)
|
|
1259
|
+
return;
|
|
1260
|
+
const absFile = resolvePath(filePath);
|
|
1261
|
+
const absVault = resolvePath(vaultPath);
|
|
1262
|
+
if (isPathInsideOrEqual(absVault, absFile)) {
|
|
1263
|
+
this.writeTracker.markWriting(absFile);
|
|
1264
|
+
logger.debug({ filePath: absFile }, "Gemini vault write pre-marked for observer attribution");
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
pickSummaryModel() {
|
|
1268
|
+
// Registry-derived lite-tier model is the canonical pick (compaction
|
|
1269
|
+
// summaries are short-shape and benefit from the cheapest model).
|
|
1270
|
+
// Falls through to "any registered model" when the registry has no
|
|
1271
|
+
// available lite entry, then throws when the registry is empty for
|
|
1272
|
+
// this backend — preferable to the prior silent literal fallback
|
|
1273
|
+
// that hid registry misconfiguration.
|
|
1274
|
+
const liteFromRegistry = latestLiteFor(this.backendId);
|
|
1275
|
+
if (liteFromRegistry)
|
|
1276
|
+
return liteFromRegistry;
|
|
1277
|
+
const anyFromRegistry = this.listModels()[0]?.modelId;
|
|
1278
|
+
if (anyFromRegistry)
|
|
1279
|
+
return anyFromRegistry;
|
|
1280
|
+
throw new Error(`pickSummaryModel: no models registered for backend ${this.backendId}`);
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Resolve the Gemini per-agent-day request ceiling. The daemon does
|
|
1284
|
+
* not store per-backend account tiers (Aitne runs on
|
|
1285
|
+
* `GEMINI_API_KEY` / `GOOGLE_API_KEY`, with CLI auth as fallback),
|
|
1286
|
+
* so this returns a fixed conservative default
|
|
1287
|
+
* (`GEMINI_DAILY_REQUEST_CEILING`, matching the free Gemini API tier
|
|
1288
|
+
* with 10% headroom). Operators on a paid Google account whose real
|
|
1289
|
+
* upstream quota is higher can widen the gate by editing
|
|
1290
|
+
* `runtime_state` directly or via a dashboard config surface (out of
|
|
1291
|
+
* scope for this layer).
|
|
1292
|
+
*
|
|
1293
|
+
* Returns `null` only when no db handle is injected (test/dev paths
|
|
1294
|
+
* that bypass enforcement entirely).
|
|
1295
|
+
*/
|
|
1296
|
+
resolveDailyRequestCeiling() {
|
|
1297
|
+
if (!this.db)
|
|
1298
|
+
return null;
|
|
1299
|
+
return GEMINI_DAILY_REQUEST_CEILING;
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Read the current request count for today's agent day. Returns 0 when:
|
|
1303
|
+
* - the stored state is from a previous agent day (natural daily reset),
|
|
1304
|
+
* - the row is missing,
|
|
1305
|
+
* - or the db handle is not injected.
|
|
1306
|
+
*/
|
|
1307
|
+
readRequestsCount(today) {
|
|
1308
|
+
if (!this.db)
|
|
1309
|
+
return 0;
|
|
1310
|
+
const state = readRuntimeState(this.db, GEMINI_REQUESTS_STATE_KEY);
|
|
1311
|
+
if (!state || state.date !== today)
|
|
1312
|
+
return 0;
|
|
1313
|
+
return state.count;
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Increment the per-agent-day counter. Resets to 1 when the stored date
|
|
1317
|
+
* does not match the current agent day (that is the reset path — no
|
|
1318
|
+
* separate scheduler job is needed because the key is agent-day scoped).
|
|
1319
|
+
*/
|
|
1320
|
+
incrementRequestsCount(today) {
|
|
1321
|
+
if (!this.db)
|
|
1322
|
+
return;
|
|
1323
|
+
try {
|
|
1324
|
+
const state = readRuntimeState(this.db, GEMINI_REQUESTS_STATE_KEY);
|
|
1325
|
+
const nextCount = state && state.date === today ? state.count + 1 : 1;
|
|
1326
|
+
writeRuntimeState(this.db, GEMINI_REQUESTS_STATE_KEY, {
|
|
1327
|
+
date: today,
|
|
1328
|
+
count: nextCount,
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
catch (err) {
|
|
1332
|
+
// A counter write failure must not break an in-flight execution.
|
|
1333
|
+
logger.warn({ err }, "Failed to persist Gemini request counter");
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
classifyFailure(message) {
|
|
1337
|
+
if (isMaxBudgetMessage(message)) {
|
|
1338
|
+
return new BackendQuotaError(this.backendId, "max_budget_usd", null, message);
|
|
1339
|
+
}
|
|
1340
|
+
if (/rate limit|quota|429/i.test(message)) {
|
|
1341
|
+
return new BackendQuotaError(this.backendId, "rate_limited", null, message);
|
|
1342
|
+
}
|
|
1343
|
+
if (/authentication page|oauth|api key|login|required/i.test(message)) {
|
|
1344
|
+
return new BackendDecisiveFailure(this.backendId, "auth", new Error(message));
|
|
1345
|
+
}
|
|
1346
|
+
if (/timed out|timeout/i.test(message)) {
|
|
1347
|
+
return new BackendDecisiveFailure(this.backendId, "timeout", new Error(message));
|
|
1348
|
+
}
|
|
1349
|
+
return new BackendDecisiveFailure(this.backendId, "other_non_retryable", new Error(message));
|
|
1350
|
+
}
|
|
1351
|
+
assertWithinMaxBudget(costUsd, maxBudgetUsd, modelId) {
|
|
1352
|
+
if (maxBudgetUsd === undefined || costUsd <= maxBudgetUsd) {
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
throw new BackendQuotaError(this.backendId, "max_budget_usd", null, `Gemini CLI estimated cost $${costUsd.toFixed(4)} exceeded the per-turn budget limit $${maxBudgetUsd.toFixed(2)} for ${modelId}.`);
|
|
1356
|
+
}
|
|
1357
|
+
assertPromptWithinMaxBudget(prompt, maxBudgetUsd, modelId) {
|
|
1358
|
+
if (maxBudgetUsd === undefined) {
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
const estimatedUsage = {
|
|
1362
|
+
inputTokens: estimateTextInputTokens(prompt),
|
|
1363
|
+
outputTokens: 0,
|
|
1364
|
+
cacheCreationInputTokens: 0,
|
|
1365
|
+
cacheReadInputTokens: 0,
|
|
1366
|
+
};
|
|
1367
|
+
const { costUsd } = this.priceFetcher.estimateUsageCost({
|
|
1368
|
+
backendId: this.backendId,
|
|
1369
|
+
modelId,
|
|
1370
|
+
usage: estimatedUsage,
|
|
1371
|
+
fallbackModel: findRegisteredModel(this.backendId, modelId),
|
|
1372
|
+
});
|
|
1373
|
+
if (costUsd <= maxBudgetUsd) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
throw new BackendQuotaError(this.backendId, "max_budget_usd", null, `Gemini CLI estimated prompt cost $${costUsd.toFixed(4)} exceeded the per-turn budget limit $${maxBudgetUsd.toFixed(2)} for ${modelId}.`);
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Delegated proxy invocation — Gemini CLI path.
|
|
1380
|
+
*
|
|
1381
|
+
* **Result extraction**: Gemini CLI ≥ 0.40 emits structured
|
|
1382
|
+
* `tool_result` events with `tool_id` correlation and a string-encoded
|
|
1383
|
+
* `output` field. We capture the first `tool_result` whose `tool_id`
|
|
1384
|
+
* pairs with a `tool_use` event for the requested tool name; that
|
|
1385
|
+
* output (parsed JSON when possible) becomes the `toolResult`. This
|
|
1386
|
+
* matches the Claude/Codex paths and is robust to Gemini Flash Lite
|
|
1387
|
+
* disregarding the proxy prompt's "return raw result, do not narrate"
|
|
1388
|
+
* instruction by emitting prose summaries — the prose lands in the
|
|
1389
|
+
* assistant message stream, which we now ignore.
|
|
1390
|
+
*
|
|
1391
|
+
* **Fallback**: if no matching `tool_result` event arrives (older CLI,
|
|
1392
|
+
* unexpected stream shape) we fall back to the previous text-based
|
|
1393
|
+
* extraction from the final assistant message — strictly worse, but
|
|
1394
|
+
* better than failing the call outright.
|
|
1395
|
+
*
|
|
1396
|
+
* Reachable for every integration whose registry descriptor lists a
|
|
1397
|
+
* Gemini connector. As of 2026-04-26 that includes `gmail` (via the
|
|
1398
|
+
* `google-workspace` extension), `google_calendar` (same extension),
|
|
1399
|
+
* and `notion` (user-installed Notion MCP server registered as
|
|
1400
|
+
* `notion`). Gmail and Calendar are also in `PROXY_DRIVEN_INTEGRATIONS`,
|
|
1401
|
+
* so a Claude/Codex DM session with `delegatedBackend = "gemini"` for
|
|
1402
|
+
* either lands here through `/api/integrations/:key/invoke`.
|
|
1403
|
+
*/
|
|
1404
|
+
async runDelegatedTool(params) {
|
|
1405
|
+
const startMs = Date.now();
|
|
1406
|
+
const { toolName, toolArgs, modelId, sessionDir, abortSignal } = params;
|
|
1407
|
+
if (!this.cliPath) {
|
|
1408
|
+
return {
|
|
1409
|
+
ok: false,
|
|
1410
|
+
errorClass: "subprocess_crashed",
|
|
1411
|
+
message: "gemini CLI not found on PATH",
|
|
1412
|
+
cost: withDurationMs(emptyCost(), startMs),
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
const auth = await this.checkAuth();
|
|
1416
|
+
if (!auth.ok) {
|
|
1417
|
+
return {
|
|
1418
|
+
ok: false,
|
|
1419
|
+
errorClass: "auth_error",
|
|
1420
|
+
message: auth.reason,
|
|
1421
|
+
cost: withDurationMs(emptyCost(), startMs),
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
// Daily-request quota gate. Mirrors `runTurn` (line 480-495) — Gemini
|
|
1425
|
+
// meters per-day model requests via plan-preset `dailyRequestCeiling`
|
|
1426
|
+
// (900/1350/1800), and proxy invocations consume the same quota as
|
|
1427
|
+
// direct execute()s. Without this gate, proxy calls would silently
|
|
1428
|
+
// exceed the day cap. Counter increment lives at the end of the
|
|
1429
|
+
// success path so failed proxy calls don't bias the count toward
|
|
1430
|
+
// saturation. See `docs/design/09-safety-cost.md` §9.4.8.
|
|
1431
|
+
const today = getAgentDayDateStr(this.config.timezone, this.config.dayBoundaryHour);
|
|
1432
|
+
const ceiling = this.resolveDailyRequestCeiling();
|
|
1433
|
+
if (ceiling !== null) {
|
|
1434
|
+
const currentCount = this.readRequestsCount(today);
|
|
1435
|
+
if (currentCount >= ceiling) {
|
|
1436
|
+
return {
|
|
1437
|
+
ok: false,
|
|
1438
|
+
errorClass: "auth_error",
|
|
1439
|
+
message: `Gemini daily-request ceiling reached (${currentCount}/${ceiling}) — `
|
|
1440
|
+
+ `resets at the next agent-day boundary.`,
|
|
1441
|
+
cost: withDurationMs(emptyCost(), startMs),
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
// Write the same admin policy the normal path uses so context-dir
|
|
1446
|
+
// writes and pipe-chained shell commands are denied even on the
|
|
1447
|
+
// proxy session. Proxy.md tells the model to call exactly one tool;
|
|
1448
|
+
// the policy is belt-and-suspenders.
|
|
1449
|
+
const allowMode = this.config.geminiExecutionPermissionMode === "allow";
|
|
1450
|
+
const policyPath = join(sessionDir, ADMIN_POLICY_FILENAME);
|
|
1451
|
+
writeFileSync(policyPath, allowMode
|
|
1452
|
+
? this.generateAllowModeMinimalPolicy()
|
|
1453
|
+
: this.generateAdminPolicy({ webSearchEnabled: false }), "utf-8");
|
|
1454
|
+
const prompt = buildDelegatedToolPrompt(toolName, toolArgs);
|
|
1455
|
+
const args = this.buildArgs({ prompt, modelId }, policyPath);
|
|
1456
|
+
let calledMatchingTool = false;
|
|
1457
|
+
let wrongToolName = null;
|
|
1458
|
+
let assistantDelta = "";
|
|
1459
|
+
let finalAssistantMessage = "";
|
|
1460
|
+
let lastError = null;
|
|
1461
|
+
let resultStatus = null;
|
|
1462
|
+
let stats = null;
|
|
1463
|
+
// tool_use → tool_id mapping for the requested tool. Gemini sometimes
|
|
1464
|
+
// calls the matching tool more than once (e.g. retries with refined
|
|
1465
|
+
// args, or extra confirmation calls disregarding the prompt's
|
|
1466
|
+
// "do not call other tools"). We capture every matching tool_id and
|
|
1467
|
+
// keep only the first paired tool_result so the proxy returns
|
|
1468
|
+
// deterministic output.
|
|
1469
|
+
const matchingToolIds = new Set();
|
|
1470
|
+
let capturedToolOutput = null;
|
|
1471
|
+
let capturedToolStatus = null;
|
|
1472
|
+
// Local aborter bridged from the caller's signal so we can also
|
|
1473
|
+
// trigger an early abort on wrong-tool detection without polluting
|
|
1474
|
+
// the caller's signal. Without early abort, a wrong-tool failure
|
|
1475
|
+
// burns the full 180s gemini wall-clock waiting for natural
|
|
1476
|
+
// completion (audit log 2026-05-01: 9.5s when subprocess completes
|
|
1477
|
+
// naturally, but up to 180s when the model loops on the missing
|
|
1478
|
+
// tool name). Abort caps it at ~5s.
|
|
1479
|
+
const proxyAborter = new AbortController();
|
|
1480
|
+
const callerAbortListener = () => {
|
|
1481
|
+
proxyAborter.abort(abortSignal?.reason);
|
|
1482
|
+
};
|
|
1483
|
+
if (abortSignal) {
|
|
1484
|
+
if (abortSignal.aborted) {
|
|
1485
|
+
proxyAborter.abort(abortSignal.reason);
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
abortSignal.addEventListener("abort", callerAbortListener, {
|
|
1489
|
+
once: true,
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
// Idle / hang watchdog. `gemini-cli` has been observed (audit log
|
|
1494
|
+
// 2026-05-02 / 2026-05-03: 9 / 83 cadence calls) to lock up entirely
|
|
1495
|
+
// — subprocess alive, zero stream-json output, only the 180s wall-
|
|
1496
|
+
// clock fires. The idle detector trips much earlier (75s default,
|
|
1497
|
+
// see `delegated-proxy-config.ts`) by treating each arrived line as
|
|
1498
|
+
// a heartbeat. On trip, we abort with a `DelegatedProxyTimeoutError`
|
|
1499
|
+
// so the post-loop classifier maps to `errorClass="timeout"`,
|
|
1500
|
+
// identical to wall-clock — the cadence retry path stays uniform.
|
|
1501
|
+
let idleTimedOut = false;
|
|
1502
|
+
const idleTimeoutMs = DELEGATED_PROXY_DEFAULTS.idleTimeoutMsByBackend.gemini
|
|
1503
|
+
?? DELEGATED_PROXY_DEFAULTS.idleTimeoutMs;
|
|
1504
|
+
const idleWatchdog = new IdleWatchdog({
|
|
1505
|
+
idleTimeoutMs,
|
|
1506
|
+
onTimeout: (idleMs) => {
|
|
1507
|
+
idleTimedOut = true;
|
|
1508
|
+
logger.warn({ idleMs, idleTimeoutMs, toolName }, "gemini delegated proxy idle watchdog tripped");
|
|
1509
|
+
proxyAborter.abort(new DelegatedProxyTimeoutError(`gemini stream idle for ${idleMs}ms (limit ${idleTimeoutMs}ms)`));
|
|
1510
|
+
},
|
|
1511
|
+
});
|
|
1512
|
+
try {
|
|
1513
|
+
idleWatchdog.start();
|
|
1514
|
+
const runResult = await runLineCommand({
|
|
1515
|
+
command: this.cliPath,
|
|
1516
|
+
args,
|
|
1517
|
+
cwd: sessionDir,
|
|
1518
|
+
env: buildDaemonApiCliEnv(sessionDir, this.config.apiPort, { sessionBackend: "gemini" }),
|
|
1519
|
+
// Safety-net timeout, derived from the invoker's per-backend
|
|
1520
|
+
// abort timeout (`callTimeoutMsByBackend.gemini`). We add a 60s
|
|
1521
|
+
// grace window so the abort signal *always* fires first and we
|
|
1522
|
+
// get a clean `timeout` errorClass instead of the watchdog's
|
|
1523
|
+
// ambiguous "subprocess exceeded local safety-net timeout".
|
|
1524
|
+
// Deriving rather than hard-coding prevents drift if the abort
|
|
1525
|
+
// cap is later raised or lowered.
|
|
1526
|
+
timeoutMs: (DELEGATED_PROXY_DEFAULTS.callTimeoutMsByBackend.gemini
|
|
1527
|
+
?? DELEGATED_PROXY_DEFAULTS.callTimeoutMs)
|
|
1528
|
+
+ 60_000,
|
|
1529
|
+
abortSignal: proxyAborter.signal,
|
|
1530
|
+
onStdoutLine: (line) => {
|
|
1531
|
+
idleWatchdog.beat();
|
|
1532
|
+
const event = parseJsonLine(line);
|
|
1533
|
+
if (!event?.type)
|
|
1534
|
+
return;
|
|
1535
|
+
if (event.type === "tool_use"
|
|
1536
|
+
&& typeof event.tool_name === "string") {
|
|
1537
|
+
if (event.tool_name === toolName) {
|
|
1538
|
+
calledMatchingTool = true;
|
|
1539
|
+
if (typeof event.tool_id === "string") {
|
|
1540
|
+
matchingToolIds.add(event.tool_id);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
else if (wrongToolName === null) {
|
|
1544
|
+
wrongToolName = event.tool_name;
|
|
1545
|
+
// Early abort: kill the subprocess as soon as we see the
|
|
1546
|
+
// model call a tool that doesn't match `toolName`. The
|
|
1547
|
+
// post-await classifier checks `wrongToolName` BEFORE
|
|
1548
|
+
// `abortSignal?.aborted`, so the resulting failure is
|
|
1549
|
+
// attributed to wrong_tool (not cancelled).
|
|
1550
|
+
proxyAborter.abort(new Error("wrong_tool"));
|
|
1551
|
+
}
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
if (event.type === "tool_result"
|
|
1555
|
+
&& typeof event.tool_id === "string"
|
|
1556
|
+
&& matchingToolIds.has(event.tool_id)
|
|
1557
|
+
&& capturedToolOutput === null) {
|
|
1558
|
+
capturedToolOutput =
|
|
1559
|
+
typeof event.output === "string" ? event.output : "";
|
|
1560
|
+
capturedToolStatus = event.status ?? null;
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
if (event.type === "message" && event.role === "assistant") {
|
|
1564
|
+
const content = typeof event.content === "string" ? event.content : "";
|
|
1565
|
+
if (!content)
|
|
1566
|
+
return;
|
|
1567
|
+
if (event.delta) {
|
|
1568
|
+
assistantDelta += content;
|
|
1569
|
+
}
|
|
1570
|
+
else {
|
|
1571
|
+
finalAssistantMessage = content;
|
|
1572
|
+
}
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
if (event.type === "result") {
|
|
1576
|
+
const payload = event.result ?? event;
|
|
1577
|
+
resultStatus = payload.status ?? resultStatus;
|
|
1578
|
+
stats = payload.stats ?? stats;
|
|
1579
|
+
lastError = payload.error ?? lastError;
|
|
1580
|
+
}
|
|
1581
|
+
},
|
|
1582
|
+
onStderrLine: (line) => {
|
|
1583
|
+
idleWatchdog.beat();
|
|
1584
|
+
if (isLikelyGeminiFailure(line)) {
|
|
1585
|
+
lastError = line.trim();
|
|
1586
|
+
}
|
|
1587
|
+
},
|
|
1588
|
+
});
|
|
1589
|
+
const usage = normalizeGeminiUsage(stats);
|
|
1590
|
+
const actualModelId = modelId;
|
|
1591
|
+
const estimatedCost = this.priceFetcher.estimateUsageCost({
|
|
1592
|
+
backendId: this.backendId,
|
|
1593
|
+
modelId: actualModelId,
|
|
1594
|
+
usage,
|
|
1595
|
+
fallbackModel: findRegisteredModel(this.backendId, actualModelId),
|
|
1596
|
+
});
|
|
1597
|
+
const cost = withDurationMs({
|
|
1598
|
+
tokensInput: usage.inputTokens,
|
|
1599
|
+
tokensOutput: usage.outputTokens,
|
|
1600
|
+
cacheCreationTokens: usage.cacheCreationInputTokens,
|
|
1601
|
+
cacheReadTokens: usage.cacheReadInputTokens,
|
|
1602
|
+
costUsd: estimatedCost.costUsd,
|
|
1603
|
+
durationMs: 0,
|
|
1604
|
+
numTurns: 1,
|
|
1605
|
+
}, startMs);
|
|
1606
|
+
// Order matters: wrong_tool is checked before the abort branch
|
|
1607
|
+
// because the early-abort path (proxyAborter.abort) sets
|
|
1608
|
+
// wrongToolName + triggers a kill. Without this ordering, the
|
|
1609
|
+
// failure would surface as "cancelled" instead of the actual
|
|
1610
|
+
// upstream cause. The idle-watchdog branch is checked AFTER
|
|
1611
|
+
// wrong_tool (a wrong-tool-then-idle race should still attribute
|
|
1612
|
+
// wrong_tool, since the model demonstrably misbehaved) but BEFORE
|
|
1613
|
+
// the caller's abortSignal branch (idle-induced proxyAborter abort
|
|
1614
|
+
// does not propagate back to abortSignal, so without this ordering
|
|
1615
|
+
// an idle hang would mis-classify as `no_tool_call`).
|
|
1616
|
+
if (wrongToolName !== null && !calledMatchingTool) {
|
|
1617
|
+
return {
|
|
1618
|
+
ok: false,
|
|
1619
|
+
errorClass: "wrong_tool",
|
|
1620
|
+
message: `model called '${wrongToolName}' instead of requested '${toolName}'`,
|
|
1621
|
+
cost,
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
if (idleTimedOut) {
|
|
1625
|
+
return {
|
|
1626
|
+
ok: false,
|
|
1627
|
+
errorClass: "timeout",
|
|
1628
|
+
message: `delegated proxy stream went idle (no gemini events for ${idleTimeoutMs}ms)`,
|
|
1629
|
+
cost,
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
if (abortSignal?.aborted) {
|
|
1633
|
+
const errorClass = classifyAbortReason(abortSignal.reason);
|
|
1634
|
+
return {
|
|
1635
|
+
ok: false,
|
|
1636
|
+
errorClass,
|
|
1637
|
+
message: errorClass === "timeout"
|
|
1638
|
+
? "delegated proxy timed out (wall-clock)"
|
|
1639
|
+
: "delegated proxy cancelled by caller",
|
|
1640
|
+
cost,
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
if (runResult.timedOut) {
|
|
1644
|
+
return {
|
|
1645
|
+
ok: false,
|
|
1646
|
+
errorClass: "timeout",
|
|
1647
|
+
message: "gemini subprocess exceeded local safety-net timeout",
|
|
1648
|
+
cost,
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
if (!calledMatchingTool) {
|
|
1652
|
+
const failure = lastError;
|
|
1653
|
+
if (failure && /unauthorized|forbidden|api key|login|auth/i.test(failure)) {
|
|
1654
|
+
return {
|
|
1655
|
+
ok: false,
|
|
1656
|
+
errorClass: "auth_error",
|
|
1657
|
+
message: failure,
|
|
1658
|
+
cost,
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
return {
|
|
1662
|
+
ok: false,
|
|
1663
|
+
errorClass: "no_tool_call",
|
|
1664
|
+
message: failure
|
|
1665
|
+
?? `model did not invoke '${toolName}' (resultStatus=${resultStatus ?? "unknown"})`,
|
|
1666
|
+
cost,
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
// Preferred path: structured tool_result captured from the stream.
|
|
1670
|
+
// Reliable across Gemini Flash Lite's prose-narration tendency
|
|
1671
|
+
// because we ignore the assistant message and use the connector's
|
|
1672
|
+
// raw output verbatim.
|
|
1673
|
+
if (capturedToolOutput !== null) {
|
|
1674
|
+
if (capturedToolStatus === "error") {
|
|
1675
|
+
// Gemini CLI surfaces an MCP-tool-name-not-in-registry condition
|
|
1676
|
+
// as a tool_result with `status="error"` and an output starting
|
|
1677
|
+
// `Tool "<name>" not found. Did you mean…`. This is a transient
|
|
1678
|
+
// CLI-side registry miss (extension MCP server cold-start race
|
|
1679
|
+
// observed when the cadence worker's first tick fires <10s after
|
|
1680
|
+
// an integration switches to `delegated` — the host's
|
|
1681
|
+
// google-workspace MCP hasn't completed handshake yet, so the
|
|
1682
|
+
// model's tool_use lands on an empty registry). It is NOT a
|
|
1683
|
+
// real connector error from the upstream API, so we classify it
|
|
1684
|
+
// distinctly and let `delegated-sync-worker`'s retry policy
|
|
1685
|
+
// bounce once with a delay; the second attempt finds the tool
|
|
1686
|
+
// registered.
|
|
1687
|
+
if (isToolNotRegisteredError(capturedToolOutput, toolName)) {
|
|
1688
|
+
return {
|
|
1689
|
+
ok: false,
|
|
1690
|
+
errorClass: "tool_not_registered",
|
|
1691
|
+
message: capturedToolOutput,
|
|
1692
|
+
cost,
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
return {
|
|
1696
|
+
ok: false,
|
|
1697
|
+
errorClass: "tool_error",
|
|
1698
|
+
message: capturedToolOutput || "tool returned error",
|
|
1699
|
+
cost,
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
this.incrementRequestsCount(today);
|
|
1703
|
+
return {
|
|
1704
|
+
ok: true,
|
|
1705
|
+
toolResult: tryParseToolResult(capturedToolOutput),
|
|
1706
|
+
cost,
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
// Fallback: text-based extraction from the assistant message.
|
|
1710
|
+
// Reached when the CLI's stream-json schema doesn't emit
|
|
1711
|
+
// tool_result events (older versions) or when the matching
|
|
1712
|
+
// tool_use never paired with a tool_result for some reason.
|
|
1713
|
+
//
|
|
1714
|
+
// Warn so production can detect CLI version drift — silent fallback
|
|
1715
|
+
// is what produced the original Flash Lite "narrate-the-result"
|
|
1716
|
+
// bug. If this warning starts firing, audit the CLI version.
|
|
1717
|
+
logger.warn({
|
|
1718
|
+
integrationKey: params.integrationKey,
|
|
1719
|
+
toolName,
|
|
1720
|
+
backendId: this.backendId,
|
|
1721
|
+
modelId,
|
|
1722
|
+
matchingToolUseCount: matchingToolIds.size,
|
|
1723
|
+
}, "Gemini delegated proxy fell back to assistant-text extraction — no paired tool_result event captured. Audit CLI stream schema if this fires regularly.");
|
|
1724
|
+
const resultText = finalAssistantMessage.trim().length > 0
|
|
1725
|
+
? finalAssistantMessage.trim()
|
|
1726
|
+
: assistantDelta.trim();
|
|
1727
|
+
if (resultText.length === 0) {
|
|
1728
|
+
return {
|
|
1729
|
+
ok: false,
|
|
1730
|
+
errorClass: "parse_error",
|
|
1731
|
+
message: "gemini emitted matching tool_use but no tool_result event and no assistant text to extract result from",
|
|
1732
|
+
cost,
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
// resultStatus === "error" with content present is a connector
|
|
1736
|
+
// tool_error path — surface the message verbatim per proxy.md.
|
|
1737
|
+
if (resultStatus === "error") {
|
|
1738
|
+
return {
|
|
1739
|
+
ok: false,
|
|
1740
|
+
errorClass: "tool_error",
|
|
1741
|
+
message: resultText,
|
|
1742
|
+
cost,
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
// Bump the per-agent-day counter on the success path only, mirroring
|
|
1746
|
+
// `runTurn` line 720. Proxy success consumes one Gemini request
|
|
1747
|
+
// (turn-level approximation; tool fanout still counts as one,
|
|
1748
|
+
// matching the existing accounting).
|
|
1749
|
+
this.incrementRequestsCount(today);
|
|
1750
|
+
return {
|
|
1751
|
+
ok: true,
|
|
1752
|
+
toolResult: tryParseToolResult(resultText),
|
|
1753
|
+
cost,
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
catch (err) {
|
|
1757
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1758
|
+
const cost = withDurationMs(emptyCost(), startMs);
|
|
1759
|
+
if (abortSignal?.aborted) {
|
|
1760
|
+
return {
|
|
1761
|
+
ok: false,
|
|
1762
|
+
errorClass: classifyAbortReason(abortSignal.reason),
|
|
1763
|
+
message,
|
|
1764
|
+
cost,
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
return { ok: false, errorClass: "subprocess_crashed", message, cost };
|
|
1768
|
+
}
|
|
1769
|
+
finally {
|
|
1770
|
+
// Stop the idle watchdog before listener cleanup so a late-firing
|
|
1771
|
+
// poll cannot abort an already-resolved subprocess.
|
|
1772
|
+
idleWatchdog.stop();
|
|
1773
|
+
// Drop the listener regardless of how the call resolved so a
|
|
1774
|
+
// long-lived caller signal doesn't accumulate references across
|
|
1775
|
+
// back-to-back proxy invocations.
|
|
1776
|
+
if (abortSignal && !abortSignal.aborted) {
|
|
1777
|
+
abortSignal.removeEventListener("abort", callerAbortListener);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* DELEGATED-TASK-MODE-DESIGN.md §9.3 — Gemini CLI task mode.
|
|
1783
|
+
*
|
|
1784
|
+
* The admin policy is rebuilt per-request as a tightly-scoped task-only
|
|
1785
|
+
* surface (not the full strict/allow generators):
|
|
1786
|
+
* - catch-all deny at priority 1
|
|
1787
|
+
* - per-task allow rules at priority 920 for each tool in `allowedTools`
|
|
1788
|
+
* - destructive denies at priority 998 (when allowDestructive=false)
|
|
1789
|
+
* - absolute-block at priority 999
|
|
1790
|
+
* No shell or file allows — task mode runs MCP tools only.
|
|
1791
|
+
*
|
|
1792
|
+
* Stream parsing tracks `tool_use` / `tool_result` pairs to populate the
|
|
1793
|
+
* trace, counts assistant turns for the §8.3 Gemini per-day request
|
|
1794
|
+
* counter, and aborts on `maxToolCalls` overrun or any tool not in
|
|
1795
|
+
* `allowedTools` (defense-in-depth past the admin TOML).
|
|
1796
|
+
*/
|
|
1797
|
+
async runDelegatedTask(params) {
|
|
1798
|
+
const startMs = Date.now();
|
|
1799
|
+
const { systemPrompt, allowedTools, destructiveTools, writeClassTools, modelId, maxToolCalls, sessionDir, abortSignal, onToolStep, allowDestructive, } = params;
|
|
1800
|
+
const trace = [];
|
|
1801
|
+
let writeClassToolFired = false;
|
|
1802
|
+
// §6.2 / §7.4 — match against the *write-class* set (destructive ∪
|
|
1803
|
+
// reversible writes), not just destructive. Otherwise reversible
|
|
1804
|
+
// write tools like `create_draft` slip past the retry guard and the
|
|
1805
|
+
// single retry creates a duplicate side effect.
|
|
1806
|
+
//
|
|
1807
|
+
// Phase 1 (`/exec`) entries are fully-qualified exact names; Phase 2
|
|
1808
|
+
// (`/api/delegated/run`) may pass `*`-suffixed glob patterns. The shared
|
|
1809
|
+
// `matchRunAllowedToolPattern` helper covers both shapes — exact equality
|
|
1810
|
+
// OR trailing-`*` prefix match — so the same matcher applies to the
|
|
1811
|
+
// allowed-tool guard further down (§9.3 stream-level enforcement).
|
|
1812
|
+
const writeClassMatcher = (name) => writeClassTools.some((pattern) => matchRunAllowedToolPattern(pattern, name));
|
|
1813
|
+
if (!this.cliPath) {
|
|
1814
|
+
return {
|
|
1815
|
+
ok: false,
|
|
1816
|
+
errorClass: "subprocess_crashed",
|
|
1817
|
+
message: "gemini CLI not found on PATH",
|
|
1818
|
+
cost: withDurationMs(emptyCost(), startMs),
|
|
1819
|
+
trace,
|
|
1820
|
+
writeClassToolFired,
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
const auth = await this.checkAuth();
|
|
1824
|
+
if (!auth.ok) {
|
|
1825
|
+
return {
|
|
1826
|
+
ok: false,
|
|
1827
|
+
errorClass: "auth_error",
|
|
1828
|
+
message: auth.reason,
|
|
1829
|
+
cost: withDurationMs(emptyCost(), startMs),
|
|
1830
|
+
trace,
|
|
1831
|
+
writeClassToolFired,
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
// §8.3 — pre-spawn daily-request gate. Mirrors runDelegatedTool because
|
|
1835
|
+
// task mode consumes the same daily budget.
|
|
1836
|
+
const today = getAgentDayDateStr(this.config.timezone, this.config.dayBoundaryHour);
|
|
1837
|
+
const ceiling = this.resolveDailyRequestCeiling();
|
|
1838
|
+
if (ceiling !== null) {
|
|
1839
|
+
const currentCount = this.readRequestsCount(today);
|
|
1840
|
+
if (currentCount >= ceiling) {
|
|
1841
|
+
return {
|
|
1842
|
+
ok: false,
|
|
1843
|
+
errorClass: "auth_error",
|
|
1844
|
+
message: `Gemini daily-request ceiling reached (${currentCount}/${ceiling}) — `
|
|
1845
|
+
+ `resets at the next agent-day boundary.`,
|
|
1846
|
+
cost: withDurationMs(emptyCost(), startMs),
|
|
1847
|
+
trace,
|
|
1848
|
+
writeClassToolFired,
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
const policyPath = join(sessionDir, ADMIN_POLICY_FILENAME);
|
|
1853
|
+
writeFileSync(policyPath, this.generateTaskModePolicy({
|
|
1854
|
+
allowedTools,
|
|
1855
|
+
destructiveTools: allowDestructive ? [] : destructiveTools,
|
|
1856
|
+
}), "utf-8");
|
|
1857
|
+
const args = this.buildArgs({ prompt: systemPrompt, modelId }, policyPath);
|
|
1858
|
+
const pendingByUseId = new Map();
|
|
1859
|
+
let toolCallCount = 0;
|
|
1860
|
+
let loopAborted = false;
|
|
1861
|
+
let assistantDelta = "";
|
|
1862
|
+
let finalAssistantMessage = "";
|
|
1863
|
+
let lastError = null;
|
|
1864
|
+
let resultStatus = null;
|
|
1865
|
+
let stats = null;
|
|
1866
|
+
let policyViolationTool = null;
|
|
1867
|
+
let assistantTurns = 0;
|
|
1868
|
+
const aborter = new AbortController();
|
|
1869
|
+
const callerListener = () => aborter.abort(abortSignal?.reason);
|
|
1870
|
+
if (abortSignal) {
|
|
1871
|
+
if (abortSignal.aborted) {
|
|
1872
|
+
aborter.abort(abortSignal.reason);
|
|
1873
|
+
}
|
|
1874
|
+
else {
|
|
1875
|
+
abortSignal.addEventListener("abort", callerListener, { once: true });
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
// §9.3 — stream-level allowed-tools guard. `allowedTools` may carry
|
|
1879
|
+
// Phase 2 (`/api/delegated/run`) trailing-`*` glob patterns
|
|
1880
|
+
// (DELEGATED-TASK-MODE-DESIGN.md §4.2), so an exact-equality `Set.has`
|
|
1881
|
+
// would silently reject every glob-admitted call as `policy_violation`.
|
|
1882
|
+
// The Gemini admin policy TOML at priority 920 honors the same glob
|
|
1883
|
+
// shape upstream (verified against the gemini-cli policy engine docs);
|
|
1884
|
+
// the daemon-side guard mirrors that semantics so the two layers agree.
|
|
1885
|
+
// Phase 1 (`/exec`) entries are fully-qualified exact names — the
|
|
1886
|
+
// exact-equality fast path inside `matchRunAllowedToolPattern` covers
|
|
1887
|
+
// them at one comparison.
|
|
1888
|
+
const isAllowedTool = (name) => allowedTools.some((pattern) => matchRunAllowedToolPattern(pattern, name));
|
|
1889
|
+
// `destructiveTools` is fully-qualified exact names from the registry
|
|
1890
|
+
// (Phase 1 only — Phase 2 passes `[]`), so a Set lookup is correct here
|
|
1891
|
+
// and stays O(1).
|
|
1892
|
+
const destructiveSet = allowDestructive
|
|
1893
|
+
? new Set()
|
|
1894
|
+
: new Set(destructiveTools);
|
|
1895
|
+
try {
|
|
1896
|
+
const runResult = await runLineCommand({
|
|
1897
|
+
command: this.cliPath,
|
|
1898
|
+
args,
|
|
1899
|
+
cwd: sessionDir,
|
|
1900
|
+
env: buildDaemonApiCliEnv(sessionDir, this.config.apiPort, { sessionBackend: "gemini" }),
|
|
1901
|
+
timeoutMs: (DELEGATED_PROXY_DEFAULTS.callTimeoutMsByBackend.gemini
|
|
1902
|
+
?? DELEGATED_PROXY_DEFAULTS.callTimeoutMs)
|
|
1903
|
+
+ 60_000,
|
|
1904
|
+
abortSignal: aborter.signal,
|
|
1905
|
+
onStdoutLine: (line) => {
|
|
1906
|
+
const event = parseJsonLine(line);
|
|
1907
|
+
if (!event?.type)
|
|
1908
|
+
return;
|
|
1909
|
+
if (event.type === "tool_use" && typeof event.tool_name === "string") {
|
|
1910
|
+
const toolName = event.tool_name;
|
|
1911
|
+
if (!isAllowedTool(toolName) || destructiveSet.has(toolName)) {
|
|
1912
|
+
policyViolationTool = toolName;
|
|
1913
|
+
loopAborted = true;
|
|
1914
|
+
aborter.abort(new Error("policy_violation"));
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
toolCallCount += 1;
|
|
1918
|
+
if (toolCallCount > maxToolCalls) {
|
|
1919
|
+
loopAborted = true;
|
|
1920
|
+
aborter.abort(new Error("loop_aborted"));
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
if (writeClassMatcher(toolName)) {
|
|
1924
|
+
writeClassToolFired = true;
|
|
1925
|
+
}
|
|
1926
|
+
if (typeof event.tool_id === "string") {
|
|
1927
|
+
pendingByUseId.set(event.tool_id, {
|
|
1928
|
+
name: toolName,
|
|
1929
|
+
args: event.args ?? null,
|
|
1930
|
+
startedAt: Date.now(),
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
if (event.type === "tool_result"
|
|
1936
|
+
&& typeof event.tool_id === "string") {
|
|
1937
|
+
const pending = pendingByUseId.get(event.tool_id);
|
|
1938
|
+
if (!pending)
|
|
1939
|
+
return;
|
|
1940
|
+
pendingByUseId.delete(event.tool_id);
|
|
1941
|
+
const status = event.status === "error" ? "error" : "ok";
|
|
1942
|
+
// `event.output` is a string-encoded tool result (the
|
|
1943
|
+
// google-workspace extension and most other connectors
|
|
1944
|
+
// serialize JSON responses to text). Try to parse so the
|
|
1945
|
+
// response-shape walker downstream can pluck ids; fall back
|
|
1946
|
+
// to the raw string when parsing fails (free-form text
|
|
1947
|
+
// replies).
|
|
1948
|
+
let parsedToolResult;
|
|
1949
|
+
if (typeof event.output === "string") {
|
|
1950
|
+
try {
|
|
1951
|
+
parsedToolResult = JSON.parse(event.output);
|
|
1952
|
+
}
|
|
1953
|
+
catch {
|
|
1954
|
+
parsedToolResult = event.output;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
const step = {
|
|
1958
|
+
toolName: pending.name,
|
|
1959
|
+
toolArgs: pending.args,
|
|
1960
|
+
durationMs: Date.now() - pending.startedAt,
|
|
1961
|
+
status,
|
|
1962
|
+
costUsd: null,
|
|
1963
|
+
tokensInput: null,
|
|
1964
|
+
tokensOutput: null,
|
|
1965
|
+
toolResult: parsedToolResult,
|
|
1966
|
+
};
|
|
1967
|
+
trace.push(step);
|
|
1968
|
+
onToolStep?.(step);
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
if (event.type === "message" && event.role === "assistant") {
|
|
1972
|
+
assistantTurns += 1;
|
|
1973
|
+
const content = typeof event.content === "string" ? event.content : "";
|
|
1974
|
+
if (!content)
|
|
1975
|
+
return;
|
|
1976
|
+
if (event.delta) {
|
|
1977
|
+
assistantDelta += content;
|
|
1978
|
+
}
|
|
1979
|
+
else {
|
|
1980
|
+
finalAssistantMessage = content;
|
|
1981
|
+
}
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
if (event.type === "result") {
|
|
1985
|
+
const payload = event.result ?? event;
|
|
1986
|
+
resultStatus = payload.status ?? resultStatus;
|
|
1987
|
+
stats = payload.stats ?? stats;
|
|
1988
|
+
lastError = payload.error ?? lastError;
|
|
1989
|
+
}
|
|
1990
|
+
},
|
|
1991
|
+
onStderrLine: (line) => {
|
|
1992
|
+
if (isLikelyGeminiFailure(line)) {
|
|
1993
|
+
lastError = line.trim();
|
|
1994
|
+
}
|
|
1995
|
+
},
|
|
1996
|
+
});
|
|
1997
|
+
const usage = normalizeGeminiUsage(stats);
|
|
1998
|
+
const estimatedCost = this.priceFetcher.estimateUsageCost({
|
|
1999
|
+
backendId: this.backendId,
|
|
2000
|
+
modelId,
|
|
2001
|
+
usage,
|
|
2002
|
+
fallbackModel: findRegisteredModel(this.backendId, modelId),
|
|
2003
|
+
});
|
|
2004
|
+
const cost = withDurationMs({
|
|
2005
|
+
tokensInput: usage.inputTokens,
|
|
2006
|
+
tokensOutput: usage.outputTokens,
|
|
2007
|
+
cacheCreationTokens: usage.cacheCreationInputTokens,
|
|
2008
|
+
cacheReadTokens: usage.cacheReadInputTokens,
|
|
2009
|
+
costUsd: estimatedCost.costUsd,
|
|
2010
|
+
durationMs: 0,
|
|
2011
|
+
numTurns: assistantTurns,
|
|
2012
|
+
}, startMs);
|
|
2013
|
+
// §8.3 per-turn counter — increment once per assistant message
|
|
2014
|
+
// observed (turn-level approximation matching Google's billing
|
|
2015
|
+
// surface).
|
|
2016
|
+
for (let i = 0; i < assistantTurns; i++) {
|
|
2017
|
+
this.incrementRequestsCount(today);
|
|
2018
|
+
}
|
|
2019
|
+
if (policyViolationTool) {
|
|
2020
|
+
return {
|
|
2021
|
+
ok: false,
|
|
2022
|
+
errorClass: "policy_violation",
|
|
2023
|
+
message: `subprocess attempted to call '${policyViolationTool}' which is outside the per-task allowlist`,
|
|
2024
|
+
cost,
|
|
2025
|
+
trace,
|
|
2026
|
+
writeClassToolFired,
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
if (loopAborted) {
|
|
2030
|
+
return {
|
|
2031
|
+
ok: false,
|
|
2032
|
+
errorClass: "loop_aborted",
|
|
2033
|
+
message: `subprocess exceeded maxToolCalls=${maxToolCalls}`,
|
|
2034
|
+
cost,
|
|
2035
|
+
trace,
|
|
2036
|
+
writeClassToolFired,
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
if (abortSignal?.aborted) {
|
|
2040
|
+
const errorClass = classifyAbortReason(abortSignal.reason);
|
|
2041
|
+
return {
|
|
2042
|
+
ok: false,
|
|
2043
|
+
errorClass,
|
|
2044
|
+
message: errorClass === "timeout"
|
|
2045
|
+
? "delegated task timed out (wall-clock)"
|
|
2046
|
+
: "delegated task cancelled by caller",
|
|
2047
|
+
cost,
|
|
2048
|
+
trace,
|
|
2049
|
+
writeClassToolFired,
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
if (runResult.timedOut) {
|
|
2053
|
+
return {
|
|
2054
|
+
ok: false,
|
|
2055
|
+
errorClass: "timeout",
|
|
2056
|
+
message: "gemini subprocess exceeded local safety-net timeout",
|
|
2057
|
+
cost,
|
|
2058
|
+
trace,
|
|
2059
|
+
writeClassToolFired,
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
const finalText = finalAssistantMessage.trim().length > 0
|
|
2063
|
+
? finalAssistantMessage.trim()
|
|
2064
|
+
: assistantDelta.trim();
|
|
2065
|
+
if (finalText.length === 0) {
|
|
2066
|
+
const failure = lastError;
|
|
2067
|
+
if (failure && /unauthorized|forbidden|api key|login|auth/i.test(failure)) {
|
|
2068
|
+
return {
|
|
2069
|
+
ok: false,
|
|
2070
|
+
errorClass: "auth_error",
|
|
2071
|
+
message: failure,
|
|
2072
|
+
cost,
|
|
2073
|
+
trace,
|
|
2074
|
+
writeClassToolFired,
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
return {
|
|
2078
|
+
ok: false,
|
|
2079
|
+
errorClass: "parse_error",
|
|
2080
|
+
message: failure
|
|
2081
|
+
?? `gemini emitted no final assistant message (resultStatus=${resultStatus ?? "unknown"})`,
|
|
2082
|
+
cost,
|
|
2083
|
+
trace,
|
|
2084
|
+
writeClassToolFired,
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
return {
|
|
2088
|
+
ok: true,
|
|
2089
|
+
rawAssistantText: finalText,
|
|
2090
|
+
cost,
|
|
2091
|
+
trace,
|
|
2092
|
+
writeClassToolFired,
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
catch (err) {
|
|
2096
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2097
|
+
const cost = withDurationMs(emptyCost(), startMs);
|
|
2098
|
+
if (abortSignal?.aborted || aborter.signal.aborted) {
|
|
2099
|
+
return {
|
|
2100
|
+
ok: false,
|
|
2101
|
+
errorClass: classifyAbortReason(abortSignal?.reason ?? aborter.signal.reason),
|
|
2102
|
+
message,
|
|
2103
|
+
cost,
|
|
2104
|
+
trace,
|
|
2105
|
+
writeClassToolFired,
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
return {
|
|
2109
|
+
ok: false,
|
|
2110
|
+
errorClass: "subprocess_crashed",
|
|
2111
|
+
message,
|
|
2112
|
+
cost,
|
|
2113
|
+
trace,
|
|
2114
|
+
writeClassToolFired,
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
finally {
|
|
2118
|
+
if (abortSignal) {
|
|
2119
|
+
abortSignal.removeEventListener("abort", callerListener);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* DELEGATED-TASK-MODE-DESIGN.md §9.3 — task-mode admin policy. Tightly
|
|
2125
|
+
* scoped: catch-all deny at priority 1, per-task allow rules at 920,
|
|
2126
|
+
* destructive denies at 998, absolute-block at 999. No shell or file
|
|
2127
|
+
* allows — the task subprocess only needs MCP tool calls.
|
|
2128
|
+
*/
|
|
2129
|
+
generateTaskModePolicy(args) {
|
|
2130
|
+
const parts = [];
|
|
2131
|
+
parts.push(`# ${APP_NAME} task-mode admin policy (auto-generated)`, "# DELEGATED-TASK-MODE-DESIGN.md §9.3", "", "# Catch-all deny", "[[rule]]", 'toolName = "*"', 'decision = "deny"', 'denyMessage = "Tool not in the per-task allowlist."', "priority = 1", "");
|
|
2132
|
+
if (args.destructiveTools.length > 0) {
|
|
2133
|
+
parts.push("# Destructive tools — denied at priority 998 (allowDestructive=false)");
|
|
2134
|
+
for (const tool of args.destructiveTools) {
|
|
2135
|
+
parts.push("[[rule]]", `toolName = ${JSON.stringify(tool)}`, 'decision = "deny"', 'denyMessage = "Destructive tool denied — caller did not pass allowDestructive=true."', "priority = 998", "");
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
if (args.allowedTools.length > 0) {
|
|
2139
|
+
parts.push("# Per-task allowed tools (priority 920)");
|
|
2140
|
+
for (const tool of args.allowedTools) {
|
|
2141
|
+
parts.push("[[rule]]", `toolName = ${JSON.stringify(tool)}`, 'decision = "allow"', "priority = 920", "");
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
parts.push(this.buildAbsoluteBlockRules());
|
|
2145
|
+
return parts.join("\n");
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
function isMaxBudgetMessage(message) {
|
|
2149
|
+
return /max(?:imum)? budget|max_budget_usd|budget limit|per-turn budget/i.test(message);
|
|
2150
|
+
}
|
|
2151
|
+
function normalizeGeminiUsage(stats) {
|
|
2152
|
+
if (!stats) {
|
|
2153
|
+
return { ...EMPTY_USAGE };
|
|
2154
|
+
}
|
|
2155
|
+
const totalInputTokens = readNumber(stats.input_tokens);
|
|
2156
|
+
const cacheReadInputTokens = readNumber(stats.cached);
|
|
2157
|
+
return {
|
|
2158
|
+
inputTokens: nonCachedInputTokens(totalInputTokens, cacheReadInputTokens),
|
|
2159
|
+
outputTokens: readNumber(stats.output_tokens),
|
|
2160
|
+
cacheCreationInputTokens: 0,
|
|
2161
|
+
cacheReadInputTokens,
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
function buildGeminiModelUsage(priceFetcher, stats) {
|
|
2165
|
+
if (!stats?.models) {
|
|
2166
|
+
return { modelUsage: {}, costSource: "hardcoded" };
|
|
2167
|
+
}
|
|
2168
|
+
const usage = {};
|
|
2169
|
+
let allFromLitellm = true;
|
|
2170
|
+
for (const [modelId, modelStats] of Object.entries(stats.models)) {
|
|
2171
|
+
const cacheReadInputTokens = readNumber(modelStats.cached);
|
|
2172
|
+
const normalized = {
|
|
2173
|
+
inputTokens: nonCachedInputTokens(readNumber(modelStats.input_tokens), cacheReadInputTokens),
|
|
2174
|
+
outputTokens: readNumber(modelStats.output_tokens),
|
|
2175
|
+
cacheCreationInputTokens: 0,
|
|
2176
|
+
cacheReadInputTokens,
|
|
2177
|
+
};
|
|
2178
|
+
const priceEstimate = priceFetcher.estimateUsageCost({
|
|
2179
|
+
backendId: "gemini",
|
|
2180
|
+
modelId,
|
|
2181
|
+
usage: normalized,
|
|
2182
|
+
fallbackModel: findRegisteredModel("gemini", modelId),
|
|
2183
|
+
});
|
|
2184
|
+
usage[modelId] = {
|
|
2185
|
+
inputTokens: normalized.inputTokens,
|
|
2186
|
+
outputTokens: normalized.outputTokens,
|
|
2187
|
+
costUsd: priceEstimate.costUsd,
|
|
2188
|
+
};
|
|
2189
|
+
if (priceEstimate.costSource !== "litellm") {
|
|
2190
|
+
allFromLitellm = false;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
return {
|
|
2194
|
+
modelUsage: usage,
|
|
2195
|
+
costSource: allFromLitellm ? "litellm" : "hardcoded",
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function nonCachedInputTokens(totalInputTokens, cacheReadInputTokens, cacheCreationInputTokens = 0) {
|
|
2199
|
+
return Math.max(totalInputTokens - cacheReadInputTokens - cacheCreationInputTokens, 0);
|
|
2200
|
+
}
|
|
2201
|
+
function resolveActualGeminiModel(requestedModel, modelUsage) {
|
|
2202
|
+
const [firstModel] = Object.keys(modelUsage);
|
|
2203
|
+
return firstModel ?? requestedModel;
|
|
2204
|
+
}
|
|
2205
|
+
function isLikelyGeminiFailure(line) {
|
|
2206
|
+
return /error|failed|authentication|oauth|api key|quota|rate limit/i.test(line);
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
* Detect Gemini CLI's "tool name not in registry" tool_result error pattern.
|
|
2210
|
+
*
|
|
2211
|
+
* Format produced by `getToolSuggestion` in @google/gemini-cli (verified
|
|
2212
|
+
* 2026-05-04 against bundle 0.40.1):
|
|
2213
|
+
* `Tool "<name>" not found. Did you mean one of: "<a>", "<b>"?` (≥2 hits)
|
|
2214
|
+
* `Tool "<name>" not found. Did you mean "<a>"?` (1 hit)
|
|
2215
|
+
*
|
|
2216
|
+
* The expected `toolName` is included in the literal because we want to
|
|
2217
|
+
* avoid false positives — a connector tool that itself happens to emit a
|
|
2218
|
+
* "not found" message about some upstream resource (e.g. a Notion page
|
|
2219
|
+
* not_found error) should NOT be reclassified as transient.
|
|
2220
|
+
*/
|
|
2221
|
+
export function isToolNotRegisteredError(output, expectedToolName) {
|
|
2222
|
+
if (typeof output !== "string" || output.length === 0)
|
|
2223
|
+
return false;
|
|
2224
|
+
const escapedName = expectedToolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2225
|
+
return new RegExp(`^Tool "${escapedName}" not found\\. Did you mean`).test(output);
|
|
2226
|
+
}
|
|
2227
|
+
function firstFailureLine(lines) {
|
|
2228
|
+
const line = lines.find((candidate) => isLikelyGeminiFailure(candidate));
|
|
2229
|
+
return line?.trim() ?? null;
|
|
2230
|
+
}
|
|
2231
|
+
function readNumber(value) {
|
|
2232
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
2233
|
+
}
|
|
2234
|
+
/**
|
|
2235
|
+
* Parse `PA_GEMINI_OAUTH_GRACE_HOURS`. Returns `fallback` for undefined
|
|
2236
|
+
* or malformed values; a parseable 0 disables the grace entirely (the
|
|
2237
|
+
* intended escape hatch once the upstream CLI bug is fixed).
|
|
2238
|
+
*/
|
|
2239
|
+
function parseGraceHours(value, fallback) {
|
|
2240
|
+
if (value === undefined)
|
|
2241
|
+
return fallback;
|
|
2242
|
+
const n = Number(value);
|
|
2243
|
+
if (!Number.isFinite(n) || n < 0)
|
|
2244
|
+
return fallback;
|
|
2245
|
+
return n;
|
|
2246
|
+
}
|
|
2247
|
+
/**
|
|
2248
|
+
* Append `@<relativePath>` tokens for every staged attachment to the tail
|
|
2249
|
+
* of the Gemini prompt. Gemini CLI v0.37+ expands these in non-interactive
|
|
2250
|
+
* mode by reading the file bytes and inlining them as multimodal content.
|
|
2251
|
+
*
|
|
2252
|
+
* The bracketed `[Attached files]` text block composed by the dispatcher
|
|
2253
|
+
* still describes what each file is; this helper only adds the
|
|
2254
|
+
* machine-readable `@`-tokens the CLI looks for. Kept on a single trailing
|
|
2255
|
+
* line so prompt hashing / diff comparisons remain stable when the list is
|
|
2256
|
+
* empty.
|
|
2257
|
+
*
|
|
2258
|
+
* Pure — exported for unit testing.
|
|
2259
|
+
*/
|
|
2260
|
+
export function appendGeminiAttachmentTokens(prompt, staged) {
|
|
2261
|
+
if (!staged || staged.length === 0)
|
|
2262
|
+
return prompt;
|
|
2263
|
+
const tokens = staged.map((att) => `@${att.relativePath}`).join(" ");
|
|
2264
|
+
return `${prompt}\n\n${tokens}`;
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Read a Gemini config file (extension manifest or settings.json) and
|
|
2268
|
+
* collect every key under `mcpServers`. Tolerant of missing keys, malformed
|
|
2269
|
+
* JSON, and non-object payloads — best-effort host scan, not validation.
|
|
2270
|
+
*
|
|
2271
|
+
* Exported for unit testing.
|
|
2272
|
+
*/
|
|
2273
|
+
export function collectMcpServerNames(filePath, out) {
|
|
2274
|
+
let raw;
|
|
2275
|
+
try {
|
|
2276
|
+
raw = readFileSync(filePath, "utf-8");
|
|
2277
|
+
}
|
|
2278
|
+
catch {
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
let parsed;
|
|
2282
|
+
try {
|
|
2283
|
+
parsed = JSON.parse(raw);
|
|
2284
|
+
}
|
|
2285
|
+
catch {
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
2289
|
+
return;
|
|
2290
|
+
const mcpServers = parsed.mcpServers;
|
|
2291
|
+
// typeof null === "object" and typeof [] === "object" — guard both. A
|
|
2292
|
+
// user who hand-edited settings.json with `mcpServers: [{...}]` (array
|
|
2293
|
+
// instead of dict) would otherwise have `Object.keys` return numeric
|
|
2294
|
+
// index strings ("0", "1") that get added as bogus server names.
|
|
2295
|
+
if (!mcpServers
|
|
2296
|
+
|| typeof mcpServers !== "object"
|
|
2297
|
+
|| Array.isArray(mcpServers)) {
|
|
2298
|
+
return;
|
|
2299
|
+
}
|
|
2300
|
+
for (const name of Object.keys(mcpServers)) {
|
|
2301
|
+
out.add(name);
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
/**
|
|
2305
|
+
* Extract the MCP server name from a Gemini-convention `toolNamespace`.
|
|
2306
|
+
* Gemini's MCP namespace is `mcp_<server>_<rest>`, so the server name is
|
|
2307
|
+
* everything between the first `_` and the second `_` after the `mcp_`
|
|
2308
|
+
* prefix. Returns null if the namespace is not Gemini-shaped.
|
|
2309
|
+
*
|
|
2310
|
+
* Examples:
|
|
2311
|
+
* `mcp_google-workspace_gmail.` → `google-workspace`
|
|
2312
|
+
* `mcp_notion_` → `notion`
|
|
2313
|
+
* `mcp__claude_ai_Gmail__` → null (Claude double-underscore form)
|
|
2314
|
+
*
|
|
2315
|
+
* Exported for unit testing.
|
|
2316
|
+
*/
|
|
2317
|
+
export function extractGeminiServerName(toolNamespace) {
|
|
2318
|
+
if (!toolNamespace.startsWith("mcp_"))
|
|
2319
|
+
return null;
|
|
2320
|
+
// Reject Claude / Codex double-underscore namespaces — they begin with
|
|
2321
|
+
// `mcp__` which also starts with `mcp_`. Single-underscore is the
|
|
2322
|
+
// distinguishing trait of Gemini's convention.
|
|
2323
|
+
if (toolNamespace.startsWith("mcp__"))
|
|
2324
|
+
return null;
|
|
2325
|
+
const rest = toolNamespace.slice("mcp_".length);
|
|
2326
|
+
const sep = rest.indexOf("_");
|
|
2327
|
+
if (sep === -1)
|
|
2328
|
+
return rest.length > 0 ? rest : null;
|
|
2329
|
+
return sep === 0 ? null : rest.slice(0, sep);
|
|
2330
|
+
}
|
|
2331
|
+
//# sourceMappingURL=gemini-cli-core.js.map
|