@vellumai/assistant 0.4.23 → 0.4.25

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.
Files changed (60) hide show
  1. package/bun.lock +3 -0
  2. package/package.json +2 -1
  3. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -15
  4. package/src/__tests__/assistant-events-sse-hardening.test.ts +9 -3
  5. package/src/__tests__/config-schema.test.ts +38 -178
  6. package/src/__tests__/conversation-routes-guardian-reply.test.ts +4 -1
  7. package/src/__tests__/credential-security-invariants.test.ts +0 -2
  8. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +2 -2
  9. package/src/__tests__/ipc-snapshot.test.ts +0 -9
  10. package/src/__tests__/onboarding-template-contract.test.ts +10 -20
  11. package/src/__tests__/relay-server.test.ts +3 -3
  12. package/src/__tests__/runtime-events-sse-parity.test.ts +10 -0
  13. package/src/__tests__/runtime-events-sse.test.ts +7 -0
  14. package/src/__tests__/session-runtime-assembly.test.ts +34 -8
  15. package/src/__tests__/system-prompt.test.ts +7 -1
  16. package/src/__tests__/trusted-contact-approval-notifier.test.ts +12 -8
  17. package/src/__tests__/twilio-routes-twiml.test.ts +2 -2
  18. package/src/__tests__/twilio-routes.test.ts +2 -3
  19. package/src/__tests__/voice-quality.test.ts +21 -132
  20. package/src/calls/relay-server.ts +11 -5
  21. package/src/calls/twilio-routes.ts +4 -38
  22. package/src/calls/voice-quality.ts +7 -63
  23. package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +7 -10
  24. package/src/config/bundled-skills/messaging/SKILL.md +3 -5
  25. package/src/config/bundled-skills/phone-calls/SKILL.md +143 -82
  26. package/src/config/bundled-skills/sms-setup/SKILL.md +0 -20
  27. package/src/config/bundled-skills/twilio-setup/SKILL.md +9 -17
  28. package/src/config/bundled-skills/voice-setup/SKILL.md +36 -1
  29. package/src/config/bundled-skills/voice-setup/icon.svg +20 -0
  30. package/src/config/calls-schema.ts +3 -53
  31. package/src/config/elevenlabs-schema.ts +33 -0
  32. package/src/config/schema.ts +183 -137
  33. package/src/config/types.ts +0 -1
  34. package/src/daemon/handlers/browser.ts +1 -6
  35. package/src/daemon/ipc-contract/browser.ts +5 -14
  36. package/src/daemon/ipc-contract-inventory.json +0 -2
  37. package/src/daemon/session-agent-loop-handlers.ts +3 -0
  38. package/src/daemon/session-runtime-assembly.ts +9 -7
  39. package/src/mcp/client.ts +2 -1
  40. package/src/memory/conversation-crud.ts +339 -166
  41. package/src/runtime/routes/events-routes.ts +7 -0
  42. package/src/runtime/routes/inbound-message-handler.ts +3 -4
  43. package/src/schedule/scheduler.ts +159 -45
  44. package/src/security/secure-keys.ts +3 -3
  45. package/src/tools/browser/browser-manager.ts +72 -228
  46. package/src/tools/browser/browser-screencast.ts +0 -5
  47. package/src/tools/network/script-proxy/certs.ts +7 -237
  48. package/src/tools/network/script-proxy/connect-tunnel.ts +1 -82
  49. package/src/tools/network/script-proxy/http-forwarder.ts +2 -151
  50. package/src/tools/network/script-proxy/logging.ts +12 -196
  51. package/src/tools/network/script-proxy/mitm-handler.ts +2 -270
  52. package/src/tools/network/script-proxy/policy.ts +4 -152
  53. package/src/tools/network/script-proxy/router.ts +2 -60
  54. package/src/tools/network/script-proxy/server.ts +5 -137
  55. package/src/tools/network/script-proxy/types.ts +19 -125
  56. package/src/tools/system/voice-config.ts +23 -1
  57. package/src/util/logger.ts +4 -1
  58. package/src/__tests__/elevenlabs-config.test.ts +0 -95
  59. package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -407
  60. package/src/calls/elevenlabs-config.ts +0 -32
package/bun.lock CHANGED
@@ -11,6 +11,7 @@
11
11
  "@modelcontextprotocol/sdk": "^1.15.1",
