@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
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
|
+
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
|
+
import { formatNumber, prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
6
|
+
import type { RenderResultOptions } from "../../extensibility/custom-tools/types";
|
|
7
|
+
import type { Theme, ThemeColor } from "../../modes/theme/theme";
|
|
8
|
+
import goalDescription from "../../prompts/tools/goal.md" with { type: "text" };
|
|
9
|
+
import { formatDuration } from "../../slash-commands/helpers/format";
|
|
10
|
+
import type { ToolSession } from "../../tools";
|
|
11
|
+
import { formatErrorMessage, TRUNCATE_LENGTHS } from "../../tools/render-utils";
|
|
12
|
+
import { ToolError } from "../../tools/tool-errors";
|
|
13
|
+
import { renderStatusLine, truncateToWidth } from "../../tui";
|
|
14
|
+
import { completionBudgetReport, remainingTokens } from "../runtime";
|
|
15
|
+
import type { Goal, GoalStatus, GoalToolDetails } from "../state";
|
|
16
|
+
|
|
17
|
+
const goalSchema = Type.Object({
|
|
18
|
+
op: Type.Union([Type.Literal("create"), Type.Literal("get"), Type.Literal("complete")], {
|
|
19
|
+
description: "Goal operation.",
|
|
20
|
+
}),
|
|
21
|
+
objective: Type.Optional(Type.String({ description: "Goal objective. Required when op=create." })),
|
|
22
|
+
token_budget: Type.Optional(
|
|
23
|
+
Type.Integer({
|
|
24
|
+
description: "Optional positive token budget. Only honored when op=create.",
|
|
25
|
+
}),
|
|
26
|
+
),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export type GoalToolInput = Static<typeof goalSchema>;
|
|
30
|
+
|
|
31
|
+
export interface GoalToolResponse {
|
|
32
|
+
goal: Goal | null;
|
|
33
|
+
remainingTokens: number | null;
|
|
34
|
+
completionBudgetReport: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function buildGoalToolResponse(
|
|
38
|
+
goal: Goal | null | undefined,
|
|
39
|
+
options?: { includeCompletionReport?: boolean },
|
|
40
|
+
): GoalToolResponse {
|
|
41
|
+
const resolvedGoal = goal ?? null;
|
|
42
|
+
return {
|
|
43
|
+
goal: resolvedGoal,
|
|
44
|
+
remainingTokens: remainingTokens(resolvedGoal),
|
|
45
|
+
completionBudgetReport:
|
|
46
|
+
options?.includeCompletionReport && resolvedGoal?.status === "complete"
|
|
47
|
+
? completionBudgetReport(resolvedGoal)
|
|
48
|
+
: null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function validateCreateParams(params: GoalToolInput): { objective: string; tokenBudget?: number } {
|
|
53
|
+
const objective = params.objective?.trim();
|
|
54
|
+
if (!objective) {
|
|
55
|
+
throw new ToolError("objective is required when op=create");
|
|
56
|
+
}
|
|
57
|
+
const tokenBudget = params.token_budget;
|
|
58
|
+
if (tokenBudget !== undefined && (!Number.isInteger(tokenBudget) || tokenBudget <= 0)) {
|
|
59
|
+
throw new ToolError("token_budget must be a positive integer when provided");
|
|
60
|
+
}
|
|
61
|
+
return { objective, tokenBudget };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class GoalTool implements AgentTool<typeof goalSchema, GoalToolDetails> {
|
|
65
|
+
readonly name = "goal";
|
|
66
|
+
readonly label = "Goal";
|
|
67
|
+
readonly description = prompt.render(goalDescription);
|
|
68
|
+
readonly parameters = goalSchema;
|
|
69
|
+
readonly strict = true;
|
|
70
|
+
readonly intent = "omit" as const;
|
|
71
|
+
readonly #session: ToolSession;
|
|
72
|
+
|
|
73
|
+
constructor(session: ToolSession) {
|
|
74
|
+
this.#session = session;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async execute(
|
|
78
|
+
_toolCallId: string,
|
|
79
|
+
params: GoalToolInput,
|
|
80
|
+
_signal?: AbortSignal,
|
|
81
|
+
_onUpdate?: AgentToolUpdateCallback<GoalToolDetails>,
|
|
82
|
+
_context?: AgentToolContext,
|
|
83
|
+
): Promise<AgentToolResult<GoalToolDetails>> {
|
|
84
|
+
const runtime = this.#session.getGoalRuntime?.();
|
|
85
|
+
if (!runtime) {
|
|
86
|
+
throw new ToolError("Goal mode is not active.");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let response: GoalToolResponse;
|
|
90
|
+
if (params.op === "create") {
|
|
91
|
+
const created = await runtime.createGoal(validateCreateParams(params));
|
|
92
|
+
response = buildGoalToolResponse(created.goal);
|
|
93
|
+
} else if (params.op === "get") {
|
|
94
|
+
const state = this.#session.getGoalModeState?.();
|
|
95
|
+
response = buildGoalToolResponse(state?.enabled ? state.goal : null);
|
|
96
|
+
} else {
|
|
97
|
+
const completed = await runtime.completeGoalFromTool();
|
|
98
|
+
response = buildGoalToolResponse(completed, { includeCompletionReport: true });
|
|
99
|
+
}
|
|
100
|
+
let text: string;
|
|
101
|
+
if (response.goal) {
|
|
102
|
+
text = `Goal: ${response.goal.objective}\nStatus: ${response.goal.status}\nTokens: ${response.goal.tokensUsed} used`;
|
|
103
|
+
if (response.goal.tokenBudget !== undefined) {
|
|
104
|
+
text += ` / ${response.goal.tokenBudget} budget`;
|
|
105
|
+
}
|
|
106
|
+
if (response.remainingTokens !== null) {
|
|
107
|
+
text += `\nRemaining tokens: ${response.remainingTokens}`;
|
|
108
|
+
}
|
|
109
|
+
if (response.completionBudgetReport) {
|
|
110
|
+
text += `\n\n${response.completionBudgetReport}`;
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
text = "No active goal.";
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
content: [{ type: "text", text }],
|
|
117
|
+
details: {
|
|
118
|
+
op: params.op,
|
|
119
|
+
goal: response.goal,
|
|
120
|
+
remainingTokens: response.remainingTokens,
|
|
121
|
+
completionBudgetReport: response.completionBudgetReport,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function describeOp(op: string | undefined): string {
|
|
128
|
+
switch (op) {
|
|
129
|
+
case "create":
|
|
130
|
+
return "set";
|
|
131
|
+
case "complete":
|
|
132
|
+
return "complete";
|
|
133
|
+
case "get":
|
|
134
|
+
return "check";
|
|
135
|
+
default:
|
|
136
|
+
return op ?? "?";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function goalBadgeColor(status: GoalStatus): ThemeColor {
|
|
141
|
+
switch (status) {
|
|
142
|
+
case "complete":
|
|
143
|
+
return "success";
|
|
144
|
+
case "budget-limited":
|
|
145
|
+
return "warning";
|
|
146
|
+
case "paused":
|
|
147
|
+
case "dropped":
|
|
148
|
+
return "muted";
|
|
149
|
+
default:
|
|
150
|
+
return "accent";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface GoalRenderArgs {
|
|
155
|
+
op?: GoalToolInput["op"];
|
|
156
|
+
objective?: string;
|
|
157
|
+
token_budget?: number;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const goalToolRenderer = {
|
|
161
|
+
renderCall(args: GoalRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
162
|
+
const description = describeOp(args.op);
|
|
163
|
+
const meta: string[] = [];
|
|
164
|
+
const trimmedObjective = args.objective?.trim();
|
|
165
|
+
if (args.op === "create" && trimmedObjective) {
|
|
166
|
+
const objective = truncateToWidth(trimmedObjective, TRUNCATE_LENGTHS.TITLE);
|
|
167
|
+
meta.push(uiTheme.italic(uiTheme.fg("muted", `"${objective}"`)));
|
|
168
|
+
}
|
|
169
|
+
if (args.op === "create" && args.token_budget !== undefined) {
|
|
170
|
+
meta.push(`budget ${formatNumber(args.token_budget)}`);
|
|
171
|
+
}
|
|
172
|
+
const text = renderStatusLine({ icon: "pending", title: "Goal", description, meta }, uiTheme);
|
|
173
|
+
return new Text(text, 0, 0);
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
renderResult(
|
|
177
|
+
result: { content: Array<{ type: string; text?: string }>; details?: GoalToolDetails; isError?: boolean },
|
|
178
|
+
_options: RenderResultOptions,
|
|
179
|
+
uiTheme: Theme,
|
|
180
|
+
args?: GoalRenderArgs,
|
|
181
|
+
): Component {
|
|
182
|
+
const fallbackText = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
183
|
+
const details = result.details;
|
|
184
|
+
const op = details?.op ?? args?.op;
|
|
185
|
+
const description = describeOp(op);
|
|
186
|
+
|
|
187
|
+
if (result.isError) {
|
|
188
|
+
const header = renderStatusLine({ icon: "error", title: "Goal", description }, uiTheme);
|
|
189
|
+
const body = formatErrorMessage(fallbackText || "Goal tool failed", uiTheme);
|
|
190
|
+
return new Text([header, body].join("\n"), 0, 0);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const goal = details?.goal ?? null;
|
|
194
|
+
if (!goal) {
|
|
195
|
+
const header = renderStatusLine({ icon: "warning", title: "Goal", description }, uiTheme);
|
|
196
|
+
const body = uiTheme.fg("muted", "No active goal.");
|
|
197
|
+
return new Text([header, body].join("\n"), 0, 0);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const lines: string[] = [];
|
|
201
|
+
lines.push(
|
|
202
|
+
renderStatusLine(
|
|
203
|
+
{
|
|
204
|
+
icon: "success",
|
|
205
|
+
title: "Goal",
|
|
206
|
+
description,
|
|
207
|
+
badge: { label: goal.status, color: goalBadgeColor(goal.status) },
|
|
208
|
+
},
|
|
209
|
+
uiTheme,
|
|
210
|
+
),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const objectiveText = truncateToWidth(goal.objective.trim(), TRUNCATE_LENGTHS.LONG);
|
|
214
|
+
lines.push(` ${uiTheme.italic(uiTheme.fg("muted", `"${objectiveText}"`))}`);
|
|
215
|
+
|
|
216
|
+
const used = formatNumber(goal.tokensUsed);
|
|
217
|
+
const tokensLine =
|
|
218
|
+
goal.tokenBudget !== undefined
|
|
219
|
+
? `${used} / ${formatNumber(goal.tokenBudget)} tokens (${formatNumber(Math.max(0, goal.tokenBudget - goal.tokensUsed))} left)`
|
|
220
|
+
: `${used} tokens`;
|
|
221
|
+
lines.push(` ${uiTheme.fg("dim", tokensLine)}`);
|
|
222
|
+
|
|
223
|
+
if (goal.timeUsedSeconds > 0) {
|
|
224
|
+
lines.push(` ${uiTheme.fg("dim", `${formatDuration(goal.timeUsedSeconds * 1000)} elapsed`)}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const report = details?.completionBudgetReport;
|
|
228
|
+
if (report) {
|
|
229
|
+
lines.push("");
|
|
230
|
+
lines.push(uiTheme.italic(uiTheme.fg("muted", report)));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return new Text(lines.join("\n"), 0, 0);
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
mergeCallAndResult: true,
|
|
237
|
+
};
|
package/src/hashline/anchors.ts
CHANGED
|
@@ -63,9 +63,9 @@ export class HashlineMismatchError extends Error {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
private static rejectionHeader(mismatches: HashMismatch[]): string[] {
|
|
66
|
-
const noun = mismatches.length > 1 ? "
|
|
66
|
+
const noun = mismatches.length > 1 ? "anchors do" : "anchor does";
|
|
67
67
|
return [
|
|
68
|
-
`Edit rejected: ${mismatches.length} ${noun}
|
|
68
|
+
`Edit rejected: ${mismatches.length} ${noun} not match the current file (marked *).`,
|
|
69
69
|
"The edit was NOT applied, please use the updated file content shown below, and issue another edit tool-call.",
|
|
70
70
|
];
|
|
71
71
|
}
|
package/src/hashline/input.ts
CHANGED
|
@@ -108,7 +108,8 @@ export function splitHashlineInputs(input: string, options: SplitHashlineOptions
|
|
|
108
108
|
|
|
109
109
|
const flush = () => {
|
|
110
110
|
if (currentPath.length === 0) return;
|
|
111
|
-
|
|
111
|
+
const hasOps = currentLines.some(rawLine => stripTrailingCarriageReturn(rawLine).trim().length > 0);
|
|
112
|
+
if (hasOps) sections.push({ path: currentPath, diff: currentLines.join("\n") });
|
|
112
113
|
currentLines = [];
|
|
113
114
|
};
|
|
114
115
|
|
package/src/hashline/parser.ts
CHANGED
|
@@ -86,9 +86,33 @@ function collectPayload(
|
|
|
86
86
|
let index = startIndex;
|
|
87
87
|
while (index < lines.length) {
|
|
88
88
|
const line = stripTrailingCarriageReturn(lines[index]);
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
if (line.startsWith(HL_EDIT_SEP)) {
|
|
90
|
+
payload.push(line.slice(1));
|
|
91
|
+
index++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// Silently recover from a missing payload prefix on an otherwise blank
|
|
95
|
+
// line: if more payload follows (possibly past further blanks), treat
|
|
96
|
+
// each intervening blank as an empty `${HL_EDIT_SEP}` payload line.
|
|
97
|
+
// Additionally, when the op explicitly requires payload (`+`/`<`) and
|
|
98
|
+
// we have not collected any yet, accept the blank(s) themselves as the
|
|
99
|
+
// empty payload — common typo of forgetting the `${HL_EDIT_SEP}` prefix
|
|
100
|
+
// when inserting a blank line.
|
|
101
|
+
if (line.length === 0) {
|
|
102
|
+
let lookahead = index + 1;
|
|
103
|
+
while (lookahead < lines.length && stripTrailingCarriageReturn(lines[lookahead]).length === 0) {
|
|
104
|
+
lookahead++;
|
|
105
|
+
}
|
|
106
|
+
const followedByPayload =
|
|
107
|
+
lookahead < lines.length && stripTrailingCarriageReturn(lines[lookahead]).startsWith(HL_EDIT_SEP);
|
|
108
|
+
const acceptBareBlank = requirePayload && payload.length === 0;
|
|
109
|
+
if (followedByPayload || acceptBareBlank) {
|
|
110
|
+
for (let j = index; j < lookahead; j++) payload.push("");
|
|
111
|
+
index = lookahead;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
92
116
|
}
|
|
93
117
|
if (payload.length === 0 && requirePayload) {
|
|
94
118
|
throw new Error(`line ${opLineNum}: + and < operations require at least one ${HL_EDIT_SEP}TEXT payload line.`);
|
|
@@ -379,4 +379,4 @@ export const MENTAL_MODEL_FIRST_TURN_DEADLINE_MS = 1500;
|
|
|
379
379
|
export const MENTAL_MODEL_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
380
380
|
|
|
381
381
|
/** Need-only export of the raw seed list for tests. */
|
|
382
|
-
export const
|
|
382
|
+
export const builtinSeedsForTest: ReadonlyArray<Readonly<RawSeed>> = BUILTIN_SEEDS;
|
|
@@ -14,29 +14,10 @@
|
|
|
14
14
|
import * as fs from "node:fs/promises";
|
|
15
15
|
import * as path from "node:path";
|
|
16
16
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
17
|
-
import { AgentRegistry } from "../registry/agent-registry";
|
|
18
17
|
import { applyQuery, pathToQuery } from "./json-query";
|
|
18
|
+
import { artifactsDirsFromRegistry } from "./registry-helpers";
|
|
19
19
|
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
* Snapshot of artifacts dirs for every registered session, deduped.
|
|
23
|
-
*
|
|
24
|
-
* Prefers `sessionManager.getArtifactsDir()` because subagents adopt the
|
|
25
|
-
* parent's manager and report the parent's dir there; dedup then collapses
|
|
26
|
-
* the whole agent tree to one entry. Falls back to the raw session file
|
|
27
|
-
* when no live session reference is attached.
|
|
28
|
-
*/
|
|
29
|
-
function artifactsDirsFromRegistry(): string[] {
|
|
30
|
-
const dirs: string[] = [];
|
|
31
|
-
for (const ref of AgentRegistry.global().list()) {
|
|
32
|
-
const dir =
|
|
33
|
-
ref.session?.sessionManager.getArtifactsDir() ?? (ref.sessionFile ? ref.sessionFile.slice(0, -6) : null);
|
|
34
|
-
if (!dir) continue;
|
|
35
|
-
if (!dirs.includes(dir)) dirs.push(dir);
|
|
36
|
-
}
|
|
37
|
-
return dirs;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
21
|
/**
|
|
41
22
|
* Handler for agent:// URLs.
|
|
42
23
|
*
|
|
@@ -12,27 +12,9 @@
|
|
|
12
12
|
import * as fs from "node:fs/promises";
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
15
|
-
import {
|
|
15
|
+
import { artifactsDirsFromRegistry } from "./registry-helpers";
|
|
16
16
|
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* Snapshot of artifacts dirs across all registered sessions, deduped.
|
|
20
|
-
*
|
|
21
|
-
* Subagents adopt their parent's `ArtifactManager`, so their
|
|
22
|
-
* `sessionManager.getArtifactsDir()` returns the parent's dir; dedup
|
|
23
|
-
* collapses parent + N subagents to a single entry.
|
|
24
|
-
*/
|
|
25
|
-
function artifactsDirsFromRegistry(): string[] {
|
|
26
|
-
const dirs: string[] = [];
|
|
27
|
-
for (const ref of AgentRegistry.global().list()) {
|
|
28
|
-
const dir =
|
|
29
|
-
ref.session?.sessionManager.getArtifactsDir() ?? (ref.sessionFile ? ref.sessionFile.slice(0, -6) : null);
|
|
30
|
-
if (!dir) continue;
|
|
31
|
-
if (!dirs.includes(dir)) dirs.push(dir);
|
|
32
|
-
}
|
|
33
|
-
return dirs;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
18
|
export class ArtifactProtocolHandler implements ProtocolHandler {
|
|
37
19
|
readonly scheme = "artifact";
|
|
38
20
|
readonly immutable = true;
|