gsd-pi 2.76.0 → 2.77.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/README.md +45 -25
- package/dist/claude-cli-check.js +32 -3
- package/dist/mcp-server.d.ts +7 -0
- package/dist/mcp-server.js +35 -1
- package/dist/onboarding.js +45 -0
- package/dist/resource-loader.d.ts +1 -1
- package/dist/resource-loader.js +2 -8
- package/dist/resources/agents/researcher.md +1 -1
- package/dist/resources/extensions/claude-code-cli/readiness.js +31 -8
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
- package/dist/resources/extensions/gsd/auto/loop.js +9 -0
- package/dist/resources/extensions/gsd/auto/phases.js +104 -11
- package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
- package/dist/resources/extensions/gsd/auto/session.js +22 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +53 -16
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
- package/dist/resources/extensions/gsd/auto-start.js +58 -57
- package/dist/resources/extensions/gsd/auto-verification.js +33 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
- package/dist/resources/extensions/gsd/auto.js +70 -28
- package/dist/resources/extensions/gsd/blocked-models.js +68 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +93 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +12 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +84 -23
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
- package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +54 -89
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
- package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
- package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
- package/dist/resources/extensions/gsd/db-writer.js +88 -16
- package/dist/resources/extensions/gsd/doctor-git-checks.js +23 -29
- package/dist/resources/extensions/gsd/doctor-providers.js +51 -5
- package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +1 -0
- package/dist/resources/extensions/gsd/error-classifier.js +31 -3
- package/dist/resources/extensions/gsd/exec-history.js +120 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/gsd-db.js +168 -23
- package/dist/resources/extensions/gsd/guided-flow.js +190 -1
- package/dist/resources/extensions/gsd/health-widget.js +4 -1
- package/dist/resources/extensions/gsd/hook-emitter.js +108 -0
- package/dist/resources/extensions/gsd/init-wizard.js +15 -1
- package/dist/resources/extensions/gsd/key-manager.js +28 -0
- package/dist/resources/extensions/gsd/memory-backfill.js +126 -0
- package/dist/resources/extensions/gsd/memory-store.js +19 -0
- package/dist/resources/extensions/gsd/model-router.js +36 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
- package/dist/resources/extensions/gsd/preferences-types.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
- package/dist/resources/extensions/gsd/preferences.js +17 -17
- package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
- package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
- package/dist/resources/extensions/gsd/state.js +43 -4
- package/dist/resources/extensions/gsd/token-counter.js +22 -5
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +26 -1
- package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
- package/dist/resources/extensions/gsd/workflow-templates/spike.md +6 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
- package/dist/resources/extensions/search-the-web/command-search-provider.js +5 -4
- package/dist/resources/extensions/search-the-web/native-search.js +45 -13
- package/dist/resources/skills/api-design/SKILL.md +190 -0
- package/dist/resources/skills/create-mcp-server/SKILL.md +121 -0
- package/dist/resources/skills/decompose-into-slices/SKILL.md +139 -0
- package/dist/resources/skills/dependency-upgrade/SKILL.md +158 -0
- package/dist/resources/skills/design-an-interface/SKILL.md +102 -0
- package/dist/resources/skills/forensics/SKILL.md +153 -0
- package/dist/resources/skills/grill-me/SKILL.md +93 -0
- package/dist/resources/skills/handoff/SKILL.md +121 -0
- package/dist/resources/skills/observability/SKILL.md +174 -0
- package/dist/resources/skills/security-review/SKILL.md +181 -0
- package/dist/resources/skills/spike-wrap-up/SKILL.md +138 -0
- package/dist/resources/skills/tdd/SKILL.md +112 -0
- package/dist/resources/skills/verify-before-complete/SKILL.md +98 -0
- package/dist/resources/skills/write-docs/SKILL.md +82 -0
- package/dist/resources/skills/write-milestone-brief/SKILL.md +135 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +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/server.js +1 -1
- package/dist/welcome-screen.js +6 -1
- package/dist/wizard.js +2 -0
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
- package/packages/mcp-server/dist/remote-questions.js +732 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +7 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +70 -8
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.d.ts +14 -0
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +49 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +163 -25
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -3
- package/packages/mcp-server/src/mcp-server.test.ts +67 -0
- package/packages/mcp-server/src/remote-questions.test.ts +294 -0
- package/packages/mcp-server/src/remote-questions.ts +916 -0
- package/packages/mcp-server/src/server.ts +89 -14
- package/packages/mcp-server/src/session-manager.ts +43 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
- package/packages/mcp-server/src/workflow-tools.ts +215 -43
- package/packages/mcp-server/tsconfig.test.json +19 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +12 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +30 -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-agent-core/src/agent-loop.ts +14 -0
- package/packages/pi-agent-core/src/types.ts +34 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/models/custom.d.ts +38 -0
- package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/custom.js +41 -0
- package/packages/pi-ai/dist/models/custom.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +13 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +13 -4
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +16 -1
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +12 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +164 -14
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +15 -3
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +67 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +16 -3
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +67 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +289 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +1 -0
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/models/custom.ts +42 -0
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
- package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
- package/packages/pi-ai/src/providers/anthropic.ts +15 -4
- package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
- package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
- package/packages/pi-ai/src/providers/simple-options.ts +17 -1
- package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
- package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
- package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +200 -23
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +12 -2
- package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +84 -0
- package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +15 -5
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +84 -0
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +16 -5
- package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +363 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +32 -2
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +4 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +35 -2
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +233 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +205 -2
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts +53 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.js +337 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +234 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/index.js +1 -0
- package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js +40 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +55 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
- package/packages/pi-coding-agent/src/core/agent-session.ts +38 -2
- package/packages/pi-coding-agent/src/core/extensions/index.ts +16 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +5 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +351 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +258 -0
- package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +269 -0
- package/packages/pi-coding-agent/src/core/hooks-runner.ts +460 -0
- package/packages/pi-coding-agent/src/core/index.ts +10 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
- package/packages/pi-coding-agent/src/core/model-registry-auth-header.test.ts +44 -0
- package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
- package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
- package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
- package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
- package/packages/pi-coding-agent/src/core/settings-manager.ts +57 -0
- package/packages/pi-coding-agent/src/index.ts +16 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/scripts/link-workspace-packages.cjs +1 -0
- package/src/resources/agents/researcher.md +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +32 -8
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
- package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
- package/src/resources/extensions/gsd/auto/loop.ts +9 -0
- package/src/resources/extensions/gsd/auto/phases.ts +131 -10
- package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
- package/src/resources/extensions/gsd/auto/session.ts +35 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +71 -15
- package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
- package/src/resources/extensions/gsd/auto-start.ts +60 -68
- package/src/resources/extensions/gsd/auto-verification.ts +33 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
- package/src/resources/extensions/gsd/auto.ts +73 -28
- package/src/resources/extensions/gsd/blocked-models.ts +98 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +120 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +5 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +15 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +89 -26
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
- package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +55 -90
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
- package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
- package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
- package/src/resources/extensions/gsd/db-writer.ts +88 -17
- package/src/resources/extensions/gsd/doctor-git-checks.ts +23 -27
- package/src/resources/extensions/gsd/doctor-providers.ts +59 -6
- package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +2 -0
- package/src/resources/extensions/gsd/error-classifier.ts +36 -3
- package/src/resources/extensions/gsd/exec-history.ts +153 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
- package/src/resources/extensions/gsd/gitignore.ts +1 -1
- package/src/resources/extensions/gsd/gsd-db.ts +186 -23
- package/src/resources/extensions/gsd/guided-flow.ts +222 -1
- package/src/resources/extensions/gsd/health-widget.ts +3 -1
- package/src/resources/extensions/gsd/hook-emitter.ts +188 -0
- package/src/resources/extensions/gsd/init-wizard.ts +15 -1
- package/src/resources/extensions/gsd/journal.ts +2 -1
- package/src/resources/extensions/gsd/key-manager.ts +28 -0
- package/src/resources/extensions/gsd/memory-backfill.ts +140 -0
- package/src/resources/extensions/gsd/memory-store.ts +26 -0
- package/src/resources/extensions/gsd/model-router.ts +42 -1
- package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
- package/src/resources/extensions/gsd/preferences-types.ts +46 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
- package/src/resources/extensions/gsd/preferences.ts +17 -17
- package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
- package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
- package/src/resources/extensions/gsd/state.ts +45 -4
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +95 -1
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/blocked-models.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +68 -66
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +8 -4
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +148 -3
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +306 -1
- package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +40 -9
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-git-symlink-cwd.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/load-memory-block.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +103 -4
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
- package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
- package/src/resources/extensions/gsd/token-counter.ts +22 -5
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
- package/src/resources/extensions/gsd/tools/memory-tools.ts +31 -1
- package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +4 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- package/src/resources/extensions/gsd/workflow-templates/spike.md +6 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
- package/src/resources/extensions/search-the-web/command-search-provider.ts +5 -4
- package/src/resources/extensions/search-the-web/native-search.ts +48 -12
- package/src/resources/skills/api-design/SKILL.md +190 -0
- package/src/resources/skills/create-mcp-server/SKILL.md +121 -0
- package/src/resources/skills/decompose-into-slices/SKILL.md +139 -0
- package/src/resources/skills/dependency-upgrade/SKILL.md +158 -0
- package/src/resources/skills/design-an-interface/SKILL.md +102 -0
- package/src/resources/skills/forensics/SKILL.md +153 -0
- package/src/resources/skills/grill-me/SKILL.md +93 -0
- package/src/resources/skills/handoff/SKILL.md +121 -0
- package/src/resources/skills/observability/SKILL.md +174 -0
- package/src/resources/skills/security-review/SKILL.md +181 -0
- package/src/resources/skills/spike-wrap-up/SKILL.md +138 -0
- package/src/resources/skills/tdd/SKILL.md +112 -0
- package/src/resources/skills/verify-before-complete/SKILL.md +98 -0
- package/src/resources/skills/write-docs/SKILL.md +82 -0
- package/src/resources/skills/write-milestone-brief/SKILL.md +135 -0
- /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// GSD Compaction Snapshot — writes a ≤2 KB markdown digest of durable
|
|
2
|
+
// project state before the session context is compacted. On resume, an
|
|
3
|
+
// agent can `gsd_resume` (or Read .gsd/last-snapshot.md) to re-orient
|
|
4
|
+
// without re-deriving the same memories.
|
|
5
|
+
//
|
|
6
|
+
// Inspired by mksglu/context-mode. Independent implementation.
|
|
7
|
+
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
|
+
|
|
11
|
+
import { getActiveMemoriesRanked, type Memory } from "./memory-store.js";
|
|
12
|
+
import { listExecHistory, type ExecHistoryEntry } from "./exec-history.js";
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_SNAPSHOT_BYTES = 2048;
|
|
15
|
+
export const SNAPSHOT_FILENAME = "last-snapshot.md";
|
|
16
|
+
|
|
17
|
+
export interface SnapshotSources {
|
|
18
|
+
memories: Memory[];
|
|
19
|
+
execHistory: ExecHistoryEntry[];
|
|
20
|
+
generatedAt: Date;
|
|
21
|
+
/** Optional free-form context string (e.g. active unit id). */
|
|
22
|
+
activeContext?: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface BuildSnapshotOptions {
|
|
26
|
+
/** Hard cap in bytes (UTF-8). Default 2048. */
|
|
27
|
+
maxBytes?: number;
|
|
28
|
+
/** Memory count cap before truncation (default 6). */
|
|
29
|
+
maxMemories?: number;
|
|
30
|
+
/** Exec history cap (default 5). */
|
|
31
|
+
maxExec?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Build a priority-tiered markdown snapshot. Pure — no I/O. Tiers:
|
|
36
|
+
* 1. Active context (if any)
|
|
37
|
+
* 2. Top memories by rank
|
|
38
|
+
* 3. Recent exec runs (failures highlighted)
|
|
39
|
+
* The result is guaranteed to be <= opts.maxBytes (truncated with an
|
|
40
|
+
* ellipsis marker if necessary).
|
|
41
|
+
*/
|
|
42
|
+
export function buildSnapshot(sources: SnapshotSources, opts: BuildSnapshotOptions = {}): string {
|
|
43
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_SNAPSHOT_BYTES;
|
|
44
|
+
const maxMemories = opts.maxMemories ?? 6;
|
|
45
|
+
const maxExec = opts.maxExec ?? 5;
|
|
46
|
+
|
|
47
|
+
const lines: string[] = [];
|
|
48
|
+
lines.push(`# GSD context snapshot (${sources.generatedAt.toISOString()})`);
|
|
49
|
+
lines.push("");
|
|
50
|
+
|
|
51
|
+
if (sources.activeContext && sources.activeContext.trim().length > 0) {
|
|
52
|
+
lines.push("## Active context");
|
|
53
|
+
lines.push(sources.activeContext.trim());
|
|
54
|
+
lines.push("");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const memories = sources.memories.slice(0, maxMemories);
|
|
58
|
+
if (memories.length > 0) {
|
|
59
|
+
lines.push("## Top project memories");
|
|
60
|
+
for (const memory of memories) {
|
|
61
|
+
lines.push(`- [${memory.id}] (${memory.category}) ${memory.content.trim()}`);
|
|
62
|
+
}
|
|
63
|
+
lines.push("");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const exec = sources.execHistory.slice(0, maxExec);
|
|
67
|
+
if (exec.length > 0) {
|
|
68
|
+
lines.push("## Recent gsd_exec runs");
|
|
69
|
+
for (const entry of exec) {
|
|
70
|
+
const status = entry.timed_out
|
|
71
|
+
? "timeout"
|
|
72
|
+
: entry.exit_code === null
|
|
73
|
+
? "exit:null"
|
|
74
|
+
: `exit:${entry.exit_code}`;
|
|
75
|
+
const purpose = entry.purpose ? ` — ${entry.purpose}` : "";
|
|
76
|
+
lines.push(`- [${entry.id}] ${entry.runtime} ${status}${purpose}`);
|
|
77
|
+
}
|
|
78
|
+
lines.push("");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (memories.length === 0 && exec.length === 0 && !sources.activeContext) {
|
|
82
|
+
lines.push("_No durable memories, active context, or exec history to surface._");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return enforceByteCap(lines.join("\n").trimEnd(), maxBytes);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function enforceByteCap(input: string, maxBytes: number): string {
|
|
89
|
+
if (Buffer.byteLength(input, "utf-8") <= maxBytes) return input;
|
|
90
|
+
const marker = "\n…[truncated]";
|
|
91
|
+
const markerBytes = Buffer.byteLength(marker, "utf-8");
|
|
92
|
+
const budget = Math.max(0, maxBytes - markerBytes);
|
|
93
|
+
// Walk backwards until the trimmed string fits. utf-8 is variable-width;
|
|
94
|
+
// naive char slicing is safe for ASCII but may split a multi-byte char.
|
|
95
|
+
// Guard by decoding the trimmed Buffer and relying on the replacement-char
|
|
96
|
+
// fallback in TextDecoder (implicit via toString).
|
|
97
|
+
const buf = Buffer.from(input, "utf-8").subarray(0, budget);
|
|
98
|
+
return `${buf.toString("utf-8")}${marker}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface WriteSnapshotOptions extends BuildSnapshotOptions {
|
|
102
|
+
activeContext?: string | null;
|
|
103
|
+
now?: () => Date;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface WriteSnapshotResult {
|
|
107
|
+
path: string;
|
|
108
|
+
bytes: number;
|
|
109
|
+
memories: number;
|
|
110
|
+
execRuns: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function writeCompactionSnapshot(
|
|
114
|
+
baseDir: string,
|
|
115
|
+
opts: WriteSnapshotOptions = {},
|
|
116
|
+
): WriteSnapshotResult {
|
|
117
|
+
const memories = safeGetMemories();
|
|
118
|
+
const execHistory = safeListExec(baseDir);
|
|
119
|
+
const content = buildSnapshot(
|
|
120
|
+
{
|
|
121
|
+
memories,
|
|
122
|
+
execHistory,
|
|
123
|
+
generatedAt: (opts.now ?? (() => new Date()))(),
|
|
124
|
+
activeContext: opts.activeContext ?? null,
|
|
125
|
+
},
|
|
126
|
+
opts,
|
|
127
|
+
);
|
|
128
|
+
const gsdDir = resolve(baseDir, ".gsd");
|
|
129
|
+
if (!existsSync(gsdDir)) mkdirSync(gsdDir, { recursive: true });
|
|
130
|
+
const path = resolve(gsdDir, SNAPSHOT_FILENAME);
|
|
131
|
+
const finalContent = `${content}\n`;
|
|
132
|
+
writeFileSync(path, finalContent, "utf-8");
|
|
133
|
+
return {
|
|
134
|
+
path,
|
|
135
|
+
bytes: Buffer.byteLength(finalContent, "utf-8"),
|
|
136
|
+
memories: memories.length,
|
|
137
|
+
execRuns: execHistory.length,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function readCompactionSnapshot(baseDir: string): string | null {
|
|
142
|
+
const path = resolve(baseDir, ".gsd", SNAPSHOT_FILENAME);
|
|
143
|
+
if (!existsSync(path)) return null;
|
|
144
|
+
try {
|
|
145
|
+
return readFileSync(path, "utf-8");
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function safeGetMemories(): Memory[] {
|
|
152
|
+
try {
|
|
153
|
+
return getActiveMemoriesRanked(12);
|
|
154
|
+
} catch {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function safeListExec(baseDir: string): ExecHistoryEntry[] {
|
|
160
|
+
try {
|
|
161
|
+
return listExecHistory(baseDir);
|
|
162
|
+
} catch {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -32,11 +32,13 @@ export interface TaskMetadata {
|
|
|
32
32
|
// ─── Unit Type → Default Tier Mapping ────────────────────────────────────────
|
|
33
33
|
|
|
34
34
|
const UNIT_TYPE_TIERS: Record<string, ComplexityTier> = {
|
|
35
|
-
// Tier 1 — Light:
|
|
36
|
-
"complete-slice": "light",
|
|
35
|
+
// Tier 1 — Light: compact verification turns
|
|
37
36
|
"run-uat": "light",
|
|
38
37
|
|
|
39
|
-
// Tier 2 — Standard: research, routine discussion
|
|
38
|
+
// Tier 2 — Standard: research, routine discussion, slice completion
|
|
39
|
+
// complete-slice can carry large inlined context; avoid routing it to the
|
|
40
|
+
// cheapest "light" model by default (#4520).
|
|
41
|
+
"complete-slice": "standard",
|
|
40
42
|
"discuss-milestone": "standard",
|
|
41
43
|
"discuss-slice": "standard",
|
|
42
44
|
"research-milestone": "standard",
|
|
@@ -227,6 +227,18 @@ export async function nextDecisionId(): Promise<string> {
|
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
/** Synchronous variant for use inside db.transaction(). */
|
|
231
|
+
function nextDecisionIdSync(adapter: ReturnType<typeof import('./gsd-db.js')._getAdapter>): string {
|
|
232
|
+
if (!adapter) return 'D001';
|
|
233
|
+
const row = adapter
|
|
234
|
+
.prepare('SELECT MAX(CAST(SUBSTR(id, 2) AS INTEGER)) as max_num FROM decisions')
|
|
235
|
+
.get();
|
|
236
|
+
const maxNum = row ? (row['max_num'] as number | null) : null;
|
|
237
|
+
if (maxNum == null || isNaN(maxNum)) return 'D001';
|
|
238
|
+
const next = maxNum + 1;
|
|
239
|
+
return `D${String(next).padStart(3, '0')}`;
|
|
240
|
+
}
|
|
241
|
+
|
|
230
242
|
// ─── Next Requirement ID ─────────────────────────────────────────────────
|
|
231
243
|
|
|
232
244
|
/**
|
|
@@ -363,6 +375,18 @@ export async function saveRequirementToDb(
|
|
|
363
375
|
}
|
|
364
376
|
}
|
|
365
377
|
|
|
378
|
+
// ─── Async Mutex for Decision Saves ───────────────────────────────────────
|
|
379
|
+
//
|
|
380
|
+
// Serializes the entire saveDecisionToDb operation (ID generation + DB upsert
|
|
381
|
+
// + file read + markdown regeneration + file write) so that parallel callers
|
|
382
|
+
// cannot interleave and produce a last-writer-wins race on DECISIONS.md.
|
|
383
|
+
let _decisionSaveLock: Promise<unknown> = Promise.resolve();
|
|
384
|
+
|
|
385
|
+
/** Reset the mutex — only for tests. */
|
|
386
|
+
export function _resetDecisionSaveLock(): void {
|
|
387
|
+
_decisionSaveLock = Promise.resolve();
|
|
388
|
+
}
|
|
389
|
+
|
|
366
390
|
// ─── Save Decision to DB + Regenerate Markdown ────────────────────────────
|
|
367
391
|
|
|
368
392
|
export interface SaveDecisionFields {
|
|
@@ -381,9 +405,10 @@ export interface SaveDecisionFields {
|
|
|
381
405
|
* Save a new decision to DB and regenerate DECISIONS.md.
|
|
382
406
|
* Auto-assigns the next ID via nextDecisionId().
|
|
383
407
|
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
* and
|
|
408
|
+
* Concurrency: uses an async mutex (promise chain) to serialize the entire
|
|
409
|
+
* operation — ID generation, DB upsert, file read, markdown regeneration,
|
|
410
|
+
* and file write — preventing parallel callers from overwriting each other's
|
|
411
|
+
* output (last-writer-wins race condition).
|
|
387
412
|
*
|
|
388
413
|
* Returns the assigned ID.
|
|
389
414
|
*/
|
|
@@ -391,23 +416,25 @@ export async function saveDecisionToDb(
|
|
|
391
416
|
fields: SaveDecisionFields,
|
|
392
417
|
basePath: string,
|
|
393
418
|
): Promise<{ id: string }> {
|
|
419
|
+
// Serialize via async mutex: each call waits for the previous one to
|
|
420
|
+
// complete before starting, preventing interleaved DB + file writes.
|
|
421
|
+
let release: () => void;
|
|
422
|
+
const prev = _decisionSaveLock;
|
|
423
|
+
_decisionSaveLock = new Promise<void>(r => { release = r; });
|
|
424
|
+
|
|
394
425
|
try {
|
|
395
|
-
|
|
426
|
+
await prev;
|
|
427
|
+
} catch {
|
|
428
|
+
// Previous call failed — proceed regardless; the lock chain must continue.
|
|
429
|
+
}
|
|
396
430
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const id = db.transaction(() => {
|
|
400
|
-
const adapter = db._getAdapter();
|
|
401
|
-
if (!adapter) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
431
|
+
try {
|
|
432
|
+
const db = await import('./gsd-db.js');
|
|
402
433
|
|
|
403
|
-
|
|
404
|
-
.prepare('SELECT MAX(CAST(SUBSTR(id, 2) AS INTEGER)) as max_num FROM decisions')
|
|
405
|
-
.get();
|
|
406
|
-
const maxNum = row ? (row['max_num'] as number | null) : null;
|
|
407
|
-
const nextId = (maxNum == null || isNaN(maxNum))
|
|
408
|
-
? 'D001'
|
|
409
|
-
: `D${String(maxNum + 1).padStart(3, '0')}`;
|
|
434
|
+
const adapter = db._getAdapter();
|
|
410
435
|
|
|
436
|
+
const id = db.transaction(() => {
|
|
437
|
+
const nextId = nextDecisionIdSync(adapter);
|
|
411
438
|
db.upsertDecision({
|
|
412
439
|
id: nextId,
|
|
413
440
|
when_context: fields.when_context ?? '',
|
|
@@ -421,11 +448,11 @@ export async function saveDecisionToDb(
|
|
|
421
448
|
superseded_by: null,
|
|
422
449
|
});
|
|
423
450
|
|
|
451
|
+
|
|
424
452
|
return nextId;
|
|
425
453
|
});
|
|
426
454
|
|
|
427
455
|
// Fetch all decisions (including superseded for the full register)
|
|
428
|
-
const adapter = db._getAdapter();
|
|
429
456
|
let allDecisions: Decision[] = [];
|
|
430
457
|
if (adapter) {
|
|
431
458
|
const rows = adapter.prepare('SELECT * FROM decisions ORDER BY seq').all();
|
|
@@ -502,10 +529,54 @@ export async function saveDecisionToDb(
|
|
|
502
529
|
clearPathCache();
|
|
503
530
|
clearParseCache();
|
|
504
531
|
|
|
532
|
+
// ADR-013 dual-write: keep the memory store in sync with every decision
|
|
533
|
+
// persisted via the legacy gsd_save_decision path. Without this, prompts
|
|
534
|
+
// that still call gsd_save_decision (discuss.md, plan-milestone.md,
|
|
535
|
+
// guided-plan-slice.md, et al. during the deprecation window) would
|
|
536
|
+
// create decisions rows invisible to memory_query and loadMemoryBlock.
|
|
537
|
+
// Best-effort — never throw, never roll back the decision on failure.
|
|
538
|
+
try {
|
|
539
|
+
const { createMemory } = await import('./memory-store.js');
|
|
540
|
+
const decisionText = (fields.decision ?? '').trim();
|
|
541
|
+
const choiceText = (fields.choice ?? '').trim();
|
|
542
|
+
const rationaleText = (fields.rationale ?? '').trim();
|
|
543
|
+
const contentParts: string[] = [];
|
|
544
|
+
if (decisionText) contentParts.push(decisionText);
|
|
545
|
+
if (choiceText) contentParts.push(`Chose: ${choiceText}.`);
|
|
546
|
+
if (rationaleText) contentParts.push(`Rationale: ${rationaleText}.`);
|
|
547
|
+
const content = contentParts.join(' ').slice(0, 600);
|
|
548
|
+
if (content) {
|
|
549
|
+
createMemory({
|
|
550
|
+
category: 'architecture',
|
|
551
|
+
content,
|
|
552
|
+
scope: fields.scope || 'project',
|
|
553
|
+
confidence: 0.85,
|
|
554
|
+
structuredFields: {
|
|
555
|
+
sourceDecisionId: id,
|
|
556
|
+
when_context: fields.when_context ?? '',
|
|
557
|
+
scope: fields.scope,
|
|
558
|
+
decision: fields.decision,
|
|
559
|
+
choice: fields.choice,
|
|
560
|
+
rationale: fields.rationale,
|
|
561
|
+
made_by: fields.made_by ?? 'agent',
|
|
562
|
+
revisable: fields.revisable ?? '',
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
} catch (mirrorErr) {
|
|
567
|
+
logError('manifest', 'memory-store mirror write failed (non-fatal)', {
|
|
568
|
+
fn: 'saveDecisionToDb',
|
|
569
|
+
decisionId: id,
|
|
570
|
+
error: String((mirrorErr as Error).message),
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
505
574
|
return { id };
|
|
506
575
|
} catch (err) {
|
|
507
576
|
logError('manifest', 'saveDecisionToDb failed', { fn: 'saveDecisionToDb', error: String((err as Error).message) });
|
|
508
577
|
throw err;
|
|
578
|
+
} finally {
|
|
579
|
+
release!();
|
|
509
580
|
}
|
|
510
581
|
}
|
|
511
582
|
|
|
@@ -4,7 +4,7 @@ import { join, sep } from "node:path";
|
|
|
4
4
|
import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js";
|
|
5
5
|
import { loadFile } from "./files.js";
|
|
6
6
|
import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js";
|
|
7
|
-
import { isDbAvailable,
|
|
7
|
+
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
8
8
|
import { resolveMilestoneFile } from "./paths.js";
|
|
9
9
|
import { deriveState, isMilestoneComplete } from "./state.js";
|
|
10
10
|
import { listWorktrees, resolveGitDir, worktreesDir } from "./worktree-manager.js";
|
|
@@ -52,6 +52,22 @@ function isSameOrNestedPath(candidate: string, container: string): boolean {
|
|
|
52
52
|
normalizedCandidate.startsWith(`${normalizedContainer}/`);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
async function isCompletedMilestoneTerminal(basePath: string, milestoneId: string): Promise<boolean> {
|
|
56
|
+
const summaryPath = resolveMilestoneFile(basePath, milestoneId, "SUMMARY");
|
|
57
|
+
if (!summaryPath) return false;
|
|
58
|
+
|
|
59
|
+
if (isDbAvailable()) {
|
|
60
|
+
const milestone = getMilestone(milestoneId);
|
|
61
|
+
return !!milestone && milestone.status === "complete";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
65
|
+
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
66
|
+
if (!roadmapContent) return false;
|
|
67
|
+
const roadmap = parseLegacyRoadmap(roadmapContent);
|
|
68
|
+
return isMilestoneComplete(roadmap);
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
export async function checkGitHealth(
|
|
56
72
|
basePath: string,
|
|
57
73
|
issues: DoctorIssue[],
|
|
@@ -81,23 +97,9 @@ export async function checkGitHealth(
|
|
|
81
97
|
// Extract milestone ID from branch name "milestone/M001" → "M001"
|
|
82
98
|
const milestoneId = wt.branch.replace(/^milestone\//, "");
|
|
83
99
|
const milestoneEntry = state.registry.find(m => m.id === milestoneId);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (milestoneEntry) {
|
|
88
|
-
if (isDbAvailable()) {
|
|
89
|
-
const dbSlices = getMilestoneSlices(milestoneId);
|
|
90
|
-
isComplete = dbSlices.length > 0 && dbSlices.every(s => s.status === "complete");
|
|
91
|
-
} else {
|
|
92
|
-
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
93
|
-
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
94
|
-
if (roadmapContent) {
|
|
95
|
-
const roadmap = parseLegacyRoadmap(roadmapContent);
|
|
96
|
-
isComplete = isMilestoneComplete(roadmap);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
// When DB unavailable and no roadmap, isComplete stays false
|
|
100
|
-
}
|
|
100
|
+
const isComplete = milestoneEntry
|
|
101
|
+
? await isCompletedMilestoneTerminal(basePath, milestoneId)
|
|
102
|
+
: false;
|
|
101
103
|
|
|
102
104
|
if (isComplete) {
|
|
103
105
|
issues.push({
|
|
@@ -151,15 +153,9 @@ export async function checkGitHealth(
|
|
|
151
153
|
const milestoneId = branch.replace(/^milestone\//, "");
|
|
152
154
|
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
153
155
|
let branchMilestoneComplete = false;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} else {
|
|
158
|
-
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
159
|
-
if (!roadmapContent) continue;
|
|
160
|
-
const roadmap = parseLegacyRoadmap(roadmapContent);
|
|
161
|
-
branchMilestoneComplete = isMilestoneComplete(roadmap);
|
|
162
|
-
}
|
|
156
|
+
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
157
|
+
if (!roadmapContent) continue;
|
|
158
|
+
branchMilestoneComplete = await isCompletedMilestoneTerminal(basePath, milestoneId);
|
|
163
159
|
if (branchMilestoneComplete) {
|
|
164
160
|
issues.push({
|
|
165
161
|
severity: "info",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* - Optional search/tool integrations (Brave, Tavily, Jina, Context7)
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { existsSync } from "node:fs";
|
|
15
|
-
import { join } from "node:path";
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { delimiter, join } from "node:path";
|
|
16
16
|
import { AuthStorage } from "@gsd/pi-coding-agent";
|
|
17
17
|
import { getEnvApiKey } from "@gsd/pi-ai";
|
|
18
18
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
@@ -72,7 +72,8 @@ function modelToProviderId(model: string): string | null {
|
|
|
72
72
|
|
|
73
73
|
// Explicit provider prefix (e.g. "openrouter/deepseek-r1")
|
|
74
74
|
if (model.includes("/")) {
|
|
75
|
-
const
|
|
75
|
+
const rawPrefix = model.split("/")[0];
|
|
76
|
+
const prefix = rawPrefix.toLowerCase();
|
|
76
77
|
// Map known prefixes to registry IDs
|
|
77
78
|
const prefixMap: Record<string, string> = {
|
|
78
79
|
"anthropic-vertex": "anthropic-vertex",
|
|
@@ -86,6 +87,7 @@ function modelToProviderId(model: string): string | null {
|
|
|
86
87
|
"github-copilot": "github-copilot",
|
|
87
88
|
};
|
|
88
89
|
if (prefixMap[prefix]) return prefixMap[prefix];
|
|
90
|
+
return rawPrefix;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
const lower = model.toLowerCase();
|
|
@@ -145,7 +147,7 @@ function collectConfiguredModelProviders(): Set<string> {
|
|
|
145
147
|
|
|
146
148
|
interface KeyLookup {
|
|
147
149
|
found: boolean;
|
|
148
|
-
source: "auth.json" | "env" | "none";
|
|
150
|
+
source: "auth.json" | "env" | "models.json" | "none";
|
|
149
151
|
backedOff: boolean;
|
|
150
152
|
}
|
|
151
153
|
|
|
@@ -167,8 +169,55 @@ const CLI_BINARY_MAP: Record<string, string> = {
|
|
|
167
169
|
function isCliBinaryInPath(providerId: string): boolean {
|
|
168
170
|
const binary = CLI_BINARY_MAP[providerId];
|
|
169
171
|
if (!binary) return false;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
+
|
|
173
|
+
const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
174
|
+
|
|
175
|
+
// On Windows, command shims are commonly installed as .cmd/.exe/.bat/.com.
|
|
176
|
+
// Scan PATHEXT candidates in addition to the bare binary name.
|
|
177
|
+
const executableNames: string[] = [binary];
|
|
178
|
+
if (process.platform === "win32") {
|
|
179
|
+
const rawPathExt = process.env.PATHEXT
|
|
180
|
+
?.split(";")
|
|
181
|
+
.map(ext => ext.trim())
|
|
182
|
+
.filter(Boolean) ?? [];
|
|
183
|
+
const normalizedPathExt = rawPathExt.map(ext =>
|
|
184
|
+
ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`,
|
|
185
|
+
);
|
|
186
|
+
const defaultExt = [".exe", ".cmd", ".bat", ".com"];
|
|
187
|
+
for (const ext of [...normalizedPathExt, ...defaultExt]) {
|
|
188
|
+
const candidate = `${binary}${ext}`;
|
|
189
|
+
if (!executableNames.includes(candidate)) executableNames.push(candidate);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return pathDirs.some(dir => executableNames.some(name => existsSync(join(dir, name))));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function modelsJsonPaths(): string[] {
|
|
197
|
+
const home = process.env.HOME ?? "~";
|
|
198
|
+
return [
|
|
199
|
+
join(home, ".gsd", "agent", "models.json"),
|
|
200
|
+
// Keep parity with custom-provider discovery during auto bootstrap.
|
|
201
|
+
join(home, ".pi", "agent", "models.json"),
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function hasModelsJsonApiKey(providerId: string): boolean {
|
|
206
|
+
for (const path of modelsJsonPaths()) {
|
|
207
|
+
if (!existsSync(path)) continue;
|
|
208
|
+
try {
|
|
209
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8")) as {
|
|
210
|
+
providers?: Record<string, { apiKey?: unknown }>;
|
|
211
|
+
};
|
|
212
|
+
const apiKey = parsed.providers?.[providerId]?.apiKey;
|
|
213
|
+
if (typeof apiKey === "string" && apiKey.trim().length > 0) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
// Malformed models.json should not break the dashboard health check.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
172
221
|
}
|
|
173
222
|
|
|
174
223
|
function resolveKey(providerId: string): KeyLookup {
|
|
@@ -221,6 +270,10 @@ function resolveKey(providerId: string): KeyLookup {
|
|
|
221
270
|
return { found: true, source: "env", backedOff: false };
|
|
222
271
|
}
|
|
223
272
|
|
|
273
|
+
if (hasModelsJsonApiKey(providerId)) {
|
|
274
|
+
return { found: true, source: "models.json", backedOff: false };
|
|
275
|
+
}
|
|
276
|
+
|
|
224
277
|
return { found: false, source: "none", backedOff: false };
|
|
225
278
|
}
|
|
226
279
|
|
|
@@ -160,6 +160,8 @@ export function createGSDExtensionAPI(
|
|
|
160
160
|
pi.emitBeforeModelSelect(...args),
|
|
161
161
|
emitAdjustToolSet: (...args: Parameters<ExtensionAPI["emitAdjustToolSet"]>) =>
|
|
162
162
|
pi.emitAdjustToolSet(...args),
|
|
163
|
+
emitExtensionEvent: (...args: Parameters<ExtensionAPI["emitExtensionEvent"]>) =>
|
|
164
|
+
pi.emitExtensionEvent(...args),
|
|
163
165
|
|
|
164
166
|
// ── Tool / command / shortcut / flag registration ──────────────────
|
|
165
167
|
registerTool: ((tool: any) => pi.registerTool(tool)) as ExtensionAPI["registerTool"],
|
|
@@ -19,6 +19,7 @@ export type ErrorClass =
|
|
|
19
19
|
| { kind: "stream"; retryAfterMs: number }
|
|
20
20
|
| { kind: "connection"; retryAfterMs: number }
|
|
21
21
|
| { kind: "model-error" }
|
|
22
|
+
| { kind: "unsupported-model" }
|
|
22
23
|
| { kind: "permanent" }
|
|
23
24
|
| { kind: "unknown" };
|
|
24
25
|
|
|
@@ -46,12 +47,19 @@ const PERMANENT_RE = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|bill
|
|
|
46
47
|
// Include provider-specific quota-window phrasing like:
|
|
47
48
|
// - "You've hit your limit"
|
|
48
49
|
// - "usage limit" / "quota reached"
|
|
49
|
-
|
|
50
|
+
// - "out of extra usage"
|
|
51
|
+
const RATE_LIMIT_RE = /rate.?limit|too many requests|429|hit your limit|usage limit|out of extra usage|quota (?:reached|hit)|limit.*resets?/i;
|
|
50
52
|
// OpenRouter affordability-style quota errors should be treated as transient
|
|
51
53
|
// so core retry logic can lower maxTokens and continue in-session.
|
|
52
54
|
const AFFORDABILITY_RE = /requires more credits|can only afford|insufficient credits|not enough credits|fewer max_tokens/i;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
// "Stream idle timeout" and "partial response received" are emitted by the SDK/harness
|
|
56
|
+
// for mid-stream disconnects. Both indicate a transient network-level interruption.
|
|
57
|
+
// See: https://github.com/gsd-build/gsd-2/issues/4558
|
|
58
|
+
const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns|unexpected eof|stream idle timeout|partial response received/i;
|
|
59
|
+
// Context overflow errors (context window/length exceeded) should be treated as server-class
|
|
60
|
+
// transient errors so auto-mode can retry with reduced budget or fall back to a larger-context model.
|
|
61
|
+
// See: https://github.com/gsd-build/gsd-2/issues/4528
|
|
62
|
+
const SERVER_RE = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable|context (?:window|length) exceed|context window exceed/i;
|
|
55
63
|
// ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
|
|
56
64
|
const CONNECTION_RE = /terminated|connection.?(?:refused|error)|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
|
|
57
65
|
// Catch-all for V8 JSON.parse errors: all modern variants end with "in JSON at position \d+".
|
|
@@ -59,6 +67,18 @@ const CONNECTION_RE = /terminated|connection.?(?:refused|error)|other side close
|
|
|
59
67
|
const STREAM_RE = /in JSON at position \d+|Unexpected end of JSON|SyntaxError.*JSON/i;
|
|
60
68
|
const RESET_DELAY_RE = /reset in (\d+)s/i;
|
|
61
69
|
|
|
70
|
+
// Provider-side model entitlement rejection: the SDK accepted the model switch,
|
|
71
|
+
// but the provider refused at request time because the current account/plan/tier
|
|
72
|
+
// cannot use that model. Must match all three of: a model/deployment token,
|
|
73
|
+
// a negative-entitlement indicator, and an account/plan/tier/subscription token.
|
|
74
|
+
// Requiring all three keeps generic "account suspended" errors in `permanent`
|
|
75
|
+
// (no model token) while catching the phrasings providers actually use.
|
|
76
|
+
// See issue #4513.
|
|
77
|
+
const UNSUPPORTED_MODEL_MODEL_RE = /\b(?:model|deployment)\b/i;
|
|
78
|
+
const UNSUPPORTED_MODEL_INDICATOR_RE =
|
|
79
|
+
/\bnot support(?:ed|s)?\b|\bunsupported\b|\bnot available\b|\bunavailable\b|\bno access\b|\bdoes(?:n['’]t| not) (?:have access|support)\b|\bnot authori[sz]ed\b/i;
|
|
80
|
+
const UNSUPPORTED_MODEL_SCOPE_RE = /\b(?:account|plan|tier|subscription)\b/i;
|
|
81
|
+
|
|
62
82
|
/**
|
|
63
83
|
* Classify an error message into one of the ErrorClass kinds.
|
|
64
84
|
*
|
|
@@ -74,6 +94,19 @@ const RESET_DELAY_RE = /reset in (\d+)s/i;
|
|
|
74
94
|
export function classifyError(errorMsg: string, retryAfterMs?: number): ErrorClass {
|
|
75
95
|
const isPermanent = PERMANENT_RE.test(errorMsg);
|
|
76
96
|
const isRateLimit = RATE_LIMIT_RE.test(errorMsg) || AFFORDABILITY_RE.test(errorMsg);
|
|
97
|
+
const isUnsupportedModel =
|
|
98
|
+
UNSUPPORTED_MODEL_MODEL_RE.test(errorMsg) &&
|
|
99
|
+
UNSUPPORTED_MODEL_INDICATOR_RE.test(errorMsg) &&
|
|
100
|
+
UNSUPPORTED_MODEL_SCOPE_RE.test(errorMsg);
|
|
101
|
+
|
|
102
|
+
// 0. Unsupported model (account/plan entitlement rejection) — checked before
|
|
103
|
+
// `permanent` because PERMANENT_RE also matches /account/i and would
|
|
104
|
+
// otherwise swallow these errors, blocking the blocklist-driven fallback.
|
|
105
|
+
// Rate limit still wins when both patterns appear (a throttled account is
|
|
106
|
+
// not an entitlement failure).
|
|
107
|
+
if (isUnsupportedModel && !isRateLimit) {
|
|
108
|
+
return { kind: "unsupported-model" };
|
|
109
|
+
}
|
|
77
110
|
|
|
78
111
|
// 1. Permanent — but rate limit takes precedence
|
|
79
112
|
if (isPermanent && !isRateLimit) {
|