12
12
  "@qdrant/js-client-rest": "^1.16.2",
13
13
  "@sentry/node": "^10.38.0",
14
+ "@vellumai/proxy-sidecar": "file:../proxy-sidecar",
14
15
  "agentmail": "^0.1.0",
15
16
  "archiver": "^7.0.1",
16
17
  "commander": "^13.1.0",
@@ -517,6 +518,8 @@
517
518
 
518
519
  "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg=="],
519
520
 
521
+ "@vellumai/proxy-sidecar": ["@vellumai/proxy-sidecar@file:../proxy-sidecar", { "devDependencies": { "@types/bun": "^1.2.4", "@types/node": "^25.2.2", "typescript": "^5.7.3" } }],
522
+
520
523
  "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
521
524
 
522
525
  "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.4.23",
3
+ "version": "0.4.25",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "vellum": "./src/index.ts"
@@ -27,6 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@anthropic-ai/claude-agent-sdk": "^0.2.42",
30
+ "@vellumai/proxy-sidecar": "file:../proxy-sidecar",
30
31
  "@anthropic-ai/sdk": "^0.39.0",
31
32
  "@google/genai": "^1.40.0",
32
33
  "@modelcontextprotocol/sdk": "^1.15.1",
@@ -768,14 +768,6 @@ exports[`IPC message snapshots ClientMessage types oauth_connect_start serialize
768
768
  }
769
769
  `;
770
770
 
771
- exports[`IPC message snapshots ClientMessage types browser_cdp_response serializes to expected JSON 1`] = `
772
- {
773
- "sessionId": "test-session",
774
- "success": true,
775
- "type": "browser_cdp_response",
776
- }
777
- `;
778
-
779
771
  exports[`IPC message snapshots ClientMessage types work_items_list serializes to expected JSON 1`] = `
780
772
  {
781
773
  "status": "queued",
@@ -2455,13 +2447,6 @@ exports[`IPC message snapshots ServerMessage types oauth_connect_result serializ
2455
2447
  }
2456
2448
  `;
2457
2449
 
2458
- exports[`IPC message snapshots ServerMessage types browser_cdp_request serializes to expected JSON 1`] = `
2459
- {
2460
- "sessionId": "test-session",
2461
- "type": "browser_cdp_request",
2462
- }
2463
- `;
2464
-
2465
2450
  exports[`IPC message snapshots ServerMessage types document_editor_show serializes to expected JSON 1`] = `
