@vellumai/assistant 0.4.2 → 0.4.4

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 (221) hide show
  1. package/.env.example +3 -0
  2. package/ARCHITECTURE.md +124 -10
  3. package/README.md +43 -35
  4. package/docs/trusted-contact-access.md +20 -0
  5. package/package.json +1 -1
  6. package/scripts/ipc/generate-swift.ts +1 -0
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +58 -120
  8. package/src/__tests__/access-request-decision.test.ts +0 -1
  9. package/src/__tests__/actor-token-service.test.ts +1099 -0
  10. package/src/__tests__/agent-loop.test.ts +51 -0
  11. package/src/__tests__/approval-routes-http.test.ts +2 -0
  12. package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -5
  13. package/src/__tests__/assistant-id-boundary-guard.test.ts +415 -0
  14. package/src/__tests__/call-controller.test.ts +49 -0
  15. package/src/__tests__/call-pointer-message-composer.test.ts +171 -0
  16. package/src/__tests__/call-pointer-messages.test.ts +93 -3
  17. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +42 -0
  18. package/src/__tests__/call-routes-http.test.ts +0 -25
  19. package/src/__tests__/callback-handoff-copy.test.ts +186 -0
  20. package/src/__tests__/channel-approval-routes.test.ts +133 -12
  21. package/src/__tests__/channel-guardian.test.ts +0 -86
  22. package/src/__tests__/channel-readiness-service.test.ts +10 -16
  23. package/src/__tests__/checker.test.ts +33 -12
  24. package/src/__tests__/config-schema.test.ts +6 -0
  25. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +410 -0
  26. package/src/__tests__/conversation-routes-guardian-reply.test.ts +256 -0
  27. package/src/__tests__/conversation-routes.test.ts +12 -3
  28. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  29. package/src/__tests__/daemon-server-session-init.test.ts +4 -0
  30. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
  31. package/src/__tests__/guardian-actions-endpoint.test.ts +39 -13
  32. package/src/__tests__/guardian-dispatch.test.ts +8 -0
  33. package/src/__tests__/guardian-outbound-http.test.ts +4 -5
  34. package/src/__tests__/guardian-question-mode.test.ts +200 -0
  35. package/src/__tests__/guardian-routing-invariants.test.ts +178 -0
  36. package/src/__tests__/guardian-routing-state.test.ts +525 -0
  37. package/src/__tests__/handle-user-message-secret-resume.test.ts +2 -0
  38. package/src/__tests__/handlers-telegram-config.test.ts +0 -83
  39. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +55 -0
  40. package/src/__tests__/headless-browser-navigate.test.ts +2 -0
  41. package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
  42. package/src/__tests__/ingress-routes-http.test.ts +55 -0
  43. package/src/__tests__/ipc-snapshot.test.ts +18 -51
  44. package/src/__tests__/non-member-access-request.test.ts +159 -9
  45. package/src/__tests__/notification-decision-fallback.test.ts +129 -4
  46. package/src/__tests__/notification-decision-strategy.test.ts +106 -2
  47. package/src/__tests__/notification-guardian-path.test.ts +3 -0
  48. package/src/__tests__/recording-intent-handler.test.ts +1 -0
  49. package/src/__tests__/relay-server.test.ts +1475 -33
  50. package/src/__tests__/send-endpoint-busy.test.ts +5 -0
  51. package/src/__tests__/session-agent-loop.test.ts +1 -0
  52. package/src/__tests__/session-confirmation-signals.test.ts +523 -0
  53. package/src/__tests__/session-init.benchmark.test.ts +0 -2
  54. package/src/__tests__/session-runtime-assembly.test.ts +4 -1
  55. package/src/__tests__/session-surfaces-task-progress.test.ts +44 -1
  56. package/src/__tests__/session-tool-setup-app-refresh.test.ts +81 -2
  57. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -1
  58. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -1
  59. package/src/__tests__/tool-executor.test.ts +21 -2
  60. package/src/__tests__/tool-grant-request-escalation.test.ts +333 -27
  61. package/src/__tests__/trusted-contact-approval-notifier.test.ts +678 -0
  62. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1064 -0
  63. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +11 -1
  64. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  65. package/src/__tests__/trusted-contact-verification.test.ts +0 -1
  66. package/src/__tests__/twilio-config.test.ts +2 -13
  67. package/src/__tests__/twilio-routes.test.ts +4 -3
  68. package/src/__tests__/update-bulletin.test.ts +0 -1
  69. package/src/agent/loop.ts +1 -1
  70. package/src/approvals/guardian-decision-primitive.ts +12 -3
  71. package/src/approvals/guardian-request-resolvers.ts +169 -11
  72. package/src/calls/call-constants.ts +29 -0
  73. package/src/calls/call-controller.ts +11 -3
  74. package/src/calls/call-domain.ts +33 -11
  75. package/src/calls/call-pointer-message-composer.ts +154 -0
  76. package/src/calls/call-pointer-messages.ts +106 -27
  77. package/src/calls/guardian-dispatch.ts +4 -2
  78. package/src/calls/relay-server.ts +921 -112
  79. package/src/calls/twilio-config.ts +4 -11
  80. package/src/calls/twilio-routes.ts +4 -6
  81. package/src/calls/types.ts +3 -1
  82. package/src/calls/voice-session-bridge.ts +4 -3
  83. package/src/cli/core-commands.ts +7 -4
  84. package/src/cli.ts +5 -4
  85. package/src/config/bundled-skills/agentmail/SKILL.md +4 -0
  86. package/src/config/bundled-skills/app-builder/SKILL.md +309 -10
  87. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -1
  88. package/src/config/bundled-skills/email-setup/SKILL.md +1 -1
  89. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +105 -81
  90. package/src/config/bundled-skills/messaging/SKILL.md +61 -12
  91. package/src/config/bundled-skills/messaging/TOOLS.json +58 -0
  92. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +6 -1
  93. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +35 -0
  94. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +52 -0
  95. package/src/config/bundled-skills/phone-calls/SKILL.md +30 -39
  96. package/src/config/bundled-skills/twitter/SKILL.md +3 -3
  97. package/src/config/bundled-skills/vercel-token-setup/SKILL.md +215 -0
  98. package/src/config/calls-schema.ts +36 -0
  99. package/src/config/env.ts +22 -0
  100. package/src/config/feature-flag-registry.json +8 -8
  101. package/src/config/schema.ts +2 -2
  102. package/src/config/skills.ts +11 -0
  103. package/src/config/system-prompt.ts +11 -1
  104. package/src/config/templates/SOUL.md +2 -0
  105. package/src/config/vellum-skills/sms-setup/SKILL.md +71 -82
  106. package/src/config/vellum-skills/trusted-contacts/SKILL.md +8 -1
  107. package/src/config/vellum-skills/twilio-setup/SKILL.md +88 -73
  108. package/src/daemon/call-pointer-generators.ts +59 -0
  109. package/src/daemon/computer-use-session.ts +2 -5
  110. package/src/daemon/handlers/apps.ts +76 -20
  111. package/src/daemon/handlers/config-channels.ts +9 -61
  112. package/src/daemon/handlers/config-inbox.ts +11 -3
  113. package/src/daemon/handlers/config-ingress.ts +28 -3
  114. package/src/daemon/handlers/config-telegram.ts +12 -0
  115. package/src/daemon/handlers/config.ts +2 -6
  116. package/src/daemon/handlers/index.ts +2 -1
  117. package/src/daemon/handlers/pairing.ts +2 -0
  118. package/src/daemon/handlers/publish.ts +11 -46
  119. package/src/daemon/handlers/sessions.ts +59 -5
  120. package/src/daemon/handlers/shared.ts +17 -2
  121. package/src/daemon/ipc-contract/apps.ts +1 -0
  122. package/src/daemon/ipc-contract/inbox.ts +4 -0
  123. package/src/daemon/ipc-contract/integrations.ts +1 -97
  124. package/src/daemon/ipc-contract/messages.ts +47 -1
  125. package/src/daemon/ipc-contract/notifications.ts +11 -0
  126. package/src/daemon/ipc-contract-inventory.json +2 -4
  127. package/src/daemon/lifecycle.ts +17 -0
  128. package/src/daemon/server.ts +16 -2
  129. package/src/daemon/session-agent-loop-handlers.ts +20 -0
  130. package/src/daemon/session-agent-loop.ts +24 -12
  131. package/src/daemon/session-lifecycle.ts +1 -1
  132. package/src/daemon/session-process.ts +11 -1
  133. package/src/daemon/session-runtime-assembly.ts +6 -1
  134. package/src/daemon/session-surfaces.ts +32 -3
  135. package/src/daemon/session.ts +88 -1
  136. package/src/daemon/tool-side-effects.ts +22 -0
  137. package/src/home-base/prebuilt/brain-graph.html +1483 -0
  138. package/src/home-base/prebuilt/index.html +40 -0
  139. package/src/inbound/platform-callback-registration.ts +157 -0
  140. package/src/memory/canonical-guardian-store.ts +1 -1
  141. package/src/memory/conversation-crud.ts +2 -1
  142. package/src/memory/conversation-title-service.ts +16 -2
  143. package/src/memory/db-init.ts +8 -0
  144. package/src/memory/delivery-crud.ts +2 -1
  145. package/src/memory/guardian-action-store.ts +2 -1
  146. package/src/memory/guardian-approvals.ts +3 -2
  147. package/src/memory/ingress-invite-store.ts +12 -2
  148. package/src/memory/ingress-member-store.ts +4 -3
  149. package/src/memory/migrations/038-actor-token-records.ts +39 -0
  150. package/src/memory/migrations/124-voice-invite-display-metadata.ts +14 -0
  151. package/src/memory/migrations/index.ts +2 -0
  152. package/src/memory/schema.ts +26 -5
  153. package/src/messaging/provider-types.ts +24 -0
  154. package/src/messaging/provider.ts +7 -0
  155. package/src/messaging/providers/gmail/adapter.ts +127 -0
  156. package/src/messaging/providers/sms/adapter.ts +40 -37
  157. package/src/notifications/adapters/macos.ts +45 -2
  158. package/src/notifications/broadcaster.ts +16 -0
  159. package/src/notifications/copy-composer.ts +50 -2
  160. package/src/notifications/decision-engine.ts +22 -9
  161. package/src/notifications/destination-resolver.ts +16 -2
  162. package/src/notifications/emit-signal.ts +18 -9
  163. package/src/notifications/guardian-question-mode.ts +419 -0
  164. package/src/notifications/signal.ts +14 -3
  165. package/src/permissions/checker.ts +13 -1
  166. package/src/permissions/prompter.ts +14 -0
  167. package/src/providers/anthropic/client.ts +20 -0
  168. package/src/providers/provider-send-message.ts +15 -3
  169. package/src/runtime/access-request-helper.ts +82 -4
  170. package/src/runtime/actor-token-service.ts +234 -0
  171. package/src/runtime/actor-token-store.ts +236 -0
  172. package/src/runtime/actor-trust-resolver.ts +2 -2
  173. package/src/runtime/assistant-scope.ts +10 -0
  174. package/src/runtime/channel-approvals.ts +5 -3
  175. package/src/runtime/channel-readiness-service.ts +23 -64
  176. package/src/runtime/channel-readiness-types.ts +3 -4
  177. package/src/runtime/channel-retry-sweep.ts +4 -1
  178. package/src/runtime/confirmation-request-guardian-bridge.ts +197 -0
  179. package/src/runtime/guardian-action-followup-executor.ts +1 -1
  180. package/src/runtime/guardian-context-resolver.ts +82 -0
  181. package/src/runtime/guardian-outbound-actions.ts +5 -7
  182. package/src/runtime/guardian-reply-router.ts +67 -30
  183. package/src/runtime/guardian-vellum-migration.ts +57 -0
  184. package/src/runtime/http-server.ts +75 -31
  185. package/src/runtime/http-types.ts +13 -0
  186. package/src/runtime/ingress-service.ts +14 -0
  187. package/src/runtime/invite-redemption-service.ts +10 -1
  188. package/src/runtime/local-actor-identity.ts +76 -0
  189. package/src/runtime/middleware/actor-token.ts +271 -0
  190. package/src/runtime/middleware/twilio-validation.ts +2 -4
  191. package/src/runtime/routes/approval-routes.ts +82 -7
  192. package/src/runtime/routes/brain-graph-routes.ts +222 -0
  193. package/src/runtime/routes/call-routes.ts +2 -1
  194. package/src/runtime/routes/channel-readiness-routes.ts +71 -0
  195. package/src/runtime/routes/channel-route-shared.ts +3 -3
  196. package/src/runtime/routes/conversation-attention-routes.ts +2 -1
  197. package/src/runtime/routes/conversation-routes.ts +142 -53
  198. package/src/runtime/routes/events-routes.ts +22 -8
  199. package/src/runtime/routes/guardian-action-routes.ts +45 -3
  200. package/src/runtime/routes/guardian-approval-interception.ts +29 -0
  201. package/src/runtime/routes/guardian-bootstrap-routes.ts +145 -0
  202. package/src/runtime/routes/inbound-conversation.ts +4 -3
  203. package/src/runtime/routes/inbound-message-handler.ts +147 -5
  204. package/src/runtime/routes/ingress-routes.ts +2 -0
  205. package/src/runtime/routes/integration-routes.ts +7 -15
  206. package/src/runtime/routes/pairing-routes.ts +163 -0
  207. package/src/runtime/routes/twilio-routes.ts +934 -0
  208. package/src/runtime/tool-grant-request-helper.ts +3 -1
  209. package/src/security/oauth2.ts +27 -2
  210. package/src/security/token-manager.ts +46 -10
  211. package/src/tools/browser/browser-execution.ts +4 -3
  212. package/src/tools/browser/browser-handoff.ts +10 -18
  213. package/src/tools/browser/browser-manager.ts +80 -25
  214. package/src/tools/browser/browser-screencast.ts +35 -119
  215. package/src/tools/calls/call-start.ts +2 -1
  216. package/src/tools/permission-checker.ts +15 -4
  217. package/src/tools/terminal/parser.ts +12 -0
  218. package/src/tools/tool-approval-handler.ts +244 -19
  219. package/src/workspace/git-service.ts +19 -0
  220. package/src/__tests__/handlers-twilio-config.test.ts +0 -1928
  221. package/src/daemon/handlers/config-twilio.ts +0 -1082
