@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
package/src/daemon/session.ts
CHANGED
|
@@ -35,18 +35,23 @@ import {
|
|
|
35
35
|
} from "../events/tool-profiling-listener.js";
|
|
36
36
|
import { registerToolTraceListener } from "../events/tool-trace-listener.js";
|
|
37
37
|
import { getHookManager } from "../hooks/manager.js";
|
|
38
|
+
import { resolveCanonicalGuardianRequest } from "../memory/canonical-guardian-store.js";
|
|
38
39
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
39
40
|
import { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
41
|
+
import { patternMatchesCandidate } from "../permissions/trust-store.js";
|
|
40
42
|
import type { UserDecision } from "../permissions/types.js";
|
|
41
43
|
import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
42
44
|
import type { Message } from "../providers/types.js";
|
|
43
45
|
import type { Provider } from "../providers/types.js";
|
|
44
46
|
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
45
47
|
import type { AuthContext } from "../runtime/auth/types.js";
|
|
48
|
+
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
46
49
|
import * as approvalOverrides from "../runtime/session-approval-overrides.js";
|
|
47
50
|
import { ToolExecutor } from "../tools/executor.js";
|
|
48
51
|
import type { AssistantAttachmentDraft } from "./assistant-attachments.js";
|
|
49
52
|
import { HostBashProxy } from "./host-bash-proxy.js";
|
|
53
|
+
import type { CuObservationResult } from "./host-cu-proxy.js";
|
|
54
|
+
import { HostCuProxy } from "./host-cu-proxy.js";
|
|
50
55
|
import { HostFileProxy } from "./host-file-proxy.js";
|
|
51
56
|
import type {
|
|
52
57
|
ServerMessage,
|
|
@@ -161,6 +166,7 @@ export class Session {
|
|
|
161
166
|
/** @internal */ taskRunId?: string;
|
|
162
167
|
/** @internal */ callSessionId?: string;
|
|
163
168
|
/** @internal */ hostBashProxy?: HostBashProxy;
|
|
169
|
+
/** @internal */ hostCuProxy?: HostCuProxy;
|
|
164
170
|
/** @internal */ hostFileProxy?: HostFileProxy;
|
|
165
171
|
/** @internal */ readonly queue = new MessageQueue();
|
|
166
172
|
/** @internal */ currentActiveSurfaceId?: string;
|
|
@@ -199,10 +205,6 @@ export class Session {
|
|
|
199
205
|
actions?: Array<{ id: string; label: string; style?: string }>;
|
|
200
206
|
display?: string;
|
|
201
207
|
}> = [];
|
|
202
|
-
/** @internal */ onEscalateToComputerUse?: (
|
|
203
|
-
task: string,
|
|
204
|
-
sourceSessionId: string,
|
|
205
|
-
) => boolean;
|
|
206
208
|
/** @internal */ workspaceTopLevelContext: string | null = null;
|
|
207
209
|
/** @internal */ workspaceTopLevelDirty = true;
|
|
208
210
|
public readonly traceEmitter: TraceEmitter;
|
|
@@ -346,7 +348,7 @@ export class Session {
|
|
|
346
348
|
);
|
|
347
349
|
this.contextWindowManager = new ContextWindowManager({
|
|
348
350
|
provider,
|
|
349
|
-
systemPrompt,
|
|
351
|
+
systemPrompt: () => resolveSystemPromptCallback([]).systemPrompt,
|
|
350
352
|
config: config.contextWindow,
|
|
351
353
|
});
|
|
352
354
|
|
|
@@ -388,6 +390,7 @@ export class Session {
|
|
|
388
390
|
this.traceEmitter.updateSender(sendToClient);
|
|
389
391
|
if (!opts?.skipProxySenderUpdate) {
|
|
390
392
|
this.hostBashProxy?.updateSender(sendToClient, !hasNoClient);
|
|
393
|
+
this.hostCuProxy?.updateSender(sendToClient, !hasNoClient);
|
|
391
394
|
this.hostFileProxy?.updateSender(sendToClient, !hasNoClient);
|
|
392
395
|
}
|
|
393
396
|
}
|
|
@@ -400,6 +403,7 @@ export class Session {
|
|
|
400
403
|
/** Mark host proxies as unavailable so tool execution uses local fallback. */
|
|
401
404
|
clearProxyAvailability(): void {
|
|
402
405
|
this.hostBashProxy?.updateSender(this.sendToClient, false);
|
|
406
|
+
this.hostCuProxy?.updateSender(this.sendToClient, false);
|
|
403
407
|
this.hostFileProxy?.updateSender(this.sendToClient, false);
|
|
404
408
|
}
|
|
405
409
|
|
|
@@ -407,6 +411,7 @@ export class Session {
|
|
|
407
411
|
restoreProxyAvailability(): void {
|
|
408
412
|
if (!this.hasNoClient) {
|
|
409
413
|
this.hostBashProxy?.updateSender(this.sendToClient, true);
|
|
414
|
+
this.hostCuProxy?.updateSender(this.sendToClient, true);
|
|
410
415
|
this.hostFileProxy?.updateSender(this.sendToClient, true);
|
|
411
416
|
}
|
|
412
417
|
}
|
|
@@ -415,16 +420,6 @@ export class Session {
|
|
|
415
420
|
this.sandboxOverride = enabled;
|
|
416
421
|
}
|
|
417
422
|
|
|
418
|
-
setEscalationHandler(
|
|
419
|
-
handler: (task: string, sourceSessionId: string) => boolean,
|
|
420
|
-
): void {
|
|
421
|
-
this.onEscalateToComputerUse = handler;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
hasEscalationHandler(): boolean {
|
|
425
|
-
return this.onEscalateToComputerUse !== undefined;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
423
|
isProcessing(): boolean {
|
|
429
424
|
return this.processing;
|
|
430
425
|
}
|
|
@@ -447,6 +442,7 @@ export class Session {
|
|
|
447
442
|
dispose(): void {
|
|
448
443
|
approvalOverrides.clearMode(this.conversationId);
|
|
449
444
|
this.hostBashProxy?.dispose();
|
|
445
|
+
this.hostCuProxy?.dispose();
|
|
450
446
|
this.hostFileProxy?.dispose();
|
|
451
447
|
disposeSession(this);
|
|
452
448
|
}
|
|
@@ -589,6 +585,122 @@ export class Session {
|
|
|
589
585
|
undefined,
|
|
590
586
|
"Resuming after approval",
|
|
591
587
|
);
|
|
588
|
+
|
|
589
|
+
// Cascade to other pending confirmations that match this decision
|
|
590
|
+
this.cascadePendingApprovals(requestId, decision, selectedPattern);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* After resolving one confirmation, auto-resolve other pending
|
|
595
|
+
* confirmations in the same conversation that match the decision.
|
|
596
|
+
*
|
|
597
|
+
* - allow_10m / allow_thread → approve ALL pending in conversation
|
|
598
|
+
* - always_allow / always_allow_high_risk → approve pattern-matching pending
|
|
599
|
+
* - always_deny → deny pattern-matching pending
|
|
600
|
+
* - allow / deny (one-time) → no cascading
|
|
601
|
+
*/
|
|
602
|
+
private cascadePendingApprovals(
|
|
603
|
+
primaryRequestId: string,
|
|
604
|
+
decision: UserDecision,
|
|
605
|
+
selectedPattern?: string,
|
|
606
|
+
): void {
|
|
607
|
+
// Single-action decisions don't cascade
|
|
608
|
+
if (decision === "allow" || decision === "deny") return;
|
|
609
|
+
|
|
610
|
+
const pendingRequestIds = this.prompter.getPendingRequestIds();
|
|
611
|
+
if (pendingRequestIds.length === 0) return;
|
|
612
|
+
|
|
613
|
+
for (const candidateId of pendingRequestIds) {
|
|
614
|
+
if (candidateId === primaryRequestId) continue;
|
|
615
|
+
|
|
616
|
+
const interaction = pendingInteractions.get(candidateId);
|
|
617
|
+
if (!interaction) continue;
|
|
618
|
+
if (interaction.conversationId !== this.conversationId) continue;
|
|
619
|
+
if (interaction.kind !== "confirmation") continue;
|
|
620
|
+
|
|
621
|
+
const cascadeResult = this.shouldCascade(
|
|
622
|
+
decision,
|
|
623
|
+
selectedPattern,
|
|
624
|
+
interaction.confirmationDetails,
|
|
625
|
+
);
|
|
626
|
+
if (!cascadeResult) continue;
|
|
627
|
+
|
|
628
|
+
// Consume from pending-interactions tracker
|
|
629
|
+
pendingInteractions.resolve(candidateId);
|
|
630
|
+
|
|
631
|
+
// Resolve via handleConfirmationResponse which emits events.
|
|
632
|
+
// Use simple "allow"/"deny" so the permission-checker won't save
|
|
633
|
+
// duplicate rules or re-activate temporary modes. Recursion
|
|
634
|
+
// terminates because allow/deny exit cascadePendingApprovals early.
|
|
635
|
+
this.handleConfirmationResponse(
|
|
636
|
+
candidateId,
|
|
637
|
+
cascadeResult.allow ? "allow" : "deny",
|
|
638
|
+
undefined,
|
|
639
|
+
undefined,
|
|
640
|
+
undefined,
|
|
641
|
+
{
|
|
642
|
+
source: "system",
|
|
643
|
+
causedByRequestId: primaryRequestId,
|
|
644
|
+
},
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
// Sync the canonical guardian request status for the cascaded request.
|
|
648
|
+
// Best-effort: canonical request tracking should not break the cascade flow.
|
|
649
|
+
try {
|
|
650
|
+
const targetStatus = cascadeResult.allow ? "approved" : "denied";
|
|
651
|
+
resolveCanonicalGuardianRequest(candidateId, "pending", {
|
|
652
|
+
status: targetStatus,
|
|
653
|
+
});
|
|
654
|
+
} catch {
|
|
655
|
+
// Ignore — canonical request tracking is best-effort
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Determine whether a pending confirmation should be auto-resolved
|
|
662
|
+
* based on the cascading decision and pattern.
|
|
663
|
+
*/
|
|
664
|
+
private shouldCascade(
|
|
665
|
+
decision: UserDecision,
|
|
666
|
+
selectedPattern: string | undefined,
|
|
667
|
+
details?: import("../runtime/pending-interactions.js").ConfirmationDetails,
|
|
668
|
+
): { allow: boolean } | null {
|
|
669
|
+
// Temporary overrides apply to the entire conversation
|
|
670
|
+
if (decision === "allow_10m" || decision === "allow_thread") {
|
|
671
|
+
return { allow: true };
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Persistent allow: cascade if the pattern matches any allowlist candidate.
|
|
675
|
+
// "always_allow" must NOT cascade to high-risk pending confirmations —
|
|
676
|
+
// only "always_allow_high_risk" has consent for those.
|
|
677
|
+
if (
|
|
678
|
+
(decision === "always_allow" || decision === "always_allow_high_risk") &&
|
|
679
|
+
selectedPattern &&
|
|
680
|
+
details
|
|
681
|
+
) {
|
|
682
|
+
if (decision === "always_allow" && details.riskLevel === "high") {
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
for (const option of details.allowlistOptions) {
|
|
686
|
+
if (patternMatchesCandidate(selectedPattern, option.pattern)) {
|
|
687
|
+
return { allow: true };
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Persistent deny: cascade denial if the pattern matches
|
|
694
|
+
if (decision === "always_deny" && selectedPattern && details) {
|
|
695
|
+
for (const option of details.allowlistOptions) {
|
|
696
|
+
if (patternMatchesCandidate(selectedPattern, option.pattern)) {
|
|
697
|
+
return { allow: false };
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return null;
|
|
592
704
|
}
|
|
593
705
|
|
|
594
706
|
handleSecretResponse(
|
|
@@ -632,6 +744,17 @@ export class Session {
|
|
|
632
744
|
this.hostFileProxy = proxy;
|
|
633
745
|
}
|
|
634
746
|
|
|
747
|
+
resolveHostCu(requestId: string, observation: CuObservationResult): void {
|
|
748
|
+
this.hostCuProxy?.resolve(requestId, observation);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
setHostCuProxy(proxy: HostCuProxy | undefined): void {
|
|
752
|
+
if (this.hostCuProxy && this.hostCuProxy !== proxy) {
|
|
753
|
+
this.hostCuProxy.dispose();
|
|
754
|
+
}
|
|
755
|
+
this.hostCuProxy = proxy;
|
|
756
|
+
}
|
|
757
|
+
|
|
635
758
|
// ── Server-authoritative state signals ─────────────────────────────
|
|
636
759
|
|
|
637
760
|
emitConfirmationStateChanged(
|
|
@@ -220,9 +220,7 @@ registerHook(
|
|
|
220
220
|
const SETTING_TO_KEY: Record<string, string> = {
|
|
221
221
|
activation_key: "pttActivationKey",
|
|
222
222
|
tts_voice_id: "ttsVoiceId",
|
|
223
|
-
|
|
224
|
-
wake_word_keyword: "wakeWordKeyword",
|
|
225
|
-
wake_word_timeout: "wakeWordTimeoutSeconds",
|
|
223
|
+
conversation_timeout: "voiceConversationTimeoutSeconds",
|
|
226
224
|
};
|
|
227
225
|
const key = SETTING_TO_KEY[setting];
|
|
228
226
|
if (!key) return;
|
|
@@ -231,12 +229,8 @@ registerHook(
|
|
|
231
229
|
// the validation logic in the tool's execute method.
|
|
232
230
|
const raw = input.value;
|
|
233
231
|
let coerced: string | boolean | number = raw as string;
|
|
234
|
-
if (setting === "
|
|
235
|
-
coerced = raw === true || raw === "true";
|
|
236
|
-
} else if (setting === "wake_word_timeout") {
|
|
232
|
+
if (setting === "conversation_timeout") {
|
|
237
233
|
coerced = typeof raw === "number" ? raw : Number(raw);
|
|
238
|
-
} else if (setting === "wake_word_keyword" && typeof raw === "string") {
|
|
239
|
-
coerced = raw.trim();
|
|
240
234
|
} else if (setting === "tts_voice_id" && typeof raw === "string") {
|
|
241
235
|
coerced = raw.trim();
|
|
242
236
|
}
|
|
@@ -79,8 +79,8 @@ export async function handleWatchObservation(
|
|
|
79
79
|
"Observation added to session",
|
|
80
80
|
);
|
|
81
81
|
|
|
82
|
-
// 4. Every 3 observations: call the LLM for live commentary
|
|
83
|
-
if (
|
|
82
|
+
// 4. Every 3 observations: call the LLM for live commentary
|
|
83
|
+
if (session.observations.length % 3 === 0) {
|
|
84
84
|
log.debug(
|
|
85
85
|
{ watchId: msg.watchId, observationCount: session.observations.length },
|
|
86
86
|
"Triggering commentary generation (every 3rd observation)",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getLogger,
|
|
1
|
+
import { getLogger, truncateForLog } from "../util/logger.js";
|
|
2
2
|
import type { EventBus, Subscription } from "./bus.js";
|
|
3
3
|
import type { AssistantDomainEvents } from "./domain-events.js";
|
|
4
4
|
|
|
@@ -23,7 +23,7 @@ export function registerToolMetricsLoggingListener(
|
|
|
23
23
|
options?: MetricsListenerOptions,
|
|
24
24
|
): Subscription {
|
|
25
25
|
const logger = options?.logger ?? defaultLogger;
|
|
26
|
-
const debugEnabled = options?.debugEnabled ??
|
|
26
|
+
const debugEnabled = options?.debugEnabled ?? (() => false);
|
|
27
27
|
const truncate = options?.truncate ?? truncateForLog;
|
|
28
28
|
|
|
29
29
|
return eventBus.onAny((event) => {
|
package/src/hooks/manager.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type FSWatcher, watch } from "node:fs";
|
|
|
2
2
|
|
|
3
3
|
import { Debouncer } from "../util/debounce.js";
|
|
4
4
|
import { pathExists } from "../util/fs.js";
|
|
5
|
-
import { getLogger
|
|
5
|
+
import { getLogger } from "../util/logger.js";
|
|
6
6
|
import { getHooksDir } from "../util/platform.js";
|
|
7
7
|
import { discoverHooks } from "./discovery.js";
|
|
8
8
|
import { runHookScript } from "./runner.js";
|
|
@@ -73,9 +73,6 @@ export class HookManager {
|
|
|
73
73
|
"Hook exited with non-zero code",
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
|
-
if (result.stderr && isDebug()) {
|
|
77
|
-
process.stderr.write(result.stderr);
|
|
78
|
-
}
|
|
79
76
|
} catch (err) {
|
|
80
77
|
log.warn({ err, hook: hook.name, event }, "Hook execution failed");
|
|
81
78
|
}
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
* 1. **User Settings** (`config.ingress.publicBaseUrl`) — set via
|
|
9
9
|
* the in-chat config flow, the Settings UI, or `config set ingress.publicBaseUrl`. This is the
|
|
10
10
|
* primary source of truth. When the assistant spawns or restarts
|
|
11
|
-
* the gateway,
|
|
12
|
-
*
|
|
11
|
+
* the gateway, the workspace config file is read so both processes
|
|
12
|
+
* agree on the same URL.
|
|
13
13
|
*
|
|
14
|
-
* 2. **
|
|
15
|
-
* fallback for operational use (e.g.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* 2. **Module-level state** (`getIngressPublicBaseUrl()`) — serves as a
|
|
15
|
+
* fallback for operational use (e.g. runtime tunnel updates). When
|
|
16
|
+
* tunnels start or stop, `setIngressPublicBaseUrl()` updates this
|
|
17
|
+
* value in-process.
|
|
18
18
|
*
|
|
19
19
|
* This chain ensures that:
|
|
20
20
|
* - The assistant's outbound callback URLs (Twilio webhooks, OAuth
|
|
@@ -70,7 +70,7 @@ export function getPublicBaseUrl(config: IngressConfig): string {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
throw new Error(
|
|
73
|
-
"No public base URL configured. Set ingress.publicBaseUrl in config
|
|
73
|
+
"No public base URL configured. Set ingress.publicBaseUrl in config.",
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
76
|
|
package/src/logfire.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getLogfireToken
|
|
1
|
+
import { getLogfireToken } from "./config/env.js";
|
|
2
2
|
import type {
|
|
3
3
|
Message,
|
|
4
4
|
Provider,
|
|
@@ -13,7 +13,7 @@ const log = getLogger("logfire");
|
|
|
13
13
|
|
|
14
14
|
type LogfireModule = typeof import("@pydantic/logfire-node");
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
let logfireEnabled: boolean = !!getLogfireToken();
|
|
17
17
|
|
|
18
18
|
let logfireInstance: LogfireModule | null = null;
|
|
19
19
|
|
|
@@ -23,7 +23,7 @@ let logfireInstance: LogfireModule | null = null;
|
|
|
23
23
|
* Non-fatal on failure (logs warning and continues).
|
|
24
24
|
*/
|
|
25
25
|
export async function initLogfire(): Promise<void> {
|
|
26
|
-
if (!
|
|
26
|
+
if (!logfireEnabled) return;
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
29
|
const logfire = await import("@pydantic/logfire-node");
|
|
@@ -42,12 +42,23 @@ export async function initLogfire(): Promise<void> {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Disable Logfire after early initialization. Called when the user has opted
|
|
47
|
+
* out via the feature flag. Nulls out the instance so future wrapWithLogfire
|
|
48
|
+
* calls become no-ops.
|
|
49
|
+
*/
|
|
50
|
+
export function disableLogfire(): void {
|
|
51
|
+
logfireEnabled = false;
|
|
52
|
+
logfireInstance = null;
|
|
53
|
+
log.info("Logfire disabled by feature flag");
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
/**
|
|
46
57
|
* Wraps a provider with Logfire tracing spans.
|
|
47
|
-
* When
|
|
58
|
+
* When logfireEnabled is false, returns the provider as-is (no wrapper allocated).
|
|
48
59
|
*/
|
|
49
60
|
export function wrapWithLogfire(provider: Provider): Provider {
|
|
50
|
-
if (!
|
|
61
|
+
if (!logfireEnabled) return provider;
|
|
51
62
|
return new LogfireProvider(provider);
|
|
52
63
|
}
|
|
53
64
|
|
|
@@ -117,6 +117,27 @@ export function getOrCreateConversation(conversationKey: string): {
|
|
|
117
117
|
return { conversationId: existing.conversationId, created: false };
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
// Check if the conversationKey itself is an existing conversation ID.
|
|
121
|
+
// This happens when the client loads a thread from the conversations list
|
|
122
|
+
// and uses the server's conversationId as its local sessionId / conversationKey.
|
|
123
|
+
const existingConversation = tx
|
|
124
|
+
.select({ id: conversations.id })
|
|
125
|
+
.from(conversations)
|
|
126
|
+
.where(eq(conversations.id, conversationKey))
|
|
127
|
+
.get();
|
|
128
|
+
|
|
129
|
+
if (existingConversation) {
|
|
130
|
+
tx.insert(conversationKeys)
|
|
131
|
+
.values({
|
|
132
|
+
id: uuid(),
|
|
133
|
+
conversationKey,
|
|
134
|
+
conversationId: existingConversation.id,
|
|
135
|
+
createdAt: Date.now(),
|
|
136
|
+
})
|
|
137
|
+
.run();
|
|
138
|
+
return { conversationId: existingConversation.id, created: false };
|
|
139
|
+
}
|
|
140
|
+
|
|
120
141
|
const now = Date.now();
|
|
121
142
|
const conversationId = uuid();
|
|
122
143
|
|
package/src/memory/db-init.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
createMediaAssetsTables,
|
|
29
29
|
createMessagesFts,
|
|
30
30
|
createNotificationTables,
|
|
31
|
+
createOAuthTables,
|
|
31
32
|
createScopedApprovalGrantsTable,
|
|
32
33
|
createSequenceTables,
|
|
33
34
|
createTasksAndWorkItemsTables,
|
|
@@ -340,6 +341,9 @@ export function initializeDb(): void {
|
|
|
340
341
|
// 52. Drop the legacy reminders table after data migration
|
|
341
342
|
migrateDropRemindersTable(database);
|
|
342
343
|
|
|
344
|
+
// 53. OAuth provider/app/connection tables
|
|
345
|
+
createOAuthTables(database);
|
|
346
|
+
|
|
343
347
|
validateMigrationState(database);
|
|
344
348
|
|
|
345
349
|
if (process.env.BUN_TEST === "1") {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider, app, and connection tables.
|
|
5
|
+
* Creates tables in FK-dependency order: providers → apps → connections.
|
|
6
|
+
*/
|
|
7
|
+
export function createOAuthTables(database: DrizzleDb): void {
|
|
8
|
+
database.run(/*sql*/ `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS oauth_providers (
|
|
10
|
+
provider_key TEXT PRIMARY KEY,
|
|
11
|
+
auth_url TEXT NOT NULL,
|
|
12
|
+
token_url TEXT NOT NULL,
|
|
13
|
+
token_endpoint_auth_method TEXT,
|
|
14
|
+
userinfo_url TEXT,
|
|
15
|
+
base_url TEXT,
|
|
16
|
+
default_scopes TEXT NOT NULL DEFAULT '[]',
|
|
17
|
+
scope_policy TEXT NOT NULL DEFAULT '{}',
|
|
18
|
+
extra_params TEXT,
|
|
19
|
+
callback_transport TEXT,
|
|
20
|
+
loopback_port INTEGER,
|
|
21
|
+
created_at INTEGER NOT NULL,
|
|
22
|
+
updated_at INTEGER NOT NULL
|
|
23
|
+
)
|
|
24
|
+
`);
|
|
25
|
+
|
|
26
|
+
database.run(/*sql*/ `
|
|
27
|
+
CREATE TABLE IF NOT EXISTS oauth_apps (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
provider_key TEXT NOT NULL REFERENCES oauth_providers(provider_key),
|
|
30
|
+
client_id TEXT NOT NULL,
|
|
31
|
+
created_at INTEGER NOT NULL,
|
|
32
|
+
updated_at INTEGER NOT NULL
|
|
33
|
+
)
|
|
34
|
+
`);
|
|
35
|
+
|
|
36
|
+
database.run(/*sql*/ `
|
|
37
|
+
CREATE TABLE IF NOT EXISTS oauth_connections (
|
|
38
|
+
id TEXT PRIMARY KEY,
|
|
39
|
+
oauth_app_id TEXT NOT NULL REFERENCES oauth_apps(id),
|
|
40
|
+
provider_key TEXT NOT NULL,
|
|
41
|
+
account_info TEXT,
|
|
42
|
+
granted_scopes TEXT NOT NULL DEFAULT '[]',
|
|
43
|
+
expires_at INTEGER,
|
|
44
|
+
has_refresh_token INTEGER NOT NULL DEFAULT 0,
|
|
45
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
46
|
+
label TEXT,
|
|
47
|
+
metadata TEXT,
|
|
48
|
+
created_at INTEGER NOT NULL,
|
|
49
|
+
updated_at INTEGER NOT NULL
|
|
50
|
+
)
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
database.run(
|
|
54
|
+
/*sql*/ `CREATE UNIQUE INDEX IF NOT EXISTS idx_oauth_apps_provider_client ON oauth_apps(provider_key, client_id)`,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
database.run(
|
|
58
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_oauth_connections_provider_key ON oauth_connections(provider_key)`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -90,6 +90,7 @@ export { migrateDropAccountsTable } from "./145-drop-accounts-table.js";
|
|
|
90
90
|
export { migrateScheduleOneShotRouting } from "./146-schedule-oneshot-routing.js";
|
|
91
91
|
export { migrateRemindersToSchedules } from "./147-migrate-reminders-to-schedules.js";
|
|
92
92
|
export { migrateDropRemindersTable } from "./148-drop-reminders-table.js";
|
|
93
|
+
export { createOAuthTables } from "./149-oauth-tables.js";
|
|
93
94
|
export {
|
|
94
95
|
MIGRATION_REGISTRY,
|
|
95
96
|
type MigrationRegistryEntry,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
index,
|
|
3
|
+
integer,
|
|
4
|
+
sqliteTable,
|
|
5
|
+
text,
|
|
6
|
+
uniqueIndex,
|
|
7
|
+
} from "drizzle-orm/sqlite-core";
|
|
8
|
+
|
|
9
|
+
export const oauthProviders = sqliteTable("oauth_providers", {
|
|
10
|
+
providerKey: text("provider_key").primaryKey(),
|
|
11
|
+
authUrl: text("auth_url").notNull(),
|
|
12
|
+
tokenUrl: text("token_url").notNull(),
|
|
13
|
+
tokenEndpointAuthMethod: text("token_endpoint_auth_method"),
|
|
14
|
+
userinfoUrl: text("userinfo_url"),
|
|
15
|
+
baseUrl: text("base_url"),
|
|
16
|
+
defaultScopes: text("default_scopes").notNull().default("[]"),
|
|
17
|
+
scopePolicy: text("scope_policy").notNull().default("{}"),
|
|
18
|
+
extraParams: text("extra_params"),
|
|
19
|
+
callbackTransport: text("callback_transport"),
|
|
20
|
+
loopbackPort: integer("loopback_port"),
|
|
21
|
+
createdAt: integer("created_at").notNull(),
|
|
22
|
+
updatedAt: integer("updated_at").notNull(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const oauthApps = sqliteTable(
|
|
26
|
+
"oauth_apps",
|
|
27
|
+
{
|
|
28
|
+
id: text("id").primaryKey(),
|
|
29
|
+
providerKey: text("provider_key")
|
|
30
|
+
.notNull()
|
|
31
|
+
.references(() => oauthProviders.providerKey),
|
|
32
|
+
clientId: text("client_id").notNull(),
|
|
33
|
+
createdAt: integer("created_at").notNull(),
|
|
34
|
+
updatedAt: integer("updated_at").notNull(),
|
|
35
|
+
},
|
|
36
|
+
(table) => [
|
|
37
|
+
uniqueIndex("idx_oauth_apps_provider_client").on(
|
|
38
|
+
table.providerKey,
|
|
39
|
+
table.clientId,
|
|
40
|
+
),
|
|
41
|
+
],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const oauthConnections = sqliteTable(
|
|
45
|
+
"oauth_connections",
|
|
46
|
+
{
|
|
47
|
+
id: text("id").primaryKey(),
|
|
48
|
+
oauthAppId: text("oauth_app_id")
|
|
49
|
+
.notNull()
|
|
50
|
+
.references(() => oauthApps.id),
|
|
51
|
+
providerKey: text("provider_key").notNull(),
|
|
52
|
+
accountInfo: text("account_info"),
|
|
53
|
+
grantedScopes: text("granted_scopes").notNull().default("[]"),
|
|
54
|
+
expiresAt: integer("expires_at"),
|
|
55
|
+
hasRefreshToken: integer("has_refresh_token").notNull().default(0),
|
|
56
|
+
status: text("status").notNull().default("active"),
|
|
57
|
+
label: text("label"),
|
|
58
|
+
metadata: text("metadata"),
|
|
59
|
+
createdAt: integer("created_at").notNull(),
|
|
60
|
+
updatedAt: integer("updated_at").notNull(),
|
|
61
|
+
},
|
|
62
|
+
(table) => [
|
|
63
|
+
index("idx_oauth_connections_provider_key").on(table.providerKey),
|
|
64
|
+
],
|
|
65
|
+
);
|
|
@@ -82,10 +82,10 @@ export interface MessagingProvider {
|
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* Override the default credential check used by getConnectedProviders().
|
|
85
|
-
* When present, the registry calls this instead of
|
|
86
|
-
*
|
|
87
|
-
* that don't use OAuth (e.g. Telegram bot tokens stored
|
|
88
|
-
* non-standard key).
|
|
85
|
+
* When present, the registry calls this instead of checking for an
|
|
86
|
+
* active oauth-store connection via isProviderConnected(). Useful
|
|
87
|
+
* for providers that don't use OAuth (e.g. Telegram bot tokens stored
|
|
88
|
+
* under a non-standard key).
|
|
89
89
|
*/
|
|
90
90
|
isConnected?(): boolean;
|
|
91
91
|
|