@oh-my-pi/pi-coding-agent 15.0.0 → 15.0.2
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 +79 -0
- package/examples/extensions/plan-mode.ts +0 -1
- package/package.json +10 -10
- package/scripts/build-binary.ts +5 -0
- package/src/autoresearch/helpers.ts +17 -0
- package/src/autoresearch/tools/log-experiment.ts +9 -17
- package/src/autoresearch/tools/run-experiment.ts +2 -17
- package/src/capability/skill.ts +7 -0
- package/src/cli/list-models.ts +1 -1
- package/src/cli/shell-cli.ts +3 -13
- package/src/cli/update-cli.ts +1 -1
- package/src/cli.ts +10 -29
- package/src/commands/commit.ts +10 -0
- package/src/commit/agentic/tools/propose-changelog.ts +8 -1
- package/src/commit/analysis/conventional.ts +8 -66
- package/src/commit/map-reduce/reduce-phase.ts +6 -65
- package/src/commit/pipeline.ts +2 -2
- package/src/commit/shared-llm.ts +89 -0
- package/src/config/config-file.ts +210 -0
- package/src/config/model-equivalence.ts +8 -11
- package/src/config/model-registry.ts +44 -3
- package/src/config/model-resolver.ts +1 -4
- package/src/config/settings-schema.ts +82 -1
- package/src/config/settings.ts +1 -1
- package/src/config.ts +3 -219
- package/src/discovery/claude-plugins.ts +19 -7
- package/src/edit/renderer.ts +7 -1
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/shared/rewrite-imports.ts +2 -2
- package/src/eval/py/executor.ts +5 -0
- package/src/eval/py/runner.py +42 -11
- package/src/eval/py/runtime.ts +1 -0
- package/src/exa/factory.ts +2 -2
- package/src/exa/mcp-client.ts +74 -1
- package/src/exec/bash-executor.ts +5 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -11
- package/src/extensibility/extensions/get-commands-handler.ts +77 -0
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +89 -223
- package/src/extensibility/hooks/types.ts +89 -314
- package/src/extensibility/plugins/legacy-pi-compat.ts +48 -31
- package/src/extensibility/shared-events.ts +343 -0
- package/src/extensibility/skills.ts +9 -0
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +500 -0
- package/src/goals/state.ts +37 -0
- package/src/goals/tools/goal-tool.ts +237 -0
- package/src/hashline/anchors.ts +2 -2
- package/src/hashline/input.ts +2 -1
- package/src/hashline/parser.ts +27 -3
- package/src/hindsight/mental-models.ts +1 -1
- package/src/internal-urls/agent-protocol.ts +1 -20
- package/src/internal-urls/artifact-protocol.ts +1 -19
- package/src/internal-urls/docs-index.generated.ts +11 -12
- package/src/internal-urls/registry-helpers.ts +25 -0
- package/src/internal-urls/router.ts +8 -0
- package/src/internal-urls/types.ts +21 -0
- package/src/lsp/config.ts +15 -6
- package/src/lsp/defaults.json +6 -2
- package/src/main.ts +11 -2
- package/src/mcp/oauth-flow.ts +20 -0
- package/src/modes/acp/acp-agent.ts +327 -95
- package/src/modes/components/assistant-message.ts +14 -8
- package/src/modes/components/bash-execution.ts +24 -63
- package/src/modes/components/custom-message.ts +14 -40
- package/src/modes/components/eval-execution.ts +27 -57
- package/src/modes/components/execution-shared.ts +102 -0
- package/src/modes/components/hook-message.ts +17 -49
- package/src/modes/components/mcp-add-wizard.ts +26 -5
- package/src/modes/components/message-frame.ts +88 -0
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +6 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +93 -8
- package/src/modes/components/status-line/types.ts +4 -0
- package/src/modes/components/status-line.ts +28 -10
- package/src/modes/components/tool-execution.ts +7 -8
- package/src/modes/controllers/command-controller-shared.ts +108 -0
- package/src/modes/controllers/command-controller.ts +13 -4
- package/src/modes/controllers/event-controller.ts +36 -7
- package/src/modes/controllers/extension-ui-controller.ts +3 -2
- package/src/modes/controllers/input-controller.ts +13 -0
- package/src/modes/controllers/mcp-command-controller.ts +56 -61
- package/src/modes/controllers/ssh-command-controller.ts +18 -57
- package/src/modes/interactive-mode.ts +624 -52
- package/src/modes/print-mode.ts +16 -86
- package/src/modes/rpc/host-uris.ts +235 -0
- package/src/modes/rpc/rpc-mode.ts +41 -88
- package/src/modes/rpc/rpc-types.ts +57 -0
- package/src/modes/runtime-init.ts +116 -0
- package/src/modes/theme/defaults/dark-poimandres.json +3 -0
- package/src/modes/theme/defaults/light-poimandres.json +3 -0
- package/src/modes/theme/theme.ts +24 -6
- package/src/modes/types.ts +14 -3
- package/src/modes/utils/context-usage.ts +13 -13
- package/src/modes/utils/ui-helpers.ts +10 -3
- package/src/plan-mode/approved-plan.ts +35 -1
- package/src/prompts/goals/goal-budget-limit.md +16 -0
- package/src/prompts/goals/goal-continuation.md +28 -0
- package/src/prompts/goals/goal-mode-active.md +23 -0
- package/src/prompts/system/plan-mode-active.md +5 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/tools/bash.md +6 -0
- package/src/prompts/tools/github.md +4 -4
- package/src/prompts/tools/goal.md +13 -0
- package/src/prompts/tools/hashline.md +101 -117
- package/src/prompts/tools/read.md +55 -36
- package/src/prompts/tools/resolve.md +6 -5
- package/src/sdk.ts +12 -5
- package/src/session/agent-session.ts +428 -106
- package/src/session/blob-store.ts +36 -3
- package/src/session/messages.ts +67 -2
- package/src/session/session-manager.ts +131 -12
- package/src/session/session-storage.ts +33 -15
- package/src/session/streaming-output.ts +309 -13
- package/src/slash-commands/builtin-registry.ts +18 -0
- package/src/ssh/ssh-executor.ts +5 -0
- package/src/system-prompt.ts +4 -2
- package/src/task/discovery.ts +5 -2
- package/src/task/executor.ts +19 -8
- package/src/task/index.ts +3 -0
- package/src/task/render.ts +21 -15
- package/src/task/types.ts +4 -0
- package/src/tools/ast-edit.ts +21 -120
- package/src/tools/ast-grep.ts +21 -119
- package/src/tools/bash-command-fixup.ts +47 -0
- package/src/tools/bash-interactive.ts +9 -1
- package/src/tools/bash.ts +66 -19
- package/src/tools/browser/attach.ts +3 -3
- package/src/tools/browser/launch.ts +81 -18
- package/src/tools/browser/registry.ts +1 -5
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +51 -14
- package/src/tools/conflict-detect.ts +15 -4
- package/src/tools/eval.ts +12 -2
- package/src/tools/find.ts +20 -38
- package/src/tools/gh.ts +44 -10
- package/src/tools/index.ts +22 -11
- package/src/tools/inspect-image.ts +3 -10
- package/src/tools/job.ts +16 -7
- package/src/tools/output-meta.ts +202 -37
- package/src/tools/path-utils.ts +125 -2
- package/src/tools/read.ts +548 -237
- package/src/tools/render-utils.ts +92 -0
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +72 -44
- package/src/tools/search.ts +120 -186
- package/src/tools/ssh.ts +3 -2
- package/src/tools/write.ts +64 -9
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/image-loading.ts +7 -3
- package/src/utils/image-resize.ts +32 -43
- package/src/vim/parser.ts +0 -17
- package/src/vim/render.ts +1 -1
- package/src/vim/types.ts +1 -1
- package/src/web/search/providers/anthropic.ts +5 -0
- package/src/web/search/providers/exa.ts +3 -0
- package/src/web/search/providers/gemini.ts +40 -95
- package/src/web/search/providers/jina.ts +5 -2
- package/src/web/search/providers/zai.ts +5 -2
- package/src/prompts/tools/exit-plan-mode.md +0 -6
- package/src/tools/exit-plan-mode.ts +0 -97
- package/src/utils/fuzzy.ts +0 -108
- package/src/utils/image-convert.ts +0 -27
package/src/tools/find.ts
CHANGED
|
@@ -12,15 +12,7 @@ import { InternalUrlRouter } from "../internal-urls";
|
|
|
12
12
|
import type { Theme } from "../modes/theme/theme";
|
|
13
13
|
import findDescription from "../prompts/tools/find.md" with { type: "text" };
|
|
14
14
|
import { type TruncationResult, truncateHead } from "../session/streaming-output";
|
|
15
|
-
import {
|
|
16
|
-
Ellipsis,
|
|
17
|
-
Hasher,
|
|
18
|
-
type RenderCache,
|
|
19
|
-
renderFileList,
|
|
20
|
-
renderStatusLine,
|
|
21
|
-
renderTreeList,
|
|
22
|
-
truncateToWidth,
|
|
23
|
-
} from "../tui";
|
|
15
|
+
import { Ellipsis, renderFileList, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
24
16
|
import type { ToolSession } from ".";
|
|
25
17
|
import { applyListLimit } from "./list-limit";
|
|
26
18
|
import { formatFullOutputReference, type OutputMeta } from "./output-meta";
|
|
@@ -33,7 +25,13 @@ import {
|
|
|
33
25
|
resolveExplicitFindPatterns,
|
|
34
26
|
resolveToCwd,
|
|
35
27
|
} from "./path-utils";
|
|
36
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
createCachedComponent,
|
|
30
|
+
formatCount,
|
|
31
|
+
formatEmptyMessage,
|
|
32
|
+
formatErrorMessage,
|
|
33
|
+
PREVIEW_LIMITS,
|
|
34
|
+
} from "./render-utils";
|
|
37
35
|
import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
|
|
38
36
|
import { toolResult } from "./tool-result";
|
|
39
37
|
|
|
@@ -401,30 +399,22 @@ export const findToolRenderer = {
|
|
|
401
399
|
},
|
|
402
400
|
uiTheme,
|
|
403
401
|
);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const { expanded } = options;
|
|
408
|
-
const key = new Hasher().bool(expanded).u32(width).digest();
|
|
409
|
-
if (cached?.key === key) return cached.lines;
|
|
402
|
+
return createCachedComponent(
|
|
403
|
+
() => options.expanded,
|
|
404
|
+
width => {
|
|
410
405
|
const listLines = renderTreeList(
|
|
411
406
|
{
|
|
412
407
|
items: lines,
|
|
413
|
-
expanded,
|
|
408
|
+
expanded: options.expanded,
|
|
414
409
|
maxCollapsed: COLLAPSED_LIST_LIMIT,
|
|
415
410
|
itemType: "file",
|
|
416
411
|
renderItem: line => uiTheme.fg("accent", line),
|
|
417
412
|
},
|
|
418
413
|
uiTheme,
|
|
419
414
|
);
|
|
420
|
-
|
|
421
|
-
cached = { key, lines: result };
|
|
422
|
-
return result;
|
|
415
|
+
return [header, ...listLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
|
|
423
416
|
},
|
|
424
|
-
|
|
425
|
-
cached = undefined;
|
|
426
|
-
},
|
|
427
|
-
};
|
|
417
|
+
);
|
|
428
418
|
}
|
|
429
419
|
|
|
430
420
|
const fileCount = details?.fileCount ?? 0;
|
|
@@ -467,28 +457,20 @@ export const findToolRenderer = {
|
|
|
467
457
|
}
|
|
468
458
|
if (missingNote) extraLines.push(missingNote);
|
|
469
459
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const { expanded } = options;
|
|
474
|
-
const key = new Hasher().bool(expanded).u32(width).digest();
|
|
475
|
-
if (cached?.key === key) return cached.lines;
|
|
460
|
+
return createCachedComponent(
|
|
461
|
+
() => options.expanded,
|
|
462
|
+
width => {
|
|
476
463
|
const fileLines = renderFileList(
|
|
477
464
|
{
|
|
478
465
|
files: files.map(entry => ({ path: entry, isDirectory: entry.endsWith("/") })),
|
|
479
|
-
expanded,
|
|
466
|
+
expanded: options.expanded,
|
|
480
467
|
maxCollapsed: COLLAPSED_LIST_LIMIT,
|
|
481
468
|
},
|
|
482
469
|
uiTheme,
|
|
483
470
|
);
|
|
484
|
-
|
|
485
|
-
cached = { key, lines: result };
|
|
486
|
-
return result;
|
|
487
|
-
},
|
|
488
|
-
invalidate() {
|
|
489
|
-
cached = undefined;
|
|
471
|
+
return [header, ...fileLines, ...extraLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
|
|
490
472
|
},
|
|
491
|
-
|
|
473
|
+
);
|
|
492
474
|
},
|
|
493
475
|
mergeCallAndResult: true,
|
|
494
476
|
};
|
package/src/tools/gh.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { scheduler } from "node:timers/promises";
|
|
4
5
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
5
6
|
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
6
|
-
import {
|
|
7
|
+
import { getWorktreesDir, isEnoent, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
7
8
|
import { type Static, Type } from "@sinclair/typebox";
|
|
8
9
|
import type { Settings } from "../config/settings";
|
|
9
10
|
import githubDescription from "../prompts/tools/github.md" with { type: "text" };
|
|
@@ -1773,6 +1774,39 @@ export async function resolveDefaultRepoMemoized(cwd: string, signal?: AbortSign
|
|
|
1773
1774
|
return untilAborted(signal, pending);
|
|
1774
1775
|
}
|
|
1775
1776
|
|
|
1777
|
+
/**
|
|
1778
|
+
* Matches search-query qualifiers that already scope to a repository, org, or
|
|
1779
|
+
* user. When present, callers should avoid layering a default `repo:<current>`
|
|
1780
|
+
* on top — the user has already expressed an explicit scope.
|
|
1781
|
+
*
|
|
1782
|
+
* Only the leading `repo:`/`org:`/`user:`/`owner:` token is treated as a
|
|
1783
|
+
* scope marker; arbitrary substrings (e.g. inside quoted text) are ignored.
|
|
1784
|
+
*/
|
|
1785
|
+
const REPO_SCOPE_QUALIFIER_PATTERN = /(?:^|\s)-?(?:repo|org|user|owner):\S/i;
|
|
1786
|
+
|
|
1787
|
+
/**
|
|
1788
|
+
* Resolve the effective `repo:` scope for a search op. Returns the explicit
|
|
1789
|
+
* `repo` when set, `undefined` when the query already carries a scoping
|
|
1790
|
+
* qualifier, and otherwise the current checkout's `owner/repo` via
|
|
1791
|
+
* `resolveDefaultRepoMemoized`. Resolution failures (no git/gh context, no
|
|
1792
|
+
* configured remote) silently fall back to `undefined` so the search proceeds
|
|
1793
|
+
* across all of GitHub instead of throwing.
|
|
1794
|
+
*/
|
|
1795
|
+
async function resolveSearchRepoScope(
|
|
1796
|
+
cwd: string,
|
|
1797
|
+
repo: string | undefined,
|
|
1798
|
+
query: string | undefined,
|
|
1799
|
+
signal: AbortSignal | undefined,
|
|
1800
|
+
): Promise<string | undefined> {
|
|
1801
|
+
if (repo) return repo;
|
|
1802
|
+
if (query && REPO_SCOPE_QUALIFIER_PATTERN.test(query)) return undefined;
|
|
1803
|
+
try {
|
|
1804
|
+
return await resolveDefaultRepoMemoized(cwd, signal);
|
|
1805
|
+
} catch {
|
|
1806
|
+
return undefined;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1776
1810
|
async function resolveGitHubBranchHead(
|
|
1777
1811
|
cwd: string,
|
|
1778
1812
|
repo: string,
|
|
@@ -3266,11 +3300,11 @@ async function executeSearchIssues(
|
|
|
3266
3300
|
params: GithubInput,
|
|
3267
3301
|
signal: AbortSignal | undefined,
|
|
3268
3302
|
): Promise<AgentToolResult<GhToolDetails>> {
|
|
3269
|
-
const repo = normalizeOptionalString(params.repo);
|
|
3270
3303
|
const limit = resolveSearchLimit(params.limit);
|
|
3271
3304
|
const dateField = resolveSearchDateField("issues", params.dateField);
|
|
3272
3305
|
const dateQualifier = buildSearchDateQualifier(dateField, params.since, params.until);
|
|
3273
3306
|
const displayQuery = composeSearchQuery([params.query, dateQualifier]);
|
|
3307
|
+
const repo = await resolveSearchRepoScope(session.cwd, normalizeOptionalString(params.repo), displayQuery, signal);
|
|
3274
3308
|
const apiQuery = composeSearchQuery([displayQuery, repo ? `repo:${repo}` : undefined, "is:issue"]);
|
|
3275
3309
|
const args = buildGhApiSearchArgs("issues", apiQuery, limit);
|
|
3276
3310
|
|
|
@@ -3284,11 +3318,11 @@ async function executeSearchPrs(
|
|
|
3284
3318
|
params: GithubInput,
|
|
3285
3319
|
signal: AbortSignal | undefined,
|
|
3286
3320
|
): Promise<AgentToolResult<GhToolDetails>> {
|
|
3287
|
-
const repo = normalizeOptionalString(params.repo);
|
|
3288
3321
|
const limit = resolveSearchLimit(params.limit);
|
|
3289
3322
|
const dateField = resolveSearchDateField("prs", params.dateField);
|
|
3290
3323
|
const dateQualifier = buildSearchDateQualifier(dateField, params.since, params.until);
|
|
3291
3324
|
const displayQuery = composeSearchQuery([params.query, dateQualifier]);
|
|
3325
|
+
const repo = await resolveSearchRepoScope(session.cwd, normalizeOptionalString(params.repo), displayQuery, signal);
|
|
3292
3326
|
const apiQuery = composeSearchQuery([displayQuery, repo ? `repo:${repo}` : undefined, "is:pr"]);
|
|
3293
3327
|
const args = buildGhApiSearchArgs("issues", apiQuery, limit);
|
|
3294
3328
|
|
|
@@ -3306,8 +3340,8 @@ async function executeSearchCode(
|
|
|
3306
3340
|
if (params.since !== undefined || params.until !== undefined) {
|
|
3307
3341
|
throw new ToolError("search_code does not support since/until; GitHub code search has no date qualifier.");
|
|
3308
3342
|
}
|
|
3309
|
-
const repo = normalizeOptionalString(params.repo);
|
|
3310
3343
|
const limit = resolveSearchLimit(params.limit);
|
|
3344
|
+
const repo = await resolveSearchRepoScope(session.cwd, normalizeOptionalString(params.repo), query, signal);
|
|
3311
3345
|
const apiQuery = composeSearchQuery([query, repo ? `repo:${repo}` : undefined]);
|
|
3312
3346
|
const args = buildGhApiSearchArgs("code", apiQuery, limit, ["Accept: application/vnd.github.text-match+json"]);
|
|
3313
3347
|
|
|
@@ -3321,11 +3355,11 @@ async function executeSearchCommits(
|
|
|
3321
3355
|
params: GithubInput,
|
|
3322
3356
|
signal: AbortSignal | undefined,
|
|
3323
3357
|
): Promise<AgentToolResult<GhToolDetails>> {
|
|
3324
|
-
const repo = normalizeOptionalString(params.repo);
|
|
3325
3358
|
const limit = resolveSearchLimit(params.limit);
|
|
3326
3359
|
const dateField = resolveSearchDateField("commits", params.dateField);
|
|
3327
3360
|
const dateQualifier = buildSearchDateQualifier(dateField, params.since, params.until);
|
|
3328
3361
|
const displayQuery = composeSearchQuery([params.query, dateQualifier]);
|
|
3362
|
+
const repo = await resolveSearchRepoScope(session.cwd, normalizeOptionalString(params.repo), displayQuery, signal);
|
|
3329
3363
|
const apiQuery = composeSearchQuery([displayQuery, repo ? `repo:${repo}` : undefined]);
|
|
3330
3364
|
const args = buildGhApiSearchArgs("commits", apiQuery, limit);
|
|
3331
3365
|
|
|
@@ -3400,7 +3434,7 @@ async function executeRunWatch(
|
|
|
3400
3434
|
note,
|
|
3401
3435
|
}),
|
|
3402
3436
|
});
|
|
3403
|
-
await
|
|
3437
|
+
await scheduler.wait(graceSeconds * 1000, { signal });
|
|
3404
3438
|
run = await fetchRunSnapshot(session.cwd, repo, runId, signal);
|
|
3405
3439
|
}
|
|
3406
3440
|
|
|
@@ -3435,7 +3469,7 @@ async function executeRunWatch(
|
|
|
3435
3469
|
return buildTextResult(formatRunWatchResult(repo, run, [], tail), run.url, finalDetails);
|
|
3436
3470
|
}
|
|
3437
3471
|
|
|
3438
|
-
await
|
|
3472
|
+
await scheduler.wait(intervalSeconds * 1000, { signal });
|
|
3439
3473
|
}
|
|
3440
3474
|
}
|
|
3441
3475
|
|
|
@@ -3477,7 +3511,7 @@ async function executeRunWatch(
|
|
|
3477
3511
|
note,
|
|
3478
3512
|
}),
|
|
3479
3513
|
});
|
|
3480
|
-
await
|
|
3514
|
+
await scheduler.wait(graceSeconds * 1000, { signal });
|
|
3481
3515
|
runs = await fetchRunsForCommit(session.cwd, repo, headSha, branch, signal);
|
|
3482
3516
|
}
|
|
3483
3517
|
|
|
@@ -3533,11 +3567,11 @@ async function executeRunWatch(
|
|
|
3533
3567
|
note,
|
|
3534
3568
|
}),
|
|
3535
3569
|
});
|
|
3536
|
-
await
|
|
3570
|
+
await scheduler.wait(intervalSeconds * 1000, { signal });
|
|
3537
3571
|
continue;
|
|
3538
3572
|
}
|
|
3539
3573
|
|
|
3540
3574
|
settledSuccessSignature = undefined;
|
|
3541
|
-
await
|
|
3575
|
+
await scheduler.wait(intervalSeconds * 1000, { signal });
|
|
3542
3576
|
}
|
|
3543
3577
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type { Settings } from "../config/settings";
|
|
|
6
6
|
import { EditTool } from "../edit";
|
|
7
7
|
import { checkPythonKernelAvailability } from "../eval/py/kernel";
|
|
8
8
|
import type { Skill } from "../extensibility/skills";
|
|
9
|
+
import type { GoalModeState, GoalRuntime } from "../goals";
|
|
10
|
+
import { GoalTool } from "../goals/tools/goal-tool";
|
|
9
11
|
import type { HindsightSessionState } from "../hindsight/state";
|
|
10
12
|
import { LspTool } from "../lsp";
|
|
11
13
|
import type { PlanModeState } from "../plan-mode/state";
|
|
@@ -29,7 +31,6 @@ import { CalculatorTool } from "./calculator";
|
|
|
29
31
|
import { type CheckpointState, CheckpointTool, RewindTool } from "./checkpoint";
|
|
30
32
|
import { DebugTool } from "./debug";
|
|
31
33
|
import { EvalTool } from "./eval";
|
|
32
|
-
import { ExitPlanModeTool } from "./exit-plan-mode";
|
|
33
34
|
import { FindTool } from "./find";
|
|
34
35
|
import { GithubTool } from "./gh";
|
|
35
36
|
import { HindsightRecallTool } from "./hindsight-recall";
|
|
@@ -57,6 +58,7 @@ import { YieldTool } from "./yield";
|
|
|
57
58
|
export * from "../edit";
|
|
58
59
|
export * from "../exa";
|
|
59
60
|
export type * from "../exa/types";
|
|
61
|
+
export * from "../goals";
|
|
60
62
|
export * from "../lsp";
|
|
61
63
|
export * from "../session/streaming-output";
|
|
62
64
|
export * from "../task";
|
|
@@ -70,7 +72,6 @@ export * from "./calculator";
|
|
|
70
72
|
export * from "./checkpoint";
|
|
71
73
|
export * from "./debug";
|
|
72
74
|
export * from "./eval";
|
|
73
|
-
export * from "./exit-plan-mode";
|
|
74
75
|
export * from "./find";
|
|
75
76
|
export * from "./gh";
|
|
76
77
|
export * from "./hindsight-recall";
|
|
@@ -179,6 +180,10 @@ export interface ToolSession {
|
|
|
179
180
|
settings: Settings;
|
|
180
181
|
/** Plan mode state (if active) */
|
|
181
182
|
getPlanModeState?: () => PlanModeState | undefined;
|
|
183
|
+
/** Goal mode state (if active or paused) */
|
|
184
|
+
getGoalModeState?: () => GoalModeState | undefined;
|
|
185
|
+
/** Goal runtime for the active agent session. */
|
|
186
|
+
getGoalRuntime?: () => GoalRuntime | undefined;
|
|
182
187
|
/** Bridge to the connected client (e.g. ACP editor host). Tools should route fs/terminal/permission requests through this when available. */
|
|
183
188
|
getClientBridge?: () => ClientBridge | undefined;
|
|
184
189
|
/** Get compact conversation context for subagents (excludes tool results, system prompts) */
|
|
@@ -220,6 +225,12 @@ export interface ToolSession {
|
|
|
220
225
|
steer?(message: { customType: string; content: string; details?: unknown }): void;
|
|
221
226
|
/** Peek the currently in-flight tool-choice queue directive's invocation handler. Used by the `resolve` tool to dispatch to the pending action. */
|
|
222
227
|
peekQueueInvoker?(): ((input: unknown) => Promise<unknown> | unknown) | undefined;
|
|
228
|
+
/** Peek the long-lived "standing" resolve handler registered by a mode (e.g. plan mode).
|
|
229
|
+
* Consulted by the `resolve` tool as a fallback when no queue invoker is in flight,
|
|
230
|
+
* letting modes accept `resolve` invocations without forcing the tool choice every turn. */
|
|
231
|
+
peekStandingResolveHandler?(): ((input: unknown) => Promise<unknown> | unknown) | undefined;
|
|
232
|
+
/** Register or clear the standing resolve handler. Passing `null` clears it. */
|
|
233
|
+
setStandingResolveHandler?(handler: ((input: unknown) => Promise<unknown> | unknown) | null): void;
|
|
223
234
|
/** Get active checkpoint state if any. */
|
|
224
235
|
getCheckpointState?: () => CheckpointState | undefined;
|
|
225
236
|
/** Set or clear active checkpoint state. */
|
|
@@ -303,8 +314,8 @@ export const HIDDEN_TOOLS: Record<string, ToolFactory> = {
|
|
|
303
314
|
yield: s => new YieldTool(s),
|
|
304
315
|
report_finding: () => reportFindingTool,
|
|
305
316
|
report_tool_issue: s => createReportToolIssueTool(s),
|
|
306
|
-
exit_plan_mode: s => new ExitPlanModeTool(s),
|
|
307
317
|
resolve: s => new ResolveTool(s),
|
|
318
|
+
goal: s => new GoalTool(s),
|
|
308
319
|
};
|
|
309
320
|
|
|
310
321
|
export type ToolName = keyof typeof BUILTIN_TOOLS;
|
|
@@ -351,11 +362,12 @@ export function resolveEvalBackends(session: ToolSession): EvalBackendsAllowance
|
|
|
351
362
|
export async function createTools(session: ToolSession, toolNames?: string[]): Promise<Tool[]> {
|
|
352
363
|
const includeYield = session.requireYieldTool === true;
|
|
353
364
|
const enableLsp = session.enableLsp ?? true;
|
|
354
|
-
|
|
365
|
+
let requestedTools =
|
|
355
366
|
toolNames && toolNames.length > 0 ? [...new Set(toolNames.map(name => name.toLowerCase()))] : undefined;
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
367
|
+
const goalEnabled = session.settings.get("goal.enabled");
|
|
368
|
+
const goalModeActive = goalEnabled && session.getGoalModeState?.()?.enabled === true;
|
|
369
|
+
if (goalModeActive && requestedTools && !requestedTools.includes("goal")) {
|
|
370
|
+
requestedTools = [...requestedTools, "goal"];
|
|
359
371
|
}
|
|
360
372
|
const backends = resolveEvalBackends(session);
|
|
361
373
|
const allowPython = backends.python;
|
|
@@ -428,7 +440,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
428
440
|
|
|
429
441
|
const allTools: Record<string, ToolFactory> = { ...BUILTIN_TOOLS, ...HIDDEN_TOOLS };
|
|
430
442
|
const isToolAllowed = (name: string) => {
|
|
431
|
-
if (name === "
|
|
443
|
+
if (name === "goal") return goalEnabled && goalModeActive;
|
|
432
444
|
if (name === "lsp") return enableLsp && session.settings.get("lsp.enabled");
|
|
433
445
|
if (name === "bash") return true;
|
|
434
446
|
if (name === "eval") return allowEval;
|
|
@@ -478,7 +490,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
478
490
|
.filter(([name]) => isToolAllowed(name))
|
|
479
491
|
.map(([name, factory]) => [name, factory] as const),
|
|
480
492
|
...(includeYield ? ([["yield", HIDDEN_TOOLS.yield]] as const) : []),
|
|
481
|
-
...(
|
|
493
|
+
...(goalModeActive ? ([["goal", HIDDEN_TOOLS.goal]] as const) : []),
|
|
482
494
|
];
|
|
483
495
|
|
|
484
496
|
const baseResults = await Promise.all(
|
|
@@ -488,8 +500,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
488
500
|
}),
|
|
489
501
|
);
|
|
490
502
|
const tools = baseResults.filter((r): r is Tool => r !== null);
|
|
491
|
-
|
|
492
|
-
if (hasDeferrableTools && !tools.some(tool => tool.name === "resolve")) {
|
|
503
|
+
if (!tools.some(tool => tool.name === "resolve")) {
|
|
493
504
|
const resolveTool = await logger.time("createTools:resolve", HIDDEN_TOOLS.resolve, session);
|
|
494
505
|
if (resolveTool) {
|
|
495
506
|
tools.push(wrapToolWithMetaNotice(resolveTool));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import { type Api,
|
|
2
|
+
import { type Api, completeSimple, type Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
4
|
import { type Static, Type } from "@sinclair/typebox";
|
|
5
|
+
import { extractTextContent } from "../commit/utils";
|
|
5
6
|
import { expandRoleAlias, resolveModelFromString } from "../config/model-resolver";
|
|
6
7
|
import inspectImageDescription from "../prompts/tools/inspect-image.md" with { type: "text" };
|
|
7
8
|
import inspectImageSystemPromptTemplate from "../prompts/tools/inspect-image-system.md" with { type: "text" };
|
|
@@ -30,14 +31,6 @@ export interface InspectImageToolDetails {
|
|
|
30
31
|
mimeType: string;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
function extractResponseText(message: AssistantMessage): string {
|
|
34
|
-
return message.content
|
|
35
|
-
.filter(content => content.type === "text")
|
|
36
|
-
.map(content => content.text)
|
|
37
|
-
.join("")
|
|
38
|
-
.trim();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
34
|
export class InspectImageTool implements AgentTool<typeof inspectImageSchema, InspectImageToolDetails> {
|
|
42
35
|
readonly name = "inspect_image";
|
|
43
36
|
readonly label = "InspectImage";
|
|
@@ -151,7 +144,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
|
|
|
151
144
|
throw new ToolError("inspect_image request aborted.");
|
|
152
145
|
}
|
|
153
146
|
|
|
154
|
-
const text =
|
|
147
|
+
const text = extractTextContent(response);
|
|
155
148
|
if (!text) {
|
|
156
149
|
throw new ToolError("inspect_image model returned no text output.");
|
|
157
150
|
}
|
package/src/tools/job.ts
CHANGED
|
@@ -362,6 +362,8 @@ const COLLAPSED_LIST_LIMIT = PREVIEW_LIMITS.COLLAPSED_ITEMS;
|
|
|
362
362
|
const LABEL_MAX_WIDTH = 60;
|
|
363
363
|
const PREVIEW_LINES_COLLAPSED = 1;
|
|
364
364
|
const PREVIEW_LINES_EXPANDED = 4;
|
|
365
|
+
const LABEL_LINES_COLLAPSED = 1;
|
|
366
|
+
const LABEL_LINES_EXPANDED = 3;
|
|
365
367
|
const PREVIEW_LINE_WIDTH = 80;
|
|
366
368
|
|
|
367
369
|
function statusToIcon(status: JobSnapshot["status"]): ToolUIStatus {
|
|
@@ -488,14 +490,21 @@ export const jobToolRenderer = {
|
|
|
488
490
|
);
|
|
489
491
|
const typeBadge = formatBadge(job.type, statusToColor(job.status), uiTheme);
|
|
490
492
|
const idText = uiTheme.fg("muted", job.id);
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
493
|
+
const rawLabelLines = (job.label || "(no label)").split(/\r?\n/);
|
|
494
|
+
const maxLabelLines = expanded ? LABEL_LINES_EXPANDED : LABEL_LINES_COLLAPSED;
|
|
495
|
+
const visibleLabelLines = rawLabelLines
|
|
496
|
+
.slice(0, maxLabelLines)
|
|
497
|
+
.map(l => truncateToWidth(replaceTabs(l), LABEL_MAX_WIDTH, Ellipsis.Unicode));
|
|
498
|
+
if (rawLabelLines.length > maxLabelLines && visibleLabelLines.length > 0) {
|
|
499
|
+
const last = visibleLabelLines[visibleLabelLines.length - 1]!;
|
|
500
|
+
visibleLabelLines[visibleLabelLines.length - 1] = `${last} …`;
|
|
501
|
+
}
|
|
497
502
|
const durationText = uiTheme.fg("dim", formatDuration(job.durationMs));
|
|
498
|
-
|
|
503
|
+
const headLabel = uiTheme.fg("toolOutput", visibleLabelLines[0] ?? "");
|
|
504
|
+
lines.push(`${icon} ${idText} ${typeBadge} ${headLabel} ${durationText}`);
|
|
505
|
+
for (let i = 1; i < visibleLabelLines.length; i++) {
|
|
506
|
+
lines.push(` ${uiTheme.fg("toolOutput", visibleLabelLines[i]!)}`);
|
|
507
|
+
}
|
|
499
508
|
|
|
500
509
|
const preview = job.errorText?.trim() || job.resultText?.trim();
|
|
501
510
|
if (preview) {
|