@@ -9,20 +9,18 @@ You are helping your user set up SMS messaging. This skill orchestrates Twilio s
9
9
 
10
10
  ## Step 1: Check Channel Readiness
11
11
 
12
- First, check the current SMS channel readiness state by sending the `channel_readiness` IPC message:
13
-
14
- ```json
15
- {
16
- "type": "channel_readiness",
17
- "action": "get",
18
- "channel": "sms"
19
- }
12
+ First, check the current SMS channel readiness state via the gateway:
13
+
14
+ ```bash
15
+ TOKEN=$(cat ~/.vellum/http-token)
16
+ curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
17
+ -H "Authorization: Bearer $TOKEN"
20
18
  ```
21
19
 
22
- Inspect the `channel_readiness_response`. The response contains `snapshots` with each channel's readiness state.
20
+ Inspect the response for `hasCredentials` and `phoneNumber`.
23
21
 
24
- - If the SMS channel shows `ready: true` and all `localChecks` pass, skip to Step 3.
25
- - If any local checks fail, proceed to Step 2 to fix the baseline.
22
+ - If both are present and all baseline requirements are met, skip to Step 3.
23
+ - If credentials or phone number are missing, proceed to Step 2 to fix the baseline.
26
24
 
27
25
  ## Step 2: Establish Baseline (Twilio Setup)
28
26
 
@@ -34,47 +32,35 @@ skill_load skill=twilio-setup
34
32
 
35
33
  Tell the user: *"SMS needs Twilio configured first. I've loaded the Twilio setup guide — let's walk through it."*
36
34
 
37
- After twilio-setup completes, re-check readiness:
35
+ After twilio-setup completes, re-check readiness by calling the config endpoint again:
38
36
 
39
- ```json
40
- {
41
- "type": "channel_readiness",
42
- "action": "refresh",
43
- "channel": "sms"
44
- }
37
+ ```bash
38
+ TOKEN=$(cat ~/.vellum/http-token)
39
+ curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
40
+ -H "Authorization: Bearer $TOKEN"
45
41
  ```
