@vellumai/assistant 0.3.27 → 0.4.0
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 +81 -4
- package/Dockerfile +2 -2
- package/bun.lock +4 -1
- package/docs/trusted-contact-access.md +9 -2
- package/package.json +6 -3
- package/scripts/ipc/generate-swift.ts +9 -5
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +80 -0
- package/src/__tests__/agent-loop-thinking.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +119 -0
- package/src/__tests__/approval-routes-http.test.ts +13 -5
- package/src/__tests__/asset-materialize-tool.test.ts +2 -0
- package/src/__tests__/asset-search-tool.test.ts +2 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +4 -2
- package/src/__tests__/attachments-store.test.ts +2 -0
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/bundled-asset.test.ts +107 -0
- package/src/__tests__/call-controller.test.ts +30 -29
- package/src/__tests__/call-routes-http.test.ts +34 -32
- package/src/__tests__/call-start-guardian-guard.test.ts +2 -0
- package/src/__tests__/canonical-guardian-store.test.ts +636 -0
- package/src/__tests__/channel-approval-routes.test.ts +174 -1
- package/src/__tests__/channel-invite-transport.test.ts +6 -6
- package/src/__tests__/channel-reply-delivery.test.ts +19 -0
- package/src/__tests__/channel-retry-sweep.test.ts +130 -0
- package/src/__tests__/clarification-resolver.test.ts +2 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -1
- package/src/__tests__/computer-use-session-lifecycle.test.ts +2 -0
- package/src/__tests__/computer-use-session-working-dir.test.ts +1 -0
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +2 -0
- package/src/__tests__/config-schema.test.ts +5 -5
- package/src/__tests__/config-watcher.test.ts +3 -1
- package/src/__tests__/connection-policy.test.ts +14 -5
- package/src/__tests__/contacts-tools.test.ts +3 -1
- package/src/__tests__/contradiction-checker.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +10 -0
- package/src/__tests__/conversation-routes.test.ts +1 -1
- package/src/__tests__/credential-security-invariants.test.ts +16 -6
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/credential-vault.test.ts +5 -4
- package/src/__tests__/daemon-lifecycle.test.ts +9 -0
- package/src/__tests__/daemon-server-session-init.test.ts +27 -0
- package/src/__tests__/elevenlabs-config.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +43 -1
- package/src/__tests__/encrypted-store.test.ts +10 -5
- package/src/__tests__/followup-tools.test.ts +3 -1
- package/src/__tests__/gateway-only-enforcement.test.ts +21 -21
- package/src/__tests__/gmail-integration.test.ts +0 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +205 -345
- package/src/__tests__/guardian-control-plane-policy.test.ts +19 -19
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +599 -0
- package/src/__tests__/guardian-dispatch.test.ts +21 -19
- package/src/__tests__/guardian-grant-minting.test.ts +68 -1
- package/src/__tests__/guardian-outbound-http.test.ts +12 -9
- package/src/__tests__/guardian-routing-invariants.test.ts +1092 -0
- package/src/__tests__/handle-user-message-secret-resume.test.ts +1 -0
- package/src/__tests__/handlers-slack-config.test.ts +3 -1
- package/src/__tests__/handlers-telegram-config.test.ts +3 -1
- package/src/__tests__/handlers-twilio-config.test.ts +3 -1
- package/src/__tests__/handlers-twitter-config.test.ts +3 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +318 -0
- package/src/__tests__/heartbeat-service.test.ts +20 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +33 -0
- package/src/__tests__/ingress-reconcile.test.ts +3 -1
- package/src/__tests__/ingress-routes-http.test.ts +231 -4
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +13 -0
- package/src/__tests__/mcp-cli.test.ts +77 -0
- package/src/__tests__/media-generate-image.test.ts +21 -0
- package/src/__tests__/media-reuse-story.e2e.test.ts +2 -0
- package/src/__tests__/memory-regressions.test.ts +20 -20
- package/src/__tests__/non-member-access-request.test.ts +212 -36
- package/src/__tests__/notification-decision-fallback.test.ts +63 -3
- package/src/__tests__/notification-decision-strategy.test.ts +78 -0
- package/src/__tests__/notification-guardian-path.test.ts +15 -15
- package/src/__tests__/oauth-connect-handler.test.ts +3 -1
- package/src/__tests__/oauth2-gateway-transport.test.ts +2 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +4 -4
- package/src/__tests__/onboarding-template-contract.test.ts +116 -21
- package/src/__tests__/pairing-routes.test.ts +171 -0
- package/src/__tests__/playbook-execution.test.ts +3 -1
- package/src/__tests__/playbook-tools.test.ts +3 -1
- package/src/__tests__/provider-error-scenarios.test.ts +59 -8
- package/src/__tests__/proxy-approval-callback.test.ts +2 -0
- package/src/__tests__/recording-handler.test.ts +11 -0
- package/src/__tests__/recording-intent-handler.test.ts +15 -0
- package/src/__tests__/recording-state-machine.test.ts +13 -2
- package/src/__tests__/registry.test.ts +7 -3
- package/src/__tests__/relay-server.test.ts +148 -28
- package/src/__tests__/runtime-attachment-metadata.test.ts +4 -2
- package/src/__tests__/runtime-events-sse-parity.test.ts +21 -0
- package/src/__tests__/runtime-events-sse.test.ts +4 -2
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -0
- package/src/__tests__/schedule-tools.test.ts +3 -1
- package/src/__tests__/secret-scanner-executor.test.ts +59 -0
- package/src/__tests__/secret-scanner.test.ts +8 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -0
- package/src/__tests__/sensitive-output-placeholders.test.ts +208 -0
- package/src/__tests__/session-abort-tool-results.test.ts +23 -0
- package/src/__tests__/session-agent-loop.test.ts +16 -0
- package/src/__tests__/session-conflict-gate.test.ts +21 -0
- package/src/__tests__/session-load-history-repair.test.ts +27 -17
- package/src/__tests__/session-pre-run-repair.test.ts +23 -0
- package/src/__tests__/session-profile-injection.test.ts +21 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +20 -0
- package/src/__tests__/session-queue.test.ts +23 -0
- package/src/__tests__/session-runtime-assembly.test.ts +126 -59
- package/src/__tests__/session-skill-tools.test.ts +27 -5
- package/src/__tests__/session-slash-known.test.ts +23 -0
- package/src/__tests__/session-slash-queue.test.ts +23 -0
- package/src/__tests__/session-slash-unknown.test.ts +23 -0
- package/src/__tests__/session-workspace-cache-state.test.ts +7 -0
- package/src/__tests__/session-workspace-injection.test.ts +21 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +21 -0
- package/src/__tests__/shell-credential-ref.test.ts +2 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -4
- package/src/__tests__/skill-projection-feature-flag.test.ts +22 -0
- package/src/__tests__/skills.test.ts +8 -4
- package/src/__tests__/slack-channel-config.test.ts +3 -1
- package/src/__tests__/subagent-tools.test.ts +19 -0
- package/src/__tests__/swarm-recursion.test.ts +2 -0
- package/src/__tests__/swarm-session-integration.test.ts +2 -0
- package/src/__tests__/swarm-tool.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +3 -1
- package/src/__tests__/task-compiler.test.ts +3 -1
- package/src/__tests__/task-management-tools.test.ts +3 -1
- package/src/__tests__/task-tools.test.ts +3 -1
- package/src/__tests__/terminal-sandbox.test.ts +13 -12
- package/src/__tests__/terminal-tools.test.ts +2 -0
- package/src/__tests__/tool-approval-handler.test.ts +15 -15
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -0
- package/src/__tests__/tool-grant-request-escalation.test.ts +497 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +48 -0
- package/src/__tests__/trusted-contact-multichannel.test.ts +22 -19
- package/src/__tests__/trusted-contact-verification.test.ts +91 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +2 -0
- package/src/__tests__/twitter-auth-handler.test.ts +3 -1
- package/src/__tests__/twitter-cli-routing.test.ts +3 -1
- package/src/__tests__/view-image-tool.test.ts +3 -1
- package/src/__tests__/voice-invite-redemption.test.ts +329 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +7 -5
- package/src/__tests__/voice-session-bridge.test.ts +10 -10
- package/src/__tests__/work-item-output.test.ts +3 -1
- package/src/__tests__/workspace-lifecycle.test.ts +13 -2
- package/src/agent/loop.ts +46 -3
- package/src/approvals/guardian-decision-primitive.ts +285 -0
- package/src/approvals/guardian-request-resolvers.ts +539 -0
- package/src/calls/call-controller.ts +26 -23
- package/src/calls/guardian-action-sweep.ts +10 -2
- package/src/calls/guardian-dispatch.ts +46 -40
- package/src/calls/relay-server.ts +358 -24
- package/src/calls/types.ts +1 -1
- package/src/calls/voice-session-bridge.ts +3 -3
- package/src/cli.ts +12 -0
- package/src/config/agent-schema.ts +14 -3
- package/src/config/calls-schema.ts +6 -6
- package/src/config/core-schema.ts +3 -3
- package/src/config/feature-flag-registry.json +8 -0
- package/src/config/mcp-schema.ts +1 -1
- package/src/config/memory-schema.ts +27 -19
- package/src/config/schema.ts +21 -21
- package/src/config/skills-schema.ts +7 -7
- package/src/config/system-prompt.ts +2 -1
- package/src/config/templates/BOOTSTRAP.md +47 -31
- package/src/config/templates/USER.md +5 -0
- package/src/config/update-bulletin-template-path.ts +4 -1
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +149 -21
- package/src/daemon/handlers/config-inbox.ts +4 -4
- package/src/daemon/handlers/guardian-actions.ts +45 -66
- package/src/daemon/handlers/sessions.ts +148 -4
- package/src/daemon/ipc-contract/guardian-actions.ts +7 -0
- package/src/daemon/ipc-contract/messages.ts +16 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +22 -16
- package/src/daemon/pairing-store.ts +86 -3
- package/src/daemon/server.ts +18 -0
- package/src/daemon/session-agent-loop-handlers.ts +5 -4
- package/src/daemon/session-agent-loop.ts +33 -6
- package/src/daemon/session-lifecycle.ts +25 -17
- package/src/daemon/session-memory.ts +2 -2
- package/src/daemon/session-process.ts +68 -326
- package/src/daemon/session-runtime-assembly.ts +119 -25
- package/src/daemon/session-tool-setup.ts +3 -2
- package/src/daemon/session.ts +4 -3
- package/src/home-base/prebuilt/seed.ts +2 -1
- package/src/hooks/templates.ts +2 -1
- package/src/memory/canonical-guardian-store.ts +586 -0
- package/src/memory/channel-guardian-store.ts +2 -0
- package/src/memory/conversation-crud.ts +7 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/embedding-local.ts +257 -39
- package/src/memory/embedding-runtime-manager.ts +471 -0
- package/src/memory/guardian-action-store.ts +7 -60
- package/src/memory/guardian-approvals.ts +9 -4
- package/src/memory/guardian-bindings.ts +25 -1
- package/src/memory/indexer.ts +3 -3
- package/src/memory/ingress-invite-store.ts +45 -0
- package/src/memory/job-handlers/backfill.ts +16 -9
- package/src/memory/migrations/036-normalize-phone-identities.ts +289 -0
- package/src/memory/migrations/037-voice-invite-columns.ts +16 -0
- package/src/memory/migrations/118-reminder-routing-intent.ts +3 -3
- package/src/memory/migrations/121-canonical-guardian-requests.ts +59 -0
- package/src/memory/migrations/122-canonical-guardian-requester-chat-id.ts +15 -0
- package/src/memory/migrations/123-canonical-guardian-deliveries-destination-index.ts +15 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +5 -0
- package/src/memory/qdrant-client.ts +31 -22
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/schema.ts +56 -0
- package/src/notifications/copy-composer.ts +31 -4
- package/src/notifications/decision-engine.ts +57 -0
- package/src/permissions/defaults.ts +2 -0
- package/src/runtime/access-request-helper.ts +173 -0
- package/src/runtime/actor-trust-resolver.ts +221 -0
- package/src/runtime/channel-guardian-service.ts +12 -4
- package/src/runtime/channel-invite-transports/voice.ts +58 -0
- package/src/runtime/channel-retry-sweep.ts +18 -6
- package/src/runtime/guardian-context-resolver.ts +38 -71
- package/src/runtime/guardian-decision-types.ts +6 -0
- package/src/runtime/guardian-reply-router.ts +717 -0
- package/src/runtime/http-server.ts +8 -0
- package/src/runtime/ingress-service.ts +80 -3
- package/src/runtime/invite-redemption-service.ts +141 -2
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +116 -0
- package/src/runtime/routes/channel-route-shared.ts +1 -1
- package/src/runtime/routes/channel-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +20 -2
- package/src/runtime/routes/guardian-action-routes.ts +100 -109
- package/src/runtime/routes/guardian-approval-interception.ts +17 -6
- package/src/runtime/routes/inbound-message-handler.ts +205 -529
- package/src/runtime/routes/ingress-routes.ts +52 -4
- package/src/runtime/routes/pairing-routes.ts +3 -0
- package/src/runtime/tool-grant-request-helper.ts +195 -0
- package/src/tools/executor.ts +13 -1
- package/src/tools/guardian-control-plane-policy.ts +2 -2
- package/src/tools/sensitive-output-placeholders.ts +203 -0
- package/src/tools/tool-approval-handler.ts +53 -10
- package/src/tools/types.ts +13 -2
- package/src/util/bundled-asset.ts +31 -0
- package/src/util/canonicalize-identity.ts +52 -0
- package/src/util/logger.ts +20 -8
- package/src/util/platform.ts +10 -0
- package/src/util/voice-code.ts +29 -0
- package/src/daemon/guardian-invite-intent.ts +0 -124
|
@@ -67,6 +67,11 @@ mock.module('../memory/ingress-member-store.js', () => ({
|
|
|
67
67
|
updateLastSeen: () => {},
|
|
68
68
|
}));
|
|
69
69
|
import type { Session } from '../daemon/session.js';
|
|
70
|
+
import {
|
|
71
|
+
createCanonicalGuardianDelivery,
|
|
72
|
+
createCanonicalGuardianRequest,
|
|
73
|
+
getCanonicalGuardianRequest,
|
|
74
|
+
} from '../memory/canonical-guardian-store.js';
|
|
70
75
|
import * as channelDeliveryStore from '../memory/channel-delivery-store.js';
|
|
71
76
|
import {
|
|
72
77
|
createApprovalRequest,
|
|
@@ -110,6 +115,9 @@ function ensureConversation(conversationId: string): void {
|
|
|
110
115
|
|
|
111
116
|
function resetTables(): void {
|
|
112
117
|
const db = getDb();
|
|
118
|
+
db.run('DELETE FROM scoped_approval_grants');
|
|
119
|
+
db.run('DELETE FROM canonical_guardian_deliveries');
|
|
120
|
+
db.run('DELETE FROM canonical_guardian_requests');
|
|
113
121
|
db.run('DELETE FROM channel_guardian_approval_requests');
|
|
114
122
|
db.run('DELETE FROM channel_guardian_verification_challenges');
|
|
115
123
|
db.run('DELETE FROM channel_guardian_bindings');
|
|
@@ -458,7 +466,7 @@ describe('empty content with callbackData bypasses validation', () => {
|
|
|
458
466
|
const res = await handleChannelInbound(req, noopProcessMessage);
|
|
459
467
|
expect(res.status).toBe(400);
|
|
460
468
|
const body = await res.json() as Record<string, unknown>;
|
|
461
|
-
expect(body.error).toBe('content or attachmentIds is required');
|
|
469
|
+
expect((body.error as Record<string, unknown>).message).toBe('content or attachmentIds is required');
|
|
462
470
|
});
|
|
463
471
|
|
|
464
472
|
test('allows empty content when callbackData is present', async () => {
|
|
@@ -2645,4 +2653,169 @@ describe('background channel processing approval prompts', () => {
|
|
|
2645
2653
|
expect(processCalls.length).toBeGreaterThan(0);
|
|
2646
2654
|
expect(processCalls[0].options?.isInteractive).toBe(false);
|
|
2647
2655
|
});
|
|
2656
|
+
|
|
2657
|
+
test('unverified channel turns never broadcast approval prompts', async () => {
|
|
2658
|
+
// No guardian binding is created, so the sender resolves to unverified_channel.
|
|
2659
|
+
const deliverPromptSpy = spyOn(gatewayClient, 'deliverApprovalPrompt').mockResolvedValue(undefined);
|
|
2660
|
+
const processCalls: Array<{ options?: Record<string, unknown> }> = [];
|
|
2661
|
+
|
|
2662
|
+
const processMessage = mock(async (
|
|
2663
|
+
conversationId: string,
|
|
2664
|
+
_content: string,
|
|
2665
|
+
_attachmentIds?: string[],
|
|
2666
|
+
options?: Record<string, unknown>,
|
|
2667
|
+
) => {
|
|
2668
|
+
processCalls.push({ options });
|
|
2669
|
+
|
|
2670
|
+
// Simulate a pending confirmation becoming visible while background
|
|
2671
|
+
// processing is running. Unverified actors must still not receive it.
|
|
2672
|
+
registerPendingInteraction('req-bg-unverified-1', conversationId, 'host_bash', {
|
|
2673
|
+
input: { command: 'ls -la' },
|
|
2674
|
+
riskLevel: 'medium',
|
|
2675
|
+
});
|
|
2676
|
+
|
|
2677
|
+
await new Promise((resolve) => setTimeout(resolve, 350));
|
|
2678
|
+
return { messageId: 'msg-bg-unverified-1' };
|
|
2679
|
+
});
|
|
2680
|
+
|
|
2681
|
+
const req = makeInboundRequest({
|
|
2682
|
+
content: 'run ls',
|
|
2683
|
+
sourceChannel: 'telegram',
|
|
2684
|
+
replyCallbackUrl: 'https://gateway.test/deliver/telegram',
|
|
2685
|
+
externalMessageId: 'msg-bg-unverified-1',
|
|
2686
|
+
});
|
|
2687
|
+
|
|
2688
|
+
const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage, 'token');
|
|
2689
|
+
const body = await res.json() as Record<string, unknown>;
|
|
2690
|
+
expect(body.accepted).toBe(true);
|
|
2691
|
+
|
|
2692
|
+
await new Promise((resolve) => setTimeout(resolve, 700));
|
|
2693
|
+
|
|
2694
|
+
expect(processCalls.length).toBeGreaterThan(0);
|
|
2695
|
+
expect(processCalls[0].options?.isInteractive).toBe(false);
|
|
2696
|
+
expect(deliverPromptSpy).not.toHaveBeenCalled();
|
|
2697
|
+
|
|
2698
|
+
deliverPromptSpy.mockRestore();
|
|
2699
|
+
});
|
|
2700
|
+
});
|
|
2701
|
+
|
|
2702
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2703
|
+
// NL approval routing via destination-scoped canonical requests
|
|
2704
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2705
|
+
|
|
2706
|
+
describe('NL approval routing via destination-scoped canonical requests', () => {
|
|
2707
|
+
beforeEach(() => {
|
|
2708
|
+
resetTables();
|
|
2709
|
+
noopProcessMessage.mockClear();
|
|
2710
|
+
});
|
|
2711
|
+
|
|
2712
|
+
test('guardian plain-text "yes" resolves a pending_question with no guardianExternalUserId via delivery-scoped hint', async () => {
|
|
2713
|
+
// Simulate a voice-originated pending_question without guardianExternalUserId
|
|
2714
|
+
const guardianChatId = 'guardian-chat-nl-1';
|
|
2715
|
+
const guardianUserId = 'guardian-user-nl-1';
|
|
2716
|
+
|
|
2717
|
+
// Ensure the conversation exists so the resolver finds it
|
|
2718
|
+
ensureConversation('conv-voice-nl-1');
|
|
2719
|
+
|
|
2720
|
+
// Create guardian binding for Telegram
|
|
2721
|
+
createBinding({
|
|
2722
|
+
assistantId: 'self',
|
|
2723
|
+
channel: 'telegram',
|
|
2724
|
+
guardianExternalUserId: guardianUserId,
|
|
2725
|
+
guardianDeliveryChatId: guardianChatId,
|
|
2726
|
+
});
|
|
2727
|
+
|
|
2728
|
+
// Create canonical tool_approval request WITHOUT guardianExternalUserId
|
|
2729
|
+
// but WITH a conversationId (required by the tool_approval resolver)
|
|
2730
|
+
const canonicalReq = createCanonicalGuardianRequest({
|
|
2731
|
+
kind: 'tool_approval',
|
|
2732
|
+
sourceType: 'voice',
|
|
2733
|
+
sourceChannel: 'twilio',
|
|
2734
|
+
conversationId: 'conv-voice-nl-1',
|
|
2735
|
+
toolName: 'shell',
|
|
2736
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
2737
|
+
// guardianExternalUserId intentionally omitted
|
|
2738
|
+
});
|
|
2739
|
+
|
|
2740
|
+
// Register pending interaction so resolver can find it
|
|
2741
|
+
registerPendingInteraction(canonicalReq.id, 'conv-voice-nl-1', 'shell');
|
|
2742
|
+
|
|
2743
|
+
// Create canonical delivery row targeting guardian chat
|
|
2744
|
+
createCanonicalGuardianDelivery({
|
|
2745
|
+
requestId: canonicalReq.id,
|
|
2746
|
+
destinationChannel: 'telegram',
|
|
2747
|
+
destinationChatId: guardianChatId,
|
|
2748
|
+
});
|
|
2749
|
+
|
|
2750
|
+
// Send inbound guardian text reply "yes" from that chat
|
|
2751
|
+
const req = makeInboundRequest({
|
|
2752
|
+
sourceChannel: 'telegram',
|
|
2753
|
+
externalChatId: guardianChatId,
|
|
2754
|
+
senderExternalUserId: guardianUserId,
|
|
2755
|
+
content: 'yes',
|
|
2756
|
+
externalMessageId: `msg-nl-approve-${Date.now()}`,
|
|
2757
|
+
});
|
|
2758
|
+
const res = await handleChannelInbound(req, noopProcessMessage as any, TEST_BEARER_TOKEN);
|
|
2759
|
+
const body = await res.json() as Record<string, unknown>;
|
|
2760
|
+
|
|
2761
|
+
expect(body.accepted).toBe(true);
|
|
2762
|
+
expect(body.canonicalRouter).toBe('canonical_decision_applied');
|
|
2763
|
+
|
|
2764
|
+
// Verify the request was resolved
|
|
2765
|
+
const resolved = getCanonicalGuardianRequest(canonicalReq.id);
|
|
2766
|
+
expect(resolved).not.toBeNull();
|
|
2767
|
+
expect(resolved!.status).toBe('approved');
|
|
2768
|
+
});
|
|
2769
|
+
|
|
2770
|
+
test('inbound from different chat ID does not auto-match delivery-scoped canonical request', async () => {
|
|
2771
|
+
const guardianChatId = 'guardian-chat-nl-2';
|
|
2772
|
+
const guardianUserId = 'guardian-user-nl-2';
|
|
2773
|
+
const differentChatId = 'different-chat-999';
|
|
2774
|
+
|
|
2775
|
+
// Create guardian binding for the guardian user on the different chat
|
|
2776
|
+
createBinding({
|
|
2777
|
+
assistantId: 'self',
|
|
2778
|
+
channel: 'telegram',
|
|
2779
|
+
guardianExternalUserId: guardianUserId,
|
|
2780
|
+
guardianDeliveryChatId: differentChatId,
|
|
2781
|
+
});
|
|
2782
|
+
|
|
2783
|
+
// Create canonical pending_question WITHOUT guardianExternalUserId
|
|
2784
|
+
const canonicalReq = createCanonicalGuardianRequest({
|
|
2785
|
+
kind: 'tool_approval',
|
|
2786
|
+
sourceType: 'voice',
|
|
2787
|
+
sourceChannel: 'twilio',
|
|
2788
|
+
toolName: 'shell',
|
|
2789
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
2790
|
+
});
|
|
2791
|
+
|
|
2792
|
+
// Delivery targets the original guardian chat, NOT the different chat
|
|
2793
|
+
createCanonicalGuardianDelivery({
|
|
2794
|
+
requestId: canonicalReq.id,
|
|
2795
|
+
destinationChannel: 'telegram',
|
|
2796
|
+
destinationChatId: guardianChatId,
|
|
2797
|
+
});
|
|
2798
|
+
|
|
2799
|
+
// Send from differentChatId — delivery-scoped lookup should not match
|
|
2800
|
+
const req = makeInboundRequest({
|
|
2801
|
+
sourceChannel: 'telegram',
|
|
2802
|
+
externalChatId: differentChatId,
|
|
2803
|
+
senderExternalUserId: guardianUserId,
|
|
2804
|
+
content: 'approve',
|
|
2805
|
+
externalMessageId: `msg-nl-mismatch-${Date.now()}`,
|
|
2806
|
+
});
|
|
2807
|
+
const res = await handleChannelInbound(req, noopProcessMessage as any, TEST_BEARER_TOKEN);
|
|
2808
|
+
const body = await res.json() as Record<string, unknown>;
|
|
2809
|
+
|
|
2810
|
+
expect(body.accepted).toBe(true);
|
|
2811
|
+
// Should NOT have been consumed by canonical router since there are no
|
|
2812
|
+
// delivery-scoped pending requests for this chat, and identity-based
|
|
2813
|
+
// fallback finds no match either (no guardianExternalUserId on request)
|
|
2814
|
+
expect(body.canonicalRouter).toBeUndefined();
|
|
2815
|
+
|
|
2816
|
+
// Request should remain pending
|
|
2817
|
+
const unchanged = getCanonicalGuardianRequest(canonicalReq.id);
|
|
2818
|
+
expect(unchanged).not.toBeNull();
|
|
2819
|
+
expect(unchanged!.status).toBe('pending');
|
|
2820
|
+
});
|
|
2648
2821
|
});
|
|
@@ -71,7 +71,7 @@ describe('channel-invite-transport', () => {
|
|
|
71
71
|
|
|
72
72
|
describe('telegram buildShareableInvite', () => {
|
|
73
73
|
test('produces a valid Telegram deep link', () => {
|
|
74
|
-
const result = telegramInviteTransport.buildShareableInvite({
|
|
74
|
+
const result = telegramInviteTransport.buildShareableInvite!({
|
|
75
75
|
rawToken: 'abc123_test-token',
|
|
76
76
|
sourceChannel: 'telegram',
|
|
77
77
|
});
|
|
@@ -81,15 +81,15 @@ describe('channel-invite-transport', () => {
|
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
test('deep link is deterministic for the same token', () => {
|
|
84
|
-
const a = telegramInviteTransport.buildShareableInvite({ rawToken: 'tok1', sourceChannel: 'telegram' });
|
|
85
|
-
const b = telegramInviteTransport.buildShareableInvite({ rawToken: 'tok1', sourceChannel: 'telegram' });
|
|
84
|
+
const a = telegramInviteTransport.buildShareableInvite!({ rawToken: 'tok1', sourceChannel: 'telegram' });
|
|
85
|
+
const b = telegramInviteTransport.buildShareableInvite!({ rawToken: 'tok1', sourceChannel: 'telegram' });
|
|
86
86
|
expect(a.url).toBe(b.url);
|
|
87
87
|
expect(a.displayText).toBe(b.displayText);
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
test('uses the configured bot username', () => {
|
|
91
91
|
mockBotUsername = 'my_custom_bot';
|
|
92
|
-
const result = telegramInviteTransport.buildShareableInvite({
|
|
92
|
+
const result = telegramInviteTransport.buildShareableInvite!({
|
|
93
93
|
rawToken: 'token',
|
|
94
94
|
sourceChannel: 'telegram',
|
|
95
95
|
});
|
|
@@ -103,7 +103,7 @@ describe('channel-invite-transport', () => {
|
|
|
103
103
|
delete process.env.TELEGRAM_BOT_USERNAME;
|
|
104
104
|
try {
|
|
105
105
|
expect(() =>
|
|
106
|
-
telegramInviteTransport.buildShareableInvite({
|
|
106
|
+
telegramInviteTransport.buildShareableInvite!({
|
|
107
107
|
rawToken: 'token',
|
|
108
108
|
sourceChannel: 'telegram',
|
|
109
109
|
}),
|
|
@@ -118,7 +118,7 @@ describe('channel-invite-transport', () => {
|
|
|
118
118
|
const prev = process.env.TELEGRAM_BOT_USERNAME;
|
|
119
119
|
process.env.TELEGRAM_BOT_USERNAME = 'env_bot';
|
|
120
120
|
try {
|
|
121
|
-
const result = telegramInviteTransport.buildShareableInvite({
|
|
121
|
+
const result = telegramInviteTransport.buildShareableInvite!({
|
|
122
122
|
rawToken: 'token',
|
|
123
123
|
sourceChannel: 'telegram',
|
|
124
124
|
});
|
|
@@ -50,6 +50,25 @@ mock.module('../runtime/gateway-client.js', () => ({
|
|
|
50
50
|
}));
|
|
51
51
|
|
|
52
52
|
mock.module('../memory/conversation-store.js', () => ({
|
|
53
|
+
getConversationThreadType: () => 'default',
|
|
54
|
+
setConversationOriginChannelIfUnset: () => {},
|
|
55
|
+
updateConversationContextWindow: () => {},
|
|
56
|
+
deleteMessageById: () => {},
|
|
57
|
+
updateConversationTitle: () => {},
|
|
58
|
+
updateConversationUsage: () => {},
|
|
59
|
+
addMessage: () => ({ id: 'mock-msg-id' }),
|
|
60
|
+
getConversation: () => ({
|
|
61
|
+
id: 'conv-1',
|
|
62
|
+
contextSummary: null,
|
|
63
|
+
contextCompactedMessageCount: 0,
|
|
64
|
+
totalInputTokens: 0,
|
|
65
|
+
totalOutputTokens: 0,
|
|
66
|
+
totalEstimatedCost: 0,
|
|
67
|
+
title: null,
|
|
68
|
+
}),
|
|
69
|
+
provenanceFromGuardianContext: () => ({ source: 'user', guardianContext: undefined }),
|
|
70
|
+
getConversationOriginInterface: () => null,
|
|
71
|
+
getConversationOriginChannel: () => null,
|
|
53
72
|
getMessages: () => conversationMessages,
|
|
54
73
|
}));
|
|
55
74
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
6
|
+
import { eq } from 'drizzle-orm';
|
|
7
|
+
|
|
8
|
+
const testDir = mkdtempSync(join(tmpdir(), 'channel-retry-sweep-test-'));
|
|
9
|
+
|
|
10
|
+
mock.module('../util/platform.js', () => ({
|
|
11
|
+
getDataDir: () => testDir,
|
|
12
|
+
isMacOS: () => process.platform === 'darwin',
|
|
13
|
+
isLinux: () => process.platform === 'linux',
|
|
14
|
+
isWindows: () => process.platform === 'win32',
|
|
15
|
+
getSocketPath: () => join(testDir, 'test.sock'),
|
|
16
|
+
getPidPath: () => join(testDir, 'test.pid'),
|
|
17
|
+
getDbPath: () => join(testDir, 'test.db'),
|
|
18
|
+
getLogPath: () => join(testDir, 'test.log'),
|
|
19
|
+
ensureDataDir: () => {},
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
mock.module('../util/logger.js', () => ({
|
|
23
|
+
getLogger: () => new Proxy({} as Record<string, unknown>, {
|
|
24
|
+
get: () => () => {},
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
import * as channelDeliveryStore from '../memory/channel-delivery-store.js';
|
|
29
|
+
import { getDb, initializeDb, resetDb } from '../memory/db.js';
|
|
30
|
+
import { channelInboundEvents, messages } from '../memory/schema.js';
|
|
31
|
+
import { sweepFailedEvents } from '../runtime/channel-retry-sweep.js';
|
|
32
|
+
|
|
33
|
+
initializeDb();
|
|
34
|
+
|
|
35
|
+
afterAll(() => {
|
|
36
|
+
resetDb();
|
|
37
|
+
try {
|
|
38
|
+
rmSync(testDir, { recursive: true });
|
|
39
|
+
} catch {
|
|
40
|
+
// Best effort cleanup
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
function resetTables(): void {
|
|
45
|
+
const db = getDb();
|
|
46
|
+
db.run('DELETE FROM channel_inbound_events');
|
|
47
|
+
db.run('DELETE FROM conversation_keys');
|
|
48
|
+
db.run('DELETE FROM messages');
|
|
49
|
+
db.run('DELETE FROM conversations');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function seedFailedLegacyEvent(actorRole: 'guardian' | 'non-guardian' | 'unverified_channel'): string {
|
|
53
|
+
const inbound = channelDeliveryStore.recordInbound('telegram', 'chat-legacy', `msg-${actorRole}`);
|
|
54
|
+
channelDeliveryStore.storePayload(inbound.eventId, {
|
|
55
|
+
content: 'retry me',
|
|
56
|
+
sourceChannel: 'telegram',
|
|
57
|
+
interface: 'telegram',
|
|
58
|
+
guardianCtx: {
|
|
59
|
+
actorRole,
|
|
60
|
+
sourceChannel: 'telegram',
|
|
61
|
+
requesterExternalUserId: 'legacy-user',
|
|
62
|
+
requesterChatId: 'chat-legacy',
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const db = getDb();
|
|
67
|
+
db.update(channelInboundEvents)
|
|
68
|
+
.set({
|
|
69
|
+
processingStatus: 'failed',
|
|
70
|
+
processingAttempts: 1,
|
|
71
|
+
retryAfter: Date.now() - 1,
|
|
72
|
+
})
|
|
73
|
+
.where(eq(channelInboundEvents.id, inbound.eventId))
|
|
74
|
+
.run();
|
|
75
|
+
|
|
76
|
+
return inbound.eventId;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
describe('channel-retry-sweep', () => {
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
resetTables();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('replays legacy guardianCtx.actorRole with preserved trust semantics', async () => {
|
|
85
|
+
const cases: Array<{
|
|
86
|
+
actorRole: 'guardian' | 'non-guardian' | 'unverified_channel';
|
|
87
|
+
expectedTrustClass: 'guardian' | 'trusted_contact' | 'unknown';
|
|
88
|
+
expectedInteractive: boolean;
|
|
89
|
+
}> = [
|
|
90
|
+
{ actorRole: 'guardian', expectedTrustClass: 'guardian', expectedInteractive: true },
|
|
91
|
+
{ actorRole: 'non-guardian', expectedTrustClass: 'trusted_contact', expectedInteractive: false },
|
|
92
|
+
{ actorRole: 'unverified_channel', expectedTrustClass: 'unknown', expectedInteractive: false },
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (const c of cases) {
|
|
96
|
+
const eventId = seedFailedLegacyEvent(c.actorRole);
|
|
97
|
+
let capturedOptions: {
|
|
98
|
+
guardianContext?: { trustClass?: string };
|
|
99
|
+
isInteractive?: boolean;
|
|
100
|
+
} | undefined;
|
|
101
|
+
|
|
102
|
+
await sweepFailedEvents(
|
|
103
|
+
async (conversationId, _content, _attachmentIds, options) => {
|
|
104
|
+
capturedOptions = options as {
|
|
105
|
+
guardianContext?: { trustClass?: string };
|
|
106
|
+
isInteractive?: boolean;
|
|
107
|
+
};
|
|
108
|
+
const messageId = `message-${c.actorRole}`;
|
|
109
|
+
const db = getDb();
|
|
110
|
+
db.insert(messages).values({
|
|
111
|
+
id: messageId,
|
|
112
|
+
conversationId,
|
|
113
|
+
role: 'user',
|
|
114
|
+
content: JSON.stringify([{ type: 'text', text: 'retry me' }]),
|
|
115
|
+
createdAt: Date.now(),
|
|
116
|
+
}).run();
|
|
117
|
+
return { messageId };
|
|
118
|
+
},
|
|
119
|
+
undefined,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(capturedOptions?.guardianContext?.trustClass).toBe(c.expectedTrustClass);
|
|
123
|
+
expect(capturedOptions?.isInteractive).toBe(c.expectedInteractive);
|
|
124
|
+
|
|
125
|
+
const db = getDb();
|
|
126
|
+
const row = db.select().from(channelInboundEvents).where(eq(channelInboundEvents.id, eventId)).get();
|
|
127
|
+
expect(row?.processingStatus).toBe('processed');
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -3,11 +3,12 @@ import { existsSync,mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
|
|
6
|
-
import { afterEach,beforeEach, describe, expect, test } from 'bun:test';
|
|
6
|
+
import { afterAll, afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
_resetEnrichmentService,
|
|
10
10
|
CommitEnrichmentService,
|
|
11
|
+
getEnrichmentService,
|
|
11
12
|
} from '../workspace/commit-message-enrichment-service.js';
|
|
12
13
|
import type { CommitContext } from '../workspace/commit-message-provider.js';
|
|
13
14
|
import { _resetGitServiceRegistry,WorkspaceGitService } from '../workspace/git-service.js';
|
|
@@ -27,11 +28,18 @@ describe('CommitEnrichmentService', () => {
|
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
afterEach(async () => {
|
|
31
|
+
try { await getEnrichmentService().shutdown(); } catch { /* ignore */ }
|
|
32
|
+
_resetEnrichmentService();
|
|
30
33
|
if (existsSync(testDir)) {
|
|
31
34
|
rmSync(testDir, { recursive: true, force: true });
|
|
32
35
|
}
|
|
33
36
|
});
|
|
34
37
|
|
|
38
|
+
afterAll(async () => {
|
|
39
|
+
try { await getEnrichmentService().shutdown(); } catch { /* ignore */ }
|
|
40
|
+
_resetEnrichmentService();
|
|
41
|
+
});
|
|
42
|
+
|
|
35
43
|
function makeContext(overrides?: Partial<CommitContext>): CommitContext {
|
|
36
44
|
return {
|
|
37
45
|
workspaceDir: testDir,
|
|
@@ -5,6 +5,8 @@ import { describe, expect, mock,test } from 'bun:test';
|
|
|
5
5
|
// (which have no trust rules in test) don't trigger approval prompts.
|
|
6
6
|
mock.module('../config/loader.js', () => ({
|
|
7
7
|
getConfig: () => ({
|
|
8
|
+
ui: {},
|
|
9
|
+
|
|
8
10
|
provider: 'mock-provider',
|
|
9
11
|
permissions: { mode: 'legacy' },
|
|
10
12
|
apiKeys: {},
|
|
@@ -3,6 +3,8 @@ import { beforeAll, describe, expect, mock,test } from 'bun:test';
|
|
|
3
3
|
// Mock config before importing modules that depend on it.
|
|
4
4
|
mock.module('../config/loader.js', () => ({
|
|
5
5
|
getConfig: () => ({
|
|
6
|
+
ui: {},
|
|
7
|
+
|
|
6
8
|
provider: 'mock-provider',
|
|
7
9
|
permissions: { mode: 'legacy' },
|
|
8
10
|
apiKeys: {},
|
|
@@ -79,7 +79,7 @@ describe('AssistantConfigSchema', () => {
|
|
|
79
79
|
expect(result.thinking).toEqual({ enabled: false, budgetTokens: 10000, streamThinking: false });
|
|
80
80
|
expect(result.contextWindow).toEqual({
|
|
81
81
|
enabled: true,
|
|
82
|
-
maxInputTokens:
|
|
82
|
+
maxInputTokens: 200000,
|
|
83
83
|
targetInputTokens: 110000,
|
|
84
84
|
compactThreshold: 0.8,
|
|
85
85
|
preserveRecentUserTurns: 8,
|
|
@@ -1098,7 +1098,7 @@ describe('loadConfig with schema validation', () => {
|
|
|
1098
1098
|
expect(config.thinking).toEqual({ enabled: false, budgetTokens: 10000, streamThinking: false });
|
|
1099
1099
|
expect(config.contextWindow).toEqual({
|
|
1100
1100
|
enabled: true,
|
|
1101
|
-
maxInputTokens:
|
|
1101
|
+
maxInputTokens: 200000,
|
|
1102
1102
|
targetInputTokens: 110000,
|
|
1103
1103
|
compactThreshold: 0.8,
|
|
1104
1104
|
preserveRecentUserTurns: 8,
|
|
@@ -1188,7 +1188,7 @@ describe('loadConfig with schema validation', () => {
|
|
|
1188
1188
|
test('falls back for invalid contextWindow relationship', () => {
|
|
1189
1189
|
writeConfig({ contextWindow: { maxInputTokens: 1000, targetInputTokens: 1000 } });
|
|
1190
1190
|
const config = loadConfig();
|
|
1191
|
-
expect(config.contextWindow.maxInputTokens).toBe(
|
|
1191
|
+
expect(config.contextWindow.maxInputTokens).toBe(200000);
|
|
1192
1192
|
expect(config.contextWindow.targetInputTokens).toBe(110000);
|
|
1193
1193
|
});
|
|
1194
1194
|
|
|
@@ -1348,7 +1348,7 @@ describe('Call entrypoint gating', () => {
|
|
|
1348
1348
|
const response = await handleStartCall(req);
|
|
1349
1349
|
expect(response.status).toBe(403);
|
|
1350
1350
|
|
|
1351
|
-
const body = await response.json() as { error: string };
|
|
1352
|
-
expect(body.error).toContain('disabled');
|
|
1351
|
+
const body = await response.json() as { error: { message: string } };
|
|
1352
|
+
expect(body.error.message).toContain('disabled');
|
|
1353
1353
|
});
|
|
1354
1354
|
});
|
|
@@ -96,7 +96,9 @@ mock.module('node:fs', () => {
|
|
|
96
96
|
|
|
97
97
|
// Mock config/loader and other dependencies that ConfigWatcher imports
|
|
98
98
|
mock.module('../config/loader.js', () => ({
|
|
99
|
-
getConfig: () => ({
|
|
99
|
+
getConfig: () => ({
|
|
100
|
+
ui: {},
|
|
101
|
+
}),
|
|
100
102
|
invalidateConfigCache: () => {},
|
|
101
103
|
}));
|
|
102
104
|
|
|
@@ -41,21 +41,30 @@ describe('hasNoAuthOverride', () => {
|
|
|
41
41
|
expect(hasNoAuthOverride({ VELLUM_DAEMON_NOAUTH: 'false' })).toBe(false);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
test('returns true when VELLUM_DAEMON_NOAUTH is 1', () => {
|
|
45
|
-
expect(hasNoAuthOverride({ VELLUM_DAEMON_NOAUTH: '1' })).toBe(true);
|
|
44
|
+
test('returns true when VELLUM_DAEMON_NOAUTH is 1 with safety gate', () => {
|
|
45
|
+
expect(hasNoAuthOverride({ VELLUM_DAEMON_NOAUTH: '1', VELLUM_UNSAFE_AUTH_BYPASS: '1' })).toBe(true);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
test('returns
|
|
49
|
-
expect(hasNoAuthOverride({ VELLUM_DAEMON_NOAUTH: '
|
|
48
|
+
test('returns false when VELLUM_DAEMON_NOAUTH is 1 without safety gate', () => {
|
|
49
|
+
expect(hasNoAuthOverride({ VELLUM_DAEMON_NOAUTH: '1' })).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('returns true when VELLUM_DAEMON_NOAUTH is true with safety gate', () => {
|
|
53
|
+
expect(hasNoAuthOverride({ VELLUM_DAEMON_NOAUTH: 'true', VELLUM_UNSAFE_AUTH_BYPASS: '1' })).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('returns false when VELLUM_DAEMON_NOAUTH is true without safety gate', () => {
|
|
57
|
+
expect(hasNoAuthOverride({ VELLUM_DAEMON_NOAUTH: 'true' })).toBe(false);
|
|
50
58
|
});
|
|
51
59
|
|
|
52
60
|
test('is independent of VELLUM_DAEMON_SOCKET', () => {
|
|
53
61
|
// Socket override alone does NOT enable no-auth
|
|
54
62
|
expect(hasNoAuthOverride({ VELLUM_DAEMON_SOCKET: '/tmp/custom.sock' })).toBe(false);
|
|
55
|
-
// No-auth requires its own explicit flag
|
|
63
|
+
// No-auth requires its own explicit flag plus safety gate
|
|
56
64
|
expect(hasNoAuthOverride({
|
|
57
65
|
VELLUM_DAEMON_SOCKET: '/tmp/custom.sock',
|
|
58
66
|
VELLUM_DAEMON_NOAUTH: '1',
|
|
67
|
+
VELLUM_UNSAFE_AUTH_BYPASS: '1',
|
|
59
68
|
})).toBe(true);
|
|
60
69
|
});
|
|
61
70
|
});
|
|
@@ -49,6 +49,16 @@ const getConversationMock = mock((id: string) => {
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
mock.module('../memory/conversation-store.js', () => ({
|
|
52
|
+
getConversationThreadType: () => 'default',
|
|
53
|
+
setConversationOriginChannelIfUnset: () => {},
|
|
54
|
+
updateConversationContextWindow: () => {},
|
|
55
|
+
deleteMessageById: () => {},
|
|
56
|
+
updateConversationTitle: () => {},
|
|
57
|
+
updateConversationUsage: () => {},
|
|
58
|
+
getMessages: () => [],
|
|
59
|
+
provenanceFromGuardianContext: () => ({ source: 'user', guardianContext: undefined }),
|
|
60
|
+
getConversationOriginInterface: () => null,
|
|
61
|
+
getConversationOriginChannel: () => null,
|
|
52
62
|
createConversation: createConversationMock,
|
|
53
63
|
addMessage: addMessageMock,
|
|
54
64
|
getConversation: getConversationMock,
|
|
@@ -49,7 +49,7 @@ describe('handleSendMessage', () => {
|
|
|
49
49
|
expect(body.messageId).toBe('msg-legacy-fallback');
|
|
50
50
|
expect(capturedSourceChannel).toBe('telegram');
|
|
51
51
|
expect(capturedOptions?.guardianContext).toEqual({
|
|
52
|
-
|
|
52
|
+
trustClass: 'guardian',
|
|
53
53
|
sourceChannel: 'telegram',
|
|
54
54
|
});
|
|
55
55
|
});
|