fix(extensions): split shared runtime type seams

This commit is contained in:
Vincent Koc
2026-04-10 07:15:18 +01:00
parent d752ff7191
commit 4a275cf6b1
12 changed files with 82 additions and 79 deletions

View File

@@ -1,21 +1,19 @@
import { resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
import { type OpenClawConfig, type RuntimeEnv } from "../runtime-api.js";
import type { MSTeamsConversationStore } from "./conversation-store.js";
import { formatUnknownError } from "./errors.js";
import { buildFeedbackEvent, runFeedbackReflection } from "./feedback-reflection.js";
import { buildFileInfoCard, parseFileConsentInvoke, uploadToConsentUrl } from "./file-consent.js";
import { extractMSTeamsConversationMessageId, normalizeMSTeamsConversationId } from "./inbound.js";
import type { MSTeamsAdapter } from "./messenger.js";
import { resolveMSTeamsSenderAccess } from "./monitor-handler/access.js";
import { createMSTeamsMessageHandler } from "./monitor-handler/message-handler.js";
import type { MSTeamsMonitorLogger } from "./monitor-types.js";
import { getPendingUpload, removePendingUpload } from "./pending-uploads.js";
import type { MSTeamsPollStore } from "./polls.js";
import { withRevokedProxyFallback } from "./revoked-context.js";
import { getMSTeamsRuntime } from "./runtime.js";
import type { MSTeamsTurnContext } from "./sdk-types.js";
import { buildGroupWelcomeText, buildWelcomeCard } from "./welcome-card.js";
export type { MSTeamsMessageHandlerDeps } from "./monitor-handler.types.js";
import type { MSTeamsMessageHandlerDeps } from "./monitor-handler.types.js";
export type MSTeamsAccessTokenProvider = {
getAccessToken: (scope: string) => Promise<string>;
@@ -37,19 +35,6 @@ export type MSTeamsActivityHandler = {
run?: (context: unknown) => Promise<void>;
};
export type MSTeamsMessageHandlerDeps = {
cfg: OpenClawConfig;
runtime: RuntimeEnv;
appId: string;
adapter: MSTeamsAdapter;
tokenProvider: MSTeamsAccessTokenProvider;
textLimit: number;
mediaMaxBytes: number;
conversationStore: MSTeamsConversationStore;
pollStore: MSTeamsPollStore;
log: MSTeamsMonitorLogger;
};
function serializeAdaptiveCardActionValue(value: unknown): string | null {
if (typeof value === "string") {
const trimmed = value.trim();

View File

@@ -0,0 +1,20 @@
import { type OpenClawConfig, type RuntimeEnv } from "../runtime-api.js";
import type { MSTeamsConversationStore } from "./conversation-store.js";
import type { MSTeamsAdapter } from "./messenger.js";
import type { MSTeamsMonitorLogger } from "./monitor-types.js";
import type { MSTeamsPollStore } from "./polls.js";
export type MSTeamsMessageHandlerDeps = {
cfg: OpenClawConfig;
runtime: RuntimeEnv;
appId: string;
adapter: MSTeamsAdapter;
tokenProvider: {
getAccessToken: (scope: string) => Promise<string>;
};
textLimit: number;
mediaMaxBytes: number;
conversationStore: MSTeamsConversationStore;
pollStore: MSTeamsPollStore;
log: MSTeamsMonitorLogger;
};

View File

@@ -75,7 +75,7 @@ function extractTextFromHtmlAttachments(attachments: MSTeamsAttachmentLike[]): s
}
return "";
}
import type { MSTeamsMessageHandlerDeps } from "../monitor-handler.js";
import type { MSTeamsMessageHandlerDeps } from "../monitor-handler.types.js";
import {
isMSTeamsGroupAllowed,
resolveMSTeamsAllowlistMatch,

View File

@@ -1,7 +1,7 @@
import crypto from "node:crypto";
import { safeEqualSecret } from "openclaw/plugin-sdk/browser-security-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { TwilioConfig, WebhookSecurityConfig } from "../config.js";
import type { TwilioConfig } from "../config.js";
import { getHeader } from "../http-headers.js";
import type { MediaStreamHandler } from "../media-stream.js";
import { chunkAudio } from "../telephony-audio.js";
@@ -29,13 +29,11 @@ import {
normalizeProviderStatus,
} from "./shared/call-status.js";
import { guardedJsonApiRequest } from "./shared/guarded-json-api.js";
import type { TwilioProviderOptions } from "./twilio.types.js";
import { twilioApiRequest } from "./twilio/api.js";
import { decideTwimlResponse, readTwimlRequestView } from "./twilio/twiml-policy.js";
import { verifyTwilioProviderWebhook } from "./twilio/webhook.js";
type StreamSendResult = {
sent: boolean;
};
export type { TwilioProviderOptions } from "./twilio.types.js";
function createTwilioRequestDedupeKey(ctx: WebhookContext, verifiedRequestKey?: string): string {
if (verifiedRequestKey) {
@@ -58,27 +56,9 @@ function createTwilioRequestDedupeKey(ctx: WebhookContext, verifiedRequestKey?:
.digest("hex")}`;
}
/**
* Twilio Voice API provider implementation.
*
* Uses Twilio Programmable Voice API with Media Streams for real-time
* bidirectional audio streaming.
*
* @see https://www.twilio.com/docs/voice
* @see https://www.twilio.com/docs/voice/media-streams
*/
export interface TwilioProviderOptions {
/** Allow ngrok free tier compatibility mode (loopback only, less secure) */
allowNgrokFreeTierLoopbackBypass?: boolean;
/** Override public URL for signature verification */
publicUrl?: string;
/** Path for media stream WebSocket (e.g., /voice/stream) */
streamPath?: string;
/** Skip webhook signature verification (development only) */
skipVerification?: boolean;
/** Webhook security options (forwarded headers/allowlist) */
webhookSecurity?: WebhookSecurityConfig;
}
type StreamSendResult = {
sent: boolean;
};
export class TwilioProvider implements VoiceCallProvider {
readonly name = "twilio" as const;

View File

@@ -0,0 +1,17 @@
import type { WebhookSecurityConfig } from "../config.js";
/**
* Twilio Voice API provider options.
*/
export interface TwilioProviderOptions {
/** Allow ngrok free tier compatibility mode (loopback only, less secure) */
allowNgrokFreeTierLoopbackBypass?: boolean;
/** Override public URL for signature verification */
publicUrl?: string;
/** Path for media stream WebSocket (e.g., /voice/stream) */
streamPath?: string;
/** Skip webhook signature verification (development only) */
skipVerification?: boolean;
/** Webhook security options (forwarded headers/allowlist) */
webhookSecurity?: WebhookSecurityConfig;
}

View File

@@ -1,6 +1,6 @@
import type { WebhookContext, WebhookVerificationResult } from "../../types.js";
import { verifyTwilioWebhook } from "../../webhook-security.js";
import type { TwilioProviderOptions } from "../twilio.js";
import type { TwilioProviderOptions } from "../twilio.types.js";
export function verifyTwilioProviderWebhook(params: {
ctx: WebhookContext;

View File

@@ -22,6 +22,7 @@ import type { VoiceCallProvider } from "./providers/base.js";
import { isProviderStatusTerminal } from "./providers/shared/call-status.js";
import type { TwilioProvider } from "./providers/twilio.js";
import type { CallRecord, NormalizedEvent, WebhookContext } from "./types.js";
import type { WebhookResponsePayload } from "./webhook.types.js";
import type { RealtimeCallHandler } from "./webhook/realtime-handler.js";
import { startStaleCallReaper } from "./webhook/stale-call-reaper.js";
@@ -48,12 +49,6 @@ function sanitizeTranscriptForLog(value: string): string {
return `${sanitized.slice(0, TRANSCRIPT_LOG_MAX_CHARS)}...`;
}
export type WebhookResponsePayload = {
statusCode: number;
body: string;
headers?: Record<string, string>;
};
function buildRequestUrl(
requestUrl: string | undefined,
requestHost: string | undefined,

View File

@@ -0,0 +1,5 @@
export type WebhookResponsePayload = {
statusCode: number;
body: string;
headers?: Record<string, string>;
};

View File

@@ -12,7 +12,7 @@ import type { VoiceCallRealtimeConfig } from "../config.js";
import type { CallManager } from "../manager.js";
import type { VoiceCallProvider } from "../providers/base.js";
import type { CallRecord, NormalizedEvent } from "../types.js";
import type { WebhookResponsePayload } from "../webhook.js";
import type { WebhookResponsePayload } from "../webhook.types.js";
export type ToolHandlerFn = (args: unknown, callId: string) => Promise<unknown>;

View File

@@ -1,4 +1,4 @@
import type { XaiWebSearchResponse } from "./web-search-shared.js";
import type { XaiWebSearchResponse } from "./web-search-response.types.js";
export const XAI_RESPONSES_ENDPOINT = "https://api.x.ai/v1/responses";

View File

@@ -0,0 +1,25 @@
export type XaiWebSearchResponse = {
output?: Array<{
type?: string;
text?: string;
content?: Array<{
type?: string;
text?: string;
annotations?: Array<{
type?: string;
url?: string;
}>;
}>;
annotations?: Array<{
type?: string;
url?: string;
}>;
}>;
output_text?: string;
citations?: string[];
inline_citations?: Array<{
start_index: number;
end_index: number;
url: string;
}>;
};

View File

@@ -7,37 +7,13 @@ import {
XAI_RESPONSES_ENDPOINT,
} from "./responses-tool-shared.js";
import { isRecord } from "./tool-config-shared.js";
import type { XaiWebSearchResponse } from "./web-search-response.types.js";
export { extractXaiWebSearchContent } from "./responses-tool-shared.js";
export type { XaiWebSearchResponse } from "./web-search-response.types.js";
export const XAI_WEB_SEARCH_ENDPOINT = XAI_RESPONSES_ENDPOINT;
export const XAI_DEFAULT_WEB_SEARCH_MODEL = "grok-4-1-fast";
export type XaiWebSearchResponse = {
output?: Array<{
type?: string;
text?: string;
content?: Array<{
type?: string;
text?: string;
annotations?: Array<{
type?: string;
url?: string;
}>;
}>;
annotations?: Array<{
type?: string;
url?: string;
}>;
}>;
output_text?: string;
citations?: string[];
inline_citations?: Array<{
start_index: number;
end_index: number;
url: string;
}>;
};
type XaiWebSearchConfig = Record<string, unknown> & {
model?: unknown;
inlineCitations?: unknown;