@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
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { getConfig } from "../../config/loader.js";
|
|
18
18
|
import { renderHistoryContent } from "../../daemon/handlers/shared.js";
|
|
19
19
|
import { HostBashProxy } from "../../daemon/host-bash-proxy.js";
|
|
20
|
+
import { HostCuProxy } from "../../daemon/host-cu-proxy.js";
|
|
20
21
|
import { HostFileProxy } from "../../daemon/host-file-proxy.js";
|
|
21
22
|
import type { ServerMessage } from "../../daemon/message-protocol.js";
|
|
22
23
|
import {
|
|
@@ -449,6 +450,12 @@ function makeHubPublisher(
|
|
|
449
450
|
conversationId,
|
|
450
451
|
kind: "host_file",
|
|
451
452
|
});
|
|
453
|
+
} else if (msg.type === "host_cu_request") {
|
|
454
|
+
pendingInteractions.register(msg.requestId, {
|
|
455
|
+
session,
|
|
456
|
+
conversationId,
|
|
457
|
+
kind: "host_cu",
|
|
458
|
+
});
|
|
452
459
|
}
|
|
453
460
|
|
|
454
461
|
// ServerMessage is a large union; sessionId exists on most but not all variants.
|
|
@@ -640,9 +647,14 @@ export async function handleSendMessage(
|
|
|
640
647
|
});
|
|
641
648
|
session.setHostFileProxy(fileProxy);
|
|
642
649
|
}
|
|
650
|
+
if (!session.isProcessing() || !session.hostCuProxy) {
|
|
651
|
+
const cuProxy = new HostCuProxy(onEvent);
|
|
652
|
+
session.setHostCuProxy(cuProxy);
|
|
653
|
+
}
|
|
643
654
|
} else if (!session.isProcessing()) {
|
|
644
655
|
session.setHostBashProxy(undefined);
|
|
645
656
|
session.setHostFileProxy(undefined);
|
|
657
|
+
session.setHostCuProxy(undefined);
|
|
646
658
|
}
|
|
647
659
|
// Wire sendToClient to the SSE hub so all subsystems can reach the HTTP client.
|
|
648
660
|
// Called after setHostBashProxy so updateSender targets the current proxy.
|
|
@@ -679,7 +691,13 @@ export async function handleSendMessage(
|
|
|
679
691
|
attachments,
|
|
680
692
|
session,
|
|
681
693
|
onEvent,
|
|
682
|
-
|
|
694
|
+
// Desktop path: disable NL classification to avoid consuming non-decision
|
|
695
|
+
// messages while a tool confirmation is pending. Deterministic code-prefix
|
|
696
|
+
// and callback parsing remain active. Mirrors session-process.ts behavior.
|
|
697
|
+
approvalConversationGenerator:
|
|
698
|
+
sourceChannel === "vellum"
|
|
699
|
+
? undefined
|
|
700
|
+
: deps.approvalConversationGenerator,
|
|
683
701
|
verifiedActorExternalUserId,
|
|
684
702
|
verifiedActorPrincipalId,
|
|
685
703
|
});
|
|
@@ -687,6 +705,7 @@ export async function handleSendMessage(
|
|
|
687
705
|
return Response.json(
|
|
688
706
|
{
|
|
689
707
|
accepted: true,
|
|
708
|
+
conversationId: mapping.conversationId,
|
|
690
709
|
...(inlineReplyResult.messageId
|
|
691
710
|
? { messageId: inlineReplyResult.messageId }
|
|
692
711
|
: {}),
|
|
@@ -751,7 +770,10 @@ export async function handleSendMessage(
|
|
|
751
770
|
pendingInteractions.removeBySession(session);
|
|
752
771
|
}
|
|
753
772
|
|
|
754
|
-
return Response.json(
|
|
773
|
+
return Response.json(
|
|
774
|
+
{ accepted: true, queued: true, conversationId: mapping.conversationId },
|
|
775
|
+
{ status: 202 },
|
|
776
|
+
);
|
|
755
777
|
}
|
|
756
778
|
|
|
757
779
|
// Session is idle — persist and fire agent loop immediately
|
|
@@ -782,6 +804,7 @@ export async function handleSendMessage(
|
|
|
782
804
|
|
|
783
805
|
if (slashResult.kind === "unknown") {
|
|
784
806
|
session.processing = true;
|
|
807
|
+
let cleanupDeferred = false;
|
|
785
808
|
try {
|
|
786
809
|
const provenance = provenanceFromTrustContext(session.trustContext);
|
|
787
810
|
const channelMeta = {
|
|
@@ -818,26 +841,54 @@ export async function handleSendMessage(
|
|
|
818
841
|
sourceInterface,
|
|
819
842
|
);
|
|
820
843
|
|
|
821
|
-
//
|
|
822
|
-
//
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
onEvent({ type: "assistant_text_delta", text: slashResult.message });
|
|
829
|
-
onEvent({
|
|
830
|
-
type: "message_complete",
|
|
831
|
-
sessionId: mapping.conversationId,
|
|
832
|
-
});
|
|
844
|
+
// Snapshot model info now so the deferred callback cannot observe
|
|
845
|
+
// a config change from a concurrent request.
|
|
846
|
+
const modelInfoEvent =
|
|
847
|
+
isModelSlashCommand(rawContent) || isProviderShortcut(rawContent)
|
|
848
|
+
? buildModelInfoEvent()
|
|
849
|
+
: null;
|
|
833
850
|
|
|
834
|
-
|
|
835
|
-
{
|
|
851
|
+
const response = Response.json(
|
|
852
|
+
{
|
|
853
|
+
accepted: true,
|
|
854
|
+
messageId: persisted.id,
|
|
855
|
+
conversationId: mapping.conversationId,
|
|
856
|
+
},
|
|
836
857
|
{ status: 202 },
|
|
837
858
|
);
|
|
859
|
+
|
|
860
|
+
// Defer event publishing to next tick so the HTTP response reaches the
|
|
861
|
+
// client first. This ensures the client's serverToLocalSessionMap is
|
|
862
|
+
// populated before SSE events arrive, preventing dropped events in new
|
|
863
|
+
// desktop threads.
|
|
864
|
+
//
|
|
865
|
+
// session.processing and drainQueue are also deferred so the current
|
|
866
|
+
// slash command's events are emitted before the next queued message
|
|
867
|
+
// starts processing.
|
|
868
|
+
const conversationId = mapping.conversationId;
|
|
869
|
+
const message = slashResult.message;
|
|
870
|
+
setTimeout(() => {
|
|
871
|
+
if (modelInfoEvent) {
|
|
872
|
+
onEvent(modelInfoEvent);
|
|
873
|
+
}
|
|
874
|
+
onEvent({ type: "assistant_text_delta", text: message });
|
|
875
|
+
onEvent({
|
|
876
|
+
type: "message_complete",
|
|
877
|
+
sessionId: conversationId,
|
|
878
|
+
});
|
|
879
|
+
session.processing = false;
|
|
880
|
+
session.drainQueue().catch(() => {});
|
|
881
|
+
}, 0);
|
|
882
|
+
|
|
883
|
+
cleanupDeferred = true;
|
|
884
|
+
return response;
|
|
838
885
|
} finally {
|
|
839
|
-
|
|
840
|
-
|
|
886
|
+
// No-op for the slash-command early-return path (handled inside
|
|
887
|
+
// setTimeout above), but still needed for error paths.
|
|
888
|
+
if (!cleanupDeferred && session.processing) {
|
|
889
|
+
session.processing = false;
|
|
890
|
+
session.drainQueue().catch(() => {});
|
|
891
|
+
}
|
|
841
892
|
}
|
|
842
893
|
}
|
|
843
894
|
|
|
@@ -874,7 +925,10 @@ export async function handleSendMessage(
|
|
|
874
925
|
);
|
|
875
926
|
});
|
|
876
927
|
|
|
877
|
-
return Response.json(
|
|
928
|
+
return Response.json(
|
|
929
|
+
{ accepted: true, messageId, conversationId: mapping.conversationId },
|
|
930
|
+
{ status: 202 },
|
|
931
|
+
);
|
|
878
932
|
}
|
|
879
933
|
|
|
880
934
|
async function generateLlmSuggestion(
|
|
@@ -7,12 +7,17 @@
|
|
|
7
7
|
* is called. The AuthContext is threaded through from the HTTP server
|
|
8
8
|
* layer, so no additional actor-token verification is needed here.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* When `conversationKey` is provided, subscribers receive events scoped to
|
|
11
|
+
* that conversation. When omitted, subscribers receive events from ALL
|
|
12
|
+
* conversations for this assistant (unfiltered).
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
import { getOrCreateConversation } from "../../memory/conversation-key-store.js";
|
|
14
16
|
import { formatSseFrame, formatSseHeartbeat } from "../assistant-event.js";
|
|
15
|
-
import type {
|
|
17
|
+
import type {
|
|
18
|
+
AssistantEventFilter,
|
|
19
|
+
AssistantEventSubscription,
|
|
20
|
+
} from "../assistant-event-hub.js";
|
|
16
21
|
import {
|
|
17
22
|
AssistantEventHub,
|
|
18
23
|
assistantEventHub,
|
|
@@ -26,10 +31,12 @@ import type { RouteDefinition } from "../http-router.js";
|
|
|
26
31
|
const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
|
-
* Stream assistant events as Server-Sent Events
|
|
34
|
+
* Stream assistant events as Server-Sent Events.
|
|
30
35
|
*
|
|
31
36
|
* Query params:
|
|
32
|
-
* conversationKey --
|
|
37
|
+
* conversationKey -- optional; when provided, scopes the stream to one
|
|
38
|
+
* conversation. When omitted, the stream delivers events
|
|
39
|
+
* from ALL conversations for this assistant.
|
|
33
40
|
*
|
|
34
41
|
* Options (for testing):
|
|
35
42
|
* hub -- override the event hub (defaults to process singleton).
|
|
@@ -56,15 +63,21 @@ export function handleSubscribeAssistantEvents(
|
|
|
56
63
|
// scope and principal type requirements.
|
|
57
64
|
|
|
58
65
|
const conversationKey = url.searchParams.get("conversationKey");
|
|
59
|
-
if (!conversationKey) {
|
|
60
|
-
return httpError("BAD_REQUEST", "conversationKey
|
|
66
|
+
if (url.searchParams.has("conversationKey") && !conversationKey?.trim()) {
|
|
67
|
+
return httpError("BAD_REQUEST", "conversationKey must not be empty", 400);
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
const hub = options?.hub ?? assistantEventHub;
|
|
64
71
|
const heartbeatIntervalMs =
|
|
65
72
|
options?.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
66
73
|
|
|
67
|
-
const
|
|
74
|
+
const filter: AssistantEventFilter = {
|
|
75
|
+
assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
|
|
76
|
+
};
|
|
77
|
+
if (conversationKey) {
|
|
78
|
+
const mapping = getOrCreateConversation(conversationKey);
|
|
79
|
+
filter.sessionId = mapping.conversationId;
|
|
80
|
+
}
|
|
68
81
|
const encoder = new TextEncoder();
|
|
69
82
|
|
|
70
83
|
// -- Eager subscribe --------------------------------------------------------
|
|
@@ -90,10 +103,7 @@ export function handleSubscribeAssistantEvents(
|
|
|
90
103
|
|
|
91
104
|
try {
|
|
92
105
|
sub = hub.subscribe(
|
|
93
|
-
|
|
94
|
-
assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
|
|
95
|
-
sessionId: mapping.conversationId,
|
|
96
|
-
},
|
|
106
|
+
filter,
|
|
97
107
|
(event) => {
|
|
98
108
|
const controller = controllerRef;
|
|
99
109
|
if (!controller) return;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handler for host CU (computer-use) result submissions.
|
|
3
|
+
*
|
|
4
|
+
* Resolves pending host CU proxy requests by requestId when the desktop
|
|
5
|
+
* client returns observation results via HTTP.
|
|
6
|
+
*/
|
|
7
|
+
import { requireBoundGuardian } from "../auth/require-bound-guardian.js";
|
|
8
|
+
import type { AuthContext } from "../auth/types.js";
|
|
9
|
+
import { httpError } from "../http-errors.js";
|
|
10
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
11
|
+
import * as pendingInteractions from "../pending-interactions.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* POST /v1/host-cu-result — resolve a pending host CU request by requestId.
|
|
15
|
+
* Requires AuthContext with guardian-bound actor.
|
|
16
|
+
*/
|
|
17
|
+
export async function handleHostCuResult(
|
|
18
|
+
req: Request,
|
|
19
|
+
authContext: AuthContext,
|
|
20
|
+
): Promise<Response> {
|
|
21
|
+
const authError = requireBoundGuardian(authContext);
|
|
22
|
+
if (authError) return authError;
|
|
23
|
+
|
|
24
|
+
const body = (await req.json()) as {
|
|
25
|
+
requestId?: string;
|
|
26
|
+
axTree?: string;
|
|
27
|
+
axDiff?: string;
|
|
28
|
+
screenshot?: string;
|
|
29
|
+
screenshotWidthPx?: number;
|
|
30
|
+
screenshotHeightPx?: number;
|
|
31
|
+
screenWidthPt?: number;
|
|
32
|
+
screenHeightPt?: number;
|
|
33
|
+
executionResult?: string;
|
|
34
|
+
executionError?: string;
|
|
35
|
+
secondaryWindows?: string;
|
|
36
|
+
userGuidance?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const { requestId } = body;
|
|
40
|
+
|
|
41
|
+
if (!requestId || typeof requestId !== "string") {
|
|
42
|
+
return httpError("BAD_REQUEST", "requestId is required", 400);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Peek first (non-destructive) so we can validate the interaction kind
|
|
46
|
+
// without accidentally consuming a confirmation or secret interaction.
|
|
47
|
+
const peeked = pendingInteractions.get(requestId);
|
|
48
|
+
if (!peeked) {
|
|
49
|
+
return httpError(
|
|
50
|
+
"NOT_FOUND",
|
|
51
|
+
"No pending interaction found for this requestId",
|
|
52
|
+
404,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (peeked.kind !== "host_cu") {
|
|
57
|
+
return httpError(
|
|
58
|
+
"CONFLICT",
|
|
59
|
+
`Pending interaction is of kind "${peeked.kind}", expected "host_cu"`,
|
|
60
|
+
409,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Validation passed — consume the pending interaction.
|
|
65
|
+
const interaction = pendingInteractions.resolve(requestId)!;
|
|
66
|
+
|
|
67
|
+
interaction.session.resolveHostCu(requestId, {
|
|
68
|
+
axTree: body.axTree,
|
|
69
|
+
axDiff: body.axDiff,
|
|
70
|
+
screenshot: body.screenshot,
|
|
71
|
+
screenshotWidthPx: body.screenshotWidthPx,
|
|
72
|
+
screenshotHeightPx: body.screenshotHeightPx,
|
|
73
|
+
screenWidthPt: body.screenWidthPt,
|
|
74
|
+
screenHeightPt: body.screenHeightPt,
|
|
75
|
+
executionResult: body.executionResult,
|
|
76
|
+
executionError: body.executionError,
|
|
77
|
+
secondaryWindows: body.secondaryWindows,
|
|
78
|
+
userGuidance: body.userGuidance,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return Response.json({ accepted: true });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Route definitions
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
export function hostCuRouteDefinitions(): RouteDefinition[] {
|
|
89
|
+
return [
|
|
90
|
+
{
|
|
91
|
+
endpoint: "host-cu-result",
|
|
92
|
+
method: "POST",
|
|
93
|
+
handler: async ({ req, authContext }) =>
|
|
94
|
+
handleHostCuResult(req, authContext),
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
}
|
|
@@ -31,12 +31,8 @@ import type {
|
|
|
31
31
|
ApprovalCopyGenerator,
|
|
32
32
|
MessageProcessor,
|
|
33
33
|
} from "../../http-types.js";
|
|
34
|
-
import { TelegramStreamingDelivery } from "../../telegram-streaming-delivery.js";
|
|
35
34
|
import { resolveRoutingState } from "../../trust-context-resolver.js";
|
|
36
|
-
import {
|
|
37
|
-
deliverAttachmentsOnly,
|
|
38
|
-
deliverReplyViaCallback,
|
|
39
|
-
} from "../channel-delivery-routes.js";
|
|
35
|
+
import { deliverReplyViaCallback } from "../channel-delivery-routes.js";
|
|
40
36
|
import { deliverGeneratedApprovalPrompt } from "../guardian-approval-prompt.js";
|
|
41
37
|
|
|
42
38
|
const log = getLogger("runtime-http");
|
|
@@ -112,12 +108,6 @@ export function processChannelMessageInBackground(
|
|
|
112
108
|
} = params;
|
|
113
109
|
|
|
114
110
|
(async () => {
|
|
115
|
-
const boundGuardianActor = isBoundGuardianActor({
|
|
116
|
-
trustClass: trustCtx.trustClass,
|
|
117
|
-
guardianExternalUserId: trustCtx.guardianExternalUserId,
|
|
118
|
-
requesterExternalUserId: trustCtx.requesterExternalUserId,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
111
|
const typingCallbackUrl = shouldEmitTelegramTyping(
|
|
122
112
|
sourceChannel,
|
|
123
113
|
replyCallbackUrl,
|
|
@@ -181,16 +171,6 @@ export function processChannelMessageInBackground(
|
|
|
181
171
|
}
|
|
182
172
|
}
|
|
183
173
|
|
|
184
|
-
const telegramStreaming =
|
|
185
|
-
sourceChannel === "telegram" && replyCallbackUrl
|
|
186
|
-
? new TelegramStreamingDelivery({
|
|
187
|
-
callbackUrl: replyCallbackUrl,
|
|
188
|
-
chatId: externalChatId,
|
|
189
|
-
mintBearerToken,
|
|
190
|
-
assistantId,
|
|
191
|
-
})
|
|
192
|
-
: undefined;
|
|
193
|
-
|
|
194
174
|
try {
|
|
195
175
|
const cmdIntent =
|
|
196
176
|
commandIntent && typeof commandIntent.type === "string"
|
|
@@ -218,9 +198,6 @@ export function processChannelMessageInBackground(
|
|
|
218
198
|
trustContext: trustCtx,
|
|
219
199
|
isInteractive: resolveRoutingState(trustCtx).promptWaitingAllowed,
|
|
220
200
|
...(cmdIntent ? { commandIntent: cmdIntent } : {}),
|
|
221
|
-
...(telegramStreaming
|
|
222
|
-
? { onEvent: (msg) => telegramStreaming.onEvent(msg) }
|
|
223
|
-
: {}),
|
|
224
201
|
},
|
|
225
202
|
sourceChannel,
|
|
226
203
|
sourceInterface,
|
|
@@ -228,94 +205,18 @@ export function processChannelMessageInBackground(
|
|
|
228
205
|
deliveryCrud.linkMessage(eventId, userMessageId);
|
|
229
206
|
deliveryStatus.markProcessed(eventId);
|
|
230
207
|
|
|
231
|
-
if (telegramStreaming) {
|
|
232
|
-
// Retrieve approval metadata from pending interactions (if any)
|
|
233
|
-
// so approval buttons can be attached to the final streamed message.
|
|
234
|
-
// Approval prompts are guardian-only and must never be attached for
|
|
235
|
-
// non-guardian or unverified actors.
|
|
236
|
-
const prompt = boundGuardianActor
|
|
237
|
-
? getChannelApprovalPrompt(conversationId)
|
|
238
|
-
: undefined;
|
|
239
|
-
const pending = boundGuardianActor
|
|
240
|
-
? getApprovalInfoByConversation(conversationId)
|
|
241
|
-
: [];
|
|
242
|
-
const approvalMeta =
|
|
243
|
-
prompt && pending.length > 0
|
|
244
|
-
? buildApprovalUIMetadata(prompt, pending[0])
|
|
245
|
-
: undefined;
|
|
246
|
-
try {
|
|
247
|
-
await telegramStreaming.finish(approvalMeta);
|
|
248
|
-
deliveryChannels.updateDeliveredSegmentCount(eventId, 1);
|
|
249
|
-
} catch (err) {
|
|
250
|
-
log.error(
|
|
251
|
-
{ err, conversationId },
|
|
252
|
-
"Telegram streaming finalization failed",
|
|
253
|
-
);
|
|
254
|
-
// Fallback: deliver approval as a standalone message so buttons
|
|
255
|
-
// are not permanently lost when finish() fails.
|
|
256
|
-
if (approvalMeta && replyCallbackUrl) {
|
|
257
|
-
try {
|
|
258
|
-
await deliverChannelReply(
|
|
259
|
-
replyCallbackUrl,
|
|
260
|
-
{
|
|
261
|
-
chatId: externalChatId,
|
|
262
|
-
text: approvalMeta.plainTextFallback ?? "Action needed:",
|
|
263
|
-
approval: approvalMeta,
|
|
264
|
-
assistantId,
|
|
265
|
-
},
|
|
266
|
-
mintBearerToken(),
|
|
267
|
-
);
|
|
268
|
-
} catch (fallbackErr) {
|
|
269
|
-
log.error(
|
|
270
|
-
{ err: fallbackErr, conversationId },
|
|
271
|
-
"Fallback approval delivery also failed",
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
208
|
if (replyCallbackUrl) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
mintBearerToken(),
|
|
291
|
-
assistantId,
|
|
292
|
-
);
|
|
293
|
-
} else {
|
|
294
|
-
// Non-streaming path, or streaming partially failed (some text
|
|
295
|
-
// was delivered but finish/finalization threw). In the partial
|
|
296
|
-
// failure case the user has a truncated message, so we deliver
|
|
297
|
-
// the full response to ensure nothing is lost.
|
|
298
|
-
if (
|
|
299
|
-
telegramStreaming?.hasDeliveredText &&
|
|
300
|
-
!telegramStreaming.finishSucceeded
|
|
301
|
-
) {
|
|
302
|
-
log.warn(
|
|
303
|
-
{ conversationId },
|
|
304
|
-
"Telegram streaming partially failed — falling back to full text delivery",
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
await deliverReplyViaCallback(
|
|
308
|
-
conversationId,
|
|
309
|
-
externalChatId,
|
|
310
|
-
replyCallbackUrl,
|
|
311
|
-
mintBearerToken(),
|
|
312
|
-
assistantId,
|
|
313
|
-
{
|
|
314
|
-
onSegmentDelivered: (count) =>
|
|
315
|
-
deliveryChannels.updateDeliveredSegmentCount(eventId, count),
|
|
316
|
-
},
|
|
317
|
-
);
|
|
318
|
-
}
|
|
209
|
+
await deliverReplyViaCallback(
|
|
210
|
+
conversationId,
|
|
211
|
+
externalChatId,
|
|
212
|
+
replyCallbackUrl,
|
|
213
|
+
mintBearerToken(),
|
|
214
|
+
assistantId,
|
|
215
|
+
{
|
|
216
|
+
onSegmentDelivered: (count) =>
|
|
217
|
+
deliveryChannels.updateDeliveredSegmentCount(eventId, count),
|
|
218
|
+
},
|
|
219
|
+
);
|
|
319
220
|
}
|
|
320
221
|
} catch (err) {
|
|
321
222
|
log.error(
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
userInfo,
|
|
13
13
|
} from "../../../../messaging/providers/slack/client.js";
|
|
14
14
|
import type { SlackConversation } from "../../../../messaging/providers/slack/types.js";
|
|
15
|
-
import {
|
|
15
|
+
import { getConnectionByProvider } from "../../../../oauth/oauth-store.js";
|
|
16
16
|
import { getSecureKey } from "../../../../security/secure-keys.js";
|
|
17
17
|
import { getLogger } from "../../../../util/logger.js";
|
|
18
18
|
import { httpError } from "../../../http-errors.js";
|
|
@@ -25,14 +25,13 @@ const log = getLogger("slack-share");
|
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Resolve the Slack bot token from
|
|
29
|
-
* Prefers the OAuth integration token, falls back to the legacy channel token.
|
|
28
|
+
* Resolve the Slack bot token from the OAuth connection store.
|
|
30
29
|
*/
|
|
31
30
|
function resolveSlackToken(): string | undefined {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
getSecureKey(
|
|
35
|
-
|
|
31
|
+
const conn = getConnectionByProvider("integration:slack");
|
|
32
|
+
return conn
|
|
33
|
+
? getSecureKey(`oauth_connection/${conn.id}/access_token`)
|
|
34
|
+
: undefined;
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
// ---------------------------------------------------------------------------
|
|
@@ -14,7 +14,11 @@ import { desc } from "drizzle-orm";
|
|
|
14
14
|
import { getDb } from "../../memory/db.js";
|
|
15
15
|
import { toolInvocations } from "../../memory/schema.js";
|
|
16
16
|
import { getLogger } from "../../util/logger.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
getDataDir,
|
|
19
|
+
getRootDir,
|
|
20
|
+
getWorkspaceConfigPath,
|
|
21
|
+
} from "../../util/platform.js";
|
|
18
22
|
import { httpError } from "../http-errors.js";
|
|
19
23
|
import type { RouteDefinition } from "../http-router.js";
|
|
20
24
|
|
|
@@ -31,6 +35,7 @@ interface ExportResponse {
|
|
|
31
35
|
success: true;
|
|
32
36
|
auditRows: Array<Record<string, unknown>>;
|
|
33
37
|
logFiles: Record<string, string>;
|
|
38
|
+
configSnapshot?: Record<string, unknown>;
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
/**
|
|
@@ -83,21 +88,134 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
|
|
91
|
+
// --- Sanitized config snapshot ---
|
|
92
|
+
const configSnapshot = readSanitizedConfig();
|
|
93
|
+
|
|
86
94
|
log.info(
|
|
87
|
-
{
|
|
95
|
+
{
|
|
96
|
+
auditCount: auditRows.length,
|
|
97
|
+
logFileCount: Object.keys(logFiles).length,
|
|
98
|
+
totalBytes,
|
|
99
|
+
hasConfig: configSnapshot !== undefined,
|
|
100
|
+
},
|
|
88
101
|
"Export completed",
|
|
89
102
|
);
|
|
90
103
|
|
|
91
|
-
const payload: ExportResponse = {
|
|
104
|
+
const payload: ExportResponse = {
|
|
105
|
+
success: true,
|
|
106
|
+
auditRows,
|
|
107
|
+
logFiles,
|
|
108
|
+
configSnapshot,
|
|
109
|
+
};
|
|
92
110
|
return Response.json(payload);
|
|
93
111
|
} catch (err) {
|
|
94
112
|
const message = err instanceof Error ? err.message : String(err);
|
|
95
113
|
log.error({ err }, "Failed to export");
|
|
96
|
-
return httpError(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
114
|
+
return httpError("INTERNAL_ERROR", `Failed to export: ${message}`, 500);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Replaces a string value with a presence flag: "(set)" if truthy, "(empty)" otherwise.
|
|
120
|
+
*/
|
|
121
|
+
function redactStringValue(val: unknown): string {
|
|
122
|
+
return val ? "(set)" : "(empty)";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Reads the workspace config.json and strips sensitive fields.
|
|
127
|
+
* Returns undefined if the file is missing or unreadable.
|
|
128
|
+
*/
|
|
129
|
+
function readSanitizedConfig(): Record<string, unknown> | undefined {
|
|
130
|
+
const configPath = getWorkspaceConfigPath();
|
|
131
|
+
if (!existsSync(configPath)) return undefined;
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
135
|
+
const config = JSON.parse(raw) as Record<string, unknown>;
|
|
136
|
+
|
|
137
|
+
// Strip API key values — preserve which providers have keys configured
|
|
138
|
+
if (config.apiKeys && typeof config.apiKeys === "object") {
|
|
139
|
+
const keys = config.apiKeys as Record<string, unknown>;
|
|
140
|
+
config.apiKeys = Object.fromEntries(
|
|
141
|
+
Object.keys(keys).map((k) => [k, redactStringValue(keys[k])]),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Strip ingress webhook secret
|
|
146
|
+
if (config.ingress && typeof config.ingress === "object") {
|
|
147
|
+
const ingress = config.ingress as Record<string, unknown>;
|
|
148
|
+
if (ingress.webhook && typeof ingress.webhook === "object") {
|
|
149
|
+
const webhook = ingress.webhook as Record<string, unknown>;
|
|
150
|
+
webhook.secret = redactStringValue(webhook.secret);
|
|
151
|
+
ingress.webhook = webhook;
|
|
152
|
+
}
|
|
153
|
+
config.ingress = ingress;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Strip skill-level API keys and env vars
|
|
157
|
+
if (config.skills && typeof config.skills === "object") {
|
|
158
|
+
const skills = config.skills as Record<string, unknown>;
|
|
159
|
+
if (skills.entries && typeof skills.entries === "object") {
|
|
160
|
+
const entries = skills.entries as Record<string, unknown>;
|
|
161
|
+
for (const name of Object.keys(entries)) {
|
|
162
|
+
const entry = entries[name];
|
|
163
|
+
if (entry && typeof entry === "object") {
|
|
164
|
+
const e = entry as Record<string, unknown>;
|
|
165
|
+
if ("apiKey" in e) e.apiKey = redactStringValue(e.apiKey);
|
|
166
|
+
if (e.env && typeof e.env === "object") {
|
|
167
|
+
const env = e.env as Record<string, unknown>;
|
|
168
|
+
e.env = Object.fromEntries(
|
|
169
|
+
Object.keys(env).map((k) => [k, redactStringValue(env[k])]),
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Strip Twilio accountSid
|
|
178
|
+
if (config.twilio && typeof config.twilio === "object") {
|
|
179
|
+
const twilio = config.twilio as Record<string, unknown>;
|
|
180
|
+
twilio.accountSid = redactStringValue(twilio.accountSid);
|
|
181
|
+
config.twilio = twilio;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Strip MCP transport headers (SSE/streamable-http) and env vars (stdio)
|
|
185
|
+
if (config.mcp && typeof config.mcp === "object") {
|
|
186
|
+
const mcp = config.mcp as Record<string, unknown>;
|
|
187
|
+
if (mcp.servers && typeof mcp.servers === "object") {
|
|
188
|
+
const servers = mcp.servers as Record<string, unknown>;
|
|
189
|
+
for (const name of Object.keys(servers)) {
|
|
190
|
+
const server = servers[name];
|
|
191
|
+
if (server && typeof server === "object") {
|
|
192
|
+
const s = server as Record<string, unknown>;
|
|
193
|
+
if (s.transport && typeof s.transport === "object") {
|
|
194
|
+
const transport = s.transport as Record<string, unknown>;
|
|
195
|
+
if (transport.headers && typeof transport.headers === "object") {
|
|
196
|
+
const headers = transport.headers as Record<string, unknown>;
|
|
197
|
+
transport.headers = Object.fromEntries(
|
|
198
|
+
Object.keys(headers).map((k) => [
|
|
199
|
+
k,
|
|
200
|
+
redactStringValue(headers[k]),
|
|
201
|
+
]),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
if (transport.env && typeof transport.env === "object") {
|
|
205
|
+
const env = transport.env as Record<string, unknown>;
|
|
206
|
+
transport.env = Object.fromEntries(
|
|
207
|
+
Object.keys(env).map((k) => [k, redactStringValue(env[k])]),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return config;
|
|
217
|
+
} catch {
|
|
218
|
+
return undefined;
|
|
101
219
|
}
|
|
102
220
|
}
|
|
103
221
|
|