@opengsd/gsd-pi 1.9.0-dev.e10b60d4 → 1.10.0-dev.a5e42e99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -9
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/loop.js +24 -4
- package/dist/resources/extensions/gsd/auto/run-unit.js +19 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +1 -0
- package/dist/resources/extensions/gsd/auto-start.js +30 -10
- package/dist/resources/extensions/gsd/auto-verification.js +25 -12
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +7 -3
- package/dist/resources/extensions/gsd/auto.js +12 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +4 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +12 -3
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +37 -6
- package/dist/resources/extensions/gsd/flat-phase-migration.js +12 -0
- package/dist/resources/extensions/gsd/gsd-db.js +75 -31
- package/dist/resources/extensions/gsd/model-cost-table.js +6 -3
- package/dist/resources/extensions/gsd/model-router.js +10 -5
- package/dist/resources/extensions/gsd/planning-subagent-registry.js +12 -4
- package/dist/resources/extensions/gsd/preferences-models.js +50 -10
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +41 -6
- package/dist/resources/extensions/gsd/preferences.js +7 -0
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +3 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -1
- package/dist/resources/extensions/gsd/verification-evidence.js +5 -3
- package/dist/resources/extensions/gsd/worktree-state-projection.js +20 -18
- package/dist/resources/extensions/ollama/ollama-chat-provider.js +2 -2
- package/dist/resources/extensions/subagent/index.js +1 -1
- package/dist/startup-model-validation.d.ts +1 -1
- 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 +6 -6
- 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 +6 -6
- package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +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/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +7 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/rpc.d.ts +1 -1
- package/packages/contracts/dist/rpc.d.ts.map +1 -1
- package/packages/contracts/dist/rpc.js +1 -1
- package/packages/contracts/dist/rpc.js.map +1 -1
- package/packages/contracts/dist/rpc.test.js +1 -1
- package/packages/contracts/dist/rpc.test.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/cli/args.js +2 -2
- package/packages/gsd-agent-modes/dist/cli/args.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/README.md +21 -5
- package/packages/gsd-cloud/dist/background-cli.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/background-cli.test.js +325 -0
- package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cli.js +146 -16
- package/packages/gsd-cloud/dist/cli.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.js +7 -1
- package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts +7 -4
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.js +115 -31
- package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.test.js +224 -59
- package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -1
- package/packages/gsd-cloud/dist/runtime-artifacts.d.ts +3 -0
- package/packages/gsd-cloud/dist/runtime-artifacts.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-artifacts.js +36 -0
- package/packages/gsd-cloud/dist/runtime-artifacts.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.d.ts +32 -0
- package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.js +640 -0
- package/packages/gsd-cloud/dist/runtime-process.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js +543 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.d.ts +117 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.js +291 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.test.js +434 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.test.js.map +1 -0
- package/packages/gsd-cloud/package.json +2 -2
- package/packages/mcp-server/dist/cli-runner.d.ts +3 -0
- package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
- package/packages/mcp-server/dist/cli-runner.js +119 -33
- package/packages/mcp-server/dist/cli-runner.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +2 -2
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +130 -2
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +101 -9
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.js +18 -6
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +3 -2
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/packages/pi-ai/dist/providers/google.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +9 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -3
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +54 -0
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +1 -0
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme.d.ts +1 -1
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +1 -0
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/loop.ts +48 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +20 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +1 -0
- package/src/resources/extensions/gsd/auto-start.ts +32 -9
- package/src/resources/extensions/gsd/auto-verification.ts +46 -12
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +10 -3
- package/src/resources/extensions/gsd/auto.ts +13 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +4 -3
- package/src/resources/extensions/gsd/docs/preferences-reference.md +12 -3
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +42 -6
- package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -0
- package/src/resources/extensions/gsd/gsd-db.ts +85 -33
- package/src/resources/extensions/gsd/model-cost-table.ts +14 -3
- package/src/resources/extensions/gsd/model-router.ts +10 -5
- package/src/resources/extensions/gsd/planning-subagent-registry.ts +18 -4
- package/src/resources/extensions/gsd/preferences-models.ts +54 -10
- package/src/resources/extensions/gsd/preferences-types.ts +10 -2
- package/src/resources/extensions/gsd/preferences-validation.ts +49 -6
- package/src/resources/extensions/gsd/preferences.ts +13 -0
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +3 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +74 -1
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +22 -7
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/preferences.test.ts +71 -4
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/workflow-authority-faults.test.ts +238 -0
- package/src/resources/extensions/gsd/tests/workflow-authority-fixture.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/workflow-authority-fixture.ts +141 -0
- package/src/resources/extensions/gsd/tests/workflow-authority-projection-conflict.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/workflow-fault-harness.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/workflow-fault-harness.ts +72 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +40 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +5 -1
- package/src/resources/extensions/gsd/verification-evidence.ts +5 -2
- package/src/resources/extensions/gsd/worktree-state-projection.ts +21 -24
- package/src/resources/extensions/ollama/ollama-chat-provider.ts +2 -2
- package/src/resources/extensions/ollama/tests/ollama-chat-provider-think.test.ts +4 -0
- package/src/resources/extensions/subagent/index.ts +1 -1
- /package/dist/web/standalone/.next/static/{V9MAVyYVPboQUB9KTTAMi → xwUaOqui04jIIFVsIBLiL}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{V9MAVyYVPboQUB9KTTAMi → xwUaOqui04jIIFVsIBLiL}/_ssgManifest.js +0 -0
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import type { ExtensionContext, ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
17
17
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
18
|
-
import { gsdProjectionRoot, relMilestoneFile, resolveSliceFile, resolveSlicePath } from "./paths.js";
|
|
18
|
+
import { gsdProjectionRoot, legacyMilestonesDir, relMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "./paths.js";
|
|
19
19
|
import { resolveMilestoneValidationVerdict } from "./milestone-validation-verdict.js";
|
|
20
20
|
import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
|
|
21
21
|
import { parseUnitId } from "./unit-id.js";
|
|
@@ -62,6 +62,30 @@ export interface VerificationContext {
|
|
|
62
62
|
export type VerificationResult = "continue" | "retry" | "pause";
|
|
63
63
|
type PauseAutoFn = (ctx?: ExtensionContext, pi?: ExtensionAPI, errorContext?: ErrorContext) => Promise<void>;
|
|
64
64
|
|
|
65
|
+
interface VerificationEvidenceLocation {
|
|
66
|
+
dir: string;
|
|
67
|
+
fileSliceId?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resolveVerificationEvidenceLocation(
|
|
71
|
+
basePath: string,
|
|
72
|
+
milestoneId: string,
|
|
73
|
+
sliceId: string,
|
|
74
|
+
): VerificationEvidenceLocation | null {
|
|
75
|
+
const mDir = resolveMilestonePath(basePath, milestoneId);
|
|
76
|
+
if (!mDir) return null;
|
|
77
|
+
|
|
78
|
+
const legacyBase = legacyMilestonesDir(basePath);
|
|
79
|
+
const isLegacy = mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\");
|
|
80
|
+
if (!isLegacy) {
|
|
81
|
+
return { dir: mDir, fileSliceId: sliceId };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
|
|
85
|
+
if (!sDir) return null;
|
|
86
|
+
return { dir: join(sDir, "tasks") };
|
|
87
|
+
}
|
|
88
|
+
|
|
65
89
|
function getCurrentUnitCostStats(unitId: string): { unitCostUsd: number; rollingAvgUsd: number } {
|
|
66
90
|
const ledger = getLedger();
|
|
67
91
|
if (!ledger || !Array.isArray(ledger.units) || ledger.units.length === 0) {
|
|
@@ -551,11 +575,18 @@ export async function runPostUnitVerification(
|
|
|
551
575
|
const taskAlreadyComplete = taskRow?.status === "complete" || taskRow?.status === "done";
|
|
552
576
|
if (mid && sid && tid) {
|
|
553
577
|
try {
|
|
554
|
-
const
|
|
555
|
-
if (
|
|
556
|
-
const tasksDir = join(sDir, "tasks");
|
|
578
|
+
const evidenceLocation = resolveVerificationEvidenceLocation(s.basePath, mid, sid);
|
|
579
|
+
if (evidenceLocation) {
|
|
557
580
|
if (result.passed) {
|
|
558
|
-
writeVerificationJSON(
|
|
581
|
+
writeVerificationJSON(
|
|
582
|
+
result,
|
|
583
|
+
evidenceLocation.dir,
|
|
584
|
+
tid,
|
|
585
|
+
s.currentUnit.id,
|
|
586
|
+
undefined,
|
|
587
|
+
undefined,
|
|
588
|
+
evidenceLocation.fileSliceId,
|
|
589
|
+
);
|
|
559
590
|
} else {
|
|
560
591
|
const nextAttempt = attempt + 1;
|
|
561
592
|
const includeRetryMetadata =
|
|
@@ -566,11 +597,12 @@ export async function runPostUnitVerification(
|
|
|
566
597
|
nextAttempt <= maxRetries;
|
|
567
598
|
writeVerificationJSON(
|
|
568
599
|
result,
|
|
569
|
-
|
|
600
|
+
evidenceLocation.dir,
|
|
570
601
|
tid,
|
|
571
602
|
s.currentUnit.id,
|
|
572
603
|
includeRetryMetadata ? nextAttempt : undefined,
|
|
573
604
|
includeRetryMetadata ? maxRetries : undefined,
|
|
605
|
+
evidenceLocation.fileSliceId,
|
|
574
606
|
);
|
|
575
607
|
}
|
|
576
608
|
}
|
|
@@ -718,9 +750,8 @@ export async function runPostUnitVerification(
|
|
|
718
750
|
// Re-write verification evidence JSON with post-execution checks
|
|
719
751
|
if (postExecChecks && postExecChecks.length > 0 && mid && sid && tid) {
|
|
720
752
|
try {
|
|
721
|
-
const
|
|
722
|
-
if (
|
|
723
|
-
const tasksDir = join(sDir, "tasks");
|
|
753
|
+
const evidenceLocation = resolveVerificationEvidenceLocation(s.basePath, mid, sid);
|
|
754
|
+
if (evidenceLocation) {
|
|
724
755
|
// Add postExecutionChecks to the result for the JSON write
|
|
725
756
|
const resultWithPostExec = {
|
|
726
757
|
...result,
|
|
@@ -730,12 +761,13 @@ export async function runPostUnitVerification(
|
|
|
730
761
|
// Manually write with postExecutionChecks field
|
|
731
762
|
writeVerificationJSONWithPostExec(
|
|
732
763
|
resultWithPostExec,
|
|
733
|
-
|
|
764
|
+
evidenceLocation.dir,
|
|
734
765
|
tid,
|
|
735
766
|
s.currentUnit.id,
|
|
736
767
|
postExecChecks,
|
|
737
768
|
postExecBlockingFailure ? attempt + 1 : undefined,
|
|
738
|
-
postExecBlockingFailure ? maxRetries : undefined
|
|
769
|
+
postExecBlockingFailure ? maxRetries : undefined,
|
|
770
|
+
evidenceLocation.fileSliceId,
|
|
739
771
|
);
|
|
740
772
|
}
|
|
741
773
|
} catch (evidenceErr) {
|
|
@@ -940,6 +972,7 @@ function writeVerificationJSONWithPostExec(
|
|
|
940
972
|
postExecutionChecks: PostExecutionCheckJSON[],
|
|
941
973
|
retryAttempt?: number,
|
|
942
974
|
maxRetries?: number,
|
|
975
|
+
sliceId?: string,
|
|
943
976
|
): void {
|
|
944
977
|
mkdirSync(tasksDir, { recursive: true });
|
|
945
978
|
|
|
@@ -980,6 +1013,7 @@ function writeVerificationJSONWithPostExec(
|
|
|
980
1013
|
}));
|
|
981
1014
|
}
|
|
982
1015
|
|
|
983
|
-
const
|
|
1016
|
+
const fileName = sliceId ? `${sliceId}-${taskId}-VERIFY.json` : `${taskId}-VERIFY.json`;
|
|
1017
|
+
const filePath = join(tasksDir, fileName);
|
|
984
1018
|
writeFileSync(filePath, JSON.stringify(evidence, null, 2) + "\n", "utf-8");
|
|
985
1019
|
}
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
_isSamePath as isSamePath,
|
|
17
17
|
_shouldReconcileWorktreeDb,
|
|
18
18
|
} from "./auto-worktree-cleanup.js";
|
|
19
|
-
import { resolveGsdPathContract } from "./paths.js";
|
|
19
|
+
import { dirIsContentBearingLegacyMilestone, resolveGsdPathContract } from "./paths.js";
|
|
20
20
|
import type { MilestoneScope } from "./workspace.js";
|
|
21
21
|
import { WorktreeStateProjection } from "./worktree-state-projection.js";
|
|
22
22
|
import { logWarning } from "./workflow-logger.js";
|
|
@@ -211,10 +211,17 @@ function syncMilestoneLayout(
|
|
|
211
211
|
if (!existsSync(mainMilestonesDir)) return;
|
|
212
212
|
|
|
213
213
|
try {
|
|
214
|
-
mkdirSync(wtMilestonesDir, { recursive: true });
|
|
215
214
|
const mainMilestones = readdirSync(mainMilestonesDir, { withFileTypes: true })
|
|
216
215
|
.filter((entry) => entry.isDirectory())
|
|
217
|
-
.map((entry) => entry.name)
|
|
216
|
+
.map((entry) => entry.name)
|
|
217
|
+
.filter((name) =>
|
|
218
|
+
layoutSegment !== "milestones" ||
|
|
219
|
+
dirIsContentBearingLegacyMilestone(join(mainMilestonesDir, name)),
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
if (mainMilestones.length === 0) return;
|
|
223
|
+
|
|
224
|
+
mkdirSync(wtMilestonesDir, { recursive: true });
|
|
218
225
|
|
|
219
226
|
for (const milestoneId of mainMilestones) {
|
|
220
227
|
syncMilestoneDirectory(
|
|
@@ -3163,7 +3163,19 @@ export async function dispatchHookUnit(
|
|
|
3163
3163
|
workspaceRoot: s.basePath,
|
|
3164
3164
|
});
|
|
3165
3165
|
|
|
3166
|
-
const
|
|
3166
|
+
const cmdCtx = s.cmdCtx;
|
|
3167
|
+
if (typeof cmdCtx?.newSession !== "function") {
|
|
3168
|
+
const message = "Auto-mode has no command context for dispatch.";
|
|
3169
|
+
ctx.ui.notify(message, "error");
|
|
3170
|
+
const unitToCommit = previousCurrentUnit ?? s.currentUnit;
|
|
3171
|
+
if (unitToCommit) {
|
|
3172
|
+
await autoCommitUnit(s.basePath, unitToCommit.type, unitToCommit.id, ctx);
|
|
3173
|
+
}
|
|
3174
|
+
await stopAuto(ctx, pi, message, { preserveWorktree: true });
|
|
3175
|
+
return false;
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
const result = await cmdCtx.newSession({ workspaceRoot: s.basePath });
|
|
3167
3179
|
if (result.cancelled) {
|
|
3168
3180
|
await stopAuto(ctx, pi);
|
|
3169
3181
|
return false;
|
|
@@ -7,7 +7,7 @@ import { minimatch } from "minimatch";
|
|
|
7
7
|
import { GSD_PHASE_SCOPE_DISPLAY_REASON, shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
|
|
8
8
|
import { canonicalToolName } from "../engine-hook-contract.js";
|
|
9
9
|
import { loadJsonFileOrNull } from "../json-persistence.js";
|
|
10
|
-
import { getIsolationMode } from "../preferences.js";
|
|
10
|
+
import { getIsolationMode, loadEffectiveGSDPreferences } from "../preferences.js";
|
|
11
11
|
import { compileSubagentPermissionContract, type ToolsPolicy } from "../unit-context-manifest.js";
|
|
12
12
|
import {
|
|
13
13
|
allowedPlanningDispatchAgentsList,
|
|
@@ -1354,6 +1354,7 @@ export function shouldBlockPlanningUnit(
|
|
|
1354
1354
|
const dispatchContract = compileSubagentPermissionContract(policy);
|
|
1355
1355
|
const allowedSubagents = dispatchContract.allowedSubagents;
|
|
1356
1356
|
const allowed = new Set(allowedSubagents);
|
|
1357
|
+
const planningSubagentRegistry = loadEffectiveGSDPreferences(basePath)?.preferences.planning_subagent_registry;
|
|
1357
1358
|
// When agentClasses is undefined, the caller has not been updated to extract
|
|
1358
1359
|
// agent identities yet. Block and warn so stale callers surface in telemetry
|
|
1359
1360
|
// instead of silently bypassing the gate.
|
|
@@ -1371,12 +1372,12 @@ export function shouldBlockPlanningUnit(
|
|
|
1371
1372
|
if (requested.length === 0) {
|
|
1372
1373
|
return { block: false };
|
|
1373
1374
|
}
|
|
1374
|
-
const globallyDisallowed = requested.find(a => !isReadOnlyPlanningDispatchAgent(a));
|
|
1375
|
+
const globallyDisallowed = requested.find(a => !isReadOnlyPlanningDispatchAgent(a, planningSubagentRegistry));
|
|
1375
1376
|
if (globallyDisallowed) {
|
|
1376
1377
|
return planningBlock(
|
|
1377
1378
|
unitType,
|
|
1378
1379
|
policy.mode,
|
|
1379
|
-
`subagent dispatch of "${globallyDisallowed}" not permitted; only read-only specialists (${allowedPlanningDispatchAgentsList()}) may be dispatched from ${policy.mode} units`,
|
|
1380
|
+
`subagent dispatch of "${globallyDisallowed}" not permitted; only read-only specialists (${allowedPlanningDispatchAgentsList(planningSubagentRegistry)}) may be dispatched from ${policy.mode} units`,
|
|
1380
1381
|
);
|
|
1381
1382
|
}
|
|
1382
1383
|
const disallowedByPolicy = requested.find(a => !allowed.has(a));
|
|
@@ -124,11 +124,11 @@ Diagnostics record the file path, scope (global/project), severity (error/warnin
|
|
|
124
124
|
- `validation` — used for gate evaluation, roadmap reassessment, milestone validation, and doc rewrites. Falls back to `planning` if unset.
|
|
125
125
|
- `uat` — used for UAT runs. Falls back to `completion` if unset.
|
|
126
126
|
|
|
127
|
-
- `thinking`: per-phase reasoning effort (ADR-026), separate from `models`. Same phase keys as `models`. Values: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`. Thinking travels with the model — model choice and reasoning effort are independent controls, so you can run one model across phases at different reasoning levels.
|
|
127
|
+
- `thinking`: per-phase reasoning effort (ADR-026), separate from `models`. Same phase keys as `models`. Values: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Thinking travels with the model — model choice and reasoning effort are independent controls, so you can run one model across phases at different reasoning levels.
|
|
128
128
|
- Two equivalent ways to set it: inline as `models.<phase>.thinking`, or as a separate `thinking:` block keyed by phase. For the same phase, the inline value wins over the block; project preferences win over global; a phase that's unset inherits via the same sibling chain as `models` (e.g. `discuss → planning`).
|
|
129
129
|
- If no thinking is configured for a phase, the session level (set via `/model`) is used, exactly as before — this is fully backward-compatible.
|
|
130
130
|
- `execute-task` (code-writing) has a measured reasoning floor of `medium`: at lower levels models stop planning edits and thrash on file re-reads. The floor applies to the session/default path. An **explicit** `execution` thinking level bypasses the floor and is honored verbatim (with a one-time advisory) — set `execution: low` deliberately if you want it.
|
|
131
|
-
- Levels a model can't support are clamped to the nearest supported level at dispatch and never sent to the provider, so a model/level mismatch never fails a unit mid-run. `xhigh` support
|
|
131
|
+
- Levels a model can't support are clamped to the nearest supported level at dispatch and never sent to the provider, so a model/level mismatch never fails a unit mid-run. `xhigh` and `max` support are model-dependent; current examples include `gpt-5.6-sol`, `gpt-5.6-terra`, and `gpt-5.6-luna`, which advertise both.
|
|
132
132
|
|
|
133
133
|
- `skill_staleness_days`: number — skills unused for this many days get deprioritized during discovery. Set to `0` to disable staleness tracking. Default: `60`.
|
|
134
134
|
|
|
@@ -357,24 +357,33 @@ In `"parent"` mode, slice/task `targetRepositories` default to the declared chil
|
|
|
357
357
|
- `"replace"` requires `prompt`.
|
|
358
358
|
- `"skip"` is valid with no additional fields.
|
|
359
359
|
|
|
360
|
+
- `planning_subagent_registry`: object — optional classification for custom planning agents. Each key is an agent id and must set:
|
|
361
|
+
- `read_only_specialist`: boolean — set to `true` to classify the custom agent as safe for planning dispatch.
|
|
362
|
+
|
|
360
363
|
- `planning_subagents`: object — project-local widening for read-only planning subagent dispatch. Supported unit keys are `plan-milestone` and `plan-slice`; each has:
|
|
361
364
|
- `allowed`: string[] — additional planning-safe agents for that unit.
|
|
362
365
|
|
|
363
|
-
Agents must
|
|
366
|
+
Agents must be built-in read-only planning specialists (`mnemo`, `scout`, `planner`, `reviewer`, `security`, or `tester`) or registered in `planning_subagent_registry` with `read_only_specialist: true`. The write gate still blocks source writes outside `.gsd/**`, unrestricted bash, stale subagent calls without agent identities, agents outside the read-only registry, and agents not listed for the active unit.
|
|
364
367
|
|
|
365
368
|
Example:
|
|
366
369
|
|
|
367
370
|
```yaml
|
|
371
|
+
planning_subagent_registry:
|
|
372
|
+
my-custom-planner:
|
|
373
|
+
read_only_specialist: true
|
|
374
|
+
|
|
368
375
|
planning_subagents:
|
|
369
376
|
plan-milestone:
|
|
370
377
|
allowed:
|
|
371
378
|
- scout
|
|
372
379
|
- planner
|
|
380
|
+
- my-custom-planner
|
|
373
381
|
- security
|
|
374
382
|
plan-slice:
|
|
375
383
|
allowed:
|
|
376
384
|
- scout
|
|
377
385
|
- planner
|
|
386
|
+
- my-custom-planner
|
|
378
387
|
- reviewer
|
|
379
388
|
- security
|
|
380
389
|
```
|
|
@@ -32,6 +32,7 @@ import { parseRoadmapSlices } from "./roadmap-slices.js";
|
|
|
32
32
|
import { parsePlan } from "./parsers-legacy.js";
|
|
33
33
|
import { parseSummary } from "./files.js";
|
|
34
34
|
import { LAYOUT_SEGMENTS } from "./layout-policy.js";
|
|
35
|
+
import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
|
|
35
36
|
|
|
36
37
|
const USER_AUTHORED_ARTIFACT_TYPES = new Set(["CONTEXT", "RESEARCH"]);
|
|
37
38
|
|
|
@@ -95,9 +96,29 @@ function reportCheckboxDbStatusDivergence(
|
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
function bareDuplicateMilestoneId(milestoneId: string): string | null {
|
|
100
|
+
const match = milestoneId.match(/^(M\d{3})-[a-z0-9]{6}$/i);
|
|
101
|
+
return match?.[1]?.toUpperCase() ?? null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function checkboxDbStatusMilestoneIds(basePath: string, milestoneIds: string[]): string[] {
|
|
105
|
+
if (!hasFlatPhaseLayout(basePath)) return milestoneIds;
|
|
106
|
+
|
|
107
|
+
// Flat-phase directories are keyed by phase number. A restored
|
|
108
|
+
// M003-xxxxxx row alongside M003 is a stale duplicate for the same
|
|
109
|
+
// phases/03-* projection, so checking both can compare DB status against two
|
|
110
|
+
// competing PLAN files and report a persistent false divergence.
|
|
111
|
+
const allIds = new Set(milestoneIds.map((id) => id.toUpperCase()));
|
|
112
|
+
return milestoneIds.filter((milestoneId) => {
|
|
113
|
+
const bareId = bareDuplicateMilestoneId(milestoneId);
|
|
114
|
+
return !bareId || !allIds.has(bareId);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
98
118
|
function checkProjectionCheckboxDbStatus(basePath: string, milestoneIds: string[], issues: DoctorIssue[]): void {
|
|
99
119
|
for (const milestoneId of milestoneIds) {
|
|
100
|
-
const
|
|
120
|
+
const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, milestoneId);
|
|
121
|
+
const roadmapPath = resolveMilestoneFile(artifactBasePath, milestoneId, "ROADMAP");
|
|
101
122
|
const slices = getMilestoneSlices(milestoneId);
|
|
102
123
|
|
|
103
124
|
if (roadmapPath && existsSync(roadmapPath)) {
|
|
@@ -123,7 +144,7 @@ function checkProjectionCheckboxDbStatus(basePath: string, milestoneIds: string[
|
|
|
123
144
|
}
|
|
124
145
|
|
|
125
146
|
for (const slice of slices) {
|
|
126
|
-
const planPath = resolveSliceFile(
|
|
147
|
+
const planPath = resolveSliceFile(artifactBasePath, milestoneId, slice.id, "PLAN");
|
|
127
148
|
if (!planPath || !existsSync(planPath)) continue;
|
|
128
149
|
try {
|
|
129
150
|
const plan = readFileSync(planPath, "utf-8");
|
|
@@ -173,7 +194,7 @@ function artifactExistsOnDisk(basePath: string, artifactPath: string, row?: Arti
|
|
|
173
194
|
return resolveArtifactDiskPath(basePath, artifactPath, row) !== null;
|
|
174
195
|
}
|
|
175
196
|
|
|
176
|
-
function resolveLiteralArtifactDiskPath(basePath: string, artifactPath: string): string | null {
|
|
197
|
+
function resolveLiteralArtifactDiskPath(basePath: string, artifactPath: string, row?: ArtifactRow): string | null {
|
|
177
198
|
const relativeArtifactPath = artifactPathRelativeToGsd(artifactPath);
|
|
178
199
|
if (isAbsolute(relativeArtifactPath)) {
|
|
179
200
|
return existsSync(relativeArtifactPath) ? relativeArtifactPath : null;
|
|
@@ -182,6 +203,15 @@ function resolveLiteralArtifactDiskPath(basePath: string, artifactPath: string):
|
|
|
182
203
|
const candidate = join(root, relativeArtifactPath);
|
|
183
204
|
if (isPathInside(root, candidate) && existsSync(candidate)) return candidate;
|
|
184
205
|
}
|
|
206
|
+
if (row?.milestone_id) {
|
|
207
|
+
const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, row.milestone_id);
|
|
208
|
+
if (artifactBasePath !== basePath) {
|
|
209
|
+
for (const root of [gsdProjectionRoot(artifactBasePath), gsdRoot(artifactBasePath)]) {
|
|
210
|
+
const candidate = join(root, relativeArtifactPath);
|
|
211
|
+
if (isPathInside(root, candidate) && existsSync(candidate)) return candidate;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
185
215
|
return null;
|
|
186
216
|
}
|
|
187
217
|
|
|
@@ -224,7 +254,7 @@ function resolveArtifactDiskPath(basePath: string, artifactPath: string, row?: A
|
|
|
224
254
|
const flatPath = resolveFlatPhaseArtifactDiskPath(basePath, row);
|
|
225
255
|
if (flatPath) return flatPath;
|
|
226
256
|
}
|
|
227
|
-
return resolveLiteralArtifactDiskPath(basePath, artifactPath);
|
|
257
|
+
return resolveLiteralArtifactDiskPath(basePath, artifactPath, row);
|
|
228
258
|
}
|
|
229
259
|
|
|
230
260
|
function taskExistsInPlan(basePath: string, milestoneId: string, sliceId: string, taskId: string): boolean {
|
|
@@ -434,7 +464,9 @@ function staleMilestonesArtifactRowFixable(basePath: string, row: ArtifactRow, a
|
|
|
434
464
|
|
|
435
465
|
function stalePhasesArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
|
|
436
466
|
const issuePath = artifactPathRelativeToGsd(row.path);
|
|
437
|
-
return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) &&
|
|
467
|
+
return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) &&
|
|
468
|
+
(hasPresentFlatPhaseReplacement(basePath, row, artifactRows) ||
|
|
469
|
+
hasPresentMilestonesReplacement(basePath, row, artifactRows));
|
|
438
470
|
}
|
|
439
471
|
|
|
440
472
|
function staleArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
|
|
@@ -807,7 +839,11 @@ export async function checkEngineHealth(
|
|
|
807
839
|
// block doctor.
|
|
808
840
|
try {
|
|
809
841
|
if (isDbAvailable()) {
|
|
810
|
-
checkProjectionCheckboxDbStatus(
|
|
842
|
+
checkProjectionCheckboxDbStatus(
|
|
843
|
+
basePath,
|
|
844
|
+
checkboxDbStatusMilestoneIds(basePath, getAllMilestones().map((milestone) => milestone.id)),
|
|
845
|
+
issues,
|
|
846
|
+
);
|
|
811
847
|
}
|
|
812
848
|
} catch {
|
|
813
849
|
// Non-fatal: checkbox-vs-DB divergence check must never block doctor
|
|
@@ -155,6 +155,19 @@ function isInsideFlatProjectionBackup(backupDir: string, src: string): boolean {
|
|
|
155
155
|
return src === phaseBackupDir || src.startsWith(`${phaseBackupDir}/`) || src.startsWith(`${phaseBackupDir}\\`);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
function removeFlatProjectionBackup(backupDir: string): void {
|
|
159
|
+
const phaseBackupDir = join(backupDir, "__phases");
|
|
160
|
+
if (!existsSync(phaseBackupDir)) return;
|
|
161
|
+
try {
|
|
162
|
+
removePathWithRetries(phaseBackupDir);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
logWarning(
|
|
165
|
+
"migration",
|
|
166
|
+
`flat-phase migration succeeded but could not remove temporary ${LAYOUT_SEGMENTS.level1}/ backup: ${(err as Error).message}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
158
171
|
function restoreFlatProjectionFromBackup(basePath: string, backupDir: string): void {
|
|
159
172
|
const phaseBackupDir = join(backupDir, "__phases");
|
|
160
173
|
if (!existsSync(phaseBackupDir)) return;
|
|
@@ -494,4 +507,5 @@ export async function migrateToFlatPhase(basePath: string): Promise<void> {
|
|
|
494
507
|
`flat-phase migration succeeded but could not remove ${LEGACY_MIGRATING_SEGMENT}: ${(err as Error).message}`,
|
|
495
508
|
);
|
|
496
509
|
}
|
|
510
|
+
removeFlatProjectionBackup(backupDir);
|
|
497
511
|
}
|
|
@@ -67,6 +67,13 @@ export type { SliceRow, TaskRow } from "./db-task-slice-rows.js";
|
|
|
67
67
|
|
|
68
68
|
import { TERMINAL_STATUS_SQL } from "./db/sql-constants.js";
|
|
69
69
|
import { applyStatusTransition } from "./db/writers/status.js";
|
|
70
|
+
import {
|
|
71
|
+
LAYOUT_SEGMENTS,
|
|
72
|
+
derivePhaseSlug,
|
|
73
|
+
milestoneIdToPhaseNum,
|
|
74
|
+
milestoneIdUniqueSuffix,
|
|
75
|
+
phaseDirName,
|
|
76
|
+
} from "./layout-policy.js";
|
|
70
77
|
|
|
71
78
|
export function insertDecision(d: Omit<Decision, "seq">): void {
|
|
72
79
|
if (!getDbOrNull()!) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
@@ -206,6 +213,47 @@ export function insertArtifact(a: {
|
|
|
206
213
|
});
|
|
207
214
|
}
|
|
208
215
|
|
|
216
|
+
function canonicalPhaseDirNameForDb(milestoneId: string, title: string): string {
|
|
217
|
+
const phaseNum = milestoneIdToPhaseNum(milestoneId);
|
|
218
|
+
const slug = derivePhaseSlug(title || milestoneId);
|
|
219
|
+
const suffix = milestoneIdUniqueSuffix(milestoneId);
|
|
220
|
+
return phaseDirName(phaseNum, suffix ? `${suffix}-${slug}` : slug);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function reconcileMilestonePhaseArtifactPaths(milestoneId: string, title: string): void {
|
|
224
|
+
const db = getDbOrNull()!;
|
|
225
|
+
const phaseNumPrefix = String(milestoneIdToPhaseNum(milestoneId)).padStart(2, "0");
|
|
226
|
+
const canonicalDir = canonicalPhaseDirNameForDb(milestoneId, title);
|
|
227
|
+
const staleRows = db.prepare(
|
|
228
|
+
`SELECT path
|
|
229
|
+
FROM artifacts
|
|
230
|
+
WHERE milestone_id = :milestone_id
|
|
231
|
+
AND path LIKE :phase_prefix`,
|
|
232
|
+
).all({
|
|
233
|
+
":milestone_id": milestoneId,
|
|
234
|
+
":phase_prefix": `${LAYOUT_SEGMENTS.level1}/${phaseNumPrefix}-%/%`,
|
|
235
|
+
}) as Array<{ path: string }>;
|
|
236
|
+
|
|
237
|
+
const updatePath = db.prepare("UPDATE artifacts SET path = :new_path WHERE path = :old_path");
|
|
238
|
+
const deletePath = db.prepare("DELETE FROM artifacts WHERE path = :path");
|
|
239
|
+
const existingPath = db.prepare("SELECT 1 AS present FROM artifacts WHERE path = :path");
|
|
240
|
+
|
|
241
|
+
for (const row of staleRows) {
|
|
242
|
+
const parts = row.path.split("/");
|
|
243
|
+
if (parts.length < 3) continue;
|
|
244
|
+
if (parts[0] !== LAYOUT_SEGMENTS.level1) continue;
|
|
245
|
+
if (parts[1] === canonicalDir) continue;
|
|
246
|
+
if (!parts[1]?.startsWith(`${phaseNumPrefix}-`)) continue;
|
|
247
|
+
|
|
248
|
+
const newPath = [LAYOUT_SEGMENTS.level1, canonicalDir, ...parts.slice(2)].join("/");
|
|
249
|
+
if (existingPath.get({ ":path": newPath })) {
|
|
250
|
+
deletePath.run({ ":path": row.path });
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
updatePath.run({ ":new_path": newPath, ":old_path": row.path });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
209
257
|
export interface MilestonePlanningRecord {
|
|
210
258
|
vision: string;
|
|
211
259
|
successCriteria: string[];
|
|
@@ -287,39 +335,43 @@ export function insertMilestone(m: {
|
|
|
287
335
|
|
|
288
336
|
export function upsertMilestonePlanning(milestoneId: string, planning: Partial<MilestonePlanningRecord> & { title?: string; status?: string; depends_on?: string[] }): void {
|
|
289
337
|
if (!getDbOrNull()!) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
338
|
+
transaction(() => {
|
|
339
|
+
getDbOrNull()!.prepare(
|
|
340
|
+
`UPDATE milestones SET
|
|
341
|
+
title = COALESCE(NULLIF(:title, ''), title),
|
|
342
|
+
status = COALESCE(NULLIF(:status, ''), status),
|
|
343
|
+
depends_on = COALESCE(:depends_on, depends_on),
|
|
344
|
+
vision = COALESCE(:vision, vision),
|
|
345
|
+
success_criteria = COALESCE(:success_criteria, success_criteria),
|
|
346
|
+
key_risks = COALESCE(:key_risks, key_risks),
|
|
347
|
+
proof_strategy = COALESCE(:proof_strategy, proof_strategy),
|
|
348
|
+
verification_contract = COALESCE(:verification_contract, verification_contract),
|
|
349
|
+
verification_integration = COALESCE(:verification_integration, verification_integration),
|
|
350
|
+
verification_operational = COALESCE(:verification_operational, verification_operational),
|
|
351
|
+
verification_uat = COALESCE(:verification_uat, verification_uat),
|
|
352
|
+
definition_of_done = COALESCE(:definition_of_done, definition_of_done),
|
|
353
|
+
requirement_coverage = COALESCE(:requirement_coverage, requirement_coverage),
|
|
354
|
+
boundary_map_markdown = COALESCE(:boundary_map_markdown, boundary_map_markdown)
|
|
355
|
+
WHERE id = :id`,
|
|
356
|
+
).run({
|
|
357
|
+
":id": milestoneId,
|
|
358
|
+
":title": planning.title ?? "",
|
|
359
|
+
":status": planning.status ?? "",
|
|
360
|
+
":depends_on": planning.depends_on ? JSON.stringify(planning.depends_on) : null,
|
|
361
|
+
":vision": planning.vision ?? null,
|
|
362
|
+
":success_criteria": planning.successCriteria ? JSON.stringify(planning.successCriteria) : null,
|
|
363
|
+
":key_risks": planning.keyRisks ? JSON.stringify(planning.keyRisks) : null,
|
|
364
|
+
":proof_strategy": planning.proofStrategy ? JSON.stringify(planning.proofStrategy) : null,
|
|
365
|
+
":verification_contract": planning.verificationContract ?? null,
|
|
366
|
+
":verification_integration": planning.verificationIntegration ?? null,
|
|
367
|
+
":verification_operational": planning.verificationOperational ?? null,
|
|
368
|
+
":verification_uat": planning.verificationUat ?? null,
|
|
369
|
+
":definition_of_done": planning.definitionOfDone ? JSON.stringify(planning.definitionOfDone) : null,
|
|
370
|
+
":requirement_coverage": planning.requirementCoverage ?? null,
|
|
371
|
+
":boundary_map_markdown": planning.boundaryMapMarkdown ?? null,
|
|
372
|
+
});
|
|
373
|
+
const finalTitle = planning.title?.trim();
|
|
374
|
+
if (finalTitle) reconcileMilestonePhaseArtifactPaths(milestoneId, finalTitle);
|
|
323
375
|
});
|
|
324
376
|
}
|
|
325
377
|
|
|
@@ -12,6 +12,12 @@ export interface ModelCostEntry {
|
|
|
12
12
|
inputPer1k: number;
|
|
13
13
|
/** Approximate cost per 1K output tokens in USD */
|
|
14
14
|
outputPer1k: number;
|
|
15
|
+
/** Input-token-based long-context pricing tiers, when published */
|
|
16
|
+
tiers?: Array<{
|
|
17
|
+
inputTokensAbove: number;
|
|
18
|
+
inputPer1k: number;
|
|
19
|
+
outputPer1k: number;
|
|
20
|
+
}>;
|
|
15
21
|
/** Last updated date */
|
|
16
22
|
updatedAt: string;
|
|
17
23
|
}
|
|
@@ -62,8 +68,9 @@ export const BUNDLED_COST_TABLE: ModelCostEntry[] = [
|
|
|
62
68
|
// GPT-5.5 API list price, also used for live Codex OAuth routing.
|
|
63
69
|
// Source: https://openai.com/api/pricing/
|
|
64
70
|
{ id: "gpt-5.5", inputPer1k: 0.005, outputPer1k: 0.03, updatedAt: "2026-04-23" },
|
|
65
|
-
|
|
66
|
-
{ id: "gpt-5.6", inputPer1k: 0.005, outputPer1k: 0.
|
|
71
|
+
{ id: "gpt-5.6-sol", inputPer1k: 0.005, outputPer1k: 0.03, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.01, outputPer1k: 0.045 }], updatedAt: "2026-07-11" },
|
|
72
|
+
{ id: "gpt-5.6-terra", inputPer1k: 0.0025, outputPer1k: 0.015, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.005, outputPer1k: 0.0225 }], updatedAt: "2026-07-11" },
|
|
73
|
+
{ id: "gpt-5.6-luna", inputPer1k: 0.001, outputPer1k: 0.006, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.002, outputPer1k: 0.009 }], updatedAt: "2026-07-11" },
|
|
67
74
|
|
|
68
75
|
// Google
|
|
69
76
|
{ id: "gemini-2.0-flash", inputPer1k: 0.0001, outputPer1k: 0.0004, updatedAt: "2025-03-15" },
|
|
@@ -80,7 +87,11 @@ export const BUNDLED_COST_TABLE: ModelCostEntry[] = [
|
|
|
80
87
|
export function lookupModelCost(modelId: string): ModelCostEntry | undefined {
|
|
81
88
|
const bareId = modelId.includes("/") ? modelId.split("/").pop()! : modelId;
|
|
82
89
|
return BUNDLED_COST_TABLE.find(e => e.id === bareId)
|
|
83
|
-
?? BUNDLED_COST_TABLE.find(e =>
|
|
90
|
+
?? BUNDLED_COST_TABLE.find(e =>
|
|
91
|
+
bareId.startsWith(`${e.id}-`) ||
|
|
92
|
+
bareId.startsWith(`${e.id}:`) ||
|
|
93
|
+
bareId.startsWith(`${e.id}@`)
|
|
94
|
+
);
|
|
84
95
|
}
|
|
85
96
|
|
|
86
97
|
/**
|
|
@@ -111,7 +111,9 @@ export const MODEL_CAPABILITY_TIER: Record<string, ComplexityTier> = {
|
|
|
111
111
|
"gpt-5.3-codex": "heavy",
|
|
112
112
|
"gpt-5.4": "heavy",
|
|
113
113
|
"gpt-5.5": "heavy",
|
|
114
|
-
"gpt-5.6": "heavy",
|
|
114
|
+
"gpt-5.6-sol": "heavy",
|
|
115
|
+
"gpt-5.6-terra": "heavy",
|
|
116
|
+
"gpt-5.6-luna": "heavy",
|
|
115
117
|
"o1": "heavy",
|
|
116
118
|
"o3": "heavy",
|
|
117
119
|
"o4-mini": "heavy",
|
|
@@ -151,7 +153,9 @@ const MODEL_COST_PER_1K_INPUT: Record<string, number> = {
|
|
|
151
153
|
"gpt-5.3-codex-spark": 0.0003,
|
|
152
154
|
"gpt-5.4": 0.005,
|
|
153
155
|
"gpt-5.5": 0.005,
|
|
154
|
-
"gpt-5.6": 0.005,
|
|
156
|
+
"gpt-5.6-sol": 0.005,
|
|
157
|
+
"gpt-5.6-terra": 0.0025,
|
|
158
|
+
"gpt-5.6-luna": 0.001,
|
|
155
159
|
"o4-mini": 0.005,
|
|
156
160
|
"o4-mini-deep-research": 0.005,
|
|
157
161
|
"gemini-2.0-flash": 0.0001,
|
|
@@ -202,9 +206,10 @@ export const MODEL_CAPABILITY_PROFILES: Record<string, ModelCapabilities> = {
|
|
|
202
206
|
// OpenAI's 2026-04-23 published eval deltas across coding, tool use, and long context.
|
|
203
207
|
// Source: https://openai.com/index/introducing-gpt-5-5/
|
|
204
208
|
"gpt-5.5": { coding: 96, debugging: 93, research: 89, reasoning: 95, speed: 42, longContext: 90, instruction: 93 },
|
|
205
|
-
// GPT-5.6
|
|
206
|
-
|
|
207
|
-
"gpt-5.6":
|
|
209
|
+
// GPT-5.6 profiles are provisional until official eval results are available.
|
|
210
|
+
"gpt-5.6-sol": { coding: 97, debugging: 94, research: 90, reasoning: 96, speed: 42, longContext: 91, instruction: 94 },
|
|
211
|
+
"gpt-5.6-terra": { coding: 96, debugging: 93, research: 89, reasoning: 95, speed: 48, longContext: 91, instruction: 93 },
|
|
212
|
+
"gpt-5.6-luna": { coding: 92, debugging: 88, research: 84, reasoning: 91, speed: 72, longContext: 91, instruction: 90 },
|
|
208
213
|
|
|
209
214
|
// ── OpenAI o-series (reasoning-first) ──────────────────────────────────────
|
|
210
215
|
"o1": { coding: 78, debugging: 82, research: 78, reasoning: 90, speed: 20, longContext: 65, instruction: 82 },
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* preference validation before any configured planning allowlist takes effect.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { PlanningSubagentRegistryConfig } from "./preferences-types.js";
|
|
10
|
+
|
|
9
11
|
const PLANNING_DISPATCH_AGENT_REGISTRY = {
|
|
10
12
|
mnemo: { readOnlySpecialist: true },
|
|
11
13
|
scout: { readOnlySpecialist: true },
|
|
@@ -21,11 +23,23 @@ export const ALLOWED_PLANNING_DISPATCH_AGENTS = new Set<string>(
|
|
|
21
23
|
.map(([agentId]) => agentId),
|
|
22
24
|
);
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
function configuredPlanningDispatchAgents(registry?: PlanningSubagentRegistryConfig): string[] {
|
|
27
|
+
return Object.entries(registry ?? {})
|
|
28
|
+
.filter(([, metadata]) => metadata.read_only_specialist === true)
|
|
29
|
+
.map(([agentId]) => agentId);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isReadOnlyPlanningDispatchAgent(
|
|
33
|
+
agentId: string,
|
|
34
|
+
registry?: PlanningSubagentRegistryConfig,
|
|
35
|
+
): boolean {
|
|
25
36
|
const metadata = PLANNING_DISPATCH_AGENT_REGISTRY[agentId as keyof typeof PLANNING_DISPATCH_AGENT_REGISTRY];
|
|
26
|
-
return metadata?.readOnlySpecialist === true;
|
|
37
|
+
return metadata?.readOnlySpecialist === true || registry?.[agentId]?.read_only_specialist === true;
|
|
27
38
|
}
|
|
28
39
|
|
|
29
|
-
export function allowedPlanningDispatchAgentsList(): string {
|
|
30
|
-
return
|
|
40
|
+
export function allowedPlanningDispatchAgentsList(registry?: PlanningSubagentRegistryConfig): string {
|
|
41
|
+
return Array.from(new Set([
|
|
42
|
+
...ALLOWED_PLANNING_DISPATCH_AGENTS,
|
|
43
|
+
...configuredPlanningDispatchAgents(registry),
|
|
44
|
+
])).join(", ");
|
|
31
45
|
}
|