@vellumai/assistant 0.4.46 → 0.4.49
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/ARCHITECTURE.md +7 -7
- package/README.md +2 -23
- package/docs/architecture/integrations.md +45 -41
- package/docs/architecture/keychain-broker.md +3 -3
- package/docs/architecture/security.md +5 -5
- package/docs/runbook-trusted-contacts.md +3 -8
- package/hook-templates/debug-prompt-logger/hook.json +1 -1
- package/hook-templates/debug-prompt-logger/run.sh +1 -3
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +0 -1
- package/src/__tests__/anthropic-provider.test.ts +156 -0
- package/src/__tests__/approval-cascade.test.ts +810 -0
- package/src/__tests__/approval-primitive.test.ts +0 -1
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +12 -34
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
- package/src/__tests__/browser-fill-credential.test.ts +5 -2
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
- package/src/__tests__/channel-guardian.test.ts +0 -2
- package/src/__tests__/channel-readiness-routes.test.ts +35 -25
- package/src/__tests__/channel-readiness-service.test.ts +10 -9
- package/src/__tests__/checker.test.ts +9 -29
- package/src/__tests__/cli.test.ts +23 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-tools.test.ts +2 -19
- package/src/__tests__/config-watcher.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-image-dimensions.test.ts +332 -0
- package/src/__tests__/context-token-estimator.test.ts +196 -13
- package/src/__tests__/conversation-attention-store.test.ts +0 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +144 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
- package/src/__tests__/credential-broker-server-use.test.ts +22 -21
- package/src/__tests__/credential-broker.test.ts +2 -1
- package/src/__tests__/credential-metadata-store.test.ts +239 -26
- package/src/__tests__/credential-resolve.test.ts +5 -4
- package/src/__tests__/credential-security-e2e.test.ts +8 -8
- package/src/__tests__/credential-security-invariants.test.ts +111 -7
- package/src/__tests__/credential-vault-unit.test.ts +287 -54
- package/src/__tests__/credential-vault.test.ts +406 -12
- package/src/__tests__/credentials-cli.test.ts +82 -6
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-image-service.test.ts +75 -45
- package/src/__tests__/gemini-provider.test.ts +9 -6
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
- package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
- package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
- package/src/__tests__/guardian-grant-minting.test.ts +35 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
- package/src/__tests__/heartbeat-service.test.ts +0 -1
- package/src/__tests__/host-cu-proxy.test.ts +629 -0
- package/src/__tests__/host-shell-tool.test.ts +27 -15
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/ingress-url-consistency.test.ts +14 -21
- package/src/__tests__/integration-status.test.ts +38 -25
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +10 -9
- package/src/__tests__/keychain-broker-client.test.ts +11 -43
- package/src/__tests__/managed-proxy-context.test.ts +5 -3
- package/src/__tests__/media-generate-image.test.ts +63 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
- package/src/__tests__/messaging-send-tool.test.ts +4 -6
- package/src/__tests__/notification-routing-intent.test.ts +0 -1
- package/src/__tests__/oauth-cli.test.ts +373 -14
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/oauth-store.test.ts +756 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +0 -1
- package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
- package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +15 -21
- package/src/__tests__/recording-handler.test.ts +3 -4
- package/src/__tests__/registry.test.ts +2 -2
- package/src/__tests__/runtime-events-sse.test.ts +55 -7
- package/src/__tests__/schedule-store.test.ts +0 -1
- package/src/__tests__/scheduler-recurrence.test.ts +0 -1
- package/src/__tests__/schema-transforms.test.ts +226 -0
- package/src/__tests__/scoped-approval-grants.test.ts +0 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
- package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
- package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
- package/src/__tests__/secret-ingress-handler.test.ts +0 -1
- package/src/__tests__/secret-onetime-send.test.ts +5 -3
- package/src/__tests__/send-endpoint-busy.test.ts +21 -6
- package/src/__tests__/sequence-store.test.ts +0 -1
- package/src/__tests__/session-init.benchmark.test.ts +4 -5
- package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
- package/src/__tests__/skill-include-graph.test.ts +66 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-load-tool.test.ts +149 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skills-uninstall.test.ts +3 -3
- package/src/__tests__/skills.test.ts +3 -12
- package/src/__tests__/slack-channel-config.test.ts +76 -11
- package/src/__tests__/slack-share-routes.test.ts +17 -14
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
- package/src/__tests__/terminal-tools.test.ts +4 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
- package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
- package/src/__tests__/trust-store.test.ts +1 -22
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +2 -1
- package/src/__tests__/twilio-provider.test.ts +4 -2
- package/src/__tests__/twilio-routes.test.ts +5 -20
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/agent/ax-tree-compaction.test.ts +235 -0
- package/src/agent/loop.ts +76 -130
- package/src/calls/call-domain.ts +8 -10
- package/src/calls/relay-server.ts +9 -13
- package/src/calls/twilio-config.ts +4 -8
- package/src/calls/twilio-provider.ts +2 -1
- package/src/calls/twilio-rest.ts +2 -1
- package/src/calls/twilio-routes.ts +1 -2
- package/src/calls/voice-ingress-preflight.ts +1 -1
- package/src/cli/commands/browser-relay.ts +46 -15
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/credentials.ts +110 -23
- package/src/cli/commands/oauth/apps.ts +255 -0
- package/src/cli/commands/oauth/connections.ts +299 -0
- package/src/cli/commands/oauth/index.ts +52 -0
- package/src/cli/commands/oauth/providers.ts +242 -0
- package/src/cli/commands/skills.ts +4 -338
- package/src/cli/program.ts +1 -5
- package/src/cli/reference.ts +1 -3
- package/src/cli.ts +3 -2
- package/src/config/assistant-feature-flags.ts +0 -3
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
- package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +22 -4
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
- package/src/config/bundled-skills/gmail/SKILL.md +4 -4
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
- package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +90 -44
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
- package/src/config/bundled-skills/messaging/SKILL.md +6 -6
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +12 -15
- package/src/config/bundled-skills/settings/SKILL.md +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +2 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
- package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
- package/src/config/env-registry.ts +14 -83
- package/src/config/env.ts +11 -50
- package/src/config/feature-flag-registry.json +16 -16
- package/src/config/schema.ts +3 -1
- package/src/config/skills.ts +21 -2
- package/src/context/image-dimensions.ts +229 -0
- package/src/context/token-estimator.ts +75 -12
- package/src/context/window-manager.ts +49 -10
- package/src/daemon/assistant-attachments.ts +1 -13
- package/src/daemon/guardian-action-generators.ts +4 -5
- package/src/daemon/handlers/config-ingress.ts +8 -33
- package/src/daemon/handlers/config-slack-channel.ts +76 -56
- package/src/daemon/handlers/config-telegram.ts +53 -24
- package/src/daemon/handlers/sessions.ts +10 -24
- package/src/daemon/handlers/shared.ts +0 -130
- package/src/daemon/host-cu-proxy.ts +401 -0
- package/src/daemon/lifecycle.ts +39 -63
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/computer-use.ts +2 -119
- package/src/daemon/message-types/host-cu.ts +19 -0
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/message-types/messages.ts +3 -0
- package/src/daemon/server.ts +14 -21
- package/src/daemon/session-agent-loop-handlers.ts +2 -0
- package/src/daemon/session-attachments.ts +1 -2
- package/src/daemon/session-messaging.ts +3 -1
- package/src/daemon/session-slash.ts +1 -1
- package/src/daemon/session-surfaces.ts +40 -28
- package/src/daemon/session-tool-setup.ts +20 -11
- package/src/daemon/session.ts +139 -16
- package/src/daemon/tool-side-effects.ts +2 -8
- package/src/daemon/watch-handler.ts +2 -2
- package/src/email/providers/index.ts +2 -1
- package/src/events/tool-metrics-listener.ts +2 -2
- package/src/hooks/manager.ts +1 -4
- package/src/inbound/public-ingress-urls.ts +7 -7
- package/src/instrument.ts +15 -1
- package/src/logfire.ts +16 -5
- package/src/media/app-icon-generator.ts +30 -4
- package/src/media/avatar-router.ts +26 -3
- package/src/media/gemini-image-service.ts +28 -2
- package/src/memory/conversation-key-store.ts +21 -0
- package/src/memory/db-init.ts +4 -0
- package/src/memory/guardian-action-store.ts +1 -1
- package/src/memory/migrations/149-oauth-tables.ts +60 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema/guardian.ts +1 -1
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/oauth.ts +65 -0
- package/src/messaging/provider.ts +19 -13
- package/src/messaging/providers/gmail/adapter.ts +40 -23
- package/src/messaging/providers/gmail/client.ts +283 -122
- package/src/messaging/providers/gmail/people-client.ts +32 -24
- package/src/messaging/providers/slack/adapter.ts +29 -19
- package/src/messaging/providers/slack/client.ts +265 -78
- package/src/messaging/providers/telegram-bot/adapter.ts +19 -18
- package/src/messaging/providers/whatsapp/adapter.ts +17 -11
- package/src/messaging/registry.ts +2 -31
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/signal.ts +4 -5
- package/src/oauth/byo-connection.test.ts +537 -0
- package/src/oauth/byo-connection.ts +128 -0
- package/src/oauth/connect-orchestrator.ts +139 -56
- package/src/oauth/connect-types.ts +17 -23
- package/src/oauth/connection-resolver.ts +58 -0
- package/src/oauth/connection.ts +38 -0
- package/src/oauth/manual-token-connection.ts +104 -0
- package/src/oauth/oauth-store.ts +496 -0
- package/src/oauth/platform-connection.test.ts +192 -0
- package/src/oauth/platform-connection.ts +111 -0
- package/src/oauth/provider-behaviors.ts +124 -0
- package/src/oauth/scope-policy.ts +9 -2
- package/src/oauth/seed-providers.ts +161 -0
- package/src/oauth/token-persistence.ts +74 -78
- package/src/permissions/checker.ts +8 -4
- package/src/permissions/defaults.ts +0 -1
- package/src/permissions/prompter.ts +10 -1
- package/src/permissions/trust-store.ts +13 -0
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
- package/src/prompts/system-prompt.ts +70 -45
- package/src/providers/anthropic/client.ts +133 -24
- package/src/providers/gemini/client.ts +15 -6
- package/src/providers/managed-proxy/constants.ts +2 -2
- package/src/providers/managed-proxy/context.ts +5 -1
- package/src/providers/ratelimit.ts +17 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -27
- package/src/runtime/AGENTS.md +17 -0
- package/src/runtime/auth/route-policy.ts +0 -3
- package/src/runtime/channel-invite-transports/telegram.ts +2 -1
- package/src/runtime/channel-readiness-service.ts +168 -195
- package/src/runtime/channel-readiness-types.ts +4 -0
- package/src/runtime/channel-reply-delivery.ts +0 -40
- package/src/runtime/gateway-client.ts +0 -7
- package/src/runtime/guardian-action-conversation-turn.ts +1 -3
- package/src/runtime/guardian-action-followup-executor.ts +1 -1
- package/src/runtime/guardian-action-message-composer.ts +3 -23
- package/src/runtime/http-server.ts +17 -10
- package/src/runtime/http-types.ts +2 -3
- package/src/runtime/middleware/rate-limiter.ts +74 -20
- package/src/runtime/middleware/twilio-validation.ts +1 -11
- package/src/runtime/pending-interactions.ts +14 -12
- package/src/runtime/routes/channel-delivery-routes.ts +0 -1
- package/src/runtime/routes/channel-readiness-routes.ts +2 -0
- package/src/runtime/routes/conversation-routes.ts +73 -19
- package/src/runtime/routes/diagnostics-routes.ts +11 -9
- package/src/runtime/routes/events-routes.ts +21 -11
- package/src/runtime/routes/guardian-approval-interception.ts +20 -5
- package/src/runtime/routes/host-cu-routes.ts +97 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
- package/src/runtime/routes/integrations/slack/share.ts +6 -6
- package/src/runtime/routes/integrations/twilio.ts +6 -5
- package/src/runtime/routes/log-export-routes.ts +126 -8
- package/src/runtime/routes/secret-routes.ts +3 -2
- package/src/runtime/routes/settings-routes.ts +113 -48
- package/src/runtime/routes/surface-action-routes.ts +1 -1
- package/src/runtime/routes/watch-routes.ts +128 -0
- package/src/schedule/integration-status.ts +10 -8
- package/src/security/credential-key.ts +14 -0
- package/src/security/keychain-broker-client.ts +5 -6
- package/src/security/oauth2.ts +1 -1
- package/src/security/token-manager.ts +145 -43
- package/src/skills/catalog-install.ts +358 -0
- package/src/skills/include-graph.ts +32 -0
- package/src/telegram/bot-username.ts +2 -3
- package/src/tools/apps/definitions.ts +0 -5
- package/src/tools/assets/materialize.ts +0 -5
- package/src/tools/assets/search.ts +0 -5
- package/src/tools/browser/headless-browser.ts +1 -67
- package/src/tools/browser/network-recorder.ts +1 -1
- package/src/tools/browser/network-recording-types.ts +1 -1
- package/src/tools/claude-code/claude-code.ts +0 -5
- package/src/tools/computer-use/definitions.ts +46 -11
- package/src/tools/computer-use/registry.ts +4 -5
- package/src/tools/credentials/broker.ts +5 -4
- package/src/tools/credentials/metadata-store.ts +22 -74
- package/src/tools/credentials/resolve.ts +2 -1
- package/src/tools/credentials/vault.ts +139 -151
- package/src/tools/filesystem/edit.ts +1 -6
- package/src/tools/filesystem/read.ts +0 -5
- package/src/tools/filesystem/write.ts +1 -6
- package/src/tools/host-filesystem/edit.ts +1 -6
- package/src/tools/host-filesystem/read.ts +1 -6
- package/src/tools/host-filesystem/write.ts +1 -6
- package/src/tools/mcp/mcp-tool-factory.ts +18 -1
- package/src/tools/memory/definitions.ts +0 -5
- package/src/tools/network/web-fetch.ts +0 -5
- package/src/tools/network/web-search.ts +0 -5
- package/src/tools/registry.ts +2 -7
- package/src/tools/schema-transforms.ts +99 -0
- package/src/tools/skills/load.ts +62 -8
- package/src/tools/swarm/delegate.ts +0 -5
- package/src/tools/system/avatar-generator.ts +0 -5
- package/src/tools/ui-surface/definitions.ts +0 -15
- package/src/tools/watch/screen-watch.ts +0 -5
- package/src/tools/watch/watch-state.ts +0 -12
- package/src/util/logger.ts +7 -41
- package/src/util/platform.ts +9 -28
- package/src/version.ts +10 -0
- package/src/watcher/providers/github.ts +51 -52
- package/src/watcher/providers/gmail.ts +88 -80
- package/src/watcher/providers/google-calendar.ts +94 -86
- package/src/watcher/providers/linear.ts +87 -93
- package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
- package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
- package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
- package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
- package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
- package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
- package/src/cli/commands/dev.ts +0 -129
- package/src/cli/commands/map.ts +0 -391
- package/src/cli/commands/oauth.ts +0 -77
- package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +0 -16
- package/src/daemon/computer-use-session.ts +0 -1020
- package/src/daemon/ride-shotgun-handler.ts +0 -567
- package/src/oauth/provider-profiles.ts +0 -192
- package/src/prompts/computer-use-prompt.ts +0 -98
- package/src/runtime/routes/computer-use-routes.ts +0 -641
- package/src/runtime/telegram-streaming-delivery.test.ts +0 -597
- package/src/runtime/telegram-streaming-delivery.ts +0 -383
- package/src/tools/computer-use/request-computer-control.ts +0 -61
|
@@ -118,6 +118,7 @@ function makeIdleSession(opts?: {
|
|
|
118
118
|
updateClient: () => {},
|
|
119
119
|
setHostBashProxy: () => {},
|
|
120
120
|
setHostFileProxy: () => {},
|
|
121
|
+
setHostCuProxy: () => {},
|
|
121
122
|
enqueueMessage: () => ({ queued: false, requestId: "noop" }),
|
|
122
123
|
hasAnyPendingConfirmation: () => false,
|
|
123
124
|
runAgentLoop: async (
|
|
@@ -181,6 +182,7 @@ function makeConfirmationEmittingSession(opts?: {
|
|
|
181
182
|
updateClient: () => {},
|
|
182
183
|
setHostBashProxy: () => {},
|
|
183
184
|
setHostFileProxy: () => {},
|
|
185
|
+
setHostCuProxy: () => {},
|
|
184
186
|
enqueueMessage: () => ({ queued: false, requestId: "noop" }),
|
|
185
187
|
hasAnyPendingConfirmation: () => false,
|
|
186
188
|
runAgentLoop: async (
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
estimateBase64Bytes,
|
|
11
11
|
inferMimeType,
|
|
12
12
|
MAX_ASSISTANT_ATTACHMENT_BYTES,
|
|
13
|
-
MAX_ASSISTANT_ATTACHMENTS,
|
|
14
13
|
validateDrafts,
|
|
15
14
|
} from "../daemon/assistant-attachments.js";
|
|
16
15
|
|
|
@@ -163,33 +162,27 @@ describe("validateDrafts", () => {
|
|
|
163
162
|
expect(result.warnings[0]).toContain("exceeds");
|
|
164
163
|
});
|
|
165
164
|
|
|
166
|
-
test("
|
|
167
|
-
const drafts = Array.from(
|
|
168
|
-
{
|
|
169
|
-
(_, i) => makeDraft({ filename: `file-${i}.txt` }),
|
|
165
|
+
test("accepts many drafts without count limit", () => {
|
|
166
|
+
const drafts = Array.from({ length: 20 }, (_, i) =>
|
|
167
|
+
makeDraft({ filename: `file-${i}.txt` }),
|
|
170
168
|
);
|
|
171
169
|
const result = validateDrafts(drafts);
|
|
172
|
-
expect(result.accepted).toHaveLength(
|
|
173
|
-
expect(result.warnings).toHaveLength(
|
|
174
|
-
expect(result.warnings[0]).toContain(
|
|
175
|
-
`file-${MAX_ASSISTANT_ATTACHMENTS}.txt`,
|
|
176
|
-
);
|
|
177
|
-
expect(result.warnings[0]).toContain("exceeded maximum");
|
|
170
|
+
expect(result.accepted).toHaveLength(20);
|
|
171
|
+
expect(result.warnings).toHaveLength(0);
|
|
178
172
|
});
|
|
179
173
|
|
|
180
|
-
test("rejects oversized
|
|
174
|
+
test("rejects oversized while accepting all valid drafts", () => {
|
|
181
175
|
const drafts = [
|
|
182
176
|
makeDraft({
|
|
183
177
|
filename: "big.bin",
|
|
184
178
|
sizeBytes: MAX_ASSISTANT_ATTACHMENT_BYTES + 1,
|
|
185
179
|
}),
|
|
186
|
-
...Array.from({ length:
|
|
180
|
+
...Array.from({ length: 10 }, (_, i) =>
|
|
187
181
|
makeDraft({ filename: `ok-${i}.txt` }),
|
|
188
182
|
),
|
|
189
183
|
];
|
|
190
184
|
const result = validateDrafts(drafts);
|
|
191
|
-
|
|
192
|
-
expect(result.accepted).toHaveLength(MAX_ASSISTANT_ATTACHMENTS);
|
|
185
|
+
expect(result.accepted).toHaveLength(10);
|
|
193
186
|
expect(result.warnings).toHaveLength(1);
|
|
194
187
|
expect(result.warnings[0]).toContain("big.bin");
|
|
195
188
|
});
|
|
@@ -431,11 +424,8 @@ describe("contentBlocksToDrafts", () => {
|
|
|
431
424
|
// ---------------------------------------------------------------------------
|
|
432
425
|
|
|
433
426
|
describe("validateDrafts with reversed tool drafts", () => {
|
|
434
|
-
test("
|
|
435
|
-
|
|
436
|
-
// After reversing, the most recent (highest index) should appear first
|
|
437
|
-
// and win the MAX_ASSISTANT_ATTACHMENTS cap.
|
|
438
|
-
const totalScreenshots = MAX_ASSISTANT_ATTACHMENTS + 3;
|
|
427
|
+
test("all tool screenshots accepted after reversing", () => {
|
|
428
|
+
const totalScreenshots = 8;
|
|
439
429
|
const toolDrafts = Array.from({ length: totalScreenshots }, (_, i) =>
|
|
440
430
|
makeDraft({
|
|
441
431
|
sourceType: "tool_block",
|
|
@@ -446,23 +436,11 @@ describe("validateDrafts with reversed tool drafts", () => {
|
|
|
446
436
|
}),
|
|
447
437
|
);
|
|
448
438
|
|
|
449
|
-
// Reverse to prioritize most recent
|
|
450
439
|
toolDrafts.reverse();
|
|
451
440
|
|
|
452
441
|
const result = validateDrafts(toolDrafts);
|
|
453
|
-
expect(result.accepted).toHaveLength(
|
|
454
|
-
|
|
455
|
-
// The accepted drafts should be the most recent screenshots (highest step numbers)
|
|
456
|
-
const acceptedFilenames = result.accepted.map((d) => d.filename);
|
|
457
|
-
for (let i = 0; i < MAX_ASSISTANT_ATTACHMENTS; i++) {
|
|
458
|
-
expect(acceptedFilenames[i]).toBe(
|
|
459
|
-
`screenshot-step-${totalScreenshots - 1 - i}.png`,
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// The oldest screenshots should be dropped
|
|
464
|
-
expect(result.warnings).toHaveLength(3);
|
|
465
|
-
expect(result.warnings[0]).toContain("screenshot-step-2.png");
|
|
442
|
+
expect(result.accepted).toHaveLength(totalScreenshots);
|
|
443
|
+
expect(result.warnings).toHaveLength(0);
|
|
466
444
|
});
|
|
467
445
|
});
|
|
468
446
|
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* `isAssistantFeatureFlagEnabled('<key>', ...)` in production code must be
|
|
11
11
|
* declared in the unified registry. This keeps flag usage declarative while
|
|
12
12
|
* allowing skills to exist without corresponding feature flags.
|
|
13
|
+
*
|
|
14
|
+
* 3. Indirect key coverage: all `feature_flags.<id>.enabled` string literals
|
|
15
|
+
* anywhere in production code (maps, constants, variables, etc.) must be
|
|
16
|
+
* declared in the unified registry. This catches indirect key patterns that
|
|
17
|
+
* Guard 2 would miss, such as flag keys stored in lookup maps or constants.
|
|
13
18
|
*/
|
|
14
19
|
|
|
15
20
|
import { execSync } from "node:child_process";
|
|
@@ -197,3 +202,74 @@ describe("assistant feature flag declaration coverage guard", () => {
|
|
|
197
202
|
}
|
|
198
203
|
});
|
|
199
204
|
});
|
|
205
|
+
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Guard 3: Indirect key coverage — flag key literals anywhere in production code
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
describe("assistant feature flag indirect key coverage guard", () => {
|
|
211
|
+
test("all feature_flags.<id>.enabled string literals in production code are declared in the unified registry", () => {
|
|
212
|
+
const repoRoot = getRepoRoot();
|
|
213
|
+
|
|
214
|
+
// Load the unified registry and extract all declared keys (any scope)
|
|
215
|
+
const registry = loadRegistry();
|
|
216
|
+
const declaredKeys = new Set(registry.flags.map((f) => f.key));
|
|
217
|
+
|
|
218
|
+
// Search for any string literal matching the canonical key pattern
|
|
219
|
+
// in production .ts files under assistant/src/ and gateway/src/.
|
|
220
|
+
// This catches keys in maps, constants, variables, or any other
|
|
221
|
+
// indirect patterns that Guard 2 would miss.
|
|
222
|
+
let grepOutput = "";
|
|
223
|
+
try {
|
|
224
|
+
grepOutput = execSync(
|
|
225
|
+
`git grep -nE "feature_flags\\.[a-z0-9_-]+\\.enabled\\b" -- 'assistant/src/**/*.ts' 'gateway/src/**/*.ts'`,
|
|
226
|
+
{ encoding: "utf-8", cwd: repoRoot },
|
|
227
|
+
).trim();
|
|
228
|
+
} catch (err) {
|
|
229
|
+
// Exit code 1 means no matches — happy path
|
|
230
|
+
if ((err as { status?: number }).status === 1) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const keyPattern = /feature_flags\.[a-z0-9_-]+\.enabled\b/g;
|
|
237
|
+
const undeclared: string[] = [];
|
|
238
|
+
|
|
239
|
+
for (const line of grepOutput.split("\n")) {
|
|
240
|
+
if (!line) continue;
|
|
241
|
+
|
|
242
|
+
// Format: "file:line:content"
|
|
243
|
+
const colonIdx = line.indexOf(":");
|
|
244
|
+
if (colonIdx === -1) continue;
|
|
245
|
+
const filePath = line.slice(0, colonIdx);
|
|
246
|
+
|
|
247
|
+
// Skip test files
|
|
248
|
+
if (isTestFile(filePath)) continue;
|
|
249
|
+
|
|
250
|
+
// Extract all key occurrences from this line
|
|
251
|
+
const content = line.slice(colonIdx + 1);
|
|
252
|
+
for (const match of content.matchAll(keyPattern)) {
|
|
253
|
+
const key = match[0];
|
|
254
|
+
if (!declaredKeys.has(key)) {
|
|
255
|
+
undeclared.push(`${filePath}: ${key}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (undeclared.length > 0) {
|
|
261
|
+
const message = [
|
|
262
|
+
"Found feature_flags.<id>.enabled string literals in production code that are NOT declared in the unified registry.",
|
|
263
|
+
"This catches indirect flag key usage (maps, constants, variables) that the direct-call guard misses.",
|
|
264
|
+
`Registry: meta/feature-flags/feature-flag-registry.json`,
|
|
265
|
+
"",
|
|
266
|
+
"Undeclared keys:",
|
|
267
|
+
...undeclared.map((k) => ` - ${k}`),
|
|
268
|
+
"",
|
|
269
|
+
"To fix: add the missing key(s) to the unified registry, or remove the stale reference.",
|
|
270
|
+
].join("\n");
|
|
271
|
+
|
|
272
|
+
expect(undeclared, message).toEqual([]);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -76,6 +76,7 @@ mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
|
76
76
|
_setMetadataPath: () => {},
|
|
77
77
|
}));
|
|
78
78
|
|
|
79
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
79
80
|
import { executeBrowserFillCredential } from "../tools/browser/browser-execution.js";
|
|
80
81
|
import type { ToolContext } from "../tools/types.js";
|
|
81
82
|
|
|
@@ -142,7 +143,9 @@ describe("executeBrowserFillCredential", () => {
|
|
|
142
143
|
'[data-vellum-eid="e1"]',
|
|
143
144
|
"super-secret-password",
|
|
144
145
|
);
|
|
145
|
-
expect(mockGetSecureKey).toHaveBeenCalledWith(
|
|
146
|
+
expect(mockGetSecureKey).toHaveBeenCalledWith(
|
|
147
|
+
credentialKey("gmail", "password"),
|
|
148
|
+
);
|
|
146
149
|
});
|
|
147
150
|
|
|
148
151
|
test("fills credential by CSS selector", async () => {
|
|
@@ -297,7 +300,7 @@ describe("executeBrowserFillCredential", () => {
|
|
|
297
300
|
"password",
|
|
298
301
|
);
|
|
299
302
|
expect(mockGetSecureKey).toHaveBeenCalledWith(
|
|
300
|
-
"
|
|
303
|
+
credentialKey("gmail", "password"),
|
|
301
304
|
);
|
|
302
305
|
});
|
|
303
306
|
|
|
@@ -55,10 +55,10 @@ describe("browser skill cutover — startup tool payload", () => {
|
|
|
55
55
|
const definitions = getAllToolDefinitions();
|
|
56
56
|
const serialized = JSON.stringify(definitions);
|
|
57
57
|
// Startup payload is ~22 000 chars without browser tools.
|
|
58
|
-
// Floor at
|
|
58
|
+
// Floor at 14 000 catches accidental wholesale removal; ceiling at 35 000
|
|
59
59
|
// gives headroom while still catching browser tool leakage
|
|
60
60
|
// (~4 640 chars would push it past the ceiling).
|
|
61
|
-
expect(serialized.length).toBeGreaterThan(
|
|
61
|
+
expect(serialized.length).toBeGreaterThan(14_000);
|
|
62
62
|
expect(serialized.length).toBeLessThan(35_000);
|
|
63
63
|
});
|
|
64
64
|
|
|
@@ -90,7 +90,7 @@ const GATEWAY_RETRIEVAL_BANLIST: Array<{
|
|
|
90
90
|
bannedSnippets: [
|
|
91
91
|
'curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/',
|
|
92
92
|
"security find-generic-password",
|
|
93
|
-
"secret-tool lookup service vellum-assistant account credential
|
|
93
|
+
"secret-tool lookup service vellum-assistant account credential/ngrok/authtoken",
|
|
94
94
|
],
|
|
95
95
|
},
|
|
96
96
|
{
|
|
@@ -119,6 +119,7 @@ const KEYCHAIN_ALLOWLIST = new Set<string>([
|
|
|
119
119
|
const KEYCHAIN_PATTERNS = [
|
|
120
120
|
"security find-generic-password",
|
|
121
121
|
"secret-tool lookup service vellum-assistant account credential:",
|
|
122
|
+
"secret-tool lookup service vellum-assistant account credential/",
|
|
122
123
|
];
|
|
123
124
|
|
|
124
125
|
const HOST_BASH_RETRIEVAL_ALLOWLIST = new Set<string>([
|
|
@@ -1305,8 +1305,6 @@ function createMockCtx(): {
|
|
|
1305
1305
|
let captured: ChannelVerificationSessionResponse | null = null;
|
|
1306
1306
|
const ctx = {
|
|
1307
1307
|
sessions: new Map(),
|
|
1308
|
-
cuSessions: new Map(),
|
|
1309
|
-
cuObservationParseSequence: new Map(),
|
|
1310
1308
|
sharedRequestTimestamps: [],
|
|
1311
1309
|
debounceTimers: {
|
|
1312
1310
|
schedule: () => {},
|
|
@@ -11,7 +11,6 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
11
11
|
// Mocks — must be set up before importing the service
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
|
|
14
|
-
let mockTwilioPhoneNumberEnv: string | undefined;
|
|
15
14
|
let mockRawConfig: Record<string, unknown> | undefined;
|
|
16
15
|
let mockSecureKeys: Record<string, string>;
|
|
17
16
|
let mockHasTwilioCredentials: boolean;
|
|
@@ -23,16 +22,27 @@ mock.module("../calls/twilio-rest.js", () => ({
|
|
|
23
22
|
getTollFreeVerificationStatus: async () => null,
|
|
24
23
|
}));
|
|
25
24
|
|
|
26
|
-
mock.module("../config/env.js", () => ({
|
|
27
|
-
getTwilioPhoneNumberEnv: () => mockTwilioPhoneNumberEnv,
|
|
28
|
-
}));
|
|
25
|
+
mock.module("../config/env.js", () => ({}));
|
|
29
26
|
|
|
30
27
|
mock.module("../config/loader.js", () => ({
|
|
31
28
|
loadRawConfig: () => mockRawConfig,
|
|
29
|
+
loadConfig: () => {
|
|
30
|
+
const raw = mockRawConfig ?? {};
|
|
31
|
+
const wa = (raw.whatsapp ?? {}) as Record<string, unknown>;
|
|
32
|
+
const tw = (raw.twilio ?? {}) as Record<string, unknown>;
|
|
33
|
+
return {
|
|
34
|
+
twilio: { phoneNumber: (tw.phoneNumber as string) ?? "" },
|
|
35
|
+
whatsapp: { phoneNumber: (wa.phoneNumber as string) ?? "" },
|
|
36
|
+
};
|
|
37
|
+
},
|
|
32
38
|
getConfig: () => {
|
|
33
39
|
const raw = mockRawConfig ?? {};
|
|
34
40
|
const wa = (raw.whatsapp ?? {}) as Record<string, unknown>;
|
|
35
|
-
|
|
41
|
+
const tw = (raw.twilio ?? {}) as Record<string, unknown>;
|
|
42
|
+
return {
|
|
43
|
+
twilio: { phoneNumber: (tw.phoneNumber as string) ?? "" },
|
|
44
|
+
whatsapp: { phoneNumber: (wa.phoneNumber as string) ?? "" },
|
|
45
|
+
};
|
|
36
46
|
},
|
|
37
47
|
invalidateConfigCache: () => {},
|
|
38
48
|
}));
|
|
@@ -52,6 +62,7 @@ mock.module("../email/service.js", () => ({
|
|
|
52
62
|
// ---------------------------------------------------------------------------
|
|
53
63
|
|
|
54
64
|
import { createReadinessService } from "../runtime/channel-readiness-service.js";
|
|
65
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
55
66
|
|
|
56
67
|
// ---------------------------------------------------------------------------
|
|
57
68
|
// Tests
|
|
@@ -59,7 +70,6 @@ import { createReadinessService } from "../runtime/channel-readiness-service.js"
|
|
|
59
70
|
|
|
60
71
|
describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
61
72
|
beforeEach(() => {
|
|
62
|
-
mockTwilioPhoneNumberEnv = undefined;
|
|
63
73
|
mockRawConfig = undefined;
|
|
64
74
|
mockSecureKeys = {};
|
|
65
75
|
mockHasTwilioCredentials = false;
|
|
@@ -197,10 +207,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
197
207
|
|
|
198
208
|
test("reports ready when all Meta credentials and display number are configured", async () => {
|
|
199
209
|
mockSecureKeys = {
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"
|
|
210
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
211
|
+
[credentialKey("whatsapp", "access_token")]: "EAAxxxxxx",
|
|
212
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
213
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
204
214
|
};
|
|
205
215
|
mockRawConfig = {
|
|
206
216
|
whatsapp: { phoneNumber: "+15551234567" },
|
|
@@ -215,10 +225,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
215
225
|
|
|
216
226
|
test("reports not ready when display phone number is missing", async () => {
|
|
217
227
|
mockSecureKeys = {
|
|
218
|
-
"
|
|
219
|
-
"
|
|
220
|
-
"
|
|
221
|
-
"
|
|
228
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
229
|
+
[credentialKey("whatsapp", "access_token")]: "EAAxxxxxx",
|
|
230
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
231
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
222
232
|
};
|
|
223
233
|
mockRawConfig = {
|
|
224
234
|
ingress: { publicBaseUrl: "https://example.com", enabled: true },
|
|
@@ -236,10 +246,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
236
246
|
|
|
237
247
|
test("checks each Meta credential individually", async () => {
|
|
238
248
|
mockSecureKeys = {
|
|
239
|
-
"
|
|
249
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
240
250
|
// access_token missing
|
|
241
|
-
"
|
|
242
|
-
"
|
|
251
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
252
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
243
253
|
};
|
|
244
254
|
mockRawConfig = {
|
|
245
255
|
ingress: { publicBaseUrl: "https://example.com", enabled: true },
|
|
@@ -272,10 +282,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
272
282
|
|
|
273
283
|
test("checks invite policy", async () => {
|
|
274
284
|
mockSecureKeys = {
|
|
275
|
-
"
|
|
276
|
-
"
|
|
277
|
-
"
|
|
278
|
-
"
|
|
285
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
286
|
+
[credentialKey("whatsapp", "access_token")]: "EAAxxxxxx",
|
|
287
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
288
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
279
289
|
};
|
|
280
290
|
mockRawConfig = {
|
|
281
291
|
whatsapp: { phoneNumber: "+15551234567" },
|
|
@@ -293,10 +303,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
293
303
|
|
|
294
304
|
test("checks ingress configuration", async () => {
|
|
295
305
|
mockSecureKeys = {
|
|
296
|
-
"
|
|
297
|
-
"
|
|
298
|
-
"
|
|
299
|
-
"
|
|
306
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
307
|
+
[credentialKey("whatsapp", "access_token")]: "EAAxxxxxx",
|
|
308
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
309
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
300
310
|
};
|
|
301
311
|
mockRawConfig = {
|
|
302
312
|
whatsapp: { phoneNumber: "+15551234567" },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
let
|
|
3
|
+
let mockTwilioPhoneNumber: string | undefined;
|
|
4
4
|
let mockRawConfig: Record<string, unknown> | undefined;
|
|
5
5
|
let mockSecureKeys: Record<string, string>;
|
|
6
6
|
let mockHasTwilioCredentials: boolean;
|
|
@@ -27,16 +27,17 @@ mock.module("../channels/config.js", () => ({
|
|
|
27
27
|
}),
|
|
28
28
|
}));
|
|
29
29
|
|
|
30
|
-
mock.module("../config/env.js", () => ({
|
|
31
|
-
getTwilioPhoneNumberEnv: () => mockTwilioPhoneNumberEnv,
|
|
32
|
-
}));
|
|
30
|
+
mock.module("../config/env.js", () => ({}));
|
|
33
31
|
|
|
34
32
|
mock.module("../config/loader.js", () => ({
|
|
35
33
|
loadRawConfig: () => mockRawConfig,
|
|
34
|
+
loadConfig: () => ({
|
|
35
|
+
twilio: { phoneNumber: mockTwilioPhoneNumber ?? "" },
|
|
36
|
+
whatsapp: { phoneNumber: "" },
|
|
37
|
+
}),
|
|
36
38
|
getConfig: () => ({
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
39
|
+
twilio: { phoneNumber: mockTwilioPhoneNumber ?? "" },
|
|
40
|
+
whatsapp: { phoneNumber: "" },
|
|
40
41
|
}),
|
|
41
42
|
}));
|
|
42
43
|
|
|
@@ -100,7 +101,7 @@ describe("ChannelReadinessService", () => {
|
|
|
100
101
|
|
|
101
102
|
beforeEach(() => {
|
|
102
103
|
service = new ChannelReadinessService();
|
|
103
|
-
|
|
104
|
+
mockTwilioPhoneNumber = undefined;
|
|
104
105
|
mockRawConfig = undefined;
|
|
105
106
|
mockSecureKeys = {};
|
|
106
107
|
mockHasTwilioCredentials = false;
|
|
@@ -406,7 +407,7 @@ describe("ChannelReadinessService", () => {
|
|
|
406
407
|
|
|
407
408
|
test("voice readiness includes gateway_health when ingress is configured", async () => {
|
|
408
409
|
mockHasTwilioCredentials = true;
|
|
409
|
-
|
|
410
|
+
mockTwilioPhoneNumber = "+15550001111";
|
|
410
411
|
mockRawConfig = {
|
|
411
412
|
ingress: {
|
|
412
413
|
enabled: true,
|
|
@@ -135,9 +135,7 @@ registerTool(mockBundledSkillTool);
|
|
|
135
135
|
// Register CU tools so classifyRisk returns their declared Low risk level
|
|
136
136
|
// instead of falling through to Medium (unknown tool).
|
|
137
137
|
import { registerComputerUseActionTools } from "../tools/computer-use/registry.js";
|
|
138
|
-
import { requestComputerControlTool } from "../tools/computer-use/request-computer-control.js";
|
|
139
138
|
registerComputerUseActionTools();
|
|
140
|
-
registerTool(requestComputerControlTool);
|
|
141
139
|
|
|
142
140
|
function writeSkill(
|
|
143
141
|
skillId: string,
|
|
@@ -673,14 +671,14 @@ describe("Permission Checker", () => {
|
|
|
673
671
|
expect(result.decision).toBe("allow");
|
|
674
672
|
});
|
|
675
673
|
|
|
676
|
-
test("file_write within workspace with no rule →
|
|
674
|
+
test("file_write within workspace with no rule → prompt (medium risk bypasses workspace auto-allow)", async () => {
|
|
677
675
|
const result = await check(
|
|
678
676
|
"file_write",
|
|
679
677
|
{ path: "/tmp/file.txt" },
|
|
680
678
|
"/tmp",
|
|
681
679
|
);
|
|
682
|
-
expect(result.decision).toBe("
|
|
683
|
-
expect(result.reason).toContain("
|
|
680
|
+
expect(result.decision).toBe("prompt");
|
|
681
|
+
expect(result.reason).toContain("medium risk");
|
|
684
682
|
});
|
|
685
683
|
|
|
686
684
|
test("file_write outside workspace with no rule → prompt", async () => {
|
|
@@ -897,19 +895,6 @@ describe("Permission Checker", () => {
|
|
|
897
895
|
);
|
|
898
896
|
});
|
|
899
897
|
|
|
900
|
-
test("computer_use_request_control prompts by default via computer-use ask rule", async () => {
|
|
901
|
-
const result = await check(
|
|
902
|
-
"computer_use_request_control",
|
|
903
|
-
{ task: "Open system settings" },
|
|
904
|
-
"/tmp",
|
|
905
|
-
);
|
|
906
|
-
expect(result.decision).toBe("prompt");
|
|
907
|
-
expect(result.reason).toContain("ask rule");
|
|
908
|
-
expect(result.matchedRule?.id).toBe(
|
|
909
|
-
"default:ask-computer_use_request_control-global",
|
|
910
|
-
);
|
|
911
|
-
});
|
|
912
|
-
|
|
913
898
|
test("higher-priority allow rule can override default computer-use ask rule", async () => {
|
|
914
899
|
addRule(
|
|
915
900
|
"computer_use_click",
|
|
@@ -4407,11 +4392,6 @@ describe("computer-use tool permission defaults", () => {
|
|
|
4407
4392
|
expect(risk).toBe(RiskLevel.Low);
|
|
4408
4393
|
}
|
|
4409
4394
|
});
|
|
4410
|
-
|
|
4411
|
-
test("computer_use_request_control classifies as Low risk", async () => {
|
|
4412
|
-
const risk = await classifyRisk("computer_use_request_control", {});
|
|
4413
|
-
expect(risk).toBe(RiskLevel.Low);
|
|
4414
|
-
});
|
|
4415
4395
|
});
|
|
4416
4396
|
|
|
4417
4397
|
// ---------------------------------------------------------------------------
|
|
@@ -4588,24 +4568,24 @@ describe("workspace mode — auto-allow workspace-scoped operations", () => {
|
|
|
4588
4568
|
expect(result.reason).toContain("Workspace mode");
|
|
4589
4569
|
});
|
|
4590
4570
|
|
|
4591
|
-
test("file_write within workspace →
|
|
4571
|
+
test("file_write within workspace → prompt (Medium risk bypasses workspace auto-allow)", async () => {
|
|
4592
4572
|
const result = await check(
|
|
4593
4573
|
"file_write",
|
|
4594
4574
|
{ file_path: "/home/user/my-project/src/index.ts" },
|
|
4595
4575
|
workspaceDir,
|
|
4596
4576
|
);
|
|
4597
|
-
expect(result.decision).toBe("
|
|
4598
|
-
expect(result.reason).toContain("
|
|
4577
|
+
expect(result.decision).toBe("prompt");
|
|
4578
|
+
expect(result.reason).toContain("medium risk");
|
|
4599
4579
|
});
|
|
4600
4580
|
|
|
4601
|
-
test("file_edit within workspace →
|
|
4581
|
+
test("file_edit within workspace → prompt (Medium risk bypasses workspace auto-allow)", async () => {
|
|
4602
4582
|
const result = await check(
|
|
4603
4583
|
"file_edit",
|
|
4604
4584
|
{ file_path: "/home/user/my-project/src/index.ts" },
|
|
4605
4585
|
workspaceDir,
|
|
4606
4586
|
);
|
|
4607
|
-
expect(result.decision).toBe("
|
|
4608
|
-
expect(result.reason).toContain("
|
|
4587
|
+
expect(result.decision).toBe("prompt");
|
|
4588
|
+
expect(result.reason).toContain("medium risk");
|
|
4609
4589
|
});
|
|
4610
4590
|
|
|
4611
4591
|
// ── file operations outside workspace follow risk-based fallback ──
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { describe, expect, test } from "bun:test";
|
|
2
3
|
|
|
3
4
|
import {
|
|
@@ -66,3 +67,25 @@ describe("formatConfirmationCommandPreview", () => {
|
|
|
66
67
|
expect(preview).toBe("edit /tmp/sample.txt");
|
|
67
68
|
});
|
|
68
69
|
});
|
|
70
|
+
|
|
71
|
+
const UUID_RE =
|
|
72
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
73
|
+
|
|
74
|
+
describe("new-session conversationKey format", () => {
|
|
75
|
+
test("uses a valid UUID, not a timestamp", () => {
|
|
76
|
+
// Mirror the key construction in startCli()
|
|
77
|
+
const key = `builtin-cli:${randomUUID()}`;
|
|
78
|
+
const suffix = key.replace("builtin-cli:", "");
|
|
79
|
+
|
|
80
|
+
expect(suffix).toMatch(UUID_RE);
|
|
81
|
+
// A numeric timestamp would parse to a finite number; a UUID must not.
|
|
82
|
+
expect(Number.isFinite(Number(suffix))).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("generates unique keys across calls", () => {
|
|
86
|
+
const key1 = `builtin-cli:${randomUUID()}`;
|
|
87
|
+
const key2 = `builtin-cli:${randomUUID()}`;
|
|
88
|
+
|
|
89
|
+
expect(key1).not.toBe(key2);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -29,7 +29,7 @@ const manifestPath = resolve(
|
|
|
29
29
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
30
30
|
|
|
31
31
|
describe("computer-use skill manifest regression", () => {
|
|
32
|
-
test("manifest has exactly
|
|
32
|
+
test("manifest has exactly 11 tools", () => {
|
|
33
33
|
expect(manifest.tools).toHaveLength(COMPUTER_USE_TOOL_COUNT);
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
computerUseTypeTextTool,
|
|
14
14
|
computerUseWaitTool,
|
|
15
15
|
} from "../tools/computer-use/definitions.js";
|
|
16
|
-
import { requestComputerControlTool } from "../tools/computer-use/request-computer-control.js";
|
|
17
16
|
import { forwardComputerUseProxyTool } from "../tools/computer-use/skill-proxy-bridge.js";
|
|
18
17
|
import type { ToolContext } from "../tools/types.js";
|
|
19
18
|
|
|
@@ -40,22 +39,20 @@ const ctx: ToolContext = {
|
|
|
40
39
|
// ── Tool definitions ────────────────────────────────────────────────
|
|
41
40
|
|
|
42
41
|
describe("computer-use tool definitions", () => {
|
|
43
|
-
test("allComputerUseTools contains
|
|
44
|
-
expect(allComputerUseTools.length).toBe(
|
|
42
|
+
test("allComputerUseTools contains 11 tools", () => {
|
|
43
|
+
expect(allComputerUseTools.length).toBe(11);
|
|
45
44
|
});
|
|
46
45
|
|
|
47
46
|
test("all tools have proxy execution mode", () => {
|
|
48
47
|
for (const tool of allComputerUseTools) {
|
|
49
48
|
expect(tool.executionMode).toBe("proxy");
|
|
50
49
|
}
|
|
51
|
-
expect(requestComputerControlTool.executionMode).toBe("proxy");
|
|
52
50
|
});
|
|
53
51
|
|
|
54
52
|
test("all tools belong to computer-use category", () => {
|
|
55
53
|
for (const tool of allComputerUseTools) {
|
|
56
54
|
expect(tool.category).toBe("computer-use");
|
|
57
55
|
}
|
|
58
|
-
expect(requestComputerControlTool.category).toBe("computer-use");
|
|
59
56
|
});
|
|
60
57
|
|
|
61
58
|
test("all tools have unique names", () => {
|
|
@@ -225,20 +222,6 @@ describe("computer_use_respond", () => {
|
|
|
225
222
|
});
|
|
226
223
|
});
|
|
227
224
|
|
|
228
|
-
// ── request_computer_control ────────────────────────────────────────
|
|
229
|
-
|
|
230
|
-
describe("computer_use_request_control", () => {
|
|
231
|
-
test("requires task parameter", () => {
|
|
232
|
-
expect(schema(requestComputerControlTool).required).toContain("task");
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
test("execute throws proxy error", () => {
|
|
236
|
-
expect(() => requestComputerControlTool.execute({}, ctx)).toThrow(
|
|
237
|
-
"surfaceProxyResolver",
|
|
238
|
-
);
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
225
|
// ── skill-proxy-bridge ──────────────────────────────────────────────
|
|
243
226
|
|
|
244
227
|
describe("forwardComputerUseProxyTool", () => {
|