pi-goal-x 0.32.1 → 0.32.3
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.
|
@@ -229,15 +229,17 @@ export function isErrorAssistantMessage(message: unknown): boolean {
|
|
|
229
229
|
/**
|
|
230
230
|
* Transient provider failures are safe for goal-level recovery backoff:
|
|
231
231
|
* Pi-declared network errors plus HTTP 429/5xx-style outages surfaced by
|
|
232
|
-
* providers (429 rate limits, 503 server_error,
|
|
233
|
-
* overloaded, gateway failures). Deliberately excludes auth and
|
|
232
|
+
* providers (429 rate limits, 503 server_error, HTTP/2 PROTOCOL_ERROR,
|
|
233
|
+
* "Endpoint is unavailable", overloaded, gateway failures). Deliberately excludes auth and
|
|
234
234
|
* malformed-request failures (and quota/billing text), which retrying
|
|
235
235
|
* cannot fix.
|
|
236
236
|
*/
|
|
237
237
|
const TRANSIENT_PROVIDER_ERROR_RE = new RegExp(
|
|
238
238
|
[
|
|
239
239
|
"\\bnetwork[_\\s-]?error\\b",
|
|
240
|
+
"\\bprotocol[_\\s-]?error\\b",
|
|
240
241
|
"\\bserver[_\\s]?error\\b",
|
|
242
|
+
"\\bfinish[_\\s-]?reason:\\s*error\\b",
|
|
241
243
|
"\\b(?:429|502|503|504|529)\\b",
|
|
242
244
|
"\\brate[_\\s-]?limited\\b",
|
|
243
245
|
"\\brate[_\\s-]?limit\\b",
|
package/extensions/goal-state.ts
CHANGED
|
@@ -681,6 +681,7 @@ export function createGoalCore(
|
|
|
681
681
|
getExpanded: () => dashboardExpanded,
|
|
682
682
|
getLedgerEvents: () => state.goal ? goalActivityEvents(storage, state.goal.id) : [],
|
|
683
683
|
getAuditResult: () => auditResult,
|
|
684
|
+
componentRef: goalWidgetComponentRef,
|
|
684
685
|
}),
|
|
685
686
|
{ placement: "aboveEditor" },
|
|
686
687
|
);
|
|
@@ -711,6 +712,7 @@ export function createGoalCore(
|
|
|
711
712
|
getExpanded: () => dashboardExpanded,
|
|
712
713
|
getLedgerEvents: () => state.goal ? goalActivityEvents(storage, state.goal.id) : [],
|
|
713
714
|
getAuditResult: () => auditResult,
|
|
715
|
+
componentRef: goalWidgetComponentRef,
|
|
714
716
|
}),
|
|
715
717
|
{ placement: "aboveEditor" },
|
|
716
718
|
);
|
|
@@ -9,6 +9,10 @@ import type { GoalCore } from "./goal-state.ts";
|
|
|
9
9
|
|
|
10
10
|
const DEBUG_GOALS_DIR = ".pi/goals/debug";
|
|
11
11
|
|
|
12
|
+
function isKittyRepeatOrRelease(data: string): boolean {
|
|
13
|
+
return data.startsWith("\x1b[") && /^[0-9:;]+:[23][u~ABCDHF]$/.test(data.slice(2));
|
|
14
|
+
}
|
|
15
|
+
|
|
12
16
|
/**
|
|
13
17
|
* Terminal input keybindings (Escape pause/abort-audit, Ctrl+Shift+T dashboard
|
|
14
18
|
* overlay, and the hidden debug-mode bindings) plus the debug goal/task/audit
|
|
@@ -111,8 +115,7 @@ export async function toggleTaskViaService(core: GoalCore, ctx: ExtensionContext
|
|
|
111
115
|
export function syncTerminalInputPause(core: GoalCore, ctx: ExtensionContext): void {
|
|
112
116
|
if (!ctx.hasUI) return;
|
|
113
117
|
core.terminalInputUnsubscribe?.();
|
|
114
|
-
const
|
|
115
|
-
const keybindings = settings.keybindings?.dashboard ?? DEFAULT_GOAL_KEYBINDINGS.dashboard;
|
|
118
|
+
const goalCwd = typeof ctx.cwd === "string" ? ctx.cwd : process.cwd();
|
|
116
119
|
core.terminalInputUnsubscribe = ctx.ui.onTerminalInput((data) => {
|
|
117
120
|
// If an audit is running, Escape aborts the audit instead of pausing.
|
|
118
121
|
// Must return { consume: true } so the TUI doesn't also process the key
|
|
@@ -128,6 +131,15 @@ export function syncTerminalInputPause(core: GoalCore, ctx: ExtensionContext): v
|
|
|
128
131
|
// before the dialog could process it (bn-l pattern). Depth counter so
|
|
129
132
|
// nested goal modals remain guarded.
|
|
130
133
|
if (core.goalModalDepth > 0) return undefined;
|
|
134
|
+
const keybindings = loadGoalSettings(goalCwd).keybindings?.dashboard ?? DEFAULT_GOAL_KEYBINDINGS.dashboard;
|
|
135
|
+
if (isKittyRepeatOrRelease(data)) {
|
|
136
|
+
const goalShortcut = matchesKey(data, keybindings.toggleExpand) || matchesKey(data, "ctrl+shift+a") ||
|
|
137
|
+
(matchesKey(data, "escape") && (core.isDashboardExpanded() || core.state.goal !== null)) ||
|
|
138
|
+
(core.isDashboardExpanded() && (["up", "down", "pageUp", "pageDown", "home", "end"] as const).some(key => matchesKey(data, key))) ||
|
|
139
|
+
(!core.isDashboardExpanded() && core.goalWidgetComponentRef.current !== null &&
|
|
140
|
+
([keybindings.scrollUp, keybindings.scrollDown, "ctrl+shift+pageUp", "ctrl+shift+pageDown", "ctrl+shift+home", "ctrl+shift+end"] as const).some(key => matchesKey(data, key)));
|
|
141
|
+
return goalShortcut ? { consume: true } : undefined;
|
|
142
|
+
}
|
|
131
143
|
if (matchesKey(data, "escape") && core.auditProgress) {
|
|
132
144
|
core.abortAudit(ctx);
|
|
133
145
|
return { consume: true };
|
|
@@ -82,20 +82,26 @@ export function makeGoalWidgetFactory(opts: {
|
|
|
82
82
|
getExpanded?: () => boolean;
|
|
83
83
|
getLedgerEvents?: () => GoalLedgerEvent[];
|
|
84
84
|
getAuditResult?: () => AuditResultView | null;
|
|
85
|
+
/** Receives the live widget so terminal shortcuts can scroll its viewports. */
|
|
86
|
+
componentRef?: { current: GoalWidgetComponent | null };
|
|
85
87
|
}) {
|
|
86
|
-
return (tui: TUI, theme: Theme) =>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
88
|
+
return (tui: TUI, theme: Theme) => {
|
|
89
|
+
const component = new GoalWidgetComponent({
|
|
90
|
+
tui,
|
|
91
|
+
theme,
|
|
92
|
+
getGoal: opts.getGoal,
|
|
93
|
+
getOpenGoalCount: opts.getOpenGoalCount,
|
|
94
|
+
getAuditorProgress: opts.getAuditorProgress,
|
|
95
|
+
getSettings: opts.getSettings,
|
|
96
|
+
getDebugMode: opts.getDebugMode,
|
|
97
|
+
getStalled: opts.getStalled,
|
|
98
|
+
getExpanded: opts.getExpanded,
|
|
99
|
+
getLedgerEvents: opts.getLedgerEvents,
|
|
100
|
+
getAuditResult: opts.getAuditResult,
|
|
101
|
+
});
|
|
102
|
+
if (opts.componentRef) opts.componentRef.current = component;
|
|
103
|
+
return component;
|
|
104
|
+
};
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
export interface AuditorWidgetProgress {
|
|
@@ -623,6 +629,7 @@ export class GoalWidgetComponent implements Component {
|
|
|
623
629
|
* list. Returns true when the key was consumed.
|
|
624
630
|
*/
|
|
625
631
|
handleCompactScrollKey(key: "up" | "down" | "pageUp" | "pageDown" | "home" | "end"): boolean {
|
|
632
|
+
if (this.getExpanded()) return false;
|
|
626
633
|
const settings = this.getSettings();
|
|
627
634
|
const goal = this.getGoal();
|
|
628
635
|
const model = goal ? deriveGoalDashboardModel(goal as GoalRecord | null, {
|
|
@@ -632,6 +639,7 @@ export class GoalWidgetComponent implements Component {
|
|
|
632
639
|
tasksDisabled: settings.disableTasks === true,
|
|
633
640
|
maxAutonomousRuns: settings.maxAutonomousRuns,
|
|
634
641
|
}) : null;
|
|
642
|
+
this.maybeReanchor(model);
|
|
635
643
|
const list = model?.taskTree.filter((n) => n.depth === 0) ?? [];
|
|
636
644
|
const rows = compactTaskViewportRows(this.lastRenderWidth);
|
|
637
645
|
if (list.length <= rows) return false;
|
|
@@ -667,11 +675,12 @@ export class GoalWidgetComponent implements Component {
|
|
|
667
675
|
tasksDisabled: settings.disableTasks === true,
|
|
668
676
|
maxAutonomousRuns: settings.maxAutonomousRuns,
|
|
669
677
|
}) : null;
|
|
678
|
+
this.maybeReanchor(model);
|
|
670
679
|
const list = model?.taskTree ?? [];
|
|
671
680
|
if (list.length === 0) return false;
|
|
672
681
|
const rows = expandedTaskViewportRows(this.lastRenderWidth);
|
|
673
682
|
const maxO = maxScrollOffset(list.length, rows);
|
|
674
|
-
if (maxO <= 0) return
|
|
683
|
+
if (maxO <= 0) return true;
|
|
675
684
|
let offset = clampScrollOffset(this.expandedScrollOffset, list.length, rows);
|
|
676
685
|
if (key === "up") offset -= 1;
|
|
677
686
|
else if (key === "down") offset += 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-x",
|
|
3
|
-
"version": "0.32.
|
|
3
|
+
"version": "0.32.3",
|
|
4
4
|
"description": "Adds /goal to pi: conversational goal planning, flexible or ordered goals, persistent progress, and an independent completion auditor.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "pi-goal-x contributors",
|