@vellumai/assistant 0.6.5 → 0.6.6
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/AGENTS.md +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
package/AGENTS.md
CHANGED
|
@@ -14,7 +14,15 @@ When you introduce a new env var that the assistant process needs to read at run
|
|
|
14
14
|
|
|
15
15
|
## Daemon startup philosophy
|
|
16
16
|
|
|
17
|
-
The daemon must **never** block startup under
|
|
17
|
+
The daemon must **never** block startup under _any circumstance_. All possible errors should be logged so that the assistant can recover from it's corrupted state after the fact.
|
|
18
|
+
|
|
19
|
+
## Post-execution hooks
|
|
20
|
+
|
|
21
|
+
Tool post-execution hooks (`src/daemon/tool-side-effects.ts`) run after a tool executor returns. They are an **observation-and-notification layer** only: refresh client-side state, broadcast events, kick off orthogonal background work (e.g. icon generation). Hooks must not re-do work the executor already performed, and must not attempt recovery when the executor failed — failures surface in the tool result for the LLM to act on.
|
|
22
|
+
|
|
23
|
+
Do not coordinate hook behaviour by re-parsing the tool's JSON response to infer what the executor did (e.g. "if field X is missing, retry step Y"). That couples the LLM-facing response shape to internal daemon logic and breaks silently when the response shape evolves. Keep the hook's logic independent of the result payload, or if the hook genuinely needs executor-internal state, pass it through a typed side channel — never through a JSON round-trip.
|
|
24
|
+
|
|
25
|
+
Shared mutable resources written by more than one caller (e.g. `dist/` directories produced by `compileApp()`) must be serialised per-resource so concurrent callers cannot race on `rm -rf` + write sequences.
|
|
18
26
|
|
|
19
27
|
## Code comments
|
|
20
28
|
|
package/ARCHITECTURE.md
CHANGED
|
@@ -1045,13 +1045,11 @@ When all four reducer tiers are exhausted and the provider still rejects, the ov
|
|
|
1045
1045
|
|
|
1046
1046
|
| Session Type | Config Policy | Action |
|
|
1047
1047
|
| --------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
1048
|
-
| Interactive | `"summarize"` (default) | `
|
|
1048
|
+
| Interactive | `"summarize"` (default) | `auto_compress_latest_turn` — compress without asking |
|
|
1049
1049
|
| Non-interactive | `"truncate"` (default) | `auto_compress_latest_turn` — compress without asking |
|
|
1050
1050
|
| Any | `"drop"` | `fail_gracefully` — fall through to the final context-overflow fallback, which emits a `conversation_error` |
|
|
1051
1051
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
**Deny handling:** If the user declines compression, the session emits a graceful assistant explanation message ("The conversation has grown too long...") instead of a `conversation_error`. The deny message is persisted to conversation history and delivered via `assistant_text_delta` events, so the user sees a normal chat bubble rather than an error toast. The turn ends cleanly without triggering the error classification pipeline.
|
|
1052
|
+
When standard compaction has been exhausted and the provider still reports a context overflow, the recovery pipeline forces an emergency compaction of the latest turn with aggressive settings (`force: true`, `minKeepRecentUserTurns: 0`). The user is not prompted — compaction is always automatic. Users who want to opt out entirely can set `contextWindow.overflowRecovery.interactiveLatestTurnCompression` to `"drop"`, which short-circuits to a graceful failure instead.
|
|
1055
1053
|
|
|
1056
1054
|
### Config
|
|
1057
1055
|
|
|
@@ -1067,13 +1065,12 @@ All overflow recovery settings live under `contextWindow.overflowRecovery` in th
|
|
|
1067
1065
|
|
|
1068
1066
|
### Key Source Files
|
|
1069
1067
|
|
|
1070
|
-
| File
|
|
1071
|
-
|
|
|
1072
|
-
| `src/daemon/context-overflow-reducer.ts`
|
|
1073
|
-
| `src/daemon/context-overflow-policy.ts`
|
|
1074
|
-
| `src/daemon/
|
|
1075
|
-
| `src/
|
|
1076
|
-
| `src/config/core-schema.ts` | `ContextOverflowRecoveryConfigSchema` with defaults and validation |
|
|
1068
|
+
| File | Purpose |
|
|
1069
|
+
| ---------------------------------------- | ----------------------------------------------------------------------------- |
|
|
1070
|
+
| `src/daemon/context-overflow-reducer.ts` | Tiered reducer: four-tier pipeline with idempotent steps and cumulative state |
|
|
1071
|
+
| `src/daemon/context-overflow-policy.ts` | Overflow policy resolver: maps config + interactivity to concrete action |
|
|
1072
|
+
| `src/daemon/conversation-agent-loop.ts` | Integration: preflight budget check, convergence loop, emergency compaction |
|
|
1073
|
+
| `src/config/core-schema.ts` | `ContextOverflowRecoveryConfigSchema` with defaults and validation |
|
|
1077
1074
|
|
|
1078
1075
|
---
|
|
1079
1076
|
|
|
@@ -1562,13 +1559,14 @@ graph TB
|
|
|
1562
1559
|
|
|
1563
1560
|
FIND_RULE -->|"Deny rule"| DENY["decision: deny<br/>Blocked by rule"]
|
|
1564
1561
|
FIND_RULE -->|"Ask rule"| PROMPT_ASK["decision: prompt<br/>Always ask user"]
|
|
1565
|
-
FIND_RULE -->|"Allow rule"|
|
|
1566
|
-
|
|
1562
|
+
FIND_RULE -->|"Allow rule / No match"| SANDBOX_CHECK{"sandboxAutoApprove?<br/>(bash + allowlisted +<br/>containerized)"}
|
|
1563
|
+
|
|
1564
|
+
SANDBOX_CHECK -->|"yes"| AUTO_SANDBOX["decision: allow<br/>Sandbox auto-approve"]
|
|
1565
|
+
SANDBOX_CHECK -->|"no, has Allow rule"| RISK_CHECK{"Risk level?"}
|
|
1566
|
+
SANDBOX_CHECK -->|"no, no match"| NO_MATCH{"Fallback logic"}
|
|
1567
1567
|
|
|
1568
1568
|
RISK_CHECK -->|"Low / Medium"| AUTO_ALLOW["decision: allow<br/>Auto-allowed by rule"]
|
|
1569
|
-
RISK_CHECK -->|"High"|
|
|
1570
|
-
HIGH_CHECK -->|"yes"| AUTO_ALLOW
|
|
1571
|
-
HIGH_CHECK -->|"no"| RISK_THRESHOLD{"Risk-based<br/>threshold fallback"}
|
|
1569
|
+
RISK_CHECK -->|"High"| RISK_THRESHOLD{"Risk-based<br/>threshold fallback"}
|
|
1572
1570
|
|
|
1573
1571
|
NO_MATCH -->|"tool.origin === 'skill'"| PROMPT_SKILL["decision: prompt<br/>Skill tools always ask"]
|
|
1574
1572
|
NO_MATCH -->|"strict mode"| PROMPT_STRICT["decision: prompt<br/>No implicit auto-allow"]
|
|
@@ -1711,7 +1709,7 @@ When a permission prompt is sent to the client (via `confirmation_request` SSE e
|
|
|
1711
1709
|
| `allowlistOptions` | Suggested patterns for "always allow" rules |
|
|
1712
1710
|
| `scopeOptions` | Suggested scopes for rule persistence |
|
|
1713
1711
|
|
|
1714
|
-
The user can respond with: `allow` (one-time), `always_allow` (create allow rule), `deny` (one-time), or `always_deny` (create deny rule).
|
|
1712
|
+
The user can respond with: `allow` (one-time), `always_allow` (create allow rule), `deny` (one-time), or `always_deny` (create deny rule). In containerized environments, commands tagged with `sandboxAutoApprove` in their risk spec are auto-allowed via the approval policy's sandbox auto-approve check; non-allowlisted commands (network tools, runtimes, package managers) use the user's `autoApproveUpTo` threshold. All other risk-based decisions use the `autoApproveUpTo` threshold (default: `"low"`) -- tools at or below the threshold are auto-allowed, those above are prompted.
|
|
1715
1713
|
|
|
1716
1714
|
### Canonical Paths
|
|
1717
1715
|
|
package/Dockerfile
CHANGED
|
@@ -36,9 +36,6 @@ RUN set -eu; for pkg in /app/skills/*/package.json; do \
|
|
|
36
36
|
(cd "$dir" && (bun install --frozen-lockfile 2>/dev/null || bun install)); \
|
|
37
37
|
done
|
|
38
38
|
|
|
39
|
-
# Copy source
|
|
40
|
-
COPY assistant ./assistant
|
|
41
|
-
|
|
42
39
|
# Final stage
|
|
43
40
|
FROM debian:trixie-slim@sha256:4ffb3a1511099754cddc70eb1b12e50ffdb67619aa0ab6c13fcd800a78ef7c7a AS runner
|
|
44
41
|
|
|
@@ -133,8 +130,13 @@ EXPOSE 3001
|
|
|
133
130
|
ENV RUNTIME_HTTP_PORT=3001
|
|
134
131
|
ENV IS_CONTAINERIZED=true
|
|
135
132
|
|
|
136
|
-
# Copy from builder
|
|
133
|
+
# Copy installed deps + shared packages + bundled skills from builder.
|
|
134
|
+
# Skills stay in the builder copy because they require per-skill `bun install` runs.
|
|
137
135
|
COPY --from=builder /app /app
|
|
136
|
+
|
|
137
|
+
# Copy source separately to avoid invalidating builder layer.
|
|
138
|
+
COPY assistant ./
|
|
139
|
+
|
|
138
140
|
RUN chmod +x /app/assistant/docker-entrypoint.sh
|
|
139
141
|
|
|
140
142
|
# Run the daemon + http server
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// ── Mocks ────────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
let mockFeatureFlagEnabled = true;
|
|
6
|
+
|
|
7
|
+
mock.module("../../src/config/assistant-feature-flags.js", () => ({
|
|
8
|
+
isAssistantFeatureFlagEnabled: (_key: string, _config: unknown) =>
|
|
9
|
+
mockFeatureFlagEnabled,
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
mock.module("../../src/config/loader.js", () => ({
|
|
13
|
+
getConfig: () => ({}),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
// Track gatewayGet calls for assertion
|
|
17
|
+
const gatewayGetCalls: string[] = [];
|
|
18
|
+
let gatewayGetHandler: (path: string) => unknown = () => ({});
|
|
19
|
+
|
|
20
|
+
mock.module("../../src/runtime/gateway-internal-client.js", () => ({
|
|
21
|
+
GatewayRequestError: class GatewayRequestError extends Error {
|
|
22
|
+
statusCode: number;
|
|
23
|
+
gatewayError: string | undefined;
|
|
24
|
+
constructor(message: string, statusCode: number, gatewayError?: string) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "GatewayRequestError";
|
|
27
|
+
this.statusCode = statusCode;
|
|
28
|
+
this.gatewayError = gatewayError;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
gatewayGet: async <T>(path: string): Promise<T> => {
|
|
32
|
+
gatewayGetCalls.push(path);
|
|
33
|
+
return gatewayGetHandler(path) as T;
|
|
34
|
+
},
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
// Suppress logger output in tests
|
|
38
|
+
mock.module("../../src/util/logger.js", () => ({
|
|
39
|
+
getLogger: () => ({
|
|
40
|
+
warn: () => {},
|
|
41
|
+
info: () => {},
|
|
42
|
+
error: () => {},
|
|
43
|
+
debug: () => {},
|
|
44
|
+
}),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
import {
|
|
48
|
+
_clearGlobalCacheForTesting,
|
|
49
|
+
getAutoApproveThreshold,
|
|
50
|
+
} from "../../src/permissions/gateway-threshold-reader.js";
|
|
51
|
+
// Import GatewayRequestError from the mock so we can throw instances of it
|
|
52
|
+
const { GatewayRequestError } =
|
|
53
|
+
await import("../../src/runtime/gateway-internal-client.js");
|
|
54
|
+
|
|
55
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
function resetMocks(): void {
|
|
58
|
+
mockFeatureFlagEnabled = true;
|
|
59
|
+
gatewayGetCalls.length = 0;
|
|
60
|
+
gatewayGetHandler = () => ({});
|
|
61
|
+
_clearGlobalCacheForTesting();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
afterEach(resetMocks);
|
|
65
|
+
|
|
66
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
describe("getAutoApproveThreshold", () => {
|
|
69
|
+
test("returns undefined when feature flag is off", async () => {
|
|
70
|
+
mockFeatureFlagEnabled = false;
|
|
71
|
+
const result = await getAutoApproveThreshold("conv-123", "conversation");
|
|
72
|
+
expect(result).toBeUndefined();
|
|
73
|
+
// Should not make any gateway calls
|
|
74
|
+
expect(gatewayGetCalls).toHaveLength(0);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("returns global defaults when gateway returns them", async () => {
|
|
78
|
+
gatewayGetHandler = (path: string) => {
|
|
79
|
+
if (path === "/v1/permissions/thresholds") {
|
|
80
|
+
return {
|
|
81
|
+
interactive: "medium",
|
|
82
|
+
background: "low",
|
|
83
|
+
headless: "none",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// conversation maps to interactive
|
|
90
|
+
expect(await getAutoApproveThreshold(undefined, "conversation")).toBe(
|
|
91
|
+
"medium",
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
_clearGlobalCacheForTesting();
|
|
95
|
+
|
|
96
|
+
expect(await getAutoApproveThreshold(undefined, "background")).toBe("low");
|
|
97
|
+
|
|
98
|
+
_clearGlobalCacheForTesting();
|
|
99
|
+
|
|
100
|
+
expect(await getAutoApproveThreshold(undefined, "headless")).toBe("none");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("returns conversation override when it exists", async () => {
|
|
104
|
+
gatewayGetHandler = (path: string) => {
|
|
105
|
+
if (path === "/v1/permissions/thresholds/conversations/conv-xyz") {
|
|
106
|
+
return { threshold: "medium" };
|
|
107
|
+
}
|
|
108
|
+
if (path === "/v1/permissions/thresholds") {
|
|
109
|
+
return {
|
|
110
|
+
interactive: "low",
|
|
111
|
+
background: "medium",
|
|
112
|
+
headless: "none",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {};
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const result = await getAutoApproveThreshold("conv-xyz", "conversation");
|
|
119
|
+
expect(result).toBe("medium");
|
|
120
|
+
// Should have called the conversation endpoint, not the global one
|
|
121
|
+
expect(gatewayGetCalls).toEqual([
|
|
122
|
+
"/v1/permissions/thresholds/conversations/conv-xyz",
|
|
123
|
+
]);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("falls back to global when conversation override returns 404", async () => {
|
|
127
|
+
gatewayGetHandler = (path: string) => {
|
|
128
|
+
if (path.startsWith("/v1/permissions/thresholds/conversations/")) {
|
|
129
|
+
throw new GatewayRequestError("Not found", 404, "Not found");
|
|
130
|
+
}
|
|
131
|
+
if (path === "/v1/permissions/thresholds") {
|
|
132
|
+
return {
|
|
133
|
+
interactive: "low",
|
|
134
|
+
background: "medium",
|
|
135
|
+
headless: "none",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const result = await getAutoApproveThreshold("conv-123", "conversation");
|
|
142
|
+
expect(result).toBe("low");
|
|
143
|
+
// Should have called both endpoints
|
|
144
|
+
expect(gatewayGetCalls).toEqual([
|
|
145
|
+
"/v1/permissions/thresholds/conversations/conv-123",
|
|
146
|
+
"/v1/permissions/thresholds",
|
|
147
|
+
]);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("falls back to hardcoded defaults on gateway error", async () => {
|
|
151
|
+
gatewayGetHandler = () => {
|
|
152
|
+
throw new Error("Connection refused");
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// conversation → "low"
|
|
156
|
+
expect(await getAutoApproveThreshold(undefined, "conversation")).toBe(
|
|
157
|
+
"low",
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
_clearGlobalCacheForTesting();
|
|
161
|
+
|
|
162
|
+
// background → "medium"
|
|
163
|
+
expect(await getAutoApproveThreshold(undefined, "background")).toBe(
|
|
164
|
+
"medium",
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
_clearGlobalCacheForTesting();
|
|
168
|
+
|
|
169
|
+
// headless → "none"
|
|
170
|
+
expect(await getAutoApproveThreshold(undefined, "headless")).toBe("none");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("falls back to hardcoded defaults on non-404 conversation error", async () => {
|
|
174
|
+
gatewayGetHandler = (path: string) => {
|
|
175
|
+
if (path.startsWith("/v1/permissions/thresholds/conversations/")) {
|
|
176
|
+
throw new GatewayRequestError("Internal error", 500, "Server error");
|
|
177
|
+
}
|
|
178
|
+
// Should not reach global endpoint
|
|
179
|
+
return {
|
|
180
|
+
interactive: "medium",
|
|
181
|
+
background: "medium",
|
|
182
|
+
headless: "medium",
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const result = await getAutoApproveThreshold("conv-123", "conversation");
|
|
187
|
+
// Should fall back to hardcoded default for conversation, not global endpoint
|
|
188
|
+
expect(result).toBe("low");
|
|
189
|
+
// Should have only called the conversation endpoint
|
|
190
|
+
expect(gatewayGetCalls).toEqual([
|
|
191
|
+
"/v1/permissions/thresholds/conversations/conv-123",
|
|
192
|
+
]);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("caching: second call within 30s does not re-fetch global", async () => {
|
|
196
|
+
let fetchCount = 0;
|
|
197
|
+
gatewayGetHandler = (path: string) => {
|
|
198
|
+
if (path === "/v1/permissions/thresholds") {
|
|
199
|
+
fetchCount++;
|
|
200
|
+
return {
|
|
201
|
+
interactive: "medium",
|
|
202
|
+
background: "low",
|
|
203
|
+
headless: "none",
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return {};
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// First call — should fetch
|
|
210
|
+
const first = await getAutoApproveThreshold(undefined, "conversation");
|
|
211
|
+
expect(first).toBe("medium");
|
|
212
|
+
expect(fetchCount).toBe(1);
|
|
213
|
+
|
|
214
|
+
// Second call — should use cache
|
|
215
|
+
const second = await getAutoApproveThreshold(undefined, "background");
|
|
216
|
+
expect(second).toBe("low");
|
|
217
|
+
expect(fetchCount).toBe(1); // Still 1, cache hit
|
|
218
|
+
|
|
219
|
+
// Third call — still cached
|
|
220
|
+
const third = await getAutoApproveThreshold(undefined, "headless");
|
|
221
|
+
expect(third).toBe("none");
|
|
222
|
+
expect(fetchCount).toBe(1); // Still 1
|
|
223
|
+
|
|
224
|
+
// After clearing cache, should re-fetch
|
|
225
|
+
_clearGlobalCacheForTesting();
|
|
226
|
+
const fourth = await getAutoApproveThreshold(undefined, "conversation");
|
|
227
|
+
expect(fourth).toBe("medium");
|
|
228
|
+
expect(fetchCount).toBe(2); // Incremented
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("defaults executionContext to conversation when omitted", async () => {
|
|
232
|
+
gatewayGetHandler = (path: string) => {
|
|
233
|
+
if (path === "/v1/permissions/thresholds") {
|
|
234
|
+
return {
|
|
235
|
+
interactive: "medium",
|
|
236
|
+
background: "low",
|
|
237
|
+
headless: "none",
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return {};
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// executionContext omitted — should default to "conversation" → interactive
|
|
244
|
+
const result = await getAutoApproveThreshold(undefined, undefined);
|
|
245
|
+
expect(result).toBe("medium");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("skips conversation override when no conversationId", async () => {
|
|
249
|
+
gatewayGetHandler = (path: string) => {
|
|
250
|
+
if (path === "/v1/permissions/thresholds") {
|
|
251
|
+
return {
|
|
252
|
+
interactive: "low",
|
|
253
|
+
background: "medium",
|
|
254
|
+
headless: "none",
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return {};
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const result = await getAutoApproveThreshold(undefined, "conversation");
|
|
261
|
+
expect(result).toBe("low");
|
|
262
|
+
// Should only call global endpoint, not conversation
|
|
263
|
+
expect(gatewayGetCalls).toEqual(["/v1/permissions/thresholds"]);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("skips conversation override for non-conversation contexts", async () => {
|
|
267
|
+
gatewayGetHandler = (path: string) => {
|
|
268
|
+
if (path === "/v1/permissions/thresholds") {
|
|
269
|
+
return {
|
|
270
|
+
interactive: "low",
|
|
271
|
+
background: "medium",
|
|
272
|
+
headless: "none",
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return {};
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// Even with a conversationId, background context should not check conversation override
|
|
279
|
+
const result = await getAutoApproveThreshold("conv-123", "background");
|
|
280
|
+
expect(result).toBe("medium");
|
|
281
|
+
expect(gatewayGetCalls).toEqual(["/v1/permissions/thresholds"]);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -149,7 +149,7 @@ sequenceDiagram
|
|
|
149
149
|
| Decision | Rationale |
|
|
150
150
|
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
151
151
|
| PKCE by default, optional client_secret | Desktop apps prefer PKCE; some providers (Slack) require a secret, which is stored in the credential store (`oauth_app/{id}/client_secret`) for autonomous refresh |
|
|
152
|
-
| Shared connect orchestrator | All OAuth providers route through `orchestrateOAuthConnect()`, which resolves profiles,
|
|
152
|
+
| Shared connect orchestrator | All OAuth providers route through `orchestrateOAuthConnect()`, which resolves profiles, resolves scopes, runs the flow, stores tokens, and verifies identity. Adding a provider is a declarative profile entry, not new orchestration code |
|
|
153
153
|
| Canonical credential naming | All reads and writes use `client_id`/`client_secret` as canonical field names |
|
|
154
154
|
| Caller-driven callback transport | Transport (`loopback` or `gateway`) is chosen per-flow via the `callbackTransport` option on the connect API, defaulting to loopback. Desktop clients use loopback (no tunnel needed); web clients can pass `callback_transport: "gateway"`. Provider configuration no longer dictates transport. |
|
|
155
155
|
| Unified `MessagingProvider` interface | All platforms implement the same contract; generic tools work immediately for new providers |
|
|
@@ -161,34 +161,33 @@ sequenceDiagram
|
|
|
161
161
|
|
|
162
162
|
### Source Files
|
|
163
163
|
|
|
164
|
-
| File | Role
|
|
165
|
-
| ------------------------------------------------ |
|
|
166
|
-
| `assistant/src/security/oauth2.ts` | OAuth2 flow: PKCE or client_secret, Bun.serve callback, token exchange
|
|
167
|
-
| `assistant/src/security/token-manager.ts` | `withValidToken()` — auto-refresh, 401 retry, expiry buffer
|
|
168
|
-
| `assistant/src/messaging/provider.ts` | `MessagingProvider` interface
|
|
169
|
-
| `assistant/src/messaging/provider-types.ts` | Platform-agnostic types (Conversation, Message, SearchResult)
|
|
170
|
-
| `assistant/src/messaging/registry.ts` | Provider registry: register, lookup, list connected
|
|
171
|
-
| `assistant/src/messaging/style-analyzer.ts` | Writing style extraction from message corpus
|
|
172
|
-
| `assistant/src/messaging/draft-store.ts` | Local draft storage (platform/id JSON files)
|
|
173
|
-
| `assistant/src/messaging/providers/slack/` | Slack adapter, client, types
|
|
174
|
-
| `assistant/src/messaging/providers/gmail/` | Gmail adapter, client, types
|
|
175
|
-
| `assistant/src/config/bundled-skills/messaging/` | Core messaging skill (send, read, search, reply across platforms)
|
|
176
|
-
| `assistant/src/config/bundled-skills/sequences/` | Email sequence management skill (drip campaigns, enrollment, analytics)
|
|
177
|
-
| `assistant/src/watcher/providers/gmail.ts` | Gmail watcher using History API
|
|
178
|
-
| `assistant/src/watcher/providers/github.ts` | GitHub watcher for PRs, issues, review requests, and mentions
|
|
179
|
-
| `assistant/src/watcher/providers/linear.ts` | Linear watcher for assigned issues, status changes, and @mentions
|
|
180
|
-
| `assistant/src/oauth/seed-providers.ts` | Provider seed data: injection templates, identity config, setup metadata (seeded to DB on startup)
|
|
181
|
-
| `assistant/src/oauth/connect-orchestrator.ts` | Shared OAuth connect orchestrator: profile resolution, scope
|
|
182
|
-
| `assistant/src/oauth/
|
|
183
|
-
| `assistant/src/oauth/
|
|
184
|
-
| `assistant/src/oauth
|
|
185
|
-
| `assistant/src/daemon/handlers/oauth-connect.ts` | Generic OAuth connect handler (`oauth_connect_start` / `oauth_connect_result`) |
|
|
164
|
+
| File | Role |
|
|
165
|
+
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
|
|
166
|
+
| `assistant/src/security/oauth2.ts` | OAuth2 flow: PKCE or client_secret, Bun.serve callback, token exchange |
|
|
167
|
+
| `assistant/src/security/token-manager.ts` | `withValidToken()` — auto-refresh, 401 retry, expiry buffer |
|
|
168
|
+
| `assistant/src/messaging/provider.ts` | `MessagingProvider` interface |
|
|
169
|
+
| `assistant/src/messaging/provider-types.ts` | Platform-agnostic types (Conversation, Message, SearchResult) |
|
|
170
|
+
| `assistant/src/messaging/registry.ts` | Provider registry: register, lookup, list connected |
|
|
171
|
+
| `assistant/src/messaging/style-analyzer.ts` | Writing style extraction from message corpus |
|
|
172
|
+
| `assistant/src/messaging/draft-store.ts` | Local draft storage (platform/id JSON files) |
|
|
173
|
+
| `assistant/src/messaging/providers/slack/` | Slack adapter, client, types |
|
|
174
|
+
| `assistant/src/messaging/providers/gmail/` | Gmail adapter, client, types |
|
|
175
|
+
| `assistant/src/config/bundled-skills/messaging/` | Core messaging skill (send, read, search, reply across platforms) |
|
|
176
|
+
| `assistant/src/config/bundled-skills/sequences/` | Email sequence management skill (drip campaigns, enrollment, analytics) |
|
|
177
|
+
| `assistant/src/watcher/providers/gmail.ts` | Gmail watcher using History API |
|
|
178
|
+
| `assistant/src/watcher/providers/github.ts` | GitHub watcher for PRs, issues, review requests, and mentions |
|
|
179
|
+
| `assistant/src/watcher/providers/linear.ts` | Linear watcher for assigned issues, status changes, and @mentions |
|
|
180
|
+
| `assistant/src/oauth/seed-providers.ts` | Provider seed data: injection templates, identity config, setup metadata (seeded to DB on startup) |
|
|
181
|
+
| `assistant/src/oauth/connect-orchestrator.ts` | Shared OAuth connect orchestrator: profile resolution, scope resolution, flow execution, token storage |
|
|
182
|
+
| `assistant/src/oauth/connect-types.ts` | Shared types: `AvailableScopes`, `OAuthConnectResult` |
|
|
183
|
+
| `assistant/src/oauth/token-persistence.ts` | Token storage helper: persists tokens, metadata, and runs post-connect hooks |
|
|
184
|
+
| `assistant/src/daemon/handlers/oauth-connect.ts` | Generic OAuth connect handler (`oauth_connect_start` / `oauth_connect_result`) |
|
|
186
185
|
|
|
187
186
|
---
|
|
188
187
|
|
|
189
188
|
## OAuth Extensibility — DB-Driven Provider Config, Scope Policy, and Connect Orchestrator
|
|
190
189
|
|
|
191
|
-
The OAuth extensibility layer makes adding a new OAuth provider a fully declarative operation. All provider configuration — protocol fields (auth URLs, token URLs, scopes
|
|
190
|
+
The OAuth extensibility layer makes adding a new OAuth provider a fully declarative operation. All provider configuration — protocol fields (auth URLs, token URLs, scopes), behavioral fields (identity verification, injection templates, setup metadata), and display metadata — is stored in the `oauth_providers` SQLite table and seeded on startup via `seed-providers.ts`. The shared **connect orchestrator** handles the full flow from provider resolution through token storage.
|
|
192
191
|
|
|
193
192
|
### Provider Configuration (DB-Driven)
|
|
194
193
|
|
|
@@ -199,24 +198,19 @@ Each provider row includes:
|
|
|
199
198
|
| Column group | Fields | Purpose |
|
|
200
199
|
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
201
200
|
| **Protocol** | `authUrl`, `tokenUrl`, `tokenEndpointAuthMethod`, `extraParams`, `loopbackPort` | OAuth2 flow parameters |
|
|
202
|
-
| **Scopes** | `defaultScopes`, `
|
|
201
|
+
| **Scopes** | `defaultScopes`, `availableScopes` | Default scopes for connect flow; informational available scopes for assistant context |
|
|
203
202
|
| **Identity verification** | `identityUrl`, `identityMethod`, `identityHeaders`, `identityBody`, `identityResponsePaths`, `identityFormat`, `identityOkField` | Data-driven identity verifier fetches human-readable account info after token exchange |
|
|
204
203
|
| **Injection templates** | `injectionTemplates` | Auto-applied credential injection rules for the script proxy |
|
|
205
204
|
| **Setup metadata** | `displayName`, `description`, `dashboardUrl`, `appType`, `setupNotes`, `requiresClientSecret` | Metadata for the generic OAuth setup skill |
|
|
206
205
|
| **Ping config** | `pingUrl`, `pingMethod`, `pingHeaders`, `pingBody` | Health-check endpoint for `assistant oauth ping` |
|
|
207
206
|
|
|
208
|
-
### Scope
|
|
207
|
+
### Scope Resolution
|
|
209
208
|
|
|
210
|
-
|
|
209
|
+
Scope resolution is simple — no validation layer:
|
|
211
210
|
|
|
212
|
-
1. No requested scopes →
|
|
213
|
-
2. Requested scopes provided →
|
|
214
|
-
|
|
215
|
-
- Rejected if `allowAdditionalScopes` is `false`.
|
|
216
|
-
- Rejected if not in `allowedOptionalScopes`.
|
|
217
|
-
- Accepted otherwise, added to the union.
|
|
218
|
-
|
|
219
|
-
Returns `{ ok: true, scopes }` or `{ ok: false, error, allowedScopes }`.
|
|
211
|
+
1. No requested scopes → uses `defaultScopes`.
|
|
212
|
+
2. Requested scopes provided → uses the requested scopes directly (no validation — the OAuth provider rejects invalid scopes).
|
|
213
|
+
3. `availableScopes` is informational context surfaced via CLI for the assistant to consult.
|
|
220
214
|
|
|
221
215
|
### Connect Orchestrator
|
|
222
216
|
|
|
@@ -224,7 +218,7 @@ Returns `{ ok: true, scopes }` or `{ ok: false, error, allowedScopes }`.
|
|
|
224
218
|
|
|
225
219
|
1. **Receive canonical provider name** — the orchestrator receives the canonical provider name directly (e.g. `google`, `slack`).
|
|
226
220
|
2. **Load provider row** — reads all config from the `oauth_providers` DB table.
|
|
227
|
-
3. **Compute scopes** —
|
|
221
|
+
3. **Compute scopes** — uses requested scopes directly, or falls back to `defaultScopes`.
|
|
228
222
|
4. **Build OAuth config** — assemble protocol-level config from the DB provider row.
|
|
229
223
|
5. **Run flow** — interactive (opens browser, blocks until completion) or deferred (returns auth URL for the caller to deliver).
|
|
230
224
|
6. **Verify identity** — runs the generic data-driven identity verifier using the provider row's identity columns.
|
|
@@ -246,7 +240,7 @@ This replaces provider-specific handlers — any provider in the registry can be
|
|
|
246
240
|
### Adding a New OAuth Provider
|
|
247
241
|
|
|
248
242
|
1. **Add seed data** to `PROVIDER_SEED_DATA` in `assistant/src/oauth/seed-providers.ts`:
|
|
249
|
-
- Set protocol fields: `authUrl`, `tokenUrl`, `defaultScopes`, `
|
|
243
|
+
- Set protocol fields: `authUrl`, `tokenUrl`, `defaultScopes`, `availableScopes`, `loopbackPort`.
|
|
250
244
|
- Set identity verification: `identityUrl`, `identityMethod`, `identityHeaders`, `identityResponsePaths`, `identityFormat`.
|
|
251
245
|
- Set injection templates: `injectionTemplates` for providers whose tokens should be auto-injected by the script proxy.
|
|
252
246
|
- Set setup metadata: `displayName`, `dashboardUrl`, `appType` enable the generic OAuth setup skill to guide users through app creation.
|
|
@@ -259,9 +253,8 @@ This replaces provider-specific handlers — any provider in the registry can be
|
|
|
259
253
|
| File | Role |
|
|
260
254
|
| ------------------------------------------------ | --------------------------------------------------------------------------- |
|
|
261
255
|
| `assistant/src/oauth/seed-providers.ts` | Provider seed data and startup seeding |
|
|
262
|
-
| `assistant/src/oauth/scope-policy.ts` | Scope resolution and policy enforcement (pure, no I/O) |
|
|
263
256
|
| `assistant/src/oauth/connect-orchestrator.ts` | Shared connect orchestrator (profile → scopes → flow → tokens) |
|
|
264
|
-
| `assistant/src/oauth/connect-types.ts` | Shared types (`
|
|
257
|
+
| `assistant/src/oauth/connect-types.ts` | Shared types (`AvailableScopes`, `OAuthConnectResult`) |
|
|
265
258
|
| `assistant/src/oauth/token-persistence.ts` | Token storage: credential store writes, metadata upsert, post-connect hooks |
|
|
266
259
|
| `assistant/src/oauth/identity-verifier.ts` | Generic data-driven identity verifier (reads config from provider DB row) |
|
|
267
260
|
| `assistant/src/daemon/handlers/oauth-connect.ts` | Generic `oauth_connect_start` / `oauth_connect_result` handler |
|