@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.
- package/ARCHITECTURE.md +2 -2
- package/README.md +2 -23
- package/docs/architecture/integrations.md +45 -41
- package/docs/architecture/keychain-broker.md +3 -3
- package/docs/runbook-trusted-contacts.md +3 -8
- package/hook-templates/debug-prompt-logger/hook.json +1 -1
- package/hook-templates/debug-prompt-logger/run.sh +1 -3
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +0 -1
- package/src/__tests__/anthropic-provider.test.ts +156 -0
- package/src/__tests__/approval-cascade.test.ts +810 -0
- package/src/__tests__/approval-primitive.test.ts +0 -1
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +12 -34
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/channel-guardian.test.ts +0 -2
- package/src/__tests__/channel-readiness-routes.test.ts +15 -6
- package/src/__tests__/channel-readiness-service.test.ts +10 -9
- package/src/__tests__/checker.test.ts +9 -29
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-tools.test.ts +2 -19
- package/src/__tests__/config-watcher.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-image-dimensions.test.ts +332 -0
- package/src/__tests__/context-token-estimator.test.ts +196 -13
- package/src/__tests__/conversation-attention-store.test.ts +0 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +144 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-metadata-store.test.ts +64 -73
- package/src/__tests__/credential-security-invariants.test.ts +13 -7
- package/src/__tests__/credential-vault-unit.test.ts +280 -49
- package/src/__tests__/credential-vault.test.ts +138 -16
- package/src/__tests__/credentials-cli.test.ts +71 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
- package/src/__tests__/heartbeat-service.test.ts +0 -1
- package/src/__tests__/host-cu-proxy.test.ts +629 -0
- package/src/__tests__/host-shell-tool.test.ts +27 -15
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/ingress-url-consistency.test.ts +14 -21
- package/src/__tests__/integration-status.test.ts +32 -51
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +10 -9
- package/src/__tests__/keychain-broker-client.test.ts +11 -43
- package/src/__tests__/notification-routing-intent.test.ts +0 -1
- package/src/__tests__/oauth-cli.test.ts +373 -14
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/oauth-store.test.ts +756 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +0 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +15 -21
- package/src/__tests__/recording-handler.test.ts +3 -4
- package/src/__tests__/registry.test.ts +2 -2
- package/src/__tests__/runtime-events-sse.test.ts +55 -7
- package/src/__tests__/schedule-store.test.ts +0 -1
- package/src/__tests__/scheduler-recurrence.test.ts +0 -1
- package/src/__tests__/scoped-approval-grants.test.ts +0 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
- package/src/__tests__/secret-ingress-handler.test.ts +0 -1
- package/src/__tests__/send-endpoint-busy.test.ts +21 -6
- package/src/__tests__/sequence-store.test.ts +0 -1
- package/src/__tests__/session-init.benchmark.test.ts +4 -5
- package/src/__tests__/skill-include-graph.test.ts +66 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-load-tool.test.ts +149 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skills-uninstall.test.ts +1 -1
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-channel-config.test.ts +67 -3
- package/src/__tests__/slack-share-routes.test.ts +17 -19
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
- package/src/__tests__/terminal-tools.test.ts +4 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
- package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
- package/src/__tests__/trust-store.test.ts +1 -22
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +0 -16
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/agent/ax-tree-compaction.test.ts +235 -0
- package/src/agent/loop.ts +76 -130
- package/src/calls/call-domain.ts +1 -6
- package/src/calls/relay-server.ts +9 -13
- package/src/calls/twilio-config.ts +2 -7
- package/src/calls/twilio-routes.ts +1 -2
- package/src/calls/voice-ingress-preflight.ts +1 -1
- package/src/cli/commands/browser-relay.ts +18 -12
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/credentials.ts +101 -15
- package/src/cli/commands/oauth/apps.ts +255 -0
- package/src/cli/commands/oauth/connections.ts +299 -0
- package/src/cli/commands/oauth/index.ts +52 -0
- package/src/cli/commands/oauth/providers.ts +242 -0
- package/src/cli/commands/skills.ts +4 -338
- package/src/cli/program.ts +1 -5
- package/src/cli/reference.ts +1 -3
- package/src/config/assistant-feature-flags.ts +0 -3
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +22 -4
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
- package/src/config/bundled-skills/settings/SKILL.md +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +2 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
- package/src/config/env-registry.ts +14 -83
- package/src/config/env.ts +11 -50
- package/src/config/feature-flag-registry.json +16 -16
- package/src/config/loader.ts +0 -6
- package/src/config/schema.ts +3 -1
- package/src/config/skills.ts +21 -2
- package/src/context/image-dimensions.ts +229 -0
- package/src/context/token-estimator.ts +75 -12
- package/src/context/window-manager.ts +49 -10
- package/src/daemon/assistant-attachments.ts +1 -13
- package/src/daemon/handlers/config-ingress.ts +8 -33
- package/src/daemon/handlers/config-slack-channel.ts +49 -46
- package/src/daemon/handlers/config-telegram.ts +32 -16
- package/src/daemon/handlers/sessions.ts +10 -24
- package/src/daemon/handlers/shared.ts +0 -130
- package/src/daemon/host-cu-proxy.ts +401 -0
- package/src/daemon/lifecycle.ts +36 -68
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/computer-use.ts +2 -119
- package/src/daemon/message-types/host-cu.ts +19 -0
- package/src/daemon/message-types/messages.ts +3 -0
- package/src/daemon/server.ts +14 -21
- package/src/daemon/session-agent-loop-handlers.ts +2 -0
- package/src/daemon/session-attachments.ts +1 -2
- package/src/daemon/session-slash.ts +1 -1
- package/src/daemon/session-surfaces.ts +40 -28
- package/src/daemon/session-tool-setup.ts +2 -9
- package/src/daemon/session.ts +138 -15
- package/src/daemon/tool-side-effects.ts +2 -8
- package/src/daemon/watch-handler.ts +2 -2
- package/src/events/tool-metrics-listener.ts +2 -2
- package/src/hooks/manager.ts +1 -4
- package/src/inbound/public-ingress-urls.ts +7 -7
- package/src/logfire.ts +16 -5
- package/src/memory/conversation-key-store.ts +21 -0
- package/src/memory/db-init.ts +4 -0
- package/src/memory/migrations/149-oauth-tables.ts +60 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/oauth.ts +65 -0
- package/src/messaging/provider.ts +4 -4
- package/src/messaging/providers/gmail/client.ts +82 -2
- package/src/messaging/providers/gmail/people-client.ts +10 -10
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
- package/src/messaging/providers/whatsapp/adapter.ts +11 -8
- package/src/messaging/registry.ts +2 -32
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/signal.ts +4 -5
- package/src/oauth/byo-connection.test.ts +126 -25
- package/src/oauth/byo-connection.ts +22 -6
- package/src/oauth/connect-orchestrator.ts +113 -57
- package/src/oauth/connect-types.ts +17 -23
- package/src/oauth/connection-resolver.ts +35 -11
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +104 -0
- package/src/oauth/oauth-store.ts +496 -0
- package/src/oauth/platform-connection.test.ts +29 -0
- package/src/oauth/platform-connection.ts +6 -5
- package/src/oauth/provider-behaviors.ts +124 -0
- package/src/oauth/scope-policy.ts +9 -2
- package/src/oauth/seed-providers.ts +161 -0
- package/src/oauth/token-persistence.ts +74 -78
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +0 -1
- package/src/permissions/prompter.ts +10 -1
- package/src/permissions/trust-store.ts +13 -0
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
- package/src/prompts/system-prompt.ts +28 -40
- package/src/providers/anthropic/client.ts +133 -24
- package/src/providers/retry.ts +1 -27
- package/src/runtime/auth/route-policy.ts +0 -3
- package/src/runtime/channel-reply-delivery.ts +0 -40
- package/src/runtime/gateway-client.ts +0 -7
- package/src/runtime/http-server.ts +8 -6
- package/src/runtime/http-types.ts +2 -2
- package/src/runtime/middleware/twilio-validation.ts +1 -11
- package/src/runtime/pending-interactions.ts +14 -12
- package/src/runtime/routes/channel-delivery-routes.ts +0 -1
- package/src/runtime/routes/conversation-routes.ts +73 -19
- package/src/runtime/routes/events-routes.ts +21 -11
- package/src/runtime/routes/host-cu-routes.ts +97 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
- package/src/runtime/routes/integrations/slack/share.ts +6 -7
- package/src/runtime/routes/log-export-routes.ts +126 -8
- package/src/runtime/routes/settings-routes.ts +55 -48
- package/src/runtime/routes/surface-action-routes.ts +1 -1
- package/src/runtime/routes/watch-routes.ts +128 -0
- package/src/schedule/integration-status.ts +10 -9
- package/src/security/credential-key.ts +0 -156
- package/src/security/keychain-broker-client.ts +5 -6
- package/src/security/oauth2.ts +1 -1
- package/src/security/token-manager.ts +119 -46
- package/src/skills/catalog-install.ts +358 -0
- package/src/skills/include-graph.ts +32 -0
- package/src/telegram/bot-username.ts +2 -3
- package/src/tools/browser/network-recorder.ts +1 -1
- package/src/tools/browser/network-recording-types.ts +1 -1
- package/src/tools/computer-use/definitions.ts +46 -11
- package/src/tools/computer-use/registry.ts +4 -5
- package/src/tools/credentials/broker.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +17 -121
- package/src/tools/credentials/vault.ts +94 -167
- package/src/tools/registry.ts +2 -7
- package/src/tools/skills/load.ts +62 -3
- package/src/tools/watch/watch-state.ts +0 -12
- package/src/util/logger.ts +7 -41
- package/src/util/platform.ts +9 -28
- package/src/watcher/providers/google-calendar.ts +2 -1
- package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
- package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
- package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
- package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
- package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
- package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
- package/src/cli/commands/dev.ts +0 -129
- package/src/cli/commands/map.ts +0 -391
- package/src/cli/commands/oauth.ts +0 -77
- package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +0 -16
- package/src/daemon/computer-use-session.ts +0 -1026
- package/src/daemon/ride-shotgun-handler.ts +0 -569
- package/src/oauth/provider-base-urls.ts +0 -21
- package/src/oauth/provider-profiles.ts +0 -192
- package/src/prompts/computer-use-prompt.ts +0 -98
- package/src/runtime/routes/computer-use-routes.ts +0 -641
- package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
- package/src/runtime/telegram-streaming-delivery.ts +0 -393
- 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
|
-
};
|