gsd-pi 2.75.0-dev.2203010a0 → 2.75.0-dev.96d4bb599
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/onboarding.d.ts +5 -1
- package/dist/onboarding.js +5 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +23 -19
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +128 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +17 -4
- package/dist/resources/extensions/gsd/commands/handlers/onboarding.js +52 -68
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands-memory.js +462 -0
- package/dist/resources/extensions/gsd/gsd-db.js +237 -4
- package/dist/resources/extensions/gsd/memory-embeddings.js +219 -0
- package/dist/resources/extensions/gsd/memory-extractor.js +78 -27
- package/dist/resources/extensions/gsd/memory-ingest.js +218 -0
- package/dist/resources/extensions/gsd/memory-relations.js +189 -0
- package/dist/resources/extensions/gsd/memory-source-store.js +113 -0
- package/dist/resources/extensions/gsd/memory-store.js +299 -6
- package/dist/resources/extensions/gsd/model-router.js +9 -5
- package/dist/resources/extensions/gsd/notification-overlay.js +7 -22
- package/dist/resources/extensions/gsd/tools/memory-tools.js +306 -0
- package/dist/resources/extensions/gsd/tools/skip-slice.js +78 -0
- 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/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +12 -10
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +8 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +113 -14
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/mcp-server.test.ts +40 -4
- package/packages/mcp-server/src/server.ts +12 -10
- package/packages/mcp-server/src/session-manager.ts +10 -3
- package/packages/mcp-server/src/workflow-tools.test.ts +91 -1
- package/packages/mcp-server/src/workflow-tools.ts +128 -18
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +24 -20
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +158 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +20 -4
- package/src/resources/extensions/gsd/commands/handlers/onboarding.ts +65 -98
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands-memory.ts +551 -0
- package/src/resources/extensions/gsd/gsd-db.ts +273 -4
- package/src/resources/extensions/gsd/memory-embeddings.ts +235 -0
- package/src/resources/extensions/gsd/memory-extractor.ts +100 -34
- package/src/resources/extensions/gsd/memory-ingest.ts +286 -0
- package/src/resources/extensions/gsd/memory-relations.ts +240 -0
- package/src/resources/extensions/gsd/memory-source-store.ts +138 -0
- package/src/resources/extensions/gsd/memory-store.ts +351 -7
- package/src/resources/extensions/gsd/model-router.ts +10 -5
- package/src/resources/extensions/gsd/notification-overlay.ts +9 -19
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-embeddings.test.ts +213 -0
- package/src/resources/extensions/gsd/tests/memory-ingest.test.ts +153 -0
- package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/memory-relations.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +295 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +56 -37
- package/src/resources/extensions/gsd/tests/skip-slice-cascades-tasks.test.ts +125 -0
- package/src/resources/extensions/gsd/tools/memory-tools.ts +380 -0
- package/src/resources/extensions/gsd/tools/skip-slice.ts +133 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
- /package/dist/web/standalone/.next/static/{8FZqxNe9FxQDmsbRzR8tA → o61X3klsB6C0UE0X1x3PA}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{8FZqxNe9FxQDmsbRzR8tA → o61X3klsB6C0UE0X1x3PA}/_ssgManifest.js +0 -0
|
@@ -32,26 +32,47 @@ function resolveExternalStateRoot(allowedRoot) {
|
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
function validateProjectDir(projectDir, env = process.env) {
|
|
35
|
+
export function validateProjectDir(projectDir, env = process.env) {
|
|
36
36
|
if (!isAbsolute(projectDir)) {
|
|
37
37
|
throw new Error(`projectDir must be an absolute path. Received: ${projectDir}`);
|
|
38
38
|
}
|
|
39
|
-
const
|
|
39
|
+
const lexicallyResolved = resolve(projectDir);
|
|
40
|
+
// Resolve symlinks on the candidate before the containment check so that a
|
|
41
|
+
// symlink inside the allowed root pointing outside of it cannot bypass the
|
|
42
|
+
// guard. Falls back to the lexical path if the candidate does not exist yet
|
|
43
|
+
// (legitimate for a brand-new worktree dir about to be created).
|
|
44
|
+
const resolvedProjectDir = safeRealpath(lexicallyResolved);
|
|
40
45
|
const allowedRoot = getAllowedProjectRoot(env);
|
|
41
46
|
if (!allowedRoot)
|
|
42
47
|
return resolvedProjectDir;
|
|
43
|
-
|
|
48
|
+
const resolvedAllowedRoot = safeRealpath(allowedRoot);
|
|
49
|
+
if (isWithinRoot(resolvedProjectDir, resolvedAllowedRoot))
|
|
44
50
|
return resolvedProjectDir;
|
|
45
51
|
// External state layout: `<allowedRoot>/.gsd` may be a symlink into
|
|
46
52
|
// `~/.gsd/projects/<hash>/`, and auto-worktrees live under
|
|
47
53
|
// `~/.gsd/projects/<hash>/worktrees/<MID>/`. Accept candidates that are
|
|
48
54
|
// under the realpath of `<allowedRoot>/.gsd` — they belong to this project
|
|
49
55
|
// even though their absolute path is outside allowedRoot (#issue-a44).
|
|
50
|
-
const externalRoot = resolveExternalStateRoot(
|
|
56
|
+
const externalRoot = resolveExternalStateRoot(resolvedAllowedRoot);
|
|
51
57
|
if (externalRoot && isWithinRoot(resolvedProjectDir, externalRoot)) {
|
|
52
58
|
return resolvedProjectDir;
|
|
53
59
|
}
|
|
54
|
-
throw new Error(`projectDir must stay within the configured workflow project root. Received: ${resolvedProjectDir}; allowed root: ${
|
|
60
|
+
throw new Error(`projectDir must stay within the configured workflow project root. Received: ${resolvedProjectDir}; allowed root: ${resolvedAllowedRoot}`);
|
|
61
|
+
}
|
|
62
|
+
function safeRealpath(path) {
|
|
63
|
+
try {
|
|
64
|
+
return realpathSync(path);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
// Only fall back for non-existent paths — a legitimate case when a worktree
|
|
68
|
+
// directory hasn't been created yet. Permission errors (EACCES), not-a-
|
|
69
|
+
// directory (ENOTDIR), etc. must propagate so we do not silently degrade
|
|
70
|
+
// to a lexical-only containment check that a restricted symlink could
|
|
71
|
+
// bypass.
|
|
72
|
+
if (err?.code === "ENOENT")
|
|
73
|
+
return path;
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
55
76
|
}
|
|
56
77
|
function parseToolArgs(schema, args) {
|
|
57
78
|
return schema.parse(args);
|
|
@@ -276,17 +297,55 @@ export const WORKFLOW_TOOL_NAMES = [
|
|
|
276
297
|
"gsd_milestone_status",
|
|
277
298
|
"gsd_journal_query",
|
|
278
299
|
];
|
|
300
|
+
const DEFAULT_WORKFLOW_OP_TIMEOUT_MS = 5 * 60 * 1000;
|
|
301
|
+
function getWorkflowOpTimeoutMs(env = process.env) {
|
|
302
|
+
const raw = env.GSD_MCP_WORKFLOW_TIMEOUT_MS?.trim();
|
|
303
|
+
if (!raw)
|
|
304
|
+
return DEFAULT_WORKFLOW_OP_TIMEOUT_MS;
|
|
305
|
+
const parsed = Number.parseInt(raw, 10);
|
|
306
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
307
|
+
return DEFAULT_WORKFLOW_OP_TIMEOUT_MS;
|
|
308
|
+
return parsed; // 0 disables the timeout
|
|
309
|
+
}
|
|
279
310
|
async function runSerializedWorkflowOperation(fn) {
|
|
280
311
|
// The shared DB adapter and workflow log base path are process-global, so
|
|
281
312
|
// workflow MCP mutations must not overlap within a single server process.
|
|
313
|
+
// A per-operation deadline prevents a single stuck call from wedging every
|
|
314
|
+
// subsequent write for the lifetime of the process.
|
|
315
|
+
//
|
|
316
|
+
// Known limitation: on timeout we surface an error and release the queue,
|
|
317
|
+
// but Promise.race cannot cancel the underlying `fn()` — it may continue
|
|
318
|
+
// running in the background and overlap with the next admitted operation.
|
|
319
|
+
// Proper cancellation requires threading an AbortSignal through every
|
|
320
|
+
// workflow executor (`workflow-tool-executors.ts` and friends), which is
|
|
321
|
+
// a larger change. The current trade-off: risk a theoretical overlap after
|
|
322
|
+
// a 5-minute wall-clock timeout vs permanently wedging the server. The
|
|
323
|
+
// overlap window is bounded by how long the zombie `fn()` keeps running;
|
|
324
|
+
// in practice DB writes complete quickly even when the caller gave up.
|
|
282
325
|
const prior = workflowExecutionQueue;
|
|
283
326
|
let release;
|
|
284
327
|
workflowExecutionQueue = new Promise((resolve) => {
|
|
285
328
|
release = resolve;
|
|
286
329
|
});
|
|
287
330
|
await prior;
|
|
331
|
+
const timeoutMs = getWorkflowOpTimeoutMs();
|
|
288
332
|
try {
|
|
289
|
-
|
|
333
|
+
if (timeoutMs === 0) {
|
|
334
|
+
return await fn();
|
|
335
|
+
}
|
|
336
|
+
let timer;
|
|
337
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
338
|
+
timer = setTimeout(() => {
|
|
339
|
+
reject(new Error(`Workflow operation exceeded ${timeoutMs}ms deadline (GSD_MCP_WORKFLOW_TIMEOUT_MS)`));
|
|
340
|
+
}, timeoutMs);
|
|
341
|
+
});
|
|
342
|
+
try {
|
|
343
|
+
return await Promise.race([fn(), timeoutPromise]);
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
if (timer)
|
|
347
|
+
clearTimeout(timer);
|
|
348
|
+
}
|
|
290
349
|
}
|
|
291
350
|
finally {
|
|
292
351
|
release();
|
|
@@ -398,10 +457,14 @@ const planMilestoneParams = {
|
|
|
398
457
|
depends: z.array(z.string()),
|
|
399
458
|
demo: z.string(),
|
|
400
459
|
goal: z.string(),
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
460
|
+
// ADR-011: heavy planning fields are optional for sketch slices; required for full slices.
|
|
461
|
+
successCriteria: z.string().optional(),
|
|
462
|
+
proofLevel: z.string().optional(),
|
|
463
|
+
integrationClosure: z.string().optional(),
|
|
464
|
+
observabilityImpact: z.string().optional(),
|
|
465
|
+
// ADR-011 sketch-then-refine fields.
|
|
466
|
+
isSketch: z.boolean().optional().describe("ADR-011: true marks this slice as a sketch awaiting refine-slice expansion"),
|
|
467
|
+
sketchScope: z.string().optional().describe("ADR-011: 2-3 sentence scope boundary, required when isSketch=true"),
|
|
405
468
|
})).describe("Planned slices for the milestone"),
|
|
406
469
|
status: z.string().optional().describe("Milestone status"),
|
|
407
470
|
dependsOn: z.array(z.string()).optional().describe("Milestone dependencies"),
|
|
@@ -501,7 +564,7 @@ const saveGateResultParams = {
|
|
|
501
564
|
projectDir: projectDirParam,
|
|
502
565
|
milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
|
|
503
566
|
sliceId: z.string().describe("Slice ID (e.g. S01)"),
|
|
504
|
-
gateId: z.
|
|
567
|
+
gateId: z.string().describe("Gate ID (e.g. Q3, Q4, Q5, Q6, Q7, Q8, MV01, MV02, MV03, MV04). Accepts any string for forward-compatibility with new gates."),
|
|
505
568
|
taskId: z.string().optional().describe("Task ID for task-scoped gates"),
|
|
506
569
|
verdict: z.enum(["pass", "flag", "omitted"]).describe("Gate verdict"),
|
|
507
570
|
rationale: z.string().describe("One-sentence justification"),
|
|
@@ -654,6 +717,18 @@ const taskCompleteParams = {
|
|
|
654
717
|
keyFiles: z.array(z.string()).optional().describe("List of key files created or modified"),
|
|
655
718
|
keyDecisions: z.array(z.string()).optional().describe("List of key decisions made during this task"),
|
|
656
719
|
blockerDiscovered: z.boolean().optional().describe("Whether a plan-invalidating blocker was discovered"),
|
|
720
|
+
// ADR-011 Phase 2: mid-execution escalation — agent asks the user to resolve an ambiguity.
|
|
721
|
+
escalation: z.object({
|
|
722
|
+
question: z.string().describe("The question the user needs to answer — one clear sentence."),
|
|
723
|
+
options: z.array(z.object({
|
|
724
|
+
id: z.string().describe("Short id (e.g. 'A', 'B') used by /gsd escalate resolve."),
|
|
725
|
+
label: z.string().describe("One-line label."),
|
|
726
|
+
tradeoffs: z.string().describe("1-2 sentences on the tradeoffs of this option."),
|
|
727
|
+
})).min(2).max(4).describe("2-4 options the user can choose between."),
|
|
728
|
+
recommendation: z.string().describe("Option id the executor recommends."),
|
|
729
|
+
recommendationRationale: z.string().describe("Why the recommendation — 1-2 sentences."),
|
|
730
|
+
continueWithDefault: z.boolean().describe("When true, loop continues (artifact logged for later review). When false, auto-mode pauses until the user resolves via /gsd escalate resolve."),
|
|
731
|
+
}).optional().describe("ADR-011 Phase 2: optional escalation payload. Only honored when phases.mid_execution_escalation is true."),
|
|
657
732
|
verificationEvidence: z.array(z.union([
|
|
658
733
|
z.object({
|
|
659
734
|
command: z.string(),
|
|
@@ -753,7 +828,18 @@ export function registerWorkflowTools(server) {
|
|
|
753
828
|
return reserved;
|
|
754
829
|
}
|
|
755
830
|
const allIds = [...new Set([...findMilestoneIds(projectDir), ...getReservedMilestoneIds()])];
|
|
756
|
-
const
|
|
831
|
+
const prefsMod = await importLocalModule("../../../src/resources/extensions/gsd/preferences.js").catch(() => null);
|
|
832
|
+
// Graceful degradation: a corrupt preferences file should not crash
|
|
833
|
+
// milestone-id generation. Fall back to non-unique IDs if anything
|
|
834
|
+
// throws here — matches the pre-fix behavior for missing prefs.
|
|
835
|
+
let uniqueEnabled = false;
|
|
836
|
+
try {
|
|
837
|
+
uniqueEnabled = !!prefsMod?.loadEffectiveGSDPreferences?.()?.preferences?.unique_milestone_ids;
|
|
838
|
+
}
|
|
839
|
+
catch {
|
|
840
|
+
uniqueEnabled = false;
|
|
841
|
+
}
|
|
842
|
+
const nextId = nextMilestoneId(allIds, uniqueEnabled);
|
|
757
843
|
await ensureMilestoneDbRow(nextId);
|
|
758
844
|
return nextId;
|
|
759
845
|
});
|
|
@@ -770,7 +856,18 @@ export function registerWorkflowTools(server) {
|
|
|
770
856
|
return reserved;
|
|
771
857
|
}
|
|
772
858
|
const allIds = [...new Set([...findMilestoneIds(projectDir), ...getReservedMilestoneIds()])];
|
|
773
|
-
const
|
|
859
|
+
const prefsMod = await importLocalModule("../../../src/resources/extensions/gsd/preferences.js").catch(() => null);
|
|
860
|
+
// Graceful degradation: a corrupt preferences file should not crash
|
|
861
|
+
// milestone-id generation. Fall back to non-unique IDs if anything
|
|
862
|
+
// throws here — matches the pre-fix behavior for missing prefs.
|
|
863
|
+
let uniqueEnabled = false;
|
|
864
|
+
try {
|
|
865
|
+
uniqueEnabled = !!prefsMod?.loadEffectiveGSDPreferences?.()?.preferences?.unique_milestone_ids;
|
|
866
|
+
}
|
|
867
|
+
catch {
|
|
868
|
+
uniqueEnabled = false;
|
|
869
|
+
}
|
|
870
|
+
const nextId = nextMilestoneId(allIds, uniqueEnabled);
|
|
774
871
|
await ensureMilestoneDbRow(nextId);
|
|
775
872
|
return nextId;
|
|
776
873
|
});
|
|
@@ -910,8 +1007,10 @@ export function registerWorkflowTools(server) {
|
|
|
910
1007
|
return handleTaskComplete(projectDir, taskArgs);
|
|
911
1008
|
});
|
|
912
1009
|
server.tool("gsd_milestone_status", "Read the current status of a milestone and all its slices from the GSD database.", milestoneStatusParams, async (args) => {
|
|
1010
|
+
// gsd_milestone_status is a read-only query. In-process (query-tools.ts)
|
|
1011
|
+
// does not apply the write-gate; MCP must match to avoid blocking reads
|
|
1012
|
+
// during pending-gate or queue-mode states.
|
|
913
1013
|
const { projectDir, milestoneId } = parseWorkflowArgs(milestoneStatusSchema, args);
|
|
914
|
-
await enforceWorkflowWriteGate("gsd_milestone_status", projectDir, milestoneId);
|
|
915
1014
|
const { executeMilestoneStatus } = await getWorkflowToolExecutors();
|
|
916
1015
|
return runSerializedWorkflowOperation(() => executeMilestoneStatus({ milestoneId }, projectDir));
|
|
917
1016
|
});
|