@vellumai/assistant 0.4.48 → 0.4.50
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/ARCHITECTURE.md +26 -35
- package/README.md +5 -26
- package/docs/architecture/integrations.md +45 -41
- package/docs/architecture/keychain-broker.md +3 -3
- package/docs/architecture/memory.md +180 -119
- package/docs/runbook-trusted-contacts.md +3 -8
- package/hook-templates/debug-prompt-logger/hook.json +1 -1
- package/hook-templates/debug-prompt-logger/run.sh +1 -3
- package/package.json +2 -2
- package/src/__tests__/actor-token-service.test.ts +0 -1
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +249 -2
- package/src/__tests__/approval-cascade.test.ts +796 -0
- package/src/__tests__/approval-primitive.test.ts +0 -1
- package/src/__tests__/approval-routes-http.test.ts +4 -0
- package/src/__tests__/assistant-attachments.test.ts +12 -34
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/channel-guardian.test.ts +0 -2
- package/src/__tests__/channel-readiness-routes.test.ts +15 -6
- package/src/__tests__/channel-readiness-service.test.ts +10 -9
- package/src/__tests__/checker.test.ts +13 -20
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-tools.test.ts +2 -19
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-image-dimensions.test.ts +332 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/context-token-estimator.test.ts +196 -13
- package/src/__tests__/conversation-attention-store.test.ts +0 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +152 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
- package/src/__tests__/credential-metadata-store.test.ts +64 -73
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +13 -7
- package/src/__tests__/credential-vault-unit.test.ts +284 -49
- package/src/__tests__/credential-vault.test.ts +150 -16
- package/src/__tests__/credentials-cli.test.ts +71 -0
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
- package/src/__tests__/heartbeat-service.test.ts +0 -1
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +791 -0
- package/src/__tests__/host-shell-tool.test.ts +27 -15
- package/src/__tests__/http-user-message-parity.test.ts +2 -0
- package/src/__tests__/ingress-url-consistency.test.ts +14 -21
- package/src/__tests__/integration-status.test.ts +32 -51
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/invite-redemption-service.test.ts +65 -1
- package/src/__tests__/invite-routes-http.test.ts +10 -9
- package/src/__tests__/keychain-broker-client.test.ts +14 -46
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/notification-routing-intent.test.ts +0 -1
- package/src/__tests__/oauth-cli.test.ts +941 -15
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/oauth-store.test.ts +870 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +0 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +15 -21
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/recording-handler.test.ts +3 -4
- package/src/__tests__/registry.test.ts +2 -3
- package/src/__tests__/relay-server.test.ts +46 -1
- package/src/__tests__/runtime-events-sse.test.ts +55 -7
- package/src/__tests__/schedule-store.test.ts +0 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/scheduler-recurrence.test.ts +0 -1
- package/src/__tests__/scoped-approval-grants.test.ts +0 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-ingress-handler.test.ts +0 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secure-keys.test.ts +7 -2
- package/src/__tests__/send-endpoint-busy.test.ts +24 -6
- package/src/__tests__/sequence-store.test.ts +0 -1
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-init.benchmark.test.ts +4 -5
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skill-include-graph.test.ts +66 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-load-tool.test.ts +149 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills-uninstall.test.ts +1 -1
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +67 -3
- package/src/__tests__/slack-share-routes.test.ts +17 -19
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
- package/src/__tests__/terminal-tools.test.ts +4 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
- package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
- package/src/__tests__/trust-store.test.ts +7 -13
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +0 -16
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-invite-redemption.test.ts +32 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/agent/ax-tree-compaction.test.ts +286 -0
- package/src/agent/loop.ts +104 -131
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +133 -6
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +52 -18
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +3 -8
- package/src/calls/twilio-routes.ts +1 -2
- package/src/calls/types.ts +3 -1
- package/src/calls/voice-ingress-preflight.ts +1 -1
- package/src/cli/commands/browser-relay.ts +18 -12
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/credentials.ts +101 -15
- package/src/cli/commands/doctor.ts +4 -3
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +284 -0
- package/src/cli/commands/oauth/connections.ts +633 -0
- package/src/cli/commands/oauth/index.ts +52 -0
- package/src/cli/commands/oauth/providers.ts +256 -0
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +177 -339
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +6 -11
- package/src/cli/reference.ts +1 -3
- package/src/cli.ts +4 -10
- package/src/config/assistant-feature-flags.ts +0 -3
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +23 -5
- package/src/config/bundled-skills/computer-use/tools/{computer-use-request-control.ts → computer-use-observe.ts} +1 -5
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
- package/src/config/bundled-skills/settings/SKILL.md +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +2 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/env-registry.ts +14 -83
- package/src/config/env.ts +11 -50
- package/src/config/feature-flag-registry.json +16 -16
- package/src/config/loader.ts +0 -6
- package/src/config/schema.ts +4 -13
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/skills.ts +21 -2
- package/src/config/types.ts +0 -4
- package/src/context/image-dimensions.ts +229 -0
- package/src/context/token-estimator.ts +75 -12
- package/src/context/window-manager.ts +53 -11
- package/src/daemon/assistant-attachments.ts +1 -13
- package/src/daemon/config-watcher.ts +61 -3
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +8 -33
- package/src/daemon/handlers/config-slack-channel.ts +49 -46
- package/src/daemon/handlers/config-telegram.ts +32 -16
- package/src/daemon/handlers/sessions.ts +27 -36
- package/src/daemon/handlers/shared.ts +0 -130
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +430 -0
- package/src/daemon/lifecycle.ts +67 -71
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/computer-use.ts +1 -129
- package/src/daemon/message-types/host-cu.ts +19 -0
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +4 -0
- package/src/daemon/message-types/sessions.ts +4 -0
- package/src/daemon/server.ts +25 -21
- package/src/daemon/session-agent-loop-handlers.ts +40 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-attachments.ts +1 -2
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +1 -1
- package/src/daemon/session-surfaces.ts +43 -28
- package/src/daemon/session-tool-setup.ts +9 -10
- package/src/daemon/session.ts +150 -17
- package/src/daemon/tool-side-effects.ts +2 -8
- package/src/daemon/watch-handler.ts +2 -2
- package/src/events/tool-metrics-listener.ts +2 -2
- package/src/hooks/manager.ts +1 -4
- package/src/inbound/public-ingress-urls.ts +7 -7
- package/src/instrument.ts +61 -1
- package/src/logfire.ts +16 -5
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-key-store.ts +21 -0
- package/src/memory/conversation-queries.ts +22 -3
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +62 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +67 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +4 -4
- package/src/messaging/providers/gmail/client.ts +82 -2
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/gmail/people-client.ts +10 -10
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
- package/src/messaging/providers/whatsapp/adapter.ts +11 -8
- package/src/messaging/registry.ts +2 -32
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/signal.ts +4 -5
- package/src/oauth/byo-connection.test.ts +133 -25
- package/src/oauth/byo-connection.ts +22 -6
- package/src/oauth/connect-orchestrator.ts +113 -57
- package/src/oauth/connect-types.ts +17 -23
- package/src/oauth/connection-resolver.ts +35 -11
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +104 -0
- package/src/oauth/oauth-store.ts +582 -0
- package/src/oauth/platform-connection.test.ts +29 -0
- package/src/oauth/platform-connection.ts +6 -5
- package/src/oauth/provider-behaviors.ts +124 -0
- package/src/oauth/scope-policy.ts +9 -2
- package/src/oauth/seed-providers.ts +167 -0
- package/src/oauth/token-persistence.ts +81 -77
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +1 -1
- package/src/permissions/prompter.ts +10 -1
- package/src/permissions/trust-store.ts +36 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
- package/src/prompts/system-prompt.ts +46 -42
- package/src/providers/anthropic/client.ts +59 -20
- package/src/providers/retry.ts +1 -27
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -6
- package/src/runtime/channel-reply-delivery.ts +0 -40
- package/src/runtime/gateway-client.ts +0 -7
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +10 -8
- package/src/runtime/http-types.ts +2 -2
- package/src/runtime/invite-redemption-service.ts +19 -1
- package/src/runtime/invite-service.ts +25 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -11
- package/src/runtime/pending-interactions.ts +14 -12
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/channel-delivery-routes.ts +0 -1
- package/src/runtime/routes/conversation-routes.ts +81 -19
- package/src/runtime/routes/events-routes.ts +21 -11
- package/src/runtime/routes/host-cu-routes.ts +97 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
- package/src/runtime/routes/integrations/slack/share.ts +6 -7
- package/src/runtime/routes/log-export-routes.ts +126 -8
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +55 -48
- package/src/runtime/routes/surface-action-routes.ts +1 -1
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/watch-routes.ts +128 -0
- package/src/runtime/routes/workspace-routes.ts +2 -1
- package/src/schedule/integration-status.ts +10 -9
- package/src/security/credential-key.ts +0 -156
- package/src/security/keychain-broker-client.ts +22 -10
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +25 -3
- package/src/security/token-manager.ts +137 -64
- package/src/skills/catalog-install.ts +414 -0
- package/src/skills/include-graph.ts +32 -0
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/telegram/bot-username.ts +2 -3
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/browser/network-recorder.ts +1 -1
- package/src/tools/browser/network-recording-types.ts +1 -1
- package/src/tools/computer-use/definitions.ts +36 -11
- package/src/tools/computer-use/registry.ts +5 -6
- package/src/tools/credentials/broker.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +17 -121
- package/src/tools/credentials/vault.ts +92 -167
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/registry.ts +2 -7
- package/src/tools/schedule/create.ts +8 -1
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +85 -3
- package/src/tools/watch/watch-state.ts +0 -12
- package/src/util/logger.ts +7 -41
- package/src/util/platform.ts +9 -28
- package/src/watcher/providers/google-calendar.ts +2 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
- package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
- package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
- package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
- package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/cli/commands/dev.ts +0 -129
- package/src/cli/commands/map.ts +0 -391
- package/src/cli/commands/oauth.ts +0 -77
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/computer-use-session.ts +0 -1026
- package/src/daemon/ride-shotgun-handler.ts +0 -569
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/oauth/provider-base-urls.ts +0 -21
- package/src/oauth/provider-profiles.ts +0 -192
- package/src/prompts/computer-use-prompt.ts +0 -98
- package/src/runtime/routes/computer-use-routes.ts +0 -641
- package/src/runtime/routes/mcp-routes.ts +0 -20
- package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
- package/src/runtime/telegram-streaming-delivery.ts +0 -393
- package/src/tools/computer-use/request-computer-control.ts +0 -56
|
@@ -161,60 +161,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
161
161
|
expect(result.secretDetection.action).toBe("block");
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
-
test("applies
|
|
165
|
-
const result = AssistantConfigSchema.parse({});
|
|
166
|
-
expect(result.memory.conflicts).toEqual({
|
|
167
|
-
enabled: true,
|
|
168
|
-
gateMode: "soft",
|
|
169
|
-
resolverLlmTimeoutMs: 12000,
|
|
170
|
-
relevanceThreshold: 0.3,
|
|
171
|
-
conflictableKinds: [
|
|
172
|
-
"preference",
|
|
173
|
-
"profile",
|
|
174
|
-
"constraint",
|
|
175
|
-
"instruction",
|
|
176
|
-
"style",
|
|
177
|
-
],
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test("rejects invalid memory.conflicts.relevanceThreshold", () => {
|
|
182
|
-
const result = AssistantConfigSchema.safeParse({
|
|
183
|
-
memory: { conflicts: { relevanceThreshold: 2 } },
|
|
184
|
-
});
|
|
185
|
-
expect(result.success).toBe(false);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test("rejects invalid memory.conflicts.conflictableKinds entry", () => {
|
|
189
|
-
const result = AssistantConfigSchema.safeParse({
|
|
190
|
-
memory: { conflicts: { conflictableKinds: ["invalid_kind"] } },
|
|
191
|
-
});
|
|
192
|
-
expect(result.success).toBe(false);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("rejects empty memory.conflicts.conflictableKinds", () => {
|
|
196
|
-
const result = AssistantConfigSchema.safeParse({
|
|
197
|
-
memory: { conflicts: { conflictableKinds: [] } },
|
|
198
|
-
});
|
|
199
|
-
expect(result.success).toBe(false);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("applies memory.profile defaults", () => {
|
|
203
|
-
const result = AssistantConfigSchema.parse({});
|
|
204
|
-
expect(result.memory.profile).toEqual({
|
|
205
|
-
enabled: true,
|
|
206
|
-
maxInjectTokens: 800,
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
test("rejects invalid memory.profile.maxInjectTokens", () => {
|
|
211
|
-
const result = AssistantConfigSchema.safeParse({
|
|
212
|
-
memory: { profile: { maxInjectTokens: 0 } },
|
|
213
|
-
});
|
|
214
|
-
expect(result.success).toBe(false);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test("applies rollout defaults for dynamic budget and entity relation features", () => {
|
|
164
|
+
test("applies rollout defaults for dynamic budget", () => {
|
|
218
165
|
const result = AssistantConfigSchema.parse({});
|
|
219
166
|
expect(result.memory.retrieval.dynamicBudget).toEqual({
|
|
220
167
|
enabled: true,
|
|
@@ -222,19 +169,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
222
169
|
maxInjectTokens: 10000,
|
|
223
170
|
targetHeadroomTokens: 10000,
|
|
224
171
|
});
|
|
225
|
-
expect(result.memory.entity.extractRelations).toEqual({
|
|
226
|
-
enabled: true,
|
|
227
|
-
backfillBatchSize: 200,
|
|
228
|
-
});
|
|
229
|
-
expect(result.memory.entity.relationRetrieval).toEqual({
|
|
230
|
-
enabled: true,
|
|
231
|
-
maxSeedEntities: 8,
|
|
232
|
-
maxNeighborEntities: 20,
|
|
233
|
-
maxEdges: 40,
|
|
234
|
-
neighborScoreMultiplier: 0.7,
|
|
235
|
-
maxDepth: 3,
|
|
236
|
-
depthDecay: true,
|
|
237
|
-
});
|
|
238
172
|
});
|
|
239
173
|
|
|
240
174
|
test("applies memory.cleanup defaults", () => {
|
|
@@ -242,7 +176,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
242
176
|
expect(result.memory.cleanup).toEqual({
|
|
243
177
|
enabled: true,
|
|
244
178
|
enqueueIntervalMs: 6 * 60 * 60 * 1000,
|
|
245
|
-
resolvedConflictRetentionMs: 30 * 24 * 60 * 60 * 1000,
|
|
246
179
|
supersededItemRetentionMs: 30 * 24 * 60 * 60 * 1000,
|
|
247
180
|
conversationRetentionDays: 90,
|
|
248
181
|
});
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { describe, expect, it } from "bun:test";
|
|
4
|
+
|
|
5
|
+
import { parseImageDimensions } from "../context/image-dimensions.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper: build a Buffer of given bytes and return its base64 encoding.
|
|
9
|
+
*/
|
|
10
|
+
function toBase64(bytes: number[]): string {
|
|
11
|
+
return Buffer.from(bytes).toString("base64");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Minimal valid PNG IHDR: 8-byte signature + 13-byte IHDR chunk.
|
|
16
|
+
* Width = 320, Height = 240.
|
|
17
|
+
*/
|
|
18
|
+
function minimalPngHeader(width: number, height: number): number[] {
|
|
19
|
+
const sig = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
20
|
+
// IHDR chunk: length (13 = 0x0000000D), "IHDR", width(4), height(4), bitDepth, colorType, compression, filter, interlace
|
|
21
|
+
const ihdrLength = [0x00, 0x00, 0x00, 0x0d];
|
|
22
|
+
const ihdrType = [0x49, 0x48, 0x44, 0x52]; // "IHDR"
|
|
23
|
+
const w = [
|
|
24
|
+
(width >> 24) & 0xff,
|
|
25
|
+
(width >> 16) & 0xff,
|
|
26
|
+
(width >> 8) & 0xff,
|
|
27
|
+
width & 0xff,
|
|
28
|
+
];
|
|
29
|
+
const h = [
|
|
30
|
+
(height >> 24) & 0xff,
|
|
31
|
+
(height >> 16) & 0xff,
|
|
32
|
+
(height >> 8) & 0xff,
|
|
33
|
+
height & 0xff,
|
|
34
|
+
];
|
|
35
|
+
const rest = [0x08, 0x06, 0x00, 0x00, 0x00]; // bit depth, color type RGBA, compression, filter, interlace
|
|
36
|
+
const crc = [0x00, 0x00, 0x00, 0x00]; // dummy CRC (not validated by parser)
|
|
37
|
+
return [...sig, ...ihdrLength, ...ihdrType, ...w, ...h, ...rest, ...crc];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Minimal valid JPEG with SOF0 marker.
|
|
42
|
+
* Structure: SOI + APP0 (short) + SOF0 with given dimensions.
|
|
43
|
+
*/
|
|
44
|
+
function minimalJpegHeader(width: number, height: number): number[] {
|
|
45
|
+
const soi = [0xff, 0xd8]; // Start of image
|
|
46
|
+
// APP0 marker (JFIF) - minimal
|
|
47
|
+
const app0 = [
|
|
48
|
+
0xff,
|
|
49
|
+
0xe0, // APP0 marker
|
|
50
|
+
0x00,
|
|
51
|
+
0x10, // length = 16
|
|
52
|
+
0x4a,
|
|
53
|
+
0x46,
|
|
54
|
+
0x49,
|
|
55
|
+
0x46,
|
|
56
|
+
0x00, // "JFIF\0"
|
|
57
|
+
0x01,
|
|
58
|
+
0x01, // version 1.1
|
|
59
|
+
0x00, // aspect ratio units
|
|
60
|
+
0x00,
|
|
61
|
+
0x01, // X density
|
|
62
|
+
0x00,
|
|
63
|
+
0x01, // Y density
|
|
64
|
+
0x00,
|
|
65
|
+
0x00, // no thumbnail
|
|
66
|
+
];
|
|
67
|
+
// SOF0 marker
|
|
68
|
+
const sof0 = [
|
|
69
|
+
0xff,
|
|
70
|
+
0xc0, // SOF0 marker
|
|
71
|
+
0x00,
|
|
72
|
+
0x0b, // length = 11
|
|
73
|
+
0x08, // precision = 8 bits
|
|
74
|
+
(height >> 8) & 0xff,
|
|
75
|
+
height & 0xff, // height
|
|
76
|
+
(width >> 8) & 0xff,
|
|
77
|
+
width & 0xff, // width
|
|
78
|
+
0x03, // number of components
|
|
79
|
+
0x01,
|
|
80
|
+
0x11,
|
|
81
|
+
0x00, // Y component
|
|
82
|
+
];
|
|
83
|
+
return [...soi, ...app0, ...sof0];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Minimal valid GIF89a header with given dimensions.
|
|
88
|
+
*/
|
|
89
|
+
function minimalGifHeader(width: number, height: number): number[] {
|
|
90
|
+
// "GIF89a"
|
|
91
|
+
const sig = [0x47, 0x49, 0x46, 0x38, 0x39, 0x61];
|
|
92
|
+
const w = [width & 0xff, (width >> 8) & 0xff]; // little-endian uint16
|
|
93
|
+
const h = [height & 0xff, (height >> 8) & 0xff]; // little-endian uint16
|
|
94
|
+
return [...sig, ...w, ...h, 0x00, 0x00]; // pad to 12 bytes
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Minimal valid WebP VP8 (lossy) header with given dimensions.
|
|
99
|
+
*/
|
|
100
|
+
function minimalWebpVP8Header(width: number, height: number): number[] {
|
|
101
|
+
const riff = [0x52, 0x49, 0x46, 0x46]; // "RIFF"
|
|
102
|
+
const fileSize = [0x00, 0x00, 0x00, 0x00]; // dummy file size
|
|
103
|
+
const webp = [0x57, 0x45, 0x42, 0x50]; // "WEBP"
|
|
104
|
+
const vp8 = [0x56, 0x50, 0x38, 0x20]; // "VP8 "
|
|
105
|
+
const chunkSize = [0x00, 0x00, 0x00, 0x00]; // dummy chunk size
|
|
106
|
+
// VP8 bitstream header (bytes 20-25): frame tag + start code
|
|
107
|
+
const frameTag = [0x9d, 0x01, 0x2a]; // key frame tag bytes
|
|
108
|
+
const padding = [0x00, 0x00, 0x00]; // padding to reach offset 26
|
|
109
|
+
// Width at byte 26 (LE uint16), height at byte 28 (LE uint16)
|
|
110
|
+
const w = [width & 0xff, (width >> 8) & 0x3f]; // little-endian uint16, upper bits masked
|
|
111
|
+
const h = [height & 0xff, (height >> 8) & 0x3f]; // little-endian uint16, upper bits masked
|
|
112
|
+
return [
|
|
113
|
+
...riff,
|
|
114
|
+
...fileSize,
|
|
115
|
+
...webp,
|
|
116
|
+
...vp8,
|
|
117
|
+
...chunkSize,
|
|
118
|
+
...frameTag,
|
|
119
|
+
...padding,
|
|
120
|
+
...w,
|
|
121
|
+
...h,
|
|
122
|
+
0x00,
|
|
123
|
+
0x00,
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Minimal valid WebP VP8L (lossless) header with given dimensions.
|
|
129
|
+
*/
|
|
130
|
+
function minimalWebpVP8LHeader(width: number, height: number): number[] {
|
|
131
|
+
const riff = [0x52, 0x49, 0x46, 0x46];
|
|
132
|
+
const fileSize = [0x00, 0x00, 0x00, 0x00];
|
|
133
|
+
const webp = [0x57, 0x45, 0x42, 0x50];
|
|
134
|
+
const vp8l = [0x56, 0x50, 0x38, 0x4c]; // "VP8L"
|
|
135
|
+
const chunkSize = [0x00, 0x00, 0x00, 0x00];
|
|
136
|
+
// Signature byte at offset 20
|
|
137
|
+
const sigByte = [0x2f];
|
|
138
|
+
// At offset 21: LE uint32 encoding width-1 in bits 0-13 and height-1 in bits 14-27
|
|
139
|
+
const bits = ((width - 1) & 0x3fff) | (((height - 1) & 0x3fff) << 14);
|
|
140
|
+
const bitsBytes = [
|
|
141
|
+
bits & 0xff,
|
|
142
|
+
(bits >> 8) & 0xff,
|
|
143
|
+
(bits >> 16) & 0xff,
|
|
144
|
+
(bits >> 24) & 0xff,
|
|
145
|
+
];
|
|
146
|
+
return [
|
|
147
|
+
...riff,
|
|
148
|
+
...fileSize,
|
|
149
|
+
...webp,
|
|
150
|
+
...vp8l,
|
|
151
|
+
...chunkSize,
|
|
152
|
+
...sigByte,
|
|
153
|
+
...bitsBytes,
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Minimal valid WebP VP8X (extended) header with given dimensions.
|
|
159
|
+
*/
|
|
160
|
+
function minimalWebpVP8XHeader(width: number, height: number): number[] {
|
|
161
|
+
const riff = [0x52, 0x49, 0x46, 0x46];
|
|
162
|
+
const fileSize = [0x00, 0x00, 0x00, 0x00];
|
|
163
|
+
const webp = [0x57, 0x45, 0x42, 0x50];
|
|
164
|
+
const vp8x = [0x56, 0x50, 0x38, 0x58]; // "VP8X"
|
|
165
|
+
const chunkSize = [0x0a, 0x00, 0x00, 0x00]; // chunk size = 10
|
|
166
|
+
const flags = [0x00, 0x00, 0x00, 0x00]; // flags (bytes 20-23)
|
|
167
|
+
// Width-1 as LE uint24 at offset 24
|
|
168
|
+
const w1 = width - 1;
|
|
169
|
+
const wBytes = [w1 & 0xff, (w1 >> 8) & 0xff, (w1 >> 16) & 0xff];
|
|
170
|
+
// Height-1 as LE uint24 at offset 27
|
|
171
|
+
const h1 = height - 1;
|
|
172
|
+
const hBytes = [h1 & 0xff, (h1 >> 8) & 0xff, (h1 >> 16) & 0xff];
|
|
173
|
+
return [
|
|
174
|
+
...riff,
|
|
175
|
+
...fileSize,
|
|
176
|
+
...webp,
|
|
177
|
+
...vp8x,
|
|
178
|
+
...chunkSize,
|
|
179
|
+
...flags,
|
|
180
|
+
...wBytes,
|
|
181
|
+
...hBytes,
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
describe("parseImageDimensions", () => {
|
|
186
|
+
describe("PNG", () => {
|
|
187
|
+
it("extracts dimensions from a valid PNG header", () => {
|
|
188
|
+
const base64 = toBase64(minimalPngHeader(320, 240));
|
|
189
|
+
const result = parseImageDimensions(base64, "image/png");
|
|
190
|
+
expect(result).toEqual({ width: 320, height: 240 });
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("extracts dimensions from a large PNG", () => {
|
|
194
|
+
const base64 = toBase64(minimalPngHeader(3840, 2160));
|
|
195
|
+
const result = parseImageDimensions(base64, "image/png");
|
|
196
|
+
expect(result).toEqual({ width: 3840, height: 2160 });
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("returns null for truncated PNG data", () => {
|
|
200
|
+
const bytes = minimalPngHeader(320, 240);
|
|
201
|
+
const truncated = toBase64(bytes.slice(0, 10));
|
|
202
|
+
expect(parseImageDimensions(truncated, "image/png")).toBeNull();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("returns null for corrupt PNG signature", () => {
|
|
206
|
+
const bytes = minimalPngHeader(320, 240);
|
|
207
|
+
bytes[0] = 0x00; // corrupt signature
|
|
208
|
+
expect(parseImageDimensions(toBase64(bytes), "image/png")).toBeNull();
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe("JPEG", () => {
|
|
213
|
+
it("extracts dimensions from a valid JPEG with SOF0", () => {
|
|
214
|
+
const base64 = toBase64(minimalJpegHeader(640, 480));
|
|
215
|
+
const result = parseImageDimensions(base64, "image/jpeg");
|
|
216
|
+
expect(result).toEqual({ width: 640, height: 480 });
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("extracts dimensions from a JPEG with SOF2 (progressive)", () => {
|
|
220
|
+
const bytes = minimalJpegHeader(800, 600);
|
|
221
|
+
// Change SOF0 (0xC0) to SOF2 (0xC2)
|
|
222
|
+
const sof0Idx = bytes.indexOf(0xc0, 2);
|
|
223
|
+
bytes[sof0Idx] = 0xc2;
|
|
224
|
+
const result = parseImageDimensions(toBase64(bytes), "image/jpeg");
|
|
225
|
+
expect(result).toEqual({ width: 800, height: 600 });
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("returns null for truncated JPEG data", () => {
|
|
229
|
+
const truncated = toBase64([0xff, 0xd8, 0xff, 0xc0]);
|
|
230
|
+
expect(parseImageDimensions(truncated, "image/jpeg")).toBeNull();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("returns null for corrupt JPEG (missing SOI)", () => {
|
|
234
|
+
const bytes = minimalJpegHeader(640, 480);
|
|
235
|
+
bytes[0] = 0x00;
|
|
236
|
+
expect(parseImageDimensions(toBase64(bytes), "image/jpeg")).toBeNull();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe("GIF", () => {
|
|
241
|
+
it("extracts dimensions from a valid GIF89a header", () => {
|
|
242
|
+
const base64 = toBase64(minimalGifHeader(100, 50));
|
|
243
|
+
const result = parseImageDimensions(base64, "image/gif");
|
|
244
|
+
expect(result).toEqual({ width: 100, height: 50 });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("extracts dimensions from GIF87a header", () => {
|
|
248
|
+
const bytes = minimalGifHeader(256, 128);
|
|
249
|
+
bytes[4] = 0x37; // Change '9' to '7' for GIF87a — signature check is GIF8 only
|
|
250
|
+
bytes[5] = 0x61;
|
|
251
|
+
const result = parseImageDimensions(toBase64(bytes), "image/gif");
|
|
252
|
+
expect(result).toEqual({ width: 256, height: 128 });
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("returns null for truncated GIF data", () => {
|
|
256
|
+
const truncated = toBase64([0x47, 0x49, 0x46, 0x38, 0x39, 0x61]);
|
|
257
|
+
expect(parseImageDimensions(truncated, "image/gif")).toBeNull();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("returns null for corrupt GIF signature", () => {
|
|
261
|
+
const bytes = minimalGifHeader(100, 50);
|
|
262
|
+
bytes[0] = 0x00;
|
|
263
|
+
expect(parseImageDimensions(toBase64(bytes), "image/gif")).toBeNull();
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe("WebP", () => {
|
|
268
|
+
it("extracts dimensions from a VP8 (lossy) WebP", () => {
|
|
269
|
+
const base64 = toBase64(minimalWebpVP8Header(400, 300));
|
|
270
|
+
const result = parseImageDimensions(base64, "image/webp");
|
|
271
|
+
expect(result).toEqual({ width: 400, height: 300 });
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("extracts dimensions from a VP8L (lossless) WebP", () => {
|
|
275
|
+
const base64 = toBase64(minimalWebpVP8LHeader(500, 250));
|
|
276
|
+
const result = parseImageDimensions(base64, "image/webp");
|
|
277
|
+
expect(result).toEqual({ width: 500, height: 250 });
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("extracts dimensions from a VP8X (extended) WebP", () => {
|
|
281
|
+
const base64 = toBase64(minimalWebpVP8XHeader(1920, 1080));
|
|
282
|
+
const result = parseImageDimensions(base64, "image/webp");
|
|
283
|
+
expect(result).toEqual({ width: 1920, height: 1080 });
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("returns null for truncated WebP data", () => {
|
|
287
|
+
const truncated = toBase64([
|
|
288
|
+
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00,
|
|
289
|
+
]);
|
|
290
|
+
expect(parseImageDimensions(truncated, "image/webp")).toBeNull();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("returns null for corrupt RIFF signature", () => {
|
|
294
|
+
const bytes = minimalWebpVP8Header(400, 300);
|
|
295
|
+
bytes[0] = 0x00;
|
|
296
|
+
expect(parseImageDimensions(toBase64(bytes), "image/webp")).toBeNull();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe("unknown media type", () => {
|
|
301
|
+
it("returns null for unsupported media type", () => {
|
|
302
|
+
expect(parseImageDimensions("AAAA", "image/bmp")).toBeNull();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it("returns null for non-image media type", () => {
|
|
306
|
+
expect(parseImageDimensions("AAAA", "application/pdf")).toBeNull();
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe("empty/invalid data", () => {
|
|
311
|
+
it("returns null for empty base64 string", () => {
|
|
312
|
+
expect(parseImageDimensions("", "image/png")).toBeNull();
|
|
313
|
+
expect(parseImageDimensions("", "image/jpeg")).toBeNull();
|
|
314
|
+
expect(parseImageDimensions("", "image/gif")).toBeNull();
|
|
315
|
+
expect(parseImageDimensions("", "image/webp")).toBeNull();
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe("real image file", () => {
|
|
320
|
+
it("parses dimensions from an actual PNG file in the repo", () => {
|
|
321
|
+
const pngPath = join(
|
|
322
|
+
import.meta.dir,
|
|
323
|
+
"../../..",
|
|
324
|
+
"clients/chrome-extension/icons/icon16.png",
|
|
325
|
+
);
|
|
326
|
+
const pngData = readFileSync(pngPath);
|
|
327
|
+
const base64 = pngData.toString("base64");
|
|
328
|
+
const result = parseImageDimensions(base64, "image/png");
|
|
329
|
+
expect(result).toEqual({ width: 16, height: 16 });
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
});
|
|
@@ -20,9 +20,6 @@ import { computeRecallBudget } from "../memory/retrieval-budget.js";
|
|
|
20
20
|
import { buildMemoryRecall } from "../memory/retriever.js";
|
|
21
21
|
import {
|
|
22
22
|
conversations,
|
|
23
|
-
memoryEntities,
|
|
24
|
-
memoryEntityRelations,
|
|
25
|
-
memoryItemEntities,
|
|
26
23
|
memoryItems,
|
|
27
24
|
memoryItemSources,
|
|
28
25
|
messages,
|
|
@@ -52,6 +49,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
52
49
|
mock.module("../memory/qdrant-client.js", () => ({
|
|
53
50
|
getQdrantClient: () => ({
|
|
54
51
|
searchWithFilter: async () => [],
|
|
52
|
+
hybridSearch: async () => [],
|
|
55
53
|
upsertPoints: async () => {},
|
|
56
54
|
deletePoints: async () => {},
|
|
57
55
|
}),
|
|
@@ -168,13 +166,9 @@ describe("Context + Memory E2E regression", () => {
|
|
|
168
166
|
beforeEach(() => {
|
|
169
167
|
const db = getDb();
|
|
170
168
|
db.run("DELETE FROM memory_item_sources");
|
|
171
|
-
db.run("DELETE FROM memory_item_entities");
|
|
172
|
-
db.run("DELETE FROM memory_entity_relations");
|
|
173
|
-
db.run("DELETE FROM memory_entities");
|
|
174
169
|
db.run("DELETE FROM memory_embeddings");
|
|
175
|
-
db.run("DELETE FROM memory_summaries");
|
|
176
170
|
db.run("DELETE FROM memory_items");
|
|
177
|
-
|
|
171
|
+
|
|
178
172
|
db.run("DELETE FROM memory_segments");
|
|
179
173
|
db.run("DELETE FROM messages");
|
|
180
174
|
db.run("DELETE FROM conversations");
|
|
@@ -279,43 +273,6 @@ describe("Context + Memory E2E regression", () => {
|
|
|
279
273
|
now + 100_000,
|
|
280
274
|
);
|
|
281
275
|
|
|
282
|
-
db.insert(memoryEntities)
|
|
283
|
-
.values([
|
|
284
|
-
{
|
|
285
|
-
id: "entity-apollo",
|
|
286
|
-
name: "Apollo",
|
|
287
|
-
type: "project",
|
|
288
|
-
aliases: JSON.stringify(["project-apollo"]),
|
|
289
|
-
description: null,
|
|
290
|
-
firstSeenAt: now,
|
|
291
|
-
lastSeenAt: now + 100_000,
|
|
292
|
-
mentionCount: 6,
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
id: "entity-hermes",
|
|
296
|
-
name: "HermesGate",
|
|
297
|
-
type: "strategy",
|
|
298
|
-
aliases: JSON.stringify(["hermes-gate"]),
|
|
299
|
-
description: null,
|
|
300
|
-
firstSeenAt: now,
|
|
301
|
-
lastSeenAt: now + 100_000,
|
|
302
|
-
mentionCount: 4,
|
|
303
|
-
},
|
|
304
|
-
])
|
|
305
|
-
.run();
|
|
306
|
-
|
|
307
|
-
db.insert(memoryEntityRelations)
|
|
308
|
-
.values({
|
|
309
|
-
id: "rel-apollo-hermes",
|
|
310
|
-
sourceEntityId: "entity-apollo",
|
|
311
|
-
targetEntityId: "entity-hermes",
|
|
312
|
-
relation: "uses",
|
|
313
|
-
evidence: "Apollo uses HermesGate for risky changes",
|
|
314
|
-
firstSeenAt: now,
|
|
315
|
-
lastSeenAt: now + 50_000,
|
|
316
|
-
})
|
|
317
|
-
.run();
|
|
318
|
-
|
|
319
276
|
insertMemoryItem({
|
|
320
277
|
id: "item-apollo-direct",
|
|
321
278
|
kind: "preference",
|
|
@@ -335,16 +292,10 @@ describe("Context + Memory E2E regression", () => {
|
|
|
335
292
|
createdAt: now + 10_000,
|
|
336
293
|
})
|
|
337
294
|
.run();
|
|
338
|
-
db.insert(memoryItemEntities)
|
|
339
|
-
.values({
|
|
340
|
-
memoryItemId: "item-apollo-direct",
|
|
341
|
-
entityId: "entity-apollo",
|
|
342
|
-
})
|
|
343
|
-
.run();
|
|
344
295
|
|
|
345
296
|
insertMemoryItem({
|
|
346
297
|
id: "item-hermes-relation",
|
|
347
|
-
kind: "
|
|
298
|
+
kind: "identity",
|
|
348
299
|
subject: "hermes rollout execution",
|
|
349
300
|
statement:
|
|
350
301
|
"HermesGate rollout guidance: start at 5% traffic and promote only after error budget checks pass.",
|
|
@@ -361,12 +312,6 @@ describe("Context + Memory E2E regression", () => {
|
|
|
361
312
|
createdAt: now + 12_000,
|
|
362
313
|
})
|
|
363
314
|
.run();
|
|
364
|
-
db.insert(memoryItemEntities)
|
|
365
|
-
.values({
|
|
366
|
-
memoryItemId: "item-hermes-relation",
|
|
367
|
-
entityId: "entity-hermes",
|
|
368
|
-
})
|
|
369
|
-
.run();
|
|
370
315
|
|
|
371
316
|
insertMemoryItem({
|
|
372
317
|
id: "item-apollo-stale",
|
|
@@ -387,16 +332,10 @@ describe("Context + Memory E2E regression", () => {
|
|
|
387
332
|
createdAt: now - 390 * 24 * 60 * 60 * 1000,
|
|
388
333
|
})
|
|
389
334
|
.run();
|
|
390
|
-
db.insert(memoryItemEntities)
|
|
391
|
-
.values({
|
|
392
|
-
memoryItemId: "item-apollo-stale",
|
|
393
|
-
entityId: "entity-apollo",
|
|
394
|
-
})
|
|
395
|
-
.run();
|
|
396
335
|
|
|
397
336
|
insertMemoryItem({
|
|
398
337
|
id: "item-apollo-secret",
|
|
399
|
-
kind: "
|
|
338
|
+
kind: "identity",
|
|
400
339
|
subject: "apollo secret",
|
|
401
340
|
statement: "Current-turn secret: Apollo code is 123XYZ.",
|
|
402
341
|
confidence: 0.99,
|
|
@@ -412,12 +351,6 @@ describe("Context + Memory E2E regression", () => {
|
|
|
412
351
|
createdAt: now + 100_000,
|
|
413
352
|
})
|
|
414
353
|
.run();
|
|
415
|
-
db.insert(memoryItemEntities)
|
|
416
|
-
.values({
|
|
417
|
-
memoryItemId: "item-apollo-secret",
|
|
418
|
-
entityId: "entity-apollo",
|
|
419
|
-
})
|
|
420
|
-
.run();
|
|
421
354
|
|
|
422
355
|
const summaryCounter = { calls: 0 };
|
|
423
356
|
const provider = makeSummaryProvider(summaryCounter);
|
|
@@ -454,13 +387,7 @@ describe("Context + Memory E2E regression", () => {
|
|
|
454
387
|
},
|
|
455
388
|
retrieval: {
|
|
456
389
|
...DEFAULT_CONFIG.memory.retrieval,
|
|
457
|
-
lexicalTopK: 50,
|
|
458
|
-
semanticTopK: 16,
|
|
459
390
|
maxInjectTokens: 900,
|
|
460
|
-
reranking: {
|
|
461
|
-
...DEFAULT_CONFIG.memory.retrieval.reranking,
|
|
462
|
-
enabled: false,
|
|
463
|
-
},
|
|
464
391
|
dynamicBudget: {
|
|
465
392
|
enabled: true,
|
|
466
393
|
minInjectTokens: 180,
|
|
@@ -468,17 +395,6 @@ describe("Context + Memory E2E regression", () => {
|
|
|
468
395
|
targetHeadroomTokens: 700,
|
|
469
396
|
},
|
|
470
397
|
},
|
|
471
|
-
entity: {
|
|
472
|
-
...DEFAULT_CONFIG.memory.entity,
|
|
473
|
-
relationRetrieval: {
|
|
474
|
-
...DEFAULT_CONFIG.memory.entity.relationRetrieval,
|
|
475
|
-
enabled: true,
|
|
476
|
-
maxSeedEntities: 4,
|
|
477
|
-
maxNeighborEntities: 6,
|
|
478
|
-
maxEdges: 8,
|
|
479
|
-
neighborScoreMultiplier: 0.65,
|
|
480
|
-
},
|
|
481
|
-
},
|
|
482
398
|
},
|
|
483
399
|
};
|
|
484
400
|
|
|
@@ -511,19 +427,14 @@ describe("Context + Memory E2E regression", () => {
|
|
|
511
427
|
);
|
|
512
428
|
|
|
513
429
|
expect(recall.injectedTokens).toBeLessThanOrEqual(recallBudget);
|
|
514
|
-
expect(recall.relationSeedEntityCount).toBeGreaterThan(0);
|
|
515
|
-
expect(recall.relationTraversedEdgeCount).toBeGreaterThan(0);
|
|
516
|
-
expect(recall.relationNeighborEntityCount).toBeGreaterThan(0);
|
|
517
|
-
expect(recall.relationExpandedItemCount).toBeGreaterThan(0);
|
|
518
430
|
|
|
519
|
-
|
|
520
|
-
|
|
431
|
+
// With Qdrant mocked empty the only retrieval path is recency search,
|
|
432
|
+
// but recency-only candidates score below the tier-2 threshold (0.6)
|
|
433
|
+
// since finalScore = semantic*0.7 + recency*0.2 + confidence*0.1 and
|
|
434
|
+
// semantic=0 for recency hits. This means no candidates pass tier
|
|
435
|
+
// classification and injectedText is empty — which is correct behavior:
|
|
436
|
+
// the pipeline requires at least tier-2 quality to inject memory context.
|
|
437
|
+
// Verify current-turn secrets never leak regardless.
|
|
521
438
|
expect(recall.injectedText).not.toContain("123XYZ");
|
|
522
|
-
|
|
523
|
-
const directIndex = recall.injectedText.indexOf("staged canary releases");
|
|
524
|
-
const relationIndex = recall.injectedText.indexOf("start at 5% traffic");
|
|
525
|
-
expect(directIndex).toBeGreaterThanOrEqual(0);
|
|
526
|
-
expect(relationIndex).toBeGreaterThanOrEqual(0);
|
|
527
|
-
expect(directIndex).toBeLessThan(relationIndex);
|
|
528
439
|
});
|
|
529
440
|
});
|