46
42
 
47
43
  If baseline is still not ready, report the specific failures and ask the user to address them before continuing.
48
44
 
49
45
  ## Step 3: Remote Compliance Check
50
46
 
51
- Once baseline is ready, run a full readiness check including remote (Twilio API) checks:
47
+ Once baseline is ready, check SMS compliance status including remote (Twilio API) checks:
52
48
 
53
- ```json
54
- {
55
- "type": "channel_readiness",
56
- "action": "refresh",
57
- "channel": "sms",
58
- "includeRemote": true
59
- }
49
+ ```bash
50
+ TOKEN=$(cat ~/.vellum/http-token)
51
+ curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/compliance" \
52
+ -H "Authorization: Bearer $TOKEN"
60
53
  ```
61
54
 
62
- Examine the remote check results:
63
- - If all remote checks pass, proceed to Step 4.
55
+ Examine the compliance results:
56
+ - If all checks pass, proceed to Step 4.
64
57
  - If compliance issues are found (e.g., toll-free verification needed), guide the user through the compliance flow.
65
58
 
66
59
  ### Toll-Free Verification Submission
67
60
 
68
- When the remote check returns `toll_free_verification` as a failing check, use the daemon's built-in IPC compliance actions. These handle credential lookup, phone number SID resolution, field validation, and Twilio API calls internally.
61
+ When the compliance check returns a toll-free verification requirement, use the gateway's compliance endpoints. These handle credential lookup, phone number SID resolution, field validation, and Twilio API calls internally.
69
62
 
70
- **Step 3a: Check compliance status.** First check if a verification already exists:
71
-
72
- ```json
73
- {
74
- "type": "twilio_config",
75
- "action": "sms_compliance_status"
76
- }
77
- ```
63
+ **Step 3a: Check compliance status.** First check if a verification already exists (use the response from the compliance check above):
78
64
 
79
65
  The response includes a `compliance` object with `numberType`, `tollfreePhoneNumberSid`, `verificationSid`, `verificationStatus`, `rejectionReason`, `rejectionReasons`, `editAllowed`, and `editExpiration` fields. For toll-free numbers, `tollfreePhoneNumberSid` contains the Twilio phone number SID needed for verification submission.
80
66
 
@@ -98,15 +84,16 @@ The response includes a `compliance` object with `numberType`, `tollfreePhoneNum
98
84
  | Opt-in type | `optInType` | `VERBAL`, `WEB_FORM`, `PAPER_FORM`, `VIA_TEXT`, `MOBILE_QR_CODE` |
