@vellumai/assistant 0.4.49 → 0.4.51
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 +24 -33
- package/README.md +3 -3
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/keychain-broker.md +6 -6
- package/docs/architecture/memory.md +180 -119
- package/knip.json +32 -0
- package/package.json +3 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/btw-routes.test.ts +61 -5
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +8 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +8 -7
- package/src/__tests__/credential-vault-unit.test.ts +23 -18
- package/src/__tests__/credential-vault.test.ts +30 -18
- package/src/__tests__/credentials-cli.test.ts +257 -82
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
- package/src/__tests__/integration-status.test.ts +31 -30
- package/src/__tests__/invite-redemption-service.test.ts +166 -13
- package/src/__tests__/invite-routes-http.test.ts +166 -5
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/list-messages-attachments.test.ts +193 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +824 -31
- package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +363 -17
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +55 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
- package/src/__tests__/secure-keys.test.ts +78 -18
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills.test.ts +2 -2
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +10 -8
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/twilio-config.test.ts +11 -10
- package/src/__tests__/twilio-provider.test.ts +9 -4
- package/src/__tests__/voice-invite-redemption.test.ts +85 -5
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +134 -3
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +44 -6
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +5 -4
- package/src/calls/twilio-provider.ts +14 -9
- package/src/calls/twilio-rest.ts +10 -7
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/config.ts +14 -9
- package/src/cli/commands/contacts.ts +3 -0
- package/src/cli/commands/credentials.ts +170 -174
- package/src/cli/commands/doctor.ts +11 -8
- package/src/cli/commands/keys.ts +9 -9
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +68 -10
- package/src/cli/commands/oauth/connections.ts +475 -105
- package/src/cli/commands/oauth/index.ts +3 -3
- package/src/cli/commands/oauth/providers.ts +18 -4
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +20 -22
- package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-skills/contacts/SKILL.md +35 -11
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +1 -1
- package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
- package/src/config/bundled-skills/messaging/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
- package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/loader.ts +6 -42
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/contacts/contact-store.ts +39 -2
- package/src/contacts/contacts-write.ts +9 -0
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +55 -2
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +59 -39
- package/src/daemon/handlers/config-telegram.ts +23 -14
- package/src/daemon/handlers/session-history.ts +1 -358
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/shared.ts +3 -17
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +39 -4
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -42
- package/src/daemon/server.ts +6 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +3 -5
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/email/providers/index.ts +2 -2
- package/src/instrument.ts +61 -1
- package/src/media/avatar-router.ts +1 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +25 -83
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/invite-store.ts +19 -0
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/157-invite-contact-id.ts +104 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +1 -1
- package/src/messaging/providers/gmail/adapter.ts +1 -1
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
- package/src/messaging/providers/whatsapp/adapter.ts +13 -9
- package/src/messaging/registry.ts +9 -5
- package/src/oauth/byo-connection.test.ts +40 -25
- package/src/oauth/connect-orchestrator.ts +4 -10
- package/src/oauth/connection-resolver.ts +20 -6
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +183 -31
- package/src/oauth/platform-connection.test.ts +1 -1
- package/src/oauth/provider-behaviors.ts +503 -4
- package/src/oauth/seed-providers.ts +214 -8
- package/src/oauth/token-persistence.ts +31 -16
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/channel-readiness-service.ts +48 -40
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/invite-redemption-service.ts +72 -12
- package/src/runtime/invite-service.ts +43 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -1
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/btw-routes.ts +10 -5
- package/src/runtime/routes/conversation-routes.ts +56 -11
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/integrations/slack/channel.ts +2 -2
- package/src/runtime/routes/integrations/telegram.ts +2 -2
- package/src/runtime/routes/integrations/twilio.ts +17 -17
- package/src/runtime/routes/invite-routes.ts +29 -4
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/secret-routes.ts +17 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +3 -3
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +9 -4
- package/src/runtime/routes/workspace-utils.ts +8 -2
- package/src/schedule/integration-status.ts +26 -19
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/oauth2.ts +6 -7
- package/src/security/secure-keys.ts +44 -19
- package/src/security/token-manager.ts +46 -39
- package/src/services/vercel-deploy.ts +0 -24
- package/src/signals/confirm.ts +78 -0
- package/src/signals/mcp-reload.ts +18 -0
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +22 -7
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/network/script-proxy/session-manager.ts +8 -8
- package/src/tools/schedule/create.ts +10 -3
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/watcher/provider-types.ts +1 -1
- package/src/watcher/providers/github.ts +1 -1
- package/src/watcher/providers/gmail.ts +3 -3
- package/src/watcher/providers/google-calendar.ts +3 -3
- package/src/watcher/providers/linear.ts +1 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
|
@@ -392,8 +392,8 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
392
392
|
expect(user.content[1].cache_control).toEqual({ type: "ephemeral" });
|
|
393
393
|
});
|
|
394
394
|
|
|
395
|
-
test("workspace +
|
|
396
|
-
// Simulates workspace prepended +
|
|
395
|
+
test("workspace + multi-block user message: cache still lands on trailing block", async () => {
|
|
396
|
+
// Simulates workspace prepended + extra context block appended
|
|
397
397
|
const injectedUser: Message = {
|
|
398
398
|
role: "user",
|
|
399
399
|
content: [
|
|
@@ -670,7 +670,9 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
670
670
|
// ensureToolPairing — server_tool_use / web_search_tool_result pairing
|
|
671
671
|
// -----------------------------------------------------------------------
|
|
672
672
|
|
|
673
|
-
test("server_tool_use
|
|
673
|
+
test("server_tool_use without web_search_tool_result passes through as-is (no synthetic injection)", async () => {
|
|
674
|
+
// Server-side tools are self-paired within the assistant message.
|
|
675
|
+
// ensureToolPairing should NOT inject synthetic results for them.
|
|
674
676
|
const messages: Message[] = [
|
|
675
677
|
userMsg("Search for something"),
|
|
676
678
|
{
|
|
@@ -684,7 +686,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
684
686
|
},
|
|
685
687
|
],
|
|
686
688
|
},
|
|
687
|
-
userMsg("Thanks"),
|
|
689
|
+
userMsg("Thanks"),
|
|
688
690
|
];
|
|
689
691
|
await provider.sendMessage(messages);
|
|
690
692
|
|
|
@@ -697,14 +699,17 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
697
699
|
}>;
|
|
698
700
|
}>;
|
|
699
701
|
|
|
700
|
-
//
|
|
701
|
-
|
|
702
|
-
expect(
|
|
703
|
-
expect(
|
|
704
|
-
expect(
|
|
702
|
+
// server_tool_use stays in the assistant message, no synthetic result injected
|
|
703
|
+
expect(sent).toHaveLength(3);
|
|
704
|
+
expect(sent[1].role).toBe("assistant");
|
|
705
|
+
expect(sent[1].content[0].type).toBe("server_tool_use");
|
|
706
|
+
expect(sent[2].role).toBe("user");
|
|
707
|
+
expect(sent[2].content[0].type).toBe("text"); // original user text, not a synthetic result
|
|
705
708
|
});
|
|
706
709
|
|
|
707
|
-
test("server_tool_use at end of messages
|
|
710
|
+
test("server_tool_use at end of messages is not modified (no synthetic append)", async () => {
|
|
711
|
+
// Server-side tools don't need cross-message pairing, so no synthetic
|
|
712
|
+
// user message should be appended.
|
|
708
713
|
const messages: Message[] = [
|
|
709
714
|
userMsg("Search something"),
|
|
710
715
|
{
|
|
@@ -726,11 +731,10 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
726
731
|
content: Array<{ type: string; tool_use_id?: string }>;
|
|
727
732
|
}>;
|
|
728
733
|
|
|
729
|
-
//
|
|
730
|
-
expect(sent).toHaveLength(
|
|
731
|
-
expect(sent[
|
|
732
|
-
expect(sent[
|
|
733
|
-
expect(sent[2].content[0].tool_use_id).toBe("srvtoolu_end");
|
|
734
|
+
// No synthetic user message appended — just the original 2 messages
|
|
735
|
+
expect(sent).toHaveLength(2);
|
|
736
|
+
expect(sent[1].role).toBe("assistant");
|
|
737
|
+
expect(sent[1].content[0].type).toBe("server_tool_use");
|
|
734
738
|
});
|
|
735
739
|
|
|
736
740
|
test("server_tool_use with matching web_search_tool_result passes through unchanged", async () => {
|
|
@@ -781,7 +785,85 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
781
785
|
expect(resultBlocks[0].tool_use_id).toBe("srvtoolu_ok");
|
|
782
786
|
});
|
|
783
787
|
|
|
784
|
-
test("
|
|
788
|
+
test("server_tool_use + web_search_tool_result + tool_use in same assistant message stays intact", async () => {
|
|
789
|
+
// This is the core bug scenario: Anthropic returns server_tool_use,
|
|
790
|
+
// web_search_tool_result, text, and tool_use all in one assistant message.
|
|
791
|
+
// The server pair must stay together in the assistant message.
|
|
792
|
+
const messages: Message[] = [
|
|
793
|
+
userMsg("Search and fetch"),
|
|
794
|
+
{
|
|
795
|
+
role: "assistant",
|
|
796
|
+
content: [
|
|
797
|
+
{
|
|
798
|
+
type: "server_tool_use",
|
|
799
|
+
id: "srvtoolu_search",
|
|
800
|
+
name: "web_search",
|
|
801
|
+
input: { query: "test" },
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
type: "web_search_tool_result",
|
|
805
|
+
tool_use_id: "srvtoolu_search",
|
|
806
|
+
content: [
|
|
807
|
+
{
|
|
808
|
+
type: "web_search_result",
|
|
809
|
+
url: "https://example.com",
|
|
810
|
+
title: "Example",
|
|
811
|
+
encrypted_content: "enc_123",
|
|
812
|
+
},
|
|
813
|
+
],
|
|
814
|
+
},
|
|
815
|
+
{ type: "text", text: "Based on the search results..." },
|
|
816
|
+
{
|
|
817
|
+
type: "tool_use",
|
|
818
|
+
id: "tu_fetch",
|
|
819
|
+
name: "fetch_url",
|
|
820
|
+
input: { url: "https://example.com" },
|
|
821
|
+
},
|
|
822
|
+
],
|
|
823
|
+
},
|
|
824
|
+
toolResultMsg("tu_fetch", "page content here"),
|
|
825
|
+
];
|
|
826
|
+
await provider.sendMessage(messages);
|
|
827
|
+
|
|
828
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
829
|
+
role: string;
|
|
830
|
+
content: Array<{
|
|
831
|
+
type: string;
|
|
832
|
+
id?: string;
|
|
833
|
+
tool_use_id?: string;
|
|
834
|
+
}>;
|
|
835
|
+
}>;
|
|
836
|
+
|
|
837
|
+
// The server_tool_use pair (server_tool_use + web_search_tool_result) should
|
|
838
|
+
// be in the leading portion of the assistant message, before tool_use.
|
|
839
|
+
// splitAssistantForToolPairing: leading=[server_tool_use, web_search_tool_result, text],
|
|
840
|
+
// toolUseBlocks=[tool_use], carryover=[]
|
|
841
|
+
const assistantMsg = sent[1];
|
|
842
|
+
expect(assistantMsg.role).toBe("assistant");
|
|
843
|
+
const blockTypes = assistantMsg.content.map((b) => b.type);
|
|
844
|
+
expect(blockTypes).toContain("server_tool_use");
|
|
845
|
+
expect(blockTypes).toContain("web_search_tool_result");
|
|
846
|
+
expect(blockTypes).toContain("tool_use");
|
|
847
|
+
|
|
848
|
+
// The tool_result for the client-side tool_use should be in the user message
|
|
849
|
+
const userMsg2 = sent[2];
|
|
850
|
+
expect(userMsg2.role).toBe("user");
|
|
851
|
+
expect(
|
|
852
|
+
userMsg2.content.some(
|
|
853
|
+
(b) => b.type === "tool_result" && b.tool_use_id === "tu_fetch",
|
|
854
|
+
),
|
|
855
|
+
).toBe(true);
|
|
856
|
+
|
|
857
|
+
// No synthetic web_search_tool_result injected anywhere
|
|
858
|
+
const allBlocks = sent.flatMap((m) => m.content);
|
|
859
|
+
const webSearchResults = allBlocks.filter(
|
|
860
|
+
(b) => b.type === "web_search_tool_result",
|
|
861
|
+
);
|
|
862
|
+
expect(webSearchResults).toHaveLength(1); // only the original one
|
|
863
|
+
expect(webSearchResults[0].tool_use_id).toBe("srvtoolu_search");
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
test("mixed tool_use and server_tool_use — only client-side tool_use gets pairing, server tools pass through", async () => {
|
|
785
867
|
const messages: Message[] = [
|
|
786
868
|
userMsg("Do things"),
|
|
787
869
|
{
|
|
@@ -796,7 +878,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
796
878
|
},
|
|
797
879
|
],
|
|
798
880
|
},
|
|
799
|
-
// Only tu_a has a result
|
|
881
|
+
// Only tu_a has a result — server_tool_use doesn't need one in the user message
|
|
800
882
|
toolResultMsg("tu_a", "result A"),
|
|
801
883
|
];
|
|
802
884
|
await provider.sendMessage(messages);
|
|
@@ -806,20 +888,29 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
806
888
|
content: Array<{
|
|
807
889
|
type: string;
|
|
808
890
|
tool_use_id?: string;
|
|
891
|
+
id?: string;
|
|
809
892
|
}>;
|
|
810
893
|
}>;
|
|
811
894
|
|
|
895
|
+
// Assistant message should have tool_use in paired portion, server_tool_use in carryover
|
|
896
|
+
// ensureToolPairing splits: paired = [tool_use(tu_a)], carryover = [server_tool_use(srvtoolu_b)]
|
|
897
|
+
// Result: assistant(tool_use) → user(tool_result) → assistant(server_tool_use) → user(continue)
|
|
898
|
+
const assistantMsg = sent[1];
|
|
899
|
+
expect(assistantMsg.role).toBe("assistant");
|
|
900
|
+
expect(assistantMsg.content[0].type).toBe("tool_use");
|
|
901
|
+
|
|
812
902
|
const userAfterAssistant = sent[2];
|
|
813
|
-
|
|
814
|
-
|
|
903
|
+
expect(userAfterAssistant.role).toBe("user");
|
|
904
|
+
// Only tool_result for tu_a — no synthetic web_search_tool_result
|
|
815
905
|
expect(userAfterAssistant.content[0]).toMatchObject({
|
|
816
906
|
type: "tool_result",
|
|
817
907
|
tool_use_id: "tu_a",
|
|
818
908
|
});
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
909
|
+
|
|
910
|
+
// server_tool_use preserved in a carryover assistant message
|
|
911
|
+
const carryoverAssistant = sent[3];
|
|
912
|
+
expect(carryoverAssistant.role).toBe("assistant");
|
|
913
|
+
expect(carryoverAssistant.content[0].type).toBe("server_tool_use");
|
|
823
914
|
});
|
|
824
915
|
|
|
825
916
|
test("assistant message with only unknown blocks gets placeholder text", async () => {
|
|
@@ -81,7 +81,6 @@ mock.module("../config/loader.js", () => ({
|
|
|
81
81
|
timeouts: { permissionTimeoutSec: 300 },
|
|
82
82
|
apiKeys: {},
|
|
83
83
|
skills: { entries: {}, allowBundled: true },
|
|
84
|
-
memory: { retrieval: { injectionStrategy: "inline" } },
|
|
85
84
|
permissions: { mode: "workspace" },
|
|
86
85
|
}),
|
|
87
86
|
loadRawConfig: () => ({}),
|
|
@@ -130,18 +129,6 @@ mock.module("../security/secret-allowlist.js", () => ({
|
|
|
130
129
|
resetAllowlist: () => {},
|
|
131
130
|
}));
|
|
132
131
|
|
|
133
|
-
mock.module("../memory/admin.js", () => ({
|
|
134
|
-
getMemoryConflictAndCleanupStats: () => ({
|
|
135
|
-
conflicts: { pending: 0, resolved: 0, oldestPendingAgeMs: null },
|
|
136
|
-
cleanup: {
|
|
137
|
-
resolvedBacklog: 0,
|
|
138
|
-
supersededBacklog: 0,
|
|
139
|
-
resolvedCompleted24h: 0,
|
|
140
|
-
supersededCompleted24h: 0,
|
|
141
|
-
},
|
|
142
|
-
}),
|
|
143
|
-
}));
|
|
144
|
-
|
|
145
132
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
146
133
|
getConversationThreadType: () => "default",
|
|
147
134
|
setConversationOriginChannelIfUnset: () => {},
|
|
@@ -182,13 +169,12 @@ mock.module("../memory/retriever.js", () => ({
|
|
|
182
169
|
enabled: false,
|
|
183
170
|
degraded: false,
|
|
184
171
|
injectedText: "",
|
|
185
|
-
|
|
172
|
+
|
|
186
173
|
semanticHits: 0,
|
|
187
174
|
recencyHits: 0,
|
|
188
175
|
injectedTokens: 0,
|
|
189
176
|
latencyMs: 0,
|
|
190
177
|
}),
|
|
191
|
-
injectMemoryRecallIntoUserMessage: (msg: Message) => msg,
|
|
192
178
|
stripMemoryRecallMessages: (msgs: Message[]) => msgs,
|
|
193
179
|
}));
|
|
194
180
|
|
|
@@ -119,6 +119,7 @@ function makeIdleSession(opts?: {
|
|
|
119
119
|
setHostBashProxy: () => {},
|
|
120
120
|
setHostFileProxy: () => {},
|
|
121
121
|
setHostCuProxy: () => {},
|
|
122
|
+
addPreactivatedSkillId: () => {},
|
|
122
123
|
enqueueMessage: () => ({ queued: false, requestId: "noop" }),
|
|
123
124
|
hasAnyPendingConfirmation: () => false,
|
|
124
125
|
runAgentLoop: async (
|
|
@@ -183,6 +184,7 @@ function makeConfirmationEmittingSession(opts?: {
|
|
|
183
184
|
setHostBashProxy: () => {},
|
|
184
185
|
setHostFileProxy: () => {},
|
|
185
186
|
setHostCuProxy: () => {},
|
|
187
|
+
addPreactivatedSkillId: () => {},
|
|
186
188
|
enqueueMessage: () => ({ queued: false, requestId: "noop" }),
|
|
187
189
|
hasAnyPendingConfirmation: () => false,
|
|
188
190
|
runAgentLoop: async (
|
|
@@ -170,29 +170,6 @@ describe("assistant feature flag guard", () => {
|
|
|
170
170
|
// Test: registry entries have required fields
|
|
171
171
|
// ---------------------------------------------------------------------------
|
|
172
172
|
|
|
173
|
-
// ---------------------------------------------------------------------------
|
|
174
|
-
// Test: bundled registry copy stays in sync with canonical meta/ copy
|
|
175
|
-
// ---------------------------------------------------------------------------
|
|
176
|
-
|
|
177
|
-
test("bundled assistant/src/config/feature-flag-registry.json matches canonical meta/ copy", () => {
|
|
178
|
-
const canonicalPath = getRegistryPath();
|
|
179
|
-
const bundledPath = join(
|
|
180
|
-
process.cwd(),
|
|
181
|
-
"src",
|
|
182
|
-
"config",
|
|
183
|
-
"feature-flag-registry.json",
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
const canonical = JSON.parse(readFileSync(canonicalPath, "utf-8"));
|
|
187
|
-
const bundled = JSON.parse(readFileSync(bundledPath, "utf-8"));
|
|
188
|
-
|
|
189
|
-
expect(bundled).toEqual(canonical);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
|
-
// Test: registry entries have required fields
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
|
|
196
173
|
test("all assistant-scope entries in the unified registry have required fields", () => {
|
|
197
174
|
const registry = loadRegistry();
|
|
198
175
|
const assistantFlags = registry.flags.filter(
|
|
@@ -19,13 +19,21 @@ mock.module("../util/logger.js", () => ({
|
|
|
19
19
|
}),
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
conversationId:
|
|
24
|
-
|
|
25
|
-
})
|
|
22
|
+
const mockGetConversationByKey = mock(
|
|
23
|
+
(_key: string): { conversationId: string } | null => ({
|
|
24
|
+
conversationId: "conv-test-123",
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
26
27
|
|
|
27
28
|
mock.module("../memory/conversation-key-store.js", () => ({
|
|
28
|
-
|
|
29
|
+
getConversationByKey: mockGetConversationByKey,
|
|
30
|
+
// Ensure getOrCreateConversation is never called — BTW must not create
|
|
31
|
+
// persistent conversations.
|
|
32
|
+
getOrCreateConversation: () => {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"getOrCreateConversation must not be called from btw-routes",
|
|
35
|
+
);
|
|
36
|
+
},
|
|
29
37
|
}));
|
|
30
38
|
|
|
31
39
|
const mockAddMessage = mock(() => {});
|
|
@@ -323,4 +331,52 @@ describe("POST /v1/btw", () => {
|
|
|
323
331
|
// processing should still be false — the handler never sets it
|
|
324
332
|
expect(session.processing).toBe(false);
|
|
325
333
|
});
|
|
334
|
+
|
|
335
|
+
// -- No conversation creation (regression) --
|
|
336
|
+
|
|
337
|
+
test("unknown conversationKey does not create a DB conversation", async () => {
|
|
338
|
+
// Simulate a greeting request for a draft thread — no conversation exists.
|
|
339
|
+
mockGetConversationByKey.mockReturnValueOnce(null);
|
|
340
|
+
|
|
341
|
+
const session = makeMockSession();
|
|
342
|
+
const deps = makeSendMessageDeps(session);
|
|
343
|
+
const getOrCreateSessionSpy = deps.getOrCreateSession as ReturnType<
|
|
344
|
+
typeof mock
|
|
345
|
+
>;
|
|
346
|
+
|
|
347
|
+
const res = await callHandler(
|
|
348
|
+
{ conversationKey: "greeting-abc123", content: "Generate a greeting" },
|
|
349
|
+
{ sendMessageDeps: deps },
|
|
350
|
+
);
|
|
351
|
+
await readStream(res);
|
|
352
|
+
|
|
353
|
+
expect(res.status).toBe(200);
|
|
354
|
+
|
|
355
|
+
// Read-only lookup should be called
|
|
356
|
+
expect(mockGetConversationByKey).toHaveBeenCalledWith("greeting-abc123");
|
|
357
|
+
|
|
358
|
+
// Session should be created with the raw key (no DB conversation created)
|
|
359
|
+
expect(getOrCreateSessionSpy).toHaveBeenCalledWith("greeting-abc123");
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("known conversationKey resolves to existing conversation ID", async () => {
|
|
363
|
+
mockGetConversationByKey.mockReturnValueOnce({
|
|
364
|
+
conversationId: "existing-conv-id",
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const session = makeMockSession();
|
|
368
|
+
const deps = makeSendMessageDeps(session);
|
|
369
|
+
const getOrCreateSessionSpy = deps.getOrCreateSession as ReturnType<
|
|
370
|
+
typeof mock
|
|
371
|
+
>;
|
|
372
|
+
|
|
373
|
+
const res = await callHandler(
|
|
374
|
+
{ conversationKey: "my-thread-key", content: "What is 2+2?" },
|
|
375
|
+
{ sendMessageDeps: deps },
|
|
376
|
+
);
|
|
377
|
+
await readStream(res);
|
|
378
|
+
|
|
379
|
+
expect(res.status).toBe(200);
|
|
380
|
+
expect(getOrCreateSessionSpy).toHaveBeenCalledWith("existing-conv-id");
|
|
381
|
+
});
|
|
326
382
|
});
|
|
@@ -26,11 +26,13 @@ mock.module("../util/logger.js", () => ({
|
|
|
26
26
|
import {
|
|
27
27
|
createCanonicalGuardianDelivery,
|
|
28
28
|
createCanonicalGuardianRequest,
|
|
29
|
+
expireAllPendingCanonicalRequests,
|
|
29
30
|
getCanonicalGuardianRequest,
|
|
30
31
|
listCanonicalGuardianDeliveries,
|
|
31
32
|
listCanonicalGuardianRequests,
|
|
32
33
|
listPendingCanonicalGuardianRequestsByDestinationChat,
|
|
33
34
|
listPendingCanonicalGuardianRequestsByDestinationConversation,
|
|
35
|
+
listPendingRequestsByConversationScope,
|
|
34
36
|
resolveCanonicalGuardianRequest,
|
|
35
37
|
updateCanonicalGuardianDelivery,
|
|
36
38
|
updateCanonicalGuardianRequest,
|
|
@@ -717,4 +719,97 @@ describe("canonical-guardian-store", () => {
|
|
|
717
719
|
);
|
|
718
720
|
expect(pending).toHaveLength(0);
|
|
719
721
|
});
|
|
722
|
+
|
|
723
|
+
// ── listPendingRequestsByConversationScope expiry filtering ─────────
|
|
724
|
+
|
|
725
|
+
test("listPendingRequestsByConversationScope excludes expired requests", () => {
|
|
726
|
+
// Create a pending request that has already expired
|
|
727
|
+
createCanonicalGuardianRequest({
|
|
728
|
+
kind: "tool_approval",
|
|
729
|
+
sourceType: "desktop",
|
|
730
|
+
conversationId: "conv-scope-1",
|
|
731
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
732
|
+
expiresAt: new Date(Date.now() - 10_000).toISOString(),
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
// Create a pending request that has not expired
|
|
736
|
+
const unexpired = createCanonicalGuardianRequest({
|
|
737
|
+
kind: "tool_approval",
|
|
738
|
+
sourceType: "desktop",
|
|
739
|
+
conversationId: "conv-scope-1",
|
|
740
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
741
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
const results = listPendingRequestsByConversationScope("conv-scope-1");
|
|
745
|
+
expect(results).toHaveLength(1);
|
|
746
|
+
expect(results[0].id).toBe(unexpired.id);
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
test("listPendingRequestsByConversationScope includes requests with no expiresAt", () => {
|
|
750
|
+
const noExpiry = createCanonicalGuardianRequest({
|
|
751
|
+
kind: "tool_approval",
|
|
752
|
+
sourceType: "desktop",
|
|
753
|
+
conversationId: "conv-scope-2",
|
|
754
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
const results = listPendingRequestsByConversationScope("conv-scope-2");
|
|
758
|
+
expect(results).toHaveLength(1);
|
|
759
|
+
expect(results[0].id).toBe(noExpiry.id);
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
// ── expireAllPendingCanonicalRequests ───────────────────────────────
|
|
763
|
+
|
|
764
|
+
test("expireAllPendingCanonicalRequests transitions all pending to expired", () => {
|
|
765
|
+
const req1 = createCanonicalGuardianRequest({
|
|
766
|
+
kind: "tool_approval",
|
|
767
|
+
sourceType: "desktop",
|
|
768
|
+
conversationId: "conv-bulk-1",
|
|
769
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
770
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
771
|
+
});
|
|
772
|
+
const req2 = createCanonicalGuardianRequest({
|
|
773
|
+
kind: "tool_approval",
|
|
774
|
+
sourceType: "channel",
|
|
775
|
+
conversationId: "conv-bulk-2",
|
|
776
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
777
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
const count = expireAllPendingCanonicalRequests();
|
|
781
|
+
expect(count).toBe(2);
|
|
782
|
+
|
|
783
|
+
expect(getCanonicalGuardianRequest(req1.id)!.status).toBe("expired");
|
|
784
|
+
expect(getCanonicalGuardianRequest(req2.id)!.status).toBe("expired");
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
test("expireAllPendingCanonicalRequests does not affect already-resolved requests", () => {
|
|
788
|
+
const approved = createCanonicalGuardianRequest({
|
|
789
|
+
kind: "tool_approval",
|
|
790
|
+
sourceType: "desktop",
|
|
791
|
+
conversationId: "conv-bulk-3",
|
|
792
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
793
|
+
});
|
|
794
|
+
updateCanonicalGuardianRequest(approved.id, { status: "approved" });
|
|
795
|
+
|
|
796
|
+
const denied = createCanonicalGuardianRequest({
|
|
797
|
+
kind: "tool_approval",
|
|
798
|
+
sourceType: "desktop",
|
|
799
|
+
conversationId: "conv-bulk-3",
|
|
800
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
801
|
+
});
|
|
802
|
+
updateCanonicalGuardianRequest(denied.id, { status: "denied" });
|
|
803
|
+
|
|
804
|
+
const count = expireAllPendingCanonicalRequests();
|
|
805
|
+
expect(count).toBe(0);
|
|
806
|
+
|
|
807
|
+
expect(getCanonicalGuardianRequest(approved.id)!.status).toBe("approved");
|
|
808
|
+
expect(getCanonicalGuardianRequest(denied.id)!.status).toBe("denied");
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
test("expireAllPendingCanonicalRequests returns 0 when no pending requests exist", () => {
|
|
812
|
+
const count = expireAllPendingCanonicalRequests();
|
|
813
|
+
expect(count).toBe(0);
|
|
814
|
+
});
|
|
720
815
|
});
|
|
@@ -895,6 +895,19 @@ describe("Permission Checker", () => {
|
|
|
895
895
|
);
|
|
896
896
|
});
|
|
897
897
|
|
|
898
|
+
test("computer_use_observe prompts by default via computer-use ask rule", async () => {
|
|
899
|
+
const result = await check(
|
|
900
|
+
"computer_use_observe",
|
|
901
|
+
{ reason: "Check current screen state before acting" },
|
|
902
|
+
"/tmp",
|
|
903
|
+
);
|
|
904
|
+
expect(result.decision).toBe("prompt");
|
|
905
|
+
expect(result.reason).toContain("ask rule");
|
|
906
|
+
expect(result.matchedRule?.id).toBe(
|
|
907
|
+
"default:ask-computer_use_observe-global",
|
|
908
|
+
);
|
|
909
|
+
});
|
|
910
|
+
|
|
898
911
|
test("higher-priority allow rule can override default computer-use ask rule", async () => {
|
|
899
912
|
addRule(
|
|
900
913
|
"computer_use_click",
|
|
@@ -161,60 +161,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
161
161
|
expect(result.secretDetection.action).toBe("block");
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
-
test("applies
|
|
165
|
-
const result = AssistantConfigSchema.parse({});
|
|
166
|
-
expect(result.memory.conflicts).toEqual({
|
|
167
|
-
enabled: true,
|
|
168
|
-
gateMode: "soft",
|
|
169
|
-
resolverLlmTimeoutMs: 12000,
|
|
170
|
-
relevanceThreshold: 0.3,
|
|
171
|
-
conflictableKinds: [
|
|
172
|
-
"preference",
|
|
173
|
-
"profile",
|
|
174
|
-
"constraint",
|
|
175
|
-
"instruction",
|
|
176
|
-
"style",
|
|
177
|
-
],
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test("rejects invalid memory.conflicts.relevanceThreshold", () => {
|
|
182
|
-
const result = AssistantConfigSchema.safeParse({
|
|
183
|
-
memory: { conflicts: { relevanceThreshold: 2 } },
|
|
184
|
-
});
|
|
185
|
-
expect(result.success).toBe(false);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test("rejects invalid memory.conflicts.conflictableKinds entry", () => {
|
|
189
|
-
const result = AssistantConfigSchema.safeParse({
|
|
190
|
-
memory: { conflicts: { conflictableKinds: ["invalid_kind"] } },
|
|
191
|
-
});
|
|
192
|
-
expect(result.success).toBe(false);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("rejects empty memory.conflicts.conflictableKinds", () => {
|
|
196
|
-
const result = AssistantConfigSchema.safeParse({
|
|
197
|
-
memory: { conflicts: { conflictableKinds: [] } },
|
|
198
|
-
});
|
|
199
|
-
expect(result.success).toBe(false);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("applies memory.profile defaults", () => {
|
|
203
|
-
const result = AssistantConfigSchema.parse({});
|
|
204
|
-
expect(result.memory.profile).toEqual({
|
|
205
|
-
enabled: true,
|
|
206
|
-
maxInjectTokens: 800,
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
test("rejects invalid memory.profile.maxInjectTokens", () => {
|
|
211
|
-
const result = AssistantConfigSchema.safeParse({
|
|
212
|
-
memory: { profile: { maxInjectTokens: 0 } },
|
|
213
|
-
});
|
|
214
|
-
expect(result.success).toBe(false);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test("applies rollout defaults for dynamic budget and entity relation features", () => {
|
|
164
|
+
test("applies rollout defaults for dynamic budget", () => {
|
|
218
165
|
const result = AssistantConfigSchema.parse({});
|
|
219
166
|
expect(result.memory.retrieval.dynamicBudget).toEqual({
|
|
220
167
|
enabled: true,
|
|
@@ -222,19 +169,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
222
169
|
maxInjectTokens: 10000,
|
|
223
170
|
targetHeadroomTokens: 10000,
|
|
224
171
|
});
|
|
225
|
-
expect(result.memory.entity.extractRelations).toEqual({
|
|
226
|
-
enabled: true,
|
|
227
|
-
backfillBatchSize: 200,
|
|
228
|
-
});
|
|
229
|
-
expect(result.memory.entity.relationRetrieval).toEqual({
|
|
230
|
-
enabled: true,
|
|
231
|
-
maxSeedEntities: 8,
|
|
232
|
-
maxNeighborEntities: 20,
|
|
233
|
-
maxEdges: 40,
|
|
234
|
-
neighborScoreMultiplier: 0.7,
|
|
235
|
-
maxDepth: 3,
|
|
236
|
-
depthDecay: true,
|
|
237
|
-
});
|
|
238
172
|
});
|
|
239
173
|
|
|
240
174
|
test("applies memory.cleanup defaults", () => {
|
|
@@ -242,7 +176,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
242
176
|
expect(result.memory.cleanup).toEqual({
|
|
243
177
|
enabled: true,
|
|
244
178
|
enqueueIntervalMs: 6 * 60 * 60 * 1000,
|
|
245
|
-
resolvedConflictRetentionMs: 30 * 24 * 60 * 60 * 1000,
|
|
246
179
|
supersededItemRetentionMs: 30 * 24 * 60 * 60 * 1000,
|
|
247
180
|
conversationRetentionDays: 90,
|
|
248
181
|
});
|
|
@@ -120,6 +120,14 @@ mock.module("../providers/registry.js", () => ({
|
|
|
120
120
|
initializeProviders: () => {},
|
|
121
121
|
}));
|
|
122
122
|
|
|
123
|
+
mock.module("../signals/mcp-reload.js", () => ({
|
|
124
|
+
handleMcpReloadSignal: () => {},
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
mock.module("../signals/confirm.js", () => ({
|
|
128
|
+
handleConfirmationSignal: () => {},
|
|
129
|
+
}));
|
|
130
|
+
|
|
123
131
|
let resetAllowlistCallCount = 0;
|
|
124
132
|
mock.module("../security/secret-allowlist.js", () => ({
|
|
125
133
|
resetAllowlist: () => {
|