2466
2451
  {
2467
2452
  "initialContent": "# Hello World",
@@ -193,9 +193,15 @@ describe("SSE route — capacity limit", () => {
193
193
  expect(res2.status).toBe(200);
194
194
  expect(hub.subscriberCount()).toBe(1); // evicted 1, added 1
195
195
 
196
- // First stream should be closed by onEvict.
197
- const { done } = await reader1.read();
198
- expect(done).toBe(true);
196
+ // First stream: the immediate heartbeat was enqueued during start(),
197
+ // then eviction closed the controller. Read past any buffered data
198
+ // until the stream signals done.
199
+ let evictDone = false;
200
+ while (!evictDone) {
201
+ const result = await reader1.read();
202
+ evictDone = result.done;
203
+ }
204
+ expect(evictDone).toBe(true);
199
205
 
200
206
  ac2.abort();
201
207
  });
@@ -658,10 +658,10 @@ describe("AssistantConfigSchema", () => {
658
658
  userConsultTimeoutSeconds: 120,
659
659
  ttsPlaybackDelayMs: 3000,
660
660
  accessRequestPollIntervalMs: 500,
661
- guardianWaitUpdateInitialIntervalMs: 5000,
661
+ guardianWaitUpdateInitialIntervalMs: 15000,
662
662
  guardianWaitUpdateInitialWindowMs: 30000,
663
- guardianWaitUpdateSteadyMinIntervalMs: 7000,
664
- guardianWaitUpdateSteadyMaxIntervalMs: 10000,
663
+ guardianWaitUpdateSteadyMinIntervalMs: 20000,
664
+ guardianWaitUpdateSteadyMaxIntervalMs: 30000,
665
665
  disclosure: {
666
666
  enabled: true,
667
667
  text: 'At the very beginning of the call, introduce yourself as an assistant calling on behalf of the person you represent. Do not say "AI assistant".',
@@ -670,21 +670,8 @@ describe("AssistantConfigSchema", () => {
670
670
  denyCategories: [],
671
671
  },
672
672
  voice: {
673
- mode: "twilio_standard",
674
673
  language: "en-US",
675
674
  transcriptionProvider: "Deepgram",
676
- fallbackToStandardOnError: true,
677
- elevenlabs: {
678
- voiceId: "",
679
- voiceModelId: "",
680
- speed: 1.0,
681
- stability: 0.5,
682
- similarityBoost: 0.75,
683
- useSpeakerBoost: true,
684
- agentId: "",
685
- apiBaseUrl: "https://api.elevenlabs.io",
686
- registerCallTimeoutMs: 5000,
687
- },
688
675
  },
689
676
  callerIdentity: {
690
677
  allowPerCallOverride: true,
@@ -789,43 +776,28 @@ describe("AssistantConfigSchema", () => {
789
776
 
790
777
  test("config without calls.voice parses correctly and produces defaults", () => {
791
778
  const result = AssistantConfigSchema.parse({});
792
- expect(result.calls.voice.mode).toBe("twilio_standard");
793
779
  expect(result.calls.voice.language).toBe("en-US");
794
780
  expect(result.calls.voice.transcriptionProvider).toBe("Deepgram");
795
- expect(result.calls.voice.fallbackToStandardOnError).toBe(true);
796
- expect(result.calls.voice.elevenlabs.voiceId).toBe("");
797
- expect(result.calls.voice.elevenlabs.voiceModelId).toBe("");
798
- expect(result.calls.voice.elevenlabs.speed).toBe(1.0);
799
- expect(result.calls.voice.elevenlabs.stability).toBe(0.5);
800
- expect(result.calls.voice.elevenlabs.similarityBoost).toBe(0.75);
801
- expect(result.calls.voice.elevenlabs.useSpeakerBoost).toBe(true);
802
- expect(result.calls.voice.elevenlabs.agentId).toBe("");
803
- expect(result.calls.voice.elevenlabs.apiBaseUrl).toBe(
804
- "https://api.elevenlabs.io",
805
- );
806
- expect(result.calls.voice.elevenlabs.registerCallTimeoutMs).toBe(5000);
807
781
  });
808
782
 
809
- test("legacy style field is silently stripped by schema", () => {
810
- const result = AssistantConfigSchema.parse({
811
- calls: { voice: { elevenlabs: { style: 0.5 } } },
812
- });
813
- expect(
814
- (result.calls.voice.elevenlabs as Record<string, unknown>).style,
815
- ).toBeUndefined();
816
- expect(result.calls.voice.elevenlabs.speed).toBe(1.0);
783
+ test("elevenlabs tuning params have correct defaults", () => {
784
+ const result = AssistantConfigSchema.parse({});
785
+ expect(result.elevenlabs.voiceModelId).toBe("");
786
+ expect(result.elevenlabs.speed).toBe(1.0);
787
+ expect(result.elevenlabs.stability).toBe(0.5);
788
+ expect(result.elevenlabs.similarityBoost).toBe(0.75);
817
789
  });
818
790
 
819
- test("rejects calls.voice.elevenlabs.speed below 0.7", () => {
791
+ test("rejects elevenlabs.speed below 0.7", () => {
820
792
  const result = AssistantConfigSchema.safeParse({
821
- calls: { voice: { elevenlabs: { speed: 0.5 } } },
793
+ elevenlabs: { speed: 0.5 },
822
794
  });
823
795
  expect(result.success).toBe(false);
824
796
  });
825
797
 
826
- test("rejects calls.voice.elevenlabs.speed above 1.2", () => {
798
+ test("rejects elevenlabs.speed above 1.2", () => {
827
799
  const result = AssistantConfigSchema.safeParse({
828
- calls: { voice: { elevenlabs: { speed: 1.5 } } },
800
+ elevenlabs: { speed: 1.5 },
829
801
  });
830
802
  expect(result.success).toBe(false);
831
803
  });
@@ -834,37 +806,20 @@ describe("AssistantConfigSchema", () => {
834
806
  const result = AssistantConfigSchema.parse({
835
807
  calls: {
836
808
  voice: {
837
- mode: "twilio_elevenlabs_tts",
838
809
  language: "es-ES",
839
810
  transcriptionProvider: "Google",
840
- fallbackToStandardOnError: false,
841
- elevenlabs: {
842
- voiceId: "abc123",
843
- stability: 0.8,
844
- },
845
811
  },
846
812
  },
813
+ elevenlabs: {
814
+ stability: 0.8,
815
+ },
847
816
  });
848
- expect(result.calls.voice.mode).toBe("twilio_elevenlabs_tts");
849
817
  expect(result.calls.voice.language).toBe("es-ES");
850
818
  expect(result.calls.voice.transcriptionProvider).toBe("Google");
851
- expect(result.calls.voice.fallbackToStandardOnError).toBe(false);
852
- expect(result.calls.voice.elevenlabs.voiceId).toBe("abc123");
853
- expect(result.calls.voice.elevenlabs.stability).toBe(0.8);
819
+ expect(result.elevenlabs.stability).toBe(0.8);
854
820
  // Defaults preserved for unset fields
855
- expect(result.calls.voice.elevenlabs.voiceModelId).toBe("");
856
- expect(result.calls.voice.elevenlabs.similarityBoost).toBe(0.75);
857
- });
858
-
859
- test("rejects invalid calls.voice.mode", () => {
860
- const result = AssistantConfigSchema.safeParse({
861
- calls: { voice: { mode: "invalid_mode" } },
862
- });
863
- expect(result.success).toBe(false);
864
- if (!result.success) {
865
- const msgs = result.error.issues.map((i) => i.message);
866
- expect(msgs.some((m) => m.includes("calls.voice.mode"))).toBe(true);
867
- }
821
+ expect(result.elevenlabs.voiceModelId).toBe("");
822
+ expect(result.elevenlabs.similarityBoost).toBe(0.75);
868
823
  });
869
824
 
870
825
  test("rejects invalid calls.voice.transcriptionProvider", () => {
@@ -880,23 +835,9 @@ describe("AssistantConfigSchema", () => {
880
835
  }
881
836
  });
882
837
 
883
- test("rejects calls.voice.elevenlabs.stability out of range", () => {
838
+ test("rejects elevenlabs.stability out of range", () => {
884
839
  const result = AssistantConfigSchema.safeParse({
885
- calls: { voice: { elevenlabs: { stability: 1.5 } } },
886
- });
887
- expect(result.success).toBe(false);
888
- });
889
-
890
- test("rejects calls.voice.elevenlabs.registerCallTimeoutMs below 1000", () => {
891
- const result = AssistantConfigSchema.safeParse({
892
- calls: { voice: { elevenlabs: { registerCallTimeoutMs: 500 } } },
893
- });
894
- expect(result.success).toBe(false);
895
- });
896
-
897
- test("rejects calls.voice.elevenlabs.registerCallTimeoutMs above 15000", () => {
898
- const result = AssistantConfigSchema.safeParse({
899
- calls: { voice: { elevenlabs: { registerCallTimeoutMs: 20000 } } },
840
+ elevenlabs: { stability: 1.5 },
900
841
  });
901
842
  expect(result.success).toBe(false);
902
843
  });
@@ -972,114 +913,40 @@ describe("AssistantConfigSchema", () => {
972
913
  // ---------------------------------------------------------------------------
973
914
 
974
915
  describe("resolveVoiceQualityProfile", () => {
975
- test("returns correct profile for twilio_standard", () => {
916
+ test("always returns ElevenLabs ttsProvider", () => {
976
917
  const config = AssistantConfigSchema.parse({});
977
918
  const profile = resolveVoiceQualityProfile(config);
978
- expect(profile.mode).toBe("twilio_standard");
979
- expect(profile.ttsProvider).toBe("Google");
980
- expect(profile.voice).toBe("Google.en-US-Journey-O");
919
+ expect(profile.ttsProvider).toBe("ElevenLabs");
981
920
  expect(profile.transcriptionProvider).toBe("Deepgram");
982
- expect(profile.fallbackToStandardOnError).toBe(true);
983
- expect(profile.validationErrors).toEqual([]);
984
921
  });
985
922
 
986
- test("returns correct profile for twilio_elevenlabs_tts with valid voiceId", () => {
923
+ test("uses shared elevenlabs.voiceId for voice", () => {
987
924
  const config = AssistantConfigSchema.parse({
988
- calls: {
989
- voice: {
990
- mode: "twilio_elevenlabs_tts",
991
- elevenlabs: { voiceId: "test-voice-id" },
992
- },
993
- },
925
+ elevenlabs: { voiceId: "test-voice-id" },
994
926
  });
995
927
  const profile = resolveVoiceQualityProfile(config);
996
- expect(profile.mode).toBe("twilio_elevenlabs_tts");
997
928
  expect(profile.ttsProvider).toBe("ElevenLabs");
998
929
  expect(profile.voice).toBe("test-voice-id");
999
- expect(profile.validationErrors).toEqual([]);
1000
930
  });
1001
931
 
1002
- test("falls back for twilio_elevenlabs_tts with empty voiceId and fallback enabled", () => {
1003
- const config = AssistantConfigSchema.parse({
1004
- calls: {
1005
- voice: {
1006
- mode: "twilio_elevenlabs_tts",
1007
- fallbackToStandardOnError: true,
1008
- elevenlabs: { voiceId: "" },
1009
- },
1010
- },
1011
- });
1012
- const profile = resolveVoiceQualityProfile(config);
1013
- expect(profile.mode).toBe("twilio_standard");
1014
- expect(profile.ttsProvider).toBe("Google");
1015
- expect(profile.voice).toBe("Google.en-US-Journey-O");
1016
- expect(profile.validationErrors.length).toBe(1);
1017
- expect(profile.validationErrors[0]).toContain("falling back");
1018
- });
1019
-
1020
- test("returns errors for twilio_elevenlabs_tts with empty voiceId and fallback disabled", () => {
1021
- const config = AssistantConfigSchema.parse({
1022
- calls: {
1023
- voice: {
1024
- mode: "twilio_elevenlabs_tts",
1025
- fallbackToStandardOnError: false,
1026
- elevenlabs: { voiceId: "" },
1027
- },
1028
- },
1029
- });
1030
- const profile = resolveVoiceQualityProfile(config);
1031
- expect(profile.mode).toBe("twilio_elevenlabs_tts");
1032
- expect(profile.validationErrors.length).toBe(1);
1033
- expect(profile.validationErrors[0]).toContain("voiceId is required");
1034
- });
1035
-
1036
- test("returns correct profile for elevenlabs_agent with valid agentId", () => {
1037
- const config = AssistantConfigSchema.parse({
1038
- calls: {
1039
- voice: {
1040
- mode: "elevenlabs_agent",
1041
- elevenlabs: { agentId: "agent-123", voiceId: "v1" },
1042
- },
1043
- },
1044
- });
1045
- const profile = resolveVoiceQualityProfile(config);
1046
- expect(profile.mode).toBe("elevenlabs_agent");
1047
- expect(profile.ttsProvider).toBe("ElevenLabs");
1048
- expect(profile.voice).toBe("v1");
1049
- expect(profile.agentId).toBe("agent-123");
1050
- expect(profile.validationErrors).toEqual([]);
1051
- });
1052
-
1053
- test("falls back for elevenlabs_agent with empty agentId and fallback enabled", () => {
1054
- const config = AssistantConfigSchema.parse({
1055
- calls: {
1056
- voice: {
1057
- mode: "elevenlabs_agent",
1058
- fallbackToStandardOnError: true,
1059
- elevenlabs: { agentId: "" },
1060
- },
1061
- },
1062
- });
932
+ test("defaults to Rachel voice ID when elevenlabs.voiceId is not set", () => {
933
+ const config = AssistantConfigSchema.parse({});
1063
934
  const profile = resolveVoiceQualityProfile(config);
1064
- expect(profile.mode).toBe("twilio_standard");
1065
- expect(profile.validationErrors.length).toBe(1);
1066
- expect(profile.validationErrors[0]).toContain("agentId is empty");
935
+ expect(profile.voice).toBe("21m00Tcm4TlvDq8ikWAM");
1067
936
  });
1068
937
 
1069
- test("returns errors for elevenlabs_agent with empty agentId and fallback disabled", () => {
938
+ test("applies voice tuning params from elevenlabs config", () => {
1070
939
  const config = AssistantConfigSchema.parse({
1071
- calls: {
1072
- voice: {
1073
- mode: "elevenlabs_agent",
1074
- fallbackToStandardOnError: false,
1075
- elevenlabs: { agentId: "" },
1076
- },
940
+ elevenlabs: {
941
+ voiceId: "abc123",
942
+ voiceModelId: "turbo_v2_5",
943
+ speed: 0.9,
944
+ stability: 0.8,
945
+ similarityBoost: 0.9,
1077
946
  },
1078
947
  });
1079
948
  const profile = resolveVoiceQualityProfile(config);
1080
- expect(profile.mode).toBe("elevenlabs_agent");
1081
- expect(profile.validationErrors.length).toBe(1);
1082
- expect(profile.validationErrors[0]).toContain("agentId is required");
949
+ expect(profile.voice).toBe("abc123-turbo_v2_5-0.9_0.8_0.9");
1083
950
  });
1084
951
  });
1085
952
 
@@ -1123,14 +990,9 @@ describe("buildElevenLabsVoiceSpec", () => {
1123
990
 
1124
991
  test("default config uses a bare voiceId when no model override is set", () => {
1125
992
  const config = AssistantConfigSchema.parse({
1126
- calls: {
1127
- voice: {
1128
- mode: "twilio_elevenlabs_tts",
1129
- elevenlabs: { voiceId: "test" },
1130
- },
1131
- },
993
+ elevenlabs: { voiceId: "test" },
1132
994
  });
1133
- const spec = buildElevenLabsVoiceSpec(config.calls.voice.elevenlabs);
995
+ const spec = buildElevenLabsVoiceSpec(config.elevenlabs);
1134
996
  expect(spec).toBe("test");
1135
997
  });
1136
998
  });
@@ -1375,10 +1237,8 @@ describe("loadConfig with schema validation", () => {
1375
1237
  expect(config.calls.userConsultTimeoutSeconds).toBe(120);
1376
1238
  expect(config.calls.disclosure.enabled).toBe(true);
1377
1239
  expect(config.calls.safety.denyCategories).toEqual([]);
1378
- expect(config.calls.voice.mode).toBe("twilio_standard");
1379
1240
  expect(config.calls.voice.language).toBe("en-US");
1380
1241
  expect(config.calls.voice.transcriptionProvider).toBe("Deepgram");
1381
- expect(config.calls.voice.elevenlabs.voiceId).toBe("");
1382
1242
  expect(config.calls.model).toBeUndefined();
1383
1243
  expect(config.calls.callerIdentity).toEqual({
1384
1244
  allowPerCallOverride: true,
@@ -84,7 +84,10 @@ mock.module("../runtime/guardian-context-resolver.js", () => ({
84
84
  trustClass: "guardian",
85
85
  sourceChannel: "vellum",
86
86
  }),
87
- toGuardianRuntimeContext: (ctx: unknown) => ctx,
87
+ toGuardianRuntimeContext: (sourceChannel: unknown, ctx: unknown) => ({
88
+ ...(ctx as Record<string, unknown>),
89
+ sourceChannel,
90
+ }),
88
91
  }));
89
92
 
90
93
  import type { AuthContext } from "../runtime/auth/types.js";
@@ -226,7 +226,6 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
226
226
  "tools/network/script-proxy/session-manager.ts", // proxy credential injection at runtime
227
227
  "messaging/registry.ts", // checks stored credentials for connected providers
228
228
  "calls/call-domain.ts", // caller identity resolution (user phone number lookup)
229
- "calls/elevenlabs-config.ts", // ElevenLabs voice quality API key lookup
230
229
  "calls/twilio-config.ts", // call infrastructure credential lookup
231
230
  "calls/twilio-provider.ts", // call infrastructure credential lookup
232
231
  "calls/twilio-rest.ts", // Twilio REST API credential lookup
@@ -234,7 +233,6 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
234
233
  "runtime/http-server.ts", // HTTP server credential lookup
235
234
  "daemon/handlers/twitter-auth.ts", // Twitter OAuth token storage
236
235
  "twitter/oauth-client.ts", // Twitter OAuth API client (reads access token for API calls)
237
- "calls/elevenlabs-config.ts", // ElevenLabs credential lookup
238
236
  "cli/config-commands.ts", // CLI config management
239
237
  "messaging/providers/telegram-bot/adapter.ts", // Telegram bot token lookup for connectivity check
240
238
  "messaging/providers/sms/adapter.ts", // Twilio credential lookup for SMS connectivity check
@@ -119,13 +119,13 @@ describe("guardian-verify-setup skill — voice auto-followup", () => {
119
119
  expect(pollingSection).toContain("Non-rebind flows");
120
120
  });
121
121
 
122
- test("polling is voice-only — does not apply to SMS or Telegram", () => {
122
+ test("polling is voice-only — does not apply to Telegram", () => {
123
123
  const pollingSection =
124
124
  skillContent
125
125
  .split("## Voice Auto-Check Polling")[1]
126
126
  ?.split("## Step 6")[0] ?? "";
127
127
  expect(pollingSection).toContain("voice-only");
128
- expect(pollingSection).toContain("Do NOT poll for SMS or Telegram");
128
+ expect(pollingSection).toContain("Do NOT poll for Telegram");
129
129
  });
130
130
 
131
131
  test('no instruction requires waiting for user to ask "did it work?"', () => {
@@ -476,11 +476,6 @@ const clientMessages: Record<ClientMessageType, ClientMessage> = {
476
476
  service: "gmail",
477
477
  requestedScopes: ["https://www.googleapis.com/auth/gmail.readonly"],
478
478
  },
479
- browser_cdp_response: {
480
- type: "browser_cdp_response",
481
- sessionId: "test-session",
482
- success: true,
483
- },
484
479
  work_items_list: {
485
480
  type: "work_items_list",
486
481
  status: "queued",
@@ -1592,10 +1587,6 @@ const serverMessages: Record<ServerMessageType, ServerMessage> = {
1592
1587
  grantedScopes: ["https://www.googleapis.com/auth/gmail.readonly"],
1593
1588
  accountInfo: "user@example.com",
1594
1589
  },
1595
- browser_cdp_request: {
1596
- type: "browser_cdp_request",
1597
- sessionId: "test-session",
1598
- },
1599
1590
  document_editor_show: {
1600
1591
  type: "document_editor_show",
1601
1592
  sessionId: "sess-001",
@@ -14,11 +14,11 @@ describe("onboarding template contracts", () => {
14
14
  expect(lower).toContain("who am i");
15
15
  });
16
16
 
17
- test("infers personality indirectly instead of asking directly", () => {
17
+ test("infers personality organically instead of asking directly", () => {
18
18
  const lower = bootstrap.toLowerCase();
19
- // Personality step must instruct indirect/organic discovery
19
+ // Personality step must instruct organic discovery via conversation
20
20
  expect(lower).toContain("personality");
21
- expect(lower).toContain("indirectly");
21
+ expect(lower).toContain("emerge");
22
22
  expect(lower).toContain("vibe");
23
23
  });
24
24
 
@@ -41,15 +41,15 @@ describe("onboarding template contracts", () => {
41
41
  const lower = bootstrap.toLowerCase();
42
42
  // The template must prompt the assistant to ask about names.
43
43
  expect(lower).toContain("name");
44
- // The first step should be about locking in the assistant's name
45
- expect(lower).toContain("lock in your name");
44
+ // The first step should be about the assistant's name
45
+ expect(lower).toContain("your name");
46
46
  // The conversation sequence must include identity/naming
47
47
  expect(lower).toContain("who am i");
48
48
  });
49
49
 
50
50
  test("asks user name AFTER assistant identity is established", () => {
51
- // Step 1 is locking in the assistant's name, step 3 is asking the user's name
52
- const assistantNameIdx = bootstrap.indexOf("Lock in your name.");
51
+ // Step 1 is the assistant's name, step 4 is asking the user's name
52
+ const assistantNameIdx = bootstrap.indexOf("Your name:");
53
53
  const userNameIdx = bootstrap.indexOf("who am I talking to?");
54
54
  expect(assistantNameIdx).toBeGreaterThan(-1);
55
55
  expect(userNameIdx).toBeGreaterThan(-1);
@@ -87,10 +87,8 @@ describe("onboarding template contracts", () => {
87
87
  expect(lower).toContain("home base");
88
88
  });
89
89
 
90
- test("contains privacy/refusal policy", () => {
90
+ test("contains refusal policy", () => {
91
91
  const lower = bootstrap.toLowerCase();
92
- // Must have a privacy section
93
- expect(lower).toContain("privacy");
94
92
  // Assistant name is hard-required, user details are best-effort
95
93
  expect(lower).toContain("hard-required");
96
94
  expect(lower).toContain("best-effort");
@@ -107,16 +105,8 @@ describe("onboarding template contracts", () => {
107
105
  expect(lower).toContain("declined");
108
106
  });
109
107
 
110
- test("preserves no em dashes instruction", () => {
111
- const lower = bootstrap.toLowerCase();
112
- expect(lower).toContain("em dashes");
113
- });
114
-
115
- test("preserves no technical jargon instruction", () => {
116
- const lower = bootstrap.toLowerCase();
117
- expect(lower).toContain("technical jargon");
118
- expect(lower).toContain("system internals");
119
- });
108
+ // em-dash and technical jargon instructions are now hardcoded in the system
109
+ // prompt builder (buildSystemPrompt) rather than in the BOOTSTRAP.md template.
120
110
 
121
111
  test("preserves comment line format instruction", () => {
122
112
  // The template must start with the comment format explanation
@@ -2276,7 +2276,7 @@ describe("relay-server", () => {
2276
2276
  .map((raw) => JSON.parse(raw) as { type: string; token?: string })
2277
2277
  .filter((m) => m.type === "text");
2278
2278
  const promptText = textMessages.map((m) => m.token ?? "").join("");
2279
- expect(promptText).toContain("Hi, this is my guardian's assistant.");
2279
+ expect(promptText).toContain("Hi, this is my human's assistant.");
2280
2280
  expect(promptText).not.toContain("Vellum");
2281
2281
  expect(promptText).toContain("don't recognize this number");
2282
2282
  expect(promptText).toContain("Can I get your name");
@@ -2326,13 +2326,13 @@ describe("relay-server", () => {
2326
2326
  // Should have transitioned to awaiting guardian decision
2327
2327
  expect(relay.getConnectionState()).toBe("awaiting_guardian_decision");
2328
2328
 
2329
- // Should have sent the hold message (guardian label defaults to "my guardian")
2329
+ // Should have sent the hold message (guardian label defaults to "my human")
2330
2330
  const textMessages = ws.sentMessages
2331
2331
  .map((raw) => JSON.parse(raw) as { type: string; token?: string })
2332
2332
  .filter((m) => m.type === "text");
2333
2333
  expect(
2334
2334
  textMessages.some((m) =>
2335
- (m.token ?? "").includes("I've let my guardian know"),
2335
+ (m.token ?? "").includes("I've let my human know"),
2336
2336
  ),
2337
2337
  ).toBe(true);
2338
2338
  expect(
@@ -106,6 +106,11 @@ async function publishAndReadFrame(
106
106
  await assistantEventHub.publish(event);
107
107
 
108
108
  const reader = response.body!.getReader();
109
+
110
+ // The first chunk is the immediate heartbeat comment enqueued in start().
111
+ await reader.read();
112
+
113
+ // The second chunk is the actual assistant event.
109
114
  const { value } = await reader.read();
110
115
  ac.abort();
111
116
 
@@ -366,6 +371,11 @@ describe("SSE IPC parity — streaming/delta message types", () => {
366
371
  await assistantEventHub.publish(published);
367
372
 
368
373
  const reader = response.body!.getReader();
374
+
375
+ // The first chunk is the immediate heartbeat comment enqueued in start().
376
+ await reader.read();
377
+
378
+ // The second chunk is the actual assistant event.
369
379
  const { value } = await reader.read();
370
380
  ac.abort();
371
381
 
@@ -168,6 +168,13 @@ describe("SSE assistant-events endpoint", () => {
168
168
 
169
169
  // Read the first frame directly from the response body stream.
170
170
  const reader = response.body!.getReader();
171
+
172
+ // The first chunk is the immediate heartbeat comment enqueued in start().
173
+ const initial = await reader.read();
174
+ expect(initial.done).toBe(false);
175
+ expect(new TextDecoder().decode(initial.value)).toBe(": heartbeat\n\n");
176
+
177
+ // The second chunk is the actual assistant event.
171
178
  const { value, done } = await reader.read();
172
179
  ac.abort();
173
180