@vellumai/assistant 0.5.10 → 0.5.11
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/AGENTS.md +8 -0
- package/ARCHITECTURE.md +43 -43
- package/Dockerfile +2 -0
- package/docs/architecture/integrations.md +3 -10
- package/docs/architecture/memory.md +7 -12
- package/docs/credential-execution-service.md +9 -9
- package/docs/skills.md +1 -1
- package/node_modules/@vellumai/credential-storage/src/index.ts +2 -2
- package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
- package/openapi.yaml +7130 -0
- package/package.json +2 -1
- package/scripts/generate-openapi.ts +562 -0
- package/src/__tests__/acp-session.test.ts +239 -44
- package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
- package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
- package/src/__tests__/browser-skill-endstate.test.ts +1 -1
- package/src/__tests__/btw-routes.test.ts +8 -0
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
- package/src/__tests__/channel-approvals.test.ts +7 -7
- package/src/__tests__/channel-readiness-service.test.ts +41 -0
- package/src/__tests__/config-schema.test.ts +10 -2
- package/src/__tests__/context-memory-e2e.test.ts +2 -6
- package/src/__tests__/conversation-skill-tools.test.ts +1 -3
- package/src/__tests__/conversation-title-service.test.ts +2 -15
- package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
- package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
- package/src/__tests__/credential-security-e2e.test.ts +4 -4
- package/src/__tests__/credential-security-invariants.test.ts +3 -3
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/heartbeat-service.test.ts +35 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
- package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
- package/src/__tests__/memory-recall-log-store.test.ts +182 -0
- package/src/__tests__/memory-recall-quality.test.ts +6 -8
- package/src/__tests__/memory-regressions.test.ts +53 -42
- package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
- package/src/__tests__/messaging-skill-split.test.ts +2 -17
- package/src/__tests__/oauth-cli.test.ts +98 -551
- package/src/__tests__/platform-callback-registration.test.ts +119 -0
- package/src/__tests__/secret-ingress-channel.test.ts +261 -0
- package/src/__tests__/secret-ingress-cli.test.ts +201 -0
- package/src/__tests__/secret-ingress-http.test.ts +312 -0
- package/src/__tests__/secret-ingress.test.ts +283 -0
- package/src/__tests__/secret-onetime-send.test.ts +4 -4
- package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
- package/src/__tests__/skill-feature-flags.test.ts +11 -19
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
- package/src/__tests__/skill-load-inline-command.test.ts +3 -3
- package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
- package/src/__tests__/skill-memory.test.ts +2 -4
- package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
- package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
- package/src/__tests__/skills.test.ts +16 -2
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-skill.test.ts +5 -69
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
- package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
- package/src/acp/client-handler.ts +113 -31
- package/src/acp/session-manager.ts +29 -27
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/cli/AGENTS.md +73 -0
- package/src/cli/commands/autonomy.ts +3 -5
- package/src/cli/commands/credential-execution.ts +1 -2
- package/src/cli/commands/memory.ts +2 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +785 -0
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +760 -0
- package/src/cli/commands/oauth/__tests__/mode.test.ts +672 -0
- package/src/cli/commands/oauth/__tests__/ping.test.ts +690 -0
- package/src/cli/commands/oauth/__tests__/status.test.ts +579 -0
- package/src/cli/commands/oauth/__tests__/token.test.ts +467 -0
- package/src/cli/commands/oauth/apps.ts +26 -8
- package/src/cli/commands/oauth/connect.ts +373 -0
- package/src/cli/commands/oauth/connections.ts +14 -493
- package/src/cli/commands/oauth/disconnect.ts +333 -0
- package/src/cli/commands/oauth/index.ts +62 -10
- package/src/cli/commands/oauth/mode.ts +263 -0
- package/src/cli/commands/oauth/ping.ts +222 -0
- package/src/cli/commands/oauth/providers.ts +30 -3
- package/src/cli/commands/oauth/request.ts +576 -0
- package/src/cli/commands/oauth/shared.ts +132 -0
- package/src/cli/commands/oauth/status.ts +202 -0
- package/src/cli/commands/oauth/token.ts +159 -0
- package/src/cli/commands/platform.ts +20 -14
- package/src/cli.ts +82 -17
- package/src/config/assistant-feature-flags.ts +74 -11
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +13 -36
- package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +2 -2
- package/src/config/bundled-skills/settings/SKILL.md +5 -3
- package/src/config/bundled-skills/settings/TOOLS.json +17 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
- package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
- package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
- package/src/config/bundled-skills/slack/SKILL.md +58 -44
- package/src/config/bundled-tool-registry.ts +2 -19
- package/src/config/env.ts +5 -1
- package/src/config/feature-flag-registry.json +57 -41
- package/src/config/loader.ts +4 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/security.ts +9 -1
- package/src/config/schemas/services.ts +1 -1
- package/src/config/skill-state.ts +1 -3
- package/src/config/skills.ts +2 -4
- package/src/credential-execution/feature-gates.ts +9 -16
- package/src/credential-execution/process-manager.ts +12 -0
- package/src/daemon/config-watcher.ts +4 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
- package/src/daemon/conversation-agent-loop.ts +49 -2
- package/src/daemon/conversation-memory.ts +0 -1
- package/src/daemon/handlers/config-slack-channel.ts +43 -1
- package/src/daemon/handlers/conversations.ts +41 -33
- package/src/daemon/lifecycle.ts +26 -2
- package/src/daemon/message-types/acp.ts +0 -15
- package/src/daemon/message-types/memory.ts +0 -1
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/schedules.ts +9 -0
- package/src/daemon/server.ts +19 -7
- package/src/email/feature-gate.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +48 -0
- package/src/inbound/platform-callback-registration.ts +61 -7
- package/src/mcp/mcp-oauth-provider.ts +3 -3
- package/src/memory/app-store.ts +3 -3
- package/src/memory/conversation-crud.ts +124 -0
- package/src/memory/conversation-title-service.ts +7 -17
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +47 -2
- package/src/memory/indexer.ts +13 -10
- package/src/memory/items-extractor.ts +12 -4
- package/src/memory/job-utils.ts +5 -0
- package/src/memory/jobs-store.ts +10 -2
- package/src/memory/journal-memory.ts +6 -2
- package/src/memory/llm-request-log-store.ts +88 -21
- package/src/memory/memory-recall-log-store.ts +128 -0
- package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
- package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/retriever.test.ts +4 -5
- package/src/memory/schema/infrastructure.ts +31 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -1
- package/src/oauth/connect-orchestrator.ts +54 -0
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +26 -5
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -2
- package/src/permissions/trust-client.ts +2 -2
- package/src/platform/client.ts +2 -2
- package/src/prompts/journal-context.ts +6 -1
- package/src/providers/anthropic/client.ts +143 -1
- package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
- package/src/runtime/auth/route-policy.ts +0 -1
- package/src/runtime/btw-sidechain.ts +7 -1
- package/src/runtime/channel-approvals.ts +2 -2
- package/src/runtime/channel-readiness-service.ts +30 -7
- package/src/runtime/http-router.ts +31 -0
- package/src/runtime/http-server.ts +21 -4
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/pending-interactions.ts +21 -3
- package/src/runtime/routes/acp-routes.ts +46 -28
- package/src/runtime/routes/app-management-routes.ts +123 -0
- package/src/runtime/routes/app-routes.ts +31 -0
- package/src/runtime/routes/approval-routes.ts +108 -3
- package/src/runtime/routes/attachment-routes.ts +45 -0
- package/src/runtime/routes/avatar-routes.ts +16 -0
- package/src/runtime/routes/brain-graph-routes.ts +18 -0
- package/src/runtime/routes/btw-routes.ts +20 -0
- package/src/runtime/routes/call-routes.ts +81 -0
- package/src/runtime/routes/channel-readiness-routes.ts +48 -7
- package/src/runtime/routes/channel-routes.ts +18 -0
- package/src/runtime/routes/channel-verification-routes.ts +49 -1
- package/src/runtime/routes/contact-routes.ts +77 -0
- package/src/runtime/routes/conversation-attention-routes.ts +37 -0
- package/src/runtime/routes/conversation-management-routes.ts +94 -0
- package/src/runtime/routes/conversation-query-routes.ts +78 -0
- package/src/runtime/routes/conversation-routes.ts +115 -38
- package/src/runtime/routes/conversation-starter-routes.ts +29 -0
- package/src/runtime/routes/debug-routes.ts +23 -0
- package/src/runtime/routes/diagnostics-routes.ts +30 -0
- package/src/runtime/routes/documents-routes.ts +42 -0
- package/src/runtime/routes/events-routes.ts +10 -0
- package/src/runtime/routes/global-search-routes.ts +35 -0
- package/src/runtime/routes/guardian-action-routes.ts +47 -2
- package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
- package/src/runtime/routes/heartbeat-routes.ts +278 -0
- package/src/runtime/routes/host-bash-routes.ts +16 -1
- package/src/runtime/routes/host-cu-routes.ts +23 -1
- package/src/runtime/routes/host-file-routes.ts +18 -1
- package/src/runtime/routes/identity-routes.ts +35 -0
- package/src/runtime/routes/inbound-message-handler.ts +46 -25
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
- package/src/runtime/routes/integrations/twilio.ts +32 -22
- package/src/runtime/routes/invite-routes.ts +83 -0
- package/src/runtime/routes/log-export-routes.ts +14 -0
- package/src/runtime/routes/memory-item-routes.ts +99 -1
- package/src/runtime/routes/migration-rollback-routes.ts +25 -0
- package/src/runtime/routes/migration-routes.ts +40 -0
- package/src/runtime/routes/notification-routes.ts +20 -0
- package/src/runtime/routes/oauth-apps.ts +11 -3
- package/src/runtime/routes/pairing-routes.ts +15 -0
- package/src/runtime/routes/recording-routes.ts +72 -0
- package/src/runtime/routes/schedule-routes.ts +77 -5
- package/src/runtime/routes/secret-routes.ts +63 -1
- package/src/runtime/routes/settings-routes.ts +90 -0
- package/src/runtime/routes/skills-routes.ts +98 -16
- package/src/runtime/routes/subagents-routes.ts +38 -3
- package/src/runtime/routes/surface-action-routes.ts +66 -24
- package/src/runtime/routes/surface-content-routes.ts +20 -0
- package/src/runtime/routes/telemetry-routes.ts +12 -0
- package/src/runtime/routes/trace-event-routes.ts +25 -0
- package/src/runtime/routes/trust-rules-routes.ts +46 -0
- package/src/runtime/routes/tts-routes.ts +15 -4
- package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
- package/src/runtime/routes/usage-routes.ts +59 -0
- package/src/runtime/routes/watch-routes.ts +28 -0
- package/src/runtime/routes/work-items-routes.ts +59 -0
- package/src/runtime/routes/workspace-commit-routes.ts +12 -0
- package/src/runtime/routes/workspace-routes.ts +102 -0
- package/src/schedule/scheduler.ts +7 -1
- package/src/security/AGENTS.md +7 -0
- package/src/security/credential-backend.ts +1 -1
- package/src/security/encrypted-store.ts +3 -3
- package/src/security/oauth2.ts +55 -0
- package/src/security/secret-ingress.ts +174 -0
- package/src/security/secret-patterns.ts +133 -0
- package/src/security/secret-scanner.ts +28 -117
- package/src/signals/confirm.ts +12 -8
- package/src/signals/user-message.ts +18 -3
- package/src/skills/skill-memory.ts +1 -2
- package/src/tasks/task-runner.ts +7 -1
- package/src/tools/credentials/broker.ts +1 -1
- package/src/tools/credentials/metadata-store.ts +1 -1
- package/src/tools/credentials/vault.ts +2 -3
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +2 -4
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/tool-manifest.ts +1 -1
- package/src/util/log-redact.ts +9 -34
- package/docs/architecture/keychain-broker.md +0 -68
- package/src/cli/commands/oauth/platform.ts +0 -525
- package/src/config/bundled-skills/slack/TOOLS.json +0 -272
- package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
- package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
- package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
|
@@ -187,7 +187,6 @@ export async function prepareMemoryContext(
|
|
|
187
187
|
}
|
|
188
188
|
: undefined,
|
|
189
189
|
semanticHits: recall.semanticHits,
|
|
190
|
-
recencyHits: 0,
|
|
191
190
|
tier1Count: recall.tier1Count ?? 0,
|
|
192
191
|
tier2Count: recall.tier2Count ?? 0,
|
|
193
192
|
hybridSearchLatencyMs: recall.hybridSearchMs ?? 0,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "../../security/secure-keys.js";
|
|
20
20
|
import {
|
|
21
21
|
deleteCredentialMetadata,
|
|
22
|
+
getCredentialMetadata,
|
|
22
23
|
upsertCredentialMetadata,
|
|
23
24
|
} from "../../tools/credentials/metadata-store.js";
|
|
24
25
|
import { log as _log } from "./shared.js";
|
|
@@ -38,6 +39,39 @@ export interface SlackChannelConfigResult {
|
|
|
38
39
|
warning?: string;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
// -- Helpers --
|
|
43
|
+
|
|
44
|
+
const SLACK_INJECTION_TEMPLATES = [
|
|
45
|
+
{
|
|
46
|
+
hostPattern: "slack.com" as const,
|
|
47
|
+
injectionType: "header" as const,
|
|
48
|
+
headerName: "Authorization",
|
|
49
|
+
valuePrefix: "Bearer ",
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
/** Ensure the bot token credential has injection templates for the proxy. */
|
|
54
|
+
function ensureBotTokenInjectionTemplates(): void {
|
|
55
|
+
upsertCredentialMetadata("slack_channel", "bot_token", {
|
|
56
|
+
allowedDomains: ["slack.com"],
|
|
57
|
+
injectionTemplates: SLACK_INJECTION_TEMPLATES,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Backfill injection templates on the Slack bot token credential.
|
|
63
|
+
* Called on daemon startup so existing credentials get proxy support.
|
|
64
|
+
*/
|
|
65
|
+
export function backfillSlackInjectionTemplates(): void {
|
|
66
|
+
const meta = getCredentialMetadata("slack_channel", "bot_token");
|
|
67
|
+
if (
|
|
68
|
+
meta &&
|
|
69
|
+
(!meta.injectionTemplates || meta.injectionTemplates.length === 0)
|
|
70
|
+
) {
|
|
71
|
+
ensureBotTokenInjectionTemplates();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
41
75
|
// -- Business logic --
|
|
42
76
|
|
|
43
77
|
export async function getSlackChannelConfig(): Promise<SlackChannelConfigResult> {
|
|
@@ -56,6 +90,13 @@ export async function getSlackChannelConfig(): Promise<SlackChannelConfigResult>
|
|
|
56
90
|
const conn = getConnectionByProvider("slack_channel");
|
|
57
91
|
const connected =
|
|
58
92
|
!!(conn && conn.status === "active") && hasBotToken && hasAppToken;
|
|
93
|
+
|
|
94
|
+
// Backfill injection templates for existing credentials that were stored
|
|
95
|
+
// before proxy support was added. Safe to call repeatedly (upsert merges).
|
|
96
|
+
if (hasBotToken) {
|
|
97
|
+
ensureBotTokenInjectionTemplates();
|
|
98
|
+
}
|
|
99
|
+
|
|
59
100
|
return {
|
|
60
101
|
success: true,
|
|
61
102
|
hasBotToken,
|
|
@@ -168,7 +209,7 @@ export async function setSlackChannelConfig(
|
|
|
168
209
|
};
|
|
169
210
|
}
|
|
170
211
|
|
|
171
|
-
|
|
212
|
+
ensureBotTokenInjectionTemplates();
|
|
172
213
|
|
|
173
214
|
const raw = loadRawConfig();
|
|
174
215
|
setNestedValue(raw, "slack.teamId", metadata.teamId ?? "");
|
|
@@ -255,6 +296,7 @@ export async function setSlackChannelConfig(
|
|
|
255
296
|
// Sync oauth_connection record so getConnectionByProvider("slack_channel")
|
|
256
297
|
// reflects the current credential state.
|
|
257
298
|
if (hasBotToken && hasAppToken) {
|
|
299
|
+
ensureBotTokenInjectionTemplates();
|
|
258
300
|
const accountInfo = metadata.teamName
|
|
259
301
|
? `${metadata.teamName}${metadata.botUsername ? ` (@${metadata.botUsername})` : ""}`
|
|
260
302
|
: undefined;
|
|
@@ -59,41 +59,49 @@ export function makeEventSender(params: {
|
|
|
59
59
|
|
|
60
60
|
return (event: ServerMessage) => {
|
|
61
61
|
if (event.type === "confirmation_request") {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
allowlistOptions: event.allowlistOptions,
|
|
72
|
-
scopeOptions: event.scopeOptions,
|
|
73
|
-
persistentDecisionsAllowed: event.persistentDecisionsAllowed,
|
|
74
|
-
temporaryOptionsAvailable: event.temporaryOptionsAvailable,
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
const trustContext = conversation.trustContext;
|
|
80
|
-
createCanonicalGuardianRequest({
|
|
81
|
-
id: event.requestId,
|
|
82
|
-
kind: "tool_approval",
|
|
83
|
-
sourceType: "desktop",
|
|
84
|
-
sourceChannel,
|
|
62
|
+
// ACP permission requests are handled by client-handler.ts — skip
|
|
63
|
+
// the normal registration and guardian request creation for them.
|
|
64
|
+
// The ACP handler registers its own entry with directResolve after
|
|
65
|
+
// this callback returns.
|
|
66
|
+
const isAcpPermission = "acpToolKind" in event && !!event.acpToolKind;
|
|
67
|
+
|
|
68
|
+
if (!isAcpPermission) {
|
|
69
|
+
pendingInteractions.register(event.requestId, {
|
|
70
|
+
conversation,
|
|
85
71
|
conversationId,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
72
|
+
kind: "confirmation",
|
|
73
|
+
confirmationDetails: {
|
|
74
|
+
toolName: event.toolName,
|
|
75
|
+
input: event.input,
|
|
76
|
+
riskLevel: event.riskLevel,
|
|
77
|
+
executionTarget: event.executionTarget,
|
|
78
|
+
allowlistOptions: event.allowlistOptions,
|
|
79
|
+
scopeOptions: event.scopeOptions,
|
|
80
|
+
persistentDecisionsAllowed: event.persistentDecisionsAllowed,
|
|
81
|
+
temporaryOptionsAvailable: event.temporaryOptionsAvailable,
|
|
82
|
+
},
|
|
91
83
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const trustContext = conversation.trustContext;
|
|
87
|
+
createCanonicalGuardianRequest({
|
|
88
|
+
id: event.requestId,
|
|
89
|
+
kind: "tool_approval",
|
|
90
|
+
sourceType: "desktop",
|
|
91
|
+
sourceChannel,
|
|
92
|
+
conversationId,
|
|
93
|
+
guardianPrincipalId: trustContext?.guardianPrincipalId ?? undefined,
|
|
94
|
+
toolName: event.toolName,
|
|
95
|
+
status: "pending",
|
|
96
|
+
requestCode: generateCanonicalRequestCode(),
|
|
97
|
+
expiresAt: Date.now() + 5 * 60 * 1000,
|
|
98
|
+
});
|
|
99
|
+
} catch (err) {
|
|
100
|
+
log.debug(
|
|
101
|
+
{ err, requestId: event.requestId, conversationId },
|
|
102
|
+
"Failed to create canonical request from local confirmation event",
|
|
103
|
+
);
|
|
104
|
+
}
|
|
97
105
|
}
|
|
98
106
|
} else if (event.type === "secret_request") {
|
|
99
107
|
pendingInteractions.register(event.requestId, {
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -109,6 +109,7 @@ import {
|
|
|
109
109
|
createGuardianActionCopyGenerator,
|
|
110
110
|
createGuardianFollowUpConversationGenerator,
|
|
111
111
|
} from "./guardian-action-generators.js";
|
|
112
|
+
import { backfillSlackInjectionTemplates } from "./handlers/config-slack-channel.js";
|
|
112
113
|
import {
|
|
113
114
|
cancelGeneration,
|
|
114
115
|
clearAllConversations,
|
|
@@ -282,7 +283,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
282
283
|
log.info("Daemon startup: workspace migrations complete");
|
|
283
284
|
|
|
284
285
|
// Backfill oauth_connection rows for manual-token providers (Telegram,
|
|
285
|
-
// Slack channel) that already have
|
|
286
|
+
// Slack channel) that already have stored credentials from before the
|
|
286
287
|
// oauth_connection migration. Safe to call on every startup.
|
|
287
288
|
//
|
|
288
289
|
// Must run AFTER workspace migrations.
|
|
@@ -297,6 +298,17 @@ export async function runDaemon(): Promise<void> {
|
|
|
297
298
|
);
|
|
298
299
|
}
|
|
299
300
|
|
|
301
|
+
// Backfill injection templates on Slack bot token credentials so the
|
|
302
|
+
// credential proxy can inject Authorization headers. Safe on every startup.
|
|
303
|
+
try {
|
|
304
|
+
backfillSlackInjectionTemplates();
|
|
305
|
+
} catch (err) {
|
|
306
|
+
log.warn(
|
|
307
|
+
{ err },
|
|
308
|
+
"Slack injection template backfill failed — continuing startup",
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
300
312
|
// Now that workspace migrations have run (including 003-seed-device-id
|
|
301
313
|
// which may copy the legacy installationId into device.json), it is safe
|
|
302
314
|
// to read the device ID and set the Sentry tag.
|
|
@@ -839,6 +851,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
839
851
|
getHandlerContext: () => server.getHandlerContext(),
|
|
840
852
|
}),
|
|
841
853
|
getCesClient: () => server.getCesClient(),
|
|
854
|
+
getHeartbeatService: () => server.getHeartbeatService(),
|
|
842
855
|
});
|
|
843
856
|
|
|
844
857
|
// Inject voice bridge deps BEFORE attempting to start the HTTP server.
|
|
@@ -1092,8 +1105,19 @@ export async function runDaemon(): Promise<void> {
|
|
|
1092
1105
|
const heartbeatConfig = config.heartbeat;
|
|
1093
1106
|
const heartbeat = new HeartbeatService({
|
|
1094
1107
|
processMessage: (conversationId, content) =>
|
|
1095
|
-
server.processMessage(conversationId, content
|
|
1108
|
+
server.processMessage(conversationId, content, undefined, {
|
|
1109
|
+
trustContext: {
|
|
1110
|
+
sourceChannel: "vellum",
|
|
1111
|
+
trustClass: "guardian",
|
|
1112
|
+
},
|
|
1113
|
+
}),
|
|
1096
1114
|
alerter: (alert) => server.broadcast(alert),
|
|
1115
|
+
onConversationCreated: (info) =>
|
|
1116
|
+
server.broadcast({
|
|
1117
|
+
type: "heartbeat_conversation_created",
|
|
1118
|
+
conversationId: info.conversationId,
|
|
1119
|
+
title: info.title,
|
|
1120
|
+
}),
|
|
1097
1121
|
});
|
|
1098
1122
|
heartbeat.start();
|
|
1099
1123
|
server.setHeartbeatService(heartbeat);
|
|
@@ -25,20 +25,6 @@ export interface AcpSessionUpdate {
|
|
|
25
25
|
toolStatus?: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export interface AcpPermissionRequest {
|
|
29
|
-
type: "acp_permission_request";
|
|
30
|
-
acpSessionId: string;
|
|
31
|
-
requestId: string;
|
|
32
|
-
toolTitle: string;
|
|
33
|
-
toolKind: string;
|
|
34
|
-
rawInput?: unknown;
|
|
35
|
-
options: Array<{
|
|
36
|
-
optionId: string;
|
|
37
|
-
name: string;
|
|
38
|
-
kind: "allow_once" | "allow_always" | "reject_once" | "reject_always";
|
|
39
|
-
}>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
28
|
export interface AcpSessionCompleted {
|
|
43
29
|
type: "acp_session_completed";
|
|
44
30
|
acpSessionId: string;
|
|
@@ -61,6 +47,5 @@ export interface AcpSessionError {
|
|
|
61
47
|
export type _AcpServerMessages =
|
|
62
48
|
| AcpSessionSpawned
|
|
63
49
|
| AcpSessionUpdate
|
|
64
|
-
| AcpPermissionRequest
|
|
65
50
|
| AcpSessionCompleted
|
|
66
51
|
| AcpSessionError;
|
|
@@ -44,7 +44,7 @@ export interface SecretResponse {
|
|
|
44
44
|
type: "secret_response";
|
|
45
45
|
requestId: string;
|
|
46
46
|
value?: string; // undefined = user cancelled
|
|
47
|
-
/** How the secret should be delivered: 'store' persists to
|
|
47
|
+
/** How the secret should be delivered: 'store' persists to credential store (default), 'transient_send' for one-time use without persisting. */
|
|
48
48
|
delivery?: "store" | "transient_send";
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -156,6 +156,14 @@ export interface ConfirmationRequest {
|
|
|
156
156
|
temporaryOptionsAvailable?: Array<"allow_10m" | "allow_conversation">;
|
|
157
157
|
/** The tool_use block ID for client-side correlation with specific tool calls. */
|
|
158
158
|
toolUseId?: string;
|
|
159
|
+
/** ACP tool kind from the agent (e.g. "read", "edit", "execute"). Present only for ACP permission requests. */
|
|
160
|
+
acpToolKind?: string;
|
|
161
|
+
/** ACP permission options from the agent. Present only for ACP permission requests. Clients should use these to render the correct buttons. */
|
|
162
|
+
acpOptions?: Array<{
|
|
163
|
+
optionId: string;
|
|
164
|
+
name: string;
|
|
165
|
+
kind: "allow_once" | "allow_always" | "reject_once" | "reject_always";
|
|
166
|
+
}>;
|
|
159
167
|
}
|
|
160
168
|
|
|
161
169
|
export interface SecretRequest {
|
|
@@ -84,6 +84,13 @@ export interface HeartbeatAlert {
|
|
|
84
84
|
body: string;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/** Server push — broadcast when a heartbeat creates a conversation. */
|
|
88
|
+
export interface HeartbeatConversationCreated {
|
|
89
|
+
type: "heartbeat_conversation_created";
|
|
90
|
+
conversationId: string;
|
|
91
|
+
title: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
export interface HeartbeatConfigResponse {
|
|
88
95
|
type: "heartbeat_config_response";
|
|
89
96
|
enabled: boolean;
|
|
@@ -91,6 +98,7 @@ export interface HeartbeatConfigResponse {
|
|
|
91
98
|
activeHoursStart: number | null;
|
|
92
99
|
activeHoursEnd: number | null;
|
|
93
100
|
nextRunAt: number | null;
|
|
101
|
+
lastRunAt: number | null;
|
|
94
102
|
success: boolean;
|
|
95
103
|
error?: string;
|
|
96
104
|
}
|
|
@@ -141,6 +149,7 @@ export type _SchedulesClientMessages =
|
|
|
141
149
|
export type _SchedulesServerMessages =
|
|
142
150
|
| SchedulesListResponse
|
|
143
151
|
| HeartbeatAlert
|
|
152
|
+
| HeartbeatConversationCreated
|
|
144
153
|
| HeartbeatConfigResponse
|
|
145
154
|
| HeartbeatRunsListResponse
|
|
146
155
|
| HeartbeatRunNowResponse
|
package/src/daemon/server.ts
CHANGED
|
@@ -40,16 +40,14 @@ import { updateMetaFile } from "../memory/conversation-disk-view.js";
|
|
|
40
40
|
import { getOrCreateConversation } from "../memory/conversation-key-store.js";
|
|
41
41
|
import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
42
42
|
import { RateLimitProvider } from "../providers/ratelimit.js";
|
|
43
|
-
import {
|
|
44
|
-
getProvider,
|
|
45
|
-
initializeProviders,
|
|
46
|
-
} from "../providers/registry.js";
|
|
43
|
+
import { getProvider, initializeProviders } from "../providers/registry.js";
|
|
47
44
|
import { buildAssistantEvent } from "../runtime/assistant-event.js";
|
|
48
45
|
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
49
46
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
50
47
|
import { getSigningKeyFingerprint } from "../runtime/auth/token-service.js";
|
|
51
48
|
import { bridgeConfirmationRequestToGuardian } from "../runtime/confirmation-request-guardian-bridge.js";
|
|
52
49
|
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
50
|
+
import { checkIngressForSecrets } from "../security/secret-ingress.js";
|
|
53
51
|
import { registerCancelCallback } from "../signals/cancel.js";
|
|
54
52
|
import { registerConversationUndoCallback } from "../signals/conversation-undo.js";
|
|
55
53
|
import { appendEventToStream } from "../signals/event-stream.js";
|
|
@@ -304,6 +302,10 @@ export class DaemonServer {
|
|
|
304
302
|
this._heartbeatService = service;
|
|
305
303
|
}
|
|
306
304
|
|
|
305
|
+
getHeartbeatService(): HeartbeatService | undefined {
|
|
306
|
+
return this._heartbeatService;
|
|
307
|
+
}
|
|
308
|
+
|
|
307
309
|
private deriveMemoryPolicy(conversationId: string): ConversationMemoryPolicy {
|
|
308
310
|
const conversationType = getConversationType(conversationId);
|
|
309
311
|
if (conversationType === "private") {
|
|
@@ -508,6 +510,18 @@ export class DaemonServer {
|
|
|
508
510
|
);
|
|
509
511
|
|
|
510
512
|
registerUserMessageCallback(async (params) => {
|
|
513
|
+
// Block messages containing known-format secrets before persistence
|
|
514
|
+
if (!params.bypassSecretCheck) {
|
|
515
|
+
const ingressResult = checkIngressForSecrets(params.content);
|
|
516
|
+
if (ingressResult.blocked) {
|
|
517
|
+
return {
|
|
518
|
+
accepted: false,
|
|
519
|
+
error: "secret_blocked" as const,
|
|
520
|
+
message: ingressResult.userNotice,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
511
525
|
const { conversationId } = getOrCreateConversation(
|
|
512
526
|
params.conversationKey,
|
|
513
527
|
);
|
|
@@ -702,9 +716,7 @@ export class DaemonServer {
|
|
|
702
716
|
|
|
703
717
|
const createPromise = (async () => {
|
|
704
718
|
const config = getConfig();
|
|
705
|
-
let provider = getProvider(
|
|
706
|
-
config.services.inference.provider,
|
|
707
|
-
);
|
|
719
|
+
let provider = getProvider(config.services.inference.provider);
|
|
708
720
|
const { rateLimit } = config;
|
|
709
721
|
if (rateLimit.maxRequestsPerMinute > 0) {
|
|
710
722
|
provider = new RateLimitProvider(
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
* Delegates to the unified feature-flag resolver so that config overrides
|
|
6
6
|
* and registry defaults are respected uniformly.
|
|
7
7
|
*
|
|
8
|
-
* The flag key
|
|
9
|
-
*
|
|
8
|
+
* The flag key uses simple kebab-case format and is declared in
|
|
9
|
+
* `meta/feature-flags/feature-flag-registry.json`.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
13
13
|
import type { AssistantConfig } from "../config/schema.js";
|
|
14
14
|
|
|
15
15
|
/** Gate for the entire email integration. */
|
|
16
|
-
export const EMAIL_FLAG_KEY = "
|
|
16
|
+
export const EMAIL_FLAG_KEY = "email-channel" as const;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Whether the email integration is enabled.
|
|
@@ -17,6 +17,10 @@ export interface HeartbeatDeps {
|
|
|
17
17
|
content: string,
|
|
18
18
|
) => Promise<{ messageId: string }>;
|
|
19
19
|
alerter: (alert: HeartbeatAlert) => void;
|
|
20
|
+
onConversationCreated?: (info: {
|
|
21
|
+
conversationId: string;
|
|
22
|
+
title: string;
|
|
23
|
+
}) => void;
|
|
20
24
|
/** Override for current hour (0-23), for testing. */
|
|
21
25
|
getCurrentHour?: () => number;
|
|
22
26
|
}
|
|
@@ -25,20 +29,34 @@ export class HeartbeatService {
|
|
|
25
29
|
private readonly deps: HeartbeatDeps;
|
|
26
30
|
private timer: ReturnType<typeof setInterval> | null = null;
|
|
27
31
|
private activeRun: Promise<void> | null = null;
|
|
32
|
+
private _lastRunAt: number | null = null;
|
|
33
|
+
private _nextRunAt: number | null = null;
|
|
28
34
|
|
|
29
35
|
constructor(deps: HeartbeatDeps) {
|
|
30
36
|
this.deps = deps;
|
|
31
37
|
}
|
|
32
38
|
|
|
39
|
+
/** Epoch-ms timestamp of the last completed heartbeat run. */
|
|
40
|
+
get lastRunAt(): number | null {
|
|
41
|
+
return this._lastRunAt;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Epoch-ms timestamp of the next scheduled heartbeat run. */
|
|
45
|
+
get nextRunAt(): number | null {
|
|
46
|
+
return this._nextRunAt;
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
start(): void {
|
|
34
50
|
const config = getConfig().heartbeat;
|
|
35
51
|
if (!config.enabled) {
|
|
36
52
|
log.info("Heartbeat disabled by config");
|
|
53
|
+
this._nextRunAt = null;
|
|
37
54
|
return;
|
|
38
55
|
}
|
|
39
56
|
if (this.timer) return;
|
|
40
57
|
|
|
41
58
|
log.info({ intervalMs: config.intervalMs }, "Heartbeat service started");
|
|
59
|
+
this.scheduleNextRun(config.intervalMs);
|
|
42
60
|
this.timer = setInterval(() => {
|
|
43
61
|
this.runOnce().catch((err) => {
|
|
44
62
|
log.error({ err }, "Heartbeat runOnce failed");
|
|
@@ -52,14 +70,33 @@ export class HeartbeatService {
|
|
|
52
70
|
clearInterval(this.timer);
|
|
53
71
|
this.timer = null;
|
|
54
72
|
}
|
|
73
|
+
this._nextRunAt = null;
|
|
55
74
|
this.start();
|
|
56
75
|
}
|
|
57
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Reset the heartbeat timer so the next run is a full interval from now.
|
|
79
|
+
* Called when the guardian sends a message — no need for a heartbeat shortly
|
|
80
|
+
* after an active conversation.
|
|
81
|
+
*/
|
|
82
|
+
resetTimer(): void {
|
|
83
|
+
if (!this.timer) return;
|
|
84
|
+
const config = getConfig().heartbeat;
|
|
85
|
+
clearInterval(this.timer);
|
|
86
|
+
this.scheduleNextRun(config.intervalMs);
|
|
87
|
+
this.timer = setInterval(() => {
|
|
88
|
+
this.runOnce().catch((err) => {
|
|
89
|
+
log.error({ err }, "Heartbeat runOnce failed");
|
|
90
|
+
});
|
|
91
|
+
}, config.intervalMs);
|
|
92
|
+
}
|
|
93
|
+
|
|
58
94
|
async stop(): Promise<void> {
|
|
59
95
|
if (this.timer) {
|
|
60
96
|
clearInterval(this.timer);
|
|
61
97
|
this.timer = null;
|
|
62
98
|
}
|
|
99
|
+
this._nextRunAt = null;
|
|
63
100
|
if (this.activeRun) {
|
|
64
101
|
let timerId: ReturnType<typeof setTimeout>;
|
|
65
102
|
const timeout = new Promise<void>((resolve) => {
|
|
@@ -116,10 +153,16 @@ export class HeartbeatService {
|
|
|
116
153
|
await run;
|
|
117
154
|
} finally {
|
|
118
155
|
this.activeRun = null;
|
|
156
|
+
this._lastRunAt = Date.now();
|
|
157
|
+
this.scheduleNextRun(getConfig().heartbeat.intervalMs);
|
|
119
158
|
}
|
|
120
159
|
return true;
|
|
121
160
|
}
|
|
122
161
|
|
|
162
|
+
private scheduleNextRun(intervalMs: number): void {
|
|
163
|
+
this._nextRunAt = Date.now() + intervalMs;
|
|
164
|
+
}
|
|
165
|
+
|
|
123
166
|
private async executeRun(): Promise<void> {
|
|
124
167
|
log.info("Running heartbeat");
|
|
125
168
|
|
|
@@ -134,6 +177,11 @@ export class HeartbeatService {
|
|
|
134
177
|
systemHint: "Heartbeat",
|
|
135
178
|
});
|
|
136
179
|
|
|
180
|
+
this.deps.onConversationCreated?.({
|
|
181
|
+
conversationId: conversation.id,
|
|
182
|
+
title: "Heartbeat",
|
|
183
|
+
});
|
|
184
|
+
|
|
137
185
|
await this.deps.processMessage(conversation.id, prompt);
|
|
138
186
|
log.info({ conversationId: conversation.id }, "Heartbeat completed");
|
|
139
187
|
} catch (err) {
|
|
@@ -24,10 +24,22 @@ import {
|
|
|
24
24
|
getPlatformInternalApiKey,
|
|
25
25
|
} from "../config/env.js";
|
|
26
26
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
27
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
28
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
27
29
|
import { getLogger } from "../util/logger.js";
|
|
28
30
|
|
|
29
31
|
const log = getLogger("platform-callback-registration");
|
|
30
32
|
|
|
33
|
+
export interface PlatformCallbackRegistrationContext {
|
|
34
|
+
containerized: boolean;
|
|
35
|
+
platformBaseUrl: string;
|
|
36
|
+
assistantId: string;
|
|
37
|
+
hasInternalApiKey: boolean;
|
|
38
|
+
hasAssistantApiKey: boolean;
|
|
39
|
+
authHeader: string | null;
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
/**
|
|
32
44
|
* Whether the daemon should register callback routes with the platform.
|
|
33
45
|
* True when IS_CONTAINERIZED, VELLUM_PLATFORM_URL, and PLATFORM_ASSISTANT_ID
|
|
@@ -43,6 +55,45 @@ export function shouldUsePlatformCallbacks(): boolean {
|
|
|
43
55
|
);
|
|
44
56
|
}
|
|
45
57
|
|
|
58
|
+
export async function resolvePlatformCallbackRegistrationContext(): Promise<PlatformCallbackRegistrationContext> {
|
|
59
|
+
const containerized = getIsContainerized();
|
|
60
|
+
const [storedBaseUrlRaw, storedAssistantIdRaw, storedAssistantApiKeyRaw] =
|
|
61
|
+
await Promise.all([
|
|
62
|
+
getSecureKeyAsync(credentialKey("vellum", "platform_base_url")),
|
|
63
|
+
getSecureKeyAsync(credentialKey("vellum", "platform_assistant_id")),
|
|
64
|
+
getSecureKeyAsync(credentialKey("vellum", "assistant_api_key")),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
const storedBaseUrl = storedBaseUrlRaw?.trim();
|
|
68
|
+
const platformBaseUrl = (storedBaseUrl || getPlatformBaseUrl()).replace(
|
|
69
|
+
/\/+$/,
|
|
70
|
+
"",
|
|
71
|
+
);
|
|
72
|
+
const assistantId =
|
|
73
|
+
getPlatformAssistantId().trim() || storedAssistantIdRaw?.trim() || "";
|
|
74
|
+
const internalApiKey = getPlatformInternalApiKey().trim();
|
|
75
|
+
const assistantApiKey = storedAssistantApiKeyRaw?.trim() || "";
|
|
76
|
+
const authHeader = internalApiKey
|
|
77
|
+
? `Bearer ${internalApiKey}`
|
|
78
|
+
: assistantApiKey
|
|
79
|
+
? `Api-Key ${assistantApiKey}`
|
|
80
|
+
: null;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
containerized,
|
|
84
|
+
platformBaseUrl,
|
|
85
|
+
assistantId,
|
|
86
|
+
hasInternalApiKey: internalApiKey.length > 0,
|
|
87
|
+
hasAssistantApiKey: assistantApiKey.length > 0,
|
|
88
|
+
authHeader,
|
|
89
|
+
enabled:
|
|
90
|
+
containerized &&
|
|
91
|
+
platformBaseUrl.length > 0 &&
|
|
92
|
+
assistantId.length > 0 &&
|
|
93
|
+
authHeader !== null,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
46
97
|
interface RegisterCallbackRouteResponse {
|
|
47
98
|
callback_url: string;
|
|
48
99
|
callback_path: string;
|
|
@@ -65,20 +116,23 @@ export async function registerCallbackRoute(
|
|
|
65
116
|
callbackPath: string,
|
|
66
117
|
type: string,
|
|
67
118
|
): Promise<string> {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
119
|
+
const context = await resolvePlatformCallbackRegistrationContext();
|
|
120
|
+
if (!context.enabled || !context.authHeader) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
"Platform callbacks not available — missing containerized platform registration context",
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const platformBaseUrl = context.platformBaseUrl;
|
|
127
|
+
const assistantId = context.assistantId;
|
|
71
128
|
|
|
72
129
|
const url = `${platformBaseUrl}/v1/internal/gateway/callback-routes/register/`;
|
|
73
130
|
|
|
74
131
|
const headers: Record<string, string> = {
|
|
75
132
|
"Content-Type": "application/json",
|
|
133
|
+
Authorization: context.authHeader,
|
|
76
134
|
};
|
|
77
135
|
|
|
78
|
-
if (apiKey) {
|
|
79
|
-
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
136
|
const body = JSON.stringify({
|
|
83
137
|
assistant_id: assistantId,
|
|
84
138
|
callback_path: callbackPath,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OAuthClientProvider implementation for MCP servers.
|
|
3
3
|
*
|
|
4
|
-
* Uses secure-keys (
|
|
5
|
-
*
|
|
4
|
+
* Uses secure-keys (credential store) for persistent credential storage
|
|
5
|
+
* and a loopback HTTP server for the browser callback.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createServer, type Server } from "node:http";
|
|
@@ -30,7 +30,7 @@ const log = getLogger("mcp-oauth");
|
|
|
30
30
|
const CALLBACK_PATH = "/oauth/callback";
|
|
31
31
|
const CALLBACK_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Credential store key helpers
|
|
34
34
|
function tokensKey(serverId: string): string {
|
|
35
35
|
return `mcp:${serverId}:tokens`;
|
|
36
36
|
}
|
package/src/memory/app-store.ts
CHANGED
|
@@ -75,10 +75,10 @@ export function inlineDistAssets(appDir: string, html: string): string {
|
|
|
75
75
|
// Inline main.js
|
|
76
76
|
const jsPath = join(distDir, "main.js");
|
|
77
77
|
if (existsSync(jsPath)) {
|
|
78
|
-
const js = readFileSync(jsPath, "utf-8");
|
|
78
|
+
const js = readFileSync(jsPath, "utf-8").replace(/<\/script>/g, "<\\/script>");
|
|
79
79
|
html = html.replace(
|
|
80
80
|
/<script\s+type="module"\s+src="main\.js"\s*><\/script>/,
|
|
81
|
-
`<script type="module">${js}</script>`,
|
|
81
|
+
() => `<script type="module">${js}</script>`,
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -88,7 +88,7 @@ export function inlineDistAssets(appDir: string, html: string): string {
|
|
|
88
88
|
const css = readFileSync(cssPath, "utf-8");
|
|
89
89
|
html = html.replace(
|
|
90
90
|
/<link\s+rel="stylesheet"\s+href="main\.css"\s*>/,
|
|
91
|
-
`<style>${css}</style>`,
|
|
91
|
+
() => `<style>${css}</style>`,
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
94
|
|