gsd-pi 2.37.1 → 2.38.0-dev.63ad7e5
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/README.md +1 -1
- package/dist/app-paths.js +1 -1
- package/dist/cli.js +9 -0
- package/dist/extension-discovery.d.ts +5 -3
- package/dist/extension-discovery.js +14 -9
- package/dist/extension-registry.js +2 -2
- package/dist/onboarding.js +1 -0
- package/dist/remote-questions-config.js +2 -2
- package/dist/resources/extensions/browser-tools/package.json +3 -1
- package/dist/resources/extensions/cmux/index.js +55 -1
- package/dist/resources/extensions/context7/package.json +1 -1
- package/dist/resources/extensions/env-utils.js +29 -0
- package/dist/resources/extensions/get-secrets-from-user.js +5 -24
- package/dist/resources/extensions/google-search/package.json +3 -1
- package/dist/resources/extensions/gsd/auto/session.js +6 -23
- package/dist/resources/extensions/gsd/auto-dispatch.js +74 -9
- package/dist/resources/extensions/gsd/auto-loop.js +68 -97
- package/dist/resources/extensions/gsd/auto-post-unit.js +87 -69
- package/dist/resources/extensions/gsd/auto-prompts.js +98 -33
- package/dist/resources/extensions/gsd/auto-recovery.js +37 -1
- package/dist/resources/extensions/gsd/auto-start.js +13 -2
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
- package/dist/resources/extensions/gsd/auto.js +143 -96
- package/dist/resources/extensions/gsd/captures.js +9 -1
- package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +16 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +22 -2
- package/dist/resources/extensions/gsd/context-budget.js +2 -10
- package/dist/resources/extensions/gsd/detection.js +1 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
- package/dist/resources/extensions/gsd/doctor-format.js +15 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +62 -12
- package/dist/resources/extensions/gsd/doctor.js +184 -11
- package/dist/resources/extensions/gsd/export.js +1 -1
- package/dist/resources/extensions/gsd/files.js +43 -2
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/index.js +2 -1
- package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
- package/dist/resources/extensions/gsd/observability-validator.js +24 -0
- package/dist/resources/extensions/gsd/package.json +1 -1
- package/dist/resources/extensions/gsd/preferences-models.js +0 -12
- package/dist/resources/extensions/gsd/preferences-types.js +2 -2
- package/dist/resources/extensions/gsd/preferences-validation.js +43 -11
- package/dist/resources/extensions/gsd/preferences.js +5 -5
- package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
- package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -10
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/dist/resources/extensions/gsd/reactive-graph.js +227 -0
- package/dist/resources/extensions/gsd/repo-identity.js +21 -4
- package/dist/resources/extensions/gsd/resource-version.js +2 -1
- package/dist/resources/extensions/gsd/state.js +1 -1
- package/dist/resources/extensions/gsd/templates/task-plan.md +11 -3
- package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
- package/dist/resources/extensions/gsd/worktree.js +35 -16
- package/dist/resources/extensions/remote-questions/status.js +2 -1
- package/dist/resources/extensions/remote-questions/store.js +2 -1
- package/dist/resources/extensions/search-the-web/provider.js +2 -1
- package/dist/resources/extensions/subagent/index.js +12 -3
- package/dist/resources/extensions/subagent/isolation.js +2 -1
- package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
- package/dist/resources/extensions/universal-config/package.json +1 -1
- package/dist/welcome-screen.d.ts +12 -0
- package/dist/welcome-screen.js +53 -0
- package/package.json +2 -1
- package/packages/pi-ai/dist/env-api-keys.js +13 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +172 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +172 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +64 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.js +668 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts +5 -0
- package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-vertex.js +85 -0
- package/packages/pi-ai/dist/providers/anthropic-vertex.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic.d.ts +4 -30
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +47 -764
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.js +6 -0
- package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -2
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -0
- package/packages/pi-ai/src/env-api-keys.ts +14 -0
- package/packages/pi-ai/src/models.generated.ts +172 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +761 -0
- package/packages/pi-ai/src/providers/anthropic-vertex.ts +130 -0
- package/packages/pi-ai/src/providers/anthropic.ts +76 -868
- package/packages/pi-ai/src/providers/register-builtins.ts +7 -0
- package/packages/pi-ai/src/types.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
- package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
- package/pkg/package.json +1 -1
- package/src/resources/extensions/cmux/index.ts +57 -1
- package/src/resources/extensions/env-utils.ts +31 -0
- package/src/resources/extensions/get-secrets-from-user.ts +5 -24
- package/src/resources/extensions/gsd/auto/session.ts +7 -25
- package/src/resources/extensions/gsd/auto-dispatch.ts +99 -8
- package/src/resources/extensions/gsd/auto-loop.ts +88 -133
- package/src/resources/extensions/gsd/auto-post-unit.ts +64 -40
- package/src/resources/extensions/gsd/auto-prompts.ts +132 -36
- package/src/resources/extensions/gsd/auto-recovery.ts +42 -0
- package/src/resources/extensions/gsd/auto-start.ts +18 -2
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
- package/src/resources/extensions/gsd/auto.ts +139 -101
- package/src/resources/extensions/gsd/captures.ts +10 -1
- package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +17 -2
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +24 -2
- package/src/resources/extensions/gsd/context-budget.ts +2 -12
- package/src/resources/extensions/gsd/detection.ts +2 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
- package/src/resources/extensions/gsd/doctor-format.ts +20 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +64 -10
- package/src/resources/extensions/gsd/doctor-types.ts +16 -1
- package/src/resources/extensions/gsd/doctor.ts +177 -13
- package/src/resources/extensions/gsd/export.ts +1 -1
- package/src/resources/extensions/gsd/files.ts +47 -2
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/index.ts +3 -1
- package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
- package/src/resources/extensions/gsd/observability-validator.ts +27 -0
- package/src/resources/extensions/gsd/preferences-models.ts +0 -12
- package/src/resources/extensions/gsd/preferences-types.ts +5 -5
- package/src/resources/extensions/gsd/preferences-validation.ts +42 -11
- package/src/resources/extensions/gsd/preferences.ts +5 -5
- package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -8
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +25 -10
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/src/resources/extensions/gsd/reactive-graph.ts +289 -0
- package/src/resources/extensions/gsd/repo-identity.ts +23 -4
- package/src/resources/extensions/gsd/resource-version.ts +3 -1
- package/src/resources/extensions/gsd/state.ts +1 -1
- package/src/resources/extensions/gsd/templates/task-plan.md +11 -3
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +11 -31
- package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +191 -3
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +111 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +511 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +299 -0
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +11 -3
- package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
- package/src/resources/extensions/gsd/types.ts +43 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
- package/src/resources/extensions/gsd/worktree.ts +35 -15
- package/src/resources/extensions/remote-questions/status.ts +3 -1
- package/src/resources/extensions/remote-questions/store.ts +3 -1
- package/src/resources/extensions/search-the-web/provider.ts +2 -1
- package/src/resources/extensions/subagent/index.ts +12 -3
- package/src/resources/extensions/subagent/isolation.ts +3 -1
- package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
- package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
- package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
- package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
- package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
- package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
- package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
- package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
- package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
- package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
- package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
- package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* pattern with a while loop. The agent_end event resolves a promise instead
|
|
6
6
|
* of recursing.
|
|
7
7
|
*
|
|
8
|
-
* MAINTENANCE RULE:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* MAINTENANCE RULE: Module-level mutable state is limited to `_currentResolve`
|
|
9
|
+
* (per-unit one-shot resolver) and `_sessionSwitchInFlight` (guard for
|
|
10
|
+
* session rotation). No queue — stale agent_end events are dropped.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
@@ -18,7 +18,7 @@ import type { GSDPreferences } from "./preferences.js";
|
|
|
18
18
|
import type { SessionLockStatus } from "./session-lock.js";
|
|
19
19
|
import type { GSDState } from "./types.js";
|
|
20
20
|
import type { CloseoutOptions } from "./auto-unit-closeout.js";
|
|
21
|
-
import type { PostUnitContext } from "./auto-post-unit.js";
|
|
21
|
+
import type { PostUnitContext, PreVerificationOpts } from "./auto-post-unit.js";
|
|
22
22
|
import type {
|
|
23
23
|
VerificationContext,
|
|
24
24
|
VerificationResult,
|
|
@@ -36,6 +36,19 @@ import type { CmuxLogLevel } from "../cmux/index.js";
|
|
|
36
36
|
*/
|
|
37
37
|
const MAX_LOOP_ITERATIONS = 500;
|
|
38
38
|
|
|
39
|
+
/** Data-driven budget threshold notifications (75/80/90%). The 100% case is
|
|
40
|
+
* handled inline because it requires break/pause/stop control flow. */
|
|
41
|
+
const BUDGET_THRESHOLDS: Array<{
|
|
42
|
+
pct: number;
|
|
43
|
+
label: string;
|
|
44
|
+
notifyLevel: "info" | "warning";
|
|
45
|
+
cmuxLevel: "progress" | "warning";
|
|
46
|
+
}> = [
|
|
47
|
+
{ pct: 90, label: "Budget 90%", notifyLevel: "warning", cmuxLevel: "warning" },
|
|
48
|
+
{ pct: 80, label: "Approaching budget ceiling — 80%", notifyLevel: "warning", cmuxLevel: "warning" },
|
|
49
|
+
{ pct: 75, label: "Budget 75%", notifyLevel: "info", cmuxLevel: "progress" },
|
|
50
|
+
];
|
|
51
|
+
|
|
39
52
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
40
53
|
|
|
41
54
|
/**
|
|
@@ -54,17 +67,15 @@ export interface UnitResult {
|
|
|
54
67
|
event?: AgentEndEvent;
|
|
55
68
|
}
|
|
56
69
|
|
|
57
|
-
// ───
|
|
70
|
+
// ─── Per-unit one-shot promise state ────────────────────────────────────────
|
|
58
71
|
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
72
|
+
// A single module-level resolve function scoped to the current unit execution.
|
|
73
|
+
// No queue — if an agent_end arrives with no pending resolver, it is dropped
|
|
74
|
+
// (logged as warning). This is simpler and safer than the previous session-
|
|
75
|
+
// scoped pendingResolve + pendingAgentEndQueue pattern.
|
|
61
76
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
* on entry so that the agent_end handler in index.ts can resolve the correct
|
|
65
|
-
* session's promise without needing a direct reference to `s`.
|
|
66
|
-
*/
|
|
67
|
-
let _activeSession: AutoSession | null = null;
|
|
77
|
+
let _currentResolve: ((result: UnitResult) => void) | null = null;
|
|
78
|
+
let _sessionSwitchInFlight = false;
|
|
68
79
|
|
|
69
80
|
// ─── resolveAgentEnd ─────────────────────────────────────────────────────────
|
|
70
81
|
|
|
@@ -73,60 +84,48 @@ let _activeSession: AutoSession | null = null;
|
|
|
73
84
|
* in-flight unit promise. One-shot: the resolver is nulled before calling
|
|
74
85
|
* to prevent double-resolution from model fallback retries.
|
|
75
86
|
*
|
|
76
|
-
* If no
|
|
77
|
-
* the event is
|
|
87
|
+
* If no resolver exists (event arrived between loop iterations or during
|
|
88
|
+
* session switch), the event is dropped with a debug warning.
|
|
78
89
|
*/
|
|
79
90
|
export function resolveAgentEnd(event: AgentEndEvent): void {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
debugLog("resolveAgentEnd", {
|
|
83
|
-
status: "no-active-session",
|
|
84
|
-
warning: "agent_end with no active loop session",
|
|
85
|
-
});
|
|
91
|
+
if (_sessionSwitchInFlight) {
|
|
92
|
+
debugLog("resolveAgentEnd", { status: "ignored-during-switch" });
|
|
86
93
|
return;
|
|
87
94
|
}
|
|
88
|
-
|
|
89
|
-
if (s.pendingResolve) {
|
|
95
|
+
if (_currentResolve) {
|
|
90
96
|
debugLog("resolveAgentEnd", { status: "resolving", hasEvent: true });
|
|
91
|
-
const r =
|
|
92
|
-
|
|
97
|
+
const r = _currentResolve;
|
|
98
|
+
_currentResolve = null;
|
|
93
99
|
r({ status: "completed", event });
|
|
94
100
|
} else {
|
|
95
|
-
// Queue the event so the next runUnit picks it up immediately
|
|
96
101
|
debugLog("resolveAgentEnd", {
|
|
97
|
-
status: "
|
|
98
|
-
|
|
99
|
-
warning:
|
|
100
|
-
"agent_end arrived between loop iterations — queued for next runUnit",
|
|
102
|
+
status: "no-pending-resolve",
|
|
103
|
+
warning: "agent_end with no pending unit",
|
|
101
104
|
});
|
|
102
|
-
s.pendingAgentEndQueue.push(event);
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
export function isSessionSwitchInFlight(): boolean {
|
|
107
|
-
return
|
|
109
|
+
return _sessionSwitchInFlight;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
// ─── resetPendingResolve (test helper) ───────────────────────────────────────
|
|
111
113
|
|
|
112
114
|
/**
|
|
113
|
-
* Reset
|
|
114
|
-
* should never call this.
|
|
115
|
+
* Reset module-level promise state. Only exported for test cleanup —
|
|
116
|
+
* production code should never call this.
|
|
115
117
|
*/
|
|
116
118
|
export function _resetPendingResolve(): void {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
_activeSession.pendingAgentEndQueue = [];
|
|
120
|
-
}
|
|
121
|
-
_activeSession = null;
|
|
119
|
+
_currentResolve = null;
|
|
120
|
+
_sessionSwitchInFlight = false;
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
124
|
+
* No-op for backward compatibility with tests that previously set the
|
|
125
|
+
* active session. The module no longer holds a session reference.
|
|
127
126
|
*/
|
|
128
|
-
export function _setActiveSession(
|
|
129
|
-
|
|
127
|
+
export function _setActiveSession(_session: AutoSession | null): void {
|
|
128
|
+
// No-op — kept for test backward compatibility
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
// ─── runUnit ─────────────────────────────────────────────────────────────────
|
|
@@ -150,41 +149,15 @@ export async function runUnit(
|
|
|
150
149
|
): Promise<UnitResult> {
|
|
151
150
|
debugLog("runUnit", { phase: "start", unitType, unitId });
|
|
152
151
|
|
|
153
|
-
// ── Drain queued events from error-recovery retries ──
|
|
154
|
-
// If an agent_end arrived between iterations (e.g. from a model fallback
|
|
155
|
-
// sendMessage retry), consume it immediately instead of creating a new promise.
|
|
156
|
-
// Cap queue to 3 entries to prevent unbounded growth from stale events.
|
|
157
|
-
if (s.pendingAgentEndQueue.length > 3) {
|
|
158
|
-
debugLog("runUnit", {
|
|
159
|
-
phase: "queue-overflow",
|
|
160
|
-
dropped: s.pendingAgentEndQueue.length - 1,
|
|
161
|
-
unitType,
|
|
162
|
-
unitId,
|
|
163
|
-
});
|
|
164
|
-
s.pendingAgentEndQueue = [
|
|
165
|
-
s.pendingAgentEndQueue[s.pendingAgentEndQueue.length - 1]!,
|
|
166
|
-
];
|
|
167
|
-
}
|
|
168
|
-
if (s.pendingAgentEndQueue.length > 0) {
|
|
169
|
-
const queued = s.pendingAgentEndQueue.shift()!;
|
|
170
|
-
debugLog("runUnit", {
|
|
171
|
-
phase: "drained-queued-event",
|
|
172
|
-
unitType,
|
|
173
|
-
unitId,
|
|
174
|
-
queueRemaining: s.pendingAgentEndQueue.length,
|
|
175
|
-
});
|
|
176
|
-
return { status: "completed", event: queued };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
152
|
// ── Session creation with timeout ──
|
|
180
153
|
debugLog("runUnit", { phase: "session-create", unitType, unitId });
|
|
181
154
|
|
|
182
155
|
let sessionResult: { cancelled: boolean };
|
|
183
156
|
let sessionTimeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
184
|
-
|
|
157
|
+
_sessionSwitchInFlight = true;
|
|
185
158
|
try {
|
|
186
159
|
const sessionPromise = s.cmdCtx!.newSession().finally(() => {
|
|
187
|
-
|
|
160
|
+
_sessionSwitchInFlight = false;
|
|
188
161
|
});
|
|
189
162
|
const timeoutPromise = new Promise<{ cancelled: true }>((resolve) => {
|
|
190
163
|
sessionTimeoutHandle = setTimeout(
|
|
@@ -216,11 +189,12 @@ export async function runUnit(
|
|
|
216
189
|
return { status: "cancelled" };
|
|
217
190
|
}
|
|
218
191
|
|
|
219
|
-
// ── Create the agent_end promise (
|
|
192
|
+
// ── Create the agent_end promise (per-unit one-shot) ──
|
|
220
193
|
// This happens after newSession completes so session-switch agent_end events
|
|
221
194
|
// from the previous session cannot resolve the new unit.
|
|
195
|
+
_sessionSwitchInFlight = false;
|
|
222
196
|
const unitPromise = new Promise<UnitResult>((resolve) => {
|
|
223
|
-
|
|
197
|
+
_currentResolve = resolve;
|
|
224
198
|
});
|
|
225
199
|
|
|
226
200
|
// Ensure cwd matches basePath before dispatch (#1389).
|
|
@@ -383,6 +357,7 @@ export interface LoopDeps {
|
|
|
383
357
|
midTitle: string;
|
|
384
358
|
state: GSDState;
|
|
385
359
|
prefs: GSDPreferences | undefined;
|
|
360
|
+
session?: AutoSession;
|
|
386
361
|
}) => Promise<DispatchAction>;
|
|
387
362
|
runPreDispatchHooks: (
|
|
388
363
|
unitType: string,
|
|
@@ -500,6 +475,7 @@ export interface LoopDeps {
|
|
|
500
475
|
// Post-unit processing
|
|
501
476
|
postUnitPreVerification: (
|
|
502
477
|
pctx: PostUnitContext,
|
|
478
|
+
opts?: PreVerificationOpts,
|
|
503
479
|
) => Promise<"dispatched" | "continue">;
|
|
504
480
|
runPostUnitVerification: (
|
|
505
481
|
vctx: VerificationContext,
|
|
@@ -530,7 +506,6 @@ export async function autoLoop(
|
|
|
530
506
|
deps: LoopDeps,
|
|
531
507
|
): Promise<void> {
|
|
532
508
|
debugLog("autoLoop", { phase: "enter" });
|
|
533
|
-
_activeSession = s;
|
|
534
509
|
let iteration = 0;
|
|
535
510
|
let lastDerivedUnit = "";
|
|
536
511
|
let sameUnitCount = 0;
|
|
@@ -787,7 +762,7 @@ export async function autoLoop(
|
|
|
787
762
|
(m: { status: string }) =>
|
|
788
763
|
m.status !== "complete" && m.status !== "parked",
|
|
789
764
|
);
|
|
790
|
-
if (incomplete.length === 0) {
|
|
765
|
+
if (incomplete.length === 0 && state.registry.length > 0) {
|
|
791
766
|
// All milestones complete — merge milestone branch before stopping
|
|
792
767
|
if (s.currentMilestoneId) {
|
|
793
768
|
deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
|
|
@@ -804,6 +779,18 @@ export async function autoLoop(
|
|
|
804
779
|
"success",
|
|
805
780
|
);
|
|
806
781
|
await deps.stopAuto(ctx, pi, "All milestones complete");
|
|
782
|
+
} else if (incomplete.length === 0 && state.registry.length === 0) {
|
|
783
|
+
// Empty registry — no milestones visible, likely a path resolution bug
|
|
784
|
+
const diag = `basePath=${s.basePath}, phase=${state.phase}`;
|
|
785
|
+
ctx.ui.notify(
|
|
786
|
+
`No milestones visible in current scope. Possible path resolution issue.\n Diagnostic: ${diag}`,
|
|
787
|
+
"error",
|
|
788
|
+
);
|
|
789
|
+
await deps.stopAuto(
|
|
790
|
+
ctx,
|
|
791
|
+
pi,
|
|
792
|
+
`No milestones found — check basePath resolution`,
|
|
793
|
+
);
|
|
807
794
|
} else if (state.phase === "blocked") {
|
|
808
795
|
const blockerMsg = `Blocked: ${state.blockers.join(", ")}`;
|
|
809
796
|
await deps.stopAuto(ctx, pi, blockerMsg);
|
|
@@ -965,62 +952,26 @@ export async function autoLoop(
|
|
|
965
952
|
ctx.ui.notify(`${msg} Continuing (enforcement: warn).`, "warning");
|
|
966
953
|
deps.sendDesktopNotification("GSD", msg, "warning", "budget");
|
|
967
954
|
deps.logCmuxEvent(prefs, msg, "warning");
|
|
968
|
-
} else
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
`Budget 90%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
973
|
-
"warning",
|
|
974
|
-
);
|
|
975
|
-
deps.sendDesktopNotification(
|
|
976
|
-
"GSD",
|
|
977
|
-
`Budget 90%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
978
|
-
"warning",
|
|
979
|
-
"budget",
|
|
980
|
-
);
|
|
981
|
-
deps.logCmuxEvent(
|
|
982
|
-
prefs,
|
|
983
|
-
`Budget 90%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
984
|
-
"warning",
|
|
985
|
-
);
|
|
986
|
-
} else if (newBudgetAlertLevel === 80) {
|
|
987
|
-
s.lastBudgetAlertLevel =
|
|
988
|
-
newBudgetAlertLevel as AutoSession["lastBudgetAlertLevel"];
|
|
989
|
-
ctx.ui.notify(
|
|
990
|
-
`Approaching budget ceiling — 80%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
991
|
-
"warning",
|
|
992
|
-
);
|
|
993
|
-
deps.sendDesktopNotification(
|
|
994
|
-
"GSD",
|
|
995
|
-
`Approaching budget ceiling — 80%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
996
|
-
"warning",
|
|
997
|
-
"budget",
|
|
998
|
-
);
|
|
999
|
-
deps.logCmuxEvent(
|
|
1000
|
-
prefs,
|
|
1001
|
-
`Budget 80%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
1002
|
-
"warning",
|
|
1003
|
-
);
|
|
1004
|
-
} else if (newBudgetAlertLevel === 75) {
|
|
1005
|
-
s.lastBudgetAlertLevel =
|
|
1006
|
-
newBudgetAlertLevel as AutoSession["lastBudgetAlertLevel"];
|
|
1007
|
-
ctx.ui.notify(
|
|
1008
|
-
`Budget 75%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
1009
|
-
"info",
|
|
1010
|
-
);
|
|
1011
|
-
deps.sendDesktopNotification(
|
|
1012
|
-
"GSD",
|
|
1013
|
-
`Budget 75%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
1014
|
-
"info",
|
|
1015
|
-
"budget",
|
|
1016
|
-
);
|
|
1017
|
-
deps.logCmuxEvent(
|
|
1018
|
-
prefs,
|
|
1019
|
-
`Budget 75%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`,
|
|
1020
|
-
"progress",
|
|
955
|
+
} else {
|
|
956
|
+
// Data-driven 75/80/90% threshold notifications
|
|
957
|
+
const threshold = BUDGET_THRESHOLDS.find(
|
|
958
|
+
(t) => newBudgetAlertLevel === t.pct,
|
|
1021
959
|
);
|
|
1022
|
-
|
|
1023
|
-
|
|
960
|
+
if (threshold) {
|
|
961
|
+
s.lastBudgetAlertLevel =
|
|
962
|
+
newBudgetAlertLevel as AutoSession["lastBudgetAlertLevel"];
|
|
963
|
+
const msg = `${threshold.label}: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`;
|
|
964
|
+
ctx.ui.notify(msg, threshold.notifyLevel);
|
|
965
|
+
deps.sendDesktopNotification(
|
|
966
|
+
"GSD",
|
|
967
|
+
msg,
|
|
968
|
+
threshold.notifyLevel,
|
|
969
|
+
"budget",
|
|
970
|
+
);
|
|
971
|
+
deps.logCmuxEvent(prefs, msg, threshold.cmuxLevel);
|
|
972
|
+
} else if (budgetAlertLevel === 0) {
|
|
973
|
+
s.lastBudgetAlertLevel = 0;
|
|
974
|
+
}
|
|
1024
975
|
}
|
|
1025
976
|
} else {
|
|
1026
977
|
s.lastBudgetAlertLevel = 0;
|
|
@@ -1091,6 +1042,7 @@ export async function autoLoop(
|
|
|
1091
1042
|
midTitle: midTitle!,
|
|
1092
1043
|
state,
|
|
1093
1044
|
prefs,
|
|
1045
|
+
session: s,
|
|
1094
1046
|
});
|
|
1095
1047
|
|
|
1096
1048
|
if (dispatchResult.action === "stop") {
|
|
@@ -1649,9 +1601,12 @@ export async function autoLoop(
|
|
|
1649
1601
|
break;
|
|
1650
1602
|
}
|
|
1651
1603
|
|
|
1652
|
-
// Run pre-verification for the sidecar unit
|
|
1604
|
+
// Run pre-verification for the sidecar unit (lightweight path)
|
|
1605
|
+
const sidecarPreOpts: PreVerificationOpts = item.kind === "hook"
|
|
1606
|
+
? { skipSettleDelay: true, skipDoctor: true, skipStateRebuild: true, skipWorktreeSync: true }
|
|
1607
|
+
: { skipSettleDelay: true, skipStateRebuild: true };
|
|
1653
1608
|
const sidecarPreResult =
|
|
1654
|
-
await deps.postUnitPreVerification(postUnitCtx);
|
|
1609
|
+
await deps.postUnitPreVerification(postUnitCtx, sidecarPreOpts);
|
|
1655
1610
|
if (sidecarPreResult === "dispatched") {
|
|
1656
1611
|
// Pre-verification caused stop/pause
|
|
1657
1612
|
debugLog("autoLoop", {
|
|
@@ -1740,6 +1695,6 @@ export async function autoLoop(
|
|
|
1740
1695
|
}
|
|
1741
1696
|
}
|
|
1742
1697
|
|
|
1743
|
-
|
|
1698
|
+
_currentResolve = null;
|
|
1744
1699
|
debugLog("autoLoop", { phase: "exit", totalIterations: iteration });
|
|
1745
1700
|
}
|
|
@@ -33,7 +33,6 @@ import { writeUnitRuntimeRecord, clearUnitRuntimeRecord } from "./unit-runtime.j
|
|
|
33
33
|
import { runGSDDoctor, rebuildState, summarizeDoctorIssues } from "./doctor.js";
|
|
34
34
|
import { recordHealthSnapshot, checkHealEscalation } from "./doctor-proactive.js";
|
|
35
35
|
import { syncStateToProjectRoot } from "./auto-worktree-sync.js";
|
|
36
|
-
import { resetRewriteCircuitBreaker } from "./auto-dispatch.js";
|
|
37
36
|
import { isDbAvailable } from "./gsd-db.js";
|
|
38
37
|
import { consumeSignal } from "./session-status-io.js";
|
|
39
38
|
import {
|
|
@@ -56,6 +55,13 @@ import { join } from "node:path";
|
|
|
56
55
|
/** Throttle STATE.md rebuilds — at most once per 30 seconds */
|
|
57
56
|
const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
58
57
|
|
|
58
|
+
export interface PreVerificationOpts {
|
|
59
|
+
skipSettleDelay?: boolean;
|
|
60
|
+
skipDoctor?: boolean;
|
|
61
|
+
skipStateRebuild?: boolean;
|
|
62
|
+
skipWorktreeSync?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
59
65
|
export interface PostUnitContext {
|
|
60
66
|
s: AutoSession;
|
|
61
67
|
ctx: ExtensionContext;
|
|
@@ -73,7 +79,7 @@ export interface PostUnitContext {
|
|
|
73
79
|
*
|
|
74
80
|
* Returns "dispatched" if a signal caused stop/pause, "continue" to proceed.
|
|
75
81
|
*/
|
|
76
|
-
export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"dispatched" | "continue"> {
|
|
82
|
+
export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreVerificationOpts): Promise<"dispatched" | "continue"> {
|
|
77
83
|
const { s, ctx, pi, buildSnapshotOpts, stopAuto, pauseAuto } = pctx;
|
|
78
84
|
|
|
79
85
|
// ── Parallel worker signal check ──
|
|
@@ -95,8 +101,10 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
95
101
|
// Invalidate all caches
|
|
96
102
|
invalidateAllCaches();
|
|
97
103
|
|
|
98
|
-
// Small delay to let files settle
|
|
99
|
-
|
|
104
|
+
// Small delay to let files settle (skipped for sidecars where latency matters more)
|
|
105
|
+
if (!opts?.skipSettleDelay) {
|
|
106
|
+
await new Promise(r => setTimeout(r, 100));
|
|
107
|
+
}
|
|
100
108
|
|
|
101
109
|
// Auto-commit
|
|
102
110
|
if (s.currentUnit) {
|
|
@@ -120,8 +128,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
120
128
|
keyFiles: summary.frontmatter.key_files?.filter(f => !f.includes("{{")) || undefined,
|
|
121
129
|
};
|
|
122
130
|
}
|
|
123
|
-
} catch {
|
|
124
|
-
|
|
131
|
+
} catch (e) {
|
|
132
|
+
debugLog("postUnit", { phase: "task-summary-parse", error: String(e) });
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
}
|
|
@@ -131,12 +139,12 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
131
139
|
if (commitMsg) {
|
|
132
140
|
ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
|
|
133
141
|
}
|
|
134
|
-
} catch {
|
|
135
|
-
|
|
142
|
+
} catch (e) {
|
|
143
|
+
debugLog("postUnit", { phase: "auto-commit", error: String(e) });
|
|
136
144
|
}
|
|
137
145
|
|
|
138
|
-
// Doctor: fix mechanical bookkeeping
|
|
139
|
-
try {
|
|
146
|
+
// Doctor: fix mechanical bookkeeping (skipped for lightweight sidecars)
|
|
147
|
+
if (!opts?.skipDoctor) try {
|
|
140
148
|
const scopeParts = s.currentUnit.id.split("/").slice(0, 2);
|
|
141
149
|
const doctorScope = scopeParts.join("/");
|
|
142
150
|
const sliceTerminalUnits = new Set(["complete-slice", "run-uat"]);
|
|
@@ -168,24 +176,26 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
168
176
|
const reportText = formatDoctorReport(report, { scope: doctorScope, includeWarnings: true });
|
|
169
177
|
const structuredIssues = formatDoctorIssuesForPrompt(actionable);
|
|
170
178
|
dispatchDoctorHeal(pi, doctorScope, reportText, structuredIssues);
|
|
171
|
-
} catch {
|
|
172
|
-
|
|
179
|
+
} catch (e) {
|
|
180
|
+
debugLog("postUnit", { phase: "doctor-heal-dispatch", error: String(e) });
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
}
|
|
176
|
-
} catch {
|
|
177
|
-
|
|
184
|
+
} catch (e) {
|
|
185
|
+
debugLog("postUnit", { phase: "doctor", error: String(e) });
|
|
178
186
|
}
|
|
179
187
|
|
|
180
|
-
// Throttled STATE.md rebuild
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
// Throttled STATE.md rebuild (skipped for lightweight sidecars)
|
|
189
|
+
if (!opts?.skipStateRebuild) {
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
if (now - s.lastStateRebuildAt >= STATE_REBUILD_MIN_INTERVAL_MS) {
|
|
192
|
+
try {
|
|
193
|
+
await rebuildState(s.basePath);
|
|
194
|
+
s.lastStateRebuildAt = now;
|
|
195
|
+
autoCommitCurrentBranch(s.basePath, "state-rebuild", s.currentUnit.id);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
debugLog("postUnit", { phase: "state-rebuild", error: String(e) });
|
|
198
|
+
}
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
|
|
@@ -193,16 +203,16 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
193
203
|
try {
|
|
194
204
|
const { pruneDeadProcesses } = await import("../bg-shell/process-manager.js");
|
|
195
205
|
pruneDeadProcesses();
|
|
196
|
-
} catch {
|
|
197
|
-
|
|
206
|
+
} catch (e) {
|
|
207
|
+
debugLog("postUnit", { phase: "prune-bg-shell", error: String(e) });
|
|
198
208
|
}
|
|
199
209
|
|
|
200
|
-
// Sync worktree state back to project root
|
|
201
|
-
if (s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
210
|
+
// Sync worktree state back to project root (skipped for lightweight sidecars)
|
|
211
|
+
if (!opts?.skipWorktreeSync && s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
202
212
|
try {
|
|
203
213
|
syncStateToProjectRoot(s.basePath, s.originalBasePath, s.currentMilestoneId);
|
|
204
|
-
} catch {
|
|
205
|
-
|
|
214
|
+
} catch (e) {
|
|
215
|
+
debugLog("postUnit", { phase: "worktree-sync", error: String(e) });
|
|
206
216
|
}
|
|
207
217
|
}
|
|
208
218
|
|
|
@@ -210,10 +220,24 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
210
220
|
if (s.currentUnit.type === "rewrite-docs") {
|
|
211
221
|
try {
|
|
212
222
|
await resolveAllOverrides(s.basePath);
|
|
213
|
-
|
|
223
|
+
s.rewriteAttemptCount = 0;
|
|
214
224
|
ctx.ui.notify("Override(s) resolved — rewrite-docs completed.", "info");
|
|
215
|
-
} catch {
|
|
216
|
-
|
|
225
|
+
} catch (e) {
|
|
226
|
+
debugLog("postUnit", { phase: "rewrite-docs-resolve", error: String(e) });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Reactive state cleanup on slice completion
|
|
231
|
+
if (s.currentUnit.type === "complete-slice") {
|
|
232
|
+
try {
|
|
233
|
+
const parts = s.currentUnit.id.split("/");
|
|
234
|
+
const [mid, sid] = parts;
|
|
235
|
+
if (mid && sid) {
|
|
236
|
+
const { clearReactiveState } = await import("./reactive-graph.js");
|
|
237
|
+
clearReactiveState(s.basePath, mid, sid);
|
|
238
|
+
}
|
|
239
|
+
} catch (e) {
|
|
240
|
+
debugLog("postUnit", { phase: "reactive-state-cleanup", error: String(e) });
|
|
217
241
|
}
|
|
218
242
|
}
|
|
219
243
|
|
|
@@ -266,8 +290,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
266
290
|
if (triggerArtifactVerified) {
|
|
267
291
|
invalidateAllCaches();
|
|
268
292
|
}
|
|
269
|
-
} catch {
|
|
270
|
-
|
|
293
|
+
} catch (e) {
|
|
294
|
+
debugLog("postUnit", { phase: "artifact-verify", error: String(e) });
|
|
271
295
|
}
|
|
272
296
|
} else {
|
|
273
297
|
// Hook unit completed — finalize its runtime record
|
|
@@ -278,8 +302,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
278
302
|
lastProgressKind: "hook-completed",
|
|
279
303
|
});
|
|
280
304
|
clearUnitRuntimeRecord(s.basePath, s.currentUnit.type, s.currentUnit.id);
|
|
281
|
-
} catch {
|
|
282
|
-
|
|
305
|
+
} catch (e) {
|
|
306
|
+
debugLog("postUnit", { phase: "hook-finalize", error: String(e) });
|
|
283
307
|
}
|
|
284
308
|
}
|
|
285
309
|
}
|
|
@@ -415,8 +439,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
415
439
|
}
|
|
416
440
|
}
|
|
417
441
|
}
|
|
418
|
-
} catch {
|
|
419
|
-
|
|
442
|
+
} catch (e) {
|
|
443
|
+
debugLog("postUnit", { phase: "triage-check", error: String(e) });
|
|
420
444
|
}
|
|
421
445
|
}
|
|
422
446
|
|
|
@@ -461,8 +485,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
461
485
|
);
|
|
462
486
|
|
|
463
487
|
return "continue";
|
|
464
|
-
} catch {
|
|
465
|
-
|
|
488
|
+
} catch (e) {
|
|
489
|
+
debugLog("postUnit", { phase: "quick-task-dispatch", error: String(e) });
|
|
466
490
|
}
|
|
467
491
|
}
|
|
468
492
|
|