@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
|
@@ -38,6 +38,7 @@ export * from "./db/queries.js";
|
|
|
38
38
|
export * from "./db/writers/cascades.js";
|
|
39
39
|
import { TERMINAL_STATUS_SQL } from "./db/sql-constants.js";
|
|
40
40
|
import { applyStatusTransition } from "./db/writers/status.js";
|
|
41
|
+
import { LAYOUT_SEGMENTS, derivePhaseSlug, milestoneIdToPhaseNum, milestoneIdUniqueSuffix, phaseDirName, } from "./layout-policy.js";
|
|
41
42
|
export function insertDecision(d) {
|
|
42
43
|
if (!getDbOrNull())
|
|
43
44
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
@@ -169,6 +170,44 @@ export function insertArtifact(a) {
|
|
|
169
170
|
":content_hash": contentHash,
|
|
170
171
|
});
|
|
171
172
|
}
|
|
173
|
+
function canonicalPhaseDirNameForDb(milestoneId, title) {
|
|
174
|
+
const phaseNum = milestoneIdToPhaseNum(milestoneId);
|
|
175
|
+
const slug = derivePhaseSlug(title || milestoneId);
|
|
176
|
+
const suffix = milestoneIdUniqueSuffix(milestoneId);
|
|
177
|
+
return phaseDirName(phaseNum, suffix ? `${suffix}-${slug}` : slug);
|
|
178
|
+
}
|
|
179
|
+
function reconcileMilestonePhaseArtifactPaths(milestoneId, title) {
|
|
180
|
+
const db = getDbOrNull();
|
|
181
|
+
const phaseNumPrefix = String(milestoneIdToPhaseNum(milestoneId)).padStart(2, "0");
|
|
182
|
+
const canonicalDir = canonicalPhaseDirNameForDb(milestoneId, title);
|
|
183
|
+
const staleRows = db.prepare(`SELECT path
|
|
184
|
+
FROM artifacts
|
|
185
|
+
WHERE milestone_id = :milestone_id
|
|
186
|
+
AND path LIKE :phase_prefix`).all({
|
|
187
|
+
":milestone_id": milestoneId,
|
|
188
|
+
":phase_prefix": `${LAYOUT_SEGMENTS.level1}/${phaseNumPrefix}-%/%`,
|
|
189
|
+
});
|
|
190
|
+
const updatePath = db.prepare("UPDATE artifacts SET path = :new_path WHERE path = :old_path");
|
|
191
|
+
const deletePath = db.prepare("DELETE FROM artifacts WHERE path = :path");
|
|
192
|
+
const existingPath = db.prepare("SELECT 1 AS present FROM artifacts WHERE path = :path");
|
|
193
|
+
for (const row of staleRows) {
|
|
194
|
+
const parts = row.path.split("/");
|
|
195
|
+
if (parts.length < 3)
|
|
196
|
+
continue;
|
|
197
|
+
if (parts[0] !== LAYOUT_SEGMENTS.level1)
|
|
198
|
+
continue;
|
|
199
|
+
if (parts[1] === canonicalDir)
|
|
200
|
+
continue;
|
|
201
|
+
if (!parts[1]?.startsWith(`${phaseNumPrefix}-`))
|
|
202
|
+
continue;
|
|
203
|
+
const newPath = [LAYOUT_SEGMENTS.level1, canonicalDir, ...parts.slice(2)].join("/");
|
|
204
|
+
if (existingPath.get({ ":path": newPath })) {
|
|
205
|
+
deletePath.run({ ":path": row.path });
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
updatePath.run({ ":new_path": newPath, ":old_path": row.path });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
172
211
|
export function insertMilestone(m) {
|
|
173
212
|
if (!getDbOrNull())
|
|
174
213
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
@@ -207,37 +246,42 @@ export function insertMilestone(m) {
|
|
|
207
246
|
export function upsertMilestonePlanning(milestoneId, planning) {
|
|
208
247
|
if (!getDbOrNull())
|
|
209
248
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
249
|
+
transaction(() => {
|
|
250
|
+
getDbOrNull().prepare(`UPDATE milestones SET
|
|
251
|
+
title = COALESCE(NULLIF(:title, ''), title),
|
|
252
|
+
status = COALESCE(NULLIF(:status, ''), status),
|
|
253
|
+
depends_on = COALESCE(:depends_on, depends_on),
|
|
254
|
+
vision = COALESCE(:vision, vision),
|
|
255
|
+
success_criteria = COALESCE(:success_criteria, success_criteria),
|
|
256
|
+
key_risks = COALESCE(:key_risks, key_risks),
|
|
257
|
+
proof_strategy = COALESCE(:proof_strategy, proof_strategy),
|
|
258
|
+
verification_contract = COALESCE(:verification_contract, verification_contract),
|
|
259
|
+
verification_integration = COALESCE(:verification_integration, verification_integration),
|
|
260
|
+
verification_operational = COALESCE(:verification_operational, verification_operational),
|
|
261
|
+
verification_uat = COALESCE(:verification_uat, verification_uat),
|
|
262
|
+
definition_of_done = COALESCE(:definition_of_done, definition_of_done),
|
|
263
|
+
requirement_coverage = COALESCE(:requirement_coverage, requirement_coverage),
|
|
264
|
+
boundary_map_markdown = COALESCE(:boundary_map_markdown, boundary_map_markdown)
|
|
265
|
+
WHERE id = :id`).run({
|
|
266
|
+
":id": milestoneId,
|
|
267
|
+
":title": planning.title ?? "",
|
|
268
|
+
":status": planning.status ?? "",
|
|
269
|
+
":depends_on": planning.depends_on ? JSON.stringify(planning.depends_on) : null,
|
|
270
|
+
":vision": planning.vision ?? null,
|
|
271
|
+
":success_criteria": planning.successCriteria ? JSON.stringify(planning.successCriteria) : null,
|
|
272
|
+
":key_risks": planning.keyRisks ? JSON.stringify(planning.keyRisks) : null,
|
|
273
|
+
":proof_strategy": planning.proofStrategy ? JSON.stringify(planning.proofStrategy) : null,
|
|
274
|
+
":verification_contract": planning.verificationContract ?? null,
|
|
275
|
+
":verification_integration": planning.verificationIntegration ?? null,
|
|
276
|
+
":verification_operational": planning.verificationOperational ?? null,
|
|
277
|
+
":verification_uat": planning.verificationUat ?? null,
|
|
278
|
+
":definition_of_done": planning.definitionOfDone ? JSON.stringify(planning.definitionOfDone) : null,
|
|
279
|
+
":requirement_coverage": planning.requirementCoverage ?? null,
|
|
280
|
+
":boundary_map_markdown": planning.boundaryMapMarkdown ?? null,
|
|
281
|
+
});
|
|
282
|
+
const finalTitle = planning.title?.trim();
|
|
283
|
+
if (finalTitle)
|
|
284
|
+
reconcileMilestonePhaseArtifactPaths(milestoneId, finalTitle);
|
|
241
285
|
});
|
|
242
286
|
}
|
|
243
287
|
export function insertSlice(s) {
|
|
@@ -48,8 +48,9 @@ export const BUNDLED_COST_TABLE = [
|
|
|
48
48
|
// GPT-5.5 API list price, also used for live Codex OAuth routing.
|
|
49
49
|
// Source: https://openai.com/api/pricing/
|
|
50
50
|
{ id: "gpt-5.5", inputPer1k: 0.005, outputPer1k: 0.03, updatedAt: "2026-04-23" },
|
|
51
|
-
|
|
52
|
-
{ id: "gpt-5.6", inputPer1k: 0.005, outputPer1k: 0.
|
|
51
|
+
{ id: "gpt-5.6-sol", inputPer1k: 0.005, outputPer1k: 0.03, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.01, outputPer1k: 0.045 }], updatedAt: "2026-07-11" },
|
|
52
|
+
{ id: "gpt-5.6-terra", inputPer1k: 0.0025, outputPer1k: 0.015, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.005, outputPer1k: 0.0225 }], updatedAt: "2026-07-11" },
|
|
53
|
+
{ id: "gpt-5.6-luna", inputPer1k: 0.001, outputPer1k: 0.006, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.002, outputPer1k: 0.009 }], updatedAt: "2026-07-11" },
|
|
53
54
|
// Google
|
|
54
55
|
{ id: "gemini-2.0-flash", inputPer1k: 0.0001, outputPer1k: 0.0004, updatedAt: "2025-03-15" },
|
|
55
56
|
{ id: "gemini-flash-2.0", inputPer1k: 0.0001, outputPer1k: 0.0004, updatedAt: "2025-03-15" },
|
|
@@ -63,7 +64,9 @@ export const BUNDLED_COST_TABLE = [
|
|
|
63
64
|
export function lookupModelCost(modelId) {
|
|
64
65
|
const bareId = modelId.includes("/") ? modelId.split("/").pop() : modelId;
|
|
65
66
|
return BUNDLED_COST_TABLE.find(e => e.id === bareId)
|
|
66
|
-
?? BUNDLED_COST_TABLE.find(e => bareId.
|
|
67
|
+
?? BUNDLED_COST_TABLE.find(e => bareId.startsWith(`${e.id}-`) ||
|
|
68
|
+
bareId.startsWith(`${e.id}:`) ||
|
|
69
|
+
bareId.startsWith(`${e.id}@`));
|
|
67
70
|
}
|
|
68
71
|
/**
|
|
69
72
|
* Compare two models by input cost. Returns negative if a is cheaper.
|
|
@@ -46,7 +46,9 @@ export const MODEL_CAPABILITY_TIER = {
|
|
|
46
46
|
"gpt-5.3-codex": "heavy",
|
|
47
47
|
"gpt-5.4": "heavy",
|
|
48
48
|
"gpt-5.5": "heavy",
|
|
49
|
-
"gpt-5.6": "heavy",
|
|
49
|
+
"gpt-5.6-sol": "heavy",
|
|
50
|
+
"gpt-5.6-terra": "heavy",
|
|
51
|
+
"gpt-5.6-luna": "heavy",
|
|
50
52
|
"o1": "heavy",
|
|
51
53
|
"o3": "heavy",
|
|
52
54
|
"o4-mini": "heavy",
|
|
@@ -84,7 +86,9 @@ const MODEL_COST_PER_1K_INPUT = {
|
|
|
84
86
|
"gpt-5.3-codex-spark": 0.0003,
|
|
85
87
|
"gpt-5.4": 0.005,
|
|
86
88
|
"gpt-5.5": 0.005,
|
|
87
|
-
"gpt-5.6": 0.005,
|
|
89
|
+
"gpt-5.6-sol": 0.005,
|
|
90
|
+
"gpt-5.6-terra": 0.0025,
|
|
91
|
+
"gpt-5.6-luna": 0.001,
|
|
88
92
|
"o4-mini": 0.005,
|
|
89
93
|
"o4-mini-deep-research": 0.005,
|
|
90
94
|
"gemini-2.0-flash": 0.0001,
|
|
@@ -132,9 +136,10 @@ export const MODEL_CAPABILITY_PROFILES = {
|
|
|
132
136
|
// OpenAI's 2026-04-23 published eval deltas across coding, tool use, and long context.
|
|
133
137
|
// Source: https://openai.com/index/introducing-gpt-5-5/
|
|
134
138
|
"gpt-5.5": { coding: 96, debugging: 93, research: 89, reasoning: 95, speed: 42, longContext: 90, instruction: 93 },
|
|
135
|
-
// GPT-5.6
|
|
136
|
-
|
|
137
|
-
"gpt-5.6": { coding:
|
|
139
|
+
// GPT-5.6 profiles are provisional until official eval results are available.
|
|
140
|
+
"gpt-5.6-sol": { coding: 97, debugging: 94, research: 90, reasoning: 96, speed: 42, longContext: 91, instruction: 94 },
|
|
141
|
+
"gpt-5.6-terra": { coding: 96, debugging: 93, research: 89, reasoning: 95, speed: 48, longContext: 91, instruction: 93 },
|
|
142
|
+
"gpt-5.6-luna": { coding: 92, debugging: 88, research: 84, reasoning: 91, speed: 72, longContext: 91, instruction: 90 },
|
|
138
143
|
// ── OpenAI o-series (reasoning-first) ──────────────────────────────────────
|
|
139
144
|
"o1": { coding: 78, debugging: 82, research: 78, reasoning: 90, speed: 20, longContext: 65, instruction: 82 },
|
|
140
145
|
"o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
|
|
@@ -16,10 +16,18 @@ const PLANNING_DISPATCH_AGENT_REGISTRY = {
|
|
|
16
16
|
export const ALLOWED_PLANNING_DISPATCH_AGENTS = new Set(Object.entries(PLANNING_DISPATCH_AGENT_REGISTRY)
|
|
17
17
|
.filter(([, metadata]) => metadata.readOnlySpecialist)
|
|
18
18
|
.map(([agentId]) => agentId));
|
|
19
|
-
|
|
19
|
+
function configuredPlanningDispatchAgents(registry) {
|
|
20
|
+
return Object.entries(registry ?? {})
|
|
21
|
+
.filter(([, metadata]) => metadata.read_only_specialist === true)
|
|
22
|
+
.map(([agentId]) => agentId);
|
|
23
|
+
}
|
|
24
|
+
export function isReadOnlyPlanningDispatchAgent(agentId, registry) {
|
|
20
25
|
const metadata = PLANNING_DISPATCH_AGENT_REGISTRY[agentId];
|
|
21
|
-
return metadata?.readOnlySpecialist === true;
|
|
26
|
+
return metadata?.readOnlySpecialist === true || registry?.[agentId]?.read_only_specialist === true;
|
|
22
27
|
}
|
|
23
|
-
export function allowedPlanningDispatchAgentsList() {
|
|
24
|
-
return
|
|
28
|
+
export function allowedPlanningDispatchAgentsList(registry) {
|
|
29
|
+
return Array.from(new Set([
|
|
30
|
+
...ALLOWED_PLANNING_DISPATCH_AGENTS,
|
|
31
|
+
...configuredPlanningDispatchAgents(registry),
|
|
32
|
+
])).join(", ");
|
|
25
33
|
}
|
|
@@ -8,10 +8,31 @@
|
|
|
8
8
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { join } from "node:path";
|
|
11
|
+
import { getProviders } from "@gsd/pi-ai";
|
|
11
12
|
import { gsdHome } from "./gsd-home.js";
|
|
12
13
|
import { canonicalModelForTier, defaultRoutingConfig, resolveModelForTier } from "./model-router.js";
|
|
13
14
|
import { clearGSDPreferencesCache, loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
|
|
14
15
|
import { getUnitPhaseChain } from "./unit-registry.js";
|
|
16
|
+
/**
|
|
17
|
+
* First-party providers shipped as bundled extensions that delegate to a
|
|
18
|
+
* vendor CLI (`authMode: "externalCli"`). They are registered into the live
|
|
19
|
+
* model registry at runtime, so they are absent from the generated `@gsd/pi-ai`
|
|
20
|
+
* catalog (`getProviders()`), but they are NOT custom: `PREFERENCES.md` can
|
|
21
|
+
* route to them, so they must defer to `PREFERENCES.md` like any other built-in
|
|
22
|
+
* provider. Without this exclusion the registry check below would classify the
|
|
23
|
+
* default `claude-code` provider (and the other CLI providers) as custom and
|
|
24
|
+
* silently skip `PREFERENCES.md` in auto-mode (#801, extends #4122).
|
|
25
|
+
*
|
|
26
|
+
* Kept in sync with the `registerProvider(...)` calls in the bundled CLI
|
|
27
|
+
* extensions (claude-code-cli, cursor-cli, google-cli). Names are lowercase for
|
|
28
|
+
* direct comparison against normalized provider ids.
|
|
29
|
+
*/
|
|
30
|
+
const BUILTIN_EXTENSION_PROVIDERS = new Set([
|
|
31
|
+
"claude-code",
|
|
32
|
+
"cursor-agent",
|
|
33
|
+
"google-gemini-cli",
|
|
34
|
+
"google-antigravity",
|
|
35
|
+
]);
|
|
15
36
|
/**
|
|
16
37
|
* Resolve which model ID to use for a given auto-mode unit type.
|
|
17
38
|
* Returns undefined if no model preference is set for this unit type.
|
|
@@ -281,9 +302,8 @@ export function resolveDefaultSessionModel(sessionProvider, basePath, availableM
|
|
|
281
302
|
return undefined;
|
|
282
303
|
}
|
|
283
304
|
/**
|
|
284
|
-
* Returns true if `provider` is
|
|
285
|
-
*
|
|
286
|
-
* proxies, etc.).
|
|
305
|
+
* Returns true if `provider` is a custom provider. Custom providers may be
|
|
306
|
+
* declared in `models.json` or registered dynamically by a Pi extension.
|
|
287
307
|
*
|
|
288
308
|
* Used by auto-mode bootstrap to decide whether the session model
|
|
289
309
|
* (set via `/gsd model`) should override `PREFERENCES.md`. Custom providers
|
|
@@ -292,13 +312,16 @@ export function resolveDefaultSessionModel(sessionProvider, basePath, availableM
|
|
|
292
312
|
* priority — otherwise auto-mode tries to start the built-in provider from
|
|
293
313
|
* PREFERENCES.md and fails with "Not logged in · Please run /login" (#4122).
|
|
294
314
|
*
|
|
295
|
-
* Reads models.json directly with a lightweight JSON parse
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
315
|
+
* Reads models.json directly with a lightweight JSON parse, then checks the
|
|
316
|
+
* live model registry for extension-registered providers that are not in the
|
|
317
|
+
* generated built-in provider catalog (`getProviders()`) and not one of the
|
|
318
|
+
* bundled external-CLI providers (`BUILTIN_EXTENSION_PROVIDERS`, e.g.
|
|
319
|
+
* `claude-code`), which register at runtime but are still built-in. Falls back
|
|
320
|
+
* to `~/.pi/agent/models.json` for parity with `resolveModelsJsonPath()`. Any
|
|
321
|
+
* read, parse, or registry error yields `false` (treat as not-custom) so
|
|
322
|
+
* bootstrap stays non-fatal.
|
|
300
323
|
*/
|
|
301
|
-
export function isCustomProvider(provider) {
|
|
324
|
+
export function isCustomProvider(provider, registry) {
|
|
302
325
|
if (!provider)
|
|
303
326
|
return false;
|
|
304
327
|
const candidates = [
|
|
@@ -319,7 +342,24 @@ export function isCustomProvider(provider) {
|
|
|
319
342
|
// Ignore — malformed models.json must not break bootstrap.
|
|
320
343
|
}
|
|
321
344
|
}
|
|
322
|
-
|
|
345
|
+
try {
|
|
346
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
347
|
+
if (!normalizedProvider)
|
|
348
|
+
return false;
|
|
349
|
+
if (BUILTIN_EXTENSION_PROVIDERS.has(normalizedProvider))
|
|
350
|
+
return false;
|
|
351
|
+
const builtInProviders = new Set(getProviders().map((p) => p.toLowerCase()));
|
|
352
|
+
if (builtInProviders.has(normalizedProvider))
|
|
353
|
+
return false;
|
|
354
|
+
const registryModels = registry?.getAllWithDiscovered?.()
|
|
355
|
+
?? registry?.getAll?.()
|
|
356
|
+
?? registry?.getAvailable?.()
|
|
357
|
+
?? [];
|
|
358
|
+
return registryModels.some((m) => m.provider.toLowerCase() === normalizedProvider);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
323
363
|
}
|
|
324
364
|
/**
|
|
325
365
|
* Determines the next fallback model to try when the current model fails.
|
|
@@ -10,7 +10,7 @@ import { VALID_BRANCH_NAME } from "./git-service.js";
|
|
|
10
10
|
import { normalizeStringArray } from "../shared/format-utils.js";
|
|
11
11
|
import { getGateIdsForTurn } from "./gate-registry.js";
|
|
12
12
|
import { KNOWN_PREFERENCE_KEYS, KNOWN_UNIT_LABELS, CONFIGURABLE_PLANNING_SUBAGENT_UNITS, GSD_MODEL_PHASE_KEYS, SKILL_ACTIONS, } from "./preferences-types.js";
|
|
13
|
-
import {
|
|
13
|
+
import { allowedPlanningDispatchAgentsList, isReadOnlyPlanningDispatchAgent, } from "./planning-subagent-registry.js";
|
|
14
14
|
const VALID_TOKEN_PROFILES = new Set(["budget", "balanced", "quality", "burn-max"]);
|
|
15
15
|
const VALID_THINKING_LEVELS = new Set([
|
|
16
16
|
"off",
|
|
@@ -19,6 +19,7 @@ const VALID_THINKING_LEVELS = new Set([
|
|
|
19
19
|
"medium",
|
|
20
20
|
"high",
|
|
21
21
|
"xhigh",
|
|
22
|
+
"max",
|
|
22
23
|
]);
|
|
23
24
|
const KNOWN_MODEL_PHASE_KEYS = new Set(GSD_MODEL_PHASE_KEYS);
|
|
24
25
|
const VALID_UOK_TURN_ACTIONS = new Set([
|
|
@@ -69,7 +70,7 @@ function sanitizeModelField(raw, label, errors, warnings) {
|
|
|
69
70
|
}
|
|
70
71
|
else {
|
|
71
72
|
warnings?.push(`${label} thinking "${String(obj.thinking)}" is not a valid thinking level ` +
|
|
72
|
-
`(off, minimal, low, medium, high, xhigh) — ignored`);
|
|
73
|
+
`(off, minimal, low, medium, high, xhigh, max) — ignored`);
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
return sanitized;
|
|
@@ -457,7 +458,7 @@ export function validatePreferences(preferences) {
|
|
|
457
458
|
const level = entry.thinking;
|
|
458
459
|
if (level !== undefined && !VALID_THINKING_LEVELS.has(level)) {
|
|
459
460
|
warnings.push(`models.${phase}.thinking "${String(level)}" is not a valid thinking level ` +
|
|
460
|
-
`(off, minimal, low, medium, high, xhigh) — ignored`);
|
|
461
|
+
`(off, minimal, low, medium, high, xhigh, max) — ignored`);
|
|
461
462
|
const { thinking: _ignored, ...rest } = entry;
|
|
462
463
|
// If stripping the bad thinking leaves no usable model, drop the
|
|
463
464
|
// phase entirely rather than storing a hollow `{}` / `{ provider }`
|
|
@@ -488,7 +489,7 @@ export function validatePreferences(preferences) {
|
|
|
488
489
|
}
|
|
489
490
|
if (!VALID_THINKING_LEVELS.has(level)) {
|
|
490
491
|
warnings.push(`thinking.${phase} "${String(level)}" is not a valid thinking level ` +
|
|
491
|
-
`(off, minimal, low, medium, high, xhigh) — ignored`);
|
|
492
|
+
`(off, minimal, low, medium, high, xhigh, max) — ignored`);
|
|
492
493
|
continue;
|
|
493
494
|
}
|
|
494
495
|
validatedThinking[phase] = level;
|
|
@@ -728,6 +729,38 @@ export function validatePreferences(preferences) {
|
|
|
728
729
|
validated.pre_dispatch_hooks = validPreHooks;
|
|
729
730
|
}
|
|
730
731
|
}
|
|
732
|
+
// ─── Planning Subagent Registry ──────────────────────────────────────────
|
|
733
|
+
if (preferences.planning_subagent_registry !== undefined) {
|
|
734
|
+
if (!preferences.planning_subagent_registry ||
|
|
735
|
+
typeof preferences.planning_subagent_registry !== "object" ||
|
|
736
|
+
Array.isArray(preferences.planning_subagent_registry)) {
|
|
737
|
+
errors.push("planning_subagent_registry must be an object keyed by agent id");
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
const validRegistry = {};
|
|
741
|
+
for (const [rawAgentId, rawConfig] of Object.entries(preferences.planning_subagent_registry)) {
|
|
742
|
+
const agentId = rawAgentId.trim();
|
|
743
|
+
if (!agentId) {
|
|
744
|
+
errors.push("planning_subagent_registry includes an empty agent id");
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
748
|
+
errors.push(`planning_subagent_registry.${agentId} must be an object with read_only_specialist: true`);
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
const readOnlySpecialist = rawConfig.read_only_specialist;
|
|
752
|
+
if (readOnlySpecialist !== true) {
|
|
753
|
+
errors.push(`planning_subagent_registry.${agentId}.read_only_specialist must be true to classify ` +
|
|
754
|
+
`"${agentId}" as a read-only planning specialist`);
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
validRegistry[agentId] = { read_only_specialist: true };
|
|
758
|
+
}
|
|
759
|
+
if (Object.keys(validRegistry).length > 0) {
|
|
760
|
+
validated.planning_subagent_registry = validRegistry;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
731
764
|
// ─── Planning Subagents ─────────────────────────────────────────────────
|
|
732
765
|
if (preferences.planning_subagents !== undefined) {
|
|
733
766
|
if (!preferences.planning_subagents ||
|
|
@@ -755,9 +788,11 @@ export function validatePreferences(preferences) {
|
|
|
755
788
|
}
|
|
756
789
|
const sanitizedAllowed = [];
|
|
757
790
|
for (const agentId of allowed) {
|
|
758
|
-
if (!
|
|
791
|
+
if (!isReadOnlyPlanningDispatchAgent(agentId, validated.planning_subagent_registry)) {
|
|
759
792
|
errors.push(`planning_subagents.${unitType}.allowed includes "${agentId}", which is not classified as a ` +
|
|
760
|
-
`read-only planning specialist. Allowed agents:
|
|
793
|
+
`read-only planning specialist. Allowed agents: ` +
|
|
794
|
+
`${allowedPlanningDispatchAgentsList(validated.planning_subagent_registry)}. ` +
|
|
795
|
+
`For custom planning agents, add planning_subagent_registry.${agentId}.read_only_specialist: true`);
|
|
761
796
|
continue;
|
|
762
797
|
}
|
|
763
798
|
sanitizedAllowed.push(agentId);
|
|
@@ -635,6 +635,7 @@ function mergePreferences(base, override) {
|
|
|
635
635
|
: undefined,
|
|
636
636
|
post_unit_hooks: mergePostUnitHooks(base.post_unit_hooks, override.post_unit_hooks),
|
|
637
637
|
pre_dispatch_hooks: mergePreDispatchHooks(base.pre_dispatch_hooks, override.pre_dispatch_hooks),
|
|
638
|
+
planning_subagent_registry: mergePlanningSubagentRegistry(base.planning_subagent_registry, override.planning_subagent_registry),
|
|
638
639
|
planning_subagents: mergePlanningSubagents(base.planning_subagents, override.planning_subagents),
|
|
639
640
|
dynamic_routing: (base.dynamic_routing || override.dynamic_routing)
|
|
640
641
|
? { ...(base.dynamic_routing ?? {}), ...(override.dynamic_routing ?? {}) }
|
|
@@ -769,6 +770,12 @@ function mergePlanningSubagents(base, override) {
|
|
|
769
770
|
}
|
|
770
771
|
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
771
772
|
}
|
|
773
|
+
function mergePlanningSubagentRegistry(base, override) {
|
|
774
|
+
if (!base && !override)
|
|
775
|
+
return undefined;
|
|
776
|
+
const merged = { ...(base ?? {}), ...(override ?? {}) };
|
|
777
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
778
|
+
}
|
|
772
779
|
// ─── System Prompt Rendering ──────────────────────────────────────────────────
|
|
773
780
|
export function renderPreferencesForSystemPrompt(preferences, resolutions, options) {
|
|
774
781
|
const validated = validatePreferences(preferences);
|
|
@@ -92,6 +92,9 @@ remote_questions:
|
|
|
92
92
|
uat_dispatch:
|
|
93
93
|
post_unit_hooks: []
|
|
94
94
|
pre_dispatch_hooks: []
|
|
95
|
+
# planning_subagent_registry:
|
|
96
|
+
# my-custom-planner:
|
|
97
|
+
# read_only_specialist: true
|
|
95
98
|
# planning_subagents:
|
|
96
99
|
# plan-milestone:
|
|
97
100
|
# allowed: [scout, planner, security]
|
|
@@ -6,7 +6,7 @@ import { isNonEmptyString, validateStringArray } from "../validation.js";
|
|
|
6
6
|
import { getGateIdsForTurn } from "../gate-registry.js";
|
|
7
7
|
import { transaction, getMilestone, getSlice, getSliceTasks, insertTask, upsertSlicePlanning, upsertTaskPlanning, insertGateRow, updateSliceStatus, setSliceSketchFlag, deleteTask, deleteArtifactByPath, } from "../gsd-db.js";
|
|
8
8
|
import { invalidateStateCache } from "../state.js";
|
|
9
|
-
import { renderPlanFromDb } from "../markdown-renderer.js";
|
|
9
|
+
import { renderPlanCheckboxes, renderPlanFromDb } from "../markdown-renderer.js";
|
|
10
10
|
import { writeManifest } from "../workflow-manifest.js";
|
|
11
11
|
import { appendEvent } from "../workflow-events.js";
|
|
12
12
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -371,9 +371,13 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
371
371
|
deleteArtifactByPath(artifactPath);
|
|
372
372
|
}
|
|
373
373
|
const sliceTasks = getSliceTasks(params.milestoneId, params.sliceId);
|
|
374
|
+
const hasClosedTasks = sliceTasks.some((task) => isClosedStatus(task.status));
|
|
374
375
|
const renderResult = sliceTasks.length === 0
|
|
375
376
|
? { planPath: "", taskPlanPaths: [] }
|
|
376
377
|
: await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
|
|
378
|
+
if (sliceTasks.length > 0 && hasClosedTasks) {
|
|
379
|
+
await renderPlanCheckboxes(basePath, params.milestoneId, params.sliceId);
|
|
380
|
+
}
|
|
377
381
|
if (sliceTasks.length > 0) {
|
|
378
382
|
setSliceSketchFlag(params.milestoneId, params.sliceId, false);
|
|
379
383
|
}
|
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
/**
|
|
14
|
-
* Write a T##-VERIFY.json artifact to the
|
|
14
|
+
* Write a T##-VERIFY.json artifact to the evidence directory.
|
|
15
15
|
* Creates the directory with mkdirSync({ recursive: true }) if it doesn't exist.
|
|
16
|
+
* Flat-phase callers can pass sliceId to write S##-T##-VERIFY.json.
|
|
16
17
|
*
|
|
17
18
|
* stdout/stderr are excluded from the JSON — the full output lives in VerificationResult
|
|
18
19
|
* in memory and is logged to stderr during the gate run.
|
|
19
20
|
*/
|
|
20
|
-
export function writeVerificationJSON(result, tasksDir, taskId, unitId, retryAttempt, maxRetries) {
|
|
21
|
+
export function writeVerificationJSON(result, tasksDir, taskId, unitId, retryAttempt, maxRetries, sliceId) {
|
|
21
22
|
mkdirSync(tasksDir, { recursive: true });
|
|
22
23
|
const evidence = {
|
|
23
24
|
schemaVersion: 1,
|
|
@@ -52,7 +53,8 @@ export function writeVerificationJSON(result, tasksDir, taskId, unitId, retryAtt
|
|
|
52
53
|
fixAvailable: w.fixAvailable,
|
|
53
54
|
}));
|
|
54
55
|
}
|
|
55
|
-
const
|
|
56
|
+
const fileName = sliceId ? `${sliceId}-${taskId}-VERIFY.json` : `${taskId}-VERIFY.json`;
|
|
57
|
+
const filePath = join(tasksDir, fileName);
|
|
56
58
|
writeFileSync(filePath, JSON.stringify(evidence, null, 2) + "\n", "utf-8");
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, unlinkSync, } from "node:fs";
|
|
22
22
|
import { join } from "node:path";
|
|
23
23
|
import { reconcileWorktreeDb } from "./gsd-db.js";
|
|
24
|
-
import { resolveGsdPathContract } from "./paths.js";
|
|
24
|
+
import { dirIsContentBearingLegacyMilestone, resolveGsdPathContract } from "./paths.js";
|
|
25
25
|
import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
|
|
26
26
|
import { logError, logWarning } from "./workflow-logger.js";
|
|
27
27
|
// ─── Private helpers ──────────────────────────────────────────────────────
|
|
@@ -129,6 +129,8 @@ function syncOtherMilestoneArtifacts(srcMilestonesDir, dstMilestonesDir, current
|
|
|
129
129
|
if (milestoneEntry.name === currentMilestoneId)
|
|
130
130
|
continue;
|
|
131
131
|
const srcMilestoneDir = join(srcMilestonesDir, milestoneEntry.name);
|
|
132
|
+
if (!dirIsContentBearingLegacyMilestone(srcMilestoneDir))
|
|
133
|
+
continue;
|
|
132
134
|
const dstMilestoneDir = join(dstMilestonesDir, milestoneEntry.name);
|
|
133
135
|
// Additively project the entire milestone subtree (force:false), not just
|
|
134
136
|
// top-level files. Prior completed milestones keep their per-slice and
|
|
@@ -219,25 +221,25 @@ export function _projectRootToWorktreeImpl(projectRoot, worktreePath_, milestone
|
|
|
219
221
|
// Without this, worktree-local files (e.g. VALIDATION.md written
|
|
220
222
|
// by validate-milestone) get clobbered by stale project root copies,
|
|
221
223
|
// causing an infinite re-validation loop (#1886).
|
|
222
|
-
safeCopyRecursive(join(prGsd, "milestones", milestoneId), join(wtGsd, "milestones", milestoneId), { force: false });
|
|
223
|
-
// Additively project the full subtree of every OTHER milestone so
|
|
224
|
-
// worktree-bound units can read prior-milestone context artifacts and so
|
|
225
|
-
// completed milestones retain their per-slice/per-task SUMMARY.md and
|
|
226
|
-
// UAT.md on the worktree filesystem (otherwise the stale-render detector
|
|
227
|
-
// flags them as "complete in DB but missing on disk").
|
|
228
|
-
syncOtherMilestoneArtifacts(join(prGsd, "milestones"), join(wtGsd, "milestones"), milestoneId);
|
|
229
|
-
// Force-sync ASSESSMENT files that have a verdict from project root (#2821).
|
|
230
|
-
// The additive-only copy above preserves worktree-local files, but
|
|
231
|
-
// ASSESSMENT files are special: after run-uat writes a verdict and post-unit
|
|
232
|
-
// syncs it to the project root, the worktree may retain a stale copy (e.g.
|
|
233
|
-
// verdict:fail while the project root has verdict:pass from a retry). On
|
|
234
|
-
// session resume the DB is rebuilt from disk, and if the stale ASSESSMENT
|
|
235
|
-
// persists, checkNeedsRunUat finds no passing verdict → re-dispatches
|
|
236
|
-
// run-uat indefinitely (stuck-loop ×9).
|
|
237
224
|
const prMilestoneDir = join(prGsd, "milestones", milestoneId);
|
|
238
225
|
const wtMilestoneDir = join(wtGsd, "milestones", milestoneId);
|
|
239
|
-
|
|
240
|
-
|
|
226
|
+
if (dirIsContentBearingLegacyMilestone(prMilestoneDir)) {
|
|
227
|
+
safeCopyRecursive(prMilestoneDir, wtMilestoneDir, { force: false });
|
|
228
|
+
// Force-sync ASSESSMENT files that have a verdict from project root (#2821).
|
|
229
|
+
// The additive-only copy above preserves worktree-local files, but
|
|
230
|
+
// ASSESSMENT files are special: after run-uat writes a verdict and post-unit
|
|
231
|
+
// syncs it to the project root, the worktree may retain a stale copy (e.g.
|
|
232
|
+
// verdict:fail while the project root has verdict:pass from a retry). On
|
|
233
|
+
// session resume the DB is rebuilt from disk, and if the stale ASSESSMENT
|
|
234
|
+
// persists, checkNeedsRunUat finds no passing verdict → re-dispatches
|
|
235
|
+
// run-uat indefinitely (stuck-loop ×9).
|
|
236
|
+
forceOverwriteValidationWithVerdict(prMilestoneDir, wtMilestoneDir, milestoneId);
|
|
237
|
+
forceOverwriteAssessmentsWithVerdict(prMilestoneDir, wtMilestoneDir);
|
|
238
|
+
}
|
|
239
|
+
// Additively project the full subtree of every OTHER content-bearing legacy
|
|
240
|
+
// milestone so worktree-bound units can read prior-milestone context artifacts
|
|
241
|
+
// without recreating empty/meta-only milestones/ scaffolds in flat-phase projects.
|
|
242
|
+
syncOtherMilestoneArtifacts(join(prGsd, "milestones"), join(wtGsd, "milestones"), milestoneId);
|
|
241
243
|
// Forward-sync completed-units.json from project root to worktree.
|
|
242
244
|
// Project root is authoritative for completion state after crash recovery;
|
|
243
245
|
// without this, the worktree re-dispatches already-completed units (#1886).
|
|
@@ -160,7 +160,7 @@ export function streamOllamaChat(model, context, options) {
|
|
|
160
160
|
* - `"low" | "medium" | "high"` are passed through as strings; gpt-oss honors
|
|
161
161
|
* them as strength levels, other thinking models treat any present value
|
|
162
162
|
* as "thinking enabled".
|
|
163
|
-
* - `"xhigh"`
|
|
163
|
+
* - `"xhigh"` and `"max"` are collapsed to `"high"` (ollama caps strength at high).
|
|
164
164
|
*/
|
|
165
165
|
export function buildThinkParam(model, options) {
|
|
166
166
|
if (!model.reasoning)
|
|
@@ -170,7 +170,7 @@ export function buildThinkParam(model, options) {
|
|
|
170
170
|
return undefined;
|
|
171
171
|
if (level === "minimal")
|
|
172
172
|
return false;
|
|
173
|
-
if (level === "xhigh")
|
|
173
|
+
if (level === "xhigh" || level === "max")
|
|
174
174
|
return "high";
|
|
175
175
|
return level;
|
|
176
176
|
}
|
|
@@ -591,7 +591,7 @@ thinkingOverride) {
|
|
|
591
591
|
}
|
|
592
592
|
}
|
|
593
593
|
}
|
|
594
|
-
const ThinkingLevelSchema = StringEnum(["off", "minimal", "low", "medium", "high", "xhigh"], {
|
|
594
|
+
const ThinkingLevelSchema = StringEnum(["off", "minimal", "low", "medium", "high", "xhigh", "max"], {
|
|
595
595
|
description: "Reasoning effort override for the subagent (forwarded as --thinking). " +
|
|
596
596
|
"Independent of model choice; unsupported levels are clamped to the model at dispatch.",
|
|
597
597
|
});
|
|
@@ -15,7 +15,7 @@ interface MinimalModel {
|
|
|
15
15
|
interface MinimalModelRegistry {
|
|
16
16
|
getAvailable(): MinimalModel[];
|
|
17
17
|
}
|
|
18
|
-
type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
18
|
+
type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
19
19
|
interface MinimalSettingsManager {
|
|
20
20
|
getDefaultProvider(): string | undefined;
|
|
21
21
|
getDefaultModel(): string | undefined;
|