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
|
import { NEW_SESSION_TIMEOUT_MS } from "./auto/session.js";
|
|
13
13
|
import { debugLog } from "./debug-logger.js";
|
|
@@ -18,71 +18,66 @@ import { debugLog } from "./debug-logger.js";
|
|
|
18
18
|
* generous headroom including retries and sidecar work.
|
|
19
19
|
*/
|
|
20
20
|
const MAX_LOOP_ITERATIONS = 500;
|
|
21
|
-
|
|
21
|
+
/** Data-driven budget threshold notifications (75/80/90%). The 100% case is
|
|
22
|
+
* handled inline because it requires break/pause/stop control flow. */
|
|
23
|
+
const BUDGET_THRESHOLDS = [
|
|
24
|
+
{ pct: 90, label: "Budget 90%", notifyLevel: "warning", cmuxLevel: "warning" },
|
|
25
|
+
{ pct: 80, label: "Approaching budget ceiling — 80%", notifyLevel: "warning", cmuxLevel: "warning" },
|
|
26
|
+
{ pct: 75, label: "Budget 75%", notifyLevel: "info", cmuxLevel: "progress" },
|
|
27
|
+
];
|
|
28
|
+
// ─── Per-unit one-shot promise state ────────────────────────────────────────
|
|
22
29
|
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*/
|
|
30
|
-
let _activeSession = null;
|
|
30
|
+
// A single module-level resolve function scoped to the current unit execution.
|
|
31
|
+
// No queue — if an agent_end arrives with no pending resolver, it is dropped
|
|
32
|
+
// (logged as warning). This is simpler and safer than the previous session-
|
|
33
|
+
// scoped pendingResolve + pendingAgentEndQueue pattern.
|
|
34
|
+
let _currentResolve = null;
|
|
35
|
+
let _sessionSwitchInFlight = false;
|
|
31
36
|
// ─── resolveAgentEnd ─────────────────────────────────────────────────────────
|
|
32
37
|
/**
|
|
33
38
|
* Called from the agent_end event handler in index.ts to resolve the
|
|
34
39
|
* in-flight unit promise. One-shot: the resolver is nulled before calling
|
|
35
40
|
* to prevent double-resolution from model fallback retries.
|
|
36
41
|
*
|
|
37
|
-
* If no
|
|
38
|
-
* the event is
|
|
42
|
+
* If no resolver exists (event arrived between loop iterations or during
|
|
43
|
+
* session switch), the event is dropped with a debug warning.
|
|
39
44
|
*/
|
|
40
45
|
export function resolveAgentEnd(event) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
debugLog("resolveAgentEnd", {
|
|
44
|
-
status: "no-active-session",
|
|
45
|
-
warning: "agent_end with no active loop session",
|
|
46
|
-
});
|
|
46
|
+
if (_sessionSwitchInFlight) {
|
|
47
|
+
debugLog("resolveAgentEnd", { status: "ignored-during-switch" });
|
|
47
48
|
return;
|
|
48
49
|
}
|
|
49
|
-
if (
|
|
50
|
+
if (_currentResolve) {
|
|
50
51
|
debugLog("resolveAgentEnd", { status: "resolving", hasEvent: true });
|
|
51
|
-
const r =
|
|
52
|
-
|
|
52
|
+
const r = _currentResolve;
|
|
53
|
+
_currentResolve = null;
|
|
53
54
|
r({ status: "completed", event });
|
|
54
55
|
}
|
|
55
56
|
else {
|
|
56
|
-
// Queue the event so the next runUnit picks it up immediately
|
|
57
57
|
debugLog("resolveAgentEnd", {
|
|
58
|
-
status: "
|
|
59
|
-
|
|
60
|
-
warning: "agent_end arrived between loop iterations — queued for next runUnit",
|
|
58
|
+
status: "no-pending-resolve",
|
|
59
|
+
warning: "agent_end with no pending unit",
|
|
61
60
|
});
|
|
62
|
-
s.pendingAgentEndQueue.push(event);
|
|
63
61
|
}
|
|
64
62
|
}
|
|
65
63
|
export function isSessionSwitchInFlight() {
|
|
66
|
-
return
|
|
64
|
+
return _sessionSwitchInFlight;
|
|
67
65
|
}
|
|
68
66
|
// ─── resetPendingResolve (test helper) ───────────────────────────────────────
|
|
69
67
|
/**
|
|
70
|
-
* Reset
|
|
71
|
-
* should never call this.
|
|
68
|
+
* Reset module-level promise state. Only exported for test cleanup —
|
|
69
|
+
* production code should never call this.
|
|
72
70
|
*/
|
|
73
71
|
export function _resetPendingResolve() {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
_activeSession.pendingAgentEndQueue = [];
|
|
77
|
-
}
|
|
78
|
-
_activeSession = null;
|
|
72
|
+
_currentResolve = null;
|
|
73
|
+
_sessionSwitchInFlight = false;
|
|
79
74
|
}
|
|
80
75
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
76
|
+
* No-op for backward compatibility with tests that previously set the
|
|
77
|
+
* active session. The module no longer holds a session reference.
|
|
83
78
|
*/
|
|
84
|
-
export function _setActiveSession(
|
|
85
|
-
|
|
79
|
+
export function _setActiveSession(_session) {
|
|
80
|
+
// No-op — kept for test backward compatibility
|
|
86
81
|
}
|
|
87
82
|
// ─── runUnit ─────────────────────────────────────────────────────────────────
|
|
88
83
|
/**
|
|
@@ -95,39 +90,14 @@ export function _setActiveSession(session) {
|
|
|
95
90
|
*/
|
|
96
91
|
export async function runUnit(ctx, pi, s, unitType, unitId, prompt, _prefs) {
|
|
97
92
|
debugLog("runUnit", { phase: "start", unitType, unitId });
|
|
98
|
-
// ── Drain queued events from error-recovery retries ──
|
|
99
|
-
// If an agent_end arrived between iterations (e.g. from a model fallback
|
|
100
|
-
// sendMessage retry), consume it immediately instead of creating a new promise.
|
|
101
|
-
// Cap queue to 3 entries to prevent unbounded growth from stale events.
|
|
102
|
-
if (s.pendingAgentEndQueue.length > 3) {
|
|
103
|
-
debugLog("runUnit", {
|
|
104
|
-
phase: "queue-overflow",
|
|
105
|
-
dropped: s.pendingAgentEndQueue.length - 1,
|
|
106
|
-
unitType,
|
|
107
|
-
unitId,
|
|
108
|
-
});
|
|
109
|
-
s.pendingAgentEndQueue = [
|
|
110
|
-
s.pendingAgentEndQueue[s.pendingAgentEndQueue.length - 1],
|
|
111
|
-
];
|
|
112
|
-
}
|
|
113
|
-
if (s.pendingAgentEndQueue.length > 0) {
|
|
114
|
-
const queued = s.pendingAgentEndQueue.shift();
|
|
115
|
-
debugLog("runUnit", {
|
|
116
|
-
phase: "drained-queued-event",
|
|
117
|
-
unitType,
|
|
118
|
-
unitId,
|
|
119
|
-
queueRemaining: s.pendingAgentEndQueue.length,
|
|
120
|
-
});
|
|
121
|
-
return { status: "completed", event: queued };
|
|
122
|
-
}
|
|
123
93
|
// ── Session creation with timeout ──
|
|
124
94
|
debugLog("runUnit", { phase: "session-create", unitType, unitId });
|
|
125
95
|
let sessionResult;
|
|
126
96
|
let sessionTimeoutHandle;
|
|
127
|
-
|
|
97
|
+
_sessionSwitchInFlight = true;
|
|
128
98
|
try {
|
|
129
99
|
const sessionPromise = s.cmdCtx.newSession().finally(() => {
|
|
130
|
-
|
|
100
|
+
_sessionSwitchInFlight = false;
|
|
131
101
|
});
|
|
132
102
|
const timeoutPromise = new Promise((resolve) => {
|
|
133
103
|
sessionTimeoutHandle = setTimeout(() => resolve({ cancelled: true }), NEW_SESSION_TIMEOUT_MS);
|
|
@@ -155,11 +125,12 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt, _prefs) {
|
|
|
155
125
|
if (!s.active) {
|
|
156
126
|
return { status: "cancelled" };
|
|
157
127
|
}
|
|
158
|
-
// ── Create the agent_end promise (
|
|
128
|
+
// ── Create the agent_end promise (per-unit one-shot) ──
|
|
159
129
|
// This happens after newSession completes so session-switch agent_end events
|
|
160
130
|
// from the previous session cannot resolve the new unit.
|
|
131
|
+
_sessionSwitchInFlight = false;
|
|
161
132
|
const unitPromise = new Promise((resolve) => {
|
|
162
|
-
|
|
133
|
+
_currentResolve = resolve;
|
|
163
134
|
});
|
|
164
135
|
// Ensure cwd matches basePath before dispatch (#1389).
|
|
165
136
|
// async_bash and background jobs can drift cwd away from the worktree.
|
|
@@ -195,7 +166,6 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt, _prefs) {
|
|
|
195
166
|
*/
|
|
196
167
|
export async function autoLoop(ctx, pi, s, deps) {
|
|
197
168
|
debugLog("autoLoop", { phase: "enter" });
|
|
198
|
-
_activeSession = s;
|
|
199
169
|
let iteration = 0;
|
|
200
170
|
let lastDerivedUnit = "";
|
|
201
171
|
let sameUnitCount = 0;
|
|
@@ -373,7 +343,7 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
373
343
|
await deps.closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
|
|
374
344
|
}
|
|
375
345
|
const incomplete = state.registry.filter((m) => m.status !== "complete" && m.status !== "parked");
|
|
376
|
-
if (incomplete.length === 0) {
|
|
346
|
+
if (incomplete.length === 0 && state.registry.length > 0) {
|
|
377
347
|
// All milestones complete — merge milestone branch before stopping
|
|
378
348
|
if (s.currentMilestoneId) {
|
|
379
349
|
deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
|
|
@@ -382,6 +352,12 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
382
352
|
deps.logCmuxEvent(deps.loadEffectiveGSDPreferences()?.preferences, "All milestones complete.", "success");
|
|
383
353
|
await deps.stopAuto(ctx, pi, "All milestones complete");
|
|
384
354
|
}
|
|
355
|
+
else if (incomplete.length === 0 && state.registry.length === 0) {
|
|
356
|
+
// Empty registry — no milestones visible, likely a path resolution bug
|
|
357
|
+
const diag = `basePath=${s.basePath}, phase=${state.phase}`;
|
|
358
|
+
ctx.ui.notify(`No milestones visible in current scope. Possible path resolution issue.\n Diagnostic: ${diag}`, "error");
|
|
359
|
+
await deps.stopAuto(ctx, pi, `No milestones found — check basePath resolution`);
|
|
360
|
+
}
|
|
385
361
|
else if (state.phase === "blocked") {
|
|
386
362
|
const blockerMsg = `Blocked: ${state.blockers.join(", ")}`;
|
|
387
363
|
await deps.stopAuto(ctx, pi, blockerMsg);
|
|
@@ -487,29 +463,20 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
487
463
|
deps.sendDesktopNotification("GSD", msg, "warning", "budget");
|
|
488
464
|
deps.logCmuxEvent(prefs, msg, "warning");
|
|
489
465
|
}
|
|
490
|
-
else
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
else if (newBudgetAlertLevel === 75) {
|
|
505
|
-
s.lastBudgetAlertLevel =
|
|
506
|
-
newBudgetAlertLevel;
|
|
507
|
-
ctx.ui.notify(`Budget 75%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`, "info");
|
|
508
|
-
deps.sendDesktopNotification("GSD", `Budget 75%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`, "info", "budget");
|
|
509
|
-
deps.logCmuxEvent(prefs, `Budget 75%: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`, "progress");
|
|
510
|
-
}
|
|
511
|
-
else if (budgetAlertLevel === 0) {
|
|
512
|
-
s.lastBudgetAlertLevel = 0;
|
|
466
|
+
else {
|
|
467
|
+
// Data-driven 75/80/90% threshold notifications
|
|
468
|
+
const threshold = BUDGET_THRESHOLDS.find((t) => newBudgetAlertLevel === t.pct);
|
|
469
|
+
if (threshold) {
|
|
470
|
+
s.lastBudgetAlertLevel =
|
|
471
|
+
newBudgetAlertLevel;
|
|
472
|
+
const msg = `${threshold.label}: ${deps.formatCost(totalCost)} / ${deps.formatCost(budgetCeiling)}`;
|
|
473
|
+
ctx.ui.notify(msg, threshold.notifyLevel);
|
|
474
|
+
deps.sendDesktopNotification("GSD", msg, threshold.notifyLevel, "budget");
|
|
475
|
+
deps.logCmuxEvent(prefs, msg, threshold.cmuxLevel);
|
|
476
|
+
}
|
|
477
|
+
else if (budgetAlertLevel === 0) {
|
|
478
|
+
s.lastBudgetAlertLevel = 0;
|
|
479
|
+
}
|
|
513
480
|
}
|
|
514
481
|
}
|
|
515
482
|
else {
|
|
@@ -557,6 +524,7 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
557
524
|
midTitle: midTitle,
|
|
558
525
|
state,
|
|
559
526
|
prefs,
|
|
527
|
+
session: s,
|
|
560
528
|
});
|
|
561
529
|
if (dispatchResult.action === "stop") {
|
|
562
530
|
if (s.currentUnit) {
|
|
@@ -916,8 +884,11 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
916
884
|
sidecarBroke = true;
|
|
917
885
|
break;
|
|
918
886
|
}
|
|
919
|
-
// Run pre-verification for the sidecar unit
|
|
920
|
-
const
|
|
887
|
+
// Run pre-verification for the sidecar unit (lightweight path)
|
|
888
|
+
const sidecarPreOpts = item.kind === "hook"
|
|
889
|
+
? { skipSettleDelay: true, skipDoctor: true, skipStateRebuild: true, skipWorktreeSync: true }
|
|
890
|
+
: { skipSettleDelay: true, skipStateRebuild: true };
|
|
891
|
+
const sidecarPreResult = await deps.postUnitPreVerification(postUnitCtx, sidecarPreOpts);
|
|
921
892
|
if (sidecarPreResult === "dispatched") {
|
|
922
893
|
// Pre-verification caused stop/pause
|
|
923
894
|
debugLog("autoLoop", {
|
|
@@ -990,6 +961,6 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
990
961
|
}
|
|
991
962
|
}
|
|
992
963
|
}
|
|
993
|
-
|
|
964
|
+
_currentResolve = null;
|
|
994
965
|
debugLog("autoLoop", { phase: "exit", totalIterations: iteration });
|
|
995
966
|
}
|
|
@@ -22,7 +22,6 @@ import { writeUnitRuntimeRecord, clearUnitRuntimeRecord } from "./unit-runtime.j
|
|
|
22
22
|
import { runGSDDoctor, rebuildState, summarizeDoctorIssues } from "./doctor.js";
|
|
23
23
|
import { recordHealthSnapshot, checkHealEscalation } from "./doctor-proactive.js";
|
|
24
24
|
import { syncStateToProjectRoot } from "./auto-worktree-sync.js";
|
|
25
|
-
import { resetRewriteCircuitBreaker } from "./auto-dispatch.js";
|
|
26
25
|
import { isDbAvailable } from "./gsd-db.js";
|
|
27
26
|
import { consumeSignal } from "./session-status-io.js";
|
|
28
27
|
import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookState, } from "./post-unit-hooks.js";
|
|
@@ -36,7 +35,7 @@ const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
|
36
35
|
*
|
|
37
36
|
* Returns "dispatched" if a signal caused stop/pause, "continue" to proceed.
|
|
38
37
|
*/
|
|
39
|
-
export async function postUnitPreVerification(pctx) {
|
|
38
|
+
export async function postUnitPreVerification(pctx, opts) {
|
|
40
39
|
const { s, ctx, pi, buildSnapshotOpts, stopAuto, pauseAuto } = pctx;
|
|
41
40
|
// ── Parallel worker signal check ──
|
|
42
41
|
const milestoneLock = process.env.GSD_MILESTONE_LOCK;
|
|
@@ -55,8 +54,10 @@ export async function postUnitPreVerification(pctx) {
|
|
|
55
54
|
}
|
|
56
55
|
// Invalidate all caches
|
|
57
56
|
invalidateAllCaches();
|
|
58
|
-
// Small delay to let files settle
|
|
59
|
-
|
|
57
|
+
// Small delay to let files settle (skipped for sidecars where latency matters more)
|
|
58
|
+
if (!opts?.skipSettleDelay) {
|
|
59
|
+
await new Promise(r => setTimeout(r, 100));
|
|
60
|
+
}
|
|
60
61
|
// Auto-commit
|
|
61
62
|
if (s.currentUnit) {
|
|
62
63
|
try {
|
|
@@ -79,8 +80,8 @@ export async function postUnitPreVerification(pctx) {
|
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
|
-
catch {
|
|
83
|
-
|
|
83
|
+
catch (e) {
|
|
84
|
+
debugLog("postUnit", { phase: "task-summary-parse", error: String(e) });
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
}
|
|
@@ -90,57 +91,60 @@ export async function postUnitPreVerification(pctx) {
|
|
|
90
91
|
ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
|
-
catch {
|
|
94
|
-
|
|
94
|
+
catch (e) {
|
|
95
|
+
debugLog("postUnit", { phase: "auto-commit", error: String(e) });
|
|
95
96
|
}
|
|
96
|
-
// Doctor: fix mechanical bookkeeping
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
97
|
+
// Doctor: fix mechanical bookkeeping (skipped for lightweight sidecars)
|
|
98
|
+
if (!opts?.skipDoctor)
|
|
99
|
+
try {
|
|
100
|
+
const scopeParts = s.currentUnit.id.split("/").slice(0, 2);
|
|
101
|
+
const doctorScope = scopeParts.join("/");
|
|
102
|
+
const sliceTerminalUnits = new Set(["complete-slice", "run-uat"]);
|
|
103
|
+
const effectiveFixLevel = sliceTerminalUnits.has(s.currentUnit.type) ? "all" : "task";
|
|
104
|
+
const report = await runGSDDoctor(s.basePath, { fix: true, scope: doctorScope, fixLevel: effectiveFixLevel });
|
|
105
|
+
if (report.fixesApplied.length > 0) {
|
|
106
|
+
ctx.ui.notify(`Post-hook: applied ${report.fixesApplied.length} fix(es).`, "info");
|
|
107
|
+
}
|
|
108
|
+
// Proactive health tracking
|
|
109
|
+
const summary = summarizeDoctorIssues(report.issues);
|
|
110
|
+
recordHealthSnapshot(summary.errors, summary.warnings, report.fixesApplied.length);
|
|
111
|
+
// Check if we should escalate to LLM-assisted heal
|
|
112
|
+
if (summary.errors > 0) {
|
|
113
|
+
const unresolvedErrors = report.issues
|
|
114
|
+
.filter(i => i.severity === "error" && !i.fixable)
|
|
115
|
+
.map(i => ({ code: i.code, message: i.message, unitId: i.unitId }));
|
|
116
|
+
const escalation = checkHealEscalation(summary.errors, unresolvedErrors);
|
|
117
|
+
if (escalation.shouldEscalate) {
|
|
118
|
+
ctx.ui.notify(`Doctor heal escalation: ${escalation.reason}. Dispatching LLM-assisted heal.`, "warning");
|
|
119
|
+
try {
|
|
120
|
+
const { formatDoctorIssuesForPrompt, formatDoctorReport } = await import("./doctor.js");
|
|
121
|
+
const { dispatchDoctorHeal } = await import("./commands-handlers.js");
|
|
122
|
+
const actionable = report.issues.filter(i => i.severity === "error");
|
|
123
|
+
const reportText = formatDoctorReport(report, { scope: doctorScope, includeWarnings: true });
|
|
124
|
+
const structuredIssues = formatDoctorIssuesForPrompt(actionable);
|
|
125
|
+
dispatchDoctorHeal(pi, doctorScope, reportText, structuredIssues);
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
debugLog("postUnit", { phase: "doctor-heal-dispatch", error: String(e) });
|
|
129
|
+
}
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// Non-fatal
|
|
133
|
-
}
|
|
134
|
-
// Throttled STATE.md rebuild
|
|
135
|
-
const now = Date.now();
|
|
136
|
-
if (now - s.lastStateRebuildAt >= STATE_REBUILD_MIN_INTERVAL_MS) {
|
|
137
|
-
try {
|
|
138
|
-
await rebuildState(s.basePath);
|
|
139
|
-
s.lastStateRebuildAt = now;
|
|
140
|
-
autoCommitCurrentBranch(s.basePath, "state-rebuild", s.currentUnit.id);
|
|
133
|
+
catch (e) {
|
|
134
|
+
debugLog("postUnit", { phase: "doctor", error: String(e) });
|
|
141
135
|
}
|
|
142
|
-
|
|
143
|
-
|
|
136
|
+
// Throttled STATE.md rebuild (skipped for lightweight sidecars)
|
|
137
|
+
if (!opts?.skipStateRebuild) {
|
|
138
|
+
const now = Date.now();
|
|
139
|
+
if (now - s.lastStateRebuildAt >= STATE_REBUILD_MIN_INTERVAL_MS) {
|
|
140
|
+
try {
|
|
141
|
+
await rebuildState(s.basePath);
|
|
142
|
+
s.lastStateRebuildAt = now;
|
|
143
|
+
autoCommitCurrentBranch(s.basePath, "state-rebuild", s.currentUnit.id);
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
debugLog("postUnit", { phase: "state-rebuild", error: String(e) });
|
|
147
|
+
}
|
|
144
148
|
}
|
|
145
149
|
}
|
|
146
150
|
// Prune dead bg-shell processes
|
|
@@ -148,27 +152,41 @@ export async function postUnitPreVerification(pctx) {
|
|
|
148
152
|
const { pruneDeadProcesses } = await import("../bg-shell/process-manager.js");
|
|
149
153
|
pruneDeadProcesses();
|
|
150
154
|
}
|
|
151
|
-
catch {
|
|
152
|
-
|
|
155
|
+
catch (e) {
|
|
156
|
+
debugLog("postUnit", { phase: "prune-bg-shell", error: String(e) });
|
|
153
157
|
}
|
|
154
|
-
// Sync worktree state back to project root
|
|
155
|
-
if (s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
158
|
+
// Sync worktree state back to project root (skipped for lightweight sidecars)
|
|
159
|
+
if (!opts?.skipWorktreeSync && s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
156
160
|
try {
|
|
157
161
|
syncStateToProjectRoot(s.basePath, s.originalBasePath, s.currentMilestoneId);
|
|
158
162
|
}
|
|
159
|
-
catch {
|
|
160
|
-
|
|
163
|
+
catch (e) {
|
|
164
|
+
debugLog("postUnit", { phase: "worktree-sync", error: String(e) });
|
|
161
165
|
}
|
|
162
166
|
}
|
|
163
167
|
// Rewrite-docs completion
|
|
164
168
|
if (s.currentUnit.type === "rewrite-docs") {
|
|
165
169
|
try {
|
|
166
170
|
await resolveAllOverrides(s.basePath);
|
|
167
|
-
|
|
171
|
+
s.rewriteAttemptCount = 0;
|
|
168
172
|
ctx.ui.notify("Override(s) resolved — rewrite-docs completed.", "info");
|
|
169
173
|
}
|
|
170
|
-
catch {
|
|
171
|
-
|
|
174
|
+
catch (e) {
|
|
175
|
+
debugLog("postUnit", { phase: "rewrite-docs-resolve", error: String(e) });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Reactive state cleanup on slice completion
|
|
179
|
+
if (s.currentUnit.type === "complete-slice") {
|
|
180
|
+
try {
|
|
181
|
+
const parts = s.currentUnit.id.split("/");
|
|
182
|
+
const [mid, sid] = parts;
|
|
183
|
+
if (mid && sid) {
|
|
184
|
+
const { clearReactiveState } = await import("./reactive-graph.js");
|
|
185
|
+
clearReactiveState(s.basePath, mid, sid);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
debugLog("postUnit", { phase: "reactive-state-cleanup", error: String(e) });
|
|
172
190
|
}
|
|
173
191
|
}
|
|
174
192
|
// Post-triage: execute actionable resolutions
|
|
@@ -210,8 +228,8 @@ export async function postUnitPreVerification(pctx) {
|
|
|
210
228
|
invalidateAllCaches();
|
|
211
229
|
}
|
|
212
230
|
}
|
|
213
|
-
catch {
|
|
214
|
-
|
|
231
|
+
catch (e) {
|
|
232
|
+
debugLog("postUnit", { phase: "artifact-verify", error: String(e) });
|
|
215
233
|
}
|
|
216
234
|
}
|
|
217
235
|
else {
|
|
@@ -224,8 +242,8 @@ export async function postUnitPreVerification(pctx) {
|
|
|
224
242
|
});
|
|
225
243
|
clearUnitRuntimeRecord(s.basePath, s.currentUnit.type, s.currentUnit.id);
|
|
226
244
|
}
|
|
227
|
-
catch {
|
|
228
|
-
|
|
245
|
+
catch (e) {
|
|
246
|
+
debugLog("postUnit", { phase: "hook-finalize", error: String(e) });
|
|
229
247
|
}
|
|
230
248
|
}
|
|
231
249
|
}
|
|
@@ -338,8 +356,8 @@ export async function postUnitPostVerification(pctx) {
|
|
|
338
356
|
}
|
|
339
357
|
}
|
|
340
358
|
}
|
|
341
|
-
catch {
|
|
342
|
-
|
|
359
|
+
catch (e) {
|
|
360
|
+
debugLog("postUnit", { phase: "triage-check", error: String(e) });
|
|
343
361
|
}
|
|
344
362
|
}
|
|
345
363
|
// ── Quick-task dispatch ──
|
|
@@ -373,8 +391,8 @@ export async function postUnitPostVerification(pctx) {
|
|
|
373
391
|
ctx.ui.notify(`Executing quick-task: ${capture.id} — "${capture.text}"`, "info");
|
|
374
392
|
return "continue";
|
|
375
393
|
}
|
|
376
|
-
catch {
|
|
377
|
-
|
|
394
|
+
catch (e) {
|
|
395
|
+
debugLog("postUnit", { phase: "quick-task-dispatch", error: String(e) });
|
|
378
396
|
}
|
|
379
397
|
}
|
|
380
398
|
// Step mode → show wizard instead of dispatch
|