@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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PlaygroundRouteDeps } from "./deps.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Body code for flag-off playground 404s. Distinct from the generic
|
|
5
|
+
* `NOT_FOUND` code so the Swift `CompactionPlaygroundClient` can route
|
|
6
|
+
* these to `.notAvailable` (toast: "Playground endpoints disabled")
|
|
7
|
+
* rather than `.notFound` (toast: "Conversation not found"). The two
|
|
8
|
+
* cases are otherwise indistinguishable on conv-scoped routes because
|
|
9
|
+
* `assertPlaygroundEnabled` runs *before* the conversation lookup, so a
|
|
10
|
+
* URL-path heuristic on the client misclassifies flag-off as missing-
|
|
11
|
+
* conversation. See `conversation-not-found.ts` for the matching code on
|
|
12
|
+
* the other branch.
|
|
13
|
+
*/
|
|
14
|
+
export const PLAYGROUND_DISABLED_CODE = "playground_disabled";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Defense-in-depth guard every playground route calls first. Returns a 404
|
|
18
|
+
* Response when the `compaction-playground` feature flag is disabled so the
|
|
19
|
+
* entire /playground/* surface is invisible in production regardless of UI
|
|
20
|
+
* gating.
|
|
21
|
+
*/
|
|
22
|
+
export function assertPlaygroundEnabled(
|
|
23
|
+
deps: PlaygroundRouteDeps,
|
|
24
|
+
): Response | null {
|
|
25
|
+
if (!deps.isPlaygroundEnabled()) {
|
|
26
|
+
return Response.json(
|
|
27
|
+
{
|
|
28
|
+
error: {
|
|
29
|
+
code: PLAYGROUND_DISABLED_CODE,
|
|
30
|
+
message: "Compaction playground is not enabled",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{ status: 404 },
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { RouteDefinition } from "../../http-router.js";
|
|
2
|
+
import type { PlaygroundRouteDeps } from "./deps.js";
|
|
3
|
+
import { forceCompactRouteDefinitions } from "./force-compact.js";
|
|
4
|
+
import { injectFailuresRouteDefinitions } from "./inject-failures.js";
|
|
5
|
+
import { resetCircuitRouteDefinitions } from "./reset-circuit.js";
|
|
6
|
+
import { seedConversationRouteDefinitions } from "./seed-conversation.js";
|
|
7
|
+
import { seededConversationsRouteDefinitions } from "./seeded-conversations.js";
|
|
8
|
+
import { stateRouteDefinitions } from "./state.js";
|
|
9
|
+
|
|
10
|
+
export type { PlaygroundRouteDeps };
|
|
11
|
+
export { assertPlaygroundEnabled } from "./guard.js";
|
|
12
|
+
|
|
13
|
+
export function playgroundRouteDefinitions(
|
|
14
|
+
deps: PlaygroundRouteDeps,
|
|
15
|
+
): RouteDefinition[] {
|
|
16
|
+
// Each playground route file exports its own `*RouteDefinitions(deps)`
|
|
17
|
+
// factory; this aggregator spreads the arrays together. Later PRs in the
|
|
18
|
+
// plan append more imports here — keeping it purely additive minimizes
|
|
19
|
+
// conflicts across concurrent playground PRs.
|
|
20
|
+
return [
|
|
21
|
+
...forceCompactRouteDefinitions(deps),
|
|
22
|
+
...injectFailuresRouteDefinitions(deps),
|
|
23
|
+
...resetCircuitRouteDefinitions(deps),
|
|
24
|
+
...seedConversationRouteDefinitions(deps),
|
|
25
|
+
...seededConversationsRouteDefinitions(deps),
|
|
26
|
+
...stateRouteDefinitions(deps),
|
|
27
|
+
];
|
|
28
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /v1/conversations/:id/playground/inject-compaction-failures
|
|
3
|
+
* directly mutates the compaction circuit-breaker state on a conversation.
|
|
4
|
+
*
|
|
5
|
+
* This is a dev-only playground endpoint gated by the
|
|
6
|
+
* `compaction-playground` feature flag. It lets integration tests and the
|
|
7
|
+
* macOS playground UI drive the circuit breaker into interesting states
|
|
8
|
+
* without having to wait for real consecutive summary-LLM failures.
|
|
9
|
+
*
|
|
10
|
+
* When `circuitOpenForMs` is set to a positive number, the endpoint emits a
|
|
11
|
+
* `compaction_circuit_open` event with reason `3_consecutive_failures`
|
|
12
|
+
* (matching the event shape produced by `trackCompactionOutcome` in
|
|
13
|
+
* `conversation-agent-loop.ts`). Passing `circuitOpenForMs: 0` clears the
|
|
14
|
+
* open-until timestamp and emits `compaction_circuit_closed`, mirroring the
|
|
15
|
+
* transition event the daemon emits on the open → closed edge.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
|
|
20
|
+
import { getConfig } from "../../../config/loader.js";
|
|
21
|
+
import { estimatePromptTokens } from "../../../context/token-estimator.js";
|
|
22
|
+
import type { Conversation } from "../../../daemon/conversation.js";
|
|
23
|
+
import { httpError } from "../../http-errors.js";
|
|
24
|
+
import type { RouteDefinition } from "../../http-router.js";
|
|
25
|
+
import { conversationNotFoundResponse } from "./conversation-not-found.js";
|
|
26
|
+
import type { PlaygroundRouteDeps } from "./deps.js";
|
|
27
|
+
import { assertPlaygroundEnabled } from "./guard.js";
|
|
28
|
+
|
|
29
|
+
const InjectBodySchema = z.object({
|
|
30
|
+
consecutiveFailures: z.number().int().min(0).max(10).optional(),
|
|
31
|
+
circuitOpenForMs: z
|
|
32
|
+
.number()
|
|
33
|
+
.int()
|
|
34
|
+
.min(0)
|
|
35
|
+
.max(24 * 60 * 60 * 1000)
|
|
36
|
+
.optional(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export function injectFailuresRouteDefinitions(
|
|
40
|
+
deps: PlaygroundRouteDeps,
|
|
41
|
+
): RouteDefinition[] {
|
|
42
|
+
return [
|
|
43
|
+
{
|
|
44
|
+
endpoint: "conversations/:id/playground/inject-compaction-failures",
|
|
45
|
+
method: "POST",
|
|
46
|
+
policyKey: "conversations/playground/inject-failures",
|
|
47
|
+
summary:
|
|
48
|
+
"Directly mutate compaction circuit-breaker state (dev-only playground)",
|
|
49
|
+
tags: ["playground"],
|
|
50
|
+
requestBody: InjectBodySchema,
|
|
51
|
+
handler: async ({ req, params }) => {
|
|
52
|
+
const gate = assertPlaygroundEnabled(deps);
|
|
53
|
+
if (gate) return gate;
|
|
54
|
+
|
|
55
|
+
const conversation = await deps.getConversationById(params.id);
|
|
56
|
+
if (!conversation) {
|
|
57
|
+
return conversationNotFoundResponse(params.id);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let rawBody: unknown = {};
|
|
61
|
+
try {
|
|
62
|
+
const contentLength = req.headers.get("content-length");
|
|
63
|
+
if (contentLength !== "0" && req.body !== null) {
|
|
64
|
+
rawBody = await req.json();
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
return httpError("BAD_REQUEST", "Invalid JSON body", 400);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const parsed = InjectBodySchema.safeParse(rawBody ?? {});
|
|
71
|
+
if (!parsed.success) {
|
|
72
|
+
return httpError("BAD_REQUEST", parsed.error.message, 400);
|
|
73
|
+
}
|
|
74
|
+
const { consecutiveFailures, circuitOpenForMs } = parsed.data;
|
|
75
|
+
|
|
76
|
+
if (consecutiveFailures !== undefined) {
|
|
77
|
+
conversation.consecutiveCompactionFailures = consecutiveFailures;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (circuitOpenForMs !== undefined) {
|
|
81
|
+
if (circuitOpenForMs === 0) {
|
|
82
|
+
// Mirror `trackCompactionOutcome()` (and `reset-circuit.ts`) —
|
|
83
|
+
// emit `compaction_circuit_closed` only on the open→closed
|
|
84
|
+
// transition so clients don't receive a redundant close event
|
|
85
|
+
// when the breaker was already closed.
|
|
86
|
+
if (conversation.compactionCircuitOpenUntil !== null) {
|
|
87
|
+
conversation.compactionCircuitOpenUntil = null;
|
|
88
|
+
conversation.sendToClient({
|
|
89
|
+
type: "compaction_circuit_closed",
|
|
90
|
+
conversationId: conversation.conversationId,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
const openUntil = Date.now() + circuitOpenForMs;
|
|
95
|
+
conversation.compactionCircuitOpenUntil = openUntil;
|
|
96
|
+
conversation.sendToClient({
|
|
97
|
+
type: "compaction_circuit_open",
|
|
98
|
+
conversationId: conversation.conversationId,
|
|
99
|
+
reason: "3_consecutive_failures",
|
|
100
|
+
openUntil,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return Response.json(buildCompactionState(conversation));
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Local state-builder — identical in shape to `buildCompactionStateResponse`
|
|
113
|
+
// in `state.ts` and the local copy in `reset-circuit.ts`. A follow-up cleanup
|
|
114
|
+
// can consolidate these onto `state.ts`'s exported helper.
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
export interface CompactionStateResponse {
|
|
118
|
+
estimatedInputTokens: number;
|
|
119
|
+
maxInputTokens: number;
|
|
120
|
+
compactThresholdRatio: number;
|
|
121
|
+
thresholdTokens: number;
|
|
122
|
+
messageCount: number;
|
|
123
|
+
contextCompactedMessageCount: number;
|
|
124
|
+
contextCompactedAt: number | null;
|
|
125
|
+
consecutiveCompactionFailures: number;
|
|
126
|
+
compactionCircuitOpenUntil: number | null;
|
|
127
|
+
isCircuitOpen: boolean;
|
|
128
|
+
isCompactionEnabled: boolean;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function buildCompactionState(
|
|
132
|
+
conversation: Conversation,
|
|
133
|
+
): CompactionStateResponse {
|
|
134
|
+
const ctxConfig = getConfig().llm.default.contextWindow;
|
|
135
|
+
const maxInputTokens = ctxConfig.maxInputTokens;
|
|
136
|
+
const compactThresholdRatio = ctxConfig.compactThreshold;
|
|
137
|
+
const isCompactionEnabled = ctxConfig.enabled;
|
|
138
|
+
const thresholdTokens = Math.floor(maxInputTokens * compactThresholdRatio);
|
|
139
|
+
|
|
140
|
+
const messages = conversation.getMessages();
|
|
141
|
+
const estimatedInputTokens = estimatePromptTokens(messages);
|
|
142
|
+
const circuitOpenUntil = conversation.compactionCircuitOpenUntil;
|
|
143
|
+
const isCircuitOpen =
|
|
144
|
+
circuitOpenUntil !== null && Date.now() < circuitOpenUntil;
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
estimatedInputTokens,
|
|
148
|
+
maxInputTokens,
|
|
149
|
+
compactThresholdRatio,
|
|
150
|
+
thresholdTokens,
|
|
151
|
+
messageCount: messages.length,
|
|
152
|
+
contextCompactedMessageCount: conversation.contextCompactedMessageCount,
|
|
153
|
+
contextCompactedAt: conversation.contextCompactedAt,
|
|
154
|
+
consecutiveCompactionFailures: conversation.consecutiveCompactionFailures,
|
|
155
|
+
compactionCircuitOpenUntil: circuitOpenUntil,
|
|
156
|
+
isCircuitOpen,
|
|
157
|
+
isCompactionEnabled,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /v1/conversations/:id/playground/reset-compaction-circuit
|
|
3
|
+
*
|
|
4
|
+
* Dev-only playground endpoint that clears the compaction circuit-breaker
|
|
5
|
+
* state on a live conversation. Intended for reproducing flows that normally
|
|
6
|
+
* require three real summary-LLM failures to trigger — without this hatch,
|
|
7
|
+
* exercising the "auto-compaction paused" banner requires bespoke fault
|
|
8
|
+
* injection.
|
|
9
|
+
*
|
|
10
|
+
* Behavior:
|
|
11
|
+
* - `consecutiveCompactionFailures` is always zeroed.
|
|
12
|
+
* - `compactionCircuitOpenUntil` is cleared to null only when it was set;
|
|
13
|
+
* the `compaction_circuit_closed` event is emitted on the open→closed
|
|
14
|
+
* transition so the Swift banner dismisses immediately, mirroring the
|
|
15
|
+
* behavior of a successful compaction in `trackCompactionOutcome()`.
|
|
16
|
+
* Calling this endpoint while the breaker is already closed is a no-op
|
|
17
|
+
* on the event channel — it never emits a redundant close event.
|
|
18
|
+
*
|
|
19
|
+
* Guarded by `assertPlaygroundEnabled()` — the route 404s when the
|
|
20
|
+
* `compaction-playground` feature flag is disabled, so the entire surface
|
|
21
|
+
* is invisible in production.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { getConfig } from "../../../config/loader.js";
|
|
25
|
+
import { estimatePromptTokens } from "../../../context/token-estimator.js";
|
|
26
|
+
import type { Conversation } from "../../../daemon/conversation.js";
|
|
27
|
+
import type { RouteDefinition } from "../../http-router.js";
|
|
28
|
+
// Import directly from the source modules (not ./index.js) — index.ts imports
|
|
29
|
+
// this file's `resetCircuitRouteDefinitions`, so pulling its re-exports back
|
|
30
|
+
// through the barrel would create a cycle.
|
|
31
|
+
import { conversationNotFoundResponse } from "./conversation-not-found.js";
|
|
32
|
+
import type { PlaygroundRouteDeps } from "./deps.js";
|
|
33
|
+
import { assertPlaygroundEnabled } from "./guard.js";
|
|
34
|
+
|
|
35
|
+
export function resetCircuitRouteDefinitions(
|
|
36
|
+
deps: PlaygroundRouteDeps,
|
|
37
|
+
): RouteDefinition[] {
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
endpoint: "conversations/:id/playground/reset-compaction-circuit",
|
|
41
|
+
method: "POST",
|
|
42
|
+
policyKey: "conversations/playground/reset-circuit",
|
|
43
|
+
summary: "Clear compaction circuit-breaker state (dev-only playground)",
|
|
44
|
+
tags: ["playground"],
|
|
45
|
+
handler: async ({ params }) => {
|
|
46
|
+
const gate = assertPlaygroundEnabled(deps);
|
|
47
|
+
if (gate) return gate;
|
|
48
|
+
|
|
49
|
+
const conversation = await deps.getConversationById(params.id);
|
|
50
|
+
if (!conversation) {
|
|
51
|
+
return conversationNotFoundResponse(params.id);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
conversation.consecutiveCompactionFailures = 0;
|
|
55
|
+
if (conversation.compactionCircuitOpenUntil !== null) {
|
|
56
|
+
conversation.compactionCircuitOpenUntil = null;
|
|
57
|
+
// Mirror `trackCompactionOutcome()` — emit only on the open→closed
|
|
58
|
+
// transition so clients don't receive a redundant close event when
|
|
59
|
+
// the breaker was already closed.
|
|
60
|
+
conversation.sendToClient({
|
|
61
|
+
type: "compaction_circuit_closed",
|
|
62
|
+
conversationId: conversation.conversationId,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return Response.json(buildCompactionState(conversation));
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Local state-builder with the same shape as the (future) shared
|
|
74
|
+
* `CompactionStateResponse`. Sibling PRs (7, 9) carry near-identical copies;
|
|
75
|
+
* PR 9 extracts a consolidated version and this local copy can be deleted at
|
|
76
|
+
* that cleanup step.
|
|
77
|
+
*/
|
|
78
|
+
function buildCompactionState(conversation: Conversation): {
|
|
79
|
+
estimatedInputTokens: number;
|
|
80
|
+
maxInputTokens: number;
|
|
81
|
+
compactThresholdRatio: number;
|
|
82
|
+
thresholdTokens: number;
|
|
83
|
+
messageCount: number;
|
|
84
|
+
contextCompactedMessageCount: number;
|
|
85
|
+
contextCompactedAt: number | null;
|
|
86
|
+
consecutiveCompactionFailures: number;
|
|
87
|
+
compactionCircuitOpenUntil: number | null;
|
|
88
|
+
isCircuitOpen: boolean;
|
|
89
|
+
isCompactionEnabled: boolean;
|
|
90
|
+
} {
|
|
91
|
+
const config = getConfig();
|
|
92
|
+
const contextWindow = config.llm.default.contextWindow;
|
|
93
|
+
const messages = conversation.getMessages();
|
|
94
|
+
const estimatedInputTokens = estimatePromptTokens(messages);
|
|
95
|
+
const maxInputTokens = contextWindow.maxInputTokens;
|
|
96
|
+
const compactThresholdRatio = contextWindow.compactThreshold;
|
|
97
|
+
const thresholdTokens = Math.floor(maxInputTokens * compactThresholdRatio);
|
|
98
|
+
const isCircuitOpen =
|
|
99
|
+
conversation.compactionCircuitOpenUntil !== null &&
|
|
100
|
+
Date.now() < conversation.compactionCircuitOpenUntil;
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
estimatedInputTokens,
|
|
104
|
+
maxInputTokens,
|
|
105
|
+
compactThresholdRatio,
|
|
106
|
+
thresholdTokens,
|
|
107
|
+
messageCount: messages.length,
|
|
108
|
+
contextCompactedMessageCount: conversation.contextCompactedMessageCount,
|
|
109
|
+
contextCompactedAt: conversation.contextCompactedAt,
|
|
110
|
+
consecutiveCompactionFailures: conversation.consecutiveCompactionFailures,
|
|
111
|
+
compactionCircuitOpenUntil: conversation.compactionCircuitOpenUntil,
|
|
112
|
+
isCircuitOpen,
|
|
113
|
+
isCompactionEnabled: contextWindow.enabled,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /v1/playground/seed-conversation
|
|
3
|
+
*
|
|
4
|
+
* Creates a synthetic conversation for compaction testing. Inserts N
|
|
5
|
+
* user/assistant message pairs of roughly `avgTokensPerTurn` tokens each and
|
|
6
|
+
* returns the new conversation id plus an estimated prompt-token count.
|
|
7
|
+
*
|
|
8
|
+
* Seeded conversations are prefixed with `[Playground] ` so other playground
|
|
9
|
+
* endpoints (e.g. seeded-conversations list/delete) can filter by prefix.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
import { estimatePromptTokens } from "../../../context/token-estimator.js";
|
|
15
|
+
import type { Message } from "../../../providers/types.js";
|
|
16
|
+
import { httpError } from "../../http-errors.js";
|
|
17
|
+
import type { RouteDefinition } from "../../http-router.js";
|
|
18
|
+
import { assertPlaygroundEnabled, type PlaygroundRouteDeps } from "./index.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Title prefix applied to every seeded-playground conversation. Exported so
|
|
22
|
+
* sibling playground endpoints (list/delete) can share the exact string
|
|
23
|
+
* rather than duplicating a literal.
|
|
24
|
+
*/
|
|
25
|
+
export const PLAYGROUND_TITLE_PREFIX = "[Playground] ";
|
|
26
|
+
|
|
27
|
+
const SeedBodySchema = z.object({
|
|
28
|
+
turns: z.number().int().positive().max(500),
|
|
29
|
+
avgTokensPerTurn: z
|
|
30
|
+
.number()
|
|
31
|
+
.int()
|
|
32
|
+
.positive()
|
|
33
|
+
.max(5000)
|
|
34
|
+
.optional()
|
|
35
|
+
.default(500),
|
|
36
|
+
title: z.string().trim().max(120).optional(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const LOREM_BASE =
|
|
40
|
+
"Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ";
|
|
41
|
+
|
|
42
|
+
export function seedConversationRouteDefinitions(
|
|
43
|
+
deps: PlaygroundRouteDeps,
|
|
44
|
+
): RouteDefinition[] {
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
endpoint: "playground/seed-conversation",
|
|
48
|
+
method: "POST",
|
|
49
|
+
policyKey: "playground/seed-conversation",
|
|
50
|
+
summary: "Create a synthetic seeded conversation for compaction testing",
|
|
51
|
+
tags: ["playground"],
|
|
52
|
+
requestBody: SeedBodySchema,
|
|
53
|
+
handler: async ({ req }) => {
|
|
54
|
+
const gate = assertPlaygroundEnabled(deps);
|
|
55
|
+
if (gate) return gate;
|
|
56
|
+
|
|
57
|
+
let rawBody: unknown;
|
|
58
|
+
try {
|
|
59
|
+
rawBody = await req.json();
|
|
60
|
+
} catch {
|
|
61
|
+
return httpError("BAD_REQUEST", "Invalid JSON body", 400);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const parsed = SeedBodySchema.safeParse(rawBody);
|
|
65
|
+
if (!parsed.success) {
|
|
66
|
+
return httpError("BAD_REQUEST", parsed.error.message, 400);
|
|
67
|
+
}
|
|
68
|
+
const { turns, avgTokensPerTurn, title } = parsed.data;
|
|
69
|
+
|
|
70
|
+
// Derive effective title: strip an accidental prefix first so a
|
|
71
|
+
// caller passing "[Playground] foo" doesn't end up with
|
|
72
|
+
// "[Playground] [Playground] foo".
|
|
73
|
+
const userSuppliedTitle =
|
|
74
|
+
title && title.length > 0
|
|
75
|
+
? title
|
|
76
|
+
: new Date().toISOString().slice(0, 19);
|
|
77
|
+
const withoutPrefix = userSuppliedTitle.startsWith(
|
|
78
|
+
PLAYGROUND_TITLE_PREFIX,
|
|
79
|
+
)
|
|
80
|
+
? userSuppliedTitle.slice(PLAYGROUND_TITLE_PREFIX.length)
|
|
81
|
+
: userSuppliedTitle;
|
|
82
|
+
const effectiveTitle = PLAYGROUND_TITLE_PREFIX + withoutPrefix;
|
|
83
|
+
|
|
84
|
+
const { id: conversationId } =
|
|
85
|
+
await deps.createConversation(effectiveTitle);
|
|
86
|
+
|
|
87
|
+
// Generate lorem-ipsum-style text. ~4 chars/token is the rough
|
|
88
|
+
// approximation the rest of the daemon uses. Paragraph is repeated
|
|
89
|
+
// and sliced to the target byte count, and a "Turn N" prefix keeps
|
|
90
|
+
// each message unique so downstream search/compaction doesn't
|
|
91
|
+
// collapse them.
|
|
92
|
+
const charsPerMessage = avgTokensPerTurn * 4;
|
|
93
|
+
|
|
94
|
+
const messages: Array<{ role: "user" | "assistant"; text: string }> =
|
|
95
|
+
[];
|
|
96
|
+
for (let i = 0; i < turns; i++) {
|
|
97
|
+
const userBase = `Turn ${i + 1} user message: ` + LOREM_BASE;
|
|
98
|
+
const userText = userBase
|
|
99
|
+
.repeat(Math.ceil(charsPerMessage / userBase.length))
|
|
100
|
+
.slice(0, charsPerMessage);
|
|
101
|
+
const asstBase = `Turn ${i + 1} assistant response: ` + LOREM_BASE;
|
|
102
|
+
const asstText = asstBase
|
|
103
|
+
.repeat(Math.ceil(charsPerMessage / asstBase.length))
|
|
104
|
+
.slice(0, charsPerMessage);
|
|
105
|
+
messages.push({ role: "user", text: userText });
|
|
106
|
+
messages.push({ role: "assistant", text: asstText });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const msg of messages) {
|
|
110
|
+
const contentJson = JSON.stringify([
|
|
111
|
+
{ type: "text", text: msg.text },
|
|
112
|
+
]);
|
|
113
|
+
// Skip memory/vector indexing for seeded synthetic messages — the
|
|
114
|
+
// lorem-ipsum content has no semantic value and would otherwise
|
|
115
|
+
// spam the embedding pipeline (2 embeddings per turn × up to 500
|
|
116
|
+
// turns) and pollute the vector store.
|
|
117
|
+
await deps.addMessage(conversationId, msg.role, contentJson, {
|
|
118
|
+
skipIndexing: true,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Reconstruct the in-memory Message[] shape estimatePromptTokens
|
|
123
|
+
// expects so the returned estimate matches what compaction will
|
|
124
|
+
// see once the conversation is loaded.
|
|
125
|
+
const estimatorMessages: Message[] = messages.map((m) => ({
|
|
126
|
+
role: m.role,
|
|
127
|
+
content: [{ type: "text", text: m.text }],
|
|
128
|
+
}));
|
|
129
|
+
const estimatedTokens = estimatePromptTokens(estimatorMessages);
|
|
130
|
+
|
|
131
|
+
return Response.json({
|
|
132
|
+
conversationId,
|
|
133
|
+
messagesInserted: messages.length,
|
|
134
|
+
estimatedTokens,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playground-only list/delete endpoints for conversations seeded by the
|
|
3
|
+
* seed-conversation route. Prefix-gated on the title (every seeded
|
|
4
|
+
* conversation is titled `[Playground] ...`) so a flag-on caller cannot use
|
|
5
|
+
* these routes to list or delete unrelated conversations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { httpError } from "../../http-errors.js";
|
|
9
|
+
import type { RouteDefinition } from "../../http-router.js";
|
|
10
|
+
import { assertPlaygroundEnabled, type PlaygroundRouteDeps } from "./index.js";
|
|
11
|
+
import { PLAYGROUND_TITLE_PREFIX } from "./seed-conversation.js";
|
|
12
|
+
|
|
13
|
+
export function seededConversationsRouteDefinitions(
|
|
14
|
+
deps: PlaygroundRouteDeps,
|
|
15
|
+
): RouteDefinition[] {
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
endpoint: "playground/seeded-conversations",
|
|
19
|
+
method: "GET",
|
|
20
|
+
policyKey: "playground/seeded-conversations/list",
|
|
21
|
+
summary: "List conversations created by the seed-conversation endpoint",
|
|
22
|
+
tags: ["playground"],
|
|
23
|
+
handler: async () => {
|
|
24
|
+
const gate = assertPlaygroundEnabled(deps);
|
|
25
|
+
if (gate) return gate;
|
|
26
|
+
const conversations = deps.listConversationsByTitlePrefix(
|
|
27
|
+
PLAYGROUND_TITLE_PREFIX,
|
|
28
|
+
);
|
|
29
|
+
return Response.json({ conversations });
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
endpoint: "playground/seeded-conversations/:id",
|
|
34
|
+
method: "DELETE",
|
|
35
|
+
policyKey: "playground/seeded-conversations/delete-one",
|
|
36
|
+
summary: "Delete a single seeded conversation (prefix-gated)",
|
|
37
|
+
tags: ["playground"],
|
|
38
|
+
handler: async ({ params }) => {
|
|
39
|
+
const gate = assertPlaygroundEnabled(deps);
|
|
40
|
+
if (gate) return gate;
|
|
41
|
+
|
|
42
|
+
// The in-memory `Conversation` object does not carry the DB title,
|
|
43
|
+
// and the seeded row may not be loaded into memory at all. Use the
|
|
44
|
+
// prefix-filtered DB listing as the authoritative membership check
|
|
45
|
+
// so we reject attempts to delete arbitrary conversations via this
|
|
46
|
+
// endpoint even when the flag is on.
|
|
47
|
+
const seeded = deps
|
|
48
|
+
.listConversationsByTitlePrefix(PLAYGROUND_TITLE_PREFIX)
|
|
49
|
+
.find((c) => c.id === params.id);
|
|
50
|
+
if (!seeded) {
|
|
51
|
+
return httpError("FORBIDDEN", "Not a playground conversation", 403);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const deleted = deps.deleteConversationById(params.id);
|
|
55
|
+
return Response.json({ deletedCount: deleted ? 1 : 0 });
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
endpoint: "playground/seeded-conversations",
|
|
60
|
+
method: "DELETE",
|
|
61
|
+
policyKey: "playground/seeded-conversations/delete-all",
|
|
62
|
+
summary: "Delete every seeded playground conversation (prefix-gated)",
|
|
63
|
+
tags: ["playground"],
|
|
64
|
+
handler: async () => {
|
|
65
|
+
const gate = assertPlaygroundEnabled(deps);
|
|
66
|
+
if (gate) return gate;
|
|
67
|
+
const candidates = deps.listConversationsByTitlePrefix(
|
|
68
|
+
PLAYGROUND_TITLE_PREFIX,
|
|
69
|
+
);
|
|
70
|
+
let deletedCount = 0;
|
|
71
|
+
for (const c of candidates) {
|
|
72
|
+
if (deps.deleteConversationById(c.id)) deletedCount++;
|
|
73
|
+
}
|
|
74
|
+
return Response.json({ deletedCount });
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /v1/conversations/:id/playground/compaction-state
|
|
3
|
+
*
|
|
4
|
+
* Read-only view of compaction-relevant state for a conversation. Returns the
|
|
5
|
+
* token estimate, the configured maxInputTokens / compactThreshold, the
|
|
6
|
+
* derived threshold token count, current message count, compaction-progress
|
|
7
|
+
* counters, and circuit-breaker status.
|
|
8
|
+
*
|
|
9
|
+
* The endpoint is gated by the `compaction-playground` feature flag via the
|
|
10
|
+
* shared `assertPlaygroundEnabled` guard — when disabled the whole surface is
|
|
11
|
+
* invisible in production.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { getConfig } from "../../../config/loader.js";
|
|
15
|
+
import { estimatePromptTokens } from "../../../context/token-estimator.js";
|
|
16
|
+
import type { Conversation } from "../../../daemon/conversation.js";
|
|
17
|
+
import type { RouteDefinition } from "../../http-router.js";
|
|
18
|
+
import { conversationNotFoundResponse } from "./conversation-not-found.js";
|
|
19
|
+
import { assertPlaygroundEnabled, type PlaygroundRouteDeps } from "./index.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build the `CompactionStateResponse` payload used by:
|
|
23
|
+
* - GET ...playground/compaction-state (this file)
|
|
24
|
+
* - POST ...playground/inject-compaction-failures (PR 7)
|
|
25
|
+
* - POST ...playground/reset-compaction-circuit (PR 8)
|
|
26
|
+
*
|
|
27
|
+
* Exported so follow-up cleanup PRs can replace inline copies in PR 7 / PR 8
|
|
28
|
+
* with this canonical implementation.
|
|
29
|
+
*/
|
|
30
|
+
export function buildCompactionStateResponse(conversation: Conversation) {
|
|
31
|
+
const messages = conversation.getMessages();
|
|
32
|
+
const estimatedInputTokens = estimatePromptTokens(messages);
|
|
33
|
+
const cfg = getConfig().llm.default.contextWindow;
|
|
34
|
+
const maxInputTokens = cfg.maxInputTokens;
|
|
35
|
+
const compactThresholdRatio = cfg.compactThreshold;
|
|
36
|
+
const thresholdTokens = Math.floor(maxInputTokens * compactThresholdRatio);
|
|
37
|
+
const compactionCircuitOpenUntil = conversation.compactionCircuitOpenUntil;
|
|
38
|
+
return {
|
|
39
|
+
estimatedInputTokens,
|
|
40
|
+
maxInputTokens,
|
|
41
|
+
compactThresholdRatio,
|
|
42
|
+
thresholdTokens,
|
|
43
|
+
messageCount: messages.length,
|
|
44
|
+
contextCompactedMessageCount: conversation.contextCompactedMessageCount,
|
|
45
|
+
contextCompactedAt: conversation.contextCompactedAt,
|
|
46
|
+
consecutiveCompactionFailures: conversation.consecutiveCompactionFailures,
|
|
47
|
+
compactionCircuitOpenUntil,
|
|
48
|
+
isCircuitOpen:
|
|
49
|
+
compactionCircuitOpenUntil !== null &&
|
|
50
|
+
Date.now() < compactionCircuitOpenUntil,
|
|
51
|
+
isCompactionEnabled: cfg.enabled,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function stateRouteDefinitions(
|
|
56
|
+
deps: PlaygroundRouteDeps,
|
|
57
|
+
): RouteDefinition[] {
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
endpoint: "conversations/:id/playground/compaction-state",
|
|
61
|
+
method: "GET",
|
|
62
|
+
policyKey: "conversations/playground/state",
|
|
63
|
+
summary: "Read current compaction state for a conversation",
|
|
64
|
+
tags: ["playground"],
|
|
65
|
+
handler: async ({ params }) => {
|
|
66
|
+
const gate = assertPlaygroundEnabled(deps);
|
|
67
|
+
if (gate) return gate;
|
|
68
|
+
|
|
69
|
+
const conversation = await deps.getConversationById(params.id);
|
|
70
|
+
if (!conversation) {
|
|
71
|
+
return conversationNotFoundResponse(params.id);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return Response.json(buildCompactionStateResponse(conversation));
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
}
|