@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.72e3af2a
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/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +11 -2
- package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
- package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +20 -19
- package/dist/resources/extensions/gsd/auto-prompts.js +26 -11
- package/dist/resources/extensions/gsd/auto-recovery.js +6 -507
- package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
- package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +38 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +6 -1
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +10 -19
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +68 -10
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
- package/dist/resources/extensions/gsd/commands-context.js +19 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +16 -10
- package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
- package/dist/resources/extensions/gsd/db/queries.js +60 -0
- package/dist/resources/extensions/gsd/db-workspace.js +55 -3
- package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
- package/dist/resources/extensions/gsd/exec-sandbox.js +45 -9
- package/dist/resources/extensions/gsd/forensics.js +2 -32
- package/dist/resources/extensions/gsd/git-service.js +4 -4
- package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
- package/dist/resources/extensions/gsd/health-widget.js +55 -29
- package/dist/resources/extensions/gsd/layout-policy.js +3 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
- package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
- package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
- package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
- package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/dist/resources/extensions/gsd/quick.js +45 -2
- package/dist/resources/extensions/gsd/session-forensics.js +11 -1
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
- package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
- package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
- package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
- package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
- package/dist/resources/extensions/gsd/state.js +12 -611
- package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
- package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
- package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
- package/dist/resources/extensions/gsd/unit-registry.js +32 -4
- package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
- package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
- package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
- 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_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 +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- 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/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/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +1 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +2 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
- package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/agent-session.js +3 -0
- package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
- package/packages/gsd-agent-core/dist/index.d.ts +2 -0
- package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/index.js +2 -0
- package/packages/gsd-agent-core/dist/index.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
- package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
- package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
- package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/README.md +1 -1
- package/packages/mcp-server/dist/server.d.ts +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +3 -3
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +34 -20
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/README.md +3 -2
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme-schema.d.ts +75 -75
- package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
- package/pkg/dist/theme/theme-schema.js +1 -1
- package/pkg/dist/theme/theme-schema.js.map +1 -1
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +11 -7
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
- package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
- package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
- package/src/resources/extensions/gsd/auto/session.ts +5 -0
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +21 -23
- package/src/resources/extensions/gsd/auto-prompts.ts +38 -12
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -508
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
- package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +43 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +6 -1
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -19
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +74 -10
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
- package/src/resources/extensions/gsd/commands-context.ts +18 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +14 -9
- package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
- package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
- package/src/resources/extensions/gsd/db/queries.ts +79 -0
- package/src/resources/extensions/gsd/db-workspace.ts +61 -3
- package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
- package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
- package/src/resources/extensions/gsd/forensics.ts +2 -33
- package/src/resources/extensions/gsd/git-service.ts +5 -5
- package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
- package/src/resources/extensions/gsd/health-widget.ts +69 -32
- package/src/resources/extensions/gsd/layout-policy.ts +2 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
- package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
- package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
- package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
- package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/src/resources/extensions/gsd/quick.ts +43 -2
- package/src/resources/extensions/gsd/session-forensics.ts +11 -1
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
- package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
- package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
- package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
- package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
- package/src/resources/extensions/gsd/state.ts +32 -732
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -1
- package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
- package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +119 -1
- package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
- package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
- package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
- package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
- package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
- package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
- package/src/resources/extensions/gsd/unit-registry.ts +32 -4
- package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
- package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
- package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
- package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
- package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
- package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
- /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → O7xDYXO0r4zFhIzY1hrWV}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → O7xDYXO0r4zFhIzY1hrWV}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
const PROVISIONAL_PRE_TOOL_ACTIONS = "check|inspect|look(?:\\s+into)?|read|open|search|grep|scan|run|verify|test|trace|review|investigate|reproduce|use|gather|find|pull|query|take a look|update|patch|edit|change|modify|write|create|add|remove|apply";
|
|
2
|
+
const FIRST_PERSON_PROVISIONAL_PRE_TOOL_RE = new RegExp(`^(?:i(?:'ll| will)|i(?:'m| am) going to|let me|i need to)\\s+(?:${PROVISIONAL_PRE_TOOL_ACTIONS})\\b`, "i");
|
|
3
|
+
const GERUND_PROVISIONAL_PRE_TOOL_RE = /^(?:checking|inspecting|reading|searching|running|verifying|testing|tracing|reviewing|investigating|scanning|updating|patching|editing|writing|creating|applying)\b/i;
|
|
4
|
+
export function getVisibleTextLikeBlockType(block, hideThinkingBlock = false) {
|
|
5
|
+
if (block?.type === "text" && typeof block.text === "string" && block.text.trim().length > 0)
|
|
6
|
+
return "text";
|
|
7
|
+
if (hideThinkingBlock)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (block?.type === "thinking" && typeof block.thinking === "string" && block.thinking.trim().length > 0)
|
|
10
|
+
return "thinking";
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
/** True when assistant prose is handing off to the user (question or explicit invite). */
|
|
14
|
+
export function textInvitesUserReply(text) {
|
|
15
|
+
const trimmed = text.trim();
|
|
16
|
+
if (!trimmed)
|
|
17
|
+
return false;
|
|
18
|
+
if (/\?(?:\s|$)/m.test(trimmed))
|
|
19
|
+
return true;
|
|
20
|
+
return /\b(?:what do you want|what's on your mind|let me know|tell me what|help me understand)\b/i.test(trimmed);
|
|
21
|
+
}
|
|
22
|
+
const DISCUSS_RESTATE_RE = /\b(?:what do you want|what should we|before i can write|context file|placeholder name|need to understand what|what(?:'s| is) (?:on your mind|the next)|help me understand what you want)\b/i;
|
|
23
|
+
/** Second sub-turn that only says it is waiting after questions were already asked. */
|
|
24
|
+
const HANDOFF_WAIT_RESTATE_RE = /\b(?:holding\s+(?:here|for)|waiting\s+(?:here|for)|no\s+need\s+for\s+anything\s+else|until\s+you\s+(?:point|tell|let\s+me\s+know|answer|reply)|i(?:'ve| have)\s+asked)\b/i;
|
|
25
|
+
function isWaitOnlyQuestionFragment(fragment) {
|
|
26
|
+
return /^(?:i(?:'ve| have)\s+asked\b|(?:i'?m|i am)\s+(?:holding|waiting)\b|no\s+need\s+for\s+anything\s+else\b)/i.test(fragment)
|
|
27
|
+
&& !/\b(?:should|do you|would you|can we|could we|what|which|how|where|when|who|also|add|include)\b/i.test(fragment);
|
|
28
|
+
}
|
|
29
|
+
/** True when text adds a question beyond wait/hold boilerplate. */
|
|
30
|
+
function containsNewSubstantiveQuestion(text) {
|
|
31
|
+
for (let i = 0; i < text.length; i++) {
|
|
32
|
+
if (text[i] !== "?")
|
|
33
|
+
continue;
|
|
34
|
+
const previousBreak = Math.max(text.lastIndexOf("\n", i), text.lastIndexOf(".", i), text.lastIndexOf("!", i), text.lastIndexOf("?", i - 1), -1);
|
|
35
|
+
const fragment = text.slice(previousBreak + 1, i + 1).trim();
|
|
36
|
+
if (fragment.length < 8)
|
|
37
|
+
continue;
|
|
38
|
+
if (isWaitOnlyQuestionFragment(fragment))
|
|
39
|
+
continue;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
function isHandoffWaitRestatement(next) {
|
|
45
|
+
if (!HANDOFF_WAIT_RESTATE_RE.test(next))
|
|
46
|
+
return false;
|
|
47
|
+
// Keep follow-ups that add a real question even when they also say holding/waiting.
|
|
48
|
+
if (containsNewSubstantiveQuestion(next))
|
|
49
|
+
return false;
|
|
50
|
+
// Only classify as a pure wait ack when the text is short; long text likely
|
|
51
|
+
// contains substantive content alongside incidental wait language.
|
|
52
|
+
if (next.length > 400)
|
|
53
|
+
return false;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Claude Code can emit a second text sub-turn that restates the same milestone
|
|
58
|
+
* discuss ask. Drop it when the prior sub-turn already invited a user reply.
|
|
59
|
+
*/
|
|
60
|
+
export function isRedundantDiscussRestatement(priorText, newText) {
|
|
61
|
+
const prior = priorText.trim();
|
|
62
|
+
const next = newText.trim();
|
|
63
|
+
if (!prior || !next)
|
|
64
|
+
return false;
|
|
65
|
+
if (!textInvitesUserReply(prior))
|
|
66
|
+
return false;
|
|
67
|
+
const isDiscussRestate = DISCUSS_RESTATE_RE.test(next);
|
|
68
|
+
const isWaitRestate = isHandoffWaitRestatement(next);
|
|
69
|
+
if (!isDiscussRestate && !isWaitRestate)
|
|
70
|
+
return false;
|
|
71
|
+
// Wait acks are gated on length and no-? inside isHandoffWaitRestatement.
|
|
72
|
+
if (isWaitRestate)
|
|
73
|
+
return true;
|
|
74
|
+
if (next.length > prior.length * 1.1)
|
|
75
|
+
return false;
|
|
76
|
+
return next.length <= prior.length || next.length < 900;
|
|
77
|
+
}
|
|
78
|
+
export function isSubTurnTextReplacement(blocks, rendered) {
|
|
79
|
+
for (const seg of rendered) {
|
|
80
|
+
if (seg.kind !== "text-run")
|
|
81
|
+
continue;
|
|
82
|
+
const oldText = (seg.cachedText ?? "").trim();
|
|
83
|
+
if (!oldText)
|
|
84
|
+
continue;
|
|
85
|
+
const newText = getTextFromContentBlocks(blocks, seg.startIndex, seg.endIndex, seg.contentType).trim();
|
|
86
|
+
if (!newText || newText === oldText)
|
|
87
|
+
continue;
|
|
88
|
+
// Streaming growth extends prior text; a new sub-turn replaces it wholesale.
|
|
89
|
+
if (!newText.startsWith(oldText) && !oldText.startsWith(newText))
|
|
90
|
+
return seg.startIndex;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
export function getTextFromContentBlocks(blocks, startIndex, endIndex, contentType = "text") {
|
|
95
|
+
const parts = [];
|
|
96
|
+
for (let i = startIndex; i <= endIndex && i < blocks.length; i++) {
|
|
97
|
+
const block = blocks[i];
|
|
98
|
+
if (contentType === "text" && block?.type === "text" && typeof block.text === "string" && block.text.trim()) {
|
|
99
|
+
parts.push(block.text.trim());
|
|
100
|
+
}
|
|
101
|
+
else if (contentType === "thinking"
|
|
102
|
+
&& block?.type === "thinking"
|
|
103
|
+
&& typeof block.thinking === "string"
|
|
104
|
+
&& block.thinking.trim()) {
|
|
105
|
+
parts.push(block.thinking.trim());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return parts.join("\n\n");
|
|
109
|
+
}
|
|
110
|
+
export function filterRedundantDiscussTextRuns(desired, blocks) {
|
|
111
|
+
const textRuns = desired.filter((seg) => seg.kind === "text-run" && seg.contentType === "text");
|
|
112
|
+
if (textRuns.length < 2)
|
|
113
|
+
return desired;
|
|
114
|
+
const skipStarts = new Set();
|
|
115
|
+
let lastKeptText;
|
|
116
|
+
for (const seg of textRuns) {
|
|
117
|
+
const text = getTextFromContentBlocks(blocks, seg.startIndex, seg.endIndex);
|
|
118
|
+
if (lastKeptText && isRedundantDiscussRestatement(lastKeptText, text)) {
|
|
119
|
+
skipStarts.add(seg.startIndex);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
lastKeptText = text;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return desired.filter((seg) => !(seg.kind === "text-run" && seg.contentType === "text" && skipStarts.has(seg.startIndex)));
|
|
126
|
+
}
|
|
127
|
+
function extractAssistantText(msg) {
|
|
128
|
+
if (!msg)
|
|
129
|
+
return "";
|
|
130
|
+
const content = msg.content;
|
|
131
|
+
if (typeof content === "string")
|
|
132
|
+
return content;
|
|
133
|
+
if (!Array.isArray(content))
|
|
134
|
+
return "";
|
|
135
|
+
const parts = [];
|
|
136
|
+
for (const block of content) {
|
|
137
|
+
if (!block || typeof block !== "object")
|
|
138
|
+
continue;
|
|
139
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
140
|
+
parts.push(block.text);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return parts.join("\n");
|
|
144
|
+
}
|
|
145
|
+
function latestPriorUserFacingText(orphaned, rendered) {
|
|
146
|
+
const runs = [...orphaned, ...rendered].filter((seg) => seg.kind === "text-run" && seg.contentType === "text");
|
|
147
|
+
return runs.at(-1)?.cachedText;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Walk session history backward for the previous assistant prose block, skipping
|
|
151
|
+
* toolResult rows. Used when Claude Code emits a second assistant message
|
|
152
|
+
* (new timestamp) after tools in the same prompt.
|
|
153
|
+
*/
|
|
154
|
+
export function priorAssistantTextFromSession(messages, opts) {
|
|
155
|
+
let assistantFromEnd = 0;
|
|
156
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
157
|
+
const message = messages[i];
|
|
158
|
+
if (!message)
|
|
159
|
+
continue;
|
|
160
|
+
if (message.role === "user")
|
|
161
|
+
return undefined;
|
|
162
|
+
if (message.role === "toolResult")
|
|
163
|
+
continue;
|
|
164
|
+
if (message.role === "assistant") {
|
|
165
|
+
const text = extractAssistantText(message).trim();
|
|
166
|
+
if (!text)
|
|
167
|
+
continue;
|
|
168
|
+
if (opts?.skipLastAssistant) {
|
|
169
|
+
assistantFromEnd += 1;
|
|
170
|
+
if (assistantFromEnd === 1)
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
return text;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
export function shouldSuppressEntireAssistantMessage(message, sessionMessages, orphaned) {
|
|
179
|
+
const textBlocks = (message.content ?? []).filter((block) => block?.type === "text" && typeof block.text === "string" && block.text.trim());
|
|
180
|
+
if (textBlocks.length !== 1)
|
|
181
|
+
return false;
|
|
182
|
+
return shouldSuppressRedundantHandoffText(sessionMessages, textBlocks[0].text, orphaned, []);
|
|
183
|
+
}
|
|
184
|
+
export function shouldSuppressRedundantHandoffText(sessionMessages, currentText, orphaned, rendered) {
|
|
185
|
+
const next = currentText.trim();
|
|
186
|
+
if (!next)
|
|
187
|
+
return false;
|
|
188
|
+
const priorInline = latestPriorUserFacingText(orphaned, rendered);
|
|
189
|
+
if (priorInline && isRedundantDiscussRestatement(priorInline, next)) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
const last = sessionMessages[sessionMessages.length - 1];
|
|
193
|
+
const skipLastAssistant = last?.role === "assistant" && extractAssistantText(last).trim() === next;
|
|
194
|
+
const priorSession = priorAssistantTextFromSession(sessionMessages, { skipLastAssistant });
|
|
195
|
+
return !!(priorSession && isRedundantDiscussRestatement(priorSession, next));
|
|
196
|
+
}
|
|
197
|
+
export function buildDesiredSegments(blocks, options = {}) {
|
|
198
|
+
const desired = [];
|
|
199
|
+
let runStart = -1;
|
|
200
|
+
let runEnd = -1;
|
|
201
|
+
let runType;
|
|
202
|
+
const closeRun = () => {
|
|
203
|
+
if (runStart !== -1 && runType) {
|
|
204
|
+
desired.push({ kind: "text-run", startIndex: runStart, endIndex: runEnd, contentType: runType });
|
|
205
|
+
runStart = -1;
|
|
206
|
+
runEnd = -1;
|
|
207
|
+
runType = undefined;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
211
|
+
const block = blocks[i];
|
|
212
|
+
const blockType = getVisibleTextLikeBlockType(block, options.hideThinkingBlock);
|
|
213
|
+
const isInvisibleTextLike = blockType === undefined && (block?.type === "text" || block?.type === "thinking");
|
|
214
|
+
const isTool = block?.type === "toolCall" || block?.type === "serverToolUse";
|
|
215
|
+
if (blockType) {
|
|
216
|
+
if (options.shouldSkipTextBlock?.(block, i)) {
|
|
217
|
+
closeRun();
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (runStart === -1) {
|
|
221
|
+
runStart = i;
|
|
222
|
+
runEnd = i;
|
|
223
|
+
runType = blockType;
|
|
224
|
+
}
|
|
225
|
+
else if (runType !== blockType) {
|
|
226
|
+
closeRun();
|
|
227
|
+
runStart = i;
|
|
228
|
+
runEnd = i;
|
|
229
|
+
runType = blockType;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
runEnd = i;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
if (isInvisibleTextLike)
|
|
237
|
+
continue;
|
|
238
|
+
closeRun();
|
|
239
|
+
if (isTool) {
|
|
240
|
+
desired.push({ kind: "tool", contentIndex: i, toolId: block.id });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
closeRun();
|
|
245
|
+
return desired;
|
|
246
|
+
}
|
|
247
|
+
function isToolUseBlock(block) {
|
|
248
|
+
return block?.type === "toolCall" || block?.type === "serverToolUse";
|
|
249
|
+
}
|
|
250
|
+
function isMcpToolBlock(block) {
|
|
251
|
+
if (!isToolUseBlock(block))
|
|
252
|
+
return false;
|
|
253
|
+
const toolName = typeof block?.name === "string" ? block.name : "";
|
|
254
|
+
return typeof block?.mcpServer === "string" || toolName.startsWith("mcp__");
|
|
255
|
+
}
|
|
256
|
+
function hasPostToolText(blocks, firstToolIdx) {
|
|
257
|
+
if (firstToolIdx < 0)
|
|
258
|
+
return false;
|
|
259
|
+
return blocks.some((b, idx) => (idx > firstToolIdx
|
|
260
|
+
&& b?.type === "text"
|
|
261
|
+
&& typeof b?.text === "string"
|
|
262
|
+
&& b.text.trim().length > 0));
|
|
263
|
+
}
|
|
264
|
+
function normalizeProvisionalText(text) {
|
|
265
|
+
return text
|
|
266
|
+
.trim()
|
|
267
|
+
.replace(/[’`]/g, "'")
|
|
268
|
+
.replace(/\s+/g, " ");
|
|
269
|
+
}
|
|
270
|
+
export function isProvisionalPreToolProse(text) {
|
|
271
|
+
const normalized = normalizeProvisionalText(text);
|
|
272
|
+
if (!normalized)
|
|
273
|
+
return false;
|
|
274
|
+
if (textInvitesUserReply(normalized))
|
|
275
|
+
return false;
|
|
276
|
+
if (/\?\s*$/.test(normalized))
|
|
277
|
+
return false;
|
|
278
|
+
return FIRST_PERSON_PROVISIONAL_PRE_TOOL_RE.test(normalized)
|
|
279
|
+
|| GERUND_PROVISIONAL_PRE_TOOL_RE.test(normalized);
|
|
280
|
+
}
|
|
281
|
+
export function getProvisionalPreToolPrunePlan(message) {
|
|
282
|
+
const blocks = message.content;
|
|
283
|
+
const firstToolIdx = blocks.findIndex(isToolUseBlock);
|
|
284
|
+
return {
|
|
285
|
+
firstToolIdx,
|
|
286
|
+
shouldPrune: message.provider === "claude-code"
|
|
287
|
+
&& firstToolIdx >= 0
|
|
288
|
+
&& blocks.some(isMcpToolBlock)
|
|
289
|
+
&& hasPostToolText(blocks, firstToolIdx),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
export function buildDesiredSegmentsForMessage(message, options = {}) {
|
|
293
|
+
const { shouldPrune, firstToolIdx } = getProvisionalPreToolPrunePlan(message);
|
|
294
|
+
return buildDesiredSegments(message.content, {
|
|
295
|
+
hideThinkingBlock: options.hideThinkingBlock,
|
|
296
|
+
shouldSkipTextBlock: (block, index) => {
|
|
297
|
+
if (!shouldPrune || firstToolIdx < 0 || index >= firstToolIdx)
|
|
298
|
+
return false;
|
|
299
|
+
if (getVisibleTextLikeBlockType(block, options.hideThinkingBlock) !== "text")
|
|
300
|
+
return false;
|
|
301
|
+
const textValue = typeof block?.text === "string" ? block.text : "";
|
|
302
|
+
return isProvisionalPreToolProse(textValue);
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
export function hasVisibleAssistantContent(message, hideThinkingBlock = false) {
|
|
307
|
+
return message.content.some((c) => getVisibleTextLikeBlockType(c, hideThinkingBlock) !== undefined);
|
|
308
|
+
}
|
|
309
|
+
export function hasAssistantToolBlocks(message) {
|
|
310
|
+
return message.content.some((c) => c.type === "toolCall" || c.type === "serverToolUse");
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=chat-handoff-filter.js.map
|
package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-handoff-filter.js","sourceRoot":"","sources":["../../../../src/modes/interactive/controllers/chat-handoff-filter.ts"],"names":[],"mappings":"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","sourcesContent":["// Project/App: gsd-pi\n// File Purpose: Assistant handoff / redundant prose filtering for interactive chat.\nimport type { DesiredSegment, RenderedSegment } from \"../streaming-render-state.js\";\n\nconst PROVISIONAL_PRE_TOOL_ACTIONS =\n\t\"check|inspect|look(?:\\\\s+into)?|read|open|search|grep|scan|run|verify|test|trace|review|investigate|reproduce|use|gather|find|pull|query|take a look|update|patch|edit|change|modify|write|create|add|remove|apply\";\nconst FIRST_PERSON_PROVISIONAL_PRE_TOOL_RE = new RegExp(\n\t`^(?:i(?:'ll| will)|i(?:'m| am) going to|let me|i need to)\\\\s+(?:${PROVISIONAL_PRE_TOOL_ACTIONS})\\\\b`,\n\t\"i\",\n);\nconst GERUND_PROVISIONAL_PRE_TOOL_RE =\n\t/^(?:checking|inspecting|reading|searching|running|verifying|testing|tracing|reviewing|investigating|scanning|updating|patching|editing|writing|creating|applying)\\b/i;\n\nexport function getVisibleTextLikeBlockType(block: any, hideThinkingBlock = false): \"text\" | \"thinking\" | undefined {\n\tif (block?.type === \"text\" && typeof block.text === \"string\" && block.text.trim().length > 0) return \"text\";\n\tif (hideThinkingBlock) return undefined;\n\tif (block?.type === \"thinking\" && typeof block.thinking === \"string\" && block.thinking.trim().length > 0) return \"thinking\";\n\treturn undefined;\n}\n\n/** True when assistant prose is handing off to the user (question or explicit invite). */\nexport function textInvitesUserReply(text: string): boolean {\n\tconst trimmed = text.trim();\n\tif (!trimmed) return false;\n\tif (/\\?(?:\\s|$)/m.test(trimmed)) return true;\n\treturn /\\b(?:what do you want|what's on your mind|let me know|tell me what|help me understand)\\b/i.test(trimmed);\n}\n\nconst DISCUSS_RESTATE_RE =\n\t/\\b(?:what do you want|what should we|before i can write|context file|placeholder name|need to understand what|what(?:'s| is) (?:on your mind|the next)|help me understand what you want)\\b/i;\n\n/** Second sub-turn that only says it is waiting after questions were already asked. */\nconst HANDOFF_WAIT_RESTATE_RE =\n\t/\\b(?:holding\\s+(?:here|for)|waiting\\s+(?:here|for)|no\\s+need\\s+for\\s+anything\\s+else|until\\s+you\\s+(?:point|tell|let\\s+me\\s+know|answer|reply)|i(?:'ve| have)\\s+asked)\\b/i;\n\nfunction isWaitOnlyQuestionFragment(fragment: string): boolean {\n\treturn /^(?:i(?:'ve| have)\\s+asked\\b|(?:i'?m|i am)\\s+(?:holding|waiting)\\b|no\\s+need\\s+for\\s+anything\\s+else\\b)/i.test(fragment)\n\t\t&& !/\\b(?:should|do you|would you|can we|could we|what|which|how|where|when|who|also|add|include)\\b/i.test(fragment);\n}\n\n/** True when text adds a question beyond wait/hold boilerplate. */\nfunction containsNewSubstantiveQuestion(text: string): boolean {\n\tfor (let i = 0; i < text.length; i++) {\n\t\tif (text[i] !== \"?\") continue;\n\t\tconst previousBreak = Math.max(\n\t\t\ttext.lastIndexOf(\"\\n\", i),\n\t\t\ttext.lastIndexOf(\".\", i),\n\t\t\ttext.lastIndexOf(\"!\", i),\n\t\t\ttext.lastIndexOf(\"?\", i - 1),\n\t\t\t-1,\n\t\t);\n\t\tconst fragment = text.slice(previousBreak + 1, i + 1).trim();\n\t\tif (fragment.length < 8) continue;\n\t\tif (isWaitOnlyQuestionFragment(fragment)) continue;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nfunction isHandoffWaitRestatement(next: string): boolean {\n\tif (!HANDOFF_WAIT_RESTATE_RE.test(next)) return false;\n\t// Keep follow-ups that add a real question even when they also say holding/waiting.\n\tif (containsNewSubstantiveQuestion(next)) return false;\n\t// Only classify as a pure wait ack when the text is short; long text likely\n\t// contains substantive content alongside incidental wait language.\n\tif (next.length > 400) return false;\n\treturn true;\n}\n\n/**\n * Claude Code can emit a second text sub-turn that restates the same milestone\n * discuss ask. Drop it when the prior sub-turn already invited a user reply.\n */\nexport function isRedundantDiscussRestatement(priorText: string, newText: string): boolean {\n\tconst prior = priorText.trim();\n\tconst next = newText.trim();\n\tif (!prior || !next) return false;\n\tif (!textInvitesUserReply(prior)) return false;\n\tconst isDiscussRestate = DISCUSS_RESTATE_RE.test(next);\n\tconst isWaitRestate = isHandoffWaitRestatement(next);\n\tif (!isDiscussRestate && !isWaitRestate) return false;\n\t// Wait acks are gated on length and no-? inside isHandoffWaitRestatement.\n\tif (isWaitRestate) return true;\n\tif (next.length > prior.length * 1.1) return false;\n\treturn next.length <= prior.length || next.length < 900;\n}\n\nexport function isSubTurnTextReplacement(\n\tblocks: Array<any>,\n\trendered: RenderedSegment[],\n): number | null {\n\tfor (const seg of rendered) {\n\t\tif (seg.kind !== \"text-run\") continue;\n\t\tconst oldText = (seg.cachedText ?? \"\").trim();\n\t\tif (!oldText) continue;\n\t\tconst newText = getTextFromContentBlocks(blocks, seg.startIndex, seg.endIndex, seg.contentType).trim();\n\t\tif (!newText || newText === oldText) continue;\n\t\t// Streaming growth extends prior text; a new sub-turn replaces it wholesale.\n\t\tif (!newText.startsWith(oldText) && !oldText.startsWith(newText)) return seg.startIndex;\n\t}\n\treturn null;\n}\n\nexport function getTextFromContentBlocks(\n\tblocks: Array<any>,\n\tstartIndex: number,\n\tendIndex: number,\n\tcontentType: \"text\" | \"thinking\" = \"text\",\n): string {\n\tconst parts: string[] = [];\n\tfor (let i = startIndex; i <= endIndex && i < blocks.length; i++) {\n\t\tconst block = blocks[i];\n\t\tif (contentType === \"text\" && block?.type === \"text\" && typeof block.text === \"string\" && block.text.trim()) {\n\t\t\tparts.push(block.text.trim());\n\t\t} else if (\n\t\t\tcontentType === \"thinking\"\n\t\t\t&& block?.type === \"thinking\"\n\t\t\t&& typeof block.thinking === \"string\"\n\t\t\t&& block.thinking.trim()\n\t\t) {\n\t\t\tparts.push(block.thinking.trim());\n\t\t}\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n\nexport function filterRedundantDiscussTextRuns(\n\tdesired: DesiredSegment[],\n\tblocks: Array<any>,\n): DesiredSegment[] {\n\tconst textRuns = desired.filter(\n\t\t(seg): seg is Extract<DesiredSegment, { kind: \"text-run\" }> =>\n\t\t\tseg.kind === \"text-run\" && seg.contentType === \"text\",\n\t);\n\tif (textRuns.length < 2) return desired;\n\n\tconst skipStarts = new Set<number>();\n\tlet lastKeptText: string | undefined;\n\tfor (const seg of textRuns) {\n\t\tconst text = getTextFromContentBlocks(blocks, seg.startIndex, seg.endIndex);\n\t\tif (lastKeptText && isRedundantDiscussRestatement(lastKeptText, text)) {\n\t\t\tskipStarts.add(seg.startIndex);\n\t\t} else {\n\t\t\tlastKeptText = text;\n\t\t}\n\t}\n\n\treturn desired.filter(\n\t\t(seg) => !(seg.kind === \"text-run\" && seg.contentType === \"text\" && skipStarts.has(seg.startIndex)),\n\t);\n}\n\nfunction extractAssistantText(msg: { content?: unknown }): string {\n\tif (!msg) return \"\";\n\tconst content = msg.content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tif ((block as { type?: string }).type === \"text\" && typeof (block as { text?: string }).text === \"string\") {\n\t\t\tparts.push((block as { text: string }).text);\n\t\t}\n\t}\n\treturn parts.join(\"\\n\");\n}\n\nfunction latestPriorUserFacingText(\n\torphaned: RenderedSegment[],\n\trendered: RenderedSegment[],\n): string | undefined {\n\tconst runs = [...orphaned, ...rendered].filter(\n\t\t(seg): seg is Extract<RenderedSegment, { kind: \"text-run\" }> =>\n\t\t\tseg.kind === \"text-run\" && seg.contentType === \"text\",\n\t);\n\treturn runs.at(-1)?.cachedText;\n}\n\n/**\n * Walk session history backward for the previous assistant prose block, skipping\n * toolResult rows. Used when Claude Code emits a second assistant message\n * (new timestamp) after tools in the same prompt.\n */\nexport function priorAssistantTextFromSession(\n\tmessages: Array<{ role?: string; content?: unknown }>,\n\topts?: { skipLastAssistant?: boolean },\n): string | undefined {\n\tlet assistantFromEnd = 0;\n\tfor (let i = messages.length - 1; i >= 0; i--) {\n\t\tconst message = messages[i];\n\t\tif (!message) continue;\n\t\tif (message.role === \"user\") return undefined;\n\t\tif (message.role === \"toolResult\") continue;\n\t\tif (message.role === \"assistant\") {\n\t\t\tconst text = extractAssistantText(message).trim();\n\t\t\tif (!text) continue;\n\t\t\tif (opts?.skipLastAssistant) {\n\t\t\t\tassistantFromEnd += 1;\n\t\t\t\tif (assistantFromEnd === 1) continue;\n\t\t\t}\n\t\t\treturn text;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport function shouldSuppressEntireAssistantMessage(\n\tmessage: { content?: Array<any> },\n\tsessionMessages: Array<{ role?: string; content?: unknown }>,\n\torphaned: RenderedSegment[],\n): boolean {\n\tconst textBlocks = (message.content ?? []).filter(\n\t\t(block) => block?.type === \"text\" && typeof block.text === \"string\" && block.text.trim(),\n\t);\n\tif (textBlocks.length !== 1) return false;\n\treturn shouldSuppressRedundantHandoffText(\n\t\tsessionMessages,\n\t\ttextBlocks[0].text,\n\t\torphaned,\n\t\t[],\n\t);\n}\n\nexport function shouldSuppressRedundantHandoffText(\n\tsessionMessages: Array<{ role?: string; content?: unknown }>,\n\tcurrentText: string,\n\torphaned: RenderedSegment[],\n\trendered: RenderedSegment[],\n): boolean {\n\tconst next = currentText.trim();\n\tif (!next) return false;\n\n\tconst priorInline = latestPriorUserFacingText(orphaned, rendered);\n\tif (priorInline && isRedundantDiscussRestatement(priorInline, next)) {\n\t\treturn true;\n\t}\n\n\tconst last = sessionMessages[sessionMessages.length - 1];\n\tconst skipLastAssistant =\n\t\tlast?.role === \"assistant\" && extractAssistantText(last).trim() === next;\n\tconst priorSession = priorAssistantTextFromSession(sessionMessages, { skipLastAssistant });\n\treturn !!(priorSession && isRedundantDiscussRestatement(priorSession, next));\n}\n\nexport function buildDesiredSegments(\n\tblocks: Array<any>,\n\toptions: { hideThinkingBlock?: boolean; shouldSkipTextBlock?: (block: any, index: number) => boolean } = {},\n): DesiredSegment[] {\n\tconst desired: DesiredSegment[] = [];\n\tlet runStart = -1;\n\tlet runEnd = -1;\n\tlet runType: \"text\" | \"thinking\" | undefined;\n\tconst closeRun = () => {\n\t\tif (runStart !== -1 && runType) {\n\t\t\tdesired.push({ kind: \"text-run\", startIndex: runStart, endIndex: runEnd, contentType: runType });\n\t\t\trunStart = -1;\n\t\t\trunEnd = -1;\n\t\t\trunType = undefined;\n\t\t}\n\t};\n\n\tfor (let i = 0; i < blocks.length; i++) {\n\t\tconst block = blocks[i];\n\t\tconst blockType = getVisibleTextLikeBlockType(block, options.hideThinkingBlock);\n\t\tconst isInvisibleTextLike = blockType === undefined && (block?.type === \"text\" || block?.type === \"thinking\");\n\t\tconst isTool = block?.type === \"toolCall\" || block?.type === \"serverToolUse\";\n\n\t\tif (blockType) {\n\t\t\tif (options.shouldSkipTextBlock?.(block, i)) {\n\t\t\t\tcloseRun();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (runStart === -1) {\n\t\t\t\trunStart = i;\n\t\t\t\trunEnd = i;\n\t\t\t\trunType = blockType;\n\t\t\t} else if (runType !== blockType) {\n\t\t\t\tcloseRun();\n\t\t\t\trunStart = i;\n\t\t\t\trunEnd = i;\n\t\t\t\trunType = blockType;\n\t\t\t} else {\n\t\t\t\trunEnd = i;\n\t\t\t}\n\t\t} else {\n\t\t\tif (isInvisibleTextLike) continue;\n\t\t\tcloseRun();\n\t\t\tif (isTool) {\n\t\t\t\tdesired.push({ kind: \"tool\", contentIndex: i, toolId: block.id });\n\t\t\t}\n\t\t}\n\t}\n\tcloseRun();\n\n\treturn desired;\n}\n\nfunction isToolUseBlock(block: any): boolean {\n\treturn block?.type === \"toolCall\" || block?.type === \"serverToolUse\";\n}\n\nfunction isMcpToolBlock(block: any): boolean {\n\tif (!isToolUseBlock(block)) return false;\n\tconst toolName = typeof block?.name === \"string\" ? block.name : \"\";\n\treturn typeof block?.mcpServer === \"string\" || toolName.startsWith(\"mcp__\");\n}\n\nfunction hasPostToolText(blocks: Array<any>, firstToolIdx: number): boolean {\n\tif (firstToolIdx < 0) return false;\n\treturn blocks.some(\n\t\t(b: any, idx: number) => (\n\t\t\tidx > firstToolIdx\n\t\t\t&& b?.type === \"text\"\n\t\t\t&& typeof b?.text === \"string\"\n\t\t\t&& b.text.trim().length > 0\n\t\t),\n\t);\n}\n\nfunction normalizeProvisionalText(text: string): string {\n\treturn text\n\t\t.trim()\n\t\t.replace(/[’`]/g, \"'\")\n\t\t.replace(/\\s+/g, \" \");\n}\n\nexport function isProvisionalPreToolProse(text: string): boolean {\n\tconst normalized = normalizeProvisionalText(text);\n\tif (!normalized) return false;\n\tif (textInvitesUserReply(normalized)) return false;\n\tif (/\\?\\s*$/.test(normalized)) return false;\n\n\treturn FIRST_PERSON_PROVISIONAL_PRE_TOOL_RE.test(normalized)\n\t\t|| GERUND_PROVISIONAL_PRE_TOOL_RE.test(normalized);\n}\n\nexport function getProvisionalPreToolPrunePlan(message: { provider?: string; content: Array<any> }): {\n\tshouldPrune: boolean;\n\tfirstToolIdx: number;\n} {\n\tconst blocks = message.content;\n\tconst firstToolIdx = blocks.findIndex(isToolUseBlock);\n\treturn {\n\t\tfirstToolIdx,\n\t\tshouldPrune:\n\t\t\tmessage.provider === \"claude-code\"\n\t\t\t&& firstToolIdx >= 0\n\t\t\t&& blocks.some(isMcpToolBlock)\n\t\t\t&& hasPostToolText(blocks, firstToolIdx),\n\t};\n}\n\nexport function buildDesiredSegmentsForMessage(\n\tmessage: { provider?: string; content: Array<any> },\n\toptions: { hideThinkingBlock?: boolean } = {},\n): DesiredSegment[] {\n\tconst { shouldPrune, firstToolIdx } = getProvisionalPreToolPrunePlan(message);\n\treturn buildDesiredSegments(message.content, {\n\t\thideThinkingBlock: options.hideThinkingBlock,\n\t\tshouldSkipTextBlock: (block: any, index: number) => {\n\t\t\tif (!shouldPrune || firstToolIdx < 0 || index >= firstToolIdx) return false;\n\t\t\tif (getVisibleTextLikeBlockType(block, options.hideThinkingBlock) !== \"text\") return false;\n\t\t\tconst textValue = typeof block?.text === \"string\" ? block.text : \"\";\n\t\t\treturn isProvisionalPreToolProse(textValue);\n\t\t},\n\t});\n}\n\nexport function hasVisibleAssistantContent(message: { content: Array<any> }, hideThinkingBlock = false): boolean {\n\treturn message.content.some((c) => getVisibleTextLikeBlockType(c, hideThinkingBlock) !== undefined);\n}\n\nexport function hasAssistantToolBlocks(message: { content: Array<any> }): boolean {\n\treturn message.content.some((c) => c.type === \"toolCall\" || c.type === \"serverToolUse\");\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { InteractiveModeStateHost } from "../interactive-mode-state.js";
|
|
2
|
+
import type { StreamingRenderState } from "../streaming-render-state.js";
|
|
3
|
+
export declare function findLatestPinnableCandidates(contentBlocks: Array<any>): Array<{
|
|
4
|
+
text: string;
|
|
5
|
+
contentIndex: number;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function findLatestPinnableText(contentBlocks: Array<any>): string;
|
|
8
|
+
export declare function rowsRenderedAfterContentIndex(contentIndex: number, width: number, streamState: StreamingRenderState): number;
|
|
9
|
+
export declare function tearDownPinnedZone(host: {
|
|
10
|
+
pinnedMessageContainer: {
|
|
11
|
+
clear(): void;
|
|
12
|
+
};
|
|
13
|
+
ui: {
|
|
14
|
+
requestRender(force?: boolean): void;
|
|
15
|
+
};
|
|
16
|
+
streamingRenderState: StreamingRenderState;
|
|
17
|
+
}, options?: {
|
|
18
|
+
realignViewport?: boolean;
|
|
19
|
+
}): void;
|
|
20
|
+
export declare function updatePinnedMessageZone(host: InteractiveModeStateHost & {
|
|
21
|
+
getMarkdownThemeWithSettings: () => any;
|
|
22
|
+
loadingAnimation?: {
|
|
23
|
+
stop(): void;
|
|
24
|
+
} | undefined;
|
|
25
|
+
statusContainer: {
|
|
26
|
+
clear(): void;
|
|
27
|
+
};
|
|
28
|
+
}, rs: StreamingRenderState, contentBlocks: Array<any>): {
|
|
29
|
+
toreDownPinnedZone: boolean;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=chat-pinned-zone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-pinned-zone.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/controllers/chat-pinned-zone.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAOzE,wBAAgB,4BAA4B,CAC3C,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,GACvB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAiB/C;AAED,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAExE;AAKD,wBAAgB,6BAA6B,CAC5C,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,oBAAoB,GAC/B,MAAM,CAeR;AAED,wBAAgB,kBAAkB,CACjC,IAAI,EAAE;IAAE,sBAAsB,EAAE;QAAE,KAAK,IAAI,IAAI,CAAA;KAAE,CAAC;IAAC,EAAE,EAAE;QAAE,aAAa,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;IAAC,oBAAoB,EAAE,oBAAoB,CAAA;CAAE,EAC7I,OAAO,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACrC,IAAI,CAaN;AAED,wBAAgB,uBAAuB,CACtC,IAAI,EAAE,wBAAwB,GAAG;IAChC,4BAA4B,EAAE,MAAM,GAAG,CAAC;IACxC,gBAAgB,CAAC,EAAE;QAAE,IAAI,IAAI,IAAI,CAAA;KAAE,GAAG,SAAS,CAAC;IAChD,eAAe,EAAE;QAAE,KAAK,IAAI,IAAI,CAAA;KAAE,CAAC;CACnC,EACD,EAAE,EAAE,oBAAoB,EACxB,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,GACvB;IAAE,kBAAkB,EAAE,OAAO,CAAA;CAAE,CAyEjC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Pinned assistant output zone above the editor during tool runs.
|
|
3
|
+
import { Markdown } from "@gsd/pi-tui";
|
|
4
|
+
import { theme } from "@gsd/pi-coding-agent/theme/theme.js";
|
|
5
|
+
import { DynamicBorder } from "../components/dynamic-border.js";
|
|
6
|
+
// Pinnable text candidates: non-empty text blocks that appear strictly before
|
|
7
|
+
// the most recent tool call, returned newest-first. Text blocks after the last
|
|
8
|
+
// tool call are still streaming live into the chat container.
|
|
9
|
+
export function findLatestPinnableCandidates(contentBlocks) {
|
|
10
|
+
let lastToolIdx = -1;
|
|
11
|
+
for (let i = contentBlocks.length - 1; i >= 0; i--) {
|
|
12
|
+
const c = contentBlocks[i];
|
|
13
|
+
if (c?.type === "toolCall" || c?.type === "serverToolUse") {
|
|
14
|
+
lastToolIdx = i;
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const out = [];
|
|
19
|
+
for (let i = lastToolIdx - 1; i >= 0; i--) {
|
|
20
|
+
const c = contentBlocks[i];
|
|
21
|
+
if (c?.type === "text" && typeof c.text === "string" && c.text.trim()) {
|
|
22
|
+
out.push({ text: c.text.trim(), contentIndex: i });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
export function findLatestPinnableText(contentBlocks) {
|
|
28
|
+
return findLatestPinnableCandidates(contentBlocks)[0]?.text ?? "";
|
|
29
|
+
}
|
|
30
|
+
// Sum rendered line counts of segments that appear strictly after the given
|
|
31
|
+
// content-block index. Used to decide whether a pinnable text block has
|
|
32
|
+
// scrolled out of the viewport and therefore warrants mirroring.
|
|
33
|
+
export function rowsRenderedAfterContentIndex(contentIndex, width, streamState) {
|
|
34
|
+
let rows = 0;
|
|
35
|
+
for (const seg of streamState.renderedSegments) {
|
|
36
|
+
try {
|
|
37
|
+
if (seg.kind === "text-run" && seg.startIndex > contentIndex) {
|
|
38
|
+
rows += seg.component.render(width).length;
|
|
39
|
+
}
|
|
40
|
+
else if (seg.kind === "tool" && seg.contentIndex > contentIndex) {
|
|
41
|
+
rows += seg.component.render(width).length;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Defensive: a component that throws during measurement shouldn't
|
|
46
|
+
// destabilize pinned-zone logic. Skip it.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return rows;
|
|
50
|
+
}
|
|
51
|
+
export function tearDownPinnedZone(host, options) {
|
|
52
|
+
const streamState = host.streamingRenderState;
|
|
53
|
+
const needsRealign = streamState.pinnedZoneNeedsViewportRealign;
|
|
54
|
+
if (streamState.pinnedBorder)
|
|
55
|
+
streamState.pinnedBorder.stopSpinner();
|
|
56
|
+
streamState.pinnedBorder = undefined;
|
|
57
|
+
streamState.pinnedTextComponent = undefined;
|
|
58
|
+
host.pinnedMessageContainer.clear();
|
|
59
|
+
streamState.lastPinnedText = "";
|
|
60
|
+
streamState.hasToolsInTurn = false;
|
|
61
|
+
streamState.pinnedZoneNeedsViewportRealign = false;
|
|
62
|
+
if (options?.realignViewport && needsRealign) {
|
|
63
|
+
host.ui.requestRender(true);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function updatePinnedMessageZone(host, rs, contentBlocks) {
|
|
67
|
+
const hasTools = contentBlocks.some((c) => c.type === "toolCall" || c.type === "serverToolUse");
|
|
68
|
+
if (hasTools)
|
|
69
|
+
rs.hasToolsInTurn = true;
|
|
70
|
+
if (!rs.hasToolsInTurn) {
|
|
71
|
+
return { toreDownPinnedZone: false };
|
|
72
|
+
}
|
|
73
|
+
const candidates = findLatestPinnableCandidates(contentBlocks);
|
|
74
|
+
const termRows = host.ui.terminal.rows;
|
|
75
|
+
const termCols = host.ui.terminal.columns;
|
|
76
|
+
const pinnedMax = Math.max(3, Math.floor(termRows * 0.4));
|
|
77
|
+
// Reserve rows for pinned zone + its border + editor + footer chrome.
|
|
78
|
+
// Anything below this row budget is still in the viewport.
|
|
79
|
+
const offscreenThreshold = Math.max(1, termRows - pinnedMax - 8);
|
|
80
|
+
// Walk candidates newest→oldest; pick the first whose following
|
|
81
|
+
// segments have pushed enough rows to scroll it off-screen.
|
|
82
|
+
let picked;
|
|
83
|
+
for (const c of candidates) {
|
|
84
|
+
if (rowsRenderedAfterContentIndex(c.contentIndex, termCols, rs) >= offscreenThreshold) {
|
|
85
|
+
picked = c;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (picked) {
|
|
90
|
+
if (picked.text !== rs.lastPinnedText) {
|
|
91
|
+
rs.lastPinnedText = picked.text;
|
|
92
|
+
if (!rs.pinnedBorder) {
|
|
93
|
+
// First time: create border + text component
|
|
94
|
+
host.pinnedMessageContainer.clear();
|
|
95
|
+
rs.pinnedBorder = new DynamicBorder((str) => theme.fg("dim", str), "Working · Latest Output");
|
|
96
|
+
rs.pinnedBorder.startSpinner(host.ui, (str) => theme.fg("accent", str));
|
|
97
|
+
host.pinnedMessageContainer.addChild(rs.pinnedBorder);
|
|
98
|
+
rs.pinnedTextComponent = new Markdown(picked.text, 1, 0, host.getMarkdownThemeWithSettings());
|
|
99
|
+
// Cap pinned content to ~40% of terminal height so tall output
|
|
100
|
+
// doesn't exceed the viewport and cause render flashing.
|
|
101
|
+
rs.pinnedTextComponent.maxLines = pinnedMax;
|
|
102
|
+
host.pinnedMessageContainer.addChild(rs.pinnedTextComponent);
|
|
103
|
+
rs.pinnedZoneNeedsViewportRealign = true;
|
|
104
|
+
// Hide the separate status loader — the pinned zone replaces it
|
|
105
|
+
if (host.loadingAnimation) {
|
|
106
|
+
host.loadingAnimation.stop();
|
|
107
|
+
host.loadingAnimation = undefined;
|
|
108
|
+
}
|
|
109
|
+
host.statusContainer.clear();
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// Update existing markdown component in-place
|
|
113
|
+
rs.pinnedTextComponent?.setText(picked.text);
|
|
114
|
+
// Refresh maxLines in case terminal was resized
|
|
115
|
+
if (rs.pinnedTextComponent) {
|
|
116
|
+
rs.pinnedTextComponent.maxLines = pinnedMax;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { toreDownPinnedZone: false };
|
|
121
|
+
}
|
|
122
|
+
if (rs.pinnedBorder) {
|
|
123
|
+
// Every candidate is still visible in the chat scrollback —
|
|
124
|
+
// tear down the pinned zone so we don't duplicate on-screen text.
|
|
125
|
+
tearDownPinnedZone(host);
|
|
126
|
+
return { toreDownPinnedZone: true };
|
|
127
|
+
}
|
|
128
|
+
return { toreDownPinnedZone: false };
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=chat-pinned-zone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-pinned-zone.js","sourceRoot":"","sources":["../../../../src/modes/interactive/controllers/chat-pinned-zone.ts"],"names":[],"mappings":"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","sourcesContent":["// Project/App: gsd-pi\n// File Purpose: Pinned assistant output zone above the editor during tool runs.\nimport { Markdown } from \"@gsd/pi-tui\";\n\nimport type { InteractiveModeStateHost } from \"../interactive-mode-state.js\";\nimport type { StreamingRenderState } from \"../streaming-render-state.js\";\nimport { theme } from \"@gsd/pi-coding-agent/theme/theme.js\";\nimport { DynamicBorder } from \"../components/dynamic-border.js\";\n\n// Pinnable text candidates: non-empty text blocks that appear strictly before\n// the most recent tool call, returned newest-first. Text blocks after the last\n// tool call are still streaming live into the chat container.\nexport function findLatestPinnableCandidates(\n\tcontentBlocks: Array<any>,\n): Array<{ text: string; contentIndex: number }> {\n\tlet lastToolIdx = -1;\n\tfor (let i = contentBlocks.length - 1; i >= 0; i--) {\n\t\tconst c = contentBlocks[i];\n\t\tif (c?.type === \"toolCall\" || c?.type === \"serverToolUse\") {\n\t\t\tlastToolIdx = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\tconst out: Array<{ text: string; contentIndex: number }> = [];\n\tfor (let i = lastToolIdx - 1; i >= 0; i--) {\n\t\tconst c = contentBlocks[i];\n\t\tif (c?.type === \"text\" && typeof c.text === \"string\" && c.text.trim()) {\n\t\t\tout.push({ text: c.text.trim(), contentIndex: i });\n\t\t}\n\t}\n\treturn out;\n}\n\nexport function findLatestPinnableText(contentBlocks: Array<any>): string {\n\treturn findLatestPinnableCandidates(contentBlocks)[0]?.text ?? \"\";\n}\n\n// Sum rendered line counts of segments that appear strictly after the given\n// content-block index. Used to decide whether a pinnable text block has\n// scrolled out of the viewport and therefore warrants mirroring.\nexport function rowsRenderedAfterContentIndex(\n\tcontentIndex: number,\n\twidth: number,\n\tstreamState: StreamingRenderState,\n): number {\n\tlet rows = 0;\n\tfor (const seg of streamState.renderedSegments) {\n\t\ttry {\n\t\t\tif (seg.kind === \"text-run\" && seg.startIndex > contentIndex) {\n\t\t\t\trows += seg.component.render(width).length;\n\t\t\t} else if (seg.kind === \"tool\" && seg.contentIndex > contentIndex) {\n\t\t\t\trows += seg.component.render(width).length;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Defensive: a component that throws during measurement shouldn't\n\t\t\t// destabilize pinned-zone logic. Skip it.\n\t\t}\n\t}\n\treturn rows;\n}\n\nexport function tearDownPinnedZone(\n\thost: { pinnedMessageContainer: { clear(): void }; ui: { requestRender(force?: boolean): void }; streamingRenderState: StreamingRenderState },\n\toptions?: { realignViewport?: boolean },\n): void {\n\tconst streamState = host.streamingRenderState;\n\tconst needsRealign = streamState.pinnedZoneNeedsViewportRealign;\n\tif (streamState.pinnedBorder) streamState.pinnedBorder.stopSpinner();\n\tstreamState.pinnedBorder = undefined;\n\tstreamState.pinnedTextComponent = undefined;\n\thost.pinnedMessageContainer.clear();\n\tstreamState.lastPinnedText = \"\";\n\tstreamState.hasToolsInTurn = false;\n\tstreamState.pinnedZoneNeedsViewportRealign = false;\n\tif (options?.realignViewport && needsRealign) {\n\t\thost.ui.requestRender(true);\n\t}\n}\n\nexport function updatePinnedMessageZone(\n\thost: InteractiveModeStateHost & {\n\t\tgetMarkdownThemeWithSettings: () => any;\n\t\tloadingAnimation?: { stop(): void } | undefined;\n\t\tstatusContainer: { clear(): void };\n\t},\n\trs: StreamingRenderState,\n\tcontentBlocks: Array<any>,\n): { toreDownPinnedZone: boolean } {\n\tconst hasTools = contentBlocks.some(\n\t\t(c: any) => c.type === \"toolCall\" || c.type === \"serverToolUse\",\n\t);\n\tif (hasTools) rs.hasToolsInTurn = true;\n\n\tif (!rs.hasToolsInTurn) {\n\t\treturn { toreDownPinnedZone: false };\n\t}\n\n\tconst candidates = findLatestPinnableCandidates(contentBlocks);\n\tconst termRows = host.ui.terminal.rows;\n\tconst termCols = host.ui.terminal.columns;\n\tconst pinnedMax = Math.max(3, Math.floor(termRows * 0.4));\n\t// Reserve rows for pinned zone + its border + editor + footer chrome.\n\t// Anything below this row budget is still in the viewport.\n\tconst offscreenThreshold = Math.max(1, termRows - pinnedMax - 8);\n\n\t// Walk candidates newest→oldest; pick the first whose following\n\t// segments have pushed enough rows to scroll it off-screen.\n\tlet picked: { text: string; contentIndex: number } | undefined;\n\tfor (const c of candidates) {\n\t\tif (rowsRenderedAfterContentIndex(c.contentIndex, termCols, rs) >= offscreenThreshold) {\n\t\t\tpicked = c;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (picked) {\n\t\tif (picked.text !== rs.lastPinnedText) {\n\t\t\trs.lastPinnedText = picked.text;\n\n\t\t\tif (!rs.pinnedBorder) {\n\t\t\t\t// First time: create border + text component\n\t\t\t\thost.pinnedMessageContainer.clear();\n\t\t\t\trs.pinnedBorder = new DynamicBorder(\n\t\t\t\t\t(str: string) => theme.fg(\"dim\", str),\n\t\t\t\t\t\"Working · Latest Output\",\n\t\t\t\t);\n\t\t\t\trs.pinnedBorder.startSpinner(host.ui, (str: string) => theme.fg(\"accent\", str));\n\t\t\t\thost.pinnedMessageContainer.addChild(rs.pinnedBorder);\n\t\t\t\trs.pinnedTextComponent = new Markdown(picked.text, 1, 0, host.getMarkdownThemeWithSettings());\n\t\t\t\t// Cap pinned content to ~40% of terminal height so tall output\n\t\t\t\t// doesn't exceed the viewport and cause render flashing.\n\t\t\t\trs.pinnedTextComponent.maxLines = pinnedMax;\n\t\t\t\thost.pinnedMessageContainer.addChild(rs.pinnedTextComponent);\n\t\t\t\trs.pinnedZoneNeedsViewportRealign = true;\n\t\t\t\t// Hide the separate status loader — the pinned zone replaces it\n\t\t\t\tif (host.loadingAnimation) {\n\t\t\t\t\thost.loadingAnimation.stop();\n\t\t\t\t\thost.loadingAnimation = undefined;\n\t\t\t\t}\n\t\t\t\thost.statusContainer.clear();\n\t\t\t} else {\n\t\t\t\t// Update existing markdown component in-place\n\t\t\t\trs.pinnedTextComponent?.setText(picked.text);\n\t\t\t\t// Refresh maxLines in case terminal was resized\n\t\t\t\tif (rs.pinnedTextComponent) {\n\t\t\t\t\trs.pinnedTextComponent.maxLines = pinnedMax;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn { toreDownPinnedZone: false };\n\t}\n\n\tif (rs.pinnedBorder) {\n\t\t// Every candidate is still visible in the chat scrollback —\n\t\t// tear down the pinned zone so we don't duplicate on-screen text.\n\t\ttearDownPinnedZone(host);\n\t\treturn { toreDownPinnedZone: true };\n\t}\n\n\treturn { toreDownPinnedZone: false };\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { InteractiveModeStateHost } from "../interactive-mode-state.js";
|
|
2
|
+
import type { StreamingRenderState } from "../streaming-render-state.js";
|
|
3
|
+
import type { TimestampFormat } from "../components/timestamp.js";
|
|
4
|
+
/** Host surface for streaming helpers — extends state host with mode methods. */
|
|
5
|
+
type ChatStreamHost = InteractiveModeStateHost & {
|
|
6
|
+
formatWebSearchResult: (content: unknown) => string;
|
|
7
|
+
getRegisteredToolDefinition: (toolName: string) => any;
|
|
8
|
+
getMarkdownThemeWithSettings: () => any;
|
|
9
|
+
};
|
|
10
|
+
export declare function applySubTurnContentShrink(rs: StreamingRenderState, contentBlocks: Array<any>): void;
|
|
11
|
+
export declare function scanNewContentBlocks(host: ChatStreamHost, rs: StreamingRenderState, contentBlocks: Array<any>): void;
|
|
12
|
+
export declare function runSegmentWalker(host: ChatStreamHost, rs: StreamingRenderState, timestampFormat: TimestampFormat): void;
|
|
13
|
+
export declare function rebuildSegmentsOnMessageEnd(host: ChatStreamHost, rs: StreamingRenderState, timestampFormat: TimestampFormat): void;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=chat-segment-walker.d.ts.map
|
package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-segment-walker.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/controllers/chat-segment-walker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,KAAK,EAAmB,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAE1F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAelE,iFAAiF;AACjF,KAAK,cAAc,GAAG,wBAAwB,GAAG;IAChD,qBAAqB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;IACpD,2BAA2B,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,CAAC;IACvD,4BAA4B,EAAE,MAAM,GAAG,CAAC;CACxC,CAAC;AAEF,wBAAgB,yBAAyB,CACxC,EAAE,EAAE,oBAAoB,EACxB,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,GACvB,IAAI,CA8BN;AAED,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,oBAAoB,EACxB,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,GACvB,IAAI,CAuDN;AAED,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,oBAAoB,EACxB,eAAe,EAAE,eAAe,GAC9B,IAAI,CAmJN;AAED,wBAAgB,2BAA2B,CAC1C,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,oBAAoB,EACxB,eAAe,EAAE,eAAe,GAC9B,IAAI,CAsGN"}
|