gsd-pi 2.74.0-dev.703eabc → 2.74.0-dev.ffbcc03
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/resources/extensions/gsd/auto-recovery.js +24 -10
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -5
- package/dist/resources/extensions/gsd/cache.js +16 -5
- package/dist/resources/extensions/gsd/guided-flow.js +1 -1
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +15 -30
- 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 +14 -14
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- 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/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- 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/package.json +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +88 -6
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/workflow-tools.ts +95 -10
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +8 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +17 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +19 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +29 -9
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +16 -5
- package/src/resources/extensions/gsd/cache.ts +16 -5
- package/src/resources/extensions/gsd/guided-flow.ts +1 -1
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +15 -31
- package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +272 -0
- /package/dist/web/standalone/.next/static/{3U-oZ5FT59BM7sm2GInic → kn6xzWKYnogsxp2b6RpDD}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{3U-oZ5FT59BM7sm2GInic → kn6xzWKYnogsxp2b6RpDD}/_ssgManifest.js +0 -0
|
@@ -215,20 +215,28 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
|
|
|
215
215
|
const absPath = resolveExpectedArtifactPath(unitType, unitId, base);
|
|
216
216
|
// For unit types with no verifiable artifact (null path), the parent directory
|
|
217
217
|
// is missing on disk — treat as stale completion state so the key gets evicted (#313).
|
|
218
|
-
if (!absPath)
|
|
218
|
+
if (!absPath) {
|
|
219
|
+
logWarning("recovery", `verify-fail ${unitType} ${unitId}: resolveExpectedArtifactPath returned null (parent dir missing)`);
|
|
219
220
|
return false;
|
|
220
|
-
|
|
221
|
+
}
|
|
222
|
+
if (!existsSync(absPath)) {
|
|
223
|
+
logWarning("recovery", `verify-fail ${unitType} ${unitId}: existsSync false for ${absPath}`);
|
|
221
224
|
return false;
|
|
225
|
+
}
|
|
222
226
|
if (unitType === "validate-milestone") {
|
|
223
227
|
const validationContent = readFileSync(absPath, "utf-8");
|
|
224
|
-
if (!isValidationTerminal(validationContent))
|
|
228
|
+
if (!isValidationTerminal(validationContent)) {
|
|
229
|
+
logWarning("recovery", `verify-fail ${unitType} ${unitId}: validation not terminal (len=${validationContent.length}) at ${absPath}`);
|
|
225
230
|
return false;
|
|
231
|
+
}
|
|
226
232
|
}
|
|
227
233
|
if (unitType === "plan-milestone") {
|
|
228
234
|
try {
|
|
229
235
|
const roadmap = parseLegacyRoadmap(readFileSync(absPath, "utf-8"));
|
|
230
|
-
if (roadmap.slices.length === 0)
|
|
236
|
+
if (roadmap.slices.length === 0) {
|
|
237
|
+
logWarning("recovery", `verify-fail ${unitType} ${unitId}: roadmap has zero slices at ${absPath}`);
|
|
231
238
|
return false;
|
|
239
|
+
}
|
|
232
240
|
}
|
|
233
241
|
catch (err) {
|
|
234
242
|
logWarning("recovery", `plan-milestone roadmap verification failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -245,8 +253,10 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
|
|
|
245
253
|
// Accept checkbox-style (- [x] **T01: ...) or heading-style (### T01 -- / ### T01: / ### T01 —)
|
|
246
254
|
const hasCheckboxTask = /^- \[[xX ]\] \*\*T\d+:/m.test(planContent);
|
|
247
255
|
const hasHeadingTask = /^#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
|
|
248
|
-
if (!hasCheckboxTask && !hasHeadingTask)
|
|
256
|
+
if (!hasCheckboxTask && !hasHeadingTask) {
|
|
257
|
+
logWarning("recovery", `verify-fail ${unitType} ${unitId}: plan has no task checkbox/heading (len=${planContent.length}) at ${absPath}`);
|
|
249
258
|
return false;
|
|
259
|
+
}
|
|
250
260
|
}
|
|
251
261
|
// execute-task: DB status is authoritative. Fall back to checked-checkbox
|
|
252
262
|
// detection when the DB is unavailable (unmigrated projects).
|
|
@@ -306,11 +316,15 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
|
|
|
306
316
|
}
|
|
307
317
|
if (taskIds && taskIds.length > 0) {
|
|
308
318
|
const tasksDir = resolveTasksDir(base, mid, sid);
|
|
309
|
-
if (tasksDir) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
319
|
+
if (!tasksDir) {
|
|
320
|
+
logWarning("recovery", `verify-fail ${unitType} ${unitId}: resolveTasksDir returned null for ${mid}/${sid}`);
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
for (const tid of taskIds) {
|
|
324
|
+
const taskPlanFile = join(tasksDir, `${tid}-PLAN.md`);
|
|
325
|
+
if (!existsSync(taskPlanFile)) {
|
|
326
|
+
logWarning("recovery", `verify-fail ${unitType} ${unitId}: task plan missing ${taskPlanFile}`);
|
|
327
|
+
return false;
|
|
314
328
|
}
|
|
315
329
|
}
|
|
316
330
|
}
|
|
@@ -41,8 +41,12 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
41
41
|
resetToolCallLoopGuard();
|
|
42
42
|
resetAskUserQuestionsCache();
|
|
43
43
|
await syncServiceTierStatus(ctx);
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// Skip MCP auto-prep when running inside an auto-worktree (see session_switch below).
|
|
45
|
+
const { isInAutoWorktree } = await import("../auto-worktree.js");
|
|
46
|
+
if (!isInAutoWorktree(process.cwd())) {
|
|
47
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
48
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
49
|
+
}
|
|
46
50
|
// Apply show_token_cost preference (#1515)
|
|
47
51
|
try {
|
|
48
52
|
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
@@ -82,8 +86,15 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
82
86
|
resetAskUserQuestionsCache();
|
|
83
87
|
clearDiscussionFlowState();
|
|
84
88
|
await syncServiceTierStatus(ctx);
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
// Skip MCP auto-prep when running inside an auto-worktree. The worktree
|
|
90
|
+
// already has .mcp.json from createAutoWorktree, and re-running the writer
|
|
91
|
+
// post-chdir rewrites the file mid-run (non-idempotent due to cwd-relative
|
|
92
|
+
// CLI path resolution), dirtying the tree and breaking the milestone merge.
|
|
93
|
+
const { isInAutoWorktree } = await import("../auto-worktree.js");
|
|
94
|
+
if (!isInAutoWorktree(process.cwd())) {
|
|
95
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
96
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
97
|
+
}
|
|
87
98
|
loadToolApiKeys();
|
|
88
99
|
});
|
|
89
100
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
@@ -281,7 +292,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
281
292
|
pi.on("tool_call", async (event, ctx) => {
|
|
282
293
|
if (!isAutoActive())
|
|
283
294
|
return;
|
|
284
|
-
safetyRecordToolCall(event.toolName, event.input);
|
|
295
|
+
safetyRecordToolCall(event.toolCallId, event.toolName, event.input);
|
|
285
296
|
// Destructive command classification (warn only, never block)
|
|
286
297
|
if (isToolCallEventType("bash", event)) {
|
|
287
298
|
const classification = classifyCommand(event.input.command);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// GSD Extension — Unified Cache Invalidation
|
|
2
2
|
//
|
|
3
|
-
// Three module-scoped caches exist across the GSD extension:
|
|
3
|
+
// Three module-scoped read caches exist across the GSD extension:
|
|
4
4
|
// 1. State cache (state.ts) — memoized deriveState() result
|
|
5
5
|
// 2. Path cache (paths.ts) — directory listing results (readdirSync)
|
|
6
6
|
// 3. Parse cache (files.ts) — parsed markdown file results
|
|
@@ -8,20 +8,31 @@
|
|
|
8
8
|
// After any file write that changes .gsd/ contents, all three must be
|
|
9
9
|
// invalidated together to prevent stale reads. This module provides a
|
|
10
10
|
// single function that clears all three atomically.
|
|
11
|
+
//
|
|
12
|
+
// NOTE: The DB `artifacts` table is NOT included here. Earlier versions
|
|
13
|
+
// called clearArtifacts() as part of this bundle (#793), intending to
|
|
14
|
+
// force deriveState() to re-parse from disk when files were edited
|
|
15
|
+
// out-of-band. But invalidateAllCaches() fires on every post-unit pass,
|
|
16
|
+
// so bundling a DESTRUCTIVE `DELETE FROM artifacts` with routine cache
|
|
17
|
+
// invalidation meant every row written by saveArtifactToDb / writeAndStore
|
|
18
|
+
// was wiped within seconds — leaving the milestone completed on disk but
|
|
19
|
+
// the `artifacts` table empty and the agent looping on "file exists but
|
|
20
|
+
// DB record missing" recovery calls. If a call site genuinely needs the
|
|
21
|
+
// artifact table cleared after an out-of-band file mutation, it should
|
|
22
|
+
// invoke clearArtifacts() from gsd-db.js explicitly — do not add it back
|
|
23
|
+
// here.
|
|
11
24
|
import { invalidateStateCache } from './state.js';
|
|
12
25
|
import { clearPathCache } from './paths.js';
|
|
13
26
|
import { clearParseCache } from './files.js';
|
|
14
|
-
import { clearArtifacts } from './gsd-db.js';
|
|
15
27
|
/**
|
|
16
|
-
* Invalidate all GSD runtime caches in one call.
|
|
28
|
+
* Invalidate all GSD runtime read caches in one call.
|
|
17
29
|
*
|
|
18
30
|
* Call this after file writes, milestone transitions, merge reconciliation,
|
|
19
31
|
* or any operation that changes .gsd/ contents on disk. Forgetting to clear
|
|
20
|
-
* any single cache causes stale reads (see #431
|
|
32
|
+
* any single cache causes stale reads (see #431).
|
|
21
33
|
*/
|
|
22
34
|
export function invalidateAllCaches() {
|
|
23
35
|
invalidateStateCache();
|
|
24
36
|
clearPathCache();
|
|
25
37
|
clearParseCache();
|
|
26
|
-
clearArtifacts();
|
|
27
38
|
}
|
|
@@ -219,7 +219,7 @@ export function checkAutoStartAfterDiscuss() {
|
|
|
219
219
|
logWarning("guided", `manifest unlink failed: ${e.message}`);
|
|
220
220
|
}
|
|
221
221
|
pendingAutoStartMap.delete(basePath);
|
|
222
|
-
ctx.ui.notify(`Milestone ${milestoneId} ready.`, "
|
|
222
|
+
ctx.ui.notify(`Milestone ${milestoneId} ready.`, "success");
|
|
223
223
|
startAutoDetached(ctx, pi, basePath, false, { step });
|
|
224
224
|
return true;
|
|
225
225
|
}
|
|
@@ -32,11 +32,11 @@ export function getFilePaths() {
|
|
|
32
32
|
* Record a tool call at dispatch time (before execution).
|
|
33
33
|
* Exit codes and output are filled in by recordToolResult after execution.
|
|
34
34
|
*/
|
|
35
|
-
export function recordToolCall(toolName, input) {
|
|
35
|
+
export function recordToolCall(toolCallId, toolName, input) {
|
|
36
36
|
if (toolName === "bash" || toolName === "Bash") {
|
|
37
37
|
unitEvidence.push({
|
|
38
38
|
kind: "bash",
|
|
39
|
-
toolCallId
|
|
39
|
+
toolCallId,
|
|
40
40
|
command: String(input.command ?? ""),
|
|
41
41
|
exitCode: -1,
|
|
42
42
|
outputSnippet: "",
|
|
@@ -46,7 +46,7 @@ export function recordToolCall(toolName, input) {
|
|
|
46
46
|
else if (toolName === "write" || toolName === "Write") {
|
|
47
47
|
unitEvidence.push({
|
|
48
48
|
kind: "write",
|
|
49
|
-
toolCallId
|
|
49
|
+
toolCallId,
|
|
50
50
|
path: String(input.file_path ?? input.path ?? ""),
|
|
51
51
|
timestamp: Date.now(),
|
|
52
52
|
});
|
|
@@ -54,44 +54,29 @@ export function recordToolCall(toolName, input) {
|
|
|
54
54
|
else if (toolName === "edit" || toolName === "Edit") {
|
|
55
55
|
unitEvidence.push({
|
|
56
56
|
kind: "edit",
|
|
57
|
-
toolCallId
|
|
57
|
+
toolCallId,
|
|
58
58
|
path: String(input.file_path ?? input.path ?? ""),
|
|
59
59
|
timestamp: Date.now(),
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
* Record a tool execution result. Matches the
|
|
65
|
-
*
|
|
64
|
+
* Record a tool execution result. Matches the entry by toolCallId (assigned
|
|
65
|
+
* at dispatch time) and fills in exit code + output. Prior versions matched
|
|
66
|
+
* by `kind + empty-string` which corrupted parallel tool calls.
|
|
66
67
|
*/
|
|
67
68
|
export function recordToolResult(toolCallId, toolName, result, isError) {
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
entry.exitCode = exitMatch ? Number(exitMatch[1]) : (isError ? 1 : 0);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
else if (normalizedName === "write" || normalizedName === "edit") {
|
|
80
|
-
const entry = findLastUnresolved(normalizedName);
|
|
81
|
-
if (entry) {
|
|
82
|
-
entry.toolCallId = toolCallId;
|
|
83
|
-
}
|
|
69
|
+
const entry = unitEvidence.find(e => e.toolCallId === toolCallId);
|
|
70
|
+
if (!entry)
|
|
71
|
+
return;
|
|
72
|
+
if (entry.kind === "bash") {
|
|
73
|
+
const text = extractResultText(result);
|
|
74
|
+
entry.outputSnippet = text.slice(0, 500);
|
|
75
|
+
const exitMatch = text.match(/Command exited with code (\d+)/);
|
|
76
|
+
entry.exitCode = exitMatch ? Number(exitMatch[1]) : (isError ? 1 : 0);
|
|
84
77
|
}
|
|
85
78
|
}
|
|
86
79
|
// ─── Internals ──────────────────────────────────────────────────────────────
|
|
87
|
-
function findLastUnresolved(kind) {
|
|
88
|
-
for (let i = unitEvidence.length - 1; i >= 0; i--) {
|
|
89
|
-
if (unitEvidence[i].kind === kind && unitEvidence[i].toolCallId === "") {
|
|
90
|
-
return unitEvidence[i];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
80
|
function extractResultText(result) {
|
|
96
81
|
if (typeof result === "string")
|
|
97
82
|
return result;
|