gsd-pi 2.41.0 → 2.42.0-dev.1df898f
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 +92 -29
- package/dist/cli-web-branch.d.ts +6 -0
- package/dist/cli-web-branch.js +17 -0
- package/dist/cli.js +18 -3
- package/dist/loader.js +3 -1
- package/dist/onboarding.js +2 -1
- package/dist/resource-loader.js +39 -6
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
- package/dist/resources/extensions/async-jobs/await-tool.js +5 -0
- package/dist/resources/extensions/async-jobs/index.js +2 -0
- package/dist/resources/extensions/gsd/auto/loop.js +89 -1
- package/dist/resources/extensions/gsd/auto/phases.js +29 -13
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +3 -16
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +16 -14
- package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
- package/dist/resources/extensions/gsd/auto.js +64 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +25 -3
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
- package/dist/resources/extensions/gsd/context-injector.js +74 -0
- package/dist/resources/extensions/gsd/context-store.js +4 -3
- package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
- package/dist/resources/extensions/gsd/custom-verification.js +145 -0
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
- package/dist/resources/extensions/gsd/db-writer.js +5 -2
- package/dist/resources/extensions/gsd/definition-loader.js +352 -0
- package/dist/resources/extensions/gsd/detection.js +20 -1
- package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
- package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +31 -1
- package/dist/resources/extensions/gsd/doctor-providers.js +10 -0
- package/dist/resources/extensions/gsd/doctor.js +11 -1
- package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
- package/dist/resources/extensions/gsd/engine-types.js +8 -0
- package/dist/resources/extensions/gsd/execution-policy.js +8 -0
- package/dist/resources/extensions/gsd/exit-command.js +12 -2
- package/dist/resources/extensions/gsd/export.js +9 -13
- package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
- package/dist/resources/extensions/gsd/files.js +28 -11
- package/dist/resources/extensions/gsd/forensics.js +94 -3
- package/dist/resources/extensions/gsd/git-constants.js +1 -0
- package/dist/resources/extensions/gsd/git-service.js +6 -2
- package/dist/resources/extensions/gsd/graph.js +225 -0
- package/dist/resources/extensions/gsd/gsd-db.js +25 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +7 -3
- package/dist/resources/extensions/gsd/journal.js +85 -0
- package/dist/resources/extensions/gsd/md-importer.js +5 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +3 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
- package/dist/resources/extensions/gsd/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences.js +60 -8
- package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +92 -7
- package/dist/resources/extensions/gsd/rule-registry.js +489 -0
- package/dist/resources/extensions/gsd/rule-types.js +6 -0
- package/dist/resources/extensions/gsd/run-manager.js +134 -0
- package/dist/resources/extensions/gsd/service-tier.js +147 -0
- package/dist/resources/extensions/gsd/session-lock.js +2 -2
- package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
- package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
- package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
- package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
- package/dist/resources/extensions/gsd/worktree-resolver.js +21 -4
- package/dist/resources/extensions/gsd/worktree.js +2 -2
- package/dist/resources/extensions/mcp-client/index.js +2 -1
- package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
- package/dist/resources/extensions/subagent/index.js +7 -3
- package/dist/resources/extensions/voice/index.js +4 -4
- package/dist/resources/skills/create-workflow/SKILL.md +103 -0
- package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/dist/web-mode.d.ts +4 -0
- package/dist/web-mode.js +69 -11
- package/package.json +1 -1
- package/packages/native/src/__tests__/text.test.mjs +33 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +6 -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/src/agent.test.ts +53 -0
- package/packages/pi-agent-core/src/agent.ts +3 -0
- package/packages/pi-agent-core/src/types.ts +6 -0
- package/packages/pi-agent-core/tsconfig.json +1 -1
- package/packages/pi-ai/dist/models.d.ts +5 -3
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +801 -1468
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +1135 -1588
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +60 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +1543 -0
- package/packages/pi-ai/src/models.generated.ts +1140 -1593
- package/packages/pi-ai/src/models.ts +7 -4
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +74 -2
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +8 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +29 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +60 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.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 +18 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +23 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +63 -11
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +20 -6
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -5
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +9 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.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 +34 -10
- 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.ts +7 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +68 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -2
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +18 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +29 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +3 -0
- package/packages/pi-coding-agent/src/core/package-manager.ts +99 -58
- package/packages/pi-coding-agent/src/core/resource-loader.ts +24 -6
- package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +10 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +36 -11
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-timeout.test.ts +122 -0
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +40 -4
- package/src/resources/extensions/async-jobs/await-tool.test.ts +47 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +5 -0
- package/src/resources/extensions/async-jobs/index.ts +1 -0
- package/src/resources/extensions/async-jobs/job-manager.ts +2 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -2
- package/src/resources/extensions/gsd/auto/loop.ts +101 -1
- package/src/resources/extensions/gsd/auto/phases.ts +31 -13
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto/types.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +2 -18
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +15 -13
- package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
- package/src/resources/extensions/gsd/auto.ts +71 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +25 -4
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
- package/src/resources/extensions/gsd/context-injector.ts +100 -0
- package/src/resources/extensions/gsd/context-store.ts +4 -3
- package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
- package/src/resources/extensions/gsd/custom-verification.ts +180 -0
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
- package/src/resources/extensions/gsd/db-writer.ts +6 -2
- package/src/resources/extensions/gsd/definition-loader.ts +462 -0
- package/src/resources/extensions/gsd/detection.ts +20 -1
- package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
- package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +32 -1
- package/src/resources/extensions/gsd/doctor-providers.ts +13 -0
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/doctor.ts +12 -1
- package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
- package/src/resources/extensions/gsd/engine-types.ts +71 -0
- package/src/resources/extensions/gsd/execution-policy.ts +43 -0
- package/src/resources/extensions/gsd/exit-command.ts +14 -2
- package/src/resources/extensions/gsd/export.ts +8 -15
- package/src/resources/extensions/gsd/extension-manifest.json +2 -2
- package/src/resources/extensions/gsd/files.ts +29 -12
- package/src/resources/extensions/gsd/forensics.ts +101 -3
- package/src/resources/extensions/gsd/git-constants.ts +1 -0
- package/src/resources/extensions/gsd/git-service.ts +5 -5
- package/src/resources/extensions/gsd/gitignore.ts +1 -1
- package/src/resources/extensions/gsd/graph.ts +312 -0
- package/src/resources/extensions/gsd/gsd-db.ts +37 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +7 -3
- package/src/resources/extensions/gsd/journal.ts +134 -0
- package/src/resources/extensions/gsd/md-importer.ts +6 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +3 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
- package/src/resources/extensions/gsd/preferences-types.ts +6 -0
- package/src/resources/extensions/gsd/preferences.ts +63 -6
- package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +95 -7
- package/src/resources/extensions/gsd/rule-registry.ts +599 -0
- package/src/resources/extensions/gsd/rule-types.ts +68 -0
- package/src/resources/extensions/gsd/run-manager.ts +180 -0
- package/src/resources/extensions/gsd/service-tier.ts +184 -0
- package/src/resources/extensions/gsd/session-lock.ts +2 -2
- package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
- package/src/resources/extensions/gsd/templates/decisions.md +2 -2
- package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
- package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
- package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +56 -3
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +135 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +203 -106
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +79 -5
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
- package/src/resources/extensions/gsd/types.ts +3 -0
- package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
- package/src/resources/extensions/gsd/worktree-resolver.ts +32 -12
- package/src/resources/extensions/gsd/worktree.ts +2 -2
- package/src/resources/extensions/mcp-client/index.ts +5 -1
- package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
- package/src/resources/extensions/subagent/index.ts +7 -3
- package/src/resources/extensions/voice/index.ts +4 -4
- package/src/resources/skills/create-workflow/SKILL.md +103 -0
- package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
- /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → qw8qDHXOTLUXBq1vEknSz}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → qw8qDHXOTLUXBq1vEknSz}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import test, { describe } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
supportsServiceTier,
|
|
6
|
+
formatServiceTierStatus,
|
|
7
|
+
formatServiceTierFooterStatus,
|
|
8
|
+
resolveServiceTierIcon,
|
|
9
|
+
} from "../service-tier.ts";
|
|
10
|
+
|
|
11
|
+
// ─── supportsServiceTier ─────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
describe("supportsServiceTier", () => {
|
|
14
|
+
test("returns true for gpt-5.4", () => {
|
|
15
|
+
assert.equal(supportsServiceTier("gpt-5.4"), true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("returns true for gpt-5.4-pro", () => {
|
|
19
|
+
assert.equal(supportsServiceTier("gpt-5.4-pro"), true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("returns true for gpt-5.4-mini", () => {
|
|
23
|
+
assert.equal(supportsServiceTier("gpt-5.4-mini"), true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("returns true for openai/gpt-5.4 (provider-prefixed)", () => {
|
|
27
|
+
assert.equal(supportsServiceTier("openai/gpt-5.4"), true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("returns true for vibeproxy-openai/gpt-5.4 (proxy provider-prefixed)", () => {
|
|
31
|
+
assert.equal(supportsServiceTier("vibeproxy-openai/gpt-5.4"), true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("returns false for provider-only identifier without gpt-5.4 model suffix", () => {
|
|
35
|
+
assert.equal(supportsServiceTier("vibeproxy-openai"), false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("returns false for claude-opus-4-6", () => {
|
|
39
|
+
assert.equal(supportsServiceTier("claude-opus-4-6"), false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("returns false for gemini-2.5-pro", () => {
|
|
43
|
+
assert.equal(supportsServiceTier("gemini-2.5-pro"), false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("returns false for gpt-4o", () => {
|
|
47
|
+
assert.equal(supportsServiceTier("gpt-4o"), false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("returns false for empty string", () => {
|
|
51
|
+
assert.equal(supportsServiceTier(""), false);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// ─── formatServiceTierStatus ─────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
describe("formatServiceTierStatus", () => {
|
|
58
|
+
test("shows disabled when service_tier is undefined", () => {
|
|
59
|
+
const output = formatServiceTierStatus(undefined);
|
|
60
|
+
assert.ok(output.includes("disabled"), `Expected 'disabled' in: ${output}`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("mentions provider-agnostic model gating", () => {
|
|
64
|
+
const output = formatServiceTierStatus("priority");
|
|
65
|
+
assert.ok(output.includes("regardless of provider"), `Expected provider note in: ${output}`);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("shows priority when set to priority", () => {
|
|
69
|
+
const output = formatServiceTierStatus("priority");
|
|
70
|
+
assert.ok(output.includes("priority"), `Expected 'priority' in: ${output}`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("shows flex when set to flex", () => {
|
|
74
|
+
const output = formatServiceTierStatus("flex");
|
|
75
|
+
assert.ok(output.includes("flex"), `Expected 'flex' in: ${output}`);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ─── formatServiceTierFooterStatus ───────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
describe("formatServiceTierFooterStatus", () => {
|
|
82
|
+
test("returns priority footer status for supported model", () => {
|
|
83
|
+
assert.equal(formatServiceTierFooterStatus("priority", "vibeproxy-openai/gpt-5.4"), "fast: ⚡ priority");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("returns undefined for unsupported model", () => {
|
|
87
|
+
assert.equal(formatServiceTierFooterStatus("priority", "claude-opus-4-6"), undefined);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("returns undefined when tier is disabled", () => {
|
|
91
|
+
assert.equal(formatServiceTierFooterStatus(undefined, "gpt-5.4"), undefined);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ─── resolveServiceTierIcon ──────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
describe("resolveServiceTierIcon", () => {
|
|
98
|
+
test("returns lightning bolt for priority tier on supported model", () => {
|
|
99
|
+
const icon = resolveServiceTierIcon("priority", "gpt-5.4");
|
|
100
|
+
assert.equal(icon, "⚡");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("returns money icon for flex tier on supported model", () => {
|
|
104
|
+
const icon = resolveServiceTierIcon("flex", "gpt-5.4");
|
|
105
|
+
assert.equal(icon, "💰");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("returns empty string when tier is set but model does not support it", () => {
|
|
109
|
+
const icon = resolveServiceTierIcon("priority", "claude-opus-4-6");
|
|
110
|
+
assert.equal(icon, "");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("returns empty string when tier is undefined", () => {
|
|
114
|
+
const icon = resolveServiceTierIcon(undefined, "gpt-5.4");
|
|
115
|
+
assert.equal(icon, "");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("returns empty string when both tier and model are unsupported", () => {
|
|
119
|
+
const icon = resolveServiceTierIcon(undefined, "claude-opus-4-6");
|
|
120
|
+
assert.equal(icon, "");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("returns empty string when model is empty", () => {
|
|
124
|
+
const icon = resolveServiceTierIcon("priority", "");
|
|
125
|
+
assert.equal(icon, "");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -39,7 +39,7 @@ function buildBlock(
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
test("buildSkillActivationBlock
|
|
42
|
+
test("buildSkillActivationBlock does not auto-activate skills via broad context heuristic", () => {
|
|
43
43
|
const base = makeTempBase();
|
|
44
44
|
try {
|
|
45
45
|
writeSkill(base, "react", "Use for React components, hooks, JSX, and frontend UI work.");
|
|
@@ -52,7 +52,29 @@ test("buildSkillActivationBlock matches installed skills from task context", ()
|
|
|
52
52
|
taskTitle: "Implement React settings panel",
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
// Skills should not be activated just because their name appears in task context.
|
|
56
|
+
// Activation requires explicit preference sources (always_use, skill_rules, prefer_skills, skills_used).
|
|
57
|
+
assert.equal(result, "");
|
|
58
|
+
} finally {
|
|
59
|
+
cleanup(base);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("buildSkillActivationBlock activates skills via prefer_skills when context matches", () => {
|
|
64
|
+
const base = makeTempBase();
|
|
65
|
+
try {
|
|
66
|
+
writeSkill(base, "react", "Use for React components, hooks, JSX, and frontend UI work.");
|
|
67
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views, iOS layout, and Apple platform UI work.");
|
|
68
|
+
loadOnlyTestSkills(base);
|
|
69
|
+
|
|
70
|
+
const result = buildBlock(base, {
|
|
71
|
+
sliceTitle: "Build React dashboard",
|
|
72
|
+
taskId: "T01",
|
|
73
|
+
taskTitle: "Implement React settings panel",
|
|
74
|
+
}, {
|
|
75
|
+
prefer_skills: ["react"],
|
|
76
|
+
});
|
|
77
|
+
|
|
56
78
|
assert.match(result, /Call Skill\('react'\)/);
|
|
57
79
|
assert.doesNotMatch(result, /swiftui/);
|
|
58
80
|
} finally {
|
|
@@ -105,7 +127,7 @@ test("buildSkillActivationBlock includes skill_rules matches and task-plan skill
|
|
|
105
127
|
}
|
|
106
128
|
});
|
|
107
129
|
|
|
108
|
-
test("buildSkillActivationBlock honors avoid_skills", () => {
|
|
130
|
+
test("buildSkillActivationBlock honors avoid_skills against always_use_skills", () => {
|
|
109
131
|
const base = makeTempBase();
|
|
110
132
|
try {
|
|
111
133
|
writeSkill(base, "react", "Use for React components and frontend UI work.");
|
|
@@ -114,6 +136,7 @@ test("buildSkillActivationBlock honors avoid_skills", () => {
|
|
|
114
136
|
const result = buildBlock(base, {
|
|
115
137
|
taskTitle: "Implement React settings panel",
|
|
116
138
|
}, {
|
|
139
|
+
always_use_skills: ["react"],
|
|
117
140
|
avoid_skills: ["react"],
|
|
118
141
|
});
|
|
119
142
|
|
|
@@ -138,3 +161,33 @@ test("buildSkillActivationBlock falls back cleanly when nothing matches", () =>
|
|
|
138
161
|
cleanup(base);
|
|
139
162
|
}
|
|
140
163
|
});
|
|
164
|
+
|
|
165
|
+
test("buildSkillActivationBlock does not activate skills from extraContext or taskPlanContent body", () => {
|
|
166
|
+
const base = makeTempBase();
|
|
167
|
+
try {
|
|
168
|
+
writeSkill(base, "xcode-build", "Use for Xcode build workflows and iOS compilation.");
|
|
169
|
+
writeSkill(base, "ableton-lom", "Use for Ableton Live Object Model scripting.");
|
|
170
|
+
writeSkill(base, "frontend-design", "Use for frontend design systems and UI components.");
|
|
171
|
+
loadOnlyTestSkills(base);
|
|
172
|
+
|
|
173
|
+
const taskPlan = [
|
|
174
|
+
"---",
|
|
175
|
+
"skills_used: []",
|
|
176
|
+
"---",
|
|
177
|
+
"# T01: Build the API endpoint",
|
|
178
|
+
"Use xcode-build patterns and frontend-design tokens.",
|
|
179
|
+
].join("\n");
|
|
180
|
+
|
|
181
|
+
const result = buildBlock(base, {
|
|
182
|
+
taskTitle: "Build REST API",
|
|
183
|
+
extraContext: ["Build workflow for iOS and Ableton integration testing"],
|
|
184
|
+
taskPlanContent: taskPlan,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// None of these skills should activate — extraContext and taskPlanContent body
|
|
188
|
+
// must not be used for heuristic matching.
|
|
189
|
+
assert.equal(result, "");
|
|
190
|
+
} finally {
|
|
191
|
+
cleanup(base);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Tests the pure functions — no file I/O, no extension context.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { describe, it
|
|
6
|
+
import { describe, it } from "node:test";
|
|
7
7
|
import assert from "node:assert/strict";
|
|
8
8
|
import type { UnitMetrics } from "../metrics.js";
|
|
9
9
|
|
|
@@ -72,7 +72,7 @@ describe("skill-health", () => {
|
|
|
72
72
|
|
|
73
73
|
// With no metrics file, should return empty
|
|
74
74
|
const result = computeStaleAvoidList("/nonexistent/path", ["some-skill"]);
|
|
75
|
-
assert.
|
|
75
|
+
assert.deepEqual(result, []);
|
|
76
76
|
});
|
|
77
77
|
});
|
|
78
78
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #1855: Stalled tool detection crashes with
|
|
3
|
+
* "The path argument must be of type string. Received undefined"
|
|
4
|
+
*
|
|
5
|
+
* When a tool stalls in-flight for 10+ minutes, the idle watchdog fires
|
|
6
|
+
* recoverTimedOutUnit(). In auto/phases.ts, buildRecoveryContext was
|
|
7
|
+
* returning an empty object `{}`, so basePath was undefined. The recovery
|
|
8
|
+
* code passed undefined to readUnitRuntimeRecord → runtimePath → join(),
|
|
9
|
+
* which throws a TypeError. The session is permanently frozen because the
|
|
10
|
+
* error propagates into the idle watchdog catch handler but the unit
|
|
11
|
+
* promise is never resolved.
|
|
12
|
+
*
|
|
13
|
+
* This test calls recoverTimedOutUnit with an empty RecoveryContext (the
|
|
14
|
+
* bug) and verifies it crashes, then calls it with a valid RecoveryContext
|
|
15
|
+
* (the fix) and verifies it does not crash.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { recoverTimedOutUnit, type RecoveryContext } from "../auto-timeout-recovery.ts";
|
|
22
|
+
import { createTestContext } from './test-helpers.ts';
|
|
23
|
+
|
|
24
|
+
const { assertTrue, report } = createTestContext();
|
|
25
|
+
|
|
26
|
+
// Minimal mock for ExtensionContext — only the fields recoverTimedOutUnit touches.
|
|
27
|
+
function makeMockCtx() {
|
|
28
|
+
return {
|
|
29
|
+
ui: {
|
|
30
|
+
notify: () => {},
|
|
31
|
+
},
|
|
32
|
+
} as any;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Minimal mock for ExtensionAPI — only sendMessage is called during recovery.
|
|
36
|
+
function makeMockPi() {
|
|
37
|
+
return {
|
|
38
|
+
sendMessage: () => {},
|
|
39
|
+
} as any;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ═══ #1855: empty RecoveryContext (basePath undefined) crashes ════════════════
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
console.log("\n=== #1855: recoverTimedOutUnit crashes when basePath is undefined ===");
|
|
46
|
+
const ctx = makeMockCtx();
|
|
47
|
+
const pi = makeMockPi();
|
|
48
|
+
|
|
49
|
+
// Simulate the bug: buildRecoveryContext returns {} (empty object).
|
|
50
|
+
// basePath is undefined, which causes join(undefined, ".gsd") to throw.
|
|
51
|
+
const emptyRctx = {} as RecoveryContext;
|
|
52
|
+
|
|
53
|
+
let crashed = false;
|
|
54
|
+
try {
|
|
55
|
+
await recoverTimedOutUnit(ctx, pi, "execute-task", "M001/S01/T01", "idle", emptyRctx);
|
|
56
|
+
} catch (err: any) {
|
|
57
|
+
crashed = true;
|
|
58
|
+
assertTrue(
|
|
59
|
+
err.message.includes("path") || err.message.includes("string") || err.code === "ERR_INVALID_ARG_TYPE",
|
|
60
|
+
`should crash with path/type error, got: ${err.message}`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
assertTrue(crashed, "should crash when basePath is undefined (reproduces #1855)");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ═══ #1855: valid RecoveryContext does not crash ═════════════════════════════
|
|
67
|
+
|
|
68
|
+
{
|
|
69
|
+
console.log("\n=== #1855: recoverTimedOutUnit succeeds with valid RecoveryContext ===");
|
|
70
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-stalled-tool-test-"));
|
|
71
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
|
|
72
|
+
mkdirSync(join(base, ".gsd", "runtime", "units"), { recursive: true });
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const ctx = makeMockCtx();
|
|
76
|
+
const pi = makeMockPi();
|
|
77
|
+
|
|
78
|
+
const validRctx: RecoveryContext = {
|
|
79
|
+
basePath: base,
|
|
80
|
+
verbose: false,
|
|
81
|
+
currentUnitStartedAt: Date.now(),
|
|
82
|
+
unitRecoveryCount: new Map(),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
let crashed = false;
|
|
86
|
+
let result: string | undefined;
|
|
87
|
+
try {
|
|
88
|
+
result = await recoverTimedOutUnit(ctx, pi, "execute-task", "M001/S01/T01", "idle", validRctx);
|
|
89
|
+
} catch (err: any) {
|
|
90
|
+
crashed = true;
|
|
91
|
+
console.error(` Unexpected crash: ${err.message}`);
|
|
92
|
+
}
|
|
93
|
+
assertTrue(!crashed, "should not crash with valid basePath");
|
|
94
|
+
// With no runtime record on disk and recoveryAttempts=0, the function
|
|
95
|
+
// should attempt steering recovery (sendMessage) and return "recovered".
|
|
96
|
+
assertTrue(result === "recovered", `should return 'recovered', got '${result}'`);
|
|
97
|
+
} finally {
|
|
98
|
+
rmSync(base, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
report();
|
|
@@ -86,16 +86,17 @@ describe("structured-data-formatter: formatDecisionCompact", () => {
|
|
|
86
86
|
const result = formatDecisionCompact(sampleDecision);
|
|
87
87
|
assert.equal(
|
|
88
88
|
result,
|
|
89
|
-
"D001 | M001/S01 | architecture | Use SQLite for storage | WAL mode, single-writer | Built-in, no external deps | yes",
|
|
89
|
+
"D001 | M001/S01 | architecture | Use SQLite for storage | WAL mode, single-writer | Built-in, no external deps | yes | agent",
|
|
90
90
|
);
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
it("includes all fields in the correct order", () => {
|
|
94
94
|
const result = formatDecisionCompact(sampleDecision);
|
|
95
95
|
const parts = result.split(" | ");
|
|
96
|
-
assert.equal(parts.length,
|
|
96
|
+
assert.equal(parts.length, 8);
|
|
97
97
|
assert.equal(parts[0], "D001");
|
|
98
98
|
assert.equal(parts[6], "yes");
|
|
99
|
+
assert.equal(parts[7], "agent");
|
|
99
100
|
});
|
|
100
101
|
});
|
|
101
102
|
|
|
@@ -107,7 +108,7 @@ describe("structured-data-formatter: formatDecisionsCompact", () => {
|
|
|
107
108
|
it("includes Fields header line", () => {
|
|
108
109
|
const result = formatDecisionsCompact([sampleDecision]);
|
|
109
110
|
assert.ok(result.startsWith("# Decisions (compact)"));
|
|
110
|
-
assert.ok(result.includes("Fields: id | when | scope | decision | choice | rationale | revisable"));
|
|
111
|
+
assert.ok(result.includes("Fields: id | when | scope | decision | choice | rationale | revisable | made_by"));
|
|
111
112
|
});
|
|
112
113
|
|
|
113
114
|
it("formats multiple decisions on separate lines", () => {
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for macOS numbered symlink variant cleanup (#2205).
|
|
3
|
+
*
|
|
4
|
+
* macOS can rename `.gsd` to `.gsd 2`, `.gsd 3`, etc. when a directory
|
|
5
|
+
* already exists at the target path. ensureGsdSymlink() must detect and
|
|
6
|
+
* remove these numbered variants so the real `.gsd` symlink is always
|
|
7
|
+
* the one in use.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
mkdtempSync,
|
|
12
|
+
rmSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
existsSync,
|
|
15
|
+
lstatSync,
|
|
16
|
+
realpathSync,
|
|
17
|
+
mkdirSync,
|
|
18
|
+
symlinkSync,
|
|
19
|
+
readlinkSync,
|
|
20
|
+
} from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { tmpdir } from "node:os";
|
|
23
|
+
import { execSync } from "node:child_process";
|
|
24
|
+
|
|
25
|
+
import { ensureGsdSymlink, externalGsdRoot } from "../repo-identity.ts";
|
|
26
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
27
|
+
|
|
28
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
29
|
+
|
|
30
|
+
function run(command: string, cwd: string): string {
|
|
31
|
+
return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function main(): Promise<void> {
|
|
35
|
+
const base = realpathSync(mkdtempSync(join(tmpdir(), "gsd-symlink-variants-")));
|
|
36
|
+
const stateDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-state-variants-")));
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
process.env.GSD_STATE_DIR = stateDir;
|
|
40
|
+
|
|
41
|
+
// Set up a minimal git repo
|
|
42
|
+
run("git init -b main", base);
|
|
43
|
+
run('git config user.name "Pi Test"', base);
|
|
44
|
+
run('git config user.email "pi@example.com"', base);
|
|
45
|
+
run('git remote add origin git@github.com:example/repo.git', base);
|
|
46
|
+
writeFileSync(join(base, "README.md"), "# Test Repo\n", "utf-8");
|
|
47
|
+
run("git add README.md", base);
|
|
48
|
+
run('git commit -m "chore: init"', base);
|
|
49
|
+
|
|
50
|
+
const externalPath = externalGsdRoot(base);
|
|
51
|
+
|
|
52
|
+
// ── Test: numbered variant directories are cleaned up ──────────────
|
|
53
|
+
console.log("\n=== ensureGsdSymlink removes numbered .gsd variants (#2205) ===");
|
|
54
|
+
{
|
|
55
|
+
// Simulate macOS creating numbered variants: ".gsd 2", ".gsd 3"
|
|
56
|
+
mkdirSync(join(base, ".gsd 2"), { recursive: true });
|
|
57
|
+
mkdirSync(join(base, ".gsd 3"), { recursive: true });
|
|
58
|
+
mkdirSync(join(base, ".gsd 4"), { recursive: true });
|
|
59
|
+
|
|
60
|
+
const result = ensureGsdSymlink(base);
|
|
61
|
+
assertEq(result, externalPath, "ensureGsdSymlink returns external path");
|
|
62
|
+
assertTrue(existsSync(join(base, ".gsd")), ".gsd exists after ensureGsdSymlink");
|
|
63
|
+
assertTrue(lstatSync(join(base, ".gsd")).isSymbolicLink(), ".gsd is a symlink");
|
|
64
|
+
|
|
65
|
+
// The numbered variants must have been removed
|
|
66
|
+
assertTrue(!existsSync(join(base, ".gsd 2")), '".gsd 2" directory was cleaned up');
|
|
67
|
+
assertTrue(!existsSync(join(base, ".gsd 3")), '".gsd 3" directory was cleaned up');
|
|
68
|
+
assertTrue(!existsSync(join(base, ".gsd 4")), '".gsd 4" directory was cleaned up');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── Test: numbered variant symlinks are cleaned up ─────────────────
|
|
72
|
+
console.log("\n=== ensureGsdSymlink removes numbered symlink variants ===");
|
|
73
|
+
{
|
|
74
|
+
// Clean slate
|
|
75
|
+
rmSync(join(base, ".gsd"), { recursive: true, force: true });
|
|
76
|
+
|
|
77
|
+
// Simulate: ".gsd 2" is a symlink to the correct target (the real .gsd)
|
|
78
|
+
// and ".gsd" doesn't exist — this is the actual macOS scenario
|
|
79
|
+
const staleTarget = join(stateDir, "projects", "stale-target");
|
|
80
|
+
mkdirSync(staleTarget, { recursive: true });
|
|
81
|
+
symlinkSync(externalPath, join(base, ".gsd 2"), "junction");
|
|
82
|
+
symlinkSync(staleTarget, join(base, ".gsd 3"), "junction");
|
|
83
|
+
|
|
84
|
+
const result = ensureGsdSymlink(base);
|
|
85
|
+
assertEq(result, externalPath, "ensureGsdSymlink returns external path when variants exist");
|
|
86
|
+
assertTrue(existsSync(join(base, ".gsd")), ".gsd exists");
|
|
87
|
+
assertTrue(lstatSync(join(base, ".gsd")).isSymbolicLink(), ".gsd is a symlink");
|
|
88
|
+
|
|
89
|
+
assertTrue(!existsSync(join(base, ".gsd 2")), '".gsd 2" symlink variant was cleaned up');
|
|
90
|
+
assertTrue(!existsSync(join(base, ".gsd 3")), '".gsd 3" symlink variant was cleaned up');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── Test: real .gsd directory blocks symlink, but variants still cleaned ──
|
|
94
|
+
console.log("\n=== ensureGsdSymlink cleans variants even when .gsd is a real directory ===");
|
|
95
|
+
{
|
|
96
|
+
// Clean slate
|
|
97
|
+
rmSync(join(base, ".gsd"), { recursive: true, force: true });
|
|
98
|
+
|
|
99
|
+
// .gsd is a real directory (git-tracked) and numbered variants exist
|
|
100
|
+
mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
|
|
101
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001.md"), "# M001\n", "utf-8");
|
|
102
|
+
mkdirSync(join(base, ".gsd 2"), { recursive: true });
|
|
103
|
+
mkdirSync(join(base, ".gsd 3"), { recursive: true });
|
|
104
|
+
|
|
105
|
+
const result = ensureGsdSymlink(base);
|
|
106
|
+
// When .gsd is a real directory, ensureGsdSymlink preserves it
|
|
107
|
+
assertEq(result, join(base, ".gsd"), "real .gsd directory preserved");
|
|
108
|
+
assertTrue(lstatSync(join(base, ".gsd")).isDirectory(), ".gsd remains a directory");
|
|
109
|
+
|
|
110
|
+
// But the numbered variants should still be cleaned up
|
|
111
|
+
assertTrue(!existsSync(join(base, ".gsd 2")), '".gsd 2" cleaned even when .gsd is a directory');
|
|
112
|
+
assertTrue(!existsSync(join(base, ".gsd 3")), '".gsd 3" cleaned even when .gsd is a directory');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Test: only numeric-suffixed variants are removed ───────────────
|
|
116
|
+
console.log("\n=== ensureGsdSymlink only removes .gsd + space + digit variants ===");
|
|
117
|
+
{
|
|
118
|
+
rmSync(join(base, ".gsd"), { recursive: true, force: true });
|
|
119
|
+
|
|
120
|
+
// These should NOT be touched
|
|
121
|
+
mkdirSync(join(base, ".gsd-backup"), { recursive: true });
|
|
122
|
+
mkdirSync(join(base, ".gsd_old"), { recursive: true });
|
|
123
|
+
|
|
124
|
+
// These SHOULD be removed (macOS collision pattern)
|
|
125
|
+
mkdirSync(join(base, ".gsd 2"), { recursive: true });
|
|
126
|
+
mkdirSync(join(base, ".gsd 10"), { recursive: true });
|
|
127
|
+
|
|
128
|
+
ensureGsdSymlink(base);
|
|
129
|
+
|
|
130
|
+
assertTrue(existsSync(join(base, ".gsd-backup")), ".gsd-backup is NOT removed");
|
|
131
|
+
assertTrue(existsSync(join(base, ".gsd_old")), ".gsd_old is NOT removed");
|
|
132
|
+
assertTrue(!existsSync(join(base, ".gsd 2")), '".gsd 2" removed');
|
|
133
|
+
assertTrue(!existsSync(join(base, ".gsd 10")), '".gsd 10" removed');
|
|
134
|
+
|
|
135
|
+
// Cleanup non-variant dirs
|
|
136
|
+
rmSync(join(base, ".gsd-backup"), { recursive: true, force: true });
|
|
137
|
+
rmSync(join(base, ".gsd_old"), { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
} finally {
|
|
141
|
+
delete process.env.GSD_STATE_DIR;
|
|
142
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
143
|
+
try { rmSync(stateDir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
144
|
+
report();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
main().catch((error) => {
|
|
149
|
+
console.error(error);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
});
|
|
@@ -118,6 +118,51 @@ console.log('\n── Loop guard: arg order is normalized ──');
|
|
|
118
118
|
assertEq(getToolCallLoopCount(), 2, 'Should detect as same call regardless of key order');
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
122
|
+
// Nested/array arguments produce distinct hashes
|
|
123
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
124
|
+
|
|
125
|
+
console.log('\n── Loop guard: nested args are not stripped ──');
|
|
126
|
+
|
|
127
|
+
{
|
|
128
|
+
resetToolCallLoopGuard();
|
|
129
|
+
|
|
130
|
+
// Simulate ask_user_questions-style calls with different nested content
|
|
131
|
+
for (let i = 1; i <= 5; i++) {
|
|
132
|
+
const result = checkToolCallLoop('ask_user_questions', {
|
|
133
|
+
questions: [{ id: `q${i}`, question: `Question ${i}?` }],
|
|
134
|
+
});
|
|
135
|
+
assertTrue(result.block === false, `Nested call ${i} with unique content should be allowed`);
|
|
136
|
+
assertEq(getToolCallLoopCount(), 1, `Each unique nested call should reset count to 1`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Truly identical nested calls should still be detected
|
|
140
|
+
resetToolCallLoopGuard();
|
|
141
|
+
for (let i = 1; i <= 4; i++) {
|
|
142
|
+
checkToolCallLoop('ask_user_questions', {
|
|
143
|
+
questions: [{ id: 'same', question: 'Same?' }],
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const blocked = checkToolCallLoop('ask_user_questions', {
|
|
147
|
+
questions: [{ id: 'same', question: 'Same?' }],
|
|
148
|
+
});
|
|
149
|
+
assertTrue(blocked.block === true, 'Identical nested calls should still be blocked');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
153
|
+
// Nested object key order is normalized
|
|
154
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
155
|
+
|
|
156
|
+
console.log('\n── Loop guard: nested key order is normalized ──');
|
|
157
|
+
|
|
158
|
+
{
|
|
159
|
+
resetToolCallLoopGuard();
|
|
160
|
+
|
|
161
|
+
checkToolCallLoop('tool', { outer: { b: 2, a: 1 } });
|
|
162
|
+
const result = checkToolCallLoop('tool', { outer: { a: 1, b: 2 } });
|
|
163
|
+
assertEq(getToolCallLoopCount(), 2, 'Same nested args in different key order should match');
|
|
164
|
+
}
|
|
165
|
+
|
|
121
166
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
122
167
|
|
|
123
168
|
report();
|