mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
chore: Enable more lint rules, disable some that trigger a lot. Will clean up later.
This commit is contained in:
@@ -7,13 +7,24 @@
|
||||
],
|
||||
"categories": {
|
||||
"correctness": "error",
|
||||
"perf": "error"
|
||||
"perf": "error",
|
||||
"suspicious": "error",
|
||||
},
|
||||
"rules": {
|
||||
"eslint-plugin-unicorn/prefer-array-find": "off",
|
||||
"eslint/no-await-in-loop": "off",
|
||||
"oxc/no-accumulating-spread": "off",
|
||||
"oxc/no-map-spread": "off"
|
||||
"oxc/no-map-spread": "off",
|
||||
"typescript/no-unsafe-type-assertion": "off",
|
||||
"typescript/no-unnecessary-template-expression": "off",
|
||||
"unicorn/consistent-function-scoping": "off",
|
||||
"typescript/no-extraneous-class": "off",
|
||||
"oxc/no-async-endpoint-handlers": "off",
|
||||
"eslint/no-useless-concat": "off",
|
||||
"eslint/no-unused-vars": "off",
|
||||
"eslint/no-new": "off",
|
||||
"eslint/preserve-caught-error": "off",
|
||||
"unicorn/require-post-message-target-origin": "off",
|
||||
},
|
||||
"ignorePatterns": ["src/canvas-host/a2ui/a2ui.bundle.js"]
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ export function createAnthropicPayloadLogger(params: {
|
||||
|
||||
const wrapStreamFn: AnthropicPayloadLogger["wrapStreamFn"] = (streamFn) => {
|
||||
const wrapped: StreamFn = (model, context, options) => {
|
||||
if (!isAnthropicModel(model as Model<Api>)) {
|
||||
if (!isAnthropicModel(model)) {
|
||||
return streamFn(model, context, options);
|
||||
}
|
||||
const nextOnPayload = (payload: unknown) => {
|
||||
|
||||
@@ -85,7 +85,7 @@ function applyReplacements(
|
||||
replacements: Array<[number, number, string[]]>,
|
||||
): string[] {
|
||||
const result = [...lines];
|
||||
for (const [startIndex, oldLen, newLines] of [...replacements].reverse()) {
|
||||
for (const [startIndex, oldLen, newLines] of [...replacements].toReversed()) {
|
||||
for (let i = 0; i < oldLen; i += 1) {
|
||||
if (startIndex < result.length) {
|
||||
result.splice(startIndex, 1);
|
||||
|
||||
@@ -169,7 +169,7 @@ export function buildAuthHealthSummary(params: {
|
||||
warnAfterMs,
|
||||
}),
|
||||
)
|
||||
.sort((a, b) => {
|
||||
.toSorted((a, b) => {
|
||||
if (a.provider !== b.provider) {
|
||||
return a.provider.localeCompare(b.provider);
|
||||
}
|
||||
@@ -236,7 +236,7 @@ export function buildAuthHealthSummary(params: {
|
||||
}
|
||||
}
|
||||
|
||||
const providers = Array.from(providersMap.values()).sort((a, b) =>
|
||||
const providers = Array.from(providersMap.values()).toSorted((a, b) =>
|
||||
a.provider.localeCompare(b.provider),
|
||||
);
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@ export function resolveAuthProfileDisplayLabel(params: {
|
||||
const { cfg, store, profileId } = params;
|
||||
const profile = store.profiles[profileId];
|
||||
const configEmail = cfg?.auth?.profiles?.[profileId]?.email?.trim();
|
||||
const email =
|
||||
configEmail ||
|
||||
(profile && "email" in profile ? (profile.email as string | undefined)?.trim() : undefined);
|
||||
const email = configEmail || (profile && "email" in profile ? profile.email?.trim() : undefined);
|
||||
if (email) return `${profileId} (${email})`;
|
||||
return profileId;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ async function refreshOAuthTokenWithLock(params: {
|
||||
const newCredentials = await refreshQwenPortalCredentials(cred);
|
||||
return { apiKey: newCredentials.access, newCredentials };
|
||||
})()
|
||||
: await getOAuthApiKey(cred.provider as OAuthProvider, oauthCreds);
|
||||
: await getOAuthApiKey(cred.provider, oauthCreds);
|
||||
if (!result) return null;
|
||||
store.profiles[params.profileId] = {
|
||||
...cred,
|
||||
@@ -233,6 +233,7 @@ export async function resolveApiKeyForProfile(params: {
|
||||
`OAuth token refresh failed for ${cred.provider}: ${message}. ` +
|
||||
"Please try again or re-authenticate." +
|
||||
(hint ? `\n\n${hint}` : ""),
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ export function resolveAuthProfileOrder(params: {
|
||||
}
|
||||
|
||||
const cooldownSorted = inCooldown
|
||||
.sort((a, b) => a.cooldownUntil - b.cooldownUntil)
|
||||
.toSorted((a, b) => a.cooldownUntil - b.cooldownUntil)
|
||||
.map((entry) => entry.profileId);
|
||||
|
||||
const ordered = [...available, ...cooldownSorted];
|
||||
@@ -163,7 +163,7 @@ function orderProfilesByMode(order: string[], store: AuthProfileStore): string[]
|
||||
// Primary sort: type preference (oauth > token > api_key).
|
||||
// Secondary sort: lastUsed (oldest first for round-robin within type).
|
||||
const sorted = scored
|
||||
.sort((a, b) => {
|
||||
.toSorted((a, b) => {
|
||||
// First by type (oauth > token > api_key)
|
||||
if (a.typeScore !== b.typeScore) return a.typeScore - b.typeScore;
|
||||
// Then by lastUsed (oldest first)
|
||||
@@ -177,7 +177,7 @@ function orderProfilesByMode(order: string[], store: AuthProfileStore): string[]
|
||||
profileId,
|
||||
cooldownUntil: resolveProfileUnusableUntil(store.usageStats?.[profileId] ?? {}) ?? now,
|
||||
}))
|
||||
.sort((a, b) => a.cooldownUntil - b.cooldownUntil)
|
||||
.toSorted((a, b) => a.cooldownUntil - b.cooldownUntil)
|
||||
.map((entry) => entry.profileId);
|
||||
|
||||
return [...sorted, ...cooldownSorted];
|
||||
|
||||
@@ -45,7 +45,7 @@ export function suggestOAuthProfileIdForLegacyDefault(params: {
|
||||
const byEmail = oauthProfiles.find((id) => {
|
||||
const cred = params.store.profiles[id];
|
||||
if (!cred || cred.type !== "oauth") return false;
|
||||
const email = (cred.email as string | undefined)?.trim();
|
||||
const email = cred.email?.trim();
|
||||
return email === configuredEmail || id === `${providerKey}:${configuredEmail}`;
|
||||
});
|
||||
if (byEmail) return byEmail;
|
||||
@@ -93,11 +93,10 @@ export function repairOAuthProfileIdMismatch(params: {
|
||||
}
|
||||
|
||||
const toCred = params.store.profiles[toProfileId];
|
||||
const toEmail =
|
||||
toCred?.type === "oauth" ? (toCred.email as string | undefined)?.trim() : undefined;
|
||||
const toEmail = toCred?.type === "oauth" ? toCred.email?.trim() : undefined;
|
||||
|
||||
const nextProfiles = {
|
||||
...(params.cfg.auth?.profiles as Record<string, AuthProfileConfig> | undefined),
|
||||
...params.cfg.auth?.profiles,
|
||||
} as Record<string, AuthProfileConfig>;
|
||||
delete nextProfiles[legacyProfileId];
|
||||
nextProfiles[toProfileId] = {
|
||||
|
||||
@@ -912,6 +912,7 @@ export function createExecTool(
|
||||
if (!nodeQuery && String(err).includes("node required")) {
|
||||
throw new Error(
|
||||
"exec host=node requires a node id when multiple nodes are available (set tools.exec.node or exec.node).",
|
||||
{ cause: err },
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
@@ -941,11 +942,11 @@ export function createExecTool(
|
||||
let allowlistSatisfied = false;
|
||||
if (hostAsk === "on-miss" && hostSecurity === "allowlist" && analysisOk) {
|
||||
try {
|
||||
const approvalsSnapshot = (await callGatewayTool(
|
||||
const approvalsSnapshot = await callGatewayTool(
|
||||
"exec.approvals.node.get",
|
||||
{ timeoutMs: 10_000 },
|
||||
{ nodeId },
|
||||
)) as { file?: unknown } | null;
|
||||
);
|
||||
const approvalsFile =
|
||||
approvalsSnapshot && typeof approvalsSnapshot === "object"
|
||||
? approvalsSnapshot.file
|
||||
@@ -1016,7 +1017,7 @@ export function createExecTool(
|
||||
void (async () => {
|
||||
let decision: string | null = null;
|
||||
try {
|
||||
const decisionResult = (await callGatewayTool(
|
||||
const decisionResult = await callGatewayTool(
|
||||
"exec.approval.request",
|
||||
{ timeoutMs: DEFAULT_APPROVAL_REQUEST_TIMEOUT_MS },
|
||||
{
|
||||
@@ -1031,7 +1032,7 @@ export function createExecTool(
|
||||
sessionKey: defaults?.sessionKey,
|
||||
timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS,
|
||||
},
|
||||
)) as { decision?: string } | null;
|
||||
);
|
||||
decision =
|
||||
decisionResult && typeof decisionResult === "object"
|
||||
? (decisionResult.decision ?? null)
|
||||
@@ -1124,20 +1125,11 @@ export function createExecTool(
|
||||
}
|
||||
|
||||
const startedAt = Date.now();
|
||||
const raw = (await callGatewayTool(
|
||||
const raw = await callGatewayTool(
|
||||
"node.invoke",
|
||||
{ timeoutMs: invokeTimeoutMs },
|
||||
buildInvokeParams(false, null),
|
||||
)) as {
|
||||
payload?: {
|
||||
exitCode?: number;
|
||||
timedOut?: boolean;
|
||||
success?: boolean;
|
||||
stdout?: string;
|
||||
stderr?: string;
|
||||
error?: string | null;
|
||||
};
|
||||
};
|
||||
);
|
||||
const payload = raw?.payload ?? {};
|
||||
return {
|
||||
content: [
|
||||
@@ -1197,7 +1189,7 @@ export function createExecTool(
|
||||
void (async () => {
|
||||
let decision: string | null = null;
|
||||
try {
|
||||
const decisionResult = (await callGatewayTool(
|
||||
const decisionResult = await callGatewayTool(
|
||||
"exec.approval.request",
|
||||
{ timeoutMs: DEFAULT_APPROVAL_REQUEST_TIMEOUT_MS },
|
||||
{
|
||||
@@ -1212,7 +1204,7 @@ export function createExecTool(
|
||||
sessionKey: defaults?.sessionKey,
|
||||
timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS,
|
||||
},
|
||||
)) as { decision?: string } | null;
|
||||
);
|
||||
decision =
|
||||
decisionResult && typeof decisionResult === "object"
|
||||
? (decisionResult.decision ?? null)
|
||||
|
||||
@@ -116,7 +116,7 @@ export function createProcessTool(
|
||||
exitSignal: s.exitSignal ?? undefined,
|
||||
}));
|
||||
const lines = [...running, ...finished]
|
||||
.sort((a, b) => b.startedAt - a.startedAt)
|
||||
.toSorted((a, b) => b.startedAt - a.startedAt)
|
||||
.map((s) => {
|
||||
const label = s.name ? truncateMiddle(s.name, 80) : truncateMiddle(s.command, 120);
|
||||
return `${s.sessionId} ${pad(s.status, 9)} ${formatDuration(s.runtimeMs)} :: ${label}`;
|
||||
|
||||
@@ -32,7 +32,7 @@ function normalizeProviderFilter(filter?: string[]): string[] {
|
||||
const normalized = new Set(
|
||||
filter.map((entry) => entry.trim().toLowerCase()).filter((entry) => entry.length > 0),
|
||||
);
|
||||
return Array.from(normalized).sort();
|
||||
return Array.from(normalized).toSorted();
|
||||
}
|
||||
|
||||
function buildCacheKey(params: {
|
||||
@@ -168,7 +168,7 @@ export async function discoverBedrockModels(params: {
|
||||
}),
|
||||
);
|
||||
}
|
||||
return discovered.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return discovered.toSorted((a, b) => a.name.localeCompare(b.name));
|
||||
})();
|
||||
|
||||
if (refreshIntervalSeconds > 0) {
|
||||
|
||||
@@ -147,7 +147,7 @@ function stableStringify(value: unknown): string {
|
||||
return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
|
||||
}
|
||||
const record = value as Record<string, unknown>;
|
||||
const keys = Object.keys(record).sort();
|
||||
const keys = Object.keys(record).toSorted();
|
||||
const entries = keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`);
|
||||
return `{${entries.join(",")}}`;
|
||||
}
|
||||
@@ -249,9 +249,9 @@ export function createCacheTrace(params: CacheTraceInit): CacheTrace | null {
|
||||
const wrapped: StreamFn = (model, context, options) => {
|
||||
recordStage("stream:context", {
|
||||
model: {
|
||||
id: (model as Model<Api>)?.id,
|
||||
provider: (model as Model<Api>)?.provider,
|
||||
api: (model as Model<Api>)?.api,
|
||||
id: model?.id,
|
||||
provider: model?.provider,
|
||||
api: model?.api,
|
||||
},
|
||||
system: (context as { system?: unknown }).system,
|
||||
messages: (context as { messages?: AgentMessage[] }).messages ?? [],
|
||||
|
||||
@@ -159,7 +159,7 @@ function buildModelAliasLines(cfg?: OpenClawConfig) {
|
||||
entries.push({ alias, model });
|
||||
}
|
||||
return entries
|
||||
.sort((a, b) => a.alias.localeCompare(b.alias))
|
||||
.toSorted((a, b) => a.alias.localeCompare(b.alias))
|
||||
.map((entry) => `- ${entry.alias}: ${entry.model}`);
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ export function normalizeCliModel(modelId: string, backend: CliBackendConfig): s
|
||||
|
||||
function toUsage(raw: Record<string, unknown>): CliUsage | undefined {
|
||||
const pick = (key: string) =>
|
||||
typeof raw[key] === "number" && raw[key] > 0 ? (raw[key] as number) : undefined;
|
||||
typeof raw[key] === "number" && raw[key] > 0 ? raw[key] : undefined;
|
||||
const input = pick("input_tokens") ?? pick("inputTokens");
|
||||
const output = pick("output_tokens") ?? pick("outputTokens");
|
||||
const cacheRead =
|
||||
|
||||
@@ -128,8 +128,8 @@ describe("pruneHistoryForContextShare", () => {
|
||||
const allIds = [
|
||||
...pruned.droppedMessagesList.map((m) => m.timestamp),
|
||||
...pruned.messages.map((m) => m.timestamp),
|
||||
].sort((a, b) => a - b);
|
||||
const originalIds = messages.map((m) => m.timestamp).sort((a, b) => a - b);
|
||||
].toSorted((a, b) => a - b);
|
||||
const originalIds = messages.map((m) => m.timestamp).toSorted((a, b) => a - b);
|
||||
expect(allIds).toEqual(originalIds);
|
||||
});
|
||||
|
||||
|
||||
@@ -34,13 +34,11 @@ function resolveProviderConfig(
|
||||
const matched = Object.entries(providers).find(
|
||||
([key]) => normalizeProviderId(key) === normalized,
|
||||
);
|
||||
return matched?.[1] as ModelProviderConfig | undefined;
|
||||
return matched?.[1];
|
||||
}
|
||||
return (
|
||||
(providers[normalized] as ModelProviderConfig | undefined) ??
|
||||
(Object.entries(providers).find(([key]) => normalizeProviderId(key) === normalized)?.[1] as
|
||||
| ModelProviderConfig
|
||||
| undefined)
|
||||
Object.entries(providers).find(([key]) => normalizeProviderId(key) === normalized)?.[1]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +83,7 @@ export async function loadModelCatalog(params?: {
|
||||
? entry.contextWindow
|
||||
: undefined;
|
||||
const reasoning = typeof entry?.reasoning === "boolean" ? entry.reasoning : undefined;
|
||||
const input = Array.isArray(entry?.input)
|
||||
? (entry.input as Array<"text" | "image">)
|
||||
: undefined;
|
||||
const input = Array.isArray(entry?.input) ? entry.input : undefined;
|
||||
models.push({ id, name, provider, contextWindow, reasoning, input });
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export function normalizeModelCompat(model: Model<Api>): Model<Api> {
|
||||
const isZai = model.provider === "zai" || baseUrl.includes("api.z.ai");
|
||||
if (!isZai || !isOpenAiCompletionsModel(model)) return model;
|
||||
|
||||
const openaiModel = model as Model<"openai-completions">;
|
||||
const openaiModel = model;
|
||||
const compat = openaiModel.compat ?? undefined;
|
||||
if (compat?.supportsDeveloperRole === false) return model;
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ export async function runWithModelFallback<T>(params: {
|
||||
let lastError: unknown;
|
||||
|
||||
for (let i = 0; i < candidates.length; i += 1) {
|
||||
const candidate = candidates[i] as ModelCandidate;
|
||||
const candidate = candidates[i];
|
||||
if (authStore) {
|
||||
const profileIds = resolveAuthProfileOrder({
|
||||
cfg: params.cfg,
|
||||
@@ -330,7 +330,7 @@ export async function runWithImageModelFallback<T>(params: {
|
||||
let lastError: unknown;
|
||||
|
||||
for (let i = 0; i < candidates.length; i += 1) {
|
||||
const candidate = candidates[i] as ModelCandidate;
|
||||
const candidate = candidates[i];
|
||||
try {
|
||||
const result = await params.run(candidate.provider, candidate.model);
|
||||
return {
|
||||
|
||||
@@ -325,7 +325,7 @@ async function mapWithConcurrency<T, R>(
|
||||
opts?: { onProgress?: (completed: number, total: number) => void },
|
||||
): Promise<R[]> {
|
||||
const limit = Math.max(1, Math.floor(concurrency));
|
||||
const results = Array.from({ length: items.length }) as R[];
|
||||
const results = Array.from({ length: items.length });
|
||||
let nextIndex = 0;
|
||||
let completed = 0;
|
||||
|
||||
@@ -334,7 +334,7 @@ async function mapWithConcurrency<T, R>(
|
||||
const current = nextIndex;
|
||||
nextIndex += 1;
|
||||
if (current >= items.length) return;
|
||||
results[current] = await fn(items[current] as T, current);
|
||||
results[current] = await fn(items[current], current);
|
||||
completed += 1;
|
||||
opts?.onProgress?.(completed, items.length);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ export async function ensureOpenClawModelsJson(
|
||||
const cfg = config ?? loadConfig();
|
||||
const agentDir = agentDirOverride?.trim() ? agentDirOverride.trim() : resolveOpenClawAgentDir();
|
||||
|
||||
const explicitProviders = (cfg.models?.providers ?? {}) as Record<string, ProviderConfig>;
|
||||
const explicitProviders = cfg.models?.providers ?? {};
|
||||
const implicitProviders = await resolveImplicitProviders({ agentDir });
|
||||
const providers: Record<string, ProviderConfig> = mergeProviders({
|
||||
implicit: implicitProviders,
|
||||
|
||||
@@ -167,7 +167,7 @@ describeLive("live models (profile keys)", () => {
|
||||
const agentDir = resolveOpenClawAgentDir();
|
||||
const authStorage = discoverAuthStorage(agentDir);
|
||||
const modelRegistry = discoverModels(authStorage, agentDir);
|
||||
const models = modelRegistry.getAll() as Array<Model<Api>>;
|
||||
const models = modelRegistry.getAll();
|
||||
|
||||
const rawModels = process.env.OPENCLAW_LIVE_MODELS?.trim();
|
||||
const useModern = rawModels === "modern" || rawModels === "all";
|
||||
|
||||
@@ -160,7 +160,7 @@ function stableStringify(value: unknown): string {
|
||||
return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
|
||||
}
|
||||
const record = value as Record<string, unknown>;
|
||||
const keys = Object.keys(record).sort();
|
||||
const keys = Object.keys(record).toSorted();
|
||||
const entries = keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`);
|
||||
return `{${entries.join(",")}}`;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export async function sanitizeSessionMessagesImages(
|
||||
const toolMsg = msg as Extract<AgentMessage, { role: "toolResult" }>;
|
||||
const content = Array.isArray(toolMsg.content) ? toolMsg.content : [];
|
||||
const nextContent = (await sanitizeContentBlocksImages(
|
||||
content as ContentBlock[],
|
||||
content,
|
||||
label,
|
||||
)) as unknown as typeof toolMsg.content;
|
||||
out.push({ ...toolMsg, content: nextContent });
|
||||
|
||||
@@ -87,16 +87,16 @@ export function downgradeOpenAIReasoningBlocks(messages: AgentMessage[]): AgentM
|
||||
}
|
||||
const record = block as OpenAIThinkingBlock;
|
||||
if (record.type !== "thinking") {
|
||||
nextContent.push(block as AssistantContentBlock);
|
||||
nextContent.push(block);
|
||||
continue;
|
||||
}
|
||||
const signature = parseOpenAIReasoningSignature(record.thinkingSignature);
|
||||
if (!signature) {
|
||||
nextContent.push(block as AssistantContentBlock);
|
||||
nextContent.push(block);
|
||||
continue;
|
||||
}
|
||||
if (hasFollowingNonThinkingBlock(assistantMsg.content, i)) {
|
||||
nextContent.push(block as AssistantContentBlock);
|
||||
nextContent.push(block);
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
|
||||
@@ -65,7 +65,7 @@ function createStreamFnWithExtraParams(
|
||||
|
||||
const underlying = baseStreamFn ?? streamSimple;
|
||||
const wrappedStreamFn: StreamFn = (model, context, options) =>
|
||||
underlying(model as Model<Api>, context, {
|
||||
underlying(model, context, {
|
||||
...streamParams,
|
||||
...options,
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ function sanitizeAntigravityThinkingBlocks(messages: AgentMessage[]): AgentMessa
|
||||
out.push(msg);
|
||||
continue;
|
||||
}
|
||||
const assistant = msg as Extract<AgentMessage, { role: "assistant" }>;
|
||||
const assistant = msg;
|
||||
if (!Array.isArray(assistant.content)) {
|
||||
out.push(msg);
|
||||
continue;
|
||||
|
||||
@@ -46,7 +46,7 @@ export function buildModelAliasLines(cfg?: OpenClawConfig) {
|
||||
entries.push({ alias, model });
|
||||
}
|
||||
return entries
|
||||
.sort((a, b) => a.alias.localeCompare(b.alias))
|
||||
.toSorted((a, b) => a.alias.localeCompare(b.alias))
|
||||
.map((entry) => `- ${entry.alias}: ${entry.model}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,11 +82,7 @@ vi.mock("../defaults.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../failover-error.js", () => ({
|
||||
FailoverError: class extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
}
|
||||
},
|
||||
FailoverError: class extends Error {},
|
||||
resolveFailoverStatus: vi.fn(),
|
||||
}));
|
||||
|
||||
|
||||
@@ -854,7 +854,7 @@ export async function runEmbeddedAttempt(
|
||||
|
||||
const lastAssistant = messagesSnapshot
|
||||
.slice()
|
||||
.reverse()
|
||||
.toReversed()
|
||||
.find((m) => (m as AgentMessage)?.role === "assistant") as AssistantMessage | undefined;
|
||||
|
||||
const toolMetasNormalized = toolMetas
|
||||
|
||||
@@ -162,7 +162,7 @@ export function handleMessageEnd(
|
||||
const msg = evt.message;
|
||||
if (msg?.role !== "assistant") return;
|
||||
|
||||
const assistantMessage = msg as AssistantMessage;
|
||||
const assistantMessage = msg;
|
||||
promoteThinkingTagsToBlocks(assistantMessage);
|
||||
|
||||
const rawText = extractAssistantText(assistantMessage);
|
||||
|
||||
@@ -14,7 +14,7 @@ export function setCompactionSafeguardRuntime(
|
||||
return;
|
||||
}
|
||||
|
||||
const key = sessionManager as object;
|
||||
const key = sessionManager;
|
||||
if (value === null) {
|
||||
REGISTRY.delete(key);
|
||||
return;
|
||||
@@ -30,5 +30,5 @@ export function getCompactionSafeguardRuntime(
|
||||
return null;
|
||||
}
|
||||
|
||||
return REGISTRY.get(sessionManager as object) ?? null;
|
||||
return REGISTRY.get(sessionManager) ?? null;
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ function computeFileLists(fileOps: FileOperations): {
|
||||
modifiedFiles: string[];
|
||||
} {
|
||||
const modified = new Set([...fileOps.edited, ...fileOps.written]);
|
||||
const readFiles = [...fileOps.read].filter((f) => !modified.has(f)).sort();
|
||||
const modifiedFiles = [...modified].sort();
|
||||
const readFiles = [...fileOps.read].filter((f) => !modified.has(f)).toSorted();
|
||||
const modifiedFiles = [...modified].toSorted();
|
||||
return { readFiles, modifiedFiles };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { ContextEvent, ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
import { pruneContextMessages } from "./pruner.js";
|
||||
@@ -21,7 +20,7 @@ export default function contextPruningExtension(api: ExtensionAPI): void {
|
||||
}
|
||||
|
||||
const next = pruneContextMessages({
|
||||
messages: event.messages as AgentMessage[],
|
||||
messages: event.messages,
|
||||
settings: runtime.settings,
|
||||
ctx,
|
||||
isToolPrunable: runtime.isToolPrunable,
|
||||
|
||||
@@ -20,7 +20,7 @@ export function setContextPruningRuntime(
|
||||
return;
|
||||
}
|
||||
|
||||
const key = sessionManager as object;
|
||||
const key = sessionManager;
|
||||
if (value === null) {
|
||||
REGISTRY.delete(key);
|
||||
return;
|
||||
@@ -36,5 +36,5 @@ export function getContextPruningRuntime(
|
||||
return null;
|
||||
}
|
||||
|
||||
return REGISTRY.get(sessionManager as object) ?? null;
|
||||
return REGISTRY.get(sessionManager) ?? null;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
|
||||
label: tool.label ?? name,
|
||||
description: tool.description ?? "",
|
||||
// biome-ignore lint/suspicious/noExplicitAny: TypeBox schema from pi-agent-core uses a different module instance.
|
||||
parameters: tool.parameters as any,
|
||||
parameters: tool.parameters,
|
||||
execute: async (
|
||||
toolCallId,
|
||||
params,
|
||||
|
||||
@@ -117,7 +117,7 @@ describe("createOpenClawCodingTools", () => {
|
||||
return {
|
||||
name: tool.name,
|
||||
type: schema?.type,
|
||||
keys: schema ? Object.keys(schema).sort() : null,
|
||||
keys: schema ? Object.keys(schema).toSorted() : null,
|
||||
};
|
||||
})
|
||||
.filter((entry) => entry.type !== "object");
|
||||
|
||||
@@ -272,11 +272,7 @@ export function createOpenClawReadTool(base: AnyAgentTool): AnyAgentTool {
|
||||
normalized ??
|
||||
(params && typeof params === "object" ? (params as Record<string, unknown>) : undefined);
|
||||
assertRequiredParams(record, CLAUDE_PARAM_GROUPS.read, base.name);
|
||||
const result = (await base.execute(
|
||||
toolCallId,
|
||||
normalized ?? params,
|
||||
signal,
|
||||
)) as AgentToolResult<unknown>;
|
||||
const result = await base.execute(toolCallId, normalized ?? params, signal);
|
||||
const filePath = typeof record?.path === "string" ? String(record.path) : "<unknown>";
|
||||
const normalizedResult = await normalizeReadImageResult(result, filePath);
|
||||
return sanitizeToolResultImages(normalizedResult, `read:${filePath}`);
|
||||
|
||||
@@ -247,7 +247,7 @@ export function createOpenClawCodingTools(options?: {
|
||||
// Wrap with param normalization for Claude Code compatibility
|
||||
return [wrapToolParamNormalization(createEditTool(workspaceRoot), CLAUDE_PARAM_GROUPS.edit)];
|
||||
}
|
||||
return [tool as AnyAgentTool];
|
||||
return [tool];
|
||||
});
|
||||
const { cleanupMs: cleanupMsOverride, ...execDefaults } = options?.exec ?? {};
|
||||
const execTool = createExecTool({
|
||||
@@ -338,13 +338,13 @@ export function createOpenClawCodingTools(options?: {
|
||||
];
|
||||
const coreToolNames = new Set(
|
||||
tools
|
||||
.filter((tool) => !getPluginToolMeta(tool as AnyAgentTool))
|
||||
.filter((tool) => !getPluginToolMeta(tool))
|
||||
.map((tool) => normalizeToolName(tool.name))
|
||||
.filter(Boolean),
|
||||
);
|
||||
const pluginGroups = buildPluginToolGroups({
|
||||
tools,
|
||||
toolMeta: (tool) => getPluginToolMeta(tool as AnyAgentTool),
|
||||
toolMeta: (tool) => getPluginToolMeta(tool),
|
||||
});
|
||||
const resolvePolicy = (policy: typeof profilePolicy, label: string) => {
|
||||
const resolved = stripPluginOnlyAllowlist(policy, pluginGroups, coreToolNames);
|
||||
|
||||
@@ -20,14 +20,14 @@ function normalizeForHash(value: unknown): unknown {
|
||||
.filter((item): item is unknown => item !== undefined);
|
||||
const primitives = normalized.filter(isPrimitive);
|
||||
if (primitives.length === normalized.length) {
|
||||
return [...primitives].sort((a, b) =>
|
||||
return [...primitives].toSorted((a, b) =>
|
||||
primitiveToString(a).localeCompare(primitiveToString(b)),
|
||||
);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
if (value && typeof value === "object") {
|
||||
const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
|
||||
const entries = Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b));
|
||||
const normalized: Record<string, unknown> = {};
|
||||
for (const [key, entryValue] of entries) {
|
||||
const next = normalizeForHash(entryValue);
|
||||
|
||||
@@ -95,7 +95,7 @@ export function repairToolUseResultPairing(messages: AgentMessage[]): ToolUseRep
|
||||
};
|
||||
|
||||
for (let i = 0; i < messages.length; i += 1) {
|
||||
const msg = messages[i] as AgentMessage;
|
||||
const msg = messages[i];
|
||||
if (!msg || typeof msg !== "object") {
|
||||
out.push(msg);
|
||||
continue;
|
||||
@@ -129,7 +129,7 @@ export function repairToolUseResultPairing(messages: AgentMessage[]): ToolUseRep
|
||||
|
||||
let j = i + 1;
|
||||
for (; j < messages.length; j += 1) {
|
||||
const next = messages[j] as AgentMessage;
|
||||
const next = messages[j];
|
||||
if (!next || typeof next !== "object") {
|
||||
remainder.push(next);
|
||||
continue;
|
||||
|
||||
@@ -57,7 +57,7 @@ describe("buildWorkspaceSkillCommandSpecs", () => {
|
||||
reservedNames: new Set(["help"]),
|
||||
});
|
||||
|
||||
const names = commands.map((entry) => entry.name).sort();
|
||||
const names = commands.map((entry) => entry.name).toSorted();
|
||||
expect(names).toEqual(["hello_world", "hello_world_2", "help_2"]);
|
||||
expect(commands.find((entry) => entry.skillName === "hidden-skill")).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -62,7 +62,7 @@ describe("buildWorkspaceSkillSnapshot", () => {
|
||||
|
||||
expect(snapshot.prompt).toContain("visible-skill");
|
||||
expect(snapshot.prompt).not.toContain("hidden-skill");
|
||||
expect(snapshot.skills.map((skill) => skill.name).sort()).toEqual([
|
||||
expect(snapshot.skills.map((skill) => skill.name).toSorted()).toEqual([
|
||||
"hidden-skill",
|
||||
"visible-skill",
|
||||
]);
|
||||
|
||||
@@ -39,7 +39,7 @@ export function resolveSkillsInstallPreferences(config?: OpenClawConfig) {
|
||||
const manager = managerRaw.toLowerCase();
|
||||
const nodeManager =
|
||||
manager === "pnpm" || manager === "yarn" || manager === "bun" || manager === "npm"
|
||||
? (manager as "npm" | "pnpm" | "yarn" | "bun")
|
||||
? manager
|
||||
: "npm";
|
||||
return { preferBrew, nodeManager };
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export function resolveConfigPath(config: OpenClawConfig | undefined, pathStr: s
|
||||
export function isConfigPathTruthy(config: OpenClawConfig | undefined, pathStr: string): boolean {
|
||||
const value = resolveConfigPath(config, pathStr);
|
||||
if (value === undefined && pathStr in DEFAULT_CONFIG_VALUES) {
|
||||
return DEFAULT_CONFIG_VALUES[pathStr] === true;
|
||||
return DEFAULT_CONFIG_VALUES[pathStr];
|
||||
}
|
||||
return isTruthy(value);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ function parseInstallSpec(input: unknown): SkillInstallSpec | undefined {
|
||||
}
|
||||
|
||||
const spec: SkillInstallSpec = {
|
||||
kind: kind as SkillInstallSpec["kind"],
|
||||
kind: kind,
|
||||
};
|
||||
|
||||
if (typeof raw.id === "string") spec.id = raw.id;
|
||||
@@ -78,7 +78,7 @@ export function resolveOpenClawMetadata(
|
||||
const raw = getFrontmatterValue(frontmatter, "metadata");
|
||||
if (!raw) return undefined;
|
||||
try {
|
||||
const parsed = JSON5.parse(raw) as Record<string, unknown>;
|
||||
const parsed = JSON5.parse(raw);
|
||||
if (!parsed || typeof parsed !== "object") return undefined;
|
||||
const metadataRawCandidates = [MANIFEST_KEY, ...LEGACY_MANIFEST_KEYS];
|
||||
let metadataRaw: unknown;
|
||||
|
||||
@@ -327,19 +327,14 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
let outcome: SubagentRunOutcome | undefined = params.outcome;
|
||||
if (!reply && params.waitForCompletion !== false) {
|
||||
const waitMs = Math.min(params.timeoutMs, 60_000);
|
||||
const wait = (await callGateway({
|
||||
const wait = await callGateway({
|
||||
method: "agent.wait",
|
||||
params: {
|
||||
runId: params.childRunId,
|
||||
timeoutMs: waitMs,
|
||||
},
|
||||
timeoutMs: waitMs + 2000,
|
||||
})) as {
|
||||
status?: string;
|
||||
error?: string;
|
||||
startedAt?: number;
|
||||
endedAt?: number;
|
||||
};
|
||||
});
|
||||
if (wait?.status === "timeout") {
|
||||
outcome = { status: "timeout" };
|
||||
} else if (wait?.status === "error") {
|
||||
|
||||
@@ -170,8 +170,7 @@ function ensureListener() {
|
||||
}
|
||||
const phase = evt.data?.phase;
|
||||
if (phase === "start") {
|
||||
const startedAt =
|
||||
typeof evt.data?.startedAt === "number" ? (evt.data.startedAt as number) : undefined;
|
||||
const startedAt = typeof evt.data?.startedAt === "number" ? evt.data.startedAt : undefined;
|
||||
if (startedAt) {
|
||||
entry.startedAt = startedAt;
|
||||
persistSubagentRuns();
|
||||
@@ -179,11 +178,10 @@ function ensureListener() {
|
||||
return;
|
||||
}
|
||||
if (phase !== "end" && phase !== "error") return;
|
||||
const endedAt =
|
||||
typeof evt.data?.endedAt === "number" ? (evt.data.endedAt as number) : Date.now();
|
||||
const endedAt = typeof evt.data?.endedAt === "number" ? evt.data.endedAt : Date.now();
|
||||
entry.endedAt = endedAt;
|
||||
if (phase === "error") {
|
||||
const error = typeof evt.data?.error === "string" ? (evt.data.error as string) : undefined;
|
||||
const error = typeof evt.data?.error === "string" ? evt.data.error : undefined;
|
||||
entry.outcome = { status: "error", error };
|
||||
} else {
|
||||
entry.outcome = { status: "ok" };
|
||||
@@ -284,14 +282,14 @@ export function registerSubagentRun(params: {
|
||||
async function waitForSubagentCompletion(runId: string, waitTimeoutMs: number) {
|
||||
try {
|
||||
const timeoutMs = Math.max(1, Math.floor(waitTimeoutMs));
|
||||
const wait = (await callGateway({
|
||||
const wait = await callGateway({
|
||||
method: "agent.wait",
|
||||
params: {
|
||||
runId,
|
||||
timeoutMs,
|
||||
},
|
||||
timeoutMs: timeoutMs + 10_000,
|
||||
})) as { status?: string; startedAt?: number; endedAt?: number; error?: string };
|
||||
});
|
||||
if (wait?.status !== "ok" && wait?.status !== "error") return;
|
||||
const entry = subagentRuns.get(runId);
|
||||
if (!entry) return;
|
||||
|
||||
@@ -264,7 +264,7 @@ export function buildAgentSystemPrompt(params: {
|
||||
const name = resolveToolName(tool);
|
||||
return summary ? `- ${name}: ${summary}` : `- ${name}`;
|
||||
});
|
||||
for (const tool of extraTools.sort()) {
|
||||
for (const tool of extraTools.toSorted()) {
|
||||
const summary = coreToolSummaries[tool] ?? externalToolSummaries.get(tool);
|
||||
const name = resolveToolName(tool);
|
||||
toolLines.push(summary ? `- ${name}: ${summary}` : `- ${name}`);
|
||||
|
||||
@@ -91,7 +91,7 @@ async function resizeImageBase64IfNeeded(params: {
|
||||
const sideGrid = [sideStart, 1800, 1600, 1400, 1200, 1000, 800]
|
||||
.map((v) => Math.min(params.maxDimensionPx, v))
|
||||
.filter((v, i, arr) => v > 0 && arr.indexOf(v) === i)
|
||||
.sort((a, b) => b - a);
|
||||
.toSorted((a, b) => b - a);
|
||||
|
||||
let smallest: { buffer: Buffer; size: number } | null = null;
|
||||
for (const side of sideGrid) {
|
||||
@@ -191,7 +191,7 @@ export async function sanitizeImageBlocks(
|
||||
): Promise<{ images: ImageContent[]; dropped: number }> {
|
||||
if (images.length === 0) return { images, dropped: 0 };
|
||||
const sanitized = await sanitizeContentBlocksImages(images as ToolContentBlock[], label, opts);
|
||||
const next = sanitized.filter(isImageBlock) as ImageContent[];
|
||||
const next = sanitized.filter(isImageBlock);
|
||||
return { images: next, dropped: Math.max(0, images.length - next.length) };
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ export async function readLatestAssistantReply(params: {
|
||||
sessionKey: string;
|
||||
limit?: number;
|
||||
}): Promise<string | undefined> {
|
||||
const history = (await callGateway({
|
||||
const history = await callGateway({
|
||||
method: "chat.history",
|
||||
params: { sessionKey: params.sessionKey, limit: params.limit ?? 50 },
|
||||
})) as { messages?: unknown[] };
|
||||
});
|
||||
const filtered = stripToolMessages(Array.isArray(history?.messages) ? history.messages : []);
|
||||
const last = filtered.length > 0 ? filtered[filtered.length - 1] : undefined;
|
||||
return last ? extractAssistantText(last) : undefined;
|
||||
@@ -27,7 +27,7 @@ export async function runAgentStep(params: {
|
||||
lane?: string;
|
||||
}): Promise<string | undefined> {
|
||||
const stepIdem = crypto.randomUUID();
|
||||
const response = (await callGateway({
|
||||
const response = await callGateway({
|
||||
method: "agent",
|
||||
params: {
|
||||
message: params.message,
|
||||
@@ -39,19 +39,19 @@ export async function runAgentStep(params: {
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
},
|
||||
timeoutMs: 10_000,
|
||||
})) as { runId?: string; acceptedAt?: number };
|
||||
});
|
||||
|
||||
const stepRunId = typeof response?.runId === "string" && response.runId ? response.runId : "";
|
||||
const resolvedRunId = stepRunId || stepIdem;
|
||||
const stepWaitMs = Math.min(params.timeoutMs, 60_000);
|
||||
const wait = (await callGateway({
|
||||
const wait = await callGateway({
|
||||
method: "agent.wait",
|
||||
params: {
|
||||
runId: resolvedRunId,
|
||||
timeoutMs: stepWaitMs,
|
||||
},
|
||||
timeoutMs: stepWaitMs + 2000,
|
||||
})) as { status?: string };
|
||||
});
|
||||
if (wait?.status !== "ok") return undefined;
|
||||
return await readLatestAssistantReply({ sessionKey: params.sessionKey });
|
||||
}
|
||||
|
||||
@@ -72,7 +72,9 @@ export function createAgentsListTool(opts?: {
|
||||
}
|
||||
|
||||
const all = Array.from(allowed);
|
||||
const rest = all.filter((id) => id !== requesterAgentId).sort((a, b) => a.localeCompare(b));
|
||||
const rest = all
|
||||
.filter((id) => id !== requesterAgentId)
|
||||
.toSorted((a, b) => a.localeCompare(b));
|
||||
const ordered = [requesterAgentId, ...rest];
|
||||
const agents: AgentListEntry[] = ordered.map((id) => ({
|
||||
id,
|
||||
|
||||
@@ -93,7 +93,7 @@ async function resolveBrowserNodeTarget(params: {
|
||||
|
||||
if (params.target === "node") {
|
||||
if (browserNodes.length === 1) {
|
||||
const node = browserNodes[0]!;
|
||||
const node = browserNodes[0];
|
||||
return { nodeId: node.nodeId, label: node.displayName ?? node.remoteIp ?? node.nodeId };
|
||||
}
|
||||
throw new Error(
|
||||
@@ -104,7 +104,7 @@ async function resolveBrowserNodeTarget(params: {
|
||||
if (mode === "manual") return null;
|
||||
|
||||
if (browserNodes.length === 1) {
|
||||
const node = browserNodes[0]!;
|
||||
const node = browserNodes[0];
|
||||
return { nodeId: node.nodeId, label: node.displayName ?? node.remoteIp ?? node.nodeId };
|
||||
}
|
||||
return null;
|
||||
@@ -123,7 +123,7 @@ async function callBrowserProxy(params: {
|
||||
typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs)
|
||||
? Math.max(1, Math.floor(params.timeoutMs))
|
||||
: DEFAULT_BROWSER_PROXY_TIMEOUT_MS;
|
||||
const payload = (await callGatewayTool(
|
||||
const payload = await callGatewayTool(
|
||||
"node.invoke",
|
||||
{ timeoutMs: gatewayTimeoutMs },
|
||||
{
|
||||
@@ -139,11 +139,7 @@ async function callBrowserProxy(params: {
|
||||
},
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
},
|
||||
)) as {
|
||||
ok?: boolean;
|
||||
payload?: BrowserProxyResult;
|
||||
payloadJSON?: string | null;
|
||||
};
|
||||
);
|
||||
const parsed =
|
||||
payload?.payload ??
|
||||
(typeof payload?.payloadJSON === "string" && payload.payloadJSON
|
||||
@@ -414,7 +410,7 @@ export function createBrowserTool(opts?: {
|
||||
const snapshotDefaults = loadConfig().browser?.snapshotDefaults;
|
||||
const format =
|
||||
params.snapshotFormat === "ai" || params.snapshotFormat === "aria"
|
||||
? (params.snapshotFormat as "ai" | "aria")
|
||||
? params.snapshotFormat
|
||||
: "ai";
|
||||
const mode =
|
||||
params.mode === "efficient"
|
||||
@@ -697,10 +693,12 @@ export function createBrowserTool(opts?: {
|
||||
if (!tabs.length) {
|
||||
throw new Error(
|
||||
"No Chrome tabs are attached via the OpenClaw Browser Relay extension. Click the toolbar icon on the tab you want to control (badge ON), then retry.",
|
||||
{ cause: err },
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Chrome tab not found (stale targetId?). Run action=tabs profile="chrome" and use one of the returned targetIds.`,
|
||||
{ cause: err },
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
|
||||
@@ -103,10 +103,10 @@ async function buildReminderContextLines(params: {
|
||||
const { mainKey, alias } = resolveMainSessionAlias(cfg);
|
||||
const resolvedKey = resolveInternalSessionKey({ key: sessionKey, alias, mainKey });
|
||||
try {
|
||||
const res = (await callGatewayTool("chat.history", params.gatewayOpts, {
|
||||
const res = await callGatewayTool("chat.history", params.gatewayOpts, {
|
||||
sessionKey: resolvedKey,
|
||||
limit: maxMessages,
|
||||
})) as { messages?: unknown[] };
|
||||
});
|
||||
const messages = Array.isArray(res?.messages) ? res.messages : [];
|
||||
const parsed = messages
|
||||
.map((msg) => extractMessageText(msg as ChatMessage))
|
||||
|
||||
@@ -267,10 +267,10 @@ async function runImagePrompt(params: {
|
||||
}
|
||||
|
||||
const context = buildImageContext(params.prompt, params.base64, params.mimeType);
|
||||
const message = (await complete(model, context, {
|
||||
const message = await complete(model, context, {
|
||||
apiKey,
|
||||
maxTokens: 512,
|
||||
})) as AssistantMessage;
|
||||
});
|
||||
const text = coerceImageAssistantText({
|
||||
message,
|
||||
provider: model.provider,
|
||||
|
||||
@@ -307,7 +307,7 @@ function buildMessageToolDescription(options?: {
|
||||
if (channelActions.length > 0) {
|
||||
// Always include "send" as a base action
|
||||
const allActions = new Set(["send", ...channelActions]);
|
||||
const actionList = Array.from(allActions).sort().join(", ");
|
||||
const actionList = Array.from(allActions).toSorted().join(", ");
|
||||
return `${baseDescription} Current channel (${options.currentChannel}) supports: ${actionList}.`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ export function createNodesTool(options?: {
|
||||
const details: Array<Record<string, unknown>> = [];
|
||||
|
||||
for (const facing of facings) {
|
||||
const raw = (await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
nodeId,
|
||||
command: "camera.snap",
|
||||
params: {
|
||||
@@ -211,7 +211,7 @@ export function createNodesTool(options?: {
|
||||
deviceId,
|
||||
},
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
})) as { payload?: unknown };
|
||||
});
|
||||
const payload = parseCameraSnapPayload(raw?.payload);
|
||||
const normalizedFormat = payload.format.toLowerCase();
|
||||
if (
|
||||
@@ -250,12 +250,12 @@ export function createNodesTool(options?: {
|
||||
case "camera_list": {
|
||||
const node = readStringParam(params, "node", { required: true });
|
||||
const nodeId = await resolveNodeId(gatewayOpts, node);
|
||||
const raw = (await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
nodeId,
|
||||
command: "camera.list",
|
||||
params: {},
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
})) as { payload?: unknown };
|
||||
});
|
||||
const payload =
|
||||
raw && typeof raw.payload === "object" && raw.payload !== null ? raw.payload : {};
|
||||
return jsonResult(payload);
|
||||
@@ -280,7 +280,7 @@ export function createNodesTool(options?: {
|
||||
typeof params.deviceId === "string" && params.deviceId.trim()
|
||||
? params.deviceId.trim()
|
||||
: undefined;
|
||||
const raw = (await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
nodeId,
|
||||
command: "camera.clip",
|
||||
params: {
|
||||
@@ -291,7 +291,7 @@ export function createNodesTool(options?: {
|
||||
deviceId,
|
||||
},
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
})) as { payload?: unknown };
|
||||
});
|
||||
const payload = parseCameraClipPayload(raw?.payload);
|
||||
const filePath = cameraTempPath({
|
||||
kind: "clip",
|
||||
@@ -326,7 +326,7 @@ export function createNodesTool(options?: {
|
||||
: 0;
|
||||
const includeAudio =
|
||||
typeof params.includeAudio === "boolean" ? params.includeAudio : true;
|
||||
const raw = (await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
nodeId,
|
||||
command: "screen.record",
|
||||
params: {
|
||||
@@ -337,7 +337,7 @@ export function createNodesTool(options?: {
|
||||
includeAudio,
|
||||
},
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
})) as { payload?: unknown };
|
||||
});
|
||||
const payload = parseScreenRecordPayload(raw?.payload);
|
||||
const filePath =
|
||||
typeof params.outPath === "string" && params.outPath.trim()
|
||||
@@ -373,7 +373,7 @@ export function createNodesTool(options?: {
|
||||
Number.isFinite(params.locationTimeoutMs)
|
||||
? params.locationTimeoutMs
|
||||
: undefined;
|
||||
const raw = (await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
nodeId,
|
||||
command: "location.get",
|
||||
params: {
|
||||
@@ -382,7 +382,7 @@ export function createNodesTool(options?: {
|
||||
timeoutMs: locationTimeoutMs,
|
||||
},
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
})) as { payload?: unknown };
|
||||
});
|
||||
return jsonResult(raw?.payload ?? {});
|
||||
}
|
||||
case "run": {
|
||||
@@ -423,7 +423,7 @@ export function createNodesTool(options?: {
|
||||
typeof params.needsScreenRecording === "boolean"
|
||||
? params.needsScreenRecording
|
||||
: undefined;
|
||||
const raw = (await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
nodeId,
|
||||
command: "system.run",
|
||||
params: {
|
||||
@@ -437,7 +437,7 @@ export function createNodesTool(options?: {
|
||||
},
|
||||
timeoutMs: invokeTimeoutMs,
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
})) as { payload?: unknown };
|
||||
});
|
||||
return jsonResult(raw?.payload ?? {});
|
||||
}
|
||||
default:
|
||||
@@ -454,6 +454,7 @@ export function createNodesTool(options?: {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(
|
||||
`agent=${agentLabel} node=${nodeLabel} gateway=${gatewayLabel} action=${action}: ${message}`,
|
||||
{ cause: err },
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -71,10 +71,10 @@ function normalizeNodeKey(value: string) {
|
||||
|
||||
async function loadNodes(opts: GatewayCallOptions): Promise<NodeListNode[]> {
|
||||
try {
|
||||
const res = (await callGatewayTool("node.list", opts, {})) as unknown;
|
||||
const res = await callGatewayTool("node.list", opts, {});
|
||||
return parseNodeList(res);
|
||||
} catch {
|
||||
const res = (await callGatewayTool("node.pair.list", opts, {})) as unknown;
|
||||
const res = await callGatewayTool("node.pair.list", opts, {});
|
||||
const { paired } = parsePairingList(res);
|
||||
return paired.map((n) => ({
|
||||
nodeId: n.nodeId,
|
||||
|
||||
@@ -20,14 +20,14 @@ export async function resolveAnnounceTarget(params: {
|
||||
}
|
||||
|
||||
try {
|
||||
const list = (await callGateway({
|
||||
const list = await callGateway({
|
||||
method: "sessions.list",
|
||||
params: {
|
||||
includeGlobal: true,
|
||||
includeUnknown: true,
|
||||
limit: 200,
|
||||
},
|
||||
})) as { sessions?: Array<Record<string, unknown>> };
|
||||
});
|
||||
const sessions = Array.isArray(list?.sessions) ? list.sessions : [];
|
||||
const match =
|
||||
sessions.find((entry) => entry?.key === params.sessionKey) ??
|
||||
|
||||
@@ -135,7 +135,7 @@ async function resolveSessionKeyFromSessionId(params: {
|
||||
}): Promise<SessionReferenceResolution> {
|
||||
try {
|
||||
// Resolve via gateway so we respect store routing and visibility rules.
|
||||
const result = (await callGateway({
|
||||
const result = await callGateway({
|
||||
method: "sessions.resolve",
|
||||
params: {
|
||||
sessionId: params.sessionId,
|
||||
@@ -143,7 +143,7 @@ async function resolveSessionKeyFromSessionId(params: {
|
||||
includeGlobal: !params.restrictToSpawned,
|
||||
includeUnknown: !params.restrictToSpawned,
|
||||
},
|
||||
})) as { key?: unknown };
|
||||
});
|
||||
const key = typeof result?.key === "string" ? result.key.trim() : "";
|
||||
if (!key) {
|
||||
throw new Error(
|
||||
@@ -188,13 +188,13 @@ async function resolveSessionKeyFromKey(params: {
|
||||
}): Promise<SessionReferenceResolution | null> {
|
||||
try {
|
||||
// Try key-based resolution first so non-standard keys keep working.
|
||||
const result = (await callGateway({
|
||||
const result = await callGateway({
|
||||
method: "sessions.resolve",
|
||||
params: {
|
||||
key: params.key,
|
||||
spawnedBy: params.restrictToSpawned ? params.requesterInternalKey : undefined,
|
||||
},
|
||||
})) as { key?: unknown };
|
||||
});
|
||||
const key = typeof result?.key === "string" ? result.key.trim() : "";
|
||||
if (!key) return null;
|
||||
return {
|
||||
|
||||
@@ -28,7 +28,7 @@ async function isSpawnedSessionAllowed(params: {
|
||||
targetSessionKey: string;
|
||||
}): Promise<boolean> {
|
||||
try {
|
||||
const list = (await callGateway({
|
||||
const list = await callGateway({
|
||||
method: "sessions.list",
|
||||
params: {
|
||||
includeGlobal: false,
|
||||
@@ -36,7 +36,7 @@ async function isSpawnedSessionAllowed(params: {
|
||||
limit: 500,
|
||||
spawnedBy: params.requesterSessionKey,
|
||||
},
|
||||
})) as { sessions?: Array<Record<string, unknown>> };
|
||||
});
|
||||
const sessions = Array.isArray(list?.sessions) ? list.sessions : [];
|
||||
return sessions.some((entry) => entry?.key === params.targetSessionKey);
|
||||
} catch {
|
||||
@@ -126,10 +126,10 @@ export function createSessionsHistoryTool(opts?: {
|
||||
? Math.max(1, Math.floor(params.limit))
|
||||
: undefined;
|
||||
const includeTools = Boolean(params.includeTools);
|
||||
const result = (await callGateway({
|
||||
const result = await callGateway({
|
||||
method: "chat.history",
|
||||
params: { sessionKey: resolvedKey, limit },
|
||||
})) as { messages?: unknown[] };
|
||||
});
|
||||
const rawMessages = Array.isArray(result?.messages) ? result.messages : [];
|
||||
const messages = includeTools ? rawMessages : stripToolMessages(rawMessages);
|
||||
return jsonResult({
|
||||
|
||||
@@ -79,7 +79,7 @@ export function createSessionsListTool(opts?: {
|
||||
: 0;
|
||||
const messageLimit = Math.min(messageLimitRaw, 20);
|
||||
|
||||
const list = (await callGateway({
|
||||
const list = await callGateway({
|
||||
method: "sessions.list",
|
||||
params: {
|
||||
limit,
|
||||
@@ -88,10 +88,7 @@ export function createSessionsListTool(opts?: {
|
||||
includeUnknown: !restrictToSpawned,
|
||||
spawnedBy: restrictToSpawned ? requesterInternalKey : undefined,
|
||||
},
|
||||
})) as {
|
||||
path?: string;
|
||||
sessions?: Array<Record<string, unknown>>;
|
||||
};
|
||||
});
|
||||
|
||||
const sessions = Array.isArray(list?.sessions) ? list.sessions : [];
|
||||
const storePath = typeof list?.path === "string" ? list.path : undefined;
|
||||
@@ -187,10 +184,10 @@ export function createSessionsListTool(opts?: {
|
||||
alias,
|
||||
mainKey,
|
||||
});
|
||||
const history = (await callGateway({
|
||||
const history = await callGateway({
|
||||
method: "chat.history",
|
||||
params: { sessionKey: resolvedKey, limit: messageLimit },
|
||||
})) as { messages?: unknown[] };
|
||||
});
|
||||
const rawMessages = Array.isArray(history?.messages) ? history.messages : [];
|
||||
const filtered = stripToolMessages(rawMessages);
|
||||
row.messages = filtered.length > messageLimit ? filtered.slice(-messageLimit) : filtered;
|
||||
|
||||
@@ -33,14 +33,14 @@ export async function runSessionsSendA2AFlow(params: {
|
||||
let latestReply = params.roundOneReply;
|
||||
if (!primaryReply && params.waitRunId) {
|
||||
const waitMs = Math.min(params.announceTimeoutMs, 60_000);
|
||||
const wait = (await callGateway({
|
||||
const wait = await callGateway({
|
||||
method: "agent.wait",
|
||||
params: {
|
||||
runId: params.waitRunId,
|
||||
timeoutMs: waitMs,
|
||||
},
|
||||
timeoutMs: waitMs + 2000,
|
||||
})) as { status?: string };
|
||||
});
|
||||
if (wait?.status === "ok") {
|
||||
primaryReply = await readLatestAssistantReply({
|
||||
sessionKey: params.targetSessionKey,
|
||||
|
||||
@@ -81,11 +81,11 @@ export function createSessionsSendTool(opts?: {
|
||||
}
|
||||
|
||||
const listSessions = async (listParams: Record<string, unknown>) => {
|
||||
const result = (await callGateway({
|
||||
const result = await callGateway({
|
||||
method: "sessions.list",
|
||||
params: listParams,
|
||||
timeoutMs: 10_000,
|
||||
})) as { sessions?: Array<Record<string, unknown>> };
|
||||
});
|
||||
return Array.isArray(result?.sessions) ? result.sessions : [];
|
||||
};
|
||||
|
||||
@@ -136,11 +136,11 @@ export function createSessionsSendTool(opts?: {
|
||||
};
|
||||
let resolvedKey = "";
|
||||
try {
|
||||
const resolved = (await callGateway({
|
||||
const resolved = await callGateway({
|
||||
method: "sessions.resolve",
|
||||
params: resolveParams,
|
||||
timeoutMs: 10_000,
|
||||
})) as { key?: unknown };
|
||||
});
|
||||
resolvedKey = typeof resolved?.key === "string" ? resolved.key.trim() : "";
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
@@ -283,11 +283,11 @@ export function createSessionsSendTool(opts?: {
|
||||
|
||||
if (timeoutSeconds === 0) {
|
||||
try {
|
||||
const response = (await callGateway({
|
||||
const response = await callGateway({
|
||||
method: "agent",
|
||||
params: sendParams,
|
||||
timeoutMs: 10_000,
|
||||
})) as { runId?: string; acceptedAt?: number };
|
||||
});
|
||||
if (typeof response?.runId === "string" && response.runId) {
|
||||
runId = response.runId;
|
||||
}
|
||||
@@ -311,11 +311,11 @@ export function createSessionsSendTool(opts?: {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = (await callGateway({
|
||||
const response = await callGateway({
|
||||
method: "agent",
|
||||
params: sendParams,
|
||||
timeoutMs: 10_000,
|
||||
})) as { runId?: string; acceptedAt?: number };
|
||||
});
|
||||
if (typeof response?.runId === "string" && response.runId) {
|
||||
runId = response.runId;
|
||||
}
|
||||
@@ -333,14 +333,14 @@ export function createSessionsSendTool(opts?: {
|
||||
let waitStatus: string | undefined;
|
||||
let waitError: string | undefined;
|
||||
try {
|
||||
const wait = (await callGateway({
|
||||
const wait = await callGateway({
|
||||
method: "agent.wait",
|
||||
params: {
|
||||
runId,
|
||||
timeoutMs,
|
||||
},
|
||||
timeoutMs: timeoutMs + 2000,
|
||||
})) as { status?: string; error?: string };
|
||||
});
|
||||
waitStatus = typeof wait?.status === "string" ? wait.status : undefined;
|
||||
waitError = typeof wait?.error === "string" ? wait.error : undefined;
|
||||
} catch (err) {
|
||||
@@ -371,10 +371,10 @@ export function createSessionsSendTool(opts?: {
|
||||
});
|
||||
}
|
||||
|
||||
const history = (await callGateway({
|
||||
const history = await callGateway({
|
||||
method: "chat.history",
|
||||
params: { sessionKey: resolvedKey, limit: 50 },
|
||||
})) as { messages?: unknown[] };
|
||||
});
|
||||
const filtered = stripToolMessages(Array.isArray(history?.messages) ? history.messages : []);
|
||||
const last = filtered.length > 0 ? filtered[filtered.length - 1] : undefined;
|
||||
const reply = last ? extractAssistantText(last) : undefined;
|
||||
|
||||
@@ -84,9 +84,7 @@ export function createSessionsSpawnTool(opts?: {
|
||||
const modelOverride = readStringParam(params, "model");
|
||||
const thinkingOverrideRaw = readStringParam(params, "thinking");
|
||||
const cleanup =
|
||||
params.cleanup === "keep" || params.cleanup === "delete"
|
||||
? (params.cleanup as "keep" | "delete")
|
||||
: "keep";
|
||||
params.cleanup === "keep" || params.cleanup === "delete" ? params.cleanup : "keep";
|
||||
const requesterOrigin = normalizeDeliveryContext({
|
||||
channel: opts?.agentChannel,
|
||||
accountId: opts?.agentAccountId,
|
||||
@@ -211,7 +209,7 @@ export function createSessionsSpawnTool(opts?: {
|
||||
const childIdem = crypto.randomUUID();
|
||||
let childRunId: string = childIdem;
|
||||
try {
|
||||
const response = (await callGateway({
|
||||
const response = await callGateway({
|
||||
method: "agent",
|
||||
params: {
|
||||
message: task,
|
||||
@@ -230,7 +228,7 @@ export function createSessionsSpawnTool(opts?: {
|
||||
groupSpace: opts?.agentGroupSpace ?? undefined,
|
||||
},
|
||||
timeoutMs: 10_000,
|
||||
})) as { runId?: string };
|
||||
});
|
||||
if (typeof response?.runId === "string" && response.runId) {
|
||||
childRunId = response.runId;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ function formatZonedTimestamp(date: Date, timeZone?: string): string | undefined
|
||||
const hh = pick("hour");
|
||||
const min = pick("minute");
|
||||
const tz = [...parts]
|
||||
.reverse()
|
||||
.toReversed()
|
||||
.find((part) => part.type === "timeZoneName")
|
||||
?.value?.trim();
|
||||
if (!yyyy || !mm || !dd || !hh || !min) return undefined;
|
||||
|
||||
@@ -38,7 +38,7 @@ function formatListTop(
|
||||
entries: Array<{ name: string; value: number }>,
|
||||
cap: number,
|
||||
): { lines: string[]; omitted: number } {
|
||||
const sorted = [...entries].sort((a, b) => b.value - a.value);
|
||||
const sorted = [...entries].toSorted((a, b) => b.value - a.value);
|
||||
const top = sorted.slice(0, cap);
|
||||
const omitted = Math.max(0, sorted.length - top.length);
|
||||
const lines = top.map((e) => `- ${e.name}: ${formatCharsAndTokens(e.value)}`);
|
||||
@@ -263,7 +263,7 @@ export async function buildContextReply(params: HandleCommandsParams): Promise<R
|
||||
);
|
||||
const toolPropsLines = report.tools.entries
|
||||
.filter((t) => t.propertiesCount != null)
|
||||
.sort((a, b) => (b.propertiesCount ?? 0) - (a.propertiesCount ?? 0))
|
||||
.toSorted((a, b) => (b.propertiesCount ?? 0) - (a.propertiesCount ?? 0))
|
||||
.slice(0, 30)
|
||||
.map((t) => `- ${t.name}: ${t.propertiesCount} params`);
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ export async function resolveModelsCommandReply(params: {
|
||||
add(resolvedDefault.provider, resolvedDefault.model);
|
||||
addModelConfigEntries();
|
||||
|
||||
const providers = [...byProvider.keys()].sort();
|
||||
const providers = [...byProvider.keys()].toSorted();
|
||||
|
||||
if (!provider) {
|
||||
const lines: string[] = [
|
||||
@@ -181,7 +181,7 @@ export async function resolveModelsCommandReply(params: {
|
||||
return { text: lines.join("\n") };
|
||||
}
|
||||
|
||||
const models = [...(byProvider.get(provider) ?? new Set<string>())].sort();
|
||||
const models = [...(byProvider.get(provider) ?? new Set<string>())].toSorted();
|
||||
const total = models.length;
|
||||
|
||||
if (total === 0) {
|
||||
|
||||
@@ -289,7 +289,7 @@ export const handleStopCommand: CommandHandler = async (params, allowTextCommand
|
||||
params.sessionStore[abortTarget.key] = abortTarget.entry;
|
||||
if (params.storePath) {
|
||||
await updateSessionStore(params.storePath, (store) => {
|
||||
store[abortTarget.key] = abortTarget.entry as SessionEntry;
|
||||
store[abortTarget.key] = abortTarget.entry;
|
||||
});
|
||||
}
|
||||
} else if (params.command.abortKey) {
|
||||
@@ -336,7 +336,7 @@ export const handleAbortTrigger: CommandHandler = async (params, allowTextComman
|
||||
params.sessionStore[abortTarget.key] = abortTarget.entry;
|
||||
if (params.storePath) {
|
||||
await updateSessionStore(params.storePath, (store) => {
|
||||
store[abortTarget.key] = abortTarget.entry as SessionEntry;
|
||||
store[abortTarget.key] = abortTarget.entry;
|
||||
});
|
||||
}
|
||||
} else if (params.command.abortKey) {
|
||||
|
||||
@@ -316,10 +316,10 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo
|
||||
reply: { text: `⚠️ ${resolved.error ?? "Unknown subagent."}` },
|
||||
};
|
||||
}
|
||||
const history = (await callGateway({
|
||||
const history = await callGateway({
|
||||
method: "chat.history",
|
||||
params: { sessionKey: resolved.entry.childSessionKey, limit },
|
||||
})) as { messages?: unknown[] };
|
||||
});
|
||||
const rawMessages = Array.isArray(history?.messages) ? history.messages : [];
|
||||
const filtered = includeTools ? rawMessages : stripToolMessages(rawMessages);
|
||||
const lines = formatLogLines(filtered as ChatMessage[]);
|
||||
@@ -349,7 +349,7 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo
|
||||
const idempotencyKey = crypto.randomUUID();
|
||||
let runId: string = idempotencyKey;
|
||||
try {
|
||||
const response = (await callGateway({
|
||||
const response = await callGateway({
|
||||
method: "agent",
|
||||
params: {
|
||||
message,
|
||||
@@ -360,7 +360,7 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo
|
||||
lane: AGENT_LANE_SUBAGENT,
|
||||
},
|
||||
timeoutMs: 10_000,
|
||||
})) as { runId?: string };
|
||||
});
|
||||
if (response?.runId) runId = response.runId;
|
||||
} catch (err) {
|
||||
const messageText =
|
||||
@@ -369,11 +369,11 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo
|
||||
}
|
||||
|
||||
const waitMs = 30_000;
|
||||
const wait = (await callGateway({
|
||||
const wait = await callGateway({
|
||||
method: "agent.wait",
|
||||
params: { runId, timeoutMs: waitMs },
|
||||
timeoutMs: waitMs + 2000,
|
||||
})) as { status?: string; error?: string };
|
||||
});
|
||||
if (wait?.status === "timeout") {
|
||||
return {
|
||||
shouldContinue: false,
|
||||
@@ -389,10 +389,10 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo
|
||||
};
|
||||
}
|
||||
|
||||
const history = (await callGateway({
|
||||
const history = await callGateway({
|
||||
method: "chat.history",
|
||||
params: { sessionKey: resolved.entry.childSessionKey, limit: 50 },
|
||||
})) as { messages?: unknown[] };
|
||||
});
|
||||
const filtered = stripToolMessages(Array.isArray(history?.messages) ? history.messages : []);
|
||||
const last = filtered.length > 0 ? filtered[filtered.length - 1] : undefined;
|
||||
const replyText = last ? extractAssistantText(last) : undefined;
|
||||
|
||||
@@ -54,8 +54,7 @@ function resolveExecDefaults(params: {
|
||||
(agentExec?.ask as ExecAsk | undefined) ??
|
||||
(globalExec?.ask as ExecAsk | undefined) ??
|
||||
"on-miss",
|
||||
node:
|
||||
(params.sessionEntry?.execNode as string | undefined) ?? agentExec?.node ?? globalExec?.node,
|
||||
node: params.sessionEntry?.execNode ?? agentExec?.node ?? globalExec?.node,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -196,8 +196,8 @@ export async function applyInlineDirectiveOverrides(params: {
|
||||
model,
|
||||
contextTokens,
|
||||
resolvedThinkLevel: resolvedDefaultThinkLevel,
|
||||
resolvedVerboseLevel: (currentVerboseLevel ?? "off") as VerboseLevel,
|
||||
resolvedReasoningLevel: (currentReasoningLevel ?? "off") as ReasoningLevel,
|
||||
resolvedVerboseLevel: currentVerboseLevel ?? "off",
|
||||
resolvedReasoningLevel: currentReasoningLevel ?? "off",
|
||||
resolvedElevatedLevel,
|
||||
resolveDefaultThinkingLevel: async () => resolvedDefaultThinkLevel,
|
||||
isGroup,
|
||||
|
||||
@@ -339,20 +339,20 @@ export async function resolveReplyDirectives(params: {
|
||||
});
|
||||
const defaultActivation = defaultGroupActivation(requireMention);
|
||||
const resolvedThinkLevel =
|
||||
(directives.thinkLevel as ThinkLevel | undefined) ??
|
||||
directives.thinkLevel ??
|
||||
(sessionEntry?.thinkingLevel as ThinkLevel | undefined) ??
|
||||
(agentCfg?.thinkingDefault as ThinkLevel | undefined);
|
||||
|
||||
const resolvedVerboseLevel =
|
||||
(directives.verboseLevel as VerboseLevel | undefined) ??
|
||||
directives.verboseLevel ??
|
||||
(sessionEntry?.verboseLevel as VerboseLevel | undefined) ??
|
||||
(agentCfg?.verboseDefault as VerboseLevel | undefined);
|
||||
const resolvedReasoningLevel: ReasoningLevel =
|
||||
(directives.reasoningLevel as ReasoningLevel | undefined) ??
|
||||
directives.reasoningLevel ??
|
||||
(sessionEntry?.reasoningLevel as ReasoningLevel | undefined) ??
|
||||
"off";
|
||||
const resolvedElevatedLevel = elevatedAllowed
|
||||
? ((directives.elevatedLevel as ElevatedLevel | undefined) ??
|
||||
? (directives.elevatedLevel ??
|
||||
(sessionEntry?.elevatedLevel as ElevatedLevel | undefined) ??
|
||||
(agentCfg?.elevatedDefault as ElevatedLevel | undefined) ??
|
||||
"on")
|
||||
|
||||
@@ -50,7 +50,7 @@ export function resolveGroupRequireMention(params: {
|
||||
}
|
||||
|
||||
export function defaultGroupActivation(requireMention: boolean): "always" | "mention" {
|
||||
return requireMention === false ? "always" : "mention";
|
||||
return !requireMention ? "always" : "mention";
|
||||
}
|
||||
|
||||
export function buildGroupIntro(params: {
|
||||
|
||||
@@ -436,7 +436,7 @@ export function resolveModelDirectiveSelection(params: {
|
||||
});
|
||||
return Object.assign({ candidate }, details);
|
||||
})
|
||||
.sort((a, b) => {
|
||||
.toSorted((a, b) => {
|
||||
if (b.score !== a.score) return b.score - a.score;
|
||||
if (a.isDefault !== b.isDefault) return a.isDefault ? -1 : 1;
|
||||
if (a.variantMatchCount !== b.variantMatchCount)
|
||||
|
||||
@@ -87,7 +87,7 @@ export async function prependSystemEvents(params: {
|
||||
const min = pick("minute");
|
||||
const sec = pick("second");
|
||||
const tz = [...parts]
|
||||
.reverse()
|
||||
.toReversed()
|
||||
.find((part) => part.type === "timeZoneName")
|
||||
?.value?.trim();
|
||||
if (!yyyy || !mm || !dd || !hh || !min || !sec) return undefined;
|
||||
|
||||
@@ -235,10 +235,9 @@ export async function initSessionState(params: {
|
||||
const baseEntry = !isNewSession && freshEntry ? entry : undefined;
|
||||
// Track the originating channel/to for announce routing (subagent announce-back).
|
||||
const lastChannelRaw = (ctx.OriginatingChannel as string | undefined) || baseEntry?.lastChannel;
|
||||
const lastToRaw = (ctx.OriginatingTo as string | undefined) || ctx.To || baseEntry?.lastTo;
|
||||
const lastAccountIdRaw = (ctx.AccountId as string | undefined) || baseEntry?.lastAccountId;
|
||||
const lastThreadIdRaw =
|
||||
(ctx.MessageThreadId as string | number | undefined) || baseEntry?.lastThreadId;
|
||||
const lastToRaw = ctx.OriginatingTo || ctx.To || baseEntry?.lastTo;
|
||||
const lastAccountIdRaw = ctx.AccountId || baseEntry?.lastAccountId;
|
||||
const lastThreadIdRaw = ctx.MessageThreadId || baseEntry?.lastThreadId;
|
||||
const deliveryFields = normalizeSessionDeliveryFields({
|
||||
deliveryContext: {
|
||||
channel: lastChannelRaw,
|
||||
|
||||
@@ -42,7 +42,7 @@ export function formatRunStatus(entry: SubagentRunRecord) {
|
||||
}
|
||||
|
||||
export function sortSubagentRuns(runs: SubagentRunRecord[]) {
|
||||
return [...runs].sort((a, b) => {
|
||||
return [...runs].toSorted((a, b) => {
|
||||
const aTime = a.startedAt ?? a.createdAt ?? 0;
|
||||
const bTime = b.startedAt ?? b.createdAt ?? 0;
|
||||
return bTime - aTime;
|
||||
|
||||
@@ -296,7 +296,7 @@ export async function snapshotDom(opts: {
|
||||
awaitPromise: true,
|
||||
returnByValue: true,
|
||||
});
|
||||
const value = evaluated.result?.value as unknown;
|
||||
const value = evaluated.result?.value;
|
||||
if (!value || typeof value !== "object") return { nodes: [] };
|
||||
const nodes = (value as { nodes?: unknown }).nodes;
|
||||
return { nodes: Array.isArray(nodes) ? (nodes as DomSnapshotNode[]) : [] };
|
||||
|
||||
@@ -458,7 +458,7 @@ export async function ensureChromeExtensionRelayServer(opts: {
|
||||
if ("error" in parsed && typeof parsed.error === "string" && parsed.error.trim()) {
|
||||
pending.reject(new Error(parsed.error));
|
||||
} else {
|
||||
pending.resolve((parsed as ExtensionResponseMessage).result);
|
||||
pending.resolve(parsed.result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -183,18 +183,18 @@ describe("color allocation", () => {
|
||||
|
||||
it("allocates next unused color from palette", () => {
|
||||
// biome-ignore lint/style/noNonNullAssertion: Test file with known array
|
||||
const usedColors = new Set([PROFILE_COLORS[0]!.toUpperCase()]);
|
||||
const usedColors = new Set([PROFILE_COLORS[0].toUpperCase()]);
|
||||
expect(allocateColor(usedColors)).toBe(PROFILE_COLORS[1]);
|
||||
});
|
||||
|
||||
it("skips multiple used colors", () => {
|
||||
const usedColors = new Set([
|
||||
// biome-ignore lint/style/noNonNullAssertion: Test file with known array
|
||||
PROFILE_COLORS[0]!.toUpperCase(),
|
||||
PROFILE_COLORS[0].toUpperCase(),
|
||||
// biome-ignore lint/style/noNonNullAssertion: Test file with known array
|
||||
PROFILE_COLORS[1]!.toUpperCase(),
|
||||
PROFILE_COLORS[1].toUpperCase(),
|
||||
// biome-ignore lint/style/noNonNullAssertion: Test file with known array
|
||||
PROFILE_COLORS[2]!.toUpperCase(),
|
||||
PROFILE_COLORS[2].toUpperCase(),
|
||||
]);
|
||||
expect(allocateColor(usedColors)).toBe(PROFILE_COLORS[3]);
|
||||
});
|
||||
|
||||
@@ -91,7 +91,7 @@ export function allocateColor(usedColors: Set<string>): string {
|
||||
// All colors used, cycle based on count
|
||||
const index = usedColors.size % PROFILE_COLORS.length;
|
||||
// biome-ignore lint/style/noNonNullAssertion: Array is non-empty constant
|
||||
return PROFILE_COLORS[index] ?? PROFILE_COLORS[0]!;
|
||||
return PROFILE_COLORS[index] ?? PROFILE_COLORS[0];
|
||||
}
|
||||
|
||||
export function getUsedColors(
|
||||
|
||||
@@ -84,7 +84,7 @@ describe("pw-role-snapshot", () => {
|
||||
expect(res.snapshot).toContain('- button "Save"');
|
||||
expect(res.snapshot).not.toContain("navigation");
|
||||
expect(res.snapshot).not.toContain("heading");
|
||||
expect(Object.keys(res.refs).sort()).toEqual(["e5", "e7"]);
|
||||
expect(Object.keys(res.refs).toSorted()).toEqual(["e5", "e7"]);
|
||||
expect(res.refs.e5).toMatchObject({ role: "link", name: "Home" });
|
||||
expect(res.refs.e7).toMatchObject({ role: "button", name: "Save" });
|
||||
});
|
||||
|
||||
@@ -55,13 +55,13 @@ describe("pw-tools-core", () => {
|
||||
() =>
|
||||
new Promise((r) => {
|
||||
resolve1 = r;
|
||||
}) as Promise<unknown>,
|
||||
}),
|
||||
)
|
||||
.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((r) => {
|
||||
resolve2 = r;
|
||||
}) as Promise<unknown>,
|
||||
}),
|
||||
);
|
||||
|
||||
currentPage = {
|
||||
|
||||
@@ -93,7 +93,7 @@ export async function responseBodyViaPlaywright(opts: {
|
||||
bodyText = new TextDecoder("utf-8").decode(buf);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to read response body for "${url}": ${String(err)}`);
|
||||
throw new Error(`Failed to read response body for "${url}": ${String(err)}`, { cause: err });
|
||||
}
|
||||
|
||||
const trimmed = bodyText.length > maxChars ? bodyText.slice(0, maxChars) : bodyText;
|
||||
|
||||
@@ -200,6 +200,6 @@ export async function pdfViaPlaywright(opts: {
|
||||
}): Promise<{ buffer: Buffer }> {
|
||||
const page = await getPageForTargetId(opts);
|
||||
ensurePageState(page);
|
||||
const buffer = await (page as Page).pdf({ printBackground: true });
|
||||
const buffer = await page.pdf({ printBackground: true });
|
||||
return { buffer };
|
||||
}
|
||||
|
||||
@@ -136,7 +136,8 @@ export async function setTimezoneViaPlaywright(opts: {
|
||||
} catch (err) {
|
||||
const msg = String(err);
|
||||
if (msg.includes("Timezone override is already in effect")) return;
|
||||
if (msg.includes("Invalid timezone")) throw new Error(`Invalid timezone ID: ${timezoneId}`);
|
||||
if (msg.includes("Invalid timezone"))
|
||||
throw new Error(`Invalid timezone ID: ${timezoneId}`, { cause: err });
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -207,7 +207,7 @@ export function registerBrowserAgentActRoutes(
|
||||
loadStateRaw === "load" ||
|
||||
loadStateRaw === "domcontentloaded" ||
|
||||
loadStateRaw === "networkidle"
|
||||
? (loadStateRaw as "load" | "domcontentloaded" | "networkidle")
|
||||
? loadStateRaw
|
||||
: undefined;
|
||||
const fn = toStringOrEmpty(body.fn) || undefined;
|
||||
const timeoutMs = toNumber(body.timeoutMs) ?? undefined;
|
||||
|
||||
@@ -53,7 +53,7 @@ export function registerBrowserAgentStorageRoutes(
|
||||
secure: toBoolean(cookie.secure) ?? undefined,
|
||||
sameSite:
|
||||
cookie.sameSite === "Lax" || cookie.sameSite === "None" || cookie.sameSite === "Strict"
|
||||
? (cookie.sameSite as "Lax" | "None" | "Strict")
|
||||
? cookie.sameSite
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
@@ -270,7 +270,7 @@ export function registerBrowserAgentStorageRoutes(
|
||||
const schemeRaw = toStringOrEmpty(body.colorScheme);
|
||||
const colorScheme =
|
||||
schemeRaw === "dark" || schemeRaw === "light" || schemeRaw === "no-preference"
|
||||
? (schemeRaw as "dark" | "light" | "no-preference")
|
||||
? schemeRaw
|
||||
: schemeRaw === "none"
|
||||
? null
|
||||
: undefined;
|
||||
|
||||
@@ -27,7 +27,7 @@ export async function normalizeBrowserScreenshot(
|
||||
const sideGrid = [sideStart, 1800, 1600, 1400, 1200, 1000, 800]
|
||||
.map((v) => Math.min(maxSide, v))
|
||||
.filter((v, i, arr) => v > 0 && arr.indexOf(v) === i)
|
||||
.sort((a, b) => b - a);
|
||||
.toSorted((a, b) => b - a);
|
||||
|
||||
let smallest: { buffer: Buffer; size: number } | null = null;
|
||||
|
||||
|
||||
@@ -286,11 +286,11 @@ describe("browser control server", () => {
|
||||
async () => {
|
||||
const base = await startServerAndBase();
|
||||
|
||||
const select = (await postJson(`${base}/act`, {
|
||||
const select = await postJson(`${base}/act`, {
|
||||
kind: "select",
|
||||
ref: "5",
|
||||
values: ["a", "b"],
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(select.ok).toBe(true);
|
||||
expect(pwMocks.selectOptionViaPlaywright).toHaveBeenCalledWith({
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -299,10 +299,10 @@ describe("browser control server", () => {
|
||||
values: ["a", "b"],
|
||||
});
|
||||
|
||||
const fill = (await postJson(`${base}/act`, {
|
||||
const fill = await postJson(`${base}/act`, {
|
||||
kind: "fill",
|
||||
fields: [{ ref: "6", type: "textbox", value: "hello" }],
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(fill.ok).toBe(true);
|
||||
expect(pwMocks.fillFormViaPlaywright).toHaveBeenCalledWith({
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -310,11 +310,11 @@ describe("browser control server", () => {
|
||||
fields: [{ ref: "6", type: "textbox", value: "hello" }],
|
||||
});
|
||||
|
||||
const resize = (await postJson(`${base}/act`, {
|
||||
const resize = await postJson(`${base}/act`, {
|
||||
kind: "resize",
|
||||
width: 800,
|
||||
height: 600,
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(resize.ok).toBe(true);
|
||||
expect(pwMocks.resizeViewportViaPlaywright).toHaveBeenCalledWith({
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -323,10 +323,10 @@ describe("browser control server", () => {
|
||||
height: 600,
|
||||
});
|
||||
|
||||
const wait = (await postJson(`${base}/act`, {
|
||||
const wait = await postJson(`${base}/act`, {
|
||||
kind: "wait",
|
||||
timeMs: 5,
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(wait.ok).toBe(true);
|
||||
expect(pwMocks.waitForViaPlaywright).toHaveBeenCalledWith({
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -336,10 +336,10 @@ describe("browser control server", () => {
|
||||
textGone: undefined,
|
||||
});
|
||||
|
||||
const evalRes = (await postJson(`${base}/act`, {
|
||||
const evalRes = await postJson(`${base}/act`, {
|
||||
kind: "evaluate",
|
||||
fn: "() => 1",
|
||||
})) as { ok: boolean; result?: unknown };
|
||||
});
|
||||
expect(evalRes.ok).toBe(true);
|
||||
expect(evalRes.result).toBe("ok");
|
||||
expect(pwMocks.evaluateViaPlaywright).toHaveBeenCalledWith({
|
||||
@@ -358,17 +358,17 @@ describe("browser control server", () => {
|
||||
cfgEvaluateEnabled = false;
|
||||
const base = await startServerAndBase();
|
||||
|
||||
const waitRes = (await postJson(`${base}/act`, {
|
||||
const waitRes = await postJson(`${base}/act`, {
|
||||
kind: "wait",
|
||||
fn: "() => window.ready === true",
|
||||
})) as { error?: string };
|
||||
});
|
||||
expect(waitRes.error).toContain("browser.evaluateEnabled=false");
|
||||
expect(pwMocks.waitForViaPlaywright).not.toHaveBeenCalled();
|
||||
|
||||
const res = (await postJson(`${base}/act`, {
|
||||
const res = await postJson(`${base}/act`, {
|
||||
kind: "evaluate",
|
||||
fn: "() => 1",
|
||||
})) as { error?: string };
|
||||
});
|
||||
|
||||
expect(res.error).toContain("browser.evaluateEnabled=false");
|
||||
expect(pwMocks.evaluateViaPlaywright).not.toHaveBeenCalled();
|
||||
@@ -441,17 +441,14 @@ describe("browser control server", () => {
|
||||
expect(consoleRes.ok).toBe(true);
|
||||
expect(Array.isArray(consoleRes.messages)).toBe(true);
|
||||
|
||||
const pdf = (await postJson(`${base}/pdf`, {})) as {
|
||||
ok: boolean;
|
||||
path?: string;
|
||||
};
|
||||
const pdf = await postJson(`${base}/pdf`, {});
|
||||
expect(pdf.ok).toBe(true);
|
||||
expect(typeof pdf.path).toBe("string");
|
||||
|
||||
const shot = (await postJson(`${base}/screenshot`, {
|
||||
const shot = await postJson(`${base}/screenshot`, {
|
||||
element: "body",
|
||||
type: "jpeg",
|
||||
})) as { ok: boolean; path?: string };
|
||||
});
|
||||
expect(shot.ok).toBe(true);
|
||||
expect(typeof shot.path).toBe("string");
|
||||
});
|
||||
|
||||
@@ -306,9 +306,9 @@ describe("browser control server", () => {
|
||||
it("agent contract: navigation + common act commands", async () => {
|
||||
const base = await startServerAndBase();
|
||||
|
||||
const nav = (await postJson(`${base}/navigate`, {
|
||||
const nav = await postJson(`${base}/navigate`, {
|
||||
url: "https://example.com",
|
||||
})) as { ok: boolean; targetId?: string };
|
||||
});
|
||||
expect(nav.ok).toBe(true);
|
||||
expect(typeof nav.targetId).toBe("string");
|
||||
expect(pwMocks.navigateViaPlaywright).toHaveBeenCalledWith({
|
||||
@@ -317,12 +317,12 @@ describe("browser control server", () => {
|
||||
url: "https://example.com",
|
||||
});
|
||||
|
||||
const click = (await postJson(`${base}/act`, {
|
||||
const click = await postJson(`${base}/act`, {
|
||||
kind: "click",
|
||||
ref: "1",
|
||||
button: "left",
|
||||
modifiers: ["Shift"],
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(click.ok).toBe(true);
|
||||
expect(pwMocks.clickViaPlaywright).toHaveBeenNthCalledWith(1, {
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -343,11 +343,11 @@ describe("browser control server", () => {
|
||||
/'selector' is not supported/i,
|
||||
);
|
||||
|
||||
const type = (await postJson(`${base}/act`, {
|
||||
const type = await postJson(`${base}/act`, {
|
||||
kind: "type",
|
||||
ref: "1",
|
||||
text: "",
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(type.ok).toBe(true);
|
||||
expect(pwMocks.typeViaPlaywright).toHaveBeenNthCalledWith(1, {
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -358,10 +358,10 @@ describe("browser control server", () => {
|
||||
slowly: false,
|
||||
});
|
||||
|
||||
const press = (await postJson(`${base}/act`, {
|
||||
const press = await postJson(`${base}/act`, {
|
||||
kind: "press",
|
||||
key: "Enter",
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(press.ok).toBe(true);
|
||||
expect(pwMocks.pressKeyViaPlaywright).toHaveBeenCalledWith({
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -369,10 +369,10 @@ describe("browser control server", () => {
|
||||
key: "Enter",
|
||||
});
|
||||
|
||||
const hover = (await postJson(`${base}/act`, {
|
||||
const hover = await postJson(`${base}/act`, {
|
||||
kind: "hover",
|
||||
ref: "2",
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(hover.ok).toBe(true);
|
||||
expect(pwMocks.hoverViaPlaywright).toHaveBeenCalledWith({
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -380,10 +380,10 @@ describe("browser control server", () => {
|
||||
ref: "2",
|
||||
});
|
||||
|
||||
const scroll = (await postJson(`${base}/act`, {
|
||||
const scroll = await postJson(`${base}/act`, {
|
||||
kind: "scrollIntoView",
|
||||
ref: "2",
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(scroll.ok).toBe(true);
|
||||
expect(pwMocks.scrollIntoViewViaPlaywright).toHaveBeenCalledWith({
|
||||
cdpUrl: cdpBaseUrl,
|
||||
@@ -391,11 +391,11 @@ describe("browser control server", () => {
|
||||
ref: "2",
|
||||
});
|
||||
|
||||
const drag = (await postJson(`${base}/act`, {
|
||||
const drag = await postJson(`${base}/act`, {
|
||||
kind: "drag",
|
||||
startRef: "3",
|
||||
endRef: "4",
|
||||
})) as { ok: boolean };
|
||||
});
|
||||
expect(drag.ok).toBe(true);
|
||||
expect(pwMocks.dragViaPlaywright).toHaveBeenCalledWith({
|
||||
cdpUrl: cdpBaseUrl,
|
||||
|
||||
@@ -347,7 +347,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
const deleteMessageDays = readNumberParam(actionParams, "deleteDays", {
|
||||
integer: true,
|
||||
});
|
||||
const discordAction = action as "timeout" | "kick" | "ban";
|
||||
const discordAction = action;
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
action: discordAction,
|
||||
|
||||
@@ -268,7 +268,7 @@ export function listChannelPluginCatalogEntries(
|
||||
|
||||
return Array.from(resolved.values())
|
||||
.map(({ entry }) => entry)
|
||||
.sort((a, b) => {
|
||||
.toSorted((a, b) => {
|
||||
const orderA = a.meta.order ?? 999;
|
||||
const orderB = b.meta.order ?? 999;
|
||||
if (orderA !== orderB) return orderA - orderB;
|
||||
|
||||
@@ -30,7 +30,7 @@ export function setAccountEnabledInConfigSection(params: {
|
||||
} as OpenClawConfig;
|
||||
}
|
||||
|
||||
const baseAccounts = (base?.accounts ?? {}) as Record<string, Record<string, unknown>>;
|
||||
const baseAccounts = base?.accounts ?? {};
|
||||
const existing = baseAccounts[accountKey] ?? {};
|
||||
return {
|
||||
...params.cfg,
|
||||
|
||||
@@ -30,7 +30,7 @@ describe("directory (config-backed)", () => {
|
||||
query: null,
|
||||
limit: null,
|
||||
});
|
||||
expect(peers?.map((e) => e.id).sort()).toEqual([
|
||||
expect(peers?.map((e) => e.id).toSorted()).toEqual([
|
||||
"user:u123",
|
||||
"user:u234",
|
||||
"user:u777",
|
||||
@@ -73,7 +73,7 @@ describe("directory (config-backed)", () => {
|
||||
query: null,
|
||||
limit: null,
|
||||
});
|
||||
expect(peers?.map((e) => e.id).sort()).toEqual(["user:111", "user:12345", "user:222"]);
|
||||
expect(peers?.map((e) => e.id).toSorted()).toEqual(["user:111", "user:12345", "user:222"]);
|
||||
|
||||
const groups = await listDiscordDirectoryGroupsFromConfig({
|
||||
cfg,
|
||||
@@ -81,7 +81,7 @@ describe("directory (config-backed)", () => {
|
||||
query: null,
|
||||
limit: null,
|
||||
});
|
||||
expect(groups?.map((e) => e.id).sort()).toEqual(["channel:555", "channel:666"]);
|
||||
expect(groups?.map((e) => e.id).toSorted()).toEqual(["channel:555", "channel:666"]);
|
||||
});
|
||||
|
||||
it("lists Telegram peers/groups from config", async () => {
|
||||
@@ -102,7 +102,7 @@ describe("directory (config-backed)", () => {
|
||||
query: null,
|
||||
limit: null,
|
||||
});
|
||||
expect(peers?.map((e) => e.id).sort()).toEqual(["123", "456", "@alice", "@bob"]);
|
||||
expect(peers?.map((e) => e.id).toSorted()).toEqual(["123", "456", "@alice", "@bob"]);
|
||||
|
||||
const groups = await listTelegramDirectoryGroupsFromConfig({
|
||||
cfg,
|
||||
|
||||
@@ -28,7 +28,7 @@ function dedupeChannels(channels: ChannelPlugin[]): ChannelPlugin[] {
|
||||
|
||||
export function listChannelPlugins(): ChannelPlugin[] {
|
||||
const combined = dedupeChannels(listPluginChannels());
|
||||
return combined.sort((a, b) => {
|
||||
return combined.toSorted((a, b) => {
|
||||
const indexA = CHAT_CHANNEL_ORDER.indexOf(a.id as ChatChannelId);
|
||||
const indexB = CHAT_CHANNEL_ORDER.indexOf(b.id as ChatChannelId);
|
||||
const orderA = a.meta.order ?? (indexA === -1 ? 999 : indexA);
|
||||
|
||||
@@ -33,11 +33,11 @@ export async function promptChannelAccessPolicy(params: {
|
||||
options.push({ value: "disabled", label: "Disabled (block all channels)" });
|
||||
}
|
||||
const initialValue = params.currentPolicy ?? "allowlist";
|
||||
return (await params.prompter.select({
|
||||
return await params.prompter.select({
|
||||
message: `${params.label} access`,
|
||||
options,
|
||||
initialValue,
|
||||
})) as ChannelAccessPolicy;
|
||||
});
|
||||
}
|
||||
|
||||
export async function promptChannelAllowlist(params: {
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { PromptAccountId, PromptAccountIdParams } from "../onboarding-types
|
||||
export const promptAccountId: PromptAccountId = async (params: PromptAccountIdParams) => {
|
||||
const existingIds = params.listAccountIds(params.cfg);
|
||||
const initial = params.currentId?.trim() || params.defaultAccountId || DEFAULT_ACCOUNT_ID;
|
||||
const choice = (await params.prompter.select({
|
||||
const choice = await params.prompter.select({
|
||||
message: `${params.label} account`,
|
||||
options: [
|
||||
...existingIds.map((id) => ({
|
||||
@@ -14,7 +14,7 @@ export const promptAccountId: PromptAccountId = async (params: PromptAccountIdPa
|
||||
{ value: "__new__", label: "Add a new account" },
|
||||
],
|
||||
initialValue: initial,
|
||||
})) as string;
|
||||
});
|
||||
|
||||
if (choice !== "__new__") return normalizeAccountId(choice);
|
||||
|
||||
|
||||
@@ -107,13 +107,13 @@ async function promptWhatsAppAllowFrom(
|
||||
"WhatsApp DM access",
|
||||
);
|
||||
|
||||
const phoneMode = (await prompter.select({
|
||||
const phoneMode = await prompter.select({
|
||||
message: "WhatsApp phone setup",
|
||||
options: [
|
||||
{ value: "personal", label: "This is my personal phone number" },
|
||||
{ value: "separate", label: "Separate phone just for OpenClaw" },
|
||||
],
|
||||
})) as "personal" | "separate";
|
||||
});
|
||||
|
||||
if (phoneMode === "personal") {
|
||||
await prompter.note(
|
||||
@@ -187,13 +187,13 @@ async function promptWhatsAppAllowFrom(
|
||||
{ value: "list", label: "Set allowFrom to specific numbers" },
|
||||
] as const);
|
||||
|
||||
const mode = (await prompter.select({
|
||||
const mode = await prompter.select({
|
||||
message: "WhatsApp allowFrom (optional pre-allowlist)",
|
||||
options: allowOptions.map((opt) => ({
|
||||
value: opt.value,
|
||||
label: opt.label,
|
||||
})),
|
||||
})) as (typeof allowOptions)[number]["value"];
|
||||
});
|
||||
|
||||
if (mode === "keep") {
|
||||
// Keep allowFrom as-is.
|
||||
|
||||
@@ -27,7 +27,7 @@ function getSessionRecipients(cfg: OpenClawConfig) {
|
||||
updatedAt: entry?.updatedAt ?? 0,
|
||||
}))
|
||||
.filter(({ to }) => to.length > 1)
|
||||
.sort((a, b) => b.updatedAt - a.updatedAt);
|
||||
.toSorted((a, b) => b.updatedAt - a.updatedAt);
|
||||
|
||||
// Dedupe while preserving recency ordering.
|
||||
const seen = new Set<string>();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user