@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
|
@@ -8,6 +8,7 @@ import { z } from "zod";
|
|
|
8
8
|
|
|
9
9
|
import { bootstrapConversation } from "../../memory/conversation-bootstrap.js";
|
|
10
10
|
import { getConversation } from "../../memory/conversation-crud.js";
|
|
11
|
+
import { runScript } from "../../schedule/run-script.js";
|
|
11
12
|
import {
|
|
12
13
|
cancelSchedule,
|
|
13
14
|
completeScheduleRun,
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
describeCronExpression,
|
|
17
18
|
getLastScheduleConversationId,
|
|
18
19
|
getSchedule,
|
|
20
|
+
getScheduleRuns,
|
|
19
21
|
listSchedules,
|
|
20
22
|
updateSchedule,
|
|
21
23
|
} from "../../schedule/schedule-store.js";
|
|
@@ -46,6 +48,7 @@ function handleListSchedules(): Response {
|
|
|
46
48
|
cronExpression: j.cronExpression,
|
|
47
49
|
timezone: j.timezone,
|
|
48
50
|
message: j.message,
|
|
51
|
+
script: j.script,
|
|
49
52
|
nextRunAt: j.nextRunAt,
|
|
50
53
|
lastRunAt: j.lastRunAt,
|
|
51
54
|
lastStatus: j.lastStatus,
|
|
@@ -108,7 +111,7 @@ function handleCancelSchedule(id: string): Response {
|
|
|
108
111
|
return handleListSchedules();
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
const VALID_MODES = ["notify", "execute"] as const;
|
|
114
|
+
const VALID_MODES = ["notify", "execute", "script"] as const;
|
|
112
115
|
const VALID_ROUTING_INTENTS = [
|
|
113
116
|
"single_channel",
|
|
114
117
|
"multi_channel",
|
|
@@ -149,6 +152,7 @@ function handleUpdateSchedule(
|
|
|
149
152
|
"expression",
|
|
150
153
|
"timezone",
|
|
151
154
|
"message",
|
|
155
|
+
"script",
|
|
152
156
|
"mode",
|
|
153
157
|
"routingIntent",
|
|
154
158
|
"quiet",
|
|
@@ -178,6 +182,28 @@ function handleUpdateSchedule(
|
|
|
178
182
|
return handleListSchedules();
|
|
179
183
|
}
|
|
180
184
|
|
|
185
|
+
function handleListScheduleRuns(id: string, limit: number): Response {
|
|
186
|
+
const schedule = getSchedule(id);
|
|
187
|
+
if (!schedule) {
|
|
188
|
+
return httpError("NOT_FOUND", "Schedule not found", 404);
|
|
189
|
+
}
|
|
190
|
+
const runs = getScheduleRuns(id, limit);
|
|
191
|
+
return Response.json({
|
|
192
|
+
runs: runs.map((r) => ({
|
|
193
|
+
id: r.id,
|
|
194
|
+
jobId: r.jobId,
|
|
195
|
+
status: r.status,
|
|
196
|
+
startedAt: r.startedAt,
|
|
197
|
+
finishedAt: r.finishedAt,
|
|
198
|
+
durationMs: r.durationMs,
|
|
199
|
+
output: r.output,
|
|
200
|
+
error: r.error,
|
|
201
|
+
conversationId: r.conversationId,
|
|
202
|
+
createdAt: r.createdAt,
|
|
203
|
+
})),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
181
207
|
async function handleRunScheduleNow(
|
|
182
208
|
id: string,
|
|
183
209
|
sendMessageDeps?: SendMessageDeps,
|
|
@@ -187,6 +213,38 @@ async function handleRunScheduleNow(
|
|
|
187
213
|
return httpError("NOT_FOUND", "Schedule not found", 404);
|
|
188
214
|
}
|
|
189
215
|
|
|
216
|
+
// ── Script mode (shell command, no LLM) ──────────────────────────
|
|
217
|
+
if (schedule.mode === "script") {
|
|
218
|
+
if (!schedule.script) {
|
|
219
|
+
return httpError(
|
|
220
|
+
"BAD_REQUEST",
|
|
221
|
+
"Script schedule has no script command",
|
|
222
|
+
400,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
const runId = createScheduleRun(schedule.id, `script:${schedule.id}`);
|
|
226
|
+
try {
|
|
227
|
+
log.info(
|
|
228
|
+
{ jobId: schedule.id, name: schedule.name },
|
|
229
|
+
"Executing script schedule manually via HTTP (run now)",
|
|
230
|
+
);
|
|
231
|
+
const result = await runScript(schedule.script);
|
|
232
|
+
completeScheduleRun(runId, {
|
|
233
|
+
status: result.exitCode === 0 ? "ok" : "error",
|
|
234
|
+
output: result.stdout || undefined,
|
|
235
|
+
error: result.stderr || undefined,
|
|
236
|
+
});
|
|
237
|
+
} catch (err) {
|
|
238
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
239
|
+
log.warn(
|
|
240
|
+
{ err, jobId: schedule.id, name: schedule.name },
|
|
241
|
+
"Manual script schedule execution failed",
|
|
242
|
+
);
|
|
243
|
+
completeScheduleRun(runId, { status: "error", error: errorMsg });
|
|
244
|
+
}
|
|
245
|
+
return handleListSchedules();
|
|
246
|
+
}
|
|
247
|
+
|
|
190
248
|
// Check if message is a task invocation (run_task:<task_id>)
|
|
191
249
|
const taskMatch = schedule.message.match(/^run_task:(\S+)$/);
|
|
192
250
|
if (taskMatch) {
|
|
@@ -205,10 +263,12 @@ async function handleRunScheduleNow(
|
|
|
205
263
|
"sendMessageDeps not available for schedule execution",
|
|
206
264
|
);
|
|
207
265
|
}
|
|
208
|
-
const conversation =
|
|
209
|
-
|
|
266
|
+
const conversation = await sendMessageDeps.getOrCreateConversation(
|
|
267
|
+
conversationId,
|
|
268
|
+
{
|
|
210
269
|
trustContext: SCHEDULE_GUARDIAN_TRUST_CONTEXT,
|
|
211
|
-
}
|
|
270
|
+
},
|
|
271
|
+
);
|
|
212
272
|
conversation.taskRunId = taskRunId;
|
|
213
273
|
try {
|
|
214
274
|
await conversation.processMessage(
|
|
@@ -285,10 +345,12 @@ async function handleRunScheduleNow(
|
|
|
285
345
|
if (!sendMessageDeps) {
|
|
286
346
|
throw new Error("sendMessageDeps not available for schedule execution");
|
|
287
347
|
}
|
|
288
|
-
const activeConversation =
|
|
289
|
-
|
|
348
|
+
const activeConversation = await sendMessageDeps.getOrCreateConversation(
|
|
349
|
+
conversationId,
|
|
350
|
+
{
|
|
290
351
|
trustContext: SCHEDULE_GUARDIAN_TRUST_CONTEXT,
|
|
291
|
-
}
|
|
352
|
+
},
|
|
353
|
+
);
|
|
292
354
|
activeConversation.taskRunId = undefined;
|
|
293
355
|
await activeConversation.processMessage(
|
|
294
356
|
schedule.message,
|
|
@@ -331,6 +393,24 @@ export function scheduleRouteDefinitions(deps: {
|
|
|
331
393
|
}),
|
|
332
394
|
handler: () => handleListSchedules(),
|
|
333
395
|
},
|
|
396
|
+
{
|
|
397
|
+
endpoint: "schedules/:id/runs",
|
|
398
|
+
method: "GET",
|
|
399
|
+
policyKey: "schedules",
|
|
400
|
+
summary: "List schedule runs",
|
|
401
|
+
description: "Return recent invocation history for a schedule.",
|
|
402
|
+
tags: ["schedules"],
|
|
403
|
+
responseBody: z.object({
|
|
404
|
+
runs: z.array(z.unknown()).describe("Schedule run objects"),
|
|
405
|
+
}),
|
|
406
|
+
handler: ({ params, url }) => {
|
|
407
|
+
const rawLimit = Number(url.searchParams.get("limit") ?? 10);
|
|
408
|
+
const limit = Number.isFinite(rawLimit)
|
|
409
|
+
? Math.min(Math.max(Math.floor(rawLimit), 1), 100)
|
|
410
|
+
: 10;
|
|
411
|
+
return handleListScheduleRuns(params.id, limit);
|
|
412
|
+
},
|
|
413
|
+
},
|
|
334
414
|
{
|
|
335
415
|
endpoint: "schedules/:id/toggle",
|
|
336
416
|
method: "POST",
|
|
@@ -376,7 +456,8 @@ export function scheduleRouteDefinitions(deps: {
|
|
|
376
456
|
expression: z.string(),
|
|
377
457
|
timezone: z.string(),
|
|
378
458
|
message: z.string(),
|
|
379
|
-
|
|
459
|
+
script: z.string().nullable().describe("Shell command for script mode"),
|
|
460
|
+
mode: z.string().describe("notify, execute, or script"),
|
|
380
461
|
routingIntent: z
|
|
381
462
|
.string()
|
|
382
463
|
.describe("single_channel, multi_channel, or all_channels"),
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Registry for skill-provided HTTP route handlers.
|
|
3
3
|
*
|
|
4
|
-
* Skills register route matchers + handlers at initialization
|
|
5
|
-
* runtime HTTP server checks the registry for each inbound request
|
|
6
|
-
* falling through to its own route table.
|
|
4
|
+
* Skills and plugins register route matchers + handlers at initialization
|
|
5
|
+
* time. The runtime HTTP server checks the registry for each inbound request
|
|
6
|
+
* before falling through to its own route table.
|
|
7
|
+
*
|
|
8
|
+
* Registrations are identified by an opaque {@link SkillRouteHandle} returned
|
|
9
|
+
* from {@link registerSkillRoute}. Callers must pass that exact handle back
|
|
10
|
+
* to {@link unregisterSkillRoute} to remove the registration — pattern text
|
|
11
|
+
* is intentionally not a stable key, because two owners can legitimately
|
|
12
|
+
* register the same regex, and keying on `source + flags` would let one
|
|
13
|
+
* owner's teardown silently drop another owner's route.
|
|
7
14
|
*/
|
|
8
15
|
|
|
9
16
|
import { getLogger } from "../util/logger.js";
|
|
@@ -23,17 +30,62 @@ export type SkillRouteMatch =
|
|
|
23
30
|
| { kind: "match"; route: SkillRoute; match: RegExpMatchArray }
|
|
24
31
|
| { kind: "methodMismatch"; allow: string[] };
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Opaque token returned from {@link registerSkillRoute}. The token has no
|
|
35
|
+
* observable fields — callers must treat it as a black box whose only valid
|
|
36
|
+
* use is to pass it to {@link unregisterSkillRoute}. Identity comparison on
|
|
37
|
+
* the token is what the registry keys against, so every call to
|
|
38
|
+
* `registerSkillRoute` returns a fresh handle even when the route's
|
|
39
|
+
* `pattern`/`methods`/`handler` are deep-equal to an existing entry.
|
|
40
|
+
*/
|
|
41
|
+
declare const skillRouteHandleBrand: unique symbol;
|
|
42
|
+
export interface SkillRouteHandle {
|
|
43
|
+
readonly [skillRouteHandleBrand]: true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface RegisteredRoute {
|
|
47
|
+
readonly handle: SkillRouteHandle;
|
|
48
|
+
readonly route: SkillRoute;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const routes: RegisteredRoute[] = [];
|
|
27
52
|
|
|
28
53
|
/**
|
|
29
|
-
* Register a skill-provided HTTP route. Called
|
|
54
|
+
* Register a skill- or plugin-provided HTTP route. Called at initialization
|
|
55
|
+
* time. Returns an opaque handle the caller must retain and pass back to
|
|
56
|
+
* {@link unregisterSkillRoute} at teardown time. Do not attempt to derive
|
|
57
|
+
* the handle from the route's pattern — identity is the only stable key.
|
|
30
58
|
*/
|
|
31
|
-
export function registerSkillRoute(route: SkillRoute):
|
|
32
|
-
|
|
59
|
+
export function registerSkillRoute(route: SkillRoute): SkillRouteHandle {
|
|
60
|
+
const handle = Object.freeze({}) as SkillRouteHandle;
|
|
61
|
+
routes.push({ handle, route });
|
|
33
62
|
log.info(
|
|
34
63
|
{ pattern: route.pattern.source, methods: route.methods },
|
|
35
64
|
"Skill route registered",
|
|
36
65
|
);
|
|
66
|
+
return handle;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Unregister a previously-registered skill route by handle.
|
|
71
|
+
*
|
|
72
|
+
* Returns `true` if a route was removed, `false` otherwise. Not finding a
|
|
73
|
+
* match is not an error: the plugin-shutdown path calls this best-effort for
|
|
74
|
+
* every handle a plugin retained, and a stale handle (e.g. the registry was
|
|
75
|
+
* cleared externally) should not crash shutdown.
|
|
76
|
+
*/
|
|
77
|
+
export function unregisterSkillRoute(handle: SkillRouteHandle): boolean {
|
|
78
|
+
const index = routes.findIndex((entry) => entry.handle === handle);
|
|
79
|
+
if (index === -1) {
|
|
80
|
+
log.warn({}, "unregisterSkillRoute: no matching route found for handle");
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const [removed] = routes.splice(index, 1);
|
|
84
|
+
log.info(
|
|
85
|
+
{ pattern: removed!.route.pattern.source },
|
|
86
|
+
"Skill route unregistered",
|
|
87
|
+
);
|
|
88
|
+
return true;
|
|
37
89
|
}
|
|
38
90
|
|
|
39
91
|
/**
|
|
@@ -55,17 +107,25 @@ export function matchSkillRoute(
|
|
|
55
107
|
method: string,
|
|
56
108
|
): SkillRouteMatch | null {
|
|
57
109
|
const pathMatches: SkillRoute[] = [];
|
|
58
|
-
for (const
|
|
59
|
-
const match = path.match(route.pattern);
|
|
110
|
+
for (const entry of routes) {
|
|
111
|
+
const match = path.match(entry.route.pattern);
|
|
60
112
|
if (!match) continue;
|
|
61
|
-
if (route.methods.includes(method)) {
|
|
62
|
-
return { kind: "match", route, match };
|
|
113
|
+
if (entry.route.methods.includes(method)) {
|
|
114
|
+
return { kind: "match", route: entry.route, match };
|
|
63
115
|
}
|
|
64
|
-
pathMatches.push(route);
|
|
116
|
+
pathMatches.push(entry.route);
|
|
65
117
|
}
|
|
66
118
|
if (pathMatches.length === 0) return null;
|
|
67
|
-
const allow = Array.from(
|
|
68
|
-
new Set(pathMatches.flatMap((r) => r.methods)),
|
|
69
|
-
);
|
|
119
|
+
const allow = Array.from(new Set(pathMatches.flatMap((r) => r.methods)));
|
|
70
120
|
return { kind: "methodMismatch", allow };
|
|
71
121
|
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Test-only helper — drops every registered route. Production code has no
|
|
125
|
+
* legitimate need for this; a real shutdown walks the handles each owner
|
|
126
|
+
* retained. Exported so tests that bypass the normal shutdown path (e.g.
|
|
127
|
+
* those that crash mid-bootstrap) can reset registry state between cases.
|
|
128
|
+
*/
|
|
129
|
+
export function resetSkillRoutesForTests(): void {
|
|
130
|
+
routes.length = 0;
|
|
131
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { buildSanitizedEnv } from "../tools/terminal/safe-env.js";
|
|
2
|
+
import { getLogger } from "../util/logger.js";
|
|
3
|
+
import { getWorkspaceDir } from "../util/platform.js";
|
|
4
|
+
|
|
5
|
+
const log = getLogger("run-script");
|
|
6
|
+
|
|
7
|
+
/** Maximum combined stdout + stderr captured (bytes). */
|
|
8
|
+
const MAX_OUTPUT_BYTES = 10_000;
|
|
9
|
+
/** Default timeout for script execution (ms). */
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
11
|
+
|
|
12
|
+
export interface ScriptResult {
|
|
13
|
+
exitCode: number;
|
|
14
|
+
stdout: string;
|
|
15
|
+
stderr: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Run a shell command and capture its output.
|
|
20
|
+
*
|
|
21
|
+
* Uses Bun.spawn with /bin/sh so the command string supports pipes,
|
|
22
|
+
* redirects, and shell builtins. Output is truncated to
|
|
23
|
+
* {@link MAX_OUTPUT_BYTES} to keep schedule_runs rows bounded.
|
|
24
|
+
*/
|
|
25
|
+
export async function runScript(
|
|
26
|
+
command: string,
|
|
27
|
+
options?: { timeoutMs?: number; cwd?: string },
|
|
28
|
+
): Promise<ScriptResult> {
|
|
29
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
30
|
+
const cwd = options?.cwd ?? getWorkspaceDir();
|
|
31
|
+
|
|
32
|
+
log.info({ command, cwd, timeoutMs }, "Running script");
|
|
33
|
+
|
|
34
|
+
const proc = Bun.spawn(["sh", "-c", command], {
|
|
35
|
+
cwd,
|
|
36
|
+
stdout: "pipe",
|
|
37
|
+
stderr: "pipe",
|
|
38
|
+
env: buildSanitizedEnv(),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Race process completion against a timeout
|
|
42
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
43
|
+
const timer = setTimeout(() => {
|
|
44
|
+
proc.kill("SIGKILL");
|
|
45
|
+
reject(new Error(`Script timed out after ${timeoutMs}ms`));
|
|
46
|
+
}, timeoutMs);
|
|
47
|
+
timer.unref();
|
|
48
|
+
// Clean up timer if process finishes first
|
|
49
|
+
proc.exited.then(() => clearTimeout(timer));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const exitCode = await Promise.race([proc.exited, timeoutPromise]);
|
|
53
|
+
|
|
54
|
+
const stdout = truncate(await new Response(proc.stdout).text());
|
|
55
|
+
const stderr = truncate(await new Response(proc.stderr).text());
|
|
56
|
+
|
|
57
|
+
log.info(
|
|
58
|
+
{ command, exitCode, stdoutLen: stdout.length, stderrLen: stderr.length },
|
|
59
|
+
"Script completed",
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return { exitCode, stdout, stderr };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function truncate(text: string): string {
|
|
66
|
+
if (text.length <= MAX_OUTPUT_BYTES) return text;
|
|
67
|
+
return text.slice(0, MAX_OUTPUT_BYTES) + "\n... (truncated)";
|
|
68
|
+
}
|
|
@@ -14,7 +14,7 @@ import type { ScheduleSyntax } from "./recurrence-types.js";
|
|
|
14
14
|
|
|
15
15
|
const logger = getLogger("schedule-store");
|
|
16
16
|
|
|
17
|
-
export type ScheduleMode = "notify" | "execute";
|
|
17
|
+
export type ScheduleMode = "notify" | "execute" | "script";
|
|
18
18
|
export type RoutingIntent = "single_channel" | "multi_channel" | "all_channels";
|
|
19
19
|
export type ScheduleStatus = "active" | "firing" | "fired" | "cancelled";
|
|
20
20
|
|
|
@@ -27,6 +27,7 @@ export interface ScheduleJob {
|
|
|
27
27
|
cronExpression: string | null;
|
|
28
28
|
timezone: string | null;
|
|
29
29
|
message: string;
|
|
30
|
+
script: string | null;
|
|
30
31
|
nextRunAt: number;
|
|
31
32
|
lastRunAt: number | null;
|
|
32
33
|
lastStatus: string | null;
|
|
@@ -85,6 +86,7 @@ export function createSchedule(params: {
|
|
|
85
86
|
cronExpression?: string | null;
|
|
86
87
|
timezone?: string | null;
|
|
87
88
|
message: string;
|
|
89
|
+
script?: string | null;
|
|
88
90
|
enabled?: boolean;
|
|
89
91
|
createdBy?: string;
|
|
90
92
|
syntax?: ScheduleSyntax;
|
|
@@ -142,6 +144,7 @@ export function createSchedule(params: {
|
|
|
142
144
|
scheduleSyntax: syntax,
|
|
143
145
|
timezone,
|
|
144
146
|
message: params.message,
|
|
147
|
+
script: params.script ?? null,
|
|
145
148
|
nextRunAt,
|
|
146
149
|
lastRunAt: null as number | null,
|
|
147
150
|
lastStatus: null as string | null,
|
|
@@ -217,6 +220,7 @@ export function updateSchedule(
|
|
|
217
220
|
cronExpression?: string;
|
|
218
221
|
timezone?: string | null;
|
|
219
222
|
message?: string;
|
|
223
|
+
script?: string | null;
|
|
220
224
|
enabled?: boolean;
|
|
221
225
|
syntax?: ScheduleSyntax;
|
|
222
226
|
expression?: string;
|
|
@@ -273,6 +277,7 @@ export function updateSchedule(
|
|
|
273
277
|
if (updates.syntax !== undefined) set.scheduleSyntax = newSyntax;
|
|
274
278
|
if (updates.timezone !== undefined) set.timezone = updates.timezone;
|
|
275
279
|
if (updates.message !== undefined) set.message = updates.message;
|
|
280
|
+
if (updates.script !== undefined) set.script = updates.script;
|
|
276
281
|
if (updates.enabled !== undefined) set.enabled = updates.enabled;
|
|
277
282
|
if (updates.mode !== undefined) set.mode = updates.mode;
|
|
278
283
|
if (updates.routingIntent !== undefined)
|
|
@@ -777,6 +782,7 @@ function parseJobRow(row: typeof scheduleJobs.$inferSelect): ScheduleJob {
|
|
|
777
782
|
cronExpression: row.cronExpression,
|
|
778
783
|
timezone: row.timezone,
|
|
779
784
|
message: row.message,
|
|
785
|
+
script: row.script ?? null,
|
|
780
786
|
nextRunAt: row.nextRunAt,
|
|
781
787
|
lastRunAt: row.lastRunAt,
|
|
782
788
|
lastStatus: row.lastStatus,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type WatcherNotifier,
|
|
12
12
|
} from "../watcher/engine.js";
|
|
13
13
|
import { hasSetConstructs } from "./recurrence-engine.js";
|
|
14
|
+
import { runScript, type ScriptResult } from "./run-script.js";
|
|
14
15
|
import {
|
|
15
16
|
claimDueSchedules,
|
|
16
17
|
completeOneShot,
|
|
@@ -49,8 +50,6 @@ export type ScheduleNotifyModeNotifier = (payload: {
|
|
|
49
50
|
routingHints: Record<string, unknown>;
|
|
50
51
|
}) => void | Promise<void>;
|
|
51
52
|
|
|
52
|
-
export type ScheduleNotifier = (schedule: { id: string; name: string }) => void;
|
|
53
|
-
|
|
54
53
|
export type ScheduleConversationCreatedNotifier = (info: {
|
|
55
54
|
conversationId: string;
|
|
56
55
|
scheduleJobId: string;
|
|
@@ -67,7 +66,6 @@ const TICK_INTERVAL_MS = 15_000;
|
|
|
67
66
|
export function startScheduler(
|
|
68
67
|
processMessage: ScheduleMessageProcessor,
|
|
69
68
|
notifyScheduleOneShot: ScheduleNotifyModeNotifier,
|
|
70
|
-
notifySchedule: ScheduleNotifier,
|
|
71
69
|
watcherNotifier?: WatcherNotifier,
|
|
72
70
|
watcherEscalator?: WatcherEscalator,
|
|
73
71
|
onScheduleConversationCreated?: ScheduleConversationCreatedNotifier,
|
|
@@ -82,7 +80,6 @@ export function startScheduler(
|
|
|
82
80
|
await runScheduleOnce(
|
|
83
81
|
processMessage,
|
|
84
82
|
notifyScheduleOneShot,
|
|
85
|
-
notifySchedule,
|
|
86
83
|
watcherNotifier,
|
|
87
84
|
watcherEscalator,
|
|
88
85
|
onScheduleConversationCreated,
|
|
@@ -105,7 +102,6 @@ export function startScheduler(
|
|
|
105
102
|
return runScheduleOnce(
|
|
106
103
|
processMessage,
|
|
107
104
|
notifyScheduleOneShot,
|
|
108
|
-
notifySchedule,
|
|
109
105
|
watcherNotifier,
|
|
110
106
|
watcherEscalator,
|
|
111
107
|
onScheduleConversationCreated,
|
|
@@ -121,7 +117,6 @@ export function startScheduler(
|
|
|
121
117
|
async function runScheduleOnce(
|
|
122
118
|
processMessage: ScheduleMessageProcessor,
|
|
123
119
|
notifyScheduleOneShot: ScheduleNotifyModeNotifier,
|
|
124
|
-
notifySchedule: ScheduleNotifier,
|
|
125
120
|
watcherNotifier?: WatcherNotifier,
|
|
126
121
|
watcherEscalator?: WatcherEscalator,
|
|
127
122
|
onScheduleConversationCreated?: ScheduleConversationCreatedNotifier,
|
|
@@ -185,6 +180,53 @@ async function runScheduleOnce(
|
|
|
185
180
|
continue;
|
|
186
181
|
}
|
|
187
182
|
|
|
183
|
+
// ── Script mode (shell command, no LLM) ────────────────────────
|
|
184
|
+
if (job.mode === "script") {
|
|
185
|
+
if (!job.script) {
|
|
186
|
+
log.warn(
|
|
187
|
+
{ jobId: job.id, name: job.name },
|
|
188
|
+
"Script schedule has no script command — skipping",
|
|
189
|
+
);
|
|
190
|
+
processed += 1;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const runId = createScheduleRun(job.id, `script:${job.id}`);
|
|
194
|
+
try {
|
|
195
|
+
log.info(
|
|
196
|
+
{ jobId: job.id, name: job.name, isOneShot },
|
|
197
|
+
"Executing script schedule",
|
|
198
|
+
);
|
|
199
|
+
const result: ScriptResult = await runScript(job.script);
|
|
200
|
+
completeScheduleRun(runId, {
|
|
201
|
+
status: result.exitCode === 0 ? "ok" : "error",
|
|
202
|
+
output: result.stdout || undefined,
|
|
203
|
+
error: result.stderr || undefined,
|
|
204
|
+
});
|
|
205
|
+
if (result.exitCode === 0) {
|
|
206
|
+
if (!job.quiet) {
|
|
207
|
+
emitScheduleFeedEvent({
|
|
208
|
+
title: job.name,
|
|
209
|
+
summary: "Script ran.",
|
|
210
|
+
dedupKey: `schedule-run:${runId}`,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (isOneShot) completeOneShot(job.id);
|
|
214
|
+
} else {
|
|
215
|
+
if (isOneShot) failOneShot(job.id);
|
|
216
|
+
}
|
|
217
|
+
} catch (err) {
|
|
218
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
219
|
+
log.warn(
|
|
220
|
+
{ err, jobId: job.id, name: job.name, isOneShot },
|
|
221
|
+
"Script schedule execution failed",
|
|
222
|
+
);
|
|
223
|
+
completeScheduleRun(runId, { status: "error", error: errorMsg });
|
|
224
|
+
if (isOneShot) failOneShot(job.id);
|
|
225
|
+
}
|
|
226
|
+
processed += 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
188
230
|
// ── Execute mode ────────────────────────────────────────────────
|
|
189
231
|
|
|
190
232
|
// Check if message is a task invocation (run_task:<task_id>)
|
|
@@ -241,7 +283,6 @@ async function runScheduleOnce(
|
|
|
241
283
|
} else {
|
|
242
284
|
completeScheduleRun(runId, { status: "ok" });
|
|
243
285
|
if (!job.quiet) {
|
|
244
|
-
notifySchedule({ id: job.id, name: job.name });
|
|
245
286
|
emitScheduleFeedEvent({
|
|
246
287
|
title: job.name,
|
|
247
288
|
summary: "Scheduled task ran.",
|
|
@@ -340,7 +381,6 @@ async function runScheduleOnce(
|
|
|
340
381
|
});
|
|
341
382
|
completeScheduleRun(runId, { status: "ok" });
|
|
342
383
|
if (!job.quiet) {
|
|
343
|
-
notifySchedule({ id: job.id, name: job.name });
|
|
344
384
|
emitScheduleFeedEvent({
|
|
345
385
|
title: job.name,
|
|
346
386
|
summary: isOneShot ? "One-shot reminder ran." : "Scheduled job ran.",
|
|
@@ -37,6 +37,16 @@ export async function getCatalog(): Promise<CatalogSkill[]> {
|
|
|
37
37
|
catalog = remote;
|
|
38
38
|
}
|
|
39
39
|
} catch (err) {
|
|
40
|
+
if (cachedCatalog) {
|
|
41
|
+
log.warn(
|
|
42
|
+
{ err },
|
|
43
|
+
"Failed to fetch Vellum catalog, keeping stale merged cache",
|
|
44
|
+
);
|
|
45
|
+
// Reset the TTL window so subsequent calls during the outage are served
|
|
46
|
+
// from cache instead of re-entering fetchCatalog() on every call.
|
|
47
|
+
cacheTimestamp = Date.now();
|
|
48
|
+
return cachedCatalog;
|
|
49
|
+
}
|
|
40
50
|
if (local.length > 0) {
|
|
41
51
|
log.warn(
|
|
42
52
|
{ err },
|
|
@@ -44,11 +54,8 @@ export async function getCatalog(): Promise<CatalogSkill[]> {
|
|
|
44
54
|
);
|
|
45
55
|
catalog = local;
|
|
46
56
|
} else {
|
|
47
|
-
log.warn(
|
|
48
|
-
|
|
49
|
-
"Failed to fetch Vellum catalog, using stale cache or empty",
|
|
50
|
-
);
|
|
51
|
-
return cachedCatalog ?? [];
|
|
57
|
+
log.warn({ err }, "Failed to fetch Vellum catalog, returning empty");
|
|
58
|
+
return [];
|
|
52
59
|
}
|
|
53
60
|
}
|
|
54
61
|
cachedCatalog = catalog;
|