@vellumai/assistant 0.5.13 → 0.5.14
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/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +4 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +64 -88
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +3 -14
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +71 -33
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
|
@@ -22,6 +22,7 @@ import { isHttpAuthDisabled } from "../../config/env.js";
|
|
|
22
22
|
import { getConfig } from "../../config/loader.js";
|
|
23
23
|
import {
|
|
24
24
|
buildModelInfoEvent,
|
|
25
|
+
formatCompactResult,
|
|
25
26
|
isModelSlashCommand,
|
|
26
27
|
} from "../../daemon/conversation-process.js";
|
|
27
28
|
import {
|
|
@@ -64,6 +65,8 @@ import { searchConversations } from "../../memory/conversation-queries.js";
|
|
|
64
65
|
import { getConfiguredProvider } from "../../providers/provider-send-message.js";
|
|
65
66
|
import type { Provider } from "../../providers/types.js";
|
|
66
67
|
import { checkIngressForSecrets } from "../../security/secret-ingress.js";
|
|
68
|
+
import { redactSecrets } from "../../security/secret-scanner.js";
|
|
69
|
+
import { summarizeToolInput } from "../../tools/tool-input-summary.js";
|
|
67
70
|
import { getLogger } from "../../util/logger.js";
|
|
68
71
|
import { silentlyWithLog } from "../../util/silently.js";
|
|
69
72
|
import { buildAssistantEvent } from "../assistant-event.js";
|
|
@@ -417,6 +420,19 @@ export function handleListMessages(
|
|
|
417
420
|
}
|
|
418
421
|
const rendered = renderHistoryContent(content);
|
|
419
422
|
|
|
423
|
+
// Extract sentAt from metadata for display timestamps. When a message
|
|
424
|
+
// was queued or its persistence was delayed (long assistant generation),
|
|
425
|
+
// sentAt captures the actual event time. Falls back to createdAt.
|
|
426
|
+
let sentAt: number | undefined;
|
|
427
|
+
if (msg.metadata) {
|
|
428
|
+
try {
|
|
429
|
+
const meta = JSON.parse(msg.metadata);
|
|
430
|
+
if (typeof meta.sentAt === "number") sentAt = meta.sentAt;
|
|
431
|
+
} catch {
|
|
432
|
+
// Ignore malformed metadata
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
420
436
|
// Strip <no_response/> markers from assistant messages so web/API
|
|
421
437
|
// clients never see the raw sentinel. Only assistant messages produce
|
|
422
438
|
// this marker; user messages are left untouched.
|
|
@@ -449,11 +465,15 @@ export function handleListMessages(
|
|
|
449
465
|
role: msg.role,
|
|
450
466
|
text: rendered.text.replace(NO_RESPONSE_INLINE_RE, "").trim(),
|
|
451
467
|
timestamp: msg.createdAt,
|
|
468
|
+
sentAt,
|
|
452
469
|
toolCalls: rendered.toolCalls,
|
|
453
470
|
toolCallsBeforeText: rendered.toolCallsBeforeText,
|
|
454
471
|
textSegments: filteredSegments,
|
|
455
472
|
contentOrder: filteredContentOrder,
|
|
456
473
|
surfaces: rendered.surfaces,
|
|
474
|
+
...(rendered.thinkingSegments.length > 0
|
|
475
|
+
? { thinkingSegments: rendered.thinkingSegments }
|
|
476
|
+
: {}),
|
|
457
477
|
id: msg.id,
|
|
458
478
|
};
|
|
459
479
|
}
|
|
@@ -462,11 +482,15 @@ export function handleListMessages(
|
|
|
462
482
|
role: msg.role,
|
|
463
483
|
text: rendered.text,
|
|
464
484
|
timestamp: msg.createdAt,
|
|
485
|
+
sentAt,
|
|
465
486
|
toolCalls: rendered.toolCalls,
|
|
466
487
|
toolCallsBeforeText: rendered.toolCallsBeforeText,
|
|
467
488
|
textSegments: rendered.textSegments,
|
|
468
489
|
contentOrder: rendered.contentOrder,
|
|
469
490
|
surfaces: rendered.surfaces,
|
|
491
|
+
...(rendered.thinkingSegments.length > 0
|
|
492
|
+
? { thinkingSegments: rendered.thinkingSegments }
|
|
493
|
+
: {}),
|
|
470
494
|
id: msg.id,
|
|
471
495
|
};
|
|
472
496
|
});
|
|
@@ -535,16 +559,26 @@ export function handleListMessages(
|
|
|
535
559
|
prevAssistantTimestamp = msgTimestamp;
|
|
536
560
|
}
|
|
537
561
|
|
|
562
|
+
// Use sentAt (actual event time) for the display timestamp when
|
|
563
|
+
// available, falling back to createdAt (persistence time).
|
|
564
|
+
// Note: clients use this display timestamp as their pagination cursor
|
|
565
|
+
// after memory-pressure trimming, while server-side pagination filters
|
|
566
|
+
// on createdAt. The mismatch is benign — it may return slightly extra
|
|
567
|
+
// data on a page boundary but never loses messages.
|
|
568
|
+
const displayTimestamp = m.sentAt ?? m.timestamp;
|
|
538
569
|
return {
|
|
539
570
|
id: m.id ?? "",
|
|
540
571
|
role: m.role,
|
|
541
572
|
content: m.text,
|
|
542
|
-
timestamp: new Date(
|
|
573
|
+
timestamp: new Date(displayTimestamp).toISOString(),
|
|
543
574
|
attachments: msgAttachments,
|
|
544
575
|
...(m.toolCalls.length > 0 ? { toolCalls: m.toolCalls } : {}),
|
|
545
576
|
...(interfaces ? { interfaces } : {}),
|
|
546
577
|
...(m.surfaces.length > 0 ? { surfaces: m.surfaces } : {}),
|
|
547
578
|
...(m.textSegments.length > 0 ? { textSegments: m.textSegments } : {}),
|
|
579
|
+
...(m.thinkingSegments?.length
|
|
580
|
+
? { thinkingSegments: m.thinkingSegments }
|
|
581
|
+
: {}),
|
|
548
582
|
...(m.contentOrder.length > 0 ? { contentOrder: m.contentOrder } : {}),
|
|
549
583
|
};
|
|
550
584
|
});
|
|
@@ -604,6 +638,14 @@ function makeHubPublisher(
|
|
|
604
638
|
try {
|
|
605
639
|
const trustContext = conversation.trustContext;
|
|
606
640
|
const sourceChannel = trustContext?.sourceChannel ?? "vellum";
|
|
641
|
+
const inputRecord = msg.input as Record<string, unknown>;
|
|
642
|
+
const activityRaw =
|
|
643
|
+
(typeof inputRecord.activity === "string"
|
|
644
|
+
? inputRecord.activity
|
|
645
|
+
: undefined) ??
|
|
646
|
+
(typeof inputRecord.reason === "string"
|
|
647
|
+
? inputRecord.reason
|
|
648
|
+
: undefined);
|
|
607
649
|
const canonicalRequest = createCanonicalGuardianRequest({
|
|
608
650
|
id: msg.requestId,
|
|
609
651
|
kind: "tool_approval",
|
|
@@ -615,6 +657,15 @@ function makeHubPublisher(
|
|
|
615
657
|
guardianExternalUserId: trustContext?.guardianExternalUserId,
|
|
616
658
|
guardianPrincipalId: trustContext?.guardianPrincipalId ?? undefined,
|
|
617
659
|
toolName: msg.toolName,
|
|
660
|
+
commandPreview:
|
|
661
|
+
redactSecrets(
|
|
662
|
+
summarizeToolInput(msg.toolName, inputRecord),
|
|
663
|
+
) || undefined,
|
|
664
|
+
riskLevel: msg.riskLevel,
|
|
665
|
+
activityText: activityRaw
|
|
666
|
+
? redactSecrets(activityRaw)
|
|
667
|
+
: undefined,
|
|
668
|
+
executionTarget: msg.executionTarget,
|
|
618
669
|
status: "pending",
|
|
619
670
|
requestCode: generateCanonicalRequestCode(),
|
|
620
671
|
expiresAt: Date.now() + 5 * 60 * 1000,
|
|
@@ -1291,6 +1342,74 @@ export async function handleSendMessage(
|
|
|
1291
1342
|
}
|
|
1292
1343
|
}
|
|
1293
1344
|
|
|
1345
|
+
if (slashResult.kind === "compact") {
|
|
1346
|
+
conversation.processing = true;
|
|
1347
|
+
let cleanupDeferred = false;
|
|
1348
|
+
try {
|
|
1349
|
+
const provenance = provenanceFromTrustContext(conversation.trustContext);
|
|
1350
|
+
const channelMeta = {
|
|
1351
|
+
...provenance,
|
|
1352
|
+
userMessageChannel: sourceChannel,
|
|
1353
|
+
assistantMessageChannel: sourceChannel,
|
|
1354
|
+
userMessageInterface: sourceInterface,
|
|
1355
|
+
assistantMessageInterface: sourceInterface,
|
|
1356
|
+
};
|
|
1357
|
+
const cleanMsg = createUserMessage(rawContent, attachments);
|
|
1358
|
+
const persisted = await addMessage(
|
|
1359
|
+
mapping.conversationId,
|
|
1360
|
+
"user",
|
|
1361
|
+
JSON.stringify(cleanMsg.content),
|
|
1362
|
+
channelMeta,
|
|
1363
|
+
);
|
|
1364
|
+
conversation.getMessages().push(cleanMsg);
|
|
1365
|
+
|
|
1366
|
+
conversation.emitActivityState(
|
|
1367
|
+
"thinking",
|
|
1368
|
+
"context_compacting",
|
|
1369
|
+
"assistant_turn",
|
|
1370
|
+
);
|
|
1371
|
+
const result = await conversation.forceCompact();
|
|
1372
|
+
const responseText = formatCompactResult(result);
|
|
1373
|
+
|
|
1374
|
+
const assistantMsg = createAssistantMessage(responseText);
|
|
1375
|
+
await addMessage(
|
|
1376
|
+
mapping.conversationId,
|
|
1377
|
+
"assistant",
|
|
1378
|
+
JSON.stringify(assistantMsg.content),
|
|
1379
|
+
channelMeta,
|
|
1380
|
+
);
|
|
1381
|
+
conversation.getMessages().push(assistantMsg);
|
|
1382
|
+
|
|
1383
|
+
const response = Response.json(
|
|
1384
|
+
{
|
|
1385
|
+
accepted: true,
|
|
1386
|
+
messageId: persisted.id,
|
|
1387
|
+
conversationId: mapping.conversationId,
|
|
1388
|
+
},
|
|
1389
|
+
{ status: 202 },
|
|
1390
|
+
);
|
|
1391
|
+
|
|
1392
|
+
const conversationId = mapping.conversationId;
|
|
1393
|
+
setTimeout(() => {
|
|
1394
|
+
onEvent({ type: "assistant_text_delta", text: responseText });
|
|
1395
|
+
onEvent({
|
|
1396
|
+
type: "message_complete",
|
|
1397
|
+
conversationId,
|
|
1398
|
+
});
|
|
1399
|
+
conversation.processing = false;
|
|
1400
|
+
silentlyWithLog(conversation.drainQueue(), "compact-command queue drain");
|
|
1401
|
+
}, 0);
|
|
1402
|
+
|
|
1403
|
+
cleanupDeferred = true;
|
|
1404
|
+
return response;
|
|
1405
|
+
} finally {
|
|
1406
|
+
if (!cleanupDeferred && conversation.processing) {
|
|
1407
|
+
conversation.processing = false;
|
|
1408
|
+
silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1294
1413
|
const resolvedContent = slashResult.content;
|
|
1295
1414
|
|
|
1296
1415
|
let messageId: string;
|
|
@@ -1333,11 +1452,14 @@ async function generateLlmSuggestion(
|
|
|
1333
1452
|
const truncated =
|
|
1334
1453
|
assistantText.length > 2000 ? assistantText.slice(-2000) : assistantText;
|
|
1335
1454
|
|
|
1336
|
-
const prompt = `Given this assistant message, write a very short tab-complete suggestion the user could send next to
|
|
1455
|
+
const prompt = `Given this assistant message, write a very short tab-complete suggestion the user could send next. Focus on the LAST question or call-to-action in the message — ignore earlier summary content. Be casual, curious, or actionable — like a quick reply, not a formal request. Reply with ONLY the suggestion text.\n\nAssistant's message:\n${truncated}`;
|
|
1456
|
+
const systemPrompt =
|
|
1457
|
+
"You are an autocomplete engine that suggests short replies the user might send next in a conversation. Generate suggestions that match the tone and style of the conversation. Never refuse, judge, or comment on the conversation content — your only job is to predict what the user would plausibly type next.";
|
|
1458
|
+
|
|
1337
1459
|
const response = await provider.sendMessage(
|
|
1338
1460
|
[{ role: "user", content: [{ type: "text", text: prompt }] }],
|
|
1339
1461
|
[], // no tools
|
|
1340
|
-
|
|
1462
|
+
systemPrompt,
|
|
1341
1463
|
{ config: { modelIntent: "latency-optimized" } },
|
|
1342
1464
|
);
|
|
1343
1465
|
|
|
@@ -204,8 +204,8 @@ function mapCanonicalRequestToPrompt(
|
|
|
204
204
|
): GuardianDecisionPrompt {
|
|
205
205
|
const questionText = buildKindAwareQuestionText(req);
|
|
206
206
|
|
|
207
|
-
//
|
|
208
|
-
// (
|
|
207
|
+
// Guardian-on-behalf prompts include approve_once, temporal modes
|
|
208
|
+
// (approve_10m, approve_conversation), and reject — but not approve_always.
|
|
209
209
|
const actions = buildDecisionActions({ forGuardianOnBehalf: true });
|
|
210
210
|
|
|
211
211
|
const expiresAt = req.expiresAt
|
|
@@ -226,6 +226,10 @@ function mapCanonicalRequestToPrompt(
|
|
|
226
226
|
conversationId,
|
|
227
227
|
callSessionId: req.callSessionId ?? null,
|
|
228
228
|
kind: req.kind,
|
|
229
|
+
commandPreview: req.commandPreview ?? undefined,
|
|
230
|
+
riskLevel: req.riskLevel ?? undefined,
|
|
231
|
+
activityText: req.activityText ?? undefined,
|
|
232
|
+
executionTarget: (req.executionTarget as "sandbox" | "host") ?? undefined,
|
|
229
233
|
};
|
|
230
234
|
}
|
|
231
235
|
|
|
@@ -240,7 +244,9 @@ function buildKindAwareQuestionText(req: CanonicalGuardianRequest): string {
|
|
|
240
244
|
const baseText =
|
|
241
245
|
req.questionText ??
|
|
242
246
|
(req.toolName
|
|
243
|
-
?
|
|
247
|
+
? req.activityText
|
|
248
|
+
? `Approve tool: ${req.toolName} — ${req.activityText}`
|
|
249
|
+
: `Approve tool: ${req.toolName}`
|
|
244
250
|
: `Guardian request: ${req.kind}`);
|
|
245
251
|
|
|
246
252
|
if (req.kind === "access_request") {
|
|
@@ -9,10 +9,12 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
|
|
12
|
-
import { getBaseDataDir } from "../../config/env-registry.js";
|
|
13
12
|
import { parseIdentityFields } from "../../daemon/handlers/identity.js";
|
|
14
13
|
import { getMaxMigrationVersion } from "../../memory/migrations/registry.js";
|
|
15
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
getWorkspaceDir,
|
|
16
|
+
getWorkspacePromptPath,
|
|
17
|
+
} from "../../util/platform.js";
|
|
16
18
|
import { WORKSPACE_MIGRATIONS } from "../../workspace/migrations/registry.js";
|
|
17
19
|
import { getLastWorkspaceMigrationId } from "../../workspace/migrations/runner.js";
|
|
18
20
|
import { httpError } from "../http-errors.js";
|
|
@@ -28,8 +30,8 @@ interface DiskSpaceInfo {
|
|
|
28
30
|
|
|
29
31
|
function getDiskSpaceInfo(): DiskSpaceInfo | null {
|
|
30
32
|
try {
|
|
31
|
-
const
|
|
32
|
-
const diskPath =
|
|
33
|
+
const wsDir = getWorkspaceDir();
|
|
34
|
+
const diskPath = existsSync(wsDir) ? wsDir : "/";
|
|
33
35
|
const stats = statfsSync(diskPath);
|
|
34
36
|
const totalBytes = stats.bsize * stats.blocks;
|
|
35
37
|
const freeBytes = stats.bsize * stats.bavail;
|
|
@@ -261,6 +263,25 @@ export function identityRouteDefinitions(): RouteDefinition[] {
|
|
|
261
263
|
migrations: z.object({}).passthrough(),
|
|
262
264
|
}),
|
|
263
265
|
},
|
|
266
|
+
{
|
|
267
|
+
endpoint: "healthz",
|
|
268
|
+
method: "GET",
|
|
269
|
+
handler: () => handleDetailedHealth(),
|
|
270
|
+
policyKey: "health",
|
|
271
|
+
summary: "Detailed health check (alias)",
|
|
272
|
+
description:
|
|
273
|
+
"Alias for /v1/health. Returns runtime health including version, disk, memory, CPU, and migration status.",
|
|
274
|
+
tags: ["system"],
|
|
275
|
+
responseBody: z.object({
|
|
276
|
+
status: z.string(),
|
|
277
|
+
timestamp: z.string(),
|
|
278
|
+
version: z.string(),
|
|
279
|
+
disk: z.object({}).passthrough(),
|
|
280
|
+
memory: z.object({}).passthrough(),
|
|
281
|
+
cpu: z.object({}).passthrough(),
|
|
282
|
+
migrations: z.object({}).passthrough(),
|
|
283
|
+
}),
|
|
284
|
+
},
|
|
264
285
|
{
|
|
265
286
|
endpoint: "identity",
|
|
266
287
|
method: "GET",
|
|
@@ -33,8 +33,8 @@ import {
|
|
|
33
33
|
} from "../../memory/schema.js";
|
|
34
34
|
import { getLogger } from "../../util/logger.js";
|
|
35
35
|
import {
|
|
36
|
+
getDaemonStderrLogPath,
|
|
36
37
|
getDataDir,
|
|
37
|
-
getRootDir,
|
|
38
38
|
getWorkspaceConfigPath,
|
|
39
39
|
getWorkspaceDir,
|
|
40
40
|
} from "../../util/platform.js";
|
|
@@ -186,7 +186,7 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
const stderrPath =
|
|
189
|
+
const stderrPath = getDaemonStderrLogPath();
|
|
190
190
|
if (existsSync(stderrPath)) {
|
|
191
191
|
try {
|
|
192
192
|
const stat = statSync(stderrPath);
|
|
@@ -452,6 +452,8 @@ const WORKSPACE_SKIP_DIRS = new Set([
|
|
|
452
452
|
"data/attachments",
|
|
453
453
|
"data/sounds",
|
|
454
454
|
"conversations",
|
|
455
|
+
"signals",
|
|
456
|
+
"deprecated",
|
|
455
457
|
]);
|
|
456
458
|
|
|
457
459
|
/** Files at the workspace root to skip (already covered by sanitized fields). */
|
|
@@ -666,6 +668,20 @@ function readSanitizedConfig(): Record<string, unknown> | undefined {
|
|
|
666
668
|
// ---------------------------------------------------------------------------
|
|
667
669
|
|
|
668
670
|
export function logExportRouteDefinitions(): RouteDefinition[] {
|
|
671
|
+
const exportRequestBody = z.object({
|
|
672
|
+
auditLimit: z
|
|
673
|
+
.number()
|
|
674
|
+
.int()
|
|
675
|
+
.optional()
|
|
676
|
+
.describe("Max audit records (default 1000)"),
|
|
677
|
+
conversationId: z
|
|
678
|
+
.string()
|
|
679
|
+
.optional()
|
|
680
|
+
.describe("Scope to a single conversation"),
|
|
681
|
+
startTime: z.number().optional().describe("Lower bound epoch ms"),
|
|
682
|
+
endTime: z.number().optional().describe("Upper bound epoch ms"),
|
|
683
|
+
});
|
|
684
|
+
|
|
669
685
|
return [
|
|
670
686
|
{
|
|
671
687
|
endpoint: "export",
|
|
@@ -673,17 +689,23 @@ export function logExportRouteDefinitions(): RouteDefinition[] {
|
|
|
673
689
|
policyKey: "export",
|
|
674
690
|
summary: "Export logs and audit data",
|
|
675
691
|
description:
|
|
676
|
-
"Export audit records,
|
|
692
|
+
"Export audit records, assistant logs, workspace contents, and config as a tar.gz archive.",
|
|
693
|
+
tags: ["export"],
|
|
694
|
+
requestBody: exportRequestBody,
|
|
695
|
+
handler: async ({ req }) => {
|
|
696
|
+
const body = (await req.json()) as ExportRequestBody;
|
|
697
|
+
return handleExport(body);
|
|
698
|
+
},
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
endpoint: "logs/export",
|
|
702
|
+
method: "POST",
|
|
703
|
+
policyKey: "export",
|
|
704
|
+
summary: "Export logs and audit data (alias)",
|
|
705
|
+
description:
|
|
706
|
+
"Alias for /v1/export. Export audit records, assistant logs, workspace contents, and config as a tar.gz archive.",
|
|
677
707
|
tags: ["export"],
|
|
678
|
-
requestBody:
|
|
679
|
-
auditLimit: z
|
|
680
|
-
.number()
|
|
681
|
-
.int()
|
|
682
|
-
.describe("Max audit records (default 1000)"),
|
|
683
|
-
conversationId: z.string().describe("Scope to a single conversation"),
|
|
684
|
-
startTime: z.number().describe("Lower bound epoch ms"),
|
|
685
|
-
endTime: z.number().describe("Upper bound epoch ms"),
|
|
686
|
-
}),
|
|
708
|
+
requestBody: exportRequestBody,
|
|
687
709
|
handler: async ({ req }) => {
|
|
688
710
|
const body = (await req.json()) as ExportRequestBody;
|
|
689
711
|
return handleExport(body);
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
* results with is_valid flag and detailed error descriptions.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { join } from "node:path";
|
|
15
14
|
import { Database } from "bun:sqlite";
|
|
16
15
|
|
|
17
16
|
import { z } from "zod";
|
|
@@ -23,9 +22,8 @@ import { clearCache as clearTrustCache } from "../../permissions/trust-store.js"
|
|
|
23
22
|
import { getLogger } from "../../util/logger.js";
|
|
24
23
|
import {
|
|
25
24
|
getDbPath,
|
|
26
|
-
getHooksDir,
|
|
27
|
-
getRootDir,
|
|
28
25
|
getWorkspaceDir,
|
|
26
|
+
getWorkspaceHooksDir,
|
|
29
27
|
} from "../../util/platform.js";
|
|
30
28
|
import { httpError } from "../http-errors.js";
|
|
31
29
|
import type { RouteDefinition } from "../http-router.js";
|
|
@@ -145,8 +143,9 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
|
|
|
145
143
|
|
|
146
144
|
try {
|
|
147
145
|
const { archive, manifest } = buildExportVBundle({
|
|
148
|
-
|
|
149
|
-
hooksDir
|
|
146
|
+
// hooksDir is intentionally omitted — hooks now live under workspace/hooks/
|
|
147
|
+
// and are included in the workspace walk. Passing hooksDir separately would
|
|
148
|
+
// export them twice (once as workspace/hooks/... and again as hooks/...).
|
|
150
149
|
workspaceDir: getWorkspaceDir(),
|
|
151
150
|
source: "runtime-export",
|
|
152
151
|
description,
|
|
@@ -301,11 +300,9 @@ export async function handleMigrationImportPreflight(
|
|
|
301
300
|
});
|
|
302
301
|
}
|
|
303
302
|
|
|
304
|
-
// Step 2: Analyze what would change on import
|
|
305
303
|
const pathResolver = new DefaultPathResolver(
|
|
306
|
-
join(getRootDir(), "protected"),
|
|
307
304
|
getWorkspaceDir(),
|
|
308
|
-
|
|
305
|
+
getWorkspaceHooksDir(),
|
|
309
306
|
);
|
|
310
307
|
|
|
311
308
|
const report = analyzeImport({
|
|
@@ -386,9 +383,8 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
|
|
|
386
383
|
}
|
|
387
384
|
|
|
388
385
|
const pathResolver = new DefaultPathResolver(
|
|
389
|
-
join(getRootDir(), "protected"),
|
|
390
386
|
getWorkspaceDir(),
|
|
391
|
-
|
|
387
|
+
getWorkspaceHooksDir(),
|
|
392
388
|
);
|
|
393
389
|
|
|
394
390
|
// Close the live SQLite connection before overwriting assistant.db on disk.
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
listConnections,
|
|
19
19
|
upsertApp,
|
|
20
20
|
} from "../../oauth/oauth-store.js";
|
|
21
|
+
import { serializeProviderSummary } from "../../oauth/provider-serializer.js";
|
|
21
22
|
import { httpError } from "../http-errors.js";
|
|
22
23
|
import type { RouteDefinition } from "../http-router.js";
|
|
23
24
|
|
|
@@ -75,15 +76,7 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
|
|
|
75
76
|
|
|
76
77
|
const providerRow = getProvider(providerKey);
|
|
77
78
|
const provider = providerRow
|
|
78
|
-
?
|
|
79
|
-
provider_key: providerRow.providerKey,
|
|
80
|
-
display_name: providerRow.displayName ?? null,
|
|
81
|
-
description: providerRow.description ?? null,
|
|
82
|
-
dashboard_url: providerRow.dashboardUrl ?? null,
|
|
83
|
-
client_id_placeholder: providerRow.clientIdPlaceholder ?? null,
|
|
84
|
-
requires_client_secret: !!(providerRow.requiresClientSecret ?? 1),
|
|
85
|
-
supports_managed_mode: !!providerRow.managedServiceConfigKey,
|
|
86
|
-
}
|
|
79
|
+
? serializeProviderSummary(providerRow)
|
|
87
80
|
: null;
|
|
88
81
|
|
|
89
82
|
return Response.json({
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handlers for listing and getting OAuth providers.
|
|
3
|
+
*
|
|
4
|
+
* Provides read-only endpoints for querying the registered OAuth provider
|
|
5
|
+
* catalog. All endpoints are bearer-token authenticated via the standard
|
|
6
|
+
* runtime auth middleware.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { getProvider, listProviders } from "../../oauth/oauth-store.js";
|
|
10
|
+
import { serializeProviderSummary } from "../../oauth/provider-serializer.js";
|
|
11
|
+
import { httpError } from "../http-errors.js";
|
|
12
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Build route definitions for OAuth provider list/get endpoints.
|
|
16
|
+
*/
|
|
17
|
+
export function oauthProvidersRouteDefinitions(): RouteDefinition[] {
|
|
18
|
+
return [
|
|
19
|
+
// GET /v1/oauth/providers — List all providers with optional filtering.
|
|
20
|
+
{
|
|
21
|
+
endpoint: "oauth/providers",
|
|
22
|
+
method: "GET",
|
|
23
|
+
handler: ({ url }) => {
|
|
24
|
+
const rows = listProviders();
|
|
25
|
+
let serialized = rows
|
|
26
|
+
.map((row) => serializeProviderSummary(row))
|
|
27
|
+
.filter((s): s is NonNullable<typeof s> => s !== null);
|
|
28
|
+
|
|
29
|
+
const supportsManagedModeParam = url.searchParams.get(
|
|
30
|
+
"supports_managed_mode",
|
|
31
|
+
);
|
|
32
|
+
if (supportsManagedModeParam === "true") {
|
|
33
|
+
serialized = serialized.filter((p) => p.supports_managed_mode);
|
|
34
|
+
} else if (supportsManagedModeParam === "false") {
|
|
35
|
+
serialized = serialized.filter((p) => !p.supports_managed_mode);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return Response.json({ providers: serialized });
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// GET /v1/oauth/providers/:providerKey — Get a single provider.
|
|
43
|
+
{
|
|
44
|
+
endpoint: "oauth/providers/:providerKey",
|
|
45
|
+
method: "GET",
|
|
46
|
+
handler: ({ params }) => {
|
|
47
|
+
const row = getProvider(params.providerKey);
|
|
48
|
+
if (!row) {
|
|
49
|
+
return httpError(
|
|
50
|
+
"NOT_FOUND",
|
|
51
|
+
`No OAuth provider registered for "${params.providerKey}"`,
|
|
52
|
+
404,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Response.json({ provider: serializeProviderSummary(row) });
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
}
|
|
@@ -147,8 +147,6 @@ export function cleanupPairingState(pairingRequestId: string): void {
|
|
|
147
147
|
export interface PairingHandlerContext {
|
|
148
148
|
pairingStore: PairingStore;
|
|
149
149
|
bearerToken: string | undefined;
|
|
150
|
-
/** Feature-flag client token to include in pairing approval responses so iOS can PATCH flags. */
|
|
151
|
-
featureFlagToken: string | undefined;
|
|
152
150
|
pairingBroadcast?: (msg: ServerMessage) => void;
|
|
153
151
|
}
|
|
154
152
|
|
|
@@ -281,9 +279,6 @@ export async function handlePairingRequest(
|
|
|
281
279
|
bearerToken: ctx.bearerToken,
|
|
282
280
|
gatewayUrl: entry.gatewayUrl,
|
|
283
281
|
localLanUrl: entry.localLanUrl,
|
|
284
|
-
...(ctx.featureFlagToken
|
|
285
|
-
? { featureFlagToken: ctx.featureFlagToken }
|
|
286
|
-
: {}),
|
|
287
282
|
...(credentials
|
|
288
283
|
? {
|
|
289
284
|
accessToken: credentials.accessToken,
|
|
@@ -388,9 +383,6 @@ export function handlePairingStatus(
|
|
|
388
383
|
bearerToken: entry.bearerToken,
|
|
389
384
|
gatewayUrl: entry.gatewayUrl,
|
|
390
385
|
localLanUrl: entry.localLanUrl,
|
|
391
|
-
...(ctx.featureFlagToken
|
|
392
|
-
? { featureFlagToken: ctx.featureFlagToken }
|
|
393
|
-
: {}),
|
|
394
386
|
...(credentialEntry
|
|
395
387
|
? {
|
|
396
388
|
accessToken: credentialEntry.credentials.accessToken,
|
|
@@ -35,6 +35,9 @@ export async function handleRecordLifecycleEvent(
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const event = recordLifecycleEvent(eventName);
|
|
38
|
+
if (!event) {
|
|
39
|
+
return Response.json({ skipped: true });
|
|
40
|
+
}
|
|
38
41
|
log.info({ eventName, eventId: event.id }, "Recorded lifecycle event");
|
|
39
42
|
|
|
40
43
|
return Response.json({ id: event.id, event_name: event.eventName });
|
|
@@ -55,10 +58,19 @@ export function telemetryRouteDefinitions(): RouteDefinition[] {
|
|
|
55
58
|
requestBody: z.object({
|
|
56
59
|
event_name: z.string().describe("Event name: app_open or hatch"),
|
|
57
60
|
}),
|
|
58
|
-
responseBody: z.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
responseBody: z.union([
|
|
62
|
+
z.object({
|
|
63
|
+
id: z.string().describe("Event ID"),
|
|
64
|
+
event_name: z.string(),
|
|
65
|
+
}),
|
|
66
|
+
z.object({
|
|
67
|
+
skipped: z
|
|
68
|
+
.literal(true)
|
|
69
|
+
.describe(
|
|
70
|
+
"Event skipped due to usage data collection being disabled",
|
|
71
|
+
),
|
|
72
|
+
}),
|
|
73
|
+
]),
|
|
62
74
|
handler: async ({ req }) => handleRecordLifecycleEvent(req),
|
|
63
75
|
},
|
|
64
76
|
];
|
|
@@ -30,7 +30,7 @@ import { dirname, join } from "node:path";
|
|
|
30
30
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
31
31
|
import { ensureDir, pathExists } from "../util/fs.js";
|
|
32
32
|
import { getLogger } from "../util/logger.js";
|
|
33
|
-
import { getPlatformName,
|
|
33
|
+
import { getPlatformName, getProtectedDir } from "../util/platform.js";
|
|
34
34
|
|
|
35
35
|
const log = getLogger("encrypted-store");
|
|
36
36
|
|
|
@@ -80,7 +80,7 @@ interface EncryptedEntry {
|
|
|
80
80
|
let storePathOverride: string | null = null;
|
|
81
81
|
|
|
82
82
|
function getStorePath(): string {
|
|
83
|
-
return storePathOverride ?? join(
|
|
83
|
+
return storePathOverride ?? join(getProtectedDir(), "keys.enc");
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/** @internal Test-only: override the store file path. Pass `null` to reset. */
|
|
@@ -18,7 +18,7 @@ import { join } from "node:path";
|
|
|
18
18
|
|
|
19
19
|
import { pathExists } from "../util/fs.js";
|
|
20
20
|
import { getLogger } from "../util/logger.js";
|
|
21
|
-
import {
|
|
21
|
+
import { getProtectedDir } from "../util/platform.js";
|
|
22
22
|
|
|
23
23
|
const log = getLogger("secret-allowlist");
|
|
24
24
|
|
|
@@ -45,7 +45,7 @@ let allowedRegexes: RegExp[] = [];
|
|
|
45
45
|
export function loadAllowlist(): void {
|
|
46
46
|
if (loaded || fileChecked) return;
|
|
47
47
|
|
|
48
|
-
const filePath = join(
|
|
48
|
+
const filePath = join(getProtectedDir(), "secret-allowlist.json");
|
|
49
49
|
if (!pathExists(filePath)) {
|
|
50
50
|
fileChecked = true;
|
|
51
51
|
return;
|
|
@@ -169,7 +169,7 @@ function validateAllowlist(
|
|
|
169
169
|
* Returns validation errors, or null if the file doesn't exist.
|
|
170
170
|
*/
|
|
171
171
|
export function validateAllowlistFile(): AllowlistValidationError[] | null {
|
|
172
|
-
const filePath = join(
|
|
172
|
+
const filePath = join(getProtectedDir(), "secret-allowlist.json");
|
|
173
173
|
if (!pathExists(filePath)) return null;
|
|
174
174
|
|
|
175
175
|
const raw = readFileSync(filePath, "utf-8");
|