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
|
@@ -289,10 +289,17 @@ export class CmuxClient {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
async createSplit(direction: "right" | "down" | "left" | "up"): Promise<string | null> {
|
|
292
|
+
return this.createSplitFrom(this.config.surfaceId, direction);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async createSplitFrom(
|
|
296
|
+
sourceSurfaceId: string | undefined,
|
|
297
|
+
direction: "right" | "down" | "left" | "up",
|
|
298
|
+
): Promise<string | null> {
|
|
292
299
|
if (!this.config.splits) return null;
|
|
293
300
|
const before = new Set(await this.listSurfaceIds());
|
|
294
301
|
const args = ["new-split", direction];
|
|
295
|
-
const scopedArgs = this.appendSurface(this.appendWorkspace(args),
|
|
302
|
+
const scopedArgs = this.appendSurface(this.appendWorkspace(args), sourceSurfaceId);
|
|
296
303
|
await this.runAsync(scopedArgs);
|
|
297
304
|
const after = await this.listSurfaceIds();
|
|
298
305
|
for (const id of after) {
|
|
@@ -301,6 +308,55 @@ export class CmuxClient {
|
|
|
301
308
|
return null;
|
|
302
309
|
}
|
|
303
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Create a grid of surfaces for parallel agent execution.
|
|
313
|
+
*
|
|
314
|
+
* Layout strategy (gsd stays in the original surface):
|
|
315
|
+
* 1 agent: [gsd | A]
|
|
316
|
+
* 2 agents: [gsd | A]
|
|
317
|
+
* [ | B]
|
|
318
|
+
* 3 agents: [gsd | A]
|
|
319
|
+
* [ C | B]
|
|
320
|
+
* 4 agents: [gsd | A]
|
|
321
|
+
* [ C | B] (D splits from B downward)
|
|
322
|
+
* [ | D]
|
|
323
|
+
*
|
|
324
|
+
* Returns surface IDs in order, or empty array on failure.
|
|
325
|
+
*/
|
|
326
|
+
async createGridLayout(count: number): Promise<string[]> {
|
|
327
|
+
if (!this.config.splits || count <= 0) return [];
|
|
328
|
+
const surfaces: string[] = [];
|
|
329
|
+
|
|
330
|
+
// First split: create right column from the gsd surface
|
|
331
|
+
const rightCol = await this.createSplitFrom(this.config.surfaceId, "right");
|
|
332
|
+
if (!rightCol) return [];
|
|
333
|
+
surfaces.push(rightCol);
|
|
334
|
+
if (count === 1) return surfaces;
|
|
335
|
+
|
|
336
|
+
// Second split: split right column down → bottom-right
|
|
337
|
+
const bottomRight = await this.createSplitFrom(rightCol, "down");
|
|
338
|
+
if (!bottomRight) return surfaces;
|
|
339
|
+
surfaces.push(bottomRight);
|
|
340
|
+
if (count === 2) return surfaces;
|
|
341
|
+
|
|
342
|
+
// Third split: split gsd surface down → bottom-left
|
|
343
|
+
const bottomLeft = await this.createSplitFrom(this.config.surfaceId, "down");
|
|
344
|
+
if (!bottomLeft) return surfaces;
|
|
345
|
+
surfaces.push(bottomLeft);
|
|
346
|
+
if (count === 3) return surfaces;
|
|
347
|
+
|
|
348
|
+
// Fourth+: split subsequent surfaces down from the last created
|
|
349
|
+
let lastSurface = bottomRight;
|
|
350
|
+
for (let i = 3; i < count; i++) {
|
|
351
|
+
const next = await this.createSplitFrom(lastSurface, "down");
|
|
352
|
+
if (!next) break;
|
|
353
|
+
surfaces.push(next);
|
|
354
|
+
lastSurface = next;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return surfaces;
|
|
358
|
+
}
|
|
359
|
+
|
|
304
360
|
async sendSurface(surfaceId: string, text: string): Promise<boolean> {
|
|
305
361
|
const payload = text.endsWith("\n") ? text : `${text}\n`;
|
|
306
362
|
const stdout = await this.runAsync(["send-surface", "--surface", surfaceId, payload]);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// GSD Extension — Environment variable utilities
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
//
|
|
4
|
+
// Pure utility for checking existing env keys in .env files and process.env.
|
|
5
|
+
// Extracted from get-secrets-from-user.ts to avoid pulling in @gsd/pi-tui
|
|
6
|
+
// when only env-checking is needed (e.g. from files.ts during report generation).
|
|
7
|
+
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check which keys already exist in a .env file or process.env.
|
|
12
|
+
* Returns the subset of `keys` that are already set.
|
|
13
|
+
*/
|
|
14
|
+
export async function checkExistingEnvKeys(keys: string[], envFilePath: string): Promise<string[]> {
|
|
15
|
+
let fileContent = "";
|
|
16
|
+
try {
|
|
17
|
+
fileContent = await readFile(envFilePath, "utf8");
|
|
18
|
+
} catch {
|
|
19
|
+
// ENOENT or other read error — proceed with empty content
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const existing: string[] = [];
|
|
23
|
+
for (const key of keys) {
|
|
24
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25
|
+
const regex = new RegExp(`^${escaped}\\s*=`, "m");
|
|
26
|
+
if (regex.test(fileContent) || key in process.env) {
|
|
27
|
+
existing.push(key);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return existing;
|
|
31
|
+
}
|
|
@@ -67,30 +67,11 @@ async function writeEnvKey(filePath: string, key: string, value: string): Promis
|
|
|
67
67
|
|
|
68
68
|
// ─── Exported utilities ───────────────────────────────────────────────────────
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*/
|
|
76
|
-
export async function checkExistingEnvKeys(keys: string[], envFilePath: string): Promise<string[]> {
|
|
77
|
-
let fileContent = "";
|
|
78
|
-
try {
|
|
79
|
-
fileContent = await readFile(envFilePath, "utf8");
|
|
80
|
-
} catch {
|
|
81
|
-
// ENOENT or other read error — proceed with empty content
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const existing: string[] = [];
|
|
85
|
-
for (const key of keys) {
|
|
86
|
-
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
87
|
-
const regex = new RegExp(`^${escaped}\\s*=`, "m");
|
|
88
|
-
if (regex.test(fileContent) || key in process.env) {
|
|
89
|
-
existing.push(key);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return existing;
|
|
93
|
-
}
|
|
70
|
+
// Re-export from env-utils.ts so existing consumers still work.
|
|
71
|
+
// The implementation lives in env-utils.ts to avoid pulling @gsd/pi-tui
|
|
72
|
+
// into modules that only need env-checking (e.g. files.ts during reports).
|
|
73
|
+
import { checkExistingEnvKeys } from "./env-utils.js";
|
|
74
|
+
export { checkExistingEnvKeys };
|
|
94
75
|
|
|
95
76
|
/**
|
|
96
77
|
* Detect the write destination based on project files in basePath.
|
|
@@ -124,6 +124,9 @@ export class AutoSession {
|
|
|
124
124
|
// ── Sidecar queue ─────────────────────────────────────────────────────
|
|
125
125
|
sidecarQueue: SidecarItem[] = [];
|
|
126
126
|
|
|
127
|
+
// ── Dispatch circuit breakers ──────────────────────────────────────
|
|
128
|
+
rewriteAttemptCount = 0;
|
|
129
|
+
|
|
127
130
|
// ── Metrics ──────────────────────────────────────────────────────────────
|
|
128
131
|
autoStartTime = 0;
|
|
129
132
|
lastPromptCharCount: number | undefined;
|
|
@@ -134,27 +137,8 @@ export class AutoSession {
|
|
|
134
137
|
sigtermHandler: (() => void) | null = null;
|
|
135
138
|
|
|
136
139
|
// ── Loop promise state ──────────────────────────────────────────────────
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
* emitted from the previous session's abort during this window must be
|
|
140
|
-
* ignored; they do not belong to the new unit.
|
|
141
|
-
*/
|
|
142
|
-
sessionSwitchInFlight = false;
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* One-shot resolver for the current unit's agent_end promise.
|
|
146
|
-
* Non-null only while a unit is in-flight (between sendMessage and agent_end).
|
|
147
|
-
* Scoped to the session to prevent concurrent session corruption.
|
|
148
|
-
*/
|
|
149
|
-
pendingResolve: ((result: { status: "completed" | "cancelled" | "error"; event?: { messages: unknown[] } }) => void) | null = null;
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Queue for agent_end events that arrive when no pendingResolve exists.
|
|
153
|
-
* This happens when error-recovery sendMessage retries produce agent_end
|
|
154
|
-
* events between loop iterations. The next runUnit drains this queue
|
|
155
|
-
* instead of waiting for a new event.
|
|
156
|
-
*/
|
|
157
|
-
pendingAgentEndQueue: Array<{ messages: unknown[] }> = [];
|
|
140
|
+
// Per-unit resolve function and session-switch guard live at module level
|
|
141
|
+
// in auto-loop.ts (_currentResolve, _sessionSwitchInFlight).
|
|
158
142
|
|
|
159
143
|
// ── Methods ──────────────────────────────────────────────────────────────
|
|
160
144
|
|
|
@@ -228,14 +212,12 @@ export class AutoSession {
|
|
|
228
212
|
this.lastBaselineCharCount = undefined;
|
|
229
213
|
this.pendingQuickTasks = [];
|
|
230
214
|
this.sidecarQueue = [];
|
|
215
|
+
this.rewriteAttemptCount = 0;
|
|
231
216
|
|
|
232
217
|
// Signal handler
|
|
233
218
|
this.sigtermHandler = null;
|
|
234
219
|
|
|
235
|
-
// Loop promise state
|
|
236
|
-
this.sessionSwitchInFlight = false;
|
|
237
|
-
this.pendingResolve = null;
|
|
238
|
-
this.pendingAgentEndQueue = [];
|
|
220
|
+
// Loop promise state lives in auto-loop.ts module scope
|
|
239
221
|
}
|
|
240
222
|
|
|
241
223
|
toJSON(): Record<string, unknown> {
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
buildRunUatPrompt,
|
|
39
39
|
buildReassessRoadmapPrompt,
|
|
40
40
|
buildRewriteDocsPrompt,
|
|
41
|
+
buildReactiveExecutePrompt,
|
|
41
42
|
checkNeedsReassessment,
|
|
42
43
|
checkNeedsRunUat,
|
|
43
44
|
} from "./auto-prompts.js";
|
|
@@ -61,6 +62,7 @@ export interface DispatchContext {
|
|
|
61
62
|
midTitle: string;
|
|
62
63
|
state: GSDState;
|
|
63
64
|
prefs: GSDPreferences | undefined;
|
|
65
|
+
session?: import("./auto/session.js").AutoSession;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
interface DispatchRule {
|
|
@@ -81,26 +83,23 @@ function missingSliceStop(mid: string, phase: string): DispatchAction {
|
|
|
81
83
|
// ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
|
|
82
84
|
|
|
83
85
|
const MAX_REWRITE_ATTEMPTS = 3;
|
|
84
|
-
let rewriteAttemptCount = 0;
|
|
85
|
-
export function resetRewriteCircuitBreaker(): void {
|
|
86
|
-
rewriteAttemptCount = 0;
|
|
87
|
-
}
|
|
88
86
|
|
|
89
87
|
// ─── Rules ────────────────────────────────────────────────────────────────
|
|
90
88
|
|
|
91
89
|
const DISPATCH_RULES: DispatchRule[] = [
|
|
92
90
|
{
|
|
93
91
|
name: "rewrite-docs (override gate)",
|
|
94
|
-
match: async ({ mid, midTitle, state, basePath }) => {
|
|
92
|
+
match: async ({ mid, midTitle, state, basePath, session }) => {
|
|
95
93
|
const pendingOverrides = await loadActiveOverrides(basePath);
|
|
96
94
|
if (pendingOverrides.length === 0) return null;
|
|
97
|
-
|
|
95
|
+
const count = session?.rewriteAttemptCount ?? 0;
|
|
96
|
+
if (count >= MAX_REWRITE_ATTEMPTS) {
|
|
98
97
|
const { resolveAllOverrides } = await import("./files.js");
|
|
99
98
|
await resolveAllOverrides(basePath);
|
|
100
|
-
rewriteAttemptCount = 0;
|
|
99
|
+
if (session) session.rewriteAttemptCount = 0;
|
|
101
100
|
return null;
|
|
102
101
|
}
|
|
103
|
-
rewriteAttemptCount++;
|
|
102
|
+
if (session) session.rewriteAttemptCount++;
|
|
104
103
|
const unitId = state.activeSlice ? `${mid}/${state.activeSlice.id}` : mid;
|
|
105
104
|
return {
|
|
106
105
|
action: "dispatch",
|
|
@@ -309,6 +308,98 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
309
308
|
};
|
|
310
309
|
},
|
|
311
310
|
},
|
|
311
|
+
{
|
|
312
|
+
name: "executing → reactive-execute (parallel dispatch)",
|
|
313
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
314
|
+
if (state.phase !== "executing" || !state.activeTask) return null;
|
|
315
|
+
if (!state.activeSlice) return null; // fall through
|
|
316
|
+
|
|
317
|
+
// Only activate when reactive_execution is explicitly enabled
|
|
318
|
+
const reactiveConfig = prefs?.reactive_execution;
|
|
319
|
+
if (!reactiveConfig?.enabled) return null;
|
|
320
|
+
|
|
321
|
+
const sid = state.activeSlice.id;
|
|
322
|
+
const sTitle = state.activeSlice.title;
|
|
323
|
+
const maxParallel = reactiveConfig.max_parallel ?? 2;
|
|
324
|
+
|
|
325
|
+
// Dry-run mode: max_parallel=1 means graph is derived and logged but
|
|
326
|
+
// execution remains sequential
|
|
327
|
+
if (maxParallel <= 1) return null;
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
const {
|
|
331
|
+
loadSliceTaskIO,
|
|
332
|
+
deriveTaskGraph,
|
|
333
|
+
isGraphAmbiguous,
|
|
334
|
+
getReadyTasks,
|
|
335
|
+
chooseNonConflictingSubset,
|
|
336
|
+
graphMetrics,
|
|
337
|
+
} = await import("./reactive-graph.js");
|
|
338
|
+
|
|
339
|
+
const taskIO = await loadSliceTaskIO(basePath, mid, sid);
|
|
340
|
+
if (taskIO.length < 2) return null; // single task, no point
|
|
341
|
+
|
|
342
|
+
const graph = deriveTaskGraph(taskIO);
|
|
343
|
+
|
|
344
|
+
// Ambiguous graph → fall through to sequential
|
|
345
|
+
if (isGraphAmbiguous(graph)) return null;
|
|
346
|
+
|
|
347
|
+
const completed = new Set(graph.filter((n) => n.done).map((n) => n.id));
|
|
348
|
+
const readyIds = getReadyTasks(graph, completed, new Set());
|
|
349
|
+
|
|
350
|
+
// Only activate reactive dispatch when >1 task is ready
|
|
351
|
+
if (readyIds.length <= 1) return null;
|
|
352
|
+
|
|
353
|
+
const selected = chooseNonConflictingSubset(
|
|
354
|
+
readyIds,
|
|
355
|
+
graph,
|
|
356
|
+
maxParallel,
|
|
357
|
+
new Set(),
|
|
358
|
+
);
|
|
359
|
+
if (selected.length <= 1) return null;
|
|
360
|
+
|
|
361
|
+
// Log graph metrics for observability
|
|
362
|
+
const metrics = graphMetrics(graph);
|
|
363
|
+
process.stderr.write(
|
|
364
|
+
`gsd-reactive: ${mid}/${sid} graph — tasks:${metrics.taskCount} edges:${metrics.edgeCount} ` +
|
|
365
|
+
`ready:${metrics.readySetSize} dispatching:${selected.length} ambiguous:${metrics.ambiguous}\n`,
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
// Persist dispatched batch so verification and recovery can check
|
|
369
|
+
// exactly which tasks were sent.
|
|
370
|
+
const { saveReactiveState } = await import("./reactive-graph.js");
|
|
371
|
+
saveReactiveState(basePath, mid, sid, {
|
|
372
|
+
sliceId: sid,
|
|
373
|
+
completed: [...completed],
|
|
374
|
+
dispatched: selected,
|
|
375
|
+
graphSnapshot: metrics,
|
|
376
|
+
updatedAt: new Date().toISOString(),
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Encode selected task IDs in unitId for artifact verification.
|
|
380
|
+
// Format: M001/S01/reactive+T02,T03
|
|
381
|
+
const batchSuffix = selected.join(",");
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
action: "dispatch",
|
|
385
|
+
unitType: "reactive-execute",
|
|
386
|
+
unitId: `${mid}/${sid}/reactive+${batchSuffix}`,
|
|
387
|
+
prompt: await buildReactiveExecutePrompt(
|
|
388
|
+
mid,
|
|
389
|
+
midTitle,
|
|
390
|
+
sid,
|
|
391
|
+
sTitle,
|
|
392
|
+
selected,
|
|
393
|
+
basePath,
|
|
394
|
+
),
|
|
395
|
+
};
|
|
396
|
+
} catch (err) {
|
|
397
|
+
// Non-fatal — fall through to sequential execution
|
|
398
|
+
process.stderr.write(`gsd-reactive: graph derivation failed: ${(err as Error).message}\n`);
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
},
|
|
312
403
|
{
|
|
313
404
|
name: "executing → execute-task (recover missing task plan → plan-slice)",
|
|
314
405
|
match: async ({ state, mid, midTitle, basePath }) => {
|