@vellumai/assistant 0.4.35 → 0.4.37
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/AGENTS.md +1 -1
- package/ARCHITECTURE.md +44 -49
- package/README.md +32 -20
- package/docs/architecture/keychain-broker.md +186 -0
- package/docs/architecture/security.md +110 -116
- package/docs/runbook-trusted-contacts.md +2 -2
- package/docs/skills.md +25 -25
- package/package.json +5 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
- package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundle-scanner.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +102 -102
- package/src/__tests__/channel-invite-transport.test.ts +155 -256
- package/src/__tests__/channel-readiness-routes.test.ts +336 -0
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/chrome-cdp.test.ts +350 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
- package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-migration.test.ts +85 -0
- package/src/__tests__/conversation-pairing.test.ts +370 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
- package/src/__tests__/credential-broker-server-use.test.ts +1 -10
- package/src/__tests__/credential-security-e2e.test.ts +7 -1
- package/src/__tests__/credential-security-invariants.test.ts +14 -20
- package/src/__tests__/credential-vault-unit.test.ts +1 -11
- package/src/__tests__/credential-vault.test.ts +5 -19
- package/src/__tests__/credentials-cli.test.ts +814 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
- package/src/__tests__/email-invite-adapter.test.ts +78 -0
- package/src/__tests__/email-service-config-fallback.test.ts +102 -0
- package/src/__tests__/encrypted-store.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
- package/src/__tests__/guardian-outbound-http.test.ts +53 -47
- package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
- package/src/__tests__/handlers-telegram-config.test.ts +8 -2
- package/src/__tests__/handlers-twitter-config.test.ts +2 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
- package/src/__tests__/ingress-reconcile.test.ts +6 -0
- package/src/__tests__/intent-routing.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -0
- package/src/__tests__/ipc-snapshot.test.ts +8 -2
- package/src/__tests__/keychain-broker-client.test.ts +543 -0
- package/src/__tests__/llm-usage-store.test.ts +344 -0
- package/src/__tests__/mcp-client-auth.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/migration-transport.test.ts +49 -0
- package/src/__tests__/notification-broadcaster.test.ts +205 -5
- package/src/__tests__/notification-deep-link.test.ts +365 -1
- package/src/__tests__/oauth-connect-handler.test.ts +2 -2
- package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
- package/src/__tests__/proxy-approval-callback.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +6 -1
- package/src/__tests__/recording-state-machine.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +9 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +8 -2
- package/src/__tests__/secure-keys.test.ts +175 -216
- package/src/__tests__/session-confirmation-signals.test.ts +1 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/session-queue.test.ts +2 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
- package/src/__tests__/skill-feature-flags.test.ts +12 -9
- package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skills.test.ts +34 -4
- package/src/__tests__/slack-channel-config.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +26 -4
- package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
- package/src/__tests__/twitter-auth-handler.test.ts +2 -2
- package/src/__tests__/twitter-oauth-client.test.ts +1 -1
- package/src/__tests__/usage-routes.test.ts +339 -0
- package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
- package/src/agent/loop.ts +3 -0
- package/src/amazon/checkout.ts +0 -1
- package/src/approvals/guardian-request-resolvers.ts +9 -1
- package/src/bundler/app-bundler.ts +28 -12
- package/src/bundler/bundle-scanner.ts +1 -1
- package/src/bundler/bundle-signer.ts +3 -3
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/signature-verifier.ts +3 -3
- package/src/channels/config.ts +1 -1
- package/src/cli/AGENTS.md +63 -0
- package/src/cli/__tests__/notifications.test.ts +470 -0
- package/src/cli/amazon.ts +344 -167
- package/src/cli/audit.ts +85 -0
- package/src/cli/autonomy.ts +369 -0
- package/src/cli/channels.ts +51 -0
- package/src/cli/completions.ts +208 -0
- package/src/cli/config.ts +220 -0
- package/src/cli/contacts.ts +471 -0
- package/src/cli/credentials.ts +564 -0
- package/src/cli/default-action.ts +14 -0
- package/src/cli/dev.ts +131 -0
- package/src/cli/doctor.ts +398 -0
- package/src/cli/email.ts +494 -0
- package/src/cli/influencer.ts +72 -0
- package/src/cli/integrations.ts +248 -57
- package/src/cli/keys.ts +114 -0
- package/src/cli/map.ts +46 -54
- package/src/cli/mcp.ts +111 -3
- package/src/cli/{config-commands.ts → memory.ts} +134 -245
- package/src/cli/notifications.ts +407 -0
- package/src/cli/program.ts +65 -0
- package/src/cli/reference.ts +48 -0
- package/src/cli/sequence.ts +154 -0
- package/src/cli/sessions.ts +262 -0
- package/src/cli/trust.ts +175 -0
- package/src/cli/twitter.ts +323 -106
- package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
- package/src/config/bundled-skills/amazon/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
- package/src/config/bundled-skills/contacts/SKILL.md +178 -10
- package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/core-schema.ts +7 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +26 -0
- package/src/config/schema.ts +4 -0
- package/src/config/skill-state.ts +0 -13
- package/src/config/system-prompt.ts +27 -0
- package/src/contacts/contact-store.ts +25 -0
- package/src/daemon/computer-use-session.ts +1 -1
- package/src/daemon/handlers/apps.ts +1 -0
- package/src/daemon/handlers/config-channels.ts +3 -3
- package/src/daemon/handlers/config-dispatch.ts +29 -0
- package/src/daemon/handlers/config-inbox.ts +4 -3
- package/src/daemon/handlers/config.ts +3 -43
- package/src/daemon/handlers/contacts.ts +34 -0
- package/src/daemon/handlers/index.ts +17 -3
- package/src/daemon/handlers/session-user-message.ts +7 -0
- package/src/daemon/handlers/sessions.ts +21 -2
- package/src/daemon/handlers/shared.ts +17 -0
- package/src/daemon/ipc-contract/apps.ts +2 -0
- package/src/daemon/ipc-contract/computer-use.ts +9 -0
- package/src/daemon/ipc-contract/contacts.ts +3 -3
- package/src/daemon/ipc-contract/inbox.ts +2 -0
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/ride-shotgun-handler.ts +139 -25
- package/src/daemon/session-agent-loop-handlers.ts +100 -0
- package/src/daemon/session-agent-loop.ts +72 -0
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/daemon/session.ts +23 -1
- package/src/daemon/tool-side-effects.ts +39 -1
- package/src/email/service.ts +59 -2
- package/src/index.ts +2 -60
- package/src/mcp/mcp-oauth-provider.ts +90 -8
- package/src/media/app-icon-generator.ts +86 -0
- package/src/memory/db-init.ts +11 -0
- package/src/memory/llm-usage-store.ts +186 -0
- package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
- package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/shared-app-links-store.ts +1 -1
- package/src/messaging/registry.ts +27 -0
- package/src/notifications/README.md +79 -70
- package/src/notifications/broadcaster.ts +2 -1
- package/src/notifications/conversation-pairing.ts +147 -13
- package/src/notifications/copy-composer.ts +7 -3
- package/src/notifications/destination-resolver.ts +14 -1
- package/src/notifications/emit-signal.ts +3 -2
- package/src/notifications/signal.ts +105 -1
- package/src/notifications/types.ts +16 -0
- package/src/permissions/checker.ts +29 -3
- package/src/permissions/prompter.ts +11 -3
- package/src/runtime/access-request-helper.ts +2 -1
- package/src/runtime/auth/route-policy.ts +7 -1
- package/src/runtime/channel-invite-transport.ts +40 -63
- package/src/runtime/channel-invite-transports/email.ts +13 -39
- package/src/runtime/channel-invite-transports/slack.ts +5 -34
- package/src/runtime/channel-invite-transports/sms.ts +8 -29
- package/src/runtime/channel-invite-transports/telegram.ts +69 -28
- package/src/runtime/channel-invite-transports/voice.ts +0 -7
- package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
- package/src/runtime/channel-readiness-service.ts +202 -45
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
- package/src/runtime/guardian-outbound-actions.ts +8 -5
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/invite-instruction-generator.ts +178 -0
- package/src/runtime/invite-service.ts +22 -25
- package/src/runtime/migrations/migration-transport.ts +13 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
- package/src/runtime/routes/channel-readiness-routes.ts +30 -11
- package/src/runtime/routes/contact-routes.ts +54 -26
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +1 -1
- package/src/runtime/routes/invite-routes.ts +1 -1
- package/src/runtime/routes/secret-routes.ts +31 -7
- package/src/runtime/routes/twilio-routes.ts +32 -1
- package/src/runtime/routes/usage-routes.ts +114 -0
- package/src/runtime/tool-grant-request-helper.ts +2 -1
- package/src/security/encrypted-store.ts +9 -5
- package/src/security/keychain-broker-client.ts +393 -0
- package/src/security/secure-keys.ts +106 -321
- package/src/tools/apps/executors.ts +73 -0
- package/src/tools/browser/auto-navigate.ts +15 -6
- package/src/tools/browser/chrome-cdp.ts +211 -0
- package/src/tools/browser/network-recorder.test.ts +83 -0
- package/src/tools/browser/network-recorder.ts +8 -7
- package/src/tools/browser/x-auto-navigate.ts +12 -6
- package/src/tools/credentials/policy-types.ts +24 -0
- package/src/tools/credentials/vault.ts +22 -27
- package/src/tools/network/script-proxy/session-manager.ts +47 -3
- package/src/tools/permission-checker.ts +1 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +1 -2
- package/src/tools/watch/watch-state.ts +2 -0
- package/src/__tests__/key-migration.test.ts +0 -240
- package/src/__tests__/keychain.test.ts +0 -286
- package/src/cli/core-commands.ts +0 -899
- package/src/security/keychain-to-encrypted-migration.ts +0 -66
- package/src/security/keychain.ts +0 -490
|
@@ -195,21 +195,24 @@ beforeEach(() => {
|
|
|
195
195
|
// ===========================================================================
|
|
196
196
|
|
|
197
197
|
describe("startOutbound", () => {
|
|
198
|
-
test("SMS: returns missing_destination when destination is absent", () => {
|
|
199
|
-
const result = startOutbound({ channel: "sms" });
|
|
198
|
+
test("SMS: returns missing_destination when destination is absent", async () => {
|
|
199
|
+
const result = await startOutbound({ channel: "sms" });
|
|
200
200
|
expect(result.success).toBe(false);
|
|
201
201
|
expect(result.error).toBe("missing_destination");
|
|
202
202
|
expect(result.channel).toBe("sms");
|
|
203
203
|
});
|
|
204
204
|
|
|
205
|
-
test("SMS: returns invalid_destination for garbage phone number", () => {
|
|
206
|
-
const result = startOutbound({
|
|
205
|
+
test("SMS: returns invalid_destination for garbage phone number", async () => {
|
|
206
|
+
const result = await startOutbound({
|
|
207
|
+
channel: "sms",
|
|
208
|
+
destination: "notaphone",
|
|
209
|
+
});
|
|
207
210
|
expect(result.success).toBe(false);
|
|
208
211
|
expect(result.error).toBe("invalid_destination");
|
|
209
212
|
});
|
|
210
213
|
|
|
211
|
-
test("SMS: succeeds with valid E.164 number", () => {
|
|
212
|
-
const result = startOutbound({
|
|
214
|
+
test("SMS: succeeds with valid E.164 number", async () => {
|
|
215
|
+
const result = await startOutbound({
|
|
213
216
|
channel: "sms",
|
|
214
217
|
destination: "+15551234567",
|
|
215
218
|
});
|
|
@@ -222,8 +225,8 @@ describe("startOutbound", () => {
|
|
|
222
225
|
expect(result.channel).toBe("sms");
|
|
223
226
|
});
|
|
224
227
|
|
|
225
|
-
test("SMS: succeeds with loose phone format (parentheses + dashes)", () => {
|
|
226
|
-
const result = startOutbound({
|
|
228
|
+
test("SMS: succeeds with loose phone format (parentheses + dashes)", async () => {
|
|
229
|
+
const result = await startOutbound({
|
|
227
230
|
channel: "sms",
|
|
228
231
|
destination: "(555) 987-6543",
|
|
229
232
|
});
|
|
@@ -231,14 +234,14 @@ describe("startOutbound", () => {
|
|
|
231
234
|
expect(result.verificationSessionId).toBeDefined();
|
|
232
235
|
});
|
|
233
236
|
|
|
234
|
-
test("Telegram: returns missing_destination when absent", () => {
|
|
235
|
-
const result = startOutbound({ channel: "telegram" });
|
|
237
|
+
test("Telegram: returns missing_destination when absent", async () => {
|
|
238
|
+
const result = await startOutbound({ channel: "telegram" });
|
|
236
239
|
expect(result.success).toBe(false);
|
|
237
240
|
expect(result.error).toBe("missing_destination");
|
|
238
241
|
});
|
|
239
242
|
|
|
240
|
-
test("Telegram: succeeds with numeric chat ID", () => {
|
|
241
|
-
const result = startOutbound({
|
|
243
|
+
test("Telegram: succeeds with numeric chat ID", async () => {
|
|
244
|
+
const result = await startOutbound({
|
|
242
245
|
channel: "telegram",
|
|
243
246
|
destination: "123456789",
|
|
244
247
|
});
|
|
@@ -248,8 +251,8 @@ describe("startOutbound", () => {
|
|
|
248
251
|
expect(result.sendCount).toBe(1);
|
|
249
252
|
});
|
|
250
253
|
|
|
251
|
-
test("Telegram: returns invalid_destination for negative (group) chat ID", () => {
|
|
252
|
-
const result = startOutbound({
|
|
254
|
+
test("Telegram: returns invalid_destination for negative (group) chat ID", async () => {
|
|
255
|
+
const result = await startOutbound({
|
|
253
256
|
channel: "telegram",
|
|
254
257
|
destination: "-100123456",
|
|
255
258
|
});
|
|
@@ -257,8 +260,8 @@ describe("startOutbound", () => {
|
|
|
257
260
|
expect(result.error).toBe("invalid_destination");
|
|
258
261
|
});
|
|
259
262
|
|
|
260
|
-
test("Telegram: returns pending_bootstrap for handle destination", () => {
|
|
261
|
-
const result = startOutbound({
|
|
263
|
+
test("Telegram: returns pending_bootstrap for handle destination", async () => {
|
|
264
|
+
const result = await startOutbound({
|
|
262
265
|
channel: "telegram",
|
|
263
266
|
destination: "@someuser",
|
|
264
267
|
});
|
|
@@ -270,9 +273,9 @@ describe("startOutbound", () => {
|
|
|
270
273
|
expect(result.secret).toBeUndefined();
|
|
271
274
|
});
|
|
272
275
|
|
|
273
|
-
test("Telegram: returns no_bot_username when bot not configured", () => {
|
|
276
|
+
test("Telegram: returns no_bot_username when bot not configured", async () => {
|
|
274
277
|
mockBotUsername = undefined;
|
|
275
|
-
const result = startOutbound({
|
|
278
|
+
const result = await startOutbound({
|
|
276
279
|
channel: "telegram",
|
|
277
280
|
destination: "@someuser",
|
|
278
281
|
});
|
|
@@ -280,20 +283,23 @@ describe("startOutbound", () => {
|
|
|
280
283
|
expect(result.error).toBe("no_bot_username");
|
|
281
284
|
});
|
|
282
285
|
|
|
283
|
-
test("voice: returns missing_destination when absent", () => {
|
|
284
|
-
const result = startOutbound({ channel: "voice" });
|
|
286
|
+
test("voice: returns missing_destination when absent", async () => {
|
|
287
|
+
const result = await startOutbound({ channel: "voice" });
|
|
285
288
|
expect(result.success).toBe(false);
|
|
286
289
|
expect(result.error).toBe("missing_destination");
|
|
287
290
|
});
|
|
288
291
|
|
|
289
|
-
test("voice: returns invalid_destination for garbage", () => {
|
|
290
|
-
const result = startOutbound({
|
|
292
|
+
test("voice: returns invalid_destination for garbage", async () => {
|
|
293
|
+
const result = await startOutbound({
|
|
294
|
+
channel: "voice",
|
|
295
|
+
destination: "badphone",
|
|
296
|
+
});
|
|
291
297
|
expect(result.success).toBe(false);
|
|
292
298
|
expect(result.error).toBe("invalid_destination");
|
|
293
299
|
});
|
|
294
300
|
|
|
295
|
-
test("voice: succeeds with valid phone", () => {
|
|
296
|
-
const result = startOutbound({
|
|
301
|
+
test("voice: succeeds with valid phone", async () => {
|
|
302
|
+
const result = await startOutbound({
|
|
297
303
|
channel: "voice",
|
|
298
304
|
destination: "+15559876543",
|
|
299
305
|
});
|
|
@@ -303,9 +309,9 @@ describe("startOutbound", () => {
|
|
|
303
309
|
expect(result.sendCount).toBe(1);
|
|
304
310
|
});
|
|
305
311
|
|
|
306
|
-
test("voice: passes originConversationId to startGuardianVerificationCall", () => {
|
|
312
|
+
test("voice: passes originConversationId to startGuardianVerificationCall", async () => {
|
|
307
313
|
voiceCallInitCalls.length = 0;
|
|
308
|
-
const result = startOutbound({
|
|
314
|
+
const result = await startOutbound({
|
|
309
315
|
channel: "voice",
|
|
310
316
|
destination: "+15559876543",
|
|
311
317
|
originConversationId: "conv-origin-linkage-test",
|
|
@@ -316,9 +322,9 @@ describe("startOutbound", () => {
|
|
|
316
322
|
expect(voiceCallInitCalls[0].phoneNumber).toBe("+15559876543");
|
|
317
323
|
});
|
|
318
324
|
|
|
319
|
-
test("voice: succeeds without originConversationId (backward compat)", () => {
|
|
325
|
+
test("voice: succeeds without originConversationId (backward compat)", async () => {
|
|
320
326
|
voiceCallInitCalls.length = 0;
|
|
321
|
-
const result = startOutbound({
|
|
327
|
+
const result = await startOutbound({
|
|
322
328
|
channel: "voice",
|
|
323
329
|
destination: "+15551119999",
|
|
324
330
|
});
|
|
@@ -326,9 +332,9 @@ describe("startOutbound", () => {
|
|
|
326
332
|
expect(voiceCallInitCalls.length).toBe(1);
|
|
327
333
|
});
|
|
328
334
|
|
|
329
|
-
test("unsupported channel returns unsupported_channel", () => {
|
|
335
|
+
test("unsupported channel returns unsupported_channel", async () => {
|
|
330
336
|
// Cast to bypass type checking for test purposes
|
|
331
|
-
const result = startOutbound({ channel: "email" as never });
|
|
337
|
+
const result = await startOutbound({ channel: "email" as never });
|
|
332
338
|
expect(result.success).toBe(false);
|
|
333
339
|
expect(result.error).toBe("unsupported_channel");
|
|
334
340
|
});
|
|
@@ -345,9 +351,9 @@ describe("resendOutbound", () => {
|
|
|
345
351
|
expect(result.error).toBe("no_active_session");
|
|
346
352
|
});
|
|
347
353
|
|
|
348
|
-
test("SMS: succeeds when an active session exists and cooldown has passed", () => {
|
|
354
|
+
test("SMS: succeeds when an active session exists and cooldown has passed", async () => {
|
|
349
355
|
// Start a session first
|
|
350
|
-
const startResult = startOutbound({
|
|
356
|
+
const startResult = await startOutbound({
|
|
351
357
|
channel: "sms",
|
|
352
358
|
destination: "+15551112222",
|
|
353
359
|
});
|
|
@@ -369,8 +375,8 @@ describe("resendOutbound", () => {
|
|
|
369
375
|
expect(resendResult.sendCount).toBe(2);
|
|
370
376
|
});
|
|
371
377
|
|
|
372
|
-
test("SMS: preserves originConversationId on resend", () => {
|
|
373
|
-
const startResult = startOutbound({
|
|
378
|
+
test("SMS: preserves originConversationId on resend", async () => {
|
|
379
|
+
const startResult = await startOutbound({
|
|
374
380
|
channel: "sms",
|
|
375
381
|
destination: "+15551113333",
|
|
376
382
|
});
|
|
@@ -395,7 +401,7 @@ describe("resendOutbound", () => {
|
|
|
395
401
|
|
|
396
402
|
test("voice: preserves originConversationId on resend and passes it to call initiation", async () => {
|
|
397
403
|
voiceCallInitCalls.length = 0;
|
|
398
|
-
const startResult = startOutbound({
|
|
404
|
+
const startResult = await startOutbound({
|
|
399
405
|
channel: "voice",
|
|
400
406
|
destination: "+15559991111",
|
|
401
407
|
});
|
|
@@ -440,8 +446,8 @@ describe("cancelOutbound", () => {
|
|
|
440
446
|
expect(result.error).toBe("no_active_session");
|
|
441
447
|
});
|
|
442
448
|
|
|
443
|
-
test("succeeds when an active session exists", () => {
|
|
444
|
-
const startResult = startOutbound({
|
|
449
|
+
test("succeeds when an active session exists", async () => {
|
|
450
|
+
const startResult = await startOutbound({
|
|
445
451
|
channel: "sms",
|
|
446
452
|
destination: "+15553334444",
|
|
447
453
|
});
|
|
@@ -582,8 +588,8 @@ describe("HTTP route: handleCancelOutbound", () => {
|
|
|
582
588
|
// ===========================================================================
|
|
583
589
|
|
|
584
590
|
describe("origin conversation linkage", () => {
|
|
585
|
-
test("startOutbound SMS echoes originConversationId in result", () => {
|
|
586
|
-
const result = startOutbound({
|
|
591
|
+
test("startOutbound SMS echoes originConversationId in result", async () => {
|
|
592
|
+
const result = await startOutbound({
|
|
587
593
|
channel: "sms",
|
|
588
594
|
destination: "+15551119999",
|
|
589
595
|
originConversationId: "conv-origin-sms-test",
|
|
@@ -592,8 +598,8 @@ describe("origin conversation linkage", () => {
|
|
|
592
598
|
expect(result.originConversationId).toBe("conv-origin-sms-test");
|
|
593
599
|
});
|
|
594
600
|
|
|
595
|
-
test("startOutbound voice echoes originConversationId in result", () => {
|
|
596
|
-
const result = startOutbound({
|
|
601
|
+
test("startOutbound voice echoes originConversationId in result", async () => {
|
|
602
|
+
const result = await startOutbound({
|
|
597
603
|
channel: "voice",
|
|
598
604
|
destination: "+15552229999",
|
|
599
605
|
originConversationId: "conv-origin-voice-test",
|
|
@@ -602,8 +608,8 @@ describe("origin conversation linkage", () => {
|
|
|
602
608
|
expect(result.originConversationId).toBe("conv-origin-voice-test");
|
|
603
609
|
});
|
|
604
610
|
|
|
605
|
-
test("startOutbound Telegram (chat ID) echoes originConversationId in result", () => {
|
|
606
|
-
const result = startOutbound({
|
|
611
|
+
test("startOutbound Telegram (chat ID) echoes originConversationId in result", async () => {
|
|
612
|
+
const result = await startOutbound({
|
|
607
613
|
channel: "telegram",
|
|
608
614
|
destination: "999888777",
|
|
609
615
|
originConversationId: "conv-origin-tg-test",
|
|
@@ -612,8 +618,8 @@ describe("origin conversation linkage", () => {
|
|
|
612
618
|
expect(result.originConversationId).toBe("conv-origin-tg-test");
|
|
613
619
|
});
|
|
614
620
|
|
|
615
|
-
test("startOutbound without originConversationId returns undefined for field", () => {
|
|
616
|
-
const result = startOutbound({
|
|
621
|
+
test("startOutbound without originConversationId returns undefined for field", async () => {
|
|
622
|
+
const result = await startOutbound({
|
|
617
623
|
channel: "sms",
|
|
618
624
|
destination: "+15553338888",
|
|
619
625
|
});
|
|
@@ -635,7 +641,7 @@ describe("origin conversation linkage", () => {
|
|
|
635
641
|
});
|
|
636
642
|
|
|
637
643
|
test("voice call initiation receives originConversationId", async () => {
|
|
638
|
-
const result = startOutbound({
|
|
644
|
+
const result = await startOutbound({
|
|
639
645
|
channel: "voice",
|
|
640
646
|
destination: "+15554443333",
|
|
641
647
|
originConversationId: "conv-origin-voice-init",
|
|
@@ -656,7 +662,7 @@ describe("origin conversation linkage", () => {
|
|
|
656
662
|
voiceCallInitCalls.length = 0;
|
|
657
663
|
|
|
658
664
|
// Start a voice session (no origin initially)
|
|
659
|
-
const startResult = startOutbound({
|
|
665
|
+
const startResult = await startOutbound({
|
|
660
666
|
channel: "voice",
|
|
661
667
|
destination: "+15552228888",
|
|
662
668
|
});
|
|
@@ -40,10 +40,33 @@ mock.module("../config/loader.js", () => {
|
|
|
40
40
|
applyNestedDefaults: (c: unknown) => c,
|
|
41
41
|
getNestedValue: () => undefined,
|
|
42
42
|
setNestedValue: () => {},
|
|
43
|
+
syncConfigToLockfile: () => {},
|
|
43
44
|
API_KEY_PROVIDERS: [],
|
|
44
45
|
};
|
|
45
46
|
});
|
|
46
47
|
|
|
48
|
+
const noopLogger = {
|
|
49
|
+
info: () => {},
|
|
50
|
+
warn: () => {},
|
|
51
|
+
error: () => {},
|
|
52
|
+
debug: () => {},
|
|
53
|
+
trace: () => {},
|
|
54
|
+
fatal: () => {},
|
|
55
|
+
child: () => noopLogger,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
59
|
+
const realLogger = require("../util/logger.js");
|
|
60
|
+
mock.module("../util/logger.js", () => ({
|
|
61
|
+
...realLogger,
|
|
62
|
+
getLogger: () => noopLogger,
|
|
63
|
+
getCliLogger: () => noopLogger,
|
|
64
|
+
isDebug: () => false,
|
|
65
|
+
truncateForLog: (v: string) => v,
|
|
66
|
+
initLogger: () => {},
|
|
67
|
+
pruneOldLogFiles: () => 0,
|
|
68
|
+
}));
|
|
69
|
+
|
|
47
70
|
const { handleUserMessage } = await import("../daemon/handlers/sessions.js");
|
|
48
71
|
|
|
49
72
|
describe("handleUserMessage secret redirect continuation", () => {
|
|
@@ -6,41 +6,50 @@ import { beforeEach, describe, expect, test } from "bun:test";
|
|
|
6
6
|
import { mock } from "bun:test";
|
|
7
7
|
|
|
8
8
|
const testDir = mkdtempSync(join(tmpdir(), "trust-rule-metadata-test-"));
|
|
9
|
+
const resolveTestDir = () => process.env.BASE_DATA_DIR ?? testDir;
|
|
9
10
|
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
12
|
+
const realPlatform = require("../util/platform.js");
|
|
10
13
|
mock.module("../util/platform.js", () => ({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
...realPlatform,
|
|
15
|
+
getRootDir: () => resolveTestDir(),
|
|
16
|
+
getDataDir: () => join(resolveTestDir(), "data"),
|
|
17
|
+
getWorkspaceSkillsDir: () => join(resolveTestDir(), "skills"),
|
|
14
18
|
isMacOS: () => process.platform === "darwin",
|
|
15
19
|
isLinux: () => process.platform === "linux",
|
|
16
20
|
isWindows: () => process.platform === "win32",
|
|
17
|
-
getSocketPath: () => join(
|
|
18
|
-
getPidPath: () => join(
|
|
19
|
-
getDbPath: () => join(
|
|
20
|
-
getLogPath: () => join(
|
|
21
|
+
getSocketPath: () => join(resolveTestDir(), "test.sock"),
|
|
22
|
+
getPidPath: () => join(resolveTestDir(), "test.pid"),
|
|
23
|
+
getDbPath: () => join(resolveTestDir(), "test.db"),
|
|
24
|
+
getLogPath: () => join(resolveTestDir(), "test.log"),
|
|
21
25
|
ensureDataDir: () => {},
|
|
22
|
-
getIpcBlobDir: () => join(
|
|
26
|
+
getIpcBlobDir: () => join(resolveTestDir(), "ipc-blobs"),
|
|
23
27
|
}));
|
|
24
28
|
|
|
29
|
+
const noopLogger = {
|
|
30
|
+
info: () => {},
|
|
31
|
+
warn: () => {},
|
|
32
|
+
error: () => {},
|
|
33
|
+
debug: () => {},
|
|
34
|
+
trace: () => {},
|
|
35
|
+
fatal: () => {},
|
|
36
|
+
child: () => noopLogger,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
40
|
+
const realLogger = require("../util/logger.js");
|
|
25
41
|
mock.module("../util/logger.js", () => ({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
child: () => ({
|
|
34
|
-
info: () => {},
|
|
35
|
-
warn: () => {},
|
|
36
|
-
error: () => {},
|
|
37
|
-
debug: () => {},
|
|
38
|
-
}),
|
|
39
|
-
}),
|
|
42
|
+
...realLogger,
|
|
43
|
+
getLogger: () => noopLogger,
|
|
44
|
+
getCliLogger: () => noopLogger,
|
|
45
|
+
isDebug: () => false,
|
|
46
|
+
truncateForLog: (value: string) => value,
|
|
47
|
+
initLogger: () => {},
|
|
48
|
+
pruneOldLogFiles: () => 0,
|
|
40
49
|
}));
|
|
41
50
|
|
|
42
51
|
const testConfig: Record<string, any> = {
|
|
43
|
-
permissions: { mode: "
|
|
52
|
+
permissions: { mode: "workspace" as "strict" | "workspace" },
|
|
44
53
|
skills: { load: { extraDirs: [] as string[] } },
|
|
45
54
|
sandbox: { enabled: true },
|
|
46
55
|
};
|
|
@@ -22,7 +22,10 @@ mock.module("../config/loader.js", () => ({
|
|
|
22
22
|
invalidateConfigCache: () => {},
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
26
|
+
const realPlatform = require("../util/platform.js");
|
|
25
27
|
mock.module("../util/platform.js", () => ({
|
|
28
|
+
...realPlatform,
|
|
26
29
|
getRootDir: () => testDir,
|
|
27
30
|
getDataDir: () => testDir,
|
|
28
31
|
getIpcBlobDir: () => join(testDir, "ipc-blobs"),
|
|
@@ -36,7 +39,10 @@ mock.module("../util/platform.js", () => ({
|
|
|
36
39
|
ensureDataDir: () => {},
|
|
37
40
|
}));
|
|
38
41
|
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
43
|
+
const realLogger = require("../util/logger.js");
|
|
39
44
|
mock.module("../util/logger.js", () => ({
|
|
45
|
+
...realLogger,
|
|
40
46
|
getLogger: () => ({
|
|
41
47
|
info: () => {},
|
|
42
48
|
warn: () => {},
|
|
@@ -70,9 +76,9 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
70
76
|
deleteSecureKey: (account: string) => {
|
|
71
77
|
if (account in secureKeyStore) {
|
|
72
78
|
delete secureKeyStore[account];
|
|
73
|
-
return
|
|
79
|
+
return "deleted";
|
|
74
80
|
}
|
|
75
|
-
return
|
|
81
|
+
return "not-found";
|
|
76
82
|
},
|
|
77
83
|
listSecureKeys: () => Object.keys(secureKeyStore),
|
|
78
84
|
getBackendType: () => "encrypted",
|
|
@@ -71,9 +71,9 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
71
71
|
deleteSecureKey: (account: string) => {
|
|
72
72
|
if (account in secureKeyStore) {
|
|
73
73
|
delete secureKeyStore[account];
|
|
74
|
-
return
|
|
74
|
+
return "deleted";
|
|
75
75
|
}
|
|
76
|
-
return
|
|
76
|
+
return "not-found";
|
|
77
77
|
},
|
|
78
78
|
listSecureKeys: () => Object.keys(secureKeyStore),
|
|
79
79
|
getBackendType: () => "encrypted",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as net from "node:net";
|
|
2
|
-
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
3
3
|
|
|
4
4
|
mock.module("../config/env.js", () => ({ isHttpAuthDisabled: () => true }));
|
|
5
5
|
|
|
@@ -47,13 +47,102 @@ mock.module("../runtime/guardian-reply-router.js", () => ({
|
|
|
47
47
|
routeGuardianReply: routeGuardianReplyMock,
|
|
48
48
|
}));
|
|
49
49
|
|
|
50
|
+
// Bun's module mocks are global within the worker, so keep this mock
|
|
51
|
+
// transparent when this file is not actively exercising it.
|
|
52
|
+
const realCanonicalGuardianStore =
|
|
53
|
+
await import("../memory/canonical-guardian-store.js");
|
|
54
|
+
(
|
|
55
|
+
globalThis as Record<string, unknown>
|
|
56
|
+
).__approvalConsumptionUseMockCanonicalStore = false;
|
|
57
|
+
|
|
50
58
|
mock.module("../memory/canonical-guardian-store.js", () => ({
|
|
51
|
-
createCanonicalGuardianRequest:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
createCanonicalGuardianRequest: (
|
|
60
|
+
...args: Parameters<
|
|
61
|
+
typeof realCanonicalGuardianStore.createCanonicalGuardianRequest
|
|
62
|
+
>
|
|
63
|
+
) =>
|
|
64
|
+
(globalThis as Record<string, unknown>)
|
|
65
|
+
.__approvalConsumptionUseMockCanonicalStore
|
|
66
|
+
? (
|
|
67
|
+
createCanonicalGuardianRequestMock as unknown as (
|
|
68
|
+
...mockArgs: Parameters<
|
|
69
|
+
typeof realCanonicalGuardianStore.createCanonicalGuardianRequest
|
|
70
|
+
>
|
|
71
|
+
) => ReturnType<
|
|
72
|
+
typeof realCanonicalGuardianStore.createCanonicalGuardianRequest
|
|
73
|
+
>
|
|
74
|
+
)(...args)
|
|
75
|
+
: realCanonicalGuardianStore.createCanonicalGuardianRequest(...args),
|
|
76
|
+
generateCanonicalRequestCode: (
|
|
77
|
+
...args: Parameters<
|
|
78
|
+
typeof realCanonicalGuardianStore.generateCanonicalRequestCode
|
|
79
|
+
>
|
|
80
|
+
) =>
|
|
81
|
+
(globalThis as Record<string, unknown>)
|
|
82
|
+
.__approvalConsumptionUseMockCanonicalStore
|
|
83
|
+
? (
|
|
84
|
+
generateCanonicalRequestCodeMock as unknown as (
|
|
85
|
+
...mockArgs: Parameters<
|
|
86
|
+
typeof realCanonicalGuardianStore.generateCanonicalRequestCode
|
|
87
|
+
>
|
|
88
|
+
) => ReturnType<
|
|
89
|
+
typeof realCanonicalGuardianStore.generateCanonicalRequestCode
|
|
90
|
+
>
|
|
91
|
+
)(...args)
|
|
92
|
+
: realCanonicalGuardianStore.generateCanonicalRequestCode(...args),
|
|
93
|
+
listPendingCanonicalGuardianRequestsByDestinationConversation: (
|
|
94
|
+
...args: Parameters<
|
|
95
|
+
typeof realCanonicalGuardianStore.listPendingCanonicalGuardianRequestsByDestinationConversation
|
|
96
|
+
>
|
|
97
|
+
) =>
|
|
98
|
+
(globalThis as Record<string, unknown>)
|
|
99
|
+
.__approvalConsumptionUseMockCanonicalStore
|
|
100
|
+
? (
|
|
101
|
+
listPendingByDestinationMock as unknown as (
|
|
102
|
+
...mockArgs: Parameters<
|
|
103
|
+
typeof realCanonicalGuardianStore.listPendingCanonicalGuardianRequestsByDestinationConversation
|
|
104
|
+
>
|
|
105
|
+
) => ReturnType<
|
|
106
|
+
typeof realCanonicalGuardianStore.listPendingCanonicalGuardianRequestsByDestinationConversation
|
|
107
|
+
>
|
|
108
|
+
)(...args)
|
|
109
|
+
: realCanonicalGuardianStore.listPendingCanonicalGuardianRequestsByDestinationConversation(
|
|
110
|
+
...args,
|
|
111
|
+
),
|
|
112
|
+
listCanonicalGuardianRequests: (
|
|
113
|
+
...args: Parameters<
|
|
114
|
+
typeof realCanonicalGuardianStore.listCanonicalGuardianRequests
|
|
115
|
+
>
|
|
116
|
+
) =>
|
|
117
|
+
(globalThis as Record<string, unknown>)
|
|
118
|
+
.__approvalConsumptionUseMockCanonicalStore
|
|
119
|
+
? (
|
|
120
|
+
listCanonicalMock as unknown as (
|
|
121
|
+
...mockArgs: Parameters<
|
|
122
|
+
typeof realCanonicalGuardianStore.listCanonicalGuardianRequests
|
|
123
|
+
>
|
|
124
|
+
) => ReturnType<
|
|
125
|
+
typeof realCanonicalGuardianStore.listCanonicalGuardianRequests
|
|
126
|
+
>
|
|
127
|
+
)(...args)
|
|
128
|
+
: realCanonicalGuardianStore.listCanonicalGuardianRequests(...args),
|
|
129
|
+
resolveCanonicalGuardianRequest: (
|
|
130
|
+
...args: Parameters<
|
|
131
|
+
typeof realCanonicalGuardianStore.resolveCanonicalGuardianRequest
|
|
132
|
+
>
|
|
133
|
+
) =>
|
|
134
|
+
(globalThis as Record<string, unknown>)
|
|
135
|
+
.__approvalConsumptionUseMockCanonicalStore
|
|
136
|
+
? (
|
|
137
|
+
resolveCanonicalGuardianRequestMock as unknown as (
|
|
138
|
+
...mockArgs: Parameters<
|
|
139
|
+
typeof realCanonicalGuardianStore.resolveCanonicalGuardianRequest
|
|
140
|
+
>
|
|
141
|
+
) => ReturnType<
|
|
142
|
+
typeof realCanonicalGuardianStore.resolveCanonicalGuardianRequest
|
|
143
|
+
>
|
|
144
|
+
)(...args)
|
|
145
|
+
: realCanonicalGuardianStore.resolveCanonicalGuardianRequest(...args),
|
|
57
146
|
}));
|
|
58
147
|
|
|
59
148
|
mock.module("../runtime/pending-interactions.js", () => ({
|
|
@@ -98,7 +187,10 @@ mock.module("../runtime/local-actor-identity.js", () => ({
|
|
|
98
187
|
}),
|
|
99
188
|
}));
|
|
100
189
|
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
191
|
+
const realLogger = require("../util/logger.js");
|
|
101
192
|
mock.module("../util/logger.js", () => ({
|
|
193
|
+
...realLogger,
|
|
102
194
|
getLogger: () => ({
|
|
103
195
|
info: () => {},
|
|
104
196
|
warn: () => {},
|
|
@@ -214,6 +306,9 @@ function makeSession(overrides: Partial<TestSession> = {}): TestSession {
|
|
|
214
306
|
|
|
215
307
|
describe("handleUserMessage pending-confirmation reply interception", () => {
|
|
216
308
|
beforeEach(() => {
|
|
309
|
+
(
|
|
310
|
+
globalThis as Record<string, unknown>
|
|
311
|
+
).__approvalConsumptionUseMockCanonicalStore = true;
|
|
217
312
|
routeGuardianReplyMock.mockClear();
|
|
218
313
|
createCanonicalGuardianRequestMock.mockClear();
|
|
219
314
|
generateCanonicalRequestCodeMock.mockClear();
|
|
@@ -227,6 +322,12 @@ describe("handleUserMessage pending-confirmation reply interception", () => {
|
|
|
227
322
|
getConfigMock.mockClear();
|
|
228
323
|
});
|
|
229
324
|
|
|
325
|
+
afterAll(() => {
|
|
326
|
+
(
|
|
327
|
+
globalThis as Record<string, unknown>
|
|
328
|
+
).__approvalConsumptionUseMockCanonicalStore = false;
|
|
329
|
+
});
|
|
330
|
+
|
|
230
331
|
test("consumes decision replies before auto-deny", async () => {
|
|
231
332
|
listPendingByDestinationMock.mockReturnValue([
|
|
232
333
|
{ id: "req-1", kind: "tool_approval" },
|
|
@@ -24,7 +24,10 @@ mock.module("../config/loader.js", () => ({
|
|
|
24
24
|
invalidateConfigCache: () => {},
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
28
|
+
const realPlatform = require("../util/platform.js");
|
|
27
29
|
mock.module("../util/platform.js", () => ({
|
|
30
|
+
...realPlatform,
|
|
28
31
|
getRootDir: () => testDir,
|
|
29
32
|
getDataDir: () => testDir,
|
|
30
33
|
getIpcBlobDir: () => join(testDir, "ipc-blobs"),
|
|
@@ -38,7 +41,10 @@ mock.module("../util/platform.js", () => ({
|
|
|
38
41
|
ensureDataDir: () => {},
|
|
39
42
|
}));
|
|
40
43
|
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
45
|
+
const realLogger = require("../util/logger.js");
|
|
41
46
|
mock.module("../util/logger.js", () => ({
|
|
47
|
+
...realLogger,
|
|
42
48
|
getLogger: () => ({
|
|
43
49
|
info: () => {},
|
|
44
50
|
warn: () => {},
|
|
@@ -6,7 +6,10 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
6
6
|
// ── Mock platform to isolate tests from the real workspace ────────────
|
|
7
7
|
const TEST_DIR = join(tmpdir(), `vellum-routing-test-${crypto.randomUUID()}`);
|
|
8
8
|
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
10
|
+
const realPlatform = require("../util/platform.js");
|
|
9
11
|
mock.module("../util/platform.js", () => ({
|
|
12
|
+
...realPlatform,
|
|
10
13
|
getRootDir: () => TEST_DIR,
|
|
11
14
|
getDataDir: () => TEST_DIR,
|
|
12
15
|
getWorkspaceDir: () => TEST_DIR,
|
|
@@ -30,19 +33,27 @@ mock.module("../util/platform.js", () => ({
|
|
|
30
33
|
isWindows: () => process.platform === "win32",
|
|
31
34
|
getPlatformName: () => process.platform,
|
|
32
35
|
getClipboardCommand: () => null,
|
|
36
|
+
readSessionToken: () => null,
|
|
33
37
|
removeSocketFile: () => {},
|
|
34
38
|
migratePath: () => {},
|
|
35
39
|
migrateToWorkspaceLayout: () => {},
|
|
36
40
|
migrateToDataLayout: () => {},
|
|
37
41
|
}));
|
|
38
42
|
|
|
43
|
+
const noopLogger = new Proxy({} as Record<string, unknown>, {
|
|
44
|
+
get: (_target, prop) => (prop === "child" ? () => noopLogger : () => {}),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
48
|
+
const realLogger = require("../util/logger.js");
|
|
39
49
|
mock.module("../util/logger.js", () => ({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}),
|
|
50
|
+
...realLogger,
|
|
51
|
+
getLogger: () => noopLogger,
|
|
52
|
+
getCliLogger: () => noopLogger,
|
|
44
53
|
isDebug: () => false,
|
|
45
54
|
truncateForLog: (v: string) => v,
|
|
55
|
+
initLogger: () => {},
|
|
56
|
+
pruneOldLogFiles: () => 0,
|
|
46
57
|
}));
|
|
47
58
|
|
|
48
59
|
mock.module("../config/loader.js", () => ({
|
|
@@ -54,6 +65,14 @@ mock.module("../config/loader.js", () => ({
|
|
|
54
65
|
"feature_flags.guardian-verify-setup.enabled": true,
|
|
55
66
|
},
|
|
56
67
|
}),
|
|
68
|
+
loadConfig: () => ({}),
|
|
69
|
+
loadRawConfig: () => ({}),
|
|
70
|
+
saveConfig: () => {},
|
|
71
|
+
saveRawConfig: () => {},
|
|
72
|
+
invalidateConfigCache: () => {},
|
|
73
|
+
getNestedValue: () => undefined,
|
|
74
|
+
setNestedValue: () => {},
|
|
75
|
+
syncConfigToLockfile: () => {},
|
|
57
76
|
}));
|
|
58
77
|
|
|
59
78
|
// ── Import after mocks ───────────────────────────────────────────────
|
|
@@ -24,6 +24,18 @@ mock.module("../util/logger.js", () => ({
|
|
|
24
24
|
}),
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
+
// Prevent ensureTelegramBotUsernameResolved() from reading real credentials
|
|
28
|
+
// and calling the Telegram API, which would populate credential metadata
|
|
29
|
+
// with the real bot username and shadow the env var override in tests.
|
|
30
|
+
mock.module("../security/secure-keys.js", () => ({
|
|
31
|
+
getSecureKey: () => undefined,
|
|
32
|
+
setSecureKey: () => {},
|
|
33
|
+
deleteSecureKey: () => {},
|
|
34
|
+
getSecureKeyAsync: async () => undefined,
|
|
35
|
+
setSecureKeyAsync: async () => {},
|
|
36
|
+
deleteSecureKeyAsync: async () => {},
|
|
37
|
+
}));
|
|
38
|
+
|
|
27
39
|
import { getSqlite, initializeDb, resetDb } from "../memory/db.js";
|
|
28
40
|
import {
|
|
29
41
|
handleCreateInvite,
|