refactor(coding-agent)!: removed StringEnum helper and shortened tool schema descriptions
- Replaced all StringEnum(...) usages with z.enum([...]) across tools, examples, and tests. - Removed StringEnum re-export from @oh-my-pi/pi-coding-agent public API. - Condensed verbose tool parameter descriptions to minimal lowercase phrases. - Renamed AuthCredentialStore to SqliteAuthCredentialStore at usage sites.
This commit is contained in:
@@ -119,7 +119,7 @@ Supported formats:
|
||||
|
||||
Behavior:
|
||||
|
||||
- Validates parsed data with AJV against a provided TypeBox schema.
|
||||
- Validates parsed data against a provided Zod schema.
|
||||
- Caches load result until `invalidate()`.
|
||||
- Returns tri-state result via `tryLoad()`:
|
||||
- `ok`
|
||||
|
||||
@@ -6,7 +6,7 @@ A custom tool is a TypeScript/JavaScript module that exports a factory. The fact
|
||||
|
||||
## What this is (and is not)
|
||||
|
||||
- **Custom tool**: callable by the model during a turn (`execute` + Zod parameter schema; legacy TypeBox is still accepted and lifted to Zod at registration).
|
||||
- **Custom tool**: callable by the model during a turn (`execute` + Zod parameter schema).
|
||||
- **Extension**: lifecycle/event framework that can register tools and intercept/modify events.
|
||||
- **Hook**: external pre/post command scripts.
|
||||
- **Skill**: static guidance/context package, not executable tool code.
|
||||
@@ -105,7 +105,7 @@ const factory: CustomToolFactory = (pi) => ({
|
||||
export default factory;
|
||||
```
|
||||
|
||||
Legacy TypeBox-authored factories can still call `pi.typebox` — it's now a small Zod-backed shim (`Type.Object`, `Type.String`, etc.) baked into the host, not the real `@sinclair/typebox` package. Schemas flow through the same Zod pipeline as `pi.zod` and need no separate normalization.
|
||||
Schemas are authored with Zod (`pi.zod`) and flow through the shared validation/wire pipeline.
|
||||
|
||||
Factory return type:
|
||||
|
||||
@@ -122,8 +122,7 @@ From `types.ts` and `loader.ts`:
|
||||
- `ui`: UI context (can be no-op in headless modes)
|
||||
- `hasUI`: `false` in non-interactive flows
|
||||
- `logger`: shared file logger
|
||||
- `zod`: injected `zod` module (**preferred** for new tool schemas; use `pi.zod.object`, `pi.zod.string`, …)
|
||||
- `typebox`: injected zod-backed `Type.*` shim (legacy extension compatibility)
|
||||
- `zod`: injected `zod` module (use `pi.zod.object`, `pi.zod.string`, …)
|
||||
- `pi`: injected `@oh-my-pi/pi-coding-agent` exports
|
||||
- `pushPendingAction(action)`: register a preview action for hidden `resolve` tool (`docs/resolve-tool-runtime.md`)
|
||||
|
||||
@@ -137,7 +136,7 @@ Loader starts with a no-op UI context and requires host code to call `setUIConte
|
||||
execute(toolCallId, params, onUpdate, ctx, signal);
|
||||
```
|
||||
|
||||
- `params` is statically typed from your Zod schema via `z.infer<typeof schema>` (`Static<TParams>` in API types). Legacy TypeBox schemas are lifted to Zod internally.
|
||||
- `params` is statically typed from your Zod schema via `z.infer<typeof schema>` (`Static<TParams>` in API types).
|
||||
- Runtime argument validation happens before execution in the agent loop.
|
||||
- `onUpdate` emits partial results for UI streaming.
|
||||
- `ctx` includes session/model state and an `abort()` helper.
|
||||
|
||||
+1
-2
@@ -125,8 +125,7 @@ In interactive mode, `input` handlers run before the built-in first-message auto
|
||||
Also exposed:
|
||||
|
||||
- `pi.logger`
|
||||
- `pi.zod` (injected `zod` module — **preferred** for new tool schemas)
|
||||
- `pi.typebox` (zod-backed `Type.*` shim — retained for legacy extension compat)
|
||||
- `pi.zod` (injected `zod` module — use for tool parameter schemas)
|
||||
- `pi.pi` (package exports)
|
||||
|
||||
### Message delivery semantics
|
||||
|
||||
+1
-1
@@ -341,6 +341,6 @@ Use this workflow:
|
||||
|
||||
- All `colors` tokens are required for custom themes.
|
||||
- `export` and `symbols` are optional.
|
||||
- `$schema` in theme JSON is informational; runtime validation is enforced by compiled TypeBox schema in code.
|
||||
- `$schema` in theme JSON is informational; runtime validation is enforced by a Zod schema in code.
|
||||
- `setTheme` failure falls back to `dark`; `previewTheme` failure does not replace current theme.
|
||||
- File watcher reload errors or temporary missing files keep the current loaded theme until a successful reload or explicit theme switch.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `goal` | `string` | Yes | Investigation goal. Required by the TypeBox schema and echoed in the tool result. |
|
||||
| `goal` | `string` | Yes | Investigation goal. Required by the schema and echoed in the tool result. |
|
||||
|
||||
## Outputs
|
||||
The tool returns a single text result plus structured details:
|
||||
|
||||
@@ -279,7 +279,7 @@ const agent = new Agent({
|
||||
|
||||
## Tools
|
||||
|
||||
Define tools using `AgentTool` with a Zod parameter schema (via `z` from `@oh-my-pi/pi-ai`). Legacy TypeBox-authored schemas are still accepted at runtime and are lifted to Zod internally.
|
||||
Define tools using `AgentTool` with a Zod parameter schema (via `z` from `@oh-my-pi/pi-ai`).
|
||||
|
||||
```typescript
|
||||
import { z } from "@oh-my-pi/pi-ai";
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
- Renamed public schema utilities in `@oh-my-pi/pi-ai/utils/schema` by replacing `sanitizeSchemaForGoogle`, `sanitizeSchemaForCCA`, `prepareSchemaForCCA`, and `sanitizeSchemaForMCP` with `normalizeSchemaForGoogle`, `normalizeSchemaForCCA`, and `normalizeSchemaForMCP`
|
||||
- Added MCP schema normalization via `normalizeSchemaForMCP` for compatibility checks
|
||||
- Removed the `StringEnum` helper from `@oh-my-pi/pi-ai/utils/schema`. Use `z.enum([...])` directly; Zod's emitted JSON Schema is already wire-compatible with Google and other providers.
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ npm install @oh-my-pi/pi-ai
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { z, getModel, stream, complete, Context, Tool, StringEnum } from "@oh-my-pi/pi-ai";
|
||||
import { z, getModel, stream, complete, Context, Tool } from "@oh-my-pi/pi-ai";
|
||||
|
||||
// Fully typed with auto-complete support for both providers and models
|
||||
const model = getModel("openai", "gpt-4o-mini");
|
||||
@@ -221,7 +221,7 @@ Tools enable LLMs to interact with external systems. This library uses **Zod** s
|
||||
### Defining Tools
|
||||
|
||||
```typescript
|
||||
import { z, Tool, StringEnum } from "@oh-my-pi/pi-ai";
|
||||
import { z, Tool } from "@oh-my-pi/pi-ai";
|
||||
|
||||
// Define tool parameters with Zod
|
||||
const weatherTool: Tool = {
|
||||
@@ -229,13 +229,10 @@ const weatherTool: Tool = {
|
||||
description: "Get current weather for a location",
|
||||
parameters: z.object({
|
||||
location: z.string().describe("City name or coordinates"),
|
||||
units: StringEnum(["celsius", "fahrenheit"], { default: "celsius" }),
|
||||
units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
|
||||
}),
|
||||
};
|
||||
|
||||
// Note: For Google API compatibility, use the StringEnum helper instead of z.enum alone
|
||||
// when you need wire-compatible { type: "string", enum: [...] } shapes.
|
||||
|
||||
const bookMeetingTool: Tool = {
|
||||
name: "book_meeting",
|
||||
description: "Schedule a meeting",
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Effort } from "@oh-my-pi/pi-ai";
|
||||
import { getBundledModel } from "@oh-my-pi/pi-ai/models";
|
||||
import { complete, getEnvApiKey, stream } from "@oh-my-pi/pi-ai/stream";
|
||||
import type { Api, Context, ImageContent, Model, OptionsForApi, Tool, ToolResultMessage } from "@oh-my-pi/pi-ai/types";
|
||||
import { StringEnum } from "@oh-my-pi/pi-ai/utils/schema";
|
||||
import { $which } from "@oh-my-pi/pi-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { e2eApiKey, resolveApiKey } from "./oauth";
|
||||
@@ -34,14 +33,12 @@ function hasBedrockCredentials(): boolean {
|
||||
}
|
||||
|
||||
// Calculator tool definition (same as examples)
|
||||
// Note: Using StringEnum helper because Google's API doesn't support anyOf/const patterns
|
||||
// that some schema authors emit for string unions. Google requires { type: "string", enum: [...] } format.
|
||||
const calculatorSchema = z.object({
|
||||
a: z.number().describe("First number"),
|
||||
b: z.number().describe("Second number"),
|
||||
operation: StringEnum(["add", "subtract", "multiply", "divide"], {
|
||||
description: "The operation to perform. One of 'add', 'subtract', 'multiply', 'divide'.",
|
||||
}),
|
||||
operation: z
|
||||
.enum(["add", "subtract", "multiply", "divide"])
|
||||
.describe("The operation to perform. One of 'add', 'subtract', 'multiply', 'divide'."),
|
||||
});
|
||||
|
||||
const calculatorTool: Tool<typeof calculatorSchema> = {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
### Breaking Changes
|
||||
|
||||
- Renamed the embedded-documentation internal URL scheme from `pi://` to `omp://`. `OmpProtocolHandler` replaces `PiProtocolHandler`; update any external references accordingly.
|
||||
- Removed the `StringEnum` re-export from `@oh-my-pi/pi-coding-agent`. Custom tools and extensions should use `z.enum([...])` directly via the injected `pi.zod`.
|
||||
|
||||
## [15.1.2] - 2026-05-15
|
||||
### Fixed
|
||||
|
||||
@@ -47,7 +47,6 @@ See [docs/custom-tools.md](../../docs/custom-tools.md) for full documentation.
|
||||
**Factory pattern:**
|
||||
|
||||
```typescript
|
||||
import { StringEnum } from "@oh-my-pi/pi-ai";
|
||||
import { Text } from "@oh-my-pi/pi-tui";
|
||||
import type { CustomToolFactory } from "@oh-my-pi/pi-coding-agent";
|
||||
|
||||
@@ -56,7 +55,7 @@ const factory: CustomToolFactory = (pi) => ({
|
||||
label: "My Tool",
|
||||
description: "Tool description for LLM",
|
||||
parameters: pi.zod.object({
|
||||
action: StringEnum(["list", "add"] as const),
|
||||
action: pi.zod.enum(["list", "add"]),
|
||||
}),
|
||||
|
||||
// Called on session start/switch/branch/clear
|
||||
@@ -76,9 +75,6 @@ const factory: CustomToolFactory = (pi) => ({
|
||||
|
||||
export default factory;
|
||||
```
|
||||
|
||||
**Legacy:** `parameters: pi.typebox.Type.Object({ ... })` still works; the injected `typebox` is a small Zod-backed shim, and schemas flow through the same Zod pipeline as `pi.zod` schemas.
|
||||
|
||||
**Custom rendering:**
|
||||
|
||||
```typescript
|
||||
@@ -97,17 +93,12 @@ renderResult(result, { expanded, isPartial }, theme) {
|
||||
},
|
||||
```
|
||||
|
||||
**Use `StringEnum` for discriminated string tool args** (required for Google API compatibility):
|
||||
**Use `z.enum` for discriminated string tool args:**
|
||||
|
||||
```typescript
|
||||
import { StringEnum } from "@oh-my-pi/pi-ai";
|
||||
|
||||
const { z } = pi.zod;
|
||||
|
||||
// Good — Google-safe enum wiring
|
||||
parameters: z.object({
|
||||
action: StringEnum(["list", "add"] as const),
|
||||
action: z.enum(["list", "add"]),
|
||||
});
|
||||
|
||||
// Avoid raw union-of-literals patterns that don't degrade well for strict JSON Schema providers
|
||||
```
|
||||
|
||||
@@ -108,29 +108,16 @@ export default function (pi: ExtensionAPI) {
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Legacy TypeBox-style schemas** (`pi.typebox`) remain available for older extensions and are backed by a tiny Zod-shim — prefer `pi.zod` directly for new code.
|
||||
|
||||
```typescript
|
||||
const { Type } = pi.typebox;
|
||||
parameters: Type.Object({ name: Type.String() });
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
**Use `StringEnum` for discriminated string tool args** (required for Google API compatibility):
|
||||
**Use `z.enum` for discriminated string tool args:**
|
||||
|
||||
```typescript
|
||||
import { StringEnum } from "@oh-my-pi/pi-ai";
|
||||
|
||||
const { z } = pi.zod;
|
||||
|
||||
// Good — Google-safe enum wiring
|
||||
parameters: z.object({
|
||||
action: StringEnum(["list", "add"] as const),
|
||||
action: z.enum(["list", "add"]),
|
||||
});
|
||||
|
||||
// Avoid raw union-of-literals patterns that don't degrade well for strict JSON Schema providers
|
||||
```
|
||||
|
||||
**State persistence via details:**
|
||||
|
||||
@@ -10,9 +10,6 @@ import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
||||
export default function (pi: ExtensionAPI) {
|
||||
const { z } = pi.zod;
|
||||
|
||||
// Access shared schema helpers from package exports (e.g. StringEnum for Google-safe enums)
|
||||
const { StringEnum } = pi.pi;
|
||||
|
||||
// Access the logger for debugging
|
||||
pi.logger.debug("API demo extension loaded");
|
||||
|
||||
@@ -22,10 +19,7 @@ export default function (pi: ExtensionAPI) {
|
||||
description: "Demonstrates ExtensionAPI capabilities: logger, zod, and pi module access",
|
||||
parameters: z.object({
|
||||
message: z.string().describe("Test message"),
|
||||
logLevel: StringEnum(["error", "warn", "debug"], {
|
||||
description: "Log level to use",
|
||||
default: "debug",
|
||||
}),
|
||||
logLevel: z.enum(["error", "warn", "debug"]).default("debug").describe("Log level to use"),
|
||||
}),
|
||||
|
||||
async execute(_toolCallId, params, _onUpdate, ctx, _signal) {
|
||||
|
||||
@@ -17,42 +17,20 @@ export const DEFAULT_HARNESS_COMMAND = `bash ${HARNESS_FILENAME}`;
|
||||
const HARNESS_COMMIT_TITLE = "autoresearch: harness setup";
|
||||
|
||||
const initExperimentSchema = z.object({
|
||||
name: z.string().describe("Human-readable experiment name."),
|
||||
goal: z.string().describe("Free-form description of what this session optimizes.").optional(),
|
||||
primary_metric: z
|
||||
.string()
|
||||
.describe(
|
||||
"Primary metric name shown in the dashboard. Match the `METRIC <name>=<value>` lines printed by the benchmark.",
|
||||
),
|
||||
metric_unit: z.string().describe("Unit for the primary metric (e.g. ms, µs, mb). Empty when unitless.").optional(),
|
||||
name: z.string().describe("experiment name"),
|
||||
goal: z.string().describe("session goal").optional(),
|
||||
primary_metric: z.string().describe("primary metric name"),
|
||||
metric_unit: z.string().describe("metric unit (e.g. ms, µs, mb)").optional(),
|
||||
direction: z
|
||||
.enum(["lower", "higher"] as const)
|
||||
.describe("Whether lower or higher values are better. Defaults to lower.")
|
||||
.optional(),
|
||||
secondary_metrics: z
|
||||
.array(z.string())
|
||||
.describe("Names of secondary metrics tracked alongside the primary metric.")
|
||||
.optional(),
|
||||
scope_paths: z
|
||||
.array(z.string())
|
||||
.describe(
|
||||
"Files or directories the agent expects to modify. Used post-hoc to flag scope deviations on log_experiment; never used to block edits.",
|
||||
)
|
||||
.optional(),
|
||||
off_limits: z
|
||||
.array(z.string())
|
||||
.describe(
|
||||
"Paths the agent SHOULD NOT modify. Used post-hoc to flag scope deviations on log_experiment; never used to block edits.",
|
||||
)
|
||||
.optional(),
|
||||
constraints: z.array(z.string()).describe("Free-form constraints (e.g. 'no api break').").optional(),
|
||||
max_iterations: z.number().describe("Soft cap on iterations per segment. Optional.").optional(),
|
||||
new_segment: z
|
||||
.boolean()
|
||||
.describe(
|
||||
"When true, bump to a new segment even when an active session exists. New baselines and best-metric reset.",
|
||||
)
|
||||
.describe("better direction (default lower)")
|
||||
.optional(),
|
||||
secondary_metrics: z.array(z.string()).describe("secondary metric names").optional(),
|
||||
scope_paths: z.array(z.string()).describe("expected-to-modify paths").optional(),
|
||||
off_limits: z.array(z.string()).describe("off-limits paths").optional(),
|
||||
constraints: z.array(z.string()).describe("free-form constraints").optional(),
|
||||
max_iterations: z.number().describe("soft iteration cap per segment").optional(),
|
||||
new_segment: z.boolean().describe("bump to a new segment in existing session").optional(),
|
||||
});
|
||||
|
||||
interface InitExperimentDetails {
|
||||
|
||||
@@ -37,35 +37,21 @@ import type {
|
||||
const EXPERIMENT_TOOL_NAMES = ["init_experiment", "run_experiment", "log_experiment", "update_notes"];
|
||||
|
||||
const logExperimentSchema = z.object({
|
||||
metric: z
|
||||
.number()
|
||||
.describe("Primary metric value for this run. May differ from the parsed value; deviation is recorded."),
|
||||
status: z.enum(["keep", "discard", "crash", "checks_failed"] as const).describe("Outcome for this run."),
|
||||
description: z.string().describe("Short description of the experiment."),
|
||||
metrics: z.record(z.string(), z.number()).describe("Secondary metrics for this run.").optional(),
|
||||
asi: z
|
||||
.object({})
|
||||
.passthrough()
|
||||
.describe("Free-form structured metadata captured for this run (hypothesis, learnings, etc.).")
|
||||
.optional(),
|
||||
commit: z
|
||||
.string()
|
||||
.describe("Override the commit hash recorded for this run. Defaults to the current HEAD.")
|
||||
.optional(),
|
||||
justification: z
|
||||
.string()
|
||||
.describe(
|
||||
"Required when the run modifies paths outside scope or inside off-limits and you still want it kept. Free-form explanation.",
|
||||
)
|
||||
.optional(),
|
||||
metric: z.number().describe("primary metric value"),
|
||||
status: z.enum(["keep", "discard", "crash", "checks_failed"] as const).describe("run outcome"),
|
||||
description: z.string().describe("short run description"),
|
||||
metrics: z.record(z.string(), z.number()).describe("secondary metrics").optional(),
|
||||
asi: z.object({}).passthrough().describe("free-form structured metadata").optional(),
|
||||
commit: z.string().describe("override recorded commit hash").optional(),
|
||||
justification: z.string().describe("required when keeping a scope-deviating run").optional(),
|
||||
flag_runs: z
|
||||
.array(
|
||||
z.object({
|
||||
run_id: z.number().describe("Run id (#) of a previously logged run to flag as suspect."),
|
||||
reason: z.string().describe("Why this earlier run is suspect (e.g. reward-hacked, broken metric)."),
|
||||
run_id: z.number().describe("run id to flag"),
|
||||
reason: z.string().describe("why this run is suspect"),
|
||||
}),
|
||||
)
|
||||
.describe("Mark earlier runs as flagged. Flagged runs are excluded from baseline and best-metric math.")
|
||||
.describe("flag earlier runs as suspect")
|
||||
.optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import type { AutoresearchToolFactoryOptions, RunDetails, RunExperimentProgressD
|
||||
import { DEFAULT_HARNESS_COMMAND } from "./init-experiment";
|
||||
|
||||
const runExperimentSchema = z.object({
|
||||
timeout_seconds: z.number().describe("Timeout in seconds. Defaults to 600.").optional(),
|
||||
timeout_seconds: z.number().describe("timeout in seconds (default 600)").optional(),
|
||||
});
|
||||
|
||||
interface ProcessExecutionResult {
|
||||
|
||||
@@ -9,15 +9,8 @@ import { openAutoresearchStorageIfExists } from "../storage";
|
||||
import type { AutoresearchToolFactoryOptions } from "../types";
|
||||
|
||||
const updateNotesSchema = z.object({
|
||||
body: z
|
||||
.string()
|
||||
.describe("Replacement markdown body for the active autoresearch session's notes (your durable playbook)."),
|
||||
append_idea: z
|
||||
.string()
|
||||
.describe(
|
||||
"When set, append this string as a new bullet under an Ideas section instead of replacing the body. `body` is ignored.",
|
||||
)
|
||||
.optional(),
|
||||
body: z.string().describe("replacement notes body"),
|
||||
append_idea: z.string().describe("append as bullet under Ideas instead of replacing body").optional(),
|
||||
});
|
||||
|
||||
interface UpdateNotesDetails {
|
||||
|
||||
@@ -13,8 +13,8 @@ import type { ToolSession } from "../../../tools";
|
||||
import { getFilePriority } from "./git-file-diff";
|
||||
|
||||
const analyzeFileSchema = z.object({
|
||||
files: z.array(z.string().describe("File path")).min(1),
|
||||
goal: z.string().describe("Optional analysis focus").optional(),
|
||||
files: z.array(z.string().describe("file path")).min(1),
|
||||
goal: z.string().describe("analysis focus").optional(),
|
||||
});
|
||||
|
||||
const analyzeFileOutputSchema = {
|
||||
|
||||
@@ -132,8 +132,8 @@ function processDiffs(files: string[], diffs: Map<string, string>): { result: st
|
||||
}
|
||||
|
||||
const gitFileDiffSchema = z.object({
|
||||
files: z.array(z.string().describe("Files to diff")).min(1).max(10),
|
||||
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
||||
files: z.array(z.string().describe("file to diff")).min(1).max(10),
|
||||
staged: z.boolean().describe("use staged changes (default true)").optional(),
|
||||
});
|
||||
|
||||
export function createGitFileDiffTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitFileDiffSchema> {
|
||||
|
||||
@@ -4,9 +4,9 @@ import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
import * as git from "../../../utils/git";
|
||||
|
||||
const gitHunkSchema = z.object({
|
||||
file: z.string().describe("File path"),
|
||||
hunks: z.array(z.number().describe("1-based hunk indices")).min(1).optional(),
|
||||
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
||||
file: z.string().describe("file path"),
|
||||
hunks: z.array(z.number().describe("1-based hunk index")).min(1).optional(),
|
||||
staged: z.boolean().describe("use staged changes (default true)").optional(),
|
||||
});
|
||||
|
||||
function selectHunks(fileHunks: FileHunks, requested?: number[]): DiffHunk[] {
|
||||
|
||||
@@ -43,8 +43,8 @@ function filterExcludedFiles(files: string[]): { filtered: string[]; excluded: s
|
||||
}
|
||||
|
||||
const gitOverviewSchema = z.object({
|
||||
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
||||
include_untracked: z.boolean().describe("Include untracked files when staged=false").optional(),
|
||||
staged: z.boolean().describe("use staged changes (default true)").optional(),
|
||||
include_untracked: z.boolean().describe("include untracked when unstaged").optional(),
|
||||
});
|
||||
|
||||
export function createGitOverviewTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitOverviewSchema> {
|
||||
|
||||
@@ -12,9 +12,7 @@ const changelogEntryProperties = CHANGELOG_CATEGORIES.reduce<Record<ChangelogCat
|
||||
);
|
||||
|
||||
const changelogEntriesSchema = z.object(changelogEntryProperties);
|
||||
const changelogDeletionsSchema = z
|
||||
.object(changelogEntryProperties)
|
||||
.describe("Entries to remove from existing changelog sections (case-insensitive match)");
|
||||
const changelogDeletionsSchema = z.object(changelogEntryProperties).describe("entries to remove");
|
||||
|
||||
const changelogEntrySchema = z.object({
|
||||
path: z.string(),
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
import * as git from "../../../utils/git";
|
||||
|
||||
const recentCommitsSchema = z.object({
|
||||
count: z.number().min(1).max(50).describe("Number of commits to fetch").optional(),
|
||||
count: z.number().min(1).max(50).describe("commit count").optional(),
|
||||
});
|
||||
|
||||
interface RecentCommitStats {
|
||||
|
||||
@@ -17,15 +17,7 @@ export const commitTypeSchema = z.enum([
|
||||
export const detailSchema = z.object({
|
||||
text: z.string(),
|
||||
changelog_category: z
|
||||
.union([
|
||||
z.literal("Added"),
|
||||
z.literal("Changed"),
|
||||
z.literal("Fixed"),
|
||||
z.literal("Deprecated"),
|
||||
z.literal("Removed"),
|
||||
z.literal("Security"),
|
||||
z.literal("Breaking Changes"),
|
||||
])
|
||||
.enum(["Added", "Changed", "Fixed", "Deprecated", "Removed", "Security", "Breaking Changes"])
|
||||
.optional(),
|
||||
user_visible: z.boolean().optional(),
|
||||
});
|
||||
|
||||
@@ -14,11 +14,7 @@ import { ApplyPatchError } from "../diff";
|
||||
import type { PatchEditEntry } from "./patch";
|
||||
|
||||
export const applyPatchSchema = z.object({
|
||||
input: z
|
||||
.string()
|
||||
.describe(
|
||||
"Full Codex apply_patch envelope, including '*** Begin Patch' and '*** End Patch'. Contains any mix of Add/Delete/Update (with optional Move to) file operations.",
|
||||
),
|
||||
input: z.string().describe("apply_patch envelope"),
|
||||
});
|
||||
|
||||
export type ApplyPatchParams = z.infer<typeof applyPatchSchema>;
|
||||
|
||||
@@ -1578,16 +1578,16 @@ export async function computePatchDiff(
|
||||
|
||||
export const patchEditEntrySchema = z
|
||||
.object({
|
||||
op: z.enum(["create", "delete", "update"]).optional().describe("Operation (default: update)"),
|
||||
rename: z.string().describe("New path for move").optional(),
|
||||
diff: z.string().describe("Diff hunks (update) or full content (create)").optional(),
|
||||
op: z.enum(["create", "delete", "update"]).optional().describe("operation (default update)"),
|
||||
rename: z.string().describe("new path for move").optional(),
|
||||
diff: z.string().describe("diff hunks or full content for create").optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const patchEditSchema = z
|
||||
.object({
|
||||
path: z.string().describe("file path for edits"),
|
||||
edits: z.array(patchEditEntrySchema).min(1).describe("Patch operations"),
|
||||
path: z.string().describe("file path"),
|
||||
edits: z.array(patchEditEntrySchema).min(1).describe("patch operations"),
|
||||
})
|
||||
.strict();
|
||||
|
||||
|
||||
@@ -978,16 +978,16 @@ export function findContextLine(
|
||||
|
||||
export const replaceEditEntrySchema = z
|
||||
.object({
|
||||
old_text: z.string().describe("Text to find (fuzzy whitespace matching enabled)"),
|
||||
new_text: z.string().describe("Replacement text"),
|
||||
all: z.boolean().describe("Replace all occurrences (default: unique match required)").optional(),
|
||||
old_text: z.string().describe("text to find"),
|
||||
new_text: z.string().describe("replacement text"),
|
||||
all: z.boolean().describe("replace all occurrences").optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const replaceEditSchema = z
|
||||
.object({
|
||||
path: z.string().describe("file path for edits"),
|
||||
edits: z.array(replaceEditEntrySchema).min(1).describe("Replacements"),
|
||||
path: z.string().describe("file path"),
|
||||
edits: z.array(replaceEditEntrySchema).min(1).describe("replacements"),
|
||||
})
|
||||
.strict();
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ const researcherStartTool = createExaTool(
|
||||
"Start Deep Research",
|
||||
"Start an asynchronous deep research task using Exa's researcher. Returns a task_id for polling completion.",
|
||||
z.object({
|
||||
query: z.string().describe("Research query to investigate"),
|
||||
depth: z.number().int().min(1).max(5).describe("Research depth (1-5, default: 3)").optional(),
|
||||
breadth: z.number().int().min(1).max(5).describe("Research breadth (1-5, default: 3)").optional(),
|
||||
query: z.string().describe("research query"),
|
||||
depth: z.number().int().min(1).max(5).describe("research depth (1-5)").optional(),
|
||||
breadth: z.number().int().min(1).max(5).describe("research breadth (1-5)").optional(),
|
||||
}),
|
||||
"deep_researcher_start",
|
||||
{ formatResponse: false },
|
||||
@@ -27,7 +27,7 @@ const researcherPollTool = createExaTool(
|
||||
"Poll Research Status",
|
||||
"Poll the status of an asynchronous research task. Returns status (pending|running|completed|failed) and result if completed.",
|
||||
z.object({
|
||||
task_id: z.string().describe("Task ID returned from exa_researcher_start"),
|
||||
task_id: z.string().describe("task id"),
|
||||
}),
|
||||
"deep_researcher_check",
|
||||
{ formatResponse: false },
|
||||
|
||||
@@ -30,28 +30,16 @@ Parameters:
|
||||
- num_results: Maximum number of results to return (default: 10, max: 100)`,
|
||||
|
||||
z.object({
|
||||
query: z.string().describe("Search query"),
|
||||
type: z
|
||||
.enum(["keyword", "neural", "auto"])
|
||||
.describe("Search type - neural (semantic), keyword (exact), or auto")
|
||||
.optional(),
|
||||
include_domains: z.array(z.string()).describe("Only include results from these domains").optional(),
|
||||
exclude_domains: z.array(z.string()).describe("Exclude results from these domains").optional(),
|
||||
start_published_date: z
|
||||
.string()
|
||||
.describe("Filter results published after this date (ISO 8601 format)")
|
||||
.optional(),
|
||||
end_published_date: z.string().describe("Filter results published before this date (ISO 8601 format)").optional(),
|
||||
use_autoprompt: z.boolean().describe("Let Exa optimize your query automatically (default: true)").optional(),
|
||||
text: z.boolean().describe("Include page text content in results (costs more, default: false)").optional(),
|
||||
highlights: z.boolean().describe("Include highlighted relevant snippets (default: false)").optional(),
|
||||
num_results: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.describe("Maximum number of results to return (default: 10, max: 100)")
|
||||
.optional(),
|
||||
query: z.string().describe("search query"),
|
||||
type: z.enum(["keyword", "neural", "auto"]).describe("search type").optional(),
|
||||
include_domains: z.array(z.string()).describe("include domains").optional(),
|
||||
exclude_domains: z.array(z.string()).describe("exclude domains").optional(),
|
||||
start_published_date: z.string().describe("published after (iso 8601)").optional(),
|
||||
end_published_date: z.string().describe("published before (iso 8601)").optional(),
|
||||
use_autoprompt: z.boolean().describe("autoprompt").optional(),
|
||||
text: z.boolean().describe("include page text").optional(),
|
||||
highlights: z.boolean().describe("include highlights").optional(),
|
||||
num_results: z.number().int().min(1).max(100).describe("max results (1-100)").optional(),
|
||||
}),
|
||||
"web_search_exa",
|
||||
);
|
||||
|
||||
@@ -53,8 +53,8 @@ const websetCreateTool = createWebsetTool(
|
||||
"Create Webset",
|
||||
"Create a new webset collection for organizing web content.",
|
||||
z.object({
|
||||
name: z.string().describe("Name of the webset"),
|
||||
description: z.string().describe("Optional description").optional(),
|
||||
name: z.string().describe("webset name"),
|
||||
description: z.string().describe("description").optional(),
|
||||
}),
|
||||
"create_webset",
|
||||
);
|
||||
@@ -72,7 +72,7 @@ const websetGetTool = createWebsetTool(
|
||||
"Get Webset",
|
||||
"Get details of a specific webset by ID.",
|
||||
z.object({
|
||||
id: z.string().describe("Webset ID"),
|
||||
id: z.string().describe("webset id"),
|
||||
}),
|
||||
"get_webset",
|
||||
);
|
||||
@@ -82,9 +82,9 @@ const websetUpdateTool = createWebsetTool(
|
||||
"Update Webset",
|
||||
"Update a webset's name or description.",
|
||||
z.object({
|
||||
id: z.string().describe("Webset ID"),
|
||||
name: z.string().describe("New name").optional(),
|
||||
description: z.string().describe("New description").optional(),
|
||||
id: z.string().describe("webset id"),
|
||||
name: z.string().describe("new name").optional(),
|
||||
description: z.string().describe("new description").optional(),
|
||||
}),
|
||||
"update_webset",
|
||||
);
|
||||
@@ -94,7 +94,7 @@ const websetDeleteTool = createWebsetTool(
|
||||
"Delete Webset",
|
||||
"Delete a webset and all its contents.",
|
||||
z.object({
|
||||
id: z.string().describe("Webset ID"),
|
||||
id: z.string().describe("webset id"),
|
||||
}),
|
||||
"delete_webset",
|
||||
);
|
||||
@@ -105,9 +105,9 @@ const websetItemsListTool = createWebsetTool(
|
||||
"List Webset Items",
|
||||
"List items in a webset with optional pagination.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
limit: z.number().describe("Number of items to return").optional(),
|
||||
offset: z.number().describe("Pagination offset").optional(),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
limit: z.number().describe("max items").optional(),
|
||||
offset: z.number().describe("offset").optional(),
|
||||
}),
|
||||
"list_webset_items",
|
||||
);
|
||||
@@ -117,8 +117,8 @@ const websetItemGetTool = createWebsetTool(
|
||||
"Get Webset Item",
|
||||
"Get a specific item from a webset.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
item_id: z.string().describe("Item ID"),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
item_id: z.string().describe("item id"),
|
||||
}),
|
||||
"get_item",
|
||||
);
|
||||
@@ -129,8 +129,8 @@ const websetSearchCreateTool = createWebsetTool(
|
||||
"Create Webset Search",
|
||||
"Create a new search within a webset.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
query: z.string().describe("Search query"),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
query: z.string().describe("search query"),
|
||||
}),
|
||||
"create_search",
|
||||
);
|
||||
@@ -140,8 +140,8 @@ const websetSearchGetTool = createWebsetTool(
|
||||
"Get Webset Search",
|
||||
"Get the status and results of a webset search.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
search_id: z.string().describe("Search ID"),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
search_id: z.string().describe("search id"),
|
||||
}),
|
||||
"get_search",
|
||||
);
|
||||
@@ -151,8 +151,8 @@ const websetSearchCancelTool = createWebsetTool(
|
||||
"Cancel Webset Search",
|
||||
"Cancel a running webset search.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
search_id: z.string().describe("Search ID"),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
search_id: z.string().describe("search id"),
|
||||
}),
|
||||
"cancel_search",
|
||||
);
|
||||
@@ -163,9 +163,9 @@ const websetEnrichmentCreateTool = createWebsetTool(
|
||||
"Create Enrichment",
|
||||
"Create a new enrichment task for a webset.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
name: z.string().describe("Enrichment name"),
|
||||
prompt: z.string().describe("Enrichment prompt"),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
name: z.string().describe("enrichment name"),
|
||||
prompt: z.string().describe("enrichment prompt"),
|
||||
}),
|
||||
"create_enrichment",
|
||||
);
|
||||
@@ -175,8 +175,8 @@ const websetEnrichmentGetTool = createWebsetTool(
|
||||
"Get Enrichment",
|
||||
"Get the status and results of an enrichment task.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
enrichment_id: z.string().describe("Enrichment ID"),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
enrichment_id: z.string().describe("enrichment id"),
|
||||
}),
|
||||
"get_enrichment",
|
||||
);
|
||||
@@ -186,10 +186,10 @@ const websetEnrichmentUpdateTool = createWebsetTool(
|
||||
"Update Enrichment",
|
||||
"Update an enrichment's name or prompt.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
enrichment_id: z.string().describe("Enrichment ID"),
|
||||
name: z.string().describe("New name").optional(),
|
||||
prompt: z.string().describe("New prompt").optional(),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
enrichment_id: z.string().describe("enrichment id"),
|
||||
name: z.string().describe("new name").optional(),
|
||||
prompt: z.string().describe("new prompt").optional(),
|
||||
}),
|
||||
"update_enrichment",
|
||||
);
|
||||
@@ -199,8 +199,8 @@ const websetEnrichmentDeleteTool = createWebsetTool(
|
||||
"Delete Enrichment",
|
||||
"Delete an enrichment task.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
enrichment_id: z.string().describe("Enrichment ID"),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
enrichment_id: z.string().describe("enrichment id"),
|
||||
}),
|
||||
"delete_enrichment",
|
||||
);
|
||||
@@ -210,8 +210,8 @@ const websetEnrichmentCancelTool = createWebsetTool(
|
||||
"Cancel Enrichment",
|
||||
"Cancel a running enrichment task.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
enrichment_id: z.string().describe("Enrichment ID"),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
enrichment_id: z.string().describe("enrichment id"),
|
||||
}),
|
||||
"cancel_enrichment",
|
||||
);
|
||||
@@ -222,8 +222,8 @@ const websetMonitorCreateTool = createWebsetTool(
|
||||
"Create Monitor",
|
||||
"Create a monitoring task for a webset with optional webhook notifications.",
|
||||
z.object({
|
||||
webset_id: z.string().describe("Webset ID"),
|
||||
webhook_url: z.string().describe("Webhook URL for notifications").optional(),
|
||||
webset_id: z.string().describe("webset id"),
|
||||
webhook_url: z.string().describe("webhook url").optional(),
|
||||
}),
|
||||
"create_monitor",
|
||||
);
|
||||
|
||||
@@ -15,9 +15,9 @@ import { completionBudgetReport, remainingTokens } from "../runtime";
|
||||
import type { Goal, GoalStatus, GoalToolDetails } from "../state";
|
||||
|
||||
const goalSchema = z.object({
|
||||
op: z.union([z.literal("create"), z.literal("get"), z.literal("complete")]).describe("Goal operation."),
|
||||
objective: z.string().describe("Goal objective. Required when op=create.").optional(),
|
||||
token_budget: z.number().int().describe("Optional positive token budget. Only honored when op=create.").optional(),
|
||||
op: z.enum(["create", "get", "complete"]).describe("goal operation"),
|
||||
objective: z.string().describe("goal objective").optional(),
|
||||
token_budget: z.number().int().describe("token budget").optional(),
|
||||
});
|
||||
|
||||
export type GoalToolInput = z.infer<typeof goalSchema>;
|
||||
|
||||
@@ -2,9 +2,6 @@ import { HookEditorComponent, HookInputComponent, HookSelectorComponent } from "
|
||||
|
||||
// Core session management
|
||||
|
||||
// TypeBox helper for string enums (convenience for custom tools)
|
||||
// Re-export from pi-ai which uses the correct enum-based schema format
|
||||
export { StringEnum } from "@oh-my-pi/pi-ai";
|
||||
// Re-export TUI components for custom tool rendering
|
||||
export { Container, Markdown, Spacer, Text } from "@oh-my-pi/pi-tui";
|
||||
// Logging
|
||||
|
||||
@@ -22,17 +22,14 @@ export const lspSchema = z.object({
|
||||
"capabilities",
|
||||
"request",
|
||||
]),
|
||||
file: z.string().describe("File path or source path for rename_file").optional(),
|
||||
line: z.number().describe("Line number (1-indexed)").optional(),
|
||||
symbol: z.string().describe("Symbol/substring to locate on the line").optional(),
|
||||
query: z.string().describe("Search query, code-action selector, or LSP method name for action=request").optional(),
|
||||
new_name: z.string().describe("New name for rename, or destination path for rename_file").optional(),
|
||||
apply: z.boolean().describe("Apply edits (default: true for rename/rename_file)").optional(),
|
||||
timeout: z.number().describe("Request timeout in seconds").optional(),
|
||||
payload: z
|
||||
.string()
|
||||
.describe("JSON-encoded params for action=request. When omitted, params are auto-built from file/line/symbol.")
|
||||
.optional(),
|
||||
file: z.string().describe("file path or source path for rename_file").optional(),
|
||||
line: z.number().describe("line number (1-indexed)").optional(),
|
||||
symbol: z.string().describe("symbol substring on the line").optional(),
|
||||
query: z.string().describe("search query or code-action selector").optional(),
|
||||
new_name: z.string().describe("new symbol name or destination path").optional(),
|
||||
apply: z.boolean().describe("apply edits").optional(),
|
||||
timeout: z.number().describe("request timeout in seconds").optional(),
|
||||
payload: z.string().describe("json-encoded request params").optional(),
|
||||
});
|
||||
|
||||
export type LspParams = z.infer<typeof lspSchema>;
|
||||
|
||||
@@ -2,7 +2,7 @@ Resolves a pending action by either applying or discarding it.
|
||||
- `action` is required:
|
||||
- `"apply"` persists / submits the pending action.
|
||||
- `"discard"` rejects the pending action.
|
||||
- `reason` is required and must briefly explain why you chose to apply or discard.
|
||||
- `reason` is required: one short complete sentence explaining why, starting with a capital letter and ending with a period.
|
||||
- `extra` (optional) is free-form metadata passed to the resolving tool. Schema depends on context:
|
||||
|
||||
Valid whenever a pending action exists — either a preview-style staging (e.g. `ast_edit`) or a long-lived approval gate.
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { Database, type Statement } from "bun:sqlite";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { type AuthCredential, AuthCredentialStore, type StoredAuthCredential } from "@oh-my-pi/pi-ai";
|
||||
import {
|
||||
type AuthCredential,
|
||||
type AuthCredentialStore,
|
||||
SqliteAuthCredentialStore,
|
||||
type StoredAuthCredential,
|
||||
} from "@oh-my-pi/pi-ai";
|
||||
import { getAgentDbPath, isRecord, logger } from "@oh-my-pi/pi-utils";
|
||||
import type { RawSettings as Settings } from "../config/settings";
|
||||
|
||||
@@ -57,7 +62,7 @@ export class AgentStorage {
|
||||
this.#hardenPermissions(dbPath);
|
||||
|
||||
// Create AuthCredentialStore with our open database
|
||||
this.#authStore = new AuthCredentialStore(this.#db);
|
||||
this.#authStore = new SqliteAuthCredentialStore(this.#db);
|
||||
|
||||
this.#listSettingsStmt = this.#db.prepare("SELECT key, value FROM settings");
|
||||
this.#upsertModelUsageStmt = this.#db.prepare(
|
||||
|
||||
@@ -14,4 +14,4 @@ export type {
|
||||
SerializedAuthStorage,
|
||||
StoredAuthCredential,
|
||||
} from "@oh-my-pi/pi-ai";
|
||||
export { AuthStorage } from "@oh-my-pi/pi-ai";
|
||||
export { AuthStorage, SqliteAuthCredentialStore } from "@oh-my-pi/pi-ai";
|
||||
|
||||
@@ -57,18 +57,13 @@ export interface SubagentLifecyclePayload {
|
||||
index: number;
|
||||
}
|
||||
|
||||
const assignmentDescriptionForContextEnabled =
|
||||
"Complete per-task instructions the subagent executes. Must follow the Target/Change/Edge Cases/Acceptance structure. Only include per-task deltas — shared background belongs in `context`.";
|
||||
const assignmentDescriptionForContextDisabled =
|
||||
"Complete per-task instructions the subagent executes. Must follow the Target/Change/Edge Cases/Acceptance structure, and include any background that would otherwise live in `context` since shared context is disabled in this mode.";
|
||||
const assignmentDescription = "per-task instructions; self-contained";
|
||||
|
||||
const createTaskItemSchema = (contextEnabled: boolean) =>
|
||||
const createTaskItemSchema = (_contextEnabled: boolean) =>
|
||||
z.object({
|
||||
id: z.string().max(48).describe("CamelCase identifier, max 48 chars"),
|
||||
description: z.string().describe("Short one-liner for UI display only — not seen by the subagent"),
|
||||
assignment: z
|
||||
.string()
|
||||
.describe(contextEnabled ? assignmentDescriptionForContextEnabled : assignmentDescriptionForContextDisabled),
|
||||
id: z.string().max(48).describe("camelcase identifier"),
|
||||
description: z.string().describe("ui label, not seen by subagent"),
|
||||
assignment: z.string().describe(assignmentDescription),
|
||||
});
|
||||
|
||||
/** Single task item for parallel execution (default shape with context enabled). */
|
||||
@@ -80,44 +75,24 @@ const createTaskSchema = (options: { isolationEnabled: boolean; simpleMode: Task
|
||||
const itemSchema = createTaskItemSchema(contextEnabled);
|
||||
|
||||
let schema = z.object({
|
||||
agent: z.string().describe("Agent type for all tasks in this batch"),
|
||||
tasks: z
|
||||
.array(itemSchema)
|
||||
.describe(
|
||||
contextEnabled
|
||||
? "Tasks to execute in parallel. Each must be small-scoped (3-5 files max) and self-contained given context + assignment."
|
||||
: "Tasks to execute in parallel. Each must be small-scoped (3-5 files max) and fully self-contained inside assignment because shared context is disabled.",
|
||||
),
|
||||
agent: z.string().describe("agent type"),
|
||||
tasks: z.array(itemSchema).describe("tasks to execute in parallel"),
|
||||
});
|
||||
|
||||
if (contextEnabled) {
|
||||
schema = schema.extend({
|
||||
context: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"Shared background prepended to every task's assignment. Put goal, non-goals, constraints, conventions, reference paths, API contracts, and global acceptance commands here once — instead of duplicating across assignments.",
|
||||
),
|
||||
context: z.string().optional().describe("shared background prepended to each assignment"),
|
||||
});
|
||||
}
|
||||
|
||||
if (customSchemaEnabled) {
|
||||
schema = schema.extend({
|
||||
schema: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"JSON-encoded JTD schema defining expected response structure. Output format belongs here — never in context or assignment.",
|
||||
),
|
||||
schema: z.string().optional().describe("jtd schema for expected response shape"),
|
||||
});
|
||||
}
|
||||
|
||||
if (options.isolationEnabled) {
|
||||
schema = schema.extend({
|
||||
isolated: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe("Run in isolated environment; returns patches. Use when tasks edit overlapping files."),
|
||||
isolated: z.boolean().optional().describe("run in isolated env; returns patches"),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,19 +18,16 @@ export type { Observation, ObservationEntry } from "./browser/tab-protocol";
|
||||
const DEFAULT_TAB_NAME = "main";
|
||||
|
||||
const appSchema = z.object({
|
||||
path: z.string().describe("absolute path to a binary to spawn (single-instance reuse)").optional(),
|
||||
cdp_url: z.string().describe("existing CDP endpoint to connect to (e.g. http://127.0.0.1:9222)").optional(),
|
||||
args: z.array(z.string()).describe("extra CLI args when spawning").optional(),
|
||||
target: z.string().describe("substring matched against url+title to pick a BrowserWindow").optional(),
|
||||
path: z.string().describe("binary path to spawn").optional(),
|
||||
cdp_url: z.string().describe("existing cdp endpoint").optional(),
|
||||
args: z.array(z.string()).describe("extra cli args").optional(),
|
||||
target: z.string().describe("substring to pick a window").optional(),
|
||||
});
|
||||
|
||||
const browserSchema = z.object({
|
||||
action: z.enum(["open", "close", "run"] as const).describe("tab/browser operation"),
|
||||
name: z
|
||||
.string()
|
||||
.describe("tab id; default 'main'. Multiple tabs can coexist; reusable across run() calls and subagents.")
|
||||
.optional(),
|
||||
url: z.string().describe("open: navigate after acquiring tab").optional(),
|
||||
action: z.enum(["open", "close", "run"] as const).describe("operation"),
|
||||
name: z.string().describe("tab id (default 'main')").optional(),
|
||||
url: z.string().describe("url to open").optional(),
|
||||
app: appSchema.optional(),
|
||||
viewport: z
|
||||
.object({
|
||||
@@ -41,21 +38,16 @@ const browserSchema = z.object({
|
||||
.optional(),
|
||||
wait_until: z
|
||||
.enum(["load", "domcontentloaded", "networkidle0", "networkidle2"] as const)
|
||||
.describe("navigation wait condition for url")
|
||||
.describe("navigation wait condition")
|
||||
.optional(),
|
||||
dialogs: z
|
||||
.enum(["accept", "dismiss"] as const)
|
||||
.describe("open: auto-handle alert/confirm/beforeunload dialogs (default: leave for caller to handle)")
|
||||
.optional(),
|
||||
code: z
|
||||
.string()
|
||||
.describe(
|
||||
"run: JS body executed with `page`, `browser`, `tab`, `display`, `assert`, `wait` in scope. Treated as the body of an async function. Use `display(value)` to attach text/JSON/images; the function's return value is JSON-serialized as a final block.",
|
||||
)
|
||||
.describe("auto-handle dialogs")
|
||||
.optional(),
|
||||
code: z.string().describe("js body to run in tab").optional(),
|
||||
timeout: z.number().default(30).describe("timeout in seconds").optional(),
|
||||
all: z.boolean().describe("close: close every tab").optional(),
|
||||
kill: z.boolean().describe("close: also kill spawned-app browsers (default: leave running)").optional(),
|
||||
all: z.boolean().describe("close every tab").optional(),
|
||||
kill: z.boolean().describe("also kill spawned-app browsers").optional(),
|
||||
});
|
||||
|
||||
/** Input schema for the browser tool. */
|
||||
|
||||
@@ -213,58 +213,34 @@ const githubSchema = z
|
||||
"run_watch",
|
||||
] as const)
|
||||
.describe("github operation"),
|
||||
repo: z.string().describe("owner/repo (any op)").optional(),
|
||||
branch: z.string().describe("branch (repo_view, pr_push local branch, run_watch)").optional(),
|
||||
repo: z.string().describe("owner/repo").optional(),
|
||||
branch: z.string().describe("branch").optional(),
|
||||
pr: z
|
||||
.union([z.string(), z.array(z.string())])
|
||||
.describe(
|
||||
"pr number, url, or branch (pr_checkout); pass an array to batch-process multiple pull requests in one call",
|
||||
)
|
||||
.optional(),
|
||||
force: z.boolean().describe("reset existing local branch (pr_checkout)").optional(),
|
||||
forceWithLease: z.boolean().describe("force-with-lease push (pr_push)").optional(),
|
||||
title: z.string().describe("PR title (pr_create)").optional(),
|
||||
body: z.string().describe("PR body markdown (pr_create); mutually exclusive with fill").optional(),
|
||||
base: z.string().describe("PR base branch (pr_create); defaults to repo default branch").optional(),
|
||||
head: z.string().describe("PR head branch (pr_create); defaults to current branch").optional(),
|
||||
draft: z.boolean().describe("open PR as draft (pr_create)").optional(),
|
||||
fill: z
|
||||
.boolean()
|
||||
.describe("auto-fill PR title/body from commits (pr_create); mutually exclusive with title/body")
|
||||
.optional(),
|
||||
reviewer: z.array(z.string()).describe("reviewers to request (pr_create); accepts users or org/team").optional(),
|
||||
assignee: z.array(z.string()).describe("assignees (pr_create); use @me for the authenticated user").optional(),
|
||||
label: z.array(z.string()).describe("labels to apply (pr_create)").optional(),
|
||||
query: z
|
||||
.string()
|
||||
.describe("search query (search_issues, search_prs, search_code, search_commits, search_repos)")
|
||||
.optional(),
|
||||
since: z
|
||||
.string()
|
||||
.describe(
|
||||
"lower-bound date for search_issues/search_prs/search_commits/search_repos. Accepts a relative duration (`<n><unit>` with unit `m`/`h`/`d`/`w`/`mo`/`y`, e.g. `3d`, `12h`, `2w`) or an ISO date (`YYYY-MM-DD`) / datetime. Translated to a `created:>=…` (or `committer-date:`/`pushed:`) qualifier; not supported by search_code.",
|
||||
)
|
||||
.optional(),
|
||||
until: z
|
||||
.string()
|
||||
.describe(
|
||||
"upper-bound date in the same format as `since`. With both, builds a `field:since..until` range qualifier.",
|
||||
)
|
||||
.describe("pr number, url, or branch")
|
||||
.optional(),
|
||||
force: z.boolean().describe("reset existing local branch").optional(),
|
||||
forceWithLease: z.boolean().describe("force-with-lease push").optional(),
|
||||
title: z.string().describe("pr title").optional(),
|
||||
body: z.string().describe("pr body markdown").optional(),
|
||||
base: z.string().describe("pr base branch").optional(),
|
||||
head: z.string().describe("pr head branch").optional(),
|
||||
draft: z.boolean().describe("open pr as draft").optional(),
|
||||
fill: z.boolean().describe("auto-fill pr title/body from commits").optional(),
|
||||
reviewer: z.array(z.string()).describe("reviewers").optional(),
|
||||
assignee: z.array(z.string()).describe("assignees").optional(),
|
||||
label: z.array(z.string()).describe("labels").optional(),
|
||||
query: z.string().describe("search query").optional(),
|
||||
since: z.string().describe("lower-bound date filter").optional(),
|
||||
until: z.string().describe("upper-bound date filter").optional(),
|
||||
dateField: z
|
||||
.enum(["created", "updated"] as const)
|
||||
.describe(
|
||||
"date field used by `since`/`until`. issues/prs: `created` (default) or `updated`. repos: `created` (default) or `updated` (mapped to GitHub's `pushed:`). commits: ignored — always uses `committer-date`.",
|
||||
)
|
||||
.describe("date field")
|
||||
.default("created")
|
||||
.optional(),
|
||||
limit: z
|
||||
.number()
|
||||
.default(10)
|
||||
.describe("max results (search_issues, search_prs, search_code, search_commits, search_repos)")
|
||||
.optional(),
|
||||
run: z.string().describe("actions run id or url (run_watch)").optional(),
|
||||
tail: z.number().default(15).describe("log lines per failed job (run_watch)").optional(),
|
||||
limit: z.number().default(10).describe("max results").optional(),
|
||||
run: z.string().describe("actions run id or url").optional(),
|
||||
tail: z.number().default(15).describe("log lines per failed job").optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import recallDescription from "../prompts/tools/recall.md" with { type: "text" }
|
||||
import type { ToolSession } from ".";
|
||||
|
||||
const hindsightRecallSchema = z.object({
|
||||
query: z.string().describe("Natural language search query. Be specific about what you need to know."),
|
||||
query: z.string().describe("natural language search query"),
|
||||
});
|
||||
|
||||
export type HindsightRecallParams = z.infer<typeof hindsightRecallSchema>;
|
||||
|
||||
@@ -6,8 +6,8 @@ import reflectDescription from "../prompts/tools/reflect.md" with { type: "text"
|
||||
import type { ToolSession } from ".";
|
||||
|
||||
const hindsightReflectSchema = z.object({
|
||||
query: z.string().describe("The question to answer using long-term memory."),
|
||||
context: z.string().describe("Optional additional context to guide the reflection.").optional(),
|
||||
query: z.string().describe("question to answer"),
|
||||
context: z.string().describe("optional context").optional(),
|
||||
});
|
||||
|
||||
export type HindsightReflectParams = z.infer<typeof hindsightReflectSchema>;
|
||||
|
||||
@@ -7,16 +7,12 @@ const hindsightRetainSchema = z.object({
|
||||
items: z
|
||||
.array(
|
||||
z.object({
|
||||
content: z
|
||||
.string()
|
||||
.describe("The information to remember. Be specific and self-contained — include who, what, when, why."),
|
||||
context: z.string().describe("Optional context describing where this information came from.").optional(),
|
||||
content: z.string().describe("information to remember"),
|
||||
context: z.string().describe("source context").optional(),
|
||||
}),
|
||||
)
|
||||
.min(1)
|
||||
.describe(
|
||||
"One or more memories to retain. Batch related facts in a single call rather than calling retain repeatedly — they are deduplicated and consolidated together.",
|
||||
),
|
||||
.describe("memories to retain"),
|
||||
});
|
||||
|
||||
export type HindsightRetainParams = z.infer<typeof hindsightRetainSchema>;
|
||||
|
||||
@@ -26,18 +26,10 @@ import type { AgentRef, AgentRegistry } from "../registry/agent-registry";
|
||||
import type { ToolSession } from ".";
|
||||
|
||||
const ircSchema = z.object({
|
||||
op: z
|
||||
.union([
|
||||
z.literal("send").describe("Send a message to one peer or to all peers"),
|
||||
z.literal("list").describe("List currently visible peers"),
|
||||
])
|
||||
.describe("IRC operation"),
|
||||
to: z.string().optional().describe('Recipient agent id (e.g. "0-Main", "0-AuthLoader") or "all" to broadcast'),
|
||||
message: z.string().optional().describe("Message body to deliver"),
|
||||
awaitReply: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe("Wait for the recipient's prose reply (default: true for DM, false for broadcast)"),
|
||||
op: z.enum(["send", "list"]).describe("irc operation"),
|
||||
to: z.string().optional().describe('recipient agent id or "all"'),
|
||||
message: z.string().optional().describe("message body"),
|
||||
awaitReply: z.boolean().optional().describe("wait for prose reply"),
|
||||
});
|
||||
|
||||
type IrcParams = z.infer<typeof ircSchema>;
|
||||
|
||||
@@ -23,17 +23,9 @@ import {
|
||||
import { ToolError } from "./tool-errors";
|
||||
|
||||
const jobSchema = z.object({
|
||||
poll: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe("background job ids to wait for; omit (with no `cancel`) to wait on all running jobs"),
|
||||
cancel: z.array(z.string()).optional().describe("background job ids to cancel"),
|
||||
list: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe(
|
||||
"Return an immediate snapshot of every job spawned by this agent (running + completed within retention). Read-only \u2014 cannot be combined with `poll` or `cancel`.",
|
||||
),
|
||||
poll: z.array(z.string()).optional().describe("job ids to wait for"),
|
||||
cancel: z.array(z.string()).optional().describe("job ids to cancel"),
|
||||
list: z.boolean().optional().describe("snapshot all jobs"),
|
||||
});
|
||||
|
||||
type JobParams = z.infer<typeof jobSchema>;
|
||||
|
||||
@@ -12,14 +12,9 @@ import { replaceTabs } from "./render-utils";
|
||||
import { ToolError } from "./tool-errors";
|
||||
|
||||
const resolveSchema = z.object({
|
||||
action: z.union([z.literal("apply"), z.literal("discard")]),
|
||||
action: z.enum(["apply", "discard"]),
|
||||
reason: z.string().describe("reason for action"),
|
||||
extra: z
|
||||
.record(z.string(), z.unknown())
|
||||
.optional()
|
||||
.describe(
|
||||
'Free-form metadata interpreted by the resolving tool (e.g. plan-mode approval requires `{ title: "<PLAN_TITLE>" }`).',
|
||||
),
|
||||
extra: z.record(z.string(), z.unknown()).optional().describe("free-form metadata"),
|
||||
});
|
||||
|
||||
type ResolveParams = z.infer<typeof resolveSchema>;
|
||||
|
||||
@@ -49,31 +49,24 @@ const TodoOp = z
|
||||
.describe("operation to apply");
|
||||
|
||||
const InitListEntry = z.object({
|
||||
phase: z.string().describe("phase name (short noun phrase)"),
|
||||
items: z
|
||||
.array(z.string().describe("task content (5-10 words)"))
|
||||
.min(1)
|
||||
.describe("tasks for this phase, in execution order; all start as pending"),
|
||||
phase: z.string().describe("phase name"),
|
||||
items: z.array(z.string().describe("task content")).min(1).describe("tasks for this phase"),
|
||||
});
|
||||
|
||||
const TodoOpEntry = z.object({
|
||||
op: TodoOp,
|
||||
list: z.array(InitListEntry).optional().describe("phased task list for op=init"),
|
||||
task: z.string().optional().describe("task content for start/done/rm/drop/note"),
|
||||
phase: z.string().optional().describe("phase name for done/rm/drop/append"),
|
||||
items: z
|
||||
.array(z.string().describe("task content (5-10 words)"))
|
||||
.min(1)
|
||||
.optional()
|
||||
.describe("tasks to append to `phase` for op=append"),
|
||||
text: z.string().optional().describe("note text for op=note (appended with newline)"),
|
||||
list: z.array(InitListEntry).optional().describe("phased task list (init)"),
|
||||
task: z.string().optional().describe("task content"),
|
||||
phase: z.string().optional().describe("phase name"),
|
||||
items: z.array(z.string().describe("task content")).min(1).optional().describe("tasks to append"),
|
||||
text: z.string().optional().describe("note text"),
|
||||
});
|
||||
|
||||
const todoWriteSchema = z
|
||||
.object({
|
||||
ops: z.array(TodoOpEntry).min(1).describe("ordered todo operations"),
|
||||
})
|
||||
.describe("Apply ordered todo operations");
|
||||
.describe("apply ordered todo operations");
|
||||
|
||||
type TodoWriteParams = z.infer<typeof todoWriteSchema>;
|
||||
type TodoOpEntryValue = TodoWriteParams["ops"][number];
|
||||
|
||||
@@ -21,12 +21,12 @@ import { SearchProviderError } from "./types";
|
||||
|
||||
/** Web search tool parameters schema */
|
||||
export const webSearchSchema = z.object({
|
||||
query: z.string().describe("Search query"),
|
||||
recency: z.enum(["day", "week", "month", "year"]).describe("Recency filter (Brave, Perplexity)").optional(),
|
||||
limit: z.number().describe("Max results to return").optional(),
|
||||
max_tokens: z.number().describe("Maximum output tokens").optional(),
|
||||
temperature: z.number().describe("Sampling temperature").optional(),
|
||||
num_search_results: z.number().describe("Number of search results to retrieve").optional(),
|
||||
query: z.string().describe("search query"),
|
||||
recency: z.enum(["day", "week", "month", "year"]).describe("recency filter").optional(),
|
||||
limit: z.number().describe("max results").optional(),
|
||||
max_tokens: z.number().describe("max output tokens").optional(),
|
||||
temperature: z.number().describe("sampling temperature").optional(),
|
||||
num_search_results: z.number().describe("number of search results").optional(),
|
||||
});
|
||||
|
||||
export type SearchToolParams = z.infer<typeof webSearchSchema>;
|
||||
|
||||
Reference in New Issue
Block a user