Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assistant/src/config/bundled-skills/slack/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: slack
description: Read, send, and manage Slack messages via the Web API
compatibility: "Designed for Vellum personal assistants"
compatibility: "Works with Vellum personal and platform-hosted assistants"
metadata:
emoji: "💬"
vellum:
Expand Down
18 changes: 5 additions & 13 deletions assistant/src/daemon/handlers/config-slack-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getSecureKeyAsync,
setSecureKeyAsync,
} from "../../security/secure-keys.js";
import { getInjectionRegistryEntry } from "../../tools/credentials/injection-registry.js";
import {
deleteCredentialMetadata,
getCredentialMetadata,
Expand All @@ -41,21 +42,12 @@ export interface SlackChannelConfigResult {

// -- Helpers --

const SLACK_INJECTION_TEMPLATES = [
{
hostPattern: "slack.com" as const,
injectionType: "header" as const,
headerName: "Authorization",
valuePrefix: "Bearer ",
},
];

/** Ensure the bot token credential has injection templates for the proxy. */
function ensureBotTokenInjectionTemplates(): void {
upsertCredentialMetadata("slack_channel", "bot_token", {
allowedDomains: ["slack.com"],
injectionTemplates: SLACK_INJECTION_TEMPLATES,
});
const entry = getInjectionRegistryEntry("slack_channel", "bot_token");
if (entry) {
upsertCredentialMetadata("slack_channel", "bot_token", entry);
}
}

/**
Expand Down
4 changes: 3 additions & 1 deletion assistant/src/runtime/routes/secret-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
listSecureKeysAsync,
setSecureKeyAsync,
} from "../../security/secure-keys.js";
import { getInjectionRegistryEntry } from "../../tools/credentials/injection-registry.js";
import {
assertMetadataWritable,
deleteCredentialMetadata,
Expand Down Expand Up @@ -259,7 +260,8 @@ export async function handleAddSecret(
500,
);
}
upsertCredentialMetadata(service, field, {});
const registryEntry = getInjectionRegistryEntry(service, field);
upsertCredentialMetadata(service, field, registryEntry ?? {});
await syncManualTokenConnection(service);
if (service === "vellum" && field === "platform_base_url") {
setPlatformBaseUrl(effectiveValue);
Expand Down
71 changes: 71 additions & 0 deletions assistant/src/tools/credentials/injection-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Credential injection template registry.
*
* Maps well-known credential service/field pairs to their default injection
* templates and allowed domains. When a credential is stored (via the daemon
* config handler *or* via the HTTP secret route), the registry is consulted
* so that the proxy injection metadata is written regardless of which code
* path provisioned the credential.
*
* To add a new credential type with proxy injection support, add an entry
* to {@link INJECTION_REGISTRY} below.
*/

import type { CredentialInjectionTemplate } from "./policy-types.js";

// ---------------------------------------------------------------------------
// Registry entry type
// ---------------------------------------------------------------------------

export interface InjectionRegistryEntry {
/** Domains the credential is scoped to. */
allowedDomains: string[];
/** Templates describing how to inject the credential into proxied requests. */
injectionTemplates: CredentialInjectionTemplate[];
}

// ---------------------------------------------------------------------------
// Registry
// ---------------------------------------------------------------------------

/**
* Static registry of well-known credential types that require proxy injection.
*
* Key format: `{service}/{field}` (matches the credential ref format used by
* skill definitions, e.g. `credential_ids: ["slack_channel/bot_token"]`).
*/
const INJECTION_REGISTRY: ReadonlyMap<string, InjectionRegistryEntry> = new Map(
[
[
"slack_channel/bot_token",
{
allowedDomains: ["slack.com"],
injectionTemplates: [
{
hostPattern: "slack.com",
injectionType: "header" as const,
headerName: "Authorization",
valuePrefix: "Bearer ",
},
],
},
],
],
);

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

/**
* Look up the default injection metadata for a credential type.
*
* @returns The registry entry if a well-known injection configuration exists,
* or `undefined` if the credential type has no registered injection.
*/
export function getInjectionRegistryEntry(
service: string,
field: string,
): InjectionRegistryEntry | undefined {
return INJECTION_REGISTRY.get(`${service}/${field}`);
}
Loading