@aexol/spectral 0.7.1 → 0.7.5

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.
Files changed (219) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/agent/agents.js +1 -1
  3. package/dist/agent/index.js +199 -184
  4. package/dist/commands/serve.js +0 -3
  5. package/dist/designer/data/systems/renault/DESIGN.md +1 -1
  6. package/dist/designer/philosophies.js +668 -0
  7. package/dist/mcp/sampling-handler.js +1 -1
  8. package/dist/memory/commands/status.js +1 -1
  9. package/dist/memory/compaction.js +2 -2
  10. package/dist/memory/config.js +1 -1
  11. package/dist/memory/debug-log.js +1 -1
  12. package/dist/memory/hooks/compaction-hook.js +29 -0
  13. package/dist/memory/index.js +2 -0
  14. package/dist/memory/observer.js +2 -2
  15. package/dist/memory/project-observations-store.js +14 -0
  16. package/dist/memory/tokens.js +1 -1
  17. package/dist/memory/tools/read-project-observations.js +82 -0
  18. package/dist/memory/tools/recall-observation.js +2 -2
  19. package/dist/pi/agent-core/agent-loop.js +501 -0
  20. package/dist/pi/agent-core/agent.js +401 -0
  21. package/dist/pi/agent-core/harness/agent-harness.js +899 -0
  22. package/dist/pi/agent-core/harness/compaction/branch-summarization.js +173 -0
  23. package/dist/pi/agent-core/harness/compaction/compaction.js +532 -0
  24. package/dist/pi/agent-core/harness/compaction/utils.js +130 -0
  25. package/dist/pi/agent-core/harness/env/nodejs.js +485 -0
  26. package/dist/pi/agent-core/harness/messages.js +101 -0
  27. package/dist/pi/agent-core/harness/prompt-templates.js +229 -0
  28. package/dist/pi/agent-core/harness/session/jsonl-repo.js +100 -0
  29. package/dist/pi/agent-core/harness/session/jsonl-storage.js +230 -0
  30. package/dist/pi/agent-core/harness/session/memory-repo.js +41 -0
  31. package/dist/pi/agent-core/harness/session/memory-storage.js +113 -0
  32. package/dist/pi/agent-core/harness/session/repo-utils.js +38 -0
  33. package/dist/pi/agent-core/harness/session/session.js +196 -0
  34. package/dist/pi/agent-core/harness/session/uuid.js +49 -0
  35. package/dist/pi/agent-core/harness/skills.js +310 -0
  36. package/dist/pi/agent-core/harness/system-prompt.js +29 -0
  37. package/dist/pi/agent-core/harness/types.js +93 -0
  38. package/dist/pi/agent-core/harness/utils/shell-output.js +125 -0
  39. package/dist/pi/agent-core/harness/utils/truncate.js +289 -0
  40. package/dist/pi/agent-core/index.js +24 -0
  41. package/dist/pi/agent-core/node.js +2 -0
  42. package/dist/pi/agent-core/proxy.js +277 -0
  43. package/dist/pi/agent-core/types.js +1 -0
  44. package/dist/pi/ai/api-registry.js +43 -0
  45. package/dist/pi/ai/cli.js +120 -0
  46. package/dist/pi/ai/env-api-keys.js +169 -0
  47. package/dist/pi/ai/image-models.generated.js +441 -0
  48. package/dist/pi/ai/image-models.js +22 -0
  49. package/dist/pi/ai/images-api-registry.js +21 -0
  50. package/dist/pi/ai/images.js +13 -0
  51. package/dist/pi/ai/index.js +18 -0
  52. package/dist/pi/ai/models.generated.js +16220 -0
  53. package/dist/pi/ai/models.js +70 -0
  54. package/dist/pi/ai/oauth.js +1 -0
  55. package/dist/pi/ai/providers/anthropic.js +945 -0
  56. package/dist/pi/ai/providers/faux.js +367 -0
  57. package/dist/pi/ai/providers/github-copilot-headers.js +28 -0
  58. package/dist/pi/ai/providers/openai-completions.js +945 -0
  59. package/dist/pi/ai/providers/openai-prompt-cache.js +9 -0
  60. package/dist/pi/ai/providers/register-builtins.js +97 -0
  61. package/dist/pi/ai/providers/simple-options.js +40 -0
  62. package/dist/pi/ai/providers/transform-messages.js +183 -0
  63. package/dist/pi/ai/session-resources.js +21 -0
  64. package/dist/pi/ai/stream.js +26 -0
  65. package/dist/pi/ai/types.js +1 -0
  66. package/dist/pi/ai/utils/diagnostics.js +24 -0
  67. package/dist/pi/ai/utils/event-stream.js +80 -0
  68. package/dist/pi/ai/utils/hash.js +13 -0
  69. package/dist/pi/ai/utils/headers.js +7 -0
  70. package/dist/pi/ai/utils/json-parse.js +112 -0
  71. package/dist/pi/ai/utils/node-http-proxy.js +96 -0
  72. package/dist/pi/ai/utils/oauth/anthropic.js +334 -0
  73. package/dist/pi/ai/utils/oauth/device-code.js +54 -0
  74. package/dist/pi/ai/utils/oauth/github-copilot.js +270 -0
  75. package/dist/pi/ai/utils/oauth/index.js +121 -0
  76. package/dist/pi/ai/utils/oauth/oauth-page.js +104 -0
  77. package/dist/pi/ai/utils/oauth/openai-codex.js +384 -0
  78. package/dist/pi/ai/utils/oauth/pkce.js +30 -0
  79. package/dist/pi/ai/utils/oauth/types.js +1 -0
  80. package/dist/pi/ai/utils/overflow.js +150 -0
  81. package/dist/pi/ai/utils/sanitize-unicode.js +25 -0
  82. package/dist/pi/ai/utils/typebox-helpers.js +20 -0
  83. package/dist/pi/ai/utils/validation.js +280 -0
  84. package/dist/pi/coding-agent/bun/cli.js +7 -0
  85. package/dist/pi/coding-agent/bun/restore-sandbox-env.js +31 -0
  86. package/dist/pi/coding-agent/cli/args.js +340 -0
  87. package/dist/pi/coding-agent/cli/file-processor.js +82 -0
  88. package/dist/pi/coding-agent/cli/initial-message.js +21 -0
  89. package/dist/pi/coding-agent/cli.js +17 -0
  90. package/dist/pi/coding-agent/config.js +414 -0
  91. package/dist/pi/coding-agent/core/agent-session-runtime.js +299 -0
  92. package/dist/pi/coding-agent/core/agent-session-services.js +117 -0
  93. package/dist/pi/coding-agent/core/agent-session.js +2498 -0
  94. package/dist/pi/coding-agent/core/auth-guidance.js +20 -0
  95. package/dist/pi/coding-agent/core/auth-storage.js +441 -0
  96. package/dist/pi/coding-agent/core/bash-executor.js +110 -0
  97. package/dist/pi/coding-agent/core/compaction/branch-summarization.js +242 -0
  98. package/dist/pi/coding-agent/core/compaction/compaction.js +624 -0
  99. package/dist/pi/coding-agent/core/compaction/index.js +6 -0
  100. package/dist/pi/coding-agent/core/compaction/utils.js +152 -0
  101. package/dist/pi/coding-agent/core/defaults.js +1 -0
  102. package/dist/pi/coding-agent/core/diagnostics.js +1 -0
  103. package/dist/pi/coding-agent/core/event-bus.js +24 -0
  104. package/dist/pi/coding-agent/core/exec.js +74 -0
  105. package/dist/pi/coding-agent/core/export-html/ansi-to-html.js +248 -0
  106. package/dist/pi/coding-agent/core/export-html/index.js +225 -0
  107. package/dist/pi/coding-agent/core/export-html/tool-renderer.js +107 -0
  108. package/dist/pi/coding-agent/core/extensions/index.js +8 -0
  109. package/dist/pi/coding-agent/core/extensions/loader.js +485 -0
  110. package/dist/pi/coding-agent/core/extensions/runner.js +824 -0
  111. package/dist/pi/coding-agent/core/extensions/types.js +44 -0
  112. package/dist/pi/coding-agent/core/extensions/wrapper.js +21 -0
  113. package/dist/pi/coding-agent/core/footer-data-provider.js +309 -0
  114. package/dist/pi/coding-agent/core/http-dispatcher.js +47 -0
  115. package/dist/pi/coding-agent/core/index.js +11 -0
  116. package/dist/pi/coding-agent/core/keybindings.js +294 -0
  117. package/dist/pi/coding-agent/core/messages.js +122 -0
  118. package/dist/pi/coding-agent/core/model-registry.js +728 -0
  119. package/dist/pi/coding-agent/core/model-resolver.js +494 -0
  120. package/dist/pi/coding-agent/core/output-guard.js +58 -0
  121. package/dist/pi/coding-agent/core/package-manager.js +2020 -0
  122. package/dist/pi/coding-agent/core/prompt-templates.js +237 -0
  123. package/dist/pi/coding-agent/core/provider-display-names.js +32 -0
  124. package/dist/pi/coding-agent/core/resolve-config-value.js +125 -0
  125. package/dist/pi/coding-agent/core/resource-loader.js +733 -0
  126. package/dist/pi/coding-agent/core/sdk.js +282 -0
  127. package/dist/pi/coding-agent/core/session-cwd.js +37 -0
  128. package/dist/pi/coding-agent/core/session-manager.js +1146 -0
  129. package/dist/pi/coding-agent/core/settings-manager.js +794 -0
  130. package/dist/pi/coding-agent/core/skills.js +386 -0
  131. package/dist/pi/coding-agent/core/slash-commands.js +24 -0
  132. package/dist/pi/coding-agent/core/source-info.js +18 -0
  133. package/dist/pi/coding-agent/core/system-prompt.js +122 -0
  134. package/dist/pi/coding-agent/core/telemetry.js +8 -0
  135. package/dist/pi/coding-agent/core/timings.js +30 -0
  136. package/dist/pi/coding-agent/core/tools/bash.js +341 -0
  137. package/dist/pi/coding-agent/core/tools/edit-diff.js +344 -0
  138. package/dist/pi/coding-agent/core/tools/edit.js +324 -0
  139. package/dist/pi/coding-agent/core/tools/file-mutation-queue.js +36 -0
  140. package/dist/pi/coding-agent/core/tools/find.js +297 -0
  141. package/dist/pi/coding-agent/core/tools/grep.js +303 -0
  142. package/dist/pi/coding-agent/core/tools/index.js +111 -0
  143. package/dist/pi/coding-agent/core/tools/ls.js +168 -0
  144. package/dist/pi/coding-agent/core/tools/output-accumulator.js +183 -0
  145. package/dist/pi/coding-agent/core/tools/path-utils.js +61 -0
  146. package/dist/pi/coding-agent/core/tools/read.js +288 -0
  147. package/dist/pi/coding-agent/core/tools/render-utils.js +48 -0
  148. package/dist/pi/coding-agent/core/tools/tool-definition-wrapper.js +33 -0
  149. package/dist/pi/coding-agent/core/tools/truncate.js +214 -0
  150. package/dist/pi/coding-agent/core/tools/write.js +212 -0
  151. package/dist/pi/coding-agent/index.js +41 -0
  152. package/dist/pi/coding-agent/main.js +5 -0
  153. package/dist/pi/coding-agent/migrations.js +280 -0
  154. package/dist/pi/coding-agent/modes/index.js +7 -0
  155. package/dist/pi/coding-agent/modes/interactive/components/diff.js +132 -0
  156. package/dist/pi/coding-agent/modes/interactive/components/keybinding-hints.js +35 -0
  157. package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +32 -0
  158. package/dist/pi/coding-agent/modes/interactive/interactive-mode.js +3 -0
  159. package/dist/pi/coding-agent/modes/interactive/theme/theme.js +1023 -0
  160. package/dist/pi/coding-agent/modes/print-mode.js +130 -0
  161. package/dist/pi/coding-agent/modes/rpc/jsonl.js +48 -0
  162. package/dist/pi/coding-agent/modes/rpc/rpc-client.js +409 -0
  163. package/dist/pi/coding-agent/modes/rpc/rpc-mode.js +600 -0
  164. package/dist/pi/coding-agent/modes/rpc/rpc-types.js +7 -0
  165. package/dist/pi/coding-agent/utils/ansi.js +51 -0
  166. package/dist/pi/coding-agent/utils/changelog.js +86 -0
  167. package/dist/pi/coding-agent/utils/child-process.js +87 -0
  168. package/dist/pi/coding-agent/utils/clipboard-image.js +244 -0
  169. package/dist/pi/coding-agent/utils/clipboard-native.js +13 -0
  170. package/dist/pi/coding-agent/utils/clipboard.js +116 -0
  171. package/dist/pi/coding-agent/utils/exif-orientation.js +157 -0
  172. package/dist/pi/coding-agent/utils/frontmatter.js +25 -0
  173. package/dist/pi/coding-agent/utils/fs-watch.js +24 -0
  174. package/dist/pi/coding-agent/utils/git.js +162 -0
  175. package/dist/pi/coding-agent/utils/html.js +39 -0
  176. package/dist/pi/coding-agent/utils/image-convert.js +38 -0
  177. package/dist/pi/coding-agent/utils/image-resize.js +136 -0
  178. package/dist/pi/coding-agent/utils/mime.js +68 -0
  179. package/dist/pi/coding-agent/utils/paths.js +91 -0
  180. package/dist/pi/coding-agent/utils/photon.js +120 -0
  181. package/dist/pi/coding-agent/utils/pi-user-agent.js +4 -0
  182. package/dist/pi/coding-agent/utils/shell.js +194 -0
  183. package/dist/pi/coding-agent/utils/sleep.js +16 -0
  184. package/dist/pi/coding-agent/utils/syntax-highlight.js +117 -0
  185. package/dist/pi/coding-agent/utils/tools-manager.js +327 -0
  186. package/dist/pi/coding-agent/utils/version-check.js +81 -0
  187. package/dist/pi/coding-agent/utils/windows-self-update.js +76 -0
  188. package/dist/pi/tui/autocomplete.js +631 -0
  189. package/dist/pi/tui/components/box.js +103 -0
  190. package/dist/pi/tui/components/cancellable-loader.js +34 -0
  191. package/dist/pi/tui/components/editor.js +1915 -0
  192. package/dist/pi/tui/components/image.js +88 -0
  193. package/dist/pi/tui/components/input.js +425 -0
  194. package/dist/pi/tui/components/loader.js +68 -0
  195. package/dist/pi/tui/components/markdown.js +633 -0
  196. package/dist/pi/tui/components/select-list.js +158 -0
  197. package/dist/pi/tui/components/settings-list.js +184 -0
  198. package/dist/pi/tui/components/spacer.js +22 -0
  199. package/dist/pi/tui/components/text.js +88 -0
  200. package/dist/pi/tui/components/truncated-text.js +50 -0
  201. package/dist/pi/tui/editor-component.js +1 -0
  202. package/dist/pi/tui/fuzzy.js +109 -0
  203. package/dist/pi/tui/index.js +31 -0
  204. package/dist/pi/tui/keybindings.js +173 -0
  205. package/dist/pi/tui/keys.js +1172 -0
  206. package/dist/pi/tui/kill-ring.js +43 -0
  207. package/dist/pi/tui/stdin-buffer.js +360 -0
  208. package/dist/pi/tui/terminal-image.js +335 -0
  209. package/dist/pi/tui/terminal.js +324 -0
  210. package/dist/pi/tui/tui.js +1076 -0
  211. package/dist/pi/tui/undo-stack.js +24 -0
  212. package/dist/pi/tui/utils.js +1016 -0
  213. package/dist/relay/dispatcher.js +30 -0
  214. package/dist/server/handlers/queue.js +52 -0
  215. package/dist/server/pi-bridge.js +9 -1
  216. package/dist/server/session-stream.js +76 -111
  217. package/dist/server/storage.js +154 -2
  218. package/dist/server/title-generator.js +14 -153
  219. package/package.json +24 -6
