fix: align tool schemas and health snapshot

This commit is contained in:
Peter Steinberger
2026-01-01 17:30:19 +01:00
parent 04691ed598
commit 56ea6b6e43
7 changed files with 47 additions and 22 deletions

View File

@@ -127,6 +127,11 @@
"vitest": "^4.0.16", "vitest": "^4.0.16",
"wireit": "^0.14.12" "wireit": "^0.14.12"
}, },
"pnpm": {
"overrides": {
"@sinclair/typebox": "0.34.45"
}
},
"vitest": { "vitest": {
"coverage": { "coverage": {
"provider": "v8", "provider": "v8",

5
pnpm-lock.yaml generated
View File

@@ -4,6 +4,9 @@ settings:
autoInstallPeers: true autoInstallPeers: true
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides:
'@sinclair/typebox': 0.34.45
patchedDependencies: patchedDependencies:
'@mariozechner/pi-ai': '@mariozechner/pi-ai':
hash: bf3e904ebaad236b8c3bb48c7d1150a1463735e783acaab6d15d6cd381b43832 hash: bf3e904ebaad236b8c3bb48c7d1150a1463735e783acaab6d15d6cd381b43832
@@ -29,7 +32,7 @@ importers:
specifier: ^0.30.2 specifier: ^0.30.2
version: 0.30.2(ws@8.18.3)(zod@4.2.1) version: 0.30.2(ws@8.18.3)(zod@4.2.1)
'@sinclair/typebox': '@sinclair/typebox':
specifier: ^0.34.45 specifier: 0.34.45
version: 0.34.45 version: 0.34.45
'@whiskeysockets/baileys': '@whiskeysockets/baileys':
specifier: 7.0.0-rc.9 specifier: 7.0.0-rc.9

View File

@@ -1,7 +1,6 @@
import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process"; import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
import { randomUUID } from "node:crypto"; import { randomUUID } from "node:crypto";
import type { AgentTool, AgentToolResult } from "@mariozechner/pi-ai"; import type { AgentTool, AgentToolResult } from "@mariozechner/pi-ai";
import { StringEnum } from "@mariozechner/pi-ai";
import { Type } from "@sinclair/typebox"; import { Type } from "@sinclair/typebox";
import { import {
@@ -31,6 +30,18 @@ const DEFAULT_MAX_OUTPUT = clampNumber(
150_000, 150_000,
); );
const stringEnum = (
values: readonly string[],
options?: Parameters<typeof Type.Union>[1],
) =>
Type.Union(
values.map((value) => Type.Literal(value)) as [
ReturnType<typeof Type.Literal>,
...ReturnType<typeof Type.Literal>[],
],
options,
);
export type BashToolDefaults = { export type BashToolDefaults = {
backgroundMs?: number; backgroundMs?: number;
timeoutSec?: number; timeoutSec?: number;
@@ -60,7 +71,7 @@ const bashSchema = Type.Object({
}), }),
), ),
stdinMode: Type.Optional( stdinMode: Type.Optional(
StringEnum(["pipe", "pty"] as const, { stringEnum(["pipe", "pty"] as const, {
description: "Only pipe is supported", description: "Only pipe is supported",
}), }),
), ),
@@ -83,7 +94,8 @@ export type BashToolDetails =
export function createBashTool( export function createBashTool(
defaults?: BashToolDefaults, defaults?: BashToolDefaults,
): AgentTool<typeof bashSchema, BashToolDetails> { // biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-ai uses a different module instance.
): AgentTool<any, BashToolDetails> {
const defaultBackgroundMs = clampNumber( const defaultBackgroundMs = clampNumber(
defaults?.backgroundMs ?? readEnvInt("PI_BASH_YIELD_MS"), defaults?.backgroundMs ?? readEnvInt("PI_BASH_YIELD_MS"),
20_000, 20_000,
@@ -329,7 +341,7 @@ export function createBashTool(
export const bashTool = createBashTool(); export const bashTool = createBashTool();
const processSchema = Type.Object({ const processSchema = Type.Object({
action: StringEnum( action: stringEnum(
["list", "poll", "log", "write", "kill", "clear", "remove"] as const, ["list", "poll", "log", "write", "kill", "clear", "remove"] as const,
{ {
description: "Process action", description: "Process action",
@@ -346,7 +358,8 @@ const processSchema = Type.Object({
export function createProcessTool( export function createProcessTool(
defaults?: ProcessToolDefaults, defaults?: ProcessToolDefaults,
): AgentTool<typeof processSchema> { // biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-ai uses a different module instance.
): AgentTool<any> {
if (defaults?.cleanupMs !== undefined) { if (defaults?.cleanupMs !== undefined) {
setJobTtlMs(defaults.cleanupMs); setJobTtlMs(defaults.cleanupMs);
} }

View File

@@ -2,7 +2,7 @@ import crypto from "node:crypto";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import type { AgentTool, AgentToolResult } from "@mariozechner/pi-ai"; import type { AgentTool, AgentToolResult } from "@mariozechner/pi-ai";
import { type TSchema, Type } from "@sinclair/typebox"; import { Type } from "@sinclair/typebox";
import { import {
browserCloseTab, browserCloseTab,
browserFocusTab, browserFocusTab,
@@ -45,7 +45,8 @@ import { callGateway } from "../gateway/call.js";
import { detectMime } from "../media/mime.js"; import { detectMime } from "../media/mime.js";
import { sanitizeToolResultImages } from "./tool-images.js"; import { sanitizeToolResultImages } from "./tool-images.js";
type AnyAgentTool = AgentTool<TSchema, unknown>; // biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-ai uses a different module instance.
type AnyAgentTool = AgentTool<any, unknown>;
const DEFAULT_GATEWAY_URL = "ws://127.0.0.1:18789"; const DEFAULT_GATEWAY_URL = "ws://127.0.0.1:18789";

View File

@@ -1,6 +1,6 @@
import type { AgentTool, AgentToolResult } from "@mariozechner/pi-ai"; import type { AgentTool, AgentToolResult } from "@mariozechner/pi-ai";
import { codingTools, readTool } from "@mariozechner/pi-coding-agent"; import { codingTools, readTool } from "@mariozechner/pi-coding-agent";
import { type TSchema, Type } from "@sinclair/typebox"; import { Type } from "@sinclair/typebox";
import { detectMime } from "../media/mime.js"; import { detectMime } from "../media/mime.js";
import { startWebLoginWithQr, waitForWebLogin } from "../web/login-qr.js"; import { startWebLoginWithQr, waitForWebLogin } from "../web/login-qr.js";
@@ -103,7 +103,8 @@ async function normalizeReadImageResult(
return { ...result, content: nextContent }; return { ...result, content: nextContent };
} }
type AnyAgentTool = AgentTool<TSchema, unknown>; // biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-ai uses a different module instance.
type AnyAgentTool = AgentTool<any, unknown>;
function extractEnumValues(schema: unknown): unknown[] | undefined { function extractEnumValues(schema: unknown): unknown[] | undefined {
if (!schema || typeof schema !== "object") return undefined; if (!schema || typeof schema !== "object") return undefined;
@@ -204,7 +205,7 @@ function normalizeToolParameters(tool: AnyAgentTool): AnyAgentTool {
: {}), : {}),
additionalProperties: additionalProperties:
"additionalProperties" in schema ? schema.additionalProperties : true, "additionalProperties" in schema ? schema.additionalProperties : true,
} as unknown as TSchema, },
}; };
} }

View File

@@ -109,6 +109,7 @@ describe("getHealthSnapshot", () => {
fs.writeFileSync(tokenFile, "t-file\n", "utf-8"); fs.writeFileSync(tokenFile, "t-file\n", "utf-8");
testConfig = { telegram: { tokenFile } }; testConfig = { telegram: { tokenFile } };
testStore = {}; testStore = {};
vi.stubEnv("TELEGRAM_BOT_TOKEN", "");
const calls: string[] = []; const calls: string[] = [];
vi.stubGlobal( vi.stubGlobal(

View File

@@ -431,7 +431,6 @@ export type ClawdisConfig = {
canvasHost?: CanvasHostConfig; canvasHost?: CanvasHostConfig;
talk?: TalkConfig; talk?: TalkConfig;
gateway?: GatewayConfig; gateway?: GatewayConfig;
skills?: SkillsConfig;
}; };
/** /**
@@ -903,16 +902,18 @@ const ClawdisSchema = z.object({
.optional(), .optional(),
}) })
.optional(), .optional(),
entries: z.record( entries: z
z.string(), .record(
z z.string(),
.object({ z
enabled: z.boolean().optional(), .object({
apiKey: z.string().optional(), enabled: z.boolean().optional(),
env: z.record(z.string(), z.string()).optional(), apiKey: z.string().optional(),
}) env: z.record(z.string(), z.string()).optional(),
.passthrough(), })
).optional(), .passthrough(),
)
.optional(),
}) })
.optional(), .optional(),
}); });