99
85
  | Opt-in image URL | `optInImageUrls` | Array of URLs showing opt-in mechanism (can be website URL) |
100
86
 
101
- The `tollfreePhoneNumberSid` is returned by the `sms_compliance_status` response in the `compliance` object. Use `compliance.tollfreePhoneNumberSid` from the Step 3a response as the value for `verificationParams.tollfreePhoneNumberSid` when submitting. Do NOT ask for EIN, business registration number, or business registration authority. Explain that Twilio labels some fields as "business" fields even for individual submitters.
87
+ The `tollfreePhoneNumberSid` is returned by the compliance status response in the `compliance` object. Use `compliance.tollfreePhoneNumberSid` from the Step 3a response as the value for `tollfreePhoneNumberSid` when submitting. Do NOT ask for EIN, business registration number, or business registration authority. Explain that Twilio labels some fields as "business" fields even for individual submitters.
102
88
 
103
89
  **Step 3c: Submit verification:**
104
90
 
105
- ```json
106
- {
107
- "type": "twilio_config",
108
- "action": "sms_submit_tollfree_verification",
109
- "verificationParams": {
91
+ ```bash
92
+ TOKEN=$(cat ~/.vellum/http-token)
93
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/compliance/tollfree" \
94
+ -H "Authorization: Bearer $TOKEN" \
95
+ -H "Content-Type: application/json" \
96
+ -d '{
110
97
  "tollfreePhoneNumberSid": "<compliance.tollfreePhoneNumberSid from Step 3a>",
111
98
  "businessName": "...",
112
99
  "businessWebsite": "...",
@@ -118,11 +105,10 @@ The `tollfreePhoneNumberSid` is returned by the `sms_compliance_status` response
118
105
  "optInType": "VERBAL",
119
106
  "messageVolume": "100",
120
107
  "businessType": "SOLE_PROPRIETOR"
