fix(typebox): migrate to v1 with extension compat (#3474)
* fix(typebox): migrate to v1 with extension compat Replace AJV-based validation with TypeBox-native validation, keep legacy extension imports working (including @sinclair/typebox/compiler), and restore coercion for serialized/plain JSON schemas. This change closes #3112. * fix(typebox): use canonical imports and harden coercion Switch first-party code to canonical typebox imports while retaining legacy extension aliases in the loader. Remove obsolete runtime codegen guards, expand serialized JSON-schema coercion coverage, and update related tests and fixtures. Fixes #3112. --------- Co-authored-by: Mario Zechner <badlogicgames@gmail.com>
This commit is contained in:
97
package-lock.json
generated
97
package-lock.json
generated
@@ -2868,12 +2868,6 @@
|
|||||||
"integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==",
|
"integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@sinclair/typebox": {
|
|
||||||
"version": "0.34.49",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz",
|
|
||||||
"integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@slack/logger": {
|
"node_modules/@slack/logger": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.1.tgz",
|
||||||
@@ -4262,39 +4256,6 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ajv": {
|
|
||||||
"version": "8.18.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
|
||||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.3",
|
|
||||||
"fast-uri": "^3.0.1",
|
|
||||||
"json-schema-traverse": "^1.0.0",
|
|
||||||
"require-from-string": "^2.0.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ajv-formats": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ajv": "^8.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"ajv": "^8.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"ajv": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/alien-signals": {
|
"node_modules/alien-signals": {
|
||||||
"version": "2.0.8",
|
"version": "2.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.8.tgz",
|
||||||
@@ -5313,12 +5274,6 @@
|
|||||||
"@types/yauzl": "^2.9.1"
|
"@types/yauzl": "^2.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fast-deep-equal": {
|
|
||||||
"version": "3.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||||
@@ -5336,22 +5291,6 @@
|
|||||||
"node": ">=8.6.0"
|
"node": ">=8.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fast-uri": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/fastify"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/fastify"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/fast-xml-builder": {
|
"node_modules/fast-xml-builder": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz",
|
||||||
@@ -6067,12 +6006,6 @@
|
|||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/json-schema-traverse": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/jsonschema": {
|
"node_modules/jsonschema": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz",
|
||||||
@@ -7295,15 +7228,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/require-from-string": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.12",
|
"version": "1.22.12",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
|
||||||
@@ -8098,6 +8022,12 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typebox": {
|
||||||
|
"version": "1.1.29",
|
||||||
|
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.29.tgz",
|
||||||
|
"integrity": "sha512-zPwgxCka1v976Zn2Hg0THUHY6JFPzbBkz12ZOZeyc/vcoeqLyeFEsXw5zGg/nMhbNE/rOEALVKRTlHIEDR3Jhw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
@@ -8597,7 +8527,8 @@
|
|||||||
"version": "0.68.1",
|
"version": "0.68.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/pi-ai": "^0.68.1"
|
"@mariozechner/pi-ai": "^0.68.1",
|
||||||
|
"typebox": "^1.1.24"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
@@ -8634,13 +8565,11 @@
|
|||||||
"@aws-sdk/client-bedrock-runtime": "^3.1030.0",
|
"@aws-sdk/client-bedrock-runtime": "^3.1030.0",
|
||||||
"@google/genai": "^1.40.0",
|
"@google/genai": "^1.40.0",
|
||||||
"@mistralai/mistralai": "^2.2.0",
|
"@mistralai/mistralai": "^2.2.0",
|
||||||
"@sinclair/typebox": "^0.34.41",
|
|
||||||
"ajv": "^8.17.1",
|
|
||||||
"ajv-formats": "^3.0.1",
|
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"openai": "6.26.0",
|
"openai": "6.26.0",
|
||||||
"partial-json": "^0.1.7",
|
"partial-json": "^0.1.7",
|
||||||
"proxy-agent": "^6.5.0",
|
"proxy-agent": "^6.5.0",
|
||||||
|
"typebox": "^1.1.24",
|
||||||
"undici": "^7.19.1",
|
"undici": "^7.19.1",
|
||||||
"zod-to-json-schema": "^3.24.6"
|
"zod-to-json-schema": "^3.24.6"
|
||||||
},
|
},
|
||||||
@@ -8683,8 +8612,6 @@
|
|||||||
"@mariozechner/pi-ai": "^0.68.1",
|
"@mariozechner/pi-ai": "^0.68.1",
|
||||||
"@mariozechner/pi-tui": "^0.68.1",
|
"@mariozechner/pi-tui": "^0.68.1",
|
||||||
"@silvia-odwyer/photon-node": "^0.3.4",
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
||||||
"@sinclair/typebox": "^0.34.41",
|
|
||||||
"ajv": "^8.17.1",
|
|
||||||
"chalk": "^5.5.0",
|
"chalk": "^5.5.0",
|
||||||
"cli-highlight": "^2.1.11",
|
"cli-highlight": "^2.1.11",
|
||||||
"diff": "^8.0.2",
|
"diff": "^8.0.2",
|
||||||
@@ -8697,6 +8624,7 @@
|
|||||||
"minimatch": "^10.2.3",
|
"minimatch": "^10.2.3",
|
||||||
"proper-lockfile": "^4.1.2",
|
"proper-lockfile": "^4.1.2",
|
||||||
"strip-ansi": "^7.1.0",
|
"strip-ansi": "^7.1.0",
|
||||||
|
"typebox": "^1.1.24",
|
||||||
"undici": "^7.19.1",
|
"undici": "^7.19.1",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"yaml": "^2.8.2"
|
"yaml": "^2.8.2"
|
||||||
@@ -8781,12 +8709,12 @@
|
|||||||
"@mariozechner/pi-agent-core": "^0.68.1",
|
"@mariozechner/pi-agent-core": "^0.68.1",
|
||||||
"@mariozechner/pi-ai": "^0.68.1",
|
"@mariozechner/pi-ai": "^0.68.1",
|
||||||
"@mariozechner/pi-coding-agent": "^0.68.1",
|
"@mariozechner/pi-coding-agent": "^0.68.1",
|
||||||
"@sinclair/typebox": "^0.34.0",
|
|
||||||
"@slack/socket-mode": "^2.0.0",
|
"@slack/socket-mode": "^2.0.0",
|
||||||
"@slack/web-api": "^7.0.0",
|
"@slack/web-api": "^7.0.0",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"croner": "^9.1.0",
|
"croner": "^9.1.0",
|
||||||
"diff": "^8.0.2"
|
"diff": "^8.0.2",
|
||||||
|
"typebox": "^1.1.24"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"mom": "dist/main.js"
|
"mom": "dist/main.js"
|
||||||
@@ -8893,6 +8821,7 @@
|
|||||||
"lucide": "^0.544.0",
|
"lucide": "^0.544.0",
|
||||||
"ollama": "^0.6.0",
|
"ollama": "^0.6.0",
|
||||||
"pdfjs-dist": "5.4.394",
|
"pdfjs-dist": "5.4.394",
|
||||||
|
"typebox": "^1.1.24",
|
||||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
"prepublishOnly": "npm run clean && npm run build"
|
"prepublishOnly": "npm run clean && npm run build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/pi-ai": "^0.68.1"
|
"@mariozechner/pi-ai": "^0.68.1",
|
||||||
|
"typebox": "^1.1.24"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"ai",
|
"ai",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type {
|
|||||||
Tool,
|
Tool,
|
||||||
ToolResultMessage,
|
ToolResultMessage,
|
||||||
} from "@mariozechner/pi-ai";
|
} from "@mariozechner/pi-ai";
|
||||||
import type { Static, TSchema } from "@sinclair/typebox";
|
import type { Static, TSchema } from "typebox";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream function used by the agent loop.
|
* Stream function used by the agent loop.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
type Model,
|
type Model,
|
||||||
type UserMessage,
|
type UserMessage,
|
||||||
} from "@mariozechner/pi-ai";
|
} from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { agentLoop, agentLoopContinue } from "../src/agent-loop.js";
|
import { agentLoop, agentLoopContinue } from "../src/agent-loop.js";
|
||||||
import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, AgentTool } from "../src/types.js";
|
import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, AgentTool } from "../src/types.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Static, Type } from "@sinclair/typebox";
|
import { type Static, Type } from "typebox";
|
||||||
import type { AgentTool, AgentToolResult } from "../../src/types.js";
|
import type { AgentTool, AgentToolResult } from "../../src/types.js";
|
||||||
|
|
||||||
export interface CalculateResult extends AgentToolResult<undefined> {
|
export interface CalculateResult extends AgentToolResult<undefined> {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Static, Type } from "@sinclair/typebox";
|
import { type Static, Type } from "typebox";
|
||||||
import type { AgentTool, AgentToolResult } from "../../src/types.js";
|
import type { AgentTool, AgentToolResult } from "../../src/types.js";
|
||||||
|
|
||||||
export interface GetCurrentTimeResult extends AgentToolResult<{ utcTimestamp: number }> {}
|
export interface GetCurrentTimeResult extends AgentToolResult<{ utcTimestamp: number }> {}
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ for (const block of response.content) {
|
|||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
Tools enable LLMs to interact with external systems. This library uses TypeBox schemas for type-safe tool definitions with automatic validation using AJV. TypeBox schemas can be serialized and deserialized as plain JSON, making them ideal for distributed systems.
|
Tools enable LLMs to interact with external systems. This library uses TypeBox schemas for type-safe tool definitions with automatic validation using TypeBox's built-in validator and value conversion utilities. TypeBox schemas can be serialized and deserialized as plain JSON, making them ideal for distributed systems.
|
||||||
|
|
||||||
### Defining Tools
|
### Defining Tools
|
||||||
|
|
||||||
|
|||||||
@@ -76,9 +76,7 @@
|
|||||||
"@aws-sdk/client-bedrock-runtime": "^3.1030.0",
|
"@aws-sdk/client-bedrock-runtime": "^3.1030.0",
|
||||||
"@google/genai": "^1.40.0",
|
"@google/genai": "^1.40.0",
|
||||||
"@mistralai/mistralai": "^2.2.0",
|
"@mistralai/mistralai": "^2.2.0",
|
||||||
"@sinclair/typebox": "^0.34.41",
|
"typebox": "^1.1.24",
|
||||||
"ajv": "^8.17.1",
|
|
||||||
"ajv-formats": "^3.0.1",
|
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"openai": "6.26.0",
|
"openai": "6.26.0",
|
||||||
"partial-json": "^0.1.7",
|
"partial-json": "^0.1.7",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export type { Static, TSchema } from "@sinclair/typebox";
|
export type { Static, TSchema } from "typebox";
|
||||||
export { Type } from "@sinclair/typebox";
|
export { Type } from "typebox";
|
||||||
|
|
||||||
export * from "./api-registry.js";
|
export * from "./api-registry.js";
|
||||||
export * from "./env-api-keys.js";
|
export * from "./env-api-keys.js";
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
type ToolConfiguration,
|
type ToolConfiguration,
|
||||||
ToolResultStatus,
|
ToolResultStatus,
|
||||||
} from "@aws-sdk/client-bedrock-runtime";
|
} from "@aws-sdk/client-bedrock-runtime";
|
||||||
|
import type { DocumentType } from "@smithy/types";
|
||||||
import { calculateCost } from "../models.js";
|
import { calculateCost } from "../models.js";
|
||||||
import type {
|
import type {
|
||||||
Api,
|
Api,
|
||||||
@@ -760,7 +761,7 @@ function convertToolConfig(
|
|||||||
toolSpec: {
|
toolSpec: {
|
||||||
name: tool.name,
|
name: tool.name,
|
||||||
description: tool.description,
|
description: tool.description,
|
||||||
inputSchema: { json: tool.parameters },
|
inputSchema: { json: tool.parameters as unknown as DocumentType },
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ export interface ToolResultMessage<TDetails = any> {
|
|||||||
|
|
||||||
export type Message = UserMessage | AssistantMessage | ToolResultMessage;
|
export type Message = UserMessage | AssistantMessage | ToolResultMessage;
|
||||||
|
|
||||||
import type { TSchema } from "@sinclair/typebox";
|
import type { TSchema } from "typebox";
|
||||||
|
|
||||||
export interface Tool<TParameters extends TSchema = TSchema> {
|
export interface Tool<TParameters extends TSchema = TSchema> {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type TUnsafe, Type } from "@sinclair/typebox";
|
import { type TUnsafe, Type } from "typebox";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a string enum schema compatible with Google's API and other providers
|
* Creates a string enum schema compatible with Google's API and other providers
|
||||||
|
|||||||
@@ -1,42 +1,270 @@
|
|||||||
import AjvModule from "ajv";
|
import { Compile } from "typebox/compile";
|
||||||
import addFormatsModule from "ajv-formats";
|
import type { TLocalizedValidationError } from "typebox/error";
|
||||||
|
import { Value } from "typebox/value";
|
||||||
// Handle both default and named exports
|
|
||||||
const Ajv = (AjvModule as any).default || AjvModule;
|
|
||||||
const addFormats = (addFormatsModule as any).default || addFormatsModule;
|
|
||||||
|
|
||||||
import type { Tool, ToolCall } from "../types.js";
|
import type { Tool, ToolCall } from "../types.js";
|
||||||
|
|
||||||
// Detect if we're in a browser extension environment with strict CSP
|
const validatorCache = new WeakMap<object, ReturnType<typeof Compile>>();
|
||||||
// Chrome extensions with Manifest V3 don't allow eval/Function constructor
|
const TYPEBOX_KIND = Symbol.for("TypeBox.Kind");
|
||||||
const isBrowserExtension = typeof globalThis !== "undefined" && (globalThis as any).chrome?.runtime?.id !== undefined;
|
|
||||||
|
|
||||||
function canUseRuntimeCodegen(): boolean {
|
interface JsonSchemaObject {
|
||||||
if (isBrowserExtension) {
|
type?: string | string[];
|
||||||
return false;
|
properties?: Record<string, JsonSchemaObject>;
|
||||||
|
items?: JsonSchemaObject | JsonSchemaObject[];
|
||||||
|
additionalProperties?: boolean | JsonSchemaObject;
|
||||||
|
allOf?: JsonSchemaObject[];
|
||||||
|
anyOf?: JsonSchemaObject[];
|
||||||
|
oneOf?: JsonSchemaObject[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return typeof value === "object" && value !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isJsonSchemaObject(value: unknown): value is JsonSchemaObject {
|
||||||
|
return isRecord(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasTypeBoxMetadata(schema: unknown): boolean {
|
||||||
|
return isRecord(schema) && Object.getOwnPropertySymbols(schema).includes(TYPEBOX_KIND);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSchemaTypes(schema: JsonSchemaObject): string[] {
|
||||||
|
if (typeof schema.type === "string") {
|
||||||
|
return [schema.type];
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(schema.type)) {
|
||||||
|
return schema.type.filter((type): type is string => typeof type === "string");
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
function matchesJsonType(value: unknown, type: string): boolean {
|
||||||
new Function("return true;");
|
switch (type) {
|
||||||
return true;
|
case "number":
|
||||||
} catch {
|
return typeof value === "number";
|
||||||
return false;
|
case "integer":
|
||||||
|
return typeof value === "number" && Number.isInteger(value);
|
||||||
|
case "boolean":
|
||||||
|
return typeof value === "boolean";
|
||||||
|
case "string":
|
||||||
|
return typeof value === "string";
|
||||||
|
case "null":
|
||||||
|
return value === null;
|
||||||
|
case "array":
|
||||||
|
return Array.isArray(value);
|
||||||
|
case "object":
|
||||||
|
return isRecord(value) && !Array.isArray(value);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a singleton AJV instance with formats only when runtime code generation is available.
|
function isValidatorSchema(value: unknown): value is Tool["parameters"] {
|
||||||
let ajv: any = null;
|
return isRecord(value);
|
||||||
if (canUseRuntimeCodegen()) {
|
}
|
||||||
try {
|
|
||||||
ajv = new Ajv({
|
function getSubSchemaValidator(schema: JsonSchemaObject): ReturnType<typeof Compile> | undefined {
|
||||||
allErrors: true,
|
if (!isValidatorSchema(schema)) {
|
||||||
strict: false,
|
return undefined;
|
||||||
coerceTypes: true,
|
|
||||||
});
|
|
||||||
addFormats(ajv);
|
|
||||||
} catch (_e) {
|
|
||||||
console.warn("AJV validation disabled due to CSP restrictions");
|
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
return getValidator(schema);
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function coercePrimitiveByType(value: unknown, type: string): unknown {
|
||||||
|
switch (type) {
|
||||||
|
case "number": {
|
||||||
|
if (value === null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (typeof value === "string" && value.trim() !== "") {
|
||||||
|
const parsed = Number(value);
|
||||||
|
if (Number.isFinite(parsed)) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof value === "boolean") {
|
||||||
|
return value ? 1 : 0;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case "integer": {
|
||||||
|
if (value === null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (typeof value === "string" && value.trim() !== "") {
|
||||||
|
const parsed = Number(value);
|
||||||
|
if (Number.isInteger(parsed)) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof value === "boolean") {
|
||||||
|
return value ? 1 : 0;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case "boolean": {
|
||||||
|
if (value === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeof value === "string") {
|
||||||
|
if (value === "true") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value === "false") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof value === "number") {
|
||||||
|
if (value === 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case "string": {
|
||||||
|
if (value === null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (typeof value === "number" || typeof value === "boolean") {
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case "null": {
|
||||||
|
if (value === "" || value === 0 || value === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applySchemaObjectCoercion(value: Record<string, unknown>, schema: JsonSchemaObject): void {
|
||||||
|
const properties = schema.properties;
|
||||||
|
const definedKeys = new Set<string>(properties ? Object.keys(properties) : []);
|
||||||
|
|
||||||
|
if (properties) {
|
||||||
|
for (const [key, propertySchema] of Object.entries(properties)) {
|
||||||
|
if (!(key in value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value[key] = coerceWithJsonSchema(value[key], propertySchema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.additionalProperties && isJsonSchemaObject(schema.additionalProperties)) {
|
||||||
|
for (const [key, propertyValue] of Object.entries(value)) {
|
||||||
|
if (definedKeys.has(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value[key] = coerceWithJsonSchema(propertyValue, schema.additionalProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applySchemaArrayCoercion(value: unknown[], schema: JsonSchemaObject): void {
|
||||||
|
if (Array.isArray(schema.items)) {
|
||||||
|
for (let index = 0; index < value.length; index++) {
|
||||||
|
const itemSchema = schema.items[index];
|
||||||
|
if (!itemSchema) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value[index] = coerceWithJsonSchema(value[index], itemSchema);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJsonSchemaObject(schema.items)) {
|
||||||
|
for (let index = 0; index < value.length; index++) {
|
||||||
|
value[index] = coerceWithJsonSchema(value[index], schema.items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function coerceWithUnionSchema(value: unknown, schemas: JsonSchemaObject[]): unknown {
|
||||||
|
for (const schema of schemas) {
|
||||||
|
const candidate = structuredClone(value);
|
||||||
|
const coerced = coerceWithJsonSchema(candidate, schema);
|
||||||
|
const validator = getSubSchemaValidator(schema);
|
||||||
|
if (validator?.Check(coerced)) {
|
||||||
|
return coerced;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function coerceWithJsonSchema(value: unknown, schema: JsonSchemaObject): unknown {
|
||||||
|
let nextValue = value;
|
||||||
|
|
||||||
|
if (Array.isArray(schema.allOf)) {
|
||||||
|
for (const nested of schema.allOf) {
|
||||||
|
nextValue = coerceWithJsonSchema(nextValue, nested);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(schema.anyOf)) {
|
||||||
|
nextValue = coerceWithUnionSchema(nextValue, schema.anyOf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(schema.oneOf)) {
|
||||||
|
nextValue = coerceWithUnionSchema(nextValue, schema.oneOf);
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaTypes = getSchemaTypes(schema);
|
||||||
|
const matchesUnionMember =
|
||||||
|
schemaTypes.length > 1 && schemaTypes.some((schemaType) => matchesJsonType(nextValue, schemaType));
|
||||||
|
if (schemaTypes.length > 0 && !matchesUnionMember) {
|
||||||
|
for (const schemaType of schemaTypes) {
|
||||||
|
const candidate = coercePrimitiveByType(nextValue, schemaType);
|
||||||
|
if (candidate !== nextValue) {
|
||||||
|
nextValue = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schemaTypes.includes("object") && isRecord(nextValue) && !Array.isArray(nextValue)) {
|
||||||
|
applySchemaObjectCoercion(nextValue, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schemaTypes.includes("array") && Array.isArray(nextValue)) {
|
||||||
|
applySchemaArrayCoercion(nextValue, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValidator(schema: Tool["parameters"]): ReturnType<typeof Compile> {
|
||||||
|
const key = schema as object;
|
||||||
|
const cached = validatorCache.get(key);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
const validator = Compile(schema);
|
||||||
|
validatorCache.set(key, validator);
|
||||||
|
return validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatValidationPath(error: TLocalizedValidationError): string {
|
||||||
|
if (error.keyword === "required") {
|
||||||
|
const requiredProperties = (error.params as { requiredProperties?: string[] }).requiredProperties;
|
||||||
|
const requiredProperty = requiredProperties?.[0];
|
||||||
|
if (requiredProperty) {
|
||||||
|
const basePath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
||||||
|
return basePath ? `${basePath}.${requiredProperty}` : requiredProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const path = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
||||||
|
return path || "root";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,29 +290,32 @@ export function validateToolCall(tools: Tool[], toolCall: ToolCall): any {
|
|||||||
* @throws Error with formatted message if validation fails
|
* @throws Error with formatted message if validation fails
|
||||||
*/
|
*/
|
||||||
export function validateToolArguments(tool: Tool, toolCall: ToolCall): any {
|
export function validateToolArguments(tool: Tool, toolCall: ToolCall): any {
|
||||||
// Skip validation in environments where runtime code generation is unavailable.
|
const args = structuredClone(toolCall.arguments);
|
||||||
if (!ajv || !canUseRuntimeCodegen()) {
|
Value.Convert(tool.parameters, args);
|
||||||
return toolCall.arguments;
|
|
||||||
|
const validator = getValidator(tool.parameters);
|
||||||
|
if (!hasTypeBoxMetadata(tool.parameters) && isJsonSchemaObject(tool.parameters)) {
|
||||||
|
const coerced = coerceWithJsonSchema(args, tool.parameters);
|
||||||
|
if (coerced !== args) {
|
||||||
|
if (isRecord(args) && isRecord(coerced)) {
|
||||||
|
for (const key of Object.keys(args)) {
|
||||||
|
delete args[key];
|
||||||
|
}
|
||||||
|
Object.assign(args, coerced);
|
||||||
|
} else {
|
||||||
|
return validator.Check(coerced) ? coerced : args;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the schema.
|
if (validator.Check(args)) {
|
||||||
const validate = ajv.compile(tool.parameters);
|
|
||||||
|
|
||||||
// Clone arguments so AJV can safely mutate for type coercion
|
|
||||||
const args = structuredClone(toolCall.arguments);
|
|
||||||
|
|
||||||
// Validate the arguments (AJV mutates args in-place for type coercion)
|
|
||||||
if (validate(args)) {
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format validation errors nicely
|
|
||||||
const errors =
|
const errors =
|
||||||
validate.errors
|
validator
|
||||||
?.map((err: any) => {
|
.Errors(args)
|
||||||
const path = err.instancePath ? err.instancePath.substring(1) : err.params.missingProperty || "root";
|
.map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)
|
||||||
return ` - ${path}: ${err.message}`;
|
|
||||||
})
|
|
||||||
.join("\n") || "Unknown validation error";
|
.join("\n") || "Unknown validation error";
|
||||||
|
|
||||||
const errorMessage = `Validation failed for tool "${toolCall.name}":\n${errors}\n\nReceived arguments:\n${JSON.stringify(toolCall.arguments, null, 2)}`;
|
const errorMessage = `Validation failed for tool "${toolCall.name}":\n${errors}\n\nReceived arguments:\n${JSON.stringify(toolCall.arguments, null, 2)}`;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { stream } from "../src/stream.js";
|
import { stream } from "../src/stream.js";
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
* Fixtures are generated fresh on each run.
|
* Fixtures are generated fresh on each run.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import { writeFileSync } from "fs";
|
import { writeFileSync } from "fs";
|
||||||
|
import { Type } from "typebox";
|
||||||
import { beforeAll, describe, expect, it } from "vitest";
|
import { beforeAll, describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { completeSimple, getEnvApiKey } from "../src/stream.js";
|
import { completeSimple, getEnvApiKey } from "../src/stream.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { streamGoogleGeminiCli } from "../src/providers/google-gemini-cli.js";
|
import { streamGoogleGeminiCli } from "../src/providers/google-gemini-cli.js";
|
||||||
import type { Context, Model, ToolCall } from "../src/types.js";
|
import type { Context, Model, ToolCall } from "../src/types.js";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { Api, Context, Model, Tool, ToolResultMessage } from "../src/index.js";
|
import type { Api, Context, Model, Tool, ToolResultMessage } from "../src/index.js";
|
||||||
import { complete, getModel } from "../src/index.js";
|
import { complete, getModel } from "../src/index.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getEnvApiKey } from "../src/env-api-keys.js";
|
import { getEnvApiKey } from "../src/env-api-keys.js";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { complete } from "../src/stream.js";
|
import { complete } from "../src/stream.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { streamOpenAICompletions } from "../src/providers/openai-completions.js";
|
import { streamOpenAICompletions } from "../src/providers/openai-completions.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { streamSimple } from "../src/stream.js";
|
import { streamSimple } from "../src/stream.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { complete, getEnvApiKey } from "../src/stream.js";
|
import { complete, getEnvApiKey } from "../src/stream.js";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import type { ResponseFunctionCallOutputItemList } from "openai/resources/responses/responses.js";
|
import type { ResponseFunctionCallOutputItemList } from "openai/resources/responses/responses.js";
|
||||||
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { Api, Context, Model, StreamOptions, Tool, ToolResultMessage } from "../src/index.js";
|
import type { Api, Context, Model, StreamOptions, Tool, ToolResultMessage } from "../src/index.js";
|
||||||
import { complete, getModel } from "../src/index.js";
|
import { complete, getModel } from "../src/index.js";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import { type ChildProcess, execSync, spawn } from "child_process";
|
import { type ChildProcess, execSync, spawn } from "child_process";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
|
import { Type } from "typebox";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
* Regression test for: https://github.com/badlogic/pi-mono/issues/1022
|
* Regression test for: https://github.com/badlogic/pi-mono/issues/1022
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { completeSimple, getEnvApiKey } from "../src/stream.js";
|
import { completeSimple, getEnvApiKey } from "../src/stream.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { complete } from "../src/stream.js";
|
import { complete } from "../src/stream.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { complete } from "../src/stream.js";
|
import { complete } from "../src/stream.js";
|
||||||
|
|||||||
@@ -1,17 +1,41 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { ToolCall } from "../src/types.js";
|
import type { Tool, ToolCall } from "../src/types.js";
|
||||||
import { validateToolArguments } from "../src/utils/validation.js";
|
import { validateToolArguments } from "../src/utils/validation.js";
|
||||||
|
|
||||||
afterEach(() => {
|
function createToolCallWithPlainSchema(
|
||||||
vi.restoreAllMocks();
|
schema: Tool["parameters"],
|
||||||
});
|
value: unknown,
|
||||||
|
): {
|
||||||
|
tool: Tool;
|
||||||
|
toolCall: ToolCall;
|
||||||
|
} {
|
||||||
|
const tool: Tool = {
|
||||||
|
name: "echo",
|
||||||
|
description: "Echo tool",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
value: schema,
|
||||||
|
},
|
||||||
|
required: ["value"],
|
||||||
|
} as Tool["parameters"],
|
||||||
|
};
|
||||||
|
|
||||||
|
const toolCall: ToolCall = {
|
||||||
|
type: "toolCall",
|
||||||
|
id: "tool-1",
|
||||||
|
name: "echo",
|
||||||
|
arguments: { value },
|
||||||
|
};
|
||||||
|
|
||||||
|
return { tool, toolCall };
|
||||||
|
}
|
||||||
|
|
||||||
describe("validateToolArguments", () => {
|
describe("validateToolArguments", () => {
|
||||||
it("falls back to raw arguments without writing to stderr when runtime code generation is blocked", () => {
|
it("still validates when Function constructor is unavailable", () => {
|
||||||
const originalFunction = globalThis.Function;
|
const originalFunction = globalThis.Function;
|
||||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
const tool: Tool = {
|
||||||
const tool = {
|
|
||||||
name: "echo",
|
name: "echo",
|
||||||
description: "Echo tool",
|
description: "Echo tool",
|
||||||
parameters: Type.Object({
|
parameters: Type.Object({
|
||||||
@@ -30,10 +54,63 @@ describe("validateToolArguments", () => {
|
|||||||
}) as unknown as FunctionConstructor;
|
}) as unknown as FunctionConstructor;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
expect(validateToolArguments(tool, toolCall)).toEqual(toolCall.arguments);
|
expect(validateToolArguments(tool, toolCall)).toEqual({ count: 42 });
|
||||||
expect(errorSpy).not.toHaveBeenCalled();
|
|
||||||
} finally {
|
} finally {
|
||||||
globalThis.Function = originalFunction;
|
globalThis.Function = originalFunction;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("coerces serialized plain JSON schemas with AJV-compatible primitive rules", () => {
|
||||||
|
const passingCases: Array<{
|
||||||
|
schema: Tool["parameters"];
|
||||||
|
input: unknown;
|
||||||
|
expected: unknown;
|
||||||
|
}> = [
|
||||||
|
{ schema: { type: "number" } as Tool["parameters"], input: "42", expected: 42 },
|
||||||
|
{ schema: { type: "number" } as Tool["parameters"], input: true, expected: 1 },
|
||||||
|
{ schema: { type: "number" } as Tool["parameters"], input: null, expected: 0 },
|
||||||
|
{ schema: { type: "integer" } as Tool["parameters"], input: "42", expected: 42 },
|
||||||
|
{ schema: { type: "boolean" } as Tool["parameters"], input: "true", expected: true },
|
||||||
|
{ schema: { type: "boolean" } as Tool["parameters"], input: "false", expected: false },
|
||||||
|
{ schema: { type: "boolean" } as Tool["parameters"], input: 1, expected: true },
|
||||||
|
{ schema: { type: "boolean" } as Tool["parameters"], input: 0, expected: false },
|
||||||
|
{ schema: { type: "string" } as Tool["parameters"], input: null, expected: "" },
|
||||||
|
{ schema: { type: "string" } as Tool["parameters"], input: true, expected: "true" },
|
||||||
|
{ schema: { type: "null" } as Tool["parameters"], input: "", expected: null },
|
||||||
|
{ schema: { type: "null" } as Tool["parameters"], input: 0, expected: null },
|
||||||
|
{ schema: { type: "null" } as Tool["parameters"], input: false, expected: null },
|
||||||
|
{
|
||||||
|
schema: { type: ["number", "string"] } as Tool["parameters"],
|
||||||
|
input: "1",
|
||||||
|
expected: "1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
schema: { type: ["boolean", "number"] } as Tool["parameters"],
|
||||||
|
input: "1",
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const testCase of passingCases) {
|
||||||
|
const { tool, toolCall } = createToolCallWithPlainSchema(testCase.schema, testCase.input);
|
||||||
|
expect(validateToolArguments(tool, toolCall)).toEqual({ value: testCase.expected });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects invalid coercions for serialized plain JSON schemas", () => {
|
||||||
|
const failingCases: Array<{
|
||||||
|
schema: Tool["parameters"];
|
||||||
|
input: unknown;
|
||||||
|
}> = [
|
||||||
|
{ schema: { type: "boolean" } as Tool["parameters"], input: "1" },
|
||||||
|
{ schema: { type: "boolean" } as Tool["parameters"], input: "0" },
|
||||||
|
{ schema: { type: "null" } as Tool["parameters"], input: "null" },
|
||||||
|
{ schema: { type: "integer" } as Tool["parameters"], input: "42.1" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const testCase of failingCases) {
|
||||||
|
const { tool, toolCall } = createToolCallWithPlainSchema(testCase.schema, testCase.input);
|
||||||
|
expect(() => validateToolArguments(tool, toolCall)).toThrow("Validation failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { StringEnum } from "@mariozechner/pi-ai";
|
import { StringEnum } from "@mariozechner/pi-ai";
|
||||||
import { type ExtensionAPI, getAgentDir, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
|
import { type ExtensionAPI, getAgentDir, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
import { type Static, Type } from "typebox";
|
||||||
|
|
||||||
const PROVIDER = "google-antigravity";
|
const PROVIDER = "google-antigravity";
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
|
|
||||||
const ECHO_PARAMS = Type.Object({
|
const ECHO_PARAMS = Type.Object({
|
||||||
message: Type.String({ description: "Message to echo" }),
|
message: Type.String({ description: "Message to echo" }),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Editor, type EditorTheme, Key, matchesKey, Text, truncateToWidth } from "@mariozechner/pi-tui";
|
import { Editor, type EditorTheme, Key, matchesKey, Text, truncateToWidth } from "@mariozechner/pi-tui";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
|
|
||||||
interface OptionWithDesc {
|
interface OptionWithDesc {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Editor, type EditorTheme, Key, matchesKey, Text, truncateToWidth } from "@mariozechner/pi-tui";
|
import { Editor, type EditorTheme, Key, matchesKey, Text, truncateToWidth } from "@mariozechner/pi-tui";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
interface QuestionOption {
|
interface QuestionOption {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
|
|
||||||
export default function (pi: ExtensionAPI) {
|
export default function (pi: ExtensionAPI) {
|
||||||
// Command entrypoint for reload.
|
// Command entrypoint for reload.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
|
|
||||||
export default function (pi: ExtensionAPI) {
|
export default function (pi: ExtensionAPI) {
|
||||||
// Register a /quit command that cleanly exits pi
|
// Register a /quit command that cleanly exits pi
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import type { Message } from "@mariozechner/pi-ai";
|
|||||||
import { StringEnum } from "@mariozechner/pi-ai";
|
import { StringEnum } from "@mariozechner/pi-ai";
|
||||||
import { type ExtensionAPI, getMarkdownTheme, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
|
import { type ExtensionAPI, getMarkdownTheme, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
|
||||||
import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
|
import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.js";
|
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.js";
|
||||||
|
|
||||||
const MAX_PARALLEL_TASKS = 8;
|
const MAX_PARALLEL_TASKS = 8;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import { StringEnum } from "@mariozechner/pi-ai";
|
import { StringEnum } from "@mariozechner/pi-ai";
|
||||||
import type { ExtensionAPI, ExtensionContext, Theme, ToolExecutionMode } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI, ExtensionContext, Theme, ToolExecutionMode } from "@mariozechner/pi-coding-agent";
|
||||||
import { type Component, matchesKey, Text, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
import { type Component, matchesKey, Text, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
|
|
||||||
// Thrown from the tool on illegal actions. The agent runtime surfaces thrown
|
// Thrown from the tool on illegal actions. The agent runtime surfaces thrown
|
||||||
// errors as tool errors (isError=true) without resetting any of our state.
|
// errors as tool errors (isError=true) without resetting any of our state.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
import { StringEnum } from "@mariozechner/pi-ai";
|
import { StringEnum } from "@mariozechner/pi-ai";
|
||||||
import type { ExtensionAPI, ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI, ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
|
||||||
import { matchesKey, Text, truncateToWidth } from "@mariozechner/pi-tui";
|
import { matchesKey, Text, truncateToWidth } from "@mariozechner/pi-tui";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
|
|
||||||
interface Todo {
|
interface Todo {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
@@ -22,10 +22,10 @@
|
|||||||
|
|
||||||
import type { TextContent } from "@mariozechner/pi-ai";
|
import type { TextContent } from "@mariozechner/pi-ai";
|
||||||
import { type ExtensionAPI, getAgentDir, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
|
import { type ExtensionAPI, getAgentDir, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import { constants, readFileSync } from "fs";
|
import { constants, readFileSync } from "fs";
|
||||||
import { access, appendFile, readFile } from "fs/promises";
|
import { access, appendFile, readFile } from "fs/promises";
|
||||||
import { join, resolve } from "path";
|
import { join, resolve } from "path";
|
||||||
|
import { Type } from "typebox";
|
||||||
|
|
||||||
const LOG_FILE = join(getAgentDir(), "read-access.log");
|
const LOG_FILE = join(getAgentDir(), "read-access.log");
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ import {
|
|||||||
withFileMutationQueue,
|
withFileMutationQueue,
|
||||||
} from "@mariozechner/pi-coding-agent";
|
} from "@mariozechner/pi-coding-agent";
|
||||||
import { Text } from "@mariozechner/pi-tui";
|
import { Text } from "@mariozechner/pi-tui";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
import { tmpdir } from "os";
|
import { tmpdir } from "os";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
import { Type } from "typebox";
|
||||||
|
|
||||||
const RgParams = Type.Object({
|
const RgParams = Type.Object({
|
||||||
pattern: Type.String({ description: "Search pattern (regex)" }),
|
pattern: Type.String({ description: "Search pattern (regex)" }),
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
import { Type } from "typebox";
|
||||||
|
|
||||||
export default function (pi: ExtensionAPI) {
|
export default function (pi: ExtensionAPI) {
|
||||||
// Register a tool that uses ms
|
// Register a tool that uses ms
|
||||||
|
|||||||
@@ -44,8 +44,7 @@
|
|||||||
"@mariozechner/pi-ai": "^0.68.1",
|
"@mariozechner/pi-ai": "^0.68.1",
|
||||||
"@mariozechner/pi-tui": "^0.68.1",
|
"@mariozechner/pi-tui": "^0.68.1",
|
||||||
"@silvia-odwyer/photon-node": "^0.3.4",
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
||||||
"@sinclair/typebox": "^0.34.41",
|
"typebox": "^1.1.24",
|
||||||
"ajv": "^8.17.1",
|
|
||||||
"chalk": "^5.5.0",
|
"chalk": "^5.5.0",
|
||||||
"cli-highlight": "^2.1.11",
|
"cli-highlight": "^2.1.11",
|
||||||
"diff": "^8.0.2",
|
"diff": "^8.0.2",
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ import * as _bundledPiTui from "@mariozechner/pi-tui";
|
|||||||
// Static imports of packages that extensions may use.
|
// Static imports of packages that extensions may use.
|
||||||
// These MUST be static so Bun bundles them into the compiled binary.
|
// These MUST be static so Bun bundles them into the compiled binary.
|
||||||
// The virtualModules option then makes them available to extensions.
|
// The virtualModules option then makes them available to extensions.
|
||||||
import * as _bundledTypebox from "@sinclair/typebox";
|
import * as _bundledTypebox from "typebox";
|
||||||
|
import * as _bundledTypeboxCompile from "typebox/compile";
|
||||||
|
import * as _bundledTypeboxValue from "typebox/value";
|
||||||
import { getAgentDir, isBunBinary } from "../../config.js";
|
import { getAgentDir, isBunBinary } from "../../config.js";
|
||||||
// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
|
// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
|
||||||
// avoiding a circular dependency. Extensions can import from @mariozechner/pi-coding-agent.
|
// avoiding a circular dependency. Extensions can import from @mariozechner/pi-coding-agent.
|
||||||
@@ -27,6 +29,7 @@ import { createEventBus, type EventBus } from "../event-bus.js";
|
|||||||
import type { ExecOptions } from "../exec.js";
|
import type { ExecOptions } from "../exec.js";
|
||||||
import { execCommand } from "../exec.js";
|
import { execCommand } from "../exec.js";
|
||||||
import { createSyntheticSourceInfo } from "../source-info.js";
|
import { createSyntheticSourceInfo } from "../source-info.js";
|
||||||
|
import * as _bundledTypeboxCompilerCompat from "./typebox-compiler-compat.js";
|
||||||
import type {
|
import type {
|
||||||
Extension,
|
Extension,
|
||||||
ExtensionAPI,
|
ExtensionAPI,
|
||||||
@@ -41,7 +44,14 @@ import type {
|
|||||||
|
|
||||||
/** Modules available to extensions via virtualModules (for compiled Bun binary) */
|
/** Modules available to extensions via virtualModules (for compiled Bun binary) */
|
||||||
const VIRTUAL_MODULES: Record<string, unknown> = {
|
const VIRTUAL_MODULES: Record<string, unknown> = {
|
||||||
|
typebox: _bundledTypebox,
|
||||||
|
"typebox/compile": _bundledTypeboxCompile,
|
||||||
|
"typebox/value": _bundledTypeboxValue,
|
||||||
|
"typebox/compiler": _bundledTypeboxCompilerCompat,
|
||||||
"@sinclair/typebox": _bundledTypebox,
|
"@sinclair/typebox": _bundledTypebox,
|
||||||
|
"@sinclair/typebox/compile": _bundledTypeboxCompile,
|
||||||
|
"@sinclair/typebox/value": _bundledTypeboxValue,
|
||||||
|
"@sinclair/typebox/compiler": _bundledTypeboxCompilerCompat,
|
||||||
"@mariozechner/pi-agent-core": _bundledPiAgentCore,
|
"@mariozechner/pi-agent-core": _bundledPiAgentCore,
|
||||||
"@mariozechner/pi-tui": _bundledPiTui,
|
"@mariozechner/pi-tui": _bundledPiTui,
|
||||||
"@mariozechner/pi-ai": _bundledPiAi,
|
"@mariozechner/pi-ai": _bundledPiAi,
|
||||||
@@ -56,14 +66,25 @@ const require = createRequire(import.meta.url);
|
|||||||
* In Bun binary mode, virtualModules is used instead.
|
* In Bun binary mode, virtualModules is used instead.
|
||||||
*/
|
*/
|
||||||
let _aliases: Record<string, string> | null = null;
|
let _aliases: Record<string, string> | null = null;
|
||||||
|
|
||||||
|
function resolveTypeboxCompilerCompatPath(baseDir: string): string {
|
||||||
|
const jsPath = path.resolve(baseDir, "typebox-compiler-compat.js");
|
||||||
|
if (fs.existsSync(jsPath)) {
|
||||||
|
return jsPath;
|
||||||
|
}
|
||||||
|
return path.resolve(baseDir, "typebox-compiler-compat.ts");
|
||||||
|
}
|
||||||
|
|
||||||
function getAliases(): Record<string, string> {
|
function getAliases(): Record<string, string> {
|
||||||
if (_aliases) return _aliases;
|
if (_aliases) return _aliases;
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const packageIndex = path.resolve(__dirname, "../..", "index.js");
|
const packageIndex = path.resolve(__dirname, "../..", "index.js");
|
||||||
|
|
||||||
const typeboxEntry = require.resolve("@sinclair/typebox");
|
const typeboxEntry = require.resolve("typebox");
|
||||||
const typeboxRoot = typeboxEntry.replace(/[\\/]build[\\/]cjs[\\/]index\.js$/, "");
|
const typeboxCompileEntry = require.resolve("typebox/compile");
|
||||||
|
const typeboxValueEntry = require.resolve("typebox/value");
|
||||||
|
const typeboxCompilerCompat = resolveTypeboxCompilerCompatPath(__dirname);
|
||||||
|
|
||||||
const packagesRoot = path.resolve(__dirname, "../../../../");
|
const packagesRoot = path.resolve(__dirname, "../../../../");
|
||||||
const resolveWorkspaceOrImport = (workspaceRelativePath: string, specifier: string): string => {
|
const resolveWorkspaceOrImport = (workspaceRelativePath: string, specifier: string): string => {
|
||||||
@@ -80,7 +101,14 @@ function getAliases(): Record<string, string> {
|
|||||||
"@mariozechner/pi-tui": resolveWorkspaceOrImport("tui/dist/index.js", "@mariozechner/pi-tui"),
|
"@mariozechner/pi-tui": resolveWorkspaceOrImport("tui/dist/index.js", "@mariozechner/pi-tui"),
|
||||||
"@mariozechner/pi-ai": resolveWorkspaceOrImport("ai/dist/index.js", "@mariozechner/pi-ai"),
|
"@mariozechner/pi-ai": resolveWorkspaceOrImport("ai/dist/index.js", "@mariozechner/pi-ai"),
|
||||||
"@mariozechner/pi-ai/oauth": resolveWorkspaceOrImport("ai/dist/oauth.js", "@mariozechner/pi-ai/oauth"),
|
"@mariozechner/pi-ai/oauth": resolveWorkspaceOrImport("ai/dist/oauth.js", "@mariozechner/pi-ai/oauth"),
|
||||||
"@sinclair/typebox": typeboxRoot,
|
typebox: typeboxEntry,
|
||||||
|
"typebox/compile": typeboxCompileEntry,
|
||||||
|
"typebox/value": typeboxValueEntry,
|
||||||
|
"typebox/compiler": typeboxCompilerCompat,
|
||||||
|
"@sinclair/typebox": typeboxEntry,
|
||||||
|
"@sinclair/typebox/compile": typeboxCompileEntry,
|
||||||
|
"@sinclair/typebox/value": typeboxValueEntry,
|
||||||
|
"@sinclair/typebox/compiler": typeboxCompilerCompat,
|
||||||
};
|
};
|
||||||
|
|
||||||
return _aliases;
|
return _aliases;
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Code, Compile, Validator } from "typebox/compile";
|
||||||
|
|
||||||
|
export { Code, Compile, Validator };
|
||||||
|
|
||||||
|
// Legacy @sinclair/typebox/compiler compatibility for extensions.
|
||||||
|
export const TypeCompiler = {
|
||||||
|
Compile,
|
||||||
|
};
|
||||||
|
|
||||||
|
// In TypeBox 0.x this was a named export. Map it to the v1 Validator class.
|
||||||
|
export const TypeCheck = Validator;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Code,
|
||||||
|
Compile,
|
||||||
|
Validator,
|
||||||
|
TypeCompiler,
|
||||||
|
TypeCheck,
|
||||||
|
};
|
||||||
@@ -39,7 +39,7 @@ import type {
|
|||||||
OverlayOptions,
|
OverlayOptions,
|
||||||
TUI,
|
TUI,
|
||||||
} from "@mariozechner/pi-tui";
|
} from "@mariozechner/pi-tui";
|
||||||
import type { Static, TSchema } from "@sinclair/typebox";
|
import type { Static, TSchema } from "typebox";
|
||||||
import type { Theme } from "../../modes/interactive/theme/theme.js";
|
import type { Theme } from "../../modes/interactive/theme/theme.js";
|
||||||
import type { BashResult } from "../bash-executor.js";
|
import type { BashResult } from "../bash-executor.js";
|
||||||
import type { CompactionPreparation, CompactionResult } from "../compaction/index.js";
|
import type { CompactionPreparation, CompactionResult } from "../compaction/index.js";
|
||||||
@@ -479,7 +479,7 @@ type AnyToolDefinition = ToolDefinition<any, any, any>;
|
|||||||
export function defineTool<TParams extends TSchema, TDetails = unknown, TState = any>(
|
export function defineTool<TParams extends TSchema, TDetails = unknown, TState = any>(
|
||||||
tool: ToolDefinition<TParams, TDetails, TState>,
|
tool: ToolDefinition<TParams, TDetails, TState>,
|
||||||
): ToolDefinition<TParams, TDetails, TState> & AnyToolDefinition {
|
): ToolDefinition<TParams, TDetails, TState> & AnyToolDefinition {
|
||||||
return tool;
|
return tool as ToolDefinition<TParams, TDetails, TState> & AnyToolDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ import {
|
|||||||
type SimpleStreamOptions,
|
type SimpleStreamOptions,
|
||||||
} from "@mariozechner/pi-ai";
|
} from "@mariozechner/pi-ai";
|
||||||
import { registerOAuthProvider, resetOAuthProviders } from "@mariozechner/pi-ai/oauth";
|
import { registerOAuthProvider, resetOAuthProviders } from "@mariozechner/pi-ai/oauth";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import AjvModule from "ajv";
|
|
||||||
import { existsSync, readFileSync } from "fs";
|
import { existsSync, readFileSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
|
import { Compile } from "typebox/compile";
|
||||||
|
import type { TLocalizedValidationError } from "typebox/error";
|
||||||
import { getAgentDir } from "../config.js";
|
import { getAgentDir } from "../config.js";
|
||||||
import type { AuthStorage } from "./auth-storage.js";
|
import type { AuthStorage } from "./auth-storage.js";
|
||||||
import {
|
import {
|
||||||
@@ -31,9 +32,6 @@ import {
|
|||||||
resolveHeadersOrThrow,
|
resolveHeadersOrThrow,
|
||||||
} from "./resolve-config-value.js";
|
} from "./resolve-config-value.js";
|
||||||
|
|
||||||
const Ajv = (AjvModule as any).default || AjvModule;
|
|
||||||
const ajv = new Ajv();
|
|
||||||
|
|
||||||
// Schema for OpenRouter routing preferences
|
// Schema for OpenRouter routing preferences
|
||||||
const PercentileCutoffsSchema = Type.Object({
|
const PercentileCutoffsSchema = Type.Object({
|
||||||
p50: Type.Optional(Type.Number()),
|
p50: Type.Optional(Type.Number()),
|
||||||
@@ -179,10 +177,23 @@ const ModelsConfigSchema = Type.Object({
|
|||||||
providers: Type.Record(Type.String(), ProviderConfigSchema),
|
providers: Type.Record(Type.String(), ProviderConfigSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
ajv.addSchema(ModelsConfigSchema, "ModelsConfig");
|
const validateModelsConfig = Compile(ModelsConfigSchema);
|
||||||
|
|
||||||
type ModelsConfig = Static<typeof ModelsConfigSchema>;
|
type ModelsConfig = Static<typeof ModelsConfigSchema>;
|
||||||
|
|
||||||
|
function formatValidationPath(error: TLocalizedValidationError): string {
|
||||||
|
if (error.keyword === "required") {
|
||||||
|
const requiredProperties = (error.params as { requiredProperties?: string[] }).requiredProperties;
|
||||||
|
const requiredProperty = requiredProperties?.[0];
|
||||||
|
if (requiredProperty) {
|
||||||
|
const basePath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
||||||
|
return basePath ? `${basePath}.${requiredProperty}` : requiredProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const path = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
||||||
|
return path || "root";
|
||||||
|
}
|
||||||
|
|
||||||
/** Provider override config (baseUrl, compat) without request auth/headers */
|
/** Provider override config (baseUrl, compat) without request auth/headers */
|
||||||
interface ProviderOverride {
|
interface ProviderOverride {
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
@@ -417,17 +428,19 @@ export class ModelRegistry {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(modelsJsonPath, "utf-8");
|
const content = readFileSync(modelsJsonPath, "utf-8");
|
||||||
const config: ModelsConfig = JSON.parse(content);
|
const parsed = JSON.parse(content) as unknown;
|
||||||
|
|
||||||
// Validate schema
|
if (!validateModelsConfig.Check(parsed)) {
|
||||||
const validate = ajv.getSchema("ModelsConfig")!;
|
|
||||||
if (!validate(config)) {
|
|
||||||
const errors =
|
const errors =
|
||||||
validate.errors?.map((e: any) => ` - ${e.instancePath || "root"}: ${e.message}`).join("\n") ||
|
validateModelsConfig
|
||||||
"Unknown schema error";
|
.Errors(parsed)
|
||||||
|
.map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)
|
||||||
|
.join("\n") || "Unknown schema error";
|
||||||
return emptyCustomModelsResult(`Invalid models.json schema:\n${errors}\n\nFile: ${modelsJsonPath}`);
|
return emptyCustomModelsResult(`Invalid models.json schema:\n${errors}\n\nFile: ${modelsJsonPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const config = parsed as ModelsConfig;
|
||||||
|
|
||||||
// Additional validation
|
// Additional validation
|
||||||
this.validateConfig(config);
|
this.validateConfig(config);
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { tmpdir } from "node:os";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Container, Text, truncateToWidth } from "@mariozechner/pi-tui";
|
import { Container, Text, truncateToWidth } from "@mariozechner/pi-tui";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
||||||
import { truncateToVisualLines } from "../../modes/interactive/components/visual-truncate.js";
|
import { truncateToVisualLines } from "../../modes/interactive/components/visual-truncate.js";
|
||||||
import { theme } from "../../modes/interactive/theme/theme.js";
|
import { theme } from "../../modes/interactive/theme/theme.js";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Box, Container, Spacer, Text } from "@mariozechner/pi-tui";
|
import { Box, Container, Spacer, Text } from "@mariozechner/pi-tui";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { constants } from "fs";
|
import { constants } from "fs";
|
||||||
import { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from "fs/promises";
|
import { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from "fs/promises";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { renderDiff } from "../../modes/interactive/components/diff.js";
|
import { renderDiff } from "../../modes/interactive/components/diff.js";
|
||||||
import type { ToolDefinition } from "../extensions/types.js";
|
import type { ToolDefinition } from "../extensions/types.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { createInterface } from "node:readline";
|
import { createInterface } from "node:readline";
|
||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Text } from "@mariozechner/pi-tui";
|
import { Text } from "@mariozechner/pi-tui";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
||||||
import { ensureTool } from "../../utils/tools-manager.js";
|
import { ensureTool } from "../../utils/tools-manager.js";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { createInterface } from "node:readline";
|
import { createInterface } from "node:readline";
|
||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Text } from "@mariozechner/pi-tui";
|
import { Text } from "@mariozechner/pi-tui";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
import { readFileSync, statSync } from "fs";
|
import { readFileSync, statSync } from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
||||||
import { ensureTool } from "../../utils/tools-manager.js";
|
import { ensureTool } from "../../utils/tools-manager.js";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Text } from "@mariozechner/pi-tui";
|
import { Text } from "@mariozechner/pi-tui";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { existsSync, readdirSync, statSync } from "fs";
|
import { existsSync, readdirSync, statSync } from "fs";
|
||||||
import nodePath from "path";
|
import nodePath from "path";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
||||||
import { resolveToCwd } from "./path-utils.js";
|
import { resolveToCwd } from "./path-utils.js";
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import type { Api, ImageContent, Model, TextContent } from "@mariozechner/pi-ai";
|
import type { Api, ImageContent, Model, TextContent } from "@mariozechner/pi-ai";
|
||||||
import { Text } from "@mariozechner/pi-tui";
|
import { Text } from "@mariozechner/pi-tui";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { constants } from "fs";
|
import { constants } from "fs";
|
||||||
import { access as fsAccess, readFile as fsReadFile } from "fs/promises";
|
import { access as fsAccess, readFile as fsReadFile } from "fs/promises";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
||||||
import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.js";
|
import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.js";
|
||||||
import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
|
import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Container, Text } from "@mariozechner/pi-tui";
|
import { Container, Text } from "@mariozechner/pi-tui";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { mkdir as fsMkdir, writeFile as fsWriteFile } from "fs/promises";
|
import { mkdir as fsMkdir, writeFile as fsWriteFile } from "fs/promises";
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
||||||
import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.js";
|
import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.js";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as fs from "node:fs";
|
import * as fs from "node:fs";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import type { EditorTheme, MarkdownTheme, SelectListTheme } from "@mariozechner/pi-tui";
|
import type { EditorTheme, MarkdownTheme, SelectListTheme } from "@mariozechner/pi-tui";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { highlight, supportsLanguage } from "cli-highlight";
|
import { highlight, supportsLanguage } from "cli-highlight";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
|
import { Compile } from "typebox/compile";
|
||||||
import { getCustomThemesDir, getThemesDir } from "../../../config.js";
|
import { getCustomThemesDir, getThemesDir } from "../../../config.js";
|
||||||
import type { SourceInfo } from "../../../core/source-info.js";
|
import type { SourceInfo } from "../../../core/source-info.js";
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ const ThemeJsonSchema = Type.Object({
|
|||||||
|
|
||||||
type ThemeJson = Static<typeof ThemeJsonSchema>;
|
type ThemeJson = Static<typeof ThemeJsonSchema>;
|
||||||
|
|
||||||
const validateThemeJson = TypeCompiler.Compile(ThemeJsonSchema);
|
const validateThemeJson = Compile(ThemeJsonSchema);
|
||||||
|
|
||||||
export type ThemeColor =
|
export type ThemeColor =
|
||||||
| "accent"
|
| "accent"
|
||||||
@@ -515,23 +515,29 @@ export function getAvailableThemesWithPaths(): ThemeInfo[] {
|
|||||||
function parseThemeJson(label: string, json: unknown): ThemeJson {
|
function parseThemeJson(label: string, json: unknown): ThemeJson {
|
||||||
if (!validateThemeJson.Check(json)) {
|
if (!validateThemeJson.Check(json)) {
|
||||||
const errors = Array.from(validateThemeJson.Errors(json));
|
const errors = Array.from(validateThemeJson.Errors(json));
|
||||||
const missingColors: string[] = [];
|
const missingColors = new Set<string>();
|
||||||
const otherErrors: string[] = [];
|
const otherErrors: string[] = [];
|
||||||
|
|
||||||
for (const e of errors) {
|
for (const error of errors) {
|
||||||
// Check for missing required color properties
|
if (error.keyword === "required" && error.instancePath === "/colors") {
|
||||||
const match = e.path.match(/^\/colors\/(\w+)$/);
|
const requiredProperties = (error.params as { requiredProperties?: string[] }).requiredProperties;
|
||||||
if (match && e.message.includes("Required")) {
|
for (const requiredProperty of requiredProperties ?? []) {
|
||||||
missingColors.push(match[1]);
|
missingColors.add(requiredProperty);
|
||||||
} else {
|
}
|
||||||
otherErrors.push(` - ${e.path}: ${e.message}`);
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const path = error.instancePath || "/";
|
||||||
|
otherErrors.push(` - ${path}: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let errorMessage = `Invalid theme "${label}":\n`;
|
let errorMessage = `Invalid theme "${label}":\n`;
|
||||||
if (missingColors.length > 0) {
|
if (missingColors.size > 0) {
|
||||||
errorMessage += "\nMissing required color tokens:\n";
|
errorMessage += "\nMissing required color tokens:\n";
|
||||||
errorMessage += missingColors.map((c) => ` - ${c}`).join("\n");
|
errorMessage += Array.from(missingColors)
|
||||||
|
.sort()
|
||||||
|
.map((color) => ` - ${color}`)
|
||||||
|
.join("\n");
|
||||||
errorMessage += '\n\nPlease add these colors to your theme\'s "colors" object.';
|
errorMessage += '\n\nPlease add these colors to your theme\'s "colors" object.';
|
||||||
errorMessage += "\nSee the built-in themes (dark.json, light.json) for reference values.";
|
errorMessage += "\nSee the built-in themes (dark.json, light.json) for reference values.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
type ImageContent,
|
type ImageContent,
|
||||||
type TextContent,
|
type TextContent,
|
||||||
} from "@mariozechner/pi-ai";
|
} from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||||
import { AgentSession } from "../src/core/agent-session.js";
|
import { AgentSession } from "../src/core/agent-session.js";
|
||||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { getModel } from "@mariozechner/pi-ai";
|
import { getModel } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||||
import { DefaultResourceLoader } from "../src/core/resource-loader.js";
|
import { DefaultResourceLoader } from "../src/core/resource-loader.js";
|
||||||
import { createAgentSession } from "../src/core/sdk.js";
|
import { createAgentSession } from "../src/core/sdk.js";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { Agent, type AgentEvent, type AgentTool } from "@mariozechner/pi-agent-core";
|
import { Agent, type AgentEvent, type AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { type AssistantMessage, type AssistantMessageEvent, EventStream, getModel } from "@mariozechner/pi-ai";
|
import { type AssistantMessage, type AssistantMessageEvent, EventStream, getModel } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||||
import { AgentSession } from "../src/core/agent-session.js";
|
import { AgentSession } from "../src/core/agent-session.js";
|
||||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ describe("extensions discovery", () => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const extensionCodeWithTool = (toolName: string) => `
|
const extensionCodeWithTool = (toolName: string) => `
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
export default function(pi) {
|
export default function(pi) {
|
||||||
pi.registerTool({
|
pi.registerTool({
|
||||||
name: "${toolName}",
|
name: "${toolName}",
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ describe("ExtensionRunner", () => {
|
|||||||
describe("tool collection", () => {
|
describe("tool collection", () => {
|
||||||
it("collects tools from multiple extensions", async () => {
|
it("collects tools from multiple extensions", async () => {
|
||||||
const toolCode = (name: string) => `
|
const toolCode = (name: string) => `
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
export default function(pi) {
|
export default function(pi) {
|
||||||
pi.registerTool({
|
pi.registerTool({
|
||||||
name: "${name}",
|
name: "${name}",
|
||||||
@@ -315,7 +315,7 @@ describe("ExtensionRunner", () => {
|
|||||||
|
|
||||||
it("keeps first tool when two extensions register the same name", async () => {
|
it("keeps first tool when two extensions register the same name", async () => {
|
||||||
const first = `
|
const first = `
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
export default function(pi) {
|
export default function(pi) {
|
||||||
pi.registerTool({
|
pi.registerTool({
|
||||||
name: "shared",
|
name: "shared",
|
||||||
@@ -327,7 +327,7 @@ describe("ExtensionRunner", () => {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const second = `
|
const second = `
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
export default function(pi) {
|
export default function(pi) {
|
||||||
pi.registerTool({
|
pi.registerTool({
|
||||||
name: "shared",
|
name: "shared",
|
||||||
|
|||||||
@@ -471,7 +471,7 @@ Content`,
|
|||||||
join(ext1Dir, "index.ts"),
|
join(ext1Dir, "index.ts"),
|
||||||
`
|
`
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
export default function(pi: ExtensionAPI) {
|
export default function(pi: ExtensionAPI) {
|
||||||
pi.registerTool({
|
pi.registerTool({
|
||||||
name: "duplicate-tool",
|
name: "duplicate-tool",
|
||||||
@@ -486,7 +486,7 @@ export default function(pi: ExtensionAPI) {
|
|||||||
join(ext2Dir, "index.ts"),
|
join(ext2Dir, "index.ts"),
|
||||||
`
|
`
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
export default function(pi: ExtensionAPI) {
|
export default function(pi: ExtensionAPI) {
|
||||||
pi.registerTool({
|
pi.registerTool({
|
||||||
name: "duplicate-tool",
|
name: "duplicate-tool",
|
||||||
@@ -513,7 +513,7 @@ export default function(pi: ExtensionAPI) {
|
|||||||
join(globalExtDir, "global.ts"),
|
join(globalExtDir, "global.ts"),
|
||||||
`
|
`
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
export default function(pi: ExtensionAPI) {
|
export default function(pi: ExtensionAPI) {
|
||||||
pi.registerTool({
|
pi.registerTool({
|
||||||
name: "duplicate-tool",
|
name: "duplicate-tool",
|
||||||
@@ -532,7 +532,7 @@ export default function(pi: ExtensionAPI) {
|
|||||||
explicitExtPath,
|
explicitExtPath,
|
||||||
`
|
`
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
export default function(pi: ExtensionAPI) {
|
export default function(pi: ExtensionAPI) {
|
||||||
pi.registerTool({
|
pi.registerTool({
|
||||||
name: "duplicate-tool",
|
name: "duplicate-tool",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ async function runCli(args: string[]): Promise<{ stdout: string; stderr: string;
|
|||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
[ENV_AGENT_DIR]: agentDir,
|
[ENV_AGENT_DIR]: agentDir,
|
||||||
|
TSX_TSCONFIG_PATH: resolve(__dirname, "../../../tsconfig.json"),
|
||||||
},
|
},
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Buffer } from "node:buffer";
|
import { Buffer } from "node:buffer";
|
||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { fauxAssistantMessage, fauxToolCall } from "@mariozechner/pi-ai";
|
import { fauxAssistantMessage, fauxToolCall } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { BashOperations } from "../../src/core/tools/bash.js";
|
import type { BashOperations } from "../../src/core/tools/bash.js";
|
||||||
import { createHarness, type Harness } from "./harness.js";
|
import { createHarness, type Harness } from "./harness.js";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { AgentTool, ThinkingLevel } from "@mariozechner/pi-agent-core";
|
import type { AgentTool, ThinkingLevel } from "@mariozechner/pi-agent-core";
|
||||||
import { fauxAssistantMessage, fauxToolCall, type Model } from "@mariozechner/pi-ai";
|
import { fauxAssistantMessage, fauxToolCall, type Model } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { ExtensionAPI } from "../../src/index.js";
|
import type { ExtensionAPI } from "../../src/index.js";
|
||||||
import { createHarness, getAssistantTexts, type Harness } from "./harness.js";
|
import { createHarness, getAssistantTexts, type Harness } from "./harness.js";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { fauxAssistantMessage, fauxToolCall, type Model } from "@mariozechner/pi-ai";
|
import { fauxAssistantMessage, fauxToolCall, type Model } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { PromptTemplate } from "../../src/core/prompt-templates.js";
|
import type { PromptTemplate } from "../../src/core/prompt-templates.js";
|
||||||
import { createSyntheticSourceInfo } from "../../src/core/source-info.js";
|
import { createSyntheticSourceInfo } from "../../src/core/source-info.js";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { fauxAssistantMessage, fauxToolCall } from "@mariozechner/pi-ai";
|
import { fauxAssistantMessage, fauxToolCall } from "@mariozechner/pi-ai";
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import { createHarness, getAssistantTexts, getMessageText, getUserTexts, type Harness } from "./harness.js";
|
import { createHarness, getAssistantTexts, getMessageText, getUserTexts, type Harness } from "./harness.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { fauxAssistantMessage, fauxThinking, fauxToolCall } from "@mariozechner/pi-ai";
|
import { fauxAssistantMessage, fauxThinking, fauxToolCall } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import { createHarness, type Harness } from "./harness.js";
|
import { createHarness, type Harness } from "./harness.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { fauxAssistantMessage, fauxToolCall } from "@mariozechner/pi-ai";
|
import { fauxAssistantMessage, fauxToolCall } from "@mariozechner/pi-ai";
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import { createHarness, getAssistantTexts, getUserTexts, type Harness } from "../harness.js";
|
import { createHarness, getAssistantTexts, getUserTexts, type Harness } from "../harness.js";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { getModel } from "@mariozechner/pi-ai";
|
import { getModel } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||||
import { DefaultResourceLoader } from "../../../src/core/resource-loader.js";
|
import { DefaultResourceLoader } from "../../../src/core/resource-loader.js";
|
||||||
import { createAgentSession } from "../../../src/core/sdk.js";
|
import { createAgentSession } from "../../../src/core/sdk.js";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import { createHarness, createHarnessWithExtensions, type Harness } from "./test-harness.js";
|
import { createHarness, createHarnessWithExtensions, type Harness } from "./test-harness.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Text, type TUI } from "@mariozechner/pi-tui";
|
import { Text, type TUI } from "@mariozechner/pi-tui";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import stripAnsi from "strip-ansi";
|
import stripAnsi from "strip-ansi";
|
||||||
|
import { Type } from "typebox";
|
||||||
import { beforeAll, describe, expect, test } from "vitest";
|
import { beforeAll, describe, expect, test } from "vitest";
|
||||||
import type { ToolDefinition } from "../src/core/extensions/types.js";
|
import type { ToolDefinition } from "../src/core/extensions/types.js";
|
||||||
import { type BashOperations, createBashToolDefinition } from "../src/core/tools/bash.js";
|
import { type BashOperations, createBashToolDefinition } from "../src/core/tools/bash.js";
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"@mariozechner/pi-coding-agent/hooks": ["./src/core/hooks/index.ts"],
|
"@mariozechner/pi-coding-agent/hooks": ["./src/core/hooks/index.ts"],
|
||||||
"@mariozechner/pi-tui": ["../tui/src/index.ts"],
|
"@mariozechner/pi-tui": ["../tui/src/index.ts"],
|
||||||
"@mariozechner/pi-ai": ["../ai/src/index.ts"],
|
"@mariozechner/pi-ai": ["../ai/src/index.ts"],
|
||||||
"@sinclair/typebox": ["../../node_modules/@sinclair/typebox"]
|
"typebox": ["../../node_modules/typebox"]
|
||||||
},
|
},
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
import { defineConfig } from 'vitest/config';
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
const aiSrcIndex = fileURLToPath(new URL("../ai/src/index.ts", import.meta.url));
|
||||||
|
const aiSrcOAuth = fileURLToPath(new URL("../ai/src/oauth.ts", import.meta.url));
|
||||||
|
const agentSrcIndex = fileURLToPath(new URL("../agent/src/index.ts", import.meta.url));
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
globals: true,
|
globals: true,
|
||||||
environment: 'node',
|
environment: "node",
|
||||||
testTimeout: 30000, // 30 seconds for API calls
|
testTimeout: 30000,
|
||||||
server: {
|
server: {
|
||||||
deps: {
|
deps: {
|
||||||
external: [/@silvia-odwyer\/photon-node/],
|
external: [/@silvia-odwyer\/photon-node/],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: [
|
||||||
|
{ find: /^@mariozechner\/pi-ai$/, replacement: aiSrcIndex },
|
||||||
|
{ find: /^@mariozechner\/pi-ai\/oauth$/, replacement: aiSrcOAuth },
|
||||||
|
{ find: /^@mariozechner\/pi-agent-core$/, replacement: agentSrcIndex },
|
||||||
|
],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"@mariozechner/pi-agent-core": "^0.68.1",
|
"@mariozechner/pi-agent-core": "^0.68.1",
|
||||||
"@mariozechner/pi-ai": "^0.68.1",
|
"@mariozechner/pi-ai": "^0.68.1",
|
||||||
"@mariozechner/pi-coding-agent": "^0.68.1",
|
"@mariozechner/pi-coding-agent": "^0.68.1",
|
||||||
"@sinclair/typebox": "^0.34.0",
|
"typebox": "^1.1.24",
|
||||||
"@slack/socket-mode": "^2.0.0",
|
"@slack/socket-mode": "^2.0.0",
|
||||||
"@slack/web-api": "^7.0.0",
|
"@slack/web-api": "^7.0.0",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import { basename, resolve as resolvePath } from "path";
|
import { basename, resolve as resolvePath } from "path";
|
||||||
|
import { Type } from "typebox";
|
||||||
|
|
||||||
// This will be set by the agent before running
|
// This will be set by the agent before running
|
||||||
let uploadFn: ((filePath: string, title?: string) => Promise<void>) | null = null;
|
let uploadFn: ((filePath: string, title?: string) => Promise<void>) | null = null;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createWriteStream } from "node:fs";
|
|||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import type { Executor } from "../sandbox.js";
|
import type { Executor } from "../sandbox.js";
|
||||||
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from "./truncate.js";
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from "./truncate.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import * as Diff from "diff";
|
import * as Diff from "diff";
|
||||||
|
import { Type } from "typebox";
|
||||||
import type { Executor } from "../sandbox.js";
|
import type { Executor } from "../sandbox.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import type { ImageContent, TextContent } from "@mariozechner/pi-ai";
|
import type { ImageContent, TextContent } from "@mariozechner/pi-ai";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import { extname } from "path";
|
import { extname } from "path";
|
||||||
|
import { Type } from "typebox";
|
||||||
import type { Executor } from "../sandbox.js";
|
import type { Executor } from "../sandbox.js";
|
||||||
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from "./truncate.js";
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from "./truncate.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "typebox";
|
||||||
import type { Executor } from "../sandbox.js";
|
import type { Executor } from "../sandbox.js";
|
||||||
|
|
||||||
const writeSchema = Type.Object({
|
const writeSchema = Type.Object({
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"@lmstudio/sdk": "^1.5.0",
|
"@lmstudio/sdk": "^1.5.0",
|
||||||
"@mariozechner/pi-ai": "^0.68.1",
|
"@mariozechner/pi-ai": "^0.68.1",
|
||||||
"@mariozechner/pi-tui": "^0.68.1",
|
"@mariozechner/pi-tui": "^0.68.1",
|
||||||
|
"typebox": "^1.1.24",
|
||||||
"docx-preview": "^0.3.7",
|
"docx-preview": "^0.3.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"lucide": "^0.544.0",
|
"lucide": "^0.544.0",
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
|
|||||||
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
|
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
|
||||||
import type { Agent, AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
import type { Agent, AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import { StringEnum, type ToolCall } from "@mariozechner/pi-ai";
|
import { StringEnum, type ToolCall } from "@mariozechner/pi-ai";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { html, LitElement, type TemplateResult } from "lit";
|
import { html, LitElement, type TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { createRef, type Ref, ref } from "lit/directives/ref.js";
|
import { createRef, type Ref, ref } from "lit/directives/ref.js";
|
||||||
import { X } from "lucide";
|
import { X } from "lucide";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import type { ArtifactMessage } from "../../components/Messages.js";
|
import type { ArtifactMessage } from "../../components/Messages.js";
|
||||||
import { ArtifactsRuntimeProvider } from "../../components/sandbox/ArtifactsRuntimeProvider.js";
|
import { ArtifactsRuntimeProvider } from "../../components/sandbox/ArtifactsRuntimeProvider.js";
|
||||||
import { AttachmentsRuntimeProvider } from "../../components/sandbox/AttachmentsRuntimeProvider.js";
|
import { AttachmentsRuntimeProvider } from "../../components/sandbox/AttachmentsRuntimeProvider.js";
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { createRef, ref } from "lit/directives/ref.js";
|
import { createRef, ref } from "lit/directives/ref.js";
|
||||||
import { FileText } from "lucide";
|
import { FileText } from "lucide";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { EXTRACT_DOCUMENT_DESCRIPTION } from "../prompts/prompts.js";
|
import { EXTRACT_DOCUMENT_DESCRIPTION } from "../prompts/prompts.js";
|
||||||
import { loadAttachment } from "../utils/attachment-utils.js";
|
import { loadAttachment } from "../utils/attachment-utils.js";
|
||||||
import { isCorsError } from "../utils/proxy-utils.js";
|
import { isCorsError } from "../utils/proxy-utils.js";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { i18n } from "@mariozechner/mini-lit";
|
import { i18n } from "@mariozechner/mini-lit";
|
||||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { createRef, ref } from "lit/directives/ref.js";
|
import { createRef, ref } from "lit/directives/ref.js";
|
||||||
import { Code } from "lucide";
|
import { Code } from "lucide";
|
||||||
|
import { type Static, Type } from "typebox";
|
||||||
import { type SandboxFile, SandboxIframe, type SandboxResult } from "../components/SandboxedIframe.js";
|
import { type SandboxFile, SandboxIframe, type SandboxResult } from "../components/SandboxedIframe.js";
|
||||||
import type { SandboxRuntimeProvider } from "../components/sandbox/SandboxRuntimeProvider.js";
|
import type { SandboxRuntimeProvider } from "../components/sandbox/SandboxRuntimeProvider.js";
|
||||||
import { JAVASCRIPT_REPL_TOOL_DESCRIPTION } from "../prompts/prompts.js";
|
import { JAVASCRIPT_REPL_TOOL_DESCRIPTION } from "../prompts/prompts.js";
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"@mariozechner/pi-coding-agent": ["./packages/coding-agent/src/index.ts"],
|
"@mariozechner/pi-coding-agent": ["./packages/coding-agent/src/index.ts"],
|
||||||
"@mariozechner/pi-coding-agent/hooks": ["./packages/coding-agent/src/core/hooks/index.ts"],
|
"@mariozechner/pi-coding-agent/hooks": ["./packages/coding-agent/src/core/hooks/index.ts"],
|
||||||
"@mariozechner/pi-coding-agent/*": ["./packages/coding-agent/src/*"],
|
"@mariozechner/pi-coding-agent/*": ["./packages/coding-agent/src/*"],
|
||||||
"@sinclair/typebox": ["./node_modules/@sinclair/typebox"],
|
"typebox": ["./node_modules/typebox"],
|
||||||
"@mariozechner/pi-mom": ["./packages/mom/src/index.ts"],
|
"@mariozechner/pi-mom": ["./packages/mom/src/index.ts"],
|
||||||
"@mariozechner/pi-mom/*": ["./packages/mom/src/*"],
|
"@mariozechner/pi-mom/*": ["./packages/mom/src/*"],
|
||||||
"@mariozechner/pi": ["./packages/pods/src/index.ts"],
|
"@mariozechner/pi": ["./packages/pods/src/index.ts"],
|
||||||
|
|||||||
Reference in New Issue
Block a user