refactor(v2): remove trigger_credential_collection MCP tool
Drops the in-chat credential-collection flow introduced in e92b245. Agents
can no longer collect API keys via a secure modal — users must add secrets
through OneCLI directly. Keeps the OneCLI manual-approval handler and
threaded-routing work from the same commit intact.
Removed:
* container/agent-runner/src/mcp-tools/credentials.ts (MCP tool)
* src/credentials.ts (host-side modal/OneCLI pipeline)
* src/db/credentials.ts + migration 005 (pending_credentials table)
* src/onecli-secrets.ts (createSecret CLI facade, only caller was credentials.ts)
* findCredentialResponse from agent-runner DB layer
* PendingCredential types
* Four credential hooks from ChannelSetup (getCredentialForModal,
onCredentialReject, onCredentialSubmit, onCredentialChannelUnsupported)
* Credential card/modal handling in chat-sdk-bridge (nccr/nccm prefixes,
Modal/TextInput imports)
* credential_request text fallback in WhatsApp adapter
* request_credential system-action case in delivery.ts
Added:
* Migration 009 drops pending_credentials on existing installs.
Vercel skill now tells the agent to ask the user to register the token via
OneCLI instead of invoking the removed tool.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,8 +12,6 @@ import {
|
||||
CardText,
|
||||
Actions,
|
||||
Button,
|
||||
Modal,
|
||||
TextInput,
|
||||
type Adapter,
|
||||
type ConcurrencyStrategy,
|
||||
type Message as ChatMessage,
|
||||
@@ -191,72 +189,8 @@ export function createChatSdkBridge(config: ChatSdkBridgeConfig): ChannelAdapter
|
||||
await thread.subscribe();
|
||||
});
|
||||
|
||||
// Handle button clicks (ask_user_question, credential card)
|
||||
// Handle button clicks (ask_user_question)
|
||||
chat.onAction(async (event) => {
|
||||
// Credential card actions: nccr:<credentialId>:<enter|reject>
|
||||
if (event.actionId.startsWith('nccr:')) {
|
||||
const [, credentialId, subAction] = event.actionId.split(':');
|
||||
if (!credentialId || !subAction) return;
|
||||
|
||||
if (subAction === 'reject') {
|
||||
try {
|
||||
await adapter.editMessage(event.threadId, event.messageId, {
|
||||
markdown: `🔑 Credential request\n\n❌ Rejected`,
|
||||
});
|
||||
} catch (err) {
|
||||
log.warn('Failed to update credential card after reject', { err });
|
||||
}
|
||||
setupConfig.onCredentialReject?.(credentialId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subAction === 'enter') {
|
||||
const pending = setupConfig.getCredentialForModal?.(credentialId);
|
||||
if (!pending) {
|
||||
log.warn('Credential card clicked but row not pending', { credentialId });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const modalChildren = [
|
||||
CardText(pending.description ?? `Enter the value for ${pending.name} (host: ${pending.hostPattern}).`),
|
||||
TextInput({
|
||||
id: 'value',
|
||||
label: pending.name,
|
||||
placeholder: 'Paste your credential value',
|
||||
}),
|
||||
];
|
||||
// Modal children include a text element for context; the SDK
|
||||
// accepts TextElement in ModalChild so this is valid.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const modal = Modal({
|
||||
callbackId: `nccm:${credentialId}`,
|
||||
title: 'Enter credential',
|
||||
submitLabel: 'Save',
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
children: modalChildren as any,
|
||||
});
|
||||
const result = await event.openModal(modal);
|
||||
if (!result) {
|
||||
log.warn('openModal returned undefined — channel unsupported', { credentialId });
|
||||
setupConfig.onCredentialChannelUnsupported?.(credentialId);
|
||||
try {
|
||||
await adapter.editMessage(event.threadId, event.messageId, {
|
||||
markdown: `🔑 Credential request\n\n⚠️ This channel does not support modals.`,
|
||||
});
|
||||
} catch {
|
||||
// best effort
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
log.error('Failed to open credential modal', { credentialId, err });
|
||||
setupConfig.onCredentialChannelUnsupported?.(credentialId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event.actionId.startsWith('ncq:')) return;
|
||||
const parts = event.actionId.split(':');
|
||||
if (parts.length < 3) return;
|
||||
@@ -283,18 +217,6 @@ export function createChatSdkBridge(config: ChatSdkBridgeConfig): ChannelAdapter
|
||||
setupConfig.onAction(questionId, selectedOption, userId);
|
||||
});
|
||||
|
||||
// Modal submissions for credential collection
|
||||
chat.onModalSubmit(async (event) => {
|
||||
if (!event.callbackId.startsWith('nccm:')) return;
|
||||
const credentialId = event.callbackId.slice('nccm:'.length);
|
||||
const value = event.values?.value ?? '';
|
||||
if (!value) {
|
||||
log.warn('Credential modal submitted with empty value', { credentialId });
|
||||
return;
|
||||
}
|
||||
setupConfig.onCredentialSubmit?.(credentialId, value);
|
||||
});
|
||||
|
||||
await chat.initialize();
|
||||
|
||||
// Start Gateway listener for adapters that support it (e.g., Discord)
|
||||
@@ -394,26 +316,6 @@ export function createChatSdkBridge(config: ChatSdkBridgeConfig): ChannelAdapter
|
||||
return result?.id;
|
||||
}
|
||||
|
||||
// Credential request card — buttons open a modal for secure input
|
||||
if (content.type === 'credential_request' && content.credentialId) {
|
||||
const credentialId = content.credentialId as string;
|
||||
const card = Card({
|
||||
title: '🔑 Credential request',
|
||||
children: [
|
||||
CardText(content.question as string),
|
||||
Actions([
|
||||
Button({ id: `nccr:${credentialId}:enter`, label: 'Enter credential', value: 'enter' }),
|
||||
Button({ id: `nccr:${credentialId}:reject`, label: 'Reject', value: 'reject' }),
|
||||
]),
|
||||
],
|
||||
});
|
||||
const result = await adapter.postMessage(tid, {
|
||||
card,
|
||||
fallbackText: `Credential request — open in a channel that supports modals.`,
|
||||
});
|
||||
return result?.id;
|
||||
}
|
||||
|
||||
// Normal message
|
||||
const rawText = (content.markdown as string) || (content.text as string);
|
||||
const text = rawText ? transformText(rawText) : rawText;
|
||||
|
||||
Reference in New Issue
Block a user