@opengsd/gsd-core 1.7.0 → 1.9.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/.opencode/plugins/gsd-core.js +45 -1
- package/README.md +2 -0
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-codebase-mapper.md +1 -1
- package/agents/gsd-debug-session-manager.md +78 -4
- package/agents/gsd-debugger.md +87 -29
- package/agents/gsd-executor.md +49 -9
- package/agents/gsd-intel-updater.md +3 -3
- package/agents/gsd-phase-researcher.md +4 -2
- package/agents/gsd-plan-checker.md +20 -0
- package/agents/gsd-planner.md +44 -59
- package/agents/gsd-project-researcher.md +2 -2
- package/agents/gsd-ui-auditor.md +0 -40
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +1338 -135
- package/commands/gsd/ai-integration-phase.md +1 -1
- package/commands/gsd/mempalace-capture.md +9 -5
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/plan-phase.md +5 -3
- package/commands/gsd/plan-review-convergence.md +7 -2
- package/gsd-core/bin/gsd-tools.cjs +2690 -2472
- package/gsd-core/bin/lib/adapter-imperative.cjs +8 -1
- package/gsd-core/bin/lib/agent-command-router.cjs +20 -5
- package/gsd-core/bin/lib/api-coverage.cjs +360 -53
- package/gsd-core/bin/lib/audit.cjs +8 -8
- package/gsd-core/bin/lib/broken-windows.cjs +716 -0
- package/gsd-core/bin/lib/capability-command-router.cjs +733 -0
- package/gsd-core/bin/lib/capability-consent.cjs +40 -1
- package/gsd-core/bin/lib/capability-lifecycle.cjs +58 -0
- package/gsd-core/bin/lib/capability-loader.cjs +23 -1
- package/gsd-core/bin/lib/capability-registry.cjs +1450 -160
- package/gsd-core/bin/lib/capability-trust.cjs +468 -33
- package/gsd-core/bin/lib/capability-validator.cjs +882 -6
- package/gsd-core/bin/lib/capability-writer.cjs +6 -1
- package/gsd-core/bin/lib/check-command-router.cjs +140 -27
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +15 -0
- package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +209 -31
- package/gsd-core/bin/lib/claude-orchestration.cjs +203 -25
- package/gsd-core/bin/lib/command-aliases.cjs +14 -0
- package/gsd-core/bin/lib/commands.cjs +326 -21
- package/gsd-core/bin/lib/config-loader.cjs +214 -30
- package/gsd-core/bin/lib/config.cjs +158 -22
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/decisions.cjs +32 -8
- package/gsd-core/bin/lib/docs.cjs +6 -0
- package/gsd-core/bin/lib/estimate-cli.cjs +336 -0
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +14 -2
- package/gsd-core/bin/lib/frontmatter.cjs +125 -15
- package/gsd-core/bin/lib/gap-checker.cjs +17 -2
- package/gsd-core/bin/lib/host-integration.cjs +215 -8
- package/gsd-core/bin/lib/init.cjs +155 -66
- package/gsd-core/bin/lib/install-engine.cjs +299 -23
- package/gsd-core/bin/lib/install-profiles.cjs +239 -1
- package/gsd-core/bin/lib/installer-migrations/005-opencode-baseline-commands-dir.cjs +146 -0
- package/gsd-core/bin/lib/installer-migrations/006-pi-extension-cjs-to-js.cjs +91 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +44 -5
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +107 -0
- package/gsd-core/bin/lib/milestone.cjs +248 -14
- package/gsd-core/bin/lib/model-catalog.cjs +69 -4
- package/gsd-core/bin/lib/model-resolver.cjs +189 -7
- package/gsd-core/bin/lib/observability/logger.cjs +7 -2
- package/gsd-core/bin/lib/onboard-projection.cjs +11 -8
- package/gsd-core/bin/lib/phase-command-router.cjs +10 -1
- package/gsd-core/bin/lib/phase-estimation.cjs +398 -0
- package/gsd-core/bin/lib/phase-id.cjs +304 -9
- package/gsd-core/bin/lib/phase.cjs +258 -17
- package/gsd-core/bin/lib/plan-drift-guard.cjs +1 -1
- package/gsd-core/bin/lib/plan-scan.cjs +70 -2
- package/gsd-core/bin/lib/planning-workspace.cjs +9 -2
- package/gsd-core/bin/lib/profile-output.cjs +34 -8
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +927 -0
- package/gsd-core/bin/lib/review-lane-invocation.cjs +348 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +594 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +114 -32
- package/gsd-core/bin/lib/roadmap-parser.cjs +61 -10
- package/gsd-core/bin/lib/roadmap.cjs +23 -7
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +38 -5
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +23 -9
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +156 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +15 -2
- package/gsd-core/bin/lib/smart-entry.cjs +70 -5
- package/gsd-core/bin/lib/state-document.cjs +171 -24
- package/gsd-core/bin/lib/state-transition.cjs +50 -11
- package/gsd-core/bin/lib/state.cjs +206 -32
- package/gsd-core/bin/lib/surface.cjs +51 -9
- package/gsd-core/bin/lib/uat-predicate.cjs +6 -4
- package/gsd-core/bin/lib/uat.cjs +428 -11
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +2 -2
- package/gsd-core/bin/lib/unusable-input.cjs +216 -0
- package/gsd-core/bin/lib/validate.cjs +44 -8
- package/gsd-core/bin/lib/verification.cjs +163 -31
- package/gsd-core/bin/lib/verify.cjs +348 -42
- package/gsd-core/bin/lib/worktree-safety.cjs +360 -15
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +4 -15
- package/gsd-core/bin/shared/model-catalog.json +5 -0
- package/gsd-core/bin/shared/runtime-aliases.manifest.json +5 -0
- package/gsd-core/references/api-coverage.md +37 -7
- package/gsd-core/references/checkpoints.md +1 -1
- package/gsd-core/references/common-bug-patterns.md +13 -0
- package/gsd-core/references/context-budget.md +40 -0
- package/gsd-core/references/debugger-bug-taxonomy.md +111 -0
- package/gsd-core/references/debugger-fix-acceptance.md +157 -0
- package/gsd-core/references/debugger-philosophy.md +1 -0
- package/gsd-core/references/debugger-prevention.md +98 -0
- package/gsd-core/references/debugger-rca-branching.md +98 -0
- package/gsd-core/references/debugger-repro-hardening.md +130 -0
- package/gsd-core/references/debugger-sbfl.md +110 -0
- package/gsd-core/references/debugger-semantic-recall.md +81 -0
- package/gsd-core/references/execute-phase-quota-recovery.md +55 -0
- package/gsd-core/references/execute-phase-requirement-revert.md +8 -0
- package/gsd-core/references/execute-phase-response-language.md +7 -0
- package/gsd-core/references/gate-prompts.md +6 -3
- package/gsd-core/references/model-profile-resolution.md +64 -13
- package/gsd-core/references/offer-next.md +88 -0
- package/gsd-core/references/planner-antipatterns.md +6 -0
- package/gsd-core/references/planner-mvp-mode.md +12 -13
- package/gsd-core/references/planner-preconditions.md +156 -0
- package/gsd-core/references/planner-reversibility.md +132 -0
- package/gsd-core/references/planning-config.md +2 -1
- package/gsd-core/references/reviewer-instances.md +28 -19
- package/gsd-core/references/runtime-aware-dispatch.md +42 -0
- package/gsd-core/references/skeleton-template.md +1 -1
- package/gsd-core/references/thinking-models-planning.md +3 -1
- package/gsd-core/references/ui-consideration-probe.md +2 -2
- package/gsd-core/references/worktree-branch-check.md +4 -4
- package/gsd-core/templates/DEBUG.md +5 -3
- package/gsd-core/templates/summary-minimal.md +4 -0
- package/gsd-core/templates/summary-standard.md +4 -0
- package/gsd-core/templates/summary.md +7 -0
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +3 -1
- package/gsd-core/workflows/add-todo.md +32 -1
- package/gsd-core/workflows/ai-integration-phase.md +8 -6
- package/gsd-core/workflows/audit-fix.md +6 -2
- package/gsd-core/workflows/audit-milestone.md +8 -0
- package/gsd-core/workflows/autonomous.md +19 -15
- package/gsd-core/workflows/check-todos.md +5 -3
- package/gsd-core/workflows/cleanup.md +7 -1
- package/gsd-core/workflows/code-review-fix.md +14 -6
- package/gsd-core/workflows/code-review.md +93 -24
- package/gsd-core/workflows/complete-milestone.md +3 -0
- package/gsd-core/workflows/debug.md +35 -7
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discovery-phase.md +7 -0
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -4
- package/gsd-core/workflows/discuss-phase/modes/auto.md +0 -6
- package/gsd-core/workflows/discuss-phase/templates/context.md +16 -2
- package/gsd-core/workflows/discuss-phase-assumptions.md +18 -9
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +7 -1
- package/gsd-core/workflows/docs-update.md +9 -0
- package/gsd-core/workflows/eval-review.md +4 -1
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +4 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +160 -0
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +4 -4
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -2
- package/gsd-core/workflows/execute-phase.md +110 -149
- package/gsd-core/workflows/execute-plan.md +20 -8
- package/gsd-core/workflows/explore.md +4 -0
- package/gsd-core/workflows/extract-learnings.md +21 -0
- package/gsd-core/workflows/graduation.md +3 -0
- package/gsd-core/workflows/health.md +7 -1
- package/gsd-core/workflows/help/modes/full.md +9 -5
- package/gsd-core/workflows/import.md +11 -2
- package/gsd-core/workflows/inbox.md +7 -0
- package/gsd-core/workflows/ingest-docs.md +19 -10
- package/gsd-core/workflows/manager.md +3 -1
- package/gsd-core/workflows/map-codebase.md +17 -10
- package/gsd-core/workflows/mvp-phase.md +3 -0
- package/gsd-core/workflows/new-milestone.md +79 -23
- package/gsd-core/workflows/new-project.md +28 -19
- package/gsd-core/workflows/new-workspace.md +3 -1
- package/gsd-core/workflows/next.md +5 -2
- package/gsd-core/workflows/onboard.md +3 -0
- package/gsd-core/workflows/plan-phase.md +56 -51
- package/gsd-core/workflows/plan-review-convergence.md +61 -12
- package/gsd-core/workflows/plant-seed.md +3 -0
- package/gsd-core/workflows/profile-user.md +7 -1
- package/gsd-core/workflows/progress.md +31 -3
- package/gsd-core/workflows/quick.md +33 -10
- package/gsd-core/workflows/remove-workspace.md +3 -0
- package/gsd-core/workflows/review.md +172 -585
- package/gsd-core/workflows/scan.md +10 -2
- package/gsd-core/workflows/secure-phase.md +13 -2
- package/gsd-core/workflows/settings-integrations.md +3 -0
- package/gsd-core/workflows/settings.md +3 -0
- package/gsd-core/workflows/ship.md +88 -11
- package/gsd-core/workflows/sketch.md +3 -0
- package/gsd-core/workflows/smart-entry.md +4 -1
- package/gsd-core/workflows/spike.md +7 -1
- package/gsd-core/workflows/ui-phase.md +11 -2
- package/gsd-core/workflows/ui-review.md +11 -1
- package/gsd-core/workflows/undo.md +7 -0
- package/gsd-core/workflows/update.md +106 -5
- package/gsd-core/workflows/validate-phase.md +13 -2
- package/gsd-core/workflows/verify-phase.md +2 -2
- package/gsd-core/workflows/verify-work.md +15 -4
- package/hooks/dist/gsd-context-monitor.js +27 -9
- package/hooks/dist/gsd-cursor-session-start.js +6 -2
- package/hooks/dist/gsd-cursor-stop.js +6 -2
- package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
- package/hooks/dist/gsd-graphify-update.sh +9 -0
- package/hooks/dist/gsd-phase-boundary.sh +14 -2
- package/hooks/dist/gsd-prompt-guard.js +101 -2
- package/hooks/dist/gsd-read-guard.js +100 -2
- package/hooks/dist/gsd-read-injection-scanner.js +109 -2
- package/hooks/dist/gsd-statusline.js +97 -9
- package/hooks/dist/gsd-workflow-guard.js +110 -6
- package/hooks/dist/gsd-worktree-path-guard.js +132 -8
- package/hooks/dist/lib/cursor-workspace.js +74 -0
- package/hooks/gsd-context-monitor.js +27 -9
- package/hooks/gsd-cursor-session-start.js +6 -2
- package/hooks/gsd-cursor-stop.js +6 -2
- package/hooks/gsd-cursor-subagent-start.js +6 -2
- package/hooks/gsd-graphify-update.sh +9 -0
- package/hooks/gsd-phase-boundary.sh +14 -2
- package/hooks/gsd-prompt-guard.js +101 -2
- package/hooks/gsd-read-guard.js +100 -2
- package/hooks/gsd-read-injection-scanner.js +109 -2
- package/hooks/gsd-statusline.js +97 -9
- package/hooks/gsd-workflow-guard.js +110 -6
- package/hooks/gsd-worktree-path-guard.js +132 -8
- package/hooks/lib/cursor-workspace.js +74 -0
- package/package.json +10 -8
- package/pi/gsd.cjs +34 -3
- package/scripts/changeset/lint.cjs +1 -0
- package/scripts/changeset/parse.cjs +26 -0
- package/scripts/check-coverage-gate.cjs +51 -0
- package/scripts/check-glossary-refs.cjs +244 -0
- package/scripts/ci-rebase-check.cjs +48 -4
- package/scripts/ci-test-scope.cjs +67 -17
- package/scripts/gen-adr-index.cjs +528 -0
- package/scripts/gen-capability-matrix.cjs +26 -2
- package/scripts/gen-capability-registry.cjs +132 -34
- package/scripts/gen-emitted-baseline.cjs +145 -0
- package/scripts/gen-test-timings.cjs +201 -0
- package/scripts/lint-compiled-artifact-sync.cjs +146 -0
- package/scripts/lint-emitted-drift-ack.cjs +149 -0
- package/scripts/lint-fix-has-regression-test.cjs +131 -0
- package/scripts/lint-portable-timeout.cjs +140 -0
- package/scripts/lint-resolution-provenance.cjs +9 -0
- package/scripts/lint-test-file-count.allowlist.json +1 -0
- package/scripts/mutation-matrix.cjs +4 -0
- package/scripts/prompt-injection-scan.sh +6 -0
- package/scripts/registry-schema.cjs +57 -8
- package/scripts/release-notes/conventional-title.cjs +19 -1
- package/scripts/release-notes/format-github-release-notes.cjs +7 -3
- package/scripts/release-tarball-smoke.cjs +18 -11
- package/scripts/run-tests.cjs +420 -58
- package/scripts/workflow-size.cjs +16 -8
- package/skills/gsd-ai-integration-phase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +9 -5
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-plan-phase/SKILL.md +5 -3
- package/skills/gsd-plan-review-convergence/SKILL.md +7 -2
- package/vscode/package.json +1 -1
- package/scripts/gen-golden-install-parity-zcode.cjs +0 -77
- package/scripts/update-size-baseline.cjs +0 -68
|
@@ -298,7 +298,16 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
298
298
|
const mismatchMsg = '## Post-Planning Gap Analysis\n\nextracted 0 of N — possible format mismatch in CONTEXT.md decisions block.\n';
|
|
299
299
|
// If there are also requirement items, include them in the return with the
|
|
300
300
|
// mismatch summary appended, so the caller still sees requirement coverage.
|
|
301
|
-
|
|
301
|
+
// #2334 HIGH 1: gate on `ghostReqIds.length > 0` too — identical defect to
|
|
302
|
+
// the one fixed at #2316-6b (~34 lines below, at the `items.length === 0`
|
|
303
|
+
// early return): a phase whose EVERY cited REQ-ID is unregistered has
|
|
304
|
+
// `items.length === 0` (all its requirement items were filtered out at
|
|
305
|
+
// ~line 297) but still has real ghost rows to report. Without this guard,
|
|
306
|
+
// a single malformed `<decisions>` line in CONTEXT.md made an all-ghost
|
|
307
|
+
// phase's ghost rows silently vanish (this could-not-parse branch fell
|
|
308
|
+
// through to the bare `mismatchMsg`-only return below, dropping ghost
|
|
309
|
+
// rows that the general path further down correctly surfaces).
|
|
310
|
+
if (items.length > 0 || ghostReqIds.length > 0) {
|
|
302
311
|
const rows = sortRows([
|
|
303
312
|
...detectCoverage(items, planText),
|
|
304
313
|
...ghostReqIds.map(id => ({ source: 'REQUIREMENTS.md', item: id, status: 'Missing from REQUIREMENTS.md' })),
|
|
@@ -325,7 +334,13 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
325
334
|
};
|
|
326
335
|
}
|
|
327
336
|
// #1365: if no items at all, surface a clean no-check message.
|
|
328
|
-
|
|
337
|
+
// #2316-6b: this must NOT fire when `ghostReqIds` is non-empty — a phase
|
|
338
|
+
// whose EVERY cited REQ-ID is unregistered has `items.length === 0` (all
|
|
339
|
+
// its requirement items were filtered out at ~line 297) but still has real
|
|
340
|
+
// ghost rows to report below. Without this guard, an all-orphan phase
|
|
341
|
+
// reported LESS than a partially-orphan one (which falls through to the
|
|
342
|
+
// general path further down and correctly surfaces its ghost rows).
|
|
343
|
+
if (items.length === 0 && ghostReqIds.length === 0) {
|
|
329
344
|
return {
|
|
330
345
|
enabled: true,
|
|
331
346
|
rows: [],
|
|
@@ -37,7 +37,32 @@ const HOST_INTEGRATION_AXES = Object.freeze({
|
|
|
37
37
|
stateIO: Object.freeze(['filesystem', 'sandboxed-storage', 'session-log-append']),
|
|
38
38
|
transport: Object.freeze(['mcp', 'native-extension']),
|
|
39
39
|
runtime: Object.freeze(['node', 'bun', 'sandboxed-web', 'python', 'go', 'rust', 'electron', 'other']),
|
|
40
|
-
subagentToolkit: Object.freeze(['full', 'read-only']),
|
|
40
|
+
subagentToolkit: Object.freeze(['full', 'read-only', 'built-in-only']),
|
|
41
|
+
// ADR-1239 amendment (#2481): how reasoning effort reaches this host.
|
|
42
|
+
// `argv` — deliverable as an argument on the host's own invocation.
|
|
43
|
+
// `none` — the host exposes no reasoning-effort mechanism.
|
|
44
|
+
// `undocumented` is NOT a member here; it is the corpus-wide sentinel above.
|
|
45
|
+
// A config-file-only surface deliberately has NO vocabulary member: the only
|
|
46
|
+
// host that ever had one (Gemini CLI's thinkingConfig) was removed as a sunset
|
|
47
|
+
// runtime in #1928/#1996, and neither its successor Antigravity CLI nor ZCode
|
|
48
|
+
// documents a reasoning setting. Adding a member with no host would be a guess.
|
|
49
|
+
effortSurface: Object.freeze(['argv', 'none']),
|
|
50
|
+
// ADR-1239 Codex-binding amendment (#2584): a `dispatch` sub-field — not a new
|
|
51
|
+
// axis — declaring how a host isolates concurrent same-wave executors.
|
|
52
|
+
// `harness-worktree` — the host's own harness creates + binds a git worktree
|
|
53
|
+
// per executor; GSD passes the host's own isolation flag and calls no git
|
|
54
|
+
// itself (host-driven fan-out).
|
|
55
|
+
// `orchestrator-worktree` — GSD itself process-spawns each executor with an
|
|
56
|
+
// explicit working directory into a worktree GSD created, validated, and
|
|
57
|
+
// merges (GSD-driven fan-out; concurrency is OS-level, not the host's).
|
|
58
|
+
// `none` — no isolation primitive; same-wave plans run inline/sequentially
|
|
59
|
+
// (the #853 flatten rule).
|
|
60
|
+
// `undocumented` is NOT a member here; it is the corpus-wide sentinel above.
|
|
61
|
+
// Mechanism-specific ("worktree"), not abstract — same "name only what a
|
|
62
|
+
// host actually has" rule that kept effortSurface from guessing a
|
|
63
|
+
// config-file member above. A future non-worktree isolation mechanism adds a
|
|
64
|
+
// `*-container` member then, evidence-backed.
|
|
65
|
+
isolation: Object.freeze(['harness-worktree', 'orchestrator-worktree', 'none']),
|
|
41
66
|
});
|
|
42
67
|
const INTERFACE_POINTS = Object.freeze(['command', 'dispatch', 'model', 'hooks', 'state', 'artifact']);
|
|
43
68
|
// ---------------------------------------------------------------------------
|
|
@@ -47,43 +72,47 @@ const INTERFACE_POINTS = Object.freeze(['command', 'dispatch', 'model', 'hooks',
|
|
|
47
72
|
const SAFE_DEFAULTS = {
|
|
48
73
|
embeddingMode: 'declarative',
|
|
49
74
|
commandSurface: 'prose-only',
|
|
50
|
-
dispatch: { namedDispatch: false, nested: false, maxDepth: 0, background: false, subagentToolkit: 'read-only', backgroundDispatch: false },
|
|
75
|
+
dispatch: { namedDispatch: false, nested: false, maxDepth: 0, background: false, subagentToolkit: 'read-only', backgroundDispatch: false, isolation: 'none' },
|
|
51
76
|
modelMode: 'passive',
|
|
52
77
|
hookBus: 'none',
|
|
53
78
|
stateIO: 'session-log-append',
|
|
54
79
|
transport: 'mcp',
|
|
55
80
|
runtime: 'node',
|
|
81
|
+
effortSurface: 'none',
|
|
56
82
|
};
|
|
57
83
|
const PROFILE_BASELINES = Object.freeze({
|
|
58
84
|
'programmatic-cli': Object.freeze({
|
|
59
85
|
embeddingMode: 'imperative',
|
|
60
86
|
commandSurface: 'slash-file',
|
|
61
|
-
dispatch: Object.freeze({ namedDispatch: true, nested: true, maxDepth: -1, background: true, subagentToolkit: 'full', backgroundDispatch: true }),
|
|
87
|
+
dispatch: Object.freeze({ namedDispatch: true, nested: true, maxDepth: -1, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none' }),
|
|
62
88
|
modelMode: 'passive',
|
|
63
89
|
hookBus: 'host',
|
|
64
90
|
stateIO: 'filesystem',
|
|
65
91
|
transport: 'mcp',
|
|
66
92
|
runtime: 'node',
|
|
93
|
+
effortSurface: 'none',
|
|
67
94
|
}),
|
|
68
95
|
'declarative-cli': Object.freeze({
|
|
69
96
|
embeddingMode: 'declarative',
|
|
70
97
|
commandSurface: 'slash-file',
|
|
71
|
-
dispatch: Object.freeze({ namedDispatch: true, nested: false, maxDepth: 1, background: false, subagentToolkit: 'full', backgroundDispatch: false }),
|
|
98
|
+
dispatch: Object.freeze({ namedDispatch: true, nested: false, maxDepth: 1, background: false, subagentToolkit: 'full', backgroundDispatch: false, isolation: 'none' }),
|
|
72
99
|
modelMode: 'passive',
|
|
73
100
|
hookBus: 'host',
|
|
74
101
|
stateIO: 'filesystem',
|
|
75
102
|
transport: 'mcp',
|
|
76
103
|
runtime: 'node',
|
|
104
|
+
effortSurface: 'none',
|
|
77
105
|
}),
|
|
78
106
|
'ide': Object.freeze({
|
|
79
107
|
embeddingMode: 'imperative',
|
|
80
108
|
commandSurface: 'palette',
|
|
81
|
-
dispatch: Object.freeze({ namedDispatch: true, nested: true, maxDepth: 5, background: true, subagentToolkit: 'full', backgroundDispatch: true }),
|
|
109
|
+
dispatch: Object.freeze({ namedDispatch: true, nested: true, maxDepth: 5, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none' }),
|
|
82
110
|
modelMode: 'active',
|
|
83
111
|
hookBus: 'engine',
|
|
84
112
|
stateIO: 'sandboxed-storage',
|
|
85
113
|
transport: 'mcp',
|
|
86
114
|
runtime: 'sandboxed-web',
|
|
115
|
+
effortSurface: 'none',
|
|
87
116
|
}),
|
|
88
117
|
});
|
|
89
118
|
// ---------------------------------------------------------------------------
|
|
@@ -136,6 +165,13 @@ function degradationFor(point, axes) {
|
|
|
136
165
|
return { level: 'degraded', fallback: 'flat dispatch — waves run inline' };
|
|
137
166
|
}
|
|
138
167
|
case 'model': {
|
|
168
|
+
// NOTE (#2481): the effortSurface axis is deliberately NOT folded into this
|
|
169
|
+
// level. `modelMode` has graded interface point 3 since Phase A, and every
|
|
170
|
+
// existing consumer reads it as "can GSD drive model selection". Widening it
|
|
171
|
+
// to also mean "…and deliver effort" silently redefines an established
|
|
172
|
+
// contract — an `active` host with no declared effort surface would flip
|
|
173
|
+
// from `full` to `absent`. Effort is negotiated on its own axis and read
|
|
174
|
+
// from `effective.effortSurface` by the consumers that care.
|
|
139
175
|
const mm = axes.modelMode;
|
|
140
176
|
if (mm === 'active')
|
|
141
177
|
return { level: 'full', fallback: '' };
|
|
@@ -199,12 +235,13 @@ const DEFAULT_ENGINE = {
|
|
|
199
235
|
axes: {
|
|
200
236
|
embeddingMode: 'imperative',
|
|
201
237
|
commandSurface: 'slash-file',
|
|
202
|
-
dispatch: { namedDispatch: true, nested: true, maxDepth: -1, background: true, subagentToolkit: 'full', backgroundDispatch: true },
|
|
238
|
+
dispatch: { namedDispatch: true, nested: true, maxDepth: -1, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none' },
|
|
203
239
|
modelMode: 'active',
|
|
204
240
|
hookBus: 'host',
|
|
205
241
|
stateIO: 'filesystem',
|
|
206
242
|
transport: 'mcp',
|
|
207
243
|
runtime: 'node',
|
|
244
|
+
effortSurface: 'argv',
|
|
208
245
|
},
|
|
209
246
|
known: HOST_INTEGRATION_AXES,
|
|
210
247
|
};
|
|
@@ -264,6 +301,15 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
264
301
|
if (hostVal === 'active' && engineVal === 'passive')
|
|
265
302
|
return 'passive';
|
|
266
303
|
}
|
|
304
|
+
// For effortSurface: 'argv' > 'none'. An engine that cannot deliver the
|
|
305
|
+
// host's richer channel caps the result to what it can drive.
|
|
306
|
+
if (axis === 'effortSurface') {
|
|
307
|
+
const RANK = { argv: 1, none: 0 };
|
|
308
|
+
const hr = RANK[hostVal] ?? 0;
|
|
309
|
+
const er = RANK[engineVal] ?? 0;
|
|
310
|
+
if (hr > er)
|
|
311
|
+
return engineVal;
|
|
312
|
+
}
|
|
267
313
|
return hostVal;
|
|
268
314
|
}
|
|
269
315
|
// Negotiate all scalar axes
|
|
@@ -274,6 +320,7 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
274
320
|
const effectiveStateIO = negotiateScalar('stateIO');
|
|
275
321
|
const effectiveTransport = negotiateScalar('transport');
|
|
276
322
|
const effectiveRuntime = negotiateScalar('runtime');
|
|
323
|
+
const effectiveEffortSurface = negotiateScalar('effortSurface');
|
|
277
324
|
// ---------------------------------------------------------------------------
|
|
278
325
|
// Dispatch struct negotiation
|
|
279
326
|
// ---------------------------------------------------------------------------
|
|
@@ -287,6 +334,7 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
287
334
|
let effectiveBackgroundDispatch;
|
|
288
335
|
let effectiveSubagentToolkit;
|
|
289
336
|
let effectiveMaxDepth;
|
|
337
|
+
let effectiveIsolation;
|
|
290
338
|
if (hostDispatch === null) {
|
|
291
339
|
// Host didn't declare dispatch at all — fail-closed to most-restrictive values
|
|
292
340
|
warnings.push(`host did not declare 'dispatch'`);
|
|
@@ -296,6 +344,7 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
296
344
|
effectiveBackgroundDispatch = false;
|
|
297
345
|
effectiveSubagentToolkit = 'read-only';
|
|
298
346
|
effectiveMaxDepth = 0;
|
|
347
|
+
effectiveIsolation = 'none';
|
|
299
348
|
}
|
|
300
349
|
else {
|
|
301
350
|
// N1: observability warnings for 'undocumented' sentinel on dispatch fields
|
|
@@ -314,6 +363,16 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
314
363
|
if (hostDispatch.backgroundDispatch === 'undocumented') {
|
|
315
364
|
warnings.push(`dispatch.backgroundDispatch is undocumented — degraded closed`);
|
|
316
365
|
}
|
|
366
|
+
if (hostDispatch.isolation === 'undocumented') {
|
|
367
|
+
warnings.push(`dispatch.isolation is undocumented — degraded closed (none)`);
|
|
368
|
+
}
|
|
369
|
+
if (hostDispatch.maxDepth === UNDOCUMENTED) {
|
|
370
|
+
// #2603: maxDepth was the one dispatch sub-axis with no sentinel-specific
|
|
371
|
+
// warning, so a descriptor carrying the documented fail-closed sentinel was
|
|
372
|
+
// reported as `missing or not a number` — indistinguishable from a genuinely
|
|
373
|
+
// malformed descriptor. Six shipped runtimes use the sentinel here.
|
|
374
|
+
warnings.push(`dispatch.maxDepth is undocumented — degraded closed (0)`);
|
|
375
|
+
}
|
|
317
376
|
effectiveNamedDispatch = (hostDispatch.namedDispatch === true) && engineDispatch.namedDispatch;
|
|
318
377
|
effectiveNested = (hostDispatch.nested === true) && engineDispatch.nested;
|
|
319
378
|
effectiveBackground = (hostDispatch.background === true) && engineDispatch.background;
|
|
@@ -323,10 +382,22 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
323
382
|
const hostToolkit = hostDispatch.subagentToolkit === 'full' ? 'full' : 'read-only';
|
|
324
383
|
const engineToolkit = engineDispatch.subagentToolkit === 'read-only' ? 'read-only' : 'full';
|
|
325
384
|
effectiveSubagentToolkit = (hostToolkit === 'read-only' || engineToolkit === 'read-only') ? 'read-only' : 'full';
|
|
326
|
-
//
|
|
385
|
+
// isolation: effective = the host's declared value only if it is a known
|
|
386
|
+
// valid vocabulary member; otherwise 'none'. NOT host && engine gated —
|
|
387
|
+
// GSD owns the vocabulary, so "engine-known" == "in the valid set" (this
|
|
388
|
+
// still satisfies effective ⊆ host-declared ∩ engine-known).
|
|
389
|
+
const hostIso = hostDispatch.isolation;
|
|
390
|
+
effectiveIsolation = (typeof hostIso === 'string' && HOST_INTEGRATION_AXES.isolation.includes(hostIso))
|
|
391
|
+
? hostIso
|
|
392
|
+
: 'none';
|
|
393
|
+
// maxDepth: missing/non-number/non-finite → 0 + warning. The documented
|
|
394
|
+
// 'undocumented' sentinel also degrades to 0, but is reported by the
|
|
395
|
+
// sentinel-specific warning above rather than as a malformed value (#2603).
|
|
327
396
|
let hostMaxDepth;
|
|
328
397
|
if (typeof hostDispatch.maxDepth !== 'number' || !Number.isFinite(hostDispatch.maxDepth)) {
|
|
329
|
-
|
|
398
|
+
if (hostDispatch.maxDepth !== UNDOCUMENTED) {
|
|
399
|
+
warnings.push(`host dispatch.maxDepth is missing or not a number — treating as 0`);
|
|
400
|
+
}
|
|
330
401
|
hostMaxDepth = 0;
|
|
331
402
|
}
|
|
332
403
|
else {
|
|
@@ -352,6 +423,7 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
352
423
|
background: effectiveBackground,
|
|
353
424
|
subagentToolkit: effectiveSubagentToolkit,
|
|
354
425
|
backgroundDispatch: effectiveBackgroundDispatch,
|
|
426
|
+
isolation: effectiveIsolation,
|
|
355
427
|
};
|
|
356
428
|
// ---------------------------------------------------------------------------
|
|
357
429
|
// Assemble effective axes
|
|
@@ -365,6 +437,7 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
365
437
|
stateIO: effectiveStateIO,
|
|
366
438
|
transport: effectiveTransport,
|
|
367
439
|
runtime: effectiveRuntime,
|
|
440
|
+
effortSurface: effectiveEffortSurface,
|
|
368
441
|
};
|
|
369
442
|
// ---------------------------------------------------------------------------
|
|
370
443
|
// Compute points (fresh objects — mutation-safe)
|
|
@@ -395,6 +468,63 @@ function shouldFlattenDispatch(dispatch) {
|
|
|
395
468
|
return !canBackground;
|
|
396
469
|
}
|
|
397
470
|
// ---------------------------------------------------------------------------
|
|
471
|
+
// resolveDispatchType — ADR-1239 / epic #2505 Phase 4 (Option A)
|
|
472
|
+
//
|
|
473
|
+
// Maps a requested GSD subagent name (e.g. "gsd-planner") to the type an
|
|
474
|
+
// Agent() call should actually use on the CURRENT runtime. On runtimes whose
|
|
475
|
+
// descriptor declares `hostIntegration.dispatch.namedDispatch: true` (Claude,
|
|
476
|
+
// OpenCode, Cursor, …), the requested name is returned unchanged — those hosts
|
|
477
|
+
// can dispatch GSD's named subagents directly. On runtimes with
|
|
478
|
+
// `namedDispatch: false` (kimi-code — only three built-in subagents
|
|
479
|
+
// `coder`/`explore`/`plan`, per moonshotai.github.io/kimi-code/en/customization/
|
|
480
|
+
// agents), the name is mapped to the closest built-in by role-suffix
|
|
481
|
+
// heuristic. The persona rides the existing `${AGENT_SKILLS_*}` prompt
|
|
482
|
+
// injection (Phase 3 / #2510) regardless of the resolved type, so the
|
|
483
|
+
// dispatcher does not need to know the persona — only the toolkit tier.
|
|
484
|
+
//
|
|
485
|
+
// This is Option A of the Phase 4 design (per-workflow runtime detection via
|
|
486
|
+
// `gsd_run query resolve-dispatch-type`), not Option B (PreToolUse mutation) —
|
|
487
|
+
// Kimi Code's documented hook API supports only allow/deny on PreToolUse, not
|
|
488
|
+
// tool_input rewriting, so a hook-based remap is infeasible (see #2508).
|
|
489
|
+
//
|
|
490
|
+
// Fail-closed: unknown dispatch shape or missing namedDispatch axis ⇒ return
|
|
491
|
+
// the requested name unchanged (named-dispatch is the GSD default; degrading
|
|
492
|
+
// to it on unknown runtimes preserves behavior for every runtime already in
|
|
493
|
+
// the field).
|
|
494
|
+
// ---------------------------------------------------------------------------
|
|
495
|
+
// Role-suffix → built-in mapping. Order matters: the first match wins.
|
|
496
|
+
// `plan`-tier agents plan/design without touching files; `explore`-tier agents
|
|
497
|
+
// are read-only; everything else (executors, writers, fixers, debuggers) maps
|
|
498
|
+
// to `coder` (the general-purpose built-in with the full tool set).
|
|
499
|
+
const DISPATCH_TYPE_SUFFIX_MAP = Object.freeze([
|
|
500
|
+
[/-?(planner|roadmapper|selector|spec)$/i, 'plan'],
|
|
501
|
+
[/-?(researcher|mapper|checker|verifier|auditor|analyzer|synthesizer|profiler|curator|classifier|reviewer)$/i, 'explore'],
|
|
502
|
+
]);
|
|
503
|
+
// Names that are already generic (not gsd-*) and should map to the
|
|
504
|
+
// general-purpose built-in on built-in-only runtimes.
|
|
505
|
+
const GENERIC_NAMES_TO_CODER = Object.freeze(new Set([
|
|
506
|
+
'general-purpose', 'general', 'default', 'sonnet', 'opus', 'haiku',
|
|
507
|
+
]));
|
|
508
|
+
function resolveDispatchType(requested, dispatch) {
|
|
509
|
+
if (typeof requested !== 'string' || requested.length === 0)
|
|
510
|
+
return 'coder';
|
|
511
|
+
// Built-in-only runtime (EXPLICIT namedDispatch: false, e.g. kimi-code):
|
|
512
|
+
// map to coder/explore/plan by suffix heuristic.
|
|
513
|
+
if (dispatch && typeof dispatch === 'object' && dispatch.namedDispatch === false) {
|
|
514
|
+
if (GENERIC_NAMES_TO_CODER.has(requested))
|
|
515
|
+
return 'coder';
|
|
516
|
+
for (const [pattern, builtin] of DISPATCH_TYPE_SUFFIX_MAP) {
|
|
517
|
+
if (pattern.test(requested))
|
|
518
|
+
return builtin;
|
|
519
|
+
}
|
|
520
|
+
return 'coder';
|
|
521
|
+
}
|
|
522
|
+
// Named-dispatch runtime (namedDispatch: true OR unknown/absent): use the
|
|
523
|
+
// requested name unchanged. Absent namedDispatch degrades to named-dispatch
|
|
524
|
+
// (the GSD default) so every runtime already in the field keeps working.
|
|
525
|
+
return requested;
|
|
526
|
+
}
|
|
527
|
+
// ---------------------------------------------------------------------------
|
|
398
528
|
// Managed-hook event surface per hookEvents dialect (ADR-1239 / ADR-1016)
|
|
399
529
|
// ---------------------------------------------------------------------------
|
|
400
530
|
// Host-fireable MANAGED-hook events per `hookEvents` dialect. `hookEvents` is the
|
|
@@ -492,6 +622,81 @@ function extensionEventSurfaceFor(extensionEvents) {
|
|
|
492
622
|
return null;
|
|
493
623
|
return EXTENSION_EVENT_SURFACES[extensionEvents] || null;
|
|
494
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* Resolve an `orchestratorExec` descriptor + target cwd (+ optional executor
|
|
627
|
+
* prompt) into a concrete argv/cwd shape for a process-spawn primitive.
|
|
628
|
+
*
|
|
629
|
+
* Fail-closed: never throws, always returns a discriminated result. When
|
|
630
|
+
* `cwdFlag` is a non-empty string, `[cwdFlag, cwd]` is appended to `args`
|
|
631
|
+
* exactly once (e.g. codex: `exec --cd <cwd>`); when `cwdFlag` is `null` or
|
|
632
|
+
* absent (e.g. kimi-code, which binds via the spawned process's own cwd —
|
|
633
|
+
* "process-cwd" case), no flag is appended and `cwd` is returned for the
|
|
634
|
+
* caller to bind via the subprocess's own working-directory option.
|
|
635
|
+
*
|
|
636
|
+
* Prompt passing (Phase 3, #2627) is descriptor data for the same reason the
|
|
637
|
+
* cwd flag is: the confirmed `orchestrator-worktree` hosts disagree on the
|
|
638
|
+
* shape. `codex exec "<prompt>"` and `opencode run "<prompt>"` take it
|
|
639
|
+
* positionally; `kimi --print --prompt "<p>"` and Kimi Code's `kimi -p "<p>"`
|
|
640
|
+
* take a flag. Encoding that as `promptFlag` keeps the scheduler free of the
|
|
641
|
+
* per-host branch ADR-1239 exists to remove. Omit `prompt` entirely and the
|
|
642
|
+
* resolution is byte-identical to Phase 2's (the unconsumed-resolver shape).
|
|
643
|
+
*
|
|
644
|
+
* Argv order is base args → cwd flag → prompt, so the prompt stays the final
|
|
645
|
+
* positional token for the hosts that read it that way.
|
|
646
|
+
*/
|
|
647
|
+
function resolveOrchestratorExec(orchestratorExec, cwd, prompt) {
|
|
648
|
+
if (!orchestratorExec || typeof orchestratorExec !== 'object' || Array.isArray(orchestratorExec)) {
|
|
649
|
+
return { ok: false, reason: 'missing_command' };
|
|
650
|
+
}
|
|
651
|
+
const oe = orchestratorExec;
|
|
652
|
+
if (typeof oe.command !== 'string' || oe.command.length === 0) {
|
|
653
|
+
return { ok: false, reason: 'missing_command' };
|
|
654
|
+
}
|
|
655
|
+
if (typeof cwd !== 'string' || cwd.length === 0) {
|
|
656
|
+
return { ok: false, reason: 'invalid_cwd' };
|
|
657
|
+
}
|
|
658
|
+
if (oe.args !== undefined && (!Array.isArray(oe.args) || !oe.args.every((a) => typeof a === 'string'))) {
|
|
659
|
+
return { ok: false, reason: 'invalid_args' };
|
|
660
|
+
}
|
|
661
|
+
if (oe.cwdFlag !== undefined && oe.cwdFlag !== null && typeof oe.cwdFlag !== 'string') {
|
|
662
|
+
return { ok: false, reason: 'invalid_cwd_flag' };
|
|
663
|
+
}
|
|
664
|
+
if (oe.promptFlag !== undefined && oe.promptFlag !== null && typeof oe.promptFlag !== 'string') {
|
|
665
|
+
return { ok: false, reason: 'invalid_prompt_flag' };
|
|
666
|
+
}
|
|
667
|
+
// An executor spawned with no instruction is a hang, not a degraded run —
|
|
668
|
+
// fail closed rather than launching a prompt-less process.
|
|
669
|
+
if (prompt !== undefined && (typeof prompt !== 'string' || prompt.length === 0)) {
|
|
670
|
+
return { ok: false, reason: 'invalid_prompt' };
|
|
671
|
+
}
|
|
672
|
+
// Leading-dash guard, mirroring worktree-safety.cts's `unsafe_leading_dash`
|
|
673
|
+
// check on git arguments. A positional prompt (or a cwd) beginning with '-'
|
|
674
|
+
// is parsed by the spawned CLI as a FLAG, not a value — the same failure the
|
|
675
|
+
// git path already rejects, and for the same reason: `--` end-of-options
|
|
676
|
+
// support is inconsistent across these CLIs, so rejecting outright is the
|
|
677
|
+
// portable fix rather than relying on a separator. Applied to the resolver
|
|
678
|
+
// (not just its current caller) because this is a general descriptor->argv
|
|
679
|
+
// seam: a future caller must not have to rediscover the hazard.
|
|
680
|
+
if (typeof prompt === 'string' && prompt.startsWith('-')) {
|
|
681
|
+
return { ok: false, reason: 'unsafe_leading_dash_prompt' };
|
|
682
|
+
}
|
|
683
|
+
if (cwd.startsWith('-')) {
|
|
684
|
+
return { ok: false, reason: 'unsafe_leading_dash_cwd' };
|
|
685
|
+
}
|
|
686
|
+
const baseArgs = Array.isArray(oe.args) ? [...oe.args] : [];
|
|
687
|
+
const args = typeof oe.cwdFlag === 'string' && oe.cwdFlag.length > 0
|
|
688
|
+
? [...baseArgs, oe.cwdFlag, cwd]
|
|
689
|
+
: baseArgs;
|
|
690
|
+
if (typeof prompt === 'string') {
|
|
691
|
+
if (typeof oe.promptFlag === 'string' && oe.promptFlag.length > 0) {
|
|
692
|
+
args.push(oe.promptFlag, prompt);
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
args.push(prompt);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return { ok: true, command: oe.command, args, cwd };
|
|
699
|
+
}
|
|
495
700
|
module.exports = {
|
|
496
701
|
PROTOCOL_VERSION,
|
|
497
702
|
UNDOCUMENTED,
|
|
@@ -505,6 +710,8 @@ module.exports = {
|
|
|
505
710
|
profileOf,
|
|
506
711
|
negotiateHostCapabilities,
|
|
507
712
|
shouldFlattenDispatch,
|
|
713
|
+
resolveDispatchType,
|
|
508
714
|
hookEventSurfaceFor,
|
|
509
715
|
extensionEventSurfaceFor,
|
|
716
|
+
resolveOrchestratorExec,
|
|
510
717
|
};
|