@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
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scope resolution and policy enforcement for OAuth providers.
|
|
3
|
-
*
|
|
4
|
-
* Pure module (no side effects, no I/O) that resolves the final set of
|
|
5
|
-
* scopes for an OAuth flow based on the provider profile's scope policy.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { OAuthScopePolicy } from "./connect-types.js";
|
|
9
|
-
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
// Result types
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
export type ScopeResolutionResult =
|
|
15
|
-
| { ok: true; scopes: string[] }
|
|
16
|
-
| { ok: false; error: string; allowedScopes?: string[] };
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Implementation
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Resolve the final set of scopes for an OAuth flow.
|
|
24
|
-
*
|
|
25
|
-
* - If `requestedScopes` is undefined or empty, returns the provider's
|
|
26
|
-
* `defaultScopes`.
|
|
27
|
-
* - Otherwise, starts with `defaultScopes` and validates each additional
|
|
28
|
-
* requested scope against the provider's `scopePolicy`.
|
|
29
|
-
* - Returns a deduplicated union of default + approved requested scopes.
|
|
30
|
-
*/
|
|
31
|
-
/** Minimal shape needed by the scope resolver. */
|
|
32
|
-
export interface ScopeResolverInput {
|
|
33
|
-
service: string;
|
|
34
|
-
defaultScopes: string[];
|
|
35
|
-
scopePolicy: OAuthScopePolicy;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function resolveScopes(
|
|
39
|
-
profile: ScopeResolverInput,
|
|
40
|
-
requestedScopes?: string[],
|
|
41
|
-
): ScopeResolutionResult {
|
|
42
|
-
const { defaultScopes, scopePolicy, service } = profile;
|
|
43
|
-
|
|
44
|
-
// No requested scopes — use defaults
|
|
45
|
-
if (!requestedScopes || requestedScopes.length === 0) {
|
|
46
|
-
return { ok: true, scopes: [...defaultScopes] };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const defaultSet = new Set(defaultScopes);
|
|
50
|
-
const finalScopes = new Set(defaultScopes);
|
|
51
|
-
|
|
52
|
-
for (const scope of requestedScopes) {
|
|
53
|
-
// Already in defaults — no policy check needed
|
|
54
|
-
if (defaultSet.has(scope)) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Check forbidden list first
|
|
59
|
-
if (scopePolicy.forbiddenScopes.includes(scope)) {
|
|
60
|
-
return {
|
|
61
|
-
ok: false,
|
|
62
|
-
error: `Scope '${scope}' is forbidden for ${service}`,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Additional scopes not allowed at all
|
|
67
|
-
if (!scopePolicy.allowAdditionalScopes) {
|
|
68
|
-
return {
|
|
69
|
-
ok: false,
|
|
70
|
-
error: `Additional scopes are not allowed for ${service}. Only the default scopes may be used.`,
|
|
71
|
-
allowedScopes: [...defaultScopes],
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Additional scopes allowed, but this one isn't in the optional list
|
|
76
|
-
if (!scopePolicy.allowedOptionalScopes.includes(scope)) {
|
|
77
|
-
return {
|
|
78
|
-
ok: false,
|
|
79
|
-
error: `Scope '${scope}' is not in the allowed optional scopes for ${service}`,
|
|
80
|
-
allowedScopes: [...defaultScopes, ...scopePolicy.allowedOptionalScopes],
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Passed all checks — include it
|
|
85
|
-
finalScopes.add(scope);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return { ok: true, scopes: [...finalScopes] };
|
|
89
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { getGatewayInternalBaseUrl } from "../config/env.js";
|
|
2
|
-
import { mintEdgeRelayToken } from "./auth/token-service.js";
|
|
3
|
-
|
|
4
|
-
export class GatewayRequestError extends Error {
|
|
5
|
-
statusCode: number;
|
|
6
|
-
gatewayError: string | undefined;
|
|
7
|
-
|
|
8
|
-
constructor(
|
|
9
|
-
message: string,
|
|
10
|
-
statusCode: number,
|
|
11
|
-
gatewayError: string | undefined,
|
|
12
|
-
) {
|
|
13
|
-
super(message);
|
|
14
|
-
this.name = "GatewayRequestError";
|
|
15
|
-
this.statusCode = statusCode;
|
|
16
|
-
this.gatewayError = gatewayError;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Parse a non-ok gateway response into a human-readable error message.
|
|
22
|
-
* Matches the existing pattern in contact tools: try JSON parse, extract
|
|
23
|
-
* `.error` string, fall back to raw body text, fall back to status code.
|
|
24
|
-
*/
|
|
25
|
-
async function parseErrorResponse(
|
|
26
|
-
resp: Response,
|
|
27
|
-
): Promise<GatewayRequestError> {
|
|
28
|
-
const body = await resp.text();
|
|
29
|
-
let gatewayError: string | undefined;
|
|
30
|
-
let message = `Gateway request failed (${resp.status})`;
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const parsed = JSON.parse(body) as {
|
|
34
|
-
error?: string | { code?: string; message?: string };
|
|
35
|
-
};
|
|
36
|
-
if (parsed.error) {
|
|
37
|
-
// Runtime httpError() returns { error: { code, message } } while
|
|
38
|
-
// gateway returns { error: "string" }. Handle both formats.
|
|
39
|
-
const errStr =
|
|
40
|
-
typeof parsed.error === "string"
|
|
41
|
-
? parsed.error
|
|
42
|
-
: (parsed.error.message ?? JSON.stringify(parsed.error));
|
|
43
|
-
gatewayError = errStr;
|
|
44
|
-
message = errStr;
|
|
45
|
-
}
|
|
46
|
-
} catch {
|
|
47
|
-
if (body) message = body;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return new GatewayRequestError(message, resp.status, gatewayError);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export async function gatewayGet<T>(path: string): Promise<T> {
|
|
54
|
-
const baseUrl = getGatewayInternalBaseUrl();
|
|
55
|
-
const token = mintEdgeRelayToken();
|
|
56
|
-
|
|
57
|
-
const resp = await fetch(`${baseUrl}${path}`, {
|
|
58
|
-
method: "GET",
|
|
59
|
-
headers: {
|
|
60
|
-
Accept: "application/json",
|
|
61
|
-
Authorization: `Bearer ${token}`,
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
if (!resp.ok) {
|
|
66
|
-
throw await parseErrorResponse(resp);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return (await resp.json()) as T;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export async function gatewayPost<T>(
|
|
73
|
-
path: string,
|
|
74
|
-
body: unknown,
|
|
75
|
-
): Promise<{ status: number; data: T }> {
|
|
76
|
-
const baseUrl = getGatewayInternalBaseUrl();
|
|
77
|
-
const token = mintEdgeRelayToken();
|
|
78
|
-
|
|
79
|
-
const resp = await fetch(`${baseUrl}${path}`, {
|
|
80
|
-
method: "POST",
|
|
81
|
-
headers: {
|
|
82
|
-
"Content-Type": "application/json",
|
|
83
|
-
Authorization: `Bearer ${token}`,
|
|
84
|
-
},
|
|
85
|
-
body: JSON.stringify(body),
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
if (!resp.ok) {
|
|
89
|
-
throw await parseErrorResponse(resp);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const data = (await resp.json()) as T;
|
|
93
|
-
return { status: resp.status, data };
|
|
94
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP route handler for watch (ambient observation) functionality.
|
|
3
|
-
*
|
|
4
|
-
* Decoupled from computer-use routes so that the watch endpoint has
|
|
5
|
-
* zero dependency on CU session state.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
|
|
10
|
-
import { getLogger } from "../../util/logger.js";
|
|
11
|
-
import { httpError } from "../http-errors.js";
|
|
12
|
-
import type { RouteDefinition } from "../http-router.js";
|
|
13
|
-
|
|
14
|
-
const log = getLogger("watch-routes");
|
|
15
|
-
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Dependency injection interface
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Minimal interface for watch observation handling.
|
|
22
|
-
* The daemon wires a concrete implementation at startup.
|
|
23
|
-
*/
|
|
24
|
-
export interface WatchDeps {
|
|
25
|
-
/** Handle a watch observation. */
|
|
26
|
-
handleWatchObservation: (params: {
|
|
27
|
-
watchId: string;
|
|
28
|
-
conversationId: string;
|
|
29
|
-
ocrText: string;
|
|
30
|
-
appName?: string;
|
|
31
|
-
windowTitle?: string;
|
|
32
|
-
bundleIdentifier?: string;
|
|
33
|
-
timestamp: number;
|
|
34
|
-
captureIndex: number;
|
|
35
|
-
totalExpected: number;
|
|
36
|
-
}) => Promise<void>;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
// Route handler
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* POST /v1/computer-use/watch — send a watch observation.
|
|
45
|
-
*
|
|
46
|
-
* Body: { watchId, conversationId, ocrText, appName?, windowTitle?,
|
|
47
|
-
* bundleIdentifier?, timestamp, captureIndex, totalExpected }
|
|
48
|
-
*/
|
|
49
|
-
async function handleWatchObservationRoute(
|
|
50
|
-
req: Request,
|
|
51
|
-
deps: WatchDeps,
|
|
52
|
-
): Promise<Response> {
|
|
53
|
-
const body = (await req.json()) as {
|
|
54
|
-
watchId?: string;
|
|
55
|
-
conversationId?: string;
|
|
56
|
-
ocrText?: string;
|
|
57
|
-
appName?: string;
|
|
58
|
-
windowTitle?: string;
|
|
59
|
-
bundleIdentifier?: string;
|
|
60
|
-
timestamp?: number;
|
|
61
|
-
captureIndex?: number;
|
|
62
|
-
totalExpected?: number;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
if (!body.watchId || typeof body.watchId !== "string") {
|
|
66
|
-
return httpError("BAD_REQUEST", "watchId is required", 400);
|
|
67
|
-
}
|
|
68
|
-
if (!body.conversationId || typeof body.conversationId !== "string") {
|
|
69
|
-
return httpError("BAD_REQUEST", "conversationId is required", 400);
|
|
70
|
-
}
|
|
71
|
-
if (!body.ocrText || typeof body.ocrText !== "string") {
|
|
72
|
-
return httpError("BAD_REQUEST", "ocrText is required", 400);
|
|
73
|
-
}
|
|
74
|
-
if (typeof body.timestamp !== "number") {
|
|
75
|
-
return httpError("BAD_REQUEST", "timestamp is required", 400);
|
|
76
|
-
}
|
|
77
|
-
if (typeof body.captureIndex !== "number") {
|
|
78
|
-
return httpError("BAD_REQUEST", "captureIndex is required", 400);
|
|
79
|
-
}
|
|
80
|
-
if (typeof body.totalExpected !== "number") {
|
|
81
|
-
return httpError("BAD_REQUEST", "totalExpected is required", 400);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
await deps.handleWatchObservation({
|
|
86
|
-
watchId: body.watchId,
|
|
87
|
-
conversationId: body.conversationId,
|
|
88
|
-
ocrText: body.ocrText,
|
|
89
|
-
appName: body.appName,
|
|
90
|
-
windowTitle: body.windowTitle,
|
|
91
|
-
bundleIdentifier: body.bundleIdentifier,
|
|
92
|
-
timestamp: body.timestamp,
|
|
93
|
-
captureIndex: body.captureIndex,
|
|
94
|
-
totalExpected: body.totalExpected,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
return Response.json({ ok: true });
|
|
98
|
-
} catch (err) {
|
|
99
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
100
|
-
log.error(
|
|
101
|
-
{ err, watchId: body.watchId },
|
|
102
|
-
"Failed to handle watch observation via HTTP",
|
|
103
|
-
);
|
|
104
|
-
return httpError("INTERNAL_ERROR", message, 500);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
// Route definitions
|
|
110
|
-
// ---------------------------------------------------------------------------
|
|
111
|
-
|
|
112
|
-
export function watchRouteDefinitions(deps: {
|
|
113
|
-
getWatchDeps?: () => WatchDeps;
|
|
114
|
-
}): RouteDefinition[] {
|
|
115
|
-
const getDeps = (): WatchDeps => {
|
|
116
|
-
if (!deps.getWatchDeps) {
|
|
117
|
-
throw new Error("Watch deps not available");
|
|
118
|
-
}
|
|
119
|
-
return deps.getWatchDeps();
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
return [
|
|
123
|
-
{
|
|
124
|
-
endpoint: "computer-use/watch",
|
|
125
|
-
method: "POST",
|
|
126
|
-
policyKey: "computer-use/watch",
|
|
127
|
-
summary: "Submit watch observation",
|
|
128
|
-
description: "Send a screen observation from ambient watch mode.",
|
|
129
|
-
tags: ["computer-use"],
|
|
130
|
-
handler: async ({ req }) => handleWatchObservationRoute(req, getDeps()),
|
|
131
|
-
requestBody: z.object({
|
|
132
|
-
watchId: z.string().describe("Watch session ID"),
|
|
133
|
-
conversationId: z.string().describe("Conversation to associate with"),
|
|
134
|
-
ocrText: z.string().describe("OCR text from screen capture"),
|
|
135
|
-
appName: z.string().describe("Active application name").optional(),
|
|
136
|
-
windowTitle: z.string().describe("Active window title").optional(),
|
|
137
|
-
bundleIdentifier: z
|
|
138
|
-
.string()
|
|
139
|
-
.describe("Application bundle identifier")
|
|
140
|
-
.optional(),
|
|
141
|
-
timestamp: z.number().describe("Capture timestamp (epoch ms)"),
|
|
142
|
-
captureIndex: z
|
|
143
|
-
.number()
|
|
144
|
-
.int()
|
|
145
|
-
.describe("Index of this capture in the batch"),
|
|
146
|
-
totalExpected: z
|
|
147
|
-
.number()
|
|
148
|
-
.int()
|
|
149
|
-
.describe("Total captures expected in the batch"),
|
|
150
|
-
}),
|
|
151
|
-
responseBody: z.object({
|
|
152
|
-
ok: z.boolean(),
|
|
153
|
-
}),
|
|
154
|
-
},
|
|
155
|
-
];
|
|
156
|
-
}
|
package/src/signals/shotgun.ts
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handle shotgun (screen-watch) signals delivered via signal files from the CLI.
|
|
3
|
-
*
|
|
4
|
-
* Each invocation writes JSON to a unique `signals/shotgun.<requestId>` file.
|
|
5
|
-
* ConfigWatcher detects the new file and invokes {@link handleShotgunSignal},
|
|
6
|
-
* which reads the payload, creates or queries a watch session, and writes
|
|
7
|
-
* the result to `signals/shotgun.<requestId>.result` for the CLI to pick up.
|
|
8
|
-
*
|
|
9
|
-
* Supports two actions:
|
|
10
|
-
* - `start`: Create a new watch session and return the watchId/conversationId.
|
|
11
|
-
* - `status`: Query the status of an existing watch session by watchId.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import crypto from "node:crypto";
|
|
15
|
-
import { readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
16
|
-
import { join } from "node:path";
|
|
17
|
-
|
|
18
|
-
import { getIsContainerized } from "../config/env-registry.js";
|
|
19
|
-
import {
|
|
20
|
-
fireWatchCompletionNotifier,
|
|
21
|
-
fireWatchStartNotifier,
|
|
22
|
-
type WatchSession,
|
|
23
|
-
watchSessions,
|
|
24
|
-
} from "../tools/watch/watch-state.js";
|
|
25
|
-
import { getLogger } from "../util/logger.js";
|
|
26
|
-
import { getSignalsDir } from "../util/platform.js";
|
|
27
|
-
|
|
28
|
-
const log = getLogger("signal:shotgun");
|
|
29
|
-
|
|
30
|
-
const SHORT_HASH_LENGTH = 8;
|
|
31
|
-
|
|
32
|
-
interface ShotgunResult {
|
|
33
|
-
requestId: string;
|
|
34
|
-
ok: boolean;
|
|
35
|
-
error?: string;
|
|
36
|
-
watchId?: string;
|
|
37
|
-
conversationId?: string;
|
|
38
|
-
status?: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function writeResult(requestId: string, result: ShotgunResult): void {
|
|
42
|
-
try {
|
|
43
|
-
writeFileSync(
|
|
44
|
-
join(getSignalsDir(), `shotgun.${requestId}.result`),
|
|
45
|
-
JSON.stringify(result),
|
|
46
|
-
);
|
|
47
|
-
} catch (err) {
|
|
48
|
-
log.error({ err, requestId }, "Failed to write shotgun signal result");
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Read a `signals/shotgun.<requestId>` file, process the action, and write
|
|
54
|
-
* the result to `signals/shotgun.<requestId>.result`. Called by ConfigWatcher
|
|
55
|
-
* when a matching signal file is created or modified.
|
|
56
|
-
*/
|
|
57
|
-
export function handleShotgunSignal(filename: string): void {
|
|
58
|
-
if (getIsContainerized()) return;
|
|
59
|
-
|
|
60
|
-
const signalPath = join(getSignalsDir(), filename);
|
|
61
|
-
let raw: string;
|
|
62
|
-
try {
|
|
63
|
-
raw = readFileSync(signalPath, "utf-8");
|
|
64
|
-
} catch {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
unlinkSync(signalPath);
|
|
70
|
-
} catch {
|
|
71
|
-
// Best-effort cleanup.
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let payload: {
|
|
75
|
-
requestId?: string;
|
|
76
|
-
action?: string;
|
|
77
|
-
durationSeconds?: number;
|
|
78
|
-
intervalSeconds?: number;
|
|
79
|
-
focusArea?: string;
|
|
80
|
-
watchId?: string;
|
|
81
|
-
};
|
|
82
|
-
try {
|
|
83
|
-
payload = JSON.parse(raw) as typeof payload;
|
|
84
|
-
} catch (err) {
|
|
85
|
-
log.error({ err, filename }, "Failed to parse shotgun signal file");
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const { requestId } = payload;
|
|
90
|
-
if (!requestId || typeof requestId !== "string") {
|
|
91
|
-
log.warn("Shotgun signal missing requestId");
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const { action } = payload;
|
|
96
|
-
|
|
97
|
-
if (action === "start") {
|
|
98
|
-
handleStart(requestId, payload);
|
|
99
|
-
} else if (action === "status") {
|
|
100
|
-
handleStatus(requestId, payload);
|
|
101
|
-
} else {
|
|
102
|
-
writeResult(requestId, {
|
|
103
|
-
requestId,
|
|
104
|
-
ok: false,
|
|
105
|
-
error: `Unknown action: ${String(action)}`,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function handleStart(
|
|
111
|
-
requestId: string,
|
|
112
|
-
payload: {
|
|
113
|
-
durationSeconds?: number;
|
|
114
|
-
intervalSeconds?: number;
|
|
115
|
-
focusArea?: string;
|
|
116
|
-
},
|
|
117
|
-
): void {
|
|
118
|
-
const durationSeconds =
|
|
119
|
-
typeof payload.durationSeconds === "number" && payload.durationSeconds > 0
|
|
120
|
-
? payload.durationSeconds
|
|
121
|
-
: 300;
|
|
122
|
-
|
|
123
|
-
const intervalSeconds =
|
|
124
|
-
typeof payload.intervalSeconds === "number" && payload.intervalSeconds > 0
|
|
125
|
-
? payload.intervalSeconds
|
|
126
|
-
: 5;
|
|
127
|
-
|
|
128
|
-
const focusArea =
|
|
129
|
-
typeof payload.focusArea === "string" && payload.focusArea.length > 0
|
|
130
|
-
? payload.focusArea
|
|
131
|
-
: "general observation";
|
|
132
|
-
|
|
133
|
-
const watchId = crypto.randomUUID().slice(0, SHORT_HASH_LENGTH);
|
|
134
|
-
const conversationId = `shotgun-${watchId}`;
|
|
135
|
-
const now = Date.now();
|
|
136
|
-
|
|
137
|
-
const session: WatchSession = {
|
|
138
|
-
watchId,
|
|
139
|
-
conversationId,
|
|
140
|
-
focusArea,
|
|
141
|
-
durationSeconds,
|
|
142
|
-
intervalSeconds,
|
|
143
|
-
observations: [],
|
|
144
|
-
commentaryCount: 0,
|
|
145
|
-
status: "active",
|
|
146
|
-
startedAt: now,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
watchSessions.set(watchId, session);
|
|
150
|
-
fireWatchStartNotifier(conversationId, session);
|
|
151
|
-
|
|
152
|
-
session.timeoutHandle = setTimeout(() => {
|
|
153
|
-
session.status = "completing";
|
|
154
|
-
session.timeoutHandle = undefined;
|
|
155
|
-
log.info(
|
|
156
|
-
{ watchId, focusArea },
|
|
157
|
-
"Shotgun session duration expired, marking as completing",
|
|
158
|
-
);
|
|
159
|
-
fireWatchCompletionNotifier(conversationId, session);
|
|
160
|
-
}, durationSeconds * 1000);
|
|
161
|
-
|
|
162
|
-
log.info(
|
|
163
|
-
{ watchId, conversationId, focusArea, durationSeconds, intervalSeconds },
|
|
164
|
-
"Shotgun watch session started via signal",
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
writeResult(requestId, {
|
|
168
|
-
requestId,
|
|
169
|
-
ok: true,
|
|
170
|
-
watchId,
|
|
171
|
-
conversationId,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function handleStatus(requestId: string, payload: { watchId?: string }): void {
|
|
176
|
-
const { watchId } = payload;
|
|
177
|
-
if (!watchId || typeof watchId !== "string") {
|
|
178
|
-
writeResult(requestId, {
|
|
179
|
-
requestId,
|
|
180
|
-
ok: false,
|
|
181
|
-
error: "Missing watchId for status query",
|
|
182
|
-
});
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const session = watchSessions.get(watchId);
|
|
187
|
-
if (!session) {
|
|
188
|
-
writeResult(requestId, {
|
|
189
|
-
requestId,
|
|
190
|
-
ok: false,
|
|
191
|
-
error: `No watch session found for watchId: ${watchId}`,
|
|
192
|
-
});
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
writeResult(requestId, {
|
|
197
|
-
requestId,
|
|
198
|
-
ok: true,
|
|
199
|
-
watchId,
|
|
200
|
-
conversationId: session.conversationId,
|
|
201
|
-
status: session.status,
|
|
202
|
-
});
|
|
203
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
|
|
3
|
-
import { RiskLevel } from "../../permissions/types.js";
|
|
4
|
-
import type { ToolDefinition } from "../../providers/types.js";
|
|
5
|
-
import { getLogger } from "../../util/logger.js";
|
|
6
|
-
import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
7
|
-
import type { WatchSession } from "./watch-state.js";
|
|
8
|
-
import {
|
|
9
|
-
fireWatchCompletionNotifier,
|
|
10
|
-
fireWatchStartNotifier,
|
|
11
|
-
getActiveWatchSession,
|
|
12
|
-
watchSessions,
|
|
13
|
-
} from "./watch-state.js";
|
|
14
|
-
|
|
15
|
-
const log = getLogger("screen-watch");
|
|
16
|
-
|
|
17
|
-
const SHORT_HASH_LENGTH = 8;
|
|
18
|
-
|
|
19
|
-
class ScreenWatchTool implements Tool {
|
|
20
|
-
name = "start_screen_watch";
|
|
21
|
-
description =
|
|
22
|
-
"Start observing the screen at regular intervals for a specified duration. Captures OCR text from the active window and provides periodic commentary.";
|
|
23
|
-
category = "observation";
|
|
24
|
-
defaultRiskLevel = RiskLevel.Low;
|
|
25
|
-
|
|
26
|
-
getDefinition(): ToolDefinition {
|
|
27
|
-
return {
|
|
28
|
-
name: this.name,
|
|
29
|
-
description: this.description,
|
|
30
|
-
input_schema: {
|
|
31
|
-
type: "object",
|
|
32
|
-
properties: {
|
|
33
|
-
duration_minutes: {
|
|
34
|
-
type: "number",
|
|
35
|
-
description: "How long to watch in minutes (1-15, default 5)",
|
|
36
|
-
},
|
|
37
|
-
interval_seconds: {
|
|
38
|
-
type: "number",
|
|
39
|
-
description: "Seconds between screen captures (5-30, default 10)",
|
|
40
|
-
},
|
|
41
|
-
focus_area: {
|
|
42
|
-
type: "string",
|
|
43
|
-
description: "What to focus on observing",
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
required: ["focus_area"],
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async execute(
|
|
52
|
-
input: Record<string, unknown>,
|
|
53
|
-
context: ToolContext,
|
|
54
|
-
): Promise<ToolExecutionResult> {
|
|
55
|
-
const { conversationId } = context;
|
|
56
|
-
|
|
57
|
-
// Validate focus_area
|
|
58
|
-
const focusArea =
|
|
59
|
-
typeof input.focus_area === "string" && input.focus_area.length > 0
|
|
60
|
-
? input.focus_area
|
|
61
|
-
: undefined;
|
|
62
|
-
|
|
63
|
-
if (!focusArea) {
|
|
64
|
-
return {
|
|
65
|
-
content: "Error: focus_area is required and must be a non-empty string",
|
|
66
|
-
isError: true,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Clamp duration to 1-15 minutes
|
|
71
|
-
let durationMinutes =
|
|
72
|
-
typeof input.duration_minutes === "number" ? input.duration_minutes : 5;
|
|
73
|
-
durationMinutes = Math.max(1, Math.min(15, durationMinutes));
|
|
74
|
-
|
|
75
|
-
// Clamp interval to 5-30 seconds
|
|
76
|
-
let intervalSeconds =
|
|
77
|
-
typeof input.interval_seconds === "number" ? input.interval_seconds : 10;
|
|
78
|
-
intervalSeconds = Math.max(5, Math.min(30, intervalSeconds));
|
|
79
|
-
|
|
80
|
-
// Check for existing active session
|
|
81
|
-
const existing = getActiveWatchSession(conversationId);
|
|
82
|
-
if (existing) {
|
|
83
|
-
return {
|
|
84
|
-
content: `Error: An active watch session already exists for this conversation (watchId: ${existing.watchId}, focus: "${existing.focusArea}"). Cancel or wait for it to complete before starting a new one.`,
|
|
85
|
-
isError: true,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Generate watchId
|
|
90
|
-
const watchId = crypto.randomUUID().slice(0, SHORT_HASH_LENGTH);
|
|
91
|
-
const now = Date.now();
|
|
92
|
-
const durationSeconds = durationMinutes * 60;
|
|
93
|
-
|
|
94
|
-
// Create session
|
|
95
|
-
const session: WatchSession = {
|
|
96
|
-
watchId,
|
|
97
|
-
conversationId,
|
|
98
|
-
focusArea,
|
|
99
|
-
durationSeconds,
|
|
100
|
-
intervalSeconds,
|
|
101
|
-
observations: [],
|
|
102
|
-
commentaryCount: 0,
|
|
103
|
-
status: "active",
|
|
104
|
-
startedAt: now,
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// Store in sessions map
|
|
108
|
-
watchSessions.set(watchId, session);
|
|
109
|
-
|
|
110
|
-
// Fire start notifier
|
|
111
|
-
fireWatchStartNotifier(conversationId, session);
|
|
112
|
-
|
|
113
|
-
// Set timeout for duration expiry
|
|
114
|
-
session.timeoutHandle = setTimeout(() => {
|
|
115
|
-
session.status = "completing";
|
|
116
|
-
session.timeoutHandle = undefined;
|
|
117
|
-
log.info(
|
|
118
|
-
{ watchId, focusArea },
|
|
119
|
-
"Watch session duration expired, marking as completing",
|
|
120
|
-
);
|
|
121
|
-
fireWatchCompletionNotifier(conversationId, session);
|
|
122
|
-
}, durationSeconds * 1000);
|
|
123
|
-
|
|
124
|
-
log.info(
|
|
125
|
-
{ watchId, conversationId, focusArea, durationMinutes, intervalSeconds },
|
|
126
|
-
"Screen watch session started",
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const expectedCaptures = Math.floor(durationSeconds / intervalSeconds);
|
|
130
|
-
const content = [
|
|
131
|
-
`Screen watch started (watchId: ${watchId})`,
|
|
132
|
-
`Focus: ${focusArea}`,
|
|
133
|
-
`Duration: ${durationMinutes} minute${durationMinutes !== 1 ? "s" : ""}`,
|
|
134
|
-
`Interval: every ${intervalSeconds} seconds`,
|
|
135
|
-
`Expected captures: ~${expectedCaptures}`,
|
|
136
|
-
`Started at: ${new Date(now).toISOString()}`,
|
|
137
|
-
`Expected end: ${new Date(now + durationSeconds * 1000).toISOString()}`,
|
|
138
|
-
].join("\n");
|
|
139
|
-
|
|
140
|
-
return { content, isError: false };
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export const screenWatchTool = new ScreenWatchTool();
|