gsd-pi 2.76.0-dev.b072ebb73 → 2.76.0-dev.fe143342a
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/mcp-server.d.ts +7 -0
- package/dist/mcp-server.js +35 -1
- package/dist/resource-loader.d.ts +1 -1
- package/dist/resource-loader.js +2 -8
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +66 -4
- package/dist/resources/extensions/gsd/auto/phases.js +4 -1
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto-model-selection.js +39 -13
- package/dist/resources/extensions/gsd/auto-start.js +39 -21
- package/dist/resources/extensions/gsd/auto.js +15 -12
- package/dist/resources/extensions/gsd/blocked-models.js +68 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +76 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +35 -0
- package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
- package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
- package/dist/resources/extensions/gsd/error-classifier.js +31 -3
- package/dist/resources/extensions/gsd/exec-history.js +120 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
- package/dist/resources/extensions/gsd/gsd-db.js +62 -4
- package/dist/resources/extensions/gsd/init-wizard.js +15 -1
- package/dist/resources/extensions/gsd/key-manager.js +6 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +13 -3
- package/dist/resources/extensions/gsd/preferences-types.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
- package/dist/resources/extensions/gsd/preferences.js +17 -17
- package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +1 -1
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
- package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
- package/dist/resources/extensions/search-the-web/command-search-provider.js +5 -4
- package/dist/resources/extensions/search-the-web/native-search.js +45 -13
- 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 +8 -8
- 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/required-server-files.json +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.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 +8 -8
- 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/dist/web/standalone/server.js +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 +64 -25
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
- package/packages/mcp-server/src/workflow-tools.ts +84 -43
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
- package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
- package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
- package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +76 -10
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +9 -5
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +25 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +86 -10
- package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
- package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +36 -1
- package/packages/pi-coding-agent/src/core/session-manager.ts +9 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/scripts/link-workspace-packages.cjs +1 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +67 -4
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +137 -2
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/phases.ts +4 -0
- package/src/resources/extensions/gsd/auto/session.ts +7 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +50 -12
- package/src/resources/extensions/gsd/auto-start.ts +40 -22
- package/src/resources/extensions/gsd/auto.ts +15 -12
- package/src/resources/extensions/gsd/blocked-models.ts +98 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +97 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -0
- package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
- package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
- package/src/resources/extensions/gsd/error-classifier.ts +36 -3
- package/src/resources/extensions/gsd/exec-history.ts +153 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
- package/src/resources/extensions/gsd/gsd-db.ts +68 -4
- package/src/resources/extensions/gsd/init-wizard.ts +15 -1
- package/src/resources/extensions/gsd/key-manager.ts +6 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +13 -3
- package/src/resources/extensions/gsd/preferences-types.ts +38 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
- package/src/resources/extensions/gsd/preferences.ts +17 -17
- package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/blocked-models.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +124 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
- package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- package/src/resources/extensions/search-the-web/command-search-provider.ts +5 -4
- package/src/resources/extensions/search-the-web/native-search.ts +48 -12
- /package/dist/web/standalone/.next/static/{pBwmOoye64ZrRp-_rf0v1 → n21VtX2hZlkpdEUO_nU4z}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{pBwmOoye64ZrRp-_rf0v1 → n21VtX2hZlkpdEUO_nU4z}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdirSync, rmSync, writeFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { mkdirSync, rmSync, writeFileSync, readFileSync, existsSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
@@ -288,6 +288,136 @@ describe("workflow MCP tools", () => {
|
|
|
288
288
|
}
|
|
289
289
|
});
|
|
290
290
|
|
|
291
|
+
it("#4477 gsd_task_complete forwards every schema field to the executor (regression for destructure-rebuild bug class)", async () => {
|
|
292
|
+
// Locks in the class-fix from PR #4477 review: handleTaskComplete previously
|
|
293
|
+
// destructured args into a hand-listed set of fields and rebuilt the call
|
|
294
|
+
// payload, which silently dropped ADR-011's `escalation` field (and any
|
|
295
|
+
// future schema field added without updating the rebuild). The fix passes
|
|
296
|
+
// `args` through directly, matching the spread pattern of sibling
|
|
297
|
+
// handlers. This test verifies the contract by injecting a mock executor
|
|
298
|
+
// module that captures the args, calling gsd_task_complete with an
|
|
299
|
+
// `escalation` payload, and asserting the field reached the executor.
|
|
300
|
+
const base = makeTmpBase();
|
|
301
|
+
const capturePath = join(base, "captured-args.json");
|
|
302
|
+
const mockModulePath = join(base, "mock-executors.mjs");
|
|
303
|
+
const prevModule = process.env.GSD_WORKFLOW_EXECUTORS_MODULE;
|
|
304
|
+
const prevCapture = process.env.GSD_TEST_TASK_COMPLETE_CAPTURE_PATH;
|
|
305
|
+
try {
|
|
306
|
+
// Mock module: implements the WorkflowToolExecutors shape.
|
|
307
|
+
// executeTaskComplete writes its received args to disk for assertion.
|
|
308
|
+
// Other executors are no-op stubs to satisfy isWorkflowToolExecutors.
|
|
309
|
+
const mockSource = `
|
|
310
|
+
import { writeFileSync } from "node:fs";
|
|
311
|
+
|
|
312
|
+
const noop = async () => ({ content: [{ type: "text", text: "noop" }] });
|
|
313
|
+
|
|
314
|
+
export const SUPPORTED_SUMMARY_ARTIFACT_TYPES = ["SUMMARY", "UAT", "CONTEXT", "PLAN"];
|
|
315
|
+
export const executeMilestoneStatus = noop;
|
|
316
|
+
export const executePlanMilestone = noop;
|
|
317
|
+
export const executePlanSlice = noop;
|
|
318
|
+
export const executeReplanSlice = noop;
|
|
319
|
+
export const executeSliceComplete = noop;
|
|
320
|
+
export const executeCompleteMilestone = noop;
|
|
321
|
+
export const executeValidateMilestone = noop;
|
|
322
|
+
export const executeReassessRoadmap = noop;
|
|
323
|
+
export const executeSaveGateResult = noop;
|
|
324
|
+
export const executeSummarySave = noop;
|
|
325
|
+
|
|
326
|
+
export const executeTaskComplete = async (params, projectDir) => {
|
|
327
|
+
const capturePath = process.env.GSD_TEST_TASK_COMPLETE_CAPTURE_PATH;
|
|
328
|
+
if (capturePath) {
|
|
329
|
+
writeFileSync(capturePath, JSON.stringify({ params, projectDir }, null, 2));
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
content: [{ type: "text", text: "mock task complete" }],
|
|
333
|
+
details: { taskId: params.taskId },
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
`;
|
|
337
|
+
writeFileSync(mockModulePath, mockSource, "utf-8");
|
|
338
|
+
process.env.GSD_WORKFLOW_EXECUTORS_MODULE = mockModulePath;
|
|
339
|
+
process.env.GSD_TEST_TASK_COMPLETE_CAPTURE_PATH = capturePath;
|
|
340
|
+
|
|
341
|
+
// Fresh import bypasses the cached workflowToolExecutorsPromise so the
|
|
342
|
+
// mock module is actually loaded for this test.
|
|
343
|
+
const { registerWorkflowTools: freshRegisterWorkflowTools } = await import(
|
|
344
|
+
`./workflow-tools.ts?escalation-test=${randomUUID()}`
|
|
345
|
+
);
|
|
346
|
+
const server = makeMockServer();
|
|
347
|
+
freshRegisterWorkflowTools(server as any);
|
|
348
|
+
const taskTool = server.tools.find((t) => t.name === "gsd_task_complete");
|
|
349
|
+
assert.ok(taskTool, "task tool should be registered");
|
|
350
|
+
|
|
351
|
+
// Mirrors the ADR-011 escalation schema: question + 2-4 options
|
|
352
|
+
// (each with id/label/tradeoffs) + recommendation + rationale +
|
|
353
|
+
// continueWithDefault flag.
|
|
354
|
+
const escalationPayload = {
|
|
355
|
+
question: "Should the auth flow use OAuth or PAT?",
|
|
356
|
+
options: [
|
|
357
|
+
{ id: "A", label: "OAuth", tradeoffs: "Best UX; requires more setup." },
|
|
358
|
+
{ id: "B", label: "PAT", tradeoffs: "Simpler; weaker rotation story." },
|
|
359
|
+
],
|
|
360
|
+
recommendation: "A",
|
|
361
|
+
recommendationRationale: "Initial requirement implied multi-user; OAuth fits better.",
|
|
362
|
+
continueWithDefault: true,
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
await taskTool!.handler({
|
|
366
|
+
projectDir: base,
|
|
367
|
+
taskId: "T01",
|
|
368
|
+
sliceId: "S01",
|
|
369
|
+
milestoneId: "M001",
|
|
370
|
+
oneLiner: "Completed task with escalation",
|
|
371
|
+
narrative: "Did the work but flagged an ambiguity",
|
|
372
|
+
verification: "npm test",
|
|
373
|
+
escalation: escalationPayload,
|
|
374
|
+
verificationEvidence: [
|
|
375
|
+
{ command: "npm test", exitCode: 0, verdict: "pass", durationMs: 1234 },
|
|
376
|
+
],
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
assert.ok(existsSync(capturePath), "mock executor should have written captured args to disk");
|
|
380
|
+
const captured = JSON.parse(readFileSync(capturePath, "utf-8"));
|
|
381
|
+
|
|
382
|
+
// The handler resolves projectDir via realpathSync (security/symlink check),
|
|
383
|
+
// so on macOS where /var symlinks to /private/var, the captured path will
|
|
384
|
+
// be the realpath form. Normalize both sides.
|
|
385
|
+
assert.equal(captured.projectDir, realpathSync(base), "projectDir should be passed as second arg");
|
|
386
|
+
assert.deepEqual(
|
|
387
|
+
captured.params.escalation,
|
|
388
|
+
escalationPayload,
|
|
389
|
+
"escalation payload must reach the executor verbatim — regression guard for the destructure-rebuild bug class (#4477 review)",
|
|
390
|
+
);
|
|
391
|
+
// Spot-check a couple of other fields to ensure the spread pattern
|
|
392
|
+
// doesn't accidentally exclude the rest while including escalation.
|
|
393
|
+
assert.equal(captured.params.taskId, "T01", "taskId must be forwarded");
|
|
394
|
+
assert.equal(captured.params.milestoneId, "M001", "milestoneId must be forwarded");
|
|
395
|
+
assert.deepEqual(
|
|
396
|
+
captured.params.verificationEvidence,
|
|
397
|
+
[{ command: "npm test", exitCode: 0, verdict: "pass", durationMs: 1234 }],
|
|
398
|
+
"verificationEvidence must be forwarded (existing field)",
|
|
399
|
+
);
|
|
400
|
+
// Ensure no projectDir leak into params (it should be the second arg only).
|
|
401
|
+
assert.equal(
|
|
402
|
+
captured.params.projectDir,
|
|
403
|
+
undefined,
|
|
404
|
+
"projectDir must NOT appear in params — it's stripped via the spread destructure",
|
|
405
|
+
);
|
|
406
|
+
} finally {
|
|
407
|
+
if (prevModule === undefined) {
|
|
408
|
+
delete process.env.GSD_WORKFLOW_EXECUTORS_MODULE;
|
|
409
|
+
} else {
|
|
410
|
+
process.env.GSD_WORKFLOW_EXECUTORS_MODULE = prevModule;
|
|
411
|
+
}
|
|
412
|
+
if (prevCapture === undefined) {
|
|
413
|
+
delete process.env.GSD_TEST_TASK_COMPLETE_CAPTURE_PATH;
|
|
414
|
+
} else {
|
|
415
|
+
process.env.GSD_TEST_TASK_COMPLETE_CAPTURE_PATH = prevCapture;
|
|
416
|
+
}
|
|
417
|
+
cleanup(base);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
291
421
|
it("gsd_complete_task alias delegates to gsd_task_complete behavior", async () => {
|
|
292
422
|
const base = makeTmpBase();
|
|
293
423
|
try {
|
|
@@ -1237,6 +1367,21 @@ describe("workflow MCP tools", () => {
|
|
|
1237
1367
|
findings: "No new attack surface was introduced.",
|
|
1238
1368
|
});
|
|
1239
1369
|
assert.match((gateResult as any).content[0].text as string, /Gate Q3 result saved/);
|
|
1370
|
+
// #4472: executor `details` must be adapted to MCP `structuredContent`
|
|
1371
|
+
// so it survives the protocol transport intact. Asserting property
|
|
1372
|
+
// *absence* rather than `=== undefined` so a future regression that
|
|
1373
|
+
// explicitly sets `details: undefined` (rather than removing it) still
|
|
1374
|
+
// fails this contract test.
|
|
1375
|
+
assert.equal(
|
|
1376
|
+
Object.prototype.hasOwnProperty.call(gateResult, "details"),
|
|
1377
|
+
false,
|
|
1378
|
+
"executor `details` field must be stripped from MCP tool result",
|
|
1379
|
+
);
|
|
1380
|
+
assert.deepEqual(
|
|
1381
|
+
(gateResult as any).structuredContent,
|
|
1382
|
+
{ operation: "save_gate_result", gateId: "Q3", verdict: "pass" },
|
|
1383
|
+
"executor details must be forwarded on the MCP `structuredContent` channel",
|
|
1384
|
+
);
|
|
1240
1385
|
const gateRows = _getAdapter()!.prepare(
|
|
1241
1386
|
"SELECT status, verdict, rationale FROM quality_gates WHERE milestone_id = ? AND slice_id = ? AND gate_id = ?",
|
|
1242
1387
|
).all("M006", "S06", "Q3") as Array<Record<string, unknown>>;
|
|
@@ -617,6 +617,51 @@ function getWorkflowOpTimeoutMs(env: NodeJS.ProcessEnv = process.env): number {
|
|
|
617
617
|
return parsed; // 0 disables the timeout
|
|
618
618
|
}
|
|
619
619
|
|
|
620
|
+
/**
|
|
621
|
+
* Adapt an executor `ToolExecutionResult` ({ content, details?, isError? }) to
|
|
622
|
+
* the MCP `CallToolResult` shape ({ content, structuredContent?, isError? }).
|
|
623
|
+
*
|
|
624
|
+
* MCP transports (including stdio) only serialize fields declared in the
|
|
625
|
+
* protocol, so a non-standard `details` field is silently dropped over the
|
|
626
|
+
* wire. Mirroring it into `structuredContent` — the protocol's supported
|
|
627
|
+
* channel for structured tool payloads — preserves the data for clients that
|
|
628
|
+
* render from it (e.g. the save_gate_result renderer that reads gateId /
|
|
629
|
+
* verdict). See #4472.
|
|
630
|
+
*
|
|
631
|
+
* Discard policy for non-plain-object `details`: the `isPlainObject` guard
|
|
632
|
+
* accepts the canonical case (a record literal) and intentionally drops bare
|
|
633
|
+
* primitives (string, number, boolean), bare arrays, and class instances /
|
|
634
|
+
* Date objects. This is deliberate — MCP `structuredContent` is specified as
|
|
635
|
+
* a JSON object; non-object payloads can't round-trip cleanly. No current
|
|
636
|
+
* executor returns a non-object `details`, so this never fires in practice.
|
|
637
|
+
* Future executors needing to return a primitive should wrap it
|
|
638
|
+
* (`details: { value: 42 }`) rather than relying on the discard.
|
|
639
|
+
*/
|
|
640
|
+
function adaptExecutorResult(result: unknown): unknown {
|
|
641
|
+
if (!result || typeof result !== "object") return result;
|
|
642
|
+
const r = result as Record<string, unknown>;
|
|
643
|
+
if (!("details" in r)) return result;
|
|
644
|
+
const { details, ...rest } = r;
|
|
645
|
+
return isPlainObject(details) ? { ...rest, structuredContent: details } : rest;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Strict plain-object guard. True only for object literals and
|
|
650
|
+
* `Object.create(null)` — not for `Date`, `URL`, `Map`, `Set`, class instances,
|
|
651
|
+
* or arrays. Used to gate `structuredContent` forwarding so the MCP transport
|
|
652
|
+
* receives only true JSON objects (the protocol contract).
|
|
653
|
+
*
|
|
654
|
+
* Mirrored in `src/mcp-server.ts` for the agent-tool registry path's
|
|
655
|
+
* structured-content gate. Keep both copies in sync if the contract definition
|
|
656
|
+
* needs to evolve. See #4477 review.
|
|
657
|
+
*/
|
|
658
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
659
|
+
if (value === null || typeof value !== "object") return false;
|
|
660
|
+
if (Array.isArray(value)) return false;
|
|
661
|
+
const proto = Object.getPrototypeOf(value);
|
|
662
|
+
return proto === null || proto === Object.prototype;
|
|
663
|
+
}
|
|
664
|
+
|
|
620
665
|
async function runSerializedWorkflowOperation<T>(fn: () => Promise<T>): Promise<T> {
|
|
621
666
|
// The shared DB adapter and workflow log base path are process-global, so
|
|
622
667
|
// workflow MCP mutations must not overlap within a single server process.
|
|
@@ -709,39 +754,15 @@ async function handleTaskComplete(
|
|
|
709
754
|
args: Omit<z.infer<typeof taskCompleteSchema>, "projectDir">,
|
|
710
755
|
): Promise<unknown> {
|
|
711
756
|
await enforceWorkflowWriteGate("gsd_task_complete", projectDir, args.milestoneId);
|
|
712
|
-
const {
|
|
713
|
-
taskId,
|
|
714
|
-
sliceId,
|
|
715
|
-
milestoneId,
|
|
716
|
-
oneLiner,
|
|
717
|
-
narrative,
|
|
718
|
-
verification,
|
|
719
|
-
deviations,
|
|
720
|
-
knownIssues,
|
|
721
|
-
keyFiles,
|
|
722
|
-
keyDecisions,
|
|
723
|
-
blockerDiscovered,
|
|
724
|
-
verificationEvidence,
|
|
725
|
-
} = args;
|
|
726
757
|
const { executeTaskComplete } = await getWorkflowToolExecutors();
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
verification,
|
|
736
|
-
deviations,
|
|
737
|
-
knownIssues,
|
|
738
|
-
keyFiles,
|
|
739
|
-
keyDecisions,
|
|
740
|
-
blockerDiscovered,
|
|
741
|
-
verificationEvidence,
|
|
742
|
-
},
|
|
743
|
-
projectDir,
|
|
744
|
-
),
|
|
758
|
+
// Pass `args` through directly rather than destructure-then-rebuild. The
|
|
759
|
+
// previous implementation re-listed each field, which silently dropped
|
|
760
|
+
// schema fields that weren't in the rebuild list (e.g., ADR-011's
|
|
761
|
+
// `escalation` payload). The destructure-then-rebuild pattern is the bug
|
|
762
|
+
// class; matching the spread shape used by sibling handlers (handleSliceComplete,
|
|
763
|
+
// handleReplanSlice) eliminates the recurrence risk by construction.
|
|
764
|
+
return adaptExecutorResult(
|
|
765
|
+
await runSerializedWorkflowOperation(() => executeTaskComplete(args, projectDir)),
|
|
745
766
|
);
|
|
746
767
|
}
|
|
747
768
|
|
|
@@ -752,7 +773,9 @@ async function handleSliceComplete(
|
|
|
752
773
|
await enforceWorkflowWriteGate("gsd_slice_complete", projectDir, args.milestoneId);
|
|
753
774
|
const { executeSliceComplete } = await getWorkflowToolExecutors();
|
|
754
775
|
const { projectDir: _projectDir, ...params } = args;
|
|
755
|
-
return
|
|
776
|
+
return adaptExecutorResult(
|
|
777
|
+
await runSerializedWorkflowOperation(() => executeSliceComplete(params, projectDir)),
|
|
778
|
+
);
|
|
756
779
|
}
|
|
757
780
|
|
|
758
781
|
async function handleReplanSlice(
|
|
@@ -762,7 +785,9 @@ async function handleReplanSlice(
|
|
|
762
785
|
await enforceWorkflowWriteGate("gsd_replan_slice", projectDir, args.milestoneId);
|
|
763
786
|
const { executeReplanSlice } = await getWorkflowToolExecutors();
|
|
764
787
|
const { projectDir: _projectDir, ...params } = args;
|
|
765
|
-
return
|
|
788
|
+
return adaptExecutorResult(
|
|
789
|
+
await runSerializedWorkflowOperation(() => executeReplanSlice(params, projectDir)),
|
|
790
|
+
);
|
|
766
791
|
}
|
|
767
792
|
|
|
768
793
|
async function handleCompleteMilestone(
|
|
@@ -772,7 +797,9 @@ async function handleCompleteMilestone(
|
|
|
772
797
|
await enforceWorkflowWriteGate("gsd_complete_milestone", projectDir, args.milestoneId);
|
|
773
798
|
const { executeCompleteMilestone } = await getWorkflowToolExecutors();
|
|
774
799
|
const { projectDir: _projectDir, ...params } = args;
|
|
775
|
-
return
|
|
800
|
+
return adaptExecutorResult(
|
|
801
|
+
await runSerializedWorkflowOperation(() => executeCompleteMilestone(params, projectDir)),
|
|
802
|
+
);
|
|
776
803
|
}
|
|
777
804
|
|
|
778
805
|
async function handleValidateMilestone(
|
|
@@ -782,7 +809,9 @@ async function handleValidateMilestone(
|
|
|
782
809
|
await enforceWorkflowWriteGate("gsd_validate_milestone", projectDir, args.milestoneId);
|
|
783
810
|
const { executeValidateMilestone } = await getWorkflowToolExecutors();
|
|
784
811
|
const { projectDir: _projectDir, ...params } = args;
|
|
785
|
-
return
|
|
812
|
+
return adaptExecutorResult(
|
|
813
|
+
await runSerializedWorkflowOperation(() => executeValidateMilestone(params, projectDir)),
|
|
814
|
+
);
|
|
786
815
|
}
|
|
787
816
|
|
|
788
817
|
async function handleReassessRoadmap(
|
|
@@ -792,7 +821,9 @@ async function handleReassessRoadmap(
|
|
|
792
821
|
await enforceWorkflowWriteGate("gsd_reassess_roadmap", projectDir, args.milestoneId);
|
|
793
822
|
const { executeReassessRoadmap } = await getWorkflowToolExecutors();
|
|
794
823
|
const { projectDir: _projectDir, ...params } = args;
|
|
795
|
-
return
|
|
824
|
+
return adaptExecutorResult(
|
|
825
|
+
await runSerializedWorkflowOperation(() => executeReassessRoadmap(params, projectDir)),
|
|
826
|
+
);
|
|
796
827
|
}
|
|
797
828
|
|
|
798
829
|
async function handleSaveGateResult(
|
|
@@ -802,7 +833,9 @@ async function handleSaveGateResult(
|
|
|
802
833
|
await enforceWorkflowWriteGate("gsd_save_gate_result", projectDir, args.milestoneId);
|
|
803
834
|
const { executeSaveGateResult } = await getWorkflowToolExecutors();
|
|
804
835
|
const { projectDir: _projectDir, ...params } = args;
|
|
805
|
-
return
|
|
836
|
+
return adaptExecutorResult(
|
|
837
|
+
await runSerializedWorkflowOperation(() => executeSaveGateResult(params, projectDir)),
|
|
838
|
+
);
|
|
806
839
|
}
|
|
807
840
|
|
|
808
841
|
async function ensureMilestoneDbRow(milestoneId: string): Promise<void> {
|
|
@@ -1384,7 +1417,9 @@ export function registerWorkflowTools(server: McpToolServer): void {
|
|
|
1384
1417
|
const { projectDir, ...params } = parsed;
|
|
1385
1418
|
await enforceWorkflowWriteGate("gsd_plan_milestone", projectDir, params.milestoneId);
|
|
1386
1419
|
const { executePlanMilestone } = await getWorkflowToolExecutors();
|
|
1387
|
-
return
|
|
1420
|
+
return adaptExecutorResult(
|
|
1421
|
+
await runSerializedWorkflowOperation(() => executePlanMilestone(params, projectDir)),
|
|
1422
|
+
);
|
|
1388
1423
|
},
|
|
1389
1424
|
);
|
|
1390
1425
|
|
|
@@ -1397,7 +1432,9 @@ export function registerWorkflowTools(server: McpToolServer): void {
|
|
|
1397
1432
|
const { projectDir, ...params } = parsed;
|
|
1398
1433
|
await enforceWorkflowWriteGate("gsd_plan_slice", projectDir, params.milestoneId);
|
|
1399
1434
|
const { executePlanSlice } = await getWorkflowToolExecutors();
|
|
1400
|
-
return
|
|
1435
|
+
return adaptExecutorResult(
|
|
1436
|
+
await runSerializedWorkflowOperation(() => executePlanSlice(params, projectDir)),
|
|
1437
|
+
);
|
|
1401
1438
|
},
|
|
1402
1439
|
);
|
|
1403
1440
|
|
|
@@ -1598,8 +1635,10 @@ export function registerWorkflowTools(server: McpToolServer): void {
|
|
|
1598
1635
|
`artifact_type must be one of: ${supportedArtifactTypes.join(", ")}`,
|
|
1599
1636
|
);
|
|
1600
1637
|
}
|
|
1601
|
-
return
|
|
1602
|
-
|
|
1638
|
+
return adaptExecutorResult(
|
|
1639
|
+
await runSerializedWorkflowOperation(() =>
|
|
1640
|
+
executors.executeSummarySave({ milestone_id, slice_id, task_id, artifact_type, content }, projectDir),
|
|
1641
|
+
),
|
|
1603
1642
|
);
|
|
1604
1643
|
},
|
|
1605
1644
|
);
|
|
@@ -1636,7 +1675,9 @@ export function registerWorkflowTools(server: McpToolServer): void {
|
|
|
1636
1675
|
// during pending-gate or queue-mode states.
|
|
1637
1676
|
const { projectDir, milestoneId } = parseWorkflowArgs(milestoneStatusSchema, args);
|
|
1638
1677
|
const { executeMilestoneStatus } = await getWorkflowToolExecutors();
|
|
1639
|
-
return
|
|
1678
|
+
return adaptExecutorResult(
|
|
1679
|
+
await runSerializedWorkflowOperation(() => executeMilestoneStatus({ milestoneId }, projectDir)),
|
|
1680
|
+
);
|
|
1640
1681
|
},
|
|
1641
1682
|
);
|
|
1642
1683
|
|