gsd-pi 2.79.0-dev.ece5fd8ba → 2.80.0
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/dist/loader.js +0 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/contracts.js +1 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +146 -0
- package/dist/resources/extensions/gsd/auto/phases.js +55 -6
- package/dist/resources/extensions/gsd/auto/session.js +8 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +45 -52
- package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -0
- package/dist/resources/extensions/gsd/auto.js +159 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
- package/dist/resources/extensions/gsd/gsd-db.js +34 -1
- package/dist/resources/extensions/gsd/post-execution-checks.js +25 -6
- package/dist/resources/extensions/shared/interview-ui.js +15 -4
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-ff639266d978f2a0.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/package.json +2 -2
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/contracts.ts +87 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +10 -3
- package/src/resources/extensions/gsd/auto/orchestrator.ts +161 -0
- package/src/resources/extensions/gsd/auto/phases.ts +82 -8
- package/src/resources/extensions/gsd/auto/session.ts +11 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +42 -50
- package/src/resources/extensions/gsd/auto-runtime-state.ts +7 -0
- package/src/resources/extensions/gsd/auto.ts +167 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +14 -1
- package/src/resources/extensions/gsd/gsd-db.ts +35 -1
- package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
- package/src/resources/extensions/gsd/post-execution-checks.ts +31 -6
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +46 -0
- package/src/resources/extensions/shared/interview-ui.ts +18 -5
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +41 -0
- package/dist/web/standalone/.next/static/chunks/app/page-fab3ebb85b006001.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- /package/dist/web/standalone/.next/static/{TzEVJ1Lh8vbez4n4Q9TqQ → V-3Ehy4B24f9FCGiLPWIM}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{TzEVJ1Lh8vbez4n4Q9TqQ → V-3Ehy4B24f9FCGiLPWIM}/_ssgManifest.js +0 -0
package/dist/loader.js
CHANGED
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
63159c1114dd0d48
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
function now() {
|
|
2
|
+
return Date.now();
|
|
3
|
+
}
|
|
4
|
+
export class AutoOrchestrator {
|
|
5
|
+
status = {
|
|
6
|
+
phase: "idle",
|
|
7
|
+
transitionCount: 0,
|
|
8
|
+
};
|
|
9
|
+
deps;
|
|
10
|
+
lastAdvanceKey = null;
|
|
11
|
+
constructor(deps) {
|
|
12
|
+
this.deps = deps;
|
|
13
|
+
}
|
|
14
|
+
async start(_sessionContext) {
|
|
15
|
+
this.lastAdvanceKey = null;
|
|
16
|
+
this.status.phase = "running";
|
|
17
|
+
this.bumpTransition();
|
|
18
|
+
await this.deps.runtime.journalTransition({ name: "start" });
|
|
19
|
+
await this.deps.notifications.notifyLifecycle({ name: "start" });
|
|
20
|
+
return this.advance();
|
|
21
|
+
}
|
|
22
|
+
async advance() {
|
|
23
|
+
try {
|
|
24
|
+
await this.deps.runtime.ensureLockOwnership();
|
|
25
|
+
const gate = await this.deps.health.preAdvanceGate();
|
|
26
|
+
if (!gate.allow) {
|
|
27
|
+
const blocked = { kind: "blocked", reason: gate.reason ?? "health gate blocked" };
|
|
28
|
+
await this.deps.runtime.journalTransition({ name: "advance-blocked", reason: blocked.reason });
|
|
29
|
+
await this.deps.health.postAdvanceRecord(blocked);
|
|
30
|
+
return blocked;
|
|
31
|
+
}
|
|
32
|
+
const decision = await this.deps.dispatch.decideNextUnit();
|
|
33
|
+
if (!decision) {
|
|
34
|
+
const stopped = { kind: "stopped", reason: "no remaining units" };
|
|
35
|
+
this.status.phase = "stopped";
|
|
36
|
+
this.status.activeUnit = undefined;
|
|
37
|
+
this.lastAdvanceKey = null;
|
|
38
|
+
this.bumpTransition();
|
|
39
|
+
await this.deps.runtime.journalTransition({ name: "advance-stopped", reason: stopped.reason });
|
|
40
|
+
await this.deps.health.postAdvanceRecord(stopped);
|
|
41
|
+
return stopped;
|
|
42
|
+
}
|
|
43
|
+
const nextKey = `${decision.unitType}:${decision.unitId}`;
|
|
44
|
+
if (this.lastAdvanceKey === nextKey) {
|
|
45
|
+
const blocked = { kind: "blocked", reason: "idempotent advance: unit already active" };
|
|
46
|
+
await this.deps.runtime.journalTransition({
|
|
47
|
+
name: "advance-blocked",
|
|
48
|
+
reason: blocked.reason,
|
|
49
|
+
unitType: decision.unitType,
|
|
50
|
+
unitId: decision.unitId,
|
|
51
|
+
});
|
|
52
|
+
await this.deps.health.postAdvanceRecord(blocked);
|
|
53
|
+
return blocked;
|
|
54
|
+
}
|
|
55
|
+
this.status.activeUnit = { unitType: decision.unitType, unitId: decision.unitId };
|
|
56
|
+
this.status.phase = "running";
|
|
57
|
+
this.lastAdvanceKey = nextKey;
|
|
58
|
+
this.bumpTransition();
|
|
59
|
+
await this.deps.runtime.journalTransition({
|
|
60
|
+
name: "advance",
|
|
61
|
+
reason: decision.reason,
|
|
62
|
+
unitType: decision.unitType,
|
|
63
|
+
unitId: decision.unitId,
|
|
64
|
+
});
|
|
65
|
+
await this.deps.worktree.prepareForUnit(decision.unitType, decision.unitId);
|
|
66
|
+
await this.deps.worktree.syncAfterUnit(decision.unitType, decision.unitId);
|
|
67
|
+
const advanced = { kind: "advanced" };
|
|
68
|
+
await this.deps.health.postAdvanceRecord(advanced);
|
|
69
|
+
return advanced;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const recovery = await this.deps.recovery.classifyAndRecover({
|
|
73
|
+
error,
|
|
74
|
+
unitType: this.status.activeUnit?.unitType,
|
|
75
|
+
unitId: this.status.activeUnit?.unitId,
|
|
76
|
+
});
|
|
77
|
+
const result = recovery.action === "retry"
|
|
78
|
+
? { kind: "paused", reason: recovery.reason }
|
|
79
|
+
: recovery.action === "escalate"
|
|
80
|
+
? { kind: "error", reason: recovery.reason }
|
|
81
|
+
: { kind: "stopped", reason: recovery.reason };
|
|
82
|
+
if (result.kind === "paused") {
|
|
83
|
+
this.status.phase = "paused";
|
|
84
|
+
}
|
|
85
|
+
else if (result.kind === "stopped") {
|
|
86
|
+
this.status.phase = "stopped";
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
this.status.phase = "error";
|
|
90
|
+
}
|
|
91
|
+
if (result.kind === "stopped") {
|
|
92
|
+
this.lastAdvanceKey = null;
|
|
93
|
+
this.status.activeUnit = undefined;
|
|
94
|
+
}
|
|
95
|
+
this.bumpTransition();
|
|
96
|
+
const journalName = result.kind === "paused"
|
|
97
|
+
? "advance-paused"
|
|
98
|
+
: result.kind === "stopped"
|
|
99
|
+
? "advance-stopped"
|
|
100
|
+
: "advance-error";
|
|
101
|
+
await this.deps.runtime.journalTransition({ name: journalName, reason: recovery.reason });
|
|
102
|
+
if (result.kind === "paused") {
|
|
103
|
+
await this.deps.notifications.notifyLifecycle({ name: "pause", detail: recovery.reason });
|
|
104
|
+
}
|
|
105
|
+
else if (result.kind === "stopped") {
|
|
106
|
+
await this.deps.notifications.notifyLifecycle({ name: "stopped", detail: recovery.reason });
|
|
107
|
+
}
|
|
108
|
+
else if (result.kind === "error") {
|
|
109
|
+
await this.deps.notifications.notifyLifecycle({ name: "error", detail: recovery.reason });
|
|
110
|
+
}
|
|
111
|
+
await this.deps.health.postAdvanceRecord(result);
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async resume() {
|
|
116
|
+
this.lastAdvanceKey = null;
|
|
117
|
+
this.status.phase = "running";
|
|
118
|
+
this.bumpTransition();
|
|
119
|
+
await this.deps.runtime.journalTransition({ name: "resume" });
|
|
120
|
+
await this.deps.notifications.notifyLifecycle({ name: "resume" });
|
|
121
|
+
return this.advance();
|
|
122
|
+
}
|
|
123
|
+
async stop(reason) {
|
|
124
|
+
if (this.status.phase === "stopped") {
|
|
125
|
+
return { kind: "stopped", reason };
|
|
126
|
+
}
|
|
127
|
+
await this.deps.worktree.cleanupOnStop(reason);
|
|
128
|
+
this.status.phase = "stopped";
|
|
129
|
+
this.status.activeUnit = undefined;
|
|
130
|
+
this.lastAdvanceKey = null;
|
|
131
|
+
this.bumpTransition();
|
|
132
|
+
await this.deps.runtime.journalTransition({ name: "stop", reason });
|
|
133
|
+
await this.deps.notifications.notifyLifecycle({ name: "stop", detail: reason });
|
|
134
|
+
return { kind: "stopped", reason };
|
|
135
|
+
}
|
|
136
|
+
getStatus() {
|
|
137
|
+
return { ...this.status, activeUnit: this.status.activeUnit ? { ...this.status.activeUnit } : undefined };
|
|
138
|
+
}
|
|
139
|
+
bumpTransition() {
|
|
140
|
+
this.status.transitionCount += 1;
|
|
141
|
+
this.status.lastTransitionAt = now();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export function createAutoOrchestrator(deps) {
|
|
145
|
+
return new AutoOrchestrator(deps);
|
|
146
|
+
}
|
|
@@ -28,7 +28,7 @@ import { writeUnitRuntimeRecord } from "../unit-runtime.js";
|
|
|
28
28
|
import { withTimeout, FINALIZE_PRE_TIMEOUT_MS, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
|
|
29
29
|
import { getEligibleSlices } from "../slice-parallel-eligibility.js";
|
|
30
30
|
import { startSliceParallel } from "../slice-parallel-orchestrator.js";
|
|
31
|
-
import { isDbAvailable, getMilestoneSlices } from "../gsd-db.js";
|
|
31
|
+
import { isDbAvailable, getMilestoneSlices, refreshOpenDatabaseFromDisk } from "../gsd-db.js";
|
|
32
32
|
import { ensurePlanV2Graph, isEmptyPlanV2GraphResult, isMissingFinalizedContextResult } from "../uok/plan-v2.js";
|
|
33
33
|
import { resolveUokFlags } from "../uok/flags.js";
|
|
34
34
|
import { UokGateRunner } from "../uok/gate-runner.js";
|
|
@@ -42,6 +42,13 @@ import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit,
|
|
|
42
42
|
function isSamePathLocal(a, b) {
|
|
43
43
|
return normalizeWorktreePathForCompare(a) === normalizeWorktreePathForCompare(b);
|
|
44
44
|
}
|
|
45
|
+
function refreshPlanSliceRecoveryDbIfNeeded(unitType) {
|
|
46
|
+
if (unitType !== "plan-slice")
|
|
47
|
+
return true;
|
|
48
|
+
if (!isDbAvailable())
|
|
49
|
+
return true;
|
|
50
|
+
return refreshOpenDatabaseFromDisk();
|
|
51
|
+
}
|
|
45
52
|
// ─── Session timeout auto-resume state ────────────────────────────────────────
|
|
46
53
|
let consecutiveSessionTimeouts = 0;
|
|
47
54
|
const MAX_SESSION_TIMEOUT_AUTO_RESUMES = 3;
|
|
@@ -145,6 +152,22 @@ async function emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, errorCon
|
|
|
145
152
|
causedBy: { flowId: ic.flowId, seq: unitStartSeq },
|
|
146
153
|
});
|
|
147
154
|
}
|
|
155
|
+
export function _buildCancelledUnitStopReason(unitType, unitId, errorContext) {
|
|
156
|
+
const cancellationMessage = errorContext?.message ?? "unknown";
|
|
157
|
+
const isSessionCreationFailure = errorContext?.category === "session-failed";
|
|
158
|
+
if (isSessionCreationFailure) {
|
|
159
|
+
return {
|
|
160
|
+
notifyMessage: `Session creation failed for ${unitType} ${unitId}: ${cancellationMessage}. Stopping auto-mode.`,
|
|
161
|
+
stopReason: `Session creation failed: ${cancellationMessage}`,
|
|
162
|
+
loopReason: "session-failed",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
notifyMessage: `Unit ${unitType} ${unitId} aborted after dispatch: ${cancellationMessage}. Stopping auto-mode.`,
|
|
167
|
+
stopReason: `Unit aborted: ${cancellationMessage}`,
|
|
168
|
+
loopReason: "unit-aborted",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
148
171
|
async function failClosedOnFinalizeTimeout(ic, iterData, loopState, stage, startedAt) {
|
|
149
172
|
const { ctx, pi, s, deps } = ic;
|
|
150
173
|
const now = Date.now();
|
|
@@ -710,7 +733,10 @@ export async function runDispatch(ic, preData, loopState) {
|
|
|
710
733
|
// See: https://github.com/gsd-build/gsd-2/issues/2474
|
|
711
734
|
if (dispatchResult.level === "warning") {
|
|
712
735
|
ctx.ui.notify(dispatchResult.reason, "warning");
|
|
713
|
-
await deps.pauseAuto(ctx, pi
|
|
736
|
+
await deps.pauseAuto(ctx, pi, {
|
|
737
|
+
message: dispatchResult.reason,
|
|
738
|
+
category: "unknown",
|
|
739
|
+
});
|
|
714
740
|
}
|
|
715
741
|
else {
|
|
716
742
|
await closeoutAndStop(ctx, pi, s, deps, dispatchResult.reason);
|
|
@@ -770,7 +796,13 @@ export async function runDispatch(ic, preData, loopState) {
|
|
|
770
796
|
action: "artifact-found",
|
|
771
797
|
});
|
|
772
798
|
ctx.ui.notify(`Stuck recovery: artifact for ${unitType} ${unitId} found on disk. Invalidating caches.`, "info");
|
|
799
|
+
if (!refreshPlanSliceRecoveryDbIfNeeded(unitType)) {
|
|
800
|
+
ctx.ui.notify(`Stuck recovery found ${unitType} ${unitId} artifacts, but the DB refresh failed. Keeping stuck state for retry.`, "warning");
|
|
801
|
+
return { action: "continue" };
|
|
802
|
+
}
|
|
773
803
|
deps.invalidateAllCaches();
|
|
804
|
+
loopState.recentUnits.length = 0;
|
|
805
|
+
loopState.stuckRecoveryAttempts = 0;
|
|
774
806
|
return { action: "continue" };
|
|
775
807
|
}
|
|
776
808
|
ctx.ui.notify(`Stuck on ${unitType} ${unitId} (${stuckSignal.reason}). Invalidating caches and retrying.`, "warning");
|
|
@@ -778,6 +810,22 @@ export async function runDispatch(ic, preData, loopState) {
|
|
|
778
810
|
}
|
|
779
811
|
else {
|
|
780
812
|
// Level 2: hard stop — genuinely stuck
|
|
813
|
+
deps.invalidateAllCaches();
|
|
814
|
+
const artifactExists = verifyExpectedArtifact(unitType, unitId, s.basePath);
|
|
815
|
+
if (artifactExists && unitType !== "complete-milestone") {
|
|
816
|
+
debugLog("autoLoop", {
|
|
817
|
+
phase: "stuck-recovery",
|
|
818
|
+
level: 2,
|
|
819
|
+
action: "artifact-found",
|
|
820
|
+
});
|
|
821
|
+
ctx.ui.notify(`Stuck recovery: artifact for ${unitType} ${unitId} found on disk after cache invalidation. Continuing.`, "info");
|
|
822
|
+
if (refreshPlanSliceRecoveryDbIfNeeded(unitType)) {
|
|
823
|
+
loopState.recentUnits.length = 0;
|
|
824
|
+
loopState.stuckRecoveryAttempts = 0;
|
|
825
|
+
return { action: "continue" };
|
|
826
|
+
}
|
|
827
|
+
ctx.ui.notify(`Stuck recovery found ${unitType} ${unitId} artifacts, but the DB refresh failed. Stopping for manual recovery.`, "warning");
|
|
828
|
+
}
|
|
781
829
|
debugLog("autoLoop", {
|
|
782
830
|
phase: "stuck-detected",
|
|
783
831
|
unitType,
|
|
@@ -1384,10 +1432,11 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
1384
1432
|
}
|
|
1385
1433
|
await deps.autoCommitUnit?.(s.basePath, unitType, unitId, ctx);
|
|
1386
1434
|
await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1435
|
+
const cancelledStop = _buildCancelledUnitStopReason(unitType, unitId, unitResult.errorContext);
|
|
1436
|
+
ctx.ui.notify(cancelledStop.notifyMessage, "warning");
|
|
1437
|
+
await deps.stopAuto(ctx, pi, cancelledStop.stopReason);
|
|
1438
|
+
debugLog("autoLoop", { phase: "exit", reason: cancelledStop.loopReason });
|
|
1439
|
+
return { action: "break", reason: cancelledStop.loopReason };
|
|
1391
1440
|
}
|
|
1392
1441
|
// ── Immediate unit closeout (metrics, activity log, memory) ────────
|
|
1393
1442
|
// Run right after runUnit() returns so telemetry is never lost to a
|
|
@@ -148,6 +148,8 @@ export class AutoSession {
|
|
|
148
148
|
// ── Remote command polling ───────────────────────────────────────────────
|
|
149
149
|
/** Cleanup function returned by startCommandPolling(); null when not running. */
|
|
150
150
|
commandPollingCleanup = null;
|
|
151
|
+
// ── Orchestration seam ───────────────────────────────────────────────────
|
|
152
|
+
orchestration = null;
|
|
151
153
|
// ── Loop promise state ──────────────────────────────────────────────────
|
|
152
154
|
// Per-unit resolve function and session-switch guard live at module level
|
|
153
155
|
// in auto-loop.ts (_currentResolve, _sessionSwitchInFlight).
|
|
@@ -268,9 +270,12 @@ export class AutoSession {
|
|
|
268
270
|
this.sigtermHandler = null;
|
|
269
271
|
// Remote command polling — cleanup must be called before reset (auto.ts stopAuto)
|
|
270
272
|
this.commandPollingCleanup = null;
|
|
273
|
+
// Orchestration seam
|
|
274
|
+
this.orchestration = null;
|
|
271
275
|
// Loop promise state lives in auto-loop.ts module scope
|
|
272
276
|
}
|
|
273
277
|
toJSON() {
|
|
278
|
+
const orchestrationStatus = this.orchestration?.getStatus();
|
|
274
279
|
return {
|
|
275
280
|
active: this.active,
|
|
276
281
|
paused: this.paused,
|
|
@@ -280,6 +285,9 @@ export class AutoSession {
|
|
|
280
285
|
activeRunDir: this.activeRunDir,
|
|
281
286
|
currentMilestoneId: this.currentMilestoneId,
|
|
282
287
|
currentUnit: this.currentUnit,
|
|
288
|
+
orchestrationPhase: orchestrationStatus?.phase,
|
|
289
|
+
orchestrationTransitionCount: orchestrationStatus?.transitionCount,
|
|
290
|
+
orchestrationLastTransitionAt: orchestrationStatus?.lastTransitionAt,
|
|
283
291
|
unitDispatchCount: Object.fromEntries(this.unitDispatchCount),
|
|
284
292
|
};
|
|
285
293
|
}
|
|
@@ -12,7 +12,7 @@ import { appendEvent } from "./workflow-events.js";
|
|
|
12
12
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
13
13
|
import { clearParseCache } from "./files.js";
|
|
14
14
|
import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
|
|
15
|
-
import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone } from "./gsd-db.js";
|
|
15
|
+
import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone, refreshOpenDatabaseFromDisk } from "./gsd-db.js";
|
|
16
16
|
import { isValidationTerminal } from "./state.js";
|
|
17
17
|
import { getErrorMessage } from "./error-utils.js";
|
|
18
18
|
import { logWarning, logError } from "./workflow-logger.js";
|
|
@@ -510,66 +510,32 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
|
|
|
510
510
|
return false;
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
|
-
// plan-slice
|
|
514
|
-
//
|
|
515
|
-
//
|
|
516
|
-
// unit gets skipped — but deriveState still returns phase:"planning" because the
|
|
517
|
-
// plan has no tasks, creating an infinite skip loop (#699).
|
|
518
|
-
if (unitType === "plan-slice") {
|
|
519
|
-
const planContent = readFileSync(absPath, "utf-8");
|
|
520
|
-
// Accept checkbox-style (- [x] **T01: ...) or heading-style (### T01 -- / ### T01: / ### T01 —)
|
|
521
|
-
const hasCheckboxTask = /^- \[[xX ]\] \*\*T\d+:/m.test(planContent);
|
|
522
|
-
const hasHeadingTask = /^#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
|
|
523
|
-
if (!hasCheckboxTask && !hasHeadingTask) {
|
|
524
|
-
logWarning("recovery", `verify-fail ${unitType} ${unitId}: plan has no task checkbox/heading (len=${planContent.length}) at ${absPath}`);
|
|
525
|
-
return false;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
// execute-task: DB status is authoritative. Fall back to checked-checkbox
|
|
529
|
-
// detection when the DB is unavailable (unmigrated projects), or when the
|
|
530
|
-
// disk artifacts already reflect completion but the DB replay is one beat
|
|
531
|
-
// behind the completion write.
|
|
532
|
-
if (unitType === "execute-task") {
|
|
533
|
-
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
534
|
-
if (mid && sid && tid) {
|
|
535
|
-
const dbTask = getTask(mid, sid, tid);
|
|
536
|
-
if (dbTask) {
|
|
537
|
-
if (dbTask.status !== "complete" && dbTask.status !== "done" && !hasCheckedTaskCompletionOnDisk(base, mid, sid, tid)) {
|
|
538
|
-
return false;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
else if (!isDbAvailable()) {
|
|
542
|
-
// LEGACY: Pre-migration fallback for projects without DB.
|
|
543
|
-
// Require a CHECKED checkbox — a bare heading or unchecked checkbox
|
|
544
|
-
// does not prove gsd_complete_task ran. Summary file on disk alone
|
|
545
|
-
// is not sufficient evidence (could be a rogue write) (#3607).
|
|
546
|
-
if (!hasCheckedTaskCompletionOnDisk(base, mid, sid, tid))
|
|
547
|
-
return false;
|
|
548
|
-
}
|
|
549
|
-
else {
|
|
550
|
-
// DB available but task row not found — completion tool never ran (#3607)
|
|
551
|
-
return false;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
// plan-slice must also produce individual task plan files for every task listed
|
|
556
|
-
// in the slice plan. Without this check, a plan-slice that wrote S{sid}-PLAN.md
|
|
557
|
-
// but omitted T{tid}-PLAN.md files would be marked complete, causing execute-task
|
|
558
|
-
// to dispatch with a missing task plan (see issue #739).
|
|
513
|
+
// plan-slice verification is DB-primary. The slice plan is a projection, so
|
|
514
|
+
// DB task rows prove the slice was planned even if the rendered markdown no
|
|
515
|
+
// longer uses legacy checkbox/heading syntax.
|
|
559
516
|
if (unitType === "plan-slice") {
|
|
560
517
|
const { milestone: mid, slice: sid } = parseUnitId(unitId);
|
|
561
518
|
if (mid && sid) {
|
|
562
519
|
try {
|
|
563
|
-
// DB primary path — get task IDs to verify task plan files exist
|
|
564
520
|
let taskIds = null;
|
|
565
521
|
if (isDbAvailable()) {
|
|
566
|
-
const
|
|
567
|
-
if (
|
|
568
|
-
|
|
522
|
+
const refreshed = refreshOpenDatabaseFromDisk();
|
|
523
|
+
if (refreshed) {
|
|
524
|
+
const tasks = getSliceTasks(mid, sid);
|
|
525
|
+
if (tasks.length > 0)
|
|
526
|
+
taskIds = tasks.map(t => t.id);
|
|
527
|
+
}
|
|
569
528
|
}
|
|
570
529
|
if (!taskIds) {
|
|
571
|
-
// LEGACY: DB unavailable or no tasks in DB
|
|
530
|
+
// LEGACY: DB unavailable or no tasks in DB. Require actual task
|
|
531
|
+
// entries so an empty scaffold cannot advance the pipeline (#699).
|
|
572
532
|
const planContent = readFileSync(absPath, "utf-8");
|
|
533
|
+
const hasCheckboxTask = /^\s*- \[[xX ]\] \*\*T\d+:/m.test(planContent);
|
|
534
|
+
const hasHeadingTask = /^\s*#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
|
|
535
|
+
if (!hasCheckboxTask && !hasHeadingTask) {
|
|
536
|
+
logWarning("recovery", `verify-fail ${unitType} ${unitId}: plan has no task checkbox/heading (len=${planContent.length}) at ${absPath}`);
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
573
539
|
const plan = parseLegacyPlan(planContent);
|
|
574
540
|
if (plan.tasks.length > 0)
|
|
575
541
|
taskIds = plan.tasks.map((t) => t.id);
|
|
@@ -595,6 +561,33 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
|
|
|
595
561
|
}
|
|
596
562
|
}
|
|
597
563
|
}
|
|
564
|
+
// execute-task: DB status is authoritative. Fall back to checked-checkbox
|
|
565
|
+
// detection when the DB is unavailable (unmigrated projects), or when the
|
|
566
|
+
// disk artifacts already reflect completion but the DB replay is one beat
|
|
567
|
+
// behind the completion write.
|
|
568
|
+
if (unitType === "execute-task") {
|
|
569
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
570
|
+
if (mid && sid && tid) {
|
|
571
|
+
const dbTask = getTask(mid, sid, tid);
|
|
572
|
+
if (dbTask) {
|
|
573
|
+
if (dbTask.status !== "complete" && dbTask.status !== "done" && !hasCheckedTaskCompletionOnDisk(base, mid, sid, tid)) {
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
else if (!isDbAvailable()) {
|
|
578
|
+
// LEGACY: Pre-migration fallback for projects without DB.
|
|
579
|
+
// Require a CHECKED checkbox — a bare heading or unchecked checkbox
|
|
580
|
+
// does not prove gsd_complete_task ran. Summary file on disk alone
|
|
581
|
+
// is not sufficient evidence (could be a rogue write) (#3607).
|
|
582
|
+
if (!hasCheckedTaskCompletionOnDisk(base, mid, sid, tid))
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
// DB available but task row not found — completion tool never ran (#3607)
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
598
591
|
// complete-slice: DB status is authoritative for whether the slice is done.
|
|
599
592
|
// Fall back to file-based check (roadmap [x]) when DB is unavailable.
|
|
600
593
|
if (unitType === "complete-slice") {
|
|
@@ -3,11 +3,15 @@ import { AutoSession } from "./auto/session.js";
|
|
|
3
3
|
import { isDeterministicPolicyError, isQueuedUserMessageSkip, isToolInvocationError, markToolEnd as markTrackedToolEnd, markToolStart as markTrackedToolStart, } from "./auto-tool-tracking.js";
|
|
4
4
|
export const autoSession = new AutoSession();
|
|
5
5
|
export function getAutoRuntimeSnapshot() {
|
|
6
|
+
const orchestrationStatus = autoSession.orchestration?.getStatus();
|
|
6
7
|
return {
|
|
7
8
|
active: autoSession.active,
|
|
8
9
|
paused: autoSession.paused,
|
|
9
10
|
currentUnit: autoSession.currentUnit ? { ...autoSession.currentUnit } : null,
|
|
10
11
|
basePath: autoSession.basePath,
|
|
12
|
+
orchestrationPhase: orchestrationStatus?.phase,
|
|
13
|
+
orchestrationTransitionCount: orchestrationStatus?.transitionCount,
|
|
14
|
+
orchestrationLastTransitionAt: orchestrationStatus?.lastTransitionAt,
|
|
11
15
|
};
|
|
12
16
|
}
|
|
13
17
|
export function isAutoActive() {
|