@ai-agent-forge/agent-forge 0.86.7 → 0.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +8 -9
- package/dist/artifacts/artifact-store.d.ts.map +1 -1
- package/dist/artifacts/artifact-store.js +9 -5
- package/dist/artifacts/artifact-store.js.map +1 -1
- package/dist/bundle/.build-manifest.json +410 -371
- package/dist/bundle/chunks/{chunk-A7GUOX2H.js → chunk-6ZKDNZHA.js} +5 -1
- package/dist/bundle/chunks/chunk-BFC2BDVJ.js +13 -0
- package/dist/bundle/chunks/chunk-MOHOLL3F.js +1456 -0
- package/dist/bundle/chunks/image-resize-worker.js +12 -1
- package/dist/bundle/chunks/{virtual-modules-3IZ4MYSQ.js → virtual-modules-KZ5DRH7Q.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/client.js +1 -1
- package/dist/bundle/index.js +2 -2
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +231 -185
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-check.d.ts.map +1 -1
- package/dist/cli/auth-check.js +3 -2
- package/dist/cli/auth-check.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js +15 -9
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/cli/credential-print.d.ts.map +1 -1
- package/dist/cli/credential-print.js +14 -7
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/cli/experimental/command.d.ts.map +1 -1
- package/dist/cli/experimental/command.js +6 -5
- package/dist/cli/experimental/command.js.map +1 -1
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +9 -8
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/plugin-manager-controller.d.ts.map +1 -1
- package/dist/cli/plugin-manager-controller.js +2 -1
- package/dist/cli/plugin-manager-controller.js.map +1 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +8 -5
- package/dist/cli/serve.js.map +1 -1
- package/dist/client/remote-session.d.ts.map +1 -1
- package/dist/client/remote-session.js +16 -9
- package/dist/client/remote-session.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-capability-contract.d.ts +0 -7
- package/dist/core/agent-capability-contract.d.ts.map +1 -1
- package/dist/core/agent-capability-contract.js +72 -68
- package/dist/core/agent-capability-contract.js.map +1 -1
- package/dist/core/agent-session-bash.d.ts.map +1 -1
- package/dist/core/agent-session-bash.js +5 -4
- package/dist/core/agent-session-bash.js.map +1 -1
- package/dist/core/agent-session-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-compaction.js +28 -22
- package/dist/core/agent-session-compaction.js.map +1 -1
- package/dist/core/agent-session-context.d.ts.map +1 -1
- package/dist/core/agent-session-context.js +18 -13
- package/dist/core/agent-session-context.js.map +1 -1
- package/dist/core/agent-session-model.d.ts.map +1 -1
- package/dist/core/agent-session-model.js +2 -1
- package/dist/core/agent-session-model.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +3 -2
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +7 -6
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session-tools.d.ts.map +1 -1
- package/dist/core/agent-session-tools.js +3 -2
- package/dist/core/agent-session-tools.js.map +1 -1
- package/dist/core/agent-session.d.ts +40 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +142 -34
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-guidance.d.ts.map +1 -1
- package/dist/core/auth-guidance.js +11 -5
- package/dist/core/auth-guidance.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +9 -6
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +2 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/embedded-sdk.d.ts.map +1 -1
- package/dist/core/embedded-sdk.js +10 -6
- package/dist/core/embedded-sdk.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +9 -4
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/extensions/agent-composition.d.ts.map +1 -1
- package/dist/core/extensions/agent-composition.js +26 -19
- package/dist/core/extensions/agent-composition.js.map +1 -1
- package/dist/core/extensions/bootstrap-agent-loop.d.ts.map +1 -1
- package/dist/core/extensions/bootstrap-agent-loop.js +3 -2
- package/dist/core/extensions/bootstrap-agent-loop.js.map +1 -1
- package/dist/core/extensions/bootstrap-lifecycle.d.ts.map +1 -1
- package/dist/core/extensions/bootstrap-lifecycle.js +20 -17
- package/dist/core/extensions/bootstrap-lifecycle.js.map +1 -1
- package/dist/core/extensions/bootstrap-overflow-policy.d.ts.map +1 -1
- package/dist/core/extensions/bootstrap-overflow-policy.js +2 -1
- package/dist/core/extensions/bootstrap-overflow-policy.js.map +1 -1
- package/dist/core/extensions/bootstrap-retry-policy.d.ts.map +1 -1
- package/dist/core/extensions/bootstrap-retry-policy.js +2 -1
- package/dist/core/extensions/bootstrap-retry-policy.js.map +1 -1
- package/dist/core/extensions/bootstrap-skills.d.ts.map +1 -1
- package/dist/core/extensions/bootstrap-skills.js +7 -6
- package/dist/core/extensions/bootstrap-skills.js.map +1 -1
- package/dist/core/extensions/bootstrap-tools.d.ts.map +1 -1
- package/dist/core/extensions/bootstrap-tools.js +2 -1
- package/dist/core/extensions/bootstrap-tools.js.map +1 -1
- package/dist/core/extensions/capability-plugin.d.ts.map +1 -1
- package/dist/core/extensions/capability-plugin.js +37 -13
- package/dist/core/extensions/capability-plugin.js.map +1 -1
- package/dist/core/extensions/capability-runtime-events.d.ts.map +1 -1
- package/dist/core/extensions/capability-runtime-events.js +17 -13
- package/dist/core/extensions/capability-runtime-events.js.map +1 -1
- package/dist/core/extensions/capability-runtime-guards.d.ts.map +1 -1
- package/dist/core/extensions/capability-runtime-guards.js +16 -15
- package/dist/core/extensions/capability-runtime-guards.js.map +1 -1
- package/dist/core/extensions/capability-runtime-manifest.d.ts.map +1 -1
- package/dist/core/extensions/capability-runtime-manifest.js +52 -30
- package/dist/core/extensions/capability-runtime-manifest.js.map +1 -1
- package/dist/core/extensions/capability-runtime-validation.d.ts.map +1 -1
- package/dist/core/extensions/capability-runtime-validation.js +66 -65
- package/dist/core/extensions/capability-runtime-validation.js.map +1 -1
- package/dist/core/extensions/capability-runtime.d.ts.map +1 -1
- package/dist/core/extensions/capability-runtime.js +147 -127
- package/dist/core/extensions/capability-runtime.js.map +1 -1
- package/dist/core/extensions/compaction-memory-failure-mode.d.ts.map +1 -1
- package/dist/core/extensions/compaction-memory-failure-mode.js +3 -2
- package/dist/core/extensions/compaction-memory-failure-mode.js.map +1 -1
- package/dist/core/extensions/context-strategy-binding.d.ts.map +1 -1
- package/dist/core/extensions/context-strategy-binding.js +7 -4
- package/dist/core/extensions/context-strategy-binding.js.map +1 -1
- package/dist/core/extensions/lifecycle-catalog.d.ts.map +1 -1
- package/dist/core/extensions/lifecycle-catalog.js +290 -279
- package/dist/core/extensions/lifecycle-catalog.js.map +1 -1
- package/dist/core/extensions/lifecycle-delivery-queue.d.ts.map +1 -1
- package/dist/core/extensions/lifecycle-delivery-queue.js +3 -2
- package/dist/core/extensions/lifecycle-delivery-queue.js.map +1 -1
- package/dist/core/facade/coding-agent-facade.d.ts +6 -0
- package/dist/core/facade/coding-agent-facade.d.ts.map +1 -1
- package/dist/core/facade/coding-agent-facade.js +2 -0
- package/dist/core/facade/coding-agent-facade.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +2 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/i18n/index.d.ts +14 -0
- package/dist/core/i18n/index.d.ts.map +1 -0
- package/dist/core/i18n/index.js +67 -0
- package/dist/core/i18n/index.js.map +1 -0
- package/dist/core/i18n/locales/en/example.d.ts +7 -0
- package/dist/core/i18n/locales/en/example.d.ts.map +1 -0
- package/dist/core/i18n/locales/en/example.js +7 -0
- package/dist/core/i18n/locales/en/example.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/cli.d.ts +8 -0
- package/dist/core/i18n/locales/zh-cn/cli.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/cli.js +227 -0
- package/dist/core/i18n/locales/zh-cn/cli.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/common.d.ts +6 -0
- package/dist/core/i18n/locales/zh-cn/common.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/common.js +6 -0
- package/dist/core/i18n/locales/zh-cn/common.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts +13 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-core.js +173 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-core.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts +12 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js +582 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-final.d.ts +9 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-final.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-final.js +55 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-final.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-plugins.d.ts +11 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-plugins.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-plugins.js +455 -0
- package/dist/core/i18n/locales/zh-cn/core-contract-plugins.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-errors.d.ts +9 -0
- package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/core-errors.js +191 -0
- package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/example.d.ts +7 -0
- package/dist/core/i18n/locales/zh-cn/example.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/example.js +10 -0
- package/dist/core/i18n/locales/zh-cn/example.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/index.d.ts +2 -0
- package/dist/core/i18n/locales/zh-cn/index.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/index.js +41 -0
- package/dist/core/i18n/locales/zh-cn/index.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts +10 -0
- package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/interactive-mode.js +265 -0
- package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/keybindings.d.ts +11 -0
- package/dist/core/i18n/locales/zh-cn/keybindings.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/keybindings.js +67 -0
- package/dist/core/i18n/locales/zh-cn/keybindings.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts +7 -0
- package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/slash-commands.js +39 -0
- package/dist/core/i18n/locales/zh-cn/slash-commands.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-builtin.d.ts +10 -0
- package/dist/core/i18n/locales/zh-cn/tui-builtin.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-builtin.js +15 -0
- package/dist/core/i18n/locales/zh-cn/tui-builtin.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-components-misc.d.ts +13 -0
- package/dist/core/i18n/locales/zh-cn/tui-components-misc.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-components-misc.js +102 -0
- package/dist/core/i18n/locales/zh-cn/tui-components-misc.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-components-selectors.d.ts +12 -0
- package/dist/core/i18n/locales/zh-cn/tui-components-selectors.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-components-selectors.js +288 -0
- package/dist/core/i18n/locales/zh-cn/tui-components-selectors.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.d.ts +9 -0
- package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.js +79 -0
- package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.js.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-self-owned.d.ts +8 -0
- package/dist/core/i18n/locales/zh-cn/tui-self-owned.d.ts.map +1 -0
- package/dist/core/i18n/locales/zh-cn/tui-self-owned.js +72 -0
- package/dist/core/i18n/locales/zh-cn/tui-self-owned.js.map +1 -0
- package/dist/core/keybindings.d.ts +15 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +18 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +5 -1
- package/dist/core/logger.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +8 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-install-host.d.ts.map +1 -1
- package/dist/core/package-install-host.js +4 -3
- package/dist/core/package-install-host.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +15 -10
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/package-resource-collection.d.ts.map +1 -1
- package/dist/core/package-resource-collection.js +2 -1
- package/dist/core/package-resource-collection.js.map +1 -1
- package/dist/core/package-source.d.ts.map +1 -1
- package/dist/core/package-source.js +3 -2
- package/dist/core/package-source.js.map +1 -1
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +22 -13
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +6 -2
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +15 -5
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/sdk.d.ts +19 -5
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +74 -54
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-cwd.d.ts.map +1 -1
- package/dist/core/session-cwd.js +5 -2
- package/dist/core/session-cwd.js.map +1 -1
- package/dist/core/session-export.d.ts.map +1 -1
- package/dist/core/session-export.js +2 -1
- package/dist/core/session-export.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +39 -29
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-storage.d.ts.map +1 -1
- package/dist/core/session-storage.js +3 -3
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/settings-manager.d.ts +11 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +27 -5
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +53 -32
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +25 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +11 -7
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +9 -4
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +4 -3
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +3 -2
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +2 -1
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +6 -2
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/durable/durable-service.d.ts.map +1 -1
- package/dist/durable/durable-service.js +25 -18
- package/dist/durable/durable-service.js.map +1 -1
- package/dist/host/checkpoint-commit.d.ts.map +1 -1
- package/dist/host/checkpoint-commit.js +2 -1
- package/dist/host/checkpoint-commit.js.map +1 -1
- package/dist/host/manual-compaction.d.ts.map +1 -1
- package/dist/host/manual-compaction.js +2 -1
- package/dist/host/manual-compaction.js.map +1 -1
- package/dist/host/output-projection.d.ts +0 -13
- package/dist/host/output-projection.d.ts.map +1 -1
- package/dist/host/output-projection.js +16 -2
- package/dist/host/output-projection.js.map +1 -1
- package/dist/host/summary-retry.d.ts.map +1 -1
- package/dist/host/summary-retry.js +3 -2
- package/dist/host/summary-retry.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +94 -58
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/agents-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/agents-view.js +18 -15
- package/dist/modes/interactive/components/agents-view.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +6 -5
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +12 -7
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +2 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +6 -5
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +9 -5
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +18 -15
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-entry.js +2 -1
- package/dist/modes/interactive/components/custom-entry.js.map +1 -1
- package/dist/modes/interactive/components/dialog-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/dialog-editor.js +5 -4
- package/dist/modes/interactive/components/dialog-editor.js.map +1 -1
- package/dist/modes/interactive/components/dialog-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/dialog-input.js +2 -1
- package/dist/modes/interactive/components/dialog-input.js.map +1 -1
- package/dist/modes/interactive/components/dialog-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/dialog-selector.js +4 -3
- package/dist/modes/interactive/components/dialog-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +11 -10
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +7 -4
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +3 -2
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +12 -11
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/mermaid.d.ts.map +1 -1
- package/dist/modes/interactive/components/mermaid.js +6 -2
- package/dist/modes/interactive/components/mermaid.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +28 -16
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +10 -9
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/plugin-manager-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/plugin-manager-entry.js +22 -13
- package/dist/modes/interactive/components/plugin-manager-entry.js.map +1 -1
- package/dist/modes/interactive/components/plugin-selection-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/plugin-selection-entry.js +27 -12
- package/dist/modes/interactive/components/plugin-selection-entry.js.map +1 -1
- package/dist/modes/interactive/components/queue-manager-selector.d.ts +60 -0
- package/dist/modes/interactive/components/queue-manager-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/queue-manager-selector.js +166 -0
- package/dist/modes/interactive/components/queue-manager-selector.js.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +16 -14
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +43 -31
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +113 -96
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.js +5 -4
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +3 -2
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +4 -3
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-indicator.js +14 -5
- package/dist/modes/interactive/components/status-indicator.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +2 -1
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +11 -4
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +8 -3
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-list.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-list.js +7 -6
- package/dist/modes/interactive/components/tree-list.js.map +1 -1
- package/dist/modes/interactive/components/tree-render.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-render.js +28 -23
- package/dist/modes/interactive/components/tree-render.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +9 -7
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +5 -4
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/workflow-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/workflow-view.js +36 -21
- package/dist/modes/interactive/components/workflow-view.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-auth.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-auth.js +71 -40
- package/dist/modes/interactive/interactive-auth.js.map +1 -1
- package/dist/modes/interactive/interactive-commands.d.ts +1 -0
- package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-commands.js +3 -0
- package/dist/modes/interactive/interactive-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-dialog-ui.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-dialog-ui.js +5 -5
- package/dist/modes/interactive/interactive-dialog-ui.js.map +1 -1
- package/dist/modes/interactive/interactive-format.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-format.js +34 -18
- package/dist/modes/interactive/interactive-format.js.map +1 -1
- package/dist/modes/interactive/interactive-helpers.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-helpers.js +7 -2
- package/dist/modes/interactive/interactive-helpers.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +327 -243
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/interactive-selectors.d.ts +9 -0
- package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-selectors.js +79 -25
- package/dist/modes/interactive/interactive-selectors.js.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +5 -1
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +26 -23
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +3 -2
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +3 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +3 -2
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +28 -13
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/plugins/ecosystem/catalog.d.ts.map +1 -1
- package/dist/plugins/ecosystem/catalog.js +3 -2
- package/dist/plugins/ecosystem/catalog.js.map +1 -1
- package/dist/plugins/ecosystem/cli.d.ts.map +1 -1
- package/dist/plugins/ecosystem/cli.js +2 -1
- package/dist/plugins/ecosystem/cli.js.map +1 -1
- package/dist/plugins/ecosystem/default-plugin-market.d.ts.map +1 -1
- package/dist/plugins/ecosystem/default-plugin-market.js +3 -2
- package/dist/plugins/ecosystem/default-plugin-market.js.map +1 -1
- package/dist/plugins/ecosystem/distribution.d.ts.map +1 -1
- package/dist/plugins/ecosystem/distribution.js +2 -1
- package/dist/plugins/ecosystem/distribution.js.map +1 -1
- package/dist/plugins/ecosystem/installer.d.ts.map +1 -1
- package/dist/plugins/ecosystem/installer.js +3 -2
- package/dist/plugins/ecosystem/installer.js.map +1 -1
- package/dist/plugins/ecosystem/node-disk-registry-source.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-disk-registry-source.js +20 -17
- package/dist/plugins/ecosystem/node-disk-registry-source.js.map +1 -1
- package/dist/plugins/ecosystem/node-distribution-host.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-distribution-host.js +35 -34
- package/dist/plugins/ecosystem/node-distribution-host.js.map +1 -1
- package/dist/plugins/ecosystem/node-npm-registry-source.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-npm-registry-source.js +35 -34
- package/dist/plugins/ecosystem/node-npm-registry-source.js.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-host.js +9 -8
- package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-persistence.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-persistence.js +4 -3
- package/dist/plugins/ecosystem/node-package-install-persistence.js.map +1 -1
- package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-plugin-bootstrap.js +21 -5
- package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
- package/dist/plugins/ecosystem/node-plugin-trust-root.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-plugin-trust-root.js +10 -9
- package/dist/plugins/ecosystem/node-plugin-trust-root.js.map +1 -1
- package/dist/plugins/ecosystem/node-registry-source.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-registry-source.js +31 -30
- package/dist/plugins/ecosystem/node-registry-source.js.map +1 -1
- package/dist/plugins/ecosystem/node-release-package-source.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-release-package-source.js +30 -29
- package/dist/plugins/ecosystem/node-release-package-source.js.map +1 -1
- package/dist/plugins/ecosystem/node-tar-writer.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-tar-writer.js +11 -10
- package/dist/plugins/ecosystem/node-tar-writer.js.map +1 -1
- package/dist/plugins/ecosystem/node-url-package-source.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-url-package-source.js +21 -20
- package/dist/plugins/ecosystem/node-url-package-source.js.map +1 -1
- package/dist/plugins/ecosystem/plugin-acquisition.d.ts.map +1 -1
- package/dist/plugins/ecosystem/plugin-acquisition.js +3 -2
- package/dist/plugins/ecosystem/plugin-acquisition.js.map +1 -1
- package/dist/plugins/ecosystem/registry-cli-adapter.d.ts.map +1 -1
- package/dist/plugins/ecosystem/registry-cli-adapter.js +2 -1
- package/dist/plugins/ecosystem/registry-cli-adapter.js.map +1 -1
- package/dist/plugins/ecosystem/registry.d.ts.map +1 -1
- package/dist/plugins/ecosystem/registry.js +35 -32
- package/dist/plugins/ecosystem/registry.js.map +1 -1
- package/dist/plugins/ecosystem/resolver.d.ts.map +1 -1
- package/dist/plugins/ecosystem/resolver.js +3 -2
- package/dist/plugins/ecosystem/resolver.js.map +1 -1
- package/dist/plugins/ecosystem/selection-store.d.ts.map +1 -1
- package/dist/plugins/ecosystem/selection-store.js +11 -10
- package/dist/plugins/ecosystem/selection-store.js.map +1 -1
- package/dist/plugins/ecosystem/selection.d.ts.map +1 -1
- package/dist/plugins/ecosystem/selection.js +8 -7
- package/dist/plugins/ecosystem/selection.js.map +1 -1
- package/dist/plugins/ecosystem/state.d.ts.map +1 -1
- package/dist/plugins/ecosystem/state.js +24 -23
- package/dist/plugins/ecosystem/state.js.map +1 -1
- package/dist/plugins/ecosystem/updates.d.ts.map +1 -1
- package/dist/plugins/ecosystem/updates.js +5 -4
- package/dist/plugins/ecosystem/updates.js.map +1 -1
- package/dist/plugins/mcp-http.d.ts +0 -17
- package/dist/plugins/mcp-http.d.ts.map +1 -1
- package/dist/plugins/mcp-http.js +30 -7
- package/dist/plugins/mcp-http.js.map +1 -1
- package/dist/plugins/plugin-supervisor.d.ts.map +1 -1
- package/dist/plugins/plugin-supervisor.js +19 -1
- package/dist/plugins/plugin-supervisor.js.map +1 -1
- package/dist/profile/first-roles.d.ts.map +1 -1
- package/dist/profile/first-roles.js +10 -1
- package/dist/profile/first-roles.js.map +1 -1
- package/dist/profile/goal-plugin.d.ts.map +1 -1
- package/dist/profile/goal-plugin.js +24 -5
- package/dist/profile/goal-plugin.js.map +1 -1
- package/dist/profile/identity.d.ts.map +1 -1
- package/dist/profile/identity.js +7 -6
- package/dist/profile/identity.js.map +1 -1
- package/dist/profile/lock.d.ts.map +1 -1
- package/dist/profile/lock.js +15 -1
- package/dist/profile/lock.js.map +1 -1
- package/dist/profile/plan-plugin.d.ts.map +1 -1
- package/dist/profile/plan-plugin.js +26 -13
- package/dist/profile/plan-plugin.js.map +1 -1
- package/dist/profile/plugin-profiles.d.ts.map +1 -1
- package/dist/profile/plugin-profiles.js +2 -1
- package/dist/profile/plugin-profiles.js.map +1 -1
- package/dist/profile/policy-manifest.d.ts.map +1 -1
- package/dist/profile/policy-manifest.js +5 -4
- package/dist/profile/policy-manifest.js.map +1 -1
- package/dist/profile/profile-registry.d.ts +0 -12
- package/dist/profile/profile-registry.d.ts.map +1 -1
- package/dist/profile/profile-registry.js +14 -1
- package/dist/profile/profile-registry.js.map +1 -1
- package/dist/profile/role-catalog.d.ts +0 -1
- package/dist/profile/role-catalog.d.ts.map +1 -1
- package/dist/profile/role-catalog.js +17 -15
- package/dist/profile/role-catalog.js.map +1 -1
- package/dist/profile/role-orchestrator.d.ts.map +1 -1
- package/dist/profile/role-orchestrator.js +8 -7
- package/dist/profile/role-orchestrator.js.map +1 -1
- package/dist/profile/role-runner.d.ts.map +1 -1
- package/dist/profile/role-runner.js +3 -2
- package/dist/profile/role-runner.js.map +1 -1
- package/dist/profile/root-facade.d.ts.map +1 -1
- package/dist/profile/root-facade.js +2 -1
- package/dist/profile/root-facade.js.map +1 -1
- package/dist/profile/skill-discovery-wiring.d.ts.map +1 -1
- package/dist/profile/skill-discovery-wiring.js +7 -2
- package/dist/profile/skill-discovery-wiring.js.map +1 -1
- package/dist/profile/state-store.d.ts.map +1 -1
- package/dist/profile/state-store.js +13 -9
- package/dist/profile/state-store.js.map +1 -1
- package/dist/profile/todo-plugin.d.ts.map +1 -1
- package/dist/profile/todo-plugin.js +9 -4
- package/dist/profile/todo-plugin.js.map +1 -1
- package/dist/profiles/builtin-suites.d.ts +13 -6
- package/dist/profiles/builtin-suites.d.ts.map +1 -1
- package/dist/profiles/builtin-suites.js +15 -6
- package/dist/profiles/builtin-suites.js.map +1 -1
- package/dist/profiles/suite-loader.d.ts +25 -1
- package/dist/profiles/suite-loader.d.ts.map +1 -1
- package/dist/profiles/suite-loader.js +34 -9
- package/dist/profiles/suite-loader.js.map +1 -1
- package/dist/remote/server-entry.d.ts.map +1 -1
- package/dist/remote/server-entry.js +3 -2
- package/dist/remote/server-entry.js.map +1 -1
- package/dist/remote/session-source.d.ts.map +1 -1
- package/dist/remote/session-source.js +8 -4
- package/dist/remote/session-source.js.map +1 -1
- package/dist/share/session-share.d.ts.map +1 -1
- package/dist/share/session-share.js +14 -2
- package/dist/share/session-share.js.map +1 -1
- package/dist/testing/plugin-testkit.d.ts.map +1 -1
- package/dist/testing/plugin-testkit.js +4 -3
- package/dist/testing/plugin-testkit.js.map +1 -1
- package/dist/testing/runtime-delivery-queue.d.ts.map +1 -1
- package/dist/testing/runtime-delivery-queue.js +2 -1
- package/dist/testing/runtime-delivery-queue.js.map +1 -1
- package/dist/testing/session-plugin-testkit.d.ts.map +1 -1
- package/dist/testing/session-plugin-testkit.js +10 -7
- package/dist/testing/session-plugin-testkit.js.map +1 -1
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js +2 -1
- package/dist/utils/ansi.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +7 -6
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/image-resize-worker.d.ts.map +1 -1
- package/dist/utils/image-resize-worker.js +3 -2
- package/dist/utils/image-resize-worker.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +9 -8
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +23 -13
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +5 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/keybindings.md +3 -0
- package/docs/quickstart.md +1 -1
- package/docs/settings.md +1 -0
- package/docs/terminal-setup.md +2 -3
- package/docs/usage.md +6 -5
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/with-deps/plugin.json +1 -1
- package/package.json +1 -1
- package/dist/bundle/chunks/chunk-EALJ5DDX.js +0 -1452
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-message-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/user-message-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, Container, getKeybindings, Spacer, Text, truncateToWidth } from \"@agent-forge/tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\ninterface UserMessageItem {\n\tid: string; // Entry ID in the session\n\ttext: string; // The message text\n\ttimestamp?: string; // Optional timestamp if available\n\t/** First line of the reply that followed this message; conversational\n\t * context for recognizing \"which prompt am I returning to\". */\n\treplySummary?: string;\n}\n\n/**\n * Custom user message list component with selection\n */\nclass UserMessageList implements Component {\n\tprivate messages: UserMessageItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tpublic onSelect?: (entryId: string) => void;\n\tpublic onCancel?: () => void;\n\tprivate maxVisible: number = 10; // Max messages visible\n\n\tconstructor(messages: UserMessageItem[], initialSelectedId?: string) {\n\t\t// Store messages in chronological order (oldest to newest)\n\t\tthis.messages = messages;\n\t\tconst initialIndex = initialSelectedId ? messages.findIndex((message) => message.id === initialSelectedId) : -1;\n\t\t// Start with selected message if provided, else default to the most recent\n\t\tthis.selectedIndex = initialIndex >= 0 ? initialIndex : Math.max(0, messages.length - 1);\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.messages.length === 0) {\n\t\t\tlines.push(theme.fg(\"muted\", \" No user messages found\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.messages.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.messages.length);\n\n\t\t// Render visible messages (2 lines per message + blank line)\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst message = this.messages[i];\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\t// Normalize message to single line\n\t\t\tconst normalizedMessage = message.text.replace(/\\n/g, \" \").trim();\n\n\t\t\t// First line: cursor + message\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\t\t\tconst maxMsgWidth = width - 2; // Account for cursor (2 chars)\n\t\t\tconst truncatedMsg = truncateToWidth(normalizedMessage, maxMsgWidth);\n\t\t\tconst messageLine = cursor + (isSelected ? theme.bold(truncatedMsg) : truncatedMsg);\n\n\t\t\tlines.push(messageLine);\n\n\t\t\t// Second line: position in history plus the first line of the reply\n\t\t\t// that followed (absent when the message never got a reply) so the\n\t\t\t// picker reads as \"which turn am I returning to\", not a bare text list.\n\t\t\tconst position = i + 1;\n\t\t\tconst parts = [`Message ${position} of ${this.messages.length}`];\n\t\t\tif (message.replySummary !== undefined) {\n\t\t\t\tconst maxSummaryWidth = Math.max(10, width - 2 - parts[0].length - 5);\n\t\t\t\tparts.push(`↳ ${truncateToWidth(message.replySummary, maxSummaryWidth)}`);\n\t\t\t}\n\t\t\tconst metadataLine = theme.fg(\"muted\", ` ${parts.join(\" \")}`);\n\t\t\tlines.push(metadataLine);\n\t\t\tlines.push(\"\"); // Blank line between messages\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.messages.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.messages.length})`);\n\t\t\tlines.push(scrollInfo);\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\t// Up arrow - go to previous (older) message, wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.messages.length - 1 : this.selectedIndex - 1;\n\t\t}\n\t\t// Down arrow - go to next (newer) message, wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === this.messages.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t}\n\t\t// Enter - select message and branch\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selected = this.messages[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.id);\n\t\t\t}\n\t\t}\n\t\t// Escape - cancel\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Component that renders a user message selector for branching\n */\nexport class UserMessageSelectorComponent extends Container {\n\tprivate messageList: UserMessageList;\n\n\tconstructor(\n\t\tmessages: UserMessageItem[],\n\t\tonSelect: (entryId: string) => void,\n\t\tonCancel: () => void,\n\t\tinitialSelectedId?: string,\n\t) {\n\t\tsuper();\n\n\t\t// Add header\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.bold(\"Fork from Message\"), 1, 0));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", \"Select a user message to copy the active path up to that point into a new session\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create message list\n\t\tthis.messageList = new UserMessageList(messages, initialSelectedId);\n\t\tthis.messageList.onSelect = onSelect;\n\t\tthis.messageList.onCancel = onCancel;\n\n\t\tthis.addChild(this.messageList);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Auto-cancel if no messages\n\t\tif (messages.length === 0) {\n\t\t\tsetTimeout(() => onCancel(), 100);\n\t\t}\n\t}\n\n\tgetMessageList(): UserMessageList {\n\t\treturn this.messageList;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"user-message-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/user-message-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, Container, getKeybindings, Spacer, Text, truncateToWidth } from \"@agent-forge/tui\";\nimport { t } from \"../../../core/i18n/index.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\ninterface UserMessageItem {\n\tid: string; // Entry ID in the session\n\ttext: string; // The message text\n\ttimestamp?: string; // Optional timestamp if available\n\t/** First line of the reply that followed this message; conversational\n\t * context for recognizing \"which prompt am I returning to\". */\n\treplySummary?: string;\n}\n\n/**\n * Custom user message list component with selection\n */\nclass UserMessageList implements Component {\n\tprivate messages: UserMessageItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tpublic onSelect?: (entryId: string) => void;\n\tpublic onCancel?: () => void;\n\tprivate maxVisible: number = 10; // Max messages visible\n\n\tconstructor(messages: UserMessageItem[], initialSelectedId?: string) {\n\t\t// Store messages in chronological order (oldest to newest)\n\t\tthis.messages = messages;\n\t\tconst initialIndex = initialSelectedId ? messages.findIndex((message) => message.id === initialSelectedId) : -1;\n\t\t// Start with selected message if provided, else default to the most recent\n\t\tthis.selectedIndex = initialIndex >= 0 ? initialIndex : Math.max(0, messages.length - 1);\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.messages.length === 0) {\n\t\t\tlines.push(theme.fg(\"muted\", ` ${t(\"No user messages found\")}`));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.messages.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.messages.length);\n\n\t\t// Render visible messages (2 lines per message + blank line)\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst message = this.messages[i];\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\t// Normalize message to single line\n\t\t\tconst normalizedMessage = message.text.replace(/\\n/g, \" \").trim();\n\n\t\t\t// First line: cursor + message\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\t\t\tconst maxMsgWidth = width - 2; // Account for cursor (2 chars)\n\t\t\tconst truncatedMsg = truncateToWidth(normalizedMessage, maxMsgWidth);\n\t\t\tconst messageLine = cursor + (isSelected ? theme.bold(truncatedMsg) : truncatedMsg);\n\n\t\t\tlines.push(messageLine);\n\n\t\t\t// Second line: position in history plus the first line of the reply\n\t\t\t// that followed (absent when the message never got a reply) so the\n\t\t\t// picker reads as \"which turn am I returning to\", not a bare text list.\n\t\t\tconst position = i + 1;\n\t\t\tconst parts = [t(\"Message {position} of {total}\", { position, total: this.messages.length })];\n\t\t\tif (message.replySummary !== undefined) {\n\t\t\t\tconst maxSummaryWidth = Math.max(10, width - 2 - parts[0].length - 5);\n\t\t\t\tparts.push(`↳ ${truncateToWidth(message.replySummary, maxSummaryWidth)}`);\n\t\t\t}\n\t\t\tconst metadataLine = theme.fg(\"muted\", ` ${parts.join(\" \")}`);\n\t\t\tlines.push(metadataLine);\n\t\t\tlines.push(\"\"); // Blank line between messages\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.messages.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.messages.length})`);\n\t\t\tlines.push(scrollInfo);\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\t// Up arrow - go to previous (older) message, wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.messages.length - 1 : this.selectedIndex - 1;\n\t\t}\n\t\t// Down arrow - go to next (newer) message, wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === this.messages.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t}\n\t\t// Enter - select message and branch\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selected = this.messages[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.id);\n\t\t\t}\n\t\t}\n\t\t// Escape - cancel\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Component that renders a user message selector for branching\n */\nexport class UserMessageSelectorComponent extends Container {\n\tprivate messageList: UserMessageList;\n\n\tconstructor(\n\t\tmessages: UserMessageItem[],\n\t\tonSelect: (entryId: string) => void,\n\t\tonCancel: () => void,\n\t\tinitialSelectedId?: string,\n\t) {\n\t\tsuper();\n\n\t\t// Add header\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.bold(t(\"Fork from Message\")), 1, 0));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", t(\"Select a user message to copy the active path up to that point into a new session\")),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create message list\n\t\tthis.messageList = new UserMessageList(messages, initialSelectedId);\n\t\tthis.messageList.onSelect = onSelect;\n\t\tthis.messageList.onCancel = onCancel;\n\n\t\tthis.addChild(this.messageList);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Auto-cancel if no messages\n\t\tif (messages.length === 0) {\n\t\t\tsetTimeout(() => onCancel(), 100);\n\t\t}\n\t}\n\n\tgetMessageList(): UserMessageList {\n\t\treturn this.messageList;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-view.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/workflow-view.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Workflow overlay view (§9 L2/L3 of docs/design/工作流编排插件设计.md).\n *\n * Read-only panorama (L2) and transition stream (L3) rendered from the\n * session's `workflow.state` / `workflow.transition` custom entries — the\n * same isomorphic read the footer performs on `goal.state`, so the plugin\n * does not need to be present and this module never imports plugin code.\n * Waiting-condition labels mirror the engine's `computeWaitingOn` order\n * (active-limit precedes write-scope conflict); plan lookups read the latest\n * `plan.state` snapshot the same way.\n *\n * Operations are never executed here: approve/reject/skip/cancel forward an\n * action text (e.g. `approve stage s2-review`) to the conversation input and\n * the main agent drives it through workflow_advance — rendering and execution\n * stay separated (设计 §9 L2 \"转发到对话\").\n */\n\nimport { type Component, Container, getKeybindings, Text, truncateToWidth } from \"@agent-forge/tui\";\nimport type { SessionEntry } from \"../../../core/session-manager.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\n// ---------------------------------------------------------------------------\n// View model (pure; exported for tests)\n// ---------------------------------------------------------------------------\n\ntype StageStatus = \"pending\" | \"active\" | \"awaiting_review\" | \"completed\" | \"failed\" | \"skipped\";\n\nconst STAGE_STATUSES: readonly string[] = [\"pending\", \"active\", \"awaiting_review\", \"completed\", \"failed\", \"skipped\"];\n\n/** One selectable stage row of the L2 panorama tree. */\nexport interface WorkflowStageRow {\n\tworkflowId: string;\n\tstageId: string;\n\ttitle: string;\n\tdepth: number;\n\tstatus: StageStatus;\n\t/** §8.1 waiting-condition label, or null when nothing blocks the stage. */\n\twaitingOn: string | null;\n\tattempts: number;\n\tevidenceCount: number;\n\t/** Conflicting active write stage id (write-scope marker), or null. */\n\tscopeConflict: string | null;\n\t/** Diamond edge collapsed to a back-reference instead of a duplicate subtree. */\n\tbackReference: boolean;\n}\n\n/** One workflow block of the L2 panorama. */\nexport interface WorkflowBlock {\n\tworkflowId: string;\n\ttitle: string;\n\tstatus: string;\n\trevision: number;\n\tgoalId: string | undefined;\n\tactiveCount: number;\n\tmaxActiveStages: number;\n\tretreatCount: number;\n\tmaxRetreats: number;\n\tdiagnostics: string[];\n\tstages: WorkflowStageRow[];\n}\n\n/** One L3 transition row (already newest-first, capped by the builder). */\nexport interface WorkflowTransitionRow {\n\trevision: number;\n\tworkflowId: string;\n\tstageId: string | undefined;\n\tfrom: string;\n\tto: string;\n\ttrigger: string;\n\tcode: string;\n\tmessage: string;\n\tdetail: string | undefined;\n}\n\nexport interface WorkflowViewModel {\n\t/** Undefined when the session has no `workflow.state` entry at all. */\n\tpanorama: WorkflowBlock[] | undefined;\n\ttransitions: WorkflowTransitionRow[];\n\t/** Newest non-terminal workflow id (footer's pick), for workflow-level actions. */\n\tactiveWorkflowId: string | undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringOrUndefined(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\ninterface ParsedStageSpec {\n\tid: string;\n\ttitle: string;\n\tdependsOn: string[];\n\tplanId: string | undefined;\n\tkind: \"read\" | \"write\";\n\tscope: string[] | undefined;\n\tacceptanceIds: string[];\n}\n\ninterface ParsedStageRuntime {\n\tstageId: string;\n\tstatus: StageStatus;\n\tattempts: number;\n\tevidenceCount: number;\n\tcoveredAcceptanceIds: Set<string>;\n}\n\ninterface ParsedWorkflow {\n\tid: string;\n\ttitle: string;\n\tstatus: string;\n\trevision: number;\n\tgoalId: string | undefined;\n\tmaxActiveStages: number;\n\tmaxRetreats: number;\n\tretreatCount: number;\n\tdiagnostics: string[];\n\tstages: ParsedStageSpec[];\n\truntime: Map<string, ParsedStageRuntime>;\n}\n\nfunction parseStageSpec(value: unknown): ParsedStageSpec | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst id = stringOrUndefined(value.id);\n\tif (id === undefined) return undefined;\n\tconst dependsOn = Array.isArray(value.dependsOn) ? value.dependsOn.filter(isString) : [];\n\tconst acceptance =\n\t\tisRecord(value.objective) && Array.isArray(value.objective.acceptance) ? value.objective.acceptance : [];\n\tconst scope = Array.isArray(value.scope) ? value.scope.filter(isString) : undefined;\n\treturn {\n\t\tid,\n\t\ttitle: stringOrUndefined(value.title) ?? id,\n\t\tdependsOn,\n\t\tplanId: stringOrUndefined(value.planId),\n\t\tkind: value.kind === \"read\" ? \"read\" : \"write\",\n\t\tscope: scope !== undefined && scope.length > 0 ? scope : undefined,\n\t\tacceptanceIds: acceptance\n\t\t\t.map((item) => (isRecord(item) ? stringOrUndefined(item.id) : undefined))\n\t\t\t.filter((item): item is string => item !== undefined),\n\t};\n}\n\nfunction isString(value: unknown): value is string {\n\treturn typeof value === \"string\";\n}\n\nfunction parseStageRuntime(value: unknown): ParsedStageRuntime | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst stageId = stringOrUndefined(value.stageId);\n\tconst status = stringOrUndefined(value.status);\n\tif (stageId === undefined || status === undefined || !STAGE_STATUSES.includes(status)) return undefined;\n\tconst evidence = Array.isArray(value.evidence) ? value.evidence : [];\n\tconst covered = new Set<string>();\n\tlet evidenceCount = 0;\n\tfor (const item of evidence) {\n\t\tif (!isRecord(item)) continue;\n\t\tevidenceCount++;\n\t\tconst acceptanceId = stringOrUndefined(item.acceptanceId);\n\t\tif (acceptanceId !== undefined) covered.add(acceptanceId);\n\t}\n\treturn {\n\t\tstageId,\n\t\tstatus: status as StageStatus,\n\t\tattempts: typeof value.attempts === \"number\" ? value.attempts : 0,\n\t\tevidenceCount,\n\t\tcoveredAcceptanceIds: covered,\n\t};\n}\n\nfunction parseWorkflowRecord(value: unknown): ParsedWorkflow | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst id = stringOrUndefined(value.id);\n\tconst title = stringOrUndefined(value.title);\n\tconst status = stringOrUndefined(value.status);\n\tif (id === undefined || title === undefined || status === undefined) return undefined;\n\tconst stages: ParsedStageSpec[] = [];\n\tfor (const item of Array.isArray(value.stages) ? value.stages : []) {\n\t\tconst spec = parseStageSpec(item);\n\t\tif (spec !== undefined) stages.push(spec);\n\t}\n\tconst runtime = new Map<string, ParsedStageRuntime>();\n\tfor (const item of Array.isArray(value.runtime) ? value.runtime : []) {\n\t\tconst parsed = parseStageRuntime(item);\n\t\tif (parsed !== undefined) runtime.set(parsed.stageId, parsed);\n\t}\n\tconst diagnostics = Array.isArray(value.diagnostics) ? value.diagnostics.filter(isString) : [];\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tstatus,\n\t\trevision: typeof value.revision === \"number\" ? value.revision : 0,\n\t\tgoalId: stringOrUndefined(value.goalId),\n\t\tmaxActiveStages: typeof value.maxActiveStages === \"number\" ? value.maxActiveStages : 4,\n\t\tmaxRetreats: typeof value.maxRetreats === \"number\" ? value.maxRetreats : 3,\n\t\tretreatCount: typeof value.retreatCount === \"number\" ? value.retreatCount : 0,\n\t\tdiagnostics,\n\t\tstages,\n\t\truntime,\n\t};\n}\n\n/** Latest `plan.state` snapshot reduced to id -> status (plan-dangling / plan-incomplete labels). */\nfunction parsePlanStatuses(data: unknown): Map<string, string> {\n\tconst statuses = new Map<string, string>();\n\tif (!isRecord(data) || !Array.isArray(data.plans)) return statuses;\n\tfor (const item of data.plans) {\n\t\tif (!isRecord(item)) continue;\n\t\tconst id = stringOrUndefined(item.id);\n\t\tconst status = stringOrUndefined(item.status);\n\t\tif (id !== undefined && status !== undefined) statuses.set(id, status);\n\t}\n\treturn statuses;\n}\n\nfunction parseTransition(value: unknown): WorkflowTransitionRow | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst workflowId = stringOrUndefined(value.workflowId);\n\tconst from = stringOrUndefined(value.from);\n\tconst to = stringOrUndefined(value.to);\n\tconst reason = isRecord(value.reason) ? value.reason : undefined;\n\tconst code = reason !== undefined ? stringOrUndefined(reason.code) : undefined;\n\tif (\n\t\tworkflowId === undefined ||\n\t\tfrom === undefined ||\n\t\tto === undefined ||\n\t\tcode === undefined ||\n\t\ttypeof value.revision !== \"number\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\trevision: value.revision,\n\t\tworkflowId,\n\t\tstageId: stringOrUndefined(value.stageId),\n\t\tfrom,\n\t\tto,\n\t\ttrigger: stringOrUndefined(value.trigger) ?? \"tool\",\n\t\tcode,\n\t\tmessage: reason !== undefined ? (stringOrUndefined(reason.message) ?? \"\") : \"\",\n\t\tdetail: reason !== undefined ? stringOrUndefined(reason.detail) : undefined,\n\t};\n}\n\nfunction scopesIntersect(a: string[] | undefined, b: string[] | undefined): boolean {\n\tif (a === undefined || a.length === 0 || b === undefined || b.length === 0) return true;\n\treturn a.some((tag) => b.includes(tag));\n}\n\nfunction findScopeConflict(workflow: ParsedWorkflow, spec: ParsedStageSpec): string | null {\n\tif (spec.kind !== \"write\") return null;\n\tfor (const other of workflow.stages) {\n\t\tif (other.id === spec.id || other.kind !== \"write\") continue;\n\t\tif (workflow.runtime.get(other.id)?.status !== \"active\") continue;\n\t\tif (scopesIntersect(spec.scope, other.scope)) return other.id;\n\t}\n\treturn null;\n}\n\nfunction uncoveredAcceptanceIds(spec: ParsedStageSpec, runtime: ParsedStageRuntime | undefined): string[] {\n\tif (runtime === undefined) return [...spec.acceptanceIds];\n\treturn spec.acceptanceIds.filter((id) => !runtime.coveredAcceptanceIds.has(id));\n}\n\n/**\n * §8.1 waiting-condition label, mirroring the engine's `computeWaitingOn`\n * check order (active-limit precedes write-scope conflict) so the overlay\n * names the same condition the audit stream recorded. Plan lookups use the\n * latest `plan.state` snapshot instead of the engine's live dependency.\n */\nfunction computeWaitingOn(\n\tworkflow: ParsedWorkflow,\n\tspec: ParsedStageSpec,\n\truntime: ParsedStageRuntime | undefined,\n\tplanStatuses: Map<string, string>,\n): string | null {\n\tconst status = runtime?.status ?? \"pending\";\n\tif (status === \"awaiting_review\") return \"review\";\n\tif (status === \"pending\") {\n\t\tif (workflow.status === \"draft\") return \"not-yet-active\";\n\t\tif ((runtime?.attempts ?? 0) > 0) {\n\t\t\tif (spec.acceptanceIds.length === 0) return \"retry-wait\";\n\t\t\treturn `acceptance-missing:[${uncoveredAcceptanceIds(spec, runtime).join(\", \")}]`;\n\t\t}\n\t\tconst incomplete = spec.dependsOn.filter((dep) => {\n\t\t\tconst dependency = workflow.runtime.get(dep)?.status;\n\t\t\treturn dependency !== \"completed\" && dependency !== \"skipped\";\n\t\t});\n\t\tif (incomplete.length > 0) return `deps-incomplete:[${incomplete.join(\", \")}]`;\n\t\tif (spec.planId !== undefined && !planStatuses.has(spec.planId)) return \"plan-dangling\";\n\t\tconst activeCount = [...workflow.runtime.values()].filter((item) => item.status === \"active\").length;\n\t\tif (activeCount >= workflow.maxActiveStages) return \"active-limit\";\n\t\tconst conflict = findScopeConflict(workflow, spec);\n\t\tif (conflict !== null) return `write-scope-conflict:[${conflict}]`;\n\t\treturn \"not-yet-active\";\n\t}\n\tif (status === \"active\") {\n\t\tif (spec.planId === undefined) return null;\n\t\tconst planStatus = planStatuses.get(spec.planId);\n\t\tif (planStatus === undefined) return \"plan-dangling\";\n\t\tif (planStatus !== \"completed\") return \"plan-incomplete\";\n\t\treturn null;\n\t}\n\treturn null;\n}\n\nfunction buildStageRows(workflow: ParsedWorkflow, planStatuses: Map<string, string>): WorkflowStageRow[] {\n\tconst rows: WorkflowStageRow[] = [];\n\tconst visited = new Set<string>();\n\tconst walk = (spec: ParsedStageSpec, depth: number): void => {\n\t\tif (visited.has(spec.id)) {\n\t\t\trows.push({\n\t\t\t\tworkflowId: workflow.id,\n\t\t\t\tstageId: spec.id,\n\t\t\t\ttitle: spec.title,\n\t\t\t\tdepth,\n\t\t\t\tstatus: workflow.runtime.get(spec.id)?.status ?? \"pending\",\n\t\t\t\twaitingOn: null,\n\t\t\t\tattempts: 0,\n\t\t\t\tevidenceCount: 0,\n\t\t\t\tscopeConflict: null,\n\t\t\t\tbackReference: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tvisited.add(spec.id);\n\t\tconst runtime = workflow.runtime.get(spec.id);\n\t\trows.push({\n\t\t\tworkflowId: workflow.id,\n\t\t\tstageId: spec.id,\n\t\t\ttitle: spec.title,\n\t\t\tdepth,\n\t\t\tstatus: runtime?.status ?? \"pending\",\n\t\t\twaitingOn: computeWaitingOn(workflow, spec, runtime, planStatuses),\n\t\t\tattempts: runtime?.attempts ?? 0,\n\t\t\tevidenceCount: runtime?.evidenceCount ?? 0,\n\t\t\tscopeConflict: findScopeConflict(workflow, spec),\n\t\t\tbackReference: false,\n\t\t});\n\t\tfor (const child of workflow.stages.filter((item) => item.dependsOn.includes(spec.id))) {\n\t\t\twalk(child, depth + 1);\n\t\t}\n\t};\n\tfor (const root of workflow.stages.filter((item) => item.dependsOn.length === 0)) {\n\t\twalk(root, 0);\n\t}\n\t// Stages unreachable from a root (defensive; the engine validates acyclic\n\t// graphs) still render at the top level, mirroring the L1 text panorama.\n\tfor (const spec of workflow.stages) {\n\t\tif (!visited.has(spec.id)) walk(spec, 0);\n\t}\n\treturn rows;\n}\n\n/**\n * Build the L2/L3 view model from session entries: the latest\n * `workflow.state` snapshot plus the `workflow.transition` audit stream\n * (newest first, capped). Malformed records are skipped, never thrown.\n */\nexport function buildWorkflowViewModel(entries: readonly SessionEntry[], transitionLimit = 30): WorkflowViewModel {\n\tlet stateData: unknown;\n\tlet hasStateEntry = false;\n\tlet planData: unknown;\n\tconst transitions: WorkflowTransitionRow[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"custom\") continue;\n\t\tif (entry.customType === \"workflow.state\") {\n\t\t\thasStateEntry = true;\n\t\t\tstateData = entry.data;\n\t\t} else if (entry.customType === \"plan.state\") {\n\t\t\tplanData = entry.data;\n\t\t} else if (entry.customType === \"workflow.transition\") {\n\t\t\tconst parsed = parseTransition(entry.data);\n\t\t\tif (parsed !== undefined) transitions.push(parsed);\n\t\t}\n\t}\n\n\tconst planStatuses = parsePlanStatuses(planData);\n\tlet blocks: WorkflowBlock[] | undefined;\n\tif (hasStateEntry) {\n\t\tconst workflows = isRecord(stateData) && Array.isArray(stateData.workflows) ? stateData.workflows : [];\n\t\tblocks = workflows\n\t\t\t.map((item) => {\n\t\t\t\tconst parsed = parseWorkflowRecord(item);\n\t\t\t\tif (parsed === undefined) return undefined;\n\t\t\t\treturn {\n\t\t\t\t\tworkflowId: parsed.id,\n\t\t\t\t\ttitle: parsed.title,\n\t\t\t\t\tstatus: parsed.status,\n\t\t\t\t\trevision: parsed.revision,\n\t\t\t\t\tgoalId: parsed.goalId,\n\t\t\t\t\tactiveCount: [...parsed.runtime.values()].filter((item) => item.status === \"active\").length,\n\t\t\t\t\tmaxActiveStages: parsed.maxActiveStages,\n\t\t\t\t\tretreatCount: parsed.retreatCount,\n\t\t\t\t\tmaxRetreats: parsed.maxRetreats,\n\t\t\t\t\tdiagnostics: [...parsed.diagnostics],\n\t\t\t\t\tstages: buildStageRows(parsed, planStatuses),\n\t\t\t\t} satisfies WorkflowBlock;\n\t\t\t})\n\t\t\t.filter((block): block is WorkflowBlock => block !== undefined);\n\t}\n\n\tconst activeRecord = blocks?.filter((block) => block.status === \"draft\" || block.status === \"running\").at(-1);\n\tconst activeWorkflowId = activeRecord?.workflowId ?? blocks?.at(-1)?.workflowId;\n\n\treturn {\n\t\tpanorama: blocks,\n\t\ttransitions: transitions.slice(-transitionLimit).reverse(),\n\t\tactiveWorkflowId,\n\t};\n}\n\n/** Forwarded action vocabulary (§9 L2): rendered here, executed via workflow_advance by the main agent. */\nexport type WorkflowForwardAction = \"approve\" | \"reject\" | \"skip\" | \"cancel\";\n\n/** Action text injected into the conversation input, e.g. `approve stage s2-review`. */\nexport function buildWorkflowActionText(\n\taction: WorkflowForwardAction,\n\tworkflowId: string,\n\tstageId: string | undefined,\n): string {\n\tif (action === \"cancel\" || stageId === undefined) return `cancel workflow ${workflowId}`;\n\treturn `${action} stage ${stageId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Component (L2 panorama + L3 log)\n// ---------------------------------------------------------------------------\n\n/** Selectable row shape used by the component; stage rows carry action targets. */\ninterface ViewRow {\n\ttext: string;\n\tstage: { workflowId: string; stageId: string } | undefined;\n}\n\nfunction stageStatusColor(status: StageStatus): (text: string) => string {\n\tswitch (status) {\n\t\tcase \"active\":\n\t\t\treturn (text) => theme.fg(\"accent\", text);\n\t\tcase \"awaiting_review\":\n\t\t\treturn (text) => theme.fg(\"warning\", text);\n\t\tcase \"completed\":\n\t\t\treturn (text) => theme.fg(\"success\", text);\n\t\tcase \"failed\":\n\t\t\treturn (text) => theme.fg(\"error\", text);\n\t\tcase \"skipped\":\n\t\t\treturn (text) => theme.fg(\"dim\", text);\n\t\tcase \"pending\":\n\t\t\treturn (text) => theme.fg(\"muted\", text);\n\t}\n}\n\nfunction workflowStatusColor(status: string): (text: string) => string {\n\tif (status === \"running\") return (text) => theme.fg(\"accent\", text);\n\tif (status === \"completed\") return (text) => theme.fg(\"success\", text);\n\tif (status === \"failed\") return (text) => theme.fg(\"error\", text);\n\treturn (text) => theme.fg(\"dim\", text);\n}\n\nfunction transitionCodeColor(code: string): (text: string) => string {\n\tif (code.includes(\"failed\") || code.includes(\"retreat\") || code.includes(\"error\") || code.includes(\"limit\")) {\n\t\treturn (text) => theme.fg(\"error\", text);\n\t}\n\tif (code.includes(\"held\") || code.includes(\"retry\") || code.includes(\"amended\")) {\n\t\treturn (text) => theme.fg(\"warning\", text);\n\t}\n\treturn (text) => theme.fg(\"muted\", text);\n}\n\n/** Renders the visible row window; reads live state from the owning component. */\nclass WorkflowRowsView implements Component {\n\tprivate view: WorkflowViewComponent;\n\n\tconstructor(view: WorkflowViewComponent) {\n\t\tthis.view = view;\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\treturn this.view.renderRows(width);\n\t}\n}\n\nexport interface WorkflowViewConfig {\n\t/** Entries provider; rescanned when the array identity or length changes (append-only sessions). */\n\tgetEntries: () => readonly SessionEntry[];\n\t/** Maximum visible rows before the list scrolls. */\n\tmaxRows: number;\n\t/** Request a TUI re-render (used for transient status flashes). */\n\trequestRender?: () => void;\n}\n\nexport class WorkflowViewComponent extends Container {\n\tpublic onClose?: () => void;\n\tpublic onForwardAction?: (text: string) => void;\n\n\tprivate config: WorkflowViewConfig;\n\tprivate mode: \"panorama\" | \"log\" = \"panorama\";\n\tprivate rows: ViewRow[] = [];\n\tprivate selectedIndex = 0;\n\tprivate offset = 0;\n\tprivate titleText: Text;\n\tprivate modeHintText: Text;\n\tprivate helpText: Text;\n\tprivate flashText: Text;\n\tprivate cache: { entries: readonly SessionEntry[]; length: number; model: WorkflowViewModel } | undefined;\n\tprivate flashTimeout: ReturnType<typeof setTimeout> | null = null;\n\n\tconstructor(config: WorkflowViewConfig) {\n\t\tsuper();\n\n\t\tthis.config = config;\n\t\tthis.titleText = new Text(theme.fg(\"accent\", theme.bold(\" Workflow Overview\")), 1, 0);\n\t\tthis.modeHintText = new Text(\"\", 1, 0);\n\t\tthis.helpText = new Text(\"\", 1, 0);\n\t\tthis.flashText = new Text(\"\", 1, 0);\n\t\tthis.updateModeTexts();\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(this.modeHintText);\n\t\tthis.addChild(new WorkflowRowsView(this));\n\t\tthis.addChild(this.flashText);\n\t\tthis.addChild(this.helpText);\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tdispose(): void {\n\t\tif (this.flashTimeout !== null) {\n\t\t\tclearTimeout(this.flashTimeout);\n\t\t\tthis.flashTimeout = null;\n\t\t}\n\t}\n\n\tprivate updateModeTexts(): void {\n\t\tconst label = this.mode === \"panorama\" ? \"panorama\" : \"transition log (newest first)\";\n\t\tthis.modeHintText.setText(theme.fg(\"dim\", ` ${label}`));\n\t\tthis.helpText.setText(this.buildHelpLine());\n\t}\n\n\tprivate buildHelpLine(): string {\n\t\tconst parts = [rawKeyHint(\"↑↓\", \"move\")];\n\t\tif (this.mode === \"panorama\") {\n\t\t\tparts.push(\n\t\t\t\trawKeyHint(\"tab/l\", \"log\"),\n\t\t\t\trawKeyHint(\"a\", \"approve\"),\n\t\t\t\trawKeyHint(\"r\", \"reject\"),\n\t\t\t\trawKeyHint(\"s\", \"skip\"),\n\t\t\t\trawKeyHint(\"c\", \"cancel\"),\n\t\t\t);\n\t\t} else {\n\t\t\tparts.push(rawKeyHint(\"tab/l\", \"panorama\"));\n\t\t}\n\t\tparts.push(keyHint(\"tui.select.cancel\", \"close\"));\n\t\treturn ` ${parts.join(\" \")}`;\n\t}\n\n\tprivate getModel(): WorkflowViewModel {\n\t\tconst entries = this.config.getEntries();\n\t\tif (this.cache && this.cache.entries === entries && this.cache.length === entries.length) {\n\t\t\treturn this.cache.model;\n\t\t}\n\t\tconst model = buildWorkflowViewModel(entries);\n\t\tthis.cache = { entries, length: entries.length, model };\n\t\treturn model;\n\t}\n\n\tprivate buildRows(model: WorkflowViewModel): ViewRow[] {\n\t\tconst rows: ViewRow[] = [];\n\t\tif (this.mode === \"log\") {\n\t\t\tif (model.transitions.length === 0) {\n\t\t\t\trows.push({ text: \" no workflow transitions recorded\", stage: undefined });\n\t\t\t\treturn rows;\n\t\t\t}\n\t\t\tfor (const transition of model.transitions) {\n\t\t\t\tconst stage = transition.stageId !== undefined ? `/${transition.stageId}` : \"\";\n\t\t\t\tconst detail = transition.detail !== undefined ? ` — ${transition.detail}` : \"\";\n\t\t\t\tconst code = transitionCodeColor(transition.code)(transition.code);\n\t\t\t\trows.push({\n\t\t\t\t\ttext: ` #${transition.revision} ${transition.workflowId}${stage} ${transition.from} -> ${transition.to} [${transition.trigger}] ${code}: ${transition.message}${detail}`,\n\t\t\t\t\tstage: undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn rows;\n\t\t}\n\n\t\tconst blocks = model.panorama;\n\t\tif (blocks === undefined) {\n\t\t\trows.push({ text: \" no workflow.state entry in this session\", stage: undefined });\n\t\t\treturn rows;\n\t\t}\n\t\tif (blocks.length === 0) {\n\t\t\trows.push({ text: \" no workflows in this session; create one with workflow_set\", stage: undefined });\n\t\t\treturn rows;\n\t\t}\n\t\tfor (const block of blocks) {\n\t\t\tconst status = workflowStatusColor(block.status)(block.status);\n\t\t\trows.push({\n\t\t\t\ttext: ` workflow \"${block.title}\" (${block.workflowId}) — ${status} · revision ${block.revision} · active ${block.activeCount}/${block.maxActiveStages} · retreats ${block.retreatCount}/${block.maxRetreats}`,\n\t\t\t\tstage: undefined,\n\t\t\t});\n\t\t\tif (block.goalId !== undefined) {\n\t\t\t\trows.push({ text: ` goal: ${block.goalId}`, stage: undefined });\n\t\t\t}\n\t\t\tfor (const stage of block.stages) {\n\t\t\t\trows.push({\n\t\t\t\t\ttext: this.formatStageRow(stage),\n\t\t\t\t\tstage: { workflowId: block.workflowId, stageId: stage.stageId },\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (block.diagnostics.length > 0) {\n\t\t\t\trows.push({ text: \" diagnostics:\", stage: undefined });\n\t\t\t\tfor (const diagnostic of block.diagnostics) {\n\t\t\t\t\trows.push({ text: theme.fg(\"warning\", ` - ${diagnostic}`), stage: undefined });\n\t\t\t\t}\n\t\t\t}\n\t\t\trows.push({ text: \"\", stage: undefined });\n\t\t}\n\t\treturn rows;\n\t}\n\n\tprivate formatStageRow(stage: WorkflowStageRow): string {\n\t\tconst pad = \" \".repeat(stage.depth + 1);\n\t\tif (stage.backReference) {\n\t\t\treturn `${pad} -> ${stage.stageId} (shown above)`;\n\t\t}\n\t\tconst bits = [`- ${stage.stageId} [${stageStatusColor(stage.status)(stage.status)}] ${stage.title}`];\n\t\tif (stage.waitingOn !== null) bits.push(theme.fg(\"dim\", `— waiting: ${stage.waitingOn}`));\n\t\tif (stage.attempts > 0) bits.push(theme.fg(\"dim\", `· attempts ${stage.attempts}`));\n\t\tif (stage.evidenceCount > 0) bits.push(theme.fg(\"dim\", `· evidence ${stage.evidenceCount}`));\n\t\tif (stage.scopeConflict !== null) {\n\t\t\tbits.push(theme.fg(\"warning\", `⚠ scope:${stage.scopeConflict}`));\n\t\t}\n\t\treturn `${pad}${bits.join(\" \")}`;\n\t}\n\n\t/** Rows are rebuilt lazily on access; the key couples rebuild + selection restore. */\n\tprivate syncRows(): void {\n\t\tconst model = this.getModel();\n\t\tconst previous = this.rows[this.selectedIndex]?.stage;\n\t\tthis.rows = this.buildRows(model);\n\t\tif (previous !== undefined) {\n\t\t\tconst restored = this.rows.findIndex((row) => row.stage?.stageId === previous.stageId);\n\t\t\tif (restored !== -1) this.selectedIndex = restored;\n\t\t}\n\t\tif (this.selectedIndex >= this.rows.length) this.selectedIndex = Math.max(0, this.rows.length - 1);\n\t\tthis.clampOffset();\n\t}\n\n\tprivate clampOffset(): void {\n\t\tconst maxRows = Math.max(1, this.config.maxRows);\n\t\tif (this.selectedIndex < this.offset) this.offset = this.selectedIndex;\n\t\tif (this.selectedIndex >= this.offset + maxRows) this.offset = this.selectedIndex - maxRows + 1;\n\t\tif (this.offset < 0) this.offset = 0;\n\t}\n\n\trenderRows(width: number): string[] {\n\t\tthis.syncRows();\n\t\tconst maxRows = Math.max(1, this.config.maxRows);\n\t\tconst lines: string[] = [];\n\t\tfor (let index = this.offset; index < Math.min(this.rows.length, this.offset + maxRows); index++) {\n\t\t\tconst row = this.rows[index];\n\t\t\tif (row === undefined) continue;\n\t\t\tconst selected = index === this.selectedIndex;\n\t\t\tconst prefix = selected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tlines.push(truncateToWidth(`${prefix}${row.text}`, width));\n\t\t}\n\t\tconst hiddenAbove = this.offset;\n\t\tconst hiddenBelow = this.rows.length - this.offset - maxRows;\n\t\tconst hidden: string[] = [];\n\t\tif (hiddenAbove > 0) hidden.push(`↑${hiddenAbove}`);\n\t\tif (hiddenBelow > 0) hidden.push(`↓${hiddenBelow}`);\n\t\t// Fixed output height (exactly maxRows content rows plus this final\n\t\t// indicator row), the same contract as agents-view renderRows: the\n\t\t// overlay's grid footprint must not change with mode switches or row\n\t\t// counts, or the chat/editor layout boundary moves between frames.\n\t\twhile (lines.length < maxRows) lines.push(truncateToWidth(\" \", width));\n\t\tlines.push(truncateToWidth(hidden.length > 0 ? theme.fg(\"dim\", ` ${hidden.join(\" \")}`) : \" \", width));\n\t\treturn lines;\n\t}\n\n\tprivate move(delta: number): void {\n\t\tif (this.rows.length === 0) return;\n\t\tthis.selectedIndex = Math.min(this.rows.length - 1, Math.max(0, this.selectedIndex + delta));\n\t\tthis.clampOffset();\n\t}\n\n\tprivate flash(message: string): void {\n\t\tthis.flashText.setText(theme.fg(\"warning\", ` ${message}`));\n\t\tif (this.flashTimeout !== null) clearTimeout(this.flashTimeout);\n\t\tthis.flashTimeout = setTimeout(() => {\n\t\t\tthis.flashText.setText(\"\");\n\t\t\tthis.flashTimeout = null;\n\t\t\tthis.config.requestRender?.();\n\t\t}, 2500);\n\t\tthis.config.requestRender?.();\n\t}\n\n\tprivate toggleMode(): void {\n\t\tthis.mode = this.mode === \"panorama\" ? \"log\" : \"panorama\";\n\t\tthis.selectedIndex = 0;\n\t\tthis.offset = 0;\n\t\tthis.updateModeTexts();\n\t\tthis.config.requestRender?.();\n\t}\n\n\tprivate forward(action: WorkflowForwardAction): void {\n\t\tconst selected = this.rows[this.selectedIndex];\n\t\tif (selected?.stage === undefined) {\n\t\t\tif (action === \"cancel\") {\n\t\t\t\tconst workflowId = this.getModel().activeWorkflowId;\n\t\t\t\tif (workflowId === undefined) {\n\t\t\t\t\tthis.flash(\"No workflow to cancel\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.onForwardAction?.(buildWorkflowActionText(action, workflowId, undefined));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.flash(\"Select a stage row first\");\n\t\t\treturn;\n\t\t}\n\t\tthis.onForwardAction?.(buildWorkflowActionText(action, selected.stage.workflowId, selected.stage.stageId));\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onClose?.();\n\t\t} else if (kb.matches(keyData, \"tui.input.tab\") || keyData === \"l\") {\n\t\t\tthis.toggleMode();\n\t\t} else if (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.move(-1);\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.move(1);\n\t\t} else if (kb.matches(keyData, \"tui.select.pageUp\")) {\n\t\t\tthis.move(-Math.max(1, this.config.maxRows));\n\t\t} else if (kb.matches(keyData, \"tui.select.pageDown\")) {\n\t\t\tthis.move(Math.max(1, this.config.maxRows));\n\t\t} else if (keyData === \"a\") {\n\t\t\tthis.forward(\"approve\");\n\t\t} else if (keyData === \"r\") {\n\t\t\tthis.forward(\"reject\");\n\t\t} else if (keyData === \"s\") {\n\t\t\tthis.forward(\"skip\");\n\t\t} else if (keyData === \"c\") {\n\t\t\tthis.forward(\"cancel\");\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workflow-view.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/workflow-view.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Workflow overlay view (§9 L2/L3 of docs/design/工作流编排插件设计.md).\n *\n * Read-only panorama (L2) and transition stream (L3) rendered from the\n * session's `workflow.state` / `workflow.transition` custom entries — the\n * same isomorphic read the footer performs on `goal.state`, so the plugin\n * does not need to be present and this module never imports plugin code.\n * Waiting-condition labels mirror the engine's `computeWaitingOn` order\n * (active-limit precedes write-scope conflict); plan lookups read the latest\n * `plan.state` snapshot the same way.\n *\n * Operations are never executed here: approve/reject/skip/cancel forward an\n * action text (e.g. `approve stage s2-review`) to the conversation input and\n * the main agent drives it through workflow_advance — rendering and execution\n * stay separated (设计 §9 L2 \"转发到对话\").\n */\n\nimport { type Component, Container, getKeybindings, Text, truncateToWidth } from \"@agent-forge/tui\";\nimport { t } from \"../../../core/i18n/index.ts\";\nimport type { SessionEntry } from \"../../../core/session-manager.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\n// ---------------------------------------------------------------------------\n// View model (pure; exported for tests)\n// ---------------------------------------------------------------------------\n\ntype StageStatus = \"pending\" | \"active\" | \"awaiting_review\" | \"completed\" | \"failed\" | \"skipped\";\n\nconst STAGE_STATUSES: readonly string[] = [\"pending\", \"active\", \"awaiting_review\", \"completed\", \"failed\", \"skipped\"];\n\n/** One selectable stage row of the L2 panorama tree. */\nexport interface WorkflowStageRow {\n\tworkflowId: string;\n\tstageId: string;\n\ttitle: string;\n\tdepth: number;\n\tstatus: StageStatus;\n\t/** §8.1 waiting-condition label, or null when nothing blocks the stage. */\n\twaitingOn: string | null;\n\tattempts: number;\n\tevidenceCount: number;\n\t/** Conflicting active write stage id (write-scope marker), or null. */\n\tscopeConflict: string | null;\n\t/** Diamond edge collapsed to a back-reference instead of a duplicate subtree. */\n\tbackReference: boolean;\n}\n\n/** One workflow block of the L2 panorama. */\nexport interface WorkflowBlock {\n\tworkflowId: string;\n\ttitle: string;\n\tstatus: string;\n\trevision: number;\n\tgoalId: string | undefined;\n\tactiveCount: number;\n\tmaxActiveStages: number;\n\tretreatCount: number;\n\tmaxRetreats: number;\n\tdiagnostics: string[];\n\tstages: WorkflowStageRow[];\n}\n\n/** One L3 transition row (already newest-first, capped by the builder). */\nexport interface WorkflowTransitionRow {\n\trevision: number;\n\tworkflowId: string;\n\tstageId: string | undefined;\n\tfrom: string;\n\tto: string;\n\ttrigger: string;\n\tcode: string;\n\tmessage: string;\n\tdetail: string | undefined;\n}\n\nexport interface WorkflowViewModel {\n\t/** Undefined when the session has no `workflow.state` entry at all. */\n\tpanorama: WorkflowBlock[] | undefined;\n\ttransitions: WorkflowTransitionRow[];\n\t/** Newest non-terminal workflow id (footer's pick), for workflow-level actions. */\n\tactiveWorkflowId: string | undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringOrUndefined(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\ninterface ParsedStageSpec {\n\tid: string;\n\ttitle: string;\n\tdependsOn: string[];\n\tplanId: string | undefined;\n\tkind: \"read\" | \"write\";\n\tscope: string[] | undefined;\n\tacceptanceIds: string[];\n}\n\ninterface ParsedStageRuntime {\n\tstageId: string;\n\tstatus: StageStatus;\n\tattempts: number;\n\tevidenceCount: number;\n\tcoveredAcceptanceIds: Set<string>;\n}\n\ninterface ParsedWorkflow {\n\tid: string;\n\ttitle: string;\n\tstatus: string;\n\trevision: number;\n\tgoalId: string | undefined;\n\tmaxActiveStages: number;\n\tmaxRetreats: number;\n\tretreatCount: number;\n\tdiagnostics: string[];\n\tstages: ParsedStageSpec[];\n\truntime: Map<string, ParsedStageRuntime>;\n}\n\nfunction parseStageSpec(value: unknown): ParsedStageSpec | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst id = stringOrUndefined(value.id);\n\tif (id === undefined) return undefined;\n\tconst dependsOn = Array.isArray(value.dependsOn) ? value.dependsOn.filter(isString) : [];\n\tconst acceptance =\n\t\tisRecord(value.objective) && Array.isArray(value.objective.acceptance) ? value.objective.acceptance : [];\n\tconst scope = Array.isArray(value.scope) ? value.scope.filter(isString) : undefined;\n\treturn {\n\t\tid,\n\t\ttitle: stringOrUndefined(value.title) ?? id,\n\t\tdependsOn,\n\t\tplanId: stringOrUndefined(value.planId),\n\t\tkind: value.kind === \"read\" ? \"read\" : \"write\",\n\t\tscope: scope !== undefined && scope.length > 0 ? scope : undefined,\n\t\tacceptanceIds: acceptance\n\t\t\t.map((item) => (isRecord(item) ? stringOrUndefined(item.id) : undefined))\n\t\t\t.filter((item): item is string => item !== undefined),\n\t};\n}\n\nfunction isString(value: unknown): value is string {\n\treturn typeof value === \"string\";\n}\n\nfunction parseStageRuntime(value: unknown): ParsedStageRuntime | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst stageId = stringOrUndefined(value.stageId);\n\tconst status = stringOrUndefined(value.status);\n\tif (stageId === undefined || status === undefined || !STAGE_STATUSES.includes(status)) return undefined;\n\tconst evidence = Array.isArray(value.evidence) ? value.evidence : [];\n\tconst covered = new Set<string>();\n\tlet evidenceCount = 0;\n\tfor (const item of evidence) {\n\t\tif (!isRecord(item)) continue;\n\t\tevidenceCount++;\n\t\tconst acceptanceId = stringOrUndefined(item.acceptanceId);\n\t\tif (acceptanceId !== undefined) covered.add(acceptanceId);\n\t}\n\treturn {\n\t\tstageId,\n\t\tstatus: status as StageStatus,\n\t\tattempts: typeof value.attempts === \"number\" ? value.attempts : 0,\n\t\tevidenceCount,\n\t\tcoveredAcceptanceIds: covered,\n\t};\n}\n\nfunction parseWorkflowRecord(value: unknown): ParsedWorkflow | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst id = stringOrUndefined(value.id);\n\tconst title = stringOrUndefined(value.title);\n\tconst status = stringOrUndefined(value.status);\n\tif (id === undefined || title === undefined || status === undefined) return undefined;\n\tconst stages: ParsedStageSpec[] = [];\n\tfor (const item of Array.isArray(value.stages) ? value.stages : []) {\n\t\tconst spec = parseStageSpec(item);\n\t\tif (spec !== undefined) stages.push(spec);\n\t}\n\tconst runtime = new Map<string, ParsedStageRuntime>();\n\tfor (const item of Array.isArray(value.runtime) ? value.runtime : []) {\n\t\tconst parsed = parseStageRuntime(item);\n\t\tif (parsed !== undefined) runtime.set(parsed.stageId, parsed);\n\t}\n\tconst diagnostics = Array.isArray(value.diagnostics) ? value.diagnostics.filter(isString) : [];\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tstatus,\n\t\trevision: typeof value.revision === \"number\" ? value.revision : 0,\n\t\tgoalId: stringOrUndefined(value.goalId),\n\t\tmaxActiveStages: typeof value.maxActiveStages === \"number\" ? value.maxActiveStages : 4,\n\t\tmaxRetreats: typeof value.maxRetreats === \"number\" ? value.maxRetreats : 3,\n\t\tretreatCount: typeof value.retreatCount === \"number\" ? value.retreatCount : 0,\n\t\tdiagnostics,\n\t\tstages,\n\t\truntime,\n\t};\n}\n\n/** Latest `plan.state` snapshot reduced to id -> status (plan-dangling / plan-incomplete labels). */\nfunction parsePlanStatuses(data: unknown): Map<string, string> {\n\tconst statuses = new Map<string, string>();\n\tif (!isRecord(data) || !Array.isArray(data.plans)) return statuses;\n\tfor (const item of data.plans) {\n\t\tif (!isRecord(item)) continue;\n\t\tconst id = stringOrUndefined(item.id);\n\t\tconst status = stringOrUndefined(item.status);\n\t\tif (id !== undefined && status !== undefined) statuses.set(id, status);\n\t}\n\treturn statuses;\n}\n\nfunction parseTransition(value: unknown): WorkflowTransitionRow | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst workflowId = stringOrUndefined(value.workflowId);\n\tconst from = stringOrUndefined(value.from);\n\tconst to = stringOrUndefined(value.to);\n\tconst reason = isRecord(value.reason) ? value.reason : undefined;\n\tconst code = reason !== undefined ? stringOrUndefined(reason.code) : undefined;\n\tif (\n\t\tworkflowId === undefined ||\n\t\tfrom === undefined ||\n\t\tto === undefined ||\n\t\tcode === undefined ||\n\t\ttypeof value.revision !== \"number\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\trevision: value.revision,\n\t\tworkflowId,\n\t\tstageId: stringOrUndefined(value.stageId),\n\t\tfrom,\n\t\tto,\n\t\ttrigger: stringOrUndefined(value.trigger) ?? \"tool\",\n\t\tcode,\n\t\tmessage: reason !== undefined ? (stringOrUndefined(reason.message) ?? \"\") : \"\",\n\t\tdetail: reason !== undefined ? stringOrUndefined(reason.detail) : undefined,\n\t};\n}\n\nfunction scopesIntersect(a: string[] | undefined, b: string[] | undefined): boolean {\n\tif (a === undefined || a.length === 0 || b === undefined || b.length === 0) return true;\n\treturn a.some((tag) => b.includes(tag));\n}\n\nfunction findScopeConflict(workflow: ParsedWorkflow, spec: ParsedStageSpec): string | null {\n\tif (spec.kind !== \"write\") return null;\n\tfor (const other of workflow.stages) {\n\t\tif (other.id === spec.id || other.kind !== \"write\") continue;\n\t\tif (workflow.runtime.get(other.id)?.status !== \"active\") continue;\n\t\tif (scopesIntersect(spec.scope, other.scope)) return other.id;\n\t}\n\treturn null;\n}\n\nfunction uncoveredAcceptanceIds(spec: ParsedStageSpec, runtime: ParsedStageRuntime | undefined): string[] {\n\tif (runtime === undefined) return [...spec.acceptanceIds];\n\treturn spec.acceptanceIds.filter((id) => !runtime.coveredAcceptanceIds.has(id));\n}\n\n/**\n * §8.1 waiting-condition label, mirroring the engine's `computeWaitingOn`\n * check order (active-limit precedes write-scope conflict) so the overlay\n * names the same condition the audit stream recorded. Plan lookups use the\n * latest `plan.state` snapshot instead of the engine's live dependency.\n */\nfunction computeWaitingOn(\n\tworkflow: ParsedWorkflow,\n\tspec: ParsedStageSpec,\n\truntime: ParsedStageRuntime | undefined,\n\tplanStatuses: Map<string, string>,\n): string | null {\n\tconst status = runtime?.status ?? \"pending\";\n\tif (status === \"awaiting_review\") return \"review\";\n\tif (status === \"pending\") {\n\t\tif (workflow.status === \"draft\") return \"not-yet-active\";\n\t\tif ((runtime?.attempts ?? 0) > 0) {\n\t\t\tif (spec.acceptanceIds.length === 0) return \"retry-wait\";\n\t\t\treturn `acceptance-missing:[${uncoveredAcceptanceIds(spec, runtime).join(\", \")}]`;\n\t\t}\n\t\tconst incomplete = spec.dependsOn.filter((dep) => {\n\t\t\tconst dependency = workflow.runtime.get(dep)?.status;\n\t\t\treturn dependency !== \"completed\" && dependency !== \"skipped\";\n\t\t});\n\t\tif (incomplete.length > 0) return `deps-incomplete:[${incomplete.join(\", \")}]`;\n\t\tif (spec.planId !== undefined && !planStatuses.has(spec.planId)) return \"plan-dangling\";\n\t\tconst activeCount = [...workflow.runtime.values()].filter((item) => item.status === \"active\").length;\n\t\tif (activeCount >= workflow.maxActiveStages) return \"active-limit\";\n\t\tconst conflict = findScopeConflict(workflow, spec);\n\t\tif (conflict !== null) return `write-scope-conflict:[${conflict}]`;\n\t\treturn \"not-yet-active\";\n\t}\n\tif (status === \"active\") {\n\t\tif (spec.planId === undefined) return null;\n\t\tconst planStatus = planStatuses.get(spec.planId);\n\t\tif (planStatus === undefined) return \"plan-dangling\";\n\t\tif (planStatus !== \"completed\") return \"plan-incomplete\";\n\t\treturn null;\n\t}\n\treturn null;\n}\n\nfunction buildStageRows(workflow: ParsedWorkflow, planStatuses: Map<string, string>): WorkflowStageRow[] {\n\tconst rows: WorkflowStageRow[] = [];\n\tconst visited = new Set<string>();\n\tconst walk = (spec: ParsedStageSpec, depth: number): void => {\n\t\tif (visited.has(spec.id)) {\n\t\t\trows.push({\n\t\t\t\tworkflowId: workflow.id,\n\t\t\t\tstageId: spec.id,\n\t\t\t\ttitle: spec.title,\n\t\t\t\tdepth,\n\t\t\t\tstatus: workflow.runtime.get(spec.id)?.status ?? \"pending\",\n\t\t\t\twaitingOn: null,\n\t\t\t\tattempts: 0,\n\t\t\t\tevidenceCount: 0,\n\t\t\t\tscopeConflict: null,\n\t\t\t\tbackReference: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tvisited.add(spec.id);\n\t\tconst runtime = workflow.runtime.get(spec.id);\n\t\trows.push({\n\t\t\tworkflowId: workflow.id,\n\t\t\tstageId: spec.id,\n\t\t\ttitle: spec.title,\n\t\t\tdepth,\n\t\t\tstatus: runtime?.status ?? \"pending\",\n\t\t\twaitingOn: computeWaitingOn(workflow, spec, runtime, planStatuses),\n\t\t\tattempts: runtime?.attempts ?? 0,\n\t\t\tevidenceCount: runtime?.evidenceCount ?? 0,\n\t\t\tscopeConflict: findScopeConflict(workflow, spec),\n\t\t\tbackReference: false,\n\t\t});\n\t\tfor (const child of workflow.stages.filter((item) => item.dependsOn.includes(spec.id))) {\n\t\t\twalk(child, depth + 1);\n\t\t}\n\t};\n\tfor (const root of workflow.stages.filter((item) => item.dependsOn.length === 0)) {\n\t\twalk(root, 0);\n\t}\n\t// Stages unreachable from a root (defensive; the engine validates acyclic\n\t// graphs) still render at the top level, mirroring the L1 text panorama.\n\tfor (const spec of workflow.stages) {\n\t\tif (!visited.has(spec.id)) walk(spec, 0);\n\t}\n\treturn rows;\n}\n\n/**\n * Build the L2/L3 view model from session entries: the latest\n * `workflow.state` snapshot plus the `workflow.transition` audit stream\n * (newest first, capped). Malformed records are skipped, never thrown.\n */\nexport function buildWorkflowViewModel(entries: readonly SessionEntry[], transitionLimit = 30): WorkflowViewModel {\n\tlet stateData: unknown;\n\tlet hasStateEntry = false;\n\tlet planData: unknown;\n\tconst transitions: WorkflowTransitionRow[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"custom\") continue;\n\t\tif (entry.customType === \"workflow.state\") {\n\t\t\thasStateEntry = true;\n\t\t\tstateData = entry.data;\n\t\t} else if (entry.customType === \"plan.state\") {\n\t\t\tplanData = entry.data;\n\t\t} else if (entry.customType === \"workflow.transition\") {\n\t\t\tconst parsed = parseTransition(entry.data);\n\t\t\tif (parsed !== undefined) transitions.push(parsed);\n\t\t}\n\t}\n\n\tconst planStatuses = parsePlanStatuses(planData);\n\tlet blocks: WorkflowBlock[] | undefined;\n\tif (hasStateEntry) {\n\t\tconst workflows = isRecord(stateData) && Array.isArray(stateData.workflows) ? stateData.workflows : [];\n\t\tblocks = workflows\n\t\t\t.map((item) => {\n\t\t\t\tconst parsed = parseWorkflowRecord(item);\n\t\t\t\tif (parsed === undefined) return undefined;\n\t\t\t\treturn {\n\t\t\t\t\tworkflowId: parsed.id,\n\t\t\t\t\ttitle: parsed.title,\n\t\t\t\t\tstatus: parsed.status,\n\t\t\t\t\trevision: parsed.revision,\n\t\t\t\t\tgoalId: parsed.goalId,\n\t\t\t\t\tactiveCount: [...parsed.runtime.values()].filter((item) => item.status === \"active\").length,\n\t\t\t\t\tmaxActiveStages: parsed.maxActiveStages,\n\t\t\t\t\tretreatCount: parsed.retreatCount,\n\t\t\t\t\tmaxRetreats: parsed.maxRetreats,\n\t\t\t\t\tdiagnostics: [...parsed.diagnostics],\n\t\t\t\t\tstages: buildStageRows(parsed, planStatuses),\n\t\t\t\t} satisfies WorkflowBlock;\n\t\t\t})\n\t\t\t.filter((block): block is WorkflowBlock => block !== undefined);\n\t}\n\n\tconst activeRecord = blocks?.filter((block) => block.status === \"draft\" || block.status === \"running\").at(-1);\n\tconst activeWorkflowId = activeRecord?.workflowId ?? blocks?.at(-1)?.workflowId;\n\n\treturn {\n\t\tpanorama: blocks,\n\t\ttransitions: transitions.slice(-transitionLimit).reverse(),\n\t\tactiveWorkflowId,\n\t};\n}\n\n/** Forwarded action vocabulary (§9 L2): rendered here, executed via workflow_advance by the main agent. */\nexport type WorkflowForwardAction = \"approve\" | \"reject\" | \"skip\" | \"cancel\";\n\n/** Action text injected into the conversation input, e.g. `approve stage s2-review`. */\nexport function buildWorkflowActionText(\n\taction: WorkflowForwardAction,\n\tworkflowId: string,\n\tstageId: string | undefined,\n): string {\n\tif (action === \"cancel\" || stageId === undefined) return `cancel workflow ${workflowId}`;\n\treturn `${action} stage ${stageId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Component (L2 panorama + L3 log)\n// ---------------------------------------------------------------------------\n\n/** Selectable row shape used by the component; stage rows carry action targets. */\ninterface ViewRow {\n\ttext: string;\n\tstage: { workflowId: string; stageId: string } | undefined;\n}\n\nfunction stageStatusColor(status: StageStatus): (text: string) => string {\n\tswitch (status) {\n\t\tcase \"active\":\n\t\t\treturn (text) => theme.fg(\"accent\", text);\n\t\tcase \"awaiting_review\":\n\t\t\treturn (text) => theme.fg(\"warning\", text);\n\t\tcase \"completed\":\n\t\t\treturn (text) => theme.fg(\"success\", text);\n\t\tcase \"failed\":\n\t\t\treturn (text) => theme.fg(\"error\", text);\n\t\tcase \"skipped\":\n\t\t\treturn (text) => theme.fg(\"dim\", text);\n\t\tcase \"pending\":\n\t\t\treturn (text) => theme.fg(\"muted\", text);\n\t}\n}\n\nfunction workflowStatusColor(status: string): (text: string) => string {\n\tif (status === \"running\") return (text) => theme.fg(\"accent\", text);\n\tif (status === \"completed\") return (text) => theme.fg(\"success\", text);\n\tif (status === \"failed\") return (text) => theme.fg(\"error\", text);\n\treturn (text) => theme.fg(\"dim\", text);\n}\n\nfunction transitionCodeColor(code: string): (text: string) => string {\n\tif (code.includes(\"failed\") || code.includes(\"retreat\") || code.includes(\"error\") || code.includes(\"limit\")) {\n\t\treturn (text) => theme.fg(\"error\", text);\n\t}\n\tif (code.includes(\"held\") || code.includes(\"retry\") || code.includes(\"amended\")) {\n\t\treturn (text) => theme.fg(\"warning\", text);\n\t}\n\treturn (text) => theme.fg(\"muted\", text);\n}\n\n/** Renders the visible row window; reads live state from the owning component. */\nclass WorkflowRowsView implements Component {\n\tprivate view: WorkflowViewComponent;\n\n\tconstructor(view: WorkflowViewComponent) {\n\t\tthis.view = view;\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\treturn this.view.renderRows(width);\n\t}\n}\n\nexport interface WorkflowViewConfig {\n\t/** Entries provider; rescanned when the array identity or length changes (append-only sessions). */\n\tgetEntries: () => readonly SessionEntry[];\n\t/** Maximum visible rows before the list scrolls. */\n\tmaxRows: number;\n\t/** Request a TUI re-render (used for transient status flashes). */\n\trequestRender?: () => void;\n}\n\nexport class WorkflowViewComponent extends Container {\n\tpublic onClose?: () => void;\n\tpublic onForwardAction?: (text: string) => void;\n\n\tprivate config: WorkflowViewConfig;\n\tprivate mode: \"panorama\" | \"log\" = \"panorama\";\n\tprivate rows: ViewRow[] = [];\n\tprivate selectedIndex = 0;\n\tprivate offset = 0;\n\tprivate titleText: Text;\n\tprivate modeHintText: Text;\n\tprivate helpText: Text;\n\tprivate flashText: Text;\n\tprivate cache: { entries: readonly SessionEntry[]; length: number; model: WorkflowViewModel } | undefined;\n\tprivate flashTimeout: ReturnType<typeof setTimeout> | null = null;\n\n\tconstructor(config: WorkflowViewConfig) {\n\t\tsuper();\n\n\t\tthis.config = config;\n\t\tthis.titleText = new Text(theme.fg(\"accent\", theme.bold(` ${t(\"Workflow Overview\")}`)), 1, 0);\n\t\tthis.modeHintText = new Text(\"\", 1, 0);\n\t\tthis.helpText = new Text(\"\", 1, 0);\n\t\tthis.flashText = new Text(\"\", 1, 0);\n\t\tthis.updateModeTexts();\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(this.modeHintText);\n\t\tthis.addChild(new WorkflowRowsView(this));\n\t\tthis.addChild(this.flashText);\n\t\tthis.addChild(this.helpText);\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tdispose(): void {\n\t\tif (this.flashTimeout !== null) {\n\t\t\tclearTimeout(this.flashTimeout);\n\t\t\tthis.flashTimeout = null;\n\t\t}\n\t}\n\n\tprivate updateModeTexts(): void {\n\t\tconst label = this.mode === \"panorama\" ? t(\"panorama\") : t(\"transition log (newest first)\");\n\t\tthis.modeHintText.setText(theme.fg(\"dim\", ` ${label}`));\n\t\tthis.helpText.setText(this.buildHelpLine());\n\t}\n\n\tprivate buildHelpLine(): string {\n\t\tconst parts = [rawKeyHint(\"↑↓\", t(\"move\"))];\n\t\tif (this.mode === \"panorama\") {\n\t\t\tparts.push(\n\t\t\t\trawKeyHint(\"tab/l\", t(\"log\")),\n\t\t\t\trawKeyHint(\"a\", t(\"approve\")),\n\t\t\t\trawKeyHint(\"r\", t(\"reject\")),\n\t\t\t\trawKeyHint(\"s\", t(\"skip\")),\n\t\t\t\trawKeyHint(\"c\", t(\"cancel\")),\n\t\t\t);\n\t\t} else {\n\t\t\tparts.push(rawKeyHint(\"tab/l\", t(\"panorama\")));\n\t\t}\n\t\tparts.push(keyHint(\"tui.select.cancel\", t(\"close\")));\n\t\treturn ` ${parts.join(\" \")}`;\n\t}\n\n\tprivate getModel(): WorkflowViewModel {\n\t\tconst entries = this.config.getEntries();\n\t\tif (this.cache && this.cache.entries === entries && this.cache.length === entries.length) {\n\t\t\treturn this.cache.model;\n\t\t}\n\t\tconst model = buildWorkflowViewModel(entries);\n\t\tthis.cache = { entries, length: entries.length, model };\n\t\treturn model;\n\t}\n\n\tprivate buildRows(model: WorkflowViewModel): ViewRow[] {\n\t\tconst rows: ViewRow[] = [];\n\t\tif (this.mode === \"log\") {\n\t\t\tif (model.transitions.length === 0) {\n\t\t\t\trows.push({ text: ` ${t(\"no workflow transitions recorded\")}`, stage: undefined });\n\t\t\t\treturn rows;\n\t\t\t}\n\t\t\tfor (const transition of model.transitions) {\n\t\t\t\tconst stage = transition.stageId !== undefined ? `/${transition.stageId}` : \"\";\n\t\t\t\tconst detail = transition.detail !== undefined ? ` — ${transition.detail}` : \"\";\n\t\t\t\tconst code = transitionCodeColor(transition.code)(transition.code);\n\t\t\t\trows.push({\n\t\t\t\t\ttext: ` #${transition.revision} ${transition.workflowId}${stage} ${transition.from} -> ${transition.to} [${transition.trigger}] ${code}: ${transition.message}${detail}`,\n\t\t\t\t\tstage: undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn rows;\n\t\t}\n\n\t\tconst blocks = model.panorama;\n\t\tif (blocks === undefined) {\n\t\t\trows.push({ text: ` ${t(\"no workflow.state entry in this session\")}`, stage: undefined });\n\t\t\treturn rows;\n\t\t}\n\t\tif (blocks.length === 0) {\n\t\t\trows.push({\n\t\t\t\ttext: ` ${t(\"no workflows in this session; create one with workflow_set\")}`,\n\t\t\t\tstage: undefined,\n\t\t\t});\n\t\t\treturn rows;\n\t\t}\n\t\tfor (const block of blocks) {\n\t\t\tconst status = workflowStatusColor(block.status)(block.status);\n\t\t\trows.push({\n\t\t\t\ttext: ` ${t(\n\t\t\t\t\t'workflow \"{title}\" ({workflowId}) — {status} · revision {revision} · active {activeCount}/{maxActiveStages} · retreats {retreatCount}/{maxRetreats}',\n\t\t\t\t\t{\n\t\t\t\t\t\ttitle: block.title,\n\t\t\t\t\t\tworkflowId: block.workflowId,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trevision: block.revision,\n\t\t\t\t\t\tactiveCount: block.activeCount,\n\t\t\t\t\t\tmaxActiveStages: block.maxActiveStages,\n\t\t\t\t\t\tretreatCount: block.retreatCount,\n\t\t\t\t\t\tmaxRetreats: block.maxRetreats,\n\t\t\t\t\t},\n\t\t\t\t)}`,\n\t\t\t\tstage: undefined,\n\t\t\t});\n\t\t\tif (block.goalId !== undefined) {\n\t\t\t\trows.push({ text: ` ${t(\"goal: {goalId}\", { goalId: block.goalId })}`, stage: undefined });\n\t\t\t}\n\t\t\tfor (const stage of block.stages) {\n\t\t\t\trows.push({\n\t\t\t\t\ttext: this.formatStageRow(stage),\n\t\t\t\t\tstage: { workflowId: block.workflowId, stageId: stage.stageId },\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (block.diagnostics.length > 0) {\n\t\t\t\trows.push({ text: ` ${t(\"diagnostics:\")}`, stage: undefined });\n\t\t\t\tfor (const diagnostic of block.diagnostics) {\n\t\t\t\t\trows.push({ text: theme.fg(\"warning\", ` - ${diagnostic}`), stage: undefined });\n\t\t\t\t}\n\t\t\t}\n\t\t\trows.push({ text: \"\", stage: undefined });\n\t\t}\n\t\treturn rows;\n\t}\n\n\tprivate formatStageRow(stage: WorkflowStageRow): string {\n\t\tconst pad = \" \".repeat(stage.depth + 1);\n\t\tif (stage.backReference) {\n\t\t\treturn `${pad} -> ${stage.stageId} ${t(\"(shown above)\")}`;\n\t\t}\n\t\tconst bits = [`- ${stage.stageId} [${stageStatusColor(stage.status)(stage.status)}] ${stage.title}`];\n\t\tif (stage.waitingOn !== null) {\n\t\t\tbits.push(theme.fg(\"dim\", `— ${t(\"waiting: {condition}\", { condition: stage.waitingOn })}`));\n\t\t}\n\t\tif (stage.attempts > 0) bits.push(theme.fg(\"dim\", t(\"· attempts {count}\", { count: stage.attempts })));\n\t\tif (stage.evidenceCount > 0) {\n\t\t\tbits.push(theme.fg(\"dim\", t(\"· evidence {count}\", { count: stage.evidenceCount })));\n\t\t}\n\t\tif (stage.scopeConflict !== null) {\n\t\t\tbits.push(theme.fg(\"warning\", `⚠ ${t(\"scope:{conflict}\", { conflict: stage.scopeConflict })}`));\n\t\t}\n\t\treturn `${pad}${bits.join(\" \")}`;\n\t}\n\n\t/** Rows are rebuilt lazily on access; the key couples rebuild + selection restore. */\n\tprivate syncRows(): void {\n\t\tconst model = this.getModel();\n\t\tconst previous = this.rows[this.selectedIndex]?.stage;\n\t\tthis.rows = this.buildRows(model);\n\t\tif (previous !== undefined) {\n\t\t\tconst restored = this.rows.findIndex((row) => row.stage?.stageId === previous.stageId);\n\t\t\tif (restored !== -1) this.selectedIndex = restored;\n\t\t}\n\t\tif (this.selectedIndex >= this.rows.length) this.selectedIndex = Math.max(0, this.rows.length - 1);\n\t\tthis.clampOffset();\n\t}\n\n\tprivate clampOffset(): void {\n\t\tconst maxRows = Math.max(1, this.config.maxRows);\n\t\tif (this.selectedIndex < this.offset) this.offset = this.selectedIndex;\n\t\tif (this.selectedIndex >= this.offset + maxRows) this.offset = this.selectedIndex - maxRows + 1;\n\t\tif (this.offset < 0) this.offset = 0;\n\t}\n\n\trenderRows(width: number): string[] {\n\t\tthis.syncRows();\n\t\tconst maxRows = Math.max(1, this.config.maxRows);\n\t\tconst lines: string[] = [];\n\t\tfor (let index = this.offset; index < Math.min(this.rows.length, this.offset + maxRows); index++) {\n\t\t\tconst row = this.rows[index];\n\t\t\tif (row === undefined) continue;\n\t\t\tconst selected = index === this.selectedIndex;\n\t\t\tconst prefix = selected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tlines.push(truncateToWidth(`${prefix}${row.text}`, width));\n\t\t}\n\t\tconst hiddenAbove = this.offset;\n\t\tconst hiddenBelow = this.rows.length - this.offset - maxRows;\n\t\tconst hidden: string[] = [];\n\t\tif (hiddenAbove > 0) hidden.push(`↑${hiddenAbove}`);\n\t\tif (hiddenBelow > 0) hidden.push(`↓${hiddenBelow}`);\n\t\t// Fixed output height (exactly maxRows content rows plus this final\n\t\t// indicator row), the same contract as agents-view renderRows: the\n\t\t// overlay's grid footprint must not change with mode switches or row\n\t\t// counts, or the chat/editor layout boundary moves between frames.\n\t\twhile (lines.length < maxRows) lines.push(truncateToWidth(\" \", width));\n\t\tlines.push(truncateToWidth(hidden.length > 0 ? theme.fg(\"dim\", ` ${hidden.join(\" \")}`) : \" \", width));\n\t\treturn lines;\n\t}\n\n\tprivate move(delta: number): void {\n\t\tif (this.rows.length === 0) return;\n\t\tthis.selectedIndex = Math.min(this.rows.length - 1, Math.max(0, this.selectedIndex + delta));\n\t\tthis.clampOffset();\n\t}\n\n\tprivate flash(message: string): void {\n\t\tthis.flashText.setText(theme.fg(\"warning\", ` ${message}`));\n\t\tif (this.flashTimeout !== null) clearTimeout(this.flashTimeout);\n\t\tthis.flashTimeout = setTimeout(() => {\n\t\t\tthis.flashText.setText(\"\");\n\t\t\tthis.flashTimeout = null;\n\t\t\tthis.config.requestRender?.();\n\t\t}, 2500);\n\t\tthis.config.requestRender?.();\n\t}\n\n\tprivate toggleMode(): void {\n\t\tthis.mode = this.mode === \"panorama\" ? \"log\" : \"panorama\";\n\t\tthis.selectedIndex = 0;\n\t\tthis.offset = 0;\n\t\tthis.updateModeTexts();\n\t\tthis.config.requestRender?.();\n\t}\n\n\tprivate forward(action: WorkflowForwardAction): void {\n\t\tconst selected = this.rows[this.selectedIndex];\n\t\tif (selected?.stage === undefined) {\n\t\t\tif (action === \"cancel\") {\n\t\t\t\tconst workflowId = this.getModel().activeWorkflowId;\n\t\t\t\tif (workflowId === undefined) {\n\t\t\t\t\tthis.flash(t(\"No workflow to cancel\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.onForwardAction?.(buildWorkflowActionText(action, workflowId, undefined));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.flash(t(\"Select a stage row first\"));\n\t\t\treturn;\n\t\t}\n\t\tthis.onForwardAction?.(buildWorkflowActionText(action, selected.stage.workflowId, selected.stage.stageId));\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onClose?.();\n\t\t} else if (kb.matches(keyData, \"tui.input.tab\") || keyData === \"l\") {\n\t\t\tthis.toggleMode();\n\t\t} else if (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.move(-1);\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.move(1);\n\t\t} else if (kb.matches(keyData, \"tui.select.pageUp\")) {\n\t\t\tthis.move(-Math.max(1, this.config.maxRows));\n\t\t} else if (kb.matches(keyData, \"tui.select.pageDown\")) {\n\t\t\tthis.move(Math.max(1, this.config.maxRows));\n\t\t} else if (keyData === \"a\") {\n\t\t\tthis.forward(\"approve\");\n\t\t} else if (keyData === \"r\") {\n\t\t\tthis.forward(\"reject\");\n\t\t} else if (keyData === \"s\") {\n\t\t\tthis.forward(\"skip\");\n\t\t} else if (keyData === \"c\") {\n\t\t\tthis.forward(\"cancel\");\n\t\t}\n\t}\n}\n"]}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* stay separated (设计 §9 L2 "转发到对话").
|
|
16
16
|
*/
|
|
17
17
|
import { Container, getKeybindings, Text, truncateToWidth } from "@agent-forge/tui";
|
|
18
|
+
import { t } from "../../../core/i18n/index.js";
|
|
18
19
|
import { theme } from "../theme/theme.js";
|
|
19
20
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
20
21
|
import { keyHint, rawKeyHint } from "./keybinding-hints.js";
|
|
@@ -396,7 +397,7 @@ export class WorkflowViewComponent extends Container {
|
|
|
396
397
|
constructor(config) {
|
|
397
398
|
super();
|
|
398
399
|
this.config = config;
|
|
399
|
-
this.titleText = new Text(theme.fg("accent", theme.bold("
|
|
400
|
+
this.titleText = new Text(theme.fg("accent", theme.bold(` ${t("Workflow Overview")}`)), 1, 0);
|
|
400
401
|
this.modeHintText = new Text("", 1, 0);
|
|
401
402
|
this.helpText = new Text("", 1, 0);
|
|
402
403
|
this.flashText = new Text("", 1, 0);
|
|
@@ -416,19 +417,19 @@ export class WorkflowViewComponent extends Container {
|
|
|
416
417
|
}
|
|
417
418
|
}
|
|
418
419
|
updateModeTexts() {
|
|
419
|
-
const label = this.mode === "panorama" ? "panorama" : "transition log (newest first)";
|
|
420
|
+
const label = this.mode === "panorama" ? t("panorama") : t("transition log (newest first)");
|
|
420
421
|
this.modeHintText.setText(theme.fg("dim", ` ${label}`));
|
|
421
422
|
this.helpText.setText(this.buildHelpLine());
|
|
422
423
|
}
|
|
423
424
|
buildHelpLine() {
|
|
424
|
-
const parts = [rawKeyHint("↑↓", "move")];
|
|
425
|
+
const parts = [rawKeyHint("↑↓", t("move"))];
|
|
425
426
|
if (this.mode === "panorama") {
|
|
426
|
-
parts.push(rawKeyHint("tab/l", "log"), rawKeyHint("a", "approve"), rawKeyHint("r", "reject"), rawKeyHint("s", "skip"), rawKeyHint("c", "cancel"));
|
|
427
|
+
parts.push(rawKeyHint("tab/l", t("log")), rawKeyHint("a", t("approve")), rawKeyHint("r", t("reject")), rawKeyHint("s", t("skip")), rawKeyHint("c", t("cancel")));
|
|
427
428
|
}
|
|
428
429
|
else {
|
|
429
|
-
parts.push(rawKeyHint("tab/l", "panorama"));
|
|
430
|
+
parts.push(rawKeyHint("tab/l", t("panorama")));
|
|
430
431
|
}
|
|
431
|
-
parts.push(keyHint("tui.select.cancel", "close"));
|
|
432
|
+
parts.push(keyHint("tui.select.cancel", t("close")));
|
|
432
433
|
return ` ${parts.join(" ")}`;
|
|
433
434
|
}
|
|
434
435
|
getModel() {
|
|
@@ -444,7 +445,7 @@ export class WorkflowViewComponent extends Container {
|
|
|
444
445
|
const rows = [];
|
|
445
446
|
if (this.mode === "log") {
|
|
446
447
|
if (model.transitions.length === 0) {
|
|
447
|
-
rows.push({ text: "
|
|
448
|
+
rows.push({ text: ` ${t("no workflow transitions recorded")}`, stage: undefined });
|
|
448
449
|
return rows;
|
|
449
450
|
}
|
|
450
451
|
for (const transition of model.transitions) {
|
|
@@ -460,21 +461,33 @@ export class WorkflowViewComponent extends Container {
|
|
|
460
461
|
}
|
|
461
462
|
const blocks = model.panorama;
|
|
462
463
|
if (blocks === undefined) {
|
|
463
|
-
rows.push({ text: "
|
|
464
|
+
rows.push({ text: ` ${t("no workflow.state entry in this session")}`, stage: undefined });
|
|
464
465
|
return rows;
|
|
465
466
|
}
|
|
466
467
|
if (blocks.length === 0) {
|
|
467
|
-
rows.push({
|
|
468
|
+
rows.push({
|
|
469
|
+
text: ` ${t("no workflows in this session; create one with workflow_set")}`,
|
|
470
|
+
stage: undefined,
|
|
471
|
+
});
|
|
468
472
|
return rows;
|
|
469
473
|
}
|
|
470
474
|
for (const block of blocks) {
|
|
471
475
|
const status = workflowStatusColor(block.status)(block.status);
|
|
472
476
|
rows.push({
|
|
473
|
-
text: ` workflow "
|
|
477
|
+
text: ` ${t('workflow "{title}" ({workflowId}) — {status} · revision {revision} · active {activeCount}/{maxActiveStages} · retreats {retreatCount}/{maxRetreats}', {
|
|
478
|
+
title: block.title,
|
|
479
|
+
workflowId: block.workflowId,
|
|
480
|
+
status,
|
|
481
|
+
revision: block.revision,
|
|
482
|
+
activeCount: block.activeCount,
|
|
483
|
+
maxActiveStages: block.maxActiveStages,
|
|
484
|
+
retreatCount: block.retreatCount,
|
|
485
|
+
maxRetreats: block.maxRetreats,
|
|
486
|
+
})}`,
|
|
474
487
|
stage: undefined,
|
|
475
488
|
});
|
|
476
489
|
if (block.goalId !== undefined) {
|
|
477
|
-
rows.push({ text: ` goal:
|
|
490
|
+
rows.push({ text: ` ${t("goal: {goalId}", { goalId: block.goalId })}`, stage: undefined });
|
|
478
491
|
}
|
|
479
492
|
for (const stage of block.stages) {
|
|
480
493
|
rows.push({
|
|
@@ -483,7 +496,7 @@ export class WorkflowViewComponent extends Container {
|
|
|
483
496
|
});
|
|
484
497
|
}
|
|
485
498
|
if (block.diagnostics.length > 0) {
|
|
486
|
-
rows.push({ text: "
|
|
499
|
+
rows.push({ text: ` ${t("diagnostics:")}`, stage: undefined });
|
|
487
500
|
for (const diagnostic of block.diagnostics) {
|
|
488
501
|
rows.push({ text: theme.fg("warning", ` - ${diagnostic}`), stage: undefined });
|
|
489
502
|
}
|
|
@@ -495,17 +508,19 @@ export class WorkflowViewComponent extends Container {
|
|
|
495
508
|
formatStageRow(stage) {
|
|
496
509
|
const pad = " ".repeat(stage.depth + 1);
|
|
497
510
|
if (stage.backReference) {
|
|
498
|
-
return `${pad} -> ${stage.stageId} (shown above)`;
|
|
511
|
+
return `${pad} -> ${stage.stageId} ${t("(shown above)")}`;
|
|
499
512
|
}
|
|
500
513
|
const bits = [`- ${stage.stageId} [${stageStatusColor(stage.status)(stage.status)}] ${stage.title}`];
|
|
501
|
-
if (stage.waitingOn !== null)
|
|
502
|
-
bits.push(theme.fg("dim", `— waiting:
|
|
514
|
+
if (stage.waitingOn !== null) {
|
|
515
|
+
bits.push(theme.fg("dim", `— ${t("waiting: {condition}", { condition: stage.waitingOn })}`));
|
|
516
|
+
}
|
|
503
517
|
if (stage.attempts > 0)
|
|
504
|
-
bits.push(theme.fg("dim",
|
|
505
|
-
if (stage.evidenceCount > 0)
|
|
506
|
-
bits.push(theme.fg("dim",
|
|
518
|
+
bits.push(theme.fg("dim", t("· attempts {count}", { count: stage.attempts })));
|
|
519
|
+
if (stage.evidenceCount > 0) {
|
|
520
|
+
bits.push(theme.fg("dim", t("· evidence {count}", { count: stage.evidenceCount })));
|
|
521
|
+
}
|
|
507
522
|
if (stage.scopeConflict !== null) {
|
|
508
|
-
bits.push(theme.fg("warning", `⚠ scope
|
|
523
|
+
bits.push(theme.fg("warning", `⚠ ${t("scope:{conflict}", { conflict: stage.scopeConflict })}`));
|
|
509
524
|
}
|
|
510
525
|
return `${pad}${bits.join(" ")}`;
|
|
511
526
|
}
|
|
@@ -590,13 +605,13 @@ export class WorkflowViewComponent extends Container {
|
|
|
590
605
|
if (action === "cancel") {
|
|
591
606
|
const workflowId = this.getModel().activeWorkflowId;
|
|
592
607
|
if (workflowId === undefined) {
|
|
593
|
-
this.flash("No workflow to cancel");
|
|
608
|
+
this.flash(t("No workflow to cancel"));
|
|
594
609
|
return;
|
|
595
610
|
}
|
|
596
611
|
this.onForwardAction?.(buildWorkflowActionText(action, workflowId, undefined));
|
|
597
612
|
return;
|
|
598
613
|
}
|
|
599
|
-
this.flash("Select a stage row first");
|
|
614
|
+
this.flash(t("Select a stage row first"));
|
|
600
615
|
return;
|
|
601
616
|
}
|
|
602
617
|
this.onForwardAction?.(buildWorkflowActionText(action, selected.stage.workflowId, selected.stage.stageId));
|