@oh-my-pi/pi-coding-agent 15.7.2 → 15.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.
- package/CHANGELOG.md +114 -6
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/extension-flags.d.ts +36 -0
- package/dist/types/config/config-file.d.ts +4 -0
- package/dist/types/config/file-lock.d.ts +23 -0
- package/dist/types/config/keybindings.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +69 -65
- package/dist/types/edit/hashline/diff.d.ts +3 -3
- package/dist/types/eval/__tests__/agent-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/budget-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/idle-timeout.test.d.ts +1 -0
- package/dist/types/eval/agent-bridge.d.ts +25 -0
- package/dist/types/eval/backend.d.ts +17 -2
- package/dist/types/eval/budget-bridge.d.ts +29 -0
- package/dist/types/eval/idle-timeout.d.ts +28 -0
- package/dist/types/eval/js/executor.d.ts +8 -0
- package/dist/types/eval/js/tool-bridge.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +13 -0
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/plugins/git-url.d.ts +11 -1
- package/dist/types/extensibility/plugins/manager.d.ts +12 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/local-protocol.d.ts +19 -9
- package/dist/types/internal-urls/types.d.ts +14 -0
- package/dist/types/lsp/client.d.ts +3 -0
- package/dist/types/mcp/manager.d.ts +14 -5
- package/dist/types/memory-backend/index.d.ts +1 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +3 -3
- package/dist/types/mnemopi/backend.d.ts +4 -0
- package/dist/types/mnemopi/config.d.ts +29 -0
- package/dist/types/mnemopi/state.d.ts +72 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -2
- package/dist/types/modes/components/omfg-panel.d.ts +19 -0
- package/dist/types/modes/controllers/command-controller.d.ts +6 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/controllers/omfg-controller.d.ts +10 -0
- package/dist/types/modes/controllers/omfg-rule.d.ts +26 -0
- package/dist/types/modes/gradient-highlight.d.ts +5 -1
- package/dist/types/modes/interactive-mode.d.ts +7 -3
- package/dist/types/modes/magic-keywords.d.ts +14 -0
- package/dist/types/modes/markdown-prose.d.ts +27 -0
- package/dist/types/modes/orchestrate.d.ts +7 -2
- package/dist/types/modes/shared.d.ts +1 -1
- package/dist/types/modes/turn-budget.d.ts +18 -0
- package/dist/types/modes/types.d.ts +7 -3
- package/dist/types/modes/ultrathink.d.ts +7 -2
- package/dist/types/modes/workflow.d.ts +15 -0
- package/dist/types/sdk.d.ts +13 -3
- package/dist/types/session/agent-session.d.ts +36 -17
- package/dist/types/session/session-manager.d.ts +18 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/session/shake-types.d.ts +24 -0
- package/dist/types/task/executor.d.ts +2 -2
- package/dist/types/task/repair-args.d.ts +52 -0
- package/dist/types/tiny/models.d.ts +1 -1
- package/dist/types/tiny/title-client.d.ts +28 -2
- package/dist/types/tiny/title-protocol.d.ts +8 -5
- package/dist/types/tools/find.d.ts +1 -1
- package/dist/types/tools/index.d.ts +19 -3
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/dist/types/tools/path-utils.d.ts +7 -0
- package/dist/types/tui/output-block.d.ts +7 -7
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/package.json +10 -10
- package/scripts/build-binary.ts +0 -1
- package/src/autoresearch/tools/run-experiment.ts +45 -113
- package/src/cli/args.ts +39 -16
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/plugin-cli.ts +11 -2
- package/src/cli.ts +59 -0
- package/src/config/config-file.ts +98 -13
- package/src/config/file-lock.ts +60 -17
- package/src/config/keybindings.ts +78 -27
- package/src/config/model-registry.ts +7 -1
- package/src/config/settings-schema.ts +73 -67
- package/src/config/settings.ts +22 -0
- package/src/edit/hashline/diff.ts +81 -24
- package/src/edit/renderer.ts +16 -12
- package/src/eval/__tests__/agent-bridge.test.ts +433 -0
- package/src/eval/__tests__/budget-bridge.test.ts +69 -0
- package/src/eval/__tests__/idle-timeout.test.ts +66 -0
- package/src/eval/__tests__/shared-executors.test.ts +21 -0
- package/src/eval/agent-bridge.ts +295 -0
- package/src/eval/backend.ts +17 -2
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/idle-timeout.ts +80 -0
- package/src/eval/js/executor.ts +35 -7
- package/src/eval/js/index.ts +2 -1
- package/src/eval/js/shared/prelude.txt +85 -1
- package/src/eval/js/tool-bridge.ts +9 -0
- package/src/eval/py/executor.ts +41 -14
- package/src/eval/py/index.ts +2 -1
- package/src/eval/py/prelude.py +132 -1
- package/src/exec/bash-executor.ts +2 -3
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/runner.ts +12 -2
- package/src/extensibility/plugins/git-url.ts +90 -4
- package/src/extensibility/plugins/manager.ts +103 -7
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +88 -88
- package/src/internal-urls/local-protocol.ts +23 -11
- package/src/internal-urls/types.ts +15 -0
- package/src/lsp/client.ts +28 -5
- package/src/main.ts +44 -55
- package/src/mcp/manager.ts +87 -4
- package/src/memory-backend/index.ts +1 -1
- package/src/memory-backend/resolve.ts +3 -3
- package/src/memory-backend/types.ts +3 -3
- package/src/{mnemosyne → mnemopi}/backend.ts +70 -70
- package/src/{mnemosyne → mnemopi}/config.ts +36 -36
- package/src/{mnemosyne → mnemopi}/state.ts +67 -67
- package/src/modes/components/agent-dashboard.ts +6 -6
- package/src/modes/components/custom-editor.ts +4 -11
- package/src/modes/components/extensions/state-manager.ts +3 -4
- package/src/modes/components/footer.ts +8 -9
- package/src/modes/components/hook-selector.ts +86 -20
- package/src/modes/components/oauth-selector.ts +93 -21
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/settings-defs.ts +2 -2
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +38 -19
- package/src/modes/components/tree-selector.ts +4 -3
- package/src/modes/components/user-message-selector.ts +94 -19
- package/src/modes/components/user-message.ts +8 -1
- package/src/modes/controllers/command-controller.ts +25 -0
- package/src/modes/controllers/event-controller.ts +68 -3
- package/src/modes/controllers/input-controller.ts +14 -11
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/omfg-controller.ts +283 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/gradient-highlight.ts +23 -6
- package/src/modes/interactive-mode.ts +41 -7
- package/src/modes/magic-keywords.ts +20 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/orchestrate.ts +17 -11
- package/src/modes/shared.ts +3 -11
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/ultrathink.ts +16 -10
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/workflow.ts +42 -0
- package/src/prompts/system/omfg-user.md +51 -0
- package/src/prompts/system/project-prompt.md +3 -2
- package/src/prompts/system/subagent-system-prompt.md +12 -8
- package/src/prompts/system/system-prompt.md +9 -6
- package/src/prompts/system/workflow-notice.md +70 -0
- package/src/prompts/tools/eval.md +13 -1
- package/src/prompts/tools/memory-edit.md +1 -1
- package/src/sdk.ts +63 -33
- package/src/session/agent-session.ts +290 -55
- package/src/session/session-manager.ts +32 -0
- package/src/session/session-storage.ts +68 -8
- package/src/session/shake-types.ts +43 -0
- package/src/slash-commands/builtin-registry.ts +39 -16
- package/src/task/executor.ts +17 -7
- package/src/task/index.ts +9 -8
- package/src/task/repair-args.ts +117 -0
- package/src/tiny/models.ts +2 -2
- package/src/tiny/title-client.ts +133 -43
- package/src/tiny/title-protocol.ts +10 -5
- package/src/tiny/worker.ts +3 -46
- package/src/tools/ast-edit.ts +3 -0
- package/src/tools/ast-grep.ts +3 -0
- package/src/tools/eval.ts +202 -26
- package/src/tools/find.ts +20 -6
- package/src/tools/gh.ts +1 -0
- package/src/tools/grouped-file-output.ts +9 -2
- package/src/tools/index.ts +17 -5
- package/src/tools/memory-edit.ts +4 -4
- package/src/tools/memory-recall.ts +5 -5
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +4 -4
- package/src/tools/path-utils.ts +13 -2
- package/src/tools/read.ts +1 -0
- package/src/tools/render-utils.ts +2 -1
- package/src/tools/search.ts +491 -76
- package/src/tui/output-block.ts +37 -75
- package/src/utils/git.ts +9 -3
- package/dist/types/mnemosyne/backend.d.ts +0 -4
- package/dist/types/mnemosyne/config.d.ts +0 -29
- package/dist/types/mnemosyne/state.d.ts +0 -72
- /package/dist/types/{mnemosyne → mnemopi}/index.d.ts +0 -0
- /package/src/{mnemosyne → mnemopi}/index.ts +0 -0
package/src/tools/eval.ts
CHANGED
|
@@ -2,13 +2,16 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { Markdown, Text } from "@oh-my-pi/pi-tui";
|
|
5
|
-
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
+
import { formatNumber, prompt } from "@oh-my-pi/pi-utils";
|
|
6
6
|
import * as z from "zod/v4";
|
|
7
|
+
import { settings } from "../config/settings";
|
|
7
8
|
import { jsBackend, pythonBackend } from "../eval";
|
|
8
|
-
import type { ExecutorBackend } from "../eval/backend";
|
|
9
|
+
import type { ExecutorBackend, ExecutorBackendResult } from "../eval/backend";
|
|
10
|
+
import { IdleTimeout } from "../eval/idle-timeout";
|
|
9
11
|
import { defaultEvalSessionId } from "../eval/session-id";
|
|
10
12
|
import type { EvalCellResult, EvalDisplayOutput, EvalLanguage, EvalStatusEvent, EvalToolDetails } from "../eval/types";
|
|
11
13
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
14
|
+
import { formatContextUsage } from "../modes/components/status-line/context-thresholds";
|
|
12
15
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
13
16
|
import { shimmerEnabled } from "../modes/theme/shimmer";
|
|
14
17
|
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
@@ -33,7 +36,16 @@ import {
|
|
|
33
36
|
resolveOutputSinkHeadBytes,
|
|
34
37
|
stripOutputNotice,
|
|
35
38
|
} from "./output-meta";
|
|
36
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
formatBadge,
|
|
41
|
+
formatDuration,
|
|
42
|
+
formatStatusIcon,
|
|
43
|
+
formatTitle,
|
|
44
|
+
replaceTabs,
|
|
45
|
+
shortenPath,
|
|
46
|
+
truncateToWidth,
|
|
47
|
+
wrapBrackets,
|
|
48
|
+
} from "./render-utils";
|
|
37
49
|
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
38
50
|
import { toolResult } from "./tool-result";
|
|
39
51
|
import { clampTimeout } from "./tool-timeouts";
|
|
@@ -335,12 +347,20 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
335
347
|
for (let i = 0; i < cells.length; i++) {
|
|
336
348
|
const cell = cells[i];
|
|
337
349
|
const backend = cell.resolved.backend;
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
350
|
+
// The per-cell `timeout` is an *inactivity* budget, not a hard
|
|
351
|
+
// wall-clock cap: it bounds the gap between progress signals
|
|
352
|
+
// (status events — agent() updates, log()/phase(), tool-bridge
|
|
353
|
+
// activity), so a long fanout that keeps reporting progress runs to
|
|
354
|
+
// completion while a genuinely stalled cell (no progress for the
|
|
355
|
+
// whole window) is still interrupted. Raw stdout deliberately does
|
|
356
|
+
// NOT re-arm it, so pure-compute runaway loops stay bounded. The
|
|
357
|
+
// watchdog drives `combinedSignal`; we pass no wall-clock deadline
|
|
358
|
+
// downstream so the backends never arm a competing fixed timer.
|
|
359
|
+
const idleTimeoutMs = timeoutSecondsFromMs(cell.timeoutMs) * 1000;
|
|
360
|
+
const idle = new IdleTimeout(idleTimeoutMs);
|
|
341
361
|
const combinedSignal = signal
|
|
342
|
-
? AbortSignal.any([signal,
|
|
343
|
-
: AbortSignal.any([
|
|
362
|
+
? AbortSignal.any([signal, idle.signal, sessionAbortController.signal])
|
|
363
|
+
: AbortSignal.any([idle.signal, sessionAbortController.signal]);
|
|
344
364
|
|
|
345
365
|
const cellResult = cellResults[i];
|
|
346
366
|
cellResult.status = "running";
|
|
@@ -351,21 +371,32 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
351
371
|
pushUpdate();
|
|
352
372
|
|
|
353
373
|
const startTime = Date.now();
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
374
|
+
let result: ExecutorBackendResult;
|
|
375
|
+
try {
|
|
376
|
+
result = await backend.execute(cell.code, {
|
|
377
|
+
cwd: session.cwd,
|
|
378
|
+
sessionId,
|
|
379
|
+
sessionFile: sessionFile ?? undefined,
|
|
380
|
+
kernelOwnerId,
|
|
381
|
+
signal: combinedSignal,
|
|
382
|
+
session,
|
|
383
|
+
idleTimeoutMs,
|
|
384
|
+
reset: cell.reset,
|
|
385
|
+
artifactPath,
|
|
386
|
+
artifactId,
|
|
387
|
+
onChunk: chunk => {
|
|
388
|
+
outputSink!.push(chunk);
|
|
389
|
+
},
|
|
390
|
+
onStatus: event => {
|
|
391
|
+
idle.bump();
|
|
392
|
+
cellResult.statusEvents ??= [];
|
|
393
|
+
upsertStatusEvent(cellResult.statusEvents, event);
|
|
394
|
+
pushUpdate();
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
} finally {
|
|
398
|
+
idle.dispose();
|
|
399
|
+
}
|
|
369
400
|
const durationMs = Date.now() - startTime;
|
|
370
401
|
|
|
371
402
|
const cellStatusEvents: EvalStatusEvent[] = [];
|
|
@@ -400,8 +431,8 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
400
431
|
}
|
|
401
432
|
}
|
|
402
433
|
if (output.type === "status") {
|
|
403
|
-
statusEvents
|
|
404
|
-
cellStatusEvents
|
|
434
|
+
upsertStatusEvent(statusEvents, output.event);
|
|
435
|
+
upsertStatusEvent(cellStatusEvents, output.event);
|
|
405
436
|
}
|
|
406
437
|
if (output.type === "markdown") {
|
|
407
438
|
cellHasMarkdown = true;
|
|
@@ -608,6 +639,137 @@ function getRenderCells(args: EvalRenderArgs | undefined): EvalRenderCell[] {
|
|
|
608
639
|
return out;
|
|
609
640
|
}
|
|
610
641
|
|
|
642
|
+
type AgentEventStatus = "pending" | "running" | "completed" | "failed" | "aborted";
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Append or replace a status event. `agent` events are progress snapshots keyed
|
|
646
|
+
* by `id`, so they coalesce in place (preserving first-seen order); every other
|
|
647
|
+
* op is a discrete action and simply appends. Keeps the persisted event list
|
|
648
|
+
* bounded even when a subagent emits hundreds of throttled progress ticks.
|
|
649
|
+
*/
|
|
650
|
+
function upsertStatusEvent(events: EvalStatusEvent[], event: EvalStatusEvent): void {
|
|
651
|
+
if (event.op === "agent" && typeof event.id === "string") {
|
|
652
|
+
const id = event.id;
|
|
653
|
+
const idx = events.findIndex(e => e.op === "agent" && e.id === id);
|
|
654
|
+
if (idx >= 0) {
|
|
655
|
+
events[idx] = event;
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
events.push(event);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function eventString(value: unknown): string | undefined {
|
|
663
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function eventNumber(value: unknown): number {
|
|
667
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function agentEventStatus(value: unknown): AgentEventStatus {
|
|
671
|
+
switch (value) {
|
|
672
|
+
case "pending":
|
|
673
|
+
case "running":
|
|
674
|
+
case "completed":
|
|
675
|
+
case "failed":
|
|
676
|
+
case "aborted":
|
|
677
|
+
return value;
|
|
678
|
+
default:
|
|
679
|
+
return "running";
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** Append the toolCount · context · cost · model stat run, mirroring the task tool. */
|
|
684
|
+
function formatAgentStats(event: EvalStatusEvent, theme: Theme): string {
|
|
685
|
+
let line = "";
|
|
686
|
+
const toolCount = eventNumber(event.toolCount);
|
|
687
|
+
if (toolCount > 0) {
|
|
688
|
+
line += `${theme.sep.dot}${theme.fg("dim", `${formatNumber(toolCount)} ${theme.icon.extensionTool}`)}`;
|
|
689
|
+
}
|
|
690
|
+
const contextTokens = eventNumber(event.contextTokens);
|
|
691
|
+
if (contextTokens > 0) {
|
|
692
|
+
const contextWindow = eventNumber(event.contextWindow);
|
|
693
|
+
const ctx =
|
|
694
|
+
contextWindow > 0
|
|
695
|
+
? formatContextUsage((contextTokens / contextWindow) * 100, contextWindow)
|
|
696
|
+
: formatNumber(contextTokens);
|
|
697
|
+
line += `${theme.sep.dot}${theme.fg("dim", ctx)}`;
|
|
698
|
+
}
|
|
699
|
+
const cost = eventNumber(event.cost);
|
|
700
|
+
if (cost > 0) {
|
|
701
|
+
line += `${theme.sep.dot}${theme.fg("statusLineCost", `$${cost.toFixed(2)}`)}`;
|
|
702
|
+
}
|
|
703
|
+
const model = eventString(event.model);
|
|
704
|
+
if (model && settings.get("task.showResolvedModelBadge")) {
|
|
705
|
+
line += `${theme.sep.dot}${theme.fg("dim", truncateToWidth(replaceTabs(model), 30))}`;
|
|
706
|
+
}
|
|
707
|
+
return line;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Render coalesced `agent()` progress as a Task-tool-style tree, one entry per
|
|
712
|
+
* subagent: a status line (icon · id · stats) plus, while running, the current
|
|
713
|
+
* tool/intent. Drawn below the cell box so progress streams live.
|
|
714
|
+
*/
|
|
715
|
+
function renderAgentProgressEvents(events: EvalStatusEvent[], theme: Theme, spinnerFrame?: number): string[] {
|
|
716
|
+
const lines: string[] = [];
|
|
717
|
+
for (let i = 0; i < events.length; i++) {
|
|
718
|
+
const event = events[i];
|
|
719
|
+
const isLast = i === events.length - 1;
|
|
720
|
+
const prefix = theme.fg("dim", isLast ? theme.tree.last : theme.tree.branch);
|
|
721
|
+
const cont = isLast ? " " : `${theme.fg("dim", theme.tree.vertical)} `;
|
|
722
|
+
|
|
723
|
+
const status = agentEventStatus(event.status);
|
|
724
|
+
const iconStatus =
|
|
725
|
+
status === "completed"
|
|
726
|
+
? "success"
|
|
727
|
+
: status === "failed"
|
|
728
|
+
? "error"
|
|
729
|
+
: status === "aborted"
|
|
730
|
+
? "aborted"
|
|
731
|
+
: status === "pending"
|
|
732
|
+
? "pending"
|
|
733
|
+
: "running";
|
|
734
|
+
const iconColor =
|
|
735
|
+
status === "completed" ? "success" : status === "failed" || status === "aborted" ? "error" : "accent";
|
|
736
|
+
const icon = formatStatusIcon(iconStatus, theme, status === "running" ? spinnerFrame : undefined);
|
|
737
|
+
|
|
738
|
+
const id = eventString(event.id) ?? "agent";
|
|
739
|
+
let line = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", theme.bold(id))}`;
|
|
740
|
+
|
|
741
|
+
if (status === "failed" || status === "aborted") {
|
|
742
|
+
line += ` ${formatBadge(status, iconColor, theme)}`;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const currentTool = eventString(event.currentTool);
|
|
746
|
+
const lastIntent = eventString(event.lastIntent);
|
|
747
|
+
if (status === "running" && !currentTool && !lastIntent) {
|
|
748
|
+
const preview = eventString(event.taskPreview);
|
|
749
|
+
if (preview) line += ` ${theme.fg("muted", truncateToWidth(replaceTabs(preview), 48))}`;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
line += formatAgentStats(event, theme);
|
|
753
|
+
if (status === "completed" || status === "failed" || status === "aborted") {
|
|
754
|
+
const durationMs = eventNumber(event.durationMs);
|
|
755
|
+
if (durationMs > 0) line += `${theme.sep.dot}${theme.fg("dim", formatDuration(durationMs))}`;
|
|
756
|
+
}
|
|
757
|
+
lines.push(line);
|
|
758
|
+
|
|
759
|
+
if (status === "running") {
|
|
760
|
+
if (currentTool) {
|
|
761
|
+
let toolLine = `${cont}${theme.tree.hook} ${theme.fg("muted", currentTool)}`;
|
|
762
|
+
const detail = lastIntent ?? eventString(event.currentToolArgs);
|
|
763
|
+
if (detail) toolLine += `: ${theme.fg("dim", truncateToWidth(replaceTabs(detail), 48))}`;
|
|
764
|
+
lines.push(toolLine);
|
|
765
|
+
} else if (lastIntent) {
|
|
766
|
+
lines.push(`${cont}${theme.tree.hook} ${theme.fg("dim", truncateToWidth(replaceTabs(lastIntent), 48))}`);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return lines;
|
|
771
|
+
}
|
|
772
|
+
|
|
611
773
|
/** Format a status event as a single line for display. */
|
|
612
774
|
function formatStatusEvent(event: EvalStatusEvent, theme: Theme): string {
|
|
613
775
|
const { op, ...data } = event;
|
|
@@ -636,6 +798,8 @@ function formatStatusEvent(event: EvalStatusEvent, theme: Theme): string {
|
|
|
636
798
|
env: "icon.package",
|
|
637
799
|
batch: "icon.package",
|
|
638
800
|
llm: "icon.package",
|
|
801
|
+
log: "icon.package",
|
|
802
|
+
phase: "icon.package",
|
|
639
803
|
};
|
|
640
804
|
|
|
641
805
|
const iconKey = opIcons[op] ?? "icon.file";
|
|
@@ -716,6 +880,12 @@ function formatStatusEvent(event: EvalStatusEvent, theme: Theme): string {
|
|
|
716
880
|
case "touch":
|
|
717
881
|
if (data.path) parts.push(shortenPath(String(data.path)));
|
|
718
882
|
break;
|
|
883
|
+
case "log":
|
|
884
|
+
parts.push(String(data.message ?? ""));
|
|
885
|
+
break;
|
|
886
|
+
case "phase":
|
|
887
|
+
parts.push(String(data.title ?? ""));
|
|
888
|
+
break;
|
|
719
889
|
default:
|
|
720
890
|
if (data.count !== undefined) {
|
|
721
891
|
parts.push(String(data.count));
|
|
@@ -963,7 +1133,10 @@ export const evalToolRenderer = {
|
|
|
963
1133
|
const lines: string[] = [];
|
|
964
1134
|
for (let i = 0; i < cellResults.length; i++) {
|
|
965
1135
|
const cell = cellResults[i];
|
|
966
|
-
const
|
|
1136
|
+
const allEvents = cell.statusEvents ?? [];
|
|
1137
|
+
const agentEvents = allEvents.filter(e => e.op === "agent");
|
|
1138
|
+
const otherEvents = agentEvents.length > 0 ? allEvents.filter(e => e.op !== "agent") : allEvents;
|
|
1139
|
+
const statusLines = renderStatusEvents(otherEvents, uiTheme, expanded);
|
|
967
1140
|
const outputContent = formatCellOutputLines(cell, expanded, previewLines, uiTheme, width);
|
|
968
1141
|
const outputLines = [...outputContent.lines];
|
|
969
1142
|
if (!expanded && outputContent.hiddenCount > 0) {
|
|
@@ -997,6 +1170,9 @@ export const evalToolRenderer = {
|
|
|
997
1170
|
uiTheme,
|
|
998
1171
|
);
|
|
999
1172
|
lines.push(...cellLines);
|
|
1173
|
+
if (agentEvents.length > 0) {
|
|
1174
|
+
lines.push(...renderAgentProgressEvents(agentEvents, uiTheme, options.spinnerFrame));
|
|
1175
|
+
}
|
|
1000
1176
|
if (i < cellResults.length - 1) {
|
|
1001
1177
|
lines.push("");
|
|
1002
1178
|
}
|
package/src/tools/find.ts
CHANGED
|
@@ -192,7 +192,12 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
192
192
|
if (hasGlobPathChars(rawPattern)) {
|
|
193
193
|
throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPattern}`);
|
|
194
194
|
}
|
|
195
|
-
const resource = await internalRouter.resolve(rawPattern
|
|
195
|
+
const resource = await internalRouter.resolve(rawPattern, {
|
|
196
|
+
cwd: this.session.cwd,
|
|
197
|
+
settings: this.session.settings,
|
|
198
|
+
signal,
|
|
199
|
+
localProtocolOptions: this.session.localProtocolOptions,
|
|
200
|
+
});
|
|
196
201
|
if (!resource.sourcePath) {
|
|
197
202
|
throw new ToolError(`Cannot find internal URL without a backing file: ${rawPattern}`);
|
|
198
203
|
}
|
|
@@ -443,10 +448,14 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
443
448
|
// =============================================================================
|
|
444
449
|
|
|
445
450
|
interface FindRenderArgs {
|
|
446
|
-
paths?: string[];
|
|
451
|
+
paths?: string | string[];
|
|
447
452
|
limit?: number;
|
|
448
453
|
}
|
|
449
454
|
|
|
455
|
+
function formatFindRenderPaths(paths: FindRenderArgs["paths"]): string | undefined {
|
|
456
|
+
return Array.isArray(paths) ? paths.join(", ") : paths;
|
|
457
|
+
}
|
|
458
|
+
|
|
450
459
|
const COLLAPSED_LIST_LIMIT = PREVIEW_LIMITS.COLLAPSED_ITEMS;
|
|
451
460
|
|
|
452
461
|
export const findToolRenderer = {
|
|
@@ -456,7 +465,7 @@ export const findToolRenderer = {
|
|
|
456
465
|
if (args.limit !== undefined) meta.push(`limit:${args.limit}`);
|
|
457
466
|
|
|
458
467
|
const text = renderStatusLine(
|
|
459
|
-
{ icon: "pending", title: "Find", description: args.paths
|
|
468
|
+
{ icon: "pending", title: "Find", description: formatFindRenderPaths(args.paths) || "*", meta },
|
|
460
469
|
uiTheme,
|
|
461
470
|
);
|
|
462
471
|
return new Text(text, 0, 0);
|
|
@@ -493,7 +502,7 @@ export const findToolRenderer = {
|
|
|
493
502
|
{
|
|
494
503
|
icon: "success",
|
|
495
504
|
title: "Find",
|
|
496
|
-
description: args?.paths
|
|
505
|
+
description: formatFindRenderPaths(args?.paths),
|
|
497
506
|
meta: [formatCount("file", lines.length)],
|
|
498
507
|
},
|
|
499
508
|
uiTheme,
|
|
@@ -528,7 +537,7 @@ export const findToolRenderer = {
|
|
|
528
537
|
|
|
529
538
|
if (fileCount === 0) {
|
|
530
539
|
const header = renderStatusLine(
|
|
531
|
-
{ icon: "warning", title: "Find", description: args?.paths
|
|
540
|
+
{ icon: "warning", title: "Find", description: formatFindRenderPaths(args?.paths), meta: ["0 files"] },
|
|
532
541
|
uiTheme,
|
|
533
542
|
);
|
|
534
543
|
const lines = [header, formatEmptyMessage("No files found", uiTheme)];
|
|
@@ -539,7 +548,12 @@ export const findToolRenderer = {
|
|
|
539
548
|
if (details?.scopePath) meta.push(`in ${details.scopePath}`);
|
|
540
549
|
if (truncated) meta.push(uiTheme.fg("warning", "truncated"));
|
|
541
550
|
const header = renderStatusLine(
|
|
542
|
-
{
|
|
551
|
+
{
|
|
552
|
+
icon: truncated ? "warning" : "success",
|
|
553
|
+
title: "Find",
|
|
554
|
+
description: formatFindRenderPaths(args?.paths),
|
|
555
|
+
meta,
|
|
556
|
+
},
|
|
543
557
|
uiTheme,
|
|
544
558
|
);
|
|
545
559
|
|
package/src/tools/gh.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
+
const URL_LIKE_PATH_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
4
|
+
|
|
5
|
+
function isUrlLikePath(filePath: string): boolean {
|
|
6
|
+
return URL_LIKE_PATH_RE.test(filePath);
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
/**
|
|
4
10
|
* One file's contribution to a grouped file output. The header itself is generated
|
|
5
11
|
* by `formatGroupedFiles` (single `#` for root files, `##` for files inside a dir);
|
|
@@ -43,7 +49,7 @@ export function formatGroupedFiles(
|
|
|
43
49
|
): GroupedFilesOutput {
|
|
44
50
|
const filesByDirectory = new Map<string, string[]>();
|
|
45
51
|
for (const filePath of files) {
|
|
46
|
-
const directory = path.dirname(filePath).replace(/\\/g, "/");
|
|
52
|
+
const directory = isUrlLikePath(filePath) ? "." : path.dirname(filePath).replace(/\\/g, "/");
|
|
47
53
|
if (!filesByDirectory.has(directory)) {
|
|
48
54
|
filesByDirectory.set(directory, []);
|
|
49
55
|
}
|
|
@@ -66,7 +72,8 @@ export function formatGroupedFiles(
|
|
|
66
72
|
const section = renderFile(filePath);
|
|
67
73
|
if (section.skip) continue;
|
|
68
74
|
pushSeparatorIfNeeded();
|
|
69
|
-
const
|
|
75
|
+
const headerName = isUrlLikePath(filePath) ? filePath : path.basename(filePath);
|
|
76
|
+
const header = `# ${headerName}${section.headerSuffix ?? ""}`;
|
|
70
77
|
model.push(header, ...section.modelLines);
|
|
71
78
|
display.push(header, ...(section.displayLines ?? section.modelLines));
|
|
72
79
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -10,8 +10,10 @@ import type { Skill } from "../extensibility/skills";
|
|
|
10
10
|
import type { GoalModeState, GoalRuntime } from "../goals";
|
|
11
11
|
import { GoalTool } from "../goals/tools/goal-tool";
|
|
12
12
|
import type { HindsightSessionState } from "../hindsight/state";
|
|
13
|
+
import type { LocalProtocolOptions } from "../internal-urls";
|
|
13
14
|
import { LspTool } from "../lsp";
|
|
14
|
-
import type {
|
|
15
|
+
import type { MCPManager } from "../mcp";
|
|
16
|
+
import type { MnemopiSessionState } from "../mnemopi/state";
|
|
15
17
|
import type { PlanModeState } from "../plan-mode/state";
|
|
16
18
|
import { type AgentRegistry, MAIN_AGENT_ID } from "../registry/agent-registry";
|
|
17
19
|
import type { ArtifactManager } from "../session/artifacts";
|
|
@@ -153,8 +155,8 @@ export interface ToolSession {
|
|
|
153
155
|
getSessionId?: () => string | null;
|
|
154
156
|
/** Get Hindsight runtime state for this agent session. */
|
|
155
157
|
getHindsightSessionState?: () => HindsightSessionState | undefined;
|
|
156
|
-
/** Get
|
|
157
|
-
|
|
158
|
+
/** Get Mnemopi runtime state for this agent session. */
|
|
159
|
+
getMnemopiSessionState?: () => MnemopiSessionState | undefined;
|
|
158
160
|
/** Agent identity used for IRC routing. Returns the registry id (e.g. "0-Main", "0-AuthLoader"). */
|
|
159
161
|
getAgentId?: () => string | null;
|
|
160
162
|
/** Look up a registered tool by name (used by the eval js backend's tool bridge). */
|
|
@@ -179,6 +181,10 @@ export interface ToolSession {
|
|
|
179
181
|
modelRegistry?: import("../config/model-registry").ModelRegistry;
|
|
180
182
|
/** Agent output manager for unique agent:// IDs across task invocations */
|
|
181
183
|
agentOutputManager?: AgentOutputManager;
|
|
184
|
+
/** MCP manager visible to subagents without relying on the process-global singleton. */
|
|
185
|
+
mcpManager?: MCPManager;
|
|
186
|
+
/** Local protocol root to propagate to nested subagents and eval-created agents. */
|
|
187
|
+
localProtocolOptions?: LocalProtocolOptions;
|
|
182
188
|
/** Settings instance for passing to subagents */
|
|
183
189
|
settings: Settings;
|
|
184
190
|
/** Plan mode state (if active) */
|
|
@@ -187,6 +193,12 @@ export interface ToolSession {
|
|
|
187
193
|
getGoalModeState?: () => GoalModeState | undefined;
|
|
188
194
|
/** Goal runtime for the active agent session. */
|
|
189
195
|
getGoalRuntime?: () => GoalRuntime | undefined;
|
|
196
|
+
/** Get cumulative session usage statistics (input/output tokens, cost). */
|
|
197
|
+
getUsageStatistics?: () => import("../session/session-manager").UsageStatistics;
|
|
198
|
+
/** Current per-turn token budget {total, spent, hard} for the eval `budget` helper. */
|
|
199
|
+
getTurnBudget?: () => { total: number | null; spent: number; hard: boolean };
|
|
200
|
+
/** Record output tokens consumed by an eval-spawned subagent toward the current turn budget. */
|
|
201
|
+
recordEvalSubagentUsage?: (output: number) => void;
|
|
190
202
|
/** Bridge to the connected client (e.g. ACP editor host). Tools should route fs/terminal/permission requests through this when available. */
|
|
191
203
|
getClientBridge?: () => ClientBridge | undefined;
|
|
192
204
|
/** Get compact conversation context for subagents (excludes tool results, system prompts) */
|
|
@@ -413,7 +425,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
413
425
|
) {
|
|
414
426
|
requestedTools.push("ast_edit");
|
|
415
427
|
}
|
|
416
|
-
if (["hindsight", "
|
|
428
|
+
if (["hindsight", "mnemopi"].includes(session.settings.get("memory.backend") ?? "")) {
|
|
417
429
|
for (const name of ["recall", "retain", "reflect"]) {
|
|
418
430
|
if (!requestedTools.includes(name)) requestedTools.push(name);
|
|
419
431
|
}
|
|
@@ -458,7 +470,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
458
470
|
return true;
|
|
459
471
|
}
|
|
460
472
|
if (name === "retain" || name === "recall" || name === "reflect") {
|
|
461
|
-
return ["hindsight", "
|
|
473
|
+
return ["hindsight", "mnemopi"].includes(session.settings.get("memory.backend") ?? "");
|
|
462
474
|
}
|
|
463
475
|
if (name === "task") {
|
|
464
476
|
const maxDepth = session.settings.get("task.maxRecursionDepth") ?? 2;
|
package/src/tools/memory-edit.ts
CHANGED
|
@@ -21,20 +21,20 @@ export class MemoryEditTool implements AgentTool<typeof memoryEditSchema> {
|
|
|
21
21
|
readonly parameters = memoryEditSchema;
|
|
22
22
|
readonly strict = true;
|
|
23
23
|
readonly loadMode = "discoverable";
|
|
24
|
-
readonly summary = "Update, forget, or invalidate
|
|
24
|
+
readonly summary = "Update, forget, or invalidate Mnemopi memories";
|
|
25
25
|
|
|
26
26
|
constructor(private readonly session: ToolSession) {}
|
|
27
27
|
|
|
28
28
|
static createIf(session: ToolSession): MemoryEditTool | null {
|
|
29
29
|
const backend = session.settings.get("memory.backend");
|
|
30
|
-
if (backend !== "
|
|
30
|
+
if (backend !== "mnemopi") return null;
|
|
31
31
|
return new MemoryEditTool(session);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
async execute(_id: string, params: MemoryEditParams): Promise<AgentToolResult> {
|
|
35
|
-
const state = this.session.
|
|
35
|
+
const state = this.session.getMnemopiSessionState?.();
|
|
36
36
|
if (!state) {
|
|
37
|
-
throw new Error("
|
|
37
|
+
throw new Error("Mnemopi backend is not initialised for this session.");
|
|
38
38
|
}
|
|
39
39
|
if (params.op === "update" && params.content === undefined && params.importance === undefined) {
|
|
40
40
|
throw new Error("memory_edit update requires content or importance.");
|
|
@@ -25,17 +25,17 @@ export class MemoryRecallTool implements AgentTool<typeof memoryRecallSchema> {
|
|
|
25
25
|
|
|
26
26
|
static createIf(session: ToolSession): MemoryRecallTool | null {
|
|
27
27
|
const backend = session.settings.get("memory.backend");
|
|
28
|
-
if (backend !== "hindsight" && backend !== "
|
|
28
|
+
if (backend !== "hindsight" && backend !== "mnemopi") return null;
|
|
29
29
|
return new MemoryRecallTool(session);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
async execute(_id: string, params: MemoryRecallParams, signal?: AbortSignal): Promise<AgentToolResult> {
|
|
33
33
|
return untilAborted(signal, async () => {
|
|
34
34
|
const backend = this.session.settings.get("memory.backend");
|
|
35
|
-
if (backend === "
|
|
36
|
-
const state = this.session.
|
|
35
|
+
if (backend === "mnemopi") {
|
|
36
|
+
const state = this.session.getMnemopiSessionState?.();
|
|
37
37
|
if (!state) {
|
|
38
|
-
throw new Error("
|
|
38
|
+
throw new Error("Mnemopi backend is not initialised for this session.");
|
|
39
39
|
}
|
|
40
40
|
try {
|
|
41
41
|
const results = state.recallResultsScoped(params.query);
|
|
@@ -56,7 +56,7 @@ export class MemoryRecallTool implements AgentTool<typeof memoryRecallSchema> {
|
|
|
56
56
|
details: {},
|
|
57
57
|
};
|
|
58
58
|
} catch (err) {
|
|
59
|
-
logger.warn("recall failed", { backend: "
|
|
59
|
+
logger.warn("recall failed", { backend: "mnemopi", bank: state.config.bank, error: String(err) });
|
|
60
60
|
throw err instanceof Error ? err : new Error(String(err));
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -26,17 +26,17 @@ export class MemoryReflectTool implements AgentTool<typeof memoryReflectSchema>
|
|
|
26
26
|
|
|
27
27
|
static createIf(session: ToolSession): MemoryReflectTool | null {
|
|
28
28
|
const backend = session.settings.get("memory.backend");
|
|
29
|
-
if (backend !== "hindsight" && backend !== "
|
|
29
|
+
if (backend !== "hindsight" && backend !== "mnemopi") return null;
|
|
30
30
|
return new MemoryReflectTool(session);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
async execute(_id: string, params: MemoryReflectParams, signal?: AbortSignal): Promise<AgentToolResult> {
|
|
34
34
|
return untilAborted(signal, async () => {
|
|
35
35
|
const backend = this.session.settings.get("memory.backend");
|
|
36
|
-
if (backend === "
|
|
37
|
-
const state = this.session.
|
|
36
|
+
if (backend === "mnemopi") {
|
|
37
|
+
const state = this.session.getMnemopiSessionState?.();
|
|
38
38
|
if (!state) {
|
|
39
|
-
throw new Error("
|
|
39
|
+
throw new Error("Mnemopi backend is not initialised for this session.");
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
try {
|
|
@@ -56,7 +56,7 @@ export class MemoryReflectTool implements AgentTool<typeof memoryReflectSchema>
|
|
|
56
56
|
details: {},
|
|
57
57
|
};
|
|
58
58
|
} catch (err) {
|
|
59
|
-
logger.warn("reflect failed", { backend: "
|
|
59
|
+
logger.warn("reflect failed", { backend: "mnemopi", bank: state.config.bank, error: String(err) });
|
|
60
60
|
throw err instanceof Error ? err : new Error(String(err));
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -30,16 +30,16 @@ export class MemoryRetainTool implements AgentTool<typeof memoryRetainSchema> {
|
|
|
30
30
|
|
|
31
31
|
static createIf(session: ToolSession): MemoryRetainTool | null {
|
|
32
32
|
const backend = session.settings.get("memory.backend");
|
|
33
|
-
if (backend !== "hindsight" && backend !== "
|
|
33
|
+
if (backend !== "hindsight" && backend !== "mnemopi") return null;
|
|
34
34
|
return new MemoryRetainTool(session);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
async execute(_id: string, params: MemoryRetainParams): Promise<AgentToolResult> {
|
|
38
38
|
const backend = this.session.settings.get("memory.backend");
|
|
39
|
-
if (backend === "
|
|
40
|
-
const state = this.session.
|
|
39
|
+
if (backend === "mnemopi") {
|
|
40
|
+
const state = this.session.getMnemopiSessionState?.();
|
|
41
41
|
if (!state) {
|
|
42
|
-
throw new Error("
|
|
42
|
+
throw new Error("Mnemopi backend is not initialised for this session.");
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
for (const item of params.items) {
|
package/src/tools/path-utils.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as os from "node:os";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as url from "node:url";
|
|
5
5
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
6
|
-
import { InternalUrlRouter } from "../internal-urls";
|
|
6
|
+
import { InternalUrlRouter, type LocalProtocolOptions } from "../internal-urls";
|
|
7
7
|
import { ToolError } from "./tool-errors";
|
|
8
8
|
|
|
9
9
|
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
@@ -740,6 +740,12 @@ export interface ToolScopeOptions {
|
|
|
740
740
|
surfaceExactFilePaths?: boolean;
|
|
741
741
|
/** Extra hint appended to "Path not found" when stat fails and the user supplied multiple paths. */
|
|
742
742
|
multipathStatHint?: string;
|
|
743
|
+
/** Calling session's settings — forwarded to the internal-URL router so caller-aware handlers (issue://, pr://) honor it. */
|
|
744
|
+
settings?: unknown;
|
|
745
|
+
/** Caller's abort signal — forwarded to the internal-URL router. */
|
|
746
|
+
signal?: AbortSignal;
|
|
747
|
+
/** Calling session's `local://` root mapping — pins resolutions to the calling session. */
|
|
748
|
+
localProtocolOptions?: LocalProtocolOptions;
|
|
743
749
|
}
|
|
744
750
|
|
|
745
751
|
export interface ToolScopeResolution {
|
|
@@ -778,7 +784,12 @@ export async function resolveToolSearchScope(opts: ToolScopeOptions): Promise<To
|
|
|
778
784
|
if (hasGlobPathChars(rawPath)) {
|
|
779
785
|
throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPath}`);
|
|
780
786
|
}
|
|
781
|
-
const resource = await internalRouter.resolve(rawPath
|
|
787
|
+
const resource = await internalRouter.resolve(rawPath, {
|
|
788
|
+
cwd,
|
|
789
|
+
settings: opts.settings,
|
|
790
|
+
signal: opts.signal,
|
|
791
|
+
localProtocolOptions: opts.localProtocolOptions,
|
|
792
|
+
});
|
|
782
793
|
if (!resource.sourcePath) {
|
|
783
794
|
throw new ToolError(`Cannot ${internalUrlAction} internal URL without a backing file: ${rawPath}`);
|
|
784
795
|
}
|
package/src/tools/read.ts
CHANGED
|
@@ -2141,6 +2141,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2141
2141
|
cwd: this.session.cwd,
|
|
2142
2142
|
settings: this.session.settings,
|
|
2143
2143
|
signal,
|
|
2144
|
+
localProtocolOptions: this.session.localProtocolOptions,
|
|
2144
2145
|
});
|
|
2145
2146
|
const details: ReadToolDetails = { resolvedPath: resource.sourcePath, contentType: resource.contentType };
|
|
2146
2147
|
|
|
@@ -177,7 +177,8 @@ export function formatMeta(meta: string[], theme: Theme): string {
|
|
|
177
177
|
|
|
178
178
|
export function formatErrorMessage(message: string | undefined, theme: Theme): string {
|
|
179
179
|
const clean = (message ?? "").replace(/^Error:\s*/, "").trim();
|
|
180
|
-
|
|
180
|
+
const safe = clean ? replaceTabs(truncateToWidth(clean, TRUNCATE_LENGTHS.LINE)) : "Unknown error";
|
|
181
|
+
return `${theme.styledSymbol("status.error", "error")} ${theme.fg("error", `Error: ${safe}`)}`;
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
export function formatEmptyMessage(message: string, theme: Theme): string {
|