chore: format and sync protocol outputs
This commit is contained in:
@@ -1679,6 +1679,27 @@ public struct ChatAbortParams: Codable, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ChatInjectParams: Codable, Sendable {
|
||||||
|
public let sessionkey: String
|
||||||
|
public let message: String
|
||||||
|
public let label: String?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
sessionkey: String,
|
||||||
|
message: String,
|
||||||
|
label: String?
|
||||||
|
) {
|
||||||
|
self.sessionkey = sessionkey
|
||||||
|
self.message = message
|
||||||
|
self.label = label
|
||||||
|
}
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case sessionkey = "sessionKey"
|
||||||
|
case message
|
||||||
|
case label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct ChatEvent: Codable, Sendable {
|
public struct ChatEvent: Codable, Sendable {
|
||||||
public let runid: String
|
public let runid: String
|
||||||
public let sessionkey: String
|
public let sessionkey: String
|
||||||
|
|||||||
@@ -126,15 +126,7 @@ describe("cleanupSuspendedCliProcesses", () => {
|
|||||||
await cleanupSuspendedCliProcesses(
|
await cleanupSuspendedCliProcesses(
|
||||||
{
|
{
|
||||||
command: "codex",
|
command: "codex",
|
||||||
resumeArgs: [
|
resumeArgs: ["exec", "resume", "{sessionId}", "--color", "never", "--sandbox", "read-only"],
|
||||||
"exec",
|
|
||||||
"resume",
|
|
||||||
"{sessionId}",
|
|
||||||
"--color",
|
|
||||||
"never",
|
|
||||||
"--sandbox",
|
|
||||||
"read-only",
|
|
||||||
],
|
|
||||||
} as CliBackendConfig,
|
} as CliBackendConfig,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ describe("sanitizeUserFacingText", () => {
|
|||||||
|
|
||||||
it("sanitizes raw API error payloads", () => {
|
it("sanitizes raw API error payloads", () => {
|
||||||
const raw = '{"type":"error","error":{"message":"Something exploded","type":"server_error"}}';
|
const raw = '{"type":"error","error":{"message":"Something exploded","type":"server_error"}}';
|
||||||
expect(sanitizeUserFacingText(raw)).toBe(
|
expect(sanitizeUserFacingText(raw)).toBe("The AI service returned an error. Please try again.");
|
||||||
"The AI service returned an error. Please try again.",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -230,7 +230,11 @@ export function formatAssistantErrorText(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Catch role ordering errors - including JSON-wrapped and "400" prefix variants
|
// Catch role ordering errors - including JSON-wrapped and "400" prefix variants
|
||||||
if (/incorrect role information|roles must alternate|400.*role|"message".*role.*information/i.test(raw)) {
|
if (
|
||||||
|
/incorrect role information|roles must alternate|400.*role|"message".*role.*information/i.test(
|
||||||
|
raw,
|
||||||
|
)
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
"Message ordering conflict - please try again. " +
|
"Message ordering conflict - please try again. " +
|
||||||
"If this persists, use /new to start a fresh session."
|
"If this persists, use /new to start a fresh session."
|
||||||
|
|||||||
@@ -422,7 +422,8 @@ export async function runEmbeddedAttempt(
|
|||||||
|
|
||||||
// Check if last message is a user message to prevent consecutive user turns
|
// Check if last message is a user message to prevent consecutive user turns
|
||||||
const lastMsg = activeSession.messages[activeSession.messages.length - 1];
|
const lastMsg = activeSession.messages[activeSession.messages.length - 1];
|
||||||
const lastMsgRole = lastMsg && typeof lastMsg === "object" ? (lastMsg as { role?: unknown }).role : undefined;
|
const lastMsgRole =
|
||||||
|
lastMsg && typeof lastMsg === "object" ? (lastMsg as { role?: unknown }).role : undefined;
|
||||||
|
|
||||||
if (lastMsgRole === "user") {
|
if (lastMsgRole === "user") {
|
||||||
// Last message was a user message. Adding another user message would create
|
// Last message was a user message. Adding another user message would create
|
||||||
@@ -433,9 +434,11 @@ export async function runEmbeddedAttempt(
|
|||||||
// Skip this prompt to prevent "400 Incorrect role information" error.
|
// Skip this prompt to prevent "400 Incorrect role information" error.
|
||||||
log.warn(
|
log.warn(
|
||||||
`Skipping prompt because last message is a user message (would create consecutive user turns). ` +
|
`Skipping prompt because last message is a user message (would create consecutive user turns). ` +
|
||||||
`runId=${params.runId} sessionId=${params.sessionId}`
|
`runId=${params.runId} sessionId=${params.sessionId}`,
|
||||||
|
);
|
||||||
|
promptError = new Error(
|
||||||
|
"Incorrect role information: consecutive user messages would violate role ordering",
|
||||||
);
|
);
|
||||||
promptError = new Error("Incorrect role information: consecutive user messages would violate role ordering");
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
await activeSession.prompt(params.prompt, { images: params.images });
|
await activeSession.prompt(params.prompt, { images: params.images });
|
||||||
|
|||||||
@@ -27,10 +27,7 @@ function buildSkillsSection(params: {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildMemorySection(params: {
|
function buildMemorySection(params: { isMinimal: boolean; availableTools: Set<string> }) {
|
||||||
isMinimal: boolean;
|
|
||||||
availableTools: Set<string>;
|
|
||||||
}) {
|
|
||||||
if (params.isMinimal) return [];
|
if (params.isMinimal) return [];
|
||||||
if (!params.availableTools.has("memory_search") && !params.availableTools.has("memory_get")) {
|
if (!params.availableTools.has("memory_search") && !params.availableTools.has("memory_get")) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -156,10 +156,7 @@ export async function buildStatusReply(params: {
|
|||||||
usageProviders.add(currentUsageProvider);
|
usageProviders.add(currentUsageProvider);
|
||||||
}
|
}
|
||||||
const usageByProvider = new Map<string, string>();
|
const usageByProvider = new Map<string, string>();
|
||||||
let usageSummaryCache:
|
let usageSummaryCache: Awaited<ReturnType<typeof loadProviderUsageSummary>> | null | undefined;
|
||||||
| Awaited<ReturnType<typeof loadProviderUsageSummary>>
|
|
||||||
| null
|
|
||||||
| undefined;
|
|
||||||
if (usageProviders.size > 0) {
|
if (usageProviders.size > 0) {
|
||||||
try {
|
try {
|
||||||
usageSummaryCache = await loadProviderUsageSummary({
|
usageSummaryCache = await loadProviderUsageSummary({
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ export function applyReplyTagsToPayload(
|
|||||||
export function isRenderablePayload(payload: ReplyPayload): boolean {
|
export function isRenderablePayload(payload: ReplyPayload): boolean {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
payload.text ||
|
payload.text ||
|
||||||
payload.mediaUrl ||
|
payload.mediaUrl ||
|
||||||
(payload.mediaUrls && payload.mediaUrls.length > 0) ||
|
(payload.mediaUrls && payload.mediaUrls.length > 0) ||
|
||||||
payload.audioAsVoice,
|
payload.audioAsVoice,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ export function findChromeExecutableMac(): BrowserExecutable | null {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
kind: "edge",
|
kind: "edge",
|
||||||
path: path.join(os.homedir(), "Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"),
|
path: path.join(
|
||||||
|
os.homedir(),
|
||||||
|
"Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
kind: "chromium",
|
kind: "chromium",
|
||||||
|
|||||||
@@ -19,14 +19,7 @@ import { getActivePluginRegistry } from "../../plugins/runtime.js";
|
|||||||
// - add an entry to `src/channels/dock.ts` for shared behavior (capabilities, allowFrom, threading, …)
|
// - add an entry to `src/channels/dock.ts` for shared behavior (capabilities, allowFrom, threading, …)
|
||||||
// - add ids/aliases in `src/channels/registry.ts`
|
// - add ids/aliases in `src/channels/registry.ts`
|
||||||
function resolveCoreChannels(): ChannelPlugin[] {
|
function resolveCoreChannels(): ChannelPlugin[] {
|
||||||
return [
|
return [telegramPlugin, whatsappPlugin, discordPlugin, slackPlugin, signalPlugin, imessagePlugin];
|
||||||
telegramPlugin,
|
|
||||||
whatsappPlugin,
|
|
||||||
discordPlugin,
|
|
||||||
slackPlugin,
|
|
||||||
signalPlugin,
|
|
||||||
imessagePlugin,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function listPluginChannels(): ChannelPlugin[] {
|
function listPluginChannels(): ChannelPlugin[] {
|
||||||
@@ -80,12 +73,5 @@ export function normalizeChannelId(raw?: string | null): ChannelId | null {
|
|||||||
return plugin?.id ?? null;
|
return plugin?.id ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export { discordPlugin, imessagePlugin, signalPlugin, slackPlugin, telegramPlugin, whatsappPlugin };
|
||||||
discordPlugin,
|
|
||||||
imessagePlugin,
|
|
||||||
signalPlugin,
|
|
||||||
slackPlugin,
|
|
||||||
telegramPlugin,
|
|
||||||
whatsappPlugin,
|
|
||||||
};
|
|
||||||
export type { ChannelId, ChannelPlugin } from "./types.js";
|
export type { ChannelId, ChannelPlugin } from "./types.js";
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import {
|
|||||||
githubCopilotLoginCommand,
|
githubCopilotLoginCommand,
|
||||||
modelsAliasesAddCommand,
|
modelsAliasesAddCommand,
|
||||||
modelsAliasesListCommand,
|
modelsAliasesListCommand,
|
||||||
modelsAliasesRemoveCommand,
|
modelsAliasesRemoveCommand,
|
||||||
modelsAuthAddCommand,
|
modelsAuthAddCommand,
|
||||||
modelsAuthLoginCommand,
|
modelsAuthLoginCommand,
|
||||||
modelsAuthOrderClearCommand,
|
modelsAuthOrderClearCommand,
|
||||||
modelsAuthOrderGetCommand,
|
modelsAuthOrderGetCommand,
|
||||||
modelsAuthOrderSetCommand,
|
modelsAuthOrderSetCommand,
|
||||||
modelsAuthPasteTokenCommand,
|
modelsAuthPasteTokenCommand,
|
||||||
modelsAuthSetupTokenCommand,
|
modelsAuthSetupTokenCommand,
|
||||||
modelsFallbacksAddCommand,
|
modelsFallbacksAddCommand,
|
||||||
modelsFallbacksClearCommand,
|
modelsFallbacksClearCommand,
|
||||||
modelsFallbacksListCommand,
|
modelsFallbacksListCommand,
|
||||||
|
|||||||
@@ -119,11 +119,14 @@ describe("runConfigureWizard", () => {
|
|||||||
mocks.clackText.mockResolvedValue("");
|
mocks.clackText.mockResolvedValue("");
|
||||||
mocks.clackConfirm.mockResolvedValue(false);
|
mocks.clackConfirm.mockResolvedValue(false);
|
||||||
|
|
||||||
await runConfigureWizard({ command: "configure" }, {
|
await runConfigureWizard(
|
||||||
log: vi.fn(),
|
{ command: "configure" },
|
||||||
error: vi.fn(),
|
{
|
||||||
exit: vi.fn(),
|
log: vi.fn(),
|
||||||
});
|
error: vi.fn(),
|
||||||
|
exit: vi.fn(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
expect(mocks.writeConfigFile).toHaveBeenCalledWith(
|
expect(mocks.writeConfigFile).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
|||||||
@@ -8,10 +8,18 @@ import {
|
|||||||
upsertAuthProfile,
|
upsertAuthProfile,
|
||||||
} from "../../agents/auth-profiles.js";
|
} from "../../agents/auth-profiles.js";
|
||||||
import { normalizeProviderId } from "../../agents/model-selection.js";
|
import { normalizeProviderId } from "../../agents/model-selection.js";
|
||||||
import { resolveAgentDir, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
import {
|
||||||
|
resolveAgentDir,
|
||||||
|
resolveAgentWorkspaceDir,
|
||||||
|
resolveDefaultAgentId,
|
||||||
|
} from "../../agents/agent-scope.js";
|
||||||
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
|
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
|
||||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||||
import { CONFIG_PATH_CLAWDBOT, readConfigFileSnapshot, type ClawdbotConfig } from "../../config/config.js";
|
import {
|
||||||
|
CONFIG_PATH_CLAWDBOT,
|
||||||
|
readConfigFileSnapshot,
|
||||||
|
type ClawdbotConfig,
|
||||||
|
} from "../../config/config.js";
|
||||||
import type { RuntimeEnv } from "../../runtime.js";
|
import type { RuntimeEnv } from "../../runtime.js";
|
||||||
import { stylePromptHint, stylePromptMessage } from "../../terminal/prompt-style.js";
|
import { stylePromptHint, stylePromptMessage } from "../../terminal/prompt-style.js";
|
||||||
import { applyAuthProfileConfig } from "../onboard-auth.js";
|
import { applyAuthProfileConfig } from "../onboard-auth.js";
|
||||||
@@ -21,7 +29,11 @@ import { createVpsAwareOAuthHandlers } from "../oauth-flow.js";
|
|||||||
import { updateConfig } from "./shared.js";
|
import { updateConfig } from "./shared.js";
|
||||||
import { resolvePluginProviders } from "../../plugins/providers.js";
|
import { resolvePluginProviders } from "../../plugins/providers.js";
|
||||||
import { createClackPrompter } from "../../wizard/clack-prompter.js";
|
import { createClackPrompter } from "../../wizard/clack-prompter.js";
|
||||||
import type { ProviderAuthMethod, ProviderAuthResult, ProviderPlugin } from "../../plugins/types.js";
|
import type {
|
||||||
|
ProviderAuthMethod,
|
||||||
|
ProviderAuthResult,
|
||||||
|
ProviderPlugin,
|
||||||
|
} from "../../plugins/types.js";
|
||||||
import type { AuthProfileCredential } from "../../agents/auth-profiles/types.js";
|
import type { AuthProfileCredential } from "../../agents/auth-profiles/types.js";
|
||||||
|
|
||||||
const confirm = (params: Parameters<typeof clackConfirm>[0]) =>
|
const confirm = (params: Parameters<typeof clackConfirm>[0]) =>
|
||||||
@@ -334,14 +346,16 @@ export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: Runtim
|
|||||||
const prompter = createClackPrompter();
|
const prompter = createClackPrompter();
|
||||||
const selectedProvider =
|
const selectedProvider =
|
||||||
resolveProviderMatch(providers, opts.provider) ??
|
resolveProviderMatch(providers, opts.provider) ??
|
||||||
(await prompter.select({
|
(await prompter
|
||||||
message: "Select a provider",
|
.select({
|
||||||
options: providers.map((provider) => ({
|
message: "Select a provider",
|
||||||
value: provider.id,
|
options: providers.map((provider) => ({
|
||||||
label: provider.label,
|
value: provider.id,
|
||||||
hint: provider.docsPath ? `Docs: ${provider.docsPath}` : undefined,
|
label: provider.label,
|
||||||
})),
|
hint: provider.docsPath ? `Docs: ${provider.docsPath}` : undefined,
|
||||||
}).then((id) => resolveProviderMatch(providers, String(id))));
|
})),
|
||||||
|
})
|
||||||
|
.then((id) => resolveProviderMatch(providers, String(id))));
|
||||||
|
|
||||||
if (!selectedProvider) {
|
if (!selectedProvider) {
|
||||||
throw new Error("Unknown provider. Use --provider <id> to pick a provider plugin.");
|
throw new Error("Unknown provider. Use --provider <id> to pick a provider plugin.");
|
||||||
@@ -351,16 +365,16 @@ export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: Runtim
|
|||||||
pickAuthMethod(selectedProvider, opts.method) ??
|
pickAuthMethod(selectedProvider, opts.method) ??
|
||||||
(selectedProvider.auth.length === 1
|
(selectedProvider.auth.length === 1
|
||||||
? selectedProvider.auth[0]
|
? selectedProvider.auth[0]
|
||||||
: await prompter.select({
|
: await prompter
|
||||||
message: `Auth method for ${selectedProvider.label}`,
|
.select({
|
||||||
options: selectedProvider.auth.map((method) => ({
|
message: `Auth method for ${selectedProvider.label}`,
|
||||||
value: method.id,
|
options: selectedProvider.auth.map((method) => ({
|
||||||
label: method.label,
|
value: method.id,
|
||||||
hint: method.hint,
|
label: method.label,
|
||||||
})),
|
hint: method.hint,
|
||||||
}).then((id) =>
|
})),
|
||||||
selectedProvider.auth.find((method) => method.id === String(id)),
|
})
|
||||||
));
|
.then((id) => selectedProvider.auth.find((method) => method.id === String(id))));
|
||||||
|
|
||||||
if (!chosenMethod) {
|
if (!chosenMethod) {
|
||||||
throw new Error("Unknown auth method. Use --method <id> to select one.");
|
throw new Error("Unknown auth method. Use --method <id> to select one.");
|
||||||
|
|||||||
@@ -177,9 +177,7 @@ describe("setupChannels", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(select).toHaveBeenCalledWith(
|
expect(select).toHaveBeenCalledWith(expect.objectContaining({ message: "Select a channel" }));
|
||||||
expect.objectContaining({ message: "Select a channel" }),
|
|
||||||
);
|
|
||||||
expect(multiselect).not.toHaveBeenCalled();
|
expect(multiselect).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -317,7 +317,10 @@ export async function setupChannels(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const buildSelectionOptions = (
|
const buildSelectionOptions = (
|
||||||
entries: Array<{ id: ChannelChoice; meta: { id: string; label: string; selectionLabel?: string } }>,
|
entries: Array<{
|
||||||
|
id: ChannelChoice;
|
||||||
|
meta: { id: string; label: string; selectionLabel?: string };
|
||||||
|
}>,
|
||||||
) =>
|
) =>
|
||||||
entries.map((entry) => {
|
entries.map((entry) => {
|
||||||
const status = statusByChannel.get(entry.id);
|
const status = statusByChannel.get(entry.id);
|
||||||
|
|||||||
@@ -108,9 +108,7 @@ export function resolveHeartbeatIntervalMs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resolveHeartbeatPrompt(cfg: ClawdbotConfig, heartbeat?: HeartbeatConfig) {
|
export function resolveHeartbeatPrompt(cfg: ClawdbotConfig, heartbeat?: HeartbeatConfig) {
|
||||||
return resolveHeartbeatPromptText(
|
return resolveHeartbeatPromptText(heartbeat?.prompt ?? cfg.agents?.defaults?.heartbeat?.prompt);
|
||||||
heartbeat?.prompt ?? cfg.agents?.defaults?.heartbeat?.prompt,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveHeartbeatAckMaxChars(cfg: ClawdbotConfig, heartbeat?: HeartbeatConfig) {
|
function resolveHeartbeatAckMaxChars(cfg: ClawdbotConfig, heartbeat?: HeartbeatConfig) {
|
||||||
@@ -127,9 +125,7 @@ function resolveHeartbeatSession(cfg: ClawdbotConfig, agentId?: string) {
|
|||||||
const scope = sessionCfg?.scope ?? "per-sender";
|
const scope = sessionCfg?.scope ?? "per-sender";
|
||||||
const resolvedAgentId = normalizeAgentId(agentId ?? resolveDefaultAgentId(cfg));
|
const resolvedAgentId = normalizeAgentId(agentId ?? resolveDefaultAgentId(cfg));
|
||||||
const sessionKey =
|
const sessionKey =
|
||||||
scope === "global"
|
scope === "global" ? "global" : resolveAgentMainSessionKey({ cfg, agentId: resolvedAgentId });
|
||||||
? "global"
|
|
||||||
: resolveAgentMainSessionKey({ cfg, agentId: resolvedAgentId });
|
|
||||||
const storeAgentId = scope === "global" ? resolveDefaultAgentId(cfg) : resolvedAgentId;
|
const storeAgentId = scope === "global" ? resolveDefaultAgentId(cfg) : resolvedAgentId;
|
||||||
const storePath = resolveStorePath(sessionCfg?.store, { agentId: storeAgentId });
|
const storePath = resolveStorePath(sessionCfg?.store, { agentId: storeAgentId });
|
||||||
const store = loadSessionStore(storePath);
|
const store = loadSessionStore(storePath);
|
||||||
@@ -337,8 +333,10 @@ export async function runHeartbeatOnce(opts: {
|
|||||||
|
|
||||||
// Suppress duplicate heartbeats (same payload) within a short window.
|
// Suppress duplicate heartbeats (same payload) within a short window.
|
||||||
// This prevents "nagging" when nothing changed but the model repeats the same items.
|
// This prevents "nagging" when nothing changed but the model repeats the same items.
|
||||||
const prevHeartbeatText = typeof entry?.lastHeartbeatText === "string" ? entry.lastHeartbeatText : "";
|
const prevHeartbeatText =
|
||||||
const prevHeartbeatAt = typeof entry?.lastHeartbeatSentAt === "number" ? entry.lastHeartbeatSentAt : undefined;
|
typeof entry?.lastHeartbeatText === "string" ? entry.lastHeartbeatText : "";
|
||||||
|
const prevHeartbeatAt =
|
||||||
|
typeof entry?.lastHeartbeatSentAt === "number" ? entry.lastHeartbeatSentAt : undefined;
|
||||||
const isDuplicateMain =
|
const isDuplicateMain =
|
||||||
!shouldSkipMain &&
|
!shouldSkipMain &&
|
||||||
!mediaUrls.length &&
|
!mediaUrls.length &&
|
||||||
|
|||||||
@@ -210,10 +210,11 @@ export async function runMessageAction(
|
|||||||
const to = readStringParam(params, "to", { required: true });
|
const to = readStringParam(params, "to", { required: true });
|
||||||
// Allow message to be omitted when sending media-only (e.g., voice notes)
|
// Allow message to be omitted when sending media-only (e.g., voice notes)
|
||||||
const mediaHint = readStringParam(params, "media", { trim: false });
|
const mediaHint = readStringParam(params, "media", { trim: false });
|
||||||
let message = readStringParam(params, "message", {
|
let message =
|
||||||
required: !mediaHint, // Only require message if no media hint
|
readStringParam(params, "message", {
|
||||||
allowEmpty: true,
|
required: !mediaHint, // Only require message if no media hint
|
||||||
}) ?? "";
|
allowEmpty: true,
|
||||||
|
}) ?? "";
|
||||||
|
|
||||||
const parsed = parseReplyDirectives(message);
|
const parsed = parseReplyDirectives(message);
|
||||||
message = parsed.text;
|
message = parsed.text;
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ export type GatewaySessionList = {
|
|||||||
ts: number;
|
ts: number;
|
||||||
path: string;
|
path: string;
|
||||||
count: number;
|
count: number;
|
||||||
defaults?: { model?: string | null; modelProvider?: string | null; contextTokens?: number | null };
|
defaults?: {
|
||||||
|
model?: string | null;
|
||||||
|
modelProvider?: string | null;
|
||||||
|
contextTokens?: number | null;
|
||||||
|
};
|
||||||
sessions: Array<{
|
sessions: Array<{
|
||||||
key: string;
|
key: string;
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user