@opengsd/gsd-core 1.10.0 → 1.11.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-debug-session-manager.md +11 -0
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-executor.md +5 -5
- package/agents/gsd-mempalace-curator.md +5 -2
- package/agents/gsd-phase-researcher.md +20 -1
- package/agents/gsd-plan-checker.md +37 -0
- package/agents/gsd-planner.md +44 -46
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +12 -3
- package/bin/install.js +841 -971
- package/bin/lib/ui-safety-gate.cjs +2 -0
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/quick.md +1 -1
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +469 -88
- package/gsd-core/bin/lib/active-workstream-store.cjs +138 -22
- package/gsd-core/bin/lib/agent-install-check.cjs +230 -32
- package/gsd-core/bin/lib/api-coverage.cjs +3 -5
- package/gsd-core/bin/lib/artifacts.cjs +3 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +876 -240
- package/gsd-core/bin/lib/broken-windows.cjs +1 -1
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-registry.cjs +575 -101
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +495 -22
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +71 -37
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/commands.cjs +651 -86
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
- package/gsd-core/bin/lib/config-loader.cjs +75 -0
- package/gsd-core/bin/lib/config.cjs +10 -1
- package/gsd-core/bin/lib/core-utils.cjs +127 -29
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +155 -20
- package/gsd-core/bin/lib/gap-checker.cjs +68 -7
- package/gsd-core/bin/lib/git-base-branch.cjs +102 -0
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init.cjs +321 -129
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +745 -258
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
- package/gsd-core/bin/lib/install-profiles.cjs +134 -57
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +138 -31
- package/gsd-core/bin/lib/io.cjs +10 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +133 -20
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +754 -70
- package/gsd-core/bin/lib/model-catalog.cjs +59 -1
- package/gsd-core/bin/lib/model-resolver.cjs +183 -40
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
- package/gsd-core/bin/lib/phase-id.cjs +444 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
- package/gsd-core/bin/lib/phase-locator.cjs +125 -18
- package/gsd-core/bin/lib/phase.cjs +646 -143
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +86 -2
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +56 -6
- package/gsd-core/bin/lib/probe-core.cjs +1 -1
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +13 -4
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +34 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +943 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +385 -94
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +608 -46
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +426 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +115 -3
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +275 -3
- package/gsd-core/bin/lib/smart-entry.cjs +142 -22
- package/gsd-core/bin/lib/state-command-router.cjs +5 -1
- package/gsd-core/bin/lib/state-document.cjs +152 -8
- package/gsd-core/bin/lib/state-transition.cjs +371 -117
- package/gsd-core/bin/lib/state.cjs +1794 -357
- package/gsd-core/bin/lib/surface.cjs +23 -9
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +9 -3
- package/gsd-core/bin/lib/uat.cjs +399 -56
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
- package/gsd-core/bin/lib/unusable-input.cjs +24 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +37 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +258 -8
- package/gsd-core/bin/lib/verify.cjs +368 -888
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +176 -9
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +7 -1
- package/gsd-core/references/agent-contracts.md +43 -26
- package/gsd-core/references/checkpoints.md +2 -2
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/execute-mvp-tdd.md +3 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +6 -2
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-planning-commit.md +2 -1
- package/gsd-core/references/loop-hook-dispatch.md +39 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planning-config.md +8 -6
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +3 -3
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +22 -6
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +2 -4
- package/gsd-core/templates/state.md +4 -4
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/ai-integration-phase.md +9 -11
- package/gsd-core/workflows/autonomous.md +1 -1
- package/gsd-core/workflows/cleanup.md +62 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +13 -3
- package/gsd-core/workflows/code-review-fix.md +37 -10
- package/gsd-core/workflows/code-review.md +38 -12
- package/gsd-core/workflows/complete-milestone.md +141 -18
- package/gsd-core/workflows/debug.md +7 -5
- package/gsd-core/workflows/diagnose-issues.md +35 -9
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +2 -1
- package/gsd-core/workflows/edit-phase.md +26 -1
- package/gsd-core/workflows/eval-review.md +3 -5
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +31 -6
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +2 -0
- package/gsd-core/workflows/execute-phase.md +38 -50
- package/gsd-core/workflows/execute-plan.md +36 -4
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/health.md +73 -4
- package/gsd-core/workflows/import.md +4 -4
- package/gsd-core/workflows/ingest-docs.md +5 -5
- package/gsd-core/workflows/mvp-phase.md +6 -3
- package/gsd-core/workflows/new-milestone.md +14 -9
- package/gsd-core/workflows/new-project.md +14 -14
- package/gsd-core/workflows/next.md +12 -0
- package/gsd-core/workflows/plan-phase.md +41 -17
- package/gsd-core/workflows/plan-review-convergence.md +50 -2
- package/gsd-core/workflows/progress.md +34 -6
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -4
- package/gsd-core/workflows/quick/steps/quick-verification.md +27 -6
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -2
- package/gsd-core/workflows/quick.md +35 -15
- package/gsd-core/workflows/review.md +26 -5
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings.md +66 -2
- package/gsd-core/workflows/ship.md +104 -44
- package/gsd-core/workflows/spec-phase.md +30 -12
- package/gsd-core/workflows/sync-skills.md +63 -8
- package/gsd-core/workflows/transition.md +46 -11
- package/gsd-core/workflows/ui-phase.md +5 -5
- package/gsd-core/workflows/ui-review.md +2 -2
- package/gsd-core/workflows/update.md +1 -1
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +9 -7
- package/hooks/dist/gsd-agent-isolation-guard.js +103 -14
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
- package/hooks/dist/gsd-cursor-subagent-start.js +77 -2
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/dist/gsd-prompt-guard.js +21 -20
- package/hooks/dist/gsd-read-injection-scanner.js +38 -24
- package/hooks/dist/gsd-statusline.js +18 -0
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-workflow-guard.js +134 -36
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +9 -0
- package/hooks/gsd-agent-isolation-guard.js +103 -14
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-cursor-pre-tool.js +0 -3
- package/hooks/gsd-cursor-subagent-start.js +77 -2
- package/hooks/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/gsd-prompt-guard.js +21 -20
- package/hooks/gsd-read-injection-scanner.js +38 -24
- package/hooks/gsd-statusline.js +18 -0
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-workflow-guard.js +134 -36
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +9 -0
- package/package.json +21 -9
- package/pi/gsd.cjs +19 -5
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/changeset/lint.cjs +60 -5
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/ci-test-scope.cjs +19 -2
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +3 -15
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +4 -24
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/drift-scan.cjs +278 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-phase-enumeration-drift.cjs +479 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +434 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +320 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +1045 -0
- package/scripts/lint-test-file-count.allowlist.json +21 -10
- package/scripts/lint-unreachable-guard-drift.cjs +843 -0
- package/scripts/lint-vendored-deps.cjs +124 -0
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +25 -0
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/state-write-path-drift-baseline.json +19 -0
- package/scripts/sync-runtime-launcher.cjs +2 -4
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +1 -1
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -43,8 +43,9 @@
|
|
|
43
43
|
// authoritative — `none`/`orchestrator-worktree` ALLOW immediately
|
|
44
44
|
// (sequential/orchestrator-managed dispatch is legitimate, not a bug); an
|
|
45
45
|
// absent/stale sentinel falls back to a conservative registry+config check
|
|
46
|
-
// (GSD_RUNTIME env > .planning/config.json `runtime`
|
|
47
|
-
//
|
|
46
|
+
// (GSD_RUNTIME env > .planning/config.json `runtime` > the per-install
|
|
47
|
+
// `.gsd-runtime` marker, #3566 — no confident signal degrades to inert rather
|
|
48
|
+
// than guessing 'claude', see resolveRegistryIsolation)
|
|
48
49
|
// gated additionally by `workflow.use_worktrees` — read directly, in-process,
|
|
49
50
|
// no subprocess spawn.
|
|
50
51
|
//
|
|
@@ -63,6 +64,15 @@ const fs = require('fs');
|
|
|
63
64
|
const path = require('path');
|
|
64
65
|
const os = require('os');
|
|
65
66
|
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch } = require('./lib/isolation-sentinel.js');
|
|
67
|
+
const { REASON_CODE } = require('./lib/isolation-deny-reason.js');
|
|
68
|
+
// #3582: gsd-core/bin/lib/*.cjs (runtime-name-policy.cjs, capability-registry.cjs
|
|
69
|
+
// below) are tsc build artifacts (ADR-457), gitignored and absent on a raw
|
|
70
|
+
// plugin-marketplace / git-clone install that never ran `npm run build:lib`.
|
|
71
|
+
// Self-heal before the first such require (resolveRegistryIsolation, below) —
|
|
72
|
+
// see ensureRuntimeBuild's own header for the full rationale. This module
|
|
73
|
+
// itself (gsd-core/bin/ensure-runtime-build.cjs) depends on nothing under
|
|
74
|
+
// ./lib, so requiring it here is always safe.
|
|
75
|
+
const { ensureRuntimeBuild, RuntimeBuildError } = require('../gsd-core/bin/ensure-runtime-build.cjs');
|
|
66
76
|
|
|
67
77
|
// No other executor-shaped subagent_type exists in agents/ today
|
|
68
78
|
// (verified: only agents/gsd-executor.md). A Set, not a bare string compare,
|
|
@@ -85,6 +95,42 @@ function parseHarnessFlag(flag) {
|
|
|
85
95
|
return { param: m[1], value: m[2] };
|
|
86
96
|
}
|
|
87
97
|
|
|
98
|
+
// ─── #3566: per-install runtime marker ────────────────────────────────────────
|
|
99
|
+
// bin/install.js writes `<install>/gsd-core/.gsd-runtime` for EVERY runtime
|
|
100
|
+
// install (#2297), co-located with VERSION. Unlike `~/.gsd/defaults.json` —
|
|
101
|
+
// which is host-wide and names whichever runtime's install ran LAST, the exact
|
|
102
|
+
// leakage #2840's config.cjs change exists to prevent — the marker describes
|
|
103
|
+
// THIS install, which is the property runtime identity needs on a machine
|
|
104
|
+
// with 2+ runtimes. Mirrors readInstallRuntimeMarker in src/model-resolver.cts
|
|
105
|
+
// (same cache + test-seam shape); this hook cannot import that module without
|
|
106
|
+
// dragging the whole model-resolution stack into a PreToolUse hot path, so the
|
|
107
|
+
// 5-line read lives here against the same sibling-layout assumption the hook's
|
|
108
|
+
// own require('../gsd-core/bin/lib/…') already makes. Epic #3473 B3 owns
|
|
109
|
+
// consolidating every marker reader into one shared seam.
|
|
110
|
+
let _installMarkerCache; // undefined = unread; null = known absent; string = value
|
|
111
|
+
|
|
112
|
+
function readInstallRuntimeMarker() {
|
|
113
|
+
if (_installMarkerCache !== undefined) return _installMarkerCache;
|
|
114
|
+
try {
|
|
115
|
+
const markerPath = path.join(__dirname, '..', 'gsd-core', '.gsd-runtime');
|
|
116
|
+
const raw = fs.readFileSync(markerPath, 'utf-8').trim();
|
|
117
|
+
_installMarkerCache = raw || null;
|
|
118
|
+
} catch {
|
|
119
|
+
// No marker: dev/source tree, or an install predating #2297 — "no signal
|
|
120
|
+
// from this rung", never a resolution failure. Falls through to the
|
|
121
|
+
// defaults rung below.
|
|
122
|
+
_installMarkerCache = null;
|
|
123
|
+
}
|
|
124
|
+
return _installMarkerCache;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Test seam for the marker rung (the dev/source tree has no marker file, so
|
|
128
|
+
// the read always bottoms out at null there — same seam contract as
|
|
129
|
+
// model-resolver.cts's _setInstallRuntimeMarkerForTests, #2297).
|
|
130
|
+
function _setInstallRuntimeMarkerForTests(value) {
|
|
131
|
+
_installMarkerCache = value;
|
|
132
|
+
}
|
|
133
|
+
|
|
88
134
|
/**
|
|
89
135
|
* Resolve this project's declared `runtime` identity WITHOUT defaulting to
|
|
90
136
|
* 'claude' when no explicit signal exists (#3045 MAJOR 2).
|
|
@@ -100,9 +146,10 @@ function parseHarnessFlag(flag) {
|
|
|
100
146
|
*
|
|
101
147
|
* Returns `{ runtimeId, confident }`. `confident` is true only when an
|
|
102
148
|
* explicit signal exists (GSD_RUNTIME env override, a `runtime` key literally
|
|
103
|
-
* present in config.json,
|
|
104
|
-
* by the installer — see
|
|
105
|
-
*
|
|
149
|
+
* present in config.json, the per-install `.gsd-runtime` marker, or a
|
|
150
|
+
* `runtime` persisted to `~/.gsd/defaults.json` by the installer — see
|
|
151
|
+
* below); false means "cannot determine" and callers must NOT silently
|
|
152
|
+
* substitute 'claude' — see resolveRegistryIsolation.
|
|
106
153
|
*
|
|
107
154
|
* #3045 BLOCKER 2 fix: precedence is GSD_RUNTIME env > config.json `runtime`
|
|
108
155
|
* key > `~/.gsd/defaults.json` `runtime`. The first two are unchanged; the
|
|
@@ -117,6 +164,17 @@ function parseHarnessFlag(flag) {
|
|
|
117
164
|
* (`nativeModelAliases` short-circuits it) and therefore correctly still rely
|
|
118
165
|
* on config.json/env. Reading the installer's own persisted signal makes
|
|
119
166
|
* "confident" the common case instead.
|
|
167
|
+
*
|
|
168
|
+
* #3566: the per-install `.gsd-runtime` marker now sits BETWEEN config.json
|
|
169
|
+
* and defaults.json. defaults.json is host-wide and names whichever runtime
|
|
170
|
+
* installed LAST — on a 2-runtime machine that confidently resolves the WRONG
|
|
171
|
+
* runtime (a Codex install's `runtime:"codex"` leaking into Claude projects),
|
|
172
|
+
* and when the wrong runtime declares no harnessIsolationFlag the guard goes
|
|
173
|
+
* silently inert. The marker describes THIS install (written for every
|
|
174
|
+
* runtime since #2297), which is the source #2840's config.cjs change names
|
|
175
|
+
* as correct. defaults.json stays as the final rung so single-runtime default
|
|
176
|
+
* installs and pre-#2297 installs (no marker on disk) keep the #3045
|
|
177
|
+
* BLOCKER 2 behavior.
|
|
120
178
|
*/
|
|
121
179
|
function resolveRuntimeIdentity(cwd, configPath, resolveRuntimeNameFromCandidates) {
|
|
122
180
|
const envRuntime = resolveRuntimeNameFromCandidates(process.env.GSD_RUNTIME);
|
|
@@ -132,6 +190,13 @@ function resolveRuntimeIdentity(cwd, configPath, resolveRuntimeNameFromCandidate
|
|
|
132
190
|
if (configRuntime) return { runtimeId: configRuntime, confident: true };
|
|
133
191
|
}
|
|
134
192
|
|
|
193
|
+
// #3566: the per-install marker, above the host-wide defaults — see the
|
|
194
|
+
// block comment on readInstallRuntimeMarker. An empty/whitespace-only file
|
|
195
|
+
// or an unknown value degrades exactly like the other rungs (no signal /
|
|
196
|
+
// future-runtime tolerance via resolveRuntimeNameFromCandidates).
|
|
197
|
+
const markerRuntime = resolveRuntimeNameFromCandidates(readInstallRuntimeMarker());
|
|
198
|
+
if (markerRuntime) return { runtimeId: markerRuntime, confident: true };
|
|
199
|
+
|
|
135
200
|
// #3045 BLOCKER 2: fall back to the installer-persisted default. Read
|
|
136
201
|
// defensively — an absent/corrupt/non-object defaults.json is "no signal",
|
|
137
202
|
// never a resolution failure (this function only ever throws for the
|
|
@@ -183,6 +248,15 @@ function resolveHarnessFlag(runtimeId, runtimes) {
|
|
|
183
248
|
* run, e.g. a manual Agent() call before any sentinel has been written).
|
|
184
249
|
*/
|
|
185
250
|
function resolveRegistryIsolation(cwd, configPath) {
|
|
251
|
+
// #3582: self-heal the compiled runtime library BEFORE either require
|
|
252
|
+
// below — this is the only reaching path to both (resolveRegistryIsolation
|
|
253
|
+
// is the sole caller of each), so one call here covers both. Throws
|
|
254
|
+
// RuntimeBuildError on an unbuildable tree; the caller (resolveIsolationState)
|
|
255
|
+
// already wraps this whole function in try/catch and folds any error into
|
|
256
|
+
// its fail-closed `error` result — evaluateDispatch below distinguishes a
|
|
257
|
+
// RuntimeBuildError there so it surfaces this seam's actionable message
|
|
258
|
+
// instead of being misreported as an unreadable config.json (#3050 lesson).
|
|
259
|
+
ensureRuntimeBuild();
|
|
186
260
|
const { resolveRuntimeNameFromCandidates } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
|
|
187
261
|
const { runtimes } = require('../gsd-core/bin/lib/capability-registry.cjs');
|
|
188
262
|
|
|
@@ -360,13 +434,27 @@ function evaluateDispatch(data, { clock = Date } = {}) {
|
|
|
360
434
|
if (!state.gsdProject) return { action: 'allow' };
|
|
361
435
|
|
|
362
436
|
if (state.error) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
437
|
+
// #3582: a missing/unbuildable compiled runtime library (RuntimeBuildError,
|
|
438
|
+
// thrown by ensureRuntimeBuild in resolveRegistryIsolation) is a DIFFERENT,
|
|
439
|
+
// actionable failure from an unreadable/unparsable config.json — surface
|
|
440
|
+
// its own message instead of misreporting it as the generic
|
|
441
|
+
// "could not read or resolve ... configuration" text (the exact #3050
|
|
442
|
+
// misreport this issue exists to fix). Both cases still fail closed
|
|
443
|
+
// (block); only the message differs.
|
|
444
|
+
const isBuildFailure = state.error instanceof RuntimeBuildError;
|
|
445
|
+
const reason = isBuildFailure
|
|
446
|
+
? `Agent isolation guard: cannot resolve this project's dispatch-isolation ` +
|
|
447
|
+
`configuration because the GSD runtime library failed to self-build. ` +
|
|
448
|
+
`${state.error.message} Refusing to dispatch subagent_type="${subagentType}" until ` +
|
|
449
|
+
`the runtime library is built — a guard that cannot verify must not answer "safe" ` +
|
|
450
|
+
`(#3050).`
|
|
451
|
+
: `Agent isolation guard: could not read or resolve this project's dispatch-isolation ` +
|
|
452
|
+
`configuration ('.planning/config.json' under '${cwd}'). Refusing to dispatch ` +
|
|
453
|
+
`subagent_type="${subagentType}" without being able to verify whether isolation is ` +
|
|
454
|
+
`required — a guard that cannot verify must not answer "safe" (#3050). Retry once the ` +
|
|
455
|
+
`project configuration is readable.`;
|
|
456
|
+
const reasonCode = isBuildFailure ? REASON_CODE.RUNTIME_BUILD_FAILED : REASON_CODE.CONFIG_UNREADABLE;
|
|
457
|
+
return { action: 'block', reason, reasonCode };
|
|
370
458
|
}
|
|
371
459
|
|
|
372
460
|
if (state.isolation !== 'harness-worktree') return { action: 'allow' };
|
|
@@ -382,7 +470,7 @@ function evaluateDispatch(data, { clock = Date } = {}) {
|
|
|
382
470
|
`${parsed.param}="${parsed.value}". Add ${parsed.param}="${parsed.value}" to the Agent() ` +
|
|
383
471
|
`call so the executor runs in an isolated worktree instead of the primary checkout ` +
|
|
384
472
|
`(gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md).`;
|
|
385
|
-
return { action: 'block', reason };
|
|
473
|
+
return { action: 'block', reason, reasonCode: REASON_CODE.HARNESS_FLAG_MISSING };
|
|
386
474
|
}
|
|
387
475
|
|
|
388
476
|
/* istanbul ignore next -- stdin adapter, exercised via spawnSync in tests */
|
|
@@ -397,7 +485,7 @@ function main() {
|
|
|
397
485
|
const data = JSON.parse(input);
|
|
398
486
|
const decision = evaluateDispatch(data);
|
|
399
487
|
if (decision.action === 'block') {
|
|
400
|
-
const out = { decision: 'block', reason: decision.reason };
|
|
488
|
+
const out = { decision: 'block', reason: decision.reason, reason_code: decision.reasonCode };
|
|
401
489
|
process.stdout.write(JSON.stringify(out));
|
|
402
490
|
// Kimi feeds stderr (not stdout) back to the model on exit 2.
|
|
403
491
|
process.stderr.write(decision.reason);
|
|
@@ -425,4 +513,5 @@ module.exports = {
|
|
|
425
513
|
resolveHarnessFlag,
|
|
426
514
|
resolveRegistryIsolation,
|
|
427
515
|
parseHarnessFlag,
|
|
516
|
+
_setInstallRuntimeMarkerForTests,
|
|
428
517
|
};
|
|
@@ -11,18 +11,61 @@
|
|
|
11
11
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
|
-
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
|
|
15
|
+
// #3582: gsd-core/bin/lib/semver-compare.cjs and package-identity.cjs (and,
|
|
16
|
+
// transitively, check-latest-version.cjs's own gsd-core/bin/lib/cli-exit.cjs
|
|
17
|
+
// + shell-command-projection.cjs) are tsc build artifacts (ADR-457),
|
|
18
|
+
// gitignored and absent on a raw plugin-marketplace / git-clone install that
|
|
19
|
+
// never ran `npm run build:lib`. This worker is a DETACHED SessionStart
|
|
20
|
+
// background process (spawned with stdio: 'ignore') — a build failure here
|
|
21
|
+
// must DEGRADE to the no-signal fallbacks below (mirroring the
|
|
22
|
+
// managed-hooks-registry.cjs degrade just below) so the worker still runs to
|
|
23
|
+
// completion and writes a result cache record, rather than dying silently
|
|
24
|
+
// with no visible signal and no cache-file write at all.
|
|
25
|
+
//
|
|
26
|
+
// This try/require/ensureRuntimeBuild/require/catch shape repeats (with
|
|
27
|
+
// different destructured names) in hooks/gsd-check-update.js and
|
|
28
|
+
// hooks/gsd-update-banner.js. It is deliberately NOT extracted into a shared
|
|
29
|
+
// hooks/lib/ helper: scripts/lint-hooks-runtime-build-seam.cjs enforces this
|
|
30
|
+
// exact seam textually, PER FILE — it greps each hooks/ file for its OWN
|
|
31
|
+
// literal `require('.../ensure-runtime-build.cjs')` + `ensureRuntimeBuild(`
|
|
32
|
+
// call co-occurring with its OWN literal `require('.../gsd-core/bin/lib/*.cjs')`.
|
|
33
|
+
// A generic helper taking the compiled module's path as a variable would move
|
|
34
|
+
// the literal compiled-lib require OUT of this file and into the helper,
|
|
35
|
+
// called with a non-literal argument — the scan's regex (see that script's
|
|
36
|
+
// "Known limitations") cannot see a require() called with a variable, so this
|
|
37
|
+
// file would then read as "requires nothing" and the lint would stop
|
|
38
|
+
// protecting it. A ceremony-only helper (just the ensureRuntimeBuild call,
|
|
39
|
+
// each caller keeping its own literal compiled-lib require) fails the SAME
|
|
40
|
+
// way from the other side: it would remove this file's own literal
|
|
41
|
+
// `require('.../ensure-runtime-build.cjs')` + `ensureRuntimeBuild(` call,
|
|
42
|
+
// which the lint also requires to be textually present in THIS file. Either
|
|
43
|
+
// shape needs the lint script itself widened to special-case the helper,
|
|
44
|
+
// which is a bigger, riskier change than the ~6 duplicated lines it would
|
|
45
|
+
// save; kept inline instead.
|
|
46
|
+
let isSemverNewer = () => false;
|
|
47
|
+
let checkLatestVersion = () => ({ ok: false });
|
|
48
|
+
let PACKAGE_NAME = null;
|
|
49
|
+
try {
|
|
50
|
+
const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
|
|
51
|
+
ensureRuntimeBuild();
|
|
52
|
+
({ isSemverNewer } = require('../gsd-core/bin/lib/semver-compare.cjs'));
|
|
53
|
+
// Latest-version lookup is delegated to the single deterministic adapter
|
|
54
|
+
// (#498). checkLatestVersion() owns the npm-view call, the timeout/semver
|
|
55
|
+
// policy, and the package name — sourced from the baked Package Identity seam.
|
|
56
|
+
// The previous `require('../package.json').name` (#378) never yielded a name in
|
|
57
|
+
// the installed tree — at the time it resolved to the synthetic
|
|
58
|
+
// {"type":"commonjs"} marker GSD wrote at the config root, which has no `.name`,
|
|
59
|
+
// so the background check never reported updates. Since #2544 GSD writes no
|
|
60
|
+
// marker there at all, so that require would now fail to resolve outright.
|
|
61
|
+
// Either way the name must come from the baked seam, never a walk-up.
|
|
62
|
+
({ checkLatestVersion } = require('../gsd-core/bin/check-latest-version.cjs'));
|
|
63
|
+
({ PACKAGE_NAME } = require('../gsd-core/bin/lib/package-identity.cjs'));
|
|
64
|
+
} catch (e) {
|
|
65
|
+
// Runtime library missing/broken and could not self-build — degrade to the
|
|
66
|
+
// no-signal fallbacks declared above; the worker still writes a result
|
|
67
|
+
// cache record (package_name: null, update_available: false).
|
|
68
|
+
}
|
|
26
69
|
// Authoritative list of managed hooks — shared with tests to retire source-grep
|
|
27
70
|
// assertions (pending-migration-to-typed-ir [#455]).
|
|
28
71
|
// NOTE: managed-hooks-registry.cjs must be in HOOKS_TO_COPY (scripts/build-hooks.js)
|
|
@@ -63,7 +106,7 @@ try {
|
|
|
63
106
|
// (e.g., gsd-intel-*.js) must be ignored to avoid permanent stale warnings (#1750)
|
|
64
107
|
// MANAGED_HOOKS is imported from ./managed-hooks-registry.cjs above.
|
|
65
108
|
|
|
66
|
-
|
|
109
|
+
const staleHooks = [];
|
|
67
110
|
if (configDir) {
|
|
68
111
|
// #3023: the bundle's directory name is runtime-descriptor-driven (pi stages
|
|
69
112
|
// it as `gsd-hooks/`), so deriving it as `<configDir>/hooks` silently scanned
|
|
@@ -8,7 +8,25 @@ const path = require('path');
|
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const { spawn } = require('child_process');
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// #3582: gsd-core/bin/lib/package-identity.cjs is a tsc build artifact
|
|
12
|
+
// (ADR-457), gitignored and absent on a raw plugin-marketplace / git-clone
|
|
13
|
+
// install that never ran `npm run build:lib`. This SessionStart hook must
|
|
14
|
+
// DEGRADE (fall back to a generic cache filename) rather than crash session
|
|
15
|
+
// start. gsd-check-update-worker.js — the process this hook spawns — degrades
|
|
16
|
+
// identically and independently, so the shared fallback literal keeps the
|
|
17
|
+
// cache path consistent between writer and reader even in the (rare)
|
|
18
|
+
// doubly-degraded case. This try/require/ensureRuntimeBuild/require/catch
|
|
19
|
+
// shape is deliberately duplicated (not extracted to hooks/lib/) — see
|
|
20
|
+
// gsd-check-update-worker.js's identical #3582 comment for why.
|
|
21
|
+
let updateCacheFileName = 'gsd-update-check.json';
|
|
22
|
+
try {
|
|
23
|
+
const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
|
|
24
|
+
ensureRuntimeBuild();
|
|
25
|
+
({ updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs'));
|
|
26
|
+
} catch (e) {
|
|
27
|
+
// Runtime library missing/broken and could not self-build — degrade to the
|
|
28
|
+
// fallback filename above rather than crash the SessionStart hook.
|
|
29
|
+
}
|
|
12
30
|
|
|
13
31
|
const homeDir = os.homedir();
|
|
14
32
|
const cwd = process.cwd();
|
|
@@ -22,9 +22,6 @@
|
|
|
22
22
|
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
|
-
const fs = require('fs');
|
|
26
|
-
const path = require('path');
|
|
27
|
-
|
|
28
25
|
const WRITE_TOOL_RE = /write|edit|replace|create|delete|remove|append|apply|patch|insert|mkdir/i;
|
|
29
26
|
const PATH_KEY_RE = /^(path|file|file_?path|filepath|target_?path|target|dir|directory|uri|filename)$/i;
|
|
30
27
|
const PLANNING_PATH_RE = /(^|[\\/])\.planning([\\/]|$)/;
|
|
@@ -62,6 +62,15 @@ const os = require('os');
|
|
|
62
62
|
// writeCursorHooksJson so the require always resolves post-install.
|
|
63
63
|
const { resolveStatePath } = require('./lib/cursor-workspace.js');
|
|
64
64
|
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch } = require('./lib/isolation-sentinel.js');
|
|
65
|
+
const { REASON_CODE } = require('./lib/isolation-deny-reason.js');
|
|
66
|
+
// #3582: gsd-core/bin/lib/*.cjs (runtime-homes.cjs, worktree-safety.cjs,
|
|
67
|
+
// runtime-name-policy.cjs, capability-registry.cjs — required below, inside
|
|
68
|
+
// resolveIsolationEvidence and resolveFallbackIsolation) are tsc build
|
|
69
|
+
// artifacts (ADR-457), gitignored and absent on a raw plugin-marketplace /
|
|
70
|
+
// git-clone install that never ran `npm run build:lib`. Self-heal once, in
|
|
71
|
+
// evaluateRootIsolation, before any of those four requires run — see the
|
|
72
|
+
// call site below. This module itself depends on nothing under ./lib.
|
|
73
|
+
const { ensureRuntimeBuild, RuntimeBuildError } = require('../gsd-core/bin/ensure-runtime-build.cjs');
|
|
65
74
|
|
|
66
75
|
const MSG_PRESENT =
|
|
67
76
|
'GSD: Subagent session started — review .planning/STATE.md for the current phase and any blockers before acting.';
|
|
@@ -333,6 +342,37 @@ function resolveIsolationDecision(data, { clock = Date, realpath = fs.realpathSy
|
|
|
333
342
|
return { action: 'allow' };
|
|
334
343
|
}
|
|
335
344
|
|
|
345
|
+
// ─── #3566: per-install runtime marker ────────────────────────────────────────
|
|
346
|
+
// Same contract as hooks/gsd-agent-isolation-guard.js's readInstallRuntimeMarker
|
|
347
|
+
// (mirroring src/model-resolver.cts #2297): bin/install.js writes
|
|
348
|
+
// `<install>/gsd-core/.gsd-runtime` beside VERSION for every runtime install;
|
|
349
|
+
// this hook ships at `<install>/hooks/`, so the marker is the `gsd-core` sibling
|
|
350
|
+
// of this file's own directory — the same sibling-layout assumption the
|
|
351
|
+
// require('../gsd-core/bin/lib/…') calls below already make. Epic #3473 B3 owns
|
|
352
|
+
// consolidating every marker reader into one shared seam.
|
|
353
|
+
let _installMarkerCache; // undefined = unread; null = known absent; string = value
|
|
354
|
+
|
|
355
|
+
function readInstallRuntimeMarker() {
|
|
356
|
+
if (_installMarkerCache !== undefined) return _installMarkerCache;
|
|
357
|
+
try {
|
|
358
|
+
const markerPath = path.join(__dirname, '..', 'gsd-core', '.gsd-runtime');
|
|
359
|
+
const raw = fs.readFileSync(markerPath, 'utf-8').trim();
|
|
360
|
+
_installMarkerCache = raw || null;
|
|
361
|
+
} catch {
|
|
362
|
+
// No marker: dev/source tree, or an install predating #2297 — "no signal
|
|
363
|
+
// from this rung", never a resolution failure.
|
|
364
|
+
_installMarkerCache = null;
|
|
365
|
+
}
|
|
366
|
+
return _installMarkerCache;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Test seam — same contract as model-resolver.cts's #2297 seam; the dev/source
|
|
370
|
+
// tree has no marker file, so spawned-hook tests (fresh process, no marker)
|
|
371
|
+
// are unaffected.
|
|
372
|
+
function _setInstallRuntimeMarkerForTests(value) {
|
|
373
|
+
_installMarkerCache = value;
|
|
374
|
+
}
|
|
375
|
+
|
|
336
376
|
/**
|
|
337
377
|
* Conservative fallback resolution used when the #3045 sentinel is absent or
|
|
338
378
|
* stale for `root`: re-derive isolation from the registry CAPABILITY, gated
|
|
@@ -355,7 +395,8 @@ function resolveIsolationDecision(data, { clock = Date, realpath = fs.realpathSy
|
|
|
355
395
|
* otherwise legitimate dispatches, unlike `hooks/gsd-agent-isolation-guard.js`,
|
|
356
396
|
* which degrades an undeterminable runtime to inert (#3045 MAJOR 2). Aligned
|
|
357
397
|
* here: an explicit signal is now required — `GSD_RUNTIME` > config.json
|
|
358
|
-
* `runtime` key >
|
|
398
|
+
* `runtime` key > the per-install `.gsd-runtime` marker (#3566) >
|
|
399
|
+
* `~/.gsd/defaults.json` `runtime` (mirrors the Claude hook's
|
|
359
400
|
* `resolveRuntimeIdentity`; `bin/install.js`'s `writeNonClaudeDefaults`
|
|
360
401
|
* persists the installed runtime there for every non-Claude install,
|
|
361
402
|
* including Cursor, so a REAL Cursor+GSD install still resolves confidently
|
|
@@ -372,6 +413,13 @@ function resolveFallbackIsolation(root, configPath) {
|
|
|
372
413
|
if (!runtimeId && parsedConfig && typeof parsedConfig === 'object' && 'runtime' in parsedConfig) {
|
|
373
414
|
runtimeId = resolveRuntimeNameFromCandidates(parsedConfig.runtime) || null;
|
|
374
415
|
}
|
|
416
|
+
if (!runtimeId) {
|
|
417
|
+
// #3566: the per-install marker, above the host-wide defaults — same fix as
|
|
418
|
+
// hooks/gsd-agent-isolation-guard.js's resolveRuntimeIdentity. defaults.json
|
|
419
|
+
// is host-wide and names whichever runtime installed LAST (#2840's poison);
|
|
420
|
+
// the marker describes THIS install (written for every runtime since #2297).
|
|
421
|
+
runtimeId = resolveRuntimeNameFromCandidates(readInstallRuntimeMarker()) || null;
|
|
422
|
+
}
|
|
375
423
|
if (!runtimeId) {
|
|
376
424
|
try {
|
|
377
425
|
const defaultsPath = path.join(os.homedir(), '.gsd', 'defaults.json');
|
|
@@ -419,6 +467,28 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
419
467
|
}
|
|
420
468
|
if (!isGsdProject) return { action: 'allow' };
|
|
421
469
|
|
|
470
|
+
// #3582: self-heal the compiled runtime library BEFORE any of its four
|
|
471
|
+
// downstream requires (resolveFallbackIsolation's two, resolveIsolationEvidence's
|
|
472
|
+
// two — reached only below this point). Checked separately from the
|
|
473
|
+
// sentinel/fallback try block below so a build failure surfaces its own
|
|
474
|
+
// actionable RuntimeBuildError message rather than being folded into the
|
|
475
|
+
// generic "could not read or resolve ... configuration" deny reason (the
|
|
476
|
+
// #3050 misreport this issue exists to fix). Still fails closed either way.
|
|
477
|
+
try {
|
|
478
|
+
ensureRuntimeBuild();
|
|
479
|
+
} catch (err) {
|
|
480
|
+
return {
|
|
481
|
+
action: 'deny',
|
|
482
|
+
reason:
|
|
483
|
+
`GSD subagent isolation guard: cannot resolve this project's dispatch-isolation ` +
|
|
484
|
+
`configuration because the GSD runtime library failed to self-build. ` +
|
|
485
|
+
`${err instanceof RuntimeBuildError ? err.message : String(err && err.message || err)} ` +
|
|
486
|
+
`Refusing to allow this subagent to spawn until the runtime library is built — a guard ` +
|
|
487
|
+
`that cannot verify must not answer "safe" (#3050).`,
|
|
488
|
+
reasonCode: REASON_CODE.RUNTIME_BUILD_FAILED,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
422
492
|
let declaredIsolation;
|
|
423
493
|
try {
|
|
424
494
|
// #3045 BLOCKER fix: a fresh sentinel is authoritative for THIS
|
|
@@ -439,6 +509,7 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
439
509
|
`Refusing to allow this subagent to spawn without being able to verify whether ` +
|
|
440
510
|
`isolation is required — a guard that cannot verify must not answer "safe" (#3050). ` +
|
|
441
511
|
`Retry once the project configuration is readable.`,
|
|
512
|
+
reasonCode: REASON_CODE.CONFIG_UNREADABLE,
|
|
442
513
|
};
|
|
443
514
|
}
|
|
444
515
|
|
|
@@ -455,6 +526,7 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
455
526
|
`"harness-worktree", but the subagentStart payload for this dispatch carries no usable ` +
|
|
456
527
|
`subagent_type. Refusing to allow it to spawn without being able to confirm whether it ` +
|
|
457
528
|
`is a GSD executor — a guard that cannot verify must not answer "safe" (#3050).`,
|
|
529
|
+
reasonCode: REASON_CODE.NO_SUBAGENT_TYPE,
|
|
458
530
|
};
|
|
459
531
|
}
|
|
460
532
|
|
|
@@ -471,6 +543,7 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
471
543
|
`could not be determined (git did not respond). Refusing to allow subagent_type=` +
|
|
472
544
|
`"${subagentType}" to spawn without being able to verify isolation — a guard that ` +
|
|
473
545
|
`cannot verify must not answer "safe" (#3050). Retry once git is responsive.`,
|
|
546
|
+
reasonCode: REASON_CODE.CANNOT_DETERMINE_ISOLATION,
|
|
474
547
|
};
|
|
475
548
|
}
|
|
476
549
|
|
|
@@ -483,6 +556,7 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
483
556
|
`directly, with no consent and no warning. Start an isolated session first (the ` +
|
|
484
557
|
`"--worktree" CLI flag or the "/worktree" chat command; Cursor manages these worktrees ` +
|
|
485
558
|
`under "~/.cursor/worktrees/") and retry.`,
|
|
559
|
+
reasonCode: REASON_CODE.NOT_ISOLATED_WORKTREE,
|
|
486
560
|
};
|
|
487
561
|
}
|
|
488
562
|
|
|
@@ -532,7 +606,7 @@ function main() {
|
|
|
532
606
|
decision = { action: 'allow' };
|
|
533
607
|
}
|
|
534
608
|
if (decision.action === 'deny') {
|
|
535
|
-
const out = { permission: 'deny', user_message: decision.reason };
|
|
609
|
+
const out = { permission: 'deny', user_message: decision.reason, reason_code: decision.reasonCode };
|
|
536
610
|
if (additionalContext !== null) out.additional_context = additionalContext;
|
|
537
611
|
process.stdout.write(JSON.stringify(out));
|
|
538
612
|
return;
|
|
@@ -557,4 +631,5 @@ module.exports = {
|
|
|
557
631
|
resolveFallbackIsolation,
|
|
558
632
|
resolveIsolationEvidence,
|
|
559
633
|
getWorkspaceRoots,
|
|
634
|
+
_setInstallRuntimeMarkerForTests,
|
|
560
635
|
};
|
|
@@ -20,13 +20,14 @@
|
|
|
20
20
|
|
|
21
21
|
'use strict';
|
|
22
22
|
|
|
23
|
-
let raw = '';
|
|
24
23
|
const stdinTimeout = setTimeout(() => {
|
|
25
24
|
process.exit(0);
|
|
26
25
|
}, 10000);
|
|
27
26
|
|
|
28
27
|
process.stdin.setEncoding('utf8');
|
|
29
|
-
|
|
28
|
+
// Drain stdin (puts the stream into flowing mode so 'end' fires); the
|
|
29
|
+
// payload itself is unused — this hook responds unconditionally.
|
|
30
|
+
process.stdin.on('data', () => {});
|
|
30
31
|
process.stdin.on('end', () => {
|
|
31
32
|
clearTimeout(stdinTimeout);
|
|
32
33
|
try {
|
|
@@ -11,26 +11,14 @@
|
|
|
11
11
|
// The goal is to surface suspicious content so the orchestrator can inspect it,
|
|
12
12
|
// not to create false-positive deadlocks.
|
|
13
13
|
|
|
14
|
-
const fs = require('fs');
|
|
15
14
|
const path = require('path');
|
|
16
15
|
|
|
17
|
-
// Prompt injection patterns
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/override\s+(system|previous)\s+(prompt|instructions)/i,
|
|
24
|
-
/you\s+are\s+now\s+(?:a|an|the)\s+/i,
|
|
25
|
-
/act\s+as\s+(?:a|an|the)\s+(?!plan|phase|wave)/i,
|
|
26
|
-
/pretend\s+(?:you(?:'re| are)\s+|to\s+be\s+)/i,
|
|
27
|
-
/from\s+now\s+on,?\s+you\s+(?:are|will|should|must)/i,
|
|
28
|
-
/(?:print|output|reveal|show|display|repeat)\s+(?:your\s+)?(?:system\s+)?(?:prompt|instructions)/i,
|
|
29
|
-
/<\/?(?:system|assistant|human)>/i,
|
|
30
|
-
/\[SYSTEM\]/i,
|
|
31
|
-
/\[INST\]/i,
|
|
32
|
-
/<<\s*SYS\s*>>/i,
|
|
33
|
-
];
|
|
16
|
+
// Prompt injection patterns — shared with gsd-read-injection-scanner.js via
|
|
17
|
+
// hooks/lib/injection-patterns.js so the two surfaces cannot drift (#3504).
|
|
18
|
+
// Deliberately a subset of security.cjs's set: hooks stay loadable without the
|
|
19
|
+
// compiled lib tree. Staging of the lib helper is allowlisted in
|
|
20
|
+
// GSD_HOOK_LIB_FILES (bin/install.js).
|
|
21
|
+
const { INJECTION_PATTERNS } = require('./lib/injection-patterns.js');
|
|
34
22
|
|
|
35
23
|
// #2304: Kimi's native hook bus delivers Kimi's tool vocabulary in the payload
|
|
36
24
|
// (Write → WriteFile, Edit/MultiEdit → StrReplaceFile) while the [[hooks]]
|
|
@@ -153,8 +141,21 @@ process.stdin.on('end', () => {
|
|
|
153
141
|
process.exit(0);
|
|
154
142
|
}
|
|
155
143
|
|
|
156
|
-
// Get the content being written
|
|
157
|
-
|
|
144
|
+
// Get the content being written. #3504 (isolated review finding 3): the
|
|
145
|
+
// bare `||` chain handed a NON-STRING truthy `content` straight to
|
|
146
|
+
// pattern.test(), where ToString can throw ("Cannot convert object to a
|
|
147
|
+
// primitive value" for `{"toString": null}`) into the outer catch — the
|
|
148
|
+
// exact crash-to-allow class #2547/#2595 hardened inside
|
|
149
|
+
// normalizeKimiPayload, unreached on this read. Guarded selection: take
|
|
150
|
+
// the first field that is a string, or String-coerces without throwing,
|
|
151
|
+
// so a poisoned `content` no longer shadows a real `new_string`.
|
|
152
|
+
let content = '';
|
|
153
|
+
for (const candidate of [data.tool_input?.content, data.tool_input?.new_string]) {
|
|
154
|
+
if (typeof candidate === 'string' && candidate) { content = candidate; break; }
|
|
155
|
+
if (candidate && typeof candidate !== 'string') {
|
|
156
|
+
try { const s = String(candidate); if (s) { content = s; break; } } catch { /* keep looking */ }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
158
159
|
if (!content) {
|
|
159
160
|
process.exit(0);
|
|
160
161
|
}
|