@vellumai/assistant 0.4.48 → 0.4.49

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 (252) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/README.md +2 -23
  3. package/docs/architecture/integrations.md +45 -41
  4. package/docs/architecture/keychain-broker.md +3 -3
  5. package/docs/runbook-trusted-contacts.md +3 -8
  6. package/hook-templates/debug-prompt-logger/hook.json +1 -1
  7. package/hook-templates/debug-prompt-logger/run.sh +1 -3
  8. package/package.json +1 -1
  9. package/src/__tests__/actor-token-service.test.ts +0 -1
  10. package/src/__tests__/anthropic-provider.test.ts +156 -0
  11. package/src/__tests__/approval-cascade.test.ts +810 -0
  12. package/src/__tests__/approval-primitive.test.ts +0 -1
  13. package/src/__tests__/approval-routes-http.test.ts +2 -0
  14. package/src/__tests__/assistant-attachments.test.ts +12 -34
  15. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
  16. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
  17. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  18. package/src/__tests__/channel-guardian.test.ts +0 -2
  19. package/src/__tests__/channel-readiness-routes.test.ts +15 -6
  20. package/src/__tests__/channel-readiness-service.test.ts +10 -9
  21. package/src/__tests__/checker.test.ts +9 -29
  22. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  23. package/src/__tests__/computer-use-tools.test.ts +2 -19
  24. package/src/__tests__/config-watcher.test.ts +0 -1
  25. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  26. package/src/__tests__/context-image-dimensions.test.ts +332 -0
  27. package/src/__tests__/context-token-estimator.test.ts +196 -13
  28. package/src/__tests__/conversation-attention-store.test.ts +0 -1
  29. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  30. package/src/__tests__/conversation-routes-guardian-reply.test.ts +144 -0
  31. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  32. package/src/__tests__/credential-metadata-store.test.ts +64 -73
  33. package/src/__tests__/credential-security-invariants.test.ts +13 -7
  34. package/src/__tests__/credential-vault-unit.test.ts +280 -49
  35. package/src/__tests__/credential-vault.test.ts +138 -16
  36. package/src/__tests__/credentials-cli.test.ts +71 -0
  37. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  38. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  39. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  40. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
  41. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
  42. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  43. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  44. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
  45. package/src/__tests__/heartbeat-service.test.ts +0 -1
  46. package/src/__tests__/host-cu-proxy.test.ts +629 -0
  47. package/src/__tests__/host-shell-tool.test.ts +27 -15
  48. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  49. package/src/__tests__/ingress-url-consistency.test.ts +14 -21
  50. package/src/__tests__/integration-status.test.ts +32 -51
  51. package/src/__tests__/intent-routing.test.ts +0 -1
  52. package/src/__tests__/invite-routes-http.test.ts +10 -9
  53. package/src/__tests__/keychain-broker-client.test.ts +11 -43
  54. package/src/__tests__/notification-routing-intent.test.ts +0 -1
  55. package/src/__tests__/oauth-cli.test.ts +373 -14
  56. package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
  57. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  58. package/src/__tests__/oauth-store.test.ts +756 -0
  59. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
  60. package/src/__tests__/provider-error-scenarios.test.ts +0 -1
  61. package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
  62. package/src/__tests__/public-ingress-urls.test.ts +15 -21
  63. package/src/__tests__/recording-handler.test.ts +3 -4
  64. package/src/__tests__/registry.test.ts +2 -2
  65. package/src/__tests__/runtime-events-sse.test.ts +55 -7
  66. package/src/__tests__/schedule-store.test.ts +0 -1
  67. package/src/__tests__/scheduler-recurrence.test.ts +0 -1
  68. package/src/__tests__/scoped-approval-grants.test.ts +0 -1
  69. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
  70. package/src/__tests__/secret-ingress-handler.test.ts +0 -1
  71. package/src/__tests__/send-endpoint-busy.test.ts +21 -6
  72. package/src/__tests__/sequence-store.test.ts +0 -1
  73. package/src/__tests__/session-init.benchmark.test.ts +4 -5
  74. package/src/__tests__/skill-include-graph.test.ts +66 -0
  75. package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
  76. package/src/__tests__/skill-load-tool.test.ts +149 -1
  77. package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
  78. package/src/__tests__/skills-uninstall.test.ts +1 -1
  79. package/src/__tests__/skills.test.ts +3 -3
  80. package/src/__tests__/slack-channel-config.test.ts +67 -3
  81. package/src/__tests__/slack-share-routes.test.ts +17 -19
  82. package/src/__tests__/system-prompt.test.ts +0 -1
  83. package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
  84. package/src/__tests__/terminal-tools.test.ts +4 -3
  85. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  86. package/src/__tests__/tool-approval-handler.test.ts +0 -1
  87. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
  88. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  89. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
  90. package/src/__tests__/tool-executor.test.ts +0 -1
  91. package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
  92. package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
  93. package/src/__tests__/trust-store.test.ts +1 -22
  94. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  95. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  96. package/src/__tests__/twilio-routes.test.ts +0 -16
  97. package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
  98. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
  99. package/src/agent/ax-tree-compaction.test.ts +235 -0
  100. package/src/agent/loop.ts +76 -130
  101. package/src/calls/call-domain.ts +1 -6
  102. package/src/calls/relay-server.ts +9 -13
  103. package/src/calls/twilio-config.ts +2 -7
  104. package/src/calls/twilio-routes.ts +1 -2
  105. package/src/calls/voice-ingress-preflight.ts +1 -1
  106. package/src/cli/commands/browser-relay.ts +18 -12
  107. package/src/cli/commands/completions.ts +0 -3
  108. package/src/cli/commands/credentials.ts +101 -15
  109. package/src/cli/commands/oauth/apps.ts +255 -0
  110. package/src/cli/commands/oauth/connections.ts +299 -0
  111. package/src/cli/commands/oauth/index.ts +52 -0
  112. package/src/cli/commands/oauth/providers.ts +242 -0
  113. package/src/cli/commands/skills.ts +4 -338
  114. package/src/cli/program.ts +1 -5
  115. package/src/cli/reference.ts +1 -3
  116. package/src/config/assistant-feature-flags.ts +0 -3
  117. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  118. package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
  119. package/src/config/bundled-skills/computer-use/TOOLS.json +22 -4
  120. package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
  121. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
  122. package/src/config/bundled-skills/settings/SKILL.md +1 -1
  123. package/src/config/bundled-skills/settings/TOOLS.json +2 -8
  124. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
  125. package/src/config/env-registry.ts +14 -83
  126. package/src/config/env.ts +11 -50
  127. package/src/config/feature-flag-registry.json +16 -16
  128. package/src/config/loader.ts +0 -6
  129. package/src/config/schema.ts +3 -1
  130. package/src/config/skills.ts +21 -2
  131. package/src/context/image-dimensions.ts +229 -0
  132. package/src/context/token-estimator.ts +75 -12
  133. package/src/context/window-manager.ts +49 -10
  134. package/src/daemon/assistant-attachments.ts +1 -13
  135. package/src/daemon/handlers/config-ingress.ts +8 -33
  136. package/src/daemon/handlers/config-slack-channel.ts +49 -46
  137. package/src/daemon/handlers/config-telegram.ts +32 -16
  138. package/src/daemon/handlers/sessions.ts +10 -24
  139. package/src/daemon/handlers/shared.ts +0 -130
  140. package/src/daemon/host-cu-proxy.ts +401 -0
  141. package/src/daemon/lifecycle.ts +36 -68
  142. package/src/daemon/message-protocol.ts +3 -0
  143. package/src/daemon/message-types/computer-use.ts +2 -119
  144. package/src/daemon/message-types/host-cu.ts +19 -0
  145. package/src/daemon/message-types/messages.ts +3 -0
  146. package/src/daemon/server.ts +14 -21
  147. package/src/daemon/session-agent-loop-handlers.ts +2 -0
  148. package/src/daemon/session-attachments.ts +1 -2
  149. package/src/daemon/session-slash.ts +1 -1
  150. package/src/daemon/session-surfaces.ts +40 -28
  151. package/src/daemon/session-tool-setup.ts +2 -9
  152. package/src/daemon/session.ts +138 -15
  153. package/src/daemon/tool-side-effects.ts +2 -8
  154. package/src/daemon/watch-handler.ts +2 -2
  155. package/src/events/tool-metrics-listener.ts +2 -2
  156. package/src/hooks/manager.ts +1 -4
  157. package/src/inbound/public-ingress-urls.ts +7 -7
  158. package/src/logfire.ts +16 -5
  159. package/src/memory/conversation-key-store.ts +21 -0
  160. package/src/memory/db-init.ts +4 -0
  161. package/src/memory/migrations/149-oauth-tables.ts +60 -0
  162. package/src/memory/migrations/index.ts +1 -0
  163. package/src/memory/schema/index.ts +1 -0
  164. package/src/memory/schema/oauth.ts +65 -0
  165. package/src/messaging/provider.ts +4 -4
  166. package/src/messaging/providers/gmail/client.ts +82 -2
  167. package/src/messaging/providers/gmail/people-client.ts +10 -10
  168. package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
  169. package/src/messaging/providers/whatsapp/adapter.ts +11 -8
  170. package/src/messaging/registry.ts +2 -32
  171. package/src/notifications/copy-composer.ts +0 -5
  172. package/src/notifications/signal.ts +4 -5
  173. package/src/oauth/byo-connection.test.ts +126 -25
  174. package/src/oauth/byo-connection.ts +22 -6
  175. package/src/oauth/connect-orchestrator.ts +113 -57
  176. package/src/oauth/connect-types.ts +17 -23
  177. package/src/oauth/connection-resolver.ts +35 -11
  178. package/src/oauth/connection.ts +1 -1
  179. package/src/oauth/manual-token-connection.ts +104 -0
  180. package/src/oauth/oauth-store.ts +496 -0
  181. package/src/oauth/platform-connection.test.ts +29 -0
  182. package/src/oauth/platform-connection.ts +6 -5
  183. package/src/oauth/provider-behaviors.ts +124 -0
  184. package/src/oauth/scope-policy.ts +9 -2
  185. package/src/oauth/seed-providers.ts +161 -0
  186. package/src/oauth/token-persistence.ts +74 -78
  187. package/src/permissions/checker.ts +3 -3
  188. package/src/permissions/defaults.ts +0 -1
  189. package/src/permissions/prompter.ts +10 -1
  190. package/src/permissions/trust-store.ts +13 -0
  191. package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
  192. package/src/prompts/system-prompt.ts +28 -40
  193. package/src/providers/anthropic/client.ts +133 -24
  194. package/src/providers/retry.ts +1 -27
  195. package/src/runtime/auth/route-policy.ts +0 -3
  196. package/src/runtime/channel-reply-delivery.ts +0 -40
  197. package/src/runtime/gateway-client.ts +0 -7
  198. package/src/runtime/http-server.ts +8 -6
  199. package/src/runtime/http-types.ts +2 -2
  200. package/src/runtime/middleware/twilio-validation.ts +1 -11
  201. package/src/runtime/pending-interactions.ts +14 -12
  202. package/src/runtime/routes/channel-delivery-routes.ts +0 -1
  203. package/src/runtime/routes/conversation-routes.ts +73 -19
  204. package/src/runtime/routes/events-routes.ts +21 -11
  205. package/src/runtime/routes/host-cu-routes.ts +97 -0
  206. package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
  207. package/src/runtime/routes/integrations/slack/share.ts +6 -7
  208. package/src/runtime/routes/log-export-routes.ts +126 -8
  209. package/src/runtime/routes/settings-routes.ts +55 -48
  210. package/src/runtime/routes/surface-action-routes.ts +1 -1
  211. package/src/runtime/routes/watch-routes.ts +128 -0
  212. package/src/schedule/integration-status.ts +10 -9
  213. package/src/security/credential-key.ts +0 -156
  214. package/src/security/keychain-broker-client.ts +5 -6
  215. package/src/security/oauth2.ts +1 -1
  216. package/src/security/token-manager.ts +119 -46
  217. package/src/skills/catalog-install.ts +358 -0
  218. package/src/skills/include-graph.ts +32 -0
  219. package/src/telegram/bot-username.ts +2 -3
  220. package/src/tools/browser/network-recorder.ts +1 -1
  221. package/src/tools/browser/network-recording-types.ts +1 -1
  222. package/src/tools/computer-use/definitions.ts +46 -11
  223. package/src/tools/computer-use/registry.ts +4 -5
  224. package/src/tools/credentials/broker.ts +1 -2
  225. package/src/tools/credentials/metadata-store.ts +17 -121
  226. package/src/tools/credentials/vault.ts +94 -167
  227. package/src/tools/registry.ts +2 -7
  228. package/src/tools/skills/load.ts +62 -3
  229. package/src/tools/watch/watch-state.ts +0 -12
  230. package/src/util/logger.ts +7 -41
  231. package/src/util/platform.ts +9 -28
  232. package/src/watcher/providers/google-calendar.ts +2 -1
  233. package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
  234. package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
  235. package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
  236. package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
  237. package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
  238. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
  239. package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
  240. package/src/cli/commands/dev.ts +0 -129
  241. package/src/cli/commands/map.ts +0 -391
  242. package/src/cli/commands/oauth.ts +0 -77
  243. package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +0 -16
  244. package/src/daemon/computer-use-session.ts +0 -1026
  245. package/src/daemon/ride-shotgun-handler.ts +0 -569
  246. package/src/oauth/provider-base-urls.ts +0 -21
  247. package/src/oauth/provider-profiles.ts +0 -192
  248. package/src/prompts/computer-use-prompt.ts +0 -98
  249. package/src/runtime/routes/computer-use-routes.ts +0 -641
  250. package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
  251. package/src/runtime/telegram-streaming-delivery.ts +0 -393
  252. package/src/tools/computer-use/request-computer-control.ts +0 -56
