@vellumai/assistant 0.5.13 → 0.5.15
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
package/src/memory/admin.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
import { and, count, desc, eq, sql } from "drizzle-orm";
|
|
2
|
+
|
|
1
3
|
import { getConfig } from "../config/loader.js";
|
|
2
4
|
import { getLogger } from "../util/logger.js";
|
|
3
|
-
import {
|
|
5
|
+
import { deleteMemoryCheckpoint } from "./checkpoints.js";
|
|
6
|
+
import { getConversationMemoryScopeId } from "./conversation-crud.js";
|
|
7
|
+
import { getDb, rawGet } from "./db.js";
|
|
4
8
|
import { getMemoryBackendStatus } from "./embedding-backend.js";
|
|
5
|
-
import { enqueueBackfillJob, enqueueRebuildIndexJob } from "./indexer.js";
|
|
9
|
+
import { enqueueBackfillJob, enqueueRebuildIndexJob, MIN_SEGMENT_CHARS } from "./indexer.js";
|
|
6
10
|
import {
|
|
7
11
|
enqueueCleanupStaleSupersededItemsJob,
|
|
12
|
+
enqueueMemoryJob,
|
|
8
13
|
getMemoryJobCounts,
|
|
9
14
|
} from "./jobs-store.js";
|
|
15
|
+
import { withQdrantBreaker } from "./qdrant-circuit-breaker.js";
|
|
16
|
+
import { getQdrantClient } from "./qdrant-client.js";
|
|
10
17
|
import { queryMemoryForCli } from "./retriever.js";
|
|
18
|
+
import { conversations, memorySegments, memorySummaries, messages } from "./schema.js";
|
|
11
19
|
|
|
12
20
|
const log = getLogger("memory-admin");
|
|
13
21
|
|
|
@@ -106,3 +114,174 @@ export function requestMemoryCleanup(retentionMs?: number): {
|
|
|
106
114
|
export async function queryMemory(query: string, conversationId: string) {
|
|
107
115
|
return queryMemoryForCli(query, conversationId, getConfig());
|
|
108
116
|
}
|
|
117
|
+
|
|
118
|
+
// ── Short segment cleanup ─────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
export interface CleanupShortSegmentsResult {
|
|
121
|
+
removed: number;
|
|
122
|
+
failed: number;
|
|
123
|
+
dryRunCount?: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Remove segments shorter than MIN_SEGMENT_CHARS from SQLite and Qdrant.
|
|
128
|
+
* These short fragments waste embedding budget, retrieval slots, and
|
|
129
|
+
* injection tokens.
|
|
130
|
+
*/
|
|
131
|
+
export async function cleanupShortSegments(
|
|
132
|
+
opts?: { dryRun?: boolean },
|
|
133
|
+
): Promise<CleanupShortSegmentsResult> {
|
|
134
|
+
const db = getDb();
|
|
135
|
+
|
|
136
|
+
const shortSegments = db
|
|
137
|
+
.select({ id: memorySegments.id })
|
|
138
|
+
.from(memorySegments)
|
|
139
|
+
.where(sql`length(${memorySegments.text}) < ${MIN_SEGMENT_CHARS}`)
|
|
140
|
+
.all();
|
|
141
|
+
|
|
142
|
+
if (opts?.dryRun) {
|
|
143
|
+
return { removed: 0, failed: 0, dryRunCount: shortSegments.length };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let removed = 0;
|
|
147
|
+
let failed = 0;
|
|
148
|
+
for (const row of shortSegments) {
|
|
149
|
+
try {
|
|
150
|
+
const qdrant = getQdrantClient();
|
|
151
|
+
await withQdrantBreaker(() => qdrant.deleteByTarget("segment", row.id));
|
|
152
|
+
} catch (err) {
|
|
153
|
+
// Keep the SQLite row so the target ID is preserved for retry
|
|
154
|
+
log.warn({ segmentId: row.id, err }, "Qdrant deletion failed — skipping SQLite deletion to preserve target ID");
|
|
155
|
+
failed++;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
db.delete(memorySegments)
|
|
160
|
+
.where(eq(memorySegments.id, row.id))
|
|
161
|
+
.run();
|
|
162
|
+
removed++;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
log.info({ removed, failed, threshold: MIN_SEGMENT_CHARS }, "Cleaned up short segments");
|
|
166
|
+
return { removed, failed };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ── Re-extraction ──────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
export interface ReextractTarget {
|
|
172
|
+
conversationId: string;
|
|
173
|
+
title: string | null;
|
|
174
|
+
messageCount: number;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Find the top N conversations by message count for re-extraction.
|
|
179
|
+
* Excludes background and private conversations.
|
|
180
|
+
*/
|
|
181
|
+
export function findReextractTargets(limit: number): ReextractTarget[] {
|
|
182
|
+
const db = getDb();
|
|
183
|
+
interface Row {
|
|
184
|
+
id: string;
|
|
185
|
+
title: string | null;
|
|
186
|
+
msg_count: number;
|
|
187
|
+
}
|
|
188
|
+
const rows = db
|
|
189
|
+
.select({
|
|
190
|
+
id: conversations.id,
|
|
191
|
+
title: conversations.title,
|
|
192
|
+
msg_count: count(messages.id),
|
|
193
|
+
})
|
|
194
|
+
.from(conversations)
|
|
195
|
+
.leftJoin(messages, eq(messages.conversationId, conversations.id))
|
|
196
|
+
.where(
|
|
197
|
+
sql`${conversations.conversationType} NOT IN ('background', 'private')`,
|
|
198
|
+
)
|
|
199
|
+
.groupBy(conversations.id)
|
|
200
|
+
.orderBy(desc(sql`count(${messages.id})`))
|
|
201
|
+
.limit(limit)
|
|
202
|
+
.all() as Row[];
|
|
203
|
+
|
|
204
|
+
return rows.map((r) => ({
|
|
205
|
+
conversationId: r.id,
|
|
206
|
+
title: r.title,
|
|
207
|
+
messageCount: r.msg_count,
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Look up a conversation for re-extraction targeting.
|
|
213
|
+
*/
|
|
214
|
+
export function findReextractTarget(
|
|
215
|
+
conversationId: string,
|
|
216
|
+
): ReextractTarget | null {
|
|
217
|
+
const db = getDb();
|
|
218
|
+
const conv = db
|
|
219
|
+
.select({ id: conversations.id, title: conversations.title })
|
|
220
|
+
.from(conversations)
|
|
221
|
+
.where(eq(conversations.id, conversationId))
|
|
222
|
+
.get();
|
|
223
|
+
if (!conv) return null;
|
|
224
|
+
|
|
225
|
+
const [{ total }] = db
|
|
226
|
+
.select({ total: count() })
|
|
227
|
+
.from(messages)
|
|
228
|
+
.where(eq(messages.conversationId, conversationId))
|
|
229
|
+
.all();
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
conversationId: conv.id,
|
|
233
|
+
title: conv.title,
|
|
234
|
+
messageCount: total,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Queue re-extraction for a set of conversations.
|
|
240
|
+
* Resets extraction checkpoints and clears extraction summaries so the
|
|
241
|
+
* batch extraction handler processes all messages from scratch with
|
|
242
|
+
* expanded supersession context.
|
|
243
|
+
*/
|
|
244
|
+
export function requestReextract(
|
|
245
|
+
targets: ReextractTarget[],
|
|
246
|
+
): { jobIds: string[] } {
|
|
247
|
+
const db = getDb();
|
|
248
|
+
const jobIds: string[] = [];
|
|
249
|
+
|
|
250
|
+
for (const target of targets) {
|
|
251
|
+
const { conversationId } = target;
|
|
252
|
+
|
|
253
|
+
// Reset batch extraction checkpoints
|
|
254
|
+
deleteMemoryCheckpoint(
|
|
255
|
+
`batch_extract:${conversationId}:last_message_id`,
|
|
256
|
+
);
|
|
257
|
+
deleteMemoryCheckpoint(
|
|
258
|
+
`batch_extract:${conversationId}:pending_count`,
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Clear the extraction summary so it starts fresh
|
|
262
|
+
db.delete(memorySummaries)
|
|
263
|
+
.where(
|
|
264
|
+
and(
|
|
265
|
+
eq(memorySummaries.scope, "extraction_context"),
|
|
266
|
+
eq(memorySummaries.scopeKey, conversationId),
|
|
267
|
+
),
|
|
268
|
+
)
|
|
269
|
+
.run();
|
|
270
|
+
|
|
271
|
+
// Resolve scope and enqueue with fullReextract flag
|
|
272
|
+
const scopeId = getConversationMemoryScopeId(conversationId);
|
|
273
|
+
const jobId = enqueueMemoryJob("batch_extract", {
|
|
274
|
+
conversationId,
|
|
275
|
+
scopeId,
|
|
276
|
+
fullReextract: true,
|
|
277
|
+
});
|
|
278
|
+
jobIds.push(jobId);
|
|
279
|
+
|
|
280
|
+
log.info(
|
|
281
|
+
{ conversationId, title: target.title, messages: target.messageCount },
|
|
282
|
+
"Queued re-extraction job",
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return { jobIds };
|
|
287
|
+
}
|
|
@@ -26,6 +26,27 @@ import { getAppsDir, resolveAppDir } from "./app-store.js";
|
|
|
26
26
|
|
|
27
27
|
const log = getLogger("app-git");
|
|
28
28
|
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Pending commit message — set by app tool executors, consumed at turn boundary
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
const pendingAppCommitMessages = new Map<string, string>();
|
|
34
|
+
|
|
35
|
+
/** Set the commit message for the next app turn-boundary commit. */
|
|
36
|
+
export function setAppCommitMessage(
|
|
37
|
+
conversationId: string,
|
|
38
|
+
message: string,
|
|
39
|
+
): void {
|
|
40
|
+
pendingAppCommitMessages.set(conversationId, message);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Consume and clear the pending commit message for a conversation. */
|
|
44
|
+
function consumeAppCommitMessage(conversationId: string): string | undefined {
|
|
45
|
+
const msg = pendingAppCommitMessages.get(conversationId);
|
|
46
|
+
pendingAppCommitMessages.delete(conversationId);
|
|
47
|
+
return msg;
|
|
48
|
+
}
|
|
49
|
+
|
|
29
50
|
// ---------------------------------------------------------------------------
|
|
30
51
|
// Types
|
|
31
52
|
// ---------------------------------------------------------------------------
|
|
@@ -170,15 +191,51 @@ export async function commitAppTurnChanges(
|
|
|
170
191
|
conversationId: string,
|
|
171
192
|
turnNumber: number,
|
|
172
193
|
): Promise<void> {
|
|
194
|
+
// Consume before any work that could throw, so the message doesn't leak
|
|
195
|
+
const changeSummary = consumeAppCommitMessage(conversationId);
|
|
173
196
|
try {
|
|
174
197
|
const appsDir = getAppsDir();
|
|
175
198
|
ensureAppGitignoreRules(appsDir);
|
|
176
199
|
|
|
177
200
|
const gitService = getWorkspaceGitService(appsDir);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
201
|
+
|
|
202
|
+
await gitService.commitIfDirty((status) => {
|
|
203
|
+
if (changeSummary) {
|
|
204
|
+
return {
|
|
205
|
+
message: changeSummary,
|
|
206
|
+
metadata: { conversationId, turnNumber },
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Fallback: derive app names from changed file paths
|
|
211
|
+
const allFiles = [
|
|
212
|
+
...new Set([...status.staged, ...status.modified, ...status.untracked]),
|
|
213
|
+
];
|
|
214
|
+
const dirNames = [
|
|
215
|
+
...new Set(allFiles.map((f) => f.split("/")[0].replace(/\.json$/, ""))),
|
|
216
|
+
];
|
|
217
|
+
const appNames = dirNames.map((dirName) => {
|
|
218
|
+
try {
|
|
219
|
+
const jsonPath = join(appsDir, `${dirName}.json`);
|
|
220
|
+
const raw = readFileSync(jsonPath, "utf-8");
|
|
221
|
+
const app = JSON.parse(raw) as { name?: string };
|
|
222
|
+
return app.name || dirName;
|
|
223
|
+
} catch {
|
|
224
|
+
return dirName;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
const subject =
|
|
228
|
+
appNames.length === 1
|
|
229
|
+
? `update ${appNames[0]}`
|
|
230
|
+
: appNames.length <= 3
|
|
231
|
+
? `update ${appNames.join(", ")}`
|
|
232
|
+
: `update ${appNames.length} apps`;
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
message: subject,
|
|
236
|
+
metadata: { conversationId, turnNumber },
|
|
237
|
+
};
|
|
238
|
+
});
|
|
182
239
|
} catch (err) {
|
|
183
240
|
log.error(
|
|
184
241
|
{ err, conversationId, turnNumber },
|
|
@@ -59,6 +59,10 @@ export interface CanonicalGuardianRequest {
|
|
|
59
59
|
requestCode: string | null;
|
|
60
60
|
toolName: string | null;
|
|
61
61
|
inputDigest: string | null;
|
|
62
|
+
commandPreview: string | null;
|
|
63
|
+
riskLevel: string | null;
|
|
64
|
+
activityText: string | null;
|
|
65
|
+
executionTarget: string | null;
|
|
62
66
|
status: CanonicalRequestStatus;
|
|
63
67
|
answerText: string | null;
|
|
64
68
|
decidedByExternalUserId: string | null;
|
|
@@ -149,6 +153,10 @@ function rowToRequest(
|
|
|
149
153
|
requestCode: row.requestCode,
|
|
150
154
|
toolName: row.toolName,
|
|
151
155
|
inputDigest: row.inputDigest,
|
|
156
|
+
commandPreview: row.commandPreview,
|
|
157
|
+
riskLevel: row.riskLevel,
|
|
158
|
+
activityText: row.activityText,
|
|
159
|
+
executionTarget: row.executionTarget,
|
|
152
160
|
status: row.status as CanonicalRequestStatus,
|
|
153
161
|
answerText: row.answerText,
|
|
154
162
|
decidedByExternalUserId: row.decidedByExternalUserId,
|
|
@@ -196,6 +204,10 @@ export interface CreateCanonicalGuardianRequestParams {
|
|
|
196
204
|
requestCode?: string;
|
|
197
205
|
toolName?: string;
|
|
198
206
|
inputDigest?: string;
|
|
207
|
+
commandPreview?: string;
|
|
208
|
+
riskLevel?: string;
|
|
209
|
+
activityText?: string;
|
|
210
|
+
executionTarget?: string;
|
|
199
211
|
status?: CanonicalRequestStatus;
|
|
200
212
|
answerText?: string;
|
|
201
213
|
decidedByExternalUserId?: string;
|
|
@@ -250,6 +262,10 @@ export function createCanonicalGuardianRequest(
|
|
|
250
262
|
requestCode: params.requestCode ?? generateCanonicalRequestCode(),
|
|
251
263
|
toolName: params.toolName ?? null,
|
|
252
264
|
inputDigest: params.inputDigest ?? null,
|
|
265
|
+
commandPreview: params.commandPreview ?? null,
|
|
266
|
+
riskLevel: params.riskLevel ?? null,
|
|
267
|
+
activityText: params.activityText ?? null,
|
|
268
|
+
executionTarget: params.executionTarget ?? null,
|
|
253
269
|
status: params.status ?? ("pending" as const),
|
|
254
270
|
answerText: params.answerText ?? null,
|
|
255
271
|
decidedByExternalUserId: params.decidedByExternalUserId ?? null,
|
package/src/memory/db-init.ts
CHANGED
|
@@ -82,6 +82,7 @@ import {
|
|
|
82
82
|
migrateGuardianDeliveryConversationIndex,
|
|
83
83
|
migrateGuardianPrincipalIdColumns,
|
|
84
84
|
migrateGuardianPrincipalIdNotNull,
|
|
85
|
+
migrateGuardianRequestEnrichmentColumns,
|
|
85
86
|
migrateGuardianTimestampsEpochMs,
|
|
86
87
|
migrateGuardianVerificationPurpose,
|
|
87
88
|
migrateGuardianVerificationSessions,
|
|
@@ -122,6 +123,7 @@ import {
|
|
|
122
123
|
migrateSchemaIndexesAndColumns,
|
|
123
124
|
migrateStripIntegrationPrefixFromProviderKeys,
|
|
124
125
|
migrateUsageDashboardIndexes,
|
|
126
|
+
migrateUsageLlmCallCount,
|
|
125
127
|
migrateVoiceInviteColumns,
|
|
126
128
|
migrateVoiceInviteDisplayMetadata,
|
|
127
129
|
recoverCrashedMigrations,
|
|
@@ -532,6 +534,12 @@ export function initializeDb(): void {
|
|
|
532
534
|
// 96. Drop the setup_skill_id column from oauth_providers (concept removed)
|
|
533
535
|
migrateDropSetupSkillIdColumn(database);
|
|
534
536
|
|
|
537
|
+
// 97. Add enrichment columns to canonical_guardian_requests for guardian approval UX
|
|
538
|
+
migrateGuardianRequestEnrichmentColumns(database);
|
|
539
|
+
|
|
540
|
+
// 98. Add llm_call_count column to llm_usage_events for accurate LLM call counting
|
|
541
|
+
migrateUsageLlmCallCount(database);
|
|
542
|
+
|
|
535
543
|
validateMigrationState(database);
|
|
536
544
|
|
|
537
545
|
if (process.env.BUN_TEST === "1") {
|
|
@@ -3,7 +3,10 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
5
5
|
import { getLogger } from "../util/logger.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getEmbeddingModelsDir,
|
|
8
|
+
getEmbedWorkerPidPath,
|
|
9
|
+
} from "../util/platform.js";
|
|
7
10
|
import { PromiseGuard } from "../util/promise-guard.js";
|
|
8
11
|
import type {
|
|
9
12
|
EmbeddingBackend,
|
|
@@ -404,7 +407,7 @@ export class LocalEmbeddingBackend implements EmbeddingBackend {
|
|
|
404
407
|
if (getIsContainerized()) {
|
|
405
408
|
return join("/tmp", LocalEmbeddingBackend.PID_FILENAME);
|
|
406
409
|
}
|
|
407
|
-
return
|
|
410
|
+
return getEmbedWorkerPidPath();
|
|
408
411
|
}
|
|
409
412
|
|
|
410
413
|
private writePidFile(pid: number): void {
|
package/src/memory/indexer.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { getConfig } from "../config/loader.js";
|
|
|
5
5
|
import type { MemoryConfig } from "../config/types.js";
|
|
6
6
|
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
7
7
|
import { getLogger } from "../util/logger.js";
|
|
8
|
+
import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
|
|
8
9
|
import { getDb } from "./db.js";
|
|
9
10
|
import { selectedBackendSupportsMultimodal } from "./embedding-backend.js";
|
|
10
11
|
import { enqueueMemoryJob, upsertDebouncedJob } from "./jobs-store.js";
|
|
@@ -17,10 +18,8 @@ import { segmentText } from "./segmenter.js";
|
|
|
17
18
|
|
|
18
19
|
const log = getLogger("memory-indexer");
|
|
19
20
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
* summary is only built once the conversation has been idle for this long. */
|
|
23
|
-
const SUMMARY_DEBOUNCE_MS = 3 * 60 * 1000; // 3 minutes
|
|
21
|
+
/** Minimum character length for a segment to be worth storing and embedding (~12-15 tokens). */
|
|
22
|
+
export const MIN_SEGMENT_CHARS = 50;
|
|
24
23
|
|
|
25
24
|
export interface IndexMessageInput {
|
|
26
25
|
messageId: string;
|
|
@@ -59,12 +58,7 @@ export async function indexMessageNow(
|
|
|
59
58
|
|
|
60
59
|
const text = extractTextFromStoredMessageContent(input.content);
|
|
61
60
|
if (text.length === 0) {
|
|
62
|
-
|
|
63
|
-
"build_conversation_summary",
|
|
64
|
-
{ conversationId: input.conversationId },
|
|
65
|
-
Date.now() + SUMMARY_DEBOUNCE_MS,
|
|
66
|
-
);
|
|
67
|
-
return { indexedSegments: 0, enqueuedJobs: 1 };
|
|
61
|
+
return { indexedSegments: 0, enqueuedJobs: 0 };
|
|
68
62
|
}
|
|
69
63
|
|
|
70
64
|
const db = getDb();
|
|
@@ -95,8 +89,13 @@ export async function indexMessageNow(
|
|
|
95
89
|
// Wrap all segment inserts and job enqueues in a single transaction so they
|
|
96
90
|
// either all succeed or all roll back, preventing partial/orphaned state.
|
|
97
91
|
let skippedEmbedJobs = 0;
|
|
92
|
+
let skippedShortSegments = 0;
|
|
98
93
|
db.transaction((tx) => {
|
|
99
94
|
for (const segment of segments) {
|
|
95
|
+
if (segment.text.length < MIN_SEGMENT_CHARS) {
|
|
96
|
+
skippedShortSegments++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
100
99
|
const segmentId = buildSegmentId(input.messageId, segment.segmentIndex);
|
|
101
100
|
const hash = createHash("sha256").update(segment.text).digest("hex");
|
|
102
101
|
|
|
@@ -151,22 +150,42 @@ export async function indexMessageNow(
|
|
|
151
150
|
);
|
|
152
151
|
}
|
|
153
152
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ── Batch extraction tracking ──────────────────────────────────────
|
|
156
|
+
// Instead of per-message extraction, track pending unextracted messages
|
|
157
|
+
// and trigger batch extraction when the threshold is reached or after idle.
|
|
158
|
+
if (shouldExtract && isTrustedActor && !input.automated && config.extraction.useLLM) {
|
|
159
|
+
const pendingKey = `batch_extract:${input.conversationId}:pending_count`;
|
|
160
|
+
const currentVal = getMemoryCheckpoint(pendingKey);
|
|
161
|
+
const pendingCount = (currentVal ? parseInt(currentVal, 10) : 0) + 1;
|
|
162
|
+
setMemoryCheckpoint(pendingKey, String(pendingCount));
|
|
163
|
+
|
|
164
|
+
const batchSize = config.extraction.batchSize ?? 10;
|
|
165
|
+
const idleTimeoutMs = config.extraction.idleTimeoutMs ?? 300_000;
|
|
166
|
+
|
|
167
|
+
if (pendingCount >= batchSize) {
|
|
168
|
+
// Threshold reached — trigger immediate batch extraction
|
|
169
|
+
enqueueMemoryJob("batch_extract", {
|
|
170
|
+
conversationId: input.conversationId,
|
|
171
|
+
scopeId: input.scopeId ?? "default",
|
|
172
|
+
});
|
|
161
173
|
}
|
|
162
174
|
|
|
175
|
+
// Also maintain idle debounce: enqueue a delayed batch_extract that fires
|
|
176
|
+
// if no new messages arrive within the idle timeout window.
|
|
163
177
|
upsertDebouncedJob(
|
|
164
|
-
"
|
|
178
|
+
"batch_extract",
|
|
165
179
|
{ conversationId: input.conversationId },
|
|
166
|
-
Date.now() +
|
|
167
|
-
tx,
|
|
180
|
+
Date.now() + idleTimeoutMs,
|
|
168
181
|
);
|
|
169
|
-
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (skippedShortSegments > 0) {
|
|
185
|
+
log.debug(
|
|
186
|
+
`Skipped ${skippedShortSegments}/${segments.length} segments shorter than ${MIN_SEGMENT_CHARS} chars`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
170
189
|
|
|
171
190
|
if (skippedEmbedJobs > 0) {
|
|
172
191
|
log.debug(
|
|
@@ -188,14 +207,13 @@ export async function indexMessageNow(
|
|
|
188
207
|
log.info("Skipping extraction job: LLM extraction is disabled (useLLM=false)");
|
|
189
208
|
}
|
|
190
209
|
|
|
191
|
-
const
|
|
210
|
+
const storedSegments = segments.length - skippedShortSegments;
|
|
192
211
|
const enqueuedJobs =
|
|
193
|
-
|
|
212
|
+
storedSegments -
|
|
194
213
|
skippedEmbedJobs +
|
|
195
|
-
mediaBlocks.length
|
|
196
|
-
(shouldExtract && !extractionGated ? 2 : 1);
|
|
214
|
+
mediaBlocks.length;
|
|
197
215
|
return {
|
|
198
|
-
indexedSegments:
|
|
216
|
+
indexedSegments: storedSegments,
|
|
199
217
|
enqueuedJobs,
|
|
200
218
|
};
|
|
201
219
|
}
|