@openeryc/pi-coding-agent 0.75.57 → 0.75.58
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 +9 -0
- package/dist/modes/interactive/components/login-dialog.d.ts +7 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +14 -4
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/goal-runner-utils.d.ts +11 -0
- package/dist/modes/interactive/goal-runner-utils.d.ts.map +1 -0
- package/dist/modes/interactive/goal-runner-utils.js +39 -0
- package/dist/modes/interactive/goal-runner-utils.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +51 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -62,6 +62,7 @@ import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
|
62
62
|
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
63
63
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
64
64
|
import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
|
|
65
|
+
import { isNearDuplicateGoalOutput, normalizeGoalOutput } from "./goal-runner-utils.js";
|
|
65
66
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
66
67
|
function isExpandable(obj) {
|
|
67
68
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
@@ -181,9 +182,13 @@ export class InteractiveMode {
|
|
|
181
182
|
goalRunnerAbortController = undefined;
|
|
182
183
|
goalRunnerRetryAttempt = 0;
|
|
183
184
|
goalRunnerStatusDisplayed = false;
|
|
185
|
+
/** Normalized assistant texts from recent goal turns (for duplicate detection). */
|
|
186
|
+
goalRunnerRecentOutputs = [];
|
|
184
187
|
GOAL_RUNNER_MAX_RETRIES = 10;
|
|
185
188
|
GOAL_RUNNER_BASE_DELAY_MS = 3000;
|
|
186
189
|
GOAL_RUNNER_TURN_DELAY_MS = 800;
|
|
190
|
+
/** Pause when this many consecutive turns look like near-duplicates. */
|
|
191
|
+
GOAL_RUNNER_MAX_DUPLICATE_STREAK = 3;
|
|
187
192
|
// Extension UI state
|
|
188
193
|
extensionSelector = undefined;
|
|
189
194
|
extensionInput = undefined;
|
|
@@ -4031,7 +4036,9 @@ export class InteractiveMode {
|
|
|
4031
4036
|
this.ui.requestRender();
|
|
4032
4037
|
};
|
|
4033
4038
|
try {
|
|
4034
|
-
const rawName = (await dialog.showPrompt(isEdit ? "Provider name/id:" : "Provider name/id (e.g. ollama, agnes):",
|
|
4039
|
+
const rawName = (await dialog.showPrompt(isEdit ? "Provider name/id:" : "Provider name/id (e.g. ollama, agnes):", isEdit
|
|
4040
|
+
? { value: existing?.providerId ?? "" }
|
|
4041
|
+
: { placeholder: "ollama", value: existing?.providerId ?? "ollama" })).trim();
|
|
4035
4042
|
const providerId = slugifyProviderId(rawName);
|
|
4036
4043
|
if (!providerId) {
|
|
4037
4044
|
throw new Error("Provider name cannot be empty.");
|
|
@@ -4039,7 +4046,9 @@ export class InteractiveMode {
|
|
|
4039
4046
|
if (BUILT_IN_MODEL_PROVIDERS.has(providerId) && options.target === "openai_compatible") {
|
|
4040
4047
|
throw new Error(`"${providerId}" is a built-in provider. Use a different name, or add it via Manage → models.json override.`);
|
|
4041
4048
|
}
|
|
4042
|
-
const baseUrl = (await dialog.showPrompt("Enter base URL:", existing?.baseUrl
|
|
4049
|
+
const baseUrl = (await dialog.showPrompt("Enter base URL:", isEdit || existing?.baseUrl
|
|
4050
|
+
? { value: existing?.baseUrl || "http://localhost:11434/v1" }
|
|
4051
|
+
: { placeholder: "http://localhost:11434/v1", value: "http://localhost:11434/v1" })).trim();
|
|
4043
4052
|
if (!baseUrl) {
|
|
4044
4053
|
throw new Error("Base URL cannot be empty.");
|
|
4045
4054
|
}
|
|
@@ -4050,7 +4059,8 @@ export class InteractiveMode {
|
|
|
4050
4059
|
: isEdit
|
|
4051
4060
|
? "API key (blank keeps current; type not-needed for local):"
|
|
4052
4061
|
: "Enter API key (or leave blank for local endpoints):";
|
|
4053
|
-
const
|
|
4062
|
+
const apiKeyCurrent = existing?.apiKey === "not-needed" ? "" : (existing?.apiKey ?? "");
|
|
4063
|
+
const apiKey = (await dialog.showPrompt(apiKeyPrompt, isEdit || apiKeyCurrent ? { value: apiKeyCurrent } : { placeholder: "sk-... or leave blank" })).trim();
|
|
4054
4064
|
// Blank on edit keeps the existing apiKey (models.json and openai_compatible).
|
|
4055
4065
|
const effectiveApiKey = apiKey ? apiKey : isEdit ? undefined : "not-needed";
|
|
4056
4066
|
dialog.showInfo([theme.fg("muted", "Discovering models from /models ...")]);
|
|
@@ -4119,7 +4129,7 @@ export class InteractiveMode {
|
|
|
4119
4129
|
]);
|
|
4120
4130
|
this.ui.requestRender();
|
|
4121
4131
|
const defaultIds = existing?.models.map((m) => m.id).join(", ") || "llama3.1";
|
|
4122
|
-
const modelNames = (await dialog.showPrompt("Enter model name(s) (comma-separated):", defaultIds)).trim();
|
|
4132
|
+
const modelNames = (await dialog.showPrompt("Enter model name(s) (comma-separated):", { value: defaultIds })).trim();
|
|
4123
4133
|
if (!modelNames) {
|
|
4124
4134
|
throw new Error("At least one model name is required.");
|
|
4125
4135
|
}
|
|
@@ -4603,6 +4613,9 @@ export class InteractiveMode {
|
|
|
4603
4613
|
const lower = goal.toLowerCase();
|
|
4604
4614
|
if (lower === "on" || lower === "off") {
|
|
4605
4615
|
this.goalRunnerEnabled = lower === "on";
|
|
4616
|
+
if (lower === "on") {
|
|
4617
|
+
this.goalRunnerRecentOutputs = [];
|
|
4618
|
+
}
|
|
4606
4619
|
const currentGoal = this.session.sessionGoal;
|
|
4607
4620
|
if (currentGoal) {
|
|
4608
4621
|
const label = lower === "on" ? theme.fg("success", "enabled") : theme.fg("warning", "paused");
|
|
@@ -4617,6 +4630,7 @@ export class InteractiveMode {
|
|
|
4617
4630
|
}
|
|
4618
4631
|
this.session.setSessionGoal(goal);
|
|
4619
4632
|
this.goalRunnerEnabled = true;
|
|
4633
|
+
this.goalRunnerRecentOutputs = [];
|
|
4620
4634
|
this.chatContainer.addChild(new Spacer(1));
|
|
4621
4635
|
this.chatContainer.addChild(new Text(theme.fg("dim", `Session goal set: ${goal}`), 1, 0));
|
|
4622
4636
|
this.ui.requestRender();
|
|
@@ -4655,9 +4669,41 @@ export class InteractiveMode {
|
|
|
4655
4669
|
this.showStatus(`Pursuing goal: "${goal}" (${keyText("app.interrupt")} to pause)`);
|
|
4656
4670
|
}
|
|
4657
4671
|
try {
|
|
4658
|
-
await this.session.prompt(`Continue pursuing the goal: "${goal}". If you have fully completed this goal, report what was accomplished. If you cannot make further progress, explain what blocked you.`);
|
|
4672
|
+
await this.session.prompt(`Continue pursuing the goal: "${goal}". If you have fully completed this goal, report what was accomplished. If you cannot make further progress, explain what blocked you. Do not repeat the same status/report as previous turns — either make concrete progress or clearly state that the goal is done/blocked.`);
|
|
4659
4673
|
// Successful turn completed, reset retry counter
|
|
4660
4674
|
this.goalRunnerRetryAttempt = 0;
|
|
4675
|
+
// Auto-pause when the model keeps emitting near-duplicate content.
|
|
4676
|
+
const lastText = this.session.getLastAssistantText();
|
|
4677
|
+
if (lastText) {
|
|
4678
|
+
const normalized = normalizeGoalOutput(lastText);
|
|
4679
|
+
const prev = this.goalRunnerRecentOutputs[this.goalRunnerRecentOutputs.length - 1];
|
|
4680
|
+
if (prev && isNearDuplicateGoalOutput(prev, normalized)) {
|
|
4681
|
+
this.goalRunnerRecentOutputs.push(normalized);
|
|
4682
|
+
}
|
|
4683
|
+
else {
|
|
4684
|
+
this.goalRunnerRecentOutputs = [normalized];
|
|
4685
|
+
}
|
|
4686
|
+
// Keep a small window
|
|
4687
|
+
if (this.goalRunnerRecentOutputs.length > this.GOAL_RUNNER_MAX_DUPLICATE_STREAK) {
|
|
4688
|
+
this.goalRunnerRecentOutputs = this.goalRunnerRecentOutputs.slice(-this.GOAL_RUNNER_MAX_DUPLICATE_STREAK);
|
|
4689
|
+
}
|
|
4690
|
+
if (this.goalRunnerRecentOutputs.length >= this.GOAL_RUNNER_MAX_DUPLICATE_STREAK) {
|
|
4691
|
+
const window = this.goalRunnerRecentOutputs;
|
|
4692
|
+
let allDup = true;
|
|
4693
|
+
for (let i = 1; i < window.length; i++) {
|
|
4694
|
+
if (!isNearDuplicateGoalOutput(window[i - 1], window[i])) {
|
|
4695
|
+
allDup = false;
|
|
4696
|
+
break;
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
if (allDup) {
|
|
4700
|
+
this.goalRunnerEnabled = false;
|
|
4701
|
+
this.goalRunnerRecentOutputs = [];
|
|
4702
|
+
this.showStatus(`Goal runner paused: model repeated similar output ${this.GOAL_RUNNER_MAX_DUPLICATE_STREAK} turns in a row. Use /goal on to resume, or refine the goal.`);
|
|
4703
|
+
break;
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4661
4707
|
}
|
|
4662
4708
|
catch (error) {
|
|
4663
4709
|
this.goalRunnerRetryAttempt++;
|