@oh-my-pi/pi-coding-agent 15.0.1 → 15.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -1
- package/examples/custom-tools/README.md +11 -7
- package/examples/custom-tools/hello/index.ts +2 -2
- package/examples/extensions/README.md +19 -8
- package/examples/extensions/api-demo.ts +15 -19
- package/examples/extensions/hello.ts +5 -6
- package/examples/extensions/plan-mode.ts +1 -1
- package/examples/extensions/reload-runtime.ts +4 -3
- package/examples/extensions/with-deps/index.ts +4 -3
- package/examples/sdk/06-extensions.ts +4 -2
- package/package.json +8 -18
- package/src/autoresearch/tools/init-experiment.ts +38 -41
- package/src/autoresearch/tools/log-experiment.ts +32 -41
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +11 -11
- package/src/commands/commit.ts +10 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -4
- package/src/commit/agentic/tools/git-file-diff.ts +4 -4
- package/src/commit/agentic/tools/git-hunk.ts +5 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +13 -13
- package/src/commit/agentic/tools/propose-commit.ts +6 -6
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/schemas.ts +28 -28
- package/src/commit/agentic/tools/split-commit.ts +22 -21
- package/src/commit/analysis/summary.ts +4 -4
- package/src/commit/changelog/generate.ts +7 -11
- package/src/commit/shared-llm.ts +22 -34
- package/src/config/config-file.ts +35 -13
- package/src/config/model-registry.ts +40 -191
- package/src/config/models-config-schema.ts +166 -0
- package/src/config/settings-schema.ts +29 -0
- package/src/discovery/claude-plugins.ts +19 -7
- package/src/edit/index.ts +2 -2
- package/src/edit/modes/apply-patch.ts +7 -6
- package/src/edit/modes/patch.ts +18 -25
- package/src/edit/modes/replace.ts +18 -20
- package/src/eval/js/shared/rewrite-imports.ts +131 -10
- package/src/eval/py/executor.ts +233 -623
- package/src/eval/py/kernel.ts +27 -2
- package/src/eval/py/runner.py +42 -11
- package/src/eval/py/runtime.ts +1 -0
- package/src/exa/factory.ts +5 -4
- package/src/exa/mcp-client.ts +1 -1
- package/src/exa/researcher.ts +9 -20
- package/src/exa/search.ts +26 -52
- package/src/exa/types.ts +1 -1
- package/src/exa/websets.ts +54 -53
- package/src/exec/bash-executor.ts +2 -1
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +4 -2
- package/src/extensibility/custom-tools/loader.ts +5 -3
- package/src/extensibility/custom-tools/types.ts +7 -6
- package/src/extensibility/custom-tools/wrapper.ts +1 -1
- package/src/extensibility/extensions/get-commands-handler.ts +77 -0
- package/src/extensibility/extensions/loader.ts +7 -3
- package/src/extensibility/extensions/types.ts +9 -5
- package/src/extensibility/extensions/wrapper.ts +1 -2
- package/src/extensibility/hooks/loader.ts +3 -1
- package/src/extensibility/hooks/tool-wrapper.ts +1 -1
- package/src/extensibility/hooks/types.ts +4 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +78 -31
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/typebox.ts +391 -0
- package/src/goals/tools/goal-tool.ts +6 -12
- package/src/hashline/input.ts +2 -1
- package/src/hashline/parser.ts +27 -3
- package/src/hashline/types.ts +4 -4
- package/src/hindsight/state.ts +2 -2
- package/src/index.ts +0 -2
- package/src/internal-urls/docs-index.generated.ts +15 -15
- package/src/internal-urls/router.ts +8 -0
- package/src/internal-urls/types.ts +21 -0
- package/src/lsp/config.ts +15 -6
- package/src/lsp/defaults.json +6 -2
- package/src/lsp/types.ts +30 -38
- package/src/mcp/manager.ts +1 -1
- package/src/mcp/tool-bridge.ts +1 -1
- package/src/modes/acp/acp-agent.ts +248 -50
- package/src/modes/components/session-observer-overlay.ts +12 -1
- package/src/modes/components/status-line/segments.ts +39 -4
- package/src/modes/controllers/command-controller.ts +27 -2
- package/src/modes/controllers/event-controller.ts +3 -4
- package/src/modes/controllers/extension-ui-controller.ts +3 -2
- package/src/modes/interactive-mode.ts +1 -1
- package/src/modes/rpc/host-tools.ts +1 -1
- package/src/modes/rpc/host-uris.ts +235 -0
- package/src/modes/rpc/rpc-client.ts +1 -1
- package/src/modes/rpc/rpc-mode.ts +27 -1
- package/src/modes/rpc/rpc-types.ts +58 -1
- package/src/modes/runtime-init.ts +2 -1
- package/src/modes/theme/defaults/dark-poimandres.json +1 -0
- package/src/modes/theme/defaults/light-poimandres.json +1 -0
- package/src/modes/theme/theme.ts +117 -117
- package/src/modes/types.ts +1 -1
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/prompts/tools/github.md +4 -4
- package/src/prompts/tools/hashline.md +22 -26
- package/src/prompts/tools/read.md +55 -37
- package/src/sdk.ts +31 -8
- package/src/session/agent-session.ts +74 -104
- package/src/session/messages.ts +16 -51
- package/src/session/session-manager.ts +22 -2
- package/src/session/streaming-output.ts +16 -6
- package/src/task/discovery.ts +5 -2
- package/src/task/executor.ts +210 -87
- package/src/task/index.ts +15 -11
- package/src/task/render.ts +32 -5
- package/src/task/types.ts +54 -39
- package/src/tools/ask.ts +12 -12
- package/src/tools/ast-edit.ts +11 -15
- package/src/tools/ast-grep.ts +9 -10
- package/src/tools/bash-command-fixup.ts +47 -0
- package/src/tools/bash.ts +48 -38
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser.ts +39 -53
- package/src/tools/calculator.ts +12 -11
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +40 -43
- package/src/tools/eval.ts +16 -10
- package/src/tools/find.ts +10 -13
- package/src/tools/gh.ts +108 -132
- package/src/tools/hindsight-recall.ts +4 -6
- package/src/tools/hindsight-reflect.ts +5 -5
- package/src/tools/hindsight-retain.ts +15 -17
- package/src/tools/image-gen.ts +31 -81
- package/src/tools/index.ts +4 -1
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +15 -27
- package/src/tools/job.ts +30 -28
- package/src/tools/output-meta.ts +26 -0
- package/src/tools/read.ts +39 -12
- package/src/tools/recipe/index.ts +7 -9
- package/src/tools/render-mermaid.ts +12 -12
- package/src/tools/report-tool-issue.ts +4 -4
- package/src/tools/resolve.ts +11 -11
- package/src/tools/review.ts +14 -26
- package/src/tools/search-tool-bm25.ts +7 -9
- package/src/tools/search.ts +19 -22
- package/src/tools/ssh.ts +10 -9
- package/src/tools/todo-write.ts +26 -34
- package/src/tools/vim.ts +10 -26
- package/src/tools/write.ts +25 -5
- package/src/tools/yield.ts +100 -54
- package/src/web/search/index.ts +9 -24
- package/src/web/search/providers/anthropic.ts +5 -0
- package/src/web/search/providers/exa.ts +3 -0
- package/src/web/search/providers/gemini.ts +5 -0
- package/src/web/search/providers/jina.ts +5 -2
- package/src/web/search/providers/zai.ts +5 -2
- package/src/prompts/compaction/branch-summary-context.md +0 -5
- package/src/prompts/compaction/branch-summary-preamble.md +0 -2
- package/src/prompts/compaction/branch-summary.md +0 -30
- package/src/prompts/compaction/compaction-short-summary.md +0 -9
- package/src/prompts/compaction/compaction-summary-context.md +0 -5
- package/src/prompts/compaction/compaction-summary.md +0 -38
- package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
- package/src/prompts/compaction/compaction-update-summary.md +0 -45
- package/src/prompts/system/auto-handoff-threshold-focus.md +0 -1
- package/src/prompts/system/file-operations.md +0 -10
- package/src/prompts/system/handoff-document.md +0 -49
- package/src/prompts/system/summarization-system.md +0 -3
- package/src/session/compaction/branch-summarization.ts +0 -324
- package/src/session/compaction/compaction.ts +0 -1420
- package/src/session/compaction/errors.ts +0 -31
- package/src/session/compaction/index.ts +0 -8
- package/src/session/compaction/pruning.ts +0 -91
- package/src/session/compaction/utils.ts +0 -184
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal `@sinclair/typebox` runtime compatibility shim, backed by Zod.
|
|
3
|
+
*
|
|
4
|
+
* Historically the coding agent injected the real `@sinclair/typebox` (~5MB
|
|
5
|
+
* dependency) into extensions, hooks, custom tools, and custom commands so
|
|
6
|
+
* they could author parameter schemas as `Type.Object({ name: Type.String() })`.
|
|
7
|
+
* Internally everything already runs through Zod (`wire.ts`, `validation.ts`);
|
|
8
|
+
* the only reason TypeBox remained was extension-author compat.
|
|
9
|
+
*
|
|
10
|
+
* This module replaces that injection with a tiny façade whose `Type` builders
|
|
11
|
+
* return Zod schemas. Output is indistinguishable from hand-written Zod inside
|
|
12
|
+
* the agent pipeline:
|
|
13
|
+
*
|
|
14
|
+
* - `isZodSchema()` keys off the Zod `_zod` marker that every schema carries.
|
|
15
|
+
* - `zodToWireSchema()` emits the same draft-7 JSON Schema providers expect
|
|
16
|
+
* from TypeBox-authored tools (defaulted fields treated as optional, etc.).
|
|
17
|
+
*
|
|
18
|
+
* The surface intentionally covers only the common TypeBox builders. Plugins
|
|
19
|
+
* that reached for niche TypeBox-only APIs (`TypeCompiler`, the global
|
|
20
|
+
* `TypeRegistry`, custom `Symbol(TypeBox.Kind)` introspection) must vendor
|
|
21
|
+
* `@sinclair/typebox` directly in their own package.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { areJsonValuesEqual } from "@oh-my-pi/pi-ai/utils/schema";
|
|
25
|
+
import {
|
|
26
|
+
type ZodArray,
|
|
27
|
+
type ZodEnum,
|
|
28
|
+
type ZodObject,
|
|
29
|
+
type ZodOptional,
|
|
30
|
+
type ZodRawShape,
|
|
31
|
+
type ZodType,
|
|
32
|
+
z,
|
|
33
|
+
} from "zod/v4";
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Type aliases — exported so `import type { Static, TSchema } from "..."`
|
|
37
|
+
// patterns keep compiling at the call site.
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
export type TSchema = ZodType;
|
|
41
|
+
export type Static<T extends ZodType> = z.infer<T>;
|
|
42
|
+
export type TAny = ZodType;
|
|
43
|
+
export type TUnknown = ZodType;
|
|
44
|
+
export type TNever = ZodType;
|
|
45
|
+
export type TNull = ZodType;
|
|
46
|
+
export type TString = z.ZodString;
|
|
47
|
+
export type TNumber = z.ZodNumber;
|
|
48
|
+
export type TInteger = z.ZodNumber;
|
|
49
|
+
export type TBoolean = z.ZodBoolean;
|
|
50
|
+
export type TLiteral<V extends string | number | boolean> = z.ZodLiteral<V>;
|
|
51
|
+
export type TArray<E extends ZodType> = ZodArray<E>;
|
|
52
|
+
export type TObject<P extends ZodRawShape = ZodRawShape> = ZodObject<P>;
|
|
53
|
+
export type TOptional<E extends ZodType> = ZodOptional<E>;
|
|
54
|
+
export type TUnion<_T extends readonly ZodType[] = readonly ZodType[]> = ZodType;
|
|
55
|
+
export type TEnum<T extends readonly (string | number)[] = readonly (string | number)[]> = ZodEnum<{
|
|
56
|
+
[K in T[number] as `${K}`]: K;
|
|
57
|
+
}>;
|
|
58
|
+
export type TRecord<_K extends ZodType, _V extends ZodType> = ZodType;
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Option shapes — loose subset of JSON Schema metadata + per-type constraints.
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
interface Meta {
|
|
65
|
+
title?: string;
|
|
66
|
+
description?: string;
|
|
67
|
+
default?: unknown;
|
|
68
|
+
examples?: unknown[];
|
|
69
|
+
// Real TypeBox accepts arbitrary extra JSON Schema keywords; we tolerate
|
|
70
|
+
// them silently so callers don't blow up on niche metadata.
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface StringOpts extends Meta {
|
|
75
|
+
minLength?: number;
|
|
76
|
+
maxLength?: number;
|
|
77
|
+
pattern?: string;
|
|
78
|
+
format?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface NumberOpts extends Meta {
|
|
82
|
+
minimum?: number;
|
|
83
|
+
maximum?: number;
|
|
84
|
+
exclusiveMinimum?: number;
|
|
85
|
+
exclusiveMaximum?: number;
|
|
86
|
+
multipleOf?: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface ArrayOpts extends Meta {
|
|
90
|
+
minItems?: number;
|
|
91
|
+
maxItems?: number;
|
|
92
|
+
uniqueItems?: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface ObjectOpts extends Meta {
|
|
96
|
+
/**
|
|
97
|
+
* TypeBox default: extra keys are preserved. Set `false` to reject unknowns,
|
|
98
|
+
* `true` to allow any, or a schema to validate them.
|
|
99
|
+
*/
|
|
100
|
+
additionalProperties?: boolean | ZodType;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Helpers
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
function withMeta<T extends ZodType>(schema: T, opts: Meta | undefined): T {
|
|
108
|
+
if (!opts) return schema;
|
|
109
|
+
let out: ZodType = schema;
|
|
110
|
+
if (typeof opts.description === "string") out = out.describe(opts.description);
|
|
111
|
+
if ("default" in opts) out = out.default(opts.default as never) as unknown as ZodType;
|
|
112
|
+
|
|
113
|
+
const metadata: Record<string, unknown> = {};
|
|
114
|
+
for (const [key, value] of Object.entries(opts)) {
|
|
115
|
+
if (key === "description" || key === "default" || key === "additionalProperties") continue;
|
|
116
|
+
metadata[key] = value;
|
|
117
|
+
}
|
|
118
|
+
if (Object.keys(metadata).length > 0) out = out.meta(metadata);
|
|
119
|
+
return out as T;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Builders
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
function tString(opts?: StringOpts): ZodType {
|
|
127
|
+
let s: ZodType = z.string();
|
|
128
|
+
if (opts) {
|
|
129
|
+
// Format selection swaps the base schema for a more specific Zod string
|
|
130
|
+
// validator that emits the right `format` keyword in JSON Schema.
|
|
131
|
+
switch (opts.format) {
|
|
132
|
+
case "email":
|
|
133
|
+
s = z.email();
|
|
134
|
+
break;
|
|
135
|
+
case "url":
|
|
136
|
+
case "uri":
|
|
137
|
+
s = z.url();
|
|
138
|
+
break;
|
|
139
|
+
case "uuid":
|
|
140
|
+
s = z.uuid();
|
|
141
|
+
break;
|
|
142
|
+
case "date-time":
|
|
143
|
+
s = z.iso.datetime();
|
|
144
|
+
break;
|
|
145
|
+
case "date":
|
|
146
|
+
s = z.iso.date();
|
|
147
|
+
break;
|
|
148
|
+
case "time":
|
|
149
|
+
s = z.iso.time();
|
|
150
|
+
break;
|
|
151
|
+
case "ipv4":
|
|
152
|
+
s = z.ipv4();
|
|
153
|
+
break;
|
|
154
|
+
case "ipv6":
|
|
155
|
+
s = z.ipv6();
|
|
156
|
+
break;
|
|
157
|
+
default:
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
// Length/pattern constraints live on the `_ZodString` base that every
|
|
161
|
+
// format-specific schema (ZodEmail, ZodURL, ZodISODateTime, ...) extends,
|
|
162
|
+
// so we apply them regardless of which concrete subclass `s` ended up as.
|
|
163
|
+
const sf = s as z.ZodString;
|
|
164
|
+
if (typeof opts.minLength === "number") s = sf.min(opts.minLength);
|
|
165
|
+
if (typeof opts.maxLength === "number") s = (s as z.ZodString).max(opts.maxLength);
|
|
166
|
+
if (typeof opts.pattern === "string") s = (s as z.ZodString).regex(new RegExp(opts.pattern));
|
|
167
|
+
}
|
|
168
|
+
return withMeta(s, opts);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function applyNumberConstraints(base: z.ZodNumber, opts: NumberOpts | undefined): z.ZodNumber {
|
|
172
|
+
if (!opts) return base;
|
|
173
|
+
let out = base;
|
|
174
|
+
if (typeof opts.minimum === "number") out = out.min(opts.minimum);
|
|
175
|
+
if (typeof opts.maximum === "number") out = out.max(opts.maximum);
|
|
176
|
+
if (typeof opts.exclusiveMinimum === "number") out = out.gt(opts.exclusiveMinimum);
|
|
177
|
+
if (typeof opts.exclusiveMaximum === "number") out = out.lt(opts.exclusiveMaximum);
|
|
178
|
+
if (typeof opts.multipleOf === "number") out = out.multipleOf(opts.multipleOf);
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function tNumber(opts?: NumberOpts): ZodType {
|
|
183
|
+
return withMeta(applyNumberConstraints(z.number(), opts), opts);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function tInteger(opts?: NumberOpts): ZodType {
|
|
187
|
+
return withMeta(applyNumberConstraints(z.number().int(), opts), opts);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function tBoolean(opts?: Meta): ZodType {
|
|
191
|
+
return withMeta(z.boolean(), opts);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function tNull(opts?: Meta): ZodType {
|
|
195
|
+
return withMeta(z.null(), opts);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function tAny(opts?: Meta): ZodType {
|
|
199
|
+
return withMeta(z.any(), opts);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function tUnknown(opts?: Meta): ZodType {
|
|
203
|
+
return withMeta(z.unknown(), opts);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function tNever(opts?: Meta): ZodType {
|
|
207
|
+
return withMeta(z.never(), opts);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function tLiteral<V extends string | number | boolean>(value: V, opts?: Meta): ZodType {
|
|
211
|
+
return withMeta(z.literal(value), opts);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function tUnion<T extends readonly ZodType[]>(schemas: T, opts?: Meta): ZodType {
|
|
215
|
+
if (schemas.length === 0) return withMeta(z.never(), opts);
|
|
216
|
+
if (schemas.length === 1) return withMeta(schemas[0] as ZodType, opts);
|
|
217
|
+
return withMeta(z.union(schemas as unknown as [ZodType, ZodType, ...ZodType[]]), opts);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function tIntersect(schemas: readonly ZodType[], opts?: Meta): ZodType {
|
|
221
|
+
if (schemas.length === 0) return withMeta(z.unknown(), opts);
|
|
222
|
+
if (schemas.length === 1) return withMeta(schemas[0] as ZodType, opts);
|
|
223
|
+
let out: ZodType = schemas[0] as ZodType;
|
|
224
|
+
for (let i = 1; i < schemas.length; i++) out = z.intersection(out, schemas[i] as ZodType) as ZodType;
|
|
225
|
+
return withMeta(out, opts);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function isArrayIndexKey(key: string): boolean {
|
|
229
|
+
if (!/^(?:0|[1-9]\\d*)$/.test(key)) return false;
|
|
230
|
+
const index = Number(key);
|
|
231
|
+
return Number.isSafeInteger(index) && index >= 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function uniqueLiteralValues(values: readonly (string | number | boolean)[]): Array<string | number | boolean> {
|
|
235
|
+
const unique: Array<string | number | boolean> = [];
|
|
236
|
+
for (const value of values) {
|
|
237
|
+
if (!unique.some(existing => existing === value)) unique.push(value);
|
|
238
|
+
}
|
|
239
|
+
return unique;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function literalUnion(values: readonly (string | number | boolean)[], opts?: Meta): ZodType {
|
|
243
|
+
const unique = uniqueLiteralValues(values);
|
|
244
|
+
if (unique.length === 0) return withMeta(z.never(), opts);
|
|
245
|
+
if (unique.length === 1) return withMeta(z.literal(unique[0] as string | number | boolean), opts);
|
|
246
|
+
const schemas = unique.map(value => z.literal(value as string | number | boolean)) as unknown as [
|
|
247
|
+
ZodType,
|
|
248
|
+
ZodType,
|
|
249
|
+
...ZodType[],
|
|
250
|
+
];
|
|
251
|
+
return withMeta(z.union(schemas), opts);
|
|
252
|
+
}
|
|
253
|
+
function tEnum<T extends Record<string, string | number> | readonly (string | number)[]>(
|
|
254
|
+
values: T,
|
|
255
|
+
opts?: Meta,
|
|
256
|
+
): ZodType {
|
|
257
|
+
const list = Array.isArray(values)
|
|
258
|
+
? values
|
|
259
|
+
: Object.entries(values)
|
|
260
|
+
.filter(([key, value]) => !(isArrayIndexKey(key) && typeof value === "string"))
|
|
261
|
+
.map(([, value]) => value);
|
|
262
|
+
return literalUnion(list, opts);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function tArray<E extends ZodType>(item: E, opts?: ArrayOpts): ZodType {
|
|
266
|
+
let arr: ZodType = z.array(item);
|
|
267
|
+
if (opts) {
|
|
268
|
+
if (typeof opts.minItems === "number") arr = (arr as ZodArray<E>).min(opts.minItems);
|
|
269
|
+
if (typeof opts.maxItems === "number") arr = (arr as ZodArray<E>).max(opts.maxItems);
|
|
270
|
+
if (opts.uniqueItems === true) {
|
|
271
|
+
arr = arr.refine(items => {
|
|
272
|
+
if (!Array.isArray(items)) return true;
|
|
273
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
274
|
+
for (let j = i + 1; j < items.length; j += 1) {
|
|
275
|
+
if (areJsonValuesEqual(items[i], items[j])) return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
}, "Expected array items to be unique");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return withMeta(arr, opts);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function tTuple(items: readonly ZodType[], opts?: Meta): ZodType {
|
|
286
|
+
return withMeta(z.tuple(items as unknown as [ZodType, ...ZodType[]]) as unknown as ZodType, opts);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function isOptional(schema: ZodType): boolean {
|
|
290
|
+
const def = (schema as { _zod?: { def?: { type?: string } } })._zod?.def;
|
|
291
|
+
return def?.type === "optional";
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function tObject<P extends ZodRawShape>(properties: P, opts?: ObjectOpts): ZodObject<P> {
|
|
295
|
+
// `z.object` automatically derives `required` from non-optional entries,
|
|
296
|
+
// so `Type.Optional(...)` flows through unchanged (Zod treats `.optional()`
|
|
297
|
+
// and `Type.Optional`-style wrappers identically).
|
|
298
|
+
let obj = z.object(properties);
|
|
299
|
+
const ap = opts?.additionalProperties;
|
|
300
|
+
if (ap === false) {
|
|
301
|
+
obj = obj.strict() as unknown as ZodObject<P>;
|
|
302
|
+
} else if (ap === undefined || ap === true) {
|
|
303
|
+
// TypeBox preserves unknown keys by default; Zod's default is `.strip()`.
|
|
304
|
+
obj = obj.loose() as unknown as ZodObject<P>;
|
|
305
|
+
} else {
|
|
306
|
+
obj = obj.catchall(ap) as unknown as ZodObject<P>;
|
|
307
|
+
}
|
|
308
|
+
return withMeta(obj, opts);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function tRecord<V extends ZodType>(key: ZodType, value: V, opts?: Meta): ZodType {
|
|
312
|
+
return withMeta(z.record(key as never, value as never) as unknown as ZodType, opts);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function tOptional<E extends ZodType>(schema: E, _opts?: Meta): ZodOptional<E> {
|
|
316
|
+
return isOptional(schema) ? (schema as unknown as ZodOptional<E>) : (schema.optional() as ZodOptional<E>);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function tNullable<E extends ZodType>(schema: E, opts?: Meta): ZodType {
|
|
320
|
+
return withMeta(schema.nullable() as ZodType, opts);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function tReadonly<E extends ZodType>(schema: E): E {
|
|
324
|
+
// TypeBox's `Type.Readonly` is purely a marker; runtime parsing is identical.
|
|
325
|
+
return schema;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function tPartial<P extends ZodRawShape>(obj: ZodObject<P>): ZodObject<P> {
|
|
329
|
+
return obj.partial() as unknown as ZodObject<P>;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function tRequired<P extends ZodRawShape>(obj: ZodObject<P>): ZodObject<P> {
|
|
333
|
+
return obj.required() as unknown as ZodObject<P>;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function tPick<P extends ZodRawShape, K extends keyof P>(obj: ZodObject<P>, keys: readonly K[]): ZodObject<Pick<P, K>> {
|
|
337
|
+
const mask = Object.fromEntries(keys.map(k => [k as string, true]));
|
|
338
|
+
return obj.pick(mask as never) as unknown as ZodObject<Pick<P, K>>;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function tOmit<P extends ZodRawShape, K extends keyof P>(obj: ZodObject<P>, keys: readonly K[]): ZodObject<Omit<P, K>> {
|
|
342
|
+
const mask = Object.fromEntries(keys.map(k => [k as string, true]));
|
|
343
|
+
return obj.omit(mask as never) as unknown as ZodObject<Omit<P, K>>;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function tComposite(objects: readonly ZodObject<ZodRawShape>[], opts?: Meta): ZodObject<ZodRawShape> {
|
|
347
|
+
// `Type.Composite([...])` flattens every object schema into one object schema
|
|
348
|
+
// rather than producing an intersection. Mirror that via repeated `extend`.
|
|
349
|
+
if (objects.length === 0) return withMeta(z.object({}), opts) as ZodObject<ZodRawShape>;
|
|
350
|
+
let out = objects[0] as ZodObject<ZodRawShape>;
|
|
351
|
+
for (let i = 1; i < objects.length; i += 1) {
|
|
352
|
+
out = out.extend(objects[i].shape) as ZodObject<ZodRawShape>;
|
|
353
|
+
}
|
|
354
|
+
return withMeta(out, opts) as ZodObject<ZodRawShape>;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
// Public `Type` namespace
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
|
|
361
|
+
export const Type = {
|
|
362
|
+
String: tString,
|
|
363
|
+
Number: tNumber,
|
|
364
|
+
Integer: tInteger,
|
|
365
|
+
Boolean: tBoolean,
|
|
366
|
+
Null: tNull,
|
|
367
|
+
Any: tAny,
|
|
368
|
+
Unknown: tUnknown,
|
|
369
|
+
Never: tNever,
|
|
370
|
+
Literal: tLiteral,
|
|
371
|
+
Union: tUnion,
|
|
372
|
+
Intersect: tIntersect,
|
|
373
|
+
Enum: tEnum,
|
|
374
|
+
Array: tArray,
|
|
375
|
+
Tuple: tTuple,
|
|
376
|
+
Object: tObject,
|
|
377
|
+
Record: tRecord,
|
|
378
|
+
Optional: tOptional,
|
|
379
|
+
Nullable: tNullable,
|
|
380
|
+
Readonly: tReadonly,
|
|
381
|
+
Partial: tPartial,
|
|
382
|
+
Required: tRequired,
|
|
383
|
+
Pick: tPick,
|
|
384
|
+
Omit: tOmit,
|
|
385
|
+
Composite: tComposite,
|
|
386
|
+
} as const;
|
|
387
|
+
|
|
388
|
+
export type TypeBuilder = typeof Type;
|
|
389
|
+
|
|
390
|
+
/** Default namespace export so `import * as typebox from "./typebox"` still resolves the `Type` key. */
|
|
391
|
+
export default { Type };
|
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { formatNumber, prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import * as z from "zod/v4";
|
|
6
6
|
import type { RenderResultOptions } from "../../extensibility/custom-tools/types";
|
|
7
7
|
import type { Theme, ThemeColor } from "../../modes/theme/theme";
|
|
8
8
|
import goalDescription from "../../prompts/tools/goal.md" with { type: "text" };
|
|
@@ -14,19 +14,13 @@ import { renderStatusLine, truncateToWidth } from "../../tui";
|
|
|
14
14
|
import { completionBudgetReport, remainingTokens } from "../runtime";
|
|
15
15
|
import type { Goal, GoalStatus, GoalToolDetails } from "../state";
|
|
16
16
|
|
|
17
|
-
const goalSchema =
|
|
18
|
-
op:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
objective: Type.Optional(Type.String({ description: "Goal objective. Required when op=create." })),
|
|
22
|
-
token_budget: Type.Optional(
|
|
23
|
-
Type.Integer({
|
|
24
|
-
description: "Optional positive token budget. Only honored when op=create.",
|
|
25
|
-
}),
|
|
26
|
-
),
|
|
17
|
+
const goalSchema = z.object({
|
|
18
|
+
op: z.union([z.literal("create"), z.literal("get"), z.literal("complete")]).describe("Goal operation."),
|
|
19
|
+
objective: z.string().describe("Goal objective. Required when op=create.").optional(),
|
|
20
|
+
token_budget: z.number().int().describe("Optional positive token budget. Only honored when op=create.").optional(),
|
|
27
21
|
});
|
|
28
22
|
|
|
29
|
-
export type GoalToolInput =
|
|
23
|
+
export type GoalToolInput = z.infer<typeof goalSchema>;
|
|
30
24
|
|
|
31
25
|
export interface GoalToolResponse {
|
|
32
26
|
goal: Goal | null;
|
package/src/hashline/input.ts
CHANGED
|
@@ -108,7 +108,8 @@ export function splitHashlineInputs(input: string, options: SplitHashlineOptions
|
|
|
108
108
|
|
|
109
109
|
const flush = () => {
|
|
110
110
|
if (currentPath.length === 0) return;
|
|
111
|
-
|
|
111
|
+
const hasOps = currentLines.some(rawLine => stripTrailingCarriageReturn(rawLine).trim().length > 0);
|
|
112
|
+
if (hasOps) sections.push({ path: currentPath, diff: currentLines.join("\n") });
|
|
112
113
|
currentLines = [];
|
|
113
114
|
};
|
|
114
115
|
|
package/src/hashline/parser.ts
CHANGED
|
@@ -86,9 +86,33 @@ function collectPayload(
|
|
|
86
86
|
let index = startIndex;
|
|
87
87
|
while (index < lines.length) {
|
|
88
88
|
const line = stripTrailingCarriageReturn(lines[index]);
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
if (line.startsWith(HL_EDIT_SEP)) {
|
|
90
|
+
payload.push(line.slice(1));
|
|
91
|
+
index++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// Silently recover from a missing payload prefix on an otherwise blank
|
|
95
|
+
// line: if more payload follows (possibly past further blanks), treat
|
|
96
|
+
// each intervening blank as an empty `${HL_EDIT_SEP}` payload line.
|
|
97
|
+
// Additionally, when the op explicitly requires payload (`+`/`<`) and
|
|
98
|
+
// we have not collected any yet, accept the blank(s) themselves as the
|
|
99
|
+
// empty payload — common typo of forgetting the `${HL_EDIT_SEP}` prefix
|
|
100
|
+
// when inserting a blank line.
|
|
101
|
+
if (line.length === 0) {
|
|
102
|
+
let lookahead = index + 1;
|
|
103
|
+
while (lookahead < lines.length && stripTrailingCarriageReturn(lines[lookahead]).length === 0) {
|
|
104
|
+
lookahead++;
|
|
105
|
+
}
|
|
106
|
+
const followedByPayload =
|
|
107
|
+
lookahead < lines.length && stripTrailingCarriageReturn(lines[lookahead]).startsWith(HL_EDIT_SEP);
|
|
108
|
+
const acceptBareBlank = requirePayload && payload.length === 0;
|
|
109
|
+
if (followedByPayload || acceptBareBlank) {
|
|
110
|
+
for (let j = index; j < lookahead; j++) payload.push("");
|
|
111
|
+
index = lookahead;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
92
116
|
}
|
|
93
117
|
if (payload.length === 0 && requirePayload) {
|
|
94
118
|
throw new Error(`line ${opLineNum}: + and < operations require at least one ${HL_EDIT_SEP}TEXT payload line.`);
|
package/src/hashline/types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Type } from "@sinclair/typebox";
|
|
1
|
+
import * as z from "zod/v4";
|
|
3
2
|
import type { LspBatchRequest } from "../edit/renderer";
|
|
4
3
|
import type { WritethroughCallback, WritethroughDeferredHandle } from "../lsp";
|
|
5
4
|
import type { ToolSession } from "../tools";
|
|
@@ -26,8 +25,9 @@ export type HashlineEdit =
|
|
|
26
25
|
| { kind: "insert"; cursor: HashlineCursor; text: string; lineNum: number; index: number }
|
|
27
26
|
| { kind: "delete"; anchor: Anchor; lineNum: number; index: number; oldAssertion?: string };
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
export
|
|
28
|
+
/** `path` is accepted by the edit tool runtime; other extra keys are preserved. */
|
|
29
|
+
export const hashlineEditParamsSchema = z.object({ input: z.string(), path: z.string().optional() }).passthrough();
|
|
30
|
+
export type HashlineParams = z.infer<typeof hashlineEditParamsSchema>;
|
|
31
31
|
|
|
32
32
|
export interface HashlineStreamOptions {
|
|
33
33
|
/** First line number to use when formatting (1-indexed). */
|
package/src/hindsight/state.ts
CHANGED
|
@@ -346,7 +346,7 @@ export class HindsightSessionState {
|
|
|
346
346
|
async maybeRecallOnAgentStart(): Promise<void> {
|
|
347
347
|
if (!this.config.autoRecall || this.hasRecalledForFirstTurn) return;
|
|
348
348
|
const messages = extractMessages(this.session.sessionManager);
|
|
349
|
-
const lastUser =
|
|
349
|
+
const lastUser = messages.findLast(m => m.role === "user");
|
|
350
350
|
if (!lastUser) return;
|
|
351
351
|
|
|
352
352
|
const query = composeRecallQuery(lastUser.content, messages, this.config.recallContextTurns);
|
|
@@ -386,7 +386,7 @@ export class HindsightSessionState {
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
async recallForCompaction(messages: HindsightMessage[]): Promise<string | undefined> {
|
|
389
|
-
const lastUser =
|
|
389
|
+
const lastUser = messages.findLast(m => m.role === "user");
|
|
390
390
|
if (!lastUser) return undefined;
|
|
391
391
|
|
|
392
392
|
const query = composeRecallQuery(lastUser.content, messages, this.config.recallContextTurns);
|
package/src/index.ts
CHANGED
|
@@ -43,8 +43,6 @@ export * from "./sdk";
|
|
|
43
43
|
export * from "./session/agent-session";
|
|
44
44
|
// Auth and model registry
|
|
45
45
|
export * from "./session/auth-storage";
|
|
46
|
-
// Compaction
|
|
47
|
-
export * from "./session/compaction";
|
|
48
46
|
export * from "./session/messages";
|
|
49
47
|
export * from "./session/session-dump-format";
|
|
50
48
|
export * from "./session/session-manager";
|