@vellumai/assistant 0.7.2 → 0.7.3
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 +16 -1
- package/docs/architecture/memory.md +5 -2
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
- package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- package/openapi.yaml +449 -22
- package/package.json +1 -1
- package/src/__tests__/app-control-flow.test.ts +21 -11
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/assistant-event.test.ts +0 -10
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- package/src/__tests__/call-conversation-messages.test.ts +8 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +4 -2
- package/src/__tests__/config-loader-backfill.test.ts +379 -0
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
- package/src/__tests__/config-watcher.test.ts +140 -69
- package/src/__tests__/context-search-agent-runner.test.ts +61 -3
- package/src/__tests__/context-search-conversations-source.test.ts +0 -24
- package/src/__tests__/context-search-fanout.test.ts +0 -1
- package/src/__tests__/context-search-memory-source.test.ts +3 -7
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop.test.ts +454 -5
- package/src/__tests__/conversation-error.test.ts +150 -3
- package/src/__tests__/conversation-process-callsite.test.ts +43 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-store.test.ts +0 -18
- package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/credentials-cli.test.ts +7 -0
- package/src/__tests__/cu-unified-flow.test.ts +176 -10
- package/src/__tests__/date-context.test.ts +164 -2
- package/src/__tests__/disk-pressure-guard.test.ts +262 -0
- package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
- package/src/__tests__/disk-pressure-policy.test.ts +241 -0
- package/src/__tests__/disk-pressure-routes.test.ts +379 -0
- package/src/__tests__/disk-pressure-tools.test.ts +277 -0
- package/src/__tests__/disk-usage.test.ts +150 -0
- package/src/__tests__/events-client-registration.test.ts +52 -0
- package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
- package/src/__tests__/file-write-tool.test.ts +4 -10
- package/src/__tests__/filing-service.test.ts +3 -4
- package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +260 -11
- package/src/__tests__/host-app-control-proxy.test.ts +195 -25
- package/src/__tests__/host-bash-proxy.test.ts +227 -34
- package/src/__tests__/host-bash-routes.test.ts +178 -13
- package/src/__tests__/host-cu-proxy.test.ts +210 -3
- package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
- package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
- package/src/__tests__/host-file-proxy.test.ts +268 -6
- package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
- package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
- package/src/__tests__/http-user-message-parity.test.ts +107 -1
- package/src/__tests__/injector-chain.test.ts +18 -6
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- package/src/__tests__/managed-profile-guard.test.ts +18 -0
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- package/src/__tests__/onboarding-persona-write.test.ts +308 -0
- package/src/__tests__/openai-provider.test.ts +45 -8
- package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
- package/src/__tests__/platform-callback-registration.test.ts +21 -4
- package/src/__tests__/platform.test.ts +2 -1
- package/src/__tests__/playbook-execution.test.ts +0 -43
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
- package/src/__tests__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/relay-server.test.ts +15 -4
- package/src/__tests__/runtime-events-sse.test.ts +4 -8
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -1
- package/src/__tests__/suggestion-routes.test.ts +46 -0
- package/src/__tests__/twilio-validation.test.ts +2 -2
- package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
- package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
- package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
- package/src/approvals/guardian-decision-primitive.ts +13 -0
- package/src/approvals/guardian-request-resolvers.ts +16 -17
- package/src/backup/snapshot-lock.ts +2 -27
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-conversation-messages.ts +46 -10
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/contacts.ts +64 -25
- package/src/cli/commands/credentials.ts +56 -0
- package/src/cli/commands/memory-v2.ts +7 -6
- package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
- package/src/cli/commands/oauth/connect.ts +127 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
- package/src/cli/commands/platform/index.ts +16 -7
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/program.ts +4 -2
- package/src/config/assistant-feature-flags.ts +13 -3
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
- package/src/config/env.ts +0 -8
- package/src/config/feature-flag-registry.json +27 -3
- package/src/config/loader.ts +127 -8
- package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +2 -0
- package/src/config/schemas/memory-lifecycle.ts +13 -0
- package/src/config/schemas/memory-v2.ts +75 -11
- package/src/config/schemas/platform.ts +43 -3
- package/src/config/schemas/services.ts +28 -0
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -25
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/config-watcher.ts +85 -57
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +170 -33
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +1 -3
- package/src/daemon/conversation-process.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +26 -0
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +195 -15
- package/src/daemon/conversation-tool-setup.ts +57 -14
- package/src/daemon/conversation.ts +17 -22
- package/src/daemon/date-context.ts +71 -22
- package/src/daemon/disk-pressure-background-gate.ts +73 -0
- package/src/daemon/disk-pressure-guard.ts +343 -0
- package/src/daemon/disk-pressure-policy.ts +163 -0
- package/src/daemon/handlers/shared.ts +0 -1
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +137 -41
- package/src/daemon/host-bash-proxy.ts +46 -21
- package/src/daemon/host-cu-proxy.ts +49 -3
- package/src/daemon/host-file-proxy.ts +43 -7
- package/src/daemon/host-transfer-proxy.ts +95 -4
- package/src/daemon/lifecycle.ts +79 -28
- package/src/daemon/meet-host-supervisor.ts +4 -4
- package/src/daemon/meet-manifest-loader.ts +0 -1
- package/src/daemon/memory-v2-startup.ts +14 -4
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +4 -0
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- package/src/daemon/message-types/messages.ts +3 -0
- package/src/daemon/profiler-run-store.ts +5 -5
- package/src/daemon/tool-setup-types.ts +2 -2
- package/src/documents/document-store.ts +85 -0
- package/src/filing/filing-service.ts +30 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
- package/src/heartbeat/heartbeat-run-store.ts +13 -0
- package/src/heartbeat/heartbeat-service.ts +205 -31
- package/src/home/feed-scheduler.ts +18 -0
- package/src/inbound/platform-callback-registration.ts +8 -15
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/assistant-server.ts +56 -2
- package/src/ipc/gateway-client.ts +37 -3
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/protocol.ts +5 -7
- package/src/media/image-service.ts +1 -7
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
- package/src/memory/admin.ts +5 -9
- package/src/memory/context-search/agent-runner.ts +19 -2
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +5 -4
- package/src/memory/context-search/sources/memory.ts +0 -1
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +4 -12
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +32 -21
- package/src/memory/graph/conversation-graph-memory.ts +42 -54
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +10 -67
- package/src/memory/graph/graph-search.ts +1 -20
- package/src/memory/graph/retriever.test.ts +6 -0
- package/src/memory/graph/retriever.ts +6 -10
- package/src/memory/indexer.ts +54 -45
- package/src/memory/job-handlers/backfill.ts +2 -11
- package/src/memory/job-handlers/cleanup.ts +43 -0
- package/src/memory/job-handlers/embedding.ts +6 -8
- package/src/memory/job-handlers/summarization.ts +2 -7
- package/src/memory/jobs-store.ts +48 -0
- package/src/memory/jobs-worker.ts +81 -43
- package/src/memory/memory-v2-activation-log-store.ts +32 -14
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +6 -0
- package/src/memory/qdrant-client.ts +0 -13
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/search/semantic.ts +6 -67
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +311 -250
- package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
- package/src/memory/v2/__tests__/injection.test.ts +157 -167
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +5 -199
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/static-context.test.ts +76 -1
- package/src/memory/v2/activation.ts +149 -156
- package/src/memory/v2/consolidation-job.ts +62 -12
- package/src/memory/v2/injection.ts +47 -60
- package/src/memory/v2/prompts/consolidation.ts +36 -1
- package/src/memory/v2/qdrant.ts +99 -0
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +10 -84
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +82 -59
- package/src/memory/v2/static-context.ts +22 -0
- package/src/memory/v2/types.ts +10 -10
- package/src/notifications/copy-composer.ts +13 -0
- package/src/notifications/signal.ts +4 -0
- package/src/oauth/AGENTS.md +3 -1
- package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- package/src/plugins/defaults/injectors.ts +35 -2
- package/src/plugins/defaults/memory-retrieval.ts +5 -6
- package/src/plugins/types.ts +7 -0
- package/src/proactive-artifact/aux-message-injector.ts +74 -0
- package/src/proactive-artifact/decision.test.ts +226 -0
- package/src/proactive-artifact/decision.ts +165 -0
- package/src/proactive-artifact/index.ts +7 -0
- package/src/proactive-artifact/job.test.ts +867 -0
- package/src/proactive-artifact/job.ts +352 -0
- package/src/proactive-artifact/message-copy.ts +41 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +21 -7
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/providers/__tests__/retry-callsite.test.ts +222 -1
- package/src/providers/model-intents.ts +7 -0
- package/src/providers/openrouter/client.ts +8 -0
- package/src/providers/retry.ts +50 -0
- package/src/providers/types.ts +1 -0
- package/src/runtime/__tests__/agent-wake.test.ts +456 -3
- package/src/runtime/agent-wake.ts +238 -100
- package/src/runtime/assistant-event-hub.ts +36 -6
- package/src/runtime/assistant-event.ts +0 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/auth/same-actor.ts +216 -0
- package/src/runtime/channel-retry-sweep.ts +65 -1
- package/src/runtime/guardian-reply-router.ts +10 -0
- package/src/runtime/local-actor-identity.ts +52 -11
- package/src/runtime/pending-interactions.ts +8 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/client-routes.ts +20 -2
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-routes.ts +35 -26
- package/src/runtime/routes/debug-bash-routes.ts +163 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +6 -2
- package/src/runtime/routes/documents-routes.ts +2 -75
- package/src/runtime/routes/events-routes.ts +41 -9
- package/src/runtime/routes/host-bash-routes.ts +23 -3
- package/src/runtime/routes/host-cu-routes.ts +33 -6
- package/src/runtime/routes/host-file-routes.ts +32 -6
- package/src/runtime/routes/host-transfer-routes.ts +79 -16
- package/src/runtime/routes/identity-routes.ts +7 -138
- package/src/runtime/routes/inbound-message-handler.ts +77 -12
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/memory-item-routes.test.ts +41 -15
- package/src/runtime/routes/memory-v2-routes.ts +33 -0
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- package/src/schedule/run-script.ts +37 -5
- package/src/schedule/scheduler.ts +20 -1
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/secure-keys.ts +55 -0
- package/src/skills/remote-skill-policy.ts +4 -10
- package/src/subagent/index.ts +1 -7
- package/src/subagent/manager.ts +1 -15
- package/src/tasks/task-runner.ts +0 -1
- package/src/tasks/task-store.ts +0 -3
- package/src/tools/background-tool-registry.ts +17 -3
- package/src/tools/host-filesystem/edit.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +43 -1
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +43 -1
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +56 -11
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +43 -1
- package/src/tools/host-terminal/host-shell.ts +13 -6
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +12 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- package/src/tools/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +31 -6
- package/src/tools/types.ts +24 -2
- package/src/tts/provider-catalog.ts +3 -5
- package/src/util/disk-usage.ts +138 -0
- package/src/util/platform.ts +21 -11
- package/src/util/process-liveness.ts +26 -0
- package/src/workspace/heartbeat-service.ts +19 -0
- package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -404
- package/src/signals/bash.ts +0 -198
|
@@ -153,25 +153,18 @@ describe("pre-chat onboarding contract", () => {
|
|
|
153
153
|
const result = buildSystemPrompt({ onboardingContext: context });
|
|
154
154
|
const dynamic = dynamicBlock(result);
|
|
155
155
|
|
|
156
|
-
expect(dynamic).toContain("##
|
|
156
|
+
expect(dynamic).toContain("## First-Run User Context");
|
|
157
157
|
expect(dynamic).toContain(
|
|
158
|
-
"The user completed
|
|
159
|
-
);
|
|
160
|
-
expect(dynamic).toContain('"tools"');
|
|
161
|
-
expect(dynamic).toContain('"slack"');
|
|
162
|
-
expect(dynamic).toContain('"linear"');
|
|
163
|
-
expect(dynamic).toContain('"tasks"');
|
|
164
|
-
expect(dynamic).toContain('"code-building"');
|
|
165
|
-
expect(dynamic).toContain('"tone": "warm"');
|
|
166
|
-
expect(dynamic).toContain('"userName": "Alex"');
|
|
167
|
-
expect(dynamic).toContain('"assistantName": "Nova"');
|
|
168
|
-
expect(dynamic).toContain("```json");
|
|
169
|
-
expect(dynamic).toContain(
|
|
170
|
-
"Use this to personalize your opener and skip redundant discovery.",
|
|
171
|
-
);
|
|
172
|
-
expect(dynamic).toContain(
|
|
173
|
-
"If `assistantName` is present, it is the name the user chose for you; preserve it in IDENTITY.md.",
|
|
158
|
+
"The user completed setup before this conversation.",
|
|
174
159
|
);
|
|
160
|
+
expect(dynamic).toContain("- Daily tools: Slack, Linear");
|
|
161
|
+
expect(dynamic).toContain("- Common work: builds code, apps, or tools");
|
|
162
|
+
expect(dynamic).toContain("- Name: Alex");
|
|
163
|
+
expect(dynamic).toContain("- Chosen assistant name: Nova");
|
|
164
|
+
expect(dynamic).toContain("Apply this context quietly.");
|
|
165
|
+
|
|
166
|
+
// Raw JSON must NOT be present
|
|
167
|
+
expect(dynamic).not.toContain("```json");
|
|
175
168
|
});
|
|
176
169
|
|
|
177
170
|
test("does NOT inject onboarding context when BOOTSTRAP.md does not exist", () => {
|
|
@@ -186,9 +179,9 @@ describe("pre-chat onboarding contract", () => {
|
|
|
186
179
|
const result = buildSystemPrompt({ onboardingContext: context });
|
|
187
180
|
const dynamic = dynamicBlock(result);
|
|
188
181
|
|
|
189
|
-
expect(dynamic).not.toContain("##
|
|
190
|
-
expect(dynamic).not.toContain("
|
|
191
|
-
expect(dynamic).not.toContain(
|
|
182
|
+
expect(dynamic).not.toContain("## First-Run User Context");
|
|
183
|
+
expect(dynamic).not.toContain("First-Run User Context");
|
|
184
|
+
expect(dynamic).not.toContain("- Daily tools:");
|
|
192
185
|
});
|
|
193
186
|
|
|
194
187
|
test("does NOT inject onboarding context when excludeBootstrap is true", () => {
|
|
@@ -209,7 +202,7 @@ describe("pre-chat onboarding contract", () => {
|
|
|
209
202
|
});
|
|
210
203
|
const dynamic = dynamicBlock(result);
|
|
211
204
|
|
|
212
|
-
expect(dynamic).not.toContain("##
|
|
205
|
+
expect(dynamic).not.toContain("## First-Run User Context");
|
|
213
206
|
expect(dynamic).not.toContain("First-Run Ritual");
|
|
214
207
|
});
|
|
215
208
|
|
|
@@ -225,7 +218,7 @@ describe("pre-chat onboarding contract", () => {
|
|
|
225
218
|
// Bootstrap should still be present
|
|
226
219
|
expect(dynamic).toContain("First-Run Ritual");
|
|
227
220
|
// But no onboarding context section
|
|
228
|
-
expect(dynamic).not.toContain("##
|
|
221
|
+
expect(dynamic).not.toContain("## First-Run User Context");
|
|
229
222
|
});
|
|
230
223
|
|
|
231
224
|
test("accepts all four personality tones", () => {
|
|
@@ -247,12 +240,12 @@ describe("pre-chat onboarding contract", () => {
|
|
|
247
240
|
const result = buildSystemPrompt({ onboardingContext: context });
|
|
248
241
|
const dynamic = dynamicBlock(result);
|
|
249
242
|
|
|
250
|
-
expect(dynamic).toContain("##
|
|
251
|
-
expect(dynamic).toContain(
|
|
243
|
+
expect(dynamic).toContain("## First-Run User Context");
|
|
244
|
+
expect(dynamic).toContain(`- Preferred initial voice: ${tone}`);
|
|
252
245
|
}
|
|
253
246
|
});
|
|
254
247
|
|
|
255
|
-
test("
|
|
248
|
+
test("renders compact markdown, not JSON", () => {
|
|
256
249
|
writeFileSync(
|
|
257
250
|
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
258
251
|
"# Bootstrap\n\nOnboarding.",
|
|
@@ -269,9 +262,65 @@ describe("pre-chat onboarding contract", () => {
|
|
|
269
262
|
const result = buildSystemPrompt({ onboardingContext: context });
|
|
270
263
|
const dynamic = dynamicBlock(result);
|
|
271
264
|
|
|
272
|
-
//
|
|
265
|
+
// Should contain compact markdown lines
|
|
266
|
+
expect(dynamic).toContain("## First-Run User Context");
|
|
267
|
+
expect(dynamic).toContain("- Name: Jane");
|
|
268
|
+
expect(dynamic).toContain("- Common work: plans and coordinates work");
|
|
269
|
+
expect(dynamic).toContain("- Daily tools: Notion");
|
|
270
|
+
expect(dynamic).toContain("- Chosen assistant name: Kit");
|
|
271
|
+
expect(dynamic).toContain("- Preferred initial voice: warm");
|
|
272
|
+
|
|
273
|
+
// Must NOT contain JSON output
|
|
274
|
+
expect(dynamic).not.toContain("```json");
|
|
273
275
|
const expectedJson = JSON.stringify(context, null, 2);
|
|
274
|
-
expect(dynamic).toContain(expectedJson);
|
|
276
|
+
expect(dynamic).not.toContain(expectedJson);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("empty tools/tasks arrays result in no Daily tools / Common work lines", () => {
|
|
280
|
+
writeFileSync(
|
|
281
|
+
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
282
|
+
"# Bootstrap\n\nOnboarding.",
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const context: OnboardingContext = {
|
|
286
|
+
tools: [],
|
|
287
|
+
tasks: [],
|
|
288
|
+
tone: "warm",
|
|
289
|
+
userName: "Alex",
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const result = buildSystemPrompt({ onboardingContext: context });
|
|
293
|
+
const dynamic = dynamicBlock(result);
|
|
294
|
+
|
|
295
|
+
expect(dynamic).toContain("## First-Run User Context");
|
|
296
|
+
expect(dynamic).toContain("- Name: Alex");
|
|
297
|
+
expect(dynamic).not.toContain("- Daily tools:");
|
|
298
|
+
expect(dynamic).not.toContain("- Common work:");
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("absent userName results in no Name line", () => {
|
|
302
|
+
writeFileSync(
|
|
303
|
+
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
304
|
+
"# Bootstrap\n\nOnboarding.",
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
const context: OnboardingContext = {
|
|
308
|
+
tools: ["slack"],
|
|
309
|
+
tasks: ["writing"],
|
|
310
|
+
tone: "warm",
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const result = buildSystemPrompt({ onboardingContext: context });
|
|
314
|
+
const dynamic = dynamicBlock(result);
|
|
315
|
+
|
|
316
|
+
expect(dynamic).toContain("## First-Run User Context");
|
|
317
|
+
expect(dynamic).not.toContain("- Name:");
|
|
318
|
+
// Other fields should still be present
|
|
319
|
+
expect(dynamic).toContain("- Daily tools: Slack");
|
|
320
|
+
expect(dynamic).toContain(
|
|
321
|
+
"- Common work: writes docs, emails, or content",
|
|
322
|
+
);
|
|
323
|
+
expect(dynamic).toContain("- Preferred initial voice: warm");
|
|
275
324
|
});
|
|
276
325
|
});
|
|
277
326
|
|
|
@@ -290,4 +339,142 @@ describe("pre-chat onboarding contract", () => {
|
|
|
290
339
|
expect(true).toBe(true); // structural acknowledgment
|
|
291
340
|
});
|
|
292
341
|
});
|
|
342
|
+
|
|
343
|
+
describe("end-to-end onboarding integration", () => {
|
|
344
|
+
test("with BOOTSTRAP.md present, onboarding context produces compact markdown with normalized labels", () => {
|
|
345
|
+
writeFileSync(
|
|
346
|
+
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
347
|
+
"# Bootstrap\n\nWelcome to your first conversation.",
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
const context: OnboardingContext = {
|
|
351
|
+
tools: ["slack", "notion", "linear"],
|
|
352
|
+
tasks: ["code-building", "writing", "project-management"],
|
|
353
|
+
tone: "grounded",
|
|
354
|
+
userName: "Alice",
|
|
355
|
+
assistantName: "Pax",
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const result = buildSystemPrompt({ onboardingContext: context });
|
|
359
|
+
const dynamic = dynamicBlock(result);
|
|
360
|
+
|
|
361
|
+
// Heading is present
|
|
362
|
+
expect(dynamic).toContain("## First-Run User Context");
|
|
363
|
+
|
|
364
|
+
// Normalized labels appear (capitalised tool names, human-readable task descriptions)
|
|
365
|
+
expect(dynamic).toContain("- Daily tools: Slack, Notion, Linear");
|
|
366
|
+
expect(dynamic).toContain("- Name: Alice");
|
|
367
|
+
expect(dynamic).toContain("- Chosen assistant name: Pax");
|
|
368
|
+
expect(dynamic).toContain("- Preferred initial voice: grounded");
|
|
369
|
+
// Common work descriptions are normalised from task IDs
|
|
370
|
+
expect(dynamic).toContain("- Common work:");
|
|
371
|
+
|
|
372
|
+
// No raw JSON anywhere in the dynamic block
|
|
373
|
+
expect(dynamic).not.toContain("```json");
|
|
374
|
+
expect(dynamic).not.toContain('"tools"');
|
|
375
|
+
expect(dynamic).not.toContain('"tasks"');
|
|
376
|
+
expect(dynamic).not.toContain('"tone"');
|
|
377
|
+
expect(dynamic).not.toContain('"userName"');
|
|
378
|
+
expect(dynamic).not.toContain('"assistantName"');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("without BOOTSTRAP.md, onboarding context does NOT appear in system prompt", () => {
|
|
382
|
+
// No BOOTSTRAP.md created — simulates a returning user session
|
|
383
|
+
const context: OnboardingContext = {
|
|
384
|
+
tools: ["slack", "figma"],
|
|
385
|
+
tasks: ["design", "writing"],
|
|
386
|
+
tone: "warm",
|
|
387
|
+
userName: "Bob",
|
|
388
|
+
assistantName: "Kit",
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const result = buildSystemPrompt({ onboardingContext: context });
|
|
392
|
+
const dynamic = dynamicBlock(result);
|
|
393
|
+
|
|
394
|
+
// Onboarding section must be absent
|
|
395
|
+
expect(dynamic).not.toContain("## First-Run User Context");
|
|
396
|
+
expect(dynamic).not.toContain("First-Run Ritual");
|
|
397
|
+
expect(dynamic).not.toContain("- Daily tools:");
|
|
398
|
+
expect(dynamic).not.toContain("- Name: Bob");
|
|
399
|
+
expect(dynamic).not.toContain("- Chosen assistant name:");
|
|
400
|
+
expect(dynamic).not.toContain("Apply this context quietly.");
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("excludeBootstrap suppresses both bootstrap and onboarding sections", () => {
|
|
404
|
+
writeFileSync(
|
|
405
|
+
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
406
|
+
"# Bootstrap\n\nFirst run instructions.",
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
const context: OnboardingContext = {
|
|
410
|
+
tools: ["linear"],
|
|
411
|
+
tasks: ["code-building"],
|
|
412
|
+
tone: "energetic",
|
|
413
|
+
userName: "Charlie",
|
|
414
|
+
assistantName: "Nova",
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const result = buildSystemPrompt({
|
|
418
|
+
onboardingContext: context,
|
|
419
|
+
excludeBootstrap: true,
|
|
420
|
+
});
|
|
421
|
+
const dynamic = dynamicBlock(result);
|
|
422
|
+
|
|
423
|
+
// Both bootstrap and onboarding must be suppressed
|
|
424
|
+
expect(dynamic).not.toContain("First-Run Ritual");
|
|
425
|
+
expect(dynamic).not.toContain("## First-Run User Context");
|
|
426
|
+
expect(dynamic).not.toContain("- Daily tools:");
|
|
427
|
+
expect(dynamic).not.toContain("- Name: Charlie");
|
|
428
|
+
expect(dynamic).not.toContain("Apply this context quietly.");
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test("userPersona is included independently of onboarding context", () => {
|
|
432
|
+
// No BOOTSTRAP.md — the durable persona path after bootstrap is deleted
|
|
433
|
+
const personaContent =
|
|
434
|
+
"# User Persona\n\nPrefers concise answers. Works in fintech.";
|
|
435
|
+
|
|
436
|
+
const result = buildSystemPrompt({
|
|
437
|
+
userPersona: personaContent,
|
|
438
|
+
// No onboardingContext — simulates post-onboarding conversation
|
|
439
|
+
});
|
|
440
|
+
const dynamic = dynamicBlock(result);
|
|
441
|
+
|
|
442
|
+
// Persona content appears in prompt even without bootstrap or onboarding
|
|
443
|
+
expect(dynamic).toContain("# User Persona");
|
|
444
|
+
expect(dynamic).toContain("Prefers concise answers. Works in fintech.");
|
|
445
|
+
|
|
446
|
+
// No onboarding section should be present
|
|
447
|
+
expect(dynamic).not.toContain("## First-Run User Context");
|
|
448
|
+
expect(dynamic).not.toContain("First-Run Ritual");
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
test("userPersona appears alongside onboarding context during first run", () => {
|
|
452
|
+
writeFileSync(
|
|
453
|
+
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
454
|
+
"# Bootstrap\n\nOnboarding flow.",
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
const personaContent =
|
|
458
|
+
"# User Persona\n\nEarly-stage startup founder. Likes bullet points.";
|
|
459
|
+
const context: OnboardingContext = {
|
|
460
|
+
tools: ["slack"],
|
|
461
|
+
tasks: ["writing"],
|
|
462
|
+
tone: "warm",
|
|
463
|
+
userName: "Dana",
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const result = buildSystemPrompt({
|
|
467
|
+
userPersona: personaContent,
|
|
468
|
+
onboardingContext: context,
|
|
469
|
+
});
|
|
470
|
+
const dynamic = dynamicBlock(result);
|
|
471
|
+
|
|
472
|
+
// Both persona and onboarding context appear
|
|
473
|
+
expect(dynamic).toContain("# User Persona");
|
|
474
|
+
expect(dynamic).toContain("Likes bullet points.");
|
|
475
|
+
expect(dynamic).toContain("## First-Run User Context");
|
|
476
|
+
expect(dynamic).toContain("- Name: Dana");
|
|
477
|
+
expect(dynamic).toContain("- Daily tools: Slack");
|
|
478
|
+
});
|
|
479
|
+
});
|
|
293
480
|
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
isProviderSafeToolName,
|
|
5
|
+
toProviderSafeToolName,
|
|
6
|
+
} from "../tools/provider-tool-name.js";
|
|
7
|
+
|
|
8
|
+
describe("provider tool names", () => {
|
|
9
|
+
test("leaves already-safe names unchanged", () => {
|
|
10
|
+
expect(toProviderSafeToolName("deploy")).toBe("deploy");
|
|
11
|
+
expect(isProviderSafeToolName("deploy")).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("preserves raw-name identity for names that differ by edge whitespace", () => {
|
|
15
|
+
const plain = toProviderSafeToolName("deploy");
|
|
16
|
+
const padded = toProviderSafeToolName(" deploy ");
|
|
17
|
+
|
|
18
|
+
expect(plain).toBe("deploy");
|
|
19
|
+
expect(padded).toMatch(/^deploy__[a-f0-9]{12}$/);
|
|
20
|
+
expect(padded).not.toBe(plain);
|
|
21
|
+
expect(isProviderSafeToolName(padded)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -362,11 +362,22 @@ function getLatestAssistantText(conversationId: string): string | null {
|
|
|
362
362
|
if (Array.isArray(parsed)) {
|
|
363
363
|
return parsed
|
|
364
364
|
.filter(
|
|
365
|
-
(block): block is {
|
|
366
|
-
|
|
365
|
+
(block): block is {
|
|
366
|
+
type: string;
|
|
367
|
+
text?: string;
|
|
368
|
+
surfaceType?: string;
|
|
369
|
+
data?: { summaryText?: string };
|
|
370
|
+
} => typeof block === "object" && block != null,
|
|
367
371
|
)
|
|
368
|
-
.
|
|
369
|
-
|
|
372
|
+
.map((block) => {
|
|
373
|
+
if (block.type === "text") return block.text ?? "";
|
|
374
|
+
if (
|
|
375
|
+
block.type === "ui_surface" &&
|
|
376
|
+
block.surfaceType === "call_summary"
|
|
377
|
+
)
|
|
378
|
+
return block.data?.summaryText ?? "";
|
|
379
|
+
return "";
|
|
380
|
+
})
|
|
370
381
|
.join("");
|
|
371
382
|
}
|
|
372
383
|
if (typeof parsed === "string") return parsed;
|
|
@@ -135,12 +135,10 @@ describe("SSE assistant-events endpoint", () => {
|
|
|
135
135
|
// Read the first frame directly from the stream.
|
|
136
136
|
const reader = stream.getReader();
|
|
137
137
|
|
|
138
|
-
// The first chunk is the immediate heartbeat
|
|
138
|
+
// The first chunk is the immediate heartbeat comment enqueued in start().
|
|
139
139
|
const initial = await reader.read();
|
|
140
140
|
expect(initial.done).toBe(false);
|
|
141
|
-
|
|
142
|
-
expect(initialText).toContain(": heartbeat");
|
|
143
|
-
expect(initialText).toContain('{"type":"heartbeat"}');
|
|
141
|
+
expect(new TextDecoder().decode(initial.value)).toBe(": heartbeat\n\n");
|
|
144
142
|
|
|
145
143
|
// The second chunk is the actual assistant event.
|
|
146
144
|
const { value, done } = await reader.read();
|
|
@@ -172,12 +170,10 @@ describe("SSE assistant-events endpoint", () => {
|
|
|
172
170
|
|
|
173
171
|
const reader = stream.getReader();
|
|
174
172
|
|
|
175
|
-
// Consume the initial heartbeat
|
|
173
|
+
// Consume the initial heartbeat.
|
|
176
174
|
const heartbeat = await reader.read();
|
|
177
175
|
expect(heartbeat.done).toBe(false);
|
|
178
|
-
|
|
179
|
-
expect(heartbeatText).toContain(": heartbeat");
|
|
180
|
-
expect(heartbeatText).toContain('{"type":"heartbeat"}');
|
|
176
|
+
expect(new TextDecoder().decode(heartbeat.value)).toBe(": heartbeat\n\n");
|
|
181
177
|
|
|
182
178
|
// Publish events with two different conversationIds.
|
|
183
179
|
const eventA = buildAssistantEvent({ type: "pong" }, "conversation-aaa");
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
mock.module("../util/logger.js", () => ({
|
|
4
|
+
getLogger: () => ({
|
|
5
|
+
info: () => {},
|
|
6
|
+
debug: () => {},
|
|
7
|
+
warn: () => {},
|
|
8
|
+
error: () => {},
|
|
9
|
+
}),
|
|
10
|
+
truncateForLog: (value: string) => value,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
mock.module("../runtime/agent-wake.js", () => ({
|
|
14
|
+
wakeAgentForOpportunity: mock(() =>
|
|
15
|
+
Promise.resolve({ invoked: true, producedToolCalls: false }),
|
|
16
|
+
),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
mock.module("../home/emit-feed-event.js", () => ({
|
|
20
|
+
emitFeedEvent: mock(() => Promise.resolve()),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
mock.module("../config/loader.js", () => ({
|
|
24
|
+
getConfig: () => ({}),
|
|
25
|
+
loadConfig: () => ({}),
|
|
26
|
+
loadRawConfig: () => ({}),
|
|
27
|
+
saveRawConfig: () => {},
|
|
28
|
+
getConfigReadOnly: () => ({}),
|
|
29
|
+
applyNestedDefaults: (config: unknown) => config,
|
|
30
|
+
deepMergeOverwrite: (base: unknown) => base,
|
|
31
|
+
mergeDefaultWorkspaceConfig: () => {},
|
|
32
|
+
getNestedValue: () => undefined,
|
|
33
|
+
setNestedValue: () => {},
|
|
34
|
+
API_KEY_PROVIDERS: [],
|
|
35
|
+
_appendQuarantineBulletin: () => {},
|
|
36
|
+
invalidateConfigCache: () => {},
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
let locked = true;
|
|
40
|
+
mock.module("../daemon/disk-pressure-background-gate.js", () => ({
|
|
41
|
+
checkDiskPressureBackgroundGate: () =>
|
|
42
|
+
locked
|
|
43
|
+
? {
|
|
44
|
+
action: "skip",
|
|
45
|
+
reason: "disk_pressure",
|
|
46
|
+
blockedCapability: "background-work",
|
|
47
|
+
status: {
|
|
48
|
+
enabled: true,
|
|
49
|
+
state: "critical",
|
|
50
|
+
locked: true,
|
|
51
|
+
acknowledged: true,
|
|
52
|
+
overrideActive: false,
|
|
53
|
+
effectivelyLocked: true,
|
|
54
|
+
lockId: "disk-pressure-test",
|
|
55
|
+
usagePercent: 98,
|
|
56
|
+
thresholdPercent: 95,
|
|
57
|
+
path: "/",
|
|
58
|
+
lastCheckedAt: "2026-05-05T00:00:00.000Z",
|
|
59
|
+
blockedCapabilities: [
|
|
60
|
+
"agent-turns",
|
|
61
|
+
"background-work",
|
|
62
|
+
"remote-ingress",
|
|
63
|
+
],
|
|
64
|
+
error: null,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
: {
|
|
68
|
+
action: "allow",
|
|
69
|
+
status: {
|
|
70
|
+
enabled: false,
|
|
71
|
+
state: "disabled",
|
|
72
|
+
locked: false,
|
|
73
|
+
acknowledged: false,
|
|
74
|
+
overrideActive: false,
|
|
75
|
+
effectivelyLocked: false,
|
|
76
|
+
lockId: null,
|
|
77
|
+
usagePercent: null,
|
|
78
|
+
thresholdPercent: 95,
|
|
79
|
+
path: null,
|
|
80
|
+
lastCheckedAt: null,
|
|
81
|
+
blockedCapabilities: [],
|
|
82
|
+
error: null,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
diskPressureBackgroundSkipLogFields: () => ({
|
|
86
|
+
reason: "disk_pressure",
|
|
87
|
+
thresholdPercent: 95,
|
|
88
|
+
usagePercent: 98,
|
|
89
|
+
blockedCapability: "background-work",
|
|
90
|
+
lockId: "disk-pressure-test",
|
|
91
|
+
path: "/",
|
|
92
|
+
}),
|
|
93
|
+
shouldLogDiskPressureBackgroundSkip: () => true,
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
import { getDb } from "../memory/db-connection.js";
|
|
97
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
98
|
+
import { createSchedule } from "../schedule/schedule-store.js";
|
|
99
|
+
import { runScheduleOnce } from "../schedule/scheduler.js";
|
|
100
|
+
|
|
101
|
+
initializeDb();
|
|
102
|
+
|
|
103
|
+
function rawDb(): import("bun:sqlite").Database {
|
|
104
|
+
return (getDb() as unknown as { $client: import("bun:sqlite").Database })
|
|
105
|
+
.$client;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
describe("scheduler disk pressure gate", () => {
|
|
109
|
+
beforeEach(() => {
|
|
110
|
+
locked = true;
|
|
111
|
+
const db = getDb();
|
|
112
|
+
db.run("DELETE FROM cron_runs");
|
|
113
|
+
db.run("DELETE FROM cron_jobs");
|
|
114
|
+
db.run("DELETE FROM task_runs");
|
|
115
|
+
db.run("DELETE FROM tasks");
|
|
116
|
+
db.run("DELETE FROM messages");
|
|
117
|
+
db.run("DELETE FROM conversations");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("skips before claiming due schedules while disk pressure is locked", async () => {
|
|
121
|
+
const dueAt = Date.now() - 10_000;
|
|
122
|
+
const schedule = createSchedule({
|
|
123
|
+
name: "Due reminder",
|
|
124
|
+
message: "Do not fire while locked",
|
|
125
|
+
mode: "notify",
|
|
126
|
+
nextRunAt: dueAt,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const processMessage = mock(() => Promise.resolve());
|
|
130
|
+
const notify = mock(() => Promise.resolve());
|
|
131
|
+
|
|
132
|
+
const processed = await runScheduleOnce(processMessage, notify);
|
|
133
|
+
|
|
134
|
+
expect(processed).toBe(0);
|
|
135
|
+
expect(processMessage).not.toHaveBeenCalled();
|
|
136
|
+
expect(notify).not.toHaveBeenCalled();
|
|
137
|
+
|
|
138
|
+
const row = rawDb()
|
|
139
|
+
.query("SELECT status, next_run_at FROM cron_jobs WHERE id = ?")
|
|
140
|
+
.get(schedule.id) as { status: string; next_run_at: number } | null;
|
|
141
|
+
expect(row).toEqual({ status: "active", next_run_at: dueAt });
|
|
142
|
+
|
|
143
|
+
const runCount = rawDb()
|
|
144
|
+
.query("SELECT COUNT(*) AS count FROM cron_runs")
|
|
145
|
+
.get() as { count: number };
|
|
146
|
+
expect(runCount.count).toBe(0);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -106,7 +106,6 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
106
106
|
provenanceFromTrustContext: () => undefined,
|
|
107
107
|
setConversationOriginChannelIfUnset: () => {},
|
|
108
108
|
setConversationOriginInterfaceIfUnset: () => {},
|
|
109
|
-
getConversationMemoryScopeId: () => undefined,
|
|
110
109
|
}));
|
|
111
110
|
|
|
112
111
|
mock.module("../runtime/local-actor-identity.js", () => ({
|
|
@@ -561,6 +561,52 @@ describe("GET /v1/suggestion", () => {
|
|
|
561
561
|
expect(options?.config?.callSite).toBe("conversationStarters");
|
|
562
562
|
});
|
|
563
563
|
|
|
564
|
+
test("disables thinking and zeros effort to avoid Anthropic temp/thinking 400", async () => {
|
|
565
|
+
// Regression guard: this call hardcodes `temperature: 0.7` for response
|
|
566
|
+
// variety. Anthropic 400s on `temperature` ≠ 1 whenever thinking is
|
|
567
|
+
// enabled or in adaptive mode, so any user profile that resolves
|
|
568
|
+
// thinking-enabled (Opus 4.x at `effort: high|xhigh`, etc.) would fail
|
|
569
|
+
// unless we explicitly opt out of thinking on this call site.
|
|
570
|
+
//
|
|
571
|
+
// Pinning `thinking: { type: "disabled" }` and `effort: "none"` ensures
|
|
572
|
+
// the call works on every profile shape. A 60-token reply chip doesn't
|
|
573
|
+
// benefit from extended thinking anyway.
|
|
574
|
+
const provider = makeMockProvider("Quick reply");
|
|
575
|
+
mockGetConfiguredProvider.mockImplementation(async () => provider);
|
|
576
|
+
mockGetConversationByKey.mockImplementation(() => ({
|
|
577
|
+
conversationId: "conv-test",
|
|
578
|
+
}));
|
|
579
|
+
mockGetMessages.mockImplementation(() => [
|
|
580
|
+
{
|
|
581
|
+
id: "msg-asst-thinking",
|
|
582
|
+
conversationId: "conv-test",
|
|
583
|
+
role: "assistant",
|
|
584
|
+
content: JSON.stringify([{ type: "text", text: "Hello!" }]),
|
|
585
|
+
createdAt: Date.now(),
|
|
586
|
+
metadata: null,
|
|
587
|
+
},
|
|
588
|
+
]);
|
|
589
|
+
|
|
590
|
+
const args = makeArgs({ conversationKey: "test-key" });
|
|
591
|
+
const deps = makeDeps();
|
|
592
|
+
await handleGetSuggestion(args, deps);
|
|
593
|
+
|
|
594
|
+
expect(provider.sendMessage).toHaveBeenCalledTimes(1);
|
|
595
|
+
const callArgs = provider.sendMessage.mock.calls[0] as unknown[];
|
|
596
|
+
const options = callArgs[3] as
|
|
597
|
+
| {
|
|
598
|
+
config?: {
|
|
599
|
+
temperature?: number;
|
|
600
|
+
thinking?: { type?: string };
|
|
601
|
+
effort?: string;
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
| undefined;
|
|
605
|
+
expect(options?.config?.temperature).toBe(0.7);
|
|
606
|
+
expect(options?.config?.thinking).toEqual({ type: "disabled" });
|
|
607
|
+
expect(options?.config?.effort).toBe("none");
|
|
608
|
+
});
|
|
609
|
+
|
|
564
610
|
test("does not send an assistant-role prefill message", async () => {
|
|
565
611
|
// Regression guard: Anthropic rejects assistant-message prefill
|
|
566
612
|
// whenever the request triggers extended thinking (e.g. Opus 4.x at
|
|
@@ -49,7 +49,7 @@ describe("Twilio validation middleware", () => {
|
|
|
49
49
|
};
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
test("validates signatures against
|
|
52
|
+
test("validates signatures against configured public ingress", async () => {
|
|
53
53
|
mockConfig = {
|
|
54
54
|
ingress: {
|
|
55
55
|
publicBaseUrl: " https://twilio.example.com/// ",
|
|
@@ -77,7 +77,7 @@ describe("Twilio validation middleware", () => {
|
|
|
77
77
|
]);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
-
test("
|
|
80
|
+
test("uses configured public ingress for status callbacks", async () => {
|
|
81
81
|
const req = new Request("http://127.0.0.1:7821/v1/calls/twilio/status", {
|
|
82
82
|
method: "POST",
|
|
83
83
|
headers: { "x-twilio-signature": "valid" },
|