@vellumai/assistant 0.8.12 → 0.9.0-staging.2
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 +0 -14
- package/ARCHITECTURE.md +45 -45
- package/README.md +1 -1
- package/bun.lock +200 -154
- package/docs/architecture/integrations.md +3 -3
- package/docs/architecture/memory.md +2 -2
- package/docs/architecture/security.md +10 -10
- package/docs/runbook-trusted-contacts.md +12 -12
- package/docs/skills.md +6 -6
- package/docs/workflows-testing.md +221 -0
- package/docs/workflows.md +510 -0
- package/examples/plugins/echo/README.md +5 -5
- package/knip.json +2 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
- package/openapi.yaml +7197 -5708
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +66 -114
- package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
- package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
- package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
- package/src/__tests__/app-compiler.test.ts +7 -1
- package/src/__tests__/app-dir-path-guard.test.ts +27 -3
- package/src/__tests__/app-executors.test.ts +43 -0
- package/src/__tests__/approval-cascade.test.ts +0 -5
- package/src/__tests__/approval-routes-http.test.ts +91 -0
- package/src/__tests__/assistant-stream-state.test.ts +107 -0
- package/src/__tests__/browser-fill-credential.test.ts +3 -3
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +63 -7
- package/src/__tests__/compaction-trail-store.test.ts +74 -1
- package/src/__tests__/compaction.benchmark.test.ts +63 -41
- package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -5
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
- package/src/__tests__/conversation-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
- package/src/__tests__/conversation-speed-override.test.ts +0 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
- package/src/__tests__/credential-broker-server-use.test.ts +2 -2
- package/src/__tests__/credential-broker.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +417 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault.test.ts +37 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
- package/src/__tests__/dynamic-page-surface.test.ts +219 -0
- package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
- package/src/__tests__/gateway-flag-listener.test.ts +24 -7
- package/src/__tests__/guardian-action-sweep.test.ts +56 -219
- package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
- package/src/__tests__/llm-resolver.test.ts +115 -0
- package/src/__tests__/managed-profile-guard.test.ts +6 -5
- package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
- package/src/__tests__/media-generate-image.test.ts +20 -9
- package/src/__tests__/mock-gateway-ipc.ts +23 -0
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/normalize-onboarding.test.ts +26 -0
- package/src/__tests__/notification-decision-strategy.test.ts +4 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
- package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
- package/src/__tests__/require-fresh-approval.test.ts +425 -1
- package/src/__tests__/resolve-app-id.test.ts +56 -0
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
- package/src/__tests__/schedule-routes.test.ts +257 -4
- package/src/__tests__/schedule-store.test.ts +60 -0
- package/src/__tests__/schedule-tools.test.ts +247 -2
- package/src/__tests__/skill-execute-input.test.ts +85 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
- package/src/__tests__/task-scheduler.test.ts +299 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
- package/src/__tests__/tool-result-spool.test.ts +3 -1
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
- package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
- package/src/agent/compaction-circuit.ts +11 -0
- package/src/agent/loop.ts +181 -12
- package/src/api/constants/call-sites.ts +12 -0
- package/src/api/events/assistant-thinking-delta.ts +10 -0
- package/src/api/events/usage-update.ts +7 -0
- package/src/api/index.ts +4 -1
- package/src/api/responses/memory-v3-selection-log.ts +18 -11
- package/src/approvals/approval-primitive.ts +2 -2
- package/src/background-wake/background-wake-routes.test.ts +5 -2
- package/src/bundler/compiler-tools.ts +1 -1
- package/src/bundler/package-resolver.ts +0 -1
- package/src/calls/call-domain.ts +1 -1
- package/src/calls/guardian-action-sweep.ts +16 -93
- package/src/cli/AGENTS.md +4 -0
- package/src/cli/commands/__tests__/schedules.test.ts +430 -1
- package/src/cli/commands/credentials.ts +28 -24
- package/src/cli/commands/image-generation.ts +23 -9
- package/src/cli/commands/notifications.ts +1 -1
- package/src/cli/commands/plugins.ts +89 -46
- package/src/cli/commands/schedules.ts +384 -11
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
- package/src/cli/lib/inspect-plugin.ts +62 -1
- package/src/cli/lib/install-from-github.ts +52 -5
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/config/__tests__/workflows-schema.test.ts +60 -0
- package/src/config/bundled-skills/acp/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
- package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
- package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
- package/src/config/bundled-skills/schedule/SKILL.md +7 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
- package/src/config/bundled-skills/workflows/SKILL.md +214 -0
- package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
- package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
- package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
- package/src/config/bundled-tool-registry.ts +14 -2
- package/src/config/call-site-defaults.ts +5 -0
- package/src/config/feature-flag-registry.json +12 -4
- package/src/config/llm-context-resolution.ts +8 -0
- package/src/config/llm-resolver.ts +30 -0
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
- package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
- package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
- package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/heartbeat.ts +4 -1
- package/src/config/schemas/llm.ts +33 -26
- package/src/config/schemas/memory-retrospective.ts +19 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/services.ts +5 -2
- package/src/config/schemas/workflows.ts +42 -0
- package/src/config/skills.ts +3 -3
- package/src/context/compactor.ts +273 -39
- package/src/context/post-turn-tool-result-truncation.ts +23 -6
- package/src/context/tool-result-spool.ts +12 -17
- package/src/credential-execution/executable-discovery.ts +1 -1
- package/src/credential-execution/process-manager.ts +37 -3
- package/src/credential-execution/prompted-credential.ts +205 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
- package/src/daemon/conversation-process.ts +11 -2
- package/src/daemon/conversation-surfaces.ts +167 -3
- package/src/daemon/conversation-tool-setup.ts +103 -26
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +115 -11
- package/src/daemon/handlers/shared.ts +26 -14
- package/src/daemon/host-cu-proxy.ts +15 -12
- package/src/daemon/host-file-proxy.ts +15 -12
- package/src/daemon/host-transfer-proxy.ts +30 -24
- package/src/daemon/lifecycle.ts +40 -3
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/messages.ts +2 -10
- package/src/daemon/message-types/workflows.ts +49 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
- package/src/daemon/process-message.ts +6 -0
- package/src/daemon/tool-setup-types.ts +57 -0
- package/src/daemon/wake-conversation-ops.ts +18 -0
- package/src/heartbeat/heartbeat-run-store.ts +8 -2
- package/src/home/feed-types.ts +1 -1
- package/src/ipc/gateway-flag-listener.ts +28 -6
- package/src/mcp/mcp-auth-state.ts +8 -20
- package/src/media/__tests__/image-models.test.ts +57 -0
- package/src/media/image-models.ts +66 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
- package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
- package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
- package/src/memory/auto-analysis-enqueue.ts +14 -1
- package/src/memory/compaction-log-store-clickhouse.ts +6 -4
- package/src/memory/conversation-crud.ts +9 -2
- package/src/memory/conversation-disk-view.ts +1 -1
- package/src/memory/conversation-queries.ts +22 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/db-maintenance.ts +16 -0
- package/src/memory/embedding-runtime-manager.ts +1 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
- package/src/memory/llm-request-log-source-local.ts +19 -1
- package/src/memory/llm-request-log-source.ts +35 -6
- package/src/memory/llm-request-log-store.ts +90 -2
- package/src/memory/memory-retrospective-constants.ts +9 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -6
- package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
- package/src/memory/memory-retrospective-job.ts +500 -208
- package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
- package/src/memory/memory-retrospective-state.ts +85 -2
- package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
- package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
- package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
- package/src/memory/migrations/284-workflow-runs.ts +51 -0
- package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
- package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
- package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
- package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
- package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
- package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
- package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/preloaded-apps.ts +116 -0
- package/src/memory/schema/infrastructure.ts +4 -0
- package/src/memory/schema/memory-core.ts +4 -0
- package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
- package/src/memory/v2/__tests__/page-store.test.ts +13 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -6
- package/src/memory/v2/page-store.ts +4 -3
- package/src/memory/v2/qdrant.ts +42 -3
- package/src/memory/v2/types.ts +16 -10
- package/src/messaging/draft-store.ts +1 -1
- package/src/notifications/access-request-copy.ts +200 -113
- package/src/notifications/adapters/slack.ts +250 -111
- package/src/notifications/adapters/telegram.ts +7 -44
- package/src/notifications/approval-card-builder.ts +93 -0
- package/src/notifications/broadcaster.ts +74 -0
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/copy-composer.ts +32 -26
- package/src/notifications/decision-engine.ts +59 -7
- package/src/notifications/guardian-question-mode.ts +145 -155
- package/src/notifications/home-feed-side-effect.ts +28 -11
- package/src/notifications/notification-utils.ts +66 -0
- package/src/notifications/signal.ts +6 -0
- package/src/notifications/tool-approval-copy.ts +142 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/threshold.ts +11 -0
- package/src/plugin-api/types.ts +16 -4
- package/src/plugins/defaults/compaction/window-manager.ts +44 -0
- package/src/plugins/defaults/index.ts +46 -0
- package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
- package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
- package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
- package/src/prompts/__tests__/system-prompt.test.ts +100 -1
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
- package/src/prompts/normalize-onboarding.ts +2 -0
- package/src/prompts/persona-resolver.ts +3 -0
- package/src/prompts/system-prompt.ts +51 -2
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
- package/src/prompts/templates/system-sections.ts +8 -3
- package/src/providers/call-site-routing.ts +6 -3
- package/src/providers/fireworks/client.ts +3 -0
- package/src/providers/inference/auth.ts +52 -46
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +47 -9
- package/src/providers/openai/coerce-object-args.ts +104 -0
- package/src/providers/retry.ts +8 -5
- package/src/providers/types.ts +10 -0
- package/src/runtime/__tests__/agent-wake.test.ts +629 -7
- package/src/runtime/access-request-helper.ts +16 -9
- package/src/runtime/agent-wake.ts +302 -51
- package/src/runtime/assistant-stream-state.ts +141 -8
- package/src/runtime/background-job-runner.ts +9 -0
- package/src/runtime/channel-approval-types.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +6 -5
- package/src/runtime/channel-invite-transports/voice.ts +2 -2
- package/src/runtime/channel-invite-types.ts +4 -2
- package/src/runtime/channel-retry-sweep.ts +19 -41
- package/src/runtime/finalize-event-delivery.ts +72 -0
- package/src/runtime/guardian-action-message-composer.ts +0 -54
- package/src/runtime/http-server.ts +6 -14
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/message-composer-types.ts +0 -9
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
- package/src/runtime/middleware/auth.ts +27 -3
- package/src/runtime/middleware/rate-limiter.ts +28 -1
- package/src/runtime/migrations/vbundle-builder.ts +6 -5
- package/src/runtime/pending-interactions.ts +20 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
- package/src/runtime/routes/approval-routes.ts +35 -8
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
- package/src/runtime/routes/btw-routes.ts +37 -1
- package/src/runtime/routes/channel-delivery-routes.ts +11 -7
- package/src/runtime/routes/channel-route-definitions.ts +3 -0
- package/src/runtime/routes/channel-route-shared.ts +3 -1
- package/src/runtime/routes/consolidation-routes.ts +17 -13
- package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
- package/src/runtime/routes/conversation-list-routes.ts +41 -4
- package/src/runtime/routes/conversation-query-routes.ts +196 -11
- package/src/runtime/routes/conversation-routes.ts +15 -1
- package/src/runtime/routes/credential-prompt-routes.ts +39 -17
- package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
- package/src/runtime/routes/heartbeat-routes.ts +18 -13
- package/src/runtime/routes/image-generation-routes.ts +20 -2
- package/src/runtime/routes/inbound-message-handler.ts +13 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/log-export/AGENTS.md +1 -1
- package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +5 -9
- package/src/runtime/routes/plugins-routes.ts +26 -0
- package/src/runtime/routes/ps-routes.ts +10 -8
- package/src/runtime/routes/retrospective-routes.ts +235 -0
- package/src/runtime/routes/runs-pagination.ts +75 -0
- package/src/runtime/routes/schedule-routes.ts +367 -59
- package/src/runtime/routes/sounds-config-routes.ts +239 -0
- package/src/runtime/routes/surface-action-routes.ts +84 -4
- package/src/runtime/routes/workflow-routes.test.ts +372 -0
- package/src/runtime/routes/workflow-routes.ts +363 -0
- package/src/runtime/routes/workspace-routes.test.ts +61 -1
- package/src/runtime/routes/workspace-routes.ts +26 -1
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
- package/src/runtime/services/analyze-conversation.ts +26 -13
- package/src/schedule/inference-profile.ts +28 -0
- package/src/schedule/schedule-store.ts +152 -4
- package/src/schedule/scheduler-types.ts +6 -0
- package/src/schedule/scheduler.ts +96 -0
- package/src/security/secret-allowlist.ts +1 -1
- package/src/skills/path-classifier.ts +1 -1
- package/src/tools/apps/executors.ts +23 -0
- package/src/tools/apps/resolve-app-id.ts +42 -0
- package/src/tools/browser/browser-execution.ts +9 -11
- package/src/tools/credentials/broker.ts +4 -4
- package/src/tools/credentials/vault.ts +26 -137
- package/src/tools/executor.ts +69 -0
- package/src/tools/flag-gated-tools.test.ts +76 -0
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/registry.ts +51 -0
- package/src/tools/schedule/create.ts +77 -1
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +73 -1
- package/src/tools/skills/execute.ts +56 -0
- package/src/tools/terminal/shell.ts +1 -1
- package/src/tools/ui-surface/definitions.ts +91 -2
- package/src/tools/workflows/manage-workflows.ts +183 -0
- package/src/tools/workflows/run-workflow.test.ts +442 -0
- package/src/tools/workflows/run-workflow.ts +88 -0
- package/src/types/onboarding-context.ts +2 -0
- package/src/usage/attribution.ts +24 -0
- package/src/util/canonicalize-identity.ts +12 -3
- package/src/util/platform.ts +17 -17
- package/src/watcher/__tests__/engine.test.ts +24 -0
- package/src/watcher/__tests__/telemetry.test.ts +135 -0
- package/src/watcher/engine.ts +7 -0
- package/src/watcher/telemetry.ts +74 -0
- package/src/workflows/capabilities.test.ts +365 -0
- package/src/workflows/capabilities.ts +359 -0
- package/src/workflows/deterministic-stringify.ts +27 -0
- package/src/workflows/engine-integration.test.ts +656 -0
- package/src/workflows/engine.test.ts +1144 -0
- package/src/workflows/engine.ts +1078 -0
- package/src/workflows/fanout-load.test.ts +168 -0
- package/src/workflows/journal-store.test.ts +369 -0
- package/src/workflows/journal-store.ts +470 -0
- package/src/workflows/leaf-runner.test.ts +704 -0
- package/src/workflows/leaf-runner.ts +589 -0
- package/src/workflows/library.test.ts +134 -0
- package/src/workflows/library.ts +124 -0
- package/src/workflows/run-manager.test.ts +711 -0
- package/src/workflows/run-manager.ts +593 -0
- package/src/workflows/sandbox-escape.test.ts +339 -0
- package/src/workflows/sandbox.test.ts +251 -0
- package/src/workflows/sandbox.ts +447 -0
- package/src/workspace/adaptive-thinking-repair.ts +33 -11
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
- package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
- package/src/workspace/migrations/031-drop-user-md.ts +1 -4
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
- package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
- package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
- package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/migrations/runner.ts +1 -1
- package/tsconfig.json +1 -1
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
- package/src/__tests__/guardian-action-store.test.ts +0 -106
- package/src/daemon/guardian-action-generators.ts +0 -71
- package/src/memory/guardian-action-store.ts +0 -484
- package/src/runtime/guardian-action-grant-minter.ts +0 -150
|
@@ -24,11 +24,20 @@
|
|
|
24
24
|
// conversation might be the active one. We're conservative and only
|
|
25
25
|
// sweep when no job exists at all, since the worst-case false-positive
|
|
26
26
|
// is leaving a few extra orphans for the next sweep to catch.)
|
|
27
|
-
// - AND the row is NOT the
|
|
28
|
-
// conversation. The
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
27
|
+
// - AND the row is NOT the preserved dedup baseline for its source
|
|
28
|
+
// conversation. The primary dedup baseline is the persisted
|
|
29
|
+
// `remembered_log` on `memory_retrospective_state`, but state rows that
|
|
30
|
+
// predate the log column fall back to scanning the most-recent prior
|
|
31
|
+
// retro (via `findMostRecentRetrospectiveFor`) to seed their
|
|
32
|
+
// `<already_remembered>` dedup block; sweeping it would force such a
|
|
33
|
+
// run to re-save facts the prior pass already captured. The baseline is
|
|
34
|
+
// the most recent row that actually produced output (see
|
|
35
|
+
// `selectPreservedBaseline`) — a crash orphan with no post-fork
|
|
36
|
+
// assistant message would seed an EMPTY dedup block, so when an older
|
|
37
|
+
// row with output exists we preserve that one instead.
|
|
38
|
+
//
|
|
39
|
+
// The sweep is skipped entirely when `memory.retrospective.keepSupersededRuns`
|
|
40
|
+
// is true — see the comment inside the function.
|
|
32
41
|
|
|
33
42
|
import {
|
|
34
43
|
and,
|
|
@@ -42,16 +51,27 @@ import {
|
|
|
42
51
|
sql,
|
|
43
52
|
} from "drizzle-orm";
|
|
44
53
|
|
|
54
|
+
import { getConfig } from "../config/loader.js";
|
|
45
55
|
import { getLogger } from "../util/logger.js";
|
|
46
56
|
import { deleteConversation } from "./conversation-crud.js";
|
|
47
57
|
import { getDb } from "./db-connection.js";
|
|
48
58
|
import { MEMORY_RETROSPECTIVE_SOURCES } from "./memory-retrospective-constants.js";
|
|
59
|
+
import { loadRetrospectiveRunMessages } from "./memory-retrospective-fork-boundary.js";
|
|
49
60
|
import { conversations, memoryJobs } from "./schema.js";
|
|
50
61
|
|
|
51
62
|
const log = getLogger("memory-retrospective-startup-cleanup");
|
|
52
63
|
|
|
53
64
|
const ORPHAN_AGE_MS = 60 * 60 * 1000;
|
|
54
65
|
|
|
66
|
+
/**
|
|
67
|
+
* How many of the newest retrospective rows per source the baseline selector
|
|
68
|
+
* will load messages for when looking for one with output. Bounds the
|
|
69
|
+
* per-startup query volume — after GC (`deleteSupersededPriorRetrospective`)
|
|
70
|
+
* a source normally has 1–2 retro rows, so this only matters for pathological
|
|
71
|
+
* crash pileups, where the older rows are orphans too.
|
|
72
|
+
*/
|
|
73
|
+
const MAX_BASELINE_CANDIDATES_PER_SOURCE = 3;
|
|
74
|
+
|
|
55
75
|
export interface CleanupResult {
|
|
56
76
|
swept: number;
|
|
57
77
|
}
|
|
@@ -65,6 +85,16 @@ export interface CleanupResult {
|
|
|
65
85
|
export function sweepOrphanMemoryRetrospectiveConversations(
|
|
66
86
|
now: number = Date.now(),
|
|
67
87
|
): CleanupResult {
|
|
88
|
+
// When the operator opted into retaining superseded retrospective runs
|
|
89
|
+
// (`memory.retrospective.keepSupersededRuns`), skip the sweep entirely —
|
|
90
|
+
// retained runs must survive restarts, and the sweep cannot distinguish a
|
|
91
|
+
// retained superseded run from a crash orphan. Tradeoff: under this opt-in,
|
|
92
|
+
// genuine crash orphans persist too. That's acceptable — the operator asked
|
|
93
|
+
// for full run history, and an orphan is just one more retained conversation.
|
|
94
|
+
if (getConfig().memory?.retrospective?.keepSupersededRuns === true) {
|
|
95
|
+
return { swept: 0 };
|
|
96
|
+
}
|
|
97
|
+
|
|
68
98
|
const cutoff = now - ORPHAN_AGE_MS;
|
|
69
99
|
const db = getDb();
|
|
70
100
|
|
|
@@ -90,13 +120,14 @@ export function sweepOrphanMemoryRetrospectiveConversations(
|
|
|
90
120
|
.map((row) => row.conversationId)
|
|
91
121
|
.filter((id): id is string => typeof id === "string" && id.length > 0);
|
|
92
122
|
|
|
93
|
-
// Compute the
|
|
94
|
-
// `
|
|
95
|
-
//
|
|
96
|
-
//
|
|
123
|
+
// Compute the preserved dedup baseline per source. Runs whose state row
|
|
124
|
+
// predates the persisted `remembered_log` pull dedup context by scanning
|
|
125
|
+
// the most-recent prior retro (via `findMostRecentRetrospectiveFor`);
|
|
126
|
+
// sweeping it would leave those runs with no baseline at all.
|
|
97
127
|
const allRetros = db
|
|
98
128
|
.select({
|
|
99
129
|
id: conversations.id,
|
|
130
|
+
source: conversations.source,
|
|
100
131
|
forkParentConversationId: conversations.forkParentConversationId,
|
|
101
132
|
createdAt: conversations.createdAt,
|
|
102
133
|
})
|
|
@@ -108,21 +139,21 @@ export function sweepOrphanMemoryRetrospectiveConversations(
|
|
|
108
139
|
),
|
|
109
140
|
)
|
|
110
141
|
.all();
|
|
111
|
-
const
|
|
112
|
-
string,
|
|
113
|
-
{ id: string; createdAt: number }
|
|
114
|
-
>();
|
|
142
|
+
const retrosPerSource = new Map<string, RetroRow[]>();
|
|
115
143
|
for (const row of allRetros) {
|
|
116
144
|
const parent = row.forkParentConversationId;
|
|
117
145
|
if (parent === null) continue;
|
|
118
|
-
const
|
|
119
|
-
if (
|
|
120
|
-
|
|
146
|
+
const rows = retrosPerSource.get(parent);
|
|
147
|
+
if (rows) {
|
|
148
|
+
rows.push(row);
|
|
149
|
+
} else {
|
|
150
|
+
retrosPerSource.set(parent, [row]);
|
|
121
151
|
}
|
|
122
152
|
}
|
|
123
|
-
const preservedIds = new Set(
|
|
124
|
-
|
|
125
|
-
|
|
153
|
+
const preservedIds = new Set<string>();
|
|
154
|
+
for (const rows of retrosPerSource.values()) {
|
|
155
|
+
preservedIds.add(selectPreservedBaseline(rows));
|
|
156
|
+
}
|
|
126
157
|
|
|
127
158
|
const orphans = db
|
|
128
159
|
.select({ id: conversations.id })
|
|
@@ -173,3 +204,50 @@ export function sweepOrphanMemoryRetrospectiveConversations(
|
|
|
173
204
|
}
|
|
174
205
|
return { swept };
|
|
175
206
|
}
|
|
207
|
+
|
|
208
|
+
interface RetroRow {
|
|
209
|
+
id: string;
|
|
210
|
+
source: string | null;
|
|
211
|
+
createdAt: number;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Pick which retrospective row to preserve as the source's dedup baseline:
|
|
216
|
+
* the most recent row that actually produced output (a crash orphan with no
|
|
217
|
+
* post-fork assistant message would seed the next run with an EMPTY
|
|
218
|
+
* `<already_remembered>` block and cause re-saves). Falls back to the plain
|
|
219
|
+
* most-recent row when no candidate qualifies — preserves the previous
|
|
220
|
+
* behavior when every row is an orphan, and an orphan baseline at least
|
|
221
|
+
* keeps `findMostRecentRetrospectiveFor` stable until a successful run
|
|
222
|
+
* supersedes it.
|
|
223
|
+
*
|
|
224
|
+
* PR-E's persisted `remembered_log` reduces the impact of preserving a bad
|
|
225
|
+
* baseline (the next run can fall back to the persisted log), but the log
|
|
226
|
+
* fallback path still reads the preserved conversation, so the sweep should
|
|
227
|
+
* keep preferring a row that's actually useful.
|
|
228
|
+
*
|
|
229
|
+
* Only loads messages for the newest `MAX_BASELINE_CANDIDATES_PER_SOURCE`
|
|
230
|
+
* rows — never for every retro row.
|
|
231
|
+
*/
|
|
232
|
+
function selectPreservedBaseline(rows: RetroRow[]): string {
|
|
233
|
+
const sorted = [...rows].sort((a, b) => b.createdAt - a.createdAt);
|
|
234
|
+
const withOutput = sorted
|
|
235
|
+
.slice(0, MAX_BASELINE_CANDIDATES_PER_SOURCE)
|
|
236
|
+
.find(retrospectiveHasOutput);
|
|
237
|
+
return (withOutput ?? sorted[0]!).id;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Whether the retrospective row produced any assistant output of its own.
|
|
242
|
+
* `loadRetrospectiveRunMessages` scopes fork-kind rows to the post-fork tail
|
|
243
|
+
* (the copied source prefix contains the source's own assistant turns) and
|
|
244
|
+
* returns `null` for rows whose output cannot be determined (load failure or
|
|
245
|
+
* no detectable fork boundary) — those rows contribute nothing to dedup
|
|
246
|
+
* (`collectPriorRetrospectiveRemembers` treats them as empty), so they don't
|
|
247
|
+
* qualify. Legacy-kind rows start empty, so any assistant message counts.
|
|
248
|
+
*/
|
|
249
|
+
function retrospectiveHasOutput(row: RetroRow): boolean {
|
|
250
|
+
const runMessages = loadRetrospectiveRunMessages(row.id, row.source);
|
|
251
|
+
if (runMessages == null) return false;
|
|
252
|
+
return runMessages.some((m) => m.role === "assistant");
|
|
253
|
+
}
|
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
// per-conversation cooldown gate in the trigger-check helper so failing
|
|
11
11
|
// jobs can't loop in tight retries across trigger types.
|
|
12
12
|
//
|
|
13
|
+
// A third column rides along with the success-path pointer write:
|
|
14
|
+
// - `rememberedLog` (JSON array of strings) — the cumulative `remember`
|
|
15
|
+
// contents saved across retrospective passes. The job's
|
|
16
|
+
// `<already_remembered>` dedup block reads from this log so dedup context
|
|
17
|
+
// survives GC of superseded retrospective conversations and spans more
|
|
18
|
+
// than the last pass. Capped — see `appendToRememberedLog`.
|
|
19
|
+
//
|
|
13
20
|
// The schema enforces the foreign key with ON DELETE CASCADE, so deleting a
|
|
14
21
|
// conversation collects its state row automatically.
|
|
15
22
|
|
|
@@ -22,6 +29,59 @@ export interface MemoryRetrospectiveState {
|
|
|
22
29
|
conversationId: string;
|
|
23
30
|
lastProcessedMessageId: string;
|
|
24
31
|
lastRunAt: number;
|
|
32
|
+
/**
|
|
33
|
+
* Cumulative `remember` contents from prior retrospective passes, oldest
|
|
34
|
+
* first. Empty for rows that predate the `remembered_log` column or have
|
|
35
|
+
* no saves yet — callers fall back to scanning the prior retrospective
|
|
36
|
+
* conversation in that case.
|
|
37
|
+
*/
|
|
38
|
+
rememberedLog: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Cap for the persisted remembered log: keep the most recent entries up to
|
|
43
|
+
* 100 entries or 8 KB serialized, whichever binds first. The log is injected
|
|
44
|
+
* verbatim into every retrospective prompt, so the byte cap bounds prompt
|
|
45
|
+
* growth; the entry cap bounds list length for pathological tiny entries.
|
|
46
|
+
*/
|
|
47
|
+
export const REMEMBERED_LOG_MAX_ENTRIES = 100;
|
|
48
|
+
export const REMEMBERED_LOG_MAX_BYTES = 8 * 1024;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Append new entries to the remembered log and apply the cap, dropping the
|
|
52
|
+
* OLDEST entries first. A single entry larger than the byte cap is dropped
|
|
53
|
+
* entirely rather than truncated mid-string.
|
|
54
|
+
*/
|
|
55
|
+
export function appendToRememberedLog(
|
|
56
|
+
existing: string[],
|
|
57
|
+
newEntries: string[],
|
|
58
|
+
): string[] {
|
|
59
|
+
const combined = [...existing, ...newEntries];
|
|
60
|
+
let result = combined.slice(
|
|
61
|
+
Math.max(0, combined.length - REMEMBERED_LOG_MAX_ENTRIES),
|
|
62
|
+
);
|
|
63
|
+
while (
|
|
64
|
+
result.length > 0 &&
|
|
65
|
+
Buffer.byteLength(JSON.stringify(result), "utf8") > REMEMBERED_LOG_MAX_BYTES
|
|
66
|
+
) {
|
|
67
|
+
result = result.slice(1);
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseRememberedLog(raw: string | null): string[] {
|
|
73
|
+
if (!raw) return [];
|
|
74
|
+
try {
|
|
75
|
+
const parsed: unknown = JSON.parse(raw);
|
|
76
|
+
if (!Array.isArray(parsed)) return [];
|
|
77
|
+
return parsed.filter((entry): entry is string => typeof entry === "string");
|
|
78
|
+
} catch {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function serializeRememberedLog(log: string[]): string | null {
|
|
84
|
+
return log.length === 0 ? null : JSON.stringify(log);
|
|
25
85
|
}
|
|
26
86
|
|
|
27
87
|
/**
|
|
@@ -40,25 +100,43 @@ export function getRetrospectiveState(
|
|
|
40
100
|
conversationId: row.conversationId,
|
|
41
101
|
lastProcessedMessageId: row.lastProcessedMessageId,
|
|
42
102
|
lastRunAt: row.lastRunAt,
|
|
103
|
+
rememberedLog: parseRememberedLog(row.rememberedLog),
|
|
43
104
|
};
|
|
44
105
|
}
|
|
45
106
|
|
|
46
107
|
/**
|
|
47
108
|
* Upsert both pointers atomically. Used on successful retrospective runs.
|
|
109
|
+
*
|
|
110
|
+
* `rememberedLog`, when provided, is written in the same statement so the
|
|
111
|
+
* cumulative dedup log can never drift from the pointer it was computed
|
|
112
|
+
* against. When omitted, the stored log is left untouched (and seeded NULL on
|
|
113
|
+
* first insert).
|
|
48
114
|
*/
|
|
49
|
-
export function upsertRetrospectiveState(
|
|
115
|
+
export function upsertRetrospectiveState(
|
|
116
|
+
args: Omit<MemoryRetrospectiveState, "rememberedLog"> & {
|
|
117
|
+
rememberedLog?: string[];
|
|
118
|
+
},
|
|
119
|
+
): void {
|
|
50
120
|
const db = getDb();
|
|
121
|
+
const serializedLog =
|
|
122
|
+
args.rememberedLog === undefined
|
|
123
|
+
? undefined
|
|
124
|
+
: serializeRememberedLog(args.rememberedLog);
|
|
51
125
|
db.insert(memoryRetrospectiveState)
|
|
52
126
|
.values({
|
|
53
127
|
conversationId: args.conversationId,
|
|
54
128
|
lastProcessedMessageId: args.lastProcessedMessageId,
|
|
55
129
|
lastRunAt: args.lastRunAt,
|
|
130
|
+
rememberedLog: serializedLog ?? null,
|
|
56
131
|
})
|
|
57
132
|
.onConflictDoUpdate({
|
|
58
133
|
target: memoryRetrospectiveState.conversationId,
|
|
59
134
|
set: {
|
|
60
135
|
lastProcessedMessageId: args.lastProcessedMessageId,
|
|
61
136
|
lastRunAt: args.lastRunAt,
|
|
137
|
+
...(serializedLog !== undefined
|
|
138
|
+
? { rememberedLog: serializedLog }
|
|
139
|
+
: {}),
|
|
62
140
|
},
|
|
63
141
|
})
|
|
64
142
|
.run();
|
|
@@ -83,6 +161,8 @@ export function upsertRetrospectiveState(args: MemoryRetrospectiveState): void {
|
|
|
83
161
|
* wait for new post-fork messages before its first retro fires.
|
|
84
162
|
*
|
|
85
163
|
* `lastRunAt` is copied verbatim — the cooldown gate inherits from source.
|
|
164
|
+
* `rememberedLog` is copied verbatim — the parent's saves remain the child's
|
|
165
|
+
* dedup baseline.
|
|
86
166
|
*/
|
|
87
167
|
export function forkRetrospectiveState(args: {
|
|
88
168
|
database: DrizzleDb;
|
|
@@ -125,12 +205,14 @@ export function forkRetrospectiveState(args: {
|
|
|
125
205
|
conversationId: forkedConversationId,
|
|
126
206
|
lastProcessedMessageId: forkedPointer,
|
|
127
207
|
lastRunAt: sourceRow.lastRunAt,
|
|
208
|
+
rememberedLog: sourceRow.rememberedLog,
|
|
128
209
|
})
|
|
129
210
|
.onConflictDoUpdate({
|
|
130
211
|
target: memoryRetrospectiveState.conversationId,
|
|
131
212
|
set: {
|
|
132
213
|
lastProcessedMessageId: forkedPointer,
|
|
133
214
|
lastRunAt: sourceRow.lastRunAt,
|
|
215
|
+
rememberedLog: sourceRow.rememberedLog,
|
|
134
216
|
},
|
|
135
217
|
})
|
|
136
218
|
.run();
|
|
@@ -141,7 +223,8 @@ export function forkRetrospectiveState(args: {
|
|
|
141
223
|
* applies to subsequent trigger-driven enqueues. If no row exists yet (first
|
|
142
224
|
* attempt failed), seed `lastProcessedMessageId` to the empty string — a
|
|
143
225
|
* sentinel meaning "nothing successfully processed yet" that subsequent
|
|
144
|
-
* `getMessagesSince(...)` queries treat the same as a missing row.
|
|
226
|
+
* `getMessagesSince(...)` queries treat the same as a missing row. An
|
|
227
|
+
* existing row's `rememberedLog` is left untouched.
|
|
145
228
|
*/
|
|
146
229
|
export function bumpRetrospectiveLastRunAt(
|
|
147
230
|
conversationId: string,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
4
|
+
|
|
5
|
+
const CHECKPOINT_KEY = "migration_memory_retrospective_remembered_log_v1";
|
|
6
|
+
|
|
7
|
+
const TABLE = "memory_retrospective_state";
|
|
8
|
+
const COLUMN = "remembered_log";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Add a nullable `remembered_log TEXT` column to `memory_retrospective_state`.
|
|
12
|
+
*
|
|
13
|
+
* Stores a JSON array of strings — the cumulative `remember` contents saved
|
|
14
|
+
* by retrospective passes over the conversation (capped; see
|
|
15
|
+
* `memory-retrospective-state.ts`). The retrospective job's
|
|
16
|
+
* `<already_remembered>` dedup block reads from this log so it survives GC of
|
|
17
|
+
* superseded retrospective conversations and spans more than the last pass.
|
|
18
|
+
*
|
|
19
|
+
* `NULL` for rows persisted before this migration ran — the job falls back
|
|
20
|
+
* to scanning the prior retrospective conversation for those.
|
|
21
|
+
*
|
|
22
|
+
* Idempotent — the PRAGMA guard makes re-running a no-op once the column
|
|
23
|
+
* exists.
|
|
24
|
+
*/
|
|
25
|
+
export function migrateMemoryRetrospectiveRememberedLog(
|
|
26
|
+
database: DrizzleDb,
|
|
27
|
+
): void {
|
|
28
|
+
withCrashRecovery(database, CHECKPOINT_KEY, () => {
|
|
29
|
+
const raw = getSqliteFrom(database);
|
|
30
|
+
|
|
31
|
+
const columns = raw.query(`PRAGMA table_info(${TABLE})`).all() as Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
}>;
|
|
34
|
+
const columnNames = new Set(columns.map((c) => c.name));
|
|
35
|
+
|
|
36
|
+
if (!columnNames.has(COLUMN)) {
|
|
37
|
+
raw.exec(`ALTER TABLE ${TABLE} ADD COLUMN ${COLUMN} TEXT`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../schema.js";
|
|
7
|
+
import { migrateScheduleInferenceProfile } from "./282-schedule-inference-profile.js";
|
|
8
|
+
|
|
9
|
+
function createTestDb() {
|
|
10
|
+
const sqlite = new Database(":memory:");
|
|
11
|
+
// Pre-282 shape: no inference_profile column (trimmed to the columns the
|
|
12
|
+
// migration and assertions touch).
|
|
13
|
+
sqlite.exec(/*sql*/ `
|
|
14
|
+
CREATE TABLE cron_jobs (
|
|
15
|
+
id TEXT PRIMARY KEY,
|
|
16
|
+
name TEXT NOT NULL,
|
|
17
|
+
message TEXT NOT NULL,
|
|
18
|
+
next_run_at INTEGER NOT NULL,
|
|
19
|
+
created_by TEXT NOT NULL,
|
|
20
|
+
created_at INTEGER NOT NULL,
|
|
21
|
+
updated_at INTEGER NOT NULL
|
|
22
|
+
)
|
|
23
|
+
`);
|
|
24
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function columnNames(sqlite: Database): string[] {
|
|
28
|
+
return (
|
|
29
|
+
sqlite.query("PRAGMA table_info(cron_jobs)").all() as Array<{
|
|
30
|
+
name: string;
|
|
31
|
+
}>
|
|
32
|
+
).map((c) => c.name);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe("migration 282: cron_jobs inference_profile", () => {
|
|
36
|
+
test("adds a nullable inference_profile column", () => {
|
|
37
|
+
const { sqlite, db } = createTestDb();
|
|
38
|
+
expect(columnNames(sqlite)).not.toContain("inference_profile");
|
|
39
|
+
|
|
40
|
+
migrateScheduleInferenceProfile(db);
|
|
41
|
+
|
|
42
|
+
const column = (
|
|
43
|
+
sqlite.query("PRAGMA table_info(cron_jobs)").all() as Array<{
|
|
44
|
+
name: string;
|
|
45
|
+
notnull: number;
|
|
46
|
+
}>
|
|
47
|
+
).find((c) => c.name === "inference_profile");
|
|
48
|
+
expect(column).toBeDefined();
|
|
49
|
+
expect(column?.notnull).toBe(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("existing rows read back with inference_profile null", () => {
|
|
53
|
+
const { sqlite, db } = createTestDb();
|
|
54
|
+
sqlite.exec(/*sql*/ `
|
|
55
|
+
INSERT INTO cron_jobs (id, name, message, next_run_at, created_by, created_at, updated_at)
|
|
56
|
+
VALUES ('job-1', 'Daily digest', 'write the digest', 1000, 'agent', 1000, 1000)
|
|
57
|
+
`);
|
|
58
|
+
|
|
59
|
+
migrateScheduleInferenceProfile(db);
|
|
60
|
+
|
|
61
|
+
const row = sqlite
|
|
62
|
+
.query("SELECT inference_profile FROM cron_jobs WHERE id = 'job-1'")
|
|
63
|
+
.get() as { inference_profile: string | null };
|
|
64
|
+
expect(row.inference_profile).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("is idempotent — re-run is a no-op", () => {
|
|
68
|
+
const { sqlite, db } = createTestDb();
|
|
69
|
+
|
|
70
|
+
migrateScheduleInferenceProfile(db);
|
|
71
|
+
expect(() => migrateScheduleInferenceProfile(db)).not.toThrow();
|
|
72
|
+
|
|
73
|
+
expect(
|
|
74
|
+
columnNames(sqlite).filter((name) => name === "inference_profile"),
|
|
75
|
+
).toHaveLength(1);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Add a nullable `inference_profile TEXT` column to `cron_jobs`.
|
|
6
|
+
*
|
|
7
|
+
* Stores the `llm.profiles` key a schedule's LLM-executed runs should use as
|
|
8
|
+
* their per-turn inference-profile override. `NULL` (all pre-existing rows)
|
|
9
|
+
* means no override — runs resolve through the default `mainAgent` call-site
|
|
10
|
+
* configuration, exactly as before this migration.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent — the PRAGMA guard makes re-running a no-op once the column
|
|
13
|
+
* exists.
|
|
14
|
+
*/
|
|
15
|
+
export function migrateScheduleInferenceProfile(database: DrizzleDb): void {
|
|
16
|
+
const raw = getSqliteFrom(database);
|
|
17
|
+
|
|
18
|
+
const columns = raw.query(`PRAGMA table_info(cron_jobs)`).all() as Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
}>;
|
|
21
|
+
const columnNames = new Set(columns.map((c) => c.name));
|
|
22
|
+
|
|
23
|
+
if (!columnNames.has("inference_profile")) {
|
|
24
|
+
raw.exec(`ALTER TABLE cron_jobs ADD COLUMN inference_profile TEXT`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../schema.js";
|
|
7
|
+
import { migrateMemoryV3SelectionsMessageIdAndSections } from "./283-memory-v3-selections-message-id-and-sections.js";
|
|
8
|
+
|
|
9
|
+
function createTestDb() {
|
|
10
|
+
const sqlite = new Database(":memory:");
|
|
11
|
+
// Pre-283 shape: slug/source/pinned only, no message_id or section columns.
|
|
12
|
+
sqlite.exec(/*sql*/ `
|
|
13
|
+
CREATE TABLE memory_v3_selections (
|
|
14
|
+
conversation_id TEXT NOT NULL,
|
|
15
|
+
turn INTEGER NOT NULL,
|
|
16
|
+
slug TEXT NOT NULL,
|
|
17
|
+
source TEXT NOT NULL,
|
|
18
|
+
pinned INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
created_at INTEGER NOT NULL,
|
|
20
|
+
PRIMARY KEY (conversation_id, turn, slug)
|
|
21
|
+
)
|
|
22
|
+
`);
|
|
23
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function columnNames(sqlite: Database): string[] {
|
|
27
|
+
return (
|
|
28
|
+
sqlite.query("PRAGMA table_info(memory_v3_selections)").all() as Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
}>
|
|
31
|
+
).map((c) => c.name);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function indexNames(sqlite: Database): string[] {
|
|
35
|
+
return (
|
|
36
|
+
sqlite.query("PRAGMA index_list(memory_v3_selections)").all() as Array<{
|
|
37
|
+
name: string;
|
|
38
|
+
}>
|
|
39
|
+
).map((i) => i.name);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("migration 283: memory_v3_selections message_id + section columns", () => {
|
|
43
|
+
test("adds nullable message_id, section_ordinal, section_title + message_id index", () => {
|
|
44
|
+
const { sqlite, db } = createTestDb();
|
|
45
|
+
expect(columnNames(sqlite)).not.toContain("message_id");
|
|
46
|
+
|
|
47
|
+
migrateMemoryV3SelectionsMessageIdAndSections(db);
|
|
48
|
+
|
|
49
|
+
const cols = sqlite
|
|
50
|
+
.query("PRAGMA table_info(memory_v3_selections)")
|
|
51
|
+
.all() as Array<{ name: string; notnull: number }>;
|
|
52
|
+
for (const name of ["message_id", "section_ordinal", "section_title"]) {
|
|
53
|
+
const col = cols.find((c) => c.name === name);
|
|
54
|
+
expect(col).toBeDefined();
|
|
55
|
+
expect(col?.notnull).toBe(0); // nullable
|
|
56
|
+
}
|
|
57
|
+
expect(indexNames(sqlite)).toContain("idx_memory_v3_selections_message");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("existing rows read back with the new columns null", () => {
|
|
61
|
+
const { sqlite, db } = createTestDb();
|
|
62
|
+
sqlite.exec(/*sql*/ `
|
|
63
|
+
INSERT INTO memory_v3_selections
|
|
64
|
+
(conversation_id, turn, slug, source, pinned, created_at)
|
|
65
|
+
VALUES ('conv-1', 0, 'a/page', 'needle', 0, 1000)
|
|
66
|
+
`);
|
|
67
|
+
|
|
68
|
+
migrateMemoryV3SelectionsMessageIdAndSections(db);
|
|
69
|
+
|
|
70
|
+
const row = sqlite
|
|
71
|
+
.query(
|
|
72
|
+
`SELECT message_id, section_ordinal, section_title
|
|
73
|
+
FROM memory_v3_selections WHERE conversation_id = 'conv-1'`,
|
|
74
|
+
)
|
|
75
|
+
.get() as {
|
|
76
|
+
message_id: string | null;
|
|
77
|
+
section_ordinal: number | null;
|
|
78
|
+
section_title: string | null;
|
|
79
|
+
};
|
|
80
|
+
expect(row.message_id).toBeNull();
|
|
81
|
+
expect(row.section_ordinal).toBeNull();
|
|
82
|
+
expect(row.section_title).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("is idempotent — re-run is a no-op", () => {
|
|
86
|
+
const { sqlite, db } = createTestDb();
|
|
87
|
+
|
|
88
|
+
migrateMemoryV3SelectionsMessageIdAndSections(db);
|
|
89
|
+
expect(() =>
|
|
90
|
+
migrateMemoryV3SelectionsMessageIdAndSections(db),
|
|
91
|
+
).not.toThrow();
|
|
92
|
+
|
|
93
|
+
expect(columnNames(sqlite).filter((n) => n === "message_id")).toHaveLength(
|
|
94
|
+
1,
|
|
95
|
+
);
|
|
96
|
+
expect(
|
|
97
|
+
indexNames(sqlite).filter(
|
|
98
|
+
(n) => n === "idx_memory_v3_selections_message",
|
|
99
|
+
),
|
|
100
|
+
).toHaveLength(1);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Add three nullable columns to `memory_v3_selections` for the inspector's
|
|
6
|
+
* Memory V3 panel:
|
|
7
|
+
*
|
|
8
|
+
* - `message_id TEXT` — the turn's assistant message id, backfilled at turn
|
|
9
|
+
* end (mirrors the v2 activation log). The inspector looks selections up by
|
|
10
|
+
* the inspected message's turn message ids, which is robust against the
|
|
11
|
+
* drift between v2's tracker turn and v3's orchestrator `turnCount` that
|
|
12
|
+
* previously left the panel blank. NULL for pre-existing rows and until the
|
|
13
|
+
* turn-end backfill stamps it.
|
|
14
|
+
* - `section_ordinal INTEGER` / `section_title TEXT` — the matched section a
|
|
15
|
+
* finder-lane selection surfaced (from `OrchestrateResult.matchedSections`).
|
|
16
|
+
* NULL for pre-existing rows and for core/hot/fresh/edge selections that
|
|
17
|
+
* carry no matched section (those render full-page, as before).
|
|
18
|
+
*
|
|
19
|
+
* All three are nullable and append-only — old rows degrade to the prior
|
|
20
|
+
* page-level / turn-keyed behavior. The PK (conversation_id, turn, slug) is
|
|
21
|
+
* unchanged; these are secondary attributes. The existing offline A/B readers
|
|
22
|
+
* (`summarizeSelections`, hot-set, learned-edges, prune) ignore them.
|
|
23
|
+
*
|
|
24
|
+
* Idempotent — the PRAGMA guards make re-running a no-op once the columns
|
|
25
|
+
* exist, and the index uses IF NOT EXISTS.
|
|
26
|
+
*/
|
|
27
|
+
export function migrateMemoryV3SelectionsMessageIdAndSections(
|
|
28
|
+
database: DrizzleDb,
|
|
29
|
+
): void {
|
|
30
|
+
const raw = getSqliteFrom(database);
|
|
31
|
+
|
|
32
|
+
const columns = raw
|
|
33
|
+
.query(`PRAGMA table_info(memory_v3_selections)`)
|
|
34
|
+
.all() as Array<{ name: string }>;
|
|
35
|
+
const columnNames = new Set(columns.map((c) => c.name));
|
|
36
|
+
|
|
37
|
+
if (!columnNames.has("message_id")) {
|
|
38
|
+
raw.exec(`ALTER TABLE memory_v3_selections ADD COLUMN message_id TEXT`);
|
|
39
|
+
}
|
|
40
|
+
if (!columnNames.has("section_ordinal")) {
|
|
41
|
+
raw.exec(
|
|
42
|
+
`ALTER TABLE memory_v3_selections ADD COLUMN section_ordinal INTEGER`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (!columnNames.has("section_title")) {
|
|
46
|
+
raw.exec(`ALTER TABLE memory_v3_selections ADD COLUMN section_title TEXT`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
raw.exec(
|
|
50
|
+
`CREATE INDEX IF NOT EXISTS idx_memory_v3_selections_message
|
|
51
|
+
ON memory_v3_selections (message_id)`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create the workflow-engine persistence tables.
|
|
5
|
+
*
|
|
6
|
+
* `workflow_runs` is one row per orchestration run (a sandboxed script that
|
|
7
|
+
* spawns parallel leaf agents). `workflow_journal` is an append-only log of
|
|
8
|
+
* every leaf call (agent / host function / nested workflow) keyed by
|
|
9
|
+
* `(run_id, seq)`, so a run can RESUME after a daemon restart by replaying
|
|
10
|
+
* cached results for the unchanged call prefix.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent — re-running is a no-op once the tables and index exist.
|
|
13
|
+
*/
|
|
14
|
+
export function migrateWorkflowRuns(database: DrizzleDb): void {
|
|
15
|
+
database.run(/*sql*/ `
|
|
16
|
+
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
17
|
+
id TEXT PRIMARY KEY,
|
|
18
|
+
name TEXT,
|
|
19
|
+
script_source TEXT NOT NULL,
|
|
20
|
+
script_hash TEXT NOT NULL,
|
|
21
|
+
args_json TEXT,
|
|
22
|
+
capabilities_json TEXT,
|
|
23
|
+
status TEXT NOT NULL,
|
|
24
|
+
conversation_id TEXT,
|
|
25
|
+
agents_spawned INTEGER NOT NULL DEFAULT 0,
|
|
26
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
27
|
+
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
28
|
+
result_json TEXT,
|
|
29
|
+
error TEXT,
|
|
30
|
+
created_at INTEGER,
|
|
31
|
+
updated_at INTEGER,
|
|
32
|
+
finished_at INTEGER
|
|
33
|
+
)
|
|
34
|
+
`);
|
|
35
|
+
database.run(
|
|
36
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_workflow_runs_status_created_at ON workflow_runs (status, created_at)`,
|
|
37
|
+
);
|
|
38
|
+
database.run(/*sql*/ `
|
|
39
|
+
CREATE TABLE IF NOT EXISTS workflow_journal (
|
|
40
|
+
run_id TEXT NOT NULL,
|
|
41
|
+
seq INTEGER NOT NULL,
|
|
42
|
+
call_hash TEXT NOT NULL,
|
|
43
|
+
kind TEXT NOT NULL,
|
|
44
|
+
request_json TEXT,
|
|
45
|
+
result_json TEXT,
|
|
46
|
+
status TEXT NOT NULL,
|
|
47
|
+
created_at INTEGER,
|
|
48
|
+
PRIMARY KEY (run_id, seq)
|
|
49
|
+
)
|
|
50
|
+
`);
|
|
51
|
+
}
|