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,121 @@
|
|
|
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
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
9
|
+
import { getActiveMemoriesRanked } from "./memory-store.js";
|
|
10
|
+
import { listExecHistory } from "./exec-history.js";
|
|
11
|
+
export const DEFAULT_SNAPSHOT_BYTES = 2048;
|
|
12
|
+
export const SNAPSHOT_FILENAME = "last-snapshot.md";
|
|
13
|
+
/**
|
|
14
|
+
* Build a priority-tiered markdown snapshot. Pure — no I/O. Tiers:
|
|
15
|
+
* 1. Active context (if any)
|
|
16
|
+
* 2. Top memories by rank
|
|
17
|
+
* 3. Recent exec runs (failures highlighted)
|
|
18
|
+
* The result is guaranteed to be <= opts.maxBytes (truncated with an
|
|
19
|
+
* ellipsis marker if necessary).
|
|
20
|
+
*/
|
|
21
|
+
export function buildSnapshot(sources, opts = {}) {
|
|
22
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_SNAPSHOT_BYTES;
|
|
23
|
+
const maxMemories = opts.maxMemories ?? 6;
|
|
24
|
+
const maxExec = opts.maxExec ?? 5;
|
|
25
|
+
const lines = [];
|
|
26
|
+
lines.push(`# GSD context snapshot (${sources.generatedAt.toISOString()})`);
|
|
27
|
+
lines.push("");
|
|
28
|
+
if (sources.activeContext && sources.activeContext.trim().length > 0) {
|
|
29
|
+
lines.push("## Active context");
|
|
30
|
+
lines.push(sources.activeContext.trim());
|
|
31
|
+
lines.push("");
|
|
32
|
+
}
|
|
33
|
+
const memories = sources.memories.slice(0, maxMemories);
|
|
34
|
+
if (memories.length > 0) {
|
|
35
|
+
lines.push("## Top project memories");
|
|
36
|
+
for (const memory of memories) {
|
|
37
|
+
lines.push(`- [${memory.id}] (${memory.category}) ${memory.content.trim()}`);
|
|
38
|
+
}
|
|
39
|
+
lines.push("");
|
|
40
|
+
}
|
|
41
|
+
const exec = sources.execHistory.slice(0, maxExec);
|
|
42
|
+
if (exec.length > 0) {
|
|
43
|
+
lines.push("## Recent gsd_exec runs");
|
|
44
|
+
for (const entry of exec) {
|
|
45
|
+
const status = entry.timed_out
|
|
46
|
+
? "timeout"
|
|
47
|
+
: entry.exit_code === null
|
|
48
|
+
? "exit:null"
|
|
49
|
+
: `exit:${entry.exit_code}`;
|
|
50
|
+
const purpose = entry.purpose ? ` — ${entry.purpose}` : "";
|
|
51
|
+
lines.push(`- [${entry.id}] ${entry.runtime} ${status}${purpose}`);
|
|
52
|
+
}
|
|
53
|
+
lines.push("");
|
|
54
|
+
}
|
|
55
|
+
if (memories.length === 0 && exec.length === 0 && !sources.activeContext) {
|
|
56
|
+
lines.push("_No durable memories, active context, or exec history to surface._");
|
|
57
|
+
}
|
|
58
|
+
return enforceByteCap(lines.join("\n").trimEnd(), maxBytes);
|
|
59
|
+
}
|
|
60
|
+
function enforceByteCap(input, maxBytes) {
|
|
61
|
+
if (Buffer.byteLength(input, "utf-8") <= maxBytes)
|
|
62
|
+
return input;
|
|
63
|
+
const marker = "\n…[truncated]";
|
|
64
|
+
const markerBytes = Buffer.byteLength(marker, "utf-8");
|
|
65
|
+
const budget = Math.max(0, maxBytes - markerBytes);
|
|
66
|
+
// Walk backwards until the trimmed string fits. utf-8 is variable-width;
|
|
67
|
+
// naive char slicing is safe for ASCII but may split a multi-byte char.
|
|
68
|
+
// Guard by decoding the trimmed Buffer and relying on the replacement-char
|
|
69
|
+
// fallback in TextDecoder (implicit via toString).
|
|
70
|
+
const buf = Buffer.from(input, "utf-8").subarray(0, budget);
|
|
71
|
+
return `${buf.toString("utf-8")}${marker}`;
|
|
72
|
+
}
|
|
73
|
+
export function writeCompactionSnapshot(baseDir, opts = {}) {
|
|
74
|
+
const memories = safeGetMemories();
|
|
75
|
+
const execHistory = safeListExec(baseDir);
|
|
76
|
+
const content = buildSnapshot({
|
|
77
|
+
memories,
|
|
78
|
+
execHistory,
|
|
79
|
+
generatedAt: (opts.now ?? (() => new Date()))(),
|
|
80
|
+
activeContext: opts.activeContext ?? null,
|
|
81
|
+
}, opts);
|
|
82
|
+
const gsdDir = resolve(baseDir, ".gsd");
|
|
83
|
+
if (!existsSync(gsdDir))
|
|
84
|
+
mkdirSync(gsdDir, { recursive: true });
|
|
85
|
+
const path = resolve(gsdDir, SNAPSHOT_FILENAME);
|
|
86
|
+
const finalContent = `${content}\n`;
|
|
87
|
+
writeFileSync(path, finalContent, "utf-8");
|
|
88
|
+
return {
|
|
89
|
+
path,
|
|
90
|
+
bytes: Buffer.byteLength(finalContent, "utf-8"),
|
|
91
|
+
memories: memories.length,
|
|
92
|
+
execRuns: execHistory.length,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function readCompactionSnapshot(baseDir) {
|
|
96
|
+
const path = resolve(baseDir, ".gsd", SNAPSHOT_FILENAME);
|
|
97
|
+
if (!existsSync(path))
|
|
98
|
+
return null;
|
|
99
|
+
try {
|
|
100
|
+
return readFileSync(path, "utf-8");
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function safeGetMemories() {
|
|
107
|
+
try {
|
|
108
|
+
return getActiveMemoriesRanked(12);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function safeListExec(baseDir) {
|
|
115
|
+
try {
|
|
116
|
+
return listExecHistory(baseDir);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -8,10 +8,12 @@ import { getAdaptiveTierAdjustment } from "./routing-history.js";
|
|
|
8
8
|
import { parseUnitId } from "./unit-id.js";
|
|
9
9
|
// ─── Unit Type → Default Tier Mapping ────────────────────────────────────────
|
|
10
10
|
const UNIT_TYPE_TIERS = {
|
|
11
|
-
// Tier 1 — Light:
|
|
12
|
-
"complete-slice": "light",
|
|
11
|
+
// Tier 1 — Light: compact verification turns
|
|
13
12
|
"run-uat": "light",
|
|
14
|
-
// Tier 2 — Standard: research, routine discussion
|
|
13
|
+
// Tier 2 — Standard: research, routine discussion, slice completion
|
|
14
|
+
// complete-slice can carry large inlined context; avoid routing it to the
|
|
15
|
+
// cheapest "light" model by default (#4520).
|
|
16
|
+
"complete-slice": "standard",
|
|
15
17
|
"discuss-milestone": "standard",
|
|
16
18
|
"discuss-slice": "standard",
|
|
17
19
|
"research-milestone": "standard",
|
|
@@ -205,6 +205,19 @@ export async function nextDecisionId() {
|
|
|
205
205
|
return 'D001';
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
+
/** Synchronous variant for use inside db.transaction(). */
|
|
209
|
+
function nextDecisionIdSync(adapter) {
|
|
210
|
+
if (!adapter)
|
|
211
|
+
return 'D001';
|
|
212
|
+
const row = adapter
|
|
213
|
+
.prepare('SELECT MAX(CAST(SUBSTR(id, 2) AS INTEGER)) as max_num FROM decisions')
|
|
214
|
+
.get();
|
|
215
|
+
const maxNum = row ? row['max_num'] : null;
|
|
216
|
+
if (maxNum == null || isNaN(maxNum))
|
|
217
|
+
return 'D001';
|
|
218
|
+
const next = maxNum + 1;
|
|
219
|
+
return `D${String(next).padStart(3, '0')}`;
|
|
220
|
+
}
|
|
208
221
|
// ─── Next Requirement ID ─────────────────────────────────────────────────
|
|
209
222
|
/**
|
|
210
223
|
* Compute the next requirement ID from the current DB state.
|
|
@@ -318,32 +331,44 @@ export async function saveRequirementToDb(fields, basePath) {
|
|
|
318
331
|
throw err;
|
|
319
332
|
}
|
|
320
333
|
}
|
|
334
|
+
// ─── Async Mutex for Decision Saves ───────────────────────────────────────
|
|
335
|
+
//
|
|
336
|
+
// Serializes the entire saveDecisionToDb operation (ID generation + DB upsert
|
|
337
|
+
// + file read + markdown regeneration + file write) so that parallel callers
|
|
338
|
+
// cannot interleave and produce a last-writer-wins race on DECISIONS.md.
|
|
339
|
+
let _decisionSaveLock = Promise.resolve();
|
|
340
|
+
/** Reset the mutex — only for tests. */
|
|
341
|
+
export function _resetDecisionSaveLock() {
|
|
342
|
+
_decisionSaveLock = Promise.resolve();
|
|
343
|
+
}
|
|
321
344
|
/**
|
|
322
345
|
* Save a new decision to DB and regenerate DECISIONS.md.
|
|
323
346
|
* Auto-assigns the next ID via nextDecisionId().
|
|
324
347
|
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
* and
|
|
348
|
+
* Concurrency: uses an async mutex (promise chain) to serialize the entire
|
|
349
|
+
* operation — ID generation, DB upsert, file read, markdown regeneration,
|
|
350
|
+
* and file write — preventing parallel callers from overwriting each other's
|
|
351
|
+
* output (last-writer-wins race condition).
|
|
328
352
|
*
|
|
329
353
|
* Returns the assigned ID.
|
|
330
354
|
*/
|
|
331
355
|
export async function saveDecisionToDb(fields, basePath) {
|
|
356
|
+
// Serialize via async mutex: each call waits for the previous one to
|
|
357
|
+
// complete before starting, preventing interleaved DB + file writes.
|
|
358
|
+
let release;
|
|
359
|
+
const prev = _decisionSaveLock;
|
|
360
|
+
_decisionSaveLock = new Promise(r => { release = r; });
|
|
361
|
+
try {
|
|
362
|
+
await prev;
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
// Previous call failed — proceed regardless; the lock chain must continue.
|
|
366
|
+
}
|
|
332
367
|
try {
|
|
333
368
|
const db = await import('./gsd-db.js');
|
|
334
|
-
|
|
335
|
-
// parallel calls from racing on the same MAX(id) value.
|
|
369
|
+
const adapter = db._getAdapter();
|
|
336
370
|
const id = db.transaction(() => {
|
|
337
|
-
const
|
|
338
|
-
if (!adapter)
|
|
339
|
-
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
340
|
-
const row = adapter
|
|
341
|
-
.prepare('SELECT MAX(CAST(SUBSTR(id, 2) AS INTEGER)) as max_num FROM decisions')
|
|
342
|
-
.get();
|
|
343
|
-
const maxNum = row ? row['max_num'] : null;
|
|
344
|
-
const nextId = (maxNum == null || isNaN(maxNum))
|
|
345
|
-
? 'D001'
|
|
346
|
-
: `D${String(maxNum + 1).padStart(3, '0')}`;
|
|
371
|
+
const nextId = nextDecisionIdSync(adapter);
|
|
347
372
|
db.upsertDecision({
|
|
348
373
|
id: nextId,
|
|
349
374
|
when_context: fields.when_context ?? '',
|
|
@@ -359,7 +384,6 @@ export async function saveDecisionToDb(fields, basePath) {
|
|
|
359
384
|
return nextId;
|
|
360
385
|
});
|
|
361
386
|
// Fetch all decisions (including superseded for the full register)
|
|
362
|
-
const adapter = db._getAdapter();
|
|
363
387
|
let allDecisions = [];
|
|
364
388
|
if (adapter) {
|
|
365
389
|
const rows = adapter.prepare('SELECT * FROM decisions ORDER BY seq').all();
|
|
@@ -434,12 +458,60 @@ export async function saveDecisionToDb(fields, basePath) {
|
|
|
434
458
|
invalidateStateCache();
|
|
435
459
|
clearPathCache();
|
|
436
460
|
clearParseCache();
|
|
461
|
+
// ADR-013 dual-write: keep the memory store in sync with every decision
|
|
462
|
+
// persisted via the legacy gsd_save_decision path. Without this, prompts
|
|
463
|
+
// that still call gsd_save_decision (discuss.md, plan-milestone.md,
|
|
464
|
+
// guided-plan-slice.md, et al. during the deprecation window) would
|
|
465
|
+
// create decisions rows invisible to memory_query and loadMemoryBlock.
|
|
466
|
+
// Best-effort — never throw, never roll back the decision on failure.
|
|
467
|
+
try {
|
|
468
|
+
const { createMemory } = await import('./memory-store.js');
|
|
469
|
+
const decisionText = (fields.decision ?? '').trim();
|
|
470
|
+
const choiceText = (fields.choice ?? '').trim();
|
|
471
|
+
const rationaleText = (fields.rationale ?? '').trim();
|
|
472
|
+
const contentParts = [];
|
|
473
|
+
if (decisionText)
|
|
474
|
+
contentParts.push(decisionText);
|
|
475
|
+
if (choiceText)
|
|
476
|
+
contentParts.push(`Chose: ${choiceText}.`);
|
|
477
|
+
if (rationaleText)
|
|
478
|
+
contentParts.push(`Rationale: ${rationaleText}.`);
|
|
479
|
+
const content = contentParts.join(' ').slice(0, 600);
|
|
480
|
+
if (content) {
|
|
481
|
+
createMemory({
|
|
482
|
+
category: 'architecture',
|
|
483
|
+
content,
|
|
484
|
+
scope: fields.scope || 'project',
|
|
485
|
+
confidence: 0.85,
|
|
486
|
+
structuredFields: {
|
|
487
|
+
sourceDecisionId: id,
|
|
488
|
+
when_context: fields.when_context ?? '',
|
|
489
|
+
scope: fields.scope,
|
|
490
|
+
decision: fields.decision,
|
|
491
|
+
choice: fields.choice,
|
|
492
|
+
rationale: fields.rationale,
|
|
493
|
+
made_by: fields.made_by ?? 'agent',
|
|
494
|
+
revisable: fields.revisable ?? '',
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
catch (mirrorErr) {
|
|
500
|
+
logError('manifest', 'memory-store mirror write failed (non-fatal)', {
|
|
501
|
+
fn: 'saveDecisionToDb',
|
|
502
|
+
decisionId: id,
|
|
503
|
+
error: String(mirrorErr.message),
|
|
504
|
+
});
|
|
505
|
+
}
|
|
437
506
|
return { id };
|
|
438
507
|
}
|
|
439
508
|
catch (err) {
|
|
440
509
|
logError('manifest', 'saveDecisionToDb failed', { fn: 'saveDecisionToDb', error: String(err.message) });
|
|
441
510
|
throw err;
|
|
442
511
|
}
|
|
512
|
+
finally {
|
|
513
|
+
release();
|
|
514
|
+
}
|
|
443
515
|
}
|
|
444
516
|
/**
|
|
445
517
|
* Extract a milestone/slice reference from a deferral decision.
|
|
@@ -2,7 +2,7 @@ import { existsSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs
|
|
|
2
2
|
import { join, sep } from "node:path";
|
|
3
3
|
import { loadFile } from "./files.js";
|
|
4
4
|
import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js";
|
|
5
|
-
import { isDbAvailable,
|
|
5
|
+
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
6
6
|
import { resolveMilestoneFile } from "./paths.js";
|
|
7
7
|
import { deriveState, isMilestoneComplete } from "./state.js";
|
|
8
8
|
import { listWorktrees, resolveGitDir, worktreesDir } from "./worktree-manager.js";
|
|
@@ -48,6 +48,21 @@ function isSameOrNestedPath(candidate, container) {
|
|
|
48
48
|
return normalizedCandidate === normalizedContainer ||
|
|
49
49
|
normalizedCandidate.startsWith(`${normalizedContainer}/`);
|
|
50
50
|
}
|
|
51
|
+
async function isCompletedMilestoneTerminal(basePath, milestoneId) {
|
|
52
|
+
const summaryPath = resolveMilestoneFile(basePath, milestoneId, "SUMMARY");
|
|
53
|
+
if (!summaryPath)
|
|
54
|
+
return false;
|
|
55
|
+
if (isDbAvailable()) {
|
|
56
|
+
const milestone = getMilestone(milestoneId);
|
|
57
|
+
return !!milestone && milestone.status === "complete";
|
|
58
|
+
}
|
|
59
|
+
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
60
|
+
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
61
|
+
if (!roadmapContent)
|
|
62
|
+
return false;
|
|
63
|
+
const roadmap = parseLegacyRoadmap(roadmapContent);
|
|
64
|
+
return isMilestoneComplete(roadmap);
|
|
65
|
+
}
|
|
51
66
|
export async function checkGitHealth(basePath, issues, fixesApplied, shouldFix, isolationMode = "none") {
|
|
52
67
|
// Degrade gracefully if not a git repo
|
|
53
68
|
if (!nativeIsRepo(basePath)) {
|
|
@@ -67,23 +82,9 @@ export async function checkGitHealth(basePath, issues, fixesApplied, shouldFix,
|
|
|
67
82
|
// Extract milestone ID from branch name "milestone/M001" → "M001"
|
|
68
83
|
const milestoneId = wt.branch.replace(/^milestone\//, "");
|
|
69
84
|
const milestoneEntry = state.registry.find(m => m.id === milestoneId);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (isDbAvailable()) {
|
|
74
|
-
const dbSlices = getMilestoneSlices(milestoneId);
|
|
75
|
-
isComplete = dbSlices.length > 0 && dbSlices.every(s => s.status === "complete");
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
79
|
-
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
80
|
-
if (roadmapContent) {
|
|
81
|
-
const roadmap = parseLegacyRoadmap(roadmapContent);
|
|
82
|
-
isComplete = isMilestoneComplete(roadmap);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
// When DB unavailable and no roadmap, isComplete stays false
|
|
86
|
-
}
|
|
85
|
+
const isComplete = milestoneEntry
|
|
86
|
+
? await isCompletedMilestoneTerminal(basePath, milestoneId)
|
|
87
|
+
: false;
|
|
87
88
|
if (isComplete) {
|
|
88
89
|
issues.push({
|
|
89
90
|
severity: "warning",
|
|
@@ -136,17 +137,10 @@ export async function checkGitHealth(basePath, issues, fixesApplied, shouldFix,
|
|
|
136
137
|
const milestoneId = branch.replace(/^milestone\//, "");
|
|
137
138
|
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
138
139
|
let branchMilestoneComplete = false;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
else {
|
|
144
|
-
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
145
|
-
if (!roadmapContent)
|
|
146
|
-
continue;
|
|
147
|
-
const roadmap = parseLegacyRoadmap(roadmapContent);
|
|
148
|
-
branchMilestoneComplete = isMilestoneComplete(roadmap);
|
|
149
|
-
}
|
|
140
|
+
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
141
|
+
if (!roadmapContent)
|
|
142
|
+
continue;
|
|
143
|
+
branchMilestoneComplete = await isCompletedMilestoneTerminal(basePath, milestoneId);
|
|
150
144
|
if (branchMilestoneComplete) {
|
|
151
145
|
issues.push({
|
|
152
146
|
severity: "info",
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* - Remote questions channel if configured (Slack/Discord/Telegram token)
|
|
11
11
|
* - Optional search/tool integrations (Brave, Tavily, Jina, Context7)
|
|
12
12
|
*/
|
|
13
|
-
import { existsSync } from "node:fs";
|
|
14
|
-
import { join } from "node:path";
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { delimiter, join } from "node:path";
|
|
15
15
|
import { AuthStorage } from "@gsd/pi-coding-agent";
|
|
16
16
|
import { getEnvApiKey } from "@gsd/pi-ai";
|
|
17
17
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
@@ -47,7 +47,8 @@ function modelToProviderId(model) {
|
|
|
47
47
|
return null;
|
|
48
48
|
// Explicit provider prefix (e.g. "openrouter/deepseek-r1")
|
|
49
49
|
if (model.includes("/")) {
|
|
50
|
-
const
|
|
50
|
+
const rawPrefix = model.split("/")[0];
|
|
51
|
+
const prefix = rawPrefix.toLowerCase();
|
|
51
52
|
// Map known prefixes to registry IDs
|
|
52
53
|
const prefixMap = {
|
|
53
54
|
"anthropic-vertex": "anthropic-vertex",
|
|
@@ -62,6 +63,7 @@ function modelToProviderId(model) {
|
|
|
62
63
|
};
|
|
63
64
|
if (prefixMap[prefix])
|
|
64
65
|
return prefixMap[prefix];
|
|
66
|
+
return rawPrefix;
|
|
65
67
|
}
|
|
66
68
|
const lower = model.toLowerCase();
|
|
67
69
|
if (lower.startsWith("claude"))
|
|
@@ -136,8 +138,49 @@ function isCliBinaryInPath(providerId) {
|
|
|
136
138
|
const binary = CLI_BINARY_MAP[providerId];
|
|
137
139
|
if (!binary)
|
|
138
140
|
return false;
|
|
139
|
-
const pathDirs = (process.env.PATH ?? "").split(
|
|
140
|
-
|
|
141
|
+
const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
142
|
+
// On Windows, command shims are commonly installed as .cmd/.exe/.bat/.com.
|
|
143
|
+
// Scan PATHEXT candidates in addition to the bare binary name.
|
|
144
|
+
const executableNames = [binary];
|
|
145
|
+
if (process.platform === "win32") {
|
|
146
|
+
const rawPathExt = process.env.PATHEXT
|
|
147
|
+
?.split(";")
|
|
148
|
+
.map(ext => ext.trim())
|
|
149
|
+
.filter(Boolean) ?? [];
|
|
150
|
+
const normalizedPathExt = rawPathExt.map(ext => ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`);
|
|
151
|
+
const defaultExt = [".exe", ".cmd", ".bat", ".com"];
|
|
152
|
+
for (const ext of [...normalizedPathExt, ...defaultExt]) {
|
|
153
|
+
const candidate = `${binary}${ext}`;
|
|
154
|
+
if (!executableNames.includes(candidate))
|
|
155
|
+
executableNames.push(candidate);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return pathDirs.some(dir => executableNames.some(name => existsSync(join(dir, name))));
|
|
159
|
+
}
|
|
160
|
+
function modelsJsonPaths() {
|
|
161
|
+
const home = process.env.HOME ?? "~";
|
|
162
|
+
return [
|
|
163
|
+
join(home, ".gsd", "agent", "models.json"),
|
|
164
|
+
// Keep parity with custom-provider discovery during auto bootstrap.
|
|
165
|
+
join(home, ".pi", "agent", "models.json"),
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
function hasModelsJsonApiKey(providerId) {
|
|
169
|
+
for (const path of modelsJsonPaths()) {
|
|
170
|
+
if (!existsSync(path))
|
|
171
|
+
continue;
|
|
172
|
+
try {
|
|
173
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
174
|
+
const apiKey = parsed.providers?.[providerId]?.apiKey;
|
|
175
|
+
if (typeof apiKey === "string" && apiKey.trim().length > 0) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Malformed models.json should not break the dashboard health check.
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
141
184
|
}
|
|
142
185
|
function resolveKey(providerId) {
|
|
143
186
|
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
@@ -182,6 +225,9 @@ function resolveKey(providerId) {
|
|
|
182
225
|
if (info?.envVar && process.env[info.envVar]) {
|
|
183
226
|
return { found: true, source: "env", backedOff: false };
|
|
184
227
|
}
|
|
228
|
+
if (hasModelsJsonApiKey(providerId)) {
|
|
229
|
+
return { found: true, source: "models.json", backedOff: false };
|
|
230
|
+
}
|
|
185
231
|
return { found: false, source: "none", backedOff: false };
|
|
186
232
|
}
|
|
187
233
|
// ── Individual check groups ────────────────────────────────────────────────────
|
|
@@ -101,6 +101,7 @@ export function createGSDExtensionAPI(pi, sharedHandlers) {
|
|
|
101
101
|
// ── Event emission ─────────────────────────────────────────────────
|
|
102
102
|
emitBeforeModelSelect: (...args) => pi.emitBeforeModelSelect(...args),
|
|
103
103
|
emitAdjustToolSet: (...args) => pi.emitAdjustToolSet(...args),
|
|
104
|
+
emitExtensionEvent: (...args) => pi.emitExtensionEvent(...args),
|
|
104
105
|
// ── Tool / command / shortcut / flag registration ──────────────────
|
|
105
106
|
registerTool: ((tool) => pi.registerTool(tool)),
|
|
106
107
|
registerCommand: (...args) => pi.registerCommand(...args),
|
|
@@ -22,18 +22,35 @@ const PERMANENT_RE = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|bill
|
|
|
22
22
|
// Include provider-specific quota-window phrasing like:
|
|
23
23
|
// - "You've hit your limit"
|
|
24
24
|
// - "usage limit" / "quota reached"
|
|
25
|
-
|
|
25
|
+
// - "out of extra usage"
|
|
26
|
+
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;
|
|
26
27
|
// OpenRouter affordability-style quota errors should be treated as transient
|
|
27
28
|
// so core retry logic can lower maxTokens and continue in-session.
|
|
28
29
|
const AFFORDABILITY_RE = /requires more credits|can only afford|insufficient credits|not enough credits|fewer max_tokens/i;
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
// "Stream idle timeout" and "partial response received" are emitted by the SDK/harness
|
|
31
|
+
// for mid-stream disconnects. Both indicate a transient network-level interruption.
|
|
32
|
+
// See: https://github.com/gsd-build/gsd-2/issues/4558
|
|
33
|
+
const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns|unexpected eof|stream idle timeout|partial response received/i;
|
|
34
|
+
// Context overflow errors (context window/length exceeded) should be treated as server-class
|
|
35
|
+
// transient errors so auto-mode can retry with reduced budget or fall back to a larger-context model.
|
|
36
|
+
// See: https://github.com/gsd-build/gsd-2/issues/4528
|
|
37
|
+
const SERVER_RE = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable|context (?:window|length) exceed|context window exceed/i;
|
|
31
38
|
// ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
|
|
32
39
|
const CONNECTION_RE = /terminated|connection.?(?:refused|error)|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
|
|
33
40
|
// Catch-all for V8 JSON.parse errors: all modern variants end with "in JSON at position \d+".
|
|
34
41
|
// This eliminates the need to enumerate every error message variant individually.
|
|
35
42
|
const STREAM_RE = /in JSON at position \d+|Unexpected end of JSON|SyntaxError.*JSON/i;
|
|
36
43
|
const RESET_DELAY_RE = /reset in (\d+)s/i;
|
|
44
|
+
// Provider-side model entitlement rejection: the SDK accepted the model switch,
|
|
45
|
+
// but the provider refused at request time because the current account/plan/tier
|
|
46
|
+
// cannot use that model. Must match all three of: a model/deployment token,
|
|
47
|
+
// a negative-entitlement indicator, and an account/plan/tier/subscription token.
|
|
48
|
+
// Requiring all three keeps generic "account suspended" errors in `permanent`
|
|
49
|
+
// (no model token) while catching the phrasings providers actually use.
|
|
50
|
+
// See issue #4513.
|
|
51
|
+
const UNSUPPORTED_MODEL_MODEL_RE = /\b(?:model|deployment)\b/i;
|
|
52
|
+
const UNSUPPORTED_MODEL_INDICATOR_RE = /\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;
|
|
53
|
+
const UNSUPPORTED_MODEL_SCOPE_RE = /\b(?:account|plan|tier|subscription)\b/i;
|
|
37
54
|
/**
|
|
38
55
|
* Classify an error message into one of the ErrorClass kinds.
|
|
39
56
|
*
|
|
@@ -49,6 +66,17 @@ const RESET_DELAY_RE = /reset in (\d+)s/i;
|
|
|
49
66
|
export function classifyError(errorMsg, retryAfterMs) {
|
|
50
67
|
const isPermanent = PERMANENT_RE.test(errorMsg);
|
|
51
68
|
const isRateLimit = RATE_LIMIT_RE.test(errorMsg) || AFFORDABILITY_RE.test(errorMsg);
|
|
69
|
+
const isUnsupportedModel = UNSUPPORTED_MODEL_MODEL_RE.test(errorMsg) &&
|
|
70
|
+
UNSUPPORTED_MODEL_INDICATOR_RE.test(errorMsg) &&
|
|
71
|
+
UNSUPPORTED_MODEL_SCOPE_RE.test(errorMsg);
|
|
72
|
+
// 0. Unsupported model (account/plan entitlement rejection) — checked before
|
|
73
|
+
// `permanent` because PERMANENT_RE also matches /account/i and would
|
|
74
|
+
// otherwise swallow these errors, blocking the blocklist-driven fallback.
|
|
75
|
+
// Rate limit still wins when both patterns appear (a throttled account is
|
|
76
|
+
// not an entitlement failure).
|
|
77
|
+
if (isUnsupportedModel && !isRateLimit) {
|
|
78
|
+
return { kind: "unsupported-model" };
|
|
79
|
+
}
|
|
52
80
|
// 1. Permanent — but rate limit takes precedence
|
|
53
81
|
if (isPermanent && !isRateLimit) {
|
|
54
82
|
return { kind: "permanent" };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// GSD Exec History — read-side helpers for the exec sandbox.
|
|
2
|
+
//
|
|
3
|
+
// Pure I/O: scans `.gsd/exec/*.meta.json` under a base directory and
|
|
4
|
+
// returns lightweight records. Used by the gsd_exec_search tool and
|
|
5
|
+
// any future compaction-snapshot enrichment.
|
|
6
|
+
import { closeSync, openSync, readdirSync, readFileSync, readSync, statSync } from "node:fs";
|
|
7
|
+
import { join, resolve } from "node:path";
|
|
8
|
+
function listMetaFiles(baseDir) {
|
|
9
|
+
const dir = resolve(baseDir, ".gsd", "exec");
|
|
10
|
+
try {
|
|
11
|
+
return readdirSync(dir)
|
|
12
|
+
.filter((name) => name.endsWith(".meta.json"))
|
|
13
|
+
.map((name) => join(dir, name));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function safeReadMeta(path) {
|
|
20
|
+
try {
|
|
21
|
+
const raw = readFileSync(path, "utf-8");
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
if (typeof parsed.id !== "string" || typeof parsed.runtime !== "string")
|
|
24
|
+
return null;
|
|
25
|
+
return {
|
|
26
|
+
id: parsed.id,
|
|
27
|
+
runtime: parsed.runtime,
|
|
28
|
+
purpose: typeof parsed.purpose === "string" ? parsed.purpose : null,
|
|
29
|
+
started_at: typeof parsed.started_at === "string" ? parsed.started_at : "",
|
|
30
|
+
finished_at: typeof parsed.finished_at === "string" ? parsed.finished_at : "",
|
|
31
|
+
duration_ms: typeof parsed.duration_ms === "number" ? parsed.duration_ms : 0,
|
|
32
|
+
exit_code: typeof parsed.exit_code === "number" ? parsed.exit_code : null,
|
|
33
|
+
signal: typeof parsed.signal === "string" ? parsed.signal : null,
|
|
34
|
+
timed_out: parsed.timed_out === true,
|
|
35
|
+
stdout_bytes: typeof parsed.stdout_bytes === "number" ? parsed.stdout_bytes : 0,
|
|
36
|
+
stderr_bytes: typeof parsed.stderr_bytes === "number" ? parsed.stderr_bytes : 0,
|
|
37
|
+
stdout_truncated: parsed.stdout_truncated === true,
|
|
38
|
+
stderr_truncated: parsed.stderr_truncated === true,
|
|
39
|
+
stdout_path: path.replace(/\.meta\.json$/, ".stdout"),
|
|
40
|
+
stderr_path: path.replace(/\.meta\.json$/, ".stderr"),
|
|
41
|
+
meta_path: path,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function listExecHistory(baseDir) {
|
|
49
|
+
const metas = listMetaFiles(baseDir)
|
|
50
|
+
.map((path) => {
|
|
51
|
+
let mtime = 0;
|
|
52
|
+
try {
|
|
53
|
+
mtime = statSync(path).mtimeMs;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* ignore */
|
|
57
|
+
}
|
|
58
|
+
const entry = safeReadMeta(path);
|
|
59
|
+
return entry ? { entry, mtime } : null;
|
|
60
|
+
})
|
|
61
|
+
.filter((value) => value !== null);
|
|
62
|
+
metas.sort((a, b) => b.mtime - a.mtime);
|
|
63
|
+
return metas.map((m) => m.entry);
|
|
64
|
+
}
|
|
65
|
+
function matchesFilters(entry, opts) {
|
|
66
|
+
if (opts.runtime && entry.runtime !== opts.runtime)
|
|
67
|
+
return false;
|
|
68
|
+
if (opts.failing_only) {
|
|
69
|
+
const failed = entry.timed_out || (entry.exit_code !== 0 && entry.exit_code !== null);
|
|
70
|
+
if (!failed)
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const query = (opts.query ?? "").trim().toLowerCase();
|
|
74
|
+
if (!query)
|
|
75
|
+
return true;
|
|
76
|
+
const haystack = `${entry.id} ${entry.purpose ?? ""}`.toLowerCase();
|
|
77
|
+
return haystack.includes(query);
|
|
78
|
+
}
|
|
79
|
+
function readDigestPreview(entry, maxChars) {
|
|
80
|
+
if (!entry.stdout_path || maxChars <= 0)
|
|
81
|
+
return undefined;
|
|
82
|
+
try {
|
|
83
|
+
const size = statSync(entry.stdout_path).size;
|
|
84
|
+
if (size === 0)
|
|
85
|
+
return undefined;
|
|
86
|
+
const readBytes = Math.min(size, maxChars * 4); // 4 bytes/char upper bound for UTF-8
|
|
87
|
+
const buf = Buffer.allocUnsafe(readBytes);
|
|
88
|
+
const fd = openSync(entry.stdout_path, "r");
|
|
89
|
+
try {
|
|
90
|
+
const bytesRead = readSync(fd, buf, 0, readBytes, Math.max(0, size - readBytes));
|
|
91
|
+
const text = buf.subarray(0, bytesRead).toString("utf-8");
|
|
92
|
+
const trimmed = text.trimEnd();
|
|
93
|
+
return trimmed.length <= maxChars ? trimmed : trimmed.slice(trimmed.length - maxChars);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
closeSync(fd);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function searchExecHistory(baseDir, opts = {}) {
|
|
104
|
+
const limit = clampLimit(opts.limit, 20, 200);
|
|
105
|
+
const entries = listExecHistory(baseDir);
|
|
106
|
+
const filtered = entries.filter((entry) => matchesFilters(entry, opts));
|
|
107
|
+
return filtered.slice(0, limit).map((entry) => ({
|
|
108
|
+
entry,
|
|
109
|
+
digest_preview: readDigestPreview(entry, 300),
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
function clampLimit(value, fallback, max) {
|
|
113
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
114
|
+
return fallback;
|
|
115
|
+
if (value < 1)
|
|
116
|
+
return 1;
|
|
117
|
+
if (value > max)
|
|
118
|
+
return max;
|
|
119
|
+
return Math.floor(value);
|
|
120
|
+
}
|