@oh-my-pi/pi-coding-agent 17.2.10 → 17.2.12
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 +53 -0
- package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
- package/dist/cli.js +10775 -10762
- package/dist/types/advisor/runtime.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -1
- package/dist/types/config/custom-models.d.ts +31 -0
- package/dist/types/config/model-config-values.d.ts +19 -0
- package/dist/types/config/model-patch.d.ts +93 -0
- package/dist/types/config/model-provider-discovery.d.ts +51 -0
- package/dist/types/config/model-registry.d.ts +9 -52
- package/dist/types/config/settings-schema.d.ts +1 -34
- package/dist/types/config/settings.d.ts +5 -3
- package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
- package/dist/types/discovery/agent-plugin-format.d.ts +136 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +33 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/eval/executor-base.d.ts +8 -2
- package/dist/types/eval/kernel-session-registry.d.ts +60 -0
- package/dist/types/export/share.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- package/dist/types/lsp/diagnostics.d.ts +96 -0
- package/dist/types/lsp/index.d.ts +7 -128
- package/dist/types/lsp/servers.d.ts +72 -0
- package/dist/types/lsp/tool.d.ts +42 -0
- package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
- package/dist/types/lsp/writethrough.d.ts +33 -0
- package/dist/types/mcp/transports/header-policy.d.ts +46 -0
- package/dist/types/mcp/types.d.ts +15 -0
- package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
- package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
- package/dist/types/modes/components/agent-hub.d.ts +2 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/theme/color.d.ts +19 -0
- package/dist/types/modes/theme/loader.d.ts +19 -0
- package/dist/types/modes/theme/schema.d.ts +175 -0
- package/dist/types/modes/theme/symbols.d.ts +28 -0
- package/dist/types/modes/theme/theme-class.d.ts +244 -0
- package/dist/types/modes/theme/theme.d.ts +9 -280
- package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +1 -3
- package/dist/types/secrets/index.d.ts +26 -2
- package/dist/types/secrets/message-transform.d.ts +40 -0
- package/dist/types/secrets/obfuscator.d.ts +0 -108
- package/dist/types/secrets/placeholder-scan.d.ts +95 -0
- package/dist/types/secrets/placeholder.d.ts +94 -0
- package/dist/types/secrets/replacement.d.ts +82 -0
- package/dist/types/session/agent-session-events.d.ts +2 -2
- package/dist/types/session/agent-session-types.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -1
- package/dist/types/session/session-handoff.d.ts +3 -3
- package/dist/types/session/session-manager.d.ts +32 -0
- package/dist/types/session/session-provider-boundary.d.ts +1 -1
- package/dist/types/session/session-tools.d.ts +8 -0
- package/dist/types/session/turn-recovery.d.ts +9 -4
- package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
- package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
- package/dist/types/slash-commands/builtin-control.d.ts +2 -0
- package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
- package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
- package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/slash-commands/builtin-session.d.ts +2 -0
- package/dist/types/task/index.d.ts +2 -0
- package/dist/types/tools/gh-common.d.ts +69 -0
- package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
- package/dist/types/tools/gh-pr-diff.d.ts +102 -0
- package/dist/types/tools/gh-run-watch.d.ts +78 -0
- package/dist/types/tools/gh-search.d.ts +61 -0
- package/dist/types/tools/gh-types.d.ts +340 -0
- package/dist/types/tools/gh-view.d.ts +88 -0
- package/dist/types/tools/gh.d.ts +4 -201
- package/dist/types/tools/read-archive.d.ts +16 -0
- package/dist/types/tools/read-format.d.ts +104 -0
- package/dist/types/tools/read-path-resolution.d.ts +19 -0
- package/dist/types/tools/read-pdf-images.d.ts +12 -0
- package/dist/types/tools/read-renderer.d.ts +24 -0
- package/dist/types/tools/read-selector.d.ts +27 -0
- package/dist/types/tools/read-sqlite.d.ts +16 -0
- package/dist/types/tools/read-summary.d.ts +19 -0
- package/dist/types/tools/read.d.ts +1 -23
- package/dist/types/tools/shell-tokenize.d.ts +18 -1
- package/dist/types/utils/changelog.d.ts +0 -5
- package/package.json +13 -13
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +12 -7
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/command-help.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/share.ts +71 -0
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/custom-models.ts +188 -0
- package/src/config/model-config-values.ts +128 -0
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-patch.ts +252 -0
- package/src/config/model-provider-discovery.ts +132 -0
- package/src/config/model-registry.ts +84 -704
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +53 -3
- package/src/debug/raw-sse-buffer.ts +20 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +75 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- package/src/edit/hashline/diff.ts +5 -1
- package/src/eval/executor-base.ts +39 -6
- package/src/eval/jl/executor.ts +82 -371
- package/src/eval/kernel-session-registry.ts +398 -0
- package/src/eval/py/executor.ts +53 -261
- package/src/eval/rb/executor.ts +36 -279
- package/src/exec/non-interactive-env.ts +1 -0
- package/src/export/share.ts +2 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +78 -14
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +12 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- package/src/lsp/diagnostics.ts +516 -0
- package/src/lsp/index.ts +20 -2819
- package/src/lsp/servers.ts +296 -0
- package/src/lsp/tool.ts +1352 -0
- package/src/lsp/workspace-diagnostics.ts +170 -0
- package/src/lsp/writethrough.ts +561 -0
- package/src/main.ts +3 -2
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +15 -4
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +2 -2
- package/src/modes/components/agent-hub-renderer.ts +3 -7
- package/src/modes/components/agent-hub.ts +5 -0
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +6 -4
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +8 -2
- package/src/modes/controllers/event-controller.ts +15 -15
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/theme/color.ts +133 -0
- package/src/modes/theme/loader.ts +178 -0
- package/src/modes/theme/schema.ts +263 -0
- package/src/modes/theme/symbols.ts +1000 -0
- package/src/modes/theme/theme-class.ts +611 -0
- package/src/modes/theme/theme.ts +27 -2453
- package/src/modes/theme/tui-adapters.ts +279 -0
- package/src/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/registry/agent-registry.ts +2 -2
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +51 -6
- package/src/secrets/message-transform.ts +287 -0
- package/src/secrets/obfuscator.ts +39 -1303
- package/src/secrets/placeholder-scan.ts +506 -0
- package/src/secrets/placeholder.ts +309 -0
- package/src/secrets/replacement.ts +216 -0
- package/src/session/agent-session-events.ts +2 -2
- package/src/session/agent-session-types.ts +6 -0
- package/src/session/agent-session.ts +146 -21
- package/src/session/indexed-session-storage.ts +7 -2
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-advisors.ts +32 -34
- package/src/session/session-context.ts +3 -5
- package/src/session/session-handoff.ts +39 -16
- package/src/session/session-manager.ts +67 -3
- package/src/session/session-provider-boundary.ts +3 -6
- package/src/session/session-tools.ts +37 -3
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +115 -84
- package/src/slash-commands/builtin-collaboration.ts +504 -0
- package/src/slash-commands/builtin-completions.ts +291 -0
- package/src/slash-commands/builtin-control.ts +78 -0
- package/src/slash-commands/builtin-lifecycle.ts +506 -0
- package/src/slash-commands/builtin-marketplace.ts +567 -0
- package/src/slash-commands/builtin-modes.ts +518 -0
- package/src/slash-commands/builtin-registry.ts +21 -2996
- package/src/slash-commands/builtin-session.ts +592 -0
- package/src/task/executor.ts +17 -14
- package/src/task/index.ts +21 -6
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +10 -11
- package/src/tools/debug.ts +1 -1
- package/src/tools/gh-common.ts +288 -0
- package/src/tools/gh-pr-checkout.ts +679 -0
- package/src/tools/gh-pr-diff.ts +473 -0
- package/src/tools/gh-run-watch.ts +1015 -0
- package/src/tools/gh-search.ts +500 -0
- package/src/tools/gh-types.ts +379 -0
- package/src/tools/gh-view.ts +612 -0
- package/src/tools/gh.ts +109 -3821
- package/src/tools/jtd-to-json-schema.ts +123 -21
- package/src/tools/read-archive.ts +209 -0
- package/src/tools/read-format.ts +593 -0
- package/src/tools/read-path-resolution.ts +36 -0
- package/src/tools/read-pdf-images.ts +250 -0
- package/src/tools/read-renderer.ts +289 -0
- package/src/tools/read-selector.ts +84 -0
- package/src/tools/read-sqlite.ts +215 -0
- package/src/tools/read-summary.ts +199 -0
- package/src/tools/read.ts +82 -1824
- package/src/tools/shell-tokenize.ts +99 -1
- package/src/utils/changelog.ts +1 -1
- package/src/utils/title-generator.ts +3 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/zai.ts +12 -3
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @see https://datatracker.ietf.org/doc/html/rfc8927
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { isRecord } from "@oh-my-pi/pi-utils";
|
|
11
12
|
import type { JTDPrimitive } from "./jtd-utils.js";
|
|
12
13
|
import {
|
|
13
14
|
isJTDDiscriminator,
|
|
@@ -134,6 +135,18 @@ function convertSchema(schema: unknown): unknown {
|
|
|
134
135
|
return {};
|
|
135
136
|
}
|
|
136
137
|
|
|
138
|
+
const jtdOnlyPrimitiveTypes: Record<string, true> = {
|
|
139
|
+
timestamp: true,
|
|
140
|
+
float32: true,
|
|
141
|
+
float64: true,
|
|
142
|
+
int8: true,
|
|
143
|
+
uint8: true,
|
|
144
|
+
int16: true,
|
|
145
|
+
uint16: true,
|
|
146
|
+
int32: true,
|
|
147
|
+
uint32: true,
|
|
148
|
+
};
|
|
149
|
+
|
|
137
150
|
/**
|
|
138
151
|
* Detect if a schema is JTD format (vs JSON Schema).
|
|
139
152
|
*
|
|
@@ -155,11 +168,8 @@ export function isJTDSchema(schema: unknown): boolean {
|
|
|
155
168
|
if ("ref" in obj) return true;
|
|
156
169
|
|
|
157
170
|
// JTD type primitives (JSON Schema doesn't have int32, float64, etc.)
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
if (jtdPrimitives.includes(obj.type as string)) {
|
|
161
|
-
return true;
|
|
162
|
-
}
|
|
171
|
+
if (typeof obj.type === "string" && Object.hasOwn(jtdOnlyPrimitiveTypes, obj.type)) {
|
|
172
|
+
return true;
|
|
163
173
|
}
|
|
164
174
|
|
|
165
175
|
// JTD properties form without type: "object" (JSON Schema requires it)
|
|
@@ -170,36 +180,128 @@ export function isJTDSchema(schema: unknown): boolean {
|
|
|
170
180
|
return false;
|
|
171
181
|
}
|
|
172
182
|
|
|
173
|
-
function
|
|
174
|
-
if (schema
|
|
175
|
-
|
|
183
|
+
function isUnambiguousJTDSchema(schema: unknown): boolean {
|
|
184
|
+
if (!isRecord(schema)) return false;
|
|
185
|
+
|
|
186
|
+
if (isRecord(schema.elements) || isRecord(schema.values) || isRecord(schema.optionalProperties)) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
if (typeof schema.ref === "string") return true;
|
|
190
|
+
if (typeof schema.type === "string" && Object.hasOwn(jtdOnlyPrimitiveTypes, schema.type)) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (typeof schema.discriminator !== "string" || !isRecord(schema.mapping)) {
|
|
194
|
+
return false;
|
|
176
195
|
}
|
|
177
196
|
|
|
178
|
-
|
|
179
|
-
|
|
197
|
+
for (const key in schema.mapping) {
|
|
198
|
+
if (!Object.hasOwn(schema.mapping, key)) continue;
|
|
199
|
+
const mapping = schema.mapping[key];
|
|
200
|
+
if (!isRecord(mapping)) return false;
|
|
201
|
+
|
|
202
|
+
let hasSchemaProperties = false;
|
|
203
|
+
if (Object.hasOwn(mapping, "properties")) {
|
|
204
|
+
if (!isRecord(mapping.properties)) return false;
|
|
205
|
+
hasSchemaProperties = true;
|
|
206
|
+
}
|
|
207
|
+
if (Object.hasOwn(mapping, "optionalProperties")) {
|
|
208
|
+
if (!isRecord(mapping.optionalProperties)) return false;
|
|
209
|
+
hasSchemaProperties = true;
|
|
210
|
+
}
|
|
211
|
+
if (!hasSchemaProperties) return false;
|
|
180
212
|
}
|
|
181
213
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function normalizeJsonSchemaArray(value: unknown): unknown {
|
|
218
|
+
if (!Array.isArray(value)) return value;
|
|
219
|
+
|
|
220
|
+
let normalized: unknown[] | undefined;
|
|
221
|
+
for (let index = 0; index < value.length; index++) {
|
|
222
|
+
const item = value[index];
|
|
223
|
+
const converted = normalizeJsonSchemaNode(item);
|
|
224
|
+
if (converted === item) continue;
|
|
225
|
+
normalized ??= value.slice();
|
|
226
|
+
normalized[index] = converted;
|
|
227
|
+
}
|
|
228
|
+
return normalized ?? value;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function normalizeJsonSchemaMap(value: unknown): unknown {
|
|
232
|
+
if (!isRecord(value)) return value;
|
|
233
|
+
|
|
234
|
+
let normalized: Record<string, unknown> | undefined;
|
|
235
|
+
for (const key in value) {
|
|
236
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
237
|
+
const item = value[key];
|
|
238
|
+
const converted = normalizeJsonSchemaNode(item);
|
|
239
|
+
if (converted === item) continue;
|
|
240
|
+
normalized ??= { ...value };
|
|
241
|
+
normalized[key] = converted;
|
|
188
242
|
}
|
|
243
|
+
return normalized ?? value;
|
|
244
|
+
}
|
|
189
245
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
246
|
+
function normalizeJsonSchemaNode(schema: unknown): unknown {
|
|
247
|
+
if (!isRecord(schema)) return schema;
|
|
248
|
+
if (isUnambiguousJTDSchema(schema)) return convertSchema(schema);
|
|
249
|
+
|
|
250
|
+
let normalized: Record<string, unknown> | undefined;
|
|
251
|
+
for (const key in schema) {
|
|
252
|
+
if (!Object.hasOwn(schema, key)) continue;
|
|
253
|
+
|
|
254
|
+
const value = schema[key];
|
|
255
|
+
let converted: unknown;
|
|
256
|
+
switch (key) {
|
|
257
|
+
case "not":
|
|
258
|
+
case "if":
|
|
259
|
+
case "then":
|
|
260
|
+
case "else":
|
|
261
|
+
case "items":
|
|
262
|
+
case "contains":
|
|
263
|
+
case "propertyNames":
|
|
264
|
+
case "additionalProperties":
|
|
265
|
+
case "unevaluatedProperties":
|
|
266
|
+
case "unevaluatedItems":
|
|
267
|
+
case "contentSchema":
|
|
268
|
+
converted = normalizeJsonSchemaNode(value);
|
|
269
|
+
break;
|
|
270
|
+
case "allOf":
|
|
271
|
+
case "anyOf":
|
|
272
|
+
case "oneOf":
|
|
273
|
+
case "prefixItems":
|
|
274
|
+
converted = normalizeJsonSchemaArray(value);
|
|
275
|
+
break;
|
|
276
|
+
case "properties":
|
|
277
|
+
case "patternProperties":
|
|
278
|
+
case "$defs":
|
|
279
|
+
case "definitions":
|
|
280
|
+
case "dependentSchemas":
|
|
281
|
+
converted = normalizeJsonSchemaMap(value);
|
|
282
|
+
break;
|
|
283
|
+
default:
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (converted === value) continue;
|
|
288
|
+
normalized ??= { ...schema };
|
|
289
|
+
normalized[key] = converted;
|
|
193
290
|
}
|
|
194
291
|
|
|
195
|
-
return normalized;
|
|
292
|
+
return normalized ?? schema;
|
|
196
293
|
}
|
|
294
|
+
|
|
197
295
|
/**
|
|
198
296
|
* Convert JTD schema to JSON Schema.
|
|
199
297
|
* If already JSON Schema, returns as-is.
|
|
200
298
|
*/
|
|
201
299
|
export function jtdToJsonSchema(schema: unknown): unknown {
|
|
202
|
-
|
|
300
|
+
if (isJTDSchema(schema)) {
|
|
301
|
+
// convertSchema is recursive; re-walking its JSON Schema output caused #1345.
|
|
302
|
+
return convertSchema(schema);
|
|
303
|
+
}
|
|
304
|
+
return normalizeJsonSchemaNode(schema);
|
|
203
305
|
}
|
|
204
306
|
|
|
205
307
|
/**
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { TextContent } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { ToolSession } from "../sdk";
|
|
4
|
+
import { truncateHead } from "../session/streaming-output";
|
|
5
|
+
import { type ArchiveReader, formatArchiveEntryLines, openArchive, parseArchivePathCandidates } from "../utils/zip";
|
|
6
|
+
import { applyListLimit } from "./list-limit";
|
|
7
|
+
import { resolveReadPath } from "./path-utils";
|
|
8
|
+
import type { ReadToolDetails } from "./read";
|
|
9
|
+
import {
|
|
10
|
+
buildInMemoryMultiRangeResult,
|
|
11
|
+
buildInMemoryTextResult,
|
|
12
|
+
decodeUtf8Text,
|
|
13
|
+
markMarkdownContentType,
|
|
14
|
+
prependSuffixResolutionNotice,
|
|
15
|
+
} from "./read-format";
|
|
16
|
+
import {
|
|
17
|
+
findSuffixMatchCached,
|
|
18
|
+
isNotFoundError,
|
|
19
|
+
isRemoteMountPath,
|
|
20
|
+
type SuffixMatchCache,
|
|
21
|
+
} from "./read-path-resolution";
|
|
22
|
+
import { isMultiRange, isRawSelector, type ParsedSelector, parseSel, selToOffsetLimit } from "./read-selector";
|
|
23
|
+
import { formatBytes } from "./render-utils";
|
|
24
|
+
import { ToolError, throwIfAborted } from "./tool-errors";
|
|
25
|
+
import { toolResult } from "./tool-result";
|
|
26
|
+
|
|
27
|
+
interface ResolvedArchiveReadPath {
|
|
28
|
+
absolutePath: string;
|
|
29
|
+
archiveSubPath: string;
|
|
30
|
+
suffixResolution?: { from: string; to: string };
|
|
31
|
+
}
|
|
32
|
+
export async function resolveArchiveReadPath(
|
|
33
|
+
session: ToolSession,
|
|
34
|
+
readPath: string,
|
|
35
|
+
suffixCache: SuffixMatchCache,
|
|
36
|
+
signal?: AbortSignal,
|
|
37
|
+
): Promise<ResolvedArchiveReadPath | null> {
|
|
38
|
+
const candidates = parseArchivePathCandidates(readPath);
|
|
39
|
+
for (const candidate of candidates) {
|
|
40
|
+
let absolutePath = resolveReadPath(candidate.archivePath, session.cwd);
|
|
41
|
+
let suffixResolution: { from: string; to: string } | undefined;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const stat = await Bun.file(absolutePath).stat();
|
|
45
|
+
if (stat.isDirectory()) continue;
|
|
46
|
+
return {
|
|
47
|
+
absolutePath,
|
|
48
|
+
archiveSubPath: candidate.archivePath === readPath ? "" : candidate.subPath,
|
|
49
|
+
suffixResolution,
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (!isNotFoundError(error) || isRemoteMountPath(absolutePath)) continue;
|
|
53
|
+
|
|
54
|
+
const suffixMatch = await findSuffixMatchCached(session, suffixCache, candidate.archivePath, signal);
|
|
55
|
+
if (!suffixMatch) continue;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const retryStat = await Bun.file(suffixMatch.absolutePath).stat();
|
|
59
|
+
if (retryStat.isDirectory()) continue;
|
|
60
|
+
|
|
61
|
+
absolutePath = suffixMatch.absolutePath;
|
|
62
|
+
suffixResolution = { from: candidate.archivePath, to: suffixMatch.displayPath };
|
|
63
|
+
return {
|
|
64
|
+
absolutePath,
|
|
65
|
+
archiveSubPath: candidate.archivePath === readPath ? "" : candidate.subPath,
|
|
66
|
+
suffixResolution,
|
|
67
|
+
};
|
|
68
|
+
} catch (retryError) {
|
|
69
|
+
if (!isNotFoundError(retryError)) {
|
|
70
|
+
throw retryError;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
async function readArchiveDirectory(
|
|
79
|
+
archive: ArchiveReader,
|
|
80
|
+
archivePath: string,
|
|
81
|
+
subPath: string,
|
|
82
|
+
offset: number | undefined,
|
|
83
|
+
limit: number | undefined,
|
|
84
|
+
details: ReadToolDetails,
|
|
85
|
+
signal?: AbortSignal,
|
|
86
|
+
): Promise<AgentToolResult<ReadToolDetails>> {
|
|
87
|
+
const DEFAULT_LIMIT = 500;
|
|
88
|
+
const effectiveLimit = limit ?? DEFAULT_LIMIT;
|
|
89
|
+
const allEntries = archive.listDirectory(subPath);
|
|
90
|
+
// `offset` is 1-indexed (line-selector semantics): `a.zip:dir:50` starts
|
|
91
|
+
// the listing at the 50th entry instead of being silently ignored.
|
|
92
|
+
const entries = offset !== undefined && offset > 1 ? allEntries.slice(offset - 1) : allEntries;
|
|
93
|
+
|
|
94
|
+
const listLimit = applyListLimit(entries, { limit: effectiveLimit });
|
|
95
|
+
const limitedEntries = listLimit.items;
|
|
96
|
+
const limitMeta = listLimit.meta;
|
|
97
|
+
|
|
98
|
+
for (let index = 0; index < limitedEntries.length; index++) {
|
|
99
|
+
throwIfAborted(signal);
|
|
100
|
+
}
|
|
101
|
+
const results = formatArchiveEntryLines(limitedEntries);
|
|
102
|
+
|
|
103
|
+
const output = results.length > 0 ? results.join("\n") : "(empty archive directory)";
|
|
104
|
+
const text = prependSuffixResolutionNotice(output, details.suffixResolution);
|
|
105
|
+
const truncation = truncateHead(text, { maxLines: Number.MAX_SAFE_INTEGER });
|
|
106
|
+
const directoryDetails: ReadToolDetails = { ...details, isDirectory: true };
|
|
107
|
+
const resultBuilder = toolResult<ReadToolDetails>(directoryDetails).text(truncation.content);
|
|
108
|
+
resultBuilder.sourcePath(archivePath).limits({ resultLimit: limitMeta.resultLimit?.reached });
|
|
109
|
+
if (truncation.truncated) {
|
|
110
|
+
directoryDetails.truncation = truncation;
|
|
111
|
+
resultBuilder.truncation(truncation, { direction: "head" });
|
|
112
|
+
}
|
|
113
|
+
return resultBuilder.done();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function readArchive(
|
|
117
|
+
session: ToolSession,
|
|
118
|
+
readPath: string,
|
|
119
|
+
parsedSel: ParsedSelector,
|
|
120
|
+
resolvedArchivePath: ResolvedArchiveReadPath,
|
|
121
|
+
signal?: AbortSignal,
|
|
122
|
+
): Promise<AgentToolResult<ReadToolDetails>> {
|
|
123
|
+
throwIfAborted(signal);
|
|
124
|
+
const archive = await openArchive(resolvedArchivePath.absolutePath);
|
|
125
|
+
throwIfAborted(signal);
|
|
126
|
+
|
|
127
|
+
const details: ReadToolDetails = markMarkdownContentType(
|
|
128
|
+
session,
|
|
129
|
+
{
|
|
130
|
+
resolvedPath: resolvedArchivePath.absolutePath,
|
|
131
|
+
suffixResolution: resolvedArchivePath.suffixResolution,
|
|
132
|
+
},
|
|
133
|
+
resolvedArchivePath.archiveSubPath,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
let archiveSubPath = resolvedArchivePath.archiveSubPath;
|
|
137
|
+
let sel = parsedSel;
|
|
138
|
+
let node = archive.getNode(archiveSubPath);
|
|
139
|
+
if (!node && archiveSubPath) {
|
|
140
|
+
// `archive.zip:500` / `archive.zip:raw`: the whole subPath is a
|
|
141
|
+
// selector on the archive root, not a member name. Member names take
|
|
142
|
+
// precedence (getNode above); fall back to root + selector.
|
|
143
|
+
const wholeSel = parseSel(archiveSubPath);
|
|
144
|
+
if (wholeSel.kind !== "none") {
|
|
145
|
+
node = archive.getNode("");
|
|
146
|
+
archiveSubPath = "";
|
|
147
|
+
sel = wholeSel;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!node) {
|
|
151
|
+
throw new ToolError(`Path '${readPath}' not found inside archive`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (node.isDirectory) {
|
|
155
|
+
if (isMultiRange(sel)) {
|
|
156
|
+
throw new ToolError("Multi-range line selectors are not supported for archive directory listings.");
|
|
157
|
+
}
|
|
158
|
+
const { offset, limit } = selToOffsetLimit(sel);
|
|
159
|
+
return readArchiveDirectory(
|
|
160
|
+
archive,
|
|
161
|
+
resolvedArchivePath.absolutePath,
|
|
162
|
+
archiveSubPath,
|
|
163
|
+
offset,
|
|
164
|
+
limit,
|
|
165
|
+
details,
|
|
166
|
+
signal,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const entry = await archive.readFile(archiveSubPath);
|
|
171
|
+
const text = decodeUtf8Text(entry.bytes);
|
|
172
|
+
if (text === null) {
|
|
173
|
+
return toolResult<ReadToolDetails>(details)
|
|
174
|
+
.text(
|
|
175
|
+
prependSuffixResolutionNotice(
|
|
176
|
+
`[Cannot read binary archive entry '${entry.path}' (${formatBytes(entry.size)})]`,
|
|
177
|
+
resolvedArchivePath.suffixResolution,
|
|
178
|
+
),
|
|
179
|
+
)
|
|
180
|
+
.sourcePath(resolvedArchivePath.absolutePath)
|
|
181
|
+
.done();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Archive members are immutable: there is no edit path for bytes inside
|
|
185
|
+
// an archive, and a hashline tag keyed to the archive file would invite
|
|
186
|
+
// (and fail) edits while clobbering sibling members' snapshots.
|
|
187
|
+
const raw = isRawSelector(sel);
|
|
188
|
+
const result =
|
|
189
|
+
isMultiRange(sel) && sel.kind === "lines"
|
|
190
|
+
? buildInMemoryMultiRangeResult(session, text, sel.ranges, {
|
|
191
|
+
details,
|
|
192
|
+
sourcePath: resolvedArchivePath.absolutePath,
|
|
193
|
+
entityLabel: "archive entry",
|
|
194
|
+
raw,
|
|
195
|
+
immutable: true,
|
|
196
|
+
})
|
|
197
|
+
: buildInMemoryTextResult(session, text, selToOffsetLimit(sel).offset, selToOffsetLimit(sel).limit, {
|
|
198
|
+
details,
|
|
199
|
+
sourcePath: resolvedArchivePath.absolutePath,
|
|
200
|
+
entityLabel: "archive entry",
|
|
201
|
+
raw,
|
|
202
|
+
immutable: true,
|
|
203
|
+
});
|
|
204
|
+
const firstText = result.content.find((content): content is TextContent => content.type === "text");
|
|
205
|
+
if (firstText) {
|
|
206
|
+
firstText.text = prependSuffixResolutionNotice(firstText.text, resolvedArchivePath.suffixResolution);
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|