@@ -0,0 +1,280 @@
1
+ import { Compile } from "typebox/compile";
2
+ import { Value } from "typebox/value";
3
+ const validatorCache = new WeakMap();
4
+ const TYPEBOX_KIND = Symbol.for("TypeBox.Kind");
5
+ function isRecord(value) {
6
+ return typeof value === "object" && value !== null;
7
+ }
8
+ function isJsonSchemaObject(value) {
9
+ return isRecord(value);
10
+ }
11
+ function hasTypeBoxMetadata(schema) {
12
+ return isRecord(schema) && Object.getOwnPropertySymbols(schema).includes(TYPEBOX_KIND);
13
+ }
14
+ function getSchemaTypes(schema) {
15
+ if (typeof schema.type === "string") {
16
+ return [schema.type];
17
+ }
18
+ if (Array.isArray(schema.type)) {
19
+ return schema.type.filter((type) => typeof type === "string");
20
+ }
21
+ return [];
22
+ }
23
+ function matchesJsonType(value, type) {
24
+ switch (type) {
25
+ case "number":
26
+ return typeof value === "number";
27
+ case "integer":
28
+ return typeof value === "number" && Number.isInteger(value);
29
+ case "boolean":
30
+ return typeof value === "boolean";
31
+ case "string":
32
+ return typeof value === "string";
33
+ case "null":
34
+ return value === null;
35
+ case "array":
36
+ return Array.isArray(value);
37
+ case "object":
38
+ return isRecord(value) && !Array.isArray(value);
39
+ default:
40
+ return false;
41
+ }
42
+ }
43
+ function isValidatorSchema(value) {
44
+ return isRecord(value);
45
+ }
46
+ function getSubSchemaValidator(schema) {
47
+ if (!isValidatorSchema(schema)) {
48
+ return undefined;
49
+ }
50
+ try {
51
+ return getValidator(schema);
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ function coercePrimitiveByType(value, type) {
58
+ switch (type) {
59
+ case "number": {
60
+ if (value === null) {
61
+ return 0;
62
+ }
63
+ if (typeof value === "string" && value.trim() !== "") {
64
+ const parsed = Number(value);
65
+ if (Number.isFinite(parsed)) {
66
+ return parsed;
67
+ }
68
+ }
69
+ if (typeof value === "boolean") {
70
+ return value ? 1 : 0;
71
+ }
72
+ return value;
73
+ }
74
+ case "integer": {
75
+ if (value === null) {
76
+ return 0;
77
+ }
78
+ if (typeof value === "string" && value.trim() !== "") {
79
+ const parsed = Number(value);
80
+ if (Number.isInteger(parsed)) {
81
+ return parsed;
82
+ }
83
+ }
84
+ if (typeof value === "boolean") {
85
+ return value ? 1 : 0;
86
+ }
87
+ return value;
88
+ }
89
+ case "boolean": {
90
+ if (value === null) {
91
+ return false;
92
+ }
93
+ if (typeof value === "string") {
94
+ if (value === "true") {
95
+ return true;
96
+ }
97
+ if (value === "false") {
98
+ return false;
99
+ }
100
+ }
101
+ if (typeof value === "number") {
102
+ if (value === 1) {
103
+ return true;
104
+ }
105
+ if (value === 0) {
106
+ return false;
107
+ }
108
+ }
109
+ return value;
110
+ }
111
+ case "string": {
112
+ if (value === null) {
113
+ return "";
114
+ }
115
+ if (typeof value === "number" || typeof value === "boolean") {
116
+ return String(value);
117
+ }
118
+ return value;
119
+ }
120
+ case "null": {
121
+ if (value === "" || value === 0 || value === false) {
122
+ return null;
123
+ }
124
+ return value;
125
+ }
126
+ default:
127
+ return value;
128
+ }
129
+ }
130
+ function applySchemaObjectCoercion(value, schema) {
131
+ const properties = schema.properties;
132
+ const definedKeys = new Set(properties ? Object.keys(properties) : []);
133
+ if (properties) {
134
+ for (const [key, propertySchema] of Object.entries(properties)) {
135
+ if (!(key in value)) {
136
+ continue;
137
+ }
138
+ value[key] = coerceWithJsonSchema(value[key], propertySchema);
139
+ }
140
+ }
141
+ if (schema.additionalProperties && isJsonSchemaObject(schema.additionalProperties)) {
142
+ for (const [key, propertyValue] of Object.entries(value)) {
143
+ if (definedKeys.has(key)) {
144
+ continue;
145
+ }
146
+ value[key] = coerceWithJsonSchema(propertyValue, schema.additionalProperties);
147
+ }
148
+ }
149
+ }
150
+ function applySchemaArrayCoercion(value, schema) {
151
+ if (Array.isArray(schema.items)) {
152
+ for (let index = 0; index < value.length; index++) {
153
+ const itemSchema = schema.items[index];
154
+ if (!itemSchema) {
155
+ continue;
156
+ }
157
+ value[index] = coerceWithJsonSchema(value[index], itemSchema);
158
+ }
159
+ return;
160
+ }
161
+ if (isJsonSchemaObject(schema.items)) {
162
+ for (let index = 0; index < value.length; index++) {
163
+ value[index] = coerceWithJsonSchema(value[index], schema.items);
164
+ }
165
+ }
166
+ }
167
+ function coerceWithUnionSchema(value, schemas) {
168
+ for (const schema of schemas) {
169
+ const candidate = structuredClone(value);
170
+ const coerced = coerceWithJsonSchema(candidate, schema);
171
+ const validator = getSubSchemaValidator(schema);
172
+ if (validator?.Check(coerced)) {
173
+ return coerced;
174
+ }
175
+ }
176
+ return value;
177
+ }
178
+ function coerceWithJsonSchema(value, schema) {
179
+ let nextValue = value;
180
+ if (Array.isArray(schema.allOf)) {
181
+ for (const nested of schema.allOf) {
182
+ nextValue = coerceWithJsonSchema(nextValue, nested);
183
+ }
184
+ }
185
+ if (Array.isArray(schema.anyOf)) {
186
+ nextValue = coerceWithUnionSchema(nextValue, schema.anyOf);
187
+ }
188
+ if (Array.isArray(schema.oneOf)) {
189
+ nextValue = coerceWithUnionSchema(nextValue, schema.oneOf);
190
+ }
191
+ const schemaTypes = getSchemaTypes(schema);
192
+ const matchesUnionMember = schemaTypes.length > 1 && schemaTypes.some((schemaType) => matchesJsonType(nextValue, schemaType));
193
+ if (schemaTypes.length > 0 && !matchesUnionMember) {
194
+ for (const schemaType of schemaTypes) {
195
+ const candidate = coercePrimitiveByType(nextValue, schemaType);
196
+ if (candidate !== nextValue) {
197
+ nextValue = candidate;
198
+ break;
199
+ }
200
+ }
201
+ }
202
+ if (schemaTypes.includes("object") && isRecord(nextValue) && !Array.isArray(nextValue)) {
203
+ applySchemaObjectCoercion(nextValue, schema);
204
+ }
205
+ if (schemaTypes.includes("array") && Array.isArray(nextValue)) {
206
+ applySchemaArrayCoercion(nextValue, schema);
207
+ }
208
+ return nextValue;
209
+ }
210
+ function getValidator(schema) {
211
+ const key = schema;
212
+ const cached = validatorCache.get(key);
213
+ if (cached) {
214
+ return cached;
215
+ }
216
+ const validator = Compile(schema);
217
+ validatorCache.set(key, validator);
218
+ return validator;
219
+ }
220
+ function formatValidationPath(error) {
221
+ if (error.keyword === "required") {
222
+ const requiredProperties = error.params.requiredProperties;
223
+ const requiredProperty = requiredProperties?.[0];
224
+ if (requiredProperty) {
225
+ const basePath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
226
+ return basePath ? `${basePath}.${requiredProperty}` : requiredProperty;
227
+ }
228
+ }
229
+ const path = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
230
+ return path || "root";
231
+ }
232
+ /**
233
+ * Finds a tool by name and validates the tool call arguments against its TypeBox schema
234
+ * @param tools Array of tool definitions
235
+ * @param toolCall The tool call from the LLM
236
+ * @returns The validated arguments
237
+ * @throws Error if tool is not found or validation fails
238
+ */
239
+ export function validateToolCall(tools, toolCall) {
240
+ const tool = tools.find((t) => t.name === toolCall.name);
241
+ if (!tool) {
242
+ throw new Error(`Tool "${toolCall.name}" not found`);
243
+ }
244
+ return validateToolArguments(tool, toolCall);
245
+ }
246
+ /**
247
+ * Validates tool call arguments against the tool's TypeBox schema
248
+ * @param tool The tool definition with TypeBox schema
249
+ * @param toolCall The tool call from the LLM
250
+ * @returns The validated (and potentially coerced) arguments
251
+ * @throws Error with formatted message if validation fails
252
+ */
253
+ export function validateToolArguments(tool, toolCall) {
254
+ const args = structuredClone(toolCall.arguments);
255
+ Value.Convert(tool.parameters, args);
256
+ const validator = getValidator(tool.parameters);
257
+ if (!hasTypeBoxMetadata(tool.parameters) && isJsonSchemaObject(tool.parameters)) {
258
+ const coerced = coerceWithJsonSchema(args, tool.parameters);
259
+ if (coerced !== args) {
260
+ if (isRecord(args) && isRecord(coerced)) {
261
+ for (const key of Object.keys(args)) {
262
+ delete args[key];
263
+ }
264
+ Object.assign(args, coerced);
265
+ }
266
+ else {
267
+ return validator.Check(coerced) ? coerced : args;
268
+ }
269
+ }
270
+ }
271
+ if (validator.Check(args)) {
272
+ return args;
273
+ }
274
+ const errors = validator
275
+ .Errors(args)
276
+ .map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)
277
+ .join("\n") || "Unknown validation error";
278
+ const errorMessage = `Validation failed for tool "${toolCall.name}":\n${errors}\n\nReceived arguments:\n${JSON.stringify(toolCall.arguments, null, 2)}`;
279
+ throw new Error(errorMessage);
280
+ }
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { APP_NAME } from "../config.js";
3
+ process.title = APP_NAME;
4
+ process.emitWarning = (() => { });
5
+ import { restoreSandboxEnv } from "./restore-sandbox-env.js";
6
+ restoreSandboxEnv();
7
+ await import("../cli.js");
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Workaround for https://github.com/oven-sh/bun/issues/27802
3
+ *
4
+ * Bun compiled binaries have an empty `process.env` when running inside
5
+ * sandbox environments (e.g. nono on Linux/macOS). On Linux we can recover
6
+ * the environment from `/proc/self/environ`.
7
+ */
8
+ import { readFileSync } from "node:fs";
9
+ /**
10
+ * Restore environment variables from `/proc/self/environ` when running
11
+ * inside a sandbox where Bun's `process.env` is empty.
12
+ */
13
+ export function restoreSandboxEnv() {
14
+ if (!process.versions?.bun)
15
+ return;
16
+ // If process.env already has entries, nothing to fix.
17
+ if (Object.keys(process.env).length > 0)
18
+ return;
19
+ try {
20
+ const data = readFileSync("/proc/self/environ", "utf-8");
21
+ for (const entry of data.split("\0")) {
22
+ const idx = entry.indexOf("=");
23
+ if (idx > 0) {
24
+ process.env[entry.slice(0, idx)] = entry.slice(idx + 1);
25
+ }
26
+ }
27
+ }
28
+ catch {
29
+ // /proc/self/environ may not be readable; ignore.
30
+ }
31
+ }
@@ -0,0 +1,340 @@
1
+ /**
2
+ * CLI argument parsing and help display
3
+ */
4
+ import chalk from "chalk";
5
+ import { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR, ENV_SESSION_DIR } from "../config.js";
6
+ const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
7
+ export function isValidThinkingLevel(level) {
8
+ return VALID_THINKING_LEVELS.includes(level);
9
+ }
10
+ export function parseArgs(args) {
11
+ const result = {
12
+ messages: [],
13
+ fileArgs: [],
14
+ unknownFlags: new Map(),
15
+ diagnostics: [],
16
+ };
17
+ for (let i = 0; i < args.length; i++) {
18
+ const arg = args[i];
19
+ if (arg === "--help" || arg === "-h") {
20
+ result.help = true;
21
+ }
22
+ else if (arg === "--version" || arg === "-v") {
23
+ result.version = true;
24
+ }
25
+ else if (arg === "--mode" && i + 1 < args.length) {
26
+ const mode = args[++i];
27
+ if (mode === "text" || mode === "json" || mode === "rpc") {
28
+ result.mode = mode;
29
+ }
30
+ }
31
+ else if (arg === "--continue" || arg === "-c") {
32
+ result.continue = true;
33
+ }
34
+ else if (arg === "--resume" || arg === "-r") {
35
+ result.resume = true;
36
+ }
37
+ else if (arg === "--provider" && i + 1 < args.length) {
38
+ result.provider = args[++i];
39
+ }
40
+ else if (arg === "--model" && i + 1 < args.length) {
41
+ result.model = args[++i];
42
+ }
43
+ else if (arg === "--api-key" && i + 1 < args.length) {
44
+ result.apiKey = args[++i];
45
+ }
46
+ else if (arg === "--system-prompt" && i + 1 < args.length) {
47
+ result.systemPrompt = args[++i];
48
+ }
49
+ else if (arg === "--append-system-prompt" && i + 1 < args.length) {
50
+ result.appendSystemPrompt = result.appendSystemPrompt ?? [];
51
+ result.appendSystemPrompt.push(args[++i]);
52
+ }
53
+ else if (arg === "--no-session") {
54
+ result.noSession = true;
55
+ }
56
+ else if (arg === "--session" && i + 1 < args.length) {
57
+ result.session = args[++i];
58
+ }
59
+ else if (arg === "--fork" && i + 1 < args.length) {
60
+ result.fork = args[++i];
61
+ }
62
+ else if (arg === "--session-dir" && i + 1 < args.length) {
63
+ result.sessionDir = args[++i];
64
+ }
65
+ else if (arg === "--models" && i + 1 < args.length) {
66
+ result.models = args[++i].split(",").map((s) => s.trim());
67
+ }
68
+ else if (arg === "--no-tools" || arg === "-nt") {
69
+ result.noTools = true;
70
+ }
71
+ else if (arg === "--no-builtin-tools" || arg === "-nbt") {
72
+ result.noBuiltinTools = true;
73
+ }
74
+ else if ((arg === "--tools" || arg === "-t") && i + 1 < args.length) {
75
+ result.tools = args[++i]
76
+ .split(",")
77
+ .map((s) => s.trim())
78
+ .filter((name) => name.length > 0);
79
+ }
80
+ else if (arg === "--thinking" && i + 1 < args.length) {
81
+ const level = args[++i];
82
+ if (isValidThinkingLevel(level)) {
83
+ result.thinking = level;
84
+ }
85
+ else {
86
+ result.diagnostics.push({
87
+ type: "warning",
88
+ message: `Invalid thinking level "${level}". Valid values: ${VALID_THINKING_LEVELS.join(", ")}`,
89
+ });
90
+ }
91
+ }
92
+ else if (arg === "--print" || arg === "-p") {
93
+ result.print = true;
94
+ const next = args[i + 1];
95
+ if (next !== undefined && !next.startsWith("@") && (!next.startsWith("-") || next.startsWith("---"))) {
96
+ result.messages.push(next);
97
+ i++;
98
+ }
99
+ }
100
+ else if (arg === "--export" && i + 1 < args.length) {
101
+ result.export = args[++i];
102
+ }
103
+ else if ((arg === "--extension" || arg === "-e") && i + 1 < args.length) {
104
+ result.extensions = result.extensions ?? [];
105
+ result.extensions.push(args[++i]);
106
+ }
107
+ else if (arg === "--no-extensions" || arg === "-ne") {
108
+ result.noExtensions = true;
109
+ }
110
+ else if (arg === "--skill" && i + 1 < args.length) {
111
+ result.skills = result.skills ?? [];
112
+ result.skills.push(args[++i]);
113
+ }
114
+ else if (arg === "--prompt-template" && i + 1 < args.length) {
115
+ result.promptTemplates = result.promptTemplates ?? [];
116
+ result.promptTemplates.push(args[++i]);
117
+ }
118
+ else if (arg === "--theme" && i + 1 < args.length) {
119
+ result.themes = result.themes ?? [];
120
+ result.themes.push(args[++i]);
121
+ }
122
+ else if (arg === "--no-skills" || arg === "-ns") {
123
+ result.noSkills = true;
124
+ }
125
+ else if (arg === "--no-prompt-templates" || arg === "-np") {
126
+ result.noPromptTemplates = true;
127
+ }
128
+ else if (arg === "--no-themes") {
129
+ result.noThemes = true;
130
+ }
131
+ else if (arg === "--no-context-files" || arg === "-nc") {
132
+ result.noContextFiles = true;
133
+ }
134
+ else if (arg === "--list-models") {
135
+ // Check if next arg is a search pattern (not a flag or file arg)
136
+ if (i + 1 < args.length && !args[i + 1].startsWith("-") && !args[i + 1].startsWith("@")) {
137
+ result.listModels = args[++i];
138
+ }
139
+ else {
140
+ result.listModels = true;
141
+ }
142
+ }
143
+ else if (arg === "--verbose") {
144
+ result.verbose = true;
145
+ }
146
+ else if (arg === "--offline") {
147
+ result.offline = true;
148
+ }
149
+ else if (arg.startsWith("@")) {
150
+ result.fileArgs.push(arg.slice(1)); // Remove @ prefix
151
+ }
152
+ else if (arg.startsWith("--")) {
153
+ const eqIndex = arg.indexOf("=");
154
+ if (eqIndex !== -1) {
155
+ result.unknownFlags.set(arg.slice(2, eqIndex), arg.slice(eqIndex + 1));
156
+ }
157
+ else {
158
+ const flagName = arg.slice(2);
159
+ const next = args[i + 1];
160
+ if (next !== undefined && !next.startsWith("-") && !next.startsWith("@")) {
161
+ result.unknownFlags.set(flagName, next);
162
+ i++;
163
+ }
164
+ else {
165
+ result.unknownFlags.set(flagName, true);
166
+ }
167
+ }
168
+ }
169
+ else if (arg.startsWith("-") && !arg.startsWith("--")) {
170
+ result.diagnostics.push({ type: "error", message: `Unknown option: ${arg}` });
171
+ }
172
+ else if (!arg.startsWith("-")) {
173
+ result.messages.push(arg);
174
+ }
175
+ }
176
+ return result;
177
+ }
178
+ export function printHelp(extensionFlags) {
179
+ const extensionFlagsText = extensionFlags && extensionFlags.length > 0
180
+ ? `\n${chalk.bold("Extension CLI Flags:")}\n${extensionFlags
181
+ .map((flag) => {
182
+ const value = flag.type === "string" ? " <value>" : "";
183
+ const description = flag.description ?? `Registered by ${flag.extensionPath}`;
184
+ return ` --${flag.name}${value}`.padEnd(30) + description;
185
+ })
186
+ .join("\n")}\n`
187
+ : "";
188
+ console.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools
189
+
190
+ ${chalk.bold("Usage:")}
191
+ ${APP_NAME} [options] [@files...] [messages...]
192
+
193
+ ${chalk.bold("Commands:")}
194
+ ${APP_NAME} install <source> [-l] Install extension source and add to settings
195
+ ${APP_NAME} remove <source> [-l] Remove extension source from settings
196
+ ${APP_NAME} uninstall <source> [-l] Alias for remove
197
+ ${APP_NAME} update [source|self|pi] Update pi and installed extensions
198
+ ${APP_NAME} list List installed extensions from settings
199
+ ${APP_NAME} config Open TUI to enable/disable package resources
200
+ ${APP_NAME} <command> --help Show help for install/remove/uninstall/update/list
201
+
202
+ ${chalk.bold("Options:")}
203
+ --provider <name> Provider name (default: google)
204
+ --model <pattern> Model pattern or ID (supports "provider/id" and optional ":<thinking>")
205
+ --api-key <key> API key (defaults to env vars)
206
+ --system-prompt <text> System prompt (default: coding assistant prompt)
207
+ --append-system-prompt <text> Append text or file contents to the system prompt (can be used multiple times)
208
+ --mode <mode> Output mode: text (default), json, or rpc
209
+ --print, -p Non-interactive mode: process prompt and exit
210
+ --continue, -c Continue previous session
211
+ --resume, -r Select a session to resume
212
+ --session <path|id> Use specific session file or partial UUID
213
+ --fork <path|id> Fork specific session file or partial UUID into a new session
214
+ --session-dir <dir> Directory for session storage and lookup
215
+ --no-session Don't save session (ephemeral)
216
+ --models <patterns> Comma-separated model patterns for Ctrl+P cycling
217
+ Supports globs (anthropic/*, *sonnet*) and fuzzy matching
218
+ --no-tools, -nt Disable all tools by default (built-in and extension)
219
+ --no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled
220
+ --tools, -t <tools> Comma-separated allowlist of tool names to enable
221
+ Applies to built-in, extension, and custom tools
222
+ --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
223
+ --extension, -e <path> Load an extension file (can be used multiple times)
224
+ --no-extensions, -ne Disable extension discovery (explicit -e paths still work)
225
+ --skill <path> Load a skill file or directory (can be used multiple times)
226
+ --no-skills, -ns Disable skills discovery and loading
227
+ --prompt-template <path> Load a prompt template file or directory (can be used multiple times)
228
+ --no-prompt-templates, -np Disable prompt template discovery and loading
229
+ --theme <path> Load a theme file or directory (can be used multiple times)
230
+ --no-themes Disable theme discovery and loading
231
+ --no-context-files, -nc Disable AGENTS.md and CLAUDE.md discovery and loading
232
+ --export <file> Export session file to HTML and exit
233
+ --list-models [search] List available models (with optional fuzzy search)
234
+ --verbose Force verbose startup (overrides quietStartup setting)
235
+ --offline Disable startup network operations (same as PI_OFFLINE=1)
236
+ --help, -h Show this help
237
+ --version, -v Show version number
238
+
239
+ Extensions can register additional flags (e.g., --plan from plan-mode extension).${extensionFlagsText}
240
+
241
+ ${chalk.bold("Examples:")}
242
+ # Interactive mode
243
+ ${APP_NAME}
244
+
245
+ # Interactive mode with initial prompt
246
+ ${APP_NAME} "List all .ts files in src/"
247
+
248
+ # Include files in initial message
249
+ ${APP_NAME} @prompt.md @image.png "What color is the sky?"
250
+
251
+ # Non-interactive mode (process and exit)
252
+ ${APP_NAME} -p "List all .ts files in src/"
253
+
254
+ # Multiple messages (interactive)
255
+ ${APP_NAME} "Read package.json" "What dependencies do we have?"
256
+
257
+ # Continue previous session
258
+ ${APP_NAME} --continue "What did we discuss?"
259
+
260
+ # Use different model
261
+ ${APP_NAME} --provider openai --model gpt-4o-mini "Help me refactor this code"
262
+
263
+ # Use model with provider prefix (no --provider needed)
264
+ ${APP_NAME} --model openai/gpt-4o "Help me refactor this code"
265
+
266
+ # Use model with thinking level shorthand
267
+ ${APP_NAME} --model sonnet:high "Solve this complex problem"
268
+
269
+ # Limit model cycling to specific models
270
+ ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o
271
+
272
+ # Limit to a specific provider with glob pattern
273
+ ${APP_NAME} --models "github-copilot/*"
274
+
275
+ # Cycle models with fixed thinking levels
276
+ ${APP_NAME} --models sonnet:high,haiku:low
277
+
278
+ # Start with a specific thinking level
279
+ ${APP_NAME} --thinking high "Solve this complex problem"
280
+
281
+ # Read-only mode (no file modifications possible)
282
+ ${APP_NAME} --tools read,grep,find,ls -p "Review the code in src/"
283
+
284
+ # Export a session file to HTML
285
+ ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent-core/sessions/--path--/session.jsonl
286
+ ${APP_NAME} --export session.jsonl output.html
287
+
288
+ ${chalk.bold("Environment Variables:")}
289
+ ANTHROPIC_API_KEY - Anthropic Claude API key
290
+ ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)
291
+ OPENAI_API_KEY - OpenAI GPT API key
292
+ AZURE_OPENAI_API_KEY - Azure OpenAI API key
293
+ AZURE_OPENAI_BASE_URL - Azure OpenAI/Cognitive Services base URL (e.g. https://{resource}.openai.azure.com)
294
+ AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)
295
+ AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)
296
+ AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)
297
+ DEEPSEEK_API_KEY - DeepSeek API key
298
+ GEMINI_API_KEY - Google Gemini API key
299
+ GROQ_API_KEY - Groq API key
300
+ CEREBRAS_API_KEY - Cerebras API key
301
+ XAI_API_KEY - xAI Grok API key
302
+ FIREWORKS_API_KEY - Fireworks API key
303
+ TOGETHER_API_KEY - Together AI API key
304
+ OPENROUTER_API_KEY - OpenRouter API key
305
+ AI_GATEWAY_API_KEY - Vercel AI Gateway API key
306
+ ZAI_API_KEY - ZAI API key
307
+ MISTRAL_API_KEY - Mistral API key
308
+ MINIMAX_API_KEY - MiniMax API key
309
+ MOONSHOT_API_KEY - Moonshot AI API key
310
+ OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key
311
+ KIMI_API_KEY - Kimi For Coding API key
312
+ CLOUDFLARE_API_KEY - Cloudflare API token (Workers AI and AI Gateway)
313
+ CLOUDFLARE_ACCOUNT_ID - Cloudflare account id (required for both)
314
+ CLOUDFLARE_GATEWAY_ID - Cloudflare AI Gateway slug (required for AI Gateway)
315
+ XIAOMI_API_KEY - Xiaomi MiMo API key (api.xiaomimimo.com billing)
316
+ XIAOMI_TOKEN_PLAN_CN_API_KEY - Xiaomi MiMo Token Plan API key (China region)
317
+ XIAOMI_TOKEN_PLAN_AMS_API_KEY - Xiaomi MiMo Token Plan API key (Amsterdam region)
318
+ XIAOMI_TOKEN_PLAN_SGP_API_KEY - Xiaomi MiMo Token Plan API key (Singapore region)
319
+ AWS_PROFILE - AWS profile for Amazon Bedrock
320
+ AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock
321
+ AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock
322
+ AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)
323
+ AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)
324
+ ${ENV_AGENT_DIR.padEnd(32)} - Config directory (default: ~/${CONFIG_DIR_NAME}/agent)
325
+ ${ENV_SESSION_DIR.padEnd(32)} - Session storage directory (overridden by --session-dir)
326
+ PI_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)
327
+ PI_OFFLINE - Disable startup network operations when set to 1/true/yes
328
+ PI_TELEMETRY - Override install telemetry when set to 1/true/yes or 0/false/no
329
+ PI_SHARE_VIEWER_URL - Base URL for /share command (default: https://pi.dev/session/)
330
+
331
+ ${chalk.bold("Built-in Tool Names:")}
332
+ read - Read file contents
333
+ bash - Execute bash commands
334
+ edit - Edit files with find/replace
335
+ write - Write files (creates/overwrites)
336
+ grep - Search file contents (read-only, off by default)
337
+ find - Find files by glob pattern (read-only, off by default)
338
+ ls - List directory contents (read-only, off by default)
339
+ `);
340
+ }