@opengsd/gsd-pi 1.1.1-dev.74e8dd1 → 1.1.1-dev.9bb7453
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +3 -2
- package/dist/help-text.js +10 -6
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
- package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
- package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
- package/dist/resources/extensions/browser-tools/index.js +57 -9
- package/dist/resources/extensions/browser-tools/package.json +5 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +77 -13
- package/dist/resources/extensions/gsd/auto-dispatch.js +5 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +59 -22
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +9 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -4
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -5
- package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
- package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
- package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
- package/dist/resources/extensions/gsd/escalation.js +4 -4
- package/dist/resources/extensions/gsd/forensics.js +74 -2
- package/dist/resources/extensions/gsd/gsd-db.js +5 -2
- package/dist/resources/extensions/gsd/guided-flow.js +29 -68
- package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
- package/dist/resources/extensions/gsd/memory-store.js +4 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
- package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +40 -22
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- package/dist/resources/extensions/gsd/rule-registry.js +428 -52
- package/dist/resources/extensions/gsd/state.js +2 -2
- package/dist/resources/extensions/gsd/templates/plan.md +3 -1
- package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +51 -14
- package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
- package/dist/resources/extensions/gsd/verification-gate.js +72 -1
- package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
- package/dist/rtk.d.ts +7 -1
- package/dist/rtk.js +27 -11
- package/dist/update-check.d.ts +15 -1
- package/dist/update-check.js +87 -12
- package/dist/update-cmd.d.ts +1 -0
- package/dist/update-cmd.js +53 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
- 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/api/update/route.js +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 +7 -7
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- 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/package.json +4 -2
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
- package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/agent-session.js +32 -0
- package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
- package/packages/gsd-agent-core/dist/index.d.ts +1 -0
- package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/index.js +1 -0
- package/packages/gsd-agent-core/dist/index.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
- package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
- package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
- package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
- package/packages/gsd-agent-core/package.json +6 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
- package/packages/mcp-server/dist/remote-questions.js +23 -9
- package/packages/mcp-server/dist/remote-questions.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +2 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +38 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +5 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +3 -0
- 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/api-registry.d.ts +2 -0
- package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
- package/packages/pi-ai/dist/api-registry.js +23 -0
- package/packages/pi-ai/dist/api-registry.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +68 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +72 -4
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/stream.js +6 -6
- package/packages/pi-ai/dist/stream.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 +2 -2
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
- package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
- package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
- package/src/resources/extensions/browser-tools/index.ts +60 -9
- package/src/resources/extensions/browser-tools/package.json +5 -1
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
- package/src/resources/extensions/gsd/auto-dispatch.ts +5 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +93 -15
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +12 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -4
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +9 -5
- package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
- package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
- package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
- package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
- package/src/resources/extensions/gsd/escalation.ts +4 -4
- package/src/resources/extensions/gsd/forensics.ts +99 -5
- package/src/resources/extensions/gsd/gsd-db.ts +5 -2
- package/src/resources/extensions/gsd/guided-flow.ts +90 -82
- package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
- package/src/resources/extensions/gsd/memory-store.ts +4 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +40 -22
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- package/src/resources/extensions/gsd/rule-registry.ts +558 -58
- package/src/resources/extensions/gsd/rule-types.ts +2 -0
- package/src/resources/extensions/gsd/state.ts +2 -2
- package/src/resources/extensions/gsd/templates/plan.md +3 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
- package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
- package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
- package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +130 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
- package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +63 -15
- package/src/resources/extensions/gsd/types.ts +69 -5
- package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
- package/src/resources/extensions/gsd/verification-gate.ts +87 -1
- package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
- /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → jBtwT9v1u2lUA3UEOy_ZH}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → jBtwT9v1u2lUA3UEOy_ZH}/_ssgManifest.js +0 -0
|
@@ -832,7 +832,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
832
832
|
recommendation: Type.String({ description: "Option id the executor recommends." }),
|
|
833
833
|
recommendationRationale: Type.String({ description: "Why the recommendation — 1–2 sentences." }),
|
|
834
834
|
continueWithDefault: Type.Boolean({
|
|
835
|
-
description: "When true,
|
|
835
|
+
description: "When true, the recommendation is recorded as the default, but auto-mode still pauses until the user resolves via /gsd escalate resolve.",
|
|
836
836
|
}),
|
|
837
837
|
}, { description: "ADR-011 Phase 2: optional escalation payload. Only honored when phases.mid_execution_escalation is true." })),
|
|
838
838
|
verificationEvidence: Type.Optional(Type.Array(
|
|
@@ -881,7 +881,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
881
881
|
sliceTitle: Type.String({ description: "Title of the slice" }),
|
|
882
882
|
oneLiner: Type.String({ description: "One-line summary of what the slice accomplished" }),
|
|
883
883
|
narrative: Type.String({ description: "Detailed narrative of what happened across all tasks" }),
|
|
884
|
-
verification: Type.String({ description: "What was verified across all tasks" }),
|
|
884
|
+
verification: Type.Optional(Type.String({ description: "What was verified across all tasks — if omitted, summary records verification as passed without detail." })),
|
|
885
885
|
uatContent: Type.String({ description: "UAT test content (markdown body)" }),
|
|
886
886
|
// ── Enrichment metadata (optional — defaults to empty) ────────────
|
|
887
887
|
deviations: Type.Optional(Type.String({ description: "Deviations from the slice plan, or 'None.'" })),
|
|
@@ -1095,7 +1095,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
1095
1095
|
promptGuidelines: [
|
|
1096
1096
|
"Use gsd_validate_milestone when all slices are done and the milestone needs validation before completion.",
|
|
1097
1097
|
"Parameters: milestoneId, verdict, remediationRound, successCriteriaChecklist, sliceDeliveryAudit, crossSliceIntegration, requirementCoverage, verificationClasses (optional), verdictRationale, remediationPlan (optional).",
|
|
1098
|
-
"If verification classes were planned, verificationClasses must
|
|
1098
|
+
"If verification classes were planned, verificationClasses must be a complete canonical table with one row for every applicable planned class using the exact class names Contract, Integration, Operational, and UAT. Do not submit a partial table.",
|
|
1099
1099
|
"Planned verification text marked as none/not required/not applicable/N/A (including suffixed variants such as 'not required - backend-only') is treated as not applicable and does not require a class row.",
|
|
1100
1100
|
"If verdict is 'needs-remediation', also provide remediationPlan and use gsd_reassess_roadmap to add remediation slices to the roadmap.",
|
|
1101
1101
|
"On success, returns validationPath where VALIDATION.md was written.",
|
|
@@ -1108,7 +1108,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
1108
1108
|
sliceDeliveryAudit: Type.String({ description: "Markdown table auditing each slice's claimed vs delivered output" }),
|
|
1109
1109
|
crossSliceIntegration: Type.String({ description: "Markdown describing any cross-slice boundary mismatches" }),
|
|
1110
1110
|
requirementCoverage: Type.String({ description: "Markdown describing any unaddressed requirements" }),
|
|
1111
|
-
verificationClasses: Type.Optional(Type.String({ description: "
|
|
1111
|
+
verificationClasses: Type.Optional(Type.String({ description: "Complete markdown table describing verification class compliance and gaps; include one canonical row for every applicable planned class (Contract, Integration, Operational, UAT)" })),
|
|
1112
1112
|
verdictRationale: Type.String({ description: "Why this verdict was chosen" }),
|
|
1113
1113
|
remediationPlan: Type.Optional(Type.String({ description: "Remediation plan (required if verdict is needs-remediation)" })),
|
|
1114
1114
|
}),
|
|
@@ -17,7 +17,7 @@ import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer,
|
|
|
17
17
|
import { resolveManifest } from "../unit-context-manifest.js";
|
|
18
18
|
import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
|
|
19
19
|
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
20
|
-
import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
|
|
20
|
+
import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoCompletionStopInProgress, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
|
|
21
21
|
|
|
22
22
|
import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
|
|
23
23
|
import { maybePauseAutoForApprovalGate, resetPendingGatePauseGuard } from "./pending-gate-pause.js";
|
|
@@ -535,8 +535,9 @@ export function registerHooks(
|
|
|
535
535
|
|
|
536
536
|
pi.on("session_start", async (_event, ctx) => {
|
|
537
537
|
const basePath = contextBasePath(ctx);
|
|
538
|
+
const preserveCloseoutSurface = isAutoCompletionStopInProgress();
|
|
538
539
|
initSessionNotifications(ctx);
|
|
539
|
-
if (!isAutoActive()) {
|
|
540
|
+
if (!isAutoActive() && !preserveCloseoutSurface) {
|
|
540
541
|
const { initHealthWidget } = await import("../health-widget.js");
|
|
541
542
|
initHealthWidget(ctx);
|
|
542
543
|
}
|
|
@@ -556,15 +557,18 @@ export function registerHooks(
|
|
|
556
557
|
const prefs = loadEffectiveGSDPreferences(basePath);
|
|
557
558
|
process.env.GSD_SHOW_TOKEN_COST = prefs?.preferences.show_token_cost ? "1" : "";
|
|
558
559
|
} catch { /* non-fatal */ }
|
|
559
|
-
|
|
560
|
+
if (!preserveCloseoutSurface) {
|
|
561
|
+
await installWelcomeHeader(ctx);
|
|
562
|
+
}
|
|
560
563
|
await loadToolApiKeysForSession();
|
|
561
|
-
if (isAutoActive()) {
|
|
564
|
+
if (isAutoActive() || preserveCloseoutSurface) {
|
|
562
565
|
ctx.ui.setWidget("gsd-health", undefined);
|
|
563
566
|
}
|
|
564
567
|
});
|
|
565
568
|
|
|
566
569
|
pi.on("session_switch", async (_event, ctx) => {
|
|
567
570
|
const basePath = contextBasePath(ctx);
|
|
571
|
+
const preserveCloseoutSurface = isAutoCompletionStopInProgress();
|
|
568
572
|
initSessionNotifications(ctx);
|
|
569
573
|
resetWriteGateState(basePath);
|
|
570
574
|
resetToolCallLoopGuard();
|
|
@@ -576,7 +580,7 @@ export function registerHooks(
|
|
|
576
580
|
await applyCompactionThresholdOverride(ctx);
|
|
577
581
|
await prepareWorkflowMcpForHookContext(ctx, basePath);
|
|
578
582
|
await loadToolApiKeysForSession();
|
|
579
|
-
if (!isAutoActive()) {
|
|
583
|
+
if (!isAutoActive() && !preserveCloseoutSurface) {
|
|
580
584
|
ctx.ui.setWidget("gsd-progress", undefined);
|
|
581
585
|
ctx.ui.setWidget("gsd-outcome", undefined);
|
|
582
586
|
const { initHealthWidget } = await import("../health-widget.js");
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
2
|
// File Purpose: Shared browser-observable UAT requirement and evidence detection.
|
|
3
3
|
|
|
4
|
-
export const BROWSER_REQUIREMENT_RE = /\b(?:
|
|
4
|
+
export const BROWSER_REQUIREMENT_RE = /\b(?:file:\/\/|localhost|playwright|chrome|screenshot|snapshot|browser_(?:assert|batch|find|verify|snapshot_refs))\b|\b(?:open|launch|navigate|load|visit|serve|start)\b.{0,80}\b(?:browser|page|localhost|file:\/\/)\b|\bbrowser\s+(?:check|session|test|uat|tool|automation|interaction|flow)\b/i;
|
|
5
5
|
export const NO_BROWSER_EVIDENCE_RE = /\b(?:no|without|not|wasn'?t|isn'?t)\s+(?:automated\s+)?(?:live\s+)?browser(?:\s+(?:session|test|uat))?|\bno\s+automated\s+browser\b|\bnot\s+conducted\b/i;
|
|
6
6
|
export const BROWSER_RUNTIME_RE = /\b(?:browser|playwright|chrome|camoufox|browser_(?:assert|batch|find|verify|snapshot_refs)|screenshot|snapshot|file:\/\/|localhost)\b/i;
|
|
7
7
|
export const BROWSER_ACTION_RE = /\b(?:open(?:ed)?|navigate(?:d)?|click(?:ed)?|type(?:d)?|reload(?:ed)?|capture(?:d)?|screenshot|snapshot)\b/i;
|
|
8
8
|
export const BROWSER_ASSERTION_RE = /\b(?:assert(?:ed|ion)?|observed|confirmed|verified|expected|visible|text|count|label|strikethrough|localstorage|screenshot|snapshot|passed)\b/i;
|
|
9
|
+
const NON_REQUIREMENT_BROWSER_HEADING_RE = /^(?:not\s+proven|not\s+covered|out\s+of\s+scope|deferred|follow-?ups?|known\s+limitations|notes\s+for\s+tester)\b/i;
|
|
10
|
+
const NON_REQUIREMENT_BROWSER_LINE_RE = /\b(?:deferred|not\s+proven|not\s+covered|out\s+of\s+scope|future\s+slice|follow-?up|no\s+(?:live\s+)?browser|without\s+(?:a\s+)?browser|not\s+(?:a\s+)?browser)\b/i;
|
|
9
11
|
|
|
10
12
|
export function compactTextParts(parts: Array<string | string[] | null | undefined>): string {
|
|
11
13
|
return parts.flatMap((part) => Array.isArray(part) ? part : [part])
|
|
@@ -14,7 +16,29 @@ export function compactTextParts(parts: Array<string | string[] | null | undefin
|
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function hasBrowserRequiredText(text: string): boolean {
|
|
17
|
-
|
|
19
|
+
let inNonRequirementSection = false;
|
|
20
|
+
let nonRequirementDepth = 0;
|
|
21
|
+
for (const line of text.split(/\r?\n/)) {
|
|
22
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+?)\s*$/);
|
|
23
|
+
if (headingMatch) {
|
|
24
|
+
const depth = headingMatch[1]!.length;
|
|
25
|
+
const title = headingMatch[2] ?? "";
|
|
26
|
+
// Only update section context when at the same or higher level than the
|
|
27
|
+
// heading that opened the non-requirement zone. A sub-heading deeper than
|
|
28
|
+
// the opening heading must not escape or re-enter the zone on its own.
|
|
29
|
+
if (!inNonRequirementSection || depth <= nonRequirementDepth) {
|
|
30
|
+
inNonRequirementSection = NON_REQUIREMENT_BROWSER_HEADING_RE.test(title);
|
|
31
|
+
nonRequirementDepth = inNonRequirementSection ? depth : 0;
|
|
32
|
+
}
|
|
33
|
+
// Check the heading title itself — section state is already updated, so
|
|
34
|
+
// we correctly skip headings that opened a non-requirement zone.
|
|
35
|
+
if (!inNonRequirementSection && BROWSER_REQUIREMENT_RE.test(title)) return true;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (inNonRequirementSection || NON_REQUIREMENT_BROWSER_LINE_RE.test(line)) continue;
|
|
39
|
+
if (BROWSER_REQUIREMENT_RE.test(line)) return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
18
42
|
}
|
|
19
43
|
|
|
20
44
|
export function hasBrowserEvidenceText(text: string): boolean {
|
|
@@ -282,8 +282,8 @@ Examples:
|
|
|
282
282
|
await handleInspect(ctx);
|
|
283
283
|
return true;
|
|
284
284
|
}
|
|
285
|
-
if (trimmed === "update" || trimmed === "upgrade") {
|
|
286
|
-
await handleUpdate(ctx);
|
|
285
|
+
if (trimmed === "update" || trimmed.startsWith("update ") || trimmed === "upgrade" || trimmed.startsWith("upgrade ")) {
|
|
286
|
+
await handleUpdate(ctx, trimmed.replace(/^(?:update|upgrade)\s*/, "").trim());
|
|
287
287
|
return true;
|
|
288
288
|
}
|
|
289
289
|
if (trimmed === "fast" || trimmed.startsWith("fast ")) {
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
9
9
|
import { existsSync, readFileSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { execFileSync } from "node:child_process";
|
|
11
|
+
import { createRequire } from "node:module";
|
|
10
12
|
import { join, resolve as resolvePath, sep } from "node:path";
|
|
11
13
|
import { homedir } from "node:os";
|
|
12
14
|
import { deriveState } from "./state.js";
|
|
@@ -37,7 +39,10 @@ import {
|
|
|
37
39
|
scopeGsdWorkflowToolsForDispatch,
|
|
38
40
|
} from "./bootstrap/register-hooks.js";
|
|
39
41
|
|
|
42
|
+
const GSD_PI_PACKAGE = "@opengsd/gsd-pi";
|
|
43
|
+
const GSD_BROWSER_PACKAGE = "@opengsd/gsd-browser";
|
|
40
44
|
const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/@opengsd%2fgsd-pi/latest";
|
|
45
|
+
const BROWSER_UPDATE_REGISTRY_URL = "https://registry.npmjs.org/@opengsd%2fgsd-browser/latest";
|
|
41
46
|
const UPDATE_FETCH_TIMEOUT_MS = 5000;
|
|
42
47
|
|
|
43
48
|
// Detects a bun-installed gsd via `process.argv[1]`. Mirrors isBunInstall in
|
|
@@ -62,12 +67,12 @@ function resolveInstallCommand(pkg: string): string {
|
|
|
62
67
|
return `npm install -g ${pkg}`;
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
async function fetchLatestVersionForCommand(): Promise<string | null> {
|
|
70
|
+
async function fetchLatestVersionForCommand(registryUrl: string = UPDATE_REGISTRY_URL): Promise<string | null> {
|
|
66
71
|
const controller = new AbortController();
|
|
67
72
|
const timeout = setTimeout(() => controller.abort(), UPDATE_FETCH_TIMEOUT_MS);
|
|
68
73
|
|
|
69
74
|
try {
|
|
70
|
-
const res = await fetch(
|
|
75
|
+
const res = await fetch(registryUrl, { signal: controller.signal });
|
|
71
76
|
if (!res.ok) return null;
|
|
72
77
|
const data = (await res.json()) as { version?: string };
|
|
73
78
|
const latest = typeof data.version === "string" ? data.version.trim().replace(/^v/, "") : "";
|
|
@@ -79,6 +84,19 @@ async function fetchLatestVersionForCommand(): Promise<string | null> {
|
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
|
|
87
|
+
function resolveInstalledPackageVersionForCommand(packageName: string): string | null {
|
|
88
|
+
try {
|
|
89
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
90
|
+
const packageJsonPath = requireFromHere.resolve(`${packageName}/package.json`);
|
|
91
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as { version?: unknown };
|
|
92
|
+
return typeof pkg.version === "string" && pkg.version.trim().length > 0
|
|
93
|
+
? pkg.version.trim().replace(/^v/, "")
|
|
94
|
+
: null;
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
82
100
|
export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined, reportText: string, structuredIssues: string): void {
|
|
83
101
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(gsdHome(), "agent", "GSD-WORKFLOW.md");
|
|
84
102
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
@@ -473,34 +491,81 @@ function compareSemverLocal(a: string, b: string): number {
|
|
|
473
491
|
return 0
|
|
474
492
|
}
|
|
475
493
|
|
|
476
|
-
|
|
494
|
+
function formatCommandVersion(version: string | null): string {
|
|
495
|
+
return version ? `v${version}` : "unknown";
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function pickHigherVersionForCommand(a: string | null, b: string | null): string | null {
|
|
499
|
+
if (!a) return b;
|
|
500
|
+
if (!b) return a;
|
|
501
|
+
return compareSemverLocal(a, b) >= 0 ? a : b;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Mirrors resolveGsdBrowserPathVersion in src/update-check.ts — duplicated because
|
|
505
|
+
// tsconfig.resources.json rootDir prevents importing from src/.
|
|
506
|
+
function resolveGsdBrowserPathVersionForCommand(env: NodeJS.ProcessEnv = process.env): string | null {
|
|
507
|
+
const explicit = env.GSD_BROWSER_PATH_VERSION?.trim();
|
|
508
|
+
if (explicit) return explicit.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
|
|
509
|
+
try {
|
|
510
|
+
const out = execFileSync("gsd-browser", ["--version"], {
|
|
511
|
+
encoding: "utf-8",
|
|
512
|
+
env,
|
|
513
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
514
|
+
timeout: 2000,
|
|
515
|
+
});
|
|
516
|
+
return out.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
|
|
517
|
+
} catch {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export async function handleUpdate(ctx: ExtensionCommandContext, args = ""): Promise<void> {
|
|
477
523
|
const { execSync } = await import("node:child_process");
|
|
478
524
|
|
|
479
|
-
const
|
|
480
|
-
const
|
|
525
|
+
const target = args.trim();
|
|
526
|
+
const browserUpdate = target === "browser" || target === "gsd-browser";
|
|
527
|
+
if (target && !browserUpdate) {
|
|
528
|
+
ctx.ui.notify("Usage: /gsd update [browser]", "warning");
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const NPM_PACKAGE = browserUpdate ? GSD_BROWSER_PACKAGE : GSD_PI_PACKAGE;
|
|
533
|
+
const registryUrl = browserUpdate ? BROWSER_UPDATE_REGISTRY_URL : UPDATE_REGISTRY_URL;
|
|
534
|
+
const bundledVersion = browserUpdate
|
|
535
|
+
? resolveInstalledPackageVersionForCommand(GSD_BROWSER_PACKAGE)
|
|
536
|
+
: null;
|
|
537
|
+
const current = browserUpdate
|
|
538
|
+
? pickHigherVersionForCommand(bundledVersion, resolveGsdBrowserPathVersionForCommand())
|
|
539
|
+
: process.env.GSD_VERSION || "0.0.0";
|
|
540
|
+
const label = browserUpdate ? "gsd-browser version" : "version";
|
|
481
541
|
|
|
482
|
-
ctx.ui.notify(`Current
|
|
542
|
+
ctx.ui.notify(`Current ${label}: ${formatCommandVersion(current)}\nChecking npm registry...`, "info");
|
|
483
543
|
|
|
484
|
-
const latest = await fetchLatestVersionForCommand();
|
|
544
|
+
const latest = await fetchLatestVersionForCommand(registryUrl);
|
|
485
545
|
if (!latest) {
|
|
486
546
|
ctx.ui.notify("Failed to reach npm registry. Check your network connection.", "error");
|
|
487
547
|
return;
|
|
488
548
|
}
|
|
489
549
|
|
|
490
|
-
if (compareSemverLocal(latest, current) <= 0) {
|
|
491
|
-
ctx.ui.notify(`Already up to date (
|
|
550
|
+
if (current && compareSemverLocal(latest, current) <= 0) {
|
|
551
|
+
ctx.ui.notify(`Already up to date (${formatCommandVersion(current)}).`, "info");
|
|
492
552
|
return;
|
|
493
553
|
}
|
|
494
554
|
|
|
495
|
-
ctx.ui.notify(`Updating:
|
|
555
|
+
ctx.ui.notify(`Updating: ${formatCommandVersion(current)} → v${latest}...`, "info");
|
|
496
556
|
|
|
497
557
|
const installCmd = resolveInstallCommand(`${NPM_PACKAGE}@latest`);
|
|
498
558
|
try {
|
|
499
559
|
execSync(installCmd, {
|
|
500
560
|
stdio: ["ignore", "pipe", "ignore"],
|
|
501
561
|
});
|
|
562
|
+
const newPathVersion = browserUpdate ? resolveGsdBrowserPathVersionForCommand() : null;
|
|
563
|
+
const pathReady = !browserUpdate || (!!newPathVersion && compareSemverLocal(newPathVersion, latest) >= 0);
|
|
502
564
|
ctx.ui.notify(
|
|
503
|
-
|
|
565
|
+
browserUpdate
|
|
566
|
+
? `Updated gsd-browser to v${latest}. Restart your GSD session to use the new browser automation version.` +
|
|
567
|
+
(pathReady ? "" : "\nNote: Ensure the npm global bin directory is on your PATH so MCP automation uses the updated binary.")
|
|
568
|
+
: `Updated to v${latest}. Restart your GSD session to use the new version.`,
|
|
504
569
|
"info",
|
|
505
570
|
);
|
|
506
571
|
} catch {
|
|
@@ -72,7 +72,8 @@ export function formatMcpInitResult(
|
|
|
72
72
|
`Project: ${targetPath}`,
|
|
73
73
|
`Config: ${configPath}`,
|
|
74
74
|
"",
|
|
75
|
-
"MCP-capable clients can now load the GSD workflow MCP
|
|
75
|
+
"MCP-capable clients can now load the GSD workflow and gsd-browser MCP servers from this folder.",
|
|
76
|
+
"Pi Providers use the managed gsd-browser engine directly; this project config is for External MCP Clients.",
|
|
76
77
|
"Restart or reconnect any client that already has this project open.",
|
|
77
78
|
].join("\n");
|
|
78
79
|
}
|
|
@@ -71,6 +71,14 @@ export class GSDDashboardOverlay {
|
|
|
71
71
|
private refreshInFlight: Promise<void> | null = null;
|
|
72
72
|
private disposed = false;
|
|
73
73
|
private resizeHandler: (() => void) | null = null;
|
|
74
|
+
private cachedMetrics: {
|
|
75
|
+
totals: ReturnType<typeof getProjectTotals>;
|
|
76
|
+
promptStats: ReturnType<typeof getPromptSizeStats>;
|
|
77
|
+
phases: ReturnType<typeof aggregateByPhase>;
|
|
78
|
+
slices: ReturnType<typeof aggregateBySlice>;
|
|
79
|
+
models: ReturnType<typeof aggregateByModel>;
|
|
80
|
+
} | null = null;
|
|
81
|
+
private lastSeenUnitCount = -1;
|
|
74
82
|
|
|
75
83
|
constructor(
|
|
76
84
|
tui: { requestRender: () => void },
|
|
@@ -123,7 +131,8 @@ export class GSDDashboardOverlay {
|
|
|
123
131
|
this.dashData = getAutoDashboardData();
|
|
124
132
|
const nextIdentity = this.computeDashboardIdentity(this.dashData);
|
|
125
133
|
|
|
126
|
-
|
|
134
|
+
const identityChanged = initial || nextIdentity !== this.loadedDashboardIdentity;
|
|
135
|
+
if (identityChanged) {
|
|
127
136
|
const loaded = await this.loadData();
|
|
128
137
|
if (this.disposed) return;
|
|
129
138
|
if (loaded) {
|
|
@@ -135,7 +144,9 @@ export class GSDDashboardOverlay {
|
|
|
135
144
|
this.loading = false;
|
|
136
145
|
}
|
|
137
146
|
|
|
138
|
-
|
|
147
|
+
if (identityChanged) {
|
|
148
|
+
this.invalidate();
|
|
149
|
+
}
|
|
139
150
|
this.tui.requestRender();
|
|
140
151
|
}
|
|
141
152
|
|
|
@@ -459,7 +470,7 @@ export class GSDDashboardOverlay {
|
|
|
459
470
|
|
|
460
471
|
const ledger = getLedger();
|
|
461
472
|
if (ledger && ledger.units.length > 0) {
|
|
462
|
-
const totals =
|
|
473
|
+
const { totals, promptStats, phases, slices, models } = this.ensureMetricsCache(ledger.units);
|
|
463
474
|
|
|
464
475
|
lines.push(blank());
|
|
465
476
|
lines.push(hr());
|
|
@@ -496,7 +507,6 @@ export class GSDDashboardOverlay {
|
|
|
496
507
|
lines.push(row(budgetParts.join(` ${th.fg("dim", "·")} `)));
|
|
497
508
|
}
|
|
498
509
|
|
|
499
|
-
const promptStats = getPromptSizeStats(ledger.units);
|
|
500
510
|
if (promptStats) {
|
|
501
511
|
const promptParts = [
|
|
502
512
|
`${th.fg("dim", "avg prompt:")} ${th.fg("text", formatCharCount(promptStats.averagePromptChars))}`,
|
|
@@ -508,7 +518,6 @@ export class GSDDashboardOverlay {
|
|
|
508
518
|
lines.push(row(promptParts.join(` ${th.fg("dim", "·")} `)));
|
|
509
519
|
}
|
|
510
520
|
|
|
511
|
-
const phases = aggregateByPhase(ledger.units);
|
|
512
521
|
if (phases.length > 0) {
|
|
513
522
|
lines.push(blank());
|
|
514
523
|
lines.push(row(th.fg("dim", "By Phase")));
|
|
@@ -520,7 +529,6 @@ export class GSDDashboardOverlay {
|
|
|
520
529
|
}
|
|
521
530
|
}
|
|
522
531
|
|
|
523
|
-
const slices = aggregateBySlice(ledger.units);
|
|
524
532
|
if (slices.length > 0) {
|
|
525
533
|
lines.push(blank());
|
|
526
534
|
lines.push(row(th.fg("dim", "By Slice")));
|
|
@@ -551,7 +559,6 @@ export class GSDDashboardOverlay {
|
|
|
551
559
|
}
|
|
552
560
|
}
|
|
553
561
|
|
|
554
|
-
const models = aggregateByModel(ledger.units);
|
|
555
562
|
if (models.length >= 1) {
|
|
556
563
|
lines.push(blank());
|
|
557
564
|
lines.push(row(th.fg("dim", "By Model")));
|
|
@@ -625,6 +632,20 @@ export class GSDDashboardOverlay {
|
|
|
625
632
|
return `${th.fg("dim", labelText)}${" ".repeat(gap)}${bar}${" ".repeat(gap)}${th.fg("dim", rightText)}`;
|
|
626
633
|
}
|
|
627
634
|
|
|
635
|
+
private ensureMetricsCache(units: UnitMetrics[]) {
|
|
636
|
+
if (!this.cachedMetrics || units.length !== this.lastSeenUnitCount) {
|
|
637
|
+
this.cachedMetrics = {
|
|
638
|
+
totals: getProjectTotals(units),
|
|
639
|
+
promptStats: getPromptSizeStats(units),
|
|
640
|
+
phases: aggregateByPhase(units),
|
|
641
|
+
slices: aggregateBySlice(units),
|
|
642
|
+
models: aggregateByModel(units),
|
|
643
|
+
};
|
|
644
|
+
this.lastSeenUnitCount = units.length;
|
|
645
|
+
}
|
|
646
|
+
return this.cachedMetrics!;
|
|
647
|
+
}
|
|
648
|
+
|
|
628
649
|
invalidate(): void {
|
|
629
650
|
this.cachedWidth = undefined;
|
|
630
651
|
this.cachedLines = undefined;
|
|
@@ -305,10 +305,18 @@ This config sets a parent workspace with two child repositories. The implicit `p
|
|
|
305
305
|
- `max_cycles`: number — max times this hook fires per trigger (default: 1, max: 10).
|
|
306
306
|
- `model`: string — optional model override.
|
|
307
307
|
- `artifact`: string — expected output file name (relative to task/slice dir). Hook is skipped if file already exists (idempotent).
|
|
308
|
+
- `criticality`: `"advisory"` or `"blocking"` — advisory preserves current best-effort behavior; blocking requires clean hook completion plus a valid outcome verdict before auto-mode advances. Default: `"advisory"`.
|
|
308
309
|
- `retry_on`: string — if this file is produced instead of the artifact, re-run the trigger unit then re-run hooks.
|
|
310
|
+
- `on_block`: object — optional routing for blocking findings:
|
|
311
|
+
- `action`: `"retry-unit"`, `"retry-task"`, `"queue-task"`, `"queue-slice"`, or `"pause"`.
|
|
312
|
+
- `artifact`: string — optional compatibility artifact for retry routing.
|
|
309
313
|
- `agent`: string — agent definition file to use for hook execution.
|
|
310
314
|
- `enabled`: boolean — toggle without removing (default: `true`).
|
|
311
315
|
|
|
316
|
+
Blocking hook artifacts must begin with YAML frontmatter containing either `verdict` or `outcome.verdict`.
|
|
317
|
+
Supported verdicts are `pass`, `advisory`, `needs-rework`, `needs-remediation`, and `needs-attention`.
|
|
318
|
+
`pass` and `advisory` continue; `needs-rework` retries the trigger unit when routed with `retry-unit`/`retry-task`; `needs-remediation` and `needs-attention` pause with recovery guidance.
|
|
319
|
+
|
|
312
320
|
- `pre_dispatch_hooks`: array — hooks that fire before a unit is dispatched. Each entry has:
|
|
313
321
|
- `name`: string — unique hook identifier.
|
|
314
322
|
- `before`: string[] — unit types to intercept.
|
|
@@ -269,14 +269,14 @@ export async function checkRuntimeHealth(
|
|
|
269
269
|
} catch {
|
|
270
270
|
count = MAX_UAT_ATTEMPTS + 1;
|
|
271
271
|
}
|
|
272
|
-
if (count
|
|
272
|
+
if (count < MAX_UAT_ATTEMPTS) continue;
|
|
273
273
|
|
|
274
274
|
issues.push({
|
|
275
275
|
severity: "warning",
|
|
276
276
|
code: "uat_retry_exhausted",
|
|
277
277
|
scope: "slice",
|
|
278
278
|
unitId: `${mid}/${sid}`,
|
|
279
|
-
message: `run-uat for ${mid}/${sid} exhausted ${count
|
|
279
|
+
message: `run-uat for ${mid}/${sid} exhausted ${count} attempt(s) without an ASSESSMENT verdict. Reset the retry counter after fixing the underlying UAT/tool issue, then rerun /gsd auto.`,
|
|
280
280
|
file: `.gsd/runtime/${fileName}`,
|
|
281
281
|
fixable: true,
|
|
282
282
|
});
|
|
@@ -159,13 +159,13 @@ export function readEscalationArtifact(path: string): EscalationArtifact | null
|
|
|
159
159
|
// ─── Detection ────────────────────────────────────────────────────────────
|
|
160
160
|
|
|
161
161
|
/**
|
|
162
|
-
* Returns the task id of the first task with an
|
|
163
|
-
*
|
|
164
|
-
*
|
|
162
|
+
* Returns the task id of the first task with an unresolved escalation.
|
|
163
|
+
* `continueWithDefault=true` artifacts keep the awaiting_review flag for
|
|
164
|
+
* compatibility, but still pause dispatch until the user explicitly responds.
|
|
165
165
|
*/
|
|
166
166
|
export function detectPendingEscalation(tasks: TaskRow[], basePath: string): string | null {
|
|
167
167
|
for (const t of tasks) {
|
|
168
|
-
if (t.escalation_pending !== 1) continue;
|
|
168
|
+
if (t.escalation_pending !== 1 && t.escalation_awaiting_review !== 1) continue;
|
|
169
169
|
if (!t.escalation_artifact_path) continue;
|
|
170
170
|
const art = readEscalationArtifact(t.escalation_artifact_path);
|
|
171
171
|
if (art && !art.respondedAt) return t.id;
|
|
@@ -123,6 +123,91 @@ interface ForensicReport {
|
|
|
123
123
|
worktreeTelemetry: WorktreeTelemetrySummary | null;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
// ─── Filing Tool Scope ───────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
const FORENSICS_FILING_TOOLS = ["bash", "write"] as const;
|
|
129
|
+
|
|
130
|
+
type ForensicsFilingTool = typeof FORENSICS_FILING_TOOLS[number];
|
|
131
|
+
|
|
132
|
+
export interface ForensicsToolScope {
|
|
133
|
+
savedTools: string[];
|
|
134
|
+
activeToolsForTurn: string[];
|
|
135
|
+
availableFilingTools: ForensicsFilingTool[];
|
|
136
|
+
missingFilingTools: ForensicsFilingTool[];
|
|
137
|
+
toolsChanged: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function uniqueAppend(base: readonly string[], additions: readonly string[]): string[] {
|
|
141
|
+
const seen = new Set(base);
|
|
142
|
+
const next = [...base];
|
|
143
|
+
for (const name of additions) {
|
|
144
|
+
if (seen.has(name)) continue;
|
|
145
|
+
seen.add(name);
|
|
146
|
+
next.push(name);
|
|
147
|
+
}
|
|
148
|
+
return next;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function sameOrderedTools(a: readonly string[], b: readonly string[]): boolean {
|
|
152
|
+
return a.length === b.length && a.every((name, index) => name === b[index]);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function getRegisteredToolNames(
|
|
156
|
+
pi: Pick<ExtensionAPI, "getActiveTools"> & Partial<Pick<ExtensionAPI, "getAllTools">>,
|
|
157
|
+
fallback: readonly string[],
|
|
158
|
+
): string[] {
|
|
159
|
+
if (typeof pi.getAllTools === "function") {
|
|
160
|
+
return pi.getAllTools().map((tool) => tool.name);
|
|
161
|
+
}
|
|
162
|
+
return [...fallback];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function createForensicsToolScope(
|
|
166
|
+
pi: Pick<ExtensionAPI, "getActiveTools"> & Partial<Pick<ExtensionAPI, "getAllTools">>,
|
|
167
|
+
): ForensicsToolScope {
|
|
168
|
+
const savedTools = [...pi.getActiveTools()];
|
|
169
|
+
const registeredTools = new Set([...getRegisteredToolNames(pi, savedTools), ...savedTools]);
|
|
170
|
+
const availableFilingTools = FORENSICS_FILING_TOOLS.filter((name) => registeredTools.has(name));
|
|
171
|
+
const missingFilingTools = FORENSICS_FILING_TOOLS.filter((name) => !registeredTools.has(name));
|
|
172
|
+
const activeToolsForTurn = uniqueAppend(savedTools, availableFilingTools);
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
savedTools,
|
|
176
|
+
activeToolsForTurn,
|
|
177
|
+
availableFilingTools,
|
|
178
|
+
missingFilingTools,
|
|
179
|
+
toolsChanged: !sameOrderedTools(savedTools, activeToolsForTurn),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function applyForensicsToolScope(pi: Pick<ExtensionAPI, "setActiveTools">, scope: ForensicsToolScope): void {
|
|
184
|
+
if (scope.toolsChanged) pi.setActiveTools(scope.activeToolsForTurn);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function restoreForensicsToolScope(pi: Pick<ExtensionAPI, "setActiveTools">, scope: ForensicsToolScope): void {
|
|
188
|
+
if (scope.toolsChanged) pi.setActiveTools(scope.savedTools);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function buildForensicsToolingSection(scope: ForensicsToolScope): string {
|
|
192
|
+
const available = new Set(scope.availableFilingTools);
|
|
193
|
+
const requested = scope.availableFilingTools.length
|
|
194
|
+
? scope.availableFilingTools.map((name) => `\`${name}\``).join(", ")
|
|
195
|
+
: "none";
|
|
196
|
+
const statusFor = (name: ForensicsFilingTool) => available.has(name)
|
|
197
|
+
? `- \`${name}\`: available for this queued forensics turn`
|
|
198
|
+
: `- \`${name}\`: unavailable in this host session`;
|
|
199
|
+
|
|
200
|
+
return `
|
|
201
|
+
## Filing Tool Availability
|
|
202
|
+
|
|
203
|
+
For this queued forensic turn, the extension requested the registered filing tools: ${requested}.
|
|
204
|
+
|
|
205
|
+
${FORENSICS_FILING_TOOLS.map(statusFor).join("\n")}
|
|
206
|
+
|
|
207
|
+
If \`bash\` is available, use the GitHub duplicate-check and issue-creation protocols below. If \`bash\` is unavailable, do not attempt duplicate-check or issue-creation tool calls with another tool; provide the paste-once shell script fallback instead.
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
210
|
+
|
|
126
211
|
// ─── Duplicate Detection ──────────────────────────────────────────────────────
|
|
127
212
|
|
|
128
213
|
const DEDUP_PROMPT_SECTION = `
|
|
@@ -134,6 +219,8 @@ Before reading GSD source code or performing deep analysis, you MUST search for
|
|
|
134
219
|
|
|
135
220
|
Use keywords from the user's problem description and the anomaly summaries in the forensic report above.
|
|
136
221
|
|
|
222
|
+
If \`bash\` is unavailable in the Filing Tool Availability section, do not attempt live duplicate-search tool calls. Say the live duplicate search must be run by the user, continue the source investigation, and include the duplicate-search commands in the paste-once fallback when issue filing is accepted.
|
|
223
|
+
|
|
137
224
|
1. **Search closed issues** for similar keywords:
|
|
138
225
|
\`\`\`
|
|
139
226
|
gh issue list --repo open-gsd/gsd-pi --state closed --search "<keywords from root cause>" --limit 20
|
|
@@ -265,21 +352,28 @@ export async function handleForensics(
|
|
|
265
352
|
}
|
|
266
353
|
|
|
267
354
|
const forensicData = formatReportForPrompt(report);
|
|
355
|
+
const toolScope = createForensicsToolScope(pi);
|
|
268
356
|
const content = loadPrompt("forensics", {
|
|
269
357
|
problemDescription,
|
|
270
358
|
forensicData,
|
|
271
359
|
gsdSourceDir,
|
|
272
360
|
dedupSection,
|
|
361
|
+
toolingSection: buildForensicsToolingSection(toolScope),
|
|
273
362
|
});
|
|
274
363
|
|
|
275
364
|
ctx.ui.notify(`Forensic report saved: ${relative(basePath, savedPath)}`, "info");
|
|
276
365
|
ctx.ui.setStatus("gsd-forensics", "running");
|
|
277
366
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
367
|
+
try {
|
|
368
|
+
applyForensicsToolScope(pi, toolScope);
|
|
369
|
+
await pi.sendMessage(
|
|
370
|
+
{ customType: "gsd-forensics", content, display: false },
|
|
371
|
+
{ triggerTurn: true },
|
|
372
|
+
);
|
|
373
|
+
} finally {
|
|
374
|
+
restoreForensicsToolScope(pi, toolScope);
|
|
375
|
+
ctx.ui.setStatus("gsd-forensics", undefined);
|
|
376
|
+
}
|
|
283
377
|
|
|
284
378
|
// Persist forensics context so follow-up turns can re-inject it (#2941)
|
|
285
379
|
writeForensicsMarker(basePath, savedPath, content);
|
|
@@ -1488,7 +1488,7 @@ export function setTaskEscalationPending(
|
|
|
1488
1488
|
).run({ ":path": artifactPath, ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
1489
1489
|
}
|
|
1490
1490
|
|
|
1491
|
-
/** Set awaiting-review state (artifact exists
|
|
1491
|
+
/** Set awaiting-review state (artifact exists and requires explicit user review). Mutually exclusive with pending. */
|
|
1492
1492
|
export function setTaskEscalationAwaitingReview(
|
|
1493
1493
|
milestoneId: string, sliceId: string, taskId: string,
|
|
1494
1494
|
artifactPath: string,
|
|
@@ -3233,7 +3233,10 @@ export function decayMemoriesBefore(cutoffTs: string, now: string): void {
|
|
|
3233
3233
|
currentDb.prepare(
|
|
3234
3234
|
`UPDATE memories
|
|
3235
3235
|
SET confidence = MAX(0.1, confidence - 0.1), updated_at = :now
|
|
3236
|
-
WHERE superseded_by IS NULL
|
|
3236
|
+
WHERE superseded_by IS NULL
|
|
3237
|
+
AND updated_at < :cutoff
|
|
3238
|
+
AND confidence > 0.1
|
|
3239
|
+
AND (structured_fields IS NULL OR structured_fields NOT LIKE '%"sourceDecisionId"%')`,
|
|
3237
3240
|
).run({ ":now": now, ":cutoff": cutoffTs });
|
|
3238
3241
|
}
|
|
3239
3242
|
|