gsd-pi 2.78.1-dev.84a383f51 → 2.78.1
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 +7 -7
- package/dist/cli.js +55 -95
- package/dist/headless-query.d.ts +0 -22
- package/dist/headless-query.js +4 -24
- package/dist/headless.d.ts +0 -10
- package/dist/headless.js +1 -16
- package/dist/loader.js +10 -7
- package/dist/onboarding.d.ts +0 -10
- package/dist/onboarding.js +2 -2
- package/dist/provider-migrations.d.ts +2 -2
- package/dist/provider-migrations.js +2 -5
- package/dist/resource-loader.d.ts +2 -5
- package/dist/resource-loader.js +5 -28
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +601 -0
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +651 -0
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +91 -0
- package/dist/resources/extensions/gsd/auto/loop.js +0 -23
- package/dist/resources/extensions/gsd/auto/phases.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +1 -3
- package/dist/resources/extensions/gsd/auto/session.js +0 -3
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -43
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +0 -30
- package/dist/resources/extensions/gsd/auto.js +5 -14
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -14
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +5 -7
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +4 -5
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +31 -94
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +6 -11
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +8 -34
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +2 -38
- package/dist/resources/extensions/gsd/commands/catalog.js +5 -69
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -22
- package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -4
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +1 -39
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/forensics.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +5 -12
- package/dist/resources/extensions/gsd/gsd-db.js +2 -11
- package/dist/resources/extensions/gsd/guided-flow.js +23 -23
- package/dist/resources/extensions/gsd/memory-store.js +31 -66
- package/dist/resources/extensions/gsd/model-router.js +9 -114
- package/dist/resources/extensions/gsd/native-git-bridge.js +1 -7
- package/dist/resources/extensions/gsd/preferences-models.js +15 -91
- package/dist/resources/extensions/gsd/preferences-types.js +0 -2
- package/dist/resources/extensions/gsd/preferences-validation.js +0 -32
- package/dist/resources/extensions/gsd/preferences.js +3 -5
- package/dist/resources/extensions/gsd/prompt-loader.js +12 -23
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -9
- package/dist/resources/extensions/gsd/state.js +0 -42
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +0 -1
- package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +112 -0
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +23 -0
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +5 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -18
- package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
- package/dist/resources/extensions/gsd/watch/header-renderer.js +1 -3
- package/dist/resources/extensions/gsd/worktree-command.js +46 -26
- package/dist/resources/extensions/mcp-client/index.js +3 -6
- package/dist/resources/extensions/slash-commands/create-extension.js +22 -36
- package/dist/resources/skills/create-gsd-extension/SKILL.md +5 -9
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +12 -32
- package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
- package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +608 -0
- package/dist/rtk-shared.d.ts +0 -3
- package/dist/rtk-shared.js +0 -17
- package/dist/rtk.d.ts +5 -2
- package/dist/rtk.js +20 -3
- 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 +13 -13
- 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 +4 -44
- 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 +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/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/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/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/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/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 +2 -2
- 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 -4
- 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/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- 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 +13 -13
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- 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 +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +11 -0
- package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +20 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-151349214571e2b6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
- package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +1 -0
- 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/package.json +1 -2
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +46 -74
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +0 -26
- package/packages/mcp-server/src/workflow-tools.ts +58 -93
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +19 -48
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +0 -13
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +3 -24
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +0 -26
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +20 -52
- package/packages/pi-ai/src/types.ts +0 -13
- package/packages/pi-ai/src/utils/repair-tool-json.ts +3 -24
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +0 -32
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +0 -6
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +0 -4
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +2 -19
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +0 -10
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +0 -18
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +0 -13
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -20
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -14
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +0 -7
- package/packages/pi-coding-agent/src/core/messages.ts +0 -4
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +2 -32
- package/packages/pi-coding-agent/src/core/model-registry.ts +0 -21
- package/packages/pi-coding-agent/src/core/system-prompt.ts +15 -33
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -17
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +3 -17
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +3 -20
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/loop.ts +2 -24
- package/src/resources/extensions/gsd/auto/phases.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +1 -3
- package/src/resources/extensions/gsd/auto/session.ts +0 -3
- package/src/resources/extensions/gsd/auto/types.ts +0 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +8 -46
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +4 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +0 -38
- package/src/resources/extensions/gsd/auto.ts +4 -14
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +13 -15
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +7 -8
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +9 -10
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +31 -102
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +6 -12
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -39
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +11 -39
- package/src/resources/extensions/gsd/commands/catalog.ts +5 -75
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -22
- package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -3
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -15
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -4
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +1 -39
- package/src/resources/extensions/gsd/doctor-types.ts +1 -3
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/forensics.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +5 -13
- package/src/resources/extensions/gsd/gsd-db.ts +2 -12
- package/src/resources/extensions/gsd/guided-flow.ts +25 -25
- package/src/resources/extensions/gsd/memory-store.ts +28 -81
- package/src/resources/extensions/gsd/model-router.ts +9 -172
- package/src/resources/extensions/gsd/native-git-bridge.ts +1 -7
- package/src/resources/extensions/gsd/preferences-models.ts +15 -101
- package/src/resources/extensions/gsd/preferences-types.ts +0 -6
- package/src/resources/extensions/gsd/preferences-validation.ts +0 -35
- package/src/resources/extensions/gsd/preferences.ts +2 -16
- package/src/resources/extensions/gsd/prompt-loader.ts +12 -26
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -9
- package/src/resources/extensions/gsd/state.ts +0 -42
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +0 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +1 -178
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +0 -58
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +5 -9
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -21
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +211 -138
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +59 -142
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +4 -7
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +32 -89
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +23 -41
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +43 -3
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +3 -5
- package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +87 -22
- package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +118 -7
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +60 -18
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +76 -14
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +83 -22
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +63 -1
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +1 -26
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +0 -30
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +4 -14
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +12 -22
- package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +1 -64
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +0 -22
- package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +0 -128
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +1 -33
- package/src/resources/extensions/gsd/tests/model-router.test.ts +8 -169
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +0 -8
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +43 -32
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +10 -4
- package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -127
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +0 -16
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +0 -7
- package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +19 -168
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +1 -7
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -23
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +4 -51
- package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +16 -7
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +1 -15
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +0 -15
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -17
- package/src/resources/extensions/gsd/unit-context-manifest.ts +8 -8
- package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
- package/src/resources/extensions/gsd/watch/header-renderer.ts +1 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
- package/src/resources/extensions/gsd/worktree-command.ts +44 -31
- package/src/resources/extensions/mcp-client/index.ts +3 -6
- package/src/resources/extensions/slash-commands/create-extension.ts +24 -38
- package/src/resources/skills/create-gsd-extension/SKILL.md +5 -9
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +12 -32
- package/dist/cli-policy.d.ts +0 -13
- package/dist/cli-policy.js +0 -17
- package/dist/resources/.managed-resources-content-hash +0 -1
- package/dist/resources/extensions/gsd/auto-runtime-state.js +0 -31
- package/dist/resources/extensions/gsd/milestone-id-reservation.js +0 -36
- package/dist/resources/extensions/gsd/worktree-session-state.js +0 -33
- package/dist/runtime-checks.d.ts +0 -27
- package/dist/runtime-checks.js +0 -38
- package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +0 -1
- package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +0 -1
- package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +0 -1
- package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +0 -1
- package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +0 -1
- package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +0 -10
- package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +0 -2
- package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +0 -20
- package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-9bf2e0c50fb2ca05.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +0 -1
- package/dist/worktree-status-banner.d.ts +0 -1
- package/dist/worktree-status-banner.js +0 -132
- package/packages/mcp-server/dist/alias-telemetry.d.ts +0 -8
- package/packages/mcp-server/dist/alias-telemetry.d.ts.map +0 -1
- package/packages/mcp-server/dist/alias-telemetry.js +0 -30
- package/packages/mcp-server/dist/alias-telemetry.js.map +0 -1
- package/packages/mcp-server/src/alias-telemetry.test.ts +0 -78
- package/packages/mcp-server/src/alias-telemetry.ts +0 -30
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +0 -2
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +0 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +0 -231
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +0 -1
- package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +0 -289
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +0 -37
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/core/token-telemetry.js +0 -49
- package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +0 -133
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +0 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +0 -78
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +0 -1
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +0 -181
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +0 -1
- package/packages/pi-coding-agent/src/core/token-telemetry.ts +0 -77
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +0 -212
- package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +0 -102
- package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +0 -200
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +0 -2
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +0 -1
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +0 -161
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +0 -1
- package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +0 -219
- package/src/resources/extensions/gsd/auto-runtime-state.ts +0 -51
- package/src/resources/extensions/gsd/milestone-id-reservation.ts +0 -47
- package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +0 -116
- package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +0 -100
- package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +0 -93
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +0 -47
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +0 -63
- package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +0 -81
- package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +0 -57
- package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +0 -145
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +0 -124
- package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +0 -152
- package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +0 -50
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +0 -93
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +0 -101
- package/src/resources/extensions/gsd/worktree-session-state.ts +0 -35
- package/src/resources/extensions/mcp-client/tests/global-config.test.ts +0 -91
- package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +0 -58
- /package/dist/web/standalone/.next/static/{UF5VF4F1tB0miEtJS7LyX → 7afp7gq8-DVbxum83zRQ-}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{UF5VF4F1tB0miEtJS7LyX → 7afp7gq8-DVbxum83zRQ-}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repair-tool-json.js","sourceRoot":"","sources":["../../src/utils/repair-tool-json.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Repair malformed JSON in LLM tool-call arguments.\n *\n * LLMs sometimes copy YAML template formatting into JSON tool arguments,\n * producing patterns like:\n *\n * \"keyDecisions\": - Used Web Notification API...,\n * \"keyFiles\": - src-tauri/src/lib.rs — Extended...\n *\n * instead of valid JSON arrays:\n *\n * \"keyDecisions\": [\"Used Web Notification API...\"],\n * \"keyFiles\": [\"src-tauri/src/lib.rs — Extended...\"]\n *\n * This module detects and repairs such patterns before JSON.parse is called.\n *\n * @see https://github.com/gsd-build/gsd-2/issues/2660\n */\n\n/**\n * Detect whether a JSON string contains YAML-style bullet-list values\n * (i.e. `\"key\": - item` instead of `\"key\": [\"item\"]`).\n */\nexport function hasYamlBulletLists(json: string): boolean {\n\t// Match: \"key\": followed by whitespace then a dash-space pattern (YAML bullet)\n\t// The negative lookahead excludes negative numbers (e.g. \"key\": -1)\n\treturn /\"\\s*:\\s*-\\s+(?!\\d)/.test(json);\n}\n\n/**\n * Detect whether a JSON string contains XML parameter tags\n * (i.e. `<parameter name=\"X\">value</parameter>`).\n *\n * Some models mix XML tool-call syntax into JSON string values,\n * producing hybrid output that fails JSON.parse.\n *\n * @see https://github.com/gsd-build/gsd-2/issues/3403\n */\nexport function hasXmlParameterTags(json: string): boolean {\n\treturn /<\\/?parameter[\\s>]/.test(json);\n}\n\n/**\n * Detect whether a JSON string contains truncated numeric values\n * (e.g. `\"exitCode\": -,` or `\"durationMs\": ,`).\n *\n * Smaller models sometimes emit incomplete numbers when the value\n * is cut off mid-generation.\n *\n * @see https://github.com/gsd-build/gsd-2/issues/3464\n */\nexport function hasTruncatedNumbers(json: string): boolean {\n\t// Match: colon, optional whitespace, then a comma or } without a value\n\t// Or: colon, optional whitespace, bare minus sign followed by comma/}\n\treturn /:\\s*,/.test(json) || /:\\s*-\\s*[,}]/.test(json);\n}\n\ntype XmlParameterBlock = {\n\tname: string;\n\tvalue: unknown;\n};\n\nconst xmlParameterBlockPattern = /<parameter\\s+name=\"([^\"]+)\"\\s*>([\\s\\S]*?)<\\/parameter>/g;\nconst xmlParameterOpenPattern = /<parameter\\s+name=\"([^\"]+)\"\\s*>/g;\n\nfunction parseXmlParameterValue(raw: string): unknown {\n\tconst trimmed = raw.trim();\n\tif (trimmed === \"\") return \"\";\n\ttry {\n\t\treturn JSON.parse(trimmed);\n\t} catch {\n\t\treturn trimmed;\n\t}\n}\n\nfunction extractXmlParameterBlocks(text: string): XmlParameterBlock[] {\n\tconst strictBlocks: XmlParameterBlock[] = [];\n\tlet hasNestedParameterOpening = false;\n\tfor (const match of text.matchAll(xmlParameterBlockPattern)) {\n\t\tconst rawValue = match[2] ?? \"\";\n\t\thasNestedParameterOpening ||= rawValue.includes(\"<parameter\");\n\t\tstrictBlocks.push({\n\t\t\tname: match[1],\n\t\t\tvalue: parseXmlParameterValue(rawValue),\n\t\t});\n\t}\n\tif (strictBlocks.length > 0 && !hasNestedParameterOpening) return strictBlocks;\n\n\tconst blocks: XmlParameterBlock[] = [];\n\tconst openings = [...text.matchAll(xmlParameterOpenPattern)];\n\tfor (let i = 0; i < openings.length; i++) {\n\t\tconst current = openings[i];\n\t\tconst next = openings[i + 1];\n\t\tif (current.index === undefined) continue;\n\n\t\tconst start = current.index + current[0].length;\n\t\tconst end = next?.index ?? text.length;\n\t\tconst rawValue = text.slice(start, end).replace(/\\s*<\\/parameter>\\s*$/, \"\");\n\t\tblocks.push({\n\t\t\tname: current[1],\n\t\t\tvalue: parseXmlParameterValue(rawValue),\n\t\t});\n\t}\n\treturn blocks;\n}\n\nfunction trimLeakedXmlTail(fieldName: string, value: string): string {\n\tlet cut = value.length;\n\tconst parameterIndex = value.indexOf(\"<parameter\");\n\tif (parameterIndex >= 0) cut = Math.min(cut, parameterIndex);\n\n\tconst closingTagIndex = value.indexOf(`</${fieldName}>`);\n\tif (closingTagIndex >= 0) cut = Math.min(cut, closingTagIndex);\n\n\treturn value.slice(0, cut).trimEnd();\n}\n\n/**\n * Strip XML `<parameter>` tags from a JSON string, leaving only the\n * text content. This handles the case where the LLM mixes XML\n * tool-call format into JSON string values.\n */\nfunction stripXmlParameterTags(json: string): string {\n\t// Remove opening tags: <parameter name=\"X\">\n\tlet cleaned = json.replace(/<parameter\\s+name=\"[^\"]*\"\\s*>/g, \"\");\n\t// Remove closing tags: </parameter>\n\tcleaned = cleaned.replace(/<\\/parameter>/g, \"\");\n\treturn cleaned;\n}\n\nfunction promoteXmlParametersToTopLevel(json: string): string {\n\ttry {\n\t\tconst parsed = JSON.parse(json) as Record<string, unknown>;\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\t\treturn stripXmlParameterTags(json);\n\t\t}\n\n\t\tlet changed = false;\n\t\tfor (const [fieldName, value] of Object.entries(parsed)) {\n\t\t\tif (typeof value !== \"string\" || !hasXmlParameterTags(value)) continue;\n\n\t\t\tconst blocks = extractXmlParameterBlocks(value);\n\t\t\tif (blocks.length === 0) continue;\n\n\t\t\tparsed[fieldName] = trimLeakedXmlTail(fieldName, value);\n\t\t\tfor (const block of blocks) {\n\t\t\t\tif (!(block.name in parsed)) {\n\t\t\t\t\tparsed[block.name] = block.value;\n\t\t\t\t}\n\t\t\t}\n\t\t\tchanged = true;\n\t\t}\n\n\t\treturn changed ? JSON.stringify(parsed) : stripXmlParameterTags(json);\n\t} catch {\n\t\treturn stripXmlParameterTags(json);\n\t}\n}\n\n/**\n * Replace truncated numeric values with 0.\n * Handles: `\"key\": ,` → `\"key\": 0,` and `\"key\": -,` → `\"key\": 0,`\n */\nfunction repairTruncatedNumbers(json: string): string {\n\t// Bare comma after colon (missing value entirely)\n\tlet repaired = json.replace(/:\\s*,/g, \": 0,\");\n\t// Bare minus sign followed by comma or closing brace\n\trepaired = repaired.replace(/:\\s*-\\s*([,}])/g, \": 0$1\");\n\treturn repaired;\n}\n\n/**\n * Attempt to repair malformed JSON in LLM tool-call arguments.\n *\n * Handles three categories of malformation:\n *\n * 1. **YAML bullet lists** (#2660): `\"key\": - item1\\n - item2` → `\"key\": [\"item1\", \"item2\"]`\n * 2. **XML parameter tags** (#3403): `<parameter name=\"X\">value</parameter>` → stripped to content\n * 3. **Truncated numbers** (#3464): `\"exitCode\": -,` → `\"exitCode\": 0,`\n *\n * Returns the original string unchanged if no patterns are detected\n * or if the repair itself would produce invalid JSON.\n */\nexport function repairToolJson(json: string): string {\n\tlet repaired = json;\n\n\t// Phase 1: Strip XML parameter tags\n\tif (hasXmlParameterTags(repaired)) {\n\t\trepaired = promoteXmlParametersToTopLevel(repaired);\n\t}\n\n\t// Phase 2: Repair truncated numbers\n\tif (hasTruncatedNumbers(repaired)) {\n\t\trepaired = repairTruncatedNumbers(repaired);\n\t}\n\n\t// Phase 3: Repair YAML bullet lists\n\tif (!hasYamlBulletLists(repaired)) {\n\t\treturn repaired;\n\t}\n\n\t// Strategy: find each `\"key\": - item1\\n - item2\\n - item3` region and\n\t// wrap items in a JSON array.\n\t//\n\t// We work on the raw string because the JSON is not parseable yet.\n\t// The pattern we target:\n\t// \"someKey\":\\s*- item text (possibly multiline)\n\t// optionally followed by more `- item` lines\n\t// terminated by the next `\"key\":` or `}` or end of string.\n\n\t// Match a key followed by YAML-style bullet list.\n\t// Capture: (1) the key portion including colon, (2) the bullet-list body,\n\t// (3) the separator (comma or empty) before the next key/bracket.\n\t// The bullet list body ends at the next `\"key\":` or `}` or `]` or end of string.\n\tconst keyBulletPattern =\n\t\t/(\"(?:[^\"\\\\]|\\\\.)*\"\\s*:\\s*)(- .+?)(,?\\s*)(?=\"(?:[^\"\\\\]|\\\\.)*\"\\s*:|[}\\]]|$)/gs;\n\n\trepaired = repaired.replace(\n\t\tkeyBulletPattern,\n\t\t(_match, keyPart: string, bulletBody: string, separator: string) => {\n\t\t\t// Split the bullet body into individual items on `- ` boundaries.\n\t\t\t// Items may contain embedded newlines for multi-line values.\n\t\t\tconst items = bulletBody\n\t\t\t\t.split(/\\n?\\s*- /)\n\t\t\t\t.filter((s) => s.trim().length > 0)\n\t\t\t\t.map((s) => s.replace(/,\\s*$/, \"\").trim());\n\n\t\t\t// JSON-encode each item as a string, then wrap in an array.\n\t\t\tconst jsonArray = \"[\" + items.map((item) => JSON.stringify(item)).join(\", \") + \"]\";\n\n\t\t\t// Re-emit the separator (comma) so the next key is properly delimited\n\t\t\tconst sep = separator.trim() ? separator : (/^\\s*\"/.test(separator + \"x\") ? \", \" : \"\");\n\t\t\treturn keyPart + jsonArray + sep;\n\t\t},\n\t);\n\n\t// Strip trailing commas before } or ] (common in repaired JSON)\n\trepaired = repaired.replace(/,(\\s*[}\\]])/g, \"$1\");\n\n\treturn repaired;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"repair-tool-json.js","sourceRoot":"","sources":["../../src/utils/repair-tool-json.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Repair malformed JSON in LLM tool-call arguments.\n *\n * LLMs sometimes copy YAML template formatting into JSON tool arguments,\n * producing patterns like:\n *\n * \"keyDecisions\": - Used Web Notification API...,\n * \"keyFiles\": - src-tauri/src/lib.rs — Extended...\n *\n * instead of valid JSON arrays:\n *\n * \"keyDecisions\": [\"Used Web Notification API...\"],\n * \"keyFiles\": [\"src-tauri/src/lib.rs — Extended...\"]\n *\n * This module detects and repairs such patterns before JSON.parse is called.\n *\n * @see https://github.com/gsd-build/gsd-2/issues/2660\n */\n\n/**\n * Detect whether a JSON string contains YAML-style bullet-list values\n * (i.e. `\"key\": - item` instead of `\"key\": [\"item\"]`).\n */\nexport function hasYamlBulletLists(json: string): boolean {\n\t// Match: \"key\": followed by whitespace then a dash-space pattern (YAML bullet)\n\t// The negative lookahead excludes negative numbers (e.g. \"key\": -1)\n\treturn /\"\\s*:\\s*-\\s+(?!\\d)/.test(json);\n}\n\n/**\n * Detect whether a JSON string contains XML parameter tags\n * (i.e. `<parameter name=\"X\">value</parameter>`).\n *\n * Some models mix XML tool-call syntax into JSON string values,\n * producing hybrid output that fails JSON.parse.\n *\n * @see https://github.com/gsd-build/gsd-2/issues/3403\n */\nexport function hasXmlParameterTags(json: string): boolean {\n\treturn /<\\/?parameter[\\s>]/.test(json);\n}\n\n/**\n * Detect whether a JSON string contains truncated numeric values\n * (e.g. `\"exitCode\": -,` or `\"durationMs\": ,`).\n *\n * Smaller models sometimes emit incomplete numbers when the value\n * is cut off mid-generation.\n *\n * @see https://github.com/gsd-build/gsd-2/issues/3464\n */\nexport function hasTruncatedNumbers(json: string): boolean {\n\t// Match: colon, optional whitespace, then a comma or } without a value\n\t// Or: colon, optional whitespace, bare minus sign followed by comma/}\n\treturn /:\\s*,/.test(json) || /:\\s*-\\s*[,}]/.test(json);\n}\n\ntype XmlParameterBlock = {\n\tname: string;\n\tvalue: unknown;\n};\n\nconst xmlParameterBlockPattern = /<parameter\\s+name=\"([^\"]+)\"\\s*>([\\s\\S]*?)<\\/parameter>/g;\n\nfunction parseXmlParameterValue(raw: string): unknown {\n\tconst trimmed = raw.trim();\n\tif (trimmed === \"\") return \"\";\n\ttry {\n\t\treturn JSON.parse(trimmed);\n\t} catch {\n\t\treturn trimmed;\n\t}\n}\n\nfunction extractXmlParameterBlocks(text: string): XmlParameterBlock[] {\n\tconst blocks: XmlParameterBlock[] = [];\n\tfor (const match of text.matchAll(xmlParameterBlockPattern)) {\n\t\tblocks.push({\n\t\t\tname: match[1],\n\t\t\tvalue: parseXmlParameterValue(match[2] ?? \"\"),\n\t\t});\n\t}\n\treturn blocks;\n}\n\nfunction trimLeakedXmlTail(fieldName: string, value: string): string {\n\tlet cut = value.length;\n\tconst parameterIndex = value.indexOf(\"<parameter\");\n\tif (parameterIndex >= 0) cut = Math.min(cut, parameterIndex);\n\n\tconst closingTagIndex = value.indexOf(`</${fieldName}>`);\n\tif (closingTagIndex >= 0) cut = Math.min(cut, closingTagIndex);\n\n\treturn value.slice(0, cut).trimEnd();\n}\n\n/**\n * Strip XML `<parameter>` tags from a JSON string, leaving only the\n * text content. This handles the case where the LLM mixes XML\n * tool-call format into JSON string values.\n */\nfunction stripXmlParameterTags(json: string): string {\n\t// Remove opening tags: <parameter name=\"X\">\n\tlet cleaned = json.replace(/<parameter\\s+name=\"[^\"]*\"\\s*>/g, \"\");\n\t// Remove closing tags: </parameter>\n\tcleaned = cleaned.replace(/<\\/parameter>/g, \"\");\n\treturn cleaned;\n}\n\nfunction promoteXmlParametersToTopLevel(json: string): string {\n\ttry {\n\t\tconst parsed = JSON.parse(json) as Record<string, unknown>;\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\t\treturn stripXmlParameterTags(json);\n\t\t}\n\n\t\tlet changed = false;\n\t\tfor (const [fieldName, value] of Object.entries(parsed)) {\n\t\t\tif (typeof value !== \"string\" || !hasXmlParameterTags(value)) continue;\n\n\t\t\tconst blocks = extractXmlParameterBlocks(value);\n\t\t\tif (blocks.length === 0) continue;\n\n\t\t\tparsed[fieldName] = trimLeakedXmlTail(fieldName, value);\n\t\t\tfor (const block of blocks) {\n\t\t\t\tif (!(block.name in parsed)) {\n\t\t\t\t\tparsed[block.name] = block.value;\n\t\t\t\t}\n\t\t\t}\n\t\t\tchanged = true;\n\t\t}\n\n\t\treturn changed ? JSON.stringify(parsed) : stripXmlParameterTags(json);\n\t} catch {\n\t\treturn stripXmlParameterTags(json);\n\t}\n}\n\n/**\n * Replace truncated numeric values with 0.\n * Handles: `\"key\": ,` → `\"key\": 0,` and `\"key\": -,` → `\"key\": 0,`\n */\nfunction repairTruncatedNumbers(json: string): string {\n\t// Bare comma after colon (missing value entirely)\n\tlet repaired = json.replace(/:\\s*,/g, \": 0,\");\n\t// Bare minus sign followed by comma or closing brace\n\trepaired = repaired.replace(/:\\s*-\\s*([,}])/g, \": 0$1\");\n\treturn repaired;\n}\n\n/**\n * Attempt to repair malformed JSON in LLM tool-call arguments.\n *\n * Handles three categories of malformation:\n *\n * 1. **YAML bullet lists** (#2660): `\"key\": - item1\\n - item2` → `\"key\": [\"item1\", \"item2\"]`\n * 2. **XML parameter tags** (#3403): `<parameter name=\"X\">value</parameter>` → stripped to content\n * 3. **Truncated numbers** (#3464): `\"exitCode\": -,` → `\"exitCode\": 0,`\n *\n * Returns the original string unchanged if no patterns are detected\n * or if the repair itself would produce invalid JSON.\n */\nexport function repairToolJson(json: string): string {\n\tlet repaired = json;\n\n\t// Phase 1: Strip XML parameter tags\n\tif (hasXmlParameterTags(repaired)) {\n\t\trepaired = promoteXmlParametersToTopLevel(repaired);\n\t}\n\n\t// Phase 2: Repair truncated numbers\n\tif (hasTruncatedNumbers(repaired)) {\n\t\trepaired = repairTruncatedNumbers(repaired);\n\t}\n\n\t// Phase 3: Repair YAML bullet lists\n\tif (!hasYamlBulletLists(repaired)) {\n\t\treturn repaired;\n\t}\n\n\t// Strategy: find each `\"key\": - item1\\n - item2\\n - item3` region and\n\t// wrap items in a JSON array.\n\t//\n\t// We work on the raw string because the JSON is not parseable yet.\n\t// The pattern we target:\n\t// \"someKey\":\\s*- item text (possibly multiline)\n\t// optionally followed by more `- item` lines\n\t// terminated by the next `\"key\":` or `}` or end of string.\n\n\t// Match a key followed by YAML-style bullet list.\n\t// Capture: (1) the key portion including colon, (2) the bullet-list body,\n\t// (3) the separator (comma or empty) before the next key/bracket.\n\t// The bullet list body ends at the next `\"key\":` or `}` or `]` or end of string.\n\tconst keyBulletPattern =\n\t\t/(\"(?:[^\"\\\\]|\\\\.)*\"\\s*:\\s*)(- .+?)(,?\\s*)(?=\"(?:[^\"\\\\]|\\\\.)*\"\\s*:|[}\\]]|$)/gs;\n\n\trepaired = repaired.replace(\n\t\tkeyBulletPattern,\n\t\t(_match, keyPart: string, bulletBody: string, separator: string) => {\n\t\t\t// Split the bullet body into individual items on `- ` boundaries.\n\t\t\t// Items may contain embedded newlines for multi-line values.\n\t\t\tconst items = bulletBody\n\t\t\t\t.split(/\\n?\\s*- /)\n\t\t\t\t.filter((s) => s.trim().length > 0)\n\t\t\t\t.map((s) => s.replace(/,\\s*$/, \"\").trim());\n\n\t\t\t// JSON-encode each item as a string, then wrap in an array.\n\t\t\tconst jsonArray = \"[\" + items.map((item) => JSON.stringify(item)).join(\", \") + \"]\";\n\n\t\t\t// Re-emit the separator (comma) so the next key is properly delimited\n\t\t\tconst sep = separator.trim() ? separator : (/^\\s*\"/.test(separator + \"x\") ? \", \" : \"\");\n\t\t\treturn keyPart + jsonArray + sep;\n\t\t},\n\t);\n\n\t// Strip trailing commas before } or ] (common in repaired JSON)\n\trepaired = repaired.replace(/,(\\s*[}\\]])/g, \"$1\");\n\n\treturn repaired;\n}\n"]}
|
|
@@ -103,32 +103,6 @@ describe("repairToolJson — XML parameter tag stripping (#3403)", () => {
|
|
|
103
103
|
assert.equal(parsed.oneLiner, "done");
|
|
104
104
|
assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
|
|
105
105
|
});
|
|
106
|
-
test("promotes dangling XML parameters trapped inside valid JSON string values", () => {
|
|
107
|
-
const malformed = JSON.stringify({
|
|
108
|
-
narrative: 'text.\n<parameter name="verification">all tests pass\n<parameter name="verificationEvidence">["npm test"]',
|
|
109
|
-
oneLiner: "done",
|
|
110
|
-
});
|
|
111
|
-
const repaired = repairToolJson(malformed);
|
|
112
|
-
const parsed = JSON.parse(repaired);
|
|
113
|
-
assert.equal(parsed.narrative, "text.");
|
|
114
|
-
assert.equal(parsed.verification, "all tests pass");
|
|
115
|
-
assert.deepEqual(parsed.verificationEvidence, ["npm test"]);
|
|
116
|
-
assert.equal(parsed.oneLiner, "done");
|
|
117
|
-
assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
|
|
118
|
-
});
|
|
119
|
-
test("promotes mixed dangling and closed XML parameters from valid JSON string values", () => {
|
|
120
|
-
const malformed = JSON.stringify({
|
|
121
|
-
narrative: 'text.\n<parameter name="verification">all tests pass\n<parameter name="verificationEvidence">["npm test"]</parameter>',
|
|
122
|
-
oneLiner: "done",
|
|
123
|
-
});
|
|
124
|
-
const repaired = repairToolJson(malformed);
|
|
125
|
-
const parsed = JSON.parse(repaired);
|
|
126
|
-
assert.equal(parsed.narrative, "text.");
|
|
127
|
-
assert.equal(parsed.verification, "all tests pass");
|
|
128
|
-
assert.deepEqual(parsed.verificationEvidence, ["npm test"]);
|
|
129
|
-
assert.equal(parsed.oneLiner, "done");
|
|
130
|
-
assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
|
|
131
|
-
});
|
|
132
106
|
});
|
|
133
107
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
134
108
|
// Truncated number repair (#3464)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repair-tool-json.test.js","sourceRoot":"","sources":["../../../src/utils/tests/repair-tool-json.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, test } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { repairToolJson, hasYamlBulletLists, hasXmlParameterTags, hasTruncatedNumbers } from \"../repair-tool-json.js\";\n\ndescribe(\"repairToolJson — YAML bullet list repair (#2660)\", () => {\n\t// ── Detection ──────────────────────────────────────────────────────────\n\n\ttest(\"hasYamlBulletLists detects YAML-style bullets\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('\"keyDecisions\": - Used Web Notification API'),\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest(\"hasYamlBulletLists ignores negative numbers\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('\"offset\": -1'),\n\t\t\tfalse,\n\t\t\t\"negative number should not be detected as YAML bullet\",\n\t\t);\n\t});\n\n\ttest(\"hasYamlBulletLists returns false for valid JSON\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('{\"keyDecisions\": [\"item1\", \"item2\"]}'),\n\t\t\tfalse,\n\t\t);\n\t});\n\n\t// ── Single bullet item ────────────────────────────────────────────────\n\n\ttest(\"repairs single YAML bullet to JSON array\", () => {\n\t\tconst malformed = '{\"keyDecisions\": - Used Web Notification API}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\"Used Web Notification API\"]);\n\t});\n\n\t// ── Multiple bullet items (newline-separated) ─────────────────────────\n\n\ttest(\"repairs multiple YAML bullets separated by newlines\", () => {\n\t\tconst malformed =\n\t\t\t'{\"keyDecisions\": - Used Web Notification API\\n - Chose Tauri over Electron\\n - Adopted SQLite for storage, \"title\": \"M005\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\n\t\t\t\"Used Web Notification API\",\n\t\t\t\"Chose Tauri over Electron\",\n\t\t\t\"Adopted SQLite for storage\",\n\t\t]);\n\t\tassert.equal(parsed.title, \"M005\");\n\t});\n\n\t// ── Multiple fields with YAML bullets ─────────────────────────────────\n\n\ttest(\"repairs multiple fields each with YAML bullet lists\", () => {\n\t\tconst malformed =\n\t\t\t'{\"keyDecisions\": - decision one\\n - decision two, \"keyFiles\": - src/lib.rs — Extended menu\\n - src/main.ts — Entry point, \"title\": \"done\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\"decision one\", \"decision two\"]);\n\t\tassert.deepEqual(parsed.keyFiles, [\n\t\t\t\"src/lib.rs \\u2014 Extended menu\",\n\t\t\t\"src/main.ts \\u2014 Entry point\",\n\t\t]);\n\t\tassert.equal(parsed.title, \"done\");\n\t});\n\n\t// ── Exact reproduction from issue #2660 ───────────────────────────────\n\n\ttest(\"repairs the exact malformed JSON from issue #2660\", () => {\n\t\tconst malformed = `{\"milestoneId\": \"M005\", \"title\": \"Native Desktop Polish\", \"oneLiner\": \"summary\", \"narrative\": \"details\", \"successCriteriaResults\": \"all pass\", \"definitionOfDoneResults\": \"all done\", \"requirementOutcomes\": \"met\", \"keyDecisions\": - Used Web Notification API (new window.Notification()) instead of Tauri sendNotification wrapper, \"keyFiles\": - src-tauri/src/lib.rs \\u2014 Extended menu builder with notification toggle, \"lessonsLearned\": - Always test notification permissions before sending, \"followUps\": \"none\", \"deviations\": \"none\", \"verificationPassed\": true}`;\n\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.milestoneId, \"M005\");\n\t\tassert.equal(parsed.title, \"Native Desktop Polish\");\n\t\tassert.ok(Array.isArray(parsed.keyDecisions), \"keyDecisions should be an array\");\n\t\tassert.ok(parsed.keyDecisions[0].includes(\"Web Notification API\"));\n\t\tassert.ok(Array.isArray(parsed.keyFiles), \"keyFiles should be an array\");\n\t\tassert.ok(parsed.keyFiles[0].includes(\"src-tauri/src/lib.rs\"));\n\t\tassert.ok(Array.isArray(parsed.lessonsLearned), \"lessonsLearned should be an array\");\n\t\tassert.equal(parsed.verificationPassed, true);\n\t});\n\n\t// ── Passthrough for valid JSON ────────────────────────────────────────\n\n\ttest(\"returns valid JSON unchanged\", () => {\n\t\tconst valid = '{\"keyDecisions\": [\"item1\", \"item2\"], \"count\": -5}';\n\t\tconst result = repairToolJson(valid);\n\t\tassert.equal(result, valid, \"valid JSON should be returned unchanged\");\n\t});\n\n\t// ── Negative numbers are preserved ────────────────────────────────────\n\n\ttest(\"does not mangle negative numbers\", () => {\n\t\tconst valid = '{\"offset\": -1, \"limit\": -100}';\n\t\tconst result = repairToolJson(valid);\n\t\tassert.equal(result, valid);\n\t});\n});\n\n// ═══════════════════════════════════════════════════════════════════════════\n// XML parameter tag repair (#3403)\n// ═══════════════════════════════════════════════════════════════════════════\n\ndescribe(\"repairToolJson — XML parameter tag stripping (#3403)\", () => {\n\ttest(\"hasXmlParameterTags detects opening tags\", () => {\n\t\tassert.equal(\n\t\t\thasXmlParameterTags('<parameter name=\"narrative\">some text</parameter>'),\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest(\"hasXmlParameterTags returns false for clean JSON\", () => {\n\t\tassert.equal(\n\t\t\thasXmlParameterTags('{\"narrative\": \"some text\"}'),\n\t\t\tfalse,\n\t\t);\n\t});\n\n\ttest(\"strips XML parameter tags from JSON values\", () => {\n\t\tconst malformed = '{\"sliceId\": \"S03\", \"narrative\": <parameter name=\"narrative\">The slice work</parameter>}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\t// After stripping tags, the content should be parseable or at least tag-free\n\t\tassert.ok(!repaired.includes(\"<parameter\"), \"should not contain <parameter tags\");\n\t\tassert.ok(!repaired.includes(\"</parameter>\"), \"should not contain </parameter> tags\");\n\t});\n\n\ttest(\"handles mixed XML and JSON content\", () => {\n\t\tconst malformed = '{\"oneLiner\": \"done\", \"verification\": <parameter name=\"verification\">all tests pass</parameter>}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tassert.ok(!repaired.includes(\"<parameter\"), \"XML tags should be stripped\");\n\t\tassert.ok(repaired.includes(\"all tests pass\"), \"content should be preserved\");\n\t});\n\n\ttest(\"promotes XML parameters trapped inside valid JSON string values\", () => {\n\t\tconst malformed =\n\t\t\t'{\"narrative\":\"text.</narrative>\\\\n<parameter name=\\\\\"verification\\\\\">all tests pass</parameter>\\\\n<parameter name=\\\\\"verificationEvidence\\\\\">[\\\\\"npm test\\\\\"]</parameter>\",\"oneLiner\":\"done\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.narrative, \"text.\");\n\t\tassert.equal(parsed.verification, \"all tests pass\");\n\t\tassert.deepEqual(parsed.verificationEvidence, [\"npm test\"]);\n\t\tassert.equal(parsed.oneLiner, \"done\");\n\t\tassert.ok(!parsed.narrative.includes(\"<parameter\"), \"narrative should not retain leaked XML\");\n\t});\n\n\ttest(\"promotes dangling XML parameters trapped inside valid JSON string values\", () => {\n\t\tconst malformed = JSON.stringify({\n\t\t\tnarrative:\n\t\t\t\t'text.\\n<parameter name=\"verification\">all tests pass\\n<parameter name=\"verificationEvidence\">[\"npm test\"]',\n\t\t\toneLiner: \"done\",\n\t\t});\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.narrative, \"text.\");\n\t\tassert.equal(parsed.verification, \"all tests pass\");\n\t\tassert.deepEqual(parsed.verificationEvidence, [\"npm test\"]);\n\t\tassert.equal(parsed.oneLiner, \"done\");\n\t\tassert.ok(!parsed.narrative.includes(\"<parameter\"), \"narrative should not retain leaked XML\");\n\t});\n\n\ttest(\"promotes mixed dangling and closed XML parameters from valid JSON string values\", () => {\n\t\tconst malformed = JSON.stringify({\n\t\t\tnarrative:\n\t\t\t\t'text.\\n<parameter name=\"verification\">all tests pass\\n<parameter name=\"verificationEvidence\">[\"npm test\"]</parameter>',\n\t\t\toneLiner: \"done\",\n\t\t});\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.narrative, \"text.\");\n\t\tassert.equal(parsed.verification, \"all tests pass\");\n\t\tassert.deepEqual(parsed.verificationEvidence, [\"npm test\"]);\n\t\tassert.equal(parsed.oneLiner, \"done\");\n\t\tassert.ok(!parsed.narrative.includes(\"<parameter\"), \"narrative should not retain leaked XML\");\n\t});\n});\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Truncated number repair (#3464)\n// ═══════════════════════════════════════════════════════════════════════════\n\ndescribe(\"repairToolJson — truncated number repair (#3464)\", () => {\n\ttest(\"hasTruncatedNumbers detects bare comma after colon\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": ,'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers detects bare minus before comma\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": -,'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers detects bare minus before closing brace\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"durationMs\": -}'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers returns false for valid numbers\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": 0, \"durationMs\": 1234'), false);\n\t});\n\n\ttest(\"hasTruncatedNumbers returns false for negative numbers\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": -1, \"offset\": -100'), false);\n\t});\n\n\ttest(\"repairs truncated exitCode with bare comma\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": , \"verdict\": \"pass\", \"durationMs\": 500}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, 0);\n\t\tassert.equal(parsed.durationMs, 500);\n\t});\n\n\ttest(\"repairs truncated exitCode with bare minus\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": -, \"verdict\": \"pass\", \"durationMs\": 1234}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, 0);\n\t\tassert.equal(parsed.verdict, \"pass\");\n\t});\n\n\ttest(\"repairs truncated durationMs at end of object\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": 0, \"verdict\": \"pass\", \"durationMs\": -}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.durationMs, 0);\n\t\tassert.equal(parsed.exitCode, 0);\n\t});\n\n\ttest(\"does not mangle valid negative numbers\", () => {\n\t\tconst valid = '{\"exitCode\": -1, \"offset\": -100}';\n\t\tconst repaired = repairToolJson(valid);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, -1);\n\t\tassert.equal(parsed.offset, -100);\n\t});\n});\n"]}
|
|
1
|
+
{"version":3,"file":"repair-tool-json.test.js","sourceRoot":"","sources":["../../../src/utils/tests/repair-tool-json.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, test } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { repairToolJson, hasYamlBulletLists, hasXmlParameterTags, hasTruncatedNumbers } from \"../repair-tool-json.js\";\n\ndescribe(\"repairToolJson — YAML bullet list repair (#2660)\", () => {\n\t// ── Detection ──────────────────────────────────────────────────────────\n\n\ttest(\"hasYamlBulletLists detects YAML-style bullets\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('\"keyDecisions\": - Used Web Notification API'),\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest(\"hasYamlBulletLists ignores negative numbers\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('\"offset\": -1'),\n\t\t\tfalse,\n\t\t\t\"negative number should not be detected as YAML bullet\",\n\t\t);\n\t});\n\n\ttest(\"hasYamlBulletLists returns false for valid JSON\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('{\"keyDecisions\": [\"item1\", \"item2\"]}'),\n\t\t\tfalse,\n\t\t);\n\t});\n\n\t// ── Single bullet item ────────────────────────────────────────────────\n\n\ttest(\"repairs single YAML bullet to JSON array\", () => {\n\t\tconst malformed = '{\"keyDecisions\": - Used Web Notification API}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\"Used Web Notification API\"]);\n\t});\n\n\t// ── Multiple bullet items (newline-separated) ─────────────────────────\n\n\ttest(\"repairs multiple YAML bullets separated by newlines\", () => {\n\t\tconst malformed =\n\t\t\t'{\"keyDecisions\": - Used Web Notification API\\n - Chose Tauri over Electron\\n - Adopted SQLite for storage, \"title\": \"M005\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\n\t\t\t\"Used Web Notification API\",\n\t\t\t\"Chose Tauri over Electron\",\n\t\t\t\"Adopted SQLite for storage\",\n\t\t]);\n\t\tassert.equal(parsed.title, \"M005\");\n\t});\n\n\t// ── Multiple fields with YAML bullets ─────────────────────────────────\n\n\ttest(\"repairs multiple fields each with YAML bullet lists\", () => {\n\t\tconst malformed =\n\t\t\t'{\"keyDecisions\": - decision one\\n - decision two, \"keyFiles\": - src/lib.rs — Extended menu\\n - src/main.ts — Entry point, \"title\": \"done\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\"decision one\", \"decision two\"]);\n\t\tassert.deepEqual(parsed.keyFiles, [\n\t\t\t\"src/lib.rs \\u2014 Extended menu\",\n\t\t\t\"src/main.ts \\u2014 Entry point\",\n\t\t]);\n\t\tassert.equal(parsed.title, \"done\");\n\t});\n\n\t// ── Exact reproduction from issue #2660 ───────────────────────────────\n\n\ttest(\"repairs the exact malformed JSON from issue #2660\", () => {\n\t\tconst malformed = `{\"milestoneId\": \"M005\", \"title\": \"Native Desktop Polish\", \"oneLiner\": \"summary\", \"narrative\": \"details\", \"successCriteriaResults\": \"all pass\", \"definitionOfDoneResults\": \"all done\", \"requirementOutcomes\": \"met\", \"keyDecisions\": - Used Web Notification API (new window.Notification()) instead of Tauri sendNotification wrapper, \"keyFiles\": - src-tauri/src/lib.rs \\u2014 Extended menu builder with notification toggle, \"lessonsLearned\": - Always test notification permissions before sending, \"followUps\": \"none\", \"deviations\": \"none\", \"verificationPassed\": true}`;\n\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.milestoneId, \"M005\");\n\t\tassert.equal(parsed.title, \"Native Desktop Polish\");\n\t\tassert.ok(Array.isArray(parsed.keyDecisions), \"keyDecisions should be an array\");\n\t\tassert.ok(parsed.keyDecisions[0].includes(\"Web Notification API\"));\n\t\tassert.ok(Array.isArray(parsed.keyFiles), \"keyFiles should be an array\");\n\t\tassert.ok(parsed.keyFiles[0].includes(\"src-tauri/src/lib.rs\"));\n\t\tassert.ok(Array.isArray(parsed.lessonsLearned), \"lessonsLearned should be an array\");\n\t\tassert.equal(parsed.verificationPassed, true);\n\t});\n\n\t// ── Passthrough for valid JSON ────────────────────────────────────────\n\n\ttest(\"returns valid JSON unchanged\", () => {\n\t\tconst valid = '{\"keyDecisions\": [\"item1\", \"item2\"], \"count\": -5}';\n\t\tconst result = repairToolJson(valid);\n\t\tassert.equal(result, valid, \"valid JSON should be returned unchanged\");\n\t});\n\n\t// ── Negative numbers are preserved ────────────────────────────────────\n\n\ttest(\"does not mangle negative numbers\", () => {\n\t\tconst valid = '{\"offset\": -1, \"limit\": -100}';\n\t\tconst result = repairToolJson(valid);\n\t\tassert.equal(result, valid);\n\t});\n});\n\n// ═══════════════════════════════════════════════════════════════════════════\n// XML parameter tag repair (#3403)\n// ═══════════════════════════════════════════════════════════════════════════\n\ndescribe(\"repairToolJson — XML parameter tag stripping (#3403)\", () => {\n\ttest(\"hasXmlParameterTags detects opening tags\", () => {\n\t\tassert.equal(\n\t\t\thasXmlParameterTags('<parameter name=\"narrative\">some text</parameter>'),\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest(\"hasXmlParameterTags returns false for clean JSON\", () => {\n\t\tassert.equal(\n\t\t\thasXmlParameterTags('{\"narrative\": \"some text\"}'),\n\t\t\tfalse,\n\t\t);\n\t});\n\n\ttest(\"strips XML parameter tags from JSON values\", () => {\n\t\tconst malformed = '{\"sliceId\": \"S03\", \"narrative\": <parameter name=\"narrative\">The slice work</parameter>}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\t// After stripping tags, the content should be parseable or at least tag-free\n\t\tassert.ok(!repaired.includes(\"<parameter\"), \"should not contain <parameter tags\");\n\t\tassert.ok(!repaired.includes(\"</parameter>\"), \"should not contain </parameter> tags\");\n\t});\n\n\ttest(\"handles mixed XML and JSON content\", () => {\n\t\tconst malformed = '{\"oneLiner\": \"done\", \"verification\": <parameter name=\"verification\">all tests pass</parameter>}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tassert.ok(!repaired.includes(\"<parameter\"), \"XML tags should be stripped\");\n\t\tassert.ok(repaired.includes(\"all tests pass\"), \"content should be preserved\");\n\t});\n\n\ttest(\"promotes XML parameters trapped inside valid JSON string values\", () => {\n\t\tconst malformed =\n\t\t\t'{\"narrative\":\"text.</narrative>\\\\n<parameter name=\\\\\"verification\\\\\">all tests pass</parameter>\\\\n<parameter name=\\\\\"verificationEvidence\\\\\">[\\\\\"npm test\\\\\"]</parameter>\",\"oneLiner\":\"done\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.narrative, \"text.\");\n\t\tassert.equal(parsed.verification, \"all tests pass\");\n\t\tassert.deepEqual(parsed.verificationEvidence, [\"npm test\"]);\n\t\tassert.equal(parsed.oneLiner, \"done\");\n\t\tassert.ok(!parsed.narrative.includes(\"<parameter\"), \"narrative should not retain leaked XML\");\n\t});\n});\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Truncated number repair (#3464)\n// ═══════════════════════════════════════════════════════════════════════════\n\ndescribe(\"repairToolJson — truncated number repair (#3464)\", () => {\n\ttest(\"hasTruncatedNumbers detects bare comma after colon\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": ,'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers detects bare minus before comma\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": -,'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers detects bare minus before closing brace\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"durationMs\": -}'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers returns false for valid numbers\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": 0, \"durationMs\": 1234'), false);\n\t});\n\n\ttest(\"hasTruncatedNumbers returns false for negative numbers\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": -1, \"offset\": -100'), false);\n\t});\n\n\ttest(\"repairs truncated exitCode with bare comma\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": , \"verdict\": \"pass\", \"durationMs\": 500}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, 0);\n\t\tassert.equal(parsed.durationMs, 500);\n\t});\n\n\ttest(\"repairs truncated exitCode with bare minus\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": -, \"verdict\": \"pass\", \"durationMs\": 1234}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, 0);\n\t\tassert.equal(parsed.verdict, \"pass\");\n\t});\n\n\ttest(\"repairs truncated durationMs at end of object\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": 0, \"verdict\": \"pass\", \"durationMs\": -}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.durationMs, 0);\n\t\tassert.equal(parsed.exitCode, 0);\n\t});\n\n\ttest(\"does not mangle valid negative numbers\", () => {\n\t\tconst valid = '{\"exitCode\": -1, \"offset\": -100}';\n\t\tconst repaired = repairToolJson(valid);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, -1);\n\t\tassert.equal(parsed.offset, -100);\n\t});\n});\n"]}
|
|
@@ -262,11 +262,6 @@ export function convertMessages(
|
|
|
262
262
|
cacheControl?: { type: "ephemeral"; ttl?: "1h" },
|
|
263
263
|
): MessageParam[] {
|
|
264
264
|
const params: MessageParam[] = [];
|
|
265
|
-
// Indices into `params` for messages flagged with `cacheBreakpoint: true` —
|
|
266
|
-
// e.g. compaction summaries. We apply cache_control to the most recent one
|
|
267
|
-
// (in addition to the last message) so the stable summary + kept-history
|
|
268
|
-
// block can earn cache reads on every post-compaction turn. (#5027)
|
|
269
|
-
const breakpointIndices: number[] = [];
|
|
270
265
|
|
|
271
266
|
const transformedMessages = transformMessagesWithReport(messages, model, normalizeToolCallId, "anthropic-messages");
|
|
272
267
|
|
|
@@ -280,7 +275,6 @@ export function convertMessages(
|
|
|
280
275
|
role: "user",
|
|
281
276
|
content: sanitizeSurrogates(msg.content),
|
|
282
277
|
});
|
|
283
|
-
if (msg.cacheBreakpoint) breakpointIndices.push(params.length - 1);
|
|
284
278
|
}
|
|
285
279
|
} else {
|
|
286
280
|
const blocks: ContentBlockParam[] = msg.content.map((item) => {
|
|
@@ -312,7 +306,6 @@ export function convertMessages(
|
|
|
312
306
|
role: "user",
|
|
313
307
|
content: filteredBlocks,
|
|
314
308
|
});
|
|
315
|
-
if (msg.cacheBreakpoint) breakpointIndices.push(params.length - 1);
|
|
316
309
|
}
|
|
317
310
|
} else if (msg.role === "assistant") {
|
|
318
311
|
const blocks: ContentBlockParam[] = [];
|
|
@@ -410,50 +403,31 @@ export function convertMessages(
|
|
|
410
403
|
}
|
|
411
404
|
|
|
412
405
|
if (cacheControl && params.length > 0) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
406
|
+
const lastMessage = params[params.length - 1];
|
|
407
|
+
if (lastMessage.role === "user") {
|
|
408
|
+
if (Array.isArray(lastMessage.content)) {
|
|
409
|
+
const lastBlock = lastMessage.content[lastMessage.content.length - 1];
|
|
410
|
+
if (
|
|
411
|
+
lastBlock &&
|
|
412
|
+
(lastBlock.type === "text" || lastBlock.type === "image" || lastBlock.type === "tool_result")
|
|
413
|
+
) {
|
|
414
|
+
(lastBlock as any).cache_control = cacheControl;
|
|
415
|
+
}
|
|
416
|
+
} else if (typeof lastMessage.content === "string") {
|
|
417
|
+
lastMessage.content = [
|
|
418
|
+
{
|
|
419
|
+
type: "text",
|
|
420
|
+
text: lastMessage.content,
|
|
421
|
+
cache_control: cacheControl,
|
|
422
|
+
},
|
|
423
|
+
] as any;
|
|
424
|
+
}
|
|
424
425
|
}
|
|
425
426
|
}
|
|
426
427
|
|
|
427
428
|
return params;
|
|
428
429
|
}
|
|
429
430
|
|
|
430
|
-
/** Apply `cache_control` to the last cacheable block of the user-role param at `index`. No-op for non-user roles. */
|
|
431
|
-
function applyCacheControlToParam(
|
|
432
|
-
params: MessageParam[],
|
|
433
|
-
index: number,
|
|
434
|
-
cacheControl: { type: "ephemeral"; ttl?: "1h" },
|
|
435
|
-
): void {
|
|
436
|
-
const param = params[index];
|
|
437
|
-
if (!param || param.role !== "user") return;
|
|
438
|
-
if (Array.isArray(param.content)) {
|
|
439
|
-
const lastBlock = param.content[param.content.length - 1];
|
|
440
|
-
if (
|
|
441
|
-
lastBlock &&
|
|
442
|
-
(lastBlock.type === "text" || lastBlock.type === "image" || lastBlock.type === "tool_result")
|
|
443
|
-
) {
|
|
444
|
-
(lastBlock as any).cache_control = cacheControl;
|
|
445
|
-
}
|
|
446
|
-
} else if (typeof param.content === "string") {
|
|
447
|
-
param.content = [
|
|
448
|
-
{
|
|
449
|
-
type: "text",
|
|
450
|
-
text: param.content,
|
|
451
|
-
cache_control: cacheControl,
|
|
452
|
-
},
|
|
453
|
-
] as any;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
431
|
/** Convert GSD tools to Anthropic SDK tool definitions, applying cache control to the last entry. */
|
|
458
432
|
export function convertTools(
|
|
459
433
|
tools: Tool[],
|
|
@@ -501,17 +475,11 @@ export function buildParams(
|
|
|
501
475
|
};
|
|
502
476
|
|
|
503
477
|
if (isOAuthToken) {
|
|
504
|
-
// Only the LAST system block carries `cache_control` — the boundary
|
|
505
|
-
// covers the entire system prefix up to that point. Putting cache_control
|
|
506
|
-
// on the short "You are Claude Code" header AND the user systemPrompt
|
|
507
|
-
// would consume two of Anthropic's 4 breakpoint slots for redundant
|
|
508
|
-
// coverage, leaving no room for a compaction-summary breakpoint. (#5027)
|
|
509
|
-
const hasUserSystemPrompt = Boolean(context.systemPrompt);
|
|
510
478
|
params.system = [
|
|
511
479
|
{
|
|
512
480
|
type: "text",
|
|
513
481
|
text: "You are Claude Code, Anthropic's official CLI for Claude.",
|
|
514
|
-
...(cacheControl
|
|
482
|
+
...(cacheControl ? { cache_control: cacheControl } : {}),
|
|
515
483
|
},
|
|
516
484
|
];
|
|
517
485
|
if (context.systemPrompt) {
|
|
@@ -201,19 +201,6 @@ export interface UserMessage {
|
|
|
201
201
|
role: "user";
|
|
202
202
|
content: string | (TextContent | ImageContent)[];
|
|
203
203
|
timestamp: number; // Unix timestamp in milliseconds
|
|
204
|
-
/**
|
|
205
|
-
* Hint to provider adapters that this message marks a stable point in the
|
|
206
|
-
* conversation suitable as a prompt-cache anchor. Providers that support
|
|
207
|
-
* prompt caching (e.g. Anthropic) may apply a `cache_control` breakpoint
|
|
208
|
-
* here so the prefix up to and including this message can earn cache
|
|
209
|
-
* reads on subsequent turns.
|
|
210
|
-
*
|
|
211
|
-
* Optional and additive — providers that do not support caching, or
|
|
212
|
-
* messages without the hint, behave exactly as before. Set by upstream
|
|
213
|
-
* code that knows the message represents a stable boundary (e.g. a
|
|
214
|
-
* compaction summary). (#5027)
|
|
215
|
-
*/
|
|
216
|
-
cacheBreakpoint?: boolean;
|
|
217
204
|
}
|
|
218
205
|
|
|
219
206
|
export interface AssistantMessage {
|
|
@@ -61,7 +61,6 @@ type XmlParameterBlock = {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
const xmlParameterBlockPattern = /<parameter\s+name="([^"]+)"\s*>([\s\S]*?)<\/parameter>/g;
|
|
64
|
-
const xmlParameterOpenPattern = /<parameter\s+name="([^"]+)"\s*>/g;
|
|
65
64
|
|
|
66
65
|
function parseXmlParameterValue(raw: string): unknown {
|
|
67
66
|
const trimmed = raw.trim();
|
|
@@ -74,31 +73,11 @@ function parseXmlParameterValue(raw: string): unknown {
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
function extractXmlParameterBlocks(text: string): XmlParameterBlock[] {
|
|
77
|
-
const strictBlocks: XmlParameterBlock[] = [];
|
|
78
|
-
let hasNestedParameterOpening = false;
|
|
79
|
-
for (const match of text.matchAll(xmlParameterBlockPattern)) {
|
|
80
|
-
const rawValue = match[2] ?? "";
|
|
81
|
-
hasNestedParameterOpening ||= rawValue.includes("<parameter");
|
|
82
|
-
strictBlocks.push({
|
|
83
|
-
name: match[1],
|
|
84
|
-
value: parseXmlParameterValue(rawValue),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
if (strictBlocks.length > 0 && !hasNestedParameterOpening) return strictBlocks;
|
|
88
|
-
|
|
89
76
|
const blocks: XmlParameterBlock[] = [];
|
|
90
|
-
const
|
|
91
|
-
for (let i = 0; i < openings.length; i++) {
|
|
92
|
-
const current = openings[i];
|
|
93
|
-
const next = openings[i + 1];
|
|
94
|
-
if (current.index === undefined) continue;
|
|
95
|
-
|
|
96
|
-
const start = current.index + current[0].length;
|
|
97
|
-
const end = next?.index ?? text.length;
|
|
98
|
-
const rawValue = text.slice(start, end).replace(/\s*<\/parameter>\s*$/, "");
|
|
77
|
+
for (const match of text.matchAll(xmlParameterBlockPattern)) {
|
|
99
78
|
blocks.push({
|
|
100
|
-
name:
|
|
101
|
-
value: parseXmlParameterValue(
|
|
79
|
+
name: match[1],
|
|
80
|
+
value: parseXmlParameterValue(match[2] ?? ""),
|
|
102
81
|
});
|
|
103
82
|
}
|
|
104
83
|
return blocks;
|
|
@@ -147,38 +147,6 @@ describe("repairToolJson — XML parameter tag stripping (#3403)", () => {
|
|
|
147
147
|
assert.equal(parsed.oneLiner, "done");
|
|
148
148
|
assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
|
|
149
149
|
});
|
|
150
|
-
|
|
151
|
-
test("promotes dangling XML parameters trapped inside valid JSON string values", () => {
|
|
152
|
-
const malformed = JSON.stringify({
|
|
153
|
-
narrative:
|
|
154
|
-
'text.\n<parameter name="verification">all tests pass\n<parameter name="verificationEvidence">["npm test"]',
|
|
155
|
-
oneLiner: "done",
|
|
156
|
-
});
|
|
157
|
-
const repaired = repairToolJson(malformed);
|
|
158
|
-
const parsed = JSON.parse(repaired);
|
|
159
|
-
|
|
160
|
-
assert.equal(parsed.narrative, "text.");
|
|
161
|
-
assert.equal(parsed.verification, "all tests pass");
|
|
162
|
-
assert.deepEqual(parsed.verificationEvidence, ["npm test"]);
|
|
163
|
-
assert.equal(parsed.oneLiner, "done");
|
|
164
|
-
assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("promotes mixed dangling and closed XML parameters from valid JSON string values", () => {
|
|
168
|
-
const malformed = JSON.stringify({
|
|
169
|
-
narrative:
|
|
170
|
-
'text.\n<parameter name="verification">all tests pass\n<parameter name="verificationEvidence">["npm test"]</parameter>',
|
|
171
|
-
oneLiner: "done",
|
|
172
|
-
});
|
|
173
|
-
const repaired = repairToolJson(malformed);
|
|
174
|
-
const parsed = JSON.parse(repaired);
|
|
175
|
-
|
|
176
|
-
assert.equal(parsed.narrative, "text.");
|
|
177
|
-
assert.equal(parsed.verification, "all tests pass");
|
|
178
|
-
assert.deepEqual(parsed.verificationEvidence, ["npm test"]);
|
|
179
|
-
assert.equal(parsed.oneLiner, "done");
|
|
180
|
-
assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
|
|
181
|
-
});
|
|
182
150
|
});
|
|
183
151
|
|
|
184
152
|
// ═══════════════════════════════════════════════════════════════════════════
|