121
- }
122
- }
108
+ }'
123
109
  ```
124
110
 
125
- The daemon validates all fields before submitting to Twilio and returns clear error messages for invalid values.
111
+ The endpoint validates all fields before submitting to Twilio and returns clear error messages for invalid values.
126
112
 
127
113
  **On success:** The response contains `compliance.verificationSid` and `compliance.verificationStatus` (typically `PENDING_REVIEW`). Tell the user Twilio typically reviews within 1-5 business days.
128
114
 
@@ -130,36 +116,33 @@ The daemon validates all fields before submitting to Twilio and returns clear er
130
116
 
131
117
  **Step 3d: Update a rejected verification** (if `editAllowed` is true):
132
118
 
133
- ```json
134
- {
135
- "type": "twilio_config",
136
- "action": "sms_update_tollfree_verification",
137
- "verificationSid": "<sid from compliance status>",
138
- "verificationParams": {
119
+ ```bash
120
+ TOKEN=$(cat ~/.vellum/http-token)
121
+ curl -s -X PATCH "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/compliance/tollfree/<verificationSid>" \
122
+ -H "Authorization: Bearer $TOKEN" \
123
+ -H "Content-Type: application/json" \
124
+ -d '{
139
125
  "businessName": "updated value",
140
126
  "useCaseSummary": "updated value"
141
- }
142
- }
127
+ }'
143
128
  ```
144
129
 
145
- Only include fields that need to change. The daemon checks edit eligibility and expiration before attempting the update.
130
+ Only include fields that need to change. The endpoint checks edit eligibility and expiration before attempting the update.
146
131
 
147
132
  **Step 3e: Delete and resubmit** (if editing is not allowed):
148
133
 
149
- ```json
150
- {
151
- "type": "twilio_config",
152
- "action": "sms_delete_tollfree_verification",
153
- "verificationSid": "<sid from compliance status>"
154
- }
134
+ ```bash
135
+ TOKEN=$(cat ~/.vellum/http-token)
136
+ curl -s -X DELETE "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/compliance/tollfree/<verificationSid>" \
137
+ -H "Authorization: Bearer $TOKEN"
155
138
  ```
156
139
 
157
140
  After deletion, return to Step 3b to collect information and resubmit. Warn the user that deleting resets their position in the review queue.
158
141
 
159
142
  **Common errors:**
160
143
  - `"Customer profiles submitted with verifications must be either ISV Starters or Secondary Customer Profiles"` — The number is linked to a Primary Customer Profile in Trust Hub, which blocks toll-free verification. Tell the user and suggest they resolve the profile assignment in the Twilio Console.
161
- - Missing required fields — The daemon validates and reports which fields are missing.
162
- - Invalid enum values — The daemon validates `optInType`, `messageVolume`, and `useCaseCategories` and reports valid values.
144
+ - Missing required fields — The endpoint validates and reports which fields are missing.
145
+ - Invalid enum values — The endpoint validates `optInType`, `messageVolume`, and `useCaseCategories` and reports valid values.
163
146
 
164
147
  **On success:** Tell the user the verification has been submitted and is now `PENDING_REVIEW`. Twilio typically reviews within 1-5 business days. They'll receive status updates at the notification email provided.
165
148
 
@@ -195,11 +178,31 @@ Tell the user: *"Let's send a test SMS to verify everything works. What phone nu
195
178
 
196
179
  **Trial account limitation:** On Twilio trial accounts, SMS can only be sent to verified phone numbers. If the send fails with a "not verified" error, tell the user to verify the recipient number in the Twilio Console under Verified Caller IDs, or upgrade their account.
197
180
 
198
- After the user provides a number, send a test message using the messaging tools:
199
- - Use `messaging_send` with `platform: "sms"`, `conversation_id: "<phone number>"`, and a test message like "Test SMS from your Vellum assistant."
200
- - Report the result honestly:
201
- - If the send succeeds: *"The message was accepted by Twilio. Note: 'accepted' means Twilio received it for delivery, not that it reached the handset yet. Delivery can take a few seconds to a few minutes. If verification is still pending, carriers may silently drop the message."*
202
- - If the send fails: report the error and suggest troubleshooting steps
181
+ After the user provides a number, send a test message via the gateway:
182
+
183
+ ```bash
184
+ TOKEN=$(cat ~/.vellum/http-token)
185
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/test" \
186
+ -H "Authorization: Bearer $TOKEN" \
187
+ -H "Content-Type: application/json" \
188
+ -d '{"phoneNumber":"<recipient phone number>","text":"Test SMS from your Vellum assistant."}'
189
+ ```
190
+
191
+ Report the result honestly:
192
+ - If the send succeeds: *"The message was accepted by Twilio. Note: 'accepted' means Twilio received it for delivery, not that it reached the handset yet. Delivery can take a few seconds to a few minutes. If verification is still pending, carriers may silently drop the message."*
193
+ - If the send fails: report the error and suggest troubleshooting steps
194
+
195
+ ### SMS Diagnostics
196
+
197
+ If the test fails or the user reports SMS issues, run the SMS doctor:
198
+
199
+ ```bash
200
+ TOKEN=$(cat ~/.vellum/http-token)
201
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/doctor" \
202
+ -H "Authorization: Bearer $TOKEN"
203
+ ```
204
+
205
+ This runs a comprehensive health diagnostic, checking channel readiness, compliance/toll-free verification status, and the last test result. Report the diagnostics and actionable items to the user.
203
206
 
204
207
  ## Step 5: Final Status Report
205
208
 
@@ -229,17 +232,3 @@ Common issues:
229
232
  - **"Twilio error on send"** — Check credentials, phone number assignment, and ingress
230
233
  - **"Trial account limitations"** — Explain that trial accounts can only send to verified numbers
231
234
  - **"Customer profiles must be ISV Starters or Secondary"** — The toll-free number is linked to a Primary Customer Profile in Trust Hub. Must be unlinked or reassigned before verification can be submitted.
232
-
233
- ## Accessing the Twilio API
234
-
235
- The skill references IPC messages (`channel_readiness`, `twilio_config`) that are sent via Unix socket to the daemon. The assistant does not have an HTTP endpoint for IPC. Use the following pattern to send IPC messages:
236
-
237
- ```bash
238
- cd "$(git rev-parse --show-toplevel)/assistant" && bun -e '
239
- import { sendOneMessage } from "./src/cli/ipc-client.js";
240
- const res = await sendOneMessage({ type: "twilio_config", action: "get" });
241
- console.log(JSON.stringify(res, null, 2));
242
- '
243
- ```
244
-
245
- All compliance operations (status checks, verification submission, updates, and deletion) are handled through the `twilio_config` IPC actions — no direct Twilio REST calls are needed.
@@ -254,6 +254,8 @@ INVITE_JSON=$(curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites" \
254
254
  -d '{
255
255
  "sourceChannel": "voice",
256
256
  "expectedExternalUserId": "<phone_number_E164>",
257
+ "friendName": "<invitee display name>",
258
+ "guardianName": "<guardian display name>",
257
259
  "maxUses": 1,
258
260
  "note": "<optional note, e.g. the person it is for>"
259
261
  }')
@@ -263,6 +265,8 @@ printf '%s\n' "$INVITE_JSON"
263
265
  Required fields:
264
266
  - `sourceChannel` — must be `"voice"`
265
267
  - `expectedExternalUserId` — the invitee's phone number in E.164 format (e.g., `+15551234567`)
268
+ - `friendName` — the invitee's display name (e.g., "Mom", "Dr. Smith"). Used during the voice verification call to personalize the experience.
269
+ - `guardianName` — the guardian's display name (e.g., "Alex"). Used during the voice verification call so the invitee knows who invited them.
266
270
 
267
271
  Optional fields:
268
272
  - `maxUses` — how many times the code can be used (default: 1)
@@ -270,8 +274,9 @@ Optional fields:
270
274
  - ~~`voiceCodeDigits`~~ — always 6 digits; this parameter is accepted but ignored
271
275
  - `note` — a human-readable label for the invite (e.g., "For Mom", "Dr. Smith")
272
276
 
273
- The create response contains `{ ok: true, invite: { id, voiceCode, expectedExternalUserId, ... } }`.
277
+ The create response contains `{ ok: true, invite: { id, voiceCode, expectedExternalUserId, friendName, guardianName, ... } }`.
274
278
  - `voiceCode` is the numeric code the invitee must enter and is only returned at creation time.
279
+ - `friendName` and `guardianName` are echoed back in the response.
275
280
  - Voice invite responses do **not** include `token` or `share.url`. Do not try to build or send a deep link for voice invites.
276
281
 
277
282
  **Presenting to the guardian**: Give the guardian clear instructions to relay to the invitee:
@@ -348,6 +353,8 @@ Replace `<invite_id>` with the invite's `id` from the list response. The same re
348
353
  - `sourceChannel is required for create` — when creating an invite, always pass `"sourceChannel": "telegram"` for Telegram or `"sourceChannel": "voice"` for voice invites.
349
354
  - `expectedExternalUserId is required for voice invites` — voice invites must include the invitee's phone number.
350
355
  - `expectedExternalUserId must be in E.164 format` — the phone number must start with `+` followed by country code and number (e.g., `+15551234567`).
356
+ - `friendName is required for voice invites` — voice invites must include the invitee's display name.
357
+ - `guardianName is required for voice invites` — voice invites must include the guardian's display name.
351
358
  - `Invite not found or already revoked` — the invite ID may be invalid or the invite is already revoked.
352
359
 
353
360
  ## Typical Workflows
@@ -6,7 +6,26 @@ includes: ["public-ingress"]
6
6
  metadata: {"vellum": {"emoji": "\ud83d\udcf1"}}
7
7
  ---
8
8
 
9
- You are helping your user configure Twilio for voice calls and SMS messaging. Twilio is the shared telephony provider for both the **phone-calls** and **SMS messaging** capabilities. When this skill is invoked, walk through each step below using the `twilio_config` IPC contract and existing tools.
9
+ You are helping your user configure Twilio for voice calls and SMS messaging. Twilio is the shared telephony provider for both the **phone-calls** and **SMS messaging** capabilities. When this skill is invoked, walk through each step below using the Twilio HTTP control-plane endpoints and existing tools.
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ TOKEN=$(cat ~/.vellum/http-token)
15
+ # 1. Check current status
16
+ curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
17
+ -H "Authorization: Bearer $TOKEN"
18
+ # 2. Store credentials (after collecting via credential_store prompt)
19
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/credentials" \
20
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
21
+ -d '{"accountSid":"ACxxx","authToken":"xxx"}'
22
+ # 3. Provision or assign a number
23
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/provision" \
24
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
25
+ -d '{"country":"US","areaCode":"415"}'
26
+ ```
27
+
28
+ For voice call setup after Twilio is configured, use `phone-calls` + `call_start`.
10
29
 
11
30
  ## Overview
12
31
 
@@ -16,42 +35,40 @@ This skill manages the full Twilio lifecycle:
16
35
  - **Phone number assignment** — Assign an existing Twilio number to the assistant
17
36
  - **Status checking** — Verify credentials and assigned number
18
37
 
19
- All operations go through the `twilio_config` IPC handler on the daemon, which validates inputs, stores credentials securely, and manages phone number state.
38
+ All operations go through the Twilio HTTP control-plane endpoints on the gateway, which validates inputs, stores credentials securely, and manages phone number state.
20
39
 
21
40
  ### Multi-Assistant Setups
22
41
 
23
- In a multi-assistant environment (multiple assistants sharing the same daemon), some `twilio_config` actions are **assistant-scoped** while others are **global** (shared across all assistants):
42
+ In a multi-assistant environment (multiple assistants sharing the same daemon), some actions are **assistant-scoped** while others are **global** (shared across all assistants):
24
43
 
25
44
  **Global actions** (ignore `assistantId` — credentials are shared across all assistants):
26
- - `set_credentials` — Stores Account SID and Auth Token in global secure storage (`credential:twilio:*` keys). All assistants share the same Twilio account credentials.
27
- - `clear_credentials` — Removes the globally stored Account SID and Auth Token. This affects all assistants.
45
+ - `POST /v1/integrations/twilio/credentials` — Stores Account SID and Auth Token in global secure storage (`credential:twilio:*` keys). All assistants share the same Twilio account credentials.
46
+ - `DELETE /v1/integrations/twilio/credentials` — Removes the globally stored Account SID and Auth Token. This affects all assistants.
28
47
 
29
- **Assistant-scoped actions** (use `assistantId` to scope phone number configuration per assistant):
30
- - `get` — Returns the phone number assigned to the specified assistant (falls back to the legacy global number if no per-assistant mapping exists).
31
- - `assign_number` — Assigns a phone number to a specific assistant via the per-assistant mapping.
32
- - `provision_number` — Provisions a new number and assigns it to the specified assistant.
33
- - `list_numbers` — Lists all phone numbers on the shared Twilio account (uses global credentials).
48
+ **Assistant-scoped actions** (use `assistantId` query parameter to scope phone number configuration per assistant):
49
+ - `GET /v1/integrations/twilio/config` — Returns the phone number assigned to the specified assistant (falls back to the legacy global number if no per-assistant mapping exists).
50
+ - `POST /v1/integrations/twilio/numbers/assign` — Assigns a phone number to a specific assistant via the per-assistant mapping.
51
+ - `POST /v1/integrations/twilio/numbers/provision` — Provisions a new number and assigns it to the specified assistant.
52
+ - `GET /v1/integrations/twilio/numbers` — Lists all phone numbers on the shared Twilio account (uses global credentials).
34
53
 
35
- Include `assistantId` in assistant-scoped actions whenever:
54
+ Include `assistantId` as a query parameter in assistant-scoped requests whenever:
36
55
  - Multiple assistants share the same Twilio account but use different phone numbers
37
56
  - You want to ensure configuration changes only affect a specific assistant
38
57
  - The user has explicitly selected or referenced a particular assistant
39
58
 
40
- All IPC examples below include the optional `assistantId` field in assistant-scoped actions. Omit it in single-assistant setups. For global actions (`set_credentials`, `clear_credentials`), the `assistantId` field is accepted but ignored.
59
+ All HTTP examples below include the optional `assistantId` query parameter in assistant-scoped requests. Omit it in single-assistant setups. For global actions (credentials), the `assistantId` parameter is accepted but ignored.
41
60
 
42
61
  ## Step 1: Check Current Configuration
43
62
 
44
- First, check whether Twilio is already configured by sending the `twilio_config` IPC message with `action: "get"`:
63
+ First, check whether Twilio is already configured:
45
64
 
46
- ```json
47
- {
48
- "type": "twilio_config",
49
- "action": "get",
50
- "assistantId": "<optional — omit for single-assistant setups>"
51
- }
65
+ ```bash
66
+ TOKEN=$(cat ~/.vellum/http-token)
67
+ curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
68
+ -H "Authorization: Bearer $TOKEN"
52
69
  ```
53
70
 
54
- The daemon returns a `twilio_config_response` with:
71
+ The response includes:
55
72
  - `hasCredentials` — whether Account SID and Auth Token are stored
56
73
  - `phoneNumber` — the currently assigned phone number (if any)
57
74
 
@@ -71,20 +88,19 @@ If credentials are not yet stored, guide the user through Twilio account setup:
71
88
  - Call `credential_store` with `action: "prompt"`, `service: "twilio"`, `field: "account_sid"`, `label: "Twilio Account SID"`, `description: "Enter your Account SID from the Twilio Console dashboard"`, and `placeholder: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"`.
72
89
  - Call `credential_store` with `action: "prompt"`, `service: "twilio"`, `field: "auth_token"`, `label: "Twilio Auth Token"`, `description: "Enter your Auth Token from the Twilio Console dashboard"`, and `placeholder: "your_auth_token"`.
73
90
 
74
- After both credentials are collected, retrieve them from secure storage and pass them to the daemon:
91
+ After both credentials are collected, retrieve them from secure storage and send them to the gateway:
75
92
 
76
- ```json
77
- {
78
- "type": "twilio_config",
79
- "action": "set_credentials",
80
- "accountSid": "<value from credential_store for twilio/account_sid>",
81
- "authToken": "<value from credential_store for twilio/auth_token>"
82
- }
93
+ ```bash
94
+ TOKEN=$(cat ~/.vellum/http-token)
95
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/credentials" \
96
+ -H "Authorization: Bearer $TOKEN" \
97
+ -H "Content-Type: application/json" \
98
+ -d '{"accountSid":"<value from credential_store for twilio/account_sid>","authToken":"<value from credential_store for twilio/auth_token>"}'
83
99
  ```
84
100
 
85
- Both `accountSid` and `authToken` are required — the daemon validates the credentials against the Twilio API before storing them. If credentials are invalid, the daemon returns an error. Tell the user and ask them to re-enter via the secure prompt.
101
+ Both `accountSid` and `authToken` are required — the endpoint validates the credentials against the Twilio API before storing them. If credentials are invalid, the response returns an error. Tell the user and ask them to re-enter via the secure prompt.
86
102
 
87
- **Note:** `set_credentials` is a global operation — credentials are stored once and shared across all assistants. The `assistantId` field is accepted but ignored.
103
+ **Note:** Setting credentials is a global operation — credentials are stored once and shared across all assistants. The `assistantId` parameter is accepted but ignored.
88
104
 
89
105
  ## Step 3: Get a Phone Number
90
106
 
@@ -92,24 +108,22 @@ The assistant needs a phone number to make calls and send SMS. There are two pat
92
108
 
93
109
  ### Option A: Provision a New Number
94
110
 
95
- If the user wants to buy a new number through Twilio, send:
111
+ If the user wants to buy a new number through Twilio:
96
112
 
97
- ```json
98
- {
99
- "type": "twilio_config",
100
- "action": "provision_number",
101
- "areaCode": "415",
102
- "country": "US",
103
- "assistantId": "<optional — omit for single-assistant setups>"
104
- }
113
+ ```bash
114
+ TOKEN=$(cat ~/.vellum/http-token)
115
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/provision" \
116
+ -H "Authorization: Bearer $TOKEN" \
117
+ -H "Content-Type: application/json" \
118
+ -d '{"country":"US","areaCode":"415"}'
105
119
  ```
106
120
 
107
121
  - `areaCode` is optional — ask the user if they have a preferred area code
108
122
  - `country` defaults to `"US"` — ask if they want a different country (ISO 3166-1 alpha-2)
109
123
 
110
- The daemon provisions the number via the Twilio API, automatically assigns it to the assistant (persisting to both secure storage and config), and configures Twilio webhooks (voice, status callback, SMS) if a public ingress URL is available. The response includes the new `phoneNumber`. No separate `assign_number` call is needed.
124
+ The endpoint provisions the number via the Twilio API, automatically assigns it to the assistant (persisting to both secure storage and config), and configures Twilio webhooks (voice, status callback, SMS) if a public ingress URL is available. The response includes the new `phoneNumber`. No separate assign call is needed.
111
125
 
112
- **Webhook auto-configuration:** When `ingress.publicBaseUrl` is configured, the daemon automatically sets the following webhooks on the Twilio phone number:
126
+ **Webhook auto-configuration:** When `ingress.publicBaseUrl` is configured, the endpoint automatically sets the following webhooks on the Twilio phone number:
113
127
  - Voice webhook: `{publicBaseUrl}/webhooks/twilio/voice`
114
128
  - Voice status callback: `{publicBaseUrl}/webhooks/twilio/status`
115
129
  - SMS webhook: `{publicBaseUrl}/webhooks/twilio/sms`
@@ -122,28 +136,25 @@ If ingress is not yet configured, webhook setup is skipped gracefully — the nu
122
136
 
123
137
  If the user already has a Twilio phone number, first list available numbers:
124
138
 
125
- ```json
126
- {
127
- "type": "twilio_config",
128
- "action": "list_numbers",
129
- "assistantId": "<optional — omit for single-assistant setups>"
130
- }
139
+ ```bash
140
+ TOKEN=$(cat ~/.vellum/http-token)
141
+ curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers" \
142
+ -H "Authorization: Bearer $TOKEN"
131
143
  ```
132
144
 
133
145
  The response includes a `numbers` array with each number's `phoneNumber`, `friendlyName`, and `capabilities` (voice, SMS). Present these to the user and let them choose.
134
146
 
135
147
  Then assign the chosen number:
136
148
 
137
- ```json
138
- {
139
- "type": "twilio_config",
140
- "action": "assign_number",
141
- "phoneNumber": "+14155551234",
142
- "assistantId": "<optional — omit for single-assistant setups>"
143
- }
149
+ ```bash
150
+ TOKEN=$(cat ~/.vellum/http-token)
151
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/assign" \
152
+ -H "Authorization: Bearer $TOKEN" \
153
+ -H "Content-Type: application/json" \
154
+ -d '{"phoneNumber":"+14155551234"}'
144
155
  ```
145
156
 
146
- The phone number must be in E.164 format. Like `provision_number`, `assign_number` also auto-configures Twilio webhooks when a public ingress URL is available.
157
+ The phone number must be in E.164 format. Like provisioning, assigning also auto-configures Twilio webhooks when a public ingress URL is available.
147
158
 
148
159
  ### Option C: Manual Entry
149
160
 
@@ -153,15 +164,14 @@ If the user wants to enter a number directly (e.g., they know it already), store
153
164
  credential_store action=store service=twilio field=phone_number value=+14155551234
154
165
  ```
155
166
 
156
- Then assign it through the IPC:
167
+ Then assign it through the gateway:
157
168
 
158
- ```json
159
- {
160
- "type": "twilio_config",
161
- "action": "assign_number",
162
- "phoneNumber": "+14155551234",
163
- "assistantId": "<optional — omit for single-assistant setups>"
164
- }
169
+ ```bash
170
+ TOKEN=$(cat ~/.vellum/http-token)
171
+ curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/assign" \
172
+ -H "Authorization: Bearer $TOKEN" \
173
+ -H "Content-Type: application/json" \
174
+ -d '{"phoneNumber":"+14155551234"}'
165
175
  ```
166
176
 
167
177
  ## Step 4: Set Up Public Ingress
@@ -187,11 +197,17 @@ skill_load skill=public-ingress
187
197
  - ConversationRelay WebSocket: `{publicBaseUrl}/webhooks/twilio/relay` (wss://)
188
198
  - SMS webhook: `{publicBaseUrl}/webhooks/twilio/sms`
189
199
 
190
- Webhook URLs are automatically configured on the Twilio phone number when `provision_number` or `assign_number` is called with a valid ingress URL. No manual Twilio Console webhook configuration is needed.
200
+ Webhook URLs are automatically configured on the Twilio phone number when provisioning or assigning a number with a valid ingress URL. No manual Twilio Console webhook configuration is needed.
191
201
 
192
202
  ## Step 5: Verify Setup
193
203
 
194
- After configuration, verify by sending `twilio_config` with `action: "get"` again.
204
+ After configuration, verify by checking the config endpoint again.
205
+
206
+ ```bash
207
+ TOKEN=$(cat ~/.vellum/http-token)
208
+ curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
209
+ -H "Authorization: Bearer $TOKEN"
210
+ ```
195
211
 
196
212
  Confirm:
197
213
  - `hasCredentials` is `true`
@@ -251,18 +267,17 @@ SMS is available automatically once Twilio is configured — no additional featu
251
267
 
252
268
  ## Clearing Credentials
253
269
 
254
- If the user wants to disconnect Twilio, send:
270
+ If the user wants to disconnect Twilio:
255
271
 
256
- ```json
257
- {
258
- "type": "twilio_config",
259
- "action": "clear_credentials"
260
- }
272
+ ```bash
273
+ TOKEN=$(cat ~/.vellum/http-token)
274
+ curl -s -X DELETE "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/credentials" \
275
+ -H "Authorization: Bearer $TOKEN"
261
276
  ```
262
277
 
263
278
  This removes the stored Account SID and Auth Token. Phone number assignments are preserved. Voice calls and SMS will stop working until credentials are reconfigured.
264
279
 
265
- **Note:** `clear_credentials` is a global operation — it removes credentials for all assistants, not just the current one. The `assistantId` field is accepted but ignored. In multi-assistant setups, warn the user that clearing credentials will affect all assistants sharing this Twilio account.
280
+ **Note:** Clearing credentials is a global operation — it removes credentials for all assistants, not just the current one. The `assistantId` parameter is accepted but ignored. In multi-assistant setups, warn the user that clearing credentials will affect all assistants sharing this Twilio account.
266
281
 
267
282
  ## Troubleshooting
268
283
 
@@ -284,5 +299,5 @@ Run Step 3 to provision or assign a phone number.
284
299
 
285
300
  ### "Number not found" when assigning
286
301
  - The number must be owned by the same Twilio account
287
- - Use `list_numbers` to see available numbers
302
+ - Use the list numbers endpoint to see available numbers
288
303
  - Ensure the number is in E.164 format (`+` followed by country code and number)
@@ -0,0 +1,59 @@
1
+ import {
2
+ buildPointerGenerationPrompt,
3
+ getPointerFallbackMessage,
4
+ includesRequiredFacts,
5
+ POINTER_COPY_MAX_TOKENS,
6
+ POINTER_COPY_SYSTEM_PROMPT,
7
+ POINTER_COPY_TIMEOUT_MS,
8
+ } from '../calls/call-pointer-message-composer.js';
9
+ import { loadConfig } from '../config/loader.js';
10
+ import { getFailoverProvider } from '../providers/registry.js';
11
+ import type { PointerCopyGenerator } from '../runtime/http-types.js';
12
+
13
+ /**
14
+ * Create the daemon-owned pointer copy generator that resolves providers
15
+ * and calls `provider.sendMessage` to generate pointer message text.
16
+ * This keeps all provider awareness in the daemon lifecycle, away from
17
+ * the runtime composer.
18
+ */
19
+ export function createPointerCopyGenerator(): PointerCopyGenerator {
20
+ return async (context, options = {}) => {
21
+ const config = loadConfig();
22
+ let provider;
23
+ try {
24
+ provider = getFailoverProvider(config.provider, config.providerOrder);
25
+ } catch {
26
+ return null;
27
+ }
28
+
29
+ const fallbackText = options.fallbackText?.trim() || getPointerFallbackMessage(context);
30
+ const requiredFacts = options.requiredFacts
31
+ ?.map((f) => f.trim())
32
+ .filter((f) => f.length > 0);
33
+ const prompt = buildPointerGenerationPrompt(context, fallbackText, requiredFacts);
34
+
35
+ const response = await provider.sendMessage(
36
+ [{ role: 'user', content: [{ type: 'text', text: prompt }] }],
37
+ [],
38
+ POINTER_COPY_SYSTEM_PROMPT,
39
+ {
40
+ config: {
41
+ max_tokens: options.maxTokens ?? POINTER_COPY_MAX_TOKENS,
42
+ modelIntent: 'latency-optimized',
43
+ },
44
+ signal: AbortSignal.timeout(options.timeoutMs ?? POINTER_COPY_TIMEOUT_MS),
45
+ },
46
+ );
47
+
48
+ const block = response.content.find((entry) => entry.type === 'text');
49
+ const text = block && 'text' in block ? block.text.trim() : '';
50
+ if (!text) return null;
51
+ const cleaned = text
52
+ .replace(/^["'`]+/, '')
53
+ .replace(/["'`]+$/, '')
54
+ .trim();
55
+ if (!cleaned) return null;
56
+ if (!includesRequiredFacts(cleaned, requiredFacts)) return null;
57
+ return cleaned;
58
+ };
59
+ }
@@ -79,7 +79,7 @@ export class ComputerUseSession {
79
79
  private pendingSurfaceActions = new Map<string, {
80
80
  resolve: (result: ToolExecutionResult) => void;
81
81
  }>();
82
- private surfaceState = new Map<string, { surfaceType: SurfaceType; data: SurfaceData }>();
82
+ private surfaceState = new Map<string, { surfaceType: SurfaceType; data: SurfaceData; title?: string }>();
83
83
  private terminalNotified = false;
84
84
  private prompter: PermissionPrompter | null = null;
85
85
 
@@ -337,7 +337,7 @@ export class ComputerUseSession {
337
337
  const awaitAction = (input.await_action as boolean) ?? isInteractive;
338
338
 
339
339
  // Track surface state for ui_update merging
340
- this.surfaceState.set(surfaceId, { surfaceType, data });
340
+ this.surfaceState.set(surfaceId, { surfaceType, data, title });
341
341
 
342
342
  this.sendToClient({
343
343
  type: 'ui_surface_show',
@@ -532,9 +532,6 @@ export class ComputerUseSession {
532
532
  maxTokens: 4096,
533
533
  maxInputTokens: cuConfig.contextWindow.maxInputTokens,
534
534
  toolChoice: { type: 'any' },
535
- // Allow MAX_STEPS non-terminal actions plus one terminal turn
536
- // (computer_use_done/computer_use_respond), since AgentLoop caps tool turns globally.
537
- maxToolUseTurns: MAX_STEPS + 1,
538
535
  },
539
536
  toolDefs,
540
537
  toolExecutor,