@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,4 +1,5 @@
|
|
|
1
1
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
2
|
+
import { parseImageDimensions } from "./image-dimensions.js";
|
|
2
3
|
|
|
3
4
|
const CHARS_PER_TOKEN = 4;
|
|
4
5
|
const MESSAGE_OVERHEAD_TOKENS = 4;
|
|
@@ -12,6 +13,22 @@ const OTHER_BLOCK_TOKENS = 16;
|
|
|
12
13
|
const SYSTEM_PROMPT_OVERHEAD_TOKENS = 8;
|
|
13
14
|
const GEMINI_INLINE_FILE_MIME_TYPES = new Set(["application/pdf"]);
|
|
14
15
|
|
|
16
|
+
// Anthropic scales images to fit within 1568x1568 maintaining aspect ratio,
|
|
17
|
+
// then charges ~(width * height) / 750 tokens.
|
|
18
|
+
const ANTHROPIC_IMAGE_MAX_DIMENSION = 1568;
|
|
19
|
+
const ANTHROPIC_IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
20
|
+
const ANTHROPIC_IMAGE_MAX_TOKENS = Math.ceil(
|
|
21
|
+
ANTHROPIC_IMAGE_MAX_DIMENSION *
|
|
22
|
+
ANTHROPIC_IMAGE_MAX_DIMENSION *
|
|
23
|
+
ANTHROPIC_IMAGE_TOKENS_PER_PIXEL,
|
|
24
|
+
); // ~3,277 tokens
|
|
25
|
+
|
|
26
|
+
// Anthropic renders each PDF page as an image (~1,568 tokens at standard
|
|
27
|
+
// resolution) plus any extracted text. Typical PDF pages are 50-150 KB.
|
|
28
|
+
// Using ~100 KB/page and ~1,600 tokens/page gives ~0.016 tokens/byte.
|
|
29
|
+
const ANTHROPIC_PDF_TOKENS_PER_BYTE = 0.016;
|
|
30
|
+
const ANTHROPIC_PDF_MIN_TOKENS = 1600; // At least one page
|
|
31
|
+
|
|
15
32
|
export interface TokenEstimatorOptions {
|
|
16
33
|
providerName?: string;
|
|
17
34
|
}
|
|
@@ -21,21 +38,69 @@ export function estimateTextTokens(text: string): number {
|
|
|
21
38
|
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
22
39
|
}
|
|
23
40
|
|
|
24
|
-
function
|
|
41
|
+
function estimateAnthropicPdfTokens(base64Data: string): number {
|
|
42
|
+
const rawBytes = Math.ceil((base64Data.length * 3) / 4);
|
|
43
|
+
return Math.max(
|
|
44
|
+
ANTHROPIC_PDF_MIN_TOKENS,
|
|
45
|
+
Math.ceil(rawBytes * ANTHROPIC_PDF_TOKENS_PER_BYTE),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function estimateFileDataTokens(
|
|
25
50
|
block: Extract<ContentBlock, { type: "file" }>,
|
|
26
51
|
options?: TokenEstimatorOptions,
|
|
27
|
-
):
|
|
28
|
-
|
|
29
|
-
|
|
52
|
+
): number {
|
|
53
|
+
const providerName = options?.providerName;
|
|
54
|
+
|
|
55
|
+
// Anthropic sends PDFs as native document blocks and renders each page as an image
|
|
56
|
+
if (
|
|
57
|
+
providerName === "anthropic" &&
|
|
58
|
+
block.source.media_type === "application/pdf"
|
|
59
|
+
) {
|
|
60
|
+
return estimateAnthropicPdfTokens(block.source.data);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Gemini sends certain file types inline as base64
|
|
64
|
+
if (
|
|
65
|
+
providerName === "gemini" &&
|
|
66
|
+
GEMINI_INLINE_FILE_MIME_TYPES.has(block.source.media_type)
|
|
67
|
+
) {
|
|
68
|
+
return estimateTextTokens(block.source.data);
|
|
30
69
|
}
|
|
31
|
-
|
|
70
|
+
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function estimateAnthropicImageTokens(width: number, height: number): number {
|
|
75
|
+
// Scale down to fit within 1568x1568 bounding box, maintaining aspect ratio
|
|
76
|
+
const scale = Math.min(
|
|
77
|
+
1,
|
|
78
|
+
ANTHROPIC_IMAGE_MAX_DIMENSION / Math.max(width, height),
|
|
79
|
+
);
|
|
80
|
+
const scaledWidth = Math.round(width * scale);
|
|
81
|
+
const scaledHeight = Math.round(height * scale);
|
|
82
|
+
return Math.max(
|
|
83
|
+
IMAGE_BLOCK_TOKENS, // minimum 1024
|
|
84
|
+
Math.ceil(scaledWidth * scaledHeight * ANTHROPIC_IMAGE_TOKENS_PER_PIXEL),
|
|
85
|
+
);
|
|
32
86
|
}
|
|
33
87
|
|
|
34
|
-
function
|
|
88
|
+
function estimateImageTokens(
|
|
35
89
|
block: Extract<ContentBlock, { type: "image" }>,
|
|
90
|
+
options?: TokenEstimatorOptions,
|
|
36
91
|
): number {
|
|
37
|
-
|
|
38
|
-
|
|
92
|
+
if (options?.providerName === "anthropic") {
|
|
93
|
+
const dims = parseImageDimensions(
|
|
94
|
+
block.source.data,
|
|
95
|
+
block.source.media_type,
|
|
96
|
+
);
|
|
97
|
+
if (dims) {
|
|
98
|
+
return estimateAnthropicImageTokens(dims.width, dims.height);
|
|
99
|
+
}
|
|
100
|
+
// Fallback: if dimensions can't be parsed, use Anthropic's max
|
|
101
|
+
return ANTHROPIC_IMAGE_MAX_TOKENS;
|
|
102
|
+
}
|
|
103
|
+
// Non-Anthropic: keep existing base64-size heuristic
|
|
39
104
|
return estimateTextTokens(block.source.data);
|
|
40
105
|
}
|
|
41
106
|
|
|
@@ -69,16 +134,14 @@ export function estimateContentBlockTokens(
|
|
|
69
134
|
IMAGE_BLOCK_TOKENS,
|
|
70
135
|
IMAGE_BLOCK_OVERHEAD_TOKENS +
|
|
71
136
|
estimateTextTokens(block.source.media_type) +
|
|
72
|
-
|
|
137
|
+
estimateImageTokens(block, options),
|
|
73
138
|
);
|
|
74
139
|
case "file":
|
|
75
140
|
return (
|
|
76
141
|
FILE_BLOCK_OVERHEAD_TOKENS +
|
|
77
142
|
estimateTextTokens(block.source.filename) +
|
|
78
143
|
estimateTextTokens(block.source.media_type) +
|
|
79
|
-
(
|
|
80
|
-
? estimateTextTokens(block.source.data)
|
|
81
|
-
: 0) +
|
|
144
|
+
estimateFileDataTokens(block, options) +
|
|
82
145
|
estimateTextTokens(block.extracted_text ?? "")
|
|
83
146
|
);
|
|
84
147
|
case "thinking":
|
|
@@ -83,21 +83,44 @@ export interface ContextWindowCompactOptions {
|
|
|
83
83
|
|
|
84
84
|
export interface ContextWindowManagerOptions {
|
|
85
85
|
provider: Provider;
|
|
86
|
-
systemPrompt: string;
|
|
86
|
+
systemPrompt: string | (() => string);
|
|
87
87
|
config: ContextWindowConfig;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export class ContextWindowManager {
|
|
91
91
|
private readonly provider: Provider;
|
|
92
|
-
private readonly
|
|
92
|
+
private readonly _systemPrompt: string | (() => string);
|
|
93
93
|
private readonly config: ContextWindowConfig;
|
|
94
|
+
/**
|
|
95
|
+
* Cached resolved system prompt. Lazily populated on first access via the
|
|
96
|
+
* `systemPrompt` getter and cleared after each compaction pass so the next
|
|
97
|
+
* pass picks up any prompt changes.
|
|
98
|
+
*/
|
|
99
|
+
private _resolvedSystemPrompt: string | undefined;
|
|
94
100
|
|
|
95
101
|
constructor(options: ContextWindowManagerOptions) {
|
|
96
102
|
this.provider = options.provider;
|
|
97
|
-
this.
|
|
103
|
+
this._systemPrompt = options.systemPrompt;
|
|
98
104
|
this.config = options.config;
|
|
99
105
|
}
|
|
100
106
|
|
|
107
|
+
/** Lazily resolve and cache the system prompt for the duration of a compaction pass. */
|
|
108
|
+
private get systemPrompt(): string {
|
|
109
|
+
if (this._resolvedSystemPrompt !== undefined) {
|
|
110
|
+
return this._resolvedSystemPrompt;
|
|
111
|
+
}
|
|
112
|
+
const resolved =
|
|
113
|
+
typeof this._systemPrompt === "function"
|
|
114
|
+
? this._systemPrompt()
|
|
115
|
+
: this._systemPrompt;
|
|
116
|
+
this._resolvedSystemPrompt = resolved;
|
|
117
|
+
return resolved;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private clearSystemPromptCache(): void {
|
|
121
|
+
this._resolvedSystemPrompt = undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
101
124
|
/**
|
|
102
125
|
* Cheap pre-check: returns whether the estimated token count exceeds
|
|
103
126
|
* the compaction threshold, along with the estimated token count so
|
|
@@ -106,19 +129,35 @@ export class ContextWindowManager {
|
|
|
106
129
|
*/
|
|
107
130
|
shouldCompact(messages: Message[]): ShouldCompactResult {
|
|
108
131
|
if (!this.config.enabled) return { needed: false, estimatedTokens: 0 };
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
try {
|
|
133
|
+
const estimated = estimatePromptTokens(messages, this.systemPrompt, {
|
|
134
|
+
providerName: this.provider.name,
|
|
135
|
+
});
|
|
136
|
+
const threshold = Math.floor(
|
|
137
|
+
this.config.maxInputTokens * this.config.compactThreshold,
|
|
138
|
+
);
|
|
139
|
+
return { needed: estimated >= threshold, estimatedTokens: estimated };
|
|
140
|
+
} finally {
|
|
141
|
+
this.clearSystemPromptCache();
|
|
142
|
+
}
|
|
116
143
|
}
|
|
117
144
|
|
|
118
145
|
async maybeCompact(
|
|
119
146
|
messages: Message[],
|
|
120
147
|
signal?: AbortSignal,
|
|
121
148
|
options?: ContextWindowCompactOptions,
|
|
149
|
+
): Promise<ContextWindowResult> {
|
|
150
|
+
try {
|
|
151
|
+
return await this._maybeCompact(messages, signal, options);
|
|
152
|
+
} finally {
|
|
153
|
+
this.clearSystemPromptCache();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private async _maybeCompact(
|
|
158
|
+
messages: Message[],
|
|
159
|
+
signal?: AbortSignal,
|
|
160
|
+
options?: ContextWindowCompactOptions,
|
|
122
161
|
): Promise<ContextWindowResult> {
|
|
123
162
|
const previousEstimatedInputTokens =
|
|
124
163
|
options?.precomputedEstimate ??
|
|
@@ -17,9 +17,6 @@ import {
|
|
|
17
17
|
// Constants
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
|
|
20
|
-
/** Maximum number of attachments the assistant may emit per turn. */
|
|
21
|
-
export const MAX_ASSISTANT_ATTACHMENTS = 5;
|
|
22
|
-
|
|
23
20
|
/** Maximum size in bytes for a single assistant attachment (20 MB). */
|
|
24
21
|
export const MAX_ASSISTANT_ATTACHMENT_BYTES = 20 * 1024 * 1024;
|
|
25
22
|
|
|
@@ -122,10 +119,9 @@ export interface ValidatedDrafts {
|
|
|
122
119
|
}
|
|
123
120
|
|
|
124
121
|
/**
|
|
125
|
-
* Enforce per-
|
|
122
|
+
* Enforce per-attachment size cap.
|
|
126
123
|
*
|
|
127
124
|
* - Rejects individual drafts that exceed `MAX_ASSISTANT_ATTACHMENT_BYTES`.
|
|
128
|
-
* - Truncates the list at `MAX_ASSISTANT_ATTACHMENTS`.
|
|
129
125
|
*/
|
|
130
126
|
export function validateDrafts(
|
|
131
127
|
drafts: AssistantAttachmentDraft[],
|
|
@@ -144,14 +140,6 @@ export function validateDrafts(
|
|
|
144
140
|
continue;
|
|
145
141
|
}
|
|
146
142
|
|
|
147
|
-
if (accepted.length >= MAX_ASSISTANT_ATTACHMENTS) {
|
|
148
|
-
warnings.push(
|
|
149
|
-
`Skipped attachment "${draft.filename}": ` +
|
|
150
|
-
`exceeded maximum of ${MAX_ASSISTANT_ATTACHMENTS} attachments per turn.`,
|
|
151
|
-
);
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
143
|
accepted.push(draft);
|
|
156
144
|
}
|
|
157
145
|
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
} from "../../calls/twilio-rest.js";
|
|
6
6
|
import {
|
|
7
7
|
getGatewayInternalBaseUrl,
|
|
8
|
-
getIngressPublicBaseUrl,
|
|
9
8
|
setIngressPublicBaseUrl,
|
|
10
9
|
} from "../../config/env.js";
|
|
11
10
|
import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
|
|
@@ -26,20 +25,6 @@ import {
|
|
|
26
25
|
log,
|
|
27
26
|
} from "./shared.js";
|
|
28
27
|
|
|
29
|
-
// Lazily capture the env-provided INGRESS_PUBLIC_BASE_URL on first access
|
|
30
|
-
// rather than at module load time. The daemon loads ~/.vellum/.env inside
|
|
31
|
-
// runDaemon() (see lifecycle.ts), which runs AFTER static ES module imports
|
|
32
|
-
// resolve. A module-level snapshot would miss dotenv-provided values.
|
|
33
|
-
let _originalIngressEnvCaptured = false;
|
|
34
|
-
let _originalIngressEnv: string | undefined;
|
|
35
|
-
function getOriginalIngressEnv(): string | undefined {
|
|
36
|
-
if (!_originalIngressEnvCaptured) {
|
|
37
|
-
_originalIngressEnv = getIngressPublicBaseUrl();
|
|
38
|
-
_originalIngressEnvCaptured = true;
|
|
39
|
-
}
|
|
40
|
-
return _originalIngressEnv;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
28
|
export function computeGatewayTarget(): string {
|
|
44
29
|
return getGatewayInternalBaseUrl();
|
|
45
30
|
}
|
|
@@ -108,13 +93,11 @@ export async function handleIngressConfig(
|
|
|
108
93
|
});
|
|
109
94
|
} else if (msg.action === "set") {
|
|
110
95
|
const value = (msg.publicBaseUrl ?? "").trim().replace(/\/+$/, "");
|
|
111
|
-
// Ensure we capture the original env value before any mutation below
|
|
112
|
-
getOriginalIngressEnv();
|
|
113
96
|
const raw = loadRawConfig();
|
|
114
97
|
|
|
115
98
|
// Update ingress.publicBaseUrl — this is the single source of truth for
|
|
116
|
-
// the canonical public ingress URL. The gateway
|
|
117
|
-
// the
|
|
99
|
+
// the canonical public ingress URL. The gateway reads this value from
|
|
100
|
+
// the workspace config file via ConfigFileCache.
|
|
118
101
|
// The gateway also validates Twilio signatures against forwarded public
|
|
119
102
|
// URL headers, so local tunnel updates generally apply without restarts.
|
|
120
103
|
const ingress = (raw?.ingress ?? {}) as Record<string, unknown>;
|
|
@@ -139,24 +122,17 @@ export async function handleIngressConfig(
|
|
|
139
122
|
CONFIG_RELOAD_DEBOUNCE_MS,
|
|
140
123
|
);
|
|
141
124
|
|
|
142
|
-
// Propagate to the
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
// gateway is restarted (e.g. by the self-upgrade skill or a manual
|
|
147
|
-
// `pkill -f gateway`).
|
|
148
|
-
// Only export the URL when ingress is enabled; clearing it when
|
|
125
|
+
// Propagate to module-level state so the assistant's in-process URL
|
|
126
|
+
// resolution stays in sync. The gateway reads from the workspace config
|
|
127
|
+
// file directly via ConfigFileCache, so no env var propagation is needed.
|
|
128
|
+
// Only set the URL when ingress is enabled; clearing it when
|
|
149
129
|
// disabled ensures the gateway stops accepting inbound webhooks.
|
|
150
130
|
const isEnabled = (ingress.enabled as boolean | undefined) ?? false;
|
|
151
131
|
if (value && isEnabled) {
|
|
152
132
|
setIngressPublicBaseUrl(value);
|
|
153
|
-
} else if (isEnabled && getOriginalIngressEnv() !== undefined) {
|
|
154
|
-
// Ingress is enabled but the user cleared the URL — fall back to the
|
|
155
|
-
// env var that was present when the process started.
|
|
156
|
-
setIngressPublicBaseUrl(getOriginalIngressEnv()!);
|
|
157
133
|
} else {
|
|
158
|
-
// Ingress is disabled or no URL is configured
|
|
159
|
-
//
|
|
134
|
+
// Ingress is disabled or no URL is configured — clear the module-level
|
|
135
|
+
// URL so the gateway stops accepting webhooks.
|
|
160
136
|
setIngressPublicBaseUrl(undefined);
|
|
161
137
|
}
|
|
162
138
|
|
|
@@ -250,4 +226,3 @@ export async function handleIngressConfig(
|
|
|
250
226
|
});
|
|
251
227
|
}
|
|
252
228
|
}
|
|
253
|
-
|
|
@@ -5,6 +5,11 @@ import {
|
|
|
5
5
|
saveRawConfig,
|
|
6
6
|
setNestedValue,
|
|
7
7
|
} from "../../config/loader.js";
|
|
8
|
+
import {
|
|
9
|
+
ensureManualTokenConnection,
|
|
10
|
+
removeManualTokenConnection,
|
|
11
|
+
} from "../../oauth/manual-token-connection.js";
|
|
12
|
+
import { getConnectionByProvider } from "../../oauth/oauth-store.js";
|
|
8
13
|
import { credentialKey } from "../../security/credential-key.js";
|
|
9
14
|
import {
|
|
10
15
|
deleteSecureKeyAsync,
|
|
@@ -41,12 +46,15 @@ export function getSlackChannelConfig(): SlackChannelConfigResult {
|
|
|
41
46
|
const hasAppToken = !!getSecureKey(
|
|
42
47
|
credentialKey("slack_channel", "app_token"),
|
|
43
48
|
);
|
|
49
|
+
const conn = getConnectionByProvider("slack_channel");
|
|
50
|
+
const connected =
|
|
51
|
+
!!(conn && conn.status === "active") && hasBotToken && hasAppToken;
|
|
44
52
|
const { teamId, teamName, botUserId, botUsername } = getConfig().slack;
|
|
45
53
|
return {
|
|
46
54
|
success: true,
|
|
47
55
|
hasBotToken,
|
|
48
56
|
hasAppToken,
|
|
49
|
-
connected
|
|
57
|
+
connected,
|
|
50
58
|
...(teamId ? { teamId } : {}),
|
|
51
59
|
...(teamName ? { teamName } : {}),
|
|
52
60
|
...(botUserId ? { botUserId } : {}),
|
|
@@ -83,17 +91,13 @@ export async function setSlackChannelConfig(
|
|
|
83
91
|
user?: string;
|
|
84
92
|
};
|
|
85
93
|
if (!data.ok) {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
);
|
|
89
|
-
const storedAppToken = !!getSecureKey(
|
|
90
|
-
credentialKey("slack_channel", "app_token"),
|
|
91
|
-
);
|
|
94
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
95
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
92
96
|
return {
|
|
93
97
|
success: false,
|
|
94
|
-
hasBotToken:
|
|
95
|
-
hasAppToken:
|
|
96
|
-
connected:
|
|
98
|
+
hasBotToken: errConnected,
|
|
99
|
+
hasAppToken: errConnected,
|
|
100
|
+
connected: errConnected,
|
|
97
101
|
error: `Slack API validation failed: ${
|
|
98
102
|
data.error ?? "unknown error"
|
|
99
103
|
}`,
|
|
@@ -107,17 +111,13 @@ export async function setSlackChannelConfig(
|
|
|
107
111
|
};
|
|
108
112
|
} catch (err) {
|
|
109
113
|
const message = err instanceof Error ? err.message : String(err);
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
);
|
|
113
|
-
const storedAppToken = !!getSecureKey(
|
|
114
|
-
credentialKey("slack_channel", "app_token"),
|
|
115
|
-
);
|
|
114
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
115
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
116
116
|
return {
|
|
117
117
|
success: false,
|
|
118
|
-
hasBotToken:
|
|
119
|
-
hasAppToken:
|
|
120
|
-
connected:
|
|
118
|
+
hasBotToken: errConnected,
|
|
119
|
+
hasAppToken: errConnected,
|
|
120
|
+
connected: errConnected,
|
|
121
121
|
error: `Failed to validate bot token: ${message}`,
|
|
122
122
|
};
|
|
123
123
|
}
|
|
@@ -127,17 +127,13 @@ export async function setSlackChannelConfig(
|
|
|
127
127
|
botToken,
|
|
128
128
|
);
|
|
129
129
|
if (!stored) {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
);
|
|
133
|
-
const storedAppToken = !!getSecureKey(
|
|
134
|
-
credentialKey("slack_channel", "app_token"),
|
|
135
|
-
);
|
|
130
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
131
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
136
132
|
return {
|
|
137
133
|
success: false,
|
|
138
|
-
hasBotToken:
|
|
139
|
-
hasAppToken:
|
|
140
|
-
connected:
|
|
134
|
+
hasBotToken: errConnected,
|
|
135
|
+
hasAppToken: errConnected,
|
|
136
|
+
connected: errConnected,
|
|
141
137
|
error: "Failed to store bot token in secure storage",
|
|
142
138
|
};
|
|
143
139
|
}
|
|
@@ -165,17 +161,13 @@ export async function setSlackChannelConfig(
|
|
|
165
161
|
// Validate and store app token
|
|
166
162
|
if (appToken) {
|
|
167
163
|
if (!appToken.startsWith("xapp-")) {
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
);
|
|
171
|
-
const storedAppToken = !!getSecureKey(
|
|
172
|
-
credentialKey("slack_channel", "app_token"),
|
|
173
|
-
);
|
|
164
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
165
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
174
166
|
return {
|
|
175
167
|
success: false,
|
|
176
|
-
hasBotToken:
|
|
177
|
-
hasAppToken:
|
|
178
|
-
connected:
|
|
168
|
+
hasBotToken: errConnected,
|
|
169
|
+
hasAppToken: errConnected,
|
|
170
|
+
connected: errConnected,
|
|
179
171
|
error: 'Invalid app token: must start with "xapp-"',
|
|
180
172
|
};
|
|
181
173
|
}
|
|
@@ -185,17 +177,13 @@ export async function setSlackChannelConfig(
|
|
|
185
177
|
appToken,
|
|
186
178
|
);
|
|
187
179
|
if (!stored) {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
);
|
|
191
|
-
const storedAppToken = !!getSecureKey(
|
|
192
|
-
credentialKey("slack_channel", "app_token"),
|
|
193
|
-
);
|
|
180
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
181
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
194
182
|
return {
|
|
195
183
|
success: false,
|
|
196
|
-
hasBotToken:
|
|
197
|
-
hasAppToken:
|
|
198
|
-
connected:
|
|
184
|
+
hasBotToken: errConnected,
|
|
185
|
+
hasAppToken: errConnected,
|
|
186
|
+
connected: errConnected,
|
|
199
187
|
error: "Failed to store app token in secure storage",
|
|
200
188
|
};
|
|
201
189
|
}
|
|
@@ -218,6 +206,17 @@ export async function setSlackChannelConfig(
|
|
|
218
206
|
"App token stored but bot token is missing — connection incomplete.";
|
|
219
207
|
}
|
|
220
208
|
|
|
209
|
+
// Sync oauth_connection record so getConnectionByProvider("slack_channel")
|
|
210
|
+
// reflects the current credential state.
|
|
211
|
+
if (hasBotToken && hasAppToken) {
|
|
212
|
+
const accountInfo = metadata.teamName
|
|
213
|
+
? `${metadata.teamName}${metadata.botUsername ? ` (@${metadata.botUsername})` : ""}`
|
|
214
|
+
: undefined;
|
|
215
|
+
await ensureManualTokenConnection("slack_channel", accountInfo);
|
|
216
|
+
} else {
|
|
217
|
+
removeManualTokenConnection("slack_channel");
|
|
218
|
+
}
|
|
219
|
+
|
|
221
220
|
return {
|
|
222
221
|
success: true,
|
|
223
222
|
hasBotToken,
|
|
@@ -237,6 +236,7 @@ export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResul
|
|
|
237
236
|
);
|
|
238
237
|
|
|
239
238
|
if (r1 === "error" || r2 === "error") {
|
|
239
|
+
// Check each key individually so partial deletions report accurate status.
|
|
240
240
|
const hasBotToken = !!getSecureKey(
|
|
241
241
|
credentialKey("slack_channel", "bot_token"),
|
|
242
242
|
);
|
|
@@ -255,6 +255,9 @@ export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResul
|
|
|
255
255
|
deleteCredentialMetadata("slack_channel", "bot_token");
|
|
256
256
|
deleteCredentialMetadata("slack_channel", "app_token");
|
|
257
257
|
|
|
258
|
+
// Remove the oauth_connection row so getConnectionByProvider returns undefined.
|
|
259
|
+
removeManualTokenConnection("slack_channel");
|
|
260
|
+
|
|
258
261
|
const raw = loadRawConfig();
|
|
259
262
|
setNestedValue(raw, "slack.teamId", "");
|
|
260
263
|
setNestedValue(raw, "slack.teamName", "");
|
|
@@ -8,6 +8,11 @@ import {
|
|
|
8
8
|
registerCallbackRoute,
|
|
9
9
|
shouldUsePlatformCallbacks,
|
|
10
10
|
} from "../../inbound/platform-callback-registration.js";
|
|
11
|
+
import {
|
|
12
|
+
ensureManualTokenConnection,
|
|
13
|
+
removeManualTokenConnection,
|
|
14
|
+
} from "../../oauth/manual-token-connection.js";
|
|
15
|
+
import { getConnectionByProvider } from "../../oauth/oauth-store.js";
|
|
11
16
|
import { credentialKey } from "../../security/credential-key.js";
|
|
12
17
|
import {
|
|
13
18
|
deleteSecureKeyAsync,
|
|
@@ -65,12 +70,14 @@ export function getTelegramConfig(): TelegramConfigResult {
|
|
|
65
70
|
const hasWebhookSecret = !!getSecureKey(
|
|
66
71
|
credentialKey("telegram", "webhook_secret"),
|
|
67
72
|
);
|
|
73
|
+
const conn = getConnectionByProvider("telegram");
|
|
74
|
+
const connected = !!(conn && conn.status === "active");
|
|
68
75
|
const botUsername = getTelegramBotUsername();
|
|
69
76
|
return {
|
|
70
77
|
success: true,
|
|
71
78
|
hasBotToken,
|
|
72
79
|
botUsername,
|
|
73
|
-
connected: hasBotToken && hasWebhookSecret,
|
|
80
|
+
connected: connected && hasBotToken && hasWebhookSecret,
|
|
74
81
|
hasWebhookSecret,
|
|
75
82
|
};
|
|
76
83
|
}
|
|
@@ -200,6 +207,13 @@ export async function setTelegramConfig(
|
|
|
200
207
|
upsertCredentialMetadata("telegram", "webhook_secret", {});
|
|
201
208
|
}
|
|
202
209
|
|
|
210
|
+
// Sync oauth_connection record so getConnectionByProvider("telegram")
|
|
211
|
+
// reflects the current credential state.
|
|
212
|
+
await ensureManualTokenConnection(
|
|
213
|
+
"telegram",
|
|
214
|
+
botUsername ? `@${botUsername}` : undefined,
|
|
215
|
+
);
|
|
216
|
+
|
|
203
217
|
const result: TelegramConfigResult = {
|
|
204
218
|
success: true,
|
|
205
219
|
hasBotToken: true,
|
|
@@ -245,6 +259,7 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
|
|
|
245
259
|
);
|
|
246
260
|
|
|
247
261
|
if (r1 === "error" || r2 === "error") {
|
|
262
|
+
// Check each key individually so partial deletions report accurate status.
|
|
248
263
|
const hasBotToken = !!getSecureKey(credentialKey("telegram", "bot_token"));
|
|
249
264
|
const hasWebhookSecret = !!getSecureKey(
|
|
250
265
|
credentialKey("telegram", "webhook_secret"),
|
|
@@ -261,6 +276,9 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
|
|
|
261
276
|
deleteCredentialMetadata("telegram", "bot_token");
|
|
262
277
|
deleteCredentialMetadata("telegram", "webhook_secret");
|
|
263
278
|
|
|
279
|
+
// Remove the oauth_connection row so getConnectionByProvider returns undefined.
|
|
280
|
+
removeManualTokenConnection("telegram");
|
|
281
|
+
|
|
264
282
|
// Clear bot username from config so getTelegramBotUsername() doesn't
|
|
265
283
|
// return a stale value after disconnect.
|
|
266
284
|
const raw = loadRawConfig();
|
|
@@ -306,38 +324,36 @@ export async function setTelegramCommands(
|
|
|
306
324
|
);
|
|
307
325
|
if (!res.ok) {
|
|
308
326
|
const body = await res.text();
|
|
327
|
+
const cmdConn = getConnectionByProvider("telegram");
|
|
328
|
+
const cmdConnected = !!(cmdConn && cmdConn.status === "active");
|
|
309
329
|
return {
|
|
310
330
|
success: false,
|
|
311
331
|
hasBotToken: true,
|
|
312
|
-
connected:
|
|
313
|
-
hasWebhookSecret:
|
|
314
|
-
credentialKey("telegram", "webhook_secret"),
|
|
315
|
-
),
|
|
332
|
+
connected: cmdConnected,
|
|
333
|
+
hasWebhookSecret: cmdConnected,
|
|
316
334
|
error: `Failed to set bot commands: ${body}`,
|
|
317
335
|
};
|
|
318
336
|
}
|
|
319
337
|
} catch (err) {
|
|
320
338
|
const message = summarizeTelegramError(err);
|
|
339
|
+
const cmdConn = getConnectionByProvider("telegram");
|
|
340
|
+
const cmdConnected = !!(cmdConn && cmdConn.status === "active");
|
|
321
341
|
return {
|
|
322
342
|
success: false,
|
|
323
343
|
hasBotToken: true,
|
|
324
|
-
connected:
|
|
325
|
-
hasWebhookSecret:
|
|
326
|
-
credentialKey("telegram", "webhook_secret"),
|
|
327
|
-
),
|
|
344
|
+
connected: cmdConnected,
|
|
345
|
+
hasWebhookSecret: cmdConnected,
|
|
328
346
|
error: `Failed to set bot commands: ${message}`,
|
|
329
347
|
};
|
|
330
348
|
}
|
|
331
349
|
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
credentialKey("telegram", "webhook_secret"),
|
|
335
|
-
);
|
|
350
|
+
const cmdConn = getConnectionByProvider("telegram");
|
|
351
|
+
const cmdConnected = !!(cmdConn && cmdConn.status === "active");
|
|
336
352
|
return {
|
|
337
353
|
success: true,
|
|
338
|
-
hasBotToken,
|
|
339
|
-
connected:
|
|
340
|
-
hasWebhookSecret,
|
|
354
|
+
hasBotToken: true,
|
|
355
|
+
connected: cmdConnected,
|
|
356
|
+
hasWebhookSecret: cmdConnected,
|
|
341
357
|
commandsRegistered: resolvedCommands.map((c) => c.command),
|
|
342
358
|
};
|
|
343
359
|
}
|