@vellumai/assistant 0.8.10 → 0.8.11-staging.1
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/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- package/src/plugins/plugin-skill-contributions.ts +0 -292
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Exposes spawn, steer, cancel, close, sessions, and permission operations
|
|
5
5
|
* over HTTP and IPC.
|
|
6
6
|
*/
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
|
|
7
9
|
import { and, desc, eq, inArray, notInArray } from "drizzle-orm";
|
|
8
10
|
import { z } from "zod";
|
|
9
11
|
|
|
@@ -13,16 +15,20 @@ import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
|
|
|
13
15
|
import { formatResolveFailure } from "../../acp/resolve-agent.js";
|
|
14
16
|
import { AcpResumeError } from "../../acp/session-manager.js";
|
|
15
17
|
import type { AcpSessionState } from "../../acp/types.js";
|
|
18
|
+
import { getConfig } from "../../config/loader.js";
|
|
16
19
|
import { getDb } from "../../memory/db-connection.js";
|
|
17
20
|
import { rawChanges } from "../../memory/raw-query.js";
|
|
18
21
|
import { acpSessionHistory } from "../../memory/schema.js";
|
|
22
|
+
import type { UserDecision } from "../../permissions/types.js";
|
|
19
23
|
import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
|
|
20
24
|
import { getLogger } from "../../util/logger.js";
|
|
21
25
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
26
|
+
import * as pendingInteractions from "../pending-interactions.js";
|
|
22
27
|
import {
|
|
23
28
|
BadRequestError,
|
|
24
29
|
ConflictError,
|
|
25
30
|
FailedDependencyError,
|
|
31
|
+
ForbiddenError,
|
|
26
32
|
NotFoundError,
|
|
27
33
|
} from "./errors.js";
|
|
28
34
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
@@ -53,7 +59,109 @@ type SessionEntry = z.infer<typeof sessionEntrySchema>;
|
|
|
53
59
|
// Handlers
|
|
54
60
|
// ---------------------------------------------------------------------------
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Surface a high-risk confirmation for an ACP route that starts a host agent
|
|
64
|
+
* subprocess (`POST /v1/acp/spawn`, and the resume branch of
|
|
65
|
+
* `POST /v1/acp/:id/steer`) and resolve to the guardian's decision.
|
|
66
|
+
*
|
|
67
|
+
* Unlike the `acp_spawn` / `acp_steer` skill tools (which are dispatched
|
|
68
|
+
* through `ToolExecutor`/`PermissionChecker` and so inherit the descriptors'
|
|
69
|
+
* `risk: "high"` approval prompt), these HTTP/IPC routes reach the ACP
|
|
70
|
+
* session manager directly. A spawned ACP agent is a host subprocess with
|
|
71
|
+
* advertised filesystem + terminal access whose in-session permission
|
|
72
|
+
* requests are auto-allowed, so starting one is exactly the high-risk action
|
|
73
|
+
* the descriptors gate. Without this prompt a caller holding only
|
|
74
|
+
* `chat.write` could start one with no human in the loop — directly via
|
|
75
|
+
* spawn (arbitrary `cwd`), or by resuming a terminal session via steer (its
|
|
76
|
+
* persisted `cwd`, enumerable through `GET /v1/acp/sessions`) (ATL-822).
|
|
77
|
+
*
|
|
78
|
+
* The confirmation is registered with `directResolve` so `POST /v1/confirm`
|
|
79
|
+
* resolves it without a live `Conversation` object. That route requires the
|
|
80
|
+
* bound guardian (`approval.write` + `requireGuardian`) — a strictly higher
|
|
81
|
+
* bar than the `chat.write` these routes demand — so the `chat.write` caller
|
|
82
|
+
* cannot self-approve. Timeout and client disconnect both deny, matching the
|
|
83
|
+
* tool path's "no interactive client → do not run" posture.
|
|
84
|
+
*/
|
|
85
|
+
function awaitRouteApproval(args: {
|
|
86
|
+
toolName: string;
|
|
87
|
+
input: Record<string, unknown>;
|
|
88
|
+
conversationId: string;
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
}): Promise<UserDecision> {
|
|
91
|
+
const { toolName, input, conversationId, signal } = args;
|
|
92
|
+
const requestId = randomUUID();
|
|
93
|
+
// Mirror the interactive prompt timeout; fall back to the schema default
|
|
94
|
+
// (300s) if the timeouts block is somehow absent so the gate never wedges.
|
|
95
|
+
const timeoutMs = (getConfig().timeouts?.permissionTimeoutSec ?? 300) * 1000;
|
|
96
|
+
|
|
97
|
+
return new Promise<UserDecision>((resolve) => {
|
|
98
|
+
let settled = false;
|
|
99
|
+
const settle = (
|
|
100
|
+
decision: UserDecision,
|
|
101
|
+
state: "approved" | "rejected" | "cancelled",
|
|
102
|
+
) => {
|
|
103
|
+
if (settled) return;
|
|
104
|
+
settled = true;
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
signal?.removeEventListener("abort", onAbort);
|
|
107
|
+
// Idempotent: the `/v1/confirm` directResolve path already removed the
|
|
108
|
+
// entry before invoking us, so this is a no-op there and only fires
|
|
109
|
+
// `interaction_resolved` for the timeout/abort paths.
|
|
110
|
+
pendingInteractions.resolve(requestId, state);
|
|
111
|
+
resolve(decision);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const timer = setTimeout(() => {
|
|
115
|
+
log.warn(
|
|
116
|
+
{ requestId, toolName, conversationId },
|
|
117
|
+
"ACP route approval timed out — denying",
|
|
118
|
+
);
|
|
119
|
+
settle("deny", "cancelled");
|
|
120
|
+
}, timeoutMs);
|
|
121
|
+
|
|
122
|
+
const onAbort = () => settle("deny", "cancelled");
|
|
123
|
+
if (signal?.aborted) {
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
resolve("deny");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
129
|
+
|
|
130
|
+
pendingInteractions.register(requestId, {
|
|
131
|
+
conversationId,
|
|
132
|
+
kind: "confirmation",
|
|
133
|
+
confirmationDetails: {
|
|
134
|
+
toolName,
|
|
135
|
+
input,
|
|
136
|
+
riskLevel: "high",
|
|
137
|
+
executionTarget: "host",
|
|
138
|
+
allowlistOptions: [],
|
|
139
|
+
scopeOptions: [],
|
|
140
|
+
persistentDecisionsAllowed: false,
|
|
141
|
+
},
|
|
142
|
+
directResolve: (decision) =>
|
|
143
|
+
settle(decision, decision === "allow" ? "approved" : "rejected"),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
broadcastMessage(
|
|
147
|
+
{
|
|
148
|
+
type: "confirmation_request",
|
|
149
|
+
requestId,
|
|
150
|
+
toolName,
|
|
151
|
+
input,
|
|
152
|
+
riskLevel: "high",
|
|
153
|
+
executionTarget: "host",
|
|
154
|
+
allowlistOptions: [],
|
|
155
|
+
scopeOptions: [],
|
|
156
|
+
conversationId,
|
|
157
|
+
persistentDecisionsAllowed: false,
|
|
158
|
+
},
|
|
159
|
+
conversationId,
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function spawnSession({ body, abortSignal }: RouteHandlerArgs) {
|
|
57
165
|
const agent = body?.agent as string | undefined;
|
|
58
166
|
const task = body?.task as string | undefined;
|
|
59
167
|
const conversationId = body?.conversationId as string | undefined;
|
|
@@ -63,6 +171,22 @@ async function spawnSession({ body }: RouteHandlerArgs) {
|
|
|
63
171
|
throw new BadRequestError("agent, task, and conversationId are required");
|
|
64
172
|
}
|
|
65
173
|
|
|
174
|
+
// High-risk approval gate. Block BEFORE any side effects — resolution can
|
|
175
|
+
// trigger a `bun` global install (auto-install.ts) and `manager.spawn`
|
|
176
|
+
// launches the host subprocess — so an unapproved request mutates nothing.
|
|
177
|
+
const decision = await awaitRouteApproval({
|
|
178
|
+
toolName: "acp_spawn",
|
|
179
|
+
input: { agent, task, cwd },
|
|
180
|
+
conversationId,
|
|
181
|
+
signal: abortSignal,
|
|
182
|
+
});
|
|
183
|
+
if (decision !== "allow") {
|
|
184
|
+
throw new ForbiddenError(
|
|
185
|
+
"Spawning an ACP coding agent requires guardian approval, which was " +
|
|
186
|
+
"not granted.",
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
66
190
|
// Resolve the agent, silently auto-installing a missing allowlisted
|
|
67
191
|
// adapter binary (see acp/auto-install.ts). Shared with the skill-tool
|
|
68
192
|
// path in tools/acp/spawn.ts; only the transport mapping differs.
|
|
@@ -117,6 +241,43 @@ async function steerSession({ pathParams, body }: RouteHandlerArgs) {
|
|
|
117
241
|
// broadcastMessage plays the sender role spawnSession gives it, so
|
|
118
242
|
// connected clients render the session.
|
|
119
243
|
const manager = getAcpSessionManager();
|
|
244
|
+
|
|
245
|
+
// Resume re-spawns the host agent subprocess, so it crosses the same
|
|
246
|
+
// high-risk boundary as spawn and needs guardian approval (ATL-822). A
|
|
247
|
+
// steer of a session still active in memory only redirects an
|
|
248
|
+
// already-running, already-approved process, so it is left unprompted —
|
|
249
|
+
// matching "gate before spawning". A resume only happens when the id is
|
|
250
|
+
// absent from memory AND a resumable history row (non-null cwd) exists.
|
|
251
|
+
if (!manager.getActiveAndPendingIds().includes(id)) {
|
|
252
|
+
const resumable = getDb()
|
|
253
|
+
.select({
|
|
254
|
+
cwd: acpSessionHistory.cwd,
|
|
255
|
+
agentId: acpSessionHistory.agentId,
|
|
256
|
+
parentConversationId: acpSessionHistory.parentConversationId,
|
|
257
|
+
})
|
|
258
|
+
.from(acpSessionHistory)
|
|
259
|
+
.where(eq(acpSessionHistory.id, id))
|
|
260
|
+
.get();
|
|
261
|
+
if (resumable?.cwd != null) {
|
|
262
|
+
// Guardian approval is an out-of-band human action that can take far
|
|
263
|
+
// longer than a client's ack timeout (the macOS client uses 10s; the
|
|
264
|
+
// approval window is permissionTimeoutSec, default 300s). Blocking the
|
|
265
|
+
// HTTP response here would surface as a client transport failure even
|
|
266
|
+
// on an eventual approval. Acknowledge immediately and run approval +
|
|
267
|
+
// resume in the background, streaming via SSE like any other ACP
|
|
268
|
+
// activity. The request abortSignal is deliberately NOT threaded into
|
|
269
|
+
// the wait — the request is already complete.
|
|
270
|
+
void approveThenResume({
|
|
271
|
+
id,
|
|
272
|
+
instruction,
|
|
273
|
+
agentId: resumable.agentId,
|
|
274
|
+
cwd: resumable.cwd,
|
|
275
|
+
conversationId: resumable.parentConversationId,
|
|
276
|
+
});
|
|
277
|
+
return { acpSessionId: id, steered: false, approvalPending: true };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
120
281
|
try {
|
|
121
282
|
const { resumed } = await manager.steerOrResume(
|
|
122
283
|
id,
|
|
@@ -138,6 +299,81 @@ async function steerSession({ pathParams, body }: RouteHandlerArgs) {
|
|
|
138
299
|
}
|
|
139
300
|
}
|
|
140
301
|
|
|
302
|
+
/**
|
|
303
|
+
* Background worker for an approval-gated ACP resume initiated over the steer
|
|
304
|
+
* route. Awaits the guardian decision (see `awaitRouteApproval`), then resumes
|
|
305
|
+
* via the session manager so the agent's output streams back over SSE. A
|
|
306
|
+
* denial, timeout, or resume failure is surfaced as an `acp_session_error` on
|
|
307
|
+
* the parent conversation so the client isn't left with an optimistic steer
|
|
308
|
+
* row and no follow-up. Never throws — it owns the request lifecycle after the
|
|
309
|
+
* route has already acked.
|
|
310
|
+
*/
|
|
311
|
+
async function approveThenResume(args: {
|
|
312
|
+
id: string;
|
|
313
|
+
instruction: string;
|
|
314
|
+
agentId: string;
|
|
315
|
+
cwd: string;
|
|
316
|
+
conversationId: string;
|
|
317
|
+
}): Promise<void> {
|
|
318
|
+
const decision = await awaitRouteApproval({
|
|
319
|
+
toolName: "acp_steer",
|
|
320
|
+
input: {
|
|
321
|
+
acp_session_id: args.id,
|
|
322
|
+
instruction: args.instruction,
|
|
323
|
+
agent: args.agentId,
|
|
324
|
+
cwd: args.cwd,
|
|
325
|
+
},
|
|
326
|
+
conversationId: args.conversationId,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (decision !== "allow") {
|
|
330
|
+
log.info(
|
|
331
|
+
{ acpSessionId: args.id, conversationId: args.conversationId },
|
|
332
|
+
"ACP resume approval not granted — skipping resume",
|
|
333
|
+
);
|
|
334
|
+
broadcastMessage(
|
|
335
|
+
{
|
|
336
|
+
type: "acp_session_error",
|
|
337
|
+
// Key the error by the daemon session id (the value the steer route
|
|
338
|
+
// accepts and ACP SSE consumers index by — see registerSession, where
|
|
339
|
+
// state.id is the broadcast acpSessionId), NOT the persisted protocol
|
|
340
|
+
// id. A denied resume never emits acp_session_spawned to map the
|
|
341
|
+
// protocol id, so a protocol-keyed error would be dropped.
|
|
342
|
+
acpSessionId: args.id,
|
|
343
|
+
error: "Resume was not approved.",
|
|
344
|
+
},
|
|
345
|
+
args.conversationId,
|
|
346
|
+
);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
await getAcpSessionManager().steerOrResume(
|
|
352
|
+
args.id,
|
|
353
|
+
args.instruction,
|
|
354
|
+
broadcastMessage,
|
|
355
|
+
);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
const message =
|
|
358
|
+
err instanceof AcpResumeError || err instanceof Error
|
|
359
|
+
? err.message
|
|
360
|
+
: String(err);
|
|
361
|
+
log.warn(
|
|
362
|
+
{ acpSessionId: args.id, conversationId: args.conversationId, err },
|
|
363
|
+
"Approved ACP resume failed",
|
|
364
|
+
);
|
|
365
|
+
broadcastMessage(
|
|
366
|
+
{
|
|
367
|
+
type: "acp_session_error",
|
|
368
|
+
// Daemon session id (route + SSE key), not the protocol id — see above.
|
|
369
|
+
acpSessionId: args.id,
|
|
370
|
+
error: message,
|
|
371
|
+
},
|
|
372
|
+
args.conversationId,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
141
377
|
async function cancelSession({ pathParams }: RouteHandlerArgs) {
|
|
142
378
|
const id = pathParams?.id as string;
|
|
143
379
|
const manager = getAcpSessionManager();
|
|
@@ -276,7 +512,10 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
276
512
|
"Send a steering instruction to an ACP session. Sessions no longer " +
|
|
277
513
|
"in memory (completed, or lost to a daemon restart) are " +
|
|
278
514
|
"transparently resumed from persisted history first, when the agent " +
|
|
279
|
-
"supports ACP session loading."
|
|
515
|
+
"supports ACP session loading. Resuming a terminal session re-spawns " +
|
|
516
|
+
"the host agent, so it requires guardian approval: the route acks " +
|
|
517
|
+
"immediately with approvalPending=true and performs the resume in the " +
|
|
518
|
+
"background once approved, streaming results over SSE.",
|
|
280
519
|
tags: ["acp"],
|
|
281
520
|
requestBody: z.object({
|
|
282
521
|
instruction: z.string(),
|
|
@@ -290,6 +529,13 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
290
529
|
.describe(
|
|
291
530
|
"True when the session was resumed from persisted history before steering.",
|
|
292
531
|
),
|
|
532
|
+
approvalPending: z
|
|
533
|
+
.boolean()
|
|
534
|
+
.optional()
|
|
535
|
+
.describe(
|
|
536
|
+
"True when the steer triggered a guardian-approval-gated resume " +
|
|
537
|
+
"that will run asynchronously; watch SSE for the outcome.",
|
|
538
|
+
),
|
|
293
539
|
}),
|
|
294
540
|
},
|
|
295
541
|
{
|
|
@@ -114,7 +114,7 @@ async function handleBrowserTabs({ body = {} }: RouteHandlerArgs) {
|
|
|
114
114
|
}
|
|
115
115
|
} else if (targetClientId) {
|
|
116
116
|
// Only scope-clear the targeted client's pin. With the per-(conversationId,
|
|
117
|
-
// clientId) pin store
|
|
117
|
+
// clientId) pin store, passing no clientId to clearPinnedTab
|
|
118
118
|
// would wipe pins for *every* connected client on this conversation
|
|
119
119
|
// and break their routing — so we skip the clear entirely when the
|
|
120
120
|
// caller didn't explicitly target a client. The stale pin (if any)
|
|
@@ -23,6 +23,7 @@ import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
|
23
23
|
import { revokePendingSessions } from "../channel-verification-service.js";
|
|
24
24
|
import {
|
|
25
25
|
cancelOutbound,
|
|
26
|
+
deliverVerificationEmail,
|
|
26
27
|
deliverVerificationSlack,
|
|
27
28
|
normalizeTelegramDestination,
|
|
28
29
|
resendOutbound,
|
|
@@ -139,11 +140,15 @@ export async function handleCreateVerificationSession({
|
|
|
139
140
|
verificationRateLimiter.recordFailure(rateLimitKey);
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
// Dispatch
|
|
143
|
+
// Dispatch delivery from the daemon process (not sandboxed).
|
|
143
144
|
if (result._pendingSlackDm) {
|
|
144
145
|
const { userId, text, assistantId: aid } = result._pendingSlackDm;
|
|
145
146
|
deliverVerificationSlack(userId, text, aid);
|
|
146
147
|
}
|
|
148
|
+
if (result._pendingEmail) {
|
|
149
|
+
const { to, text, subject, assistantId: aid } = result._pendingEmail;
|
|
150
|
+
deliverVerificationEmail(to, text, subject, aid);
|
|
151
|
+
}
|
|
147
152
|
|
|
148
153
|
if (!result.success) {
|
|
149
154
|
if (result.error === "rate_limited") {
|
|
@@ -157,8 +162,8 @@ export async function handleCreateVerificationSession({
|
|
|
157
162
|
);
|
|
158
163
|
}
|
|
159
164
|
|
|
160
|
-
// Strip internal
|
|
161
|
-
const { _pendingSlackDm: _, ...publicResult } = result;
|
|
165
|
+
// Strip internal fields from the response
|
|
166
|
+
const { _pendingSlackDm: _, _pendingEmail: __, ...publicResult } = result;
|
|
162
167
|
return publicResult;
|
|
163
168
|
}
|
|
164
169
|
|
|
@@ -204,11 +209,15 @@ export async function handleResendVerificationSession({
|
|
|
204
209
|
|
|
205
210
|
const result = resendOutbound({ channel, originConversationId });
|
|
206
211
|
|
|
207
|
-
// Dispatch
|
|
212
|
+
// Dispatch delivery from the daemon process (not sandboxed).
|
|
208
213
|
if (result._pendingSlackDm) {
|
|
209
214
|
const { userId, text, assistantId: aid } = result._pendingSlackDm;
|
|
210
215
|
deliverVerificationSlack(userId, text, aid);
|
|
211
216
|
}
|
|
217
|
+
if (result._pendingEmail) {
|
|
218
|
+
const { to, text, subject, assistantId: aid } = result._pendingEmail;
|
|
219
|
+
deliverVerificationEmail(to, text, subject, aid);
|
|
220
|
+
}
|
|
212
221
|
|
|
213
222
|
if (!result.success) {
|
|
214
223
|
if (result.error === "rate_limited") {
|
|
@@ -221,7 +230,7 @@ export async function handleResendVerificationSession({
|
|
|
221
230
|
);
|
|
222
231
|
}
|
|
223
232
|
|
|
224
|
-
const { _pendingSlackDm: _, ...publicResult } = result;
|
|
233
|
+
const { _pendingSlackDm: _, _pendingEmail: __, ...publicResult } = result;
|
|
225
234
|
return publicResult;
|
|
226
235
|
}
|
|
227
236
|
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
|
|
14
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
15
|
-
import { getConfigReadOnly } from "../../config/loader.js";
|
|
16
14
|
import { getDb } from "../../memory/db-connection.js";
|
|
17
15
|
import {
|
|
18
16
|
createConnection,
|
|
@@ -29,18 +27,8 @@ import {
|
|
|
29
27
|
import { setSecureKeyAsync } from "../../security/secure-keys.js";
|
|
30
28
|
import { getLogger } from "../../util/logger.js";
|
|
31
29
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
32
|
-
import { BadRequestError } from "./errors.js";
|
|
33
30
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
34
31
|
|
|
35
|
-
function requireFeatureFlag() {
|
|
36
|
-
const config = getConfigReadOnly();
|
|
37
|
-
if (!isAssistantFeatureFlagEnabled("chatgpt-subscription-auth", config)) {
|
|
38
|
-
throw new BadRequestError(
|
|
39
|
-
"ChatGPT subscription auth is not enabled for this assistant.",
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
32
|
const log = getLogger("chatgpt-subscription-auth");
|
|
45
33
|
|
|
46
34
|
// ---------------------------------------------------------------------------
|
|
@@ -87,7 +75,6 @@ function cleanupExpiredEntries(): void {
|
|
|
87
75
|
// ---------------------------------------------------------------------------
|
|
88
76
|
|
|
89
77
|
async function handleStartAuth(_args: RouteHandlerArgs) {
|
|
90
|
-
requireFeatureFlag();
|
|
91
78
|
cleanupExpiredEntries();
|
|
92
79
|
|
|
93
80
|
const codeVerifier = generateCodeVerifier();
|
|
@@ -115,7 +102,6 @@ async function handleStartAuth(_args: RouteHandlerArgs) {
|
|
|
115
102
|
}
|
|
116
103
|
|
|
117
104
|
async function handleExchange(args: RouteHandlerArgs) {
|
|
118
|
-
requireFeatureFlag();
|
|
119
105
|
const { code, state } = args.body as { code: string; state: string };
|
|
120
106
|
|
|
121
107
|
const pending = pendingAuths.get(state);
|
|
@@ -15,20 +15,13 @@
|
|
|
15
15
|
|
|
16
16
|
import { z } from "zod";
|
|
17
17
|
|
|
18
|
-
import {
|
|
19
|
-
getConfig,
|
|
20
|
-
invalidateConfigCache,
|
|
21
|
-
loadRawConfig,
|
|
22
|
-
saveRawConfig,
|
|
23
|
-
} from "../../config/loader.js";
|
|
24
|
-
import type { AssistantConfig } from "../../config/schema.js";
|
|
18
|
+
import { getConfig } from "../../config/loader.js";
|
|
25
19
|
import { getMemoryCheckpoint } from "../../memory/checkpoints.js";
|
|
26
20
|
import {
|
|
27
21
|
getMessageRoleStatsByConversation,
|
|
28
22
|
listConversationsBySource,
|
|
29
23
|
} from "../../memory/conversation-queries.js";
|
|
30
24
|
import {
|
|
31
|
-
cancelPendingAutomaticConsolidationJobs,
|
|
32
25
|
enqueueMemoryJob,
|
|
33
26
|
hasActiveJobOfType,
|
|
34
27
|
MEMORY_V2_CONSOLIDATION_JOB_TRIGGERS,
|
|
@@ -37,15 +30,12 @@ import { GRAPH_MAINTENANCE_CHECKPOINTS } from "../../memory/jobs-worker.js";
|
|
|
37
30
|
import { getUsageCostForConversationWindow } from "../../memory/llm-usage-store.js";
|
|
38
31
|
import { MEMORY_V2_CONSOLIDATION_SOURCE } from "../../memory/v2/constants.js";
|
|
39
32
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
40
|
-
import { BadRequestError
|
|
33
|
+
import { BadRequestError } from "./errors.js";
|
|
41
34
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
42
35
|
|
|
43
36
|
function isConsolidationAvailable(): boolean {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
function isAutomaticConsolidationEnabled(config: AssistantConfig): boolean {
|
|
48
|
-
return config.memory.v2.enabled && config.memory.v2.consolidation_enabled;
|
|
37
|
+
const config = getConfig();
|
|
38
|
+
return config.memory.enabled !== false && config.memory.v2.enabled;
|
|
49
39
|
}
|
|
50
40
|
|
|
51
41
|
function consolidationIntervalMs(): number {
|
|
@@ -63,8 +53,8 @@ function readLastRunAt(): number | null {
|
|
|
63
53
|
|
|
64
54
|
function readConsolidationConfigResponse() {
|
|
65
55
|
const config = getConfig();
|
|
66
|
-
const available = config.memory.v2.enabled;
|
|
67
|
-
const enabled =
|
|
56
|
+
const available = config.memory.enabled !== false && config.memory.v2.enabled;
|
|
57
|
+
const enabled = available;
|
|
68
58
|
const intervalMs = consolidationIntervalMs();
|
|
69
59
|
const lastRunAt = readLastRunAt();
|
|
70
60
|
const nextRunAt =
|
|
@@ -79,43 +69,6 @@ function readConsolidationConfigResponse() {
|
|
|
79
69
|
};
|
|
80
70
|
}
|
|
81
71
|
|
|
82
|
-
function handleUpdateConfig(body: Record<string, unknown>) {
|
|
83
|
-
const consolidationPatch: Record<string, unknown> = {};
|
|
84
|
-
const disablingAutomaticConsolidation = body.enabled === false;
|
|
85
|
-
if ("enabled" in body && typeof body.enabled === "boolean") {
|
|
86
|
-
consolidationPatch.consolidation_enabled = body.enabled;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const raw = loadRawConfig() as Record<string, unknown>;
|
|
91
|
-
const memory =
|
|
92
|
-
raw.memory != null && typeof raw.memory === "object"
|
|
93
|
-
? (raw.memory as Record<string, unknown>)
|
|
94
|
-
: {};
|
|
95
|
-
const v2 =
|
|
96
|
-
memory.v2 != null && typeof memory.v2 === "object"
|
|
97
|
-
? (memory.v2 as Record<string, unknown>)
|
|
98
|
-
: {};
|
|
99
|
-
|
|
100
|
-
raw.memory = {
|
|
101
|
-
...memory,
|
|
102
|
-
v2: {
|
|
103
|
-
...v2,
|
|
104
|
-
...consolidationPatch,
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
saveRawConfig(raw);
|
|
108
|
-
invalidateConfigCache();
|
|
109
|
-
if (disablingAutomaticConsolidation) {
|
|
110
|
-
cancelPendingAutomaticConsolidationJobs();
|
|
111
|
-
}
|
|
112
|
-
} catch {
|
|
113
|
-
throw new InternalError("Failed to save consolidation config");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return readConsolidationConfigResponse();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
72
|
// ---------------------------------------------------------------------------
|
|
120
73
|
// Shared ROUTES
|
|
121
74
|
// ---------------------------------------------------------------------------
|
|
@@ -145,35 +98,6 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
145
98
|
return readConsolidationConfigResponse();
|
|
146
99
|
},
|
|
147
100
|
},
|
|
148
|
-
{
|
|
149
|
-
operationId: "updateConsolidationConfig",
|
|
150
|
-
endpoint: "consolidation/config",
|
|
151
|
-
method: "PUT",
|
|
152
|
-
policy: {
|
|
153
|
-
requiredScopes: ["settings.write"],
|
|
154
|
-
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
155
|
-
},
|
|
156
|
-
summary: "Update consolidation config",
|
|
157
|
-
description: "Update the memory v2 consolidation schedule configuration.",
|
|
158
|
-
tags: ["consolidation"],
|
|
159
|
-
requestBody: z.object({
|
|
160
|
-
enabled: z
|
|
161
|
-
.boolean()
|
|
162
|
-
.optional()
|
|
163
|
-
.describe("Enable or disable automatic consolidation"),
|
|
164
|
-
}),
|
|
165
|
-
responseBody: z.object({
|
|
166
|
-
available: z.boolean(),
|
|
167
|
-
enabled: z.boolean(),
|
|
168
|
-
intervalMs: z.number(),
|
|
169
|
-
nextRunAt: z.number().nullable(),
|
|
170
|
-
lastRunAt: z.number().nullable(),
|
|
171
|
-
success: z.boolean(),
|
|
172
|
-
}),
|
|
173
|
-
handler: async ({ body = {} }: RouteHandlerArgs) => {
|
|
174
|
-
return handleUpdateConfig(body);
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
101
|
{
|
|
178
102
|
operationId: "runConsolidationNow",
|
|
179
103
|
endpoint: "consolidation/run-now",
|
|
@@ -133,6 +133,12 @@ export const conversationSummarySchema = z.object({
|
|
|
133
133
|
groupId: z.string().nullable(),
|
|
134
134
|
forkParent: forkParentSchema.optional(),
|
|
135
135
|
archivedAt: z.number().optional(),
|
|
136
|
+
/**
|
|
137
|
+
* Epoch-ms timestamp set when a background/scheduled conversation was
|
|
138
|
+
* explicitly promoted ("surfaced") into the Recents sidebar grouping via
|
|
139
|
+
* `POST /v1/conversations/:id/surface`. Absent when not surfaced.
|
|
140
|
+
*/
|
|
141
|
+
surfacedAt: z.number().optional(),
|
|
136
142
|
inferenceProfile: z.string().optional(),
|
|
137
143
|
/**
|
|
138
144
|
* True when the agent loop is currently mid-turn for this conversation.
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* DELETE /v1/conversations/:id — delete a single conversation
|
|
12
12
|
* POST /v1/conversations/:id/archive — archive a conversation
|
|
13
13
|
* POST /v1/conversations/:id/unarchive — restore an archived conversation
|
|
14
|
+
* POST /v1/conversations/:id/surface — promote to / demote from Recents
|
|
14
15
|
* POST /v1/conversations/:id/cancel — cancel generation
|
|
15
16
|
* POST /v1/conversations/:id/undo — undo last message
|
|
16
17
|
* POST /v1/conversations/:id/regenerate — regenerate last assistant response
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
deleteConversation,
|
|
37
38
|
forkConversation as forkConversationInStore,
|
|
38
39
|
getConversation,
|
|
40
|
+
setConversationSurfaced,
|
|
39
41
|
unarchiveConversation,
|
|
40
42
|
updateConversationTitle,
|
|
41
43
|
wipeConversation,
|
|
@@ -360,6 +362,43 @@ function handleUnarchiveConversation({
|
|
|
360
362
|
return { ok: true, conversationId: resolvedId };
|
|
361
363
|
}
|
|
362
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Set or clear the `surfacedAt` promotion marker on a conversation.
|
|
367
|
+
*
|
|
368
|
+
* Surfacing is the explicit opt-in that makes a background/scheduled
|
|
369
|
+
* conversation appear in the default conversation listing (and therefore the
|
|
370
|
+
* Recents sidebar grouping). It is never set automatically — product flows
|
|
371
|
+
* call this when a background conversation deserves foreground visibility
|
|
372
|
+
* (e.g. the user sent a follow-up message in it).
|
|
373
|
+
*/
|
|
374
|
+
function handleSurfaceConversation({
|
|
375
|
+
pathParams = {},
|
|
376
|
+
body = {},
|
|
377
|
+
headers,
|
|
378
|
+
}: RouteHandlerArgs) {
|
|
379
|
+
if (typeof body.surfaced !== "boolean") {
|
|
380
|
+
throw new BadRequestError("Missing surfaced boolean");
|
|
381
|
+
}
|
|
382
|
+
const resolvedId = resolveOrThrow(pathParams.id!);
|
|
383
|
+
const result = setConversationSurfaced(resolvedId, body.surfaced);
|
|
384
|
+
if (!result) {
|
|
385
|
+
throw new NotFoundError(`Conversation ${pathParams.id} not found`);
|
|
386
|
+
}
|
|
387
|
+
// Shape-changing for the default list (row appears in / disappears from
|
|
388
|
+
// the standard listing), so publish with a shape-changing reason — web
|
|
389
|
+
// refetches the paginated list, macOS gets the legacy typed broadcast.
|
|
390
|
+
publishConversationListAndMetadataChanged(
|
|
391
|
+
"reordered",
|
|
392
|
+
resolvedId,
|
|
393
|
+
headers?.["x-vellum-client-id"]?.trim() || undefined,
|
|
394
|
+
);
|
|
395
|
+
return {
|
|
396
|
+
ok: true,
|
|
397
|
+
conversationId: resolvedId,
|
|
398
|
+
surfacedAt: result.surfacedAt,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
363
402
|
function handleCancelGeneration({ pathParams = {} }: RouteHandlerArgs) {
|
|
364
403
|
const resolvedId = resolveConversationId(pathParams.id!) ?? pathParams.id!;
|
|
365
404
|
const cancelled = cancelGeneration(resolvedId);
|
|
@@ -656,6 +695,38 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
656
695
|
}),
|
|
657
696
|
handler: handleUnarchiveConversation,
|
|
658
697
|
},
|
|
698
|
+
{
|
|
699
|
+
operationId: "surfaceConversation",
|
|
700
|
+
endpoint: "conversations/:id/surface",
|
|
701
|
+
method: "POST",
|
|
702
|
+
policy: {
|
|
703
|
+
requiredScopes: ["chat.write"],
|
|
704
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
705
|
+
},
|
|
706
|
+
summary: "Surface a conversation into Recents",
|
|
707
|
+
description:
|
|
708
|
+
"Explicitly promote a background or scheduled conversation into the " +
|
|
709
|
+
"default conversation listing (the Recents sidebar grouping), or demote " +
|
|
710
|
+
"it with surfaced=false. Conversations are never surfaced automatically.",
|
|
711
|
+
tags: ["conversations"],
|
|
712
|
+
pathParams: [{ name: "id", type: "uuid" }],
|
|
713
|
+
requestBody: z.object({
|
|
714
|
+
surfaced: z
|
|
715
|
+
.boolean()
|
|
716
|
+
.describe(
|
|
717
|
+
"true to surface the conversation into Recents, false to clear the promotion.",
|
|
718
|
+
),
|
|
719
|
+
}),
|
|
720
|
+
responseBody: z.object({
|
|
721
|
+
ok: z.boolean(),
|
|
722
|
+
conversationId: z.string(),
|
|
723
|
+
surfacedAt: z
|
|
724
|
+
.number()
|
|
725
|
+
.nullable()
|
|
726
|
+
.describe("Epoch-ms timestamp of the promotion, or null when cleared."),
|
|
727
|
+
}),
|
|
728
|
+
handler: handleSurfaceConversation,
|
|
729
|
+
},
|
|
659
730
|
{
|
|
660
731
|
operationId: "cancelConversationGeneration",
|
|
661
732
|
endpoint: "conversations/:id/cancel",
|