@@ -1,393 +0,0 @@
1
- import type { ServerMessage } from "../daemon/message-protocol.js";
2
- import { getLogger } from "../util/logger.js";
3
- import type { ApprovalUIMetadata } from "./channel-approval-types.js";
4
- import type { ChannelDeliveryResult } from "./gateway-client.js";
5
- import { deliverChannelReply } from "./gateway-client.js";
6
-
7
- const log = getLogger("telegram-streaming-delivery");
8
-
9
- const EDIT_THROTTLE_MS = 1000; // Min interval between edits
10
- const TELEGRAM_MAX_TEXT_LEN = 4000; // Max chars per Telegram message
11
- const MIN_INITIAL_CHARS = 20; // Min chars before sending first message
12
-
13
- export interface TelegramStreamingOptions {
14
- callbackUrl: string;
15
- chatId: string;
16
- mintBearerToken: () => string;
17
- assistantId?: string;
18
- }
19
-
20
- export class TelegramStreamingDelivery {
21
- private readonly opts: TelegramStreamingOptions;
22
- private buffer = ""; // Accumulated text not yet sent
23
- private currentMessageId: number | null = null; // ID of current Telegram message
24
- private currentMessageText = ""; // Full text of current message
25
- private lastSentText = ""; // Last text successfully sent to Telegram
26
- private lastEditAt = 0; // Timestamp of last edit
27
- private editTimer: ReturnType<typeof setTimeout> | null = null;
28
- private messageCount = 0; // Total messages sent
29
- private finished = false;
30
- private textDelivered = false; // True once at least some text was sent
31
- private finishOk = false; // True only when finish() completes without error
32
- private initialSendInFlight = false; // Synchronous guard against duplicate initial sends
33
- private initialSendPromise: Promise<ChannelDeliveryResult> | null = null; // Tracks in-flight initial send
34
-
35
- constructor(opts: TelegramStreamingOptions) {
36
- this.opts = opts;
37
- }
38
-
39
- // ── Public API ──────────────────────────────────────────────────────
40
-
41
- onEvent(msg: ServerMessage): void {
42
- if (this.finished) return;
43
- switch (msg.type) {
44
- case "assistant_text_delta":
45
- this.onTextDelta(msg.text);
46
- break;
47
- case "tool_use_preview_start":
48
- // Early preview of tool use — ignored by Telegram; full tool_use_start follows.
49
- break;
50
- case "tool_use_start":
51
- // Flush buffer and send an edit so the message is up-to-date before the tool runs
52
- if (this.buffer.length > 0 && this.currentMessageId) {
53
- this.flushEdit();
54
- } else if (this.buffer.length > 0 && !this.textDelivered) {
55
- // No message sent yet — just move buffer to currentMessageText
56
- this.currentMessageText += this.buffer;
57
- this.buffer = "";
58
- }
59
- // When textDelivered is true but currentMessageId is null (no-messageId
60
- // response), leave buffer as-is so finish() sends it as a new message.
61
- break;
62
- case "message_complete":
63
- // Don't finalize here — let finish() handle it
64
- break;
65
- }
66
- }
67
-
68
- async finish(approval?: ApprovalUIMetadata): Promise<void> {
69
- this.finished = true;
70
- if (this.editTimer) {
71
- clearTimeout(this.editTimer);
72
- this.editTimer = null;
73
- }
74
-
75
- // If sendInitialMessage() is in-flight, wait for it so currentMessageId is resolved
76
- if (this.initialSendPromise) {
77
- await this.initialSendPromise.catch(() => {
78
- // Error already logged in sendInitialMessage; proceed with whatever state we have
79
- });
80
- }
81
-
82
- // Flush any remaining buffered text
83
- if (this.buffer.length > 0) {
84
- if (this.currentMessageId) {
85
- this.currentMessageText += this.buffer;
86
- this.buffer = "";
87
- } else {
88
- log.warn(
89
- {
90
- chatId: this.opts.chatId,
91
- bufferLen: this.buffer.length,
92
- currentMessageTextLen: this.currentMessageText.length,
93
- textDelivered: this.textDelivered,
94
- initialSendInFlight: this.initialSendInFlight,
95
- },
96
- "finish() sending as new message because currentMessageId is null",
97
- );
98
- if (this.textDelivered) {
99
- // Initial text already delivered but no messageId — just send remainder
100
- this.currentMessageText = this.buffer;
101
- } else {
102
- // Initial send failed, buffer has been restored — send everything
103
- this.currentMessageText += this.buffer;
104
- }
105
- this.buffer = "";
106
- // Send as new message
107
- await this.sendNewMessage(this.currentMessageText, approval);
108
- this.finishOk = true;
109
- return;
110
- }
111
- }
112
-
113
- // Buffer was empty but text was moved to currentMessageText (e.g. by
114
- // tool_use_start) before any Telegram message was created. Send it now.
115
- if (
116
- !this.currentMessageId &&
117
- !this.textDelivered &&
118
- this.currentMessageText.length > 0 &&
119
- this.buffer.length === 0
120
- ) {
121
- await this.sendNewMessage(this.currentMessageText, approval);
122
- this.finishOk = true;
123
- return;
124
- }
125
-
126
- // Text was delivered but no messageId was returned, and there are approval
127
- // buttons to attach. Send them as a new message so they aren't silently dropped.
128
- if (!this.currentMessageId && this.textDelivered && approval) {
129
- await this.sendNewMessage("", approval);
130
- this.finishOk = true;
131
- return;
132
- }
133
-
134
- // Final edit with approval buttons if present.
135
- // Skip the edit when text hasn't changed since the last successful
136
- // delivery and there are no approval buttons to attach — sending the
137
- // same text again would trigger Telegram's "message is not modified"
138
- // 400 error.
139
- if (this.currentMessageId && (this.currentMessageText || approval)) {
140
- if (!approval && this.currentMessageText === this.lastSentText) {
141
- this.finishOk = true;
142
- return;
143
- }
144
-
145
- // Enforce Telegram length limits: if the final text exceeds the max,
146
- // split it the same way flushEdit() does — finalize the current message
147
- // with text up to the limit, then send the overflow as a new message.
148
- if (this.currentMessageText.length > TELEGRAM_MAX_TEXT_LEN) {
149
- const cutText = this.currentMessageText.slice(0, TELEGRAM_MAX_TEXT_LEN);
150
- const overflow = this.currentMessageText.slice(TELEGRAM_MAX_TEXT_LEN);
151
-
152
- // Edit existing message with truncated text (no approval — it goes on the final message)
153
- await deliverChannelReply(
154
- this.opts.callbackUrl,
155
- {
156
- chatId: this.opts.chatId,
157
- text: cutText,
158
- messageId: this.currentMessageId,
159
- assistantId: this.opts.assistantId,
160
- },
161
- this.opts.mintBearerToken(),
162
- );
163
- this.lastSentText = cutText;
164
-
165
- // Send overflow (with approval buttons if present) as a new message
166
- await this.sendNewMessage(overflow, approval);
167
- this.finishOk = true;
168
- return;
169
- }
170
-
171
- await deliverChannelReply(
172
- this.opts.callbackUrl,
173
- {
174
- chatId: this.opts.chatId,
175
- text: this.currentMessageText,
176
- messageId: this.currentMessageId,
177
- assistantId: this.opts.assistantId,
178
- approval,
179
- },
180
- this.opts.mintBearerToken(),
181
- );
182
- this.lastSentText = this.currentMessageText;
183
- }
184
- this.finishOk = true;
185
- }
186
-
187
- get hasDeliveredText(): boolean {
188
- return this.textDelivered;
189
- }
190
-
191
- /** True only when finish() completed without throwing. */
192
- get finishSucceeded(): boolean {
193
- return this.finishOk;
194
- }
195
-
196
- // ── Internal ────────────────────────────────────────────────────────
197
-
198
- private onTextDelta(text: string): void {
199
- this.buffer += text;
200
- if (
201
- !this.currentMessageId &&
202
- !this.initialSendInFlight &&
203
- !this.textDelivered &&
204
- this.buffer.length + this.currentMessageText.length >= MIN_INITIAL_CHARS
205
- ) {
206
- this.sendInitialMessage();
207
- } else if (this.currentMessageId) {
208
- this.scheduleEdit();
209
- }
210
- }
211
-
212
- private sendInitialMessage(): void {
213
- this.initialSendInFlight = true;
214
- this.currentMessageText += this.buffer;
215
- this.buffer = "";
216
-
217
- const textSnapshot = this.currentMessageText;
218
- const promise = deliverChannelReply(
219
- this.opts.callbackUrl,
220
- {
221
- chatId: this.opts.chatId,
222
- text: textSnapshot,
223
- assistantId: this.opts.assistantId,
224
- },
225
- this.opts.mintBearerToken(),
226
- );
227
- this.initialSendPromise = promise;
228
-
229
- promise
230
- .then((result) => {
231
- if (result.messageId) {
232
- this.currentMessageId = result.messageId;
233
- } else {
234
- log.warn(
235
- { chatId: this.opts.chatId },
236
- "Initial streaming send succeeded but no messageId in response",
237
- );
238
- }
239
- this.textDelivered = true;
240
- this.lastSentText = textSnapshot;
241
- this.messageCount++;
242
- this.lastEditAt = Date.now();
243
- this.initialSendInFlight = false;
244
- })
245
- .catch((err) => {
246
- log.error(
247
- { err, chatId: this.opts.chatId },
248
- "Failed to send initial streaming message",
249
- );
250
- // Push the initial text back into the buffer so finish() can send
251
- // the full accumulated text as a single message
252
- this.buffer = this.currentMessageText + this.buffer;
253
- this.currentMessageText = "";
254
- // Fall back: clear guard so future deltas can retry
255
- this.initialSendInFlight = false;
256
- this.currentMessageId = null;
257
- });
258
- }
259
-
260
- private scheduleEdit(): void {
261
- const elapsed = Date.now() - this.lastEditAt;
262
- if (elapsed >= EDIT_THROTTLE_MS) {
263
- this.flushEdit();
264
- } else if (!this.editTimer) {
265
- const remaining = EDIT_THROTTLE_MS - elapsed;
266
- this.editTimer = setTimeout(() => this.flushEdit(), remaining);
267
- }
268
- }
269
-
270
- private flushEdit(): void {
271
- if (this.editTimer) {
272
- clearTimeout(this.editTimer);
273
- this.editTimer = null;
274
- }
275
- if (this.buffer.length === 0) return;
276
-
277
- this.currentMessageText += this.buffer;
278
- this.buffer = "";
279
-
280
- if (this.currentMessageText.length > TELEGRAM_MAX_TEXT_LEN) {
281
- // Split: finalize current message with text up to the limit,
282
- // then send the remainder as a new message.
283
- const cutText = this.currentMessageText.slice(0, TELEGRAM_MAX_TEXT_LEN);
284
- const overflow = this.currentMessageText.slice(TELEGRAM_MAX_TEXT_LEN);
285
-
286
- if (this.currentMessageId) {
287
- deliverChannelReply(
288
- this.opts.callbackUrl,
289
- {
290
- chatId: this.opts.chatId,
291
- text: cutText,
292
- messageId: this.currentMessageId,
293
- assistantId: this.opts.assistantId,
294
- },
295
- this.opts.mintBearerToken(),
296
- )
297
- .then(() => {
298
- this.lastSentText = cutText;
299
- })
300
- .catch((err) => {
301
- log.error(
302
- { err, chatId: this.opts.chatId },
303
- "Failed to edit message at split boundary",
304
- );
305
- });
306
- }
307
-
308
- // Reset and send overflow as a new message
309
- this.currentMessageId = null;
310
- this.currentMessageText = overflow;
311
- const overflowSnapshot = this.currentMessageText;
312
-
313
- deliverChannelReply(
314
- this.opts.callbackUrl,
315
- {
316
- chatId: this.opts.chatId,
317
- text: overflowSnapshot,
318
- assistantId: this.opts.assistantId,
319
- },
320
- this.opts.mintBearerToken(),
321
- )
322
- .then((result) => {
323
- if (result.messageId) {
324
- this.currentMessageId = result.messageId;
325
- }
326
- this.lastSentText = overflowSnapshot;
327
- this.messageCount++;
328
- this.lastEditAt = Date.now();
329
- })
330
- .catch((err) => {
331
- log.error(
332
- { err, chatId: this.opts.chatId },
333
- "Failed to send overflow message",
334
- );
335
- this.currentMessageId = null;
336
- });
337
- return;
338
- }
339
-
340
- if (this.currentMessageId) {
341
- const textSnapshot = this.currentMessageText;
342
- deliverChannelReply(
343
- this.opts.callbackUrl,
344
- {
345
- chatId: this.opts.chatId,
346
- text: this.currentMessageText,
347
- messageId: this.currentMessageId,
348
- assistantId: this.opts.assistantId,
349
- },
350
- this.opts.mintBearerToken(),
351
- )
352
- .then(() => {
353
- this.lastSentText = textSnapshot;
354
- })
355
- .catch((err) => {
356
- log.error(
357
- { err, chatId: this.opts.chatId },
358
- "Failed to edit streaming message",
359
- );
360
- });
361
- }
362
- this.lastEditAt = Date.now();
363
- }
364
-
365
- private async sendNewMessage(
366
- text: string,
367
- approval?: ApprovalUIMetadata,
368
- ): Promise<void> {
369
- try {
370
- const result = await deliverChannelReply(
371
- this.opts.callbackUrl,
372
- {
373
- chatId: this.opts.chatId,
374
- text,
375
- assistantId: this.opts.assistantId,
376
- approval,
377
- },
378
- this.opts.mintBearerToken(),
379
- );
380
- if (result.messageId) {
381
- this.currentMessageId = result.messageId;
382
- }
383
- this.textDelivered = true;
384
- this.lastSentText = text;
385
- this.messageCount++;
386
- } catch (err) {
387
- log.error(
388
- { err, chatId: this.opts.chatId },
389
- "Failed to send new streaming message",
390
- );
391
- }
392
- }
393
- }
@@ -1,56 +0,0 @@
1
- /**
2
- * computer_use_request_control tool definition.
3
- *
4
- * This tool allows a text_qa session to escalate to foreground computer use
5
- * when the user explicitly requests it (e.g. "go ahead and do it", "take over
6
- * for 20 seconds"). It is a proxy tool — execution is handled by the session's
7
- * surfaceProxyResolver, which creates a CU session and sends a task_routed
8
- * message to the client.
9
- *
10
- * This tool is only available to text_qa sessions. It must NOT be added to
11
- * CU sessions (that would be recursive).
12
- *
13
- * Part of the bundled computer-use skill. The definition here is imported by
14
- * buildToolDefinitions() so text_qa sessions can include it without
15
- * preactivating the entire skill.
16
- */
17
-
18
- import { RiskLevel } from "../../permissions/types.js";
19
- import type { ToolDefinition } from "../../providers/types.js";
20
- import type { Tool, ToolExecutionResult } from "../types.js";
21
-
22
- export const requestComputerControlTool: Tool = {
23
- name: "computer_use_request_control",
24
- description:
25
- "Escalate to foreground computer use. Call this when the user explicitly asks you to " +
26
- 'take control of their computer to perform a task (e.g. "go ahead and do it", ' +
27
- '"take over", "open that for me"). Provide a concise description of the task ' +
28
- "that computer use should accomplish.",
29
- category: "computer-use",
30
- defaultRiskLevel: RiskLevel.Low,
31
- executionMode: "proxy",
32
-
33
- getDefinition(): ToolDefinition {
34
- return {
35
- name: this.name,
36
- description: this.description,
37
- input_schema: {
38
- type: "object",
39
- properties: {
40
- task: {
41
- type: "string",
42
- description:
43
- "Concise description of what computer use should accomplish",
44
- },
45
- },
46
- required: ["task"],
47
- },
48
- };
49
- },
50
-
51
- execute(): Promise<ToolExecutionResult> {
52
- throw new Error(
53
- "Proxy tool: execution must be forwarded via surfaceProxyResolver",
54
- );
55
- },
56
- };