@vellumai/assistant 0.10.4 → 0.10.5-dev.202607022329.d89bf2f
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 +94 -91
- package/Dockerfile +5 -16
- package/README.md +7 -10
- package/bun.lock +0 -4
- package/docs/architecture/security.md +20 -20
- package/docs/stt-provider-onboarding.md +4 -5
- package/docs/trusted-contact-access.md +7 -7
- package/eslint-rules/cli-no-daemon-internals.js +4 -0
- package/eslint.config.mjs +7 -1
- package/knip.json +0 -1
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +36 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +233 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +40 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -8
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +33 -0
- package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +216 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +39 -3
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +0 -8
- package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +1 -5
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +3 -14
- package/openapi.yaml +1073 -225
- package/package.json +1 -4
- package/scripts/test.sh +35 -13
- package/src/__tests__/access-request-seed-content-blocks.test.ts +60 -4
- package/src/__tests__/acp-session.test.ts +4 -1
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +0 -6
- package/src/__tests__/add-message-preassigned-id.test.ts +56 -0
- package/src/__tests__/agent-loop-set-system-prompt.test.ts +139 -0
- package/src/__tests__/app-conversation-ids-backfill.test.ts +3 -0
- package/src/__tests__/app-source-watcher.test.ts +11 -0
- package/src/__tests__/approval-interception-trust-gates.test.ts +148 -3
- package/src/__tests__/assistant-attachments.test.ts +62 -4
- package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -1
- package/src/__tests__/attachments-store-heic-normalize.test.ts +230 -0
- package/src/__tests__/attachments-store.test.ts +3 -0
- package/src/__tests__/background-shell-bash.test.ts +34 -0
- package/src/__tests__/background-shell-host-bash.test.ts +12 -0
- package/src/__tests__/background-tool-registry.test.ts +97 -0
- package/src/__tests__/background-tool-routes.test.ts +72 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/btw-routes.test.ts +57 -3
- package/src/__tests__/call-controller.test.ts +474 -84
- package/src/__tests__/call-domain.test.ts +48 -1
- package/src/__tests__/call-pointer-messages.test.ts +57 -19
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +15 -14
- package/src/__tests__/call-setup-flow-invite.test.ts +568 -0
- package/src/__tests__/call-setup-flow-midcall-trust.test.ts +231 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +754 -0
- package/src/__tests__/call-setup-flow-verification.test.ts +925 -0
- package/src/__tests__/call-setup-flow.test.ts +311 -0
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -1
- package/src/__tests__/channel-guardian.test.ts +4 -7
- package/src/__tests__/channel-readiness-service.test.ts +0 -6
- package/src/__tests__/channel-readiness-slack-remote.test.ts +0 -4
- package/src/__tests__/channel-retry-sweep.test.ts +76 -64
- package/src/__tests__/channel-setup-panel-ack.test.ts +184 -0
- package/src/__tests__/clear-all-lexical.test.ts +90 -0
- package/src/__tests__/compactor-media-strip.test.ts +177 -0
- package/src/__tests__/config-loader-backfill.test.ts +5 -5
- package/src/__tests__/config-schema.test.ts +17 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +27 -70
- package/src/__tests__/contacts-relay-reads.test.ts +188 -23
- package/src/__tests__/contacts-tools.test.ts +42 -1
- package/src/__tests__/contacts-write.test.ts +181 -0
- package/src/__tests__/context-search-conversations-source.test.ts +220 -81
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +39 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -1
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -0
- package/src/__tests__/conversation-clear-safety.test.ts +0 -1
- package/src/__tests__/conversation-crud-enabled-plugins.test.ts +60 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -1
- package/src/__tests__/conversation-disk-view.test.ts +1 -0
- package/src/__tests__/conversation-enabledplugins-route.test.ts +184 -0
- package/src/__tests__/conversation-error.test.ts +0 -18
- package/src/__tests__/conversation-fork-crud.test.ts +67 -0
- package/src/__tests__/conversation-history-web-search.test.ts +7 -137
- package/src/__tests__/conversation-initial-prompt.test.ts +110 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +191 -0
- package/src/__tests__/conversation-process-callsite.test.ts +37 -0
- package/src/__tests__/conversation-queue.test.ts +149 -0
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +392 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +84 -0
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +407 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +36 -16
- package/src/__tests__/conversation-skill-tools.test.ts +73 -0
- package/src/__tests__/conversation-wipe.test.ts +20 -5
- package/src/__tests__/credential-prompt-route.test.ts +69 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -3
- package/src/__tests__/db-drop-messages-fts.test.ts +115 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -62
- package/src/__tests__/edit-propagation.test.ts +56 -2
- package/src/__tests__/empty-state-greeting-cache.test.ts +9 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +15 -110
- package/src/__tests__/guardian-expiry-notifier.test.ts +1 -0
- package/src/__tests__/guardian-question-mode.test.ts +57 -0
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
- package/src/__tests__/guardian-wait-controller.test.ts +619 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +33 -2
- package/src/__tests__/heic-fixture.ts +55 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +0 -2
- package/src/__tests__/http-conversation-lineage.test.ts +6 -1
- package/src/__tests__/image-conversion.test.ts +170 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +72 -341
- package/src/__tests__/introduction-card-resolver.test.ts +354 -0
- package/src/__tests__/invite-redeemed-ipc.test.ts +97 -0
- package/src/__tests__/invite-routes-http.test.ts +246 -420
- package/src/__tests__/job-handler-registration-guard.test.ts +104 -0
- package/src/__tests__/lexical-index-dual-write.test.ts +426 -0
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +2 -2
- package/src/__tests__/list-messages-attachments.test.ts +159 -0
- package/src/__tests__/list-messages-background-tool-completion.test.ts +141 -0
- package/src/__tests__/list-messages-queued.test.ts +25 -0
- package/src/__tests__/managed-store.test.ts +129 -1
- package/src/__tests__/media-stream-audio-transcode.test.ts +113 -0
- package/src/__tests__/media-stream-output.test.ts +350 -19
- package/src/__tests__/media-stream-server-integration.test.ts +943 -169
- package/src/__tests__/media-stream-stt-session.test.ts +498 -1
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +2 -2
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +19 -0
- package/src/__tests__/non-member-access-request.test.ts +349 -3
- package/src/__tests__/notification-decision-fallback.test.ts +104 -8
- package/src/__tests__/notification-decision-strategy.test.ts +57 -8
- package/src/__tests__/onboarding-template-contract.test.ts +27 -43
- package/src/__tests__/openai-provider.test.ts +33 -0
- package/src/__tests__/openai-responses-provider.test.ts +40 -0
- package/src/__tests__/persistence-layering-guard.test.ts +29 -20
- package/src/__tests__/persona-resolver.test.ts +56 -2
- package/src/__tests__/personal-memory-auth-bypass.test.ts +62 -0
- package/src/__tests__/phone.test.ts +1 -15
- package/src/__tests__/plugin-bootstrap.test.ts +25 -196
- package/src/__tests__/plugin-disabled-state.test.ts +33 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +227 -0
- package/src/__tests__/plugin-event-hub-facade.test.ts +323 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +156 -42
- package/src/__tests__/plugin-injector-contribution.test.ts +4 -20
- package/src/__tests__/plugin-pipeline.test.ts +63 -1
- package/src/__tests__/plugin-route-contribution.test.ts +62 -115
- package/src/__tests__/plugin-tool-contribution.test.ts +0 -22
- package/src/__tests__/plugin-types.test.ts +0 -1
- package/src/__tests__/pointer-conversation-trust.test.ts +38 -6
- package/src/__tests__/pre-model-call-sanitize.test.ts +1 -1
- package/src/__tests__/prune-old-tool-invocations-job.test.ts +97 -0
- package/src/__tests__/public-ingress-urls.test.ts +0 -48
- package/src/__tests__/qdrant-collection-migration.test.ts +145 -2
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +0 -1
- package/src/__tests__/resolve-trust-class.test.ts +28 -10
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +137 -0
- package/src/__tests__/schedule-retry.test.ts +38 -14
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +3 -3
- package/src/__tests__/schedule-routes.test.ts +14 -6
- package/src/__tests__/schedule-store.test.ts +3 -1
- package/src/__tests__/scheduler-disk-pressure.test.ts +17 -6
- package/src/__tests__/scheduler-recurrence.test.ts +57 -107
- package/src/__tests__/scheduler-reuse-conversation.test.ts +51 -46
- package/src/__tests__/scheduler-wake.test.ts +13 -23
- package/src/__tests__/scheduler-worker-standdown.test.ts +220 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/shell-observability.test.ts +1 -0
- package/src/__tests__/skill-boundary-guard.test.ts +3 -4
- package/src/__tests__/skill-load-plugin-scope.test.ts +238 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +2 -0
- package/src/__tests__/skill-script-runner-host.test.ts +53 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +5 -2
- package/src/__tests__/skill-tool-factory.test.ts +73 -1
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -14
- package/src/__tests__/slack-inbound-verification.test.ts +36 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +89 -2
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +51 -1
- package/src/__tests__/stt-hints.test.ts +2 -256
- package/src/__tests__/subagent-allowlist-validation.test.ts +47 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +72 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +14 -2
- package/src/__tests__/subagent-persistence.test.ts +122 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +99 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -1
- package/src/__tests__/system-prompt.test.ts +17 -118
- package/src/__tests__/task-scheduler.test.ts +68 -89
- package/src/__tests__/telephony-credential-preflight.test.ts +312 -0
- package/src/__tests__/telephony-tts-guard.test.ts +538 -0
- package/src/__tests__/title-generate-hook.test.ts +16 -0
- package/src/__tests__/tool-approval-handler.test.ts +87 -1
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +45 -20
- package/src/__tests__/tool-grant-request-escalation.test.ts +39 -0
- package/src/__tests__/trust-context-guards.test.ts +21 -10
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -0
- package/src/__tests__/twilio-provider.test.ts +17 -17
- package/src/__tests__/twilio-routes-twiml.test.ts +6 -543
- package/src/__tests__/twilio-routes.test.ts +257 -387
- package/src/__tests__/twilio-validation.test.ts +1 -1
- package/src/__tests__/unified-turn-context-client-os.test.ts +45 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +5 -0
- package/src/__tests__/voice-approval-resolver-relay.test.ts +150 -0
- package/src/__tests__/voice-ingress-preflight.test.ts +61 -0
- package/src/__tests__/voice-quality.test.ts +2 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -7
- package/src/__tests__/voice-session-bridge-processing-wait.test.ts +27 -0
- package/src/__tests__/voice-session-bridge.test.ts +1 -10
- package/src/__tests__/workspace-git-service.test.ts +1 -1
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -0
- package/src/__tests__/workspace-migration-118-seed-now-md.test.ts +120 -0
- package/src/__tests__/workspace-migration-120-revise-onboarding-threads.test.ts +175 -0
- package/src/__tests__/workspace-migration-121-seed-default-user-guardrails.test.ts +118 -0
- package/src/__tests__/workspace-migration-122-relocate-default-user-boundary.test.ts +119 -0
- package/src/__tests__/workspace-migration-123-swap-quality-profile-to-fable.test.ts +191 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +235 -241
- package/src/acp/__tests__/prepare-agent-env.test.ts +0 -97
- package/src/acp/__tests__/session-manager-persistence.test.ts +2 -0
- package/src/acp/__tests__/session-manager-resume.test.ts +8 -0
- package/src/acp/__tests__/session-manager-usage.test.ts +6 -0
- package/src/acp/__tests__/session-manager.test.ts +6 -0
- package/src/acp/agent-process.test.ts +116 -0
- package/src/acp/agent-process.ts +71 -0
- package/src/acp/auto-install.ts +12 -8
- package/src/acp/failure-error.test.ts +109 -0
- package/src/acp/failure-error.ts +111 -0
- package/src/acp/prepare-agent-env.ts +0 -12
- package/src/acp/resolve-agent.test.ts +6 -45
- package/src/acp/resolve-agent.ts +2 -4
- package/src/acp/session-manager.test.ts +312 -0
- package/src/acp/session-manager.ts +102 -46
- package/src/agent/ax-tree-compaction.test.ts +4 -1
- package/src/agent/image-optimize.ts +17 -127
- package/src/agent/loop.ts +45 -192
- package/src/api/events/background-tool-completed.test.ts +51 -0
- package/src/api/events/background-tool-completed.ts +29 -0
- package/src/api/events/background-tool-started.test.ts +36 -0
- package/src/api/events/background-tool-started.ts +28 -0
- package/src/api/events/conversation-error.ts +0 -1
- package/src/api/events/open-panel.ts +33 -0
- package/src/api/events/subagent-status-changed.ts +4 -1
- package/src/api/index.ts +19 -0
- package/src/api/responses/conversation-message.ts +34 -0
- package/src/approvals/guardian-decision-primitive.ts +74 -24
- package/src/approvals/guardian-request-resolvers.ts +450 -122
- package/src/apps/app-store.ts +1 -0
- package/src/apps/shared-app-links-store.ts +1 -0
- package/src/avatar/identity-avatar.ts +3 -7
- package/src/background-wake/wake-intent-hooks.test.ts +1 -1
- package/src/calls/__tests__/fish-audio-client.test.ts +73 -0
- package/src/calls/__tests__/gateway-invite-reader.test.ts +170 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +126 -102
- package/src/calls/call-controller.ts +134 -48
- package/src/calls/call-domain.ts +18 -42
- package/src/calls/call-pointer-messages.ts +51 -67
- package/src/calls/call-setup-flow-types.ts +76 -0
- package/src/calls/call-setup-flow.ts +1743 -0
- package/src/calls/call-speech-output.ts +87 -23
- package/src/calls/call-store.ts +4 -0
- package/src/calls/call-transport.ts +22 -36
- package/src/calls/fish-audio-client.ts +3 -0
- package/src/calls/gateway-invite-reader.ts +98 -0
- package/src/calls/guardian-wait-controller.ts +451 -0
- package/src/calls/media-stream-audio-transcode.ts +82 -2
- package/src/calls/media-stream-output.ts +142 -36
- package/src/calls/media-stream-server.ts +297 -178
- package/src/calls/media-stream-stt-session.ts +393 -50
- package/src/calls/media-turn-detector.ts +4 -6
- package/src/calls/relay-access-wait.ts +3 -4
- package/src/calls/relay-setup-router.ts +22 -43
- package/src/calls/relay-verification.ts +17 -24
- package/src/calls/resolve-call-tts-provider.ts +96 -29
- package/src/calls/stt-hints.ts +24 -87
- package/src/calls/telephony-credential-preflight.ts +159 -0
- package/src/calls/telephony-tts-capability.ts +183 -0
- package/src/calls/tts-call-strategy.ts +18 -20
- package/src/calls/twilio-provider.ts +3 -2
- package/src/calls/twilio-routes.ts +47 -351
- package/src/calls/types.ts +5 -3
- package/src/calls/voice-ingress-preflight.ts +9 -0
- package/src/calls/voice-quality.ts +13 -18
- package/src/calls/voice-session-bridge.ts +25 -35
- package/src/channels/__tests__/types.test.ts +28 -0
- package/src/channels/channel-verification-sessions.ts +7 -2
- package/src/channels/config.ts +0 -42
- package/src/channels/slack-thread-store.ts +12 -0
- package/src/channels/types.ts +23 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -1
- package/src/cli/commands/__tests__/schedules.test.ts +83 -34
- package/src/cli/commands/__tests__/status.test.ts +42 -2
- package/src/cli/commands/contacts.ts +2 -2
- package/src/cli/commands/credentials.ts +17 -0
- package/src/cli/commands/monitoring.ts +191 -0
- package/src/cli/commands/schedules-worker.ts +175 -0
- package/src/cli/commands/schedules.ts +169 -43
- package/src/cli/commands/status.ts +12 -1
- package/src/cli/commands/tools.ts +5 -1
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +1 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +29 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +7 -0
- package/src/cli/lib/search-plugins.ts +6 -0
- package/src/cli/lib/toggle-plugin.ts +21 -2
- package/src/cli/program.ts +3 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +23 -0
- package/src/config/acp-defaults.test.ts +0 -10
- package/src/config/acp-defaults.ts +0 -6
- package/src/config/bundled-skills/acp/SKILL.md +13 -45
- package/src/config/bundled-skills/acp/TOOLS.json +3 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -3
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +9 -9
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +1 -1
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -9
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +10 -7
- package/src/config/bundled-skills/skill-management/TOOLS.json +10 -0
- package/src/config/call-site-defaults.ts +0 -2
- package/src/config/feature-flag-registry.json +16 -8
- package/src/config/loader.ts +11 -0
- package/src/config/schema.ts +9 -1
- package/src/config/schemas/__tests__/memory-v2.test.ts +2 -2
- package/src/config/schemas/__tests__/memory-v3.test.ts +64 -0
- package/src/config/schemas/call-site-catalog.ts +0 -12
- package/src/config/schemas/calls.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +1 -1
- package/src/config/schemas/llm.ts +0 -2
- package/src/config/schemas/memory-v2.ts +1 -1
- package/src/config/schemas/memory-v3.ts +82 -0
- package/src/config/schemas/monitoring.ts +61 -0
- package/src/config/schemas/schedules.ts +35 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills-plugin-filter.test.ts +96 -0
- package/src/config/skills.ts +26 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +156 -4
- package/src/contacts/canonical-guardian-store.ts +5 -0
- package/src/contacts/contact-store.ts +81 -35
- package/src/contacts/contacts-write.ts +50 -25
- package/src/contacts/member-write-relay.ts +115 -31
- package/src/contacts/types.ts +0 -8
- package/src/context/compactor.ts +29 -15
- package/src/context/outbound-sanitize.ts +202 -0
- package/src/daemon/__tests__/abort-null-controller.test.ts +167 -0
- package/src/daemon/__tests__/conversation-tool-setup-plugin-scope.test.ts +122 -0
- package/src/daemon/__tests__/embedding-reconcile.test.ts +99 -16
- package/src/daemon/__tests__/wake-conversation-ops-completion.test.ts +107 -0
- package/src/daemon/abort-watchdog.ts +7 -0
- package/src/daemon/app-source-watcher.ts +9 -0
- package/src/daemon/assistant-attachments.ts +42 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +256 -147
- package/src/daemon/conversation-agent-loop.ts +61 -68
- package/src/daemon/conversation-error.ts +1 -15
- package/src/daemon/conversation-history.ts +32 -236
- package/src/daemon/conversation-initial-prompt.ts +77 -0
- package/src/daemon/conversation-lifecycle.ts +25 -1
- package/src/daemon/conversation-messaging.ts +2 -2
- package/src/daemon/conversation-plugin-scope.ts +38 -0
- package/src/daemon/conversation-process.ts +50 -14
- package/src/daemon/conversation-runtime-assembly.ts +36 -6
- package/src/daemon/conversation-skill-tools.ts +19 -3
- package/src/daemon/conversation-store.ts +3 -3
- package/src/daemon/conversation-surfaces.ts +171 -0
- package/src/daemon/conversation-tool-setup.test.ts +95 -0
- package/src/daemon/conversation-tool-setup.ts +116 -1
- package/src/daemon/conversation-turn-finalize.ts +211 -0
- package/src/daemon/conversation.ts +92 -1
- package/src/daemon/disk-pressure-policy.ts +2 -1
- package/src/daemon/event-loop-watchdog.ts +22 -6
- package/src/daemon/external-plugins-bootstrap.ts +47 -134
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +26 -14
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +27 -14
- package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -5
- package/src/daemon/handlers/config-a2a.ts +7 -14
- package/src/daemon/handlers/config-channels.ts +2 -10
- package/src/daemon/handlers/conversation-history.ts +3 -1
- package/src/daemon/handlers/conversations.ts +0 -52
- package/src/daemon/handlers/shared.ts +4 -24
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +59 -472
- package/src/daemon/message-protocol.ts +3 -3
- package/src/daemon/message-provenance.ts +12 -14
- package/src/daemon/message-types/background-tools.ts +28 -0
- package/src/daemon/message-types/contacts.ts +2 -10
- package/src/daemon/message-types/conversations.ts +10 -0
- package/src/daemon/message-types/integrations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pointer-conversation-trust.ts +23 -9
- package/src/daemon/pointer-turn-runner.ts +174 -0
- package/src/daemon/providers-setup.ts +16 -0
- package/src/daemon/shutdown-handlers.ts +139 -47
- package/src/daemon/skill-memory-refresh.ts +2 -2
- package/src/daemon/sqlite-corruption-watchdog.test.ts +217 -0
- package/src/daemon/sqlite-corruption-watchdog.ts +155 -0
- package/src/daemon/tool-setup-types.ts +8 -0
- package/src/daemon/trust-context.ts +21 -6
- package/src/daemon/turn-latency-tracker.test.ts +93 -0
- package/src/daemon/turn-latency-tracker.ts +38 -0
- package/src/daemon/wake-conversation-ops.ts +10 -8
- package/src/documents/document-comments-store.test.ts +7 -1
- package/src/documents/document-comments-store.ts +8 -0
- package/src/documents/document-store.ts +15 -0
- package/src/export/__tests__/transcript-formatter.test.ts +11 -0
- package/src/export/transcript-formatter.ts +49 -67
- package/src/home/__tests__/feed-writer.test.ts +247 -1
- package/src/home/conversation-starter-checkpoints.ts +1 -0
- package/src/home/feed-writer.ts +61 -0
- package/src/home/job-handlers/conversation-starters.ts +1 -0
- package/src/hooks/hook-loader.ts +12 -0
- package/src/hooks/registry.ts +32 -4
- package/src/inbound/public-ingress-urls.ts +0 -17
- package/src/ipc/__tests__/contacts-info-ipc.test.ts +285 -0
- package/src/ipc/assistant-server.ts +28 -7
- package/src/ipc/routes/__tests__/contacts-mirror-apply-atomicity.test.ts +62 -0
- package/src/ipc/routes/__tests__/contacts-mirror-apply.test.ts +272 -0
- package/src/ipc/routes/__tests__/contacts-mirror-ipc-routes.test.ts +322 -0
- package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +60 -4
- package/src/ipc/routes/contacts-info-ipc-routes.ts +239 -0
- package/src/ipc/routes/contacts-mirror-ipc-routes.ts +202 -0
- package/src/ipc/routes/db-proxy.ts +5 -2
- package/src/ipc/routes/invite-ipc-routes.ts +53 -39
- package/src/jobs/worker.ts +9 -2
- package/src/live-voice/__tests__/live-voice-archive.test.ts +10 -1
- package/src/live-voice/live-voice-archive.ts +5 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +22 -6
- package/src/messaging/providers/channel-transport.ts +6 -0
- package/src/messaging/providers/index.ts +3 -0
- package/src/messaging/providers/slack/api.test.ts +49 -1
- package/src/messaging/providers/slack/api.ts +110 -0
- package/src/messaging/providers/slack/send.test.ts +85 -0
- package/src/messaging/providers/slack/send.ts +100 -28
- package/src/messaging/providers/slack/transport.ts +7 -13
- package/src/messaging/providers/whatsapp/send.ts +29 -9
- package/src/monitoring/__tests__/control.test.ts +235 -0
- package/src/monitoring/__tests__/sample-ring-buffer.test.ts +96 -0
- package/src/monitoring/control.ts +105 -0
- package/src/monitoring/resource-sampler.ts +258 -0
- package/src/monitoring/sample-ring-buffer.ts +108 -0
- package/src/monitoring/worker.ts +94 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/access-request-copy.ts +94 -23
- package/src/notifications/adapters/slack.ts +89 -21
- package/src/notifications/approval-card-builder.ts +33 -18
- package/src/notifications/approval-card-data.ts +48 -15
- package/src/notifications/broadcaster.ts +25 -16
- package/src/notifications/decision-engine.ts +131 -32
- package/src/notifications/guardian-question-mode.ts +58 -10
- package/src/permissions/approval-policy.ts +8 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/gateway-threshold-reader.ts +7 -6
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/secret-prompter.ts +11 -3
- package/src/permissions/types.ts +2 -2
- package/src/persistence/__tests__/conversation-queries-search.test.ts +397 -0
- package/src/persistence/__tests__/conversation-search-lexical.test.ts +103 -0
- package/src/persistence/__tests__/jobs-worker-memory-disabled.test.ts +95 -0
- package/src/persistence/__tests__/slow-query-log.test.ts +266 -0
- package/src/persistence/__tests__/slow-sync-log.test.ts +155 -0
- package/src/persistence/a2a-invite-store.ts +205 -0
- package/src/persistence/attachments-store.ts +94 -10
- package/src/persistence/checkpoints.ts +45 -0
- package/src/persistence/conversation-crud.ts +453 -260
- package/src/persistence/conversation-display-order-migration.ts +8 -2
- package/src/persistence/conversation-group-migration.test.ts +8 -0
- package/src/persistence/conversation-group-migration.ts +9 -0
- package/src/persistence/conversation-queries.ts +244 -125
- package/src/persistence/conversation-row-batch-delete.ts +9 -5
- package/src/persistence/conversation-search-lexical.ts +49 -0
- package/src/persistence/db-async-query.ts +42 -0
- package/src/persistence/db-connection.ts +13 -0
- package/src/persistence/delivery-channels.ts +0 -82
- package/src/persistence/delivery-crud.ts +27 -6
- package/src/persistence/embeddings/__tests__/embedding-identity.test.ts +18 -5
- package/src/persistence/embeddings/__tests__/messages-lexical-index.test.ts +334 -0
- package/src/persistence/embeddings/embedding-identity.ts +17 -5
- package/src/persistence/embeddings/embedding-runtime-manager.ts +31 -0
- package/src/persistence/embeddings/messages-lexical-index.ts +396 -0
- package/src/persistence/embeddings/qdrant-client.ts +81 -5
- package/src/persistence/group-crud.ts +13 -1
- package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +506 -0
- package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +164 -0
- package/src/persistence/job-handlers/__tests__/message-lexical.test.ts +259 -0
- package/src/persistence/job-handlers/cleanup.ts +57 -9
- package/src/persistence/job-handlers/message-lexical-backfill.ts +187 -0
- package/src/persistence/job-handlers/message-lexical.ts +251 -0
- package/src/persistence/jobs-store.ts +102 -8
- package/src/persistence/jobs-worker.ts +68 -28
- package/src/persistence/llm-request-log-store.ts +35 -19
- package/src/persistence/llm-usage-store.ts +11 -0
- package/src/persistence/memory-lifecycle-hooks.test.ts +65 -0
- package/src/persistence/memory-lifecycle-hooks.ts +55 -1
- package/src/persistence/migrations/028-call-session-mode.ts +1 -1
- package/src/persistence/migrations/116-messages-fts.ts +8 -1
- package/src/persistence/migrations/311-create-subagents-table.ts +50 -0
- package/src/persistence/migrations/312-drop-inbox-conversation-state-table.ts +15 -0
- package/src/persistence/migrations/313-drop-messages-fts.ts +17 -0
- package/src/persistence/migrations/314-add-conversation-enabled-plugins.ts +25 -0
- package/src/persistence/migrations/315-create-a2a-invites.ts +58 -0
- package/src/persistence/migrations/316-drop-contact-channels-invite-id.ts +28 -0
- package/src/persistence/migrations/317-canonical-guardian-requester-signals.ts +24 -0
- package/src/persistence/migrations/318-drop-contact-channel-telemetry.ts +32 -0
- package/src/persistence/migrations/__tests__/315-create-a2a-invites.test.ts +161 -0
- package/src/persistence/migrations/__tests__/316-drop-contact-channels-invite-id.test.ts +156 -0
- package/src/persistence/migrations/__tests__/318-drop-contact-channel-telemetry.test.ts +134 -0
- package/src/persistence/migrations/__tests__/migration-prefix-guard.test.ts +99 -0
- package/src/persistence/raw-query.ts +50 -11
- package/src/persistence/schema/a2a.ts +14 -0
- package/src/persistence/schema/contacts.ts +0 -62
- package/src/persistence/schema/conversation-groups.ts +12 -0
- package/src/persistence/schema/conversations.ts +2 -0
- package/src/persistence/schema/documents.ts +65 -0
- package/src/persistence/schema/guardian.ts +4 -0
- package/src/persistence/schema/index.ts +4 -0
- package/src/persistence/schema/memory-injection.ts +62 -0
- package/src/persistence/schema/workflows.ts +55 -0
- package/src/persistence/slow-query-log.ts +437 -0
- package/src/persistence/slow-sync-log.ts +204 -0
- package/src/persistence/steps.ts +16 -0
- package/src/persistence/subagent-store.ts +125 -0
- package/src/persistence/worker-control.ts +45 -238
- package/src/plugin-api/event-hub-facade.ts +172 -0
- package/src/plugin-api/index.ts +9 -1
- package/src/plugin-api/types.ts +40 -9
- package/src/plugins/defaults/default-plugin-names.ts +60 -0
- package/src/plugins/defaults/index.ts +19 -29
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +46 -67
- package/src/plugins/defaults/memory/__tests__/db-async-query.test.ts +36 -1
- package/src/plugins/defaults/memory/__tests__/find-most-recent-retrospective-for.test.ts +2 -4
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +9 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +7 -4
- package/src/plugins/defaults/memory/config.ts +11 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +1 -1
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +124 -113
- package/src/plugins/defaults/memory/embeddings.ts +77 -0
- package/src/plugins/defaults/memory/find-most-recent-retrospective-for.ts +66 -0
- package/src/plugins/defaults/memory/frontmatter.ts +50 -0
- package/src/plugins/defaults/memory/graph/bootstrap.test.ts +36 -10
- package/src/plugins/defaults/memory/graph/bootstrap.ts +6 -5
- package/src/plugins/defaults/memory/graph/consolidation.ts +3 -5
- package/src/plugins/defaults/memory/graph/extraction.ts +3 -6
- package/src/plugins/defaults/memory/graph/graph-search.ts +12 -23
- package/src/plugins/defaults/memory/graph/narrative.ts +3 -5
- package/src/plugins/defaults/memory/graph/pattern-scan.ts +3 -5
- package/src/plugins/defaults/memory/graph/retriever.test.ts +1 -6
- package/src/plugins/defaults/memory/graph/retriever.ts +5 -10
- package/src/plugins/defaults/memory/graph/tools.ts +1 -1
- package/src/plugins/defaults/memory/hooks/init.ts +31 -5
- package/src/plugins/defaults/memory/indexer.ts +2 -3
- package/src/plugins/defaults/memory/injectors.ts +2 -2
- package/src/plugins/defaults/memory/job-handler-registration.ts +96 -0
- package/src/plugins/defaults/memory/job-handlers/backfill.ts +4 -26
- package/src/plugins/defaults/memory/job-handlers/embedding.test.ts +6 -19
- package/src/plugins/defaults/memory/job-handlers/embedding.ts +9 -22
- package/src/plugins/defaults/memory/job-handlers/index-maintenance.ts +2 -3
- package/src/plugins/defaults/memory/job-handlers.ts +13 -12
- package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +1 -1
- package/src/plugins/defaults/memory/llm-helpers.ts +76 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +8 -5
- package/src/plugins/defaults/memory/memory-retrospective-startup-cleanup.ts +2 -2
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +2 -0
- package/src/plugins/defaults/memory/persistence-hooks.ts +46 -1
- package/src/plugins/defaults/memory/pkb/pkb-index.ts +1 -5
- package/src/plugins/defaults/memory/pkb/pkb-search.ts +2 -2
- package/src/plugins/defaults/memory/routes/__tests__/memory-v2-simulate-route.test.ts +4 -6
- package/src/plugins/defaults/memory/routes/memory-item-routes.ts +1 -1
- package/src/plugins/defaults/memory/search/semantic.ts +2 -2
- package/src/plugins/defaults/memory/startup.ts +244 -0
- package/src/plugins/defaults/memory/task-memory-cleanup.ts +8 -0
- package/src/{daemon → plugins/defaults/memory/v2}/__tests__/memory-v2-startup.test.ts +6 -6
- package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +2 -13
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +4 -5
- package/src/plugins/defaults/memory/v2/__tests__/sweep-job.test.ts +1 -8
- package/src/plugins/defaults/memory/v2/activation.ts +2 -4
- package/src/plugins/defaults/memory/v2/cli-command-store.ts +2 -4
- package/src/plugins/defaults/memory/v2/frontmatter-sweep.ts +1 -1
- package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.test.ts +8 -8
- package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.ts +20 -22
- package/src/plugins/defaults/memory/v2/migration.ts +2 -5
- package/src/plugins/defaults/memory/v2/page-store.ts +1 -1
- package/src/plugins/defaults/memory/v2/qdrant.ts +2 -3
- package/src/plugins/defaults/memory/v2/router.ts +5 -5
- package/src/plugins/defaults/memory/v2/sim.ts +2 -4
- package/src/plugins/defaults/memory/v2/skill-store.ts +2 -4
- package/src/plugins/defaults/memory/v2/sweep-job.ts +6 -6
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +11 -9
- package/src/plugins/defaults/memory/v3/__tests__/dense.test.ts +107 -2
- package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +69 -0
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +181 -0
- package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +7 -3
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +345 -8
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -4
- package/src/plugins/defaults/memory/v3/__tests__/section-needle.test.ts +64 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +18 -13
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +62 -1
- package/src/plugins/defaults/memory/v3/card.ts +1 -4
- package/src/plugins/defaults/memory/v3/dense.ts +60 -25
- package/src/plugins/defaults/memory/v3/edge.ts +1 -1
- package/src/plugins/defaults/memory/v3/gate-flag.ts +20 -0
- package/src/plugins/defaults/memory/v3/gate.ts +158 -0
- package/src/plugins/defaults/memory/v3/maintain-job.ts +2 -4
- package/src/plugins/defaults/memory/v3/orchestrate.ts +186 -25
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +7 -9
- package/src/plugins/defaults/memory/v3/pool-select.ts +6 -6
- package/src/plugins/defaults/memory/v3/prune.ts +2 -2
- package/src/plugins/defaults/memory/v3/section-dense-store.ts +8 -11
- package/src/plugins/defaults/memory/v3/section-needle.ts +27 -7
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +10 -0
- package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +1 -4
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +5 -0
- package/src/plugins/defaults/turn-context/injectors.ts +1 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +12 -0
- package/src/plugins/injector-registry.ts +13 -2
- package/src/plugins/mtime-cache.ts +36 -47
- package/src/plugins/pipeline.ts +15 -1
- package/src/plugins/types.ts +14 -23
- package/src/prompts/__tests__/system-prompt.test.ts +170 -32
- package/src/prompts/persona-resolver.ts +13 -9
- package/src/prompts/system-prompt.ts +30 -9
- package/src/prompts/templates/BOOTSTRAP.md +16 -119
- package/src/prompts/templates/SOUL.md +43 -13
- package/src/prompts/templates/system-sections.ts +32 -27
- package/src/prompts/templates/users/default.md +19 -0
- package/src/providers/__tests__/placeholder-sentinels.test.ts +56 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +96 -1
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +14 -1
- package/src/providers/openai/responses-provider.ts +18 -0
- package/src/providers/placeholder-sentinels.ts +18 -8
- package/src/providers/retry.ts +1 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +0 -64
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +92 -79
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +98 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +62 -3
- package/src/providers/speech-to-text/provider-catalog.ts +0 -89
- package/src/providers/speech-to-text/resolve.ts +55 -0
- package/src/providers/tool-progress-events.test.ts +50 -0
- package/src/providers/tool-progress-events.ts +99 -0
- package/src/providers/types.ts +1 -1
- package/src/providers/vellum-model-routing.test.ts +51 -0
- package/src/providers/vellum-model-routing.ts +84 -0
- package/src/runtime/AGENTS.md +5 -4
- package/src/runtime/__tests__/agent-wake.test.ts +158 -1
- package/src/runtime/__tests__/channel-verification-service.test.ts +2 -2
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +37 -8
- package/src/runtime/access-request-helper.ts +154 -3
- package/src/runtime/actor-trust-resolver.ts +7 -21
- package/src/runtime/agent-wake.ts +41 -1
- package/src/runtime/assistant-event.ts +86 -12
- package/src/runtime/auth/__tests__/route-policy.test.ts +31 -0
- package/src/runtime/channel-approval-types.ts +52 -2
- package/src/runtime/channel-approvals.ts +23 -8
- package/src/runtime/channel-invite-transport.ts +2 -2
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-readiness-service.ts +3 -5
- package/src/runtime/channel-retry-sweep.ts +18 -24
- package/src/runtime/channel-verification-service.ts +9 -11
- package/src/runtime/finalize-event-delivery.ts +30 -16
- package/src/runtime/guardian-action-service.ts +10 -11
- package/src/runtime/guardian-reply-router.ts +122 -21
- package/src/runtime/http-server.ts +44 -101
- package/src/runtime/introduction-policy.test.ts +214 -0
- package/src/runtime/introduction-policy.ts +284 -0
- package/src/runtime/invite-service.ts +104 -361
- package/src/runtime/local-actor-identity.test.ts +38 -0
- package/src/runtime/local-actor-identity.ts +14 -0
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +22 -0
- package/src/runtime/middleware/rate-limiter.ts +30 -0
- package/src/runtime/middleware/twilio-validation.ts +5 -7
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +28 -0
- package/src/runtime/migrations/origin-mode.ts +6 -6
- package/src/runtime/pre-first-message-gate.ts +1 -0
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +6 -35
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +24 -3
- package/src/runtime/routes/__tests__/contact-routes.test.ts +80 -15
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +7 -1
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -3
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +142 -1
- package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +119 -30
- package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +70 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +194 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +755 -29
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +4 -0
- package/src/runtime/routes/__tests__/schedule-routes-create.test.ts +70 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +161 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +1 -1
- package/src/runtime/routes/acp-routes.ts +15 -3
- package/src/runtime/routes/app-management-routes.ts +4 -7
- package/src/runtime/routes/approval-routes.ts +4 -0
- package/src/runtime/routes/attachment-routes.ts +54 -3
- package/src/runtime/routes/avatar-routes.ts +0 -4
- package/src/runtime/routes/background-tool-routes.ts +34 -5
- package/src/runtime/routes/btw-routes.ts +32 -4
- package/src/runtime/routes/channel-route-shared.ts +18 -14
- package/src/runtime/routes/channel-verification-routes.ts +0 -3
- package/src/runtime/routes/contact-routes.ts +298 -117
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +74 -37
- package/src/runtime/routes/conversation-query-routes.ts +5 -2
- package/src/runtime/routes/conversation-routes.ts +222 -82
- package/src/runtime/routes/conversations-import-routes.ts +8 -1
- package/src/runtime/routes/credential-prompt-routes.ts +20 -5
- package/src/runtime/routes/debug-routes.ts +1 -0
- package/src/runtime/routes/documents-routes.ts +5 -2
- package/src/runtime/routes/empty-state-greeting-cache.ts +37 -10
- package/src/runtime/routes/global-search-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +51 -0
- package/src/runtime/routes/home-feed-routes.ts +60 -1
- package/src/runtime/routes/identity-routes.ts +4 -87
- package/src/runtime/routes/inbound-message-handler.ts +82 -41
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +430 -57
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +302 -572
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +76 -270
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +21 -116
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +9 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.test.ts +158 -0
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +22 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +22 -16
- package/src/runtime/routes/internal-twilio-routes.ts +0 -15
- package/src/runtime/routes/messages-lexical-routes.ts +75 -0
- package/src/runtime/routes/migration-routes.ts +69 -6
- package/src/runtime/routes/monitoring-routes.ts +237 -0
- package/src/runtime/routes/plugins-routes.ts +329 -8
- package/src/runtime/routes/schedule-routes.ts +49 -8
- package/src/runtime/routes/schedule-worker-routes.ts +210 -0
- package/src/runtime/routes/secret-routes.ts +1 -1
- package/src/runtime/routes/surface-conversation-resolver.ts +1 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +54 -9
- package/src/runtime/services/__tests__/auto-analysis-enqueue.test.ts +30 -6
- package/src/runtime/services/__tests__/conversation-analyze-job.test.ts +43 -8
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +20 -0
- package/src/runtime/services/analyze-conversation.ts +14 -4
- package/src/runtime/services/conversation-serializer.ts +5 -0
- package/src/runtime/slack-no-response.ts +47 -0
- package/src/runtime/slack-reply-session.test.ts +633 -0
- package/src/runtime/slack-reply-session.ts +382 -0
- package/src/runtime/slack-task-progress.ts +95 -0
- package/src/runtime/sync/resource-sync-events.ts +25 -0
- package/src/runtime/tool-grant-request-helper.ts +17 -1
- package/src/runtime/trust-class.test.ts +141 -0
- package/src/runtime/trust-class.ts +121 -0
- package/src/runtime/trust-verdict-consumer.ts +7 -6
- package/src/schedule/__tests__/run-script.test.ts +21 -0
- package/src/schedule/__tests__/worker-control.test.ts +241 -0
- package/src/schedule/run-script.ts +13 -6
- package/src/schedule/schedule-usage-store.ts +1 -0
- package/src/schedule/scheduler.ts +178 -127
- package/src/schedule/worker-control.ts +118 -0
- package/src/schedule/worker.ts +131 -0
- package/src/skills/__tests__/categories-cache.test.ts +38 -0
- package/src/skills/categories-cache.ts +40 -5
- package/src/skills/managed-store.ts +41 -6
- package/src/skills/validate-input.ts +47 -0
- package/src/stt/types.ts +5 -9
- package/src/subagent/manager.ts +267 -39
- package/src/subagent/types.ts +6 -2
- package/src/subagent/validate-allowlists.ts +65 -0
- package/src/telemetry/watchdog-direct-emit.test.ts +109 -0
- package/src/telemetry/watchdog-direct-emit.ts +89 -0
- package/src/tools/acp/list-agents.test.ts +0 -6
- package/src/tools/background-tool-registry.ts +61 -0
- package/src/tools/document/document-comment-tool.test.ts +4 -0
- package/src/tools/host-terminal/host-shell.test.ts +397 -0
- package/src/tools/host-terminal/host-shell.ts +203 -14
- package/src/tools/skills/find-similar-skills.test.ts +152 -1
- package/src/tools/skills/find-similar-skills.ts +30 -4
- package/src/tools/skills/load.ts +36 -0
- package/src/tools/skills/scaffold-managed.ts +66 -34
- package/src/tools/skills/skill-script-runner.ts +17 -0
- package/src/tools/skills/skill-tool-factory.ts +5 -1
- package/src/tools/terminal/safe-env.ts +3 -0
- package/src/tools/terminal/shell.test.ts +177 -0
- package/src/tools/terminal/shell.ts +98 -4
- package/src/tools/tool-approval-handler.ts +106 -29
- package/{node_modules/@vellumai/skill-host-contracts/src → src/tools}/tool-types.ts +16 -17
- package/src/tools/types.ts +29 -17
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tts/__tests__/provider-adapters.test.ts +37 -0
- package/src/tts/__tests__/provider-catalog.test.ts +21 -0
- package/src/tts/provider-catalog.ts +40 -9
- package/src/tts/providers/fish-audio-provider.ts +19 -10
- package/src/tts/types.ts +3 -4
- package/src/util/__tests__/process-tree.test.ts +10 -2
- package/src/util/__tests__/text-spacing.test.ts +53 -0
- package/src/util/cgroup-memory.ts +142 -0
- package/src/util/image-conversion.ts +281 -0
- package/src/util/phone.ts +1 -8
- package/src/util/platform.ts +33 -0
- package/src/util/process-tree.ts +10 -2
- package/src/util/text-spacing.ts +53 -0
- package/src/util/worker-process.ts +271 -0
- package/src/workflows/journal-store.ts +16 -1
- package/src/workflows/library.test.ts +35 -20
- package/src/workflows/library.ts +105 -66
- package/src/workspace/git-service.ts +1 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +83 -31
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +41 -22
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +1 -1
- package/src/workspace/migrations/118-seed-now-md.ts +64 -0
- package/src/workspace/migrations/119-strip-persisted-memory-v3-tuning-defaults.ts +169 -0
- package/src/workspace/migrations/120-revise-onboarding-threads.ts +47 -0
- package/src/workspace/migrations/121-seed-default-user-guardrails.ts +82 -0
- package/src/workspace/migrations/122-relocate-default-user-boundary.ts +111 -0
- package/src/workspace/migrations/123-swap-quality-profile-to-fable.ts +85 -0
- package/src/workspace/migrations/__tests__/119-strip-persisted-memory-v3-tuning-defaults.test.ts +181 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/drizzle/0000_dizzy_maggott.sql +0 -301
- package/drizzle/meta/0000_snapshot.json +0 -1933
- package/drizzle/meta/_journal.json +0 -13
- package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +0 -887
- package/node_modules/@vellumai/skill-host-contracts/bun.lock +0 -24
- package/node_modules/@vellumai/skill-host-contracts/package.json +0 -18
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -97
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +0 -1349
- package/node_modules/@vellumai/skill-host-contracts/src/index.ts +0 -6
- package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +0 -11
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +0 -32
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +0 -333
- package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +0 -20
- package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +0 -12
- package/src/__tests__/invite-redemption-service.test.ts +0 -1180
- package/src/__tests__/invite-service-ipc.test.ts +0 -350
- package/src/__tests__/job-handler-registry-guard.test.ts +0 -125
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -203
- package/src/__tests__/relay-server.test.ts +0 -6153
- package/src/__tests__/telephony-stt-routing.test.ts +0 -329
- package/src/__tests__/voice-invite-redemption.test.ts +0 -702
- package/src/calls/relay-server.ts +0 -2463
- package/src/calls/telephony-stt-routing.ts +0 -145
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -128
- package/src/daemon/__tests__/daemon-skill-host.test.ts +0 -273
- package/src/daemon/daemon-skill-host.ts +0 -262
- package/src/daemon/message-types/meet.ts +0 -143
- package/src/daemon/shutdown-registry.ts +0 -40
- package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -253
- package/src/ipc/__tests__/skill-server.test.ts +0 -182
- package/src/ipc/skill-ipc-types.ts +0 -54
- package/src/ipc/skill-routes/__tests__/config.test.ts +0 -146
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +0 -448
- package/src/ipc/skill-routes/__tests__/identity.test.ts +0 -62
- package/src/ipc/skill-routes/__tests__/log.test.ts +0 -133
- package/src/ipc/skill-routes/__tests__/memory.test.ts +0 -203
- package/src/ipc/skill-routes/__tests__/platform.test.ts +0 -111
- package/src/ipc/skill-routes/__tests__/providers.test.ts +0 -265
- package/src/ipc/skill-routes/__tests__/registries.test.ts +0 -400
- package/src/ipc/skill-routes/config.ts +0 -47
- package/src/ipc/skill-routes/events.ts +0 -147
- package/src/ipc/skill-routes/identity.ts +0 -21
- package/src/ipc/skill-routes/index.ts +0 -37
- package/src/ipc/skill-routes/log.ts +0 -40
- package/src/ipc/skill-routes/memory.ts +0 -92
- package/src/ipc/skill-routes/platform.ts +0 -39
- package/src/ipc/skill-routes/providers.ts +0 -162
- package/src/ipc/skill-routes/registries.ts +0 -338
- package/src/ipc/skill-server.ts +0 -831
- package/src/ipc/skill-socket-path.ts +0 -20
- package/src/jobs/register-job-handlers.ts +0 -74
- package/src/persistence/invite-store.ts +0 -481
- package/src/plugins/job-handler-registry.ts +0 -94
- package/src/providers/cache-control.ts +0 -26
- package/src/runtime/__tests__/runtime-mode.test.ts +0 -62
- package/src/runtime/invite-redemption-service.ts +0 -828
- package/src/runtime/invite-redemption-templates.ts +0 -46
- package/src/runtime/runtime-mode.ts +0 -40
- package/src/runtime/slack-dm-text-delivery.ts +0 -177
- package/src/util/voice-code.ts +0 -31
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from "./conversation-crud.js";
|
|
27
27
|
import { type DrizzleDb, getDb, getLogsDb } from "./db-connection.js";
|
|
28
28
|
import { llmRequestLogs, messages } from "./schema/index.js";
|
|
29
|
+
import { timeSyncSection } from "./slow-sync-log.js";
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* The logs connection (`assistant-logs.db`), where `llm_request_logs` lives.
|
|
@@ -167,27 +168,42 @@ export function recordRequestLog(
|
|
|
167
168
|
): string {
|
|
168
169
|
const db = logsDb();
|
|
169
170
|
const id = uuid();
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
// Synchronous insert of the full request/response payloads (an entire
|
|
172
|
+
// context window for main-agent calls) into the append-only logs DB, on the
|
|
173
|
+
// per-LLM-call critical path. Timed so an event-loop freeze the watchdog
|
|
174
|
+
// detects can be attributed to this write (see slow-sync-log).
|
|
175
|
+
timeSyncSection(
|
|
176
|
+
"llm-request-log:write",
|
|
177
|
+
() =>
|
|
178
|
+
db
|
|
179
|
+
.insert(llmRequestLogs)
|
|
180
|
+
.values({
|
|
181
|
+
id,
|
|
182
|
+
conversationId,
|
|
183
|
+
messageId: messageId ?? null,
|
|
184
|
+
provider: provider ?? null,
|
|
185
|
+
requestPayload,
|
|
186
|
+
responsePayload,
|
|
187
|
+
createdAt: Date.now(),
|
|
188
|
+
// Stamped later via setAgentLoopExitReasonOnLatestLog, once the
|
|
189
|
+
// agent loop body actually exits. Intermediate rows stay NULL.
|
|
190
|
+
agentLoopExitReason: null,
|
|
191
|
+
// Logical call site (`mainAgent`, `compactionAgent`, …). NULL when
|
|
192
|
+
// a caller hasn't been updated yet — preserves backward compat
|
|
193
|
+
// while we plumb call sites through one site at a time.
|
|
194
|
+
callSite: callSite ?? null,
|
|
195
|
+
// JSON first-token latency waterfall, supplied by `handleUsage` for
|
|
196
|
+
// main-agent calls. NULL for failed/non-instrumented call paths.
|
|
197
|
+
latencyBreakdown: latencyBreakdown ?? null,
|
|
198
|
+
})
|
|
199
|
+
.run(),
|
|
200
|
+
() => ({
|
|
173
201
|
conversationId,
|
|
174
|
-
messageId: messageId ?? null,
|
|
175
|
-
provider: provider ?? null,
|
|
176
|
-
requestPayload,
|
|
177
|
-
responsePayload,
|
|
178
|
-
createdAt: Date.now(),
|
|
179
|
-
// Stamped later via setAgentLoopExitReasonOnLatestLog, once the
|
|
180
|
-
// agent loop body actually exits. Intermediate rows stay NULL.
|
|
181
|
-
agentLoopExitReason: null,
|
|
182
|
-
// Logical call site (`mainAgent`, `compactionAgent`, …). NULL when
|
|
183
|
-
// a caller hasn't been updated yet — preserves backward compat
|
|
184
|
-
// while we plumb call sites through one site at a time.
|
|
185
202
|
callSite: callSite ?? null,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
.run();
|
|
203
|
+
requestBytes: requestPayload.length,
|
|
204
|
+
responseBytes: responsePayload.length,
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
191
207
|
return id;
|
|
192
208
|
}
|
|
193
209
|
|
|
@@ -451,6 +451,7 @@ export function getConversationUsageTotals(conversationId: string): {
|
|
|
451
451
|
total_output: number;
|
|
452
452
|
total_cost: number | null;
|
|
453
453
|
}>(
|
|
454
|
+
"usage:getConversationTotals",
|
|
454
455
|
/*sql*/ `
|
|
455
456
|
SELECT
|
|
456
457
|
COALESCE(SUM(input_tokens + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0)), 0) AS total_input,
|
|
@@ -479,6 +480,7 @@ export function getUsageCostForConversationWindow({
|
|
|
479
480
|
to: number;
|
|
480
481
|
}): number {
|
|
481
482
|
const rows = rawAll<{ total_cost: number | null }>(
|
|
483
|
+
"usage:costForConversationWindow",
|
|
482
484
|
/*sql*/ `
|
|
483
485
|
SELECT COALESCE(SUM(estimated_cost_usd), 0) AS total_cost
|
|
484
486
|
FROM llm_usage_events
|
|
@@ -520,6 +522,7 @@ export function getUsageCostForRun({
|
|
|
520
522
|
params.push(conversationId, from, to);
|
|
521
523
|
}
|
|
522
524
|
const rows = rawAll<{ total_cost: number | null }>(
|
|
525
|
+
"usage:costForRun",
|
|
523
526
|
/*sql*/ `
|
|
524
527
|
SELECT COALESCE(SUM(estimated_cost_usd), 0) AS total_cost
|
|
525
528
|
FROM llm_usage_events
|
|
@@ -538,6 +541,7 @@ export function getUsageCostForRun({
|
|
|
538
541
|
*/
|
|
539
542
|
export function listRunConversationIds(cronRunId: string): string[] {
|
|
540
543
|
const rows = rawAll<{ conversation_id: string }>(
|
|
544
|
+
"usage:listRunConversationIds",
|
|
541
545
|
/*sql*/ `
|
|
542
546
|
SELECT DISTINCT conversation_id
|
|
543
547
|
FROM llm_usage_events
|
|
@@ -558,6 +562,7 @@ export function getUsageTotals(
|
|
|
558
562
|
): UsageTotals {
|
|
559
563
|
const where = buildUsageAggregationWhere(range, filter);
|
|
560
564
|
const rows = rawAll<TotalsRow>(
|
|
565
|
+
"usage:getTotals",
|
|
561
566
|
/*sql*/ `
|
|
562
567
|
SELECT
|
|
563
568
|
COALESCE(SUM(input_tokens), 0) AS total_input_tokens,
|
|
@@ -618,6 +623,7 @@ function fetchRawBucketRows(
|
|
|
618
623
|
): UsageEventBucketRow[] {
|
|
619
624
|
const where = buildUsageAggregationWhere(range, filter);
|
|
620
625
|
return rawAll<UsageEventBucketRow>(
|
|
626
|
+
"usage:fetchRawBucketRows",
|
|
621
627
|
/*sql*/ `
|
|
622
628
|
SELECT
|
|
623
629
|
(created_at / ${USAGE_PREAGG_BUCKET_MS}) * ${USAGE_PREAGG_BUCKET_MS} AS created_at,
|
|
@@ -766,6 +772,7 @@ export function getUsageGroupBreakdown(
|
|
|
766
772
|
if (groupBy === "conversation") {
|
|
767
773
|
const where = buildUsageAggregationWhere(range, normalizedFilter, "e");
|
|
768
774
|
const rows = rawAll<GroupRow>(
|
|
775
|
+
"usage:groupBreakdown:conversation",
|
|
769
776
|
/*sql*/ `
|
|
770
777
|
SELECT
|
|
771
778
|
CASE WHEN e.conversation_id IS NULL THEN 'Other'
|
|
@@ -818,6 +825,7 @@ export function getUsageGroupBreakdown(
|
|
|
818
825
|
selectExpression: "schedule_attr_runs.job_id",
|
|
819
826
|
});
|
|
820
827
|
const rows = rawAll<GroupRow>(
|
|
828
|
+
"usage:groupBreakdown:schedule",
|
|
821
829
|
/*sql*/ `
|
|
822
830
|
WITH schedule_usage AS (
|
|
823
831
|
SELECT
|
|
@@ -856,6 +864,7 @@ export function getUsageGroupBreakdown(
|
|
|
856
864
|
const column = GROUP_BY_COLUMNS[groupBy];
|
|
857
865
|
const where = buildUsageAggregationWhere(range, normalizedFilter, "e");
|
|
858
866
|
const rows = rawAll<GroupRow>(
|
|
867
|
+
"usage:groupBreakdown:column",
|
|
859
868
|
/*sql*/ `
|
|
860
869
|
SELECT
|
|
861
870
|
e.${column} AS group_key,
|
|
@@ -903,6 +912,7 @@ export function getUsageGroupedSeries(
|
|
|
903
912
|
selectExpression: "schedule_attr_runs.job_id",
|
|
904
913
|
});
|
|
905
914
|
rows = rawAll<UsageGroupedBucketRow>(
|
|
915
|
+
"usage:groupedSeries:schedule",
|
|
906
916
|
/*sql*/ `
|
|
907
917
|
WITH schedule_usage AS (
|
|
908
918
|
SELECT
|
|
@@ -936,6 +946,7 @@ export function getUsageGroupedSeries(
|
|
|
936
946
|
const column = GROUP_BY_COLUMNS[groupBy];
|
|
937
947
|
const where = buildUsageAggregationWhere(range, normalizedFilter, "e");
|
|
938
948
|
rows = rawAll<UsageGroupedBucketRow>(
|
|
949
|
+
"usage:groupedSeries:column",
|
|
939
950
|
/*sql*/ `
|
|
940
951
|
SELECT
|
|
941
952
|
(e.created_at / ${USAGE_PREAGG_BUCKET_MS}) * ${USAGE_PREAGG_BUCKET_MS} AS created_at,
|
|
@@ -23,7 +23,13 @@ const baseHooks: MemoryPersistenceHooks = {
|
|
|
23
23
|
onConversationWiped() {
|
|
24
24
|
return 0;
|
|
25
25
|
},
|
|
26
|
+
onConversationDeleted() {},
|
|
27
|
+
onMessagesDeleted() {},
|
|
28
|
+
async onAllConversationsCleared() {},
|
|
26
29
|
onWorkerStartup() {},
|
|
30
|
+
countMemoryBufferLines() {
|
|
31
|
+
return 0;
|
|
32
|
+
},
|
|
27
33
|
};
|
|
28
34
|
|
|
29
35
|
describe("memory persistence-lifecycle seam", () => {
|
|
@@ -34,6 +40,18 @@ describe("memory persistence-lifecycle seam", () => {
|
|
|
34
40
|
await getMemoryPersistenceHooks().onMessagePersisted(event);
|
|
35
41
|
});
|
|
36
42
|
|
|
43
|
+
test("countMemoryBufferLines defaults to 0 when memory is not present", () => {
|
|
44
|
+
expect(getMemoryPersistenceHooks().countMemoryBufferLines()).toBe(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("countMemoryBufferLines returns the registered implementation's count", () => {
|
|
48
|
+
registerMemoryPersistenceHooks({
|
|
49
|
+
...baseHooks,
|
|
50
|
+
countMemoryBufferLines: () => 42,
|
|
51
|
+
});
|
|
52
|
+
expect(getMemoryPersistenceHooks().countMemoryBufferLines()).toBe(42);
|
|
53
|
+
});
|
|
54
|
+
|
|
37
55
|
test("getMemoryPersistenceHooks returns the registered implementation", async () => {
|
|
38
56
|
const seen: MessagePersistedEvent[] = [];
|
|
39
57
|
registerMemoryPersistenceHooks({
|
|
@@ -78,4 +96,51 @@ describe("memory persistence-lifecycle seam", () => {
|
|
|
78
96
|
await getMemoryPersistenceHooks().onMessagePersisted(event);
|
|
79
97
|
expect(calls).toBe(0);
|
|
80
98
|
});
|
|
99
|
+
|
|
100
|
+
test("onConversationDeleted defaults to a no-op and forwards the id to the registered impl", () => {
|
|
101
|
+
// No-op default — the "memory not present" path does not throw.
|
|
102
|
+
getMemoryPersistenceHooks().onConversationDeleted("conv-1");
|
|
103
|
+
|
|
104
|
+
const seen: string[] = [];
|
|
105
|
+
registerMemoryPersistenceHooks({
|
|
106
|
+
...baseHooks,
|
|
107
|
+
onConversationDeleted(id) {
|
|
108
|
+
seen.push(id);
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
getMemoryPersistenceHooks().onConversationDeleted("conv-1");
|
|
112
|
+
expect(seen).toEqual(["conv-1"]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("onMessagesDeleted defaults to a no-op and forwards ids to the registered impl", () => {
|
|
116
|
+
// No-op default — the "memory not present" path does not throw.
|
|
117
|
+
getMemoryPersistenceHooks().onMessagesDeleted(["msg-a", "msg-b"]);
|
|
118
|
+
|
|
119
|
+
const seen: string[][] = [];
|
|
120
|
+
registerMemoryPersistenceHooks({
|
|
121
|
+
...baseHooks,
|
|
122
|
+
onMessagesDeleted(ids) {
|
|
123
|
+
seen.push(ids);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
getMemoryPersistenceHooks().onMessagesDeleted(["msg-a", "msg-b"]);
|
|
127
|
+
expect(seen).toEqual([["msg-a", "msg-b"]]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("onAllConversationsCleared defaults to a no-op and awaits the registered impl", async () => {
|
|
131
|
+
// No-op default resolves without throwing.
|
|
132
|
+
await getMemoryPersistenceHooks().onAllConversationsCleared();
|
|
133
|
+
|
|
134
|
+
let cleared = 0;
|
|
135
|
+
registerMemoryPersistenceHooks({
|
|
136
|
+
...baseHooks,
|
|
137
|
+
async onAllConversationsCleared() {
|
|
138
|
+
// Yield so a non-awaiting caller would observe cleared === 0.
|
|
139
|
+
await Promise.resolve();
|
|
140
|
+
cleared++;
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
await getMemoryPersistenceHooks().onAllConversationsCleared();
|
|
144
|
+
expect(cleared).toBe(1);
|
|
145
|
+
});
|
|
81
146
|
});
|
|
@@ -2,7 +2,8 @@ import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
|
2
2
|
import type { DrizzleDb } from "./db-connection.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Seam for the memory feature to react to persistence-layer lifecycle events
|
|
5
|
+
* Seam for the memory feature to react to persistence-layer lifecycle events
|
|
6
|
+
* and to answer memory-domain queries the persistence layer needs.
|
|
6
7
|
*
|
|
7
8
|
* Persistence is a layer below the memory plugin, so it cannot import memory
|
|
8
9
|
* internals directly. Instead it calls into this registered-handler seam: the
|
|
@@ -80,15 +81,62 @@ export interface MemoryPersistenceHooks {
|
|
|
80
81
|
* present). Runs before the conversation's message rows are deleted, since
|
|
81
82
|
* the cancellation queries join on `messages`. Cleanup — runs even while the
|
|
82
83
|
* plugin is disabled, so jobs created while it was enabled are not orphaned.
|
|
84
|
+
*
|
|
85
|
+
* Does NOT purge the conversation's per-message index — that is
|
|
86
|
+
* {@link onConversationDeleted}, fired from the shared delete primitive that
|
|
87
|
+
* `wipeConversation` delegates to (so the purge lands after this cancellation
|
|
88
|
+
* pass and cannot be swept by it).
|
|
83
89
|
*/
|
|
84
90
|
onConversationWiped(conversationId: string): number;
|
|
85
91
|
|
|
92
|
+
/**
|
|
93
|
+
* A conversation and its messages were deleted via the shared delete
|
|
94
|
+
* primitive (`deleteConversation`/`deleteConversationGently`), which covers
|
|
95
|
+
* every caller — the HTTP route, `wipeConversation`, retrospective startup
|
|
96
|
+
* cleanup, superseded-retrospective GC, and future ones. The memory feature
|
|
97
|
+
* purges the conversation's per-message index (e.g. its lexical points, by
|
|
98
|
+
* `conversationId`). Runs after the delete commits and off the write path.
|
|
99
|
+
* Cleanup — runs even while the plugin is disabled, so points written while it
|
|
100
|
+
* was enabled are not orphaned.
|
|
101
|
+
*/
|
|
102
|
+
onConversationDeleted(conversationId: string): void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* One or more message rows were deleted (single-message delete, undo, or
|
|
106
|
+
* assistant-message consolidation) WITHOUT wiping the whole conversation. The
|
|
107
|
+
* memory feature removes each message's per-message index entry (e.g. its
|
|
108
|
+
* lexical point). Runs after the delete transaction commits and off the write
|
|
109
|
+
* path. Cleanup — runs even while the plugin is disabled, so entries written
|
|
110
|
+
* while it was enabled are not orphaned. Empty arrays are a no-op.
|
|
111
|
+
*/
|
|
112
|
+
onMessagesDeleted(messageIds: string[]): void;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Every conversation and its messages are being cleared ("delete all"). The
|
|
116
|
+
* memory feature drops the bulk per-message index (e.g. the whole lexical
|
|
117
|
+
* collection) — a bulk wipe leaves no ids to key per-message cleanup on.
|
|
118
|
+
* Cleanup — runs even while the plugin is disabled. Best-effort; the caller
|
|
119
|
+
* wraps it in try/catch and AWAITS it, so the drop completes before writes
|
|
120
|
+
* resume (a message created right after clear-all must not upsert into a
|
|
121
|
+
* collection that is about to be dropped).
|
|
122
|
+
*/
|
|
123
|
+
onAllConversationsCleared(): Promise<void>;
|
|
124
|
+
|
|
86
125
|
/**
|
|
87
126
|
* The background-job worker is starting. The memory feature sweeps orphan
|
|
88
127
|
* retrospective conversations left by a crash mid-job. Best-effort; the caller
|
|
89
128
|
* wraps it in try/catch. Cleanup — runs even while the plugin is disabled.
|
|
90
129
|
*/
|
|
91
130
|
onWorkerStartup(): void;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Current line count of the memory buffer (`memory/buffer.md`). The
|
|
134
|
+
* maintenance scheduler reads it to gate scheduled consolidation — skip a
|
|
135
|
+
* scheduled run below a floor, force one above a ceiling. Returns 0 when
|
|
136
|
+
* memory is not present, which the scheduler reads as "no buffered work" and
|
|
137
|
+
* therefore no consolidation.
|
|
138
|
+
*/
|
|
139
|
+
countMemoryBufferLines(): number;
|
|
92
140
|
}
|
|
93
141
|
|
|
94
142
|
const NOOP: MemoryPersistenceHooks = {
|
|
@@ -97,7 +145,13 @@ const NOOP: MemoryPersistenceHooks = {
|
|
|
97
145
|
onConversationWiped() {
|
|
98
146
|
return 0;
|
|
99
147
|
},
|
|
148
|
+
onConversationDeleted() {},
|
|
149
|
+
onMessagesDeleted() {},
|
|
150
|
+
async onAllConversationsCleared() {},
|
|
100
151
|
onWorkerStartup() {},
|
|
152
|
+
countMemoryBufferLines() {
|
|
153
|
+
return 0;
|
|
154
|
+
},
|
|
101
155
|
};
|
|
102
156
|
|
|
103
157
|
let current: MemoryPersistenceHooks = NOOP;
|
|
@@ -4,7 +4,7 @@ import type { DrizzleDb } from "../db-connection.js";
|
|
|
4
4
|
* Add call_mode and guardian_verification_session_id columns to call_sessions.
|
|
5
5
|
*
|
|
6
6
|
* call_mode is the persisted source of truth for deterministic flow selection
|
|
7
|
-
* in the
|
|
7
|
+
* in the call setup router. guardian_verification_session_id links the call session
|
|
8
8
|
* to the verification session for observability.
|
|
9
9
|
*/
|
|
10
10
|
export function migrateCallSessionMode(database: DrizzleDb): void {
|
|
@@ -6,6 +6,11 @@ const logger = getLogger("messages-fts");
|
|
|
6
6
|
/**
|
|
7
7
|
* FTS5 virtual table for full-text search over messages.content.
|
|
8
8
|
*
|
|
9
|
+
* Every object this step creates is dropped again by the later
|
|
10
|
+
* `migrateDropMessagesFts` step (`313-drop-messages-fts.ts`) — message-content
|
|
11
|
+
* search reads the sparse Qdrant `messages_lexical` index. This step stays
|
|
12
|
+
* registered because the migration chain is append-only.
|
|
13
|
+
*
|
|
9
14
|
* Content is stored as raw JSON in the messages table — the FTS tokenizer
|
|
10
15
|
* handles it well enough for keyword search since the structural JSON tokens
|
|
11
16
|
* (type, text, tool_use) are short common words that rarely matter as search
|
|
@@ -54,7 +59,9 @@ function isSqliteCorruptionError(err: unknown): boolean {
|
|
|
54
59
|
* from `sqlite_schema`. Without this fallback, `CREATE VIRTUAL TABLE
|
|
55
60
|
* IF NOT EXISTS` would be a no-op and the crash loop would persist.
|
|
56
61
|
*/
|
|
57
|
-
function dropFtsShadowTables(
|
|
62
|
+
export function dropFtsShadowTables(
|
|
63
|
+
raw: ReturnType<typeof getSqliteFrom>,
|
|
64
|
+
): void {
|
|
58
65
|
const drops = [
|
|
59
66
|
`DROP TRIGGER IF EXISTS messages_fts_ai`,
|
|
60
67
|
`DROP TRIGGER IF EXISTS messages_fts_ad`,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getSqlite } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the `subagents` table: durable lifecycle records for subagents
|
|
5
|
+
* spawned by `SubagentManager`.
|
|
6
|
+
*
|
|
7
|
+
* Subagents run as background child conversations whose rows already persist
|
|
8
|
+
* via `bootstrapConversation`, but the manager-only fields (parent link,
|
|
9
|
+
* label, objective, role, fork flag, status, timestamps, usage) live only in
|
|
10
|
+
* the manager's in-memory maps and are lost on restart. This table persists
|
|
11
|
+
* them so a restart can rehydrate completed subagents (their output is read
|
|
12
|
+
* from the child conversation's messages) and mark any that were still in
|
|
13
|
+
* flight as `interrupted` rather than silently orphaning them.
|
|
14
|
+
*
|
|
15
|
+
* Rows are written on spawn and on every status transition, and deleted when
|
|
16
|
+
* the manager disposes the subagent during normal operation (TTL sweep or
|
|
17
|
+
* parent eviction) — never on shutdown, so in-flight rows survive the restart.
|
|
18
|
+
*
|
|
19
|
+
* Idempotent (`IF NOT EXISTS`). No backfill — pre-existing subagents predate
|
|
20
|
+
* the table and were already only in-memory.
|
|
21
|
+
*/
|
|
22
|
+
export function migrateCreateSubagentsTable(): void {
|
|
23
|
+
const raw = getSqlite();
|
|
24
|
+
raw.exec(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS subagents (
|
|
26
|
+
id TEXT PRIMARY KEY,
|
|
27
|
+
parent_conversation_id TEXT NOT NULL,
|
|
28
|
+
conversation_id TEXT NOT NULL,
|
|
29
|
+
label TEXT NOT NULL,
|
|
30
|
+
objective TEXT NOT NULL,
|
|
31
|
+
role TEXT NOT NULL DEFAULT 'general',
|
|
32
|
+
is_fork INTEGER NOT NULL DEFAULT 0,
|
|
33
|
+
send_result_to_user INTEGER,
|
|
34
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
35
|
+
error TEXT,
|
|
36
|
+
created_at INTEGER NOT NULL,
|
|
37
|
+
started_at INTEGER,
|
|
38
|
+
completed_at INTEGER,
|
|
39
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
40
|
+
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
41
|
+
estimated_cost REAL NOT NULL DEFAULT 0
|
|
42
|
+
)
|
|
43
|
+
`);
|
|
44
|
+
raw.exec(
|
|
45
|
+
`CREATE INDEX IF NOT EXISTS idx_subagents_parent_conversation_id ON subagents(parent_conversation_id)`,
|
|
46
|
+
);
|
|
47
|
+
raw.exec(
|
|
48
|
+
`CREATE INDEX IF NOT EXISTS idx_subagents_status ON subagents(status)`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drops the dead `assistant_inbox_conversation_state` table — no production
|
|
5
|
+
* code reads or writes it.
|
|
6
|
+
*
|
|
7
|
+
* Idempotent: DROP TABLE IF EXISTS.
|
|
8
|
+
*/
|
|
9
|
+
export function migrateDropInboxConversationStateTable(
|
|
10
|
+
database: DrizzleDb,
|
|
11
|
+
): void {
|
|
12
|
+
database.run(
|
|
13
|
+
/*sql*/ `DROP TABLE IF EXISTS assistant_inbox_conversation_state`,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
import { dropFtsShadowTables } from "./116-messages-fts.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Drops the `messages_fts` FTS5 virtual table and its per-row triggers
|
|
6
|
+
* (`messages_fts_ai/ad/au`). Message-content search reads the sparse Qdrant
|
|
7
|
+
* `messages_lexical` index (see `conversation-search-lexical.ts`), so the
|
|
8
|
+
* synchronous per-row FTS maintenance — the dominant write-amplification cost
|
|
9
|
+
* on `messages` for batch forks and batch deletes — carries no reader.
|
|
10
|
+
*
|
|
11
|
+
* Idempotent: every drop is `IF EXISTS` / individually error-swallowed, and
|
|
12
|
+
* the `writable_schema` fallback inside {@link dropFtsShadowTables} handles a
|
|
13
|
+
* corrupt vtable that blocks a plain `DROP TABLE`.
|
|
14
|
+
*/
|
|
15
|
+
export function migrateDropMessagesFts(database: DrizzleDb): void {
|
|
16
|
+
dropFtsShadowTables(getSqliteFrom(database));
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { tableHasColumn } from "./schema-introspection.js";
|
|
3
|
+
|
|
4
|
+
const COLUMN_NAME = "enabled_plugins";
|
|
5
|
+
const COLUMN_DEFINITION = "enabled_plugins TEXT";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Add the nullable `enabled_plugins` column to the `conversations` table.
|
|
9
|
+
*
|
|
10
|
+
* Stores a JSON-encoded `string[]` of plugin ids scoping the chat, or NULL
|
|
11
|
+
* (the default) meaning no per-chat restriction — all globally-enabled
|
|
12
|
+
* plugins apply. No backfill is needed: existing rows default to NULL.
|
|
13
|
+
*
|
|
14
|
+
* Idempotent: guarded with `tableHasColumn` so a crash between the `ALTER
|
|
15
|
+
* TABLE` and the checkpoint write doesn't cause a duplicate-column error on
|
|
16
|
+
* the next boot.
|
|
17
|
+
*/
|
|
18
|
+
export function migrateAddConversationEnabledPlugins(
|
|
19
|
+
database: DrizzleDb,
|
|
20
|
+
): void {
|
|
21
|
+
if (tableHasColumn(database, "conversations", COLUMN_NAME)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
database.run(`ALTER TABLE conversations ADD COLUMN ${COLUMN_DEFINITION}`);
|
|
25
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the `a2a_invites` table: daemon-local store for A2A invite tokens.
|
|
5
|
+
*
|
|
6
|
+
* A2A invites are peer-assistant bindings outside the human-trust ACL model,
|
|
7
|
+
* so they live in the assistant DB rather than the gateway's `ingress_invites`
|
|
8
|
+
* table. The table carries exactly the columns the A2A flow uses.
|
|
9
|
+
*
|
|
10
|
+
* Existing A2A rows are copied from `assistant_ingress_invites` (guarded on
|
|
11
|
+
* the source table's existence) so in-flight invites survive. Source rows are
|
|
12
|
+
* left in place — that table is dropped wholesale by a later migration.
|
|
13
|
+
*
|
|
14
|
+
* Idempotent: `IF NOT EXISTS` table creation and `INSERT OR IGNORE` copy.
|
|
15
|
+
*/
|
|
16
|
+
export function migrateCreateA2aInvitesTable(db: DrizzleDb): void {
|
|
17
|
+
const raw = getSqliteFrom(db);
|
|
18
|
+
|
|
19
|
+
raw.exec(/*sql*/ `
|
|
20
|
+
CREATE TABLE IF NOT EXISTS a2a_invites (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
token_hash TEXT NOT NULL,
|
|
23
|
+
contact_id TEXT NOT NULL,
|
|
24
|
+
max_uses INTEGER NOT NULL DEFAULT 1,
|
|
25
|
+
use_count INTEGER NOT NULL DEFAULT 0,
|
|
26
|
+
expires_at INTEGER NOT NULL,
|
|
27
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
28
|
+
redeemed_by_external_user_id TEXT,
|
|
29
|
+
redeemed_at INTEGER,
|
|
30
|
+
created_at INTEGER NOT NULL,
|
|
31
|
+
updated_at INTEGER NOT NULL
|
|
32
|
+
)
|
|
33
|
+
`);
|
|
34
|
+
raw.exec(
|
|
35
|
+
`CREATE INDEX IF NOT EXISTS idx_a2a_invites_token_hash ON a2a_invites(token_hash)`,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const sourceExists = raw
|
|
39
|
+
.prepare(
|
|
40
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'assistant_ingress_invites'`,
|
|
41
|
+
)
|
|
42
|
+
.get();
|
|
43
|
+
if (!sourceExists) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
raw.exec(/*sql*/ `
|
|
48
|
+
INSERT OR IGNORE INTO a2a_invites (
|
|
49
|
+
id, token_hash, contact_id, max_uses, use_count, expires_at, status,
|
|
50
|
+
redeemed_by_external_user_id, redeemed_at, created_at, updated_at
|
|
51
|
+
)
|
|
52
|
+
SELECT
|
|
53
|
+
id, token_hash, contact_id, max_uses, use_count, expires_at, status,
|
|
54
|
+
redeemed_by_external_user_id, redeemed_at, created_at, updated_at
|
|
55
|
+
FROM assistant_ingress_invites
|
|
56
|
+
WHERE source_channel = 'a2a'
|
|
57
|
+
`);
|
|
58
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getLogger } from "../../util/logger.js";
|
|
2
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
import { tableHasColumn } from "./schema-introspection.js";
|
|
4
|
+
|
|
5
|
+
const log = getLogger("migration-316");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Drops the `contact_channels.invite_id` column.
|
|
9
|
+
*
|
|
10
|
+
* The gateway's `ingress_invites` table owns the invite lifecycle (A2A invites
|
|
11
|
+
* live in the daemon's `a2a_invites` table), so the assistant-side column is
|
|
12
|
+
* dead weight. The `assistant_ingress_invites` table is dropped by gateway
|
|
13
|
+
* data migration m0010 — not here — because gateway m0009 must backfill its
|
|
14
|
+
* rows first, and assistant migrations run at daemon boot, before the
|
|
15
|
+
* gateway's post-assistant-ready data-migration pass.
|
|
16
|
+
*
|
|
17
|
+
* No DROP INDEX needed: `invite_id` is not covered by any contact_channels
|
|
18
|
+
* index and carries no FK. Idempotent via the column-presence guard.
|
|
19
|
+
*/
|
|
20
|
+
export function migrateDropContactChannelInviteId(database: DrizzleDb): void {
|
|
21
|
+
const raw = getSqliteFrom(database);
|
|
22
|
+
|
|
23
|
+
if (!tableHasColumn(database, "contact_channels", "invite_id")) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
raw.run(/*sql*/ `ALTER TABLE contact_channels DROP COLUMN invite_id`);
|
|
27
|
+
log.info("Dropped invite_id column from contact_channels");
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add requester_signals column to canonical_guardian_requests.
|
|
5
|
+
*
|
|
6
|
+
* Stores the JSON-encoded platform identity signals for the requester
|
|
7
|
+
* (isBot / isStranger / isRestricted) at request-creation time, so the
|
|
8
|
+
* introduction-card decision path derives binding strength and bot handling
|
|
9
|
+
* from the same facts the card was rendered from.
|
|
10
|
+
*
|
|
11
|
+
* Uses ALTER TABLE ADD COLUMN with try/catch for idempotency — no registry
|
|
12
|
+
* entry needed.
|
|
13
|
+
*/
|
|
14
|
+
export function migrateCanonicalGuardianRequesterSignals(
|
|
15
|
+
database: DrizzleDb,
|
|
16
|
+
): void {
|
|
17
|
+
try {
|
|
18
|
+
database.run(
|
|
19
|
+
/*sql*/ `ALTER TABLE canonical_guardian_requests ADD COLUMN requester_signals TEXT`,
|
|
20
|
+
);
|
|
21
|
+
} catch {
|
|
22
|
+
/* already exists */
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getLogger } from "../../util/logger.js";
|
|
2
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
import { tableHasColumn } from "./schema-introspection.js";
|
|
4
|
+
|
|
5
|
+
const log = getLogger("migration-318");
|
|
6
|
+
|
|
7
|
+
const TELEMETRY_COLUMNS = [
|
|
8
|
+
"last_seen_at",
|
|
9
|
+
"interaction_count",
|
|
10
|
+
"last_interaction",
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Drops the `contact_channels` interaction-telemetry columns
|
|
15
|
+
* (`last_seen_at`, `interaction_count`, `last_interaction`).
|
|
16
|
+
*
|
|
17
|
+
* Interaction telemetry is gateway-owned: reads source it from the stamped
|
|
18
|
+
* trust verdict and the gateway rich-read relay, and writes go only to the
|
|
19
|
+
* gateway DB. The assistant-side columns are dead weight.
|
|
20
|
+
*
|
|
21
|
+
* No DROP INDEX needed: none of these columns are covered by a contact_channels
|
|
22
|
+
* index and they carry no FK. Idempotent via the per-column presence guard.
|
|
23
|
+
*/
|
|
24
|
+
export function migrateDropContactChannelTelemetry(database: DrizzleDb): void {
|
|
25
|
+
const raw = getSqliteFrom(database);
|
|
26
|
+
|
|
27
|
+
for (const column of TELEMETRY_COLUMNS) {
|
|
28
|
+
if (!tableHasColumn(database, "contact_channels", column)) continue;
|
|
29
|
+
raw.run(/*sql*/ `ALTER TABLE contact_channels DROP COLUMN ${column}`);
|
|
30
|
+
log.info(`Dropped ${column} column from contact_channels`);
|
|
31
|
+
}
|
|
32
|
+
}
|